1 |
# |
2 |
# partedUtils.py: helper functions for use with parted objects |
3 |
# |
4 |
# Matt Wilson <msw@redhat.com> |
5 |
# Jeremy Katz <katzj@redhat.com> |
6 |
# Mike Fulbright <msf@redhat.com> |
7 |
# Karsten Hopp <karsten@redhat.com> |
8 |
# |
9 |
# Copyright 2002-2003 Red Hat, Inc. |
10 |
# |
11 |
# This software may be freely redistributed under the terms of the GNU |
12 |
# library public license. |
13 |
# |
14 |
# You should have received a copy of the GNU Library Public License |
15 |
# along with this program; if not, write to the Free Software |
16 |
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
17 |
# |
18 |
"""Helper functions for use when dealing with parted objects.""" |
19 |
|
20 |
import parted |
21 |
import math |
22 |
import os, sys, string, struct |
23 |
|
24 |
from product import * |
25 |
import fsset |
26 |
import iutil, isys |
27 |
import raid |
28 |
import lvm |
29 |
from flags import flags |
30 |
from partErrors import * |
31 |
|
32 |
from rhpl.log import log |
33 |
from rhpl.translate import _ |
34 |
|
35 |
fsTypes = {} |
36 |
|
37 |
fs_type = parted.file_system_type_get_next () |
38 |
while fs_type: |
39 |
fsTypes[fs_type.name] = fs_type |
40 |
fs_type = parted.file_system_type_get_next (fs_type) |
41 |
|
42 |
|
43 |
|
44 |
def get_flags (part): |
45 |
"""Retrieve a list of strings representing the flags on the partition.""" |
46 |
string="" |
47 |
if not part.is_active (): |
48 |
return string |
49 |
first=1 |
50 |
flag = parted.partition_flag_next (0) |
51 |
while flag: |
52 |
if part.get_flag (flag): |
53 |
string = string + parted.partition_flag_get_name (flag) |
54 |
if first: |
55 |
first = 0 |
56 |
else: |
57 |
string = string + ", " |
58 |
flag = parted.partition_flag_next (flag) |
59 |
return string |
60 |
|
61 |
def start_sector_to_cyl(device, sector): |
62 |
"""Return the closest cylinder (round down) to sector on device.""" |
63 |
return int(math.floor((float(sector) |
64 |
/ (device.heads * device.sectors)) + 1)) |
65 |
|
66 |
def end_sector_to_cyl(device, sector): |
67 |
"""Return the closest cylinder (round up) to sector on device.""" |
68 |
return int(math.ceil(float((sector + 1)) |
69 |
/ (device.heads * device.sectors))) |
70 |
|
71 |
def start_cyl_to_sector(device, cyl): |
72 |
"Return the sector corresponding to cylinder as a starting cylinder." |
73 |
return long((cyl - 1) * (device.heads * device.sectors)) |
74 |
|
75 |
def end_cyl_to_sector(device, cyl): |
76 |
"Return the sector corresponding to cylinder as a ending cylinder." |
77 |
return long(((cyl) * (device.heads * device.sectors)) - 1) |
78 |
|
79 |
def getPartSize(partition): |
80 |
"""Return the size of partition in sectors.""" |
81 |
return partition.geom.length |
82 |
|
83 |
def getPartSizeMB(partition): |
84 |
"""Return the size of partition in megabytes.""" |
85 |
return (partition.geom.length * partition.geom.dev.sector_size |
86 |
/ 1024.0 / 1024.0) |
87 |
|
88 |
def getDeviceSizeMB(dev): |
89 |
"""Return the size of dev in megabytes.""" |
90 |
return (float(dev.heads * dev.cylinders * dev.sectors) / (1024 * 1024) |
91 |
* dev.sector_size) |
92 |
|
93 |
def get_partition_by_name(disks, partname): |
94 |
"""Return the parted part object associated with partname. |
95 |
|
96 |
Arguments: |
97 |
disks -- Dictionary of diskname->PedDisk objects |
98 |
partname -- Name of partition to find |
99 |
|
100 |
Return: |
101 |
PedPartition object with name partname. None if no such partition. |
102 |
""" |
103 |
for diskname in disks.keys(): |
104 |
disk = disks[diskname] |
105 |
part = disk.next_partition() |
106 |
while part: |
107 |
if get_partition_name(part) == partname: |
108 |
return part |
109 |
|
110 |
part = disk.next_partition(part) |
111 |
|
112 |
return None |
113 |
|
114 |
def get_partition_name(partition): |
115 |
"""Return the device name for the PedPartition partition.""" |
116 |
if (partition.geom.dev.type == parted.DEVICE_DAC960 |
117 |
or partition.geom.dev.type == parted.DEVICE_CPQARRAY): |
118 |
return "%sp%d" % (partition.geom.dev.path[5:], |
119 |
partition.num) |
120 |
if (parted.__dict__.has_key("DEVICE_SX8") and |
121 |
partition.geom.dev.type == parted.DEVICE_SX8): |
122 |
return "%sp%d" % (partition.geom.dev.path[5:], |
123 |
partition.num) |
124 |
|
125 |
return "%s%d" % (partition.geom.dev.path[5:], |
126 |
partition.num) |
127 |
|
128 |
|
129 |
def get_partition_file_system_type(part): |
130 |
"""Return the file system type of the PedPartition part. |
131 |
|
132 |
Arguments: |
133 |
part -- PedPartition object |
134 |
|
135 |
Return: |
136 |
Filesystem object (as defined in fsset.py) |
137 |
""" |
138 |
if part.fs_type is None and part.native_type == 0x41: |
139 |
ptype = fsset.fileSystemTypeGet("PPC PReP Boot") |
140 |
elif part.fs_type == None: |
141 |
return None |
142 |
elif part.fs_type.name == "linux-swap": |
143 |
ptype = fsset.fileSystemTypeGet("swap") |
144 |
elif (part.fs_type.name == "FAT" or part.fs_type.name == "fat16" |
145 |
or part.fs_type.name == "fat32"): |
146 |
ptype = fsset.fileSystemTypeGet("vfat") |
147 |
else: |
148 |
try: |
149 |
ptype = fsset.fileSystemTypeGet(part.fs_type.name) |
150 |
except: |
151 |
ptype = fsset.fileSystemTypeGet("foreign") |
152 |
|
153 |
return ptype |
154 |
|
155 |
|
156 |
def set_partition_file_system_type(part, fstype): |
157 |
"""Set partition type of part to PedFileSystemType implied by fstype.""" |
158 |
if fstype == None: |
159 |
return |
160 |
try: |
161 |
for flag in fstype.getPartedPartitionFlags(): |
162 |
if not part.is_flag_available(flag): |
163 |
raise PartitioningError, ("requested FileSystemType needs " |
164 |
"a flag that is not available.") |
165 |
part.set_flag(flag, 1) |
166 |
part.set_system(fstype.getPartedFileSystemType()) |
167 |
except: |
168 |
print "Failed to set partition type to ",fstype.getName() |
169 |
pass |
170 |
|
171 |
def get_partition_drive(partition): |
172 |
"""Return the device name for disk that PedPartition partition is on.""" |
173 |
return "%s" %(partition.geom.dev.path[5:]) |
174 |
|
175 |
def get_max_logical_partitions(disk): |
176 |
if not disk.type.check_feature(parted.DISK_TYPE_EXTENDED): |
177 |
return 0 |
178 |
dev = disk.dev.path[5:] |
179 |
for key in max_logical_partition_count.keys(): |
180 |
if dev.startswith(key): |
181 |
return max_logical_partition_count[key] |
182 |
# FIXME: if we don't know about it, should we pretend it can't have |
183 |
# logicals? probably safer to just use something reasonable |
184 |
return 11 |
185 |
|
186 |
def map_foreign_to_fsname(type): |
187 |
"""Return the partition type associated with the numeric type.""" |
188 |
if type in allPartitionTypesDict.keys(): |
189 |
return allPartitionTypesDict[type] |
190 |
else: |
191 |
return _("Foreign") |
192 |
|
193 |
def filter_partitions(disk, func): |
194 |
rc = [] |
195 |
part = disk.next_partition () |
196 |
while part: |
197 |
if func(part): |
198 |
rc.append(part) |
199 |
part = disk.next_partition (part) |
200 |
|
201 |
return rc |
202 |
|
203 |
def get_all_partitions(disk): |
204 |
"""Return a list of all PedPartition objects on disk.""" |
205 |
func = lambda part: part.is_active() |
206 |
return filter_partitions(disk, func) |
207 |
|
208 |
def get_logical_partitions(disk): |
209 |
"""Return a list of logical PedPartition objects on disk.""" |
210 |
func = lambda part: (part.is_active() |
211 |
and part.type & parted.PARTITION_LOGICAL) |
212 |
return filter_partitions(disk, func) |
213 |
|
214 |
def get_primary_partitions(disk): |
215 |
"""Return a list of primary PedPartition objects on disk.""" |
216 |
func = lambda part: part.type == parted.PARTITION_PRIMARY |
217 |
return filter_partitions(disk, func) |
218 |
|
219 |
def get_raid_partitions(disk): |
220 |
"""Return a list of RAID-type PedPartition objects on disk.""" |
221 |
func = lambda part: (part.is_active() |
222 |
and part.get_flag(parted.PARTITION_RAID) == 1) |
223 |
return filter_partitions(disk, func) |
224 |
|
225 |
def get_lvm_partitions(disk): |
226 |
"""Return a list of physical volume-type PedPartition objects on disk.""" |
227 |
func = lambda part: (part.is_active() |
228 |
and part.get_flag(parted.PARTITION_LVM) == 1) |
229 |
return filter_partitions(disk, func) |
230 |
|
231 |
|
232 |
def getDefaultDiskType(): |
233 |
"""Get the default partition table type for this architecture.""" |
234 |
if iutil.getArch() == "i386": |
235 |
return parted.disk_type_get("msdos") |
236 |
elif iutil.getArch() == "ia64": |
237 |
return parted.disk_type_get("gpt") |
238 |
elif iutil.getArch() == "s390": |
239 |
# the "default" type is dasd, but we don't really do dasd |
240 |
# formatting with parted and use dasdfmt directly for them |
241 |
# so if we get here, it's an fcp disk and we should write |
242 |
# an msdos partition table (#144199) |
243 |
return parted.disk_type_get("msdos") |
244 |
elif iutil.getArch() == "alpha": |
245 |
return parted.disk_type_get("bsd") |
246 |
elif iutil.getArch() == "sparc": |
247 |
return parted.disk_type_get("sun") |
248 |
elif iutil.getArch() == "ppc": |
249 |
if iutil.getPPCMachine() == "PMac": |
250 |
return parted.disk_type_get("mac") |
251 |
else: |
252 |
return parted.disk_type_get("msdos") |
253 |
else: |
254 |
return parted.disk_type_get("msdos") |
255 |
|
256 |
archLabels = {'i386': ['msdos'], |
257 |
's390': ['dasd', 'msdos'], |
258 |
'alpha': ['bsd', 'msdos'], |
259 |
'sparc': ['sun'], |
260 |
'ia64': ['msdos', 'gpt'], |
261 |
'ppc': ['msdos', 'mac'], |
262 |
'x86_64': ['msdos']} |
263 |
|
264 |
# this is kind of crappy, but we don't really want to allow LDL formatted |
265 |
# dasd to be used during the install |
266 |
def checkDasdFmt(disk, intf): |
267 |
if iutil.getArch() != "s390": |
268 |
return 0 |
269 |
|
270 |
if disk.type.name != "dasd": |
271 |
return 0 |
272 |
|
273 |
# FIXME: there has to be a better way to check LDL vs CDL |
274 |
# how do I test ldl vs cdl? |
275 |
if disk.max_primary_partition_count > 1: |
276 |
return 0 |
277 |
|
278 |
if intf: |
279 |
try: |
280 |
devs = isys.getDasdDevPort() |
281 |
dev = "/dev/%s (%s)" %(disk.dev.path[5:], devs[device]) |
282 |
except Exception, e: |
283 |
log("exception getting dasd dev ports: %s" %(e,)) |
284 |
dev = "/dev/%s" %(disk.dev.path[5:],) |
285 |
|
286 |
rc = intf.messageWindow(_("Warning"), |
287 |
_("The device %s is LDL formatted instead of " |
288 |
"CDL formatted. LDL formatted DASDs are not " |
289 |
"supported for usage during an install of %s. " |
290 |
"If you wish to use this disk for installation, " |
291 |
"it must be re-initialized causing the loss of " |
292 |
"ALL DATA on this drive.\n\n" |
293 |
"Would you like to reformat this DASD using CDL " |
294 |
"format?") |
295 |
%(dev, productName), type = "yesno") |
296 |
if rc == 0: |
297 |
return 1 |
298 |
else: |
299 |
return -1 |
300 |
else: |
301 |
return 1 |
302 |
|
303 |
|
304 |
def checkDiskLabel(disk, intf): |
305 |
"""Check that the disk label on disk is valid for this machine type.""" |
306 |
arch = iutil.getArch() |
307 |
if arch in archLabels.keys(): |
308 |
if disk.type.name in archLabels[arch]: |
309 |
# this is kind of a hack since we don't want LDL to be used |
310 |
return checkDasdFmt(disk, intf) |
311 |
else: |
312 |
if disk.type.name == "msdos": |
313 |
return 0 |
314 |
|
315 |
if intf: |
316 |
rc = intf.messageWindow(_("Warning"), |
317 |
_("/dev/%s currently has a %s partition " |
318 |
"layout. To use this disk for " |
319 |
"the installation of %s, it must be " |
320 |
"re-initialized, causing the loss of " |
321 |
"ALL DATA on this drive.\n\n" |
322 |
"Would you like to format this " |
323 |
"drive?") |
324 |
%(disk.dev.path[5:], disk.type.name, |
325 |
productName), type="custom", |
326 |
custom_buttons = [ _("_Ignore drive"), |
327 |
_("_Format drive") ], |
328 |
custom_icon="question") |
329 |
|
330 |
if rc == 0: |
331 |
return 1 |
332 |
else: |
333 |
return -1 |
334 |
else: |
335 |
return 1 |
336 |
|
337 |
# attempt to associate a parted filesystem type on a partition that |
338 |
# didn't probe as one type or another. |
339 |
def validateFsType(part): |
340 |
# we only care about primary and logical partitions |
341 |
if not part.type in (parted.PARTITION_PRIMARY, |
342 |
parted.PARTITION_LOGICAL): |
343 |
return |
344 |
# if the partition already has a type, no need to search |
345 |
if part.fs_type: |
346 |
return |
347 |
|
348 |
# first fsystem to probe wins, so sort the types into a preferred |
349 |
# order. |
350 |
fsnames = fsTypes.keys() |
351 |
goodTypes = ['ext3', 'ext2'] |
352 |
badTypes = ['linux-swap',] |
353 |
for fstype in goodTypes: |
354 |
fsnames.remove(fstype) |
355 |
fsnames = goodTypes + fsnames |
356 |
for fstype in badTypes: |
357 |
fsnames.remove(fstype) |
358 |
fsnames.extend(badTypes) |
359 |
|
360 |
# now check each type, and set the partition system accordingly. |
361 |
for fsname in fsnames: |
362 |
fstype = fsTypes[fsname] |
363 |
if fstype.probe_specific(part.geom) != None: |
364 |
# XXX verify that this will not modify system type |
365 |
# in the case where a user does not modify partitions |
366 |
part.set_system(fstype) |
367 |
return |
368 |
|
369 |
def isLinuxNativeByNumtype(numtype): |
370 |
"""Check if the type is a 'Linux native' filesystem.""" |
371 |
linuxtypes = [0x82, 0x83, 0x8e, 0xfd] |
372 |
|
373 |
for t in linuxtypes: |
374 |
if int(numtype) == t: |
375 |
return 1 |
376 |
|
377 |
return 0 |
378 |
|
379 |
def sniffFilesystemType(device): |
380 |
"""Sniff to determine the type of fs on device. |
381 |
|
382 |
device - name of device to sniff. we try to create it if it doesn't exist. |
383 |
""" |
384 |
|
385 |
if os.access(device, os.O_RDONLY): |
386 |
dev = device |
387 |
else: |
388 |
dev = "/tmp/" + device |
389 |
if not os.access(dev, os.O_RDONLY): |
390 |
try: |
391 |
isys.makeDevInode(device, dev) |
392 |
except: |
393 |
pass |
394 |
|
395 |
pagesize = isys.getpagesize() |
396 |
if pagesize > 2048: |
397 |
num = pagesize |
398 |
else: |
399 |
num = 2048 |
400 |
try: |
401 |
fd = os.open(dev, os.O_RDONLY) |
402 |
buf = os.read(fd, num) |
403 |
os.close(fd) |
404 |
except: |
405 |
return None |
406 |
|
407 |
if len(buf) < pagesize: |
408 |
try: |
409 |
log("Tried to read pagesize for %s in sniffFilesystemType and only read %s", dev, len(buf)) |
410 |
except: |
411 |
pass |
412 |
return None |
413 |
|
414 |
# physical volumes start with HM (see linux/lvm.h |
415 |
# and LVM/ver/tools/lib/pv_copy.c) |
416 |
if buf.startswith("HM"): |
417 |
return "physical volume (LVM)" |
418 |
# sniff for LVM2 label. see LVM/ver/lib/label/label.[ch] for a |
419 |
# description of the label and LVM/ver/lib/format_text/layout.h |
420 |
for sec in range(0, 4): |
421 |
off = (sec * 512) + 24 |
422 |
if buf[off:].startswith("LVM2"): |
423 |
return "physical volume (LVM)" |
424 |
|
425 |
try: |
426 |
isys.raidsbFromDevice(dev) |
427 |
return "software RAID" |
428 |
except: |
429 |
pass |
430 |
|
431 |
# ext2 check |
432 |
if struct.unpack("<H", buf[1080:1082]) == (0xef53,): |
433 |
if isys.ext2HasJournal(dev, makeDevNode = 0): |
434 |
return "ext3" |
435 |
else: |
436 |
return "ext2" |
437 |
|
438 |
# xfs signature |
439 |
if buf.startswith("XFSB"): |
440 |
return "xfs" |
441 |
|
442 |
# 2.6 doesn't support version 0, so we don't like SWAP-SPACE |
443 |
if (buf[pagesize - 10:] == "SWAPSPACE2"): |
444 |
return "swap" |
445 |
|
446 |
if fsset.isValidReiserFS(dev): |
447 |
return "reiserfs" |
448 |
|
449 |
if fsset.isValidJFS(dev): |
450 |
return "jfs" |
451 |
|
452 |
# FIXME: we don't look for vfat |
453 |
|
454 |
return None |
455 |
|
456 |
def getCentOSReleaseString(mountpoint): |
457 |
if os.access(mountpoint + "/etc/e-smith-release", os.R_OK): |
458 |
f = open(mountpoint + "/etc/e-smith-release", "r") |
459 |
try: |
460 |
lines = f.readlines() |
461 |
except IOError: |
462 |
try: |
463 |
f.close() |
464 |
except: |
465 |
pass |
466 |
return "" |
467 |
f.close() |
468 |
# return the first line with the newline at the end stripped |
469 |
if len(lines) == 0: |
470 |
return "" |
471 |
relstr = string.strip(lines[0][:-1]) |
472 |
|
473 |
# get the release name and version |
474 |
# assumes that form is something |
475 |
# like "Red Hat Linux release 6.2 (Zoot)" |
476 |
if relstr.find("release") != -1: |
477 |
try: |
478 |
idx = relstr.find("release") |
479 |
prod = relstr[:idx - 1] |
480 |
|
481 |
ver = "" |
482 |
for a in relstr[idx + 8:]: |
483 |
if a in string.digits + ".": |
484 |
ver = ver + a |
485 |
else: |
486 |
break |
487 |
|
488 |
relstr = prod + " " + ver |
489 |
except: |
490 |
pass # don't worry, just use the relstr as we have it |
491 |
return relstr |
492 |
return "" |
493 |
|
494 |
def productMatches(oldproduct, newproduct): |
495 |
"""Determine if this is a reasonable product to upgrade old product""" |
496 |
if oldproduct.startswith(newproduct): |
497 |
return 1 |
498 |
|
499 |
productUpgrades = { |
500 |
"SME Server": ("Mitel Networks", "SME Server", ), |
501 |
"Red Hat Enterprise Linux AS": ("Red Hat Linux Advanced Server", ), |
502 |
"Red Hat Enterprise Linux WS": ("Red Hat Linux Advanced Workstation",), |
503 |
# FIXME: this probably shouldn't be in a release... |
504 |
"Red Hat Enterprise Linux": ("Red Hat Linux Advanced Server", |
505 |
"Red Hat Linux Advanced Workstation", |
506 |
"Red Hat Enterprise Linux AS", |
507 |
"Red Hat Enterprise Linux ES", |
508 |
"Red Hat Enterprise Linux WS"), |
509 |
"Fedora Core": ("Red Hat Linux",) |
510 |
} |
511 |
|
512 |
if productUpgrades.has_key(newproduct): |
513 |
acceptable = productUpgrades[newproduct] |
514 |
else: |
515 |
acceptable = () |
516 |
|
517 |
for p in acceptable: |
518 |
if oldproduct.startswith(p): |
519 |
return 1 |
520 |
|
521 |
return 0 |
522 |
|
523 |
class DiskSet: |
524 |
"""The disks in the system.""" |
525 |
|
526 |
skippedDisks = [] |
527 |
mdList = [] |
528 |
def __init__ (self): |
529 |
self.disks = {} |
530 |
self.onlyPrimary = None |
531 |
|
532 |
def onlyPrimaryParts(self): |
533 |
for disk in self.disks.values(): |
534 |
if disk.type.check_feature(parted.DISK_TYPE_EXTENDED): |
535 |
return 0 |
536 |
|
537 |
return 1 |
538 |
|
539 |
|
540 |
def startAllRaid(self): |
541 |
"""Start all of the raid devices associated with the DiskSet.""" |
542 |
driveList = [] |
543 |
origDriveList = self.driveList() |
544 |
for drive in origDriveList: |
545 |
if not drive in DiskSet.skippedDisks: |
546 |
driveList.append(drive) |
547 |
DiskSet.mdList.extend(raid.startAllRaid(driveList)) |
548 |
|
549 |
def stopAllRaid(self): |
550 |
"""Stop all of the raid devices associated with the DiskSet.""" |
551 |
raid.stopAllRaid(DiskSet.mdList) |
552 |
while DiskSet.mdList: |
553 |
DiskSet.mdList.pop() |
554 |
|
555 |
def getLabels(self): |
556 |
"""Return a list of all of the labels used on partitions.""" |
557 |
labels = {} |
558 |
|
559 |
drives = self.disks.keys() |
560 |
drives.sort() |
561 |
|
562 |
for drive in drives: |
563 |
disk = self.disks[drive] |
564 |
func = lambda part: (part.is_active() and |
565 |
not (part.get_flag(parted.PARTITION_RAID) |
566 |
or part.get_flag(parted.PARTITION_LVM)) |
567 |
and part.fs_type |
568 |
and (part.fs_type.name in ("ext2", |
569 |
"ext3", "xfs"))) |
570 |
parts = filter_partitions(disk, func) |
571 |
for part in parts: |
572 |
node = get_partition_name(part) |
573 |
label = isys.readFSLabel(node) |
574 |
if label: |
575 |
labels[node] = label |
576 |
|
577 |
for dev, devices, level, numActive in DiskSet.mdList: |
578 |
label = isys.readFSLabel(dev) |
579 |
if label: |
580 |
labels[dev] = label |
581 |
|
582 |
return labels |
583 |
|
584 |
def findExistingRootPartitions(self, intf, mountpoint, upgradeany = 0): |
585 |
"""Return a list of all of the partitions which look like a root fs.""" |
586 |
rootparts = [] |
587 |
|
588 |
self.startAllRaid() |
589 |
|
590 |
for dev, devices, level, numActive in self.mdList: |
591 |
(errno, msg) = (None, None) |
592 |
found = 0 |
593 |
for fs in fsset.getFStoTry(dev): |
594 |
try: |
595 |
isys.mount(dev, mountpoint, fs, readOnly = 1) |
596 |
found = 1 |
597 |
break |
598 |
except SystemError, (errno, msg): |
599 |
pass |
600 |
|
601 |
if found: |
602 |
if os.access (mountpoint + '/etc/fstab', os.R_OK): |
603 |
relstr = getCentOSReleaseString(mountpoint) |
604 |
cmdline = open('/proc/cmdline', 'r').read() |
605 |
|
606 |
if ((cmdline.find("upgradeany") != -1) or |
607 |
(upgradeany == 1) or |
608 |
(productMatches(relstr, productName))): |
609 |
rootparts.append ((dev, fs, relstr)) |
610 |
isys.umount(mountpoint) |
611 |
|
612 |
# now, look for candidate lvm roots |
613 |
lvm.vgscan() |
614 |
lvm.vgactivate() |
615 |
|
616 |
for (vg, lv, size) in lvm.lvlist(): |
617 |
dev = "/dev/%s/%s" %(vg, lv) |
618 |
found = 0 |
619 |
for fs in fsset.getFStoTry(dev): |
620 |
try: |
621 |
isys.mount(dev, mountpoint, fs, readOnly = 1) |
622 |
found = 1 |
623 |
break |
624 |
except SystemError: |
625 |
pass |
626 |
|
627 |
if found: |
628 |
if os.access (mountpoint + '/etc/fstab', os.R_OK): |
629 |
relstr = getCentOSReleaseString(mountpoint) |
630 |
cmdline = open('/proc/cmdline', 'r').read() |
631 |
|
632 |
if ((cmdline.find("upgradeany") != -1) or |
633 |
(upgradeany == 1) or |
634 |
(productMatches(relstr, productName))): |
635 |
rootparts.append ((dev, fs, relstr)) |
636 |
isys.umount(mountpoint) |
637 |
|
638 |
lvm.vgdeactivate() |
639 |
|
640 |
# don't stop raid until after we've looked for lvm on top of it |
641 |
self.stopAllRaid() |
642 |
|
643 |
drives = self.disks.keys() |
644 |
drives.sort() |
645 |
|
646 |
for drive in drives: |
647 |
disk = self.disks[drive] |
648 |
part = disk.next_partition () |
649 |
while part: |
650 |
if (part.is_active() |
651 |
and (part.get_flag(parted.PARTITION_RAID) |
652 |
or part.get_flag(parted.PARTITION_LVM))): |
653 |
pass |
654 |
elif (part.fs_type and |
655 |
part.fs_type.name in fsset.getUsableLinuxFs()): |
656 |
node = get_partition_name(part) |
657 |
try: |
658 |
isys.mount(node, mountpoint, part.fs_type.name) |
659 |
except SystemError, (errno, msg): |
660 |
intf.messageWindow(_("Error"), |
661 |
_("Error mounting file system on " |
662 |
"%s: %s") % (node, msg)) |
663 |
part = disk.next_partition(part) |
664 |
continue |
665 |
if os.access (mountpoint + '/etc/fstab', os.R_OK): |
666 |
relstr = getCentOSReleaseString(mountpoint) |
667 |
cmdline = open('/proc/cmdline', 'r').read() |
668 |
|
669 |
if ((cmdline.find("upgradeany") != -1) or |
670 |
(upgradeany == 1) or |
671 |
(productMatches(relstr, productName))): |
672 |
rootparts.append ((node, part.fs_type.name, |
673 |
relstr)) |
674 |
isys.umount(mountpoint) |
675 |
|
676 |
part = disk.next_partition(part) |
677 |
return rootparts |
678 |
|
679 |
def driveList (self): |
680 |
"""Return the list of drives on the system.""" |
681 |
drives = isys.hardDriveDict().keys() |
682 |
drives.sort (isys.compareDrives) |
683 |
return drives |
684 |
|
685 |
def drivesByName (self): |
686 |
"""Return a dictionary of the drives on the system.""" |
687 |
return isys.hardDriveDict() |
688 |
|
689 |
def addPartition (self, device, type, spec): |
690 |
"""Add a new partition to the device. - UNUSED.""" |
691 |
if not self.disks.has_key (device): |
692 |
raise PartitioningError, ("unknown device passed to " |
693 |
"addPartition: %s" % (device,)) |
694 |
disk = self.disks[device] |
695 |
|
696 |
part = disk.next_partition () |
697 |
status = 0 |
698 |
while part: |
699 |
if (part.type == parted.PARTITION_FREESPACE |
700 |
and part.geom.length >= spec.size): |
701 |
newp = disk.partition_new (type, spec.fs_type, |
702 |
part.geom.start, |
703 |
part.geom.start + spec.size) |
704 |
constraint = disk.dev.constraint_any () |
705 |
try: |
706 |
disk.add_partition (newp, constraint) |
707 |
status = 1 |
708 |
break |
709 |
except parted.error, msg: |
710 |
raise PartitioningError, msg |
711 |
part = disk.next_partition (part) |
712 |
if not status: |
713 |
raise PartitioningError, ("Not enough free space on %s to create " |
714 |
"new partition" % (device,)) |
715 |
return newp |
716 |
|
717 |
def deleteAllPartitions (self): |
718 |
"""Delete all partitions from all disks. - UNUSED.""" |
719 |
for disk in self.disks.values(): |
720 |
disk.delete_all () |
721 |
|
722 |
def savePartitions (self): |
723 |
"""Write the partition tables out to the disks.""" |
724 |
for disk in self.disks.values(): |
725 |
disk.commit() |
726 |
#disk.close() |
727 |
del disk |
728 |
self.refreshDevices() |
729 |
|
730 |
def refreshDevices (self, intf = None, initAll = 0, |
731 |
zeroMbr = 0, clearDevs = []): |
732 |
"""Reread the state of the disks as they are on disk.""" |
733 |
self.closeDevices() |
734 |
self.disks = {} |
735 |
self.openDevices(intf, initAll, zeroMbr, clearDevs) |
736 |
|
737 |
def closeDevices (self): |
738 |
"""Close all of the disks which are open.""" |
739 |
for disk in self.disks.keys(): |
740 |
#self.disks[disk].close() |
741 |
del self.disks[disk] |
742 |
|
743 |
def dasdFmt (self, intf = None, drive = None): |
744 |
"""Format dasd devices (s390).""" |
745 |
|
746 |
if self.disks.has_key(drive): |
747 |
del self.disks[drive] |
748 |
|
749 |
w = intf.progressWindow (_("Initializing"), |
750 |
_("Please wait while formatting drive %s...\n" |
751 |
) % (drive,), 100) |
752 |
try: |
753 |
isys.makeDevInode(drive, '/tmp/' + drive) |
754 |
except: |
755 |
pass |
756 |
|
757 |
argList = [ "/sbin/dasdfmt", |
758 |
"-y", |
759 |
"-b", "4096", |
760 |
"-d", "cdl", |
761 |
"-P", |
762 |
"-F", |
763 |
"-f", |
764 |
"/tmp/%s" % drive] |
765 |
|
766 |
fd = os.open("/dev/null", os.O_RDWR | os.O_CREAT | os.O_APPEND) |
767 |
p = os.pipe() |
768 |
childpid = os.fork() |
769 |
if not childpid: |
770 |
os.close(p[0]) |
771 |
os.dup2(p[1], 1) |
772 |
os.dup2(fd, 2) |
773 |
os.close(p[1]) |
774 |
os.close(fd) |
775 |
os.execv(argList[0], argList) |
776 |
log("failed to exec %s", argList) |
777 |
os._exit(1) |
778 |
|
779 |
os.close(p[1]) |
780 |
|
781 |
num = '' |
782 |
sync = 0 |
783 |
s = 'a' |
784 |
while s: |
785 |
try: |
786 |
s = os.read(p[0], 1) |
787 |
os.write(fd, s) |
788 |
|
789 |
if s != '\n': |
790 |
try: |
791 |
num = num + s |
792 |
except: |
793 |
pass |
794 |
else: |
795 |
if num: |
796 |
val = string.split(num) |
797 |
if (val[0] == 'cyl'): |
798 |
# printf("cyl %5d of %5d | %3d%%\n", |
799 |
val = int(val[5][:-1]) |
800 |
w and w.set(val) |
801 |
# sync every 10% |
802 |
if sync + 10 <= val: |
803 |
isys.sync() |
804 |
sync = val |
805 |
num = '' |
806 |
except OSError, args: |
807 |
(errno, str) = args |
808 |
if (errno != 4): |
809 |
raise IOError, args |
810 |
|
811 |
try: |
812 |
(pid, status) = os.waitpid(childpid, 0) |
813 |
except OSError, (num, msg): |
814 |
print __name__, "waitpid:", msg |
815 |
|
816 |
os.close(fd) |
817 |
|
818 |
w and w.pop() |
819 |
|
820 |
if os.WIFEXITED(status) and (os.WEXITSTATUS(status) == 0): |
821 |
return 0 |
822 |
|
823 |
return 1 |
824 |
|
825 |
def openDevices (self, intf = None, initAll = 0, |
826 |
zeroMbr = 0, clearDevs = []): |
827 |
"""Open the disks on the system and skip unopenable devices.""" |
828 |
if self.disks: |
829 |
return |
830 |
for drive in self.driveList (): |
831 |
if drive in DiskSet.skippedDisks and not initAll: |
832 |
continue |
833 |
deviceFile = isys.makeDevInode(drive) |
834 |
if isys.driveIsRemovable(drive) and not flags.expert: |
835 |
DiskSet.skippedDisks.append(drive) |
836 |
continue |
837 |
# FIXME: need the right fix for z/VM formatted dasd |
838 |
if iutil.getArch() == "s390" and drive[:4] == "dasd" and isys.getDasdState(drive): |
839 |
devs = isys.getDasdDevPort() |
840 |
if intf is None: |
841 |
DiskSet.skippedDisks.append(drive) |
842 |
continue |
843 |
rc = intf.messageWindow(_("Warning"), |
844 |
_("The partition table on device %s (%s) was unreadable. " |
845 |
"To create new partitions it must be initialized, " |
846 |
"causing the loss of ALL DATA on this drive.\n\n" |
847 |
"This operation will override any previous " |
848 |
"installation choices about which drives to " |
849 |
"ignore.\n\n" |
850 |
"Would you like to initialize this drive, " |
851 |
"erasing ALL DATA?") |
852 |
% (drive, devs[drive]), type = "yesno") |
853 |
if rc == 0: |
854 |
DiskSet.skippedDisks.append(drive) |
855 |
continue |
856 |
else: |
857 |
if (self.dasdFmt(intf, drive)): |
858 |
DiskSet.skippedDisks.append(drive) |
859 |
continue |
860 |
|
861 |
try: |
862 |
dev = parted.PedDevice.get (deviceFile) |
863 |
except parted.error, msg: |
864 |
DiskSet.skippedDisks.append(drive) |
865 |
continue |
866 |
|
867 |
if (initAll and ((clearDevs is None) or (len(clearDevs) == 0) |
868 |
or drive in clearDevs) and not flags.test): |
869 |
if iutil.getArch() == "s390" and drive[:4] == "dasd": |
870 |
if (intf is None or self.dasdFmt(intf, drive)): |
871 |
DiskSet.skippedDisks.append(drive) |
872 |
continue |
873 |
else: |
874 |
try: |
875 |
disk = dev.disk_new_fresh(getDefaultDiskType()) |
876 |
disk.commit() |
877 |
self.disks[drive] = disk |
878 |
except parted.error, msg: |
879 |
DiskSet.skippedDisks.append(drive) |
880 |
continue |
881 |
|
882 |
try: |
883 |
disk = parted.PedDisk.new(dev) |
884 |
self.disks[drive] = disk |
885 |
except parted.error, msg: |
886 |
recreate = 0 |
887 |
if zeroMbr: |
888 |
log("zeroMBR was set and invalid partition table found " |
889 |
"on %s" % (dev.path[5:])) |
890 |
recreate = 1 |
891 |
elif not intf: |
892 |
DiskSet.skippedDisks.append(drive) |
893 |
continue |
894 |
else: |
895 |
if iutil.getArch() == "s390" and drive[:4] == "dasd": |
896 |
devs = isys.getDasdDevPort() |
897 |
format = drive + " (" + devs[drive] + ")" |
898 |
else: |
899 |
format = drive |
900 |
rc = intf.messageWindow(_("Warning"), |
901 |
_("The partition table on device %s was unreadable. " |
902 |
"To create new partitions it must be initialized, " |
903 |
"causing the loss of ALL DATA on this drive.\n\n" |
904 |
"This operation will override any previous " |
905 |
"installation choices about which drives to " |
906 |
"ignore.\n\n" |
907 |
"Would you like to initialize this drive, " |
908 |
"erasing ALL DATA?") |
909 |
% (format,), type = "yesno") |
910 |
if rc == 0: |
911 |
DiskSet.skippedDisks.append(drive) |
912 |
continue |
913 |
else: |
914 |
recreate = 1 |
915 |
|
916 |
if recreate == 1 and not flags.test: |
917 |
if iutil.getArch() == "s390" and drive[:4] == "dasd": |
918 |
if (intf is None or self.dasdFmt(intf, drive)): |
919 |
DiskSet.skippedDisks.append(drive) |
920 |
continue |
921 |
else: |
922 |
try: |
923 |
disk = dev.disk_new_fresh(getDefaultDiskType()) |
924 |
disk.commit() |
925 |
except parted.error, msg: |
926 |
DiskSet.skippedDisks.append(drive) |
927 |
continue |
928 |
try: |
929 |
disk = parted.PedDisk.new(dev) |
930 |
self.disks[drive] = disk |
931 |
except parted.error, msg: |
932 |
DiskSet.skippedDisks.append(drive) |
933 |
continue |
934 |
|
935 |
filter_partitions(disk, validateFsType) |
936 |
|
937 |
# check that their partition table is valid for their architecture |
938 |
ret = checkDiskLabel(disk, intf) |
939 |
if ret == 1: |
940 |
DiskSet.skippedDisks.append(drive) |
941 |
continue |
942 |
elif ret == -1: |
943 |
if iutil.getArch() == "s390" and drive[:4] == "dasd": |
944 |
if (intf is None or self.dasdFmt(intf, drive)): |
945 |
DiskSet.skippedDisks.append(drive) |
946 |
continue |
947 |
else: |
948 |
try: |
949 |
disk = dev.disk_new_fresh(getDefaultDiskType()) |
950 |
disk.commit() |
951 |
except parted.error, msg: |
952 |
DiskSet.skippedDisks.append(drive) |
953 |
continue |
954 |
try: |
955 |
disk = parted.PedDisk.new(dev) |
956 |
self.disks[drive] = disk |
957 |
except parted.error, msg: |
958 |
DiskSet.skippedDisks.append(drive) |
959 |
continue |
960 |
|
961 |
def partitionTypes (self): |
962 |
"""Return list of (partition, partition type) tuples for all parts.""" |
963 |
rc = [] |
964 |
drives = self.disks.keys() |
965 |
drives.sort() |
966 |
|
967 |
for drive in drives: |
968 |
disk = self.disks[drive] |
969 |
part = disk.next_partition () |
970 |
while part: |
971 |
if part.type in (parted.PARTITION_PRIMARY, |
972 |
parted.PARTITION_LOGICAL): |
973 |
device = get_partition_name(part) |
974 |
if part.fs_type: |
975 |
ptype = part.fs_type.name |
976 |
else: |
977 |
ptype = None |
978 |
rc.append((device, ptype)) |
979 |
part = disk.next_partition (part) |
980 |
|
981 |
return rc |
982 |
|
983 |
def diskState (self): |
984 |
"""Print out current disk state. DEBUG.""" |
985 |
rc = "" |
986 |
for disk in self.disks.values(): |
987 |
rc = rc + ("%s: %s length %ld, maximum " |
988 |
"primary partitions: %d\n" % |
989 |
(disk.dev.path, |
990 |
disk.dev.model, |
991 |
disk.dev.length, |
992 |
disk.max_primary_partition_count)) |
993 |
|
994 |
part = disk.next_partition() |
995 |
if part: |
996 |
rc = rc + ("Device Type Filesystem Start " |
997 |
"End Length Flags\n") |
998 |
rc = rc + ("------ ---- ---------- ----- " |
999 |
"--- ------ -----\n") |
1000 |
while part: |
1001 |
if not part.type & parted.PARTITION_METADATA: |
1002 |
device = "" |
1003 |
fs_type_name = "" |
1004 |
if part.num > 0: |
1005 |
device = get_partition_name(part) |
1006 |
if part.fs_type: |
1007 |
fs_type_name = part.fs_type.name |
1008 |
partFlags = get_flags (part) |
1009 |
rc = rc + ("%-9s %-12s %-12s %-10ld %-10ld %-10ld %7s\n" |
1010 |
% (device, part.type_name, fs_type_name, |
1011 |
part.geom.start, part.geom.end, part.geom.length, |
1012 |
partFlags)) |
1013 |
part = disk.next_partition(part) |
1014 |
return rc |
1015 |
|
1016 |
def checkNoDisks(self, intf): |
1017 |
"""Check that there are valid disk devices.""" |
1018 |
if len(self.disks.keys()) == 0: |
1019 |
intf.messageWindow(_("No Drives Found"), |
1020 |
_("An error has occurred - no valid devices were " |
1021 |
"found on which to create new file systems. " |
1022 |
"Please check your hardware for the cause " |
1023 |
"of this problem.")) |
1024 |
sys.exit(0) |
1025 |
|
1026 |
|
1027 |
|
1028 |
|
1029 |
|
1030 |
# XXX is this all of the possibilities? |
1031 |
dosPartitionTypes = [ 1, 6, 7, 11, 12, 14, 15 ] |
1032 |
|
1033 |
# master list of partition types |
1034 |
allPartitionTypesDict = { |
1035 |
0 : "Empty", |
1036 |
1: "DOS 12-bit FAT", |
1037 |
2: "XENIX root", |
1038 |
3: "XENIX usr", |
1039 |
4: "DOS 16-bit <32M", |
1040 |
5: "Extended", |
1041 |
6: "DOS 16-bit >=32M", |
1042 |
7: "NTFS/HPFS", |
1043 |
8: "AIX", |
1044 |
9: "AIX bootable", |
1045 |
10: "OS/2 Boot Manager", |
1046 |
0xb: "Win95 FAT32", |
1047 |
0xc: "Win95 FAT32", |
1048 |
0xe: "Win95 FAT16", |
1049 |
0xf: "Win95 Ext'd", |
1050 |
0x10: "OPUS", |
1051 |
0x11: "Hidden FAT12", |
1052 |
0x12: "Compaq Setup", |
1053 |
0x14: "Hidden FAT16 <32M", |
1054 |
0x16: "Hidden FAT16", |
1055 |
0x17: "Hidden HPFS/NTFS", |
1056 |
0x18: "AST SmartSleep", |
1057 |
0x1b: "Hidden Win95 FAT32", |
1058 |
0x1c: "Hidden Win95 FAT32 (LBA)", |
1059 |
0x1e: "Hidden Win95 FAT16 (LBA)", |
1060 |
0x24: "NEC_DOS", |
1061 |
0x39: "Plan 9", |
1062 |
0x40: "Venix 80286", |
1063 |
0x41: "PPC_PReP Boot", |
1064 |
0x42: "SFS", |
1065 |
0x4d: "QNX4.x", |
1066 |
0x4e: "QNX4.x 2nd part", |
1067 |
0x4f: "QNX4.x 2nd part", |
1068 |
0x51: "Novell?", |
1069 |
0x52: "Microport", |
1070 |
0x63: "GNU HURD", |
1071 |
0x64: "Novell Netware 286", |
1072 |
0x65: "Novell Netware 386", |
1073 |
0x75: "PC/IX", |
1074 |
0x80: "Old MINIX", |
1075 |
0x81: "Linux/MINIX", |
1076 |
0x82: "Linux swap", |
1077 |
0x83: "Linux native", |
1078 |
0x84: "OS/2 hidden C:", |
1079 |
0x85: "Linux Extended", |
1080 |
0x86: "NTFS volume set", |
1081 |
0x87: "NTFS volume set", |
1082 |
0x8e: "Linux LVM", |
1083 |
0x93: "Amoeba", |
1084 |
0x94: "Amoeba BBT", |
1085 |
0x9f: "BSD/OS", |
1086 |
0xa0: "IBM Thinkpad hibernation", |
1087 |
0xa5: "BSD/386", |
1088 |
0xa6: "OpenBSD", |
1089 |
0xb7: "BSDI fs", |
1090 |
0xb8: "BSDI swap", |
1091 |
0xc7: "Syrinx", |
1092 |
0xdb: "CP/M", |
1093 |
0xde: "Dell Utility", |
1094 |
0xe1: "DOS access", |
1095 |
0xe3: "DOS R/O", |
1096 |
0xeb: "BEOS", |
1097 |
0xee: "EFI GPT", |
1098 |
0xef: "EFI (FAT-12/16/32)", |
1099 |
0xf2: "DOS secondary", |
1100 |
0xfd: "Linux RAID", |
1101 |
0xff: "BBT" |
1102 |
} |
1103 |
|
1104 |
max_logical_partition_count = { |
1105 |
"hd": 59, |
1106 |
"sd": 11, |
1107 |
"ataraid/": 11, |
1108 |
"rd/": 3, |
1109 |
"cciss/": 11, |
1110 |
"i2o/": 11, |
1111 |
"iseries/vd": 3, |
1112 |
"ida/": 11, |
1113 |
"sx8/": 11, |
1114 |
} |