Blame boom/hostprofile.py

Packit 5acf3f
# Copyright (C) 2017 Red Hat, Inc., Bryn M. Reeves <bmr@redhat.com>
Packit 5acf3f
#
Packit 5acf3f
# hostprofile.py - Boom host profiles
Packit 5acf3f
#
Packit 5acf3f
# This file is part of the boom project.
Packit 5acf3f
#
Packit 5acf3f
# This copyrighted material is made available to anyone wishing to use,
Packit 5acf3f
# modify, copy, or redistribute it subject to the terms and conditions
Packit 5acf3f
# of the GNU General Public License v.2.
Packit 5acf3f
#
Packit 5acf3f
# You should have received a copy of the GNU Lesser General Public License
Packit 5acf3f
# along with this program; if not, write to the Free Software Foundation,
Packit 5acf3f
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Packit 5acf3f
"""The ``boom.hostprofile``  module defines the `HostProfile` class that
Packit 5acf3f
represents a host system profile. A `HostProfile` defines the identity
Packit 5acf3f
of a host and includes template values that override the corresponding
Packit 5acf3f
``OsProfile`` defaults for the respective host.
Packit 5acf3f
Packit 5acf3f
Functions are provided to read and write host system profiles from
Packit 5acf3f
an on-disk store, and to retrieve ``HostProfile`` instances using
Packit 5acf3f
various selection criteria.
Packit 5acf3f
Packit 5acf3f
The ``HostProfile`` class includes named properties for each profile
Packit 5acf3f
attribute ("profile key"). In addition, the class serves as a container
Packit 5acf3f
type, allowing attributes to be accessed via dictionary-style indexing.
Packit 5acf3f
This simplifies iteration over a profile's key / value pairs and allows
Packit 5acf3f
straightforward access to all members in scripts and the Python shell.
Packit 5acf3f
Packit 5acf3f
The keys used to access ``HostProfile`` members (and their corresponding
Packit 5acf3f
property names) are identical to those used by the ``OsProfile`` class.
Packit 5acf3f
"""
Packit 5acf3f
from __future__ import print_function
Packit 5acf3f
Packit 5acf3f
from boom import *
Packit 5acf3f
from boom.osprofile import *
Packit 5acf3f
Packit 5acf3f
from hashlib import sha1
Packit 5acf3f
from os.path import join as path_join
Packit 5acf3f
import logging
Packit 5acf3f
import string
Packit 5acf3f
Packit 5acf3f
# Module logging configuration
Packit 5acf3f
_log = logging.getLogger(__name__)
Packit 5acf3f
_log.set_debug_mask(BOOM_DEBUG_PROFILE)
Packit 5acf3f
Packit 5acf3f
_log_debug = _log.debug
Packit 5acf3f
_log_debug_profile = _log.debug_masked
Packit 5acf3f
_log_info = _log.info
Packit 5acf3f
_log_warn = _log.warning
Packit 5acf3f
_log_error = _log.error
Packit 5acf3f
Packit 5acf3f
#: Global host profile list
Packit 5acf3f
_host_profiles = []
Packit 5acf3f
_host_profiles_by_id = {}
Packit 5acf3f
_host_profiles_by_host_id = {}
Packit 5acf3f
Packit 5acf3f
#: Whether profiles have been read from disk
Packit 5acf3f
_host_profiles_loaded = False
Packit 5acf3f
Packit 5acf3f
#: Boom profiles directory name.
Packit 5acf3f
BOOM_HOST_PROFILES = "hosts"
Packit 5acf3f
Packit 5acf3f
#: File name format for Boom profiles.
Packit 5acf3f
BOOM_HOST_PROFILE_FORMAT = "%s-%s.host"
Packit 5acf3f
Packit 5acf3f
#: The file mode with which to create Boom profiles.
Packit 5acf3f
BOOM_HOST_PROFILE_MODE = 0o644
Packit 5acf3f
Packit 5acf3f
# Constants for Boom profile keys
Packit 5acf3f
#: Constant for the Boom host identifier profile key.
Packit 5acf3f
BOOM_HOST_ID = "BOOM_HOST_ID"
Packit 5acf3f
#: Constant for the Boom host name profile key.
Packit 5acf3f
BOOM_HOST_NAME = "BOOM_HOST_NAME"
Packit 5acf3f
#: Constant for the Boom host add options key.
Packit 5acf3f
BOOM_HOST_ADD_OPTS = "BOOM_HOST_ADD_OPTS"
Packit 5acf3f
#: Constant for the Boom host del options key.
Packit 5acf3f
BOOM_HOST_DEL_OPTS = "BOOM_HOST_DEL_OPTS"
Packit 5acf3f
#: Constant for the Boom host label key.
Packit 5acf3f
BOOM_HOST_LABEL = "BOOM_HOST_LABEL"
Packit 5acf3f
Packit 5acf3f
#: Constant for shared machine_id key
Packit 5acf3f
BOOM_ENTRY_MACHINE_ID = "BOOM_ENTRY_MACHINE_ID"
Packit 5acf3f
Packit 5acf3f
#: Ordered list of possible host profile keys, partitioned into
Packit 5acf3f
#: mandatory keys, optional host profile keys, keys mapping to
Packit 5acf3f
#: embedded ``OsProfile`` identity data, and ``OsProfile`` pattern
Packit 5acf3f
#: keys that may be overridden in the ``HostProfile``.
Packit 5acf3f
HOST_PROFILE_KEYS = [
Packit 5acf3f
    # HostProfile identifier
Packit 5acf3f
    BOOM_HOST_ID,
Packit 5acf3f
    # Machine hostname
Packit 5acf3f
    BOOM_HOST_NAME,
Packit 5acf3f
    # Binding to label, machine_id and OsProfile
Packit 5acf3f
    BOOM_ENTRY_MACHINE_ID, BOOM_OS_ID,
Packit 5acf3f
    # Optional host profile keys
Packit 5acf3f
    BOOM_HOST_LABEL, BOOM_HOST_ADD_OPTS, BOOM_HOST_DEL_OPTS,
Packit 5acf3f
    # Keys 7-10 OS identity keys mapped to the embedded OsProfile.
Packit 5acf3f
    BOOM_OS_NAME, BOOM_OS_SHORT_NAME, BOOM_OS_VERSION, BOOM_OS_VERSION_ID,
Packit 5acf3f
    # Keys 11-15 (OsProfile patterns) may be overridden in the host profile.
Packit 5acf3f
    BOOM_OS_UNAME_PATTERN, BOOM_OS_KERNEL_PATTERN, BOOM_OS_INITRAMFS_PATTERN,
Packit 5acf3f
    BOOM_OS_ROOT_OPTS_LVM2, BOOM_OS_ROOT_OPTS_BTRFS,
Packit 5acf3f
    BOOM_OS_OPTIONS
Packit 5acf3f
]
Packit 5acf3f
Packit 5acf3f
#: A map of Boom host profile keys to human readable key names suitable
Packit 5acf3f
#: for use in formatted output. These key names are used to format a
Packit 5acf3f
#: ``HostProfile`` object as a human readable string.
Packit 5acf3f
HOST_KEY_NAMES = {
Packit 5acf3f
    # Keys unique to HostProfile
Packit 5acf3f
    BOOM_HOST_ID: "Host ID",
Packit 5acf3f
    BOOM_HOST_NAME: "Host name",
Packit 5acf3f
    BOOM_HOST_LABEL: "Host label",
Packit 5acf3f
    BOOM_HOST_ADD_OPTS: "Add options",
Packit 5acf3f
    BOOM_HOST_DEL_OPTS: "Del options",
Packit 5acf3f
    # Keys shared with BootEntry
Packit 5acf3f
    BOOM_ENTRY_MACHINE_ID: "Machine ID",
Packit 5acf3f
    # Keys incorporated from OsProfile
Packit 5acf3f
    BOOM_OS_ID: OS_KEY_NAMES[BOOM_OS_ID],
Packit 5acf3f
    BOOM_OS_NAME: OS_KEY_NAMES[BOOM_OS_NAME],
Packit 5acf3f
    BOOM_OS_SHORT_NAME: OS_KEY_NAMES[BOOM_OS_SHORT_NAME],
Packit 5acf3f
    BOOM_OS_VERSION: OS_KEY_NAMES[BOOM_OS_VERSION],
Packit 5acf3f
    BOOM_OS_VERSION_ID: OS_KEY_NAMES[BOOM_OS_VERSION_ID],
Packit 5acf3f
    BOOM_OS_UNAME_PATTERN: OS_KEY_NAMES[BOOM_OS_UNAME_PATTERN],
Packit 5acf3f
    BOOM_OS_KERNEL_PATTERN: OS_KEY_NAMES[BOOM_OS_KERNEL_PATTERN],
Packit 5acf3f
    BOOM_OS_INITRAMFS_PATTERN: OS_KEY_NAMES[BOOM_OS_INITRAMFS_PATTERN],
Packit 5acf3f
    BOOM_OS_ROOT_OPTS_LVM2: OS_KEY_NAMES[BOOM_OS_ROOT_OPTS_LVM2],
Packit 5acf3f
    BOOM_OS_ROOT_OPTS_BTRFS: OS_KEY_NAMES[BOOM_OS_ROOT_OPTS_BTRFS],
Packit 5acf3f
    BOOM_OS_OPTIONS: OS_KEY_NAMES[BOOM_OS_OPTIONS]
Packit 5acf3f
}
Packit 5acf3f
Packit 5acf3f
#: Boom host profile keys that must exist in a valid profile.
Packit 5acf3f
HOST_REQUIRED_KEYS = HOST_PROFILE_KEYS[0:4]
Packit 5acf3f
Packit 5acf3f
#: Boom optional host profile configuration keys.
Packit 5acf3f
HOST_OPTIONAL_KEYS = HOST_PROFILE_KEYS[4:]
Packit 5acf3f
Packit 5acf3f
Packit 5acf3f
def _host_exists(host_id):
Packit 5acf3f
    """Test whether the specified ``host_id`` already exists.
Packit 5acf3f
Packit 5acf3f
        Used during ``HostProfile`` initialisation to test if the new
Packit 5acf3f
        ``host_id`` is already known (and to avoid passing through
Packit 5acf3f
        find_profiles(), which may trigger recursive profile loading).
Packit 5acf3f
Packit 5acf3f
        :param host_id: the host identifier to check for
Packit 5acf3f
Packit 5acf3f
        :returns: ``True`` if the identifier is known or ``False``
Packit 5acf3f
                  otherwise.
Packit 5acf3f
        :rtype: bool
Packit 5acf3f
    """
