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 

29from cleanup import LOCK_TYPE_RUNNING 

30from configparser import RawConfigParser 

31import io 

32 

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

34_KERNEL_VERSION = os.uname()[2] 

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

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

37 'iscsi_session*/') 

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

39 'session*/connection*/iscsi_connection*/') 

40else: 

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

42 'iscsi_session/session*/') 

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

44 'session*/connection*/iscsi_connection/connection*/') 

45 

46_REPLACEMENT_TMO_MPATH = 15 

47_REPLACEMENT_TMO_DEFAULT = 144 

48_REPLACEMENT_TMO_STANDARD = 120 

49 

50_ISCSI_DB_PATH = '/var/lib/iscsi' 

51 

52 

53def doexec_locked(cmd): 

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

55 _lock = None 

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

57 _lock = lock.Lock(LOCK_TYPE_RUNNING, 'iscsiadm') 

58 _lock.acquire() 

59 # util.SMlog("%s" % cmd) 

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

61 if _lock is not None and _lock.held(): 

62 _lock.release() 

63 return (rc, stdout, stderr) 

64 

65 

66def noexn_on_failure(cmd): 

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

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

69 return (stdout, stderr) 

70 

71 

72def exn_on_failure(cmd, message): 

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

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

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

76 if rc == 0: 

77 return (stdout, stderr) 

78 else: 

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

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

81 

82 

83def parse_node_output(text, targetIQN): 

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

85 get_node_records""" 

86 

87 def dotrans(x): 

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

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

90 return (portal, tpgt, iqn) 

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

92 match_targetIQN(targetIQN, x)] 

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

94 # multiple interfaces 

95 filtered_map = [] 

96 for input_value in unfiltered_map: 

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

98 filtered_map.append(input_value) 

99 return filtered_map 

100 

101 

102def parse_IP_port(portal): 

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

104 

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

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

107 or 

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

109 

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

111 """ 

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

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

114 # This is IPv6, strip off [ ] surround 

115 ipaddr = ipaddr[1:-1] 

116 return (ipaddr, port) 

117 

118 

119def save_rootdisk_nodes(tmpdirname): 

120 root_iqns = get_rootdisk_IQNs() 

121 if root_iqns: 

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

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

124 

125 

126def restore_rootdisk_nodes(tmpdirname): 

127 root_iqns = get_rootdisk_IQNs() 

128 if root_iqns: 

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

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

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

132 

133 

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

135 interfaceArray=["default"]): 

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

137 TargetIQNs available on the specified target and port. Returns 

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

139 group tag) and the target name""" 

140 

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

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

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

144 

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

146 tmpdirname = tempfile.mkdtemp() 

147 try: 

148 save_rootdisk_nodes(tmpdirname) 

149 

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

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

152 else: 

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

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

155 for interface in interfaceArray: 

156 cmd_base.append("-I") 

157 cmd_base.append(interface) 

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

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

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

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

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

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

164 "username/password (if applicable)" 

165 try: 

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

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

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

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

170 else: 

171 cmd = cmd_disc 

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

173 except: 

174 raise xs_errors.XenError('ISCSILogin') 

175 finally: 

176 restore_rootdisk_nodes(tmpdirname) 

177 finally: 

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

179 

180 return parse_node_output(stdout, targetIQN) 

181 

182 

183def get_node_records(targetIQN="any"): 

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

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

186 failuremessage = "Failed to obtain node records from iscsi daemon" 

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

188 return parse_node_output(stdout, targetIQN) 

189 

190 

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

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

193 portal/targetIQN combination""" 

194 failuremessage = "Failed to set CHAP settings" 

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

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

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

198 

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

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

201 username] 

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

203 

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

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

206 password] 

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

208 

209 if (username_in != ""): 

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

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

212 username_in] 

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

214 

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

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

217 password_in] 

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

219 

220 

221def remove_chap_settings(portal, targetIQN): 

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

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

224 (stdout, stderr) = noexn_on_failure(cmd) 

225 

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

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

228 (stdout, stderr) = noexn_on_failure(cmd) 

229 

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

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

232 (stdout, stderr) = noexn_on_failure(cmd) 

233 

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

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

236 (stdout, stderr) = noexn_on_failure(cmd) 

237 

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

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

240 (stdout, stderr) = noexn_on_failure(cmd) 

