Hide keyboard shortcuts

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#!/usr/bin/python3 

2# 

3# Copyright (C) Citrix Systems Inc. 

4# 

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

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

7# by the Free Software Foundation; version 2.1 only. 

8# 

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

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

11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

12# GNU Lesser General Public License for more details. 

13# 

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

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

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

17# 

18# ISCSISR: ISCSI software initiator SR driver 

19# 

20 

21import SR 

22import util 

23import time 

24import LUNperVDI 

25import os 

26import sys 

27import re 

28import glob 

29import xml.dom.minidom 

30import scsiutil 

31import iscsilib 

32import xs_errors 

33 

34INITIATORNAME_FILE = '/etc/iscsi/initiatorname.iscsi' 

35SECTOR_SHIFT = 9 

36DEFAULT_PORT = 3260 

37# 2^16 Max port number value 

38MAXPORT = 65535 

39MAX_TIMEOUT = 15 

40MAX_LUNID_TIMEOUT = 60 

41ISCSI_PROCNAME = "iscsi_tcp" 

42 

43 

44class BaseISCSISR(SR.SR): 

45 """ISCSI storage repository""" 

46 

47 @property 

48 def force_tapdisk(self): 

49 return self.dconf.get('force_tapdisk', 'false') == 'true' 

50 

51 @property 

52 def attached(self): 

53 if not self._attached: 

54 self._attached = False 

55 self._attached = iscsilib._checkTGT(self.targetIQN, self.target) 

56 return self._attached 

57 

58 @attached.setter 

59 def attached(self, value): 

60 self._attached = value 

61 

62 @property 

63 def pathdict(self): 

64 if not self._pathdict: 64 ↛ 65line 64 didn't jump to line 65, because the condition on line 64 was never true

65 self._initPaths() 

66 return self._pathdict 

67 

68 @property 

69 def adapter(self): 

70 if not self._adapter: 70 ↛ 71line 70 didn't jump to line 71, because the condition on line 70 was never true

71 self._initPaths() 

72 return self._adapter 

73 

74 @adapter.setter 

75 def adapter(self, value): 

76 self._adapter = value 

77 

78 @property 

79 def devs(self): 

80 if not self._devs: 

81 self._initPaths() 

82 return self._devs 

83 

84 @property 

85 def tgtidx(self): 

86 """This appears to only be referenced by a unit test. Do we really need it? """ 

87 if not self._tgtidx: 87 ↛ 88line 87 didn't jump to line 88, because the condition on line 87 was never true

88 self._initPaths() 

89 return self._tgtidx 

90 

91 @property 

92 def path(self): 

93 if not self._path: 93 ↛ 94line 93 didn't jump to line 94, because the condition on line 93 was never true

94 self._initPaths() 

95 return self._path 

96 

97 @property 

98 def address(self): 

99 if not self._address: 99 ↛ 100line 99 didn't jump to line 100, because the condition on line 99 was never true

100 self._initPaths() 

101 return self._address 

102 

103 def handles(type): 

104 return False 

105 handles = staticmethod(handles) 

106 

107 def _synchroniseAddrList(self, addrlist): 

108 if not self.multihomed: 108 ↛ 110line 108 didn't jump to line 110, because the condition on line 108 was never false

109 return 

110 change = False 

111 if 'multihomelist' not in self.dconf: 

112 change = True 

113 self.mlist = [] 

114 mstr = "" 

115 else: 

116 self.mlist = self.dconf['multihomelist'].split(',') 

117 mstr = self.dconf['multihomelist'] 

118 for val in addrlist: 

119 if not val in self.mlist: 

120 self.mlist.append(val) 

121 if len(mstr): 

122 mstr += "," 

123 mstr += val 

124 change = True 

125 if change: 

126 pbd = None 

127 try: 

128 pbd = util.find_my_pbd(self.session, self.host_ref, self.sr_ref) 

129 if pbd is not None: 

130 device_config = self.session.xenapi.PBD.get_device_config(pbd) 

131 device_config['multihomelist'] = mstr 

132 self.session.xenapi.PBD.set_device_config(pbd, device_config) 

133 except: 

134 pass 

135 

