Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

#!/usr/bin/python 

# 

# Copyright (C) Citrix Systems Inc. 

# 

# This program is free software; you can redistribute it and/or modify  

# it under the terms of the GNU Lesser General Public License as published  

# by the Free Software Foundation; version 2.1 only. 

# 

# This program is distributed in the hope that it will be useful,  

# but WITHOUT ANY WARRANTY; without even the implied warranty of  

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  

# GNU Lesser General Public License for more details. 

# 

# You should have received a copy of the GNU Lesser General Public License 

# along with this program; if not, write to the Free Software Foundation, Inc., 

# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA 

# 

# FIXME 

# RawHBASR: Hardware HBA LUN driver, e.g. Fibre Channel or SAS or 

# hardware based iSCSI 

# FIXME 

 

import B_util 

 

import SR, VDI, SRCommand, HBASR, LUNperVDI 

import util, scsiutil, devscan 

import xs_errors 

import os 

 

CAPABILITIES = ["SR_PROBE", "VDI_ATTACH", "VDI_DETACH", "VDI_DELETE"] 

 

CONFIGURATION = [ [ 'type', 'FIXME (optional)' ] ] 

 

DRIVER_INFO = { 

    'name': 'RawHBA LUN-per-VDI driver', 

    'description': 'SR plugin which represents LUNs as VDIs sourced by hardware HBA adapters, FC support', 

    'vendor': 'Citrix Systems Inc', 

    'copyright': '(C) 2012 Citrix Systems Inc', 

    'driver_version': '1.0', 

    'required_api_version': '1.0', 

    'capabilities': CAPABILITIES, 

    'configuration': CONFIGURATION 

    } 

 

NEEDS_LOADVDIS = ["sr_scan"] 

 

TYPE = 'rawhba' 

 

