diff --git a/changelogs/fragments/cli_config_junos_replace_fix.yaml b/changelogs/fragments/cli_config_junos_replace_fix.yaml new file mode 100644 index 00000000000..aa5ca8ad19d --- /dev/null +++ b/changelogs/fragments/cli_config_junos_replace_fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - Fix for junos cli_config replace option (https://github.com/ansible/ansible/pull/62131). diff --git a/lib/ansible/modules/network/cli/cli_config.py b/lib/ansible/modules/network/cli/cli_config.py index 0e893bf4766..cd671f156b2 100644 --- a/lib/ansible/modules/network/cli/cli_config.py +++ b/lib/ansible/modules/network/cli/cli_config.py @@ -40,10 +40,13 @@ options: description: - If the C(replace) argument is set to C(yes), it will replace the entire running-config of the device with the C(config) - argument value. For NXOS devices, C(replace) argument takes - path to the file on the device that will be used for replacing - the entire running-config. Nexus 9K devices only support replace. - Use I(net_put) or I(nxos_file_copy) module to copy the flat file + argument value. For devices that support replacing running + configuration from file on device like NXOS/JUNOS, the + C(replace) argument takes path to the file on the device + that will be used for replacing the entire running-config. + The value of C(config) option should be I(None) for such devices. + Nexus 9K devices only support replace. Use I(net_put) or + I(nxos_file_copy) in case of NXOS module to copy the flat file to remote device and then use set the fullpath to this argument. type: 'str' backup: @@ -168,6 +171,10 @@ EXAMPLES = """ cli_config: replace: 'bootflash:nxoscfg' +- name: junos replace config + cli_config: + replace: '/var/home/ansible/junos01.cfg' + - name: commit with comment cli_config: config: set system host-name foo @@ -242,6 +249,11 @@ def run(module, device_operations, connection, candidate, running, rollback_id): elif replace in ('no', 'false', 'False'): replace = False + if replace is not None and replace not in [True, False] and candidate is not None: + module.fail_json(msg="Replace value '%s' is a configuration file path already" + " present on the device. Hence 'replace' and 'config' options" + " are mutually exclusive" % replace) + if rollback_id is not None: resp = connection.rollback(rollback_id, commit) if 'diff' in resp: @@ -255,7 +267,7 @@ def run(module, device_operations, connection, candidate, running, rollback_id): if diff_ignore_lines: module.warn('diff_ignore_lines is ignored as the device supports onbox diff') - if not isinstance(candidate, list): + if candidate and not isinstance(candidate, list): candidate = candidate.strip('\n').splitlines() kwargs = {'candidate': candidate, 'commit': commit, 'replace': replace, @@ -375,7 +387,7 @@ def main(): if module.params['backup']: result['__backup__'] = running - if candidate or rollback_id: + if candidate or rollback_id or module.params['replace']: try: result.update(run(module, device_operations, connection, candidate, running, rollback_id)) except Exception as exc: diff --git a/lib/ansible/plugins/cliconf/junos.py b/lib/ansible/plugins/cliconf/junos.py index c800539a722..35d264f99a0 100644 --- a/lib/ansible/plugins/cliconf/junos.py +++ b/lib/ansible/plugins/cliconf/junos.py @@ -109,7 +109,7 @@ class Cliconf(CliconfBase): requests = [] if replace: - candidate = 'load replace {0}'.format(replace) + candidate = 'load override {0}'.format(replace) for line in to_list(candidate): if not isinstance(line, Mapping): @@ -133,8 +133,8 @@ class Cliconf(CliconfBase): self.discard_changes() else: - for cmd in ['top', 'exit']: - self.send_command(cmd) + self.send_command('top') + self.discard_changes() resp['request'] = requests resp['response'] = results @@ -193,7 +193,7 @@ class Cliconf(CliconfBase): resp = self.send_command(command) r = resp.splitlines() - if len(r) == 1 and '[edit]' in r[0]: + if len(r) == 1 and '[edit]' in r[0] or len(r) == 4 and r[1].startswith('- version'): resp = '' return resp diff --git a/test/integration/targets/junos_config/tests/cli_config/cli_replace.yaml b/test/integration/targets/junos_config/tests/cli_config/cli_replace.yaml new file mode 100644 index 00000000000..d3958046505 --- /dev/null +++ b/test/integration/targets/junos_config/tests/cli_config/cli_replace.yaml @@ -0,0 +1,60 @@ +--- +- debug: msg="START cli_config/cli_replace.yaml on connection={{ ansible_connection }}" + +- name: set interface config + cli_config: + config: "{{ item }}" + loop: + - "delete interfaces ge-0/0/11" + - set interfaces ge-0/0/11 description "test cli_config" + +- name: get running configuration + cli_command: + command: show configuration + register: result + +- name: copy configuration to file + copy: + content: "{{ result['stdout'] }}" + dest: /tmp/junos01.cfg + +- name: "modify interface ge-0/0/11 configuration" + replace: + path: /tmp/junos01.cfg + regexp: 'test cli_config' + replace: 'test cli_config replaced' + +- name: copy config file to remote host + net_put: + src: /tmp/junos01.cfg + dest: /var/home/{{ ansible_user }}/junos01.cfg + +- name: replace syslog test file configuration + cli_config: + replace: "/var/home/{{ ansible_user }}/junos01.cfg" + +- name: get interface configuration + cli_command: + command: show configuration interfaces ge-0/0/11 + register: result + +- name: assert that interface config change is reflected on device + assert: + that: + - "'test cli_config replaced' in result.stdout" + +- name: replace interface configuration (idempotent) + cli_config: + replace: "/var/home/{{ ansible_user }}/junos01.cfg" + register: result + +- name: Assert that the previous task was idempotent + assert: + that: + - "result['changed'] == false" + +- name: delete interface config + cli_config: + config: "delete interfaces ge-0/0/11" + +- debug: msg="END cli_config/cli_replace.yaml on connection={{ ansible_connection }}"