136 def load(self, sr_uuid): 

137 if self.force_tapdisk: 

138 self.sr_vditype = 'aio' 

139 else: 

140 self.sr_vditype = 'phy' 

141 self.discoverentry = 0 

142 self.default_vdi_visibility = False 

143 

144 # Required parameters 

145 if 'target' not in self.dconf or not self.dconf['target']: 145 ↛ 146line 145 didn't jump to line 146, because the condition on line 145 was never true

146 raise xs_errors.XenError('ConfigTargetMissing') 

147 

148 # we are no longer putting hconf in the xml. 

149 # Instead we pass a session and host ref and let the SM backend query XAPI itself 

150 try: 

151 if 'localIQN' not in self.dconf: 151 ↛ 152line 151 didn't jump to line 152, because the condition on line 151 was never true

152 self.localIQN = self.session.xenapi.host.get_other_config(self.host_ref)['iscsi_iqn'] 

153 else: 

154 self.localIQN = self.dconf['localIQN'] 

155 except: 

156 raise xs_errors.XenError('ConfigISCSIIQNMissing') 

157 

158 # Check for empty string 

159 if not self.localIQN: 159 ↛ 160line 159 didn't jump to line 160, because the condition on line 159 was never true

160 raise xs_errors.XenError('ConfigISCSIIQNMissing') 

161 

162 try: 

163 self.target = util._convertDNS(self.dconf['target'].split(',')[0]) 

164 except: 

165 raise xs_errors.XenError('DNSError') 

166 

167 self.targetlist = self.target 

168 if 'targetlist' in self.dconf: 168 ↛ 169line 168 didn't jump to line 169, because the condition on line 168 was never true

169 self.targetlist = self.dconf['targetlist'] 

170 

171 # Optional parameters 

172 self.chapuser = "" 

173 self.chappassword = "" 

174 if 'chapuser' in self.dconf \ 

175 and ('chappassword' in self.dconf or 'chappassword_secret' in self.dconf): 

176 self.chapuser = self.dconf['chapuser'].encode('utf-8') 

177 if 'chappassword_secret' in self.dconf: 177 ↛ 178line 177 didn't jump to line 178, because the condition on line 177 was never true

178 self.chappassword = util.get_secret(self.session, self.dconf['chappassword_secret']) 

179 else: 

180 self.chappassword = self.dconf['chappassword'] 

181 

182 self.chappassword = self.chappassword.encode('utf-8') 

183 

184 self.incoming_chapuser = "" 

185 self.incoming_chappassword = "" 

186 if 'incoming_chapuser' in self.dconf \ 

187 and ('incoming_chappassword' in self.dconf or 'incoming_chappassword_secret' in self.dconf): 

188 self.incoming_chapuser = self.dconf['incoming_chapuser'].encode('utf-8') 

189 if 'incoming_chappassword_secret' in self.dconf: 189 ↛ 190line 189 didn't jump to line 190, because the condition on line 189 was never true

190 self.incoming_chappassword = util.get_secret(self.session, self.dconf['incoming_chappassword_secret']) 

191 else: 

192 self.incoming_chappassword = self.dconf['incoming_chappassword'] 

193 

194 self.incoming_chappassword = self.incoming_chappassword.encode('utf-8') 

195 

196 self.port = DEFAULT_PORT 

197 if 'port' in self.dconf and self.dconf['port']: 197 ↛ 198line 197 didn't jump to line 198, because the condition on line 197 was never true

198 try: 

199 self.port = int(self.dconf['port']) 

200 except: 

201 raise xs_errors.XenError('ISCSIPort') 

202 if self.port > MAXPORT or self.port < 1: 202 ↛ 203line 202 didn't jump to line 203, because the condition on line 202 was never true

203 raise xs_errors.XenError('ISCSIPort') 

204 

205 # For backwards compatibility 

206 if 'usediscoverynumber' in self.dconf: 206 ↛ 207line 206 didn't jump to line 207, because the condition on line 206 was never true

207 self.discoverentry = self.dconf['usediscoverynumber'] 

208 

209 self.multihomed = False 

