Repository: ansible/ansible

Issue:
nxos_portchannel : module not idempotent for N1 images
<!---
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
<!--- Name of the module/plugin/task/feature -->
- nxos_portchannel

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.4.0.0 (detached HEAD d14467b029) last updated 2017/09/28 11:09:55 (GMT -400)
  config file = None
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /root/agents-ci/ansible/lib/ansible
  executable location = /root/agents-ci/ansible/bin/ansible
  python version = 2.7.6 (default, Oct 26 2016, 20:30:19) [GCC 4.8.4]
```

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste the results of "ansible-config dump --only-changed"

Otherwise, 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.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
Ubuntu 14.04

##### SUMMARY
<!--- Explain the problem briefly -->
Output of `show port-channel summary | json` returns `group` as int as compared to a string on other platforms. This causes idempotence issues on N1 images. The fix is to check for both string and int equality.
 
##### 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 port-channel mode active
    nxos_portchannel: &configpc
      group: 99
      members: ["{{ testint1 }}", "{{ testint2 }}"]
      mode: active
      force: 'true'
      state: present
      timeout: 60
      provider: "{{ connection }}"
    register: result

  - assert: &true
      that:
        - "result.changed == true"

  - name: Check Idempotence
    nxos_portchannel: *configpc
    register: result

  - assert: &false
      that:
        - "result.changed == false"
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Playbook should pass

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```
TASK [nxos_portchannel : Check Idempotence] ***************************************************************************************************************
task path: /root/agents-ci/ansible/test/integration/targets/nxos_portchannel/tests/cli/sanity.yaml:53
<n5k-xxx.example.com> connection transport is cli
<n5k-xxx.example.com> using connection plugin network_cli
<n5k-xxx.example.com> socket_path: /root/.ansible/pc/1b4e3e6315
Using module file /root/agents-ci/ansible/lib/ansible/modules/network/nxos/nxos_portchannel.py
<n5k-xxx.example.com> ESTABLISH LOCAL CONNECTION FOR USER: root
<n5k-xxx.example.com> EXEC /bin/sh -c 'echo ~ && sleep 0'
<n5k-xxx.example.com> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1506024860.42-117137983875574 `" && echo ansible-tmp-1506024860.42-117137983875574="` echo /root/.ansible/tmp/ansible-tmp-1506024860.42-117137983875574 `" ) && sleep 0'
<n5k-xxx.example.com> PUT /tmp/tmppVH_PM TO /root/.ansible/tmp/ansible-tmp-1506024860.42-117137983875574/nxos_portchannel.py
<n5k-xxx.example.com> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1506024860.42-117137983875574/ /root/.ansible/tmp/ansible-tmp-1506024860.42-117137983875574/nxos_portchannel.py && sleep 0'
<n5k-xxx.example.com> EXEC /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1506024860.42-117137983875574/nxos_portchannel.py; rm -rf "/root/.ansible/tmp/ansible-tmp-1506024860.42-117137983875574/" > /dev/null 2>&1 && sleep 0'
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_uvmlWf/ansible_module_nxos_portchannel.py", line 498, in <module>
    main()
  File "/tmp/ansible_uvmlWf/ansible_module_nxos_portchannel.py", line 481, in main
    commands = state_present(module, existing, proposed, interface_exist, force, warnings)
  File "/tmp/ansible_uvmlWf/ansible_module_nxos_portchannel.py", line 408, in state_present
    command = get_commands_to_add_members(proposed, existing, force, module)
  File "/tmp/ansible_uvmlWf/ansible_module_nxos_portchannel.py", line 300, in get_commands_to_add_members
    existing['group'], force, proposed['mode']))
KeyError: 'group'
FAILED! => {
    "changed": false, 
    "failed": true, 
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_uvmlWf/ansible_module_nxos_portchannel.py\", line 498, in <module>\n    main()\n  File \"/tmp/ansible_uvmlWf/ansible_module_nxos_portchannel.py\", line 481, in main\n    commands = state_present(module, existing, proposed, interface_exist, force, warnings)\n  File \"/tmp/ansible_uvmlWf/ansible_module_nxos_portchannel.py\", line 408, in state_present\n    command = get_commands_to_add_members(proposed, existing, force, module)\n  File \"/tmp/ansible_uvmlWf/ansible_module_nxos_portchannel.py\", line 300, in get_commands_to_add_members\n    existing['group'], force, proposed['mode']))\nKeyError: 'group'\n",  
    "module_stdout": "", 
    "msg": "MODULE FAILURE",  
    "rc": 0
}
```



Discussion:

Resolving patch:
diff --git a/lib/ansible/modules/network/nxos/nxos_portchannel.py b/lib/ansible/modules/network/nxos/nxos_portchannel.py
--- a/lib/ansible/modules/network/nxos/nxos_portchannel.py
+++ b/lib/ansible/modules/network/nxos/nxos_portchannel.py
@@ -203,6 +203,8 @@ def get_portchannel(module, netcfg=None):
         for pc in pc_table:
             if pc['group'] == module.params['group']:
                 portchannel_table = pc
+            elif module.params['group'].isdigit() and pc['group'] == int(module.params['group']):
+                portchannel_table = pc
     except (KeyError, AttributeError, TypeError, IndexError):
         return {}
 


Test patch: