Пример #1
0
    def _write_interface_rename_config(self, root, ifname_option_values,
                                       overwrite):
        """Write systemd configuration .link file for interface renaming.
        :param root: path to the root of the target system
        :type root: str
        :param ifname_option_values: list of ifname boot option values
        :type ifname_option_values: list(str)
        :param overwrite: overwrite existing configuration file
        :type overwrite: bool
        """

        if ifname_option_values:
            target_system_dir = util.join_paths(
                root, self.SYSTEMD_NETWORK_CONFIG_DIR)
            util.mkdirChain(target_system_dir)

        for ifname_value in ifname_option_values:
            iface, mac = ifname_value.split(":", 1)
            content = self.INTERFACE_RENAME_FILE_CONTENT_TEMPLATE.format(
                mac, iface)
            config_file = self.INTERFACE_RENAME_FILE_TEMPLATE.format(iface)
            config_file_path = util.join_paths(self.SYSTEMD_NETWORK_CONFIG_DIR,
                                               config_file)
            _write_config_file(
                root, config_file_path, content,
                "Cannot write {} configuration file for ifname={} option.".
                format(config_file_path, ifname_value), overwrite)
Пример #2
0
def _pick_mount_points(mount_points, download_size, install_size):
    """Pick mount points for the package installation.

    :return: a set of sufficient mount points
    """
    suitable = {
        '/var/tmp',
        conf.target.system_root,
        join_paths(conf.target.system_root, 'home'),
        join_paths(conf.target.system_root, 'tmp'),
        join_paths(conf.target.system_root, 'var'),
    }

    sufficient = set()

    for mount_point, size in mount_points.items():
        # Ignore mount points that are not suitable.
        if mount_point not in suitable:
            continue

        if size >= (download_size + install_size):
            log.debug("Considering %s (%s) for download and install.",
                      mount_point, size)
            sufficient.add(mount_point)

        elif size >= download_size and not mount_point.startswith(
                conf.target.system_root):
            log.debug("Considering %s (%s) for download.", mount_point, size)
            sufficient.add(mount_point)

    return sufficient
Пример #3
0
    def test_canceled_progress(self, proxy_getter, statvfs_mock):
        """Test the canceled installation progress."""
        callback = Mock()

        with tempfile.TemporaryDirectory() as sysroot:
            os.mkdir(join_paths(sysroot, "/boot"))
            os.mkdir(join_paths(sysroot, "/home"))

            device_tree = STORAGE.get_proxy(DEVICE_TREE)
            device_tree.GetMountPoints.return_value = {
                "/": "dev1",
                "/boot": "dev2",
                "/home": "dev3",
            }

            statvfs_mock.return_value = \
                Mock(f_frsize=1024, f_blocks=150, f_bfree=100)

            progress = InstallationProgress(
                sysroot=sysroot,
                callback=callback,
                installation_size=1024 * 100
            )

            with progress:
                time.sleep(2)

        expected = [
            call("Synchronizing writes to disk"),
            call("Installing software 0%")
        ]
        assert callback.call_args_list == expected
