diff --git a/nova/scheduler/chance.py b/nova/scheduler/chance.py index 65a24030c0ec..528711ce2f3a 100644 --- a/nova/scheduler/chance.py +++ b/nova/scheduler/chance.py @@ -23,7 +23,6 @@ import random from oslo.config import cfg -from nova.compute import rpcapi as compute_rpcapi from nova import exception from nova.i18n import _ from nova.scheduler import driver @@ -35,10 +34,6 @@ CONF.import_opt('compute_topic', 'nova.compute.rpcapi') class ChanceScheduler(driver.Scheduler): """Implements Scheduler as a random node selector.""" - def __init__(self, *args, **kwargs): - super(ChanceScheduler, self).__init__(*args, **kwargs) - self.compute_rpcapi = compute_rpcapi.ComputeAPI() - def _filter_hosts(self, request_spec, hosts, filter_properties): """Filter a list of hosts based on request_spec.""" @@ -77,34 +72,3 @@ class ChanceScheduler(driver.Scheduler): if len(dests) < num_instances: raise exception.NoValidHost(reason='') return dests - - # NOTE(alaski): Remove this method when the scheduler rpc interface is - # bumped to 4.x as it is no longer used. - def schedule_run_instance(self, context, request_spec, - admin_password, injected_files, - requested_networks, is_first_time, - filter_properties, legacy_bdm_in_spec): - """Create and run an instance or instances.""" - instance_uuids = request_spec.get('instance_uuids') - for num, instance_uuid in enumerate(instance_uuids): - request_spec['instance_properties']['launch_index'] = num - try: - host = self._schedule(context, CONF.compute_topic, - request_spec, filter_properties) - updated_instance = driver.instance_update_db(context, - instance_uuid) - self.compute_rpcapi.run_instance(context, - instance=updated_instance, host=host, - requested_networks=requested_networks, - injected_files=injected_files, - admin_password=admin_password, - is_first_time=is_first_time, - request_spec=request_spec, - filter_properties=filter_properties, - legacy_bdm_in_spec=legacy_bdm_in_spec) - except Exception as ex: - # NOTE(vish): we don't reraise the exception here to make sure - # that all instances in the request get set to - # error properly - driver.handle_schedule_error(context, ex, instance_uuid, - request_spec) diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py index 3582cb44eedc..a88c743519ba 100644 --- a/nova/scheduler/driver.py +++ b/nova/scheduler/driver.py @@ -115,16 +115,6 @@ class Scheduler(object): for service in services if self.servicegroup_api.service_is_up(service)] - # NOTE(alaski): Remove this method when the scheduler rpc interface is - # bumped to 4.x as it is no longer used. - def schedule_run_instance(self, context, request_spec, - admin_password, injected_files, - requested_networks, is_first_time, - filter_properties, legacy_bdm_in_spec): - """Must override schedule_run_instance method for scheduler to work.""" - msg = _("Driver must implement schedule_run_instance") - raise NotImplementedError(msg) - def select_destinations(self, context, request_spec, filter_properties): """Must override select_destinations method. diff --git a/nova/scheduler/filter_scheduler.py b/nova/scheduler/filter_scheduler.py index 5af733442c7d..40ffa1c93b55 100644 --- a/nova/scheduler/filter_scheduler.py +++ b/nova/scheduler/filter_scheduler.py @@ -23,14 +23,12 @@ import random from oslo.config import cfg -from nova.compute import rpcapi as compute_rpcapi from nova import exception -from nova.i18n import _, _LI, _LW +from nova.i18n import _ from nova.openstack.common import log as logging from nova import rpc from nova.scheduler import driver from nova.scheduler import scheduler_options -from nova.scheduler import utils as scheduler_utils CONF = cfg.CONF @@ -57,81 +55,8 @@ class FilterScheduler(driver.Scheduler): def __init__(self, *args, **kwargs): super(FilterScheduler, self).__init__(*args, **kwargs) self.options = scheduler_options.SchedulerOptions() - self.compute_rpcapi = compute_rpcapi.ComputeAPI() self.notifier = rpc.get_notifier('scheduler') - # NOTE(alaski): Remove this method when the scheduler rpc interface is - # bumped to 4.x as it is no longer used. - def schedule_run_instance(self, context, request_spec, - admin_password, injected_files, - requested_networks, is_first_time, - filter_properties, legacy_bdm_in_spec): - """Provisions instances that needs to be scheduled - - Applies filters and weighters on request properties to get a list of - compute hosts and calls them to spawn instance(s). - """ - payload = dict(request_spec=request_spec) - self.notifier.info(context, 'scheduler.run_instance.start', payload) - - instance_uuids = request_spec.get('instance_uuids') - LOG.info(_LI("Attempting to build %(num_instances)d instance(s) " - "uuids: %(instance_uuids)s"), - {'num_instances': len(instance_uuids), - 'instance_uuids': instance_uuids}) - LOG.debug("Request Spec: %s" % request_spec) - - # check retry policy. Rather ugly use of instance_uuids[0]... - # but if we've exceeded max retries... then we really only - # have a single instance. - scheduler_utils.populate_retry(filter_properties, - instance_uuids[0]) - weighed_hosts = self._schedule(context, request_spec, - filter_properties) - - # NOTE: Pop instance_uuids as individual creates do not need the - # set of uuids. Do not pop before here as the upper exception - # handler fo NoValidHost needs the uuid to set error state - instance_uuids = request_spec.pop('instance_uuids') - - # NOTE(comstud): Make sure we do not pass this through. It - # contains an instance of RpcContext that cannot be serialized. - filter_properties.pop('context', None) - - for num, instance_uuid in enumerate(instance_uuids): - request_spec['instance_properties']['launch_index'] = num - - try: - try: - weighed_host = weighed_hosts.pop(0) - LOG.info(_LI("Choosing host %(weighed_host)s " - "for instance %(instance_uuid)s"), - {'weighed_host': weighed_host, - 'instance_uuid': instance_uuid}) - except IndexError: - raise exception.NoValidHost(reason="") - - self._provision_resource(context, weighed_host, - request_spec, - filter_properties, - requested_networks, - injected_files, admin_password, - is_first_time, - instance_uuid=instance_uuid, - legacy_bdm_in_spec=legacy_bdm_in_spec) - except Exception as ex: - # NOTE(vish): we don't reraise the exception here to make sure - # that all instances in the request get set to - # error properly - driver.handle_schedule_error(context, ex, instance_uuid, - request_spec) - # scrub retry host list in case we're scheduling multiple - # instances: - retry = filter_properties.get('retry', {}) - retry['hosts'] = [] - - self.notifier.info(context, 'scheduler.run_instance.end', payload) - def select_destinations(self, context, request_spec, filter_properties): """Selects a filtered set of hosts and nodes.""" self.notifier.info(context, 'scheduler.select_destinations.start', @@ -161,42 +86,6 @@ class FilterScheduler(driver.Scheduler): dict(request_spec=request_spec)) return dests - def _provision_resource(self, context, weighed_host, request_spec, - filter_properties, requested_networks, injected_files, - admin_password, is_first_time, instance_uuid=None, - legacy_bdm_in_spec=True): - """Create the requested resource in this Zone.""" - # NOTE(vish): add our current instance back into the request spec - request_spec['instance_uuids'] = [instance_uuid] - payload = dict(request_spec=request_spec, - weighted_host=weighed_host.to_dict(), - instance_id=instance_uuid) - self.notifier.info(context, - 'scheduler.run_instance.scheduled', payload) - - # Update the metadata if necessary - try: - updated_instance = driver.instance_update_db(context, - instance_uuid) - except exception.InstanceNotFound: - LOG.warning(_LW("Instance disappeared during scheduling"), - context=context, instance_uuid=instance_uuid) - - else: - scheduler_utils.populate_filter_properties(filter_properties, - weighed_host.obj) - - self.compute_rpcapi.run_instance(context, - instance=updated_instance, - host=weighed_host.obj.host, - request_spec=request_spec, - filter_properties=filter_properties, - requested_networks=requested_networks, - injected_files=injected_files, - admin_password=admin_password, is_first_time=is_first_time, - node=weighed_host.obj.nodename, - legacy_bdm_in_spec=legacy_bdm_in_spec) - def _get_configuration_options(self): """Fetch options dictionary. Broken out for testing.""" return self.options.get_configuration() diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index f24abadb1772..a272654e4600 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -22,19 +22,14 @@ Scheduler Service from oslo.config import cfg from oslo import messaging from oslo.serialization import jsonutils -from oslo.utils import excutils from oslo.utils import importutils -from nova.compute import rpcapi as compute_rpcapi -from nova.compute import utils as compute_utils -from nova.compute import vm_states from nova import exception from nova import manager from nova import objects from nova.openstack.common import log as logging from nova.openstack.common import periodic_task from nova import quota -from nova.scheduler import utils as scheduler_utils LOG = logging.getLogger(__name__) @@ -60,99 +55,15 @@ QUOTAS = quota.QUOTAS class SchedulerManager(manager.Manager): """Chooses a host to run instances on.""" - target = messaging.Target(version='3.1') + target = messaging.Target(version='4.0') def __init__(self, scheduler_driver=None, *args, **kwargs): if not scheduler_driver: scheduler_driver = CONF.scheduler_driver self.driver = importutils.import_object(scheduler_driver) - self.compute_rpcapi = compute_rpcapi.ComputeAPI() super(SchedulerManager, self).__init__(service_name='scheduler', *args, **kwargs) - - # NOTE(alaski): Remove this method when the scheduler rpc interface is - # bumped to 4.x as it is no longer used. - def run_instance(self, context, request_spec, admin_password, - injected_files, requested_networks, is_first_time, - filter_properties, legacy_bdm_in_spec): - """Tries to call schedule_run_instance on the driver. - Sets instance vm_state to ERROR on exceptions - """ - instance_uuids = request_spec['instance_uuids'] - with compute_utils.EventReporter(context, 'schedule', *instance_uuids): - try: - return self.driver.schedule_run_instance(context, - request_spec, admin_password, injected_files, - requested_networks, is_first_time, filter_properties, - legacy_bdm_in_spec) - - except exception.NoValidHost as ex: - # don't re-raise - self._set_vm_state_and_notify('run_instance', - {'vm_state': vm_states.ERROR, - 'task_state': None}, - context, ex, request_spec) - except Exception as ex: - with excutils.save_and_reraise_exception(): - self._set_vm_state_and_notify('run_instance', - {'vm_state': vm_states.ERROR, - 'task_state': None}, - context, ex, request_spec) - - # NOTE(sbauza): Remove this method when the scheduler rpc interface is - # bumped to 4.x as it is no longer used. - def prep_resize(self, context, image, request_spec, filter_properties, - instance, instance_type, reservations): - """Tries to call schedule_prep_resize on the driver. - Sets instance vm_state to ACTIVE on NoHostFound - Sets vm_state to ERROR on other exceptions - """ - instance_uuid = instance['uuid'] - with compute_utils.EventReporter(context, 'schedule', instance_uuid): - try: - request_spec['num_instances'] = len( - request_spec['instance_uuids']) - hosts = self.driver.select_destinations( - context, request_spec, filter_properties) - host_state = hosts[0] - - scheduler_utils.populate_filter_properties(filter_properties, - host_state) - # context is not serializable - filter_properties.pop('context', None) - - (host, node) = (host_state['host'], host_state['nodename']) - attrs = ['metadata', 'system_metadata', 'info_cache', - 'security_groups'] - inst_obj = objects.Instance._from_db_object( - context, objects.Instance(), instance, - expected_attrs=attrs) - self.compute_rpcapi.prep_resize( - context, image, inst_obj, instance_type, host, - reservations, request_spec=request_spec, - filter_properties=filter_properties, node=node) - - except exception.NoValidHost as ex: - vm_state = instance.get('vm_state', vm_states.ACTIVE) - self._set_vm_state_and_notify('prep_resize', - {'vm_state': vm_state, - 'task_state': None}, - context, ex, request_spec) - if reservations: - QUOTAS.rollback(context, reservations) - except Exception as ex: - with excutils.save_and_reraise_exception(): - self._set_vm_state_and_notify('prep_resize', - {'vm_state': vm_states.ERROR, - 'task_state': None}, - context, ex, request_spec) - if reservations: - QUOTAS.rollback(context, reservations) - - def _set_vm_state_and_notify(self, method, updates, context, ex, - request_spec): - scheduler_utils.set_vm_state_and_notify( - context, 'scheduler', method, updates, ex, request_spec, self.db) + self.additional_endpoints.append(_SchedulerManagerV3Proxy(self)) @periodic_task.periodic_task def _expire_reservations(self, context): @@ -163,6 +74,30 @@ class SchedulerManager(manager.Manager): def _run_periodic_tasks(self, context): self.driver.run_periodic_tasks(context) + @messaging.expected_exceptions(exception.NoValidHost) + def select_destinations(self, context, request_spec, filter_properties): + """Returns destinations(s) best suited for this request_spec and + filter_properties. + + The result should be a list of dicts with 'host', 'nodename' and + 'limits' as keys. + """ + dests = self.driver.select_destinations(context, request_spec, + filter_properties) + return jsonutils.to_primitive(dests) + + +class _SchedulerManagerV3Proxy(object): + + target = messaging.Target(version='3.0') + + def __init__(self, manager): + self.manager = manager + + # NOTE(sbauza): Previous run_instance() and prep_resize() methods were + # removed from the Juno branch before Juno released, so we can safely + # remove them even from the V3.1 proxy as there is no Juno RPC client + # that can call them @messaging.expected_exceptions(exception.NoValidHost) def select_destinations(self, context, request_spec, filter_properties): """Returns destinations(s) best suited for this request_spec and @@ -178,6 +113,6 @@ class SchedulerManager(manager.Manager): # 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, + dests = self.manager.select_destinations(context, request_spec, filter_properties) - return jsonutils.to_primitive(dests) + return dests diff --git a/nova/scheduler/rpcapi.py b/nova/scheduler/rpcapi.py index 902af8661cb8..2bcdabee4982 100644 --- a/nova/scheduler/rpcapi.py +++ b/nova/scheduler/rpcapi.py @@ -86,6 +86,9 @@ class SchedulerAPI(object): * 3.1 - Made select_destinations() send flavor object + * 4.0 - Removed backwards compat for Icehouse + + ''' VERSION_ALIASES = { @@ -93,11 +96,12 @@ class SchedulerAPI(object): 'havana': '2.9', 'icehouse': '3.0', 'juno': '3.0', + 'kilo': '4.0', } def __init__(self): super(SchedulerAPI, self).__init__() - target = messaging.Target(topic=CONF.scheduler_topic, version='3.0') + target = messaging.Target(topic=CONF.scheduler_topic, version='4.0') version_cap = self.VERSION_ALIASES.get(CONF.upgrade_levels.scheduler, CONF.upgrade_levels.scheduler) serializer = objects_base.NovaObjectSerializer() @@ -105,14 +109,6 @@ class SchedulerAPI(object): serializer=serializer) def select_destinations(self, ctxt, request_spec, filter_properties): - 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) + cctxt = self.client.prepare(version='4.0') return cctxt.call(ctxt, 'select_destinations', request_spec=request_spec, filter_properties=filter_properties) diff --git a/nova/tests/unit/scheduler/test_chance_scheduler.py b/nova/tests/unit/scheduler/test_chance_scheduler.py index 31d9c9314533..a64b1d820288 100644 --- a/nova/tests/unit/scheduler/test_chance_scheduler.py +++ b/nova/tests/unit/scheduler/test_chance_scheduler.py @@ -20,14 +20,9 @@ import random from mox3 import mox -from nova.compute import rpcapi as compute_rpcapi -from nova.compute import utils as compute_utils -from nova.compute import vm_states from nova import context -from nova import db from nova import exception from nova.scheduler import chance -from nova.scheduler import driver from nova.tests.unit.scheduler import test_scheduler @@ -62,80 +57,6 @@ class ChanceSchedulerTestCase(test_scheduler.SchedulerTestCase): filter_properties=filter_properties) self.assertEqual(filtered, hosts) - def test_basic_schedule_run_instance(self): - ctxt = context.RequestContext('fake', 'fake', False) - ctxt_elevated = 'fake-context-elevated' - instance_opts = {'fake_opt1': 'meow', 'launch_index': -1} - instance1 = {'uuid': 'fake-uuid1'} - instance2 = {'uuid': 'fake-uuid2'} - request_spec = {'instance_uuids': ['fake-uuid1', 'fake-uuid2'], - 'instance_properties': instance_opts} - - def inc_launch_index(*args): - request_spec['instance_properties']['launch_index'] = ( - request_spec['instance_properties']['launch_index'] + 1) - - self.mox.StubOutWithMock(ctxt, 'elevated') - self.mox.StubOutWithMock(self.driver, 'hosts_up') - self.mox.StubOutWithMock(random, 'choice') - self.mox.StubOutWithMock(driver, 'instance_update_db') - self.mox.StubOutWithMock(compute_rpcapi.ComputeAPI, 'run_instance') - - ctxt.elevated().AndReturn(ctxt_elevated) - # instance 1 - hosts_full = ['host1', 'host2', 'host3', 'host4'] - self.driver.hosts_up(ctxt_elevated, 'compute').AndReturn(hosts_full) - random.choice(hosts_full).AndReturn('host3') - driver.instance_update_db(ctxt, instance1['uuid']).WithSideEffects( - inc_launch_index).AndReturn(instance1) - compute_rpcapi.ComputeAPI.run_instance(ctxt, host='host3', - instance=instance1, requested_networks=None, - injected_files=None, admin_password=None, is_first_time=None, - request_spec=request_spec, filter_properties={}, - legacy_bdm_in_spec=False) - - # instance 2 - ctxt.elevated().AndReturn(ctxt_elevated) - self.driver.hosts_up(ctxt_elevated, 'compute').AndReturn(hosts_full) - random.choice(hosts_full).AndReturn('host1') - driver.instance_update_db(ctxt, instance2['uuid']).WithSideEffects( - inc_launch_index).AndReturn(instance2) - compute_rpcapi.ComputeAPI.run_instance(ctxt, host='host1', - instance=instance2, requested_networks=None, - injected_files=None, admin_password=None, is_first_time=None, - request_spec=request_spec, filter_properties={}, - legacy_bdm_in_spec=False) - - self.mox.ReplayAll() - self.driver.schedule_run_instance(ctxt, request_spec, - None, None, None, None, {}, False) - - def test_basic_schedule_run_instance_no_hosts(self): - ctxt = context.RequestContext('fake', 'fake', False) - ctxt_elevated = 'fake-context-elevated' - uuid = 'fake-uuid1' - instance_opts = {'fake_opt1': 'meow', 'launch_index': -1} - request_spec = {'instance_uuids': [uuid], - 'instance_properties': instance_opts} - - self.mox.StubOutWithMock(ctxt, 'elevated') - self.mox.StubOutWithMock(self.driver, 'hosts_up') - self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc') - self.mox.StubOutWithMock(db, 'instance_update_and_get_original') - - # instance 1 - ctxt.elevated().AndReturn(ctxt_elevated) - self.driver.hosts_up(ctxt_elevated, 'compute').AndReturn([]) - old_ref, new_ref = db.instance_update_and_get_original(ctxt, uuid, - {'vm_state': vm_states.ERROR, - 'task_state': None}).AndReturn(({}, {})) - compute_utils.add_instance_fault_from_exc(ctxt, new_ref, - mox.IsA(exception.NoValidHost), mox.IgnoreArg()) - - self.mox.ReplayAll() - self.driver.schedule_run_instance( - ctxt, request_spec, None, None, None, None, {}, False) - def test_select_destinations(self): ctxt = context.RequestContext('fake', 'fake', False) ctxt_elevated = 'fake-context-elevated' diff --git a/nova/tests/unit/scheduler/test_filter_scheduler.py b/nova/tests/unit/scheduler/test_filter_scheduler.py index 24fb7eb0db05..386f2f436b96 100644 --- a/nova/tests/unit/scheduler/test_filter_scheduler.py +++ b/nova/tests/unit/scheduler/test_filter_scheduler.py @@ -17,14 +17,9 @@ Tests For Filter Scheduler. """ import mock -from mox3 import mox -from nova.compute import utils as compute_utils -from nova.compute import vm_states from nova import context -from nova import db from nova import exception -from nova.scheduler import driver from nova.scheduler import filter_scheduler from nova.scheduler import host_manager from nova.scheduler import utils as scheduler_utils @@ -42,108 +37,6 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): driver_cls = filter_scheduler.FilterScheduler - def test_run_instance_no_hosts(self): - sched = fakes.FakeFilterScheduler() - uuid = 'fake-uuid1' - fake_context = context.RequestContext('user', 'project') - instance_properties = {'project_id': 1, 'os_type': 'Linux'} - request_spec = {'instance_type': {'memory_mb': 1, 'root_gb': 1, - 'ephemeral_gb': 0}, - 'instance_properties': instance_properties, - 'instance_uuids': [uuid]} - - self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc') - self.mox.StubOutWithMock(db, 'instance_update_and_get_original') - old_ref, new_ref = db.instance_update_and_get_original(fake_context, - uuid, {'vm_state': vm_states.ERROR, 'task_state': - None}).AndReturn(({}, {})) - compute_utils.add_instance_fault_from_exc(fake_context, new_ref, - mox.IsA(exception.NoValidHost), mox.IgnoreArg()) - - self.mox.StubOutWithMock(db, 'compute_node_get_all') - db.compute_node_get_all(mox.IgnoreArg()).AndReturn([]) - - self.mox.ReplayAll() - sched.schedule_run_instance( - fake_context, request_spec, None, None, - None, None, {}, False) - - def test_run_instance_non_admin(self): - self.was_admin = False - - def fake_get(context, *args, **kwargs): - # make sure this is called with admin context, even though - # we're using user context below - self.was_admin = context.is_admin - return {} - - sched = fakes.FakeFilterScheduler() - self.stubs.Set(sched.host_manager, 'get_all_host_states', fake_get) - - fake_context = context.RequestContext('user', 'project') - - uuid = 'fake-uuid1' - instance_properties = {'project_id': 1, 'os_type': 'Linux'} - request_spec = {'instance_type': {'memory_mb': 1, 'local_gb': 1}, - 'instance_properties': instance_properties, - 'instance_uuids': [uuid]} - self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc') - self.mox.StubOutWithMock(db, 'instance_update_and_get_original') - old_ref, new_ref = db.instance_update_and_get_original(fake_context, - uuid, {'vm_state': vm_states.ERROR, 'task_state': - None}).AndReturn(({}, {})) - compute_utils.add_instance_fault_from_exc(fake_context, new_ref, - mox.IsA(exception.NoValidHost), mox.IgnoreArg()) - self.mox.ReplayAll() - sched.schedule_run_instance( - fake_context, request_spec, None, None, None, None, {}, False) - self.assertTrue(self.was_admin) - - def test_scheduler_includes_launch_index(self): - fake_context = context.RequestContext('user', 'project') - instance_opts = {'fake_opt1': 'meow'} - request_spec = {'instance_uuids': ['fake-uuid1', 'fake-uuid2'], - 'instance_properties': instance_opts} - instance1 = {'uuid': 'fake-uuid1'} - instance2 = {'uuid': 'fake-uuid2'} - - def _has_launch_index(expected_index): - """Return a function that verifies the expected index.""" - def _check_launch_index(value): - if 'instance_properties' in value: - if 'launch_index' in value['instance_properties']: - index = value['instance_properties']['launch_index'] - if index == expected_index: - return True - return False - return _check_launch_index - - self.mox.StubOutWithMock(self.driver, '_schedule') - self.mox.StubOutWithMock(self.driver, '_provision_resource') - - expected_filter_properties = {'retry': {'num_attempts': 1, - 'hosts': []}} - self.driver._schedule(fake_context, request_spec, - expected_filter_properties).AndReturn(['host1', 'host2']) - # instance 1 - self.driver._provision_resource( - fake_context, 'host1', - mox.Func(_has_launch_index(0)), expected_filter_properties, - None, None, None, None, - instance_uuid='fake-uuid1', - legacy_bdm_in_spec=False).AndReturn(instance1) - # instance 2 - self.driver._provision_resource( - fake_context, 'host2', - mox.Func(_has_launch_index(1)), expected_filter_properties, - None, None, None, None, - instance_uuid='fake-uuid2', - legacy_bdm_in_spec=False).AndReturn(instance2) - self.mox.ReplayAll() - - self.driver.schedule_run_instance(fake_context, request_spec, - None, None, None, None, {}, False) - @mock.patch('nova.db.instance_extra_get_by_instance_uuid', return_value={'numa_topology': None, 'pci_requests': None}) @@ -195,148 +88,6 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): self.assertRaises(exception.NovaException, scheduler_utils._max_attempts) - def test_retry_disabled(self): - # Retry info should not get populated when re-scheduling is off. - self.flags(scheduler_max_attempts=1) - sched = fakes.FakeFilterScheduler() - request_spec = dict(instance_properties={}, - instance_uuids=['fake-uuid1']) - filter_properties = {} - - self.mox.StubOutWithMock(sched, '_schedule') - self.mox.StubOutWithMock(sched, '_provision_resource') - - sched._schedule(self.context, request_spec, - filter_properties).AndReturn(['host1']) - sched._provision_resource( - self.context, 'host1', - request_spec, filter_properties, - None, None, None, None, - instance_uuid='fake-uuid1', - legacy_bdm_in_spec=False) - - self.mox.ReplayAll() - - sched.schedule_run_instance(self.context, request_spec, None, None, - None, None, filter_properties, False) - - def test_retry_force_hosts(self): - # Retry info should not get populated when re-scheduling is off. - self.flags(scheduler_max_attempts=2) - sched = fakes.FakeFilterScheduler() - request_spec = dict(instance_properties={}, - instance_uuids=['fake-uuid1']) - filter_properties = {'force_hosts': ['force_host']} - - self.mox.StubOutWithMock(sched, '_schedule') - self.mox.StubOutWithMock(sched, '_provision_resource') - - sched._schedule(self.context, request_spec, - filter_properties).AndReturn(['host1']) - sched._provision_resource( - self.context, 'host1', - request_spec, filter_properties, - None, None, None, None, - instance_uuid='fake-uuid1', - legacy_bdm_in_spec=False) - - self.mox.ReplayAll() - - sched.schedule_run_instance(self.context, request_spec, None, None, - None, None, filter_properties, False) - - def test_retry_force_nodes(self): - # Retry info should not get populated when re-scheduling is off. - self.flags(scheduler_max_attempts=2) - sched = fakes.FakeFilterScheduler() - request_spec = dict(instance_properties={}, - instance_uuids=['fake-uuid1']) - filter_properties = {'force_nodes': ['force_node']} - - self.mox.StubOutWithMock(sched, '_schedule') - self.mox.StubOutWithMock(sched, '_provision_resource') - - sched._schedule(self.context, request_spec, - filter_properties).AndReturn(['host1']) - sched._provision_resource( - self.context, 'host1', - request_spec, filter_properties, - None, None, None, None, - instance_uuid='fake-uuid1', - legacy_bdm_in_spec=False) - - self.mox.ReplayAll() - - sched.schedule_run_instance(self.context, request_spec, None, None, - None, None, filter_properties, False) - - def test_retry_attempt_one(self): - # Test retry logic on initial scheduling attempt. - self.flags(scheduler_max_attempts=2) - sched = fakes.FakeFilterScheduler() - request_spec = dict(instance_properties={}, - instance_uuids=['fake-uuid1']) - filter_properties = {} - expected_filter_properties = {'retry': {'num_attempts': 1, - 'hosts': []}} - self.mox.StubOutWithMock(sched, '_schedule') - self.mox.StubOutWithMock(sched, '_provision_resource') - - sched._schedule(self.context, request_spec, - expected_filter_properties).AndReturn(['host1']) - sched._provision_resource( - self.context, 'host1', - request_spec, expected_filter_properties, - None, None, None, None, - instance_uuid='fake-uuid1', - legacy_bdm_in_spec=False) - - self.mox.ReplayAll() - - sched.schedule_run_instance(self.context, request_spec, None, None, - None, None, filter_properties, False) - - def test_retry_attempt_two(self): - # Test retry logic when re-scheduling. - self.flags(scheduler_max_attempts=2) - sched = fakes.FakeFilterScheduler() - request_spec = dict(instance_properties={}, - instance_uuids=['fake-uuid1']) - filter_properties = {'retry': {'num_attempts': 1}} - expected_filter_properties = {'retry': {'num_attempts': 2}} - self.mox.StubOutWithMock(sched, '_schedule') - self.mox.StubOutWithMock(sched, '_provision_resource') - - sched._schedule(self.context, request_spec, - expected_filter_properties).AndReturn(['host1']) - sched._provision_resource( - self.context, 'host1', - request_spec, expected_filter_properties, - None, None, None, None, - instance_uuid='fake-uuid1', - legacy_bdm_in_spec=False) - - self.mox.ReplayAll() - - sched.schedule_run_instance(self.context, request_spec, None, None, - None, None, filter_properties, False) - - def test_retry_exceeded_max_attempts(self): - # Test for necessary explosion when max retries is exceeded and that - # the information needed in request_spec is still present for error - # handling - self.flags(scheduler_max_attempts=2) - sched = fakes.FakeFilterScheduler() - request_spec = dict(instance_properties={}, - instance_uuids=['fake-uuid1']) - filter_properties = {'retry': {'num_attempts': 2}} - - self.mox.ReplayAll() - - self.assertRaises(exception.NoValidHost, sched.schedule_run_instance, - self.context, request_spec, None, None, - None, None, filter_properties, False) - def test_add_retry_host(self): retry = dict(num_attempts=1, hosts=[]) filter_properties = dict(retry=retry) @@ -569,28 +320,3 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): # Make sure that we provided a reason why NoValidHost. self.assertIn('reason', e.kwargs) self.assertTrue(len(e.kwargs['reason']) > 0) - - def test_handles_deleted_instance(self): - """Test instance deletion while being scheduled.""" - - def _raise_instance_not_found(*args, **kwargs): - raise exception.InstanceNotFound(instance_id='123') - - self.stubs.Set(driver, 'instance_update_db', - _raise_instance_not_found) - - sched = fakes.FakeFilterScheduler() - - fake_context = context.RequestContext('user', 'project') - host_state = host_manager.HostState('host2', 'node2') - weighted_host = weights.WeighedHost(host_state, 1.42) - filter_properties = {} - - uuid = 'fake-uuid1' - instance_properties = {'project_id': 1, 'os_type': 'Linux'} - request_spec = {'instance_type': {'memory_mb': 1, 'local_gb': 1}, - 'instance_properties': instance_properties, - 'instance_uuids': [uuid]} - sched._provision_resource(fake_context, weighted_host, - request_spec, filter_properties, - None, None, None, None) diff --git a/nova/tests/unit/scheduler/test_rpcapi.py b/nova/tests/unit/scheduler/test_rpcapi.py index b3d6c37a2289..1cc4547d58ed 100644 --- a/nova/tests/unit/scheduler/test_rpcapi.py +++ b/nova/tests/unit/scheduler/test_rpcapi.py @@ -67,4 +67,4 @@ class SchedulerRpcAPITestCase(test.NoDBTestCase): self._test_scheduler_api('select_destinations', rpc_method='call', request_spec='fake_request_spec', filter_properties='fake_prop', - version='3.1') + version='4.0') diff --git a/nova/tests/unit/scheduler/test_scheduler.py b/nova/tests/unit/scheduler/test_scheduler.py index 45ac720525ac..8fbaf376c3cc 100644 --- a/nova/tests/unit/scheduler/test_scheduler.py +++ b/nova/tests/unit/scheduler/test_scheduler.py @@ -17,23 +17,20 @@ Tests For Scheduler """ +import mock from mox3 import mox from oslo.config import cfg from nova.compute import api as compute_api -from nova.compute import utils as compute_utils -from nova.compute import vm_states from nova import context from nova import db from nova import exception from nova.image import glance -from nova import objects from nova import rpc from nova.scheduler import driver from nova.scheduler import manager from nova import servicegroup from nova import test -from nova.tests.unit import fake_instance from nova.tests.unit import fake_server_actions from nova.tests.unit.image import fake as fake_image from nova.tests.unit.objects import test_instance_fault @@ -75,199 +72,24 @@ class SchedulerManagerTestCase(test.NoDBTestCase): self.mox.StubOutWithMock(self.manager.driver, method_name) - def test_run_instance_exception_puts_instance_in_error_state(self): - fake_instance_uuid = 'fake-instance-id' - inst = {"vm_state": "", "task_state": ""} + def test_select_destination(self): + with mock.patch.object(self.manager, 'select_destinations' + ) as select_destinations: + self.manager.select_destinations(None, None, {}) + select_destinations.assert_called_once_with(None, None, {}) - self._mox_schedule_method_helper('schedule_run_instance') - self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc') - self.mox.StubOutWithMock(db, 'instance_update_and_get_original') - request_spec = {'instance_properties': inst, - 'instance_uuids': [fake_instance_uuid]} +class SchedulerV3PassthroughTestCase(test.TestCase): + def setUp(self): + super(SchedulerV3PassthroughTestCase, self).setUp() + self.manager = manager.SchedulerManager() + self.proxy = manager._SchedulerManagerV3Proxy(self.manager) - self.manager.driver.schedule_run_instance(self.context, - request_spec, None, None, None, None, {}, False).AndRaise( - exception.NoValidHost(reason="")) - old, new_ref = db.instance_update_and_get_original(self.context, - fake_instance_uuid, - {"vm_state": vm_states.ERROR, - "task_state": None}).AndReturn((inst, inst)) - compute_utils.add_instance_fault_from_exc(self.context, - new_ref, mox.IsA(exception.NoValidHost), mox.IgnoreArg()) - - self.mox.ReplayAll() - self.manager.run_instance(self.context, request_spec, - None, None, None, None, {}, False) - - def test_prep_resize_no_valid_host_back_in_active_state(self): - fake_instance_uuid = 'fake-instance-id' - fake_instance = {'uuid': fake_instance_uuid} - inst = {"vm_state": "", "task_state": ""} - - self._mox_schedule_method_helper('select_destinations') - - self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc') - self.mox.StubOutWithMock(db, 'instance_update_and_get_original') - - request_spec = {'instance_type': 'fake_type', - 'instance_uuids': [fake_instance_uuid], - 'instance_properties': {'uuid': fake_instance_uuid}} - kwargs = { - 'context': self.context, - 'image': 'fake_image', - 'request_spec': request_spec, - 'filter_properties': 'fake_props', - 'instance': fake_instance, - 'instance_type': 'fake_type', - 'reservations': list('fake_res'), - } - self.manager.driver.select_destinations( - self.context, request_spec, 'fake_props').AndRaise( - exception.NoValidHost(reason="")) - old_ref, new_ref = db.instance_update_and_get_original(self.context, - fake_instance_uuid, - {"vm_state": vm_states.ACTIVE, "task_state": None}).AndReturn( - (inst, inst)) - compute_utils.add_instance_fault_from_exc(self.context, new_ref, - mox.IsA(exception.NoValidHost), mox.IgnoreArg()) - - self.mox.ReplayAll() - self.manager.prep_resize(**kwargs) - - def test_prep_resize_no_valid_host_back_in_shutoff_state(self): - fake_instance_uuid = 'fake-instance-id' - fake_instance = {'uuid': fake_instance_uuid, "vm_state": "stopped"} - inst = {"vm_state": "stopped", "task_state": ""} - - self._mox_schedule_method_helper('select_destinations') - - self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc') - self.mox.StubOutWithMock(db, 'instance_update_and_get_original') - - request_spec = {'instance_type': 'fake_type', - 'instance_uuids': [fake_instance_uuid], - 'instance_properties': {'uuid': fake_instance_uuid}} - kwargs = { - 'context': self.context, - 'image': 'fake_image', - 'request_spec': request_spec, - 'filter_properties': 'fake_props', - 'instance': fake_instance, - 'instance_type': 'fake_type', - 'reservations': list('fake_res'), - } - self.manager.driver.select_destinations( - self.context, request_spec, 'fake_props').AndRaise( - exception.NoValidHost(reason="")) - old_ref, new_ref = db.instance_update_and_get_original(self.context, - fake_instance_uuid, - {"vm_state": vm_states.STOPPED, "task_state": None}).AndReturn( - (inst, inst)) - compute_utils.add_instance_fault_from_exc(self.context, new_ref, - mox.IsA(exception.NoValidHost), mox.IgnoreArg()) - - self.mox.ReplayAll() - self.manager.prep_resize(**kwargs) - - def test_prep_resize_exception_host_in_error_state_and_raise(self): - fake_instance_uuid = 'fake-instance-id' - fake_instance = {'uuid': fake_instance_uuid} - - self._mox_schedule_method_helper('select_destinations') - - self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc') - self.mox.StubOutWithMock(db, 'instance_update_and_get_original') - - request_spec = { - 'instance_properties': {'uuid': fake_instance_uuid}, - 'instance_uuids': [fake_instance_uuid] - } - kwargs = { - 'context': self.context, - 'image': 'fake_image', - 'request_spec': request_spec, - 'filter_properties': 'fake_props', - 'instance': fake_instance, - 'instance_type': 'fake_type', - 'reservations': list('fake_res'), - } - - self.manager.driver.select_destinations( - self.context, request_spec, 'fake_props').AndRaise( - test.TestingException('something happened')) - - inst = { - "vm_state": "", - "task_state": "", - } - old_ref, new_ref = db.instance_update_and_get_original(self.context, - fake_instance_uuid, - {"vm_state": vm_states.ERROR, - "task_state": None}).AndReturn((inst, inst)) - compute_utils.add_instance_fault_from_exc(self.context, new_ref, - mox.IsA(test.TestingException), mox.IgnoreArg()) - - self.mox.ReplayAll() - - self.assertRaises(test.TestingException, self.manager.prep_resize, - **kwargs) - - def test_set_vm_state_and_notify_adds_instance_fault(self): - request = {'instance_properties': {'uuid': 'fake-uuid'}} - updates = {'vm_state': 'foo'} - fake_inst = {'uuid': 'fake-uuid'} - - self.mox.StubOutWithMock(db, 'instance_update_and_get_original') - self.mox.StubOutWithMock(db, 'instance_fault_create') - self.mox.StubOutWithMock(rpc, 'get_notifier') - notifier = self.mox.CreateMockAnything() - rpc.get_notifier('scheduler').AndReturn(notifier) - db.instance_update_and_get_original(self.context, 'fake-uuid', - updates).AndReturn((None, - fake_inst)) - db.instance_fault_create(self.context, mox.IgnoreArg()).AndReturn( - test_instance_fault.fake_faults['fake-uuid'][0]) - notifier.error(self.context, 'scheduler.foo', mox.IgnoreArg()) - self.mox.ReplayAll() - - self.manager._set_vm_state_and_notify('foo', {'vm_state': 'foo'}, - self.context, None, request) - - def test_prep_resize_post_populates_retry(self): - self.manager.driver = fakes.FakeFilterScheduler() - - image = 'image' - instance_uuid = 'fake-instance-id' - instance = fake_instance.fake_db_instance(uuid=instance_uuid) - - instance_properties = {'project_id': 'fake', 'os_type': 'Linux'} - instance_type = "m1.tiny" - request_spec = {'instance_properties': instance_properties, - 'instance_type': instance_type, - 'instance_uuids': [instance_uuid]} - retry = {'hosts': [], 'num_attempts': 1} - filter_properties = {'retry': retry} - reservations = None - - hosts = [dict(host='host', nodename='node', limits={})] - - self._mox_schedule_method_helper('select_destinations') - self.manager.driver.select_destinations( - self.context, request_spec, filter_properties).AndReturn(hosts) - - self.mox.StubOutWithMock(self.manager.compute_rpcapi, 'prep_resize') - self.manager.compute_rpcapi.prep_resize(self.context, image, - mox.IsA(objects.Instance), - instance_type, 'host', reservations, request_spec=request_spec, - filter_properties=filter_properties, node='node') - - self.mox.ReplayAll() - self.manager.prep_resize(self.context, image, request_spec, - filter_properties, instance, instance_type, reservations) - - self.assertEqual([['host', 'node']], - filter_properties['retry']['hosts']) + def test_select_destination(self): + with mock.patch.object(self.manager, 'select_destinations' + ) as select_destinations: + self.proxy.select_destinations(None, None, {}) + select_destinations.assert_called_once_with(None, None, {}) class SchedulerTestCase(test.NoDBTestCase): @@ -342,15 +164,6 @@ class SchedulerDriverBaseTestCase(SchedulerTestCase): that will fail if the driver is changed. """ - def test_unimplemented_schedule_run_instance(self): - fake_request_spec = {'instance_properties': - {'uuid': 'uuid'}} - - self.assertRaises(NotImplementedError, - self.driver.schedule_run_instance, - self.context, fake_request_spec, None, None, None, - None, None, False) - def test_unimplemented_select_destinations(self): self.assertRaises(NotImplementedError, self.driver.select_destinations, self.context, {}, {})