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

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

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


Revision 1.2 - (hide annotations) (download) (as text)
Sun Dec 22 04:27:51 2013 UTC (10 years, 10 months ago) by wellsi
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +0 -0 lines
Content type: text/x-python
FILE REMOVED
anaconda updates now handled via patches

1 charliebrady 1.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