summaryrefslogtreecommitdiffstats
path: root/extmods/pillar/lookup.py
blob: cb4de073c9910ff2fc946341c1301472adb8b97f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# -*- coding: utf-8 -*-
'''
Forked version of lookup.py by Georg Pfuetzenreuter <georg+salt@lysergic.dev>.
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)