Packit 5acf3f
    global _host_profiles_by_host_id
Packit 5acf3f
    if not _host_profiles_by_host_id:
Packit 5acf3f
        return False
Packit 5acf3f
    if host_id in _host_profiles_by_host_id:
Packit 5acf3f
        return True
Packit 5acf3f
    return False
Packit 5acf3f
Packit 5acf3f
Packit 5acf3f
def boom_host_profiles_path():
Packit 5acf3f
    """Return the path to the boom host profiles directory.
Packit 5acf3f
Packit 5acf3f
        :returns: The boom host profiles path.
Packit 5acf3f
        :rtype: str
Packit 5acf3f
    """
Packit 5acf3f
    return path_join(get_boom_path(), BOOM_HOST_PROFILES)
Packit 5acf3f
Packit 5acf3f
Packit 5acf3f
def host_profiles_loaded():
Packit 5acf3f
    """Test whether profiles have been loaded from disk.
Packit 5acf3f
Packit 5acf3f
        :rtype: bool
Packit 5acf3f
        :returns: ``True`` if profiles are loaded in memory or ``False``
Packit 5acf3f
                  otherwise
Packit 5acf3f
    """
Packit 5acf3f
    return _host_profiles_loaded
Packit 5acf3f
Packit 5acf3f
Packit 5acf3f
def drop_host_profiles():
Packit 5acf3f
    """Drop all in-memory host profiles.
Packit 5acf3f
    """
Packit 5acf3f
    global _host_profiles, _host_profiles_by_id, _host_profiles_by_host_id
Packit 5acf3f
    global _host_profiles_loaded
Packit 5acf3f
Packit 5acf3f
    _host_profiles = []
Packit 5acf3f
    _host_profiles_by_id = {}
Packit 5acf3f
    _host_profiles_by_host_id = {}
Packit 5acf3f
    _host_profiles_loaded = False
Packit 5acf3f
Packit 5acf3f
Packit 5acf3f
def load_host_profiles():
Packit 5acf3f
    """Load HostProfile data from disk.
Packit 5acf3f
Packit 5acf3f
        Load the set of host profiles found at the path
Packit 5acf3f
        ``boom.hostprofile.boom_profiles_path()`` into the global host
Packit 5acf3f
        profile list.
Packit 5acf3f
Packit 5acf3f
        This function may be called to explicitly load, or reload the
Packit 5acf3f
        set of profiles on-disk. Profiles are also loaded implicitly
Packit 5acf3f
        if an API function or method that requires access to profiles
Packit 5acf3f
        is invoked (for example, ``boom.bootloader.load_entries()``.
Packit 5acf3f
Packit 5acf3f
        :returns: None
Packit 5acf3f
    """
Packit 5acf3f
    global _host_profiles_loaded
Packit 5acf3f
    drop_host_profiles()
Packit 5acf3f
    profiles_path = boom_host_profiles_path()
Packit 5acf3f
    load_profiles_for_class(HostProfile, "Host", profiles_path, "host")
Packit 5acf3f
Packit 5acf3f
    _host_profiles_loaded = True
Packit 5acf3f
    _log_info("Loaded %d host profiles" % len(_host_profiles))
Packit 5acf3f
Packit 5acf3f
Packit 5acf3f
def write_host_profiles(force=False):
Packit 5acf3f
    """Write all HostProfile data to disk.
Packit 5acf3f
Packit 5acf3f
        Write the current list of host profiles to the directory located
Packit 5acf3f
        at ``boom.osprofile.boom_profiles_path()``.
Packit 5acf3f
Packit 5acf3f
        :rtype: None
Packit 5acf3f
    """
Packit 5acf3f
    global _host_profiles
Packit 5acf3f
    _log_debug("Writing host profiles to %s" % boom_host_profiles_path())
Packit 5acf3f
    for hp in _host_profiles:
Packit 5acf3f
        try:
Packit 5acf3f
            hp.write_profile(force)
Packit 5acf3f
        except Exception as e:
Packit 5acf3f
            _log_warn("Failed to write HostProfile(machine_id='%s'): %s" %
Packit 5acf3f
                      (hp.disp_machine_id, e))
Packit 5acf3f
Packit 5acf3f
Packit 5acf3f
def min_host_id_width():
Packit 5acf3f
    """Calculate the minimum unique width for host_id values.
Packit 5acf3f
Packit 5acf3f
        Calculate the minimum width to ensure uniqueness when displaying
Packit 5acf3f
        host_id values.
Packit 5acf3f
Packit 5acf3f
        :returns: the minimum host_id width.
Packit 5acf3f
        :rtype: int
Packit 5acf3f
    """
Packit 5acf3f
    return min_id_width(7, _host_profiles, "host_id")
Packit 5acf3f
Packit 5acf3f
Packit 5acf3f
def min_machine_id_width():
Packit 5acf3f
    """Calculate the minimum unique width for host_id values.
Packit 5acf3f
Packit 5acf3f
        Calculate the minimum width to ensure uniqueness when displaying
Packit 5acf3f
        host_id values.
Packit 5acf3f
Packit 5acf3f
        :returns: the minimum host_id width.
Packit 5acf3f
        :rtype: int
Packit 5acf3f
    """
Packit 5acf3f
    return min_id_width(7, _host_profiles, "machine_id")
Packit 5acf3f
Packit 5acf3f
Packit 5acf3f
def select_host_profile(s, hp):
Packit 5acf3f
    """Test the supplied host profile against selection criteria.
Packit 5acf3f
Packit 5acf3f
        Test the supplied ``HostProfile`` against the selection criteria
Packit 5acf3f
        in ``s`` and return ``True`` if it passes, or ``False``
Packit 5acf3f
        otherwise.
Packit 5acf3f
Packit 5acf3f
        :param s: The selection criteria
Packit 5acf3f
        :param hp: The ``HostProfile`` to test
Packit 5acf3f
        :rtype: bool
Packit 5acf3f
        :returns: True if ``hp`` passes selection or ``False`` otherwise.
Packit 5acf3f
    """
