Ejemplo n.º 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 = join_paths(root, self.SYSTEMD_NETWORK_CONFIG_DIR)
            make_directories(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 = 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
            )
    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
 def _create_directory(self):
     """Create a temporary directory."""
     with tempfile.TemporaryDirectory() as d:
         self.image_path = join_paths(d, "image.img")
         self.image_mount = join_paths(d, "image")
         self.iso_mount = join_paths(d, "iso")
         yield d
Ejemplo n.º 4
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
Ejemplo n.º 5
0
    def _write_repo_file(self, repo_name, content):
        """Write the specified content into a repo file."""
        repo_dir = join_paths(self._sysroot, "etc/yum.repos.d/")
        make_directories(repo_dir)

        repo_path = join_paths(repo_dir, repo_name + ".repo")
        with open(repo_path, "w") as f:
            f.write(content.strip() + "\n")
    def test_repository_with_metadata(self):
        """Test with one driver disk repository."""
        with tempfile.TemporaryDirectory() as d:
            make_directories(join_paths(d, "DD-1"))
            make_directories(join_paths(d, "DD-1", "repodata"))
            touch(join_paths(d, "DD-1", "x.rpm"))

            (r, *rs) = generate_driver_disk_repositories(d)

            assert rs == []
            assert r.name == "DD-1"
            assert r.url == "file://{}/DD-1".format(d)
Ejemplo n.º 7
0
    def _find_live_os_image(self):
        """See if there is a LiveOS/*.img style squashfs image.

        :return: a relative path to the image or None
        """
        if not os.path.exists(join_paths(self._image_mount_point, "LiveOS")):
            return None

        img_files = glob.glob(join_paths(self._image_mount_point, "LiveOS", "*.img"))

        if not img_files:
            return None

        return img_files[0]
Ejemplo n.º 8
0
    def _copy_screenshots(self):
        """Copy screenshots from the installation to the target system."""
        log.info("Copying screenshots from installation.")

        screenshots = glob.glob(SCREENSHOTS_DIRECTORY + "/*.png")
        if not screenshots:
            return

        os.makedirs(join_paths(self._sysroot, TARGET_SCREENSHOT_DIR), 0o750, True)

        for screenshot in screenshots:
            self._copy_file_to_sysroot(
                screenshot,
                join_paths(TARGET_SCREENSHOT_DIR, os.path.basename(screenshot))
            )
    def test_get_kernel_version_list(self):
        """Test the get_kernel_version_list method."""
        with tempfile.TemporaryDirectory() as tmp:
            # Create the image source.
            image_source = self._create_source()
            image_source._mount_point = tmp

            # Create a fake kernel file.
            os.makedirs(join_paths(tmp, "boot"))
            kernel_file = join_paths(tmp, "boot", "vmlinuz-1.2-3.x86_64")
            touch(kernel_file)

            self.module._update_kernel_version_list(image_source)

        assert self.module.get_kernel_version_list() == ["1.2-3.x86_64"]
Ejemplo n.º 10
0
 def _copy_post_script_logs(self):
     """Copy logs from %post scripts"""
     for logfile in glob.glob("/tmp/ks-script*.log"):
         self._copy_file_to_sysroot(
             logfile,
             join_paths(TARGET_LOG_DIR, os.path.basename(logfile))
         )
Ejemplo n.º 11
0
def get_os_release_value(name, sysroot="/"):
    """Read os-release files and return a value of the specified parameter.

    :param name: a name of the parameter (for example, "VERSION_ID")
    :param sysroot: a path to the system root
    :return: a string with the value of None if nothing found
    """
    # Match the variable assignment (for example, "VERSION_ID=").
    name += "="

    # Search all os-release files in the system root.
    paths = ("/etc/os-release", "/usr/lib/os-release")

    for path in paths:
        try:
            with open(join_paths(sysroot, path), "r") as f:
                for line in f:
                    # Match the current line.
                    if not line.startswith(name):
                        continue

                    # Get the value.
                    value = line[len(name):]

                    # Strip spaces and then quotes.
                    value = value.strip().strip("\"'")
                    return value
        except FileNotFoundError:
            pass

    # No value found.
    log.debug("%s not found in os-release files", name[:-1])
    return None
Ejemplo n.º 12
0
def _get_help_mapping(display_mode):
    """Parse the json file containing the help mapping.

    The mappings files are located in the root of the help directory.
    For example for RHEL, they are expected to be at:

        /usr/share/anaconda/help/rhel/anaconda-gui.json
        /usr/share/anaconda/help/rhel/anaconda-tui.json

    :param DisplayModes display_mode: a type of the display mode
    :return dict: a help mapping dictionary
    """
    help_directory = conf.ui.help_directory

    name = "anaconda-{}.json".format(display_mode.value.lower())
    path = join_paths(help_directory, name)

    if not os.path.exists(path):
        log.error("The help mapping file is not found at %s.", path)
        return {}

    mapping = {}

    try:
        with open(path, "rt") as f:
            mapping = json.load(f)
    except (IOError, json.JSONDecodeError) as e:
        log.error("Failed to parse the help mapping file at %s: %s", path,
                  str(e))

    return mapping
Ejemplo n.º 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 = 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 pytest.raises(SubscriptionTokenTransferError):
                task._transfer_entitlement_keys()
Ejemplo n.º 14
0
def _collect_help_files(help_directory, help_file):
    """Collect available help files.

    :param str help_directory: a path to directory with help files or None
    :param str help_file: a relative path to the requested help file
    :return dict: a dictionary of langcodes and absolute paths to the help files
    """
    if not help_file:
        return {}

    if not help_directory or not os.path.exists(help_directory):
        log.debug("The %s help directory does not exist.", help_directory)
        return {}

    files = {}

    for lang in os.listdir(help_directory):
        # Does the help file exist for this language?
        path = join_paths(help_directory, lang, help_file)
        if not os.path.isfile(path):
            continue

        # Create a valid langcode. For example, use en_US instead of en-US.
        code = lang.replace('-', '_')
        files[code] = path

    return files
