Esempio n. 1
0
    def run(self):
        """Run installation of the payload from image.

        Preserve permissions, owners, groups, ACL's, xattrs, times,
        symlinks and hardlinks. Go recursively, include devices and
        special files. Don't cross file system boundaries.
        """
        cmd = "rsync"
        args = [
            "-pogAXtlHrDx", "--exclude", "/dev/", "--exclude", "/proc/",
            "--exclude", "/tmp/*", "--exclude", "/sys/", "--exclude", "/run/",
            "--exclude", "/boot/*rescue*", "--exclude", "/boot/loader/",
            "--exclude", "/boot/efi/loader/", "--exclude", "/etc/machine-id",
            self._mount_point, self._sysroot
        ]

        try:
            rc = execWithRedirect(cmd, args)
        except (OSError, RuntimeError) as e:
            msg = "Failed to install image: {}".format(e)
            raise PayloadInstallationError(msg) from None

        if rc == 11:
            raise PayloadInstallationError("Failed to install image: "
                                           "{} exited with code {}".format(
                                               cmd, rc))
Esempio n. 2
0
    def run(self):
        """Run installation of the payload from image."""
        # TODO: remove this check for None when Live Image payload will support sources
        # The None check is just a temporary hack that Live OS has source but Live Image don't
        if self._source is not None and not self._source.get_state():
            raise PayloadInstallationError("Source is not set up!")

        cmd = "rsync"
        # preserve: permissions, owners, groups, ACL's, xattrs, times,
        #           symlinks, hardlinks
        # go recursively, include devices and special files, don't cross
        # file system boundaries
        # TODO: source will provide us source path instead of using constant here
        args = [
            "-pogAXtlHrDx", "--exclude", "/dev/", "--exclude", "/proc/",
            "--exclude", "/tmp/*", "--exclude", "/sys/", "--exclude", "/run/",
            "--exclude", "/boot/*rescue*", "--exclude", "/boot/loader/",
            "--exclude", "/boot/efi/loader/", "--exclude", "/etc/machine-id",
            INSTALL_TREE + "/", self._dest_path
        ]
        try:
            rc = execWithRedirect(cmd, args)
        except (OSError, RuntimeError) as e:
            msg = None
            err = str(e)
            log.error(err)
        else:
            err = None
            msg = "%s exited with code %d" % (cmd, rc)
            log.info(msg)

        if err or rc == 11:
            raise PayloadInstallationError(err or msg)
Esempio n. 3
0
    def run(self):
        """Pull a remote and delete it.

        All pulls in our code follow the pattern pull + delete.

        :raise: PayloadInstallationError if the pull fails
        """
        # pull requires this for some reason
        mainctx = create_new_context()
        mainctx.push_thread_default()

        cancellable = None

        # Variable substitute the ref: https://pagure.io/atomic-wg/issue/299
        ref = RpmOstree.varsubst_basearch(self._data.ref)

        self.report_progress(
            _("Starting pull of {branch_name} from {source}").format(
                branch_name=ref, source=self._data.remote))

        progress = OSTree.AsyncProgress.new()
        progress.connect('changed', self._pull_progress_cb)

        pull_opts = {'refs': Variant('as', [ref])}
        # If we're doing a kickstart, we can at least use the content as a reference:
        # See <https://github.com/rhinstaller/anaconda/issues/1117>
        # The first path here is used by <https://pagure.io/fedora-lorax-templates>
        # and the second by <https://github.com/projectatomic/rpm-ostree-toolbox/>
        # FIXME extend tests to cover this part of code
        if OSTree.check_version(2017, 8):
            for path in ['/ostree/repo', '/install/ostree/repo']:
                if os.path.isdir(path + '/objects'):
                    pull_opts['localcache-repos'] = Variant('as', [path])
                    break

        sysroot_file = Gio.File.new_for_path(conf.target.physical_root)
        sysroot = OSTree.Sysroot.new(sysroot_file)
        sysroot.load(cancellable)
        repo = sysroot.get_repo(None)[1]
        # We don't support resuming from interrupted installs
        repo.set_disable_fsync(True)

        try:
            repo.pull_with_options(self._data.remote,
                                   Variant('a{sv}', pull_opts), progress,
                                   cancellable)
        except GError as e:
            raise PayloadInstallationError(
                "Failed to pull from repository: %s" % e) from e

        log.info("ostree pull: %s", progress.get_status() or "")
        self.report_progress(_("Preparing deployment of {}").format(ref))

        # Now that we have the data pulled, delete the remote for now. This will allow a remote
        # configuration defined in the tree (if any) to override what's in the kickstart.
        # Otherwise, we'll re-add it in post.  Ideally, ostree would support a pull without adding
        # a remote, but that would get quite complex.
        repo.remote_delete(self._data.remote, None)

        mainctx.pop_thread_default()
