nova/nova/tests/unit/scheduler/test_scheduler.py

390 lines
19 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 oslo_messaging as messaging
from nova import context
from nova import objects
from nova.scheduler import caching_scheduler
from nova.scheduler import chance
from nova.scheduler import filter_scheduler
from nova.scheduler import host_manager
from nova.scheduler import manager
from nova import servicegroup
from nova import test
from nova.tests.unit import fake_server_actions
from nova.tests.unit.scheduler import fakes
from nova.tests import uuidsentinel as uuids
class SchedulerManagerInitTestCase(test.NoDBTestCase):
"""Test case for scheduler manager initiation."""
manager_cls = manager.SchedulerManager
@mock.patch.object(host_manager.HostManager, '_init_instance_info')
@mock.patch.object(host_manager.HostManager, '_init_aggregates')
def test_init_using_default_schedulerdriver(self,
mock_init_agg,
mock_init_inst):
driver = self.manager_cls().driver
self.assertIsInstance(driver, filter_scheduler.FilterScheduler)
@mock.patch.object(host_manager.HostManager, '_init_instance_info')
@mock.patch.object(host_manager.HostManager, '_init_aggregates')
def test_init_using_chance_schedulerdriver(self,
mock_init_agg,
mock_init_inst):
self.flags(driver='chance_scheduler', group='scheduler')
driver = self.manager_cls().driver
self.assertIsInstance(driver, chance.ChanceScheduler)
@mock.patch.object(host_manager.HostManager, '_init_instance_info')
@mock.patch.object(host_manager.HostManager, '_init_aggregates')
def test_init_using_caching_schedulerdriver(self,
mock_init_agg,
mock_init_inst):
self.flags(driver='caching_scheduler', group='scheduler')
driver = self.manager_cls().driver
self.assertIsInstance(driver, caching_scheduler.CachingScheduler)
@mock.patch.object(host_manager.HostManager, '_init_instance_info')
@mock.patch.object(host_manager.HostManager, '_init_aggregates')
def test_init_nonexist_schedulerdriver(self,
mock_init_agg,
mock_init_inst):
self.flags(driver='nonexist_scheduler', group='scheduler')
# The entry point has to be defined in setup.cfg and nova-scheduler has
# to be deployed again before using a custom value.
self.assertRaises(RuntimeError, self.manager_cls)
class SchedulerManagerTestCase(test.NoDBTestCase):
"""Test case for scheduler manager."""
manager_cls = manager.SchedulerManager
driver_cls = fakes.FakeScheduler
driver_plugin_name = 'fake_scheduler'
@mock.patch.object(host_manager.HostManager, '_init_instance_info')
@mock.patch.object(host_manager.HostManager, '_init_aggregates')
def setUp(self, mock_init_agg, mock_init_inst):
super(SchedulerManagerTestCase, self).setUp()
self.flags(driver=self.driver_plugin_name, group='scheduler')
with mock.patch.object(host_manager.HostManager, '_init_aggregates'):
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)
def test_1_correct_init(self):
# Correct scheduler driver
manager = self.manager
self.assertIsInstance(manager.driver, self.driver_cls)
@mock.patch('nova.scheduler.utils.resources_from_request_spec')
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'get_allocation_candidates')
def test_select_destination(self, mock_get_ac, mock_rfrs):
fake_spec = objects.RequestSpec()
fake_spec.instance_uuid = uuids.instance
fake_version = "9.42"
place_res = (fakes.ALLOC_REQS, mock.sentinel.p_sums, fake_version)
mock_get_ac.return_value = place_res
expected_alloc_reqs_by_rp_uuid = {
cn.uuid: [fakes.ALLOC_REQS[x]]
for x, cn in enumerate(fakes.COMPUTE_NODES)
}
with mock.patch.object(self.manager.driver, 'select_destinations'
) as select_destinations:
self.manager.select_destinations(self.context, spec_obj=fake_spec,
instance_uuids=[fake_spec.instance_uuid])
select_destinations.assert_called_once_with(
self.context, fake_spec,
[fake_spec.instance_uuid], expected_alloc_reqs_by_rp_uuid,
mock.sentinel.p_sums, fake_version, False)
mock_get_ac.assert_called_once_with(
self.context, mock_rfrs.return_value)
# Now call select_destinations() with True values for the params
# introduced in RPC version 4.5
select_destinations.reset_mock()
self.manager.select_destinations(None, spec_obj=fake_spec,
instance_uuids=[fake_spec.instance_uuid],
return_objects=True, return_alternates=True)
select_destinations.assert_called_once_with(None, fake_spec,
[fake_spec.instance_uuid], expected_alloc_reqs_by_rp_uuid,
mock.sentinel.p_sums, fake_version, True)
@mock.patch('nova.scheduler.utils.resources_from_request_spec')
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'get_allocation_candidates')
def test_select_destination_return_objects(self, mock_get_ac,
mock_rfrs):
fake_spec = objects.RequestSpec()
fake_spec.instance_uuid = uuids.instance
fake_version = "9.42"
place_res = (fakes.ALLOC_REQS, mock.sentinel.p_sums, fake_version)
mock_get_ac.return_value = place_res
expected_alloc_reqs_by_rp_uuid = {
cn.uuid: [fakes.ALLOC_REQS[x]]
for x, cn in enumerate(fakes.COMPUTE_NODES)
}
with mock.patch.object(self.manager.driver, 'select_destinations'
) as select_destinations:
sel_obj = objects.Selection(service_host="fake_host",
nodename="fake_node", compute_node_uuid=uuids.compute_node,
cell_uuid=uuids.cell, limits=None)
select_destinations.return_value = [[sel_obj]]
# Pass True; should get the Selection object back.
dests = self.manager.select_destinations(None, spec_obj=fake_spec,
instance_uuids=[fake_spec.instance_uuid],
return_objects=True, return_alternates=True)
sel_host = dests[0][0]
self.assertIsInstance(sel_host, objects.Selection)
# Since both return_objects and return_alternates are True, the
# driver should have been called with True for return_alternates.
select_destinations.assert_called_once_with(None, fake_spec,
[fake_spec.instance_uuid], expected_alloc_reqs_by_rp_uuid,
mock.sentinel.p_sums, fake_version, True)
# Now pass False for return objects, but keep return_alternates as
# True. Verify that the manager converted the Selection object back
# to a dict.
select_destinations.reset_mock()
dests = self.manager.select_destinations(None, spec_obj=fake_spec,
instance_uuids=[fake_spec.instance_uuid],
return_objects=False, return_alternates=True)
sel_host = dests[0]
self.assertIsInstance(sel_host, dict)
# Even though return_alternates was passed as True, since
# return_objects was False, the driver should have been called with
# return_alternates as False.
select_destinations.assert_called_once_with(None, fake_spec,
[fake_spec.instance_uuid], expected_alloc_reqs_by_rp_uuid,
mock.sentinel.p_sums, fake_version, False)
@mock.patch('nova.scheduler.utils.resources_from_request_spec')
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'get_allocation_candidates')
def _test_select_destination(self, get_allocation_candidates_response,
mock_get_ac, mock_rfrs):
fake_spec = objects.RequestSpec()
fake_spec.instance_uuid = uuids.instance
place_res = get_allocation_candidates_response
mock_get_ac.return_value = place_res
with mock.patch.object(self.manager.driver, 'select_destinations'
) as select_destinations:
self.assertRaises(messaging.rpc.dispatcher.ExpectedException,
self.manager.select_destinations, self.context,
spec_obj=fake_spec,
instance_uuids=[fake_spec.instance_uuid])
select_destinations.assert_not_called()
mock_get_ac.assert_called_once_with(
self.context, mock_rfrs.return_value)
def test_select_destination_old_placement(self):
"""Tests that we will raise NoValidhost when the scheduler
report client's get_allocation_candidates() returns None, None as it
would if placement service hasn't been upgraded before scheduler.
"""
place_res = (None, None, None)
self._test_select_destination(place_res)
def test_select_destination_placement_connect_fails(self):
"""Tests that we will raise NoValidHost when the scheduler
report client's get_allocation_candidates() returns None, which it
would if the connection to Placement failed and the safe_connect
decorator returns None.
"""
place_res = None
self._test_select_destination(place_res)
def test_select_destination_no_candidates(self):
"""Tests that we will raise NoValidHost when the scheduler
report client's get_allocation_candidates() returns [], {} which it
would if placement service hasn't yet had compute nodes populate
inventory.
"""
place_res = ([], {}, None)
self._test_select_destination(place_res)
@mock.patch('nova.scheduler.request_filter.process_reqspec')
@mock.patch('nova.scheduler.utils.resources_from_request_spec')
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'get_allocation_candidates')
def test_select_destination_is_rebuild(self, mock_get_ac, mock_rfrs,
mock_process):
fake_spec = objects.RequestSpec(
scheduler_hints={'_nova_check_type': ['rebuild']})
fake_spec.instance_uuid = uuids.instance
with mock.patch.object(self.manager.driver, 'select_destinations'
) as select_destinations:
self.manager.select_destinations(self.context, spec_obj=fake_spec,
instance_uuids=[fake_spec.instance_uuid])
select_destinations.assert_called_once_with(
self.context, fake_spec,
[fake_spec.instance_uuid], None, None, None, False)
mock_get_ac.assert_not_called()
mock_process.assert_not_called()
@mock.patch('nova.scheduler.utils.resources_from_request_spec')
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'get_allocation_candidates')
def test_select_destination_with_4_3_client(self, mock_get_ac, mock_rfrs):
fake_spec = objects.RequestSpec()
place_res = (fakes.ALLOC_REQS, mock.sentinel.p_sums, "42.0")
mock_get_ac.return_value = place_res
expected_alloc_reqs_by_rp_uuid = {
cn.uuid: [fakes.ALLOC_REQS[x]]
for x, cn in enumerate(fakes.COMPUTE_NODES)
}
with mock.patch.object(self.manager.driver, 'select_destinations'
) as select_destinations:
self.manager.select_destinations(self.context, spec_obj=fake_spec)
select_destinations.assert_called_once_with(self.context,
fake_spec, None, expected_alloc_reqs_by_rp_uuid,
mock.sentinel.p_sums, "42.0", False)
mock_get_ac.assert_called_once_with(
self.context, mock_rfrs.return_value)
# TODO(sbauza): Remove that test once the API v4 is removed
@mock.patch('nova.scheduler.utils.resources_from_request_spec')
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'get_allocation_candidates')
@mock.patch.object(objects.RequestSpec, 'from_primitives')
def test_select_destination_with_old_client(self, from_primitives,
mock_get_ac, mock_rfrs):
fake_spec = objects.RequestSpec()
fake_spec.instance_uuid = uuids.instance
from_primitives.return_value = fake_spec
place_res = (fakes.ALLOC_REQS, mock.sentinel.p_sums, "42.0")
mock_get_ac.return_value = place_res
expected_alloc_reqs_by_rp_uuid = {
cn.uuid: [fakes.ALLOC_REQS[x]]
for x, cn in enumerate(fakes.COMPUTE_NODES)
}
with mock.patch.object(self.manager.driver, 'select_destinations'
) as select_destinations:
self.manager.select_destinations(
self.context, request_spec='fake_spec',
filter_properties='fake_props',
instance_uuids=[fake_spec.instance_uuid])
select_destinations.assert_called_once_with(
self.context, fake_spec,
[fake_spec.instance_uuid], expected_alloc_reqs_by_rp_uuid,
mock.sentinel.p_sums, "42.0", False)
mock_get_ac.assert_called_once_with(
self.context, mock_rfrs.return_value)
def test_update_aggregates(self):
with mock.patch.object(self.manager.driver.host_manager,
'update_aggregates'
) as update_aggregates:
self.manager.update_aggregates(None, aggregates='agg')
update_aggregates.assert_called_once_with('agg')
def test_delete_aggregate(self):
with mock.patch.object(self.manager.driver.host_manager,
'delete_aggregate'
) as delete_aggregate:
self.manager.delete_aggregate(None, aggregate='agg')
delete_aggregate.assert_called_once_with('agg')
def test_update_instance_info(self):
with mock.patch.object(self.manager.driver.host_manager,
'update_instance_info') as mock_update:
self.manager.update_instance_info(mock.sentinel.context,
mock.sentinel.host_name,
mock.sentinel.instance_info)
mock_update.assert_called_once_with(mock.sentinel.context,
mock.sentinel.host_name,
mock.sentinel.instance_info)
def test_delete_instance_info(self):
with mock.patch.object(self.manager.driver.host_manager,
'delete_instance_info') as mock_delete:
self.manager.delete_instance_info(mock.sentinel.context,
mock.sentinel.host_name,
mock.sentinel.instance_uuid)
mock_delete.assert_called_once_with(mock.sentinel.context,
mock.sentinel.host_name,
mock.sentinel.instance_uuid)
def test_sync_instance_info(self):
with mock.patch.object(self.manager.driver.host_manager,
'sync_instance_info') as mock_sync:
self.manager.sync_instance_info(mock.sentinel.context,
mock.sentinel.host_name,
mock.sentinel.instance_uuids)
mock_sync.assert_called_once_with(mock.sentinel.context,
mock.sentinel.host_name,
mock.sentinel.instance_uuids)
def test_reset(self):
with mock.patch.object(self.manager.driver.host_manager,
'refresh_cells_caches') as mock_refresh:
self.manager.reset()
mock_refresh.assert_called_once_with()
@mock.patch('nova.objects.host_mapping.discover_hosts')
def test_discover_hosts(self, mock_discover):
cm1 = objects.CellMapping(name='cell1')
cm2 = objects.CellMapping(name='cell2')
mock_discover.return_value = [objects.HostMapping(host='a',
cell_mapping=cm1),
objects.HostMapping(host='b',
cell_mapping=cm2)]
self.manager._discover_hosts_in_cells(mock.sentinel.context)
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 = fakes.FakeScheduler
@mock.patch.object(host_manager.HostManager, '_init_instance_info')
@mock.patch.object(host_manager.HostManager, '_init_aggregates')
def setUp(self, mock_init_agg, mock_init_inst):
super(SchedulerTestCase, self).setUp()
self.driver = self.driver_cls()
self.context = context.RequestContext('fake_user', 'fake_project')
self.topic = 'fake_topic'
self.servicegroup_api = servicegroup.API()
@mock.patch('nova.objects.ServiceList.get_by_topic')
@mock.patch('nova.servicegroup.API.service_is_up')
def test_hosts_up(self, mock_service_is_up, mock_get_by_topic):
service1 = objects.Service(host='host1')
service2 = objects.Service(host='host2')
services = objects.ServiceList(objects=[service1, service2])
mock_get_by_topic.return_value = services
mock_service_is_up.side_effect = [False, True]
result = self.driver.hosts_up(self.context, self.topic)
self.assertEqual(result, ['host2'])
mock_get_by_topic.assert_called_once_with(self.context, self.topic)
calls = [mock.call(service1), mock.call(service2)]
self.assertEqual(calls, mock_service_is_up.call_args_list)