From 47e0d29a598ab61c7343f828e740d97d0ae1dac6 Mon Sep 17 00:00:00 2001 From: Ganesh Nalawade Date: Thu, 29 Nov 2018 13:21:41 +0530 Subject: [PATCH] Fix iosxr netconf plugin response namespace (#49238) * Fix iosxr netconf plugin response namespace * iosxr netconf plugin removes namespace by default for all the responses as parsing of xml is easier without namepsace in iosxr module. However to validate the response received from device against yang model requires namespace to be present in resposne. * Add a parameter in iosxr netconf plugin to control if namespace should be removed from response or not. * Fix CI issues * Fix review comment (cherry picked from commit 829fc0fedad9fd544674049a8ba0a304a40c0f9b) --- .../fragments/iosxr_netconf_plugin_fix.yaml | 2 + .../module_utils/network/iosxr/iosxr.py | 18 ++++-- lib/ansible/plugins/netconf/iosxr.py | 62 +++++++++++++------ 3 files changed, 57 insertions(+), 25 deletions(-) create mode 100644 changelogs/fragments/iosxr_netconf_plugin_fix.yaml diff --git a/changelogs/fragments/iosxr_netconf_plugin_fix.yaml b/changelogs/fragments/iosxr_netconf_plugin_fix.yaml new file mode 100644 index 00000000000..309e05cfecb --- /dev/null +++ b/changelogs/fragments/iosxr_netconf_plugin_fix.yaml @@ -0,0 +1,2 @@ +bugfixes: +- Fix iosxr netconf plugin response namespace (https://github.com/ansible/ansible/pull/49300) diff --git a/lib/ansible/module_utils/network/iosxr/iosxr.py b/lib/ansible/module_utils/network/iosxr/iosxr.py index 23575ce79d6..b49c72875af 100644 --- a/lib/ansible/module_utils/network/iosxr/iosxr.py +++ b/lib/ansible/module_utils/network/iosxr/iosxr.py @@ -358,7 +358,10 @@ def get_config_diff(module, running=None, candidate=None): def discard_config(module): conn = get_connection(module) try: - conn.discard_changes() + if is_netconf(module): + conn.discard_changes(remove_ns=True) + else: + conn.discard_changes() except ConnectionError as exc: module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) @@ -370,9 +373,9 @@ def commit_config(module, comment=None, confirmed=False, confirm_timeout=None, try: if is_netconf(module): if check: - reply = conn.validate() + reply = conn.validate(remove_ns=True) else: - reply = conn.commit(confirmed=confirmed, timeout=confirm_timeout, persist=persist) + reply = conn.commit(confirmed=confirmed, timeout=confirm_timeout, persist=persist, remove_ns=True) elif is_cliconf(module): if check: module.fail_json(msg="Validate configuration is not supported with network_cli connection type") @@ -389,7 +392,10 @@ def get_oper(module, filter=None): if filter is not None: try: - response = conn.get(filter) + if is_netconf(module): + response = conn.get(filter=filter, remove_ns=True) + else: + response = conn.get(filter) except ConnectionError as exc: module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) else: @@ -404,7 +410,7 @@ def get_config(module, config_filter=None, source='running'): # Note: Does not cache config in favour of latest config on every get operation. try: if is_netconf(module): - out = to_xml(conn.get_config(source=source, filter=config_filter)) + out = to_xml(conn.get_config(source=source, filter=config_filter, remove_ns=True)) elif is_cliconf(module): out = conn.get_config(source=source, flags=config_filter) cfg = out.strip() @@ -436,7 +442,7 @@ def load_config(module, command_filter, commit=False, replace=False, try: for filter in to_list(command_filter): - conn.edit_config(filter) + conn.edit_config(config=filter, remove_ns=True) candidate = get_config(module, source='candidate', config_filter=nc_get_filter) diff = get_config_diff(module, running, candidate) diff --git a/lib/ansible/plugins/netconf/iosxr.py b/lib/ansible/plugins/netconf/iosxr.py index a8a0e1b76c7..c64a61071b5 100644 --- a/lib/ansible/plugins/netconf/iosxr.py +++ b/lib/ansible/plugins/netconf/iosxr.py @@ -124,56 +124,80 @@ class Netconf(NetconfBase): # TODO: change .xml to .data_xml, when ncclient supports data_xml on all platforms @ensure_connected - def get(self, filter=None): + def get(self, filter=None, remove_ns=False): if isinstance(filter, list): filter = tuple(filter) try: - response = self.m.get(filter=filter) - return remove_namespaces(response) + resp = self.m.get(filter=filter) + if remove_ns: + response = remove_namespaces(resp) + else: + response = resp.data_xml if hasattr(resp, 'data_xml') else resp.xml + return response except RPCError as exc: raise Exception(to_xml(exc.xml)) @ensure_connected - def get_config(self, source=None, filter=None): + def get_config(self, source=None, filter=None, remove_ns=False): if isinstance(filter, list): filter = tuple(filter) try: - response = self.m.get_config(source=source, filter=filter) - return remove_namespaces(response) + resp = self.m.get_config(source=source, filter=filter) + if remove_ns: + response = remove_namespaces(resp) + else: + response = resp.data_xml if hasattr(resp, 'data_xml') else resp.xml + return response except RPCError as exc: raise Exception(to_xml(exc.xml)) @ensure_connected - def edit_config(self, config=None, format='xml', target='candidate', default_operation=None, test_option=None, error_option=None): + def edit_config(self, config=None, format='xml', target='candidate', default_operation=None, test_option=None, error_option=None, remove_ns=False): if config is None: raise ValueError('config value must be provided') try: - response = self.m.edit_config(config, format=format, target=target, default_operation=default_operation, test_option=test_option, - error_option=error_option) - return remove_namespaces(response) + resp = self.m.edit_config(config, format=format, target=target, default_operation=default_operation, test_option=test_option, + error_option=error_option) + if remove_ns: + response = remove_namespaces(resp) + else: + response = resp.data_xml if hasattr(resp, 'data_xml') else resp.xml + return response except RPCError as exc: raise Exception(to_xml(exc.xml)) @ensure_connected - def commit(self, confirmed=False, timeout=None, persist=None): + def commit(self, confirmed=False, timeout=None, persist=None, remove_ns=False): try: - response = self.m.commit(confirmed=confirmed, timeout=timeout, persist=persist) - return remove_namespaces(response) + resp = self.m.commit(confirmed=confirmed, timeout=timeout, persist=persist) + if remove_ns: + response = remove_namespaces(resp) + else: + response = resp.data_xml if hasattr(resp, 'data_xml') else resp.xml + return response except RPCError as exc: raise Exception(to_xml(exc.xml)) @ensure_connected - def validate(self, source="candidate"): + def validate(self, source="candidate", remove_ns=False): try: - response = self.m.validate(source=source) - return remove_namespaces(response) + resp = self.m.validate(source=source) + if remove_ns: + response = remove_namespaces(resp) + else: + response = resp.data_xml if hasattr(resp, 'data_xml') else resp.xml + return response except RPCError as exc: raise Exception(to_xml(exc.xml)) @ensure_connected - def discard_changes(self): + def discard_changes(self, remove_ns=False): try: - response = self.m.discard_changes() - return remove_namespaces(response) + resp = self.m.discard_changes() + if remove_ns: + response = remove_namespaces(resp) + else: + response = resp.data_xml if hasattr(resp, 'data_xml') else resp.xml + return response except RPCError as exc: raise Exception(to_xml(exc.xml))