Пример #4
0
    def test_find_rw_mounts(self):
        """Test that only RO mounts of install sources are in Dracut."""
        # everything what starts by string in this list will not be tested
        ignore_prefixes = ("Makefile", "README", "python-deps", "module-setup.sh")
        # define false positives
        false_positives = (re.compile(r'\bmount +(--move|--make-rprivate)'),
                           re.compile(r'\bmount /dev/mapper/live-rw'),
                           re.compile(r'\bmount +-t *overlay'))

        # filter not interesting content
        comment_regex = re.compile(r'#[^\n]*')
        log_regex = re.compile(r'\b(info|warn|error) +"[^"]*')

        # remove pyanaconda dir
        dracut_dir_path = join_paths(os.path.split(REPO_DIR)[0], "dracut")
        files = os.listdir(dracut_dir_path)

        # helper function to ignore prefixes specified above
        def _filter_ignore_files(result, file_name):
            for ignore_prefix in ignore_prefixes:
                if file_name.startswith(ignore_prefix):
                    print("ignoring", file_name)
                    return result

            if os.path.isdir(file_name):
                print("ignoring directory", file_name)
                return result

            result.append(file_name)
            return result

        files = reduce(_filter_ignore_files, files, [])

        mount_line_regex = re.compile(r'\bmount ')

        for f in files:
            path = join_paths(dracut_dir_path, f)
            with open(path, "rt") as fd:
                print("reading content of", path)
                content = fd.read()
                content = comment_regex.sub("", content)
                content = log_regex.sub("", content)

                for num, line in enumerate(content.split('\n'), start=1):
                    # remove false positives from the line string
                    for fp in false_positives:
                        line = fp.sub("", line)

                    # skip empty lines
                    if not line:
                        continue
                    # skip every line which doesn't contain mount string
                    if not mount_line_regex.search(line):
                        continue

                    # fail on every line which does not have 'mount -o ro'
                    assert re.search(r'\bmount +-o *[a-z,]*ro', line), \
                        "Dracut mount in '{}' on line '{}' is not read-only!" \
                        .format(path, num)
Пример #5
0
    def check_system_purpose_set_test(self):
        """Test the check_system_purpose_set() helper function."""
        # system purpose set
        with tempfile.TemporaryDirectory() as sysroot:
            # create a dummy syspurpose file
            syspurpose_path = RHSM_SYSPURPOSE_FILE_PATH
            directory = os.path.split(syspurpose_path)[0]
            os.makedirs(util.join_paths(sysroot, directory))
            os.mknod(util.join_paths(sysroot, syspurpose_path))
            self.assertTrue(check_system_purpose_set(sysroot))

        # system purpose not set
        with tempfile.TemporaryDirectory() as sysroot:
            self.assertFalse(check_system_purpose_set(sysroot))
Пример #6
0
    def run(self):
        """Connect the target system to Red Hat Insights."""
        # check if we should connect to Red Hat Insights
        if not self._connect_to_insights:
            log.debug(
                "insights-connect-task: Insights not requested, skipping")
            return
        elif not self._subscription_attached:
            log.debug(
                "insights-connect-task: "
                "Insights requested but target system is not subscribed, skipping"
            )
            return

        insights_path = util.join_paths(self._sysroot, self.INSIGHTS_TOOL_PATH)
        # check the insights client utility is available
        if not os.path.isfile(insights_path):
            raise InsightsClientMissingError(
                "The insight-client tool ({}) is not available.".format(
                    self.INSIGHTS_TOOL_PATH))

        # tell the insights client to connect to insights
        log.debug("insights-connect-task: connecting to insights")
        rc = util.execWithRedirect(self.INSIGHTS_TOOL_PATH, ["--register"],
                                   root=self._sysroot)
        if rc:
            raise InsightsConnectError(
                "Connecting to Red Hat Insights failed.")
Пример #7
0
 def run(self):
     """Run Repo files installation source setup."""
     log.debug("Trying to detect repo files automatically")
     for repo_dir in self._repo_dirs:
         if len(glob.glob(join_paths(repo_dir, "*.repo"))) > 0:
             return
     raise SourceSetupError("repo files not found")
Пример #8
0
def pick_download_location(dnf_manager):
    """Pick the download location.

    :param dnf_manager: the DNF manager
    :return: a path to the download location
    """
    download_size = dnf_manager.get_download_size()
    install_size = dnf_manager.get_installation_size()
    mount_points = get_free_space_map()

    # Try to find mount points that are sufficient for download and install.
    sufficient = _pick_mount_points(mount_points, download_size, install_size)

    # Or find mount points that are sufficient only for download.
    if not sufficient:
        sufficient = _pick_mount_points(mount_points,
                                        download_size,
                                        install_size=0)

    # Ignore the system root if there are other mount points.
    if len(sufficient) > 1:
        sufficient.discard(conf.target.system_root)

    if not sufficient:
        raise RuntimeError("Not enough disk space to download the "
                           "packages; size {}.".format(download_size))

    # Choose the biggest sufficient mount point.
    mount_point = _get_biggest_mount_point(mount_points, sufficient)

    log.info("Mount point %s picked as download location", mount_point)
    location = util.join_paths(mount_point, DNF_PACKAGE_CACHE_DIR_SUFFIX)

    return location
