From 5d00101e5c0e6ba5e4ecba49c132f74c18b5267c Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 3 Mar 2011 16:28:04 -0400 Subject: [PATCH 01/14] start of fanout --- nova/rpc.py | 20 ++++++++++++++++++++ nova/scheduler/zone_manager.py | 4 ++++ 2 files changed, 24 insertions(+) diff --git a/nova/rpc.py b/nova/rpc.py index 8fe4565d..a02cdc90 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/zone_manager.py b/nova/scheduler/zone_manager.py index edf9000c..eedc5c23 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 *******")) From 803b3b79a8cae9dea49cc74a5abaf5da0fa18ff2 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Sun, 20 Feb 2011 13:36:45 -0800 Subject: [PATCH 02/14] 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 00000000..c78b6fea --- /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 ...")) From dba165de077f6d79039955dc79cd5ff6e35b3acb Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Sun, 20 Feb 2011 16:40:08 -0800 Subject: [PATCH 03/14] service ping working --- nova/scheduler_manager.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/nova/scheduler_manager.py b/nova/scheduler_manager.py index c78b6fea..987ca8e9 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) From a06093b1f089d41f8ed306727400c3fb3f9850e7 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Sun, 20 Feb 2011 20:58:59 -0800 Subject: [PATCH 04/14] fanout kinda working --- nova/rpc.py | 51 +++++++++++++++++++++++++-------------- nova/scheduler/api.py | 7 ++++++ nova/scheduler_manager.py | 2 ++ 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/nova/rpc.py b/nova/rpc.py index a02cdc90..e0cf6d30 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 8491bf3a..53d72507 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 987ca8e9..a4530161 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) From e860e7d5500941394bfdcefba510fdfc5143104f Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Sun, 20 Feb 2011 22:33:39 -0800 Subject: [PATCH 05/14] fanout works --- nova/rpc.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/nova/rpc.py b/nova/rpc.py index e0cf6d30..601e45b4 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) From c7df2944f9128be2f9bd4456acf6ec2f4a982b90 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 23 Feb 2011 14:41:11 -0800 Subject: [PATCH 06/14] 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 4820e04f..44d7c91e 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() From b46458d11ab47d85284472dcd6f3476f13680f74 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 23 Feb 2011 17:58:32 -0800 Subject: [PATCH 07/14] Tests all working again --- nova/flags.py | 2 +- nova/scheduler/api.py | 1 - nova/scheduler_manager.py | 22 ++++++++++++++++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/nova/flags.py b/nova/flags.py index 6f37c82f..7036180f 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/scheduler/api.py b/nova/scheduler/api.py index 53d72507..6a6bfc03 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 a4530161..65bd71c0 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) From 3291423f09631a93e3e38aa1c3e0268242e79b6a Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 24 Feb 2011 01:53:01 -0800 Subject: [PATCH 08/14] capability aggregation working --- nova/scheduler/api.py | 15 +++++++++++++-- nova/scheduler/zone_manager.py | 35 +++++++++++++++++++++++++++++++--- nova/scheduler_manager.py | 6 ++---- 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index 6a6bfc03..ac38350e 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/zone_manager.py b/nova/scheduler/zone_manager.py index eedc5c23..09c9811f 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 65bd71c0..ca39b85d 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) From 86d79a81a149b25e3c764f9b94b289b32e031df8 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 24 Feb 2011 14:32:25 -0800 Subject: [PATCH 09/14] service capabilities test --- nova/tests/test_zones.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/nova/tests/test_zones.py b/nova/tests/test_zones.py index 5a52a050..3ca71d5f 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() From 507de7900b9797c90a45d6ecfd249eff390aee0b Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 24 Feb 2011 15:23:15 -0800 Subject: [PATCH 10/14] new tests --- nova/scheduler/api.py | 39 ++++++++++++++++++---------------- nova/scheduler/zone_manager.py | 3 +-- nova/tests/test_zones.py | 6 ++++++ 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index ac38350e..fcff2f14 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -25,34 +25,37 @@ FLAGS = flags.FLAGS LOG = logging.getLogger('nova.scheduler.api') +def _call_scheduler(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 + + :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) + + class API: """API for interacting with 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 - - :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): + @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 09c9811f..c1a50dbc 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/test_zones.py b/nova/tests/test_zones.py index 3ca71d5f..79d766f2 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() From c1df9c5d11f205d76631a2b34b3ee685d2be43ac Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 24 Feb 2011 15:44:27 -0800 Subject: [PATCH 11/14] sorry, pep8 --- nova/tests/test_zones.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nova/tests/test_zones.py b/nova/tests/test_zones.py index 79d766f2..48e1442c 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() From ac1f686186d2f67d7d3b68c8fe3790dd16cc7139 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 17 Mar 2011 07:30:22 -0700 Subject: [PATCH 12/14] Replaced capability flags with List --- nova/flags.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/flags.py b/nova/flags.py index c05cef37..3a8ec1a3 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') From d82458a3701aeac9c652c28eb12c5157e437d4c4 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Mon, 21 Mar 2011 07:49:58 -0700 Subject: [PATCH 13/14] remove scheduler.api.API. naming changes. --- nova/rpc.py | 2 +- nova/scheduler/api.py | 43 +++++++++++++++++++------------------------ 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/nova/rpc.py b/nova/rpc.py index 4918c0b9..2e3cd905 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 b6d27dac..e2cf3b6a 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) From 8cf49562b201fb635af9ec6561204e014cefc731 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 23 Mar 2011 12:31:15 -0700 Subject: [PATCH 14/14] indenting cleanup --- nova/flags.py | 2 +- nova/rpc.py | 4 ++-- nova/scheduler/api.py | 2 +- nova/tests/test_zones.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/nova/flags.py b/nova/flags.py index 3a8ec1a3..bf83b8e0 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/rpc.py b/nova/rpc.py index 6ddaea09..388f78d6 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 e2cf3b6a..19a05b71 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 48e1442c..688dc704 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)