summaryrefslogtreecommitdiff
path: root/cloudinit/mergers
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/mergers')
-rw-r--r--cloudinit/mergers/__init__.py59
-rw-r--r--cloudinit/mergers/dict.py11
-rw-r--r--cloudinit/mergers/list.py21
-rw-r--r--cloudinit/mergers/str.py5
4 files changed, 76 insertions, 20 deletions
diff --git a/cloudinit/mergers/__init__.py b/cloudinit/mergers/__init__.py
index 20658edc..4a112165 100644
--- a/cloudinit/mergers/__init__.py
+++ b/cloudinit/mergers/__init__.py
@@ -34,6 +34,13 @@ class UnknownMerger(object):
def _handle_unknown(self, meth_wanted, value, merge_with):
return value
+ # This merging will attempt to look for a '_on_X' method
+ # in our own object for a given object Y with type X,
+ # if found it will be called to perform the merge of a source
+ # object and a object to merge_with.
+ #
+ # If not found the merge will be given to a '_handle_unknown'
+ # function which can decide what to do wit the 2 values.
def merge(self, source, merge_with):
type_name = util.obj_name(source)
type_name = type_name.lower()
@@ -56,6 +63,11 @@ class LookupMerger(UnknownMerger):
else:
self._lookups = lookups
+ # For items which can not be merged by the parent this object
+ # will lookup in a internally maintained set of objects and
+ # find which one of those objects can perform the merge. If
+ # any of the contained objects have the needed method, they
+ # will be called to perform the merge.
def _handle_unknown(self, meth_wanted, value, merge_with):
meth = None
for merger in self._lookups:
@@ -70,8 +82,33 @@ class LookupMerger(UnknownMerger):
return meth(value, merge_with)
-def _extract_merger_names(merge_how):
- names = []
+def dict_extract_mergers(config):
+ parsed_mergers = []
+ raw_mergers = config.get('merger_how')
+ if raw_mergers is None:
+ raw_mergers = config.get('merge_type')
+ if raw_mergers is None:
+ return parsed_mergers
+ if isinstance(raw_mergers, (str, basestring)):
+ return string_extract_mergers(raw_mergers)
+ for m in raw_mergers:
+ if isinstance(m, (dict)):
+ name = m['name']
+ name = name.replace("-", "_").strip()
+ opts = m['settings']
+ else:
+ name = m[0]
+ if len(m) >= 2:
+ opts = m[1:]
+ else:
+ opts = []
+ if name:
+ parsed_mergers.append((name, opts))
+ return parsed_mergers
+
+
+def string_extract_mergers(merge_how):
+ parsed_mergers = []
for m_name in merge_how.split("+"):
# Canonicalize the name (so that it can be found
# even when users alter it in various ways)
@@ -79,20 +116,20 @@ def _extract_merger_names(merge_how):
m_name = m_name.replace("-", "_")
if not m_name:
continue
- names.append(m_name)
- return names
-
-
-def construct(merge_how):
- mergers_to_be = []
- for name in _extract_merger_names(merge_how):
- match = NAME_MTCH.match(name)
+ match = NAME_MTCH.match(m_name)
if not match:
- msg = "Matcher identifer '%s' is not in the right format" % (name)
+ msg = "Matcher identifer '%s' is not in the right format" % (m_name)
raise ValueError(msg)
(m_name, m_ops) = match.groups()
m_ops = m_ops.strip().split(",")
m_ops = [m.strip().lower() for m in m_ops if m.strip()]
+ parsed_mergers.append((m_name, m_ops))
+ return parsed_mergers
+
+
+def construct(parsed_mergers):
+ mergers_to_be = []
+ for (m_name, m_ops) in parsed_mergers:
merger_locs = importer.find_module(m_name,
[__name__],
['Merger'])
diff --git a/cloudinit/mergers/dict.py b/cloudinit/mergers/dict.py
index bc392afa..45a7d3a5 100644
--- a/cloudinit/mergers/dict.py
+++ b/cloudinit/mergers/dict.py
@@ -22,6 +22,17 @@ class Merger(object):
self._merger = merger
self._overwrite = 'overwrite' in opts
+ # This merging algorithm will attempt to merge with
+ # another dictionary, on encountering any other type of object
+ # it will not merge with said object, but will instead return
+ # the original value
+ #
+ # On encountering a dictionary, it will create a new dictionary
+ # composed of the original and the one to merge with, if 'overwrite'
+ # is enabled then keys that exist in the original will be overwritten
+ # by keys in the one to merge with (and associated values). Otherwise
+ # if not in overwrite mode the 2 conflicting keys themselves will
+ # be merged.
def _on_dict(self, value, merge_with):
if not isinstance(merge_with, (dict)):
return value
diff --git a/cloudinit/mergers/list.py b/cloudinit/mergers/list.py
index a848b8d6..a56ff007 100644
--- a/cloudinit/mergers/list.py
+++ b/cloudinit/mergers/list.py
@@ -26,21 +26,24 @@ class Merger(object):
def _on_tuple(self, value, merge_with):
return self._on_list(list(value), merge_with)
+ # On encountering a list or tuple type this action will be applied
+ # a new list will be returned, if the value to merge with is itself
+ # a list and we have been told to 'extend', then the value here will
+ # be extended with the other list. If in 'extend' mode then we will
+ # attempt to merge instead, which means that values from the list
+ # to merge with will replace values in te original list (they will
+ # also be merged recursively).
+ #
+ # If the value to merge with is not a list, and we are set to discared
+ # then no modifications will take place, otherwise we will just append
+ # the value to merge with onto the end of our own list.
def _on_list(self, value, merge_with):
new_value = list(value)
if isinstance(merge_with, (tuple, list)):
if self._extend:
new_value.extend(merge_with)
else:
- # Merge instead
- for m_v in merge_with:
- m_am = 0
- for (i, o_v) in enumerate(new_value):
- if m_v == o_v:
- new_value[i] = self._merger.merge(o_v, m_v)
- m_am += 1
- if m_am == 0:
- new_value.append(m_v)
+ return new_value
else:
if not self._discard_non:
new_value.append(merge_with)
diff --git a/cloudinit/mergers/str.py b/cloudinit/mergers/str.py
index 14bc46ec..f1534c5b 100644
--- a/cloudinit/mergers/str.py
+++ b/cloudinit/mergers/str.py
@@ -21,9 +21,14 @@ class Merger(object):
def __init__(self, merger, opts):
self._append = 'append' in opts
+ # On encountering a unicode object to merge value with
+ # we will for now just proxy into the string method to let it handle it.
def _on_unicode(self, value, merge_with):
return self._on_str(value, merge_with)
+ # On encountering a string object to merge with we will
+ # perform the following action, if appending we will
+ # merge them together, otherwise we will just return value.
def _on_str(self, value, merge_with):
if not self._append:
return value