From 7764e1a620b32b65ec086e5926360ec7899cec69 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Mon, 20 Jun 2011 07:45:21 -0700 Subject: [PATCH 1/5] working commit --- nova/scheduler/api.py | 6 ++-- nova/scheduler/zone_aware_scheduler.py | 32 +++++++++++++++++-- .../scheduler/test_zone_aware_scheduler.py | 1 + 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index ffe59d2c..f966528f 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -112,7 +112,7 @@ def _process(func, zone): def call_zone_method(context, method_name, errors_to_ignore=None, - novaclient_collection_name='zones', *args, **kwargs): + novaclient_collection_name='zones', zones=None, *args, **kwargs): """Returns a list of (zone, call_result) objects.""" if not isinstance(errors_to_ignore, (list, tuple)): # This will also handle the default None @@ -120,7 +120,9 @@ def call_zone_method(context, method_name, errors_to_ignore=None, pool = greenpool.GreenPool() results = [] - for zone in db.zone_get_all(context): + if zones is None: + zones = db.zone_get_all(context) + for zone in zones: try: nova = novaclient.OpenStack(zone.username, zone.password, zone.api_url) diff --git a/nova/scheduler/zone_aware_scheduler.py b/nova/scheduler/zone_aware_scheduler.py index f04defa6..b23a1a7c 100644 --- a/nova/scheduler/zone_aware_scheduler.py +++ b/nova/scheduler/zone_aware_scheduler.py @@ -48,9 +48,9 @@ class InvalidBlob(exception.NovaException): class ZoneAwareScheduler(driver.Scheduler): """Base class for creating Zone Aware Schedulers.""" - def _call_zone_method(self, context, method, specs): + def _call_zone_method(self, context, method, specs, zones): """Call novaclient zone method. Broken out for testing.""" - return api.call_zone_method(context, method, specs=specs) + return api.call_zone_method(context, method, specs=specs, zones=zones) def _provision_resource_locally(self, context, item, instance_id, kwargs): """Create the requested resource in this Zone.""" @@ -160,6 +160,30 @@ class ZoneAwareScheduler(driver.Scheduler): self._provision_resource_from_blob(context, item, instance_id, request_spec, kwargs) + def _adjust_child_weights(self, child_results, zones): + """Apply the Scale and Offset values from the Zone definition + to adjust the weights returned from the child zones. Alters + child_results in place. + """ + for zone, result in child_results: + if not result: + continue + + for zone_rec in zones: + if zone_rec['url'] != zone: + continue + + try: + offset = zone_rec['weight_offset'] + scale = zone_rec['weight_scale'] + raw_weight = zone['weight'] + cooked_weight = offset + scale * raw_weight + zone['weight'] = cooked_weight + zone['raw_weight'] = raw_weight + except Exception, e: + LOG.exception(_("Bad child zone scaling values for Zone: " + "%(zone)s") % locals()) + def schedule_run_instance(self, context, instance_id, request_spec, *args, **kwargs): """This method is called from nova.compute.api to provision @@ -234,8 +258,10 @@ class ZoneAwareScheduler(driver.Scheduler): # Next, tack on the best weights from the child zones ... json_spec = json.dumps(request_spec) + all_zones = db.zone_get_all(context) child_results = self._call_zone_method(context, "select", - specs=json_spec) + specs=json_spec, zones=all_zones) + self._adjust_child_weights(child_results, all_zones) for child_zone, result in child_results: for weighting in result: # Remember the child_zone so we can get back to diff --git a/nova/tests/scheduler/test_zone_aware_scheduler.py b/nova/tests/scheduler/test_zone_aware_scheduler.py index 9f70b9db..1cbc914e 100644 --- a/nova/tests/scheduler/test_zone_aware_scheduler.py +++ b/nova/tests/scheduler/test_zone_aware_scheduler.py @@ -16,6 +16,7 @@ Tests For Zone Aware Scheduler. """ +from nova import db from nova import exception from nova import test from nova.scheduler import driver From e92004cd778e4e4cc0d8e5d17dba3ae60f0d474a Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Mon, 20 Jun 2011 16:56:59 -0700 Subject: [PATCH 2/5] fix lp 798361 --- nova/scheduler/zone_aware_scheduler.py | 49 ++++++++++++------- .../scheduler/test_zone_aware_scheduler.py | 2 +- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/nova/scheduler/zone_aware_scheduler.py b/nova/scheduler/zone_aware_scheduler.py index dfee6cc2..364d1e17 100644 --- a/nova/scheduler/zone_aware_scheduler.py +++ b/nova/scheduler/zone_aware_scheduler.py @@ -33,6 +33,7 @@ from nova import flags from nova import log as logging from nova import rpc +from nova.compute import api as compute_api from nova.scheduler import api from nova.scheduler import driver @@ -52,10 +53,21 @@ class ZoneAwareScheduler(driver.Scheduler): """Call novaclient zone method. Broken out for testing.""" return api.call_zone_method(context, method, specs=specs, zones=zones) - def _provision_resource_locally(self, context, item, instance_id, kwargs): + def _provision_resource_locally(self, context, build_plan_item, + request_spec): """Create the requested resource in this Zone.""" - host = item['hostname'] + host = build_plan_item['hostname'] + base_options = request_spec['instance_properties'] + + # TODO(sandy): I guess someone needs to add block_device_mapping + # support at some point? Also, OS API has no concept of security + # groups. + instance = compute_api.create_db_entry_for_new_instance(context, + base_options, None, []) + + instance_id = instance['instance_id'] kwargs['instance_id'] = instance_id + rpc.cast(context, db.queue_get_for(context, "compute", host), {"method": "run_instance", @@ -115,8 +127,8 @@ class ZoneAwareScheduler(driver.Scheduler): nova.servers.create(name, image_ref, flavor_id, ipgroup, meta, files, child_blob, reservation_id=reservation_id) - def _provision_resource_from_blob(self, context, item, instance_id, - request_spec, kwargs): + def _provision_resource_from_blob(self, context, build_plan_item, + instance_id, request_spec, kwargs): """Create the requested resource locally or in a child zone based on what is stored in the zone blob info. @@ -132,12 +144,12 @@ class ZoneAwareScheduler(driver.Scheduler): request.""" host_info = None - if "blob" in item: + if "blob" in build_plan_item: # Request was passed in from above. Is it for us? - host_info = self._decrypt_blob(item['blob']) - elif "child_blob" in item: + host_info = self._decrypt_blob(build_plan_item['blob']) + elif "child_blob" in build_plan_item: # Our immediate child zone provided this info ... - host_info = item + host_info = build_plan_item if not host_info: raise InvalidBlob() @@ -147,19 +159,18 @@ class ZoneAwareScheduler(driver.Scheduler): self._ask_child_zone_to_create_instance(context, host_info, request_spec, kwargs) else: - self._provision_resource_locally(context, host_info, - instance_id, kwargs) + self._provision_resource_locally(context, host_info, request_spec) - def _provision_resource(self, context, item, instance_id, request_spec, - kwargs): + def _provision_resource(self, context, build_plan_item, instance_id, + request_spec, kwargs): """Create the requested resource in this Zone or a child zone.""" - if "hostname" in item: - self._provision_resource_locally(context, item, instance_id, - kwargs) + if "hostname" in build_plan_item: + self._provision_resource_locally(context, build_plan_item, + request_spec) return - self._provision_resource_from_blob(context, item, instance_id, - request_spec, kwargs) + self._provision_resource_from_blob(context, build_plan_item, + instance_id, request_spec, kwargs) def _adjust_child_weights(self, child_results, zones): """Apply the Scale and Offset values from the Zone definition @@ -215,8 +226,8 @@ class ZoneAwareScheduler(driver.Scheduler): break item = build_plan.pop(0) - self._provision_resource(context, item, instance_id, request_spec, - kwargs) + self._provision_resource(context, build_plan_item, instance_id, + request_spec, kwargs) # Returning None short-circuits the routing to Compute (since # we've already done it here) diff --git a/nova/tests/scheduler/test_zone_aware_scheduler.py b/nova/tests/scheduler/test_zone_aware_scheduler.py index ef6a6a46..57fddb04 100644 --- a/nova/tests/scheduler/test_zone_aware_scheduler.py +++ b/nova/tests/scheduler/test_zone_aware_scheduler.py @@ -110,7 +110,7 @@ def fake_ask_child_zone_to_create_instance(context, zone_info, was_called = True -def fake_provision_resource_locally(context, item, instance_id, kwargs): +def fake_provision_resource_locally(context, build_plan, request_spec): global was_called was_called = True From 5ca1f68069ca96d2a5b3e2cac4a57e4e6ee89135 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Tue, 21 Jun 2011 11:20:06 -0700 Subject: [PATCH 3/5] fixed local db create --- nova/scheduler/zone_aware_scheduler.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/nova/scheduler/zone_aware_scheduler.py b/nova/scheduler/zone_aware_scheduler.py index 364d1e17..c810318d 100644 --- a/nova/scheduler/zone_aware_scheduler.py +++ b/nova/scheduler/zone_aware_scheduler.py @@ -54,7 +54,7 @@ class ZoneAwareScheduler(driver.Scheduler): return api.call_zone_method(context, method, specs=specs, zones=zones) def _provision_resource_locally(self, context, build_plan_item, - request_spec): + request_spec, kwargs): """Create the requested resource in this Zone.""" host = build_plan_item['hostname'] base_options = request_spec['instance_properties'] @@ -62,10 +62,10 @@ class ZoneAwareScheduler(driver.Scheduler): # TODO(sandy): I guess someone needs to add block_device_mapping # support at some point? Also, OS API has no concept of security # groups. - instance = compute_api.create_db_entry_for_new_instance(context, + instance = compute_api.API().create_db_entry_for_new_instance(context, base_options, None, []) - instance_id = instance['instance_id'] + instance_id = instance['id'] kwargs['instance_id'] = instance_id rpc.cast(context, @@ -159,14 +159,15 @@ class ZoneAwareScheduler(driver.Scheduler): self._ask_child_zone_to_create_instance(context, host_info, request_spec, kwargs) else: - self._provision_resource_locally(context, host_info, request_spec) + self._provision_resource_locally(context, host_info, request_spec, + kwargs) def _provision_resource(self, context, build_plan_item, instance_id, request_spec, kwargs): """Create the requested resource in this Zone or a child zone.""" if "hostname" in build_plan_item: self._provision_resource_locally(context, build_plan_item, - request_spec) + request_spec, kwargs) return self._provision_resource_from_blob(context, build_plan_item, @@ -225,7 +226,7 @@ class ZoneAwareScheduler(driver.Scheduler): if not build_plan: break - item = build_plan.pop(0) + build_plan_item = build_plan.pop(0) self._provision_resource(context, build_plan_item, instance_id, request_spec, kwargs) From 43e1f7deced004a5430eaf58ae7a9bc1093e8d16 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Tue, 21 Jun 2011 12:19:01 -0700 Subject: [PATCH 4/5] pep8 --- nova/scheduler/api.py | 3 ++- nova/scheduler/zone_aware_scheduler.py | 2 +- nova/tests/scheduler/test_zone_aware_scheduler.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index 733cd3df..f2ffcbc1 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -114,7 +114,8 @@ def _process(func, zone): def call_zone_method(context, method_name, errors_to_ignore=None, - novaclient_collection_name='zones', zones=None, *args, **kwargs): + novaclient_collection_name='zones', zones=None, + *args, **kwargs): """Returns a list of (zone, call_result) objects.""" if not isinstance(errors_to_ignore, (list, tuple)): # This will also handle the default None diff --git a/nova/scheduler/zone_aware_scheduler.py b/nova/scheduler/zone_aware_scheduler.py index c810318d..dcfef6af 100644 --- a/nova/scheduler/zone_aware_scheduler.py +++ b/nova/scheduler/zone_aware_scheduler.py @@ -181,7 +181,7 @@ class ZoneAwareScheduler(driver.Scheduler): for zone, result in child_results: if not result: continue - + for zone_rec in zones: if zone_rec['api_url'] != zone: continue diff --git a/nova/tests/scheduler/test_zone_aware_scheduler.py b/nova/tests/scheduler/test_zone_aware_scheduler.py index 57fddb04..75c94e47 100644 --- a/nova/tests/scheduler/test_zone_aware_scheduler.py +++ b/nova/tests/scheduler/test_zone_aware_scheduler.py @@ -173,7 +173,7 @@ class ZoneAwareSchedulerTestCase(test.TestCase): def setUp(self): super(ZoneAwareSchedulerTestCase, self).setUp() self.stubs = stubout.StubOutForTesting() - + def tearDown(self): self.stubs.UnsetAll() super(ZoneAwareSchedulerTestCase, self).tearDown() From 14ed545ee2728ccee104719ca5b7a411718a2e21 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 29 Jun 2011 09:29:07 -0700 Subject: [PATCH 5/5] removed extra stubout, switched to isinstance and catching explicit exception --- nova/scheduler/zone_aware_scheduler.py | 2 +- nova/tests/scheduler/test_zone_aware_scheduler.py | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/nova/scheduler/zone_aware_scheduler.py b/nova/scheduler/zone_aware_scheduler.py index dcfef6af..fe033e24 100644 --- a/nova/scheduler/zone_aware_scheduler.py +++ b/nova/scheduler/zone_aware_scheduler.py @@ -194,7 +194,7 @@ class ZoneAwareScheduler(driver.Scheduler): cooked_weight = offset + scale * raw_weight item['weight'] = cooked_weight item['raw_weight'] = raw_weight - except Exception, e: + except KeyError: LOG.exception(_("Bad child zone scaling values " "for Zone: %(zone)s") % locals()) diff --git a/nova/tests/scheduler/test_zone_aware_scheduler.py b/nova/tests/scheduler/test_zone_aware_scheduler.py index 5c9df7fb..832d9e6f 100644 --- a/nova/tests/scheduler/test_zone_aware_scheduler.py +++ b/nova/tests/scheduler/test_zone_aware_scheduler.py @@ -16,8 +16,6 @@ Tests For Zone Aware Scheduler. """ -import stubout - import nova.db from nova import exception @@ -170,14 +168,6 @@ def fake_zone_get_all(context): class ZoneAwareSchedulerTestCase(test.TestCase): """Test case for Zone Aware Scheduler.""" - def setUp(self): - super(ZoneAwareSchedulerTestCase, self).setUp() - self.stubs = stubout.StubOutForTesting() - - def tearDown(self): - self.stubs.UnsetAll() - super(ZoneAwareSchedulerTestCase, self).tearDown() - def test_zone_aware_scheduler(self): """ Create a nested set of FakeZones, ensure that a select call returns the