Packit 5acf3f
    if s.host_id and not hp.host_id.startswith(s.host_id):
Packit 5acf3f
        return False
Packit 5acf3f
    if s.machine_id and hp.machine_id != s.machine_id:
Packit 5acf3f
        return False
Packit 5acf3f
    if s.host_name and hp.host_name != s.host_name:
Packit 5acf3f
        return False
Packit 5acf3f
    if s.host_label and hp.label != s.host_label:
Packit 5acf3f
        return False
Packit 5acf3f
    if s.host_short_name and hp.short_name != s.host_short_name:
Packit 5acf3f
        return False
Packit 5acf3f
    if s.host_add_opts and hp.add_opts != s.host_add_opts:
Packit 5acf3f
        return False
Packit 5acf3f
    if s.host_del_opts and hp.del_opts != s.host_del_opts:
Packit 5acf3f
        return False
Packit 5acf3f
    if s.os_id and not hp.os_id.startswith(s.os_id):
Packit 5acf3f
        return False
Packit 5acf3f
    if s.os_name and hp.os_name != s.os_name:
Packit 5acf3f
        return False
Packit 5acf3f
    if s.os_short_name and hp.os_short_name != s.os_short_name:
Packit 5acf3f
        return False
Packit 5acf3f
    if s.os_version and hp.os_version != s.os_version:
Packit 5acf3f
        return False
Packit 5acf3f
    if s.os_version_id and hp.os_version_id != s.os_version_id:
Packit 5acf3f
        return False
Packit 5acf3f
    if s.os_uname_pattern and hp.uname_pattern != s.os_uname_pattern:
Packit 5acf3f
        return False
Packit 5acf3f
    if s.os_kernel_pattern and hp.kernel_pattern != s.os_kernel_pattern:
Packit 5acf3f
        return False
Packit 5acf3f
    if (s.os_initramfs_pattern and
Packit 5acf3f
            hp.initramfs_pattern != s.os_initramfs_pattern):
Packit 5acf3f
        return False
Packit 5acf3f
    if s.os_options and hp.options != s.os_options:
Packit 5acf3f
        return False
Packit 5acf3f
    return True
Packit 5acf3f
Packit 5acf3f
Packit 5acf3f
def find_host_profiles(selection=None, match_fn=select_host_profile):
Packit 5acf3f
    """Find host profiles matching selection criteria.
Packit 5acf3f
Packit 5acf3f
        Return a list of ``HostProfile`` objects matching the specified
Packit 5acf3f
        criteria. Matching proceeds as the logical 'and' of all criteria.
Packit 5acf3f
        Criteria that are unset (``None``) are ignored.
Packit 5acf3f
Packit 5acf3f
        If the optional ``match_fn`` parameter is specified, the match
Packit 5acf3f
        criteria parameters are ignored and each ``HostProfile`` is
Packit 5acf3f
        tested in turn by calling ``match_fn``. If the matching function
Packit 5acf3f
        returns ``True`` the ``HostProfile`` will be included in the
Packit 5acf3f
        results.
Packit 5acf3f
Packit 5acf3f
        If no ``HostProfile`` matches the specified criteria the empty
Packit 5acf3f
        list is returned.
Packit 5acf3f
Packit 5acf3f
        Host profiles will be automatically loaded from disk if they are
Packit 5acf3f
        not already in memory.
Packit 5acf3f
Packit 5acf3f
        :param selection: A ``Selection`` object specifying the match
Packit 5acf3f
                          criteria for the operation.
Packit 5acf3f
        :param match_fn: An optional match function to test profiles.
Packit 5acf3f
        :returns: a list of ``HostProfile`` objects.
Packit 5acf3f
        :rtype: list
Packit 5acf3f
    """
Packit 5acf3f
    # Use null search criteria if unspecified
Packit 5acf3f
    selection = selection if selection else Selection()
Packit 5acf3f
Packit 5acf3f
    selection.check_valid_selection(host=True)
Packit 5acf3f
Packit 5acf3f
    if not host_profiles_loaded():
Packit 5acf3f
        load_host_profiles()
Packit 5acf3f
Packit 5acf3f
    matches = []
Packit 5acf3f
Packit 5acf3f
    _log_debug_profile("Finding host profiles for %s" % repr(selection))
Packit 5acf3f
    for hp in _host_profiles:
Packit 5acf3f
        if match_fn(selection, hp):
Packit 5acf3f
            matches.append(hp)
Packit 5acf3f
    _log_debug_profile("Found %d host profiles" % len(matches))
Packit 5acf3f
    matches.sort(key=lambda h: h.host_name)
Packit 5acf3f
Packit 5acf3f
    return matches
Packit 5acf3f
Packit 5acf3f
Packit 5acf3f
def get_host_profile_by_id(machine_id, label=""):
Packit 5acf3f
    """Find a HostProfile by its machine_id.
Packit 5acf3f
Packit 5acf3f
        Return the HostProfile object corresponding to ``machine_id``,
Packit 5acf3f
        or ``None`` if it is not found.
Packit 5acf3f
Packit 5acf3f
        :rtype: HostProfile
Packit 5acf3f
        :returns: An HostProfile matching machine_id or None if no match
Packit 5acf3f
                  was found.
Packit 5acf3f
    """
Packit 5acf3f
    global _host_profiles, _host_profiles_by_id, _host_profiles_by_host_id
Packit 5acf3f
    if not host_profiles_loaded():
Packit 5acf3f
        load_host_profiles()
Packit 5acf3f
    if machine_id in _host_profiles_by_id:
Packit 5acf3f
        if label in _host_profiles_by_id[machine_id]:
Packit 5acf3f
            return _host_profiles_by_id[machine_id][label]
Packit 5acf3f
    return None
Packit 5acf3f
Packit 5acf3f
Packit 5acf3f
def match_host_profile(entry):
Packit 5acf3f
    """Attempt to match a BootEntry to a corresponding HostProfile.
Packit 5acf3f
Packit 5acf3f
        Attempt to find a loaded ``HostProfile`` object with the a
Packit 5acf3f
        ``machine_id`` that matches the supplied ``BootEntry``.
Packit 5acf3f
        Checking terminates on the first matching ``HostProfile``.
Packit 5acf3f
Packit 5acf3f
        :param entry: A ``BootEntry`` object with no attached
Packit 5acf3f
                      ``HostProfile``.
Packit 5acf3f
        :returns: The corresponding ``HostProfile`` for the supplied
Packit 5acf3f
                  ``BootEntry`` or ``None`` if no match is found.
Packit 5acf3f
        :rtype: ``BootEntry`` or ``NoneType``.
Packit 5acf3f
    """
Packit 5acf3f
    global _host_profiles, _host_profiles_loaded
Packit 5acf3f
Packit 5acf3f
    if not host_profiles_loaded():
Packit 5acf3f
        load_host_profiles()
Packit 5acf3f
Packit 5acf3f
    _log_debug("Attempting to match profile for BootEntry(title='%s', "
Packit 5acf3f
               "version='%s') with machine_id='%s'" %
Packit 5acf3f
               (entry.title, entry.version, entry.machine_id))
Packit 5acf3f
Packit 5acf3f
    # Attempt to match by uname pattern
Packit 5acf3f
    for hp in _host_profiles:
Packit 5acf3f
        if hp.machine_id == entry.machine_id:
Packit 5acf3f
            _log_debug("Matched BootEntry(version='%s', boot_id='%s') "
Packit 5acf3f
                       "to HostProfile(name='%s', machine_id='%s')" %
Packit 5acf3f
                       (entry.version, entry.disp_boot_id, hp.host_name,
Packit 5acf3f
                        hp.machine_id))
Packit 5acf3f
            return hp
Packit 5acf3f
Packit 5acf3f
    return None
Packit 5acf3f
Packit 5acf3f
Packit 5acf3f
class HostProfile(BoomProfile):
Packit 5acf3f
    """ Class HostProfile implements Boom host system profiles.
Packit 5acf3f
Packit 5acf3f
        Objects of type HostProfile define a host identiry, and optional
Packit 5acf3f
        fields or ``BootParams`` modifications to be applied to the
Packit 5acf3f
        specified host.
Packit 5acf3f
Packit 5acf3f
        Host profiles may modify any non-identity ``OsProfile`` key,
Packit 5acf3f
        either adding to or replacing the value defined by an embedded
Packit 5acf3f
        ``OsProfile`` instance.
Packit 5acf3f
    """
