示例#1
0
    def _create_complete_kernel(
        self,
        location: Location,
        system_context: SystemContext,
        base_cmdline: str,
        squashfs_device: str,
        verity_device: str,
        root_hash: str,
        target_directory: str,
    ) -> str:
        full_cmdline = _setup_kernel_commandline(
            base_cmdline, squashfs_device, verity_device, root_hash
        )
        kernel_name = _kernel_name(system_context)

        self._create_efi_kernel(location, system_context, kernel_name, full_cmdline)

        if self._key and self._cert:
            debug("Signing EFI kernel.")
            self._sign_efi_kernel(
                location, system_context, kernel_name, self._key, self._cert
            )

        kernel_filename = os.path.join(target_directory, os.path.basename(kernel_name))
        shutil.copyfile(kernel_name, kernel_filename)

        return kernel_filename
示例#2
0
    def _create_complete_kernel(
        self,
        location: Location,
        system_context: SystemContext,
        cmdline: str,
        *,
        kernel_file: str,
        efi_key: str,
        efi_cert: str,
    ):
        self._create_efi_kernel(
            location,
            system_context,
            cmdline,
            kernel_file=kernel_file,
        )

        if efi_key and efi_cert:
            debug("Signing EFI kernel.")
            location.set_description("Sign EFI kernel")
            self._execute(
                location.next_line(),
                system_context,
                "sign_efi_binary",
                kernel_file,
                key=efi_key,
                cert=efi_cert,
                outside=True,
                keep_unsigned=False,
            )

        trace(f"Validating existence of {kernel_file}.")
        assert os.path.isfile(kernel_file)
示例#3
0
def run(
        *args: str,
        work_directory: str = "",
        check: bool = True,
        env: typing.Any = os.environ,  ## What is a better type for this?
) -> subprocess.CompletedProcess:
    env["LC_ALL"] = "en_US.UTF-8"

    cwd = work_directory or None
    trace('Running: "{}"...'.format('" "'.join(args)))
    result = subprocess.run(
        args,
        env=env,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        check=False,
        cwd=cwd,
    )
    if result.returncode != 0:
        debug(
            f'Command returned with exit code {result.returncode}:\nSTDOUT:\n{result.stdout.decode("utf-8")}\nSTDERR:\n{result.stderr.decode("utf-8")}.'
        )
    if result.returncode == 2 and check:
        raise subprocess.CalledProcessError(
            returncode=result.returncode,
            cmd=args,
            output=result.stdout,
            stderr=result.stderr,
        )
    return result
示例#4
0
def _copy_efi(src: str, dest: str) -> int:
    try:
        efi_path = os.path.join(dest, "EFI")
        os.makedirs(efi_path, exist_ok=True)

        trace("Copying bootloader.")

        efi_src_path = os.path.join(src, "EFI")

        dirs = [
            d for d in os.listdir(efi_src_path)
            if os.path.isdir(os.path.join(efi_src_path, d))
        ]
        for d in dirs:
            dest = os.path.join(efi_path, d)
            trace(f"Copying {os.path.join(efi_src_path, d)} to {dest}")
            copytree(
                os.path.join(efi_src_path, d),
                dest,
                dirs_exist_ok=True,
            )

        copytree(
            os.path.join(src, "loader"),
            os.path.join(dest, "loader"),
            dirs_exist_ok=True,
        )

    except Exception as e:
        debug(f"Failed to install EFI: {e}.")
        return 1
    else:
        debug("Successfully installed EFI")
        return 0
示例#5
0
def run(*args,
        work_directory: str = "",
        check: bool = True,
        env: typing.Dict[str,
                         str] = os.environ) -> subprocess.CompletedProcess:
    env["LC_ALL"] = "en_US.UTF-8"

    cwd = work_directory or None
    trace('Running borg: "{}"...'.format('" "'.join(args)))
    result = subprocess.run(args,
                            env=env,
                            capture_output=True,
                            check=False,
                            cwd=cwd)
    if result.returncode != 0:
        debug("Borg returned with exit code {}:\nSTDOUT:\n{}\nSTDERR:\n{}.".
              format(result.returncode, result.stdout, result.stderr))
    if result.returncode == 2 and check:
        raise subprocess.CalledProcessError(
            returncode=result.returncode,
            cmd=args,
            output=result.stdout,
            stderr=result.stderr,
        )

    return result