210 if 'multihomed' in self.dconf: 210 ↛ 211line 210 didn't jump to line 211, because the condition on line 210 was never true

211 if self.dconf['multihomed'] == "true": 

212 self.multihomed = True 

213 elif self.mpath == 'true': 213 ↛ 214line 213 didn't jump to line 214, because the condition on line 213 was never true

214 self.multihomed = True 

215 

216 if 'targetIQN' not in self.dconf or not self.dconf['targetIQN']: 216 ↛ 217line 216 didn't jump to line 217, because the condition on line 216 was never true

217 self._scan_IQNs() 

218 raise xs_errors.XenError('ConfigTargetIQNMissing') 

219 

220 self.targetIQN = self.dconf['targetIQN'] 

221 

222 self._attached = None 

223 self._pathdict = None 

224 self._adapter = None 

225 self._devs = None 

226 self._tgtidx = None 

227 self._path = None 

228 self._address = None 

229 

230 def _initPaths(self): 

231 self._init_adapters() 

232 # Generate a list of all possible paths 

233 self._pathdict = {} 

234 addrlist = [] 

235 rec = {} 

236 key = "%s:%d" % (self.target, self.port) 

237 rec['ipaddr'] = self.target 

238 rec['port'] = self.port 

239 rec['path'] = os.path.join("/dev/iscsi", self.targetIQN, \ 

240 key) 

241 self._pathdict[key] = rec 

242 util.SMlog("PATHDICT: key %s: %s" % (key, rec)) 

243 self._tgtidx = key 

244 addrlist.append(key) 

245 

246 self._path = rec['path'] 

247 self._address = self.tgtidx 

248 if not self.attached: 248 ↛ 249line 248 didn't jump to line 249, because the condition on line 248 was never true

249 return 

250 

251 if self.multihomed: 

252 map = iscsilib.get_node_records(targetIQN=self.targetIQN) 

253 for i in range(0, len(map)): 

254 (portal, tpgt, iqn) = map[i] 

255 (ipaddr, port) = iscsilib.parse_IP_port(portal) 

256 if self.target != ipaddr: 256 ↛ 253line 256 didn't jump to line 253, because the condition on line 256 was never false

257 key = "%s:%s" % (ipaddr, port) 

258 rec = {} 

259 rec['ipaddr'] = ipaddr 

260 rec['port'] = int(port) 

261 rec['path'] = os.path.join("/dev/iscsi", self.targetIQN, \ 

262 key) 

263 self._pathdict[key] = rec 

264 util.SMlog("PATHDICT: key %s: %s" % (key, rec)) 

265 addrlist.append(key) 

266 

267 if not os.path.exists(self.path): 

268 # Try to detect an active path in order of priority 

269 for key in self.pathdict: 269 ↛ 276line 269 didn't jump to line 276, because the loop on line 269 didn't complete

270 if key in self.adapter: 

271 self._tgtidx = key 

272 self._path = self.pathdict[self.tgtidx]['path'] 

273 if os.path.exists(self.path): 273 ↛ 269line 273 didn't jump to line 269, because the condition on line 273 was never false

274 util.SMlog("Path found: %s" % self.path) 

275 break 

276 self._address = self.tgtidx 

277 self._synchroniseAddrList(addrlist) 

278 

279 def _init_adapters(self): 

280 # Generate a list of active adapters 

281 ids = scsiutil._genHostList(ISCSI_PROCNAME) 

282 util.SMlog(ids) 

283 self._adapter = {} 

284 for host in ids: 

285 try: 

286 targetIQN = iscsilib.get_targetIQN(host) 

287 if targetIQN != self.targetIQN: 287 ↛ 288line 287 didn't jump to line 288, because the condition on line 287 was never true

288 continue 

289 (addr, port) = iscsilib.get_targetIP_and_port(host) 

290 entry = "%s:%s" % (addr, port) 

291 self._adapter[entry] = host 

292 except: 

293 pass 

294 self._devs = scsiutil.cacheSCSIidentifiers() 

295 

296 def attach(self, sr_uuid): 

297 self._mpathHandle() 

298 

299 multiTargets = False 

300 

301 npaths = 0 

302 try: 

303 pbdref = util.find_my_pbd(self.session, self.host_ref, self.sr_ref) 

304 if pbdref: 304 ↛ 310line 304 didn't jump to line 310, because the condition on line 304 was never false

305 other_config = self.session.xenapi.PBD.get_other_config(pbdref) 

306 multiTargets = util.sessions_less_than_targets(other_config, self.dconf) 

307 except: 

308 pass 

309 

310 if not self.attached or multiTargets: 310 ↛ 390line 310 didn't jump to line 390, because the condition on line 310 was never false

311 # Verify iSCSI target and port 

312 if 'multihomelist' in self.dconf and 'multiSession' not in self.dconf: 

313 targetlist = self.dconf['multihomelist'].split(',') 

314 else: 

315 targetlist = ['%s:%d' % (self.target, self.port)] 

316 conn = False 

317 for val in targetlist: 317 ↛ 327line 317 didn't jump to line 327, because the loop on line 317 didn't complete

318 (target, port) = iscsilib.parse_IP_port(val) 

319 try: 

320 util._testHost(target, int(port), 'ISCSITarget') 

321 self.target = target 

322 self.port = int(port) 

323 conn = True 

324 break 

325 except: 

326 pass 

327 if not conn: 327 ↛ 328line 327 didn't jump to line 328, because the condition on line 327 was never true

328 raise xs_errors.XenError('ISCSITarget') 

329 

330 # Test and set the initiatorname file 

331 iscsilib.ensure_daemon_running_ok(self.localIQN) 

332 

333 # Check to see if auto attach was set 

334 if not iscsilib._checkTGT(self.targetIQN, tgt=self.target) or multiTargets: 334 ↛ 389line 334 didn't jump to line 389, because the condition on line 334 was never false

335 try: 

336 iqn_map = [] 

337 if 'any' != self.targetIQN: 337 ↛ 346line 337 didn't jump to line 346, because the condition on line 337 was never false

338 try: 

339 iqn_map = iscsilib.get_node_records(self.targetIQN) 

340 except: 

341 # Pass the exception that is thrown, when there 

342 # are no nodes 

343 pass 

344 

345 # Check our current target is in the map 

346 portal = '%s:%d' % (self.target, self.port) 

347 if len(iqn_map) == 0 or not any([x[0] for x in iqn_map if x[0] == portal]): 347 ↛ exit,   347 ↛ 3522 missed branches: 1) line 347 didn't run the list comprehension on line 347, 2) line 347 didn't jump to line 352, because the condition on line 347 was never false

348 iqn_map = iscsilib.discovery(self.target, self.port, 

349 self.chapuser, self.chappassword, 

350 self.targetIQN, 

351 iscsilib.get_iscsi_interfaces()) 

352 if len(iqn_map) == 0: 352 ↛ 353line 352 didn't jump to line 353, because the condition on line 352 was never true

353 self._scan_IQNs() 

354 raise xs_errors.XenError('ISCSIDiscovery', 

355 opterr='check target settings') 

356 for i in range(0, len(iqn_map)): 

357 (portal, tpgt, iqn) = iqn_map[i] 

358 try: 

359 (ipaddr, port) = iscsilib.parse_IP_port(portal) 

360 if not self.multihomed and ipaddr != self.target: 

361 continue 

362 util._testHost(ipaddr, int(port), 'ISCSITarget') 

363 util.SMlog("Logging in to [%s:%s]" % (ipaddr, port)) 

364 iscsilib.login(portal, iqn, self.chapuser, 

365 self.chappassword, 

366 self.incoming_chapuser, 

367 self.incoming_chappassword, 

368 self.mpath == "true") 

369 npaths = npaths + 1 

370 except Exception as e: 

371 # Exceptions thrown in login are acknowledged, 

372 # the rest of exceptions are ignored since some of the 

373 # paths in multipath may not be reachable 

374 if str(e).startswith('ISCSI login'): 

375 raise 

376 else: 

377 pass 

378 

379 if not iscsilib._checkTGT(self.targetIQN, tgt=self.target): 379 ↛ 380line 379 didn't jump to line 380, because the condition on line 379 was never true

380 raise xs_errors.XenError('ISCSIDevice', \ 

381 opterr='during login') 

382 

383 # Allow the devices to settle 

384 time.sleep(5) 

385 

386 except util.CommandException as inst: 

387 raise xs_errors.XenError('ISCSILogin', \ 

388 opterr='code is %d' % inst.code) 

389 self.attached = True 

390 self._initPaths() 

391 util._incr_iscsiSR_refcount(self.targetIQN, sr_uuid) 

392 IQNs = [] 

393 if "multiSession" in self.dconf: 

394 IQNs = "" 

395 for iqn in self.dconf['multiSession'].split("|"): 

396 if len(iqn): 

397 IQNs += iqn.split(',')[2] 

398 else: 

399 IQNs.append(self.targetIQN) 

400 

401 sessions = 0 

402 paths = iscsilib.get_IQN_paths() 

403 for path in paths: 403 ↛ 404line 403 didn't jump to line 404, because the loop on line 403 never started

404 try: 

405 if util.get_single_entry(os.path.join(path, 'targetname')) in IQNs: 

406 sessions += 1 

407 util.SMlog("IQN match. Incrementing sessions to %d" % sessions) 

408 except: 

409 util.SMlog("Failed to read targetname path," \ 

410 + "iscsi_sessions value may be incorrect") 

411 

412 if pbdref: 412 ↛ 420line 412 didn't jump to line 420, because the condition on line 412 was never false

413 # Just to be safe in case of garbage left during crashes 

414 # we remove the key and add it 

415 self.session.xenapi.PBD.remove_from_other_config( 

416 pbdref, "iscsi_sessions") 

417 self.session.xenapi.PBD.add_to_other_config( 

418 pbdref, "iscsi_sessions", str(sessions)) 

419 

420 if 'SCSIid' in self.dconf: 420 ↛ exitline 420 didn't return from function 'attach', because the condition on line 420 was never false

421 if self.mpath == 'true': 421 ↛ 422line 421 didn't jump to line 422, because the condition on line 421 was never true

422 self.mpathmodule.refresh(self.dconf['SCSIid'], 0) 

423 dev_path = os.path.join("/dev/disk/by-scsid", self.dconf['SCSIid']) 

424 if not os.path.exists(dev_path): 

425 raise xs_errors.XenError('ConfigSCSIid') 

426 

427 devs = os.listdir(dev_path) 

428 for dev in devs: 

429 realdev = os.path.realpath(os.path.join(dev_path, dev)) 

430 util.set_scheduler(os.path.basename(realdev)) 

431 

432 def detach(self, sr_uuid, delete=False): 

433 keys = [] 

434 pbdref = None 

435 try: 

436 pbdref = util.find_my_pbd(self.session, self.host_ref, self.sr_ref) 

437 except: 

438 pass 

439 if 'SCSIid' in self.dconf: 

440 self.mpathmodule.reset(self.dconf['SCSIid'], explicit_unmap=True) 

441 keys.append("mpath-" + self.dconf['SCSIid']) 

442 

443 # Remove iscsi_sessions and multipathed keys 

444 if pbdref is not None: 

445 if self.cmd == 'sr_detach': 

446 keys += ["multipathed", "iscsi_sessions"] 

447 for key in keys: 

448 try: 

449 self.session.xenapi.PBD.remove_from_other_config(pbdref, key) 

450 except: 

451 pass 

452 

453 if util._decr_iscsiSR_refcount(self.targetIQN, sr_uuid) != 0: 

454 return 

455 

456 if self.direct and util._containsVDIinuse(self): 

457 return 

458 

459 if iscsilib._checkTGT(self.targetIQN): 

460 try: 

461 iscsilib.logout(self.target, self.targetIQN, all=True) 

462 if delete: 

463 iscsilib.delete(self.targetIQN) 

464 except util.CommandException as inst: 

465 raise xs_errors.XenError('ISCSIQueryDaemon', \ 

466 opterr='error is %d' % inst.code) 

467 if iscsilib._checkTGT(self.targetIQN): 

468 raise xs_errors.XenError('ISCSIQueryDaemon', \ 

469 opterr='Failed to logout from target') 

470 

471 self.attached = False 

