Repository: ansible/ansible

Issue:
Vyos_Config Module Not Saving/Commiting
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report

##### COMPONENT NAME
vyos_config

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
$ ansible --version
ansible 2.3.0.0
  config file = /Users/user/Documents/Infrastructure_Repo/ansible.cfg
  configured module search path = Default w/o overrides
  python version = 2.7.13 (default, Dec 18 2016, 07:03:39) [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)]

```

##### CONFIGURATION
<!---
Mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->

##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say “N/A” for anything that is not platform-specific.
-->
Ansible Control Node: Mac OSX
Ansible Target: VyOs
##### SUMMARY
There is an issue saving the commands with the vyos_config module. While we can successfully run commands via this module, we cannot save them using the vyos_config module. When you run the commands or lines as they're called in the module without the save argument you'll see successful output (green). These changes are actually saved and therefore is merely just a check.

When you implement the change arg to save: true or save: yes in the vyos_config module we run into errors saving the configuration. The way I understand vyatta configuration mode is that you need to run enter config mode -> run commands -> commit work -> save work -> exit config mode.

In this way I believe the vyos_config module implies we are in config mode then handles the commands. However we are missing the function to commit the work (perhaps this is implied). The save work function fails per the aforementioned issue. I suspect the exit config function is implied when the vyos_config module terminates.


##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
- name: Configure system time-zone
  vyos_config:
    provider: "{{cli}}"
    lines:
      - set system time-zone 'America/Chicago'
    save: true
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
The `set` command would be saved successfully.

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```
fatal: [mgmt-702fw]: FAILED! => {
    "changed": false, 
    "failed": true, 
    "invocation": {
        "module_args": {
            "backup": false, 
            "comment": "configured by vyos_config", 
            "config": null, 
            "host": null, 
            "lines": [
                "set system time-zone 'America/Chicago'"
            ], 
            "match": "line", 
            "password": null, 
            "port": null, 
            "provider": {
                "host": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
                "password": null, 
                "port": null, 
                "ssh_keyfile": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
                "timeout": null, 
                "transport": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
                "username": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
            }, 
            "save": true, 
            "src": null, 
            "ssh_keyfile": null, 
            "timeout": null, 
            "username": null
        }
    }, 
    "msg": "timeout trying to send command: save", 
    "rc": 1
}
```



Discussion:
cc @Qalthos
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/modules/network/vyos/vyos_config.py b/lib/ansible/modules/network/vyos/vyos_config.py
--- a/lib/ansible/modules/network/vyos/vyos_config.py
+++ b/lib/ansible/modules/network/vyos/vyos_config.py
@@ -269,9 +269,11 @@ def main():
         run(module, result)
 
     if module.params['save']:
-        if not module.check_mode:
-            run_commands(module, ['save'])
-        result['changed'] = True
+        diff = run_commands(module, commands=['configure', 'compare saved'])[1]
+        if diff != '[edit]':
+            run_commands(module, commands=['save'])
+            result['changed'] = True
+        run_commands(module, commands=['exit'])
 
     module.exit_json(**result)
 


Test patch:
diff --git a/test/integration/targets/vyos_config/tests/cli/save.yaml b/test/integration/targets/vyos_config/tests/cli/save.yaml
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/vyos_config/tests/cli/save.yaml
@@ -0,0 +1,60 @@
+---
+- debug: msg="START cli/save.yaml"
+
+- name: setup
+  vyos_config:
+    lines: set system host-name {{ inventory_hostname_short }}
+    provider: "{{ cli }}"
+    match: none
+
+- name: configure hostaname and save
+  vyos_config:
+    lines: set system host-name foo
+    provider: "{{ cli }}"
+    save: true
+  register: result
+
+- assert:
+    that:
+      - "result.changed == true"
+      - "'set system host-name foo' in result.commands"
+
+- name: configure hostaname and don't save
+  vyos_config:
+    lines: set system host-name bar
+    provider: "{{ cli }}"
+  register: result
+
+- assert:
+    that:
+      - "result.changed == true"
+      - "'set system host-name bar' in result.commands"
+
+- name: save config
+  vyos_config:
+    save: true
+    provider: "{{ cli }}"
+  register: result
+
+- assert:
+    that:
+      - "result.changed == true"
+
+- name: save config again
+  vyos_config:
+    save: true
+    provider: "{{ cli }}"
+  register: result
+
+- assert:
+    that:
+      - "result.changed == false"
+
+- name: teardown
+  vyos_config:
+    lines: set system host-name {{ inventory_hostname_short }}
+    provider: "{{ cli }}"
+    match: none
+    save: true
+
+- debug: msg="END cli/simple.yaml"
diff --git a/test/units/modules/network/vyos/test_vyos_config.py b/test/units/modules/network/vyos/test_vyos_config.py
--- a/test/units/modules/network/vyos/test_vyos_config.py
+++ b/test/units/modules/network/vyos/test_vyos_config.py
@@ -73,15 +73,6 @@ def test_vyos_config_backup(self):
         result = self.execute_module()
         self.assertIn('__backup__', result)
 
-    def test_vyos_config_save(self):
-        set_module_args(dict(save=True))
-        self.execute_module(changed=True)
-        self.assertEqual(self.run_commands.call_count, 1)
-        self.assertEqual(self.get_config.call_count, 0)
-        self.assertEqual(self.load_config.call_count, 0)
-        args = self.run_commands.call_args[0][1]
-        self.assertIn('save', args)
-
     def test_vyos_config_lines(self):
         commands = ['set system host-name foo']
         set_module_args(dict(lines=commands))