Add the ability to merge distro settings from the persona/origins
For when its needed to specify specific overrides for the distro configuration due to a specific openstack release having certain known issues add a way for this to occur. Change-Id: Ib0534cc58c2d2c514a77881b608d51505c2849db
This commit is contained in:
parent
209310a76b
commit
2ef43ad2eb
|
@ -34,6 +34,7 @@ from anvil import distro
|
|||
from anvil import exceptions as excp
|
||||
from anvil import log as logging
|
||||
from anvil import opts
|
||||
from anvil import origins as _origins
|
||||
from anvil import persona
|
||||
from anvil import pprint
|
||||
from anvil import settings
|
||||
|
@ -95,6 +96,10 @@ def run(args):
|
|||
# Ensure the anvil dirs are there if others are about to use it...
|
||||
ensure_anvil_dirs(root_dir)
|
||||
|
||||
# Load the origins...
|
||||
origins = _origins.load(args['origins_fn'],
|
||||
patch_file=args.get('origins_patch'))
|
||||
|
||||
# Load the distro/s
|
||||
possible_distros = distro.load(settings.DISTRO_DIR,
|
||||
distros_patch=args.get('distros_patch'))
|
||||
|
@ -105,11 +110,17 @@ def run(args):
|
|||
except Exception as e:
|
||||
raise excp.OptionException("Error loading persona file: %s due to %s" % (persona_fn, e))
|
||||
else:
|
||||
dist = persona_obj.match(possible_distros, args['origins_fn'],
|
||||
origins_patch=args.get('origins_patch'))
|
||||
dist = persona_obj.match(possible_distros, origins)
|
||||
LOG.info('Persona selected distro: %s from %s possible distros',
|
||||
colorizer.quote(dist.name), len(possible_distros))
|
||||
|
||||
# Update the dist with any other info...
|
||||
dist.merge(**persona_obj.distro_updates)
|
||||
dist.merge(**origins)
|
||||
|
||||
# Print it out...
|
||||
print(dist.pformat(item_max_len=128))
|
||||
|
||||
# Get the object we will be running with...
|
||||
runner = runner_cls(distro=dist,
|
||||
root_dir=root_dir,
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import jsonpatch
|
||||
import re
|
||||
|
||||
from anvil import exceptions
|
||||
from anvil import origins as _origins
|
||||
from anvil import settings
|
||||
from anvil import shell as sh
|
||||
from anvil import utils
|
||||
|
@ -76,10 +76,8 @@ class YamlMergeLoader(object):
|
|||
origins_opts = {}
|
||||
if self._origins_path:
|
||||
try:
|
||||
origins = utils.load_yaml(self._origins_path)
|
||||
if origins_patch:
|
||||
patch = jsonpatch.JsonPatch(origins_patch)
|
||||
patch.apply(origins, in_place=True)
|
||||
origins = _origins.load(self._origins_path,
|
||||
patch_file=origins_patch)
|
||||
origins_opts = origins[component]
|
||||
except KeyError:
|
||||
pass
|
||||
|
|
|
@ -29,6 +29,7 @@ import six
|
|||
from anvil import exceptions as excp
|
||||
from anvil import importer
|
||||
from anvil import log as logging
|
||||
from anvil import pprint
|
||||
from anvil import shell as sh
|
||||
from anvil import utils
|
||||
|
||||
|
@ -44,12 +45,22 @@ class Distro(object):
|
|||
install_helper, dependency_handler,
|
||||
commands, components):
|
||||
self.name = name
|
||||
self._platform_pattern_text = platform_pattern
|
||||
self._platform_pattern = re.compile(platform_pattern, re.IGNORECASE)
|
||||
self._install_helper = install_helper
|
||||
self._dependency_handler = dependency_handler
|
||||
self._commands = commands
|
||||
self._components = components
|
||||
|
||||
def pformat(self, item_max_len=None):
|
||||
data = {
|
||||
'name': self.name,
|
||||
'dependency_handler': self._dependency_handler,
|
||||
'commands': self._commands,
|
||||
'pattern': "/%s/i" % self._platform_pattern_text,
|
||||
}
|
||||
return pprint.pformat(data, item_max_len=item_max_len)
|
||||
|
||||
def _fetch_value(self, root, keys, quiet):
|
||||
end_key = keys[-1]
|
||||
for k in keys[0:-1]:
|
||||
|
@ -134,6 +145,12 @@ class Distro(object):
|
|||
else:
|
||||
return Component(entry_point, component_info, action_classes)
|
||||
|
||||
def merge(self, **kwargs):
|
||||
if 'dependency_handler' in kwargs:
|
||||
self._dependency_handler = utils.recursive_merge(
|
||||
self._dependency_handler,
|
||||
kwargs['dependency_handler'])
|
||||
|
||||
|
||||
def _match_distros(distros):
|
||||
plt = platform.platform()
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (C) 2014 Yahoo! Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import jsonpatch
|
||||
|
||||
from anvil import utils
|
||||
|
||||
|
||||
class Origin(dict):
|
||||
def __init__(self, filename, patched=False):
|
||||
super(Origin, self).__init__()
|
||||
self.filename = filename
|
||||
self.patched = patched
|
||||
|
||||
|
||||
def load(filename, patch_file=None):
|
||||
base = utils.load_yaml(filename)
|
||||
patched = False
|
||||
if patch_file:
|
||||
patch = jsonpatch.JsonPatch(patch_file)
|
||||
patch.apply(base, in_place=True)
|
||||
patched = True
|
||||
origin = Origin(filename, patched=patched)
|
||||
origin.update(base)
|
||||
return origin
|
|
@ -14,8 +14,6 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import jsonpatch
|
||||
|
||||
import six
|
||||
|
||||
from anvil import colorizer
|
||||
|
@ -28,22 +26,17 @@ SPECIAL_GROUPS = frozenset(['general'])
|
|||
|
||||
class Persona(object):
|
||||
|
||||
def __init__(self, supports, components, **kargs):
|
||||
def __init__(self, supports, components, **kwargs):
|
||||
self.distro_support = supports or []
|
||||
self.source = kargs.get('source')
|
||||
self.source = kwargs.pop('source', None)
|
||||
self.wanted_components = utils.group_builds(components)
|
||||
self.wanted_subsystems = kargs.get('subsystems') or {}
|
||||
self.component_options = kargs.get('options') or {}
|
||||
self.no_origins = kargs.get('no-origin') or []
|
||||
self.wanted_subsystems = kwargs.pop('subsystems', {})
|
||||
self.component_options = kwargs.pop('options', {})
|
||||
self.no_origins = kwargs.pop('no-origin', [])
|
||||
self.matched_components = []
|
||||
self.distro_updates = kwargs
|
||||
|
||||
def match(self, distros, origins_fn, origins_patch=None):
|
||||
# Filter out components that are disabled in origins file
|
||||
origins = utils.load_yaml(origins_fn)
|
||||
# Apply any user specified patches to origins file
|
||||
if origins_patch:
|
||||
patch = jsonpatch.JsonPatch(origins_patch)
|
||||
patch.apply(origins, in_place=True)
|
||||
def match(self, distros, origins):
|
||||
for group in self.wanted_components:
|
||||
for c in group:
|
||||
if c not in origins:
|
||||
|
@ -51,7 +44,7 @@ class Persona(object):
|
|||
LOG.debug("Automatically enabling component %s, not"
|
||||
" present in origins file %s but present in"
|
||||
" desired persona %s (origin not required).",
|
||||
c, origins_fn, self.source)
|
||||
c, origins.filename, self.source)
|
||||
origins[c] = {
|
||||
'disabled': False,
|
||||
}
|
||||
|
|
|
@ -306,6 +306,61 @@ def iso8601():
|
|||
return datetime.now().isoformat()
|
||||
|
||||
|
||||
def recursive_merge(a, b):
|
||||
# pylint: disable=C0103
|
||||
|
||||
def _merge_lists(a, b):
|
||||
merged = []
|
||||
merged.extend(a)
|
||||
merged.extend(b)
|
||||
return merged
|
||||
|
||||
def _merge_dicts(a, b):
|
||||
merged = {}
|
||||
for k in six.iterkeys(a):
|
||||
if k in b:
|
||||
merged[k] = recursive_merge(a[k], b[k])
|
||||
else:
|
||||
merged[k] = a[k]
|
||||
for k in six.iterkeys(b):
|
||||
if k in merged:
|
||||
continue
|
||||
merged[k] = b[k]
|
||||
return merged
|
||||
|
||||
def _merge_text(a, b):
|
||||
return b
|
||||
|
||||
def _merge_int(a, b):
|
||||
return b
|
||||
|
||||
def _merge_float(a, b):
|
||||
return b
|
||||
|
||||
def _merge_bool(a, b):
|
||||
return b
|
||||
|
||||
mergers = [
|
||||
(list, list, _merge_lists),
|
||||
(list, tuple, _merge_lists),
|
||||
(tuple, tuple, _merge_lists),
|
||||
(tuple, list, _merge_lists),
|
||||
(dict, dict, _merge_dicts),
|
||||
(six.string_types, six.string_types, _merge_text),
|
||||
(int, int, _merge_int),
|
||||
(bool, bool, _merge_bool),
|
||||
(float, float, _merge_float),
|
||||
]
|
||||
merger = None
|
||||
for (a_type, b_type, func) in mergers:
|
||||
if isinstance(a, a_type) and isinstance(b, b_type):
|
||||
merger = func
|
||||
break
|
||||
if not merger:
|
||||
raise TypeError("Unknown how to merge '%s' with '%s'" % (type(a), type(b)))
|
||||
return merger(a, b)
|
||||
|
||||
|
||||
def merge_dicts(*dicts, **kwargs):
|
||||
merged = OrderedDict()
|
||||
for mp in dicts:
|
||||
|
|
Loading…
Reference in New Issue