# # dasd.py - DASD class # # Copyright (C) 2009, 2010 Red Hat, Inc. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Red Hat Author(s): David Cantrell # from pyanaconda import iutil import sys import os from pyanaconda.storage.errors import DasdFormatError from pyanaconda.storage.devices import deviceNameToDiskByPath from pyanaconda.constants import * from pyanaconda.flags import flags from pyanaconda.baseudev import udev_trigger import logging log = logging.getLogger("anaconda") import gettext _ = lambda x: gettext.ldgettext("anaconda", x) P_ = lambda x, y, z: gettext.ldngettext("anaconda", x, y, z) def getDasdPorts(): """ Return comma delimited string of valid DASD ports. """ ports = [] f = open("/proc/dasd/devices", "r") lines = map(lambda x: x.strip(), f.readlines()) f.close() for line in lines: if "unknown" in line: continue if "(FBA )" in line or "(ECKD)" in line: ports.append(line.split('(')[0]) return ','.join(ports) class DASD: """ Controlling class for DASD interaction before the storage code in anaconda has initialized. The DASD class can determine if any DASD devices on the system are unformatted and can perform a dasdfmt on them. """ def __init__(self): self._dasdlist = [] self._devices = [] # list of DASDDevice objects self.totalCylinders = 0 self._completedCylinders = 0.0 self._maxFormatJobs = 0 self.dasdfmt = "/sbin/dasdfmt" self.commonArgv = ["-y", "-d", "cdl", "-b", "4096"] self.started = False def __call__(self): return self def startup(self, intf, exclusiveDisks, zeroMbr): """ Look for any unformatted DASDs in the system and offer the user the option for format them with dasdfmt or exit the installer. """ if self.started: return self.started = True out = "/dev/tty5" err = "/dev/tty5" if not iutil.isS390(): return # Trigger udev data about the dasd devices on the system udev_trigger(action="change", name="dasd*") log.info("Checking for unformatted DASD devices:") for device in os.listdir("/sys/block"): if not device.startswith("dasd"): continue statusfile = "/sys/block/%s/device/status" % (device,) if not os.path.isfile(statusfile): continue f = open(statusfile, "r") status = f.read().strip() f.close() if status in ["unformatted"] and device not in exclusiveDisks: bypath = deviceNameToDiskByPath(device) if not bypath: bypath = "/dev/" + device log.info(" %s (%s) status is %s, needs dasdfmt" % (device, bypath, status,)) self._dasdlist.append((device, bypath)) if not len(self._dasdlist): log.info(" no unformatted DASD devices found") return askUser = True if zeroMbr: askUser = False elif not intf and not zeroMbr: log.info(" non-interactive kickstart install without zerombr " "command, unable to run dasdfmt, exiting installer") sys.exit(0) c = len(self._dasdlist) if intf and askUser: devs = '' for dasd, bypath in self._dasdlist: devs += "%s\n" % (bypath,) rc = intf.questionInitializeDASD(c, devs) if rc == 1: log.info(" not running dasdfmt, continuing installation") return # gather total cylinder count argv = ["-t", "-v"] + self.commonArgv for dasd, bypath in self._dasdlist: buf = iutil.execWithCapture(self.dasdfmt, argv + ["/dev/" + dasd], stderr=err) for line in buf.splitlines(): if line.startswith("Drive Geometry: "): # line will look like this: # Drive Geometry: 3339 Cylinders * 15 Heads = 50085 Tracks cyls = long(filter(lambda s: s, line.split(' '))[2]) self.totalCylinders += cyls break # format DASDs argv = ["-P"] + self.commonArgv update = self._updateProgressWindow title = P_("Formatting DASD Device", "Formatting DASD Devices", c) msg = P_("Preparing %d DASD device for use with Linux..." % c, "Preparing %d DASD devices for use with Linux..." % c, c) if intf: if self.totalCylinders: pw = intf.progressWindow(title, msg, 1.0) else: pw = intf.progressWindow(title, msg, 100, pulse=True) for dasd, bypath in self._dasdlist: log.info("Running dasdfmt on %s" % (bypath,)) arglist = argv + ["/dev/" + dasd] try: if intf and self.totalCylinders: ret = iutil.execWithCallback(self.dasdfmt, arglist, stdout=out, stderr=err, callback=update, callback_data=pw, echo=False) rc = ret.rc elif intf: ret = iutil.execWithPulseProgress(self.dasdfmt, arglist, stdout=out, stderr=err, progress=pw) rc = ret.rc else: rc = iutil.execWithRedirect(self.dasdfmt, arglist, stdout=out, stderr=err) except Exception as e: raise DasdFormatError(e, bypath) if rc: raise DasdFormatError("dasdfmt failed: %s" % rc, bypath) if intf: pw.pop() def addDASD(self, dasd): """ Adds a DASDDevice to the internal list of DASDs. """ if dasd: self._devices.append(dasd) def clear_device_list(self): """ Clear the device list to force re-populate on next access. """ self._devices = [] def write(self): """ Write /etc/dasd.conf to target system for all DASD devices configured during installation. """ if self._devices == []: return f = open(os.path.realpath(ROOT_PATH + "/etc/dasd.conf"), "w") for dasd in self._devices: fields = [dasd.busid] + dasd.getOpts() f.write("%s\n" % (" ".join(fields),)) f.close() def _updateProgressWindow(self, data, callback_data=None): """ Reads progress output from dasdfmt and collects the number of cylinders completed so the progress window can update. """ if not callback_data: return if data == '\n': # each newline we see in this output means one more cylinder done self._completedCylinders += 1.0 callback_data.set(self._completedCylinders / self.totalCylinders) # Create DASD singleton DASD = DASD() # vim:tw=78:ts=4:et:sw=4