241 

242 

243def get_node_config (portal, targetIQN): 

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

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

246 failuremessage = "Failed to get node configurations" 

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

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

249 ini_sec = "root" 

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

251 parser = RawConfigParser() 

252 parser.readfp(str_fp) 

253 str_fp.close() 

254 return dict(parser.items(ini_sec)) 

255 

256 

257def set_replacement_tmo (portal, targetIQN, mpath): 

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

259 try: 

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

261 except: 

262 # Assume a standard TMO setting if get_node_config fails 

263 current_tmo = _REPLACEMENT_TMO_STANDARD 

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

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

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

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

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

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

270 fail_msg = "Failed to set replacement timeout" 

271 if mpath: 

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

273 if current_tmo in [_REPLACEMENT_TMO_DEFAULT, _REPLACEMENT_TMO_STANDARD]: 

274 cmd.append(str(_REPLACEMENT_TMO_MPATH)) 

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

276 else: 

277 # the current_tmo is a customized value, no change 

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

279 else: 

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

281 if current_tmo in [_REPLACEMENT_TMO_MPATH, _REPLACEMENT_TMO_STANDARD]: 

282 cmd.append(str(_REPLACEMENT_TMO_DEFAULT)) 

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

284 else: 

285 # the current_tmo is a customized value, no change 

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

287 

288 

289def get_current_initiator_name(): 

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

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

292 if os.path.exists(INITIATORNAME_FILE): 

293 try: 

294 f = open(INITIATORNAME_FILE, 'r') 

295 for line in f.readlines(): 

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

297 continue 

298 if "InitiatorName" in line: 

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

300 currentIQN = IQN.strip() 

301 f.close() 

302 return currentIQN 

303 f.close() 

304 except IOError as e: 

305 return None 

306 return None 

307 

308 

309def get_system_alias(): 

310 return socket.gethostname() 

311 

312 

313def set_current_initiator_name(localIQN): 

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

315 try: 

316 alias = get_system_alias() 

317 # MD3000i alias bug workaround 

318 if len(alias) > 30: 

319 alias = alias[0:30] 

320 f = open(INITIATORNAME_FILE, 'w') 

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

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

323 f.close() 

324 except IOError as e: 

325 raise xs_errors.XenError('ISCSIInitiator', \ 

326 opterr='Could not set initator name') 

327 

328 

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

330 multipath=False): 

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

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

333 else: 

334 remove_chap_settings(portal, target) 

335 

336 set_replacement_tmo(portal, target, multipath) 

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

338 failuremessage = "Failed to login to target." 

339 try: 

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

341 except: 

342 raise xs_errors.XenError('ISCSILogin') 

343 

344 

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

346 if all: 

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

348 else: 

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

350 failuremessage = "Failed to log out of target" 

351 try: 

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

353 except: 

354 raise xs_errors.XenError('ISCSILogout') 

355 

356 

357def delete(target): 

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

359 failuremessage = "Failed to delete target" 

360 try: 

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

362 except: 

363 raise xs_errors.XenError('ISCSIDelete') 

364 

365 

366def get_luns(targetIQN, portal): 

367 refresh_luns(targetIQN, portal) 

368 luns = [] 

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

370 try: 

371 for file in util.listdir(path): 

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

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

374 luns.append(lun) 

375 return luns 

376 except util.CommandException as inst: 

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

378 

379 

380def is_iscsi_daemon_running(): 

381 cmd = ["/sbin/pidof", "-s", "/sbin/iscsid"] 

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

383 return (rc == 0) 

384 

385 

386def stop_daemon(): 

387 if is_iscsi_daemon_running(): 

388 cmd = ["service", "iscsid", "stop"] 

389 failuremessage = "Failed to stop iscsi daemon" 

390 exn_on_failure(cmd, failuremessage) 

391 

392 

393def restart_daemon(): 

394 stop_daemon() 

395 if os.path.exists(os.path.join(_ISCSI_DB_PATH, 'nodes')): 395 ↛ 404line 395 didn't jump to line 404, because the condition on line 395 was never false

396 try: 

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

398 except: 

399 pass 

400 try: 

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

402 except: 

403 pass 

404 cmd = ["service", "iscsid", "start"] 

405 failuremessage = "Failed to start iscsi daemon" 

