diff --git a/lib/ansible/modules/network/netconf/netconf_config.py b/lib/ansible/modules/network/netconf/netconf_config.py
index b09d128f312..00712772992 100644
--- a/lib/ansible/modules/network/netconf/netconf_config.py
+++ b/lib/ansible/modules/network/netconf/netconf_config.py
@@ -169,6 +169,15 @@ options:
type: path
type: dict
version_added: "2.8"
+ get_filter:
+ description:
+ - This argument specifies the XML string which acts as a filter to restrict the portions of
+ the data retrieved from the remote device when comparing the before and after state of the
+ device following calls to edit_config. When not specified, the entire configuration or
+ state data is returned for comparison depending on the value of C(source) option. The C(get_filter)
+ value can be either XML string or XPath, if the filter is in XPath format the NETCONF server
+ running on remote host should support xpath capability else it will result in an error.
+ version_added: "2.10"
requirements:
- "ncclient"
notes:
@@ -254,10 +263,26 @@ from ansible.module_utils.basic import AnsibleModule, env_fallback
from ansible.module_utils.connection import Connection, ConnectionError
from ansible.module_utils.network.netconf.netconf import get_capabilities, get_config, sanitize_xml
+import sys
try:
- from lxml.etree import tostring
+ from lxml.etree import tostring, fromstring, XMLSyntaxError
except ImportError:
- from xml.etree.ElementTree import tostring
+ from xml.etree.ElementTree import tostring, fromstring
+ if sys.version_info < (2, 7):
+ from xml.parsers.expat import ExpatError as XMLSyntaxError
+ else:
+ from xml.etree.ElementTree import ParseError as XMLSyntaxError
+
+
+def get_filter_type(filter):
+ if not filter:
+ return None
+ else:
+ try:
+ fromstring(filter)
+ return 'subtree'
+ except XMLSyntaxError:
+ return 'xpath'
def main():
@@ -283,6 +308,7 @@ def main():
delete=dict(type='bool', default=False),
commit=dict(type='bool', default=True),
validate=dict(type='bool', default=False),
+ get_filter=dict(),
)
# deprecated options
@@ -320,6 +346,8 @@ def main():
confirm = module.params['confirm']
validate = module.params['validate']
save = module.params['save']
+ filter = module.params['get_filter']
+ filter_type = get_filter_type(filter)
conn = Connection(module._socket_path)
capabilities = get_capabilities(module)
@@ -355,6 +383,11 @@ def main():
if validate and not operations.get('supports_validate', False):
module.fail_json(msg='validate is not supported by this netconf server')
+ if filter_type == 'xpath' and not operations.get('supports_xpath', False):
+ module.fail_json(msg="filter value '%s' of type xpath is not supported on this device" % filter)
+
+ filter_spec = (filter_type, filter) if filter_type else None
+
if lock == 'never':
execute_lock = False
elif target in operations.get('lock_datastore', []):
@@ -371,7 +404,7 @@ def main():
locked = False
try:
if module.params['backup']:
- response = get_config(module, target, lock=execute_lock)
+ response = get_config(module, target, filter_spec, lock=execute_lock)
before = to_text(tostring(response), errors='surrogate_then_replace').strip()
result['__backup__'] = before.strip()
if validate:
@@ -398,7 +431,7 @@ def main():
conn.lock(target=target)
locked = True
if before is None:
- before = to_text(conn.get_config(source=target), errors='surrogate_then_replace').strip()
+ before = to_text(conn.get_config(source=target, filter=filter_spec), errors='surrogate_then_replace').strip()
kwargs = {
'config': config,
@@ -411,7 +444,7 @@ def main():
conn.edit_config(**kwargs)
if supports_commit and module.params['commit']:
- after = to_text(conn.get_config(source='candidate'), errors='surrogate_then_replace').strip()
+ after = to_text(conn.get_config(source='candidate', filter=filter_spec), errors='surrogate_then_replace').strip()
if not module.check_mode:
confirm_timeout = confirm if confirm > 0 else None
confirmed_commit = True if confirm_timeout else False
@@ -420,7 +453,7 @@ def main():
conn.discard_changes()
if after is None:
- after = to_text(conn.get_config(source='running'), errors='surrogate_then_replace').strip()
+ after = to_text(conn.get_config(source='running', filter=filter_spec), errors='surrogate_then_replace').strip()
sanitized_before = sanitize_xml(before)
sanitized_after = sanitize_xml(after)
diff --git a/test/integration/targets/nxos_netconf/defaults/main.yaml b/test/integration/targets/nxos_netconf/defaults/main.yaml
new file mode 100644
index 00000000000..9ef5ba51651
--- /dev/null
+++ b/test/integration/targets/nxos_netconf/defaults/main.yaml
@@ -0,0 +1,3 @@
+---
+testcase: "*"
+test_items: []
diff --git a/test/integration/targets/nxos_netconf/meta/main.yaml b/test/integration/targets/nxos_netconf/meta/main.yaml
new file mode 100644
index 00000000000..6a8fed7644e
--- /dev/null
+++ b/test/integration/targets/nxos_netconf/meta/main.yaml
@@ -0,0 +1,3 @@
+dependencies:
+ # Not needed for this test
+ # - prepare_nxos_tests
diff --git a/test/integration/targets/nxos_netconf/tasks/main.yaml b/test/integration/targets/nxos_netconf/tasks/main.yaml
new file mode 100644
index 00000000000..07e872c9458
--- /dev/null
+++ b/test/integration/targets/nxos_netconf/tasks/main.yaml
@@ -0,0 +1,41 @@
+---
+- name: Setup - Enable feature netconf
+ nxos_feature:
+ feature: netconf
+ state: enabled
+ vars: &ssh_credentials
+ ansible_connection: network_cli
+ ansible_ssh_port: 22
+ register: result
+ ignore_errors: yes
+
+- debug: msg='Netconf feature is not supported on this platform!'
+ when: result.failed
+
+- name: Setup - Remove Vlan
+ nxos_config:
+ lines:
+ - no vlan 42
+ ignore_errors: yes
+ when: not result.failed
+
+- block:
+ - name: Run netconf tests
+ include: netconf.yaml
+ when: not result.failed
+
+ always:
+ - name: Disable feature netconf
+ nxos_feature:
+ feature: netconf
+ state: disabled
+ vars: *ssh_credentials
+ when: not result.failed
+
+ - name: Cleanup - Remove vlan
+ nxos_config:
+ lines:
+ - no vlan 42
+ vars: *ssh_credentials
+ ignore_errors: yes
+ when: not result.failed
diff --git a/test/integration/targets/nxos_netconf/tasks/netconf.yaml b/test/integration/targets/nxos_netconf/tasks/netconf.yaml
new file mode 100644
index 00000000000..d8d042114f4
--- /dev/null
+++ b/test/integration/targets/nxos_netconf/tasks/netconf.yaml
@@ -0,0 +1,16 @@
+---
+- name: collect all cli test cases
+ find:
+ paths: "{{ role_path }}/tests/netconf"
+ patterns: "{{ testcase }}.yaml"
+ register: test_cases
+ delegate_to: localhost
+
+- name: set test_items
+ set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
+
+- name: run test case (connection=netconf)
+ include: "{{ test_case_to_run }}"
+ with_items: "{{ test_items }}"
+ loop_control:
+ loop_var: test_case_to_run
diff --git a/test/integration/targets/nxos_netconf/tests/fixtures/config.yaml b/test/integration/targets/nxos_netconf/tests/fixtures/config.yaml
new file mode 100644
index 00000000000..de3c3402633
--- /dev/null
+++ b/test/integration/targets/nxos_netconf/tests/fixtures/config.yaml
@@ -0,0 +1,14 @@
+---
+vlan_config: |
+
+
+
+
+
+ vlan-42
+ vlan-42
+
+
+
+
+
diff --git a/test/integration/targets/nxos_netconf/tests/netconf/basic.yaml b/test/integration/targets/nxos_netconf/tests/netconf/basic.yaml
new file mode 100644
index 00000000000..b9d79ab9af2
--- /dev/null
+++ b/test/integration/targets/nxos_netconf/tests/netconf/basic.yaml
@@ -0,0 +1,44 @@
+---
+- debug: msg="START nxos_netconf cli/basic.yaml"
+
+- include_vars: "{{playbook_dir }}/targets/nxos_netconf/tests/fixtures/config.yaml"
+
+- debug: msg=" {{ playbook_dir }}"
+
+- block:
+ - name: Configure vlan
+ netconf_config: &config_vlan
+ datastore: running
+ commit: false
+ get_filter:
+ content: "{{ vlan_config }}"
+ register: result
+
+ - assert: &true
+ that:
+ - "result.changed == true"
+
+ - name: Configure vlan - idempotence check
+ netconf_config: *config_vlan
+ register: result
+
+ - assert: &false
+ that:
+ - "result.changed == false"
+
+ - name: Query Running Config
+ netconf_get:
+ source: running
+ filter:
+ register: result
+
+ - assert:
+ that:
+ - "'vlan-42' in result.stdout"
+
+ vars:
+ ansible_connection: netconf
+ ansible_port: 830
+
+ always:
+ - debug: msg="END nxos_netconf cli/basic.yaml"