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

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

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


Revision 1.1 - (hide annotations) (download) (as text)
Tue Jul 30 21:01:52 2013 UTC (10 years, 11 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 charliebrady 1.1 #
2     # zfcp.py - mainframe zfcp configuration install data
3     #
4     # Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. All rights reserved.
5     #
6     # This program is free software; you can redistribute it and/or modify
7     # it under the terms of the GNU General Public License as published by
8     # the Free Software Foundation; either version 2 of the License, or
9     # (at your option) any later version.
10     #
11     # This program is distributed in the hope that it will be useful,
12     # but WITHOUT ANY WARRANTY; without even the implied warranty of
13     # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     # GNU General Public License for more details.
15     #
16     # You should have received a copy of the GNU General Public License
17     # along with this program. If not, see <http://www.gnu.org/licenses/>.
18     #
19     # Author(s): Karsten Hopp <karsten@redhat.com>
20     #
21    
22     import string
23     import os
24     from constants import *
25     from udev import udev_settle
26    
27     import gettext
28     _ = lambda x: gettext.ldgettext("anaconda", x)
29    
30     import iutil
31     import logging
32     log = logging.getLogger("anaconda")
33    
34     def loggedWriteLineToFile(fn, value):
35     f = open(fn, "w")
36     log.debug("echo %s > %s" % (value, fn))
37     f.write("%s\n" % (value))
38     f.close()
39    
40     zfcpsysfs = "/sys/bus/ccw/drivers/zfcp"
41     scsidevsysfs = "/sys/bus/scsi/devices"
42     zfcpconf = "/etc/zfcp.conf"
43    
44     class ZFCPDevice:
45     def __init__(self, devnum, wwpn, fcplun):
46     self.devnum = self.sanitizeDeviceInput(devnum)
47     self.wwpn = self.sanitizeWWPNInput(wwpn)
48     self.fcplun = self.sanitizeFCPLInput(fcplun)
49    
50     if not self.checkValidDevice(self.devnum):
51     raise ValueError, _("You have not specified a device number or the number is invalid")
52     if not self.checkValidWWPN(self.wwpn):
53     raise ValueError, _("You have not specified a worldwide port name or the name is invalid.")
54     if not self.checkValidFCPLun(self.fcplun):
55     raise ValueError, _("You have not specified a FCP LUN or the number is invalid.")
56    
57     def __str__(self):
58     return "%s %s %s" %(self.devnum, self.wwpn, self.fcplun)
59    
60     def sanitizeDeviceInput(self, dev):
61     if dev is None or dev == "":
62     return None
63     dev = dev.lower()
64     bus = dev[:string.rfind(dev, ".") + 1]
65     dev = dev[string.rfind(dev, ".") + 1:]
66     dev = "0" * (4 - len(dev)) + dev
67     if not len(bus):
68     return "0.0." + dev
69     else:
70     return bus + dev
71    
72     def sanitizeWWPNInput(self, id):
73     if id is None or id == "":
74     return None
75     id = id.lower()
76     if id[:2] != "0x":
77     return "0x" + id
78     return id
79    
80     # ZFCP LUNs are usually entered as 16 bit, sysfs accepts only 64 bit
81     # (#125632), expand with zeroes if necessary
82     def sanitizeFCPLInput(self, lun):
83     if lun is None or lun == "":
84     return None
85     lun = lun.lower()
86     if lun[:2] == "0x":
87     lun = lun[2:]
88     lun = "0x" + "0" * (4 - len(lun)) + lun
89     lun = lun + "0" * (16 - len(lun) + 2)
90     return lun
91    
92     def _hextest(self, hex):
93     try:
94     int(hex, 16)
95     return True
96     except TypeError:
97     return False
98    
99     def checkValidDevice(self, id):
100     if id is None or id == "":
101     return False
102     if len(id) != 8: # p.e. 0.0.0600
103     return False
104     if id[0] not in string.digits or id[2] not in string.digits:
105     return False
106     if id[1] != "." or id[3] != ".":
107     return False
108     return self._hextest(id[4:])
109    
110     def checkValid64BitHex(self, hex):
111     if hex is None or hex == "":
112     return False
113     if len(hex) != 18:
114     return False
115     return self._hextest(hex)
116     checkValidWWPN = checkValidFCPLun = checkValid64BitHex
117    
118     def onlineDevice(self):
119     online = "%s/%s/online" %(zfcpsysfs, self.devnum)
120     portadd = "%s/%s/port_add" %(zfcpsysfs, self.devnum)
121     portdir = "%s/%s/%s" %(zfcpsysfs, self.devnum, self.wwpn)
122     unitadd = "%s/unit_add" %(portdir)
123     unitdir = "%s/%s" %(portdir, self.fcplun)
124     failed = "%s/failed" %(unitdir)
125    
126     if not os.path.exists(online):
127     log.info("Freeing zFCP device %s" % (self.devnum,))
128     iutil.execWithRedirect("zfcp_cio_free", ["-d", self.devnum],
129     stdout="/dev/tty5", stderr="/dev/tty5")
130    
131     if not os.path.exists(online):
132     raise ValueError, _(
133     "zFCP device %s not found, not even in device ignore list."
134     %(self.devnum,))
135    
136     try:
137     f = open(online, "r")
138     devonline = f.readline().strip()
139     f.close()
140     if devonline != "1":
141     loggedWriteLineToFile(online, "1")
142     except IOError as e:
143     raise ValueError, _("Could not set zFCP device %(devnum)s "
144     "online (%(e)s).") \
145     % {'devnum': self.devnum, 'e': e}
146    
147     if not os.path.exists(portdir):
148     if os.path.exists(portadd):
149     # older zfcp sysfs interface
150     try:
151     loggedWriteLineToFile(portadd, self.wwpn)
152     udev_settle()
153     except IOError as e:
154     raise ValueError, _("Could not add WWPN %(wwpn)s to zFCP "
155     "device %(devnum)s (%(e)s).") \
156     % {'wwpn': self.wwpn,
157     'devnum': self.devnum,
158     'e': e}
159     else:
160     # newer zfcp sysfs interface with auto port scan
161     raise ValueError, _("WWPN %(wwpn)s not found at zFCP device "
162     "%(devnum)s.") % {'wwpn': self.wwpn,
163     'devnum': self.devnum}
164     else:
165     if os.path.exists(portadd):
166     # older zfcp sysfs interface
167     log.info("WWPN %(wwpn)s at zFCP device %(devnum)s already "
168     "there.") % {'wwpn': self.wwpn,
169     'devnum': self.devnum}
170    
171     if not os.path.exists(unitdir):
172     try:
173     loggedWriteLineToFile(unitadd, self.fcplun)
174     udev_settle()
175     except IOError as e:
176     raise ValueError, _("Could not add LUN %(fcplun)s to WWPN "
177     "%(wwpn)s on zFCP device %(devnum)s "
178     "(%(e)s).") \
179     % {'fcplun': self.fcplun, 'wwpn': self.wwpn,
180     'devnum': self.devnum, 'e': e}
181     else:
182     raise ValueError, _("LUN %(fcplun)s at WWPN %(wwpn)s on zFCP "
183     "device %(devnum)s already configured.") \
184     % {'fcplun': self.fcplun,
185     'wwpn': self.wwpn,
186     'devnum': self.devnum}
187    
188     fail = "0"
189     try:
190     f = open(failed, "r")
191     fail = f.readline().strip()
192     f.close()
193     except IOError as e:
194     raise ValueError, _("Could not read failed attribute of LUN "
195     "%(fcplun)s at WWPN %(wwpn)s on zFCP device "
196     "%(devnum)s (%(e)s).") \
197     % {'fcplun': self.fcplun,
198     'wwpn': self.wwpn,
199     'devnum': self.devnum,
200     'e': e}
201     if fail != "0":
202     self.offlineDevice()
203     raise ValueError, _("Failed LUN %(fcplun)s at WWPN %(wwpn)s on "
204     "zFCP device %(devnum)s removed again.") \
205     % {'fcplun': self.fcplun,
206     'wwpn': self.wwpn,
207     'devnum': self.devnum}
208    
209     return True
210    
211     def offlineSCSIDevice(self):
212     f = open("/proc/scsi/scsi", "r")
213     lines = f.readlines()
214     f.close()
215     # alternatively iterate over /sys/bus/scsi/devices/*:0:*:*/
216    
217     for line in lines:
218     if not line.startswith("Host"):
219     continue
220     scsihost = string.split(line)
221     host = scsihost[1]
222     channel = "0"
223     id = scsihost[5]
224     lun = scsihost[7]
225     scsidev = "%s:%s:%s:%s" % (host[4:], channel, id, lun)
226     fcpsysfs = "%s/%s" % (scsidevsysfs, scsidev)
227     scsidel = "%s/%s/delete" % (scsidevsysfs, scsidev)
228    
229     f = open("%s/hba_id" %(fcpsysfs), "r")
230     fcphbasysfs = f.readline().strip()
231     f.close()
232     f = open("%s/wwpn" %(fcpsysfs), "r")
233     fcpwwpnsysfs = f.readline().strip()
234     f.close()
235     f = open("%s/fcp_lun" %(fcpsysfs), "r")
236     fcplunsysfs = f.readline().strip()
237     f.close()
238    
239     if fcphbasysfs == self.devnum \
240     and fcpwwpnsysfs == self.wwpn \
241     and fcplunsysfs == self.fcplun:
242     loggedWriteLineToFile(scsidel, "1")
243     udev_settle()
244     return
245    
246     log.warn("no scsi device found to delete for zfcp %s %s %s"
247     %(self.devnum, self.wwpn, self.fcplun))
248    
249     def offlineDevice(self):
250     offline = "%s/%s/online" %(zfcpsysfs, self.devnum)
251     portadd = "%s/%s/port_add" %(zfcpsysfs, self.devnum)
252     portremove = "%s/%s/port_remove" %(zfcpsysfs, self.devnum)
253     unitremove = "%s/%s/%s/unit_remove" %(zfcpsysfs, self.devnum, self.wwpn)
254     portdir = "%s/%s/%s" %(zfcpsysfs, self.devnum, self.wwpn)
255     devdir = "%s/%s" %(zfcpsysfs, self.devnum)
256    
257     try:
258     self.offlineSCSIDevice()
259     except IOError as e:
260     raise ValueError, _("Could not correctly delete SCSI device of "
261     "zFCP %(devnum)s %(wwpn)s %(fcplun)s "
262     "(%(e)s).") \
263     % {'devnum': self.devnum, 'wwpn': self.wwpn,
264     'fcplun': self.fcplun, 'e': e}
265    
266     try:
267     loggedWriteLineToFile(unitremove, self.fcplun)
268     except IOError as e:
269     raise ValueError, _("Could not remove LUN %(fcplun)s at WWPN "
270     "%(wwpn)s on zFCP device %(devnum)s "
271     "(%(e)s).") \
272     % {'fcplun': self.fcplun, 'wwpn': self.wwpn,
273     'devnum': self.devnum, 'e': e}
274    
275     if os.path.exists(portadd):
276     # only try to remove ports with older zfcp sysfs interface
277     for lun in os.listdir(portdir):
278     if lun.startswith("0x") and \
279     os.path.isdir(os.path.join(portdir, lun)):
280     log.info("Not removing WWPN %s at zFCP device %s since port still has other LUNs, e.g. %s."
281     %(self.wwpn, self.devnum, lun))
282     return True
283    
284     try:
285     loggedWriteLineToFile(portremove, self.wwpn)
286     except IOError as e:
287     raise ValueError, _("Could not remove WWPN %(wwpn)s on zFCP "
288     "device %(devnum)s (%(e)s).") \
289     % {'wwpn': self.wwpn,
290     'devnum': self.devnum, 'e': e}
291    
292     if os.path.exists(portadd):
293     # older zfcp sysfs interface
294     for port in os.listdir(devdir):
295     if port.startswith("0x") and \
296     os.path.isdir(os.path.join(devdir, port)):
297     log.info("Not setting zFCP device %s offline since it still has other ports, e.g. %s."
298     %(self.devnum, port))
299     return True
300     else:
301     # newer zfcp sysfs interface with auto port scan
302     import glob
303     luns = glob.glob("%s/0x????????????????/0x????????????????"
304     %(devdir,))
305     if len(luns) != 0:
306     log.info("Not setting zFCP device %s offline since it still has other LUNs, e.g. %s."
307     %(self.devnum, luns[0]))
308     return True
309    
310     try:
311     loggedWriteLineToFile(offline, "0")
312     except IOError as e:
313     raise ValueError, _("Could not set zFCP device %(devnum)s "
314     "offline (%(e)s).") \
315     % {'devnum': self.devnum, 'e': e}
316    
317     return True
318    
319     class ZFCP:
320     """ ZFCP utility class.
321    
322     This class will automatically online to ZFCP drives configured in
323     /tmp/fcpconfig when the startup() method gets called. It can also be
324     used to manually configure ZFCP devices through the addFCP() method.
325    
326     As this class needs to make sure that /tmp/fcpconfig configured
327     drives are only onlined once and as it keeps a global list of all ZFCP
328     devices it is implemented as a Singleton.
329     """
330    
331     def __init__(self):
332     self.intf = None
333     self.fcpdevs = set()
334     self.hasReadConfig = False
335     self.down = True
336    
337     # So that users can write zfcp() to get the singleton instance
338     def __call__(self):
339     return self
340    
341     def readConfig(self):
342     try:
343     f = open(zfcpconf, "r")
344     except IOError:
345     log.info("no %s; not configuring zfcp" % (zfcpconf,))
346     return
347    
348     lines = map(lambda x: x.strip().lower(), f.readlines())
349     f.close()
350    
351     for line in lines:
352     if line.startswith("#") or line == '':
353     continue
354    
355     fields = line.split()
356    
357     if len(fields) == 3:
358     devnum = fields[0]
359     wwpn = fields[1]
360     fcplun = fields[2]
361     elif len(fields) == 5:
362     # support old syntax of:
363     # devno scsiid wwpn scsilun fcplun
364     devnum = fields[0]
365     wwpn = fields[2]
366     fcplun = fields[4]
367     else:
368     log.warn("Invalid line found in %s: %s" % (zfcpconf, line,))
369     continue
370    
371     try:
372     self.addFCP(devnum, wwpn, fcplun)
373     except ValueError, e:
374     if self.intf:
375     self.intf.messageWindow(_("Error"), str(e))
376     else:
377     log.warning(str(e))
378    
379     def addFCP(self, devnum, wwpn, fcplun):
380     d = ZFCPDevice(devnum, wwpn, fcplun)
381     if d.onlineDevice():
382     self.fcpdevs.add(d)
383    
384     def shutdown(self):
385     if self.down:
386     return
387     self.down = True
388     if len(self.fcpdevs) == 0:
389     return
390     for d in self.fcpdevs:
391     try:
392     d.offlineDevice()
393     except ValueError, e:
394     log.warn(str(e))
395    
396     def startup(self, intf=None):
397     self.intf = intf
398     if not self.down:
399     return
400     self.down = False
401     if not self.hasReadConfig:
402     self.readConfig()
403     self.hasReadConfig = True
404     # readConfig calls addFCP which calls onlineDevice already
405     return
406    
407     if len(self.fcpdevs) == 0:
408     return
409     for d in self.fcpdevs:
410     try:
411     d.onlineDevice()
412     except ValueError, e:
413     log.warn(str(e))
414    
415     def writeKS(self, f):
416     if len(self.fcpdevs) == 0:
417     return
418     for d in self.fcpdevs:
419     f.write("zfcp --devnum %s --wwpn %s --fcplun %s\n" %(d.devnum,
420     d.wwpn,
421     d.fcplun))
422    
423     def write(self, instPath):
424     if len(self.fcpdevs) == 0:
425     return
426     f = open(instPath + zfcpconf, "w")
427     for d in self.fcpdevs:
428     f.write("%s\n" %(d,))
429     f.close()
430    
431     f = open(instPath + "/etc/modprobe.conf", "a")
432     f.write("alias scsi_hostadapter zfcp\n")
433     f.close()
434    
435     # Create ZFCP singleton
436     ZFCP = ZFCP()
437    
438     # vim:tw=78:ts=4:et:sw=4

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