Source code for vmupdate.config

"""
    Provide a wrapper around configuration.
"""

import logging.config
import os
import pkgutil

import yaml

from vmupdate import BASE_DIR


[docs]class ConfigSection(object): """ Provide a base class for configuration sections. This class wraps a :class:`dict`. """ def __init__(self, data=None): """ Return an instance of :class:`ConfigSection`. :param dict data: the data for the config section :rtype:`ConfigSection` """ self._data = data or {} def __getitem__(self, key): """Return the value for ``key``, else ``None``.""" return self._data.get(key) def __contains__(self, key): """Return ``True`` if the config section contains ``key``, else ``False``.""" return key in self._data def __iter__(self): """Return an iterator over the keys of the config section.""" return iter(self._data) def __len__(self): """Return the number of items in the config section.""" return len(self._data)
[docs] def get(self, key, default=None): """Return the value for ``key``, else ``default``.""" return self._data.get(key, default)
[docs] def items(self): """Return a copy of the config section's list of (key, value) pairs.""" return self._data.items()
[docs] def keys(self): """Return a copy of the config section's list of keys.""" return self._data.keys()
[docs] def values(self): """Return a copy of the config section's list of values.""" return self._data.values()
[docs]class Credentials(ConfigSection): """Provide a wrapper around the credentials configuration section.""" def __init__(self, data): """ Return an instance of :class:`Credentials`. This method extends :meth:`ConfigSection.__init__`. :param dict data: the data for the config section :rtype:`Credentials` """ super(Credentials, self).__init__(data) @property def username(self): """ Return the *Username* configuration. :rtype: str """ return self.get('Username') @property def password(self): """ Return the *Password* configuration. :rtype: str """ return self.get('Password') @property def use_keyring(self): """ Return the *Use Keyring* configuration. :rtype: bool """ return self.get('Use Keyring', False) @property def run_as_elevated(self): """ Return the *Run As Elevated* configuration. :rtype: bool """ return self.get('Run As Elevated', False)
[docs]class General(ConfigSection): """Provide a wrapper around the general configuration section.""" def __init__(self, data): """ Return an instance of :class:`General`. This method extends :meth:`ConfigSection.__init__`. :param dict data: the data for the config section :rtype:`General` """ super(General, self).__init__(data) @property def wait_after_start(self): """ Return the *Wait After Start* configuration. :rtype: int """ return self['Wait After Start'] @property def wait_before_stop(self): """ Return the *Wait Before Stop* configuration. :rtype: int """ return self['Wait Before Stop']
[docs]class Machines(ConfigSection): """ Provide a wrapper around the machines configuration section. This class wraps a dict of :class:`Machine`. """ def __init__(self, data): """ Return an instance of :class:`Machines`. This method extends :meth:`ConfigSection.__init__`. :param dict data: the data for the config section :rtype:`Machines` """ super(Machines, self).__init__(data) if data: for name, machine_data in data.items(): self._data[name] = Machine(machine_data)
[docs]class Machine(ConfigSection): """Provide a wrapper around the machine configuration section.""" def __init__(self, data): """ Return an instance of :class:`Machine`. This method extends :meth:`ConfigSection.__init__`. :param dict data: the data for the config section :rtype:`Machine` """ super(Machine, self).__init__(data) @property def username(self): """ Return the *Username* configuration. :rtype: str """ return self.get('Username') @property def password(self): """ Return the *Password* configuration. :rtype: str """ return self.get('Password') @property def use_keyring(self): """ Return the *Use Keyring* configuration. :rtype: bool """ return self.get('Use Keyring') @property def run_as_elevated(self): """ Return the *Run As Elevated* configuration. :rtype: bool """ return self.get('Run As Elevated') @property def shell(self): """ Return the *Shell* configuration. :rtype: str """ return self.get('Shell') @property def ignore(self): """ Return the *Ignore* configuration. :rtype: bool """ return self.get('Ignore', False)
[docs]class Network(ConfigSection): """Provide a wrapper around the network configuration section.""" def __init__(self, data): """ Return an instance of :class:`Network`. This method extends :meth:`ConfigSection.__init__`. :param dict data: the data for the config section :rtype:`Network` """ super(Network, self).__init__(data) self._ssh = Ssh(self._data['SSH']) @property def ssh(self): """ Return the *SSH* configuration section. :rtype: :class:`Ssh` """ return self._ssh
[docs]class Ssh(ConfigSection): """Provide a wrapper around the SSH configuration section.""" def __init__(self, data): """ Return an instance of :class:`Ssh`. This method extends :meth:`ConfigSection.__init__`. :param dict data: the data for the config section :rtype:`Ssh` """ super(Ssh, self).__init__(data) @property def guest_port(self): """ Return the guest port configuration. :rtype: int """ return self['Guest']['Port'] @property def host_min_port(self): """ Return the host port minimum configuration. :rtype: int """ return self['Host']['Ports']['Min'] @property def host_max_port(self): """ Return the host port maximum configuration, else 65,535. :rtype: int """ return self['Host']['Ports'].get('Max', 65535)
[docs]class PackageManagers(ConfigSection): """Provide a wrapper around the package managers configuration section.""" def __init__(self, data): """ Return an instance of :class:`PackageManagers`. This method extends :meth:`ConfigSection.__init__`. :param dict data: the data for the config section :rtype:`PackageManagers` """ super(PackageManagers, self).__init__(data)
[docs]class Shells(ConfigSection): """Provide a wrapper around the shells configuration section.""" def __init__(self, data): """ Return an instance of :class:`Shells`. This method extends :meth:`ConfigSection.__init__`. :param dict data: the data for the config section :rtype:`Shells` """ super(Shells, self).__init__(data)
[docs]class Virtualizers(ConfigSection): """Provide a wrapper around the virtualizers configuration section.""" def __init__(self, data): """ Return an instance of :class:`Virtualizers`. This method extends :meth:`ConfigSection.__init__`. :param dict data: the data for the config section :rtype:`Virtualizers` """ super(Virtualizers, self).__init__(data)
[docs]class Config(ConfigSection): """Provide a wrapper for the merged configuration files.""" def __init__(self): super(Config, self).__init__() self._general = None self._credentials = None self._network = None self._virtualizers = None self._pkgmgrs = None self._shells = None self._machines = None self._logging = None @property def general(self): """ Return the *General* configuration section. :rtype: :class:`General` """ return self._general @property def credentials(self): """ Return the *Credentials* configuration section. :rtype: :class:`Credentials` """ return self._credentials @property def network(self): """ Return the *Network* configuration section. :rtype: :class:`Network` """ return self._network @property def virtualizers(self): """ Return the *Virtualizers* configuration section. :rtype: :class:`Virtualizers` """ return self._virtualizers @property def pkgmgrs(self): """ Return the *Package Managers* configuration section. :rtype: :class:`PackageManagers` """ return self._pkgmgrs @property def shells(self): """ Return the *Shells* configuration section. :rtype: :class:`Shells` """ return self._shells @property def machines(self): """ Return the *Machines* configuration section. :rtype: :class:`Machines` """ return self._machines
[docs] def load(self, config_path=None, log_dir=None): """ Load the configuration files and configure logging. :param str config_path: path to a user defined configuration file :param str log_dir: path to the directory where log files are to be stored """ default_config = yaml.load(pkgutil.get_data('vmupdate', 'data/vmupdate.yaml')) if config_path: with open(config_path, 'r') as config_file: user_config = yaml.load(config_file) self._data = _merge(default_config, user_config) else: self._data = default_config self._general = General(self._data['General']) self._credentials = Credentials(self._data['Credentials']) self._network = Network(self._data['Network']) self._virtualizers = Virtualizers(self._data['Virtualizers']) self._pkgmgrs = PackageManagers(self._data['Package Managers']) self._shells = Shells(self._data['Shells']) self._machines = Machines(self._data['Machines']) self._logging = yaml.load(pkgutil.get_data('vmupdate', 'data/logging.yaml')) if not log_dir: log_dir = BASE_DIR self._set_log_filename(log_dir, 'info_file') self._set_log_filename(log_dir, 'error_file') logging.config.dictConfig(self._logging)
def _set_log_filename(self, log_dir, handler_name): """ Update the log handler to write to the specified directory. :param str log_dir: path to the directory where log files are to be stored :param str handler_name: name of the log handler to update """ if handler_name in self._logging['handlers']: self._logging['handlers'][handler_name]['filename'] =\ os.path.join(log_dir, self._logging['handlers'][handler_name]['filename'])
def _merge(a, b): """ Return the merge of two dictionaries. :param dict a: dictionary to merge into :param dict b: dictionary to merge :rtype: dict """ if b is None: return a if isinstance(a, dict): for key in b: if key in a: a[key] = _merge(a[key], b[key]) else: a[key] = b[key] else: a = b return a config = Config()