Coverage for sm/core/libiscsi.py : 83%

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"""
3import re
5from . import util
6from . import f_exceptions
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"""
13 # _lock = None
14 # if os.path.basename(cmd[0]) == 'iscsiadm':
15 # _lock = lock.Lock(LOCK_TYPE_RUNNING, 'iscsiadm')
16 # _lock.acquire()
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)
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"""
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()
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()
69 return parse_node_output(stdout, target_iqn)
72def set_chap_settings (portal, targetIQN, username, password):
73 """Sets the username and password on the session identified by the
74 portal/targetIQN combination"""
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)
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)
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)
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
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)]
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
123def get_portal_target_ref(portal, target):
124 return '{}#{}'.format(portal, target)
127def login(portal, target, username, password, uuid, rescan=False):
128 """This is a slightly revised version of iscsilib.login in SM.
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.
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 """
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)
143 refcount = 0
144 legacy_refcount = 0
146 portal_target_ref = get_portal_target_ref(portal, target)
148 # Increment portal/target ref count
149 refcount = util._incr_iscsiSR_refcount(portal_target_ref, uuid)
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)
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)
175def logout(portal, target, uuid):
176 """Modified version of iscsilib.py:logout to handle refcounting
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")
187 portal_target_ref = get_portal_target_ref(portal, target)
189 if util._decr_iscsiSR_refcount(portal_target_ref, uuid):
190 # still logged in
191 return
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)
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)
213 session_re = r"^[^\[]*\[(\d+)\] " + portal + r",\d+ " + target
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")
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