예제 #1
0
def go(ui, args, answerfile_address, answerfile_script):
    extra_repo_defs = []
    results = {
        'keymap': None,
        'serial-console': None,
        'operation': init_constants.OPERATION_INSTALL,
        'boot-serial': False,
        'extra-repos': [],
        'network-backend': constants.NETWORK_BACKEND_DEFAULT,
        'root-password': ('pwdhash', '!!'),
        'create-new-partitions':
        True,  # FALSE = DOS | TRUE = GPT set via command line only with --disable-gpt
        'new-partition-layout':
        False,  # TRUE = GPT with LOG,BACKUP,ROOT,BOOT,SWAP,SR automatically set during install/upgrade
        'services': {s: None
                     for s in constants.SERVICES
                     },  # default state for services, example {'sshd': None}
    }
    suppress_extra_cd_dialog = False
    serial_console = None
    boot_console = None
    boot_serial = False

    if not xen_control_domain() or '--virtual' in args:
        hardware.useVMHardwareFunctions()

    for (opt, val) in args.items():
        if opt == "--boot-console":
            # takes precedence over --console
            if hardware.is_serialConsole(val):
                boot_console = hardware.getSerialConfig()
        elif opt == "--console":
            for console in val:
                if hardware.is_serialConsole(console):
                    serial_console = hardware.getSerialConfig()
            if hardware.is_serialConsole(val[-1]):
                boot_serial = True
        elif opt == "--keymap":
            results["keymap"] = val
            logger.log("Keymap specified on command-line: %s" % val)
        elif opt == "--extrarepo":
            extra_repo_defs += val
        elif opt == "--onecd":
            suppress_extra_cd_dialog = True
        elif opt == "--disable-gpt":
            constants.GPT_SUPPORT = False
            results["create-new-partitions"] = False
            logger.log(
                "Forcing DOS partition table and old partition layout via command-line"
            )
        elif opt == "--legacy-partitions":
            results["create-new-partitions"] = False
            logger.log("Forcing old partition layout via command-line")
        elif opt == "--cc-preparations":
            constants.CC_PREPARATIONS = True
            results['network-backend'] = constants.NETWORK_BACKEND_BRIDGE

    if boot_console and not serial_console:
        serial_console = boot_console
        boot_serial = True
    if serial_console:
        try:
            results['serial-console'] = hardware.SerialPort.from_string(
                serial_console)
            results['boot-serial'] = boot_serial
            logger.log(
                "Serial console specified on command-line: %s, default boot: %s"
                % (serial_console, boot_serial))
        except:
            pass

    interactive = True
    try:
        if os.path.isfile(constants.defaults_data_file):
            data_file = open(constants.defaults_data_file)
            try:
                defaults = json.load(data_file)
            finally:
                data_file.close()
            results.update(defaults)

        # loading an answerfile?
        assert ui is not None or answerfile_address is not None or answerfile_script is not None

        if answerfile_address and answerfile_script:
            raise RuntimeError(
                "Both answerfile and answerfile generator passed on command line."
            )

        a = None
        parsing_except = None
        if answerfile_address:
            a = answerfile.Answerfile.fetch(answerfile_address)
        elif answerfile_script:
            a = answerfile.Answerfile.generate(answerfile_script)
        if a:
            interactive = False
            results['network-hardware'] = netutil.scanConfiguration()
            try:
                results.update(a.parseScripts())
                results.update(a.processAnswerfileSetup())

                if ui and results.get('ui-confirmation-prompt', False):
                    if not ui.init.confirm_proceed():
                        logger.log("User did not confirm installation. Reboot")
                        return constants.EXIT_USER_CANCEL

                if 'extra-repos' in results:
                    # load drivers now
                    for media, address in results['extra-repos']:
                        for r in repository.repositoriesFromDefinition(
                                media, address, drivers=True):
                            r.installPackages(lambda x: (), {'root': '/'})

                if 'fcoe-interfaces' in results:
                    fcoeutil.start_fcoe(results['fcoe-interfaces'])

                util.runCmd2(util.udevsettleCmd())
                time.sleep(1)
                diskutil.mpath_part_scan()

                # ensure partitions/disks are not locked by LVM
                lvm = disktools.LVMTool()
                lvm.deactivateAll()
                del lvm

                diskutil.log_available_disks()

                results.update(a.processAnswerfile())
                results = fixMpathResults(results)
            except Exception as e:
                logger.logException(e)
                parsing_except = e

        results['extra-repos'] += extra_repo_defs
        logger.log("Driver repos: %s" % str(results['extra-repos']))

        scripts.run_scripts('installation-start')

        if parsing_except:
            raise parsing_except

        # log the modules that we loaded:
        logger.log("All needed modules should now be loaded. We have loaded:")
        util.runCmd2(["lsmod"])

        status = constants.EXIT_OK

        # how much RAM do we have?
        ram_found_mb = hardware.getHostTotalMemoryKB() / 1024
        ram_warning = ram_found_mb < constants.MIN_SYSTEM_RAM_MB
        vt_warning = not hardware.VTSupportEnabled()

        # Generate the UI sequence and populate some default
        # values in backend input.  Note that not all these screens
        # will be displayed as they have conditional to skip them at
        # the start of each function.  In future these conditionals will
        # be moved into the sequence definition and evaluated by the
        # UI dispatcher.
        aborted = False
        if ui and interactive:
            uiexit = ui.installer.runMainSequence(results, ram_warning,
                                                  vt_warning,
                                                  suppress_extra_cd_dialog)
            if uiexit == uicontroller.EXIT:
                aborted = True

        if not aborted:
            if results['install-type'] == constants.INSTALL_TYPE_RESTORE:
                logger.log('INPUT ANSWER DICTIONARY')
                backend.prettyLogAnswers(results)
                logger.log("SCRIPTS DICTIONARY:")
                backend.prettyLogAnswers(scripts.script_dict)
                logger.log("Starting actual restore")
                backup = results['backup-to-restore']
                if ui:
                    pd = tui.progress.initProgressDialog(
                        "Restoring %s" % backup,
                        "Restoring data - this may take a while...", 100)

                def progress(x):
                    if ui and pd:
                        tui.progress.displayProgressDialog(x, pd)

                restore.restoreFromBackup(backup, progress)
                if ui:
                    tui.progress.clearModelessDialog()
                    tui.progress.OKDialog(
                        "Restore",
                        "The restore operation completed successfully.")
            else:
                logger.log("Starting actual installation")
                backend.performInstallation(results, ui, interactive)

                if ui and interactive:
                    ui.installer.screens.installation_complete()

                logger.log("The installation completed successfully.")
        else:
            logger.log(
                "The user aborted the installation from within the user interface."
            )
            status = constants.EXIT_USER_CANCEL
    except Exception as e:
        try:
            # first thing to do is to get the traceback and log it:
            ex = sys.exc_info()
            err = str.join("", traceback.format_exception(*ex))
            logger.log("INSTALL FAILED.")
            logger.log("A fatal exception occurred:")
            logger.log(err)

            # run the user's scripts - an arg of "1" indicates failure
            scripts.run_scripts('installation-complete', '1')

            # collect logs where possible
            xelogging.collectLogs("/tmp")

            # now display a friendly error dialog:
            if ui:
                ui.exn_error_dialog("install-log", True, interactive)
            else:
                txt = constants.error_string(str(e), 'install-log', True)
                logger.log(txt)

            # and now on the disk if possible:
            if 'primary-disk' in results and 'primary-partnum' in results and 'logs-partnum' in results:
                backend.writeLog(results['primary-disk'],
                                 results['primary-partnum'],
                                 results['logs-partnum'])
            elif 'primary-disk' in results and 'primary-partnum' in results:
                backend.writeLog(results['primary-disk'],
                                 results['primary-partnum'], None)

            logger.log(results)
        except Exception as e:
            # Don't let logging exceptions prevent subsequent actions
            print 'Logging failed: ' + str(e)

        # exit with failure status:
        status = constants.EXIT_ERROR

    else:
        # run the user's scripts - an arg of "0" indicates success
        try:
            scripts.run_scripts('installation-complete', '0')
        except:
            pass

        # put the log in /tmp:
        xelogging.collectLogs('/tmp')

        # and now on the disk if possible:
        if 'primary-disk' in results and 'primary-partnum' in results and 'logs-partnum' in results:
            backend.writeLog(results['primary-disk'],
                             results['primary-partnum'],
                             results['logs-partnum'])
        elif 'primary-disk' in results and 'primary-partnum' in results:
            backend.writeLog(results['primary-disk'],
                             results['primary-partnum'], None)

        assert (status == constants.EXIT_OK
                or status == constants.EXIT_USER_CANCEL)

    return status
예제 #2
0
            scripts.run_scripts('installation-complete', '1')

            # collect logs where possible
            xelogging.collectLogs("/tmp")

            # now display a friendly error dialog:
            if ui:
                ui.exn_error_dialog("install-log", True, interactive)
            else:
                txt = constants.error_string(str(e), 'install-log', True)
                xelogging.log(txt)

            # and now on the disk if possible:
            if 'primary-disk' in results and 'primary-partnum' in results and 'logs-partnum' in results:
                backend.writeLog(results['primary-disk'],
                                 results['primary-partnum'],
                                 results['logs-partnum'])
            elif 'primary-disk' in results and 'primary-partnum' in results:
                backend.writeLog(results['primary-disk'],
                                 results['primary-partnum'], None)

            xelogging.log(results)
        except Exception, e:
            # Don't let logging exceptions prevent subsequent actions
            print 'Logging failed: ' + str(e)

        # exit with failure status:
        status = constants.EXIT_ERROR

    else:
        # run the user's scripts - an arg of "0" indicates success
예제 #3
0
파일: install.py 프로젝트: xtha/pxe
def go(ui, args, answerfile_address, answerfile_script):
    extra_repo_defs = []
    results = {
        'keymap': None, 
        'serial-console': None,
        'operation': init_constants.OPERATION_INSTALL,
        'boot-serial': False,
        'extra-repos': [],
        'network-backend': constants.NETWORK_BACKEND_DEFAULT,
        }
    suppress_extra_cd_dialog = False
    serial_console = None
    boot_console = None
    boot_serial = False

    if not xen_control_domain() or args.has_key('--virtual'):
        hardware.useVMHardwareFunctions()

    for (opt, val) in args.items():
        if opt == "--boot-console":
            # takes precedence over --console
            if hardware.is_serialConsole(val):
                boot_console = hardware.getSerialConfig()
        elif opt == "--console":
            for console in val:
                if hardware.is_serialConsole(console):
                    serial_console = hardware.getSerialConfig()
            if hardware.is_serialConsole(val[-1]):
                boot_serial = True
        elif opt == "--keymap":
            results["keymap"] = val
            xelogging.log("Keymap specified on command-line: %s" % val)
        elif opt == "--extrarepo":
            extra_repo_defs += val
        elif opt == "--onecd":
            suppress_extra_cd_dialog = True
        elif opt == "--disable-gpt":
            constants.GPT_SUPPORT = False

    if boot_console and not serial_console:
        serial_console = boot_console
        boot_serial = True
    if serial_console:
        try:
            results['serial-console'] = hardware.SerialPort.from_string(serial_console)
            results['boot-serial'] = boot_serial
            xelogging.log("Serial console specified on command-line: %s, default boot: %s" % 
                          (serial_console, boot_serial))
        except:
            pass

    interactive = True
    try:
        # loading an answerfile?
        assert ui != None or answerfile_address != None or answerfile_script != None

        if answerfile_address and answerfile_script:
            raise RuntimeError, "Both answerfile and answerfile generator passed on command line."

        a = None
        if answerfile_address:
            a = answerfile.Answerfile(answerfile_address)
        elif answerfile_script:
            a = answerfile.Answerfile.generate(answerfile_script)
        if a:
            interactive = False
            results['network-hardware'] = netutil.scanConfiguration()
            results.update(a.parseScripts())
            results.update(a.processAnswerfile())
            if results.has_key('extra-repos'):
                # load drivers now
                for d in results['extra-repos']:
                    media, address, _ = d
                    for r in repository.repositoriesFromDefinition(media, address):
                        for p in r:
                            if p.type.startswith('driver'):
                                if p.load() != 0:
                                    raise RuntimeError, "Failed to load driver %s." % p.name

        results['extra-repos'] += extra_repo_defs
        xelogging.log("Driver repos: %s" % str(results['extra-repos']))

        scripts.run_scripts('installation-start')

        # log the modules that we loaded:
        xelogging.log("All needed modules should now be loaded. We have loaded:")
        util.runCmd2(["/bin/lsmod"])

        status = constants.EXIT_OK

        # debug: print out what disks have been discovered
        diskutil.log_available_disks()

        # how much RAM do we have?
        ram_found_mb = hardware.getHostTotalMemoryKB() / 1024
        ram_warning = ram_found_mb < constants.MIN_SYSTEM_RAM_MB
        vt_warning = not hardware.VTSupportEnabled()

        # Generate the UI sequence and populate some default
        # values in backend input.  Note that not all these screens
        # will be displayed as they have conditional to skip them at
        # the start of each function.  In future these conditionals will
        # be moved into the sequence definition and evaluated by the
        # UI dispatcher.
        aborted = False
        if ui and interactive:
            uiexit = ui.installer.runMainSequence(
                results, ram_warning, vt_warning, suppress_extra_cd_dialog
                )
            if uiexit == uicontroller.EXIT:
                aborted = True

        if not aborted:
            if results['install-type'] == constants.INSTALL_TYPE_RESTORE:
                xelogging.log("Starting actual restore")
                backup = results['backup-to-restore']
                if ui:
                    pd = tui.progress.initProgressDialog("Restoring",
                                                         "Restoring data - this may take a while...",
                                                         100)
                def progress(x):
                    if ui and pd:
                        tui.progress.displayProgressDialog(x, pd)
                restore.restoreFromBackup(backup.partition, backup.root_disk, progress)
                if pd:
                    tui.progress.clearModelessDialog()
                    tui.progress.OKDialog("Restore", "The restore operation completed successfully.")
            else:
                xelogging.log("Starting actual installation")
                results = backend.performInstallation(results, ui, interactive)

                if ui and interactive:
                    ui.installer.screens.installation_complete()
            
                xelogging.log("The installation completed successfully.")
        else:
            xelogging.log("The user aborted the installation from within the user interface.")
            status = constants.EXIT_USER_CANCEL
    except Exception, e:
        try:
            # first thing to do is to get the traceback and log it:
            ex = sys.exc_info()
            err = str.join("", traceback.format_exception(*ex))
            xelogging.log("INSTALL FAILED.")
            xelogging.log("A fatal exception occurred:")
            xelogging.log(err)

            # run the user's scripts - an arg of "1" indicates failure
            scripts.run_scripts('installation-complete', '1')
    
            # collect logs where possible
            xelogging.collectLogs("/tmp")
    
            # now display a friendly error dialog:
            if ui:
                ui.exn_error_dialog("install-log", True, interactive)
            else:
                txt = constants.error_string(str(e), 'install-log', True)
                xelogging.log(txt)
    
            # and now on the disk if possible:
            if 'primary-disk' in results and 'primary-partnum' in results:
                backend.writeLog(results['primary-disk'], results['primary-partnum'])
    
            xelogging.log(results)
        except Exception, e:
            # Don't let logging exceptions prevent subsequent actions
            print 'Logging failed: '+str(e)
