diff options
-rwxr-xr-x | bin/nova-api-os-compute | 47 | ||||
-rwxr-xr-x | bin/nova-api-os-volume (renamed from bin/nova-api-os) | 2 | ||||
-rw-r--r-- | etc/nova/api-paste.ini | 62 | ||||
-rw-r--r-- | nova/api/mapper.py | 76 | ||||
-rw-r--r-- | nova/api/openstack/__init__.py | 69 | ||||
-rw-r--r-- | nova/api/openstack/auth.py (renamed from nova/api/openstack/v2/auth.py) | 2 | ||||
-rw-r--r-- | nova/api/openstack/compute/__init__.py (renamed from nova/api/openstack/v2/__init__.py) | 62 | ||||
-rw-r--r-- | nova/api/openstack/compute/consoles.py (renamed from nova/api/openstack/v2/consoles.py) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/__init__.py | 32 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/accounts.py (renamed from nova/api/openstack/v2/contrib/accounts.py) | 4 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/admin_actions.py (renamed from nova/api/openstack/v2/contrib/admin_actions.py) | 4 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/cloudpipe.py (renamed from nova/api/openstack/v2/contrib/cloudpipe.py) | 4 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/console_output.py (renamed from nova/api/openstack/v2/contrib/console_output.py) | 4 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/createserverext.py (renamed from nova/api/openstack/v2/contrib/createserverext.py) | 6 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/deferred_delete.py (renamed from nova/api/openstack/v2/contrib/deferred_delete.py) | 6 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/disk_config.py (renamed from nova/api/openstack/v2/contrib/disk_config.py) | 2 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/extended_status.py (renamed from nova/api/openstack/v2/contrib/extended_status.py) | 4 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/flavorextradata.py (renamed from nova/api/openstack/v2/contrib/flavorextradata.py) | 2 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/flavorextraspecs.py (renamed from nova/api/openstack/v2/contrib/flavorextraspecs.py) | 2 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/floating_ip_dns.py (renamed from nova/api/openstack/v2/contrib/floating_ip_dns.py) | 4 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/floating_ip_pools.py (renamed from nova/api/openstack/v2/contrib/floating_ip_pools.py) | 4 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/floating_ips.py (renamed from nova/api/openstack/v2/contrib/floating_ips.py) | 4 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/hosts.py (renamed from nova/api/openstack/v2/contrib/hosts.py) | 4 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/keypairs.py (renamed from nova/api/openstack/v2/contrib/keypairs.py) | 2 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/multinic.py (renamed from nova/api/openstack/v2/contrib/multinic.py) | 4 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/networks.py (renamed from nova/api/openstack/v2/contrib/networks.py) | 4 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/quotas.py (renamed from nova/api/openstack/v2/contrib/quotas.py) | 2 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/rescue.py (renamed from nova/api/openstack/v2/contrib/rescue.py) | 4 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/security_groups.py (renamed from nova/api/openstack/v2/contrib/security_groups.py) | 4 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/server_action_list.py (renamed from nova/api/openstack/v2/contrib/server_action_list.py) | 2 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/server_diagnostics.py (renamed from nova/api/openstack/v2/contrib/server_diagnostics.py) | 2 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/simple_tenant_usage.py (renamed from nova/api/openstack/v2/contrib/simple_tenant_usage.py) | 2 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/users.py (renamed from nova/api/openstack/v2/contrib/users.py) | 2 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/virtual_interfaces.py (renamed from nova/api/openstack/v2/contrib/virtual_interfaces.py) | 5 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/virtual_storage_arrays.py (renamed from nova/api/openstack/v2/contrib/virtual_storage_arrays.py) | 8 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/volumes.py (renamed from nova/api/openstack/v2/contrib/volumes.py) | 6 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/volumetypes.py (renamed from nova/api/openstack/v2/contrib/volumetypes.py) | 2 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/zones.py (renamed from nova/api/openstack/v2/contrib/zones.py) | 6 | ||||
-rw-r--r-- | nova/api/openstack/compute/extensions.py | 45 | ||||
-rw-r--r-- | nova/api/openstack/compute/flavors.py (renamed from nova/api/openstack/v2/flavors.py) | 2 | ||||
-rw-r--r-- | nova/api/openstack/compute/image_metadata.py (renamed from nova/api/openstack/v2/image_metadata.py) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/images.py (renamed from nova/api/openstack/v2/images.py) | 4 | ||||
-rw-r--r-- | nova/api/openstack/compute/ips.py (renamed from nova/api/openstack/v2/ips.py) | 4 | ||||
-rw-r--r-- | nova/api/openstack/compute/limits.py (renamed from nova/api/openstack/v2/limits.py) | 2 | ||||
-rw-r--r-- | nova/api/openstack/compute/ratelimiting/__init__.py (renamed from nova/api/openstack/v2/ratelimiting/__init__.py) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/atom-link.rng (renamed from nova/api/openstack/v2/schemas/atom-link.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/atom.rng (renamed from nova/api/openstack/v2/schemas/atom.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/v1.1/addresses.rng (renamed from nova/api/openstack/v2/schemas/v1.1/addresses.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/v1.1/extension.rng (renamed from nova/api/openstack/v2/schemas/v1.1/extension.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/v1.1/extensions.rng (renamed from nova/api/openstack/v2/schemas/v1.1/extensions.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/v1.1/flavor.rng (renamed from nova/api/openstack/v2/schemas/v1.1/flavor.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/v1.1/flavors.rng (renamed from nova/api/openstack/v2/schemas/v1.1/flavors.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/v1.1/flavors_index.rng (renamed from nova/api/openstack/v2/schemas/v1.1/flavors_index.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/v1.1/image.rng (renamed from nova/api/openstack/v2/schemas/v1.1/image.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/v1.1/images.rng (renamed from nova/api/openstack/v2/schemas/v1.1/images.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/v1.1/images_index.rng (renamed from nova/api/openstack/v2/schemas/v1.1/images_index.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/v1.1/limits.rng (renamed from nova/api/openstack/v2/schemas/v1.1/limits.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/v1.1/metadata.rng (renamed from nova/api/openstack/v2/schemas/v1.1/metadata.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/v1.1/server.rng (renamed from nova/api/openstack/v2/schemas/v1.1/server.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/v1.1/servers.rng (renamed from nova/api/openstack/v2/schemas/v1.1/servers.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/v1.1/servers_index.rng (renamed from nova/api/openstack/v2/schemas/v1.1/servers_index.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/v1.1/version.rng (renamed from nova/api/openstack/v2/schemas/v1.1/version.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/schemas/v1.1/versions.rng (renamed from nova/api/openstack/v2/schemas/v1.1/versions.rng) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/server_metadata.py (renamed from nova/api/openstack/v2/server_metadata.py) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/servers.py (renamed from nova/api/openstack/v2/servers.py) | 6 | ||||
-rw-r--r-- | nova/api/openstack/compute/versions.py (renamed from nova/api/openstack/v2/versions.py) | 2 | ||||
-rw-r--r-- | nova/api/openstack/compute/views/__init__.py (renamed from nova/api/openstack/v2/views/__init__.py) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/views/addresses.py (renamed from nova/api/openstack/v2/views/addresses.py) | 2 | ||||
-rw-r--r-- | nova/api/openstack/compute/views/flavors.py (renamed from nova/api/openstack/v2/views/flavors.py) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/views/images.py (renamed from nova/api/openstack/v2/views/images.py) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/views/limits.py (renamed from nova/api/openstack/v2/views/limits.py) | 0 | ||||
-rw-r--r-- | nova/api/openstack/compute/views/servers.py (renamed from nova/api/openstack/v2/views/servers.py) | 8 | ||||
-rw-r--r-- | nova/api/openstack/compute/views/versions.py (renamed from nova/api/openstack/v2/views/versions.py) | 0 | ||||
-rw-r--r-- | nova/api/openstack/extensions.py (renamed from nova/api/openstack/v2/extensions.py) | 80 | ||||
-rw-r--r-- | nova/api/openstack/urlmap.py (renamed from nova/api/openstack/v2/urlmap.py) | 2 | ||||
-rw-r--r-- | nova/api/openstack/v2/contrib/__init__.py | 90 | ||||
-rw-r--r-- | nova/api/openstack/volume/__init__.py | 99 | ||||
-rw-r--r-- | nova/api/openstack/volume/contrib/__init__.py | 32 | ||||
-rw-r--r-- | nova/api/openstack/volume/extensions.py | 44 | ||||
-rw-r--r-- | nova/api/openstack/volume/snapshots.py | 183 | ||||
-rw-r--r-- | nova/api/openstack/volume/types.py | 89 | ||||
-rw-r--r-- | nova/api/openstack/volume/versions.py | 83 | ||||
-rw-r--r-- | nova/api/openstack/volume/views/__init__.py | 16 | ||||
-rw-r--r-- | nova/api/openstack/volume/views/versions.py | 37 | ||||
-rw-r--r-- | nova/api/openstack/volume/volumes.py | 254 | ||||
-rw-r--r-- | nova/api/openstack/xmlutil.py | 4 | ||||
-rw-r--r-- | nova/auth/manager.py | 2 | ||||
-rw-r--r-- | nova/common/cfg.py | 8 | ||||
-rw-r--r-- | nova/flags.py | 14 | ||||
-rw-r--r-- | nova/service.py | 9 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/__init__.py (renamed from nova/tests/api/openstack/v2/__init__.py) | 0 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/__init__.py (renamed from nova/tests/api/openstack/v2/contrib/__init__.py) | 0 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_accounts.py (renamed from nova/tests/api/openstack/v2/contrib/test_accounts.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_admin_actions.py (renamed from nova/tests/api/openstack/v2/contrib/test_admin_actions.py) | 6 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_cloudpipe.py (renamed from nova/tests/api/openstack/v2/contrib/test_cloudpipe.py) | 8 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_console_output.py (renamed from nova/tests/api/openstack/v2/contrib/test_console_output.py) | 0 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_createserverext.py (renamed from nova/tests/api/openstack/v2/contrib/test_createserverext.py) | 0 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_deferred_delete.py (renamed from nova/tests/api/openstack/v2/contrib/test_deferred_delete.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_disk_config.py (renamed from nova/tests/api/openstack/v2/contrib/test_disk_config.py) | 6 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_extendedstatus.py (renamed from nova/tests/api/openstack/v2/contrib/test_extendedstatus.py) | 0 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py (renamed from nova/tests/api/openstack/v2/contrib/test_flavors_extra_specs.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_floating_ip_dns.py (renamed from nova/tests/api/openstack/v2/contrib/test_floating_ip_dns.py) | 4 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_floating_ip_pools.py (renamed from nova/tests/api/openstack/v2/contrib/test_floating_ip_pools.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_floating_ips.py (renamed from nova/tests/api/openstack/v2/contrib/test_floating_ips.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_hosts.py (renamed from nova/tests/api/openstack/v2/contrib/test_hosts.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_keypairs.py (renamed from nova/tests/api/openstack/v2/contrib/test_keypairs.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_multinic_xs.py (renamed from nova/tests/api/openstack/v2/contrib/test_multinic_xs.py) | 0 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_networks.py (renamed from nova/tests/api/openstack/v2/contrib/test_networks.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_quotas.py (renamed from nova/tests/api/openstack/v2/contrib/test_quotas.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_rescue.py (renamed from nova/tests/api/openstack/v2/contrib/test_rescue.py) | 0 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_security_groups.py (renamed from nova/tests/api/openstack/v2/contrib/test_security_groups.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_server_action_list.py (renamed from nova/tests/api/openstack/v2/contrib/test_server_action_list.py) | 8 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_server_diagnostics.py (renamed from nova/tests/api/openstack/v2/contrib/test_server_diagnostics.py) | 8 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py (renamed from nova/tests/api/openstack/v2/contrib/test_simple_tenant_usage.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_snapshots.py (renamed from nova/tests/api/openstack/v2/contrib/test_snapshots.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_users.py (renamed from nova/tests/api/openstack/v2/contrib/test_users.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_virtual_interfaces.py (renamed from nova/tests/api/openstack/v2/contrib/test_virtual_interfaces.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_volume_types.py (renamed from nova/tests/api/openstack/v2/contrib/test_volume_types.py) | 4 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_volume_types_extra_specs.py (renamed from nova/tests/api/openstack/v2/contrib/test_volume_types_extra_specs.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_volumes.py (renamed from nova/tests/api/openstack/v2/contrib/test_volumes.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_vsa.py (renamed from nova/tests/api/openstack/v2/contrib/test_vsa.py) | 5 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_zones.py (renamed from nova/tests/api/openstack/v2/contrib/test_zones.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/extensions/__init__.py (renamed from nova/tests/api/openstack/v2/extensions/__init__.py) | 0 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/extensions/foxinsocks.py (renamed from nova/tests/api/openstack/v2/extensions/foxinsocks.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/test_api.py (renamed from nova/tests/api/openstack/v2/test_api.py) | 7 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/test_auth.py (renamed from nova/tests/api/openstack/v2/test_auth.py) | 23 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/test_consoles.py (renamed from nova/tests/api/openstack/v2/test_consoles.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/test_extensions.py (renamed from nova/tests/api/openstack/v2/test_extensions.py) | 109 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/test_flavors.py (renamed from nova/tests/api/openstack/v2/test_flavors.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/test_image_metadata.py (renamed from nova/tests/api/openstack/v2/test_image_metadata.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/test_images.py (renamed from nova/tests/api/openstack/v2/test_images.py) | 4 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/test_limits.py (renamed from nova/tests/api/openstack/v2/test_limits.py) | 4 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/test_server_actions.py (renamed from nova/tests/api/openstack/v2/test_server_actions.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/test_server_metadata.py (renamed from nova/tests/api/openstack/v2/test_server_metadata.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/test_servers.py (renamed from nova/tests/api/openstack/v2/test_servers.py) | 13 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/test_urlmap.py (renamed from nova/tests/api/openstack/v2/test_urlmap.py) | 2 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/test_versions.py (renamed from nova/tests/api/openstack/v2/test_versions.py) | 4 | ||||
-rw-r--r-- | nova/tests/api/openstack/fakes.py | 32 | ||||
-rw-r--r-- | nova/tests/api/openstack/volume/__init__.py | 16 | ||||
-rw-r--r-- | nova/tests/api/openstack/volume/test_snapshots.py | 299 | ||||
-rw-r--r-- | nova/tests/api/openstack/volume/test_types.py | 166 | ||||
-rw-r--r-- | nova/tests/api/openstack/volume/test_volumes.py | 179 | ||||
-rw-r--r-- | nova/tests/integrated/api/client.py | 8 | ||||
-rw-r--r-- | nova/tests/integrated/integrated_helpers.py | 10 | ||||
-rw-r--r-- | nova/tests/integrated/test_extensions.py | 9 | ||||
-rw-r--r-- | nova/tests/integrated/test_volumes.py | 158 | ||||
-rw-r--r-- | setup.py | 3 |
147 files changed, 2157 insertions, 566 deletions
diff --git a/bin/nova-api-os-compute b/bin/nova-api-os-compute new file mode 100755 index 000000000..7bfff2a42 --- /dev/null +++ b/bin/nova-api-os-compute @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. + +"""Starter script for Nova OS API.""" + +import eventlet +eventlet.monkey_patch() + +import os +import sys + + +possible_topdir = os.path.normpath(os.path.join(os.path.abspath( + sys.argv[0]), os.pardir, os.pardir)) +if os.path.exists(os.path.join(possible_topdir, "nova", "__init__.py")): + sys.path.insert(0, possible_topdir) + + +from nova import flags +from nova import log as logging +from nova import service +from nova import utils + +if __name__ == '__main__': + utils.default_flagfile() + flags.FLAGS(sys.argv) + logging.setup() + utils.monkey_patch() + server = service.WSGIService('osapi_compute') + service.serve(server) + service.wait() diff --git a/bin/nova-api-os b/bin/nova-api-os-volume index 83a808987..33909c5b5 100755 --- a/bin/nova-api-os +++ b/bin/nova-api-os-volume @@ -42,6 +42,6 @@ if __name__ == '__main__': flags.FLAGS(sys.argv) logging.setup() utils.monkey_patch() - server = service.WSGIService('osapi') + server = service.WSGIService('osapi_volume') service.serve(server) service.wait() diff --git a/etc/nova/api-paste.ini b/etc/nova/api-paste.ini index 145044df7..c2de4b484 100644 --- a/etc/nova/api-paste.ini +++ b/etc/nova/api-paste.ini @@ -78,40 +78,60 @@ paste.app_factory = nova.api.ec2:Executor.factory # Openstack # ############# -[composite:osapi] -use = call:nova.api.openstack.v2.urlmap:urlmap_factory -/: osversions -/v1.1: openstack_api_v2 -/v2: openstack_api_v2 - -[pipeline:openstack_api_v2] -pipeline = faultwrap noauth ratelimit serialize extensions osapi_app_v2 +[composite:osapi_compute] +use = call:nova.api.openstack.urlmap:urlmap_factory +/: oscomputeversions +/v1.1: openstack_compute_api_v2 +/v2: openstack_compute_api_v2 + +[composite:osapi_volume] +use = call:nova.api.openstack.urlmap:urlmap_factory +/: osvolumeversions +/v1: openstack_volume_api_v1 + +[pipeline:openstack_compute_api_v2] +pipeline = faultwrap noauth ratelimit serialize compute_extensions osapi_compute_app_v2 # NOTE(vish): use the following pipeline for deprecated auth -# pipeline = faultwrap auth ratelimit serialize extensions osapi_app_v2 +# pipeline = faultwrap auth ratelimit serialize extensions osapi_compute_app_v2 + +[pipeline:openstack_volume_api_v1] +pipeline = faultwrap noauth ratelimit serialize volume_extensions osapi_volume_app_v1 [filter:faultwrap] -paste.filter_factory = nova.api.openstack.v2:FaultWrapper.factory +paste.filter_factory = nova.api.openstack:FaultWrapper.factory [filter:auth] -paste.filter_factory = nova.api.openstack.v2.auth:AuthMiddleware.factory +paste.filter_factory = nova.api.openstack.auth:AuthMiddleware.factory [filter:noauth] -paste.filter_factory = nova.api.openstack.v2.auth:NoAuthMiddleware.factory +paste.filter_factory = nova.api.openstack.auth:NoAuthMiddleware.factory [filter:ratelimit] -paste.filter_factory = nova.api.openstack.v2.limits:RateLimitingMiddleware.factory +paste.filter_factory = nova.api.openstack.compute.limits:RateLimitingMiddleware.factory [filter:serialize] paste.filter_factory = nova.api.openstack.wsgi:LazySerializationMiddleware.factory -[filter:extensions] -paste.filter_factory = nova.api.openstack.v2.extensions:ExtensionMiddleware.factory +[filter:compute_extensions] +paste.filter_factory = nova.api.openstack.compute.extensions:ExtensionMiddleware.factory + +[filter:volume_extensions] +paste.filter_factory = nova.api.openstack.volume.extensions:ExtensionMiddleware.factory + +[app:osapi_compute_app_v2] +paste.app_factory = nova.api.openstack.compute:APIRouter.factory + +[pipeline:oscomputeversions] +pipeline = faultwrap oscomputeversionapp + +[app:osapi_volume_app_v1] +paste.app_factory = nova.api.openstack.volume:APIRouter.factory -[app:osapi_app_v2] -paste.app_factory = nova.api.openstack.v2:APIRouter.factory +[app:oscomputeversionapp] +paste.app_factory = nova.api.openstack.compute.versions:Versions.factory -[pipeline:osversions] -pipeline = faultwrap osversionapp +[pipeline:osvolumeversions] +pipeline = faultwrap osvolumeversionapp -[app:osversionapp] -paste.app_factory = nova.api.openstack.v2.versions:Versions.factory +[app:osvolumeversionapp] +paste.app_factory = nova.api.openstack.volume.versions:Versions.factory diff --git a/nova/api/mapper.py b/nova/api/mapper.py new file mode 100644 index 000000000..cd26e06ee --- /dev/null +++ b/nova/api/mapper.py @@ -0,0 +1,76 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. + +""" +WSGI middleware for OpenStack API controllers. +""" + +import routes +import webob.dec +import webob.exc + +from nova.api.openstack import wsgi +from nova import flags +from nova import log as logging +from nova import wsgi as base_wsgi + + +LOG = logging.getLogger('nova.api.openstack.compute') +FLAGS = flags.FLAGS +flags.DEFINE_bool('allow_admin_api', + False, + 'When True, this API service will accept admin operations.') +flags.DEFINE_bool('allow_instance_snapshots', + True, + 'When True, this API service will permit instance snapshot operations.') + + +class FaultWrapper(base_wsgi.Middleware): + """Calls down the middleware stack, making exceptions into faults.""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + try: + return req.get_response(self.application) + except Exception as ex: + LOG.exception(_("Caught error: %s"), unicode(ex)) + exc = webob.exc.HTTPInternalServerError() + return wsgi.Fault(exc) + + +class APIMapper(routes.Mapper): + def routematch(self, url=None, environ=None): + if url is "": + result = self._match("", environ) + return result[0], result[1] + return routes.Mapper.routematch(self, url, environ) + + +class ProjectMapper(APIMapper): + def resource(self, member_name, collection_name, **kwargs): + if not ('parent_resource' in kwargs): + kwargs['path_prefix'] = '{project_id}/' + else: + parent_resource = kwargs['parent_resource'] + p_collection = parent_resource['collection_name'] + p_member = parent_resource['member_name'] + kwargs['path_prefix'] = '{project_id}/%s/:%s_id' % (p_collection, + p_member) + routes.Mapper.resource(self, member_name, + collection_name, + **kwargs) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index e69de29bb..dfc174a58 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -0,0 +1,69 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. + +""" +WSGI middleware for OpenStack API controllers. +""" + +import routes +import webob.dec +import webob.exc + +from nova.api.openstack import wsgi +from nova import flags +from nova import log as logging +from nova import wsgi as base_wsgi + + +LOG = logging.getLogger('nova.api.openstack') + + +class FaultWrapper(base_wsgi.Middleware): + """Calls down the middleware stack, making exceptions into faults.""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + try: + return req.get_response(self.application) + except Exception as ex: + LOG.exception(_("Caught error: %s"), unicode(ex)) + exc = webob.exc.HTTPInternalServerError() + return wsgi.Fault(exc) + + +class APIMapper(routes.Mapper): + def routematch(self, url=None, environ=None): + if url is "": + result = self._match("", environ) + return result[0], result[1] + return routes.Mapper.routematch(self, url, environ) + + +class ProjectMapper(APIMapper): + def resource(self, member_name, collection_name, **kwargs): + if not ('parent_resource' in kwargs): + kwargs['path_prefix'] = '{project_id}/' + else: + parent_resource = kwargs['parent_resource'] + p_collection = parent_resource['collection_name'] + p_member = parent_resource['member_name'] + kwargs['path_prefix'] = '{project_id}/%s/:%s_id' % (p_collection, + p_member) + routes.Mapper.resource(self, member_name, + collection_name, + **kwargs) diff --git a/nova/api/openstack/v2/auth.py b/nova/api/openstack/auth.py index ba5fb603f..9a0432d51 100644 --- a/nova/api/openstack/v2/auth.py +++ b/nova/api/openstack/auth.py @@ -32,7 +32,7 @@ from nova import log as logging from nova import utils from nova import wsgi as base_wsgi -LOG = logging.getLogger('nova.api.openstack.v2.auth') +LOG = logging.getLogger('nova.api.openstack.compute.auth') FLAGS = flags.FLAGS flags.DECLARE('use_forwarded_for', 'nova.api.auth') diff --git a/nova/api/openstack/v2/__init__.py b/nova/api/openstack/compute/__init__.py index c211cd2f9..2f6e92a42 100644 --- a/nova/api/openstack/v2/__init__.py +++ b/nova/api/openstack/compute/__init__.py @@ -24,23 +24,24 @@ import routes import webob.dec import webob.exc -from nova.api.openstack.v2 import consoles -from nova.api.openstack.v2 import extensions -from nova.api.openstack.v2 import flavors -from nova.api.openstack.v2 import images -from nova.api.openstack.v2 import image_metadata -from nova.api.openstack.v2 import ips -from nova.api.openstack.v2 import limits -from nova.api.openstack.v2 import servers -from nova.api.openstack.v2 import server_metadata -from nova.api.openstack.v2 import versions +import nova.api.openstack +from nova.api.openstack.compute import consoles +from nova.api.openstack.compute import extensions +from nova.api.openstack.compute import flavors +from nova.api.openstack.compute import images +from nova.api.openstack.compute import image_metadata +from nova.api.openstack.compute import ips +from nova.api.openstack.compute import limits +from nova.api.openstack.compute import servers +from nova.api.openstack.compute import server_metadata +from nova.api.openstack.compute import versions from nova.api.openstack import wsgi from nova import flags from nova import log as logging from nova import wsgi as base_wsgi -LOG = logging.getLogger('nova.api.openstack.v2') +LOG = logging.getLogger('nova.api.openstack.compute') FLAGS = flags.FLAGS flags.DEFINE_bool('allow_admin_api', False, @@ -50,43 +51,6 @@ flags.DEFINE_bool('allow_instance_snapshots', 'When True, this API service will permit instance snapshot operations.') -class FaultWrapper(base_wsgi.Middleware): - """Calls down the middleware stack, making exceptions into faults.""" - - @webob.dec.wsgify(RequestClass=wsgi.Request) - def __call__(self, req): - try: - return req.get_response(self.application) - except Exception as ex: - LOG.exception(_("Caught error: %s"), unicode(ex)) - exc = webob.exc.HTTPInternalServerError() - return wsgi.Fault(exc) - - -class APIMapper(routes.Mapper): - def routematch(self, url=None, environ=None): - if url is "": - result = self._match("", environ) - return result[0], result[1] - return routes.Mapper.routematch(self, url, environ) - - -class ProjectMapper(APIMapper): - - def resource(self, member_name, collection_name, **kwargs): - if not ('parent_resource' in kwargs): - kwargs['path_prefix'] = '{project_id}/' - else: - parent_resource = kwargs['parent_resource'] - p_collection = parent_resource['collection_name'] - p_member = parent_resource['member_name'] - kwargs['path_prefix'] = '{project_id}/%s/:%s_id' % (p_collection, - p_member) - routes.Mapper.resource(self, member_name, - collection_name, - **kwargs) - - class APIRouter(base_wsgi.Router): """ Routes requests on the OpenStack API to the appropriate controller @@ -102,7 +66,7 @@ class APIRouter(base_wsgi.Router): if ext_mgr is None: ext_mgr = extensions.ExtensionManager() - mapper = ProjectMapper() + mapper = nova.api.openstack.ProjectMapper() self._setup_routes(mapper) self._setup_ext_routes(mapper, ext_mgr) super(APIRouter, self).__init__(mapper) diff --git a/nova/api/openstack/v2/consoles.py b/nova/api/openstack/compute/consoles.py index e9eee4c75..e9eee4c75 100644 --- a/nova/api/openstack/v2/consoles.py +++ b/nova/api/openstack/compute/consoles.py diff --git a/nova/api/openstack/compute/contrib/__init__.py b/nova/api/openstack/compute/contrib/__init__.py new file mode 100644 index 000000000..2713a82f4 --- /dev/null +++ b/nova/api/openstack/compute/contrib/__init__.py @@ -0,0 +1,32 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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 nova import log as logging +from nova.api.openstack import extensions + + +LOG = logging.getLogger('nova.api.openstack.compute.contrib') + + +def standard_extensions(ext_mgr): + extensions.load_standard_extensions(ext_mgr, LOG, __path__, __package__) diff --git a/nova/api/openstack/v2/contrib/accounts.py b/nova/api/openstack/compute/contrib/accounts.py index 9bfff31d7..9253c037d 100644 --- a/nova/api/openstack/v2/contrib/accounts.py +++ b/nova/api/openstack/compute/contrib/accounts.py @@ -15,7 +15,7 @@ import webob.exc -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova.auth import manager @@ -25,7 +25,7 @@ from nova import log as logging FLAGS = flags.FLAGS -LOG = logging.getLogger('nova.api.openstack.v2.contrib.accounts') +LOG = logging.getLogger('nova.api.openstack.compute.contrib.accounts') class AccountTemplate(xmlutil.TemplateBuilder): diff --git a/nova/api/openstack/v2/contrib/admin_actions.py b/nova/api/openstack/compute/contrib/admin_actions.py index 5479f88c4..dedef3061 100644 --- a/nova/api/openstack/v2/contrib/admin_actions.py +++ b/nova/api/openstack/compute/contrib/admin_actions.py @@ -19,7 +19,7 @@ import webob from webob import exc from nova.api.openstack import common -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova import compute from nova import exception from nova import flags @@ -28,7 +28,7 @@ from nova.scheduler import api as scheduler_api FLAGS = flags.FLAGS -LOG = logging.getLogger("nova.api.openstack.v2.contrib.admin_actions") +LOG = logging.getLogger("nova.api.openstack.compute.contrib.admin_actions") class Admin_actions(extensions.ExtensionDescriptor): diff --git a/nova/api/openstack/v2/contrib/cloudpipe.py b/nova/api/openstack/compute/contrib/cloudpipe.py index 9327e3987..17bfa810b 100644 --- a/nova/api/openstack/v2/contrib/cloudpipe.py +++ b/nova/api/openstack/compute/contrib/cloudpipe.py @@ -18,7 +18,7 @@ import os from nova.api.openstack import wsgi from nova.api.openstack import xmlutil -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova.auth import manager from nova.cloudpipe import pipelib from nova import compute @@ -31,7 +31,7 @@ from nova import utils FLAGS = flags.FLAGS -LOG = logging.getLogger("nova.api.openstack.v2.contrib.cloudpipe") +LOG = logging.getLogger("nova.api.openstack.compute.contrib.cloudpipe") class CloudpipeTemplate(xmlutil.TemplateBuilder): diff --git a/nova/api/openstack/v2/contrib/console_output.py b/nova/api/openstack/compute/contrib/console_output.py index 0c9196398..a27d8663b 100644 --- a/nova/api/openstack/v2/contrib/console_output.py +++ b/nova/api/openstack/compute/contrib/console_output.py @@ -21,10 +21,10 @@ import webob from nova import compute from nova import exception from nova import log as logging -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions -LOG = logging.getLogger('nova.api.openstack.v2.contrib.console_output') +LOG = logging.getLogger('nova.api.openstack.compute.contrib.console_output') class Console_output(extensions.ExtensionDescriptor): diff --git a/nova/api/openstack/v2/contrib/createserverext.py b/nova/api/openstack/compute/contrib/createserverext.py index 70fe2796f..25b53a0d6 100644 --- a/nova/api/openstack/v2/contrib/createserverext.py +++ b/nova/api/openstack/compute/contrib/createserverext.py @@ -14,9 +14,9 @@ # License for the specific language governing permissions and limitations # under the License -from nova.api.openstack.v2 import extensions -from nova.api.openstack.v2 import servers -from nova.api.openstack.v2 import views +from nova.api.openstack import extensions +from nova.api.openstack.compute import servers +from nova.api.openstack.compute import views from nova.api.openstack import wsgi diff --git a/nova/api/openstack/v2/contrib/deferred_delete.py b/nova/api/openstack/compute/contrib/deferred_delete.py index 0b7c60073..312c22c80 100644 --- a/nova/api/openstack/v2/contrib/deferred_delete.py +++ b/nova/api/openstack/compute/contrib/deferred_delete.py @@ -18,14 +18,14 @@ import webob from nova.api.openstack import common -from nova.api.openstack.v2 import extensions -from nova.api.openstack.v2 import servers +from nova.api.openstack import extensions +from nova.api.openstack.compute import servers from nova import compute from nova import exception from nova import log as logging -LOG = logging.getLogger("nova.api.openstack.v2.contrib.deferred-delete") +LOG = logging.getLogger("nova.api.openstack.compute.contrib.deferred-delete") class Deferred_delete(extensions.ExtensionDescriptor): diff --git a/nova/api/openstack/v2/contrib/disk_config.py b/nova/api/openstack/compute/contrib/disk_config.py index 7ce24a3fd..392291652 100644 --- a/nova/api/openstack/v2/contrib/disk_config.py +++ b/nova/api/openstack/compute/contrib/disk_config.py @@ -20,7 +20,7 @@ from xml.dom import minidom from webob import exc -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova.api.openstack import xmlutil from nova import compute from nova import db diff --git a/nova/api/openstack/v2/contrib/extended_status.py b/nova/api/openstack/compute/contrib/extended_status.py index a3b0410f6..7f9301b93 100644 --- a/nova/api/openstack/v2/contrib/extended_status.py +++ b/nova/api/openstack/compute/contrib/extended_status.py @@ -16,7 +16,7 @@ from webob import exc -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova.api.openstack import xmlutil from nova import compute from nova import exception @@ -25,7 +25,7 @@ from nova import log as logging FLAGS = flags.FLAGS -LOG = logging.getLogger("nova.api.openstack.v2.contrib.extendedstatus") +LOG = logging.getLogger("nova.api.openstack.compute.contrib.extendedstatus") class Extended_status(extensions.ExtensionDescriptor): diff --git a/nova/api/openstack/v2/contrib/flavorextradata.py b/nova/api/openstack/compute/contrib/flavorextradata.py index 462ad1aba..bf6fa8040 100644 --- a/nova/api/openstack/v2/contrib/flavorextradata.py +++ b/nova/api/openstack/compute/contrib/flavorextradata.py @@ -24,7 +24,7 @@ attributes. This extension adds to that list: swap """ -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions class Flavorextradata(extensions.ExtensionDescriptor): diff --git a/nova/api/openstack/v2/contrib/flavorextraspecs.py b/nova/api/openstack/compute/contrib/flavorextraspecs.py index cb5b572fa..eafea5d1f 100644 --- a/nova/api/openstack/v2/contrib/flavorextraspecs.py +++ b/nova/api/openstack/compute/contrib/flavorextraspecs.py @@ -21,7 +21,7 @@ from webob import exc from nova.api.openstack import wsgi from nova.api.openstack import xmlutil -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova import db from nova import exception diff --git a/nova/api/openstack/v2/contrib/floating_ip_dns.py b/nova/api/openstack/compute/contrib/floating_ip_dns.py index de1a0a27e..032d5bd7e 100644 --- a/nova/api/openstack/v2/contrib/floating_ip_dns.py +++ b/nova/api/openstack/compute/contrib/floating_ip_dns.py @@ -20,13 +20,13 @@ import webob from nova.api.openstack import wsgi from nova.api.openstack import xmlutil -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova import exception from nova import log as logging from nova import network -LOG = logging.getLogger('nova.api.openstack.v2.contrib.floating_ip_dns') +LOG = logging.getLogger('nova.api.openstack.compute.contrib.floating_ip_dns') def make_dns_entry(elem): diff --git a/nova/api/openstack/v2/contrib/floating_ip_pools.py b/nova/api/openstack/compute/contrib/floating_ip_pools.py index 9d6386f25..01b9a3645 100644 --- a/nova/api/openstack/v2/contrib/floating_ip_pools.py +++ b/nova/api/openstack/compute/contrib/floating_ip_pools.py @@ -16,12 +16,12 @@ from nova.api.openstack import wsgi from nova.api.openstack import xmlutil -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova import log as logging from nova import network -LOG = logging.getLogger('nova.api.openstack.v2.contrib.floating_ip_poolss') +LOG = logging.getLogger('nova.api.openstack.compute.contrib.floating_ip_pools') def _translate_floating_ip_view(pool): diff --git a/nova/api/openstack/v2/contrib/floating_ips.py b/nova/api/openstack/compute/contrib/floating_ips.py index 3a6f4ee34..2400f6c83 100644 --- a/nova/api/openstack/v2/contrib/floating_ips.py +++ b/nova/api/openstack/compute/contrib/floating_ips.py @@ -21,7 +21,7 @@ import webob from nova.api.openstack import wsgi from nova.api.openstack import xmlutil -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova import compute from nova import exception from nova import log as logging @@ -29,7 +29,7 @@ from nova import network from nova import rpc -LOG = logging.getLogger('nova.api.openstack.v2.contrib.floating_ips') +LOG = logging.getLogger('nova.api.openstack.compute.contrib.floating_ips') def make_float_ip(elem): diff --git a/nova/api/openstack/v2/contrib/hosts.py b/nova/api/openstack/compute/contrib/hosts.py index 2bb61696e..66dd64def 100644 --- a/nova/api/openstack/v2/contrib/hosts.py +++ b/nova/api/openstack/compute/contrib/hosts.py @@ -21,7 +21,7 @@ from xml.parsers import expat from nova.api.openstack import wsgi from nova.api.openstack import xmlutil -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova import compute from nova import exception from nova import flags @@ -29,7 +29,7 @@ from nova import log as logging from nova.scheduler import api as scheduler_api -LOG = logging.getLogger("nova.api.openstack.v2.contrib.hosts") +LOG = logging.getLogger("nova.api.openstack.compute.contrib.hosts") FLAGS = flags.FLAGS diff --git a/nova/api/openstack/v2/contrib/keypairs.py b/nova/api/openstack/compute/contrib/keypairs.py index 2dc9b063d..5ac205df5 100644 --- a/nova/api/openstack/v2/contrib/keypairs.py +++ b/nova/api/openstack/compute/contrib/keypairs.py @@ -26,7 +26,7 @@ from webob import exc from nova.api.openstack import wsgi from nova.api.openstack import xmlutil -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova import crypto from nova import db from nova import exception diff --git a/nova/api/openstack/v2/contrib/multinic.py b/nova/api/openstack/compute/contrib/multinic.py index 6719fb18a..18b95e63d 100644 --- a/nova/api/openstack/v2/contrib/multinic.py +++ b/nova/api/openstack/compute/contrib/multinic.py @@ -18,13 +18,13 @@ import webob from webob import exc -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova import compute from nova import exception from nova import log as logging -LOG = logging.getLogger("nova.api.openstack.v2.contrib.multinic") +LOG = logging.getLogger("nova.api.openstack.compute.contrib.multinic") # Note: The class name is as it has to be for this to be loaded as an diff --git a/nova/api/openstack/v2/contrib/networks.py b/nova/api/openstack/compute/contrib/networks.py index 4a96e534f..f2381a19d 100644 --- a/nova/api/openstack/v2/contrib/networks.py +++ b/nova/api/openstack/compute/contrib/networks.py @@ -19,7 +19,7 @@ from webob import exc -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova import exception from nova import flags from nova import log as logging @@ -27,7 +27,7 @@ import nova.network.api FLAGS = flags.FLAGS -LOG = logging.getLogger('nova.api.openstack.v2.contrib.networks') +LOG = logging.getLogger('nova.api.openstack.compute.contrib.networks') def network_dict(network): diff --git a/nova/api/openstack/v2/contrib/quotas.py b/nova/api/openstack/compute/contrib/quotas.py index 191553a02..5e4a20568 100644 --- a/nova/api/openstack/v2/contrib/quotas.py +++ b/nova/api/openstack/compute/contrib/quotas.py @@ -19,7 +19,7 @@ import webob from nova.api.openstack import wsgi from nova.api.openstack import xmlutil -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova import db from nova import exception from nova import quota diff --git a/nova/api/openstack/v2/contrib/rescue.py b/nova/api/openstack/compute/contrib/rescue.py index 20c18028d..0ea77cc6a 100644 --- a/nova/api/openstack/v2/contrib/rescue.py +++ b/nova/api/openstack/compute/contrib/rescue.py @@ -17,7 +17,7 @@ import webob from webob import exc -from nova.api.openstack.v2 import extensions as exts +from nova.api.openstack import extensions as exts from nova import compute from nova import exception from nova import flags @@ -26,7 +26,7 @@ from nova import utils FLAGS = flags.FLAGS -LOG = logging.getLogger("nova.api.openstack.v2.contrib.rescue") +LOG = logging.getLogger("nova.api.openstack.compute.contrib.rescue") class Rescue(exts.ExtensionDescriptor): diff --git a/nova/api/openstack/v2/contrib/security_groups.py b/nova/api/openstack/compute/contrib/security_groups.py index f4abbcd51..f0d3dfe16 100644 --- a/nova/api/openstack/v2/contrib/security_groups.py +++ b/nova/api/openstack/compute/contrib/security_groups.py @@ -22,7 +22,7 @@ from webob import exc import webob from nova.api.openstack import common -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import compute @@ -33,7 +33,7 @@ from nova import log as logging from nova import utils -LOG = logging.getLogger("nova.api.openstack.v2.contrib.security_groups") +LOG = logging.getLogger("nova.api.openstack.compute.contrib.security_groups") FLAGS = flags.FLAGS diff --git a/nova/api/openstack/v2/contrib/server_action_list.py b/nova/api/openstack/compute/contrib/server_action_list.py index 90573043e..436758572 100644 --- a/nova/api/openstack/v2/contrib/server_action_list.py +++ b/nova/api/openstack/compute/contrib/server_action_list.py @@ -15,7 +15,7 @@ import webob.exc -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import compute diff --git a/nova/api/openstack/v2/contrib/server_diagnostics.py b/nova/api/openstack/compute/contrib/server_diagnostics.py index daeda5081..11d1affaf 100644 --- a/nova/api/openstack/v2/contrib/server_diagnostics.py +++ b/nova/api/openstack/compute/contrib/server_diagnostics.py @@ -15,7 +15,7 @@ import webob.exc -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import compute diff --git a/nova/api/openstack/v2/contrib/simple_tenant_usage.py b/nova/api/openstack/compute/contrib/simple_tenant_usage.py index 7b07dfae1..f34581f6c 100644 --- a/nova/api/openstack/v2/contrib/simple_tenant_usage.py +++ b/nova/api/openstack/compute/contrib/simple_tenant_usage.py @@ -20,7 +20,7 @@ import urlparse import webob -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova.compute import api diff --git a/nova/api/openstack/v2/contrib/users.py b/nova/api/openstack/compute/contrib/users.py index e24c7c068..55dba02e4 100644 --- a/nova/api/openstack/v2/contrib/users.py +++ b/nova/api/openstack/compute/contrib/users.py @@ -16,7 +16,7 @@ from webob import exc from nova.api.openstack import common -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova.auth import manager diff --git a/nova/api/openstack/v2/contrib/virtual_interfaces.py b/nova/api/openstack/compute/contrib/virtual_interfaces.py index 401c7133e..ea37c4d97 100644 --- a/nova/api/openstack/v2/contrib/virtual_interfaces.py +++ b/nova/api/openstack/compute/contrib/virtual_interfaces.py @@ -16,14 +16,15 @@ """The virtual interfaces extension.""" from nova.api.openstack import common -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import log as logging from nova import network -LOG = logging.getLogger("nova.api.openstack.v2.contrib.virtual_interfaces") +LOG = logging.getLogger("nova.api.openstack.compute." + "contrib.virtual_interfaces") vif_nsmap = {None: wsgi.XMLNS_V11} diff --git a/nova/api/openstack/v2/contrib/virtual_storage_arrays.py b/nova/api/openstack/compute/contrib/virtual_storage_arrays.py index 0dee3f1b4..39edd155b 100644 --- a/nova/api/openstack/v2/contrib/virtual_storage_arrays.py +++ b/nova/api/openstack/compute/contrib/virtual_storage_arrays.py @@ -22,9 +22,9 @@ import webob from webob import exc from nova.api.openstack import common -from nova.api.openstack.v2.contrib import volumes -from nova.api.openstack.v2 import extensions -from nova.api.openstack.v2 import servers +from nova.api.openstack.compute.contrib import volumes +from nova.api.openstack import extensions +from nova.api.openstack.compute import servers from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import compute @@ -40,7 +40,7 @@ from nova import volume FLAGS = flags.FLAGS -LOG = logging.getLogger("nova.api.openstack.v2.contrib.vsa") +LOG = logging.getLogger("nova.api.openstack.compute.contrib.vsa") def _vsa_view(context, vsa, details=False, instances=None): diff --git a/nova/api/openstack/v2/contrib/volumes.py b/nova/api/openstack/compute/contrib/volumes.py index 0ca50b288..972c000ef 100644 --- a/nova/api/openstack/v2/contrib/volumes.py +++ b/nova/api/openstack/compute/contrib/volumes.py @@ -19,8 +19,8 @@ from webob import exc import webob from nova.api.openstack import common -from nova.api.openstack.v2 import extensions -from nova.api.openstack.v2 import servers +from nova.api.openstack import extensions +from nova.api.openstack.compute import servers from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import compute @@ -31,7 +31,7 @@ from nova import volume from nova.volume import volume_types -LOG = logging.getLogger("nova.api.openstack.v2.contrib.volumes") +LOG = logging.getLogger("nova.api.openstack.compute.contrib.volumes") FLAGS = flags.FLAGS diff --git a/nova/api/openstack/v2/contrib/volumetypes.py b/nova/api/openstack/compute/contrib/volumetypes.py index 231c86b1b..bf249f3f8 100644 --- a/nova/api/openstack/v2/contrib/volumetypes.py +++ b/nova/api/openstack/compute/contrib/volumetypes.py @@ -19,7 +19,7 @@ from webob import exc -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import db diff --git a/nova/api/openstack/v2/contrib/zones.py b/nova/api/openstack/compute/contrib/zones.py index adbc6580f..28e6f0772 100644 --- a/nova/api/openstack/v2/contrib/zones.py +++ b/nova/api/openstack/compute/contrib/zones.py @@ -20,8 +20,8 @@ import json from nova.api.openstack import common -from nova.api.openstack.v2 import servers -from nova.api.openstack.v2 import extensions +from nova.api.openstack.compute import servers +from nova.api.openstack import extensions from nova.api.openstack import xmlutil from nova.api.openstack import wsgi from nova.compute import api as compute @@ -32,7 +32,7 @@ from nova import log as logging import nova.scheduler.api -LOG = logging.getLogger("nova.api.openstack.v2.contrib.zones") +LOG = logging.getLogger("nova.api.openstack.compute.contrib.zones") FLAGS = flags.FLAGS diff --git a/nova/api/openstack/compute/extensions.py b/nova/api/openstack/compute/extensions.py new file mode 100644 index 000000000..39849e802 --- /dev/null +++ b/nova/api/openstack/compute/extensions.py @@ -0,0 +1,45 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from nova.api.openstack import extensions as base_extensions +from nova import flags +from nova import log as logging + + +LOG = logging.getLogger('nova.api.openstack.compute.extensions') +FLAGS = flags.FLAGS + + +class ExtensionManager(base_extensions.ExtensionManager): + def __new__(cls): + if cls._ext_mgr is None: + LOG.audit(_('Initializing extension manager.')) + + cls._ext_mgr = super(ExtensionManager, cls).__new__(cls) + + cls.cls_list = FLAGS.osapi_compute_extension + cls._ext_mgr.extensions = {} + cls._ext_mgr._load_extensions() + + return cls._ext_mgr + + +class ExtensionMiddleware(base_extensions.ExtensionMiddleware): + def __init__(self, application, ext_mgr=None): + if not ext_mgr: + ext_mgr = ExtensionManager() + super(ExtensionMiddleware, self).__init__(application, ext_mgr) diff --git a/nova/api/openstack/v2/flavors.py b/nova/api/openstack/compute/flavors.py index 21c2a8120..b5212e703 100644 --- a/nova/api/openstack/v2/flavors.py +++ b/nova/api/openstack/compute/flavors.py @@ -17,7 +17,7 @@ import webob -from nova.api.openstack.v2.views import flavors as flavors_view +from nova.api.openstack.compute.views import flavors as flavors_view from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova.compute import instance_types diff --git a/nova/api/openstack/v2/image_metadata.py b/nova/api/openstack/compute/image_metadata.py index 1e29d23ce..1e29d23ce 100644 --- a/nova/api/openstack/v2/image_metadata.py +++ b/nova/api/openstack/compute/image_metadata.py diff --git a/nova/api/openstack/v2/images.py b/nova/api/openstack/compute/images.py index 96a2275e6..194c5071e 100644 --- a/nova/api/openstack/v2/images.py +++ b/nova/api/openstack/compute/images.py @@ -16,7 +16,7 @@ import webob.exc from nova.api.openstack import common -from nova.api.openstack.v2.views import images as views_images +from nova.api.openstack.compute.views import images as views_images from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import compute @@ -26,7 +26,7 @@ import nova.image from nova import log -LOG = log.getLogger('nova.api.openstack.v2.images') +LOG = log.getLogger('nova.api.openstack.compute.images') FLAGS = flags.FLAGS SUPPORTED_FILTERS = { diff --git a/nova/api/openstack/v2/ips.py b/nova/api/openstack/compute/ips.py index 3dc9cf928..ec107914a 100644 --- a/nova/api/openstack/v2/ips.py +++ b/nova/api/openstack/compute/ips.py @@ -19,14 +19,14 @@ from webob import exc import nova from nova.api.openstack import common -from nova.api.openstack.v2.views import addresses as view_addresses +from nova.api.openstack.compute.views import addresses as view_addresses from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import log as logging from nova import flags -LOG = logging.getLogger('nova.api.openstack.v2.ips') +LOG = logging.getLogger('nova.api.openstack.compute.ips') FLAGS = flags.FLAGS diff --git a/nova/api/openstack/v2/limits.py b/nova/api/openstack/compute/limits.py index e1f5ff836..0a1b80057 100644 --- a/nova/api/openstack/v2/limits.py +++ b/nova/api/openstack/compute/limits.py @@ -28,7 +28,7 @@ import time from webob.dec import wsgify import webob.exc -from nova.api.openstack.v2.views import limits as limits_views +from nova.api.openstack.compute.views import limits as limits_views from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import quota diff --git a/nova/api/openstack/v2/ratelimiting/__init__.py b/nova/api/openstack/compute/ratelimiting/__init__.py index 78dc465a7..78dc465a7 100644 --- a/nova/api/openstack/v2/ratelimiting/__init__.py +++ b/nova/api/openstack/compute/ratelimiting/__init__.py diff --git a/nova/api/openstack/v2/schemas/atom-link.rng b/nova/api/openstack/compute/schemas/atom-link.rng index edba5eee6..edba5eee6 100644 --- a/nova/api/openstack/v2/schemas/atom-link.rng +++ b/nova/api/openstack/compute/schemas/atom-link.rng diff --git a/nova/api/openstack/v2/schemas/atom.rng b/nova/api/openstack/compute/schemas/atom.rng index c2df4e410..c2df4e410 100644 --- a/nova/api/openstack/v2/schemas/atom.rng +++ b/nova/api/openstack/compute/schemas/atom.rng diff --git a/nova/api/openstack/v2/schemas/v1.1/addresses.rng b/nova/api/openstack/compute/schemas/v1.1/addresses.rng index b498e8a63..b498e8a63 100644 --- a/nova/api/openstack/v2/schemas/v1.1/addresses.rng +++ b/nova/api/openstack/compute/schemas/v1.1/addresses.rng diff --git a/nova/api/openstack/v2/schemas/v1.1/extension.rng b/nova/api/openstack/compute/schemas/v1.1/extension.rng index 336659755..336659755 100644 --- a/nova/api/openstack/v2/schemas/v1.1/extension.rng +++ b/nova/api/openstack/compute/schemas/v1.1/extension.rng diff --git a/nova/api/openstack/v2/schemas/v1.1/extensions.rng b/nova/api/openstack/compute/schemas/v1.1/extensions.rng index 4d8bff646..4d8bff646 100644 --- a/nova/api/openstack/v2/schemas/v1.1/extensions.rng +++ b/nova/api/openstack/compute/schemas/v1.1/extensions.rng diff --git a/nova/api/openstack/v2/schemas/v1.1/flavor.rng b/nova/api/openstack/compute/schemas/v1.1/flavor.rng index 08746ce3d..08746ce3d 100644 --- a/nova/api/openstack/v2/schemas/v1.1/flavor.rng +++ b/nova/api/openstack/compute/schemas/v1.1/flavor.rng diff --git a/nova/api/openstack/v2/schemas/v1.1/flavors.rng b/nova/api/openstack/compute/schemas/v1.1/flavors.rng index b7a3acc01..b7a3acc01 100644 --- a/nova/api/openstack/v2/schemas/v1.1/flavors.rng +++ b/nova/api/openstack/compute/schemas/v1.1/flavors.rng diff --git a/nova/api/openstack/v2/schemas/v1.1/flavors_index.rng b/nova/api/openstack/compute/schemas/v1.1/flavors_index.rng index d1a4fedb1..d1a4fedb1 100644 --- a/nova/api/openstack/v2/schemas/v1.1/flavors_index.rng +++ b/nova/api/openstack/compute/schemas/v1.1/flavors_index.rng diff --git a/nova/api/openstack/v2/schemas/v1.1/image.rng b/nova/api/openstack/compute/schemas/v1.1/image.rng index 505081fba..505081fba 100644 --- a/nova/api/openstack/v2/schemas/v1.1/image.rng +++ b/nova/api/openstack/compute/schemas/v1.1/image.rng diff --git a/nova/api/openstack/v2/schemas/v1.1/images.rng b/nova/api/openstack/compute/schemas/v1.1/images.rng index 064d4d9cc..064d4d9cc 100644 --- a/nova/api/openstack/v2/schemas/v1.1/images.rng +++ b/nova/api/openstack/compute/schemas/v1.1/images.rng diff --git a/nova/api/openstack/v2/schemas/v1.1/images_index.rng b/nova/api/openstack/compute/schemas/v1.1/images_index.rng index 3db0b2672..3db0b2672 100644 --- a/nova/api/openstack/v2/schemas/v1.1/images_index.rng +++ b/nova/api/openstack/compute/schemas/v1.1/images_index.rng diff --git a/nova/api/openstack/v2/schemas/v1.1/limits.rng b/nova/api/openstack/compute/schemas/v1.1/limits.rng index 1af8108ec..1af8108ec 100644 --- a/nova/api/openstack/v2/schemas/v1.1/limits.rng +++ b/nova/api/openstack/compute/schemas/v1.1/limits.rng diff --git a/nova/api/openstack/v2/schemas/v1.1/metadata.rng b/nova/api/openstack/compute/schemas/v1.1/metadata.rng index b2f5d702a..b2f5d702a 100644 --- a/nova/api/openstack/v2/schemas/v1.1/metadata.rng +++ b/nova/api/openstack/compute/schemas/v1.1/metadata.rng diff --git a/nova/api/openstack/v2/schemas/v1.1/server.rng b/nova/api/openstack/compute/schemas/v1.1/server.rng index 07fa16daa..07fa16daa 100644 --- a/nova/api/openstack/v2/schemas/v1.1/server.rng +++ b/nova/api/openstack/compute/schemas/v1.1/server.rng diff --git a/nova/api/openstack/v2/schemas/v1.1/servers.rng b/nova/api/openstack/compute/schemas/v1.1/servers.rng index 4e2bb8853..4e2bb8853 100644 --- a/nova/api/openstack/v2/schemas/v1.1/servers.rng +++ b/nova/api/openstack/compute/schemas/v1.1/servers.rng diff --git a/nova/api/openstack/v2/schemas/v1.1/servers_index.rng b/nova/api/openstack/compute/schemas/v1.1/servers_index.rng index 023e4b66a..023e4b66a 100644 --- a/nova/api/openstack/v2/schemas/v1.1/servers_index.rng +++ b/nova/api/openstack/compute/schemas/v1.1/servers_index.rng diff --git a/nova/api/openstack/v2/schemas/v1.1/version.rng b/nova/api/openstack/compute/schemas/v1.1/version.rng index ae76270ba..ae76270ba 100644 --- a/nova/api/openstack/v2/schemas/v1.1/version.rng +++ b/nova/api/openstack/compute/schemas/v1.1/version.rng diff --git a/nova/api/openstack/v2/schemas/v1.1/versions.rng b/nova/api/openstack/compute/schemas/v1.1/versions.rng index 8b2cc7f71..8b2cc7f71 100644 --- a/nova/api/openstack/v2/schemas/v1.1/versions.rng +++ b/nova/api/openstack/compute/schemas/v1.1/versions.rng diff --git a/nova/api/openstack/v2/server_metadata.py b/nova/api/openstack/compute/server_metadata.py index 52a90f96e..52a90f96e 100644 --- a/nova/api/openstack/v2/server_metadata.py +++ b/nova/api/openstack/compute/server_metadata.py diff --git a/nova/api/openstack/v2/servers.py b/nova/api/openstack/compute/servers.py index c2655a73e..60b85f591 100644 --- a/nova/api/openstack/v2/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -22,8 +22,8 @@ from webob import exc import webob from nova.api.openstack import common -from nova.api.openstack.v2 import ips -from nova.api.openstack.v2.views import servers as views_servers +from nova.api.openstack.compute import ips +from nova.api.openstack.compute.views import servers as views_servers from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import compute @@ -36,7 +36,7 @@ from nova.scheduler import api as scheduler_api from nova import utils -LOG = logging.getLogger('nova.api.openstack.v2.servers') +LOG = logging.getLogger('nova.api.openstack.compute.servers') FLAGS = flags.FLAGS diff --git a/nova/api/openstack/v2/versions.py b/nova/api/openstack/compute/versions.py index 45b84f7c2..ddc31e9f0 100644 --- a/nova/api/openstack/v2/versions.py +++ b/nova/api/openstack/compute/versions.py @@ -19,7 +19,7 @@ from datetime import datetime from lxml import etree -from nova.api.openstack.v2.views import versions as views_versions +from nova.api.openstack.compute.views import versions as views_versions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil diff --git a/nova/api/openstack/v2/views/__init__.py b/nova/api/openstack/compute/views/__init__.py index e69de29bb..e69de29bb 100644 --- a/nova/api/openstack/v2/views/__init__.py +++ b/nova/api/openstack/compute/views/__init__.py diff --git a/nova/api/openstack/v2/views/addresses.py b/nova/api/openstack/compute/views/addresses.py index 6f518b11a..776ba9e59 100644 --- a/nova/api/openstack/v2/views/addresses.py +++ b/nova/api/openstack/compute/views/addresses.py @@ -23,7 +23,7 @@ from nova import log as logging FLAGS = flags.FLAGS -LOG = logging.getLogger('nova.api.openstack.v2.views.addresses') +LOG = logging.getLogger('nova.api.openstack.compute.views.addresses') class ViewBuilder(common.ViewBuilder): diff --git a/nova/api/openstack/v2/views/flavors.py b/nova/api/openstack/compute/views/flavors.py index 64284e406..64284e406 100644 --- a/nova/api/openstack/v2/views/flavors.py +++ b/nova/api/openstack/compute/views/flavors.py diff --git a/nova/api/openstack/v2/views/images.py b/nova/api/openstack/compute/views/images.py index c4cfe8031..c4cfe8031 100644 --- a/nova/api/openstack/v2/views/images.py +++ b/nova/api/openstack/compute/views/images.py diff --git a/nova/api/openstack/v2/views/limits.py b/nova/api/openstack/compute/views/limits.py index cff6781be..cff6781be 100644 --- a/nova/api/openstack/v2/views/limits.py +++ b/nova/api/openstack/compute/views/limits.py diff --git a/nova/api/openstack/v2/views/servers.py b/nova/api/openstack/compute/views/servers.py index 859bd48ab..6a1622a66 100644 --- a/nova/api/openstack/v2/views/servers.py +++ b/nova/api/openstack/compute/views/servers.py @@ -19,14 +19,14 @@ import hashlib from nova.api.openstack import common -from nova.api.openstack.v2.views import addresses as views_addresses -from nova.api.openstack.v2.views import flavors as views_flavors -from nova.api.openstack.v2.views import images as views_images +from nova.api.openstack.compute.views import addresses as views_addresses +from nova.api.openstack.compute.views import flavors as views_flavors +from nova.api.openstack.compute.views import images as views_images from nova import log as logging from nova import utils -LOG = logging.getLogger('nova.api.openstack.v2.views.servers') +LOG = logging.getLogger('nova.api.openstack.compute.views.servers') class ViewBuilder(common.ViewBuilder): diff --git a/nova/api/openstack/v2/views/versions.py b/nova/api/openstack/compute/views/versions.py index cb2fd9f4a..cb2fd9f4a 100644 --- a/nova/api/openstack/v2/views/versions.py +++ b/nova/api/openstack/compute/views/versions.py diff --git a/nova/api/openstack/v2/extensions.py b/nova/api/openstack/extensions.py index 0aa3146a9..6c49e8ace 100644 --- a/nova/api/openstack/v2/extensions.py +++ b/nova/api/openstack/extensions.py @@ -16,11 +16,12 @@ # License for the specific language governing permissions and limitations # under the License. +import os import routes import webob.dec import webob.exc -import nova.api.openstack.v2 +import nova.api.openstack from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import exception @@ -30,7 +31,7 @@ from nova import utils from nova import wsgi as base_wsgi -LOG = logging.getLogger('nova.api.openstack.v2.extensions') +LOG = logging.getLogger('nova.api.openstack.extensions') FLAGS = flags.FLAGS @@ -326,7 +327,7 @@ class ExtensionMiddleware(base_wsgi.Middleware): ext_mgr = ExtensionManager() self.ext_mgr = ext_mgr - mapper = nova.api.openstack.v2.ProjectMapper() + mapper = nova.api.openstack.ProjectMapper() # extended actions action_resources = self._action_ext_resources(application, ext_mgr, @@ -388,17 +389,6 @@ class ExtensionManager(object): def reset(cls): cls._ext_mgr = None - def __new__(cls): - if cls._ext_mgr is None: - LOG.audit(_('Initializing extension manager.')) - - cls._ext_mgr = super(ExtensionManager, cls).__new__(cls) - - cls._ext_mgr.extensions = {} - cls._ext_mgr._load_extensions() - - return cls._ext_mgr - def register(self, ext): # Do nothing if the extension doesn't check out if not self._check_extension(ext): @@ -483,7 +473,6 @@ class ExtensionManager(object): LOG.debug(_("Loading extension %s"), ext_factory) # Load the factory - factory = utils.import_class(ext_factory) # Call it @@ -493,7 +482,7 @@ class ExtensionManager(object): def _load_extensions(self): """Load extensions specified on the command line.""" - extensions = list(FLAGS.osapi_extension) + extensions = list(self.cls_list) for ext_factory in extensions: try: @@ -573,3 +562,62 @@ def wrap_errors(fn): except Exception, e: raise webob.exc.HTTPInternalServerError() return wrapped + + +def load_standard_extensions(ext_mgr, logger, path, package): + """Registers all standard API extensions.""" + + # Walk through all the modules in our directory... + our_dir = path[0] + for dirpath, dirnames, filenames in os.walk(our_dir): + # Compute the relative package name from the dirpath + relpath = os.path.relpath(dirpath, our_dir) + if relpath == '.': + relpkg = '' + else: + relpkg = '.%s' % '.'.join(relpath.split(os.sep)) + + # Now, consider each file in turn, only considering .py files + for fname in filenames: + root, ext = os.path.splitext(fname) + + # Skip __init__ and anything that's not .py + if ext != '.py' or root == '__init__': + continue + + # Try loading it + classname = ("%s%s.%s.%s%s" % + (package, relpkg, root, + root[0].upper(), root[1:])) + try: + ext_mgr.load_extension(classname) + except Exception as exc: + logger.warn(_('Failed to load extension %(classname)s: ' + '%(exc)s') % locals()) + + # Now, let's consider any subdirectories we may have... + subdirs = [] + for dname in dirnames: + # Skip it if it does not have __init__.py + if not os.path.exists(os.path.join(dirpath, dname, + '__init__.py')): + continue + + # If it has extension(), delegate... + ext_name = ("%s%s.%s.extension" % + (package, relpkg, dname)) + try: + ext = utils.import_class(ext_name) + except exception.ClassNotFound: + # extension() doesn't exist on it, so we'll explore + # the directory for ourselves + subdirs.append(dname) + else: + try: + ext(ext_mgr) + except Exception as exc: + logger.warn(_('Failed to load extension %(ext_name)s: ' + '%(exc)s') % locals()) + + # Update the list of directories we'll explore... + dirnames[:] = subdirs diff --git a/nova/api/openstack/v2/urlmap.py b/nova/api/openstack/urlmap.py index bae69198e..825a12af2 100644 --- a/nova/api/openstack/v2/urlmap.py +++ b/nova/api/openstack/urlmap.py @@ -28,7 +28,7 @@ _option_header_piece_re = re.compile(r';\s*([^\s;=]+|%s)\s*' r'(?:=\s*([^;]+|%s))?\s*' % (_quoted_string_re, _quoted_string_re)) -LOG = logging.getLogger('nova.api.openstack.v2.map') +LOG = logging.getLogger('nova.api.openstack.compute.map') def unquote_header_value(value): diff --git a/nova/api/openstack/v2/contrib/__init__.py b/nova/api/openstack/v2/contrib/__init__.py deleted file mode 100644 index d361dac9c..000000000 --- a/nova/api/openstack/v2/contrib/__init__.py +++ /dev/null @@ -1,90 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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. - -""" - -import os - -from nova import exception -from nova import log as logging -from nova import utils - - -LOG = logging.getLogger('nova.api.openstack.v2.contrib') - - -def standard_extensions(ext_mgr): - """Registers all standard API extensions.""" - - # Walk through all the modules in our directory... - our_dir = __path__[0] - for dirpath, dirnames, filenames in os.walk(our_dir): - # Compute the relative package name from the dirpath - relpath = os.path.relpath(dirpath, our_dir) - if relpath == '.': - relpkg = '' - else: - relpkg = '.%s' % '.'.join(relpath.split(os.sep)) - - # Now, consider each file in turn, only considering .py files - for fname in filenames: - root, ext = os.path.splitext(fname) - - # Skip __init__ and anything that's not .py - if ext != '.py' or root == '__init__': - continue - - # Try loading it - classname = ("%s%s.%s.%s%s" % - (__package__, relpkg, root, - root[0].upper(), root[1:])) - try: - ext_mgr.load_extension(classname) - except Exception as exc: - LOG.warn(_('Failed to load extension %(classname)s: ' - '%(exc)s') % locals()) - - # Now, let's consider any subdirectories we may have... - subdirs = [] - for dname in dirnames: - # Skip it if it does not have __init__.py - if not os.path.exists(os.path.join(dirpath, dname, - '__init__.py')): - continue - - # If it has extension(), delegate... - ext_name = ("%s%s.%s.extension" % - (__package__, relpkg, dname)) - try: - ext = utils.import_class(ext_name) - except exception.ClassNotFound: - # extension() doesn't exist on it, so we'll explore - # the directory for ourselves - subdirs.append(dname) - else: - try: - ext(ext_mgr) - except Exception as exc: - LOG.warn(_('Failed to load extension %(ext_name)s: ' - '%(exc)s') % locals()) - - # Update the list of directories we'll explore... - dirnames[:] = subdirs diff --git a/nova/api/openstack/volume/__init__.py b/nova/api/openstack/volume/__init__.py new file mode 100644 index 000000000..075b53c29 --- /dev/null +++ b/nova/api/openstack/volume/__init__.py @@ -0,0 +1,99 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. + +""" +WSGI middleware for OpenStack Volume API. +""" + +import routes +import webob.dec +import webob.exc + +import nova.api.openstack +from nova.api.openstack.volume import extensions +from nova.api.openstack.volume import snapshots +from nova.api.openstack.volume import types +from nova.api.openstack.volume import volumes +from nova.api.openstack.volume import versions +from nova.api.openstack import wsgi +from nova import flags +from nova import log as logging +from nova import wsgi as base_wsgi + + +LOG = logging.getLogger('nova.api.openstack.volume') +FLAGS = flags.FLAGS + + +class APIRouter(base_wsgi.Router): + """ + Routes requests on the OpenStack API to the appropriate controller + and method. + """ + + @classmethod + def factory(cls, global_config, **local_config): + """Simple paste factory, :class:`nova.wsgi.Router` doesn't have one""" + return cls() + + def __init__(self, ext_mgr=None): + if ext_mgr is None: + ext_mgr = extensions.ExtensionManager() + + mapper = nova.api.openstack.ProjectMapper() + self._setup_routes(mapper) + self._setup_ext_routes(mapper, ext_mgr) + super(APIRouter, self).__init__(mapper) + + def _setup_ext_routes(self, mapper, ext_mgr): + serializer = wsgi.ResponseSerializer( + {'application/xml': wsgi.XMLDictSerializer()}) + for resource in ext_mgr.get_resources(): + LOG.debug(_('Extended resource: %s'), + resource.collection) + if resource.serializer is None: + resource.serializer = serializer + + kargs = dict( + controller=wsgi.Resource( + resource.controller, resource.deserializer, + resource.serializer), + collection=resource.collection_actions, + member=resource.member_actions) + + if resource.parent: + kargs['parent_resource'] = resource.parent + + mapper.resource(resource.collection, resource.collection, **kargs) + + def _setup_routes(self, mapper): + mapper.connect("versions", "/", + controller=versions.create_resource(), + action='show') + + mapper.redirect("", "/") + + mapper.resource("volume", "volumes", + controller=volumes.create_resource(), + collection={'detail': 'GET'}) + + mapper.resource("type", "types", + controller=types.create_resource()) + + mapper.resource("snapshot", "snapshots", + controller=snapshots.create_resource()) diff --git a/nova/api/openstack/volume/contrib/__init__.py b/nova/api/openstack/volume/contrib/__init__.py new file mode 100644 index 000000000..58c0413ab --- /dev/null +++ b/nova/api/openstack/volume/contrib/__init__.py @@ -0,0 +1,32 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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 nova import log as logging +from nova.api.openstack import extensions + + +LOG = logging.getLogger('nova.api.openstack.volume.contrib') + + +def standard_extensions(ext_mgr): + extensions.load_standard_extensions(ext_mgr, LOG, __path__, __package__) diff --git a/nova/api/openstack/volume/extensions.py b/nova/api/openstack/volume/extensions.py new file mode 100644 index 000000000..d1007629e --- /dev/null +++ b/nova/api/openstack/volume/extensions.py @@ -0,0 +1,44 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from nova.api.openstack import extensions as base_extensions +from nova import flags +from nova import log as logging + + +LOG = logging.getLogger('nova.api.openstack.volume.extensions') +FLAGS = flags.FLAGS + + +class ExtensionManager(base_extensions.ExtensionManager): + def __new__(cls): + if cls._ext_mgr is None: + LOG.audit(_('Initializing extension manager.')) + + cls._ext_mgr = super(ExtensionManager, cls).__new__(cls) + + cls.cls_list = FLAGS.osapi_volume_extension + cls._ext_mgr.extensions = {} + cls._ext_mgr._load_extensions() + + return cls._ext_mgr + + +class ExtensionMiddleware(base_extensions.ExtensionMiddleware): + def __init__(self, application, ext_mgr=None): + ext_mgr = ExtensionManager() + super(ExtensionMiddleware, self).__init__(application, ext_mgr) diff --git a/nova/api/openstack/volume/snapshots.py b/nova/api/openstack/volume/snapshots.py new file mode 100644 index 000000000..f6ec3dc5f --- /dev/null +++ b/nova/api/openstack/volume/snapshots.py @@ -0,0 +1,183 @@ +# 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 snapshots api.""" + +from webob import exc +import webob + +from nova.api.openstack import common +from nova.api.openstack import extensions +from nova.api.openstack.compute import servers +from nova.api.openstack import wsgi +from nova.api.openstack import xmlutil +from nova import compute +from nova import exception +from nova import flags +from nova import log as logging +from nova import volume +from nova.volume import volume_types + + +LOG = logging.getLogger("nova.api.openstack.volume.snapshots") + + +FLAGS = flags.FLAGS + + +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 SnapshotsController(object): + """The Volumes API controller for the OpenStack API.""" + + def __init__(self): + self.volume_api = volume.API() + super(SnapshotsController, self).__init__() + + def show(self, req, id): + """Return data about the given snapshot.""" + context = req.environ['nova.context'] + + try: + vol = self.volume_api.get_snapshot(context, id) + except exception.NotFound: + return exc.HTTPNotFound() + + return {'snapshot': _translate_snapshot_detail_view(context, vol)} + + def delete(self, req, id): + """Delete a snapshot.""" + context = req.environ['nova.context'] + + LOG.audit(_("Delete snapshot with id: %s"), id, context=context) + + try: + self.volume_api.delete_snapshot(context, snapshot_id=id) + except exception.NotFound: + return exc.HTTPNotFound() + 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'] + + 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'] + + if not body: + return exc.HTTPUnprocessableEntity() + + snapshot = body['snapshot'] + volume_id = snapshot['volume_id'] + force = snapshot.get('force', False) + LOG.audit(_("Create snapshot from volume %s"), volume_id, + context=context) + + if force: + new_snapshot = self.volume_api.create_snapshot_force(context, + volume_id, + snapshot.get('display_name'), + snapshot.get('display_description')) + else: + new_snapshot = self.volume_api.create_snapshot(context, + volume_id, + snapshot.get('display_name'), + snapshot.get('display_description')) + + retval = _translate_snapshot_detail_view(context, new_snapshot) + + return {'snapshot': retval} + + +def make_snapshot(elem): + elem.set('id') + elem.set('status') + elem.set('size') + elem.set('createdAt') + elem.set('displayName') + elem.set('displayDescription') + elem.set('volumeId') + + +class SnapshotTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('snapshot', selector='snapshot') + make_snapshot(root) + return xmlutil.MasterTemplate(root, 1) + + +class SnapshotsTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('snapshots') + elem = xmlutil.SubTemplateElement(root, 'snapshot', + selector='snapshots') + make_snapshot(elem) + return xmlutil.MasterTemplate(root, 1) + + +class SnapshotSerializer(xmlutil.XMLTemplateSerializer): + def default(self): + return SnapshotTemplate() + + def index(self): + return SnapshotsTemplate() + + def detail(self): + return SnapshotsTemplate() + + +def create_resource(): + body_serializers = { + 'application/xml': SnapshotSerializer(), + } + serializer = wsgi.ResponseSerializer(body_serializers) + + return wsgi.Resource(SnapshotsController(), serializer=serializer) diff --git a/nova/api/openstack/volume/types.py b/nova/api/openstack/volume/types.py new file mode 100644 index 000000000..1c6a68b58 --- /dev/null +++ b/nova/api/openstack/volume/types.py @@ -0,0 +1,89 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Zadara Storage Inc. +# Copyright (c) 2011 OpenStack LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" The volume type & volume types extra specs extension""" + +from webob import exc + +from nova.api.openstack import extensions +from nova.api.openstack import wsgi +from nova.api.openstack import xmlutil +from nova import db +from nova import exception +from nova.volume import volume_types + + +class VolumeTypesController(object): + """ The volume types API controller for the Openstack API """ + + def index(self, req): + """ Returns the list of volume types """ + context = req.environ['nova.context'] + return volume_types.get_all_types(context) + + def show(self, req, id): + """ Return a single volume type item """ + context = req.environ['nova.context'] + + try: + vol_type = volume_types.get_volume_type(context, id) + except exception.NotFound or exception.ApiError: + raise exc.HTTPNotFound() + + return {'volume_type': vol_type} + + +def make_voltype(elem): + elem.set('id') + elem.set('name') + extra_specs = xmlutil.make_flat_dict('extra_specs', selector='extra_specs') + elem.append(extra_specs) + + +class VolumeTypeTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('volume_type', selector='volume_type') + make_voltype(root) + return xmlutil.MasterTemplate(root, 1) + + +class VolumeTypesTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('volume_types') + sel = lambda obj, do_raise=False: obj.values() + elem = xmlutil.SubTemplateElement(root, 'volume_type', selector=sel) + make_voltype(elem) + return xmlutil.MasterTemplate(root, 1) + + +class VolumeTypesSerializer(xmlutil.XMLTemplateSerializer): + def index(self): + return VolumeTypesTemplate() + + def default(self): + return VolumeTypeTemplate() + + +def create_resource(): + body_serializers = { + 'application/xml': VolumeTypesSerializer(), + } + serializer = wsgi.ResponseSerializer(body_serializers) + + deserializer = wsgi.RequestDeserializer() + + return wsgi.Resource(VolumeTypesController(), serializer=serializer) diff --git a/nova/api/openstack/volume/versions.py b/nova/api/openstack/volume/versions.py new file mode 100644 index 000000000..9a29e4adf --- /dev/null +++ b/nova/api/openstack/volume/versions.py @@ -0,0 +1,83 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from datetime import datetime + +from lxml import etree + +from nova.api.openstack.compute import versions +from nova.api.openstack.volume.views import versions as views_versions +from nova.api.openstack import wsgi +from nova.api.openstack import xmlutil + + +VERSIONS = { + "v1": { + "id": "v1", + "status": "CURRENT", + "updated": "2012-01-04T11:33:21Z", + "links": [ + { + "rel": "describedby", + "type": "application/pdf", + "href": "http://jorgew.github.com/block-storage-api/" + "content/os-block-storage-1.0.pdf", + }, + { + "rel": "describedby", + "type": "application/vnd.sun.wadl+xml", + #(anthony) FIXME + "href": "http://docs.rackspacecloud.com/" + "servers/api/v1.1/application.wadl", + }, + ], + "media-types": [ + { + "base": "application/xml", + "type": "application/vnd.openstack.volume+xml;version=1", + }, + { + "base": "application/json", + "type": "application/vnd.openstack.volume+json;version=1", + } + ], + } +} + + +class Versions(versions.Versions): + def dispatch(self, request, *args): + """Respond to a request for all OpenStack API versions.""" + builder = views_versions.get_view_builder(request) + if request.path == '/': + # List Versions + return builder.build_versions(VERSIONS) + else: + # Versions Multiple Choice + return builder.build_choices(VERSIONS, request) + + +class VolumeVersionV1(object): + @wsgi.serializers(xml=versions.VersionTemplate, + atom=versions.VersionAtomSerializer) + def show(self, req): + builder = views_versions.get_view_builder(req) + return builder.build_version(VERSIONS['v2.0']) + + +def create_resource(): + return wsgi.Resource(VolumeVersionV1()) diff --git a/nova/api/openstack/volume/views/__init__.py b/nova/api/openstack/volume/views/__init__.py new file mode 100644 index 000000000..d65c689a8 --- /dev/null +++ b/nova/api/openstack/volume/views/__init__.py @@ -0,0 +1,16 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. diff --git a/nova/api/openstack/volume/views/versions.py b/nova/api/openstack/volume/views/versions.py new file mode 100644 index 000000000..e446a4b64 --- /dev/null +++ b/nova/api/openstack/volume/views/versions.py @@ -0,0 +1,37 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010-2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy +import os + +from nova.api.openstack.compute.views import versions as compute_views + + +def get_view_builder(req): + base_url = req.application_url + return ViewBuilder(base_url) + + +class ViewBuilder(compute_views.ViewBuilder): + def generate_href(self, path=None): + """Create an url that refers to a specific version_number.""" + version_number = 'v1' + if path: + path = path.strip('/') + return os.path.join(self.base_url, version_number, path) + else: + return os.path.join(self.base_url, version_number) + '/' diff --git a/nova/api/openstack/volume/volumes.py b/nova/api/openstack/volume/volumes.py new file mode 100644 index 000000000..5e16f4fde --- /dev/null +++ b/nova/api/openstack/volume/volumes.py @@ -0,0 +1,254 @@ +# 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 api.""" + +from webob import exc +import webob + +from nova.api.openstack import common +from nova.api.openstack.compute import servers +from nova.api.openstack import wsgi +from nova.api.openstack import xmlutil +from nova import exception +from nova import flags +from nova import log as logging +from nova import volume +from nova.volume import volume_types + + +LOG = logging.getLogger("nova.api.openstack.volume.volumes") + + +FLAGS = flags.FLAGS + + +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': + d['attachments'] = [_translate_attachment_detail_view(context, vol)] + 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.audit(_("vol=%s"), vol, context=context) + + if vol.get('volume_metadata'): + meta_dict = {} + for i in vol['volume_metadata']: + meta_dict[i['key']] = i['value'] + d['metadata'] = meta_dict + else: + d['metadata'] = {} + + return d + + +class VolumeController(object): + """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'] + + try: + vol = self.volume_api.get(context, id) + except exception.NotFound: + raise exc.HTTPNotFound() + + return {'volume': _translate_volume_detail_view(context, vol)} + + def delete(self, req, id): + """Delete a volume.""" + context = req.environ['nova.context'] + + LOG.audit(_("Delete volume with id: %s"), id, context=context) + + try: + self.volume_api.delete(context, volume_id=id) + except exception.NotFound: + raise exc.HTTPNotFound() + 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'] + + 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'] + + if not body: + raise exc.HTTPUnprocessableEntity() + + vol = body['volume'] + size = vol['size'] + LOG.audit(_("Create volume of %s GB"), size, context=context) + + vol_type = vol.get('volume_type', None) + if vol_type: + try: + vol_type = volume_types.get_volume_type_by_name(context, + vol_type) + except exception.NotFound: + raise exc.HTTPNotFound() + + metadata = vol.get('metadata', None) + + new_volume = self.volume_api.create(context, size, + vol.get('snapshot_id'), + vol.get('display_name'), + vol.get('display_description'), + volume_type=vol_type, + metadata=metadata) + + # Work around problem that instance is lazy-loaded... + new_volume = self.volume_api.get(context, new_volume['id']) + + retval = _translate_volume_detail_view(context, new_volume) + + return {'volume': retval} + + +class VolumeAttachmentTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('volumeAttachment', + selector='volumeAttachment') + make_attachment(root) + return xmlutil.MasterTemplate(root, 1) + + +class VolumeAttachmentsTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('volumeAttachments') + elem = xmlutil.SubTemplateElement(root, 'volumeAttachment', + selector='volumeAttachments') + make_attachment(elem) + return xmlutil.MasterTemplate(root, 1) + + +class VolumeAttachmentSerializer(xmlutil.XMLTemplateSerializer): + def default(self): + return VolumeAttachmentTemplate() + + def index(self): + return VolumeAttachmentsTemplate() + + +def make_attachment(elem): + elem.set('id') + elem.set('serverId') + elem.set('volumeId') + elem.set('device') + + +def make_volume(elem): + elem.set('id') + elem.set('status') + elem.set('size') + elem.set('availabilityZone') + elem.set('createdAt') + elem.set('displayName') + elem.set('displayDescription') + elem.set('volumeType') + elem.set('snapshotId') + + attachments = xmlutil.SubTemplateElement(elem, 'attachments') + attachment = xmlutil.SubTemplateElement(attachments, 'attachment', + selector='attachments') + make_attachment(attachment) + + metadata = xmlutil.make_flat_dict('metadata') + elem.append(metadata) + + +class VolumeTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('volume', selector='volume') + make_volume(root) + return xmlutil.MasterTemplate(root, 1) + + +class VolumesTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('volumes') + elem = xmlutil.SubTemplateElement(root, 'volume', selector='volumes') + make_volume(elem) + return xmlutil.MasterTemplate(root, 1) + + +class VolumeSerializer(xmlutil.XMLTemplateSerializer): + def default(self): + return VolumeTemplate() + + def index(self): + return VolumesTemplate() + + def detail(self): + return VolumesTemplate() + + +def create_resource(): + body_serializers = { + 'application/xml': VolumeSerializer(), + } + serializer = wsgi.ResponseSerializer(body_serializers) + + deserializer = wsgi.RequestDeserializer() + + return wsgi.Resource(VolumeController(), serializer=serializer) diff --git a/nova/api/openstack/xmlutil.py b/nova/api/openstack/xmlutil.py index 90861ad4f..4b64d4a8f 100644 --- a/nova/api/openstack/xmlutil.py +++ b/nova/api/openstack/xmlutil.py @@ -31,9 +31,9 @@ XMLNS_ATOM = 'http://www.w3.org/2005/Atom' def validate_schema(xml, schema_name): if isinstance(xml, str): xml = etree.fromstring(xml) - base_path = 'nova/api/openstack/v2/schemas/v1.1/' + base_path = 'nova/api/openstack/compute/schemas/v1.1/' if schema_name in ('atom', 'atom-link'): - base_path = 'nova/api/openstack/v2/schemas/' + base_path = 'nova/api/openstack/compute/schemas/' schema_path = os.path.join(utils.novadir(), '%s%s.rng' % (base_path, schema_name)) schema_doc = etree.parse(schema_path) diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 3d984f1bf..f6c88e082 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -827,7 +827,7 @@ class AuthManager(object): 's3': 'http://%s:%s' % (s3_host, FLAGS.s3_port), 'os': '%s://%s:%s%s' % (FLAGS.osapi_scheme, ec2_host, - FLAGS.osapi_port, + FLAGS.osapi_compute_listen_port, FLAGS.osapi_path), 'user': user.name, 'nova': FLAGS.ca_file, diff --git a/nova/common/cfg.py b/nova/common/cfg.py index 58d17d99f..bfb7b8a19 100644 --- a/nova/common/cfg.py +++ b/nova/common/cfg.py @@ -32,14 +32,14 @@ Options can be strings, integers, floats, booleans, lists or 'multi strings': enabled_apis_opt = \ cfg.ListOpt('enabled_apis', - default=['ec2', 'osapi'], + default=['ec2', 'osapi_compute'], help='List of APIs to enable by default') DEFAULT_EXTENSIONS = [ 'nova.api.openstack.contrib.standard_extensions' ] - osapi_extension_opt = \ - cfg.MultiStrOpt('osapi_extension', + osapi_compute_extension_opt = \ + cfg.MultiStrOpt('osapi_compute_extension', default=DEFAULT_EXTENSIONS) Option schemas are registered with with the config manager at runtime, but @@ -55,7 +55,7 @@ before the option is referenced: ... def _load_extensions(self): - for ext_factory in self.conf.osapi_extension: + for ext_factory in self.conf.osapi_compute_extension: .... A common usage pattern is for each option schema to be defined in the module or diff --git a/nova/flags.py b/nova/flags.py index a51508c97..b566f303a 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -309,19 +309,21 @@ DEFINE_integer('rabbit_max_retries', 0, 'maximum rabbit connection attempts (0=try forever)') DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to') DEFINE_boolean('rabbit_durable_queues', False, 'use durable queues') -DEFINE_list('enabled_apis', ['ec2', 'osapi', 'metadata'], +DEFINE_list('enabled_apis', + ['ec2', 'osapi_compute', 'osapi_volume', 'metadata'], 'list of APIs to enable by default') DEFINE_string('ec2_host', '$my_ip', 'ip of api server') DEFINE_string('ec2_dmz_host', '$my_ip', 'internal ip of api server') DEFINE_integer('ec2_port', 8773, 'cloud controller port') DEFINE_string('ec2_scheme', 'http', 'prefix for ec2') DEFINE_string('ec2_path', '/services/Cloud', 'suffix for ec2') -DEFINE_multistring('osapi_extension', - ['nova.api.openstack.v2.contrib.standard_extensions'], - 'osapi extension to load') -DEFINE_string('osapi_host', '$my_ip', 'ip of api server') +DEFINE_multistring('osapi_compute_extension', + ['nova.api.openstack.compute.contrib.standard_extensions'], + 'osapi compute extension to load') +DEFINE_multistring('osapi_volume_extension', + ['nova.api.openstack.volume.contrib.standard_extensions'], + 'osapi volume extension to load') DEFINE_string('osapi_scheme', 'http', 'prefix for openstack') -DEFINE_integer('osapi_port', 8774, 'OpenStack API port') DEFINE_string('osapi_path', '/v1.1/', 'suffix for openstack') DEFINE_integer('osapi_max_limit', 1000, 'max number of items returned in a collection response') diff --git a/nova/service.py b/nova/service.py index 95399f650..bfe6d1d54 100644 --- a/nova/service.py +++ b/nova/service.py @@ -48,9 +48,10 @@ flags.DEFINE_integer('periodic_interval', 60, flags.DEFINE_string('ec2_listen', "0.0.0.0", 'IP address for EC2 API to listen') flags.DEFINE_integer('ec2_listen_port', 8773, 'port for ec2 api to listen') -flags.DEFINE_string('osapi_listen', "0.0.0.0", +flags.DEFINE_string('osapi_compute_listen', "0.0.0.0", 'IP address for OpenStack API to listen') -flags.DEFINE_integer('osapi_listen_port', 8774, 'port for os api to listen') +flags.DEFINE_integer('osapi_compute_listen_port', 8774, + 'list port for osapi compute') flags.DEFINE_string('metadata_manager', 'nova.api.manager.MetadataManager', 'OpenStack metadata service manager') flags.DEFINE_string('metadata_listen', "0.0.0.0", @@ -59,6 +60,10 @@ flags.DEFINE_integer('metadata_listen_port', 8775, 'port for metadata api to listen') flags.DEFINE_string('api_paste_config', "api-paste.ini", 'File name for the paste.deploy config for nova-api') +flags.DEFINE_string('osapi_volume_listen', "0.0.0.0", + 'IP address for OpenStack Volume API to listen') +flags.DEFINE_integer('osapi_volume_listen_port', 8776, + 'port for os volume api to listen') class Launcher(object): diff --git a/nova/tests/api/openstack/v2/__init__.py b/nova/tests/api/openstack/compute/__init__.py index 00fcfbb00..00fcfbb00 100644 --- a/nova/tests/api/openstack/v2/__init__.py +++ b/nova/tests/api/openstack/compute/__init__.py diff --git a/nova/tests/api/openstack/v2/contrib/__init__.py b/nova/tests/api/openstack/compute/contrib/__init__.py index 848908a95..848908a95 100644 --- a/nova/tests/api/openstack/v2/contrib/__init__.py +++ b/nova/tests/api/openstack/compute/contrib/__init__.py diff --git a/nova/tests/api/openstack/v2/contrib/test_accounts.py b/nova/tests/api/openstack/compute/contrib/test_accounts.py index f799853dc..dbf0e2600 100644 --- a/nova/tests/api/openstack/v2/contrib/test_accounts.py +++ b/nova/tests/api/openstack/compute/contrib/test_accounts.py @@ -20,7 +20,7 @@ from lxml import etree import webob from nova import test -from nova.api.openstack.v2.contrib import accounts +from nova.api.openstack.compute.contrib import accounts from nova.auth.manager import User from nova.tests.api.openstack import fakes diff --git a/nova/tests/api/openstack/v2/contrib/test_admin_actions.py b/nova/tests/api/openstack/compute/contrib/test_admin_actions.py index e3c5e6b08..f8a5d1103 100644 --- a/nova/tests/api/openstack/v2/contrib/test_admin_actions.py +++ b/nova/tests/api/openstack/compute/contrib/test_admin_actions.py @@ -17,8 +17,8 @@ import json import webob -from nova.api.openstack import v2 -from nova.api.openstack.v2 import extensions +from nova.api.openstack import compute as compute_api +from nova.api.openstack.compute import extensions from nova.api.openstack import wsgi from nova import compute from nova import exception @@ -123,7 +123,7 @@ class CreateBackupTests(test.TestCase): self.backup_stubs = fakes.stub_out_compute_api_backup(self.stubs) self.flags(allow_admin_api=True) - router = v2.APIRouter() + router = compute_api.APIRouter() ext_middleware = extensions.ExtensionMiddleware(router) self.app = wsgi.LazySerializationMiddleware(ext_middleware) diff --git a/nova/tests/api/openstack/v2/contrib/test_cloudpipe.py b/nova/tests/api/openstack/compute/contrib/test_cloudpipe.py index b2a5d308a..7c8a8b09f 100644 --- a/nova/tests/api/openstack/v2/contrib/test_cloudpipe.py +++ b/nova/tests/api/openstack/compute/contrib/test_cloudpipe.py @@ -20,9 +20,9 @@ import webob from lxml import etree from nova.api import auth -from nova.api.openstack import v2 -from nova.api.openstack.v2 import wsgi -from nova.api.openstack.v2.contrib import cloudpipe +from nova.api.openstack import compute +from nova.api.openstack.compute import wsgi +from nova.api.openstack.compute.contrib import cloudpipe from nova.auth import manager from nova.cloudpipe import pipelib from nova import context @@ -112,7 +112,7 @@ class CloudpipeTest(test.TestCase): super(CloudpipeTest, self).setUp() self.flags(allow_admin_api=True) self.app = fakes.wsgi_app() - inner_app = v2.APIRouter() + inner_app = compute.APIRouter() self.context = context.RequestContext('fake', 'fake', is_admin=True) self.app = auth.InjectContext(self.context, inner_app) route = inner_app.map.match('/1234/os-cloudpipe') diff --git a/nova/tests/api/openstack/v2/contrib/test_console_output.py b/nova/tests/api/openstack/compute/contrib/test_console_output.py index ad22ff4cf..ad22ff4cf 100644 --- a/nova/tests/api/openstack/v2/contrib/test_console_output.py +++ b/nova/tests/api/openstack/compute/contrib/test_console_output.py diff --git a/nova/tests/api/openstack/v2/contrib/test_createserverext.py b/nova/tests/api/openstack/compute/contrib/test_createserverext.py index 2393780a2..2393780a2 100644 --- a/nova/tests/api/openstack/v2/contrib/test_createserverext.py +++ b/nova/tests/api/openstack/compute/contrib/test_createserverext.py diff --git a/nova/tests/api/openstack/v2/contrib/test_deferred_delete.py b/nova/tests/api/openstack/compute/contrib/test_deferred_delete.py index 222e62577..4addcf5c8 100644 --- a/nova/tests/api/openstack/v2/contrib/test_deferred_delete.py +++ b/nova/tests/api/openstack/compute/contrib/test_deferred_delete.py @@ -18,7 +18,7 @@ import mox import webob -from nova.api.openstack.v2.contrib import deferred_delete +from nova.api.openstack.compute.contrib import deferred_delete from nova import compute from nova import exception from nova import test diff --git a/nova/tests/api/openstack/v2/contrib/test_disk_config.py b/nova/tests/api/openstack/compute/contrib/test_disk_config.py index 95cfd3747..bd995fd3d 100644 --- a/nova/tests/api/openstack/v2/contrib/test_disk_config.py +++ b/nova/tests/api/openstack/compute/contrib/test_disk_config.py @@ -17,8 +17,8 @@ import datetime -from nova.api.openstack import v2 -from nova.api.openstack.v2 import extensions +from nova.api.openstack import compute +from nova.api.openstack.compute import extensions from nova.api.openstack import wsgi import nova.db.api from nova import flags @@ -117,7 +117,7 @@ class DiskConfigTestCase(test.TestCase): self.stubs.Set(nova.db, 'instance_create', fake_instance_create) - app = v2.APIRouter() + app = compute.APIRouter() app = extensions.ExtensionMiddleware(app) app = wsgi.LazySerializationMiddleware(app) self.app = app diff --git a/nova/tests/api/openstack/v2/contrib/test_extendedstatus.py b/nova/tests/api/openstack/compute/contrib/test_extendedstatus.py index dc7f0cefa..dc7f0cefa 100644 --- a/nova/tests/api/openstack/v2/contrib/test_extendedstatus.py +++ b/nova/tests/api/openstack/compute/contrib/test_extendedstatus.py diff --git a/nova/tests/api/openstack/v2/contrib/test_flavors_extra_specs.py b/nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py index add8c627d..94aac9621 100644 --- a/nova/tests/api/openstack/v2/contrib/test_flavors_extra_specs.py +++ b/nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py @@ -18,7 +18,7 @@ import webob from nova.api.openstack import wsgi -from nova.api.openstack.v2.contrib import flavorextraspecs +from nova.api.openstack.compute.contrib import flavorextraspecs from nova import test from nova.tests.api.openstack import fakes import nova.wsgi diff --git a/nova/tests/api/openstack/v2/contrib/test_floating_ip_dns.py b/nova/tests/api/openstack/compute/contrib/test_floating_ip_dns.py index 58fdb4788..3107e50c4 100644 --- a/nova/tests/api/openstack/v2/contrib/test_floating_ip_dns.py +++ b/nova/tests/api/openstack/compute/contrib/test_floating_ip_dns.py @@ -19,8 +19,8 @@ from lxml import etree import webob import urllib -from nova.api.openstack.v2.contrib import floating_ips -from nova.api.openstack.v2.contrib import floating_ip_dns +from nova.api.openstack.compute.contrib import floating_ips +from nova.api.openstack.compute.contrib import floating_ip_dns from nova import context from nova import db from nova import network diff --git a/nova/tests/api/openstack/v2/contrib/test_floating_ip_pools.py b/nova/tests/api/openstack/compute/contrib/test_floating_ip_pools.py index d061f9af3..ffe84a1ba 100644 --- a/nova/tests/api/openstack/v2/contrib/test_floating_ip_pools.py +++ b/nova/tests/api/openstack/compute/contrib/test_floating_ip_pools.py @@ -15,7 +15,7 @@ from lxml import etree -from nova.api.openstack.v2.contrib import floating_ip_pools +from nova.api.openstack.compute.contrib import floating_ip_pools from nova import context from nova import network from nova import test diff --git a/nova/tests/api/openstack/v2/contrib/test_floating_ips.py b/nova/tests/api/openstack/compute/contrib/test_floating_ips.py index dae58aa14..fe5444419 100644 --- a/nova/tests/api/openstack/v2/contrib/test_floating_ips.py +++ b/nova/tests/api/openstack/compute/contrib/test_floating_ips.py @@ -17,7 +17,7 @@ from lxml import etree import webob -from nova.api.openstack.v2.contrib import floating_ips +from nova.api.openstack.compute.contrib import floating_ips from nova import context from nova import db from nova import network diff --git a/nova/tests/api/openstack/v2/contrib/test_hosts.py b/nova/tests/api/openstack/compute/contrib/test_hosts.py index a954890ba..e6a91477e 100644 --- a/nova/tests/api/openstack/v2/contrib/test_hosts.py +++ b/nova/tests/api/openstack/compute/contrib/test_hosts.py @@ -21,7 +21,7 @@ from nova import exception from nova import flags from nova import log as logging from nova import test -from nova.api.openstack.v2.contrib import hosts as os_hosts +from nova.api.openstack.compute.contrib import hosts as os_hosts from nova.scheduler import api as scheduler_api diff --git a/nova/tests/api/openstack/v2/contrib/test_keypairs.py b/nova/tests/api/openstack/compute/contrib/test_keypairs.py index 6ecd3a86c..4c252e176 100644 --- a/nova/tests/api/openstack/v2/contrib/test_keypairs.py +++ b/nova/tests/api/openstack/compute/contrib/test_keypairs.py @@ -19,7 +19,7 @@ import webob from lxml import etree from nova.api.openstack import wsgi -from nova.api.openstack.v2.contrib import keypairs +from nova.api.openstack.compute.contrib import keypairs from nova import context from nova import db from nova import test diff --git a/nova/tests/api/openstack/v2/contrib/test_multinic_xs.py b/nova/tests/api/openstack/compute/contrib/test_multinic_xs.py index 0d9b6e3ce..0d9b6e3ce 100644 --- a/nova/tests/api/openstack/v2/contrib/test_multinic_xs.py +++ b/nova/tests/api/openstack/compute/contrib/test_multinic_xs.py diff --git a/nova/tests/api/openstack/v2/contrib/test_networks.py b/nova/tests/api/openstack/compute/contrib/test_networks.py index 04bd82e2c..0eefca652 100644 --- a/nova/tests/api/openstack/v2/contrib/test_networks.py +++ b/nova/tests/api/openstack/compute/contrib/test_networks.py @@ -18,7 +18,7 @@ import copy import webob -from nova.api.openstack.v2.contrib import networks +from nova.api.openstack.compute.contrib import networks from nova import context from nova import exception from nova import test diff --git a/nova/tests/api/openstack/v2/contrib/test_quotas.py b/nova/tests/api/openstack/compute/contrib/test_quotas.py index 9bcfc7ded..29b01e117 100644 --- a/nova/tests/api/openstack/v2/contrib/test_quotas.py +++ b/nova/tests/api/openstack/compute/contrib/test_quotas.py @@ -19,7 +19,7 @@ import webob from lxml import etree from nova.api.openstack import wsgi -from nova.api.openstack.v2.contrib import quotas +from nova.api.openstack.compute.contrib import quotas from nova import context from nova import test from nova.tests.api.openstack import fakes diff --git a/nova/tests/api/openstack/v2/contrib/test_rescue.py b/nova/tests/api/openstack/compute/contrib/test_rescue.py index 9a3706173..9a3706173 100644 --- a/nova/tests/api/openstack/v2/contrib/test_rescue.py +++ b/nova/tests/api/openstack/compute/contrib/test_rescue.py diff --git a/nova/tests/api/openstack/v2/contrib/test_security_groups.py b/nova/tests/api/openstack/compute/contrib/test_security_groups.py index 7848ef9bb..e4ea99f40 100644 --- a/nova/tests/api/openstack/v2/contrib/test_security_groups.py +++ b/nova/tests/api/openstack/compute/contrib/test_security_groups.py @@ -20,7 +20,7 @@ from lxml import etree import mox import webob -from nova.api.openstack.v2.contrib import security_groups +from nova.api.openstack.compute.contrib import security_groups from nova.api.openstack import wsgi import nova.db from nova import exception diff --git a/nova/tests/api/openstack/v2/contrib/test_server_action_list.py b/nova/tests/api/openstack/compute/contrib/test_server_action_list.py index d943376b3..aa5a7275c 100644 --- a/nova/tests/api/openstack/v2/contrib/test_server_action_list.py +++ b/nova/tests/api/openstack/compute/contrib/test_server_action_list.py @@ -19,9 +19,9 @@ import unittest from lxml import etree -from nova.api.openstack import v2 -from nova.api.openstack.v2.contrib import server_action_list -from nova.api.openstack.v2 import extensions +from nova.api.openstack import compute +from nova.api.openstack.compute import extensions +from nova.api.openstack.compute.contrib import server_action_list from nova.api.openstack import wsgi import nova.compute from nova import test @@ -53,7 +53,7 @@ class ServerActionsTest(test.TestCase): self.stubs.Set(nova.compute.API, 'get', fake_instance_get) self.compute_api = nova.compute.API() - self.router = v2.APIRouter() + self.router = compute.APIRouter() ext_middleware = extensions.ExtensionMiddleware(self.router) self.app = wsgi.LazySerializationMiddleware(ext_middleware) diff --git a/nova/tests/api/openstack/v2/contrib/test_server_diagnostics.py b/nova/tests/api/openstack/compute/contrib/test_server_diagnostics.py index 2e2850f32..69aabdc26 100644 --- a/nova/tests/api/openstack/v2/contrib/test_server_diagnostics.py +++ b/nova/tests/api/openstack/compute/contrib/test_server_diagnostics.py @@ -18,9 +18,9 @@ import unittest from lxml import etree -from nova.api.openstack import v2 -from nova.api.openstack.v2.contrib import server_diagnostics -from nova.api.openstack.v2 import extensions +from nova.api.openstack import compute +from nova.api.openstack.compute import extensions +from nova.api.openstack.compute.contrib import server_diagnostics from nova.api.openstack import wsgi import nova.compute from nova import test @@ -47,7 +47,7 @@ class ServerDiagnosticsTest(test.TestCase): self.stubs.Set(nova.compute.API, 'get', fake_instance_get) self.compute_api = nova.compute.API() - self.router = v2.APIRouter() + self.router = compute.APIRouter() ext_middleware = extensions.ExtensionMiddleware(self.router) self.app = wsgi.LazySerializationMiddleware(ext_middleware) diff --git a/nova/tests/api/openstack/v2/contrib/test_simple_tenant_usage.py b/nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py index 7a1f2b35c..3ff12bf3a 100644 --- a/nova/tests/api/openstack/v2/contrib/test_simple_tenant_usage.py +++ b/nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py @@ -21,7 +21,7 @@ import json from lxml import etree import webob -from nova.api.openstack.v2.contrib import simple_tenant_usage +from nova.api.openstack.compute.contrib import simple_tenant_usage from nova.compute import api from nova import context from nova import flags diff --git a/nova/tests/api/openstack/v2/contrib/test_snapshots.py b/nova/tests/api/openstack/compute/contrib/test_snapshots.py index 5b585db82..69784e516 100644 --- a/nova/tests/api/openstack/v2/contrib/test_snapshots.py +++ b/nova/tests/api/openstack/compute/contrib/test_snapshots.py @@ -20,7 +20,7 @@ import stubout from lxml import etree import webob -from nova.api.openstack.v2.contrib import volumes +from nova.api.openstack.compute.contrib import volumes from nova import context from nova import exception from nova import flags diff --git a/nova/tests/api/openstack/v2/contrib/test_users.py b/nova/tests/api/openstack/compute/contrib/test_users.py index ace243f58..5895f4f66 100644 --- a/nova/tests/api/openstack/v2/contrib/test_users.py +++ b/nova/tests/api/openstack/compute/contrib/test_users.py @@ -15,7 +15,7 @@ from lxml import etree -from nova.api.openstack.v2.contrib import users +from nova.api.openstack.compute.contrib import users from nova.auth.manager import User, Project from nova import test from nova.tests.api.openstack import fakes diff --git a/nova/tests/api/openstack/v2/contrib/test_virtual_interfaces.py b/nova/tests/api/openstack/compute/contrib/test_virtual_interfaces.py index 3530e68fd..d9f41f07b 100644 --- a/nova/tests/api/openstack/v2/contrib/test_virtual_interfaces.py +++ b/nova/tests/api/openstack/compute/contrib/test_virtual_interfaces.py @@ -18,7 +18,7 @@ import json from lxml import etree import webob -from nova.api.openstack.v2.contrib import virtual_interfaces +from nova.api.openstack.compute.contrib import virtual_interfaces from nova.api.openstack import wsgi from nova import network from nova import test diff --git a/nova/tests/api/openstack/v2/contrib/test_volume_types.py b/nova/tests/api/openstack/compute/contrib/test_volume_types.py index de3cf86e1..fdd2214e3 100644 --- a/nova/tests/api/openstack/v2/contrib/test_volume_types.py +++ b/nova/tests/api/openstack/compute/contrib/test_volume_types.py @@ -16,7 +16,7 @@ from lxml import etree import webob -from nova.api.openstack.v2.contrib import volumetypes +from nova.api.openstack.compute.contrib import volumetypes from nova import exception from nova import test from nova import log as logging @@ -24,7 +24,7 @@ from nova.volume import volume_types from nova.tests.api.openstack import fakes -LOG = logging.getLogger('nova.tests.api.openstack.v2.contrib.' +LOG = logging.getLogger('nova.tests.api.openstack.compute.contrib.' 'test_volume_types') last_param = {} diff --git a/nova/tests/api/openstack/v2/contrib/test_volume_types_extra_specs.py b/nova/tests/api/openstack/compute/contrib/test_volume_types_extra_specs.py index 81e57cee9..e9c4034f0 100644 --- a/nova/tests/api/openstack/v2/contrib/test_volume_types_extra_specs.py +++ b/nova/tests/api/openstack/compute/contrib/test_volume_types_extra_specs.py @@ -20,7 +20,7 @@ from lxml import etree import webob -from nova.api.openstack.v2.contrib import volumetypes +from nova.api.openstack.compute.contrib import volumetypes from nova import test from nova.tests.api.openstack import fakes import nova.wsgi diff --git a/nova/tests/api/openstack/v2/contrib/test_volumes.py b/nova/tests/api/openstack/compute/contrib/test_volumes.py index a5585bd64..f32f4d867 100644 --- a/nova/tests/api/openstack/v2/contrib/test_volumes.py +++ b/nova/tests/api/openstack/compute/contrib/test_volumes.py @@ -20,7 +20,7 @@ from lxml import etree import webob import nova -from nova.api.openstack.v2.contrib import volumes +from nova.api.openstack.compute.contrib import volumes from nova.compute import instance_types from nova import flags from nova import test diff --git a/nova/tests/api/openstack/v2/contrib/test_vsa.py b/nova/tests/api/openstack/compute/contrib/test_vsa.py index fa5127db6..e19aeedba 100644 --- a/nova/tests/api/openstack/v2/contrib/test_vsa.py +++ b/nova/tests/api/openstack/compute/contrib/test_vsa.py @@ -20,7 +20,8 @@ from lxml import etree import stubout import webob -from nova.api.openstack.v2.contrib import virtual_storage_arrays as vsa_ext +from nova.api.openstack.compute.contrib\ + import virtual_storage_arrays as vsa_ext from nova import context import nova.db from nova import exception @@ -34,7 +35,7 @@ from nova import vsa FLAGS = flags.FLAGS -LOG = logging.getLogger('nova.tests.api.openstack.v2.contrib.test_vsa') +LOG = logging.getLogger('nova.tests.api.openstack.compute.contrib.test_vsa') last_param = {} diff --git a/nova/tests/api/openstack/v2/contrib/test_zones.py b/nova/tests/api/openstack/compute/contrib/test_zones.py index a44a7c82e..e23ea85e6 100644 --- a/nova/tests/api/openstack/v2/contrib/test_zones.py +++ b/nova/tests/api/openstack/compute/contrib/test_zones.py @@ -18,7 +18,7 @@ import json from lxml import etree -from nova.api.openstack.v2.contrib import zones +from nova.api.openstack.compute.contrib import zones from nova.api.openstack import xmlutil from nova import crypto import nova.db diff --git a/nova/tests/api/openstack/v2/extensions/__init__.py b/nova/tests/api/openstack/compute/extensions/__init__.py index 848908a95..848908a95 100644 --- a/nova/tests/api/openstack/v2/extensions/__init__.py +++ b/nova/tests/api/openstack/compute/extensions/__init__.py diff --git a/nova/tests/api/openstack/v2/extensions/foxinsocks.py b/nova/tests/api/openstack/compute/extensions/foxinsocks.py index ba1508668..302fe7ddf 100644 --- a/nova/tests/api/openstack/v2/extensions/foxinsocks.py +++ b/nova/tests/api/openstack/compute/extensions/foxinsocks.py @@ -17,7 +17,7 @@ import webob.exc -from nova.api.openstack.v2 import extensions +from nova.api.openstack import extensions class FoxInSocksController(object): diff --git a/nova/tests/api/openstack/v2/test_api.py b/nova/tests/api/openstack/compute/test_api.py index d09bb0cfa..15768ff33 100644 --- a/nova/tests/api/openstack/v2/test_api.py +++ b/nova/tests/api/openstack/compute/test_api.py @@ -23,8 +23,9 @@ import webob.dec from webob import Request from nova import test -from nova.api.openstack import v2 -from nova.api.openstack.v2 import wsgi +from nova.api import openstack as openstack_api +from nova.api.openstack import compute +from nova.api.openstack.compute import wsgi from nova.tests.api.openstack import fakes @@ -32,7 +33,7 @@ class APITest(test.TestCase): def _wsgi_app(self, inner_app): # simpler version of the app than fakes.wsgi_app - return v2.FaultWrapper(inner_app) + return openstack_api.FaultWrapper(inner_app) def test_malformed_json(self): req = webob.Request.blank('/') diff --git a/nova/tests/api/openstack/v2/test_auth.py b/nova/tests/api/openstack/compute/test_auth.py index d1dbc3319..bc83a8b96 100644 --- a/nova/tests/api/openstack/v2/test_auth.py +++ b/nova/tests/api/openstack/compute/test_auth.py @@ -20,9 +20,9 @@ import datetime import webob import webob.dec -import nova.api.openstack.v2 -import nova.api.openstack.v2.auth +import nova.api.openstack.compute import nova.auth.manager +from nova.api.openstack import auth from nova import context from nova import db from nova import test @@ -33,7 +33,7 @@ class Test(test.TestCase): def setUp(self): super(Test, self).setUp() - self.stubs.Set(nova.api.openstack.v2.auth.AuthMiddleware, + self.stubs.Set(auth.AuthMiddleware, '__init__', fakes.fake_auth_init) self.stubs.Set(context, 'RequestContext', fakes.FakeRequestContext) fakes.FakeAuthManager.clear_fakes() @@ -80,7 +80,8 @@ class Test(test.TestCase): self.assertEqual(result.headers['X-Storage-Url'], "") token = result.headers['X-Auth-Token'] - self.stubs.Set(nova.api.openstack.v2, 'APIRouter', fakes.FakeRouter) + self.stubs.Set(nova.api.openstack.compute, 'APIRouter', + fakes.FakeRouter) req = webob.Request.blank('/v2/user1_project') req.headers['X-Auth-Token'] = token result = req.get_response(fakes.wsgi_app(fake_auth=False)) @@ -124,7 +125,8 @@ class Test(test.TestCase): self.assertEqual(result.status, '204 No Content') token = result.headers['X-Auth-Token'] - self.stubs.Set(nova.api.openstack.v2, 'APIRouter', fakes.FakeRouter) + self.stubs.Set(nova.api.openstack.compute, 'APIRouter', + fakes.FakeRouter) req = webob.Request.blank('/v2/user2_project') req.headers['X-Auth-Token'] = token result = req.get_response(fakes.wsgi_app(fake_auth=False)) @@ -177,7 +179,8 @@ class Test(test.TestCase): self.assertEqual(result.status, '204 No Content') token = result.headers['X-Auth-Token'] - self.stubs.Set(nova.api.openstack.v2, 'APIRouter', fakes.FakeRouter) + self.stubs.Set(nova.api.openstack.compute, 'APIRouter', + fakes.FakeRouter) req = webob.Request.blank('/v2/user2_project') req.headers['X-Auth-Token'] = token result = req.get_response(fakes.wsgi_app(fake_auth=False)) @@ -200,7 +203,8 @@ class Test(test.TestCase): self.assertEqual(result.status, '204 No Content') token = result.headers['X-Auth-Token'] - self.stubs.Set(nova.api.openstack.v2, 'APIRouter', fakes.FakeRouter) + self.stubs.Set(nova.api.openstack.compute, 'APIRouter', + fakes.FakeRouter) req = webob.Request.blank('/v2/user2_project') req.headers['X-Auth-Token'] = token result = req.get_response(fakes.wsgi_app(fake_auth=False)) @@ -237,7 +241,7 @@ class TestFunctional(test.TestCase): class TestLimiter(test.TestCase): def setUp(self): super(TestLimiter, self).setUp() - self.stubs.Set(nova.api.openstack.v2.auth.AuthMiddleware, + self.stubs.Set(auth.AuthMiddleware, '__init__', fakes.fake_auth_init) self.stubs.Set(context, 'RequestContext', fakes.FakeRequestContext) fakes.FakeAuthManager.clear_fakes() @@ -261,7 +265,8 @@ class TestLimiter(test.TestCase): self.assertEqual(len(result.headers['X-Auth-Token']), 40) token = result.headers['X-Auth-Token'] - self.stubs.Set(nova.api.openstack.v2, 'APIRouter', fakes.FakeRouter) + self.stubs.Set(nova.api.openstack.compute, 'APIRouter', + fakes.FakeRouter) req = webob.Request.blank('/v2/test') req.method = 'POST' req.headers['X-Auth-Token'] = token diff --git a/nova/tests/api/openstack/v2/test_consoles.py b/nova/tests/api/openstack/compute/test_consoles.py index 5953737a8..f58e76a3f 100644 --- a/nova/tests/api/openstack/v2/test_consoles.py +++ b/nova/tests/api/openstack/compute/test_consoles.py @@ -22,7 +22,7 @@ import json from lxml import etree import webob -from nova.api.openstack.v2 import consoles +from nova.api.openstack.compute import consoles from nova import console from nova import db from nova.compute import vm_states diff --git a/nova/tests/api/openstack/v2/test_extensions.py b/nova/tests/api/openstack/compute/test_extensions.py index f2a49c3ed..a4585781b 100644 --- a/nova/tests/api/openstack/v2/test_extensions.py +++ b/nova/tests/api/openstack/compute/test_extensions.py @@ -21,8 +21,9 @@ import json import webob from lxml import etree -from nova.api.openstack import v2 -from nova.api.openstack.v2 import extensions +from nova.api.openstack import compute +from nova.api.openstack import extensions as base_extensions +from nova.api.openstack.compute import extensions as compute_extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import flags @@ -86,11 +87,13 @@ class StubExtensionManager(object): class ExtensionTestCase(test.TestCase): def setUp(self): super(ExtensionTestCase, self).setUp() - ext_list = FLAGS.osapi_extension[:] - ext_list.append('nova.tests.api.openstack.v2.extensions.' - 'foxinsocks.Foxinsocks') - self.flags(osapi_extension=ext_list) - extensions.ExtensionManager.reset() + ext_list = FLAGS.osapi_compute_extension[:] + fox = ('nova.tests.api.openstack.compute.extensions.' + 'foxinsocks.Foxinsocks') + if fox not in ext_list: + ext_list.append(fox) + self.flags(osapi_compute_extension=ext_list) + compute_extensions.ExtensionManager.reset() class ExtensionControllerTest(ExtensionTestCase): @@ -133,8 +136,8 @@ class ExtensionControllerTest(ExtensionTestCase): self.ext_list.sort() def test_list_extensions_json(self): - app = v2.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) + app = compute.APIRouter() + ext_midware = compute_extensions.ExtensionMiddleware(app) ser_midware = wsgi.LazySerializationMiddleware(ext_midware) request = webob.Request.blank("/fake/extensions") response = request.get_response(ser_midware) @@ -142,8 +145,10 @@ class ExtensionControllerTest(ExtensionTestCase): # Make sure we have all the extensions. data = json.loads(response.body) - names = [x['name'] for x in data['extensions']] + names = [str(x['name']) for x in data['extensions']] names.sort() + print names + print self.ext_list self.assertEqual(names, self.ext_list) # Make sure that at least Fox in Sox is correct. @@ -160,8 +165,8 @@ class ExtensionControllerTest(ExtensionTestCase): ) def test_get_extension_json(self): - app = v2.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) + app = compute.APIRouter() + ext_midware = compute_extensions.ExtensionMiddleware(app) ser_midware = wsgi.LazySerializationMiddleware(ext_midware) request = webob.Request.blank("/fake/extensions/FOXNSOX") response = request.get_response(ser_midware) @@ -177,15 +182,15 @@ class ExtensionControllerTest(ExtensionTestCase): "links": []}) def test_get_non_existing_extension_json(self): - app = v2.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) + app = compute.APIRouter() + ext_midware = compute_extensions.ExtensionMiddleware(app) request = webob.Request.blank("/fake/extensions/4") response = request.get_response(ext_midware) self.assertEqual(404, response.status_int) def test_list_extensions_xml(self): - app = v2.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) + app = compute.APIRouter() + ext_midware = compute_extensions.ExtensionMiddleware(app) ser_midware = wsgi.LazySerializationMiddleware(ext_midware) request = webob.Request.blank("/fake/extensions") request.accept = "application/xml" @@ -212,8 +217,8 @@ class ExtensionControllerTest(ExtensionTestCase): xmlutil.validate_schema(root, 'extensions') def test_get_extension_xml(self): - app = v2.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) + app = compute.APIRouter() + ext_midware = compute_extensions.ExtensionMiddleware(app) ser_midware = wsgi.LazySerializationMiddleware(ext_midware) request = webob.Request.blank("/fake/extensions/FOXNSOX") request.accept = "application/xml" @@ -239,19 +244,19 @@ class ResourceExtensionTest(ExtensionTestCase): def test_no_extension_present(self): manager = StubExtensionManager(None) - app = v2.APIRouter(manager) - ext_midware = extensions.ExtensionMiddleware(app, manager) + app = compute.APIRouter(manager) + ext_midware = compute_extensions.ExtensionMiddleware(app, manager) ser_midware = wsgi.LazySerializationMiddleware(ext_midware) request = webob.Request.blank("/blah") response = request.get_response(ser_midware) self.assertEqual(404, response.status_int) def test_get_resources(self): - res_ext = extensions.ResourceExtension('tweedles', - StubController(response_body)) + res_ext = base_extensions.ResourceExtension('tweedles', + StubController(response_body)) manager = StubExtensionManager(res_ext) - app = v2.APIRouter(manager) - ext_midware = extensions.ExtensionMiddleware(app, manager) + app = compute.APIRouter(manager) + ext_midware = compute_extensions.ExtensionMiddleware(app, manager) ser_midware = wsgi.LazySerializationMiddleware(ext_midware) request = webob.Request.blank("/fake/tweedles") response = request.get_response(ser_midware) @@ -259,11 +264,11 @@ class ResourceExtensionTest(ExtensionTestCase): self.assertEqual(response_body, response.body) def test_get_resources_with_controller(self): - res_ext = extensions.ResourceExtension('tweedles', + res_ext = base_extensions.ResourceExtension('tweedles', StubController(response_body)) manager = StubExtensionManager(res_ext) - app = v2.APIRouter(manager) - ext_midware = extensions.ExtensionMiddleware(app, manager) + app = compute.APIRouter(manager) + ext_midware = compute_extensions.ExtensionMiddleware(app, manager) ser_midware = wsgi.LazySerializationMiddleware(ext_midware) request = webob.Request.blank("/fake/tweedles") response = request.get_response(ser_midware) @@ -271,11 +276,11 @@ class ResourceExtensionTest(ExtensionTestCase): self.assertEqual(response_body, response.body) def test_bad_request(self): - res_ext = extensions.ResourceExtension('tweedles', - StubController(response_body)) + res_ext = base_extensions.ResourceExtension('tweedles', + StubController(response_body)) manager = StubExtensionManager(res_ext) - app = v2.APIRouter(manager) - ext_midware = extensions.ExtensionMiddleware(app, manager) + app = compute.APIRouter(manager) + ext_midware = compute_extensions.ExtensionMiddleware(app, manager) ser_midware = wsgi.LazySerializationMiddleware(ext_midware) request = webob.Request.blank("/fake/tweedles") request.method = "POST" @@ -292,11 +297,11 @@ class ResourceExtensionTest(ExtensionTestCase): self.assertDictMatch(expected, body) def test_non_exist_resource(self): - res_ext = extensions.ResourceExtension('tweedles', + res_ext = base_extensions.ResourceExtension('tweedles', StubController(response_body)) manager = StubExtensionManager(res_ext) - app = v2.APIRouter(manager) - ext_midware = extensions.ExtensionMiddleware(app, manager) + app = compute.APIRouter(manager) + ext_midware = compute_extensions.ExtensionMiddleware(app, manager) ser_midware = wsgi.LazySerializationMiddleware(ext_midware) request = webob.Request.blank("/fake/tweedles/1") response = request.get_response(ser_midware) @@ -317,7 +322,7 @@ class InvalidExtension(object): alias = "THIRD" -class AdminExtension(extensions.ExtensionDescriptor): +class AdminExtension(base_extensions.ExtensionDescriptor): """Admin-only extension""" name = "Admin Ext" @@ -335,8 +340,8 @@ class ExtensionManagerTest(ExtensionTestCase): response_body = "Try to say this Mr. Knox, sir..." def test_get_resources(self): - app = v2.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) + app = compute.APIRouter() + ext_midware = compute_extensions.ExtensionMiddleware(app) ser_midware = wsgi.LazySerializationMiddleware(ext_midware) request = webob.Request.blank("/fake/foxnsocks") response = request.get_response(ser_midware) @@ -346,8 +351,8 @@ class ExtensionManagerTest(ExtensionTestCase): def test_invalid_extensions(self): # Don't need the serialization middleware here because we're # not testing any serialization - app = v2.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) + app = compute.APIRouter() + ext_midware = compute_extensions.ExtensionMiddleware(app) ext_mgr = ext_midware.ext_mgr ext_mgr.register(InvalidExtension()) self.assertTrue('FOXNSOX' in ext_mgr.extensions) @@ -355,8 +360,8 @@ class ExtensionManagerTest(ExtensionTestCase): def test_admin_extensions(self): self.flags(allow_admin_api=True) - app = v2.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) + app = compute.APIRouter() + ext_midware = compute_extensions.ExtensionMiddleware(app) ext_mgr = ext_midware.ext_mgr ext_mgr.register(AdminExtension()) self.assertTrue('FOXNSOX' in ext_mgr.extensions) @@ -364,8 +369,8 @@ class ExtensionManagerTest(ExtensionTestCase): def test_admin_extensions_no_admin_api(self): self.flags(allow_admin_api=False) - app = v2.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) + app = compute.APIRouter() + ext_midware = compute_extensions.ExtensionMiddleware(app) ext_mgr = ext_midware.ext_mgr ext_mgr.register(AdminExtension()) self.assertTrue('FOXNSOX' in ext_mgr.extensions) @@ -375,8 +380,8 @@ class ExtensionManagerTest(ExtensionTestCase): class ActionExtensionTest(ExtensionTestCase): def _send_server_action_request(self, url, body): - app = v2.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) + app = compute.APIRouter() + ext_midware = compute_extensions.ExtensionMiddleware(app) ser_midware = wsgi.LazySerializationMiddleware(ext_midware) request = webob.Request.blank(url) request.method = 'POST' @@ -443,13 +448,13 @@ class RequestExtensionTest(ExtensionTestCase): body['flavor']['googoose'] = req.GET.get('chewing') return res - req_ext = extensions.RequestExtension('GET', - '/v2/fake/flavors/:(id)', - _req_handler) + req_ext = base_extensions.RequestExtension('GET', + '/v2/fake/flavors/:(id)', + _req_handler) manager = StubExtensionManager(None, None, req_ext) app = fakes.wsgi_app(serialization=base_wsgi.Middleware) - ext_midware = extensions.ExtensionMiddleware(app, manager) + ext_midware = compute_extensions.ExtensionMiddleware(app, manager) ser_midware = wsgi.LazySerializationMiddleware(ext_midware) request = webob.Request.blank("/v2/fake/flavors/1?chewing=bluegoo") request.environ['api.version'] = '2' @@ -461,7 +466,7 @@ class RequestExtensionTest(ExtensionTestCase): def test_get_resources_with_mgr(self): app = fakes.wsgi_app(serialization=base_wsgi.Middleware) - ext_midware = extensions.ExtensionMiddleware(app) + ext_midware = compute_extensions.ExtensionMiddleware(app) ser_midware = wsgi.LazySerializationMiddleware(ext_midware) request = webob.Request.blank("/v2/fake/flavors/1?chewing=newblue") request.environ['api.version'] = '2' @@ -476,7 +481,7 @@ class RequestExtensionTest(ExtensionTestCase): class ExtensionsXMLSerializerTest(test.TestCase): def test_serialize_extension(self): - serializer = extensions.ExtensionsXMLSerializer() + serializer = base_extensions.ExtensionsXMLSerializer() data = {'extension': { 'name': 'ext1', 'namespace': 'http://docs.rack.com/servers/api/ext/pie/v1.0', @@ -509,7 +514,7 @@ class ExtensionsXMLSerializerTest(test.TestCase): xmlutil.validate_schema(root, 'extension') def test_serialize_extensions(self): - serializer = extensions.ExtensionsXMLSerializer() + serializer = base_extensions.ExtensionsXMLSerializer() data = {"extensions": [{ "name": "Public Image Extension", "namespace": "http://foo.com/api/ext/pie/v1.0", diff --git a/nova/tests/api/openstack/v2/test_flavors.py b/nova/tests/api/openstack/compute/test_flavors.py index d70d581df..37b67fc32 100644 --- a/nova/tests/api/openstack/v2/test_flavors.py +++ b/nova/tests/api/openstack/compute/test_flavors.py @@ -18,7 +18,7 @@ from lxml import etree import webob -from nova.api.openstack.v2 import flavors +from nova.api.openstack.compute import flavors from nova.api.openstack import xmlutil import nova.compute.instance_types from nova import exception diff --git a/nova/tests/api/openstack/v2/test_image_metadata.py b/nova/tests/api/openstack/compute/test_image_metadata.py index e3e774ba3..62f047a2a 100644 --- a/nova/tests/api/openstack/v2/test_image_metadata.py +++ b/nova/tests/api/openstack/compute/test_image_metadata.py @@ -18,7 +18,7 @@ import json import webob -from nova.api.openstack.v2 import image_metadata +from nova.api.openstack.compute import image_metadata from nova import flags from nova import test from nova.tests.api.openstack import fakes diff --git a/nova/tests/api/openstack/v2/test_images.py b/nova/tests/api/openstack/compute/test_images.py index 70c6db679..7bb1d44d9 100644 --- a/nova/tests/api/openstack/v2/test_images.py +++ b/nova/tests/api/openstack/compute/test_images.py @@ -26,8 +26,8 @@ from lxml import etree import stubout import webob -from nova.api.openstack.v2 import images -from nova.api.openstack.v2.views import images as images_view +from nova.api.openstack.compute import images +from nova.api.openstack.compute.views import images as images_view from nova.api.openstack import xmlutil from nova import test from nova import utils diff --git a/nova/tests/api/openstack/v2/test_limits.py b/nova/tests/api/openstack/compute/test_limits.py index 1c299d751..20ec0d1a0 100644 --- a/nova/tests/api/openstack/v2/test_limits.py +++ b/nova/tests/api/openstack/compute/test_limits.py @@ -27,8 +27,8 @@ from lxml import etree import stubout import webob -from nova.api.openstack.v2 import limits -from nova.api.openstack.v2 import views +from nova.api.openstack.compute import limits +from nova.api.openstack.compute import views from nova.api.openstack import wsgi from nova.api.openstack import xmlutil import nova.context diff --git a/nova/tests/api/openstack/v2/test_server_actions.py b/nova/tests/api/openstack/compute/test_server_actions.py index d2958e3b2..2f3976375 100644 --- a/nova/tests/api/openstack/v2/test_server_actions.py +++ b/nova/tests/api/openstack/compute/test_server_actions.py @@ -20,7 +20,7 @@ import mox import stubout import webob -from nova.api.openstack.v2 import servers +from nova.api.openstack.compute import servers from nova.compute import vm_states from nova.compute import instance_types from nova import context diff --git a/nova/tests/api/openstack/v2/test_server_metadata.py b/nova/tests/api/openstack/compute/test_server_metadata.py index 49ff1bcd8..61bf4fc47 100644 --- a/nova/tests/api/openstack/v2/test_server_metadata.py +++ b/nova/tests/api/openstack/compute/test_server_metadata.py @@ -18,7 +18,7 @@ import json import webob -from nova.api.openstack.v2 import server_metadata +from nova.api.openstack.compute import server_metadata import nova.db from nova import exception from nova import flags diff --git a/nova/tests/api/openstack/v2/test_servers.py b/nova/tests/api/openstack/compute/test_servers.py index 7216699b4..83b0e345c 100644 --- a/nova/tests/api/openstack/v2/test_servers.py +++ b/nova/tests/api/openstack/compute/test_servers.py @@ -23,9 +23,10 @@ import urlparse from lxml import etree import webob -import nova.api.openstack.v2 -from nova.api.openstack.v2 import ips -from nova.api.openstack.v2 import servers +import nova.api.openstack.compute +from nova.api.openstack.compute import ips +from nova.api.openstack.compute import servers +from nova.api.openstack.compute import views from nova.api.openstack import xmlutil import nova.compute.api from nova.compute import instance_types @@ -2290,8 +2291,8 @@ class TestServerCreateRequestXMLDeserializer(test.TestCase): class TestAddressesXMLSerialization(test.TestCase): - index_serializer = nova.api.openstack.v2.ips.AddressesTemplate() - show_serializer = nova.api.openstack.v2.ips.NetworkTemplate() + index_serializer = nova.api.openstack.compute.ips.AddressesTemplate() + show_serializer = nova.api.openstack.compute.ips.NetworkTemplate() def test_xml_declaration(self): fixture = { @@ -2369,7 +2370,7 @@ class ServersViewBuilderTest(test.TestCase): include_fake_metadata=False) self.uuid = self.instance['uuid'] - self.view_builder = nova.api.openstack.v2.views.servers.ViewBuilder() + self.view_builder = views.servers.ViewBuilder() self.request = fakes.HTTPRequest.blank("/v2") def test_build_server(self): diff --git a/nova/tests/api/openstack/v2/test_urlmap.py b/nova/tests/api/openstack/compute/test_urlmap.py index 82a870a50..ae269d01f 100644 --- a/nova/tests/api/openstack/v2/test_urlmap.py +++ b/nova/tests/api/openstack/compute/test_urlmap.py @@ -20,7 +20,7 @@ from nova import log as logging from nova import test from nova.tests.api.openstack import fakes -LOG = logging.getLogger('nova.tests.api.openstack.v2.test_urlmap') +LOG = logging.getLogger('nova.tests.api.openstack.compute.test_urlmap') class UrlmapTest(test.TestCase): diff --git a/nova/tests/api/openstack/v2/test_versions.py b/nova/tests/api/openstack/compute/test_versions.py index 7e250471a..b1383be60 100644 --- a/nova/tests/api/openstack/v2/test_versions.py +++ b/nova/tests/api/openstack/compute/test_versions.py @@ -22,8 +22,8 @@ from lxml import etree import stubout import webob -from nova.api.openstack.v2 import versions -from nova.api.openstack.v2 import views +from nova.api.openstack.compute import versions +from nova.api.openstack.compute import views from nova.api.openstack import xmlutil from nova import context from nova import test diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index f9f8dd0a0..9fa1749ef 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -24,14 +24,14 @@ import webob.request from glance import client as glance_client -import nova.api.openstack.v2.auth from nova.api import auth as api_auth -from nova.api.openstack import v2 -from nova.api.openstack.v2 import auth -from nova.api.openstack.v2 import extensions -from nova.api.openstack.v2 import limits -from nova.api.openstack.v2 import urlmap -from nova.api.openstack.v2 import versions +from nova.api import openstack as openstack_api +from nova.api.openstack import compute +from nova.api.openstack import auth +from nova.api.openstack.compute import extensions +from nova.api.openstack.compute import limits +from nova.api.openstack import urlmap +from nova.api.openstack.compute import versions from nova.api.openstack import wsgi as os_wsgi from nova.auth.manager import User, Project from nova.compute import instance_types @@ -77,24 +77,24 @@ def wsgi_app(inner_app_v2=None, fake_auth=True, fake_auth_context=None, serialization=os_wsgi.LazySerializationMiddleware, use_no_auth=False): if not inner_app_v2: - inner_app_v2 = v2.APIRouter() + inner_app_v2 = compute.APIRouter() if fake_auth: if fake_auth_context is not None: ctxt = fake_auth_context else: ctxt = context.RequestContext('fake', 'fake', auth_token=True) - api_v2 = v2.FaultWrapper(api_auth.InjectContext(ctxt, + api_v2 = openstack_api.FaultWrapper(api_auth.InjectContext(ctxt, limits.RateLimitingMiddleware( serialization( extensions.ExtensionMiddleware(inner_app_v2))))) elif use_no_auth: - api_v2 = v2.FaultWrapper(auth.NoAuthMiddleware( + api_v2 = openstack_api.FaultWrapper(auth.NoAuthMiddleware( limits.RateLimitingMiddleware( serialization( extensions.ExtensionMiddleware(inner_app_v2))))) else: - api_v2 = v2.FaultWrapper(auth.AuthMiddleware( + api_v2 = openstack_api.FaultWrapper(auth.AuthMiddleware( limits.RateLimitingMiddleware( serialization( extensions.ExtensionMiddleware(inner_app_v2))))) @@ -102,7 +102,7 @@ def wsgi_app(inner_app_v2=None, fake_auth=True, fake_auth_context=None, mapper = urlmap.URLMap() mapper['/v2'] = api_v2 mapper['/v1.1'] = api_v2 - mapper['/'] = v2.FaultWrapper(versions.Versions()) + mapper['/'] = openstack_api.FaultWrapper(versions.Versions()) return mapper @@ -138,9 +138,9 @@ def stub_out_auth(stubs): def fake_auth_init(self, app): self.application = app - stubs.Set(nova.api.openstack.v2.auth.AuthMiddleware, + stubs.Set(auth.AuthMiddleware, '__init__', fake_auth_init) - stubs.Set(nova.api.openstack.v2.auth.AuthMiddleware, + stubs.Set(auth.AuthMiddleware, '__call__', fake_wsgi) @@ -149,10 +149,10 @@ def stub_out_rate_limiting(stubs): super(limits.RateLimitingMiddleware, self).__init__(app) self.application = app - stubs.Set(nova.api.openstack.v2.limits.RateLimitingMiddleware, + stubs.Set(nova.api.openstack.compute.limits.RateLimitingMiddleware, '__init__', fake_rate_init) - stubs.Set(nova.api.openstack.v2.limits.RateLimitingMiddleware, + stubs.Set(nova.api.openstack.compute.limits.RateLimitingMiddleware, '__call__', fake_wsgi) diff --git a/nova/tests/api/openstack/volume/__init__.py b/nova/tests/api/openstack/volume/__init__.py new file mode 100644 index 000000000..00fcfbb00 --- /dev/null +++ b/nova/tests/api/openstack/volume/__init__.py @@ -0,0 +1,16 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. diff --git a/nova/tests/api/openstack/volume/test_snapshots.py b/nova/tests/api/openstack/volume/test_snapshots.py new file mode 100644 index 000000000..06543ae5f --- /dev/null +++ b/nova/tests/api/openstack/volume/test_snapshots.py @@ -0,0 +1,299 @@ +# Copyright 2011 Denali 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. + +import datetime +import json +import stubout + +from lxml import etree +import webob + +from nova.api.openstack.volume import snapshots +from nova import context +from nova import exception +from nova import flags +from nova import log as logging +from nova import test +from nova import volume +from nova.tests.api.openstack import fakes + +FLAGS = flags.FLAGS + +LOG = logging.getLogger('nova.tests.api.openstack.snapshot') + +_last_param = {} + + +def _get_default_snapshot_param(): + return { + 'id': 123, + 'volume_id': 12, + 'status': 'available', + 'volume_size': 100, + 'created_at': None, + 'display_name': 'Default name', + 'display_description': 'Default description', + } + + +def stub_snapshot_create(self, context, volume_id, name, description): + global _last_param + snapshot = _get_default_snapshot_param() + snapshot['volume_id'] = volume_id + snapshot['display_name'] = name + snapshot['display_description'] = description + + LOG.debug(_("_create: %s"), snapshot) + _last_param = snapshot + return snapshot + + +def stub_snapshot_delete(self, context, snapshot_id): + global _last_param + _last_param = dict(snapshot_id=snapshot_id) + + LOG.debug(_("_delete: %s"), locals()) + if snapshot_id != '123': + raise exception.NotFound + + +def stub_snapshot_get(self, context, snapshot_id): + global _last_param + _last_param = dict(snapshot_id=snapshot_id) + + LOG.debug(_("_get: %s"), locals()) + if snapshot_id != '123': + raise exception.NotFound + + param = _get_default_snapshot_param() + param['id'] = snapshot_id + return param + + +def stub_snapshot_get_all(self, context): + LOG.debug(_("_get_all: %s"), locals()) + param = _get_default_snapshot_param() + param['id'] = 123 + return [param] + + +class SnapshotApiTest(test.TestCase): + def setUp(self): + super(SnapshotApiTest, self).setUp() + self.stubs = stubout.StubOutForTesting() + fakes.FakeAuthManager.reset_fake_data() + fakes.FakeAuthDatabase.data = {} + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + fakes.stub_out_auth(self.stubs) + self.stubs.Set(volume.api.API, "create_snapshot", stub_snapshot_create) + self.stubs.Set(volume.api.API, "create_snapshot_force", + stub_snapshot_create) + self.stubs.Set(volume.api.API, "delete_snapshot", stub_snapshot_delete) + self.stubs.Set(volume.api.API, "get_snapshot", stub_snapshot_get) + self.stubs.Set(volume.api.API, "get_all_snapshots", + stub_snapshot_get_all) + + self.context = context.get_admin_context() + + def tearDown(self): + self.stubs.UnsetAll() + super(SnapshotApiTest, self).tearDown() + + def test_snapshot_create(self): + global _last_param + _last_param = {} + + snapshot = {"volume_id": 12, + "force": False, + "display_name": "Snapshot Test Name", + "display_description": "Snapshot Test Desc"} + body = dict(snapshot=snapshot) + req = webob.Request.blank('/v1.1/fake/os-snapshots') + req.method = 'POST' + req.body = json.dumps(body) + req.headers['content-type'] = 'application/json' + + resp = req.get_response(fakes.wsgi_app()) + LOG.debug(_("test_snapshot_create: param=%s"), _last_param) + self.assertEqual(resp.status_int, 200) + + # Compare if parameters were correctly passed to stub + self.assertEqual(_last_param['display_name'], "Snapshot Test Name") + self.assertEqual(_last_param['display_description'], + "Snapshot Test Desc") + + resp_dict = json.loads(resp.body) + LOG.debug(_("test_snapshot_create: resp_dict=%s"), resp_dict) + self.assertTrue('snapshot' in resp_dict) + self.assertEqual(resp_dict['snapshot']['displayName'], + snapshot['display_name']) + self.assertEqual(resp_dict['snapshot']['displayDescription'], + snapshot['display_description']) + + def test_snapshot_create_force(self): + global _last_param + _last_param = {} + + snapshot = {"volume_id": 12, + "force": True, + "display_name": "Snapshot Test Name", + "display_description": "Snapshot Test Desc"} + body = dict(snapshot=snapshot) + req = webob.Request.blank('/v1.1/fake/os-snapshots') + req.method = 'POST' + req.body = json.dumps(body) + req.headers['content-type'] = 'application/json' + + resp = req.get_response(fakes.wsgi_app()) + LOG.debug(_("test_snapshot_create_force: param=%s"), _last_param) + self.assertEqual(resp.status_int, 200) + + # Compare if parameters were correctly passed to stub + self.assertEqual(_last_param['display_name'], "Snapshot Test Name") + self.assertEqual(_last_param['display_description'], + "Snapshot Test Desc") + + resp_dict = json.loads(resp.body) + LOG.debug(_("test_snapshot_create_force: resp_dict=%s"), resp_dict) + self.assertTrue('snapshot' in resp_dict) + self.assertEqual(resp_dict['snapshot']['displayName'], + snapshot['display_name']) + self.assertEqual(resp_dict['snapshot']['displayDescription'], + snapshot['display_description']) + + def test_snapshot_delete(self): + global _last_param + _last_param = {} + + snapshot_id = 123 + req = webob.Request.blank('/v1.1/fake/os-snapshots/%d' % snapshot_id) + req.method = 'DELETE' + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 202) + self.assertEqual(str(_last_param['snapshot_id']), str(snapshot_id)) + + def test_snapshot_delete_invalid_id(self): + global _last_param + _last_param = {} + + snapshot_id = 234 + req = webob.Request.blank('/v1.1/fake/os-snapshots/%d' % snapshot_id) + req.method = 'DELETE' + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 404) + self.assertEqual(str(_last_param['snapshot_id']), str(snapshot_id)) + + def test_snapshot_show(self): + global _last_param + _last_param = {} + + snapshot_id = 123 + req = webob.Request.blank('/v1.1/fake/os-snapshots/%d' % snapshot_id) + req.method = 'GET' + resp = req.get_response(fakes.wsgi_app()) + + LOG.debug(_("test_snapshot_show: resp=%s"), resp) + self.assertEqual(resp.status_int, 200) + self.assertEqual(str(_last_param['snapshot_id']), str(snapshot_id)) + + resp_dict = json.loads(resp.body) + self.assertTrue('snapshot' in resp_dict) + self.assertEqual(resp_dict['snapshot']['id'], str(snapshot_id)) + + def test_snapshot_show_invalid_id(self): + global _last_param + _last_param = {} + + snapshot_id = 234 + req = webob.Request.blank('/v1.1/fake/os-snapshots/%d' % snapshot_id) + req.method = 'GET' + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 404) + self.assertEqual(str(_last_param['snapshot_id']), str(snapshot_id)) + + def test_snapshot_detail(self): + req = webob.Request.blank('/v1.1/fake/os-snapshots/detail') + req.method = 'GET' + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 200) + + resp_dict = json.loads(resp.body) + LOG.debug(_("test_snapshot_detail: resp_dict=%s"), resp_dict) + self.assertTrue('snapshots' in resp_dict) + resp_snapshots = resp_dict['snapshots'] + self.assertEqual(len(resp_snapshots), 1) + + resp_snapshot = resp_snapshots.pop() + self.assertEqual(resp_snapshot['id'], 123) + + +class SnapshotSerializerTest(test.TestCase): + def _verify_snapshot(self, snap, tree): + self.assertEqual(tree.tag, 'snapshot') + + for attr in ('id', 'status', 'size', 'createdAt', + 'displayName', 'displayDescription', 'volumeId'): + self.assertEqual(str(snap[attr]), tree.get(attr)) + + def test_snapshot_show_create_serializer(self): + serializer = snapshots.SnapshotSerializer() + raw_snapshot = dict( + id='snap_id', + status='snap_status', + size=1024, + createdAt=datetime.datetime.now(), + displayName='snap_name', + displayDescription='snap_desc', + volumeId='vol_id', + ) + text = serializer.serialize(dict(snapshot=raw_snapshot), 'show') + + print text + tree = etree.fromstring(text) + + self._verify_snapshot(raw_snapshot, tree) + + def test_snapshot_index_detail_serializer(self): + serializer = snapshots.SnapshotSerializer() + raw_snapshots = [dict( + id='snap1_id', + status='snap1_status', + size=1024, + createdAt=datetime.datetime.now(), + displayName='snap1_name', + displayDescription='snap1_desc', + volumeId='vol1_id', + ), + dict( + id='snap2_id', + status='snap2_status', + size=1024, + createdAt=datetime.datetime.now(), + displayName='snap2_name', + displayDescription='snap2_desc', + volumeId='vol2_id', + )] + text = serializer.serialize(dict(snapshots=raw_snapshots), 'index') + + print text + tree = etree.fromstring(text) + + self.assertEqual('snapshots', tree.tag) + self.assertEqual(len(raw_snapshots), len(tree)) + for idx, child in enumerate(tree): + self._verify_snapshot(raw_snapshots[idx], child) diff --git a/nova/tests/api/openstack/volume/test_types.py b/nova/tests/api/openstack/volume/test_types.py new file mode 100644 index 000000000..34d861bed --- /dev/null +++ b/nova/tests/api/openstack/volume/test_types.py @@ -0,0 +1,166 @@ +# Copyright 2011 OpenStack LLC. +# aLL Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from lxml import etree +import webob + +from nova.api.openstack.volume import types +from nova import exception +from nova import test +from nova import log as logging +from nova.volume import volume_types +from nova.tests.api.openstack import fakes + + +LOG = logging.getLogger('nova.tests.api.openstack.volume.' + 'test_volume_types') + +last_param = {} + + +def stub_volume_type(id): + specs = { + "key1": "value1", + "key2": "value2", + "key3": "value3", + "key4": "value4", + "key5": "value5"} + return dict(id=id, name='vol_type_%s' % str(id), extra_specs=specs) + + +def return_volume_types_get_all_types(context): + return dict(vol_type_1=stub_volume_type(1), + vol_type_2=stub_volume_type(2), + vol_type_3=stub_volume_type(3)) + + +def return_empty_volume_types_get_all_types(context): + return {} + + +def return_volume_types_get_volume_type(context, id): + if id == "777": + raise exception.VolumeTypeNotFound(volume_type_id=id) + return stub_volume_type(int(id)) + + +def return_volume_types_destroy(context, name): + if name == "777": + raise exception.VolumeTypeNotFoundByName(volume_type_name=name) + pass + + +def return_volume_types_create(context, name, specs): + pass + + +def return_volume_types_get_by_name(context, name): + if name == "777": + raise exception.VolumeTypeNotFoundByName(volume_type_name=name) + return stub_volume_type(int(name.split("_")[2])) + + +class VolumeTypesApiTest(test.TestCase): + def setUp(self): + super(VolumeTypesApiTest, self).setUp() + fakes.stub_out_key_pair_funcs(self.stubs) + self.controller = types.VolumeTypesController() + + def tearDown(self): + self.stubs.UnsetAll() + super(VolumeTypesApiTest, self).tearDown() + + def test_volume_types_index(self): + self.stubs.Set(volume_types, 'get_all_types', + return_volume_types_get_all_types) + + req = fakes.HTTPRequest.blank('/v2/123/os-volume-types') + res_dict = self.controller.index(req) + + self.assertEqual(3, len(res_dict)) + for name in ['vol_type_1', 'vol_type_2', 'vol_type_3']: + self.assertEqual(name, res_dict[name]['name']) + self.assertEqual('value1', res_dict[name]['extra_specs']['key1']) + + def test_volume_types_index_no_data(self): + self.stubs.Set(volume_types, 'get_all_types', + return_empty_volume_types_get_all_types) + + req = fakes.HTTPRequest.blank('/v2/123/os-volume-types') + res_dict = self.controller.index(req) + + self.assertEqual(0, len(res_dict)) + + def test_volume_types_show(self): + self.stubs.Set(volume_types, 'get_volume_type', + return_volume_types_get_volume_type) + + req = fakes.HTTPRequest.blank('/v2/123/os-volume-types/1') + res_dict = self.controller.show(req, 1) + + self.assertEqual(1, len(res_dict)) + self.assertEqual('vol_type_1', res_dict['volume_type']['name']) + + def test_volume_types_show_not_found(self): + self.stubs.Set(volume_types, 'get_volume_type', + return_volume_types_get_volume_type) + + req = fakes.HTTPRequest.blank('/v2/123/os-volume-types/777') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, + req, '777') + + +class VolumeTypesSerializerTest(test.TestCase): + def setUp(self): + super(VolumeTypesSerializerTest, self).setUp() + self.serializer = types.VolumeTypesSerializer() + + def _verify_volume_type(self, vtype, tree): + self.assertEqual('volume_type', tree.tag) + self.assertEqual(vtype['name'], tree.get('name')) + self.assertEqual(str(vtype['id']), tree.get('id')) + self.assertEqual(1, len(tree)) + extra_specs = tree[0] + self.assertEqual('extra_specs', extra_specs.tag) + seen = set(vtype['extra_specs'].keys()) + for child in extra_specs: + self.assertTrue(child.tag in seen) + self.assertEqual(vtype['extra_specs'][child.tag], child.text) + seen.remove(child.tag) + self.assertEqual(len(seen), 0) + + def test_index_serializer(self): + # Just getting some input data + vtypes = return_volume_types_get_all_types(None) + text = self.serializer.serialize(vtypes, 'index') + + print text + tree = etree.fromstring(text) + + self.assertEqual('volume_types', tree.tag) + self.assertEqual(len(vtypes), len(tree)) + for child in tree: + name = child.get('name') + self.assertTrue(name in vtypes) + self._verify_volume_type(vtypes[name], child) + + def test_voltype_serializer(self): + vtype = stub_volume_type(1) + text = self.serializer.serialize(dict(volume_type=vtype)) + + print text + tree = etree.fromstring(text) + + self._verify_volume_type(vtype, tree) diff --git a/nova/tests/api/openstack/volume/test_volumes.py b/nova/tests/api/openstack/volume/test_volumes.py new file mode 100644 index 000000000..2f17c4b22 --- /dev/null +++ b/nova/tests/api/openstack/volume/test_volumes.py @@ -0,0 +1,179 @@ +# Copyright 2013 Josh Durgin +# 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 json + +from lxml import etree +import webob + +import nova +from nova.api.openstack.volume import volumes +from nova.compute import instance_types +from nova import flags +from nova import test +from nova.tests.api.openstack import fakes + + +FLAGS = flags.FLAGS + + +class VolumeSerializerTest(test.TestCase): + def _verify_volume_attachment(self, attach, tree): + for attr in ('id', 'volumeId', 'serverId', 'device'): + self.assertEqual(str(attach[attr]), tree.get(attr)) + + def _verify_volume(self, vol, tree): + self.assertEqual(tree.tag, 'volume') + + for attr in ('id', 'status', 'size', 'availabilityZone', 'createdAt', + 'displayName', 'displayDescription', 'volumeType', + 'snapshotId'): + self.assertEqual(str(vol[attr]), tree.get(attr)) + + for child in tree: + self.assertTrue(child.tag in ('attachments', 'metadata')) + if child.tag == 'attachments': + self.assertEqual(1, len(child)) + self.assertEqual('attachment', child[0].tag) + self._verify_volume_attachment(vol['attachments'][0], child[0]) + elif child.tag == 'metadata': + not_seen = set(vol['metadata'].keys()) + for gr_child in child: + self.assertTrue(gr_child.tag in not_seen) + self.assertEqual(str(vol['metadata'][gr_child.tag]), + gr_child.text) + not_seen.remove(gr_child.tag) + self.assertEqual(0, len(not_seen)) + + def test_attach_show_create_serializer(self): + serializer = volumes.VolumeAttachmentSerializer() + raw_attach = dict( + id='vol_id', + volumeId='vol_id', + serverId='instance_uuid', + device='/foo') + text = serializer.serialize(dict(volumeAttachment=raw_attach), 'show') + + print text + tree = etree.fromstring(text) + + self.assertEqual('volumeAttachment', tree.tag) + self._verify_volume_attachment(raw_attach, tree) + + def test_attach_index_serializer(self): + serializer = volumes.VolumeAttachmentSerializer() + raw_attaches = [dict( + id='vol_id1', + volumeId='vol_id1', + serverId='instance1_uuid', + device='/foo1'), + dict( + id='vol_id2', + volumeId='vol_id2', + serverId='instance2_uuid', + device='/foo2')] + text = serializer.serialize(dict(volumeAttachments=raw_attaches), + 'index') + + print text + tree = etree.fromstring(text) + + self.assertEqual('volumeAttachments', tree.tag) + self.assertEqual(len(raw_attaches), len(tree)) + for idx, child in enumerate(tree): + self.assertEqual('volumeAttachment', child.tag) + self._verify_volume_attachment(raw_attaches[idx], child) + + def test_volume_show_create_serializer(self): + serializer = volumes.VolumeSerializer() + raw_volume = dict( + id='vol_id', + status='vol_status', + size=1024, + availabilityZone='vol_availability', + createdAt=datetime.datetime.now(), + attachments=[dict( + id='vol_id', + volumeId='vol_id', + serverId='instance_uuid', + device='/foo')], + displayName='vol_name', + displayDescription='vol_desc', + volumeType='vol_type', + snapshotId='snap_id', + metadata=dict( + foo='bar', + baz='quux', + ), + ) + text = serializer.serialize(dict(volume=raw_volume), 'show') + + print text + tree = etree.fromstring(text) + + self._verify_volume(raw_volume, tree) + + def test_volume_index_detail_serializer(self): + serializer = volumes.VolumeSerializer() + raw_volumes = [dict( + id='vol1_id', + status='vol1_status', + size=1024, + availabilityZone='vol1_availability', + createdAt=datetime.datetime.now(), + attachments=[dict( + id='vol1_id', + volumeId='vol1_id', + serverId='instance_uuid', + device='/foo1')], + displayName='vol1_name', + displayDescription='vol1_desc', + volumeType='vol1_type', + snapshotId='snap1_id', + metadata=dict( + foo='vol1_foo', + bar='vol1_bar', + ), + ), + dict( + id='vol2_id', + status='vol2_status', + size=1024, + availabilityZone='vol2_availability', + createdAt=datetime.datetime.now(), + attachments=[dict( + id='vol2_id', + volumeId='vol2_id', + serverId='instance_uuid', + device='/foo2')], + displayName='vol2_name', + displayDescription='vol2_desc', + volumeType='vol2_type', + snapshotId='snap2_id', + metadata=dict( + foo='vol2_foo', + bar='vol2_bar', + ), + )] + text = serializer.serialize(dict(volumes=raw_volumes), 'index') + + print text + tree = etree.fromstring(text) + + self.assertEqual('volumes', tree.tag) + self.assertEqual(len(raw_volumes), len(tree)) + for idx, child in enumerate(tree): + self._verify_volume(raw_volumes[idx], child) diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py index c942b0108..ccf83c1da 100644 --- a/nova/tests/integrated/api/client.py +++ b/nova/tests/integrated/api/client.py @@ -261,17 +261,17 @@ class TestOpenStackClient(object): return self.api_delete('/flavors/%s' % flavor_id) def get_volume(self, volume_id): - return self.api_get('/os-volumes/%s' % volume_id)['volume'] + return self.api_get('/volumes/%s' % volume_id)['volume'] def get_volumes(self, detail=True): - rel_url = '/os-volumes/detail' if detail else '/os-volumes' + rel_url = '/volumes/detail' if detail else '/volumes' return self.api_get(rel_url)['volumes'] def post_volume(self, volume): - return self.api_post('/os-volumes', volume)['volume'] + return self.api_post('/volumes', volume)['volume'] def delete_volume(self, volume_id): - return self.api_delete('/os-volumes/%s' % volume_id) + return self.api_delete('/volumes/%s' % volume_id) def get_server_volume(self, server_id, attachment_id): return self.api_get('/servers/%s/os-volume_attachments/%s' % diff --git a/nova/tests/integrated/integrated_helpers.py b/nova/tests/integrated/integrated_helpers.py index b18d24bd4..a98c94f65 100644 --- a/nova/tests/integrated/integrated_helpers.py +++ b/nova/tests/integrated/integrated_helpers.py @@ -80,10 +80,14 @@ class _IntegratedTestBase(test.TestCase): self.api = client.TestOpenStackClient('fake', 'fake', self.auth_url) + def tearDown(self): + self.osapi.stop() + super(_IntegratedTestBase, self).tearDown() + def _start_api_service(self): - osapi = service.WSGIService("osapi") - osapi.start() - self.auth_url = 'http://%s:%s/v2' % (osapi.host, osapi.port) + self.osapi = service.WSGIService("osapi_compute") + self.osapi.start() + self.auth_url = 'http://%s:%s/v2' % (self.osapi.host, self.osapi.port) LOG.warn(self.auth_url) def _get_flags(self): diff --git a/nova/tests/integrated/test_extensions.py b/nova/tests/integrated/test_extensions.py index b10da166a..70c998ef0 100644 --- a/nova/tests/integrated/test_extensions.py +++ b/nova/tests/integrated/test_extensions.py @@ -17,7 +17,7 @@ import os -from nova.api.openstack.v2 import extensions +from nova.api.openstack.compute import extensions from nova import flags from nova.log import logging from nova.tests.integrated import integrated_helpers @@ -32,9 +32,10 @@ class ExtensionsTest(integrated_helpers._IntegratedTestBase): extensions.ExtensionManager.reset() f = super(ExtensionsTest, self)._get_flags() - f['osapi_extension'] = FLAGS.osapi_extension[:] - f['osapi_extension'].append('nova.tests.api.openstack.v2.extensions.' - 'foxinsocks.Foxinsocks') + f['osapi_compute_extension'] = FLAGS.osapi_compute_extension[:] + f['osapi_compute_extension'].append( + 'nova.tests.api.openstack.compute.extensions.' + 'foxinsocks.Foxinsocks') return f def test_get_foxnsocks(self): diff --git a/nova/tests/integrated/test_volumes.py b/nova/tests/integrated/test_volumes.py index 9e19f2cff..d07865181 100644 --- a/nova/tests/integrated/test_volumes.py +++ b/nova/tests/integrated/test_volumes.py @@ -18,6 +18,7 @@ import unittest import time +from nova import service from nova.log import logging from nova.tests.integrated import integrated_helpers from nova.tests.integrated.api import client @@ -32,6 +33,12 @@ class VolumesTest(integrated_helpers._IntegratedTestBase): super(VolumesTest, self).setUp() driver.LoggingVolumeDriver.clear_logs() + def _start_api_service(self): + self.osapi = service.WSGIService("osapi_volume") + self.osapi.start() + self.auth_url = 'http://%s:%s/v1' % (self.osapi.host, self.osapi.port) + LOG.warn(self.auth_url) + def _get_flags(self): f = super(VolumesTest, self)._get_flags() f['use_local_volumes'] = False # Avoids calling local_path @@ -135,157 +142,6 @@ class VolumesTest(integrated_helpers._IntegratedTestBase): delete_action = export_actions[0] self.assertEquals(delete_action['id'], created_volume_id) - def test_attach_and_detach_volume(self): - """Creates, attaches, detaches and deletes a volume.""" - self.flags(stub_network=True) - - # Create server - server_req = {'server': self._build_minimal_create_server_request()} - # NOTE(justinsb): Create an extra server so that server_id != volume_id - self.api.post_server(server_req) - created_server = self.api.post_server(server_req) - LOG.debug("created_server: %s" % created_server) - server_id = created_server['id'] - - # Create volume - created_volume = self.api.post_volume({'volume': {'size': 1}}) - LOG.debug("created_volume: %s" % created_volume) - volume_id = created_volume['id'] - self._poll_while(volume_id, ['creating']) - - # Check we've got different IDs - self.assertNotEqual(server_id, volume_id) - - # List current server attachments - should be none - attachments = self.api.get_server_volumes(server_id) - self.assertEquals([], attachments) - - # Template attach request - device = '/dev/sdc' - attach_req = {'device': device} - post_req = {'volumeAttachment': attach_req} - - # Try to attach to a non-existent volume; should fail - attach_req['volumeId'] = 3405691582 - self.assertRaises(client.OpenStackApiNotFoundException, - self.api.post_server_volume, server_id, post_req) - - # Try to attach to a non-existent server; should fail - attach_req['volumeId'] = volume_id - self.assertRaises(client.OpenStackApiNotFoundException, - self.api.post_server_volume, 3405691582, post_req) - - # Should still be no attachments... - attachments = self.api.get_server_volumes(server_id) - self.assertEquals([], attachments) - - # Do a real attach - attach_req['volumeId'] = volume_id - attach_result = self.api.post_server_volume(server_id, post_req) - LOG.debug(_("Attachment = %s") % attach_result) - - attachment_id = attach_result['id'] - self.assertEquals(volume_id, attach_result['volumeId']) - - # These fields aren't set because it's async - #self.assertEquals(server_id, attach_result['serverId']) - #self.assertEquals(device, attach_result['device']) - - # This is just an implementation detail, but let's check it... - self.assertEquals(volume_id, attachment_id) - - # NOTE(justinsb): There's an issue with the attach code, in that - # it's currently asynchronous and not recorded until the attach - # completes. So the caller must be 'smart', like this... - attach_done = None - retries = 0 - while True: - try: - attach_done = self.api.get_server_volume(server_id, - attachment_id) - break - except client.OpenStackApiNotFoundException: - LOG.debug("Got 404, waiting") - - time.sleep(1) - retries = retries + 1 - if retries > 10: - break - - expect_attach = {} - expect_attach['id'] = volume_id - expect_attach['volumeId'] = volume_id - expect_attach['serverId'] = server_id - expect_attach['device'] = device - - self.assertEqual(expect_attach, attach_done) - - # Should be one attachemnt - attachments = self.api.get_server_volumes(server_id) - self.assertEquals([expect_attach], attachments) - - # Should be able to get details - attachment_info = self.api.get_server_volume(server_id, attachment_id) - self.assertEquals(expect_attach, attachment_info) - - # Getting details on a different id should fail - self.assertRaises(client.OpenStackApiNotFoundException, - self.api.get_server_volume, server_id, 3405691582) - self.assertRaises(client.OpenStackApiNotFoundException, - self.api.get_server_volume, - 3405691582, attachment_id) - - # Trying to detach a different id should fail - self.assertRaises(client.OpenStackApiNotFoundException, - self.api.delete_server_volume, server_id, 3405691582) - - # Detach should work - self.api.delete_server_volume(server_id, attachment_id) - - # Again, it's async, so wait... - retries = 0 - while True: - try: - attachment = self.api.get_server_volume(server_id, - attachment_id) - LOG.debug("Attachment still there: %s" % attachment) - except client.OpenStackApiNotFoundException: - LOG.debug("Got 404, delete done") - break - - time.sleep(1) - retries = retries + 1 - self.assertTrue(retries < 10) - - # Should be no attachments again - attachments = self.api.get_server_volumes(server_id) - self.assertEquals([], attachments) - - LOG.debug("Logs: %s" % driver.LoggingVolumeDriver.all_logs()) - - # prepare_attach and prepare_detach are called from compute - # on attach/detach - - disco_moves = driver.LoggingVolumeDriver.logs_like( - 'initialize_connection', - id=volume_id) - LOG.debug("initialize_connection actions: %s" % disco_moves) - - self.assertEquals(1, len(disco_moves)) - disco_move = disco_moves[0] - self.assertEquals(disco_move['id'], volume_id) - - last_days_of_disco_moves = driver.LoggingVolumeDriver.logs_like( - 'terminate_connection', - id=volume_id) - LOG.debug("terminate_connection actions: %s" % - last_days_of_disco_moves) - - self.assertEquals(1, len(last_days_of_disco_moves)) - undisco_move = last_days_of_disco_moves[0] - self.assertEquals(undisco_move['id'], volume_id) - self.assertEquals(undisco_move['mountpoint'], device) - def test_create_volume_with_metadata(self): """Creates and deletes a volume.""" @@ -87,7 +87,8 @@ setup(name='nova', 'bin/nova-api', 'bin/nova-api-ec2', 'bin/nova-api-metadata', - 'bin/nova-api-os', + 'bin/nova-api-os-compute', + 'bin/nova-api-os-volume', 'bin/nova-compute', 'bin/nova-console', 'bin/nova-dhcpbridge', |