summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVishvananda Ishaya <vishvananda@yahoo.com>2010-09-02 11:32:37 -0700
committerVishvananda Ishaya <vishvananda@yahoo.com>2010-09-02 11:32:37 -0700
commit7edff9298f7f01e158f90c93432384903d71e033 (patch)
treedd605a7de945ad360fe2842777100b879304bb6d
parente7eb9443bc07a173b5885f634c80c616d9d59e9b (diff)
scheduler + unittests
-rwxr-xr-xbin/nova-scheduler4
-rw-r--r--nova/db/api.py13
-rw-r--r--nova/db/sqlalchemy/api.py31
-rw-r--r--nova/endpoint/cloud.py6
-rw-r--r--nova/flags.py2
-rw-r--r--nova/scheduler/base.py80
-rw-r--r--nova/scheduler/bestfit.py34
-rw-r--r--nova/scheduler/chance.py37
-rw-r--r--nova/scheduler/driver.py62
-rw-r--r--nova/scheduler/manager.py60
-rw-r--r--nova/scheduler/service.py76
-rw-r--r--nova/scheduler/simple.py81
-rw-r--r--nova/tests/compute_unittest.py2
-rw-r--r--nova/tests/scheduler_unittest.py103
-rw-r--r--run_tests.py1
15 files changed, 390 insertions, 202 deletions
diff --git a/bin/nova-scheduler b/bin/nova-scheduler
index 1ad41bbd3..97f98b17f 100755
--- a/bin/nova-scheduler
+++ b/bin/nova-scheduler
@@ -21,12 +21,12 @@
Twistd daemon for the nova scheduler nodes.
"""
+from nova import service
from nova import twistd
-from nova.scheduler import service
if __name__ == '__main__':
twistd.serve(__file__)
if __name__ == '__builtin__':
- application = service.SchedulerService.create()
+ application = service.Service.create()
diff --git a/nova/db/api.py b/nova/db/api.py
index 6cb49b7e4..07eebd017 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -62,6 +62,19 @@ def daemon_get_by_args(context, host, binary):
return IMPL.daemon_get_by_args(context, host, binary)
+def daemon_get_all_by_topic(context, topic):
+ """Get all compute daemons for a given topi """
+ return IMPL.daemon_get_all_by_topic(context, topic)
+
+
+def daemon_get_all_compute_sorted(context):
+ """Get all compute daemons sorted by instance count
+
+ Returns a list of (Daemon, instance_count) tuples
+ """
+ return IMPL.daemon_get_all_compute_sorted(context)
+
+
def daemon_create(context, values):
"""Create a daemon from the values dictionary."""
return IMPL.daemon_create(context, values)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 5d98ee5bf..aabd74984 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -25,6 +25,7 @@ from nova import flags
from nova.db.sqlalchemy import models
from nova.db.sqlalchemy.session import managed_session
from sqlalchemy import or_
+from sqlalchemy.sql import func
FLAGS = flags.FLAGS
@@ -43,6 +44,36 @@ def daemon_get_by_args(_context, host, binary):
return models.Daemon.find_by_args(host, binary)
+def daemon_get_all_by_topic(context, topic):
+ with managed_session() as session:
+ return session.query(models.Daemon) \
+ .filter_by(deleted=False) \
+ .filter_by(topic=topic) \
+ .all()
+
+
+def daemon_get_all_compute_sorted(_context):
+ with managed_session() as session:
+ # NOTE(vish): The intended query is below
+ # SELECT daemons.*, inst_count.instance_count
+ # FROM daemons LEFT OUTER JOIN
+ # (SELECT host, count(*) AS instance_count
+ # FROM instances GROUP BY host) AS inst_count
+ print 'instance', models.Instance.find(1).host
+ subq = session.query(models.Instance.host,
+ func.count('*').label('instance_count')) \
+ .filter_by(deleted=False) \
+ .group_by(models.Instance.host) \
+ .subquery()
+ topic = 'compute'
+ return session.query(models.Daemon, subq.c.instance_count) \
+ .filter_by(topic=topic) \
+ .filter_by(deleted=False) \
+ .outerjoin((subq, models.Daemon.host == subq.c.host)) \
+ .order_by(subq.c.instance_count) \
+ .all()
+
+
def daemon_create(_context, values):
daemon_ref = models.Daemon()
for (key, value) in values.iteritems():
diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py
index 4e86145db..2c88ef406 100644
--- a/nova/endpoint/cloud.py
+++ b/nova/endpoint/cloud.py
@@ -488,9 +488,9 @@ class CloudController(object):
host = db.network_get_host(context, network_ref['id'])
if not host:
host = yield rpc.call(FLAGS.network_topic,
- {"method": "set_network_host",
- "args": {"context": None,
- "project_id": context.project.id}})
+ {"method": "set_network_host",
+ "args": {"context": None,
+ "project_id": context.project.id}})
defer.returnValue(db.queue_get_for(context, FLAGS.network_topic, host))
@rbac.allow('projectmanager', 'sysadmin')
diff --git a/nova/flags.py b/nova/flags.py
index aa9648843..40ce9c736 100644
--- a/nova/flags.py
+++ b/nova/flags.py
@@ -220,5 +220,7 @@ DEFINE_string('network_manager', 'nova.network.manager.VlanManager',
'Manager for network')
DEFINE_string('volume_manager', 'nova.volume.manager.AOEManager',
'Manager for volume')
+DEFINE_string('scheduler_manager', 'nova.scheduler.manager.SchedulerManager',
+ 'Manager for scheduler')
diff --git a/nova/scheduler/base.py b/nova/scheduler/base.py
deleted file mode 100644
index 2872ae6fe..000000000
--- a/nova/scheduler/base.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright (c) 2010 Openstack, LLC.
-#
-# 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.
-
-"""
-Scheduler base class that all Schedulers should inherit from
-"""
-
-import time
-
-from nova import flags
-from nova.datastore import Redis
-
-FLAGS = flags.FLAGS
-flags.DEFINE_integer('node_down_time',
- 60,
- 'seconds without heartbeat that determines a '
- 'compute node to be down')
-
-
-class Scheduler(object):
- """
- The base class that all Scheduler clases should inherit from
- """
-
- @staticmethod
- def compute_nodes():
- """
- Return a list of compute nodes
- """
-
- return [identifier.split(':')[0]
- for identifier in Redis.instance().smembers("daemons")
- if (identifier.split(':')[1] == "nova-compute")]
-
- @staticmethod
- def compute_node_is_up(node):
- """
- Given a node name, return whether the node is considered 'up' by
- if it's sent a heartbeat recently
- """
-
- time_str = Redis.instance().hget('%s:%s:%s' %
- ('daemon', node, 'nova-compute'),
- 'updated_at')
- if not time_str:
- return False
-
- # Would be a lot easier if we stored heartbeat time in epoch :)
-
- # The 'str()' here is to get rid of a pylint error
- time_str = str(time_str).replace('Z', 'UTC')
- time_split = time.strptime(time_str, '%Y-%m-%dT%H:%M:%S%Z')
- epoch_time = int(time.mktime(time_split)) - time.timezone
- return (time.time() - epoch_time) < FLAGS.node_down_time
-
- def compute_nodes_up(self):
- """
- Return the list of compute nodes that are considered 'up'
- """
-
- return [node for node in self.compute_nodes()
- if self.compute_node_is_up(node)]
-
- def pick_node(self, instance_id, **_kwargs):
- """You DEFINITELY want to define this in your subclass"""
-
- raise NotImplementedError("Your subclass should define pick_node")
diff --git a/nova/scheduler/bestfit.py b/nova/scheduler/bestfit.py
deleted file mode 100644
index bdd4fcbdc..000000000
--- a/nova/scheduler/bestfit.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright (c) 2010 Openstack, LLC.
-#
-# 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.
-
-"""
-Best Fit Scheduler
-"""
-
-from nova.scheduler.base import Scheduler
-
-
-class BestFitScheduler(Scheduler):
- """
- Implements Scheduler as a best-fit node selector
- """
-
- def pick_node(self, instance_id, **_kwargs):
- """
- Picks a node that is up and is a best fit for the new instance
- """
-
- raise NotImplementedError("BestFitScheduler is not done yet")
diff --git a/nova/scheduler/chance.py b/nova/scheduler/chance.py
index 719c37674..12321cec1 100644
--- a/nova/scheduler/chance.py
+++ b/nova/scheduler/chance.py
@@ -1,6 +1,9 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2010 Openstack, LLC.
+# 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
@@ -20,18 +23,40 @@ Chance (Random) Scheduler implementation
import random
-from nova.scheduler.base import Scheduler
+from nova.scheduler import driver
-class ChanceScheduler(Scheduler):
+class ChanceScheduler(driver.Scheduler):
"""
Implements Scheduler as a random node selector
"""
- def pick_node(self, instance_id, **_kwargs):
+ def pick_compute_host(self, context, instance_id, **_kwargs):
"""
- Picks a node that is up at random
+ Picks a host that is up at random
"""
- nodes = self.compute_nodes_up()
- return nodes[int(random.random() * len(nodes))]
+ hosts = self.hosts_up(context, 'compute')
+ if not hosts:
+ raise driver.NoValidHost("No hosts found")
+ return hosts[int(random.random() * len(hosts))]
+
+ def pick_volume_host(self, context, volume_id, **_kwargs):
+ """
+ Picks a host that is up at random
+ """
+
+ hosts = self.hosts_up(context, 'volume')
+ if not hosts:
+ raise driver.NoValidHost("No hosts found")
+ return hosts[int(random.random() * len(hosts))]
+
+ def pick_network_host(self, context, network_id, **_kwargs):
+ """
+ Picks a host that is up at random
+ """
+
+ hosts = self.hosts_up(context, 'network')
+ if not hosts:
+ raise driver.NoValidHost("No hosts found")
+ return hosts[int(random.random() * len(hosts))]
diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py
new file mode 100644
index 000000000..1618342c0
--- /dev/null
+++ b/nova/scheduler/driver.py
@@ -0,0 +1,62 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2010 Openstack, LLC.
+# 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.
+
+"""
+Scheduler base class that all Schedulers should inherit from
+"""
+
+import datetime
+
+from nova import db
+from nova import exception
+from nova import flags
+
+FLAGS = flags.FLAGS
+flags.DEFINE_integer('daemon_down_time',
+ 60,
+ 'seconds without heartbeat that determines a '
+ 'compute node to be down')
+
+class NoValidHost(exception.Error):
+ """There is no valid host for the command"""
+ pass
+
+class Scheduler(object):
+ """
+ The base class that all Scheduler clases should inherit from
+ """
+
+ @staticmethod
+ def daemon_is_up(daemon):
+ """
+ Given a daemon, return whether the deamon is considered 'up' by
+ if it's sent a heartbeat recently
+ """
+ elapsed = datetime.datetime.now() - daemon['updated_at']
+ return elapsed < datetime.timedelta(seconds=FLAGS.daemon_down_time)
+
+ def hosts_up(self, context, topic):
+ """
+ Return the list of hosts that have a running daemon for topic
+ """
+
+ daemons = db.daemon_get_all_by_topic(context, topic)
+ return [daemon.host
+ for daemon in daemons
+ if self.daemon_is_up(daemon)]
diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py
new file mode 100644
index 000000000..a75b4ac41
--- /dev/null
+++ b/nova/scheduler/manager.py
@@ -0,0 +1,60 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2010 Openstack, LLC.
+# 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.
+
+"""
+Scheduler Service
+"""
+
+import logging
+
+from nova import db
+from nova import flags
+from nova import manager
+from nova import rpc
+from nova import utils
+
+FLAGS = flags.FLAGS
+flags.DEFINE_string('scheduler_driver',
+ 'nova.scheduler.chance.ChanceScheduler',
+ 'Driver to use for the scheduler')
+
+
+class SchedulerManager(manager.Manager):
+ """
+ Chooses a host to run instances on.
+ """
+ def __init__(self, scheduler_driver=None, *args, **kwargs):
+ if not scheduler_driver:
+ scheduler_driver = FLAGS.scheduler_driver
+ self.driver = utils.import_object(scheduler_driver)
+ super(SchedulerManager, self).__init__(*args, **kwargs)
+
+ def run_instance(self, context, instance_id, **_kwargs):
+ """
+ Picks a node for a running VM and casts the run_instance request
+ """
+
+ host = self.driver.pick_host(context, instance_id, **_kwargs)
+
+ rpc.cast(db.queue_get_for(context, FLAGS.compute_topic, host),
+ {"method": "run_instance",
+ "args": {"context": context,
+ "instance_id": instance_id}})
+ logging.debug("Casting to compute %s for running instance %s",
+ host, instance_id)
diff --git a/nova/scheduler/service.py b/nova/scheduler/service.py
deleted file mode 100644
index 136f262c2..000000000
--- a/nova/scheduler/service.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright (c) 2010 Openstack, LLC.
-#
-# 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.
-
-"""
-Scheduler Service
-"""
-
-import logging
-from twisted.internet import defer
-
-from nova import exception
-from nova import flags
-from nova import rpc
-from nova import service
-from nova.scheduler import chance
-from nova.scheduler import bestfit
-
-FLAGS = flags.FLAGS
-flags.DEFINE_string('scheduler_type',
- 'chance',
- 'the scheduler to use')
-
-SCHEDULER_CLASSES = {'chance': chance.ChanceScheduler,
- 'bestfit': bestfit.BestFitScheduler}
-
-
-class SchedulerService(service.Service):
- """
- Manages the running instances.
- """
-
- def __init__(self):
- super(SchedulerService, self).__init__()
- if (FLAGS.scheduler_type not in SCHEDULER_CLASSES):
- raise exception.Error("Scheduler '%s' does not exist" %
- FLAGS.scheduler_type)
- self._scheduler_class = SCHEDULER_CLASSES[FLAGS.scheduler_type]
-
- @staticmethod
- def noop():
- """ simple test of an AMQP message call """
- return defer.succeed('PONG')
-
- def pick_node(self, instance_id, **_kwargs):
- """
- Return a node to use based on the selected Scheduler
- """
-
- return self._scheduler_class().pick_node(instance_id, **_kwargs)
-
- @exception.wrap_exception
- def run_instance(self, instance_id, **_kwargs):
- """
- Picks a node for a running VM and casts the run_instance request
- """
-
- node = self.pick_node(instance_id, **_kwargs)
-
- rpc.cast('%s.%s' % (FLAGS.compute_topic, node),
- {"method": "run_instance",
- "args": {"instance_id": instance_id}})
- logging.debug("Casting to node %s for running instance %s",
- node, instance_id)
diff --git a/nova/scheduler/simple.py b/nova/scheduler/simple.py
new file mode 100644
index 000000000..6c76fd322
--- /dev/null
+++ b/nova/scheduler/simple.py
@@ -0,0 +1,81 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2010 Openstack, LLC.
+# 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.
+
+"""
+Simple Scheduler
+"""
+
+from nova import db
+from nova import flags
+from nova.scheduler import driver
+
+FLAGS = flags.FLAGS
+flags.DEFINE_integer("max_instances", 16,
+ "maximum number of instances to allow per host")
+flags.DEFINE_integer("max_volumes", 100,
+ "maximum number of volumes to allow per host")
+flags.DEFINE_integer("max_networks", 1000,
+ "maximum number of networks to allow per host")
+
+class SimpleScheduler(driver.Scheduler):
+ """
+ Implements Naive Scheduler that tries to find least loaded host
+ """
+
+ def pick_compute_host(self, context, instance_id, **_kwargs):
+ """
+ Picks a host that is up and has the fewest running instances
+ """
+
+ results = db.daemon_get_all_compute_sorted(context)
+ for result in results:
+ (daemon, instance_count) = result
+ if instance_count >= FLAGS.max_instances:
+ raise driver.NoValidHost("All hosts have too many instances")
+ if self.daemon_is_up(daemon):
+ return daemon['host']
+ raise driver.NoValidHost("No hosts found")
+
+ def pick_volume_host(self, context, volume_id, **_kwargs):
+ """
+ Picks a host that is up and has the fewest volumes
+ """
+
+ results = db.daemon_get_all_volume_sorted(context)
+ for result in results:
+ (daemon, instance_count) = result
+ if instance_count >= FLAGS.max_volumes:
+ raise driver.NoValidHost("All hosts have too many volumes")
+ if self.daemon_is_up(daemon):
+ return daemon['host']
+ raise driver.NoValidHost("No hosts found")
+
+ def pick_network_host(self, context, network_id, **_kwargs):
+ """
+ Picks a host that is up and has the fewest networks
+ """
+
+ results = db.daemon_get_all_network_sorted(context)
+ for result in results:
+ (daemon, instance_count) = result
+ if instance_count >= FLAGS.max_networks:
+ raise driver.NoValidHost("All hosts have too many networks")
+ if self.daemon_is_up(daemon):
+ return daemon['host']
+ raise driver.NoValidHost("No hosts found")
diff --git a/nova/tests/compute_unittest.py b/nova/tests/compute_unittest.py
index 867b572f3..23013e4c7 100644
--- a/nova/tests/compute_unittest.py
+++ b/nova/tests/compute_unittest.py
@@ -61,7 +61,7 @@ class ComputeTestCase(test.TrialTestCase):
inst['instance_type'] = 'm1.tiny'
inst['mac_address'] = utils.generate_mac()
inst['ami_launch_index'] = 0
- return db.instance_create(None, inst)
+ return db.instance_create(self.context, inst)
@defer.inlineCallbacks
def test_run_terminate(self):
diff --git a/nova/tests/scheduler_unittest.py b/nova/tests/scheduler_unittest.py
new file mode 100644
index 000000000..d3616dd6f
--- /dev/null
+++ b/nova/tests/scheduler_unittest.py
@@ -0,0 +1,103 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# 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 logging
+
+from twisted.internet import defer
+
+from nova import db
+from nova import flags
+from nova import service
+from nova import test
+from nova import utils
+from nova.auth import manager as auth_manager
+from nova.scheduler import manager
+
+
+FLAGS = flags.FLAGS
+
+
+class SchedulerTestCase(test.TrialTestCase):
+ """Test case for scheduler"""
+ def setUp(self): # pylint: disable-msg=C0103
+ super(SchedulerTestCase, self).setUp()
+ self.flags(connection_type='fake',
+ scheduler_driver='nova.scheduler.simple.SimpleScheduler')
+ self.scheduler = manager.SchedulerManager()
+ self.context = None
+ self.manager = auth_manager.AuthManager()
+ self.user = self.manager.create_user('fake', 'fake', 'fake')
+ self.project = self.manager.create_project('fake', 'fake', 'fake')
+ self.context = None
+
+ def tearDown(self): # pylint: disable-msg=C0103
+ self.manager.delete_user(self.user)
+ self.manager.delete_project(self.project)
+
+ def _create_instance(self):
+ """Create a test instance"""
+ inst = {}
+ inst['image_id'] = 'ami-test'
+ inst['reservation_id'] = 'r-fakeres'
+ inst['launch_time'] = '10'
+ inst['user_id'] = self.user.id
+ inst['project_id'] = self.project.id
+ inst['instance_type'] = 'm1.tiny'
+ inst['mac_address'] = utils.generate_mac()
+ inst['ami_launch_index'] = 0
+ return db.instance_create(self.context, inst)
+
+ def test_hosts_are_up(self):
+ # NOTE(vish): constructing service without create method
+ # because we are going to use it without queue
+ service1 = service.Service('host1',
+ 'nova-compute',
+ 'compute',
+ FLAGS.compute_manager)
+ service2 = service.Service('host2',
+ 'nova-compute',
+ 'compute',
+ FLAGS.compute_manager)
+ service1.report_state()
+ service2.report_state()
+ hosts = self.scheduler.driver.hosts_up(self.context, 'compute')
+ self.assertEqual(len(hosts), 2)
+
+ def test_least_busy_host_gets_instance(self):
+ # NOTE(vish): constructing service without create method
+ # because we are going to use it without queue
+ service1 = service.Service('host1',
+ 'nova-compute',
+ 'compute',
+ FLAGS.compute_manager)
+ service2 = service.Service('host2',
+ 'nova-compute',
+ 'compute',
+ FLAGS.compute_manager)
+ service1.report_state()
+ service2.report_state()
+ instance_id = self._create_instance()
+ FLAGS.host = 'host1'
+ service1.run_instance(self.context,
+ instance_id)
+ print type(self.scheduler.driver)
+ host = self.scheduler.driver.pick_compute_host(self.context,
+ instance_id)
+ self.assertEqual(host, 'host2')
diff --git a/run_tests.py b/run_tests.py
index c47cbe2ec..5d76a74ca 100644
--- a/run_tests.py
+++ b/run_tests.py
@@ -60,6 +60,7 @@ from nova.tests.network_unittest import *
from nova.tests.objectstore_unittest import *
from nova.tests.process_unittest import *
from nova.tests.rpc_unittest import *
+from nova.tests.scheduler_unittest import *
from nova.tests.service_unittest import *
from nova.tests.validator_unittest import *
from nova.tests.volume_unittest import *