nova/nova/tests/scheduler/test_scheduler.py

585 lines
24 KiB
Python

# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# 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.
"""
Tests For Scheduler
"""
import mock
import mox
from oslo.config import cfg
from oslo import messaging
from nova.compute import api as compute_api
from nova.compute import task_states
from nova.compute import utils as compute_utils
from nova.compute import vm_states
from nova.conductor import api as conductor_api
from nova.conductor.tasks import live_migrate
from nova import context
from nova import db
from nova import exception
from nova.image import glance
from nova.objects import instance as instance_obj
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 import fake_instance
from nova.tests import fake_server_actions
from nova.tests.image import fake as fake_image
from nova.tests import matchers
from nova.tests.scheduler import fakes
from nova import utils
CONF = cfg.CONF
class SchedulerManagerTestCase(test.NoDBTestCase):
"""Test case for scheduler manager."""
manager_cls = manager.SchedulerManager
driver_cls = driver.Scheduler
driver_cls_name = 'nova.scheduler.driver.Scheduler'
def setUp(self):
super(SchedulerManagerTestCase, self).setUp()
self.flags(scheduler_driver=self.driver_cls_name)
self.stubs.Set(compute_api, 'API', fakes.FakeComputeAPI)
self.manager = self.manager_cls()
self.context = context.RequestContext('fake_user', 'fake_project')
self.topic = 'fake_topic'
self.fake_args = (1, 2, 3)
self.fake_kwargs = {'cat': 'meow', 'dog': 'woof'}
fake_server_actions.stub_out_action_events(self.stubs)
def test_1_correct_init(self):
# Correct scheduler driver
manager = self.manager
self.assertIsInstance(manager.driver, self.driver_cls)
def test_show_host_resources(self):
host = 'fake_host'
compute_node = {'host': host,
'compute_node': [{'vcpus': 4,
'vcpus_used': 2,
'memory_mb': 1024,
'memory_mb_used': 512,
'local_gb': 1024,
'local_gb_used': 512}]}
instances = [{'project_id': 'project1',
'vcpus': 1,
'memory_mb': 128,
'root_gb': 128,
'ephemeral_gb': 0},
{'project_id': 'project1',
'vcpus': 2,
'memory_mb': 256,
'root_gb': 384,
'ephemeral_gb': 0},
{'project_id': 'project2',
'vcpus': 2,
'memory_mb': 256,
'root_gb': 256,
'ephemeral_gb': 0}]
self.mox.StubOutWithMock(db, 'service_get_by_compute_host')
self.mox.StubOutWithMock(db, 'instance_get_all_by_host')
db.service_get_by_compute_host(self.context, host).AndReturn(
compute_node)
db.instance_get_all_by_host(self.context, host).AndReturn(instances)
self.mox.ReplayAll()
result = self.manager.show_host_resources(self.context, host)
expected = {'usage': {'project1': {'memory_mb': 384,
'vcpus': 3,
'root_gb': 512,
'ephemeral_gb': 0},
'project2': {'memory_mb': 256,
'vcpus': 2,
'root_gb': 256,
'ephemeral_gb': 0}},
'resource': {'vcpus': 4,
'vcpus_used': 2,
'local_gb': 1024,
'local_gb_used': 512,
'memory_mb': 1024,
'memory_mb_used': 512}}
self.assertThat(result, matchers.DictMatches(expected))
def _mox_schedule_method_helper(self, method_name):
# Make sure the method exists that we're going to test call
def stub_method(*args, **kwargs):
pass
setattr(self.manager.driver, method_name, stub_method)
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": ""}
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]}
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,
mox.IsA(conductor_api.LocalAPI), 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_live_migration_schedule_novalidhost(self):
inst = {"uuid": "fake-instance-id",
"vm_state": vm_states.ACTIVE,
"task_state": task_states.MIGRATING, }
dest = None
block_migration = False
disk_over_commit = False
self.mox.StubOutWithMock(self.manager, '_schedule_live_migration')
self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc')
self.mox.StubOutWithMock(db, 'instance_update_and_get_original')
self.manager._schedule_live_migration(self.context,
inst, dest, block_migration, disk_over_commit).AndRaise(
exception.NoValidHost(reason=""))
db.instance_update_and_get_original(self.context, inst["uuid"],
{"vm_state": inst['vm_state'],
"task_state": None,
"expected_task_state": task_states.MIGRATING,
}).AndReturn((inst, inst))
compute_utils.add_instance_fault_from_exc(self.context,
mox.IsA(conductor_api.LocalAPI), inst,
mox.IsA(exception.NoValidHost),
mox.IgnoreArg())
self.mox.ReplayAll()
self.manager = utils.ExceptionHelper(self.manager)
self.assertRaises(exception.NoValidHost,
self.manager.live_migration,
self.context, inst, dest, block_migration,
disk_over_commit)
def test_live_migration_compute_service_notavailable(self):
inst = {"uuid": "fake-instance-id",
"vm_state": vm_states.ACTIVE,
"task_state": task_states.MIGRATING, }
dest = 'fake_host'
block_migration = False
disk_over_commit = False
self.mox.StubOutWithMock(self.manager, '_schedule_live_migration')
self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc')
self.mox.StubOutWithMock(db, 'instance_update_and_get_original')
self.manager._schedule_live_migration(self.context,
inst, dest, block_migration, disk_over_commit).AndRaise(
exception.ComputeServiceUnavailable(host="src"))
db.instance_update_and_get_original(self.context, inst["uuid"],
{"vm_state": inst['vm_state'],
"task_state": None,
"expected_task_state": task_states.MIGRATING,
}).AndReturn((inst, inst))
compute_utils.add_instance_fault_from_exc(self.context,
mox.IsA(conductor_api.LocalAPI), inst,
mox.IsA(exception.ComputeServiceUnavailable),
mox.IgnoreArg())
self.mox.ReplayAll()
self.manager = utils.ExceptionHelper(self.manager)
self.assertRaises(exception.ComputeServiceUnavailable,
self.manager.live_migration,
self.context, inst, dest, block_migration,
disk_over_commit)
def test_live_migrate(self):
instance = {'host': 'h'}
self.mox.StubOutClassWithMocks(live_migrate, "LiveMigrationTask")
task = live_migrate.LiveMigrationTask(self.context, instance,
"dest", "bm", "doc")
task.execute()
self.mox.ReplayAll()
self.manager.live_migration(self.context, instance, "dest",
"bm", "doc")
def test_live_migration_set_vmstate_error(self):
inst = {"uuid": "fake-instance-id",
"vm_state": vm_states.ACTIVE, }
dest = 'fake_host'
block_migration = False
disk_over_commit = False
self.mox.StubOutWithMock(self.manager, '_schedule_live_migration')
self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc')
self.mox.StubOutWithMock(db, 'instance_update_and_get_original')
self.manager._schedule_live_migration(self.context,
inst, dest, block_migration, disk_over_commit).AndRaise(
ValueError)
db.instance_update_and_get_original(self.context, inst["uuid"],
{"vm_state": vm_states.ERROR,
}).AndReturn((inst, inst))
compute_utils.add_instance_fault_from_exc(self.context,
mox.IsA(conductor_api.LocalAPI), inst,
mox.IsA(ValueError),
mox.IgnoreArg())
self.mox.ReplayAll()
self.manager = utils.ExceptionHelper(self.manager)
self.assertRaises(ValueError,
self.manager.live_migration,
self.context, inst, dest, block_migration,
disk_over_commit)
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,
mox.IsA(conductor_api.LocalAPI), 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,
mox.IsA(conductor_api.LocalAPI), 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,
mox.IsA(conductor_api.LocalAPI), 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('conductor', CONF.host).AndReturn(notifier)
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())
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_select_hosts_throws_rpc_clientexception(self):
self.mox.StubOutWithMock(self.manager.driver, 'select_destinations')
self.manager.driver.select_destinations(self.context, {}, {}).AndRaise(
exception.NoValidHost(reason=""))
self.mox.ReplayAll()
self.assertRaises(messaging.ExpectedException,
self.manager.select_hosts,
self.context, {}, {})
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(instance_obj.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'])
class SchedulerTestCase(test.NoDBTestCase):
"""Test case for base scheduler driver class."""
# So we can subclass this test and re-use tests if we need.
driver_cls = driver.Scheduler
def setUp(self):
super(SchedulerTestCase, self).setUp()
self.stubs.Set(compute_api, 'API', fakes.FakeComputeAPI)
def fake_show(meh, context, id):
if id:
return {'id': id, 'min_disk': None, 'min_ram': None,
'name': 'fake_name',
'status': 'active',
'properties': {'kernel_id': 'fake_kernel_id',
'ramdisk_id': 'fake_ramdisk_id',
'something_else': 'meow'}}
else:
raise exception.ImageNotFound(image_id=id)
fake_image.stub_out_image_service(self.stubs)
self.stubs.Set(fake_image._FakeImageService, 'show', fake_show)
self.image_service = glance.get_default_image_service()
self.driver = self.driver_cls()
self.context = context.RequestContext('fake_user', 'fake_project')
self.topic = 'fake_topic'
self.servicegroup_api = servicegroup.API()
def test_hosts_up(self):
service1 = {'host': 'host1'}
service2 = {'host': 'host2'}
services = [service1, service2]
self.mox.StubOutWithMock(db, 'service_get_all_by_topic')
self.mox.StubOutWithMock(servicegroup.API, 'service_is_up')
db.service_get_all_by_topic(self.context,
self.topic).AndReturn(services)
self.servicegroup_api.service_is_up(service1).AndReturn(False)
self.servicegroup_api.service_is_up(service2).AndReturn(True)
self.mox.ReplayAll()
result = self.driver.hosts_up(self.context, self.topic)
self.assertEqual(result, ['host2'])
def test_handle_schedule_error_adds_instance_fault(self):
instance = {'uuid': 'fake-uuid'}
self.mox.StubOutWithMock(db, 'instance_update_and_get_original')
self.mox.StubOutWithMock(db, 'instance_fault_create')
db.instance_update_and_get_original(self.context, instance['uuid'],
mox.IgnoreArg()).AndReturn(
(None, instance))
db.instance_fault_create(self.context, mox.IgnoreArg())
self.mox.StubOutWithMock(rpc, 'get_notifier')
notifier = self.mox.CreateMockAnything()
rpc.get_notifier('conductor', CONF.host).AndReturn(notifier)
rpc.get_notifier('scheduler').AndReturn(notifier)
notifier.error(self.context, 'scheduler.run_instance', mox.IgnoreArg())
self.mox.ReplayAll()
driver.handle_schedule_error(self.context,
exception.NoValidHost('test'),
instance['uuid'], {})
class SchedulerDriverBaseTestCase(SchedulerTestCase):
"""Test cases for base scheduler driver class methods
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, {}, {})
class SchedulerInstanceGroupData(test.TestCase):
driver_cls = driver.Scheduler
def setUp(self):
super(SchedulerInstanceGroupData, self).setUp()
self.user_id = 'fake_user'
self.project_id = 'fake_project'
self.context = context.RequestContext(self.user_id, self.project_id)
self.driver = self.driver_cls()
def _get_default_values(self):
return {'name': 'fake_name',
'user_id': self.user_id,
'project_id': self.project_id}
def _create_instance_group(self, context, values, policies=None,
metadata=None, members=None):
return db.instance_group_create(context, values, policies=policies,
metadata=metadata, members=members)
class SchedulerV3PassthroughTestCase(test.TestCase):
def setUp(self):
super(SchedulerV3PassthroughTestCase, self).setUp()
self.manager = manager.SchedulerManager()
self.proxy = manager._SchedulerManagerV3Proxy(self.manager)
def test_select_destination(self):
with mock.patch.object(self.manager, 'select_destinations'
) as select_destinations:
self.proxy.select_destinations(None, None, None)
select_destinations.assert_called_once()
def test_run_instance(self):
with mock.patch.object(self.manager, 'run_instance'
) as run_instance:
self.proxy.run_instance(None, None, None, None, None, None, None,
None)
run_instance.assert_called_once()
def test_prep_resize(self):
with mock.patch.object(self.manager, 'prep_resize'
) as prep_resize:
self.proxy.prep_resize(None, None, None, None, None, None, None)
prep_resize.assert_called_once()