summaryrefslogtreecommitdiff
path: root/cloudinit/helpers.py
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/helpers.py')
-rw-r--r--cloudinit/helpers.py127
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