Add option -J/--pillar-pcre to match on pillar PCREs

This commit is contained in:
Thayne Harbaugh 2015-04-03 02:47:19 -06:00
parent 6c68668c52
commit c91392bc91
9 changed files with 104 additions and 7 deletions

View file

@ -496,6 +496,7 @@ class LocalClient(object):
* ``grain`` - Match based on a grain comparison
* ``grain_pcre`` - Grain comparison with a regex
* ``pillar`` - Pillar data comparison
* ``pillar_pcre`` - Pillar data comparison with a regex
* ``nodegroup`` - Match on nodegroup
* ``range`` - Use a Range server for matching
* ``compound`` - Pass a compound match string

View file

@ -1022,7 +1022,7 @@ class Minion(MinionBase):
'{0}_match'.format(data['tgt_type']), None)
if match_func is None:
return
if data['tgt_type'] in ('grain', 'grain_pcre', 'pillar'):
if data['tgt_type'] in ('grain', 'grain_pcre', 'pillar', 'pillar_pcre'):
delimiter = data.get('delimiter', DEFAULT_TARGET_DELIM)
if not match_func(data['tgt'], delimiter=delimiter):
return
@ -2729,9 +2729,22 @@ class Matcher(object):
self.opts['pillar'], tgt, delimiter=delimiter
)
def pillar_pcre_match(self, tgt, delimiter=DEFAULT_TARGET_DELIM):
'''
Reads in the pillar pcre match
'''
log.debug('pillar PCRE target: {0}'.format(tgt))
if delimiter not in tgt:
log.error('Got insufficient arguments for pillar PCRE match '
'statement from master')
return False
return salt.utils.subdict_match(
self.opts['pillar'], tgt, delimiter=delimiter, regex_match=True
)
def pillar_exact_match(self, tgt, delimiter=':'):
'''
Reads in the pillar match, no globbing
Reads in the pillar match, no globbing, no PCRE
'''
log.debug('pillar target: {0}'.format(tgt))
if delimiter not in tgt:
@ -2791,6 +2804,7 @@ class Matcher(object):
ref = {'G': 'grain',
'P': 'grain_pcre',
'I': 'pillar',
'J': 'pillar_pcre',
'L': 'list',
'S': 'ipcidr',
'E': 'pcre'}

View file

