Repository: ansible/ansible

Issue:
asa_config erroneously completes as OK while the underlying ASA operation has failed
<!---
Please do not report issues/requests related to Ansible modules here !!

Report them to the appropriate modules-core or modules-extras project:
  - https://github.com/ansible/ansible-modules-core/issues
  - https://github.com/ansible/ansible-modules-extras/issues

Also verify first that your issue/request is not already reported in GitHub
-->


##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
Network module asa_config

##### ANSIBLE VERSION
```
ansible 2.2.0.0
  config file = /home/gdanielson/ansible-asa/ansible.cfg
  configured module search path = Default w/o overrides
```

##### CONFIGURATION
```
$ cat /home/gdanielson/ansible-asa/ansible.cfg
[defaults]
inventory = ./inventories/devel/inventory
roles_path = ./.imported_roles:/some/dev/place/with/roles
transport=local
host_key_checking = False
retry_files_enabled = False
gather_subset = network
```
##### OS / ENVIRONMENT
N/A
##### SUMMARY
asa_config does not detect some failures returned by the ASA. Ansible task completes OK even though the intended operation has failed on the device.
The underlying problem is that the ASA does not always return the string `error:` in it's error messages.

##### STEPS TO REPRODUCE
On the ASA CLI attempting to delete an object-group that is referenced in an access-list will result in an error e.g.
```
# show run access-list GDTEST
access-list GDTEST extended permit icmp object-group NG_TESTNET_GROUP_A any

aklads-lab-fw1(config)# no object-group network NG_TESTNET_GROUP_A
Removing object-group (NG_TESTNET_GROUP_A) not allowed, it is being used.
aklads-lab-fw1(config)#
```
Executing the above operation in Ansible does not fail as there is the string `error:` is not returned
```
  tasks:
  - asa_config:
      lines:
        - no object-group network NG_TESTNET_GROUP_A
      provider: "{{ provider }}"
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
The task should fail with something like fatal: failed: true
##### ACTUAL RESULTS
The Ansible task completes without error while the underlying ASA CLI operation fails
```
changed: [aklads-lab-fw1] => {
    "changed": true,
    "invocation": {
        "module_args": {
            "after": null,
            "auth_pass": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "authorize": true,
            "backup": false,
            "before": null,
            "config": null,
            "context": null,
            "defaults": false,
            "host": "10.204.10.90",
            "lines": [
                "no object-group network NG_TESTNET_GROUP_A"
            ],
            "match": "line",
            "parents": null,
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "passwords": false,
            "port": null,
            "provider": {
                "authorize": true,
                "host": "10.204.10.90",
                "username": "admin"
            },
            "replace": "line",
            "save": false,
            "src": null,
            "ssh_keyfile": null,
            "timeout": 10,
            "transport": null,
            "username": "admin"
        }
    },
    "updates": [
        "no object-group network NG_TESTNET_GROUP_A"
    ]
}

PLAY RECAP *********************************************************************
aklads-lab-fw1             : ok=1    changed=1    unreachable=0    failed=0
```



Discussion:
Task fails as expected with modified CLI_ERRORS_RE
```
lib/ansible/module_utils/asa.py
@@ -47,6 +47,7 @@ class Cli(CliBase):

     CLI_ERRORS_RE = [
         re.compile(r"error:", re.I),
+        re.compile(r"not allowed", re.I),
     ]

     NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I)
```

```
fatal: [aklads-lab-fw1]: FAILED! => {
    "changed": false,
    "commands": [
        "configure terminal",
        "no object-group network NG_TESTNET_GROUP_A",
        "end"
    ],
    "failed": true,
    "invocation": {
        "module_args": {
            "after": null,
            "auth_pass": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "authorize": true,
            "backup": false,
            "before": null,
            "config": null,
            "context": null,
            "defaults": false,
            "host": "10.204.10.90",
            "lines": [
                "no object-group network NG_TESTNET_GROUP_A"
            ],
            "match": "line",
            "parents": null,
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "passwords": false,
            "port": null,
            "provider": {
                "authorize": true,
                "host": "10.204.10.90",
                "username": "admin"
            },
            "replace": "line",
            "save": false,
            "src": null,
            "ssh_keyfile": null,
            "timeout": 10,
            "transport": null,
            "username": "admin"
        }
    },
    "msg": "matched error in response: no object-group network NG_TESTNET_GROUP_A\r\nRemoving object-group (NG_TESTNET_GROUP_A) not allowed, it is being used.\r\n\raklads-lab-fw1(config)# "
}

PLAY RECAP ***********************************************************************************************************
aklads-lab-fw1             : ok=0    changed=0    unreachable=0    failed=1
```


Resolving patch:
diff --git a/lib/ansible/module_utils/asa.py b/lib/ansible/module_utils/asa.py
--- a/lib/ansible/module_utils/asa.py
+++ b/lib/ansible/module_utils/asa.py
@@ -47,6 +47,7 @@ class Cli(CliBase):
 
     CLI_ERRORS_RE = [
         re.compile(r"error:", re.I),
+        re.compile(r"^Removing.* not allowed")
     ]
 
     NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I)


Test patch:
diff --git a/test/integration/targets/asa_config/tests/cli/removal_error.yaml b/test/integration/targets/asa_config/tests/cli/removal_error.yaml
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/asa_config/tests/cli/removal_error.yaml
@@ -0,0 +1,46 @@
+---
+- debug: msg="START cli/removal_error.yaml"
+
+- name: setup
+  asa_config:
+    commands:
+      - clear configure access-list ANSIBLE-DNS
+      - no object-group network OGA-GOOGLE-DNS
+    provider: "{{ cli }}"
+  ignore_errors: yes
+
+- name: configure test object-group
+  asa_config:
+    parents: object-group network OGA-GOOGLE-DNS
+    lines: network-object host 8.8.8.8
+    provider: "{{ cli }}"
+  register: result
+
+
+- name: configure test access-list
+  asa_config:
+    lines: access-list ANSIBLE-DNS extended permit udp any object-group OGA-GOOGLE-DNS eq domain
+    provider: "{{ cli }}"
+
+- name: try to remove object-group (should fail)
+  asa_config:
+    commands:
+      - no object-group network OGA-GOOGLE-DNS
+    provider: "{{ cli }}"
+  ignore_errors: yes
+  register: result
+
+
+- name: Last command should fail
+  assert:
+    that:
+      - "result.failed == true"
+
+- name: teardown
+  asa_config:
+    commands:
+      - clear configure access-list ANSIBLE-DNS
+      - no object-group network OGA-GOOGLE-DNS
+    provider: "{{ cli }}"
+
+- debug: msg="END cli/removal_error.yaml"