Merge "New select_destinations scheduler call"
This commit is contained in:
commit
dcece3c8e4
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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, {})
|
||||
|
|
|
@ -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}, {})
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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."""
|
||||
|
|
Loading…
Reference in New Issue