summaryrefslogtreecommitdiff
path: root/cloudinit/mergers
diff options
context:
space:
mode:
authorJoshua Harlow <harlowja@gmail.com>2012-11-20 20:02:48 -0800
committerJoshua Harlow <harlowja@gmail.com>2012-11-20 20:02:48 -0800
commit8bc85abd97e06d964bbd26208eb732e80eb87c10 (patch)
tree6cbff248cbe85d3ef26da32bc185f34edd8f5c4d /cloudinit/mergers
parentd324a2cb0b10a4cd1b1b05dd23d0040ab3e9621c (diff)
downloadvyos-cloud-init-8bc85abd97e06d964bbd26208eb732e80eb87c10.tar.gz
vyos-cloud-init-8bc85abd97e06d964bbd26208eb732e80eb87c10.zip
Start allowing different merging types to be applied
After user data handling splits apart all the different content types into there various mime messages it is nice to be able to have each message specify how it should be merged (mainly for cloud-config or cloud-archive) into the single cloud config that is eventually used. This starts to add a plugable merging framework and the needed components to activate said headers and merging.
Diffstat (limited to 'cloudinit/mergers')
-rw-r--r--cloudinit/mergers/__init__.py104
-rw-r--r--cloudinit/mergers/dict.py33
-rw-r--r--cloudinit/mergers/list.py41
-rw-r--r--cloudinit/mergers/str.py28
4 files changed, 206 insertions, 0 deletions
diff --git a/cloudinit/mergers/__init__.py b/cloudinit/mergers/__init__.py
new file mode 100644
index 00000000..b3e728b0
--- /dev/null
+++ b/cloudinit/mergers/__init__.py
@@ -0,0 +1,104 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2012 Yahoo! Inc.
+#
+# Author: Joshua Harlow <harlowja@yahoo-inc.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+from cloudinit import importer
+from cloudinit import log as logging
+from cloudinit import util
+
+LOG = logging.getLogger(__name__)
+
+
+class UnknownMerger(object):
+ # Named differently so auto-method finding
+ # doesn't pick this up if there is ever a type
+ # named "unknown"
+ def _handle_unknown(self, meth_wanted, value, merge_with):
+ return value
+
+ def merge(self, source, merge_with):
+ type_name = util.obj_name(source)
+ type_name = type_name.lower()
+ method_name = "_on_%s" % (type_name)
+ meth = None
+ args = [source, merge_with]
+ if hasattr(self, method_name):
+ meth = getattr(self, method_name)
+ if not meth:
+ meth = self._handle_unknown
+ args.insert(0, method_name)
+ return meth(*args)
+
+
+class LookupMerger(UnknownMerger):
+ def __init__(self, lookups=None):
+ UnknownMerger.__init__(self)
+ if lookups is None:
+ self._lookups = []
+ else:
+ self._lookups = lookups
+
+ def _handle_unknown(self, meth_wanted, value, merge_with):
+ meth = None
+ for merger in self._lookups:
+ if hasattr(merger, meth_wanted):
+ # First one that has that method/attr gets to be
+ # the one that will be called
+ meth = getattr(merger, meth_wanted)
+ break
+ if not meth:
+ return UnknownMerger._handle_unknown(self, meth_wanted,
+ value, merge_with)
+ return meth(value, merge_with)
+
+
+def _extract_merger_names(merge_how):
+ names = []
+ for m_name in merge_how.split("+"):
+ # Canonicalize the name (so that it can be found
+ # even when users alter it in various ways...
+ m_name = m_name.lower().strip()
+ m_name = m_name.replace(" ", "_")
+ m_name = m_name.replace("\t", "_")
+ m_name = m_name.replace("-", "_")
+ if not m_name:
+ continue
+ names.append(m_name)
+ return names
+
+
+def construct(merge_how, default_classes=None):
+ mergers = []
+ merger_classes = []
+ root = LookupMerger(mergers)
+ for m_name in _extract_merger_names(merge_how):
+ merger_locs = importer.find_module(m_name,
+ [__name__],
+ ['Merger'])
+ if not merger_locs:
+ msg = "Could not find merger named %s" % (m_name)
+ raise ImportError(msg)
+ else:
+ mod = importer.import_module(merger_locs[0])
+ cls = getattr(mod, 'Merger')
+ merger_classes.append(cls)
+ if not merger_classes and default_classes:
+ merger_classes = default_classes
+ for m_class in merger_classes:
+ mergers.append(m_class(root))
+ return root \ No newline at end of file
diff --git a/cloudinit/mergers/dict.py b/cloudinit/mergers/dict.py
new file mode 100644
index 00000000..a0ffaa33
--- /dev/null
+++ b/cloudinit/mergers/dict.py
@@ -0,0 +1,33 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2012 Yahoo! Inc.
+#
+# Author: Joshua Harlow <harlowja@yahoo-inc.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+class Merger(object):
+ def __init__(self, merger):
+ self._merger = merger
+
+ def _on_dict(self, value, merge_with):
+ if not isinstance(merge_with, (dict)):
+ return value
+ merged = dict(value)
+ for (k, v) in merge_with.items():
+ if k in merged:
+ merged[k] = self._merger.merge(merged[k], v)
+ else:
+ merged[k] = v
+ return merged
diff --git a/cloudinit/mergers/list.py b/cloudinit/mergers/list.py
new file mode 100644
index 00000000..ad1b9793
--- /dev/null
+++ b/cloudinit/mergers/list.py
@@ -0,0 +1,41 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2012 Yahoo! Inc.
+#
+# Author: Joshua Harlow <harlowja@yahoo-inc.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+class Merger(object):
+ def __init__(self, merger):
+ self._merger = merger
+
+ def _on_tuple(self, value, merge_with):
+ return self._on_list(list(value), merge_with)
+
+ def _on_list(self, value, merge_with):
+ if isinstance(merge_with, (tuple, list)):
+ new_value = list(value)
+ 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)
+ else:
+ new_value = list(value)
+ new_value.append(merge_with)
+ return new_value
diff --git a/cloudinit/mergers/str.py b/cloudinit/mergers/str.py
new file mode 100644
index 00000000..7c3fa585
--- /dev/null
+++ b/cloudinit/mergers/str.py
@@ -0,0 +1,28 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2012 Yahoo! Inc.
+#
+# Author: Joshua Harlow <harlowja@yahoo-inc.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+class Merger(object):
+ def __init__(self, merger):
+ pass
+
+ def _on_unicode(self, value, merge_with):
+ return self._on_str(value, merge_with)
+
+ def _on_str(self, value, merge_with):
+ return value