示例#6
0
    def __call__(self, location: Location, system_context: SystemContext,
                 *args: typing.Any, **kwargs: typing.Any) -> None:
        """Execute command."""
        self._setup(*args, **kwargs)

        h2('Exporting system "{}".'.format(system_context.system_name))
        debug('Running Hooks.')
        self._run_all_exportcommand_hooks(system_context)

        verbose('Preparing system for export.')
        self.prepare_for_export(location, system_context)

        info('Validating installation for export.')
        if not self._skip_validation:
            _validate_installation(location.next_line(), system_context)

        export_directory \
            = self.create_export_directory(system_context)
        assert export_directory

        system_context.set_substitution('EXPORT_DIRECTORY', export_directory)

        verbose('Exporting all data in {}.'.format(export_directory))
        self._execute(location.next_line(),
                      system_context,
                      '_export_directory',
                      export_directory,
                      compression=self._repository_compression,
                      compression_level=self._repository_compression_level,
                      repository=self._repository)

        info('Cleaning up export location.')
        self.delete_export_directory(export_directory)
示例#7
0
def _copy_file(src: str, dest: str, overwrite: bool):
    file = os.path.basename(src)
    if not os.path.exists(os.path.join(dest, file)) or overwrite:
        marker = " [FORCE]" if overwrite else ""
        debug(f"Copying {src} into {dest}{marker}.")
        copy2(src, dest)
    else:
        debug(f"Skipped copy of {src} into {dest}.")
    def __call__(
        self,
        location: Location,
        system_context: SystemContext,
        *args: typing.Any,
        **kwargs: typing.Any,
    ) -> None:
        """Execute command."""
        if not os.path.exists(
                os.path.join(system_context.boot_directory, "vmlinuz")):
            info("Skipping initrd generation: No vmlinuz in boot directory.")
            return

        root_hash = kwargs.get("root_hash", "")
        vg = system_context.substitution_expanded("DEFAULT_VG", None)
        image_fs = system_context.substitution_expanded("IMAGE_FS", None)
        image_device = _device_ify(
            system_context.substitution_expanded("IMAGE_DEVICE", None))
        image_options = system_context.substitution_expanded(
            "IMAGE_OPTIONS", "")
        image_name = system_context.substitution_expanded(
            "CLRM_IMAGE_FILENAME", "")

        initrd = args[0]

        staging_area = os.path.join(system_context.cache_directory,
                                    "clrm_extra")
        os.makedirs(staging_area)

        modules = [
            *system_context.substitution_expanded("INITRD_EXTRA_MODULES",
                                                  "").split(","),
            "squashfs",
            *_install_image_file_support(staging_area, image_fs, image_device,
                                         image_options, image_name),
            *_install_lvm_support(staging_area, vg, image_name),
            *_install_sysroot_setup_support(staging_area),
            *_install_verity_support(staging_area, system_context, root_hash),
            *_install_volatile_support(staging_area, system_context),
            *_install_var_mount_support(staging_area, system_context),
            *_install_etc_shadow(staging_area, system_context),
        ]
        modules = [m for m in modules
                   if m]  # Trim empty modules (e.g. added by the substitution)
        system_context.set_or_append_substitution("INITRD_EXTRA_MODULES",
                                                  ",".join(modules))
        debug(
            f'INITRD_EXTRA_MODULES is now {system_context.substitution("INITRD_EXTRA_MODULES", "")}.'
        )

        # Create Initrd:
        run(
            "/bin/sh",
            "-c",
            f'cd "{staging_area}" ; "{self._binary(Binaries.FIND)}" . | "{self._binary(Binaries.CPIO)}" -o -H newc > "{initrd}"',
        )

        assert os.path.exists(initrd)
示例#9
0
    def __call__(self, location: Location, system_context: SystemContext,
                 *args: typing.Any, **kwargs: typing.Any) -> None:
        """Execute command."""

        if system_context.substitution("ROOT_DEVICE") is None:
            GenerateError("ROOT_DEVICE must be set when creating EFI kernel.",
                          location=location)

        output = args[0]
        kernel = kwargs.get("kernel", "")
        initrd_directory = kwargs.get(
            "initrd",
            os.path.join(system_context.boot_directory, "initrd-parts"))
        initrd_files = _get_initrd_parts(location, initrd_directory)
        cmdline_input = kwargs.get("commandline", "")
        osrelease_file = system_context.file_name("/usr/lib/os-release")
        efistub = system_context.file_name("/usr/lib/systemd/boot/efi/"
                                           "linuxx64.efi.stub")

        debug("{}: Kernel   : {}.".format(self.name, kernel))
        debug("{}: Initrd   : {}.".format(self.name, ", ".join(initrd_files)))
        debug("{}: cmdline  : {}.".format(self.name, cmdline_input))
        debug("{}: osrelease: {}.".format(self.name, osrelease_file))
        debug("{}: efistub  : {}.".format(self.name, efistub))

        self._validate_files(kernel, *initrd_files, osrelease_file, efistub)
        with tempfile.TemporaryDirectory() as tmp:
            initrd = _create_initrd(tmp, *initrd_files)
            cmdline = _create_cmdline_file(tmp, cmdline_input)

            run(
                self._binary(Binaries.OBJCOPY),
                "--add-section",
                ".osrel={}".format(osrelease_file),
                "--change-section-vma",
                ".osrel=0x20000",
                "--add-section",
                ".cmdline={}".format(cmdline),
                "--change-section-vma",
                ".cmdline=0x30000",
                "--add-section",
                ".linux={}".format(kernel),
                "--change-section-vma",
                ".linux=0x40000",
                "--add-section",
                ".initrd={}".format(initrd),
                "--change-section-vma",
                ".initrd=0x3000000",
                efistub,
                output,
            )

            os.remove(initrd)
            os.remove(cmdline)
示例#10
0
    def __call__(
        self,
        location: Location,
        system_context: SystemContext,
        *args: typing.Any,
        **kwargs: typing.Any,
    ) -> None:
        """Execute command."""
        output = args[0]
        kernel = kwargs.get("kernel", "")
        initrd_directory = kwargs.get(
            "initrd", os.path.join(system_context.boot_directory, "initrd-parts")
        )
        initrd_files = _get_initrd_parts(location, initrd_directory)
        cmdline_input = kwargs.get("commandline", "")
        osrelease_file = system_context.file_name("/usr/lib/os-release")
        efistub = system_context.file_name(
            "/usr/lib/systemd/boot/efi/" "linuxx64.efi.stub"
        )

        debug(f"{self.name}: Kernel   : {kernel}.")
        debug(f"{self.name}: Initrd   : {initrd_files}.")
        debug(f"{self.name}: cmdline  : {cmdline_input}.")
        debug(f"{self.name}: osrelease: {osrelease_file}.")
        debug(f"{self.name}: efistub  : {efistub}.")

        self._validate_files(location, kernel, *initrd_files, osrelease_file, efistub)

        initrd = _create_initrd(system_context.boot_directory, *initrd_files)
        cmdline = _create_cmdline_file(system_context.boot_directory, cmdline_input)

        run(
            self._binary(Binaries.OBJCOPY),
            "--add-section",
            f".osrel={osrelease_file}",
            "--change-section-vma",
            ".osrel=0x20000",
            "--add-section",
            f".cmdline={cmdline}",
            "--change-section-vma",
            ".cmdline=0x30000",
            "--add-section",
            f".linux={kernel}",
            "--change-section-vma",
            ".linux=0x40000",
            "--add-section",
            f".initrd={initrd}",
            "--change-section-vma",
            ".initrd=0x3000000",
            efistub,
            output,
        )
示例#11
0
    def __call__(self, location: Location, system_context: SystemContext,
                 *args: typing.Any, **kwargs: typing.Any) -> None:
        """Execute command."""
        user_name = args[0]
        key_file = args[1]

        user = UserHelper.user_data(user_name,
                                    root_directory=system_context.fs_directory)
        if user is None:
            raise GenerateError(
                '"{}" could not find user "{}".'.format(self.name, user_name),
                location=location,
            )

        debug('Installing "{}" to user "{}" ({}).'.format(
            key_file, user_name, user.home))

        self._check_or_create_directory(
            location,
            system_context,
            user.home,
            mode=0o750,
            user=user.uid,
            group=user.gid,
        )
        ssh_directory = os.path.join(user.home, ".ssh")
        self._check_or_create_directory(
            location,
            system_context,
            ssh_directory,
            mode=0o600,
            user=user.uid,
            group=user.gid,
        )

        installed_key_file = os.path.join(ssh_directory,
                                          os.path.basename(key_file))

        self._execute(
            location.next_line(),
            system_context,
            "copy",
            key_file,
            installed_key_file,
            from_outside=True,
        )
        trace("Copied key.")
        chown(system_context, user.uid, user.gid, installed_key_file)
        trace("Ownership adjusted.")
        chmod(system_context, 0o600, installed_key_file)
        trace("Mode adjusted.")
示例#12
0
def _create_initrd(directory: str, *files: str) -> str:
    target = os.path.join(directory, "initrd")
    with open(target, "wb") as target_file:
        for f in files:
            with open(f, "rb") as source_file:
                target_file.write(source_file.read())

    # compress the entire initrd:
    debug("Compressing initrd with gzip")
    run("gzip", "-9", target)
    # os.remove(target) ## Gzip kills the uncompressed file!
    os.rename(f"{target}.gz", target)

    return target
示例#13
0
    def __call__(self, location: Location, system_context: SystemContext,
                 *args: typing.Any, **kwargs: typing.Any) -> None:
        """Execute command."""
        self._run_hooks(system_context, '_teardown')
        self._run_hooks(system_context, 'testing')

        system_context.pickle()

        self._execute(location, system_context, '_store')

        debug('Cleaning up everything in "{}".'.format(
            system_context.scratch_directory))
        self._service('btrfs_helper').delete_subvolume_recursive(
            system_context.scratch_directory)
def _copy_modules(
    system_context: SystemContext,
    modules: typing.List[str],
    modules_dir: str,
    etc_dir: str,
    *,
    kernel_version: str,
):
    src_top = os.path.join(system_context.file_name("/usr/lib/modules"),
                           kernel_version)
    target_top = os.path.join(modules_dir, kernel_version)

    known_modules = _kernel_module_map(src_top)

    to_load: typing.List[str] = []
    to_install: typing.Set[str] = set()

    debug(f'Installing modules from "{src_top}" into "{target_top}"')
    for module in modules:
        m = module.replace("_", "-")
        if m not in known_modules:
            info(f"Module {m} not found. Was it built into the kernel?")
            continue
        p = known_modules[m]
        if p not in to_install:
            to_install.add(p)
            to_load.append(m)

    _write_modules_load_file(etc_dir, to_load)

    if not to_install:
        return

    # Install dependencies:
    dependencies = _parse_module_dependencies(src_top)

    to_test = to_install
    for t in to_test:
        to_install = to_install.union(_module_dependencies(t, dependencies))

    for i in to_install:
        os.makedirs(os.path.dirname(os.path.join(target_top, i)),
                    exist_ok=True)
        trace(f"Copying module {i}.")
        trace(
            f'    "{os.path.join(src_top, i)}" => "{os.path.join(target_top, i)}".'
        )
        shutil.copy2(os.path.join(src_top, i), os.path.join(target_top, i))
示例#15
0
    def _fix_dracut_modules(
        self, location: Location, system_context: SystemContext, extra: str = "",
    ):
        extra = extra.replace(",", " ")
        extra = extra.replace("  ", " ")
        extra = extra.strip()

        if extra:
            debug(f'Changing MODULES to "{extra}"')
            self._execute(
                location.next_line(),
                system_context,
                "sed",
                f"/^MODULES=/ cMODULES=({extra})",
                "/etc/mkinitcpio.conf",
            )
示例#16
0
def _find_tests(system_context: SystemContext) -> typing.Generator[str, None, None]:
    """Find tests to run."""
    tests_directory = system_context.system_tests_directory
    debug('Searching for tests in "{}".'.format(tests_directory))

    for f in sorted(os.listdir(tests_directory)):
        test = os.path.join(tests_directory, f)
        if not os.path.isfile(test):
            trace('"{}": Not a file, skipping.'.format(test))
            continue
        if not os.access(test, os.X_OK):
            trace('"{}": Not executable, skipping.'.format(test))
            continue

        info('Found test: "{}"'.format(test))
        yield test
