Пример #1
0
    def _add_single_efi_boot_target(self, partition):
        boot_disk = partition.disk
        boot_part_num = str(partition.parted_partition.number)

        rc = self.efibootmgr(
            "-c", "-w", "-L", productName.split("-")[0],  # pylint: disable=no-member
            "-d", boot_disk.path, "-p", boot_part_num,
            "-l", self.efi_dir_as_efifs_dir + self._efi_binary,  # pylint: disable=no-member
            root=conf.target.system_root
        )
        if rc:
            raise BootLoaderError("Failed to set new efi boot target. This is most "
                                  "likely a kernel or firmware bug.")
Пример #2
0
    def _add_single_efi_boot_target(self, partition):
        boot_disk = partition.disk
        boot_part_num = str(partition.parted_partition.number)

        rc = self.efibootmgr(
            "-c", "-w", "-L", productName.split("-")[0],  # pylint: disable=no-member
            "-d", boot_disk.path, "-p", boot_part_num,
            "-l", self.efi_dir_as_efifs_dir + self._efi_binary,  # pylint: disable=no-member
            root=util.getSysroot()
        )
        if rc:
            raise BootLoaderError("Failed to set new efi boot target. This is most "
                                  "likely a kernel or firmware bug.")
Пример #3
0
    def remove_efi_boot_target(self):
        buf = self.efibootmgr(capture=True)
        for line in buf.splitlines():
            try:
                (slot, _product) = line.split(None, 1)
            except ValueError:
                continue

            if _product == productName.split("-")[0]:           # pylint: disable=no-member
                slot_id = slot[4:8]
                # slot_id is hex, we can't use .isint and use this regex:
                if not re.match("^[0-9a-fA-F]+$", slot_id):
                    log.warning("failed to parse efi boot slot (%s)", slot)
                    continue

                rc = self.efibootmgr("-b", slot_id, "-B")
                if rc:
                    raise BootLoaderError("Failed to remove old efi boot entry. This is most "
                                          "likely a kernel or firmware bug.")
Пример #4
0
    def remove_efi_boot_target(self):
        buf = self.efibootmgr(capture=True)
        for line in buf.splitlines():
            try:
                (slot, _product) = line.split(None, 1)
            except ValueError:
                continue

            if _product == productName.split("-")[0]:           # pylint: disable=no-member
                slot_id = slot[4:8]
                # slot_id is hex, we can't use .isint and use this regex:
                if not re.match("^[0-9a-fA-F]+$", slot_id):
                    log.warning("failed to parse efi boot slot (%s)", slot)
                    continue

                rc = self.efibootmgr("-b", slot_id, "-B")
                if rc:
                    raise BootLoaderError("Failed to remove old efi boot entry. This is most "
                                          "likely a kernel or firmware bug.")
