From 585fd5a138fa1664f4b594b7656a53e5a90ad95b Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Tue, 10 Jun 2014 10:08:12 -0500 Subject: [PATCH 1/4] Move additional rackspace common code into module_utils/rax.py --- lib/ansible/module_utils/rax.py | 145 +++++++++++++++++++++++++++++- library/cloud/rax | 83 ++--------------- library/cloud/rax_cbs | 20 +---- library/cloud/rax_cbs_attachments | 48 +--------- library/cloud/rax_clb | 46 ++-------- library/cloud/rax_clb_nodes | 18 +--- library/cloud/rax_dns | 15 +--- library/cloud/rax_dns_record | 21 +---- library/cloud/rax_facts | 20 +---- library/cloud/rax_files | 5 -- library/cloud/rax_files_objects | 2 - library/cloud/rax_identity | 5 -- library/cloud/rax_keypair | 15 +--- library/cloud/rax_scaling_group | 67 ++------------ library/cloud/rax_scaling_policy | 19 +--- 15 files changed, 175 insertions(+), 354 deletions(-) diff --git a/lib/ansible/module_utils/rax.py b/lib/ansible/module_utils/rax.py index 98623c7d38e..9c02bd3a594 100644 --- a/lib/ansible/module_utils/rax.py +++ b/lib/ansible/module_utils/rax.py @@ -26,7 +26,147 @@ # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import os +from uuid import UUID + + +FINAL_STATUSES = ('ACTIVE', 'ERROR') +VOLUME_STATUS = ('available', 'attaching', 'creating', 'deleting', 'in-use', + 'error', 'error_deleting') + +CLB_ALGORITHMS = ['RANDOM', 'LEAST_CONNECTIONS', 'ROUND_ROBIN', + 'WEIGHTED_LEAST_CONNECTIONS', 'WEIGHTED_ROUND_ROBIN'] +CLB_PROTOCOLS = ['DNS_TCP', 'DNS_UDP', 'FTP', 'HTTP', 'HTTPS', 'IMAPS', + 'IMAPv4', 'LDAP', 'LDAPS', 'MYSQL', 'POP3', 'POP3S', 'SMTP', + 'TCP', 'TCP_CLIENT_FIRST', 'UDP', 'UDP_STREAM', 'SFTP'] + +NON_CALLABLES = (basestring, bool, dict, int, list, type(None)) +PUBLIC_NET_ID = "00000000-0000-0000-0000-000000000000" +SERVICE_NET_ID = "11111111-1111-1111-1111-111111111111" + + +def rax_slugify(value): + """Prepend a key with rax_ and normalize the key name""" + return 'rax_%s' % (re.sub('[^\w-]', '_', value).lower().lstrip('_')) + + +def rax_clb_node_to_dict(obj): + """Function to convert a CLB Node object to a dict""" + if not obj: + return {} + node = obj.to_dict() + node['id'] = obj.id + node['weight'] = obj.weight + return node + + +def rax_to_dict(obj, obj_type='standard'): + """Generic function to convert a pyrax object to a dict + + obj_type values: + standard + clb + server + + """ + instance = {} + for key in dir(obj): + value = getattr(obj, key) + if obj_type == 'clb' and key == 'nodes': + instance[key] = [] + for node in value: + instance[key].append(rax_clb_node_to_dict(node)) + elif (isinstance(value, list) and len(value) > 0 and + not isinstance(value[0], NON_CALLABLES)): + instance[key] = [] + for item in value: + instance[key].append(rax_to_dict(item)) + elif (isinstance(value, NON_CALLABLES) and not key.startswith('_')): + if obj_type == 'server': + key = rax_slugify(key) + instance[key] = value + + if obj_type == 'server': + for attr in ['id', 'accessIPv4', 'name', 'status']: + instance[attr] = instance.get(rax_slugify(attr)) + + return instance + + +def rax_find_image(module, rax_module, image): + cs = rax_module.cloudservers + try: + UUID(image) + except ValueError: + try: + image = cs.images.find(human_id=image) + except(cs.exceptions.NotFound, + cs.exceptions.NoUniqueMatch): + try: + image = cs.images.find(name=image) + except (cs.exceptions.NotFound, + cs.exceptions.NoUniqueMatch): + module.fail_json(msg='No matching image found (%s)' % + image) + + return rax_module.utils.get_id(image) + + +def rax_find_volume(module, rax_module, name): + cbs = rax_module.cloud_blockstorage + try: + UUID(name) + volume = cbs.get(name) + except ValueError: + try: + volume = cbs.find(name=name) + except rax_module.exc.NotFound: + volume = None + except Exception, e: + module.fail_json(msg='%s' % e) + return volume + + +def rax_find_network(module, rax_module, network): + cnw = rax_module.cloud_networks + try: + UUID(network) + except ValueError: + if network.lower() == 'public': + return cnw.get_server_networks(PUBLIC_NET_ID) + elif network.lower() == 'private': + return cnw.get_server_networks(SERVICE_NET_ID) + else: + try: + network_obj = cnw.find_network_by_label(network) + except (rax_module.exceptions.NetworkNotFound, + rax_module.exceptions.NetworkLabelNotUnique): + module.fail_json(msg='No matching network found (%s)' % + network) + else: + return cnw.get_server_networks(network_obj) + else: + return cnw.get_server_networks(network) + + +def rax_find_server(module, rax_module, server): + cs = rax_module.cloudservers + try: + UUID(server) + server = cs.servers.get(server) + except ValueError: + servers = cs.servers.list(search_opts=dict(name='^%s$' % server)) + if not servers: + module.fail_json(msg='No Server was matched by name, ' + 'try using the Server ID instead') + if len(servers) > 1: + module.fail_json(msg='Multiple servers matched by name, ' + 'try using the Server ID instead') + + # We made it this far, grab the first and hopefully only server + # in the list + server = servers[0] + return server + def rax_argument_spec(): return dict( @@ -48,6 +188,9 @@ def rax_required_together(): def setup_rax_module(module, rax_module): + rax_module.USER_AGENT = 'ansible/%s %s' % (ANSIBLE_VERSION, + rax_module.USER_AGENT) + api_key = module.params.get('api_key') auth_endpoint = module.params.get('auth_endpoint') credentials = module.params.get('credentials') diff --git a/library/cloud/rax b/library/cloud/rax index f7ea8e9c276..35156a01db2 100644 --- a/library/cloud/rax +++ b/library/cloud/rax @@ -194,44 +194,12 @@ EXAMPLES = ''' register: rax ''' -import os -import re -import time - -from uuid import UUID -from types import NoneType - try: import pyrax HAS_PYRAX = True except ImportError: HAS_PYRAX = False -ACTIVE_STATUSES = ('ACTIVE', 'BUILD', 'HARD_REBOOT', 'MIGRATING', 'PASSWORD', - 'REBOOT', 'REBUILD', 'RESCUE', 'RESIZE', 'REVERT_RESIZE') -FINAL_STATUSES = ('ACTIVE', 'ERROR') -NON_CALLABLES = (basestring, bool, dict, int, list, NoneType) -PUBLIC_NET_ID = "00000000-0000-0000-0000-000000000000" -SERVICE_NET_ID = "11111111-1111-1111-1111-111111111111" - - -def rax_slugify(value): - return 'rax_%s' % (re.sub('[^\w-]', '_', value).lower().lstrip('_')) - - -def server_to_dict(obj): - instance = {} - for key in dir(obj): - value = getattr(obj, key) - if (isinstance(value, NON_CALLABLES) and not key.startswith('_')): - key = rax_slugify(key) - instance[key] = value - - for attr in ['id', 'accessIPv4', 'name', 'status']: - instance[attr] = instance.get(rax_slugify(attr)) - - return instance - def create(module, names=[], flavor=None, image=None, meta={}, key_name=None, files={}, wait=True, wait_timeout=300, disk_config=None, @@ -299,7 +267,7 @@ def create(module, names=[], flavor=None, image=None, meta={}, key_name=None, server.get() except: server.status == 'ERROR' - instance = server_to_dict(server) + instance = rax_to_dict(server, 'server') if server.status == 'ACTIVE' or not wait: success.append(instance) elif server.status == 'ERROR': @@ -307,7 +275,7 @@ def create(module, names=[], flavor=None, image=None, meta={}, key_name=None, elif wait: timeout.append(instance) - untouched = [server_to_dict(s) for s in existing] + untouched = [rax_to_dict(s, 'server') for s in existing] instances = success + untouched results = { @@ -354,7 +322,7 @@ def delete(module, instance_ids=[], wait=True, wait_timeout=300, kept=[]): else: changed = True - instance = server_to_dict(server) + instance = rax_to_dict(server, 'server') instances[instance['id']] = instance # If requested, wait for server deletion @@ -384,7 +352,7 @@ def delete(module, instance_ids=[], wait=True, wait_timeout=300, kept=[]): success = filter(lambda s: s['status'] in ('', 'DELETED'), instances.values()) - instances = [server_to_dict(s) for s in kept] + instances = [rax_to_dict(s, 'server') for s in kept] results = { 'changed': changed, @@ -451,48 +419,13 @@ def cloudservers(module, state=None, name=None, flavor=None, image=None, state = 'present' was_absent = True - # Check if the provided image is a UUID and if not, search for an - # appropriate image using human_id and name if image: - try: - UUID(image) - except ValueError: - try: - image = cs.images.find(human_id=image) - except(cs.exceptions.NotFound, - cs.exceptions.NoUniqueMatch): - try: - image = cs.images.find(name=image) - except (cs.exceptions.NotFound, - cs.exceptions.NoUniqueMatch): - module.fail_json(msg='No matching image found (%s)' % - image) - - image = pyrax.utils.get_id(image) + image = rax_find_image(module, pyrax, image) - # Check if the provided network is a UUID and if not, search for an - # appropriate network using label nics = [] if networks: for network in networks: - try: - UUID(network) - except ValueError: - if network.lower() == 'public': - nics.extend(cnw.get_server_networks(PUBLIC_NET_ID)) - elif network.lower() == 'private': - nics.extend(cnw.get_server_networks(SERVICE_NET_ID)) - else: - try: - network_obj = cnw.find_network_by_label(network) - except (pyrax.exceptions.NetworkNotFound, - pyrax.exceptions.NetworkLabelNotUnique): - module.fail_json(msg='No matching network found (%s)' % - network) - else: - nics.extend(cnw.get_server_networks(network_obj)) - else: - nics.extend(cnw.get_server_networks(network)) + nics.extend(rax_find_network(module, pyrax, network)) # act on the state if state == 'present': @@ -568,7 +501,7 @@ def cloudservers(module, state=None, name=None, flavor=None, image=None, instances = [] instance_ids = [] for server in servers: - instances.append(server_to_dict(server)) + instances.append(rax_to_dict(server, 'server')) instance_ids.append(server.id) module.exit_json(changed=False, action=None, instances=instances, @@ -623,7 +556,7 @@ def cloudservers(module, state=None, name=None, flavor=None, image=None, if len(servers) >= count: instances = [] for server in servers: - instances.append(server_to_dict(server)) + instances.append(rax_to_dict(server, 'server')) instance_ids = [i['id'] for i in instances] module.exit_json(changed=False, action=None, diff --git a/library/cloud/rax_cbs b/library/cloud/rax_cbs index ade2b83eacf..a1b6ce46a6e 100644 --- a/library/cloud/rax_cbs +++ b/library/cloud/rax_cbs @@ -99,21 +99,12 @@ EXAMPLES = ''' register: my_volume ''' -import sys - -from uuid import UUID -from types import NoneType - try: import pyrax HAS_PYRAX = True except ImportError: HAS_PYRAX = False -NON_CALLABLES = (basestring, bool, dict, int, list, NoneType) -VOLUME_STATUS = ('available', 'attaching', 'creating', 'deleting', 'in-use', - 'error', 'error_deleting') - def cloud_block_storage(module, state, name, description, meta, size, snapshot_id, volume_type, wait, wait_timeout): @@ -135,16 +126,7 @@ def cloud_block_storage(module, state, name, description, meta, size, 'typically indicates an invalid region or an ' 'incorrectly capitalized region name.') - try: - UUID(name) - volume = cbs.get(name) - except ValueError: - try: - volume = cbs.find(name=name) - except pyrax.exc.NotFound: - pass - except Exception, e: - module.fail_json(msg='%s' % e) + volume = rax_find_volume(module, pyrax, name) if state == 'present': if not volume: diff --git a/library/cloud/rax_cbs_attachments b/library/cloud/rax_cbs_attachments index bc7dba9eec2..365f93cd6e2 100644 --- a/library/cloud/rax_cbs_attachments +++ b/library/cloud/rax_cbs_attachments @@ -81,19 +81,12 @@ EXAMPLES = ''' register: my_volume ''' -import sys - -from uuid import UUID -from types import NoneType - try: import pyrax HAS_PYRAX = True except ImportError: HAS_PYRAX = False -NON_CALLABLES = (basestring, bool, dict, int, list, NoneType) - def cloud_block_storage_attachments(module, state, volume, server, device, wait, wait_timeout): @@ -113,34 +106,13 @@ def cloud_block_storage_attachments(module, state, volume, server, device, changed = False instance = {} - try: - UUID(volume) - volume = cbs.get(volume) - except ValueError: - try: - volume = cbs.find(name=volume) - except Exception, e: - module.fail_json(msg='%s' % e) + volume = rax_find_volume(module, pyrax, volume) if not volume: module.fail_json(msg='No matching storage volumes were found') if state == 'present': - try: - UUID(server) - server = cs.servers.get(server) - except ValueError: - servers = cs.servers.list(search_opts=dict(name='^%s$' % server)) - if not servers: - module.fail_json(msg='No Server was matched by name, ' - 'try using the Server ID instead') - if len(servers) > 1: - module.fail_json(msg='Multiple servers matched by name, ' - 'try using the Server ID instead') - - # We made it this far, grab the first and hopefully only server - # in the list - server = servers[0] + server = rax_find_server(module, pyrax, server) if (volume.attachments and volume.attachments[0]['server_id'] == server.id): @@ -176,21 +148,7 @@ def cloud_block_storage_attachments(module, state, volume, server, device, module.exit_json(**result) elif state == 'absent': - try: - UUID(server) - server = cs.servers.get(server) - except ValueError: - servers = cs.servers.list(search_opts=dict(name='^%s$' % server)) - if not servers: - module.fail_json(msg='No Server was matched by name, ' - 'try using the Server ID instead') - if len(servers) > 1: - module.fail_json(msg='Multiple servers matched by name, ' - 'try using the Server ID instead') - - # We made it this far, grab the first and hopefully only server - # in the list - server = servers[0] + server = rax_find_server(module, pyrax, server) if (volume.attachments and volume.attachments[0]['server_id'] == server.id): diff --git a/library/cloud/rax_clb b/library/cloud/rax_clb index 85700895c7c..7a2699709da 100644 --- a/library/cloud/rax_clb +++ b/library/cloud/rax_clb @@ -130,7 +130,6 @@ EXAMPLES = ''' register: my_lb ''' -from types import NoneType try: import pyrax @@ -138,42 +137,6 @@ try: except ImportError: HAS_PYRAX = False -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 node_to_dict(obj): - node = obj.to_dict() - node['id'] = obj.id - return node - - -def to_dict(obj): - instance = {} - for key in dir(obj): - value = getattr(obj, key) - if key == '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 key == 'nodes': - instance[key] = [] - for node in value: - instance[key].append(node_to_dict(node)) - elif (isinstance(value, NON_CALLABLES) and - not key.startswith('_')): - instance[key] = value - - return instance - def cloud_load_balancer(module, state, name, meta, algorithm, port, protocol, vip_type, timeout, wait, wait_timeout, vip_id): @@ -252,7 +215,7 @@ def cloud_load_balancer(module, state, name, meta, algorithm, port, protocol, pyrax.utils.wait_for_build(balancer, interval=5, attempts=attempts) balancer.get() - instance = to_dict(balancer) + instance = rax_to_dict(balancer, 'clb') result = dict(changed=changed, balancer=instance) @@ -275,7 +238,7 @@ def cloud_load_balancer(module, state, name, meta, algorithm, port, protocol, except Exception, e: module.fail_json(msg='%s' % e.message) - instance = to_dict(balancer) + instance = rax_to_dict(balancer, 'clb') if wait: attempts = wait_timeout / 5 @@ -291,11 +254,12 @@ def main(): argument_spec = rax_argument_spec() argument_spec.update( dict( - algorithm=dict(choices=ALGORITHMS, default='LEAST_CONNECTIONS'), + algorithm=dict(choices=CLB_ALGORITHMS, + default='LEAST_CONNECTIONS'), meta=dict(type='dict', default={}), name=dict(), port=dict(type='int', default=80), - protocol=dict(choices=PROTOCOLS, default='HTTP'), + protocol=dict(choices=CLB_PROTOCOLS, default='HTTP'), state=dict(default='present', choices=['present', 'absent']), timeout=dict(type='int', default=30), type=dict(choices=['PUBLIC', 'SERVICENET'], default='PUBLIC'), diff --git a/library/cloud/rax_clb_nodes b/library/cloud/rax_clb_nodes index dc0950dca58..24325b44597 100644 --- a/library/cloud/rax_clb_nodes +++ b/library/cloud/rax_clb_nodes @@ -120,8 +120,6 @@ EXAMPLES = ''' credentials: /path/to/credentials ''' -import os - try: import pyrax HAS_PYRAX = True @@ -167,20 +165,6 @@ def _get_primary_nodes(lb): return nodes -def _node_to_dict(node): - """Return a dictionary containing node details""" - if not node: - return {} - return { - 'address': node.address, - 'condition': node.condition, - 'id': node.id, - 'port': node.port, - 'type': node.type, - 'weight': node.weight, - } - - def main(): argument_spec = rax_argument_spec() argument_spec.update( @@ -241,7 +225,7 @@ def main(): node = _get_node(lb, node_id, address, port) - result = _node_to_dict(node) + result = rax_clb_node_to_dict(node) if state == 'absent': if not node: # Removing a non-existent node diff --git a/library/cloud/rax_dns b/library/cloud/rax_dns index 57bc75acf5a..e726851d609 100644 --- a/library/cloud/rax_dns +++ b/library/cloud/rax_dns @@ -66,25 +66,12 @@ EXAMPLES = ''' register: rax_dns ''' -from types import NoneType - try: import pyrax HAS_PYRAX = True except ImportError: HAS_PYRAX = False -NON_CALLABLES = (basestring, bool, dict, int, list, NoneType) - - -def to_dict(obj): - instance = {} - for key in dir(obj): - value = getattr(obj, key) - if (isinstance(value, NON_CALLABLES) and not key.startswith('_')): - instance[key] = value - return instance - def rax_dns(module, comment, email, name, state, ttl): changed = False @@ -144,7 +131,7 @@ def rax_dns(module, comment, email, name, state, ttl): except Exception, e: module.fail_json(msg='%s' % e.message) - module.exit_json(changed=changed, domain=to_dict(domain)) + module.exit_json(changed=changed, domain=rax_to_dict(domain)) def main(): diff --git a/library/cloud/rax_dns_record b/library/cloud/rax_dns_record index dc85f2ef45d..05c7a8fc054 100644 --- a/library/cloud/rax_dns_record +++ b/library/cloud/rax_dns_record @@ -113,30 +113,15 @@ EXAMPLES = ''' register: ptr_record ''' -from uuid import UUID - try: import pyrax HAS_PYRAX = True except ImportError: HAS_PYRAX = False -NON_CALLABLES = (basestring, bool, dict, int, list, type(None)) - - -def to_dict(obj): - instance = {} - for key in dir(obj): - value = getattr(obj, key) - if (isinstance(value, NON_CALLABLES) and not key.startswith('_')): - instance[key] = value - return instance - - -def rax_dns_record_ptr(module, data=None, comment=None, loadbalancer=None, - name=None, server=None, state='present', ttl=7200): - """Function for manipulating DNS PTR records""" +def rax_dns_record(module, data=None, comment=None, loadbalancer=None, + name=None, server=None, state='present', ttl=7200): changed = False results = [] @@ -301,7 +286,7 @@ def rax_dns_record(module, comment=None, data=None, domain=None, name=None, except Exception, e: module.fail_json(msg='%s' % e.message) - module.exit_json(changed=changed, record=to_dict(record)) + module.exit_json(changed=changed, record=rax_to_dict(record)) def main(): diff --git a/library/cloud/rax_facts b/library/cloud/rax_facts index 64711f41519..68ef446f760 100644 --- a/library/cloud/rax_facts +++ b/library/cloud/rax_facts @@ -55,30 +55,12 @@ EXAMPLES = ''' ansible_ssh_host: "{{ rax_accessipv4 }}" ''' -from types import NoneType - try: import pyrax HAS_PYRAX = True except ImportError: HAS_PYRAX = False -NON_CALLABLES = (basestring, bool, dict, int, list, NoneType) - - -def rax_slugify(value): - return 'rax_%s' % (re.sub('[^\w-]', '_', value).lower().lstrip('_')) - - -def pyrax_object_to_dict(obj): - instance = {} - for key in dir(obj): - value = getattr(obj, key) - if (isinstance(value, NON_CALLABLES) and not key.startswith('_')): - key = rax_slugify(key) - instance[key] = value - return instance - def rax_facts(module, address, name, server_id): changed = False @@ -120,7 +102,7 @@ def rax_facts(module, address, name, server_id): module.fail_json(msg='Multiple servers found matching provided ' 'search parameters') elif len(servers) == 1: - ansible_facts = pyrax_object_to_dict(servers[0]) + ansible_facts = rax_to_dict(servers[0], 'server') module.exit_json(changed=changed, ansible_facts=ansible_facts) diff --git a/library/cloud/rax_files b/library/cloud/rax_files index 68e28a07f74..3c54b0a9e2f 100644 --- a/library/cloud/rax_files +++ b/library/cloud/rax_files @@ -139,8 +139,6 @@ EXAMPLES = ''' file_for: "" ''' -from ansible import __version__ - try: import pyrax HAS_PYRAX = True @@ -149,7 +147,6 @@ except ImportError, e: EXIT_DICT = dict(success=True) META_PREFIX = 'x-container-meta-' -USER_AGENT = "Ansible/%s via pyrax" % __version__ def _get_container(module, cf, container): @@ -321,8 +318,6 @@ def cloudfiles(module, container_, state, meta_, clear_meta, typ, ttl, public, 'typically indicates an invalid region or an ' 'incorrectly capitalized region name.') - cf.user_agent = USER_AGENT - if typ == "container": container(cf, module, container_, state, meta_, clear_meta, ttl, public, private, web_index, web_error) diff --git a/library/cloud/rax_files_objects b/library/cloud/rax_files_objects index d7f11900ab9..de8400c5c52 100644 --- a/library/cloud/rax_files_objects +++ b/library/cloud/rax_files_objects @@ -183,8 +183,6 @@ EXAMPLES = ''' rax_files_objects: container=testcont type=meta ''' -import os - try: import pyrax HAS_PYRAX = True diff --git a/library/cloud/rax_identity b/library/cloud/rax_identity index 591cd018e70..9de4db96611 100644 --- a/library/cloud/rax_identity +++ b/library/cloud/rax_identity @@ -47,8 +47,6 @@ EXAMPLES = ''' register: rackspace_identity ''' -from types import NoneType - try: import pyrax HAS_PYRAX = True @@ -56,9 +54,6 @@ except ImportError: HAS_PYRAX = False -NON_CALLABLES = (basestring, bool, dict, int, list, NoneType) - - def cloud_identity(module, state, identity): for arg in (state, identity): if not arg: diff --git a/library/cloud/rax_keypair b/library/cloud/rax_keypair index 2e347268a6b..591ad8c3597 100644 --- a/library/cloud/rax_keypair +++ b/library/cloud/rax_keypair @@ -84,25 +84,12 @@ EXAMPLES = ''' register: keypair ''' -from types import NoneType - try: import pyrax HAS_PYRAX = True except ImportError: HAS_PYRAX = False -NON_CALLABLES = (basestring, bool, dict, int, list, NoneType) - - -def to_dict(obj): - instance = {} - for key in dir(obj): - value = getattr(obj, key) - if (isinstance(value, NON_CALLABLES) and not key.startswith('_')): - instance[key] = value - return instance - def rax_keypair(module, name, public_key, state): changed = False @@ -149,7 +136,7 @@ def rax_keypair(module, name, public_key, state): except Exception, e: module.fail_json(msg='%s' % e.message) - module.exit_json(changed=changed, keypair=to_dict(keypair)) + module.exit_json(changed=changed, keypair=rax_to_dict(keypair)) def main(): diff --git a/library/cloud/rax_scaling_group b/library/cloud/rax_scaling_group index 2c635592b77..d884d3c1303 100644 --- a/library/cloud/rax_scaling_group +++ b/library/cloud/rax_scaling_group @@ -118,34 +118,12 @@ EXAMPLES = ''' register: asg ''' -import os - -from uuid import UUID - try: import pyrax HAS_PYRAX = True except ImportError: HAS_PYRAX = False -NON_CALLABLES = (basestring, bool, dict, int, list, type(None)) -PUBLIC_NET_ID = "00000000-0000-0000-0000-000000000000" -SERVICE_NET_ID = "11111111-1111-1111-1111-111111111111" - - -def asg_to_dict(obj): - instance = {} - for key in dir(obj): - value = getattr(obj, key) - if key == 'policies' and isinstance(value, list): - policies = [] - for policy in value: - policies.append(asg_to_dict(policy)) - instance[key] = policies - elif (isinstance(value, NON_CALLABLES) and not key.startswith('_')): - instance[key] = value - return instance - def rax_asg(module, cooldown=300, disk_config=None, files={}, flavor=None, image=None, key_name=None, loadbalancers=[], meta={}, @@ -172,48 +150,13 @@ def rax_asg(module, cooldown=300, disk_config=None, files={}, flavor=None, elif not isinstance(v, basestring): meta[k] = '%s' % v - # Check if the provided image is a UUID and if not, search for an - # appropriate image using human_id and name if image: - try: - UUID(image) - except ValueError: - try: - image = cs.images.find(human_id=image) - except(cs.exceptions.NotFound, - cs.exceptions.NoUniqueMatch): - try: - image = cs.images.find(name=image) - except (cs.exceptions.NotFound, - cs.exceptions.NoUniqueMatch): - module.fail_json(msg='No matching image found (%s)' % - image) - - image = pyrax.utils.get_id(image) - - # Check if the provided network is a UUID and if not, search for an - # appropriate network using label + image = rax_find_image(module, pyrax, image) + nics = [] if networks: for network in networks: - try: - UUID(network) - except ValueError: - if network.lower() == 'public': - nics.extend(cnw.get_server_networks(PUBLIC_NET_ID)) - elif network.lower() == 'private': - nics.extend(cnw.get_server_networks(SERVICE_NET_ID)) - else: - try: - network_obj = cnw.find_network_by_label(network) - except (pyrax.exceptions.NetworkNotFound, - pyrax.exceptions.NetworkLabelNotUnique): - module.fail_json(msg='No matching network found ' - '(%s)' % network) - else: - nics.extend(cnw.get_server_networks(network_obj)) - else: - nics.extend(cnw.get_server_networks(network)) + nics.extend(rax_find_network(module, pyrax, network)) for nic in nics: # pyrax is currently returning net-id, but we need uuid @@ -322,7 +265,7 @@ def rax_asg(module, cooldown=300, disk_config=None, files={}, flavor=None, sg.get() - module.exit_json(changed=changed, autoscale_group=asg_to_dict(sg)) + module.exit_json(changed=changed, autoscale_group=rax_to_dict(sg)) else: try: @@ -334,7 +277,7 @@ def rax_asg(module, cooldown=300, disk_config=None, files={}, flavor=None, except Exception, e: module.fail_json(msg='%s' % e.message) - module.exit_json(changed=changed, autoscale_group=asg_to_dict(sg)) + module.exit_json(changed=changed, autoscale_group=rax_to_dict(sg)) def main(): diff --git a/library/cloud/rax_scaling_policy b/library/cloud/rax_scaling_policy index 19174ba370c..b3da82460d8 100644 --- a/library/cloud/rax_scaling_policy +++ b/library/cloud/rax_scaling_policy @@ -118,27 +118,12 @@ EXAMPLES = ''' register: asp_webhook ''' -from uuid import UUID - try: import pyrax HAS_PYRAX = True except ImportError: HAS_PYRAX = False -NON_CALLABLES = (basestring, bool, dict, int, list, type(None)) -PUBLIC_NET_ID = "00000000-0000-0000-0000-000000000000" -SERVICE_NET_ID = "11111111-1111-1111-1111-111111111111" - - -def to_dict(obj): - instance = {} - for key in dir(obj): - value = getattr(obj, key) - if (isinstance(value, NON_CALLABLES) and not key.startswith('_')): - instance[key] = value - return instance - def rax_asp(module, at=None, change=0, cron=None, cooldown=300, desired_capacity=0, is_percent=False, name=None, @@ -220,7 +205,7 @@ def rax_asp(module, at=None, change=0, cron=None, cooldown=300, policy.get() - module.exit_json(changed=changed, autoscale_policy=to_dict(policy)) + module.exit_json(changed=changed, autoscale_policy=rax_to_dict(policy)) else: try: @@ -235,7 +220,7 @@ def rax_asp(module, at=None, change=0, cron=None, cooldown=300, except Exception, e: module.fail_json(msg='%s' % e.message) - module.exit_json(changed=changed, autoscale_policy=to_dict(policy)) + module.exit_json(changed=changed, autoscale_policy=rax_to_dict(policy)) def main(): From 88acb4875306d8561f844866166089279537ab68 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Tue, 10 Jun 2014 12:29:15 -0500 Subject: [PATCH 2/4] Add ANSIBLE_VERSION to module_utils/basic.py and manipulate the pyrax user-agent --- lib/ansible/module_common.py | 4 +++ lib/ansible/module_utils/basic.py | 2 ++ lib/ansible/module_utils/rax.py | 41 +++++++++++++++++-------------- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/lib/ansible/module_common.py b/lib/ansible/module_common.py index fc74e91f656..37d617812a0 100644 --- a/lib/ansible/module_common.py +++ b/lib/ansible/module_common.py @@ -25,11 +25,14 @@ import shlex from ansible import errors from ansible import utils from ansible import constants as C +from ansible import __version__ REPLACER = "#<>" REPLACER_ARGS = "\"<>\"" REPLACER_COMPLEX = "\"<>\"" REPLACER_WINDOWS = "# POWERSHELL_COMMON" +REPLACER_VERSION = "\"<>\"" + class ModuleReplacer(object): @@ -156,6 +159,7 @@ class ModuleReplacer(object): encoded_complex = repr(complex_args_json) # these strings should be part of the 'basic' snippet which is required to be included + module_data = module_data.replace(REPLACER_VERSION, repr(__version__)) module_data = module_data.replace(REPLACER_ARGS, encoded_args) module_data = module_data.replace(REPLACER_COMPLEX, encoded_complex) diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py index f06cb99282d..43fdc7426aa 100644 --- a/lib/ansible/module_utils/basic.py +++ b/lib/ansible/module_utils/basic.py @@ -29,6 +29,8 @@ # == BEGIN DYNAMICALLY INSERTED CODE == +ANSIBLE_VERSION = "<>" + MODULE_ARGS = "<>" MODULE_COMPLEX_ARGS = "<>" diff --git a/lib/ansible/module_utils/rax.py b/lib/ansible/module_utils/rax.py index 9c02bd3a594..3eada0268ec 100644 --- a/lib/ansible/module_utils/rax.py +++ b/lib/ansible/module_utils/rax.py @@ -1,30 +1,32 @@ # This code is part of Ansible, but is an independent component. # This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. +# Modules you write using this snippet, which is embedded dynamically by +# Ansible still belong to the author of the module, and may assign their own +# license to the complete work. # # Copyright (c), Michael DeHaan , 2012-2013 # All rights reserved. # -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. # -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. from uuid import UUID @@ -34,7 +36,7 @@ VOLUME_STATUS = ('available', 'attaching', 'creating', 'deleting', 'in-use', 'error', 'error_deleting') CLB_ALGORITHMS = ['RANDOM', 'LEAST_CONNECTIONS', 'ROUND_ROBIN', - 'WEIGHTED_LEAST_CONNECTIONS', 'WEIGHTED_ROUND_ROBIN'] + 'WEIGHTED_LEAST_CONNECTIONS', 'WEIGHTED_ROUND_ROBIN'] CLB_PROTOCOLS = ['DNS_TCP', 'DNS_UDP', 'FTP', 'HTTP', 'HTTPS', 'IMAPS', 'IMAPv4', 'LDAP', 'LDAPS', 'MYSQL', 'POP3', 'POP3S', 'SMTP', 'TCP', 'TCP_CLIENT_FIRST', 'UDP', 'UDP_STREAM', 'SFTP'] @@ -245,4 +247,7 @@ def setup_rax_module(module, rax_module): except Exception, e: module.fail_json(msg='%s' % e.message) + rax_module.USER_AGENT = 'ansible/%s %s' % (ANSIBLE_VERSION, + rax_module.USER_AGENT) + return rax_module From 28fcdec2dbf7275fbca878bd243e808f07481461 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Fri, 20 Jun 2014 10:23:45 -0500 Subject: [PATCH 3/4] Use new shared rax_to_dict and normalize the services key into something that makes sense --- library/cloud/rax_identity | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/library/cloud/rax_identity b/library/cloud/rax_identity index 9de4db96611..ea40ea2ef46 100644 --- a/library/cloud/rax_identity +++ b/library/cloud/rax_identity @@ -65,10 +65,8 @@ def cloud_identity(module, state, identity): ) changed = False - for key, value in vars(identity).iteritems(): - if (isinstance(value, NON_CALLABLES) and - not key.startswith('_')): - instance[key] = value + instance.update(rax_to_dict(identity)) + instance['services'] = instance.get('services', {}).keys() if state == 'present': if not identity.authenticated: From 7d0eba19ab55c312a50b351cce0a5b3016599026 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Wed, 6 Aug 2014 16:35:06 -0500 Subject: [PATCH 4/4] Dedupe PTR record related code in rax_dns_record --- lib/ansible/module_common.py | 1 - lib/ansible/module_utils/rax.py | 23 ++++++++++++++++++++ library/cloud/rax | 4 ++-- library/cloud/rax_dns_record | 38 ++++++++------------------------- 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/lib/ansible/module_common.py b/lib/ansible/module_common.py index 37d617812a0..8beff78d07d 100644 --- a/lib/ansible/module_common.py +++ b/lib/ansible/module_common.py @@ -33,7 +33,6 @@ REPLACER_COMPLEX = "\"<>\"" REPLACER_WINDOWS = "# POWERSHELL_COMMON" REPLACER_VERSION = "\"<>\"" - class ModuleReplacer(object): """ diff --git a/lib/ansible/module_utils/rax.py b/lib/ansible/module_utils/rax.py index 3eada0268ec..db8b6c4f3a4 100644 --- a/lib/ansible/module_utils/rax.py +++ b/lib/ansible/module_utils/rax.py @@ -170,6 +170,29 @@ def rax_find_server(module, rax_module, server): return server +def rax_find_loadbalancer(module, rax_module, loadbalancer): + clb = rax_module.cloud_loadbalancers + try: + UUID(loadbalancer) + found = clb.get(loadbalancer) + except: + for lb in clb.list(): + if loadbalancer == lb.name: + found.append(lb) + + if not found: + module.fail_json(msg='No loadbalancer was matched') + + if len(found) > 1: + module.fail_json(msg='Multiple loadbalancers matched') + + # We made it this far, grab the first and hopefully only item + # in the list + found = found[0] + + return found + + def rax_argument_spec(): return dict( api_key=dict(type='str', aliases=['password'], no_log=True), diff --git a/library/cloud/rax b/library/cloud/rax index 35156a01db2..d7db2c63d7e 100644 --- a/library/cloud/rax +++ b/library/cloud/rax @@ -219,7 +219,7 @@ def create(module, names=[], flavor=None, image=None, meta={}, key_name=None, except Exception, e: module.fail_json(msg='Failed to load %s' % user_data) - # Handle the file contents + # Handle the file contents for rpath in files.keys(): lpath = os.path.expanduser(files[rpath]) try: @@ -707,5 +707,5 @@ def main(): from ansible.module_utils.basic import * from ansible.module_utils.rax import * -### invoke the module +# invoke the module main() diff --git a/library/cloud/rax_dns_record b/library/cloud/rax_dns_record index 05c7a8fc054..561758f4fd8 100644 --- a/library/cloud/rax_dns_record +++ b/library/cloud/rax_dns_record @@ -120,43 +120,23 @@ except ImportError: HAS_PYRAX = False -def rax_dns_record(module, data=None, comment=None, loadbalancer=None, - name=None, server=None, state='present', ttl=7200): +def rax_dns_record_ptr(module, data=None, comment=None, loadbalancer=None, + name=None, server=None, state='present', ttl=7200): changed = False results = [] - cs = pyrax.cloudservers - clb = pyrax.cloud_loadbalancers dns = pyrax.cloud_dns - if not cs or not clb or not dns: + if not dns: module.fail_json(msg='Failed to instantiate client. This ' 'typically indicates an invalid region or an ' 'incorrectly capitalized region name.') if loadbalancer: - try: - UUID(loadbalancer) - found = [clb.get(loadbalancer)] - except: - for lb in clb.list(): - if loadbalancer == lb.name: - found.append(lb) - - if len(found) != 1: - module.fail_json(msg='Could not match a loadbalancer with %s' % - loadbalancer) + item = rax_find_loadbalancer(module, pyrax, loadbalancer) elif server: - try: - UUID(server) - found = [cs.servers.get(server)] - except: - found = cs.servers.list(search_opts=dict(name='^%s$' % server)) - if len(found) != 1: - module.fail_json(msg='Could not match a server with %s' % - server) - - item = found[0] + item = rax_find_server(module, pyrax, server) + if state == 'present': current = dns.list_ptr_records(item) for record in current: @@ -169,10 +149,10 @@ def rax_dns_record(module, data=None, comment=None, loadbalancer=None, module.fail_json(msg='%s' % e.message) record.ttl = ttl record.name = name - results.append(to_dict(record)) + results.append(rax_to_dict(record)) break else: - results.append(to_dict(record)) + results.append(rax_to_dict(record)) break if not results: @@ -190,7 +170,7 @@ def rax_dns_record(module, data=None, comment=None, loadbalancer=None, current = dns.list_ptr_records(item) for record in current: if record.data == data: - results.append(to_dict(record)) + results.append(rax_to_dict(record)) break if results: