From 10a50b4a61ee324d59bac92f9b382b8337e32029 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Sat, 2 Aug 2014 15:05:42 -0700 Subject: [PATCH] Split nova floating-ip pool logic The desires around getting a floating ip associated with a pool and getting a floating ip not associated with a pool is just different enough that following it as one set of nested ifs is tricky. Split the function into two, one for the pool and one for the non-pool logic. --- library/cloud/nova_compute | 194 ++++++++++++++++++++----------------- 1 file changed, 107 insertions(+), 87 deletions(-) diff --git a/library/cloud/nova_compute b/library/cloud/nova_compute index ed717c8262..f7fd55123e 100644 --- a/library/cloud/nova_compute +++ b/library/cloud/nova_compute @@ -193,21 +193,116 @@ def _delete_server(module, nova): module.fail_json(msg = "Timed out waiting for server to get deleted, please check manually") +def _add_floating_ip_from_pool(module, nova, server, floating_ip_obj): + + # empty dict and list + usable_floating_ips = {} + pools = [] + + # user specified + pools = module.params['floating_ip']['pools'] + + # get the list of all floating IPs. Mileage may + # vary according to Nova Compute configuration + # per cloud provider + all_floating_ips = floating_ip_obj.list() + + # iterate through all pools of IP address. Empty + # string means all and is the default value + for pool in pools: + # temporary list per pool + pool_ips = [] + # loop through all floating IPs + for f_ip in all_floating_ips: + # if not reserved and the correct pool, add + if f_ip.instance_id is None and (f_ip.pool == pool): + pool_ips.append(f_ip.ip) + # only need one + break + + # if the list is empty, add for this pool + if not pool_ips: + try: + new_ip = nova.floating_ips.create(pool) + except Exception, e: + module.fail_json(msg = "Unable to create floating ip") + pool_ips.append(new_ip.ip) + # Add to the main list + usable_floating_ips[pool] = pool_ips + + # finally, add ip(s) to instance for each pool + for pool in usable_floating_ips: + for ip in usable_floating_ips[pool]: + try: + server.add_floating_ip(ip) + # We only need to assign one ip - but there is an inherent + # race condition and some other cloud operation may have + # stolen an available floating ip + break + except Exception, e: + module.fail_json(msg = "Error attaching IP %s to instance %s: %s " % (ip, server.id, e.message)) + + +def _add_floating_ip_no_pool(module, nova, server, floating_ip_obj): + + usable_floating_ips = list() + + # if there is a list of IP addresses, make that the list + if module.params['floating_ip'].has_key('ips'): + usable_floating_ips = module.params['floating_ip']['ips'] + else: + # get the list of all floating IPs. Mileage may + # vary according to Nova Compute configuration + # per cloud provider + for f_ip in floating_ip_obj.list(): + # if not reserved and the correct pool, add + if f_ip.instance_id is None: + usable_floating_ips.append(f_ip.ip) + + if not usable_floating_ips: + try: + new_ip = nova.floating_ips.create() + except Exception, e: + module.fail_json(msg = "Unable to create floating ip") + usable_floating_ips.append(new_ip.ip) + + # finally, add ip(s) to instance + for ip in usable_floating_ips: + try: + server.add_floating_ip(ip) + except Exception, e: + module.fail_json(msg = "Error attaching IP %s to instance %s: %s " % (ip, server.id, e.message)) + + +def _add_floating_ip(module, nova, server): + # instantiate FloatingIPManager object + floating_ip_obj = floating_ips.FloatingIPManager(nova) + + if module.params['floating_ip'].has_key('pools'): + _add_floating_ip_from_pool(module, nova, server, floating_ip_obj) + else: + _add_floating_ip_no_pool(module, nova, server, floating_ip_obj) + + # this may look redundant, but if there is now a + # floating IP, then it needs to be obtained from + # a recent server object if the above code path exec'd + try: + server = nova.servers.get(server.id) + except Exception, e: + module.fail_json(msg = "Error in getting info from instance: %s " % e.message) + return server + + def _create_server(module, nova): # issue an error early on and not launch the instance - if module.params['floating_ip'] != None: + if module.params['floating_ip'] is not None: if module.params['floating_ip'].has_key('ips'): # can't specify "ips" and "auto" both - if module.params['floating_ip'].has_key('auto') and \ - module.params['floating_ip']['auto'] is True: - err_msg = "For floating_ips - " - err_msg += "you cannot specify both 'auto' and 'ips'!" - module.fail_json(msg = err_msg) + if module.params['floating_ip'].has_key('auto') and module.params['floating_ip']['auto']: + module.fail_json(msg = "For floating_ips - you cannot specify both 'auto' and 'ips'!") # can't specify "ips" and "pools" both if module.params['floating_ip'].has_key('pools'): - err_msg = "For floating_ips - " - err_msg += "you cannot specify both 'ips' and 'pools'!" - module.fail_json(msg = err_msg) + module.fail_json(msg = "For floating_ips - you cannot specify both 'pools' and 'ips'!") bootargs = [module.params['name'], module.params['image_id'], module.params['flavor_id']] bootkwargs = { @@ -231,86 +326,11 @@ def _create_server(module, nova): try: server = nova.servers.get(server.id) except Exception, e: - module.fail_json(msg = \ - "Error in getting info from instance: %s"\ - % e.message - ) + module.fail_json( msg = "Error in getting info from instance: %s" % e.message) if server.status == 'ACTIVE': # if floating_ip is specified, then attach - if module.params['floating_ip'] != None: - # instantiate FloatingIPManager object - floating_ip_obj = floating_ips.FloatingIPManager(nova) - # empty dict and list - usable_floating_ips = {} - pools = [] - - # if floating_ip pools are defined, then make that - # the list of pools - if module.params['floating_ip'].has_key('pools'): - # user specified - pools = module.params['floating_ip']['pools'] - else: - # otherwise all - pools = [''] - - # if there is a list of IP addresses, make that the list - if module.params['floating_ip'].has_key('ips'): - usable_floating_ips[''] = \ - module.params['floating_ip']['ips'] - - # if 'auto', then assign automatically, no pool needed - if module.params['floating_ip'].has_key('auto') and \ - module.params['floating_ip']['auto'] is True: - # get the list of all floating IPs. Mileage may - # vary according to Nova Compute configuration - # per cloud provider - all_floating_ips = floating_ip_obj.list() - - # iterate through all pools of IP address. Empty - # string means all and is the default value - for pool in pools: - # temporary list per pool - pool_ips = [] - # loop through all floating IPs - for f_ip in all_floating_ips: - # if not reserved and the correct pool, add - if f_ip.instance_id == None and \ - (f_ip.pool == pool or pool == ''): - pool_ips.append(f_ip.ip) - # one per pool - break - # if the list is empty, add for this pool - if len(pool_ips) == 0: - try: - new_ip = nova.floating_ips.create(pool) - except Exception, e: - module.fail_json(msg = \ - "Unable to create \ - floating ip") - pool_ips.append(new_ip.ip) - # Add to the main list - usable_floating_ips[pool] = pool_ips - - # finally, add ip(s) to instance for each pool - for pool in usable_floating_ips: - for ip in usable_floating_ips[pool]: - try: - server.add_floating_ip(ip) - except Exception, e: - module.fail_json(msg = \ - "Error attaching IP %s to \ - instance %s: %s " % \ - (ip, server.id, e.message)) - - # this may look redundant, but if there is now a - # floating IP, then it needs to be obtained from - # a recent server object if the above code path exec'd - try: - server = nova.servers.get(server.id) - except Exception, e: - module.fail_json(msg = \ - "Error in getting info from \ - instance: %s " % e.message) + if module.params['floating_ip'] is not None: + server = _add_floating_ip(module, nova, server) private = [ x['addr'] for x in getattr(server, 'addresses').itervalues().next() if 'OS-EXT-IPS:type' in x and x['OS-EXT-IPS:type'] == 'fixed'] public = [ x['addr'] for x in getattr(server, 'addresses').itervalues().next() if 'OS-EXT-IPS:type' in x and x['OS-EXT-IPS:type'] == 'floating']