From e8560a76cbaaebd25f60886f5966297a94597993 Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Mon, 1 Oct 2012 01:21:07 +0100 Subject: Add version to network rpc API. This patch adds versioning support for the nova-network rpc API. During Folsom development I added versioning to all rpc APIs with the exception of network and volume. I was holding off on those to see what the fate of that code in nova was going to be for sure. Adding it to the volume API in nova doesn't make much sense at this point (but doing it in Cinder does). Since nova-network is sticking around for a while, it seems worthwhile to add a version number to the API for any future changes while the code is still in the tree. There are plenty of things in this rpc API that could be cleaned up or improved, but I held off on doing any of those things. I would recommend the same for anyone else (unless it's a bug that needs to be fixed). I think any further improvements should be focused on Quantum integration with Nova or Quantum itself. Fixes bug 1060197. Change-Id: I63ce57657388166544202af9c44c2298ae551aea --- bin/nova-dhcpbridge | 14 +- nova/network/api.py | 208 +++++++++------------------- nova/network/manager.py | 53 +++---- nova/network/rpcapi.py | 274 +++++++++++++++++++++++++++++++++++++ nova/tests/compute/test_compute.py | 6 +- nova/tests/network/test_api.py | 2 +- nova/tests/network/test_rpcapi.py | 260 +++++++++++++++++++++++++++++++++++ 7 files changed, 622 insertions(+), 195 deletions(-) create mode 100644 nova/network/rpcapi.py create mode 100644 nova/tests/network/test_rpcapi.py diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge index 0693ae27a..114dee268 100755 --- a/bin/nova-dhcpbridge +++ b/bin/nova-dhcpbridge @@ -39,6 +39,7 @@ from nova import context from nova import db from nova import flags from nova.network import linux_net +from nova.network import rpcapi as network_rpcapi from nova.openstack.common import importutils from nova.openstack.common import log as logging from nova.openstack.common import rpc @@ -57,10 +58,8 @@ def add_lease(mac, ip_address): network_manager.lease_fixed_ip(context.get_admin_context(), ip_address) else: - rpc.cast(context.get_admin_context(), - "%s.%s" % (FLAGS.network_topic, FLAGS.host), - {"method": "lease_fixed_ip", - "args": {"address": ip_address}}) + api = network_rpcapi.NetworkAPI() + api.lease_fixed_ip(context.get_admin_context(), ip_address, FLAGS.host) def old_lease(mac, ip_address): @@ -79,10 +78,9 @@ def del_lease(mac, ip_address): network_manager.release_fixed_ip(context.get_admin_context(), ip_address) else: - rpc.cast(context.get_admin_context(), - "%s.%s" % (FLAGS.network_topic, FLAGS.host), - {"method": "release_fixed_ip", - "args": {"address": ip_address}}) + api = network_rpcapi.NetworkAPI() + api.release_fixed_ip(context.get_admin_context(), ip_address, + FLAGS.host) def init_leases(network_id): diff --git a/nova/network/api.py b/nova/network/api.py index f0c4e151d..18feea27e 100644 --- a/nova/network/api.py +++ b/nova/network/api.py @@ -23,6 +23,7 @@ import inspect from nova.db import base from nova import flags from nova.network import model as network_model +from nova.network import rpcapi as network_rpcapi from nova.openstack.common import log as logging from nova.openstack.common import rpc @@ -83,96 +84,58 @@ def update_instance_cache_with_nw_info(api, context, instance, class API(base.Base): """API for interacting with the network manager.""" + def __init__(self, **kwargs): + self.network_rpcapi = network_rpcapi.NetworkAPI() + super(API, self).__init__(**kwargs) + def get_all(self, context): - return rpc.call(context, - FLAGS.network_topic, - {'method': 'get_all_networks'}) + return self.network_rpcapi.get_all_networks(context) def get(self, context, network_uuid): - return rpc.call(context, - FLAGS.network_topic, - {'method': 'get_network', - 'args': {'network_uuid': network_uuid}}) + return self.network_rpcapi.get_network(context, network_uuid) def create(self, context, **kwargs): - return rpc.call(context, - FLAGS.network_topic, - {'method': 'create_networks', - 'args': kwargs}) + return self.network_rpcapi.create_networks(context, **kwargs) def delete(self, context, network_uuid): - return rpc.call(context, - FLAGS.network_topic, - {'method': 'delete_network', - 'args': {'fixed_range': None, - 'uuid': network_uuid}}) + return self.network_rpcapi.delete_network(context, network_uuid, None) def disassociate(self, context, network_uuid): - return rpc.call(context, - FLAGS.network_topic, - {'method': 'disassociate_network', - 'args': {'network_uuid': network_uuid}}) + return self.network_rpcapi.disassociate_network(context, network_uuid) def get_fixed_ip(self, context, id): - return rpc.call(context, - FLAGS.network_topic, - {'method': 'get_fixed_ip', - 'args': {'id': id}}) + return self.network_rpcapi.get_fixed_ip(context, id) def get_fixed_ip_by_address(self, context, address): - return rpc.call(context, - FLAGS.network_topic, - {'method': 'get_fixed_ip_by_address', - 'args': {'address': address}}) + return self.network_rpcapi.get_fixed_ip_by_address(context, address) def get_floating_ip(self, context, id): - return rpc.call(context, - FLAGS.network_topic, - {'method': 'get_floating_ip', - 'args': {'id': id}}) + return self.network_rpcapi.get_floating_ip(context, id) def get_floating_ip_pools(self, context): - return rpc.call(context, - FLAGS.network_topic, - {'method': 'get_floating_pools'}) + return self.network_rpcapi.get_floating_pools(context) def get_floating_ip_by_address(self, context, address): - return rpc.call(context, - FLAGS.network_topic, - {'method': 'get_floating_ip_by_address', - 'args': {'address': address}}) + return self.network_rpcapi.get_floating_ip_by_address(context, address) def get_floating_ips_by_project(self, context): - return rpc.call(context, - FLAGS.network_topic, - {'method': 'get_floating_ips_by_project'}) + return self.network_rpcapi.get_floating_ips_by_project(context) def get_floating_ips_by_fixed_address(self, context, fixed_address): - return rpc.call(context, - FLAGS.network_topic, - {'method': 'get_floating_ips_by_fixed_address', - 'args': {'fixed_address': fixed_address}}) + return self.network_rpcapi.get_floating_ips_by_fixed_address(context, + fixed_address) def get_instance_id_by_floating_address(self, context, address): # NOTE(tr3buchet): i hate this - return rpc.call(context, - FLAGS.network_topic, - {'method': 'get_instance_id_by_floating_address', - 'args': {'address': address}}) + return self.network_rpcapi.get_instance_id_by_floating_address(context, + address) def get_vifs_by_instance(self, context, instance): - # NOTE(vish): When the db calls are converted to store network - # data by instance_uuid, this should pass uuid instead. - return rpc.call(context, - FLAGS.network_topic, - {'method': 'get_vifs_by_instance', - 'args': {'instance_id': instance['id']}}) + return self.network_rpcapi.get_vifs_by_instance(context, + instance['id']) def get_vif_by_mac_address(self, context, mac_address): - return rpc.call(context, - FLAGS.network_topic, - {'method': 'get_vif_by_mac_address', - 'args': {'mac_address': mac_address}}) + return self.network_rpcapi.get_vif_by_mac_address(context, mac_address) def allocate_floating_ip(self, context, pool=None): """Adds a floating ip to a project from a pool. (allocates)""" @@ -180,21 +143,14 @@ class API(base.Base): # when we allocate, so just send it to any one. This # will probably need to move into a network supervisor # at some point. - return rpc.call(context, - FLAGS.network_topic, - {'method': 'allocate_floating_ip', - 'args': {'project_id': context.project_id, - 'pool': pool, - 'auto_assigned': False}}) + return self.network_rpcapi.allocate_floating_ip(context, + context.project_id, pool, False) def release_floating_ip(self, context, address, affect_auto_assigned=False): """Removes floating ip with address from a project. (deallocates)""" - rpc.call(context, - FLAGS.network_topic, - {'method': 'deallocate_floating_ip', - 'args': {'address': address, - 'affect_auto_assigned': affect_auto_assigned}}) + return self.network_rpcapi.deallocate_floating_ip(context, address, + affect_auto_assigned) @refresh_cache def associate_floating_ip(self, context, instance, @@ -204,12 +160,8 @@ class API(base.Base): ensures floating ip is allocated to the project in context """ - orig_instance_uuid = rpc.call(context, - FLAGS.network_topic, - {'method': 'associate_floating_ip', - 'args': {'floating_address': floating_address, - 'fixed_address': fixed_address, - 'affect_auto_assigned': affect_auto_assigned}}) + orig_instance_uuid = self.network_rpcapi.associate_floating_ip(context, + floating_address, fixed_address, affect_auto_assigned) if orig_instance_uuid: msg_dict = dict(address=floating_address, @@ -226,48 +178,43 @@ class API(base.Base): def disassociate_floating_ip(self, context, instance, address, affect_auto_assigned=False): """Disassociates a floating ip from fixed ip it is associated with.""" - rpc.call(context, - FLAGS.network_topic, - {'method': 'disassociate_floating_ip', - 'args': {'address': address}}) + self.network_rpcapi.disassociate_floating_ip(context, address, + affect_auto_assigned) @refresh_cache - def allocate_for_instance(self, context, instance, **kwargs): + def allocate_for_instance(self, context, instance, vpn, + requested_networks): """Allocates all network structures for an instance. :returns: network info as from get_instance_nw_info() below """ - args = kwargs + args = {} + args['vpn'] = vpn + args['requested_networks'] = requested_networks args['instance_id'] = instance['id'] args['instance_uuid'] = instance['uuid'] args['project_id'] = instance['project_id'] args['host'] = instance['host'] args['rxtx_factor'] = instance['instance_type']['rxtx_factor'] - - nw_info = rpc.call(context, FLAGS.network_topic, - {'method': 'allocate_for_instance', - 'args': args}) + nw_info = self.network_rpcapi.allocate_for_instance(context, **args) return network_model.NetworkInfo.hydrate(nw_info) - def deallocate_for_instance(self, context, instance, **kwargs): + def deallocate_for_instance(self, context, instance): """Deallocates all network structures related to instance.""" - args = kwargs + + args = {} args['instance_id'] = instance['id'] args['project_id'] = instance['project_id'] args['host'] = instance['host'] - rpc.call(context, FLAGS.network_topic, - {'method': 'deallocate_for_instance', - 'args': args}) + self.network_rpcapi.deallocate_for_instance(context, **args) def add_fixed_ip_to_instance(self, context, instance, network_id): """Adds a fixed ip to instance from specified network.""" args = {'instance_id': instance['id'], 'host': instance['host'], 'network_id': network_id} - rpc.call(context, FLAGS.network_topic, - {'method': 'add_fixed_ip_to_instance', - 'args': args}) + self.network_rpcapi.add_fixed_ip_to_instance(context, **args) def remove_fixed_ip_from_instance(self, context, instance, address): """Removes a fixed ip from instance from specified network.""" @@ -275,16 +222,12 @@ class API(base.Base): args = {'instance_id': instance['id'], 'host': instance['host'], 'address': address} - rpc.call(context, FLAGS.network_topic, - {'method': 'remove_fixed_ip_from_instance', - 'args': args}) + self.network_rpcapi.remove_fixed_ip_from_instance(context, **args) def add_network_to_project(self, context, project_id, network_uuid=None): """Force adds another network to a project.""" - rpc.call(context, FLAGS.network_topic, - {'method': 'add_network_to_project', - 'args': {'project_id': project_id, - 'network_uuid': network_uuid}}) + self.network_rpcapi.add_network_to_project(context, project_id, + network_uuid) @refresh_cache def get_instance_nw_info(self, context, instance): @@ -298,9 +241,7 @@ class API(base.Base): 'rxtx_factor': instance['instance_type']['rxtx_factor'], 'host': instance['host'], 'project_id': instance['project_id']} - nw_info = rpc.call(context, FLAGS.network_topic, - {'method': 'get_instance_nw_info', - 'args': args}) + nw_info = self.network_rpcapi.get_instance_nw_info(context, **args) return network_model.NetworkInfo.hydrate(nw_info) @@ -308,27 +249,21 @@ class API(base.Base): """validate the networks passed at the time of creating the server """ - args = {'networks': requested_networks} - return rpc.call(context, FLAGS.network_topic, - {'method': 'validate_networks', - 'args': args}) + return self.network_rpcapi.validate_networks(context, + requested_networks) def get_instance_uuids_by_ip_filter(self, context, filters): """Returns a list of dicts in the form of {'instance_uuid': uuid, 'ip': ip} that matched the ip_filter """ - args = {'filters': filters} - return rpc.call(context, FLAGS.network_topic, - {'method': 'get_instance_uuids_by_ip_filter', - 'args': args}) + return self.network_rpcapi.get_instance_uuids_by_ip_filter(context, + filters) def get_dns_domains(self, context): """Returns a list of available dns domains. These can be used to create DNS entries for floating ips. """ - return rpc.call(context, - FLAGS.network_topic, - {'method': 'get_dns_domains'}) + return self.network_rpcapi.get_dns_domains(context) def add_dns_entry(self, context, address, name, dns_type, domain): """Create specified DNS entry for address""" @@ -336,60 +271,43 @@ class API(base.Base): 'name': name, 'dns_type': dns_type, 'domain': domain} - return rpc.call(context, FLAGS.network_topic, - {'method': 'add_dns_entry', - 'args': args}) + return self.network_rpcapi.add_dns_entry(context, **args) def modify_dns_entry(self, context, name, address, domain): """Create specified DNS entry for address""" args = {'address': address, 'name': name, 'domain': domain} - return rpc.call(context, FLAGS.network_topic, - {'method': 'modify_dns_entry', - 'args': args}) + return self.network_rpcapi.modify_dns_entry(context, **args) def delete_dns_entry(self, context, name, domain): """Delete the specified dns entry.""" args = {'name': name, 'domain': domain} - return rpc.call(context, FLAGS.network_topic, - {'method': 'delete_dns_entry', - 'args': args}) + return self.network_rpcapi.delete_dns_entry(context, **args) def delete_dns_domain(self, context, domain): """Delete the specified dns domain.""" - args = {'domain': domain} - return rpc.call(context, FLAGS.network_topic, - {'method': 'delete_dns_domain', - 'args': args}) + return self.network_rpcapi.delete_dns_domain(context, domain=domain) def get_dns_entries_by_address(self, context, address, domain): """Get entries for address and domain""" args = {'address': address, 'domain': domain} - return rpc.call(context, FLAGS.network_topic, - {'method': 'get_dns_entries_by_address', - 'args': args}) + return self.network_rpcapi.get_dns_entries_by_address(context, **args) def get_dns_entries_by_name(self, context, name, domain): """Get entries for name and domain""" args = {'name': name, 'domain': domain} - return rpc.call(context, FLAGS.network_topic, - {'method': 'get_dns_entries_by_name', - 'args': args}) + return self.network_rpcapi.get_dns_entries_by_name(context, **args) def create_private_dns_domain(self, context, domain, availability_zone): """Create a private DNS domain with nova availability zone.""" args = {'domain': domain, 'av_zone': availability_zone} - return rpc.call(context, FLAGS.network_topic, - {'method': 'create_private_dns_domain', - 'args': args}) + return self.network_rpcapi.create_private_dns_domain(context, **args) def create_public_dns_domain(self, context, domain, project=None): - """Create a private DNS domain with optional nova project.""" + """Create a public DNS domain with optional nova project.""" args = {'domain': domain, 'project': project} - return rpc.call(context, FLAGS.network_topic, - {'method': 'create_public_dns_domain', - 'args': args}) + return self.network_rpcapi.create_public_dns_domain(context, **args) def setup_networks_on_host(self, context, instance, host=None, teardown=False): @@ -402,8 +320,4 @@ class API(base.Base): args = {'instance_id': instance['id'], 'host': host, 'teardown': teardown} - - # NOTE(tr3buchet): the call is just to wait for completion - rpc.call(context, FLAGS.network_topic, - {'method': 'setup_networks_on_host', - 'args': args}) + self.network_rpcapi.setup_networks_on_host(context, **args) diff --git a/nova/network/manager.py b/nova/network/manager.py index 4f08c4e41..dae710152 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -61,6 +61,7 @@ from nova import ipv6 from nova import manager from nova.network import api as network_api from nova.network import model as network_model +from nova.network import rpcapi as network_rpcapi from nova.openstack.common import cfg from nova.openstack.common import excutils from nova.openstack.common import importutils @@ -191,22 +192,12 @@ class RPCAllocateFixedIP(object): host = network['host'] # NOTE(vish): if there is no network host, set one if host is None: - host = rpc.call(context, FLAGS.network_topic, - {'method': 'set_network_host', - 'args': {'network_ref': - jsonutils.to_primitive(network)}}) + host = self.network_rpcapi.set_network_host(context, network) if host != self.host: # need to call allocate_fixed_ip to correct network host - topic = rpc.queue_get_for(context, FLAGS.network_topic, host) - args = {} - args['instance_id'] = instance_id - args['network_id'] = network['id'] - args['address'] = address - args['vpn'] = vpn - - green_pool.spawn_n(rpc.call, context, topic, - {'method': '_rpc_allocate_fixed_ip', - 'args': args}) + green_pool.spawn_n(self.network_rpcapi._rpc_allocate_fixed_ip, + context, instance_id, network['id'], address, vpn, + host) else: # i am the correct host, run here self.allocate_fixed_ip(context, instance_id, network, @@ -235,12 +226,7 @@ class RPCAllocateFixedIP(object): host = network['host'] if host != self.host: # need to call deallocate_fixed_ip on correct network host - topic = rpc.queue_get_for(context, FLAGS.network_topic, host) - args = {'address': address, - 'host': host} - rpc.call(context, topic, - {'method': 'deallocate_fixed_ip', - 'args': args}) + self.network_rpcapi.deallocate_fixed_ip(context, address, host) else: # i am the correct host, run here super(RPCAllocateFixedIP, self).deallocate_fixed_ip(context, @@ -534,12 +520,8 @@ class FloatingIP(object): fixed_address, interface) else: # send to correct host - rpc.call(context, - rpc.queue_get_for(context, FLAGS.network_topic, host), - {'method': '_associate_floating_ip', - 'args': {'floating_address': floating_address, - 'fixed_address': fixed_address, - 'interface': interface}}) + self.network_rpcapi._associate_floating_ip(context, + floating_address, fixed_address, interface, host) return orig_instance_uuid @@ -607,11 +589,8 @@ class FloatingIP(object): self._disassociate_floating_ip(context, address, interface) else: # send to correct host - rpc.call(context, - rpc.queue_get_for(context, FLAGS.network_topic, host), - {'method': '_disassociate_floating_ip', - 'args': {'address': address, - 'interface': interface}}) + self.network_rpcapi._disassociate_floating_ip(context, address, + interface, host) def _disassociate_floating_ip(self, context, address, interface): """Performs db and driver calls to disassociate floating ip""" @@ -770,6 +749,8 @@ class NetworkManager(manager.SchedulerDependentManager): The one at a time part is to flatten the layout to help scale """ + RPC_API_VERSION = '1.0' + # If True, this manager requires VIF to create a bridge. SHOULD_CREATE_BRIDGE = False @@ -793,6 +774,7 @@ class NetworkManager(manager.SchedulerDependentManager): temp = importutils.import_object(FLAGS.floating_ip_dns_manager) self.floating_dns_manager = temp self.network_api = network_api.API() + self.network_rpcapi = network_rpcapi.NetworkAPI() self.security_group_api = compute_api.SecurityGroupAPI() self.compute_api = compute_api.API( security_group_api=self.security_group_api) @@ -1657,12 +1639,9 @@ class NetworkManager(manager.SchedulerDependentManager): call_func(context, network) else: # i'm not the right host, run call on correct host - topic = rpc.queue_get_for(context, FLAGS.network_topic, host) - args = {'network_id': network['id'], 'teardown': teardown} - # NOTE(tr3buchet): the call is just to wait for completion - green_pool.spawn_n(rpc.call, context, topic, - {'method': 'rpc_setup_network_on_host', - 'args': args}) + green_pool.spawn_n( + self.network_rpcapi.rpc_setup_network_on_host, context, + network['id'], teardown, host) # wait for all of the setups (if any) to finish green_pool.waitall() diff --git a/nova/network/rpcapi.py b/nova/network/rpcapi.py new file mode 100644 index 000000000..43c17acb5 --- /dev/null +++ b/nova/network/rpcapi.py @@ -0,0 +1,274 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012, Red Hat, Inc. +# +# 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. + +""" +Client side of the network RPC API. +""" + +from nova import flags +from nova.openstack.common import jsonutils +from nova.openstack.common import rpc +from nova.openstack.common.rpc import proxy as rpc_proxy + + +FLAGS = flags.FLAGS + + +class NetworkAPI(rpc_proxy.RpcProxy): + '''Client side of the network rpc API. + + API version history: + + 1.0 - Initial version. + ''' + + # + # NOTE(russellb): This is the default minimum version that the server + # (manager) side must implement unless otherwise specified using a version + # argument to self.call()/cast()/etc. here. It should be left as X.0 where + # X is the current major API version (1.0, 2.0, ...). For more information + # about rpc API versioning, see the docs in + # openstack/common/rpc/dispatcher.py. + # + BASE_RPC_API_VERSION = '1.0' + + def __init__(self, topic=None): + topic = topic if topic else FLAGS.network_topic + super(NetworkAPI, self).__init__( + topic=topic, + default_version=self.BASE_RPC_API_VERSION) + + def get_all_networks(self, ctxt): + return self.call(ctxt, self.make_msg('get_all_networks')) + + def get_network(self, ctxt, network_uuid): + return self.call(ctxt, self.make_msg('get_network', + network_uuid=network_uuid)) + + # TODO(russellb): Convert this to named arguments. It's a pretty large + # list, so unwinding it all is probably best done in its own patch so it's + # easier to review. + def create_networks(self, ctxt, **kwargs): + return self.call(ctxt, self.make_msg('create_networks', **kwargs)) + + def delete_network(self, ctxt, uuid, fixed_range): + return self.call(ctxt, self.make_msg('delete_network', + uuid=uuid, fixed_range=fixed_range)) + + def disassociate_network(self, ctxt, network_uuid): + return self.call(ctxt, self.make_msg('disassociate_network', + network_uuid=network_uuid)) + + def get_fixed_ip(self, ctxt, id): + return self.call(ctxt, self.make_msg('get_fixed_ip', id=id)) + + def get_fixed_ip_by_address(self, ctxt, address): + return self.call(ctxt, self.make_msg('get_fixed_ip_by_address', + address=address)) + + def get_floating_ip(self, ctxt, id): + return self.call(ctxt, self.make_msg('get_floating_ip', id=id)) + + def get_floating_pools(self, ctxt): + return self.call(ctxt, self.make_msg('get_floating_pools')) + + def get_floating_ip_by_address(self, ctxt, address): + return self.call(ctxt, self.make_msg('get_floating_ip_by_address', + address=address)) + + def get_floating_ips_by_project(self, ctxt): + return self.call(ctxt, self.make_msg('get_floating_ips_by_project')) + + def get_floating_ips_by_fixed_address(self, ctxt, fixed_address): + return self.call(ctxt, self.make_msg( + 'get_floating_ips_by_fixed_address', + fixed_address=fixed_address)) + + def get_instance_id_by_floating_address(self, ctxt, address): + return self.call(ctxt, self.make_msg( + 'get_instance_id_by_floating_address', + address=address)) + + def get_vifs_by_instance(self, ctxt, instance_id): + # NOTE(vish): When the db calls are converted to store network + # data by instance_uuid, this should pass uuid instead. + return self.call(ctxt, self.make_msg('get_vifs_by_instance', + instance_id=instance_id)) + + def get_vif_by_mac_address(self, ctxt, mac_address): + return self.call(ctxt, self.make_msg('get_vif_by_mac_address', + mac_address=mac_address)) + + def allocate_floating_ip(self, ctxt, project_id, pool, auto_assigned): + return self.call(ctxt, self.make_msg('allocate_floating_ip', + project_id=project_id, pool=pool, auto_assigned=auto_assigned)) + + def deallocate_floating_ip(self, ctxt, address, affect_auto_assigned): + return self.call(ctxt, self.make_msg('deallocate_floating_ip', + address=address, affect_auto_assigned=affect_auto_assigned)) + + def associate_floating_ip(self, ctxt, floating_address, fixed_address, + affect_auto_assigned): + return self.call(ctxt, self.make_msg('associate_floating_ip', + floating_address=floating_address, fixed_address=fixed_address, + affect_auto_assigned=affect_auto_assigned)) + + def disassociate_floating_ip(self, ctxt, address, affect_auto_assigned): + return self.call(ctxt, self.make_msg('disassociate_floating_ip', + address=address, affect_auto_assigned=affect_auto_assigned)) + + def allocate_for_instance(self, ctxt, instance_id, instance_uuid, + project_id, host, rxtx_factor, vpn, + requested_networks): + return self.call(ctxt, self.make_msg('allocate_for_instance', + instance_id=instance_id, instance_uuid=instance_uuid, + project_id=project_id, host=host, rxtx_factor=rxtx_factor, + vpn=vpn, requested_networks=requested_networks)) + + def deallocate_for_instance(self, ctxt, instance_id, project_id, host): + return self.call(ctxt, self.make_msg('deallocate_for_instance', + instance_id=instance_id, project_id=project_id, host=host)) + + def add_fixed_ip_to_instance(self, ctxt, instance_id, host, network_id): + return self.call(ctxt, self.make_msg('add_fixed_ip_to_instance', + instance_id=instance_id, host=host, network_id=network_id)) + + def remove_fixed_ip_from_instance(self, ctxt, instance_id, host, address): + return self.call(ctxt, self.make_msg('remove_fixed_ip_from_instance', + instance_id=instance_id, host=host, address=address)) + + def add_network_to_project(self, ctxt, project_id, network_uuid): + return self.call(ctxt, self.make_msg('add_network_to_project', + project_id=project_id, network_uuid=network_uuid)) + + def get_instance_nw_info(self, ctxt, instance_id, instance_uuid, + rxtx_factor, host, project_id): + return self.call(ctxt, self.make_msg('get_instance_nw_info', + instance_id=instance_id, instance_uuid=instance_uuid, + rxtx_factor=rxtx_factor, host=host, project_id=project_id)) + + def validate_networks(self, ctxt, networks): + return self.call(ctxt, self.make_msg('validate_networks', + networks=networks)) + + def get_instance_uuids_by_ip_filter(self, ctxt, filters): + return self.call(ctxt, self.make_msg('get_instance_uuids_by_ip_filter', + filters=filters)) + + def get_dns_domains(self, ctxt): + return self.call(ctxt, self.make_msg('get_dns_domains')) + + def add_dns_entry(self, ctxt, address, name, dns_type, domain): + return self.call(ctxt, self.make_msg('add_dns_entry', address=address, + name=name, dns_type=dns_type, domain=domain)) + + def modify_dns_entry(self, ctxt, address, name, domain): + return self.call(ctxt, self.make_msg('modify_dns_entry', + address=address, name=name, domain=domain)) + + def delete_dns_entry(self, ctxt, name, domain): + return self.call(ctxt, self.make_msg('delete_dns_entry', + name=name, domain=domain)) + + def delete_dns_domain(self, ctxt, domain): + return self.call(ctxt, self.make_msg('delete_dns_domain', + domain=domain)) + + def get_dns_entries_by_address(self, ctxt, address, domain): + return self.call(ctxt, self.make_msg('get_dns_entries_by_address', + address=address, domain=domain)) + + def get_dns_entries_by_name(self, ctxt, name, domain): + return self.call(ctxt, self.make_msg('get_dns_entries_by_name', + name=name, domain=domain)) + + def create_private_dns_domain(self, ctxt, domain, av_zone): + return self.call(ctxt, self.make_msg('create_private_dns_domain', + domain=domain, av_zone=av_zone)) + + def create_public_dns_domain(self, ctxt, domain, project): + return self.call(ctxt, self.make_msg('create_public_dns_domain', + domain=domain, project=project)) + + def setup_networks_on_host(self, ctxt, instance_id, host, teardown): + # NOTE(tr3buchet): the call is just to wait for completion + return self.call(ctxt, self.make_msg('setup_networks_on_host', + instance_id=instance_id, host=host, teardown=teardown)) + + def lease_fixed_ip(self, ctxt, host, address): + self.cast(ctxt, self.make_msg('lease_fixed_ip', address=address), + topic=rpc.queue_get_for(ctxt, self.topic, host)) + + def release_fixed_ip(self, ctxt, host, address): + self.cast(ctxt, self.make_msg('release_fixed_ip', address=address), + topic=rpc.queue_get_for(ctxt, self.topic, host)) + + def set_network_host(self, ctxt, network_ref): + network_ref_p = jsonutils.to_primitive(network_ref) + return self.call(ctxt, self.make_msg('set_network_host', + network_ref=network_ref_p)) + + def rpc_setup_network_on_host(self, ctxt, network_id, teardown, host): + # NOTE(tr3buchet): the call is just to wait for completion + return self.call(ctxt, self.make_msg('rpc_setup_network_on_host', + network_id=network_id, teardown=teardown), + topic=rpc.queue_get_for(ctxt, self.topic, host)) + + # NOTE(russellb): Ideally this would not have a prefix of '_' since it is + # a part of the rpc API. However, this is how it was being called when the + # 1.0 API was being documented using this client proxy class. It should be + # changed if there was ever a 2.0. + def _rpc_allocate_fixed_ip(self, ctxt, instance_id, network_id, address, + vpn, host): + return self.call(ctxt, self.make_msg('_rpc_allocate_fixed_ip', + instance_id=instance_id, network_id=network_id, + address=address, vpn=vpn), + topic=rpc.queue_get_for(ctxt, self.topic, host)) + + def deallocate_fixed_ip(self, ctxt, address, host): + return self.call(ctxt, self.make_msg('deallocate_fixed_ip', + address=address, host=host), + topic=rpc.queue_get_for(ctxt, self.topic, host)) + + # NOTE(russellb): Ideally this would not have a prefix of '_' since it is + # a part of the rpc API. However, this is how it was being called when the + # 1.0 API was being documented using this client proxy class. It should be + # changed if there was ever a 2.0. + def _associate_floating_ip(self, ctxt, floating_address, fixed_address, + interface, host): + return self.call(ctxt, self.make_msg('_associate_floating_ip', + floating_address=floating_address, fixed_address=fixed_address, + interface=interface), + topic=rpc.queue_get_for(ctxt, self.topic, host)) + + # NOTE(russellb): Ideally this would not have a prefix of '_' since it is + # a part of the rpc API. However, this is how it was being called when the + # 1.0 API was being documented using this client proxy class. It should be + # changed if there was ever a 2.0. + def _disassociate_floating_ip(self, ctxt, address, interface, host): + return self.call(ctxt, self.make_msg('_disassociate_floating_ip', + address=address, interface=interface), + topic=rpc.queue_get_for(ctxt, self.topic, host)) + + def lease_fixed_ip(self, ctxt, address, host): + return self.cast(ctxt, self.make_msg('lease_fixed_ip', + address=address), + topic=rpc.queue_get_for(ctxt, self.topic, host)) + + def release_fixed_ip(self, ctxt, address, host): + return self.cast(ctxt, self.make_msg('release_fixed_ip', + address=address), + topic=rpc.queue_get_for(ctxt, self.topic, host)) diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index cad27b544..c49dccea9 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -2129,7 +2129,8 @@ class ComputeTestCase(BaseTestCase): rpc.call(c, 'network', {'method': 'setup_networks_on_host', 'args': {'instance_id': inst_id, 'host': self.compute.host, - 'teardown': False}}) + 'teardown': False}, + 'version': '1.0'}, None) rpcinst = jsonutils.to_primitive( db.instance_get_by_uuid(self.context, instance['uuid'])) rpc.call(c, topic, @@ -2224,7 +2225,8 @@ class ComputeTestCase(BaseTestCase): rpc.call(c, 'network', {'method': 'setup_networks_on_host', 'args': {'instance_id': inst_id, 'host': self.compute.host, - 'teardown': True}}) + 'teardown': True}, + 'version': '1.0'}, None) # start test self.mox.ReplayAll() diff --git a/nova/tests/network/test_api.py b/nova/tests/network/test_api.py index a29756caa..9bbd7ba92 100644 --- a/nova/tests/network/test_api.py +++ b/nova/tests/network/test_api.py @@ -35,7 +35,7 @@ class ApiTestCase(test.TestCase): new_instance = {'uuid': 'new-uuid'} - def fake_rpc_call(context, topic, msg): + def fake_rpc_call(context, topic, msg, timeout=None): return orig_instance_uuid self.stubs.Set(rpc, 'call', fake_rpc_call) diff --git a/nova/tests/network/test_rpcapi.py b/nova/tests/network/test_rpcapi.py new file mode 100644 index 000000000..a087ba97f --- /dev/null +++ b/nova/tests/network/test_rpcapi.py @@ -0,0 +1,260 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012, Red Hat, Inc. +# +# 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. + +""" +Unit Tests for nova.network.rpcapi +""" + +from nova import context +from nova import flags +from nova.network import rpcapi as network_rpcapi +from nova.openstack.common import rpc +from nova import test + + +FLAGS = flags.FLAGS + + +class NetworkRpcAPITestCase(test.TestCase): + def _test_network_api(self, method, rpc_method, **kwargs): + ctxt = context.RequestContext('fake_user', 'fake_project') + rpcapi = network_rpcapi.NetworkAPI() + expected_retval = 'foo' if method == 'call' else None + expected_version = kwargs.pop('version', rpcapi.BASE_RPC_API_VERSION) + expected_topic = FLAGS.network_topic + expected_msg = rpcapi.make_msg(method, **kwargs) + targeted_methods = [ + 'lease_fixed_ip', 'release_fixed_ip', 'rpc_setup_network_on_host', + '_rpc_allocate_fixed_ip', 'deallocate_fixed_ip', + '_associate_floating_ip', '_disassociate_floating_ip', + 'lease_fixed_ip', 'release_fixed_ip' + ] + if method in targeted_methods and 'host' in kwargs: + if method != 'deallocate_fixed_ip': + del expected_msg['args']['host'] + host = kwargs['host'] + expected_topic = rpc.queue_get_for(ctxt, FLAGS.network_topic, host) + expected_msg['version'] = expected_version + + self.fake_args = None + self.fake_kwargs = None + + def _fake_rpc_method(*args, **kwargs): + self.fake_args = args + self.fake_kwargs = kwargs + if expected_retval: + return expected_retval + + self.stubs.Set(rpc, rpc_method, _fake_rpc_method) + + retval = getattr(rpcapi, method)(ctxt, **kwargs) + + self.assertEqual(retval, expected_retval) + expected_args = [ctxt, expected_topic, expected_msg] + for arg, expected_arg in zip(self.fake_args, expected_args): + self.assertEqual(arg, expected_arg) + + def test_get_all_networks(self): + self._test_network_api('get_all_networks', rpc_method='call') + + def test_get_network(self): + self._test_network_api('get_network', rpc_method='call', + network_uuid='fake_uuid') + + def test_create_networks(self): + self._test_network_api('create_networks', rpc_method='call', + arg1='arg', arg2='arg') + + def test_delete_network(self): + self._test_network_api('delete_network', rpc_method='call', + uuid='fake_uuid', fixed_range='range') + + def test_disassociate_network(self): + self._test_network_api('disassociate_network', rpc_method='call', + network_uuid='fake_uuid') + + def test_get_fixed_ip(self): + self._test_network_api('get_fixed_ip', rpc_method='call', id='id') + + def test_get_fixed_ip_by_address(self): + self._test_network_api('get_fixed_ip_by_address', rpc_method='call', + address='a.b.c.d') + + def test_get_floating_ip(self): + self._test_network_api('get_floating_ip', rpc_method='call', id='id') + + def test_get_floating_pools(self): + self._test_network_api('get_floating_pools', rpc_method='call') + + def test_get_floating_ip_by_address(self): + self._test_network_api('get_floating_ip_by_address', rpc_method='call', + address='a.b.c.d') + + def test_get_floating_ips_by_project(self): + self._test_network_api('get_floating_ips_by_project', + rpc_method='call') + + def test_get_floating_ips_by_fixed_address(self): + self._test_network_api('get_floating_ips_by_fixed_address', + rpc_method='call', fixed_address='w.x.y.z') + + def test_get_instance_id_by_floating_address(self): + self._test_network_api('get_instance_id_by_floating_address', + rpc_method='call', address='w.x.y.z') + + def test_get_vifs_by_instance(self): + self._test_network_api('get_vifs_by_instance', + rpc_method='call', instance_id='fake_id') + + def test_get_vif_by_mac_address(self): + self._test_network_api('get_vif_by_mac_address', + rpc_method='call', mac_address='fake_mac_addr') + + def test_allocate_floating_ip(self): + self._test_network_api('allocate_floating_ip', rpc_method='call', + project_id='fake_id', pool='fake_pool', auto_assigned=False) + + def test_deallocate_floating_ip(self): + self._test_network_api('deallocate_floating_ip', rpc_method='call', + address='addr', affect_auto_assigned=True) + + def test_associate_floating_ip(self): + self._test_network_api('associate_floating_ip', rpc_method='call', + floating_address='blah', fixed_address='foo', + affect_auto_assigned=True) + + def test_disassociate_floating_ip(self): + self._test_network_api('disassociate_floating_ip', rpc_method='call', + address='addr', affect_auto_assigned=True) + + def test_allocate_for_instance(self): + self._test_network_api('allocate_for_instance', rpc_method='call', + instance_id='fake_id', instance_uuid='fake_uuid', + project_id='fake_id', host='fake_host', + rxtx_factor='fake_factor', vpn=False, requested_networks={}) + + def test_deallocate_for_instance(self): + self._test_network_api('deallocate_for_instance', rpc_method='call', + instance_id='fake_id', project_id='fake_id', host='fake_host') + + def test_add_fixed_ip_to_instance(self): + self._test_network_api('add_fixed_ip_to_instance', rpc_method='call', + instance_id='fake_id', host='fake_host', network_id='fake_id') + + def test_remove_fixed_ip_from_instance(self): + self._test_network_api('remove_fixed_ip_from_instance', + rpc_method='call', instance_id='fake_id', host='fake_host', + address='fake_address') + + def test_add_network_to_project(self): + self._test_network_api('add_network_to_project', rpc_method='call', + project_id='fake_id', network_uuid='fake_uuid') + + def test_get_instance_nw_info(self): + self._test_network_api('get_instance_nw_info', rpc_method='call', + instance_id='fake_id', instance_uuid='fake_uuid', + rxtx_factor='fake_factor', host='fake_host', + project_id='fake_id') + + def test_validate_networks(self): + self._test_network_api('validate_networks', rpc_method='call', + networks={}) + + def test_get_instance_uuids_by_ip_filter(self): + self._test_network_api('get_instance_uuids_by_ip_filter', + rpc_method='call', filters={}) + + def test_get_dns_domains(self): + self._test_network_api('get_dns_domains', rpc_method='call') + + def test_add_dns_entry(self): + self._test_network_api('add_dns_entry', rpc_method='call', + address='addr', name='name', dns_type='foo', domain='domain') + + def test_modify_dns_entry(self): + self._test_network_api('modify_dns_entry', rpc_method='call', + address='addr', name='name', domain='domain') + + def test_delete_dns_entry(self): + self._test_network_api('delete_dns_entry', rpc_method='call', + name='name', domain='domain') + + def test_delete_dns_domain(self): + self._test_network_api('delete_dns_domain', rpc_method='call', + domain='fake_domain') + + def test_get_dns_entries_by_address(self): + self._test_network_api('get_dns_entries_by_address', rpc_method='call', + address='fake_address', domain='fake_domain') + + def test_get_dns_entries_by_name(self): + self._test_network_api('get_dns_entries_by_name', rpc_method='call', + name='fake_name', domain='fake_domain') + + def test_create_private_dns_domain(self): + self._test_network_api('create_private_dns_domain', rpc_method='call', + domain='fake_domain', av_zone='fake_zone') + + def test_create_public_dns_domain(self): + self._test_network_api('create_public_dns_domain', rpc_method='call', + domain='fake_domain', project='fake_project') + + def test_setup_networks_on_host(self): + self._test_network_api('setup_networks_on_host', rpc_method='call', + instance_id='fake_id', host='fake_host', teardown=False) + + def test_lease_fixed_ip(self): + self._test_network_api('lease_fixed_ip', rpc_method='cast', + host='fake_host', address='fake_addr') + + def test_release_fixed_ip(self): + self._test_network_api('release_fixed_ip', rpc_method='cast', + host='fake_host', address='fake_addr') + + def test_set_network_host(self): + self._test_network_api('set_network_host', rpc_method='call', + network_ref={}) + + def test_rpc_setup_network_on_host(self): + self._test_network_api('rpc_setup_network_on_host', rpc_method='call', + network_id='fake_id', teardown=False, host='fake_host') + + def test_rpc_allocate_fixed_ip(self): + self._test_network_api('_rpc_allocate_fixed_ip', rpc_method='call', + instance_id='fake_id', network_id='fake_id', address='addr', + vpn=True, host='fake_host') + + def test_deallocate_fixed_ip(self): + self._test_network_api('deallocate_fixed_ip', rpc_method='call', + address='fake_addr', host='fake_host') + + def test__associate_floating_ip(self): + self._test_network_api('_associate_floating_ip', rpc_method='call', + floating_address='fake_addr', fixed_address='fixed_address', + interface='fake_interface', host='fake_host') + + def test__disassociate_floating_ip(self): + self._test_network_api('_disassociate_floating_ip', rpc_method='call', + address='fake_addr', interface='fake_interface', + host='fake_host') + + def test_lease_fixed_ip(self): + self._test_network_api('lease_fixed_ip', rpc_method='cast', + address='fake_addr', host='fake_host') + + def test_release_fixed_ip(self): + self._test_network_api('release_fixed_ip', rpc_method='cast', + address='fake_addr', host='fake_host') -- cgit