Merge "New select_destinations scheduler call"

This commit is contained in:
Jenkins 2013-07-01 17:30:40 +00:00 committed by Gerrit Code Review
commit dcece3c8e4
11 changed files with 170 additions and 2 deletions

View File

@ -72,6 +72,17 @@ class ChanceScheduler(driver.Scheduler):
raise exception.NoValidHost(reason="")
return hosts
def select_destinations(self, context, request_spec, filter_properties):
"""Selects random destinations."""
num_instances = request_spec['num_instances']
# NOTE(alaski): Returns a list of tuples for compatibility with
# filter_scheduler
dests = [(self._schedule(context, CONF.compute_topic, request_spec,
filter_properties), None) for i in range(num_instances)]
if len(dests) < num_instances:
raise exception.NoValidHost(reason='')
return dests
def schedule_run_instance(self, context, request_spec,
admin_password, injected_files,
requested_networks, is_first_time,

View File

@ -158,6 +158,15 @@ class Scheduler(object):
msg = _("Driver must implement schedule_run_instance")
raise NotImplementedError(msg)
def select_destinations(self, context, request_spec, filter_properties):
"""Must override select_destinations method.
:return: A list of (host, node) tuples that satisifies the request_spec
and filter_properties.
"""
msg = _("Driver must implement select_destinations")
raise NotImplementedError(msg)
def select_hosts(self, context, request_spec, filter_properties):
"""Must override select_hosts method for scheduler to work."""
msg = _("Driver must implement select_hosts")

View File

@ -161,6 +161,22 @@ class FilterScheduler(driver.Scheduler):
raise exception.NoValidHost(reason="")
return hosts
def select_destinations(self, context, request_spec, filter_properties):
"""Selects a filtered set of hosts and nodes."""
num_instances = request_spec['num_instances']
instance_uuids = request_spec.get('instance_uuids')
selected_hosts = self._schedule(context, request_spec,
filter_properties, instance_uuids)
# Couldn't fulfill the request_spec
if len(selected_hosts) < num_instances:
raise exception.NoValidHost(reason='')
dests = []
for host in selected_hosts:
dests.append((host.obj.host, host.obj.nodename))
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):

View File

