summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-07-01 17:30:40 +0000
committerGerrit Code Review <review@openstack.org>2013-07-01 17:30:40 +0000
commitdcece3c8e41f757a33251c5969f47fa36455c57e (patch)
tree02f08397c3235e931a1d7b11840361b4c0f1a289
parent3e232c84991edf15b2bcdb42885dcaff27b72206 (diff)
parent4f3c5411d92d488b78f06212912fc7ae6d225b79 (diff)
downloadnova-dcece3c8e41f757a33251c5969f47fa36455c57e.tar.gz
nova-dcece3c8e41f757a33251c5969f47fa36455c57e.tar.xz
nova-dcece3c8e41f757a33251c5969f47fa36455c57e.zip
Merge "New select_destinations scheduler call"
-rw-r--r--nova/scheduler/chance.py11
-rw-r--r--nova/scheduler/driver.py9
-rw-r--r--nova/scheduler/filter_scheduler.py16
-rw-r--r--nova/scheduler/manager.py13
-rw-r--r--nova/scheduler/rpcapi.py7
-rw-r--r--nova/scheduler/utils.py3
-rw-r--r--nova/tests/conductor/test_conductor.py3
-rw-r--r--nova/tests/scheduler/test_chance_scheduler.py45
-rw-r--r--nova/tests/scheduler/test_filter_scheduler.py55
-rw-r--r--nova/tests/scheduler/test_rpcapi.py6
-rw-r--r--nova/tests/scheduler/test_scheduler.py4
11 files changed, 170 insertions, 2 deletions
diff --git a/nova/scheduler/chance.py b/nova/scheduler/chance.py
index f0b1701e0..41a69d21e 100644
--- a/nova/scheduler/chance.py
+++ b/nova/scheduler/chance.py
@@ -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,
diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py
index c4265285f..765aede10 100644
--- a/nova/scheduler/driver.py
+++ b/nova/scheduler/driver.py
@@ -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")
diff --git a/nova/scheduler/filter_scheduler.py b/nova/scheduler/filter_scheduler.py
index 4e5662e65..103aec940 100644
--- a/nova/scheduler/filter_scheduler.py
+++ b/nova/scheduler/filter_scheduler.py
@@ -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):
diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py
index ea099ca2d..9d54a153e 100644
--- a/nova/scheduler/manager.py
+++ b/nova/scheduler/manager.py
@@ -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)
diff --git a/nova/scheduler/rpcapi.py b/nova/scheduler/rpcapi.py
index 369363bb4..9cd7a28b6 100644
--- a/nova/scheduler/rpcapi.py
+++ b/nova/scheduler/rpcapi.py
@@ -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):
diff --git a/nova/scheduler/utils.py b/nova/scheduler/utils.py
index d0a9d7ca5..eeb68f439 100644
--- a/nova/scheduler/utils.py
+++ b/nova/scheduler/utils.py
@@ -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)
diff --git a/nova/tests/conductor/test_conductor.py b/nova/tests/conductor/test_conductor.py
index 609c2164c..9d8725f66 100644
--- a/nova/tests/conductor/test_conductor.py
+++ b/nova/tests/conductor/test_conductor.py
@@ -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,
diff --git a/nova/tests/scheduler/test_chance_scheduler.py b/nova/tests/scheduler/test_chance_scheduler.py
index ba1701e93..4e52464ec 100644
--- a/nova/tests/scheduler/test_chance_scheduler.py
+++ b/nova/tests/scheduler/test_chance_scheduler.py
@@ -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, {})
diff --git a/nova/tests/scheduler/test_filter_scheduler.py b/nova/tests/scheduler/test_filter_scheduler.py
index ac2e73ec7..1aeab0408 100644
--- a/nova/tests/scheduler/test_filter_scheduler.py
+++ b/nova/tests/scheduler/test_filter_scheduler.py
@@ -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}, {})
diff --git a/nova/tests/scheduler/test_rpcapi.py b/nova/tests/scheduler/test_rpcapi.py
index cecc55f20..7d61ecf97 100644
--- a/nova/tests/scheduler/test_rpcapi.py
+++ b/nova/tests/scheduler/test_rpcapi.py
@@ -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')
diff --git a/nova/tests/scheduler/test_scheduler.py b/nova/tests/scheduler/test_scheduler.py
index 149c6d3c8..e2778a94b 100644
--- a/nova/tests/scheduler/test_scheduler.py
+++ b/nova/tests/scheduler/test_scheduler.py
@@ -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."""