Repository: ansible/ansible

Issue:
os_security_group_rule is not idempotent for port_range_min: -1 as advertised in the documentation
_From @stefanandres on July 5, 2016 14:56_

##### ISSUE TYPE
- Bug Report
##### COMPONENT NAME

os_security_group_rule
##### ANSIBLE VERSION

```
$ ansible --version
ansible 2.1.0.0
```
##### CONFIGURATION
##### OS / ENVIRONMENT
##### SUMMARY

When using the example from http://docs.ansible.com/ansible/os_security_group_rule_module.html for '# Another way to create the ping rule' - but use tcp instead of icmp, ansible is not idempotent.
##### STEPS TO REPRODUCE

```
$ cat foo.yml
- hosts: localhost
  become: no
  tasks:
    - name: foo
      os_security_group_rule:
        security_group: default
        protocol: icmp
        port_range_min: -1
        port_range_max: -1
        remote_ip_prefix: 1.1.1.1/32
```

```
$ ansible-playbook foo.yml
PLAY [localhost] ***************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [foo] *********************************************************************
changed: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0
```

```
$ ansible-playbook foo.yml

PLAY [localhost] ***************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [foo] *********************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "msg": "Error creating security group rule: Security group rule already exists. Rule id is 5a63c1dc-c3aa-4ef3-9280-b7b7e9e5dbff."}

NO MORE HOSTS LEFT *************************************************************
    to retry, use: --limit @foo.retry

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=1
```

This is needed to do more abstraction of security_group_rule, for example:

```
- name: Create security group rules from os_security_rules_ports
  os_security_group_rule:
    security_group: "{{ item.0.security_group | default(item.0.name) }}"
    protocol: "{{ item.0.protocol | default('tcp')  }}"
    port_range_min: "{{ item.0.port_range_min | default('-1') }}"
    port_range_max: "{{ item.0.port_range_max | default('-1') }}"
    remote_ip_prefix: "{{ item.1 }}"
  with_subelements:
    - "{{ os_security_rules_ports }}"
    - remote_ip_prefixes
  when:
    - item.0.port_range_min
    - item.0.port_range_max
    - item.1 is defined
```

With a hash of

```
os_security_rules_ports:
  - name: allow_office
    remote_ip_prefixes:
      - 1.1.1.1/32
      - 2.2.2.2/32
```

will not work, since it will always throw the above error.


_Copied from original issue: ansible/ansible-modules-core#4108_


Discussion:
_From @ansibot on July 29, 2016 21:49_

<!-- @stefanandres: Thanks for filing this bug report! -->

@emonty, @shrews, @juliakreger, @j2sol, @rcarrillocruz, ping. This issue is waiting on your response.
[click here for bot help](https://github.com/jctanner/ansibullbot/blob/ISSUE_TRIAGE_CLASS/ISSUE_HELP.md)

<!--- boilerplate: issue_bug_new --->

_From @ansibot on September 8, 2016 20:7_

@emonty, @shrews, @juliakreger, @j2sol, @rcarrillocruz, ping. This issue is still waiting on your response.
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)

<!--- boilerplate: issue_renotify_maintainer --->

_From @ansibot on October 6, 2016 2:42_

@emonty, @shrews, @juliakreger, @j2sol, @rcarrillocruz, ping. This issue is still waiting on your response.
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)

<!--- boilerplate: issue_renotify_maintainer --->

_From @j2sol on October 6, 2016 17:30_

Are you using nova-network or neutron? The behavior can be different.

_From @stefanandres on October 10, 2016 7:42_

Neutron

_From @ansibot on October 21, 2016 8:47_

@emonty, @shrews, @juliakreger, @j2sol, @rcarrillocruz, ping. This issue is still waiting on your response.
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)

<!--- boilerplate: issue_renotify_maintainer --->

_From @ansibot on November 6, 2016 9:46_

@emonty, @shrews, @juliakreger, @j2sol, @rcarrillocruz, ping. This issue is still waiting on your response.
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)

<!--- boilerplate: issue_renotify_maintainer --->

_From @ansibot on November 22, 2016 9:54_

@emonty, @shrews, @juliakreger, @j2sol, @rcarrillocruz, @thingee, ping. This issue is still waiting on your response.
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: issue_renotify_maintainer --->
_From @ansibot on December 7, 2016 16:29_

This repository has been locked. All new issues and pull requests should be filed in https://github.com/ansible/ansible

Please read through the <a href="http://docs.ansible.com/ansible/dev_guide/repomerge.html#move-issues-and-prs-to-new-repo">repomerge</a> page in the dev guide. The guide contains links to tools which automatically move your issue or pull request to the ansible/ansible repo.

<!-- boilerplate: repomerge -->

Resolving patch:
diff --git a/lib/ansible/modules/cloud/openstack/os_security_group_rule.py b/lib/ansible/modules/cloud/openstack/os_security_group_rule.py
--- a/lib/ansible/modules/cloud/openstack/os_security_group_rule.py
+++ b/lib/ansible/modules/cloud/openstack/os_security_group_rule.py
@@ -200,12 +200,17 @@ def _ports_match(protocol, module_min, module_max, rule_min, rule_max):
         if module_max and int(module_max) == -1:
             module_max = None
 
-    # Check if user is supplying None values for full TCP/UDP port range.
-    if protocol in ['tcp', 'udp'] and module_min is None and module_max is None:
-        if (rule_min and int(rule_min) == 1
-                and rule_max and int(rule_max) == 65535):
-            # (None, None) == (1, 65535)
-            return True
+    # Check if the user is supplying -1 or None values for full TPC/UDP port range.
+    if protocol in ['tcp', 'udp'] or protocol is None:
+        if module_min and module_max and int(module_min) == int(module_max) == -1:
+            module_min = None
+            module_max = None
+
+        if ((module_min is None and module_max is None) and
+                (rule_min and int(rule_min) == 1 and
+                    rule_max and int(rule_max) == 65535)):
+                    # (None, None) == (1, 65535)
+                    return True
 
     # Sanity check to make sure we don't have type comparison issues.
     if module_min:


Test patch: