diff --git a/changelogs/fragments/iosxr_config_crash.yaml b/changelogs/fragments/iosxr_config_crash.yaml new file mode 100644 index 0000000000..6485ca871b --- /dev/null +++ b/changelogs/fragments/iosxr_config_crash.yaml @@ -0,0 +1,2 @@ +bugfixes: +- iosxr_config - handle configuration block with mis-indented sublevel command diff --git a/lib/ansible/modules/network/iosxr/iosxr_config.py b/lib/ansible/modules/network/iosxr/iosxr_config.py index 21d511a0b2..4a64c48abf 100644 --- a/lib/ansible/modules/network/iosxr/iosxr_config.py +++ b/lib/ansible/modules/network/iosxr/iosxr_config.py @@ -193,6 +193,8 @@ backup_path: type: string sample: /playbooks/ansible/backup/iosxr01.2016-07-16@22:28:34 """ +import re + from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.network.iosxr.iosxr import load_config, get_config from ansible.module_utils.network.iosxr.iosxr import iosxr_argument_spec, copy_file @@ -200,6 +202,10 @@ from ansible.module_utils.network.common.config import NetworkConfig, dumps DEFAULT_COMMIT_COMMENT = 'configured by iosxr_config' +CONFIG_MISPLACED_CHILDREN = [ + re.compile(r'end-\s*(.+)$') +] + def copy_file_to_node(module): """ Copy config file to IOS-XR node. We use SFTP because older IOS-XR versions don't handle SCP very well. @@ -242,6 +248,29 @@ def get_candidate(module): return candidate +def sanitize_candidate_config(config): + last_parents = None + for regex in CONFIG_MISPLACED_CHILDREN: + for index, line in enumerate(config): + if line._parents: + last_parents = line._parents + m = regex.search(line.text) + if m and m.group(0): + config[index]._parents = last_parents + + +def sanitize_running_config(config): + last_parents = None + for regex in CONFIG_MISPLACED_CHILDREN: + for index, line in enumerate(config): + if line._parents: + last_parents = line._parents + m = regex.search(line.text) + if m and m.group(0): + config[index].text = ' ' + m.group(0) + config[index]._parents = last_parents + + def run(module, result): match = module.params['match'] replace = module.params['replace'] @@ -254,6 +283,9 @@ def run(module, result): candidate_config = get_candidate(module) running_config = get_running_config(module) + sanitize_candidate_config(candidate_config.items) + sanitize_running_config(running_config.items) + commands = None if match != 'none' and replace != 'config': commands = candidate_config.difference(running_config, path=path, match=match, replace=replace) diff --git a/test/integration/targets/iosxr_config/templates/basic/change_prefix_set.j2 b/test/integration/targets/iosxr_config/templates/basic/change_prefix_set.j2 new file mode 100644 index 0000000000..572d15bf6d --- /dev/null +++ b/test/integration/targets/iosxr_config/templates/basic/change_prefix_set.j2 @@ -0,0 +1,7 @@ +prefix-set ebpg_filter + 192.168.0.0/16 ge 15 le 30 +end-set + +interface Loopback999 + description this is a test interface for prefix-set + diff --git a/test/integration/targets/iosxr_config/templates/basic/init_prefix_set.j2 b/test/integration/targets/iosxr_config/templates/basic/init_prefix_set.j2 new file mode 100644 index 0000000000..d1170b02de --- /dev/null +++ b/test/integration/targets/iosxr_config/templates/basic/init_prefix_set.j2 @@ -0,0 +1,3 @@ +prefix-set ebpg_filter + 192.168.0.0/16 ge 17 le 30 +end-set diff --git a/test/integration/targets/iosxr_config/tests/cli/misplaced_sublevel.yaml b/test/integration/targets/iosxr_config/tests/cli/misplaced_sublevel.yaml new file mode 100644 index 0000000000..3688cfa455 --- /dev/null +++ b/test/integration/targets/iosxr_config/tests/cli/misplaced_sublevel.yaml @@ -0,0 +1,27 @@ +--- +- debug: msg="START cli/misplaced_sublevel.yaml on connection={{ ansible_connection }}" + +- name: setup + iosxr_config: + src: basic/init_prefix_set.j2 + ignore_errors: yes + +- name: Change prefix-set and new command after prefix-set + iosxr_config: + src: basic/change_prefix_set.j2 + register: result + +- assert: + that: + - "result.changed == true" + +- name: Play same config again to verify no diff in prefix-set also works + iosxr_config: + src: basic/change_prefix_set.j2 + register: result + +- assert: + that: + - "result.changed == true" + +- debug: msg="END cli/misplaced_sublevel.yaml on connection={{ ansible_connection }}"