Exemple #1
0
 def test_vm_name(self, patch):
     logging.info("testing VM names")
     vm = VirtualMachine(None, 0)
     assert vm.vm_name == "vm-0"
     logging.info("testing VM names with new fmt")
     vm = VirtualMachine(None, 0, "test-vm-{}")
     assert vm.vm_name == "test-vm-0"
    def __init__(self, vm_id: int, dns: str):
        install_info = InstallInfo.load()
        backend = get_storage_backend(install_info)

        generate_vm_conf(install_info, vm_id)
        self.vm = VirtualMachine(backend, vm_id)

        with open(Path(PROFILE_DIR) / "runtime.json", 'r') as f:
            self.runtime_info = RuntimeInfo.load(f)
        self.desktop = WinPath(r"%USERPROFILE%") / "Desktop"

        self.kernel_profile = Path(PROFILE_DIR) / "kernel.json"
        self.injector = Injector(
            self.vm.vm_name,
            self.runtime_info,
            self.kernel_profile,
        )
        setup_vm_network(vm_id, True, find_default_interface(), dns)
def memdump_export(bucket, instance):
    install_info = InstallInfo.try_load()
    if install_info is None:
        logging.error(
            "Missing installation info. Did you forget to set up the sandbox?")
        return

    backend = get_storage_backend(install_info)
    vm = VirtualMachine(backend, instance)
    if vm.is_running:
        logging.exception(f"vm-{instance} is running")
        return

    logging.info("Calculating snapshot hash...")
    snapshot_sha256 = file_sha256(os.path.join(VOLUME_DIR, "snapshot.sav"))
    name = f"{snapshot_sha256}_pre_sample.raw_memdump.gz"

    mc = get_minio_client(conf)

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

    try:
        mc.stat_object(bucket, name)
        logging.info("This file already exists in specified bucket")
        return
    except NoSuchKey:
        pass
    except Exception:
        logging.exception("Failed to check if object exists on minio")

    logging.info("Restoring VM and performing memory dump")

    try:
        vm.restore(pause=True)
    except subprocess.CalledProcessError:
        logging.exception(f"Failed to restore VM {vm.vm_name}")
        with open(f"/var/log/xen/qemu-dm-{vm.vm_name}.log", "rb") as f:
            logging.error(f.read())
    logging.info("VM restored")

    with tempfile.NamedTemporaryFile() as compressed_memdump:
        vm.memory_dump(compressed_memdump.name)

        logging.info(f"Uploading {name} to {bucket}")
        mc.fput_object(bucket, name, compressed_memdump.name)

    try:
        vm.destroy()
    except Exception:
        logging.exception("Failed to destroy VM")

    logging.info("Done")
def cleanup():
    if not check_root():
        return

    install_info = InstallInfo.try_load()

    if install_info is None:
        logging.error("The cleanup has been performed")
        return

    stop_all_drakruns()

    backend = get_storage_backend(install_info)
    vm_ids = get_all_vm_conf()

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

    for vm_id in vm_ids:
        vm = VirtualMachine(backend, vm_id)
        vm.destroy()

        delete_vm_network(
            vm_id=vm_id,
            net_enable=net_enable,
            out_interface=out_interface,
            dns_server=dns_server,
        )
        if net_enable:
            stop_dnsmasq(vm_id=vm_id)

        backend.delete_vm_volume(vm_id)

        delete_vm_conf(vm_id)

    safe_delete(os.path.join(VOLUME_DIR, "snapshot.sav"))
    cleanup_postinstall_files()

    InstallInfo.delete()
def create_missing_profiles():
    """
    Creates usermode profiles by restoring vm-1 and extracting the DLLs.
    Assumes that injector is configured properly, i.e. kernel and runtime
    profiles exist and that vm-1 is free to use.
    """

    # Prepare injector
    with open(os.path.join(PROFILE_DIR, "runtime.json"), "r") as runtime_f:
        runtime_info = RuntimeInfo.load(runtime_f)
    kernel_profile = os.path.join(PROFILE_DIR, "kernel.json")
    injector = Injector("vm-1", runtime_info, kernel_profile)

    # restore vm-1
    out_interface = conf["drakrun"].get("out_interface", "")
    dns_server = conf["drakrun"].get("dns_server", "")
    install_info = InstallInfo.load()
    backend = get_storage_backend(install_info)

    generate_vm_conf(install_info, 1)
    setup_vm_network(vm_id=1,
                     net_enable=False,
                     out_interface=out_interface,
                     dns_server=dns_server)
    vm = VirtualMachine(backend, 1)
    vm.restore()

    # Ensure that all declared usermode profiles exist
    # This is important when upgrade defines new entries in dll_file_list and compulsory_dll_file_list
    for profile in compulsory_dll_file_list:
        if not profiles_exist(profile.dest):
            create_rekall_profile(injector, profile, True)

    for profile in dll_file_list:
        if not profiles_exist(profile.dest):
            try:
                create_rekall_profile(injector, profile)
            except Exception:
                log.exception(
                    "Unexpected exception from create_rekall_profile!")

    build_os_info(APISCOUT_PROFILE_DIR, vmi_win_guid(vm.vm_name), backend)

    dll_basename_list = [dll.dest for dll in dll_file_list]
    static_apiscout_profile = build_static_apiscout_profile(
        APISCOUT_PROFILE_DIR, dll_basename_list)
    with open(
            Path(APISCOUT_PROFILE_DIR) / "static_apiscout_profile.json",
            "w") as f:
        json.dump(static_apiscout_profile, f)

    vm.destroy()
    delete_vm_network(vm_id=1,
                      net_enable=False,
                      out_interface=out_interface,
                      dns_server=dns_server)
Exemple #6
0
    def test_vm_create_and_is_running(self, config, test_vm):

        # initial cleanup
        destroy_vm(test_vm.vm_name)
        assert test_vm.is_running is False

        logging.info("testing vm create with pause=False")
        test_vm.create(pause=False)
        assert get_vm_state(test_vm.vm_name) != "p"
        assert test_vm.is_running is True

        logging.info("testing vm create for a created VM")
        with pytest.raises(Exception):
            test_vm.create(pause=True)

        # second run
        destroy_vm(test_vm.vm_name)

        logging.info("testing vm create with pause=True")
        test_vm.create(pause=True)
        assert get_vm_state(test_vm.vm_name) == "p"

        # destroy the vm
        destroy_vm(test_vm.vm_name)

        logging.info("testing vm create with non-existant file")
        with pytest.raises(Exception):
            new_vm = VirtualMachine(None, 0, "test-hvm64-example",
                                    "/tmp/unexitant-file")
            new_vm.create()

        logging.info("testing vm create with empty file")
        with tempfile.NamedTemporaryFile() as tempf:
            with pytest.raises(Exception):
                new_vm = VirtualMachine(None, 0, "test-hvm64-example",
                                        tempf.name)
                new_vm.create()

        # check if vm is shutdown
        with pytest.raises(Exception):
            get_vm_state(test_vm.name)
def postupgrade():
    if not check_root():
        return

    with open(os.path.join(ETC_DIR, 'scripts/cfg.template'), 'r') as f:
        template = f.read()

    passwd_characters = string.ascii_letters + string.digits
    passwd = ''.join(secrets.choice(passwd_characters) for _ in range(20))
    template = template.replace('{{ VNC_PASS }}', passwd)

    with open(os.path.join(ETC_DIR, 'scripts', 'cfg.template'), 'w') as f:
        f.write(template)

    detect_defaults()

    install_info = InstallInfo.try_load()
    if not install_info:
        logging.info("Postupgrade done. DRAKVUF Sandbox not installed.")
        return

    # Prepare injector
    with open(os.path.join(PROFILE_DIR, "runtime.json"), 'r') as runtime_f:
        runtime_info = RuntimeInfo.load(runtime_f)
    kernel_profile = os.path.join(PROFILE_DIR, "kernel.json")
    injector = Injector('vm-1', runtime_info, kernel_profile)

    stop_all_drakruns()

    # Use vm-1 for generating profiles
    out_interface = conf['drakrun'].get('out_interface', '')
    dns_server = conf['drakrun'].get('dns_server', '')
    setup_vm_network(vm_id=1,
                     net_enable=False,
                     out_interface=out_interface,
                     dns_server=dns_server)
    backend = get_storage_backend(install_info)
    vm = VirtualMachine(backend, 1)
    vm.restore()

    create_missing_profiles(injector)

    vm.destroy()
    delete_vm_network(vm_id=1,
                      net_enable=False,
                      out_interface=out_interface,
                      dns_server=dns_server)
    start_enabled_drakruns()