Пример #9
0
 def _dump_journal(self):
     """Dump journal from the installation environment"""
     tempfile = "/tmp/journal.log"
     with open(tempfile, "w") as logfile:
         execWithRedirect("journalctl", ["-b"], stdout=logfile)
     self._copy_file_to_sysroot(tempfile,
                                join_paths(ANACONDA_LOG_DIR, "journal.log"))
Пример #10
0
 def _transfer_file(self, target_path, target_name):
     """Transfer a file with nice logs and raise an exception if it does not exist."""
     log.debug("subscription: transferring %s", target_name)
     target_repo_file_path = util.join_paths(self._sysroot, target_path)
     if not self._copy_file(target_path, target_repo_file_path):
         msg = "{} ({}) is missing".format(target_name, self.RHSM_REPO_FILE_PATH)
         raise SubscriptionTokenTransferError(msg)
    def transfer_system_purpose_test(self, path_exists):
        """Test system purpose transfer method of the subscription token transfer task."""
        # simulate syspurpose file existing
        path_exists.return_value = True
        with tempfile.TemporaryDirectory() as sysroot:
            task = TransferSubscriptionTokensTask(
                sysroot=sysroot, transfer_subscription_tokens=True)
            task._copy_file = Mock()
            task._transfer_system_purpose()
            sysroot_path = util.join_paths(
                sysroot,
                TransferSubscriptionTokensTask.RHSM_SYSPURPOSE_FILE_PATH)
            task._copy_file.assert_called_once_with(
                TransferSubscriptionTokensTask.RHSM_SYSPURPOSE_FILE_PATH,
                sysroot_path)

        # simulate syspurpose file not existing
        # - this should result in just the copy operation not being attempted
        path_exists.return_value = False
        with tempfile.TemporaryDirectory() as sysroot:
            task = TransferSubscriptionTokensTask(
                sysroot=sysroot, transfer_subscription_tokens=True)
            task._copy_file = Mock()
            task._transfer_system_purpose()
            task._copy_file.assert_not_called()
Пример #12
0
    def run(self):
        """Set up the installation source."""
        log.debug("Setting up NFS source: %s", self._url)

        for mount_point in [self._device_mount, self._iso_mount]:
            if os.path.ismount(mount_point):
                raise SourceSetupError("The mount point {} is already in use.".format(
                    mount_point
                ))

        options, host, path = parse_nfs_url(self._url)
        path, image = self._split_iso_from_path(path)
        try:
            self._mount_nfs(host, options, path)
        except PayloadSetupError:
            raise SourceSetupError("Could not mount NFS url '{}'".format(self._url))

        iso_source_path = join_paths(self._device_mount, image) if image else self._device_mount

        iso_name = find_and_mount_iso_image(iso_source_path, self._iso_mount)

        if iso_name:
            log.debug("Using the ISO '%s' mounted at '%s'.", iso_name, self._iso_mount)
            return self._iso_mount

        if verify_valid_repository(self._device_mount):
            log.debug("Using the directory at '%s'.", self._device_mount)
            return self._device_mount

        # nothing found unmount the existing device
        unmount(self._device_mount)
        raise SourceSetupError(
            "Nothing useful found for NFS source at {}".format(self._url))