Пример #5
0
class PackagePayload(Payload):
    """ A PackagePayload installs a set of packages onto the target system. """

    DEFAULT_REPOS = [productName.split('-')[0].lower(), "rawhide"]

    def __init__(self, data):
        if self.__class__ is PackagePayload:
            raise TypeError("PackagePayload is an abstract class")

        super(PackagePayload, self).__init__(data)
        self.install_device = None
        self._rpm_macros = []

        self.requiredPackages = []
        self.requiredGroups = []

        # Used to determine which add-ons to display for each environment.
        # The dictionary keys are environment IDs. The dictionary values are two-tuples
        # consisting of lists of add-on group IDs. The first list is the add-ons specific
        # to the environment, and the second list is the other add-ons possible for the
        # environment.
        self._environmentAddons = {}

    def preInstall(self, packages=None, groups=None):
        super(PackagePayload, self).preInstall()

        # Set rpm-specific options

        # nofsync speeds things up at the risk of rpmdb data loss in a crash.
        # But if we crash mid-install you're boned anyway, so who cares?
        self.rpmMacros.append(
            ('__dbi_htconfig', 'hash nofsync %{__dbi_other} %{__dbi_perms}'))

        if self.data.packages.excludeDocs:
            self.rpmMacros.append(('_excludedocs', '1'))

        if self.data.packages.instLangs is not None:
            # Use nil if instLangs is empty
            self.rpmMacros.append(
                ('_install_langs', self.data.packages.instLangs or '%{nil}'))

        if flags.selinux:
            for d in [
                    "/tmp/updates", "/etc/selinux/targeted/contexts/files",
                    "/etc/security/selinux/src/policy", "/etc/security/selinux"
            ]:
                f = d + "/file_contexts"
                if os.access(f, os.R_OK):
                    self.rpmMacros.append(('__file_context_path', f))
                    break
        else:
            self.rpmMacros.append(('__file_context_path', '%{nil}'))

        # Add platform specific group
        groupid = iutil.get_platform_groupid()
        if groupid and groupid in self.groups:
            if isinstance(groups, list):
                log.info("Adding platform group %s", groupid)
                groups.append(groupid)
            else:
                log.warning("Could not add %s to groups, not a list.", groupid)
        elif groupid:
            log.warning("Platform group %s not available.", groupid)

    @property
    def kernelPackages(self):
        if "kernel" in self.data.packages.excludedList:
            return []

        kernels = ["kernel"]

        if isys.isPaeAvailable():
            kernels.insert(0, "kernel-PAE")

        # most ARM systems use platform-specific kernels
        if blivet.arch.isARM():
            if platform.armMachine is not None:
                kernels = ["kernel-%s" % platform.armMachine]

            if isys.isLpaeAvailable():
                kernels.insert(0, "kernel-lpae")

        return kernels

    @property
    def kernelVersionList(self):
        # Find all installed rpms that provide 'kernel'

        # If a PackagePayload is in use, rpm needs to be available
        try:
            import rpm
        except ImportError:
            raise PayloadError(
                "failed to import rpm-python, cannot determine kernel versions"
            )

        files = []

        ts = rpm.TransactionSet(iutil.getSysroot())
        mi = ts.dbMatch('providename', 'kernel')
        for hdr in mi:
            unicode_fnames = (f.decode("utf-8") for f in hdr.filenames)
            # Find all /boot/vmlinuz- files and strip off vmlinuz-
            files.extend((f.split("/")[-1][8:] for f in unicode_fnames
                          if fnmatch(f, "/boot/vmlinuz-*") or fnmatch(
                              f, "/boot/efi/EFI/%s/vmlinuz-*" %
                              self.instclass.efi_dir)))

        return sorted(files, key=functools.cmp_to_key(versionCmp))

    @property
    def rpmMacros(self):
        """A list of (name, value) pairs to define as macros in the rpm transaction."""
        return self._rpm_macros

    @rpmMacros.setter
    def rpmMacros(self, value):
        self._rpm_macros = value

    def reset(self):
        self.reset_install_device()

    def reset_install_device(self):
        """ Unmount the previous base repo and reset the install_device """
        # cdrom: install_device.teardown (INSTALL_TREE)
        # hd: umount INSTALL_TREE, install_device.teardown (ISO_DIR)
        # nfs: umount INSTALL_TREE
        # nfsiso: umount INSTALL_TREE, umount ISO_DIR
        if os.path.ismount(INSTALL_TREE) and not flags.testing:
            if self.install_device and \
               blivet.util.get_mount_device(INSTALL_TREE) == self.install_device.path:
                self.install_device.teardown(recursive=True)
            else:
                blivet.util.umount(INSTALL_TREE)

        if os.path.ismount(ISO_DIR) and not flags.testing:
            if self.install_device and \
               blivet.util.get_mount_device(ISO_DIR) == self.install_device.path:
                self.install_device.teardown(recursive=True)
            # The below code will fail when nfsiso is the stage2 source
            # But if we don't do this we may not be able to switch from
            # one nfsiso repo to another nfsiso repo.  We need to have a
            # way to detect the stage2 state and work around it.
            # Commenting out the below is a hack for F18.  FIXME
            #else:
            #    # NFS
            #    blivet.util.umount(ISO_DIR)

        self.install_device = None

    def _setupMedia(self, device):
        method = self.data.method
        if method.method == "harddrive":
            self._setupDevice(device, mountpoint=ISO_DIR)

            # check for ISO images in the newly mounted dir
            path = ISO_DIR
            if method.dir:
                path = os.path.normpath("%s/%s" % (path, method.dir))

            # XXX it would be nice to streamline this when we're just setting
            #     things back up after storage activation instead of having to
            #     pretend we don't already know which ISO image we're going to
            #     use
            image = findFirstIsoImage(path)
            if not image:
                device.teardown(recursive=True)
                raise PayloadSetupError("failed to find valid iso image")

            if path.endswith(".iso"):
                path = os.path.dirname(path)

            # this could already be set up the first time through
            if not os.path.ismount(INSTALL_TREE):
                # mount the ISO on a loop
                image = os.path.normpath("%s/%s" % (path, image))
                mountImage(image, INSTALL_TREE)

            if not method.dir.endswith(".iso"):
                method.dir = os.path.normpath(
                    "%s/%s" % (method.dir, os.path.basename(image)))
                while method.dir.startswith("/"):
                    # riduculous
                    method.dir = method.dir[1:]
        # Check to see if the device is already mounted, in which case
        # we don't need to mount it again
        elif method.method == "cdrom" and \
             blivet.util.get_mount_paths(device.path):
            return
        else:
            device.format.setup(mountpoint=INSTALL_TREE)

    def _setupInstallDevice(self, storage, checkmount):
        # XXX FIXME: does this need to handle whatever was set up by dracut?
        method = self.data.method
        sslverify = True
        url = None
        mirrorlist = None

        # See if we already have stuff mounted due to dracut
        isodev = blivet.util.get_mount_device(DRACUT_ISODIR)
        device = blivet.util.get_mount_device(DRACUT_REPODIR)

        if method.method == "harddrive":
            if method.biospart:
                log.warning("biospart support is not implemented")
                devspec = method.biospart
            else:
                devspec = method.partition
                needmount = True
                # See if we used this method for stage2, thus dracut left it
                if isodev and method.partition and method.partition in isodev \
                and DRACUT_ISODIR in device:
                    # Everything should be setup
                    url = "file://" + DRACUT_REPODIR
                    needmount = False
                    # We don't setup an install_device here
                    # because we can't tear it down

            isodevice = storage.devicetree.resolveDevice(devspec)
            if needmount:
                if not isodevice:
                    raise PayloadSetupError(
                        "device for HDISO install %s does not exist" % devspec)

                self._setupMedia(isodevice)
                url = "file://" + INSTALL_TREE
                self.install_device = isodevice
        elif method.method == "nfs":
            # There are several possible scenarios here:
            # 1. dracut could have mounted both the nfs repo and an iso and used
            #    the stage2 from inside the iso to boot from.
            #    isodev and device will be set in this case.
            # 2. dracut could have mounted the nfs repo and used a stage2 from
            #    the NFS mount w/o mounting the iso.
            #    isodev will be None and device will be the nfs: path
            # 3. dracut did not mount the nfs (eg. stage2 came from elsewhere)
            #    isodev and device are both None
            # 4. The repo may not contain an iso, in that case use it as is
            if isodev:
                path = iutil.parseNfsUrl('nfs:%s' % isodev)[2]
                # See if the dir holding the iso is what we want
                # and also if we have an iso mounted to /run/install/repo
                if path and path in isodev and DRACUT_ISODIR in device:
                    # Everything should be setup
                    url = "file://" + DRACUT_REPODIR
            else:
                # see if the nfs dir is mounted
                needmount = True
                if device:
                    _options, host, path = iutil.parseNfsUrl('nfs:%s' % device)
                    if method.server and method.server == host and \
                       method.dir and method.dir == path:
                        needmount = False
                        path = DRACUT_REPODIR
                if needmount:
                    # Mount the NFS share on INSTALL_TREE. If it ends up
                    # being nfsiso we will move the mountpoint to ISO_DIR.
                    if method.dir.endswith(".iso"):
                        nfsdir = os.path.dirname(method.dir)
                    else:
                        nfsdir = method.dir
                    self._setupNFS(INSTALL_TREE, method.server, nfsdir,
                                   method.opts)
                    path = INSTALL_TREE

                # check for ISO images in the newly mounted dir
                if method.dir.endswith(".iso"):
                    # if the given URL includes a specific ISO image file, use it
                    image_file = os.path.basename(method.dir)
                    path = os.path.normpath("%s/%s" % (path, image_file))

                image = findFirstIsoImage(path)

                # An image was found, mount it on INSTALL_TREE
                if image:
                    if path.startswith(INSTALL_TREE):
                        # move the INSTALL_TREE mount to ISO_DIR so we can
                        # mount the contents of the iso there.
                        # work around inability to move shared filesystems
                        iutil.execWithRedirect("mount",
                                               ["--make-rprivate", "/"])
                        iutil.execWithRedirect(
                            "mount", ["--move", INSTALL_TREE, ISO_DIR])
                        # The iso is now under ISO_DIR
                        path = ISO_DIR
                    elif path.endswith(".iso"):
                        path = os.path.dirname(path)

                    # mount the ISO on a loop
                    image = os.path.normpath("%s/%s" % (path, image))
                    mountImage(image, INSTALL_TREE)

                    url = "file://" + INSTALL_TREE
                else:
                    # Fall back to the mount path instead of a mounted iso
                    url = "file://" + path
        elif method.method == "url":
            url = method.url
            mirrorlist = method.mirrorlist
            sslverify = not (method.noverifyssl or flags.noverifyssl)
        elif method.method == "cdrom" or (checkmount and not method.method):
            # Did dracut leave the DVD or NFS mounted for us?
            device = blivet.util.get_mount_device(DRACUT_REPODIR)

            # Check for valid optical media if we didn't boot from one
            if not verifyMedia(DRACUT_REPODIR):
                self.install_device = opticalInstallMedia(storage.devicetree)

            # Only look at the dracut mount if we don't already have a cdrom
            if device and not self.install_device:
                self.install_device = storage.devicetree.getDeviceByPath(
                    device)
                url = "file://" + DRACUT_REPODIR
                if not method.method:
                    # See if this is a nfs mount
                    if ':' in device:
                        # prepend nfs: to the url as that's what the parser
                        # wants.  Note we don't get options from this, but
                        # that's OK for the UI at least.
                        _options, host, path = iutil.parseNfsUrl("nfs:%s" %
                                                                 device)
                        method.method = "nfs"
                        method.server = host
                        method.dir = path
                    else:
                        method.method = "cdrom"
            else:
                if self.install_device:
                    if not method.method:
                        method.method = "cdrom"
                    self._setupMedia(self.install_device)
                    url = "file://" + INSTALL_TREE
                elif method.method == "cdrom":
                    raise PayloadSetupError("no usable optical media found")

        return url, mirrorlist, sslverify

    ###
    ### METHODS FOR WORKING WITH REPOSITORIES
    ###
    @property
    def repos(self):
        """A list of repo identifiers, not objects themselves."""
        raise NotImplementedError()

    def addDriverRepos(self):
        """ Add driver repositories and packages
        """
        # Drivers are loaded by anaconda-dracut, their repos are copied
        # into /run/install/DD-X where X is a number starting at 1. The list of
        # packages that were selected is in /run/install/dd_packages

        # Add repositories
        dir_num = 0
        while True:
            dir_num += 1
            repo = "/run/install/DD-%d/" % dir_num
            if not os.path.isdir(repo):
                break

            # Run createrepo if there are rpms and no repodata
            if not os.path.isdir(repo + "/repodata"):
                rpms = glob(repo + "/*rpm")
                if not rpms:
                    continue
                log.info("Running createrepo on %s", repo)
                iutil.execWithRedirect("createrepo_c", [repo])

            ks_repo = self.data.RepoData(name="DD-%d" % dir_num,
                                         baseurl="file://" + repo,
                                         enabled=True)
            self.addRepo(ks_repo)

        # Add packages
        if not os.path.exists("/run/install/dd_packages"):
            return
        with open("/run/install/dd_packages", "r") as f:
            for line in f:
                package = line.strip()
                if package not in self.requiredPackages:
                    self.requiredPackages.append(package)
        log.debug("required packages = %s", self.requiredPackages)

    @property
    def ISOImage(self):
        """ The location of a mounted ISO repo, or None. """
        if not self.data.method.method == "harddrive":
            return None
        # This could either be mounted to INSTALL_TREE or on
        # DRACUT_ISODIR if dracut did the mount.
        dev = blivet.util.get_mount_device(INSTALL_TREE)
        if dev:
            return dev[len(ISO_DIR) + 1:]
        dev = blivet.util.get_mount_device(DRACUT_ISODIR)
        if dev:
            return dev[len(DRACUT_ISODIR) + 1:]
        return None

    ###
    ### METHODS FOR WORKING WITH ENVIRONMENTS
    ###
    @property
    def environments(self):
        raise NotImplementedError()

    def environmentHasOption(self, environmentid, grpid):
        raise NotImplementedError()

    def environmentOptionIsDefault(self, environmentid, grpid):
        raise NotImplementedError()

    def environmentDescription(self, environmentid):
        raise NotImplementedError()

    def selectEnvironment(self, environmentid, excluded=None):
        if environmentid not in self.environments:
            raise NoSuchGroup(environmentid)

        self.data.packages.environment = environmentid

        if excluded is None:
            excluded = []

    def environmentGroups(self, environmentid, optional=True):
        raise NotImplementedError()

    @property
    def environmentAddons(self):
        return self._environmentAddons

    def _isGroupVisible(self, grp):
        raise NotImplementedError()

    def _groupHasInstallableMembers(self, grp):
        raise NotImplementedError()

    def _refreshEnvironmentAddons(self):
        log.info("Refreshing environmentAddons")
        self._environmentAddons = {}

        for environment in self.environments:
            self._environmentAddons[environment] = ([], [])

            # Determine which groups are specific to this environment and which other groups
            # are available in this environment.
            for grp in self.groups:
                if not self._groupHasInstallableMembers(grp):
                    continue
                elif self.environmentHasOption(environment, grp):
                    self._environmentAddons[environment][0].append(grp)
                elif self._isGroupVisible(grp):
                    self._environmentAddons[environment][1].append(grp)

    ###
    ### METHODS FOR WORKING WITH GROUPS
    ###
    @property
    def groups(self):
        raise NotImplementedError()

    def groupDescription(self, groupid):
        raise NotImplementedError()