Exemple #8
0
def create_missing_profiles():
    """
    Creates usermode profiles by restoring vm-1 and extracting the DLLs.
    Assumes that injector is configured properly, i.e. kernel and runtime
    profiles exist and that vm-1 is free to use.
    """

    # Prepare injector
    with open(os.path.join(PROFILE_DIR, "runtime.json"), "r") as runtime_f:
        runtime_info = RuntimeInfo.load(runtime_f)
    kernel_profile = os.path.join(PROFILE_DIR, "kernel.json")
    injector = Injector("vm-1", runtime_info, kernel_profile)

    # restore vm-1
    out_interface = conf["drakrun"].get("out_interface", "")
    dns_server = conf["drakrun"].get("dns_server", "")
    install_info = InstallInfo.load()
    backend = get_storage_backend(install_info)

    generate_vm_conf(install_info, 1)
    setup_vm_network(vm_id=1,
                     net_enable=False,
                     out_interface=out_interface,
                     dns_server=dns_server)
    vm = VirtualMachine(backend, 1)
    vm.restore()

    # Ensure that all declared usermode profiles exist
    # This is important when upgrade defines new entries in dll_file_list and compulsory_dll_file_list
    for profile in compulsory_dll_file_list:
        if not profile_exists(profile):
            create_rekall_profile(injector, profile, True)

    for profile in dll_file_list:
        if not profile_exists(profile):
            try:
                create_rekall_profile(injector, profile)
            except Exception:
                # silence per-dll errors
                pass

    vm.destroy()
    delete_vm_network(vm_id=1,
                      net_enable=False,
                      out_interface=out_interface,
                      dns_server=dns_server)
Exemple #9
0
    def run_vm(self):
        backend = get_storage_backend(self.install_info)
        vm = VirtualMachine(backend, self.instance_id)

        try:
            vm.restore()
        except subprocess.CalledProcessError:
            self.log.exception(f"Failed to restore VM {self.vm_name}")
            with open(f"/var/log/xen/qemu-dm-{self.vm_name}.log", "rb") as f:
                logging.error(f.read())

        self.log.info("VM restored")

        try:
            yield vm
        finally:
            try:
                vm.destroy()
            except Exception:
                self.log.exception("Failed to destroy VM")
Exemple #10
0
def test_vm(patch, config):
    test_vm = VirtualMachine(None, 0, "test-hvm64-example", config)

    yield test_vm
