Repository: ansible/ansible

Issue:
Impossible to set icmp_code or icmp_type to 0 in cs_securitygroup_rule
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
`cs_securitygroup_rule`

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.2.0.0
  config file = /Users/m/Code/yourtes/infrastructure/ansible.cfg
  configured module search path = Default w/o overrides
```

##### CONFIGURATION
<!---
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.
-->
OS X El Capitan 10.11.6, but I don't believe it is platform-specific

##### SUMMARY
When using the `module cs_securitygroup_rule`, trying to set the protocol to `icmp`, when any of `icmp_type` or `icmp_code` is set to `0`, will fail.

Ansible will be saying:
```
"msg": "no icmp_type or icmp_code set for protocol 'icmp'"
```

But type/code 0 is a valid type/code from the RFC. Typically, allowing only ping is represented by `type 8, code 0`.

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem.
For new features, show how the feature would be used.
-->
Here is two playbook demonstrating the faillure, those assume you have access to a CloudStack server:
 
<!--- Paste example playbooks or commands between quotes below -->
```
---
- name: Provision security groups
  hosts: localhost
  connection: local
  gather_facts: false
  tasks:
    - name: Create security groups
      cs_securitygroup:
        name: default

    - name: Add icmp rule to default sg
      cs_securitygroup_rule:
        security_group: default
        type: ingress
        protocol: icmp
        icmp_code: 0
        icmp_type: -1
```

```
---
- name: Provision security groups
  hosts: localhost
  connection: local
  gather_facts: false
  tasks:
    - name: Create security groups
      cs_securitygroup:
        name: default

    - name: Add icmp rule to default sg
      cs_securitygroup_rule:
        security_group: default
        type: ingress
        protocol: icmp
        icmp_code: -1
        icmp_type: 0
```

Changing any of those 0 will make the playbook works.

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Being able to create a security group using type or code `0` for the icmp protocol.

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```
TASK [Add icmp rule to default sg] *********************************************
task path: /Users/m/Code/yourtes/infrastructure/deploy-security-groups.yml:17
Using module file /usr/local/Cellar/ansible/2.2.0.0_1/libexec/lib/python2.7/site-packages/ansible/modules/extras/cloud/cloudstack/cs_securitygroup_rule.py
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: m
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1481391211.43-161096246584508 `" && echo ansible-tmp-1481391211.43-161096246584508="` echo $HOME/.ansible/tmp/ansible-tmp-1481391211.43-161096246584508 `" ) && sleep 0'
<localhost> PUT /var/folders/tj/1qfj1r9j61bfcpfdsl6m9dsm0000gn/T/tmpbLElUb TO /Users/m/.ansible/tmp/ansible-tmp-1481391211.43-161096246584508/cs_securitygroup_rule.py
<localhost> EXEC /bin/sh -c 'chmod u+x /Users/m/.ansible/tmp/ansible-tmp-1481391211.43-161096246584508/ /Users/m/.ansible/tmp/ansible-tmp-1481391211.43-161096246584508/cs_securitygroup_rule.py && sleep 0'
<localhost> EXEC /bin/sh -c '/usr/local/bin/python /Users/m/.ansible/tmp/ansible-tmp-1481391211.43-161096246584508/cs_securitygroup_rule.py; rm -rf "/Users/m/.ansible/tmp/ansible-tmp-1481391211.43-161096246584508/" > /dev/null 2>&1 && sleep 0'
fatal: [localhost]: FAILED! => {
    "changed": false,
    "failed": true,
    "invocation": {
        "module_args": {
            "api_http_method": "get",
            "api_key": null,
            "api_region": "cloudstack",
            "api_secret": null,
            "api_timeout": 10,
            "api_url": null,
            "cidr": "0.0.0.0/0",
            "end_port": null,
            "icmp_code": 0,
            "icmp_type": -1,
            "poll_async": true,
            "project": null,
            "protocol": "icmp",
            "security_group": "default",
            "start_port": null,
            "state": "present",
            "type": "ingress",
            "user_security_group": null
        },
        "module_name": "cs_securitygroup_rule"
    },
    "msg": "no icmp_type or icmp_code set for protocol 'icmp'"
}
	to retry, use: --limit @/Users/m/Code/yourtes/infrastructure/deploy-security-groups.retry
```

Allowing myself to poke @resmo, since I know he is the maintener of those modules.


Discussion:

Resolving patch:
diff --git a/lib/ansible/modules/cloud/cloudstack/cs_securitygroup_rule.py b/lib/ansible/modules/cloud/cloudstack/cs_securitygroup_rule.py
--- a/lib/ansible/modules/cloud/cloudstack/cs_securitygroup_rule.py
+++ b/lib/ansible/modules/cloud/cloudstack/cs_securitygroup_rule.py
@@ -243,10 +243,10 @@ def _get_rule(self, rules):
         icmp_code                = self.module.params.get('icmp_code')
         icmp_type                = self.module.params.get('icmp_type')
 
-        if protocol in ['tcp', 'udp'] and not (start_port and end_port):
+        if protocol in ['tcp', 'udp'] and (start_port is None or end_port is None):
             self.module.fail_json(msg="no start_port or end_port set for protocol '%s'" % protocol)
 
-        if protocol == 'icmp' and not (icmp_type and icmp_code):
+        if protocol == 'icmp' and (icmp_type is None or icmp_code is None):
             self.module.fail_json(msg="no icmp_type or icmp_code set for protocol '%s'" % protocol)
 
         for rule in rules:


Test patch: