From 26674b9c8537ea84b691608117342c7c3ac5a9d6 Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Tue, 21 Jun 2022 18:20:26 +0200 Subject: [PATCH] Moving class CrTplConfiguration to BaseMultiConfig in module cr_vmware_tpl.config, adding evaluation of cobbler distros. --- lib/cr_vmware_tpl/config.py | 185 ++++++++++++++++++++++++++++++------ 1 file changed, 155 insertions(+), 30 deletions(-) diff --git a/lib/cr_vmware_tpl/config.py b/lib/cr_vmware_tpl/config.py index a77757a..a979ac5 100644 --- a/lib/cr_vmware_tpl/config.py +++ b/lib/cr_vmware_tpl/config.py @@ -18,14 +18,18 @@ import crypt from pathlib import Path # Own modules -from fb_tools.config import ConfigError, BaseConfiguration +from fb_tools.common import is_sequence +from fb_tools.multi_config import MultiConfigError, BaseMultiConfig +from fb_tools.multi_config import DEFAULT_ENCODING from fb_tools.xlate import format_list from fb_vmware.config import VSPhereConfigInfo +from . import DEFAULT_CONFIG_DIR + from .xlate import XLATOR -__version__ = '1.7.2' +__version__ = '1.8.1' LOG = logging.getLogger(__name__) _ = XLATOR.gettext @@ -33,7 +37,7 @@ ngettext = XLATOR.ngettext # ============================================================================= -class CrTplConfigError(ConfigError): +class CrTplConfigError(MultiConfigError): """Base error class for all exceptions happened during execution this configured application""" @@ -41,13 +45,13 @@ class CrTplConfigError(ConfigError): # ============================================================================= -class CrTplConfiguration(BaseConfiguration): +class CrTplConfiguration(BaseMultiConfig): """ A class for providing a configuration for the CrTplApplication class and methods to read it from configuration files. """ - default_os_id = 'centos8' + default_os_id = 'centos-stream-8' default_vsphere_host = 'vcs01.ppbrln.internal' default_vsphere_port = 443 @@ -124,7 +128,22 @@ class CrTplConfiguration(BaseConfiguration): # ------------------------------------------------------------------------- def __init__( self, appname=None, verbose=0, version=__version__, base_dir=None, - encoding=None, config_dir=None, config_file=None, initialized=False): + append_appname_to_stems=True, additional_stems=None, config_dir=DEFAULT_CONFIG_DIR, + additional_config_file=None, additional_cfgdirs=None, encoding=DEFAULT_ENCODING, + ensure_privacy=False, use_chardet=True, initialized=False): + + add_stems = [] + if additional_stems: + if is_sequence(additional_stems): + for stem in additional_stems: + add_stems.append(stem) + else: + add_stems.append(additional_stems) + + if 'mail' not in add_stems: + add_stems.append('mail') + if 'cobbler-repos' not in add_stems: + add_stems.append('cobbler-distros') self.os_id = self.default_os_id @@ -173,13 +192,22 @@ class CrTplConfiguration(BaseConfiguration): self.cobbler_ws_base_url = self.default_cobbler_ws_base_url self.cobbler_ws_docroot = self.default_cobbler_ws_docroot self.cobbler_ws_rel_filesdir = self.default_cobbler_ws_rel_filesdir + self.cobbler_distros = {} + self.system_status = self.default_system_status self.excluded_datastores = [] + if config_dir is None: + config_dir = DEFAULT_CONFIG_DIR + LOG.debug("Config dir: {!r}.".format(config_dir)) + super(CrTplConfiguration, self).__init__( appname=appname, verbose=verbose, version=version, base_dir=base_dir, - encoding=encoding, config_dir=config_dir, config_file=config_file, initialized=False, + append_appname_to_stems=append_appname_to_stems, config_dir=config_dir, + additional_stems=add_stems, additional_config_file=additional_config_file, + additional_cfgdirs=additional_cfgdirs, encoding=encoding, use_chardet=use_chardet, + ensure_privacy=ensure_privacy, initialized=False, ) self.vsphere_info = VSPhereConfigInfo( @@ -236,7 +264,7 @@ class CrTplConfiguration(BaseConfiguration): @property def cobbler_profile_ks(self): """The absolute pathname of the profile kickstart file.""" - return self.cobbler_ks_dir / (self.cobbler_profile + '.ks') + return self.cobbler_ks_dir / ('profile.' + self.cobbler_profile + '.ks') # ------------------------------------------------------------------------- @property @@ -316,10 +344,10 @@ class CrTplConfiguration(BaseConfiguration): return res # ------------------------------------------------------------------------- - def eval_config(self, config): - """Evaluating of all found configuration options.""" + def eval(self): + """Evaluating read configuration and storing them in object properties.""" - super(CrTplConfiguration, self).eval_config(config) + super(CrTplConfiguration, self).eval() if self.verbose > 1: LOG.debug(_("Checking for unconfigured options ...")) @@ -339,29 +367,76 @@ class CrTplConfiguration(BaseConfiguration): if not self.cobbler_profile_given: self.cobbler_profile = 'vmware-template-' + self.os_id + self.verify_cobbler_distros() + + # ------------------------------------------------------------------------- + def verify_cobbler_distros(self): + + LOG.debug(_("Verifying cobbler distros ...")) + + if not len(self.cobbler_distros): + msg = _("Did not found configured Cobbler distros.") + raise CrTplConfigError(msg) + + for distro_id in self.cobbler_distros.keys(): + distro_info = self.cobbler_distros[distro_id] + + if 'distro' not in distro_info: + msg = _("Did not found distro of configured Cobbler distro {!r}.").format( + distro_id) + raise CrTplConfigError(msg) + + if not len(distro_info['repos']): + msg = _( + "Did not found repo definitions for configured Cobbler " + "distro {!r}.").format(distro_id) + LOG.warn(msg) + + if 'description' not in distro_info: + distro_info['description'] = distro_id + + if self.os_id not in self.cobbler_distros: + msg = _("Did not found distro {!r} in configured Cobbler distros.").format(self.os_id) + raise CrTplConfigError(msg) + + self.cobbler_distro = self.cobbler_distros[self.os_id]['distro'] + + LOG.info(_("Using OS {os!r} with cobbler distro {di!r}.").format( + os=self.os_id, di=self.cobbler_distro)) + # ------------------------------------------------------------------------- - def eval_config_section(self, config, section_name): + def eval_section(self, section_name): + + re_cobbler_distros = re.compile(r'^\s*cobbler[_-]?distros\s*$', re.IGNORECASE) + + LOG.debug(_("Evaluating section {!r} ...").format(section_name)) + + super(CrTplConfiguration, self).eval_section(section_name) - super(CrTplConfiguration, self).eval_config_section(config, section_name) + sn = section_name.lower() + section = self.cfg[section_name] - if section_name.lower() == 'vsphere': - self._eval_config_vsphere(config, section_name) + if sn == 'vsphere': + self._eval_config_vsphere(section_name, section) return - if section_name.lower() == 'template': - self._eval_config_template(config, section_name) + if sn == 'template': + self._eval_config_template(section_name, section) return - if section_name.lower() == 'timeouts': - self._eval_config_timeouts(config, section_name) + if sn == 'timeouts': + self._eval_config_timeouts(section_name, section) return - if section_name.lower() == 'cobbler': - self._eval_config_cobbler(config, section_name) + if sn == 'cobbler': + self._eval_config_cobbler(section_name, section) + return + if re_cobbler_distros.match(section_name): + self._eval_cobbler_distros(section_name, section) return if self.verbose > 1: LOG.debug(_("Unhandled configuration section {!r}.").format(section_name)) # ------------------------------------------------------------------------- - def _eval_config_vsphere(self, config, section_name): + def _eval_config_vsphere(self, section_name, section): if self.verbose > 1: LOG.debug(_("Checking config section {!r} ...").format(section_name)) @@ -370,7 +445,8 @@ class CrTplConfiguration(BaseConfiguration): re_split_ds = re.compile(r'[,;\s]+') re_storage_cluster = re.compile(r'^\s*storage[-_]?cluster\s*$', re.IGNORECASE) - for (key, value) in config.items(section_name): + for key in section.keys(): + value = section[key] if key.lower() == 'host': self.vsphere_info.host = value @@ -421,7 +497,7 @@ class CrTplConfiguration(BaseConfiguration): return # ------------------------------------------------------------------------- - def _eval_config_template(self, config, section_name): + def _eval_config_template(self, section_name, section): if self.verbose > 1: LOG.debug(_("Checking config section {!r} ...").format(section_name)) @@ -432,7 +508,9 @@ class CrTplConfiguration(BaseConfiguration): re_root_pwd = re.compile(r'^\s*root[-_]?password\s*$', re.IGNORECASE) re_swap_space = re.compile(r'^\s*swap[-_]?space(?:[-_]?mb)?\s*$', re.IGNORECASE) - for (key, value) in config.items(section_name): + for key in section.keys(): + value = section[key] + if re_os_id.match(key): v = value.strip().lower() v = re_os_id_subst.sub('', v) @@ -518,12 +596,14 @@ class CrTplConfiguration(BaseConfiguration): setattr(self, prop_name, v) # ------------------------------------------------------------------------- - def _eval_config_timeouts(self, config, section_name): + def _eval_config_timeouts(self, section_name, section): if self.verbose > 1: LOG.debug(_("Checking config section {!r} ...").format(section_name)) - for (key, value) in config.items(section_name): + for key in section.keys(): + value = section[key] + if key.lower() == 'max_wait_for_general': self._eval_timeout_value( prop_name='max_wait_for_general', value=value, @@ -532,7 +612,6 @@ class CrTplConfiguration(BaseConfiguration): default_val=self.default_max_wait_for_general) continue - for (key, value) in config.items(section_name): if key.lower() == 'max_wait_for_create_vm': self._eval_timeout_value( prop_name='max_wait_for_create_vm', value=value, @@ -570,7 +649,7 @@ class CrTplConfiguration(BaseConfiguration): continue # ------------------------------------------------------------------------- - def _eval_config_cobbler(self, config, section_name): + def _eval_config_cobbler(self, section_name, section): if self.verbose > 1: LOG.debug(_("Checking config section {!r} ...").format(section_name)) @@ -592,7 +671,9 @@ class CrTplConfiguration(BaseConfiguration): re_system_status = re.compile(r'^\s*system[-_]?status\s*$', re.IGNORECASE) re_cobbler_bin_key = re.compile(r'^\s*(?:cobbler[_-]?)?bin\s*$', re.IGNORECASE) - for (key, value) in config.items(section_name): + for key in section.keys(): + value = section[key] + if key.lower() == 'distro' and value.strip() != '': self.cobbler_distro = value.strip() continue @@ -661,6 +742,50 @@ class CrTplConfiguration(BaseConfiguration): LOG.error(msg) continue + # ------------------------------------------------------------------------- + def _eval_cobbler_distros(self, section_name, section): + + if self.verbose > 1: + LOG.debug(_("Checking config section {!r} ...").format(section_name)) + + for distro_id in section.keys(): + distro_info = section[distro_id] + distro_id = distro_id.lower() + if distro_id not in self.cobbler_distros: + self.cobbler_distros[distro_id] = {} + + self._eval_cobbler_distro(distro_id, distro_info) + + # ------------------------------------------------------------------------- + def _eval_cobbler_distro(self, distro_id, distro_info): + + if self.verbose > 2: + LOG.debug(_("Evaluating Cobbler distro {!r} ...").format(distro_id)) + + distro = self.cobbler_distros[distro_id] + + if 'repos' not in distro: + distro['repos'] = [] + + for key in distro_info.keys(): + value = distro_info[key] + + if key.lower() == 'distro' and value.strip() != '': + distro['distro'] = value.strip() + continue + if key.lower() == 'description' and value.strip() != '': + distro['description'] = value.strip() + continue + if key.lower() == 'repos': + if is_sequence(value): + for repo in value: + repo = repo.strip() + if repo != '': + distro['repos'].append(repo) + elif value.strip() != '': + distro['repos'].append(value.strip()) + continue + # ------------------------------------------------------------------------- def get_root_pwd_hash(self, method='sha256'): """Generates a password hash based on the root password.""" -- 2.39.5