Exemple #11
0
class DrakmonShell:
    def __init__(self, vm_id: int, dns: str):

        self.cleanup(vm_id)

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

        generate_vm_conf(install_info, vm_id)
        self.vm = VirtualMachine(backend, vm_id)
        self._dns = dns

        with open(Path(PROFILE_DIR) / "runtime.json", "r") as f:
            self.runtime_info = RuntimeInfo.load(f)
        self.desktop = WinPath(r"%USERPROFILE%") / "Desktop"

        self.kernel_profile = Path(PROFILE_DIR) / "kernel.json"
        self.injector = Injector(
            self.vm.vm_name,
            self.runtime_info,
            self.kernel_profile,
        )
        setup_vm_network(vm_id, True, find_default_interface(), dns)

    def cleanup(self, vm_id: int):

        logging.info(f"Ensuring that drakrun@{vm_id} service is stopped...")
        try:
            subprocess.run(
                ["systemctl", "stop", f"drakrun@{vm_id}"],
                stderr=subprocess.STDOUT,
                check=True,
            )
        except subprocess.CalledProcessError:
            raise Exception(f"drakrun@{vm_id} not stopped")

    def drakvuf(self, plugins, timeout=60):
        d = tempfile.TemporaryDirectory(prefix="drakvuf_")
        workdir = Path(d.name)

        log = open(workdir / "drakmon.log", "wb")

        cmd = ["drakvuf"]
        cmd.extend([
            "-o",
            "json",
            "F",
            "-j",
            "5",
            "-t",
            str(timeout),
            "-i",
            str(self.runtime_info.inject_pid),
            "-k",
            str(self.runtime_info.vmi_offsets.kpgd),
            "-r",
            self.kernel_profile,
            "-d",
            self.vm.vm_name,
            "--dll-hooks-list",
            Path(ETC_DIR) / "hooks.txt",
        ])

        if "memdump" in plugins:
            dumps = workdir / "dumps"
            dumps.mkdir()
            cmd.extend(["--memdump-dir", dumps])

        if "ipt" in plugins:
            ipt = workdir / "ipt"
            ipt.mkdir()
            cmd.extend(["--ipt-dir", ipt])

        for chk in (["-a", plugin] for plugin in plugins):
            cmd.extend(chk)

        subprocess.run(cmd, stdout=log)

        return d

    def help(self):
        usage = dedent("""\
        Available commands:
        - copy(file_path)   # copy file onto vm desktop
        - mount(iso_path)   # mount iso, useful for installing software, e.g. office
        - drakvuf(plugins)  # start drakvuf with provided set of plugins
        - run(cmd)          # run command inside vm
        - exit()            # exit playground
        """)
        print(usage)

    def copy(self, local):
        local = Path(local)
        self.injector.write_file(local, self.desktop / local.name)

    def mount(self, local_iso_path, drive=FIRST_CDROM_DRIVE):
        local_iso_path = Path(local_iso_path)
        insert_cd(self.vm.vm_name, drive, local_iso_path)

    def run(self, cmd):
        self.injector.create_process(cmd)

    def __enter__(self):
        self.vm.restore()
        return self

    def __exit__(self, exc_type, exc_value, exc_traceback):
        self.vm.destroy()
        delete_vm_network(self.vm.vm_id, True, find_default_interface(),
                          self._dns)
Exemple #12
0
class DrakmonShell:
    def __init__(self, vm_id: int, dns: str):

        self.cleanup(vm_id)

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

        generate_vm_conf(install_info, vm_id)
        self.vm = VirtualMachine(backend, vm_id)
        self._dns = dns

        with open(Path(PROFILE_DIR) / "runtime.json", 'r') as f:
            self.runtime_info = RuntimeInfo.load(f)
        self.desktop = WinPath(r"%USERPROFILE%") / "Desktop"

        self.kernel_profile = Path(PROFILE_DIR) / "kernel.json"
        self.injector = Injector(
            self.vm.vm_name,
            self.runtime_info,
            self.kernel_profile,
        )
        setup_vm_network(vm_id, True, find_default_interface(), dns)

    def cleanup(self, vm_id: int):

        logging.info(f"Ensuring that drakrun@{vm_id} service is stopped...")
        try:
            subprocess.run(['systemctl', 'stop', f'drakrun@{vm_id}'],
                           stderr=subprocess.STDOUT,
                           check=True)
        except subprocess.CalledProcessError:
            raise Exception(f"drakrun@{vm_id} not stopped")

    def drakvuf(self, plugins, timeout=60):
        d = tempfile.TemporaryDirectory(prefix="drakvuf_")
        workdir = Path(d.name)

        log = open(workdir / "drakmon.log", "wb")

        cmd = ["drakvuf"]
        cmd.extend([
            "-o",
            "json",
            "F",
            "-j",
            "5",
            "-t",
            str(timeout),
            "-i",
            str(self.runtime_info.inject_pid),
            "-k",
            str(self.runtime_info.vmi_offsets.kpgd),
            "-r",
            self.kernel_profile,
            "-d",
            self.vm.vm_name,
            "--dll-hooks-list",
            Path(ETC_DIR) / "hooks.txt",
        ])

        if "memdump" in plugins:
            dumps = workdir / "dumps"
            dumps.mkdir()
            cmd.extend(["--memdump-dir", dumps])

        if "ipt" in plugins:
            ipt = workdir / "ipt"
            ipt.mkdir()
            cmd.extend(["--ipt-dir", ipt])

        for chk in (["-a", plugin] for plugin in plugins):
            cmd.extend(chk)

        subprocess.run(cmd, stdout=log)

        return d

    def copy(self, local):
        local = Path(local)
        self.injector.write_file(local, self.desktop / local.name)

    def run(self, cmd):
        self.injector.create_process(cmd)

    def __enter__(self):
        self.vm.restore()
        return self

    def __exit__(self, exc_type, exc_value, exc_traceback):
        self.vm.destroy()
        delete_vm_network(self.vm.vm_id, True, find_default_interface(),
                          self._dns)
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)

    vm = VirtualMachine(storage_backend, 0)

    if vm.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)

    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)

    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...")

    # snapshot domain but don't destroy it, leave it in paused state
    subprocess.check_output('xl save -p vm-0 ' +
                            os.path.join(VOLUME_DIR, "snapshot.sav"),
                            shell=True)
    logging.info("Snapshot was saved succesfully.")

    logging.info("Snapshotting persistent memory...")
    storage_backend.snapshot_vm0_volume()

    logging.info("Unpausing VM")
    subprocess.check_output('xl unpause vm-0', shell=True)

    injector = Injector('vm-0', runtime_info, kernel_profile)
    if generate_usermode:
        try:
            for file in dll_file_list:
                create_rekall_profile(injector, file)
        except RuntimeError as e:
            logging.warning("Generating usermode profiles failed")
            logging.exception(e)

    subprocess.check_output('xl destroy vm-0', shell=True)

    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>")
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>")
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()

    backend = get_storage_backend(install_info)

    vm0 = VirtualMachine(backend, 0)
    vm0.destroy()

    generate_vm_conf(install_info, 0)

    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")

    vm0.create()

    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)
def perform_xtf():
    logging.info("Testing your Xen installation...")
    module_dir = os.path.dirname(os.path.realpath(__file__))
    cfg_path = os.path.join(module_dir, "tools", "test-hvm64-example.cfg")
    firmware_path = os.path.join(module_dir, "tools", "test-hvm64-example")

    with open(cfg_path, "r") as f:
        test_cfg = (f.read().replace("{{ FIRMWARE_PATH }}",
                                     firmware_path).encode("utf-8"))

    with tempfile.NamedTemporaryFile() as tmpf:
        tmpf.write(test_cfg)
        tmpf.flush()

        test_hvm64 = VirtualMachine(None, None, "test-hvm64-example",
                                    tmpf.name)
        logging.info("Checking if the test domain already exists...")
        test_hvm64.destroy()

        logging.info("Creating new test domain...")
        test_hvm64.create(pause=True, timeout=30)

        module_dir = os.path.dirname(os.path.realpath(__file__))
        test_altp2m_tool = os.path.join(module_dir, "tools", "test-altp2m")

        logging.info("Testing altp2m feature...")
        try:
            subprocess.run(
                [test_altp2m_tool, "test-hvm64-example"],
                stderr=subprocess.STDOUT,
                check=True,
            )
        except subprocess.CalledProcessError as e:
            output = e.output.decode("utf-8", "replace")
            logging.error(
                f"Failed to enable altp2m on domain. Your hardware might not support Extended Page Tables. Logs:\n{output}"
            )
            test_hvm64.destroy()
            return False

        logging.info("Performing simple XTF test...")
        p = subprocess.Popen(
            ["xl", "console", "test-hvm64-example"],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
        test_hvm64.unpause(timeout=30)
        stdout_b, _ = p.communicate(timeout=10)

        stdout_text = stdout_b.decode("utf-8")
        stdout = [line.strip() for line in stdout_text.split("\n")]

        for line in stdout:
            if line == "Test result: SUCCESS":
                logging.info(
                    "All tests passed. Your Xen installation seems to work properly."
                )
                return True

    logging.error(
        f"Preflight check with Xen Test Framework doesn't pass. Your hardware might not support VT-x. Logs: \n{stdout_text}"
    )
    return False