Merge pull request #37489 from lorengordon/issue-18836

Creates `filter_by` as a utils function
This commit is contained in:
Mike Place 2016-11-06 14:34:52 +13:00 committed by GitHub
commit 713607c0ee
3 changed files with 118 additions and 41 deletions

View File

@ -13,7 +13,6 @@ import logging
import operator
import collections
import json
import fnmatch
from functools import reduce # pylint: disable=redefined-builtin
# Import 3rd-party libs
@ -25,7 +24,6 @@ from salt.ext.six.moves import range # pylint: disable=import-error,no-name-in-
# Import salt libs
import salt.utils
import salt.utils.dictupdate
from salt.defaults import DEFAULT_TARGET_DELIM
from salt.exceptions import SaltException
@ -569,44 +567,12 @@ def filter_by(lookup_dict, grain='os_family', merge=None, default='default', bas
salt '*' grains.filter_by '{default: {A: {B: C}, D: E}, F: {A: {B: G}}, H: {D: I}}' 'xxx' '{D: J}' 'F' 'default'
# next same as above when default='H' instead of 'F' renders {A: {B: C}, D: J}
'''
ret = None
# Default value would be an empty list if grain not found
val = salt.utils.traverse_dict_and_list(__grains__, grain, [])
# Iterate over the list of grain values to match against patterns in the lookup_dict keys
for each in val if isinstance(val, list) else [val]:
for key in sorted(lookup_dict):
if fnmatch.fnmatchcase(each, key):
ret = lookup_dict[key]
break
if ret is not None:
break
if ret is None:
ret = lookup_dict.get(default, None)
if base and base in lookup_dict:
base_values = lookup_dict[base]
if ret is None:
ret = base_values
elif isinstance(base_values, collections.Mapping):
if not isinstance(ret, collections.Mapping):
raise SaltException(
'filter_by default and look-up values must both be dictionaries.')
ret = salt.utils.dictupdate.update(copy.deepcopy(base_values), ret)
if merge:
if not isinstance(merge, collections.Mapping):
raise SaltException('filter_by merge argument must be a dictionary.')
if ret is None:
ret = merge
else:
salt.utils.dictupdate.update(ret, copy.deepcopy(merge))
return ret
return salt.utils.filter_by(lookup_dict=lookup_dict,
lookup=grain,
traverse=__grains__,
merge=merge,
default=default,
base=base)
def _dict_from_path(path, val, delimiter=DEFAULT_TARGET_DELIM):

View File

@ -361,3 +361,63 @@ def file_exists(path, saltenv=None):
# Provide a jinja function call compatible get aliased as fetch
fetch = get
def filter_by(lookup_dict,
pillar,
merge=None,
default='default',
base=None):
'''
.. versionadded:: Nitrogen
Look up the given pillar in a given dictionary and return the result
:param lookup_dict: A dictionary, keyed by a pillar, containing a value or
values relevant to systems matching that pillar. For example, a key
could be a pillar for a role and the value could the name of a package
on that particular OS.
The dictionary key can be a globbing pattern. The function will return
the corresponding ``lookup_dict`` value where the pilalr value matches
the pattern. For example:
.. code-block:: bash
# this will render 'got some salt' if ``role`` begins with 'salt'
salt '*' pillar.filter_by '{salt*: got some salt, default: salt is not here}' role
:param pillar: The name of a pillar to match with the system's pillar. For
example, the value of the "role" pillar could be used to pull values
from the ``lookup_dict`` dictionary.
The pillar value can be a list. The function will return the
``lookup_dict`` value for a first found item in the list matching
one of the ``lookup_dict`` keys.
:param merge: A dictionary to merge with the results of the pillar
selection from ``lookup_dict``. This allows another dictionary to
override the values in the ``lookup_dict``.
:param default: default lookup_dict's key used if the pillar does not exist
or if the pillar value has no match on lookup_dict. If unspecified
the value is "default".
:param base: A lookup_dict key to use for a base dictionary. The
pillar-selected ``lookup_dict`` is merged over this and then finally
the ``merge`` dictionary is merged. This allows common values for
each case to be collected in the base and overridden by the pillar
selection dictionary and the merge dictionary. Default is unset.
CLI Example:
.. code-block:: bash
salt '*' pillar.filter_by '{web: Serve it up, db: I query, default: x_x}' role
'''
return salt.utils.filter_by(lookup_dict=lookup_dict,
lookup=pillar,
traverse=__pillar__,
merge=merge,
default=default,
base=base)

View File

@ -117,13 +117,14 @@ except (ImportError, OSError, AttributeError, TypeError):
from salt.defaults import DEFAULT_TARGET_DELIM
import salt.defaults.exitcodes
import salt.log
import salt.utils.dictupdate
import salt.version
from salt.utils.decorators import memoize as real_memoize
from salt.textformat import TextFormat
from salt.exceptions import (
CommandExecutionError, SaltClientError,
CommandNotFoundError, SaltSystemExit,
SaltInvocationError
SaltInvocationError, SaltException
)
@ -3162,3 +3163,53 @@ def substr_in_list(string_to_search_for, list_to_search):
string is present in any of the strings which comprise a list
'''
return any(string_to_search_for in s for s in list_to_search)
def filter_by(lookup_dict,
lookup,
traverse,
merge=None,
default='default',
base=None):
'''
'''
ret = None
# Default value would be an empty list if lookup not found
val = traverse_dict_and_list(traverse, lookup, [])
# Iterate over the list of values to match against patterns in the
# lookup_dict keys
for each in val if isinstance(val, list) else [val]:
for key in sorted(lookup_dict):
if fnmatch.fnmatchcase(each, key):
ret = lookup_dict[key]
break
if ret is not None:
break
if ret is None:
ret = lookup_dict.get(default, None)
if base and base in lookup_dict:
base_values = lookup_dict[base]
if ret is None:
ret = base_values
elif isinstance(base_values, collections.Mapping):
if not isinstance(ret, collections.Mapping):
raise SaltException(
'filter_by default and look-up values must both be '
'dictionaries.')
ret = salt.utils.dictupdate.update(copy.deepcopy(base_values), ret)
if merge:
if not isinstance(merge, collections.Mapping):
raise SaltException(
'filter_by merge argument must be a dictionary.')
if ret is None:
ret = merge
else:
salt.utils.dictupdate.update(ret, copy.deepcopy(merge))
return ret