1 |
charliebrady |
1.1 |
# |
2 |
|
|
# mdraid.py |
3 |
|
|
# mdraid functions |
4 |
|
|
# |
5 |
|
|
# Copyright (C) 2009 Red Hat, Inc. All rights reserved. |
6 |
|
|
# |
7 |
|
|
# This program is free software; you can redistribute it and/or modify |
8 |
|
|
# it under the terms of the GNU General Public License as published by |
9 |
|
|
# the Free Software Foundation; either version 2 of the License, or |
10 |
|
|
# (at your option) any later version. |
11 |
|
|
# |
12 |
|
|
# This program is distributed in the hope that it will be useful, |
13 |
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
|
|
# GNU General Public License for more details. |
16 |
|
|
# |
17 |
|
|
# You should have received a copy of the GNU General Public License |
18 |
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 |
|
|
# |
20 |
|
|
# Author(s): Dave Lehman <dlehman@redhat.com> |
21 |
|
|
# |
22 |
|
|
|
23 |
|
|
import os |
24 |
|
|
|
25 |
|
|
import iutil |
26 |
|
|
from ..errors import * |
27 |
|
|
|
28 |
|
|
import gettext |
29 |
|
|
_ = lambda x: gettext.ldgettext("anaconda", x) |
30 |
|
|
|
31 |
|
|
import logging |
32 |
|
|
log = logging.getLogger("storage") |
33 |
|
|
|
34 |
|
|
# raidlevels constants |
35 |
|
|
RAID10 = 10 |
36 |
|
|
RAID6 = 6 |
37 |
|
|
RAID5 = 5 |
38 |
|
|
RAID4 = 4 |
39 |
|
|
RAID1 = 1 |
40 |
|
|
RAID0 = 0 |
41 |
|
|
|
42 |
|
|
def getRaidLevels(): |
43 |
|
|
mdstat_descriptors = { |
44 |
|
|
RAID10: ("[RAID10]", "[raid10]"), |
45 |
|
|
RAID6: ("[RAID6]", "[raid6]"), |
46 |
|
|
RAID5: ("[RAID5]", "[raid5]"), |
47 |
|
|
RAID4: ("[RAID4]", "[raid4]"), |
48 |
|
|
RAID1: ("[RAID1]", "[raid1]"), |
49 |
|
|
RAID0: ("[RAID0]", "[raid0]"), |
50 |
|
|
} |
51 |
|
|
avail = [] |
52 |
|
|
try: |
53 |
|
|
f = open("/proc/mdstat", "r") |
54 |
|
|
except IOError: |
55 |
|
|
pass |
56 |
|
|
else: |
57 |
|
|
for l in f.readlines(): |
58 |
|
|
if not l.startswith("Personalities"): |
59 |
|
|
continue |
60 |
|
|
|
61 |
|
|
lst = l.split() |
62 |
|
|
|
63 |
|
|
for level in mdstat_descriptors: |
64 |
|
|
for d in mdstat_descriptors[level]: |
65 |
|
|
if d in lst: |
66 |
|
|
avail.append(level) |
67 |
|
|
break |
68 |
|
|
|
69 |
|
|
f.close() |
70 |
|
|
|
71 |
|
|
avail.sort() |
72 |
|
|
return avail |
73 |
|
|
|
74 |
|
|
raid_levels = getRaidLevels() |
75 |
|
|
|
76 |
|
|
def raidLevel(descriptor): |
77 |
|
|
for level in raid_levels: |
78 |
|
|
if isRaid(level, descriptor): |
79 |
|
|
return level |
80 |
|
|
else: |
81 |
|
|
raise ValueError, "invalid raid level descriptor %s" % descriptor |
82 |
|
|
|
83 |
|
|
def isRaid(raid, raidlevel): |
84 |
|
|
"""Return whether raidlevel is a valid descriptor of raid""" |
85 |
|
|
raid_descriptors = {RAID10: ("RAID10", "raid10", "10", 10), |
86 |
|
|
RAID6: ("RAID6", "raid6", "6", 6), |
87 |
|
|
RAID5: ("RAID5", "raid5", "5", 5), |
88 |
|
|
RAID4: ("RAID4", "raid4", "4", 4), |
89 |
|
|
RAID1: ("mirror", "RAID1", "raid1", "1", 1), |
90 |
|
|
RAID0: ("stripe", "RAID0", "raid0", "0", 0)} |
91 |
|
|
|
92 |
|
|
if raid in raid_descriptors: |
93 |
|
|
return raidlevel in raid_descriptors[raid] |
94 |
|
|
else: |
95 |
|
|
raise ValueError, "invalid raid level %d" % raid |
96 |
|
|
|
97 |
|
|
def get_raid_min_members(raidlevel): |
98 |
|
|
"""Return the minimum number of raid members required for raid level""" |
99 |
|
|
raid_min_members = {RAID10: 2, |
100 |
|
|
RAID6: 4, |
101 |
|
|
RAID5: 3, |
102 |
|
|
RAID4: 3, |
103 |
|
|
RAID1: 2, |
104 |
|
|
RAID0: 2} |
105 |
|
|
|
106 |
|
|
for raid, min_members in raid_min_members.items(): |
107 |
|
|
if isRaid(raid, raidlevel): |
108 |
|
|
return min_members |
109 |
|
|
|
110 |
|
|
raise ValueError, "invalid raid level %d" % raidlevel |
111 |
|
|
|
112 |
|
|
def get_raid_max_spares(raidlevel, nummembers): |
113 |
|
|
"""Return the maximum number of raid spares for raidlevel.""" |
114 |
|
|
raid_max_spares = {RAID10: lambda: max(0, nummembers - get_raid_min_members(RAID10)), |
115 |
|
|
RAID6: lambda: max(0, nummembers - get_raid_min_members(RAID6)), |
116 |
|
|
RAID5: lambda: max(0, nummembers - get_raid_min_members(RAID5)), |
117 |
|
|
RAID4: lambda: max(0, nummembers - get_raid_min_members(RAID4)), |
118 |
|
|
RAID1: lambda: max(0, nummembers - get_raid_min_members(RAID1)), |
119 |
|
|
RAID0: lambda: 0} |
120 |
|
|
|
121 |
|
|
for raid, max_spares_func in raid_max_spares.items(): |
122 |
|
|
if isRaid(raid, raidlevel): |
123 |
|
|
return max_spares_func() |
124 |
|
|
|
125 |
|
|
raise ValueError, "invalid raid level %d" % raidlevel |
126 |
|
|
|
127 |
|
|
def mdadm(args, progress=None): |
128 |
|
|
rc = iutil.execWithPulseProgress("mdadm", args, |
129 |
|
|
stdout = "/dev/tty5", |
130 |
|
|
stderr = "/dev/tty5", |
131 |
|
|
progress=progress) |
132 |
|
|
if not rc: |
133 |
|
|
return |
134 |
|
|
|
135 |
|
|
try: |
136 |
|
|
msg = open("/tmp/program.log").readlines()[-1].strip() |
137 |
|
|
except Exception: |
138 |
|
|
msg = "" |
139 |
|
|
|
140 |
|
|
raise MDRaidError(msg) |
141 |
|
|
|
142 |
|
|
def mdcreate(device, level, disks, spares=0, metadataVer=None, bitmap=False, |
143 |
|
|
progress=None): |
144 |
|
|
argv = ["--create", device, "--run", "--level=%s" % level] |
145 |
|
|
raid_devs = len(disks) - spares |
146 |
|
|
argv.append("--raid-devices=%d" % raid_devs) |
147 |
|
|
if spares: |
148 |
|
|
argv.append("--spare-devices=%d" % spares) |
149 |
|
|
if metadataVer: |
150 |
|
|
argv.append("--metadata=%s" % metadataVer) |
151 |
|
|
if bitmap: |
152 |
|
|
argv.append("--bitmap=internal") |
153 |
|
|
argv.extend(disks) |
154 |
|
|
|
155 |
|
|
try: |
156 |
|
|
mdadm(argv, progress=progress) |
157 |
|
|
except MDRaidError as msg: |
158 |
|
|
raise MDRaidError("mdcreate failed for %s: %s" % (device, msg)) |
159 |
|
|
|
160 |
|
|
def mddestroy(device): |
161 |
|
|
args = ["--zero-superblock", device] |
162 |
|
|
|
163 |
|
|
try: |
164 |
|
|
mdadm(args) |
165 |
|
|
except MDRaidError as msg: |
166 |
|
|
raise MDRaidError("mddestroy failed for %s: %s" % (device, msg)) |
167 |
|
|
|
168 |
|
|
def mdadd(device): |
169 |
|
|
args = ["--incremental", "--quiet"] |
170 |
|
|
args.append(device) |
171 |
|
|
|
172 |
|
|
try: |
173 |
|
|
mdadm(args) |
174 |
|
|
except MDRaidError as msg: |
175 |
|
|
raise MDRaidError("mdadd failed for %s: %s" % (device, msg)) |
176 |
|
|
|
177 |
|
|
def mdactivate(device, members=[], super_minor=None, update_super_minor=False, |
178 |
|
|
uuid=None): |
179 |
|
|
if super_minor is None and not uuid: |
180 |
|
|
raise ValueError("mdactivate requires either a uuid or a super-minor") |
181 |
|
|
|
182 |
|
|
if uuid: |
183 |
|
|
identifier = "--uuid=%s" % uuid |
184 |
|
|
elif super_minor is not None: |
185 |
|
|
identifier = "--super-minor=%d" % super_minor |
186 |
|
|
else: |
187 |
|
|
identifier = "" |
188 |
|
|
|
189 |
|
|
if update_super_minor: |
190 |
|
|
extra_args = ["--update=super-minor"] |
191 |
|
|
else: |
192 |
|
|
extra_args = [ ] |
193 |
|
|
|
194 |
|
|
args = ["--assemble", device, identifier, "--run", "--auto=md"] |
195 |
|
|
args += extra_args |
196 |
|
|
args += members |
197 |
|
|
|
198 |
|
|
try: |
199 |
|
|
mdadm(args) |
200 |
|
|
except MDRaidError as msg: |
201 |
|
|
raise MDRaidError("mdactivate failed for %s: %s" % (device, msg)) |
202 |
|
|
|
203 |
|
|
def mddeactivate(device): |
204 |
|
|
args = ["--stop", device] |
205 |
|
|
|
206 |
|
|
try: |
207 |
|
|
mdadm(args) |
208 |
|
|
except MDRaidError as msg: |
209 |
|
|
raise MDRaidError("mddeactivate failed for %s: %s" % (device, msg)) |
210 |
|
|
|
211 |
|
|
def mdexamine(device): |
212 |
|
|
vars = iutil.execWithCapture("mdadm", |
213 |
|
|
["--examine", "--brief", device], |
214 |
|
|
stderr="/dev/tty5").split() |
215 |
|
|
|
216 |
|
|
info = {} |
217 |
|
|
if vars: |
218 |
|
|
try: |
219 |
|
|
info["device"] = vars[1] |
220 |
|
|
vars = vars[2:] |
221 |
|
|
except IndexError: |
222 |
|
|
return {} |
223 |
|
|
|
224 |
|
|
for var in vars: |
225 |
|
|
(name, equals, value) = var.partition("=") |
226 |
|
|
if not equals: |
227 |
|
|
continue |
228 |
|
|
|
229 |
|
|
info[name.lower()] = value.strip() |
230 |
|
|
|
231 |
|
|
return info |
232 |
|
|
|