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# Copyright (C) Citrix Systems Inc. 

2# 

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

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

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

6# 

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

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

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

10# GNU Lesser General Public License for more details. 

11# 

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

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

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

15 

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

17 

18import util 

19import os 

20import scsiutil 

21import time 

22import socket 

23import re 

24import shutil 

25import xs_errors 

26import lock 

27import glob 

28import tempfile 

29import contextlib 

30from configparser import RawConfigParser 

31import io 

32from functools import wraps 

33from typing import Iterator 

34 

35 

36# The 3.x kernel brings with it some iSCSI path changes in sysfs 

37_KERNEL_VERSION = os.uname()[2] 

38if _KERNEL_VERSION.startswith('2.6'): 38 ↛ 39line 38 didn't jump to line 39, because the condition on line 38 was never true

39 _GENERIC_SESSION_PATH = ('/sys/class/iscsi_host/host%s/device/session*/' + 

40 'iscsi_session*/') 

41 _GENERIC_CONNECTION_PATH = ('/sys/class/iscsi_host/host%s/device/' + 

42 'session*/connection*/iscsi_connection*/') 

43else: 

44 _GENERIC_SESSION_PATH = ('/sys/class/iscsi_host/host%s/device/session*/' + 

45 'iscsi_session/session*/') 

46 _GENERIC_CONNECTION_PATH = ('/sys/class/iscsi_host/host%s/device/' + 

47 'session*/connection*/iscsi_connection/connection*/') 

48 

49_REPLACEMENT_TMO_MPATH = 15 

50_REPLACEMENT_TMO_DEFAULT = 144 

51_REPLACEMENT_TMO_STANDARD = 120 

52 

53_ISCSI_DB_PATH = '/var/lib/iscsi' 

54 

55 

56@contextlib.contextmanager 

57def iscsi_lock() -> Iterator[None]: 

58 """Context manager to serialize iscsiadm calls. Blocks until lock is available.""" 

59 iscsiadm_lock = lock.Lock(lock.LOCK_TYPE_ISCSIADM_RUNNING, 'iscsiadm') 

60 iscsiadm_lock.acquire() 

61 try: 

62 yield 

63 finally: 

64 iscsiadm_lock.release() 

65 

66 

67@contextlib.contextmanager 

68def iscsi_try_lock() -> Iterator[bool]: 

69 """ 

70 Context manager to serialize iscsiadm calls. 

71 Yields True if the lock was acquired, False immediately if unavailable. 

72 """ 

73 iscsiadm_lock = lock.Lock(lock.LOCK_TYPE_ISCSIADM_RUNNING, 'iscsiadm') 

74 acquired = iscsiadm_lock.acquireNoblock() 

75 try: 

76 yield acquired 

77 finally: 

78 if acquired: 78 ↛ exitline 78 didn't return from function 'iscsi_try_lock', because the condition on line 78 was never false

79 iscsiadm_lock.release() 

80 

81 

82def with_iscsi_lock(fn): 

83 """Decorator to run a function whilst holding the iSCSI lock.""" 

84 @wraps(fn) 

85 def wrapper(*args, **kwargs): 

86 with iscsi_lock(): 

87 return fn(*args, **kwargs) 

88 return wrapper 

89 

90 

91def doexec_locked(cmd): 

92 """Executes via util.doexec the command specified whilst holding lock""" 

93 if os.path.basename(cmd[0]) == 'iscsiadm': 

94 with iscsi_lock(): 

95 return util.doexec(cmd) 

96 return util.doexec(cmd) 

97 

98 

99def noexn_on_failure(cmd): 

100 """Executes via util.doexec the command specified as best effort.""" 

101 (rc, stdout, stderr) = doexec_locked(cmd) 

102 return (stdout, stderr) 

103 

104 

105def exn_on_failure(cmd, message): 

106 """Executes via util.doexec the command specified. If the return code is 

107 non-zero, raises an ISCSIError with the given message""" 

108 (rc, stdout, stderr) = doexec_locked(cmd) 

109 if rc == 0: 

110 return (stdout, stderr) 

111 else: 

112 msg = 'rc: %d, stdout: %s, stderr: %s' % (rc, stdout, stderr) 

113 raise xs_errors.XenError('SMGeneral', opterr=msg) 

114 

115 

116def parse_node_output(text, targetIQN): 

117 """helper function - parses the output of iscsiadm for discovery and 

118 get_node_records""" 

119 

