Repository: ansible/ansible-modules-core

Issue:
ec2_elb_lb: exception thrown when using variable for the load_balancer_port
##### Issue Type: Bug Report
##### Ansible Version: ansible 1.9.1
##### Ansible Configuration: not changed.
##### Environment: Ubuntu 14.04.2 LTS
##### Summary:

I'm using the `ec2_elb_lb` module to switch ports on a ELB. It works fine when the `load_balancer_port` is hardcoded (e.g. `load_balancer_port: 80`) but not if it is passed in via a variable - a boto exception is generated.
##### Steps To Reproduce:
1. Create a test ELB in AWS. Use whatever defaults amazon gives.
2. Have a IAM user that can modify the ELB
3. Create this file called: `switch_elb.yml` but with your own ELB details in:

```

---
- hosts: localhost
  connection: local
  gather_facts: false
  tasks:
    - name: Switch ELB Port to newly deployed service
      ec2_elb_lb:
        name: "<your-elb-name>"
        aws_access_key: "<your-key>"
        aws_secret_key: "<your-secret>"
        region: "us-east-1"
        state: present
        zones:
          - us-east-1e
          - us-east-1c
        listeners:
          - protocol: http
            load_balancer_port: "{{ public_port }}"
            instance_protocol: http
            instance_port: "{{ port }}"
        purge_listeners: no
```

(the important thing is the `load_balancer_port` is a variable)

Trigger the play once:
`$ansible-playbook --connection=local --extra-vars='{"port":11006,"public_port":80}' switch_elb.yml`

Trigger it again but with a new port:
`$ansible-playbook --connection=local --extra-vars='{"port":11007,"public_port":80}' switch_elb.yml`
##### Expected Results:

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

TASK: [Switch ELB Port to newly deployed service] ***************************** 
changed: [localhost]

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

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

TASK: [Switch ELB Port to newly deployed service] ***************************** 
failed: [localhost] => {"failed": true, "parsed": false}
Traceback (most recent call last):
  File "/home/tom/.ansible/tmp/ansible-tmp-1440777913.42-88246387281716/ec2_elb_lb", line 2506, in <module>
    main()
  File "/home/tom/.ansible/tmp/ansible-tmp-1440777913.42-88246387281716/ec2_elb_lb", line 693, in main
    elb_man.ensure_ok()
  File "/home/tom/.ansible/tmp/ansible-tmp-1440777913.42-88246387281716/ec2_elb_lb", line 296, in ensure_ok
    self._set_elb_listeners()
  File "/home/tom/.ansible/tmp/ansible-tmp-1440777913.42-88246387281716/ec2_elb_lb", line 471, in _set_elb_listeners
    self._create_elb_listeners(listeners_to_add)
  File "/home/tom/.ansible/tmp/ansible-tmp-1440777913.42-88246387281716/ec2_elb_lb", line 406, in _create_elb_listeners
    complex_listeners=listeners)
  File "/usr/lib/python2.7/dist-packages/boto/ec2/elb/__init__.py", line 315, in create_load_balancer_listeners
    return self.get_status('CreateLoadBalancerListeners', params)
  File "/usr/lib/python2.7/dist-packages/boto/connection.py", line 1166, in get_status
    raise self.ResponseError(response.status, response.reason, body)
boto.exception.BotoServerError: BotoServerError: 400 Bad Request
<ErrorResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
  <Error>
    <Type>Sender</Type>
    <Code>DuplicateListener</Code>
    <Message>A listener already exists for supertasker-staging-tomtest with LoadBalancerPort 80, but with a different InstancePort, Protocol, or SSLCertificateId</Message>
  </Error>
  <RequestId>91260edf-4d9e-11e5-8c23-efa94f6e45fe</RequestId>
</ErrorResponse>

FATAL: all hosts have already failed -- aborting
```

What the exception message says is true, (i.e. "_A listener already exists for supertasker-staging-tomtest with LoadBalancerPort 80, but with a different InstancePort, Protocol, or SSLCertificateId_") but what Ansible is supposed to do in that case is remove the listener and add it back with the new `InstancePort`. This is what it does when `load_balancer_port` is hardcoded.

Change `switch_elb.yml` to use `load_balancer_port: 80` and run the play again, it'll work. Odd!

ec2_elb_lb: exception thrown when using variable for the load_balancer_port
##### Issue Type: Bug Report
##### Ansible Version: ansible 1.9.1
##### Ansible Configuration: not changed.
##### Environment: Ubuntu 14.04.2 LTS
##### Summary:

I'm using the `ec2_elb_lb` module to switch ports on a ELB. It works fine when the `load_balancer_port` is hardcoded (e.g. `load_balancer_port: 80`) but not if it is passed in via a variable - a boto exception is generated.
##### Steps To Reproduce:
1. Create a test ELB in AWS. Use whatever defaults amazon gives.
2. Have a IAM user that can modify the ELB
3. Create this file called: `switch_elb.yml` but with your own ELB details in:

```

---
- hosts: localhost
  connection: local
  gather_facts: false
  tasks:
    - name: Switch ELB Port to newly deployed service
      ec2_elb_lb:
        name: "<your-elb-name>"
        aws_access_key: "<your-key>"
        aws_secret_key: "<your-secret>"
        region: "us-east-1"
        state: present
        zones:
          - us-east-1e
          - us-east-1c
        listeners:
          - protocol: http
            load_balancer_port: "{{ public_port }}"
            instance_protocol: http
            instance_port: "{{ port }}"
        purge_listeners: no
