salt/modules/match.py
# -*- coding: utf-8 -*-
'''
The match module allows for match routines to be run and determine target specs
'''
from __future__ import absolute_import, print_function, unicode_literals
# Import python libs
import inspect
import logging
import sys
import collections
import copy
# Import salt libs
import salt.minion
import salt.loader
from salt.defaults import DEFAULT_TARGET_DELIM
from salt.ext import six
from salt.exceptions import SaltException
__func_alias__ = {
'list_': 'list'
}
log = logging.getLogger(__name__)
def compound(tgt, minion_id=None):
'''
Return True if the minion ID matches the given compound target
minion_id
Specify the minion ID to match against the target expression
.. versionadded:: 2014.7.0
CLI Example:
.. code-block:: bash
salt '*' match.compound 'L@cheese,foo and *'
'''
if minion_id is not None:
opts = copy.copy(__opts__)
if not isinstance(minion_id, six.string_types):
minion_id = six.text_type(minion_id)
opts['id'] = minion_id
else:
opts = __opts__
matchers = salt.loader.matchers(opts)
try:
return matchers['compound_match.match'](tgt, opts=opts)
except Exception as exc:
log.exception(exc)
return False
def ipcidr(tgt):
'''
Return True if the minion matches the given ipcidr target
CLI Example:
.. code-block:: bash
salt '*' match.ipcidr '192.168.44.0/24'
delimiter
Pillar Example:
.. code-block:: yaml
'172.16.0.0/12':
- match: ipcidr
- nodeclass: internal
'''
matchers = salt.loader.matchers(__opts__)
try:
return matchers['ipcidr_match.match'](tgt, opts=__opts__)
except Exception as exc:
log.exception(exc)
return False
def pillar_pcre(tgt, delimiter=DEFAULT_TARGET_DELIM):
'''
Return True if the minion matches the given pillar_pcre target. The
``delimiter`` argument can be used to specify a different delimiter.
CLI Example:
.. code-block:: bash
salt '*' match.pillar_pcre 'cheese:(swiss|american)'
salt '*' match.pillar_pcre 'clone_url|https://github\\.com/.*\\.git' delimiter='|'
delimiter
Specify an alternate delimiter to use when traversing a nested dict
.. versionadded:: 2014.7.0
delim
Specify an alternate delimiter to use when traversing a nested dict
.. versionadded:: 0.16.4
.. deprecated:: 2015.8.0
'''
matchers = salt.loader.matchers(__opts__)
try:
return matchers['pillar_pcre_match.match'](tgt, delimiter=delimiter, opts=__opts__)
except Exception as exc:
log.exception(exc)
return False
def pillar(tgt, delimiter=DEFAULT_TARGET_DELIM):
'''
Return True if the minion matches the given pillar target. The
``delimiter`` argument can be used to specify a different delimiter.
CLI Example:
.. code-block:: bash
salt '*' match.pillar 'cheese:foo'
salt '*' match.pillar 'clone_url|https://github.com/saltstack/salt.git' delimiter='|'
delimiter
Specify an alternate delimiter to use when traversing a nested dict
.. versionadded:: 2014.7.0
delim
Specify an alternate delimiter to use when traversing a nested dict
.. versionadded:: 0.16.4
.. deprecated:: 2015.8.0
'''
matchers = salt.loader.matchers(__opts__)
try:
return matchers['pillar_match.match'](tgt, delimiter=delimiter, opts=__opts__)
except Exception as exc:
log.exception(exc)
return False
def data(tgt):
'''
Return True if the minion matches the given data target
CLI Example:
.. code-block:: bash
salt '*' match.data 'spam:eggs'
'''
matchers = salt.loader.matchers(__opts__)
try:
return matchers['data_match.match'](tgt, opts=__opts__)
except Exception as exc:
log.exception(exc)
return False
def grain_pcre(tgt, delimiter=DEFAULT_TARGET_DELIM):
'''
Return True if the minion matches the given grain_pcre target. The
``delimiter`` argument can be used to specify a different delimiter.
CLI Example:
.. code-block:: bash
salt '*' match.grain_pcre 'os:Fedo.*'
salt '*' match.grain_pcre 'ipv6|2001:.*' delimiter='|'
delimiter
Specify an alternate delimiter to use when traversing a nested dict
.. versionadded:: 2014.7.0
delim
Specify an alternate delimiter to use when traversing a nested dict
.. versionadded:: 0.16.4
.. deprecated:: 2015.8.0
'''
matchers = salt.loader.matchers(__opts__)
try:
return matchers['grain_pcre_match.match'](tgt, delimiter=delimiter, opts=__opts__)
except Exception as exc:
log.exception(exc)
return False
def grain(tgt, delimiter=DEFAULT_TARGET_DELIM):
'''
Return True if the minion matches the given grain target. The ``delimiter``
argument can be used to specify a different delimiter.
CLI Example:
.. code-block:: bash
salt '*' match.grain 'os:Ubuntu'
salt '*' match.grain 'ipv6|2001:db8::ff00:42:8329' delimiter='|'
delimiter
Specify an alternate delimiter to use when traversing a nested dict
.. versionadded:: 2014.7.0
delim
Specify an alternate delimiter to use when traversing a nested dict
.. versionadded:: 0.16.4
.. deprecated:: 2015.8.0
'''
matchers = salt.loader.matchers(__opts__)
try:
return matchers['grain_match.match'](tgt, delimiter=delimiter, opts=__opts__)
except Exception as exc:
log.exception(exc)
return False
def list_(tgt, minion_id=None):
'''
Return True if the minion ID matches the given list target
minion_id
Specify the minion ID to match against the target expression
.. versionadded:: 2014.7.0
CLI Example:
.. code-block:: bash
salt '*' match.list 'server1,server2'
'''
if minion_id is not None:
opts = copy.copy(__opts__)
if not isinstance(minion_id, six.string_types):
minion_id = six.text_type(minion_id)
opts['id'] = minion_id
else:
opts = __opts__
matchers = salt.loader.matchers(opts)
try:
return matchers['list_match.match'](tgt, opts=__opts__)
except Exception as exc:
log.exception(exc)
return False
def pcre(tgt, minion_id=None):
'''
Return True if the minion ID matches the given pcre target
minion_id
Specify the minion ID to match against the target expression
.. versionadded:: 2014.7.0
CLI Example:
.. code-block:: bash
salt '*' match.pcre '.*'
'''
if minion_id is not None:
opts = copy.copy(__opts__)
if not isinstance(minion_id, six.string_types):
minion_id = six.text_type(minion_id)
opts['id'] = minion_id
else:
opts = __opts__
matchers = salt.loader.matchers(opts)
try:
return matchers['pcre_match.match'](tgt, opts=__opts__)
except Exception as exc:
log.exception(exc)
return False
def glob(tgt, minion_id=None):
'''
Return True if the minion ID matches the given glob target
minion_id
Specify the minion ID to match against the target expression
.. versionadded:: 2014.7.0
CLI Example:
.. code-block:: bash
salt '*' match.glob '*'
'''
if minion_id is not None:
opts = copy.copy(__opts__)
if not isinstance(minion_id, six.string_types):
minion_id = six.text_type(minion_id)
opts['id'] = minion_id
else:
opts = __opts__
matchers = salt.loader.matchers(opts)
try:
return matchers['glob_match.match'](tgt, opts=__opts__)
except Exception as exc:
log.exception(exc)
return False
def filter_by(lookup,
tgt_type='compound',
minion_id=None,
merge=None,
merge_lists=False,
default='default'):
'''
Return the first match in a dictionary of target patterns
.. versionadded:: 2014.7.0
CLI Example:
.. code-block:: bash
salt '*' match.filter_by '{foo*: Foo!, bar*: Bar!}' minion_id=bar03
Pillar Example:
.. code-block:: jinja
# Filter the data for the current minion into a variable:
{% set roles = salt['match.filter_by']({
'web*': ['app', 'caching'],
'db*': ['db'],
}, default='web*') %}
# Make the filtered data available to Pillar:
roles: {{ roles | yaml() }}
'''
expr_funcs = dict(inspect.getmembers(sys.modules[__name__],
predicate=inspect.isfunction))
for key in lookup:
params = (key, minion_id) if minion_id else (key, )
if expr_funcs[tgt_type](*params):
if merge:
if not isinstance(merge, collections.Mapping):
raise SaltException(
'filter_by merge argument must be a dictionary.')
if lookup[key] is None:
return merge
else:
salt.utils.dictupdate.update(lookup[key], copy.deepcopy(merge), merge_lists=merge_lists)
return lookup[key]
return lookup.get(default, None)
def search_by(lookup, tgt_type='compound', minion_id=None):
'''
Search a dictionary of target strings for matching targets
This is the inverse of :py:func:`match.filter_by
<salt.modules.match.filter_by>` and allows matching values instead of
matching keys. A minion can be matched by multiple entries.
.. versionadded:: 2017.7.0
CLI Example:
.. code-block:: bash
salt '*' match.search_by '{web: [node1, node2], db: [node2, node]}'
Pillar Example:
.. code-block:: jinja
{% set roles = salt.match.search_by({
'web': ['G@os_family:Debian not nodeX'],
'db': ['L@node2,node3 and G@datacenter:west'],
'caching': ['node3', 'node4'],
}) %}
# Make the filtered data available to Pillar:
roles: {{ roles | yaml() }}
'''
expr_funcs = dict(inspect.getmembers(sys.modules[__name__],
predicate=inspect.isfunction))
matches = []
for key, target_list in lookup.items():
for target in target_list:
params = (target, minion_id) if minion_id else (target, )
if expr_funcs[tgt_type](*params):
matches.append(key)
return matches or None