From bf805d145324dc5692ff26aecd931474bfbb4e65 Mon Sep 17 00:00:00 2001 From: Nathaniel Case Date: Tue, 4 Apr 2017 15:00:00 -0400 Subject: [PATCH] junos_config: Remove reliance on ability to output configuration in `set` format (#23225) * Remove reliance on ability to output configuration in `set` format * Support multiple warnings per rpc-reply --- lib/ansible/module_utils/junos.py | 5 ++-- lib/ansible/module_utils/netconf.py | 24 ++++++++++++++++--- .../modules/network/junos/_junos_template.py | 2 +- .../modules/network/junos/junos_config.py | 22 +++++++++-------- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/lib/ansible/module_utils/junos.py b/lib/ansible/module_utils/junos.py index dfe3148157e..211e099eb60 100644 --- a/lib/ansible/module_utils/junos.py +++ b/lib/ansible/module_utils/junos.py @@ -147,7 +147,7 @@ def get_diff(module): if output is not None: return output.text -def load_config(module, candidate, action='merge', commit=False, format='xml', +def load_config(module, candidate, warnings, action='merge', commit=False, format='xml', comment=None, confirm=False, confirm_timeout=None): with locked_config(module): @@ -155,6 +155,8 @@ def load_config(module, candidate, action='merge', commit=False, format='xml', candidate = '\n'.join(candidate) reply = load_configuration(module, candidate, action=action, format=format) + if isinstance(reply, list): + warnings.extend(reply) validate(module) diff = get_diff(module) @@ -168,4 +170,3 @@ def load_config(module, candidate, action='merge', commit=False, format='xml', discard_changes(module) return diff - diff --git a/lib/ansible/module_utils/netconf.py b/lib/ansible/module_utils/netconf.py index f2b0e037473..580de33c2b3 100644 --- a/lib/ansible/module_utils/netconf.py +++ b/lib/ansible/module_utils/netconf.py @@ -31,13 +31,31 @@ from xml.etree.ElementTree import tostring, fromstring from ansible.module_utils.connection import exec_command + +NS_MAP = {'nc': "urn:ietf:params:xml:ns:netconf:base:1.0"} + def send_request(module, obj, check_rc=True): request = tostring(obj) rc, out, err = exec_command(module, request) - if rc != 0: - if check_rc: + if rc != 0 and check_rc: + error_root = fromstring(err) + fake_parent = Element('root') + fake_parent.append(error_root) + + error_list = fake_parent.findall('.//nc:rpc-error', NS_MAP) + if not error_list: module.fail_json(msg=str(err)) - return fromstring(out) + + warnings = [] + for rpc_error in error_list: + message = rpc_error.find('./nc:error-message', NS_MAP).text + severity = rpc_error.find('./nc:error-severity', NS_MAP).text + + if severity == 'warning': + warnings.append(message) + else: + module.fail_json(msg=str(err)) + return warnings return fromstring(out) def children(root, iterable): diff --git a/lib/ansible/modules/network/junos/_junos_template.py b/lib/ansible/modules/network/junos/_junos_template.py index 7b82e4143af..5dd13cfa7f8 100644 --- a/lib/ansible/modules/network/junos/_junos_template.py +++ b/lib/ansible/modules/network/junos/_junos_template.py @@ -157,7 +157,7 @@ def main(): module.fail_json(msg='unable to retrieve device configuration') result['__backup__'] = str(match.text).strip() - diff = load_config(module, src, action=action, commit=commit, format=fmt) + diff = load_config(module, src, warnings, action=action, commit=commit, format=fmt) if diff: result['changed'] = True if module._diff: diff --git a/lib/ansible/modules/network/junos/junos_config.py b/lib/ansible/modules/network/junos/junos_config.py index c26a5fdb498..b69b1e570e4 100644 --- a/lib/ansible/modules/network/junos/junos_config.py +++ b/lib/ansible/modules/network/junos/junos_config.py @@ -219,12 +219,10 @@ def filter_delete_statements(module, candidate): reply = get_configuration(module, format='set') match = reply.find('.//configuration-set') if match is None: - module.fail_json(msg='unable to retrieve device configuration') + # Could not find configuration-set in reply, perhaps device does not support it? + return candidate config = str(match.text) - #if 'delete interfaces lo0' in candidate: - # raise ValueError(config) - modified_candidate = candidate[:] for index, line in enumerate(candidate): if line.startswith('delete'): @@ -234,7 +232,7 @@ def filter_delete_statements(module, candidate): return modified_candidate -def configure_device(module): +def configure_device(module, warnings): candidate = module.params['lines'] or module.params['src'] if isinstance(candidate, string_types): candidate = candidate.split('\n') @@ -266,7 +264,7 @@ def configure_device(module): kwargs['format'] = 'text' kwargs['action'] = 'set' - return load_config(module, candidate, **kwargs) + return load_config(module, candidate, warnings, **kwargs) def main(): """ main entry point for module execution @@ -307,10 +305,14 @@ def main(): result = {'changed': False, 'warnings': warnings} if module.params['backup']: - reply = get_configuration(module, format='set') - match = reply.find('.//configuration-set') - if match is None: + for conf_format in ['set', 'text']: + reply = get_configuration(module, format=conf_format) + match = reply.find('.//configuration-%s' % conf_format) + if match is not None: + break + else: module.fail_json(msg='unable to retrieve device configuration') + result['__backup__'] = str(match.text).strip() if module.params['rollback']: @@ -326,7 +328,7 @@ def main(): result['changed'] = True else: - diff = configure_device(module) + diff = configure_device(module, warnings) if diff: if module._diff: result['diff'] = {'prepared': diff}