def maybeCreateLabel(self): """Set up an msdos label.""" self.partedDevice = parted.getDevice(self.device) try: self.partedDisk = parted.newDisk(self.partedDevice) if self.partedDisk.type == 'msdos': self.log.info("disk %s is already msdos", self.device) return 0 self.log.warn("disk %s has wrong label %s", self.device, self.partedDisk.type) except (DiskException, PartedException) as ex: self.log.error("cannot get partition table from %s: %s", self.device, str(ex)) except Exception: self.log.exception("cannot get partition table from %s", self.device) self.log.info("clobbering disk label on %s", self.device) self.partedDevice.clobber() self.log.info("creating msdos label on %s", self.device) self.partedDisk = parted.freshDisk(self.partedDevice, 'msdos') return 0
def _get_grubenv_entry(entry_name, device): try: # Try to mount the device's first partition disk = parted.newDisk(device) esp = disk.partitions[0] os.makedirs('/mnt/boot', exist_ok=True) subprocess.run(['mount', esp.path, '/mnt/boot'], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) # Try to read entry from grubenv grub_list = subprocess.run(['grub-editenv', GRUB_ENV, 'list'], check=True, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, universal_newlines=True) entry_match = re.search("^" + entry_name + "=(.+)$", grub_list.stdout, re.MULTILINE) if entry_match == None: return None else: return entry_match.group(1) except: return None finally: subprocess.run(['umount', '/mnt/boot'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
def getDevice(self, input): ''' A method which gets information about the specified device.''' # Grab the parted device device = parted.getDevice(input) # And return a disk instance return parted.newDisk(device), device
def __init__ (self, conf, testMode=False): self.conf = conf self.testMode = testMode self.resinBootPartPath = getBootPartition(conf) self.currentResinRootPartPath = getRootPartition(conf) self.device = parted.getDevice(getRootDevice(conf)) self.disk = parted.newDisk(self.device)
def get_partitions(diskpath): """ Return array of partition names for given disk """ dev = parted.getDevice(diskpath) disk = parted.newDisk(dev) partitions = [] for part in disk.partitions: (_, _, pname) = part.path.rsplit("/") partitions.append({"name": pname, "size": part.getLength() * dev.sectorSize}) return partitions
def testUSB(): devices = [dev.path for dev in parted.getAllDevices()] print(devices) usb = parted.getDevice('/dev/sdb') print(usb) disk = parted.newDisk(usb) partitionsDesc = [(part.type, part.number) for part in disk.partitions] print(disk.partitions[0]) # print(disk.partitions[1]) print(partitionsDesc) print(disk.partitions[0].fileSystem.type)
def __init__(self, devpath): # Supported commands and corresponding handlers try: self.device = parted.getDevice(devpath) except parted.IOException as e: raise RuntimeError(e.message) try: self.disk = parted.newDisk(self.device) if self.disk.type != 'msdos': raise RuntimeError('Only MBR partitions are supported') except parted.DiskException: self.create_empty()
def revertRepartition(device, partition, deltastart, deltaend, unit='MiB'): dev = parted.getDevice(device) disk = parted.newDisk(dev) targetPartition = disk.getPartitionByPath(device + partition) geometry = targetPartition.geometry geometry.start += parted.sizeToSectors(deltastart, unit, dev.sectorSize) geometry.end += parted.sizeToSectors(deltaend, unit, dev.sectorSize) disk.deletePartition(targetPartition) partition = parted.Partition(disk=disk, type=parted.PARTITION_NORMAL, geometry=geometry) disk.addPartition(partition=partition, constraint=dev.optimalAlignedConstraint) disk.commit()
def partition(diskname, specs, force=False, check_mode=False): """ Create requested partitions. Returns nr. of created partitions or 0 when the disk was already partitioned. """ count = 0 dev = parted.getDevice(diskname) try: disk = parted.newDisk(dev) except parted.DiskException: # unrecognizable format, treat as empty disk disk = None if disk and len(disk.partitions) > 0 and not force: print "skipping", diskname return 0 # create new partition table, wiping all existing data disk = parted.freshDisk(dev, 'gpt') # calculate nr. of partitions of each size assign_space(dev.getSize(), specs) last_megabyte = 1 for spec in specs: for _ in range(spec.count): # create the partition start = parted.sizeToSectors(last_megabyte, "MiB", dev.sectorSize) length = parted.sizeToSectors(spec.size, "MiB", dev.sectorSize) geo = parted.Geometry(device=dev, start=start, length=length) filesystem = parted.FileSystem(type='ext4', geometry=geo) part = parted.Partition(disk=disk, type=parted.PARTITION_NORMAL, fs=filesystem, geometry=geo) disk.addPartition(partition=part, constraint=dev.optimalAlignedConstraint) last_megabyte += spec.size count += 1 try: if not check_mode: disk.commit() except parted.IOException: # partitions have been written, but we have been unable to inform the # kernel of the change, probably because they are in use. # Ignore it and hope for the best... pass return count
def partition(diskname, specs, force=False, check_mode=False): """ Create requested partitions. Returns nr. of created partitions or 0 when the disk was already partitioned. """ count = 0 dev = parted.getDevice(diskname) try: disk = parted.newDisk(dev) except parted.DiskException: # unrecognizable format, treat as empty disk disk = None if disk and len(disk.partitions) > 0 and not force: print "skipping", diskname return 0 # create new partition table, wiping all existing data disk = parted.freshDisk(dev, 'gpt') # calculate nr. of partitions of each size assign_space(dev.getSize(), specs) last_megabyte = 1 for spec in specs: for _ in range(spec.count): # create the partition start = parted.sizeToSectors(last_megabyte, "MiB", dev.sectorSize) length = parted.sizeToSectors(spec.size, "MiB", dev.sectorSize) geo = parted.Geometry(device=dev, start=start, length=length) filesystem = parted.FileSystem(type='ext4', geometry=geo) part = parted.Partition( disk=disk, type=parted.PARTITION_NORMAL, fs=filesystem, geometry=geo) disk.addPartition(partition=part, constraint=dev.optimalAlignedConstraint) last_megabyte += spec.size count += 1 try: if not check_mode: disk.commit() except parted.IOException: # partitions have been written, but we have been unable to inform the # kernel of the change, probably because they are in use. # Ignore it and hope for the best... pass return count
def main(): opt = opciones() verifica(opt) #Inicializando dispositivo try: print '\nVerificando dispositivo............', sdb = parted.getDevice(opt.device) except Exception: print '[ERROR]' exit(1) print '[OK]' print '\nDispositivo seleccionado: ' + opt.device c = '' while c != 'y': c = raw_input( '\nAl dispositivo ' + opt.device + ' se le eliminara la tabla de particion actual, quieres continuar? [Y/n] ' ) c = c.lower() if c == 'n': print '\nSaliendo ...\n' exit(1) elif c == '' or c == 'y': c = 'y' sdb.clobber() print '' pass try: disk = parted.newDisk(sdb) except Exception: pass print 'Creando dispositivo msdos\n' disk = parted.freshDisk(sdb, 'msdos') print 'Tipos aceptados' for x in parted.fileSystemType.keys(): print x + ' ', print '' fin = 0 n = raw_input('\nCuantas particiones van a ser? (maximo 4)> ') print '' for x in range(int(n)): print 'Particion primaria ' + str(x + 1) + ':' t = raw_input('Tipo> ') s = long(raw_input('Tamaño en MiB> ')) print '' fin = particion_pri(sdb, disk, s, t, fin)
def capture(args): """Captures an image of args.src to args.dest, and shrinks the filesystem on the last partition""" # Get device info device = parted.getDevice(args.src) lastpart = parted.newDisk(device).partitions[-1] lastsector = lastpart.geometry.end + 1 sectorsize = device.sectorSize lastbyte = lastsector * sectorsize logging.debug("Total Size: %s", str(lastbyte)) if os.path.isfile(args.dest): if not yes_or_no("File: '%s' already exists. Overwrite?", args.dest): print("Operation aborted.") raise SystemExit if not args.no_shrink: lastbyte = shrinkfs(lastpart, args.free) if not args.no_copy: docopy(args.src, args.dest, lastbyte, args.buffer_size)
def getDevice(self): devices = parted.getAllDevices() self.chosen_device = getDevice(devices) try: self.chosen_device = int(self.chosen_device) - 1 if self.chosen_device < 0 or self.chosen_device > len(devices) - 1: self.valid = False self.code = 103 self.message = "Invalid number: out of range" return device = devices[self.chosen_device].path except ValueError: if not path.exists(self.chosen_device): self.valid = False self.code = 101 self.message = "Invalid file: file does not exist" return device = self.chosen_device self.device = parted.getDevice(device) self.disk = parted.newDisk(self.device) self.disk_file = open(self.device.path, "rb")
def extract_root_partition(image_filename, force=False): root_partition_image = '{}-root'.format(image_filename) if force or not os.path.isfile(root_partition_image): device = parted.getDevice(image_filename) sector_size = device.sectorSize disk = parted.newDisk(device) for partition in disk.partitions: filesystem = partition.fileSystem if filesystem.type == 'ext4': root_partition_start = partition.geometry.start * sector_size root_partition_size = partition.geometry.length * sector_size with open(image_filename, 'rb') as f: with open(root_partition_image, 'wb') as out: bytes_read = 0 f.seek(root_partition_start) chunk = f.read(1024) while bytes_read < root_partition_size: out.write(chunk) chunk = f.read(1024) bytes_read += 1024 return root_partition_image
def lookupPartitions(self, device_name): result = [] try: dev = parted.getDevice(device_name) disk = parted.newDisk(dev) for p in disk.partitions: if p.fileSystem: fs = {} fs['filename'] = device_name fs['number'] = p.number fs['name'] = p.name fs['type'] = p.fileSystem.type fs['startBlock'] = p.fileSystem.geometry.start fs['sectorsize'] = dev.sectorSize fs['startOffset'] = dev.sectorSize * p.fileSystem.geometry.start result.append(fs) except parted.DiskException as e: fs = {} fs['error'] = "DiskException" fs['desc'] = str(e) result.append(fs) #print('DiskException lookupPartitions exception: '+str(e)) except parted.IOException as e: fs = {} fs['error'] = "file not found" fs['desc'] = str(e) result.append(fs) except Exception as e: fs = {} fs['error'] = "error" fs['desc'] = str(e) result.append(fs) #print(e) #print('lookupPartitions exception: '+str(e)) return result
def __init__(self, devpath): # Supported commands and corresponding handlers self.commands = { 'a': self.toggle_bootable, 'd': self.delete_partition, 'm': self.print_menu, 'n': self.add_partition, 'o': self.create_empty, 'p': self.print_partitions, 'q': self.quit, 'w': self.write } try: self.device = parted.getDevice(devpath) except parted.IOException as e: raise RuntimeError(e.message) try: self.disk = parted.newDisk(self.device) if self.disk.type != 'msdos': raise RuntimeError('Only MBR partitions are supported') except parted.DiskException: self.create_empty()
def findGpt(self): self.blkidParts = BlkidParser(log=self.log.getChild("blkid")) deviceOrLabel = self.im.platformConf['grub']['device'] if deviceOrLabel.startswith('/dev'): tgtDevice, tgtLabel = deviceOrLabel, None else: tgtDevice, tgtLabel = None, deviceOrLabel # enumerate labeled partitions to try to identify # the boot device for part in self.blkidParts: dev, partno = part.splitDev() if tgtLabel is not None and tgtLabel == part.label: if not len(partno): self.log.error("cannot use whole disk") return 1 if self.device is None: self.device = dev else: self.log.error("found multiple devices: %s, %s", dev, self.device) return 1 elif tgtDevice is not None and tgtDevice == dev: if not len(partno): self.log.error("cannot use whole disk") return 1 if self.device is None: self.device = dev else: self.log.error("found multiple devices: %s, %s", dev, self.device) return 1 if self.device is None: self.log.error("cannot find an install device") return 1 if not self.isUEFI: code = self.assertUnmounted() if code: return code # optionally back up a config partition # if it's on the boot device for part in self.blkidParts: dev, partno = part.splitDev() if dev == self.device and part.label == 'ONL-CONFIG': self.backupConfig(part.device) self.partedDevice = parted.getDevice(self.device) self.partedDisk = parted.newDisk(self.partedDevice) # enumerate the partitions that will stay and go minpart = -1 for part in self.partedDisk.partitions: if part.getFlag(parted.PARTITION_HIDDEN): minpart = max(minpart, part.number+1) continue # else, the partition should exist blkidParts = [x for x in self.blkidParts if x.device == part.path] if not blkidParts: self.log.warn("cannot identify partition %s", part) continue blkidPart = blkidParts[0] if not blkidPart.isOnieReserved(): continue # else, check the GPT label for reserved-ness if (part.name and ('GRUB' in part.name or 'ONIE-BOOT' in part.name or 'DIAG' in part.name)): minpart = max(minpart, part.number+1) if minpart < 0: self.log.error("cannot find an install partition") return 1 self.minpart = minpart return 0
print the partitions in a device/file according to pyparted """ import parted # adjust as needed, eg /dev/sdc.. device = "sdcard.img" # A device is a "thing" with own properties (type, manufacturer etc) - its a # high level abstraction that lets you just identify you have the right device device = parted.getDevice(device) # grab a "disk" instance - the other choice is `freshDisk`, wheras `newDisk` # seems to just read an existing disk (which is what we want for adding new # partition) disk = parted.newDisk(device) print("***** sanity check *****") print(f"result: {disk.check()}") print("===== device ====") print(f" model: {device.model}") print(f" path: {device.path}") print(f" sectorSize: {device.sectorSize}") print(f" physicalSectorSize: {device.physicalSectorSize}") print(f" length: {device.length}") # this next line can crash if you have a wonky GPT... # print(f" size (MB): {device.getSize()}") print("===== disk ====") print(f" type: {disk.type}")
def findGpt(self): self.blkidParts = BlkidParser(log=self.log.getChild("blkid")) deviceOrLabel = self.im.platformConf['grub']['device'] if deviceOrLabel.startswith('/dev'): tgtDevice, tgtLabel = deviceOrLabel, None else: tgtDevice, tgtLabel = None, deviceOrLabel # enumerate labeled partitions to try to identify # the boot device for part in self.blkidParts: dev, partno = part.splitDev() if tgtLabel is not None and tgtLabel == part.label: if not len(partno): self.log.error("cannot use whole disk") return 1 if self.device is None: self.device = dev else: self.log.error("found multiple devices: %s, %s", dev, self.device) return 1 elif tgtDevice is not None and tgtDevice == dev: if not len(partno): self.log.error("cannot use whole disk") return 1 if self.device is None: self.device = dev else: self.log.error("found multiple devices: %s, %s", dev, self.device) return 1 if self.device is None: self.log.error("cannot find an install device") return 1 code = self.assertUnmounted() if code: return code # optionally back up a config partition # if it's on the boot device for part in self.blkidParts: dev, partno = part.splitDev() if dev == self.device and part.label == 'ONL-CONFIG': self.backupConfig(part.device) self.partedDevice = parted.getDevice(self.device) self.partedDisk = parted.newDisk(self.partedDevice) # enumerate the partitions that will stay and go minpart = -1 for part in self.partedDisk.partitions: if part.getFlag(parted.PARTITION_HIDDEN): minpart = max(minpart, part.number+1) continue # else, the partition should exist blkidParts = [x for x in self.blkidParts if x.device == part.path] if not blkidParts: self.log.warn("cannot identify partition %s", part) continue blkidPart = blkidParts[0] if not blkidPart.isOnieReserved(): continue # else, check the GPT label for reserved-ness if (part.name and ('GRUB' in part.name or 'ONIE-BOOT' in part.name or 'DIAG' in part.name)): minpart = max(minpart, part.number+1) if minpart < 0: self.log.error("cannot find an install partition") return 1 self.minpart = minpart return 0
def enter(self): if not self.should_enter(): return self.context.logger.log("Entering split_root_partition state") device = parted.getDevice('/dev/sda') disk = parted.newDisk(device) original_root_fs_size = self._get_root_fs_size_in(device.sectorSize) self.context.logger.log( "Original root filesystem size (sectors): {0}".format( original_root_fs_size)) desired_boot_partition_size = parted.sizeToSectors( 256, 'MiB', device.sectorSize) self.context.logger.log( "Desired boot partition size (sectors): {0}".format( desired_boot_partition_size)) desired_root_fs_size = original_root_fs_size - desired_boot_partition_size self.context.logger.log( "Desired root filesystem size (sectors): {0}".format( desired_root_fs_size)) self.command_executor.Execute( "resize2fs /dev/sda1 {0}s".format(desired_root_fs_size), True) resized_root_fs_size = self._get_root_fs_size_in(device.sectorSize) self.context.logger.log( "Resized root filesystem size (sectors): {0}".format( resized_root_fs_size)) if not desired_root_fs_size == resized_root_fs_size: raise Exception( "resize2fs failed, desired: {0}, resized: {1}".format( desired_root_fs_size, resized_root_fs_size)) self.context.logger.log("Root filesystem resized successfully") root_partition = disk.partitions[0] original_root_partition_start = root_partition.geometry.start original_root_partition_end = root_partition.geometry.end self.context.logger.log( "Original root partition start (sectors): {0}".format( original_root_partition_start)) self.context.logger.log( "Original root partition end (sectors): {0}".format( original_root_partition_end)) desired_root_partition_start = original_root_partition_start desired_root_partition_end = original_root_partition_end - desired_boot_partition_size desired_root_partition_size = desired_root_partition_end - desired_root_partition_start self.context.logger.log( "Desired root partition start (sectors): {0}".format( desired_root_partition_start)) self.context.logger.log( "Desired root partition end (sectors): {0}".format( desired_root_partition_end)) self.context.logger.log( "Desired root partition size (sectors): {0}".format( desired_root_partition_size)) desired_root_partition_geometry = parted.Geometry( device=device, start=desired_root_partition_start, length=desired_root_partition_size) root_partition_constraint = parted.Constraint( exactGeom=desired_root_partition_geometry) disk.setPartitionGeometry(partition=root_partition, constraint=root_partition_constraint, start=desired_root_partition_start, end=desired_root_partition_end) desired_boot_partition_start = disk.getFreeSpaceRegions()[1].start desired_boot_partition_end = disk.getFreeSpaceRegions()[1].end desired_boot_partition_size = disk.getFreeSpaceRegions()[1].length self.context.logger.log( "Desired boot partition start (sectors): {0}".format( desired_boot_partition_start)) self.context.logger.log( "Desired boot partition end (sectors): {0}".format( desired_boot_partition_end)) desired_boot_partition_geometry = parted.Geometry( device=device, start=desired_boot_partition_start, length=desired_boot_partition_size) boot_partition_constraint = parted.Constraint( exactGeom=desired_boot_partition_geometry) desired_boot_partition = parted.Partition( disk=disk, type=parted.PARTITION_NORMAL, geometry=desired_boot_partition_geometry) disk.addPartition(partition=desired_boot_partition, constraint=boot_partition_constraint) disk.commit() probed_root_fs = parted.probeFileSystem(disk.partitions[0].geometry) if not probed_root_fs == 'ext4': raise Exception("Probed root fs is not ext4") disk.partitions[1].setFlag(parted.PARTITION_BOOT) disk.commit() self.command_executor.Execute("partprobe", True) self.command_executor.Execute("mkfs.ext2 /dev/sda2", True) boot_partition_uuid = self._get_uuid("/dev/sda2") # Move stuff from /oldroot/boot to new partition, make new partition mountable at the same spot self.command_executor.Execute("mount /dev/sda1 /oldroot", True) self.command_executor.Execute("mkdir /oldroot/memroot", True) self.command_executor.Execute("mount --make-rprivate /", True) self.command_executor.Execute("pivot_root /oldroot /oldroot/memroot", True) self.command_executor.ExecuteInBash( "for i in dev proc sys; do mount --move /memroot/$i /$i; done", True) self.command_executor.Execute("mv /boot /boot.backup", True) self.command_executor.Execute("mkdir /boot", True) self._append_boot_partition_uuid_to_fstab(boot_partition_uuid) self.command_executor.Execute("cp /etc/fstab /memroot/etc/fstab", True) self.command_executor.Execute("mount /boot", True) self.command_executor.ExecuteInBash("mv /boot.backup/* /boot/", True) self.command_executor.Execute("rmdir /boot.backup", True) self.command_executor.Execute("mount --make-rprivate /", True) self.command_executor.Execute("pivot_root /memroot /memroot/oldroot", True) self.command_executor.Execute("rmdir /oldroot/memroot", True) self.command_executor.ExecuteInBash( "for i in dev proc sys; do mount --move /oldroot/$i /$i; done", True) self.command_executor.Execute("systemctl restart rsyslog", True) self.command_executor.Execute("systemctl restart systemd-udevd", True) self.command_executor.Execute("systemctl restart walinuxagent", True) self.command_executor.Execute("umount /oldroot/boot", True) self.command_executor.Execute("umount /oldroot", True)
def get_part_layout(udev_ctx): """ Loop through the disk and use parted and udev to capture the partition layout. Might need to add threading to the for loop in the future for servers with a ton of disk. :return: """ from glob import glob from re import search # Define dict to store disk info. disks_dict = dict() def update(name, p_dev): if name: disks_dict.update({name: disk}) else: disks_dict.update({d.device_node: disk}) p_dev.removeFromCache() # Loop through disk pulled from udev. for d in udev_ctx.list_devices(subsystem='block', DEVTYPE='disk'): dm_name = None # Skip if the device is a /dev/loop, mdraid, cd, or usb. if (not search("/dev/loop", d.device_node) and not d.get('MD_NAME', False) and not search("cd", d.get('ID_TYPE', "")) and not search("usb", d.get('ID_BUS', "")) and not int(d.get('DM_MULTIPATH_DEVICE_PATH', False))): # If it's a dm device check if it's mpath if not skip it, # if it is set the dm_name. if search("dm-", d.device_node): if search("^mpath-", d.get('DM_UUID', "")): dm_name = f"/dev/mapper/{d.get('DM_NAME')}" else: continue # If the device is an mpath path, then skip it, would prefer # to query udev here, but apparently in the recovery # environment the DM_MULTIPATH_DEVICE_PATH variable is always # a zero for some reason. So I don't trust using it, so check # if the dev has any holders, and if they're an mpath device. holders = glob( f"/sys/block/{d.device_node.split('/')[-1]}/holders/*/dm/uuid") if holders: with open(holders[0]) as f: if f.readline().split()[0].startswith("mpath-"): continue # Define a dict to store each disk info. disk = dict() # Fetch the parted device. p_device = parted.getDevice(d.device_node) # Add parted info, and udev info to the dict. disk.update({ "id_serial": d.get('ID_SERIAL_SHORT'), "id_wwn": d.get('ID_WWN'), "id_path": d.get('ID_PATH'), "size": p_device.length }) # Add a catch for disk that don't have a label and skip them. try: # Fetch the parted disk object. p_disk = parted.newDisk(p_device) except (DeviceException, DiskException): disk.update({ "fs_type": d.get('ID_FS_TYPE'), "fs_uuid": d.get('ID_FS_UUID', '') }) update(dm_name, p_device) continue # Add parted info, and udev info to the dict. disk.update({"type": p_disk.type}) if p_disk.type == "loop": disk.update({ "fs_type": d.get('ID_FS_TYPE'), "fs_uuid": d.get('ID_FS_UUID', '') }) update(dm_name, p_device) continue # Loop through the partitions, and grab info. for p in p_disk.partitions: # Define dict to store partition info. part = dict() # Grab any part flags, and the part type. part.update({"flags": p.getFlagsAsString(), "type": p.type}) # If the disk label isn't msdos, check for part names. if "msdos" not in p_disk.type: try: if p.name: part.update({"name": p.name}) else: part.update({"name": None}) except PartitionException: part.update({"name": None}) pass else: part.update({"name": None}) # Pull the fs type from udev instead of parted. if dm_name and dm_name[-1].isnumeric(): dev = f"{dm_name}p{p.number}" elif dm_name: dev = f"{dm_name}{p.number}" elif d.device_node[-1].isnumeric(): dev = f"{d.device_node}p{p.number}" else: dev = f"{d.device_node}{p.number}" if is_block(dev): part_info = dev_from_file(udev_ctx, dev) # Add the fs info, and the geometry info. part.update({ "fs_type": part_info.get('ID_FS_TYPE', ''), "fs_uuid": part_info.get('ID_FS_UUID', ''), "fs_label": part_info.get('ID_FS_LABEL', ''), "start": p.geometry.start, "end": p.geometry.end }) # Add the part dict as an entry to the disk dict. # Might change this to the full path later, for # now just the part number. disk.update({p.number: part}) # Add the disk dict as an entry to the master dict. update(dm_name, p_device) return disks_dict
def enter(self): if not self.should_enter(): return self.context.logger.log("Entering split_root_partition state") device = parted.getDevice(self.rootfs_disk) disk = parted.newDisk(device) original_root_fs_size = self._get_root_fs_size_in(device.sectorSize) self.context.logger.log("Original root filesystem size (sectors): {0}".format(original_root_fs_size)) desired_boot_partition_size = parted.sizeToSectors(256, 'MiB', device.sectorSize) self.context.logger.log("Desired boot partition size (sectors): {0}".format(desired_boot_partition_size)) desired_root_fs_size = int(original_root_fs_size - desired_boot_partition_size) self.context.logger.log("Desired root filesystem size (sectors): {0}".format(desired_root_fs_size)) attempt = 1 while attempt < 10: resize_result = self.command_executor.Execute("resize2fs {0} {1}s".format(self.rootfs_block_device, desired_root_fs_size)) if resize_result == 0: break else: self.command_executor.Execute('systemctl restart systemd-udevd') self.command_executor.Execute('systemctl restart systemd-timesyncd') self.command_executor.Execute('udevadm trigger') sleep(10) attempt += 1 resized_root_fs_size = self._get_root_fs_size_in(device.sectorSize) self.context.logger.log("Resized root filesystem size (sectors): {0}".format(resized_root_fs_size)) if not desired_root_fs_size == resized_root_fs_size: raise Exception("resize2fs failed, desired: {0}, resized: {1}".format(desired_root_fs_size, resized_root_fs_size)) self.context.logger.log("Root filesystem resized successfully") root_partition = disk.getPartitionByPath(os.path.realpath(self.rootfs_block_device)) original_root_partition_start = root_partition.geometry.start original_root_partition_end = root_partition.geometry.end self.context.logger.log("Original root partition start (sectors): {0}".format(original_root_partition_start)) self.context.logger.log("Original root partition end (sectors): {0}".format(original_root_partition_end)) desired_root_partition_start = original_root_partition_start desired_root_partition_end = original_root_partition_end - desired_boot_partition_size desired_root_partition_size = desired_root_partition_end - desired_root_partition_start self.context.logger.log("Desired root partition start (sectors): {0}".format(desired_root_partition_start)) self.context.logger.log("Desired root partition end (sectors): {0}".format(desired_root_partition_end)) self.context.logger.log("Desired root partition size (sectors): {0}".format(desired_root_partition_size)) desired_root_partition_geometry = parted.Geometry(device=device, start=desired_root_partition_start, length=desired_root_partition_size) root_partition_constraint = parted.Constraint(exactGeom=desired_root_partition_geometry) disk.setPartitionGeometry(partition=root_partition, constraint=root_partition_constraint, start=desired_root_partition_start, end=desired_root_partition_end) desired_boot_partition_start = disk.getFreeSpaceRegions()[1].start desired_boot_partition_end = disk.getFreeSpaceRegions()[1].end desired_boot_partition_size = disk.getFreeSpaceRegions()[1].length self.context.logger.log("Desired boot partition start (sectors): {0}".format(desired_boot_partition_start)) self.context.logger.log("Desired boot partition end (sectors): {0}".format(desired_boot_partition_end)) desired_boot_partition_geometry = parted.Geometry(device=device, start=desired_boot_partition_start, length=desired_boot_partition_size) boot_partition_constraint = parted.Constraint(exactGeom=desired_boot_partition_geometry) desired_boot_partition = parted.Partition(disk=disk, type=parted.PARTITION_NORMAL, geometry=desired_boot_partition_geometry) if (root_partition.getFlag(parted.PARTITION_BOOT)): desired_boot_partition.setFlag(parted.PARTITION_BOOT) disk.addPartition(partition=desired_boot_partition, constraint=boot_partition_constraint) disk.commit() probed_root_fs = parted.probeFileSystem(desired_root_partition_geometry) if not probed_root_fs == 'ext4': raise Exception("Probed root fs is not ext4") self.command_executor.Execute("partprobe", True) self.command_executor.Execute("mkfs.ext2 {0}".format(self.bootfs_block_device), True) boot_partition_uuid = self._get_uuid(self.bootfs_block_device) # Move stuff from /oldroot/boot to new partition, make new partition mountable at the same spot self.command_executor.Execute("mount {0} /oldroot".format(self.rootfs_block_device), True) self.command_executor.Execute("mkdir -p /boot", True) self.command_executor.Execute("cp /oldroot/etc/fstab /etc/fstab", True) self._append_boot_partition_uuid_to_fstab(boot_partition_uuid) self.command_executor.Execute("cp /etc/fstab /oldroot/etc/fstab", True) self.command_executor.Execute("mount /boot", True) self.command_executor.ExecuteInBash("mv /oldroot/boot/* /boot/", True) self.command_executor.Execute("umount /boot", True) self.command_executor.Execute("umount /oldroot", True)
def partition(self, dev: Dict[str, Any]): """ Update partitioning on the SD card """ try: import parted except ModuleNotFoundError: raise Fail("please install python3-parted") # See https://github.com/dcantrell/pyparted/tree/master/examples # for pyparted examples # See https://www.gnu.org/software/parted/api/modules.html # for library documentation # See http://www.linuxvoice.com/issues/005/pyparted.pdf # for more exmaples and in-depth explanations device = parted.getDevice(dev["path"]) disk = parted.newDisk(device) if not disk.check(): raise Fail("Parted disk check failed (TODO: find out how to get details about what check failed)") partitions = list(disk.partitions) if len(partitions) > 3: raise Fail(f"SD card has too many ({len(partitions)}) partitions: reset it with --write-image") part_boot = partitions[0] fs = part_boot.fileSystem if not fs: raise Fail("SD boot partition has no file system: reset it with --write-image") if fs.type != "fat32": raise Fail("SD boot partition is not a fat32 partition: reset it with --write-image") part_root = partitions[1] fs = part_root.fileSystem if not fs: raise Fail("SD system partition has no file system: reset it with --write-image") if fs.type != "ext4": raise Fail("SD system partition is not an ext4 partition: reset it with --write-image") if len(partitions) == 3: part_media = partitions[2] else: part_media = None # TODO: check partition label, and error out if it exists and is not 'media' target_root_size = int(round(4 * 1024**3 / device.sectorSize)) need_root_resize = part_root.geometry.end - part_root.geometry.start < target_root_size - 16 if need_root_resize: log.info("%s: partition is only %.1fGB and needs resizing (current: %d, target: %d)", part_root.path, target_root_size * device.sectorSize / 1024**3, part_root.geometry.end - part_root.geometry.start, target_root_size) if part_media: log.info("%s: partition needs resize: removing media partition %s", part_root.path, part_media.path) disk.deletePartition(part_media) part_media = None # Resize rootfs partition constraint = device.optimalAlignedConstraint constraint.minSize = target_root_size constraint.maxSize = target_root_size disk.maximizePartition(part_root, constraint) disk.commit() time.sleep(0.5) self.umount(self.locate()) time.sleep(0.3) run(["e2fsck", "-fy", part_root.path]) run(["resize2fs", part_root.path]) if part_media is None: # Get the last free space free_space = disk.getFreeSpaceRegions()[-1] # Create media partition partition = parted.Partition( disk=disk, type=parted.PARTITION_NORMAL, geometry=free_space) disk.addPartition(partition=partition, constraint=device.optimalAlignedConstraint) disk.commit() time.sleep(0.5) self.umount(self.locate()) time.sleep(0.3) log.info("%s media partition created", format_gb(free_space.length * device.sectorSize)) # Create exFAT file system run(["mkexfatfs", "-n", "media", partition.path]) log.info("%s media partition formatted", format_gb(free_space.length * device.sectorSize)) else: # Current parted cannot seem to deal with exfat, let's use exfatfsck instead res = run(["exfatfsck", "-n", partitions[2].path], capture_output=True) if res.returncode != 0: raise Fail("SD media partition exFAT file system failed checks:" " reset it with --write-image and rerun --partition")
def enter(self): if not self.should_enter(): return self.context.logger.log("Entering split_root_partition state") device = parted.getDevice('/dev/sda') disk = parted.newDisk(device) original_root_fs_size = self._get_root_fs_size_in(device.sectorSize) self.context.logger.log("Original root filesystem size (sectors): {0}".format(original_root_fs_size)) desired_boot_partition_size = parted.sizeToSectors(256, 'MiB', device.sectorSize) self.context.logger.log("Desired boot partition size (sectors): {0}".format(desired_boot_partition_size)) desired_root_fs_size = original_root_fs_size - desired_boot_partition_size self.context.logger.log("Desired root filesystem size (sectors): {0}".format(desired_root_fs_size)) self.command_executor.Execute("resize2fs /dev/sda1 {0}s".format(desired_root_fs_size), True) resized_root_fs_size = self._get_root_fs_size_in(device.sectorSize) self.context.logger.log("Resized root filesystem size (sectors): {0}".format(resized_root_fs_size)) if not desired_root_fs_size == resized_root_fs_size: raise Exception("resize2fs failed, desired: {0}, resized: {1}".format(desired_root_fs_size, resized_root_fs_size)) self.context.logger.log("Root filesystem resized successfully") root_partition = disk.partitions[0] original_root_partition_start = root_partition.geometry.start original_root_partition_end = root_partition.geometry.end self.context.logger.log("Original root partition start (sectors): {0}".format(original_root_partition_start)) self.context.logger.log("Original root partition end (sectors): {0}".format(original_root_partition_end)) desired_root_partition_start = original_root_partition_start desired_root_partition_end = original_root_partition_end - desired_boot_partition_size desired_root_partition_size = desired_root_partition_end - desired_root_partition_start self.context.logger.log("Desired root partition start (sectors): {0}".format(desired_root_partition_start)) self.context.logger.log("Desired root partition end (sectors): {0}".format(desired_root_partition_end)) self.context.logger.log("Desired root partition size (sectors): {0}".format(desired_root_partition_size)) desired_root_partition_geometry = parted.Geometry(device=device, start=desired_root_partition_start, length=desired_root_partition_size) root_partition_constraint = parted.Constraint(exactGeom=desired_root_partition_geometry) disk.setPartitionGeometry(partition=root_partition, constraint=root_partition_constraint, start=desired_root_partition_start, end=desired_root_partition_end) desired_boot_partition_start = disk.getFreeSpaceRegions()[1].start desired_boot_partition_end = disk.getFreeSpaceRegions()[1].end desired_boot_partition_size = disk.getFreeSpaceRegions()[1].length self.context.logger.log("Desired boot partition start (sectors): {0}".format(desired_boot_partition_start)) self.context.logger.log("Desired boot partition end (sectors): {0}".format(desired_boot_partition_end)) desired_boot_partition_geometry = parted.Geometry(device=device, start=desired_boot_partition_start, length=desired_boot_partition_size) boot_partition_constraint = parted.Constraint(exactGeom=desired_boot_partition_geometry) desired_boot_partition = parted.Partition(disk=disk, type=parted.PARTITION_NORMAL, geometry=desired_boot_partition_geometry) disk.addPartition(partition=desired_boot_partition, constraint=boot_partition_constraint) disk.commit() probed_root_fs = parted.probeFileSystem(disk.partitions[0].geometry) if not probed_root_fs == 'ext4': raise Exception("Probed root fs is not ext4") disk.partitions[1].setFlag(parted.PARTITION_BOOT) disk.commit() self.command_executor.Execute("partprobe", True) self.command_executor.Execute("mkfs.ext2 /dev/sda2", True) boot_partition_uuid = self._get_uuid("/dev/sda2") # Move stuff from /oldroot/boot to new partition, make new partition mountable at the same spot self.command_executor.Execute("mount /dev/sda1 /oldroot", True) self.command_executor.Execute("mkdir /oldroot/memroot", True) self.command_executor.Execute("mount --make-rprivate /", True) self.command_executor.Execute("pivot_root /oldroot /oldroot/memroot", True) self.command_executor.ExecuteInBash("for i in dev proc sys; do mount --move /memroot/$i /$i; done", True) self.command_executor.Execute("mv /boot /boot.backup", True) self.command_executor.Execute("mkdir /boot", True) self._append_boot_partition_uuid_to_fstab(boot_partition_uuid) self.command_executor.Execute("cp /etc/fstab /memroot/etc/fstab", True) self.command_executor.Execute("mount /boot", True) self.command_executor.ExecuteInBash("mv /boot.backup/* /boot/", True) self.command_executor.Execute("rmdir /boot.backup", True) self.command_executor.Execute("mount --make-rprivate /", True) self.command_executor.Execute("pivot_root /memroot /memroot/oldroot", True) self.command_executor.Execute("rmdir /oldroot/memroot", True) self.command_executor.ExecuteInBash("for i in dev proc sys; do mount --move /oldroot/$i /$i; done", True) self.command_executor.Execute("systemctl restart rsyslog", True) self.command_executor.Execute("systemctl restart systemd-udevd", True) self.command_executor.Execute("systemctl restart walinuxagent", True) self.command_executor.Execute("umount /oldroot/boot", True) self.command_executor.Execute("umount /oldroot", True)