@ -70,6 +70,46 @@ def ipcidr(tgt):
return False
def pillar_pcre(tgt, delimiter=DEFAULT_TARGET_DELIM, delim=None):
'''
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:: 2014.7.0
'''
if delim is not None:
salt.utils.warn_until(
'Beryllium',
'The \'delim\' argument to match.pillar_pcre has been deprecated '
'and will be removed in a future release. Please use '
'\'delimiter\' instead.'
)
delimiter = delim
matcher = salt.minion.Matcher({'pillar': __pillar__}, __salt__)
try:
return matcher.pillar_pcre_match(tgt, delimiter=delimiter)
except Exception as exc:
log.exception(exc)
return False
def pillar(tgt, delimiter=DEFAULT_TARGET_DELIM, delim=None):
'''
Return True if the minion matches the given pillar target. The

View file

@ -213,6 +213,7 @@ def get(tgt, fun, expr_form='glob'):
grain_pcre
compound
pillar
pillar_pcre
Note that all pillar matches, whether using the compound matching system or
the pillar matching system, will be exact matches, with globbing disabled.
@ -235,6 +236,7 @@ def get(tgt, fun, expr_form='glob'):
'ipcidr': __salt__['match.ipcidr'],
'compound': __salt__['match.compound'],
'pillar': __salt__['match.pillar'],
'pillar_pcre': __salt__['match.pillar_pcre'],
}[expr_form](tgt)
if is_target:
data = __salt__['data.getval']('mine_cache')

View file

@ -160,6 +160,7 @@ def publish(tgt, fun, arg=None, expr_form='glob', returner='', timeout=5):
- grain
- grain_pcre
- pillar
- pillar_pcre
- ipcidr
- range
- compound

View file

@ -120,6 +120,7 @@ def publish(tgt, fun, arg=None, expr_form='glob', returner='', timeout=5):
- grain
- grain_pcre
- pillar
- pillar_pcre
- ipcidr
- range
- compound

View file

@ -219,6 +219,16 @@ class CkMinions(object):
'''
return self._check_cache_minions(expr, delimiter, greedy, 'pillar')
def _check_pillar_pcre_minions(self, expr, delimiter, greedy):
'''
Return the minions found by looking via pillar with PCRE
'''
return self._check_cache_minions(expr,
delimiter,
greedy,
'pillar',
regex_match=True)
def _check_pillar_exact_minions(self, expr, delimiter, greedy):
'''
Return the minions found by looking via pillar
@ -360,6 +370,7 @@ class CkMinions(object):
ref = {'G': self._check_grain_minions,
'P': self._check_grain_pcre_minions,
'I': self._check_pillar_minions,
'J': self._check_pillar_pcre_minions,
'L': self._check_list_minions,
'S': self._check_ipcidr_minions,
'E': self._check_pcre_minions,
@ -372,13 +383,13 @@ class CkMinions(object):
tokens = expr.split()
for match in tokens:
# Try to match tokens from the compound target, first by using
# the 'G, X, I, L, S, E' matcher types, then by hostname glob.
# the 'G, X, I, J, L, S, E' matcher types, then by hostname glob.
if '@' in match and match[1] == '@':
comps = match.split('@')
matcher = ref.get(comps[0])
matcher_args = ['@'.join(comps[1:])]
if comps[0] in ('G', 'P', 'I'):
if comps[0] in ('G', 'P', 'I', 'J'):
matcher_args.append(delimiter)
matcher_args.append(True)
@ -532,6 +543,7 @@ class CkMinions(object):
ref = {'G': 'grain',
'P': 'grain_pcre',
'I': 'pillar',
'J': 'pillar_pcre',
'L': 'list',
'S': 'ipcidr',
'E': 'pcre',
@ -539,7 +551,8 @@ class CkMinions(object):
infinite = [
'node',
'ipcidr',
'pillar']
'pillar',
'pillar_pcre']
if not self.opts.get('minion_data_cache', False):
infinite.append('grain')
infinite.append('grain_pcre')
@ -612,7 +625,7 @@ class CkMinions(object):
'''
if publish_validate:
v_tgt_type = tgt_type
if tgt_type.lower() == 'pillar':
if tgt_type.lower() in ('pillar', 'pillar_pcre'):
v_tgt_type = 'pillar_exact'
elif tgt_type.lower() == 'compound':
v_tgt_type = 'compound_pillar_exact'
@ -620,7 +633,8 @@ class CkMinions(object):
minions = set(self.check_minions(tgt, tgt_type))
mismatch = bool(minions.difference(v_minions))
# If the non-exact match gets more minions than the exact match
# then pillar globbing is being used, and we have a problem
# then pillar globbing or PCRE is being used, and we have a
# problem
if mismatch:
return False
# compound commands will come in a list so treat everything as a list

View file

@ -938,6 +938,15 @@ class ExtendedTargetOptionsMixIn(TargetOptionsMixIn):
'for the target is the pillar key followed by a glob'
'expression:\n"role:production*"')
)
group.add_option(
'-J', '--pillar-pcre',
default=False,
action='store_true',
help=('Instead of using shell globs to evaluate the target '
'use a pillar value to identify targets, the syntax '
'for the target is the pillar key followed by a pcre'
'regular expression:\n"role:prod.*"')
)
group.add_option(
'-S', '--ipcidr',
default=False,

View file

@ -191,6 +191,21 @@ class MatchTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
self.assertIn('minion', data)
self.assertIn('sub_minion', data)
def test_repillar(self):
'''
test salt pillar PCRE matcher
'''
data = self.run_salt(
'-t 1 --pillar-pcre "monty:^(python|hall)$" test.ping'
)
data = '\n'.join(data)
self.assertIn('minion', data)
self.assertNotIn('sub_minion', data)
data = self.run_salt('--pillar-pcre "knights:^(Robin|Lancelot)$" test.ping')
data = '\n'.join(data)
self.assertIn('sub_minion', data)
self.assertNotIn('minion', data.replace('sub_minion', 'stub'))
def test_ipcidr(self):
subnets_data = self.run_salt('--out yaml \'*\' network.subnets')
yaml_data = yaml.load('\n'.join(subnets_data))