예제 #4
0
파일: install.py 프로젝트: xtha/pxe
                backend.writeLog(results['primary-disk'], results['primary-partnum'])
    
            xelogging.log(results)
        except Exception, e:
            # Don't let logging exceptions prevent subsequent actions
            print 'Logging failed: '+str(e)
            
        # exit with failure status:
        status = constants.EXIT_ERROR

    else:
        # run the user's scripts - an arg of "0" indicates success
        try:
            scripts.run_scripts('installation-complete', '0')
        except:
            pass

        # put the log in /tmp:
        xelogging.collectLogs('/tmp')

        # and now on the disk if possible:
        if 'primary-disk' in results and 'primary-partnum' in results:
            backend.writeLog(results['primary-disk'], results['primary-partnum'])

        assert (status == constants.EXIT_OK or status == constants.EXIT_USER_CANCEL)
        
    return status

if __name__ == "__main__":
    sys.exit(main(util.splitArgs(sys.argv[1:], array_args = ('--extrarepo'))))
예제 #5
0
def doRestore(backup, progress, backup_partition_layout, has_logs_partition):

    backup_partition = backup.partition
    backup_version = backup.version
    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)
    limit_version = product.THIS_PLATFORM_VERSION
    logs_partition = tool.getPartition(logs_partnum)
    boot_partition = tool.getPartition(boot_partnum)

    assert backup_partition.startswith('/dev/')
    assert disk.startswith('/dev/')

    label = None
    bootlabel = None
    if has_logs_partition and not backup_partition_layout:  # From 7.x (new layout) to 6.x
        restore_partition = partitionDevice(disk, logs_partnum)
    else:
        restore_partition = partitionDevice(disk, primary_partnum)
    xelogging.log("Restoring to partition %s." % restore_partition)

    tool = PartitionTool(disk)
    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
            xelogging.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):
        if util.runCmd2(['mkfs.%s' % constants.rootfs_type, restore_partition
                         ]) != 0:
            raise RuntimeError, "Failed to create root filesystem"
        if efi_boot:
            if util.runCmd2(['mkfs.vfat', boot_device]) != 0:
                raise RuntimeError, "Failed to create boot filesystem"

        # mount restore partition:
        dest_fs = util.TempMount(restore_partition, 'restore-dest-')
        try:

            # 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]
                xelogging.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

            xelogging.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
                xelogging.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,
                                            branding)
                else:
                    if location == constants.BOOT_LOCATION_MBR:
                        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)
            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 has_logs_partition:
        if not backup_partition_layout:  # From 7.x (new layout) to 6.x
            # Delete backup, dom0, Boot and swap partitions
            tool.deletePartition(backup_partnum)
            tool.deletePartition(primary_partnum)
            tool.deletePartition(boot_partnum)
            tool.deletePartition(swap_partnum)

            # Rename logs partition to be n.1
            tool.renamePartition(srcNumber=logs_partnum,
                                 destNumber=primary_partnum,
                                 overwrite=False)

            # Create 4GB backup partition
            tool.createPartition(
                tool.ID_LINUX,
                sizeBytes=constants.backup_size_old * 2**20,
                startBytes=tool.partitionEnd(primary_partnum) +
                tool.sectorSize,
                number=backup_partnum)

            # Commit partition table and mark dom0 disk as bootable
            tool.commit(log=True)
            tool.commitActivePartitiontoDisk(primary_partnum)

            xelogging.log("Bootloader restoration complete.")
            xelogging.log("Restore successful.")
            backend.writeLog(disk, primary_partnum, logs_partnum)
        elif '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"
