diff --git a/compass/config_management/utils/config_filter.py b/compass/config_management/utils/config_filter.py index 15cae49c..98983a26 100644 --- a/compass/config_management/utils/config_filter.py +++ b/compass/config_management/utils/config_filter.py @@ -48,9 +48,10 @@ class ConfigFilter(object): type(self.allows_), self.allows_)) for i, allow in enumerate(self.allows_): - if not isinstance(allow, str): + if not isinstance(allow, basestring): raise TypeError( - 'allows[%s] type is %s but expected type is str: %s' % ( + 'allows[%s] type is %s but expected type ' + 'is str or unicode: %s' % ( i, type(allow), allow)) def _is_denies_valid(self): @@ -61,9 +62,10 @@ class ConfigFilter(object): type(self.denies_), self.denies_)) for i, deny in enumerate(self.denies_): - if not isinstance(deny, str): + if not isinstance(deny, basestring): raise TypeError( - 'denies[%s] type is %s but expected type is str: %s' % ( + 'denies[%s] type is %s but expected type ' + 'is str or unicode: %s' % ( i, type(deny), deny)) def _is_valid(self): diff --git a/compass/config_management/utils/config_merger.py b/compass/config_management/utils/config_merger.py index 8a55e717..92764394 100644 --- a/compass/config_management/utils/config_merger.py +++ b/compass/config_management/utils/config_merger.py @@ -54,6 +54,7 @@ class ConfigMapping(object): self.override_ = override self.override_conditions_ = override_conditions self.value_ = value + self._is_valid() def __repr__(self): return ( @@ -69,20 +70,36 @@ class ConfigMapping(object): def _is_valid_path_list(self): """Check path_list are valid.""" + if not isinstance(self.path_list_, list): + raise TypeError( + 'path_list %s type is %s while expected type is list' % ( + self.path_list_, type(self.path_list_))) + for i, path in enumerate(self.path_list_): - if not isinstance(path, str): + if not isinstance(path, basestring): raise TypeError( 'path_list[%d] type is %s while ' - 'expected type is str: %s' % ( + 'expected type is str or unicode: %s' % ( i, type(path), path)) def _is_valid_from_upper_keys(self): """Check from_upper_keys are valid.""" + if not isinstance(self.from_upper_keys_, dict): + raise TypeError( + 'from_upper_keys type is %s while expected is dict', + type(self.from_upper_keys_)) + for mapping_key, from_upper_key in self.from_upper_keys_.items(): - if not isinstance(from_upper_key, str): + if not isinstance(mapping_key, basestring): + raise TypeError( + 'key %s in from_upper_keys type is %s' + 'while expected type is str or unicode' % ( + mapping_key, type(mapping_key))) + + if not isinstance(from_upper_key, basestring): raise TypeError( 'from_upper_keys[%s] type is %s' - 'while expected type is str: %s' % ( + 'while expected type is str or unicode: %s' % ( mapping_key, type(from_upper_key), from_upper_key)) if '*' in from_upper_key: @@ -92,11 +109,22 @@ class ConfigMapping(object): def _is_valid_from_lower_keys(self): """Check from_lower_keys are valid.""" + if not isinstance(self.from_lower_keys_, dict): + raise TypeError( + 'from_lower_keys type is %s while expected type is dict', + type(self.from_lower_keys_)) + for mapping_key, from_lower_key in self.from_lower_keys_.items(): - if not isinstance(from_lower_key, str): + if not isinstance(mapping_key, basestring): + raise TypeError( + 'key %s in from_lower_keys type is %s' + 'while expected type is str or unicode: %s' % ( + mapping_key, type(mapping_key))) + + if not isinstance(from_lower_key, basestring): raise TypeError( 'from_lower_keys[%s] type' - 'is %s while expected type is str: %s' % ( + 'is %s while expected type is str or unicode: %s' % ( mapping_key, type(from_lower_key), from_lower_key)) if '*' in from_lower_key: @@ -119,14 +147,30 @@ class ConfigMapping(object): def _is_valid_to_key(self): """Check to_key is valid.""" + if not isinstance(self.to_key_, basestring): + raise TypeError( + 'to_key %s type is %s ' + 'while expected type is [str, unicode]' % ( + self.to_key_, type(self.to_key_))) + if '*' in self.to_key_: raise KeyError('to_key %s contains *' % self.to_key_) def _is_valid_override_conditions(self): """Check override conditions are valid.""" + if not isinstance(self.override_conditions_, dict): + raise TypeError( + 'override_conditions type is %s while expected type is dict', + type(self.override_conditions_)) override_items = self.override_conditions_.items() for mapping_key, override_condition in override_items: - if not util.is_instance(override_condition, [str, unicode]): + if not isinstance(mapping_key, basestring): + raise TypeError( + 'overrid_conditions key %s type is %s ' + 'while expected type is [str, unicode]' % ( + mapping_key, type(mapping_key))) + + if not isinstance(override_condition, basestring): raise TypeError( 'override_conditions[%s] type is %s ' 'while expected type is [str, unicode]: %s' % ( @@ -198,10 +242,10 @@ class ConfigMapping(object): return lower_values - return self.value_(sub_ref, ref_key, lower_sub_refs, - self.to_key_, **sub_configs) + return self.value_(sub_ref, ref_key, lower_sub_refs, self.to_key_, + **sub_configs) - def _get_override(self, ref_key, sub_ref): + def _get_override(self, ref_key, sub_ref, to_key, lower_to_ref): """Get override from ref_key, ref from ref_key.""" if not callable(self.override_): return bool(self.override_) @@ -210,13 +254,13 @@ class ConfigMapping(object): override_items = self.override_conditions_.items() for mapping_key, override_condition in override_items: if override_condition in sub_ref: - override_condition_configs[mapping_key] = \ - sub_ref[override_condition] + override_condition_configs[mapping_key] = ( + sub_ref[override_condition]) else: logging.info('%s no override condition %s in %s', self, override_condition, ref_key) - return self.override_(sub_ref, ref_key, + return self.override_(sub_ref, ref_key, lower_to_ref, to_key, **override_condition_configs) def merge(self, upper_ref, lower_refs): @@ -247,7 +291,8 @@ class ConfigMapping(object): value = values[lower_key] lower_to_ref = lower_sub_ref.setdefault(self.to_key_) - override = self._get_override(self.to_key_, lower_to_ref) + override = self._get_override( + ref_key, sub_ref, self.to_key_, lower_to_ref) lower_to_ref.update(value, override) @@ -273,6 +318,13 @@ class ConfigMerger(object): self.__class__.__name__, type(self.mappings_), self.mappings_)) + for i, mapping in enumerate(self.mappings_): + if not isinstance(mapping, ConfigMapping): + raise TypeError( + '%s mappings[%s] type is %s ' + 'while expected type is ConfigMapping' % ( + self.__class__.__name__, i, type(mapping))) + def merge(self, upper_config, lower_configs): """Merge cluster config to host configs. diff --git a/compass/config_management/utils/config_merger_callbacks.py b/compass/config_management/utils/config_merger_callbacks.py index 245b724f..6168e158 100644 --- a/compass/config_management/utils/config_merger_callbacks.py +++ b/compass/config_management/utils/config_merger_callbacks.py @@ -20,6 +20,7 @@ import copy import itertools import logging import netaddr +import re from compass.utils import util @@ -214,7 +215,14 @@ def _assign_roles_by_mins(role_bundles, lower_roles, unassigned_hosts, bundled_mins[bundled_role]) bundled_maxs[bundled_role] = _dec_max_min( bundled_maxs[bundled_role]) - lower_roles[host] = list(roles) + if host not in lower_roles: + lower_roles[host] = list(roles) + elif set(lower_roles[host]) & roles: + duplicated_roles = set(lower_roles[host]) & roles + raise ValueError( + 'duplicated roles %s on %s' % (duplicated_roles, host)) + else: + lower_roles[host].extend(list(roles)) logging.debug('assigned roles after assigning mins: %s', lower_roles) logging.debug('unassigned_hosts after assigning mins: %s', @@ -311,10 +319,11 @@ def assign_roles(_upper_ref, _from_key, lower_refs, to_key, def assign_roles_by_host_numbers(upper_ref, from_key, lower_refs, to_key, policy_by_host_numbers={}, default={}, - **_kwargs): + **kwargs): """Assign roles by role assign policy.""" host_numbers = str(len(lower_refs)) - policy_kwargs = copy.deepcopy(default) + policy_kwargs = copy.deepcopy(kwargs) + util.merge_dict(policy_kwargs, default) if host_numbers in policy_by_host_numbers: util.merge_dict(policy_kwargs, policy_by_host_numbers[host_numbers]) else: @@ -349,10 +358,25 @@ def assign_ips(_upper_ref, _from_key, lower_refs, to_key, **_kwargs): """Assign ips to hosts' configurations.""" if not ip_start or not ip_end: - return {} + raise ValueError( + 'ip_start %s or ip_end %s is empty' % (ip_start, ip_end)) + + if not re.match(r'^\d+\.\d+\.\d+\.\d+$', ip_start): + raise ValueError( + 'ip_start %s formmat is not correct' % ip_start) + + if not re.match(r'^\d+\.\d+\.\d+\.\d+$', ip_end): + raise ValueError( + 'ip_end %s format is not correct' % ip_end) + host_ips = {} unassigned_hosts = [] - ips = netaddr.IPSet(netaddr.IPRange(ip_start, ip_end)) + try: + ips = netaddr.IPSet(netaddr.IPRange(ip_start, ip_end)) + except Exception: + raise ValueError( + 'failed to create ip block [%s, %s]' % (ip_start, ip_end)) + for lower_key, lower_ref in lower_refs.items(): ip_addr = lower_ref.get(to_key, '') if ip_addr: @@ -368,6 +392,11 @@ def assign_ips(_upper_ref, _from_key, lower_refs, to_key, host = unassigned_hosts.pop(0) host_ips[host] = str(ip_addr) + if unassigned_hosts: + raise ValueError( + 'there is no enough ips to assign to %s: [%s-%s]' % ( + unassigned_hosts, ip_start, ip_end)) + logging.debug('assign %s: %s', to_key, host_ips) return host_ips @@ -377,17 +406,34 @@ def assign_from_pattern(_upper_ref, _from_key, lower_refs, to_key, """assign to_key by pattern.""" host_values = {} upper_configs = {} + if set(upper_keys) & set(lower_keys): + raise KeyError( + 'overlap between upper_keys %s and lower_keys %s' % ( + upper_keys, lower_keys)) + for key in upper_keys: + if key not in kwargs: + raise KeyError( + 'param %s is missing' % key) + upper_configs[key] = kwargs[key] for lower_key, _ in lower_refs.items(): group = copy.deepcopy(upper_configs) for key in lower_keys: + if key not in kwargs: + raise KeyError('param %s is missing' % key) + + if not isinstance(kwargs[key], dict): + raise KeyError( + 'param %s type is %s while expected type is dict' % ( + kwargs[key], type(kwargs[key]))) + group[key] = kwargs[key][lower_key] try: host_values[lower_key] = pattern % group - except Exception as error: + except KeyError as error: logging.error('failed to assign %s[%s] = %s %% %s', lower_key, to_key, pattern, group) raise error @@ -401,16 +447,29 @@ def assign_noproxy(_upper_ref, _from_key, lower_refs, hostnames={}, ips={}, **_kwargs): """Assign no proxy to hosts.""" no_proxy_list = copy.deepcopy(default) + if not clusterid: + raise KeyError( + 'clusterid %s is empty' % clusterid) for lower_key, _ in lower_refs.items(): + if lower_key not in hostnames: + raise KeyError( + 'lower_key %s is not in hostnames %s' % ( + lower_key, hostnames)) + + if lower_key not in ips: + raise KeyError( + 'lower_key %s is not in ips %s' % ( + lower_key, ips)) + mapping = { 'clusterid': clusterid, - 'hostname': hostnames.get(lower_key, ''), - 'ip': ips.get(lower_key, '') + 'hostname': hostnames[lower_key], + 'ip': ips[lower_key] } try: no_proxy_list.append(noproxy_pattern % mapping) - except Exception as error: + except KeyError as error: logging.error('failed to assign %s[%s] = %s %% %s', lower_key, to_key, noproxy_pattern, mapping) raise error @@ -423,7 +482,7 @@ def assign_noproxy(_upper_ref, _from_key, lower_refs, return host_no_proxy -def override_if_empty(lower_ref, _ref_key): +def override_if_empty(_upper_ref, _ref_key, lower_ref, _to_key): """Override if the configuration value is empty.""" if not lower_ref.config: return True diff --git a/compass/config_management/utils/config_reference.py b/compass/config_management/utils/config_reference.py index 80ef2a1e..c26b0293 100644 --- a/compass/config_management/utils/config_reference.py +++ b/compass/config_management/utils/config_reference.py @@ -63,11 +63,11 @@ class ConfigReference(object): :raises: TypeError """ - if parent and not isinstance(parent, self.__class__): + if parent and not isinstance(parent, ConfigReference): raise TypeError('parent %s type should be %s' - % (parent, self.__class__.__name__))\ + % (parent, ConfigReference)) - if parent_key and not util.is_instance(parent_key, [str, unicode]): + if parent_key and not isinstance(parent_key, basestring): raise TypeError('parent_key %s type should be [str, unicode]' % parent_key) @@ -90,7 +90,7 @@ class ConfigReference(object): if config and isinstance(config, dict): for key, value in config.items(): - if not util.is_instance(key, [str, unicode]): + if not isinstance(key, basestring): msg = 'key type is %s while expected is [str, unicode]: %s' raise TypeError(msg % (type(key), key)) ConfigReference(value, self, key) @@ -147,7 +147,7 @@ class ConfigReference(object): parts = [] - if util.is_instance(path, [str, unicode]): + if isinstance(path, basestring): parts = path.split('/') else: parts = path diff --git a/compass/config_management/utils/config_translator.py b/compass/config_management/utils/config_translator.py index 3492c693..a4f0f7f4 100644 --- a/compass/config_management/utils/config_translator.py +++ b/compass/config_management/utils/config_translator.py @@ -19,7 +19,6 @@ import logging from compass.config_management.utils import config_reference -from compass.utils import util class KeyTranslator(object): @@ -65,8 +64,14 @@ class KeyTranslator(object): if callable(self.translated_keys_): return + if not isinstance(self.translated_keys_, list): + raise TypeError( + 'translated_keys %s type is %s while expected type is ' + 'list or callable' % ( + self.translated_keys_, type(self.translated_keys_))) + for i, translated_key in enumerate(self.translated_keys_): - if util.is_instance(translated_key, [str, unicode]): + if isinstance(translated_key, basestring): if '*' in translated_key: raise KeyError( 'transalted_keys[%d] %s should not contain *' % ( @@ -79,8 +84,19 @@ class KeyTranslator(object): def _is_valid_from_keys(self): """Check from keys are valid.""" + if not isinstance(self.from_keys_, dict): + raise TypeError( + 'from_keys %s type is %s while expected type is dict' % ( + self.from_keys_, type(self.from_keys_))) + for mapping_key, from_key in self.from_keys_.items(): - if not util.is_instance(from_key, [str, unicode]): + if not isinstance(mapping_key, basestring): + raise TypeError( + 'from_keys key %s type is %s while ' + 'expected type is [str, unicode]' % ( + mapping_key, type(mapping_key))) + + if not isinstance(from_key, basestring): raise TypeError( 'from_keys[%s] type is %s while ' 'expected type is [str, unicode]: %s' % ( @@ -93,8 +109,19 @@ class KeyTranslator(object): def _is_valid_from_values(self): """Check from values are valid.""" + if not isinstance(self.from_values_, dict): + raise TypeError( + 'from_values %s type is %s while expected type is dict' % ( + self.from_values_, type(self.from_values_))) + for mapping_key, from_value in self.from_values_.items(): - if not util.is_instance(from_value, [str, unicode]): + if not isinstance(mapping_key, basestring): + raise TypeError( + 'from_values key %s type is %s while ' + 'expected type is [str, unicode]' % ( + mapping_key, type(mapping_key))) + + if not isinstance(from_value, basestring): raise TypeError( 'from_values[%s] type is %s while ' 'expected type is [str, unicode]: %s' % ( @@ -107,9 +134,22 @@ class KeyTranslator(object): def _is_valid_override_conditions(self): """Check override conditions are valid.""" + if not isinstance(self.override_conditions_, dict): + raise TypeError( + 'override_conditions %s type is %s ' + 'while expected type is dict' % ( + self.override_conditions_, + type(self.override_conditions_))) + override_items = self.override_conditions_.items() for mapping_key, override_condition in override_items: - if not util.is_instance(override_condition, [str, unicode]): + if not isinstance(mapping_key, basestring): + raise TypeError( + 'override_conditions key %s type is %s while ' + 'expected type is [str, unicode]' % ( + mapping_key, type(mapping_key))) + + if not isinstance(override_condition, basestring): raise TypeError( 'override_conditions[%s] type is %s ' 'while expected type is [str, unicode]: %s' % ( @@ -153,7 +193,7 @@ class KeyTranslator(object): logging.debug('%s ignore empty translated key', self) continue - if not util.is_instance(translated_key, [str, unicode]): + if not isinstance(translated_key, basestring): logging.error( '%s translated key %s should be [str, unicode]', self, translated_key) @@ -188,7 +228,7 @@ class KeyTranslator(object): translated_key, translated_sub_ref): """Get override.""" if not callable(self.override_): - return self.override_ + return bool(self.override_) override_condition_configs = {} override_items = self.override_conditions_.items() @@ -216,6 +256,9 @@ class KeyTranslator(object): ref_key, sub_ref, translated_key, translated_sub_ref) if translated_value is None: + logging.debug( + 'translated key %s will be ignored ' + 'since translated value is None', translated_key) continue override = self._get_override( @@ -247,16 +290,23 @@ class ConfigTranslator(object): type(self.mapping_), self.mapping_)) for key, values in self.mapping_.items(): + if not isinstance(key, basestring): + raise TypeError( + 'mapping key %s type is %s while expected ' + 'is str or unicode' % (key, type(key))) + if not isinstance(values, list): - msg = 'mapping[%s] type is %s while expected type is list: %s' - raise TypeError(msg % (key, type(values), values)) + raise TypeError( + 'mapping[%s] type is %s ' + 'while expected type is list: %s' % ( + key, type(values), values)) for i, value in enumerate(values): if not isinstance(value, KeyTranslator): - msg = ( + raise TypeError( 'mapping[%s][%d] type is %s ' - 'while expected type is KeyTranslator: %s') - raise TypeError(msg % (key, i, type(value), value)) + 'while expected type is KeyTranslator: %s' % ( + key, i, type(value), value)) def translate(self, config): """Translate config. diff --git a/compass/db/database.py b/compass/db/database.py index 81553d30..d7437b07 100644 --- a/compass/db/database.py +++ b/compass/db/database.py @@ -17,7 +17,8 @@ import logging from contextlib import contextmanager from sqlalchemy import create_engine -from sqlalchemy.orm import scoped_session, sessionmaker +from sqlalchemy.orm import scoped_session +from sqlalchemy.orm import sessionmaker from threading import local from compass.db import model diff --git a/compass/tests/config_management/utils/1 b/compass/tests/config_management/utils/1 new file mode 100644 index 00000000..3808fb33 --- /dev/null +++ b/compass/tests/config_management/utils/1 @@ -0,0 +1,300 @@ +# Copyright 2014 Huawei Technologies Co. Ltd +# +# 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. + +"""Module to set the hosts configs from cluster config. + + .. moduleauthor:: Xiaodong Wang +""" +import copy +import logging + +from compass.config_management.utils import config_reference +from compass.utils import util + + +class ConfigMapping(object): + """Class to merge cluster config ref to host config ref by path list.""" + + def __init__(self, path_list, from_upper_keys={}, + from_lower_keys={}, to_key='.', + override=False, override_conditions={}, + value=None): + """Constructor + + :param path_list: list of path to merge from cluster ref to host refs + :type path_list: list of str + :param from_upper_keys: kwargs from cluster ref for value callback. + :type from_upper_keys: dict of kwargs name to path in cluster ref + :param from_lower_keys: kwargs from host refs for value callback. + :type from_lower_keys: dict of kwargs name to path in host refs. + :param to_key: the path in host refs to be merged to. + :type to_key: str + :param override: if the path in host ref can be overridden. + :type override: callback or bool + :param override_conditions: kwargs from host ref for override callback + :type override_conditions: dict of kwargs name to path in host ref + :param value: the value to be set in host refs. + :type value: callback or any type + """ + self.path_list_ = path_list + self.from_upper_keys_ = from_upper_keys + self.from_lower_keys_ = from_lower_keys + self.to_key_ = to_key + self.override_ = override + self.override_conditions_ = override_conditions + self.value_ = value + self.is_valid() + + def __repr__(self): + return ( + '%s[path_list=%s,from_upper_keys=%s,' + 'from_lower_keys=%s,to_key=%s,override=%s,' + 'override_conditions=%s,value=%s]' + ) % ( + self.__class__.__name__, + self.path_list_, self.from_upper_keys_, + self.from_lower_keys_, self.to_key_, + self.override_, self.override_conditions_, + self.value_) + + def _is_valid_path_list(self): + """Check path_list are valid.""" + for i, path in enumerate(self.path_list_): + if not isinstance(path, str): + raise TypeError( + 'path_list[%d] type is %s while ' + 'expected type is str: %s' % ( + i, type(path), path)) + + def _is_valid_from_upper_keys(self): + """Check from_upper_keys are valid.""" + for mapping_key, from_upper_key in self.from_upper_keys_.items(): + if not isinstance(from_upper_key, str): + raise TypeError( + 'from_upper_keys[%s] type is %s' + 'while expected type is str: %s' % ( + mapping_key, type(from_upper_key), from_upper_key)) + + if '*' in from_upper_key: + raise KeyError( + 'from_upper_keys[%s] %s contains *' % ( + mapping_key, from_upper_key)) + + def _is_valid_from_lower_keys(self): + """Check from_lower_keys are valid.""" + for mapping_key, from_lower_key in self.from_lower_keys_.items(): + if not isinstance(from_lower_key, str): + raise TypeError( + 'from_lower_keys[%s] type' + 'is %s while expected type is str: %s' % ( + mapping_key, type(from_lower_key), from_lower_key)) + + if '*' in from_lower_key: + raise KeyError( + 'from_lower_keys[%s] %s contains *' % ( + mapping_key, from_lower_key)) + + def _is_valid_from_keys(self): + """Check from keys are valid.""" + self._is_valid_from_upper_keys() + self._is_valid_from_lower_keys() + upper_keys = set(self.from_upper_keys_.keys()) + lower_keys = set(self.from_lower_keys_.keys()) + intersection = upper_keys.intersection(lower_keys) + if intersection: + raise KeyError( + 'there is intersection between from_upper_keys %s' + ' and from_lower_keys %s: %s' % ( + upper_keys, lower_keys, intersection)) + + def _is_valid_to_key(self): + """Check to_key is valid.""" + if '*' in self.to_key_: + raise KeyError('to_key %s contains *' % self.to_key_) + + def _is_valid_override_conditions(self): + """Check override conditions are valid.""" + override_items = self.override_conditions_.items() + for mapping_key, override_condition in override_items: + if not util.is_instance(override_condition, [str, unicode]): + raise TypeError( + 'override_conditions[%s] type is %s ' + 'while expected type is [str, unicode]: %s' % ( + mapping_key, type(override_condition), + override_condition)) + + if '*' in override_condition: + raise KeyError( + 'override_conditions[%s] %s contains *' % ( + mapping_key, override_condition)) + + def is_valid(self): + """Check ConfigMapping instance is valid.""" + self._is_valid_path_list() + self._is_valid_from_keys() + self._is_valid_to_key() + self._is_valid_override_conditions() + + def _get_upper_sub_refs(self, upper_ref): + """get sub_refs from upper_ref.""" + upper_refs = [] + for path in self.path_list_: + upper_refs.extend(upper_ref.ref_items(path)) + + return upper_refs + + def _get_mapping_from_upper_keys(self, ref_key, sub_ref): + """Get upper config mapping from from_upper_keys.""" + sub_configs = {} + for mapping_key, from_upper_key in self.from_upper_keys_.items(): + if from_upper_key in sub_ref: + sub_configs[mapping_key] = sub_ref[from_upper_key] + else: + logging.info('%s ignore from_upper_key %s in %s', + self, from_upper_key, ref_key) + return sub_configs + + def _get_mapping_from_lower_keys(self, ref_key, lower_sub_refs): + """Get lower config mapping from from_lower_keys.""" + sub_configs = {} + for mapping_key, from_lower_key in self.from_lower_keys_.items(): + sub_configs[mapping_key] = {} + + for lower_key, lower_sub_ref in lower_sub_refs.items(): + for mapping_key, from_lower_key in self.from_lower_keys_.items(): + if from_lower_key in lower_sub_ref: + sub_configs[mapping_key][lower_key] = ( + lower_sub_ref[from_lower_key]) + else: + logging.error( + '%s ignore from_lower_key %s in %s lower_key %s', + self, from_lower_key, ref_key, lower_key) + + return sub_configs + + def _get_values(self, ref_key, sub_ref, lower_sub_refs, sub_configs): + """Get values to set to lower configs.""" + if self.value_ is None: + lower_values = {} + for lower_key in lower_sub_refs.keys(): + lower_values[lower_key] = copy.deepcopy(sub_ref.config) + + return lower_values + + if not callable(self.value_): + lower_values = {} + for lower_key in lower_sub_refs.keys(): + lower_values[lower_key] = copy.deepcopy(self.value_) + + return lower_values + + return self.value_(sub_ref, ref_key, lower_sub_refs, + self.to_key_, **sub_configs) + + def _get_override(self, ref_key, sub_ref): + """Get override from ref_key, ref from ref_key.""" + if not callable(self.override_): + return bool(self.override_) + + override_condition_configs = {} + override_items = self.override_conditions_.items() + for mapping_key, override_condition in override_items: + if override_condition in sub_ref: + override_condition_configs[mapping_key] = \ + sub_ref[override_condition] + else: + logging.info('%s no override condition %s in %s', + self, override_condition, ref_key) + + return self.override_(sub_ref, ref_key, + **override_condition_configs) + + def merge(self, upper_ref, lower_refs): + """merge upper config to lower configs.""" + upper_sub_refs = self._get_upper_sub_refs(upper_ref) + + for ref_key, sub_ref in upper_sub_refs: + sub_configs = self._get_mapping_from_upper_keys(ref_key, sub_ref) + + lower_sub_refs = {} + for lower_key, lower_ref in lower_refs.items(): + lower_sub_refs[lower_key] = lower_ref.setdefault(ref_key) + + lower_sub_configs = self._get_mapping_from_lower_keys( + ref_key, lower_sub_refs) + + util.merge_dict(sub_configs, lower_sub_configs) + + values = self._get_values( + ref_key, sub_ref, lower_sub_refs, sub_configs) + + logging.debug('%s set values %s to %s', + ref_key, self.to_key_, values) + for lower_key, lower_sub_ref in lower_sub_refs.items(): + if lower_key not in values: + logging.error('no key %s in %s', lower_key, values) + continue + + value = values[lower_key] + lower_to_ref = lower_sub_ref.setdefault(self.to_key_) + override = self._get_override(self.to_key_, lower_to_ref) + lower_to_ref.update(value, override) + + +class ConfigMerger(object): + """Class to merge clsuter config to host configs.""" + + def __init__(self, mappings): + """Constructor + + :param mappings: list of :class:`ConfigMapping` instance + """ + self.mappings_ = mappings + self.is_valid() + + def __repr__(self): + return '%s[mappings=%s]' % (self.__class__.__name__, self.mappings_) + + def is_valid(self): + """Check ConfigMerger instance is valid.""" + if not isinstance(self.mappings_, list): + raise TypeError( + '%s mapping type is %s while expect type is list: %s' % ( + self.__class__.__name__, type(self.mappings_), + self.mappings_)) + + def merge(self, upper_config, lower_configs): + """Merge cluster config to host configs. + + :param upper_config: cluster configuration to merge from. + :type upper_config: dict + :param lower_configs: host configurations to merge to. + :type lower_configs: dict of host id to host config as dict + """ + upper_ref = config_reference.ConfigReference(upper_config) + lower_refs = {} + for lower_key, lower_config in lower_configs.items(): + lower_refs[lower_key] = config_reference.ConfigReference( + lower_config) + + for mapping in self.mappings_: + logging.debug('apply merging from the rule %s', mapping) + mapping.merge(upper_ref, lower_refs) + + for lower_key, lower_config in lower_configs.items(): + lower_configs[lower_key] = config_reference.get_clean_config( + lower_config) + + logging.debug('merged upper config\n%s\nto lower configs:\n%s', + upper_config, lower_configs) diff --git a/compass/tests/config_management/utils/test_config_filter.py b/compass/tests/config_management/utils/test_config_filter.py index 33af56ae..e61a0500 100755 --- a/compass/tests/config_management/utils/test_config_filter.py +++ b/compass/tests/config_management/utils/test_config_filter.py @@ -40,53 +40,96 @@ class TestConfigFilter(unittest2.TestCase): def setUp(self): super(TestConfigFilter, self).setUp() logsetting.init() + self.config_ = { + '1': '1', + '2': { + '22': '22', + '33': { + '333': '333', + '44': '444' + } + }, + '3': {'33': '44'} + } def tearDown(self): super(TestConfigFilter, self).tearDown() - def test_allows(self): + def test_init(self): + config_filter.ConfigFilter( + allows=['abc', 'def'], denies=['def', 'ghi']) + config_filter.ConfigFilter( + allows=[u'abc', u'def'], denies=[u'def', u'ghi']) + + def test_init_allows(self): + # allows type should be a list of string. + self.assertRaises( + TypeError, config_filter.ConfigFilter, + allows={'abd': 'abc'}) + self.assertRaises( + TypeError, config_filter.ConfigFilter, + allows='abc') + self.assertRaises( + TypeError, config_filter.ConfigFilter, + allows=[{'abc': 'bdc'}]) + + def test_init_denies(self): + # denies type should be a list of string. + self.assertRaises( + TypeError, config_filter.ConfigFilter, + denies={'abd': 'abc'}) + self.assertRaises( + TypeError, config_filter.ConfigFilter, + denies='abc') + self.assertRaises( + TypeError, config_filter.ConfigFilter, + denies=[{'abc': 'bdc'}]) + + def test_allows_asterisks(self): """test allows rules.""" - config = {'1': '1', - '2': {'22': '22', - '33': {'333': '333', - '44': '444'}}, - '3': {'33': '44'}} + # keys in allows will be copied to dest. + # if '*' in allows, all keys will be copied to dest. allows = ['*', '3', '5'] configfilter = config_filter.ConfigFilter(allows) - filtered_config = configfilter.filter(config) - self.assertEqual(filtered_config, config) + filtered_config = configfilter.filter(self.config_) + self.assertEqual(filtered_config, self.config_) + + def test_allows_path(self): allows = ['/1', '2/22', '5'] expected_config = {'1': '1', '2': {'22': '22'}} configfilter = config_filter.ConfigFilter(allows) - filtered_config = configfilter.filter(config) + filtered_config = configfilter.filter(self.config_) self.assertEqual(filtered_config, expected_config) + + def test_allows_asterrisks_in_path(self): allows = ['*/33'] expected_config = {'2': {'33': {'333': '333', '44': '444'}}, '3': {'33': '44'}} configfilter = config_filter.ConfigFilter(allows) - filtered_config = configfilter.filter(config) + filtered_config = configfilter.filter(self.config_) self.assertEqual(filtered_config, expected_config) def test_denies(self): """test denies rules.""" - config = {'1': '1', '2': {'22': '22', - '33': {'333': '333', - '44': '444'}}, - '3': {'33': '44'}} + # keys in denies list will be removed from filtered config. denies = ['/1', '2/22', '2/33/333', '5'] expected_config = {'2': {'33': {'44': '444'}}, '3': {'33': '44'}} configfilter = config_filter.ConfigFilter(denies=denies) - filtered_config = configfilter.filter(config) + filtered_config = configfilter.filter(self.config_) self.assertEqual(filtered_config, expected_config) + + def test_denies_asterisks(self): denies = ['*'] configfilter = config_filter.ConfigFilter(denies=denies) - filtered_config = configfilter.filter(config) + filtered_config = configfilter.filter(self.config_) self.assertIsNone(filtered_config) + + def tet_deneis_asterisks_in_path(self): denies = ['*/33'] expected_config = {'1': '1', '2': {'22': '22'}} configfilter = config_filter.ConfigFilter(denies=denies) - filtered_config = configfilter.filter(config) + filtered_config = configfilter.filter(self.config_) self.assertEqual(filtered_config, expected_config) diff --git a/compass/tests/config_management/utils/test_config_merger.py b/compass/tests/config_management/utils/test_config_merger.py index fea54fb9..8dc1554a 100755 --- a/compass/tests/config_management/utils/test_config_merger.py +++ b/compass/tests/config_management/utils/test_config_merger.py @@ -18,7 +18,6 @@ .. moduleauthor:: Xiaodong Wang """ -import functools import os import unittest2 @@ -30,11 +29,433 @@ reload(setting) from compass.config_management.utils import config_merger -from compass.config_management.utils import config_merger_callbacks +from compass.config_management.utils import config_reference from compass.utils import flags from compass.utils import logsetting +class TestConfigMapping(unittest2.TestCase): + """test config mapping class.""" + + def setUp(self): + super(TestConfigMapping, self).setUp() + logsetting.init() + + def tearDown(self): + super(TestConfigMapping, self).tearDown() + + def test_init(self): + # path_list should be list of string. + config_merger.ConfigMapping( + path_list=['1/2/3', '/4/5/6']) + config_merger.ConfigMapping( + path_list=[u'1/2/3', u'/4/5/6']) + self.assertRaises( + TypeError, + config_merger.ConfigMapping, path_list={'1/2/3': '4'}) + self.assertRaises( + TypeError, config_merger.ConfigMapping, path_list='1234') + self.assertRaises( + TypeError, config_merger.ConfigMapping, + path_list=[{'1/2/3': '4'}]) + + def test_init_from_upper_keys(self): + # from_upper_keys should be dict of string to string. + config_merger.ConfigMapping( + path_list=['1/2/3', '/4/5/6'], from_upper_keys={'4': '4'}) + config_merger.ConfigMapping( + path_list=['1/2/3', '/4/5/6'], from_upper_keys={u'4': u'4'}) + self.assertRaises( + TypeError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + from_upper_keys=['4']) + self.assertRaises( + TypeError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + from_upper_keys='4') + self.assertRaises( + TypeError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + from_upper_keys={4: '4'}) + self.assertRaises( + TypeError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + from_upper_keys={'4': 4}) + + def test_init_from_lower_keys(self): + # from_lower_keys should be dict of string to string. + config_merger.ConfigMapping( + path_list=['1/2/3', '/4/5/6'], from_lower_keys={'4': '4'}) + config_merger.ConfigMapping( + path_list=['1/2/3', '/4/5/6'], from_lower_keys={u'4': u'4'}) + self.assertRaises( + TypeError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + from_lower_keys=['4']) + self.assertRaises( + TypeError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + from_lower_keys='4') + self.assertRaises( + TypeError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + from_lower_keys={4: '4'}) + self.assertRaises( + TypeError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + from_lower_keys={'4': 4}) + + def test_init_overlap_upper_keys_lower_keys(self): + # there should be overlap between from_upper_keys and from_lower_keys. + self.assertRaises( + KeyError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + from_upper_keys={'1': '1', '2': '2'}, + from_lower_keys={'1': '1', '3': '3'}) + config_merger.ConfigMapping( + path_list=['1/2/3', '/4/5/6'], + from_upper_keys={'1': '1', '2': '2'}, + from_lower_keys={'3': '3', '4': '4'}) + + def test_init_to_key(self): + # to_key type should be string. + config_merger.ConfigMapping( + path_list=['1/2/3', '/4/5/6'], + to_key='hello') + config_merger.ConfigMapping( + path_list=['1/2/3', '/4/5/6'], + to_key=u'hello') + self.assertRaises( + TypeError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + to_key=['hello']) + self.assertRaises( + TypeError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + to_key=123) + + def test_init_to_key_no_asterrisks(self): + # to_key should not contains '*'. + self.assertRaises( + KeyError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + to_key='abc*def') + + def test_init_override_conditions(self): + # override_conditions type should be dict of string to string. + config_merger.ConfigMapping( + path_list=['1/2/3', '/4/5/6'], + override_conditions={'hello': 'hi'}) + self.assertRaises( + TypeError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + override_conditions=['hello', 'hi']) + self.assertRaises( + TypeError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + override_conditions='hello') + self.assertRaises( + TypeError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + override_conditions={5: 'hi'}) + self.assertRaises( + TypeError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + override_conditions={'hello': 5}) + + def test_init_override_conditions_no_asterrisks(self): + # the value in each override_conditions key value pair + # should not contains '*'. + self.assertRaises( + KeyError, config_merger.ConfigMapping, + path_list=['1/2/3', '/4/5/6'], + override_conditions={'hello': 'hi*hi'}) + + def test_merge(self): + # the key in path_list will be copied + # from upper config to lower configs. + upper_config = { + 'key': 'abc', + 'key2': 'def' + } + upper_ref = config_reference.ConfigReference(upper_config) + lower_configs = { + 1: {}, 2: {}, 3: {} + } + lower_refs = {} + for lower_key, lower_config in lower_configs.items(): + lower_refs[lower_key] = config_reference.ConfigReference( + lower_config) + + merger = config_merger.ConfigMapping( + path_list=['key']) + merger.merge(upper_ref, lower_refs) + self.assertEqual( + lower_configs, + {1: {'key': 'abc'}, 2: {'key': 'abc'}, 3: {'key': 'abc'}}) + + def test_merge_all_matching_keys_copied(self): + # all the keys matching the pattern will be copied + # from upper config to lower configs. + upper_config = { + 'key': 'abc', + 'key2': 'def' + } + upper_ref = config_reference.ConfigReference(upper_config) + lower_configs = { + 1: {}, 2: {}, 3: {} + } + lower_refs = {} + for lower_key, lower_config in lower_configs.items(): + lower_refs[lower_key] = config_reference.ConfigReference( + lower_config) + + merger = config_merger.ConfigMapping( + path_list=['key*']) + merger.merge(upper_ref, lower_refs) + self.assertEqual( + lower_configs, + { + 1: {'key': 'abc', 'key2': 'def'}, + 2: {'key': 'abc', 'key2': 'def'}, + 3: {'key': 'abc', 'key2': 'def'} + } + ) + + def test_merge_value_set_explictly(self): + # key in lower configs are set to expected value. + upper_config = { + 'key': 'abc', + 'key2': 'def' + } + upper_ref = config_reference.ConfigReference(upper_config) + lower_configs = { + 1: {}, 2: {}, 3: {} + } + lower_refs = {} + for lower_key, lower_config in lower_configs.items(): + lower_refs[lower_key] = config_reference.ConfigReference( + lower_config) + + merger = config_merger.ConfigMapping( + path_list=['key'], value='def') + merger.merge(upper_ref, lower_refs) + self.assertEqual( + lower_configs, + { + 1: {'key': 'def'}, + 2: {'key': 'def'}, + 3: {'key': 'def'} + } + ) + + def test_merge_value_set_by_callback(self): + # key in lower config is called a callback to expected value . + upper_config = { + 'key': 'abc', + 'key2': 'def' + } + upper_ref = config_reference.ConfigReference(upper_config) + lower_configs = { + 1: {}, 2: {}, 3: {} + } + lower_refs = {} + for lower_key, lower_config in lower_configs.items(): + lower_refs[lower_key] = config_reference.ConfigReference( + lower_config) + + def _merge_value(sub_ref, ref_key, lower_sub_refs, to_key): + values = {} + for lower_key, lower_sub_ref in lower_sub_refs.items(): + values[lower_key] = '%s.%s' % (sub_ref.config, lower_key) + + return values + + merger = config_merger.ConfigMapping( + path_list=['key'], value=_merge_value) + merger.merge(upper_ref, lower_refs) + self.assertEqual( + lower_configs, + { + 1: {'key': 'abc.1'}, + 2: {'key': 'abc.2'}, + 3: {'key': 'abc.3'} + } + ) + + def test_merge_to_key(self): + # set lower configs expected key from the key in upper config. + upper_config = { + 'key': 'abc', + 'key2': 'def' + } + upper_ref = config_reference.ConfigReference(upper_config) + lower_configs = { + 1: {}, 2: {}, 3: {} + } + lower_refs = {} + for lower_key, lower_config in lower_configs.items(): + lower_refs[lower_key] = config_reference.ConfigReference( + lower_config) + + def _merge_value(sub_ref, ref_key, lower_sub_refs, to_key): + values = {} + for lower_key, lower_sub_ref in lower_sub_refs.items(): + values[lower_key] = '%s.%s' % (sub_ref.config, lower_key) + + return values + + merger = config_merger.ConfigMapping( + path_list=['key'], value=_merge_value, to_key='/key2') + merger.merge(upper_ref, lower_refs) + self.assertEqual( + lower_configs, + { + 1: {'key': None, 'key2': 'abc.1'}, + 2: {'key': None, 'key2': 'abc.2'}, + 3: {'key': None, 'key2': 'abc.3'} + } + ) + + def test_merge_from_upper_and_lower_configs(self): + # set lower configs from some keys of upper config and lower configs. + upper_config = { + 'key': 'abc', + 'key_prefix': 'A', + 'key_suffix': 'B' + } + upper_ref = config_reference.ConfigReference(upper_config) + lower_configs = { + 1: {'name': 'hello'}, 2: {'name': 'hi'}, 3: {'name': 'today'} + } + lower_refs = {} + for lower_key, lower_config in lower_configs.items(): + lower_refs[lower_key] = config_reference.ConfigReference( + lower_config) + + def _merge_value2( + sub_ref, ref_key, lower_sub_refs, to_key, + prefix='', suffix='', names={} + ): + values = {} + for lower_key, lower_sub_ref in lower_sub_refs.items(): + values[lower_key] = '%s%s%s' % ( + prefix, names.get(lower_key, ''), suffix) + + return values + + merger = config_merger.ConfigMapping( + path_list=['key'], value=_merge_value2, + from_upper_keys={'prefix': '/key_prefix', 'suffix': '/key_suffix'}, + from_lower_keys={'names': '/name'}) + merger.merge(upper_ref, lower_refs) + self.assertEqual( + lower_configs, + { + 1: {'name': 'hello', 'key': 'AhelloB'}, + 2: {'name': 'hi', 'key': 'AhiB'}, + 3: {'name': 'today', 'key': 'AtodayB'} + } + ) + + def test_merge_multikey_to_same_tokey_nooverride(self): + # mutli key in upper config writes the same dest key, the later one + # will be ignored if override is False. + upper_config = { + 'key1': 'abc', + 'key2': 'bcd' + } + upper_ref = config_reference.ConfigReference(upper_config) + lower_configs = { + 1: {}, 2: {}, 3: {} + } + lower_refs = {} + for lower_key, lower_config in lower_configs.items(): + lower_refs[lower_key] = config_reference.ConfigReference( + lower_config) + merger = config_merger.ConfigMapping( + path_list=['key1', 'key2'], to_key='/key', override=False) + merger.merge(upper_ref, lower_refs) + self.assertEqual( + lower_configs, + { + 1: {'key': 'abc', 'key1': None, 'key2': None}, + 2: {'key': 'abc', 'key1': None, 'key2': None}, + 3: {'key': 'abc', 'key1': None, 'key2': None} + } + ) + + def test_merge_multikey_to_sam_tokey_override(self): + # if multi key in upper config writes the same dest key, + # the later one will override the former one if override is True. + upper_config = { + 'key1': 'abc', + 'key2': 'bcd' + } + upper_ref = config_reference.ConfigReference(upper_config) + lower_configs = { + 1: {}, 2: {}, 3: {} + } + lower_refs = {} + for lower_key, lower_config in lower_configs.items(): + lower_refs[lower_key] = config_reference.ConfigReference( + lower_config) + merger = config_merger.ConfigMapping( + path_list=['key1', 'key2'], to_key='/key', override=True) + merger.merge(upper_ref, lower_refs) + self.assertEqual( + lower_configs, + { + 1: {'key': 'bcd', 'key1': None, 'key2': None}, + 2: {'key': 'bcd', 'key1': None, 'key2': None}, + 3: {'key': 'bcd', 'key1': None, 'key2': None} + } + ) + + def test_merge_override_callback(self): + # override param can be set from callback. + upper_config = { + 'key1': 'abc', + 'key2': 'bcd', + 'key3': 'def', + 'key_prefix': 'b', + 'key_suffix': 'd' + } + upper_ref = config_reference.ConfigReference(upper_config) + lower_configs = { + 1: {}, 2: {}, 3: {} + } + lower_refs = {} + for lower_key, lower_config in lower_configs.items(): + lower_refs[lower_key] = config_reference.ConfigReference( + lower_config) + + def _generate_override( + sub_ref, ref_key, lower_ref, to_key, prefix='', suffix='' + ): + return ( + sub_ref.config.startswith(prefix) and + sub_ref.config.endswith(suffix) + ) + + merger = config_merger.ConfigMapping( + path_list=['key1', 'key2', 'key3'], to_key='/key', + override=_generate_override, + override_conditions={ + 'prefix': '/key_prefix', 'suffix': '/key_suffix' + } + ) + merger.merge(upper_ref, lower_refs) + self.assertEqual( + lower_configs, + { + 1: {'key': 'bcd', 'key1': None, 'key2': None, 'key3': None}, + 2: {'key': 'bcd', 'key1': None, 'key2': None, 'key3': None}, + 3: {'key': 'bcd', 'key1': None, 'key2': None, 'key3': None} + } + ) + + class TestConfigMerger(unittest2.TestCase): """test config merger class.""" @@ -45,152 +466,110 @@ class TestConfigMerger(unittest2.TestCase): def tearDown(self): super(TestConfigMerger, self).tearDown() + def test_init(self): + # mappings should be list of ConfigMapping. + config_merger.ConfigMerger(mappings=[]) + config_merger.ConfigMerger( + mappings=[ + config_merger.ConfigMapping( + path_list=['1/2/3', '/4/5/6']) + ] + ) + self.assertRaises( + TypeError, config_merger.ConfigMerger, + mapping={'hello': config_merger.ConfigMapping(path_list=[])}) + self.assertRaises( + TypeError, config_merger.ConfigMerger, + mapping=config_merger.ConfigMapping(path_list=[])) + self.assertRaises( + TypeError, config_merger.ConfigMerger, + mapping='config_merger.ConfigMapping(path_list=[])') + self.assertRaises( + TypeError, config_merger.ConfigMerger, + mapping=[{'hello': config_merger.ConfigMapping(path_list=[])}]) + self.assertRaises( + TypeError, config_merger.ConfigMerger, + mapping=['config_merger.ConfigMapping(path_list=[])']) + def test_merge(self): - """test merge.""" - upper_config = { - 'networking': { - 'interfaces': { - 'management': { - 'ip_start': '192.168.1.1', - 'ip_end': '192.168.1.100', - 'netmask': '255.255.255.0', - 'dns_pattern': ( - '%(hostname)s.%(clustername)s.%(search_path)s'), - }, - 'floating': { - 'ip_start': '172.16.0.1', - 'ip_end': '172.16.0.100', - 'netmask': '0.0.0.0', - 'dns_pattern': ( - 'public-%(hostname)s.%(clustername)s' - '.%(search_path)s'), - }, - }, - 'global': { - 'search_path': 'ods.com', - }, - }, - 'clustername': 'cluster1', - 'dashboard_roles': ['os-single-controller'], - 'role_assign_policy': { - 'policy_by_host_numbers': {}, - 'default': { - 'roles': ['os-single-controller', 'os-network', - 'os-compute-worker'], - 'default_min': 1, - }, - }, + # if multi ConfigMapping updates the same key and no override is set + # for the later one, the later one will be ignored. + config = { + 'key1': 'abc', + 'key2': 'bcd' } lower_configs = { - 1: { - 'hostname': 'host1', - }, - 2: { - 'hostname': 'host2', - 'networking': { - 'interfaces': { - 'management': { - 'ip': '192.168.1.50', - }, - }, - }, - 'roles': ['os-single-controller', 'os-network'], - } + 1: {}, 2: {}, 3: {} } - expected_lower_configs = { - 1: { - 'networking': { - 'interfaces': { - 'floating': { - 'ip': '172.16.0.1', - 'netmask': '0.0.0.0', - 'dns_alias': 'public-host1.cluster1.ods.com' - }, - 'management': { - 'ip': '192.168.1.1', - 'netmask': '255.255.255.0', - 'dns_alias': 'host1.cluster1.ods.com' - } - }, - 'global': { - 'search_path': 'ods.com', - } - }, - 'hostname': 'host1', - 'has_dashboard_roles': False, - 'roles': ['os-compute-worker'] - }, - 2: { - 'networking': { - 'interfaces': { - 'floating': { - 'ip': '172.16.0.2', - 'netmask': '0.0.0.0', - 'dns_alias': 'public-host2.cluster1.ods.com' - }, - 'management': { - 'ip': '192.168.1.50', - 'netmask': '255.255.255.0', - 'dns_alias': 'host2.cluster1.ods.com' - } - }, - 'global': { - 'search_path': 'ods.com', - } - }, - 'hostname': 'host2', - 'has_dashboard_roles': True, - 'roles': ['os-single-controller', 'os-network'] - } + merger = config_merger.ConfigMerger( + mappings=[ + config_merger.ConfigMapping( + path_list=['key1'], to_key='/mkey'), + config_merger.ConfigMapping( + path_list=['key2'], to_key='/mkey') + ] + ) + merger.merge(config, lower_configs) + self.assertEqual( + lower_configs, + {1: {'mkey': 'abc'}, 2: {'mkey': 'abc'}, 3: {'mkey': 'abc'}} + ) + + def test_merge_multi_mapping_to_same_tokey(self): + # if multi ConfigMapping updates the same key and override is set + # for the later one, the later one will override the former one. + config = { + 'key1': 'abc', + 'key2': 'bcd' } - mappings = [ - config_merger.ConfigMapping( - path_list=['/networking/interfaces/*'], - from_upper_keys={'ip_start': 'ip_start', 'ip_end': 'ip_end'}, - to_key='ip', - value=config_merger_callbacks.assign_ips - ), - config_merger.ConfigMapping( - path_list=['/role_assign_policy'], - from_upper_keys={ - 'policy_by_host_numbers': 'policy_by_host_numbers', - 'default': 'default'}, - to_key='/roles', - value=config_merger_callbacks.assign_roles_by_host_numbers - ), - config_merger.ConfigMapping( - path_list=['/dashboard_roles'], - from_lower_keys={'lower_values': '/roles'}, - to_key='/has_dashboard_roles', - value=config_merger_callbacks.has_intersection - ), - config_merger.ConfigMapping( - path_list=[ - '/networking/global', - '/networking/interfaces/*/netmask', - '/networking/interfaces/*/nic', - '/networking/interfaces/*/promisc', - '/security/*', - '/partition', - ] - ), - config_merger.ConfigMapping( - path_list=['/networking/interfaces/*'], - from_upper_keys={'pattern': 'dns_pattern', - 'clustername': '/clustername', - 'search_path': ( - '/networking/global/search_path')}, - from_lower_keys={'hostname': '/hostname'}, - to_key='dns_alias', - value=functools.partial( - config_merger_callbacks.assign_from_pattern, - upper_keys=['search_path', 'clustername'], - lower_keys=['hostname']) - ), - ] - merger = config_merger.ConfigMerger(mappings) - merger.merge(upper_config, lower_configs) - self.assertEqual(lower_configs, expected_lower_configs) + lower_configs = { + 1: {}, 2: {}, 3: {} + } + merger = config_merger.ConfigMerger( + mappings=[ + config_merger.ConfigMapping( + path_list=['key1'], to_key='/mkey'), + config_merger.ConfigMapping( + path_list=['key2'], to_key='/mkey', + override=True) + ] + ) + merger.merge(config, lower_configs) + self.assertEqual( + lower_configs, + {1: {'mkey': 'bcd'}, 2: {'mkey': 'bcd'}, 3: {'mkey': 'bcd'}} + ) + + def test_merge_override_callback(self): + # override param can be callback. + config = { + 'key1': 'abc', + 'key2': 'bcd' + } + lower_configs = { + 1: {}, 2: {}, 3: {} + } + + def _merge_value(sub_ref, ref_key, lower_sub_refs, to_key): + values = {} + for lower_key, lower_sub_ref in lower_sub_refs.items(): + values[lower_key] = None + + return values + + merger = config_merger.ConfigMerger( + mappings=[ + config_merger.ConfigMapping( + path_list=['key1'], + value=_merge_value, + to_key='/mkey') + ] + ) + merger.merge(config, lower_configs) + self.assertEqual( + lower_configs, + {1: None, 2: None, 3: None} + ) if __name__ == '__main__': diff --git a/compass/tests/config_management/utils/test_config_merger_callbacks.py b/compass/tests/config_management/utils/test_config_merger_callbacks.py index fb433a6e..ece559fb 100755 --- a/compass/tests/config_management/utils/test_config_merger_callbacks.py +++ b/compass/tests/config_management/utils/test_config_merger_callbacks.py @@ -41,10 +41,151 @@ class TestAssignRoles(unittest2.TestCase): def setUp(self): super(TestAssignRoles, self).setUp() logsetting.init() + self.roles_ = ['control', 'api', 'compute'] + self.maxs_ = {'control': 1, 'api': 1, 'compute': -1} + self.default_min_ = 1 def tearDown(self): super(TestAssignRoles, self).tearDown() + def test_assign_roles_allinone_roles_empty(self): + """test assign roles all in one node.""" + lower_configs = { + 1: {'roles': []}, + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + assigned = config_merger_callbacks.assign_roles( + None, None, lower_refs, 'roles', roles=self.roles_, + maxs=self.maxs_, + default_min=self.default_min_) + self.assertEqual(assigned, {1: ['control', 'api', 'compute']}) + + def test_assign_roles_allinone_no_roles(self): + lower_configs = { + 1: {}, + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + assigned = config_merger_callbacks.assign_roles( + None, None, lower_refs, 'roles', roles=self.roles_, + maxs=self.maxs_, default_min=self.default_min_) + self.assertEqual(assigned, {1: ['control', 'api', 'compute']}) + + def test_assign_roles_allinone_roles_sorted(self): + lower_configs = { + 1: {'roles': ['api', 'control', 'compute']}, + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + assigned = config_merger_callbacks.assign_roles( + None, None, lower_refs, 'roles', roles=self.roles_, + maxs=self.maxs_, default_min=self.default_min_) + self.assertEqual(assigned, {1: ['control', 'api', 'compute']}) + + def test_assign_roles_allinone_roles_set_additional_roles(self): + lower_configs = { + 1: {'roles': ['control', 'api', 'compute', 'mysql']}, + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + assigned = config_merger_callbacks.assign_roles( + None, None, lower_refs, 'roles', roles=self.roles_, + maxs=self.maxs_, default_min=self.default_min_) + self.assertEqual(assigned, {1: ['control', 'api', 'compute']}) + + def test_assign_roles_allinone_roles_set_less_roles(self): + lower_configs = { + 1: {'roles': ['control', 'api']}, + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + self.assertRaises( + ValueError, config_merger_callbacks.assign_roles, + None, None, lower_refs, 'roles', roles=self.roles_, + maxs=self.maxs_, default_min=self.default_min_) + + def test_assign_roles_allinone_exclusives(self): + exclusives = ['control'] + lower_configs = { + 1: {'roles': []}, + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + self.assertRaises( + ValueError, config_merger_callbacks.assign_roles, + None, None, lower_refs, 'roles', roles=self.roles_, + maxs=self.maxs_, default_min=self.default_min_, + exclusives=exclusives) + + def test_assign_roles_allinone_bundles(self): + lower_configs = { + 1: {'roles': []}, + } + exclusives = ['control'] + bundles = [['control', 'api', 'compute']] + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + + assigned = config_merger_callbacks.assign_roles( + None, None, lower_refs, 'roles', roles=self.roles_, + maxs=self.maxs_, default_min=self.default_min_, + exclusives=exclusives, bundles=bundles) + self.assertEqual(assigned, {1: ['control', 'api', 'compute']}) + + def test_assign_roles_allinone_bundles_noenough_hosts(self): + exclusives = ['control'] + bundles = [['control', 'api']] + lower_configs = { + 1: {'roles': []}, + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + self.assertRaises( + ValueError, config_merger_callbacks.assign_roles, + None, None, lower_refs, 'roles', roles=self.roles_, + maxs=self.maxs_, default_min=self.default_min_, + exclusives=exclusives, bundles=bundles) + + def test_assign_roles_allinone_maxes_mins_noenough_hosts(self): + lower_configs = { + 1: {'roles': []}, + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + maxs = {'control': 1, 'api': 2, 'compute': -1} + mins = {'control': 1, 'api': 2} + default_min = 0 + self.assertRaises( + ValueError, config_merger_callbacks.assign_roles, + None, None, lower_refs, 'roles', roles=self.roles_, + maxs=maxs, mins=mins, + default_min=default_min) + + def test_assign_roles_allinone_maxes_mins(self): + lower_configs = { + 1: {'roles': []}, + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + maxs = {'control': 1, 'api': 2, 'compute': -1} + mins = {'control': 1, 'api': 0} + default_min = 0 + assigned = config_merger_callbacks.assign_roles( + None, None, lower_refs, 'roles', roles=self.roles_, + maxs=maxs, mins=mins, default_min=default_min) + self.assertEqual(assigned, {1: ['control']}) + def test_assign_roles(self): """test assign roles.""" lower_configs = { @@ -57,23 +198,22 @@ class TestAssignRoles(unittest2.TestCase): lower_refs = {} for hostid, config in lower_configs.items(): lower_refs[hostid] = config_reference.ConfigReference(config) - roles = ['control', 'api', 'compute'] - maxs = {'control': 1, 'api': 2, 'compute': -1} - default_min = 1 exclusives = ['control'] assigned = config_merger_callbacks.assign_roles( - None, None, lower_refs, 'roles', roles=roles, - maxs=maxs, - default_min=default_min, + None, None, lower_refs, 'roles', roles=self.roles_, + maxs=self.maxs_, + default_min=self.default_min_, exclusives=exclusives) self.assertEqual(assigned, {1: ['control'], 2: ['api', 'compute'], - 3: ['api'], + 3: ['compute'], 4: ['compute'], 5: ['compute']}) + def test_assign_roles_multihosts_one_role(self): default_min = 2 maxs = {'control': 1, 'api': 2, 'compute': 2} + exclusives = ['control'] lower_configs = { 1: {'roles': ['control']}, 2: {'roles': ['api', 'compute']}, @@ -85,7 +225,7 @@ class TestAssignRoles(unittest2.TestCase): for hostid, config in lower_configs.items(): lower_refs[hostid] = config_reference.ConfigReference(config) assigned = config_merger_callbacks.assign_roles( - None, None, lower_refs, 'roles', roles=roles, + None, None, lower_refs, 'roles', roles=self.roles_, maxs=maxs, default_min=default_min, exclusives=exclusives) self.assertEqual(assigned, {1: ['control'], @@ -94,9 +234,10 @@ class TestAssignRoles(unittest2.TestCase): 4: ['api'], 5: ['compute']}) - default_min = 1 + def test_assign_roles_bundles(self): roles = ['control', 'api', 'compute', 'mysql'] maxs = {'control': 1, 'api': 2, 'compute': -1, 'mysql': 2} + exclusives = ['control'] bundles = [['control', 'api']] lower_configs = { 1: {}, @@ -110,7 +251,7 @@ class TestAssignRoles(unittest2.TestCase): lower_refs[hostid] = config_reference.ConfigReference(config) assigned = config_merger_callbacks.assign_roles( None, None, lower_refs, 'roles', roles=roles, - maxs=maxs, default_min=default_min, + maxs=maxs, default_min=self.default_min_, exclusives=exclusives, bundles=bundles) self.assertEqual(assigned, {1: ['control', 'api'], 2: ['compute'], @@ -118,6 +259,507 @@ class TestAssignRoles(unittest2.TestCase): 4: ['mysql'], 5: ['compute']}) + def test_assign_roles_multi_default_roles(self): + roles = ['control', 'api', 'compute', 'mysql'] + maxs = {'control': 1, 'api': 2, 'compute': -1, 'mysql': -2} + exclusives = ['control'] + bundles = [['control', 'api']] + lower_configs = { + 1: {}, + 2: {}, + 3: {}, + 4: {}, + 5: {}, + 6: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + assigned = config_merger_callbacks.assign_roles( + None, None, lower_refs, 'roles', roles=roles, + maxs=maxs, default_min=self.default_min_, + exclusives=exclusives, bundles=bundles) + self.assertEqual(assigned, {1: ['control', 'api'], + 2: ['compute'], + 3: ['mysql'], + 4: ['mysql'], + 5: ['compute'], + 6: ['mysql']}) + + def test_assign_roles_hosts_portion_by_default_roles(self): + roles = ['control', 'api', 'compute', 'mysql'] + maxs = {'control': 1, 'api': 2, 'compute': -1, 'mysql': -1} + exclusives = ['control'] + bundles = [['control', 'api']] + lower_configs = { + 1: {}, + 2: {}, + 3: {}, + 4: {}, + 5: {}, + 6: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + assigned = config_merger_callbacks.assign_roles( + None, None, lower_refs, 'roles', roles=roles, + maxs=maxs, default_min=self.default_min_, + exclusives=exclusives, bundles=bundles) + self.assertEqual(assigned, {1: ['control', 'api'], + 2: ['compute'], + 3: ['mysql'], + 4: ['compute'], + 5: ['mysql'], + 6: ['compute']}) + + def test_assign_roles_by_host_number_one_host(self): + lower_configs = { + 1: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + default = { + 'roles': ['control', 'api', 'compute'], + 'maxs': {'control': 1, 'api': 1, 'compute': -1}, + 'default_min': 1, + 'exclusives': ['control'] + } + policy_by_host_numbers = { + '1': { + 'bundles': [['control', 'api', 'compute']] + }, + '2': { + 'bundles': [['control', 'api']] + }, + } + assigned = config_merger_callbacks.assign_roles_by_host_numbers( + None, None, lower_refs, 'roles', + policy_by_host_numbers=policy_by_host_numbers, + default=default) + self.assertEqual(assigned, {1: ['control', 'api', 'compute']}) + + def test_assign_roles_by_host_number_two_hosts(self): + lower_configs = { + 1: {}, + 2: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + default = { + 'roles': ['control', 'api', 'compute'], + 'maxs': {'control': 1, 'api': 1, 'compute': -1}, + 'default_min': 1, + 'exclusives': ['control'] + } + policy_by_host_numbers = { + '1': { + 'bundles': [['control', 'api', 'compute']] + }, + '2': { + 'bundles': [['control', 'api']] + }, + } + assigned = config_merger_callbacks.assign_roles_by_host_numbers( + None, None, lower_refs, 'roles', + policy_by_host_numbers=policy_by_host_numbers, + default=default) + self.assertEqual(assigned, {1: ['control', 'api'], 2: ['compute']}) + + def test_assign_roles_by_host_number_host_number_not_found(self): + lower_configs = { + 1: {}, + 2: {}, + 3: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + default = { + 'roles': ['control', 'api', 'compute'], + 'maxs': {'control': 1, 'api': 1, 'compute': -1}, + 'default_min': 1, + 'exclusives': ['control'] + } + policy_by_host_numbers = { + '1': { + 'bundles': [['control', 'api', 'compute']] + }, + '2': { + 'bundles': [['control', 'api']] + }, + } + assigned = config_merger_callbacks.assign_roles_by_host_numbers( + None, None, lower_refs, 'roles', + policy_by_host_numbers=policy_by_host_numbers, + default=default) + self.assertEqual( + assigned, {1: ['control'], 2: ['api'], 3: ['compute']}) + + def test_assign_roles_by_host_number_host_number_host_number_int(self): + default = { + 'roles': ['control', 'api', 'compute'], + 'maxs': {'control': 1, 'api': 1, 'compute': -1}, + 'default_min': 1, + 'exclusives': ['control'] + } + policy_by_host_numbers = { + 1: { + 'bundles': [['control', 'api', 'compute']] + }, + 2: { + 'bundles': [['control', 'api']] + }, + } + lower_configs = { + 1: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + self.assertRaises( + ValueError, config_merger_callbacks.assign_roles_by_host_numbers, + None, None, lower_refs, 'roles', + policy_by_host_numbers=policy_by_host_numbers, + default=default) + + +class TestAssignIPs(unittest2.TestCase): + """test assign ips.""" + + def setUp(self): + super(TestAssignIPs, self).setUp() + logsetting.init() + + def tearDown(self): + super(TestAssignIPs, self).tearDown() + + def test_assign_ips_validate(self): + lower_configs = { + 1: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + # ip_start and ip_end param should be the correct format. + self.assertRaises( + ValueError, config_merger_callbacks.assign_ips, + None, None, lower_refs, 'ip', + ip_start='') + self.assertRaises( + ValueError, config_merger_callbacks.assign_ips, + None, None, lower_refs, 'ip', + ip_start='100') + self.assertRaises( + ValueError, config_merger_callbacks.assign_ips, + None, None, lower_refs, 'ip', + ip_end='') + self.assertRaises( + ValueError, config_merger_callbacks.assign_ips, + None, None, lower_refs, 'ip', + ip_end='100') + + def test_assign_ip_ip_start_ip_end_relation(self): + lower_configs = { + 1: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + self.assertRaises( + ValueError, config_merger_callbacks.assign_ips, + None, None, lower_refs, 'ip', + ip_start='192.168.100.100', ip_end='192.168.100.99') + assigned = config_merger_callbacks.assign_ips( + None, None, lower_refs, 'ip', + ip_start='192.168.100.100', ip_end='192.168.100.100') + self.assertEqual(assigned, {1: '192.168.100.100'}) + + def test_assign_ips_multi_hosts_noenough_ips(self): + lower_configs = { + 1: {}, 2: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + self.assertRaises( + ValueError, config_merger_callbacks.assign_ips, + None, None, lower_refs, 'ip', + ip_start='192.168.100.100', ip_end='192.168.100.100') + + def test_assign_ips_multi_hosts(self): + lower_configs = { + 1: {}, 2: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + assigned = config_merger_callbacks.assign_ips( + None, None, lower_refs, 'ip', + ip_start='192.168.100.100', ip_end='192.168.100.101') + self.assertEqual( + assigned, {1: '192.168.100.100', 2: '192.168.100.101'}) + + +class TestAssignFromPattern(unittest2.TestCase): + """test assign value from pattern.""" + + def setUp(self): + super(TestAssignFromPattern, self).setUp() + logsetting.init() + + def tearDown(self): + super(TestAssignFromPattern, self).tearDown() + + def test_pattern(self): + lower_configs = { + 1: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + assigned = config_merger_callbacks.assign_from_pattern( + None, None, lower_refs, 'pattern', pattern='hello') + self.assertEqual(assigned, {1: 'hello'}) + + def test_pattern_upper_keys(self): + lower_configs = { + 1: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + assigned = config_merger_callbacks.assign_from_pattern( + None, None, lower_refs, 'pattern', + upper_keys=['clustername'], pattern='%(clustername)s', + clustername='mycluster') + self.assertEqual(assigned, {1: 'mycluster'}) + + def test_pattern_lower_keys(self): + lower_configs = { + 1: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + assigned = config_merger_callbacks.assign_from_pattern( + None, None, lower_refs, 'pattern', + lower_keys=['hostname'], pattern='%(hostname)s', + hostname={1: 'myhost'}) + self.assertEqual(assigned, {1: 'myhost'}) + + def test_pattern_upper_keys_lower_keys(self): + lower_configs = { + 1: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + assigned = config_merger_callbacks.assign_from_pattern( + None, None, lower_refs, 'pattern', + upper_keys=['clustername'], lower_keys=['hostname'], + pattern='%(hostname)s.%(clustername)s', + hostname={1: 'myhost'}, clustername='mycluster') + self.assertEqual(assigned, {1: 'myhost.mycluster'}) + + def test_pattern_upper_keys_lower_keys_overlap(self): + lower_configs = { + 1: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + self.assertRaises( + KeyError, config_merger_callbacks.assign_from_pattern, + None, None, lower_refs, 'pattern', + upper_keys=['clustername'], + lower_keys=['clustername', 'hostname'], + pattern='%(hostname)s.%(clustername)s', + hostname={1: 'myhost'}, clustername='mycluster') + + def test_pattern_extra_keys(self): + lower_configs = { + 1: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + self.assertRaises( + KeyError, config_merger_callbacks.assign_from_pattern, + None, None, lower_refs, 'pattern', + upper_keys=['clustername', 'clusterid'], + lower_keys=['hostname', 'hostid'], + pattern='%(hostname)s.%(clustername)s', + hostname={1: 'myhost'}, clustername='mycluster') + + def test_pattern_lower_key_not_dict(self): + lower_configs = { + 1: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + self.assertRaises( + KeyError, config_merger_callbacks.assign_from_pattern, + None, None, lower_refs, 'pattern', + upper_keys=['clustername'], + lower_keys=['hostname'], + pattern='%(hostname)s.%(clustername)s', + hostname='myhost', clustername='mycluster') + + def test_pattern_extra_kwargs(self): + lower_configs = { + 1: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + assigned = config_merger_callbacks.assign_from_pattern( + None, None, lower_refs, 'pattern', + upper_keys=['clustername'], + lower_keys=['hostname'], + pattern='%(hostname)s.%(clustername)s', + hostname={1: 'myhost'}, clustername='mycluster', + hostid={1: 'myhost'}, clusterid=1) + self.assertEqual(assigned, {1: 'myhost.mycluster'}) + + def test_pattern_extra_key_in_pattern(self): + lower_configs = { + 1: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + self.assertRaises( + KeyError, config_merger_callbacks.assign_from_pattern, + None, None, lower_refs, 'pattern', + upper_keys=['clustername'], + lower_keys=['hostname'], + pattern='%(hostid)s.%(clusterid)s', + hostname={1: 'myhost'}, clustername='mycluster') + + +class TestNoProxy(unittest2.TestCase): + """test assign noproxy.""" + + def setUp(self): + super(TestNoProxy, self).setUp() + logsetting.init() + + def tearDown(self): + super(TestNoProxy, self).tearDown() + + def test_noproxy(self): + lower_configs = { + 1: {}, + 2: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + assigned = config_merger_callbacks.assign_noproxy( + None, None, lower_refs, 'noproxy', + default=['127.0.0.1', 'compass', '10.145.88.3'], + clusterid=1, noproxy_pattern='%(hostname)s.%(clusterid)s,%(ip)s', + hostnames={1: 'host1', 2: 'host2'}, + ips={1: '10.145.88.1', 2: '10.145.88.2'}) + self.assertEqual( + assigned, { + 1: ( + '127.0.0.1,compass,10.145.88.3,' + 'host1.1,10.145.88.1,host2.1,10.145.88.2' + ), + 2: ( + '127.0.0.1,compass,10.145.88.3,' + 'host1.1,10.145.88.1,host2.1,10.145.88.2' + ) + }) + + def test_noproxy_noclusterid(self): + lower_configs = { + 1: {}, + 2: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + self.assertRaises( + KeyError, config_merger_callbacks.assign_noproxy, + None, None, lower_refs, 'noproxy', + default=['127.0.0.1', 'compass', '10.145.88.3'], + noproxy_pattern='%(hostname)s.%(clusterid)s,%(ip)s', + hostnames={1: 'host1', 2: 'host2'}, + ips={1: '10.145.88.1', 2: '10.145.88.2'}) + + def test_noproxy_nohostname_ips(self): + lower_configs = { + 1: {}, + 2: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + self.assertRaises( + KeyError, config_merger_callbacks.assign_noproxy, + None, None, lower_refs, 'noproxy', + default=['127.0.0.1', 'compass', '10.145.88.3'], + noproxy_pattern='%(hostname)s.%(clusterid)s,%(ip)s', + clusterid=1, hostnames={1: 'host1'}, + ips={1: '10.145.88.1'}) + + def test_noproxy_extra_keys_in_pattern(self): + lower_configs = { + 1: {}, + 2: {} + } + lower_refs = {} + for hostid, config in lower_configs.items(): + lower_refs[hostid] = config_reference.ConfigReference(config) + self.assertRaises( + KeyError, config_merger_callbacks.assign_noproxy, + None, None, lower_refs, 'noproxy', + default=['127.0.0.1', 'compass', '10.145.88.3'], + noproxy_pattern='%(hostname)s.%(clustername)s,%(ip)s', + clusterid=1, hostnames={1: 'host1', 2: 'host2'}, + ips={1: '10.145.88.1', 2: '10.145.88.2'}) + + +class TestOverrideIfEmpty(unittest2.TestCase): + """test override if empty.""" + + def setUp(self): + super(TestOverrideIfEmpty, self).setUp() + logsetting.init() + + def tearDown(self): + super(TestOverrideIfEmpty, self).tearDown() + + def test_lower_config_none(self): + lower_config = None + lower_ref = config_reference.ConfigReference(lower_config) + override = config_merger_callbacks.override_if_empty( + None, None, lower_ref, 'override') + self.assertTrue(override) + + def test_lower_config_empty(self): + lower_config = '' + lower_ref = config_reference.ConfigReference(lower_config) + override = config_merger_callbacks.override_if_empty( + None, None, lower_ref, 'override') + self.assertTrue(override) + lower_config = [] + lower_ref = config_reference.ConfigReference(lower_config) + override = config_merger_callbacks.override_if_empty( + None, None, lower_ref, 'override') + self.assertTrue(override) + lower_config = {} + lower_ref = config_reference.ConfigReference(lower_config) + override = config_merger_callbacks.override_if_empty( + None, None, lower_ref, 'override') + self.assertTrue(override) + if __name__ == '__main__': flags.init() diff --git a/compass/tests/config_management/utils/test_config_reference.py b/compass/tests/config_management/utils/test_config_reference.py index aa979622..6b4f1826 100755 --- a/compass/tests/config_management/utils/test_config_reference.py +++ b/compass/tests/config_management/utils/test_config_reference.py @@ -19,148 +19,335 @@ .. moduleauthor:: Xiaodong Wang """ import copy +import os import unittest2 + +os.environ['COMPASS_IGNORE_SETTING'] = 'true' + + +from compass.utils import setting_wrapper as setting +reload(setting) + + from compass.config_management.utils import config_reference +from compass.utils import flags +from compass.utils import logsetting from compass.utils import util +class TestCleanConfig(unittest2.TestCase): + """test clean_config function.""" + + def setUp(self): + super(TestCleanConfig, self).setUp() + logsetting.init() + + def tearDown(self): + super(TestCleanConfig, self).tearDown() + + def test_config_empty(self): + """test config is empty.""" + self.assertIsNone(config_reference.get_clean_config(None)) + self.assertIsNone(config_reference.get_clean_config({})) + self.assertEqual([], config_reference.get_clean_config([])) + self.assertEqual('', config_reference.get_clean_config('')) + + def test_recursive_empty_dict(self): + """test config is recursively empty.""" + self.assertIsNone(config_reference.get_clean_config({'test': {}})) + self.assertIsNone(config_reference.get_clean_config({'test': None})) + + def test_nromal_dict(self): + """test config is normal dict.""" + config_list = [ + {'abc': 'abc'}, + {'abc': [1, 2, 3]}, + {'abc': {'1': '123'}}, + [1, 2, 3], + 'abc', + ] + for config in config_list: + self.assertEqual(config, config_reference.get_clean_config(config)) + + def test_partial_clean(self): + """test config is partial cleaned.""" + config_and_cleans = [ + ({'abc': 1, 'bcd': None}, {'abc': 1}), + ({'abc': 1, 'bcd': {}}, {'abc': 1}), + ({'abc': 1, 'bcd': {'m': {}}}, {'abc': 1}), + ({'abc': 1, 'b': {'m': {}, 'n': 2}}, {'abc': 1, 'b': {'n': 2}}), + ] + for config, expected_config in config_and_cleans: + self.assertEqual( + expected_config, + config_reference.get_clean_config(config)) + + class TestConfigReference(unittest2.TestCase): """test config reference class.""" + def setUp(self): + super(TestConfigReference, self).setUp() + logsetting.init() + self.config_ = {'1': {'2': 3, '10': {}}, '4': [5, 6, 7], '8': 8} + self.ref_ = config_reference.ConfigReference(self.config_) + + def tearDown(self): + super(TestConfigReference, self).tearDown() + def test_init(self): """test init function.""" - config = {'1': {'2': 3, '10': {}}, '4': [5, 6, 7], '8': 8} - ref = config_reference.ConfigReference(config) - config2 = {'5': {'6': 6}} - ref2 = config_reference.ConfigReference(config2['5'], ref, '5') - expected_config = copy.deepcopy(config) - util.merge_dict(expected_config, config2) - self.assertEqual(ref.config, expected_config) - self.assertEqual(id(ref.config['5']), id(ref2.config)) - config3 = {'5': {'7': 7}} - ref3 = config_reference.ConfigReference(config3['5'], ref, '5') - self.assertEqual(id(ref.config['5']), id(ref3.config)) + # create ConfigReference instance. + self.assertEqual(self.ref_.config, self.config_) + self.assertEqual(id(self.ref_.config), id(self.config_)) - def test_ref(self): - """test ref function.""" - config = {'1': {'2': 3, '10': {}}, '4': [5, 6, 7], '8': 8} - ref = config_reference.ConfigReference(config) - self.assertRaises(KeyError, ref.ref, '') - self.assertRaises(KeyError, ref.ref, '/1/2/4') - self.assertEqual(ref.ref('.').config, config) - self.assertEqual(ref.ref('..').config, config) - self.assertEqual(ref.ref('/').config, config) - self.assertEqual(ref.ref('1').config, config['1']) - self.assertEqual(ref.ref('1/2').config, config['1']['2']) - self.assertEqual(ref.ref('1/2/.').config, config['1']['2']) - self.assertEqual(ref.ref('1/2/..').config, config['1']) - self.assertEqual(ref.ref('1/2//').config, config['1']['2']) - self.assertEqual(ref.ref('/1').config, config['1']) - self.assertEqual(ref.ref('/1/2').config, config['1']['2']) - subref = ref.ref('1') - self.assertEqual(subref.ref('2').config, config['1']['2']) - self.assertEqual(subref.ref('2/..').config, config['1']) - self.assertEqual(subref.ref('..').config, config) - self.assertEqual(subref.ref('../..').config, config) - self.assertEqual(subref.ref('/').config, config) - self.assertEqual(subref.ref('/4').config, config['4']) + def test_init_with_parent(self): + # create ConfigReference instance with parent param. + # it will add a key value pair in parent config if not exists. + config2 = {'5': {'6': 6}} + ref2 = config_reference.ConfigReference(config2['5'], self.ref_, '5') + expected_config = copy.deepcopy(self.config_) + util.merge_dict(expected_config, config2) + self.assertEqual(self.config_, expected_config) + self.assertEqual(id(self.config_['5']), id(ref2.config)) + self.assertEqual(id(ref2.config), id(config2['5'])) + + def test_init_with_parent_update(self): + # create ConfigReference instance with parent param. + # it will update the key value pair in parent config if it exists. + config3 = {'5': {'7': 7}} + ref3 = config_reference.ConfigReference(config3['5'], self.ref_, '5') + expected_config = copy.deepcopy(self.config_) + util.merge_dict(expected_config, config3) + self.assertEqual(self.config_, expected_config) + self.assertEqual(id(self.config_['5']), id(ref3.config)) + self.assertEqual(id(ref3.config), id(config3['5'])) + + def test_init_config_keys(self): + # config key should be string. + config_reference.ConfigReference(1) + config_reference.ConfigReference('1') + config_reference.ConfigReference([1, 2]) + config_reference.ConfigReference({'1': 2}) + config_reference.ConfigReference({u'1': 2}) + self.assertRaises( + TypeError, config_reference.ConfigReference, {1: 2}) + + def test_init_parent_type(self): + # parent instance should be of ConfigReference. + self.assertRaises( + TypeError, config_reference.ConfigReference, + {'1': 2}, parent=object()) + + def test_init_parent_key_type(self): + # parent key should be string. + self.assertRaises( + TypeError, config_reference.ConfigReference, + {'1': 2}, parent=self.ref_, parent_key=6) + + def test_ref_noexist_key(self): + # raise KeyError when accessing noexist key. + self.assertRaises(KeyError, self.ref_.ref, '') + self.assertRaises(KeyError, self.ref_.ref, '/1/2/4') + + def test_ref_dot_key(self): + # . key returns the same reference. + self.assertEqual(id(self.ref_.ref('.')), id(self.ref_)) + self.assertEqual(self.ref_.ref('.').config, self.config_) + + def test_ref_double_dot_key(self): + # .. key returns the same reference if ref itself + # is the top level reference. + self.assertEqual(id(self.ref_.ref('..')), id(self.ref_)) + self.assertEqual(self.ref_.ref('..').config, self.config_) + + def test_ref_slash_key(self): + # / key returns the same reference if the ref itself is + # the top level reference. + self.assertEqual(id(self.ref_.ref('/')), id(self.ref_)) + self.assertEqual(self.ref_.ref('/').config, self.config_) + + def test_ref_key(self): + # ref() returns the reference of the . + self.assertEqual(self.ref_.ref('1').config, self.config_['1']) + self.assertEqual(self.ref_.ref('1/2').config, + self.config_['1']['2']) + self.assertEqual(self.ref_.ref('1/2/.').config, + self.config_['1']['2']) + self.assertEqual(self.ref_.ref('1/2/..').config, + self.config_['1']) + self.assertEqual(self.ref_.ref('1/2//').config, + self.config_['1']['2']) + self.assertEqual(self.ref_.ref('/1').config, + self.config_['1']) + self.assertEqual(self.ref_.ref('/1/2').config, + self.config_['1']['2']) + + def test_ref_key_in_parent(self): + # from sub ref, we can get the reference of it parent or root. + subref = self.ref_.ref('1') + self.assertEqual(id(subref.ref('..')), id(self.ref_)) + self.assertEqual(subref.ref('..').config, self.config_) + self.assertEqual(id(subref.ref('../..')), id(self.ref_)) + self.assertEqual(subref.ref('../..').config, self.config_) + self.assertEqual(id(subref.ref('/')), id(self.ref_)) + self.assertEqual(subref.ref('/').config, self.config_) + self.assertEqual(subref.ref('2').config, self.config_['1']['2']) + self.assertEqual(subref.ref('2/..').config, self.config_['1']) + self.assertEqual(subref.ref('/4').config, self.config_['4']) + + def test_ref_key_not_exist(self): + subref = self.ref_.ref('1') self.assertRaises(KeyError, subref.ref, '/4/5') self.assertRaises(KeyError, subref.ref, '/9') - subref2 = ref.ref('9', True) - self.assertEqual(ref.ref('9'), subref2) + + def test_ref_key_not_exist_and_create(self): + # create sub reference if key does not exists and + # create_if_not_exist param is True. + subref2 = self.ref_.ref('9', True) + self.assertEqual(self.ref_.ref('9'), subref2) def test_refs(self): """test refs function.""" - config = {'1': {'2': 3, '10': {}}, '4': [5, 6, 7], '8': 8, '88': 88} - ref = config_reference.ConfigReference(config) - refkeys = ref.ref_keys('1') + # ref_keys will return all matching refs. + refkeys = self.ref_.ref_keys('1') self.assertEqual(set(refkeys), set(['1'])) - refkeys = ref.ref_keys('/1/*') + + def test_refs_asterisks(self): + refkeys = self.ref_.ref_keys('/1/*') self.assertEqual(set(refkeys), set(['/1/2', '/1/10'])) - refkeys = ref.ref_keys('*') - self.assertEqual(set(refkeys), set(['1', '4', '8', '88'])) - refkeys = ref.ref_keys('8*') - self.assertEqual(set(refkeys), set(['8', '88'])) - self.assertRaises(KeyError, ref.ref_keys, '') + refkeys = self.ref_.ref_keys('*') + self.assertEqual(set(refkeys), set(['1', '4', '8'])) + refkeys = self.ref_.ref_keys('8*') + self.assertEqual(set(refkeys), set(['8'])) + + def test_refs_empty_key(self): + # ref keys will raise KeyError if the param is empty. + self.assertRaises(KeyError, self.ref_.ref_keys, '') def test_contains(self): """test contains function.""" - config = {'1': {'2': '3', '10': {}}, '4': [5, 6, 7], '8': 8} - ref = config_reference.ConfigReference(config) - self.assertIn('/1/2', ref) - self.assertIn('1/10/', ref) - self.assertIn('4/', ref) - self.assertIn('/1/2/..', ref) - self.assertNotIn('/1/3/7', ref) - self.assertNotIn('/1/2/3/..', ref) + self.assertIn('/1/2', self.ref_) + self.assertIn('1/10/', self.ref_) + self.assertIn('4/', self.ref_) + self.assertIn('/1/2/..', self.ref_) + self.assertNotIn('/1/3/7', self.ref_) + self.assertNotIn('/1/2/3/..', self.ref_) + + def test_getitem(self): + """test getitem function.""" + self.assertEqual(self.ref_['1'], self.config_['1']) + self.assertEqual(self.ref_['1/2'], self.config_['1']['2']) + self.assertEqual(self.ref_['/1'], self.config_['1']) + self.assertEqual(self.ref_['1/2/../../4'], self.config_['4']) def test_setitem(self): """test setitem function.""" - config = {'1': {'2': '3', '10': {}}, '4': [5, 6, 7], '8': 8} - ref = config_reference.ConfigReference(config) - ref['/1/2'] = '6' - self.assertEqual(config['1']['2'], '6') - self.assertEqual(ref['/1/2'], '6') - ref['1/10/5'] = 7 - self.assertEqual(config['1']['10']['5'], 7) - self.assertEqual(ref['1/10/5'], 7) - ref['3/6/8'] = [1, 3, 5] - self.assertEqual(config['3']['6']['8'], [1, 3, 5]) - self.assertEqual(ref['3/6/8'], [1, 3, 5]) + self.ref_['/1/2'] = '6' + self.assertEqual(self.config_['1']['2'], '6') + self.assertEqual(self.ref_['/1/2'], '6') + self.ref_['1/10/5'] = 7 + self.assertEqual(self.config_['1']['10']['5'], 7) + self.assertEqual(self.ref_['1/10/5'], 7) + self.ref_['3/6/8'] = [1, 3, 5] + self.assertEqual(self.config_['3']['6']['8'], [1, 3, 5]) + self.assertEqual(self.ref_['3/6/8'], [1, 3, 5]) def test_del(self): """test del function.""" - config = {'1': {'2': '3', '10': {}}, '4': [5, 6, 7], '8': 8} - ref = config_reference.ConfigReference(config) - del ref['/8'] - self.assertNotIn('8', config) - del ref['1/2'] - self.assertNotIn('2', config['1']) - del ref['1'] - self.assertNotIn('1', config) - self.assertRaises(KeyError, ref.__delitem__, '9') + del self.ref_['/8'] + self.assertNotIn('8', self.config_) + del self.ref_['1/2'] + self.assertNotIn('2', self.config_['1']) + del self.ref_['1'] + self.assertNotIn('1', self.config_) + + def test_del_noexist_key(self): + # del nonexist key will raise KeyError + self.assertRaises(KeyError, self.ref_.__delitem__, '9') + + def test_len(self): + ref = config_reference.ConfigReference({}) + self.assertEqual(len(ref), 0) + ref = config_reference.ConfigReference({'1': '2', '2': '4'}) + self.assertEqual(len(ref), 2) + ref = config_reference.ConfigReference( + {'1': {'2': '3', '4': '5'}, '2': '4'}) + self.assertEqual(len(ref), 4) + + def test_bool(self): + ref = config_reference.ConfigReference({}) + self.assertFalse(ref) + ref = config_reference.ConfigReference({'1': 1}) + self.assertTrue(ref) def test_get(self): """test get function.""" - config = {'1': {'2': '3', '10': {}}, '4': [5, 6, 7], '8': 8} - ref = config_reference.ConfigReference(config) - self.assertEqual(ref.get('1/2'), config['1']['2']) - self.assertIsNone(ref.get('1/3')) - self.assertEqual(ref.get('1/3', 3), 3) - self.assertNotIn('3', config['1']) + self.assertEqual(self.ref_.get('1/2'), self.config_['1']['2']) + self.assertIsNone(self.ref_.get('1/3')) + self.assertEqual(self.ref_.get('1/3', 3), 3) + self.assertNotIn('3', self.config_['1']) def test_setdefault(self): """test setdefault function.""" - config = {'1': {'2': '3', '10': {}}, '4': [5, 6, 7], '8': 8} - ref = config_reference.ConfigReference(config) - self.assertEqual(ref.setdefault('1/2').config, config['1']['2']) - self.assertIsNone(ref.setdefault('1/3').config) - self.assertEqual(ref.setdefault('1/4', 4).config, 4) - self.assertEqual(4, config['1']['4']) + self.assertEqual(self.ref_.setdefault('1/2').config, + self.config_['1']['2']) + self.assertIsNone(self.ref_.setdefault('1/3').config) + self.assertEqual(self.ref_.setdefault('1/4', 4).config, 4) + self.assertEqual(4, self.config_['1']['4']) def test_update(self): """test update function.""" - config = {'1': {'2': '3', '10': {}}, '4': [5, 6, 7], '8': 8} - expected_config = copy.deepcopy(config) - - ref = config_reference.ConfigReference(config) + expected_config = copy.deepcopy(self.config_) config2 = {'9': 9, '10': {'10': 10}} util.merge_dict(expected_config, config2) - ref.update(config2) - self.assertEqual(ref.config, expected_config) - ref.update(10, False) - self.assertEqual(ref.config, expected_config) - ref.update(10) - self.assertEqual(ref.config, 10) + self.ref_.update(config2) + self.assertEqual(self.ref_.config, expected_config) + + def test_update_nooverride(self): + # if override is False and ref config is not None, ignore the update. + expected_config = copy.deepcopy(self.config_) + self.ref_.update(10, False) + self.assertEqual(self.config_, expected_config) + + def test_update_override(self): + # if override is True, it will force update the config. + self.ref_.update(10) + self.assertEqual(self.ref_.config, 10) def test_iter(self): """test iter function.""" - config = {'1': {'2': '3', '10': {}}, '4': [5, 6, 7], '8': 8} + items = dict(self.ref_.items()) + self.assertEqual({ + '1': {'2': 3, '10': {}}, + '1/2': 3, + '1/10': {}, + '4': [5, 6, 7], + '8': 8}, items) + self.assertEqual( + set(self.ref_.keys()), + set(['1', '1/2', '1/10', '4', '8'])) + + def test_match(self): + config = {'1': {'2': 'abcdef'}, '3': ['efg', 'hij', 'k']} ref = config_reference.ConfigReference(config) - keys = ref.keys() - self.assertEqual(set(keys), set(['1', '1/2', '1/10', '4', '8'])) + self.assertTrue(ref.match({'1/2': 'abcdef'})) + self.assertFalse(ref.match({'1/2': 'abceef'})) + self.assertTrue(ref.match({'1/2': '[a-z]+'})) + self.assertFalse(ref.match({'1/2': '[0-9]+'})) + self.assertTrue(ref.match({'3': 'efg'})) + self.assertFalse(ref.match({'3': 'm'})) + self.assertTrue(ref.match({'3': 'hij'})) + + def test_filter(self): + config = {'1': {'2': 'abcdef', '4': 4}, '3': ['efg', 'hij', 'k']} + ref = config_reference.ConfigReference(config) + self.assertEqual(ref.filter(['1/2', '1/4', '5']), + {'1/2': 'abcdef', '1/4': 4}) if __name__ == '__main__': + flags.init() + logsetting.init() unittest2.main() diff --git a/compass/tests/config_management/utils/test_config_translator.py b/compass/tests/config_management/utils/test_config_translator.py index a64aee62..b3d76083 100755 --- a/compass/tests/config_management/utils/test_config_translator.py +++ b/compass/tests/config_management/utils/test_config_translator.py @@ -18,7 +18,6 @@ .. moduleauthor:: Xiaodong Wang """ -import functools import os import unittest2 @@ -30,12 +29,362 @@ from compass.utils import setting_wrapper as setting reload(setting) +from compass.config_management.utils import config_reference from compass.config_management.utils import config_translator -from compass.config_management.utils import config_translator_callbacks from compass.utils import flags from compass.utils import logsetting +class TestKeyTranslator(unittest2.TestCase): + """test key translator class.""" + + def setUp(self): + super(TestKeyTranslator, self).setUp() + logsetting.init() + + def tearDown(self): + super(TestKeyTranslator, self).tearDown() + + def test_init(self): + # translated_keys should be callback or list of string or callback. + config_translator.KeyTranslator( + translated_keys=['/a/b/c', '/d/e']) + config_translator.KeyTranslator( + translated_keys=[u'/a/b/c', u'/d/e']) + config_translator.KeyTranslator( + translated_keys=(lambda sub_ref, ref_key: [])) + config_translator.KeyTranslator( + translated_keys=[lambda sub_ref, ref_key: '/d/e']) + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys='/a/b/c') + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys={'/a/b/c': 'd/e'}) + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys=[5, 6, 7]) + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys=[('5', '6')]) + + def test_init_translated_keys_noasterrisks(self): + # the key in translated key should not contain '*'. + self.assertRaises( + KeyError, config_translator.KeyTranslator, + translated_keys=['/a/*/b']) + + def test_init_from_keys(self): + # the from keys should be dict of string to string. + config_translator.KeyTranslator( + translated_keys=['/a/b/c', '/d/e'], from_keys={'m': '/m/n'}) + config_translator.KeyTranslator( + translated_keys=['/a/b/c', '/d/e'], from_keys={u'm': u'/m/n'}) + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys=['/a/b/c'], from_keys=['m']) + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys=['/a/b/c'], from_keys='m') + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys=['/a/b/c'], from_keys={5: 'm'}) + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys=['/a/b/c'], from_keys={'m': 5}) + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys=['/a/b/c'], from_keys={'m': ['/m/n']}) + + def test_init_from_keys_noasterisks(self): + # the value of the from_keys should not contain '*'. + self.assertRaises( + KeyError, config_translator.KeyTranslator, + translated_keys=['/a/b/c'], from_keys={'m': '/m/*/n'}) + + def test_init_from_values(self): + # from_values should be dict of string to string. + config_translator.KeyTranslator( + translated_keys=['/a/b/c'], from_values={'m': '/m'}) + config_translator.KeyTranslator( + translated_keys=['/a/b/c'], from_values={u'm': u'/m'}) + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys=['/a/b/c'], from_values=['m']) + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys=['/a/b/c'], from_values='m') + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys=['/a/b/c'], from_values={5: 'm'}) + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys=['/a/b/c'], from_keys={'m': 5}) + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys=['/a/b/c'], from_values={'m': ['/m/n']}) + + def test_init_from_values_noasterisks(self): + # the value of the from_values should not contain '*'. + self.assertRaises( + KeyError, config_translator.KeyTranslator, + translated_keys=['/a/b/c'], from_values={'m': '/m/*/n'}) + + def test_init_override_conditions(self): + # override_conditions should be dict of string to string + config_translator.KeyTranslator( + translated_keys=['1/2/3', '/4/5/6'], + override_conditions={'hello': 'hi'}) + config_translator.KeyTranslator( + translated_keys=['1/2/3', '/4/5/6'], + override_conditions={u'hello': u'hi'}) + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys=['1/2/3', '/4/5/6'], + override_conditions=['hello', 'hi']) + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys=['1/2/3', '/4/5/6'], + override_conditions='hello') + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys=['1/2/3', '/4/5/6'], + override_conditions={5: 'hi'}) + self.assertRaises( + TypeError, config_translator.KeyTranslator, + translated_keys=['1/2/3', '/4/5/6'], + override_conditions={'hello': 5}) + + def test_init_override_conditions_noasterisks(self): + # the value in override_conditions should not contains '*'. + self.assertRaises( + KeyError, config_translator.KeyTranslator, + translated_keys=['1/2/3', '/4/5/6'], + override_conditions={'hello': 'hi*hi'}) + + def test_translate(self): + # test get translated keys. + # only keys in translated_keys is set in translted config. + config = { + 'key1': 'abc', + 'key2': 'bcd', + 'key3': 'mnq' + } + ref = config_reference.ConfigReference(config) + translated_config = {} + translated_ref = config_reference.ConfigReference(translated_config) + translator = config_translator.KeyTranslator( + translated_keys=['key2', 'key3']) + translator.translate(ref, 'key1', translated_ref) + self.assertEqual(translated_config, {'key2': 'abc', 'key3': 'abc'}) + + def test_translate_translated_keys_callback(self): + # translated_keys can be callback to dynamically + # get the translated_keys. + config = { + 'key1': 'abc', + 'key2': 'bcd', + 'key3': 'mnq' + } + ref = config_reference.ConfigReference(config) + translated_config = {} + translated_ref = config_reference.ConfigReference(translated_config) + translator = config_translator.KeyTranslator( + translated_keys=( + lambda sub_ref, ref_key: ['m%s' % ref_key, 'n%s' % ref_key])) + translator.translate(ref, 'key*', translated_ref) + self.assertEqual( + translated_config, { + 'mkey1': 'abc', 'mkey2': 'bcd', 'mkey3': 'mnq', + 'nkey1': 'abc', 'nkey2': 'bcd', 'nkey3': 'mnq', + } + ) + + def test_translate_translated_keys_each_key_callback(self): + # each translated key can be a callback to dynamically + # get the translated key. + config = { + 'key1': 'abc', + 'key2': 'bcd', + 'key3': 'mnq' + } + ref = config_reference.ConfigReference(config) + translated_config = {} + translated_ref = config_reference.ConfigReference(translated_config) + translator = config_translator.KeyTranslator( + translated_keys=['key1', (lambda sub_ref, ref_key: 'mkey2')]) + translator.translate(ref, 'key1', translated_ref) + self.assertEqual( + translated_config, {'key1': 'abc', 'mkey2': 'abc'}) + + def test_translate_from_keys(self): + # generate translated_keys from some keys from config. + config = { + 'key': 'abc', + 'key_suffix': 'A', + 'key_prefix': 'B' + } + + def _generate_key(sub_ref, ref_key, prefix='', suffix=''): + return '%s%s%s' % (prefix, ref_key, suffix) + + def _generate_keys(sub_ref, ref_key, prefix='', suffix=''): + return ['%s%s%s' % (prefix, ref_key, suffix)] + + ref = config_reference.ConfigReference(config) + translated_config = {} + translated_ref = config_reference.ConfigReference(translated_config) + translator = config_translator.KeyTranslator( + translated_keys=[_generate_key], + from_keys={'prefix': '/key_prefix', 'suffix': '/key_suffix'}) + translator.translate(ref, 'key', translated_ref) + self.assertEqual(translated_config, {'BkeyA': 'abc'}) + translated_config = {} + translated_ref = config_reference.ConfigReference(translated_config) + translator = config_translator.KeyTranslator( + translated_keys=_generate_keys, + from_keys={'prefix': '/key_prefix', 'suffix': '/key_suffix'}) + translator.translate(ref, 'key', translated_ref) + self.assertEqual(translated_config, {'BkeyA': 'abc'}) + + def test_translate_translated_value(self): + # translated_value can be set explictly. + config = { + 'key': 'abc', + 'key_suffix': 'A', + 'key_prefix': 'B' + } + ref = config_reference.ConfigReference(config) + translated_config = {} + translated_ref = config_reference.ConfigReference(translated_config) + translator = config_translator.KeyTranslator( + translated_keys=['mnq'], + translated_value='mnq') + translator.translate(ref, 'key', translated_ref) + self.assertEqual(translated_config, {'mnq': 'mnq'}) + + def test_translated_value_by_callback_none(self): + # translated value can be generated from callback. + # the value will be ignored when generated translated_value is None. + config = { + 'key': 'abc', + } + ref = config_reference.ConfigReference(config) + translated_config = {} + translated_ref = config_reference.ConfigReference(translated_config) + + def _generate_none( + sub_ref, ref_key, translated_sub_ref, translated_key + ): + return None + + translator = config_translator.KeyTranslator( + translated_keys=['mnq'], + translated_value=_generate_none) + translator.translate(ref, 'key', translated_ref) + self.assertEqual(translated_config, {'mnq': None}) + + def test_translate_translated_value_by_callback(self): + # translated_value can be set from some field of config. + config = { + 'key': 'abc', + 'key_suffix': 'A', + 'key_prefix': 'B' + } + ref = config_reference.ConfigReference(config) + translated_config = {} + translated_ref = config_reference.ConfigReference(translated_config) + + def _generate_value( + sub_ref, ref_key, translated_sub_ref, translated_key, + prefix='', suffix='' + ): + return '%s%s%s' % (prefix, sub_ref.config, suffix) + + translator = config_translator.KeyTranslator( + translated_keys=['mnq'], + translated_value=_generate_value, + from_values={'prefix': '/key_prefix', 'suffix': '/key_suffix'}) + translator.translate(ref, 'key', translated_ref) + self.assertEqual(translated_config, {'mnq': 'BabcA'}) + + def test_translate_nooverride(self): + # the translated key will be ignored when the key has already existed + # in translated config and override is False. + config = { + 'key': 'abc', + } + ref = config_reference.ConfigReference(config) + translated_config = {'mnq': 'mnq'} + translated_ref = config_reference.ConfigReference(translated_config) + translator = config_translator.KeyTranslator( + translated_keys=['mnq'], override=False) + translator.translate(ref, 'key', translated_ref) + self.assertEqual(translated_config, {'mnq': 'mnq'}) + + def test_translate_override(self): + # the translated config will be overrided if override param is True. + config = { + 'key': 'abc', + } + ref = config_reference.ConfigReference(config) + translated_config = {'mnq': 'mnq'} + translated_ref = config_reference.ConfigReference(translated_config) + translator = config_translator.KeyTranslator( + translated_keys=['mnq'], override=True) + translator.translate(ref, 'key', translated_ref) + self.assertEqual(translated_config, {'mnq': 'abc'}) + + def test_translate_override_by_callback(self): + # override param can be set from callback. + config = { + 'key': 'abc', + } + ref = config_reference.ConfigReference(config) + translated_config = {'klm': 'klm', 'mnq': 'mnq'} + translated_ref = config_reference.ConfigReference(translated_config) + + def _generate_override( + sub_ref, ref_key, + translated_sub_ref, translated_key + ): + return translated_key == 'klm' + + translator = config_translator.KeyTranslator( + translated_keys=['klm', 'mnq'], override=_generate_override) + translator.translate(ref, 'key', translated_ref) + self.assertEqual(translated_config, {'klm': 'abc', 'mnq': 'mnq'}) + + def test_translate_override_conditions(self): + # override param can be set from some config fields. + config = { + 'key': 'abc', + 'key_suffix': 'A', + 'key_prefix': 'B' + } + ref = config_reference.ConfigReference(config) + translated_config = {'BmA': 'BmA', 'mnq': 'mnq'} + translated_ref = config_reference.ConfigReference(translated_config) + + def _generate_override2( + sub_ref, ref_key, + translated_sub_ref, translated_key, prefix='', suffix='', + ): + return ( + translated_key.startswith(prefix) and + translated_key.endswith(suffix)) + + translator = config_translator.KeyTranslator( + translated_keys=['BmA', 'mnq'], + override=_generate_override2, + override_conditions={ + 'prefix': '/key_prefix', 'suffix': '/key_suffix' + } + ) + translator.translate(ref, 'key', translated_ref) + self.assertEqual(translated_config, {'BmA': 'abc', 'mnq': 'mnq'}) + + class TestConfigTranslatorFunctions(unittest2.TestCase): """test config translator class.""" @@ -46,327 +395,171 @@ class TestConfigTranslatorFunctions(unittest2.TestCase): def tearDown(self): super(TestConfigTranslatorFunctions, self).tearDown() - def test_translate_1(self): - """config translate test.""" + def test_init(self): + # mapping should be dict of string to list of KeyTranslator. + config_translator.ConfigTranslator( + mapping={ + 'key1': [config_translator.KeyTranslator( + translated_keys=['abc'] + )] + } + ) + config_translator.ConfigTranslator( + mapping={ + u'key1': [config_translator.KeyTranslator( + translated_keys=['abc'] + )] + } + ) + self.assertRaises( + TypeError, config_translator.ConfigTranslator, + mapping=[config_translator.KeyTranslator(translated_keys=['abc'])] + ) + self.assertRaises( + TypeError, config_translator.ConfigTranslator, + mapping=config_translator.KeyTranslator(translated_keys=['abc']) + ) + self.assertRaises( + TypeError, config_translator.ConfigTranslator, + mapping={ + 'abc': config_translator.KeyTranslator(translated_keys=['abc']) + } + ) + self.assertRaises( + TypeError, config_translator.ConfigTranslator, + mapping={ + 1: [config_translator.KeyTranslator(translated_keys=['abc'])] + } + ) + self.assertRaises( + TypeError, config_translator.ConfigTranslator, + mapping={ + 'abc': [ + { + 'm': config_translator.KeyTranslator( + translated_keys=['abc']) + } + ] + } + ) + + def test_translate(self): + """test translate config.""" config = { - 'networking': { - 'interfaces': { - 'management': { - 'mac': '00:00:00:01:02:03', - 'ip': '192.168.1.1', - 'netmask': '255.255.255.0', - 'promisc': 0, - 'nic': 'eth0', - 'gateway': '2.3.4.5', - 'dns_alias': 'hello.ods.com', - }, - 'floating': { - 'promisc': 1, - 'nic': 'eth1', - }, - 'storage': { - 'ip': '172.16.1.1', - 'nic': 'eth2', - }, - 'tenant': { - 'ip': '10.1.1.1', - 'netmask': '255.255.0.0', - 'nic': 'eth0', - }, - }, - 'global': { - 'name_servers': ['nameserver.ods.com'], - 'search_path': 'ods.com', - 'gateway': '10.0.0.1', - 'proxy': 'http://1.2.3.4:3128', - 'ntp_server': '1.2.3.4', - 'ignore_proxy': '127.0.0.1', - }, - }, - } - expected_config = { - 'name_servers_search': 'ods.com', - 'gateway': '10.0.0.1', - 'modify_interface': { - 'dnsname-eth0': 'hello.ods.com', - 'ipaddress-eth2': '172.16.1.1', - 'static-eth2': True, - 'static-eth1': True, - 'static-eth0': True, - 'netmask-eth0': '255.255.255.0', - 'ipaddress-eth0': '192.168.1.1', - 'macaddress-eth0': '00:00:00:01:02:03', - 'management-eth2': False, - 'management-eth0': True, - 'management-eth1': False - }, - 'ksmeta': { - 'promisc_nics': 'eth1', - 'ntp_server': '1.2.3.4', - 'proxy': 'http://1.2.3.4:3128', - 'ignore_proxy': '127.0.0.1' - } + 'key1': 'abc', + 'key2': 'bcd' } translator = config_translator.ConfigTranslator( mapping={ - '/networking/global/gateway': [ + 'key1': [ config_translator.KeyTranslator( - translated_keys=['/gateway'] + translated_keys=['mkey1'] ) ], - '/networking/global/nameservers': [ + 'key2': [ config_translator.KeyTranslator( - translated_keys=['/name_servers'] - ) - ], - '/networking/global/search_path': [ + translated_keys=['mkey2'], + translated_value='mkey2' + ), config_translator.KeyTranslator( - translated_keys=['/name_servers_search'] + translated_keys=['mkey2'], + translated_value='nkey2' ) - ], - '/networking/global/proxy': [ - config_translator.KeyTranslator( - translated_keys=['/ksmeta/proxy'] - ) - ], - '/networking/global/ignore_proxy': [ - config_translator.KeyTranslator( - translated_keys=['/ksmeta/ignore_proxy'] - ) - ], - '/networking/global/ntp_server': [ - config_translator.KeyTranslator( - translated_keys=['/ksmeta/ntp_server'] - ) - ], - '/security/server_credentials/username': [ - config_translator.KeyTranslator( - translated_keys=['/ksmeta/username'] - ) - ], - '/security/server_credentials/password': [ - config_translator.KeyTranslator( - translated_keys=['/ksmeta/password'], - translated_value=( - config_translator_callbacks.get_encrypted_value) - ) - ], - '/partition': [ - config_translator.KeyTranslator( - translated_keys=['/ksmeta/partition'] - ) - ], - '/networking/interfaces/*/mac': [ - config_translator.KeyTranslator( - translated_keys=[functools.partial( - config_translator_callbacks.get_key_from_pattern, - to_pattern='/modify_interface/macaddress-%(nic)s' - )], - from_keys={'nic': '../nic'}, - override=functools.partial( - config_translator_callbacks.override_path_has, - should_exist='management') - ) - ], - '/networking/interfaces/*/ip': [ - config_translator.KeyTranslator( - translated_keys=[functools.partial( - config_translator_callbacks.get_key_from_pattern, - to_pattern='/modify_interface/ipaddress-%(nic)s')], - from_keys={'nic': '../nic'}, - override=functools.partial( - config_translator_callbacks.override_path_has, - should_exist='management') - ) - ], - '/networking/interfaces/*/netmask': [ - config_translator.KeyTranslator( - translated_keys=[functools.partial( - config_translator_callbacks.get_key_from_pattern, - to_pattern='/modify_interface/netmask-%(nic)s')], - from_keys={'nic': '../nic'}, - override=functools.partial( - config_translator_callbacks.override_path_has, - should_exist='management') - ) - ], - '/networking/interfaces/*/dns_alias': [ - config_translator.KeyTranslator( - translated_keys=[functools.partial( - config_translator_callbacks.get_key_from_pattern, - to_pattern='/modify_interface/dnsname-%(nic)s')], - from_keys={'nic': '../nic'}, - override=functools.partial( - config_translator_callbacks.override_path_has, - should_exist='management') - ) - ], - '/networking/interfaces/*/nic': [ - config_translator.KeyTranslator( - translated_keys=[functools.partial( - config_translator_callbacks.get_key_from_pattern, - to_pattern='/modify_interface/static-%(nic)s')], - from_keys={'nic': '../nic'}, - translated_value=True, - override=functools.partial( - config_translator_callbacks.override_path_has, - should_exist='management'), - ), config_translator.KeyTranslator( - translated_keys=[functools.partial( - config_translator_callbacks.get_key_from_pattern, - to_pattern='/modify_interface/management-%(nic)s' - )], - from_keys={'nic': '../nic'}, - translated_value=functools.partial( - config_translator_callbacks.override_path_has, - should_exist='management'), - override=functools.partial( - config_translator_callbacks.override_path_has, - should_exist='management') - ), config_translator.KeyTranslator( - translated_keys=['/ksmeta/promisc_nics'], - from_values={'condition': '../promisc'}, - translated_value=config_translator_callbacks.add_value, - override=True, - ) - ], + ] } ) - translated_config = translator.translate(config) - self.assertEqual(translated_config, expected_config) + self.assertEqual(translated_config, + {'mkey1': 'abc', 'mkey2': 'mkey2'}) - def test_translate_2(self): - """config translate test.""" + def test_translate_nooverride(self): + # the later KeyTranslator will be ignored if the former one + # has already set the value. + config = { + 'key1': 'abc', + 'key2': 'bcd' + } translator = config_translator.ConfigTranslator( mapping={ - '/networking/interfaces/management/ip': [ + 'key1': [ config_translator.KeyTranslator( - translated_keys=[ - '/db/mysql/bind_address', - '/mq/rabbitmg/bind_address', - '/mq/rabbitmq/bind_address', - '/endpoints/compute/metadata/host', - '/endpoints/compute/novnc/host', - '/endpoints/compute/service/host', - '/endpoints/compute/xvpvnc/host', - '/endpoints/ec2/admin/host', - '/endpoints/ec2/service/host', - '/endpoints/identity/admin/host', - '/endpoints/identity/service/host', - '/endpoints/image/registry/host', - '/endpoints/image/service/host', - '/endpoints/metering/service/host', - '/endpoints/network/service/host', - '/endpoints/volume/service/host', - ], - translated_value=( - config_translator_callbacks.get_value_if), - from_values={'condition': '/has_dashboard_roles'}, - override=config_translator_callbacks.override_if_any, - override_conditions={ - 'has_dashboard_roles': '/has_dashboard_roles'} + translated_keys=['mkey1'] ) ], + 'key2': [ + config_translator.KeyTranslator( + translated_keys=['mkey2'], + translated_value='mkey2' + ), + config_translator.KeyTranslator( + translated_keys=['mkey2'], + translated_value='nkey2', + override=False + ) + ] } ) - config1 = { - 'networking': { - 'interfaces': { - 'management': { - 'ip': '1.2.3.4', - }, - }, - }, - 'has_dashboard_roles': True, + translated_config = translator.translate(config) + self.assertEqual(translated_config, + {'mkey1': 'abc', 'mkey2': 'mkey2'}) + + def test_translate_override(self): + # the later KeyTranslator will override the former one + # if override is set. + config = { + 'key1': 'abc', + 'key2': 'bcd' } - expected_config1 = { - 'db': { - 'mysql': { - 'bind_address': '1.2.3.4' - } - }, - 'endpoints': { - 'compute': { - 'novnc': { - 'host': '1.2.3.4' - }, - 'xvpvnc': { - 'host': '1.2.3.4' - }, - 'service': { - 'host': '1.2.3.4' - }, - 'metadata': { - 'host': '1.2.3.4' - } - }, - 'network': { - 'service': { - 'host': '1.2.3.4' - } - }, - 'image': { - 'registry': { - 'host': '1.2.3.4' - }, - 'service': { - 'host': '1.2.3.4' - } - }, - 'metering': { - 'service': { - 'host': '1.2.3.4' - } - }, - 'volume': { - 'service': { - 'host': '1.2.3.4' - } - }, - 'ec2': { - 'admin': { - 'host': '1.2.3.4' - }, - 'service': { - 'host': '1.2.3.4' - } - }, - 'identity': { - 'admin': { - 'host': '1.2.3.4' - }, - 'service': { - 'host': '1.2.3.4' - } - } - }, - 'mq': { - 'rabbitmg': { - 'bind_address': '1.2.3.4' - }, - 'rabbitmq': { - 'bind_address': '1.2.3.4' - } + translator = config_translator.ConfigTranslator( + mapping={ + 'key1': [ + config_translator.KeyTranslator( + translated_keys=['mkey1'] + ) + ], + 'key2': [ + config_translator.KeyTranslator( + translated_keys=['mkey2'], + translated_value='mkey2' + ), + config_translator.KeyTranslator( + translated_keys=['mkey2'], + translated_value='nkey2', + override=True + ) + ] } - } - translated_config1 = translator.translate(config1) - self.assertEqual(translated_config1, expected_config1) + ) + translated_config = translator.translate(config) + self.assertEqual(translated_config, + {'mkey1': 'abc', 'mkey2': 'nkey2'}) - config2 = { - 'networking': { - 'interfaces': { - 'management': { - 'ip': '1.2.3.4', - }, - }, - }, - 'has_dashboard_roles': False, + def test_translated_generated_value_none(self): + # When the generated value is None, + # the translated key will be ignored. + config = { + 'key1': 'abc', + 'key2': 'bcd' } - expected_config2 = None - translated_config2 = translator.translate(config2) - self.assertEqual(translated_config2, expected_config2) + def _generate_none( + sub_ref, ref_key, translated_sub_ref, translated_key + ): + return None + + translator = config_translator.ConfigTranslator( + mapping={ + 'key1': [ + config_translator.KeyTranslator( + translated_keys=['mkey1'], + translated_value=_generate_none + ) + ] + } + ) + translated_config = translator.translate(config) + self.assertEqual(translated_config, + None) if __name__ == '__main__': diff --git a/compass/tests/db/test_database.py b/compass/tests/db/test_database.py new file mode 100644 index 00000000..322fb38c --- /dev/null +++ b/compass/tests/db/test_database.py @@ -0,0 +1,64 @@ +# Copyright 2014 Huawei Technologies Co. Ltd +# +# 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. + +"""test util module. + + .. moduleauthor:: Xiaodong Wang +""" +import os +import unittest2 + + +os.environ['COMPASS_IGNORE_SETTING'] = 'true' + + +from compass.utils import setting_wrapper as setting +reload(setting) + + +from compass.db import database +from compass.utils import flags +from compass.utils import logsetting + + +class TestDatabase(unittest2.TestCase): + """Test database actions.""" + + def setUp(self): + super(TestDatabase, self).setUp() + logsetting.init() + database.init('sqlite://') + + def tearDown(self): + super(TestDatabase, self).tearDown() + + def test_init(self): + database.init('sqlite:///tmp/app.db') + self.assertEqual(str(database.ENGINE.url), + 'sqlite:///tmp/app.db') + self.assertEqual(str(database.SCOPED_SESSION.bind.url), + 'sqlite:///tmp/app.db') + + def test_session(self): + with database.session() as session: + self.assertEqual(database.current_session(), session) + self.assertTrue(database.in_session()) + + self.assertFalse(database.in_session()) + + +if __name__ == '__main__': + flags.init() + logsetting.init() + unittest2.main()