From 26227b79e9246a87eeb83766cfcc8e96d294d28b Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Mon, 30 Jan 2012 13:10:50 -0800 Subject: Removed zones from api and distributed scheduler There is a new Zones implementation coming that will use AMQP-to-AMQP channels vs. the public API. This is being done for three reasons: 1. remove complications in the OpenStack API (and possibly allow EC2 Zones) 2. remove dependencies on keystone and novaclient 3. faster scheduling (fewer chances for race conditions) Learn more here: http://wiki.openstack.org/EssexSchedulerImprovements Change-Id: I6fe538923dd5ae19276afac2ac3311a285fd5c99 --- .../api/openstack/compute/contrib/admin_actions.py | 20 -- .../openstack/compute/contrib/console_output.py | 2 +- nova/api/openstack/compute/contrib/consoles.py | 2 +- nova/api/openstack/compute/contrib/disk_config.py | 6 - .../openstack/compute/contrib/extended_status.py | 2 +- .../compute/contrib/server_diagnostics.py | 3 - nova/api/openstack/compute/contrib/zones.py | 228 --------------------- nova/api/openstack/compute/servers.py | 58 +----- 8 files changed, 14 insertions(+), 307 deletions(-) delete mode 100644 nova/api/openstack/compute/contrib/zones.py (limited to 'nova/api') diff --git a/nova/api/openstack/compute/contrib/admin_actions.py b/nova/api/openstack/compute/contrib/admin_actions.py index 69340648b..b1bfaf6ac 100644 --- a/nova/api/openstack/compute/contrib/admin_actions.py +++ b/nova/api/openstack/compute/contrib/admin_actions.py @@ -45,8 +45,6 @@ class AdminActionsController(wsgi.Controller): # TODO(bcwaldon): These action names should be prefixed with 'os-' @wsgi.action('pause') - @exception.novaclient_converter - @scheduler_api.redirect_handler def _pause(self, req, id, body): """Permit Admins to pause the server""" ctxt = req.environ['nova.context'] @@ -64,8 +62,6 @@ class AdminActionsController(wsgi.Controller): return webob.Response(status_int=202) @wsgi.action('unpause') - @exception.novaclient_converter - @scheduler_api.redirect_handler def _unpause(self, req, id, body): """Permit Admins to unpause the server""" ctxt = req.environ['nova.context'] @@ -83,8 +79,6 @@ class AdminActionsController(wsgi.Controller): return webob.Response(status_int=202) @wsgi.action('suspend') - @exception.novaclient_converter - @scheduler_api.redirect_handler def _suspend(self, req, id, body): """Permit admins to suspend the server""" context = req.environ['nova.context'] @@ -102,8 +96,6 @@ class AdminActionsController(wsgi.Controller): return webob.Response(status_int=202) @wsgi.action('resume') - @exception.novaclient_converter - @scheduler_api.redirect_handler def _resume(self, req, id, body): """Permit admins to resume the server from suspend""" context = req.environ['nova.context'] @@ -121,8 +113,6 @@ class AdminActionsController(wsgi.Controller): return webob.Response(status_int=202) @wsgi.action('migrate') - @exception.novaclient_converter - @scheduler_api.redirect_handler def _migrate(self, req, id, body): """Permit admins to migrate a server to a new host""" context = req.environ['nova.context'] @@ -139,8 +129,6 @@ class AdminActionsController(wsgi.Controller): return webob.Response(status_int=202) @wsgi.action('resetNetwork') - @exception.novaclient_converter - @scheduler_api.redirect_handler def _reset_network(self, req, id, body): """Permit admins to reset networking on an server""" context = req.environ['nova.context'] @@ -155,8 +143,6 @@ class AdminActionsController(wsgi.Controller): return webob.Response(status_int=202) @wsgi.action('injectNetworkInfo') - @exception.novaclient_converter - @scheduler_api.redirect_handler def _inject_network_info(self, req, id, body): """Permit admins to inject network info into a server""" context = req.environ['nova.context'] @@ -173,8 +159,6 @@ class AdminActionsController(wsgi.Controller): return webob.Response(status_int=202) @wsgi.action('lock') - @exception.novaclient_converter - @scheduler_api.redirect_handler def _lock(self, req, id, body): """Permit admins to lock a server""" context = req.environ['nova.context'] @@ -191,8 +175,6 @@ class AdminActionsController(wsgi.Controller): return webob.Response(status_int=202) @wsgi.action('unlock') - @exception.novaclient_converter - @scheduler_api.redirect_handler def _unlock(self, req, id, body): """Permit admins to lock a server""" context = req.environ['nova.context'] @@ -277,8 +259,6 @@ class AdminActionsController(wsgi.Controller): return resp @wsgi.action('os-migrateLive') - @exception.novaclient_converter - @scheduler_api.redirect_handler def _migrate_live(self, req, id, body): """Permit admins to (live) migrate a server to a new host""" context = req.environ["nova.context"] diff --git a/nova/api/openstack/compute/contrib/console_output.py b/nova/api/openstack/compute/contrib/console_output.py index 0752c3c70..6fc7fcbf0 100644 --- a/nova/api/openstack/compute/contrib/console_output.py +++ b/nova/api/openstack/compute/contrib/console_output.py @@ -41,7 +41,7 @@ class ConsoleOutputController(wsgi.Controller): authorize(context) try: - instance = self.compute_api.routing_get(context, id) + instance = self.compute_api.get(context, id) except exception.NotFound: raise webob.exc.HTTPNotFound(_('Instance not found')) diff --git a/nova/api/openstack/compute/contrib/consoles.py b/nova/api/openstack/compute/contrib/consoles.py index f308c6717..833eb0f39 100644 --- a/nova/api/openstack/compute/contrib/consoles.py +++ b/nova/api/openstack/compute/contrib/consoles.py @@ -44,7 +44,7 @@ class ConsolesController(wsgi.Controller): raise webob.exc.HTTPBadRequest(_('Missing type specification')) try: - instance = self.compute_api.routing_get(context, id) + instance = self.compute_api.get(context, id) except exception.NotFound: raise webob.exc.HTTPNotFound(_('Instance not found')) diff --git a/nova/api/openstack/compute/contrib/disk_config.py b/nova/api/openstack/compute/contrib/disk_config.py index 5b249478a..e338fa5ee 100644 --- a/nova/api/openstack/compute/contrib/disk_config.py +++ b/nova/api/openstack/compute/contrib/disk_config.py @@ -103,12 +103,6 @@ class ServersDiskConfigTemplate(xmlutil.TemplateBuilder): class ServerDiskConfigController(wsgi.Controller): def _add_disk_config(self, context, servers): - # Filter out any servers that already have the key set - # (most likely from a remote zone) - servers = [s for s in servers if API_DISK_CONFIG not in s] - if not servers: - return - # Get DB information for servers uuids = [server['id'] for server in servers] db_servers = db.instance_get_all_by_filters(context, diff --git a/nova/api/openstack/compute/contrib/extended_status.py b/nova/api/openstack/compute/contrib/extended_status.py index 9447bf300..88a5f12a4 100644 --- a/nova/api/openstack/compute/contrib/extended_status.py +++ b/nova/api/openstack/compute/contrib/extended_status.py @@ -56,7 +56,7 @@ class ExtendedStatusController(wsgi.Controller): resp_obj.attach(xml=ExtendedStatusTemplate()) try: - instance = self.compute_api.routing_get(context, id) + instance = self.compute_api.get(context, id) except exception.NotFound: explanation = _("Server not found.") raise exc.HTTPNotFound(explanation=explanation) diff --git a/nova/api/openstack/compute/contrib/server_diagnostics.py b/nova/api/openstack/compute/contrib/server_diagnostics.py index 49afcac01..c03cd2d47 100644 --- a/nova/api/openstack/compute/contrib/server_diagnostics.py +++ b/nova/api/openstack/compute/contrib/server_diagnostics.py @@ -20,7 +20,6 @@ from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import compute from nova import exception -from nova.scheduler import api as scheduler_api authorize = extensions.extension_authorizer('compute', 'server_diagnostics') @@ -38,8 +37,6 @@ class ServerDiagnosticsTemplate(xmlutil.TemplateBuilder): class ServerDiagnosticsController(object): @wsgi.serializers(xml=ServerDiagnosticsTemplate) - @exception.novaclient_converter - @scheduler_api.redirect_handler def index(self, req, server_id): context = req.environ["nova.context"] authorize(context) diff --git a/nova/api/openstack/compute/contrib/zones.py b/nova/api/openstack/compute/contrib/zones.py deleted file mode 100644 index 8db16f235..000000000 --- a/nova/api/openstack/compute/contrib/zones.py +++ /dev/null @@ -1,228 +0,0 @@ -# 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. - -"""The zones extension.""" - -import json - -from nova.api.openstack import common -from nova.api.openstack.compute import servers -from nova.api.openstack import extensions -from nova.api.openstack import xmlutil -from nova.api.openstack import wsgi -from nova.compute import api as compute -from nova import crypto -from nova import exception -from nova import flags -from nova import log as logging -import nova.scheduler.api - - -LOG = logging.getLogger(__name__) -FLAGS = flags.FLAGS -authorize = extensions.extension_authorizer('compute', 'zones') - - -class CapabilitySelector(object): - def __call__(self, obj, do_raise=False): - return [(k, v) for k, v in obj.items() - if k not in ('id', 'api_url', 'name', 'capabilities')] - - -def make_zone(elem): - elem.set('id') - elem.set('api_url') - elem.set('name') - elem.set('capabilities') - - cap = xmlutil.SubTemplateElement(elem, xmlutil.Selector(0), - selector=CapabilitySelector()) - cap.text = 1 - - -zone_nsmap = {None: wsgi.XMLNS_V10} - - -class ZoneTemplate(xmlutil.TemplateBuilder): - def construct(self): - root = xmlutil.TemplateElement('zone', selector='zone') - make_zone(root) - return xmlutil.MasterTemplate(root, 1, nsmap=zone_nsmap) - - -class ZonesTemplate(xmlutil.TemplateBuilder): - def construct(self): - root = xmlutil.TemplateElement('zones') - elem = xmlutil.SubTemplateElement(root, 'zone', selector='zones') - make_zone(elem) - return xmlutil.MasterTemplate(root, 1, nsmap=zone_nsmap) - - -class WeightsTemplate(xmlutil.TemplateBuilder): - def construct(self): - root = xmlutil.TemplateElement('weights') - weight = xmlutil.SubTemplateElement(root, 'weight', selector='weights') - blob = xmlutil.SubTemplateElement(weight, 'blob') - blob.text = 'blob' - inner_weight = xmlutil.SubTemplateElement(weight, 'weight') - inner_weight.text = 'weight' - return xmlutil.MasterTemplate(root, 1, nsmap=zone_nsmap) - - -def _filter_keys(item, keys): - """ - Filters all model attributes except for keys - item is a dict - - """ - return dict((k, v) for k, v in item.iteritems() if k in keys) - - -def _exclude_keys(item, keys): - return dict((k, v) for k, v in item.iteritems() if k and (k not in keys)) - - -def _scrub_zone(zone): - return _exclude_keys(zone, ('username', 'password', 'created_at', - 'deleted', 'deleted_at', 'updated_at')) - - -def check_encryption_key(func): - def wrapped(*args, **kwargs): - if not FLAGS.build_plan_encryption_key: - raise exception.Error(_("--build_plan_encryption_key not set")) - return func(*args, **kwargs) - return wrapped - - -class Controller(object): - """Controller for Zone resources.""" - - def __init__(self): - self.compute_api = compute.API() - - @wsgi.serializers(xml=ZonesTemplate) - def index(self, req): - """Return all zones in brief""" - authorize(req.environ['nova.context']) - # Ask the ZoneManager in the Scheduler for most recent data, - # or fall-back to the database ... - items = nova.scheduler.api.get_zone_list(req.environ['nova.context']) - items = common.limited(items, req) - items = [_scrub_zone(item) for item in items] - return dict(zones=items) - - @wsgi.serializers(xml=ZonesTemplate) - def detail(self, req): - """Return all zones in detail""" - return self.index(req) - - @wsgi.serializers(xml=ZoneTemplate) - def info(self, req): - """Return name and capabilities for this zone.""" - context = req.environ['nova.context'] - authorize(context) - zone_capabs = nova.scheduler.api.get_zone_capabilities(context) - # NOTE(comstud): This should probably return, instead: - # {'zone': {'name': FLAGS.zone_name, - # 'capabilities': zone_capabs}} - zone_capabs['name'] = FLAGS.zone_name - return dict(zone=zone_capabs) - - @wsgi.serializers(xml=ZoneTemplate) - def show(self, req, id): - """Return data about the given zone id""" - context = req.environ['nova.context'] - authorize(context) - zone_id = int(id) - zone = nova.scheduler.api.zone_get(context, zone_id) - return dict(zone=_scrub_zone(zone)) - - def delete(self, req, id): - """Delete a child zone entry.""" - authorize(req.environ['nova.context']) - zone_id = int(id) - nova.scheduler.api.zone_delete(req.environ['nova.context'], zone_id) - return {} - - @wsgi.serializers(xml=ZoneTemplate) - @wsgi.deserializers(xml=servers.CreateDeserializer) - def create(self, req, body): - """Create a child zone entry.""" - context = req.environ['nova.context'] - authorize(context) - zone = nova.scheduler.api.zone_create(context, body["zone"]) - return dict(zone=_scrub_zone(zone)) - - @wsgi.serializers(xml=ZoneTemplate) - def update(self, req, id, body): - """Update a child zone entry.""" - context = req.environ['nova.context'] - authorize(context) - zone_id = int(id) - zone = nova.scheduler.api.zone_update(context, zone_id, body["zone"]) - return dict(zone=_scrub_zone(zone)) - - @wsgi.serializers(xml=WeightsTemplate) - @check_encryption_key - def select(self, req, body): - """Returns a weighted list of costs to create instances - of desired capabilities.""" - context = req.environ['nova.context'] - authorize(context) - specs = json.loads(body) - build_plan = nova.scheduler.api.select(context, specs=specs) - cooked = self._scrub_build_plan(build_plan) - return {"weights": cooked} - - def _scrub_build_plan(self, build_plan): - """Remove all the confidential data and return a sanitized - version of the build plan. Include an encrypted full version - of the weighting entry so we can get back to it later.""" - encryptor = crypto.encryptor(FLAGS.build_plan_encryption_key) - cooked = [] - for entry in build_plan: - json_entry = json.dumps(entry) - cipher_text = encryptor(json_entry) - cooked.append(dict(weight=entry['weight'], - blob=cipher_text)) - return cooked - - -class Zones(extensions.ExtensionDescriptor): - """Enables zones-related functionality such as adding child zones, - listing child zones, getting the capabilities of the local zone, - and returning build plans to parent zones' schedulers - """ - - name = "Zones" - alias = "os-zones" - namespace = "http://docs.openstack.org/compute/ext/zones/api/v1.1" - updated = "2011-09-21T00:00:00+00:00" - - def get_resources(self): - #NOTE(bcwaldon): This resource should be prefixed with 'os-' - coll_actions = { - 'detail': 'GET', - 'info': 'GET', - 'select': 'POST', - } - - res = extensions.ResourceExtension('zones', - Controller(), - collection_actions=coll_actions) - return [res] diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index 649566ec9..87e66b71f 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -33,7 +33,6 @@ from nova import exception from nova import flags from nova import log as logging from nova.rpc import common as rpc_common -from nova.scheduler import api as scheduler_api from nova import utils @@ -417,18 +416,14 @@ class Controller(wsgi.Controller): remove_invalid_options(context, search_opts, self._get_server_search_options()) - # Convert local_zone_only into a boolean - search_opts['local_zone_only'] = utils.bool_from_str( - search_opts.get('local_zone_only', False)) - - # If search by 'status', we need to convert it to 'vm_state' - # to pass on to child zones. - if 'status' in search_opts: - status = search_opts['status'] + # Verify search by 'status' contains a valid status. + # Convert it to filter by vm_state for compute_api. + status = search_opts.pop('status', None) + if status is not None: state = common.vm_state_from_status(status) if state is None: - reason = _('Invalid server status: %(status)s') % locals() - raise exception.InvalidInput(reason=reason) + msg = _('Invalid server status: %(status)s') % locals() + raise exc.HTTPBadRequest(explanation=msg) search_opts['vm_state'] = state if 'changes-since' in search_opts: @@ -474,7 +469,7 @@ class Controller(wsgi.Controller): def _get_server(self, context, instance_uuid): """Utility function for looking up an instance by uuid""" try: - return self.compute_api.routing_get(context, instance_uuid) + return self.compute_api.get(context, instance_uuid) except exception.NotFound: raise exc.HTTPNotFound() @@ -602,13 +597,11 @@ class Controller(wsgi.Controller): raise exc.HTTPBadRequest(explanation=expl) @wsgi.serializers(xml=ServerTemplate) - @exception.novaclient_converter - @scheduler_api.redirect_handler def show(self, req, id): """ Returns server details by server id """ try: context = req.environ['nova.context'] - instance = self.compute_api.routing_get(context, id) + instance = self.compute_api.get(context, id) self._add_instance_faults(context, [instance]) return self._view_builder.show(req, instance) except exception.NotFound: @@ -684,8 +677,6 @@ class Controller(wsgi.Controller): msg = _("Invalid flavorRef provided.") raise exc.HTTPBadRequest(explanation=msg) - zone_blob = server_dict.get('blob') - # optional openstack extensions: key_name = server_dict.get('key_name') user_data = server_dict.get('user_data') @@ -698,14 +689,6 @@ class Controller(wsgi.Controller): block_device_mapping = self._get_block_device_mapping(server_dict) - # Only allow admins to specify their own reservation_ids - # This is really meant to allow zones to work. - reservation_id = server_dict.get('reservation_id') - if all([reservation_id is not None, - reservation_id != '', - not context.is_admin]): - reservation_id = None - ret_resv_id = server_dict.get('return_reservation_id', False) min_count = server_dict.get('min_count') @@ -736,8 +719,6 @@ class Controller(wsgi.Controller): access_ip_v6=access_ip_v6, injected_files=injected_files, admin_password=password, - zone_blob=zone_blob, - reservation_id=reservation_id, min_count=min_count, max_count=max_count, requested_networks=requested_networks, @@ -795,7 +776,6 @@ class Controller(wsgi.Controller): self.compute_api.delete(context, instance) @wsgi.serializers(xml=ServerTemplate) - @scheduler_api.redirect_handler def update(self, req, id, body): """Update server then pass on to version-specific controller""" if len(req.body) == 0: @@ -827,7 +807,7 @@ class Controller(wsgi.Controller): body['server']['auto_disk_config']) update_dict['auto_disk_config'] = auto_disk_config - instance = self.compute_api.routing_get(ctxt, id) + instance = self.compute_api.get(ctxt, id) try: self.compute_api.update(ctxt, instance, **update_dict) @@ -843,8 +823,6 @@ class Controller(wsgi.Controller): @wsgi.serializers(xml=FullServerTemplate) @wsgi.deserializers(xml=ActionDeserializer) @wsgi.action('confirmResize') - @exception.novaclient_converter - @scheduler_api.redirect_handler def _action_confirm_resize(self, req, id, body): context = req.environ['nova.context'] instance = self._get_server(context, id) @@ -865,8 +843,6 @@ class Controller(wsgi.Controller): @wsgi.serializers(xml=FullServerTemplate) @wsgi.deserializers(xml=ActionDeserializer) @wsgi.action('revertResize') - @exception.novaclient_converter - @scheduler_api.redirect_handler def _action_revert_resize(self, req, id, body): context = req.environ['nova.context'] instance = self._get_server(context, id) @@ -887,8 +863,6 @@ class Controller(wsgi.Controller): @wsgi.serializers(xml=FullServerTemplate) @wsgi.deserializers(xml=ActionDeserializer) @wsgi.action('reboot') - @exception.novaclient_converter - @scheduler_api.redirect_handler def _action_reboot(self, req, id, body): if 'reboot' in body and 'type' in body['reboot']: valid_reboot_types = ['HARD', 'SOFT'] @@ -935,8 +909,6 @@ class Controller(wsgi.Controller): return webob.Response(status_int=202) @wsgi.response(204) - @exception.novaclient_converter - @scheduler_api.redirect_handler def delete(self, req, id): """ Destroys a server """ try: @@ -975,8 +947,6 @@ class Controller(wsgi.Controller): @wsgi.serializers(xml=FullServerTemplate) @wsgi.deserializers(xml=ActionDeserializer) @wsgi.action('changePassword') - @exception.novaclient_converter - @scheduler_api.redirect_handler def _action_change_password(self, req, id, body): context = req.environ['nova.context'] if (not 'changePassword' in body @@ -1007,8 +977,6 @@ class Controller(wsgi.Controller): @wsgi.serializers(xml=FullServerTemplate) @wsgi.deserializers(xml=ActionDeserializer) @wsgi.action('resize') - @exception.novaclient_converter - @scheduler_api.redirect_handler def _action_resize(self, req, id, body): """ Resizes a given instance to the flavor size requested """ try: @@ -1030,8 +998,6 @@ class Controller(wsgi.Controller): @wsgi.serializers(xml=FullServerTemplate) @wsgi.deserializers(xml=ActionDeserializer) @wsgi.action('rebuild') - @exception.novaclient_converter - @scheduler_api.redirect_handler def _action_rebuild(self, req, id, body): """Rebuild an instance with the given attributes""" try: @@ -1115,8 +1081,6 @@ class Controller(wsgi.Controller): @wsgi.serializers(xml=FullServerTemplate) @wsgi.deserializers(xml=ActionDeserializer) @wsgi.action('createImage') - @exception.novaclient_converter - @scheduler_api.redirect_handler @common.check_snapshots_enabled def _action_create_image(self, req, id, body): """Snapshot a server instance.""" @@ -1173,8 +1137,8 @@ class Controller(wsgi.Controller): def _get_server_search_options(self): """Return server search options allowed by non-admin""" - return ('reservation_id', 'name', 'local_zone_only', - 'status', 'image', 'flavor', 'changes-since') + return ('reservation_id', 'name', 'status', 'image', 'flavor', + 'changes-since') def create_resource(): -- cgit