Пример #13
0
    def test_transfer_entitlement_keys(self):
        """Test the entitlement keys transfer method of the subscription token transfer task."""
        # simulate entitlement keys not existing
        with tempfile.TemporaryDirectory() as sysroot:
            task = TransferSubscriptionTokensTask(
                sysroot=sysroot, transfer_subscription_tokens=True)
            task._copy_pem_files = Mock()
            task._copy_pem_files.return_value = True
            task._transfer_entitlement_keys()
            sysroot_path = util.join_paths(
                sysroot,
                TransferSubscriptionTokensTask.RHSM_ENTITLEMENT_KEYS_PATH)
            task._copy_pem_files.assert_called_once_with(
                TransferSubscriptionTokensTask.RHSM_ENTITLEMENT_KEYS_PATH,
                sysroot_path)

        # simulate entitlement keys not existing
        # - this is a critical error and should raise an exception
        #   (without proper certificates and keys the target system
        #    would be unable to communicate with the Red Hat subscription
        #    infrastructure)
        with tempfile.TemporaryDirectory() as sysroot:
            task = TransferSubscriptionTokensTask(
                sysroot=sysroot, transfer_subscription_tokens=True)
            task._copy_pem_files = Mock()
            task._copy_pem_files.return_value = False
            with self.assertRaises(SubscriptionTokenTransferError):
                task._transfer_entitlement_keys()
Пример #14
0
    def _create_iso_path(full_path_on_mounted_device, iso_name):
        # The directory parameter is not pointing directly to ISO
        if not full_path_on_mounted_device.endswith(iso_name):
            return os.path.normpath(
                join_paths(full_path_on_mounted_device, iso_name))

        # The directory parameter is pointing directly to ISO
        return full_path_on_mounted_device
Пример #15
0
    def _get_mount_points(self):
        """Get mount points in the device tree.

        :return: a list of mount points
        """
        device_tree = STORAGE.get_proxy(DEVICE_TREE)
        mount_points = device_tree.GetMountPoints()
        return [join_paths(self._sysroot, m) for m in mount_points]
Пример #16
0
 def test_connect_error(self, exec_with_redirect):
     """Test that the expected exception is raised if the Insights client fails when called."""
     with tempfile.TemporaryDirectory() as sysroot:
         # create a fake insights client tool file
         utility_path = ConnectToInsightsTask.INSIGHTS_TOOL_PATH
         directory = os.path.split(utility_path)[0]
         os.makedirs(util.join_paths(sysroot, directory))
         os.mknod(util.join_paths(sysroot, utility_path))
         task = ConnectToInsightsTask(sysroot=sysroot,
                                      subscription_attached=True,
                                      connect_to_insights=True)
         # make sure execWithRedirect has a non zero return code
         exec_with_redirect.return_value = 1
         with self.assertRaises(InsightsConnectError):
             task.run()
         # check that call to the insights client has been done with the expected parameters
         exec_with_redirect.assert_called_once_with(
             '/usr/bin/insights-client', ['--register'], root=sysroot)
Пример #17
0
 def _copy_tmp_logs(self):
     """Copy a number of log files from /tmp"""
     log_files_to_copy = [
         "anaconda.log",
         "syslog",
         "X.log",
         "program.log",
         "packaging.log",
         "storage.log",
         "ifcfg.log",
         "lvm.log",
         "dnf.librepo.log",
         "hawkey.log",
         "dbus.log",
     ]
     for logfile in log_files_to_copy:
         self._copy_file_to_sysroot(join_paths("/tmp/", logfile),
                                    join_paths(ANACONDA_LOG_DIR, logfile))
