def findXenSourceProducts(): """Scans the host and finds XenSource product installations. Returns list of ExistingInstallation objects. Currently requires supervisor privileges due to mounting filesystems.""" installs = [] for disk in diskutil.getQualifiedDiskList(): (boot, root, state, storage, logs) = diskutil.probeDisk(disk) inst = None try: if root[0] == diskutil.INSTALL_RETAIL: inst = ExistingRetailInstallation(disk, boot[1], root[1], state[1], storage) except Exception, e: xelogging.log( "A problem occurred whilst scanning for existing installations:" ) xelogging.log_exception(e) xelogging.log("This is not fatal. Continuing anyway.") if inst: xelogging.log("Found an installation: %s on %s" % (str(inst), disk)) installs.append(inst)
def disk_more_info(context): if not context: return True usage = 'unknown' (boot, root, state, storage, logs) = diskutil.probeDisk(context) if root[0]: usage = "%s installation" % MY_PRODUCT_BRAND elif storage[0]: usage = 'VM storage' else: # Determine disk is being used as an LVM SR with no partitioning rv, out = util.runCmd2([ 'pvs', context, '-o', 'vg_name', '--noheadings' ], with_stdout=True) if rv == 0: vg_name = out.strip() if vg_name.startswith('VG_XenStorage-'): usage = 'VM Storage' tui.update_help_line([' ', ' ']) snackutil.TableDialog(tui.screen, "Details", ("Disk:", diskutil.getHumanDiskName(context)), ("Vendor:", diskutil.getDiskDeviceVendor(context)), ("Model:", diskutil.getDiskDeviceModel(context)), ("Serial:", diskutil.getDiskSerialNumber(context)), ("Size:", diskutil.getHumanDiskSize(diskutil.getDiskDeviceSize(context))), ("Current usage:", usage)) tui.screen.popHelpLine() return True
def disk_more_info(context): if not context: return True usage = 'unknown' (boot, state, storage) = diskutil.probeDisk(context) if boot[0]: usage = "%s installation" % MY_PRODUCT_BRAND elif storage[0]: usage = 'VM storage' else: # Determine disk is being used as an LVM SR with no partitioning rv, out = util.runCmd2([ 'pvs', context, '-o', 'vg_name', '--noheadings' ], with_stdout=True) if rv == 0: vg_name = out.strip() if vg_name.startswith('VG_XenStorage-'): usage = 'VM Storage' tui.update_help_line([' ', ' ']) snackutil.TableDialog(tui.screen, "Details", ("Disk:", diskutil.getHumanDiskName(context)), ("Vendor:", diskutil.getDiskDeviceVendor(context)), ("Model:", diskutil.getDiskDeviceModel(context)), ("Serial:", diskutil.getDiskSerialNumber(context)), ("Size:", diskutil.getHumanDiskSize(diskutil.getDiskDeviceSize(context))), ("Current usage:", usage)) tui.screen.popHelpLine() return True
def restoreFromBackup(backup, progress=lambda x: ()): """ Restore files from backup_partition to the root partition on disk. Call progress with a value between 0 and 100. Re-install bootloader. Fails if backup is not same version as the CD in use.""" disk = backup.root_disk tool = PartitionTool(disk) _, boot_partnum, primary_partnum, backup_partnum, logs_partnum, swap_partnum, _ = backend.inspectTargetDisk( disk, None, [], constants.PRESERVE_IF_UTILITY, True, True) backup_version = backup.version limit_version = product.THIS_PLATFORM_VERSION logs_partition = tool.getPartition(logs_partnum) boot_partition = tool.getPartition(boot_partnum) root_partition = partitionDevice(disk, primary_partnum) backup_fs = util.TempMount(backup.partition, 'backup-', options=['ro']) inventory = util.readKeyValueFile(os.path.join(backup_fs.mount_point, constants.INVENTORY_FILE), strip_quotes=True) backup_partition_layout = [] if 'PARTITION_LAYOUT' in inventory: # Present from XS 7.0 backup_partition_layout = inventory['PARTITION_LAYOUT'].split(',') backup_fs.unmount() (boot, _, _, _, logs) = diskutil.probeDisk(disk) xelogging.log("BACKUP DISK PARTITION LAYOUT: %s" % backup_partition_layout) if not logs[0] and boot[ 0] and not backup_partition_layout: # From 7.x (no new layout - yes Boot partition) to 6.x restoreWithoutRepartButUEFI(backup, progress) else: doRestore(backup, progress, backup_partition_layout, logs[0])
def get_local_disk(answers): diskEntries = diskutil.getQualifiedDiskList() entries = [] target_is_sr = {} for de in diskEntries: (vendor, model, size) = diskutil.getExtendedDiskInfo(de) # determine current usage target_is_sr[de] = False (boot, root, state, storage, logs) = diskutil.probeDisk(de) if storage[0]: target_is_sr[de] = True (vendor, model, size) = diskutil.getExtendedDiskInfo(de) stringEntry = "%s - %s [%s %s]" % (diskutil.getHumanDiskName(de), diskutil.getHumanDiskSize(size), vendor, model) e = (stringEntry, de) entries.append(e) # default value: default = None if 'dest-disk' in answers: default = selectDefault(answers['dest-disk'], entries) tui.update_help_line([None, "<F5> more info"]) scroll, height = snackutil.scrollHeight(4, len(entries)) (button, entry) = snackutil.ListboxChoiceWindowEx( tui.screen, "Select Device", "Please select the device to store the report on.", entries, ['Ok', 'Back'], 55, scroll, height, default, help='getlocaldisk:info', hotkeys={'F5': disk_more_info}) tui.screen.popHelpLine() if button == 'back': return uicontroller.LEFT_BACKWARDS # entry contains the 'de' part of the tuple passed in answers['dest-disk'] = entry return uicontroller.RIGHT_FORWARDS
def disk_more_info(context): if not context: return True usage = 'unknown' (boot, state, storage) = diskutil.probeDisk(context) if boot[0]: usage = "%s installation" % (PRODUCT_BRAND or PLATFORM_NAME) elif storage[0]: usage = 'VM storage' tui.update_help_line([' ', ' ']) snackutil.TableDialog(tui.screen, "Details", ("Disk:", diskutil.getHumanDiskName(context)), ("Vendor:", diskutil.getDiskDeviceVendor(context)), ("Model:", diskutil.getDiskDeviceModel(context)), ("Size:", diskutil.getHumanDiskSize(diskutil.getDiskDeviceSize(context))), ("Current usage:", usage)) tui.screen.popHelpLine() return True
def disk_more_info(context): if not context: return True usage = 'unknown' (boot, root, state, storage, logs) = diskutil.probeDisk(context) if root[0]: usage = "%s installation" % (PRODUCT_BRAND or PLATFORM_NAME) elif storage[0]: usage = 'VM storage' tui.update_help_line([' ', ' ']) snackutil.TableDialog( tui.screen, "Details", ("Disk:", diskutil.getHumanDiskName(context)), ("Vendor:", diskutil.getDiskDeviceVendor(context)), ("Model:", diskutil.getDiskDeviceModel(context)), ("Size:", diskutil.getHumanDiskSize( diskutil.getDiskDeviceSize(context))), ("Current usage:", usage)) tui.screen.popHelpLine() return True
def get_local_disk(answers): diskEntries = diskutil.getQualifiedDiskList() entries = [] target_is_sr = {} for de in diskEntries: (vendor, model, size) = diskutil.getExtendedDiskInfo(de) # determine current usage target_is_sr[de] = False (boot, state, storage) = diskutil.probeDisk(de) if storage[0]: target_is_sr[de] = True (vendor, model, size) = diskutil.getExtendedDiskInfo(de) stringEntry = "%s - %s [%s %s]" % (diskutil.getHumanDiskName(de), diskutil.getHumanDiskSize(size), vendor, model) e = (stringEntry, de) entries.append(e) # default value: default = None if 'dest-disk' in answers: default = selectDefault(answers['dest-disk'], entries) tui.update_help_line([None, "<F5> more info"]) scroll, height = snackutil.scrollHeight(4, len(entries)) (button, entry) = snackutil.ListboxChoiceWindowEx( tui.screen, "Select Device", "Please select the device to store the report on.", entries, ['Ok', 'Back'], 55, scroll, height, default, help = 'getlocaldisk:info', hotkey = 'F5', hotkey_cb = disk_more_info) tui.screen.popHelpLine() if button == 'back': return uicontroller.LEFT_BACKWARDS # entry contains the 'de' part of the tuple passed in answers['dest-disk'] = entry return uicontroller.RIGHT_FORWARDS
def findXenSourceProducts(): """Scans the host and finds XenSource product installations. Returns list of ExistingInstallation objects. Currently requires supervisor privileges due to mounting filesystems.""" installs = [] for disk in diskutil.getQualifiedDiskList(): (boot, state, storage) = diskutil.probeDisk(disk) inst = None try: if boot[0] == diskutil.INSTALL_RETAIL: inst = ExistingRetailInstallation(disk, boot[1], state[1], storage) except Exception, e: xelogging.log("A problem occurred whilst scanning for existing installations:") xelogging.log_exception(e) xelogging.log("This is not fatal. Continuing anyway.") if inst: xelogging.log("Found an installation: %s" % str(inst)) installs.append(inst)
def select_primary_disk(answers): button = None diskEntries = filter_out_raid_member(sorted_disk_list()) entries = [] target_is_sr = {} if answers['create-new-partitions']: min_primary_disk_size = constants.min_primary_disk_size else: min_primary_disk_size = constants.min_primary_disk_size_old for de in diskEntries: (vendor, model, size) = diskutil.getExtendedDiskInfo(de) if min_primary_disk_size <= diskutil.blockSizeToGBSize(size): # determine current usage target_is_sr[de] = False (boot, root, state, storage, logs) = diskutil.probeDisk(de) if storage[0]: target_is_sr[de] = True (vendor, model, size) = diskutil.getExtendedDiskInfo(de) stringEntry = "%s - %s [%s %s]" % (diskutil.getHumanDiskName(de), diskutil.getHumanDiskSize(size), vendor, model) e = (stringEntry, de) entries.append(e) # we should have at least one disk if len(entries) == 0: ButtonChoiceWindow(tui.screen, "No Primary Disk", "No disk with sufficient space to install %s on was found." % MY_PRODUCT_BRAND, ['Cancel']) return EXIT # if only one disk, set default: if len(entries) == 1: answers['primary-disk'] = entries[0][1] else: # default value: default = None if answers.has_key('primary-disk'): default = selectDefault(answers['primary-disk'], entries) tui.update_help_line([None, "<F5> more info"]) scroll, height = snackutil.scrollHeight(4, len(entries)) (button, entry) = snackutil.ListboxChoiceWindowEx( tui.screen, "Select Primary Disk", """Please select the disk you would like to install %s on (disks with insufficient space are not shown). You may need to change your system settings to boot from this disk.""" % (MY_PRODUCT_BRAND), entries, ['Ok', 'Software RAID', 'Back'], 55, scroll, height, default, help = 'pridisk:info', hotkeys = {'F5': disk_more_info}) tui.screen.popHelpLine() # entry contains the 'de' part of the tuple passed in answers['primary-disk'] = entry if 'installation-to-overwrite' in answers: answers['target-is-sr'] = target_is_sr[answers['primary-disk']] # Warn if not all of the disk is usable. # This can happen if we are unable to use GPT because we are currently # using DOS and need to preserve some utility partitions. blocks = diskutil.getDiskDeviceSize(answers['primary-disk']) tool = PartitionTool(answers['primary-disk']) if diskutil.blockSizeToGBSize(blocks) > constants.max_primary_disk_size_dos and tool.partTableType == 'DOS': if constants.GPT_SUPPORT and tool.utilityPartitions(): val = snackutil.ButtonChoiceWindowEx(tui.screen, "Large Disk Detected", "The disk selected is larger than the %d GB limit imposed by the DOS partitioning scheme. Would you like to remove the OEM partitions that require the DOS partitioning scheme, so that the whole disk can be used?" % constants.max_primary_disk_size_dos, ['Yes', 'No'], default=1) answers['zap-utility-partitions'] = (val == 'yes') elif not constants.GPT_SUPPORT: ButtonChoiceWindow(tui.screen, "Large Disk Detected", "The disk selected to install %s to is greater than %d GB. The partitioning scheme is limited to this value and therefore the remainder of this disk will be unavailable." % (MY_PRODUCT_BRAND, constants.max_primary_disk_size_dos), ['Ok']) if button == None: return RIGHT_FORWARDS if button == 'software raid': return raid_array_ui(answers) if button == 'back': return LEFT_BACKWARDS return RIGHT_FORWARDS
def select_primary_disk(answers): button = None diskEntries = diskutil.getQualifiedDiskList() entries = [] target_is_sr = {} for de in diskEntries: (vendor, model, size) = diskutil.getExtendedDiskInfo(de) if constants.min_primary_disk_size <= diskutil.blockSizeToGBSize(size): # determine current usage target_is_sr[de] = False (boot, state, storage) = diskutil.probeDisk(de) if storage[0]: target_is_sr[de] = True (vendor, model, size) = diskutil.getExtendedDiskInfo(de) stringEntry = "%s - %s [%s %s]" % (diskutil.getHumanDiskName(de), diskutil.getHumanDiskSize(size), vendor, model) e = (stringEntry, de) entries.append(e) # we should have at least one disk if len(entries) == 0: ButtonChoiceWindow(tui.screen, "No Primary Disk", "No disk with sufficient space to install %s on was found." % MY_PRODUCT_BRAND, ['Cancel']) return EXIT # if only one disk, set default: if len(entries) == 1: answers['primary-disk'] = entries[0][1] else: # default value: default = None if answers.has_key('primary-disk'): default = selectDefault(answers['primary-disk'], entries) tui.update_help_line([None, "<F5> more info"]) scroll, height = snackutil.scrollHeight(4, len(entries)) (button, entry) = snackutil.ListboxChoiceWindowEx( tui.screen, "Select Primary Disk", """Please select the disk you would like to install %s on (disks with insufficient space are not shown). You may need to change your system settings to boot from this disk.""" % (MY_PRODUCT_BRAND), entries, ['Ok', 'Back'], 55, scroll, height, default, help = 'pridisk:info', hotkey = 'F5', hotkey_cb = disk_more_info) tui.screen.popHelpLine() # entry contains the 'de' part of the tuple passed in answers['primary-disk'] = entry if 'installation-to-overwrite' in answers: answers['target-is-sr'] = target_is_sr[answers['primary-disk']] # Warn if not all of the disk is usable. # This can happen if we are unable to use GPT because we are currently # using DOS and need to preserve some utility partitions. blocks = diskutil.getDiskDeviceSize(answers['primary-disk']) tool = PartitionTool(answers['primary-disk']) if diskutil.blockSizeToGBSize(blocks) > constants.max_primary_disk_size_dos and tool.partTableType == 'DOS': if constants.GPT_SUPPORT and tool.utilityPartitions(): val = snackutil.ButtonChoiceWindowEx(tui.screen, "Large Disk Detected", "The disk selected is larger than the %d GB limit imposed by the DOS partitioning scheme. Would you like to remove the OEM partitions that require the DOS partitioning scheme, so that the whole disk can be used?" % constants.max_primary_disk_size_dos, ['Yes', 'No'], default=1) answers['zap-utility-partitions'] = (val == 'yes') else: ButtonChoiceWindow(tui.screen, "Large Disk Detected", "The disk selected to install %s to is greater than %d GB. The partitioning scheme is limited to this value and therefore the remainder of this disk will be unavailable." % (MY_PRODUCT_BRAND, constants.max_primary_disk_size_dos), ['Ok']) if button == None: return SKIP_SCREEN if button == 'back': return LEFT_BACKWARDS return RIGHT_FORWARDS
def restoreFromBackup(backup, progress=lambda x: ()): """ Restore files from backup_partition to the root partition on disk. Call progress with a value between 0 and 100. Re-install bootloader. Fails if backup is not same version as the CD in use.""" label = None bootlabel = None disk = backup.root_disk tool = PartitionTool(disk) _, _, _, storage, _ = diskutil.probeDisk(disk) create_sr_part = storage[0] is not None _, boot_partnum, primary_partnum, backup_partnum, logs_partnum, swap_partnum, _ = backend.inspectTargetDisk( disk, None, [], constants.PRESERVE_IF_UTILITY, create_sr_part, True) backup_fs = util.TempMount(backup.partition, 'backup-', options=['ro']) inventory = util.readKeyValueFile(os.path.join(backup_fs.mount_point, constants.INVENTORY_FILE), strip_quotes=True) backup_partition_layout = inventory['PARTITION_LAYOUT'].split(',') backup_fs.unmount() logger.log("BACKUP DISK PARTITION LAYOUT: %s" % backup_partition_layout) backup_partition = backup.partition assert backup_partition.startswith('/dev/') assert disk.startswith('/dev/') restore_partition = partitionDevice(disk, primary_partnum) logger.log("Restoring to partition %s." % restore_partition) boot_part = tool.getPartition(boot_partnum) boot_device = partitionDevice(disk, boot_partnum) if boot_part else None efi_boot = boot_part and boot_part['id'] == GPTPartitionTool.ID_EFI_BOOT # determine current location of bootloader current_location = 'unknown' try: root_fs = util.TempMount(restore_partition, 'root-', options=['ro'], boot_device=boot_device) try: boot_config = bootloader.Bootloader.loadExisting( root_fs.mount_point) current_location = boot_config.location logger.log("Bootloader currently in %s" % current_location) finally: root_fs.unmount() except: pass # mount the backup fs backup_fs = util.TempMount(backup_partition, 'restore-backup-', options=['ro']) try: # extract the bootloader config boot_config = bootloader.Bootloader.loadExisting(backup_fs.mount_point) if boot_config.src_fmt == 'grub': raise RuntimeError("Backup uses grub bootloader which is no longer supported - " + \ "to restore please use a version of the installer that matches the backup partition") # format the restore partition(s): try: util.mkfs(constants.rootfs_type, restore_partition) except Exception as e: raise RuntimeError("Failed to create root filesystem: %s" % e) if efi_boot: try: util.mkfs('vfat', boot_device) except Exception as e: raise RuntimeError("Failed to create boot filesystem: %s" % e) # mount restore partition: dest_fs = util.TempMount(restore_partition, 'restore-dest-') efi_mounted = False try: if efi_boot: esp = os.path.join(dest_fs.mount_point, 'boot', 'efi') os.makedirs(esp) util.mount(boot_device, esp) efi_mounted = True # copy files from the backup partition to the restore partition: objs = filter( lambda x: x not in ['lost+found', '.xen-backup-partition', '.xen-gpt.bin'], os.listdir(backup_fs.mount_point)) for i in range(len(objs)): obj = objs[i] logger.log("Restoring subtree %s..." % obj) progress((i * 100) / len(objs)) # Use 'cp' here because Python's copying tools are useless and # get stuck in an infinite loop when copying e.g. /dev/null. if util.runCmd2([ 'cp', '-a', os.path.join(backup_fs.mount_point, obj), dest_fs.mount_point ]) != 0: raise RuntimeError("Failed to restore %s directory" % obj) logger.log( "Data restoration complete. About to re-install bootloader.") location = boot_config.location m = re.search(r'root=LABEL=(\S+)', boot_config.menu[boot_config.default].kernel_args) if m: label = m.group(1) if location == constants.BOOT_LOCATION_PARTITION and current_location == constants.BOOT_LOCATION_MBR: # if bootloader in the MBR it's probably not safe to restore with it # on the partition logger.log( "Bootloader is currently installed to MBR, restoring to MBR instead of partition" ) location = constants.BOOT_LOCATION_MBR with open(os.path.join(backup_fs.mount_point, 'etc', 'fstab'), 'r') as fstab: for line in fstab: m = re.match(r'LABEL=(\S+)\s+/boot/efi\s', line) if m: bootlabel = m.group(1) mounts = { 'root': dest_fs.mount_point, 'boot': os.path.join(dest_fs.mount_point, 'boot') } # prepare extra mounts for installing bootloader: util.bindMount("/dev", "%s/dev" % dest_fs.mount_point) util.bindMount("/sys", "%s/sys" % dest_fs.mount_point) util.bindMount("/proc", "%s/proc" % dest_fs.mount_point) if boot_config.src_fmt == 'grub2': if efi_boot: branding = util.readKeyValueFile( os.path.join(backup_fs.mount_point, constants.INVENTORY_FILE)) branding['product-brand'] = branding['PRODUCT_BRAND'] backend.setEfiBootEntry(mounts, disk, boot_partnum, constants.INSTALL_TYPE_RESTORE, branding) else: if location == constants.BOOT_LOCATION_MBR: if diskutil.is_raid(disk): for member in diskutil.getDeviceSlaves(disk): backend.installGrub2(mounts, member, False) else: backend.installGrub2(mounts, disk, False) else: backend.installGrub2(mounts, restore_partition, True) else: backend.installExtLinux(mounts, disk, probePartitioningScheme(disk), location) # restore bootloader configuration dst_file = boot_config.src_file.replace(backup_fs.mount_point, dest_fs.mount_point, 1) util.assertDir(os.path.dirname(dst_file)) boot_config.commit(dst_file) finally: util.umount("%s/proc" % dest_fs.mount_point) util.umount("%s/sys" % dest_fs.mount_point) util.umount("%s/dev" % dest_fs.mount_point) if efi_mounted: util.umount(esp) dest_fs.unmount() finally: backup_fs.unmount() if not label: raise RuntimeError( "Failed to find label required for root filesystem.") if efi_boot and not bootlabel: raise RuntimeError( "Failed to find label required for boot filesystem.") if util.runCmd2(['e2label', restore_partition, label]) != 0: raise RuntimeError("Failed to label root partition") if bootlabel: if util.runCmd2(['fatlabel', boot_device, bootlabel]) != 0: raise RuntimeError("Failed to label boot partition") if 'LOG' in backup_partition_layout: # From 7.x (new layout) to 7.x (new layout) tool.commitActivePartitiontoDisk(boot_partnum) rdm_label = label.split("-")[1] logs_part = partitionDevice(disk, logs_partnum) swap_part = partitionDevice(disk, swap_partnum) if util.runCmd2( ['e2label', logs_part, constants.logsfs_label % rdm_label]) != 0: raise RuntimeError("Failed to label logs partition") if util.runCmd2([ 'swaplabel', '-L', constants.swap_label % rdm_label, swap_part ]) != 0: raise RuntimeError("Failed to label swap partition")