# -*- coding: utf-8 -*- ''' Forked version of lookup.py by Georg Pfuetzenreuter . Notable changes: - Python 3 support - nested pillar lookups Original: https://github.com/vmware-archive/salt-contrib/blob/master/pillars/lookup.py Look up data from other pillar values or by executing a module function. Usage: Generally, this module should be configured as the final ext_pillar, if other ext_pillars are used. A pillar value matching the pattern ${...} will trigger this module to perform a lookup. A lookup may be a pillar value (e.g., ${other_value}) or a call to an execution module (${cmd.run('echo "foo"')}). Note that module functions are executed on the master. Nested functions are supported, as is the passing of a pillar value to a function. E.g.: ${cmd.run(command)} ''' # O Import python libs import inspect import logging import ast import re # Import salt libs import salt.utils # debug import json __virtualname__ = 'lookup' def __virtual__(): return __virtualname__ # Set up logging log = logging.getLogger(__name__) def ext_pillar(minion_id, pillar, *args, **kwargs): def process(o): log.debug('lookup.py: Processing') if isinstance(o, ast.Call): log.debug('lookup.py: is astCall') f = '{0}.{1}'.format(o.func.value.id, o.func.attr) args = [process(a) for a in o.args] kwargs = dict((k.arg, process(k.value)) for k in o.keywords) func = __salt__[f] spec = inspect.getargspec(func) if ('pillar' in spec.args or spec.keywords is not None): kwargs['pillar'] = pillar if ('minion_id' in spec.args or spec.keywords is not None): kwargs['minion_id'] = minion_id return func(*args, **kwargs) elif isinstance(o, ast.Name): log.debug('lookup.py: is astName') myret = salt.utils.data.traverse_dict_and_list(pillar, o.id, 'x', ':') log.debug('lookup.py: returning ' + json.dumps(myret)) return myret elif isinstance(o, ast.Expr): log.debug('lookup.py: is astExpr') return process(o.value) elif isinstance(o, ast.Str) and ':' in ast.literal_eval(o): log.debug('lookup.py: is astStr with colon') saltret = salt.utils.data.traverse_dict_and_list(pillar, ast.literal_eval(o), 'x', ':') log.debug('lookup.py: returning ' + json.dumps(saltret)) return(saltret) else: log.debug('lookup.py: is useless') log.debug(ast.dump(ast.parse(o))) return ast.literal_eval(o) def walk(data): log.debug('lookup.py: Walking') def process_val(k, v): if isinstance(v, dict) or isinstance(v, list): log.debug('lookup.py: Skipping ' + json.dumps(v)) walk(v) elif isinstance(v, bytes) or isinstance(v, str): log.debug('lookup.py: Examining ' + v) m = re.search('^\$\{(.*)\}$', v) if m: log.debug('lookup.py: Match!') s = m.groups()[0] log.debug('lookup.py: sending ' + s + ' for processing') #if ':' in s: # log.debug('lookup.py: processing as string') # process(s) #else: #log.debug('lookup.py: processing with AST') data[k] = process(ast.parse(s).body[0].value) if isinstance(data, dict): for k, v in data.items(): process_val(k, v) elif isinstance(data, list): i = 0 for v in data: process_val(i, v) i = i+1 walk(pillar)