1 |
#!/usr/bin/python |
2 |
# |
3 |
# raid.py - raid probing control |
4 |
# |
5 |
# Erik Troan <ewt@redhat.com> |
6 |
# |
7 |
# Copyright 1999-2002 Red Hat, Inc. |
8 |
# |
9 |
# This software may be freely redistributed under the terms of the GNU |
10 |
# library public license. |
11 |
# |
12 |
# You should have received a copy of the GNU Library Public License |
13 |
# along with this program; if not, write to the Free Software |
14 |
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
15 |
# |
16 |
"""Raid probing control.""" |
17 |
|
18 |
import parted |
19 |
import isys |
20 |
import os |
21 |
import partitioning |
22 |
import partedUtils |
23 |
|
24 |
from rhpl.log import log |
25 |
|
26 |
# these arches can have their /boot on RAID and not have their |
27 |
# boot loader blow up |
28 |
raidBootArches = [ "i386", "x86_64" ] |
29 |
|
30 |
def scanForRaid(drives): |
31 |
"""Scans for raid devices on drives. |
32 |
|
33 |
drives is a list of device names. |
34 |
Returns a list of (mdMinor, devices, level, totalDisks) tuples. |
35 |
""" |
36 |
|
37 |
raidSets = {} |
38 |
raidDevices = {} |
39 |
|
40 |
for d in drives: |
41 |
parts = [] |
42 |
isys.makeDevInode(d, "/tmp/" + d) |
43 |
try: |
44 |
dev = parted.PedDevice.get("/tmp/" + d) |
45 |
disk = parted.PedDisk.new(dev) |
46 |
|
47 |
raidParts = partedUtils.get_raid_partitions(disk) |
48 |
for part in raidParts: |
49 |
parts.append(partedUtils.get_partition_name(part)) |
50 |
except: |
51 |
pass |
52 |
|
53 |
os.remove("/tmp/" + d) |
54 |
for dev in parts: |
55 |
try: |
56 |
(major, minor, raidSet, level, nrDisks, totalDisks, mdMinor) =\ |
57 |
isys.raidsb(dev) |
58 |
except ValueError: |
59 |
# bad magic, this can't be part of our raid set |
60 |
log("reading raid sb failed for %s",dev) |
61 |
continue |
62 |
|
63 |
if raidSets.has_key(raidSet): |
64 |
(knownLevel, knownDisks, knownMinor, knownDevices) = \ |
65 |
raidSets[raidSet] |
66 |
if knownLevel != level or knownDisks != totalDisks or \ |
67 |
knownMinor != mdMinor: |
68 |
# Raise hell |
69 |
log("raid set inconsistency for md%d: " |
70 |
"all drives in this raid set do not " |
71 |
"agree on raid parameters. Skipping raid device", |
72 |
mdMinor) |
73 |
continue |
74 |
knownDevices.append(dev) |
75 |
raidSets[raidSet] = (knownLevel, knownDisks, knownMinor, |
76 |
knownDevices) |
77 |
else: |
78 |
raidSets[raidSet] = (level, totalDisks, mdMinor, [dev,]) |
79 |
|
80 |
if raidDevices.has_key(mdMinor): |
81 |
if (raidDevices[mdMinor] != raidSet): |
82 |
log("raid set inconsistency for md%d: " |
83 |
"found members of multiple raid sets " |
84 |
"that claim to be md%d. Using only the first " |
85 |
"array found.", mdMinor, mdMinor) |
86 |
continue |
87 |
else: |
88 |
raidDevices[mdMinor] = raidSet |
89 |
|
90 |
raidList = [] |
91 |
for key in raidSets.keys(): |
92 |
(level, totalDisks, mdMinor, devices) = raidSets[key] |
93 |
if len(devices) < totalDisks: |
94 |
log("missing components of raid device md%d. The " |
95 |
"raid device needs %d drive(s) and only %d (was/were) found. " |
96 |
"This raid device will be started in degraded mode.", mdMinor, |
97 |
totalDisks, len(devices)) |
98 |
raidList.append((mdMinor, devices, level, totalDisks)) |
99 |
|
100 |
return raidList |
101 |
|
102 |
def startAllRaid(driveList): |
103 |
"""Do a raid start on raid devices and return a list like scanForRaid.""" |
104 |
rc = [] |
105 |
mdList = scanForRaid(driveList) |
106 |
for mdDevice, deviceList, level, numActive in mdList: |
107 |
devName = "md%d" % (mdDevice,) |
108 |
isys.raidstart(devName, deviceList[0]) |
109 |
rc.append((devName, deviceList, level, numActive)) |
110 |
return rc |
111 |
|
112 |
def stopAllRaid(mdList): |
113 |
"""Do a raid stop on each of the raid device tuples given.""" |
114 |
for dev, devices, level, numActive in mdList: |
115 |
isys.raidstop(dev) |
116 |
|
117 |
def isRaid6(raidlevel): |
118 |
"""Return whether raidlevel is a valid descriptor of RAID6.""" |
119 |
if raidlevel == "RAID6": |
120 |
return 1 |
121 |
elif raidlevel == 6: |
122 |
return 1 |
123 |
elif raidlevel == "6": |
124 |
return 1 |
125 |
return 0 |
126 |
|
127 |
def isRaid5(raidlevel): |
128 |
"""Return whether raidlevel is a valid descriptor of RAID5.""" |
129 |
if raidlevel == "RAID5": |
130 |
return 1 |
131 |
elif raidlevel == 5: |
132 |
return 1 |
133 |
elif raidlevel == "5": |
134 |
return 1 |
135 |
return 0 |
136 |
|
137 |
def isRaid1(raidlevel): |
138 |
"""Return whether raidlevel is a valid descriptor of RAID1.""" |
139 |
if raidlevel == "RAID1": |
140 |
return 1 |
141 |
elif raidlevel == 1: |
142 |
return 1 |
143 |
elif raidlevel == "1": |
144 |
return 1 |
145 |
return 0 |
146 |
|
147 |
def isRaid0(raidlevel): |
148 |
"""Return whether raidlevel is a valid descriptor of RAID0.""" |
149 |
if raidlevel == "RAID0": |
150 |
return 1 |
151 |
elif raidlevel == 0: |
152 |
return 1 |
153 |
elif raidlevel == "0": |
154 |
return 1 |
155 |
return 0 |
156 |
|
157 |
def get_raid_min_members(raidlevel): |
158 |
"""Return the minimum number of raid members required for raid level""" |
159 |
if isRaid0(raidlevel): |
160 |
return 2 |
161 |
elif isRaid1(raidlevel): |
162 |
return 1 |
163 |
elif isRaid5(raidlevel): |
164 |
return 3 |
165 |
elif isRaid6(raidlevel): |
166 |
return 4 |
167 |
else: |
168 |
raise ValueError, "invalid raidlevel in get_raid_min_members" |
169 |
|
170 |
def get_raid_max_spares(raidlevel, nummembers): |
171 |
"""Return the maximum number of raid spares for raidlevel.""" |
172 |
if isRaid0(raidlevel): |
173 |
return 0 |
174 |
elif isRaid1(raidlevel) or isRaid5(raidlevel) or isRaid6(raidlevel): |
175 |
return max(0, nummembers - get_raid_min_members(raidlevel)) |
176 |
else: |
177 |
raise ValueError, "invalid raidlevel in get_raid_max_spares" |
178 |
|
179 |
def register_raid_device(mdname, newdevices, newlevel, newnumActive): |
180 |
"""Register a new RAID device in the mdlist.""" |
181 |
for dev, devices, level, numActive in partedUtils.DiskSet.mdList: |
182 |
if mdname == dev: |
183 |
if (devices != newdevices or level != newlevel or |
184 |
numActive != newnumActive): |
185 |
raise ValueError, "%s is already in the mdList!" % (mdname,) |
186 |
else: |
187 |
return |
188 |
partedUtils.DiskSet.mdList.append((mdname, newdevices[:], newlevel, |
189 |
newnumActive)) |
190 |
|
191 |
def lookup_raid_device(mdname): |
192 |
"""Return the requested RAID device information.""" |
193 |
for dev, devices, level, numActive in partedUtils.DiskSet.mdList: |
194 |
if mdname == dev: |
195 |
return (dev, devices, level, numActive) |
196 |
raise KeyError, "md device not found" |
197 |
|
198 |
def getRaidLevels(): |
199 |
avail = [] |
200 |
try: |
201 |
f = open("/proc/mdstat", "r") |
202 |
except: |
203 |
pass |
204 |
else: |
205 |
for l in f.readlines(): |
206 |
if not l.startswith("Personalities"): |
207 |
continue |
208 |
for tok in l.split(): |
209 |
for lev in ("RAID0", "RAID1", "RAID5", "RAID6"): |
210 |
if tok.upper().find(lev) != -1: |
211 |
avail.append(lev) |
212 |
|
213 |
f.close() |
214 |
|
215 |
return avail |
216 |
|
217 |
|
218 |
|