summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKen'ichi Ohmichi <ken-oomichi@wx.jp.nec.com>2016-05-24 16:14:55 -0700
committerKen'ichi Ohmichi <ken-oomichi@wx.jp.nec.com>2016-06-01 11:55:12 -0700
commita31d917af0cc5ecb55424598e7b812e02afbf28c (patch)
tree5e280c6c041cba86e36bb9dd4f0ee5731df80342
parente1e5f6790f420cb8510844107a8154aa77961935 (diff)
downloadnova-a31d917af0cc5ecb55424598e7b812e02afbf28c.tar.gz
Remove legacy v2 API code completely
There are two implementation code for similar API in Nova repository. One is newer: v2.1 API, another is legacy: v2 API. v2.1 API has been used as the default API since Liberty and legacy v2 API has been marked as deprecated. We have used and tested v2.1 API so well and now is nice time to remove legacy API code based on the consensus of the design summit of Austin. This patch removes the legacy code completely. NOTE: The gate job which used the legacy code has been removed already since Iac81b7d569b76b99e9d86eaa5001ae7f9b78cdfe. NOTE: Many developers took much burden for maintaining both APIs in long term. I am really happy to remove this code, and let's say "good bye forever" :-) Partially implements blueprint remove-legacy-v2-api-code Change-Id: Ieb8374bff81b26c680cae6e6ca92ab736209570d
-rw-r--r--nova/api/openstack/compute/legacy_v2/__init__.py0
-rw-r--r--nova/api/openstack/compute/legacy_v2/consoles.py92
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/__init__.py37
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/admin_actions.py402
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/agents.py193
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/aggregates.py308
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/assisted_volume_snapshots.py97
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/attach_interfaces.py211
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/availability_zone.py144
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/baremetal_ext_status.py25
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/baremetal_nodes.py163
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/block_device_mapping_v2_boot.py23
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/cell_capacities.py25
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/cells.py370
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/certificates.py79
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/cloudpipe.py160
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/cloudpipe_update.py74
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/config_drive.py64
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/console_auth_tokens.py68
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/console_output.py95
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/consoles.py157
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/createserverext.py30
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/deferred_delete.py77
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/disk_config.py151
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/evacuate.py120
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/extended_availability_zone.py62
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/extended_evacuate_find_host.py26
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/extended_floating_ips.py25
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/extended_hypervisors.py25
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/extended_ips.py78
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/extended_ips_mac.py76
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/extended_networks.py26
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/extended_quotas.py28
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/extended_rescue_with_image.py25
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/extended_server_attributes.py71
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/extended_services.py23
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/extended_services_delete.py23
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/extended_status.py66
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/extended_virtual_interfaces_net.py53
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/extended_volumes.py78
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/fixed_ips.py99
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/flavor_access.py177
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/flavor_disabled.py64
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/flavor_rxtx.py64
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/flavor_swap.py64
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/flavorextradata.py72
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/flavorextraspecs.py166
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/flavormanage.py112
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/floating_ip_dns.py279
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/floating_ip_pools.py67
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/floating_ips.py333
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/floating_ips_bulk.py171
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/fping.py147
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/hide_server_addresses.py79
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/hosts.py335
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/hypervisor_status.py25
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/hypervisors.py232
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/image_size.py62
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/instance_actions.py95
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/instance_usage_audit_log.py138
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/keypairs.py181
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/migrations.py80
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/multinic.py97
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/multiple_create.py25
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/networks_associate.py106
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/os_networks.py231
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/os_tenant_networks.py214
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/preserve_ephemeral_rebuild.py23
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/quota_classes.py139
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/quotas.py272
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/rescue.py99
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/scheduler_hints.py63
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/security_group_default_rules.py150
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/security_groups.py506
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/server_diagnostics.py63
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/server_external_events.py149
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/server_group_quotas.py50
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/server_groups.py243
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/server_list_multi_status.py25
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/server_password.py70
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/server_sort_keys.py25
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/server_start_stop.py84
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/server_usage.py71
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/services.py199
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/shelve.py100
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/simple_tenant_usage.py284
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/used_limits.py93
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/used_limits_for_admin.py25
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/user_data.py25
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/user_quotas.py26
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/virtual_interfaces.py87
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/volume_attachment_update.py26
-rw-r--r--nova/api/openstack/compute/legacy_v2/contrib/volumes.py613
-rw-r--r--nova/api/openstack/compute/legacy_v2/extensions.py38
-rw-r--r--nova/api/openstack/compute/legacy_v2/flavors.py112
-rw-r--r--nova/api/openstack/compute/legacy_v2/image_metadata.py126
-rw-r--r--nova/api/openstack/compute/legacy_v2/images.py150
-rw-r--r--nova/api/openstack/compute/legacy_v2/ips.py52
-rw-r--r--nova/api/openstack/compute/legacy_v2/limits.py393
-rw-r--r--nova/api/openstack/compute/legacy_v2/server_metadata.py189
-rw-r--r--nova/api/openstack/compute/legacy_v2/servers.py1146
-rw-r--r--nova/api/openstack/compute/legacy_v2/versions.py28
102 files changed, 0 insertions, 13309 deletions
diff --git a/nova/api/openstack/compute/legacy_v2/__init__.py b/nova/api/openstack/compute/legacy_v2/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
--- a/nova/api/openstack/compute/legacy_v2/__init__.py
+++ /dev/null
diff --git a/nova/api/openstack/compute/legacy_v2/consoles.py b/nova/api/openstack/compute/legacy_v2/consoles.py
deleted file mode 100644
index 24b943897d..0000000000
--- a/nova/api/openstack/compute/legacy_v2/consoles.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# Copyright 2010 OpenStack Foundation
-# 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.
-
-import webob
-from webob import exc
-
-from nova.api.openstack import wsgi
-from nova.console import api as console_api
-from nova import exception
-
-
-def _translate_keys(cons):
- """Coerces a console instance into proper dictionary format."""
- pool = cons['pool']
- info = {'id': cons['id'],
- 'console_type': pool['console_type']}
- return dict(console=info)
-
-
-def _translate_detail_keys(cons):
- """Coerces a console instance into proper dictionary format with
- correctly mapped attributes.
- """
- pool = cons['pool']
- info = {'id': cons['id'],
- 'console_type': pool['console_type'],
- 'password': cons['password'],
- 'instance_name': cons['instance_name'],
- 'port': cons['port'],
- 'host': pool['public_hostname']}
- return dict(console=info)
-
-
-class Controller(object):
- """The Consoles controller for the OpenStack API."""
-
- def __init__(self):
- self.console_api = console_api.API()
-
- def index(self, req, server_id):
- """Returns a list of consoles for this instance."""
- consoles = self.console_api.get_consoles(
- req.environ['nova.context'],
- server_id)
- return dict(consoles=[_translate_keys(console)
- for console in consoles])
-
- def create(self, req, server_id, body):
- """Creates a new console."""
- try:
- self.console_api.create_console(
- req.environ['nova.context'],
- server_id)
- except exception.InstanceNotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
-
- def show(self, req, server_id, id):
- """Shows in-depth information on a specific console."""
- try:
- console = self.console_api.get_console(
- req.environ['nova.context'],
- server_id,
- int(id))
- except exception.NotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- return _translate_detail_keys(console)
-
- def delete(self, req, server_id, id):
- """Deletes a console."""
- try:
- self.console_api.delete_console(req.environ['nova.context'],
- server_id,
- int(id))
- except exception.NotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- return webob.Response(status_int=202)
-
-
-def create_resource():
- return wsgi.Resource(Controller())
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/__init__.py b/nova/api/openstack/compute/legacy_v2/contrib/__init__.py
deleted file mode 100644
index a51bb9232d..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/__init__.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2011 Justin Santa Barbara
-# 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.
-
-"""Contrib contains extensions that are shipped with nova.
-
-It can't be called 'extensions' because that causes namespacing problems.
-
-"""
-
-from oslo_log import log as logging
-
-from nova.api.openstack import extensions
-import nova.conf
-
-CONF = nova.conf.CONF
-LOG = logging.getLogger(__name__)
-
-
-def standard_extensions(ext_mgr):
- extensions.load_standard_extensions(ext_mgr, LOG, __path__, __package__)
-
-
-def select_extensions(ext_mgr):
- extensions.load_standard_extensions(ext_mgr, LOG, __path__, __package__,
- CONF.osapi_compute_ext_list)
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/admin_actions.py b/nova/api/openstack/compute/legacy_v2/contrib/admin_actions.py
deleted file mode 100644
index ebd3c43e80..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/admin_actions.py
+++ /dev/null
@@ -1,402 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-#
-# 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 traceback
-
-from oslo_log import log as logging
-from oslo_utils import strutils
-import six
-import webob
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import compute
-from nova.compute import vm_states
-from nova import exception
-from nova.i18n import _
-from nova.i18n import _LE
-from nova import utils
-
-LOG = logging.getLogger(__name__)
-
-# States usable in resetState action
-state_map = dict(active=vm_states.ACTIVE, error=vm_states.ERROR)
-
-
-def authorize(context, action_name):
- action = 'admin_actions:%s' % action_name
- extensions.extension_authorizer('compute', action)(context)
-
-
-class AdminActionsController(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- super(AdminActionsController, self).__init__(*args, **kwargs)
- self.compute_api = compute.API()
-
- @wsgi.action('pause')
- def _pause(self, req, id, body):
- """Permit Admins to pause the server."""
- ctxt = req.environ['nova.context']
- authorize(ctxt, 'pause')
- server = common.get_instance(self.compute_api, ctxt, id)
- try:
- self.compute_api.pause(ctxt, server)
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'pause', id)
- except exception.InstanceNotFound:
- msg = _("Server not found")
- raise exc.HTTPNotFound(explanation=msg)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_LE("Compute.api::pause %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @wsgi.action('unpause')
- def _unpause(self, req, id, body):
- """Permit Admins to unpause the server."""
- ctxt = req.environ['nova.context']
- authorize(ctxt, 'unpause')
- server = common.get_instance(self.compute_api, ctxt, id)
- try:
- self.compute_api.unpause(ctxt, server)
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'unpause', id)
- except exception.InstanceNotFound:
- msg = _("Server not found")
- raise exc.HTTPNotFound(explanation=msg)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_LE("Compute.api::unpause %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @wsgi.action('suspend')
- def _suspend(self, req, id, body):
- """Permit admins to suspend the server."""
- context = req.environ['nova.context']
- authorize(context, 'suspend')
- server = common.get_instance(self.compute_api, context, id)
- try:
- self.compute_api.suspend(context, server)
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'suspend', id)
- except exception.InstanceNotFound:
- msg = _("Server not found")
- raise exc.HTTPNotFound(explanation=msg)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_LE("compute.api::suspend %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @wsgi.action('resume')
- def _resume(self, req, id, body):
- """Permit admins to resume the server from suspend."""
- context = req.environ['nova.context']
- authorize(context, 'resume')
- server = common.get_instance(self.compute_api, context, id)
- try:
- self.compute_api.resume(context, server)
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'resume', id)
- except exception.InstanceNotFound:
- msg = _("Server not found")
- raise exc.HTTPNotFound(explanation=msg)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_LE("compute.api::resume %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @wsgi.action('migrate')
- def _migrate(self, req, id, body):
- """Permit admins to migrate a server to a new host."""
- context = req.environ['nova.context']
- authorize(context, 'migrate')
- instance = common.get_instance(self.compute_api, context, id)
- try:
- self.compute_api.resize(req.environ['nova.context'], instance)
- except exception.QuotaError as error:
- raise exc.HTTPForbidden(explanation=error.format_message())
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'migrate', id)
- except exception.InstanceNotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- except exception.NoValidHost as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
- except Exception:
- LOG.exception(_LE("Error in migrate"))
- raise exc.HTTPBadRequest()
- return webob.Response(status_int=202)
-
- @wsgi.action('resetNetwork')
- def _reset_network(self, req, id, body):
- """Permit admins to reset networking on a server."""
- context = req.environ['nova.context']
- authorize(context, 'resetNetwork')
- instance = common.get_instance(self.compute_api, context, id)
- try:
- self.compute_api.reset_network(context, instance)
- except exception.InstanceNotFound:
- msg = _("Server not found")
- raise exc.HTTPNotFound(explanation=msg)
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_LE("Compute.api::reset_network %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @wsgi.action('injectNetworkInfo')
- def _inject_network_info(self, req, id, body):
- """Permit admins to inject network info into a server."""
- context = req.environ['nova.context']
- authorize(context, 'injectNetworkInfo')
- instance = common.get_instance(self.compute_api, context, id)
- try:
- self.compute_api.inject_network_info(context, instance)
- except exception.InstanceNotFound:
- msg = _("Server not found")
- raise exc.HTTPNotFound(explanation=msg)
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_LE("Compute.api::inject_network_info %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @wsgi.action('lock')
- def _lock(self, req, id, body):
- """Lock a server instance."""
- context = req.environ['nova.context']
- authorize(context, 'lock')
- instance = common.get_instance(self.compute_api, context, id)
- try:
- self.compute_api.lock(context, instance)
- except exception.InstanceNotFound:
- msg = _("Server not found")
- raise exc.HTTPNotFound(explanation=msg)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_LE("Compute.api::lock %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @wsgi.action('unlock')
- def _unlock(self, req, id, body):
- """Unlock a server instance."""
- context = req.environ['nova.context']
- authorize(context, 'unlock')
- instance = common.get_instance(self.compute_api, context, id)
- try:
- self.compute_api.unlock(context, instance)
- except exception.PolicyNotAuthorized as e:
- raise webob.exc.HTTPForbidden(explanation=e.format_message())
- except exception.InstanceNotFound:
- msg = _("Server not found")
- raise exc.HTTPNotFound(explanation=msg)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_LE("Compute.api::unlock %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
- @wsgi.action('createBackup')
- def _create_backup(self, req, id, body):
- """Backup a server instance.
-
- Images now have an `image_type` associated with them, which can be
- 'snapshot' or the backup type, like 'daily' or 'weekly'.
-
- If the image_type is backup-like, then the rotation factor can be
- included and that will cause the oldest backups that exceed the
- rotation factor to be deleted.
-
- """
- context = req.environ["nova.context"]
- authorize(context, 'createBackup')
- entity = body["createBackup"]
-
- try:
- image_name = entity["name"]
- backup_type = entity["backup_type"]
- rotation = entity["rotation"]
-
- except KeyError as missing_key:
- msg = _("createBackup entity requires %s attribute") % missing_key
- raise exc.HTTPBadRequest(explanation=msg)
-
- except TypeError:
- msg = _("Malformed createBackup entity")
- raise exc.HTTPBadRequest(explanation=msg)
-
- try:
- rotation = utils.validate_integer(rotation, "rotation",
- min_value=0)
- except exception.InvalidInput as e:
- raise webob.exc.HTTPBadRequest(explanation=e.format_message())
-
- props = {}
- metadata = entity.get('metadata', {})
- common.check_img_metadata_properties_quota(context, metadata)
- try:
- props.update(metadata)
- except ValueError:
- msg = _("Invalid metadata")
- raise exc.HTTPBadRequest(explanation=msg)
-
- instance = common.get_instance(self.compute_api, context, id)
- try:
- image = self.compute_api.backup(context, instance, image_name,
- backup_type, rotation, extra_properties=props)
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'createBackup', id)
- except exception.InvalidRequest as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- resp = webob.Response(status_int=202)
-
- # build location of newly-created image entity if rotation is not zero
- if rotation > 0:
- image_id = str(image['id'])
- image_ref = common.url_join(req.application_url, 'images',
- image_id)
- resp.headers['Location'] = image_ref
-
- return resp
-
- @wsgi.action('os-migrateLive')
- def _migrate_live(self, req, id, body):
- """Permit admins to (live) migrate a server to a new host."""
- context = req.environ["nova.context"]
- authorize(context, 'migrateLive')
-
- try:
- block_migration = body["os-migrateLive"]["block_migration"]
- disk_over_commit = body["os-migrateLive"]["disk_over_commit"]
- host = body["os-migrateLive"]["host"]
- except (TypeError, KeyError):
- msg = _("host, block_migration and disk_over_commit must "
- "be specified for live migration.")
- raise exc.HTTPBadRequest(explanation=msg)
-
- try:
- block_migration = strutils.bool_from_string(block_migration,
- strict=True)
- disk_over_commit = strutils.bool_from_string(disk_over_commit,
- strict=True)
- except ValueError as err:
- raise exc.HTTPBadRequest(explanation=six.text_type(err))
-
- instance = common.get_instance(self.compute_api, context, id)
- try:
- self.compute_api.live_migrate(context, instance, block_migration,
- disk_over_commit, host)
- except (exception.NoValidHost,
- exception.ComputeServiceUnavailable,
- exception.InvalidHypervisorType,
- exception.InvalidCPUInfo,
- exception.UnableToMigrateToSelf,
- exception.DestinationHypervisorTooOld,
- exception.InvalidLocalStorage,
- exception.InvalidSharedStorage,
- exception.HypervisorUnavailable,
- exception.MigrationPreCheckError) as ex:
- raise exc.HTTPBadRequest(explanation=ex.format_message())
- except exception.InstanceNotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'os-migrateLive', id)
- except Exception:
- if host is None:
- msg = _("Live migration of instance %s to another host "
- "failed") % id
- else:
- msg = _("Live migration of instance %(id)s to host %(host)s "
- "failed") % {'id': id, 'host': host}
- LOG.exception(msg)
- # Return messages from scheduler
- raise exc.HTTPInternalServerError(explanation=msg)
-
- return webob.Response(status_int=202)
-
- @wsgi.action('os-resetState')
- def _reset_state(self, req, id, body):
- """Permit admins to reset the state of a server."""
- context = req.environ["nova.context"]
- authorize(context, 'resetState')
-
- # Identify the desired state from the body
- try:
- state = state_map[body["os-resetState"]["state"]]
- except (TypeError, KeyError):
- msg = _("Desired state must be specified. Valid states "
- "are: %s") % ', '.join(sorted(state_map.keys()))
- raise exc.HTTPBadRequest(explanation=msg)
-
- instance = common.get_instance(self.compute_api, context, id)
- try:
- instance.vm_state = state
- instance.task_state = None
- instance.save(admin_state_reset=True)
- except exception.InstanceNotFound:
- msg = _("Server not found")
- raise exc.HTTPNotFound(explanation=msg)
- except Exception:
- readable = traceback.format_exc()
- LOG.exception(_LE("Compute.api::resetState %s"), readable)
- raise exc.HTTPUnprocessableEntity()
- return webob.Response(status_int=202)
-
-
-class Admin_actions(extensions.ExtensionDescriptor):
- """Enable admin-only server actions
-
- Actions include: pause, unpause, suspend, resume, migrate,
- resetNetwork, injectNetworkInfo, lock, unlock, createBackup
- """
-
- name = "AdminActions"
- alias = "os-admin-actions"
- namespace = "http://docs.openstack.org/compute/ext/admin-actions/api/v1.1"
- updated = "2011-09-20T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = AdminActionsController()
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/agents.py b/nova/api/openstack/compute/legacy_v2/contrib/agents.py
deleted file mode 100644
index 881926944f..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/agents.py
+++ /dev/null
@@ -1,193 +0,0 @@
-# Copyright 2012 IBM Corp.
-#
-# 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 import context as nova_context
-from nova import exception
-from nova.i18n import _
-from nova import objects
-from nova import utils
-
-
-authorize = extensions.extension_authorizer('compute', 'agents')
-
-
-class AgentController(object):
- """The agent is talking about guest agent.The host can use this for
- things like accessing files on the disk, configuring networking,
- or running other applications/scripts in the guest while it is
- running. Typically this uses some hypervisor-specific transport
- to avoid being dependent on a working network configuration.
- Xen, VMware, and VirtualBox have guest agents,although the Xen
- driver is the only one with an implementation for managing them
- in openstack. KVM doesn't really have a concept of a guest agent
- (although one could be written).
-
- You can find the design of agent update in this link:
- http://wiki.openstack.org/AgentUpdate
- and find the code in nova.virt.xenapi.vmops.VMOps._boot_new_instance.
- In this design We need update agent in guest from host, so we need
- some interfaces to update the agent info in host.
-
- You can find more information about the design of the GuestAgent in
- the following link:
- http://wiki.openstack.org/GuestAgent
- http://wiki.openstack.org/GuestAgentXenStoreCommunication
- """
- def index(self, req):
- """Return a list of all agent builds. Filter by hypervisor."""
- context = req.environ['nova.context']
- authorize(context)
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks.
- nova_context.require_admin_context(context)
- hypervisor = None
- agents = []
- if 'hypervisor' in req.GET:
- hypervisor = req.GET['hypervisor']
-
- builds = objects.AgentList.get_all(context, hypervisor=hypervisor)
- for agent_build in builds:
- agents.append({'hypervisor': agent_build.hypervisor,
- 'os': agent_build.os,
- 'architecture': agent_build.architecture,
- 'version': agent_build.version,
- 'md5hash': agent_build.md5hash,
- 'agent_id': agent_build.id,
- 'url': agent_build.url})
-
- return {'agents': agents}
-
- def update(self, req, id, body):
- """Update an existing agent build."""
- context = req.environ['nova.context']
- authorize(context)
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks.
- nova_context.require_admin_context(context)
- try:
- para = body['para']
- url = para['url']
- md5hash = para['md5hash']
- version = para['version']
- except (TypeError, KeyError) as ex:
- msg = _("Invalid request body: %s") % ex
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- try:
- utils.validate_integer(id, 'id')
- utils.check_string_length(url, 'url', max_length=255)
- utils.check_string_length(md5hash, 'md5hash', max_length=255)
- utils.check_string_length(version, 'version', max_length=255)
- except exception.InvalidInput as exc:
- raise webob.exc.HTTPBadRequest(explanation=exc.format_message())
-
- try:
- agent = objects.Agent(context=context, id=id)
- agent.obj_reset_changes()
- agent.version = version
- agent.url = url
- agent.md5hash = md5hash
- agent.save()
- except exception.AgentBuildNotFound as ex:
- raise webob.exc.HTTPNotFound(explanation=ex.format_message())
-
- # NOTE(alex_xu): The agent_id should be integer that consistent with
- # create/index actions. But parameter 'id' is string type that parsed
- # from url. This is a bug, but because back-compatibility, it can't be
- # fixed for v2 API. This will be fixed in v2.1 API by Microversions in
- # the future. lp bug #1333494
- return {"agent": {'agent_id': id, 'version': version,
- 'url': url, 'md5hash': md5hash}}
-
- def delete(self, req, id):
- """Deletes an existing agent build."""
- context = req.environ['nova.context']
- authorize(context)
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks.
- nova_context.require_admin_context(context)
- try:
- utils.validate_integer(id, 'id')
- except exception.InvalidInput as exc:
- raise webob.exc.HTTPBadRequest(explanation=exc.format_message())
-
- try:
- agent = objects.Agent(context=context, id=id)
- agent.destroy()
- except exception.AgentBuildNotFound as ex:
- raise webob.exc.HTTPNotFound(explanation=ex.format_message())
-
- def create(self, req, body):
- """Creates a new agent build."""
- context = req.environ['nova.context']
- authorize(context)
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks.
- nova_context.require_admin_context(context)
- try:
- agent = body['agent']
- hypervisor = agent['hypervisor']
- os = agent['os']
- architecture = agent['architecture']
- version = agent['version']
- url = agent['url']
- md5hash = agent['md5hash']
- except (TypeError, KeyError) as ex:
- msg = _("Invalid request body: %s") % ex
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- try:
- utils.check_string_length(hypervisor, 'hypervisor', max_length=255)
- utils.check_string_length(os, 'os', max_length=255)
- utils.check_string_length(architecture, 'architecture',
- max_length=255)
- utils.check_string_length(version, 'version', max_length=255)
- utils.check_string_length(url, 'url', max_length=255)
- utils.check_string_length(md5hash, 'md5hash', max_length=255)
- except exception.InvalidInput as exc:
- raise webob.exc.HTTPBadRequest(explanation=exc.format_message())
-
- try:
- agent_obj = objects.Agent(context=context)
- agent_obj.hypervisor = hypervisor
- agent_obj.os = os
- agent_obj.architecture = architecture
- agent_obj.version = version
- agent_obj.url = url
- agent_obj.md5hash = md5hash
- agent_obj.create()
- agent['agent_id'] = agent_obj.id
- except exception.AgentBuildExists as ex:
- raise webob.exc.HTTPConflict(explanation=ex.format_message())
- return {'agent': agent}
-
-
-class Agents(extensions.ExtensionDescriptor):
- """Agents support."""
-
- name = "Agents"
- alias = "os-agents"
- namespace = "http://docs.openstack.org/compute/ext/agents/api/v2"
- updated = "2012-10-28T00:00:00Z"
-
- def get_resources(self):
- resources = []
- resource = extensions.ResourceExtension('os-agents',
- AgentController())
- resources.append(resource)
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/aggregates.py b/nova/api/openstack/compute/legacy_v2/contrib/aggregates.py
deleted file mode 100644
index bebf942050..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/aggregates.py
+++ /dev/null
@@ -1,308 +0,0 @@
-# Copyright (c) 2012 Citrix Systems, Inc.
-# 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 Aggregate admin API extension."""
-
-import datetime
-
-import six
-from webob import exc
-
-from nova.api.openstack import extensions
-from nova.compute import api as compute_api
-from nova import context as nova_context
-from nova import exception
-from nova.i18n import _
-from nova import utils
-
-authorize = extensions.extension_authorizer('compute', 'aggregates')
-
-
-def _get_context(req):
- return req.environ['nova.context']
-
-
-def get_host_from_body(fn):
- """Makes sure that the host exists."""
- def wrapped(self, req, id, body, *args, **kwargs):
- if len(body) != 1:
- msg = _('Only host parameter can be specified')
- raise exc.HTTPBadRequest(explanation=msg)
- elif 'host' not in body:
- msg = _('Host parameter must be specified')
- raise exc.HTTPBadRequest(explanation=msg)
- try:
- utils.check_string_length(body['host'], 'host', 1, 255)
- except exception.InvalidInput as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- host = body['host']
-
- return fn(self, req, id, host, *args, **kwargs)
- return wrapped
-
-
-class AggregateController(object):
- """The Host Aggregates API controller for the OpenStack API."""
- def __init__(self):
- self.api = compute_api.AggregateAPI()
-
- def index(self, req):
- """Returns a list a host aggregate's id, name, availability_zone."""
- context = _get_context(req)
- authorize(context)
- aggregates = self.api.get_aggregate_list(context)
- return {'aggregates': [self._marshall_aggregate(a)['aggregate']
- for a in aggregates]}
-
- def create(self, req, body):
- """Creates an aggregate, given its name and
- optional availability zone.
- """
- context = _get_context(req)
- authorize(context)
-
- if len(body) != 1:
- raise exc.HTTPBadRequest()
- try:
- host_aggregate = body["aggregate"]
- name = host_aggregate["name"]
- except KeyError:
- raise exc.HTTPBadRequest()
- avail_zone = host_aggregate.get("availability_zone")
- try:
- utils.check_string_length(name, "Aggregate name", 1, 255)
- if avail_zone is not None:
- utils.check_string_length(avail_zone, "Availability_zone", 1,
- 255)
- except exception.InvalidInput as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- try:
- aggregate = self.api.create_aggregate(context, name, avail_zone)
- except exception.AggregateNameExists as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InvalidAggregateAction as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- agg = self._marshall_aggregate(aggregate)
-
- # To maintain the same API result as before the changes for returning
- # nova objects were made.
- del agg['aggregate']['hosts']
- del agg['aggregate']['metadata']
-
- return agg
-
- def show(self, req, id):
- """Shows the details of an aggregate, hosts and metadata included."""
- context = _get_context(req)
- authorize(context)
- try:
- aggregate = self.api.get_aggregate(context, id)
- except exception.AggregateNotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- return self._marshall_aggregate(aggregate)
-
- def update(self, req, id, body):
- """Updates the name and/or availability_zone of given aggregate."""
- context = _get_context(req)
- authorize(context)
-
- if len(body) != 1:
- raise exc.HTTPBadRequest()
- try:
- updates = body["aggregate"]
- except KeyError:
- raise exc.HTTPBadRequest()
-
- if len(updates) < 1:
- raise exc.HTTPBadRequest()
-
- for key in updates.keys():
- if key not in ["name", "availability_zone"]:
- raise exc.HTTPBadRequest()
-
- try:
- if 'name' in updates:
- utils.check_string_length(updates['name'], "Aggregate name", 1,
- 255)
- if updates.get("availability_zone") is not None:
- utils.check_string_length(updates['availability_zone'],
- "Availability_zone", 1, 255)
- except exception.InvalidInput as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- try:
- aggregate = self.api.update_aggregate(context, id, updates)
- except exception.AggregateNameExists as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.AggregateNotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- except exception.InvalidAggregateAction as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- return self._marshall_aggregate(aggregate)
-
- def delete(self, req, id):
- """Removes an aggregate by id."""
- context = _get_context(req)
- authorize(context)
- try:
- self.api.delete_aggregate(context, id)
- except exception.AggregateNotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- except exception.InvalidAggregateAction as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- def action(self, req, id, body):
- _actions = {
- 'add_host': self._add_host,
- 'remove_host': self._remove_host,
- 'set_metadata': self._set_metadata,
- }
- for action, data in six.iteritems(body):
- if action not in _actions.keys():
- msg = _('Aggregates does not have %s action') % action
- raise exc.HTTPBadRequest(explanation=msg)
- return _actions[action](req, id, data)
-
- raise exc.HTTPBadRequest(explanation=_("Invalid request body"))
-
- @get_host_from_body
- def _add_host(self, req, id, host):
- """Adds a host to the specified aggregate."""
- context = _get_context(req)
- authorize(context)
-
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks. This has to be left only for API v2.0 because
- # this version has to be stable even if it means that only admins
- # can call this method while the policy could be changed.
- nova_context.require_admin_context(context)
-
- try:
- aggregate = self.api.add_host_to_aggregate(context, id, host)
- except (exception.AggregateNotFound, exception.ComputeHostNotFound):
- msg = _('Cannot add host %(host)s in aggregate'
- ' %(id)s: not found') % {'host': host, 'id': id}
- raise exc.HTTPNotFound(explanation=msg)
- except (exception.AggregateHostExists,
- exception.InvalidAggregateAction):
- msg = _('Cannot add host %(host)s in aggregate'
- ' %(id)s: host exists') % {'host': host, 'id': id}
- raise exc.HTTPConflict(explanation=msg)
- return self._marshall_aggregate(aggregate)
-
- @get_host_from_body
- def _remove_host(self, req, id, host):
- """Removes a host from the specified aggregate."""
- context = _get_context(req)
- authorize(context)
-
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks. This has to be left only for API v2.0 because
- # this version has to be stable even if it means that only admins
- # can call this method while the policy could be changed.
- nova_context.require_admin_context(context)
-
- try:
- aggregate = self.api.remove_host_from_aggregate(context, id, host)
- except (exception.AggregateNotFound, exception.AggregateHostNotFound,
- exception.ComputeHostNotFound):
- msg = _('Cannot remove host %(host)s in aggregate'
- ' %(id)s: not found') % {'host': host, 'id': id}
- raise exc.HTTPNotFound(explanation=msg)
- except exception.InvalidAggregateAction:
- msg = _('Cannot remove host %(host)s in aggregate'
- ' %(id)s: invalid') % {'host': host, 'id': id}
- raise exc.HTTPConflict(explanation=msg)
- return self._marshall_aggregate(aggregate)
-
- def _set_metadata(self, req, id, body):
- """Replaces the aggregate's existing metadata with new metadata."""
- context = _get_context(req)
- authorize(context)
-
- if len(body) != 1:
- raise exc.HTTPBadRequest()
- try:
- metadata = body["metadata"]
- except KeyError:
- raise exc.HTTPBadRequest()
-
- # The metadata should be a dict
- if not isinstance(metadata, dict):
- msg = _('The value of metadata must be a dict')
- raise exc.HTTPBadRequest(explanation=msg)
- try:
- for key, value in metadata.items():
- utils.check_string_length(key, "metadata.key", 1, 255)
- if value is not None:
- utils.check_string_length(value, "metadata.value", 0, 255)
- except exception.InvalidInput as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
- try:
- aggregate = self.api.update_aggregate_metadata(context,
- id, metadata)
- except exception.AggregateNotFound:
- msg = _('Cannot set metadata %(metadata)s in aggregate'
- ' %(id)s') % {'metadata': metadata, 'id': id}
- raise exc.HTTPNotFound(explanation=msg)
- except exception.InvalidAggregateAction as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- return self._marshall_aggregate(aggregate)
-
- def _marshall_aggregate(self, aggregate):
- _aggregate = {}
- for key, value in self._build_aggregate_items(aggregate):
- # NOTE(danms): The original API specified non-TZ-aware timestamps
- if isinstance(value, datetime.datetime):
- value = value.replace(tzinfo=None)
- _aggregate[key] = value
- return {"aggregate": _aggregate}
-
- def _build_aggregate_items(self, aggregate):
- # NOTE(rlrossit): Within the compute API, metadata will always be
- # set on the aggregate object (at a minimum to {}). Because of this,
- # we can freely use getattr() on keys in obj_extra_fields (in this
- # case it is only ['availability_zone']) without worrying about
- # lazy-loading an unset variable
- keys = aggregate.obj_fields
- for key in keys:
- # NOTE(danms): Skip the uuid field because we have no microversion
- # to expose it
- if ((aggregate.obj_attr_is_set(key)
- or key in aggregate.obj_extra_fields) and
- key != 'uuid'):
- yield key, getattr(aggregate, key)
-
-
-class Aggregates(extensions.ExtensionDescriptor):
- """Admin-only aggregate administration."""
-
- name = "Aggregates"
- alias = "os-aggregates"
- namespace = "http://docs.openstack.org/compute/ext/aggregates/api/v1.1"
- updated = "2012-01-12T00:00:00Z"
-
- def get_resources(self):
- resources = []
- res = extensions.ResourceExtension('os-aggregates',
- AggregateController(),
- member_actions={"action": "POST", })
- resources.append(res)
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/assisted_volume_snapshots.py b/nova/api/openstack/compute/legacy_v2/contrib/assisted_volume_snapshots.py
deleted file mode 100644
index 73f965f13d..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/assisted_volume_snapshots.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# Copyright 2013 Red Hat, Inc.
-#
-# 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 oslo_log import log as logging
-from oslo_serialization import jsonutils
-import six
-import webob
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import compute
-from nova import exception
-from nova.i18n import _LI
-
-
-LOG = logging.getLogger(__name__)
-authorize = extensions.extension_authorizer('compute',
- 'os-assisted-volume-snapshots')
-
-
-class AssistedVolumeSnapshotsController(wsgi.Controller):
-
- def __init__(self):
- self.compute_api = compute.API()
- super(AssistedVolumeSnapshotsController, self).__init__()
-
- def create(self, req, body):
- """Creates a new snapshot."""
- context = req.environ['nova.context']
- authorize(context, action='create')
-
- if not self.is_valid_body(body, 'snapshot'):
- raise webob.exc.HTTPBadRequest()
-
- try:
- snapshot = body['snapshot']
- create_info = snapshot['create_info']
- volume_id = snapshot['volume_id']
- except KeyError:
- raise webob.exc.HTTPBadRequest()
-
- LOG.info(_LI("Create assisted snapshot from volume %s"), volume_id,
- context=context)
-
- return self.compute_api.volume_snapshot_create(context, volume_id,
- create_info)
-
- def delete(self, req, id):
- """Delete a snapshot."""
- context = req.environ['nova.context']
- authorize(context, action='delete')
-
- LOG.info(_LI("Delete snapshot with id: %s"), id, context=context)
-
- delete_metadata = {}
- delete_metadata.update(req.GET)
-
- try:
- delete_info = jsonutils.loads(delete_metadata['delete_info'])
- volume_id = delete_info['volume_id']
- except (KeyError, ValueError) as e:
- raise webob.exc.HTTPBadRequest(explanation=six.text_type(e))
-
- try:
- self.compute_api.volume_snapshot_delete(context, volume_id,
- id, delete_info)
- except exception.NotFound:
- return webob.exc.HTTPNotFound()
-
- return webob.Response(status_int=204)
-
-
-class Assisted_volume_snapshots(extensions.ExtensionDescriptor):
- """Assisted volume snapshots."""
-
- name = "AssistedVolumeSnapshots"
- alias = "os-assisted-volume-snapshots"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "assisted-volume-snapshots/api/v2")
- updated = "2013-08-29T00:00:00Z"
-
- def get_resources(self):
- resource = extensions.ResourceExtension('os-assisted-volume-snapshots',
- AssistedVolumeSnapshotsController())
-
- return [resource]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/attach_interfaces.py b/nova/api/openstack/compute/legacy_v2/contrib/attach_interfaces.py
deleted file mode 100644
index 6a92c4b9a4..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/attach_interfaces.py
+++ /dev/null
@@ -1,211 +0,0 @@
-# Copyright 2012 SINA Inc.
-# 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 instance interfaces extension."""
-
-import netaddr
-from oslo_log import log as logging
-import six
-import webob
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova import compute
-from nova import exception
-from nova.i18n import _
-from nova.i18n import _LI
-from nova import network
-
-
-LOG = logging.getLogger(__name__)
-authorize = extensions.extension_authorizer('compute', 'attach_interfaces')
-
-
-def _translate_interface_attachment_view(port_info):
- """Maps keys for interface attachment details view."""
- return {
- 'net_id': port_info['network_id'],
- 'port_id': port_info['id'],
- 'mac_addr': port_info['mac_address'],
- 'port_state': port_info['status'],
- 'fixed_ips': port_info.get('fixed_ips', None),
- }
-
-
-class InterfaceAttachmentController(object):
- """The interface attachment API controller for the OpenStack API."""
-
- def __init__(self):
- self.compute_api = compute.API()
- self.network_api = network.API()
- super(InterfaceAttachmentController, self).__init__()
-
- def index(self, req, server_id):
- """Returns the list of interface attachments for a given instance."""
- return self._items(req, server_id,
- entity_maker=_translate_interface_attachment_view)
-
- def show(self, req, server_id, id):
- """Return data about the given interface attachment."""
- context = req.environ['nova.context']
- authorize(context)
-
- port_id = id
- # NOTE(mriedem): We need to verify the instance actually exists from
- # the server_id even though we're not using the instance for anything,
- # just the port id.
- common.get_instance(self.compute_api, context, server_id)
-
- try:
- port_info = self.network_api.show_port(context, port_id)
- except exception.NotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- except exception.Forbidden as e:
- raise exc.HTTPForbidden(explanation=e.format_message())
-
- if port_info['port']['device_id'] != server_id:
- msg = _("Instance %(instance)s does not have a port with id "
- "%(port)s") % {'instance': server_id, 'port': port_id}
- raise exc.HTTPNotFound(explanation=msg)
-
- return {'interfaceAttachment': _translate_interface_attachment_view(
- port_info['port'])}
-
- def create(self, req, server_id, body):
- """Attach an interface to an instance."""
- context = req.environ['nova.context']
- authorize(context)
-
- network_id = None
- port_id = None
- req_ip = None
- if body:
- attachment = body['interfaceAttachment']
- network_id = attachment.get('net_id', None)
- port_id = attachment.get('port_id', None)
- try:
- req_ip = attachment['fixed_ips'][0]['ip_address']
- except Exception:
- pass
-
- if network_id and port_id:
- msg = _("Must not input both network_id and port_id")
- raise exc.HTTPBadRequest(explanation=msg)
- if req_ip and not network_id:
- msg = _("Must input network_id when request IP address")
- raise exc.HTTPBadRequest(explanation=msg)
-
- if req_ip:
- try:
- netaddr.IPAddress(req_ip)
- except netaddr.AddrFormatError as e:
- raise exc.HTTPBadRequest(explanation=six.text_type(e))
-
- try:
- instance = common.get_instance(self.compute_api,
- context, server_id)
- LOG.info(_LI("Attach interface"), instance=instance)
- vif = self.compute_api.attach_interface(context,
- instance, network_id, port_id, req_ip)
- except (exception.PortNotFound,
- exception.NetworkNotFound) as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- except (exception.FixedIpAlreadyInUse,
- exception.InterfaceAttachFailedNoNetwork,
- exception.NoMoreFixedIps,
- exception.PortInUse,
- exception.NetworkDuplicated,
- exception.NetworkAmbiguous,
- exception.PortNotUsable) as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except NotImplementedError:
- msg = _("Network driver does not support this function.")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
- except exception.InterfaceAttachFailed:
- msg = _("Failed to attach interface")
- raise webob.exc.HTTPInternalServerError(explanation=msg)
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'attach_interface', server_id)
-
- return self.show(req, server_id, vif['id'])
-
- def delete(self, req, server_id, id):
- """Detach an interface from an instance."""
- context = req.environ['nova.context']
- authorize(context)
- port_id = id
- instance = common.get_instance(self.compute_api,
- context, server_id)
- LOG.info(_LI("Detach interface %s"), port_id, instance=instance)
- try:
- self.compute_api.detach_interface(context,
- instance, port_id=port_id)
- except exception.PortNotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except NotImplementedError:
- msg = _("Network driver does not support this function.")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'detach_interface', server_id)
-
- return webob.Response(status_int=202)
-
- def _items(self, req, server_id, entity_maker):
- """Returns a list of attachments, transformed through entity_maker."""
- context = req.environ['nova.context']
- authorize(context)
- instance = common.get_instance(self.compute_api, context, server_id)
- search_opts = {'device_id': instance.uuid}
-
- try:
- data = self.network_api.list_ports(context, **search_opts)
- except exception.NotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- except NotImplementedError:
- msg = _("Network driver does not support this function.")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
-
- ports = data.get('ports', [])
- results = [entity_maker(port) for port in ports]
-
- return {'interfaceAttachments': results}
-
-
-class Attach_interfaces(extensions.ExtensionDescriptor):
- """Attach interface support."""
-
- name = "AttachInterfaces"
- alias = "os-attach-interfaces"
- namespace = "http://docs.openstack.org/compute/ext/interfaces/api/v1.1"
- updated = "2012-07-22T00:00:00Z"
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension('os-interface',
- InterfaceAttachmentController(),
- parent=dict(
- member_name='server',
- collection_name='servers'))
- resources.append(res)
-
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/availability_zone.py b/nova/api/openstack/compute/legacy_v2/contrib/availability_zone.py
deleted file mode 100644
index 490805f428..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/availability_zone.py
+++ /dev/null
@@ -1,144 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# 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 oslo_config import cfg
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import availability_zones
-from nova import context as nova_context
-from nova import objects
-from nova import servicegroup
-
-CONF = cfg.CONF
-
-authorize_list = extensions.extension_authorizer('compute',
- 'availability_zone:list')
-authorize_detail = extensions.extension_authorizer('compute',
- 'availability_zone:detail')
-
-
-class AvailabilityZoneController(wsgi.Controller):
- """The Availability Zone API controller for the OpenStack API."""
-
- def __init__(self):
- super(AvailabilityZoneController, self).__init__()
- self.servicegroup_api = servicegroup.API()
-
- def _get_filtered_availability_zones(self, zones, is_available):
- result = []
- for zone in zones:
- # Hide internal_service_availability_zone
- if zone == CONF.internal_service_availability_zone:
- continue
- result.append({'zoneName': zone,
- 'zoneState': {'available': is_available},
- "hosts": None})
- return result
-
- def _describe_availability_zones(self, context, **kwargs):
- ctxt = context.elevated()
- available_zones, not_available_zones = \
- availability_zones.get_availability_zones(ctxt)
-
- filtered_available_zones = \
- self._get_filtered_availability_zones(available_zones, True)
- filtered_not_available_zones = \
- self._get_filtered_availability_zones(not_available_zones, False)
- return {'availabilityZoneInfo': filtered_available_zones +
- filtered_not_available_zones}
-
- def _describe_availability_zones_verbose(self, context, **kwargs):
- ctxt = context.elevated()
- available_zones, not_available_zones = \
- availability_zones.get_availability_zones(ctxt)
-
- # Available services
- enabled_services = objects.ServiceList.get_all(context, disabled=False,
- set_zones=True)
- zone_hosts = {}
- host_services = {}
- api_services = ('nova-osapi_compute', 'nova-ec2', 'nova-metadata')
- for service in enabled_services:
- if service.binary in api_services:
- # Skip API services in the listing since they are not
- # maintained in the same way as other services
- continue
- zone_hosts.setdefault(service['availability_zone'], [])
- if service['host'] not in zone_hosts[service['availability_zone']]:
- zone_hosts[service['availability_zone']].append(
- service['host'])
-
- host_services.setdefault(service['availability_zone'] +
- service['host'], [])
- host_services[service['availability_zone'] + service['host']].\
- append(service)
-
- result = []
- for zone in available_zones:
- hosts = {}
- for host in zone_hosts.get(zone, []):
- hosts[host] = {}
- for service in host_services[zone + host]:
- alive = self.servicegroup_api.service_is_up(service)
- hosts[host][service['binary']] = {'available': alive,
- 'active': True != service['disabled'],
- 'updated_at': service['updated_at']}
- result.append({'zoneName': zone,
- 'zoneState': {'available': True},
- "hosts": hosts})
-
- for zone in not_available_zones:
- result.append({'zoneName': zone,
- 'zoneState': {'available': False},
- "hosts": None})
- return {'availabilityZoneInfo': result}
-
- def index(self, req):
- """Returns a summary list of availability zone."""
- context = req.environ['nova.context']
- authorize_list(context)
-
- return self._describe_availability_zones(context)
-
- def detail(self, req):
- """Returns a detailed list of availability zone."""
- context = req.environ['nova.context']
- authorize_detail(context)
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks.
- nova_context.require_admin_context(context)
- return self._describe_availability_zones_verbose(context)
-
-
-class Availability_zone(extensions.ExtensionDescriptor):
- """1. Add availability_zone to the Create Server v1.1 API.
- 2. Add availability zones describing.
- """
-
- name = "AvailabilityZone"
- alias = "os-availability-zone"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "availabilityzone/api/v1.1")
- updated = "2012-12-21T00:00:00Z"
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension('os-availability-zone',
- AvailabilityZoneController(),
- collection_actions={'detail': 'GET'})
- resources.append(res)
-
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/baremetal_ext_status.py b/nova/api/openstack/compute/legacy_v2/contrib/baremetal_ext_status.py
deleted file mode 100644
index 0f6c3a5940..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/baremetal_ext_status.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2013 OpenStack Foundation
-#
-# 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 extensions
-
-
-class Baremetal_ext_status(extensions.ExtensionDescriptor):
- """Add extended status in Baremetal Nodes v2 API."""
-
- name = "BareMetalExtStatus"
- alias = "os-baremetal-ext-status"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "baremetal_ext_status/api/v2")
- updated = "2013-08-27T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/baremetal_nodes.py b/nova/api/openstack/compute/legacy_v2/contrib/baremetal_nodes.py
deleted file mode 100644
index 6033c53f03..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/baremetal_nodes.py
+++ /dev/null
@@ -1,163 +0,0 @@
-# Copyright (c) 2013 NTT DOCOMO, INC.
-# 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 bare-metal admin extension with Ironic Proxy."""
-
-from oslo_utils import importutils
-import webob
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-import nova.conf
-from nova.i18n import _
-
-ironic_client = importutils.try_import('ironicclient.client')
-ironic_exc = importutils.try_import('ironicclient.exc')
-
-authorize = extensions.extension_authorizer('compute', 'baremetal_nodes')
-
-node_fields = ['id', 'cpus', 'local_gb', 'memory_mb', 'pm_address',
- 'pm_user', 'service_host', 'terminal_port', 'instance_uuid']
-
-node_ext_fields = ['uuid', 'task_state', 'updated_at', 'pxe_config_path']
-
-interface_fields = ['id', 'address', 'datapath_id', 'port_no']
-
-CONF = nova.conf.CONF
-
-
-def _check_ironic_client_enabled():
- """Check whether Ironic is installed or not."""
- if ironic_client is None:
- msg = _("Ironic client unavailable, cannot access Ironic.")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
-
-
-def _get_ironic_client():
- """return an Ironic client."""
- # TODO(NobodyCam): Fix insecure setting
- kwargs = {'os_username': CONF.ironic.admin_username,
- 'os_password': CONF.ironic.admin_password,
- 'os_auth_url': CONF.ironic.admin_url,
- 'os_tenant_name': CONF.ironic.admin_tenant_name,
- 'os_service_type': 'baremetal',
- 'os_endpoint_type': 'public',
- 'insecure': 'true',
- 'ironic_url': CONF.ironic.api_endpoint}
- ironicclient = ironic_client.get_client(CONF.ironic.api_version, **kwargs)
- return ironicclient
-
-
-def _no_ironic_proxy(cmd):
- raise webob.exc.HTTPBadRequest(
- explanation=_("Command Not supported. Please use Ironic "
- "command %(cmd)s to perform this "
- "action.") % {'cmd': cmd})
-
-
-class BareMetalNodeController(wsgi.Controller):
- """The Bare-Metal Node API controller for the OpenStack API.
-
- Ironic is used for the following commands:
- 'baremetal-node-list'
- 'baremetal-node-show'
- """
-
- def __init__(self, ext_mgr=None, *args, **kwargs):
- super(BareMetalNodeController, self).__init__(*args, **kwargs)
- self.ext_mgr = ext_mgr
-
- def _node_dict(self, node_ref):
- d = {}
- for f in node_fields:
- d[f] = node_ref.get(f)
- if self.ext_mgr.is_loaded('os-baremetal-ext-status'):
- for f in node_ext_fields:
- d[f] = node_ref.get(f)
- return d
-
- def index(self, req):
- context = req.environ['nova.context']
- authorize(context)
- nodes = []
- # proxy command to Ironic
- _check_ironic_client_enabled()
- ironicclient = _get_ironic_client()
- ironic_nodes = ironicclient.node.list(detail=True)
- for inode in ironic_nodes:
- node = {'id': inode.uuid,
- 'interfaces': [],
- 'host': 'IRONIC MANAGED',
- 'task_state': inode.provision_state,
- 'cpus': inode.properties.get('cpus', 0),
- 'memory_mb': inode.properties.get('memory_mb', 0),
- 'disk_gb': inode.properties.get('local_gb', 0)}
- nodes.append(node)
- return {'nodes': nodes}
-
- def show(self, req, id):
- context = req.environ['nova.context']
- authorize(context)
- # proxy command to Ironic
- _check_ironic_client_enabled()
- icli = _get_ironic_client()
- try:
- inode = icli.node.get(id)
- except ironic_exc.NotFound:
- msg = _("Node %s could not be found.") % id
- raise webob.exc.HTTPNotFound(explanation=msg)
- iports = icli.node.list_ports(id)
- node = {'id': inode.uuid,
- 'interfaces': [],
- 'host': 'IRONIC MANAGED',
- 'task_state': inode.provision_state,
- 'cpus': inode.properties.get('cpus', 0),
- 'memory_mb': inode.properties.get('memory_mb', 0),
- 'disk_gb': inode.properties.get('local_gb', 0),
- 'instance_uuid': inode.instance_uuid}
- for port in iports:
- node['interfaces'].append({'address': port.address})
- return {'node': node}
-
- def create(self, req, body):
- _no_ironic_proxy("port-create")
-
- def delete(self, req, id):
- _no_ironic_proxy("port-create")
-
- @wsgi.action('add_interface')
- def _add_interface(self, req, id, body):
- _no_ironic_proxy("port-create")
-
- @wsgi.action('remove_interface')
- def _remove_interface(self, req, id, body):
- _no_ironic_proxy("port-delete")
-
-
-class Baremetal_nodes(extensions.ExtensionDescriptor):
- """Admin-only bare-metal node administration."""
-
- name = "BareMetalNodes"
- alias = "os-baremetal-nodes"
- namespace = "http://docs.openstack.org/compute/ext/baremetal_nodes/api/v2"
- updated = "2013-01-04T00:00:00Z"
-
- def get_resources(self):
- resources = []
- res = extensions.ResourceExtension('os-baremetal-nodes',
- BareMetalNodeController(self.ext_mgr),
- member_actions={"action": "POST", })
- resources.append(res)
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/block_device_mapping_v2_boot.py b/nova/api/openstack/compute/legacy_v2/contrib/block_device_mapping_v2_boot.py
deleted file mode 100644
index 463a9da702..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/block_device_mapping_v2_boot.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# 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 extensions
-
-
-class Block_device_mapping_v2_boot(extensions.ExtensionDescriptor):
- """Allow boot with the new BDM data format."""
-
- name = "BlockDeviceMappingV2Boot"
- alias = "os-block-device-mapping-v2-boot"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "block_device_mapping_v2_boot/api/v2")
- updated = "2013-07-08T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/cell_capacities.py b/nova/api/openstack/compute/legacy_v2/contrib/cell_capacities.py
deleted file mode 100644
index 179d4f6a3b..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/cell_capacities.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2013 Rackspace Hosting
-#
-# 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 extensions
-
-
-class Cell_capacities(extensions.ExtensionDescriptor):
- """Adding functionality to get cell capacities."""
-
- name = "CellCapacities"
- alias = "os-cell-capacities"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "cell_capacities/api/v1.1")
- updated = "2013-05-27T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/cells.py b/nova/api/openstack/compute/legacy_v2/contrib/cells.py
deleted file mode 100644
index 4c09d31239..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/cells.py
+++ /dev/null
@@ -1,370 +0,0 @@
-# Copyright 2011-2012 OpenStack Foundation
-# All Rights Reserved.
-# Copyright 2013 Red Hat, Inc.
-#
-# 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 cells extension."""
-
-import oslo_messaging as messaging
-from oslo_utils import strutils
-from oslo_utils import timeutils
-import six
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.cells import rpcapi as cells_rpcapi
-import nova.conf
-from nova import context as nova_context
-from nova import exception
-from nova.i18n import _
-from nova import rpc
-
-
-CONF = nova.conf.CONF
-
-authorize = extensions.extension_authorizer('compute', 'cells')
-
-
-def _filter_keys(item, keys):
- """Filters all model attributes except for keys
- item is a dict
-
- """
- return {k: v for k, v in six.iteritems(item) if k in keys}
-
-
-def _fixup_cell_info(cell_info, keys):
- """If the transport_url is present in the cell, derive username,
- rpc_host, and rpc_port from it.
- """
-
- if 'transport_url' not in cell_info:
- return
-
- # Disassemble the transport URL
- transport_url = cell_info.pop('transport_url')
- try:
- transport_url = rpc.get_transport_url(transport_url)
- except messaging.InvalidTransportURL:
- # Just go with None's
- for key in keys:
- cell_info.setdefault(key, None)
- return
-
- if not transport_url.hosts:
- return
-
- transport_host = transport_url.hosts[0]
-
- transport_field_map = {'rpc_host': 'hostname', 'rpc_port': 'port'}
- for key in keys:
- if key in cell_info:
- continue
-
- transport_field = transport_field_map.get(key, key)
- cell_info[key] = getattr(transport_host, transport_field)
-
-
-def _scrub_cell(cell, detail=False):
- keys = ['name', 'username', 'rpc_host', 'rpc_port']
- if detail:
- keys.append('capabilities')
-
- cell_info = _filter_keys(cell, keys + ['transport_url'])
- _fixup_cell_info(cell_info, keys)
- cell_info['type'] = 'parent' if cell['is_parent'] else 'child'
- return cell_info
-
-
-class Controller(object):
- """Controller for Cell resources."""
-
- def __init__(self, ext_mgr):
- self.cells_rpcapi = cells_rpcapi.CellsAPI()
- self.ext_mgr = ext_mgr
-
- def _get_cells(self, ctxt, req, detail=False):
- """Return all cells."""
- # Ask the CellsManager for the most recent data
- items = self.cells_rpcapi.get_cell_info_for_neighbors(ctxt)
- items = common.limited(items, req)
- items = [_scrub_cell(item, detail=detail) for item in items]
- return dict(cells=items)
-
- @common.check_cells_enabled
- def index(self, req):
- """Return all cells in brief."""
- ctxt = req.environ['nova.context']
- authorize(ctxt)
- return self._get_cells(ctxt, req)
-
- @common.check_cells_enabled
- def detail(self, req):
- """Return all cells in detail."""
- ctxt = req.environ['nova.context']
- authorize(ctxt)
- return self._get_cells(ctxt, req, detail=True)
-
- @common.check_cells_enabled
- def info(self, req):
- """Return name and capabilities for this cell."""
- context = req.environ['nova.context']
- authorize(context)
- cell_capabs = {}
- my_caps = CONF.cells.capabilities
- for cap in my_caps:
- key, value = cap.split('=')
- cell_capabs[key] = value
- cell = {'name': CONF.cells.name,
- 'type': 'self',
- 'rpc_host': None,
- 'rpc_port': 0,
- 'username': None,
- 'capabilities': cell_capabs}
- return dict(cell=cell)
-
- @common.check_cells_enabled
- def capacities(self, req, id=None):
- """Return capacities for a given cell or all cells."""
- # TODO(kaushikc): return capacities as a part of cell info and
- # cells detail calls in v2.1, along with capabilities
- if not self.ext_mgr.is_loaded('os-cell-capacities'):
- raise exc.HTTPNotFound()
-
- context = req.environ['nova.context']
- authorize(context)
- try:
- capacities = self.cells_rpcapi.get_capacities(context,
- cell_name=id)
- except exception.CellNotFound:
- msg = (_("Cell %(id)s not found.") % {'id': id})
- raise exc.HTTPNotFound(explanation=msg)
-
- return dict(cell={"capacities": capacities})
-
- @common.check_cells_enabled
- def show(self, req, id):
- """Return data about the given cell name. 'id' is a cell name."""
- context = req.environ['nova.context']
- authorize(context)
- try:
- cell = self.cells_rpcapi.cell_get(context, id)
- except exception.CellNotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- return dict(cell=_scrub_cell(cell))
-
- @common.check_cells_enabled
- def delete(self, req, id):
- """Delete a child or parent cell entry. 'id' is a cell name."""
- context = req.environ['nova.context']
-
- authorize(context)
- authorize(context, action="delete")
- # NOTE(eliqiao): back-compatible with db layer hard-code admin
- # permission checks.
- nova_context.require_admin_context(context)
-
- try:
- num_deleted = self.cells_rpcapi.cell_delete(context, id)
- except exception.CellsUpdateUnsupported as e:
- raise exc.HTTPForbidden(explanation=e.format_message())
- if num_deleted == 0:
- raise exc.HTTPNotFound()
- return {}
-
- def _validate_cell_name(self, cell_name):
- """Validate cell name is not empty and doesn't contain '!',
- '.' or '@'.
- """
- if not cell_name:
- msg = _("Cell name cannot be empty")
- raise exc.HTTPBadRequest(explanation=msg)
- if '!' in cell_name or '.' in cell_name or '@' in cell_name:
- msg = _("Cell name cannot contain '!', '.' or '@'")
- raise exc.HTTPBadRequest(explanation=msg)
-
- def _validate_cell_type(self, cell_type):
- """Validate cell_type is 'parent' or 'child'."""
- if cell_type not in ['parent', 'child']:
- msg = _("Cell type must be 'parent' or 'child'")
- raise exc.HTTPBadRequest(explanation=msg)
-
- def _normalize_cell(self, cell, existing=None):
- """Normalize input cell data. Normalizations include:
-
- * Converting cell['type'] to is_parent boolean.
- * Merging existing transport URL with transport information.
- """
-
- # Start with the cell type conversion
- if 'type' in cell:
- self._validate_cell_type(cell['type'])
- cell['is_parent'] = cell['type'] == 'parent'
- del cell['type']
- # Avoid cell type being overwritten to 'child'
- elif existing:
- cell['is_parent'] = existing['is_parent']
- else:
- cell['is_parent'] = False
-
- # Now we disassemble the existing transport URL...
- transport_url = existing.get('transport_url') if existing else None
- transport_url = rpc.get_transport_url(transport_url)
-
- if 'rpc_virtual_host' in cell:
- transport_url.virtual_host = cell.pop('rpc_virtual_host')
-
- if not transport_url.hosts:
- transport_url.hosts.append(messaging.TransportHost())
- transport_host = transport_url.hosts[0]
- if cell.get('rpc_port') is not None:
- try:
- cell['rpc_port'] = int(cell['rpc_port'])
- except ValueError:
- raise exc.HTTPBadRequest(
- explanation=_('rpc_port must be integer'))
- # Copy over the input fields
- transport_field_map = {
- 'username': 'username',
- 'password': 'password',
- 'hostname': 'rpc_host',
- 'port': 'rpc_port',
- }
- for key, input_field in transport_field_map.items():
- # Only override the value if we're given an override
- if input_field in cell:
- setattr(transport_host, key, cell.pop(input_field))
-
- # Now set the transport URL
- cell['transport_url'] = str(transport_url)
-
- @common.check_cells_enabled
- def create(self, req, body):
- """Create a child cell entry."""
- context = req.environ['nova.context']
-
- authorize(context)
- authorize(context, action="create")
- # NOTE(eliqiao): back-compatible with db layer hard-code admin
- # permission checks.
- nova_context.require_admin_context(context)
-
- if 'cell' not in body:
- msg = _("No cell information in request")
- raise exc.HTTPBadRequest(explanation=msg)
- cell = body['cell']
- if 'name' not in cell:
- msg = _("No cell name in request")
- raise exc.HTTPBadRequest(explanation=msg)
- self._validate_cell_name(cell['name'])
- self._normalize_cell(cell)
- try:
- cell = self.cells_rpcapi.cell_create(context, cell)
- except exception.CellsUpdateUnsupported as e:
- raise exc.HTTPForbidden(explanation=e.format_message())
- return dict(cell=_scrub_cell(cell))
-
- @common.check_cells_enabled
- def update(self, req, id, body):
- """Update a child cell entry. 'id' is the cell name to update."""
- context = req.environ['nova.context']
-
- authorize(context)
- authorize(context, action="update")
- # NOTE(eliqiao): back-compatible with db layer hard-code admin
- # permission checks.
- nova_context.require_admin_context(context)
-
- if 'cell' not in body:
- msg = _("No cell information in request")
- raise exc.HTTPBadRequest(explanation=msg)
- cell = body['cell']
- cell.pop('id', None)
- if 'name' in cell:
- self._validate_cell_name(cell['name'])
- try:
- # NOTE(Vek): There is a race condition here if multiple
- # callers are trying to update the cell
- # information simultaneously. Since this
- # operation is administrative in nature, and
- # will be going away in the future, I don't see
- # it as much of a problem...
- existing = self.cells_rpcapi.cell_get(context, id)
- except exception.CellNotFound:
- raise exc.HTTPNotFound()
- self._normalize_cell(cell, existing)
- try:
- cell = self.cells_rpcapi.cell_update(context, id, cell)
- except exception.CellNotFound:
- raise exc.HTTPNotFound()
- except exception.CellsUpdateUnsupported as e:
- raise exc.HTTPForbidden(explanation=e.format_message())
- return dict(cell=_scrub_cell(cell))
-
- @common.check_cells_enabled
- def sync_instances(self, req, body):
- """Tell all cells to sync instance info."""
- context = req.environ['nova.context']
-
- authorize(context)
- authorize(context, action="sync_instances")
-
- project_id = body.pop('project_id', None)
- deleted = body.pop('deleted', False)
- updated_since = body.pop('updated_since', None)
- if body:
- msg = _("Only 'updated_since', 'project_id' and 'deleted' are "
- "understood.")
- raise exc.HTTPBadRequest(explanation=msg)
- if isinstance(deleted, six.string_types):
- try:
- deleted = strutils.bool_from_string(deleted, strict=True)
- except ValueError as err:
- raise exc.HTTPBadRequest(explanation=six.text_type(err))
- if updated_since:
- try:
- timeutils.parse_isotime(updated_since)
- except ValueError:
- msg = _('Invalid changes-since value')
- raise exc.HTTPBadRequest(explanation=msg)
- self.cells_rpcapi.sync_instances(context, project_id=project_id,
- updated_since=updated_since, deleted=deleted)
-
-
-class Cells(extensions.ExtensionDescriptor):
- """Enables cells-related functionality such as adding neighbor cells,
- listing neighbor cells, and getting the capabilities of the local cell.
- """
-
- name = "Cells"
- alias = "os-cells"
- namespace = "http://docs.openstack.org/compute/ext/cells/api/v1.1"
- updated = "2013-05-14T00:00:00Z"
-
- def get_resources(self):
- coll_actions = {
- 'detail': 'GET',
- 'info': 'GET',
- 'sync_instances': 'POST',
- 'capacities': 'GET',
- }
- memb_actions = {
- 'capacities': 'GET',
- }
-
- res = extensions.ResourceExtension('os-cells',
- Controller(self.ext_mgr), collection_actions=coll_actions,
- member_actions=memb_actions)
- return [res]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/certificates.py b/nova/api/openstack/compute/legacy_v2/contrib/certificates.py
deleted file mode 100644
index 1b30c6600a..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/certificates.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright (c) 2012 OpenStack Foundation
-#
-# 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
-import nova.cert.rpcapi
-from nova import exception
-from nova.i18n import _
-
-authorize = extensions.extension_authorizer('compute', 'certificates')
-
-
-def _translate_certificate_view(certificate, private_key=None):
- return {
- 'data': certificate,
- 'private_key': private_key,
- }
-
-
-class CertificatesController(object):
- """The x509 Certificates API controller for the OpenStack API."""
-
- def __init__(self):
- self.cert_rpcapi = nova.cert.rpcapi.CertAPI()
- super(CertificatesController, self).__init__()
-
- def show(self, req, id):
- """Return certificate information."""
- context = req.environ['nova.context']
- authorize(context)
- if id != 'root':
- msg = _("Only root certificate can be retrieved.")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
- try:
- cert = self.cert_rpcapi.fetch_ca(context,
- project_id=context.project_id)
- except exception.CryptoCAFileNotFound as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
- return {'certificate': _translate_certificate_view(cert)}
-
- def create(self, req, body=None):
- """Create a certificate."""
- context = req.environ['nova.context']
- authorize(context)
- pk, cert = self.cert_rpcapi.generate_x509_cert(context,
- user_id=context.user_id, project_id=context.project_id)
- return {'certificate': _translate_certificate_view(cert, pk)}
-
-
-class Certificates(extensions.ExtensionDescriptor):
- """Certificates support."""
-
- name = "Certificates"
- alias = "os-certificates"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "certificates/api/v1.1")
- updated = "2012-01-19T00:00:00Z"
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension('os-certificates',
- CertificatesController(),
- member_actions={})
- resources.append(res)
-
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/cloudpipe.py b/nova/api/openstack/compute/legacy_v2/contrib/cloudpipe.py
deleted file mode 100644
index 2c46b864a2..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/cloudpipe.py
+++ /dev/null
@@ -1,160 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-#
-# 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.
-
-"""Connect your vlan to the world."""
-
-from oslo_utils import fileutils
-from webob import exc
-
-from nova.api.openstack import extensions
-from nova.cloudpipe import pipelib
-from nova import compute
-from nova.compute import utils as compute_utils
-from nova.compute import vm_states
-import nova.conf
-from nova import exception
-from nova.i18n import _
-from nova import network
-from nova import utils
-
-CONF = nova.conf.CONF
-
-authorize = extensions.extension_authorizer('compute', 'cloudpipe')
-
-
-class CloudpipeController(object):
- """Handle creating and listing cloudpipe instances."""
-
- def __init__(self):
- self.compute_api = compute.API()
- self.network_api = network.API()
- self.cloudpipe = pipelib.CloudPipe()
- self.setup()
-
- def setup(self):
- """Ensure the keychains and folders exist."""
- # NOTE(vish): One of the drawbacks of doing this in the api is
- # the keys will only be on the api node that launched
- # the cloudpipe.
- fileutils.ensure_tree(CONF.crypto.keys_path)
-
- def _get_all_cloudpipes(self, context):
- """Get all cloudpipes."""
- instances = self.compute_api.get_all(context,
- search_opts={'deleted': False},
- want_objects=True)
- return [instance for instance in instances
- if pipelib.is_vpn_image(instance.image_ref)
- and instance.vm_state != vm_states.DELETED]
-
- def _get_cloudpipe_for_project(self, context):
- """Get the cloudpipe instance for a project from context."""
- cloudpipes = self._get_all_cloudpipes(context) or [None]
- return cloudpipes[0]
-
- def _vpn_dict(self, context, project_id, instance):
- elevated = context.elevated()
- rv = {'project_id': project_id}
- if not instance:
- rv['state'] = 'pending'
- return rv
- rv['instance_id'] = instance.uuid
- rv['created_at'] = utils.isotime(instance.created_at)
- nw_info = compute_utils.get_nw_info_for_instance(instance)
- if not nw_info:
- return rv
- vif = nw_info[0]
- ips = [ip for ip in vif.fixed_ips() if ip['version'] == 4]
- if ips:
- rv['internal_ip'] = ips[0]['address']
- # NOTE(vish): Currently network_api.get does an owner check on
- # project_id. This is probably no longer necessary
- # but rather than risk changes in the db layer,
- # we are working around it here by changing the
- # project_id in the context. This can be removed
- # if we remove the project_id check in the db.
- elevated.project_id = project_id
- network = self.network_api.get(elevated, vif['network']['id'])
- if network:
- vpn_ip = network['vpn_public_address']
- vpn_port = network['vpn_public_port']
- rv['public_ip'] = vpn_ip
- rv['public_port'] = vpn_port
- if vpn_ip and vpn_port:
- if utils.vpn_ping(vpn_ip, vpn_port):
- rv['state'] = 'running'
- else:
- rv['state'] = 'down'
- else:
- rv['state'] = 'invalid'
- return rv
-
- def create(self, req, body):
- """Create a new cloudpipe instance, if none exists.
-
- Parameters: {cloudpipe: {'project_id': ''}}
- """
-
- context = req.environ['nova.context']
- authorize(context)
- params = body.get('cloudpipe', {})
- project_id = params.get('project_id', context.project_id)
- # NOTE(vish): downgrade to project context. Note that we keep
- # the same token so we can still talk to glance
- context.project_id = project_id
- context.user_id = 'project-vpn'
- context.is_admin = False
- context.roles = []
- instance = self._get_cloudpipe_for_project(context)
- if not instance:
- try:
- result = self.cloudpipe.launch_vpn_instance(context)
- instance = result[0][0]
- except exception.NoMoreNetworks:
- msg = _("Unable to claim IP for VPN instances, ensure it "
- "isn't running, and try again in a few minutes")
- raise exc.HTTPBadRequest(explanation=msg)
- return {'instance_id': instance.uuid}
-
- def index(self, req):
- """List running cloudpipe instances."""
- context = req.environ['nova.context']
- authorize(context)
- vpns = [self._vpn_dict(context, x['project_id'], x)
- for x in self._get_all_cloudpipes(context)]
- return {'cloudpipes': vpns}
-
-
-class Cloudpipe(extensions.ExtensionDescriptor):
- """Adds actions to create cloudpipe instances.
-
- When running with the Vlan network mode, you need a mechanism to route
- from the public Internet to your vlans. This mechanism is known as a
- cloudpipe.
-
- At the time of creating this class, only OpenVPN is supported. Support for
- a SSH Bastion host is forthcoming.
- """
-
- name = "Cloudpipe"
- alias = "os-cloudpipe"
- namespace = "http://docs.openstack.org/compute/ext/cloudpipe/api/v1.1"
- updated = "2011-12-16T00:00:00Z"
-
- def get_resources(self):
- resources = []
- res = extensions.ResourceExtension('os-cloudpipe',
- CloudpipeController())
- resources.append(res)
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/cloudpipe_update.py b/nova/api/openstack/compute/legacy_v2/contrib/cloudpipe_update.py
deleted file mode 100644
index 8e62879062..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/cloudpipe_update.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# Copyright 2012 IBM Corp.
-#
-# 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
-import webob.exc
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova.i18n import _
-from nova import objects
-
-authorize = extensions.extension_authorizer('compute', 'cloudpipe_update')
-
-
-class CloudpipeUpdateController(wsgi.Controller):
- """Handle updating the VPN IP/port for cloudpipe instances."""
-
- def __init__(self):
- super(CloudpipeUpdateController, self).__init__()
-
- @wsgi.action("update")
- def update(self, req, id, body):
- """Configure cloudpipe parameters for the project."""
-
- context = req.environ['nova.context']
- authorize(context)
-
- if id != "configure-project":
- msg = _("Unknown action %s") % id
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- project_id = context.project_id
- networks = objects.NetworkList.get_by_project(context, project_id)
-
- try:
- params = body['configure_project']
- vpn_ip = params['vpn_ip']
- vpn_port = params['vpn_port']
- for network in networks:
- network.vpn_public_address = vpn_ip
- network.vpn_public_port = vpn_port
- network.save()
- except (TypeError, KeyError, ValueError) as ex:
- msg = _("Invalid request body: %s") % ex
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- return webob.Response(status_int=202)
-
-
-class Cloudpipe_update(extensions.ExtensionDescriptor):
- """Adds the ability to set the VPN IP/port for cloudpipe instances."""
-
- name = "CloudpipeUpdate"
- alias = "os-cloudpipe-update"
- namespace = "http://docs.openstack.org/compute/ext/cloudpipe-update/api/v2"
- updated = "2012-11-14T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = CloudpipeUpdateController()
- extension = extensions.ControllerExtension(self, 'os-cloudpipe',
- controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/config_drive.py b/nova/api/openstack/compute/legacy_v2/contrib/config_drive.py
deleted file mode 100644
index bd7ff41915..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/config_drive.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# 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.
-
-"""Config Drive extension."""
-
-from nova.api.openstack.compute.legacy_v2 import servers
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-
-authorize = extensions.soft_extension_authorizer('compute', 'config_drive')
-
-
-class Controller(servers.Controller):
-
- def _add_config_drive(self, req, servers):
- for server in servers:
- db_server = req.get_db_instance(server['id'])
- # server['id'] is guaranteed to be in the cache due to
- # the core API adding it in its 'show'/'detail' methods.
- server['config_drive'] = db_server['config_drive']
-
- def _show(self, req, resp_obj):
- if 'server' in resp_obj.obj:
- server = resp_obj.obj['server']
- self._add_config_drive(req, [server])
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- context = req.environ['nova.context']
- if authorize(context):
- self._show(req, resp_obj)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- context = req.environ['nova.context']
- if 'servers' in resp_obj.obj and authorize(context):
- servers = resp_obj.obj['servers']
- self._add_config_drive(req, servers)
-
-
-class Config_drive(extensions.ExtensionDescriptor):
- """Config Drive Extension."""
-
- name = "ConfigDrive"
- alias = "os-config-drive"
- namespace = "http://docs.openstack.org/compute/ext/config_drive/api/v1.1"
- updated = "2012-07-16T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = Controller(self.ext_mgr)
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/console_auth_tokens.py b/nova/api/openstack/compute/legacy_v2/contrib/console_auth_tokens.py
deleted file mode 100644
index 9b41f41bf4..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/console_auth_tokens.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# Copyright 2013 Cloudbase Solutions Srl
-# 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.
-
-import webob
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova.consoleauth import rpcapi as consoleauth_rpcapi
-from nova.i18n import _
-
-
-authorize = extensions.extension_authorizer('compute', 'console_auth_tokens')
-
-
-class ConsoleAuthTokensController(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- self._consoleauth_rpcapi = consoleauth_rpcapi.ConsoleAuthAPI()
- super(ConsoleAuthTokensController, self).__init__(*args, **kwargs)
-
- def show(self, req, id):
- """Checks a console auth token and returns the related connect info."""
- context = req.environ['nova.context']
- authorize(context)
-
- token = id
- connect_info = self._consoleauth_rpcapi.check_token(context, token)
- if not connect_info:
- raise webob.exc.HTTPNotFound(explanation=_("Token not found"))
-
- console_type = connect_info.get('console_type')
- # This is currently required only for RDP consoles
- if console_type != "rdp-html5":
- raise webob.exc.HTTPUnauthorized(
- explanation=_("The requested console type details are not "
- "accessible"))
-
- return {'console':
- {i: connect_info[i]
- for i in ['instance_uuid', 'host', 'port',
- 'internal_access_path']
- if i in connect_info}}
-
-
-class Console_auth_tokens(extensions.ExtensionDescriptor):
- """Console token authentication support."""
- name = "ConsoleAuthTokens"
- alias = "os-console-auth-tokens"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "consoles-auth-tokens/api/v2")
- updated = "2013-08-13T00:00:00Z"
-
- def get_resources(self):
- controller = ConsoleAuthTokensController()
- ext = extensions.ResourceExtension('os-console-auth-tokens',
- controller)
- return [ext]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/console_output.py b/nova/api/openstack/compute/legacy_v2/contrib/console_output.py
deleted file mode 100644
index 2956e12596..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/console_output.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-# Copyright 2011 Grid Dynamics
-# Copyright 2011 Eldar Nugaev, Kirill Shileev, Ilya Alekseyev
-#
-# 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 re
-
-import webob
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import compute
-from nova import exception
-from nova.i18n import _
-
-
-authorize = extensions.extension_authorizer('compute', 'console_output')
-
-
-class ConsoleOutputController(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- super(ConsoleOutputController, self).__init__(*args, **kwargs)
- self.compute_api = compute.API()
-
- @wsgi.action('os-getConsoleOutput')
- def get_console_output(self, req, id, body):
- """Get text console output."""
- context = req.environ['nova.context']
- authorize(context)
-
- instance = common.get_instance(self.compute_api, context, id)
- try:
- length = body['os-getConsoleOutput'].get('length')
- except (TypeError, KeyError):
- raise webob.exc.HTTPBadRequest(_('os-getConsoleOutput malformed '
- 'or missing from request body'))
-
- if length is not None:
- try:
- # NOTE(maurosr): cast length into a string before cast into an
- # integer to avoid thing like: int(2.5) which is 2 instead of
- # raise ValueError like it would when we try int("2.5"). This
- # can be removed once we have api validation landed.
- int(str(length))
- except ValueError:
- raise webob.exc.HTTPBadRequest(_('Length in request body must '
- 'be an integer value'))
-
- try:
- output = self.compute_api.get_console_output(context,
- instance,
- length)
- except exception.ConsoleNotAvailable as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
- except exception.NotFound:
- msg = _('Unable to get console')
- raise webob.exc.HTTPNotFound(explanation=msg)
- except exception.InstanceNotReady as e:
- raise webob.exc.HTTPConflict(explanation=e.format_message())
- except NotImplementedError:
- msg = _("Unable to get console log, functionality not implemented")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
-
- # XML output is not correctly escaped, so remove invalid characters
- remove_re = re.compile('[\x00-\x08\x0B-\x1F]')
- output = remove_re.sub('', output)
-
- return {'output': output}
-
-
-class Console_output(extensions.ExtensionDescriptor):
- """Console log output support, with tailing ability."""
-
- name = "ConsoleOutput"
- alias = "os-console-output"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "os-console-output/api/v2")
- updated = "2011-12-08T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = ConsoleOutputController()
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/consoles.py b/nova/api/openstack/compute/legacy_v2/contrib/consoles.py
deleted file mode 100644
index 0a8a00aac3..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/consoles.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# 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
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import compute
-from nova import exception
-from nova.i18n import _
-
-
-authorize = extensions.extension_authorizer('compute', 'consoles')
-
-
-class ConsolesController(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- self.compute_api = compute.API()
- super(ConsolesController, self).__init__(*args, **kwargs)
-
- @wsgi.action('os-getVNCConsole')
- def get_vnc_console(self, req, id, body):
- """Get vnc connection information to access a server."""
- context = req.environ['nova.context']
- authorize(context)
-
- # If type is not supplied or unknown, get_vnc_console below will cope
- console_type = body['os-getVNCConsole'].get('type')
- instance = common.get_instance(self.compute_api, context, id)
-
- try:
- output = self.compute_api.get_vnc_console(context,
- instance,
- console_type)
- except exception.InstanceNotReady:
- raise webob.exc.HTTPConflict(
- explanation=_('Instance not yet ready'))
- except exception.InstanceNotFound as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
- except (exception.ConsoleTypeUnavailable,
- exception.ConsoleTypeInvalid) as e:
- raise webob.exc.HTTPBadRequest(explanation=e.format_message())
- except NotImplementedError:
- msg = _("Unable to get vnc console, functionality not implemented")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
-
- return {'console': {'type': console_type, 'url': output['url']}}
-
- @wsgi.action('os-getSPICEConsole')
- def get_spice_console(self, req, id, body):
- """Get spice connection information to access a server."""
- context = req.environ['nova.context']
- authorize(context)
-
- # If type is not supplied or unknown, get_spice_console below will cope
- console_type = body['os-getSPICEConsole'].get('type')
- instance = common.get_instance(self.compute_api, context, id)
-
- try:
- output = self.compute_api.get_spice_console(context,
- instance,
- console_type)
- except (exception.ConsoleTypeUnavailable,
- exception.ConsoleTypeInvalid) as e:
- raise webob.exc.HTTPBadRequest(explanation=e.format_message())
- except exception.InstanceNotFound as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
- except exception.InstanceNotReady as e:
- raise webob.exc.HTTPConflict(explanation=e.format_message())
- except NotImplementedError:
- msg = _("Unable to get spice console, "
- "functionality not implemented")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
-
- return {'console': {'type': console_type, 'url': output['url']}}
-
- @wsgi.action('os-getRDPConsole')
- def get_rdp_console(self, req, id, body):
- """Get text console output."""
- context = req.environ['nova.context']
- authorize(context)
-
- # If type is not supplied or unknown, get_rdp_console below will cope
- console_type = body['os-getRDPConsole'].get('type')
- instance = common.get_instance(self.compute_api, context, id)
-
- try:
- output = self.compute_api.get_rdp_console(context,
- instance,
- console_type)
- except (exception.ConsoleTypeUnavailable,
- exception.ConsoleTypeInvalid) as e:
- raise webob.exc.HTTPBadRequest(explanation=e.format_message())
- except exception.InstanceNotFound as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
- except exception.InstanceNotReady as e:
- raise webob.exc.HTTPConflict(explanation=e.format_message())
- except NotImplementedError:
- msg = _("Unable to get rdp console, functionality not implemented")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
-
- return {'console': {'type': console_type, 'url': output['url']}}
-
- @wsgi.action('os-getSerialConsole')
- def get_serial_console(self, req, id, body):
- """Get connection to a serial console."""
- context = req.environ['nova.context']
- authorize(context)
-
- # If type is not supplied or unknown get_serial_console below will cope
- console_type = body['os-getSerialConsole'].get('type')
- instance = common.get_instance(self.compute_api, context, id)
- try:
- output = self.compute_api.get_serial_console(context,
- instance,
- console_type)
- except exception.InstanceNotFound as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
- except exception.InstanceNotReady as e:
- raise webob.exc.HTTPConflict(explanation=e.format_message())
- except (exception.ConsoleTypeUnavailable,
- exception.ConsoleTypeInvalid,
- exception.ImageSerialPortNumberInvalid,
- exception.ImageSerialPortNumberExceedFlavorValue,
- exception.SocketPortRangeExhaustedException) as e:
- raise webob.exc.HTTPBadRequest(explanation=e.format_message())
- except NotImplementedError:
- msg = _("Unable to get serial console, "
- "functionality not implemented")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
-
- return {'console': {'type': console_type, 'url': output['url']}}
-
-
-class Consoles(extensions.ExtensionDescriptor):
- """Interactive Console support."""
- name = "Consoles"
- alias = "os-consoles"
- namespace = "http://docs.openstack.org/compute/ext/os-consoles/api/v2"
- updated = "2011-12-23T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = ConsolesController()
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/createserverext.py b/nova/api/openstack/compute/legacy_v2/contrib/createserverext.py
deleted file mode 100644
index dd8f1e7dc3..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/createserverext.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-#
-# 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 extensions
-
-
-class Createserverext(extensions.ExtensionDescriptor):
- """Extended support to the Create Server v1.1 API."""
-
- name = "Createserverext"
- alias = "os-create-server-ext"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "createserverext/api/v1.1")
- updated = "2011-07-19T00:00:00Z"
-
- def get_resources(self):
- res = extensions.ResourceExtension('os-create-server-ext',
- inherits='servers')
- return [res]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/deferred_delete.py b/nova/api/openstack/compute/legacy_v2/contrib/deferred_delete.py
deleted file mode 100644
index 277d70c443..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/deferred_delete.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-# 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 deferred instance delete extension."""
-
-import webob
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import compute
-from nova import exception
-
-
-authorize = extensions.extension_authorizer('compute', 'deferred_delete')
-
-
-class DeferredDeleteController(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- super(DeferredDeleteController, self).__init__(*args, **kwargs)
- self.compute_api = compute.API()
-
- @wsgi.action('restore')
- def _restore(self, req, id, body):
- """Restore a previously deleted instance."""
- context = req.environ["nova.context"]
- authorize(context)
- instance = common.get_instance(self.compute_api, context, id)
-
- try:
- self.compute_api.restore(context, instance)
- except exception.QuotaError as error:
- raise webob.exc.HTTPForbidden(explanation=error.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'restore', id)
- return webob.Response(status_int=202)
-
- @wsgi.action('forceDelete')
- def _force_delete(self, req, id, body):
- """Force delete of instance before deferred cleanup."""
- context = req.environ["nova.context"]
- authorize(context)
- instance = common.get_instance(self.compute_api, context, id)
-
- try:
- self.compute_api.force_delete(context, instance)
- except exception.InstanceIsLocked as e:
- raise webob.exc.HTTPConflict(explanation=e.format_message())
- return webob.Response(status_int=202)
-
-
-class Deferred_delete(extensions.ExtensionDescriptor):
- """Instance deferred delete."""
-
- name = "DeferredDelete"
- alias = "os-deferred-delete"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "deferred-delete/api/v1.1")
- updated = "2011-09-01T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = DeferredDeleteController()
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/disk_config.py b/nova/api/openstack/compute/legacy_v2/contrib/disk_config.py
deleted file mode 100644
index eb8b05aa01..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/disk_config.py
+++ /dev/null
@@ -1,151 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-#
-# 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.
-
-"""Disk Config extension."""
-
-from oslo_utils import strutils
-from webob import exc
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova.i18n import _
-
-ALIAS = 'OS-DCF'
-XMLNS_DCF = "http://docs.openstack.org/compute/ext/disk_config/api/v1.1"
-API_DISK_CONFIG = "%s:diskConfig" % ALIAS
-INTERNAL_DISK_CONFIG = "auto_disk_config"
-authorize = extensions.soft_extension_authorizer('compute', 'disk_config')
-
-
-def disk_config_to_api(value):
- return 'AUTO' if value else 'MANUAL'
-
-
-def disk_config_from_api(value):
- if value == 'AUTO':
- return True
- elif value == 'MANUAL':
- return False
- else:
- msg = _("%s must be either 'MANUAL' or 'AUTO'.") % API_DISK_CONFIG
- raise exc.HTTPBadRequest(explanation=msg)
-
-
-class ImageDiskConfigController(wsgi.Controller):
- def _add_disk_config(self, context, images):
- for image in images:
- metadata = image['metadata']
- if INTERNAL_DISK_CONFIG in metadata:
- raw_value = metadata[INTERNAL_DISK_CONFIG]
- value = strutils.bool_from_string(raw_value)
- image[API_DISK_CONFIG] = disk_config_to_api(value)
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- context = req.environ['nova.context']
- if 'image' in resp_obj.obj and authorize(context):
- image = resp_obj.obj['image']
- self._add_disk_config(context, [image])
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- context = req.environ['nova.context']
- if 'images' in resp_obj.obj and authorize(context):
- images = resp_obj.obj['images']
- self._add_disk_config(context, images)
-
-
-class ServerDiskConfigController(wsgi.Controller):
- def _add_disk_config(self, req, servers):
- for server in servers:
- db_server = req.get_db_instance(server['id'])
- # server['id'] is guaranteed to be in the cache due to
- # the core API adding it in its 'show'/'detail' methods.
- value = db_server.get(INTERNAL_DISK_CONFIG)
- server[API_DISK_CONFIG] = disk_config_to_api(value)
-
- def _show(self, req, resp_obj):
- if 'server' in resp_obj.obj:
- server = resp_obj.obj['server']
- self._add_disk_config(req, [server])
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- context = req.environ['nova.context']
- if authorize(context):
- self._show(req, resp_obj)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- context = req.environ['nova.context']
- if 'servers' in resp_obj.obj and authorize(context):
- servers = resp_obj.obj['servers']
- self._add_disk_config(req, servers)
-
- def _set_disk_config(self, dict_):
- if API_DISK_CONFIG in dict_:
- api_value = dict_[API_DISK_CONFIG]
- internal_value = disk_config_from_api(api_value)
- dict_[INTERNAL_DISK_CONFIG] = internal_value
-
- @wsgi.extends
- def create(self, req, body):
- context = req.environ['nova.context']
- if authorize(context):
- if 'server' in body:
- self._set_disk_config(body['server'])
- resp_obj = (yield)
- self._show(req, resp_obj)
-
- @wsgi.extends
- def update(self, req, id, body):
- context = req.environ['nova.context']
- if authorize(context):
- if 'server' in body:
- self._set_disk_config(body['server'])
- resp_obj = (yield)
- self._show(req, resp_obj)
-
- @wsgi.extends(action='rebuild')
- def _action_rebuild(self, req, id, body):
- context = req.environ['nova.context']
- if authorize(context):
- self._set_disk_config(body['rebuild'])
- resp_obj = (yield)
- self._show(req, resp_obj)
-
- @wsgi.extends(action='resize')
- def _action_resize(self, req, id, body):
- context = req.environ['nova.context']
- if authorize(context):
- self._set_disk_config(body['resize'])
- yield
-
-
-class Disk_config(extensions.ExtensionDescriptor):
- """Disk Management Extension."""
-
- name = "DiskConfig"
- alias = ALIAS
- namespace = XMLNS_DCF
- updated = "2011-09-27T00:00:00Z"
-
- def get_controller_extensions(self):
- servers_extension = extensions.ControllerExtension(
- self, 'servers', ServerDiskConfigController())
-
- images_extension = extensions.ControllerExtension(
- self, 'images', ImageDiskConfigController())
-
- return [servers_extension, images_extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/evacuate.py b/nova/api/openstack/compute/legacy_v2/contrib/evacuate.py
deleted file mode 100644
index 6b9dd01561..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/evacuate.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# Copyright 2013 OpenStack Foundation
-#
-# 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 oslo_utils import strutils
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import compute
-from nova import context as nova_context
-from nova import exception
-from nova.i18n import _
-from nova import utils
-
-authorize = extensions.extension_authorizer('compute', 'evacuate')
-
-
-class Controller(wsgi.Controller):
- def __init__(self, ext_mgr, *args, **kwargs):
- super(Controller, self).__init__(*args, **kwargs)
- self.compute_api = compute.API()
- self.host_api = compute.HostAPI()
- self.ext_mgr = ext_mgr
-
- @wsgi.action('evacuate')
- def _evacuate(self, req, id, body):
- """Permit admins to evacuate a server from a failed host
- to a new one.
- If host is empty, the scheduler will select one.
- """
- context = req.environ["nova.context"]
- authorize(context)
-
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks. This has to be left only for API v2.0 because
- # this version has to be stable even if it means that only admins
- # can call this method while the policy could be changed.
- nova_context.require_admin_context(context)
-
- if not self.is_valid_body(body, "evacuate"):
- raise exc.HTTPBadRequest(_("Malformed request body"))
- evacuate_body = body["evacuate"]
-
- host = evacuate_body.get("host")
-
- if (not host and
- not self.ext_mgr.is_loaded('os-extended-evacuate-find-host')):
- msg = _("host must be specified.")
- raise exc.HTTPBadRequest(explanation=msg)
-
- try:
- on_shared_storage = strutils.bool_from_string(
- evacuate_body["onSharedStorage"])
- except (TypeError, KeyError):
- msg = _("onSharedStorage must be specified.")
- raise exc.HTTPBadRequest(explanation=msg)
-
- password = None
- if 'adminPass' in evacuate_body:
- # check that if requested to evacuate server on shared storage
- # password not specified
- if on_shared_storage:
- msg = _("admin password can't be changed on existing disk")
- raise exc.HTTPBadRequest(explanation=msg)
-
- password = evacuate_body['adminPass']
- elif not on_shared_storage:
- password = utils.generate_password()
-
- if host is not None:
- try:
- self.host_api.service_get_by_compute_host(context, host)
- except exception.NotFound:
- msg = _("Compute host %s not found.") % host
- raise exc.HTTPNotFound(explanation=msg)
-
- instance = common.get_instance(self.compute_api, context, id)
- try:
- if instance.host == host:
- msg = _("The target host can't be the same one.")
- raise exc.HTTPBadRequest(explanation=msg)
- self.compute_api.evacuate(context, instance, host,
- on_shared_storage, password)
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'evacuate', id)
- except exception.InstanceNotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- except exception.ComputeServiceInUse as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- if password:
- return {'adminPass': password}
-
-
-class Evacuate(extensions.ExtensionDescriptor):
- """Enables server evacuation."""
-
- name = "Evacuate"
- alias = "os-evacuate"
- namespace = "http://docs.openstack.org/compute/ext/evacuate/api/v2"
- updated = "2013-01-06T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = Controller(self.ext_mgr)
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_availability_zone.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_availability_zone.py
deleted file mode 100644
index f5cdf6ee32..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/extended_availability_zone.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# Copyright 2013 Netease, 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 Extended Availability Zone Status API extension."""
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import availability_zones as avail_zone
-
-authorize = extensions.soft_extension_authorizer('compute',
- 'extended_availability_zone')
-
-
-class ExtendedAZController(wsgi.Controller):
- def _extend_server(self, context, server, instance):
- key = "%s:availability_zone" % Extended_availability_zone.alias
- az = avail_zone.get_instance_availability_zone(context, instance)
- server[key] = az or ''
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- context = req.environ['nova.context']
- if authorize(context):
- server = resp_obj.obj['server']
- db_instance = req.get_db_instance(server['id'])
- self._extend_server(context, server, db_instance)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- context = req.environ['nova.context']
- if authorize(context):
- servers = list(resp_obj.obj['servers'])
- for server in servers:
- db_instance = req.get_db_instance(server['id'])
- self._extend_server(context, server, db_instance)
-
-
-class Extended_availability_zone(extensions.ExtensionDescriptor):
- """Extended Availability Zone support."""
-
- name = "ExtendedAvailabilityZone"
- alias = "OS-EXT-AZ"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "extended_availability_zone/api/v2")
- updated = "2013-01-30T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = ExtendedAZController()
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_evacuate_find_host.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_evacuate_find_host.py
deleted file mode 100644
index 2dfe3faff5..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/extended_evacuate_find_host.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# 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 extensions
-
-
-class Extended_evacuate_find_host(extensions.ExtensionDescriptor):
- """Enables server evacuation without target host. Scheduler will select
- one to target.
- """
- name = "ExtendedEvacuateFindHost"
- alias = "os-extended-evacuate-find-host"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "extended_evacuate_find_host/api/v2")
- updated = "2014-02-12T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_floating_ips.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_floating_ips.py
deleted file mode 100644
index 5ee55a81b9..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/extended_floating_ips.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# 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 extensions
-
-
-class Extended_floating_ips(extensions.ExtensionDescriptor):
- """Adds optional fixed_address to the add floating IP command."""
-
- name = "ExtendedFloatingIps"
- alias = "os-extended-floating-ips"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "extended_floating_ips/api/v2")
- updated = "2013-04-19T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_hypervisors.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_hypervisors.py
deleted file mode 100644
index 794c1295f0..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/extended_hypervisors.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2014 IBM Corp.
-#
-# 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 extensions
-
-
-class Extended_hypervisors(extensions.ExtensionDescriptor):
- """Extended hypervisors support."""
-
- name = "ExtendedHypervisors"
- alias = "os-extended-hypervisors"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "extended_hypervisors/api/v1.1")
- updated = "2014-01-04T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_ips.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_ips.py
deleted file mode 100644
index 2b55509a45..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/extended_ips.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# Copyright 2013 Nebula, Inc.
-#
-# 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 Extended Ips API extension."""
-
-import itertools
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import compute
-
-authorize = extensions.soft_extension_authorizer('compute', 'extended_ips')
-
-
-class ExtendedIpsController(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- super(ExtendedIpsController, self).__init__(*args, **kwargs)
- self.compute_api = compute.API()
-
- def _extend_server(self, context, server, instance):
- key = "%s:type" % Extended_ips.alias
- networks = common.get_networks_for_instance(context, instance)
- for label, network in networks.items():
- # NOTE(vish): ips are hidden in some states via the
- # hide_server_addresses extension.
- if label in server['addresses']:
- all_ips = itertools.chain(network["ips"],
- network["floating_ips"])
- for i, ip in enumerate(all_ips):
- server['addresses'][label][i][key] = ip['type']
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- context = req.environ['nova.context']
- if authorize(context):
- server = resp_obj.obj['server']
- db_instance = req.get_db_instance(server['id'])
- # server['id'] is guaranteed to be in the cache due to
- # the core API adding it in its 'show' method.
- self._extend_server(context, server, db_instance)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- context = req.environ['nova.context']
- if authorize(context):
- servers = list(resp_obj.obj['servers'])
- for server in servers:
- db_instance = req.get_db_instance(server['id'])
- # server['id'] is guaranteed to be in the cache due to
- # the core API adding it in its 'detail' method.
- self._extend_server(context, server, db_instance)
-
-
-class Extended_ips(extensions.ExtensionDescriptor):
- """Adds type parameter to the ip list."""
-
- name = "ExtendedIps"
- alias = "OS-EXT-IPS"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "extended_ips/api/v1.1")
- updated = "2013-01-06T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = ExtendedIpsController()
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_ips_mac.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_ips_mac.py
deleted file mode 100644
index b5535603f8..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/extended_ips_mac.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# Copyright 2013 IBM Corp.
-#
-# 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 Extended Ips API extension."""
-
-import itertools
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-
-authorize = extensions.soft_extension_authorizer('compute', 'extended_ips_mac')
-
-
-class ExtendedIpsMacController(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- super(ExtendedIpsMacController, self).__init__(*args, **kwargs)
-
- def _extend_server(self, context, server, instance):
- key = "%s:mac_addr" % Extended_ips_mac.alias
- networks = common.get_networks_for_instance(context, instance)
- for label, network in networks.items():
- # NOTE(vish): ips are hidden in some states via the
- # hide_server_addresses extension.
- if label in server['addresses']:
- all_ips = itertools.chain(network["ips"],
- network["floating_ips"])
- for i, ip in enumerate(all_ips):
- server['addresses'][label][i][key] = ip['mac_address']
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- context = req.environ['nova.context']
- if authorize(context):
- server = resp_obj.obj['server']
- db_instance = req.get_db_instance(server['id'])
- # server['id'] is guaranteed to be in the cache due to
- # the core API adding it in its 'show' method.
- self._extend_server(context, server, db_instance)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- context = req.environ['nova.context']
- if authorize(context):
- servers = list(resp_obj.obj['servers'])
- for server in servers:
- db_instance = req.get_db_instance(server['id'])
- # server['id'] is guaranteed to be in the cache due to
- # the core API adding it in its 'detail' method.
- self._extend_server(context, server, db_instance)
-
-
-class Extended_ips_mac(extensions.ExtensionDescriptor):
- """Adds mac address parameter to the ip list."""
-
- name = "ExtendedIpsMac"
- alias = "OS-EXT-IPS-MAC"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "extended_ips_mac/api/v1.1")
- updated = "2013-03-07T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = ExtendedIpsMacController()
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_networks.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_networks.py
deleted file mode 100644
index f5021a48dc..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/extended_networks.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright 2014 Nebula, Inc.
-# 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 extensions
-
-
-class Extended_networks(extensions.ExtensionDescriptor):
- """Adds additional fields to networks."""
-
- name = "ExtendedNetworks"
- alias = "os-extended-networks"
- namespace = ("http://docs.openstack.org/compute/ext/extended_networks"
- "/api/v2")
- updated = "2014-05-09T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_quotas.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_quotas.py
deleted file mode 100644
index 8161194992..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/extended_quotas.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright 2013 Rackspace Hosting
-# 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 extensions
-
-
-class Extended_quotas(extensions.ExtensionDescriptor):
- """Adds ability for admins to delete quota
- and optionally force the update Quota command.
- """
-
- name = "ExtendedQuotas"
- alias = "os-extended-quotas"
- namespace = ("http://docs.openstack.org/compute/ext/extended_quotas"
- "/api/v1.1")
- updated = "2013-06-09T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_rescue_with_image.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_rescue_with_image.py
deleted file mode 100644
index bd73197e14..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/extended_rescue_with_image.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2014 OpenStack Foundation
-#
-# 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 extensions as exts
-
-
-class Extended_rescue_with_image(exts.ExtensionDescriptor):
- """Allow the user to specify the image to use for rescue."""
-
- name = "ExtendedRescueWithImage"
- alias = "os-extended-rescue-with-image"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "extended_rescue_with_image/api/v2")
- updated = "2014-01-04T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_server_attributes.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_server_attributes.py
deleted file mode 100644
index 1ea5671f07..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/extended_server_attributes.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# 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 Extended Server Attributes API extension."""
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-
-authorize = extensions.soft_extension_authorizer('compute',
- 'extended_server_attributes')
-
-
-class ExtendedServerAttributesController(wsgi.Controller):
- def _extend_server(self, context, server, instance):
- key = "%s:hypervisor_hostname" % Extended_server_attributes.alias
- server[key] = instance.node
-
- for attr in ['host', 'name']:
- if attr == 'name':
- key = "%s:instance_%s" % (Extended_server_attributes.alias,
- attr)
- else:
- key = "%s:%s" % (Extended_server_attributes.alias, attr)
- server[key] = instance[attr]
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- context = req.environ['nova.context']
- if authorize(context):
- server = resp_obj.obj['server']
- db_instance = req.get_db_instance(server['id'])
- # server['id'] is guaranteed to be in the cache due to
- # the core API adding it in its 'show' method.
- self._extend_server(context, server, db_instance)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- context = req.environ['nova.context']
- if authorize(context):
- servers = list(resp_obj.obj['servers'])
- for server in servers:
- db_instance = req.get_db_instance(server['id'])
- # server['id'] is guaranteed to be in the cache due to
- # the core API adding it in its 'detail' method.
- self._extend_server(context, server, db_instance)
-
-
-class Extended_server_attributes(extensions.ExtensionDescriptor):
- """Extended Server Attributes support."""
-
- name = "ExtendedServerAttributes"
- alias = "OS-EXT-SRV-ATTR"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "extended_status/api/v1.1")
- updated = "2011-11-03T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = ExtendedServerAttributesController()
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_services.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_services.py
deleted file mode 100644
index 1c752b5ae5..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/extended_services.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# 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 extensions
-
-
-class Extended_services(extensions.ExtensionDescriptor):
- """Extended services support."""
-
- name = "ExtendedServices"
- alias = "os-extended-services"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "extended_services/api/v2")
- updated = "2013-05-17T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_services_delete.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_services_delete.py
deleted file mode 100644
index 78d996c904..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/extended_services_delete.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# 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 extensions
-
-
-class Extended_services_delete(extensions.ExtensionDescriptor):
- """Extended services deletion support."""
-
- name = "ExtendedServicesDelete"
- alias = "os-extended-services-delete"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "extended_services_delete/api/v2")
- updated = "2013-12-10T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_status.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_status.py
deleted file mode 100644
index 89073153ca..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/extended_status.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-#
-# 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 Extended Status Admin API extension."""
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-
-authorize = extensions.soft_extension_authorizer('compute', 'extended_status')
-
-
-class ExtendedStatusController(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- super(ExtendedStatusController, self).__init__(*args, **kwargs)
-
- def _extend_server(self, server, instance):
- for state in ['task_state', 'vm_state', 'power_state']:
- key = "%s:%s" % (Extended_status.alias, state)
- server[key] = instance[state]
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- context = req.environ['nova.context']
- if authorize(context):
- server = resp_obj.obj['server']
- db_instance = req.get_db_instance(server['id'])
- # server['id'] is guaranteed to be in the cache due to
- # the core API adding it in its 'show' method.
- self._extend_server(server, db_instance)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- context = req.environ['nova.context']
- if authorize(context):
- servers = list(resp_obj.obj['servers'])
- for server in servers:
- db_instance = req.get_db_instance(server['id'])
- # server['id'] is guaranteed to be in the cache due to
- # the core API adding it in its 'detail' method.
- self._extend_server(server, db_instance)
-
-
-class Extended_status(extensions.ExtensionDescriptor):
- """Extended Status support."""
-
- name = "ExtendedStatus"
- alias = "OS-EXT-STS"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "extended_status/api/v1.1")
- updated = "2011-11-03T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = ExtendedStatusController()
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_virtual_interfaces_net.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_virtual_interfaces_net.py
deleted file mode 100644
index 864f0252b4..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/extended_virtual_interfaces_net.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright 2013 IBM Corp.
-#
-# 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 extensions
-from nova.api.openstack import wsgi
-from nova import network
-
-authorize = extensions.soft_extension_authorizer('compute', 'extended_vif_net')
-
-
-class ExtendedServerVIFNetController(wsgi.Controller):
- def __init__(self):
- super(ExtendedServerVIFNetController, self).__init__()
- self.network_api = network.API()
-
- @wsgi.extends
- def index(self, req, resp_obj, server_id):
- key = "%s:net_id" % Extended_virtual_interfaces_net.alias
- context = req.environ['nova.context']
- if authorize(context):
- for vif in resp_obj.obj['virtual_interfaces']:
- vif1 = self.network_api.get_vif_by_mac_address(context,
- vif['mac_address'])
- vif[key] = vif1.net_uuid
-
-
-class Extended_virtual_interfaces_net(extensions.ExtensionDescriptor):
- """Adds network id parameter to the virtual interface list."""
-
- name = "ExtendedVIFNet"
- alias = "OS-EXT-VIF-NET"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "extended-virtual-interfaces-net/api/v1.1")
- updated = "2013-03-07T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = ExtendedServerVIFNetController()
- extension = extensions.ControllerExtension(self,
- 'os-virtual-interfaces',
- controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/extended_volumes.py b/nova/api/openstack/compute/legacy_v2/contrib/extended_volumes.py
deleted file mode 100644
index 90cd90fcc2..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/extended_volumes.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# Copyright 2013 OpenStack Foundation
-#
-# 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 Extended Volumes API extension."""
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import objects
-
-authorize = extensions.soft_extension_authorizer('compute', 'extended_volumes')
-
-
-class ExtendedVolumesController(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- super(ExtendedVolumesController, self).__init__(*args, **kwargs)
-
- def _extend_server(self, context, server, bdms):
- volume_ids = [bdm.volume_id for bdm in bdms if bdm.volume_id]
- key = "%s:volumes_attached" % Extended_volumes.alias
- server[key] = [{'id': volume_id} for volume_id in volume_ids]
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- context = req.environ['nova.context']
- if authorize(context):
- server = resp_obj.obj['server']
- bdms = objects.BlockDeviceMappingList.bdms_by_instance_uuid(
- context, [server['id']])
- instance_bdms = self._get_instance_bdms(bdms, server)
- self._extend_server(context, server, instance_bdms)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- context = req.environ['nova.context']
- if authorize(context):
- servers = list(resp_obj.obj['servers'])
- instance_uuids = [server['id'] for server in servers]
- bdms = objects.BlockDeviceMappingList.bdms_by_instance_uuid(
- context, instance_uuids)
- for server in servers:
- instance_bdms = self._get_instance_bdms(bdms, server)
- self._extend_server(context, server, instance_bdms)
-
- def _get_instance_bdms(self, bdms, server):
- # server['id'] is guaranteed to be in the cache due to
- # the core API adding it in the 'detail' or 'show' method.
- # If that instance has since been deleted, it won't be in the
- # 'bdms' dictionary though, so use 'get' to avoid KeyErrors.
- return bdms.get(server['id'], [])
-
-
-class Extended_volumes(extensions.ExtensionDescriptor):
- """Extended Volumes support."""
-
- name = "ExtendedVolumes"
- alias = "os-extended-volumes"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "extended_volumes/api/v1.1")
- updated = "2013-06-07T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = ExtendedVolumesController()
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
-
- def get_resources(self):
- return []
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/fixed_ips.py b/nova/api/openstack/compute/legacy_v2/contrib/fixed_ips.py
deleted file mode 100644
index 558bd66e42..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/fixed_ips.py
+++ /dev/null
@@ -1,99 +0,0 @@
-# Copyright 2012 IBM Corp.
-#
-# 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
-import webob.exc
-
-from nova.api.openstack import extensions
-from nova import exception
-from nova.i18n import _
-from nova import objects
-
-authorize = extensions.extension_authorizer('compute', 'fixed_ips')
-
-
-class FixedIPController(object):
- def show(self, req, id):
- """Return data about the given fixed IP."""
- context = req.environ['nova.context']
- authorize(context)
-
- attrs = ['network', 'instance']
- try:
- fixed_ip = objects.FixedIP.get_by_address(context, id,
- expected_attrs=attrs)
- except exception.FixedIpNotFoundForAddress as ex:
- raise webob.exc.HTTPNotFound(explanation=ex.format_message())
- except exception.FixedIpInvalid as ex:
- raise webob.exc.HTTPBadRequest(explanation=ex.format_message())
-
- fixed_ip_info = {"fixed_ip": {}}
- if fixed_ip is None:
- msg = _("Fixed IP %s has been deleted") % id
- raise webob.exc.HTTPNotFound(explanation=msg)
-
- fixed_ip_info['fixed_ip']['cidr'] = str(fixed_ip.network.cidr)
- fixed_ip_info['fixed_ip']['address'] = str(fixed_ip.address)
-
- if fixed_ip.instance:
- fixed_ip_info['fixed_ip']['hostname'] = fixed_ip.instance.hostname
- fixed_ip_info['fixed_ip']['host'] = fixed_ip.instance.host
- else:
- fixed_ip_info['fixed_ip']['hostname'] = None
- fixed_ip_info['fixed_ip']['host'] = None
-
- return fixed_ip_info
-
- def action(self, req, id, body):
- context = req.environ['nova.context']
- authorize(context)
-
- if 'reserve' in body:
- return self._set_reserved(context, id, True)
- elif 'unreserve' in body:
- return self._set_reserved(context, id, False)
- else:
- raise webob.exc.HTTPBadRequest(
- explanation="No valid action specified")
-
- def _set_reserved(self, context, address, reserved):
- try:
- fixed_ip = objects.FixedIP.get_by_address(context, address)
- fixed_ip.reserved = reserved
- fixed_ip.save()
- except exception.FixedIpNotFoundForAddress:
- msg = _("Fixed IP %s not found") % address
- raise webob.exc.HTTPNotFound(explanation=msg)
- except exception.FixedIpInvalid as ex:
- raise webob.exc.HTTPBadRequest(explanation=ex.format_message())
-
- return webob.Response(status_int=202)
-
-
-class Fixed_ips(extensions.ExtensionDescriptor):
- """Fixed IPs support."""
-
- name = "FixedIPs"
- alias = "os-fixed-ips"
- namespace = "http://docs.openstack.org/compute/ext/fixed_ips/api/v2"
- updated = "2012-10-18T19:25:27Z"
-
- def get_resources(self):
- member_actions = {'action': 'POST'}
- resources = []
- resource = extensions.ResourceExtension('os-fixed-ips',
- FixedIPController(),
- member_actions=member_actions)
- resources.append(resource)
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/flavor_access.py b/nova/api/openstack/compute/legacy_v2/contrib/flavor_access.py
deleted file mode 100644
index a2ff3c2d83..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/flavor_access.py
+++ /dev/null
@@ -1,177 +0,0 @@
-# Copyright (c) 2011 OpenStack Foundation
-# 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 flavor access extension."""
-
-import webob
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import context as nova_context
-from nova import exception
-from nova.i18n import _
-from nova import objects
-
-
-soft_authorize = extensions.soft_extension_authorizer('compute',
- 'flavor_access')
-authorize = extensions.extension_authorizer('compute', 'flavor_access')
-
-
-def _marshall_flavor_access(flavor):
- rval = []
- for project_id in flavor.projects:
- rval.append({'flavor_id': flavor.flavorid,
- 'tenant_id': project_id})
-
- return {'flavor_access': rval}
-
-
-class FlavorAccessController(object):
- """The flavor access API controller for the OpenStack API."""
-
- def __init__(self):
- super(FlavorAccessController, self).__init__()
-
- def index(self, req, flavor_id):
- context = req.environ['nova.context']
- authorize(context)
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks.
- nova_context.require_admin_context(context)
-
- flavor = common.get_flavor(context, flavor_id)
-
- # public flavor to all projects
- if flavor.is_public:
- explanation = _("Access list not available for public flavors.")
- raise webob.exc.HTTPNotFound(explanation=explanation)
-
- # private flavor to listed projects only
- return _marshall_flavor_access(flavor)
-
-
-class FlavorActionController(wsgi.Controller):
- """The flavor access API controller for the OpenStack API."""
-
- def _check_body(self, body):
- if body is None or body == "":
- raise webob.exc.HTTPBadRequest(explanation=_("No request body"))
-
- def _extend_flavor(self, flavor_rval, flavor_ref):
- key = "%s:is_public" % (Flavor_access.alias)
- flavor_rval[key] = flavor_ref['is_public']
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- context = req.environ['nova.context']
- if soft_authorize(context):
- db_flavor = req.get_db_flavor(id)
-
- self._extend_flavor(resp_obj.obj['flavor'], db_flavor)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- context = req.environ['nova.context']
- if soft_authorize(context):
- flavors = list(resp_obj.obj['flavors'])
- for flavor_rval in flavors:
- db_flavor = req.get_db_flavor(flavor_rval['id'])
- self._extend_flavor(flavor_rval, db_flavor)
-
- @wsgi.extends(action='create')
- def create(self, req, body, resp_obj):
- context = req.environ['nova.context']
- if soft_authorize(context):
- db_flavor = req.get_db_flavor(resp_obj.obj['flavor']['id'])
-
- self._extend_flavor(resp_obj.obj['flavor'], db_flavor)
-
- @wsgi.action("addTenantAccess")
- def _addTenantAccess(self, req, id, body):
- context = req.environ['nova.context']
- authorize(context, action="addTenantAccess")
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks.
- nova_context.require_admin_context(context)
- self._check_body(body)
-
- vals = body['addTenantAccess']
- tenant = vals.get('tenant')
- if not tenant:
- msg = _("Missing tenant parameter")
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- flavor = objects.Flavor(context=context, flavorid=id)
- try:
- flavor.add_access(tenant)
- except exception.FlavorAccessExists as err:
- raise webob.exc.HTTPConflict(explanation=err.format_message())
- except exception.FlavorNotFound as err:
- raise webob.exc.HTTPNotFound(explanation=err.format_message())
-
- return _marshall_flavor_access(flavor)
-
- @wsgi.action("removeTenantAccess")
- def _removeTenantAccess(self, req, id, body):
- context = req.environ['nova.context']
- authorize(context, action="removeTenantAccess")
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks.
- nova_context.require_admin_context(context)
- self._check_body(body)
-
- vals = body['removeTenantAccess']
- tenant = vals.get('tenant')
- if not tenant:
- msg = _("Missing tenant parameter")
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- flavor = objects.Flavor(context=context, flavorid=id)
- try:
- flavor.remove_access(tenant)
- except (exception.FlavorNotFound,
- exception.FlavorAccessNotFound) as err:
- raise webob.exc.HTTPNotFound(explanation=err.format_message())
-
- return _marshall_flavor_access(flavor)
-
-
-class Flavor_access(extensions.ExtensionDescriptor):
- """Flavor access support."""
-
- name = "FlavorAccess"
- alias = "os-flavor-access"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "flavor_access/api/v2")
- updated = "2012-08-01T00:00:00Z"
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension(
- 'os-flavor-access',
- controller=FlavorAccessController(),
- parent=dict(member_name='flavor', collection_name='flavors'))
- resources.append(res)
-
- return resources
-
- def get_controller_extensions(self):
- extension = extensions.ControllerExtension(
- self, 'flavors', FlavorActionController())
-
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/flavor_disabled.py b/nova/api/openstack/compute/legacy_v2/contrib/flavor_disabled.py
deleted file mode 100644
index fa8233799c..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/flavor_disabled.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# Copyright 2012 Nebula, Inc.
-#
-# 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 Flavor Disabled API extension."""
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-
-
-authorize = extensions.soft_extension_authorizer('compute', 'flavor_disabled')
-
-
-class FlavorDisabledController(wsgi.Controller):
- def _extend_flavors(self, req, flavors):
- for flavor in flavors:
- db_flavor = req.get_db_flavor(flavor['id'])
- key = "%s:disabled" % Flavor_disabled.alias
- flavor[key] = db_flavor['disabled']
-
- def _show(self, req, resp_obj):
- if not authorize(req.environ['nova.context']):
- return
- if 'flavor' in resp_obj.obj:
- self._extend_flavors(req, [resp_obj.obj['flavor']])
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- return self._show(req, resp_obj)
-
- @wsgi.extends(action='create')
- def create(self, req, resp_obj, body):
- return self._show(req, resp_obj)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- if not authorize(req.environ['nova.context']):
- return
- self._extend_flavors(req, list(resp_obj.obj['flavors']))
-
-
-class Flavor_disabled(extensions.ExtensionDescriptor):
- """Support to show the disabled status of a flavor."""
-
- name = "FlavorDisabled"
- alias = "OS-FLV-DISABLED"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "flavor_disabled/api/v1.1")
- updated = "2012-08-29T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = FlavorDisabledController()
- extension = extensions.ControllerExtension(self, 'flavors', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/flavor_rxtx.py b/nova/api/openstack/compute/legacy_v2/contrib/flavor_rxtx.py
deleted file mode 100644
index 7044564f96..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/flavor_rxtx.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# Copyright 2012 Nebula, Inc.
-#
-# 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 Flavor Rxtx API extension."""
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-
-
-authorize = extensions.soft_extension_authorizer('compute', 'flavor_rxtx')
-
-
-class FlavorRxtxController(wsgi.Controller):
- def _extend_flavors(self, req, flavors):
- for flavor in flavors:
- db_flavor = req.get_db_flavor(flavor['id'])
- key = 'rxtx_factor'
- flavor[key] = db_flavor['rxtx_factor'] or ""
-
- def _show(self, req, resp_obj):
- if not authorize(req.environ['nova.context']):
- return
- if 'flavor' in resp_obj.obj:
- self._extend_flavors(req, [resp_obj.obj['flavor']])
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- return self._show(req, resp_obj)
-
- @wsgi.extends(action='create')
- def create(self, req, resp_obj, body):
- return self._show(req, resp_obj)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- if not authorize(req.environ['nova.context']):
- return
- self._extend_flavors(req, list(resp_obj.obj['flavors']))
-
-
-class Flavor_rxtx(extensions.ExtensionDescriptor):
- """Support to show the rxtx status of a flavor."""
-
- name = "FlavorRxtx"
- alias = "os-flavor-rxtx"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "flavor_rxtx/api/v1.1")
- updated = "2012-08-29T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = FlavorRxtxController()
- extension = extensions.ControllerExtension(self, 'flavors', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/flavor_swap.py b/nova/api/openstack/compute/legacy_v2/contrib/flavor_swap.py
deleted file mode 100644
index c42538daf7..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/flavor_swap.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# Copyright 2012 Nebula, Inc.
-#
-# 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 Flavor Swap API extension."""
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-
-
-authorize = extensions.soft_extension_authorizer('compute', 'flavor_swap')
-
-
-class FlavorSwapController(wsgi.Controller):
- def _extend_flavors(self, req, flavors):
- for flavor in flavors:
- db_flavor = req.get_db_flavor(flavor['id'])
- key = 'swap'
- flavor[key] = db_flavor['swap'] or ""
-
- def _show(self, req, resp_obj):
- if not authorize(req.environ['nova.context']):
- return
- if 'flavor' in resp_obj.obj:
- self._extend_flavors(req, [resp_obj.obj['flavor']])
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- return self._show(req, resp_obj)
-
- @wsgi.extends(action='create')
- def create(self, req, resp_obj, body):
- return self._show(req, resp_obj)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- if not authorize(req.environ['nova.context']):
- return
- self._extend_flavors(req, list(resp_obj.obj['flavors']))
-
-
-class Flavor_swap(extensions.ExtensionDescriptor):
- """Support to show the swap status of a flavor."""
-
- name = "FlavorSwap"
- alias = "os-flavor-swap"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "flavor_swap/api/v1.1")
- updated = "2012-08-29T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = FlavorSwapController()
- extension = extensions.ControllerExtension(self, 'flavors', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/flavorextradata.py b/nova/api/openstack/compute/legacy_v2/contrib/flavorextradata.py
deleted file mode 100644
index d896528793..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/flavorextradata.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# Copyright 2011 Canonical Ltd.
-# 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 Flavor extra data extension
-
-OpenStack API version 1.1 lists "name", "ram", "disk", "vcpus" as flavor
-attributes. This extension adds to that list:
-
-- OS-FLV-EXT-DATA:ephemeral
-"""
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-
-
-authorize = extensions.soft_extension_authorizer('compute', 'flavorextradata')
-
-
-class FlavorextradataController(wsgi.Controller):
- def _extend_flavors(self, req, flavors):
- for flavor in flavors:
- db_flavor = req.get_db_flavor(flavor['id'])
- key = "%s:ephemeral" % Flavorextradata.alias
- flavor[key] = db_flavor['ephemeral_gb']
-
- def _show(self, req, resp_obj):
- if not authorize(req.environ['nova.context']):
- return
- if 'flavor' in resp_obj.obj:
- self._extend_flavors(req, [resp_obj.obj['flavor']])
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- return self._show(req, resp_obj)
-
- @wsgi.extends(action='create')
- def create(self, req, resp_obj, body):
- return self._show(req, resp_obj)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- if not authorize(req.environ['nova.context']):
- return
- self._extend_flavors(req, list(resp_obj.obj['flavors']))
-
-
-class Flavorextradata(extensions.ExtensionDescriptor):
- """Provide additional data for flavors."""
-
- name = "FlavorExtraData"
- alias = "OS-FLV-EXT-DATA"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "flavor_extra_data/api/v1.1")
- updated = "2011-09-14T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = FlavorextradataController()
- extension = extensions.ControllerExtension(self, 'flavors', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/flavorextraspecs.py b/nova/api/openstack/compute/legacy_v2/contrib/flavorextraspecs.py
deleted file mode 100644
index e380954596..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/flavorextraspecs.py
+++ /dev/null
@@ -1,166 +0,0 @@
-# Copyright 2011 University of Southern California
-# 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 instance type extra specs extension."""
-
-import six
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.compute import flavors
-from nova import exception
-from nova.i18n import _
-from nova import utils
-
-authorize = extensions.extension_authorizer('compute', 'flavorextraspecs')
-
-
-class FlavorExtraSpecsController(object):
- """The flavor extra specs API controller for the OpenStack API."""
-
- def _get_extra_specs(self, context, flavor_id):
- flavor = common.get_flavor(context, flavor_id)
- return dict(extra_specs=flavor.extra_specs)
-
- def _check_body(self, body):
- if body is None or body == "":
- expl = _('No Request Body')
- raise exc.HTTPBadRequest(explanation=expl)
-
- def _check_extra_specs(self, specs):
- if type(specs) is not dict:
- msg = _('Bad extra_specs provided')
- raise exc.HTTPBadRequest(explanation=msg)
-
- try:
- flavors.validate_extra_spec_keys(specs.keys())
- except TypeError:
- msg = _("Fail to validate provided extra specs keys. "
- "Expected string")
- raise exc.HTTPBadRequest(explanation=msg)
- except exception.InvalidInput as error:
- raise exc.HTTPBadRequest(explanation=error.format_message())
-
- for key, value in six.iteritems(specs):
- try:
- utils.check_string_length(key, 'extra_specs key',
- min_length=1, max_length=255)
-
- # NOTE(dims): The following check was added for backwards
- # compatibility.
- if (isinstance(value, float) or
- type(value) in six.integer_types):
- value = six.text_type(value)
- utils.check_string_length(value, 'extra_specs value',
- max_length=255)
- except exception.InvalidInput as error:
- raise exc.HTTPBadRequest(explanation=error.format_message())
-
- def index(self, req, flavor_id):
- """Returns the list of extra specs for a given flavor."""
- context = req.environ['nova.context']
- authorize(context, action='index')
- return self._get_extra_specs(context, flavor_id)
-
- def create(self, req, flavor_id, body):
- context = req.environ['nova.context']
- authorize(context, action='create')
- self._check_body(body)
- specs = body.get('extra_specs')
- self._check_extra_specs(specs)
- flavor = common.get_flavor(context, flavor_id)
-
- try:
- flavor.extra_specs = dict(flavor.extra_specs, **specs)
- flavor.save()
- except exception.FlavorExtraSpecUpdateCreateFailed as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.FlavorNotFound as error:
- raise exc.HTTPNotFound(explanation=error.format_message())
- return body
-
- def update(self, req, flavor_id, id, body):
- context = req.environ['nova.context']
- authorize(context, action='update')
- self._check_extra_specs(body)
- if id not in body:
- expl = _('Request body and URI mismatch')
- raise exc.HTTPBadRequest(explanation=expl)
- if len(body) > 1:
- expl = _('Request body contains too many items')
- raise exc.HTTPBadRequest(explanation=expl)
- flavor = common.get_flavor(context, flavor_id)
- try:
- flavor.extra_specs = dict(flavor.extra_specs, **body)
- flavor.save()
- except exception.FlavorExtraSpecUpdateCreateFailed as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.FlavorNotFound as error:
- raise exc.HTTPNotFound(explanation=error.format_message())
- return body
-
- def show(self, req, flavor_id, id):
- """Return a single extra spec item."""
- context = req.environ['nova.context']
- authorize(context, action='show')
- flavor = common.get_flavor(context, flavor_id)
-
- try:
- return {id: flavor.extra_specs[id]}
- except KeyError:
- msg = _("Flavor %(flavor_id)s has no extra specs with "
- "key %(key)s.") % dict(flavor_id=flavor_id,
- key=id)
- raise exc.HTTPNotFound(explanation=msg)
-
- def delete(self, req, flavor_id, id):
- """Deletes an existing extra spec."""
- context = req.environ['nova.context']
- authorize(context, action='delete')
- flavor = common.get_flavor(context, flavor_id)
-
- try:
- del flavor.extra_specs[id]
- flavor.save()
- except (exception.FlavorNotFound,
- exception.FlavorExtraSpecsNotFound) as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- except KeyError:
- msg = _("Flavor %(flavor_id)s has no extra specs with "
- "key %(key)s.") % dict(flavor_id=flavor_id,
- key=id)
- raise exc.HTTPNotFound(explanation=msg)
-
-
-class Flavorextraspecs(extensions.ExtensionDescriptor):
- """Instance type (flavor) extra specs."""
-
- name = "FlavorExtraSpecs"
- alias = "os-flavor-extra-specs"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "flavor_extra_specs/api/v1.1")
- updated = "2011-06-23T00:00:00Z"
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension(
- 'os-extra_specs',
- FlavorExtraSpecsController(),
- parent=dict(member_name='flavor', collection_name='flavors'))
-
- resources.append(res)
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/flavormanage.py b/nova/api/openstack/compute/legacy_v2/contrib/flavormanage.py
deleted file mode 100644
index 7d8deaa1ad..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/flavormanage.py
+++ /dev/null
@@ -1,112 +0,0 @@
-# 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
-
-from nova.api.openstack.compute.views import flavors as flavors_view
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova.compute import flavors
-from nova import exception
-from nova.i18n import _
-from nova import objects
-
-
-authorize = extensions.extension_authorizer('compute', 'flavormanage')
-
-
-class FlavorManageController(wsgi.Controller):
- """The Flavor Lifecycle API controller for the OpenStack API."""
- _view_builder_class = flavors_view.ViewBuilder
-
- def __init__(self):
- super(FlavorManageController, self).__init__()
-
- @wsgi.action("delete")
- def _delete(self, req, id):
- context = req.environ['nova.context']
- authorize(context)
-
- flavor = objects.Flavor(context=context, flavorid=id)
- try:
- flavor.destroy()
- except exception.FlavorNotFound as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
-
- return webob.Response(status_int=202)
-
- @wsgi.action("create")
- def _create(self, req, body):
- context = req.environ['nova.context']
- authorize(context)
- if not self.is_valid_body(body, 'flavor'):
- msg = _("Invalid request body")
- raise webob.exc.HTTPBadRequest(explanation=msg)
- vals = body['flavor']
- name = vals.get('name')
- if name is None:
- msg = _("A valid name parameter is required")
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- flavorid = vals.get('id')
- memory = vals.get('ram')
- if memory is None:
- msg = _("A valid ram parameter is required")
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- vcpus = vals.get('vcpus')
- if vcpus is None:
- msg = _("A valid vcpus parameter is required")
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- root_gb = vals.get('disk')
- if root_gb is None:
- msg = _("A valid disk parameter is required")
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- ephemeral_gb = vals.get('OS-FLV-EXT-DATA:ephemeral', 0)
- swap = vals.get('swap', 0)
- rxtx_factor = vals.get('rxtx_factor', 1.0)
- is_public = vals.get('os-flavor-access:is_public', True)
-
- try:
- flavor = flavors.create(name, memory, vcpus, root_gb,
- ephemeral_gb=ephemeral_gb,
- flavorid=flavorid, swap=swap,
- rxtx_factor=rxtx_factor,
- is_public=is_public)
- req.cache_db_flavor(flavor)
- except (exception.FlavorExists,
- exception.FlavorIdExists) as err:
- raise webob.exc.HTTPConflict(explanation=err.format_message())
- except exception.ObjectActionError:
- raise webob.exc.HTTPConflict(explanation=_(
- 'Not all flavors have been migrated to the API database'))
- except exception.InvalidInput as exc:
- raise webob.exc.HTTPBadRequest(explanation=exc.format_message())
-
- return self._view_builder.show(req, flavor)
-
-
-class Flavormanage(extensions.ExtensionDescriptor):
- """Flavor create/delete API support."""
-
- name = "FlavorManage"
- alias = "os-flavor-manage"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "flavor_manage/api/v1.1")
- updated = "2012-01-19T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = FlavorManageController()
- extension = extensions.ControllerExtension(self, 'flavors', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/floating_ip_dns.py b/nova/api/openstack/compute/legacy_v2/contrib/floating_ip_dns.py
deleted file mode 100644
index b462542679..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/floating_ip_dns.py
+++ /dev/null
@@ -1,279 +0,0 @@
-# Copyright 2011 Andrew Bogott for the Wikimedia Foundation
-#
-# 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 oslo_utils import netutils
-from six.moves import urllib
-import webob
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import context as nova_context
-from nova import exception
-from nova.i18n import _
-from nova import network
-
-
-authorize = extensions.extension_authorizer('compute', 'floating_ip_dns')
-
-
-def _translate_dns_entry_view(dns_entry):
- result = {}
- result['ip'] = dns_entry.get('ip')
- result['id'] = dns_entry.get('id')
- result['type'] = dns_entry.get('type')
- result['domain'] = dns_entry.get('domain')
- result['name'] = dns_entry.get('name')
- return {'dns_entry': result}
-
-
-def _translate_dns_entries_view(dns_entries):
- return {'dns_entries': [_translate_dns_entry_view(entry)['dns_entry']
- for entry in dns_entries]}
-
-
-def _translate_domain_entry_view(domain_entry):
- result = {}
- result['domain'] = domain_entry.get('domain')
- result['scope'] = domain_entry.get('scope')
- result['project'] = domain_entry.get('project')
- result['availability_zone'] = domain_entry.get('availability_zone')
- return {'domain_entry': result}
-
-
-def _translate_domain_entries_view(domain_entries):
- return {'domain_entries':
- [_translate_domain_entry_view(entry)['domain_entry']
- for entry in domain_entries]}
-
-
-def _unquote_domain(domain):
- """Unquoting function for receiving a domain name in a URL.
-
- Domain names tend to have .'s in them. Urllib doesn't quote dots,
- but Routes tends to choke on them, so we need an extra level of
- by-hand quoting here.
- """
- return urllib.parse.unquote(domain).replace('%2E', '.')
-
-
-def _create_dns_entry(ip, name, domain):
- return {'ip': ip, 'name': name, 'domain': domain}
-
-
-def _create_domain_entry(domain, scope=None, project=None, av_zone=None):
- return {'domain': domain, 'scope': scope, 'project': project,
- 'availability_zone': av_zone}
-
-
-class FloatingIPDNSDomainController(object):
- """DNS domain controller for OpenStack API."""
-
- def __init__(self):
- self.network_api = network.API()
- super(FloatingIPDNSDomainController, self).__init__()
-
- def index(self, req):
- """Return a list of available DNS domains."""
- context = req.environ['nova.context']
- authorize(context)
- try:
- domains = self.network_api.get_dns_domains(context)
- except NotImplementedError:
- msg = _("Unable to get dns domain")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
-
- domainlist = [_create_domain_entry(domain['domain'],
- domain.get('scope'),
- domain.get('project'),
- domain.get('availability_zone'))
- for domain in domains]
-
- return _translate_domain_entries_view(domainlist)
-
- def update(self, req, id, body):
- """Add or modify domain entry."""
- context = req.environ['nova.context']
- authorize(context)
- # NOTE(shaohe-feng): back-compatible with db layer hard-code
- # admin permission checks.
- nova_context.require_admin_context(context)
- fqdomain = _unquote_domain(id)
- try:
- entry = body['domain_entry']
- scope = entry['scope']
- except (TypeError, KeyError):
- raise webob.exc.HTTPUnprocessableEntity()
- project = entry.get('project', None)
- av_zone = entry.get('availability_zone', None)
- if (scope not in ('private', 'public') or
- project and av_zone or
- scope == 'private' and project or
- scope == 'public' and av_zone):
- raise webob.exc.HTTPUnprocessableEntity()
- if scope == 'private':
- create_dns_domain = self.network_api.create_private_dns_domain
- area_name, area = 'availability_zone', av_zone
- else:
- create_dns_domain = self.network_api.create_public_dns_domain
- area_name, area = 'project', project
- try:
- create_dns_domain(context, fqdomain, area)
- except NotImplementedError:
- msg = _("Unable to create dns domain")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
-
- return _translate_domain_entry_view({'domain': fqdomain,
- 'scope': scope,
- area_name: area})
-
- def delete(self, req, id):
- """Delete the domain identified by id."""
- context = req.environ['nova.context']
- authorize(context)
- # NOTE(shaohe-feng): back-compatible with db layer hard-code
- # admin permission checks.
- nova_context.require_admin_context(context)
- domain = _unquote_domain(id)
-
- # Delete the whole domain
- try:
- self.network_api.delete_dns_domain(context, domain)
- except exception.NotFound as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
- except NotImplementedError:
- msg = _("Unable to delete dns domain")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
-
- return webob.Response(status_int=202)
-
-
-class FloatingIPDNSEntryController(object):
- """DNS Entry controller for OpenStack API."""
-
- def __init__(self):
- self.network_api = network.API()
- super(FloatingIPDNSEntryController, self).__init__()
-
- def show(self, req, domain_id, id):
- """Return the DNS entry that corresponds to domain_id and id."""
- context = req.environ['nova.context']
- authorize(context)
- domain = _unquote_domain(domain_id)
-
- floating_ip = None
- # Check whether id is a valid ipv4/ipv6 address.
- if netutils.is_valid_ip(id):
- floating_ip = id
-
- try:
- if floating_ip:
- entries = self.network_api.get_dns_entries_by_address(
- context, floating_ip, domain)
- else:
- entries = self.network_api.get_dns_entries_by_name(
- context, id, domain)
- except NotImplementedError:
- msg = _("Unable to get dns entry")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
-
- if not entries:
- explanation = _("DNS entries not found.")
- raise webob.exc.HTTPNotFound(explanation=explanation)
-
- if floating_ip:
- entrylist = [_create_dns_entry(floating_ip, entry, domain)
- for entry in entries]
- dns_entries = _translate_dns_entries_view(entrylist)
- return wsgi.ResponseObject(dns_entries)
-
- entry = _create_dns_entry(entries[0], id, domain)
- return _translate_dns_entry_view(entry)
-
- def update(self, req, domain_id, id, body):
- """Add or modify dns entry."""
- context = req.environ['nova.context']
- authorize(context)
- domain = _unquote_domain(domain_id)
- name = id
- try:
- entry = body['dns_entry']
- address = entry['ip']
- dns_type = entry['dns_type']
- except (TypeError, KeyError):
- raise webob.exc.HTTPUnprocessableEntity()
-
- try:
- entries = self.network_api.get_dns_entries_by_name(
- context, name, domain)
- if not entries:
- # create!
- self.network_api.add_dns_entry(context, address, name,
- dns_type, domain)
- else:
- # modify!
- self.network_api.modify_dns_entry(context, name, address,
- domain)
- except NotImplementedError:
- msg = _("Unable to create dns entry")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
-
- return _translate_dns_entry_view({'ip': address,
- 'name': name,
- 'type': dns_type,
- 'domain': domain})
-
- def delete(self, req, domain_id, id):
- """Delete the entry identified by req and id."""
- context = req.environ['nova.context']
- authorize(context)
- domain = _unquote_domain(domain_id)
- name = id
-
- try:
- self.network_api.delete_dns_entry(context, name, domain)
- except exception.NotFound as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
- except NotImplementedError:
- msg = _("Unable to delete dns entry")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
-
- return webob.Response(status_int=202)
-
-
-class Floating_ip_dns(extensions.ExtensionDescriptor):
- """Floating IP DNS support."""
-
- name = "FloatingIpDns"
- alias = "os-floating-ip-dns"
- namespace = "http://docs.openstack.org/ext/floating_ip_dns/api/v1.1"
- updated = "2011-12-23T00:00:00Z"
-
- def __init__(self, ext_mgr):
- self.network_api = network.API()
- super(Floating_ip_dns, self).__init__(ext_mgr)
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension('os-floating-ip-dns',
- FloatingIPDNSDomainController())
- resources.append(res)
-
- res = extensions.ResourceExtension('entries',
- FloatingIPDNSEntryController(),
- parent={'member_name': 'domain',
- 'collection_name': 'os-floating-ip-dns'})
- resources.append(res)
-
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/floating_ip_pools.py b/nova/api/openstack/compute/legacy_v2/contrib/floating_ip_pools.py
deleted file mode 100644
index 145c51e9b1..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/floating_ip_pools.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
-#
-# 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 extensions
-from nova import network
-
-
-authorize = extensions.extension_authorizer('compute', 'floating_ip_pools')
-
-
-def _translate_floating_ip_view(pool_name):
- return {
- 'name': pool_name,
- }
-
-
-def _translate_floating_ip_pools_view(pools):
- return {
- 'floating_ip_pools': [_translate_floating_ip_view(pool_name)
- for pool_name in pools]
- }
-
-
-class FloatingIPPoolsController(object):
- """The Floating IP Pool API controller for the OpenStack API."""
-
- def __init__(self):
- self.network_api = network.API()
- super(FloatingIPPoolsController, self).__init__()
-
- def index(self, req):
- """Return a list of pools."""
- context = req.environ['nova.context']
- authorize(context)
- pools = self.network_api.get_floating_ip_pools(context)
- return _translate_floating_ip_pools_view(pools)
-
-
-class Floating_ip_pools(extensions.ExtensionDescriptor):
- """Floating IPs support."""
-
- name = "FloatingIpPools"
- alias = "os-floating-ip-pools"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "floating_ip_pools/api/v1.1")
- updated = "2012-01-04T00:00:00Z"
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension('os-floating-ip-pools',
- FloatingIPPoolsController(),
- member_actions={})
- resources.append(res)
-
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/floating_ips.py b/nova/api/openstack/compute/legacy_v2/contrib/floating_ips.py
deleted file mode 100644
index 59e5df440b..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/floating_ips.py
+++ /dev/null
@@ -1,333 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
-# Copyright 2011 Grid Dynamics
-# Copyright 2011 Eldar Nugaev, Kirill Shileev, Ilya Alekseyev
-#
-# 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 netaddr
-from oslo_log import log as logging
-from oslo_utils import uuidutils
-import webob
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import compute
-from nova.compute import utils as compute_utils
-from nova import exception
-from nova.i18n import _
-from nova.i18n import _LW
-from nova import network
-
-
-LOG = logging.getLogger(__name__)
-authorize = extensions.extension_authorizer('compute', 'floating_ips')
-
-
-def _translate_floating_ip_view(floating_ip):
- result = {
- 'id': floating_ip['id'],
- 'ip': floating_ip['address'],
- 'pool': floating_ip['pool'],
- }
-
- # If fixed_ip is unset on floating_ip, then we can't get any of the next
- # stuff, so we'll just short-circuit
- if 'fixed_ip' not in floating_ip:
- result['fixed_ip'] = None
- result['instance_id'] = None
- return {'floating_ip': result}
-
- # TODO(rlrossit): These look like dicts, but they're actually versioned
- # objects, so we need to do these contain checks because they will not be
- # caught by the exceptions below (it raises NotImplementedError and
- # OrphanedObjectError. This comment can probably be removed when
- # the dict syntax goes away.
- try:
- if 'address' in floating_ip['fixed_ip']:
- result['fixed_ip'] = floating_ip['fixed_ip']['address']
- else:
- result['fixed_ip'] = None
- except (TypeError, KeyError, AttributeError):
- result['fixed_ip'] = None
- try:
- if 'instance_uuid' in floating_ip['fixed_ip']:
- result['instance_id'] = floating_ip['fixed_ip']['instance_uuid']
- else:
- result['instance_id'] = None
- except (TypeError, KeyError, AttributeError):
- result['instance_id'] = None
- return {'floating_ip': result}
-
-
-def _translate_floating_ips_view(floating_ips):
- return {'floating_ips': [_translate_floating_ip_view(ip)['floating_ip']
- for ip in floating_ips]}
-
-
-def get_instance_by_floating_ip_addr(self, context, address):
- snagiibfa = self.network_api.get_instance_id_by_floating_address
- instance_id = snagiibfa(context, address)
- if instance_id:
- return common.get_instance(self.compute_api, context, instance_id)
-
-
-def disassociate_floating_ip(self, context, instance, address):
- try:
- self.network_api.disassociate_floating_ip(context, instance, address)
- except exception.Forbidden:
- raise webob.exc.HTTPForbidden()
- except exception.CannotDisassociateAutoAssignedFloatingIP:
- msg = _('Cannot disassociate auto assigned floating IP')
- raise webob.exc.HTTPForbidden(explanation=msg)
-
-
-class FloatingIPController(object):
- """The Floating IPs API controller for the OpenStack API."""
-
- def __init__(self):
- self.compute_api = compute.API()
- self.network_api = network.API()
- super(FloatingIPController, self).__init__()
-
- def show(self, req, id):
- """Return data about the given floating IP."""
- context = req.environ['nova.context']
- authorize(context)
-
- try:
- floating_ip = self.network_api.get_floating_ip(context, id)
- except (exception.NotFound, exception.InvalidID):
- msg = _("Floating IP not found for ID %s") % id
- raise webob.exc.HTTPNotFound(explanation=msg)
-
- return _translate_floating_ip_view(floating_ip)
-
- def index(self, req):
- """Return a list of floating IPs allocated to a project."""
- context = req.environ['nova.context']
- authorize(context)
-
- floating_ips = self.network_api.get_floating_ips_by_project(context)
-
- return _translate_floating_ips_view(floating_ips)
-
- def create(self, req, body=None):
- context = req.environ['nova.context']
- authorize(context)
-
- pool = None
- if body and 'pool' in body:
- pool = body['pool']
- try:
- address = self.network_api.allocate_floating_ip(context, pool)
- ip = self.network_api.get_floating_ip_by_address(context, address)
- except exception.NoMoreFloatingIps:
- if pool:
- msg = _("No more floating IPs in pool %s.") % pool
- else:
- msg = _("No more floating IPs available.")
- raise webob.exc.HTTPNotFound(explanation=msg)
- except exception.FloatingIpLimitExceeded:
- if pool:
- msg = _("IP allocation over quota in pool %s.") % pool
- else:
- msg = _("IP allocation over quota.")
- raise webob.exc.HTTPForbidden(explanation=msg)
- except exception.FloatingIpPoolNotFound as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
- except exception.FloatingIpBadRequest as e:
- raise webob.exc.HTTPBadRequest(explanation=e.format_message())
-
- return _translate_floating_ip_view(ip)
-
- def delete(self, req, id):
- context = req.environ['nova.context']
- authorize(context)
-
- # get the floating ip object
- try:
- floating_ip = self.network_api.get_floating_ip(context, id)
- except (exception.NotFound, exception.InvalidID):
- msg = _("Floating IP not found for ID %s") % id
- raise webob.exc.HTTPNotFound(explanation=msg)
- address = floating_ip['address']
-
- # get the associated instance object (if any)
- instance = get_instance_by_floating_ip_addr(self, context, address)
- try:
- self.network_api.disassociate_and_release_floating_ip(
- context, instance, floating_ip)
- except exception.Forbidden:
- raise webob.exc.HTTPForbidden()
- except exception.CannotDisassociateAutoAssignedFloatingIP:
- msg = _('Cannot disassociate auto assigned floating IP')
- raise webob.exc.HTTPForbidden(explanation=msg)
- return webob.Response(status_int=202)
-
-
-class FloatingIPActionController(wsgi.Controller):
- def __init__(self, ext_mgr=None, *args, **kwargs):
- super(FloatingIPActionController, self).__init__(*args, **kwargs)
- self.compute_api = compute.API()
- self.network_api = network.API()
- self.ext_mgr = ext_mgr
-
- @wsgi.action('addFloatingIp')
- def _add_floating_ip(self, req, id, body):
- """Associate floating_ip to an instance."""
- context = req.environ['nova.context']
- authorize(context)
-
- try:
- address = body['addFloatingIp']['address']
- except TypeError:
- msg = _("Missing parameter dict")
- raise webob.exc.HTTPBadRequest(explanation=msg)
- except KeyError:
- msg = _("Address not specified")
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- instance = common.get_instance(self.compute_api, context, id)
- cached_nwinfo = compute_utils.get_nw_info_for_instance(instance)
- if not cached_nwinfo:
- LOG.warning(
- _LW('Info cache is %r during associate'), instance.info_cache,
- instance=instance)
- msg = _('No nw_info cache associated with instance')
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- fixed_ips = cached_nwinfo.fixed_ips()
- if not fixed_ips:
- msg = _('No fixed IPs associated to instance')
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- fixed_address = None
- if self.ext_mgr.is_loaded('os-extended-floating-ips'):
- if 'fixed_address' in body['addFloatingIp']:
- fixed_address = body['addFloatingIp']['fixed_address']
- for fixed in fixed_ips:
- if fixed['address'] == fixed_address:
- break
- else:
- msg = _('Specified fixed address not assigned to instance')
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- if not fixed_address:
- try:
- fixed_address = next(ip['address'] for ip in fixed_ips
- if netaddr.valid_ipv4(ip['address']))
- except StopIteration:
- msg = _('Unable to associate floating IP %(address)s '
- 'to any fixed IPs for instance %(id)s. '
- 'Instance has no fixed IPv4 addresses to '
- 'associate.') % (
- {'address': address, 'id': id})
- raise webob.exc.HTTPBadRequest(explanation=msg)
- if len(fixed_ips) > 1:
- LOG.warning(_LW('multiple fixed_ips exist, using the first '
- 'IPv4 fixed_ip: %s'), fixed_address)
-
- try:
- self.network_api.associate_floating_ip(context, instance,
- floating_address=address,
- fixed_address=fixed_address)
- except exception.FloatingIpAssociated:
- msg = _('floating IP is already associated')
- raise webob.exc.HTTPBadRequest(explanation=msg)
- except exception.NoFloatingIpInterface:
- msg = _('l3driver call to add floating IP failed')
- raise webob.exc.HTTPBadRequest(explanation=msg)
- except exception.FloatingIpNotFoundForAddress:
- msg = _('floating IP not found')
- raise webob.exc.HTTPNotFound(explanation=msg)
- except exception.Forbidden as e:
- raise webob.exc.HTTPForbidden(explanation=e.format_message())
- except Exception as e:
- msg = _('Unable to associate floating IP %(address)s to '
- 'fixed IP %(fixed_address)s for instance %(id)s. '
- 'Error: %(error)s') % (
- {'address': address, 'fixed_address': fixed_address,
- 'id': id, 'error': e})
- LOG.exception(msg)
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- return webob.Response(status_int=202)
-
- @wsgi.action('removeFloatingIp')
- def _remove_floating_ip(self, req, id, body):
- """Dissociate floating_ip from an instance."""
- context = req.environ['nova.context']
- authorize(context)
-
- try:
- address = body['removeFloatingIp']['address']
- except TypeError:
- msg = _("Missing parameter dict")
- raise webob.exc.HTTPBadRequest(explanation=msg)
- except KeyError:
- msg = _("Address not specified")
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- # get the floating ip object
- try:
- floating_ip = self.network_api.get_floating_ip_by_address(context,
- address)
- except exception.FloatingIpNotFoundForAddress:
- msg = _("floating IP not found")
- raise webob.exc.HTTPNotFound(explanation=msg)
-
- # get the associated instance object (if any)
- instance = get_instance_by_floating_ip_addr(self, context, address)
-
- # disassociate if associated
- if (instance and
- floating_ip.get('fixed_ip_id') and
- (uuidutils.is_uuid_like(id) and
- [instance.uuid == id] or
- [instance.id == id])[0]):
- try:
- disassociate_floating_ip(self, context, instance, address)
- except exception.FloatingIpNotAssociated:
- msg = _('Floating IP is not associated')
- raise webob.exc.HTTPBadRequest(explanation=msg)
- return webob.Response(status_int=202)
- else:
- msg = _("Floating IP %(address)s is not associated with instance "
- "%(id)s.") % {'address': address, 'id': id}
- raise webob.exc.HTTPConflict(explanation=msg)
-
-
-class Floating_ips(extensions.ExtensionDescriptor):
- """Floating IPs support."""
-
- name = "FloatingIps"
- alias = "os-floating-ips"
- namespace = "http://docs.openstack.org/compute/ext/floating_ips/api/v1.1"
- updated = "2011-06-16T00:00:00Z"
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension('os-floating-ips',
- FloatingIPController(),
- member_actions={})
- resources.append(res)
-
- return resources
-
- def get_controller_extensions(self):
- controller = FloatingIPActionController(self.ext_mgr)
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/floating_ips_bulk.py b/nova/api/openstack/compute/legacy_v2/contrib/floating_ips_bulk.py
deleted file mode 100644
index 35632c4d6b..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/floating_ips_bulk.py
+++ /dev/null
@@ -1,171 +0,0 @@
-# Copyright 2012 IBM Corp.
-#
-# 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 netaddr
-import six
-import webob.exc
-
-from nova.api.openstack import extensions
-import nova.conf
-from nova import context as nova_context
-from nova import exception
-from nova.i18n import _
-from nova import objects
-
-CONF = nova.conf.CONF
-
-
-authorize = extensions.extension_authorizer('compute', 'floating_ips_bulk')
-
-
-class FloatingIPBulkController(object):
-
- def index(self, req):
- """Return a list of all floating IPs."""
- context = req.environ['nova.context']
- authorize(context)
-
- return self._get_floating_ip_info(context)
-
- def show(self, req, id):
- """Return a list of all floating IPs for a given host."""
- context = req.environ['nova.context']
- authorize(context)
-
- return self._get_floating_ip_info(context, id)
-
- def _get_floating_ip_info(self, context, host=None):
- floating_ip_info = {"floating_ip_info": []}
- # NOTE(shaohe-feng): back-compatible with db layer hard-code
- # admin permission checks.
- nova_context.require_admin_context(context)
-
- if host is None:
- try:
- floating_ips = objects.FloatingIPList.get_all(context)
- except exception.NoFloatingIpsDefined:
- return floating_ip_info
- else:
- try:
- floating_ips = objects.FloatingIPList.get_by_host(context,
- host)
- except exception.FloatingIpNotFoundForHost as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
-
- for floating_ip in floating_ips:
- instance_uuid = None
- fixed_ip = None
- if floating_ip.fixed_ip:
- instance_uuid = floating_ip.fixed_ip.instance_uuid
- fixed_ip = str(floating_ip.fixed_ip.address)
-
- result = {'address': str(floating_ip.address),
- 'pool': floating_ip.pool,
- 'interface': floating_ip.interface,
- 'project_id': floating_ip.project_id,
- 'instance_uuid': instance_uuid,
- 'fixed_ip': fixed_ip}
- floating_ip_info['floating_ip_info'].append(result)
-
- return floating_ip_info
-
- def create(self, req, body):
- """Bulk create floating IPs."""
- context = req.environ['nova.context']
- authorize(context)
-
- if 'floating_ips_bulk_create' not in body:
- raise webob.exc.HTTPUnprocessableEntity()
- params = body['floating_ips_bulk_create']
-
- if 'ip_range' not in params:
- raise webob.exc.HTTPUnprocessableEntity()
- ip_range = params['ip_range']
-
- pool = params.get('pool', CONF.default_floating_pool)
- interface = params.get('interface', CONF.public_interface)
-
- try:
- ips = [objects.FloatingIPList.make_ip_info(addr, pool, interface)
- for addr in self._address_to_hosts(ip_range)]
- except exception.InvalidInput as exc:
- raise webob.exc.HTTPBadRequest(explanation=exc.format_message())
-
- try:
- objects.FloatingIPList.create(context, ips)
- except exception.FloatingIpExists as exc:
- raise webob.exc.HTTPConflict(explanation=exc.format_message())
-
- return {"floating_ips_bulk_create": {"ip_range": ip_range,
- "pool": pool,
- "interface": interface}}
-
- def update(self, req, id, body):
- """Bulk delete floating IPs."""
- context = req.environ['nova.context']
- authorize(context)
-
- if id != "delete":
- msg = _("Unknown action")
- raise webob.exc.HTTPNotFound(explanation=msg)
-
- try:
- ip_range = body['ip_range']
- except (TypeError, KeyError):
- raise webob.exc.HTTPUnprocessableEntity()
-
- try:
- ips = (objects.FloatingIPList.make_ip_info(address, None, None)
- for address in self._address_to_hosts(ip_range))
- except exception.InvalidInput as exc:
- raise webob.exc.HTTPBadRequest(explanation=exc.format_message())
- objects.FloatingIPList.destroy(context, ips)
-
- return {"floating_ips_bulk_delete": ip_range}
-
- def _address_to_hosts(self, addresses):
- """Iterate over hosts within an address range.
-
- If an explicit range specifier is missing, the parameter is
- interpreted as a specific individual address.
- """
- try:
- return [netaddr.IPAddress(addresses)]
- except ValueError:
- net = netaddr.IPNetwork(addresses)
- if net.size < 4:
- reason = _("/%s should be specified as single address(es) "
- "not in cidr format") % net.prefixlen
- raise exception.InvalidInput(reason=reason)
- else:
- return net.iter_hosts()
- except netaddr.AddrFormatError as exc:
- raise exception.InvalidInput(reason=six.text_type(exc))
-
-
-class Floating_ips_bulk(extensions.ExtensionDescriptor):
- """Bulk handling of Floating IPs."""
-
- name = "FloatingIpsBulk"
- alias = "os-floating-ips-bulk"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "floating_ips_bulk/api/v2")
- updated = "2012-10-29T19:25:27Z"
-
- def get_resources(self):
- resources = []
- resource = extensions.ResourceExtension('os-floating-ips-bulk',
- FloatingIPBulkController())
- resources.append(resource)
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/fping.py b/nova/api/openstack/compute/legacy_v2/contrib/fping.py
deleted file mode 100644
index a10b231ffe..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/fping.py
+++ /dev/null
@@ -1,147 +0,0 @@
-# Copyright 2011 Grid Dynamics
-# Copyright 2011 OpenStack Foundation
-# 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.
-
-import itertools
-import os
-
-import six
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova import compute
-import nova.conf
-from nova.i18n import _
-from nova import utils
-
-authorize = extensions.extension_authorizer('compute', 'fping')
-authorize_all_tenants = extensions.extension_authorizer(
- 'compute', 'fping:all_tenants')
-
-CONF = nova.conf.CONF
-
-
-class FpingController(object):
-
- def __init__(self, network_api=None):
- self.compute_api = compute.API()
- self.last_call = {}
-
- def check_fping(self):
- if not os.access(CONF.fping_path, os.X_OK):
- raise exc.HTTPServiceUnavailable(
- explanation=_("fping utility is not found."))
-
- @staticmethod
- def fping(ips):
- fping_ret = utils.execute(CONF.fping_path, *ips,
- check_exit_code=False)
- if not fping_ret:
- return set()
- alive_ips = set()
- for line in fping_ret[0].split("\n"):
- ip = line.split(" ", 1)[0]
- if "alive" in line:
- alive_ips.add(ip)
- return alive_ips
-
- @staticmethod
- def _get_instance_ips(context, instance):
- ret = []
- for network in common.get_networks_for_instance(
- context, instance).values():
- all_ips = itertools.chain(network["ips"], network["floating_ips"])
- ret += [ip["address"] for ip in all_ips]
- return ret
-
- def index(self, req):
- context = req.environ["nova.context"]
- search_opts = dict(deleted=False)
- if "all_tenants" in req.GET:
- authorize_all_tenants(context)
- else:
- authorize(context)
- if context.project_id:
- search_opts["project_id"] = context.project_id
- else:
- search_opts["user_id"] = context.user_id
- self.check_fping()
- include = req.GET.get("include", None)
- if include:
- include = set(include.split(","))
- exclude = set()
- else:
- include = None
- exclude = req.GET.get("exclude", None)
- if exclude:
- exclude = set(exclude.split(","))
- else:
- exclude = set()
-
- instance_list = self.compute_api.get_all(
- context, search_opts=search_opts, want_objects=True)
- ip_list = []
- instance_ips = {}
- instance_projects = {}
-
- for instance in instance_list:
- uuid = instance.uuid
- if uuid in exclude or (include is not None and
- uuid not in include):
- continue
- ips = [str(ip) for ip in self._get_instance_ips(context, instance)]
- instance_ips[uuid] = ips
- instance_projects[uuid] = instance.project_id
- ip_list += ips
- alive_ips = self.fping(ip_list)
- res = []
- for instance_uuid, ips in six.iteritems(instance_ips):
- res.append({
- "id": instance_uuid,
- "project_id": instance_projects[instance_uuid],
- "alive": bool(set(ips) & alive_ips),
- })
- return {"servers": res}
-
- def show(self, req, id):
- context = req.environ["nova.context"]
- authorize(context)
- self.check_fping()
- instance = common.get_instance(self.compute_api, context, id)
- ips = [str(ip) for ip in self._get_instance_ips(context, instance)]
- alive_ips = self.fping(ips)
- return {
- "server": {
- "id": instance.uuid,
- "project_id": instance.project_id,
- "alive": bool(set(ips) & alive_ips),
- }
- }
-
-
-class Fping(extensions.ExtensionDescriptor):
- """Fping Management Extension."""
-
- name = "Fping"
- alias = "os-fping"
- namespace = "http://docs.openstack.org/compute/ext/fping/api/v1.1"
- updated = "2012-07-06T00:00:00Z"
-
- def get_resources(self):
- res = extensions.ResourceExtension(
- "os-fping",
- FpingController())
- return [res]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/hide_server_addresses.py b/nova/api/openstack/compute/legacy_v2/contrib/hide_server_addresses.py
deleted file mode 100644
index b6a9e89e5a..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/hide_server_addresses.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# 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.
-
-"""Extension for hiding server addresses in certain states."""
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova.compute import vm_states
-import nova.conf
-
-
-CONF = nova.conf.CONF
-
-authorize = extensions.soft_extension_authorizer('compute',
- 'hide_server_addresses')
-
-
-class Controller(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- super(Controller, self).__init__(*args, **kwargs)
- hidden_states = CONF.osapi_hide_server_address_states
-
- # NOTE(jkoelker) _ is not considered uppercase ;)
- valid_vm_states = [getattr(vm_states, state)
- for state in dir(vm_states)
- if state.isupper()]
- self.hide_address_states = [state.lower()
- for state in hidden_states
- if state in valid_vm_states]
-
- def _perhaps_hide_addresses(self, instance, resp_server):
- if instance.get('vm_state') in self.hide_address_states:
- resp_server['addresses'] = {}
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- resp = resp_obj
- if not authorize(req.environ['nova.context']):
- return
-
- if 'server' in resp.obj and 'addresses' in resp.obj['server']:
- instance = req.get_db_instance(id)
- self._perhaps_hide_addresses(instance, resp.obj['server'])
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- resp = resp_obj
- if not authorize(req.environ['nova.context']):
- return
-
- for server in list(resp.obj['servers']):
- if 'addresses' in server:
- instance = req.get_db_instance(server['id'])
- self._perhaps_hide_addresses(instance, server)
-
-
-class Hide_server_addresses(extensions.ExtensionDescriptor):
- """Support hiding server addresses in certain states."""
-
- name = 'HideServerAddresses'
- alias = 'os-hide-server-addresses'
- namespace = ('http://docs.openstack.org/compute/ext/'
- 'hide_server_addresses/api/v1.1')
- updated = '2012-12-11T00:00:00Z'
-
- def get_controller_extensions(self):
- return [extensions.ControllerExtension(self, 'servers', Controller())]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/hosts.py b/nova/api/openstack/compute/legacy_v2/contrib/hosts.py
deleted file mode 100644
index 31c4f1598b..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/hosts.py
+++ /dev/null
@@ -1,335 +0,0 @@
-# Copyright (c) 2011 OpenStack Foundation
-# 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 hosts admin extension."""
-
-from oslo_log import log as logging
-import six
-import webob.exc
-
-from nova.api.openstack import extensions
-from nova import compute
-from nova import context as nova_context
-from nova import exception
-from nova.i18n import _
-from nova.i18n import _LI
-from nova import objects
-
-LOG = logging.getLogger(__name__)
-authorize = extensions.extension_authorizer('compute', 'hosts')
-
-
-class HostController(object):
- """The Hosts API controller for the OpenStack API."""
- def __init__(self):
- self.api = compute.HostAPI()
- super(HostController, self).__init__()
-
- def index(self, req):
- """Returns a dict in the format:
-
- | {'hosts': [{'host_name': 'some.host.name',
- | 'service': 'cells',
- | 'zone': 'internal'},
- | {'host_name': 'some.other.host.name',
- | 'service': 'cells',
- | 'zone': 'internal'},
- | {'host_name': 'some.celly.host.name',
- | 'service': 'cells',
- | 'zone': 'internal'},
- | {'host_name': 'console1.host.com',
- | 'service': 'consoleauth',
- | 'zone': 'internal'},
- | {'host_name': 'network1.host.com',
- | 'service': 'network',
- | 'zone': 'internal'},
- | {'host_name': 'netwwork2.host.com',
- | 'service': 'network',
- | 'zone': 'internal'},
- | {'host_name': 'compute1.host.com',
- | 'service': 'compute',
- | 'zone': 'nova'},
- | {'host_name': 'compute2.host.com',
- | 'service': 'compute',
- | 'zone': 'nova'},
- | {'host_name': 'sched1.host.com',
- | 'service': 'scheduler',
- | 'zone': 'internal'},
- | {'host_name': 'sched2.host.com',
- | 'service': 'scheduler',
- | 'zone': 'internal'},
- | {'host_name': 'vol1.host.com',
- | 'service': 'volume',
- | 'zone': 'internal'}]}
-
- """
- context = req.environ['nova.context']
- authorize(context)
-
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks
- nova_context.require_admin_context(context)
-
- filters = {'disabled': False}
- zone = req.GET.get('zone', None)
- if zone:
- filters['availability_zone'] = zone
- services = self.api.service_get_all(context, filters=filters,
- set_zones=True)
- hosts = []
- api_services = ('nova-osapi_compute', 'nova-ec2', 'nova-metadata')
- for service in services:
- if service.binary not in api_services:
- hosts.append({'host_name': service['host'],
- 'service': service['topic'],
- 'zone': service['availability_zone']})
- return {'hosts': hosts}
-
- def update(self, req, id, body):
- """Updates a specified body.
-
- :param body: example format {'status': 'enable',
- 'maintenance_mode': 'enable'}
- """
- def read_enabled(orig_val, msg):
- """Checks a specified orig_val and returns True for 'enabled'
- and False for 'disabled'.
-
- :param orig_val: A string with either 'enable' or 'disable'. May
- be surrounded by whitespace, and case doesn't
- matter
- :param msg: The message to be passed to HTTPBadRequest. A single
- %s will be replaced with orig_val.
- """
- val = orig_val.strip().lower()
- if val == "enable":
- return True
- elif val == "disable":
- return False
- else:
- raise webob.exc.HTTPBadRequest(explanation=msg % orig_val)
- context = req.environ['nova.context']
- authorize(context)
-
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks. This has to be left only for API v2.0 because
- # this version has to be stable even if it means that only admins
- # can call this method while the policy could be changed.
- nova_context.require_admin_context(context)
-
- # See what the user wants to 'update'
- params = {k.strip().lower(): v for k, v in six.iteritems(body)}
- orig_status = status = params.pop('status', None)
- orig_maint_mode = maint_mode = params.pop('maintenance_mode', None)
- # Validate the request
- if len(params) > 0:
- # Some extra param was passed. Fail.
- explanation = _("Invalid update setting: '%s'") % list(
- params.keys())[0]
- raise webob.exc.HTTPBadRequest(explanation=explanation)
- if orig_status is not None:
- status = read_enabled(orig_status, _("Invalid status: '%s'"))
- if orig_maint_mode is not None:
- maint_mode = read_enabled(orig_maint_mode, _("Invalid mode: '%s'"))
- if status is None and maint_mode is None:
- explanation = _("'status' or 'maintenance_mode' needed for "
- "host update")
- raise webob.exc.HTTPBadRequest(explanation=explanation)
- # Make the calls and merge the results
- result = {'host': id}
- if status is not None:
- result['status'] = self._set_enabled_status(context, id, status)
- if maint_mode is not None:
- result['maintenance_mode'] = self._set_host_maintenance(context,
- id, maint_mode)
- return result
-
- def _set_host_maintenance(self, context, host_name, mode=True):
- """Start/Stop host maintenance window. On start, it triggers
- guest VMs evacuation.
- """
- LOG.info(_LI("Putting host %(host_name)s in maintenance mode "
- "%(mode)s."),
- {'host_name': host_name, 'mode': mode})
- try:
- result = self.api.set_host_maintenance(context, host_name, mode)
- except NotImplementedError:
- msg = _("Virt driver does not implement host maintenance mode.")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
- except exception.NotFound as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
- except exception.ComputeServiceUnavailable as e:
- raise webob.exc.HTTPBadRequest(explanation=e.format_message())
- if result not in ("on_maintenance", "off_maintenance"):
- raise webob.exc.HTTPBadRequest(explanation=result)
- return result
-
- def _set_enabled_status(self, context, host_name, enabled):
- """Sets the specified host's ability to accept new instances.
-
- :param enabled: a boolean - if False no new VMs will be able to start
- on the host
- """
- if enabled:
- LOG.info(_LI("Enabling host %s."), host_name)
- else:
- LOG.info(_LI("Disabling host %s."), host_name)
- try:
- result = self.api.set_host_enabled(context, host_name=host_name,
- enabled=enabled)
- except NotImplementedError:
- msg = _("Virt driver does not implement host disabled status.")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
- except exception.NotFound as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
- except exception.ComputeServiceUnavailable as e:
- raise webob.exc.HTTPBadRequest(explanation=e.format_message())
- if result not in ("enabled", "disabled"):
- raise webob.exc.HTTPBadRequest(explanation=result)
- return result
-
- def _host_power_action(self, req, host_name, action):
- """Reboots, shuts down or powers up the host."""
- context = req.environ['nova.context']
- authorize(context)
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks. This has to be left only for API v2.0 because
- # this version has to be stable even if it means that only admins
- # can call this method while the policy could be changed.
- nova_context.require_admin_context(context)
- try:
- result = self.api.host_power_action(context, host_name=host_name,
- action=action)
- except NotImplementedError:
- msg = _("Virt driver does not implement host power management.")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
- except exception.NotFound as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
- except exception.ComputeServiceUnavailable as e:
- raise webob.exc.HTTPBadRequest(explanation=e.format_message())
- return {"host": host_name, "power_action": result}
-
- def startup(self, req, id):
- return self._host_power_action(req, host_name=id, action="startup")
-
- def shutdown(self, req, id):
- return self._host_power_action(req, host_name=id, action="shutdown")
-
- def reboot(self, req, id):
- return self._host_power_action(req, host_name=id, action="reboot")
-
- @staticmethod
- def _get_total_resources(host_name, compute_node):
- return {'resource': {'host': host_name,
- 'project': '(total)',
- 'cpu': compute_node.vcpus,
- 'memory_mb': compute_node.memory_mb,
- 'disk_gb': compute_node.local_gb}}
-
- @staticmethod
- def _get_used_now_resources(host_name, compute_node):
- return {'resource': {'host': host_name,
- 'project': '(used_now)',
- 'cpu': compute_node.vcpus_used,
- 'memory_mb': compute_node.memory_mb_used,
- 'disk_gb': compute_node.local_gb_used}}
-
- @staticmethod
- def _get_resource_totals_from_instances(host_name, instances):
- cpu_sum = 0
- mem_sum = 0
- hdd_sum = 0
- for instance in instances:
- cpu_sum += instance['vcpus']
- mem_sum += instance['memory_mb']
- hdd_sum += instance['root_gb'] + instance['ephemeral_gb']
-
- return {'resource': {'host': host_name,
- 'project': '(used_max)',
- 'cpu': cpu_sum,
- 'memory_mb': mem_sum,
- 'disk_gb': hdd_sum}}
-
- @staticmethod
- def _get_resources_by_project(host_name, instances):
- # Getting usage resource per project
- project_map = {}
- for instance in instances:
- resource = project_map.setdefault(instance['project_id'],
- {'host': host_name,
- 'project': instance['project_id'],
- 'cpu': 0,
- 'memory_mb': 0,
- 'disk_gb': 0})
- resource['cpu'] += instance['vcpus']
- resource['memory_mb'] += instance['memory_mb']
- resource['disk_gb'] += (instance['root_gb'] +
- instance['ephemeral_gb'])
- return project_map
-
- def show(self, req, id):
- """Shows the physical/usage resource given by hosts.
-
- :param id: hostname
- :returns: expected to use HostShowTemplate.
- ex.::
-
- {'host': {'resource':D},..}
- D: {'host': 'hostname','project': 'admin',
- 'cpu': 1, 'memory_mb': 2048, 'disk_gb': 30}
- """
- context = req.environ['nova.context']
-
- # NOTE(eliqiao): back-compatible with db layer hard-code admin
- # permission checks. This has to be left only for API v2.0 because
- # this version has to be stable even if it means that only admins
- # can call this method while the policy could be changed.
- nova_context.require_admin_context(context)
-
- host_name = id
- try:
- compute_node = (
- objects.ComputeNode.get_first_node_by_host_for_old_compat(
- context, host_name))
- except exception.NotFound as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
- instances = self.api.instance_get_all_by_host(context, host_name)
- resources = [self._get_total_resources(host_name, compute_node)]
- resources.append(self._get_used_now_resources(host_name,
- compute_node))
- resources.append(self._get_resource_totals_from_instances(host_name,
- instances))
- by_proj_resources = self._get_resources_by_project(host_name,
- instances)
- for resource in six.itervalues(by_proj_resources):
- resources.append({'resource': resource})
- return {'host': resources}
-
-
-class Hosts(extensions.ExtensionDescriptor):
- """Admin-only host administration."""
-
- name = "Hosts"
- alias = "os-hosts"
- namespace = "http://docs.openstack.org/compute/ext/hosts/api/v1.1"
- updated = "2011-06-29T00:00:00Z"
-
- def get_resources(self):
- resources = [extensions.ResourceExtension('os-hosts',
- HostController(),
- collection_actions={'update': 'PUT'},
- member_actions={"startup": "GET", "shutdown": "GET",
- "reboot": "GET"})]
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/hypervisor_status.py b/nova/api/openstack/compute/legacy_v2/contrib/hypervisor_status.py
deleted file mode 100644
index 94bcabca48..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/hypervisor_status.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2014 Intel Corp.
-#
-# 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 extensions
-
-
-class Hypervisor_status(extensions.ExtensionDescriptor):
- """Show hypervisor status."""
-
- name = "HypervisorStatus"
- alias = "os-hypervisor-status"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "hypervisor_status/api/v1.1")
- updated = "2014-04-17T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/hypervisors.py b/nova/api/openstack/compute/legacy_v2/contrib/hypervisors.py
deleted file mode 100644
index 3b38ff57f4..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/hypervisors.py
+++ /dev/null
@@ -1,232 +0,0 @@
-# Copyright (c) 2012 OpenStack Foundation
-# 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 hypervisors admin extension."""
-
-import webob.exc
-
-from nova.api.openstack import extensions
-from nova import compute
-from nova import context as nova_context
-from nova import exception
-from nova.i18n import _
-from nova import servicegroup
-
-
-authorize = extensions.extension_authorizer('compute', 'hypervisors')
-
-
-class HypervisorsController(object):
- """The Hypervisors API controller for the OpenStack API."""
-
- def __init__(self, ext_mgr):
- self.host_api = compute.HostAPI()
- self.servicegroup_api = servicegroup.API()
- super(HypervisorsController, self).__init__()
- self.ext_mgr = ext_mgr
-
- def _view_hypervisor(self, hypervisor, service, detail, servers=None,
- **kwargs):
- hyp_dict = {
- 'id': hypervisor.id,
- 'hypervisor_hostname': hypervisor.hypervisor_hostname,
- }
-
- ext_status_loaded = self.ext_mgr.is_loaded('os-hypervisor-status')
- if ext_status_loaded:
- alive = self.servicegroup_api.service_is_up(service)
- hyp_dict['state'] = 'up' if alive else "down"
- hyp_dict['status'] = (
- 'disabled' if service.disabled else 'enabled')
-
- if detail and not servers:
- fields = ('vcpus', 'memory_mb', 'local_gb', 'vcpus_used',
- 'memory_mb_used', 'local_gb_used',
- 'hypervisor_type', 'hypervisor_version',
- 'free_ram_mb', 'free_disk_gb', 'current_workload',
- 'running_vms', 'cpu_info', 'disk_available_least')
- ext_loaded = self.ext_mgr.is_loaded('os-extended-hypervisors')
- if ext_loaded:
- fields += ('host_ip',)
- for field in fields:
- hyp_dict[field] = getattr(hypervisor, field)
-
- hyp_dict['service'] = {
- 'id': service.id,
- 'host': hypervisor.host,
- }
- if ext_status_loaded:
- hyp_dict['service'].update(
- disabled_reason=service.disabled_reason)
-
- if servers:
- hyp_dict['servers'] = [dict(name=serv['name'], uuid=serv['uuid'])
- for serv in servers]
-
- # Add any additional info
- if kwargs:
- hyp_dict.update(kwargs)
-
- return hyp_dict
-
- def index(self, req):
- context = req.environ['nova.context']
- authorize(context)
-
- # NOTE(eliqiao): back-compatible with db layer hard-code admin
- # permission checks. This has to be left only for API v2.0 because
- # this version has to be stable even if it means that only admins
- # can call this method while the policy could be changed.
- nova_context.require_admin_context(context)
-
- compute_nodes = self.host_api.compute_node_get_all(context)
- req.cache_db_compute_nodes(compute_nodes)
- return dict(hypervisors=[self._view_hypervisor(
- hyp,
- self.host_api.service_get_by_compute_host(
- context, hyp.host),
- False)
- for hyp in compute_nodes])
-
- def detail(self, req):
- context = req.environ['nova.context']
- authorize(context)
-
- # NOTE(eliqiao): back-compatible with db layer hard-code admin
- # permission checks. This has to be left only for API v2.0 because
- # this version has to be stable even if it means that only admins
- # can call this method while the policy could be changed.
- nova_context.require_admin_context(context)
-
- compute_nodes = self.host_api.compute_node_get_all(context)
- req.cache_db_compute_nodes(compute_nodes)
- return dict(hypervisors=[self._view_hypervisor(
- hyp,
- self.host_api.service_get_by_compute_host(
- context, hyp.host),
- True)
- for hyp in compute_nodes])
-
- def show(self, req, id):
- context = req.environ['nova.context']
- authorize(context)
- try:
- hyp = self.host_api.compute_node_get(context, id)
- req.cache_db_compute_node(hyp)
- except (ValueError, exception.ComputeHostNotFound):
- msg = _("Hypervisor with ID '%s' could not be found.") % id
- raise webob.exc.HTTPNotFound(explanation=msg)
- service = self.host_api.service_get_by_compute_host(
- context, hyp.host)
- return dict(hypervisor=self._view_hypervisor(hyp, service, True))
-
- def uptime(self, req, id):
- context = req.environ['nova.context']
- authorize(context)
- try:
- hyp = self.host_api.compute_node_get(context, id)
- req.cache_db_compute_node(hyp)
- except (ValueError, exception.ComputeHostNotFound):
- msg = _("Hypervisor with ID '%s' could not be found.") % id
- raise webob.exc.HTTPNotFound(explanation=msg)
-
- # Get the uptime
- try:
- host = hyp.host
- uptime = self.host_api.get_host_uptime(context, host)
- except NotImplementedError:
- msg = _("Virt driver does not implement uptime function.")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
- except exception.ComputeServiceUnavailable as e:
- raise webob.exc.HTTPBadRequest(explanation=e.format_message())
-
- service = self.host_api.service_get_by_compute_host(context, host)
- return dict(hypervisor=self._view_hypervisor(hyp, service, False,
- uptime=uptime))
-
- def search(self, req, id):
- context = req.environ['nova.context']
- authorize(context)
-
- # NOTE(eliqiao): back-compatible with db layer hard-code admin
- # permission checks. This has to be left only for API v2.0 because
- # this version has to be stable even if it means that only admins
- # can call this method while the policy could be changed.
- nova_context.require_admin_context(context)
-
- hypervisors = self.host_api.compute_node_search_by_hypervisor(
- context, id)
- if hypervisors:
- return dict(hypervisors=[self._view_hypervisor(
- hyp,
- self.host_api.service_get_by_compute_host(
- context, hyp.host),
- False)
- for hyp in hypervisors])
- else:
- msg = _("No hypervisor matching '%s' could be found.") % id
- raise webob.exc.HTTPNotFound(explanation=msg)
-
- def servers(self, req, id):
- context = req.environ['nova.context']
- authorize(context)
-
- # NOTE(eliqiao): back-compatible with db layer hard-code admin
- # permission checks. This has to be left only for API v2.0 because
- # this version has to be stable even if it means that only admins
- # can call this method while the policy could be changed.
- nova_context.require_admin_context(context)
-
- compute_nodes = self.host_api.compute_node_search_by_hypervisor(
- context, id)
- if not compute_nodes:
- msg = _("No hypervisor matching '%s' could be found.") % id
- raise webob.exc.HTTPNotFound(explanation=msg)
- hypervisors = []
- for compute_node in compute_nodes:
- instances = self.host_api.instance_get_all_by_host(context,
- compute_node.host)
- service = self.host_api.service_get_by_compute_host(
- context, compute_node.host)
- hyp = self._view_hypervisor(compute_node, service, False,
- instances)
- hypervisors.append(hyp)
- return dict(hypervisors=hypervisors)
-
- def statistics(self, req):
- context = req.environ['nova.context']
- authorize(context)
- stats = self.host_api.compute_node_statistics(context)
- return dict(hypervisor_statistics=stats)
-
-
-class Hypervisors(extensions.ExtensionDescriptor):
- """Admin-only hypervisor administration."""
-
- name = "Hypervisors"
- alias = "os-hypervisors"
- namespace = "http://docs.openstack.org/compute/ext/hypervisors/api/v1.1"
- updated = "2012-06-21T00:00:00Z"
-
- def get_resources(self):
- resources = [extensions.ResourceExtension('os-hypervisors',
- HypervisorsController(self.ext_mgr),
- collection_actions={'detail': 'GET',
- 'statistics': 'GET'},
- member_actions={'uptime': 'GET',
- 'search': 'GET',
- 'servers': 'GET'})]
-
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/image_size.py b/nova/api/openstack/compute/legacy_v2/contrib/image_size.py
deleted file mode 100644
index 265375d4bc..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/image_size.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# Copyright 2013 Rackspace Hosting
-# 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 extensions
-from nova.api.openstack import wsgi
-
-authorize = extensions.soft_extension_authorizer('compute', 'image_size')
-
-
-class ImageSizeController(wsgi.Controller):
-
- def _extend_image(self, image, image_cache):
- key = "%s:size" % Image_size.alias
- image[key] = image_cache['size']
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- context = req.environ["nova.context"]
- if authorize(context):
- image_resp = resp_obj.obj['image']
- # image guaranteed to be in the cache due to the core API adding
- # it in its 'show' method
- image_cached = req.get_db_item('images', image_resp['id'])
- self._extend_image(image_resp, image_cached)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- context = req.environ['nova.context']
- if authorize(context):
- images_resp = list(resp_obj.obj['images'])
- # images guaranteed to be in the cache due to the core API adding
- # it in its 'detail' method
- for image in images_resp:
- image_cached = req.get_db_item('images', image['id'])
- self._extend_image(image, image_cached)
-
-
-class Image_size(extensions.ExtensionDescriptor):
- """Adds image size to image listings."""
-
- name = "ImageSize"
- alias = "OS-EXT-IMG-SIZE"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "image_size/api/v1.1")
- updated = "2013-02-19T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = ImageSizeController()
- extension = extensions.ControllerExtension(self, 'images', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/instance_actions.py b/nova/api/openstack/compute/legacy_v2/contrib/instance_actions.py
deleted file mode 100644
index 290fcf9ff1..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/instance_actions.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# Copyright 2013 Rackspace Hosting
-# 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 webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import compute
-
-authorize_actions = extensions.extension_authorizer('compute',
- 'instance_actions')
-authorize_events = extensions.soft_extension_authorizer('compute',
- 'instance_actions:events')
-
-ACTION_KEYS = ['action', 'instance_uuid', 'request_id', 'user_id',
- 'project_id', 'start_time', 'message']
-EVENT_KEYS = ['event', 'start_time', 'finish_time', 'result', 'traceback']
-
-
-class InstanceActionsController(wsgi.Controller):
-
- def __init__(self):
- super(InstanceActionsController, self).__init__()
- self.compute_api = compute.API()
- self.action_api = compute.InstanceActionAPI()
-
- def _format_action(self, action_raw):
- action = {}
- for key in ACTION_KEYS:
- action[key] = action_raw.get(key)
- return action
-
- def _format_event(self, event_raw):
- event = {}
- for key in EVENT_KEYS:
- event[key] = event_raw.get(key)
- return event
-
- def index(self, req, server_id):
- """Returns the list of actions recorded for a given instance."""
- context = req.environ["nova.context"]
- instance = common.get_instance(self.compute_api, context, server_id)
- authorize_actions(context, target=instance)
- actions_raw = self.action_api.actions_get(context, instance)
- actions = [self._format_action(action) for action in actions_raw]
- return {'instanceActions': actions}
-
- def show(self, req, server_id, id):
- """Return data about the given instance action."""
- context = req.environ['nova.context']
- instance = common.get_instance(self.compute_api, context, server_id)
- authorize_actions(context, target=instance)
- action = self.action_api.action_get_by_request_id(context, instance,
- id)
- if action is None:
- raise exc.HTTPNotFound()
-
- action_id = action['id']
- action = self._format_action(action)
- if authorize_events(context):
- events_raw = self.action_api.action_events_get(context, instance,
- action_id)
- action['events'] = [self._format_event(evt) for evt in events_raw]
- return {'instanceAction': action}
-
-
-class Instance_actions(extensions.ExtensionDescriptor):
- """View a log of actions and events taken on an instance."""
-
- name = "InstanceActions"
- alias = "os-instance-actions"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "instance-actions/api/v1.1")
- updated = "2013-02-08T00:00:00Z"
-
- def get_resources(self):
- ext = extensions.ResourceExtension('os-instance-actions',
- InstanceActionsController(),
- parent=dict(
- member_name='server',
- collection_name='servers'))
- return [ext]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/instance_usage_audit_log.py b/nova/api/openstack/compute/legacy_v2/contrib/instance_usage_audit_log.py
deleted file mode 100644
index 3918c71168..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/instance_usage_audit_log.py
+++ /dev/null
@@ -1,138 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# 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.
-
-
-import datetime
-
-import webob.exc
-
-from nova.api.openstack import extensions
-from nova import compute
-import nova.conf
-from nova import context as nova_context
-from nova.i18n import _
-from nova import utils
-
-CONF = nova.conf.CONF
-
-authorize = extensions.extension_authorizer('compute',
- 'instance_usage_audit_log')
-
-
-class InstanceUsageAuditLogController(object):
- def __init__(self):
- self.host_api = compute.HostAPI()
-
- def index(self, req):
- context = req.environ['nova.context']
- authorize(context)
- task_log = self._get_audit_task_logs(context)
- return {'instance_usage_audit_logs': task_log}
-
- def show(self, req, id):
- context = req.environ['nova.context']
- authorize(context)
- try:
- if '.' in id:
- before_date = datetime.datetime.strptime(str(id),
- "%Y-%m-%d %H:%M:%S.%f")
- else:
- before_date = datetime.datetime.strptime(str(id),
- "%Y-%m-%d %H:%M:%S")
- except ValueError:
- msg = _("Invalid timestamp for date %s") % id
- raise webob.exc.HTTPBadRequest(explanation=msg)
- task_log = self._get_audit_task_logs(context,
- before=before_date)
- return {'instance_usage_audit_log': task_log}
-
- def _get_audit_task_logs(self, context, begin=None, end=None,
- before=None):
- """Returns a full log for all instance usage audit tasks on all
- computes.
-
- :param begin: datetime beginning of audit period to get logs for,
- Defaults to the beginning of the most recently completed
- audit period prior to the 'before' date.
- :param end: datetime ending of audit period to get logs for,
- Defaults to the ending of the most recently completed
- audit period prior to the 'before' date.
- :param before: By default we look for the audit period most recently
- completed before this datetime. Has no effect if both begin and end
- are specified.
- """
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks.
- nova_context.require_admin_context(context)
- defbegin, defend = utils.last_completed_audit_period(before=before)
- if begin is None:
- begin = defbegin
- if end is None:
- end = defend
- task_logs = self.host_api.task_log_get_all(context,
- "instance_usage_audit",
- begin, end)
- # We do this in this way to include disabled compute services,
- # which can have instances on them. (mdragon)
- filters = {'topic': CONF.compute_topic}
- services = self.host_api.service_get_all(context, filters=filters)
- hosts = set(serv['host'] for serv in services)
- seen_hosts = set()
- done_hosts = set()
- running_hosts = set()
- total_errors = 0
- total_items = 0
- for tlog in task_logs:
- seen_hosts.add(tlog['host'])
- if tlog['state'] == "DONE":
- done_hosts.add(tlog['host'])
- if tlog['state'] == "RUNNING":
- running_hosts.add(tlog['host'])
- total_errors += tlog['errors']
- total_items += tlog['task_items']
- log = {tl['host']: dict(state=tl['state'],
- instances=tl['task_items'],
- errors=tl['errors'],
- message=tl['message'])
- for tl in task_logs}
- missing_hosts = hosts - seen_hosts
- overall_status = "%s hosts done. %s errors." % (
- 'ALL' if len(done_hosts) == len(hosts)
- else "%s of %s" % (len(done_hosts), len(hosts)),
- total_errors)
- return dict(period_beginning=str(begin),
- period_ending=str(end),
- num_hosts=len(hosts),
- num_hosts_done=len(done_hosts),
- num_hosts_running=len(running_hosts),
- num_hosts_not_run=len(missing_hosts),
- hosts_not_run=list(missing_hosts),
- total_instances=total_items,
- total_errors=total_errors,
- overall_status=overall_status,
- log=log)
-
-
-class Instance_usage_audit_log(extensions.ExtensionDescriptor):
- """Admin-only Task Log Monitoring."""
- name = "OSInstanceUsageAuditLog"
- alias = "os-instance_usage_audit_log"
- namespace = "http://docs.openstack.org/ext/services/api/v1.1"
- updated = "2012-07-06T01:00:00Z"
-
- def get_resources(self):
- ext = extensions.ResourceExtension('os-instance_usage_audit_log',
- InstanceUsageAuditLogController())
- return [ext]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/keypairs.py b/nova/api/openstack/compute/legacy_v2/contrib/keypairs.py
deleted file mode 100644
index d4a34d035e..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/keypairs.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-# 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.
-
-"""Keypair management extension."""
-
-import webob
-import webob.exc
-
-from nova.api.openstack.compute.legacy_v2 import servers
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova.compute import api as compute_api
-from nova import exception
-from nova.i18n import _
-
-
-authorize = extensions.extension_authorizer('compute', 'keypairs')
-soft_authorize = extensions.soft_extension_authorizer('compute', 'keypairs')
-
-
-class KeypairController(object):
-
- """Keypair API controller for the OpenStack API."""
- def __init__(self):
- self.api = compute_api.KeypairAPI()
-
- def _filter_keypair(self, keypair, **attrs):
- clean = {
- 'name': keypair.name,
- 'public_key': keypair.public_key,
- 'fingerprint': keypair.fingerprint,
- }
- for attr in attrs:
- clean[attr] = keypair[attr]
- return clean
-
- def create(self, req, body):
- """Create or import keypair.
-
- Sending name will generate a key and return private_key
- and fingerprint.
-
- You can send a public_key to add an existing ssh key
-
- params: keypair object with:
- name (required) - string
- public_key (optional) - string
- """
-
- context = req.environ['nova.context']
- authorize(context, action='create')
-
- try:
- params = body['keypair']
- name = params['name']
- except KeyError:
- msg = _("Invalid request body")
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- try:
- if 'public_key' in params:
- keypair = self.api.import_key_pair(context,
- context.user_id, name,
- params['public_key'])
- keypair = self._filter_keypair(keypair, user_id=True)
- else:
- keypair, private_key = self.api.create_key_pair(
- context, context.user_id, name)
- keypair = self._filter_keypair(keypair, user_id=True)
- keypair['private_key'] = private_key
-
- return {'keypair': keypair}
-
- except exception.KeypairLimitExceeded:
- msg = _("Quota exceeded, too many key pairs.")
- raise webob.exc.HTTPForbidden(explanation=msg)
- except exception.InvalidKeypair as exc:
- raise webob.exc.HTTPBadRequest(explanation=exc.format_message())
- except exception.KeyPairExists as exc:
- raise webob.exc.HTTPConflict(explanation=exc.format_message())
-
- def delete(self, req, id):
- """Delete a keypair with a given name."""
- context = req.environ['nova.context']
- authorize(context, action='delete')
- try:
- self.api.delete_key_pair(context, context.user_id, id)
- except exception.KeypairNotFound as exc:
- raise webob.exc.HTTPNotFound(explanation=exc.format_message())
- return webob.Response(status_int=202)
-
- def show(self, req, id):
- """Return data for the given key name."""
- context = req.environ['nova.context']
- authorize(context, action='show')
-
- try:
- # The return object needs to be a dict in order to pop the 'type'
- # field, since it is incompatible with API version <= 2.1.
- keypair = self.api.get_key_pair(context, context.user_id, id)
- keypair = self._filter_keypair(keypair, created_at=True,
- deleted=True, deleted_at=True,
- id=True, user_id=True,
- updated_at=True)
- except exception.KeypairNotFound as exc:
- raise webob.exc.HTTPNotFound(explanation=exc.format_message())
- return {'keypair': keypair}
-
- def index(self, req):
- """List of keypairs for a user."""
- context = req.environ['nova.context']
- authorize(context, action='index')
- key_pairs = self.api.get_key_pairs(context, context.user_id)
- rval = []
- for key_pair in key_pairs:
- rval.append({'keypair': self._filter_keypair(key_pair)})
-
- return {'keypairs': rval}
-
-
-class Controller(servers.Controller):
-
- def _add_key_name(self, req, servers):
- for server in servers:
- db_server = req.get_db_instance(server['id'])
- # server['id'] is guaranteed to be in the cache due to
- # the core API adding it in its 'show'/'detail' methods.
- server['key_name'] = db_server['key_name']
-
- def _show(self, req, resp_obj):
- if 'server' in resp_obj.obj:
- server = resp_obj.obj['server']
- self._add_key_name(req, [server])
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- context = req.environ['nova.context']
- if soft_authorize(context):
- self._show(req, resp_obj)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- context = req.environ['nova.context']
- if 'servers' in resp_obj.obj and soft_authorize(context):
- servers = resp_obj.obj['servers']
- self._add_key_name(req, servers)
-
-
-class Keypairs(extensions.ExtensionDescriptor):
- """Keypair Support."""
-
- name = "Keypairs"
- alias = "os-keypairs"
- namespace = "http://docs.openstack.org/compute/ext/keypairs/api/v1.1"
- updated = "2011-08-08T00:00:00Z"
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension(
- 'os-keypairs',
- KeypairController())
- resources.append(res)
- return resources
-
- def get_controller_extensions(self):
- controller = Controller(self.ext_mgr)
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/migrations.py b/nova/api/openstack/compute/legacy_v2/contrib/migrations.py
deleted file mode 100644
index 472dbf6dfa..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/migrations.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# 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 extensions
-from nova import compute
-from nova import context as nova_context
-from nova.objects import base as obj_base
-
-
-XMLNS = "http://docs.openstack.org/compute/ext/migrations/api/v2.0"
-ALIAS = "os-migrations"
-
-
-def authorize(context, action_name):
- action = 'migrations:%s' % action_name
- extensions.extension_authorizer('compute', action)(context)
-
-
-def output(migrations_obj):
- """Returns the desired output of the API from an object.
-
- From a MigrationsList's object this method returns a list of
- primitive objects with the only necessary fields.
- """
- detail_keys = ['memory_total', 'memory_processed', 'memory_remaining',
- 'disk_total', 'disk_processed', 'disk_remaining']
- # Note(Shaohe Feng): We need to leverage the oslo.versionedobjects.
- # Then we can pass the target version to it's obj_to_primitive.
- objects = obj_base.obj_to_primitive(migrations_obj)
- objects = [x for x in objects if not x['hidden']]
- for obj in objects:
- del obj['deleted']
- del obj['deleted_at']
- del obj['migration_type']
- del obj['hidden']
- if 'memory_total' in obj:
- for key in detail_keys:
- del obj[key]
-
- return objects
-
-
-class MigrationsController(object):
- """Controller for accessing migrations in OpenStack API."""
- def __init__(self):
- self.compute_api = compute.API()
-
- def index(self, req):
- """Return all migrations in progress."""
- context = req.environ['nova.context']
- authorize(context, "index")
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks.
- nova_context.require_admin_context(context)
- migrations = self.compute_api.get_migrations(context, req.GET)
- return {'migrations': output(migrations)}
-
-
-class Migrations(extensions.ExtensionDescriptor):
- """Provide data on migrations."""
- name = "Migrations"
- alias = ALIAS
- namespace = XMLNS
- updated = "2013-05-30T00:00:00Z"
-
- def get_resources(self):
- resources = []
- resource = extensions.ResourceExtension('os-migrations',
- MigrationsController())
- resources.append(resource)
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/multinic.py b/nova/api/openstack/compute/legacy_v2/contrib/multinic.py
deleted file mode 100644
index 6ea2fa9075..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/multinic.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-# 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 multinic extension."""
-
-from oslo_log import log as logging
-import webob
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import compute
-from nova import exception
-from nova.i18n import _
-from nova.i18n import _LE
-
-
-LOG = logging.getLogger(__name__)
-authorize = extensions.extension_authorizer('compute', 'multinic')
-
-
-class MultinicController(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- super(MultinicController, self).__init__(*args, **kwargs)
- self.compute_api = compute.API()
-
- @wsgi.action('addFixedIp')
- def _add_fixed_ip(self, req, id, body):
- """Adds an IP on a given network to an instance."""
- context = req.environ['nova.context']
- authorize(context)
-
- # Validate the input entity
- if 'networkId' not in body['addFixedIp']:
- msg = _("Missing 'networkId' argument for addFixedIp")
- raise exc.HTTPBadRequest(explanation=msg)
-
- instance = common.get_instance(self.compute_api, context, id)
- network_id = body['addFixedIp']['networkId']
- try:
- self.compute_api.add_fixed_ip(context, instance, network_id)
- except exception.NoMoreFixedIps as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- return webob.Response(status_int=202)
-
- @wsgi.action('removeFixedIp')
- def _remove_fixed_ip(self, req, id, body):
- """Removes an IP from an instance."""
- context = req.environ['nova.context']
- authorize(context)
-
- # Validate the input entity
- if 'address' not in body['removeFixedIp']:
- msg = _("Missing 'address' argument for removeFixedIp")
- raise exc.HTTPBadRequest(explanation=msg)
-
- instance = common.get_instance(self.compute_api, context, id)
- address = body['removeFixedIp']['address']
-
- try:
- self.compute_api.remove_fixed_ip(context, instance, address)
- except exception.FixedIpNotFoundForSpecificInstance:
- LOG.exception(_LE("Unable to find address %r"), address,
- instance=instance)
- raise exc.HTTPBadRequest()
-
- return webob.Response(status_int=202)
-
-
-# Note: The class name is as it has to be for this to be loaded as an
-# extension--only first character capitalized.
-class Multinic(extensions.ExtensionDescriptor):
- """Multiple network support."""
-
- name = "Multinic"
- alias = "NMN"
- namespace = "http://docs.openstack.org/compute/ext/multinic/api/v1.1"
- updated = "2011-06-09T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = MultinicController()
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/multiple_create.py b/nova/api/openstack/compute/legacy_v2/contrib/multiple_create.py
deleted file mode 100644
index 1c0b8d7bb5..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/multiple_create.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# 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 extensions
-
-
-class Multiple_create(extensions.ExtensionDescriptor):
- """Allow multiple create in the Create Server v1.1 API."""
-
- name = "MultipleCreate"
- alias = "os-multiple-create"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "multiplecreate/api/v1.1")
- updated = "2012-08-07T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/networks_associate.py b/nova/api/openstack/compute/legacy_v2/contrib/networks_associate.py
deleted file mode 100644
index 506ff1e946..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/networks_associate.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# 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
-from webob import exc
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import context as nova_context
-from nova import exception
-from nova.i18n import _
-from nova import network
-
-authorize = extensions.extension_authorizer('compute', 'networks_associate')
-
-
-class NetworkAssociateActionController(wsgi.Controller):
- """Network Association API Controller."""
-
- def __init__(self, network_api=None):
- self.network_api = network_api or network.API()
-
- @wsgi.action("disassociate_host")
- def _disassociate_host_only(self, req, id, body):
- context = req.environ['nova.context']
- authorize(context)
- # NOTE(shaohe-feng): back-compatible with db layer hard-code
- # admin permission checks. call db API objects.Network.associate
- nova_context.require_admin_context(context)
-
- try:
- self.network_api.associate(context, id, host=None)
- except exception.NetworkNotFound:
- msg = _("Network not found")
- raise exc.HTTPNotFound(explanation=msg)
- except NotImplementedError:
- msg = _('Disassociate host is not implemented by the configured '
- 'Network API')
- raise exc.HTTPNotImplemented(explanation=msg)
- return webob.Response(status_int=202)
-
- @wsgi.action("disassociate_project")
- def _disassociate_project_only(self, req, id, body):
- context = req.environ['nova.context']
- authorize(context)
- # NOTE(shaohe-feng): back-compatible with db layer hard-code
- # admin permission checks. call db API objects.Network.associate
- nova_context.require_admin_context(context)
-
- try:
- self.network_api.associate(context, id, project=None)
- except exception.NetworkNotFound:
- msg = _("Network not found")
- raise exc.HTTPNotFound(explanation=msg)
- except NotImplementedError:
- msg = _('Disassociate project is not implemented by the '
- 'configured Network API')
- raise exc.HTTPNotImplemented(explanation=msg)
-
- return webob.Response(status_int=202)
-
- @wsgi.action("associate_host")
- def _associate_host(self, req, id, body):
- context = req.environ['nova.context']
- authorize(context)
- # NOTE(shaohe-feng): back-compatible with db layer hard-code
- # admin permission checks. call db API objects.Network.associate
- nova_context.require_admin_context(context)
-
- try:
- self.network_api.associate(context, id,
- host=body['associate_host'])
- except exception.NetworkNotFound:
- msg = _("Network not found")
- raise exc.HTTPNotFound(explanation=msg)
- except NotImplementedError:
- msg = _('Associate host is not implemented by the configured '
- 'Network API')
- raise exc.HTTPNotImplemented(explanation=msg)
-
- return webob.Response(status_int=202)
-
-
-class Networks_associate(extensions.ExtensionDescriptor):
- """Network association support."""
-
- name = "NetworkAssociationSupport"
- alias = "os-networks-associate"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "networks_associate/api/v2")
- updated = "2012-11-19T00:00:00Z"
-
- def get_controller_extensions(self):
- extension = extensions.ControllerExtension(
- self, 'os-networks', NetworkAssociateActionController())
-
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/os_networks.py b/nova/api/openstack/compute/legacy_v2/contrib/os_networks.py
deleted file mode 100644
index 5cc29054d6..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/os_networks.py
+++ /dev/null
@@ -1,231 +0,0 @@
-# Copyright 2011 Grid Dynamics
-# Copyright 2011 OpenStack Foundation
-# 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.
-
-import netaddr
-import webob
-from webob import exc
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import context as nova_context
-from nova import exception
-from nova.i18n import _
-from nova import network
-from nova.objects import base as base_obj
-from nova.objects import fields as obj_fields
-
-authorize = extensions.extension_authorizer('compute', 'networks')
-authorize_view = extensions.extension_authorizer('compute',
- 'networks:view')
-extended_fields = ('mtu', 'dhcp_server', 'enable_dhcp', 'share_address')
-
-
-def network_dict(context, network, extended):
- fields = ('id', 'cidr', 'netmask', 'gateway', 'broadcast', 'dns1', 'dns2',
- 'cidr_v6', 'gateway_v6', 'label', 'netmask_v6')
- admin_fields = ('created_at', 'updated_at', 'deleted_at', 'deleted',
- 'injected', 'bridge', 'vlan', 'vpn_public_address',
- 'vpn_public_port', 'vpn_private_address', 'dhcp_start',
- 'project_id', 'host', 'bridge_interface', 'multi_host',
- 'priority', 'rxtx_base')
- if network:
- # NOTE(mnaser): We display a limited set of fields so users can know
- # what networks are available, extra system-only fields
- # are only visible if they are an admin.
- if context.is_admin:
- fields += admin_fields
- if extended:
- fields += extended_fields
- # TODO(mriedem): Remove the NovaObject type check once the
- # network.create API is returning objects.
- is_obj = isinstance(network, base_obj.NovaObject)
- result = {}
- for field in fields:
- # NOTE(mriedem): If network is an object, IPAddress fields need to
- # be cast to a string so they look the same in the response as
- # before the objects conversion.
- if is_obj and isinstance(network.fields[field].AUTO_TYPE,
- obj_fields.IPAddress):
- # NOTE(danms): Here, network should be an object, which could
- # have come from neutron and thus be missing most of the
- # attributes. Providing a default to get() avoids trying to
- # lazy-load missing attributes.
- val = network.get(field, None)
- if val is not None:
- result[field] = str(val)
- else:
- result[field] = val
- else:
- # It's either not an object or it's not an IPAddress field.
- result[field] = network.get(field, None)
- uuid = network.get('uuid')
- if uuid:
- result['id'] = uuid
- return result
- else:
- return {}
-
-
-class NetworkController(wsgi.Controller):
-
- def __init__(self, network_api=None, ext_mgr=None):
- self.network_api = network_api or network.API()
- if ext_mgr:
- self.extended = ext_mgr.is_loaded('os-extended-networks')
- else:
- self.extended = False
-
- def index(self, req):
- context = req.environ['nova.context']
- authorize_view(context)
- networks = self.network_api.get_all(context)
- result = [network_dict(context, net_ref, self.extended)
- for net_ref in networks]
- return {'networks': result}
-
- @wsgi.action("disassociate")
- def _disassociate_host_and_project(self, req, id, body):
- context = req.environ['nova.context']
- authorize(context)
- # NOTE(shaohe-feng): back-compatible with db layer hard-code
- # admin permission checks. call db API objects.Network.associate
- nova_context.require_admin_context(context)
-
- try:
- self.network_api.associate(context, id, host=None, project=None)
- except exception.NetworkNotFound:
- msg = _("Network not found")
- raise exc.HTTPNotFound(explanation=msg)
- except NotImplementedError:
- msg = _('Disassociate network is not implemented by the '
- 'configured Network API')
- raise exc.HTTPNotImplemented(explanation=msg)
- return webob.Response(status_int=202)
-
- def show(self, req, id):
- context = req.environ['nova.context']
- authorize_view(context)
-
- try:
- network = self.network_api.get(context, id)
- except exception.NetworkNotFound:
- msg = _("Network not found")
- raise exc.HTTPNotFound(explanation=msg)
- return {'network': network_dict(context, network, self.extended)}
-
- def delete(self, req, id):
- context = req.environ['nova.context']
- authorize(context)
- try:
- self.network_api.delete(context, id)
- except exception.NetworkInUse as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.NetworkNotFound:
- msg = _("Network not found")
- raise exc.HTTPNotFound(explanation=msg)
- return webob.Response(status_int=202)
-
- def create(self, req, body):
- context = req.environ['nova.context']
- authorize(context)
- # NOTE(shaohe-feng): back-compatible with db layer hard-code
- # admin permission checks. call db API objects.Network.create
- nova_context.require_admin_context(context)
-
- def bad(e):
- return exc.HTTPBadRequest(explanation=e)
-
- if not (body and body.get("network")):
- raise bad(_("Missing network in body"))
-
- params = body["network"]
- if not params.get("label"):
- raise bad(_("Network label is required"))
-
- cidr = params.get("cidr") or params.get("cidr_v6")
- if not cidr:
- raise bad(_("Network cidr or cidr_v6 is required"))
-
- if params.get("project_id") == "":
- params["project_id"] = None
-
- params["num_networks"] = 1
- try:
- params["network_size"] = netaddr.IPNetwork(cidr).size
- except netaddr.AddrFormatError:
- msg = _('%s is not a valid IP network') % cidr
- raise exc.HTTPBadRequest(explanation=msg)
-
- if not self.extended:
- create_params = ('allowed_start', 'allowed_end')
- for field in extended_fields + create_params:
- if field in params:
- del params[field]
-
- try:
- network = self.network_api.create(context, **params)[0]
- except (exception.InvalidCidr,
- exception.InvalidIntValue,
- exception.InvalidAddress,
- exception.NetworkNotCreated) as ex:
- raise exc.HTTPBadRequest(explanation=ex.format_message)
- except exception.CidrConflict as ex:
- raise exc.HTTPConflict(explanation=ex.format_message())
- return {"network": network_dict(context, network, self.extended)}
-
- def add(self, req, body):
- context = req.environ['nova.context']
- authorize(context)
- # NOTE(shaohe-feng): back-compatible with db layer hard-code
- # admin permission checks. call db API objects.Network.associate
- nova_context.require_admin_context(context)
- if not body:
- raise exc.HTTPUnprocessableEntity()
-
- network_id = body.get('id', None)
- project_id = context.project_id
-
- try:
- self.network_api.add_network_to_project(
- context, project_id, network_id)
- except NotImplementedError:
- msg = (_("VLAN support must be enabled"))
- raise exc.HTTPNotImplemented(explanation=msg)
- except (exception.NoMoreNetworks,
- exception.NetworkNotFoundForUUID) as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- return webob.Response(status_int=202)
-
-
-class Os_networks(extensions.ExtensionDescriptor):
- """Admin-only Network Management Extension."""
-
- name = "Networks"
- alias = "os-networks"
- namespace = ("http://docs.openstack.org/compute/"
- "ext/os-networks/api/v1.1")
- updated = "2011-12-23T00:00:00Z"
-
- def get_resources(self):
- member_actions = {'action': 'POST'}
- collection_actions = {'add': 'POST'}
- res = extensions.ResourceExtension(
- 'os-networks',
- NetworkController(ext_mgr=self.ext_mgr),
- member_actions=member_actions,
- collection_actions=collection_actions)
- return [res]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/os_tenant_networks.py b/nova/api/openstack/compute/legacy_v2/contrib/os_tenant_networks.py
deleted file mode 100644
index 96699a9d97..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/os_tenant_networks.py
+++ /dev/null
@@ -1,214 +0,0 @@
-# Copyright 2013 OpenStack Foundation
-# 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.
-
-import netaddr
-import netaddr.core as netexc
-from oslo_log import log as logging
-import six
-import webob
-from webob import exc
-
-from nova.api.openstack import extensions
-import nova.conf
-from nova import context as nova_context
-from nova import exception
-from nova.i18n import _
-from nova.i18n import _LE
-import nova.network
-from nova import quota
-
-
-CONF = nova.conf.CONF
-
-QUOTAS = quota.QUOTAS
-LOG = logging.getLogger(__name__)
-authorize = extensions.extension_authorizer('compute', 'os-tenant-networks')
-
-
-def network_dict(network):
- # NOTE(danms): Here, network should be an object, which could have come
- # from neutron and thus be missing most of the attributes. Providing a
- # default to get() avoids trying to lazy-load missing attributes.
- return {"id": network.get("uuid", None) or network.get("id", None),
- "cidr": str(network.get("cidr", None)),
- "label": network.get("label", None)}
-
-
-class NetworkController(object):
- def __init__(self, network_api=None):
- self.network_api = nova.network.API()
- self._default_networks = []
-
- def _refresh_default_networks(self):
- self._default_networks = []
- if CONF.use_neutron_default_nets:
- try:
- self._default_networks = self._get_default_networks()
- except Exception:
- LOG.exception(_LE("Failed to get default networks"))
-
- def _get_default_networks(self):
- project_id = CONF.neutron_default_tenant_id
- ctx = nova_context.RequestContext(user_id=None,
- project_id=project_id)
- networks = {}
- for n in self.network_api.get_all(ctx):
- networks[n['id']] = n['label']
- return [{'id': k, 'label': v} for k, v in six.iteritems(networks)]
-
- def index(self, req):
- context = req.environ['nova.context']
- authorize(context)
- networks = list(self.network_api.get_all(context))
- if not self._default_networks:
- self._refresh_default_networks()
- networks.extend(self._default_networks)
- return {'networks': [network_dict(n) for n in networks]}
-
- def show(self, req, id):
- context = req.environ['nova.context']
- authorize(context)
-
- try:
- network = self.network_api.get(context, id)
- except exception.NetworkNotFound:
- msg = _("Network not found")
- raise exc.HTTPNotFound(explanation=msg)
- return {'network': network_dict(network)}
-
- def delete(self, req, id):
- context = req.environ['nova.context']
- authorize(context)
- reservation = None
- try:
- if CONF.enable_network_quota:
- reservation = QUOTAS.reserve(context, networks=-1)
- except Exception:
- reservation = None
- LOG.exception(_LE("Failed to update usages deallocating "
- "network."))
-
- def _rollback_quota(reservation):
- if CONF.enable_network_quota and reservation:
- QUOTAS.rollback(context, reservation)
-
- try:
- self.network_api.disassociate(context, id)
- self.network_api.delete(context, id)
- except exception.PolicyNotAuthorized as e:
- _rollback_quota(reservation)
- raise exc.HTTPForbidden(explanation=six.text_type(e))
- except exception.NetworkInUse as e:
- _rollback_quota(reservation)
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.NetworkNotFound:
- _rollback_quota(reservation)
- msg = _("Network not found")
- raise exc.HTTPNotFound(explanation=msg)
-
- if CONF.enable_network_quota and reservation:
- QUOTAS.commit(context, reservation)
- response = webob.Response(status_int=202)
-
- return response
-
- def create(self, req, body):
- if not body:
- raise exc.HTTPUnprocessableEntity()
-
- context = req.environ["nova.context"]
- authorize(context)
-
- network = body["network"]
- keys = ["cidr", "cidr_v6", "ipam", "vlan_start", "network_size",
- "num_networks"]
- kwargs = {k: network.get(k) for k in keys}
-
- if not network.get("label"):
- msg = _("Network label is required")
- raise exc.HTTPBadRequest(explanation=msg)
- label = network["label"]
-
- if not (kwargs["cidr"] or kwargs["cidr_v6"]):
- msg = _("No CIDR requested")
- raise exc.HTTPBadRequest(explanation=msg)
- if kwargs["cidr"]:
- try:
- net = netaddr.IPNetwork(kwargs["cidr"])
- if net.size < 4:
- msg = _("Requested network does not contain "
- "enough (2+) usable hosts")
- raise exc.HTTPBadRequest(explanation=msg)
- except netexc.AddrFormatError:
- msg = _("CIDR is malformed.")
- raise exc.HTTPBadRequest(explanation=msg)
- except netexc.AddrConversionError:
- msg = _("Address could not be converted.")
- raise exc.HTTPBadRequest(explanation=msg)
-
- networks = []
- try:
- if CONF.enable_network_quota:
- reservation = QUOTAS.reserve(context, networks=1)
- except exception.OverQuota:
- msg = _("Quota exceeded, too many networks.")
- raise exc.HTTPBadRequest(explanation=msg)
-
- kwargs['project_id'] = context.project_id
-
- try:
- networks = self.network_api.create(context,
- label=label, **kwargs)
- if CONF.enable_network_quota:
- QUOTAS.commit(context, reservation)
- except exception.PolicyNotAuthorized as e:
- raise exc.HTTPForbidden(explanation=six.text_type(e))
- except exception.CidrConflict as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except Exception:
- if CONF.enable_network_quota:
- QUOTAS.rollback(context, reservation)
- msg = _("Create networks failed")
- LOG.exception(msg, extra=network)
- raise exc.HTTPServiceUnavailable(explanation=msg)
- return {"network": network_dict(networks[0])}
-
-
-class Os_tenant_networks(extensions.ExtensionDescriptor):
- """Tenant-based Network Management Extension."""
-
- name = "OSTenantNetworks"
- alias = "os-tenant-networks"
- namespace = ("http://docs.openstack.org/compute/"
- "ext/os-tenant-networks/api/v2")
- updated = "2012-03-07T14:46:43Z"
-
- def get_resources(self):
- ext = extensions.ResourceExtension('os-tenant-networks',
- NetworkController())
- return [ext]
-
-
-def _sync_networks(context, project_id, session):
- ctx = nova_context.RequestContext(user_id=None, project_id=project_id)
- ctx = ctx.elevated()
- networks = nova.network.api.API().get_all(ctx)
- return dict(networks=len(networks))
-
-
-if CONF.enable_network_quota:
- QUOTAS.register_resource(quota.ReservableResource('networks',
- _sync_networks,
- 'quota_networks'))
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/preserve_ephemeral_rebuild.py b/nova/api/openstack/compute/legacy_v2/contrib/preserve_ephemeral_rebuild.py
deleted file mode 100644
index bc75d687bf..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/preserve_ephemeral_rebuild.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# 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 extensions
-
-
-class Preserve_ephemeral_rebuild(extensions.ExtensionDescriptor):
- """Allow preservation of the ephemeral partition on rebuild."""
-
- name = "PreserveEphemeralOnRebuild"
- alias = "os-preserve-ephemeral-rebuild"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "preserve_ephemeral_rebuild/api/v2")
- updated = "2013-12-17T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/quota_classes.py b/nova/api/openstack/compute/legacy_v2/contrib/quota_classes.py
deleted file mode 100644
index 0df81ef9f1..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/quota_classes.py
+++ /dev/null
@@ -1,139 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# 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.
-
-import webob
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-import nova.context
-from nova import db
-from nova import exception
-from nova.i18n import _
-from nova import quota
-from nova import utils
-
-
-QUOTAS = quota.QUOTAS
-# Quotas that are only enabled by specific extensions
-EXTENDED_QUOTAS = {'server_groups': 'os-server-group-quotas',
- 'server_group_members': 'os-server-group-quotas'}
-
-
-authorize = extensions.extension_authorizer('compute', 'quota_classes')
-
-
-class QuotaClassSetsController(wsgi.Controller):
-
- supported_quotas = []
-
- def __init__(self, ext_mgr):
- self.ext_mgr = ext_mgr
- self.supported_quotas = QUOTAS.resources
- for resource, extension in EXTENDED_QUOTAS.items():
- if not self.ext_mgr.is_loaded(extension):
- self.supported_quotas.remove(resource)
-
- def _format_quota_set(self, quota_class, quota_set):
- """Convert the quota object to a result dict."""
-
- if quota_class:
- result = dict(id=str(quota_class))
- else:
- result = {}
-
- for resource in self.supported_quotas:
- if resource in quota_set:
- result[resource] = quota_set[resource]
-
- return dict(quota_class_set=result)
-
- def show(self, req, id):
- context = req.environ['nova.context']
- authorize(context)
- try:
- nova.context.authorize_quota_class_context(context, id)
- values = QUOTAS.get_class_quotas(context, id)
- return self._format_quota_set(id, values)
- except exception.Forbidden:
- raise webob.exc.HTTPForbidden()
-
- def update(self, req, id, body):
- context = req.environ['nova.context']
- authorize(context)
- try:
- utils.check_string_length(id, 'quota_class_name',
- min_length=1, max_length=255)
- except exception.InvalidInput as e:
- raise webob.exc.HTTPBadRequest(
- explanation=e.format_message())
-
- quota_class = id
- bad_keys = []
-
- if not self.is_valid_body(body, 'quota_class_set'):
- msg = _("quota_class_set not specified")
- raise webob.exc.HTTPBadRequest(explanation=msg)
- quota_class_set = body['quota_class_set']
- for key in quota_class_set.keys():
- if key not in self.supported_quotas:
- bad_keys.append(key)
- continue
- try:
- body['quota_class_set'][key] = utils.validate_integer(
- body['quota_class_set'][key], key, max_value=db.MAX_INT)
- except exception.InvalidInput as e:
- raise webob.exc.HTTPBadRequest(
- explanation=e.format_message())
-
- if bad_keys:
- msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- try:
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks. This has to be left only for API v2.0 because
- # this version has to be stable even if it means that only admins
- # can call this method while the policy could be changed.
- nova.context.require_admin_context(context)
- except exception.AdminRequired:
- raise webob.exc.HTTPForbidden()
-
- for key, value in quota_class_set.items():
- try:
- db.quota_class_update(context, quota_class, key, value)
- except exception.QuotaClassNotFound:
- db.quota_class_create(context, quota_class, key, value)
-
- values = QUOTAS.get_class_quotas(context, quota_class)
- return self._format_quota_set(None, values)
-
-
-class Quota_classes(extensions.ExtensionDescriptor):
- """Quota classes management support."""
-
- name = "QuotaClasses"
- alias = "os-quota-class-sets"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "quota-classes-sets/api/v1.1")
- updated = "2012-03-12T00:00:00Z"
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension('os-quota-class-sets',
- QuotaClassSetsController(self.ext_mgr))
- resources.append(res)
-
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/quotas.py b/nova/api/openstack/compute/legacy_v2/contrib/quotas.py
deleted file mode 100644
index 5672258854..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/quotas.py
+++ /dev/null
@@ -1,272 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-# 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 oslo_utils import strutils
-import six.moves.urllib.parse as urlparse
-import webob
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-import nova.context
-from nova import db
-from nova import exception
-from nova.i18n import _
-from nova import objects
-from nova import quota
-from nova import utils
-
-
-QUOTAS = quota.QUOTAS
-NON_QUOTA_KEYS = ['tenant_id', 'id', 'force']
-
-# Quotas that are only enabled by specific extensions
-EXTENDED_QUOTAS = {'server_groups': 'os-server-group-quotas',
- 'server_group_members': 'os-server-group-quotas'}
-
-authorize_update = extensions.extension_authorizer('compute', 'quotas:update')
-authorize_show = extensions.extension_authorizer('compute', 'quotas:show')
-authorize_delete = extensions.extension_authorizer('compute', 'quotas:delete')
-
-
-class QuotaSetsController(wsgi.Controller):
-
- supported_quotas = []
-
- def __init__(self, ext_mgr):
- self.ext_mgr = ext_mgr
- self.supported_quotas = QUOTAS.resources
- for resource, extension in EXTENDED_QUOTAS.items():
- if not self.ext_mgr.is_loaded(extension):
- self.supported_quotas.remove(resource)
-
- def _format_quota_set(self, project_id, quota_set):
- """Convert the quota object to a result dict."""
-
- if project_id:
- result = dict(id=str(project_id))
- else:
- result = {}
-
- for resource in self.supported_quotas:
- if resource in quota_set:
- result[resource] = quota_set[resource]
-
- return dict(quota_set=result)
-
- def _validate_quota_limit(self, resource, limit, minimum, maximum):
- # NOTE: -1 is a flag value for unlimited, maximum value is limited
- # by SQL standard integer type `INT` which is `0x7FFFFFFF`, it's a
- # general value for SQL, using a hardcoded value here is not a
- # `nice` way, but it seems like the only way for now:
- # http://dev.mysql.com/doc/refman/5.0/en/integer-types.html
- # http://www.postgresql.org/docs/9.1/static/datatype-numeric.html
- if limit < -1 or limit > db.MAX_INT:
- msg = (_("Quota limit %(limit)s for %(resource)s "
- "must be in the range of -1 and %(max)s.") %
- {'limit': limit, 'resource': resource, 'max': db.MAX_INT})
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- def conv_inf(value):
- return float("inf") if value == -1 else value
-
- if conv_inf(limit) < conv_inf(minimum):
- msg = (_("Quota limit %(limit)s for %(resource)s must "
- "be greater than or equal to already used and "
- "reserved %(minimum)s.") %
- {'limit': limit, 'resource': resource, 'minimum': minimum})
- raise webob.exc.HTTPBadRequest(explanation=msg)
- if conv_inf(limit) > conv_inf(maximum):
- msg = (_("Quota limit %(limit)s for %(resource)s must be "
- "less than or equal to %(maximum)s.") %
- {'limit': limit, 'resource': resource, 'maximum': maximum})
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- def _get_quotas(self, context, id, user_id=None, usages=False):
- if user_id:
- values = QUOTAS.get_user_quotas(context, id, user_id,
- usages=usages)
- else:
- values = QUOTAS.get_project_quotas(context, id, usages=usages)
-
- if usages:
- return values
- else:
- return {k: v['limit'] for k, v in values.items()}
-
- def show(self, req, id):
- context = req.environ['nova.context']
- authorize_show(context)
- params = urlparse.parse_qs(req.environ.get('QUERY_STRING', ''))
- user_id = None
- if self.ext_mgr.is_loaded('os-user-quotas'):
- user_id = params.get('user_id', [None])[0]
- try:
- nova.context.authorize_project_context(context, id)
- return self._format_quota_set(id,
- self._get_quotas(context, id, user_id=user_id))
- except exception.Forbidden:
- raise webob.exc.HTTPForbidden()
-
- def update(self, req, id, body):
- context = req.environ['nova.context']
- authorize_update(context)
- try:
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks. This has to be left only for API v2.0 because
- # this version has to be stable even if it means that only admins
- # can call this method while the policy could be changed.
- nova.context.require_admin_context(context)
- except exception.AdminRequired:
- raise webob.exc.HTTPForbidden()
-
- project_id = id
-
- bad_keys = []
-
- # By default, we can force update the quota if the extended
- # is not loaded
- force_update = True
- extended_loaded = False
- if self.ext_mgr.is_loaded('os-extended-quotas'):
- # force optional has been enabled, the default value of
- # force_update need to be changed to False
- extended_loaded = True
- force_update = False
-
- user_id = None
- if self.ext_mgr.is_loaded('os-user-quotas'):
- # Update user quotas only if the extended is loaded
- params = urlparse.parse_qs(req.environ.get('QUERY_STRING', ''))
- user_id = params.get('user_id', [None])[0]
-
- try:
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks.
- nova.context.authorize_project_context(context, id)
- settable_quotas = QUOTAS.get_settable_quotas(context, project_id,
- user_id=user_id)
- except exception.Forbidden:
- raise webob.exc.HTTPForbidden()
-
- if not self.is_valid_body(body, 'quota_set'):
- msg = _("quota_set not specified")
- raise webob.exc.HTTPBadRequest(explanation=msg)
- quota_set = body['quota_set']
-
- # NOTE(dims): Pass #1 - In this loop for quota_set.items(), we figure
- # out if we have bad keys or if we need to forcibly set quotas or
- # if some of the values for the quotas can be converted to integers.
- for key, value in quota_set.items():
- if (key not in self.supported_quotas
- and key not in NON_QUOTA_KEYS):
- bad_keys.append(key)
- continue
- if key == 'force' and extended_loaded:
- # only check the force optional when the extended has
- # been loaded
- force_update = strutils.bool_from_string(value)
- elif key not in NON_QUOTA_KEYS and value:
- try:
- utils.validate_integer(value, key)
- except exception.InvalidInput as e:
- raise webob.exc.HTTPBadRequest(
- explanation=e.format_message())
-
- if bad_keys:
- msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- # NOTE(dims): Pass #2 - In this loop for quota_set.items(), based on
- # force_update flag we validate the quota limit. A loop just for
- # the validation of min/max values ensure that we can bail out if
- # any of the items in the set is bad.
- valid_quotas = {}
- for key, value in quota_set.items():
- if key in NON_QUOTA_KEYS or (not value and value != 0):
- continue
- # validate whether already used and reserved exceeds the new
- # quota, this check will be ignored if admin want to force
- # update
- value = int(value)
- if not force_update:
- minimum = settable_quotas[key]['minimum']
- maximum = settable_quotas[key]['maximum']
- self._validate_quota_limit(key, value, minimum, maximum)
- valid_quotas[key] = value
-
- # NOTE(dims): Pass #3 - At this point we know that all the keys and
- # values are valid and we can iterate and update them all in one
- # shot without having to worry about rolling back etc as we have done
- # the validation up front in the 2 loops above.
- for key, value in valid_quotas.items():
- try:
- objects.Quotas.create_limit(context, project_id,
- key, value, user_id=user_id)
- except exception.QuotaExists:
- objects.Quotas.update_limit(context, project_id,
- key, value, user_id=user_id)
- values = self._get_quotas(context, id, user_id=user_id)
- return self._format_quota_set(None, values)
-
- def defaults(self, req, id):
- context = req.environ['nova.context']
- authorize_show(context)
- values = QUOTAS.get_defaults(context)
- return self._format_quota_set(id, values)
-
- def delete(self, req, id):
- if self.ext_mgr.is_loaded('os-extended-quotas'):
- context = req.environ['nova.context']
- authorize_delete(context)
- params = urlparse.parse_qs(req.environ.get('QUERY_STRING', ''))
- user_id = params.get('user_id', [None])[0]
- if user_id and not self.ext_mgr.is_loaded('os-user-quotas'):
- raise webob.exc.HTTPNotFound()
- try:
- nova.context.authorize_project_context(context, id)
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks. This has to be left only for API v2.0
- # because this version has to be stable even if it means that
- # only admins can call this method while the policy could be
- # changed.
- nova.context.require_admin_context(context)
- if user_id:
- QUOTAS.destroy_all_by_project_and_user(context,
- id, user_id)
- else:
- QUOTAS.destroy_all_by_project(context, id)
- return webob.Response(status_int=202)
- except exception.Forbidden:
- raise webob.exc.HTTPForbidden()
- raise webob.exc.HTTPNotFound()
-
-
-class Quotas(extensions.ExtensionDescriptor):
- """Quotas management support."""
-
- name = "Quotas"
- alias = "os-quota-sets"
- namespace = "http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1"
- updated = "2011-08-08T00:00:00Z"
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension('os-quota-sets',
- QuotaSetsController(self.ext_mgr),
- member_actions={'defaults': 'GET'})
- resources.append(res)
-
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/rescue.py b/nova/api/openstack/compute/legacy_v2/contrib/rescue.py
deleted file mode 100644
index 673e941729..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/rescue.py
+++ /dev/null
@@ -1,99 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-#
-# 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 rescue mode extension."""
-
-import webob
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions as exts
-from nova.api.openstack import wsgi
-from nova import compute
-from nova import exception
-from nova import utils
-
-
-authorize = exts.extension_authorizer('compute', 'rescue')
-
-
-class RescueController(wsgi.Controller):
- def __init__(self, ext_mgr, *args, **kwargs):
- super(RescueController, self).__init__(*args, **kwargs)
- self.compute_api = compute.API()
- self.ext_mgr = ext_mgr
-
- @wsgi.action('rescue')
- def _rescue(self, req, id, body):
- """Rescue an instance."""
- context = req.environ["nova.context"]
- authorize(context)
-
- if body['rescue'] and 'adminPass' in body['rescue']:
- password = body['rescue']['adminPass']
- else:
- password = utils.generate_password()
-
- instance = common.get_instance(self.compute_api, context, id)
- try:
- rescue_image_ref = None
- if self.ext_mgr.is_loaded("os-extended-rescue-with-image"):
- if body['rescue'] and 'rescue_image_ref' in body['rescue']:
- rescue_image_ref = common.image_uuid_from_href(
- body['rescue']['rescue_image_ref'], 'rescue_image_ref')
- self.compute_api.rescue(context, instance,
- rescue_password=password, rescue_image_ref=rescue_image_ref)
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'rescue', id)
- except exception.InvalidVolume as volume_error:
- raise exc.HTTPConflict(explanation=volume_error.format_message())
- except exception.InstanceNotRescuable as non_rescuable:
- raise exc.HTTPBadRequest(
- explanation=non_rescuable.format_message())
-
- return {'adminPass': password}
-
- @wsgi.action('unrescue')
- def _unrescue(self, req, id, body):
- """Unrescue an instance."""
- context = req.environ["nova.context"]
- authorize(context)
- instance = common.get_instance(self.compute_api, context, id)
- try:
- self.compute_api.unrescue(context, instance)
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'unrescue',
- id)
-
- return webob.Response(status_int=202)
-
-
-class Rescue(exts.ExtensionDescriptor):
- """Instance rescue mode."""
-
- name = "Rescue"
- alias = "os-rescue"
- namespace = "http://docs.openstack.org/compute/ext/rescue/api/v1.1"
- updated = "2011-08-18T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = RescueController(self.ext_mgr)
- extension = exts.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/scheduler_hints.py b/nova/api/openstack/compute/legacy_v2/contrib/scheduler_hints.py
deleted file mode 100644
index c1d69413a4..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/scheduler_hints.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-#
-# 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
-from nova.i18n import _
-
-
-class SchedulerHintsController(wsgi.Controller):
-
- @staticmethod
- def _extract_scheduler_hints(body):
- hints = {}
-
- attr = '%s:scheduler_hints' % Scheduler_hints.alias
- try:
- if 'os:scheduler_hints' in body:
- # NOTE(vish): This is for legacy support
- hints.update(body['os:scheduler_hints'])
- elif attr in body:
- hints.update(body[attr])
- # Fail if non-dict provided
- except ValueError:
- msg = _("Malformed scheduler_hints attribute")
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- return hints
-
- @wsgi.extends
- def create(self, req, body):
- hints = self._extract_scheduler_hints(body)
-
- if 'server' in body:
- body['server']['scheduler_hints'] = hints
- yield
-
-
-class Scheduler_hints(extensions.ExtensionDescriptor):
- """Pass arbitrary key/value pairs to the scheduler."""
-
- name = "SchedulerHints"
- alias = "OS-SCH-HNT"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "scheduler-hints/api/v2")
- updated = "2011-07-19T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = SchedulerHintsController()
- ext = extensions.ControllerExtension(self, 'servers', controller)
- return [ext]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/security_group_default_rules.py b/nova/api/openstack/compute/legacy_v2/contrib/security_group_default_rules.py
deleted file mode 100644
index 486ddabb6c..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/security_group_default_rules.py
+++ /dev/null
@@ -1,150 +0,0 @@
-# Copyright 2013 Metacloud Inc.
-#
-# 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 six
-import webob
-from webob import exc
-
-from nova.api.openstack.compute.legacy_v2.contrib import security_groups as sg
-from nova.api.openstack import extensions
-from nova import context as nova_context
-from nova import exception
-from nova.i18n import _
-from nova.network.security_group import openstack_driver
-
-
-authorize = extensions.extension_authorizer('compute',
- 'security_group_default_rules')
-
-
-class SecurityGroupDefaultRulesController(sg.SecurityGroupControllerBase):
-
- def __init__(self):
- self.security_group_api = (
- openstack_driver.get_openstack_security_group_driver())
-
- def create(self, req, body):
- context = sg._authorize_context(req)
- authorize(context)
- # NOTE(shaohe-feng): back-compatible with db layer hard-code
- # admin permission checks.
- nova_context.require_admin_context(context)
-
- sg_rule = self._from_body(body, 'security_group_default_rule')
-
- try:
- values = self._rule_args_to_dict(to_port=sg_rule.get('to_port'),
- from_port=sg_rule.get('from_port'),
- ip_protocol=sg_rule.get('ip_protocol'),
- cidr=sg_rule.get('cidr'))
- except Exception as exp:
- raise exc.HTTPBadRequest(explanation=six.text_type(exp))
-
- if values is None:
- msg = _('Not enough parameters to build a valid rule.')
- raise exc.HTTPBadRequest(explanation=msg)
-
- if self.security_group_api.default_rule_exists(context, values):
- msg = _('This default rule already exists.')
- raise exc.HTTPConflict(explanation=msg)
- security_group_rule = self.security_group_api.add_default_rules(
- context, [values])[0]
- fmt_rule = self._format_security_group_default_rule(
- security_group_rule)
- return {'security_group_default_rule': fmt_rule}
-
- def _rule_args_to_dict(self, to_port=None, from_port=None,
- ip_protocol=None, cidr=None):
- cidr = self.security_group_api.parse_cidr(cidr)
- return self.security_group_api.new_cidr_ingress_rule(
- cidr, ip_protocol, from_port, to_port)
-
- def show(self, req, id):
- context = sg._authorize_context(req)
- authorize(context)
-
- id = self.security_group_api.validate_id(id)
-
- try:
- rule = self.security_group_api.get_default_rule(context, id)
- except exception.SecurityGroupDefaultRuleNotFound:
- msg = _("security group default rule not found")
- raise exc.HTTPNotFound(explanation=msg)
-
- fmt_rule = self._format_security_group_default_rule(rule)
- return {"security_group_default_rule": fmt_rule}
-
- def delete(self, req, id):
- context = sg._authorize_context(req)
- authorize(context)
- # NOTE(shaohe-feng): back-compatible with db layer hard-code
- # admin permission checks.
- nova_context.require_admin_context(context)
-
- try:
- id = self.security_group_api.validate_id(id)
- except exception.Invalid as ex:
- raise exc.HTTPBadRequest(explanation=ex.format_message())
-
- try:
- rule = self.security_group_api.get_default_rule(context, id)
- self.security_group_api.remove_default_rules(context, [rule['id']])
- except exception.SecurityGroupDefaultRuleNotFound as ex:
- raise exc.HTTPNotFound(explanation=ex.format_message())
-
- return webob.Response(status_int=204)
-
- def index(self, req):
-
- context = sg._authorize_context(req)
- authorize(context)
-
- ret = {'security_group_default_rules': []}
- try:
- for rule in self.security_group_api.get_all_default_rules(context):
- rule_fmt = self._format_security_group_default_rule(rule)
- ret['security_group_default_rules'].append(rule_fmt)
- except exception.SecurityGroupDefaultRuleNotFound as ex:
- raise exc.HTTPNotFound(explanation=ex.format_message())
- return ret
-
- def _format_security_group_default_rule(self, rule):
- sg_rule = {}
- sg_rule['id'] = rule['id']
- sg_rule['ip_protocol'] = rule['protocol']
- sg_rule['from_port'] = rule['from_port']
- sg_rule['to_port'] = rule['to_port']
- sg_rule['ip_range'] = {}
- sg_rule['ip_range'] = {'cidr': rule['cidr']}
- return sg_rule
-
-
-class Security_group_default_rules(extensions.ExtensionDescriptor):
- """Default rules for security group support."""
- name = "SecurityGroupDefaultRules"
- alias = "os-security-group-default-rules"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "securitygroupdefaultrules/api/v1.1")
- updated = "2013-02-05T00:00:00Z"
-
- def get_resources(self):
- resources = [
- extensions.ResourceExtension('os-security-group-default-rules',
- SecurityGroupDefaultRulesController(),
- collection_actions={'create': 'POST',
- 'delete': 'DELETE',
- 'index': 'GET'},
- member_actions={'show': 'GET'})]
-
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/security_groups.py b/nova/api/openstack/compute/legacy_v2/contrib/security_groups.py
deleted file mode 100644
index 1c458e127c..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/security_groups.py
+++ /dev/null
@@ -1,506 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-# Copyright 2012 Justin Santa Barbara
-# 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 security groups extension."""
-
-import contextlib
-from xml.dom import minidom
-
-from oslo_log import log as logging
-from oslo_serialization import jsonutils
-import six
-import webob
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import compute
-from nova import exception
-from nova.i18n import _
-from nova.network.security_group import openstack_driver
-from nova.virt import netutils
-
-
-LOG = logging.getLogger(__name__)
-authorize = extensions.extension_authorizer('compute', 'security_groups')
-softauth = extensions.soft_extension_authorizer('compute', 'security_groups')
-
-
-def _authorize_context(req):
- context = req.environ['nova.context']
- authorize(context)
- return context
-
-
-@contextlib.contextmanager
-def translate_exceptions():
- """Translate nova exceptions to http exceptions."""
- try:
- yield
- except exception.Invalid as exp:
- msg = exp.format_message()
- raise exc.HTTPBadRequest(explanation=msg)
- except exception.SecurityGroupNotFound as exp:
- msg = exp.format_message()
- raise exc.HTTPNotFound(explanation=msg)
- except exception.InstanceNotFound as exp:
- msg = exp.format_message()
- raise exc.HTTPNotFound(explanation=msg)
- except exception.SecurityGroupLimitExceeded as exp:
- msg = exp.format_message()
- raise exc.HTTPForbidden(explanation=msg)
- except exception.NoUniqueMatch as exp:
- msg = exp.format_message()
- raise exc.HTTPConflict(explanation=msg)
-
-
-class SecurityGroupControllerBase(object):
- """Base class for Security Group controllers."""
-
- def __init__(self):
- self.security_group_api = (
- openstack_driver.get_openstack_security_group_driver())
- self.compute_api = compute.API(
- security_group_api=self.security_group_api)
-
- def _format_security_group_rule(self, context, rule, group_rule_data=None):
- """Return a security group rule in desired API response format.
-
- If group_rule_data is passed in that is used rather than querying
- for it.
- """
- sg_rule = {}
- sg_rule['id'] = rule['id']
- sg_rule['parent_group_id'] = rule['parent_group_id']
- sg_rule['ip_protocol'] = rule['protocol']
- sg_rule['from_port'] = rule['from_port']
- sg_rule['to_port'] = rule['to_port']
- sg_rule['group'] = {}
- sg_rule['ip_range'] = {}
- if rule['group_id']:
- with translate_exceptions():
- try:
- source_group = self.security_group_api.get(
- context, id=rule['group_id'])
- except exception.SecurityGroupNotFound:
- # NOTE(arosen): There is a possible race condition that can
- # occur here if two api calls occur concurrently: one that
- # lists the security groups and another one that deletes a
- # security group rule that has a group_id before the
- # group_id is fetched. To handle this if
- # SecurityGroupNotFound is raised we return None instead
- # of the rule and the caller should ignore the rule.
- LOG.debug("Security Group ID %s does not exist",
- rule['group_id'])
- return
- sg_rule['group'] = {'name': source_group.get('name'),
- 'tenant_id': source_group.get('project_id')}
- elif group_rule_data:
- sg_rule['group'] = group_rule_data
- else:
- sg_rule['ip_range'] = {'cidr': rule['cidr']}
- return sg_rule
-
- def _format_security_group(self, context, group):
- security_group = {}
- security_group['id'] = group['id']
- security_group['description'] = group['description']
- security_group['name'] = group['name']
- security_group['tenant_id'] = group['project_id']
- security_group['rules'] = []
- for rule in group['rules']:
- formatted_rule = self._format_security_group_rule(context, rule)
- if formatted_rule:
- security_group['rules'] += [formatted_rule]
- return security_group
-
- def _from_body(self, body, key):
- if not body:
- raise exc.HTTPBadRequest(
- explanation=_("The request body can't be empty"))
- value = body.get(key, None)
- if value is None:
- raise exc.HTTPBadRequest(
- explanation=_("Missing parameter %s") % key)
- return value
-
-
-class SecurityGroupController(SecurityGroupControllerBase):
- """The Security group API controller for the OpenStack API."""
-
- def show(self, req, id):
- """Return data about the given security group."""
- context = _authorize_context(req)
-
- with translate_exceptions():
- id = self.security_group_api.validate_id(id)
- security_group = self.security_group_api.get(context, None, id,
- map_exception=True)
-
- return {'security_group': self._format_security_group(context,
- security_group)}
-
- def delete(self, req, id):
- """Delete a security group."""
- context = _authorize_context(req)
-
- with translate_exceptions():
- id = self.security_group_api.validate_id(id)
- security_group = self.security_group_api.get(context, None, id,
- map_exception=True)
- self.security_group_api.destroy(context, security_group)
-
- return webob.Response(status_int=202)
-
- def index(self, req):
- """Returns a list of security groups."""
- context = _authorize_context(req)
-
- search_opts = {}
- search_opts.update(req.GET)
-
- with translate_exceptions():
- project_id = context.project_id
- raw_groups = self.security_group_api.list(context,
- project=project_id,
- search_opts=search_opts)
-
- limited_list = common.limited(raw_groups, req)
- result = [self._format_security_group(context, group)
- for group in limited_list]
-
- return {'security_groups':
- list(sorted(result,
- key=lambda k: (k['tenant_id'], k['name'])))}
-
- def create(self, req, body):
- """Creates a new security group."""
- context = _authorize_context(req)
-
- security_group = self._from_body(body, 'security_group')
-
- group_name = security_group.get('name', None)
- group_description = security_group.get('description', None)
-
- with translate_exceptions():
- self.security_group_api.validate_property(group_name, 'name', None)
- self.security_group_api.validate_property(group_description,
- 'description', None)
- group_ref = self.security_group_api.create_security_group(
- context, group_name, group_description)
-
- return {'security_group': self._format_security_group(context,
- group_ref)}
-
- def update(self, req, id, body):
- """Update a security group."""
- context = _authorize_context(req)
-
- with translate_exceptions():
- id = self.security_group_api.validate_id(id)
- security_group = self.security_group_api.get(context, None, id,
- map_exception=True)
-
- security_group_data = self._from_body(body, 'security_group')
- group_name = security_group_data.get('name', None)
- group_description = security_group_data.get('description', None)
-
- with translate_exceptions():
- self.security_group_api.validate_property(group_name, 'name', None)
- self.security_group_api.validate_property(group_description,
- 'description', None)
- group_ref = self.security_group_api.update_security_group(
- context, security_group, group_name, group_description)
-
- return {'security_group': self._format_security_group(context,
- group_ref)}
-
-
-class SecurityGroupRulesController(SecurityGroupControllerBase):
-
- def create(self, req, body):
- context = _authorize_context(req)
-
- sg_rule = self._from_body(body, 'security_group_rule')
-
- with translate_exceptions():
- parent_group_id = self.security_group_api.validate_id(
- sg_rule.get('parent_group_id', None))
- security_group = self.security_group_api.get(context, None,
- parent_group_id,
- map_exception=True)
- try:
- new_rule = self._rule_args_to_dict(context,
- to_port=sg_rule.get('to_port'),
- from_port=sg_rule.get('from_port'),
- ip_protocol=sg_rule.get('ip_protocol'),
- cidr=sg_rule.get('cidr'),
- group_id=sg_rule.get('group_id'))
- except exception.SecurityGroupNotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- except Exception as exp:
- raise exc.HTTPBadRequest(explanation=six.text_type(exp))
-
- if new_rule is None:
- msg = _("Not enough parameters to build a valid rule.")
- raise exc.HTTPBadRequest(explanation=msg)
-
- new_rule['parent_group_id'] = security_group['id']
-
- if 'cidr' in new_rule:
- net, prefixlen = netutils.get_net_and_prefixlen(new_rule['cidr'])
- if net not in ('0.0.0.0', '::') and prefixlen == '0':
- msg = _("Bad prefix for network in cidr %s") % new_rule['cidr']
- raise exc.HTTPBadRequest(explanation=msg)
-
- group_rule_data = None
- with translate_exceptions():
- if sg_rule.get('group_id'):
- source_group = self.security_group_api.get(
- context, id=sg_rule['group_id'])
- group_rule_data = {'name': source_group.get('name'),
- 'tenant_id': source_group.get('project_id')}
-
- security_group_rule = (
- self.security_group_api.create_security_group_rule(
- context, security_group, new_rule))
-
- formatted_rule = self._format_security_group_rule(context,
- security_group_rule,
- group_rule_data)
- return {"security_group_rule": formatted_rule}
-
- def _rule_args_to_dict(self, context, to_port=None, from_port=None,
- ip_protocol=None, cidr=None, group_id=None):
-
- if group_id is not None:
- group_id = self.security_group_api.validate_id(group_id)
-
- # check if groupId exists
- self.security_group_api.get(context, id=group_id)
- return self.security_group_api.new_group_ingress_rule(
- group_id, ip_protocol, from_port, to_port)
- else:
- cidr = self.security_group_api.parse_cidr(cidr)
- return self.security_group_api.new_cidr_ingress_rule(
- cidr, ip_protocol, from_port, to_port)
-
- def delete(self, req, id):
- context = _authorize_context(req)
-
- with translate_exceptions():
- id = self.security_group_api.validate_id(id)
- rule = self.security_group_api.get_rule(context, id)
- group_id = rule['parent_group_id']
- security_group = self.security_group_api.get(context, None,
- group_id,
- map_exception=True)
- self.security_group_api.remove_rules(context, security_group,
- [rule['id']])
-
- return webob.Response(status_int=202)
-
-
-class ServerSecurityGroupController(SecurityGroupControllerBase):
-
- def index(self, req, server_id):
- """Returns a list of security groups for the given instance."""
- context = _authorize_context(req)
-
- self.security_group_api.ensure_default(context)
-
- with translate_exceptions():
- instance = common.get_instance(self.compute_api, context,
- server_id)
- groups = self.security_group_api.get_instance_security_groups(
- context, instance, True)
-
- result = [self._format_security_group(context, group)
- for group in groups]
-
- return {'security_groups':
- list(sorted(result,
- key=lambda k: (k['tenant_id'], k['name'])))}
-
-
-class SecurityGroupActionController(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- super(SecurityGroupActionController, self).__init__(*args, **kwargs)
- self.security_group_api = (
- openstack_driver.get_openstack_security_group_driver())
- self.compute_api = compute.API(
- security_group_api=self.security_group_api)
-
- def _parse(self, body, action):
- try:
- body = body[action]
- group_name = body['name']
- except TypeError:
- msg = _("Missing parameter dict")
- raise webob.exc.HTTPBadRequest(explanation=msg)
- except KeyError:
- msg = _("Security group not specified")
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- if not group_name or group_name.strip() == '':
- msg = _("Security group name cannot be empty")
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- return group_name
-
- def _invoke(self, method, context, id, group_name):
- with translate_exceptions():
- instance = common.get_instance(self.compute_api, context, id)
- method(context, instance, group_name)
-
- return webob.Response(status_int=202)
-
- @wsgi.action('addSecurityGroup')
- def _addSecurityGroup(self, req, id, body):
- context = req.environ['nova.context']
- authorize(context)
-
- group_name = self._parse(body, 'addSecurityGroup')
-
- return self._invoke(self.security_group_api.add_to_instance,
- context, id, group_name)
-
- @wsgi.action('removeSecurityGroup')
- def _removeSecurityGroup(self, req, id, body):
- context = req.environ['nova.context']
- authorize(context)
-
- group_name = self._parse(body, 'removeSecurityGroup')
-
- return self._invoke(self.security_group_api.remove_from_instance,
- context, id, group_name)
-
-
-class SecurityGroupsOutputController(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- super(SecurityGroupsOutputController, self).__init__(*args, **kwargs)
- self.compute_api = compute.API()
- self.security_group_api = (
- openstack_driver.get_openstack_security_group_driver())
-
- def _extend_servers(self, req, servers):
- # TODO(arosen) this function should be refactored to reduce duplicate
- # code and use get_instance_security_groups instead of get_db_instance.
- if not len(servers):
- return
- key = "security_groups"
- context = _authorize_context(req)
- if not openstack_driver.is_neutron_security_groups():
- for server in servers:
- instance = req.get_db_instance(server['id'])
- groups = instance.get(key)
- if groups:
- server[key] = [{"name": group["name"]} for group in groups]
- else:
- # If method is a POST we get the security groups intended for an
- # instance from the request. The reason for this is if using
- # neutron security groups the requested security groups for the
- # instance are not in the db and have not been sent to neutron yet.
- if req.method != 'POST':
- sg_instance_bindings = (
- self.security_group_api
- .get_instances_security_groups_bindings(context,
- servers))
- for server in servers:
- groups = sg_instance_bindings.get(server['id'])
- if groups:
- server[key] = groups
-
- # In this section of code len(servers) == 1 as you can only POST
- # one server in an API request.
- else:
- try:
- # try converting to json
- req_obj = jsonutils.loads(req.body)
- # Add security group to server, if no security group was in
- # request add default since that is the group it is part of
- servers[0][key] = req_obj['server'].get(
- key, [{'name': 'default'}])
- except ValueError:
- root = minidom.parseString(req.body)
- sg_root = root.getElementsByTagName(key)
- groups = []
- if sg_root:
- security_groups = sg_root[0].getElementsByTagName(
- 'security_group')
- for security_group in security_groups:
- groups.append(
- {'name': security_group.getAttribute('name')})
- if not groups:
- groups = [{'name': 'default'}]
-
- servers[0][key] = groups
-
- def _show(self, req, resp_obj):
- if not softauth(req.environ['nova.context']):
- return
- if 'server' in resp_obj.obj:
- self._extend_servers(req, [resp_obj.obj['server']])
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- return self._show(req, resp_obj)
-
- @wsgi.extends
- def create(self, req, resp_obj, body):
- return self._show(req, resp_obj)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- if not softauth(req.environ['nova.context']):
- return
- self._extend_servers(req, list(resp_obj.obj['servers']))
-
-
-class Security_groups(extensions.ExtensionDescriptor):
- """Security group support."""
- name = "SecurityGroups"
- alias = "os-security-groups"
- namespace = "http://docs.openstack.org/compute/ext/securitygroups/api/v1.1"
- updated = "2013-05-28T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = SecurityGroupActionController()
- actions = extensions.ControllerExtension(self, 'servers', controller)
- controller = SecurityGroupsOutputController()
- output = extensions.ControllerExtension(self, 'servers', controller)
- return [actions, output]
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension('os-security-groups',
- controller=SecurityGroupController())
-
- resources.append(res)
-
- res = extensions.ResourceExtension('os-security-group-rules',
- controller=SecurityGroupRulesController())
- resources.append(res)
-
- res = extensions.ResourceExtension(
- 'os-security-groups',
- controller=ServerSecurityGroupController(),
- parent=dict(member_name='server', collection_name='servers'))
- resources.append(res)
-
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_diagnostics.py b/nova/api/openstack/compute/legacy_v2/contrib/server_diagnostics.py
deleted file mode 100644
index 3939d22080..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/server_diagnostics.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-# 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.
-
-import webob.exc
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova import compute
-from nova import exception
-from nova.i18n import _
-
-
-authorize = extensions.extension_authorizer('compute', 'server_diagnostics')
-
-
-class ServerDiagnosticsController(object):
- def __init__(self):
- self.compute_api = compute.API()
-
- def index(self, req, server_id):
- context = req.environ["nova.context"]
- authorize(context)
-
- instance = common.get_instance(self.compute_api, context, server_id)
-
- try:
- return self.compute_api.get_diagnostics(context, instance)
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'get_diagnostics', server_id)
- except NotImplementedError:
- msg = _("Unable to get diagnostics, functionality not implemented")
- raise webob.exc.HTTPNotImplemented(explanation=msg)
-
-
-class Server_diagnostics(extensions.ExtensionDescriptor):
- """Allow Admins to view server diagnostics through server action."""
-
- name = "ServerDiagnostics"
- alias = "os-server-diagnostics"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "server-diagnostics/api/v1.1")
- updated = "2011-12-21T00:00:00Z"
-
- def get_resources(self):
- parent_def = {'member_name': 'server', 'collection_name': 'servers'}
- # NOTE(bcwaldon): This should be prefixed with 'os-'
- ext = extensions.ResourceExtension('diagnostics',
- ServerDiagnosticsController(),
- parent=parent_def)
- return [ext]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_external_events.py b/nova/api/openstack/compute/legacy_v2/contrib/server_external_events.py
deleted file mode 100644
index 65ba052ad7..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/server_external_events.py
+++ /dev/null
@@ -1,149 +0,0 @@
-# Copyright 2014 Red Hat, Inc.
-#
-# 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 oslo_log import log as logging
-import webob
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import compute
-from nova import exception
-from nova.i18n import _
-from nova.i18n import _LI
-from nova import objects
-from nova.objects import external_event as external_event_obj
-
-
-LOG = logging.getLogger(__name__)
-authorize = extensions.extension_authorizer('compute',
- 'os-server-external-events')
-
-
-class ServerExternalEventsController(wsgi.Controller):
-
- def __init__(self):
- self.compute_api = compute.API()
- super(ServerExternalEventsController, self).__init__()
-
- def create(self, req, body):
- """Creates a new instance event."""
- context = req.environ['nova.context']
- authorize(context, action='create')
-
- response_events = []
- accepted_events = []
- accepted_instances = set()
- instances = {}
- result = 200
-
- body_events = body.get('events', [])
- if not isinstance(body_events, list) or not len(body_events):
- raise webob.exc.HTTPBadRequest()
-
- for _event in body_events:
- client_event = dict(_event)
- event = objects.InstanceExternalEvent(context)
-
- status = client_event.get('status', 'completed')
- if status not in external_event_obj.EVENT_STATUSES:
- raise webob.exc.HTTPBadRequest(
- _('Invalid event status `%s\'') % status)
-
- if client_event.get('name') not in external_event_obj.EVENT_NAMES:
- raise webob.exc.HTTPBadRequest(
- _('Invalid event name %s') % client_event.get('name'))
-
- try:
- event.instance_uuid = client_event.pop('server_uuid')
- event.name = client_event.pop('name')
- event.status = client_event.pop('status', 'completed')
- event.tag = client_event.pop('tag', None)
- except KeyError as missing_key:
- msg = _('event entity requires key %(key)s') % missing_key
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- if client_event:
- msg = (_('event entity contains unsupported items: %s') %
- ', '.join(client_event.keys()))
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- instance = instances.get(event.instance_uuid)
- if not instance:
- try:
- instance = objects.Instance.get_by_uuid(
- context, event.instance_uuid)
- instances[event.instance_uuid] = instance
- except exception.InstanceNotFound:
- LOG.debug('Dropping event %(name)s:%(tag)s for unknown '
- 'instance %(instance_uuid)s',
- {'name': event.name, 'tag': event.tag,
- 'instance_uuid': event.instance_uuid})
- _event['status'] = 'failed'
- _event['code'] = 404
- result = 207
-
- # NOTE: before accepting the event, make sure the instance
- # for which the event is sent is assigned to a host; otherwise
- # it will not be possible to dispatch the event
- if instance:
- if instance.host:
- accepted_events.append(event)
- accepted_instances.add(instance)
- LOG.info(_LI('Creating event %(name)s:%(tag)s for '
- 'instance %(instance_uuid)s'),
- {'name': event.name, 'tag': event.tag,
- 'instance_uuid': event.instance_uuid})
- # NOTE: as the event is processed asynchronously verify
- # whether 202 is a more suitable response code than 200
- _event['status'] = 'completed'
- _event['code'] = 200
- else:
- LOG.debug("Unable to find a host for instance "
- "%(instance)s. Dropping event %(event)s",
- {'instance': event.instance_uuid,
- 'event': event.name})
- _event['status'] = 'failed'
- _event['code'] = 422
- result = 207
-
- response_events.append(_event)
-
- if accepted_events:
- self.compute_api.external_instance_event(
- context, accepted_instances, accepted_events)
- else:
- msg = _('No instances found for any event')
- raise webob.exc.HTTPNotFound(explanation=msg)
-
- # FIXME(cyeoh): This needs some infrastructure support so that
- # we have a general way to do this
- robj = wsgi.ResponseObject({'events': response_events})
- robj._code = result
- return robj
-
-
-class Server_external_events(extensions.ExtensionDescriptor):
- """Server External Event Triggers."""
-
- name = "ServerExternalEvents"
- alias = "os-server-external-events"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "server-external-events/api/v2")
- updated = "2014-02-18T00:00:00Z"
-
- def get_resources(self):
- resource = extensions.ResourceExtension('os-server-external-events',
- ServerExternalEventsController())
-
- return [resource]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_group_quotas.py b/nova/api/openstack/compute/legacy_v2/contrib/server_group_quotas.py
deleted file mode 100644
index e2001b23ac..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/server_group_quotas.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# 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 extensions
-from nova.api.openstack import wsgi
-from nova import quota
-
-QUOTAS = quota.QUOTAS
-
-
-class ExtendedLimitsController(wsgi.Controller):
-
- @wsgi.extends
- def index(self, req, resp_obj):
-
- context = req.environ['nova.context']
- quotas = QUOTAS.get_project_quotas(context, context.project_id,
- usages=False)
- abs = resp_obj.obj.get('limits', {}).get('absolute', {})
- abs['maxServerGroups'] = quotas.get('server_groups').get('limit')
- abs['maxServerGroupMembers'] =\
- quotas.get('server_group_members').get('limit')
-
-
-class Server_group_quotas(extensions.ExtensionDescriptor):
- """Adds quota support to server groups."""
-
- name = "ServerGroupQuotas"
- alias = "os-server-group-quotas"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "server-group-quotas/api/v2")
- updated = "2014-07-25T00:00:00Z"
-
- def get_controller_extensions(self):
- extension_list = [extensions.ControllerExtension(self,
- 'limits',
- ExtendedLimitsController()),
- ]
- return extension_list
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_groups.py b/nova/api/openstack/compute/legacy_v2/contrib/server_groups.py
deleted file mode 100644
index 88bf573d05..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/server_groups.py
+++ /dev/null
@@ -1,243 +0,0 @@
-# Copyright (c) 2014 Cisco Systems, Inc.
-# 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 Server Group API Extension."""
-
-from oslo_log import log as logging
-import webob
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova.api.validation import parameter_types
-import nova.exception
-from nova.i18n import _
-from nova.i18n import _LE
-from nova import objects
-from nova import utils
-
-LOG = logging.getLogger(__name__)
-
-
-SUPPORTED_POLICIES = ['anti-affinity', 'affinity']
-
-authorize = extensions.extension_authorizer('compute', 'server_groups')
-
-
-def _authorize_context(req):
- context = req.environ['nova.context']
- authorize(context)
- return context
-
-
-class ServerGroupController(wsgi.Controller):
- """The Server group API controller for the OpenStack API."""
-
- def __init__(self, ext_mgr):
- self.ext_mgr = ext_mgr
-
- def _format_server_group(self, context, group):
- # the id field has its value as the uuid of the server group
- # There is no 'uuid' key in server_group seen by clients.
- # In addition, clients see policies as a ["policy-name"] list;
- # and they see members as a ["server-id"] list.
- server_group = {}
- server_group['id'] = group.uuid
- server_group['name'] = group.name
- server_group['policies'] = group.policies or []
- # NOTE(danms): This has been exposed to the user, but never used.
- # Since we can't remove it, just make sure it's always empty.
- server_group['metadata'] = {}
- members = []
- if group.members:
- # Display the instances that are not deleted.
- filters = {'uuid': group.members, 'deleted': False}
- instances = objects.InstanceList.get_by_filters(
- context, filters=filters)
- members = [instance.uuid for instance in instances]
- server_group['members'] = members
- return server_group
-
- def _validate_policies(self, policies):
- """Validate the policies.
-
- Validates that there are no contradicting policies, for example
- 'anti-affinity' and 'affinity' in the same group.
- Validates that the defined policies are supported.
- :param policies: the given policies of the server_group
- """
- if ('anti-affinity' in policies and
- 'affinity' in policies):
- msg = _("Conflicting policies configured!")
- raise nova.exception.InvalidInput(reason=msg)
- not_supported = [policy for policy in policies
- if policy not in SUPPORTED_POLICIES]
- if not_supported:
- msg = _("Invalid policies: %s") % ', '.join(not_supported)
- raise nova.exception.InvalidInput(reason=msg)
-
- # Note(wingwj): It doesn't make sense to store duplicate policies.
- if sorted(set(policies)) != sorted(policies):
- msg = _("Duplicate policies configured!")
- raise nova.exception.InvalidInput(reason=msg)
-
- def _validate_input_body(self, body, entity_name):
- if not self.is_valid_body(body, entity_name):
- msg = _("the body is invalid.")
- raise nova.exception.InvalidInput(reason=msg)
-
- subbody = dict(body[entity_name])
-
- expected_fields = ['name', 'policies']
- for field in expected_fields:
- value = subbody.pop(field, None)
- if not value:
- msg = _("'%s' is either missing or empty.") % field
- raise nova.exception.InvalidInput(reason=msg)
- if field == 'name':
- utils.check_string_length(value, field,
- min_length=1, max_length=255)
- if not parameter_types.valid_name_regex_obj.search(value):
- msg = _("Invalid format for name: '%s'") % value
- raise nova.exception.InvalidInput(reason=msg)
- elif field == 'policies':
- if isinstance(value, list):
- [utils.check_string_length(v, field,
- min_length=1, max_length=255) for v in value]
- self._validate_policies(value)
- else:
- msg = _("'%s' is not a list") % value
- raise nova.exception.InvalidInput(reason=msg)
-
- if subbody:
- msg = _("unsupported fields: %s") % subbody.keys()
- raise nova.exception.InvalidInput(reason=msg)
-
- def show(self, req, id):
- """Return data about the given server group."""
- context = _authorize_context(req)
- try:
- sg = objects.InstanceGroup.get_by_uuid(context, id)
- except nova.exception.InstanceGroupNotFound as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
- return {'server_group': self._format_server_group(context, sg)}
-
- def delete(self, req, id):
- """Delete an server group."""
- context = _authorize_context(req)
- try:
- sg = objects.InstanceGroup.get_by_uuid(context, id)
- except nova.exception.InstanceGroupNotFound as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
-
- quotas = None
- if self.ext_mgr.is_loaded('os-server-group-quotas'):
- quotas = objects.Quotas(context=context)
- project_id, user_id = objects.quotas.ids_from_server_group(context,
- sg)
- try:
- # We have to add the quota back to the user that created
- # the server group
- quotas.reserve(project_id=project_id,
- user_id=user_id, server_groups=-1)
- except Exception:
- quotas = None
- LOG.exception(_LE("Failed to update usages deallocating "
- "server group"))
-
- try:
- sg.destroy()
- except nova.exception.InstanceGroupNotFound as e:
- if quotas:
- quotas.rollback()
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
-
- if quotas:
- quotas.commit()
-
- return webob.Response(status_int=204)
-
- def index(self, req):
- """Returns a list of server groups."""
- context = _authorize_context(req)
- project_id = context.project_id
- if 'all_projects' in req.GET and context.is_admin:
- sgs = objects.InstanceGroupList.get_all(context)
- else:
- sgs = objects.InstanceGroupList.get_by_project_id(
- context, project_id)
- limited_list = common.limited(sgs.objects, req)
- result = [self._format_server_group(context, group)
- for group in limited_list]
- return {'server_groups': result}
-
- def create(self, req, body):
- """Creates a new server group."""
- context = _authorize_context(req)
-
- try:
- self._validate_input_body(body, 'server_group')
- except nova.exception.InvalidInput as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- quotas = None
- if self.ext_mgr.is_loaded('os-server-group-quotas'):
- quotas = objects.Quotas(context=context)
- try:
- quotas.reserve(project_id=context.project_id,
- user_id=context.user_id, server_groups=1)
- except nova.exception.OverQuota:
- msg = _("Quota exceeded, too many server groups.")
- raise exc.HTTPForbidden(explanation=msg)
-
- vals = body['server_group']
- sg = objects.InstanceGroup(context)
- sg.project_id = context.project_id
- sg.user_id = context.user_id
- try:
- sg.name = vals.get('name')
- sg.policies = vals.get('policies')
- sg.create()
- except ValueError as e:
- if quotas:
- quotas.rollback()
- raise exc.HTTPBadRequest(explanation=e)
-
- if quotas:
- quotas.commit()
-
- return {'server_group': self._format_server_group(context, sg)}
-
-
-class Server_groups(extensions.ExtensionDescriptor):
- """Server group support."""
- name = "ServerGroups"
- alias = "os-server-groups"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "servergroups/api/v2")
- updated = "2013-06-20T00:00:00Z"
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension(
- 'os-server-groups',
- controller=ServerGroupController(self.ext_mgr),
- member_actions={"action": "POST", })
-
- resources.append(res)
-
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_list_multi_status.py b/nova/api/openstack/compute/legacy_v2/contrib/server_list_multi_status.py
deleted file mode 100644
index bdcb2f883f..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/server_list_multi_status.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# 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 extensions
-
-
-class Server_list_multi_status(extensions.ExtensionDescriptor):
- """Allow to specify multiple status values concurrently in the servers
- list API..
- """
-
- name = "ServerListMultiStatus"
- alias = "os-server-list-multi-status"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "os-server-list-multi-status/api/v2")
- updated = "2014-05-11T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_password.py b/nova/api/openstack/compute/legacy_v2/contrib/server_password.py
deleted file mode 100644
index 6c633e3783..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/server_password.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# Copyright (c) 2012 Nebula, Inc.
-# 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 server password extension."""
-
-from nova.api.metadata import password
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import compute
-
-
-authorize = extensions.extension_authorizer('compute', 'server_password')
-
-
-class ServerPasswordController(object):
- """The Server Password API controller for the OpenStack API."""
- def __init__(self):
- self.compute_api = compute.API()
-
- def index(self, req, server_id):
- context = req.environ['nova.context']
- authorize(context)
- instance = common.get_instance(self.compute_api, context, server_id)
-
- passw = password.extract_password(instance)
- return {'password': passw or ''}
-
- @wsgi.response(204)
- def delete(self, req, server_id):
- context = req.environ['nova.context']
- authorize(context)
- instance = common.get_instance(self.compute_api, context, server_id)
- meta = password.convert_password(context, None)
- instance.system_metadata.update(meta)
- instance.save()
-
-
-class Server_password(extensions.ExtensionDescriptor):
- """Server password support."""
-
- name = "ServerPassword"
- alias = "os-server-password"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "server-password/api/v2")
- updated = "2012-11-29T00:00:00Z"
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension(
- 'os-server-password',
- controller=ServerPasswordController(),
- collection_actions={'delete': 'DELETE'},
- parent=dict(member_name='server', collection_name='servers'))
- resources.append(res)
-
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_sort_keys.py b/nova/api/openstack/compute/legacy_v2/contrib/server_sort_keys.py
deleted file mode 100644
index 01ce14b705..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/server_sort_keys.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2014 IBM Corp.
-#
-# 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 extensions
-
-
-class Server_sort_keys(extensions.ExtensionDescriptor):
- """Add sort keys and directions to the Server GET v2 API."""
-
- name = "ServerSortKeys"
- alias = "os-server-sort-keys"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "server_sort_keys/api/v2")
- updated = "2014-05-22T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_start_stop.py b/nova/api/openstack/compute/legacy_v2/contrib/server_start_stop.py
deleted file mode 100644
index f4606aba2e..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/server_start_stop.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# Copyright 2012 Midokura Japan K.K.
-#
-# 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
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import compute
-from nova import exception
-from nova.i18n import _
-from nova import objects
-
-
-class ServerStartStopActionController(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- super(ServerStartStopActionController, self).__init__(*args, **kwargs)
- self.compute_api = compute.API()
-
- def _get_instance(self, context, instance_uuid):
- try:
- attrs = ['system_metadata', 'metadata']
- return objects.Instance.get_by_uuid(context, instance_uuid,
- expected_attrs=attrs)
- except exception.NotFound:
- msg = _("Instance not found")
- raise webob.exc.HTTPNotFound(explanation=msg)
-
- @wsgi.action('os-start')
- def _start_server(self, req, id, body):
- """Start an instance."""
- context = req.environ['nova.context']
- instance = self._get_instance(context, id)
- extensions.check_compute_policy(context, 'start', instance)
-
- try:
- self.compute_api.start(context, instance)
- except (exception.InstanceNotReady, exception.InstanceIsLocked) as e:
- raise webob.exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'start', id)
- return webob.Response(status_int=202)
-
- @wsgi.action('os-stop')
- def _stop_server(self, req, id, body):
- """Stop an instance."""
- context = req.environ['nova.context']
- instance = self._get_instance(context, id)
- extensions.check_compute_policy(context, 'stop', instance)
-
- try:
- self.compute_api.stop(context, instance)
- except (exception.InstanceNotReady, exception.InstanceIsLocked) as e:
- raise webob.exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'stop', id)
- return webob.Response(status_int=202)
-
-
-class Server_start_stop(extensions.ExtensionDescriptor):
- """Start/Stop instance compute API support."""
-
- name = "ServerStartStop"
- alias = "os-server-start-stop"
- namespace = "http://docs.openstack.org/compute/ext/servers/api/v1.1"
- updated = "2012-01-23T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = ServerStartStopActionController()
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/server_usage.py b/nova/api/openstack/compute/legacy_v2/contrib/server_usage.py
deleted file mode 100644
index f09e22db2f..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/server_usage.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright 2013 OpenStack Foundation
-#
-# 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 extensions
-from nova.api.openstack import wsgi
-from nova import compute
-
-authorize = extensions.soft_extension_authorizer('compute', 'server_usage')
-
-
-class ServerUsageController(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- super(ServerUsageController, self).__init__(*args, **kwargs)
- self.compute_api = compute.API()
-
- def _extend_server(self, server, instance):
- for k in ['launched_at', 'terminated_at']:
- key = "%s:%s" % (Server_usage.alias, k)
- # NOTE(danms): Historically, this timestamp has been generated
- # merely by grabbing str(datetime) of a TZ-naive object. The
- # only way we can keep that with instance objects is to strip
- # the tzinfo from the stamp and str() it.
- server[key] = (instance[k].replace(tzinfo=None)
- if instance[k] else None)
-
- @wsgi.extends
- def show(self, req, resp_obj, id):
- context = req.environ['nova.context']
- if authorize(context):
- server = resp_obj.obj['server']
- db_instance = req.get_db_instance(server['id'])
- # server['id'] is guaranteed to be in the cache due to
- # the core API adding it in its 'show' method.
- self._extend_server(server, db_instance)
-
- @wsgi.extends
- def detail(self, req, resp_obj):
- context = req.environ['nova.context']
- if authorize(context):
- servers = list(resp_obj.obj['servers'])
- for server in servers:
- db_instance = req.get_db_instance(server['id'])
- # server['id'] is guaranteed to be in the cache due to
- # the core API adding it in its 'detail' method.
- self._extend_server(server, db_instance)
-
-
-class Server_usage(extensions.ExtensionDescriptor):
- """Adds launched_at and terminated_at on Servers."""
-
- name = "ServerUsage"
- alias = "OS-SRV-USG"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "server_usage/api/v1.1")
- updated = "2013-04-29T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = ServerUsageController()
- extension = extensions.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/services.py b/nova/api/openstack/compute/legacy_v2/contrib/services.py
deleted file mode 100644
index 625a10157d..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/services.py
+++ /dev/null
@@ -1,199 +0,0 @@
-# Copyright 2012 IBM Corp.
-#
-# 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
-from nova import compute
-from nova import context as nova_context
-from nova import exception
-from nova.i18n import _
-from nova import servicegroup
-from nova import utils
-
-authorize = extensions.extension_authorizer('compute', 'services')
-
-
-class ServiceController(object):
-
- def __init__(self, ext_mgr=None, *args, **kwargs):
- self.host_api = compute.HostAPI()
- self.servicegroup_api = servicegroup.API()
- self.ext_mgr = ext_mgr
-
- def _get_services(self, req):
- api_services = ('nova-osapi_compute', 'nova-ec2', 'nova-metadata')
-
- context = req.environ['nova.context']
- authorize(context)
-
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks
- nova_context.require_admin_context(context)
-
- services = [
- s
- for s in self.host_api.service_get_all(context, set_zones=True)
- if s['binary'] not in api_services
- ]
-
- host = ''
- if 'host' in req.GET:
- host = req.GET['host']
- binary = ''
- if 'binary' in req.GET:
- binary = req.GET['binary']
- if host:
- services = [s for s in services if s['host'] == host]
- if binary:
- services = [s for s in services if s['binary'] == binary]
-
- return services
-
- def _get_service_detail(self, svc, detailed):
- alive = self.servicegroup_api.service_is_up(svc)
- state = (alive and "up") or "down"
- active = 'enabled'
- if svc['disabled']:
- active = 'disabled'
- service_detail = {'binary': svc['binary'], 'host': svc['host'],
- 'zone': svc['availability_zone'],
- 'status': active, 'state': state,
- 'updated_at': svc['updated_at']}
- if self.ext_mgr.is_loaded('os-extended-services-delete'):
- service_detail['id'] = svc['id']
- if detailed:
- service_detail['disabled_reason'] = svc['disabled_reason']
-
- return service_detail
-
- def _get_services_list(self, req, detailed):
- services = self._get_services(req)
- svcs = []
- for svc in services:
- svcs.append(self._get_service_detail(svc, detailed))
-
- return svcs
-
- def _is_valid_as_reason(self, reason):
- try:
- utils.check_string_length(reason.strip(), 'Disabled reason',
- min_length=1, max_length=255)
- except exception.InvalidInput:
- return False
-
- return True
-
- @wsgi.response(204)
- def delete(self, req, id):
- """Deletes the specified service."""
- if not self.ext_mgr.is_loaded('os-extended-services-delete'):
- raise webob.exc.HTTPMethodNotAllowed()
-
- context = req.environ['nova.context']
- authorize(context)
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks
- nova_context.require_admin_context(context)
-
- try:
- utils.validate_integer(id, 'id')
- except exception.InvalidInput as exc:
- raise webob.exc.HTTPBadRequest(explanation=exc.format_message())
-
- try:
- self.host_api.service_delete(context, id)
- except exception.ServiceNotFound:
- explanation = _("Service %s not found.") % id
- raise webob.exc.HTTPNotFound(explanation=explanation)
-
- def index(self, req):
- """Return a list of all running services."""
- detailed = self.ext_mgr.is_loaded('os-extended-services')
- services = self._get_services_list(req, detailed)
-
- return {'services': services}
-
- def update(self, req, id, body):
- """Enable/Disable scheduling for a service."""
- context = req.environ['nova.context']
- authorize(context)
- # NOTE(alex_xu): back-compatible with db layer hard-code admin
- # permission checks
- nova_context.require_admin_context(context)
- ext_loaded = self.ext_mgr.is_loaded('os-extended-services')
- if id == "enable":
- disabled = False
- status = "enabled"
- elif (id == "disable" or
- (id == "disable-log-reason" and ext_loaded)):
- disabled = True
- status = "disabled"
- else:
- msg = _("Unknown action")
- raise webob.exc.HTTPNotFound(explanation=msg)
- try:
- host = body['host']
- binary = body['binary']
- ret_value = {
- 'service': {
- 'host': host,
- 'binary': binary,
- 'status': status,
- },
- }
- status_detail = {
- 'disabled': disabled,
- 'disabled_reason': None,
- }
- if id == "disable-log-reason":
- reason = body['disabled_reason']
- if not self._is_valid_as_reason(reason):
- msg = _('The string containing the reason for disabling '
- 'the service contains invalid characters or is '
- 'too long.')
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- status_detail['disabled_reason'] = reason
- ret_value['service']['disabled_reason'] = reason
- except (TypeError, KeyError):
- msg = _('Invalid attribute in the request')
- if 'host' in body and 'binary' in body:
- msg = _('Missing disabled reason field')
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- try:
- self.host_api.service_update(context, host, binary, status_detail)
- except exception.HostBinaryNotFound as e:
- raise webob.exc.HTTPNotFound(explanation=e.format_message())
-
- return ret_value
-
-
-class Services(extensions.ExtensionDescriptor):
- """Services support."""
-
- name = "Services"
- alias = "os-services"
- namespace = "http://docs.openstack.org/compute/ext/services/api/v2"
- updated = "2012-10-28T00:00:00Z"
-
- def get_resources(self):
- resources = []
- resource = extensions.ResourceExtension('os-services',
- ServiceController(self.ext_mgr))
-
- resources.append(resource)
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/shelve.py b/nova/api/openstack/compute/legacy_v2/contrib/shelve.py
deleted file mode 100644
index b457e91b5c..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/shelve.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# Copyright 2013 Rackspace Hosting
-#
-# 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 shelved mode extension."""
-
-import webob
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions as exts
-from nova.api.openstack import wsgi
-from nova import compute
-from nova import exception
-
-
-auth_shelve = exts.extension_authorizer('compute', 'shelve')
-auth_shelve_offload = exts.extension_authorizer('compute', 'shelveOffload')
-auth_unshelve = exts.extension_authorizer('compute', 'unshelve')
-
-
-class ShelveController(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- super(ShelveController, self).__init__(*args, **kwargs)
- self.compute_api = compute.API()
-
- @wsgi.action('shelve')
- def _shelve(self, req, id, body):
- """Move an instance into shelved mode."""
- context = req.environ["nova.context"]
- auth_shelve(context)
-
- instance = common.get_instance(self.compute_api, context, id)
- try:
- self.compute_api.shelve(context, instance)
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'shelve', id)
-
- return webob.Response(status_int=202)
-
- @wsgi.action('shelveOffload')
- def _shelve_offload(self, req, id, body):
- """Force removal of a shelved instance from the compute node."""
- context = req.environ["nova.context"]
- auth_shelve_offload(context)
-
- instance = common.get_instance(self.compute_api, context, id)
- try:
- self.compute_api.shelve_offload(context, instance)
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'shelveOffload',
- id)
-
- return webob.Response(status_int=202)
-
- @wsgi.action('unshelve')
- def _unshelve(self, req, id, body):
- """Restore an instance from shelved mode."""
- context = req.environ["nova.context"]
- auth_unshelve(context)
- instance = common.get_instance(self.compute_api, context, id)
- try:
- self.compute_api.unshelve(context, instance)
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'unshelve',
- id)
- return webob.Response(status_int=202)
-
-
-class Shelve(exts.ExtensionDescriptor):
- """Instance shelve mode."""
-
- name = "Shelve"
- alias = "os-shelve"
- namespace = "http://docs.openstack.org/compute/ext/shelve/api/v1.1"
- updated = "2013-04-06T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = ShelveController()
- extension = exts.ControllerExtension(self, 'servers', controller)
- return [extension]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/simple_tenant_usage.py b/nova/api/openstack/compute/legacy_v2/contrib/simple_tenant_usage.py
deleted file mode 100644
index 75d1e38116..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/simple_tenant_usage.py
+++ /dev/null
@@ -1,284 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-# 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.
-
-import datetime
-
-import iso8601
-from oslo_utils import timeutils
-import six
-import six.moves.urllib.parse as urlparse
-from webob import exc
-
-from nova.api.openstack import extensions
-from nova import exception
-from nova.i18n import _
-from nova import objects
-
-authorize_show = extensions.extension_authorizer('compute',
- 'simple_tenant_usage:show')
-authorize_list = extensions.extension_authorizer('compute',
- 'simple_tenant_usage:list')
-
-
-def parse_strtime(dstr, fmt):
- try:
- return timeutils.parse_strtime(dstr, fmt)
- except (TypeError, ValueError) as e:
- raise exception.InvalidStrTime(reason=six.text_type(e))
-
-
-class SimpleTenantUsageController(object):
- def _hours_for(self, instance, period_start, period_stop):
- launched_at = instance.launched_at
- terminated_at = instance.terminated_at
- if terminated_at is not None:
- if not isinstance(terminated_at, datetime.datetime):
- # NOTE(mriedem): Instance object DateTime fields are
- # timezone-aware so convert using isotime.
- terminated_at = timeutils.parse_isotime(terminated_at)
-
- if launched_at is not None:
- if not isinstance(launched_at, datetime.datetime):
- launched_at = timeutils.parse_isotime(launched_at)
-
- if terminated_at and terminated_at < period_start:
- return 0
- # nothing if it started after the usage report ended
- if launched_at and launched_at > period_stop:
- return 0
- if launched_at:
- # if instance launched after period_started, don't charge for first
- start = max(launched_at, period_start)
- if terminated_at:
- # if instance stopped before period_stop, don't charge after
- stop = min(period_stop, terminated_at)
- else:
- # instance is still running, so charge them up to current time
- stop = period_stop
- dt = stop - start
- return dt.total_seconds() / 3600.0
- else:
- # instance hasn't launched, so no charge
- return 0
-
- def _get_flavor(self, context, instance, flavors_cache):
- """Get flavor information from the instance object,
- allowing a fallback to lookup by-id for deleted instances only.
- """
- try:
- return instance.get_flavor()
- except exception.NotFound:
- if not instance.deleted:
- # Only support the fallback mechanism for deleted instances
- # that would have been skipped by migration #153
- raise
-
- flavor_type = instance.instance_type_id
- if flavor_type in flavors_cache:
- return flavors_cache[flavor_type]
-
- try:
- flavor_ref = objects.Flavor.get_by_id(context, flavor_type)
- flavors_cache[flavor_type] = flavor_ref
- except exception.FlavorNotFound:
- # can't bill if there is no flavor
- flavor_ref = None
-
- return flavor_ref
-
- def _tenant_usages_for_period(self, context, period_start,
- period_stop, tenant_id=None, detailed=True):
-
- instances = objects.InstanceList.get_active_by_window_joined(
- context, period_start, period_stop, tenant_id,
- expected_attrs=['flavor'])
- rval = {}
- flavors = {}
-
- for instance in instances:
- info = {}
- info['hours'] = self._hours_for(instance,
- period_start,
- period_stop)
- flavor = self._get_flavor(context, instance, flavors)
- if not flavor:
- info['flavor'] = ''
- else:
- info['flavor'] = flavor.name
-
- info['instance_id'] = instance.uuid
- info['name'] = instance.display_name
-
- info['memory_mb'] = instance.memory_mb
- info['local_gb'] = instance.root_gb + instance.ephemeral_gb
- info['vcpus'] = instance.vcpus
-
- info['tenant_id'] = instance.project_id
-
- # NOTE(mriedem): We need to normalize the start/end times back
- # to timezone-naive so the response doesn't change after the
- # conversion to objects.
- info['started_at'] = timeutils.normalize_time(instance.launched_at)
-
- info['ended_at'] = (
- timeutils.normalize_time(instance.terminated_at) if
- instance.terminated_at else None)
-
- if info['ended_at']:
- info['state'] = 'terminated'
- else:
- info['state'] = instance.vm_state
-
- now = timeutils.utcnow()
-
- if info['state'] == 'terminated':
- delta = info['ended_at'] - info['started_at']
- else:
- delta = now - info['started_at']
-
- info['uptime'] = int(delta.total_seconds())
-
- if info['tenant_id'] not in rval:
- summary = {}
- summary['tenant_id'] = info['tenant_id']
- if detailed:
- summary['server_usages'] = []
- summary['total_local_gb_usage'] = 0
- summary['total_vcpus_usage'] = 0
- summary['total_memory_mb_usage'] = 0
- summary['total_hours'] = 0
- summary['start'] = timeutils.normalize_time(period_start)
- summary['stop'] = timeutils.normalize_time(period_stop)
- rval[info['tenant_id']] = summary
-
- summary = rval[info['tenant_id']]
- summary['total_local_gb_usage'] += info['local_gb'] * info['hours']
- summary['total_vcpus_usage'] += info['vcpus'] * info['hours']
- summary['total_memory_mb_usage'] += (info['memory_mb'] *
- info['hours'])
-
- summary['total_hours'] += info['hours']
- if detailed:
- summary['server_usages'].append(info)
-
- return rval.values()
-
- def _parse_datetime(self, dtstr):
- if not dtstr:
- value = timeutils.utcnow()
- elif isinstance(dtstr, datetime.datetime):
- value = dtstr
- else:
- for fmt in ["%Y-%m-%dT%H:%M:%S",
- "%Y-%m-%dT%H:%M:%S.%f",
- "%Y-%m-%d %H:%M:%S.%f"]:
- try:
- value = parse_strtime(dtstr, fmt)
- break
- except exception.InvalidStrTime:
- pass
- else:
- msg = _("Datetime is in invalid format")
- raise exception.InvalidStrTime(reason=msg)
-
- # NOTE(mriedem): Instance object DateTime fields are timezone-aware
- # so we have to force UTC timezone for comparing this datetime against
- # instance object fields and still maintain backwards compatibility
- # in the API.
- if value.utcoffset() is None:
- value = value.replace(tzinfo=iso8601.iso8601.Utc())
- return value
-
- def _get_datetime_range(self, req):
- qs = req.environ.get('QUERY_STRING', '')
- env = urlparse.parse_qs(qs)
- # NOTE(lzyeval): env.get() always returns a list
- period_start = self._parse_datetime(env.get('start', [None])[0])
- period_stop = self._parse_datetime(env.get('end', [None])[0])
-
- if not period_start < period_stop:
- msg = _("Invalid start time. The start time cannot occur after "
- "the end time.")
- raise exc.HTTPBadRequest(explanation=msg)
-
- detailed = env.get('detailed', ['0'])[0] == '1'
- return (period_start, period_stop, detailed)
-
- def index(self, req):
- """Retrieve tenant_usage for all tenants."""
- context = req.environ['nova.context']
-
- authorize_list(context)
-
- try:
- (period_start, period_stop, detailed) = self._get_datetime_range(
- req)
- except exception.InvalidStrTime as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- now = timeutils.parse_isotime(timeutils.utcnow().isoformat())
- if period_stop > now:
- period_stop = now
- usages = self._tenant_usages_for_period(context,
- period_start,
- period_stop,
- detailed=detailed)
- return {'tenant_usages': usages}
-
- def show(self, req, id):
- """Retrieve tenant_usage for a specified tenant."""
- tenant_id = id
- context = req.environ['nova.context']
-
- authorize_show(context, {'project_id': tenant_id})
-
- try:
- (period_start, period_stop, ignore) = self._get_datetime_range(
- req)
- except exception.InvalidStrTime as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- now = timeutils.parse_isotime(timeutils.utcnow().isoformat())
- if period_stop > now:
- period_stop = now
- usage = self._tenant_usages_for_period(context,
- period_start,
- period_stop,
- tenant_id=tenant_id,
- detailed=True)
- if len(usage):
- usage = list(usage)[0]
- else:
- usage = {}
- return {'tenant_usage': usage}
-
-
-class Simple_tenant_usage(extensions.ExtensionDescriptor):
- """Simple tenant usage extension."""
-
- name = "SimpleTenantUsage"
- alias = "os-simple-tenant-usage"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "os-simple-tenant-usage/api/v1.1")
- updated = "2011-08-19T00:00:00Z"
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension('os-simple-tenant-usage',
- SimpleTenantUsageController())
- resources.append(res)
-
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/used_limits.py b/nova/api/openstack/compute/legacy_v2/contrib/used_limits.py
deleted file mode 100644
index 81c4e79dc7..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/used_limits.py
+++ /dev/null
@@ -1,93 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# 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 six
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import quota
-
-
-QUOTAS = quota.QUOTAS
-
-
-XMLNS = "http://docs.openstack.org/compute/ext/used_limits/api/v1.1"
-ALIAS = "os-used-limits"
-authorize = extensions.soft_extension_authorizer('compute', 'used_limits')
-authorize_for_admin = extensions.extension_authorizer('compute',
- 'used_limits_for_admin')
-
-
-class UsedLimitsController(wsgi.Controller):
-
- def __init__(self, ext_mgr):
- self.ext_mgr = ext_mgr
-
- @staticmethod
- def _reserved(req):
- try:
- return int(req.GET['reserved'])
- except (ValueError, KeyError):
- return False
-
- @wsgi.extends
- def index(self, req, resp_obj):
- context = req.environ['nova.context']
- project_id = self._project_id(context, req)
- quotas = QUOTAS.get_project_quotas(context, project_id, usages=True)
- quota_map = {
- 'totalRAMUsed': 'ram',
- 'totalCoresUsed': 'cores',
- 'totalInstancesUsed': 'instances',
- 'totalFloatingIpsUsed': 'floating_ips',
- 'totalSecurityGroupsUsed': 'security_groups',
- }
- if self.ext_mgr.is_loaded('os-server-group-quotas'):
- quota_map['totalServerGroupsUsed'] = 'server_groups'
-
- used_limits = {}
- for display_name, key in six.iteritems(quota_map):
- if key in quotas:
- reserved = (quotas[key]['reserved']
- if self._reserved(req) else 0)
- used_limits[display_name] = quotas[key]['in_use'] + reserved
-
- resp_obj.obj['limits']['absolute'].update(used_limits)
-
- def _project_id(self, context, req):
- if self.ext_mgr.is_loaded('os-used-limits-for-admin'):
- if 'tenant_id' in req.GET:
- tenant_id = req.GET.get('tenant_id')
- target = {
- 'project_id': tenant_id,
- 'user_id': context.user_id
- }
- authorize_for_admin(context, target=target)
- return tenant_id
- return context.project_id
-
-
-class Used_limits(extensions.ExtensionDescriptor):
- """Provide data on limited resources that are being used."""
-
- name = "UsedLimits"
- alias = ALIAS
- namespace = XMLNS
- updated = "2012-07-13T00:00:00Z"
-
- def get_controller_extensions(self):
- controller = UsedLimitsController(self.ext_mgr)
- limits_ext = extensions.ControllerExtension(self, 'limits',
- controller=controller)
- return [limits_ext]
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/used_limits_for_admin.py b/nova/api/openstack/compute/legacy_v2/contrib/used_limits_for_admin.py
deleted file mode 100644
index f731a7aa0c..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/used_limits_for_admin.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2013 OpenStack Foundation
-#
-# 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 extensions
-
-
-class Used_limits_for_admin(extensions.ExtensionDescriptor):
- """Provide data to admin on limited resources used by other tenants."""
-
- name = "UsedLimitsForAdmin"
- alias = "os-used-limits-for-admin"
- namespace = ("http://docs.openstack.org/compute/ext/used_limits_for_admin"
- "/api/v1.1")
- updated = "2013-05-02T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/user_data.py b/nova/api/openstack/compute/legacy_v2/contrib/user_data.py
deleted file mode 100644
index b7e559c110..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/user_data.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# 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 extensions
-
-
-class User_data(extensions.ExtensionDescriptor):
- """Add user_data to the Create Server v1.1 API."""
-
- name = "UserData"
- alias = "os-user-data"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "userdata/api/v1.1")
- updated = "2012-08-07T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/user_quotas.py b/nova/api/openstack/compute/legacy_v2/contrib/user_quotas.py
deleted file mode 100644
index 344166e3cf..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/user_quotas.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright 2013 OpenStack Foundation
-# 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 extensions
-
-
-class User_quotas(extensions.ExtensionDescriptor):
- """Project user quota support."""
-
- name = "UserQuotas"
- alias = "os-user-quotas"
- namespace = ("http://docs.openstack.org/compute/ext/user_quotas"
- "/api/v1.1")
- updated = "2013-07-18T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/virtual_interfaces.py b/nova/api/openstack/compute/legacy_v2/contrib/virtual_interfaces.py
deleted file mode 100644
index faaabbc6a2..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/virtual_interfaces.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# Copyright (C) 2011 Midokura KK
-# 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 virtual interfaces extension."""
-
-import webob
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova import compute
-from nova.i18n import _
-from nova import network
-
-
-authorize = extensions.extension_authorizer('compute', 'virtual_interfaces')
-
-
-def _translate_vif_summary_view(vif):
- """Maps keys for VIF summary view."""
- d = {}
- d['id'] = vif.uuid
- d['mac_address'] = vif.address
- return d
-
-
-class ServerVirtualInterfaceController(object):
- """The instance VIF API controller for the OpenStack API.
- """
-
- def __init__(self):
- self.compute_api = compute.API()
- self.network_api = network.API()
- super(ServerVirtualInterfaceController, self).__init__()
-
- def _items(self, req, server_id, entity_maker):
- """Returns a list of VIFs, transformed through entity_maker."""
- context = req.environ['nova.context']
- instance = common.get_instance(self.compute_api, context, server_id)
-
- try:
- vifs = self.network_api.get_vifs_by_instance(context, instance)
- except NotImplementedError:
- msg = _('Listing virtual interfaces is not supported by this '
- 'cloud.')
- raise webob.exc.HTTPBadRequest(explanation=msg)
- limited_list = common.limited(vifs, req)
- res = [entity_maker(vif) for vif in limited_list]
- return {'virtual_interfaces': res}
-
- def index(self, req, server_id):
- """Returns the list of VIFs for a given instance."""
- authorize(req.environ['nova.context'])
- return self._items(req, server_id,
- entity_maker=_translate_vif_summary_view)
-
-
-class Virtual_interfaces(extensions.ExtensionDescriptor):
- """Virtual interface support."""
-
- name = "VirtualInterfaces"
- alias = "os-virtual-interfaces"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "virtual_interfaces/api/v1.1")
- updated = "2011-08-17T00:00:00Z"
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension(
- 'os-virtual-interfaces',
- controller=ServerVirtualInterfaceController(),
- parent=dict(member_name='server', collection_name='servers'))
- resources.append(res)
-
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/volume_attachment_update.py b/nova/api/openstack/compute/legacy_v2/contrib/volume_attachment_update.py
deleted file mode 100644
index 32d9cbdce4..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/volume_attachment_update.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright 2013 Nebula, Inc.
-# 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 extensions
-
-
-class Volume_attachment_update(extensions.ExtensionDescriptor):
- """Support for updating a volume attachment."""
-
- name = "VolumeAttachmentUpdate"
- alias = "os-volume-attachment-update"
- namespace = ("http://docs.openstack.org/compute/ext/"
- "os-volume-attachment-update/api/v2")
- updated = "2013-06-20T00:00:00Z"
diff --git a/nova/api/openstack/compute/legacy_v2/contrib/volumes.py b/nova/api/openstack/compute/legacy_v2/contrib/volumes.py
deleted file mode 100644
index 6b0e9966e2..0000000000
--- a/nova/api/openstack/compute/legacy_v2/contrib/volumes.py
+++ /dev/null
@@ -1,613 +0,0 @@
-# Copyright 2011 Justin Santa Barbara
-# 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 volumes extension."""
-
-from oslo_log import log as logging
-from oslo_utils import strutils
-from oslo_utils import uuidutils
-import webob
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import compute
-from nova import exception
-from nova.i18n import _
-from nova.i18n import _LI
-from nova import objects
-from nova import volume
-
-LOG = logging.getLogger(__name__)
-authorize = extensions.extension_authorizer('compute', 'volumes')
-
-authorize_attach = extensions.extension_authorizer('compute',
- 'volume_attachments')
-
-
-def _translate_volume_detail_view(context, vol):
- """Maps keys for volumes details view."""
-
- d = _translate_volume_summary_view(context, vol)
-
- # No additional data / lookups at the moment
-
- return d
-
-
-def _translate_volume_summary_view(context, vol):
- """Maps keys for volumes summary view."""
- d = {}
-
- d['id'] = vol['id']
- d['status'] = vol['status']
- d['size'] = vol['size']
- d['availabilityZone'] = vol['availability_zone']
- d['createdAt'] = vol['created_at']
-
- if vol['attach_status'] == 'attached':
- # NOTE(ildikov): The attachments field in the volume info that
- # Cinder sends is converted to an OrderedDict with the
- # instance_uuid as key to make it easier for the multiattach
- # feature to check the required information. Multiattach will
- # be enable in the Nova API in Newton.
- # The format looks like the following:
- # attachments = {'instance_uuid': {
- # 'attachment_id': 'attachment_uuid',
- # 'mountpoint': '/dev/sda/
- # }
- # }
- attachment = vol['attachments'].items()[0]
- d['attachments'] = [_translate_attachment_detail_view(vol['id'],
- attachment[0],
- attachment[1].get('mountpoint'))]
- else:
- d['attachments'] = [{}]
-
- d['displayName'] = vol['display_name']
- d['displayDescription'] = vol['display_description']
-
- if vol['volume_type_id'] and vol.get('volume_type'):
- d['volumeType'] = vol['volume_type']['name']
- else:
- d['volumeType'] = vol['volume_type_id']
-
- d['snapshotId'] = vol['snapshot_id']
- LOG.info(_LI("vol=%s"), vol, context=context)
-
- if vol.get('volume_metadata'):
- d['metadata'] = vol.get('volume_metadata')
- else:
- d['metadata'] = {}
-
- return d
-
-
-class VolumeController(wsgi.Controller):
- """The Volumes API controller for the OpenStack API."""
-
- def __init__(self):
- self.volume_api = volume.API()
- super(VolumeController, self).__init__()
-
- def show(self, req, id):
- """Return data about the given volume."""
- context = req.environ['nova.context']
- authorize(context)
-
- try:
- vol = self.volume_api.get(context, id)
- except exception.NotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
-
- return {'volume': _translate_volume_detail_view(context, vol)}
-
- def delete(self, req, id):
- """Delete a volume."""
- context = req.environ['nova.context']
- authorize(context)
-
- LOG.info(_LI("Delete volume with id: %s"), id, context=context)
-
- try:
- self.volume_api.delete(context, id)
- except exception.NotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- return webob.Response(status_int=202)
-
- def index(self, req):
- """Returns a summary list of volumes."""
- return self._items(req, entity_maker=_translate_volume_summary_view)
-
- def detail(self, req):
- """Returns a detailed list of volumes."""
- return self._items(req, entity_maker=_translate_volume_detail_view)
-
- def _items(self, req, entity_maker):
- """Returns a list of volumes, transformed through entity_maker."""
- context = req.environ['nova.context']
- authorize(context)
-
- volumes = self.volume_api.get_all(context)
- limited_list = common.limited(volumes, req)
- res = [entity_maker(context, vol) for vol in limited_list]
- return {'volumes': res}
-
- def create(self, req, body):
- """Creates a new volume."""
- context = req.environ['nova.context']
- authorize(context)
-
- if not self.is_valid_body(body, 'volume'):
- msg = _("volume not specified")
- raise exc.HTTPBadRequest(explanation=msg)
-
- vol = body['volume']
-
- vol_type = vol.get('volume_type', None)
-
- metadata = vol.get('metadata', None)
-
- snapshot_id = vol.get('snapshot_id')
-
- if snapshot_id is not None:
- try:
- snapshot = self.volume_api.get_snapshot(context, snapshot_id)
- except exception.SnapshotNotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- else:
- snapshot = None
-
- size = vol.get('size', None)
- if size is None and snapshot is not None:
- size = snapshot['volume_size']
-
- LOG.info(_LI("Create volume of %s GB"), size, context=context)
-
- availability_zone = vol.get('availability_zone', None)
-
- try:
- new_volume = self.volume_api.create(
- context,
- size,
- vol.get('display_name'),
- vol.get('display_description'),
- snapshot=snapshot,
- volume_type=vol_type,
- metadata=metadata,
- availability_zone=availability_zone
- )
- except exception.InvalidInput as err:
- raise exc.HTTPBadRequest(explanation=err.format_message())
- except exception.OverQuota as err:
- raise exc.HTTPForbidden(explanation=err.format_message())
- # TODO(vish): Instance should be None at db layer instead of
- # trying to lazy load, but for now we turn it into
- # a dict to avoid an error.
- retval = _translate_volume_detail_view(context, dict(new_volume))
- result = {'volume': retval}
-
- location = '%s/%s' % (req.url, new_volume['id'])
-
- return wsgi.ResponseObject(result, headers=dict(location=location))
-
-
-def _translate_attachment_detail_view(volume_id, instance_uuid, mountpoint):
- """Maps keys for attachment details view."""
-
- d = _translate_attachment_summary_view(volume_id,
- instance_uuid,
- mountpoint)
-
- # No additional data / lookups at the moment
- return d
-
-
-def _translate_attachment_summary_view(volume_id, instance_uuid, mountpoint):
- """Maps keys for attachment summary view."""
- d = {}
-
- # NOTE(justinsb): We use the volume id as the id of the attachment object
- d['id'] = volume_id
-
- d['volumeId'] = volume_id
-
- d['serverId'] = instance_uuid
- if mountpoint:
- d['device'] = mountpoint
-
- return d
-
-
-class VolumeAttachmentController(wsgi.Controller):
- """The volume attachment API controller for the OpenStack API.
-
- A child resource of the server. Note that we use the volume id
- as the ID of the attachment (though this is not guaranteed externally)
-
- """
-
- def __init__(self, ext_mgr=None):
- self.compute_api = compute.API()
- self.volume_api = volume.API()
- self.ext_mgr = ext_mgr
- super(VolumeAttachmentController, self).__init__()
-
- def index(self, req, server_id):
- """Returns the list of volume attachments for a given instance."""
- context = req.environ['nova.context']
- authorize_attach(context, action='index')
- return self._items(req, server_id,
- entity_maker=_translate_attachment_summary_view)
-
- def show(self, req, server_id, id):
- """Return data about the given volume attachment."""
- context = req.environ['nova.context']
- authorize(context)
- authorize_attach(context, action='show')
-
- volume_id = id
- instance = common.get_instance(self.compute_api, context, server_id)
- bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
- context, instance.uuid)
-
- if not bdms:
- msg = _("Instance %s is not attached.") % server_id
- raise exc.HTTPNotFound(explanation=msg)
-
- assigned_mountpoint = None
-
- for bdm in bdms:
- if bdm.volume_id == volume_id:
- assigned_mountpoint = bdm.device_name
- break
-
- if assigned_mountpoint is None:
- msg = _("volume_id not found: %s") % volume_id
- raise exc.HTTPNotFound(explanation=msg)
-
- return {'volumeAttachment': _translate_attachment_detail_view(
- volume_id,
- instance.uuid,
- assigned_mountpoint)}
-
- def _validate_volume_id(self, volume_id):
- if not uuidutils.is_uuid_like(volume_id):
- msg = _("Bad volumeId format: volumeId is "
- "not in proper format (%s)") % volume_id
- raise exc.HTTPBadRequest(explanation=msg)
-
- def create(self, req, server_id, body):
- """Attach a volume to an instance."""
- context = req.environ['nova.context']
- authorize(context)
- authorize_attach(context, action='create')
-
- if not self.is_valid_body(body, 'volumeAttachment'):
- msg = _("volumeAttachment not specified")
- raise exc.HTTPBadRequest(explanation=msg)
- try:
- volume_id = body['volumeAttachment']['volumeId']
- except KeyError:
- msg = _("volumeId must be specified.")
- raise exc.HTTPBadRequest(explanation=msg)
- device = body['volumeAttachment'].get('device')
-
- self._validate_volume_id(volume_id)
-
- LOG.info(_LI("Attach volume %(volume_id)s to instance %(server_id)s "
- "at %(device)s"),
- {'volume_id': volume_id,
- 'device': device,
- 'server_id': server_id},
- context=context)
-
- instance = common.get_instance(self.compute_api, context, server_id)
- try:
- device = self.compute_api.attach_volume(context, instance,
- volume_id, device)
- except exception.NotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'attach_volume', server_id)
-
- # The attach is async
- attachment = {}
- attachment['id'] = volume_id
- attachment['serverId'] = server_id
- attachment['volumeId'] = volume_id
- attachment['device'] = device
-
- # NOTE(justinsb): And now, we have a problem...
- # The attach is async, so there's a window in which we don't see
- # the attachment (until the attachment completes). We could also
- # get problems with concurrent requests. I think we need an
- # attachment state, and to write to the DB here, but that's a bigger
- # change.
- # For now, we'll probably have to rely on libraries being smart
-
- # TODO(justinsb): How do I return "accepted" here?
- return {'volumeAttachment': attachment}
-
- def update(self, req, server_id, id, body):
- if (not self.ext_mgr or
- not self.ext_mgr.is_loaded('os-volume-attachment-update')):
- raise exc.HTTPBadRequest()
- context = req.environ['nova.context']
- authorize(context)
- authorize_attach(context, action='update')
-
- if not self.is_valid_body(body, 'volumeAttachment'):
- msg = _("volumeAttachment not specified")
- raise exc.HTTPBadRequest(explanation=msg)
-
- old_volume_id = id
- old_volume = self.volume_api.get(context, old_volume_id)
-
- try:
- new_volume_id = body['volumeAttachment']['volumeId']
- except KeyError:
- msg = _("volumeId must be specified.")
- raise exc.HTTPBadRequest(explanation=msg)
- self._validate_volume_id(new_volume_id)
- new_volume = self.volume_api.get(context, new_volume_id)
-
- instance = common.get_instance(self.compute_api, context, server_id)
-
- bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
- context, instance.uuid)
- found = False
- try:
- for bdm in bdms:
- if bdm.volume_id != old_volume_id:
- continue
- try:
- self.compute_api.swap_volume(context, instance, old_volume,
- new_volume)
- found = True
- break
- except exception.VolumeUnattached:
- # The volume is not attached. Treat it as NotFound
- # by falling through.
- pass
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'swap_volume', server_id)
-
- if not found:
- msg = _("volume_id not found: %s") % old_volume_id
- raise exc.HTTPNotFound(explanation=msg)
- else:
- return webob.Response(status_int=202)
-
- def delete(self, req, server_id, id):
- """Detach a volume from an instance."""
- context = req.environ['nova.context']
- authorize(context)
- authorize_attach(context, action='delete')
-
- volume_id = id
- LOG.info(_LI("Detach volume %s"), volume_id, context=context)
-
- instance = common.get_instance(self.compute_api, context, server_id)
-
- volume = self.volume_api.get(context, volume_id)
-
- bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
- context, instance.uuid)
- if not bdms:
- msg = _("Instance %s is not attached.") % server_id
- raise exc.HTTPNotFound(explanation=msg)
-
- found = False
- try:
- for bdm in bdms:
- if bdm.volume_id != volume_id:
- continue
- if bdm.is_root:
- msg = _("Can't detach root device volume")
- raise exc.HTTPForbidden(explanation=msg)
- try:
- self.compute_api.detach_volume(context, instance, volume)
- found = True
- break
- except exception.VolumeUnattached:
- # The volume is not attached. Treat it as NotFound
- # by falling through.
- pass
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'detach_volume', server_id)
-
- if not found:
- msg = _("volume_id not found: %s") % volume_id
- raise exc.HTTPNotFound(explanation=msg)
- else:
- return webob.Response(status_int=202)
-
- def _items(self, req, server_id, entity_maker):
- """Returns a list of attachments, transformed through entity_maker."""
- context = req.environ['nova.context']
- authorize(context)
-
- instance = common.get_instance(self.compute_api, context, server_id)
-
- bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
- context, instance.uuid)
- limited_list = common.limited(bdms, req)
- results = []
-
- for bdm in limited_list:
- if bdm.volume_id:
- results.append(entity_maker(bdm.volume_id,
- bdm.instance_uuid,
- bdm.device_name))
-
- return {'volumeAttachments': results}
-
-
-def _translate_snapshot_detail_view(context, vol):
- """Maps keys for snapshots details view."""
-
- d = _translate_snapshot_summary_view(context, vol)
-
- # NOTE(gagupta): No additional data / lookups at the moment
- return d
-
-
-def _translate_snapshot_summary_view(context, vol):
- """Maps keys for snapshots summary view."""
- d = {}
-
- d['id'] = vol['id']
- d['volumeId'] = vol['volume_id']
- d['status'] = vol['status']
- # NOTE(gagupta): We map volume_size as the snapshot size
- d['size'] = vol['volume_size']
- d['createdAt'] = vol['created_at']
- d['displayName'] = vol['display_name']
- d['displayDescription'] = vol['display_description']
- return d
-
-
-class SnapshotController(wsgi.Controller):
- """The Snapshots API controller for the OpenStack API."""
-
- def __init__(self):
- self.volume_api = volume.API()
- super(SnapshotController, self).__init__()
-
- def show(self, req, id):
- """Return data about the given snapshot."""
- context = req.environ['nova.context']
- authorize(context)
-
- try:
- vol = self.volume_api.get_snapshot(context, id)
- except exception.NotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
-
- return {'snapshot': _translate_snapshot_detail_view(context, vol)}
-
- def delete(self, req, id):
- """Delete a snapshot."""
- context = req.environ['nova.context']
- authorize(context)
-
- LOG.info(_LI("Delete snapshot with id: %s"), id, context=context)
-
- try:
- self.volume_api.delete_snapshot(context, id)
- except exception.NotFound as e:
- raise exc.HTTPNotFound(explanation=e.format_message())
- return webob.Response(status_int=202)
-
- def index(self, req):
- """Returns a summary list of snapshots."""
- return self._items(req, entity_maker=_translate_snapshot_summary_view)
-
- def detail(self, req):
- """Returns a detailed list of snapshots."""
- return self._items(req, entity_maker=_translate_snapshot_detail_view)
-
- def _items(self, req, entity_maker):
- """Returns a list of snapshots, transformed through entity_maker."""
- context = req.environ['nova.context']
- authorize(context)
-
- snapshots = self.volume_api.get_all_snapshots(context)
- limited_list = common.limited(snapshots, req)
- res = [entity_maker(context, snapshot) for snapshot in limited_list]
- return {'snapshots': res}
-
- def create(self, req, body):
- """Creates a new snapshot."""
- context = req.environ['nova.context']
- authorize(context)
-
- if not self.is_valid_body(body, 'snapshot'):
- msg = _("snapshot not specified")
- raise exc.HTTPBadRequest(explanation=msg)
-
- snapshot = body['snapshot']
- volume_id = snapshot['volume_id']
-
- LOG.info(_LI("Create snapshot from volume %s"), volume_id,
- context=context)
-
- force = snapshot.get('force', False)
- try:
- force = strutils.bool_from_string(force, strict=True)
- except ValueError:
- msg = _("Invalid value '%s' for force.") % force
- raise exc.HTTPBadRequest(explanation=msg)
-
- if force:
- create_func = self.volume_api.create_snapshot_force
- else:
- create_func = self.volume_api.create_snapshot
-
- new_snapshot = create_func(context, volume_id,
- snapshot.get('display_name'),
- snapshot.get('display_description'))
-
- retval = _translate_snapshot_detail_view(context, new_snapshot)
- return {'snapshot': retval}
-
-
-class Volumes(extensions.ExtensionDescriptor):
- """Volumes support."""
-
- name = "Volumes"
- alias = "os-volumes"
- namespace = "http://docs.openstack.org/compute/ext/volumes/api/v1.1"
- updated = "2011-03-25T00:00:00Z"
-
- def get_resources(self):
- resources = []
-
- # NOTE(justinsb): No way to provide singular name ('volume')
- # Does this matter?
- res = extensions.ResourceExtension('os-volumes',
- VolumeController(),
- collection_actions={'detail': 'GET'})
- resources.append(res)
-
- attachment_controller = VolumeAttachmentController(self.ext_mgr)
- res = extensions.ResourceExtension('os-volume_attachments',
- attachment_controller,
- parent=dict(
- member_name='server',
- collection_name='servers'))
- resources.append(res)
-
- res = extensions.ResourceExtension('os-volumes_boot',
- inherits='servers')
- resources.append(res)
-
- res = extensions.ResourceExtension('os-snapshots',
- SnapshotController(),
- collection_actions={'detail': 'GET'})
- resources.append(res)
-
- return resources
diff --git a/nova/api/openstack/compute/legacy_v2/extensions.py b/nova/api/openstack/compute/legacy_v2/extensions.py
deleted file mode 100644
index 68839cce91..0000000000
--- a/nova/api/openstack/compute/legacy_v2/extensions.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-# 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 oslo_log import log as logging
-
-from nova.api.openstack import extensions as base_extensions
-import nova.conf
-from nova.i18n import _LW
-
-STANDARD_EXTENSIONS = ('nova.api.openstack.compute.legacy_v2.contrib.' +
- 'standard_extensions')
-
-CONF = nova.conf.CONF
-LOG = logging.getLogger(__name__)
-
-
-class ExtensionManager(base_extensions.ExtensionManager):
- def __init__(self):
- self.cls_list = CONF.osapi_compute_extension
- if (len(self.cls_list) > 0 and
- self.cls_list[0] != STANDARD_EXTENSIONS):
- LOG.warning(_LW('The extension configure options are deprecated. '
- 'In the near future you must run all of the API.'))
- self.extensions = {}
- self.sorted_ext_list = []
- self._load_extensions()
diff --git a/nova/api/openstack/compute/legacy_v2/flavors.py b/nova/api/openstack/compute/legacy_v2/flavors.py
deleted file mode 100644
index ed47389f4d..0000000000
--- a/nova/api/openstack/compute/legacy_v2/flavors.py
+++ /dev/null
@@ -1,112 +0,0 @@
-# Copyright 2010 OpenStack Foundation
-# 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 oslo_utils import strutils
-import webob
-
-from nova.api.openstack import common
-from nova.api.openstack.compute.views import flavors as flavors_view
-from nova.api.openstack import wsgi
-from nova.compute import flavors
-from nova import exception
-from nova.i18n import _
-from nova import utils
-
-
-class Controller(wsgi.Controller):
- """Flavor controller for the OpenStack API."""
-
- _view_builder_class = flavors_view.ViewBuilder
-
- def index(self, req):
- """Return all flavors in brief."""
- limited_flavors = self._get_flavors(req)
- return self._view_builder.index(req, limited_flavors)
-
- def detail(self, req):
- """Return all flavors in detail."""
- limited_flavors = self._get_flavors(req)
- req.cache_db_flavors(limited_flavors)
- return self._view_builder.detail(req, limited_flavors)
-
- def show(self, req, id):
- """Return data about the given flavor id."""
- try:
- context = req.environ['nova.context']
- flavor = flavors.get_flavor_by_flavor_id(id, ctxt=context)
- req.cache_db_flavor(flavor)
- except exception.NotFound:
- raise webob.exc.HTTPNotFound()
-
- return self._view_builder.show(req, flavor)
-
- def _parse_is_public(self, is_public):
- """Parse is_public into something usable."""
-
- if is_public is None:
- # preserve default value of showing only public flavors
- return True
- elif utils.is_none_string(is_public):
- return None
- else:
- try:
- return strutils.bool_from_string(is_public, strict=True)
- except ValueError:
- msg = _('Invalid is_public filter [%s]') % is_public
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- def _get_flavors(self, req):
- """Helper function that returns a list of flavor dicts."""
- filters = {}
- sort_key = req.params.get('sort_key') or 'flavorid'
- sort_dir = req.params.get('sort_dir') or 'asc'
- limit, marker = common.get_limit_and_marker(req)
-
- context = req.environ['nova.context']
- if context.is_admin:
- # Only admin has query access to all flavor types
- filters['is_public'] = self._parse_is_public(
- req.params.get('is_public', None))
- else:
- filters['is_public'] = True
- filters['disabled'] = False
-
- if 'minRam' in req.params:
- try:
- filters['min_memory_mb'] = int(req.params['minRam'])
- except ValueError:
- msg = _('Invalid minRam filter [%s]') % req.params['minRam']
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- if 'minDisk' in req.params:
- try:
- filters['min_root_gb'] = int(req.params['minDisk'])
- except ValueError:
- msg = _('Invalid minDisk filter [%s]') % req.params['minDisk']
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- try:
- limited_flavors = flavors.get_all_flavors_sorted_list(context,
- filters=filters, sort_key=sort_key, sort_dir=sort_dir,
- limit=limit, marker=marker)
- except exception.MarkerNotFound:
- msg = _('marker [%s] not found') % marker
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- return limited_flavors
-
-
-def create_resource():
- return wsgi.Resource(Controller())
diff --git a/nova/api/openstack/compute/legacy_v2/image_metadata.py b/nova/api/openstack/compute/legacy_v2/image_metadata.py
deleted file mode 100644
index 77fdd59c69..0000000000
--- a/nova/api/openstack/compute/legacy_v2/image_metadata.py
+++ /dev/null
@@ -1,126 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-# 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.
-
-import six
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack import wsgi
-from nova import exception
-from nova.i18n import _
-import nova.image
-
-
-class Controller(object):
- """The image metadata API controller for the OpenStack API."""
-
- def __init__(self):
- self.image_api = nova.image.API()
-
- def _get_image(self, context, image_id):
- try:
- return self.image_api.get(context, image_id)
- except exception.ImageNotAuthorized as e:
- raise exc.HTTPForbidden(explanation=e.format_message())
- except exception.ImageNotFound:
- msg = _("Image not found.")
- raise exc.HTTPNotFound(explanation=msg)
-
- def index(self, req, image_id):
- """Returns the list of metadata for a given instance."""
- context = req.environ['nova.context']
- metadata = self._get_image(context, image_id)['properties']
- return dict(metadata=metadata)
-
- def show(self, req, image_id, id):
- context = req.environ['nova.context']
- metadata = self._get_image(context, image_id)['properties']
- if id in metadata:
- return {'meta': {id: metadata[id]}}
- else:
- raise exc.HTTPNotFound()
-
- def create(self, req, image_id, body):
- context = req.environ['nova.context']
- image = self._get_image(context, image_id)
- if 'metadata' in body:
- for key, value in six.iteritems(body['metadata']):
- image['properties'][key] = value
- common.check_img_metadata_properties_quota(context,
- image['properties'])
- try:
- image = self.image_api.update(context, image_id, image, data=None,
- purge_props=True)
- except exception.ImageNotAuthorized as e:
- raise exc.HTTPForbidden(explanation=e.format_message())
- return dict(metadata=image['properties'])
-
- def update(self, req, image_id, id, body):
- context = req.environ['nova.context']
-
- try:
- meta = body['meta']
- except KeyError:
- expl = _('Incorrect request body format')
- raise exc.HTTPBadRequest(explanation=expl)
-
- if id not in meta:
- expl = _('Request body and URI mismatch')
- raise exc.HTTPBadRequest(explanation=expl)
- if len(meta) > 1:
- expl = _('Request body contains too many items')
- raise exc.HTTPBadRequest(explanation=expl)
-
- image = self._get_image(context, image_id)
- image['properties'][id] = meta[id]
- common.check_img_metadata_properties_quota(context,
- image['properties'])
- try:
- self.image_api.update(context, image_id, image, data=None,
- purge_props=True)
- except exception.ImageNotAuthorized as e:
- raise exc.HTTPForbidden(explanation=e.format_message())
- return dict(meta=meta)
-
- def update_all(self, req, image_id, body):
- context = req.environ['nova.context']
- image = self._get_image(context, image_id)
- metadata = body.get('metadata', {})
- common.check_img_metadata_properties_quota(context, metadata)
- image['properties'] = metadata
- try:
- self.image_api.update(context, image_id, image, data=None,
- purge_props=True)
- except exception.ImageNotAuthorized as e:
- raise exc.HTTPForbidden(explanation=e.format_message())
- return dict(metadata=metadata)
-
- @wsgi.response(204)
- def delete(self, req, image_id, id):
- context = req.environ['nova.context']
- image = self._get_image(context, image_id)
- if id not in image['properties']:
- msg = _("Invalid metadata key")
- raise exc.HTTPNotFound(explanation=msg)
- image['properties'].pop(id)
- try:
- self.image_api.update(context, image_id, image, data=None,
- purge_props=True)
- except exception.ImageNotAuthorized as e:
- raise exc.HTTPForbidden(explanation=e.format_message())
-
-
-def create_resource():
- return wsgi.Resource(Controller())
diff --git a/nova/api/openstack/compute/legacy_v2/images.py b/nova/api/openstack/compute/legacy_v2/images.py
deleted file mode 100644
index c2172c19b0..0000000000
--- a/nova/api/openstack/compute/legacy_v2/images.py
+++ /dev/null
@@ -1,150 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-# 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.
-
-import webob.exc
-
-from nova.api.openstack import common
-from nova.api.openstack.compute.views import images as views_images
-from nova.api.openstack import wsgi
-from nova import exception
-from nova.i18n import _
-import nova.image
-import nova.utils
-
-
-SUPPORTED_FILTERS = {
- 'name': 'name',
- 'status': 'status',
- 'changes-since': 'changes-since',
- 'server': 'property-instance_uuid',
- 'type': 'property-image_type',
- 'minRam': 'min_ram',
- 'minDisk': 'min_disk',
-}
-
-
-class Controller(wsgi.Controller):
- """Base controller for retrieving/displaying images."""
-
- _view_builder_class = views_images.ViewBuilder
-
- def __init__(self, **kwargs):
- super(Controller, self).__init__(**kwargs)
- self._image_api = nova.image.API()
-
- def _get_filters(self, req):
- """Return a dictionary of query param filters from the request.
-
- :param req: the Request object coming from the wsgi layer
- :retval a dict of key/value filters
- """
- filters = {}
- for param in req.params:
- if param in SUPPORTED_FILTERS or param.startswith('property-'):
- # map filter name or carry through if property-*
- filter_name = SUPPORTED_FILTERS.get(param, param)
- filters[filter_name] = req.params.get(param)
-
- # ensure server filter is the instance uuid
- filter_name = 'property-instance_uuid'
- try:
- filters[filter_name] = filters[filter_name].rsplit('/', 1)[1]
- except (AttributeError, IndexError, KeyError):
- pass
-
- filter_name = 'status'
- if filter_name in filters:
- # The Image API expects us to use lowercase strings for status
- filters[filter_name] = filters[filter_name].lower()
-
- return filters
-
- def show(self, req, id):
- """Return detailed information about a specific image.
-
- :param req: `wsgi.Request` object
- :param id: Image identifier
- """
- context = req.environ['nova.context']
-
- try:
- image = self._image_api.get(context, id)
- except (exception.NotFound, exception.InvalidImageRef):
- explanation = _("Image not found.")
- raise webob.exc.HTTPNotFound(explanation=explanation)
-
- req.cache_db_items('images', [image], 'id')
- return self._view_builder.show(req, image)
-
- def delete(self, req, id):
- """Delete an image, if allowed.
-
- :param req: `wsgi.Request` object
- :param id: Image identifier (integer)
- """
- context = req.environ['nova.context']
- try:
- self._image_api.delete(context, id)
- except exception.ImageNotFound:
- explanation = _("Image not found.")
- raise webob.exc.HTTPNotFound(explanation=explanation)
- except exception.ImageNotAuthorized:
- # The image service raises this exception on delete if glanceclient
- # raises HTTPForbidden.
- explanation = _("You are not allowed to delete the image.")
- raise webob.exc.HTTPForbidden(explanation=explanation)
- return webob.exc.HTTPNoContent()
-
- def index(self, req):
- """Return an index listing of images available to the request.
-
- :param req: `wsgi.Request` object
-
- """
- context = req.environ['nova.context']
- filters = self._get_filters(req)
- page_params = common.get_pagination_params(req)
-
- try:
- images = self._image_api.get_all(context, filters=filters,
- **page_params)
- except exception.Invalid as e:
- raise webob.exc.HTTPBadRequest(explanation=e.format_message())
- return self._view_builder.index(req, images)
-
- def detail(self, req):
- """Return a detailed index listing of images available to the request.
-
- :param req: `wsgi.Request` object.
-
- """
- context = req.environ['nova.context']
- filters = self._get_filters(req)
- page_params = common.get_pagination_params(req)
- try:
- images = self._image_api.get_all(context, filters=filters,
- **page_params)
- except exception.Invalid as e:
- raise webob.exc.HTTPBadRequest(explanation=e.format_message())
-
- req.cache_db_items('images', images, 'id')
- return self._view_builder.detail(req, images)
-
- def create(self, *args, **kwargs):
- raise webob.exc.HTTPMethodNotAllowed()
-
-
-def create_resource():
- return wsgi.Resource(Controller())
diff --git a/nova/api/openstack/compute/legacy_v2/ips.py b/nova/api/openstack/compute/legacy_v2/ips.py
deleted file mode 100644
index 328c7c1172..0000000000
--- a/nova/api/openstack/compute/legacy_v2/ips.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-# 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 webob import exc
-
-import nova
-from nova.api.openstack import common
-from nova.api.openstack.compute.views import addresses as view_addresses
-from nova.api.openstack import wsgi
-from nova.i18n import _
-
-
-class Controller(wsgi.Controller):
- """The servers addresses API controller for the OpenStack API."""
-
- _view_builder_class = view_addresses.ViewBuilder
-
- def __init__(self, **kwargs):
- super(Controller, self).__init__(**kwargs)
- self._compute_api = nova.compute.API()
-
- def index(self, req, server_id):
- context = req.environ["nova.context"]
- instance = common.get_instance(self._compute_api, context, server_id)
- networks = common.get_networks_for_instance(context, instance)
- return self._view_builder.index(networks)
-
- def show(self, req, server_id, id):
- context = req.environ["nova.context"]
- instance = common.get_instance(self._compute_api, context, server_id)
- networks = common.get_networks_for_instance(context, instance)
- if id not in networks:
- msg = _("Instance is not a member of specified network")
- raise exc.HTTPNotFound(explanation=msg)
-
- return self._view_builder.show(networks[id], id)
-
-
-def create_resource():
- return wsgi.Resource(Controller())
diff --git a/nova/api/openstack/compute/legacy_v2/limits.py b/nova/api/openstack/compute/legacy_v2/limits.py
deleted file mode 100644
index 1236badde3..0000000000
--- a/nova/api/openstack/compute/legacy_v2/limits.py
+++ /dev/null
@@ -1,393 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-# 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.
-
-"""
-Module dedicated functions/classes dealing with rate limiting requests.
-
-This module handles rate liming at a per-user level, so it should not be used
-to prevent intentional Denial of Service attacks, as we can assume a DOS can
-easily come through multiple user accounts. DOS protection should be done at a
-different layer. Instead this module should be used to protect against
-unintentional user actions. With that in mind the limits set here should be
-high enough as to not rate-limit any intentional actions.
-
-To find good rate-limit values, check how long requests are taking (see logs)
-in your environment to assess your capabilities and multiply out to get
-figures.
-
-NOTE: As the rate-limiting here is done in memory, this only works per
-process (each process will have its own rate limiting counter).
-"""
-
-import collections
-import copy
-import math
-import re
-import time
-
-from oslo_serialization import jsonutils
-from six.moves import http_client as httplib
-import webob.dec
-import webob.exc
-
-from nova.api.openstack.compute.views import limits as limits_views
-from nova.api.openstack import wsgi
-from nova.i18n import _
-from nova import quota
-from nova import utils
-from nova import wsgi as base_wsgi
-
-
-QUOTAS = quota.QUOTAS
-LIMITS_PREFIX = "limits."
-
-
-class LimitsController(object):
- """Controller for accessing limits in the OpenStack API."""
-
- def index(self, req):
- """Return all global and rate limit information."""
- context = req.environ['nova.context']
- project_id = req.params.get('tenant_id', context.project_id)
- quotas = QUOTAS.get_project_quotas(context, project_id,
- usages=False)
- abs_limits = {k: v['limit'] for k, v in quotas.items()}
- rate_limits = req.environ.get("nova.limits", [])
-
- builder = self._get_view_builder(req)
- return builder.build(rate_limits, abs_limits)
-
- def create(self, req, body):
- """Create a new limit."""
- raise webob.exc.HTTPNotImplemented()
-
- def delete(self, req, id):
- """Delete the limit."""
- raise webob.exc.HTTPNotImplemented()
-
- def show(self, req, id):
- """Show limit information."""
- raise webob.exc.HTTPNotImplemented()
-
- def update(self, req, id, body):
- """Update existing limit."""
- raise webob.exc.HTTPNotImplemented()
-
- def _get_view_builder(self, req):
- return limits_views.ViewBuilder()
-
-
-def create_resource():
- return wsgi.Resource(LimitsController())
-
-
-class Limit(object):
- """Stores information about a limit for HTTP requests."""
-
- UNITS = {v: k for k, v in utils.TIME_UNITS.items()}
-
- def __init__(self, verb, uri, regex, value, unit):
- """Initialize a new `Limit`.
-
- @param verb: HTTP verb (POST, PUT, etc.)
- @param uri: Human-readable URI
- @param regex: Regular expression format for this limit
- @param value: Integer number of requests which can be made
- @param unit: Unit of measure for the value parameter
- """
- self.verb = verb
- self.uri = uri
- self.regex = regex
- self.value = int(value)
- self.unit = unit
- self.unit_string = self.display_unit().lower()
- self.remaining = int(value)
-
- if value <= 0:
- raise ValueError("Limit value must be > 0")
-
- self.last_request = None
- self.next_request = None
-
- self.water_level = 0
- self.capacity = self.unit
- self.request_value = float(self.capacity) / float(self.value)
- msg = (_("Only %(value)s %(verb)s request(s) can be "
- "made to %(uri)s every %(unit_string)s.") %
- {'value': self.value, 'verb': self.verb, 'uri': self.uri,
- 'unit_string': self.unit_string})
- self.error_message = msg
-
- def __call__(self, verb, url):
- """Represents a call to this limit from a relevant request.
-
- @param verb: string http verb (POST, GET, etc.)
- @param url: string URL
- """
- if self.verb != verb or not re.match(self.regex, url):
- return
-
- now = self._get_time()
-
- if self.last_request is None:
- self.last_request = now
-
- leak_value = now - self.last_request
-
- self.water_level -= leak_value
- self.water_level = max(self.water_level, 0)
- self.water_level += self.request_value
-
- difference = self.water_level - self.capacity
-
- self.last_request = now
-
- if difference > 0:
- self.water_level -= self.request_value
- self.next_request = now + difference
- return difference
-
- cap = self.capacity
- water = self.water_level
- val = self.value
-
- self.remaining = math.floor(((cap - water) / cap) * val)
- self.next_request = now
-
- def _get_time(self):
- """Retrieve the current time. Broken out for testability."""
- return time.time()
-
- def display_unit(self):
- """Display the string name of the unit."""
- return self.UNITS.get(self.unit, "UNKNOWN")
-
- def display(self):
- """Return a useful representation of this class."""
- return {
- "verb": self.verb,
- "URI": self.uri,
- "regex": self.regex,
- "value": self.value,
- "remaining": int(self.remaining),
- "unit": self.display_unit(),
- "resetTime": int(self.next_request or self._get_time()),
- }
-
-# "Limit" format is a dictionary with the HTTP verb, human-readable URI,
-# a regular-expression to match, value and unit of measure (PER_DAY, etc.)
-
-DEFAULT_LIMITS = [
- Limit("POST", "*", ".*", 120, utils.TIME_UNITS['MINUTE']),
- Limit("POST", "*/servers", "^/servers", 120, utils.TIME_UNITS['MINUTE']),
- Limit("PUT", "*", ".*", 120, utils.TIME_UNITS['MINUTE']),
- Limit("GET", "*changes-since*", ".*changes-since.*", 120,
- utils.TIME_UNITS['MINUTE']),
- Limit("DELETE", "*", ".*", 120, utils.TIME_UNITS['MINUTE']),
- Limit("GET", "*/os-fping", "^/os-fping", 12, utils.TIME_UNITS['MINUTE']),
-]
-
-
-class RateLimitingMiddleware(base_wsgi.Middleware):
- """Rate-limits requests passing through this middleware. All limit
- information is stored in memory for this implementation.
- """
-
- def __init__(self, application, **kwargs):
- """Initialize new `RateLimitingMiddleware`.
-
- It wraps the given WSGI application and sets up the given limits.
-
- @param application: WSGI application to wrap
-
- Other parameters are passed to the constructor for the limiter.
- """
- base_wsgi.Middleware.__init__(self, application)
- limiter = Limiter
-
- self._limiter = limiter(DEFAULT_LIMITS, **kwargs)
-
- @webob.dec.wsgify(RequestClass=wsgi.Request)
- def __call__(self, req):
- """Represents a single call through this middleware.
-
- We should record the request if we have a limit relevant to it.
- If no limit is relevant to the request, ignore it.
-
- If the request should be rate limited, return a fault telling the user
- they are over the limit and need to retry later.
- """
- verb = req.method
- url = req.url
- context = req.environ.get("nova.context")
-
- if context:
- username = context.user_id
- else:
- username = None
-
- delay, error = self._limiter.check_for_delay(verb, url, username)
-
- if delay:
- msg = _("This request was rate-limited.")
- retry = time.time() + delay
- return wsgi.RateLimitFault(msg, error, retry)
-
- req.environ["nova.limits"] = self._limiter.get_limits(username)
-
- return self.application
-
-
-class Limiter(object):
- """Rate-limit checking class which handles limits in memory."""
-
- def __init__(self, limits, **kwargs):
- """Initialize the new `Limiter`.
-
- @param limits: List of `Limit` objects
- """
- self.limits = copy.deepcopy(limits)
- self.levels = collections.defaultdict(lambda: copy.deepcopy(limits))
-
- # Pick up any per-user limit information
- for key, value in kwargs.items():
- if key.startswith(LIMITS_PREFIX):
- username = key[len(LIMITS_PREFIX):]
- self.levels[username] = self.parse_limits(value)
-
- def get_limits(self, username=None):
- """Return the limits for a given user."""
- return [limit.display() for limit in self.levels[username]]
-
- def check_for_delay(self, verb, url, username=None):
- """Check the given verb/user/user triplet for limit.
-
- @return: Tuple of delay (in seconds) and error message (or None, None)
- """
- delays = []
-
- for limit in self.levels[username]:
- delay = limit(verb, url)
- if delay:
- delays.append((delay, limit.error_message))
-
- if delays:
- delays.sort()
- return delays[0]
-
- return None, None
-
- # Note: This method gets called before the class is instantiated,
- # so this must be either a static method or a class method. It is
- # used to develop a list of limits to feed to the constructor. We
- # put this in the class so that subclasses can override the
- # default limit parsing.
- @staticmethod
- def parse_limits(limits):
- """Convert a string into a list of Limit instances. This
- implementation expects a semicolon-separated sequence of
- parenthesized groups, where each group contains a
- comma-separated sequence consisting of HTTP method,
- user-readable URI, a URI reg-exp, an integer number of
- requests which can be made, and a unit of measure. Valid
- values for the latter are "SECOND", "MINUTE", "HOUR", and
- "DAY".
-
- @return: List of Limit instances.
- """
-
- # Handle empty limit strings
- limits = limits.strip()
- if not limits:
- return []
-
- # Split up the limits by semicolon
- result = []
- for group in limits.split(';'):
- group = group.strip()
- if not group.startswith('(') or not group.endswith(')'):
- raise ValueError("Limit rules must be surrounded by "
- "parentheses")
- group = group[1:-1]
-
- # Extract the Limit arguments
- args = [a.strip() for a in group.split(',')]
- if len(args) != 5:
- raise ValueError("Limit rules must contain the following "
- "arguments: verb, uri, regex, value, unit")
-
- # Pull out the arguments
- verb, uri, regex, value, unit = args
-
- # Upper-case the verb
- verb = verb.upper()
-
- # Convert value--raises ValueError if it's not integer
- value = int(value)
-
- # Convert unit
- unit = unit.upper()
- if unit not in utils.TIME_UNITS:
- raise ValueError("Invalid units specified")
- unit = utils.TIME_UNITS[unit]
-
- # Build a limit
- result.append(Limit(verb, uri, regex, value, unit))
-
- return result
-
-
-class WsgiLimiterProxy(object):
- """Rate-limit requests based on answers from a remote source."""
-
- def __init__(self, limiter_address):
- """Initialize the new `WsgiLimiterProxy`.
-
- @param limiter_address: IP/port combination of where to request limit
- """
- self.limiter_address = limiter_address
-
- def check_for_delay(self, verb, path, username=None):
- body = jsonutils.dumps({"verb": verb, "path": path})
- headers = {"Content-Type": "application/json"}
-
- conn = httplib.HTTPConnection(self.limiter_address)
-
- if username:
- conn.request("POST", "/%s" % (username), body, headers)
- else:
- conn.request("POST", "/", body, headers)
-
- resp = conn.getresponse()
-
- if 200 >= resp.status < 300:
- return None, None
-
- return resp.getheader("X-Wait-Seconds"), resp.read() or None
-
- # Note: This method gets called before the class is instantiated,
- # so this must be either a static method or a class method. It is
- # used to develop a list of limits to feed to the constructor.
- # This implementation returns an empty list, since all limit
- # decisions are made by a remote server.
- @staticmethod
- def parse_limits(limits):
- """Ignore a limits string--simply doesn't apply for the limit
- proxy.
-
- @return: Empty list.
- """
-
- return []
diff --git a/nova/api/openstack/compute/legacy_v2/server_metadata.py b/nova/api/openstack/compute/legacy_v2/server_metadata.py
deleted file mode 100644
index a23dc9609c..0000000000
--- a/nova/api/openstack/compute/legacy_v2/server_metadata.py
+++ /dev/null
@@ -1,189 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-# 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.
-
-import six
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack import wsgi
-from nova import compute
-from nova import exception
-from nova.i18n import _
-
-
-class Controller(object):
- """The server metadata API controller for the OpenStack API."""
-
- def __init__(self):
- self.compute_api = compute.API()
- super(Controller, self).__init__()
-
- def _get_metadata(self, context, server_id):
- try:
- server = common.get_instance(self.compute_api, context, server_id)
- meta = self.compute_api.get_instance_metadata(context, server)
- except exception.InstanceNotFound:
- msg = _('Server does not exist')
- raise exc.HTTPNotFound(explanation=msg)
-
- meta_dict = {}
- for key, value in six.iteritems(meta):
- meta_dict[key] = value
- return meta_dict
-
- def index(self, req, server_id):
- """Returns the list of metadata for a given instance."""
- context = req.environ['nova.context']
- return {'metadata': self._get_metadata(context, server_id)}
-
- def create(self, req, server_id, body):
- try:
- metadata = body['metadata']
- except (KeyError, TypeError):
- msg = _("Malformed request body")
- raise exc.HTTPBadRequest(explanation=msg)
- if not isinstance(metadata, dict):
- msg = _("Malformed request body. metadata must be object")
- raise exc.HTTPBadRequest(explanation=msg)
-
- context = req.environ['nova.context']
-
- new_metadata = self._update_instance_metadata(context,
- server_id,
- metadata,
- delete=False)
-
- return {'metadata': new_metadata}
-
- def update(self, req, server_id, id, body):
- try:
- meta_item = body['meta']
- except (TypeError, KeyError):
- expl = _('Malformed request body')
- raise exc.HTTPBadRequest(explanation=expl)
-
- if not isinstance(meta_item, dict):
- msg = _("Malformed request body. meta item must be object")
- raise exc.HTTPBadRequest(explanation=msg)
-
- if id not in meta_item:
- expl = _('Request body and URI mismatch')
- raise exc.HTTPBadRequest(explanation=expl)
-
- if len(meta_item) > 1:
- expl = _('Request body contains too many items')
- raise exc.HTTPBadRequest(explanation=expl)
-
- context = req.environ['nova.context']
- self._update_instance_metadata(context,
- server_id,
- meta_item,
- delete=False)
-
- return {'meta': meta_item}
-
- def update_all(self, req, server_id, body):
- try:
- metadata = body['metadata']
- except (TypeError, KeyError):
- expl = _('Malformed request body')
- raise exc.HTTPBadRequest(explanation=expl)
-
- if not isinstance(metadata, dict):
- msg = _("Malformed request body. metadata must be object")
- raise exc.HTTPBadRequest(explanation=msg)
-
- context = req.environ['nova.context']
- new_metadata = self._update_instance_metadata(context,
- server_id,
- metadata,
- delete=True)
-
- return {'metadata': new_metadata}
-
- def _update_instance_metadata(self, context, server_id, metadata,
- delete=False):
- try:
- server = common.get_instance(self.compute_api, context, server_id)
- return self.compute_api.update_instance_metadata(context,
- server,
- metadata,
- delete)
-
- except exception.InstanceNotFound:
- msg = _('Server does not exist')
- raise exc.HTTPNotFound(explanation=msg)
-
- except (ValueError, AttributeError):
- msg = _("Malformed request body")
- raise exc.HTTPBadRequest(explanation=msg)
-
- except exception.InvalidMetadata as error:
- raise exc.HTTPBadRequest(explanation=error.format_message())
-
- except exception.InvalidMetadataSize as error:
- raise exc.HTTPRequestEntityTooLarge(
- explanation=error.format_message())
-
- except exception.QuotaError as error:
- raise exc.HTTPForbidden(explanation=error.format_message())
-
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
-
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'update metadata', server_id)
-
- def show(self, req, server_id, id):
- """Return a single metadata item."""
- context = req.environ['nova.context']
- data = self._get_metadata(context, server_id)
-
- try:
- return {'meta': {id: data[id]}}
- except KeyError:
- msg = _("Metadata item was not found")
- raise exc.HTTPNotFound(explanation=msg)
-
- @wsgi.response(204)
- def delete(self, req, server_id, id):
- """Deletes an existing metadata."""
- context = req.environ['nova.context']
-
- metadata = self._get_metadata(context, server_id)
-
- if id not in metadata:
- msg = _("Metadata item was not found")
- raise exc.HTTPNotFound(explanation=msg)
-
- server = common.get_instance(self.compute_api, context, server_id)
- try:
- self.compute_api.delete_instance_metadata(context, server, id)
-
- except exception.InstanceNotFound:
- msg = _('Server does not exist')
- raise exc.HTTPNotFound(explanation=msg)
-
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
-
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'delete metadata', server_id)
-
-
-def create_resource():
- return wsgi.Resource(Controller())
diff --git a/nova/api/openstack/compute/legacy_v2/servers.py b/nova/api/openstack/compute/legacy_v2/servers.py
deleted file mode 100644
index a81afa665b..0000000000
--- a/nova/api/openstack/compute/legacy_v2/servers.py
+++ /dev/null
@@ -1,1146 +0,0 @@
-# Copyright 2010 OpenStack Foundation
-# Copyright 2011 Piston Cloud Computing, Inc
-# 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.
-
-import base64
-import re
-import sys
-
-from oslo_log import log as logging
-import oslo_messaging as messaging
-from oslo_utils import netutils
-from oslo_utils import strutils
-from oslo_utils import timeutils
-from oslo_utils import uuidutils
-import six
-import webob
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack.compute.views import servers as views_servers
-from nova.api.openstack import wsgi
-from nova import block_device
-from nova import compute
-from nova.compute import flavors
-from nova.compute import utils as compute_utils
-import nova.conf
-from nova import exception
-from nova.i18n import _
-from nova import objects
-from nova import policy
-from nova import utils
-
-
-CONF = nova.conf.CONF
-
-LOG = logging.getLogger(__name__)
-
-CREATE_EXCEPTIONS = {
- exception.InvalidMetadataSize: exc.HTTPRequestEntityTooLarge,
- exception.ImageNotFound: exc.HTTPBadRequest,
- exception.FlavorNotFound: exc.HTTPBadRequest,
- exception.KeypairNotFound: exc.HTTPBadRequest,
- exception.ConfigDriveInvalidValue: exc.HTTPBadRequest,
- exception.ImageNotActive: exc.HTTPBadRequest,
- exception.FlavorDiskTooSmall: exc.HTTPBadRequest,
- exception.FlavorMemoryTooSmall: exc.HTTPBadRequest,
- exception.NetworkNotFound: exc.HTTPBadRequest,
- exception.PortNotFound: exc.HTTPBadRequest,
- exception.FixedIpAlreadyInUse: exc.HTTPBadRequest,
- exception.SecurityGroupNotFound: exc.HTTPBadRequest,
- exception.InstanceUserDataTooLarge: exc.HTTPBadRequest,
- exception.InstanceUserDataMalformed: exc.HTTPBadRequest,
- exception.ImageNUMATopologyIncomplete: exc.HTTPBadRequest,
- exception.ImageNUMATopologyForbidden: exc.HTTPBadRequest,
- exception.ImageNUMATopologyAsymmetric: exc.HTTPBadRequest,
- exception.ImageNUMATopologyCPUOutOfRange: exc.HTTPBadRequest,
- exception.ImageNUMATopologyCPUDuplicates: exc.HTTPBadRequest,
- exception.ImageNUMATopologyCPUsUnassigned: exc.HTTPBadRequest,
- exception.ImageNUMATopologyMemoryOutOfRange: exc.HTTPBadRequest,
- exception.PortInUse: exc.HTTPConflict,
- exception.InstanceExists: exc.HTTPConflict,
- exception.NoUniqueMatch: exc.HTTPConflict,
- exception.Invalid: exc.HTTPBadRequest,
- exception.InstanceGroupNotFound: exc.HTTPBadRequest,
-}
-
-CREATE_EXCEPTIONS_MSGS = {
- exception.ImageNotFound: _("Can not find requested image"),
- exception.FlavorNotFound: _("Invalid flavorRef provided."),
- exception.KeypairNotFound: _("Invalid key_name provided."),
- exception.ConfigDriveInvalidValue: _("Invalid config_drive provided."),
-}
-
-
-class Controller(wsgi.Controller):
- """The Server API base controller class for the OpenStack API."""
-
- _view_builder_class = views_servers.ViewBuilder
-
- @staticmethod
- def _add_location(robj):
- # Just in case...
- if 'server' not in robj.obj:
- return robj
-
- link = [l for l in robj.obj['server']['links'] if l['rel'] == 'self']
- if link:
- robj['Location'] = utils.utf8(link[0]['href'])
-
- # Convenience return
- return robj
-
- def __init__(self, ext_mgr=None, **kwargs):
- super(Controller, self).__init__(**kwargs)
- self.compute_api = compute.API()
- self.ext_mgr = ext_mgr
-
- def index(self, req):
- """Returns a list of server names and ids for a given user."""
- try:
- servers = self._get_servers(req, is_detail=False)
- except exception.Invalid as err:
- raise exc.HTTPBadRequest(explanation=err.format_message())
- return servers
-
- def detail(self, req):
- """Returns a list of server details for a given user."""
- try:
- servers = self._get_servers(req, is_detail=True)
- except exception.Invalid as err:
- raise exc.HTTPBadRequest(explanation=err.format_message())
- return servers
-
- def _get_servers(self, req, is_detail):
- """Returns a list of servers, based on any search options specified."""
-
- search_opts = {}
- search_opts.update(req.GET)
-
- context = req.environ['nova.context']
- remove_invalid_options(context, search_opts,
- self._get_server_search_options())
-
- # Verify search by 'status' contains a valid status.
- # Convert it to filter by vm_state or task_state for compute_api.
- search_opts.pop('status', None)
- if 'status' in req.GET.keys():
- statuses = req.GET.getall('status')
- states = common.task_and_vm_state_from_status(statuses)
- vm_state, task_state = states
- if not vm_state and not task_state:
- return {'servers': []}
- search_opts['vm_state'] = vm_state
- # When we search by vm state, task state will return 'default'.
- # So we don't need task_state search_opt.
- if 'default' not in task_state:
- search_opts['task_state'] = task_state
-
- if 'changes-since' in search_opts:
- try:
- parsed = timeutils.parse_isotime(search_opts['changes-since'])
- except ValueError:
- msg = _('Invalid changes-since value')
- raise exc.HTTPBadRequest(explanation=msg)
- search_opts['changes-since'] = parsed
-
- # By default, compute's get_all() will return deleted instances.
- # If an admin hasn't specified a 'deleted' search option, we need
- # to filter out deleted instances by setting the filter ourselves.
- # ... Unless 'changes-since' is specified, because 'changes-since'
- # should return recently deleted images according to the API spec.
-
- if 'deleted' not in search_opts:
- if 'changes-since' not in search_opts:
- # No 'changes-since', so we only want non-deleted servers
- search_opts['deleted'] = False
- else:
- # Convert deleted filter value to a valid boolean.
- # Return non-deleted servers if an invalid value
- # is passed with deleted filter.
- search_opts['deleted'] = strutils.bool_from_string(
- search_opts['deleted'], default=False)
-
- if search_opts.get("vm_state") == ['deleted']:
- if context.is_admin:
- search_opts['deleted'] = True
- else:
- msg = _("Only administrators may list deleted instances")
- raise exc.HTTPForbidden(explanation=msg)
-
- all_tenants = common.is_all_tenants(search_opts)
- # use the boolean from here on out so remove the entry from search_opts
- # if it's present
- search_opts.pop('all_tenants', None)
-
- elevated = None
- if all_tenants:
- policy.enforce(context, 'compute:get_all_tenants',
- {'project_id': context.project_id,
- 'user_id': context.user_id})
- elevated = context.elevated()
- else:
- if context.project_id:
- search_opts['project_id'] = context.project_id
- else:
- search_opts['user_id'] = context.user_id
-
- limit, marker = common.get_limit_and_marker(req)
- # Sorting by multiple keys and directions is conditionally enabled
- sort_keys, sort_dirs = None, None
- if self.ext_mgr.is_loaded('os-server-sort-keys'):
- sort_keys, sort_dirs = common.get_sort_params(req.params)
-
- expected_attrs = None
- if is_detail:
- # merge our expected attrs with what the view builder needs for
- # showing details
- expected_attrs = self._view_builder.get_show_expected_attrs(
- expected_attrs)
-
- try:
- instance_list = self.compute_api.get_all(elevated or context,
- search_opts=search_opts, limit=limit, marker=marker,
- want_objects=True, expected_attrs=expected_attrs,
- sort_keys=sort_keys, sort_dirs=sort_dirs)
- except exception.MarkerNotFound:
- msg = _('marker [%s] not found') % marker
- raise exc.HTTPBadRequest(explanation=msg)
- except exception.FlavorNotFound:
- LOG.debug("Flavor '%s' could not be found", search_opts['flavor'])
- instance_list = objects.InstanceList()
-
- if is_detail:
- instance_list._context = context
- instance_list.fill_faults()
- response = self._view_builder.detail(req, instance_list)
- else:
- response = self._view_builder.index(req, instance_list)
- req.cache_db_instances(instance_list)
- return response
-
- def _get_server(self, context, req, instance_uuid, is_detail=False):
- """Utility function for looking up an instance by uuid.
-
- :param context: request context for auth
- :param req: HTTP request. The instance is cached in this request.
- :param instance_uuid: UUID of the server instance to get
- :param is_detail: True if you plan on showing the details of the
- instance in the response, False otherwise.
- """
- expected_attrs = ['flavor']
- if is_detail:
- expected_attrs = self._view_builder.get_show_expected_attrs(
- expected_attrs)
- instance = common.get_instance(self.compute_api, context,
- instance_uuid,
- expected_attrs=expected_attrs)
- req.cache_db_instance(instance)
- return instance
-
- def _check_string_length(self, value, name, max_length=None):
- try:
- if isinstance(value, six.string_types):
- value = value.strip()
- utils.check_string_length(value, name, min_length=1,
- max_length=max_length)
- except exception.InvalidInput as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- def _validate_server_name(self, value):
- self._check_string_length(value, 'Server name', max_length=255)
-
- def _get_injected_files(self, personality):
- """Create a list of injected files from the personality attribute.
-
- At this time, injected_files must be formatted as a list of
- (file_path, file_content) pairs for compatibility with the
- underlying compute service.
- """
- injected_files = []
-
- for item in personality:
- try:
- path = item['path']
- contents = item['contents']
- except KeyError as key:
- expl = _('Bad personality format: missing %s') % key
- raise exc.HTTPBadRequest(explanation=expl)
- except TypeError:
- expl = _('Bad personality format')
- raise exc.HTTPBadRequest(explanation=expl)
- if self._decode_base64(contents) is None:
- expl = _('Personality content for %s cannot be decoded') % path
- raise exc.HTTPBadRequest(explanation=expl)
- injected_files.append((path, contents))
- return injected_files
-
- def _get_requested_networks(self, requested_networks):
- """Create a list of requested networks from the networks attribute."""
- networks = []
- network_uuids = []
- for network in requested_networks:
- request = objects.NetworkRequest()
- try:
- try:
- request.port_id = network.get('port', None)
- except ValueError:
- msg = _("Bad port format: port uuid is "
- "not in proper format "
- "(%s)") % network.get('port')
- raise exc.HTTPBadRequest(explanation=msg)
- if request.port_id:
- request.network_id = None
- if not utils.is_neutron():
- # port parameter is only for neutron v2.0
- msg = _("Unknown argument : port")
- raise exc.HTTPBadRequest(explanation=msg)
- else:
- request.network_id = network['uuid']
-
- if (not request.port_id and not
- uuidutils.is_uuid_like(request.network_id)):
- br_uuid = request.network_id.split('-', 1)[-1]
- if not uuidutils.is_uuid_like(br_uuid):
- msg = _("Bad networks format: network uuid is "
- "not in proper format "
- "(%s)") % request.network_id
- raise exc.HTTPBadRequest(explanation=msg)
-
- # fixed IP address is optional
- # if the fixed IP address is not provided then
- # it will use one of the available IP address from the network
- try:
- request.address = network.get('fixed_ip', None)
- except ValueError:
- msg = (_("Invalid fixed IP address (%s)") %
- network.get('fixed_ip'))
- raise exc.HTTPBadRequest(explanation=msg)
-
- # duplicate networks are allowed only for neutron v2.0
- if (not utils.is_neutron() and request.network_id and
- request.network_id in network_uuids):
- expl = (_("Duplicate networks"
- " (%s) are not allowed") %
- request.network_id)
- raise exc.HTTPBadRequest(explanation=expl)
- network_uuids.append(request.network_id)
- networks.append(request)
- except KeyError as key:
- expl = _('Bad network format: missing %s') % key
- raise exc.HTTPBadRequest(explanation=expl)
- except TypeError:
- expl = _('Bad networks format')
- raise exc.HTTPBadRequest(explanation=expl)
-
- return objects.NetworkRequestList(objects=networks)
-
- # NOTE(vish): Without this regex, b64decode will happily
- # ignore illegal bytes in the base64 encoded
- # data.
- B64_REGEX = re.compile('^(?:[A-Za-z0-9+\/]{4})*'
- '(?:[A-Za-z0-9+\/]{2}=='
- '|[A-Za-z0-9+\/]{3}=)?$')
-
- def _decode_base64(self, data):
- if isinstance(data, six.binary_type) and hasattr(data, "decode"):
- try:
- data = data.decode("utf-8")
- except UnicodeDecodeError:
- return None
- data = re.sub(r'\s', '', data)
- if not self.B64_REGEX.match(data):
- return None
- try:
- return base64.b64decode(data)
- except TypeError:
- return None
-
- def _validate_access_ipv4(self, address):
- if not netutils.is_valid_ipv4(address):
- expl = _('accessIPv4 is not proper IPv4 format')
- raise exc.HTTPBadRequest(explanation=expl)
-
- def _validate_access_ipv6(self, address):
- if not netutils.is_valid_ipv6(address):
- expl = _('accessIPv6 is not proper IPv6 format')
- raise exc.HTTPBadRequest(explanation=expl)
-
- def show(self, req, id):
- """Returns server details by server id."""
- context = req.environ['nova.context']
- instance = self._get_server(context, req, id, is_detail=True)
- return self._view_builder.show(req, instance)
-
- def _extract(self, server_dict, ext_name, key):
- if self.ext_mgr.is_loaded(ext_name):
- return server_dict.get(key)
- return None
-
- def _validate_user_data(self, user_data):
- if user_data and self._decode_base64(user_data) is None:
- expl = _('Userdata content cannot be decoded')
- raise exc.HTTPBadRequest(explanation=expl)
- return user_data
-
- def _extract_bdm(self, server_dict, image_uuid_specified):
- legacy_bdm = True
- block_device_mapping_v2 = None
- if not self.ext_mgr.is_loaded('os-volumes'):
- return legacy_bdm, None
- block_device_mapping = server_dict.get('block_device_mapping', [])
- if not isinstance(block_device_mapping, list):
- msg = _('block_device_mapping must be a list')
- raise exc.HTTPBadRequest(explanation=msg)
- for bdm in block_device_mapping:
- try:
- block_device.validate_device_name(bdm.get("device_name"))
- block_device.validate_and_default_volume_size(bdm)
- except exception.InvalidBDMFormat as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- if 'delete_on_termination' in bdm:
- bdm['delete_on_termination'] = strutils.bool_from_string(
- bdm['delete_on_termination'])
-
- if self.ext_mgr.is_loaded('os-block-device-mapping-v2-boot'):
- # Consider the new data format for block device mapping
- block_device_mapping_v2 = server_dict.get(
- 'block_device_mapping_v2', [])
- # NOTE (ndipanov): Disable usage of both legacy and new
- # block device format in the same request
- if block_device_mapping and block_device_mapping_v2:
- expl = _('Using different block_device_mapping syntaxes '
- 'is not allowed in the same request.')
- raise exc.HTTPBadRequest(explanation=expl)
-
- if not isinstance(block_device_mapping_v2, list):
- msg = _('block_device_mapping_v2 must be a list')
- raise exc.HTTPBadRequest(explanation=msg)
-
- # Assume legacy format
- legacy_bdm = not bool(block_device_mapping_v2)
-
- try:
- block_device_mapping_v2 = [
- block_device.BlockDeviceDict.from_api(bdm_dict,
- image_uuid_specified)
- for bdm_dict in block_device_mapping_v2]
- except exception.InvalidBDMFormat as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- bdm = (block_device_mapping or block_device_mapping_v2)
- return legacy_bdm, bdm
-
- @staticmethod
- def _resolve_exception(matches):
- """We want the most specific exception class."""
- while len(matches) > 1:
- first = matches[0]
- second = matches[1]
- if issubclass(first, second):
- del matches[1]
- else:
- del matches[0]
- return matches[0]
-
- @staticmethod
- def _handle_create_exception(*exc_info):
- """The `CREATE_EXCEPTIONS` dict containing the relationships between
- the nova exceptions and the webob exception classes to be raised is
- defined at the top of this file.
- """
- error = exc_info[1]
- err_cls = error.__class__
- cls_to_raise = CREATE_EXCEPTIONS.get(err_cls)
- if cls_to_raise is None:
- # The error is a subclass of one of the dict keys
- to_raise = [val for key, val in CREATE_EXCEPTIONS.items()
- if isinstance(error, key)]
- if len(to_raise) > 1:
- cls_to_raise = Controller._resolve_exception(to_raise)
- elif not to_raise:
- # Not any of the expected exceptions, so re-raise
- six.reraise(*exc_info)
- else:
- cls_to_raise = to_raise[0]
-
- for key, val in CREATE_EXCEPTIONS_MSGS.items():
- if isinstance(error, key):
- raise cls_to_raise(explanation=CREATE_EXCEPTIONS_MSGS[key])
- raise cls_to_raise(explanation=error.format_message())
-
- def _determine_requested_networks(self, server_dict):
- requested_networks = None
- if (self.ext_mgr.is_loaded('os-networks')
- or utils.is_neutron()):
- requested_networks = server_dict.get('networks')
-
- if requested_networks is not None:
- if not isinstance(requested_networks, list):
- expl = _('Bad networks format')
- raise exc.HTTPBadRequest(explanation=expl)
- requested_networks = self._get_requested_networks(
- requested_networks)
- return requested_networks
-
- @wsgi.response(202)
- def create(self, req, body):
- """Creates a new server for a given user."""
- if not self.is_valid_body(body, 'server'):
- raise exc.HTTPUnprocessableEntity()
-
- context = req.environ['nova.context']
- server_dict = body['server']
- password = self._get_server_admin_password(server_dict)
-
- if 'name' not in server_dict:
- msg = _("Server name is not defined")
- raise exc.HTTPBadRequest(explanation=msg)
-
- name = server_dict['name']
- self._validate_server_name(name)
- name = name.strip()
-
- image_uuid = self._image_from_req_data(body)
-
- personality = server_dict.get('personality')
- config_drive = None
- if self.ext_mgr.is_loaded('os-config-drive'):
- config_drive = server_dict.get('config_drive')
-
- injected_files = []
- if personality:
- injected_files = self._get_injected_files(personality)
-
- sg_names = []
- if self.ext_mgr.is_loaded('os-security-groups'):
- security_groups = server_dict.get('security_groups')
- if security_groups is not None:
- try:
- sg_names = [sg['name'] for sg in security_groups
- if sg.get('name')]
- except AttributeError:
- msg = _("Invalid input for field/attribute %(path)s."
- " Value: %(value)s. %(message)s") % {
- 'path': 'security_groups',
- 'value': security_groups,
- 'message': ''
- }
- raise exc.HTTPBadRequest(explanation=msg)
- if not sg_names:
- sg_names.append('default')
-
- sg_names = list(set(sg_names))
-
- requested_networks = self._determine_requested_networks(server_dict)
-
- (access_ip_v4, ) = server_dict.get('accessIPv4'),
- if access_ip_v4 is not None:
- self._validate_access_ipv4(access_ip_v4)
-
- (access_ip_v6, ) = server_dict.get('accessIPv6'),
- if access_ip_v6 is not None:
- self._validate_access_ipv6(access_ip_v6)
-
- flavor_id = self._flavor_id_from_req_data(body)
-
- # optional openstack extensions:
- key_name = self._extract(server_dict, 'os-keypairs', 'key_name')
- availability_zone = self._extract(server_dict, 'os-availability-zone',
- 'availability_zone')
- user_data = self._extract(server_dict, 'os-user-data', 'user_data')
- self._validate_user_data(user_data)
-
- image_uuid_specified = bool(image_uuid)
- legacy_bdm, block_device_mapping = self._extract_bdm(server_dict,
- image_uuid_specified)
-
- ret_resv_id = False
- # min_count and max_count are optional. If they exist, they may come
- # in as strings. Verify that they are valid integers and > 0.
- # Also, we want to default 'min_count' to 1, and default
- # 'max_count' to be 'min_count'.
- min_count = 1
- max_count = 1
- if self.ext_mgr.is_loaded('os-multiple-create'):
- ret_resv_id = server_dict.get('return_reservation_id', False)
- min_count = server_dict.get('min_count', 1)
- max_count = server_dict.get('max_count', min_count)
-
- try:
- min_count = utils.validate_integer(
- min_count, "min_count", min_value=1)
- max_count = utils.validate_integer(
- max_count, "max_count", min_value=1)
- except exception.InvalidInput as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
-
- if min_count > max_count:
- msg = _('min_count must be <= max_count')
- raise exc.HTTPBadRequest(explanation=msg)
-
- auto_disk_config = False
- if self.ext_mgr.is_loaded('OS-DCF'):
- auto_disk_config = server_dict.get('auto_disk_config')
-
- scheduler_hints = {}
- if self.ext_mgr.is_loaded('OS-SCH-HNT'):
- scheduler_hints = server_dict.get('scheduler_hints', {})
- parse_az = self.compute_api.parse_availability_zone
- availability_zone, host, node = parse_az(context, availability_zone)
-
- check_server_group_quota = self.ext_mgr.is_loaded(
- 'os-server-group-quotas')
- try:
- _get_inst_type = flavors.get_flavor_by_flavor_id
- inst_type = _get_inst_type(flavor_id, ctxt=context,
- read_deleted="no")
-
- (instances, resv_id) = self.compute_api.create(context,
- inst_type,
- image_uuid,
- display_name=name,
- display_description=name,
- key_name=key_name,
- metadata=server_dict.get('metadata', {}),
- access_ip_v4=access_ip_v4,
- access_ip_v6=access_ip_v6,
- injected_files=injected_files,
- admin_password=password,
- min_count=min_count,
- max_count=max_count,
- requested_networks=requested_networks,
- security_group=sg_names,
- user_data=user_data,
- availability_zone=availability_zone,
- forced_host=host, forced_node=node,
- config_drive=config_drive,
- block_device_mapping=block_device_mapping,
- auto_disk_config=auto_disk_config,
- scheduler_hints=scheduler_hints,
- legacy_bdm=legacy_bdm,
- check_server_group_quota=check_server_group_quota)
- except (exception.QuotaError,
- exception.PortLimitExceeded) as error:
- raise exc.HTTPForbidden(
- explanation=error.format_message())
- except messaging.RemoteError as err:
- msg = "%(err_type)s: %(err_msg)s" % {'err_type': err.exc_type,
- 'err_msg': err.value}
- raise exc.HTTPBadRequest(explanation=msg)
- except UnicodeDecodeError as error:
- msg = "UnicodeError: %s" % error
- raise exc.HTTPBadRequest(explanation=msg)
- except Exception:
- # The remaining cases can be handled in a standard fashion.
- self._handle_create_exception(*sys.exc_info())
-
- # If the caller wanted a reservation_id, return it
- if ret_resv_id:
- return wsgi.ResponseObject({'reservation_id': resv_id})
-
- req.cache_db_instances(instances)
- server = self._view_builder.create(req, instances[0])
-
- if CONF.enable_instance_password:
- server['server']['adminPass'] = password
-
- robj = wsgi.ResponseObject(server)
-
- return self._add_location(robj)
-
- def _delete(self, context, req, instance_uuid):
- instance = self._get_server(context, req, instance_uuid)
- if CONF.reclaim_instance_interval:
- try:
- self.compute_api.soft_delete(context, instance)
- except exception.InstanceInvalidState:
- # Note(yufang521247): instance which has never been active
- # is not allowed to be soft_deleted. Thus we have to call
- # delete() to clean up the instance.
- self.compute_api.delete(context, instance)
- else:
- self.compute_api.delete(context, instance)
-
- def update(self, req, id, body):
- """Update server then pass on to version-specific controller."""
- if not self.is_valid_body(body, 'server'):
- raise exc.HTTPUnprocessableEntity()
-
- ctxt = req.environ['nova.context']
- update_dict = {}
-
- if 'name' in body['server']:
- name = body['server']['name']
- self._validate_server_name(name)
- update_dict['display_name'] = name.strip()
-
- if 'accessIPv4' in body['server']:
- access_ipv4 = body['server']['accessIPv4']
- if access_ipv4:
- self._validate_access_ipv4(access_ipv4)
- update_dict['access_ip_v4'] = (
- access_ipv4 and access_ipv4.strip() or None)
-
- if 'accessIPv6' in body['server']:
- access_ipv6 = body['server']['accessIPv6']
- if access_ipv6:
- self._validate_access_ipv6(access_ipv6)
- update_dict['access_ip_v6'] = (
- access_ipv6 and access_ipv6.strip() or None)
-
- if 'auto_disk_config' in body['server']:
- auto_disk_config = strutils.bool_from_string(
- body['server']['auto_disk_config'])
- update_dict['auto_disk_config'] = auto_disk_config
-
- if 'hostId' in body['server']:
- msg = _("HostId cannot be updated.")
- raise exc.HTTPBadRequest(explanation=msg)
-
- if 'personality' in body['server']:
- msg = _("Personality cannot be updated.")
- raise exc.HTTPBadRequest(explanation=msg)
-
- instance = self._get_server(ctxt, req, id, is_detail=True)
- try:
- policy.enforce(ctxt, 'compute:update', instance)
- instance.update(update_dict)
- # Note instance.save can throw a NotFound exception
- instance.save()
- except exception.NotFound:
- msg = _("Instance could not be found")
- raise exc.HTTPNotFound(explanation=msg)
-
- return self._view_builder.show(req, instance)
-
- @wsgi.response(204)
- @wsgi.action('confirmResize')
- def _action_confirm_resize(self, req, id, body):
- context = req.environ['nova.context']
- instance = self._get_server(context, req, id)
- try:
- self.compute_api.confirm_resize(context, instance)
- except exception.MigrationNotFound:
- msg = _("Instance has not been resized.")
- raise exc.HTTPBadRequest(explanation=msg)
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'confirmResize', id)
-
- @wsgi.response(202)
- @wsgi.action('revertResize')
- def _action_revert_resize(self, req, id, body):
- context = req.environ['nova.context']
- instance = self._get_server(context, req, id)
- try:
- self.compute_api.revert_resize(context, instance)
- except exception.MigrationNotFound:
- msg = _("Instance has not been resized.")
- raise exc.HTTPBadRequest(explanation=msg)
- except exception.FlavorNotFound:
- msg = _("Flavor used by the instance could not be found.")
- raise exc.HTTPBadRequest(explanation=msg)
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'revertResize', id)
- return webob.Response(status_int=202)
-
- @wsgi.response(202)
- @wsgi.action('reboot')
- def _action_reboot(self, req, id, body):
- if 'reboot' in body and 'type' in body['reboot']:
- if not isinstance(body['reboot']['type'], six.string_types):
- msg = _("Argument 'type' for reboot must be a string")
- LOG.error(msg)
- raise exc.HTTPBadRequest(explanation=msg)
- valid_reboot_types = ['HARD', 'SOFT']
- reboot_type = body['reboot']['type'].upper()
- if not valid_reboot_types.count(reboot_type):
- msg = _("Argument 'type' for reboot is not HARD or SOFT")
- LOG.error(msg)
- raise exc.HTTPBadRequest(explanation=msg)
- else:
- msg = _("Missing argument 'type' for reboot")
- LOG.error(msg)
- raise exc.HTTPBadRequest(explanation=msg)
-
- context = req.environ['nova.context']
- instance = self._get_server(context, req, id)
-
- try:
- self.compute_api.reboot(context, instance, reboot_type)
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'reboot', id)
- return webob.Response(status_int=202)
-
- def _resize(self, req, instance_id, flavor_id, **kwargs):
- """Begin the resize process with given instance/flavor."""
- context = req.environ["nova.context"]
- instance = self._get_server(context, req, instance_id)
- try:
- self.compute_api.resize(context, instance, flavor_id, **kwargs)
- except exception.QuotaError as error:
- raise exc.HTTPForbidden(
- explanation=error.format_message())
- except exception.FlavorNotFound:
- msg = _("Unable to locate requested flavor.")
- raise exc.HTTPBadRequest(explanation=msg)
- except exception.CannotResizeToSameFlavor:
- msg = _("Resize requires a flavor change.")
- raise exc.HTTPBadRequest(explanation=msg)
- except exception.CannotResizeDisk as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'resize', instance_id)
- except exception.ImageNotAuthorized:
- msg = _("You are not authorized to access the image "
- "the instance was started with.")
- raise exc.HTTPUnauthorized(explanation=msg)
- except exception.ImageNotFound:
- msg = _("Image that the instance was started "
- "with could not be found.")
- raise exc.HTTPBadRequest(explanation=msg)
- except (exception.NoValidHost,
- exception.AutoDiskConfigDisabledByImage) as e:
- raise exc.HTTPBadRequest(explanation=e.format_message())
- except exception.Invalid:
- msg = _("Invalid instance image.")
- raise exc.HTTPBadRequest(explanation=msg)
-
- return webob.Response(status_int=202)
-
- @wsgi.response(204)
- def delete(self, req, id):
- """Destroys a server."""
- try:
- self._delete(req.environ['nova.context'], req, id)
- except exception.NotFound:
- msg = _("Instance could not be found")
- raise exc.HTTPNotFound(explanation=msg)
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'delete', id)
-
- def _image_ref_from_req_data(self, data):
- try:
- return six.text_type(data['server']['imageRef'])
- except (TypeError, KeyError):
- msg = _("Missing imageRef attribute")
- raise exc.HTTPBadRequest(explanation=msg)
-
- def _image_from_req_data(self, data):
- """Get image data from the request or raise appropriate
- exceptions
-
- If no image is supplied - checks to see if there is
- block devices set and proper extesions loaded.
- """
- image_ref = data['server'].get('imageRef')
- bdm = data['server'].get('block_device_mapping')
- bdm_v2 = data['server'].get('block_device_mapping_v2')
-
- if (not image_ref and (
- (bdm and self.ext_mgr.is_loaded('os-volumes')) or
- (bdm_v2 and
- self.ext_mgr.is_loaded('os-block-device-mapping-v2-boot')))):
- return ''
- else:
- image_href = self._image_ref_from_req_data(data)
- image_uuid = common.image_uuid_from_href(image_href, 'imageRef')
- return image_uuid
-
- def _flavor_id_from_req_data(self, data):
- try:
- flavor_ref = data['server']['flavorRef']
- except (TypeError, KeyError):
- msg = _("Missing flavorRef attribute")
- raise exc.HTTPBadRequest(explanation=msg)
- try:
- return common.get_id_from_href(flavor_ref)
- except ValueError:
- msg = _("Invalid flavorRef provided.")
- raise exc.HTTPBadRequest(explanation=msg)
-
- @wsgi.response(202)
- @wsgi.action('changePassword')
- def _action_change_password(self, req, id, body):
- context = req.environ['nova.context']
- if (not body.get('changePassword')
- or 'adminPass' not in body['changePassword']):
- msg = _("No adminPass was specified")
- raise exc.HTTPBadRequest(explanation=msg)
- password = self._get_server_admin_password(body['changePassword'])
-
- server = self._get_server(context, req, id)
- try:
- self.compute_api.set_admin_password(context, server, password)
- except exception.InstancePasswordSetFailed as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as e:
- raise common.raise_http_conflict_for_instance_invalid_state(
- e, 'changePassword', id)
- except NotImplementedError:
- msg = _("Unable to set password on instance")
- raise exc.HTTPNotImplemented(explanation=msg)
- return webob.Response(status_int=202)
-
- def _validate_metadata(self, metadata):
- """Ensure that we can work with the metadata given."""
- try:
- six.iteritems(metadata)
- except AttributeError:
- msg = _("Unable to parse metadata key/value pairs.")
- LOG.debug(msg)
- raise exc.HTTPBadRequest(explanation=msg)
-
- @wsgi.response(202)
- @wsgi.action('resize')
- def _action_resize(self, req, id, body):
- """Resizes a given instance to the flavor size requested."""
- try:
- flavor_ref = str(body["resize"]["flavorRef"])
- if not flavor_ref:
- msg = _("Resize request has invalid 'flavorRef' attribute.")
- raise exc.HTTPBadRequest(explanation=msg)
- except (KeyError, TypeError):
- msg = _("Resize requests require 'flavorRef' attribute.")
- raise exc.HTTPBadRequest(explanation=msg)
-
- kwargs = {}
- if 'auto_disk_config' in body['resize']:
- kwargs['auto_disk_config'] = body['resize']['auto_disk_config']
-
- return self._resize(req, id, flavor_ref, **kwargs)
-
- @wsgi.response(202)
- @wsgi.action('rebuild')
- def _action_rebuild(self, req, id, body):
- """Rebuild an instance with the given attributes."""
- body = body['rebuild']
-
- try:
- image_href = body["imageRef"]
- except (KeyError, TypeError):
- msg = _("Could not parse imageRef from request.")
- raise exc.HTTPBadRequest(explanation=msg)
-
- image_href = common.image_uuid_from_href(image_href, 'imageRef')
-
- password = self._get_server_admin_password(body)
-
- context = req.environ['nova.context']
- instance = self._get_server(context, req, id)
-
- attr_map = {
- 'personality': 'files_to_inject',
- 'name': 'display_name',
- 'accessIPv4': 'access_ip_v4',
- 'accessIPv6': 'access_ip_v6',
- 'metadata': 'metadata',
- 'auto_disk_config': 'auto_disk_config',
- }
-
- kwargs = {}
-
- # take the preserve_ephemeral value into account only when the
- # corresponding extension is active
- if (self.ext_mgr.is_loaded('os-preserve-ephemeral-rebuild')
- and 'preserve_ephemeral' in body):
- kwargs['preserve_ephemeral'] = strutils.bool_from_string(
- body['preserve_ephemeral'], strict=True)
-
- if 'accessIPv4' in body:
- self._validate_access_ipv4(body['accessIPv4'])
-
- if 'accessIPv6' in body:
- self._validate_access_ipv6(body['accessIPv6'])
-
- if 'name' in body:
- self._validate_server_name(body['name'])
-
- for request_attribute, instance_attribute in attr_map.items():
- try:
- kwargs[instance_attribute] = body[request_attribute]
- except (KeyError, TypeError):
- pass
-
- self._validate_metadata(kwargs.get('metadata', {}))
-
- if 'files_to_inject' in kwargs:
- personality = kwargs.pop('files_to_inject')
- files_to_inject = self._get_injected_files(personality)
- else:
- files_to_inject = None
-
- try:
- self.compute_api.rebuild(context,
- instance,
- image_href,
- password,
- files_to_inject=files_to_inject,
- **kwargs)
- except exception.InstanceIsLocked as e:
- raise exc.HTTPConflict(explanation=e.format_message())
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'rebuild', id)
- except exception.InstanceNotFound:
- msg = _("Instance could not be found")
- raise exc.HTTPNotFound(explanation=msg)
- except exception.InvalidMetadataSize as error:
- raise exc.HTTPRequestEntityTooLarge(
- explanation=error.format_message())
- except exception.ImageNotFound:
- msg = _("Cannot find image for rebuild")
- raise exc.HTTPBadRequest(explanation=msg)
- except exception.QuotaError as error:
- raise exc.HTTPForbidden(explanation=error.format_message())
- except (exception.ImageNotActive,
- exception.FlavorDiskTooSmall,
- exception.FlavorMemoryTooSmall,
- exception.InvalidMetadata,
- exception.AutoDiskConfigDisabledByImage) as error:
- raise exc.HTTPBadRequest(explanation=error.format_message())
-
- instance = self._get_server(context, req, id, is_detail=True)
-
- view = self._view_builder.show(req, instance)
-
- # Add on the adminPass attribute since the view doesn't do it
- # unless instance passwords are disabled
- if CONF.enable_instance_password:
- view['server']['adminPass'] = password
-
- robj = wsgi.ResponseObject(view)
- return self._add_location(robj)
-
- @wsgi.response(202)
- @wsgi.action('createImage')
- @common.check_snapshots_enabled
- def _action_create_image(self, req, id, body):
- """Snapshot a server instance."""
- context = req.environ['nova.context']
- entity = body.get("createImage", {})
-
- image_name = entity.get("name")
-
- if not image_name:
- msg = _("createImage entity requires name attribute")
- raise exc.HTTPBadRequest(explanation=msg)
-
- props = {}
- metadata = entity.get('metadata', {})
- common.check_img_metadata_properties_quota(context, metadata)
- try:
- props.update(metadata)
- except ValueError:
- msg = _("Invalid metadata")
- raise exc.HTTPBadRequest(explanation=msg)
-
- instance = self._get_server(context, req, id)
-
- bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
- context, instance.uuid)
-
- try:
- if compute_utils.is_volume_backed_instance(context, instance,
- bdms):
- policy.enforce(context,
- 'compute:snapshot_volume_backed',
- {'project_id': context.project_id,
- 'user_id': context.user_id})
- image = self.compute_api.snapshot_volume_backed(
- context,
- instance,
- image_name,
- extra_properties=props)
- else:
- image = self.compute_api.snapshot(context,
- instance,
- image_name,
- extra_properties=props)
- except exception.InstanceInvalidState as state_error:
- common.raise_http_conflict_for_instance_invalid_state(state_error,
- 'createImage', id)
- except exception.Invalid as err:
- raise exc.HTTPBadRequest(explanation=err.format_message())
-
- # build location of newly-created image entity
- image_id = str(image['id'])
- url_prefix = self._view_builder._update_glance_link_prefix(
- req.application_url)
- image_ref = common.url_join(url_prefix,
- context.project_id,
- 'images',
- image_id)
-
- resp = webob.Response(status_int=202)
- resp.headers['Location'] = image_ref
- return resp
-
- def _get_server_admin_password(self, server):
- """Determine the admin password for a server on creation."""
- try:
- password = server['adminPass']
- self._validate_admin_password(password)
- except KeyError:
- password = utils.generate_password()
- except ValueError:
- raise exc.HTTPBadRequest(explanation=_("Invalid adminPass"))
-
- return password
-
- def _validate_admin_password(self, password):
- if not isinstance(password, six.string_types):
- raise ValueError()
-
- def _get_server_search_options(self):
- """Return server search options allowed by non-admin."""
- return ('reservation_id', 'name', 'status', 'image', 'flavor',
- 'ip', 'changes-since', 'all_tenants')
-
-
-def create_resource(ext_mgr):
- return wsgi.Resource(Controller(ext_mgr))
-
-
-def remove_invalid_options(context, search_options, allowed_search_options):
- """Remove search options that are not valid for non-admin API/context."""
- if context.is_admin:
- # Only remove parameters for sorting and pagination
- for key in ('sort_key', 'sort_dir', 'limit', 'marker'):
- search_options.pop(key, None)
- return
- # Otherwise, strip out all unknown options
- unknown_options = [opt for opt in search_options
- if opt not in allowed_search_options]
- LOG.debug("Removing options '%s' from query",
- ", ".join(unknown_options))
- for opt in unknown_options:
- search_options.pop(opt, None)
diff --git a/nova/api/openstack/compute/legacy_v2/versions.py b/nova/api/openstack/compute/legacy_v2/versions.py
deleted file mode 100644
index 6c36ea0376..0000000000
--- a/nova/api/openstack/compute/legacy_v2/versions.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright 2011 OpenStack Foundation
-# 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.compute import versions
-from nova.api.openstack.compute.views import versions as views_versions
-from nova.api.openstack import wsgi
-
-
-class VersionV2(object):
- def show(self, req):
- builder = views_versions.get_view_builder(req)
- return builder.build_version(versions.VERSIONS['v2.0'])
-
-
-def create_resource():
- return wsgi.Resource(VersionV2())