Packit 5acf3f
    _profile_data = None
Packit 5acf3f
    _unwritten = False
Packit 5acf3f
    _comments = None
Packit 5acf3f
Packit 5acf3f
    _profile_keys = HOST_PROFILE_KEYS
Packit 5acf3f
    _required_keys = HOST_REQUIRED_KEYS
Packit 5acf3f
    _identity_key = BOOM_HOST_ID
Packit 5acf3f
Packit 5acf3f
    _osp = None
Packit 5acf3f
Packit 5acf3f
    def _key_data(self, key):
Packit 5acf3f
        if key in self._profile_data:
Packit 5acf3f
            return self._profile_data[key]
Packit 5acf3f
        if key in self.osp._profile_data:
Packit 5acf3f
            return self.osp._profile_data[key]
Packit 5acf3f
        return None
Packit 5acf3f
Packit 5acf3f
    def _have_key(self, key):
Packit 5acf3f
        """Test for presence of a Host or Os profile key.
Packit 5acf3f
        """
Packit 5acf3f
        return key in self._profile_data or key in self.osp._profile_data
Packit 5acf3f
Packit 5acf3f
    def __str__(self):
Packit 5acf3f
        """Format this HostProfile as a human readable string.
Packit 5acf3f
Packit 5acf3f
            Profile attributes are printed as "Name: value, " pairs,
Packit 5acf3f
            with like attributes grouped together onto lines.
Packit 5acf3f
Packit 5acf3f
            :returns: A human readable string representation of this
Packit 5acf3f
                      HostProfile.
Packit 5acf3f
Packit 5acf3f
            :rtype: string
Packit 5acf3f
        """
Packit 5acf3f
        # FIXME HostProfile breaks
Packit 5acf3f
        breaks = [
Packit 5acf3f
            BOOM_HOST_ID, BOOM_HOST_NAME, BOOM_OS_ID, BOOM_ENTRY_MACHINE_ID,
Packit 5acf3f
            BOOM_HOST_LABEL, BOOM_OS_VERSION, BOOM_OS_UNAME_PATTERN,
Packit 5acf3f
            BOOM_HOST_DEL_OPTS, BOOM_OS_INITRAMFS_PATTERN,
Packit 5acf3f
            BOOM_OS_ROOT_OPTS_LVM2, BOOM_OS_ROOT_OPTS_BTRFS, BOOM_OS_OPTIONS
Packit 5acf3f
        ]
Packit 5acf3f
Packit 5acf3f
        fields = [f for f in HOST_PROFILE_KEYS if self._have_key(f)]
Packit 5acf3f
        hp_str = ""
Packit 5acf3f
        tail = ""
Packit 5acf3f
        for f in fields:
Packit 5acf3f
            hp_str += '%s: "%s"' % (HOST_KEY_NAMES[f], self._key_data(f))
Packit 5acf3f
            tail = ",\n" if f in breaks else ", "
Packit 5acf3f
            hp_str += tail
Packit 5acf3f
        hp_str = hp_str.rstrip(tail)
Packit 5acf3f
        return hp_str
Packit 5acf3f
Packit 5acf3f
    def __repr__(self):
Packit 5acf3f
        """Format this HostProfile as a machine readable string.
Packit 5acf3f
Packit 5acf3f
            Return a machine-readable representation of this ``HostProfile``
Packit 5acf3f
            object. The string is formatted as a call to the ``HostProfile``
Packit 5acf3f
            constructor, with values passed as a dictionary to the
Packit 5acf3f
            ``profile_data`` keyword argument.
Packit 5acf3f
Packit 5acf3f
            :returns: a string representation of this ``HostProfile``.
Packit 5acf3f
            :rtype: string
Packit 5acf3f
        """
Packit 5acf3f
        hp_str = "HostProfile(profile_data={"
Packit 5acf3f
        fields = [f for f in HOST_PROFILE_KEYS if self._have_key(f)]
Packit 5acf3f
        for f in fields:
Packit 5acf3f
            hp_str += '%s:"%s", ' % (f, self._key_data(f))
Packit 5acf3f
        hp_str = hp_str.rstrip(", ")
Packit 5acf3f
        return hp_str + "})"
Packit 5acf3f
Packit 5acf3f
    def __setitem__(self, key, value):
Packit 5acf3f
        """Set the specified ``HostProfile`` key to the given value.
Packit 5acf3f
Packit 5acf3f
            :param key: the ``HostProfile`` key to be set.
Packit 5acf3f
            :param value: the value to set for the specified key.
Packit 5acf3f
        """
Packit 5acf3f
Packit 5acf3f
        # FIXME: duplicated from OsProfile.__setitem__ -> factor
Packit 5acf3f
        # osprofile.check_format_key_value(key, value)
Packit 5acf3f
        # and include isstr() key name validation etc.
Packit 5acf3f
Packit 5acf3f
        # Map hp key names to a list of format keys which must not
Packit 5acf3f
        # appear in that key's value: e.g. %{kernel} in the kernel
Packit 5acf3f
        # pattern profile key.
Packit 5acf3f
        bad_key_map = {
Packit 5acf3f
            BOOM_OS_KERNEL_PATTERN: [FMT_KERNEL],
Packit 5acf3f
            BOOM_OS_INITRAMFS_PATTERN: [FMT_INITRAMFS],
Packit 5acf3f
            BOOM_OS_ROOT_OPTS_LVM2: [FMT_ROOT_OPTS],
Packit 5acf3f
            BOOM_OS_ROOT_OPTS_BTRFS: [FMT_ROOT_OPTS],
Packit 5acf3f
        }
Packit 5acf3f
Packit 5acf3f
        def _check_format_key_value(key, value, bad_keys):
Packit 5acf3f
            for bad_key in bad_keys:
Packit 5acf3f
                if bad_key in value:
Packit 5acf3f
                    raise ValueError("HostProfile.%s cannot contain %s"
Packit 5acf3f
                                     % (key, key_from_key_name(bad_key)))
Packit 5acf3f
Packit 5acf3f
        if not isinstance(key, str):
Packit 5acf3f
            raise TypeError("HostProfile key must be a string.")
Packit 5acf3f
Packit 5acf3f
        if key not in HOST_PROFILE_KEYS:
Packit 5acf3f
            raise ValueError("Invalid HostProfile key: %s" % key)
Packit 5acf3f
Packit 5acf3f
        if key in bad_key_map:
Packit 5acf3f
            _check_format_key_value(key, value, bad_key_map[key])
Packit 5acf3f
Packit 5acf3f
        self._profile_data[key] = value
Packit 5acf3f
Packit 5acf3f
    def _generate_id(self):
Packit 5acf3f
        """Generate a new host identifier.
Packit 5acf3f
Packit 5acf3f
            Generate a new sha1 profile identifier for this profile,
Packit 5acf3f
            using the name, machine_id, and os_id and store it in
Packit 5acf3f
            _profile_data.
Packit 5acf3f
Packit 5acf3f
            :returns: None
Packit 5acf3f
        """
Packit 5acf3f
        hashdata = (self.machine_id + self.label)
Packit 5acf3f
Packit 5acf3f
        digest = sha1(hashdata.encode('utf-8')).hexdigest()
Packit 5acf3f
        self._profile_data[BOOM_HOST_ID] = digest
Packit 5acf3f
Packit 5acf3f
    def __set_os_profile(self):
Packit 5acf3f
        """Set this ``HostProfile``'s ``osp`` member to the
Packit 5acf3f
            corresponding profile for the set ``os_id``.
Packit 5acf3f
        """
Packit 5acf3f
        os_id = self._profile_data[BOOM_OS_ID]
Packit 5acf3f
        osps = find_profiles(Selection(os_id=os_id))
Packit 5acf3f
        if not osps:
Packit 5acf3f
            raise ValueError("OsProfile not found: %s" % os_id)
Packit 5acf3f
        if len(osps) > 1:
Packit 5acf3f
            raise ValueError("OsProfile identifier '%s' is ambiguous" % os_id)
Packit 5acf3f
Packit 5acf3f
        self.osp = osps[0]
Packit 5acf3f
Packit 5acf3f
    def _append_profile(self):