```

(the important thing is the `load_balancer_port` is a variable)

Trigger the play once:
`$ansible-playbook --connection=local --extra-vars='{"port":11006,"public_port":80}' switch_elb.yml`

Trigger it again but with a new port:
`$ansible-playbook --connection=local --extra-vars='{"port":11007,"public_port":80}' switch_elb.yml`
##### Expected Results:

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

TASK: [Switch ELB Port to newly deployed service] ***************************** 
changed: [localhost]

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

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

TASK: [Switch ELB Port to newly deployed service] ***************************** 
failed: [localhost] => {"failed": true, "parsed": false}
Traceback (most recent call last):
  File "/home/tom/.ansible/tmp/ansible-tmp-1440777913.42-88246387281716/ec2_elb_lb", line 2506, in <module>
    main()
  File "/home/tom/.ansible/tmp/ansible-tmp-1440777913.42-88246387281716/ec2_elb_lb", line 693, in main
    elb_man.ensure_ok()
  File "/home/tom/.ansible/tmp/ansible-tmp-1440777913.42-88246387281716/ec2_elb_lb", line 296, in ensure_ok
    self._set_elb_listeners()
  File "/home/tom/.ansible/tmp/ansible-tmp-1440777913.42-88246387281716/ec2_elb_lb", line 471, in _set_elb_listeners
    self._create_elb_listeners(listeners_to_add)
  File "/home/tom/.ansible/tmp/ansible-tmp-1440777913.42-88246387281716/ec2_elb_lb", line 406, in _create_elb_listeners
    complex_listeners=listeners)
  File "/usr/lib/python2.7/dist-packages/boto/ec2/elb/__init__.py", line 315, in create_load_balancer_listeners
    return self.get_status('CreateLoadBalancerListeners', params)
  File "/usr/lib/python2.7/dist-packages/boto/connection.py", line 1166, in get_status
    raise self.ResponseError(response.status, response.reason, body)
boto.exception.BotoServerError: BotoServerError: 400 Bad Request
<ErrorResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
  <Error>
    <Type>Sender</Type>
    <Code>DuplicateListener</Code>
    <Message>A listener already exists for supertasker-staging-tomtest with LoadBalancerPort 80, but with a different InstancePort, Protocol, or SSLCertificateId</Message>
  </Error>
  <RequestId>91260edf-4d9e-11e5-8c23-efa94f6e45fe</RequestId>
</ErrorResponse>

FATAL: all hosts have already failed -- aborting
```

What the exception message says is true, (i.e. "_A listener already exists for supertasker-staging-tomtest with LoadBalancerPort 80, but with a different InstancePort, Protocol, or SSLCertificateId_") but what Ansible is supposed to do in that case is remove the listener and add it back with the new `InstancePort`. This is what it does when `load_balancer_port` is hardcoded.

Change `switch_elb.yml` to use `load_balancer_port: 80` and run the play again, it'll work. Odd!



Discussion:
@tomfotherby I don't use this module but I've seen similar errors from boto before.  Your description of the problem (It works if you enter `load_balancer_port: 80` but it fails if you use a variable `load_balancer_port: {{eighty}}` leads me to believe that it's the boto api being picky about whether it's passed a string or an integer as the port number.  I've updated the code to coerce strings to ints for the ports which will fix this provided that that is indeed the issue.

Could you verify that https://github.com/ansible/ansible-modules-core/commit/2520627fe7d487a08e077a01bb1251f3757f0515 fixes things for you?   If I don't hear back I'll assume that was the problem and close this.

Also, if you find this patch works but can't keep it patched in production, specifying the variable like this may work:

```
        listeners:
            load_balancer_port: "{{ public_port |int}}"
```

(I think the int filter is doing the right thing in 1.9.x but it may be something we got working in 2.0... patching the code would be preferable if you can do that.)

Thanks for commenting. I did already try `"{{ public_port | int}}"` with no luck. I'll try v2 and also the patch and let you know. (I'm on vacation for a week though)

@tomfotherby I don't use this module but I've seen similar errors from boto before.  Your description of the problem (It works if you enter `load_balancer_port: 80` but it fails if you use a variable `load_balancer_port: {{eighty}}` leads me to believe that it's the boto api being picky about whether it's passed a string or an integer as the port number.  I've updated the code to coerce strings to ints for the ports which will fix this provided that that is indeed the issue.

Could you verify that https://github.com/ansible/ansible-modules-core/commit/2520627fe7d487a08e077a01bb1251f3757f0515 fixes things for you?   If I don't hear back I'll assume that was the problem and close this.

Also, if you find this patch works but can't keep it patched in production, specifying the variable like this may work:

```
        listeners:
            load_balancer_port: "{{ public_port |int}}"
```

(I think the int filter is doing the right thing in 1.9.x but it may be something we got working in 2.0... patching the code would be preferable if you can do that.)

Thanks for commenting. I did already try `"{{ public_port | int}}"` with no luck. I'll try v2 and also the patch and let you know. (I'm on vacation for a week though)


Resolving patch:
diff --git a/cloud/amazon/ec2_elb_lb.py b/cloud/amazon/ec2_elb_lb.py
--- a/cloud/amazon/ec2_elb_lb.py
+++ b/cloud/amazon/ec2_elb_lb.py
@@ -522,7 +522,7 @@ def _set_elb_listeners(self):
             for existing_listener in self.elb.listeners:
                 # Since ELB allows only one listener on each incoming port, a
                 # single match on the incoming port is all we're looking for
-                if existing_listener[0] == listener['load_balancer_port']:
+                if existing_listener[0] == int(listener['load_balancer_port']):
                     existing_listener_found = self._api_listener_as_tuple(existing_listener)
                     break
 


Test patch: