aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Gaffney <agaffney@gentoo.org>2006-09-01 23:04:18 +0000
committerAndrew Gaffney <agaffney@gentoo.org>2006-09-01 23:04:18 +0000
commit1555f942f8908b5c6e74e434733f3876b84b093c (patch)
tree1e0cda4e74bfabd07f32d4bf14c0844999c949c0
parentAdded some more applications to the Extra Packages list. (diff)
downloadgli-1555f942f8908b5c6e74e434733f3876b84b093c.tar.gz
gli-1555f942f8908b5c6e74e434733f3876b84b093c.tar.bz2
gli-1555f942f8908b5c6e74e434733f3876b84b093c.zip
move partitioning code to GLIAT
git-svn-id: svn+ssh://svn.gentoo.org/var/svnroot/gli/trunk@1504 f8877401-5920-0410-a79b-8e2d7e04ca0d
-rw-r--r--ChangeLog4
-rw-r--r--src/GLIArchitectureTemplate.py420
-rw-r--r--src/templates/x86ArchitectureTemplate.py419
3 files changed, 423 insertions, 420 deletions
diff --git a/ChangeLog b/ChangeLog
index 52b697a..b8e2912 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,6 @@
# ChangeLog for Gentoo Linux Installer
# Copyright 2005-2006 Gentoo Foundation; Distributed under the GPL v2
-# $Header: /var/cvsroot/gentoo/src/installer/ChangeLog,v 1.674 2006/09/01 22:41:04 wolf31o2 Exp $
+# $Header: /var/cvsroot/gentoo/src/installer/ChangeLog,v 1.675 2006/09/01 23:04:18 agaffney Exp $
01 Sep 2006; Chris Gianelloni <wolf31o2@gentoo.org>
src/GLIInstallProfile.py:
@@ -10,6 +10,8 @@
src/templates/ppcArchitectureTemplate.py:
this doesn't need to inherit from x86AT or create an infinite loop by calling
its own __init__()
+ src/GLIArchitectureTemplate.py,src/templates/x86ArchitectureTemplate.py:
+ move partitioning code to GLIAT
31 Aug 2006; Andrew Gaffney <agaffney@gentoo.org>
src/GLIArchitectureTemplate.py:
diff --git a/src/GLIArchitectureTemplate.py b/src/GLIArchitectureTemplate.py
index 346bc39..d9d4d02 100644
--- a/src/GLIArchitectureTemplate.py
+++ b/src/GLIArchitectureTemplate.py
@@ -5,7 +5,7 @@
# of which can be found in the main directory of this project.
Gentoo Linux Installer
-$Id: GLIArchitectureTemplate.py,v 1.285 2006/08/31 15:34:59 agaffney Exp $
+$Id: GLIArchitectureTemplate.py,v 1.286 2006/09/01 23:04:18 agaffney Exp $
The ArchitectureTemplate is largely meant to be an abstract class and an
interface (yes, it is both at the same time!). The purpose of this is to create
@@ -1362,3 +1362,421 @@ class ArchitectureTemplate:
# GLIUtility.spawn("rm /tmp/compile_output.log")
GLIUtility.spawn("mv " + self._client_configuration.get_log_file() + " " + self._client_configuration.get_log_file() + ".failed")
# GLIUtility.spawn("rm /var/log/installer.log")
+
+ def _sectors_to_megabytes(self, sectors, sector_bytes=512):
+ return float((float(sectors) * sector_bytes)/ float(MEGABYTE))
+
+ def _add_partition(self, disk, start, end, type, fs, name="", strict_start=False, strict_end=False):
+ if self._debug: self._logger.log("_add_partition(): type=%s, fstype=%s" % (type, fs))
+ types = { 'primary': parted.PARTITION_PRIMARY, 'extended': parted.PARTITION_EXTENDED, 'logical': parted.PARTITION_LOGICAL }
+ fsTypes = {}
+ fs_type = parted.file_system_type_get_next ()
+ while fs_type:
+ fsTypes[fs_type.name] = fs_type
+ fs_type = parted.file_system_type_get_next (fs_type)
+ fstype = None
+ if fs: fstype = fsTypes[fs]
+ newpart = disk.partition_new(types[type], fstype, start, end)
+ constraint = disk.dev.constraint_any()
+ if strict_start:
+ constraint.start_range.set_start(start)
+ constraint.start_range.set_end(start)
+ constraint.end_range.set_start(end)
+ if strict_end:
+ constraint.start_range.set_start(start)
+ constraint.end_range.set_start(end)
+ constraint.end_range.set_end(end)
+ disk.add_partition(newpart, constraint)
+ if self._debug: self._logger.log("_add_partition(): partition added")
+
+ def _delete_partition(self, parted_disk, minor):
+ try:
+ parted_disk.delete_partition(parted_disk.get_partition(minor))
+ except:
+ self._logger.log("_delete_partition(): could not delete partition...ignoring (for now)")
+
+ def _check_table_changed(self, oldparts, newparts):
+ for part in newparts:
+ if not newparts[part]['origminor'] or not oldparts.get_partition(part):
+ return True
+ oldpart = oldparts[part]
+ newpart = newparts[part]
+ if oldpart['type'] == newpart['type'] and oldpart['mb'] == newpart['mb'] and not newpart['resized'] and not newpart['format']:
+ continue
+ else:
+ return True
+ return False
+
+ def _check_table_layout_changed(self, oldparts, newparts):
+ # This function is similar to the above function except it will see it as un-changed even if a partition is just being reformatted
+ for part in newparts:
+ if not newparts[part]['origminor'] or not oldparts.get_partition(part):
+ return True
+ oldpart = oldparts[part]
+ newpart = newparts[part]
+ if oldpart['type'] == newpart['type'] and oldpart['mb'] == newpart['mb'] and not newpart['resized']:
+ continue
+ else:
+ return True
+ return False
+
+ def _find_existing_in_new(self, oldminor, newparts):
+ for part in newparts:
+ if newparts[part]['origminor'] == oldminor:
+ return part
+ return 0
+
+ def _check_keeping_any_existing(self, newparts):
+ for part in newparts:
+ if newparts[part]['origminor']: return True
+ return False
+
+ def _find_next_partition(self, curminor, parts):
+ foundmyself = False
+ for part in parts:
+ if not part == curminor and not foundmyself: continue
+ if part == curminor:
+ foundmyself = True
+ continue
+ if foundmyself:
+ return part
+ return 0
+
+ def _find_current_minor_for_part(self, device, start):
+ tmp_oldparts = GLIStorageDevice.Device(device, arch=self._client_configuration.get_architecture_template())
+ tmp_oldparts.set_partitions_from_disk()
+ for tmp_oldpart in tmp_oldparts:
+ self._logger.log("_find_current_minor_for_part(): looking at minor %s...start sector is %s...looking for %s" % (str(tmp_oldpart), str(tmp_oldparts[tmp_oldpart]['start']), str(start)))
+ if tmp_oldparts[tmp_oldpart]['start'] == start:
+ return tmp_oldparts[tmp_oldpart]['minor']
+ else:
+ raise GLIException("PartitionResizeError", 'fatal', '_find_current_minor_for_part', "Could not determine the new devnode for partition starting at sector " + str(start))
+
+ def _partition_delete_step(self, parted_disk, oldparts, newparts):
+ self._logger.log("_partition_delete_step(): Deleting partitions that aren't being resized")
+ for oldpart in list(oldparts)[::-1]:
+ tmppart_old = oldparts[oldpart]
+ if oldparts.get_disklabel() != "mac" and tmppart_old['type'] == "free": continue
+ if tmppart_old['type'] == "extended":
+ # Iterate through logicals to see if any are being resized
+ self._logger.log("_partition_delete_step(): logicals for extended part %d: %s" % (tmppart_old['minor'], str(tmppart_old.get_logicals())))
+ for logpart in tmppart_old.get_logicals():
+ newminor = self._find_existing_in_new(logpart, newparts)
+ self._logger.log("_partition_delete_step(): newminor is " + str(newminor))
+ if newminor and newparts[newminor]['resized']:
+ self._logger.log(" Logical partition " + str(logpart) + " to be resized...can't delete extended")
+ break
+ else:
+ self._logger.log(" No logical partitions are being resized...deleting extended")
+ self._delete_partition(parted_disk, oldpart)
+ else:
+ newminor = self._find_existing_in_new(oldpart, newparts)
+ if newminor and not newparts[newminor]['format']:
+ if newparts[newminor]['resized']:
+ self._logger.log(" Ignoring old minor " + str(oldpart) + " to resize later")
+ continue
+ else:
+ self._logger.log(" Deleting old minor " + str(oldpart) + " to be recreated later")
+ else:
+ self._logger.log(" No match in new layout for old minor " + str(oldpart) + "...deleting")
+ self._delete_partition(parted_disk, oldpart)
+ parted_disk.commit()
+
+ def _partition_resize_step(self, parted_disk, device, oldparts, newparts):
+ self._logger.log("_partition_resize_step(): Resizing partitions")
+ device_sectors = newparts.get_num_sectors()
+ for oldpart in oldparts:
+ tmppart_old = oldparts[oldpart]
+ newminor = self._find_existing_in_new(oldpart, newparts)
+ if not newminor or not newparts[newminor]['resized'] or newparts[newminor]['type'] in ("extended", "free"):
+ continue
+ tmppart_new = newparts[newminor]
+ type = tmppart_new['type']
+ start = tmppart_new['start']
+ end = start + (long(tmppart_new['mb']) * MEGABYTE / 512) - 1
+
+ # Make sure calculated end sector doesn't overlap start sector of next partition
+ nextminor = self._find_next_partition(newminor, newparts)
+ if nextminor:
+ if newparts[nextminor]['start'] and end >= newparts[nextminor]['start']:
+ self._logger.log(" End sector for growing partition overlaps with start of next partition...fixing")
+ end = newparts[nextminor]['start'] - 1
+
+ # cap to end of device
+ if end >= device_sectors:
+ end = device_sectors - 1
+
+ total_sectors = end - start + 1
+ total_bytes = long(total_sectors) * 512
+
+ # Delete partition and recreate at same start point with new size if growing
+ if tmppart_new['mb'] > tmppart_old['mb']:
+ curminor = self._find_current_minor_for_part(device, start)
+ self._delete_partition(parted_disk, curminor)
+ if tmppart_new.is_logical():
+ tmptype = "logical"
+ else:
+ tmptype = "primary"
+ self._add_partition(parted_disk, start, end, tmptype, tmppart_new['type'], strict_start=True)
+ parted_disk.commit()
+
+ curminor = self._find_current_minor_for_part(device, start)
+ devnode = device + str(curminor)
+
+ # sleep a bit first
+ time.sleep(3)
+ # now sleep until it exists
+ while not GLIUtility.is_file(devnode):
+ self._logger.log("Waiting for device node " + devnode + " to exist before resizing")
+ time.sleep(1)
+ # one bit of extra sleep is needed, as there is a blip still
+ time.sleep(3)
+
+ if type in ("ext2", "ext3"):
+ resizecmd = "resize2fs %s %sK" % (devnode, str(int((total_bytes - (2 * MEGABYTE)) / 1024)))
+ self._logger.log("_partition_resize_step(): running: " + resizecmd)
+ ret = GLIUtility.spawn(resizecmd, logfile=self._compile_logfile, append_log=True)
+ if not GLIUtility.exitsuccess(ret):
+ raise GLIException("PartitionResizeError", 'fatal', 'partition', "could not resize ext2/3 filesystem on " + devnode)
+ elif type == "ntfs":
+ ret = GLIUtility.spawn("yes | ntfsresize -v --size " + str(total_bytes) + " " + devnode, logfile=self._compile_logfile, append_log=True)
+ if not GLIUtility.exitsuccess(ret):
+ raise GLIException("PartitionResizeError", 'fatal', 'partition', "could not resize NTFS filesystem on " + devnode)
+ elif type in ("linux-swap", "fat32", "fat16"):
+ parted_fs = parted_disk.get_partition(curminor).geom.file_system_open()
+ resize_constraint = parted_fs.get_resize_constraint()
+ if total_sectors < resize_constraint.min_size or start != resize_constraint.start_range.start:
+ raise GLIException("PartitionError", 'fatal', 'partition', "New size specified for " + devnode + " is not within allowed boundaries (blame parted)")
+ new_geom = resize_constraint.start_range.duplicate()
+ new_geom.set_start(start)
+ new_geom.set_end(end)
+ try:
+ parted_fs.resize(new_geom)
+ except:
+ raise GLIException("PartitionResizeError", 'fatal', 'partition', "could not resize " + devnode)
+ self._logger.log(" Deleting old minor " + str(oldpart) + " to be recreated in next pass")
+# self._delete_partition(parted_disk, oldpart)
+ parted_disk.delete_all()
+ parted_disk.commit()
+
+ def _partition_recreate_step(self, parted_disk, newparts):
+ self._logger.log("_partition_recreate_step(): Recreating partitions")
+ start = 0
+ end = 0
+ extended_start = 0
+ extended_end = 0
+ device_sectors = newparts.get_num_sectors()
+ self._logger.log(" Drive has " + str(device_sectors) + " sectors")
+ for part in newparts:
+ strict_start = False
+ strict_end = False
+ newpart = newparts[part]
+ self._logger.log(" Partition " + str(part) + " has " + str(newpart['mb']) + "MB")
+ if newpart['start']:
+ self._logger.log(" Old start sector " + str(newpart['start']) + " retrieved")
+ if start != newpart['start']:
+ self._logger.log(" Retrieved start sector is not the same as the calculated next start sector (usually not an issue)")
+ start = newpart['start']
+ strict_start = True
+ else:
+ if newpart.is_logical() and start > extended_end:
+ start = extended_start + 1
+ self._logger.log(" Start sector calculated to be " + str(start))
+ if extended_end and not newpart.is_logical() and start <= extended_end:
+ self._logger.log(" Start sector for primary is less than the end sector for previous extended")
+ start = extended_end + 1
+ if newpart['end']:
+ self._logger.log(" Old end sector " + str(newpart['end']) + " retrieved")
+ end = newpart['end']
+ part_sectors = end - start + 1
+ strict_end = True
+ else:
+ part_sectors = long(newpart['mb']) * MEGABYTE / 512
+ end = start + part_sectors
+ if newpart.is_logical() and end > extended_end:
+ end = extended_end
+ self._logger.log(" End sector calculated to be " + str(end))
+ # Make sure end doesn't overlap next partition's existing start sector
+ nextminor = self._find_next_partition(newpart, newparts)
+ if nextminor:
+ if newparts[nextminor]['start'] and end >= newparts[nextminor]['start']:
+ self._logger.log(" End sector for partition overlaps with start of next partition...fixing")
+ end = newparts[nextminor]['start'] - 1
+ strict_end = True
+ # cap to end of device
+ if end >= device_sectors:
+ end = device_sectors - 1
+ # now the actual creation
+ if newpart['type'] == "free":
+ if newparts.get_disklabel() == "mac":
+ # Create a dummy partition to be removed later because parted sucks
+ self._logger.log(" Adding dummy partition to fool parted " + str(part) + " from " + str(start) + " to " + str(end))
+ self._add_partition(parted_disk, start, end, "primary", "ext2", "free", strict_start=strict_start, strict_end=strict_end)
+ elif newpart['type'] == "extended":
+ self._logger.log(" Adding extended partition " + str(part) + " from " + str(start) + " to " + str(end))
+ self._add_partition(parted_disk, start, end, "extended", "", strict_start=strict_start, strict_end=strict_end)
+ extended_start = start
+ extended_end = end
+ elif not newpart.is_logical():
+ self._logger.log(" Adding primary partition " + str(part) + " from " + str(start) + " to " + str(end))
+ self._add_partition(parted_disk, start, end, "primary", newpart['type'], strict_start=strict_start, strict_end=strict_end)
+ elif newpart.is_logical():
+ if start >= extended_end:
+ start = extended_start + 1
+ end = start + part_sectors
+ if nextminor and not newparts[nextminor].is_logical() and end > extended_end:
+ end = extended_end
+ self._logger.log(" Adding logical partition " + str(part) + " from " + str(start) + " to " + str(end))
+ self._add_partition(parted_disk, start, end, "logical", newpart['type'], strict_start=strict_start, strict_end=strict_end)
+ if self._debug: self._logger.log("partition(): flags: " + str(newpart['flags']))
+ for flag in newpart['flags']:
+ if parted_disk.get_partition(part).is_flag_available(flag):
+ parted_disk.get_partition(part).set_flag(flag, True)
+ if newpart['name'] and parted_disk.type.check_feature(parted.DISK_TYPE_PARTITION_NAME):
+ parted_disk.set_name(newpart['name'])
+ # write to disk
+ if self._debug: self._logger.log("partition(): committing change to disk")
+ parted_disk.commit()
+ if self._debug: self._logger.log("partition(): committed change to disk")
+ start = end + 1
+
+ def _partition_format_step(self, parted_disk, device, newparts):
+ self._logger.log("_partition_format_step(): Formatting new partitions")
+ for part in newparts:
+ newpart = newparts[part]
+ devnode = newpart['devnode']
+ # This little hack is necessary because parted sucks goat nuts
+ if newparts.get_disklabel() == "mac" and newpart['type'] == "free":
+ self._delete_partition(parted_disk, newpart)
+ continue
+ if newpart['format'] and newpart['type'] not in ('extended', 'free'):
+# devnode = device + str(int(part))
+ if self._debug: self._logger.log("_partition_format_step(): devnode is %s in formatting code" % devnode)
+ # if you need a special command and
+ # some base options, place it here.
+ format_cmds = { 'linux-swap': "mkswap", 'fat16': "mkfs.vfat -F 16", 'fat32': "mkfs.vfat -F 32",
+ 'ntfs': "mkntfs", 'xfs': "mkfs.xfs -f", 'jfs': "mkfs.jfs -f",
+ 'reiserfs': "mkfs.reiserfs -f", 'ext2': "mkfs.ext2", 'ext3': "mkfs.ext3"
+ }
+ if newpart['type'] in format_cmds:
+ cmdname = format_cmds[newpart['type']]
+ else: # this should catch everything else
+ raise GLIException("PartitionFormatError", 'fatal', '_partition_format_step', "Unknown partition type " + newpart['type'])
+ # sleep a bit first
+ time.sleep(1)
+ for tries in range(10):
+ cmd = "%s %s %s" % (cmdname, newpart['mkfsopts'], devnode)
+ self._logger.log(" Formatting partition %s as %s with: %s" % (str(part),newpart['type'],cmd))
+ ret = GLIUtility.spawn(cmd, logfile=self._compile_logfile, append_log=True)
+ if not GLIUtility.exitsuccess(ret):
+ self._logger.log("Try %d failed formatting partition %s...waiting 5 seconds" % (tries+1, devnode))
+ time.sleep(5)
+ else:
+ break
+ else:
+ raise GLIException("PartitionFormatError", 'fatal', '_partition_format_step', "Could not create %s filesystem on %s" % (newpart['type'], devnode))
+
+ def partition(self):
+ """
+ TODO:
+ before step 3, wipe drive and use the default disklabel for arch
+ skip fixed partitions in all passes (in GLISD maybe?)
+ """
+ parts_old = {}
+ parts_new = self._install_profile.get_partition_tables()
+ for device in GLIStorageDevice.detect_devices():
+ parts_old[device] = GLIStorageDevice.Device(device, arch=self._client_configuration.get_architecture_template())
+ parts_old[device].set_partitions_from_disk()
+
+ self.notify_frontend("progress", (0, "Examining partitioning data"))
+ total_steps = float(len(parts_new) * 4) # 4 for the number of passes over each device
+ cur_progress = 0
+ for device in parts_new:
+ # Skip this device in parts_new if device isn't detected on current system
+ if not device in parts_old:
+ self._logger.log("There is no physical device " + device + " detected to match the entry in the install profile...skipping")
+ continue
+
+ # This just makes things simpler in the code
+ newparts = parts_new[device]
+ oldparts = parts_old[device]
+
+ # Check to see if the old and new partition table structures are the same...skip if they are
+ if not self._check_table_changed(oldparts, newparts):
+ self._logger.log("Partition table for " + device + " is unchanged...skipping")
+ continue
+
+ self._logger.log("partition(): Processing " + device + "...")
+
+ # Commit ritual sepuku if there are any mounted filesystems on this device
+ if GLIUtility.spawn("mount | grep '^" + device + "'", return_output=True)[1].strip():
+ raise GLIException("PartitionsMountedError", 'fatal', 'partition', "Cannot partition " + device + " due to filesystems being mounted")
+
+ # We also can't handle "unknown" partitions
+ for part in newparts:
+ if newparts[part]['type'] == "unknown":
+ raise GLIException("UnknownPartitionTypeError", 'fatal', 'partition', "Refusing to partition this drive due to the presence of an unknown type of partition")
+
+ # Create pyparted objects for this device
+ parted_dev = parted.PedDevice.get(device)
+ try:
+ parted_disk = parted.PedDisk.new(parted_dev)
+ except:
+ if self._debug: self._logger.log("partition(): could not load existing disklabel...creating new one")
+ parted_disk = parted_dev.disk_new_fresh(parted.disk_type_get((newparts.get_disklabel() or GLIStorageDevice.archinfo[self._architecture_name])))
+
+ # Iterate through new partitions and check for 'origminor' and 'format' == False
+ for part in newparts:
+ tmppart_new = newparts[part]
+ if not tmppart_new['origminor'] or tmppart_new['format']: continue
+ if not tmppart_new['origminor'] in oldparts:
+ raise GLIException("MissingPartitionsError", 'fatal', 'partition', "Cannot find the existing partition that a new one refers to. This is not a bug. This is in fact your (the user's) fault. You should not reuse the installprofile.xml from a previous install that started the partitioning step.")
+ tmppart_old = oldparts[tmppart_new['origminor']]
+ if parted_disk.type.check_feature(parted.DISK_TYPE_PARTITION_NAME):
+ tmppart_new['name'] = tmppart_old['name']
+ tmppart_new['flags'] = tmppart_old['flags']
+ if tmppart_new['resized']:
+ # Partition is being resized in the new layout
+ self._logger.log(" Partition " + str(part) + " has origminor " + str(tmppart_new['origminor']) + " and it being resized...saving start sector " + str(tmppart_old['start']))
+ tmppart_new['start'] = tmppart_old['start']
+ tmppart_new['end'] = 0
+ else:
+ # Partition is untouched in the new layout
+ self._logger.log(" Partition " + str(part) + " has origminor " + str(tmppart_new['origminor']) + "...saving start sector " + str(tmppart_old['start']) + " and end sector " + str(tmppart_old['end']))
+ tmppart_new['start'] = tmppart_old['start']
+ tmppart_new['end'] = tmppart_old['end']
+
+ if self._check_table_layout_changed(parts_old[device], parts_new[device]):
+ # First pass to delete old partitions that aren't resized
+ self.notify_frontend("progress", (cur_progress / total_steps, "Deleting partitioning that aren't being resized for " + device))
+ cur_progress += 1
+ self._partition_delete_step(parted_disk, oldparts, newparts)
+
+ # Second pass to resize old partitions that need to be resized
+ self.notify_frontend("progress", (cur_progress / total_steps, "Resizing remaining partitions for " + device))
+ cur_progress += 1
+ self._partition_resize_step(parted_disk, device, oldparts, newparts)
+
+ # Wiping disk and creating blank disklabel
+ try:
+ parted_disk = parted_dev.disk_new_fresh(parted.disk_type_get(newparts.get_disklabel()))
+ parted_disk.commit()
+ except:
+ raise GLIException("DiskLabelCreationError", 'fatal', 'partition', "Could not create a blank disklabel!")
+
+ # Third pass to create new partition table
+ self.notify_frontend("progress", (cur_progress / total_steps, "Recreating partition table for " + device))
+ cur_progress += 1
+ self._partition_recreate_step(parted_disk, newparts)
+ else:
+ cur_progress += 3
+
+ # Fourth pass to format partitions
+ self.notify_frontend("progress", (cur_progress / total_steps, "Formatting partitions for " + device))
+ cur_progress += 1
+ self._partition_format_step(parted_disk, device, newparts)
+
+ # All done for this device
+ self.notify_frontend("progress", (cur_progress / total_steps, "Done with partitioning for " + device))
+ cur_progress += 1
+
diff --git a/src/templates/x86ArchitectureTemplate.py b/src/templates/x86ArchitectureTemplate.py
index aa832c0..8b974e0 100644
--- a/src/templates/x86ArchitectureTemplate.py
+++ b/src/templates/x86ArchitectureTemplate.py
@@ -5,7 +5,7 @@
# of which can be found in the main directory of this project.
Gentoo Linux Installer
-$Id: x86ArchitectureTemplate.py,v 1.143 2006/08/21 14:40:50 agaffney Exp $
+$Id: x86ArchitectureTemplate.py,v 1.144 2006/09/01 23:04:18 agaffney Exp $
Copyright 2004 Gentoo Technologies Inc.
@@ -55,423 +55,6 @@ class x86ArchitectureTemplate(ArchitectureTemplate):
else:
raise GLIException("BootLoaderError",'fatal','install_bootloader',"Don't know how to configure this bootloader: "+bootloader_pkg)
- def _sectors_to_megabytes(self, sectors, sector_bytes=512):
- return float((float(sectors) * sector_bytes)/ float(MEGABYTE))
-
- def _add_partition(self, disk, start, end, type, fs, name="", strict_start=False, strict_end=False):
- if self._debug: self._logger.log("_add_partition(): type=%s, fstype=%s" % (type, fs))
- types = { 'primary': parted.PARTITION_PRIMARY, 'extended': parted.PARTITION_EXTENDED, 'logical': parted.PARTITION_LOGICAL }
- fsTypes = {}
- fs_type = parted.file_system_type_get_next ()
- while fs_type:
- fsTypes[fs_type.name] = fs_type
- fs_type = parted.file_system_type_get_next (fs_type)
- fstype = None
- if fs: fstype = fsTypes[fs]
- newpart = disk.partition_new(types[type], fstype, start, end)
- constraint = disk.dev.constraint_any()
- if strict_start:
- constraint.start_range.set_start(start)
- constraint.start_range.set_end(start)
- constraint.end_range.set_start(end)
- if strict_end:
- constraint.start_range.set_start(start)
- constraint.end_range.set_start(end)
- constraint.end_range.set_end(end)
- disk.add_partition(newpart, constraint)
- if self._debug: self._logger.log("_add_partition(): partition added")
-
- def _delete_partition(self, parted_disk, minor):
- try:
- parted_disk.delete_partition(parted_disk.get_partition(minor))
- except:
- self._logger.log("_delete_partition(): could not delete partition...ignoring (for now)")
-
- def _check_table_changed(self, oldparts, newparts):
- for part in newparts:
- if not newparts[part]['origminor'] or not oldparts.get_partition(part):
- return True
- oldpart = oldparts[part]
- newpart = newparts[part]
- if oldpart['type'] == newpart['type'] and oldpart['mb'] == newpart['mb'] and not newpart['resized'] and not newpart['format']:
- continue
- else:
- return True
- return False
-
- def _check_table_layout_changed(self, oldparts, newparts):
- # This function is similar to the above function except it will see it as un-changed even if a partition is just being reformatted
- for part in newparts:
- if not newparts[part]['origminor'] or not oldparts.get_partition(part):
- return True
- oldpart = oldparts[part]
- newpart = newparts[part]
- if oldpart['type'] == newpart['type'] and oldpart['mb'] == newpart['mb'] and not newpart['resized']:
- continue
- else:
- return True
- return False
-
- def _find_existing_in_new(self, oldminor, newparts):
- for part in newparts:
- if newparts[part]['origminor'] == oldminor:
- return part
- return 0
-
- def _check_keeping_any_existing(self, newparts):
- for part in newparts:
- if newparts[part]['origminor']: return True
- return False
-
- def _find_next_partition(self, curminor, parts):
- foundmyself = False
- for part in parts:
- if not part == curminor and not foundmyself: continue
- if part == curminor:
- foundmyself = True
- continue
- if foundmyself:
- return part
- return 0
-
- def _find_current_minor_for_part(self, device, start):
- tmp_oldparts = GLIStorageDevice.Device(device, arch=self._client_configuration.get_architecture_template())
- tmp_oldparts.set_partitions_from_disk()
- for tmp_oldpart in tmp_oldparts:
- self._logger.log("_find_current_minor_for_part(): looking at minor %s...start sector is %s...looking for %s" % (str(tmp_oldpart), str(tmp_oldparts[tmp_oldpart]['start']), str(start)))
- if tmp_oldparts[tmp_oldpart]['start'] == start:
- return tmp_oldparts[tmp_oldpart]['minor']
- else:
- raise GLIException("PartitionResizeError", 'fatal', '_find_current_minor_for_part', "Could not determine the new devnode for partition starting at sector " + str(start))
-
- def _partition_delete_step(self, parted_disk, oldparts, newparts):
- self._logger.log("_partition_delete_step(): Deleting partitions that aren't being resized")
- for oldpart in list(oldparts)[::-1]:
- tmppart_old = oldparts[oldpart]
- if oldparts.get_disklabel() != "mac" and tmppart_old['type'] == "free": continue
- if tmppart_old['type'] == "extended":
- # Iterate through logicals to see if any are being resized
- self._logger.log("_partition_delete_step(): logicals for extended part %d: %s" % (tmppart_old['minor'], str(tmppart_old.get_logicals())))
- for logpart in tmppart_old.get_logicals():
- newminor = self._find_existing_in_new(logpart, newparts)
- self._logger.log("_partition_delete_step(): newminor is " + str(newminor))
- if newminor and newparts[newminor]['resized']:
- self._logger.log(" Logical partition " + str(logpart) + " to be resized...can't delete extended")
- break
- else:
- self._logger.log(" No logical partitions are being resized...deleting extended")
- self._delete_partition(parted_disk, oldpart)
- else:
- newminor = self._find_existing_in_new(oldpart, newparts)
- if newminor and not newparts[newminor]['format']:
- if newparts[newminor]['resized']:
- self._logger.log(" Ignoring old minor " + str(oldpart) + " to resize later")
- continue
- else:
- self._logger.log(" Deleting old minor " + str(oldpart) + " to be recreated later")
- else:
- self._logger.log(" No match in new layout for old minor " + str(oldpart) + "...deleting")
- self._delete_partition(parted_disk, oldpart)
- parted_disk.commit()
-
- def _partition_resize_step(self, parted_disk, device, oldparts, newparts):
- self._logger.log("_partition_resize_step(): Resizing partitions")
- device_sectors = newparts.get_num_sectors()
- for oldpart in oldparts:
- tmppart_old = oldparts[oldpart]
- newminor = self._find_existing_in_new(oldpart, newparts)
- if not newminor or not newparts[newminor]['resized'] or newparts[newminor]['type'] in ("extended", "free"):
- continue
- tmppart_new = newparts[newminor]
- type = tmppart_new['type']
- start = tmppart_new['start']
- end = start + (long(tmppart_new['mb']) * MEGABYTE / 512) - 1
-
- # Make sure calculated end sector doesn't overlap start sector of next partition
- nextminor = self._find_next_partition(newminor, newparts)
- if nextminor:
- if newparts[nextminor]['start'] and end >= newparts[nextminor]['start']:
- self._logger.log(" End sector for growing partition overlaps with start of next partition...fixing")
- end = newparts[nextminor]['start'] - 1
-
- # cap to end of device
- if end >= device_sectors:
- end = device_sectors - 1
-
- total_sectors = end - start + 1
- total_bytes = long(total_sectors) * 512
-
- # Delete partition and recreate at same start point with new size if growing
- if tmppart_new['mb'] > tmppart_old['mb']:
- curminor = self._find_current_minor_for_part(device, start)
- self._delete_partition(parted_disk, curminor)
- if tmppart_new.is_logical():
- tmptype = "logical"
- else:
- tmptype = "primary"
- self._add_partition(parted_disk, start, end, tmptype, tmppart_new['type'], strict_start=True)
- parted_disk.commit()
-
- curminor = self._find_current_minor_for_part(device, start)
- devnode = device + str(curminor)
-
- # sleep a bit first
- time.sleep(3)
- # now sleep until it exists
- while not GLIUtility.is_file(devnode):
- self._logger.log("Waiting for device node " + devnode + " to exist before resizing")
- time.sleep(1)
- # one bit of extra sleep is needed, as there is a blip still
- time.sleep(3)
-
- if type in ("ext2", "ext3"):
- resizecmd = "resize2fs %s %sK" % (devnode, str(int((total_bytes - (2 * MEGABYTE)) / 1024)))
- self._logger.log("_partition_resize_step(): running: " + resizecmd)
- ret = GLIUtility.spawn(resizecmd, logfile=self._compile_logfile, append_log=True)
- if not GLIUtility.exitsuccess(ret):
- raise GLIException("PartitionResizeError", 'fatal', 'partition', "could not resize ext2/3 filesystem on " + devnode)
- elif type == "ntfs":
- ret = GLIUtility.spawn("yes | ntfsresize -v --size " + str(total_bytes) + " " + devnode, logfile=self._compile_logfile, append_log=True)
- if not GLIUtility.exitsuccess(ret):
- raise GLIException("PartitionResizeError", 'fatal', 'partition', "could not resize NTFS filesystem on " + devnode)
- elif type in ("linux-swap", "fat32", "fat16"):
- parted_fs = parted_disk.get_partition(curminor).geom.file_system_open()
- resize_constraint = parted_fs.get_resize_constraint()
- if total_sectors < resize_constraint.min_size or start != resize_constraint.start_range.start:
- raise GLIException("PartitionError", 'fatal', 'partition', "New size specified for " + devnode + " is not within allowed boundaries (blame parted)")
- new_geom = resize_constraint.start_range.duplicate()
- new_geom.set_start(start)
- new_geom.set_end(end)
- try:
- parted_fs.resize(new_geom)
- except:
- raise GLIException("PartitionResizeError", 'fatal', 'partition', "could not resize " + devnode)
- self._logger.log(" Deleting old minor " + str(oldpart) + " to be recreated in next pass")
-# self._delete_partition(parted_disk, oldpart)
- parted_disk.delete_all()
- parted_disk.commit()
-
- def _partition_recreate_step(self, parted_disk, newparts):
- self._logger.log("_partition_recreate_step(): Recreating partitions")
- start = 0
- end = 0
- extended_start = 0
- extended_end = 0
- device_sectors = newparts.get_num_sectors()
- self._logger.log(" Drive has " + str(device_sectors) + " sectors")
- for part in newparts:
- strict_start = False
- strict_end = False
- newpart = newparts[part]
- self._logger.log(" Partition " + str(part) + " has " + str(newpart['mb']) + "MB")
- if newpart['start']:
- self._logger.log(" Old start sector " + str(newpart['start']) + " retrieved")
- if start != newpart['start']:
- self._logger.log(" Retrieved start sector is not the same as the calculated next start sector (usually not an issue)")
- start = newpart['start']
- strict_start = True
- else:
- if newpart.is_logical() and start > extended_end:
- start = extended_start + 1
- self._logger.log(" Start sector calculated to be " + str(start))
- if extended_end and not newpart.is_logical() and start <= extended_end:
- self._logger.log(" Start sector for primary is less than the end sector for previous extended")
- start = extended_end + 1
- if newpart['end']:
- self._logger.log(" Old end sector " + str(newpart['end']) + " retrieved")
- end = newpart['end']
- part_sectors = end - start + 1
- strict_end = True
- else:
- part_sectors = long(newpart['mb']) * MEGABYTE / 512
- end = start + part_sectors
- if newpart.is_logical() and end > extended_end:
- end = extended_end
- self._logger.log(" End sector calculated to be " + str(end))
- # Make sure end doesn't overlap next partition's existing start sector
- nextminor = self._find_next_partition(newpart, newparts)
- if nextminor:
- if newparts[nextminor]['start'] and end >= newparts[nextminor]['start']:
- self._logger.log(" End sector for partition overlaps with start of next partition...fixing")
- end = newparts[nextminor]['start'] - 1
- strict_end = True
- # cap to end of device
- if end >= device_sectors:
- end = device_sectors - 1
- # now the actual creation
- if newpart['type'] == "free":
- if newparts.get_disklabel() == "mac":
- # Create a dummy partition to be removed later because parted sucks
- self._logger.log(" Adding dummy partition to fool parted " + str(part) + " from " + str(start) + " to " + str(end))
- self._add_partition(parted_disk, start, end, "primary", "ext2", "free", strict_start=strict_start, strict_end=strict_end)
- elif newpart['type'] == "extended":
- self._logger.log(" Adding extended partition " + str(part) + " from " + str(start) + " to " + str(end))
- self._add_partition(parted_disk, start, end, "extended", "", strict_start=strict_start, strict_end=strict_end)
- extended_start = start
- extended_end = end
- elif not newpart.is_logical():
- self._logger.log(" Adding primary partition " + str(part) + " from " + str(start) + " to " + str(end))
- self._add_partition(parted_disk, start, end, "primary", newpart['type'], strict_start=strict_start, strict_end=strict_end)
- elif newpart.is_logical():
- if start >= extended_end:
- start = extended_start + 1
- end = start + part_sectors
- if nextminor and not newparts[nextminor].is_logical() and end > extended_end:
- end = extended_end
- self._logger.log(" Adding logical partition " + str(part) + " from " + str(start) + " to " + str(end))
- self._add_partition(parted_disk, start, end, "logical", newpart['type'], strict_start=strict_start, strict_end=strict_end)
- if self._debug: self._logger.log("partition(): flags: " + str(newpart['flags']))
- for flag in newpart['flags']:
- if parted_disk.get_partition(part).is_flag_available(flag):
- parted_disk.get_partition(part).set_flag(flag, True)
- if newpart['name'] and parted_disk.type.check_feature(parted.DISK_TYPE_PARTITION_NAME):
- parted_disk.set_name(newpart['name'])
- # write to disk
- if self._debug: self._logger.log("partition(): committing change to disk")
- parted_disk.commit()
- if self._debug: self._logger.log("partition(): committed change to disk")
- start = end + 1
-
- def _partition_format_step(self, parted_disk, device, newparts):
- self._logger.log("_partition_format_step(): Formatting new partitions")
- for part in newparts:
- newpart = newparts[part]
- devnode = newpart['devnode']
- # This little hack is necessary because parted sucks goat nuts
- if newparts.get_disklabel() == "mac" and newpart['type'] == "free":
- self._delete_partition(parted_disk, newpart)
- continue
- if newpart['format'] and newpart['type'] not in ('extended', 'free'):
-# devnode = device + str(int(part))
- if self._debug: self._logger.log("_partition_format_step(): devnode is %s in formatting code" % devnode)
- # if you need a special command and
- # some base options, place it here.
- format_cmds = { 'linux-swap': "mkswap", 'fat16': "mkfs.vfat -F 16", 'fat32': "mkfs.vfat -F 32",
- 'ntfs': "mkntfs", 'xfs': "mkfs.xfs -f", 'jfs': "mkfs.jfs -f",
- 'reiserfs': "mkfs.reiserfs -f", 'ext2': "mkfs.ext2", 'ext3': "mkfs.ext3"
- }
- if newpart['type'] in format_cmds:
- cmdname = format_cmds[newpart['type']]
- else: # this should catch everything else
- raise GLIException("PartitionFormatError", 'fatal', '_partition_format_step', "Unknown partition type " + newpart['type'])
- # sleep a bit first
- time.sleep(1)
- for tries in range(10):
- cmd = "%s %s %s" % (cmdname, newpart['mkfsopts'], devnode)
- self._logger.log(" Formatting partition %s as %s with: %s" % (str(part),newpart['type'],cmd))
- ret = GLIUtility.spawn(cmd, logfile=self._compile_logfile, append_log=True)
- if not GLIUtility.exitsuccess(ret):
- self._logger.log("Try %d failed formatting partition %s...waiting 5 seconds" % (tries+1, devnode))
- time.sleep(5)
- else:
- break
- else:
- raise GLIException("PartitionFormatError", 'fatal', '_partition_format_step', "Could not create %s filesystem on %s" % (newpart['type'], devnode))
-
- def partition(self):
- """
- TODO:
- before step 3, wipe drive and use the default disklabel for arch
- skip fixed partitions in all passes (in GLISD maybe?)
- """
- parts_old = {}
- parts_new = self._install_profile.get_partition_tables()
- for device in GLIStorageDevice.detect_devices():
- parts_old[device] = GLIStorageDevice.Device(device, arch=self._client_configuration.get_architecture_template())
- parts_old[device].set_partitions_from_disk()
-
- self.notify_frontend("progress", (0, "Examining partitioning data"))
- total_steps = float(len(parts_new) * 4) # 4 for the number of passes over each device
- cur_progress = 0
- for device in parts_new:
- # Skip this device in parts_new if device isn't detected on current system
- if not device in parts_old:
- self._logger.log("There is no physical device " + device + " detected to match the entry in the install profile...skipping")
- continue
-
- # This just makes things simpler in the code
- newparts = parts_new[device]
- oldparts = parts_old[device]
-
- # Check to see if the old and new partition table structures are the same...skip if they are
- if not self._check_table_changed(oldparts, newparts):
- self._logger.log("Partition table for " + device + " is unchanged...skipping")
- continue
-
- self._logger.log("partition(): Processing " + device + "...")
-
- # Commit ritual sepuku if there are any mounted filesystems on this device
- if GLIUtility.spawn("mount | grep '^" + device + "'", return_output=True)[1].strip():
- raise GLIException("PartitionsMountedError", 'fatal', 'partition', "Cannot partition " + device + " due to filesystems being mounted")
-
- # We also can't handle "unknown" partitions
- for part in newparts:
- if newparts[part]['type'] == "unknown":
- raise GLIException("UnknownPartitionTypeError", 'fatal', 'partition', "Refusing to partition this drive due to the presence of an unknown type of partition")
-
- # Create pyparted objects for this device
- parted_dev = parted.PedDevice.get(device)
- try:
- parted_disk = parted.PedDisk.new(parted_dev)
- except:
- if self._debug: self._logger.log("partition(): could not load existing disklabel...creating new one")
- parted_disk = parted_dev.disk_new_fresh(parted.disk_type_get((newparts.get_disklabel() or GLIStorageDevice.archinfo[self._architecture_name])))
-
- # Iterate through new partitions and check for 'origminor' and 'format' == False
- for part in newparts:
- tmppart_new = newparts[part]
- if not tmppart_new['origminor'] or tmppart_new['format']: continue
- if not tmppart_new['origminor'] in oldparts:
- raise GLIException("MissingPartitionsError", 'fatal', 'partition', "Cannot find the existing partition that a new one refers to. This is not a bug. This is in fact your (the user's) fault. You should not reuse the installprofile.xml from a previous install that started the partitioning step.")
- tmppart_old = oldparts[tmppart_new['origminor']]
- if parted_disk.type.check_feature(parted.DISK_TYPE_PARTITION_NAME):
- tmppart_new['name'] = tmppart_old['name']
- tmppart_new['flags'] = tmppart_old['flags']
- if tmppart_new['resized']:
- # Partition is being resized in the new layout
- self._logger.log(" Partition " + str(part) + " has origminor " + str(tmppart_new['origminor']) + " and it being resized...saving start sector " + str(tmppart_old['start']))
- tmppart_new['start'] = tmppart_old['start']
- tmppart_new['end'] = 0
- else:
- # Partition is untouched in the new layout
- self._logger.log(" Partition " + str(part) + " has origminor " + str(tmppart_new['origminor']) + "...saving start sector " + str(tmppart_old['start']) + " and end sector " + str(tmppart_old['end']))
- tmppart_new['start'] = tmppart_old['start']
- tmppart_new['end'] = tmppart_old['end']
-
- if self._check_table_layout_changed(parts_old[device], parts_new[device]):
- # First pass to delete old partitions that aren't resized
- self.notify_frontend("progress", (cur_progress / total_steps, "Deleting partitioning that aren't being resized for " + device))
- cur_progress += 1
- self._partition_delete_step(parted_disk, oldparts, newparts)
-
- # Second pass to resize old partitions that need to be resized
- self.notify_frontend("progress", (cur_progress / total_steps, "Resizing remaining partitions for " + device))
- cur_progress += 1
- self._partition_resize_step(parted_disk, device, oldparts, newparts)
-
- # Wiping disk and creating blank disklabel
- try:
- parted_disk = parted_dev.disk_new_fresh(parted.disk_type_get(newparts.get_disklabel()))
- parted_disk.commit()
- except:
- raise GLIException("DiskLabelCreationError", 'fatal', 'partition', "Could not create a blank disklabel!")
-
- # Third pass to create new partition table
- self.notify_frontend("progress", (cur_progress / total_steps, "Recreating partition table for " + device))
- cur_progress += 1
- self._partition_recreate_step(parted_disk, newparts)
- else:
- cur_progress += 3
-
- # Fourth pass to format partitions
- self.notify_frontend("progress", (cur_progress / total_steps, "Formatting partitions for " + device))
- cur_progress += 1
- self._partition_format_step(parted_disk, device, newparts)
-
- # All done for this device
- self.notify_frontend("progress", (cur_progress / total_steps, "Done with partitioning for " + device))
- cur_progress += 1
-
def _configure_grub(self):
self.build_mode = self._install_profile.get_kernel_build_method()
self._gather_grub_drive_info()