Esempio n. 4
0
    def run(self):
        """Run installation of the payload from a tarball."""
        cmd = "tar"
        # preserve: ACL's, xattrs, and SELinux context
        args = [
            "--numeric-owner", "--selinux", "--acls", "--xattrs",
            "--xattrs-include", "*", "--exclude", "./dev/*", "--exclude",
            "./proc/*", "--exclude", "./tmp/*", "--exclude", "./sys/*",
            "--exclude", "./run/*", "--exclude", "./boot/*rescue*",
            "--exclude", "./boot/loader", "--exclude", "./boot/efi/loader",
            "--exclude", "./etc/machine-id", "-xaf", self._tarfile_path, "-C",
            self._dest_path
        ]
        try:
            rc = execWithRedirect(cmd, args)
        except (OSError, RuntimeError) as e:
            msg = None
            err = str(e)
            log.error(err)
        else:
            err = None
            msg = "%s exited with code %d" % (cmd, rc)
            log.info(msg)

        if err:
            raise PayloadInstallationError(err or msg)
Esempio n. 5
0
    def run(self):
        """Run the task.

        If the image is local, we return its local location. Otherwise,
        the image is downloaded at the specified download location and
        we return that one.

        :return: a path to the image
        """
        log.info("Downloading the image...")

        if self._url.startswith("file://"):
            log.info("Nothing to download.")
            return self._url.removeprefix("file://")

        with requests_session() as session:
            try:
                # Send a GET request to the image URL.
                response = self._send_request(session)

                # Download the image to a file.
                self._download_image(response)

            except requests.exceptions.RequestException as e:
                raise PayloadInstallationError(
                    "Error while downloading the image: {}".format(e)
                ) from e

        return self._download_path
Esempio n. 6
0
    def _install_tar(self):
        """Run installation of the payload from a tarball.

        Preserve ACL's, xattrs, and SELinux context.
        """
        cmd = "tar"
        args = [
            "--numeric-owner",
            "--selinux",
            "--acls",
            "--xattrs",
            "--xattrs-include", "*",
            "--exclude", "./dev/*",
            "--exclude", "./proc/*",
            "--exclude", "./tmp/*",
            "--exclude", "./sys/*",
            "--exclude", "./run/*",
            "--exclude", "./boot/*rescue*",
            "--exclude", "./boot/loader",
            "--exclude", "./boot/efi/loader",
            "--exclude", "./etc/machine-id",
            "--exclude", "./etc/machine-info",
            "-xaf", self._tarfile,
            "-C", self._sysroot
        ]

        try:
            execWithRedirect(cmd, args)
        except (OSError, RuntimeError) as e:
            msg = "Failed to install tar: {}".format(e)
            raise PayloadInstallationError(msg) from None
Esempio n. 7
0
    def _mount_image(self, image_path, mount_point):
        """Mount the image."""
        try:
            rc = blivet.util.mount(
                image_path,
                mount_point,
                fstype="auto",
                options="ro"
            )
        except OSError as e:
            raise PayloadInstallationError(str(e)) from e

        if rc != 0:
            raise PayloadInstallationError(
                "Failed to mount '{}' at '{}': {}".format(image_path, mount_point, rc)
            )