class RawHBASR(HBASR.HBASR): 

    """ Raw LUN-per-VDI HBA storage repository""" 

 

    def handles(type): 

        return type == TYPE 

    handles = staticmethod(handles) 

 

    def load(self, sr_uuid): 

        """Extend super class load by initializing the hba dict. 

        FIXME: verify this is needed 

        """ 

        super(RawHBASR, self).load(sr_uuid) 

        # There are some calls (e.g. probe) where it is not needed at all 

        #self._init_hbadict() 

 

        self._get_stats() 

 

        # For this SR the internal self.attached flag hs no meaning 

        # at all. Xapi knows about its status and if it is 

        # not attached all the commands relying on that are not 

        # executed. This variable is used in various places inside the 

        # base class so we cannot ignore it. Setting it always to true 

        # will prevent sr.create and sr.scan to throw exceptions but 

        # they need to be fixed: attached is not the flag they should 

        # look for. 

        self.attached = True 

 

    def _loadvdis(self): 

        if self.cmd not in NEEDS_LOADVDIS: 

            return 0 

        if self.vdis: 

            return 

 

        self._init_hbadict() 

        count = 0 

        self.physical_size = 0 

        root_dev_id = util.getrootdevID() 

 

        xapi_session = self.session.xenapi 

        known_scsid = {} # dict of ids processed within the following loop 

 

        for key in self.hbadict.iterkeys(): 

 

            # We need a fresh sm_config everytime because it is modified 

            # inside this loop 

            sm_config = xapi_session.SR.get_sm_config(self.sr_ref) 

 

            # The way we create vdi_path and the following check are 

            # not clear at all 

            vdi_path = os.path.join("/dev",key) 

            if not self.devs.has_key(vdi_path): 

                continue 

 

            scsi_id = scsiutil.getSCSIid(vdi_path) 

            if scsi_id == root_dev_id: 

                util.SMlog("Skipping root device %s" %scsi_id) 

                continue 

 

            # Avoid false positives: this SR can already contain this 

            # SCSIid during scan. 

            scsi_key = "scsi-" + scsi_id 

            if sm_config.has_key(scsi_key): 

                # if we know about this scsid we can skip this specific dev 

                if known_scsid.has_key(scsi_key): 

                    util.SMlog("This SCSI id (%s) is already added" %scsi_id) 

                    continue 

                else: 

                    # marked as known to avoid adding it again to sm_config 

                    known_scsid[scsi_key] = "" 

            elif util.test_SCSIid(self.session, None, scsi_id): 

                util.SMlog("This SCSI id (%s) is used by another SR" %scsi_id) 

                continue 

 

            # getuniqueserial invokes again getSCSIid -> Fix! 

            uuid = scsiutil.gen_uuid_from_string( 

                scsiutil.getuniqueserial(vdi_path) 

                ) 

            # We could have checked the SCSIid but the dictionary has 

            # uuid as key. 

            # We have already checked known_scsid, though. This block is 

            # supposed to be always False 

            if self.vdis.has_key(uuid): 

                util.SMlog("Warning: unexpected code block reached with" 

                           " uuid = %s" %scsi_id) 

                continue 

 

            obj = self.vdi(uuid) 

            path = self.mpathmodule.path(scsi_id) 

            ids = self.devs[vdi_path] 

            obj._query(vdi_path, ids[4], uuid, scsi_id) 

            self.vdis[uuid] = obj 

            self.physical_size += obj.size 

 

            count += 1 

 

            # If we know about it no need to add to sm_config 

            if known_scsid.has_key(scsi_key): 

                continue 

 

            # Prepare multipathing and make the other SRs know this SCSIid 

            # is reserved. 

            # Its counterpart is vdi_delete 

            try: 

                xapi_session.SR.add_to_sm_config(self.sr_ref, scsi_key, uuid) 

                known_scsid[scsi_key] = "" 

            except: 

                util.SMlog("Warning: add_to_sm_config failed unexpectedly") 

 

        return count 

 

 

    def scan(self, sr_uuid): 

        """ 

        This function is almost a copy of its base class equivalent. 

        Main differences are: 

        - Fixed erroneous size calculation 

        - Set VDI names automatically 

        - Avoid ScanRecord sync for missing VDIS (stale LUNs) 

 

        The last one is called in the sub-sub class so we cannot simply 

        extend the base class funcion but we need to override it 

        """ 

 

        self._init_hbadict() 

        if not self.passthrough: 

            if not self.attached: 

                raise xs_errors.XenError('SRUnavailable') 

 

            self._loadvdis() 

 

        # This block is almost SR.scan but without missing sync 

        self._db_update() 

        scanrecord = SR.ScanRecord(self) 

        scanrecord.synchronise_new() 

        scanrecord.synchronise_existing() 

 

 

        # Fixing sizes calculation 

        phys_util = 0 

        for key in self.vdis: 

            vdi_ref = self.session.xenapi.VDI.get_by_uuid(key) 

            if B_util.is_vdi_attached(self.session, vdi_ref): 

                phys_util += self.vdis[key].size 

        self._set_stats(phys_util=phys_util) 

 

        self._set_vdis_name() 

 

 

    def _set_vdis_name(self): 

        if not self.vdis: 

            return 

        for vdi_ref in self.session.xenapi.SR.get_VDIs(self.sr_ref): 

            vdi_uuid = self.session.xenapi.VDI.get_uuid(vdi_ref) 

            try: 

                vdi = self.vdis[vdi_uuid] 

            except: 

                util.SMlog("Cannot set name for for %s" %vdi_uuid) 

                continue 

            self.session.xenapi.VDI.set_name_label(vdi_ref, vdi.SCSIid) 

 

 

    def vdi(self, uuid): 

        return RawHBAVDI(self, uuid) 

 

    def _get_stats(self): 

        stats = self.get_stats() 

        self.physical_size = stats['physical_size'] 

        self.physical_utilisation = stats['physical_utilisation'] 

        self.virtual_allocation = stats['virtual_allocation'] 

 

    def get_stats(self): 

        stats = {} 

        xapi_session = self.session.xenapi 

        sr_ref = xapi_session.SR.get_by_uuid(self.uuid) 

        stats['physical_size'] = int(xapi_session.SR.get_physical_size(sr_ref)) 

        stats['physical_utilisation'] = int(xapi_session.SR. 

                                            get_physical_utilisation(sr_ref)) 

        stats['virtual_allocation'] = int(xapi_session.SR. 

                                          get_virtual_allocation(sr_ref)) 

        return stats 

 

    def _set_stats(self, phys_size=None, phys_util=None): 

        if phys_size != None: 

            self.physical_size = phys_size 

        if phys_util != None: 

            self.physical_utilisation = phys_util 

            self.virtual_allocation = phys_util 

        self._db_update() 

 

    def update_stats(self, phys_util): 

        new_util = self.physical_utilisation + phys_util 

        self._set_stats(phys_util=new_util) 

 

 

    def _add_pbd_other_config(self, key, value): 

        try: 

            pbd_ref = util.find_my_pbd(self.session, self.host_ref, self.sr_ref) 

        except: 

            util.SMlog("No pbd for sr_ref %s on host_ref %s" 

                       %(self.sr_ref, self.host_ref)) 

            return 

        try: 

            self.session.xenapi.PBD.add_to_other_config(pbd_ref, key, value) 

        except: 

            util.SMlog("add_to_other_config failed") 

 

 

    def attach(self, sr_uuid): 

        super(RawHBASR, self).attach(sr_uuid) 

        if self.mpath == 'true': 

            self._add_pbd_other_config('multipathed', 'true') 

 

 

    def _reset_pbd_other_config(self): 

        try: 

            pbd_ref = util.find_my_pbd(self.session, self.host_ref, self.sr_ref) 

        except: 

            util.SMlog("No pbd for sr_ref %s on host_ref %s" 

                       %(self.sr_ref, self.host_ref)) 

        for key in ["multipathed"]: 

            try: 

                self.session.xenapi.PBD.remove_from_other_config(pbd_ref, key) 

            except: 

                util.SMlog("remove_from_other_config failed") 

 

 

    def detach(self, sr_uuid): 

        """ Override base class function because we rely on xapi 

        to check if VDIs are in use. 

        Take care of removing multipath flags 

        """ 

        self._reset_pbd_other_config() 

 

 

