# # raid.py - raid probing control # # Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 # 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): Erik Troan # """Raid probing control.""" def getRaidLevels(): avail = [] try: f = open("/proc/mdstat", "r") except: pass else: for l in f.readlines(): if not l.startswith("Personalities"): continue lst = l.split() for lev in ["RAID0", "RAID1", "RAID5", "RAID6", "RAID10"]: if "[" + lev + "]" in lst or "[" + lev.lower() + "]" in lst: avail.append(lev) f.close() avail.sort() return avail # XXX define availraidlevels and defaultmntpts as arch characteristics availRaidLevels = getRaidLevels() import parted import isys import os import partitions import partedUtils import logging log = logging.getLogger("anaconda") # these arches can have their /boot on RAID and not have their # boot loader blow up raidBootArches = [ "i386", "x86_64", "ppc" ] def scanForRaid(drives): """Scans for raid devices on drives. drives is a list of device names. Returns a list of (mdMinor, devices, level, totalDisks) tuples. """ raidSets = {} raidDevices = {} for d in drives: parts = [] try: dev = parted.PedDevice.get("/dev/%s" % (d,)) disk = parted.PedDisk.new(dev) raidParts = partedUtils.get_raid_partitions(disk) for part in raidParts: parts.append(partedUtils.get_partition_name(part)) except: pass for dev in parts: try: (major, minor, raidSet, level, nrDisks, totalDisks, mdMinor) =\ isys.raidsb(dev) except ValueError: # bad magic, this can't be part of our raid set log.error("reading raid sb failed for %s",dev) continue if raidSets.has_key(raidSet): (knownLevel, knownDisks, knownMinor, knownDevices) = \ raidSets[raidSet] if knownLevel != level or knownDisks != totalDisks or \ knownMinor != mdMinor: # Raise hell log.error("raid set inconsistency for md%d: " "all drives in this raid set do not " "agree on raid parameters. Skipping raid device", mdMinor) continue knownDevices.append(dev) raidSets[raidSet] = (knownLevel, knownDisks, knownMinor, knownDevices) else: raidSets[raidSet] = (level, totalDisks, mdMinor, [dev,]) if raidDevices.has_key(mdMinor): if (raidDevices[mdMinor] != raidSet): log.error("raid set inconsistency for md%d: " "found members of multiple raid sets " "that claim to be md%d. Using only the first " "array found.", mdMinor, mdMinor) continue else: raidDevices[mdMinor] = raidSet raidList = [] for key in raidSets.keys(): (level, totalDisks, mdMinor, devices) = raidSets[key] if len(devices) < totalDisks: log.warning("missing components of raid device md%d. The " "raid device needs %d drive(s) and only %d (was/were) " "found. This raid device will not be started.", mdMinor, totalDisks, len(devices)) continue raidList.append((mdMinor, devices, level, totalDisks)) return raidList def startAllRaid(driveList): """Do a raid start on raid devices and return a list like scanForRaid.""" rc = [] mdList = scanForRaid(driveList) for mdDevice, deviceList, level, numActive in mdList: devName = "md%d" % (mdDevice,) isys.raidstart(devName, deviceList[0]) rc.append((devName, deviceList, level, numActive)) return rc def stopAllRaid(mdList): """Do a raid stop on each of the raid device tuples given.""" for dev, devices, level, numActive in mdList: isys.raidstop(dev) def isRaid10(raidlevel): """Return whether raidlevel is a valid descriptor of RAID10.""" if raidlevel in ("RAID10", "10", 10): return True return False def isRaid6(raidlevel): """Return whether raidlevel is a valid descriptor of RAID6.""" if raidlevel in ("RAID6", "6", 6): return True return False def isRaid5(raidlevel): """Return whether raidlevel is a valid descriptor of RAID5.""" if raidlevel in ("RAID5", "5", 5): return True return False def isRaid1(raidlevel): """Return whether raidlevel is a valid descriptor of RAID1.""" if raidlevel in ("mirror", "RAID1", "1", 1): return True return False def isRaid0(raidlevel): """Return whether raidlevel is a valid descriptor of RAID0.""" if raidlevel in ("stripe", "RAID0", "0", 0): return True return False def get_raid_min_members(raidlevel): """Return the minimum number of raid members required for raid level""" if isRaid0(raidlevel): return 2 elif isRaid1(raidlevel): return 2 elif isRaid5(raidlevel): return 3 elif isRaid6(raidlevel): return 4 elif isRaid10(raidlevel): return 4 else: raise ValueError, "invalid raidlevel in get_raid_min_members" def get_raid_max_spares(raidlevel, nummembers): """Return the maximum number of raid spares for raidlevel.""" if isRaid0(raidlevel): return 0 elif isRaid1(raidlevel) or isRaid5(raidlevel) or isRaid6(raidlevel) or isRaid10(raidlevel): return max(0, nummembers - get_raid_min_members(raidlevel)) else: raise ValueError, "invalid raidlevel in get_raid_max_spares" def register_raid_device(mdname, newdevices, newlevel, newnumActive): """Register a new RAID device in the mdlist.""" for dev, devices, level, numActive in partedUtils.DiskSet.mdList: if mdname == dev: if (devices != newdevices or level != newlevel or numActive != newnumActive): raise ValueError, "%s is already in the mdList!" % (mdname,) else: return partedUtils.DiskSet.mdList.append((mdname, newdevices[:], newlevel, newnumActive)) def lookup_raid_device(mdname): """Return the requested RAID device information.""" for dev, devices, level, numActive in partedUtils.DiskSet.mdList: if mdname == dev: return (dev, devices, level, numActive) raise KeyError, "md device not found"