Esempio n. 8
0
def process_transaction_progress(queue, callback):
    """Process the transaction progress.

    When the installation works correctly it will get 'install'
    updates followed by a 'done' message and then a 'quit' message.
    If the installation fails it will send 'quit' without 'done'.

    :param queue: a process shared queue
    :param callback: a callback for progress reporting
    :raise PayloadInstallationError: if the transaction fails
    """
    (token, msg) = queue.get()

    while token:
        if token == 'install':
            callback(_("Installing {}").format(msg))
        elif token == 'configure':
            callback(_("Configuring {}").format(msg))
        elif token == 'verify':
            callback(_("Verifying {}").format(msg))
        elif token == 'log':
            log.info(msg)
        elif token == 'post':
            callback(_("Performing post-installation setup tasks"))
        elif token == 'done':
            break  # Installation finished successfully
        elif token == 'quit':
            raise RuntimeError("The transaction process has ended abruptly: " +
                               msg)
        elif token == 'error':
            raise PayloadInstallationError(
                "An error occurred during the transaction: " + msg)

        (token, msg) = queue.get()
Esempio n. 9
0
def safe_exec_with_redirect(cmd, argv, **kwargs):
    """Like util.execWithRedirect, but treat errors as fatal.

    :raise: PayloadInstallationError if the call fails for any reason
    """
    rc = execWithRedirect(cmd, argv, **kwargs)
    if rc != 0:
        raise PayloadInstallationError("{} {} exited with code {}".format(
            cmd, argv, rc))
Esempio n. 10
0
    def install_all(self):
        """Install all the refs contained on the remote."""
        self._stuff_refs_to_transaction()

        try:
            self._transaction.run()
        except GError as e:
            raise PayloadInstallationError(
                "Failed to install flatpaks: {}".format(e)) from e
Esempio n. 11
0
    def run(self):
        """Run the installation task.

        :raise PayloadInstallationError: if the installation fails
        """
        try:
            self._run()
        except (OSError, RuntimeError) as e:
            raise PayloadInstallationError(
                "Failed to copy bootloader data: {}".format(e)) from e
Esempio n. 12
0
def safe_exec_with_redirect(cmd, argv, successful_return_codes=(0, ),
                            **kwargs):
    """Like util.execWithRedirect, but treat errors as fatal.

    :raise: PayloadInstallationError if the call fails for any reason
    """
    rc = execWithRedirect(cmd, argv, **kwargs)

    if rc not in successful_return_codes:
        raise PayloadInstallationError(
            "The command '{}' exited with the code {}.".format(
                " ".join([cmd] + argv), rc))
Esempio n. 13
0
    def _make_root_rprivate():
        """Make the mount of '/' rprivate.

        Work around inability to move shared filesystems. Also,
        do not share the image mounts with /run bind-mounted to
        physical target root during storage.mount_filesystems.
        """
        rc = execWithRedirect("mount", ["--make-rprivate", "/"])

        if rc != 0:
            raise PayloadInstallationError(
                "Failed to make the '/' mount rprivate: {}".format(rc)
            )
Esempio n. 14
0
    def download_packages(self, callback):
        """Download the packages.

        :param callback: a callback for progress reporting
        :raise PayloadInstallationError: if the download fails
        """
        packages = self._base.transaction.install_set
        progress = DownloadProgress(callback=callback)

        try:
            self._base.download_packages(packages, progress)
        except dnf.exceptions.DownloadError as e:
            msg = "Failed to download the following packages: " + str(e)
            raise PayloadInstallationError(msg) from None
Esempio n. 15
0
    def run(self):
        """Run the task."""
        if not self._checksum:
            log.debug("No checksum to verify.")
            return

        self.report_progress(_("Checking image checksum"))
        expected_checksum = self._normalize_checksum(self._checksum)
        calculated_checksum = self._calculate_checksum(self._image_path)

        if expected_checksum != calculated_checksum:
            log.error("'%s' does not match '%s'", calculated_checksum, expected_checksum)
            raise PayloadInstallationError("Checksum of the image does not match.")

        log.debug("Checksum of the image does match.")
Esempio n. 16
0
    def run(self):
        """Run the task.

        :raise PayloadInstallationError: if the selection cannot be resolved
        :raise NonCriticalInstallationError: if the selection is resolved with warnings
        """
        report = super().run()

        if report.error_messages:
            message = "\n\n".join(report.error_messages)
            log.error("The packages couldn't be resolved:\n\n%s", message)
            raise PayloadInstallationError(message)

        if report.warning_messages:
            message = "\n\n".join(report.warning_messages)
            log.warning("The packages were resolved with warnings:\n\n%s",
                        message)
            raise NonCriticalInstallationError(message)