Repository: ansible/ansible

Issue:
nxos_acl module errors when transport == nxapi
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
 - nxos_acl

##### NXOS VERSION
7.0(3)I3(1)

##### ANSIBLE VERSION
```
ansible-playbook 2.4.0 (devel 781bb44b02) last updated 2017/06/08 15:20:16 (GMT -400)
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/usr/share/ansible']
  ansible python module location = /root/agents-ci/ansible/lib/ansible
  executable location = /root/agents-ci/ansible/bin/ansible-playbook
  python version = 2.7.6 (default, Oct 26 2016, 20:30:19) [GCC 4.8.4]
```

##### OS / ENVIRONMENT
running on Ubuntu 14.04

##### SUMMARY
The module errors out in latest `devel` when using transport `nxapi`.  The issue is not seen when transport is `cli`.

##### STEPS TO REPRODUCE
```yaml
---
- debug: msg="START TRANSPORT:NXAPI nxos_acl sanity test"

- name: "TRANSPORT:NXAPI nxos_acl sanity test"
  nxos_acl:
    name: TEST_ACL
    seq: 10
    action: permit
    proto: tcp
    src: 1.1.1.1/24
    dest: any
    state: present
    provider: "{{ nxapi }}"

- debug: msg="END TRANSPORT:NXAPI nxos_acl sanity test"
```

##### EXPECTED RESULTS
Expect acl to be configured properly.

##### ACTUAL RESULTS

```shell
TASK [nxos_acl : TRANSPORT:NXAPI nxos_acl sanity test] ****************************************************************************************************************************************************************************************
task path: /root/agents-ci/ansible/test/integration/targets/nxos_acl/tests/nxapi/sanity.yaml:4
Using module_utils file /root/agents-ci/ansible/lib/ansible/module_utils/basic.py
Using module_utils file /root/agents-ci/ansible/lib/ansible/module_utils/nxos.py
Using module_utils file /root/agents-ci/ansible/lib/ansible/module_utils/_text.py
Using module_utils file /root/agents-ci/ansible/lib/ansible/module_utils/pycompat24.py
Using module_utils file /root/agents-ci/ansible/lib/ansible/module_utils/six/__init__.py
Using module_utils file /root/agents-ci/ansible/lib/ansible/module_utils/six/_six.py
Using module_utils file /root/agents-ci/ansible/lib/ansible/module_utils/connection.py
Using module_utils file /root/agents-ci/ansible/lib/ansible/module_utils/network_common.py
Using module_utils file /root/agents-ci/ansible/lib/ansible/module_utils/urls.py
Using module file /root/agents-ci/ansible/lib/ansible/modules/network/nxos/nxos_acl.py
<n9k> ESTABLISH LOCAL CONNECTION FOR USER: root
<n9k> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1496950223.18-178095914827306 `" && echo ansible-tmp-1496950223.18-178095914827306="` echo $HOME/.ansible/tmp/ansible-tmp-1496950223.18-178095914827306 `" ) && sleep 0'
<n9k> PUT /tmp/tmpPifU3a TO /root/.ansible/tmp/ansible-tmp-1496950223.18-178095914827306/nxos_acl.py
<n9k> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1496950223.18-178095914827306/ /root/.ansible/tmp/ansible-tmp-1496950223.18-178095914827306/nxos_acl.py && sleep 0'
<n9k> EXEC /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1496950223.18-178095914827306/nxos_acl.py; rm -rf "/root/.ansible/tmp/ansible-tmp-1496950223.18-178095914827306/" > /dev/null 2>&1 && sleep 0'
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_mOyuQJ/ansible_module_nxos_acl.py", line 560, in <module>
    main()
  File "/tmp/ansible_mOyuQJ/ansible_module_nxos_acl.py", line 501, in main
    existing_core, acl = get_acl(module, name, seq)
  File "/tmp/ansible_mOyuQJ/ansible_module_nxos_acl.py", line 244, in get_acl
    all_acl_body = body['TABLE_ip_ipv6_mac']['ROW_ip_ipv6_mac']
TypeError: string indices must be integers

