Пример #1
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()
Пример #2
0
    def replace_installed_refs_remote(self, new_remote_name):
        """Replace remote for all the refs.

        :param str new_remote_name: the remote name which will be used instead of the current one
        """
        install_path = self._installation.get_path()
        install_path = install_path.get_path()  # unpack the Gio.File

        variant_type = VariantType(self.FLATPAK_DEPLOY_DATA_GVARIANT_STRING)

        for ref in self.get_refs_full_format():
            deploy_path = os.path.join(install_path, ref, "active/deploy")

            with open(deploy_path, "rb") as f:
                content = f.read()

            variant = Variant.new_from_bytes(variant_type, Bytes(content),
                                             False)
            children = [
                variant.get_child_value(i) for i in range(variant.n_children())
            ]
            # Replace the origin
            children[0] = Variant('s', new_remote_name)
            new_variant = Variant.new_tuple(*children)
            serialized = new_variant.get_data_as_bytes().get_data()

            with open(deploy_path, "wb") as f:
                f.write(serialized)
Пример #3
0
    def run(self):
        cancellable = None

        sysroot_file = Gio.File.new_for_path(self._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)

        remote_options = {}

        if not self._data.gpg_verification_enabled:
            remote_options['gpg-verify'] = Variant('b', False)

        if not conf.payload.verify_ssl:
            remote_options['tls-permissive'] = Variant('b', True)

        if self._use_root:
            root = sysroot_file
        else:
            root = None

        repo.remote_change(root, OSTree.RepoRemoteChange.ADD_IF_NOT_EXISTS,
                           self._data.remote, self._data.url,
                           Variant('a{sv}', remote_options), cancellable)
Пример #4
0
def _gvariant_settings(settings,
                       updated_key1,
                       updated_key2,
                       value,
                       default_type_str=None):
    """Update setting of updated_key1, updated_key2 of settings object with value.

       If the value is None, the setting is removed.

       The type of value is determined from existing setting.
       If setting for key1, key2 does not exist, default_type_str is used or
       the type is inferred from the value supplied (string and bool only).
    """

    type_str = default_type_str

    # build copy of GVariant settings as mutable python object
    new_settings = {}
    dict1 = settings.get_child_value(0)

    # loop over first level dict (key1)
    for key1_idx in range(dict1.n_children()):

        key_dict2 = dict1.get_child_value(key1_idx)
        key1 = key_dict2.get_child_value(0).unpack()
        new_settings[key1] = {}
        dict2 = key_dict2.get_child_value(1)

        # loop over second level dict (key2)
        for key2_idx in range(dict2.n_children()):

            key_val = dict2.get_child_value(key2_idx)
            key2 = key_val.get_child_value(0).unpack()
            val = key_val.get_child_value(1).get_child_value(0)

            # get type string of updated value
            if key1 == updated_key1 and key2 == updated_key2:
                type_str = val.get_type_string()

            # copy old value to new python object
            new_settings[key1][key2] = val

    if type_str is None:
        # infer the new value type for string and boolean
        if isinstance(value, bool):
            type_str = 'b'
        elif isinstance(value, str):
            type_str = 's'

    if type_str is not None:
        if updated_key1 not in new_settings:
            new_settings[updated_key1] = {}
        if value is None:
            new_settings[updated_key1].pop(updated_key2, None)
        else:
            new_settings[updated_key1][updated_key2] = Variant(type_str, value)

    return Variant(settings.get_type_string(), (new_settings, ))
Пример #5
0
    def _get_remote_options(self):
        """Get the remote options."""
        remote_options = {}

        if not self._data.gpg_verification_enabled:
            remote_options['gpg-verify'] = Variant('b', False)

        if not conf.payload.verify_ssl:
            remote_options['tls-permissive'] = Variant('b', True)

        return Variant('a{sv}', remote_options)
