/[smeserver]/cdrom.image/sme9/updates/storage/iscsi.py
ViewVC logotype

Contents of /cdrom.image/sme9/updates/storage/iscsi.py

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (show annotations) (download) (as text)
Tue Jul 30 21:01:52 2013 UTC (11 years, 3 months ago) by charliebrady
Branch: MAIN
Content type: text/x-python
Add storage module from anaconda to updates directory, so that parts of it can be modified.

1 #
2 # iscsi.py - iscsi class
3 #
4 # Copyright (C) 2005, 2006 IBM, Inc. All rights reserved.
5 # Copyright (C) 2006 Red Hat, Inc. All rights reserved.
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #
20
21 from constants import *
22 from udev import *
23 import os
24 import iutil
25 from flags import flags
26 import logging
27 import shutil
28 import time
29 import hashlib
30 import random
31 import itertools
32 log = logging.getLogger("anaconda")
33
34 import gettext
35 _ = lambda x: gettext.ldgettext("anaconda", x)
36
37 has_libiscsi = True
38 try:
39 import libiscsi
40 except ImportError:
41 has_libiscsi = False
42
43 # Note that stage2 copies all files under /sbin to /usr/sbin
44 ISCSID=""
45 INITIATOR_FILE="/etc/iscsi/initiatorname.iscsi"
46
47 ISCSI_MODULES=['cxgb3i', 'bnx2i', 'be2iscsi']
48
49 def find_iscsi_files():
50 global ISCSID
51 if ISCSID == "":
52 for dir in ("/usr/sbin", "/tmp/updates", "/mnt/source/RHupdates"):
53 path="%s/iscsid" % (dir,)
54 if os.access(path, os.X_OK):
55 ISCSID=path
56
57 def has_iscsi():
58 find_iscsi_files()
59 if ISCSID == "" or not has_libiscsi:
60 return False
61
62 log.info("ISCSID is %s" % (ISCSID,))
63
64 # make sure the module is loaded
65 if not os.access("/sys/module/iscsi_tcp", os.X_OK):
66 return False
67 return True
68
69 def randomIname():
70 """Generate a random initiator name the same way as iscsi-iname"""
71
72 s = "iqn.1994-05.com.domain:01."
73 m = hashlib.md5()
74 u = os.uname()
75 for i in u:
76 m.update(i)
77 dig = m.hexdigest()
78
79 for i in range(0, 6):
80 s += dig[random.randrange(0, 32)]
81 return s
82
83 class iscsi(object):
84 """ iSCSI utility class.
85
86 This class will automatically discover and login to iBFT (or
87 other firmware) configured iscsi devices when the startup() method
88 gets called. It can also be used to manually configure iscsi devices
89 through the addTarget() method.
90
91 As this class needs to make sure certain things like starting iscsid
92 and logging in to firmware discovered disks only happens once
93 and as it keeps a global list of all iSCSI devices it is implemented as
94 a Singleton.
95 """
96
97 def __init__(self):
98 # Dictionary of discovered targets containing list of (node,
99 # logged_in) tuples.
100 self.discovered_targets = {}
101 # This list contains nodes discovered through iBFT (or other firmware)
102 self.ibftNodes = []
103 self._initiator = ""
104 self.initiatorSet = False
105 self.started = False
106 self.ifaces = {}
107
108 if flags.ibft:
109 try:
110 initiatorname = libiscsi.get_firmware_initiator_name()
111 self._initiator = initiatorname
112 self.initiatorSet = True
113 except:
114 pass
115
116 # So that users can write iscsi() to get the singleton instance
117 def __call__(self):
118 return self
119
120 def _getInitiator(self):
121 if self._initiator != "":
122 return self._initiator
123
124 return randomIname()
125
126 def _setInitiator(self, val):
127 if self.initiatorSet and val != self._initiator:
128 raise ValueError, _("Unable to change iSCSI initiator name once set")
129 if len(val) == 0:
130 raise ValueError, _("Must provide an iSCSI initiator name")
131 self._initiator = val
132
133 initiator = property(_getInitiator, _setInitiator)
134
135 def active_nodes(self, target=None):
136 """Nodes logged in to"""
137 if target:
138 return [node for (node, logged_in) in
139 self.discovered_targets.get(target, [])
140 if logged_in]
141 else:
142 return [node for (node, logged_in) in
143 itertools.chain(*self.discovered_targets.values())
144 if logged_in] + self.ibftNodes
145
146 def _getMode(self):
147 if not self.active_nodes():
148 return "none"
149 if self.ifaces:
150 return "bind"
151 else:
152 return "default"
153
154 mode = property(_getMode)
155
156 def _mark_node_active(self, node, active=True):
157 """Mark node as one logged in to
158
159 Returns False if not found
160 """
161 for target_nodes in self.discovered_targets.values():
162 for nodeinfo in target_nodes:
163 if nodeinfo[0] is node:
164 nodeinfo[1] = active
165 return True
166 return False
167
168 def _startIBFT(self, intf = None):
169 if not flags.ibft:
170 return
171
172 try:
173 found_nodes = libiscsi.discover_firmware()
174 except:
175 log.info("iscsi: No IBFT info found.");
176 return
177
178 for node in found_nodes:
179 try:
180 node.login()
181 log.info("iscsi IBFT: logged into %s at %s:%s through %s" % (
182 node.name, node.address, node.port, node.iface))
183 self.ibftNodes.append(node)
184 except IOError, e:
185 log.error("Could not log into ibft iscsi target %s: %s" %
186 (node.name, str(e)))
187 pass
188
189 self.stabilize(intf)
190
191 def stabilize(self, intf = None):
192 # Wait for udev to create the devices for the just added disks
193 if intf:
194 w = intf.waitWindow(_("Scanning iSCSI nodes"),
195 _("Scanning iSCSI nodes"))
196 # It is possible when we get here the events for the new devices
197 # are not send yet, so sleep to make sure the events are fired
198 time.sleep(2)
199 udev_settle()
200 if intf:
201 w.pop()
202
203 def create_interfaces(self, ifaces):
204 for iface in ifaces:
205 iscsi_iface_name = "iface%d" % len(self.ifaces)
206 #iscsiadm -m iface -I iface0 --op=new
207 iutil.execWithRedirect("iscsiadm",
208 ["-m", "iface", "-I", iscsi_iface_name, "--op=new"],
209 stdout="/dev/tty5",
210 stderr="/dev/tty5")
211 #iscsiadm -m iface -I iface0 --op=update -n iface.net_ifacename -v eth0
212 iutil.execWithRedirect("iscsiadm",
213 ["-m", "iface", "-I", iscsi_iface_name,
214 "--op=update", "-n",
215 "iface.net_ifacename", "-v", iface],
216 stdout="/dev/tty5",
217 stderr="/dev/tty5")
218
219 self.ifaces[iscsi_iface_name] = iface
220 log.debug("created_interface %s:%s" % (iscsi_iface_name, iface))
221
222 def delete_interfaces(self):
223 if not self.ifaces:
224 return None
225 for iscsi_iface_name in self.ifaces:
226 #iscsiadm -m iface -I iface0 --op=delete
227 iutil.execWithRedirect("iscsiadm",
228 ["-m", "iface", "-I", iscsi_iface_name,
229 "--op=delete"],
230 stdout="/dev/tty5",
231 stderr="/dev/tty5")
232 self.ifaces = {}
233
234 def startup(self, intf = None):
235 if self.started:
236 return
237
238 if not has_iscsi():
239 return
240
241 if self._initiator == "":
242 log.info("no initiator set")
243 return
244
245 if intf:
246 w = intf.waitWindow(_("Initializing iSCSI initiator"),
247 _("Initializing iSCSI initiator"))
248
249 log.debug("Setting up %s" % (INITIATOR_FILE, ))
250 log.info("iSCSI initiator name %s", self.initiator)
251 if os.path.exists(INITIATOR_FILE):
252 os.unlink(INITIATOR_FILE)
253 if not os.path.isdir("/etc/iscsi"):
254 os.makedirs("/etc/iscsi", 0755)
255 fd = os.open(INITIATOR_FILE, os.O_RDWR | os.O_CREAT)
256 os.write(fd, "InitiatorName=%s\n" %(self.initiator))
257 os.close(fd)
258 self.initiatorSet = True
259
260 for dir in ['ifaces','isns','nodes','send_targets','slp','static']:
261 fulldir = "/var/lib/iscsi/%s" % (dir,)
262 if not os.path.isdir(fulldir):
263 os.makedirs(fulldir, 0755)
264
265 log.info("iSCSI startup")
266 iutil.execWithRedirect('modprobe', ['-a'] + ISCSI_MODULES,
267 stdout="/dev/tty5", stderr="/dev/tty5")
268 # this is needed by Broadcom offload cards (bnx2i)
269 iscsiuio = iutil.find_program_in_path('iscsiuio',
270 raise_on_error=True)
271 log.debug("iscsi: iscsiuio is at %s" % iscsiuio)
272 iutil.execWithRedirect(iscsiuio, [],
273 stdout="/dev/tty5", stderr="/dev/tty5")
274 # run the daemon
275 iutil.execWithRedirect(ISCSID, [],
276 stdout="/dev/tty5", stderr="/dev/tty5")
277 time.sleep(1)
278
279 if intf:
280 w.pop()
281
282 self._startIBFT(intf)
283 self.started = True
284
285 def discover(self, ipaddr, port="3260", username=None, password=None,
286 r_username=None, r_password=None, intf=None):
287 """
288 Discover iSCSI nodes on the target available for login.
289
290 If we are logged in a node discovered for specified target
291 do not do the discovery again as it can corrupt credentials
292 stored for the node (setAuth and getAuth are using database
293 in /var/lib/iscsi/nodes which is filled by discovery). Just
294 return nodes obtained and stored in the first discovery
295 instead.
296
297 Returns list of nodes user can log in.
298 """
299 authinfo = None
300
301 if not has_iscsi():
302 raise IOError, _("iSCSI not available")
303 if self._initiator == "":
304 raise ValueError, _("No initiator name set")
305
306 if self.active_nodes((ipaddr, port)):
307 log.debug("iSCSI: skipping discovery of %s:%s due to active nodes" %
308 (ipaddr, port))
309 else:
310 if username or password or r_username or r_password:
311 # Note may raise a ValueError
312 authinfo = libiscsi.chapAuthInfo(username=username,
313 password=password,
314 reverse_username=r_username,
315 reverse_password=r_password)
316 self.startup(intf)
317
318 # Note may raise an IOError
319 found_nodes = libiscsi.discover_sendtargets(address=ipaddr,
320 port=int(port),
321 authinfo=authinfo)
322 if found_nodes is None:
323 return None
324 self.discovered_targets[(ipaddr, port)] = []
325 for node in found_nodes:
326 self.discovered_targets[(ipaddr, port)].append([node, False])
327 log.debug("discovered iSCSI node: %s" % node.name)
328
329 # only return the nodes we are not logged into yet
330 return [node for (node, logged_in) in
331 self.discovered_targets[(ipaddr, port)]
332 if not logged_in]
333
334 def log_into_node(self, node, username=None, password=None,
335 r_username=None, r_password=None, intf=None):
336 """
337 Raises IOError.
338 """
339 rc = False # assume failure
340 msg = ""
341
342 if intf:
343 w = intf.waitWindow(_("Logging in to iSCSI node"),
344 _("Logging in to iSCSI node %s") % node.name)
345 try:
346 authinfo = None
347 if username or password or r_username or r_password:
348 # may raise a ValueError
349 authinfo = libiscsi.chapAuthInfo(username=username,
350 password=password,
351 reverse_username=r_username,
352 reverse_password=r_password)
353 node.setAuth(authinfo)
354 node.login()
355 rc = True
356 log.info("iSCSI: logged into %s at %s:%s through %s" % (
357 node.name, node.address, node.port, node.iface))
358 if not self._mark_node_active(node):
359 log.error("iSCSI: node not found among discovered")
360 except (IOError, ValueError) as e:
361 msg = str(e)
362 log.warning("iSCSI: could not log into %s: %s" % (node.name, msg))
363 if intf:
364 w.pop()
365
366 return (rc, msg)
367
368 # NOTE: the same credentials are used for discovery and login
369 # (unlike in UI)
370 def addTarget(self, ipaddr, port="3260", user=None, pw=None,
371 user_in=None, pw_in=None, intf=None, target=None, iface=None):
372 found = 0
373 logged_in = 0
374
375 found_nodes = self.discover(ipaddr, port, user, pw, user_in, pw_in,
376 intf)
377 if found_nodes == None:
378 raise IOError, _("No iSCSI nodes discovered")
379
380 for node in found_nodes:
381 if target and target != node.name:
382 log.debug("iscsi: skipping logging to iscsi node '%s'" %
383 node.name)
384 continue
385 if iface:
386 node_net_iface = self.ifaces.get(node.iface, node.iface)
387 if iface != node_net_iface:
388 log.debug("iscsi: skipping logging to iscsi node '%s' via %s" %
389 (node.name, node_net_iface))
390 continue
391
392 found = found + 1
393
394 (rc, msg) = self.log_into_node(node, user, pw, user_in, pw_in,
395 intf)
396 if rc:
397 logged_in = logged_in +1
398
399 if found == 0:
400 raise IOError, _("No new iSCSI nodes discovered")
401
402 if logged_in == 0:
403 raise IOError, _("Could not log in to any of the discovered nodes")
404
405 self.stabilize(intf)
406
407 def writeKS(self, f):
408
409 if not self.initiatorSet:
410 return
411
412 nodes = ""
413 for n in self.active_nodes():
414 if n in self.ibftNodes:
415 continue
416 nodes += "iscsi --ipaddr %s --port %s --target %s" % (n.address, n.port, n.name)
417 if n.iface != "default":
418 nodes += " --iface %s" % self.ifaces[n.iface]
419 auth = n.getAuth()
420 if auth:
421 nodes += " --user %s" % auth.username
422 nodes += " --password %s" % auth.password
423 if len(auth.reverse_username):
424 nodes += " --reverse-user %s" % auth.reverse_username
425 if len(auth.reverse_password):
426 nodes += " --reverse-password %s" % auth.reverse_password
427 nodes += "\n"
428
429 if nodes:
430 f.write("iscsiname %s\n" %(self.initiator,))
431 f.write("%s" % nodes)
432
433 def write(self, instPath, anaconda):
434 if not self.initiatorSet:
435 return
436
437 # set iscsi nodes to autostart
438 root = anaconda.id.storage.rootDevice
439 for node in self.active_nodes():
440 autostart = True
441 disks = self.getNodeDisks(node, anaconda.id.storage)
442 for disk in disks:
443 # nodes used for root get started by the initrd
444 if root.dependsOn(disk):
445 autostart = False
446
447 if autostart:
448 node.setParameter("node.startup", "automatic")
449
450 if not os.path.isdir(instPath + "/etc/iscsi"):
451 os.makedirs(instPath + "/etc/iscsi", 0755)
452 fd = os.open(instPath + INITIATOR_FILE, os.O_RDWR | os.O_CREAT)
453 os.write(fd, "InitiatorName=%s\n" %(self.initiator))
454 os.close(fd)
455
456 # copy "db" files. *sigh*
457 if os.path.isdir(instPath + "/var/lib/iscsi"):
458 shutil.rmtree(instPath + "/var/lib/iscsi")
459 if os.path.isdir("/var/lib/iscsi"):
460 shutil.copytree("/var/lib/iscsi", instPath + "/var/lib/iscsi",
461 symlinks=True)
462
463 def getNode(self, name, address, port, iface):
464 for node in self.active_nodes():
465 if node.name == name and node.address == address and \
466 node.port == int(port) and node.iface == iface:
467 return node
468
469 return None
470
471 def getNodeDisks(self, node, storage):
472 nodeDisks = []
473 iscsiDisks = storage.devicetree.getDevicesByType("iscsi")
474 for disk in iscsiDisks:
475 if disk.node == node:
476 nodeDisks.append(disk)
477
478 return nodeDisks
479
480 # Create iscsi singleton
481 iscsi = iscsi()
482
483 # vim:tw=78:ts=4:et:sw=4

admin@koozali.org
ViewVC Help
Powered by ViewVC 1.2.1 RSS 2.0 feed