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", "ppc" ] |
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 - 1 and level in (1, 5, 6): |
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 |
elif len(devices) == totalDisks - 2 and level == 6: |
99 |
log("missing components of raid device md%d. The " |
100 |
"raid device needs %d drive(s) and only %d (was/were) found. " |
101 |
"This raid device will be started in degraded mode.", mdMinor, |
102 |
totalDisks, len(devices)) |
103 |
elif len(devices) < totalDisks: |
104 |
log("missing components of raid device md%d. The " |
105 |
"raid device needs %d drive(s) and only %d (was/were) found. " |
106 |
"This raid device will not be started.", mdMinor, |
107 |
totalDisks, len(devices)) |
108 |
continue |
109 |
raidList.append((mdMinor, devices, level, totalDisks)) |
110 |
|
111 |
return raidList |
112 |
|
113 |
def startAllRaid(driveList): |
114 |
"""Do a raid start on raid devices and return a list like scanForRaid.""" |
115 |
rc = [] |
116 |
mdList = scanForRaid(driveList) |
117 |
for mdDevice, deviceList, level, numActive in mdList: |
118 |
devName = "md%d" % (mdDevice,) |
119 |
isys.raidstart(devName, deviceList[0]) |
120 |
rc.append((devName, deviceList, level, numActive)) |
121 |
return rc |
122 |
|
123 |
def stopAllRaid(mdList): |
124 |
"""Do a raid stop on each of the raid device tuples given.""" |
125 |
for dev, devices, level, numActive in mdList: |
126 |
isys.raidstop(dev) |
127 |
|
128 |
def isRaid6(raidlevel): |
129 |
"""Return whether raidlevel is a valid descriptor of RAID6.""" |
130 |
if raidlevel == "RAID6": |
131 |
return 1 |
132 |
elif raidlevel == 6: |
133 |
return 1 |
134 |
elif raidlevel == "6": |
135 |
return 1 |
136 |
return 0 |
137 |
|
138 |
def isRaid5(raidlevel): |
139 |
"""Return whether raidlevel is a valid descriptor of RAID5.""" |
140 |
if raidlevel == "RAID5": |
141 |
return 1 |
142 |
elif raidlevel == 5: |
143 |
return 1 |
144 |
elif raidlevel == "5": |
145 |
return 1 |
146 |
return 0 |
147 |
|
148 |
def isRaid1(raidlevel): |
149 |
"""Return whether raidlevel is a valid descriptor of RAID1.""" |
150 |
if raidlevel == "RAID1": |
151 |
return 1 |
152 |
elif raidlevel == 1: |
153 |
return 1 |
154 |
elif raidlevel == "1": |
155 |
return 1 |
156 |
return 0 |
157 |
|
158 |
def isRaid0(raidlevel): |
159 |
"""Return whether raidlevel is a valid descriptor of RAID0.""" |
160 |
if raidlevel == "RAID0": |
161 |
return 1 |
162 |
elif raidlevel == 0: |
163 |
return 1 |
164 |
elif raidlevel == "0": |
165 |
return 1 |
166 |
return 0 |
167 |
|
168 |
def get_raid_min_members(raidlevel): |
169 |
"""Return the minimum number of raid members required for raid level""" |
170 |
if isRaid0(raidlevel): |
171 |
return 2 |
172 |
elif isRaid1(raidlevel): |
173 |
return 1 |
174 |
elif isRaid5(raidlevel): |
175 |
return 2 |
176 |
elif isRaid6(raidlevel): |
177 |
return 2 |
178 |
else: |
179 |
raise ValueError, "invalid raidlevel in get_raid_min_members" |
180 |
|
181 |
def get_raid_max_spares(raidlevel, nummembers): |
182 |
"""Return the maximum number of raid spares for raidlevel.""" |
183 |
if isRaid0(raidlevel): |
184 |
return 0 |
185 |
elif isRaid1(raidlevel) or isRaid5(raidlevel) or isRaid6(raidlevel): |
186 |
return max(0, nummembers - get_raid_min_members(raidlevel)) |
187 |
else: |
188 |
raise ValueError, "invalid raidlevel in get_raid_max_spares" |
189 |
|
190 |
def register_raid_device(mdname, newdevices, newlevel, newnumActive): |
191 |
"""Register a new RAID device in the mdlist.""" |
192 |
for dev, devices, level, numActive in partedUtils.DiskSet.mdList: |
193 |
if mdname == dev: |
194 |
if (devices != newdevices or level != newlevel or |
195 |
numActive != newnumActive): |
196 |
raise ValueError, "%s is already in the mdList!" % (mdname,) |
197 |
else: |
198 |
return |
199 |
partedUtils.DiskSet.mdList.append((mdname, newdevices[:], newlevel, |
200 |
newnumActive)) |
201 |
|
202 |
def lookup_raid_device(mdname): |
203 |
"""Return the requested RAID device information.""" |
204 |
for dev, devices, level, numActive in partedUtils.DiskSet.mdList: |
205 |
if mdname == dev: |
206 |
return (dev, devices, level, numActive) |
207 |
raise KeyError, "md device not found" |
208 |
|
209 |
def getRaidLevels(): |
210 |
avail = [] |
211 |
try: |
212 |
f = open("/proc/mdstat", "r") |
213 |
except: |
214 |
pass |
215 |
else: |
216 |
for l in f.readlines(): |
217 |
if not l.startswith("Personalities"): |
218 |
continue |
219 |
for tok in l.split(): |
220 |
for lev in ("RAID0", "RAID1", "RAID5", "RAID6"): |
221 |
if tok.upper().find(lev) != -1: |
222 |
avail.append(lev) |
223 |
|
224 |
f.close() |
225 |
|
226 |
return avail |
227 |
|
228 |
|
229 |
|