From 5a45ca8bb12678861da0448363597bd6e004cfc7 Mon Sep 17 00:00:00 2001 From: chouseknecht Date: Tue, 9 Feb 2016 14:59:44 -0500 Subject: [PATCH] Move Conditional class to netcfg. Added error handling for connect and execute methods. Fix comments --- lib/ansible/module_utils/eos.py | 11 ++- lib/ansible/module_utils/ios.py | 15 ++- lib/ansible/module_utils/netcfg.py | 141 +++++++++++++++++++++++------ lib/ansible/module_utils/nxos.py | 14 ++- 4 files changed, 147 insertions(+), 34 deletions(-) diff --git a/lib/ansible/module_utils/eos.py b/lib/ansible/module_utils/eos.py index c5446dc5394..5e0dfefc353 100644 --- a/lib/ansible/module_utils/eos.py +++ b/lib/ansible/module_utils/eos.py @@ -38,6 +38,7 @@ def to_list(val): else: return list() + class Eapi(object): def __init__(self, module): @@ -107,6 +108,7 @@ class Eapi(object): return response['result'] + class Cli(object): def __init__(self, module): @@ -121,15 +123,20 @@ class Cli(object): password = self.module.params['password'] self.shell = Shell() - self.shell.open(host, port=port, username=username, password=password) + + try: + self.shell.open(host, port=port, username=username, password=password) + except Exception, exc: + self.module.fail_json('Failed to connect to {0}:{1} - {2}'.format(host, port, str(exc))) def authorize(self): passwd = self.module.params['auth_pass'] self.send(Command('enable', prompt=NET_PASSWD_RE, response=passwd)) - def send(self, commands, encoding='text'): + def send(self, commands): return self.shell.send(commands) + class NetworkModule(AnsibleModule): def __init__(self, *args, **kwargs): diff --git a/lib/ansible/module_utils/ios.py b/lib/ansible/module_utils/ios.py index 95937ca2191..3340213fa81 100644 --- a/lib/ansible/module_utils/ios.py +++ b/lib/ansible/module_utils/ios.py @@ -29,6 +29,7 @@ NET_COMMON_ARGS = dict( provider=dict() ) + def to_list(val): if isinstance(val, (list, tuple)): return list(val) @@ -37,6 +38,7 @@ def to_list(val): else: return list() + class Cli(object): def __init__(self, module): @@ -51,7 +53,11 @@ class Cli(object): password = self.module.params['password'] self.shell = Shell() - self.shell.open(host, port=port, username=username, password=password) + + try: + self.shell.open(host, port=port, username=username, password=password) + except Exception, exc: + self.module.fail_json('Failed to connect to {0}:{1} - {2}'.format(host, port, str(exc))) def authorize(self): passwd = self.module.params['auth_pass'] @@ -60,6 +66,7 @@ class Cli(object): def send(self, commands): return self.shell.send(commands) + class NetworkModule(AnsibleModule): def __init__(self, *args, **kwargs): @@ -101,7 +108,10 @@ class NetworkModule(AnsibleModule): return responses def execute(self, commands, **kwargs): - return self.connection.send(commands) + try: + return self.connection.send(commands, **kwargs) + except Exception, exc: + self.fail_json(msg=exc.message, commands=commands) def disconnect(self): self.connection.close() @@ -115,6 +125,7 @@ class NetworkModule(AnsibleModule): cmd += ' all' return self.execute(cmd)[0] + def get_module(**kwargs): """Return instance of NetworkModule """ diff --git a/lib/ansible/module_utils/netcfg.py b/lib/ansible/module_utils/netcfg.py index afd8be3a56f..644a0a32dfa 100644 --- a/lib/ansible/module_utils/netcfg.py +++ b/lib/ansible/module_utils/netcfg.py @@ -38,48 +38,133 @@ class ConfigLine(object): 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'([{|}|;])' + toplevel = re.compile(r'\S') + childline = re.compile(r'^\s*(.+)$') + repl = r'([{|}|;])' - ancestors = list() - config = list() + ancestors = list() + config = list() - for line in str(lines).split('\n'): - text = str(re.sub(repl, '', line)).strip() + for line in str(lines).split('\n'): + text = str(re.sub(repl, '', line)).strip() - cfg = ConfigLine(text) - cfg.raw = line + cfg = ConfigLine(text) + cfg.raw = line - if not text or text[0] in ['!', '#']: + 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 - # handle top level commands - if toplevel.match(line): - ancestors = [cfg] + for i in range(level, len(ancestors)): + ancestors.pop() - # handle sub level commands + 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: - match = childline.match(line) - line_indent = match.start(1) - level = int(line_indent / indent) - parent_level = level - 1 + result = result.get(key) + return result - cfg.parents = ancestors[:level] + def number(self, value): + if '.' in str(value): + return float(value) + else: + return int(value) - if level > len(ancestors): - config.append(cfg) - continue + def eq(self, value): + return value == self.value - for i in range(level, len(ancestors)): - ancestors.pop() + def neq(self, value): + return value != self.value - ancestors.append(cfg) - ancestors[parent_level].children.append(cfg) + def gt(self, value): + return self.number(value) > self.value - config.append(cfg) + def ge(self, value): + return self.number(value) >= self.value - return config + 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 diff --git a/lib/ansible/module_utils/nxos.py b/lib/ansible/module_utils/nxos.py index 3c8837e0968..8c681830a6d 100644 --- a/lib/ansible/module_utils/nxos.py +++ b/lib/ansible/module_utils/nxos.py @@ -128,6 +128,7 @@ class Nxapi(object): return result + class Cli(object): def __init__(self, module): @@ -142,11 +143,16 @@ class Cli(object): password = self.module.params['password'] self.shell = Shell() - self.shell.open(host, port=port, username=username, password=password) + + try: + self.shell.open(host, port=port, username=username, password=password) + except Exception, exc: + self.module.fail_json('Failed to connect to {0}:{1} - {2}'.format(host, port, str(exc))) def send(self, commands, encoding='text'): return self.shell.send(commands) + class NetworkModule(AnsibleModule): def __init__(self, *args, **kwargs): @@ -190,7 +196,10 @@ class NetworkModule(AnsibleModule): return responses def execute(self, commands, **kwargs): - return self.connection.send(commands, **kwargs) + try: + return self.connection.send(commands, **kwargs) + except Exception, exc: + self.fail_json(msg=exc.message, commands=commands) def disconnect(self): self.connection.close() @@ -206,6 +215,7 @@ class NetworkModule(AnsibleModule): response = self.execute(cmd) return response[0] + def get_module(**kwargs): """Return instance of NetworkModule """