Packit 5acf3f
        """Append a HostProfile to the global profile list
Packit 5acf3f
        """
Packit 5acf3f
        global _host_profiles, _host_profiles_by_id, _host_profiles_by_host_id
Packit 5acf3f
        if _host_exists(self.host_id):
Packit 5acf3f
            raise ValueError("Profile already exists (host_id=%s)" %
Packit 5acf3f
                             self.disp_host_id)
Packit 5acf3f
Packit 5acf3f
        _host_profiles.append(self)
Packit 5acf3f
        machine_id = self.machine_id
Packit 5acf3f
        if machine_id not in _host_profiles_by_id:
Packit 5acf3f
            _host_profiles_by_id[machine_id] = {}
Packit 5acf3f
        _host_profiles_by_id[machine_id][self.label] = self
Packit 5acf3f
        _host_profiles_by_host_id[self.host_id] = self
Packit 5acf3f
Packit 5acf3f
    def _from_data(self, host_data, dirty=True):
Packit 5acf3f
        """Initialise a ``HostProfile`` from in-memory data.
Packit 5acf3f
Packit 5acf3f
            Initialise a new ``HostProfile`` object using the profile
Packit 5acf3f
            data in the `host_data` dictionary.
Packit 5acf3f
Packit 5acf3f
            This method should not be called directly: to build a new
Packit 5acf3f
            `Hostprofile`` object from in-memory data, use the class
Packit 5acf3f
            initialiser with the ``host_data`` argument.
Packit 5acf3f
Packit 5acf3f
            :returns: None
Packit 5acf3f
        """
Packit 5acf3f
        err_str = "Invalid profile data (missing %s)"
Packit 5acf3f
Packit 5acf3f
        for key in HOST_REQUIRED_KEYS:
Packit 5acf3f
            if key == BOOM_HOST_ID:
Packit 5acf3f
                continue
Packit 5acf3f
            if key not in host_data:
Packit 5acf3f
                raise ValueError(err_str % key)
Packit 5acf3f
Packit 5acf3f
        self._profile_data = dict(host_data)
Packit 5acf3f
Packit 5acf3f
        if BOOM_HOST_ID not in self._profile_data:
Packit 5acf3f
            self._generate_id()
Packit 5acf3f
Packit 5acf3f
        self.__set_os_profile()
Packit 5acf3f
Packit 5acf3f
        if dirty:
Packit 5acf3f
            self._dirty()
Packit 5acf3f
Packit 5acf3f
        self._append_profile()
Packit 5acf3f
Packit 5acf3f
    def __init__(self, machine_id=None, host_name=None, label=None, os_id=None,
Packit 5acf3f
                 kernel_pattern=None, initramfs_pattern=None,
Packit 5acf3f
                 root_opts_lvm2=None, root_opts_btrfs=None,
Packit 5acf3f
                 add_opts="", del_opts="",
Packit 5acf3f
                 options=None, profile_file=None, profile_data=None):
Packit 5acf3f
        """Initialise a new ``HostProfile`` object.
Packit 5acf3f
Packit 5acf3f
            If neither ``profile_file`` nor ``profile_data`` is given,
Packit 5acf3f
            all of ``machine_id``, ``name``, and ``os_id`` must be given.
Packit 5acf3f
Packit 5acf3f
            These values form the host profile identity and are used to
Packit 5acf3f
            generate the profile unique identifier.
Packit 5acf3f
Packit 5acf3f
            :param host_name: The hostname of this system
Packit 5acf3f
            :param os_id: An OS identifier specifying the ``OsProfile``
Packit 5acf3f
                          to use with this host profile.
Packit 5acf3f
            :param profile_data: An optional dictionary mapping from
Packit 5acf3f
                                 ``BOOM_*`` keys to profile values.
Packit 5acf3f
            :param profile_file: An optional path to a file from which
Packit 5acf3f
                                 profile data should be loaded. The file
Packit 5acf3f
                                 should be in Boom host profile format,
Packit 5acf3f
                                 with ``BOOM_*`` key=value pairs.
Packit 5acf3f
            :returns: A new ``HostProfile`` object.
Packit 5acf3f
            :rtype: class HostProfile
Packit 5acf3f
        """
Packit 5acf3f
        global _host_profiles
Packit 5acf3f
        self._profile_data = {}
Packit 5acf3f
Packit 5acf3f
        # Initialise BoomProfile base class
Packit 5acf3f
        super(HostProfile, self).__init__(HOST_PROFILE_KEYS,
Packit 5acf3f
                                          HOST_REQUIRED_KEYS, BOOM_HOST_ID)
Packit 5acf3f
Packit 5acf3f
        if profile_data and profile_file:
Packit 5acf3f
            raise ValueError("Only one of 'profile_data' or 'profile_file' "
Packit 5acf3f
                             "may be specified.")
Packit 5acf3f
Packit 5acf3f
        if profile_data:
Packit 5acf3f
            self._from_data(profile_data)
Packit 5acf3f
            return
Packit 5acf3f
        if profile_file:
Packit 5acf3f
            self._from_file(profile_file)
Packit 5acf3f
            return
Packit 5acf3f
Packit 5acf3f
        self._dirty()
Packit 5acf3f
Packit 5acf3f
        required_args = [machine_id, host_name, os_id]
Packit 5acf3f
        if any([not val for val in required_args]):
Packit 5acf3f
            raise ValueError("Invalid host profile arguments: machine_id, "
Packit 5acf3f
                             "host_name, and os_id are mandatory.")
Packit 5acf3f
Packit 5acf3f
        osps = find_profiles(Selection(os_id=os_id))
Packit 5acf3f
        if not osps:
Packit 5acf3f
            raise ValueError("No matching profile found for os_id=%s" % os_id)
Packit 5acf3f
        if len(osps) > 1:
Packit 5acf3f
            raise ValueError("OsProfile ID is ambiguous: %s" % os_id)
Packit 5acf3f
        os_id = osps[0].os_id
Packit 5acf3f
Packit 5acf3f
        self._profile_data[BOOM_ENTRY_MACHINE_ID] = machine_id
Packit 5acf3f
        self._profile_data[BOOM_HOST_NAME] = host_name
Packit 5acf3f
        self._profile_data[BOOM_OS_ID] = os_id
Packit 5acf3f
        self._profile_data[BOOM_HOST_LABEL] = label
Packit 5acf3f
Packit 5acf3f
        # Only set keys that have a value in the host profile data dict
Packit 5acf3f
        if kernel_pattern:
Packit 5acf3f
            self._profile_data[BOOM_OS_KERNEL_PATTERN] = kernel_pattern
Packit 5acf3f
        if initramfs_pattern:
Packit 5acf3f
            self._profile_data[BOOM_OS_INITRAMFS_PATTERN] = initramfs_pattern
Packit 5acf3f
        if root_opts_lvm2:
Packit 5acf3f
            self._profile_data[BOOM_OS_ROOT_OPTS_LVM2] = root_opts_lvm2
Packit 5acf3f
        if root_opts_btrfs:
Packit 5acf3f
            self._profile_data[BOOM_OS_ROOT_OPTS_BTRFS] = root_opts_btrfs
Packit 5acf3f
        if add_opts:
Packit 5acf3f
            self._profile_data[BOOM_HOST_ADD_OPTS] = add_opts
Packit 5acf3f
        if del_opts:
Packit 5acf3f
            self._profile_data[BOOM_HOST_DEL_OPTS] = del_opts
Packit 5acf3f
        if options:
Packit 5acf3f
            self._profile_data[BOOM_OS_OPTIONS] = options
Packit 5acf3f
Packit 5acf3f
        self.__set_os_profile()
Packit 5acf3f
Packit 5acf3f
        self._generate_id()
Packit 5acf3f
        _host_profiles.append(self)
Packit 5acf3f
Packit 5acf3f
    # We use properties for the HostProfile attributes: this is to
Packit 5acf3f
    # allow the values to be stored in a dictionary. Although
Packit 5acf3f
    # properties are quite verbose this reduces the code volume
Packit 5acf3f
    # and complexity needed to marshal and unmarshal the various
Packit 5acf3f
    # file formats used, as well as conversion to and from string
Packit 5acf3f
    # representations of HostProfile objects.
Packit 5acf3f
Packit 5acf3f
    # Keys obtained from os-release data form the profile's identity:
