From 61e5b8e7da3b36db9a09f80d62ebf2e276bbe88b Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Fri, 21 Oct 2011 00:29:54 -0700 Subject: Revert how APIs get IP address info for instances Fixes bug 862839 listing instances with IPs is extremely inefficient after changes were made to query the network manager for IP information for each instance. I tried adding a network manager call that said 'give me IP information for 'x' instances', but that was also too slow. We need a solution that caches IP info from the network manager before we can fully untie things. So, this reverts APIs to use instance['fixed_ips'] which hasn't been untied in the DB yet. Change-Id: I37d21105d6306f0a812c5eb0f0717a5094cd17b9 --- nova/api/ec2/cloud.py | 96 +++++++++++++++++--------------------------- nova/api/openstack/common.py | 62 +++++++++++++++------------- 2 files changed, 70 insertions(+), 88 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index b0130a0ad..a2bd2c32f 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -238,58 +238,43 @@ class CloudController(object): utils.runthis(_("Generating root CA: %s"), "sh", genrootca_sh_path) os.chdir(start) - def _get_floaters_for_fixed_ip(self, context, fixed_ip): - """Return all floating IPs given a fixed IP""" - return self.network_api.get_floating_ips_by_fixed_address(context, - fixed_ip) - - def _get_fixed_ips_for_instance(self, context, instance): + def _get_ip_info_for_instance(self, context, instance): """Return a list of all fixed IPs for an instance""" - ret_ips = [] - ret_ip6s = [] - nw_info = self.network_api.get_instance_nw_info(context, instance) - for net, info in nw_info: - if not info: + ip_info = dict(fixed_ips=[], fixed_ip6s=[], floating_ips=[]) + + fixed_ips = instance['fixed_ips'] + for fixed_ip in fixed_ips: + fixed_addr = fixed_ip['address'] + network = fixed_ip.get('network') + vif = fixed_ip.get('virtual_interface') + if not network or not vif: + name = instance['name'] + ip = fixed_ip['address'] + LOG.warn(_("Instance %(name)s has stale IP " + "address: %(ip)s (no network or vif)") % locals()) continue - ips = info.get('ips', []) - for ip in ips: - try: - ret_ips.append(ip['ip']) - except KeyError: - pass - if FLAGS.use_ipv6: - ip6s = info.get('ip6s', []) - for ip6 in ip6s: - try: - ret_ip6s.append(ip6['ip']) - except KeyError: - pass - return (ret_ips, ret_ip6s) - - def _get_floaters_for_instance(self, context, instance, return_all=True): - """Return all floating IPs for an instance""" - - ret_floaters = [] - # only loop through ipv4 addresses - fixed_ips = self._get_fixed_ips_for_instance(context, instance)[0] - for ip in fixed_ips: - floaters = self._get_floaters_for_fixed_ip(context, ip) - # Allows a short circuit if we just need any floater. - if floaters and not return_all: - return floaters - ret_floaters.extend(floaters) - if floaters and only_one: - return ret_floaters - return ret_floaters + cidr_v6 = network.get('cidr_v6') + if FLAGS.use_ipv6 and cidr_v6: + ipv6_addr = ipv6.to_global(cidr_v6, vif['address'], + network['project_id']) + if ipv6_addr not in ip_info['fixed_ip6s']: + ip_info['fixed_ip6s'].append(ipv6_addr) + + for floating_ip in fixed_ip.get('floating_ips', []): + float_addr = floating_ip['address'] + ip_info['floating_ips'].append(float_addr) + ip_info['fixed_ips'].append(fixed_addr) + return ip_info def _get_mpi_data(self, context, project_id): result = {} search_opts = {'project_id': project_id, 'deleted': False} for instance in self.compute_api.get_all(context, search_opts=search_opts): + ip_info = self._get_ip_info_for_instance(context, instance) # only look at ipv4 addresses - fixed_ips = self._get_fixed_ips_for_instance(context, instance)[0] + fixed_ips = ip_info['fixed_ips'] if fixed_ips: line = '%s slots=%d' % (fixed_ips[0], instance['vcpus']) key = str(instance['key_name']) @@ -378,9 +363,9 @@ class CloudController(object): host = instance_ref['host'] availability_zone = self._get_availability_zone_by_host(ctxt, host) - floaters = self._get_floaters_for_instance(ctxt, instance_ref, - return_all=False) - floating_ip = floaters and floaters[0] or '' + ip_info = self._get_ip_info_for_instance(ctxt, instance_ref) + floating_ips = ip_info['floating_ips'] + floating_ip = floating_ips and floating_ips[0] or '' ec2_id = ec2utils.id_to_ec2_id(instance_ref['id']) image_ec2_id = self.image_ec2_id(instance_ref['image_ref']) @@ -1309,20 +1294,13 @@ class CloudController(object): fixed_ip = None floating_ip = None - (fixed_ips, fixed_ip6s) = self._get_fixed_ips_for_instance(context, - instance) - if fixed_ips: - fixed_ip = fixed_ips[0] - # Now look for a floater. - for ip in fixed_ips: - floating_ips = self._get_floaters_for_fixed_ip(context, ip) - # NOTE(comstud): Will it float? - if floating_ips: - floating_ip = floating_ips[0] - # Got one, exit out. - break - if fixed_ip6s: - i['dnsNameV6'] = fixed_ip6s[0] + ip_info = self._get_ip_info_for_instance(context, instance) + if ip_info['fixed_ips']: + fixed_ip = ip_info['fixed_ips'][0] + if ip_info['floating_ips']: + floating_ip = ip_info['floating_ips'][0] + if ip_info['fixed_ip6s']: + i['dnsNameV6'] = ip_info['fixed_ip6s'][0] i['privateDnsName'] = fixed_ip i['privateIpAddress'] = fixed_ip i['publicDnsName'] = floating_ip diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 45987d40e..0f3f1fff7 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -16,9 +16,10 @@ # under the License. import functools -from lxml import etree import re import urlparse + +from lxml import etree import webob from xml.dom import minidom @@ -28,6 +29,7 @@ from nova.compute import vm_states from nova.compute import task_states from nova import exception from nova import flags +from nova import ipv6 from nova import log as logging import nova.network from nova import quota @@ -293,40 +295,42 @@ def get_networks_for_instance(context, instance): ...} """ - network_api = nova.network.API() - - def _get_floats(ip): - return network_api.get_floating_ips_by_fixed_address(context, ip) - def _emit_addr(ip, version): return {'addr': ip, 'version': version} - nw_info = network_api.get_instance_nw_info(context, instance) - networks = {} - for net, info in nw_info: - if not info: - continue - try: - network = {'ips': []} - network['floating_ips'] = [] - for ip in info['ips']: - network['ips'].append(_emit_addr(ip['ip'], 4)) - floats = [_emit_addr(addr, 4) - for addr in _get_floats(ip['ip'])] - network['floating_ips'].extend(floats) - if FLAGS.use_ipv6 and 'ip6s' in info: - network['ips'].extend([_emit_addr(ip['ip'], 6) - for ip in info['ip6s']]) - # NOTE(comstud): These exception checks are for lp830817 - # (Restoring them after a refactoring removed) - except TypeError: - raise + fixed_ips = instance['fixed_ips'] + ipv6_addrs_seen = {} + for fixed_ip in fixed_ips: + fixed_addr = fixed_ip['address'] + network = fixed_ip['network'] + vif = fixed_ip.get('virtual_interface') + if not network or not vif: + name = instance['name'] + ip = fixed_ip['address'] + LOG.warn(_("Instance %(name)s has stale IP " + "address: %(ip)s (no network or vif)") % locals()) continue - except KeyError: - raise + label = network.get('label', None) + if label is None: continue - networks[info['label']] = network + if label not in networks: + networks[label] = {'ips': [], 'floating_ips': []} + nw_dict = networks[label] + cidr_v6 = network.get('cidr_v6') + if FLAGS.use_ipv6 and cidr_v6: + ipv6_addr = ipv6.to_global(cidr_v6, vif['address'], + network['project_id']) + # Only add same IPv6 address once. It's possible we've + # seen it before if there was a previous fixed_ip with + # same network and vif as this one + if not ipv6_addrs_seen.get(ipv6_addr): + nw_dict['ips'].append(_emit_addr(ipv6_addr, 6)) + ipv6_addrs_seen[ipv6_addr] = True + nw_dict['ips'].append(_emit_addr(fixed_addr, 4)) + for floating_ip in fixed_ip.get('floating_ips', []): + float_addr = floating_ip['address'] + nw_dict['floating_ips'].append(_emit_addr(float_addr, 4)) return networks -- cgit