Ejemplo n.º 1
0
def install(storage_backend, disk_size, iso_path, zfs_tank_name, max_vms, unattended_xml):
    logging.info("Ensuring that drakrun@* services are stopped...")
    subprocess.check_output('systemctl stop \'drakrun@*\'', shell=True, stderr=subprocess.STDOUT)

    logging.info("Performing installation...")

    if unattended_xml:
        logging.info("Baking unattended.iso for automated installation")
        with tempfile.TemporaryDirectory() as tmpdir:
            tmp_xml_path = os.path.join(tmpdir, 'autounattend.xml')

            with open(tmp_xml_path, 'wb') as fw:
                with open(unattended_xml, 'rb') as fr:
                    fw.write(fr.read())

            try:
                subprocess.check_output(['genisoimage', '-o', os.path.join(LIB_DIR, "volumes/unattended.iso"), '-J', '-r', tmp_xml_path], stderr=subprocess.STDOUT)
            except subprocess.CalledProcessError:
                logging.exception("Failed to generate unattended.iso.")

    sha256_hash = hashlib.sha256()

    with open(iso_path, "rb") as f:
        for byte_block in iter(lambda: f.read(4096), b""):
            sha256_hash.update(byte_block)

        iso_sha256 = sha256_hash.hexdigest()

    install_info = InstallInfo(
        storage_backend=storage_backend,
        disk_size=disk_size,
        iso_path=os.path.abspath(iso_path),
        zfs_tank_name=zfs_tank_name,
        max_vms=max_vms,
        enable_unattended=unattended_xml is not None,
        iso_sha256=iso_sha256
    )
    install_info.save()

    logging.info("Checking xen-detect...")
    proc = subprocess.run('xen-detect -N', shell=True)

    if proc.returncode != 1:
        logging.error('It looks like the system is not running on Xen. Please reboot your machine into Xen hypervisor.')
        return

    logging.info("Testing if xl tool is sane...")

    try:
        subprocess.check_output('xl info', shell=True, stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError:
        logging.exception("Failed to test xl command.")
        return

    try:
        subprocess.check_output('xl uptime vm-0', shell=True, stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError:
        pass
    else:
        logging.info('Detected that vm-0 is already running, stopping it.')
        subprocess.run('xl destroy vm-0', shell=True, check=True)

    generate_vm_conf(install_info, 0)

    if storage_backend == "qcow2":
        try:
            subprocess.check_output("qemu-img --version", shell=True)
        except subprocess.CalledProcessError:
            logging.exception("Failed to determine qemu-img version. Make sure you have qemu-utils installed.")
            return

        try:
            subprocess.check_output(' '.join([
                "qemu-img",
                "create",
                "-f",
                "qcow2",
                os.path.join(LIB_DIR, "volumes/vm-0.img"),
                shlex.quote(disk_size)
            ]), shell=True)
        except subprocess.CalledProcessError:
            logging.exception("Failed to create a new volume using qemu-img.")
            return
    elif storage_backend == "zfs":
        try:
            subprocess.check_output("zfs -?", shell=True)
        except subprocess.CalledProcessError:
            logging.exception("Failed to execute zfs command. Make sure you have ZFS support installed.")
            return

        vm0_vol = shlex.quote(os.path.join(zfs_tank_name, 'vm-0'))

        try:
            subprocess.check_output(f"zfs destroy -Rfr {vm0_vol}", stderr=subprocess.STDOUT, shell=True)
        except subprocess.CalledProcessError as e:
            if b'dataset does not exist' not in e.output:
                logging.exception(f"Failed to destroy the existing ZFS volume {vm0_vol}.")
                return

        try:
            subprocess.check_output(' '.join([
                "zfs",
                "create",
                "-V",
                shlex.quote(disk_size),
                shlex.quote(os.path.join(zfs_tank_name, 'vm-0'))
            ]), shell=True)
        except subprocess.CalledProcessError:
            logging.exception("Failed to create a new volume using zfs create.")
            return
    else:
        raise RuntimeError('Unknown storage backend type.')

    try:
        subprocess.check_output("brctl show", shell=True)
    except subprocess.CalledProcessError:
        logging.exception("Failed to execute brctl show. Make sure you have bridge-utils installed.")
        return

    try:
        subprocess.check_output('brctl addbr drak0', stderr=subprocess.STDOUT, shell=True)
    except subprocess.CalledProcessError as e:
        if b'already exists' in e.output:
            logging.info("Bridge drak0 already exists.")
        else:
            logging.exception("Failed to create bridge drak0.")

    cfg_path = os.path.join(ETC_DIR, "configs/vm-0.cfg")

    try:
        subprocess.run('xl create {}'.format(shlex.quote(cfg_path)), shell=True, check=True)
    except subprocess.CalledProcessError:
        logging.exception("Failed to launch VM vm-0")
        return

    logging.info("-" * 80)
    logging.info("Initial VM setup is complete and the vm-0 was launched.")
    logging.info("Please now VNC to the port 5900 on this machine to perform Windows installation.")
    logging.info("After you have installed Windows and booted it to the desktop, please execute:")
    logging.info("# draksetup postinstall")

    with open(cfg_path, "r") as f:
        data = f.read()
        m = re.search(r'vncpasswd[ ]*=(.*)', data)
        if m:
            passwd = m.group(1).strip()
            if passwd[0] == '"' and passwd[-1] == '"':
                passwd = passwd[1:-1]

            logging.info("Your configured VNC password is:")
            logging.info(passwd)

    logging.info("Please note that on some machines, system installer may boot for up to 10 minutes")
    logging.info("and may look unresponsive during the process. Please be patient.")
    logging.info("-" * 80)
Ejemplo n.º 2
0
def backend(setup):
    yield LvmStorageBackend(InstallInfo.load())
Ejemplo n.º 3
0
def postinstall(report, generate_usermode):
    if not check_root():
        return

    if os.path.exists(os.path.join(ETC_DIR, "no_usage_reports")):
        report = False

    install_info = InstallInfo.load()

    logging.info("Cleaning up leftovers(if any)")
    cleanup_postinstall_files()

    logging.info("Ejecting installation CDs")
    eject_cd("vm-0", FIRST_CDROM_DRIVE)
    if install_info.enable_unattended:
        # If unattended install is enabled, we have an additional CD-ROM drive
        eject_cd("vm-0", SECOND_CDROM_DRIVE)

    output = subprocess.check_output(['vmi-win-guid', 'name', 'vm-0'],
                                     timeout=30).decode('utf-8')

    try:
        version = re.search(r'Version: (.*)', output).group(1)
        pdb = re.search(r'PDB GUID: ([0-9a-f]+)', output).group(1)
        fn = re.search(r'Kernel filename: ([a-z]+\.[a-z]+)', output).group(1)
    except AttributeError:
        logging.error("Failed to obtain kernel PDB GUID/Kernel filename.")
        return

    logging.info("Determined PDB GUID: {}".format(pdb))
    logging.info("Determined kernel filename: {}".format(fn))

    logging.info("Fetching PDB file...")
    dest = fetch_pdb(fn, pdb, destdir=PROFILE_DIR)

    logging.info("Generating profile out of PDB file...")
    profile = make_pdb_profile(dest)

    logging.info("Saving profile...")
    kernel_profile = os.path.join(PROFILE_DIR, 'kernel.json')
    with open(kernel_profile, 'w') as f:
        f.write(profile)

    vmi_offsets = extract_vmi_offsets('vm-0', kernel_profile)
    explorer_pid = extract_explorer_pid('vm-0', kernel_profile, vmi_offsets)
    runtime_info = RuntimeInfo(vmi_offsets=vmi_offsets,
                               inject_pid=explorer_pid)

    logging.info("Saving runtime profile...")
    with open(os.path.join(PROFILE_DIR, 'runtime.json'), 'w') as f:
        f.write(runtime_info.to_json(indent=4))

    logging.info("Saving VM snapshot...")
    subprocess.check_output('xl save vm-0 ' +
                            os.path.join(VOLUME_DIR, "snapshot.sav"),
                            shell=True)

    storage_backend = get_storage_backend(install_info)
    storage_backend.snapshot_vm0_volume()
    logging.info("Snapshot was saved succesfully.")

    if generate_usermode:
        try:
            create_rekall_profiles(install_info)
        except RuntimeError as e:
            logging.warning("Generating usermode profiles failed")
            logging.exception(e)

    if report:
        send_usage_report({
            "kernel": {
                "guid": pdb,
                "filename": fn,
                "version": version
            },
            "install_iso": {
                "sha256": install_info.iso_sha256
            }
        })

    logging.info("All right, drakrun setup is done.")
    logging.info("First instance of drakrun will be enabled automatically...")
    subprocess.check_output('systemctl enable drakrun@1', shell=True)
    subprocess.check_output('systemctl start drakrun@1', shell=True)

    logging.info("If you want to have more parallel instances, execute:")
    logging.info("  # draksetup scale <number of instances>")
Ejemplo n.º 4
0
def generate_profiles(no_report=False, generate_usermode=True):
    if os.path.exists(os.path.join(ETC_DIR, "no_usage_reports")):
        no_report = True

    install_info = InstallInfo.load()
    max_vms = install_info.max_vms
    output = subprocess.check_output(['vmi-win-guid', 'name', 'vm-0'], timeout=30).decode('utf-8')

    try:
        version = re.search(r'Version: (.*)', output).group(1)
        pdb = re.search(r'PDB GUID: ([0-9a-f]+)', output).group(1)
        fn = re.search(r'Kernel filename: ([a-z]+\.[a-z]+)', output).group(1)
    except AttributeError:
        logging.error("Failed to obtain kernel PDB GUID/Kernel filename.")
        return

    logging.info("Determined PDB GUID: {}".format(pdb))
    logging.info("Determined kernel filename: {}".format(fn))

    logging.info("Fetching PDB file...")
    dest = fetch_pdb(fn, pdb, destdir=os.path.join(LIB_DIR, 'profiles/'))

    logging.info("Generating profile out of PDB file...")
    profile = make_pdb_profile(dest)

    logging.info("Saving profile...")
    kernel_profile = os.path.join(LIB_DIR, 'profiles', 'kernel.json')
    with open(kernel_profile, 'w') as f:
        f.write(profile)

    output = subprocess.check_output(['vmi-win-offsets', '--name', 'vm-0', '--json-kernel', kernel_profile], timeout=30).decode('utf-8')

    offsets = re.findall(r'^([a-z_]+):(0x[0-9a-f]+)$', output, re.MULTILINE)
    if not offsets:
        logging.error("Failed to parse output of vmi-win-offsets.")
        return

    offsets_dict = {k: v for k, v in offsets}

    if 'kpgd' not in offsets_dict:
        logging.error("Failed to obtain KPGD value.")
        return

    module_dir = os.path.dirname(os.path.realpath(__file__))
    pid_tool = os.path.join(module_dir, "tools", "get-explorer-pid")
    explorer_pid_s = subprocess.check_output([pid_tool, "vm-0", kernel_profile, offsets_dict['kpgd']], timeout=30).decode('ascii', 'ignore')
    m = re.search(r'explorer\.exe:([0-9]+)', explorer_pid_s)
    explorer_pid = m.group(1)

    runtime_profile = {"vmi_offsets": offsets_dict, "inject_pid": explorer_pid}

    logging.info("Saving runtime profile...")
    with open(os.path.join(LIB_DIR, 'profiles', 'runtime.json'), 'w') as f:
        f.write(json.dumps(runtime_profile, indent=4))

    # TODO (optional) making usermode profiles (a special tool for GUID extraction is required)
    logging.info("Saving VM snapshot...")
    subprocess.check_output('xl save vm-0 ' + os.path.join(LIB_DIR, "volumes", "snapshot.sav"), shell=True)

    logging.info("Snapshot was saved succesfully.")

    if install_info.storage_backend == 'zfs':
        snap_name = shlex.quote(os.path.join(install_info.zfs_tank_name, 'vm-0@booted'))
        subprocess.check_output(f'zfs snapshot {snap_name}', shell=True)

    if generate_usermode:
        create_rekall_profiles(install_info)

    for vm_id in range(max_vms + 1):
        # we treat vm_id=0 as special internal one
        generate_vm_conf(install_info, vm_id)

    if not no_report:
        send_usage_report({
            "kernel": {
                "guid": pdb,
                "filename": fn,
                "version": version
            },
            "install_iso": {
                "sha256": install_info.iso_sha256
            }
        })

    reenable_services()
    logging.info("All right, drakrun setup is done.")
Ejemplo n.º 5
0
def install(vcpus, memory, storage_backend, disk_size, iso_path, zfs_tank_name,
            lvm_volume_group, unattended_xml):
    if not check_root():
        return

    if storage_backend == "lvm" and lvm_volume_group is None:
        raise Exception("lvm storage backend requires --lvm-volume-group")
    if storage_backend == "zfs" and zfs_tank_name is None:
        raise Exception("zfs storage backend requires --zfs-tank-name")

    if not sanity_check():
        logging.error("Sanity check failed.")
        return

    stop_all_drakruns()

    logging.info("Performing installation...")

    if vcpus < 1:
        logging.error("Your VM must have at least 1 vCPU.")
        return

    if memory < 512:
        logging.error("Your VM must have at least 512 MB RAM.")
        return

    if memory < 1536:
        logging.warning(
            "Using less than 1.5 GB RAM per VM is not recommended for any supported system."
        )

    if unattended_xml:
        logging.info("Baking unattended.iso for automated installation")
        with tempfile.TemporaryDirectory() as tmpdir:
            tmp_xml_path = os.path.join(tmpdir, 'autounattend.xml')

            with open(tmp_xml_path, 'wb') as fw:
                with open(unattended_xml, 'rb') as fr:
                    fw.write(fr.read())

            try:
                subprocess.check_output([
                    'genisoimage', '-o',
                    os.path.join(VOLUME_DIR, "unattended.iso"), '-J', '-r',
                    tmp_xml_path
                ],
                                        stderr=subprocess.STDOUT)
            except subprocess.CalledProcessError:
                logging.exception("Failed to generate unattended.iso.")

    sha256_hash = hashlib.sha256()

    logging.info("Calculating hash of iso")
    iso_file_size = os.stat(iso_path).st_size
    block_size = 128 * 1024
    with tqdm(total=iso_file_size, unit_scale=True) as pbar:
        with open(iso_path, "rb") as f:
            for byte_block in iter(lambda: f.read(block_size), b""):
                pbar.update(block_size)
                sha256_hash.update(byte_block)

            iso_sha256 = sha256_hash.hexdigest()

    install_info = InstallInfo(vcpus=vcpus,
                               memory=memory,
                               storage_backend=storage_backend,
                               disk_size=disk_size,
                               iso_path=os.path.abspath(iso_path),
                               zfs_tank_name=zfs_tank_name,
                               lvm_volume_group=lvm_volume_group,
                               enable_unattended=unattended_xml is not None,
                               iso_sha256=iso_sha256)
    install_info.save()

    try:
        subprocess.check_output('xl uptime vm-0',
                                shell=True,
                                stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError:
        pass
    else:
        logging.info('Detected that vm-0 is already running, stopping it.')
        subprocess.run('xl destroy vm-0', shell=True, check=True)

    generate_vm_conf(install_info, 0)

    backend = get_storage_backend(install_info)
    backend.initialize_vm0_volume(disk_size)

    try:
        subprocess.check_output("brctl show", shell=True)
    except subprocess.CalledProcessError:
        logging.exception(
            "Failed to execute brctl show. Make sure you have bridge-utils installed."
        )
        return

    net_enable = conf['drakrun'].getboolean('net_enable', fallback=False)
    out_interface = conf['drakrun'].get('out_interface', '')
    dns_server = conf['drakrun'].get('dns_server', '')

    setup_vm_network(vm_id=0,
                     net_enable=net_enable,
                     out_interface=out_interface,
                     dns_server=dns_server)

    if net_enable:
        start_dnsmasq(vm_id=0, dns_server=dns_server, background=True)

    cfg_path = os.path.join(VM_CONFIG_DIR, "vm-0.cfg")

    try:
        subprocess.run('xl create {}'.format(shlex.quote(cfg_path)),
                       shell=True,
                       check=True)
    except subprocess.CalledProcessError:
        logging.exception("Failed to launch VM vm-0")
        return

    logging.info("-" * 80)
    logging.info("Initial VM setup is complete and the vm-0 was launched.")
    logging.info(
        "Please now VNC to the port 5900 on this machine to perform Windows installation."
    )
    logging.info(
        "After you have installed Windows and booted it to the desktop, please execute:"
    )
    logging.info("# draksetup postinstall")

    with open(cfg_path, "r") as f:
        data = f.read()
        m = re.search(r'vncpasswd[ ]*=(.*)', data)
        if m:
            passwd = m.group(1).strip()
            if passwd[0] == '"' and passwd[-1] == '"':
                passwd = passwd[1:-1]

            logging.info("Your configured VNC password is:")
            logging.info(passwd)

    logging.info(
        "Please note that on some machines, system installer may boot for up to 10 minutes"
    )
    logging.info(
        "and may look unresponsive during the process. Please be patient.")
    logging.info("-" * 80)
Ejemplo n.º 6
0
def run_vm(vm_id):
    install_info = InstallInfo.load()

    try:
        subprocess.check_output(
            ["xl", "destroy", "vm-{vm_id}".format(vm_id=vm_id)],
            stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError:
        pass

    try:
        os.unlink(
            os.path.join(LIB_DIR,
                         "volumes/vm-{vm_id}.img".format(vm_id=vm_id)))
    except FileNotFoundError:
        pass

    if install_info.storage_backend == 'qcow2':
        subprocess.run([
            "qemu-img", "create", "-f", "qcow2", "-o", "backing_file=vm-0.img",
            os.path.join(LIB_DIR, "volumes/vm-{vm_id}.img".format(vm_id=vm_id))
        ],
                       check=True)
    elif install_info.storage_backend == 'zfs':
        vm_zvol = os.path.join('/dev/zvol', install_info.zfs_tank_name,
                               f'vm-{vm_id}')
        vm_snap = os.path.join(install_info.zfs_tank_name,
                               f'vm-{vm_id}@booted')

        if not os.path.exists(vm_zvol):
            subprocess.run([
                "zfs", "clone", "-p",
                os.path.join(install_info.zfs_tank_name, 'vm-0@booted'),
                os.path.join(install_info.zfs_tank_name, f'vm-{vm_id}')
            ],
                           check=True)

            for _ in range(120):
                if not os.path.exists(vm_zvol):
                    time.sleep(0.1)
                else:
                    break
            else:
                logging.error(
                    f'Failed to see {vm_zvol} created after executing zfs clone command.'
                )
                return

            subprocess.run(["zfs", "snapshot", vm_snap], check=True)

        subprocess.run(["zfs", "rollback", vm_snap], check=True)
    else:
        raise RuntimeError("Unknown storage backend")

    try:
        subprocess.run([
            "xl", "-vvv", "restore",
            os.path.join(ETC_DIR,
                         "configs/vm-{vm_id}.cfg".format(vm_id=vm_id)),
            os.path.join(LIB_DIR, "volumes/snapshot.sav")
        ],
                       check=True)
    except subprocess.CalledProcessError:
        logging.exception("Failed to restore VM {vm_id}".format(vm_id=vm_id))

        with open("/var/log/xen/qemu-dm-vm-{vm_id}.log".format(vm_id=vm_id),
                  "rb") as f:
            logging.error(f.read())

    subprocess.run([
        "xl", "qemu-monitor-command", "vm-{vm_id}".format(vm_id=vm_id),
        "change ide-5632 /tmp/drakrun/vm-{vm_id}/malwar.iso".format(
            vm_id=vm_id)
    ],
                   check=True)
Ejemplo n.º 7
0
def postinstall(report, generate_usermode):
    if not check_root():
        return

    if os.path.exists(os.path.join(ETC_DIR, "no_usage_reports")):
        report = False

    install_info = InstallInfo.load()
    storage_backend = get_storage_backend(install_info)

    vm0 = VirtualMachine(storage_backend, 0)

    if vm0.is_running is False:
        logging.exception("vm-0 is not running")
        return

    logging.info("Cleaning up leftovers(if any)")
    cleanup_postinstall_files()

    logging.info("Ejecting installation CDs")
    eject_cd("vm-0", FIRST_CDROM_DRIVE)
    if install_info.enable_unattended:
        # If unattended install is enabled, we have an additional CD-ROM drive
        eject_cd("vm-0", SECOND_CDROM_DRIVE)

    kernel_info = vmi_win_guid("vm-0")

    logging.info(f"Determined PDB GUID: {kernel_info.guid}")
    logging.info(f"Determined kernel filename: {kernel_info.filename}")

    logging.info("Fetching PDB file...")
    dest = fetch_pdb(kernel_info.filename,
                     kernel_info.guid,
                     destdir=PROFILE_DIR)

    logging.info("Generating profile out of PDB file...")
    profile = make_pdb_profile(dest)

    logging.info("Saving profile...")
    kernel_profile = os.path.join(PROFILE_DIR, "kernel.json")
    with open(kernel_profile, "w") as f:
        f.write(profile)

    safe_delete(dest)

    vmi_offsets = extract_vmi_offsets("vm-0", kernel_profile)
    explorer_pid = extract_explorer_pid("vm-0", kernel_profile, vmi_offsets)
    runtime_info = RuntimeInfo(vmi_offsets=vmi_offsets,
                               inject_pid=explorer_pid)

    logging.info("Saving runtime profile...")
    with open(os.path.join(PROFILE_DIR, "runtime.json"), "w") as f:
        f.write(runtime_info.to_json(indent=4))

    logging.info("Saving VM snapshot...")

    # Create vm-0 snapshot, and destroy it
    # WARNING: qcow2 snapshot method is a noop. fresh images are created on the fly
    # so we can't keep the vm-0 running
    vm0.save(os.path.join(VOLUME_DIR, "snapshot.sav"))
    logging.info("Snapshot was saved succesfully.")

    # Memory state is frozen, we can't do any writes to persistent storage
    logging.info("Snapshotting persistent memory...")
    storage_backend.snapshot_vm0_volume()

    if report:
        send_usage_report({
            "kernel": {
                "guid": kernel_info.guid,
                "filename": kernel_info.filename,
                "version": kernel_info.version,
            },
            "install_iso": {
                "sha256": install_info.iso_sha256
            },
        })

    if generate_usermode:
        # Restore a VM and create usermode profiles
        create_missing_profiles()

    logging.info("All right, drakrun setup is done.")
    logging.info("First instance of drakrun will be enabled automatically...")
    subprocess.check_output("systemctl enable drakrun@1", shell=True)
    subprocess.check_output("systemctl start drakrun@1", shell=True)

    logging.info("If you want to have more parallel instances, execute:")
    logging.info("  # draksetup scale <number of instances>")
Ejemplo n.º 8
0
def snapshot_import(name, bucket, full, zpool):
    local_install = InstallInfo.try_load()
    if local_install is not None:
        click.confirm("Detected local snapshot. It will be REMOVED. Continue?",
                      abort=True)

    mc = get_minio_client(conf)

    if not mc.bucket_exists(bucket):
        logging.error("Bucket %s doesn't exist", bucket)
        return

    ensure_dirs()

    try:
        if full:
            logging.warning(
                "Importing full snapshot. This may not work if hardware is different"
            )
            do_import_full(mc, name, bucket, zpool)
        else:
            do_import_minimal(mc, name, bucket, zpool)

            # This could probably use some refactoring
            # We're duplicating quite a lot of code from install function
            install_info = InstallInfo.load()
            generate_vm_conf(install_info, 0)
            backend = get_storage_backend(install_info)
            backend.rollback_vm_storage(0)

            net_enable = int(conf["drakrun"].get("net_enable", "0"))
            out_interface = conf["drakrun"].get("out_interface", "")
            dns_server = conf["drakrun"].get("dns_server", "")
            setup_vm_network(
                vm_id=0,
                net_enable=net_enable,
                out_interface=out_interface,
                dns_server=dns_server,
            )

            if net_enable:
                start_dnsmasq(vm_id=0, dns_server=dns_server, background=True)

            cfg_path = os.path.join(VM_CONFIG_DIR, "vm-0.cfg")

            try:
                subprocess.run(["xl" "create", cfg_path], check=True)
            except subprocess.CalledProcessError:
                logging.exception("Failed to launch VM vm-0")
                return

            logging.info(
                "Minimal snapshots require postinstall to work correctly")
            logging.info(
                "Please VNC to the port 5900 to ensure the OS booted correctly"
            )
            logging.info(
                "After that, execute this command to finish the setup")
            logging.info("# draksetup postinstall")
    except NoSuchKey:
        logging.error("Import failed. Missing files in bucket.")
Ejemplo n.º 9
0
def install(storage_backend, disk_size, iso_path, zfs_tank_name,
            unattended_xml):
    logging.info("Ensuring that drakrun@* services are stopped...")
    subprocess.check_output('systemctl stop \'drakrun@*\'',
                            shell=True,
                            stderr=subprocess.STDOUT)

    logging.info("Performing installation...")

    if unattended_xml:
        logging.info("Baking unattended.iso for automated installation")
        with tempfile.TemporaryDirectory() as tmpdir:
            tmp_xml_path = os.path.join(tmpdir, 'autounattend.xml')

            with open(tmp_xml_path, 'wb') as fw:
                with open(unattended_xml, 'rb') as fr:
                    fw.write(fr.read())

            try:
                subprocess.check_output([
                    'genisoimage', '-o',
                    os.path.join(VOLUME_DIR, "unattended.iso"), '-J', '-r',
                    tmp_xml_path
                ],
                                        stderr=subprocess.STDOUT)
            except subprocess.CalledProcessError:
                logging.exception("Failed to generate unattended.iso.")

    sha256_hash = hashlib.sha256()

    with open(iso_path, "rb") as f:
        for byte_block in iter(lambda: f.read(4096), b""):
            sha256_hash.update(byte_block)

        iso_sha256 = sha256_hash.hexdigest()

    install_info = InstallInfo(storage_backend=storage_backend,
                               disk_size=disk_size,
                               iso_path=os.path.abspath(iso_path),
                               zfs_tank_name=zfs_tank_name,
                               enable_unattended=unattended_xml is not None,
                               iso_sha256=iso_sha256)
    install_info.save()

    logging.info("Checking xen-detect...")
    proc = subprocess.run('xen-detect -N', shell=True)

    if proc.returncode != 1:
        logging.error(
            'It looks like the system is not running on Xen. Please reboot your machine into Xen hypervisor.'
        )
        return

    logging.info("Testing if xl tool is sane...")

    try:
        subprocess.check_output('xl info',
                                shell=True,
                                stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError:
        logging.exception("Failed to test xl command.")
        return

    try:
        subprocess.check_output('xl uptime vm-0',
                                shell=True,
                                stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError:
        pass
    else:
        logging.info('Detected that vm-0 is already running, stopping it.')
        subprocess.run('xl destroy vm-0', shell=True, check=True)

    generate_vm_conf(install_info, 0)

    backend = get_storage_backend(install_info)
    backend.initialize_vm0_volume(disk_size)

    try:
        subprocess.check_output("brctl show", shell=True)
    except subprocess.CalledProcessError:
        logging.exception(
            "Failed to execute brctl show. Make sure you have bridge-utils installed."
        )
        return

    net_enable = int(conf['drakrun'].get('net_enable', '0'))
    out_interface = conf['drakrun'].get('out_interface', '')
    dns_server = conf['drakrun'].get('dns_server', '')

    setup_vm_network(vm_id=0,
                     net_enable=net_enable,
                     out_interface=out_interface,
                     dns_server=dns_server)

    if net_enable:
        start_dnsmasq(vm_id=0, dns_server=dns_server, background=True)

    cfg_path = os.path.join(VM_CONFIG_DIR, "vm-0.cfg")

    try:
        subprocess.run('xl create {}'.format(shlex.quote(cfg_path)),
                       shell=True,
                       check=True)
    except subprocess.CalledProcessError:
        logging.exception("Failed to launch VM vm-0")
        return

    logging.info("-" * 80)
    logging.info("Initial VM setup is complete and the vm-0 was launched.")
    logging.info(
        "Please now VNC to the port 5900 on this machine to perform Windows installation."
    )
    logging.info(
        "After you have installed Windows and booted it to the desktop, please execute:"
    )
    logging.info("# draksetup postinstall")

    with open(cfg_path, "r") as f:
        data = f.read()
        m = re.search(r'vncpasswd[ ]*=(.*)', data)
        if m:
            passwd = m.group(1).strip()
            if passwd[0] == '"' and passwd[-1] == '"':
                passwd = passwd[1:-1]

            logging.info("Your configured VNC password is:")
            logging.info(passwd)

    logging.info(
        "Please note that on some machines, system installer may boot for up to 10 minutes"
    )
    logging.info(
        "and may look unresponsive during the process. Please be patient.")
    logging.info("-" * 80)