Packit 5acf3f
    # the corresponding attributes are read-only.
Packit 5acf3f
Packit 5acf3f
    # HostProfile properties:
Packit 5acf3f
    #
Packit 5acf3f
    #  Profile identity properties (ro):
Packit 5acf3f
    #   host_id
Packit 5acf3f
    #   disp_os_id
Packit 5acf3f
    #
Packit 5acf3f
    #  Profile identity properties (rw):
Packit 5acf3f
    #   host_name
Packit 5acf3f
    #   machine_id
Packit 5acf3f
    #   os_id
Packit 5acf3f
    #
Packit 5acf3f
    #  Properties mapped to OsProfile (ro)
Packit 5acf3f
    #   os_name
Packit 5acf3f
    #   os_short_name
Packit 5acf3f
    #   os_version
Packit 5acf3f
    #   os_version_id
Packit 5acf3f
    #   uname_pattern
Packit 5acf3f
    #
Packit 5acf3f
    # Properties overridden or mapped to OsProfile (rw)
Packit 5acf3f
    #   kernel_pattern
Packit 5acf3f
    #   initramfs_pattern
Packit 5acf3f
    #   root_opts_lvm2
Packit 5acf3f
    #   root_opts_btrfs
Packit 5acf3f
    #   options
Packit 5acf3f
    #
Packit 5acf3f
    # HostProfile specific properties (rw)
Packit 5acf3f
    #   label
Packit 5acf3f
    #   add_opts
Packit 5acf3f
    #   del_opts
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def disp_os_id(self):
Packit 5acf3f
        """The display os_id of this profile.
Packit 5acf3f
Packit 5acf3f
            Return the shortest prefix of this OsProfile's os_id that
Packit 5acf3f
            is unique within the current set of loaded profiles.
Packit 5acf3f
Packit 5acf3f
            :getter: return this OsProfile's os_id.
Packit 5acf3f
            :type: str
Packit 5acf3f
        """
Packit 5acf3f
        return self.osp.disp_os_id
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def host_id(self):
Packit 5acf3f
        if BOOM_HOST_ID not in self._profile_data:
Packit 5acf3f
            self._generate_id()
Packit 5acf3f
        return self._profile_data[BOOM_HOST_ID]
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def disp_host_id(self):
Packit 5acf3f
        """The display host_id of this profile
Packit 5acf3f
Packit 5acf3f
            Return the shortest prefix of this HostProfile's os_id that
Packit 5acf3f
            is unique within the current set of loaded profiles.
Packit 5acf3f
Packit 5acf3f
            :getter: return this HostProfile's display host_id.
Packit 5acf3f
            :type: str
Packit 5acf3f
        """
Packit 5acf3f
        return self.host_id[:min_host_id_width()]
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def disp_machine_id(self):
Packit 5acf3f
        """The machine_id of this host profile.
Packit 5acf3f
            Return the shortest prefix of this HostProfile's os_id that
Packit 5acf3f
            is unique within the current set of loaded profiles.
Packit 5acf3f
Packit 5acf3f
            :getter: return this HostProfile's display host_id.
Packit 5acf3f
            :type: str
Packit 5acf3f
        """
Packit 5acf3f
        return self.machine_id[:min_machine_id_width()]
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def machine_id(self):
Packit 5acf3f
        """The machine_id of this host profile.
Packit 5acf3f
            Return the shortest prefix of this HostProfile's os_id that
Packit 5acf3f
            is unique within the current set of loaded profiles.
Packit 5acf3f
Packit 5acf3f
            :getter: return this ``HostProfile``'s display host_id.
Packit 5acf3f
            :setter: change this ``HostProfile``'s ``machine_id``. This
Packit 5acf3f
                     will change the ``host_id``.
Packit 5acf3f
            :type: str
Packit 5acf3f
        """
Packit 5acf3f
        return self._profile_data[BOOM_ENTRY_MACHINE_ID]
Packit 5acf3f
Packit 5acf3f
    @machine_id.setter
Packit 5acf3f
    def machine_id(self, value):
Packit 5acf3f
        if value == self._profile_data[BOOM_ENTRY_MACHINE_ID]:
Packit 5acf3f
            return
Packit 5acf3f
        self._profile_data[BOOM_ENTRY_MACHINE_ID] = value
Packit 5acf3f
        self._dirty()
Packit 5acf3f
        self._generate_id()
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def os_id(self):
Packit 5acf3f
        """The ``os_id`` of this profile.
Packit 5acf3f
Packit 5acf3f
            :getter: returns the ``os_id`` as a string.
Packit 5acf3f
            :type: string
Packit 5acf3f
        """
Packit 5acf3f
        return self.osp.os_id
Packit 5acf3f
Packit 5acf3f
    @os_id.setter
Packit 5acf3f
    def os_id(self, value):
Packit 5acf3f
        if value == self._profile_data[BOOM_OS_ID]:
Packit 5acf3f
            return
Packit 5acf3f
        self._profile_data[BOOM_OS_ID] = value
Packit 5acf3f
        self.__set_os_profile()
Packit 5acf3f
        self._dirty()
Packit 5acf3f
        self._generate_id()
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def osp(self):
Packit 5acf3f
        """The ``OsProfile`` used by this ``HostProfile``.
Packit 5acf3f
Packit 5acf3f
            :getter: returns the ``OsProfile`` object used by this
Packit 5acf3f
                     ``HostProfile``.
Packit 5acf3f
            :setter: stores a new ``OsProfile`` for use by this
Packit 5acf3f
                     ``HostProfile`` and updates the stored ``os_id``
Packit 5acf3f
                     value in the host profile.
Packit 5acf3f
        """
Packit 5acf3f
        return self._osp
Packit 5acf3f
Packit 5acf3f
    @osp.setter
Packit 5acf3f
    def osp(self, osp):
Packit 5acf3f
        if self._osp and osp.os_id == self._osp.os_id:
Packit 5acf3f
            return
Packit 5acf3f
        self._osp = osp
Packit 5acf3f
        self._profile_data[BOOM_OS_ID] = osp.os_id
Packit 5acf3f
        self._dirty()
Packit 5acf3f
        self._generate_id()
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def host_name(self):
Packit 5acf3f
        """The ``host_name`` of this profile.
Packit 5acf3f
Packit 5acf3f
            Normally set to the hostname of the system corresponding to
Packit 5acf3f
            this ``HostProfile``.
Packit 5acf3f
Packit 5acf3f
            :getter: returns the ``host_name`` as a string.
Packit 5acf3f
            :type: string
Packit 5acf3f
        """
Packit 5acf3f
        return self._profile_data[BOOM_HOST_NAME]
Packit 5acf3f
Packit 5acf3f
    @host_name.setter
Packit 5acf3f
    def host_name(self, value):
Packit 5acf3f
        if value == self._profile_data[BOOM_HOST_NAME]:
Packit 5acf3f
            return
Packit 5acf3f
        self._profile_data[BOOM_HOST_NAME] = value
Packit 5acf3f
        self._dirty()
Packit 5acf3f
        self._generate_id()
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def short_name(self):
Packit 5acf3f
        """The ``short_name`` of this profile.
Packit 5acf3f
Packit 5acf3f
            If ``HostProfile.host_name`` appears to contain a DNS-style name,
Packit 5acf3f
            return only the host portion.
Packit 5acf3f
Packit 5acf3f
            :getter: returns the ``short_name`` as a string.
Packit 5acf3f
            :type: string
Packit 5acf3f
        """
Packit 5acf3f
        host_name = self._profile_data[BOOM_HOST_NAME]
Packit 5acf3f
        return host_name.split(".")[0] if "." in host_name else host_name
Packit 5acf3f
Packit 5acf3f
    #
Packit 5acf3f
    #  Properties mapped to OsProfile
Packit 5acf3f
    #
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def os_name(self):
Packit 5acf3f
        """The ``os_name`` of this profile.
Packit 5acf3f
Packit 5acf3f
            :getter: returns the ``os_name`` as a string.
Packit 5acf3f
            :type: string
Packit 5acf3f
        """
Packit 5acf3f
        return self.osp.os_name
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def os_short_name(self):
Packit 5acf3f
        """The ``os_short_name`` of this profile.
Packit 5acf3f
Packit 5acf3f
            :getter: returns the ``os_short_name`` as a string.
Packit 5acf3f
            :type: string
Packit 5acf3f
        """
