|
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
|