From 8a27da34e7189499b48b8971755393d75447cc07 Mon Sep 17 00:00:00 2001 From: melanie witt Date: Thu, 18 Sep 2014 05:08:59 +0000 Subject: [PATCH] object-ify flavors manager side of the RPC This change completes the conversion of flavors to use objects instead of direct database access. The rpcapi sends objects for the new version and primitivized data is sent for older versions. Related to blueprint kilo-objects Change-Id: Ib876f276cff99a21c5bca1c9f062a6c221a783e8 --- nova/cells/manager.py | 2 +- nova/cells/rpcapi.py | 16 +++++++++---- nova/compute/manager.py | 9 ++++++- nova/compute/rpcapi.py | 15 ++++++++---- nova/conductor/manager.py | 19 ++++++++++----- nova/conductor/rpcapi.py | 26 ++++++++++++--------- nova/scheduler/manager.py | 9 ++++++- nova/scheduler/rpcapi.py | 16 +++++++++---- nova/tests/unit/cells/test_cells_rpcapi.py | 2 +- nova/tests/unit/compute/test_rpcapi.py | 2 +- nova/tests/unit/conductor/test_conductor.py | 13 ++++++----- nova/tests/unit/scheduler/test_rpcapi.py | 3 ++- 12 files changed, 88 insertions(+), 44 deletions(-) diff --git a/nova/cells/manager.py b/nova/cells/manager.py index 3faea8024380..d8f4d3bc9770 100644 --- a/nova/cells/manager.py +++ b/nova/cells/manager.py @@ -73,7 +73,7 @@ class CellsManager(manager.Manager): Scheduling requests get passed to the scheduler class. """ - target = oslo_messaging.Target(version='1.29') + target = oslo_messaging.Target(version='1.30') def __init__(self, *args, **kwargs): LOG.warning(_LW('The cells feature of Nova is considered experimental ' diff --git a/nova/cells/rpcapi.py b/nova/cells/rpcapi.py index 9a398504d7da..162468b8da16 100644 --- a/nova/cells/rpcapi.py +++ b/nova/cells/rpcapi.py @@ -101,6 +101,8 @@ class CellsAPI(object): ... Juno supports message version 1.29. So, any changes to existing methods in 1.x after that point should be done such that they can handle the version_cap being set to 1.29. + + * 1.30 - Make build_instances() use flavor object ''' VERSION_ALIASES = { @@ -150,11 +152,15 @@ class CellsAPI(object): build_inst_kwargs['instances'] = instances_p build_inst_kwargs['image'] = jsonutils.to_primitive( build_inst_kwargs['image']) - if 'filter_properties' in build_inst_kwargs: - flavor = build_inst_kwargs['filter_properties']['instance_type'] - flavor_p = objects_base.obj_to_primitive(flavor) - build_inst_kwargs['filter_properties']['instance_type'] = flavor_p - cctxt = self.client.prepare(version='1.8') + version = '1.30' + if not self.client.can_send_version(version): + version = '1.8' + if 'filter_properties' in build_inst_kwargs: + filter_properties = build_inst_kwargs['filter_properties'] + flavor = filter_properties['instance_type'] + flavor_p = objects_base.obj_to_primitive(flavor) + filter_properties['instance_type'] = flavor_p + cctxt = self.client.prepare(version=version) cctxt.cast(ctxt, 'build_instances', build_inst_kwargs=build_inst_kwargs) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index f240709e654c..f2f782c4e1ec 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -587,7 +587,7 @@ class ComputeVirtAPI(virtapi.VirtAPI): class ComputeManager(manager.Manager): """Manages the running instances from creation to destruction.""" - target = messaging.Target(version='3.35') + target = messaging.Target(version='3.36') # How long to wait in seconds before re-issuing a shutdown # signal to a instance during power off. The overall @@ -1994,6 +1994,13 @@ class ComputeManager(manager.Manager): requested_networks = objects.NetworkRequestList( objects=[objects.NetworkRequest.from_tuple(t) for t in requested_networks]) + # NOTE(melwitt): Remove this in v4.0 of the RPC API + flavor = filter_properties.get('instance_type') + if flavor and not isinstance(flavor, objects.Flavor): + # Code downstream may expect extra_specs to be populated since it + # is receiving an object, so lookup the flavor to ensure this. + flavor = objects.Flavor.get_by_id(context, flavor['id']) + filter_properties = dict(filter_properties, instance_type=flavor) @utils.synchronized(instance.uuid) def _locked_do_build_and_run_instance(*args, **kwargs): diff --git a/nova/compute/rpcapi.py b/nova/compute/rpcapi.py index a2c5b62b2602..1c5d2fa5dcbf 100644 --- a/nova/compute/rpcapi.py +++ b/nova/compute/rpcapi.py @@ -274,6 +274,8 @@ class ComputeAPI(object): ... Juno supports message version 3.35. So, any changes to existing methods in 3.x after that point should be done such that they can handle the version_cap being set to 3.35. + + * 3.36 - Make build_and_run_instance() send a Flavor object ''' VERSION_ALIASES = { @@ -880,17 +882,20 @@ class ComputeAPI(object): filter_properties, admin_password=None, injected_files=None, requested_networks=None, security_groups=None, block_device_mapping=None, node=None, limits=None): - version = '3.33' + version = '3.36' + if not self.client.can_send_version(version): + version = '3.33' + if 'instance_type' in filter_properties: + flavor = filter_properties['instance_type'] + flavor_p = objects_base.obj_to_primitive(flavor) + filter_properties = dict(filter_properties, + instance_type=flavor_p) if not self.client.can_send_version(version): version = '3.23' if requested_networks is not None: requested_networks = [(network_id, address, port_id) for (network_id, address, port_id, _) in requested_networks.as_tuples()] - if 'instance_type' in filter_properties: - flavor = filter_properties['instance_type'] - flavor_p = objects_base.obj_to_primitive(flavor) - filter_properties = dict(filter_properties, instance_type=flavor_p) cctxt = self.client.prepare(server=host, version=version) cctxt.cast(ctxt, 'build_and_run_instance', instance=instance, diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py index 8fe6aea33d51..e39be2938d7e 100644 --- a/nova/conductor/manager.py +++ b/nova/conductor/manager.py @@ -461,7 +461,7 @@ class ComputeTaskManager(base.Base): may involve coordinating activities on multiple compute nodes. """ - target = messaging.Target(namespace='compute_task', version='1.9') + target = messaging.Target(namespace='compute_task', version='1.10') def __init__(self): super(ComputeTaskManager, self).__init__() @@ -491,6 +491,11 @@ class ComputeTaskManager(base.Base): instance = objects.Instance._from_db_object( context, objects.Instance(), instance, expected_attrs=attrs) + # NOTE(melwitt): Remove this in version 2.0 of the RPC API + if flavor and not isinstance(flavor, objects.Flavor): + # Code downstream may expect extra_specs to be populated since it + # is receiving an object, so lookup the flavor to ensure this. + flavor = objects.Flavor.get_by_id(context, flavor['id']) if live and not rebuild and not flavor: self._live_migrate(context, instance, scheduler_hint, block_migration, disk_over_commit) @@ -545,11 +550,6 @@ class ComputeTaskManager(base.Base): # context is not serializable filter_properties.pop('context', None) - # TODO(timello): originally, instance_type in request_spec - # on compute.api.resize does not have 'extra_specs', so we - # remove it for now to keep tests backward compatibility. - request_spec['instance_type'].pop('extra_specs', None) - (host, node) = (host_state['host'], host_state['nodename']) self.compute_rpcapi.prep_resize( context, image, instance, @@ -622,6 +622,13 @@ class ComputeTaskManager(base.Base): requested_networks = objects.NetworkRequestList( objects=[objects.NetworkRequest.from_tuple(t) for t in requested_networks]) + # TODO(melwitt): Remove this in version 2.0 of the RPC API + flavor = filter_properties.get('instance_type') + if flavor and not isinstance(flavor, objects.Flavor): + # Code downstream may expect extra_specs to be populated since it + # is receiving an object, so lookup the flavor to ensure this. + flavor = objects.Flavor.get_by_id(context, flavor['id']) + filter_properties = dict(filter_properties, instance_type=flavor) try: # check retry policy. Rather ugly use of instances[0]... diff --git a/nova/conductor/rpcapi.py b/nova/conductor/rpcapi.py index e0b01fe6ca93..60621742d0fe 100644 --- a/nova/conductor/rpcapi.py +++ b/nova/conductor/rpcapi.py @@ -390,6 +390,7 @@ class ComputeTaskAPI(object): 1.7 - Do not send block_device_mapping and legacy_bdm to build_instances 1.8 - Add rebuild_instance 1.9 - Converted requested_networks to NetworkRequestList object + 1.10 - Made migrate_server() and build_instances() send flavor objects """ @@ -404,17 +405,18 @@ class ComputeTaskAPI(object): def migrate_server(self, context, instance, scheduler_hint, live, rebuild, flavor, block_migration, disk_over_commit, reservations=None): - if self.client.can_send_version('1.6'): + version = '1.10' + if not self.client.can_send_version(version): + flavor = objects_base.obj_to_primitive(flavor) version = '1.6' - else: + if not self.client.can_send_version(version): instance = jsonutils.to_primitive( objects_base.obj_to_primitive(instance)) version = '1.4' - flavor_p = jsonutils.to_primitive(flavor) cctxt = self.client.prepare(version=version) return cctxt.call(context, 'migrate_server', instance=instance, scheduler_hint=scheduler_hint, - live=live, rebuild=rebuild, flavor=flavor_p, + live=live, rebuild=rebuild, flavor=flavor, block_migration=block_migration, disk_over_commit=disk_over_commit, reservations=reservations) @@ -423,19 +425,21 @@ class ComputeTaskAPI(object): admin_password, injected_files, requested_networks, security_groups, block_device_mapping, legacy_bdm=True): image_p = jsonutils.to_primitive(image) - if 'instance_type' in filter_properties: - flavor = filter_properties['instance_type'] - flavor_p = objects_base.obj_to_primitive(flavor) - filter_properties = dict(filter_properties, instance_type=flavor_p) + version = '1.10' + if not self.client.can_send_version(version): + version = '1.9' + if 'instance_type' in filter_properties: + flavor = filter_properties['instance_type'] + flavor_p = objects_base.obj_to_primitive(flavor) + filter_properties = dict(filter_properties, + instance_type=flavor_p) kw = {'instances': instances, 'image': image_p, 'filter_properties': filter_properties, 'admin_password': admin_password, 'injected_files': injected_files, 'requested_networks': requested_networks, 'security_groups': security_groups} - - version = '1.9' - if not self.client.can_send_version('1.9'): + if not self.client.can_send_version(version): version = '1.8' kw['requested_networks'] = kw['requested_networks'].as_tuples() if not self.client.can_send_version('1.7'): diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index aae2c81b26ca..f24abadb1772 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -60,7 +60,7 @@ QUOTAS = quota.QUOTAS class SchedulerManager(manager.Manager): """Chooses a host to run instances on.""" - target = messaging.Target(version='3.0') + target = messaging.Target(version='3.1') def __init__(self, scheduler_driver=None, *args, **kwargs): if not scheduler_driver: @@ -171,6 +171,13 @@ class SchedulerManager(manager.Manager): The result should be a list of dicts with 'host', 'nodename' and 'limits' as keys. """ + # TODO(melwitt): Remove this in version 4.0 of the RPC API + flavor = filter_properties.get('instance_type') + if flavor and not isinstance(flavor, objects.Flavor): + # Code downstream may expect extra_specs to be populated since it + # is receiving an object, so lookup the flavor to ensure this. + flavor = objects.Flavor.get_by_id(context, flavor['id']) + filter_properties = dict(filter_properties, instance_type=flavor) dests = self.driver.select_destinations(context, request_spec, filter_properties) return jsonutils.to_primitive(dests) diff --git a/nova/scheduler/rpcapi.py b/nova/scheduler/rpcapi.py index 131bbe76e544..902af8661cb8 100644 --- a/nova/scheduler/rpcapi.py +++ b/nova/scheduler/rpcapi.py @@ -84,6 +84,8 @@ class SchedulerAPI(object): existing methods in 3.x after that point should be done such that they can handle the version_cap being set to 3.0. + * 3.1 - Made select_destinations() send flavor object + ''' VERSION_ALIASES = { @@ -103,10 +105,14 @@ class SchedulerAPI(object): serializer=serializer) def select_destinations(self, ctxt, request_spec, filter_properties): - if 'instance_type' in filter_properties: - flavor = filter_properties['instance_type'] - flavor_p = objects_base.obj_to_primitive(flavor) - filter_properties = dict(filter_properties, instance_type=flavor_p) - cctxt = self.client.prepare() + version = '3.1' + if not self.client.can_send_version(version): + version = '3.0' + if 'instance_type' in filter_properties: + flavor = filter_properties['instance_type'] + flavor_p = objects_base.obj_to_primitive(flavor) + filter_properties = dict(filter_properties, + instance_type=flavor_p) + cctxt = self.client.prepare(version=version) return cctxt.call(ctxt, 'select_destinations', request_spec=request_spec, filter_properties=filter_properties) diff --git a/nova/tests/unit/cells/test_cells_rpcapi.py b/nova/tests/unit/cells/test_cells_rpcapi.py index 398b96d8aef2..2d7ee74c293c 100644 --- a/nova/tests/unit/cells/test_cells_rpcapi.py +++ b/nova/tests/unit/cells/test_cells_rpcapi.py @@ -135,7 +135,7 @@ class CellsAPITestCase(test.NoDBTestCase): 'arg2': 2, 'arg3': 3}} self._check_result(call_info, 'build_instances', - expected_args, version='1.8') + expected_args, version='1.30') def test_get_capacities(self): capacity_info = {"capacity": "info"} diff --git a/nova/tests/unit/compute/test_rpcapi.py b/nova/tests/unit/compute/test_rpcapi.py index bf8e41215e9a..cc9be4492c61 100644 --- a/nova/tests/unit/compute/test_rpcapi.py +++ b/nova/tests/unit/compute/test_rpcapi.py @@ -468,7 +468,7 @@ class ComputeRpcAPITestCase(test.TestCase): admin_password='passwd', injected_files=None, requested_networks=['network1'], security_groups=None, block_device_mapping=None, node='node', limits=[], - version='3.33') + version='3.36') @mock.patch('nova.utils.is_neutron', return_value=True) def test_build_and_run_instance_icehouse_compat(self, is_neutron): diff --git a/nova/tests/unit/conductor/test_conductor.py b/nova/tests/unit/conductor/test_conductor.py index 92a86813a9b6..d30173c28560 100644 --- a/nova/tests/unit/conductor/test_conductor.py +++ b/nova/tests/unit/conductor/test_conductor.py @@ -1196,9 +1196,9 @@ class _BaseTaskTestCase(object): inst = fake_instance.fake_db_instance(image_ref='image_ref') inst_obj = objects.Instance._from_db_object( self.context, objects.Instance(), inst, []) - flavor = obj_base.obj_to_primitive(flavors.get_default_flavor()) - flavor['extra_specs'] = 'extra_specs' - request_spec = {'instance_type': flavor, + flavor = flavors.get_default_flavor() + flavor.extra_specs = {'extra_specs': 'fake'} + request_spec = {'instance_type': obj_base.obj_to_primitive(flavor), 'instance_properties': {}} compute_utils.get_image_metadata( self.context, self.conductor_manager.image_api, @@ -1207,7 +1207,7 @@ class _BaseTaskTestCase(object): scheduler_utils.build_request_spec( self.context, 'image', [mox.IsA(objects.Instance)], - instance_type=flavor).AndReturn(request_spec) + instance_type=mox.IsA(objects.Flavor)).AndReturn(request_spec) scheduler_utils.setup_instance_group(self.context, request_spec, {}) @@ -1222,7 +1222,7 @@ class _BaseTaskTestCase(object): self.conductor_manager.compute_rpcapi.prep_resize( self.context, 'image', mox.IsA(objects.Instance), - mox.IsA(dict), 'host1', [], request_spec=request_spec, + mox.IsA(objects.Flavor), 'host1', [], request_spec=request_spec, filter_properties=filter_properties, node=None) self.mox.ReplayAll() @@ -1630,8 +1630,9 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase): self.context, None, None, True, True, None, None, None) def test_migrate_server_fails_with_flavor(self): + flavor = flavors.get_flavor_by_name('m1.tiny') self.assertRaises(NotImplementedError, self.conductor.migrate_server, - self.context, None, None, True, False, "dummy", None, None) + self.context, None, None, True, False, flavor, None, None) def _build_request_spec(self, instance): return { diff --git a/nova/tests/unit/scheduler/test_rpcapi.py b/nova/tests/unit/scheduler/test_rpcapi.py index 381e81deebc8..b3d6c37a2289 100644 --- a/nova/tests/unit/scheduler/test_rpcapi.py +++ b/nova/tests/unit/scheduler/test_rpcapi.py @@ -66,4 +66,5 @@ class SchedulerRpcAPITestCase(test.NoDBTestCase): def test_select_destinations(self): self._test_scheduler_api('select_destinations', rpc_method='call', request_spec='fake_request_spec', - filter_properties='fake_prop') + filter_properties='fake_prop', + version='3.1')