Packit 5acf3f
        return self.osp.os_short_name
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def os_version(self):
Packit 5acf3f
        """The ``os_version`` of this profile.
Packit 5acf3f
Packit 5acf3f
            :getter: returns the ``os_version`` as a string.
Packit 5acf3f
            :type: string
Packit 5acf3f
        """
Packit 5acf3f
        return self.osp.os_version
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def os_version_id(self):
Packit 5acf3f
        """The ``os_version_id`` of this profile.
Packit 5acf3f
Packit 5acf3f
            :getter: returns the ``os_version_id`` as a string.
Packit 5acf3f
            :type: string
Packit 5acf3f
        """
Packit 5acf3f
        return self.osp.os_version_id
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def uname_pattern(self):
Packit 5acf3f
        """The current ``uname_pattern`` setting of this profile.
Packit 5acf3f
Packit 5acf3f
            :getter: returns the ``uname_pattern`` as a string.
Packit 5acf3f
            :setter: stores a new ``uname_pattern`` setting.
Packit 5acf3f
            :type: string
Packit 5acf3f
        """
Packit 5acf3f
        if BOOM_OS_UNAME_PATTERN in self._profile_data:
Packit 5acf3f
            return self._profile_data[BOOM_OS_UNAME_PATTERN]
Packit 5acf3f
        return self.osp.uname_pattern
Packit 5acf3f
Packit 5acf3f
    #
Packit 5acf3f
    # Properties overridden or mapped to OsProfile
Packit 5acf3f
    #
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def kernel_pattern(self):
Packit 5acf3f
        """The current ``kernel_pattern`` setting of this profile.
Packit 5acf3f
Packit 5acf3f
            :getter: returns the ``kernel_pattern`` as a string.
Packit 5acf3f
            :setter: stores a new ``kernel_pattern`` setting.
Packit 5acf3f
            :type: string
Packit 5acf3f
        """
Packit 5acf3f
        if BOOM_OS_KERNEL_PATTERN in self._profile_data:
Packit 5acf3f
            return self._profile_data[BOOM_OS_KERNEL_PATTERN]
Packit 5acf3f
        return self.osp.kernel_pattern
Packit 5acf3f
Packit 5acf3f
    @kernel_pattern.setter
Packit 5acf3f
    def kernel_pattern(self, value):
Packit 5acf3f
        kernel_key = key_from_key_name(FMT_KERNEL)
Packit 5acf3f
        if kernel_key in value:
Packit 5acf3f
            raise ValueError("HostProfile.kernel cannot contain %s" %
Packit 5acf3f
                             kernel_key)
Packit 5acf3f
        self._profile_data[BOOM_OS_KERNEL_PATTERN] = value
Packit 5acf3f
        self._dirty()
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def initramfs_pattern(self):
Packit 5acf3f
        """The current ``initramfs_pattern`` setting of this profile.
Packit 5acf3f
Packit 5acf3f
            :getter: returns the ``initramfs_pattern`` as a string.
Packit 5acf3f
            :setter: store a new ``initramfs_pattern`` setting.
Packit 5acf3f
            :type: string
Packit 5acf3f
        """
Packit 5acf3f
        if BOOM_OS_INITRAMFS_PATTERN in self._profile_data:
Packit 5acf3f
            return self._profile_data[BOOM_OS_INITRAMFS_PATTERN]
Packit 5acf3f
        return self.osp.initramfs_pattern
Packit 5acf3f
Packit 5acf3f
    @initramfs_pattern.setter
Packit 5acf3f
    def initramfs_pattern(self, value):
Packit 5acf3f
        initramfs_key = key_from_key_name(FMT_INITRAMFS)
Packit 5acf3f
        if initramfs_key in value:
Packit 5acf3f
            raise ValueError("HostProfile.initramfs cannot contain %s" %
Packit 5acf3f
                             initramfs_key)
Packit 5acf3f
        self._profile_data[BOOM_OS_INITRAMFS_PATTERN] = value
Packit 5acf3f
        self._dirty()
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def root_opts_lvm2(self):
Packit 5acf3f
        """The current LVM2 root options setting of this profile.
Packit 5acf3f
Packit 5acf3f
            :getter: returns the ``root_opts_lvm2`` value as a string.
Packit 5acf3f
            :setter: store a new ``root_opts_lvm2`` value.
Packit 5acf3f
            :type: string
Packit 5acf3f
        """
Packit 5acf3f
        if BOOM_OS_ROOT_OPTS_LVM2 in self._profile_data:
Packit 5acf3f
            return self._profile_data[BOOM_OS_ROOT_OPTS_LVM2]
Packit 5acf3f
Packit 5acf3f
        return self.osp.root_opts_lvm2
Packit 5acf3f
Packit 5acf3f
    @root_opts_lvm2.setter
Packit 5acf3f
    def root_opts_lvm2(self, value):
Packit 5acf3f
        root_opts_key = key_from_key_name(FMT_ROOT_OPTS)
Packit 5acf3f
        if root_opts_key in value:
Packit 5acf3f
            raise ValueError("HostProfile.root_opts_lvm2 cannot contain ""%s" %
Packit 5acf3f
                             root_opts_key)
Packit 5acf3f
        self._profile_data[BOOM_OS_ROOT_OPTS_LVM2] = value
Packit 5acf3f
        self._dirty()
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def root_opts_btrfs(self):
Packit 5acf3f
        """The current BTRFS root options setting of this profile.
Packit 5acf3f
Packit 5acf3f
            :getter: returns the ``root_opts_btrfs`` value as a string.
Packit 5acf3f
            :setter: store a new ``root_opts_btrfs`` value.
Packit 5acf3f
            :type: string
Packit 5acf3f
        """
Packit 5acf3f
        if BOOM_OS_ROOT_OPTS_BTRFS in self._profile_data:
Packit 5acf3f
            return self._profile_data[BOOM_OS_ROOT_OPTS_BTRFS]
Packit 5acf3f
        return self.osp.root_opts_btrfs
Packit 5acf3f
Packit 5acf3f
    @root_opts_btrfs.setter
Packit 5acf3f
    def root_opts_btrfs(self, value):
Packit 5acf3f
        root_opts_key = key_from_key_name(FMT_ROOT_OPTS)
Packit 5acf3f
        if root_opts_key in value:
Packit 5acf3f
            raise ValueError("HostProfile.root_opts_btrfs cannot contain %s" %
Packit 5acf3f
                             root_opts_key)
Packit 5acf3f
        self._profile_data[BOOM_OS_ROOT_OPTS_BTRFS] = value
Packit 5acf3f
        self._dirty()
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def options(self):
Packit 5acf3f
        """The current kernel command line options setting for this
Packit 5acf3f
            profile.
Packit 5acf3f
Packit 5acf3f
            :getter: returns the ``options`` value as a string.
Packit 5acf3f
            :setter: store a new ``options`` value.
Packit 5acf3f
            :type: string
Packit 5acf3f
        """
Packit 5acf3f
        if BOOM_OS_OPTIONS in self._profile_data:
Packit 5acf3f
            return self._profile_data[BOOM_OS_OPTIONS]
Packit 5acf3f
        return self.osp.options
Packit 5acf3f
Packit 5acf3f
    @options.setter
Packit 5acf3f
    def options(self, value):
Packit 5acf3f
        self._profile_data[BOOM_OS_OPTIONS] = value
Packit 5acf3f
        self._dirty()
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def title(self):
Packit 5acf3f
        """The current title template for this profile.
Packit 5acf3f
Packit 5acf3f
            :getter: returns the ``title`` value as a string.
Packit 5acf3f
            :setter: store a new ``title`` value.
Packit 5acf3f
            :type: string
Packit 5acf3f
        """
Packit 5acf3f
        if BOOM_OS_TITLE not in self._profile_data:
Packit 5acf3f
            return None
Packit 5acf3f
        return self._profile_data[BOOM_OS_TITLE]
Packit 5acf3f
Packit 5acf3f
    @title.setter
Packit 5acf3f
    def title(self, value):
Packit 5acf3f
        if not value:
Packit 5acf3f
            # It is valid to set an empty title in a HostProfile as long
Packit 5acf3f
            # as the OsProfile defines one.