120 def dotrans(x): 

121 (rec, iqn) = x.split() 

122 (portal, tpgt) = rec.split(',') 

123 return (portal, tpgt, iqn) 

124 unfiltered_map = [dotrans(x) for x in text.split('\n') if 

125 match_targetIQN(targetIQN, x)] 

126 # We need to filter duplicates orignating from doing the discovery using 

127 # multiple interfaces 

128 filtered_map = [] 

129 for input_value in unfiltered_map: 

130 if input_value not in filtered_map: 130 ↛ 129line 130 didn't jump to line 129, because the condition on line 130 was never false

131 filtered_map.append(input_value) 

132 return filtered_map 

133 

134 

135def parse_IP_port(portal): 

136 """Extract IP address and port number from portal information. 

137 

138 Input: String encoding the IP address and port of form: 

139 - x.x.x.x:p (IPv4) 

140 or 

141 - [xxxx:xxxx:...:xxxx]:p (IPv6) 

142 

143 Return tuple of IP and port (without square brackets in case of IPv6): 

144 """ 

145 (ipaddr, port) = portal.split(',')[0].rsplit(':', 1) 

146 if ipaddr[0] == '[': 146 ↛ 148line 146 didn't jump to line 148, because the condition on line 146 was never true

147 # This is IPv6, strip off [ ] surround 

148 ipaddr = ipaddr[1:-1] 

149 return (ipaddr, port) 

150 

151 

152def save_rootdisk_nodes(tmpdirname): 

153 root_iqns = get_rootdisk_IQNs() 

154 if root_iqns: 

155 srcdirs = [os.path.join(_ISCSI_DB_PATH, 'nodes', iqn) for iqn in root_iqns] 

156 util.doexec(['/bin/cp', '-a'] + srcdirs + [tmpdirname]) 

157 

158 

159def restore_rootdisk_nodes(tmpdirname): 

160 root_iqns = get_rootdisk_IQNs() 

161 if root_iqns: 

162 srcdirs = [os.path.join(tmpdirname, iqn) for iqn in root_iqns] 

163 util.doexec(['/bin/cp', '-a'] + srcdirs + 

164 [os.path.join(_ISCSI_DB_PATH, 'nodes')]) 

165 

166 

167def discovery(target, port, chapuser, chappass, targetIQN="any", 

168 interfaceArray=["default"]): 

169 """Run iscsiadm in discovery mode to obtain a list of the  

170 TargetIQNs available on the specified target and port. Returns 

171 a list of triples - the portal (ip:port), the tpgt (target portal 

172 group tag) and the target name""" 

173 

174 # Save configuration of root LUN nodes and restore after discovery 

175 # otherwise when we do a discovery on the same filer as is hosting 

176 # our root disk we'll reset the config of the root LUNs 

177 

178 # FIXME: Replace this with TemporaryDirectory when moving to Python3 

179 tmpdirname = tempfile.mkdtemp() 

180 try: 

181 save_rootdisk_nodes(tmpdirname) 

182 

183 if ':' in target: 183 ↛ 184line 183 didn't jump to line 184, because the condition on line 183 was never true

184 targetstring = "[%s]:%s" % (target, str(port)) 

185 else: 

186 targetstring = "%s:%s" % (target, str(port)) 

187 cmd_base = ["-t", "st", "-p", targetstring] 

188 for interface in interfaceArray: 

189 cmd_base.append("-I") 

190 cmd_base.append(interface) 

191 cmd_disc = ["iscsiadm", "-m", "discovery"] + cmd_base 

192 cmd_discdb = ["iscsiadm", "-m", "discoverydb"] + cmd_base 

193 auth_args = ["-n", "discovery.sendtargets.auth.authmethod", "-v", "CHAP", 

194 "-n", "discovery.sendtargets.auth.username", "-v", chapuser, 

195 "-n", "discovery.sendtargets.auth.password", "-v", chappass] 

196 fail_msg = "Discovery failed. Check target settings and " \ 

197 "username/password (if applicable)" 

198 try: 

199 if chapuser != "" and chappass != "": 

200 exn_on_failure(cmd_discdb + ["-o", "new"], fail_msg) 

201 exn_on_failure(cmd_discdb + ["-o", "update"] + auth_args, fail_msg) 

202 cmd = cmd_discdb + ["--discover"] 

203 else: 

204 cmd = cmd_disc 

205 (stdout, stderr) = exn_on_failure(cmd, fail_msg) 

206 except: 

207 raise xs_errors.XenError('ISCSILogin') 

208 finally: 

209 restore_rootdisk_nodes(tmpdirname) 

210 finally: 

211 shutil.rmtree(tmpdirname) 211 ↛ exitline 211 didn't except from function 'discovery', because the raise on line 207 wasn't executed

212 

213 return parse_node_output(stdout, targetIQN) 

214 

215 

216def get_node_records(targetIQN="any"): 

217 """Return the node records that the iscsi daemon already knows about""" 

218 cmd = ["iscsiadm", "-m", "node"] 

219 (rc, stdout, stderr) = doexec_locked(cmd) 

220 if rc != 0: 

221 util.SMlog(f"get_node_records: iscsiadm rc={rc} stderr={stderr}") 

222 return [] 

223 return parse_node_output(stdout, targetIQN) 

224 

225 

226def set_chap_settings (portal, targetIQN, username, password, username_in, password_in): 

227 """Sets the username and password on the session identified by the  

228 portal/targetIQN combination""" 

229 failuremessage = "Failed to set CHAP settings" 

230 cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", targetIQN, "--op", 

231 "update", "-n", "node.session.auth.authmethod", "-v", "CHAP"] 

232 (stdout, stderr) = exn_on_failure(cmd, failuremessage) 

233 

234 cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", targetIQN, "--op", 

235 "update", "-n", "node.session.auth.username", "-v", 

236 username] 

237 (stdout, stderr) = exn_on_failure(cmd, failuremessage) 

238 

239 cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", targetIQN, "--op", 

240 "update", "-n", "node.session.auth.password", "-v", 

241 password] 

242 (stdout, stderr) = exn_on_failure(cmd, failuremessage) 

243 

244 if (username_in != ""): 

245 cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", targetIQN, "--op", 

246 "update", "-n", "node.session.auth.username_in", "-v", 

247 username_in] 

248 (stdout, stderr) = exn_on_failure(cmd, failuremessage) 

249 

250 cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", targetIQN, "--op", 

251 "update", "-n", "node.session.auth.password_in", "-v", 

252 password_in] 

253 (stdout, stderr) = exn_on_failure(cmd, failuremessage) 

254 

255 

256def remove_chap_settings(portal, targetIQN): 

257 cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", targetIQN, "--op", 

258 "update", "-n", "node.session.auth.authmethod", "-v", "None"] 

259 (stdout, stderr) = noexn_on_failure(cmd) 

260 

261 cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", targetIQN, "--op", 

262 "update", "-n", "node.session.auth.username", "-v", ""] 

263 (stdout, stderr) = noexn_on_failure(cmd) 

264 

265 cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", targetIQN, "--op", 

266 "update", "-n", "node.session.auth.password", "-v", ""] 

267 (stdout, stderr) = noexn_on_failure(cmd) 

268 

269 cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", targetIQN, "--op", 

270 "update", "-n", "node.session.auth.username_in", "-v", ""] 

271 (stdout, stderr) = noexn_on_failure(cmd) 

272 

273 cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", targetIQN, "--op", 

274 "update", "-n", "node.session.auth.password_in", "-v", ""] 

275 (stdout, stderr) = noexn_on_failure(cmd) 

276 

277 

278def get_node_config (portal, targetIQN): 

279 """ Using iscsadm to get the current configuration of a iscsi node. 

280 The output is parsed in ini format, and returned as a dictionary.""" 

281 failuremessage = "Failed to get node configurations" 

282 cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", targetIQN, "-S"] 

283 (stdout, stderr) = exn_on_failure(cmd, failuremessage) 

284 ini_sec = "root" 

285 str_fp = io.StringIO("[%s]\n%s" % (ini_sec, stdout)) 

286 parser = RawConfigParser() 

287 parser.read_file(str_fp) 

288 str_fp.close() 

289 return dict(parser.items(ini_sec)) 

290 

291 

292def set_replacement_tmo (portal, targetIQN, mpath): 

293 key = "node.session.timeo.replacement_timeout" 

294 try: 

295 current_tmo = int((get_node_config(portal, targetIQN))[key]) 

296 except: 

297 # Assume a standard TMO setting if get_node_config fails 

298 current_tmo = _REPLACEMENT_TMO_STANDARD 

299 # deliberately leave the "-p portal" arguments out, so that all the portals 

300 # always share the same config (esp. in corner case when switching from 

301 # mpath -> non-mpath, where we are only going to operate on one path). The 

302 # parameter could be useful if we want further flexibility in the future. 

303 cmd = ["iscsiadm", "-m", "node", "-T", targetIQN, # "-p", portal, 

304 "--op", "update", "-n", key, "-v"] 

305 fail_msg = "Failed to set replacement timeout" 

306 if mpath: 

307 # Only switch if the current config is a well-known non-mpath setting 

308 if current_tmo in [_REPLACEMENT_TMO_DEFAULT, _REPLACEMENT_TMO_STANDARD]: 

309 cmd.append(str(_REPLACEMENT_TMO_MPATH)) 

310 (stdout, stderr) = exn_on_failure(cmd, fail_msg) 

311 else: 

312 # the current_tmo is a customized value, no change 

313 util.SMlog("Keep the current replacement_timout value: %d." % current_tmo) 

314 else: 

315 # Only switch if the current config is a well-known mpath setting 

316 if current_tmo in [_REPLACEMENT_TMO_MPATH, _REPLACEMENT_TMO_STANDARD]: 

317 cmd.append(str(_REPLACEMENT_TMO_DEFAULT)) 

318 (stdout, stderr) = exn_on_failure(cmd, fail_msg) 

319 else: 

320 # the current_tmo is a customized value, no change 

321 util.SMlog("Keep the current replacement_timout value: %d." % current_tmo) 

322 

323 

324def get_current_initiator_name(): 

325 """Looks in the config file to see if we've already got a initiator name,  

326 returning it if so, or else returning None""" 

327 if os.path.exists(INITIATORNAME_FILE): 

328 try: 

329 f = open(INITIATORNAME_FILE, 'r') 

330 for line in f.readlines(): 

331 if line.strip().startswith("#"): 

332 continue 

333 if "InitiatorName" in line: 

334 IQN = line.split("=")[1] 

335 currentIQN = IQN.strip() 

336 f.close() 

337 return currentIQN 

338 f.close() 

339 except IOError as e: 

340 return None 

341 return None 

342 

343 

344def get_system_alias(): 

345 return socket.gethostname() 

346 

347 

348def set_current_initiator_name(localIQN): 

349 """Sets the initiator name in the config file. Raises an xs_error on error""" 

350 try: 

351 alias = get_system_alias() 

352 # MD3000i alias bug workaround 

353 if len(alias) > 30: 

354 alias = alias[0:30] 

355 f = open(INITIATORNAME_FILE, 'w') 

356 f.write('InitiatorName=%s\n' % localIQN) 

357 f.write('InitiatorAlias=%s\n' % alias) 

358 f.close() 

359 except IOError as e: 

360 raise xs_errors.XenError('ISCSIInitiator', \ 

361 opterr='Could not set initator name') 

362 

363 

364def login(portal, target, username, password, username_in="", password_in="", 

365 multipath=False): 

366 if username != "" and password != "": 

367 set_chap_settings(portal, target, username, password, username_in, password_in) 

368 else: 

369 remove_chap_settings(portal, target) 

370 

371 set_replacement_tmo(portal, target, multipath) 

372 cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", target, "-l"] 

373 failuremessage = "Failed to login to target." 

374 try: 

375 (stdout, stderr) = exn_on_failure(cmd, failuremessage) 

376 except: 

377 raise xs_errors.XenError('ISCSILogin') 

378 

379 

380def logout(portal, target, all=False): 

381 if all: 

382 cmd = ["iscsiadm", "-m", "node", "-T", target, "-u"] 

383 else: 

384 cmd = ["iscsiadm", "-m", "node", "-p", portal, "-T", target, "-u"] 

385 failuremessage = "Failed to log out of target" 

386 try: 

387 (stdout, stderr) = exn_on_failure(cmd, failuremessage) 

388 except: 

389 raise xs_errors.XenError('ISCSILogout') 

390 

391 

392def delete(target): 

393 cmd = ["iscsiadm", "-m", "node", "-o", "delete", "-T", target] 

394 failuremessage = "Failed to delete target" 

395 try: 

396 (stdout, stderr) = exn_on_failure(cmd, failuremessage) 

397 except: 

398 raise xs_errors.XenError('ISCSIDelete') 

399 

400 

401def get_luns(targetIQN, portal): 

402 refresh_luns(targetIQN, portal) 

403 luns = [] 

404 path = os.path.join("/dev/iscsi", targetIQN, portal) 

405 try: 

406 for file in util.listdir(path): 

407 if file.find("LUN") == 0 and file.find("_") == -1: 

408 lun = file.replace("LUN", "") 

409 luns.append(lun) 

410 return luns 

411 except util.CommandException as inst: 

412 raise xs_errors.XenError('ISCSIDevice', opterr='Failed to find any LUNs') 

413 

414 

415def is_iscsi_daemon_running(): 

416 cmd = ["/usr/bin/systemctl", "is-active", "iscsid.service"] 

417 (rc, stdout, stderr) = util.doexec(cmd) 

418 return (rc == 0) 

419 

420 

421def stop_daemon(): 

422 if is_iscsi_daemon_running(): 

423 cmd = ["/usr/bin/systemctl", "stop", "iscsid.service"] 

424 failuremessage = "Failed to stop iscsi daemon" 

425 exn_on_failure(cmd, failuremessage) 

426 

427 

428@with_iscsi_lock 

429def restart_daemon(): 

430 stop_daemon() 

431 with contextlib.suppress(OSError): 

432 shutil.rmtree(os.path.join(_ISCSI_DB_PATH, 'nodes')) 

433 with contextlib.suppress(OSError): 

434 shutil.rmtree(os.path.join(_ISCSI_DB_PATH, 'send_targets')) 

435 cmd = ["/usr/bin/systemctl", "start", "iscsid.service"] 

436 failuremessage = "Failed to start iscsi daemon" 

437 exn_on_failure(cmd, failuremessage) 

438 

439 

440def wait_for_devs(targetIQN, portal): 

441 path = os.path.join("/dev/iscsi", targetIQN, portal) 

442 for i in range(0, 15): 

443 if os.path.exists(path): 

444 return True 

445 time.sleep(1) 

446 return False 

447 

448 

449def refresh_luns(targetIQN, portal): 

450 wait_for_devs(targetIQN, portal) 

451 try: 

452 path = os.path.join("/dev/iscsi", targetIQN, portal) 

453 id = scsiutil.getSessionID(path) 

454 f = open('/sys/class/scsi_host/host%s/scan' % id, 'w') 

455 f.write('- - -\n') 

456 f.close() 

457 time.sleep(2) # FIXME 

458 except: 

459 pass 

460 

461 

462def get_IQN_paths(): 

463 """Return the list of iSCSI session directories""" 

464 return glob.glob(_GENERIC_SESSION_PATH % '*') 

465 

466 

467def get_targetIQN(iscsi_host): 

468 """Get target IQN from sysfs for given iSCSI host number""" 

469 iqn_file = os.path.join(_GENERIC_SESSION_PATH % iscsi_host, 'targetname') 

470 targetIQN = util.get_single_entry(glob.glob(iqn_file)[0]) 

471 return targetIQN 

472 

473 

474def get_targetIP_and_port(iscsi_host): 

475 """Get target IP address and port for given iSCSI host number""" 

476 connection_dir = _GENERIC_CONNECTION_PATH % iscsi_host 

477 ip = util.get_single_entry(glob.glob(os.path.join( 

478 connection_dir, 'persistent_address'))[0]) 

479 port = util.get_single_entry(glob.glob(os.path.join( 

480 connection_dir, 'persistent_port'))[0]) 

481 return (ip, port) 

482 

483 

484def get_path(targetIQN, portal, lun): 

485 """Gets the path of a specified LUN - this should be e.g. '1' or '5'""" 

486 path = os.path.join("/dev/iscsi", targetIQN, portal) 

487 return os.path.join(path, "LUN" + lun) 

488 

489 

490def get_path_safe(targetIQN, portal, lun): 

491 """Gets the path of a specified LUN, and ensures that it exists. 

492 Raises an exception if it hasn't appeared after the timeout""" 

493 path = get_path(targetIQN, portal, lun) 

494 for i in range(0, 15): 

495 if os.path.exists(path): 

496 return path 

497 time.sleep(1) 

498 raise xs_errors.XenError('ISCSIDevice', \ 

499 opterr='LUN failed to appear at path %s' % path) 

500 

501 

502def match_target(tgt, s): 

503 regex = re.compile(tgt) 

504 return regex.search(s, 0) 

505 

506 

507def match_targetIQN(tgtIQN, s): 

508 if not len(s): 

509 return False 

510 if tgtIQN == "any": 

511 return True 

512 # Extract IQN from iscsiadm -m session 