Пример #6
0
def nm_add_connection(values):
    """Add new connection specified by values.

       :param values:
                     | list of settings with new values and its types
                     | [[key1, key2, value, type_str], ...]
                     | key1: first-level key of setting (eg "connection")
                     | key2: second-level key of setting (eg "uuid")
                     | value: new value
                     | type_str: dbus type of new value (eg "ay")
       :type values:
                     | [[key1, key2, value, type_str], ...]
                     | key1: str
                     | key2: str
                     | value: object
                     | type_str: str
    """

    settings = {}
    for key1, key2, value, type_str in values:
        gvalue = Variant(type_str, value)
        if key1 not in settings:
            settings[key1] = {}
        settings[key1][key2] = gvalue

    proxy = _get_proxy(
        object_path="/org/freedesktop/NetworkManager/Settings",
        interface_name="org.freedesktop.NetworkManager.Settings")
    try:
        connection = proxy.AddConnection('(a{sa{sv}})', settings)
    except GError as e:
        if "bond.options: invalid option" in e.message:
            raise BondOptionsError(e)
        raise
    return connection
Пример #7
0
def get_property_sync(service, obj_path, iface, prop_name, connection=None):
    """
    Get value of a given property of a given object provided by a given service.

    :param service: DBus service to use
    :type service: str
    :param obj_path: object path
    :type obj_path: str
    :param iface: interface to use
    :type iface: str
    :param prop_name: name of the property
    :type prop_name: str
    :param connection: connection to use (if None, a new connection is
                       established)
    :type connection: Gio.DBusConnection
    :return: unpacked value of the property
    :rtype: tuple with elements that depend on the type of the property
    :raise DBusCallError: when the internal dbus_call_safe_sync invocation
                          raises an exception
    :raise DBusPropertyError: when the given object doesn't have the given
                              property

    """

    args = Variant('(ss)', (iface, prop_name))
    ret = call_sync(service, obj_path, DBUS_PROPS_IFACE, "Get", args,
                    connection)
    if ret is None:
        msg = "No value for the %s object's property %s" % (obj_path,
                                                            prop_name)
        raise DBusPropertyError(msg)

    return ret
Пример #8
0
    def post_install(self):
        super().post_install()

        gi.require_version("OSTree", "1.0")
        from gi.repository import OSTree
        cancellable = None

        # Following up on the "remote delete" above, we removed the
        # remote from /ostree/repo/config.  But we want it in /etc, so
        # re-add it to /etc/ostree/remotes.d, using the sysroot path.
        #
        # However, we ignore the case where the remote already exists,
        # which occurs when the content itself provides the remote
        # config file.

        # Note here we use the deployment as sysroot, because it's
        # that version of /etc that we want.
        sysroot_file = Gio.File.new_for_path(conf.target.system_root)
        sysroot = OSTree.Sysroot.new(sysroot_file)
        sysroot.load(cancellable)
        repo = sysroot.get_repo(None)[1]
        repo.remote_change(sysroot_file,
                           OSTree.RepoRemoteChange.ADD_IF_NOT_EXISTS,
                           self.data.ostreesetup.remote,
                           self.data.ostreesetup.url,
                           Variant('a{sv}', self._remoteOptions), cancellable)

        boot = conf.target.system_root + '/boot'

        # If we're using GRUB2, move its config file, also with a
        # compatibility symlink.
        boot_grub2_cfg = boot + '/grub2/grub.cfg'
        if os.path.isfile(boot_grub2_cfg):
            boot_loader = boot + '/loader'
            target_grub_cfg = boot_loader + '/grub.cfg'
            log.info("Moving %s -> %s", boot_grub2_cfg, target_grub_cfg)
            os.rename(boot_grub2_cfg, target_grub_cfg)
            os.symlink('../loader/grub.cfg', boot_grub2_cfg)

        # Skip kernel args setup for dirinstall, there is no bootloader or rootDevice setup.
        if not conf.target.is_directory:
            # OSTree owns the bootloader configuration, so here we give it
            # the argument list we computed from storage, architecture and
            # such.
            bootloader = STORAGE.get_proxy(BOOTLOADER)
            device_tree = STORAGE.get_proxy(DEVICE_TREE)
            root_device = device_tree.GetRootDevice()

            set_kargs_args = ["admin", "instutil", "set-kargs"]
            set_kargs_args.extend(bootloader.GetArguments())
            set_kargs_args.append("root=" +
                                  device_tree.GetFstabSpec(root_device))
            self._safe_exec_with_redirect("ostree",
                                          set_kargs_args,
                                          root=conf.target.system_root)