示例#17
0
def create_qemu_image(
    image_path: str,
    *,
    image_size: int,
    image_format: str = "qcow2",
    system_image_file: str,
    tmp_dir: str,
) -> str:
    trace(f"Creating image file {image_path}.")
    with disk.NbdDevice.new_image_file(image_path,
                                       image_size,
                                       disk_format=image_format) as device:
        _create_hdd_image(device)

        debug("mounting data partition for further setup.")
        with mount.Mount(
                device.device(3),
                os.path.join(tmp_dir, "data"),
                fs_type="btrfs",
                options="subvolid=0",
                fallback_cwd=os.getcwd(),
        ) as data_dir:
            _setup_btrfs(data_dir)

            trace("Copying image file")
            copyfile(
                system_image_file,
                os.path.join(data_dir, ".images",
                             os.path.basename(system_image_file)),
            )

            with mount.Mount(
                    device.device(1),
                    os.path.join(tmp_dir, "efi_dest"),
                    options="defaults",
                    fs_type="vfat",
            ) as efi_dest_mnt:
                tool.execute_with_system_mounted(
                    lambda e, _: _copy_efi(
                        e,
                        efi_dest_mnt,
                    ),
                    image_file=system_image_file,
                    tmp_dir=tmp_dir,
                )

    return image_path
示例#18
0
def create_qemu_image(image_path: str,
                      *,
                      image_size: str,
                      image_format: str = "qcow2",
                      system_name: str,
                      system_version: str = "",
                      repository: str,
                      tempdir: str) -> str:
    trace("Creating image file {}.".format(image_path))
    with disk.NbdDevice.new_image_file(image_path,
                                       image_size,
                                       disk_format=image_format) as device:
        _create_hdd_image(device)

        debug("mounting data partition for further setup.")
        with mount.Mount(
                device.device(3),
                os.path.join(tempdir, "data"),
                fs_type="btrfs",
                options="subvolid=0",
                fallback_cwd=os.getcwd(),
        ) as data_dir:
            _setup_btrfs(data_dir)

            extract_location = os.path.join(data_dir, ".images")
            verbose("Extracting system image to {}.".format(extract_location))
            extracted_version = tool.write_image(
                system_name,
                extract_location,
                repository=repository,
                version=system_version,
            )

            extracted_image = os.path.join(data_dir, ".images",
                                           "clrm_{}".format(extracted_version))
            assert os.path.isfile(extracted_image)

            tool.copy_efi_partition(
                image_file=extracted_image,
                efi_device=device.device(1),
                tempdir=tempdir,
                kernel_only=False,
            )

    return image_path
示例#19
0
    def __call__(self, location: Location, system_context: SystemContext,
                 *args: typing.Any, **kwargs: typing.Any) -> None:
        """Execute command."""
        h2('Running tests for system "{}"'
           .format(system_context.system_name),
           verbosity=2)
        env = _environment(system_context)

        for test in _find_tests(system_context):
            debug('Running test {}...'.format(test))
            test_result = run(test, system_context.system_name,
                              env=env, returncode=None,
                              work_directory=system_context.fs_directory)
            if test_result.returncode == 0:
                success('Test "{}"'.format(test), verbosity=3)
            else:
                report_completed_process(msg, test_result)
                fail('Test "{}"'.format(test))
示例#20
0
def _parse_commandline(
        *args: str, install_targets: typing.List[InstallTarget]) -> typing.Any:
    """Parse the command line options."""
    parser = ArgumentParser(description="Cleanroom OS image fire starter",
                            prog=args[0])

    parser.add_argument("--verbose",
                        action="count",
                        default=0,
                        help="Be verbose")

    parser.add_argument(
        "--repository",
        dest="repository",
        type=str,
        action="store",
        required=True,
        help="The repository of systems to work with.",
    )

    parser.add_argument(dest="system_name",
                        metavar="<system>",
                        type=str,
                        help="system to install")
    parser.add_argument(
        "--system-version",
        dest="system_version",
        default="",
        type=str,
        help="version of system to install.",
    )

    subparsers = parser.add_subparsers(
        help="Installation target specifics",
        dest="subcommand",
        required=True,
    )
    for it in install_targets:
        debug(
            f'Setting up subparser for "{it.name}" with help "{it.help_string}".'
        )
        it.setup_subparser(subparsers.add_parser(it.name, help=it.help_string))

    return parser.parse_args(args[1:])
示例#21
0
    def __call__(
        self,
        location: Location,
        system_context: SystemContext,
        *args: typing.Any,
        **kwargs: typing.Any,
    ) -> None:
        """Execute command."""
        self._run_hooks(system_context, "_teardown")
        self._run_hooks(system_context, "testing")

        system_context.pickle()

        self._execute(location, system_context, "_store")

        debug(f'Cleaning up everything in "{system_context.scratch_directory}".')
        self._service("btrfs_helper").delete_subvolume_recursive(
            system_context.scratch_directory
        )
 def __call__(self, location: Location, system_context: SystemContext,
              *args: typing.Any, **kwargs: typing.Any) -> None:
     """Execute command."""
     location.set_description("Strip documentation files")
     to_remove = [
         "/usr/share/doc/*", "/usr/share/gtk-doc/html", "/usr/share/help/*"
     ]
     if not os.path.exists(system_context.file_name("/usr/bin/man")):
         debug("No /usr/bin/man: Removing man pages.")
         to_remove += ["/usr/share/man/*"]
     if not os.path.exists(system_context.file_name("/usr/bin/info")):
         debug("No /usr/bin/info: Removing info pages.")
         to_remove += ["/usr/share/info/*"]
     self._execute(location,
                   system_context,
                   "remove",
                   *to_remove,
                   recursive=True,
                   force=True)
 def __call__(self, location: Location, system_context: SystemContext,
              *args: typing.Any, **kwargs: typing.Any) -> None:
     """Execute command."""
     location.set_description('Strip documentation files')
     to_remove = [
         '/usr/share/doc/*', '/usr/share/gtk-doc/html', '/usr/share/help/*'
     ]
     if not os.path.exists(system_context.file_name('/usr/bin/man')):
         debug('No /usr/bin/man: Removing man pages.')
         to_remove += ['/usr/share/man/*']
     if not os.path.exists(system_context.file_name('/usr/bin/info')):
         debug('No /usr/bin/info: Removing info pages.')
         to_remove += ['/usr/share/info/*']
     self._execute(location,
                   system_context,
                   'remove',
                   *to_remove,
                   recursive=True,
                   force=True)
示例#24
0
    def __call__(self, location: Location, system_context: SystemContext,
                 *args: typing.Any, **kwargs: typing.Any) -> None:
        """Execute command."""

        if system_context.substitution('ROOT_DEVICE') is None:
            GenerateError('ROOT_DEVICE must be set when creating EFI kernel.',
                          location=location)

        output = args[0]
        kernel = kwargs.get('kernel', '')
        initrd_directory \
            = kwargs.get('initrd', os.path.join(system_context.boot_directory,
                                                'initrd-parts'))
        initrd_files = _get_initrd_parts(location, initrd_directory)
        cmdline_input = kwargs.get('commandline', '')
        osrelease_file = system_context.file_name('/usr/lib/os-release')
        efistub = system_context.file_name('/usr/lib/systemd/boot/efi/'
                                           'linuxx64.efi.stub')

        debug('{}: Kernel   : {}.'.format(self.name, kernel))
        debug('{}: Initrd   : {}.'.format(self.name, ', '.join(initrd_files)))
        debug('{}: cmdline  : {}.'.format(self.name, cmdline_input))
        debug('{}: osrelease: {}.'.format(self.name, osrelease_file))
        debug('{}: efistub  : {}.'.format(self.name, efistub))

        self._validate_files(kernel, *initrd_files, osrelease_file, efistub)
        with tempfile.TemporaryDirectory() as tmp:
            initrd = _create_initrd(tmp, *initrd_files)
            cmdline = _create_cmdline_file(tmp, cmdline_input)

            run(self._binary(Binaries.OBJCOPY), '--add-section',
                '.osrel={}'.format(osrelease_file), '--change-section-vma',
                '.osrel=0x20000', '--add-section',
                '.cmdline={}'.format(cmdline), '--change-section-vma',
                '.cmdline=0x30000', '--add-section',
                '.linux={}'.format(kernel), '--change-section-vma',
                '.linux=0x40000', '--add-section', '.initrd={}'.format(initrd),
                '--change-section-vma', '.initrd=0x3000000', efistub, output)

            os.remove(initrd)
            os.remove(cmdline)
示例#25
0
def create_qemu_image(image_path: str, *,
                      image_size: str, image_format: str = 'qcow2',
                      system_name: str, system_version: str = '',
                      repository: str,
                      tempdir: str) -> str:
    trace('Creating image file {}.'.format(image_path))
    with disk.NbdDevice.new_image_file(image_path, image_size,
            disk_format=image_format) as device:
        _create_hdd_image(device)

        debug('mounting data partition for further setup.')
        with mount.Mount(device.device(3), os.path.join(tempdir, 'data'),
                         fs_type='btrfs',
                         options='subvolid=0',
                         fallback_cwd=os.getcwd()) as data_dir:
            _setup_btrfs(data_dir)

            extract_location = os.path.join(data_dir, '.images')
            verbose('Extracting system image to {}.'
                    .format(extract_location))
            extracted_version \
                = tool.write_image(system_name, extract_location,
                                   repository=repository,
                                   version=system_version)

            extracted_image \
                = os.path.join(data_dir, '.images',
                               'clrm_{}'.format(extracted_version))
            assert os.path.isfile(extracted_image)

            tool.copy_efi_partition(image_file=extracted_image,
                                    efi_device=device.device(1),
                                    tempdir=tempdir,
                                    kernel_only=False)

    return image_path