예제 #6
0
def restoreWithoutRepartButUEFI(backup, progress):

    backup_partition = backup.partition
    disk = backup.root_disk

    assert backup_partition.startswith('/dev/')
    assert disk.startswith('/dev/')

    # Restore the partition layout
    backup_fs = util.TempMount(backup_partition,
                               'restore-backup-',
                               options=['ro'])
    gpt_bin = None
    try:
        src_bin = os.path.join(backup_fs.mount_point, '.xen-gpt.bin')
        if os.path.exists(src_bin):
            gpt_bin = tempfile.mktemp()
            shutil.copyfile(src_bin, gpt_bin)
    finally:
        backup_fs.unmount()

    if gpt_bin:
        xelogging.log("Restoring partition layout")
        rc, err = util.runCmd2(["sgdisk", "-l", gpt_bin, disk],
                               with_stderr=True)
        if rc != 0:
            raise RuntimeError, "Failed to restore partition layout: %s" % err

    label = None
    bootlabel = None
    _, boot_partnum, primary_partnum, backup_partnum, logs_partnum, swap_partnum, _ = backend.inspectTargetDisk(
        disk, None, [], constants.PRESERVE_IF_UTILITY, True, True)
    restore_partition = partitionDevice(disk, primary_partnum)
    xelogging.log("Restoring to partition %s." % restore_partition)

    tool = PartitionTool(disk)
    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
            xelogging.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):
        if util.runCmd2(['mkfs.%s' % constants.rootfs_type, restore_partition
                         ]) != 0:
            raise RuntimeError, "Failed to create root filesystem"
        if efi_boot:
            if util.runCmd2(['mkfs.vfat', boot_device]) != 0:
                raise RuntimeError, "Failed to create boot filesystem"

        # mount restore partition:
        dest_fs = util.TempMount(restore_partition,
                                 'restore-dest-',
                                 boot_device=boot_device,
                                 boot_mount_point='/boot/efi')
        try:

            # 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]
                xelogging.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

            xelogging.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
                xelogging.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,
                                            branding)
                else:
                    if location == constants.BOOT_LOCATION_MBR:
                        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)
            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"

    xelogging.log("Bootloader restoration complete.")
    xelogging.log("Restore successful.")
    backend.writeLog(disk, primary_partnum, logs_partnum)