@ -57,7 +57,7 @@ QUOTAS = quota.QUOTAS
class SchedulerManager(manager.Manager):
"""Chooses a host to run instances on."""
RPC_API_VERSION = '2.6'
RPC_API_VERSION = '2.7'
def __init__(self, scheduler_driver=None, *args, **kwargs):
if not scheduler_driver:
@ -275,3 +275,14 @@ class SchedulerManager(manager.Manager):
hosts = self.driver.select_hosts(context, request_spec,
filter_properties)
return jsonutils.to_primitive(hosts)
@rpc_common.client_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 (host, nodename) tuples.
"""
dests = self.driver.select_destinations(context, request_spec,
filter_properties)
return jsonutils.to_primitive(dests)

View File

@ -67,6 +67,8 @@ class SchedulerAPI(nova.openstack.common.rpc.proxy.RpcProxy):
... Grizzly supports message version 2.6. So, any changes to existing
methods in 2.x after that point should be done such that they can
handle the version_cap being set to 2.6.
2.7 - Add select_destinations()
'''
#
@ -90,6 +92,11 @@ class SchedulerAPI(nova.openstack.common.rpc.proxy.RpcProxy):
default_version=self.BASE_RPC_API_VERSION,
version_cap=version_cap)
def select_destinations(self, ctxt, request_spec, filter_properties):
return self.call(ctxt, self.make_msg('select_destinations',
request_spec=request_spec, filter_properties=filter_properties),
version='2.7')
def run_instance(self, ctxt, request_spec, admin_password,
injected_files, requested_networks, is_first_time,
filter_properties):

View File

@ -44,6 +44,9 @@ def build_request_spec(ctxt, image, instances):
'image': image,
'instance_properties': instance,
'instance_type': instance_type,
'num_instances': len(instances),
# NOTE(alaski): This should be removed as logic moves from the
# scheduler to conductor. Provides backwards compatibility now.
'instance_uuids': [inst['uuid'] for inst in instances]}
return jsonutils.to_primitive(request_spec)

View File

@ -1251,7 +1251,8 @@ class _BaseTaskTestCase(object):
'instance_type': expected_instance_type,
'instance_uuids': ['fakeuuid', 'fakeuuid2'],
'block_device_mapping': 'block_device_mapping',
'security_group': 'security_groups'},
'security_group': 'security_groups',
'num_instances': 2},
admin_password='admin_password',
injected_files='injected_files',
requested_networks='requested_networks', is_first_time=True,

View File

@ -197,3 +197,48 @@ class ChanceSchedulerTestCase(test_scheduler.SchedulerTestCase):
self.stubs.Set(self.driver, '_schedule', _return_no_host)
self.assertRaises(exception.NoValidHost,
self.driver.select_hosts, self.context, {}, {})
def test_select_destinations(self):
ctxt = context.RequestContext('fake', 'fake', False)
ctxt_elevated = 'fake-context-elevated'
request_spec = {'num_instances': 2}
self.mox.StubOutWithMock(ctxt, 'elevated')
self.mox.StubOutWithMock(self.driver, 'hosts_up')
self.mox.StubOutWithMock(random, 'choice')
hosts_full = ['host1', 'host2', 'host3', 'host4']
ctxt.elevated().AndReturn(ctxt_elevated)
self.driver.hosts_up(ctxt_elevated, 'compute').AndReturn(hosts_full)
random.choice(hosts_full).AndReturn('host3')
ctxt.elevated().AndReturn(ctxt_elevated)
self.driver.hosts_up(ctxt_elevated, 'compute').AndReturn(hosts_full)
random.choice(hosts_full).AndReturn('host2')
self.mox.ReplayAll()
dests = self.driver.select_destinations(ctxt, request_spec, {})
self.assertEquals(2, len(dests))
(host, node) = dests[0]
self.assertEquals('host3', host)
self.assertEquals(None, node)
(host, node) = dests[1]
self.assertEquals('host2', host)
self.assertEquals(None, node)
def test_select_destinations_no_valid_host(self):
def _return_no_host(*args, **kwargs):
return []
self.mox.StubOutWithMock(self.driver, 'hosts_up')
self.driver.hosts_up(mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn([1, 2])
self.stubs.Set(self.driver, '_filter_hosts', _return_no_host)
self.mox.ReplayAll()
request_spec = {'num_instances': 1}
self.assertRaises(exception.NoValidHost,
self.driver.select_destinations, self.context,
request_spec, {})

View File

@ -609,3 +609,58 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
self.stubs.Set(self.driver, '_schedule', _return_no_host)
self.assertRaises(exception.NoValidHost,
self.driver.select_hosts, self.context, {}, {})
def test_select_destinations(self):
"""select_destinations is basically a wrapper around _schedule().
Similar to the _schedule tests, this just does a happy path test to
ensure there is nothing glaringly wrong.
"""
self.next_weight = 1.0
selected_hosts = []
selected_nodes = []
def _fake_weigh_objects(_self, functions, hosts, options):
self.next_weight += 2.0
host_state = hosts[0]
selected_hosts.append(host_state.host)
selected_nodes.append(host_state.nodename)
return [weights.WeighedHost(host_state, self.next_weight)]
sched = fakes.FakeFilterScheduler()
fake_context = context.RequestContext('user', 'project',
is_admin=True)
self.stubs.Set(sched.host_manager, 'get_filtered_hosts',
fake_get_filtered_hosts)
self.stubs.Set(weights.HostWeightHandler,
'get_weighed_objects', _fake_weigh_objects)
fakes.mox_host_manager_db_calls(self.mox, fake_context)
request_spec = {'instance_type': {'memory_mb': 512, 'root_gb': 512,
'ephemeral_gb': 0,
'vcpus': 1},
'instance_properties': {'project_id': 1,
'root_gb': 512,
'memory_mb': 512,
'ephemeral_gb': 0,
'vcpus': 1,
'os_type': 'Linux'},
'num_instances': 1}
self.mox.ReplayAll()
dests = sched.select_destinations(fake_context, request_spec, {})
(host, node) = dests[0]
self.assertEquals(host, selected_hosts[0])
self.assertEquals(node, selected_nodes[0])
def test_select_destinations_no_valid_host(self):
def _return_no_host(*args, **kwargs):
return []
self.stubs.Set(self.driver, '_schedule', _return_no_host)
self.assertRaises(exception.NoValidHost,
self.driver.select_destinations, self.context,
{'num_instances': 1}, {})

View File

@ -86,3 +86,9 @@ class SchedulerRpcAPITestCase(test.NoDBTestCase):
request_spec='fake_request_spec',
filter_properties='fake_prop',
version='2.6')
def test_select_destinations(self):
self._test_scheduler_api('select_destinations', rpc_method='call',
request_spec='fake_request_spec',
filter_properties='fake_prop',
version='2.7')

View File

@ -527,6 +527,10 @@ class SchedulerDriverBaseTestCase(SchedulerTestCase):
self.assertRaises(NotImplementedError,
self.driver.select_hosts, self.context, {}, {})
def test_unimplemented_select_destinations(self):
self.assertRaises(NotImplementedError,
self.driver.select_destinations, self.context, {}, {})
class SchedulerDriverModuleTestCase(test.NoDBTestCase):
"""Test case for scheduler driver module methods."""