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"""Temporary module to mock libiscsi""" 

2 

3import re 

4 

5from . import util 

6from . import f_exceptions 

7 

8 

9def exn_on_failure(cmd, message): 

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

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

12 

13 # _lock = None 

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

15 # _lock = lock.Lock(LOCK_TYPE_RUNNING, 'iscsiadm') 

16 # _lock.acquire() 

17 

18 (ret, stdout, stderr) = util.doexec(cmd) 

19 # if _lock <> None and _lock.held(): 

20 # _lock.release() 

21 if ret == 0: 

22 return (stdout, stderr) 

23 else: 

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

25 raise f_exceptions.XenError('SMGeneral', message+' '+msg) 

26 

27 

28def discovery(target, port, chapuser, chappass, target_iqn="any", 

29 interface_array=("default",)): 

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

31 TargetIQNs available on the specified target and port. Returns 

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

33 group tag) and the target name""" 

34 

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

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

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

38# save_rootdisk_nodes() 

39 

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

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

42 else: 

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

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

45 for interface in interface_array: 

46 cmd_base.append("-I") 

47 cmd_base.append(interface) 

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

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

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

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

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

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

54 "username/password (if applicable)" 

55 try: 

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

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

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

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

60 else: 

61 cmd = cmd_disc 

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

63 except Exception as exc: 

64# restore_rootdisk_nodes() 

65 raise f_exceptions.XenError('ISCSILogin', message=exc.message) 

66# else: 

67# restore_rootdisk_nodes() 

68 

69 return parse_node_output(stdout, target_iqn) 

70 

71 

72def set_chap_settings (portal, targetIQN, username, password): 

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

74 portal/targetIQN combination""" 

75 

76 failuremessage = "Failed to set CHAP settings" 

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

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

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

80 

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

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

83 username] 

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

85 

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

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

88 password] 

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

90 

91 

92def match_target_iqn(tgt_iqn, tgt_string): 

93 """doc placeholder""" 

94 if not len(tgt_string): 

95 return False 

96 if tgt_iqn == "any": 96 ↛ 100line 96 didn't jump to line 100, because the condition on line 96 was never false

97 return True 

98 # Extract IQN from iscsiadm -m session 

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

100 siqn = tgt_string.split(",")[1].split()[1] 

101 return siqn == tgt_iqn 

102 

103 

104def parse_node_output(text, target_iqn): 

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

106 get_node_records""" 

107 def dotrans(x): 

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

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

110 return (portal, tpgt, iqn) 

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

112 match_target_iqn(target_iqn, x)] 

113 

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

115 # multiple interfaces 

116 filtered_map = [] 

117 for input_value in unfiltered_map: 

118 if input_value not in filtered_map: 

119 filtered_map.append(input_value) 

120 return filtered_map 

121 

122 

123def get_portal_target_ref(portal, target): 

124 return '{}#{}'.format(portal, target) 

125 

126 

127def login(portal, target, username, password, uuid, rescan=False): 

128 """This is a slightly revised version of iscsilib.login in SM. 

129 

130 We are trying to stick as close as possible to the original one but fixing 

131 or improving whatever can be improved, in order to later fix it to the 

132 original one and start using that one both for the transport code and 

133 the old SM code. 

134 

135 For example, 'uuid' is a new addition in order to be able to automatically 

136 refcount in here, instead of relying on the caller to do that. 

137 """ 

138 

139 if username != "" and password != "": 139 ↛ 140line 139 didn't jump to line 140, because the condition on line 139 was never true

140 set_chap_settings(portal, target, username, password) 

141 

142 

143 refcount = 0 

144 legacy_refcount = 0 

145 

146 portal_target_ref = get_portal_target_ref(portal, target) 

147 

148 # Increment portal/target ref count 

149 refcount = util._incr_iscsiSR_refcount(portal_target_ref, uuid) 

150 

151 if refcount == 1: 

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

153 try: 

154 failuremessage = "Failed to login to target." 

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

156 # Increment legacy refcounter 

157 if uuid: 157 ↛ 170line 157 didn't jump to line 170, because the condition on line 157 was never false

158 legacy_refcount = util._incr_iscsiSR_refcount(target, uuid) 

159 except Exception as exc: 

160 util.SMlog("Failed: {}".format(" ".join(cmd)), 

161 ident="Transport") 

162 # Rollback refcount if needed 

163 if uuid: 

164 # This should be in its own try/block and be chained 

165 # with main exception below 

166 util._decr_iscsiSR_refcount(portal_target_ref, uuid) 

167 raise f_exceptions.XenError('ISCSILogin', message=exc.message) 

168 

169 # Rescan if requested and more than one refcount of either form 

170 if rescan and (refcount > 1 or legacy_refcount > 1): 

171 util.SMlog("Session already logged in for {}, rescan requested". 

172 format(target)) 

173 rescan_target(portal, target) 

174 

175def logout(portal, target, uuid): 

176 """Modified version of iscsilib.py:logout to handle refcounting 

177 

178 Given the nature of this refcounting, it is not possible to specifically 

179 logout from one ip/iqn pair without breaking the refcounting. 

180 For this reason, this version does not accept a specific ip and the 

181 parameter 'all'. 

182 """ 

183 util.SMlog("decrease logout refcount of {} ({})". 

184 format(target, uuid), 

185 ident="Transport") 

186 

187 portal_target_ref = get_portal_target_ref(portal, target) 

188 

189 if util._decr_iscsiSR_refcount(portal_target_ref, uuid): 

190 # still logged in 

191 return 

192 

193 # Decrement the legacy refcount 

194 if not util._decr_iscsiSR_refcount(target, uuid): 

195 try: 

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

197 failuremessage = "Failed to log out of target" 

198 util.SMlog("logging out {} for {}". 

199 format(target, uuid), 

200 ident="Transport") 

201 (_, _) = exn_on_failure(cmd, failuremessage) 

202 except Exception as exc: 

203 raise f_exceptions.XenError('ISCSILogout', message=exc.message) 

204 

205def rescan_target(portal, target): 

206 """ 

207 Rescan the given iscsi portal and target 

208 """ 

209 sessions_cmd = ["iscsiadm", "-m", "session"] 

210 failure_message = "Failed to read iscsi sessions" 

211 (stdout, _) = exn_on_failure(sessions_cmd, failure_message) 

212 

213 session_re = r"^[^\[]*\[(\d+)\] " + portal + r",\d+ " + target 

214 

215 for line in stdout.splitlines(): 

216 session_match = re.match(session_re, line) 

217 if session_match: 217 ↛ 215line 217 didn't jump to line 215, because the condition on line 217 was never false

218 session_id = session_match.group(1) 

219 rescan_cmd = ["iscsiadm", "-m", "session", '-r', session_id, '--rescan'] 

220 (_, _) = exn_on_failure(rescan_cmd, "Failed to rescan session") 

221 

222 

223def get_iscsi_interfaces(): 

224 result = [] 

225 try: 

226 # Get all configured iscsiadm interfaces 

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

228 (stdout, stderr) = exn_on_failure(cmd, 

229 "Failure occured querying iscsi daemon") 

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

231 # tcp,<empty>,<empty>,<empty>,<empty> 

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

233 line_element = line.split(" ") 

234 interface_name = line_element[0] 

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

236 # c_. 

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

238 result.append(interface_name) 

239 except: 

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

241 # interface 

242 pass 

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

244 # interface 

245 if len(result) == 0: 

246 result.append("default") 

247 return result