472 

473 def create(self, sr_uuid, size): 

474 # Check whether an SR already exists 

475 SRs = self.session.xenapi.SR.get_all_records() 

476 for sr in SRs: 

477 record = SRs[sr] 

478 sm_config = record["sm_config"] 

479 if 'targetIQN' in sm_config and \ 

480 sm_config['targetIQN'] == self.targetIQN: 

481 raise xs_errors.XenError('SRInUse') 

482 self.attach(sr_uuid) 

483 # Wait up to MAX_TIMEOUT for devices to appear 

484 util.wait_for_path(self.path, MAX_TIMEOUT) 

485 

486 if self._loadvdis() > 0: 

487 scanrecord = SR.ScanRecord(self) 

488 scanrecord.synchronise() 

489 try: 

490 self.detach(sr_uuid) 

491 except: 

492 pass 

493 self.sm_config = self.session.xenapi.SR.get_sm_config(self.sr_ref) 

494 self.sm_config['disktype'] = 'Raw' 

495 self.sm_config['datatype'] = 'ISCSI' 

496 self.sm_config['target'] = self.target 

497 self.sm_config['targetIQN'] = self.targetIQN 

498 self.sm_config['multipathable'] = 'true' 

499 self.session.xenapi.SR.set_sm_config(self.sr_ref, self.sm_config) 

500 return 

501 

502 def delete(self, sr_uuid): 

503 self.detach(sr_uuid) 

504 return 

505 

506 def probe(self): 

507 SRs = self.session.xenapi.SR.get_all_records() 

508 Recs = {} 

509 for sr in SRs: 

510 record = SRs[sr] 

511 sm_config = record["sm_config"] 

512 if 'targetIQN' in sm_config and \ 

513 sm_config['targetIQN'] == self.targetIQN: 

514 Recs[record["uuid"]] = sm_config 

515 return self.srlist_toxml(Recs) 

516 

517 def scan(self, sr_uuid): 

518 if not self.passthrough: 

519 if not self.attached: 

520 raise xs_errors.XenError('SRUnavailable') 

521 self.refresh() 

522 time.sleep(2) # it seems impossible to tell when a scan's finished 

523 self._loadvdis() 

524 self.physical_utilisation = self.physical_size 

525 for uuid, vdi in self.vdis.items(): 

526 if vdi.managed: 

527 self.physical_utilisation += vdi.size 

528 self.virtual_allocation = self.physical_utilisation 

529 return super(BaseISCSISR, self).scan(sr_uuid) 

530 

531 def vdi(self, uuid): 

532 return LUNperVDI.RAWVDI(self, uuid) 

533 

534 def _scan_IQNs(self): 

535 # Verify iSCSI target and port 

536 util._testHost(self.target, self.port, 'ISCSITarget') 

537 

538 # Test and set the initiatorname file 

539 iscsilib.ensure_daemon_running_ok(self.localIQN) 

540 

541 map = iscsilib.discovery(self.target, self.port, self.chapuser, 

542 self.chappassword, 

543 interfaceArray=iscsilib.get_iscsi_interfaces()) 

544 map.append(("%s:%d" % (self.targetlist, self.port), "0", "*")) 

545 self.print_entries(map) 

546 

547 def _attach_LUN_bylunid(self, lunid): 

548 if not self.attached: 

549 raise xs_errors.XenError('SRUnavailable') 

550 connected = [] 

551 for val in self.adapter: 

552 if val not in self.pathdict: 

553 continue 

554 rec = self.pathdict[val] 

555 path = os.path.join(rec['path'], "LUN%s" % lunid) 

556 realpath = os.path.realpath(path) 

557 host = self.adapter[val] 

558 l = [realpath, host, 0, 0, lunid] 

559 

560 addDevice = True 

561 if realpath in self.devs: 

562 # if the device is stale remove it before adding again 

563 real_SCSIid = None 

564 try: 

565 real_SCSIid = scsiutil.getSCSIid(realpath) 

566 except: 

567 pass 

568 

569 if real_SCSIid is not None: 

570 # make sure this is the same scsiid, if not remove the device 

