From 2a6ce075e19af5700960e3fb22c213e43a2e24b4 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 3 Mar 2011 16:28:04 -0400 Subject: start of fanout --- nova/rpc.py | 20 ++++++++++++++++++++ nova/scheduler/manager.py | 4 ++++ nova/scheduler/zone_manager.py | 4 ++++ nova/service.py | 6 ++++++ 4 files changed, 34 insertions(+) diff --git a/nova/rpc.py b/nova/rpc.py index 8fe4565dd..a02cdc90c 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -218,6 +218,16 @@ class TopicPublisher(Publisher): super(TopicPublisher, self).__init__(connection=connection) +class FanoutPublisher(Publisher): + """Publishes messages to a fanout exchange.""" + exchange_type = "fanout" + + def __init__(self, topic, connection=None): + self.exchange = "%s_fanout" % topic + self.durable = False + super(FanoutPublisher, self).__init__(connection=connection) + + class DirectConsumer(Consumer): """Consumes messages directly on a channel specified by msg_id""" exchange_type = "direct" @@ -360,6 +370,16 @@ def cast(context, topic, msg): publisher.close() +def fanout_cast(context, topic, msg): + """Sends a message on a fanout exchange without waiting for a response""" + LOG.debug(_("Making asynchronous fanout cast...")) + _pack_context(msg, context) + conn = Connection.instance() + publisher = FanoutPublisher(topic, connection=conn) + publisher.send(msg) + publisher.close() + + def generic_response(message_data, message): """Logs a result and exits""" LOG.debug(_('response %s'), message_data) diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index c94397210..7541523b0 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -59,6 +59,10 @@ class SchedulerManager(manager.Manager): """Get a list of zones from the ZoneManager.""" return self.zone_manager.get_zone_list() + def update_compute_capabilities(self, context=None): + """Process a compute node update.""" + return self.zone_manager.update_compute_capabilities() + def _schedule(self, method, context, topic, *args, **kwargs): """Tries to call schedule_* method on the driver to retrieve host. diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index edf9000cc..eedc5c235 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -105,6 +105,7 @@ class ZoneManager(object): def __init__(self): self.last_zone_db_check = datetime.min self.zone_states = {} + self.compute_states = {} self.green_pool = greenpool.GreenPool() def get_zone_list(self): @@ -141,3 +142,6 @@ class ZoneManager(object): self.last_zone_db_check = datetime.now() self._refresh_from_db(context) self._poll_zones(context) + + def update_compute_capabilities(self): + logging.debug(_("****** UPDATE COMPUTE CAPABILITIES *******")) diff --git a/nova/service.py b/nova/service.py index f47358089..3ecf46525 100644 --- a/nova/service.py +++ b/nova/service.py @@ -84,6 +84,7 @@ class Service(object): conn1 = rpc.Connection.instance(new=True) conn2 = rpc.Connection.instance(new=True) + conn3 = rpc.Connection.instance(new=True) if self.report_interval: consumer_all = rpc.AdapterConsumer( connection=conn1, @@ -93,9 +94,14 @@ class Service(object): connection=conn2, topic='%s.%s' % (self.topic, self.host), proxy=self) + fanout = rpc.AdapterConsumer( + connection=conn2, + topic='%s_fanout' % self.topic, + proxy=self) self.timers.append(consumer_all.attach_to_eventlet()) self.timers.append(consumer_node.attach_to_eventlet()) + self.timers.append(fanout.attach_to_eventlet()) pulse = utils.LoopingCall(self.report_state) pulse.start(interval=self.report_interval, now=False) -- cgit From 3d9d99a53d372abf9f69f1d6e66fa6c6491ec752 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 3 Mar 2011 19:27:15 -0400 Subject: tests passing --- nova/tests/test_service.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nova/tests/test_service.py b/nova/tests/test_service.py index 45d9afa6c..cb31a3c43 100644 --- a/nova/tests/test_service.py +++ b/nova/tests/test_service.py @@ -120,6 +120,12 @@ class ServiceTestCase(test.TestCase): proxy=mox.IsA(service.Service)).AndReturn( rpc.AdapterConsumer) + rpc.AdapterConsumer(connection=mox.IgnoreArg(), + topic='%s_fanout' % topic, + proxy=mox.IsA(service.Service)).AndReturn( + rpc.AdapterConsumer) + + rpc.AdapterConsumer.attach_to_eventlet() rpc.AdapterConsumer.attach_to_eventlet() rpc.AdapterConsumer.attach_to_eventlet() -- cgit From a10d863e5e6127b8e987719ddfb60142b9f08db9 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Sun, 20 Feb 2011 13:36:45 -0800 Subject: scheduler manager --- nova/scheduler_manager.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 nova/scheduler_manager.py diff --git a/nova/scheduler_manager.py b/nova/scheduler_manager.py new file mode 100644 index 000000000..c78b6fea5 --- /dev/null +++ b/nova/scheduler_manager.py @@ -0,0 +1,39 @@ +# Copyright 2011 OpenStack, LLC. +# 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. + +""" +This module provides SchedulerDependentManager, a base class for +any Manager that has Capabilities that should be related to the +Scheduler. + +These Capabilities are hints that can help the scheduler route +requests to the appropriate service instance. +""" + +from nova import manager +from nova.scheduler import api + + +FLAGS = flags.FLAGS + + +def SchedulerDependentManage(manager.Manager): + def __init__(self, host=None, db_driver=None): + self.last_capabilities = {} + super(SchedulerDependentManager, self).__init__(host, db_driver) + + def periodic_tasks(self, context=None): + """Pass data back to the scheduler at a periodic interval""" + logging.debug(_("Notifying Schedulers of capabilities ...")) -- cgit From 5d821114fc20c88e36f079089cfe655d8188914a Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Sun, 20 Feb 2011 16:40:08 -0800 Subject: service ping working --- nova/compute/manager.py | 4 ++-- nova/scheduler_manager.py | 9 +++++++-- nova/service.py | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 3af97683f..fd88158f7 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -44,7 +44,7 @@ import functools from nova import exception from nova import flags from nova import log as logging -from nova import manager +from nova import scheduler_manager from nova import rpc from nova import utils from nova.compute import power_state @@ -99,7 +99,7 @@ def checks_instance_lock(function): return decorated_function -class ComputeManager(manager.Manager): +class ComputeManager(scheduler_manager.SchedulerDependentManager): """Manages the running instances from creation to destruction.""" diff --git a/nova/scheduler_manager.py b/nova/scheduler_manager.py index c78b6fea5..987ca8e90 100644 --- a/nova/scheduler_manager.py +++ b/nova/scheduler_manager.py @@ -22,18 +22,23 @@ These Capabilities are hints that can help the scheduler route requests to the appropriate service instance. """ +import sys + +from nova import flags from nova import manager from nova.scheduler import api +from nova import log as logging FLAGS = flags.FLAGS -def SchedulerDependentManage(manager.Manager): +class SchedulerDependentManager(manager.Manager): def __init__(self, host=None, db_driver=None): self.last_capabilities = {} super(SchedulerDependentManager, self).__init__(host, db_driver) def periodic_tasks(self, context=None): """Pass data back to the scheduler at a periodic interval""" - logging.debug(_("Notifying Schedulers of capabilities ...")) + logging.debug(_("*** Notifying Schedulers of capabilities ...")) + super(SchedulerDependentManager, self).periodic_tasks(context) diff --git a/nova/service.py b/nova/service.py index 3ecf46525..66287b505 100644 --- a/nova/service.py +++ b/nova/service.py @@ -95,7 +95,7 @@ class Service(object): topic='%s.%s' % (self.topic, self.host), proxy=self) fanout = rpc.AdapterConsumer( - connection=conn2, + connection=conn3, topic='%s_fanout' % self.topic, proxy=self) -- cgit From 7297e6bf1536f20540200f28154c15d63372d943 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Sun, 20 Feb 2011 20:58:59 -0800 Subject: fanout kinda working --- nova/rpc.py | 51 ++++++++++++++++++++++++++++++----------------- nova/scheduler/api.py | 7 +++++++ nova/scheduler/manager.py | 2 +- nova/scheduler_manager.py | 2 ++ nova/service.py | 8 ++++---- 5 files changed, 47 insertions(+), 23 deletions(-) diff --git a/nova/rpc.py b/nova/rpc.py index a02cdc90c..e0cf6d301 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -137,24 +137,7 @@ class Consumer(messaging.Consumer): return timer -class Publisher(messaging.Publisher): - """Publisher base class""" - pass - - -class TopicConsumer(Consumer): - """Consumes messages on a specific topic""" - exchange_type = "topic" - - def __init__(self, connection=None, topic="broadcast"): - self.queue = topic - self.routing_key = topic - self.exchange = FLAGS.control_exchange - self.durable = False - super(TopicConsumer, self).__init__(connection=connection) - - -class AdapterConsumer(TopicConsumer): +class AdapterConsumer(Consumer): """Calls methods on a proxy object based on method and args""" def __init__(self, connection=None, topic="broadcast", proxy=None): LOG.debug(_('Initing the Adapter Consumer for %s') % topic) @@ -207,6 +190,37 @@ class AdapterConsumer(TopicConsumer): return +class Publisher(messaging.Publisher): + """Publisher base class""" + pass + + +class TopicAdapterConsumer(AdapterConsumer): + """Consumes messages on a specific topic""" + exchange_type = "topic" + + def __init__(self, connection=None, topic="broadcast", proxy=None): + self.queue = topic + self.routing_key = topic + self.exchange = FLAGS.control_exchange + self.durable = False + super(TopicAdapterConsumer, self).__init__(connection=connection, + topic=topic, proxy=proxy) + + +class FanoutAdapterConsumer(AdapterConsumer): + """Consumes messages from a fanout exchange""" + exchange_type = "fanout" + + def __init__(self, connection=None, topic="broadcast", proxy=None): + self.exchange = "%s_fanout" % topic + self.routing_key = topic + self.queue = "ignored" + self.durable = False + super(FanoutAdapterConsumer, self).__init__(connection=connection, + topic=topic, proxy=proxy) + + class TopicPublisher(Publisher): """Publishes messages on a specific topic""" exchange_type = "topic" @@ -214,6 +228,7 @@ class TopicPublisher(Publisher): def __init__(self, connection=None, topic="broadcast"): self.routing_key = topic self.exchange = FLAGS.control_exchange + self.queue = "ignored" self.durable = False super(TopicPublisher, self).__init__(connection=connection) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index 8491bf3a9..53d72507f 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -47,3 +47,10 @@ class API: for item in items: item['api_url'] = item['api_url'].replace('\\/', '/') return items + + @classmethod + def update_service_capabilities(cls, context, service_name, capabilities): + kwargs = dict(method='update_service_capabilities', + service_name=service_name, capabilities=capabilities) + return rpc.fanout_cast(context, 'scheduler', kwargs) + diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index 7541523b0..3ff43a9d9 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -59,7 +59,7 @@ class SchedulerManager(manager.Manager): """Get a list of zones from the ZoneManager.""" return self.zone_manager.get_zone_list() - def update_compute_capabilities(self, context=None): + def update_service_capabilities(self, context=None, service_name=None, capabilities={}): """Process a compute node update.""" return self.zone_manager.update_compute_capabilities() diff --git a/nova/scheduler_manager.py b/nova/scheduler_manager.py index 987ca8e90..a45301617 100644 --- a/nova/scheduler_manager.py +++ b/nova/scheduler_manager.py @@ -41,4 +41,6 @@ class SchedulerDependentManager(manager.Manager): def periodic_tasks(self, context=None): """Pass data back to the scheduler at a periodic interval""" logging.debug(_("*** Notifying Schedulers of capabilities ...")) + api.API.update_service_capabilities(context, 'compute', self.last_capabilities) + super(SchedulerDependentManager, self).periodic_tasks(context) diff --git a/nova/service.py b/nova/service.py index 66287b505..a98d6aac1 100644 --- a/nova/service.py +++ b/nova/service.py @@ -86,17 +86,17 @@ class Service(object): conn2 = rpc.Connection.instance(new=True) conn3 = rpc.Connection.instance(new=True) if self.report_interval: - consumer_all = rpc.AdapterConsumer( + consumer_all = rpc.TopicAdapterConsumer( connection=conn1, topic=self.topic, proxy=self) - consumer_node = rpc.AdapterConsumer( + consumer_node = rpc.TopicAdapterConsumer( connection=conn2, topic='%s.%s' % (self.topic, self.host), proxy=self) - fanout = rpc.AdapterConsumer( + fanout = rpc.FanoutAdapterConsumer( connection=conn3, - topic='%s_fanout' % self.topic, + topic=self.topic, proxy=self) self.timers.append(consumer_all.attach_to_eventlet()) -- cgit From 32f062c389a530b6af3f864eb4030a68d0a26eb1 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Sun, 20 Feb 2011 22:33:39 -0800 Subject: fanout works --- nova/rpc.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/nova/rpc.py b/nova/rpc.py index e0cf6d301..601e45b47 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -215,8 +215,12 @@ class FanoutAdapterConsumer(AdapterConsumer): def __init__(self, connection=None, topic="broadcast", proxy=None): self.exchange = "%s_fanout" % topic self.routing_key = topic - self.queue = "ignored" + unique = uuid.uuid4().hex + self.queue = "%s_fanout_%s" % (topic, unique) self.durable = False + LOG.info(_("Created '%(exchange)s' fanout exchange " + "with '%(key)s' routing key"), + dict(exchange=self.exchange, key=self.routing_key)) super(FanoutAdapterConsumer, self).__init__(connection=connection, topic=topic, proxy=proxy) @@ -228,7 +232,6 @@ class TopicPublisher(Publisher): def __init__(self, connection=None, topic="broadcast"): self.routing_key = topic self.exchange = FLAGS.control_exchange - self.queue = "ignored" self.durable = False super(TopicPublisher, self).__init__(connection=connection) @@ -239,7 +242,10 @@ class FanoutPublisher(Publisher): def __init__(self, topic, connection=None): self.exchange = "%s_fanout" % topic + self.queue = "%s_fanout" % topic self.durable = False + LOG.info(_("Writing to '%(exchange)s' fanout exchange"), + dict(exchange=self.exchange)) super(FanoutPublisher, self).__init__(connection=connection) -- cgit From 05065a72ab06879d8ddd48ab45bc870386a0562d Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 23 Feb 2011 14:41:11 -0800 Subject: tests working again --- nova/tests/test_rpc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/tests/test_rpc.py b/nova/tests/test_rpc.py index 4820e04fb..44d7c91eb 100644 --- a/nova/tests/test_rpc.py +++ b/nova/tests/test_rpc.py @@ -36,7 +36,7 @@ class RpcTestCase(test.TestCase): super(RpcTestCase, self).setUp() self.conn = rpc.Connection.instance(True) self.receiver = TestReceiver() - self.consumer = rpc.AdapterConsumer(connection=self.conn, + self.consumer = rpc.TopicAdapterConsumer(connection=self.conn, topic='test', proxy=self.receiver) self.consumer.attach_to_eventlet() @@ -97,7 +97,7 @@ class RpcTestCase(test.TestCase): nested = Nested() conn = rpc.Connection.instance(True) - consumer = rpc.AdapterConsumer(connection=conn, + consumer = rpc.TopicAdapterConsumer(connection=conn, topic='nested', proxy=nested) consumer.attach_to_eventlet() -- cgit From 05fc3ea219f36bc1c246179b25b1feb017888b01 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 23 Feb 2011 17:58:32 -0800 Subject: Tests all working again --- nova/compute/manager.py | 3 ++- nova/flags.py | 2 +- nova/network/manager.py | 7 ++++--- nova/scheduler/api.py | 1 - nova/scheduler/manager.py | 3 ++- nova/scheduler_manager.py | 22 ++++++++++++++++++---- nova/tests/test_service.py | 25 ++++++++++++++----------- nova/tests/test_test.py | 2 +- nova/volume/manager.py | 7 ++++--- 9 files changed, 46 insertions(+), 26 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index fd88158f7..b307ffa59 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -112,7 +112,8 @@ class ComputeManager(scheduler_manager.SchedulerDependentManager): self.driver = utils.import_object(compute_driver) self.network_manager = utils.import_object(FLAGS.network_manager) self.volume_manager = utils.import_object(FLAGS.volume_manager) - super(ComputeManager, self).__init__(*args, **kwargs) + super(ComputeManager, self).__init__(service_name="compute", + *args, **kwargs) def init_host(self): """Do any initialization that needs to be run if this is a diff --git a/nova/flags.py b/nova/flags.py index 6f37c82f0..7036180fc 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -356,5 +356,5 @@ DEFINE_string('node_availability_zone', 'nova', 'availability zone of this node') DEFINE_string('zone_name', 'nova', 'name of this zone') -DEFINE_string('zone_capabilities', 'kypervisor:xenserver;os:linux', +DEFINE_string('zone_capabilities', 'hypervisor:xenserver;os:linux', 'Key/Value tags which represent capabilities of this zone') diff --git a/nova/network/manager.py b/nova/network/manager.py index b36dd59cf..f5f5b17aa 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -55,7 +55,7 @@ from nova import db from nova import exception from nova import flags from nova import log as logging -from nova import manager +from nova import scheduler_manager from nova import utils from nova import rpc @@ -105,7 +105,7 @@ class AddressAlreadyAllocated(exception.Error): pass -class NetworkManager(manager.Manager): +class NetworkManager(scheduler_manager.SchedulerDependentManager): """Implements common network manager functionality. This class must be subclassed to support specific topologies. @@ -116,7 +116,8 @@ class NetworkManager(manager.Manager): if not network_driver: network_driver = FLAGS.network_driver self.driver = utils.import_object(network_driver) - super(NetworkManager, self).__init__(*args, **kwargs) + super(NetworkManager, self).__init__(service_name='network', + *args, **kwargs) def init_host(self): """Do any initialization that needs to be run if this is a diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index 53d72507f..6a6bfc032 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -53,4 +53,3 @@ class API: kwargs = dict(method='update_service_capabilities', service_name=service_name, capabilities=capabilities) return rpc.fanout_cast(context, 'scheduler', kwargs) - diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index 3ff43a9d9..6b5c6e246 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -59,7 +59,8 @@ class SchedulerManager(manager.Manager): """Get a list of zones from the ZoneManager.""" return self.zone_manager.get_zone_list() - def update_service_capabilities(self, context=None, service_name=None, capabilities={}): + def update_service_capabilities(self, context=None, service_name=None, + capabilities={}): """Process a compute node update.""" return self.zone_manager.update_compute_capabilities() diff --git a/nova/scheduler_manager.py b/nova/scheduler_manager.py index a45301617..65bd71c05 100644 --- a/nova/scheduler_manager.py +++ b/nova/scheduler_manager.py @@ -34,13 +34,27 @@ FLAGS = flags.FLAGS class SchedulerDependentManager(manager.Manager): - def __init__(self, host=None, db_driver=None): - self.last_capabilities = {} + + """Periodically send capability updates to the Scheduler services. + Services that need to update the Scheduler of their capabilities + should derive from this class. Otherwise they can derive from + manager.Manager directly. Updates are only sent after + update_service_capabilities is called with non-None values.""" + + def __init__(self, host=None, db_driver=None, service_name="undefined"): + self.last_capabilities = None + self.service_name = service_name super(SchedulerDependentManager, self).__init__(host, db_driver) + def update_service_capabilities(self, capabilities): + """Remember these capabilities to send on next periodic update.""" + self.last_capabilities = capabilities + def periodic_tasks(self, context=None): """Pass data back to the scheduler at a periodic interval""" - logging.debug(_("*** Notifying Schedulers of capabilities ...")) - api.API.update_service_capabilities(context, 'compute', self.last_capabilities) + if self.last_capabilities: + logging.debug(_("*** Notifying Schedulers of capabilities ...")) + api.API.update_service_capabilities(context, self.service_name, + self.last_capabilities) super(SchedulerDependentManager, self).periodic_tasks(context) diff --git a/nova/tests/test_service.py b/nova/tests/test_service.py index cb31a3c43..b006caadd 100644 --- a/nova/tests/test_service.py +++ b/nova/tests/test_service.py @@ -108,26 +108,29 @@ class ServiceTestCase(test.TestCase): app = service.Service.create(host=host, binary=binary) self.mox.StubOutWithMock(rpc, - 'AdapterConsumer', + 'TopicAdapterConsumer', use_mock_anything=True) - rpc.AdapterConsumer(connection=mox.IgnoreArg(), + self.mox.StubOutWithMock(rpc, + 'FanoutAdapterConsumer', + use_mock_anything=True) + rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(), topic=topic, proxy=mox.IsA(service.Service)).AndReturn( - rpc.AdapterConsumer) + rpc.TopicAdapterConsumer) - rpc.AdapterConsumer(connection=mox.IgnoreArg(), + rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(), topic='%s.%s' % (topic, host), proxy=mox.IsA(service.Service)).AndReturn( - rpc.AdapterConsumer) + rpc.TopicAdapterConsumer) - rpc.AdapterConsumer(connection=mox.IgnoreArg(), - topic='%s_fanout' % topic, + rpc.FanoutAdapterConsumer(connection=mox.IgnoreArg(), + topic=topic, proxy=mox.IsA(service.Service)).AndReturn( - rpc.AdapterConsumer) + rpc.FanoutAdapterConsumer) - rpc.AdapterConsumer.attach_to_eventlet() - rpc.AdapterConsumer.attach_to_eventlet() - rpc.AdapterConsumer.attach_to_eventlet() + rpc.TopicAdapterConsumer.attach_to_eventlet() + rpc.TopicAdapterConsumer.attach_to_eventlet() + rpc.FanoutAdapterConsumer.attach_to_eventlet() service_create = {'host': host, 'binary': binary, diff --git a/nova/tests/test_test.py b/nova/tests/test_test.py index e237674e6..35c838065 100644 --- a/nova/tests/test_test.py +++ b/nova/tests/test_test.py @@ -34,7 +34,7 @@ class IsolationTestCase(test.TestCase): def test_rpc_consumer_isolation(self): connection = rpc.Connection.instance(new=True) - consumer = rpc.TopicConsumer(connection, topic='compute') + consumer = rpc.TopicAdapterConsumer(connection, topic='compute') consumer.register_callback( lambda x, y: self.fail('I should never be called')) consumer.attach_to_eventlet() diff --git a/nova/volume/manager.py b/nova/volume/manager.py index 3e8bc16b3..c53acf1e3 100644 --- a/nova/volume/manager.py +++ b/nova/volume/manager.py @@ -49,7 +49,7 @@ from nova import context from nova import exception from nova import flags from nova import log as logging -from nova import manager +from nova import scheduler_manager from nova import utils @@ -64,14 +64,15 @@ flags.DEFINE_boolean('use_local_volumes', True, 'if True, will not discover local volumes') -class VolumeManager(manager.Manager): +class VolumeManager(scheduler_manager.SchedulerDependentManager): """Manages attachable block storage devices.""" def __init__(self, volume_driver=None, *args, **kwargs): """Load the driver from the one specified in args, or from flags.""" if not volume_driver: volume_driver = FLAGS.volume_driver self.driver = utils.import_object(volume_driver) - super(VolumeManager, self).__init__(*args, **kwargs) + super(VolumeManager, self).__init__(service_name='volume', + *args, **kwargs) # NOTE(vish): Implementation specific db handling is done # by the driver. self.driver.db = self.db -- cgit From 65b9dfbea28f1607ef471e78b73ba77183d943f6 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 24 Feb 2011 01:53:01 -0800 Subject: capability aggregation working --- nova/api/openstack/zones.py | 15 ++++++++++++--- nova/scheduler/api.py | 15 +++++++++++++-- nova/scheduler/manager.py | 12 +++++++++--- nova/scheduler/zone_manager.py | 35 ++++++++++++++++++++++++++++++++--- nova/scheduler_manager.py | 6 ++---- 5 files changed, 68 insertions(+), 15 deletions(-) diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index 989b3a235..c6c27dd4b 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -15,9 +15,10 @@ import common +from nova import db from nova import flags +from nova import log as logging from nova import wsgi -from nova import db from nova.scheduler import api @@ -67,8 +68,16 @@ class Controller(wsgi.Controller): def info(self, req): """Return name and capabilities for this zone.""" - return dict(zone=dict(name=FLAGS.zone_name, - capabilities=FLAGS.zone_capabilities)) + items = api.API().get_zone_capabilities(req.environ['nova.context']) + + zone = dict(name=FLAGS.zone_name) + caps = FLAGS.zone_capabilities.split(';') + for cap in caps: + key_values = cap.split(':') + zone[key_values[0]] = key_values[1] + for item, (min_value, max_value) in items.iteritems(): + zone[item] = "%s,%s" % (min_value, max_value) + return dict(zone=zone) def show(self, req, id): """Return data about the given zone id""" diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index 6a6bfc032..ac38350ed 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -43,13 +43,24 @@ class API: return rpc.call(context, queue, kwargs) def get_zone_list(self, context): + """Return a list of zones assoicated with this zone.""" items = self._call_scheduler('get_zone_list', context) for item in items: item['api_url'] = item['api_url'].replace('\\/', '/') return items + def get_zone_capabilities(self, context, service=None): + """Returns a dict of key, value capabilities for this zone, + or for a particular class of services running in this zone.""" + return self._call_scheduler('get_zone_capabilities', context=context, + params=dict(service=service)) + @classmethod - def update_service_capabilities(cls, context, service_name, capabilities): + def update_service_capabilities(cls, context, service_name, host, + capabilities): + """Send an update to all the scheduler services informing them + of the capabilities of this service.""" kwargs = dict(method='update_service_capabilities', - service_name=service_name, capabilities=capabilities) + args=dict(service_name=service_name, host=host, + capabilities=capabilities)) return rpc.fanout_cast(context, 'scheduler', kwargs) diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index 6b5c6e246..1bda77d89 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -59,10 +59,16 @@ class SchedulerManager(manager.Manager): """Get a list of zones from the ZoneManager.""" return self.zone_manager.get_zone_list() + def get_zone_capabilities(self, context=None, service=None): + """Get the normalized set of capabilites for this zone, + or for a particular service.""" + return self.zone_manager.get_zone_capabilities(context, service) + def update_service_capabilities(self, context=None, service_name=None, - capabilities={}): - """Process a compute node update.""" - return self.zone_manager.update_compute_capabilities() + host=None, capabilities={}): + """Process a capability update from a service node.""" + self.zone_manager.update_service_capabilities(service_name, + host, capabilities) def _schedule(self, method, context, topic, *args, **kwargs): """Tries to call schedule_* method on the driver to retrieve host. diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index eedc5c235..09c9811f3 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -105,13 +105,37 @@ class ZoneManager(object): def __init__(self): self.last_zone_db_check = datetime.min self.zone_states = {} - self.compute_states = {} + self.service_states = {} # { : { : { cap k : v }}} self.green_pool = greenpool.GreenPool() def get_zone_list(self): """Return the list of zones we know about.""" return [zone.to_dict() for zone in self.zone_states.values()] + def get_zone_capabilities(self, context, service=None): + """Roll up all the individual host info to generic 'service' + capabilities. Each capability is aggregated into + _min and _max values.""" + service_dict = self.service_states + if service: + service_dict = dict(service_name=service, + hosts=self.service_states.get(service, {})) + + # TODO(sandy) - be smarter about fabricating this structure. + # But it's likely to change once we understand what the Best-Match + # code will need better. + combined = {} # { _ : (min, max), ... } + for service_name, host_dict in service_dict.iteritems(): + for host, caps_dict in host_dict.iteritems(): + for cap, value in caps_dict.iteritems(): + key = "%s_%s" % (service_name, cap) + min_value, max_value = combined.get(key, (value, value)) + min_value = min(min_value, value) + max_value = max(max_value, value) + combined[key] = (min_value, max_value) + + return combined + def _refresh_from_db(self, context): """Make our zone state map match the db.""" # Add/update existing zones ... @@ -143,5 +167,10 @@ class ZoneManager(object): self._refresh_from_db(context) self._poll_zones(context) - def update_compute_capabilities(self): - logging.debug(_("****** UPDATE COMPUTE CAPABILITIES *******")) + def update_service_capabilities(self, service_name, host, capabilities): + """Update the per-service capabilities based on this notification.""" + logging.debug(_("Received %(service_name)s service update from " + "%(host)s: %(capabilities)s") % locals()) + service_caps = self.service_states.get(service_name, {}) + service_caps[host] = capabilities + self.service_states[service_name] = service_caps diff --git a/nova/scheduler_manager.py b/nova/scheduler_manager.py index 65bd71c05..ca39b85dd 100644 --- a/nova/scheduler_manager.py +++ b/nova/scheduler_manager.py @@ -34,13 +34,11 @@ FLAGS = flags.FLAGS class SchedulerDependentManager(manager.Manager): - """Periodically send capability updates to the Scheduler services. Services that need to update the Scheduler of their capabilities should derive from this class. Otherwise they can derive from manager.Manager directly. Updates are only sent after update_service_capabilities is called with non-None values.""" - def __init__(self, host=None, db_driver=None, service_name="undefined"): self.last_capabilities = None self.service_name = service_name @@ -53,8 +51,8 @@ class SchedulerDependentManager(manager.Manager): def periodic_tasks(self, context=None): """Pass data back to the scheduler at a periodic interval""" if self.last_capabilities: - logging.debug(_("*** Notifying Schedulers of capabilities ...")) + logging.debug(_("Notifying Schedulers of capabilities ...")) api.API.update_service_capabilities(context, self.service_name, - self.last_capabilities) + self.host, self.last_capabilities) super(SchedulerDependentManager, self).periodic_tasks(context) -- cgit From c8df2602fd8f4f2cb7716e6283f3779c6895a479 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 24 Feb 2011 14:32:25 -0800 Subject: service capabilities test --- nova/scheduler/driver.py | 7 +++++++ nova/scheduler/manager.py | 3 ++- nova/tests/test_zones.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py index 66e46c1b9..317a039cc 100644 --- a/nova/scheduler/driver.py +++ b/nova/scheduler/driver.py @@ -45,6 +45,13 @@ class WillNotSchedule(exception.Error): class Scheduler(object): """The base class that all Scheduler clases should inherit from.""" + def __init__(self): + self.zone_manager = None + + def set_zone_manager(self, zone_manager): + """Called by the Scheduler Service to supply a ZoneManager.""" + self.zone_manager = zone_manager + @staticmethod def service_is_up(service): """Check whether a service is up based on last heartbeat.""" diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index 1bda77d89..d3d338943 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -41,10 +41,11 @@ flags.DEFINE_string('scheduler_driver', class SchedulerManager(manager.Manager): """Chooses a host to run instances on.""" def __init__(self, scheduler_driver=None, *args, **kwargs): + self.zone_manager = zone_manager.ZoneManager() if not scheduler_driver: scheduler_driver = FLAGS.scheduler_driver self.driver = utils.import_object(scheduler_driver) - self.zone_manager = zone_manager.ZoneManager() + self.driver.set_zone_manager(self.zone_manager) super(SchedulerManager, self).__init__(*args, **kwargs) def __getattr__(self, key): diff --git a/nova/tests/test_zones.py b/nova/tests/test_zones.py index 5a52a0506..3ca71d5f1 100644 --- a/nova/tests/test_zones.py +++ b/nova/tests/test_zones.py @@ -76,6 +76,34 @@ class ZoneManagerTestCase(test.TestCase): self.assertEquals(len(zm.zone_states), 1) self.assertEquals(zm.zone_states[1].username, 'user1') + def test_service_capabilities(self): + zm = zone_manager.ZoneManager() + caps = zm.get_zone_capabilities(self, None) + self.assertEquals(caps, {}) + + zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) + caps = zm.get_zone_capabilities(self, None) + self.assertEquals(caps, dict(svc1_a=(1, 1), svc1_b=(2, 2))) + + zm.update_service_capabilities("svc1", "host1", dict(a=2, b=3)) + caps = zm.get_zone_capabilities(self, None) + self.assertEquals(caps, dict(svc1_a=(2, 2), svc1_b=(3, 3))) + + zm.update_service_capabilities("svc1", "host2", dict(a=20, b=30)) + caps = zm.get_zone_capabilities(self, None) + self.assertEquals(caps, dict(svc1_a=(2, 20), svc1_b=(3, 30))) + + zm.update_service_capabilities("svc10", "host1", dict(a=99, b=99)) + caps = zm.get_zone_capabilities(self, None) + self.assertEquals(caps, dict(svc1_a=(2, 20), svc1_b=(3, 30), + svc10_a=(99, 99), svc10_b=(99, 99))) + + zm.update_service_capabilities("svc1", "host3", dict(c=5)) + caps = zm.get_zone_capabilities(self, None) + self.assertEquals(caps, dict(svc1_a=(2, 20), svc1_b=(3, 30), + svc1_c=(5, 5), svc10_a=(99, 99), + svc10_b=(99, 99))) + def test_refresh_from_db_replace_existing(self): zm = zone_manager.ZoneManager() zone_state = zone_manager.ZoneState() -- cgit From 47bbfaab52642f3ff79bcdefb8d705fb02b549f9 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 24 Feb 2011 15:23:15 -0800 Subject: new tests --- nova/api/openstack/zones.py | 4 ++-- nova/scheduler/api.py | 37 ++++++++++++++++++---------------- nova/scheduler/zone_manager.py | 3 +-- nova/tests/api/openstack/test_zones.py | 30 ++++++++++++++++++++++++--- nova/tests/test_zones.py | 6 ++++++ 5 files changed, 56 insertions(+), 24 deletions(-) diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index c6c27dd4b..fecbd6fa3 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -53,7 +53,7 @@ class Controller(wsgi.Controller): """Return all zones in brief""" # Ask the ZoneManager in the Scheduler for most recent data, # or fall-back to the database ... - items = api.API().get_zone_list(req.environ['nova.context']) + items = api.API.get_zone_list(req.environ['nova.context']) if not items: items = db.zone_get_all(req.environ['nova.context']) @@ -68,7 +68,7 @@ class Controller(wsgi.Controller): def info(self, req): """Return name and capabilities for this zone.""" - items = api.API().get_zone_capabilities(req.environ['nova.context']) + items = api.API.get_zone_capabilities(req.environ['nova.context']) zone = dict(name=FLAGS.zone_name) caps = FLAGS.zone_capabilities.split(';') diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index ac38350ed..fcff2f146 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -25,34 +25,37 @@ FLAGS = flags.FLAGS LOG = logging.getLogger('nova.scheduler.api') -class API: - """API for interacting with the scheduler.""" +def _call_scheduler(method, context, params=None): + """Generic handler for RPC calls to the scheduler. - def _call_scheduler(self, method, context, params=None): - """Generic handler for RPC calls to the scheduler. + :param params: Optional dictionary of arguments to be passed to the + scheduler worker - :param params: Optional dictionary of arguments to be passed to the - scheduler worker + :retval: Result returned by scheduler worker + """ + if not params: + params = {} + queue = FLAGS.scheduler_topic + kwargs = {'method': method, 'args': params} + return rpc.call(context, queue, kwargs) - :retval: Result returned by scheduler worker - """ - if not params: - params = {} - queue = FLAGS.scheduler_topic - kwargs = {'method': method, 'args': params} - return rpc.call(context, queue, kwargs) - def get_zone_list(self, context): +class API: + """API for interacting with the scheduler.""" + + @classmethod + def get_zone_list(cls, context): """Return a list of zones assoicated with this zone.""" - items = self._call_scheduler('get_zone_list', context) + items = _call_scheduler('get_zone_list', context) for item in items: item['api_url'] = item['api_url'].replace('\\/', '/') return items - def get_zone_capabilities(self, context, service=None): + @classmethod + def get_zone_capabilities(cls, context, service=None): """Returns a dict of key, value capabilities for this zone, or for a particular class of services running in this zone.""" - return self._call_scheduler('get_zone_capabilities', context=context, + return _call_scheduler('get_zone_capabilities', context=context, params=dict(service=service)) @classmethod diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index 09c9811f3..c1a50dbc3 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -118,8 +118,7 @@ class ZoneManager(object): _min and _max values.""" service_dict = self.service_states if service: - service_dict = dict(service_name=service, - hosts=self.service_states.get(service, {})) + service_dict = {service: self.service_states.get(service, {})} # TODO(sandy) - be smarter about fabricating this structure. # But it's likely to change once we understand what the Best-Match diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index 82b892b9e..33a66df0b 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -75,6 +75,10 @@ def zone_get_all_db(context): ] +def zone_caps(method, context, params): + return dict() + + class ZonesTest(test.TestCase): def setUp(self): super(ZonesTest, self).setUp() @@ -93,13 +97,18 @@ class ZonesTest(test.TestCase): self.stubs.Set(nova.db, 'zone_create', zone_create) self.stubs.Set(nova.db, 'zone_delete', zone_delete) + self.old_zone_name = FLAGS.zone_name + self.old_zone_caps = FLAGS.zone_capabilities + def tearDown(self): self.stubs.UnsetAll() FLAGS.allow_admin_api = self.allow_admin + FLAGS.zone_name = self.old_zone_name + FLAGS.zone_capabilities = self.old_zone_caps super(ZonesTest, self).tearDown() def test_get_zone_list_scheduler(self): - self.stubs.Set(api.API, '_call_scheduler', zone_get_all_scheduler) + self.stubs.Set(api, '_call_scheduler', zone_get_all_scheduler) req = webob.Request.blank('/v1.0/zones') res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) @@ -108,8 +117,7 @@ class ZonesTest(test.TestCase): self.assertEqual(len(res_dict['zones']), 2) def test_get_zone_list_db(self): - self.stubs.Set(api.API, '_call_scheduler', - zone_get_all_scheduler_empty) + self.stubs.Set(api, '_call_scheduler', zone_get_all_scheduler_empty) self.stubs.Set(nova.db, 'zone_get_all', zone_get_all_db) req = webob.Request.blank('/v1.0/zones') res = req.get_response(fakes.wsgi_app()) @@ -162,3 +170,19 @@ class ZonesTest(test.TestCase): self.assertEqual(res_dict['zone']['id'], 1) self.assertEqual(res_dict['zone']['api_url'], 'http://example.com') self.assertFalse('username' in res_dict['zone']) + + def test_zone_info(self): + FLAGS.zone_name = 'darksecret' + FLAGS.zone_capabilities = 'cap1:a,b;cap2:c,d' + self.stubs.Set(api, '_call_scheduler', zone_caps) + + body = dict(zone=dict(username='zeb', password='sneaky')) + req = webob.Request.blank('/v1.0/zones/info') + + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + self.assertEqual(res.status_int, 200) + self.assertEqual(res_dict['zone']['name'], 'darksecret') + self.assertEqual(res_dict['zone']['cap1'], 'a,b') + self.assertEqual(res_dict['zone']['cap2'], 'c,d') + diff --git a/nova/tests/test_zones.py b/nova/tests/test_zones.py index 3ca71d5f1..79d766f28 100644 --- a/nova/tests/test_zones.py +++ b/nova/tests/test_zones.py @@ -104,6 +104,12 @@ class ZoneManagerTestCase(test.TestCase): svc1_c=(5, 5), svc10_a=(99, 99), svc10_b=(99, 99))) + caps = zm.get_zone_capabilities(self, 'svc1') + self.assertEquals(caps, dict(svc1_a=(2, 20), svc1_b=(3, 30), + svc1_c=(5, 5))) + caps = zm.get_zone_capabilities(self, 'svc10') + self.assertEquals(caps, dict(svc10_a=(99, 99), svc10_b=(99, 99))) + def test_refresh_from_db_replace_existing(self): zm = zone_manager.ZoneManager() zone_state = zone_manager.ZoneState() -- cgit From 307dcb7906ff066e2883cdee8998dfa78ebc8221 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 24 Feb 2011 15:44:27 -0800 Subject: sorry, pep8 --- nova/tests/api/openstack/test_zones.py | 5 ++--- nova/tests/test_zones.py | 10 +++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index 33a66df0b..a40d46749 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -76,7 +76,7 @@ def zone_get_all_db(context): def zone_caps(method, context, params): - return dict() + return dict() class ZonesTest(test.TestCase): @@ -175,7 +175,7 @@ class ZonesTest(test.TestCase): FLAGS.zone_name = 'darksecret' FLAGS.zone_capabilities = 'cap1:a,b;cap2:c,d' self.stubs.Set(api, '_call_scheduler', zone_caps) - + body = dict(zone=dict(username='zeb', password='sneaky')) req = webob.Request.blank('/v1.0/zones/info') @@ -185,4 +185,3 @@ class ZonesTest(test.TestCase): self.assertEqual(res_dict['zone']['name'], 'darksecret') self.assertEqual(res_dict['zone']['cap1'], 'a,b') self.assertEqual(res_dict['zone']['cap2'], 'c,d') - diff --git a/nova/tests/test_zones.py b/nova/tests/test_zones.py index 79d766f28..48e1442cf 100644 --- a/nova/tests/test_zones.py +++ b/nova/tests/test_zones.py @@ -88,28 +88,28 @@ class ZoneManagerTestCase(test.TestCase): zm.update_service_capabilities("svc1", "host1", dict(a=2, b=3)) caps = zm.get_zone_capabilities(self, None) self.assertEquals(caps, dict(svc1_a=(2, 2), svc1_b=(3, 3))) - + zm.update_service_capabilities("svc1", "host2", dict(a=20, b=30)) caps = zm.get_zone_capabilities(self, None) self.assertEquals(caps, dict(svc1_a=(2, 20), svc1_b=(3, 30))) - + zm.update_service_capabilities("svc10", "host1", dict(a=99, b=99)) caps = zm.get_zone_capabilities(self, None) self.assertEquals(caps, dict(svc1_a=(2, 20), svc1_b=(3, 30), svc10_a=(99, 99), svc10_b=(99, 99))) - + zm.update_service_capabilities("svc1", "host3", dict(c=5)) caps = zm.get_zone_capabilities(self, None) self.assertEquals(caps, dict(svc1_a=(2, 20), svc1_b=(3, 30), svc1_c=(5, 5), svc10_a=(99, 99), svc10_b=(99, 99))) - + caps = zm.get_zone_capabilities(self, 'svc1') self.assertEquals(caps, dict(svc1_a=(2, 20), svc1_b=(3, 30), svc1_c=(5, 5))) caps = zm.get_zone_capabilities(self, 'svc10') self.assertEquals(caps, dict(svc10_a=(99, 99), svc10_b=(99, 99))) - + def test_refresh_from_db_replace_existing(self): zm = zone_manager.ZoneManager() zone_state = zone_manager.ZoneState() -- cgit From abc6c82449dfc46a33dcd8190840e51f44b5b930 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 17 Mar 2011 07:30:22 -0700 Subject: Replaced capability flags with List --- nova/api/openstack/zones.py | 4 ++-- nova/flags.py | 5 +++-- nova/tests/api/openstack/test_zones.py | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index 547920901..ebfc7743c 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -71,9 +71,9 @@ class Controller(wsgi.Controller): items = api.API.get_zone_capabilities(req.environ['nova.context']) zone = dict(name=FLAGS.zone_name) - caps = FLAGS.zone_capabilities.split(';') + caps = FLAGS.zone_capabilities for cap in caps: - key_values = cap.split(':') + key_values = cap.split('=') zone[key_values[0]] = key_values[1] for item, (min_value, max_value) in items.iteritems(): zone[item] = "%s,%s" % (min_value, max_value) diff --git a/nova/flags.py b/nova/flags.py index c05cef373..3a8ec1a39 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -358,5 +358,6 @@ DEFINE_string('node_availability_zone', 'nova', 'availability zone of this node') DEFINE_string('zone_name', 'nova', 'name of this zone') -DEFINE_string('zone_capabilities', 'hypervisor:xenserver;os:linux', - 'Key/Value tags which represent capabilities of this zone') +DEFINE_list('zone_capabilities', + ['hypervisor=xenserver;kvm', 'os=linux;windows'], + 'Key/Multi-value list representng capabilities of this zone') diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index 5e3aee4a7..12d39fd29 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -178,7 +178,7 @@ class ZonesTest(test.TestCase): def test_zone_info(self): FLAGS.zone_name = 'darksecret' - FLAGS.zone_capabilities = 'cap1:a,b;cap2:c,d' + FLAGS.zone_capabilities = ['cap1=a;b', 'cap2=c;d'] self.stubs.Set(api, '_call_scheduler', zone_caps) body = dict(zone=dict(username='zeb', password='sneaky')) @@ -188,5 +188,5 @@ class ZonesTest(test.TestCase): res_dict = json.loads(res.body) self.assertEqual(res.status_int, 200) self.assertEqual(res_dict['zone']['name'], 'darksecret') - self.assertEqual(res_dict['zone']['cap1'], 'a,b') - self.assertEqual(res_dict['zone']['cap2'], 'c,d') + self.assertEqual(res_dict['zone']['cap1'], 'a;b') + self.assertEqual(res_dict['zone']['cap2'], 'c;d') -- cgit From 8f0b60f598c28b2f558f3ecdaa2f9604926393e6 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Mon, 21 Mar 2011 07:49:58 -0700 Subject: remove scheduler.api.API. naming changes. --- nova/api/openstack/zones.py | 4 ++-- nova/manager.py | 2 +- nova/rpc.py | 2 +- nova/scheduler/api.py | 43 +++++++++++++++------------------- nova/tests/api/openstack/test_zones.py | 8 +++---- 5 files changed, 27 insertions(+), 32 deletions(-) diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index ebfc7743c..d129cf34f 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -53,7 +53,7 @@ class Controller(wsgi.Controller): """Return all zones in brief""" # Ask the ZoneManager in the Scheduler for most recent data, # or fall-back to the database ... - items = api.API.get_zone_list(req.environ['nova.context']) + items = api.get_zone_list(req.environ['nova.context']) if not items: items = db.zone_get_all(req.environ['nova.context']) @@ -68,7 +68,7 @@ class Controller(wsgi.Controller): def info(self, req): """Return name and capabilities for this zone.""" - items = api.API.get_zone_capabilities(req.environ['nova.context']) + items = api.get_zone_capabilities(req.environ['nova.context']) zone = dict(name=FLAGS.zone_name) caps = FLAGS.zone_capabilities diff --git a/nova/manager.py b/nova/manager.py index f384e3f0f..508f133ca 100644 --- a/nova/manager.py +++ b/nova/manager.py @@ -96,7 +96,7 @@ class SchedulerDependentManager(Manager): """Pass data back to the scheduler at a periodic interval""" if self.last_capabilities: logging.debug(_("Notifying Schedulers of capabilities ...")) - api.API.update_service_capabilities(context, self.service_name, + api.update_service_capabilities(context, self.service_name, self.host, self.last_capabilities) super(SchedulerDependentManager, self).periodic_tasks(context) diff --git a/nova/rpc.py b/nova/rpc.py index 4918c0b95..2e3cd9057 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -244,7 +244,7 @@ class FanoutPublisher(Publisher): self.exchange = "%s_fanout" % topic self.queue = "%s_fanout" % topic self.durable = False - LOG.info(_("Writing to '%(exchange)s' fanout exchange"), + LOG.info(_("Creating '%(exchange)s' fanout exchange"), dict(exchange=self.exchange)) super(FanoutPublisher, self).__init__(connection=connection) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index b6d27dacc..e2cf3b6a3 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -40,30 +40,25 @@ def _call_scheduler(method, context, params=None): return rpc.call(context, queue, kwargs) -class API(object): - """API for interacting with the scheduler.""" +def get_zone_list(context): + """Return a list of zones assoicated with this zone.""" + items = _call_scheduler('get_zone_list', context) + for item in items: + item['api_url'] = item['api_url'].replace('\\/', '/') + return items - @classmethod - def get_zone_list(cls, context): - """Return a list of zones assoicated with this zone.""" - items = _call_scheduler('get_zone_list', context) - for item in items: - item['api_url'] = item['api_url'].replace('\\/', '/') - return items - @classmethod - def get_zone_capabilities(cls, context, service=None): - """Returns a dict of key, value capabilities for this zone, - or for a particular class of services running in this zone.""" - return _call_scheduler('get_zone_capabilities', context=context, - params=dict(service=service)) +def get_zone_capabilities(context, service=None): + """Returns a dict of key, value capabilities for this zone, + or for a particular class of services running in this zone.""" + return _call_scheduler('get_zone_capabilities', context=context, + params=dict(service=service)) - @classmethod - def update_service_capabilities(cls, context, service_name, host, - capabilities): - """Send an update to all the scheduler services informing them - of the capabilities of this service.""" - kwargs = dict(method='update_service_capabilities', - args=dict(service_name=service_name, host=host, - capabilities=capabilities)) - return rpc.fanout_cast(context, 'scheduler', kwargs) + +def update_service_capabilities(context, service_name, host, capabilities): + """Send an update to all the scheduler services informing them + of the capabilities of this service.""" + kwargs = dict(method='update_service_capabilities', + args=dict(service_name=service_name, host=host, + capabilities=capabilities)) + return rpc.fanout_cast(context, 'scheduler', kwargs) diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index 12d39fd29..a3f191aaa 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -75,7 +75,7 @@ def zone_get_all_db(context): ] -def zone_caps(method, context, params): +def zone_capabilities(method, context, params): return dict() @@ -98,13 +98,13 @@ class ZonesTest(test.TestCase): self.stubs.Set(nova.db, 'zone_delete', zone_delete) self.old_zone_name = FLAGS.zone_name - self.old_zone_caps = FLAGS.zone_capabilities + self.old_zone_capabilities = FLAGS.zone_capabilities def tearDown(self): self.stubs.UnsetAll() FLAGS.allow_admin_api = self.allow_admin FLAGS.zone_name = self.old_zone_name - FLAGS.zone_capabilities = self.old_zone_caps + FLAGS.zone_capabilities = self.old_zone_capabilities super(ZonesTest, self).tearDown() def test_get_zone_list_scheduler(self): @@ -179,7 +179,7 @@ class ZonesTest(test.TestCase): def test_zone_info(self): FLAGS.zone_name = 'darksecret' FLAGS.zone_capabilities = ['cap1=a;b', 'cap2=c;d'] - self.stubs.Set(api, '_call_scheduler', zone_caps) + self.stubs.Set(api, '_call_scheduler', zone_capabilities) body = dict(zone=dict(username='zeb', password='sneaky')) req = webob.Request.blank('/v1.0/zones/info') -- cgit From 05e6f82aa971606f7d33fb1de8f2c1c170d030de Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 23 Mar 2011 12:31:15 -0700 Subject: indenting cleanup --- nova/api/openstack/zones.py | 5 ++--- nova/compute/manager.py | 2 +- nova/flags.py | 2 +- nova/manager.py | 5 ++++- nova/rpc.py | 4 ++-- nova/scheduler/api.py | 2 +- nova/tests/test_zones.py | 2 +- 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index d129cf34f..d4a59993b 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -17,7 +17,6 @@ import common from nova import db from nova import flags -from nova import log as logging from nova import wsgi from nova.scheduler import api @@ -73,8 +72,8 @@ class Controller(wsgi.Controller): zone = dict(name=FLAGS.zone_name) caps = FLAGS.zone_capabilities for cap in caps: - key_values = cap.split('=') - zone[key_values[0]] = key_values[1] + key, value = cap.split('=') + zone[key] = value for item, (min_value, max_value) in items.iteritems(): zone[item] = "%s,%s" % (min_value, max_value) return dict(zone=zone) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index eae1fee68..289c91d8a 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -118,7 +118,7 @@ class ComputeManager(manager.SchedulerDependentManager): self.network_manager = utils.import_object(FLAGS.network_manager) self.volume_manager = utils.import_object(FLAGS.volume_manager) super(ComputeManager, self).__init__(service_name="compute", - *args, **kwargs) + *args, **kwargs) def init_host(self): """Do any initialization that needs to be run if this is a diff --git a/nova/flags.py b/nova/flags.py index 3a8ec1a39..bf83b8e0f 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -360,4 +360,4 @@ DEFINE_string('node_availability_zone', 'nova', DEFINE_string('zone_name', 'nova', 'name of this zone') DEFINE_list('zone_capabilities', ['hypervisor=xenserver;kvm', 'os=linux;windows'], - 'Key/Multi-value list representng capabilities of this zone') + 'Key/Multi-value list representng capabilities of this zone') diff --git a/nova/manager.py b/nova/manager.py index 508f133ca..804a50479 100644 --- a/nova/manager.py +++ b/nova/manager.py @@ -59,6 +59,8 @@ from nova.scheduler import api FLAGS = flags.FLAGS +LOG = logging.getLogger('nova.manager') + class Manager(base.Base): def __init__(self, host=None, db_driver=None): @@ -83,6 +85,7 @@ class SchedulerDependentManager(Manager): should derive from this class. Otherwise they can derive from manager.Manager directly. Updates are only sent after update_service_capabilities is called with non-None values.""" + def __init__(self, host=None, db_driver=None, service_name="undefined"): self.last_capabilities = None self.service_name = service_name @@ -95,7 +98,7 @@ class SchedulerDependentManager(Manager): def periodic_tasks(self, context=None): """Pass data back to the scheduler at a periodic interval""" if self.last_capabilities: - logging.debug(_("Notifying Schedulers of capabilities ...")) + LOG.debug(_("Notifying Schedulers of capabilities ...")) api.update_service_capabilities(context, self.service_name, self.host, self.last_capabilities) diff --git a/nova/rpc.py b/nova/rpc.py index 6ddaea092..388f78d69 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -219,8 +219,8 @@ class FanoutAdapterConsumer(AdapterConsumer): self.queue = "%s_fanout_%s" % (topic, unique) self.durable = False LOG.info(_("Created '%(exchange)s' fanout exchange " - "with '%(key)s' routing key"), - dict(exchange=self.exchange, key=self.routing_key)) + "with '%(key)s' routing key"), + dict(exchange=self.exchange, key=self.routing_key)) super(FanoutAdapterConsumer, self).__init__(connection=connection, topic=topic, proxy=proxy) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index e2cf3b6a3..19a05b716 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -52,7 +52,7 @@ def get_zone_capabilities(context, service=None): """Returns a dict of key, value capabilities for this zone, or for a particular class of services running in this zone.""" return _call_scheduler('get_zone_capabilities', context=context, - params=dict(service=service)) + params=dict(service=service)) def update_service_capabilities(context, service_name, host, capabilities): diff --git a/nova/tests/test_zones.py b/nova/tests/test_zones.py index 48e1442cf..688dc704d 100644 --- a/nova/tests/test_zones.py +++ b/nova/tests/test_zones.py @@ -96,7 +96,7 @@ class ZoneManagerTestCase(test.TestCase): zm.update_service_capabilities("svc10", "host1", dict(a=99, b=99)) caps = zm.get_zone_capabilities(self, None) self.assertEquals(caps, dict(svc1_a=(2, 20), svc1_b=(3, 30), - svc10_a=(99, 99), svc10_b=(99, 99))) + svc10_a=(99, 99), svc10_b=(99, 99))) zm.update_service_capabilities("svc1", "host3", dict(c=5)) caps = zm.get_zone_capabilities(self, None) -- cgit