diff options
author | Andrew Gaffney <agaffney@gentoo.org> | 2006-09-01 23:04:18 +0000 |
---|---|---|
committer | Andrew Gaffney <agaffney@gentoo.org> | 2006-09-01 23:04:18 +0000 |
commit | 1555f942f8908b5c6e74e434733f3876b84b093c (patch) | |
tree | 1e0cda4e74bfabd07f32d4bf14c0844999c949c0 | |
parent | Added some more applications to the Extra Packages list. (diff) | |
download | gli-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-- | ChangeLog | 4 | ||||
-rw-r--r-- | src/GLIArchitectureTemplate.py | 420 | ||||
-rw-r--r-- | src/templates/x86ArchitectureTemplate.py | 419 |
3 files changed, 423 insertions, 420 deletions
@@ -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() |