Repository: ansible/ansible

Issue:
(P1) nxos_interface_ospf not idempotent
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
 - nxos_interface_ospf

##### NXOS VERSION
7.3(0)I6(1)

##### ANSIBLE VERSION
```
ansible 2.4.0 (devel 85e7e342b0) last updated 2017/07/20 14:17:47 (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]
```

##### OS / ENVIRONMENT
Ubuntu 14.04

##### SUMMARY
The `nxos_interface_ospf` module is not idempotent.

Failure occurs for both `cli` and `nxapi` transports.

```yaml
  - name: configure ospf interface
    nxos_interface_ospf: &configure
      interface: "{{ testint }}"
      ospf: 1
      area: 1
      cost: default
      provider: "{{ cli }}"
    register: result

  - assert: &true
      that:
        - "result.changed == true"

  - name: "Check Idempotence"
    nxos_interface_ospf: *configure
    register: result

  - assert: &false
      that:
        - "result.changed == false"
```
##### STEPS TO REPRODUCE

```shell
TASK [nxos_interface_ospf : configure ospf interface] ***************************************************************************************************************************************************************************************************
task path: /root/agents-ci/ansible/test/integration/targets/nxos_interface_ospf/tests/cli/sanity.yaml:30
<n9k.example.com> connection transport is cli
<n9k.example.com> using connection plugin network_cli
<n9k.example.com> socket_path: /root/.ansible/pc/b5409eb0b9
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/netcfg.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/parsing/convert_bool.py
Using module_utils file /root/agents-ci/ansible/lib/ansible/module_utils/parsing/__init__.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/network_common.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/urls.py
Using module file /root/agents-ci/ansible/lib/ansible/modules/network/nxos/nxos_interface_ospf.py
<n9k.example.com> ESTABLISH LOCAL CONNECTION FOR USER: root
<n9k.example.com> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1500656740.15-258034557849986 `" && echo ansible-tmp-1500656740.15-258034557849986="` echo $HOME/.ansible/tmp/ansible-tmp-1500656740.15-258034557849986 `" ) && sleep 0'
<n9k.example.com> PUT /tmp/tmp1jmsF3 TO /root/.ansible/tmp/ansible-tmp-1500656740.15-258034557849986/nxos_interface_ospf.py
<n9k.example.com> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1500656740.15-258034557849986/ /root/.ansible/tmp/ansible-tmp-1500656740.15-258034557849986/nxos_interface_ospf.py && sleep 0'
<n9k.example.com> EXEC /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1500656740.15-258034557849986/nxos_interface_ospf.py; rm -rf "/root/.ansible/tmp/ansible-tmp-1500656740.15-258034557849986/" > /dev/null 2>&1 && sleep 0'
changed: [n9k.example.com] => {
    "changed": true, 
    "commands": [
        "interface Ethernet1/1", 
        "ip router ospf 1 area 0.0.0.1"
    ], 
    "failed": false, 
    "invocation": {
        "module_args": {
            "area": "1", 
            "config": null, 
            "cost": "default", 
            "dead_interval": null, 
            "hello_interval": null, 
            "host": null, 
            "include_defaults": "True", 
            "interface": "Ethernet1/1", 
            "message_digest": null, 
            "message_digest_algorithm_type": null, 
            "message_digest_encryption_type": null, 
            "message_digest_key_id": null, 
            "message_digest_password": null, 
            "ospf": "1", 
            "passive_interface": null, 
            "password": null, 
            "port": null, 
            "provider": null, 
            "save": false, 
            "ssh_keyfile": null, 
            "state": "present", 
            "timeout": 10, 
            "transport": "cli", 
            "use_ssl": null, 
            "username": null, 
            "validate_certs": null
        }
    }
}

TASK [nxos_interface_ospf : assert] *********************************************************************************************************************************************************************************************************************
task path: /root/agents-ci/ansible/test/integration/targets/nxos_interface_ospf/tests/cli/sanity.yaml:39
ok: [n9k.example.com] => {
    "changed": false, 
    "failed": false, 
    "msg": "All assertions passed"
}