Ejemplo n.º 15
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 = join_paths(mount_point, DNF_PACKAGE_CACHE_DIR_SUFFIX)

    return location
Ejemplo n.º 16
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 = 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(
                "Failed to connect to Red Hat Insights.")
Ejemplo n.º 17
0
    def _create_mount_point(self, *paths):
        """Create a mount point from specified paths.

        FIXME: This is a temporary workaround.
        """
        mount_point = join_paths(*paths)
        self._mount_points.append(mount_point)
        return mount_point
Ejemplo n.º 18
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 = 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 _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]
    def _create_tar(self, files):
        """Create a new tarball."""
        # Create the content.
        for path in files:
            file_path = join_paths(self.directory, path)
            make_directories(os.path.dirname(file_path))
            touch(file_path)

        # Create a local tarball.
        with tarfile.open(self.tarball, "w") as tar:
            for path in files:
                tar.add(join_paths(self.directory, path), path)

            tar.list()

        # Set up the configuration data.
        self.data.url = "file://" + self.tarball
Ejemplo n.º 21
0
    def _check_files(self, sysroot, file_names):
        """Check existence of the generated repo files."""
        path = join_paths(sysroot, "etc/yum.repos.d/")

        if not file_names:
            assert not os.path.exists(path)
        else:
            assert sorted(os.listdir(path)) == file_names
Ejemplo n.º 22
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(join_paths(sysroot, directory))
         os.mknod(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 pytest.raises(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)
Ejemplo n.º 23
0
def give_the_system_purpose(sysroot, rhsm_syspurpose_proxy, role, sla, usage, addons):
    """Set system purpose for the installed system by calling the syspurpose tool.

    The tool is called in the specified system root, so this method should only
    be called once the given system root contains the syspurpose utility.

    :param str sysroot: system root path
    :param rhsm_syspurpose_proxy: com.redhat.RHSM1.Syspurpose proxy
    :type rhsm_syspurpose_proxy: dasbus DBus proxy object
    :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
    """
    # first check if system purpose data has already been set
    if check_system_purpose_set(sysroot):
        # Remove existing system purpose data.
        #
        # This is important, as otherwise it would be both not possible to
        # clear existing system purpose data if say a user sets all values
        # to "not specified" in the GUI after setting them to some values
        # previously. Also due to syspurpose setting one value at a time
        # one could end up with unwanted hybrid configuration combining
        # new and old date, if not all fields are set in the most recent
        # invocation.
        log.debug("subscription: clearing old system purpose data")
        syspurpose_path = join_paths(sysroot, RHSM_SYSPURPOSE_FILE_PATH)
        os.remove(syspurpose_path)

    if role or sla or usage or addons:
        # Construct a dictionary of values to feed to the SetSyspurpose DBus method.
        syspurpose_dict = {}
        if role:
            syspurpose_dict["role"] = get_variant(Str, role)
        if sla:
            syspurpose_dict["service_level_agreement"] = get_variant(Str, sla)
        if usage:
            syspurpose_dict["usage"] = get_variant(Str, usage)
        if addons:
            syspurpose_dict["addons"] = get_variant(List[Str], addons)
        log.debug("subscription: setting system purpose")
        try:
            locale = os.environ.get("LANG", "")
            rhsm_syspurpose_proxy.SetSyspurpose(syspurpose_dict, locale)
            log.debug("subscription: system purpose has been set")
            return True
        except DBusError as e:
            log.debug("subscription: failed to set system purpose: %s", str(e))
            return False
    else:
        log.warning("subscription: not calling syspurpose as no fields have been provided")
        # doing nothing is still not a failure
        return True
    def test_mount_image_no_iso(self, exec_mock, mount_mock):
        """Mount an image without ISO files in the LiveOS directory."""
        exec_mock.return_value = 0
        mount_mock.return_value = 0

        with self._create_directory():
            iso_dir = join_paths(self.image_mount, "LiveOS")
            os.makedirs(iso_dir)

            assert self._run_task() == self.image_mount
Ejemplo n.º 25
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(TARGET_LOG_DIR, logfile)
         )
    def test_delete_missing_file(self):
        """Delete a file that doesn't exist."""
        with tempfile.TemporaryDirectory() as d:
            image_path = join_paths(d, "image.img")

            assert not os.path.exists(image_path)

            # Don't fail.
            task = RemoveImageTask(image_path)
            task.run()

            assert not os.path.exists(image_path)
Ejemplo n.º 27
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)
Ejemplo n.º 28
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
    def test_delete_existing_file(self):
        """Delete a file that exists."""
        with tempfile.TemporaryDirectory() as d:
            image_path = join_paths(d, "image.img")
            touch(image_path)

            assert os.path.exists(image_path)

            # Delete the file.
            task = RemoveImageTask(image_path)
            task.run()

            assert not os.path.exists(image_path)
Ejemplo n.º 30
0
    def _get_url(root_url, relative_path):
        """Get the URL of the repository."""
        if relative_path == ".":
            return root_url

        # Get the protocol.
        protocol, root_path = split_protocol(root_url)

        # Create the absolute path.
        absolute_path = join_paths(root_path, relative_path)

        # Normalize the URL to solve problems with a relative path.
        # This is especially useful for NFS (root/path/../new_path).
        return protocol + os.path.normpath(absolute_path)