1 |
gordonr |
1.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 |
|
|
|