Packit 5acf3f
            if not self.osp or not self.osp.title:
Packit 5acf3f
                raise ValueError("Entry title cannot be empty")
Packit 5acf3f
        self._profile_data[BOOM_OS_TITLE] = value
Packit 5acf3f
        self._dirty()
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def optional_keys(self):
Packit 5acf3f
        if not self.osp or not self.osp.optional_keys:
Packit 5acf3f
            return ""
Packit 5acf3f
        return self.osp.optional_keys
Packit 5acf3f
Packit 5acf3f
    #
Packit 5acf3f
    # HostProfile specific properties
Packit 5acf3f
    #
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def add_opts(self):
Packit 5acf3f
        if BOOM_HOST_ADD_OPTS in self._profile_data:
Packit 5acf3f
            return self._profile_data[BOOM_HOST_ADD_OPTS]
Packit 5acf3f
        return ""
Packit 5acf3f
Packit 5acf3f
    @add_opts.setter
Packit 5acf3f
    def add_opts(self, opts):
Packit 5acf3f
        self._profile_data[BOOM_HOST_ADD_OPTS] = opts
Packit 5acf3f
        self._dirty()
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def del_opts(self):
Packit 5acf3f
        if BOOM_HOST_DEL_OPTS in self._profile_data:
Packit 5acf3f
            return self._profile_data[BOOM_HOST_DEL_OPTS]
Packit 5acf3f
        return ""
Packit 5acf3f
Packit 5acf3f
    @del_opts.setter
Packit 5acf3f
    def del_opts(self, opts):
Packit 5acf3f
        self._profile_data[BOOM_HOST_DEL_OPTS] = opts
Packit 5acf3f
        self._dirty()
Packit 5acf3f
Packit 5acf3f
    @property
Packit 5acf3f
    def label(self):
Packit 5acf3f
        if BOOM_HOST_LABEL in self._profile_data:
Packit 5acf3f
            return self._profile_data[BOOM_HOST_LABEL]
Packit 5acf3f
        return ""
Packit 5acf3f
Packit 5acf3f
    @label.setter
Packit 5acf3f
    def label(self, value):
Packit 5acf3f
        valid_chars = string.ascii_letters + string.digits + "_- "
Packit 5acf3f
Packit 5acf3f
        if BOOM_HOST_LABEL in self._profile_data:
Packit 5acf3f
            if self._profile_data[BOOM_HOST_LABEL] == value:
Packit 5acf3f
                return
Packit 5acf3f
Packit 5acf3f
        for c in value:
Packit 5acf3f
            if c not in valid_chars:
Packit 5acf3f
                raise ValueError("Invalid host label character: '%s'" % c)
Packit 5acf3f
Packit 5acf3f
        self._profile_data[BOOM_HOST_LABEL] = value
Packit 5acf3f
        self._dirty()
Packit 5acf3f
        self._generate_id()
Packit 5acf3f
Packit 5acf3f
    def _profile_path(self):
Packit 5acf3f
        """Return the path to this profile's on-disk data.
Packit 5acf3f
Packit 5acf3f
            Return the full path to this HostProfile in the Boom profiles
Packit 5acf3f
            directory (or the location to which it will be written, if
Packit 5acf3f
            it has not yet been written).
Packit 5acf3f
Packit 5acf3f
            :rtype: str
Packit 5acf3f
            :returns: The absolute path for this HostProfile's file
Packit 5acf3f
        """
Packit 5acf3f
        if self.label:
Packit 5acf3f
            label = self.label
Packit 5acf3f
            if " " in label:
Packit 5acf3f
                label = label.replace(" ", "_")
Packit 5acf3f
            names = (self.short_name, label)
Packit 5acf3f
            name_fmt = "%s-%s"
Packit 5acf3f
        else:
Packit 5acf3f
            names = (self.short_name)
Packit 5acf3f
            name_fmt = "%s"
Packit 5acf3f
        profile_name = name_fmt % names
Packit 5acf3f
        profile_id = (self.host_id, profile_name)
Packit 5acf3f
        profile_path_name = BOOM_HOST_PROFILE_FORMAT % profile_id
Packit 5acf3f
        return path_join(boom_host_profiles_path(), profile_path_name)
Packit 5acf3f
Packit 5acf3f
    def write_profile(self, force=False):
Packit 5acf3f
        """Write out profile data to disk.
Packit 5acf3f
Packit 5acf3f
            Write out this ``HostProfile``'s data to a file in Boom
Packit 5acf3f
            format to the paths specified by the current configuration.
Packit 5acf3f
Packit 5acf3f
            Currently the ``machine_id`` and ``name`` keys are used to
Packit 5acf3f
            construct the file name.
Packit 5acf3f
Packit 5acf3f
            If the value of ``force`` is ``False`` and the ``HostProfile``
Packit 5acf3f
            is not currently marked as dirty (either new, or modified
Packit 5acf3f
            since the last load operation) the write will be skipped.
Packit 5acf3f
Packit 5acf3f
            :param force: Force this profile to be written to disk even
Packit 5acf3f
                          if the entry is unmodified.
Packit 5acf3f
            :raises: ``OsError`` if the temporary entry file cannot be
Packit 5acf3f
                     renamed, or if setting file permissions on the
Packit 5acf3f
                     new entry file fails.
Packit 5acf3f
        """
Packit 5acf3f
        path = boom_host_profiles_path()
Packit 5acf3f
        mode = BOOM_HOST_PROFILE_MODE
Packit 5acf3f
        self._write_profile(self.host_id, path, mode, force=force)
Packit 5acf3f
Packit 5acf3f
    def delete_profile(self):
Packit 5acf3f
        """Delete on-disk data for this profile.
Packit 5acf3f
Packit 5acf3f
            Remove the on-disk profile corresponding to this
Packit 5acf3f
            ``HostProfile`` object. This will permanently erase the
Packit 5acf3f
            current file (although the current data may be re-written at
Packit 5acf3f
            any time by calling ``write_profile()`` before the object is
Packit 5acf3f
            disposed of).
Packit 5acf3f
Packit 5acf3f
            :rtype: ``NoneType``
Packit 5acf3f
            :raises: ``OsError`` if an error occurs removing the file or
Packit 5acf3f
                     ``ValueError`` if the profile does not exist.
Packit 5acf3f
        """
Packit 5acf3f
        global _host_profiles, _host_profiles_by_id, _host_profiles_by_host_id
Packit 5acf3f
        self._delete_profile(self.host_id)
Packit 5acf3f
Packit 5acf3f
        machine_id = self.machine_id
Packit 5acf3f
        host_id = self.host_id
Packit 5acf3f
        if _host_profiles and self in _host_profiles:
Packit 5acf3f
            _host_profiles.remove(self)
Packit 5acf3f
        if _host_profiles_by_id and machine_id in _host_profiles_by_id:
Packit 5acf3f
            _host_profiles_by_id.pop(machine_id)
Packit 5acf3f
        if _host_profiles_by_host_id and host_id in _host_profiles_by_host_id:
Packit 5acf3f
            _host_profiles_by_host_id.pop(host_id)
Packit 5acf3f
Packit 5acf3f
Packit 5acf3f
__all__ = [
Packit 5acf3f
    # Host profiles
Packit 5acf3f
    'HostProfile',
Packit 5acf3f
    'drop_host_profiles', 'load_host_profiles', 'write_host_profiles',
Packit 5acf3f
    'host_profiles_loaded', 'find_host_profiles', 'select_host_profile',
Packit 5acf3f
    'get_host_profile_by_id', 'match_host_profile', 'select_host_profile',
Packit 5acf3f
Packit 5acf3f
    # Host profile keys
Packit 5acf3f
    'BOOM_HOST_ID', 'BOOM_HOST_NAME',
Packit 5acf3f
    'BOOM_HOST_ADD_OPTS', 'BOOM_HOST_DEL_OPTS', 'BOOM_HOST_LABEL',
Packit 5acf3f
    'HOST_PROFILE_KEYS', 'HOST_REQUIRED_KEYS', 'HOST_OPTIONAL_KEYS',
Packit 5acf3f
Packit 5acf3f
    # Path configuration
Packit 5acf3f
    'boom_host_profiles_path',
Packit 5acf3f
]
Packit 5acf3f
Packit 5acf3f
# vim: set et ts=4 sw=4 :