class RawHBAVDI(LUNperVDI.RAWVDI): 

    """Customized LUN-per-VDI class fro RawHBA storage repository""" 

 

    def load(self, vdi_uuid): 

        super(RawHBAVDI, self).load(vdi_uuid) 

        self.managed = True 

 

    # _query can work in two ways, if scsi_id is provided when called, the 

    # fn gets all devices corresponding to the scsi_id and does a device rescan, 

    # else it just rescan the device being passed. 

    def _query(self, path, id, uuid=None, scsi_id=None): 

        """Overloaded function with mostly duplicated code""" 

        if uuid: 

            self.uuid = uuid 

        else: 

            util.SMlog("RawHBA: uuid should not be generated..") 

            self.uuid = scsiutil.gen_uuid_from_string( 

                scsiutil.getuniqueserial(path) 

                ) 

        if scsi_id: 

            self.SCSIid = scsi_id 

        else: 

            # It is usually unnecessary to calculate it again but scsi_id 

            # is used as a flag in this function and we cannot guarantee 

            # this info is already available at call time 

            self.SCSIid = scsiutil.getSCSIid(path) 

        self.location = self.uuid 

        self.vendor = scsiutil.getmanufacturer(path) 

        self.serial = scsiutil.getserial(path) 

        self.LUNid = id 

 

        # Handle resize done at the array size. The resize gets reflected 

        # only when the vdi is not in detached state. Do this if we the vdi 

        # is known to xapi 

        try: 

            vdi_ref = self.sr.session.xenapi.VDI.get_by_uuid(self.uuid) 

            # Check if the vbd is not in attached state, do a LUN rescan 

            # to reflect the array LUN 

            dev = [path] 

            if scsi_id: 

                # We want all the devices with this scsi_id 

                dev = scsiutil._genReverseSCSIidmap(scsi_id) 

            if self.sr.srcmd.cmd == "vdi_attach": 

                scsiutil.refreshdev(dev) 

            elif not B_util.is_vdi_attached(self.sr.session, vdi_ref): 

                scsiutil.refreshdev(dev) 

        except: 

            pass 

 

        self.size = scsiutil.getsize(path) 

        self.path = path 

        sm_config = util.default(self, "sm_config", lambda: {}) 

        sm_config['LUNid'] = str(self.LUNid) 

        sm_config['SCSIid'] = self.SCSIid 

        # Make sure to use kernel blkback (not blktap3) for raw LUNs 

        sm_config['backend-kind'] = 'vbd' 

        self.sm_config = sm_config 

 

    def attach(self, sr_uuid, vdi_uuid): 

        # Perform a device scan for all paths, to check for any LUN resize 

        scsi_id = self.sm_config['SCSIid'] 

        devices = scsiutil._genReverseSCSIidmap(scsi_id) 

        xapi_session = self.session.xenapi 

 

        # At the vdi attach stage if devices are not found against the scsi_id,  

        # the two reasons would be 1. The machine is slave on which a bus scan 

        # was not performed, perform a bus scan to rectify the same. 2. Genuine 

        # HBA bus error, throw an error back. 

        if len(devices) == 0: 

            devscan.adapters() 

            devices = scsiutil._genReverseSCSIidmap(scsi_id) 

 

        # If no devices are found after a bus rescan, flag an error 

        if len(devices) == 0: 

            raise xs_errors.XenError('InvalidDev', \ 

                  opterr=('No HBA Device detected with SCSI Id[%s]') % scsi_id) 

 

        # Run a query on devices against the scsi id to refresh the size 

        dev_lun_info = scsiutil.cacheSCSIidentifiers() 

        for dev in devices: 

            self._query(dev, dev_lun_info[dev][4], vdi_uuid) 

 

        #Update xapi with the new size 

        vdi_ref = self.sr.session.xenapi.VDI.get_by_uuid(vdi_uuid) 

        self.sr.session.xenapi.VDI.set_virtual_size(vdi_ref, str(self.size)) 

 

        # Multipath enable 

        if self.sr.mpath == "true": 

            self.sr.mpathmodule.refresh(scsi_id, len(devices)) 

            self.path = self.sr.mpathmodule.path(scsi_id) 

            # The SCSIid is already stored inside SR sm_config. 

            # We need only to trigger mpathcount 

            try: 

                cmd = ['/opt/xensource/sm/mpathcount.py', scsi_id] 

                util.pread2(cmd) 

            except: 

                util.SMlog("RawHBA: something wrong with mpathcount") 

 

        ret = VDI.VDI.attach(self, sr_uuid, vdi_uuid) 

        self.sr.update_stats(self.size) 

        return ret 

 

 

    def delete(self, sr_uuid, vdi_uuid): 

        util.SMlog("Raw LUN VDI delete") 

        scsi_id = self.sm_config['SCSIid'] 

        xapi_session = self.session.xenapi 

 

        # Cleaning up SR sm_config 

        scsi_key = "scsi-" + scsi_id 

        xapi_session.SR.remove_from_sm_config(self.sr.sr_ref, scsi_key) 

 

 

    def detach(self, sr_uuid, vdi_uuid): 

        scsi_id = self.sm_config['SCSIid'] 

        xapi_session = self.session.xenapi 

 

        # Multipath disable 

        if self.sr.mpath == "true": 

            #devices = scsiutil._genReverseSCSIidmap(scsi_id) 

            self.sr.mpathmodule.reset(scsi_id, True) 

            util.remove_mpathcount_field(self.sr.session, self.sr.host_ref, 

                                         self.sr.sr_ref, scsi_id) 

 

        # Get size from xapi db 

        vdi_ref = xapi_session.VDI.get_by_uuid(vdi_uuid) 

        size = int(xapi_session.VDI.get_virtual_size(vdi_ref)) 

 

        self.sr.update_stats(-size) 

 

 

if __name__ == '__main__': 

    SRCommand.run(RawHBASR, DRIVER_INFO) 

else: 

    SR.registerSR(RawHBASR)