# # (c) 2015 Peter Sprygada, # # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . # import re import collections class ConfigLine(object): def __init__(self, text): self.text = text self.children = list() self.parents = list() self.raw = None def __str__(self): return self.raw def __eq__(self, other): if self.text == other.text: return self.parents == other.parents def __ne__(self, other): return not self.__eq__(other) def parse(lines, indent): toplevel = re.compile(r'\S') childline = re.compile(r'^\s*(.+)$') repl = r'([{|}|;])' ancestors = list() config = list() for line in str(lines).split('\n'): text = str(re.sub(repl, '', line)).strip() cfg = ConfigLine(text) cfg.raw = line if not text or text[0] in ['!', '#']: continue # handle top level commands if toplevel.match(line): ancestors = [cfg] # handle sub level commands else: match = childline.match(line) line_indent = match.start(1) level = int(line_indent / indent) parent_level = level - 1 cfg.parents = ancestors[:level] if level > len(ancestors): config.append(cfg) continue for i in range(level, len(ancestors)): ancestors.pop() ancestors.append(cfg) ancestors[parent_level].children.append(cfg) config.append(cfg) return config class Conditional(object): ''' Used in command modules to evaluate waitfor conditions ''' OPERATORS = { 'eq': ['eq', '=='], 'neq': ['neq', 'ne', '!='], 'gt': ['gt', '>'], 'ge': ['ge', '>='], 'lt': ['lt', '<'], 'le': ['le', '<='], 'contains': ['contains'] } def __init__(self, conditional): self.raw = conditional key, op, val = shlex.split(conditional) self.key = key self.func = self.func(op) self.value = self._cast_value(val) def __call__(self, data): try: value = self.get_value(dict(result=data)) return self.func(value) except Exception: raise ValueError(self.key) def _cast_value(self, value): if value in BOOLEANS_TRUE: return True elif value in BOOLEANS_FALSE: return False elif re.match(r'^\d+\.d+$', value): return float(value) elif re.match(r'^\d+$', value): return int(value) else: return unicode(value) def func(self, oper): for func, operators in self.OPERATORS.items(): if oper in operators: return getattr(self, func) raise AttributeError('unknown operator: %s' % oper) def get_value(self, result): for key in self.key.split('.'): match = re.match(r'^(.+)\[(\d+)\]', key) if match: key, index = match.groups() result = result[key][int(index)] else: result = result.get(key) return result def number(self, value): if '.' in str(value): return float(value) else: return int(value) def eq(self, value): return value == self.value def neq(self, value): return value != self.value def gt(self, value): return self.number(value) > self.value def ge(self, value): return self.number(value) >= self.value def lt(self, value): return self.number(value) < self.value def le(self, value): return self.number(value) <= self.value def contains(self, value): return self.value in value