示例#26
0
def main(*command_args: str) -> int:
    known_install_targets: typing.List[InstallTarget] = [
        ContainerFilesystemInstallTarget(),
        CopyInstallTarget(),
        ImagePartitionInstallTarget(),
        MountInstallTarget(),
        PartitionInstallTarget(),
        QemuImageInstallTarget(),
        QemuInstallTarget(),
        TarballInstallTarget(),
    ]

    parse_result = _parse_commandline(*command_args,
                                      install_targets=known_install_targets)

    # Set up printing:
    pr = Printer.instance()
    pr.set_verbosity(parse_result.verbose)
    pr.show_verbosity_level()

    trace(f"Arguments parsed from command line: {parse_result}.")

    install_target = next(x for x in known_install_targets
                          if x.name == parse_result.subcommand)
    assert install_target
    debug(f"Install target {install_target.name} found.")

    with TemporaryDirectory(prefix=f"fs_{install_target.name}") as tmp_dir:
        trace(f"Using temporary directory: {tmp_dir}.")

        image_dir = os.path.join(tmp_dir, "borg")
        os.makedirs(image_dir)

        with BorgMount(
                image_dir,
                system_name=parse_result.system_name,
                repository=parse_result.repository,
                version=parse_result.system_version,
        ) as image_file:
            trace(f"Mounted borg directory with image file: {image_file}.")
            debug(
                f"Running install target with parse_args={parse_result}, tmp_dir={tmp_dir} and image_file={image_file}."
            )
            result = install_target(
                parse_result=parse_result,
                tmp_dir=tmp_dir,
                image_file=image_file,
            )
            debug(f"Install target done: return code: {result}.")
            trace("Starting cleanup.")

    trace(f"Done, leaving with return code {result}.")
    return result
示例#27
0
def _copy_efi(src: str,
              dest: str,
              *,
              include_bootloader: bool = False,
              overwrite: bool = False) -> int:
    try:
        efi_path = os.path.join(dest, "EFI")
        os.makedirs(efi_path, exist_ok=True)

        linux_src_path = os.path.join(src, "EFI/Linux")
        kernels = [
            f for f in os.listdir(linux_src_path)
            if os.path.isfile(os.path.join(linux_src_path, f))
        ]
        kernels_str = '", "'.join(kernels)
        debug(f'Found kernel(s): "{kernels_str}".')
        assert len(kernels) == 1
        kernel = kernels[0]

        _copy_file(
            os.path.join(linux_src_path, kernel),
            os.path.join(dest, "EFI/Linux"),
            overwrite=overwrite,
        )

        if include_bootloader:
            trace("Copying bootloader.")

            efi_src_path = os.path.join(src, "EFI")

            dirs = [
                d for d in os.listdir(efi_src_path) if d != "Linux"
                and os.path.isdir(os.path.join(efi_src_path, d))
            ]
            for d in dirs:
                trace(f"Copying {os.path.join(efi_src_path, d)} to {efi_path}")
                copytree(os.path.join(efi_src_path, d),
                         efi_path,
                         dirs_exist_ok=True)

            copytree(
                os.path.join(src, "loader"),
                os.path.join(dest, "loader"),
                dirs_exist_ok=True,
            )

    except Exception as e:
        debug(f"Failed to install EFI: {e}.")
        return 1
    else:
        debug("Successfully installed EFI")
        return 0
示例#28
0
def _create_hdd_image(device):
    verbose('hdd.img created.')
    partitioner = disk.Partitioner(device)

    partitioner.repartition([
        disk.Partitioner.efi_partition(size='512M'),
        disk.Partitioner.swap_partition(size='1G', name='swap'),
        disk.Partitioner.data_partition(name='data')])

    verbose('hdd.img repartitioned.')

    debug('Format EFI partitition.')
    run('/usr/bin/mkfs.vfat', device.device(1))
    debug('Set up swap partitition.')
    run('/usr/bin/mkswap', device.device(2))
    debug('Format data partitition.')
    run('/usr/bin/mkfs.btrfs', '-L', 'fs_btrfs', device.device(3))
示例#29
0
def _create_hdd_image(device):
    verbose("hdd.img created.")
    partitioner = disk.Partitioner(device)

    partitioner.repartition([
        disk.Partitioner.efi_partition(size="512M"),
        disk.Partitioner.swap_partition(size="1G", name="swap"),
        disk.Partitioner.data_partition(name="data"),
    ])

    verbose("hdd.img repartitioned.")

    debug("Format EFI partitition.")
    run("/usr/bin/mkfs.vfat", device.device(1))
    debug("Set up swap partitition.")
    run("/usr/bin/mkswap", device.device(2))
    debug("Format data partitition.")
    run("/usr/bin/mkfs.btrfs", "-L", "fs_btrfs", device.device(3))
示例#30
0
    def __call__(
        self,
        location: Location,
        system_context: SystemContext,
        *args: typing.Any,
        **kwargs: typing.Any,
    ) -> None:
        """Execute command."""
        cert = kwargs.get("efi_cert", "")
        efi_emulator = kwargs.get("efi_emulator", "")
        key = kwargs.get("efi_key", "")
        skip_validation = kwargs.get("skip_validation", False)
        repository = args[0]
        repository_compression = kwargs.get("repository_compression", "zstd")
        repository_compression_level = kwargs.get(
            "repository_compression_level", 5)
        usr_only = kwargs.get("usr_only", True)

        debug_initrd = kwargs.get("debug_initrd", False)

        h2(f'Exporting system "{system_context.system_name}".')
        debug("Running Hooks.")
        self._run_all_exportcommand_hooks(system_context)

        verbose("Preparing system for export.")
        self._execute(location.next_line(), system_context,
                      "_write_deploy_info")

        # Create some extra data:
        self._create_root_tarball(location, system_context)

        root_partition = self._create_root_fsimage(location,
                                                   system_context,
                                                   usr_only=usr_only)
        assert root_partition
        (verity_partition, root_hash) = self._create_rootverity_fsimage(
            location,
            system_context,
            rootfs=root_partition,
        )
        assert root_hash

        has_kernel = os.path.exists(
            os.path.join(system_context.boot_directory, "vmlinuz"))
        if has_kernel:
            self._create_initrd(location, system_context)
            self._create_clrm_config_initrd(location,
                                            system_context,
                                            root_hash,
                                            debug=debug_initrd)

        cmdline = system_context.set_or_append_substitution(
            "KERNEL_CMDLINE", "systemd.volatile=true rootfstype=squashfs")
        cmdline = _setup_kernel_commandline(cmdline, root_hash)

        kernel_file = ""
        if has_kernel:
            trace(
                f'KERNEL_FILENAME: {system_context.substitution("KERNEL_FILENAME", "")}'
            )
            kernel_file = os.path.join(
                system_context.boot_directory,
                system_context.substitution_expanded("KERNEL_FILENAME", ""),
            )

            assert kernel_file
            self._create_complete_kernel(
                location,
                system_context,
                cmdline,
                kernel_file=kernel_file,
                efi_key=key,
                efi_cert=cert,
            )

        efi_partition = os.path.join(system_context.cache_directory,
                                     "efi_partition.img")

        self._create_efi_partition(
            location,
            system_context,
            efi_partition=efi_partition,
            kernel_file=kernel_file,
            efi_emulator=efi_emulator,
            root_hash=root_hash,
        )

        info("Validating installation for export.")
        if not skip_validation:
            _validate_installation(location.next_line(), system_context)

        export_directory = self.create_export_directory(system_context)
        assert export_directory
        self.create_image(
            location,
            system_context,
            export_directory,
            efi_partition=efi_partition,
            root_partition=root_partition,
            verity_partition=verity_partition,
            root_hash=root_hash,
        )

        system_context.set_substitution("EXPORT_DIRECTORY", export_directory)

        verbose(f"Exporting all data in {export_directory}.")
        self._execute(
            location.next_line(),
            system_context,
            "_export_directory",
            export_directory,
            compression=repository_compression,
            compression_level=repository_compression_level,
            repository=repository,
        )

        info("Cleaning up export location.")
        self.delete_export_directory(export_directory)
        system_context.set_substitution("EXPORT_DIRECTORY", "")