fatal: [n9k]: FAILED! => {
    "changed": false, 
    "failed": true, 
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_mOyuQJ/ansible_module_nxos_acl.py\", line 560, in <module>\n    main()\n  File \"/tmp/ansible_mOyuQJ/ansible_module_nxos_acl.py\", line 501, in main\n    existing_core, acl = get_acl(module, name, seq)\n  File \"/tmp/ansible_mOyuQJ/ansible_module_nxos_acl.py\", line 244, in get_acl\n    all_acl_body = body['TABLE_ip_ipv6_mac']['ROW_ip_ipv6_mac']\nTypeError: string indices must be integers\n", 
    "module_stdout": "", 
    "msg": "MODULE FAILURE", 
    "rc": 0
}
	to retry, use: --limit @/root/agents-ci/ansible/test/integration/nxos.retry

PLAY RECAP ************************************************************************************************************************************************************************************************************************************
n9k          : ok=6    changed=0    unreachable=0    failed=1   

```


Discussion:
cc @jedelman8 @GGabriele @rahushen @privateip @rcarrillocruz @trishnaguha
[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/nxos/nxos_acl.py b/lib/ansible/modules/network/nxos/nxos_acl.py
--- a/lib/ansible/modules/network/nxos/nxos_acl.py
+++ b/lib/ansible/modules/network/nxos/nxos_acl.py
@@ -227,8 +227,7 @@
 
 
 def execute_show_command(command, module):
-    if module.params['transport'] == 'cli':
-        command += ' | json'
+    command += ' | json'
     cmds = [command]
     body = run_commands(module, cmds)
     return body


Test patch:
diff --git a/test/integration/nxos.yaml b/test/integration/nxos.yaml
--- a/test/integration/nxos.yaml
+++ b/test/integration/nxos.yaml
@@ -20,3 +20,4 @@
     - { role: nxos_interface, when: "limit_to in ['*', 'nxos_interface']" }
     - { role: nxos_user, when: "limit_to in ['*', 'nxos_user']" }
     - { role: nxos_banner, when: "limit_to in ['*', 'nxos_banner']" }
+    - { role: nxos_acl, when: "limit_to in ['*', 'nxos_acl']" }
diff --git a/test/integration/targets/nxos_acl/defaults/main.yaml b/test/integration/targets/nxos_acl/defaults/main.yaml
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/nxos_acl/defaults/main.yaml
@@ -0,0 +1,2 @@
+---
+testcase: "*"
diff --git a/test/integration/targets/nxos_acl/meta/main.yml b/test/integration/targets/nxos_acl/meta/main.yml
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/nxos_acl/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+  - prepare_nxos_tests
diff --git a/test/integration/targets/nxos_acl/tasks/cli.yaml b/test/integration/targets/nxos_acl/tasks/cli.yaml
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/nxos_acl/tasks/cli.yaml
@@ -0,0 +1,15 @@
+---
+- name: collect all cli test cases
+  find:
+    paths: "{{ role_path }}/tests/cli"
+    patterns: "{{ testcase }}.yaml"
+  register: test_cases
+
+- name: set test_items
+  set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
+
+- name: run test case
+  include: "{{ test_case_to_run }}"
+  with_items: "{{ test_items }}"
+  loop_control:
+    loop_var: test_case_to_run
diff --git a/test/integration/targets/nxos_acl/tasks/main.yaml b/test/integration/targets/nxos_acl/tasks/main.yaml
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/nxos_acl/tasks/main.yaml
@@ -0,0 +1,3 @@
+---
+- { include: cli.yaml, tags: ['cli'] }
+- { include: nxapi.yaml, tags: ['nxapi'] }
diff --git a/test/integration/targets/nxos_acl/tasks/nxapi.yaml b/test/integration/targets/nxos_acl/tasks/nxapi.yaml
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/nxos_acl/tasks/nxapi.yaml
@@ -0,0 +1,28 @@
+---
+- name: collect all nxapi test cases
+  find:
+    paths: "{{ role_path }}/tests/nxapi"
+    patterns: "{{ testcase }}.yaml"
+  register: test_cases
+
+- name: set test_items
+  set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
+
+- name: enable nxapi
+  nxos_config:
+    lines:
+      - feature nxapi
+      - nxapi http port 80
+    provider: "{{ cli }}"
+
+- name: run test case
+  include: "{{ test_case_to_run }}"
+  with_items: "{{ test_items }}"
+  loop_control:
+    loop_var: test_case_to_run
+
+- name: disable nxapi
+  nxos_config:
+    lines:
+      - no feature nxapi
+    provider: "{{ cli }}"
diff --git a/test/integration/targets/nxos_acl/tests/cli/sanity.yaml b/test/integration/targets/nxos_acl/tests/cli/sanity.yaml
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/nxos_acl/tests/cli/sanity.yaml
@@ -0,0 +1,48 @@
+---
+- debug: msg="START TRANSPORT:CLI nxos_acl sanity test"
+
+- name: "Setup: Cleanup possibly existing acl."
+  nxos_acl: &remove
+    name: TEST_ACL
+    seq: 10
+    state: absent
+    provider: "{{ cli }}"
+  ignore_errors: yes
+
+- name: "Configure ACL"
+  nxos_acl: &configure
+    name: TEST_ACL
+    seq: 10
+    action: permit
+    proto: tcp
+    src: 1.1.1.1/24
+    dest: any
+    state: present
+    provider: "{{ cli }}"
+  register: result
+
+- assert: &true
+    that:
+      - "result.changed == true"
+
+- name: "Check Idempotence"
+  nxos_acl: *configure
+  register: result
+
+- assert: &false
+    that:
+      - "result.changed == false"
+
+- name: "Remove ACL"
+  nxos_acl: *remove
+  register: result
+
+- assert: *true
+
+- name: "Check Idempotence"
+  nxos_acl: *remove
+  register: result
+
+- assert: *false
+
+- debug: msg="END TRANSPORT:CLI nxos_acl sanity test"
diff --git a/test/integration/targets/nxos_acl/tests/nxapi/sanity.yaml b/test/integration/targets/nxos_acl/tests/nxapi/sanity.yaml
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/nxos_acl/tests/nxapi/sanity.yaml
@@ -0,0 +1,61 @@
+---
+- debug: msg="START TRANSPORT:NXAPI nxos_acl sanity test"
+
+- name: "Setup: Cleanup possibly existing acl."
+  nxos_acl: &remove
+    name: TEST_ACL
+    seq: 10
+    state: absent
+    provider: "{{ nxapi }}"
+  ignore_errors: yes
+
+- name: "Configure ACL"
+  nxos_acl: &configure
+    name: TEST_ACL
+    seq: 10
+    action: permit
+    proto: tcp
+    src: 1.1.1.1/24
+    src_port_op: range
+    src_port1: 5
+    src_port2: 20
+    ack: 'enable'
+    dscp: 'af43'
+    dest: any
+    urg: 'enable'
+    psh: 'enable'
+    established: 'enable'
+    log: 'enable'
+    fin: 'enable'
+    rst: 'enable'
+    syn: 'enable'
+    time_range: 'ans-range'
+    state: present
+    provider: "{{ nxapi }}"
+  register: result
+
+- assert: &true
+    that:
+      - "result.changed == true"
+
+- name: "Check Idempotence"
+  nxos_acl: *configure
+  register: result
+
+- assert: &false
+    that:
+      - "result.changed == false"
+
+- name: "Remove ACL"
+  nxos_acl: *remove
+  register: result
+
+- assert: *true
+
+- name: "Check Idempotence"
+  nxos_acl: *remove
+  register: result
+
+- assert: *false
+
+- debug: msg="END TRANSPORT:NXAPI nxos_acl sanity test"
diff --git a/test/integration/targets/prepare_nxos_tests/tasks/main.yml b/test/integration/targets/prepare_nxos_tests/tasks/main.yml
--- a/test/integration/targets/prepare_nxos_tests/tasks/main.yml
+++ b/test/integration/targets/prepare_nxos_tests/tasks/main.yml
@@ -1,6 +1,9 @@
+# NXAPI is enabled differently on NX-OS platforms.
+# Try both commands to enable NXAPI and ignore any errors.
 - name: enable nxapi on remote device
-  nxos_nxapi:
-    http: yes
-    sandbox: yes
-    state: present
+  nxos_config:
+    lines:
+      - feature cli
+      - feature cli sandbox
     provider: "{{ cli }}"
+  ignore_errors: yes