571 cur_scsibuspath = glob.glob('/dev/disk/by-scsibus/*-%s:0:0:%s' % (host, lunid)) 

572 cur_SCSIid = os.path.basename(cur_scsibuspath[0]).split("-")[0] 

573 if cur_SCSIid != real_SCSIid: 

574 # looks stale, remove it 

575 scsiutil.scsi_dev_ctrl(l, "remove") 

576 else: 

577 util.SMlog("Not attaching LUNID %s for adapter %s" \ 

578 " since the device exists and the scsi id %s seems" \ 

579 " to be valid. " % (lunid, val, real_SCSIid)) 

580 addDevice = False 

581 else: 

582 # looks stale, remove it 

583 scsiutil.scsi_dev_ctrl(l, "remove") 

584 

585 if addDevice: 

586 # add the device 

587 scsiutil.scsi_dev_ctrl(l, "add") 

588 if not util.wait_for_path(path, MAX_LUNID_TIMEOUT): 

589 util.SMlog("Unable to detect LUN attached to host on path [%s]" % path) 

590 continue 

591 connected.append(path) 

592 return connected 

593 

594 def _attach_LUN_byserialid(self, serialid): 

595 if not self.attached: 

596 raise xs_errors.XenError('SRUnavailable') 

597 connected = [] 

598 for val in self.adapter: 

599 if val not in self.pathdict: 

600 continue 

601 rec = self.pathdict[val] 

602 path = os.path.join(rec['path'], "SERIAL-%s" % serialid) 

603 realpath = os.path.realpath(path) 

604 if realpath not in self.devs: 

605 if not util.wait_for_path(path, 5): 

606 util.SMlog("Unable to detect LUN attached to host on serial path [%s]" % path) 

607 continue 

608 connected.append(path) 

609 return connected 

610 

611 def _detach_LUN_bylunid(self, lunid, SCSIid): 

612 if not self.attached: 

613 raise xs_errors.XenError('SRUnavailable') 

614 if self.mpath == 'true' and len(SCSIid): 

615 self.mpathmodule.reset(SCSIid, explicit_unmap=True) 

616 util.remove_mpathcount_field(self.session, self.host_ref, self.sr_ref, SCSIid) 

617 for val in self.adapter: 

618 if val not in self.pathdict: 

619 continue 

620 rec = self.pathdict[val] 

621 path = os.path.join(rec['path'], "LUN%s" % lunid) 

622 realpath = os.path.realpath(path) 

623 if realpath in self.devs: 

624 util.SMlog("Found key: %s" % realpath) 

625 scsiutil.scsi_dev_ctrl(self.devs[realpath], 'remove') 

626 # Wait for device to disappear 

627 if not util.wait_for_nopath(realpath, MAX_LUNID_TIMEOUT): 

628 util.SMlog("Device has not disappeared after %d seconds" % \ 

629 MAX_LUNID_TIMEOUT) 

630 else: 

631 util.SMlog("Device [%s,%s] disappeared" % (realpath, path)) 

632 

633 def _attach_LUN_bySCSIid(self, SCSIid): 

634 if not self.attached: 

635 raise xs_errors.XenError('SRUnavailable') 

636 

637 path = self.mpathmodule.path(SCSIid) 

638 if not util.pathexists(path): 

639 self.refresh() 

640 if not util.wait_for_path(path, MAX_TIMEOUT): 

641 util.SMlog("Unable to detect LUN attached to host [%s]" 

642 % path) 

643 raise xs_errors.XenError('ISCSIDevice') 

644 

645 # This function queries the session for the attached LUNs 

646 def _loadvdis(self): 

647 count = 0 

648 if not os.path.exists(self.path): 

649 return 0 

650 for file in filter(self.match_lun, util.listdir(self.path)): 

651 vdi_path = os.path.join(self.path, file) 

652 LUNid = file.replace("LUN", "") 

653 uuid = scsiutil.gen_uuid_from_string(scsiutil.getuniqueserial(vdi_path)) 

654 obj = self.vdi(uuid) 

655 obj._query(vdi_path, LUNid) 

656 self.vdis[uuid] = obj 

657 self.physical_size += obj.size 

658 count += 1 

659 return count 

