207 lines
8.2 KiB
Python
207 lines
8.2 KiB
Python
# Copyright (c) 2012 Rackspace Hosting
|
|
# 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 CellsScheduler
|
|
"""
|
|
import time
|
|
|
|
from nova.compute import vm_states
|
|
from nova import context
|
|
from nova import db
|
|
from nova import exception
|
|
from nova.openstack.common import cfg
|
|
from nova.openstack.common import uuidutils
|
|
from nova import test
|
|
from nova.tests.cells import fakes
|
|
|
|
CONF = cfg.CONF
|
|
CONF.import_opt('scheduler_retries', 'nova.cells.scheduler', group='cells')
|
|
|
|
|
|
class CellsSchedulerTestCase(test.TestCase):
|
|
"""Test case for CellsScheduler class."""
|
|
|
|
def setUp(self):
|
|
super(CellsSchedulerTestCase, self).setUp()
|
|
fakes.init(self)
|
|
self.msg_runner = fakes.get_message_runner('api-cell')
|
|
self.scheduler = self.msg_runner.scheduler
|
|
self.state_manager = self.msg_runner.state_manager
|
|
self.my_cell_state = self.state_manager.get_my_state()
|
|
self.ctxt = context.RequestContext('fake', 'fake')
|
|
instance_uuids = []
|
|
for x in xrange(3):
|
|
instance_uuids.append(uuidutils.generate_uuid())
|
|
self.instance_uuids = instance_uuids
|
|
self.request_spec = {'instance_uuids': instance_uuids,
|
|
'other': 'stuff'}
|
|
|
|
def test_create_instances_here(self):
|
|
# Just grab the first instance type
|
|
inst_type = db.instance_type_get(self.ctxt, 1)
|
|
image = {'properties': {}}
|
|
instance_props = {'hostname': 'meow',
|
|
'display_name': 'moo',
|
|
'image_ref': 'fake_image_ref',
|
|
'user_id': self.ctxt.user_id,
|
|
'project_id': self.ctxt.project_id}
|
|
request_spec = {'instance_type': inst_type,
|
|
'image': image,
|
|
'security_group': ['default'],
|
|
'block_device_mapping': [],
|
|
'instance_properties': instance_props,
|
|
'instance_uuids': self.instance_uuids}
|
|
|
|
call_info = {'uuids': []}
|
|
|
|
def _fake_instance_update_at_top(_ctxt, instance):
|
|
call_info['uuids'].append(instance['uuid'])
|
|
|
|
self.stubs.Set(self.msg_runner, 'instance_update_at_top',
|
|
_fake_instance_update_at_top)
|
|
|
|
self.scheduler._create_instances_here(self.ctxt, request_spec)
|
|
self.assertEqual(self.instance_uuids, call_info['uuids'])
|
|
|
|
for instance_uuid in self.instance_uuids:
|
|
instance = db.instance_get_by_uuid(self.ctxt, instance_uuid)
|
|
self.assertEqual('meow', instance['hostname'])
|
|
self.assertEqual('moo', instance['display_name'])
|
|
self.assertEqual('fake_image_ref', instance['image_ref'])
|
|
|
|
def test_run_instance_selects_child_cell(self):
|
|
# Make sure there's no capacity info so we're sure to
|
|
# select a child cell
|
|
our_cell_info = self.state_manager.get_my_state()
|
|
our_cell_info.capacities = {}
|
|
|
|
call_info = {'times': 0}
|
|
|
|
orig_fn = self.msg_runner.schedule_run_instance
|
|
|
|
def msg_runner_schedule_run_instance(ctxt, target_cell,
|
|
host_sched_kwargs):
|
|
# This gets called twice. Once for our running it
|
|
# in this cell.. and then it'll get called when the
|
|
# child cell is picked. So, first time.. just run it
|
|
# like normal.
|
|
if not call_info['times']:
|
|
call_info['times'] += 1
|
|
return orig_fn(ctxt, target_cell, host_sched_kwargs)
|
|
call_info['ctxt'] = ctxt
|
|
call_info['target_cell'] = target_cell
|
|
call_info['host_sched_kwargs'] = host_sched_kwargs
|
|
|
|
self.stubs.Set(self.msg_runner, 'schedule_run_instance',
|
|
msg_runner_schedule_run_instance)
|
|
|
|
host_sched_kwargs = {'request_spec': self.request_spec}
|
|
self.msg_runner.schedule_run_instance(self.ctxt,
|
|
self.my_cell_state, host_sched_kwargs)
|
|
|
|
self.assertEqual(self.ctxt, call_info['ctxt'])
|
|
self.assertEqual(host_sched_kwargs, call_info['host_sched_kwargs'])
|
|
child_cells = self.state_manager.get_child_cells()
|
|
self.assertIn(call_info['target_cell'], child_cells)
|
|
|
|
def test_run_instance_selects_current_cell(self):
|
|
# Make sure there's no child cells so that we will be
|
|
# selected
|
|
self.state_manager.child_cells = {}
|
|
|
|
call_info = {}
|
|
|
|
def fake_create_instances_here(ctxt, request_spec):
|
|
call_info['ctxt'] = ctxt
|
|
call_info['request_spec'] = request_spec
|
|
|
|
def fake_rpc_run_instance(ctxt, **host_sched_kwargs):
|
|
call_info['host_sched_kwargs'] = host_sched_kwargs
|
|
|
|
self.stubs.Set(self.scheduler, '_create_instances_here',
|
|
fake_create_instances_here)
|
|
self.stubs.Set(self.scheduler.scheduler_rpcapi,
|
|
'run_instance', fake_rpc_run_instance)
|
|
|
|
host_sched_kwargs = {'request_spec': self.request_spec,
|
|
'other': 'stuff'}
|
|
self.msg_runner.schedule_run_instance(self.ctxt,
|
|
self.my_cell_state, host_sched_kwargs)
|
|
|
|
self.assertEqual(self.ctxt, call_info['ctxt'])
|
|
self.assertEqual(self.request_spec, call_info['request_spec'])
|
|
self.assertEqual(host_sched_kwargs, call_info['host_sched_kwargs'])
|
|
|
|
def test_run_instance_retries_when_no_cells_avail(self):
|
|
self.flags(scheduler_retries=7, group='cells')
|
|
|
|
host_sched_kwargs = {'request_spec': self.request_spec}
|
|
|
|
call_info = {'num_tries': 0, 'errored_uuids': []}
|
|
|
|
def fake_run_instance(message, host_sched_kwargs):
|
|
call_info['num_tries'] += 1
|
|
raise exception.NoCellsAvailable()
|
|
|
|
def fake_sleep(_secs):
|
|
return
|
|
|
|
def fake_instance_update(ctxt, instance_uuid, values):
|
|
self.assertEqual(vm_states.ERROR, values['vm_state'])
|
|
call_info['errored_uuids'].append(instance_uuid)
|
|
|
|
self.stubs.Set(self.scheduler, '_run_instance', fake_run_instance)
|
|
self.stubs.Set(time, 'sleep', fake_sleep)
|
|
self.stubs.Set(db, 'instance_update', fake_instance_update)
|
|
|
|
self.msg_runner.schedule_run_instance(self.ctxt,
|
|
self.my_cell_state, host_sched_kwargs)
|
|
|
|
self.assertEqual(8, call_info['num_tries'])
|
|
self.assertEqual(self.instance_uuids, call_info['errored_uuids'])
|
|
|
|
def test_run_instance_on_random_exception(self):
|
|
self.flags(scheduler_retries=7, group='cells')
|
|
|
|
host_sched_kwargs = {'request_spec': self.request_spec}
|
|
|
|
call_info = {'num_tries': 0,
|
|
'errored_uuids1': [],
|
|
'errored_uuids2': []}
|
|
|
|
def fake_run_instance(message, host_sched_kwargs):
|
|
call_info['num_tries'] += 1
|
|
raise test.TestingException()
|
|
|
|
def fake_instance_update(ctxt, instance_uuid, values):
|
|
self.assertEqual(vm_states.ERROR, values['vm_state'])
|
|
call_info['errored_uuids1'].append(instance_uuid)
|
|
|
|
def fake_instance_update_at_top(ctxt, instance):
|
|
self.assertEqual(vm_states.ERROR, instance['vm_state'])
|
|
call_info['errored_uuids2'].append(instance['uuid'])
|
|
|
|
self.stubs.Set(self.scheduler, '_run_instance', fake_run_instance)
|
|
self.stubs.Set(db, 'instance_update', fake_instance_update)
|
|
self.stubs.Set(self.msg_runner, 'instance_update_at_top',
|
|
fake_instance_update_at_top)
|
|
|
|
self.msg_runner.schedule_run_instance(self.ctxt,
|
|
self.my_cell_state, host_sched_kwargs)
|
|
# Shouldn't retry
|
|
self.assertEqual(1, call_info['num_tries'])
|
|
self.assertEqual(self.instance_uuids, call_info['errored_uuids1'])
|
|
self.assertEqual(self.instance_uuids, call_info['errored_uuids2'])
|