406 exn_on_failure(cmd, failuremessage) 

407 

408 

409def wait_for_devs(targetIQN, portal): 

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

411 for i in range(0, 15): 

412 if os.path.exists(path): 

413 return True 

414 time.sleep(1) 

415 return False 

416 

417 

418def refresh_luns(targetIQN, portal): 

419 wait_for_devs(targetIQN, portal) 

420 try: 

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

422 id = scsiutil.getSessionID(path) 

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

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

425 f.close() 

426 time.sleep(2) # FIXME 

427 except: 

428 pass 

429 

430 

431def get_IQN_paths(): 

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

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

434 

435 

436def get_targetIQN(iscsi_host): 

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

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

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

440 return targetIQN 

441 

442 

443def get_targetIP_and_port(iscsi_host): 

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

445 connection_dir = _GENERIC_CONNECTION_PATH % iscsi_host 

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

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

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

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

450 return (ip, port) 

451 

452 

453def get_path(targetIQN, portal, lun): 

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

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

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

457 

458 

459def get_path_safe(targetIQN, portal, lun): 

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

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

462 path = get_path(targetIQN, portal, lun) 

463 for i in range(0, 15): 

464 if os.path.exists(path): 

465 return path 

466 time.sleep(1) 

467 raise xs_errors.XenError('ISCSIDevice', \ 

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

469 

470 

471def match_target(tgt, s): 

472 regex = re.compile(tgt) 

473 return regex.search(s, 0) 

474 

475 

476def match_targetIQN(tgtIQN, s): 

477 if not len(s): 477 ↛ 478line 477 didn't jump to line 478, because the condition on line 477 was never true

478 return False 

479 if tgtIQN == "any": 479 ↛ 483line 479 didn't jump to line 483, because the condition on line 479 was never false

480 return True 

481 # Extract IQN from iscsiadm -m session 

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

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

484 return (siqn == tgtIQN) 

485 

486 

487def match_session(s): 

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

489 return regex.search(s, 0) 

490 

491 

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

493 if not is_iscsi_daemon_running(): 

494 return False 

495 failuremessage = "Failure occured querying iscsi daemon" 

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

497 try: 

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

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

500 # Quick and dirty handling 

501 except Exception as e: 

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

503 stdout = "" 

504 for line in stdout.split('\n'): 

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

506 if len(tgt): 

507 if match_target(tgt, line): 

508 return True 

509 else: 

510 return True 

511 return False 

512 

513 

514def get_rootdisk_IQNs(): 

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

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

517 return [] 

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

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

520 

521 

522def _checkAnyTGT(): 

523 if not is_iscsi_daemon_running(): 

524 return False 

525 rootIQNs = get_rootdisk_IQNs() 

526 failuremessage = "Failure occured querying iscsi daemon" 

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

528 try: 

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

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

531 # Quick and dirty handling 

532 except Exception as e: 

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

534 stdout = "" 

535 for e in filter(match_session, stdout.split('\n')): 

536 iqn = e.split()[-1] 

537 if not iqn in rootIQNs: 

538 return True 

539 return False 

540 

541 

542def ensure_daemon_running_ok(localiqn): 

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

544 if not is_iscsi_daemon_running(): 

545 set_current_initiator_name(localiqn) 

546 restart_daemon() 

547 else: 

548 currentiqn = get_current_initiator_name() 

549 if currentiqn != localiqn: 

550 if _checkAnyTGT(): 

551 raise xs_errors.XenError('ISCSIInitiator', \ 

552 opterr='Daemon already running with ' \ 

553 + 'target(s) attached using ' \ 

554 + 'different IQN') 

555 set_current_initiator_name(localiqn) 

556 restart_daemon() 

557 

558 

559def get_iscsi_interfaces(): 

560 result = [] 

561 try: 

562 # Get all configured iscsiadm interfaces 

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

564 (stdout, stderr) = exn_on_failure(cmd, 

565 "Failure occured querying iscsi daemon") 

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

567 # tcp,<empty>,<empty>,<empty>,<empty> 

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

569 line_element = line.split(" ") 

570 interface_name = line_element[0] 

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

572 # c_. 

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

574 result.append(interface_name) 

575 except: 

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

577 # interface 

578 pass 

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

580 # interface 

581 if len(result) == 0: 

582 result.append("default") 

583 return result