From c7646aa88d564694b99a569c3cdd2c7ffbbb745d Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Fri, 20 Jan 2012 15:12:01 -0800 Subject: Add SchedulerHints compute extension This allows arbitrary key/values to be passed in on a compute create request or rebuild/resize action. That data will be made available to the compute api as a filter_properties dictionary. Change-Id: Ie2ec57dcbc0d1d178e06606cb41027f9e46719a2 --- .../openstack/compute/contrib/scheduler_hints.py | 65 +++++++++++++ nova/api/openstack/compute/servers.py | 4 +- nova/compute/api.py | 9 +- .../compute/contrib/test_scheduler_hints.py | 103 +++++++++++++++++++++ .../tests/api/openstack/compute/test_extensions.py | 1 + 5 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 nova/api/openstack/compute/contrib/scheduler_hints.py create mode 100644 nova/tests/api/openstack/compute/contrib/test_scheduler_hints.py diff --git a/nova/api/openstack/compute/contrib/scheduler_hints.py b/nova/api/openstack/compute/contrib/scheduler_hints.py new file mode 100644 index 000000000..ef665da27 --- /dev/null +++ b/nova/api/openstack/compute/contrib/scheduler_hints.py @@ -0,0 +1,65 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License + +import webob.exc + +from nova.api.openstack import extensions +from nova.api.openstack import wsgi +import nova.log as logging + +LOG = logging.getLogger('nova.api.openstack.compute.contrib.scheduler_hints') + + +class SchedulerHintsController(wsgi.Controller): + + @staticmethod + def _extract_scheduler_hints(body): + hints = {} + + try: + hints.update(body['os:scheduler_hints']) + + # Ignore if data is not present + except KeyError: + pass + + # Fail if non-dict provided + except ValueError: + msg = _("Malformed scheduler_hints attribute") + raise webob.exc.HTTPBadRequest(reason=msg) + + return hints + + @wsgi.extends + def create(self, req, body): + hints = self._extract_scheduler_hints(body) + body['server']['scheduler_hints'] = hints + yield + + +class Scheduler_hints(extensions.ExtensionDescriptor): + """Pass arbitrary key/value pairs to the scheduler""" + + name = "SchedulerHints" + alias = "os-scheduler-hints" + namespace = "http://docs.openstack.org/compute/ext/" \ + "scheduler-hints/api/v2" + updated = "2011-07-19T00:00:00+00:00" + + def get_controller_extensions(self): + controller = SchedulerHintsController() + ext = extensions.ControllerExtension(self, 'servers', controller) + return [ext] diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index 988b403b1..ee7038c6e 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -709,6 +709,7 @@ class Controller(wsgi.Controller): min_count = max_count auto_disk_config = server_dict.get('auto_disk_config') + scheduler_hints = server_dict.get('scheduler_hints', {}) try: inst_type = \ @@ -735,7 +736,8 @@ class Controller(wsgi.Controller): availability_zone=availability_zone, config_drive=config_drive, block_device_mapping=block_device_mapping, - auto_disk_config=auto_disk_config) + auto_disk_config=auto_disk_config, + scheduler_hints=scheduler_hints) except exception.QuotaError as error: self._handle_quota_error(error) except exception.InstanceTypeMemoryTooSmall as error: diff --git a/nova/compute/api.py b/nova/compute/api.py index 138d70b66..2f3749ca0 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -179,7 +179,7 @@ class API(base.Base): reservation_id, access_ip_v4, access_ip_v6, requested_networks, config_drive, block_device_mapping, auto_disk_config, - create_instance_here=False): + create_instance_here=False, scheduler_hints=None): """Verify all the input parameters regardless of the provisioning strategy being performed and schedule the instance(s) for creation.""" @@ -298,6 +298,8 @@ class API(base.Base): else: filter_properties = {} + filter_properties['scheduler_hints'] = scheduler_hints + base_options = { 'reservation_id': reservation_id, 'image_ref': image_href, @@ -571,7 +573,7 @@ class API(base.Base): reservation_id=None, block_device_mapping=None, access_ip_v4=None, access_ip_v6=None, requested_networks=None, config_drive=None, - auto_disk_config=None): + auto_disk_config=None, scheduler_hints=None): """ Provision instances, sending instance information to the scheduler. The scheduler will determine where the instance(s) @@ -610,7 +612,8 @@ class API(base.Base): reservation_id, access_ip_v4, access_ip_v6, requested_networks, config_drive, block_device_mapping, auto_disk_config, - create_instance_here=create_instance_here) + create_instance_here=create_instance_here, + scheduler_hints=scheduler_hints) if create_instance_here or instances is None: return (instances, reservation_id) diff --git a/nova/tests/api/openstack/compute/contrib/test_scheduler_hints.py b/nova/tests/api/openstack/compute/contrib/test_scheduler_hints.py new file mode 100644 index 000000000..7185873a7 --- /dev/null +++ b/nova/tests/api/openstack/compute/contrib/test_scheduler_hints.py @@ -0,0 +1,103 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# 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. + +from nova.api.openstack import compute +from nova.api.openstack.compute import extensions +from nova.api.openstack import wsgi +import nova.db.api +import nova.rpc +from nova import test +from nova.tests.api.openstack import fakes +from nova import utils + + +UUID = fakes.FAKE_UUID + + +class SchedulerHintsTestCase(test.TestCase): + + def setUp(self): + super(SchedulerHintsTestCase, self).setUp() + + self.fake_instance = fakes.stub_instance(1, uuid=UUID) + + app = compute.APIRouter() + app = extensions.ExtensionMiddleware(app) + app = wsgi.LazySerializationMiddleware(app) + self.app = app + + def test_create_server_without_hints(self): + + def fake_create(*args, **kwargs): + self.assertEqual(kwargs['scheduler_hints'], {}) + return ([self.fake_instance], '') + + self.stubs.Set(nova.compute.api.API, 'create', fake_create) + + req = fakes.HTTPRequest.blank('/fake/servers') + req.method = 'POST' + req.content_type = 'application/json' + body = {'server': { + 'name': 'server_test', + 'imageRef': 'cedef40a-ed67-4d10-800e-17455edce175', + 'flavorRef': '1', + }} + + req.body = utils.dumps(body) + res = req.get_response(self.app) + self.assertEqual(202, res.status_int) + + def test_create_server_with_hints(self): + + def fake_create(*args, **kwargs): + self.assertEqual(kwargs['scheduler_hints'], {'a': 'b'}) + return ([self.fake_instance], '') + + self.stubs.Set(nova.compute.api.API, 'create', fake_create) + + req = fakes.HTTPRequest.blank('/fake/servers') + req.method = 'POST' + req.content_type = 'application/json' + body = { + 'server': { + 'name': 'server_test', + 'imageRef': 'cedef40a-ed67-4d10-800e-17455edce175', + 'flavorRef': '1', + }, + 'os:scheduler_hints': {'a': 'b'}, + } + + req.body = utils.dumps(body) + res = req.get_response(self.app) + self.assertEqual(202, res.status_int) + + def test_create_server_bad_hints(self): + req = fakes.HTTPRequest.blank('/fake/servers') + req.method = 'POST' + req.content_type = 'application/json' + body = { + 'server': { + 'name': 'server_test', + 'imageRef': 'cedef40a-ed67-4d10-800e-17455edce175', + 'flavorRef': '1', + }, + 'os:scheduler_hints': 'here', + } + + req.body = utils.dumps(body) + res = req.get_response(self.app) + self.assertEqual(400, res.status_int) diff --git a/nova/tests/api/openstack/compute/test_extensions.py b/nova/tests/api/openstack/compute/test_extensions.py index 02356bc44..6bc56532f 100644 --- a/nova/tests/api/openstack/compute/test_extensions.py +++ b/nova/tests/api/openstack/compute/test_extensions.py @@ -173,6 +173,7 @@ class ExtensionControllerTest(ExtensionTestCase): "Multinic", "Quotas", "Rescue", + "SchedulerHints", "SecurityGroups", "ServerActionList", "ServerDiagnostics", -- cgit