Blame cloudinit/mergers/__init__.py

Packit Service a04d08
# Copyright (C) 2012 Yahoo! Inc.
Packit Service a04d08
#
Packit Service a04d08
# Author: Joshua Harlow <harlowja@yahoo-inc.com>
Packit Service a04d08
#
Packit Service a04d08
# This file is part of cloud-init. See LICENSE file for license information.
Packit Service a04d08
Packit Service a04d08
import re
Packit Service a04d08
Packit Service a04d08
from cloudinit import importer
Packit Service a04d08
from cloudinit import log as logging
Packit Service a04d08
from cloudinit import type_utils
Packit Service a04d08
Packit Service a04d08
NAME_MTCH = re.compile(r"(^[a-zA-Z_][A-Za-z0-9_]*)\((.*?)\)$")
Packit Service a04d08
Packit Service a04d08
LOG = logging.getLogger(__name__)
Packit Service a04d08
DEF_MERGE_TYPE = "list()+dict()+str()"
Packit Service a04d08
MERGER_PREFIX = 'm_'
Packit Service a04d08
MERGER_ATTR = 'Merger'
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
class UnknownMerger(object):
Packit Service a04d08
    # Named differently so auto-method finding
Packit Service a04d08
    # doesn't pick this up if there is ever a type
Packit Service a04d08
    # named "unknown"
Packit Service a04d08
    def _handle_unknown(self, _meth_wanted, value, _merge_with):
Packit Service a04d08
        return value
Packit Service a04d08
Packit Service a04d08
    # This merging will attempt to look for a '_on_X' method
Packit Service a04d08
    # in our own object for a given object Y with type X,
Packit Service a04d08
    # if found it will be called to perform the merge of a source
Packit Service a04d08
    # object and a object to merge_with.
Packit Service a04d08
    #
Packit Service a04d08
    # If not found the merge will be given to a '_handle_unknown'
Packit Service a04d08
    # function which can decide what to do wit the 2 values.
Packit Service a04d08
    def merge(self, source, merge_with):
Packit Service a04d08
        type_name = type_utils.obj_name(source)
Packit Service a04d08
        type_name = type_name.lower()
Packit Service a04d08
        method_name = "_on_%s" % (type_name)
Packit Service a04d08
        meth = None
Packit Service a04d08
        args = [source, merge_with]
Packit Service a04d08
        if hasattr(self, method_name):
Packit Service a04d08
            meth = getattr(self, method_name)
Packit Service a04d08
        if not meth:
Packit Service a04d08
            meth = self._handle_unknown
Packit Service a04d08
            args.insert(0, method_name)
Packit Service a04d08
        return meth(*args)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
class LookupMerger(UnknownMerger):
Packit Service a04d08
    def __init__(self, lookups=None):
Packit Service a04d08
        UnknownMerger.__init__(self)
Packit Service a04d08
        if lookups is None:
Packit Service a04d08
            self._lookups = []
Packit Service a04d08
        else:
Packit Service a04d08
            self._lookups = lookups
Packit Service a04d08
Packit Service a04d08
    def __str__(self):
Packit Service a04d08
        return 'LookupMerger: (%s)' % (len(self._lookups))
Packit Service a04d08
Packit Service a04d08
    # For items which can not be merged by the parent this object
Packit Service a04d08
    # will lookup in a internally maintained set of objects and
Packit Service a04d08
    # find which one of those objects can perform the merge. If
Packit Service a04d08
    # any of the contained objects have the needed method, they
Packit Service a04d08
    # will be called to perform the merge.
Packit Service a04d08
    def _handle_unknown(self, meth_wanted, value, merge_with):
Packit Service a04d08
        meth = None
Packit Service a04d08
        for merger in self._lookups:
Packit Service a04d08
            if hasattr(merger, meth_wanted):
Packit Service a04d08
                # First one that has that method/attr gets to be
Packit Service a04d08
                # the one that will be called
Packit Service a04d08
                meth = getattr(merger, meth_wanted)
Packit Service a04d08
                break
Packit Service a04d08
        if not meth:
Packit Service a04d08
            return UnknownMerger._handle_unknown(self, meth_wanted,
Packit Service a04d08
                                                 value, merge_with)
Packit Service a04d08
        return meth(value, merge_with)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def dict_extract_mergers(config):
Packit Service a04d08
    parsed_mergers = []
Packit Service a04d08
    raw_mergers = config.pop('merge_how', None)
Packit Service a04d08
    if raw_mergers is None:
Packit Service a04d08
        raw_mergers = config.pop('merge_type', None)
Packit Service a04d08
    if raw_mergers is None:
Packit Service a04d08
        return parsed_mergers
Packit Service 751c4a
    if isinstance(raw_mergers, str):
Packit Service a04d08
        return string_extract_mergers(raw_mergers)
Packit Service a04d08
    for m in raw_mergers:
Packit Service a04d08
        if isinstance(m, (dict)):
Packit Service a04d08
            name = m['name']
Packit Service a04d08
            name = name.replace("-", "_").strip()
Packit Service a04d08
            opts = m['settings']
Packit Service a04d08
        else:
Packit Service a04d08
            name = m[0]
Packit Service a04d08
            if len(m) >= 2:
Packit Service a04d08
                opts = m[1:]
Packit Service a04d08
            else:
Packit Service a04d08
                opts = []
Packit Service a04d08
        if name:
Packit Service a04d08
            parsed_mergers.append((name, opts))
Packit Service a04d08
    return parsed_mergers
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def string_extract_mergers(merge_how):
Packit Service a04d08
    parsed_mergers = []
Packit Service a04d08
    for m_name in merge_how.split("+"):
Packit Service a04d08
        # Canonicalize the name (so that it can be found
Packit Service a04d08
        # even when users alter it in various ways)
Packit Service a04d08
        m_name = m_name.lower().strip()
Packit Service a04d08
        m_name = m_name.replace("-", "_")
Packit Service a04d08
        if not m_name:
Packit Service a04d08
            continue
Packit Service a04d08
        match = NAME_MTCH.match(m_name)
Packit Service a04d08
        if not match:
Packit Service a04d08
            msg = ("Matcher identifer '%s' is not in the right format" %
Packit Service a04d08
                   (m_name))
Packit Service a04d08
            raise ValueError(msg)
Packit Service a04d08
        (m_name, m_ops) = match.groups()
Packit Service a04d08
        m_ops = m_ops.strip().split(",")
Packit Service a04d08
        m_ops = [m.strip().lower() for m in m_ops if m.strip()]
Packit Service a04d08
        parsed_mergers.append((m_name, m_ops))
Packit Service a04d08
    return parsed_mergers
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def default_mergers():
Packit Service a04d08
    return tuple(string_extract_mergers(DEF_MERGE_TYPE))
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def construct(parsed_mergers):
Packit Service a04d08
    mergers_to_be = []
Packit Service a04d08
    for (m_name, m_ops) in parsed_mergers:
Packit Service a04d08
        if not m_name.startswith(MERGER_PREFIX):
Packit Service a04d08
            m_name = MERGER_PREFIX + str(m_name)
Packit Service a04d08
        merger_locs, looked_locs = importer.find_module(m_name,
Packit Service a04d08
                                                        [__name__],
Packit Service a04d08
                                                        [MERGER_ATTR])
Packit Service a04d08
        if not merger_locs:
Packit Service a04d08
            msg = ("Could not find merger module named '%s' "
Packit Service a04d08
                   "with attribute '%s' (searched %s)") % (m_name,
Packit Service a04d08
                                                           MERGER_ATTR,
Packit Service a04d08
                                                           looked_locs)
Packit Service a04d08
            raise ImportError(msg)
Packit Service a04d08
        else:
Packit Service a04d08
            mod = importer.import_module(merger_locs[0])
Packit Service a04d08
            mod_attr = getattr(mod, MERGER_ATTR)
Packit Service a04d08
            mergers_to_be.append((mod_attr, m_ops))
Packit Service a04d08
    # Now form them...
Packit Service a04d08
    mergers = []
Packit Service a04d08
    root = LookupMerger(mergers)
Packit Service a04d08
    for (attr, opts) in mergers_to_be:
Packit Service a04d08
        mergers.append(attr(root, opts))
Packit Service a04d08
    return root
Packit Service a04d08
Packit Service a04d08
# vi: ts=4 expandtab