TASK [nxos_interface_ospf : Check Idempotence] **********************************************************************************************************************************************************************************************************
task path: /root/agents-ci/ansible/test/integration/targets/nxos_interface_ospf/tests/cli/sanity.yaml:43
<n9k.example.com> connection transport is cli
<n9k.example.com> using connection plugin network_cli
<n9k.example.com> socket_path: /root/.ansible/pc/b5409eb0b9
Using module file /root/agents-ci/ansible/lib/ansible/modules/network/nxos/nxos_interface_ospf.py
<n9k.example.com> ESTABLISH LOCAL CONNECTION FOR USER: root
<n9k.example.com> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1500656742.14-10789944496946 `" && echo ansible-tmp-1500656742.14-10789944496946="` echo $HOME/.ansible/tmp/ansible-tmp-1500656742.14-10789944496946 `" ) && sleep 0'
<n9k.example.com> PUT /tmp/tmpIjsv6R TO /root/.ansible/tmp/ansible-tmp-1500656742.14-10789944496946/nxos_interface_ospf.py
<n9k.example.com> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1500656742.14-10789944496946/ /root/.ansible/tmp/ansible-tmp-1500656742.14-10789944496946/nxos_interface_ospf.py && sleep 0'
<n9k.example.com> EXEC /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1500656742.14-10789944496946/nxos_interface_ospf.py; rm -rf "/root/.ansible/tmp/ansible-tmp-1500656742.14-10789944496946/" > /dev/null 2>&1 && sleep 0'
changed: [n9k.example.com] => {
    "changed": true, 
    "commands": [
        "interface Ethernet1/1", 
        "ip router ospf 1 area 0.0.0.1"
    ], 
    "failed": false, 
    "invocation": {
        "module_args": {
            "area": "1", 
            "config": null, 
            "cost": "default", 
            "dead_interval": null, 
            "hello_interval": null, 
            "host": null, 
            "include_defaults": "True", 
            "interface": "Ethernet1/1", 
            "message_digest": null, 
            "message_digest_algorithm_type": null, 
            "message_digest_encryption_type": null, 
            "message_digest_key_id": null, 
            "message_digest_password": null, 
            "ospf": "1", 
            "passive_interface": null, 
            "password": null, 
            "port": null, 
            "provider": null, 
            "save": false, 
            "ssh_keyfile": null, 
            "state": "present", 
            "timeout": 10, 
            "transport": "cli", 
            "use_ssl": null, 
            "username": null, 
            "validate_certs": null
        }
    }
}

TASK [nxos_interface_ospf : assert] *********************************************************************************************************************************************************************************************************************
task path: /root/agents-ci/ansible/test/integration/targets/nxos_interface_ospf/tests/cli/sanity.yaml:47
fatal: [n9k.example.com]: FAILED! => {
    "assertion": "result.changed == false", 
    "changed": false, 
    "evaluated_to": false, 
    "failed": true
}
```



Discussion:

Resolving patch:
diff --git a/lib/ansible/modules/network/nxos/nxos_interface_ospf.py b/lib/ansible/modules/network/nxos/nxos_interface_ospf.py
--- a/lib/ansible/modules/network/nxos/nxos_interface_ospf.py
+++ b/lib/ansible/modules/network/nxos/nxos_interface_ospf.py
@@ -207,7 +207,10 @@ def get_value(arg, config, module):
 def get_existing(module, args):
     existing = {}
     netcfg = CustomNetworkConfig(indent=2, contents=get_config(module))
-    parents = ['interface {0}'.format(module.params['interface'].capitalize())]
+    if module.params['interface'].startswith('loopback') or module.params['interface'].startswith('port-channel'):
+        parents = ['interface {0}'.format(module.params['interface'])]
+    else:
+        parents = ['interface {0}'.format(module.params['interface'].capitalize())]
     config = netcfg.get_section(parents)
     if 'ospf' in config:
         for arg in args:
@@ -390,7 +393,7 @@ def main():
                                                'message_digest_password']],
                            supports_check_mode=True)
 
-    if not module.params['interface'].startswith('loopback'):
+    if not module.params['interface'].startswith('loopback') and not module.params['interface'].startswith('port-channel'):
         module.params['interface'] = module.params['interface'].capitalize()
 
     warnings = list()


Test patch: