diff options
Diffstat (limited to 'cloudinit/helpers.py')
-rw-r--r-- | cloudinit/helpers.py | 127 |
1 files changed, 81 insertions, 46 deletions
diff --git a/cloudinit/helpers.py b/cloudinit/helpers.py index 9752ad28..c2c9e584 100644 --- a/cloudinit/helpers.py +++ b/cloudinit/helpers.py @@ -8,19 +8,15 @@ # # This file is part of cloud-init. See LICENSE file for license information. -from time import time - import contextlib import os -from configparser import NoSectionError, NoOptionError, RawConfigParser +from configparser import NoOptionError, NoSectionError, RawConfigParser from io import StringIO - -from cloudinit.settings import (PER_INSTANCE, PER_ALWAYS, PER_ONCE, - CFG_ENV_NAME) +from time import time from cloudinit import log as logging -from cloudinit import type_utils -from cloudinit import util +from cloudinit import persistence, type_utils, util +from cloudinit.settings import CFG_ENV_NAME, PER_ALWAYS, PER_INSTANCE, PER_ONCE LOG = logging.getLogger(__name__) @@ -91,8 +87,9 @@ class FileSemaphores(object): try: util.del_dir(self.sem_path) except (IOError, OSError): - util.logexc(LOG, "Failed deleting semaphore directory %s", - self.sem_path) + util.logexc( + LOG, "Failed deleting semaphore directory %s", self.sem_path + ) def _acquire(self, name, freq): # Check again if its been already gotten @@ -124,11 +121,14 @@ class FileSemaphores(object): # this case could happen if the migrator module hadn't run yet # but the item had run before we did canon_sem_name. if cname != name and os.path.exists(self._get_path(name, freq)): - LOG.warning("%s has run without canonicalized name [%s].\n" - "likely the migrator has not yet run. " - "It will run next boot.\n" - "run manually with: cloud-init single --name=migrator", - name, cname) + LOG.warning( + "%s has run without canonicalized name [%s].\n" + "likely the migrator has not yet run. " + "It will run next boot.\n" + "run manually with: cloud-init single --name=migrator", + name, + cname, + ) return True return False @@ -187,9 +187,14 @@ class Runners(object): class ConfigMerger(object): - def __init__(self, paths=None, datasource=None, - additional_fns=None, base_cfg=None, - include_vendor=True): + def __init__( + self, + paths=None, + datasource=None, + additional_fns=None, + base_cfg=None, + include_vendor=True, + ): self._paths = paths self._ds = datasource self._fns = additional_fns @@ -206,8 +211,11 @@ class ConfigMerger(object): if ds_cfg and isinstance(ds_cfg, (dict)): d_cfgs.append(ds_cfg) except Exception: - util.logexc(LOG, "Failed loading of datasource config object " - "from %s", self._ds) + util.logexc( + LOG, + "Failed loading of datasource config object from %s", + self._ds, + ) return d_cfgs def _get_env_configs(self): @@ -217,8 +225,7 @@ class ConfigMerger(object): try: e_cfgs.append(util.read_conf(e_fn)) except Exception: - util.logexc(LOG, 'Failed loading of env. config from %s', - e_fn) + util.logexc(LOG, "Failed loading of env. config from %s", e_fn) return e_cfgs def _get_instance_configs(self): @@ -228,9 +235,13 @@ class ConfigMerger(object): if not self._paths: return i_cfgs - cc_paths = ['cloud_config'] + cc_paths = ["cloud_config"] if self._include_vendor: - cc_paths.append('vendor_cloud_config') + # the order is important here: we want vendor2 + # (dynamic vendor data from OpenStack) + # to override vendor (static data from OpenStack) + cc_paths.append("vendor2_cloud_config") + cc_paths.append("vendor_cloud_config") for cc_p in cc_paths: cc_fn = self._paths.get_ipath_cur(cc_p) @@ -239,11 +250,14 @@ class ConfigMerger(object): i_cfgs.append(util.read_conf(cc_fn)) except PermissionError: LOG.debug( - 'Skipped loading cloud-config from %s due to' - ' non-root.', cc_fn) + "Skipped loading cloud-config from %s due to" + " non-root.", + cc_fn, + ) except Exception: - util.logexc(LOG, 'Failed loading of cloud-config from %s', - cc_fn) + util.logexc( + LOG, "Failed loading of cloud-config from %s", cc_fn + ) return i_cfgs def _read_cfg(self): @@ -259,8 +273,9 @@ class ConfigMerger(object): try: cfgs.append(util.read_conf(c_fn)) except Exception: - util.logexc(LOG, "Failed loading of configuration from %s", - c_fn) + util.logexc( + LOG, "Failed loading of configuration from %s", c_fn + ) cfgs.extend(self._get_env_configs()) cfgs.extend(self._get_instance_configs()) @@ -278,7 +293,6 @@ class ConfigMerger(object): class ContentHandlers(object): - def __init__(self): self.registered = {} self.initialized = [] @@ -313,19 +327,21 @@ class ContentHandlers(object): return list(self.registered.items()) -class Paths(object): +class Paths(persistence.CloudInitPickleMixin): + _ci_pkl_version = 1 + def __init__(self, path_cfgs, ds=None): self.cfgs = path_cfgs # Populate all the initial paths - self.cloud_dir = path_cfgs.get('cloud_dir', '/var/lib/cloud') - self.run_dir = path_cfgs.get('run_dir', '/run/cloud-init') - self.instance_link = os.path.join(self.cloud_dir, 'instance') + self.cloud_dir = path_cfgs.get("cloud_dir", "/var/lib/cloud") + self.run_dir = path_cfgs.get("run_dir", "/run/cloud-init") + self.instance_link = os.path.join(self.cloud_dir, "instance") self.boot_finished = os.path.join(self.instance_link, "boot-finished") - self.upstart_conf_d = path_cfgs.get('upstart_dir') - self.seed_dir = os.path.join(self.cloud_dir, 'seed') + self.upstart_conf_d = path_cfgs.get("upstart_dir") + self.seed_dir = os.path.join(self.cloud_dir, "seed") # This one isn't joined, since it should just be read-only - template_dir = path_cfgs.get('templates_dir', '/etc/cloud/templates/') - self.template_tpl = os.path.join(template_dir, '%s.tmpl') + template_dir = path_cfgs.get("templates_dir", "/etc/cloud/templates/") + self.template_tpl = os.path.join(template_dir, "%s.tmpl") self.lookups = { "handlers": "handlers", "scripts": "scripts", @@ -337,9 +353,12 @@ class Paths(object): "obj_pkl": "obj.pkl", "cloud_config": "cloud-config.txt", "vendor_cloud_config": "vendor-cloud-config.txt", + "vendor2_cloud_config": "vendor2-cloud-config.txt", "data": "data", "vendordata_raw": "vendor-data.txt", + "vendordata2_raw": "vendor-data2.txt", "vendordata": "vendor-data.txt.i", + "vendordata2": "vendor-data2.txt.i", "instance_id": ".instance-id", "manual_clean_marker": "manual-clean", "warnings": "warnings", @@ -347,6 +366,18 @@ class Paths(object): # Set when a datasource becomes active self.datasource = ds + def _unpickle(self, ci_pkl_version: int) -> None: + """Perform deserialization fixes for Paths.""" + if not hasattr(self, "run_dir"): + # On older versions of cloud-init the Paths class do not + # have the run_dir attribute. This is problematic because + # when loading the pickle object on newer versions of cloud-init + # we will rely on this attribute. To fix that, we are now + # manually adding that attribute here. + self.run_dir = Paths( + path_cfgs=self.cfgs, ds=self.datasource + ).run_dir + # get_ipath_cur: get the current instance path for an item def get_ipath_cur(self, name=None): return self._get_path(self.instance_link, name) @@ -364,8 +395,8 @@ class Paths(object): iid = self.datasource.get_instance_id() if iid is None: return None - path_safe_iid = str(iid).replace(os.sep, '_') - ipath = os.path.join(self.cloud_dir, 'instances', path_safe_iid) + path_safe_iid = str(iid).replace(os.sep, "_") + ipath = os.path.join(self.cloud_dir, "instances", path_safe_iid) add_on = self.lookups.get(name) if add_on: ipath = os.path.join(ipath, add_on) @@ -377,8 +408,10 @@ class Paths(object): def get_ipath(self, name=None): ipath = self._get_ipath(name) if not ipath: - LOG.warning(("No per instance data available, " - "is there an datasource/iid set?")) + LOG.warning( + "No per instance data available, " + "is there an datasource/iid set?" + ) return None else: return ipath @@ -401,6 +434,7 @@ class Paths(object): # you can avoid catching exceptions that you typically don't # care about... + class DefaultingConfigParser(RawConfigParser): DEF_INT = 0 DEF_FLOAT = 0.0 @@ -418,7 +452,7 @@ class DefaultingConfigParser(RawConfigParser): return value def set(self, section, option, value=None): - if not self.has_section(section) and section.lower() != 'default': + if not self.has_section(section) and section.lower() != "default": self.add_section(section) RawConfigParser.set(self, section, option, value) @@ -442,13 +476,14 @@ class DefaultingConfigParser(RawConfigParser): return RawConfigParser.getint(self, section, option) def stringify(self, header=None): - contents = '' + contents = "" outputstream = StringIO() self.write(outputstream) outputstream.flush() contents = outputstream.getvalue() if header: - contents = '\n'.join([header, contents, '']) + contents = "\n".join([header, contents, ""]) return contents + # vi: ts=4 expandtab |