660 

661 def refresh(self): 

662 for val in self.adapter: 

663 util.SMlog("Rescanning host adapter %s" % self.adapter[val]) 

664 scsiutil.rescan([self.adapter[val]]) 

665 

666 # Helper function for LUN-per-VDI VDI.introduce 

667 def _getLUNbySMconfig(self, sm_config): 

668 if 'LUNid' not in sm_config: 

669 raise xs_errors.XenError('VDIUnavailable') 

670 LUNid = int(sm_config['LUNid']) 

671 if not len(self._attach_LUN_bylunid(LUNid)): 

672 raise xs_errors.XenError('VDIUnavailable') 

673 return os.path.join(self.path, "LUN%d" % LUNid) 

674 

675 # This function takes an ISCSI device and populate it with 

676 # a dictionary of available LUNs on that target. 

677 def print_LUNs(self): 

678 self.LUNs = {} 

679 if os.path.exists(self.path): 

680 dom0_disks = util.dom0_disks() 

681 for file in util.listdir(self.path): 

682 if file.find("LUN") != -1 and file.find("_") == -1: 

683 vdi_path = os.path.join(self.path, file) 

684 if os.path.realpath(vdi_path) in dom0_disks: 

685 util.SMlog("Hide dom0 boot disk LUN") 

686 else: 

687 LUNid = file.replace("LUN", "") 

688 obj = self.vdi(self.uuid) 

689 obj._query(vdi_path, LUNid) 

690 self.LUNs[obj.uuid] = obj 

691 

692 def print_entries(self, map): 

693 dom = xml.dom.minidom.Document() 

694 element = dom.createElement("iscsi-target-iqns") 

695 dom.appendChild(element) 

696 count = 0 

697 for address, tpgt, iqn in map: 

698 entry = dom.createElement('TGT') 

699 element.appendChild(entry) 

700 subentry = dom.createElement('Index') 

701 entry.appendChild(subentry) 

702 textnode = dom.createTextNode(str(count)) 

703 subentry.appendChild(textnode) 

704 

705 try: 

706 # We always expect a port so this holds 

707 # regardless of IP version 

708 (addr, port) = address.rsplit(':', 1) 

709 except: 

710 addr = address 

711 port = DEFAULT_PORT 

712 subentry = dom.createElement('IPAddress') 

713 entry.appendChild(subentry) 

714 textnode = dom.createTextNode(str(addr)) 

715 subentry.appendChild(textnode) 

716 

717 if int(port) != DEFAULT_PORT: 

718 subentry = dom.createElement('Port') 

719 entry.appendChild(subentry) 

720 textnode = dom.createTextNode(str(port)) 

721 subentry.appendChild(textnode) 

722 

723 subentry = dom.createElement('TargetIQN') 

724 entry.appendChild(subentry) 

725 textnode = dom.createTextNode(str(iqn)) 

726 subentry.appendChild(textnode) 

727 count += 1 

728 print(dom.toprettyxml(), file=sys.stderr) 

729 

730 def srlist_toxml(self, SRs): 

731 dom = xml.dom.minidom.Document() 

732 element = dom.createElement("SRlist") 

733 dom.appendChild(element) 

734 

735 for val in SRs: 

736 record = SRs[val] 

737 entry = dom.createElement('SR') 

738 element.appendChild(entry) 

739 

740 subentry = dom.createElement("UUID") 

741 entry.appendChild(subentry) 

742 textnode = dom.createTextNode(val) 

743 subentry.appendChild(textnode) 

744 

745 subentry = dom.createElement("Target") 

746 entry.appendChild(subentry) 

747 textnode = dom.createTextNode(record['target']) 

748 subentry.appendChild(textnode) 

749 

750 subentry = dom.createElement("TargetIQN") 

751 entry.appendChild(subentry) 

752 textnode = dom.createTextNode(record['targetIQN']) 

753 subentry.appendChild(textnode) 

754 return dom.toprettyxml() 

755 

756 def match_lun(self, s): 

757 regex = re.compile("_") 

758 if regex.search(s, 0): 

759 return False 

760 regex = re.compile("LUN") 

761 return regex.search(s, 0)