Пример #9
0
def uninhibit_screensaver(connection, inhibit_id):
    """
    Re-enable the screensaver idle timer.

    :param connection: A handle for the session message bus
    :type connection: Gio.DBusConnection
    :param inhibit_id: The ID returned by the inhibit method
    :type inhibit_id: int
    """

    try:
        safe_dbus.call_sync(SCREENSAVER_SERVICE, SCREENSAVER_PATH, SCREENSAVER_IFACE,
                            SCREENSAVER_UNINHIBIT_METHOD, Variant('(u)', (inhibit_id,)), connection)
    except safe_dbus.DBusCallError as e:
        log.info("Unable to uninhibit the screensaver: %s", e)
Пример #10
0
    def set_layouts(self, layouts_variants, options=None, convert=False):
        """
        Method that sets X11 layouts and variants (for later X sessions) via
        systemd-localed's DBus API.

        :param layout_variant: list of 'layout (variant)' or 'layout'
                               specifications of layouts and variants
        :type layout_variant: list of strings
        :param options: list of X11 options that should be set
        :type options: list of strings
        :param convert: whether the keymap should be converted to a X11 layout
                        (see set_and_convert_keymap)
        :type convert: bool

        """

        layouts = []
        variants = []

        for layout_variant in (nonempty for nonempty in layouts_variants
                               if nonempty):
            (layout, variant) = parse_layout_variant(layout_variant)
            layouts.append(layout)
            variants.append(variant)

        layouts_str = ",".join(layouts)
        variants_str = ",".join(variants)
        if options:
            opts_str = ",".join(options)
        else:
            opts_str = ""

        # args: layout, model, variant, options, convert, user_interaction
        # where convert indicates whether the keymap should be converted
        # to X11 layout and user_interaction indicates whether PolicyKit
        # should ask for credentials or not
        args = Variant(
            "(ssssbb)",
            (layouts_str, "", variants_str, opts_str, convert, False))
        try:
            safe_dbus.call_sync(LOCALED_SERVICE, LOCALED_OBJECT_PATH,
                                LOCALED_IFACE, "SetX11Keyboard", args,
                                self._connection)
        except safe_dbus.DBusCallError as e:
            log.error("Failed to set layouts: %s", e)
Пример #11
0
def inhibit_screensaver(connection):
    """
    Inhibit the screensaver idle timer.

    :param connection: A handle for the session message bus
    :type connection: Gio.DBusConnection
    :return: The inhibit ID or None
    :rtype: int or None
    """

    try:
        inhibit_id = safe_dbus.call_sync(SCREENSAVER_SERVICE, SCREENSAVER_PATH, SCREENSAVER_IFACE,
                                         SCREENSAVER_INHIBIT_METHOD, Variant('(ss)',
                                         (SCREENSAVER_APPLICATION, SCREENSAVER_REASON)),
                                         connection)
        return inhibit_id[0]
    except safe_dbus.DBusCallError as e:
        log.info("Unable to inhibit the screensaver: %s", e)

    return None
Пример #12
0
    def set_keymap(self, keymap, convert=False):
        """
        Method that sets VConsole keymap via systemd-localed's DBus API.

        :param keymap: VConsole keymap that should be set
        :type keymap: str
        :param convert: whether the keymap should be converted to a X11 layout
                        (see set_and_convert_keymap)
        :type convert: bool

        """

        # args: keymap, keymap_toggle, convert, user_interaction
        # where convert indicates whether the keymap should be converted
        # to X11 layout and user_interaction indicates whether PolicyKit
        # should ask for credentials or not
        args = Variant('(ssbb)', (keymap, "", convert, False))

        try:
            safe_dbus.call_sync(LOCALED_SERVICE, LOCALED_OBJECT_PATH, LOCALED_IFACE,
                                "SetVConsoleKeyboard", args, self._connection)
        except safe_dbus.DBusCallError as e:
            log.error("Failed to set keymap: %s", e)
