From 6a964496b10dd52e56bb325a0145af1fca3bbf96 Mon Sep 17 00:00:00 2001 From: "Christopher H. Laco" Date: Sun, 3 Nov 2013 12:34:42 -0600 Subject: [PATCH 1/5] Renamed rax_clb to rax_clb_nodes since it's node management and not clb creation --- library/cloud/{rax_clb => rax_clb_nodes} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename library/cloud/{rax_clb => rax_clb_nodes} (100%) diff --git a/library/cloud/rax_clb b/library/cloud/rax_clb_nodes similarity index 100% rename from library/cloud/rax_clb rename to library/cloud/rax_clb_nodes From 0be54b55858a61e7378e093b3eb0287d66de61a6 Mon Sep 17 00:00:00 2001 From: "Christopher H. Laco" Date: Sun, 3 Nov 2013 13:13:13 -0600 Subject: [PATCH 2/5] Added replacement rax_clb that does LB detailed creation --- library/cloud/rax_clb | 274 ++++++++++++++++++++++++++++++++++++ library/cloud/rax_clb_nodes | 2 +- 2 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 library/cloud/rax_clb diff --git a/library/cloud/rax_clb b/library/cloud/rax_clb new file mode 100644 index 0000000000..498ab1414e --- /dev/null +++ b/library/cloud/rax_clb @@ -0,0 +1,274 @@ +#!/usr/bin/python -tt +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +DOCUMENTATION = ''' +--- +module: rax_clb +short_description: create / delete a load balancer in Rackspace Public Cloud +description: + - creates / deletes a Rackspace Public Cloud load balancer. +version_added: "1.4" +options: + state: + description: + - Indicate desired state of the resource + choices: ['present', 'absent'] + default: present + credentials: + description: + - File to find the Rackspace credentials in (ignored if C(api_key) and + C(username) are provided) + default: null + aliases: ['creds_file'] + api_key: + description: + - Rackspace API key (overrides C(credentials)) + username: + description: + - Rackspace username (overrides C(credentials)) + name: + description: + - Name to give the load balancer + default: null + algorithm: + description: + - algorithm for the balancer being created + choices: ['RANDOM', 'LEAST_CONNECTIONS', 'ROUND_ROBIN', 'WEIGHTED_LEAST_CONNECTIONS', 'WEIGHTED_ROUND_ROBIN'] + default: LEAST_CONNECTIONS + port: + description: + - Port for the balancer being created + default: 80 + protocol: + description: + - Protocol for the balancer being created + choices: ['DNS_TCP', 'DNS_UDP' ,'FTP', 'HTTP', 'HTTPS', 'IMAPS', 'IMAPv4', 'LDAP', 'LDAPS', 'MYSQL', 'POP3', 'POP3S', 'SMTP', 'TCP', 'TCP_CLIENT_FIRST', 'UDP', 'UDP_STREAM', 'SFTP'] + default: HTTP + timeout: + description: + - timeout for communication between the balancer and the node + default: 30 + type: + description: + - type of interface for the balancer being created + choices: ['PUBLIC', 'SERVICENET'] + default: PUBLIC + region: + description: + - Region to create the load balancer in + default: DFW + wait: + description: + - wait for the balancer to be in state 'running' before returning + default: "no" + choices: [ "yes", "no" ] + wait_timeout: + description: + - how long before wait gives up, in seconds + default: 300 +requirements: [ "pyrax" ] +author: Christopher H. Laco, Jesse Keating +notes: + - The following environment variables can be used, C(RAX_USERNAME), + C(RAX_API_KEY), C(RAX_CREDS), C(RAX_CREDENTIALS), C(RAX_REGION). + - C(RAX_CREDENTIALS) and C(RAX_CREDS) points to a credentials file + appropriate for pyrax + - C(RAX_USERNAME) and C(RAX_API_KEY) obviate the use of a credentials file + - C(RAX_REGION) defines a Rackspace Public Cloud region (DFW, ORD, LON, ...) +''' + +EXAMPLES = ''' +- name: Build a Load Balancer + gather_facts: False + + tasks: + - name: Provision Load Balancer + rax_lb: + credentials: ~/.raxpub + name: my-lb + port: 9000 + protocol: HTTP + type: SERVICENET + timeout: 30 + region: DFW + wait: yes + state: present + register: my_lb +''' + +import sys +import os +import time + +try: + import pyrax + import pyrax.utils + from pyrax import exc +except ImportError: + print("failed=True msg='pyrax required for this module'") + sys.exit(1) + + +def cloud_load_balancer(module, state, name, meta, algorithm, port, protocol, type, timeout, + wait, wait_timeout): + for arg in (state, name, port, protocol, type): + if not arg: + module.fail_json(msg='%s is required for cloud_loadbalancers' % arg) + + changed = False + balancer = None + balancers = [] + instances = [] + + for balancer in pyrax.cloud_loadbalancers.list(): + if name != balancer.name: + continue + + balancers.append(balancer) + + if state == 'present': + if not balancers: + try: + balancers=[pyrax.cloud_loadbalancers.create( + name, + metadata=meta, + algorithm=algorithm, + port=port, + protocol=protocol, + timeout=timeout, + virtual_ips=[pyrax.cloud_loadbalancers.VirtualIP(type=type)] + )] + changed = True + except Exception, e: + module.fail_json(msg='%s' % e.message) + + for balancer in balancers: + if wait: + pyrax.utils.wait_for_build(balancer, interval=5, attempts=5) + + if balancer.status == 'ACTIVE': + instance = dict( + id=balancer.id, + algorithm=balancer.algorithm, + cluster=balancer.cluster, + connectionLogging=balancer.connectionLogging, + contentCaching=balancer.contentCaching, + halfClosed=balancer.halfClosed, + port=balancer.port, + protocol=balancer.protocol, + sourceAddresses=balancer.sourceAddresses, + name=balancer.name, + timeout=balancer.timeout, + status=balancer.status + ) + instances.append(instance) + elif balancer.status == 'ERROR': + module.fail_json(msg='%s failed to build' % balancer.id) + elif wait: + module.fail_json(msg='Timeout waiting on %s' % balancer.id) + + elif state == 'absent': + if balancer: + try: + balancer.delete() + changed = True + except Exception, e: + module.fail_json(msg='%s' % e.message) + + instance = dict( + id=balancer.id, + algorithm=balancer.algorithm, + cluster=None, + connectionLogging=None, + contentCaching=None, + halfClosed=None, + port=balancer.port, + protocol=balancer.protocol, + sourceAddresses=None, + name=balancer.name, + timeout=balancer.timeout, + status=balancer.status + ) + instances.append(instance) + + module.exit_json(changed=changed, balancers=instances) + + +def main(): + module = AnsibleModule( + argument_spec=dict( + state=dict(default='present', + choices=['present', 'absent']), + credentials=dict(aliases=['creds_file']), + api_key=dict(), + username=dict(), + region=dict(), + name=dict(), + meta=dict(type='dict', default={}), + algorithm=dict(choices=['RANDOM', 'LEAST_CONNECTIONS', 'ROUND_ROBIN', 'WEIGHTED_LEAST_CONNECTIONS', 'WEIGHTED_ROUND_ROBIN'], default='LEAST_CONNECTIONS'), + port=dict(type='int', default=80), + protocol=dict(choices=['DNS_TCP', 'DNS_UDP' ,'FTP', 'HTTP', 'HTTPS', 'IMAPS', 'IMAPv4', 'LDAP', 'LDAPS', 'MYSQL', 'POP3', 'POP3S', 'SMTP', 'TCP', 'TCP_CLIENT_FIRST', 'UDP', 'UDP_STREAM', 'SFTP'], default='HTTP'), + type=dict(choices=['PUBLIC', 'SERVICENET'], default='PUBLIC'), + timeout=dict(type='int', default=30), + wait=dict(type='bool'), + wait_timeout=dict(default=300) + ) + ) + + credentials = module.params.get('credentials') + api_key = module.params.get('api_key') + username = module.params.get('username') + region = module.params.get('region') + state = module.params.get('state') + name = module.params.get('name') + meta = module.params.get('meta') + algorithm = module.params.get('algorithm') + port = module.params.get('port') + protocol = module.params.get('protocol') + type = module.params.get('type') + timeout = int(module.params.get('timeout')) + wait = module.params.get('wait') + wait_timeout = int(module.params.get('wait_timeout')) + + try: + username = username or os.environ.get('RAX_USERNAME') + api_key = api_key or os.environ.get('RAX_API_KEY') + credentials = (credentials or + os.environ.get('RAX_CREDENTIALS') or + os.environ.get('RAX_CREDS_FILE')) + region = region or os.environ.get('RAX_REGION') + + except KeyError, e: + module.fail_json(msg='Unable to load %s' % e.message) + + try: + pyrax.set_setting("identity_type", "rackspace") + if api_key and username: + pyrax.set_credentials(username, api_key=api_key, region=region) + elif credentials: + credentials = os.path.expanduser(credentials) + pyrax.set_credential_file(credentials, region=region) + else: + raise Exception('No credentials supplied!') + except Exception, e: + module.fail_json(msg='%s' % e.message) + + cloud_load_balancer(module, state, name, meta, algorithm, port, protocol, type, timeout, wait, wait_timeout) + +# this is magic, see lib/ansible/module_common.py +#<> + +main() diff --git a/library/cloud/rax_clb_nodes b/library/cloud/rax_clb_nodes index 9b850e9c81..ed6f3badab 100644 --- a/library/cloud/rax_clb_nodes +++ b/library/cloud/rax_clb_nodes @@ -16,7 +16,7 @@ DOCUMENTATION = ''' --- -module: rax_clb +module: rax_clb_nodes short_description: add, modify and remove nodes from a Rackspace Cloud Load Balancer description: - Adds, modifies and removes nodes from a Rackspace Cloud Load Balancer From 772fbb8d0a21eabf154269f3f0da5e5f3e2896eb Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Wed, 6 Nov 2013 11:39:47 -0600 Subject: [PATCH 3/5] Updates to rax_clb module --- library/cloud/rax_clb | 309 +++++++++++++++++++++++++----------------- 1 file changed, 184 insertions(+), 125 deletions(-) diff --git a/library/cloud/rax_clb b/library/cloud/rax_clb index 498ab1414e..404f689140 100644 --- a/library/cloud/rax_clb +++ b/library/cloud/rax_clb @@ -22,70 +22,74 @@ description: - creates / deletes a Rackspace Public Cloud load balancer. version_added: "1.4" options: - state: - description: - - Indicate desired state of the resource - choices: ['present', 'absent'] - default: present - credentials: - description: - - File to find the Rackspace credentials in (ignored if C(api_key) and - C(username) are provided) - default: null - aliases: ['creds_file'] - api_key: - description: - - Rackspace API key (overrides C(credentials)) - username: - description: - - Rackspace username (overrides C(credentials)) - name: - description: - - Name to give the load balancer - default: null algorithm: description: - - algorithm for the balancer being created + - algorithm for the balancer being created choices: ['RANDOM', 'LEAST_CONNECTIONS', 'ROUND_ROBIN', 'WEIGHTED_LEAST_CONNECTIONS', 'WEIGHTED_ROUND_ROBIN'] default: LEAST_CONNECTIONS + api_key: + description: + - Rackspace API key (overrides C(credentials)) + credentials: + description: + - File to find the Rackspace credentials in (ignored if C(api_key) and + C(username) are provided) + default: null + aliases: ['creds_file'] + meta: + description: + - A hash of metadata to associate with the instance + default: null + name: + description: + - Name to give the load balancer + default: null port: description: - - Port for the balancer being created + - Port for the balancer being created default: 80 protocol: description: - - Protocol for the balancer being created + - Protocol for the balancer being created choices: ['DNS_TCP', 'DNS_UDP' ,'FTP', 'HTTP', 'HTTPS', 'IMAPS', 'IMAPv4', 'LDAP', 'LDAPS', 'MYSQL', 'POP3', 'POP3S', 'SMTP', 'TCP', 'TCP_CLIENT_FIRST', 'UDP', 'UDP_STREAM', 'SFTP'] default: HTTP + region: + description: + - Region to create the load balancer in + default: DFW + state: + description: + - Indicate desired state of the resource + choices: ['present', 'absent'] + default: present timeout: description: - - timeout for communication between the balancer and the node + - timeout for communication between the balancer and the node default: 30 type: description: - - type of interface for the balancer being created + - type of interface for the balancer being created choices: ['PUBLIC', 'SERVICENET'] default: PUBLIC - region: + username: description: - - Region to create the load balancer in - default: DFW + - Rackspace username (overrides C(credentials)) wait: description: - - wait for the balancer to be in state 'running' before returning + - wait for the balancer to be in state 'running' before returning default: "no" choices: [ "yes", "no" ] wait_timeout: description: - - how long before wait gives up, in seconds + - how long before wait gives up, in seconds default: 300 requirements: [ "pyrax" ] -author: Christopher H. Laco, Jesse Keating +author: Christopher H. Laco, Matt Martz notes: - The following environment variables can be used, C(RAX_USERNAME), - C(RAX_API_KEY), C(RAX_CREDS), C(RAX_CREDENTIALS), C(RAX_REGION). - - C(RAX_CREDENTIALS) and C(RAX_CREDS) points to a credentials file - appropriate for pyrax + C(RAX_API_KEY), C(RAX_CREDS_FILE), C(RAX_CREDENTIALS), C(RAX_REGION). + - C(RAX_CREDENTIALS) and C(RAX_CREDS_FILE) points to a credentials file + appropriate for pyrax. See U(https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md#authenticating) - C(RAX_USERNAME) and C(RAX_API_KEY) obviate the use of a credentials file - C(RAX_REGION) defines a Rackspace Public Cloud region (DFW, ORD, LON, ...) ''' @@ -93,169 +97,224 @@ notes: EXAMPLES = ''' - name: Build a Load Balancer gather_facts: False - + hosts: local + connection: local tasks: - - name: Provision Load Balancer - rax_lb: + - name: Load Balancer create request + local_action: + module: rax_clb credentials: ~/.raxpub name: my-lb - port: 9000 + port: 8080 protocol: HTTP type: SERVICENET timeout: 30 region: DFW wait: yes state: present + meta: + app: my-cool-app register: my_lb ''' import sys import os -import time + +from types import NoneType try: import pyrax - import pyrax.utils - from pyrax import exc except ImportError: print("failed=True msg='pyrax required for this module'") sys.exit(1) +NON_CALLABLES = (basestring, bool, dict, int, list, NoneType) +ALGORITHMS = ['RANDOM', 'LEAST_CONNECTIONS', 'ROUND_ROBIN', + 'WEIGHTED_LEAST_CONNECTIONS', 'WEIGHTED_ROUND_ROBIN'] +PROTOCOLS = ['DNS_TCP', 'DNS_UDP', 'FTP', 'HTTP', 'HTTPS', 'IMAPS', 'IMAPv4', + 'LDAP', 'LDAPS', 'MYSQL', 'POP3', 'POP3S', 'SMTP', 'TCP', + 'TCP_CLIENT_FIRST', 'UDP', 'UDP_STREAM', 'SFTP'] -def cloud_load_balancer(module, state, name, meta, algorithm, port, protocol, type, timeout, - wait, wait_timeout): - for arg in (state, name, port, protocol, type): + +def cloud_load_balancer(module, state, name, meta, algorithm, port, protocol, + vip_type, timeout, wait, wait_timeout): + for arg in (state, name, port, protocol, vip_type): if not arg: - module.fail_json(msg='%s is required for cloud_loadbalancers' % arg) + module.fail_json(msg='%s is required for rax_clb' % arg) + + if int(timeout) < 30: + module.fail_json(msg='"timeout" must be greater than or equal to 30') changed = False - balancer = None balancers = [] - instances = [] - for balancer in pyrax.cloud_loadbalancers.list(): - if name != balancer.name: + clb = pyrax.cloud_loadbalancers + + for balancer in clb.list(): + if name != balancer.name and name != balancer.id: continue balancers.append(balancer) + if len(balancers) > 1: + module.fail_json(msg='Multiple Load Balancers were matched by name, ' + 'try using the Load Balancer ID instead') + if state == 'present': + if isinstance(meta, dict): + metadata = [dict(key=k, value=v) for k, v in meta.items()] + if not balancers: try: - balancers=[pyrax.cloud_loadbalancers.create( - name, - metadata=meta, - algorithm=algorithm, - port=port, - protocol=protocol, - timeout=timeout, - virtual_ips=[pyrax.cloud_loadbalancers.VirtualIP(type=type)] - )] + virtual_ips = [clb.VirtualIP(type=vip_type)] + balancer = clb.create(name, metadata=metadata, port=port, + algorithm=algorithm, protocol=protocol, + timeout=timeout, virtual_ips=virtual_ips) changed = True except Exception, e: module.fail_json(msg='%s' % e.message) + else: + balancer = balancers[0] + setattr(balancer, 'metadata', + [dict(key=k, value=v) for k, v in + balancer.get_metadata().items()]) + atts = { + 'name': name, + 'algorithm': algorithm, + 'port': port, + 'protocol': protocol, + 'timeout': timeout + } + for att, value in atts.iteritems(): + current = getattr(balancer, att) + if current != value: + changed = True - for balancer in balancers: - if wait: - pyrax.utils.wait_for_build(balancer, interval=5, attempts=5) + if changed: + balancer.update(**atts) - if balancer.status == 'ACTIVE': - instance = dict( - id=balancer.id, - algorithm=balancer.algorithm, - cluster=balancer.cluster, - connectionLogging=balancer.connectionLogging, - contentCaching=balancer.contentCaching, - halfClosed=balancer.halfClosed, - port=balancer.port, - protocol=balancer.protocol, - sourceAddresses=balancer.sourceAddresses, - name=balancer.name, - timeout=balancer.timeout, - status=balancer.status - ) - instances.append(instance) - elif balancer.status == 'ERROR': - module.fail_json(msg='%s failed to build' % balancer.id) - elif wait: - module.fail_json(msg='Timeout waiting on %s' % balancer.id) + if balancer.metadata != metadata: + balancer.set_metadata(meta) + changed = True + + virtual_ips = [clb.VirtualIP(type=vip_type)] + current_vip_types = set([v.type for v in balancer.virtual_ips]) + vip_types = set([v.type for v in virtual_ips]) + if current_vip_types != vip_types: + module.fail_json(msg='Load balancer Virtual IP type cannot ' + 'be changed') + + if wait: + attempts = wait_timeout / 5 + print attempts + pyrax.utils.wait_for_build(balancer, interval=5, attempts=attempts) + + balancer.get() + instance = {} + for key, value in vars(balancer).iteritems(): + if key == 'virtual_ips': + virtual_ips = [] + instance[key] = [] + for vip in value: + vip_dict = {} + for vip_key, vip_value in vars(vip).iteritems(): + if isinstance(vip_value, NON_CALLABLES): + vip_dict[vip_key] = vip_value + instance[key].append(vip_dict) + elif (isinstance(value, NON_CALLABLES) and + not key.startswith('_')): + instance[key] = value + + result = dict(changed=changed, balancer=instance) + + if balancer.status == 'ERROR': + result['msg'] = '%s failed to build' % balancer.id + elif wait and balancer.status not in ('ACTIVE', 'ERROR'): + result['msg'] = 'Timeout waiting on %s' % balancer.id + + if 'msg' in result: + module.fail_json(**result) + else: + module.exit_json(**result) elif state == 'absent': - if balancer: + if balancers: + balancer = balancers[0] try: balancer.delete() changed = True except Exception, e: module.fail_json(msg='%s' % e.message) - instance = dict( - id=balancer.id, - algorithm=balancer.algorithm, - cluster=None, - connectionLogging=None, - contentCaching=None, - halfClosed=None, - port=balancer.port, - protocol=balancer.protocol, - sourceAddresses=None, - name=balancer.name, - timeout=balancer.timeout, - status=balancer.status - ) - instances.append(instance) + instance = {} + for key, value in vars(balancer).iteritems(): + if key == 'virtual_ips': + virtual_ips = [] + instance[key] = [] + for vip in value: + vip_dict = {} + for vip_key, vip_value in vars(vip).iteritems(): + if isinstance(vip_value, NON_CALLABLES): + vip_dict[vip_key] = vip_value + instance[key].append(vip_dict) + elif (isinstance(value, NON_CALLABLES) and + not key.startswith('_')): + instance[key] = value - module.exit_json(changed=changed, balancers=instances) + if wait: + attempts = wait_timeout / 5 + pyrax.utils.wait_until(balancer, 'status', ('DELETED'), + interval=5, attempts=attempts) + + module.exit_json(changed=changed, balancer=instance) def main(): module = AnsibleModule( argument_spec=dict( - state=dict(default='present', - choices=['present', 'absent']), - credentials=dict(aliases=['creds_file']), + algorithm=dict(choices=ALGORITHMS, default='LEAST_CONNECTIONS'), api_key=dict(), - username=dict(), - region=dict(), - name=dict(), + credentials=dict(aliases=['creds_file']), meta=dict(type='dict', default={}), - algorithm=dict(choices=['RANDOM', 'LEAST_CONNECTIONS', 'ROUND_ROBIN', 'WEIGHTED_LEAST_CONNECTIONS', 'WEIGHTED_ROUND_ROBIN'], default='LEAST_CONNECTIONS'), + name=dict(), port=dict(type='int', default=80), - protocol=dict(choices=['DNS_TCP', 'DNS_UDP' ,'FTP', 'HTTP', 'HTTPS', 'IMAPS', 'IMAPv4', 'LDAP', 'LDAPS', 'MYSQL', 'POP3', 'POP3S', 'SMTP', 'TCP', 'TCP_CLIENT_FIRST', 'UDP', 'UDP_STREAM', 'SFTP'], default='HTTP'), - type=dict(choices=['PUBLIC', 'SERVICENET'], default='PUBLIC'), + protocol=dict(choices=PROTOCOLS, default='HTTP'), + region=dict(), + state=dict(default='present', choices=['present', 'absent']), timeout=dict(type='int', default=30), + type=dict(choices=['PUBLIC', 'SERVICENET'], default='PUBLIC'), + username=dict(), wait=dict(type='bool'), - wait_timeout=dict(default=300) - ) + wait_timeout=dict(default=300), + ), ) - credentials = module.params.get('credentials') - api_key = module.params.get('api_key') - username = module.params.get('username') - region = module.params.get('region') - state = module.params.get('state') - name = module.params.get('name') - meta = module.params.get('meta') algorithm = module.params.get('algorithm') + api_key = module.params.get('api_key') + credentials = module.params.get('credentials') + meta = module.params.get('meta') + name = module.params.get('name') port = module.params.get('port') protocol = module.params.get('protocol') - type = module.params.get('type') + region = module.params.get('region') + state = module.params.get('state') timeout = int(module.params.get('timeout')) + username = module.params.get('username') + vip_type = module.params.get('type') wait = module.params.get('wait') wait_timeout = int(module.params.get('wait_timeout')) try: username = username or os.environ.get('RAX_USERNAME') api_key = api_key or os.environ.get('RAX_API_KEY') - credentials = (credentials or - os.environ.get('RAX_CREDENTIALS') or + credentials = (credentials or os.environ.get('RAX_CREDENTIALS') or os.environ.get('RAX_CREDS_FILE')) region = region or os.environ.get('RAX_REGION') - except KeyError, e: module.fail_json(msg='Unable to load %s' % e.message) try: - pyrax.set_setting("identity_type", "rackspace") + pyrax.set_setting('identity_type', 'rackspace') if api_key and username: pyrax.set_credentials(username, api_key=api_key, region=region) elif credentials: @@ -266,9 +325,9 @@ def main(): except Exception, e: module.fail_json(msg='%s' % e.message) - cloud_load_balancer(module, state, name, meta, algorithm, port, protocol, type, timeout, wait, wait_timeout) + cloud_load_balancer(module, state, name, meta, algorithm, port, protocol, + vip_type, timeout, wait, wait_timeout) -# this is magic, see lib/ansible/module_common.py -#<> +from ansible.module_utils.basic import * main() From 9d684c9e8e527a39e747d194ec79cb08ea88d59b Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Thu, 7 Nov 2013 10:46:50 -0600 Subject: [PATCH 4/5] If you try to delete a balancer that isn't there, don't blow up --- library/cloud/rax_clb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/cloud/rax_clb b/library/cloud/rax_clb index 404f689140..76564db59c 100644 --- a/library/cloud/rax_clb +++ b/library/cloud/rax_clb @@ -265,6 +265,8 @@ def cloud_load_balancer(module, state, name, meta, algorithm, port, protocol, attempts = wait_timeout / 5 pyrax.utils.wait_until(balancer, 'status', ('DELETED'), interval=5, attempts=attempts) + else: + instance = {} module.exit_json(changed=changed, balancer=instance) From 2065253c3e97469ff8cb82291218982515cea31f Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Thu, 7 Nov 2013 12:56:59 -0600 Subject: [PATCH 5/5] Rename rax_clb to rax_clb_nodes in docs and examples --- library/cloud/rax_clb_nodes | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/cloud/rax_clb_nodes b/library/cloud/rax_clb_nodes index ed6f3badab..c26206c528 100644 --- a/library/cloud/rax_clb_nodes +++ b/library/cloud/rax_clb_nodes @@ -107,7 +107,7 @@ notes: EXAMPLES = ''' # Add a new node to the load balancer - local_action: - module: rax_clb + module: rax_clb_nodes load_balancer_id: 71 address: 10.2.2.3 port: 80 @@ -118,7 +118,7 @@ EXAMPLES = ''' # Drain connections from a node - local_action: - module: rax_clb + module: rax_clb_nodes load_balancer_id: 71 node_id: 410 condition: draining @@ -127,7 +127,7 @@ EXAMPLES = ''' # Remove a node from the load balancer - local_action: - module: rax_clb + module: rax_clb_nodes load_balancer_id: 71 node_id: 410 state: absent