From bb867ce3948ddc23cf928ca3dda100a1a977896a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jason=20K=C3=B6lker?= Date: Wed, 4 Jan 2012 11:10:10 -0600 Subject: [PATCH] Implement BP untie-nova-network-models Fixes LP853979 Remove the FK references for network data. Remove unused db functions that used the FK's Update db functions to not joinload Update notification to optionally take network_info if compute has it Update EC2 Api to use the network cache, falling back to rpc.call Remove test_instance_get_project_vpn_joins which tests calls not used Change-Id: I1a01ccc5ebcf7efeafe014af62be893325bb0825 --- nova/api/ec2/cloud.py | 17 +- nova/api/ec2/ec2utils.py | 86 ++++-- nova/api/openstack/common.py | 105 +++++--- .../openstack/compute/contrib/floating_ips.py | 37 ++- nova/api/openstack/compute/views/addresses.py | 2 +- nova/compute/manager.py | 34 ++- nova/db/api.py | 24 +- nova/db/sqlalchemy/api.py | 154 +++-------- .../versions/070_sqlite_downgrade.sql | 103 +++++++ .../versions/070_sqlite_upgrade.sql | 99 +++++++ .../versions/070_untie_nova_network_models.py | 98 +++++++ nova/db/sqlalchemy/models.py | 24 +- nova/network/api.py | 6 + nova/network/linux_net.py | 65 +++-- nova/network/manager.py | 72 +++-- nova/tests/api/ec2/test_cloud.py | 49 ++-- .../compute/contrib/test_floating_ips.py | 45 +++- .../api/openstack/compute/test_servers.py | 252 ++++++++++++------ nova/tests/api/openstack/fakes.py | 46 +--- nova/tests/db/fakes.py | 8 - nova/tests/fake_network.py | 67 +++-- nova/tests/test_compute_utils.py | 2 +- nova/tests/test_db_api.py | 13 - nova/tests/test_libvirt.py | 11 +- nova/tests/test_linux_net.py | 99 ++++--- nova/tests/test_metadata.py | 4 - nova/tests/test_network.py | 61 +++-- nova/tests/test_xenapi.py | 9 +- nova/tests/vmwareapi/db_fakes.py | 6 - nova/utils.py | 14 +- nova/virt/firewall.py | 17 +- 31 files changed, 1074 insertions(+), 555 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/070_sqlite_downgrade.sql create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/070_sqlite_upgrade.sql create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/070_untie_nova_network_models.py diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 8049d3bd771c..bdeb7dc2e798 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -1217,20 +1217,17 @@ class CloudController(object): def format_addresses(self, context): addresses = [] - if context.is_admin: - iterator = db.floating_ip_get_all(context) - else: - iterator = db.floating_ip_get_all_by_project(context, - context.project_id) - for floating_ip_ref in iterator: + floaters = self.network_api.get_floating_ips_by_project(context) + for floating_ip_ref in floaters: if floating_ip_ref['project_id'] is None: continue address = floating_ip_ref['address'] ec2_id = None - if (floating_ip_ref['fixed_ip'] - and floating_ip_ref['fixed_ip']['instance']): - instance_id = floating_ip_ref['fixed_ip']['instance']['id'] - ec2_id = ec2utils.id_to_ec2_id(instance_id) + if floating_ip_ref['fixed_ip_id']: + fixed_id = floating_ip_ref['fixed_ip_id'] + fixed = self.network_api.get_fixed_ip(context, fixed_id) + if fixed['instance_id'] is not None: + ec2_id = ec2utils.id_to_ec2_id(fixed['instance_id']) address_rv = {'public_ip': address, 'instance_id': ec2_id} if context.is_admin: diff --git a/nova/api/ec2/ec2utils.py b/nova/api/ec2/ec2utils.py index f54a9f960a54..8f641e189141 100644 --- a/nova/api/ec2/ec2utils.py +++ b/nova/api/ec2/ec2utils.py @@ -20,8 +20,9 @@ import re from nova import exception from nova import flags -from nova import ipv6 from nova import log as logging +from nova import network +from nova.network import model as network_model FLAGS = flags.FLAGS @@ -64,34 +65,67 @@ def image_ec2_id(image_id, image_type='ami'): return "ami-00000000" +def get_ip_info_for_instance_from_cache(instance): + if (not instance.get('info_cache') or + not instance['info_cache'].get('network_info')): + # NOTE(jkoelker) Raising ValueError so that we trigger the + # fallback lookup + raise ValueError + + cached_info = instance['info_cache']['network_info'] + nw_info = network_model.NetworkInfo.hydrate(cached_info) + ip_info = dict(fixed_ips=[], fixed_ip6s=[], floating_ips=[]) + + for vif in nw_info: + vif_fixed_ips = vif.fixed_ips() + + fixed_ips = [ip['address'] + for ip in vif_fixed_ips if ip['version'] == 4] + fixed_ip6s = [ip['address'] + for ip in vif_fixed_ips if ip['version'] == 6] + floating_ips = [ip['address'] + for ip in vif.floating_ips()] + ip_info['fixed_ips'].extend(fixed_ips) + ip_info['fixed_ip6s'].extend(fixed_ip6s) + ip_info['floating_ips'].extend(floating_ips) + + return ip_info + + +def get_ip_for_instance_from_nwinfo(context, instance): + # NOTE(jkoelker) When the network_api starts returning the model, this + # can be refactored out into the above function + network_api = network.API() + + def _get_floaters(ip): + return network_api.get_floating_ips_by_fixed_address(context, ip) + + ip_info = dict(fixed_ips=[], fixed_ip6s=[], floating_ips=[]) + nw_info = network_api.get_instance_nw_info(context, instance) + + for _net, info in nw_info: + for ip in info['ips']: + ip_info['fixed_ips'].append(ip['ip']) + floaters = _get_floaters(ip['ip']) + if floaters: + ip_info['floating_ips'].extend(floaters) + if 'ip6s' in info: + for ip in info['ip6s']: + ip_info['fixed_ip6s'].append(ip['ip']) + return ip_info + + def get_ip_info_for_instance(context, instance): """Return a list of all fixed IPs for an instance""" - 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 - 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 + try: + return get_ip_info_for_instance_from_cache(instance) + except (ValueError, KeyError, AttributeError): + # NOTE(jkoelker) If the json load (ValueError) or the + # sqlalchemy FK (KeyError, AttributeError) + # fail fall back to calling out to he + # network api + return get_ip_for_instance_from_nwinfo(context, instance) def get_availability_zone_by_host(services, host): diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 5df2844ac88b..0ac46d96e3d5 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -28,8 +28,9 @@ from nova.api.openstack import xmlutil from nova.compute import vm_states from nova.compute import task_states from nova import flags -from nova import ipv6 from nova import log as logging +from nova import network +from nova.network import model as network_model from nova import quota @@ -286,6 +287,63 @@ def dict_to_query_str(params): return param_str.rstrip('&') +def get_networks_for_instance_from_cache(instance): + if (not instance.get('info_cache') or + not instance['info_cache'].get('network_info')): + # NOTE(jkoelker) Raising ValueError so that we trigger the + # fallback lookup + raise ValueError + + cached_info = instance['info_cache']['network_info'] + nw_info = network_model.NetworkInfo.hydrate(cached_info) + networks = {} + + for vif in nw_info: + ips = vif.fixed_ips() + floaters = vif.floating_ips() + label = vif['network']['label'] + if label not in networks: + networks[label] = {'ips': [], 'floating_ips': []} + + networks[label]['ips'].extend(ips) + networks[label]['floating_ips'].extend(floaters) + return networks + + +def get_networks_for_instance_from_nwinfo(context, instance): + # NOTE(jkoelker) When the network_api starts returning the model, this + # can be refactored out into the above function + network_api = network.API() + + def _get_floats(ip): + return network_api.get_floating_ips_by_fixed_address(context, ip) + + def _emit_addr(ip, version): + return {'address': ip, 'version': version} + + nw_info = network_api.get_instance_nw_info(context, instance) + networks = {} + for _net, info in nw_info: + net = {'ips': [], 'floating_ips': []} + for ip in info['ips']: + net['ips'].append(_emit_addr(ip['ip'], 4)) + floaters = _get_floats(ip['ip']) + if floaters: + net['floating_ips'].extend([_emit_addr(float, 4) + for float in floaters]) + if 'ip6s' in info: + for ip in info['ip6s']: + net['ips'].append(_emit_addr(ip['ip'], 6)) + + label = info['label'] + if label not in networks: + networks[label] = {'ips': [], 'floating_ips': []} + + networks[label]['ips'].extend(net['ips']) + networks[label]['floating_ips'].extend(net['floating_ips']) + return networks + + def get_networks_for_instance(context, instance): """Returns a prepared nw_info list for passing into the view builders @@ -298,43 +356,14 @@ def get_networks_for_instance(context, instance): ...} """ - def _emit_addr(ip, version): - return {'addr': ip, 'version': version} - - networks = {} - 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 - label = network.get('label', None) - if label is None: - continue - 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 + try: + return get_networks_for_instance_from_cache(instance) + except (ValueError, KeyError, AttributeError): + # NOTE(jkoelker) If the json load (ValueError) or the + # sqlalchemy FK (KeyError, AttributeError) + # fail fall back to calling out the the + # network api + return get_networks_for_instance_from_nwinfo(context, instance) def raise_http_conflict_for_instance_invalid_state(exc, action): diff --git a/nova/api/openstack/compute/contrib/floating_ips.py b/nova/api/openstack/compute/contrib/floating_ips.py index 2400f6c832b7..a7e29d5fb414 100644 --- a/nova/api/openstack/compute/contrib/floating_ips.py +++ b/nova/api/openstack/compute/contrib/floating_ips.py @@ -68,7 +68,7 @@ def _translate_floating_ip_view(floating_ip): except (TypeError, KeyError): result['fixed_ip'] = None try: - result['instance_id'] = floating_ip['fixed_ip']['instance']['uuid'] + result['instance_id'] = floating_ip['instance']['uuid'] except (TypeError, KeyError): result['instance_id'] = None return {'floating_ip': result} @@ -83,9 +83,35 @@ class FloatingIPController(object): """The Floating IPs API controller for the OpenStack API.""" def __init__(self): + self.compute_api = compute.API() self.network_api = network.API() super(FloatingIPController, self).__init__() + def _get_fixed_ip(self, context, fixed_ip_id): + if fixed_ip_id is None: + return None + try: + return self.network_api.get_fixed_ip(context, fixed_ip_id) + except exception.FixedIpNotFound: + return None + + def _get_instance(self, context, instance_id): + return self.compute_api.get(context, instance_id) + + def _set_metadata(self, context, floating_ip): + fixed_ip_id = floating_ip['fixed_ip_id'] + floating_ip['fixed_ip'] = self._get_fixed_ip(context, + fixed_ip_id) + instance_id = None + if floating_ip['fixed_ip']: + instance_id = floating_ip['fixed_ip']['instance_id'] + + if instance_id: + floating_ip['instance'] = self._get_instance(context, + instance_id) + else: + floating_ip['instance'] = None + @wsgi.serializers(xml=FloatingIPTemplate) def show(self, req, id): """Return data about the given floating ip.""" @@ -96,6 +122,8 @@ class FloatingIPController(object): except exception.NotFound: raise webob.exc.HTTPNotFound() + self._set_metadata(context, floating_ip) + return _translate_floating_ip_view(floating_ip) @wsgi.serializers(xml=FloatingIPsTemplate) @@ -105,6 +133,9 @@ class FloatingIPController(object): floating_ips = self.network_api.get_floating_ips_by_project(context) + for floating_ip in floating_ips: + self._set_metadata(context, floating_ip) + return _translate_floating_ips_view(floating_ips) @wsgi.serializers(xml=FloatingIPTemplate) @@ -134,7 +165,7 @@ class FloatingIPController(object): context = req.environ['nova.context'] floating_ip = self.network_api.get_floating_ip(context, id) - if floating_ip.get('fixed_ip'): + if floating_ip.get('fixed_ip_id'): self.network_api.disassociate_floating_ip(context, floating_ip['address']) @@ -207,7 +238,7 @@ class Floating_ips(extensions.ExtensionDescriptor): floating_ip = self.network_api.get_floating_ip_by_address(context, address) - if floating_ip.get('fixed_ip'): + if floating_ip.get('fixed_ip_id'): try: self.network_api.disassociate_floating_ip(context, address) except exception.NotAuthorized, e: diff --git a/nova/api/openstack/compute/views/addresses.py b/nova/api/openstack/compute/views/addresses.py index 776ba9e59185..5a633a10c036 100644 --- a/nova/api/openstack/compute/views/addresses.py +++ b/nova/api/openstack/compute/views/addresses.py @@ -35,7 +35,7 @@ class ViewBuilder(common.ViewBuilder): """Return a dictionary describing an IP address.""" return { "version": ip["version"], - "addr": ip["addr"], + "addr": ip["address"], } def show(self, network, label): diff --git a/nova/compute/manager.py b/nova/compute/manager.py index cca6d491344f..c0ad9e626131 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -378,7 +378,7 @@ class ComputeManager(manager.SchedulerDependentManager): except Exception: with utils.save_and_reraise_exception(): self._deallocate_network(context, instance) - self._notify_about_instance_usage(instance) + self._notify_about_instance_usage(instance, network_info) if self._is_instance_terminated(instance_uuid): raise exception.InstanceNotFound except exception.InstanceNotFound: @@ -520,8 +520,8 @@ class ComputeManager(manager.SchedulerDependentManager): task_state=None, launched_at=utils.utcnow()) - def _notify_about_instance_usage(self, instance): - usage_info = utils.usage_from_instance(instance) + def _notify_about_instance_usage(self, instance, network_info=None): + usage_info = utils.usage_from_instance(instance, network_info) notifier.notify('compute.%s' % self.host, 'compute.instance.create', notifier.INFO, usage_info) @@ -744,7 +744,7 @@ class ComputeManager(manager.SchedulerDependentManager): task_state=None, launched_at=utils.utcnow()) - usage_info = utils.usage_from_instance(instance) + usage_info = utils.usage_from_instance(instance, network_info) notifier.notify('compute.%s' % self.host, 'compute.instance.rebuild', notifier.INFO, @@ -1043,7 +1043,7 @@ class ComputeManager(manager.SchedulerDependentManager): self.driver.confirm_migration( migration_ref, instance_ref, network_info) - usage_info = utils.usage_from_instance(instance_ref) + usage_info = utils.usage_from_instance(instance_ref, network_info) notifier.notify('compute.%s' % self.host, 'compute.instance.resize.confirm', notifier.INFO, @@ -1271,14 +1271,16 @@ class ComputeManager(manager.SchedulerDependentManager): instance_id = instance_ref['id'] self.network_api.add_fixed_ip_to_instance(context, instance_id, self.host, network_id) - usage = utils.usage_from_instance(instance_ref) + + network_info = self.inject_network_info(context, + instance_ref['uuid']) + self.reset_network(context, instance_ref['uuid']) + + usage = utils.usage_from_instance(instance_ref, network_info) notifier.notify('compute.%s' % self.host, 'compute.instance.create_ip', notifier.INFO, usage) - self.inject_network_info(context, instance_ref['uuid']) - self.reset_network(context, instance_ref['uuid']) - @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) @checks_instance_lock @wrap_instance_fault @@ -1291,14 +1293,16 @@ class ComputeManager(manager.SchedulerDependentManager): instance_id = instance_ref['id'] self.network_api.remove_fixed_ip_from_instance(context, instance_id, address) - usage = utils.usage_from_instance(instance_ref) + + network_info = self.inject_network_info(context, + instance_ref['uuid']) + self.reset_network(context, instance_ref['uuid']) + + usage = utils.usage_from_instance(instance_ref, network_info) notifier.notify('compute.%s' % self.host, 'compute.instance.delete_ip', notifier.INFO, usage) - self.inject_network_info(context, instance_ref['uuid']) - self.reset_network(context, instance_ref['uuid']) - @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) @checks_instance_lock @wrap_instance_fault @@ -1447,6 +1451,7 @@ class ComputeManager(manager.SchedulerDependentManager): LOG.debug(_("network_info to inject: |%s|"), network_info) self.driver.inject_network_info(instance, network_info) + return network_info @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) @wrap_instance_fault @@ -1831,6 +1836,9 @@ class ComputeManager(manager.SchedulerDependentManager): self.driver.unfilter_instance(instance_ref, network_info) # Database updating. + # NOTE(jkoelker) This needs to be converted to network api calls + # if nova wants to support floating_ips in + # quantum/melange try: # Not return if floating_ip is not found, otherwise, # instance never be accessible.. diff --git a/nova/db/api.py b/nova/db/api.py index 81245a285d74..1e72747a9824 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -290,11 +290,6 @@ def floating_ip_fixed_ip_associate(context, floating_address, host) -def floating_ip_get_all(context): - """Get all floating ips.""" - return IMPL.floating_ip_get_all(context) - - def floating_ip_get_all_by_host(context, host): """Get all floating ips by host.""" return IMPL.floating_ip_get_all_by_host(context, host) @@ -315,6 +310,11 @@ def floating_ip_get_by_fixed_address(context, fixed_address): return IMPL.floating_ip_get_by_fixed_address(context, fixed_address) +def floating_ip_get_by_fixed_ip_id(context, fixed_ip_id): + """Get a floating ips by fixed address""" + return IMPL.floating_ip_get_by_fixed_ip_id(context, fixed_ip_id) + + def floating_ip_update(context, address, values): """Update a floating ip by address or raise if it doesn't exist.""" return IMPL.floating_ip_update(context, address, values) @@ -407,11 +407,6 @@ def fixed_ip_get_all(context): return IMPL.fixed_ip_get_all(context) -def fixed_ip_get_all_by_instance_host(context, host): - """Get all allocated fixed ips filtered by instance host.""" - return IMPL.fixed_ip_get_all_by_instance_host(context, host) - - def fixed_ip_get_by_address(context, address): """Get a fixed ip by address or raise if it does not exist.""" return IMPL.fixed_ip_get_by_address(context, address) @@ -581,15 +576,6 @@ def instance_get_all_by_reservation(context, reservation_id): return IMPL.instance_get_all_by_reservation(context, reservation_id) -def instance_get_fixed_addresses(context, instance_id): - """Get the fixed ip address of an instance.""" - return IMPL.instance_get_fixed_addresses(context, instance_id) - - -def instance_get_fixed_addresses_v6(context, instance_id): - return IMPL.instance_get_fixed_addresses_v6(context, instance_id) - - def instance_get_floating_address(context, instance_id): """Get the first floating ip address of an instance.""" return IMPL.instance_get_floating_address(context, instance_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 7ade110fdefa..13e048ba7ea7 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -27,7 +27,6 @@ from nova import block_device from nova import db from nova import exception from nova import flags -from nova import ipv6 from nova import utils from nova import log as logging from nova.compute import vm_states @@ -478,8 +477,6 @@ def certificate_update(context, certificate_id, values): @require_context def floating_ip_get(context, id): result = model_query(context, models.FloatingIp, project_only=True).\ - options(joinedload('fixed_ip')).\ - options(joinedload_all('fixed_ip.instance')).\ filter_by(id=id).\ first() @@ -548,7 +545,7 @@ def floating_ip_fixed_ip_associate(context, floating_address, fixed_ip_ref = fixed_ip_get_by_address(context, fixed_address, session=session) - floating_ip_ref.fixed_ip = fixed_ip_ref + floating_ip_ref.fixed_ip_id = fixed_ip_ref["id"] floating_ip_ref.host = host floating_ip_ref.save(session=session) @@ -583,12 +580,13 @@ def floating_ip_disassociate(context, address): floating_ip_ref = floating_ip_get_by_address(context, address, session=session) - fixed_ip_ref = floating_ip_ref.fixed_ip + fixed_ip_ref = fixed_ip_get(context, + floating_ip_ref['fixed_ip_id']) if fixed_ip_ref: fixed_ip_address = fixed_ip_ref['address'] else: fixed_ip_address = None - floating_ip_ref.fixed_ip = None + floating_ip_ref.fixed_ip_id = None floating_ip_ref.host = None floating_ip_ref.save(session=session) return fixed_ip_address @@ -606,16 +604,7 @@ def floating_ip_set_auto_assigned(context, address): def _floating_ip_get_all(context): - return model_query(context, models.FloatingIp, read_deleted="no").\ - options(joinedload_all('fixed_ip.instance')) - - -@require_admin_context -def floating_ip_get_all(context): - floating_ip_refs = _floating_ip_get_all(context).all() - if not floating_ip_refs: - raise exception.NoFloatingIpsDefined() - return floating_ip_refs + return model_query(context, models.FloatingIp, read_deleted="no") @require_admin_context @@ -641,7 +630,6 @@ def floating_ip_get_all_by_project(context, project_id): @require_context def floating_ip_get_by_address(context, address, session=None): result = model_query(context, models.FloatingIp, session=session).\ - options(joinedload_all('fixed_ip.network')).\ filter_by(address=address).\ first() @@ -665,13 +653,22 @@ def floating_ip_get_by_fixed_address(context, fixed_address, session=None): fixed_ip_id = fixed_ip['id'] return model_query(context, models.FloatingIp, session=session).\ - options(joinedload_all('fixed_ip.network')).\ filter_by(fixed_ip_id=fixed_ip_id).\ all() # NOTE(tr3buchet) please don't invent an exception here, empty list is fine +@require_context +def floating_ip_get_by_fixed_ip_id(context, fixed_ip_id, session=None): + if not session: + session = get_session() + + return model_query(context, models.FloatingIp, session=session).\ + filter_by(fixed_ip_id=fixed_ip_id).\ + all() + + @require_context def floating_ip_update(context, address, values): session = get_session() @@ -732,7 +729,7 @@ def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None): read_deleted="no").\ filter(network_or_none).\ filter_by(reserved=False).\ - filter_by(instance=None).\ + filter_by(instance_id=None).\ filter_by(host=None).\ with_lockmode('update').\ first() @@ -740,16 +737,15 @@ def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None): # then this has concurrency issues if not fixed_ip_ref: raise exception.NoMoreFixedIps() - if not fixed_ip_ref.network: - fixed_ip_ref.network = network_get(context, - network_id, - session=session) + + if fixed_ip_ref['network_id'] is None: + fixed_ip_ref['network'] = network_id + if instance_id: - fixed_ip_ref.instance = instance_get(context, - instance_id, - session=session) + fixed_ip_ref['instance_id'] = instance_id + if host: - fixed_ip_ref.host = host + fixed_ip_ref['host'] = host session.add(fixed_ip_ref) return fixed_ip_ref['address'] @@ -798,8 +794,6 @@ def fixed_ip_disassociate_all_by_timeout(context, host, time): filter(models.FixedIp.updated_at < time).\ filter(models.FixedIp.instance_id != None).\ filter(models.FixedIp.allocated == False).\ - join(models.FixedIp.instance).\ - join(models.FixedIp.network).\ filter(host_filter).\ all() result = model_query(context, models.FixedIp, session=session, @@ -816,16 +810,15 @@ def fixed_ip_disassociate_all_by_timeout(context, host, time): def fixed_ip_get(context, id, session=None): result = model_query(context, models.FixedIp, session=session).\ filter_by(id=id).\ - options(joinedload('floating_ips')).\ - options(joinedload('network')).\ first() if not result: raise exception.FixedIpNotFound(id=id) # FIXME(sirp): shouldn't we just use project_only here to restrict the # results? - if is_user_context(context): - authorize_project_context(context, result.instance.project_id) + if is_user_context(context) and result['instance_id'] is not None: + instance = instance_get(context, result['instance_id'], session) + authorize_project_context(context, instance.project_id) return result @@ -834,7 +827,6 @@ def fixed_ip_get(context, id, session=None): def fixed_ip_get_all(context, session=None): result = model_query(context, models.FixedIp, session=session, read_deleted="yes").\ - options(joinedload('floating_ips')).\ all() if not result: raise exception.NoFixedIpsDefined() @@ -842,36 +834,20 @@ def fixed_ip_get_all(context, session=None): return result -@require_admin_context -def fixed_ip_get_all_by_instance_host(context, host=None): - result = model_query(context, models.FixedIp, read_deleted="yes").\ - options(joinedload('floating_ips')).\ - join(models.FixedIp.instance).\ - filter_by(host=host).\ - all() - - if not result: - raise exception.FixedIpNotFoundForHost(host=host) - - return result - - @require_context def fixed_ip_get_by_address(context, address, session=None): result = model_query(context, models.FixedIp, session=session, read_deleted="yes").\ filter_by(address=address).\ - options(joinedload('floating_ips')).\ - options(joinedload('network')).\ - options(joinedload('instance')).\ first() if not result: raise exception.FixedIpNotFoundForAddress(address=address) # NOTE(sirp): shouldn't we just use project_only here to restrict the # results? - if is_user_context(context): - authorize_project_context(context, result.instance.project_id) + if is_user_context(context) and result['instance_id'] is not None: + instance = instance_get(context, result['instance_id'], session) + authorize_project_context(context, instance.project_id) return result @@ -879,7 +855,6 @@ def fixed_ip_get_by_address(context, address, session=None): @require_context def fixed_ip_get_by_instance(context, instance_id): result = model_query(context, models.FixedIp, read_deleted="no").\ - options(joinedload('floating_ips')).\ filter_by(instance_id=instance_id).\ all() @@ -905,7 +880,6 @@ def fixed_ip_get_by_network_host(context, network_id, host): @require_context def fixed_ips_by_virtual_interface(context, vif_id): result = model_query(context, models.FixedIp, read_deleted="no").\ - options(joinedload('floating_ips')).\ filter_by(virtual_interface_id=vif_id).\ all() @@ -966,8 +940,7 @@ def virtual_interface_update(context, vif_id, values): @require_context def _virtual_interface_query(context, session=None): return model_query(context, models.VirtualInterface, session=session, - read_deleted="yes").\ - options(joinedload_all('fixed_ips.floating_ips')) + read_deleted="yes") @require_context @@ -1202,9 +1175,6 @@ def instance_get(context, instance_id, session=None): def _build_instance_get(context, session=None): return model_query(context, models.Instance, session=session, project_only=True).\ - options(joinedload_all('fixed_ips.floating_ips')).\ - options(joinedload_all('fixed_ips.network')).\ - options(joinedload_all('fixed_ips.virtual_interface')).\ options(joinedload_all('security_groups.rules')).\ options(joinedload('info_cache')).\ options(joinedload('volumes')).\ @@ -1215,10 +1185,8 @@ def _build_instance_get(context, session=None): @require_admin_context def instance_get_all(context): return model_query(context, models.Instance).\ - options(joinedload_all('fixed_ips.floating_ips')).\ options(joinedload('info_cache')).\ options(joinedload('security_groups')).\ - options(joinedload_all('fixed_ips.network')).\ options(joinedload('metadata')).\ options(joinedload('instance_type')).\ all() @@ -1266,9 +1234,6 @@ def instance_get_all_by_filters(context, filters): session = get_session() query_prefix = session.query(models.Instance).\ - options(joinedload_all('fixed_ips.floating_ips')).\ - options(joinedload_all('fixed_ips.network')).\ - options(joinedload_all('fixed_ips.virtual_interface')).\ options(joinedload('info_cache')).\ options(joinedload('security_groups')).\ options(joinedload('metadata')).\ @@ -1366,9 +1331,7 @@ def instance_get_active_by_window_joined(context, begin, end=None, """Return instances and joins that were continuously active over window.""" session = get_session() query = session.query(models.Instance).\ - options(joinedload_all('fixed_ips.floating_ips')).\ options(joinedload('security_groups')).\ - options(joinedload_all('fixed_ips.network')).\ options(joinedload('instance_type')).\ filter(models.Instance.launched_at < begin) if end: @@ -1384,10 +1347,8 @@ def instance_get_active_by_window_joined(context, begin, end=None, @require_admin_context def _instance_get_all_query(context, project_only=False): return model_query(context, models.Instance, project_only=project_only).\ - options(joinedload_all('fixed_ips.floating_ips')).\ options(joinedload('info_cache')).\ options(joinedload('security_groups')).\ - options(joinedload_all('fixed_ips.network')).\ options(joinedload('metadata')).\ options(joinedload('instance_type')) @@ -1425,53 +1386,24 @@ def instance_get_project_vpn(context, project_id): first() -@require_context -def instance_get_fixed_addresses(context, instance_id): - session = get_session() - with session.begin(): - try: - fixed_ips = fixed_ip_get_by_instance(context, instance_id) - except exception.NotFound: - return [] - return [fixed_ip.address for fixed_ip in fixed_ips] - - -@require_context -def instance_get_fixed_addresses_v6(context, instance_id): - session = get_session() - with session.begin(): - # get instance - instance_ref = instance_get(context, instance_id, session=session) - # assume instance has 1 mac for each network associated with it - # get networks associated with instance - network_refs = network_get_all_by_instance(context, instance_id) - # compile a list of cidr_v6 prefixes sorted by network id - prefixes = [ref.cidr_v6 for ref in - sorted(network_refs, key=lambda ref: ref.id)] - # get vifs associated with instance - vif_refs = virtual_interface_get_by_instance(context, instance_ref.id) - # compile list of the mac_addresses for vifs sorted by network id - macs = [vif_ref['address'] for vif_ref in - sorted(vif_refs, key=lambda vif_ref: vif_ref['network_id'])] - # get project id from instance - project_id = instance_ref.project_id - # combine prefixes, macs, and project_id into (prefix,mac,p_id) tuples - prefix_mac_tuples = zip(prefixes, macs, [project_id for m in macs]) - # return list containing ipv6 address for each tuple - return [ipv6.to_global(*t) for t in prefix_mac_tuples] - - +# NOTE(jkoelker) This is only being left here for compat with floating +# ips. Currently the network_api doesn't return floaters +# in network_info. Once it starts return the model. This +# function and it's call in compute/manager.py on 1829 can +# go away @require_context def instance_get_floating_address(context, instance_id): - fixed_ip_refs = fixed_ip_get_by_instance(context, instance_id) - if not fixed_ip_refs: + fixed_ips = fixed_ip_get_by_instance(context, instance_id) + if not fixed_ips: return None # NOTE(tr3buchet): this only gets the first fixed_ip # won't find floating ips associated with other fixed_ips - if not fixed_ip_refs[0].floating_ips: + floating_ips = floating_ip_get_by_fixed_address(context, + fixed_ips[0]['address']) + if not floating_ips: return None # NOTE(vish): this just returns the first floating ip - return fixed_ip_refs[0].floating_ips[0]['address'] + return floating_ips[0]['address'] @require_admin_context @@ -1878,7 +1810,6 @@ def network_get_associated_fixed_ips(context, network_id): # FIXME(sirp): since this returns fixed_ips, this would be better named # fixed_ip_get_all_by_network. return model_query(context, models.FixedIp, read_deleted="no").\ - options(joinedload_all('instance')).\ filter_by(network_id=network_id).\ filter(models.FixedIp.instance_id != None).\ filter(models.FixedIp.virtual_interface_id != None).\ @@ -1929,7 +1860,6 @@ def network_get_by_instance(context, instance_id): # note this uses fixed IP to get to instance # only works for networks the instance has an IP from result = _network_get_query(context).\ - join(models.Network.fixed_ips).\ filter_by(instance_id=instance_id).\ first() @@ -1942,7 +1872,6 @@ def network_get_by_instance(context, instance_id): @require_admin_context def network_get_all_by_instance(context, instance_id): result = _network_get_query(context).\ - join(models.Network.fixed_ips).\ filter_by(instance_id=instance_id).\ all() @@ -1959,7 +1888,6 @@ def network_get_all_by_host(context, host): host_filter = or_(models.Network.host == host, models.FixedIp.host == host) return _network_get_query(context).\ - join(models.Network.fixed_ips).\ filter(host_filter).\ all() diff --git a/nova/db/sqlalchemy/migrate_repo/versions/070_sqlite_downgrade.sql b/nova/db/sqlalchemy/migrate_repo/versions/070_sqlite_downgrade.sql new file mode 100644 index 000000000000..25270a686ffc --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/070_sqlite_downgrade.sql @@ -0,0 +1,103 @@ +BEGIN TRANSACTION; + CREATE TABLE fixed_ips_backup ( + created_at DATETIME NOT NULL, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN NOT NULL, + id INTEGER NOT NULL, + address VARCHAR(255), + virtual_interface_id INTEGER, + network_id INTEGER, + instance_id INTEGER, + allocated BOOLEAN default FALSE, + leased BOOLEAN default FALSE, + reserved BOOLEAN default FALSE, + host VARCHAR(255), + PRIMARY KEY (id) + ); + + CREATE TABLE floating_ips_backup ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + address VARCHAR(255), + fixed_ip_id INTEGER, + project_id VARCHAR(255), + host VARCHAR(255), + auto_assigned BOOLEAN, + pool VARCHAR(255), + interface VARCHAR(255), + PRIMARY KEY (id) + ); + + INSERT INTO fixed_ips_backup + SELECT created_at, updated_at, deleted_at, deleted, id, address, + virtual_interface_id, network_id, instance_id, allocated, + leased, reserved, host + FROM fixed_ips; + + INSERT INTO floating_ips_backup + SELECT created_at, updated_at, deleted_at, deleted, id, address, + fixed_ip_id, project_id, host, auto_assigned, pool, + interface + FROM floating_ips; + + DROP TABLE fixed_ips; + DROP TABLE floating_ips; + + CREATE TABLE fixed_ips ( + created_at DATETIME NOT NULL, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN NOT NULL, + id INTEGER NOT NULL, + address VARCHAR(255), + virtual_interface_id INTEGER, + network_id INTEGER, + instance_id INTEGER, + allocated BOOLEAN default FALSE, + leased BOOLEAN default FALSE, + reserved BOOLEAN default FALSE, + host VARCHAR(255), + PRIMARY KEY (id), + FOREIGN KEY(virtual_interface_id) REFERENCES virtual_interfaces (id), + FOREIGN KEY(network_id) REFERENCES networks (id), + FOREIGN KEY(instance_id) REFERENCES instanced (id) + ); + + CREATE TABLE floating_ips ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + address VARCHAR(255), + fixed_ip_id INTEGER, + project_id VARCHAR(255), + host VARCHAR(255), + auto_assigned BOOLEAN, + pool VARCHAR(255), + interface VARCHAR(255), + PRIMARY KEY (id), + CHECK (deleted IN (0, 1)), + FOREIGN KEY(fixed_ip_id) REFERENCES fixed_ips (id) + ); + + INSERT INTO fixed_ips + SELECT created_at, updated_at, deleted_at, deleted, id, address, + virtual_interface_id, network_id, instance_id, allocated, + leased, reserved, host + FROM fixed_ips_backup; + + INSERT INTO floating_ips + SELECT created_at, updated_at, deleted_at, deleted, id, address, + fixed_ip_id, project_id, host, auto_assigned, pool, + interface + FROM floating_ips_backup; + + DROP TABLE fixed_ips_backup; + DROP TABLE floating_ips_backup; + +COMMIT; diff --git a/nova/db/sqlalchemy/migrate_repo/versions/070_sqlite_upgrade.sql b/nova/db/sqlalchemy/migrate_repo/versions/070_sqlite_upgrade.sql new file mode 100644 index 000000000000..2b6f7c39a406 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/070_sqlite_upgrade.sql @@ -0,0 +1,99 @@ +BEGIN TRANSACTION; + CREATE TABLE fixed_ips_backup ( + created_at DATETIME NOT NULL, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN NOT NULL, + id INTEGER NOT NULL, + address VARCHAR(255), + virtual_interface_id INTEGER, + network_id INTEGER, + instance_id INTEGER, + allocated BOOLEAN default FALSE, + leased BOOLEAN default FALSE, + reserved BOOLEAN default FALSE, + host VARCHAR(255), + PRIMARY KEY (id) + ); + + CREATE TABLE floating_ips_backup ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + address VARCHAR(255), + fixed_ip_id INTEGER, + project_id VARCHAR(255), + host VARCHAR(255), + auto_assigned BOOLEAN, + pool VARCHAR(255), + interface VARCHAR(255), + PRIMARY KEY (id) + ); + + INSERT INTO fixed_ips_backup + SELECT created_at, updated_at, deleted_at, deleted, id, address, + virtual_interface_id, network_id, instance_id, allocated, + leased, reserved, host + FROM fixed_ips; + + INSERT INTO floating_ips_backup + SELECT created_at, updated_at, deleted_at, deleted, id, address, + fixed_ip_id, project_id, host, auto_assigned, pool, + interface + FROM floating_ips; + + DROP TABLE fixed_ips; + DROP TABLE floating_ips; + + CREATE TABLE fixed_ips ( + created_at DATETIME NOT NULL, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN NOT NULL, + id INTEGER NOT NULL, + address VARCHAR(255), + virtual_interface_id INTEGER, + network_id INTEGER, + instance_id INTEGER, + allocated BOOLEAN default FALSE, + leased BOOLEAN default FALSE, + reserved BOOLEAN default FALSE, + host VARCHAR(255), + PRIMARY KEY (id) + ); + + CREATE TABLE floating_ips ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + address VARCHAR(255), + fixed_ip_id INTEGER, + project_id VARCHAR(255), + host VARCHAR(255), + auto_assigned BOOLEAN, + pool VARCHAR(255), + interface VARCHAR(255), + PRIMARY KEY (id), + CHECK (deleted IN (0, 1)) + ); + + INSERT INTO fixed_ips + SELECT created_at, updated_at, deleted_at, deleted, id, address, + virtual_interface_id, network_id, instance_id, allocated, + leased, reserved, host + FROM fixed_ips_backup; + + INSERT INTO floating_ips + SELECT created_at, updated_at, deleted_at, deleted, id, address, + fixed_ip_id, project_id, host, auto_assigned, pool, + interface + FROM floating_ips_backup; + + DROP TABLE fixed_ips_backup; + DROP TABLE floating_ips_backup; + +COMMIT; diff --git a/nova/db/sqlalchemy/migrate_repo/versions/070_untie_nova_network_models.py b/nova/db/sqlalchemy/migrate_repo/versions/070_untie_nova_network_models.py new file mode 100644 index 000000000000..83114b451ef7 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/070_untie_nova_network_models.py @@ -0,0 +1,98 @@ +# Copyright 2011 OpenStack LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import MetaData, Table +from migrate import ForeignKeyConstraint + +from nova import log as logging + +meta = MetaData() + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + dialect = migrate_engine.url.get_dialect().name + if dialect.startswith('sqlite'): + return + + instances = Table('instances', meta, autoload=True) + networks = Table('networks', meta, autoload=True) + vifs = Table('virtual_interfaces', meta, autoload=True) + fixed_ips = Table('fixed_ips', meta, autoload=True) + floating_ips = Table('floating_ips', meta, autoload=True) + + try: + fkeys = list(fixed_ips.c.network_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[fixed_ips.c.network_id], + refcolumns=[networks.c.id], + name=fkey_name).drop() + + fkeys = list(fixed_ips.c.virtual_interface_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[fixed_ips.c.virtual_interface_id], + refcolumns=[vifs.c.id], + name=fkey_name).drop() + + fkeys = list(fixed_ips.c.instance_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[fixed_ips.c.instance_id], + refcolumns=[instances.c.id], + name=fkey_name).drop() + + fkeys = list(floating_ips.c.fixed_ip_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[floating_ips.c.fixed_ip_id], + refcolumns=[fixed_ips.c.id], + name=fkey_name).drop() + + except Exception: + logging.error(_("foreign key constraint couldn't be removed")) + raise + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + meta.bind = migrate_engine + dialect = migrate_engine.url.get_dialect().name + if dialect.startswith('sqlite'): + return + + instances = Table('instances', meta, autoload=True) + networks = Table('networks', meta, autoload=True) + vifs = Table('virtual_interfaces', meta, autoload=True) + fixed_ips = Table('fixed_ips', meta, autoload=True) + floating_ips = Table('floating_ips', meta, autoload=True) + + try: + ForeignKeyConstraint(columns=[fixed_ips.c.network_id], + refcolumns=[networks.c.id]).create() + + ForeignKeyConstraint(columns=[fixed_ips.c.virtual_interface_id], + refcolumns=[vifs.c.id]).create() + + ForeignKeyConstraint(columns=[fixed_ips.c.instance_id], + refcolumns=[instances.c.id]).create() + + ForeignKeyConstraint(columns=[floating_ips.c.fixed_ip_id], + refcolumns=[fixed_ips.c.id]).create() + except Exception: + logging.error(_("foreign key constraint couldn't be added")) + raise diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 92c9bb27f62d..4ad6dff1f53a 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -683,19 +683,9 @@ class FixedIp(BASE, NovaBase): __tablename__ = 'fixed_ips' id = Column(Integer, primary_key=True) address = Column(String(255)) - network_id = Column(Integer, ForeignKey('networks.id'), nullable=True) - network = relationship(Network, backref=backref('fixed_ips')) - virtual_interface_id = Column(Integer, ForeignKey('virtual_interfaces.id'), - nullable=True) - virtual_interface = relationship(VirtualInterface, - backref=backref('fixed_ips')) - instance_id = Column(Integer, ForeignKey('instances.id'), nullable=True) - instance = relationship(Instance, - backref=backref('fixed_ips'), - foreign_keys=instance_id, - primaryjoin='and_(' - 'FixedIp.instance_id == Instance.id,' - 'FixedIp.deleted == False)') + network_id = Column(Integer, nullable=True) + virtual_interface_id = Column(Integer, nullable=True) + instance_id = Column(Integer, nullable=True) # associated means that a fixed_ip has its instance_id column set # allocated means that a fixed_ip has a its virtual_interface_id column set allocated = Column(Boolean, default=False) @@ -710,13 +700,7 @@ class FloatingIp(BASE, NovaBase): __tablename__ = 'floating_ips' id = Column(Integer, primary_key=True) address = Column(String(255)) - fixed_ip_id = Column(Integer, ForeignKey('fixed_ips.id'), nullable=True) - fixed_ip = relationship(FixedIp, - backref=backref('floating_ips'), - foreign_keys=fixed_ip_id, - primaryjoin='and_(' - 'FloatingIp.fixed_ip_id == FixedIp.id,' - 'FloatingIp.deleted == False)') + fixed_ip_id = Column(Integer, nullable=True) project_id = Column(String(255)) host = Column(String(255)) # , ForeignKey('hosts.id')) auto_assigned = Column(Boolean, default=False, nullable=False) diff --git a/nova/network/api.py b/nova/network/api.py index d1c4f03304cc..b9adba98df62 100644 --- a/nova/network/api.py +++ b/nova/network/api.py @@ -56,6 +56,12 @@ class API(base.Base): {'method': 'disassociate_network', 'args': {'network_uuid': network_uuid}}) + def get_fixed_ip(self, context, id): + return rpc.call(context, + FLAGS.network_topic, + {'method': 'get_fixed_ip', + 'args': {'id': id}}) + def get_floating_ip(self, context, id): return rpc.call(context, FLAGS.network_topic, diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 5b0c80eec839..5d484de59f72 100755 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -555,10 +555,20 @@ def get_dhcp_leases(context, network_ref): hosts = [] for fixed_ref in db.network_get_associated_fixed_ips(context, network_ref['id']): - host = fixed_ref['instance']['host'] - if network_ref['multi_host'] and FLAGS.host != host: + vif_id = fixed_ref['virtual_interface_id'] + # NOTE(jkoelker) We need a larger refactor to happen to prevent + # looking these up here + vif_ref = db.virtual_interface_get(context, vif_id) + instance_id = fixed_ref['instance_id'] + try: + instance_ref = db.instance_get(context, instance_id) + except exception.InstanceNotFound: + msg = _("Instance %(instance_id)s not found") + LOG.debug(msg % {'instance_id': instance_id}) continue - hosts.append(_host_lease(fixed_ref)) + if network_ref['multi_host'] and FLAGS.host != instance_ref['host']: + continue + hosts.append(_host_lease(fixed_ref, vif_ref, instance_ref)) return '\n'.join(hosts) @@ -567,10 +577,20 @@ def get_dhcp_hosts(context, network_ref): hosts = [] for fixed_ref in db.network_get_associated_fixed_ips(context, network_ref['id']): - host = fixed_ref['instance']['host'] - if network_ref['multi_host'] and FLAGS.host != host: + vif_id = fixed_ref['virtual_interface_id'] + # NOTE(jkoelker) We need a larger refactor to happen to prevent + # looking these up here + vif_ref = db.virtual_interface_get(context, vif_id) + instance_id = fixed_ref['instance_id'] + try: + instance_ref = db.instance_get(context, instance_id) + except exception.InstanceNotFound: + msg = _("Instance %(instance_id)s not found") + LOG.debug(msg % {'instance_id': instance_id}) continue - hosts.append(_host_dhcp(fixed_ref)) + if network_ref['multi_host'] and FLAGS.host != instance_ref['host']: + continue + hosts.append(_host_dhcp(fixed_ref, vif_ref, instance_ref)) return '\n'.join(hosts) @@ -604,11 +624,19 @@ def get_dhcp_opts(context, network_ref): for fixed_ip_ref in ips_ref: instance_id = fixed_ip_ref['instance_id'] + try: + instance_ref = db.instance_get(context, instance_id) + except exception.InstanceNotFound: + msg = _("Instance %(instance_id)s not found") + LOG.debug(msg % {'instance_id': instance_id}) + continue + if instance_id in default_gw_network_node: target_network_id = default_gw_network_node[instance_id] # we don't want default gateway for this fixed ip if target_network_id != fixed_ip_ref['network_id']: - hosts.append(_host_dhcp_opts(fixed_ip_ref)) + hosts.append(_host_dhcp_opts(fixed_ip_ref, + instance_ref)) return '\n'.join(hosts) @@ -739,9 +767,8 @@ interface %s _execute(*cmd, run_as_root=True) -def _host_lease(fixed_ip_ref): +def _host_lease(fixed_ip_ref, vif_ref, instance_ref): """Return a host string for an address in leasefile format.""" - instance_ref = fixed_ip_ref['instance'] if instance_ref['updated_at']: timestamp = instance_ref['updated_at'] else: @@ -750,37 +777,35 @@ def _host_lease(fixed_ip_ref): seconds_since_epoch = calendar.timegm(timestamp.utctimetuple()) return '%d %s %s %s *' % (seconds_since_epoch + FLAGS.dhcp_lease_time, - fixed_ip_ref['virtual_interface']['address'], + vif_ref['address'], fixed_ip_ref['address'], instance_ref['hostname'] or '*') -def _host_dhcp_network(fixed_ip_ref): - instance_ref = fixed_ip_ref['instance'] +def _host_dhcp_network(fixed_ip_ref, instance_ref): return 'NW-i%08d-%s' % (instance_ref['id'], fixed_ip_ref['network_id']) -def _host_dhcp(fixed_ip_ref): +def _host_dhcp(fixed_ip_ref, vif_ref, instance_ref): """Return a host string for an address in dhcp-host format.""" - instance_ref = fixed_ip_ref['instance'] - vif = fixed_ip_ref['virtual_interface'] if FLAGS.use_single_default_gateway: - return '%s,%s.%s,%s,%s' % (vif['address'], + return '%s,%s.%s,%s,%s' % (vif_ref['address'], instance_ref['hostname'], FLAGS.dhcp_domain, fixed_ip_ref['address'], - "net:" + _host_dhcp_network(fixed_ip_ref)) + "net:" + _host_dhcp_network(fixed_ip_ref, + instance_ref)) else: - return '%s,%s.%s,%s' % (vif['address'], + return '%s,%s.%s,%s' % (vif_ref['address'], instance_ref['hostname'], FLAGS.dhcp_domain, fixed_ip_ref['address']) -def _host_dhcp_opts(fixed_ip_ref): +def _host_dhcp_opts(fixed_ip_ref, instance_ref): """Return a host string for an address in dhcp-host format.""" - return '%s,%s' % (_host_dhcp_network(fixed_ip_ref), 3) + return '%s,%s' % (_host_dhcp_network(fixed_ip_ref, instance_ref), 3) def _execute(*cmd, **kwargs): diff --git a/nova/network/manager.py b/nova/network/manager.py index 3a9003a7838d..15e0649f5848 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -183,7 +183,7 @@ class RPCAllocateFixedIP(object): """Sits in between _allocate_fixed_ips and allocate_fixed_ip to perform network lookup on the far side of rpc. """ - network = self.db.network_get(context, network_id) + network = self._get_network_by_id(context, network_id) return self.allocate_fixed_ip(context, instance_id, network, **kwargs) @@ -264,8 +264,11 @@ class FloatingIP(object): # add to kwargs so we can pass to super to save a db lookup there kwargs['fixed_ips'] = fixed_ips for fixed_ip in fixed_ips: + fixed_id = fixed_ip['id'] + floating_ips = self.db.floating_ip_get_by_fixed_ip_id(context, + fixed_id) # disassociate floating ips related to fixed_ip - for floating_ip in fixed_ip.floating_ips: + for floating_ip in floating_ips: address = floating_ip['address'] self.disassociate_floating_ip(context, address, True) # deallocate if auto_assigned @@ -348,11 +351,14 @@ class FloatingIP(object): fixed_ip = self.db.fixed_ip_get_by_address(context, fixed_address) # send to correct host, unless i'm the correct host - if fixed_ip['network']['multi_host']: + network = self._get_network_by_id(context.elevated(), + fixed_ip['network_id']) + if network['multi_host']: instance = self.db.instance_get(context, fixed_ip['instance_id']) host = instance['host'] else: - host = fixed_ip['network']['host'] + host = network['host'] + interface = floating_ip['interface'] if host == self.host: # i'm the correct host @@ -403,11 +409,13 @@ class FloatingIP(object): fixed_ip = self.db.fixed_ip_get(context, floating_ip['fixed_ip_id']) # send to correct host, unless i'm the correct host - if fixed_ip['network']['multi_host']: + network = self._get_network_by_id(context, fixed_ip['network_id']) + if network['multi_host']: instance = self.db.instance_get(context, fixed_ip['instance_id']) host = instance['host'] else: - host = fixed_ip['network']['host'] + host = network['host'] + interface = floating_ip['interface'] if host == self.host: # i'm the correct host @@ -597,7 +605,7 @@ class NetworkManager(manager.SchedulerDependentManager): if vif['instance_id'] is None: continue - network = self.db.network_get(context, vif['network_id']) + network = self._get_network_by_id(context, vif['network_id']) fixed_ipv6 = None if network['cidr_v6'] is not None: fixed_ipv6 = ipv6.to_global(network['cidr_v6'], @@ -609,7 +617,10 @@ class NetworkManager(manager.SchedulerDependentManager): results.append({'instance_id': vif['instance_id'], 'ip': fixed_ipv6}) - for fixed_ip in vif['fixed_ips']: + vif_id = vif['id'] + fixed_ips = self.db.fixed_ips_by_virtual_interface(context, + vif_id) + for fixed_ip in fixed_ips: if not fixed_ip or not fixed_ip['address']: continue if fixed_ip['address'] == fixed_ip_filter: @@ -643,7 +654,7 @@ class NetworkManager(manager.SchedulerDependentManager): # a non-vlan instance should connect to if requested_networks is not None and len(requested_networks) != 0: network_uuids = [uuid for (uuid, fixed_ip) in requested_networks] - networks = self.db.network_get_all_by_uuids(context, network_uuids) + networks = self.network_get_all_by_uuids(context, network_uuids) else: try: networks = self.db.network_get_all(context) @@ -722,7 +733,7 @@ class NetworkManager(manager.SchedulerDependentManager): # a vif has an address, instance_id, and network_id # it is also joined to the instance and network given by those IDs for vif in vifs: - network = self.db.network_get(context, vif['network_id']) + network = self._get_network_by_id(context, vif['network_id']) if network is None: continue @@ -809,7 +820,7 @@ class NetworkManager(manager.SchedulerDependentManager): for an instance""" nw_info = network_model.NetworkInfo() for vif in vifs: - network = self.db.network_get(context, vif['network_id']) + network = self._get_network_by_id(context, vif['network_id']) subnets = self._get_subnets_from_network(network) # if rxtx_cap data are not set everywhere, set to none @@ -968,7 +979,7 @@ class NetworkManager(manager.SchedulerDependentManager): def add_fixed_ip_to_instance(self, context, instance_id, host, network_id): """Adds a fixed ip to an instance from specified network.""" - networks = [self.db.network_get(context, network_id)] + networks = [self._get_network_by_id(context, network_id)] self._allocate_fixed_ips(context, instance_id, host, networks) def remove_fixed_ip_from_instance(self, context, instance_id, address): @@ -1023,8 +1034,7 @@ class NetworkManager(manager.SchedulerDependentManager): {'allocated': False, 'virtual_interface_id': None}) fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address) - instance_ref = fixed_ip_ref['instance'] - instance_id = instance_ref['id'] + instance_id = fixed_ip_ref['instance_id'] self._do_trigger_security_group_members_refresh_for_instance( instance_id) @@ -1032,17 +1042,19 @@ class NetworkManager(manager.SchedulerDependentManager): self.instance_dns_manager.delete_entry(name) if FLAGS.force_dhcp_release: - dev = self.driver.get_dev(fixed_ip_ref['network']) + network = self._get_network_by_id(context, + fixed_ip_ref['network_id']) + dev = self.driver.get_dev(network) vif = self.db.virtual_interface_get_by_instance_and_network( - context, instance_ref['id'], fixed_ip_ref['network']['id']) + context, instance_id, network['id']) self.driver.release_dhcp(dev, address, vif['address']) def lease_fixed_ip(self, context, address): """Called by dhcp-bridge when ip is leased.""" LOG.debug(_('Leased IP |%(address)s|'), locals(), context=context) fixed_ip = self.db.fixed_ip_get_by_address(context, address) - instance = fixed_ip['instance'] - if not instance: + + if fixed_ip['instance_id'] is None: raise exception.Error(_('IP %s leased that is not associated') % address) now = utils.utcnow() @@ -1058,8 +1070,8 @@ class NetworkManager(manager.SchedulerDependentManager): """Called by dhcp-bridge when ip is released.""" LOG.debug(_('Released IP |%(address)s|'), locals(), context=context) fixed_ip = self.db.fixed_ip_get_by_address(context, address) - instance = fixed_ip['instance'] - if not instance: + + if fixed_ip['instance_id'] is None: raise exception.Error(_('IP %s released that is not associated') % address) if not fixed_ip['leased']: @@ -1230,7 +1242,7 @@ class NetworkManager(manager.SchedulerDependentManager): def _create_fixed_ips(self, context, network_id): """Create all fixed ips for network.""" - network = self.db.network_get(context, network_id) + network = self._get_network_by_id(context, network_id) # NOTE(vish): Should these be properties of the network as opposed # to properties of the manager class? bottom_reserved = self._bottom_reserved_ips @@ -1279,12 +1291,17 @@ class NetworkManager(manager.SchedulerDependentManager): fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address) - if fixed_ip_ref['network']['uuid'] != network_uuid: + network = self._get_network_by_id(context, + fixed_ip_ref['network_id']) + if network['uuid'] != network_uuid: raise exception.FixedIpNotFoundForNetwork(address=address, network_uuid=network_uuid) - if fixed_ip_ref['instance'] is not None: + if fixed_ip_ref['instance_id'] is not None: raise exception.FixedIpAlreadyInUse(address=address) + def _get_network_by_id(self, context, network_id): + return self.db.network_get(context, network_id) + def _get_networks_by_uuids(self, context, network_uuids): return self.db.network_get_all_by_uuids(context, network_uuids) @@ -1310,6 +1327,11 @@ class NetworkManager(manager.SchedulerDependentManager): network = self.get_network(context, network_uuid) self.db.network_disassociate(context, network['id']) + def get_fixed_ip(self, context, id): + """Return a fixed ip""" + fixed = self.db.fixed_ip_get(context, id) + return dict(fixed.iteritems()) + class FlatManager(NetworkManager): """Basic network where no vlans are used. @@ -1407,6 +1429,10 @@ class FlatDHCPManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): self.db.network_update(context, network_ref['id'], {'gateway_v6': gateway}) + def _get_network_by_id(self, context, network_id): + return NetworkManager._get_network_by_id(self, context.elevated(), + network_id) + class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): """Vlan network with dhcp. diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py index d4e4d7d1ad01..ffdb74c8c2b4 100644 --- a/nova/tests/api/ec2/test_cloud.py +++ b/nova/tests/api/ec2/test_cloud.py @@ -58,32 +58,42 @@ class AjaxProxyManager(manager.SchedulerDependentManager): return None -def get_fake_fixed_ips(): - vif = {'address': 'aa:bb:cc:dd:ee:ff'} - network = {'label': 'private', - 'project_id': 'fake', - 'cidr_v6': 'fe80:b33f::/64'} - floating_ips = [{'address': '1.2.3.4'}, - {'address': '5.6.7.8'}] - fixed_ip1 = {'address': '192.168.0.3', - 'floating_ips': floating_ips, - 'virtual_interface': vif, - 'network': network} - fixed_ip2 = {'address': '192.168.0.4', - 'network': network} - return [fixed_ip1, fixed_ip2] +def get_fake_cache(): + def _ip(ip, fixed=True, floats=None): + ip_dict = {'address': ip, 'type': 'fixed'} + if not fixed: + ip_dict['type'] = 'floating' + if fixed and floats: + ip_dict['floating_ips'] = [_ip(f, fixed=False) for f in floats] + return ip_dict + + info = [{'address': 'aa:bb:cc:dd:ee:ff', + 'id': 1, + 'network': {'bridge': 'br0', + 'id': 1, + 'label': 'private', + 'subnets': [{'cidr': '192.168.0.0/24', + 'ips': [_ip('192.168.0.3', + floats=['1.2.3.4', + '5.6.7.8']), + _ip('192.168.0.4')]}]}}] + if FLAGS.use_ipv6: + ipv6_addr = 'fe80:b33f::a8bb:ccff:fedd:eeff' + info[0]['network']['subnets'].append({'cidr': 'fe80:b33f::/64', + 'ips': [_ip(ipv6_addr)]}) + return info -def get_instances_with_fixed_ips(orig_func, *args, **kwargs): - """Kludge fixed_ips into instance(s) without having to create DB +def get_instances_with_cached_ips(orig_func, *args, **kwargs): + """Kludge the cache into instance(s) without having to create DB entries """ instances = orig_func(*args, **kwargs) if isinstance(instances, list): for instance in instances: - instance['fixed_ips'] = get_fake_fixed_ips() + instance['info_cache'] = {'network_info': get_fake_cache()} else: - instances['fixed_ips'] = get_fake_fixed_ips() + instances['info_cache'] = {'network_info': get_fake_cache()} return instances @@ -136,7 +146,7 @@ class CloudTestCase(test.TestCase): orig_func = getattr(self.cloud.compute_api, func_name) def fake_get(*args, **kwargs): - return get_instances_with_fixed_ips(orig_func, *args, **kwargs) + return get_instances_with_cached_ips(orig_func, *args, **kwargs) self.stubs.Set(self.cloud.compute_api, func_name, fake_get) def _create_key(self, name): @@ -177,7 +187,6 @@ class CloudTestCase(test.TestCase): def test_release_address(self): address = "10.10.10.10" - allocate = self.cloud.allocate_address db.floating_ip_create(self.context, {'address': address, 'pool': 'nova', diff --git a/nova/tests/api/openstack/compute/contrib/test_floating_ips.py b/nova/tests/api/openstack/compute/contrib/test_floating_ips.py index fe544441973a..27c980f00f45 100644 --- a/nova/tests/api/openstack/compute/contrib/test_floating_ips.py +++ b/nova/tests/api/openstack/compute/contrib/test_floating_ips.py @@ -21,6 +21,7 @@ from nova.api.openstack.compute.contrib import floating_ips from nova import context from nova import db from nova import network +from nova import compute from nova import rpc from nova import test from nova.tests.api.openstack import fakes @@ -29,28 +30,35 @@ from nova import utils FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' +def network_api_get_fixed_ip(self, context, id): + if id is None: + return None + return {'address': '10.0.0.1', 'id': id, 'instance_id': 1} + + def network_api_get_floating_ip(self, context, id): - return {'id': 1, 'address': '10.10.10.10', - 'pool': 'nova', - 'fixed_ip': None} + return {'id': 1, 'address': '10.10.10.10', 'pool': 'nova', + 'fixed_ip_id': None} def network_api_get_floating_ip_by_address(self, context, address): - return {'id': 1, 'address': '10.10.10.10', - 'pool': 'nova', - 'fixed_ip': {'address': '10.0.0.1', - 'instance': {'uuid': FAKE_UUID}}} + return {'id': 1, 'address': '10.10.10.10', 'pool': 'nova', + 'fixed_ip_id': 10} def network_api_get_floating_ips_by_project(self, context): return [{'id': 1, 'address': '10.10.10.10', 'pool': 'nova', - 'fixed_ip': {'address': '10.0.0.1', - 'instance': {'uuid': FAKE_UUID}}}, + 'fixed_ip_id': 20}, {'id': 2, 'pool': 'nova', 'interface': 'eth0', - 'address': '10.10.10.11'}] + 'address': '10.10.10.11', + 'fixed_ip_id': None}] + + +def compute_api_get(self, context, instance_id): + return dict(uuid=FAKE_UUID) def network_api_allocate(self, context): @@ -120,6 +128,10 @@ class FloatingIpTest(test.TestCase): def setUp(self): super(FloatingIpTest, self).setUp() + self.stubs.Set(network.api.API, "get_fixed_ip", + network_api_get_fixed_ip) + self.stubs.Set(compute.api.API, "get", + compute_api_get) self.stubs.Set(network.api.API, "get_floating_ip", network_api_get_floating_ip) self.stubs.Set(network.api.API, "get_floating_ip_by_address", @@ -149,6 +161,8 @@ class FloatingIpTest(test.TestCase): floating_ip_address = self._create_floating_ip() floating_ip = db.floating_ip_get_by_address(self.context, floating_ip_address) + floating_ip['fixed_ip'] = None + floating_ip['instance'] = None view = floating_ips._translate_floating_ip_view(floating_ip) self.assertTrue('floating_ip' in view) self.assertTrue(view['floating_ip']['id']) @@ -188,11 +202,14 @@ class FloatingIpTest(test.TestCase): def test_show_associated_floating_ip(self): def get_floating_ip(self, context, id): - return {'id': 1, 'address': '10.10.10.10', - 'pool': 'nova', - 'fixed_ip': {'address': '10.0.0.1', - 'instance': {'uuid': FAKE_UUID}}} + return {'id': 1, 'address': '10.10.10.10', 'pool': 'nova', + 'fixed_ip_id': 11} + + def get_fixed_ip(self, context, id): + return {'address': '10.0.0.1', 'instance_id': 1} + self.stubs.Set(network.api.API, "get_floating_ip", get_floating_ip) + self.stubs.Set(network.api.API, "get_fixed_ip", get_fixed_ip) req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ips/1') res_dict = self.controller.show(req, 1) diff --git a/nova/tests/api/openstack/compute/test_servers.py b/nova/tests/api/openstack/compute/test_servers.py index 77c89d47db6b..80ac9baa6eb3 100644 --- a/nova/tests/api/openstack/compute/test_servers.py +++ b/nova/tests/api/openstack/compute/test_servers.py @@ -102,11 +102,11 @@ def return_server_with_uuid_and_state(vm_state, task_state): def return_servers(context, *args, **kwargs): - servers = [] + servers_list = [] for i in xrange(5): server = fakes.stub_instance(i, 'fake', 'fake', uuid=get_fake_uuid(i)) - servers.append(server) - return servers + servers_list.append(server) + return servers_list def return_servers_by_reservation(context, reservation_id=""): @@ -127,14 +127,14 @@ def return_servers_from_child_zones(*args, **kwargs): zones = [] for zone in xrange(3): - servers = [] + servers_list = [] for server_id in xrange(5): server = Server() server._info = fakes.stub_instance( server_id, reservation_id="child") - servers.append(server) + servers_list.append(server) - zones.append(("Zone%d" % zone, servers)) + zones.append(("Zone%d" % zone, servers_list)) return zones @@ -146,10 +146,6 @@ def instance_update(context, instance_id, values): return fakes.stub_instance(instance_id, name=values.get('display_name')) -def instance_addresses(context, instance_id): - return None - - def fake_compute_api(cls, req, id): return True @@ -188,16 +184,20 @@ class ServersControllerTest(test.TestCase): self.stubs.Set(nova.db, 'instance_add_security_group', return_security_group) self.stubs.Set(nova.db, 'instance_update', instance_update) - self.stubs.Set(nova.db, 'instance_get_fixed_addresses', - instance_addresses) - self.stubs.Set(nova.db, 'instance_get_floating_address', - instance_addresses) self.config_drive = None self.controller = servers.Controller() self.ips_controller = ips.Controller() + def nw_info(*args, **kwargs): + return [] + + floaters = nw_info + fakes.stub_out_nw_api_get_instance_nw_info(self.stubs, nw_info) + fakes.stub_out_nw_api_get_floating_ips_by_fixed_address(self.stubs, + floaters) + def test_get_server_by_uuid(self): """ The steps involved with resolving a UUID are pretty complicated; @@ -451,8 +451,22 @@ class ServersControllerTest(test.TestCase): self.flags(use_ipv6=True) privates = ['192.168.0.3', '192.168.0.4'] publics = ['172.19.0.1', '172.19.0.2'] - new_return_server = return_server_with_attributes( - public_ips=publics, private_ips=privates) + public6s = ['b33f::fdee:ddff:fecc:bbaa'] + + def nw_info(*args, **kwargs): + return [(None, {'label': 'public', + 'ips': [dict(ip=ip) for ip in publics], + 'ip6s': [dict(ip=ip) for ip in public6s]}), + (None, {'label': 'private', + 'ips': [dict(ip=ip) for ip in privates]})] + + def floaters(*args, **kwargs): + return [] + + new_return_server = return_server_with_attributes() + fakes.stub_out_nw_api_get_instance_nw_info(self.stubs, nw_info) + fakes.stub_out_nw_api_get_floating_ips_by_fixed_address(self.stubs, + floaters) self.stubs.Set(nova.db, 'instance_get', new_return_server) req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) @@ -467,46 +481,35 @@ class ServersControllerTest(test.TestCase): {'addr': '192.168.0.4', 'version': 4}, ], 'public': [ + {'addr': '172.19.0.1', 'version': 4}, + {'addr': '172.19.0.2', 'version': 4}, {'addr': 'b33f::fdee:ddff:fecc:bbaa', 'version': 6}, - {'addr': '172.19.0.1', 'version': 4}, - {'addr': '172.19.0.2', 'version': 4}, ], } self.assertDictMatch(addresses, expected) - def test_get_server_by_id_with_addresses_ipv6_disabled(self): - # ipv6 flag is off by default - privates = ['192.168.0.3', '192.168.0.4'] - publics = ['172.19.0.1', '172.19.0.2'] - new_return_server = return_server_with_attributes( - public_ips=publics, private_ips=privates) - self.stubs.Set(nova.db, 'instance_get', new_return_server) - - req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) - res_dict = self.controller.show(req, FAKE_UUID) - - self.assertEqual(res_dict['server']['id'], FAKE_UUID) - self.assertEqual(res_dict['server']['name'], 'server1') - addresses = res_dict['server']['addresses'] - expected = { - 'private': [ - {'addr': '192.168.0.3', 'version': 4}, - {'addr': '192.168.0.4', 'version': 4}, - ], - 'public': [ - {'addr': '172.19.0.1', 'version': 4}, - {'addr': '172.19.0.2', 'version': 4}, - ], - } - self.assertDictMatch(addresses, expected) - - def test_get_server_addresses(self): + def test_get_server_addresses_from_nwinfo(self): self.flags(use_ipv6=True) privates = ['192.168.0.3', '192.168.0.4'] publics = ['172.19.0.1', '1.2.3.4', '172.19.0.2'] - new_return_server = return_server_with_attributes_by_uuid( - public_ips=publics, private_ips=privates) + + public6s = ['b33f::fdee:ddff:fecc:bbaa'] + + def nw_info(*args, **kwargs): + return [(None, {'label': 'public', + 'ips': [dict(ip=ip) for ip in publics], + 'ip6s': [dict(ip=ip) for ip in public6s]}), + (None, {'label': 'private', + 'ips': [dict(ip=ip) for ip in privates]})] + + def floaters(*args, **kwargs): + return [] + + new_return_server = return_server_with_attributes_by_uuid() + fakes.stub_out_nw_api_get_instance_nw_info(self.stubs, nw_info) + fakes.stub_out_nw_api_get_floating_ips_by_fixed_address(self.stubs, + floaters) self.stubs.Set(nova.db, 'instance_get_by_uuid', new_return_server) req = fakes.HTTPRequest.blank('/v2/fake/servers/%s/ips' % FAKE_UUID) @@ -519,21 +522,88 @@ class ServersControllerTest(test.TestCase): {'version': 4, 'addr': '192.168.0.4'}, ], 'public': [ - {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, {'version': 4, 'addr': '172.19.0.1'}, {'version': 4, 'addr': '1.2.3.4'}, {'version': 4, 'addr': '172.19.0.2'}, + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, ], }, } self.assertDictMatch(res_dict, expected) - def test_get_server_addresses_with_floating(self): - privates = ['192.168.0.3', '192.168.0.4'] - publics = ['172.19.0.1', '1.2.3.4', '172.19.0.2'] - new_return_server = return_server_with_attributes_by_uuid( - public_ips=publics, private_ips=privates, - public_ips_are_floating=True) + def test_get_server_addresses_from_cache(self): + pub0 = ('172.19.0.1', '172.19.0.2',) + pub1 = ('1.2.3.4',) + pub2 = ('b33f::fdee:ddff:fecc:bbaa',) + priv0 = ('192.168.0.3', '192.168.0.4',) + + def _ip(ip): + return {'address': ip, 'type': 'fixed'} + + nw_cache = [ + {'address': 'aa:aa:aa:aa:aa:aa', + 'id': 1, + 'network': {'bridge': 'br0', + 'id': 1, + 'label': 'public', + 'subnets': [{'cidr': '172.19.0.0/24', + 'ips': [_ip(ip) for ip in pub0]}, + {'cidr': '1.2.3.0/16', + 'ips': [_ip(ip) for ip in pub1]}, + {'cidr': 'b33f::/64', + 'ips': [_ip(ip) for ip in pub2]}]}}, + {'address': 'bb:bb:bb:bb:bb:bb', + 'id': 2, + 'network': {'bridge': 'br1', + 'id': 2, + 'label': 'private', + 'subnets': [{'cidr': '192.168.0.0/24', + 'ips': [_ip(ip) for ip in priv0]}]}}] + + kwargs = {'nw_cache': nw_cache} + new_return_server = return_server_with_attributes_by_uuid(**kwargs) + self.stubs.Set(nova.db, 'instance_get_by_uuid', new_return_server) + + req = fakes.HTTPRequest.blank('/v2/fake/servers/%s/ips' % FAKE_UUID) + res_dict = self.ips_controller.index(req, FAKE_UUID) + + expected = { + 'addresses': { + 'private': [ + {'version': 4, 'addr': '192.168.0.3'}, + {'version': 4, 'addr': '192.168.0.4'}, + ], + 'public': [ + {'version': 4, 'addr': '172.19.0.1'}, + {'version': 4, 'addr': '172.19.0.2'}, + {'version': 4, 'addr': '1.2.3.4'}, + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, + ], + }, + } + self.assertDictMatch(res_dict, expected) + + def test_get_server_addresses_with_floating_from_nwinfo(self): + ips = dict(privates=['192.168.0.3', '192.168.0.4'], + publics=['172.19.0.1', '1.2.3.4', '172.19.0.2']) + + def nw_info(*args, **kwargs): + return [(None, {'label': 'private', + 'ips': [dict(ip=ip) + for ip in ips['privates']]})] + + def floaters(*args, **kwargs): + # NOTE(jkoelker) floaters will get called multiple times + # this makes sure it will only return data + # once + pubs = list(ips['publics']) + ips['publics'] = [] + return pubs + + new_return_server = return_server_with_attributes_by_uuid() + fakes.stub_out_nw_api_get_instance_nw_info(self.stubs, nw_info) + fakes.stub_out_nw_api_get_floating_ips_by_fixed_address(self.stubs, + floaters) self.stubs.Set(nova.db, 'instance_get_by_uuid', new_return_server) req = fakes.HTTPRequest.blank('/v2/fake/servers/%s/ips' % FAKE_UUID) @@ -552,12 +622,26 @@ class ServersControllerTest(test.TestCase): } self.assertDictMatch(res_dict, expected) - def test_get_server_addresses_single_network(self): + def test_get_server_addresses_single_network_from_nwinfo(self): self.flags(use_ipv6=True) privates = ['192.168.0.3', '192.168.0.4'] publics = ['172.19.0.1', '1.2.3.4', '172.19.0.2'] - new_return_server = return_server_with_attributes_by_uuid( - public_ips=publics, private_ips=privates) + public6s = ['b33f::fdee:ddff:fecc:bbaa'] + + def nw_info(*args, **kwargs): + return [(None, {'label': 'public', + 'ips': [dict(ip=ip) for ip in publics], + 'ip6s': [dict(ip=ip) for ip in public6s]}), + (None, {'label': 'private', + 'ips': [dict(ip=ip) for ip in privates]})] + + def floaters(*args, **kwargs): + return [] + + new_return_server = return_server_with_attributes_by_uuid() + fakes.stub_out_nw_api_get_instance_nw_info(self.stubs, nw_info) + fakes.stub_out_nw_api_get_floating_ips_by_fixed_address(self.stubs, + floaters) self.stubs.Set(nova.db, 'instance_get_by_uuid', new_return_server) url = '/v2/fake/servers/%s/ips/public' % FAKE_UUID @@ -566,10 +650,10 @@ class ServersControllerTest(test.TestCase): expected = { 'public': [ - {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, {'version': 4, 'addr': '172.19.0.1'}, {'version': 4, 'addr': '1.2.3.4'}, {'version': 4, 'addr': '172.19.0.2'}, + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, ], } self.assertDictMatch(res_dict, expected) @@ -1523,24 +1607,6 @@ class ServersControllerCreateTest(test.TestCase): flavor_ref = 'http://localhost/fake/flavors/3' access_ipv4 = '1.2.3.4' access_ipv6 = 'fead::1234' - expected_flavor = { - "id": "3", - "links": [ - { - "rel": "bookmark", - "href": 'http://localhost/fake/flavors/3', - }, - ], - } - expected_image = { - "id": image_uuid, - "links": [ - { - "rel": "bookmark", - "href": 'http://localhost/fake/images/%s' % image_uuid, - }, - ], - } body = { 'server': { 'name': 'server_test', @@ -2390,10 +2456,26 @@ class ServersViewBuilderTest(test.TestCase): image_ref="5", uuid="deadbeef-feed-edee-beef-d0ea7beefedd", display_name="test_server", - public_ips=["192.168.0.3"], - private_ips=["172.19.0.1"], include_fake_metadata=False) + privates = ['172.19.0.1'] + publics = ['192.168.0.3'] + public6s = ['b33f::fdee:ddff:fecc:bbaa'] + + def nw_info(*args, **kwargs): + return [(None, {'label': 'public', + 'ips': [dict(ip=ip) for ip in publics], + 'ip6s': [dict(ip=ip) for ip in public6s]}), + (None, {'label': 'private', + 'ips': [dict(ip=ip) for ip in privates]})] + + def floaters(*args, **kwargs): + return [] + + fakes.stub_out_nw_api_get_instance_nw_info(self.stubs, nw_info) + fakes.stub_out_nw_api_get_floating_ips_by_fixed_address(self.stubs, + floaters) + self.uuid = self.instance['uuid'] self.view_builder = views.servers.ViewBuilder() self.request = fakes.HTTPRequest.blank("/v2") @@ -2485,8 +2567,8 @@ class ServersViewBuilderTest(test.TestCase): {'version': 4, 'addr': '172.19.0.1'} ], 'public': [ - {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, {'version': 4, 'addr': '192.168.0.3'}, + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, ], }, "metadata": {}, @@ -2557,8 +2639,8 @@ class ServersViewBuilderTest(test.TestCase): {'version': 4, 'addr': '172.19.0.1'} ], 'public': [ - {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, {'version': 4, 'addr': '192.168.0.3'}, + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, ], }, "metadata": {}, @@ -2637,8 +2719,8 @@ class ServersViewBuilderTest(test.TestCase): {'version': 4, 'addr': '172.19.0.1'} ], 'public': [ - {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, {'version': 4, 'addr': '192.168.0.3'}, + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, ], }, "metadata": {}, @@ -2704,8 +2786,8 @@ class ServersViewBuilderTest(test.TestCase): {'version': 4, 'addr': '172.19.0.1'} ], 'public': [ - {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, {'version': 4, 'addr': '192.168.0.3'}, + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, ], }, "metadata": {}, @@ -2769,8 +2851,8 @@ class ServersViewBuilderTest(test.TestCase): {'version': 4, 'addr': '172.19.0.1'} ], 'public': [ - {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, {'version': 4, 'addr': '192.168.0.3'}, + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, ], }, "metadata": {}, @@ -2836,8 +2918,8 @@ class ServersViewBuilderTest(test.TestCase): {'version': 4, 'addr': '172.19.0.1'} ], 'public': [ - {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, {'version': 4, 'addr': '192.168.0.3'}, + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, ] }, "metadata": {}, @@ -2908,8 +2990,8 @@ class ServersViewBuilderTest(test.TestCase): {'version': 4, 'addr': '172.19.0.1'} ], 'public': [ - {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, {'version': 4, 'addr': '192.168.0.3'}, + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, ] }, "metadata": { diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index 9fa1749ef351..728b180a8d90 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -466,35 +466,14 @@ class FakeRateLimiter(object): FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' -def create_fixed_ips(project_id, publics, privates, publics_are_floating): - if publics is None: - publics = [] - if privates is None: - privates = [] +def create_info_cache(nw_cache): + if nw_cache is None: + return {} - fixed_ips = [] - private_vif = dict(address='aa:bb:cc:dd:ee:ff') - private_net = dict(label='private', project_id=project_id, cidr_v6=None) + if not isinstance(nw_cache, basestring): + nw_cache = utils.dumps(nw_cache) - for private in privates: - entry = dict(address=private, network=private_net, - virtual_interface=private_vif, floating_ips=[]) - if publics_are_floating: - for public in publics: - entry['floating_ips'].append(dict(address=public)) - # Only add them once - publics = [] - fixed_ips.append(entry) - - if not publics_are_floating: - public_vif = dict(address='ff:ee:dd:cc:bb:aa') - public_net = dict(label='public', project_id=project_id, - cidr_v6='b33f::/64') - for public in publics: - entry = dict(address=public, network=public_net, - virtual_interface=public_vif, floating_ips=[]) - fixed_ips.append(entry) - return fixed_ips + return {"info_cache": {"network_info": nw_cache}} def stub_instance(id, user_id='fake', project_id='fake', host=None, @@ -502,10 +481,9 @@ def stub_instance(id, user_id='fake', project_id='fake', host=None, reservation_id="", uuid=FAKE_UUID, image_ref="10", flavor_id="1", name=None, key_name='', access_ipv4=None, access_ipv6=None, progress=0, - auto_disk_config=False, public_ips=None, private_ips=None, - public_ips_are_floating=False, display_name=None, + auto_disk_config=False, display_name=None, include_fake_metadata=True, - power_state=None): + power_state=None, nw_cache=None): if include_fake_metadata: metadata = [models.InstanceMetadata(key='seq', value=id)] @@ -522,14 +500,13 @@ def stub_instance(id, user_id='fake', project_id='fake', host=None, else: key_data = '' - fixed_ips = create_fixed_ips(project_id, public_ips, private_ips, - public_ips_are_floating) - # ReservationID isn't sent back, hack it in there. server_name = name or "server%s" % id if reservation_id != "": server_name = "reservation_%s" % (reservation_id, ) + info_cache = create_info_cache(nw_cache) + instance = { "id": int(id), "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), @@ -569,8 +546,9 @@ def stub_instance(id, user_id='fake', project_id='fake', host=None, "progress": progress, "auto_disk_config": auto_disk_config, "name": "instance-%s" % id, - "fixed_ips": fixed_ips, "shutdown_terminate": True, "disable_terminate": False} + instance.update(info_cache) + return instance diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 45045989cac7..f24f020a6e7c 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -441,12 +441,6 @@ def stub_out_db_instance_api(stubs, injected=True): else: return [FakeModel(flat_network_fields)] - def fake_instance_get_fixed_addresses(context, instance_id): - return [FakeModel(fixed_ip_fields).address] - - def fake_instance_get_fixed_addresses_v6(context, instance_id): - return [FakeModel(fixed_ip_fields).address] - def fake_fixed_ip_get_by_instance(context, instance_id): return [FakeModel(fixed_ip_fields)] @@ -455,8 +449,6 @@ def stub_out_db_instance_api(stubs, injected=True): fake_instance_type_get_all, fake_instance_type_get_by_name, fake_instance_type_get, - fake_instance_get_fixed_addresses, - fake_instance_get_fixed_addresses_v6, fake_network_get_all_by_instance, fake_fixed_ip_get_by_instance] stub_out(stubs, funcs) diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index 07f07f4613e1..071990bd6e9d 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -71,6 +71,36 @@ class FakeNetworkManager(network_manager.NetworkManager): """ class FakeDB: + vifs = [{'id': 0, + 'instance_id': 0, + 'network_id': 1, + 'address': 'DC:AD:BE:FF:EF:01'}, + {'id': 1, + 'instance_id': 20, + 'network_id': 21, + 'address': 'DC:AD:BE:FF:EF:02'}, + {'id': 2, + 'instance_id': 30, + 'network_id': 31, + 'address': 'DC:AD:BE:FF:EF:03'}] + + floating_ips = [dict(address='172.16.1.1', + fixed_ip_id=100), + dict(address='172.16.1.2', + fixed_ip_id=200), + dict(address='173.16.1.2', + fixed_ip_id=210)] + + fixed_ips = [dict(id=100, + address='172.16.0.1', + virtual_interface_id=0), + dict(id=200, + address='172.16.0.2', + virtual_interface_id=1), + dict(id=210, + address='173.16.0.2', + virtual_interface_id=2)] + def fixed_ip_get_by_instance(self, context, instance_id): return [dict(address='10.0.0.0'), dict(address='10.0.0.1'), dict(address='10.0.0.2')] @@ -96,26 +126,7 @@ class FakeNetworkManager(network_manager.NetworkManager): return True def virtual_interface_get_all(self, context): - floats = [{'address': '172.16.1.1'}, - {'address': '172.16.1.2'}, - {'address': '173.16.1.2'}] - - vifs = [{'instance_id': 0, - 'network_id': 1, - 'address': 'DC:AD:BE:FF:EF:01', - 'fixed_ips': [{'address': '172.16.0.1', - 'floating_ips': [floats[0]]}]}, - {'instance_id': 20, - 'network_id': 21, - 'address': 'DC:AD:BE:FF:EF:02', - 'fixed_ips': [{'address': '172.16.0.2', - 'floating_ips': [floats[1]]}]}, - {'instance_id': 30, - 'network_id': 31, - 'address': 'DC:AD:BE:FF:EF:03', - 'fixed_ips': [{'address': '173.16.0.2', - 'floating_ips': [floats[2]]}]}] - return vifs + return self.vifs def instance_get_id_to_uuid_mapping(self, context, ids): # NOTE(jkoelker): This is just here until we can rely on UUIDs @@ -124,6 +135,10 @@ class FakeNetworkManager(network_manager.NetworkManager): mapping[id] = str(utils.gen_uuid()) return mapping + def fixed_ips_by_virtual_interface(self, context, vif_id): + return [ip for ip in self.fixed_ips + if ip['virtual_interface_id'] == vif_id] + def __init__(self): self.db = self.FakeDB() self.deallocate_called = None @@ -285,3 +300,15 @@ def fake_get_instance_nw_info(stubs, num_networks=1, ips_per_vif=2, self.project_id = 1 return network.get_instance_nw_info(FakeContext(), 0, 0, 0, None) + + +def stub_out_nw_api_get_instance_nw_info(stubs, func=None): + import nova.network + + def get_instance_nw_info(self, context, instance): + return [(None, {'label': 'public', + 'ips': [{'ip': '192.168.0.3'}], + 'ip6s': []})] + if func is None: + func = get_instance_nw_info + stubs.Set(nova.network.API, 'get_instance_nw_info', func) diff --git a/nova/tests/test_compute_utils.py b/nova/tests/test_compute_utils.py index b452ec665ca3..7ba03063f7f1 100644 --- a/nova/tests/test_compute_utils.py +++ b/nova/tests/test_compute_utils.py @@ -86,7 +86,7 @@ class UsageInfoTestCase(test.TestCase): type_id = instance_types.get_instance_type_by_name('m1.tiny')['id'] self.assertEquals(str(payload['instance_type_id']), str(type_id)) for attr in ('display_name', 'created_at', 'launched_at', - 'state', 'state_description', 'fixed_ips', + 'state', 'state_description', 'bandwidth', 'audit_period_beginning', 'audit_period_ending'): self.assertTrue(attr in payload, diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py index 524a2811f301..356be7a58af9 100644 --- a/nova/tests/test_db_api.py +++ b/nova/tests/test_db_api.py @@ -67,19 +67,6 @@ class DbApiTestCase(test.TestCase): self.project_id) self.assertEqual(instance['id'], result['id']) - def test_instance_get_project_vpn_joins(self): - values = {'instance_type_id': FLAGS.default_instance_type, - 'image_ref': FLAGS.vpn_image_id, - 'project_id': self.project_id, - } - instance = db.instance_create(self.context, values) - _setup_networking(instance['id']) - result = db.instance_get_project_vpn(self.context.elevated(), - self.project_id) - self.assertEqual(instance['id'], result['id']) - self.assertEqual(result['fixed_ips'][0]['floating_ips'][0].address, - '1.2.1.2') - def test_instance_get_all_by_filters(self): args = {'reservation_id': 'a', 'image_ref': 1, 'host': 'host1'} inst1 = db.instance_create(self.context, args) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 8334e6f9484a..1490f91737f1 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1328,17 +1328,22 @@ class IptablesFirewallTestCase(test.TestCase): return '', '' print cmd, kwargs + network_info = _fake_network_info(self.stubs, 1) + def get_fixed_ips(*args, **kwargs): ips = [] for network, info in network_info: ips.extend(info['ips']) - return [ip['ip'] for ip in ips] + return [ip['ip'] for ip in ips] + + def nw_info(*args, **kwargs): + return network_info from nova.network import linux_net linux_net.iptables_manager.execute = fake_iptables_execute - network_info = _fake_network_info(self.stubs, 1) - self.stubs.Set(db, 'instance_get_fixed_addresses', get_fixed_ips) + fake_network.stub_out_nw_api_get_instance_nw_info(self.stubs, + nw_info) self.fw.prepare_instance_filter(instance_ref, network_info) self.fw.apply_instance_filter(instance_ref, network_info) diff --git a/nova/tests/test_linux_net.py b/nova/tests/test_linux_net.py index c8f8cbe061c3..21c5284a533c 100644 --- a/nova/tests/test_linux_net.py +++ b/nova/tests/test_linux_net.py @@ -19,6 +19,7 @@ import os import mox +from nova import context from nova import db from nova import flags from nova import log as logging @@ -102,8 +103,7 @@ fixed_ips = [{'id': 0, 'instance_id': 0, 'allocated': True, 'virtual_interface_id': 0, - 'virtual_interface': addresses[0], - 'instance': instances[0], + 'instance_id': 0, 'floating_ips': []}, {'id': 1, 'network_id': 1, @@ -111,8 +111,7 @@ fixed_ips = [{'id': 0, 'instance_id': 0, 'allocated': True, 'virtual_interface_id': 1, - 'virtual_interface': addresses[1], - 'instance': instances[0], + 'instance_id': 0, 'floating_ips': []}, {'id': 2, 'network_id': 1, @@ -120,8 +119,7 @@ fixed_ips = [{'id': 0, 'instance_id': 1, 'allocated': True, 'virtual_interface_id': 2, - 'virtual_interface': addresses[2], - 'instance': instances[1], + 'instance_id': 1, 'floating_ips': []}, {'id': 3, 'network_id': 0, @@ -129,8 +127,7 @@ fixed_ips = [{'id': 0, 'instance_id': 1, 'allocated': True, 'virtual_interface_id': 3, - 'virtual_interface': addresses[3], - 'instance': instances[1], + 'instance_id': 1, 'floating_ips': []}, {'id': 4, 'network_id': 0, @@ -138,8 +135,7 @@ fixed_ips = [{'id': 0, 'instance_id': 0, 'allocated': True, 'virtual_interface_id': 4, - 'virtual_interface': addresses[4], - 'instance': instances[0], + 'instance_id': 0, 'floating_ips': []}, {'id': 5, 'network_id': 1, @@ -147,8 +143,7 @@ fixed_ips = [{'id': 0, 'instance_id': 1, 'allocated': True, 'virtual_interface_id': 5, - 'virtual_interface': addresses[5], - 'instance': instances[1], + 'instance_id': 1, 'floating_ips': []}] @@ -156,37 +151,31 @@ vifs = [{'id': 0, 'address': 'DE:AD:BE:EF:00:00', 'uuid': '00000000-0000-0000-0000-0000000000000000', 'network_id': 0, - 'network': networks[0], 'instance_id': 0}, {'id': 1, 'address': 'DE:AD:BE:EF:00:01', 'uuid': '00000000-0000-0000-0000-0000000000000001', 'network_id': 1, - 'network': networks[1], 'instance_id': 0}, {'id': 2, 'address': 'DE:AD:BE:EF:00:02', 'uuid': '00000000-0000-0000-0000-0000000000000002', 'network_id': 1, - 'network': networks[1], 'instance_id': 1}, {'id': 3, 'address': 'DE:AD:BE:EF:00:03', 'uuid': '00000000-0000-0000-0000-0000000000000003', 'network_id': 0, - 'network': networks[0], 'instance_id': 1}, {'id': 4, 'address': 'DE:AD:BE:EF:00:04', 'uuid': '00000000-0000-0000-0000-0000000000000004', 'network_id': 0, - 'network': networks[0], 'instance_id': 0}, {'id': 5, 'address': 'DE:AD:BE:EF:00:05', 'uuid': '00000000-0000-0000-0000-0000000000000005', 'network_id': 1, - 'network': networks[1], 'instance_id': 1}] @@ -197,10 +186,20 @@ class LinuxNetworkTestCase(test.TestCase): network_driver = FLAGS.network_driver self.driver = utils.import_object(network_driver) self.driver.db = db + self.context = context.RequestContext('testuser', 'testproject', + is_admin=True) def test_update_dhcp_for_nw00(self): self.flags(use_single_default_gateway=True) + def get_vif(_context, vif_id): + return vifs[vif_id] + + def get_instance(_context, instance_id): + return instances[instance_id] + + self.stubs.Set(db, 'virtual_interface_get', get_vif) + self.stubs.Set(db, 'instance_get', get_instance) self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') self.mox.StubOutWithMock(self.driver, 'write_to_file') @@ -236,10 +235,19 @@ class LinuxNetworkTestCase(test.TestCase): self.mox.ReplayAll() - self.driver.update_dhcp(None, "eth0", networks[0]) + self.driver.update_dhcp(self.context, "eth0", networks[0]) def test_update_dhcp_for_nw01(self): self.flags(use_single_default_gateway=True) + + def get_vif(_context, vif_id): + return vifs[vif_id] + + def get_instance(_context, instance_id): + return instances[instance_id] + + self.stubs.Set(db, 'virtual_interface_get', get_vif) + self.stubs.Set(db, 'instance_get', get_instance) self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') self.mox.StubOutWithMock(self.driver, 'write_to_file') @@ -275,10 +283,19 @@ class LinuxNetworkTestCase(test.TestCase): self.mox.ReplayAll() - self.driver.update_dhcp(None, "eth0", networks[0]) + self.driver.update_dhcp(self.context, "eth0", networks[0]) def test_get_dhcp_hosts_for_nw00(self): self.flags(use_single_default_gateway=True) + + def get_vif(_context, vif_id): + return vifs[vif_id] + + def get_instance(_context, instance_id): + return instances[instance_id] + + self.stubs.Set(db, 'virtual_interface_get', get_vif) + self.stubs.Set(db, 'instance_get', get_instance) self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') db.network_get_associated_fixed_ips(mox.IgnoreArg(), @@ -288,16 +305,25 @@ class LinuxNetworkTestCase(test.TestCase): self.mox.ReplayAll() expected = \ - "10.0.0.1,fake_instance00.novalocal,"\ + "DE:AD:BE:EF:00:00,fake_instance00.novalocal,"\ "192.168.0.100,net:NW-i00000000-0\n"\ - "10.0.0.4,fake_instance01.novalocal,"\ + "DE:AD:BE:EF:00:03,fake_instance01.novalocal,"\ "192.168.1.101,net:NW-i00000001-0" - actual_hosts = self.driver.get_dhcp_hosts(None, networks[1]) + actual_hosts = self.driver.get_dhcp_hosts(self.context, networks[1]) self.assertEquals(actual_hosts, expected) def test_get_dhcp_hosts_for_nw01(self): self.flags(use_single_default_gateway=True) + + def get_vif(_context, vif_id): + return vifs[vif_id] + + def get_instance(_context, instance_id): + return instances[instance_id] + + self.stubs.Set(db, 'virtual_interface_get', get_vif) + self.stubs.Set(db, 'instance_get', get_instance) self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') db.network_get_associated_fixed_ips(mox.IgnoreArg(), @@ -307,15 +333,19 @@ class LinuxNetworkTestCase(test.TestCase): self.mox.ReplayAll() expected = \ - "10.0.0.2,fake_instance00.novalocal,"\ + "DE:AD:BE:EF:00:01,fake_instance00.novalocal,"\ "192.168.1.100,net:NW-i00000000-1\n"\ - "10.0.0.3,fake_instance01.novalocal,"\ + "DE:AD:BE:EF:00:02,fake_instance01.novalocal,"\ "192.168.0.101,net:NW-i00000001-1" - actual_hosts = self.driver.get_dhcp_hosts(None, networks[0]) + actual_hosts = self.driver.get_dhcp_hosts(self.context, networks[0]) self.assertEquals(actual_hosts, expected) def test_get_dhcp_opts_for_nw00(self): + def get_instance(_context, instance_id): + return instances[instance_id] + + self.stubs.Set(db, 'instance_get', get_instance) self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') @@ -337,11 +367,16 @@ class LinuxNetworkTestCase(test.TestCase): self.mox.ReplayAll() expected_opts = 'NW-i00000001-0,3' - actual_opts = self.driver.get_dhcp_opts(None, networks[0]) + actual_opts = self.driver.get_dhcp_opts(self.context, networks[0]) self.assertEquals(actual_opts, expected_opts) def test_get_dhcp_opts_for_nw01(self): + def get_instance(_context, instance_id): + print instance_id + return instances[instance_id] + + self.stubs.Set(db, 'instance_get', get_instance) self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') @@ -363,18 +398,20 @@ class LinuxNetworkTestCase(test.TestCase): self.mox.ReplayAll() expected_opts = "NW-i00000000-1,3" - actual_opts = self.driver.get_dhcp_opts(None, networks[1]) + actual_opts = self.driver.get_dhcp_opts(self.context, networks[1]) self.assertEquals(actual_opts, expected_opts) def test_dhcp_opts_not_default_gateway_network(self): expected = "NW-i00000000-0,3" - actual = self.driver._host_dhcp_opts(fixed_ips[0]) + actual = self.driver._host_dhcp_opts(fixed_ips[0], instances[0]) self.assertEquals(actual, expected) def test_host_dhcp_without_default_gateway_network(self): - expected = ("10.0.0.1,fake_instance00.novalocal,192.168.0.100") - actual = self.driver._host_dhcp(fixed_ips[0]) + expected = ','.join(['DE:AD:BE:EF:00:00', + 'fake_instance00.novalocal', + '192.168.0.100']) + actual = self.driver._host_dhcp(fixed_ips[0], vifs[0], instances[0]) self.assertEquals(actual, expected) def test_linux_bridge_driver_plug(self): diff --git a/nova/tests/test_metadata.py b/nova/tests/test_metadata.py index 257bc28c8f56..ff67203127f4 100644 --- a/nova/tests/test_metadata.py +++ b/nova/tests/test_metadata.py @@ -76,16 +76,12 @@ class MetadataTestCase(test.TestCase): def instance_get_list(*args, **kwargs): return [self.instance] - def floating_get(*args, **kwargs): - return '99.99.99.99' - self.stubs.Set(network.API, 'get_instance_nw_info', fake_get_instance_nw_info) self.stubs.Set(network.API, 'get_floating_ips_by_fixed_address', fake_get_floating_ips_by_fixed_address) self.stubs.Set(api, 'instance_get', instance_get) self.stubs.Set(api, 'instance_get_all_by_filters', instance_get_list) - self.stubs.Set(api, 'instance_get_floating_address', floating_get) self.app = handler.MetadataRequestHandler() network_manager = fake_network.FakeNetworkManager() self.stubs.Set(self.app.compute_api.network_api, diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 2776f3664f67..8f8efc99167f 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -35,15 +35,6 @@ LOG = logging.getLogger('nova.tests.network') HOST = "testhost" -class FakeModel(dict): - """Represent a model from the db""" - def __init__(self, *args, **kwargs): - self.update(kwargs) - - def __getattr__(self, name): - return self[name] - - networks = [{'id': 0, 'uuid': "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", 'label': 'test0', @@ -85,7 +76,6 @@ networks = [{'id': 0, 'project_id': 'fake_project', 'vpn_public_address': '192.168.1.2'}] - fixed_ips = [{'id': 0, 'network_id': 0, 'address': '192.168.0.100', @@ -118,19 +108,16 @@ vifs = [{'id': 0, 'address': 'DE:AD:BE:EF:00:00', 'uuid': '00000000-0000-0000-0000-0000000000000000', 'network_id': 0, - 'network': FakeModel(**networks[0]), 'instance_id': 0}, {'id': 1, 'address': 'DE:AD:BE:EF:00:01', 'uuid': '00000000-0000-0000-0000-0000000000000001', 'network_id': 1, - 'network': FakeModel(**networks[1]), 'instance_id': 0}, {'id': 2, 'address': 'DE:AD:BE:EF:00:02', 'uuid': '00000000-0000-0000-0000-0000000000000002', 'network_id': 2, - 'network': None, 'instance_id': 0}] @@ -196,6 +183,7 @@ class FlatNetworkTestCase(test.TestCase): self.assertDictListMatch(info['ips'], check) def test_validate_networks(self): + self.mox.StubOutWithMock(db, 'network_get') self.mox.StubOutWithMock(db, 'network_get_all_by_uuids') self.mox.StubOutWithMock(db, "fixed_ip_get_by_address") @@ -203,11 +191,13 @@ class FlatNetworkTestCase(test.TestCase): "192.168.1.100")] db.network_get_all_by_uuids(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) + db.network_get(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks[1]) - fixed_ips[1]['network'] = FakeModel(**networks[1]) - fixed_ips[1]['instance'] = None + ip = fixed_ips[1].copy() + ip['instance_id'] = None db.fixed_ip_get_by_address(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(fixed_ips[1]) + mox.IgnoreArg()).AndReturn(ip) self.mox.ReplayAll() self.network.validate_networks(self.context, requested_networks) @@ -448,6 +438,10 @@ class VlanNetworkTestCase(test.TestCase): cidr='192.168.0.1/24', network_size=100) def test_validate_networks(self): + def network_get(_context, network_id): + return networks[network_id] + + self.stubs.Set(db, 'network_get', network_get) self.mox.StubOutWithMock(db, 'network_get_all_by_uuids') self.mox.StubOutWithMock(db, "fixed_ip_get_by_address") @@ -457,8 +451,8 @@ class VlanNetworkTestCase(test.TestCase): mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(networks) - fixed_ips[1]['network'] = FakeModel(**networks[1]) - fixed_ips[1]['instance'] = None + fixed_ips[1]['network_id'] = networks[1]['id'] + fixed_ips[1]['instance_id'] = None db.fixed_ip_get_by_address(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(fixed_ips[1]) @@ -613,14 +607,20 @@ class VlanNetworkTestCase(test.TestCase): return {'address': '10.0.0.1', 'pool': 'nova', 'interface': 'eth0', - 'network': {'multi_host': False, 'host': 'jibberjabber'}} + 'network_id': 'blah'} + + def fake4_network(*args, **kwargs): + return {'multi_host': False, 'host': 'jibberjabber'} # fixed ip with local host def fake5(*args, **kwargs): return {'address': '10.0.0.1', 'pool': 'nova', 'interface': 'eth0', - 'network': {'multi_host': False, 'host': 'testhost'}} + 'network_id': 'blahblah'} + + def fake5_network(*args, **kwargs): + return {'multi_host': False, 'host': 'testhost'} def fake6(*args, **kwargs): self.local = False @@ -643,6 +643,7 @@ class VlanNetworkTestCase(test.TestCase): # does not raise and makes call remotely self.local = True self.stubs.Set(self.network.db, 'fixed_ip_get_by_address', fake4) + self.stubs.Set(self.network.db, 'network_get', fake4_network) self.stubs.Set(rpc, 'cast', fake6) self.network.associate_floating_ip(ctxt, mox.IgnoreArg(), mox.IgnoreArg()) @@ -651,6 +652,7 @@ class VlanNetworkTestCase(test.TestCase): # does not raise and makes call locally self.local = False self.stubs.Set(self.network.db, 'fixed_ip_get_by_address', fake5) + self.stubs.Set(self.network.db, 'network_get', fake5_network) self.stubs.Set(self.network, '_associate_floating_ip', fake7) self.network.associate_floating_ip(ctxt, mox.IgnoreArg(), mox.IgnoreArg()) @@ -682,14 +684,21 @@ class VlanNetworkTestCase(test.TestCase): return {'address': '10.0.0.1', 'pool': 'nova', 'interface': 'eth0', - 'network': {'multi_host': False, 'host': 'jibberjabber'}} + 'network_id': 'blah'} + + def fake4_network(*args, **kwargs): + return {'multi_host': False, + 'host': 'jibberjabber'} # fixed ip with local host def fake5(*args, **kwargs): return {'address': '10.0.0.1', 'pool': 'nova', 'interface': 'eth0', - 'network': {'multi_host': False, 'host': 'testhost'}} + 'network_id': 'blahblah'} + + def fake5_network(*args, **kwargs): + return {'multi_host': False, 'host': 'testhost'} def fake6(*args, **kwargs): self.local = False @@ -711,6 +720,7 @@ class VlanNetworkTestCase(test.TestCase): # does not raise and makes call remotely self.local = True self.stubs.Set(self.network.db, 'fixed_ip_get', fake4) + self.stubs.Set(self.network.db, 'network_get', fake4_network) self.stubs.Set(rpc, 'cast', fake6) self.network.disassociate_floating_ip(ctxt, mox.IgnoreArg()) self.assertFalse(self.local) @@ -718,6 +728,7 @@ class VlanNetworkTestCase(test.TestCase): # does not raise and makes call locally self.local = False self.stubs.Set(self.network.db, 'fixed_ip_get', fake5) + self.stubs.Set(self.network.db, 'network_get', fake5_network) self.stubs.Set(self.network, '_disassociate_floating_ip', fake7) self.network.disassociate_floating_ip(ctxt, mox.IgnoreArg()) self.assertTrue(self.local) @@ -752,6 +763,11 @@ class VlanNetworkTestCase(test.TestCase): """Makes sure that we cannot deallocaate or disassociate a public ip of other project""" + def network_get(_context, network_id): + return networks[network_id] + + self.stubs.Set(db, 'network_get', network_get) + context1 = context.RequestContext('user', 'project1') context2 = context.RequestContext('user', 'project2') @@ -789,6 +805,7 @@ class VlanNetworkTestCase(test.TestCase): float_addr) # Clean up the ip addresses + self.network.disassociate_floating_ip(context1, float_addr) self.network.deallocate_floating_ip(context1, float_addr) self.network.deallocate_fixed_ip(context1, fix_addr) db.floating_ip_destroy(context1.elevated(), float_addr) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 77cde2a02c5e..180400af4587 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -1411,14 +1411,19 @@ class XenAPIDom0IptablesFirewallTestCase(test.TestCase): instance_ref = db.instance_get(admin_ctxt, instance_ref['id']) src_instance_ref = db.instance_get(admin_ctxt, src_instance_ref['id']) + network_info = fake_network.fake_get_instance_nw_info(self.stubs, 1) + def get_fixed_ips(*args, **kwargs): ips = [] for _n, info in network_info: ips.extend(info['ips']) return [ip['ip'] for ip in ips] - network_info = fake_network.fake_get_instance_nw_info(self.stubs, 1) - self.stubs.Set(db, 'instance_get_fixed_addresses', get_fixed_ips) + def nw_info(*args, **kwargs): + return network_info + + fake_network.stub_out_nw_api_get_instance_nw_info(self.stubs, + nw_info) self.fw.prepare_instance_filter(instance_ref, network_info) self.fw.apply_instance_filter(instance_ref, network_info) diff --git a/nova/tests/vmwareapi/db_fakes.py b/nova/tests/vmwareapi/db_fakes.py index c745944c7c97..599ba271cde0 100644 --- a/nova/tests/vmwareapi/db_fakes.py +++ b/nova/tests/vmwareapi/db_fakes.py @@ -96,10 +96,6 @@ def stub_out_db_instance_api(stubs): """Stubs out the db.instance_action_create method.""" pass - def fake_instance_get_fixed_addresses(context, instance_id): - """Stubs out the db.instance_get_fixed_address method.""" - return '10.10.10.10' - def fake_instance_type_get_all(context, inactive=0, filters=None): return INSTANCE_TYPES.values() @@ -109,7 +105,5 @@ def stub_out_db_instance_api(stubs): stubs.Set(db, 'instance_create', fake_instance_create) stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance) stubs.Set(db, 'instance_action_create', fake_instance_action_create) - stubs.Set(db, 'instance_get_fixed_addresses', - fake_instance_get_fixed_addresses) stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all) stubs.Set(db, 'instance_type_get_by_name', fake_instance_type_get_by_name) diff --git a/nova/utils.py b/nova/utils.py index 355f577abf61..53832153edcc 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -398,7 +398,7 @@ def current_audit_period(unit=None): return (begin, end) -def usage_from_instance(instance_ref, **kw): +def usage_from_instance(instance_ref, network_info=None, **kw): image_ref_url = "%s/images/%s" % (generate_glance_url(), instance_ref['image_ref']) @@ -415,8 +415,16 @@ def usage_from_instance(instance_ref, **kw): image_ref_url=image_ref_url, state=instance_ref['vm_state'], state_description=instance_ref['task_state'] \ - if instance_ref['task_state'] else '', - fixed_ips=[a.address for a in instance_ref['fixed_ips']]) + if instance_ref['task_state'] else '') + + # NOTE(jkoelker) This nastyness can go away once compute uses the + # network model + if network_info is not None: + fixed_ips = [] + for network, info in network_info: + fixed_ips.extend([ip['ip'] for ip in info['ips']]) + usage_info['fixed_ips'] = fixed_ips + usage_info.update(kw) return usage_info diff --git a/nova/virt/firewall.py b/nova/virt/firewall.py index c60148231a96..3426120ac2fe 100644 --- a/nova/virt/firewall.py +++ b/nova/virt/firewall.py @@ -303,13 +303,24 @@ class IptablesFirewallDriver(FirewallDriver): fw_rules += [' '.join(args)] else: if rule['grantee_group']: + # FIXME(jkoelker) This needs to be ported up into + # the compute manager which already + # has access to a nw_api handle, + # and should be the only one making + # making rpc calls. + import nova.network + nw_api = nova.network.API() for instance in rule['grantee_group']['instances']: LOG.info('instance: %r', instance) - ips = db.instance_get_fixed_addresses(ctxt, - instance['id']) + ips = [] + nw_info = nw_api.get_instance_nw_info(ctxt, + instance) + for net in nw_info: + ips.extend(net[1]['ips']) + LOG.info('ips: %r', ips) for ip in ips: - subrule = args + ['-s %s' % ip] + subrule = args + ['-s %s' % ip['ip']] fw_rules += [' '.join(subrule)] LOG.info('Using fw_rules: %r', fw_rules)