Пример #18
0
def give_the_system_purpose(sysroot, role, sla, usage, addons):
    """Set system purpose for the installed system by calling the syspurpose tool.

    The tool is called in the installed system chroot, so this method can be only
    called once the system rootfs content is in place.

    :param str sysroot: system root path
    :param role: role of the system
    :type role: str or None
    :param sla: Service Level Agreement for the system
    :type sla: str or None
    :param usage: intended usage of the system
    :type usage: str or None
    :param list addons: any additional layered products or features
    """

    if role or sla or usage or addons:
        # using join_paths() as both paths are absolute
        syspurpose_sysroot_path = util.join_paths(sysroot, SYSPURPOSE_UTILITY_PATH)
        if os.path.exists(syspurpose_sysroot_path):
            # The syspurpose utility can only set one value at a time,
            # so we might need to call it multiple times to set all the
            # requested values.
            #
            # Also as the values can contain white space we need to make sure the
            # values passed to arguments are all properly quoted.
            if role:
                args = ["set-role", role]
                if _call_syspurpose_tool(sysroot, args):
                    return False
            if sla:
                args = ["set-sla", sla]

                if _call_syspurpose_tool(sysroot, args):
                    return False
            if usage:
                args = ["set-usage", usage]
                if _call_syspurpose_tool(sysroot, args):
                    return False
            if addons:
                args = ["add", "addons"]
                for addon in addons:
                    args.append(addon)
                if _call_syspurpose_tool(sysroot, args):
                    return False
            log.debug("subscription: system purpose has been set")
            return True
        else:
            log.error("the syspurpose tool is missing, cannot set system purpose")
            return False
    else:
        log.warning("not calling syspurpose as no fields have been provided")
        # doing nothing is still not a failure
        return True
Пример #19
0
    def _copy_file_to_sysroot(self, src, dest):
        """Copy a file, if it exists, and set its access bits.

        :param str src: path to source file
        :param str dest: path to destination file within sysroot
        """
        if os.path.exists(src):
            log.info("Copying file: %s -> %s", src, dest)
            full_dest_path = join_paths(self._sysroot, dest)
            shutil.copyfile(src, full_dest_path)
            os.chmod(full_dest_path, 0o0600)
Пример #20
0
    def _copy_tree_to_sysroot(self, src, dest):
        """Copy a directory tree, if it exists, and set its access bits.

        :param str src: path to source directory
        :param str dest: path to destination directory within sysroot
        """
        if os.path.exists(src):
            log.info("Copying directory tree: %s -> %s", src, dest)
            full_dest_path = join_paths(self._sysroot, dest)
            shutil.copytree(src, full_dest_path, dirs_exist_ok=True)
            os.chmod(full_dest_path, 0o0600)
Пример #21
0
    def test_verify_no_checksum(self):
        """Test the verification of a checksum."""
        with tempfile.TemporaryDirectory() as d:
            f_name = join_paths(d, "image")

            task = VerifyImageChecksum(image_path=f_name, checksum="")

            with self.assertLogs(level="DEBUG") as cm:
                task.run()

        msg = "No checksum to verify."
        assert msg in "\n".join(cm.output)
Пример #22
0
    def from_directory(cls, directory_path):
        """Generate RepoConfigurationData url from directory path.

        This will basically add file:/// to the directory and set it to url with a proper type.

        :param str directory_path: directory which will be used to create url
        :return: RepoConfigurationData instance
        """
        data = RepoConfigurationData()

        data.url = join_paths("file:///", directory_path)

        return data
Пример #23
0
def verify_valid_repository(path):
    """Check if the given path is a valid repository.

    :param str path: path to the repository
    :returns: True if repository is valid false otherwise
    :rtype: bool
    """
    repomd_path = join_paths(path, "repodata/repomd.xml")

    if os.path.exists(repomd_path) and os.path.isfile(repomd_path):
        return True

    return False
Пример #24
0
    def get_iso_path(self):
        """Get path to the ISO from the partition root.

        This could be an empty string if the source is pointing to
        installation tree instead of ISO.

        :return: path to the ISO or empty string if no ISO is involved
        :rtype: str
        """
        if not self._iso_name:
            return ""

        return join_paths(self.directory, self._iso_name)
Пример #25
0
    def _get_destination_size(self, mount_points):
        # FIXME: Use get_df_map/get_free_space_map.
        dest_size = 0

        for mnt in mount_points:
            mnt_path = util.join_paths(conf.target.system_root, mnt)

            if not os.path.exists(mnt_path):
                continue

            mnt_stat = os.statvfs(mnt_path)
            dest_size += mnt_stat.f_frsize * (mnt_stat.f_blocks - mnt_stat.f_bfree)

        return dest_size
Пример #26
0
 def test_join_paths(self):
     assert util.join_paths("/first/path/") == \
         "/first/path/"
     assert util.join_paths("") == \
         ""
     assert util.join_paths("/first/path/", "/second/path") == \
         "/first/path/second/path"
     assert util.join_paths("/first/path/", "/second/path", "/third/path") == \
         "/first/path/second/path/third/path"
     assert util.join_paths("/first/path/", "/second/path", "third/path") == \
         "/first/path/second/path/third/path"
     assert util.join_paths("/first/path/", "second/path") == \
         "/first/path/second/path"
     assert util.join_paths("first/path", "/second/path") == \
         "first/path/second/path"
     assert util.join_paths("first/path", "second/path") == \
         "first/path/second/path"
Пример #27
0
def check_system_purpose_set(sysroot="/"):
    """Check if System Purpose has been set for the system.

    By manipulating the sysroot parameter it is possible to
    check is System Purpose has been set for both the installation
    environment and the target system.

    For installation environment use "/", for the target system
    path to the installation root.

    :param str sysroot: system root where to check
    :return: True if System Purpose has been set, False otherwise
    """
    syspurpose_path = util.join_paths(sysroot, RHSM_SYSPURPOSE_FILE_PATH)
    return os.path.exists(syspurpose_path)
Пример #28
0
def verify_valid_repository(path):
    """Check if the given path is a valid repository.

    :param str path: path to the repository
    :returns: True if repository is valid false otherwise
    :rtype: bool
    """
    repomd_path = join_paths(path, "repodata/repomd.xml")

    if os.path.exists(repomd_path) and os.path.isfile(repomd_path):
        return True

    # FIXME: Remove this temporary solution when payload source migration will be finished.
    #
    # Source should not point to an installation tree but only to a repository, however, right now
    # we are in state that sources are only for base repository and just reflecting data from
    # user. With the unified feature the above check won't work because repository is a sub-folder
    # redirected by .treeinfo file. Add this check back to fix this issue.
    if os.path.exists(join_paths(path, ".treeinfo")):
        return True
    if os.path.exists(join_paths(path, "treeinfo")):
        return True

    return False
Пример #29
0
 def join_paths_test(self):
     self.assertEqual(util.join_paths("/first/path/"),
                      "/first/path/")
     self.assertEqual(util.join_paths(""),
                      "")
     self.assertEqual(util.join_paths("/first/path/", "/second/path"),
                      "/first/path/second/path")
     self.assertEqual(util.join_paths("/first/path/", "/second/path", "/third/path"),
                      "/first/path/second/path/third/path")
     self.assertEqual(util.join_paths("/first/path/", "/second/path", "third/path"),
                      "/first/path/second/path/third/path")
     self.assertEqual(util.join_paths("/first/path/", "second/path"),
                      "/first/path/second/path")
     self.assertEqual(util.join_paths("first/path", "/second/path"),
                      "first/path/second/path")
     self.assertEqual(util.join_paths("first/path", "second/path"),
                      "first/path/second/path")
Пример #30
0
    def _set_up_fips(self):
        """Set up FIPS in the target system."""
        log.debug("Copying the crypto policy.")

        # Create /etc/crypto-policies.
        src = "/etc/crypto-policies/"
        dst = join_paths(self._sysroot, src)
        util.mkdirChain(dst)

        # Copy the config file.
        src = "/etc/crypto-policies/config"
        dst = join_paths(self._sysroot, src)
        shutil.copyfile(src, dst)

        # Log the file content on the target system.
        util.execWithRedirect("/bin/cat", [dst])

        # Copy the back-ends.
        src = "/etc/crypto-policies/back-ends/"
        dst = join_paths(self._sysroot, src)
        shutil.copytree(src, dst, symlinks=True)

        # Log the directory content on the target system.
        util.execWithRedirect("/bin/ls", ["-l", dst])