Пример #13
0
    def install(self):
        mainctx = create_new_context()
        mainctx.push_thread_default()

        cancellable = None
        gi.require_version("OSTree", "1.0")
        gi.require_version("RpmOstree", "1.0")
        from gi.repository import OSTree, RpmOstree
        ostreesetup = self.data.ostreesetup
        log.info("executing ostreesetup=%r", ostreesetup)

        # Initialize the filesystem - this will create the repo as well
        self._safe_exec_with_redirect("ostree", [
            "admin", "--sysroot=" + conf.target.physical_root, "init-fs",
            conf.target.physical_root
        ])

        # Here, we use the physical root as sysroot, because we haven't
        # yet made a deployment.
        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)

        self._remoteOptions = {}

        if hasattr(ostreesetup, 'nogpg') and ostreesetup.nogpg:
            self._remoteOptions['gpg-verify'] = Variant('b', False)

        if not conf.payload.verify_ssl:
            self._remoteOptions['tls-permissive'] = Variant('b', True)

        repo.remote_change(None, OSTree.RepoRemoteChange.ADD_IF_NOT_EXISTS,
                           ostreesetup.remote, ostreesetup.url,
                           Variant('a{sv}', self._remoteOptions), cancellable)

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

        progressQ.send_message(
            _("Starting pull of %(branchName)s from %(source)s") % {
                "branchName": ref,
                "source": ostreesetup.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/>
        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

        try:
            repo.pull_with_options(ostreesetup.remote,
                                   Variant('a{sv}', pull_opts), progress,
                                   cancellable)
        except GError as e:
            exn = PayloadInstallError("Failed to pull from repository: %s" % e)
            log.error(str(exn))
            if errors.errorHandler.cb(exn) == errors.ERROR_RAISE:
                progressQ.send_quit(1)
                util.ipmi_abort(scripts=self.data.scripts)
                sys.exit(1)

        log.info("ostree pull: %s", progress.get_status() or "")
        progressQ.send_message(_("Preparing deployment of %s") % (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.ostreesetup.remote, None)

        self._safe_exec_with_redirect("ostree", [
            "admin", "--sysroot=" + conf.target.physical_root, "os-init",
            ostreesetup.osname
        ])

        admin_deploy_args = [
            "admin", "--sysroot=" + conf.target.physical_root, "deploy",
            "--os=" + ostreesetup.osname
        ]

        admin_deploy_args.append(ostreesetup.remote + ':' + ref)

        log.info("ostree admin deploy starting")
        progressQ.send_message(_("Deployment starting: %s") % (ref, ))
        self._safe_exec_with_redirect("ostree", admin_deploy_args)
        log.info("ostree admin deploy complete")
        progressQ.send_message(_("Deployment complete: %s") % (ref, ))

        # Reload now that we've deployed, find the path to the new deployment
        sysroot.load(None)
        deployments = sysroot.get_deployments()
        assert len(deployments) > 0
        deployment = deployments[0]
        deployment_path = sysroot.get_deployment_directory(deployment)
        util.set_system_root(deployment_path.get_path())

        try:
            self._copy_bootloader_data()
        except (OSError, RuntimeError) as e:
            exn = PayloadInstallError("Failed to copy bootloader data: %s" % e)
            log.error(str(exn))
            if errors.errorHandler.cb(exn) == errors.ERROR_RAISE:
                progressQ.send_quit(1)
                util.ipmi_abort(scripts=self.data.scripts)
                sys.exit(1)

        mainctx.pop_thread_default()