513 # Ex: tcp: [17] 10.220.98.9:3260,1 iqn.2009-01.xenrt.test:iscsi4181a93e 

514 siqn = s.split(",")[1].split()[1].strip() 

515 return (siqn == tgtIQN) 

516 

517 

518def match_session(s): 

519 regex = re.compile("^tcp:") 

520 return regex.search(s, 0) 

521 

522 

523def _compare_sessions_to_tgt(session_output, tgtIQN, tgt=''): 

524 for line in session_output.split('\n'): 

525 if match_targetIQN(tgtIQN, line) and match_session(line): 

526 if len(tgt): 526 ↛ 530line 526 didn't jump to line 530, because the condition on line 526 was never false

527 if match_target(tgt, line): 

528 return True 

529 else: 

530 return True 

531 return False 

532 

533 

534def _checkTGT(tgtIQN, tgt=''): 

535 if not is_iscsi_daemon_running(): 

536 return False 

537 failuremessage = "Failure occured querying iscsi daemon" 

538 cmd = ["iscsiadm", "-m", "session"] 

539 try: 

540 (stdout, stderr) = exn_on_failure(cmd, failuremessage) 

541 # Recent versions of iscsiadm return error it this list is empty. 

542 # Quick and dirty handling 

543 except Exception as e: 

544 util.SMlog("%s failed with %s" % (cmd, e.args)) 

545 stdout = "" 

546 return _compare_sessions_to_tgt(stdout, tgtIQN, tgt) 

547 

548 

549def get_rootdisk_IQNs(): 

550 """Return the list of IQNs for targets required by root filesystem""" 

551 if not os.path.isdir('/sys/firmware/ibft/'): 551 ↛ 553line 551 didn't jump to line 553, because the condition on line 551 was never false

552 return [] 

553 dirs = [x for x in os.listdir('/sys/firmware/ibft/') if x.startswith('target')] 

554 return [open('/sys/firmware/ibft/%s/target-name' % d).read().strip() for d in dirs] 

555 

556 

557def _checkAnyTGT(): 

558 if not is_iscsi_daemon_running(): 

559 return False 

560 rootIQNs = get_rootdisk_IQNs() 

561 failuremessage = "Failure occured querying iscsi daemon" 

562 cmd = ["iscsiadm", "-m", "session"] 

563 try: 

564 (stdout, stderr) = exn_on_failure(cmd, failuremessage) 

565 # Recent versions of iscsiadm return error it this list is empty. 

566 # Quick and dirty handling 

567 except Exception as e: 

568 util.SMlog("%s failed with %s" % (cmd, e.args)) 

569 stdout = "" 

570 for session in filter(match_session, stdout.split('\n')): 

571 iqn = session.split()[-1] 

572 if not iqn in rootIQNs: 

573 return True 

574 return False 

575 

576 

577def ensure_daemon_running_ok(localiqn): 

578 """Check that the daemon is running and the initiator name is correct""" 

579 if not is_iscsi_daemon_running(): 

580 set_current_initiator_name(localiqn) 

581 restart_daemon() 

582 else: 

583 currentiqn = get_current_initiator_name() 

584 if currentiqn != localiqn: 

585 if _checkAnyTGT(): 

586 raise xs_errors.XenError('ISCSIInitiator', \ 

587 opterr='Daemon already running with ' \ 

588 + 'target(s) attached using ' \ 

589 + 'different IQN') 

590 set_current_initiator_name(localiqn) 

591 restart_daemon() 

592 

593 

594def get_iscsi_interfaces(): 

595 result = [] 

596 try: 

597 # Get all configured iscsiadm interfaces 

598 cmd = ["iscsiadm", "-m", "iface"] 

599 (stdout, stderr) = exn_on_failure(cmd, 

600 "Failure occured querying iscsi daemon") 

601 # Get the interface (first column) from a line such as default 

602 # tcp,<empty>,<empty>,<empty>,<empty> 

603 for line in stdout.split("\n"): 

604 line_element = line.split(" ") 

605 interface_name = line_element[0] 

606 # ignore interfaces which aren't marked as starting with 

607 # c_. 

608 if len(line_element) == 2 and interface_name[:2] == "c_": 

609 result.append(interface_name) 

610 except: 

611 # Ignore exception from exn on failure, still return the default 

612 # interface 

613 pass 

614 # In case there are no configured interfaces, still add the default 

615 # interface 

616 if len(result) == 0: 

617 result.append("default") 

618 return result