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

Contents of /cdrom.image/sme9/updates/storage/zfcp.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 # 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