Repository: ansible/ansible-modules-core

Issue:
Allowed_address_pairs in os_port can't be set to only an ip range since fix parameters type commit
##### Issue Type:
- Bug Report
##### Plugin Name:

os_port
##### Ansible Version:

```
ansible 2.1.0
  config file = /home/fxk8y/Dokumente/kubernetesAutoDeployment/ansible.cfg
  configured module search path = Default w/o overrides
```
##### Ansible Configuration:

```
[defaults]
host_key_checking = False
command_warnings = False

[privilege_escalation]
become = True
become_method = sudo
become_user = root

[ssh_connection]
ssh_args = -o ControlMaster=no
```
##### Environment:

Provisioning Ubuntu 15.04 instances from Ubuntu 15.10.
shade version: 1.5.1

shade runs on localhost, that's the Ubuntu 15.10 one.
##### Summary:

Since commit f71542ccf22583bf7eb214ed6abbcdaf5f18df96 of 12. feb it isn't possible anymore to only specify an ip address range for allowed_address_pairs in os_port module.
The problem came up while writing a Kubernetes deployment playbook for OpenStack which first boots the instances and then provisions them in one rush.
Both the OpenStack API and the shade library permit supplying only the ip but since the fix parameter type issue os_port prohibits this so we would have to first create the ports, query their dynamically assigned macs and then set the address range, but if you do supply only the  ip range to neutron it automatically uses the newly generated mac as second parameter while creating the network port.

Kubernetes needs allowed_address_pairs to be set to 10.0.0.0/8 in OpenStack otherwise the firewall would block the internal network communication of the Pods inside the cluster.
##### Steps To Reproduce:

Run the following snippet with ansible 2.1.0:

```
- name: Create network ports
  os_port:
    cloud: ts
    name: "{{item}}"
    network: "{{network_name}}"
    allowed_address_pairs:
      - ip_address: 10.0.0.0/8
    security_groups:
      - kubernetes
    state: present
  with_sequence: start=0 end={{instance_cnt}} format=kubernetes%d-port
```

The mac address of the port can't be supplied here because it is not known at the moment and not predictable.
##### Expected Results:

I expected the creation of the ports to complete without errors as it does in Ansible 2.0.1.0

```
changed: [localhost] => (item=kubernetes0-port)
```
##### Actual Results:

normal output:

```
TASK [Create network ports] ****************************************************
failed: [localhost] (item=kubernetes0-port) => {"failed": true, "item": "kubernetes0-port", "msg": "argument allowed_address_pairs is of type <type 'list'> and we were unable to convert to dict"}
```

debug output:

```
TASK [Create network ports] ****************************************************
task path: /home/fxk8y/Dokumente/kubernetesAutoDeployment/main.yml:14
ESTABLISH LOCAL CONNECTION FOR USER: root
localhost EXEC /bin/sh -c '( umask 22 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1458642387.89-250376251738384 `" && echo "` echo $HOME/.ansible/tmp/ansible-tmp-1458642387.89-250376251738384 `" )'
localhost PUT /tmp/tmpmEXGIb TO /root/.ansible/tmp/ansible-tmp-1458642387.89-250376251738384/os_port
localhost EXEC /bin/sh -c 'LANG=de_DE.UTF-8 LC_ALL=de_DE.UTF-8 LC_MESSAGES=de_DE.UTF-8 /usr/bin/python /root/.ansible/tmp/ansible-tmp-1458642387.89-250376251738384/os_port; rm -rf "/root/.ansible/tmp/ansible-tmp-1458642387.89-250376251738384/" > /dev/null 2>&1'
failed: [localhost] (item=kubernetes0-port) => {"failed": true, "invocation": {"module_args": {"allowed_address_pairs": [{"ip_address": "10.0.0.0/8"}], "cloud": "ts", "endpoint_type": "public", "name": "kubernetes0-port", "network": "Net1", "no_security_groups": false, "security_groups": ["kubernetes"], "state": "present", "timeout": 180, "verify": true, "wait": true}, "module_name": "os_port"}, "item": "kubernetes0-port", "msg": "argument allowed_address_pairs is of type <type 'list'> and we were unable to convert to dict"}
```



Discussion:
Ah, looks like we set the wrong type. It should be a 'list'. Will look at getting this fixed ASAP. Thanks for the report.


Resolving patch:
diff --git a/cloud/openstack/os_port.py b/cloud/openstack/os_port.py
--- a/cloud/openstack/os_port.py
+++ b/cloud/openstack/os_port.py
@@ -30,8 +30,8 @@
 author: "Davide Agnello (@dagnello)"
 version_added: "2.0"
 description:
-   - Add, Update or Remove ports from an OpenStack cloud.  A state=present,
-     will ensure the port is created or updated if required.
+   - Add, Update or Remove ports from an OpenStack cloud. A I(state) of
+     'present' will ensure the port is created or updated if required.
 options:
    network:
      description:
@@ -79,11 +79,11 @@
                   - ip_address: ..."
      required: false
      default: None
-   extra_dhcp_opt:
+   extra_dhcp_opts:
      description:
         - "Extra dhcp options to be assigned to this port.  Extra options are
           supported with dictionary structure.
-          e.g.  extra_dhcp_opt:
+          e.g.  extra_dhcp_opts:
                   - opt_name: opt name1
                     opt_value: value1
                   - opt_name: ..."
@@ -218,7 +218,7 @@ def _needs_update(module, port, cloud):
                       'device_owner',
                       'device_id']
     compare_dict = ['allowed_address_pairs',
-                    'extra_dhcp_opt']
+                    'extra_dhcp_opts']
     compare_list = ['security_groups']
 
     for key in compare_simple:
@@ -280,7 +280,7 @@ def _compose_port_args(module, cloud):
                            'mac_address',
                            'security_groups',
                            'allowed_address_pairs',
-                           'extra_dhcp_opt',
+                           'extra_dhcp_opts',
                            'device_owner',
                            'device_id']
     for optional_param in optional_parameters:
@@ -310,8 +310,8 @@ def main():
         mac_address=dict(default=None),
         security_groups=dict(default=None, type='list'),
         no_security_groups=dict(default=False, type='bool'),
-        allowed_address_pairs=dict(type='dict', default=None),
-        extra_dhcp_opt=dict(type='dict', default=None),
+        allowed_address_pairs=dict(type='list', default=None),
+        extra_dhcp_opts=dict(type='list', default=None),
         device_owner=dict(default=None),
         device_id=dict(default=None),
         state=dict(default='present', choices=['absent', 'present']),


Test patch: