From de635fc882caebd5d5c9701e755a7174e37a05c5 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Fri, 11 Nov 2011 13:26:13 -0500 Subject: Creating new v2 namespace in nova.api.openstack Related to blueprint separate-nova-adminapi Change-Id: Ida35372b7263c4a4efdafd35faa1325c4436459b --- nova/tests/api/openstack/__init__.py | 16 - nova/tests/api/openstack/contrib/__init__.py | 15 - .../api/openstack/contrib/test_admin_actions.py | 86 - .../api/openstack/contrib/test_createserverext.py | 431 --- .../api/openstack/contrib/test_disk_config.py | 248 -- .../api/openstack/contrib/test_extendedstatus.py | 87 - .../openstack/contrib/test_flavors_extra_specs.py | 171 - .../api/openstack/contrib/test_floating_ips.py | 270 -- nova/tests/api/openstack/contrib/test_keypairs.py | 112 - .../api/openstack/contrib/test_multinic_xs.py | 113 - nova/tests/api/openstack/contrib/test_quotas.py | 134 - nova/tests/api/openstack/contrib/test_rescue.py | 78 - .../api/openstack/contrib/test_security_groups.py | 848 ----- .../openstack/contrib/test_simple_tenant_usage.py | 172 - .../openstack/contrib/test_virtual_interfaces.py | 55 - .../api/openstack/contrib/test_volume_types.py | 165 - .../contrib/test_volume_types_extra_specs.py | 169 - nova/tests/api/openstack/contrib/test_volumes.py | 88 - nova/tests/api/openstack/contrib/test_vsa.py | 450 --- nova/tests/api/openstack/extensions/__init__.py | 15 - nova/tests/api/openstack/extensions/foxinsocks.py | 94 - nova/tests/api/openstack/fakes.py | 53 +- nova/tests/api/openstack/test_accounts.py | 162 - nova/tests/api/openstack/test_api.py | 127 - nova/tests/api/openstack/test_auth.py | 315 -- nova/tests/api/openstack/test_consoles.py | 299 -- nova/tests/api/openstack/test_extensions.py | 515 --- nova/tests/api/openstack/test_faults.py | 11 +- nova/tests/api/openstack/test_flavors.py | 670 ---- nova/tests/api/openstack/test_image_metadata.py | 201 -- nova/tests/api/openstack/test_images.py | 1645 --------- nova/tests/api/openstack/test_limits.py | 939 ----- nova/tests/api/openstack/test_server_actions.py | 866 ----- nova/tests/api/openstack/test_server_metadata.py | 361 -- nova/tests/api/openstack/test_servers.py | 3638 -------------------- nova/tests/api/openstack/test_urlmap.py | 84 - nova/tests/api/openstack/test_users.py | 157 - nova/tests/api/openstack/test_versions.py | 671 ---- nova/tests/api/openstack/test_zones.py | 283 -- nova/tests/api/openstack/v2/__init__.py | 16 + nova/tests/api/openstack/v2/contrib/__init__.py | 15 + .../api/openstack/v2/contrib/test_admin_actions.py | 86 + .../openstack/v2/contrib/test_createserverext.py | 431 +++ .../api/openstack/v2/contrib/test_disk_config.py | 248 ++ .../openstack/v2/contrib/test_extendedstatus.py | 88 + .../v2/contrib/test_flavors_extra_specs.py | 170 + .../api/openstack/v2/contrib/test_floating_ips.py | 269 ++ .../api/openstack/v2/contrib/test_keypairs.py | 113 + .../api/openstack/v2/contrib/test_multinic_xs.py | 114 + nova/tests/api/openstack/v2/contrib/test_quotas.py | 133 + nova/tests/api/openstack/v2/contrib/test_rescue.py | 79 + .../openstack/v2/contrib/test_security_groups.py | 849 +++++ .../v2/contrib/test_simple_tenant_usage.py | 172 + .../v2/contrib/test_virtual_interfaces.py | 56 + .../api/openstack/v2/contrib/test_volume_types.py | 167 + .../v2/contrib/test_volume_types_extra_specs.py | 168 + .../tests/api/openstack/v2/contrib/test_volumes.py | 89 + nova/tests/api/openstack/v2/contrib/test_vsa.py | 449 +++ nova/tests/api/openstack/v2/extensions/__init__.py | 15 + .../api/openstack/v2/extensions/foxinsocks.py | 94 + nova/tests/api/openstack/v2/test_accounts.py | 162 + nova/tests/api/openstack/v2/test_api.py | 126 + nova/tests/api/openstack/v2/test_auth.py | 315 ++ nova/tests/api/openstack/v2/test_consoles.py | 299 ++ nova/tests/api/openstack/v2/test_extensions.py | 516 +++ nova/tests/api/openstack/v2/test_flavors.py | 670 ++++ nova/tests/api/openstack/v2/test_image_metadata.py | 200 ++ nova/tests/api/openstack/v2/test_images.py | 1646 +++++++++ nova/tests/api/openstack/v2/test_limits.py | 940 +++++ nova/tests/api/openstack/v2/test_server_actions.py | 881 +++++ .../tests/api/openstack/v2/test_server_metadata.py | 361 ++ nova/tests/api/openstack/v2/test_servers.py | 3631 +++++++++++++++++++ nova/tests/api/openstack/v2/test_urlmap.py | 84 + nova/tests/api/openstack/v2/test_users.py | 157 + nova/tests/api/openstack/v2/test_versions.py | 671 ++++ nova/tests/api/openstack/v2/test_zones.py | 283 ++ nova/tests/integrated/test_extensions.py | 2 +- nova/tests/test_hosts.py | 3 +- 78 files changed, 14797 insertions(+), 14785 deletions(-) delete mode 100644 nova/tests/api/openstack/contrib/__init__.py delete mode 100644 nova/tests/api/openstack/contrib/test_admin_actions.py delete mode 100644 nova/tests/api/openstack/contrib/test_createserverext.py delete mode 100644 nova/tests/api/openstack/contrib/test_disk_config.py delete mode 100644 nova/tests/api/openstack/contrib/test_extendedstatus.py delete mode 100644 nova/tests/api/openstack/contrib/test_flavors_extra_specs.py delete mode 100644 nova/tests/api/openstack/contrib/test_floating_ips.py delete mode 100644 nova/tests/api/openstack/contrib/test_keypairs.py delete mode 100644 nova/tests/api/openstack/contrib/test_multinic_xs.py delete mode 100644 nova/tests/api/openstack/contrib/test_quotas.py delete mode 100644 nova/tests/api/openstack/contrib/test_rescue.py delete mode 100644 nova/tests/api/openstack/contrib/test_security_groups.py delete mode 100644 nova/tests/api/openstack/contrib/test_simple_tenant_usage.py delete mode 100644 nova/tests/api/openstack/contrib/test_virtual_interfaces.py delete mode 100644 nova/tests/api/openstack/contrib/test_volume_types.py delete mode 100644 nova/tests/api/openstack/contrib/test_volume_types_extra_specs.py delete mode 100644 nova/tests/api/openstack/contrib/test_volumes.py delete mode 100644 nova/tests/api/openstack/contrib/test_vsa.py delete mode 100644 nova/tests/api/openstack/extensions/__init__.py delete mode 100644 nova/tests/api/openstack/extensions/foxinsocks.py delete mode 100644 nova/tests/api/openstack/test_accounts.py delete mode 100644 nova/tests/api/openstack/test_api.py delete mode 100644 nova/tests/api/openstack/test_auth.py delete mode 100644 nova/tests/api/openstack/test_consoles.py delete mode 100644 nova/tests/api/openstack/test_extensions.py delete mode 100644 nova/tests/api/openstack/test_flavors.py delete mode 100644 nova/tests/api/openstack/test_image_metadata.py delete mode 100644 nova/tests/api/openstack/test_images.py delete mode 100644 nova/tests/api/openstack/test_limits.py delete mode 100644 nova/tests/api/openstack/test_server_actions.py delete mode 100644 nova/tests/api/openstack/test_server_metadata.py delete mode 100644 nova/tests/api/openstack/test_servers.py delete mode 100644 nova/tests/api/openstack/test_urlmap.py delete mode 100644 nova/tests/api/openstack/test_users.py delete mode 100644 nova/tests/api/openstack/test_versions.py delete mode 100644 nova/tests/api/openstack/test_zones.py create mode 100644 nova/tests/api/openstack/v2/__init__.py create mode 100644 nova/tests/api/openstack/v2/contrib/__init__.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_admin_actions.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_createserverext.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_disk_config.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_extendedstatus.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_flavors_extra_specs.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_floating_ips.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_keypairs.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_multinic_xs.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_quotas.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_rescue.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_security_groups.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_simple_tenant_usage.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_virtual_interfaces.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_volume_types.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_volume_types_extra_specs.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_volumes.py create mode 100644 nova/tests/api/openstack/v2/contrib/test_vsa.py create mode 100644 nova/tests/api/openstack/v2/extensions/__init__.py create mode 100644 nova/tests/api/openstack/v2/extensions/foxinsocks.py create mode 100644 nova/tests/api/openstack/v2/test_accounts.py create mode 100644 nova/tests/api/openstack/v2/test_api.py create mode 100644 nova/tests/api/openstack/v2/test_auth.py create mode 100644 nova/tests/api/openstack/v2/test_consoles.py create mode 100644 nova/tests/api/openstack/v2/test_extensions.py create mode 100644 nova/tests/api/openstack/v2/test_flavors.py create mode 100644 nova/tests/api/openstack/v2/test_image_metadata.py create mode 100644 nova/tests/api/openstack/v2/test_images.py create mode 100644 nova/tests/api/openstack/v2/test_limits.py create mode 100644 nova/tests/api/openstack/v2/test_server_actions.py create mode 100644 nova/tests/api/openstack/v2/test_server_metadata.py create mode 100644 nova/tests/api/openstack/v2/test_servers.py create mode 100644 nova/tests/api/openstack/v2/test_urlmap.py create mode 100644 nova/tests/api/openstack/v2/test_users.py create mode 100644 nova/tests/api/openstack/v2/test_versions.py create mode 100644 nova/tests/api/openstack/v2/test_zones.py (limited to 'nova/tests') diff --git a/nova/tests/api/openstack/__init__.py b/nova/tests/api/openstack/__init__.py index 00fcfbb00..e69de29bb 100644 --- a/nova/tests/api/openstack/__init__.py +++ b/nova/tests/api/openstack/__init__.py @@ -1,16 +0,0 @@ -# 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/contrib/__init__.py b/nova/tests/api/openstack/contrib/__init__.py deleted file mode 100644 index 848908a95..000000000 --- a/nova/tests/api/openstack/contrib/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/nova/tests/api/openstack/contrib/test_admin_actions.py b/nova/tests/api/openstack/contrib/test_admin_actions.py deleted file mode 100644 index 4b62c0ba7..000000000 --- a/nova/tests/api/openstack/contrib/test_admin_actions.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright 2011 OpenStack LLC. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import datetime -import json -import webob - -from nova import compute -from nova import flags -from nova import test -from nova.tests.api.openstack import fakes - - -FLAGS = flags.FLAGS - -INSTANCE = { - "id": 1, - "name": "fake", - "display_name": "test_server", - "uuid": "abcd", - "user_id": 'fake_user_id', - "tenant_id": 'fake_tenant_id', - "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), - "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), - "security_groups": [{"id": 1, "name": "test"}], - "progress": 0, - "image_ref": 'http://foo.com/123', - "fixed_ips": [], - "instance_type": {"flavorid": '124'}, - } - - -def fake_compute_api(cls, req, id): - return True - - -def fake_compute_api_get(self, context, instance_id): - return {'id': 1, 'uuid': instance_id} - - -class AdminActionsTest(test.TestCase): - - _actions = ('pause', 'unpause', 'suspend', 'resume', 'migrate', - 'resetNetwork', 'injectNetworkInfo', 'lock', 'unlock') - - _methods = ('pause', 'unpause', 'suspend', 'resume', 'resize', - 'reset_network', 'inject_network_info', 'lock', 'unlock') - - def setUp(self): - super(AdminActionsTest, self).setUp() - self.flags(allow_admin_api=True) - self.stubs.Set(compute.API, 'get', fake_compute_api_get) - for _method in self._methods: - self.stubs.Set(compute.API, _method, fake_compute_api) - - def test_admin_api_enabled(self): - app = fakes.wsgi_app() - for _action in self._actions: - req = webob.Request.blank('/v1.1/fake/servers/abcd/action') - req.method = 'POST' - req.body = json.dumps({_action: None}) - req.content_type = 'application/json' - res = req.get_response(app) - self.assertEqual(res.status_int, 202) - - def test_admin_api_disabled(self): - FLAGS.allow_admin_api = False - app = fakes.wsgi_app() - for _action in self._actions: - req = webob.Request.blank('/v1.1/fake/servers/abcd/action') - req.method = 'POST' - req.body = json.dumps({_action: None}) - req.content_type = 'application/json' - res = req.get_response(app) - self.assertEqual(res.status_int, 404) diff --git a/nova/tests/api/openstack/contrib/test_createserverext.py b/nova/tests/api/openstack/contrib/test_createserverext.py deleted file mode 100644 index f7ee96917..000000000 --- a/nova/tests/api/openstack/contrib/test_createserverext.py +++ /dev/null @@ -1,431 +0,0 @@ -# vim: tabstop=5 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 base64 -import datetime -import json -from xml.dom import minidom - -import webob - -from nova import db -from nova import exception -from nova import flags -from nova import rpc -from nova import test -import nova.api.openstack -from nova.tests.api.openstack import fakes - - -FLAGS = flags.FLAGS -FLAGS.verbose = True - -FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' - -FAKE_NETWORKS = [('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', '10.0.1.12'), - ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', '10.0.2.12')] - -DUPLICATE_NETWORKS = [('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', '10.0.1.12'), - ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', '10.0.1.12')] - -INVALID_NETWORKS = [('invalid', 'invalid-ip-address')] - -INSTANCE = { - "id": 1, - "name": "fake", - "display_name": "test_server", - "uuid": FAKE_UUID, - "user_id": 'fake_user_id', - "tenant_id": 'fake_tenant_id', - "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), - "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), - "security_groups": [{"id": 1, "name": "test"}], - "progress": 0, - "image_ref": 'http://foo.com/123', - "fixed_ips": [], - "instance_type": {"flavorid": '124'}, - } - - -def return_server_by_id(context, id, session=None): - INSTANCE['id'] = id - return INSTANCE - - -def return_security_group_non_existing(context, project_id, group_name): - raise exception.SecurityGroupNotFoundForProject(project_id=project_id, - security_group_id=group_name) - - -def return_security_group_get_by_name(context, project_id, group_name): - return {'id': 1, 'name': group_name} - - -def return_security_group_get(context, security_group_id, session): - return {'id': security_group_id} - - -def return_instance_add_security_group(context, instance_id, - security_group_id): - pass - - -class CreateserverextTest(test.TestCase): - - def setUp(self): - super(CreateserverextTest, self).setUp() - - def tearDown(self): - super(CreateserverextTest, self).tearDown() - - def _make_stub_method(self, canned_return): - def stub_method(*args, **kwargs): - return canned_return - return stub_method - - def _setup_mock_compute_api(self): - - class MockComputeAPI(nova.compute.API): - - def __init__(self): - self.injected_files = None - self.networks = None - self.user_data = None - self.db = db - - def create(self, *args, **kwargs): - if 'injected_files' in kwargs: - self.injected_files = kwargs['injected_files'] - else: - self.injected_files = None - - if 'requested_networks' in kwargs: - self.networks = kwargs['requested_networks'] - else: - self.networks = None - - if 'user_data' in kwargs: - self.user_data = kwargs['user_data'] - - resv_id = None - - return ([{'id': '1234', 'display_name': 'fakeinstance', - 'uuid': FAKE_UUID, - 'user_id': 'fake', - 'project_id': 'fake', - 'created_at': "", - 'updated_at': "", - 'fixed_ips': [], - 'progress': 0}], resv_id) - - def set_admin_password(self, *args, **kwargs): - pass - - compute_api = MockComputeAPI() - self.stubs.Set(nova.compute, 'API', - self._make_stub_method(compute_api)) - return compute_api - - def _setup_mock_network_api(self): - fakes.stub_out_nw_api(self.stubs) - - def _create_security_group_request_dict(self, security_groups): - server = {} - server['name'] = 'new-server-test' - server['imageRef'] = 'cedef40a-ed67-4d10-800e-17455edce175' - server['flavorRef'] = 1 - if security_groups is not None: - sg_list = [] - for name in security_groups: - sg_list.append({'name': name}) - server['security_groups'] = sg_list - return {'server': server} - - def _create_networks_request_dict(self, networks): - server = {} - server['name'] = 'new-server-test' - server['imageRef'] = 'cedef40a-ed67-4d10-800e-17455edce175' - server['flavorRef'] = 1 - if networks is not None: - network_list = [] - for uuid, fixed_ip in networks: - network_list.append({'uuid': uuid, 'fixed_ip': fixed_ip}) - server['networks'] = network_list - return {'server': server} - - def _create_user_data_request_dict(self, user_data): - server = {} - server['name'] = 'new-server-test' - server['imageRef'] = 'cedef40a-ed67-4d10-800e-17455edce175' - server['flavorRef'] = 1 - server['user_data'] = user_data - return {'server': server} - - def _get_create_request_json(self, body_dict): - req = webob.Request.blank('/v1.1/123/os-create-server-ext') - req.headers['Content-Type'] = 'application/json' - req.method = 'POST' - req.body = json.dumps(body_dict) - return req - - def _run_create_instance_with_mock_compute_api(self, request): - compute_api = self._setup_mock_compute_api() - self._setup_mock_network_api() - response = request.get_response(fakes.wsgi_app()) - return compute_api, response - - def _format_xml_request_body(self, body_dict): - server = body_dict['server'] - body_parts = [] - body_parts.extend([ - '', - '' % ( - server['name'], server['imageRef'], server['flavorRef'])]) - if 'metadata' in server: - metadata = server['metadata'] - body_parts.append('') - for item in metadata.iteritems(): - body_parts.append('%s' % item) - body_parts.append('') - if 'personality' in server: - personalities = server['personality'] - body_parts.append('') - for file in personalities: - item = (file['path'], file['contents']) - body_parts.append('%s' % item) - body_parts.append('') - if 'networks' in server: - networks = server['networks'] - body_parts.append('') - for network in networks: - item = (network['uuid'], network['fixed_ip']) - body_parts.append('' - % item) - body_parts.append('') - body_parts.append('') - return ''.join(body_parts) - - def _get_create_request_xml(self, body_dict): - req = webob.Request.blank('/v1.1/123/os-create-server-ext') - req.content_type = 'application/xml' - req.accept = 'application/xml' - req.method = 'POST' - req.body = self._format_xml_request_body(body_dict) - return req - - def _create_instance_with_networks_json(self, networks): - body_dict = self._create_networks_request_dict(networks) - request = self._get_create_request_json(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - return request, response, compute_api.networks - - def _create_instance_with_user_data_json(self, networks): - body_dict = self._create_user_data_request_dict(networks) - request = self._get_create_request_json(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - return request, response, compute_api.user_data - - def _create_instance_with_networks_xml(self, networks): - body_dict = self._create_networks_request_dict(networks) - request = self._get_create_request_xml(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - return request, response, compute_api.networks - - def test_create_instance_with_no_networks(self): - request, response, networks = \ - self._create_instance_with_networks_json(networks=None) - self.assertEquals(response.status_int, 202) - self.assertEquals(networks, None) - - def test_create_instance_with_no_networks_xml(self): - request, response, networks = \ - self._create_instance_with_networks_xml(networks=None) - self.assertEquals(response.status_int, 202) - self.assertEquals(networks, None) - - def test_create_instance_with_one_network(self): - request, response, networks = \ - self._create_instance_with_networks_json([FAKE_NETWORKS[0]]) - self.assertEquals(response.status_int, 202) - self.assertEquals(networks, [FAKE_NETWORKS[0]]) - - def test_create_instance_with_one_network_xml(self): - request, response, networks = \ - self._create_instance_with_networks_xml([FAKE_NETWORKS[0]]) - self.assertEquals(response.status_int, 202) - self.assertEquals(networks, [FAKE_NETWORKS[0]]) - - def test_create_instance_with_two_networks(self): - request, response, networks = \ - self._create_instance_with_networks_json(FAKE_NETWORKS) - self.assertEquals(response.status_int, 202) - self.assertEquals(networks, FAKE_NETWORKS) - - def test_create_instance_with_two_networks_xml(self): - request, response, networks = \ - self._create_instance_with_networks_xml(FAKE_NETWORKS) - self.assertEquals(response.status_int, 202) - self.assertEquals(networks, FAKE_NETWORKS) - - def test_create_instance_with_duplicate_networks(self): - request, response, networks = \ - self._create_instance_with_networks_json(DUPLICATE_NETWORKS) - self.assertEquals(response.status_int, 400) - self.assertEquals(networks, None) - - def test_create_instance_with_duplicate_networks_xml(self): - request, response, networks = \ - self._create_instance_with_networks_xml(DUPLICATE_NETWORKS) - self.assertEquals(response.status_int, 400) - self.assertEquals(networks, None) - - def test_create_instance_with_network_no_id(self): - body_dict = self._create_networks_request_dict([FAKE_NETWORKS[0]]) - del body_dict['server']['networks'][0]['uuid'] - request = self._get_create_request_json(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 400) - self.assertEquals(compute_api.networks, None) - - def test_create_instance_with_network_no_id_xml(self): - body_dict = self._create_networks_request_dict([FAKE_NETWORKS[0]]) - request = self._get_create_request_xml(body_dict) - uuid = ' uuid="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"' - request.body = request.body.replace(uuid, '') - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 400) - self.assertEquals(compute_api.networks, None) - - def test_create_instance_with_network_invalid_id(self): - request, response, networks = \ - self._create_instance_with_networks_json(INVALID_NETWORKS) - self.assertEquals(response.status_int, 400) - self.assertEquals(networks, None) - - def test_create_instance_with_network_invalid_id_xml(self): - request, response, networks = \ - self._create_instance_with_networks_xml(INVALID_NETWORKS) - self.assertEquals(response.status_int, 400) - self.assertEquals(networks, None) - - def test_create_instance_with_network_empty_fixed_ip(self): - networks = [('1', '')] - request, response, networks = \ - self._create_instance_with_networks_json(networks) - self.assertEquals(response.status_int, 400) - self.assertEquals(networks, None) - - def test_create_instance_with_network_non_string_fixed_ip(self): - networks = [('1', 12345)] - request, response, networks = \ - self._create_instance_with_networks_json(networks) - self.assertEquals(response.status_int, 400) - self.assertEquals(networks, None) - - def test_create_instance_with_network_empty_fixed_ip_xml(self): - networks = [('1', '')] - request, response, networks = \ - self._create_instance_with_networks_xml(networks) - self.assertEquals(response.status_int, 400) - self.assertEquals(networks, None) - - def test_create_instance_with_network_no_fixed_ip(self): - body_dict = self._create_networks_request_dict([FAKE_NETWORKS[0]]) - del body_dict['server']['networks'][0]['fixed_ip'] - request = self._get_create_request_json(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 202) - self.assertEquals(compute_api.networks, - [('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', None)]) - - def test_create_instance_with_network_no_fixed_ip_xml(self): - body_dict = self._create_networks_request_dict([FAKE_NETWORKS[0]]) - request = self._get_create_request_xml(body_dict) - request.body = request.body.replace(' fixed_ip="10.0.1.12"', '') - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 202) - self.assertEquals(compute_api.networks, - [('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', None)]) - - def test_create_instance_with_userdata(self): - user_data_contents = '#!/bin/bash\necho "Oh no!"\n' - user_data_contents = base64.b64encode(user_data_contents) - request, response, user_data = \ - self._create_instance_with_user_data_json(user_data_contents) - self.assertEquals(response.status_int, 202) - self.assertEquals(user_data, user_data_contents) - - def test_create_instance_with_userdata_none(self): - user_data_contents = None - request, response, user_data = \ - self._create_instance_with_user_data_json(user_data_contents) - self.assertEquals(response.status_int, 202) - self.assertEquals(user_data, user_data_contents) - - def test_create_instance_with_userdata_with_non_b64_content(self): - user_data_contents = '#!/bin/bash\necho "Oh no!"\n' - request, response, user_data = \ - self._create_instance_with_user_data_json(user_data_contents) - self.assertEquals(response.status_int, 400) - self.assertEquals(user_data, None) - - def test_create_instance_with_security_group_json(self): - security_groups = ['test', 'test1'] - self.stubs.Set(nova.db, 'security_group_get_by_name', - return_security_group_get_by_name) - self.stubs.Set(nova.db, 'instance_add_security_group', - return_instance_add_security_group) - self._setup_mock_network_api() - body_dict = self._create_security_group_request_dict(security_groups) - request = self._get_create_request_json(body_dict) - compute_api, response = \ - self._run_create_instance_with_mock_compute_api(request) - self.assertEquals(response.status_int, 202) - - def test_get_server_by_id_verify_security_groups_json(self): - self.stubs.Set(nova.db, 'instance_get', return_server_by_id) - self._setup_mock_network_api() - req = webob.Request.blank('/v1.1/123/os-create-server-ext/1') - req.headers['Content-Type'] = 'application/json' - response = req.get_response(fakes.wsgi_app()) - self.assertEquals(response.status_int, 200) - res_dict = json.loads(response.body) - expected_security_group = [{"name": "test"}] - self.assertEquals(res_dict['server']['security_groups'], - expected_security_group) - - def test_get_server_by_id_verify_security_groups_xml(self): - self.stubs.Set(nova.db, 'instance_get', return_server_by_id) - self._setup_mock_network_api() - req = webob.Request.blank('/v1.1/123/os-create-server-ext/1') - req.headers['Accept'] = 'application/xml' - response = req.get_response(fakes.wsgi_app()) - self.assertEquals(response.status_int, 200) - dom = minidom.parseString(response.body) - server = dom.childNodes[0] - sec_groups = server.getElementsByTagName('security_groups')[0] - sec_group = sec_groups.getElementsByTagName('security_group')[0] - self.assertEqual(INSTANCE['security_groups'][0]['name'], - sec_group.getAttribute("name")) diff --git a/nova/tests/api/openstack/contrib/test_disk_config.py b/nova/tests/api/openstack/contrib/test_disk_config.py deleted file mode 100644 index 40875cb84..000000000 --- a/nova/tests/api/openstack/contrib/test_disk_config.py +++ /dev/null @@ -1,248 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -import datetime - -import nova.db.api -import nova.rpc - -from nova import flags -from nova import test -from nova import utils -from nova.api import openstack -from nova.api.openstack import extensions -from nova.api.openstack import servers -from nova.api.openstack import wsgi -from nova.tests.api.openstack import fakes - -MANUAL_INSTANCE_UUID = fakes.FAKE_UUID -AUTO_INSTANCE_UUID = fakes.FAKE_UUID.replace('a', 'b') - -stub_instance = fakes.stub_instance -FLAGS = flags.FLAGS - - -def instance_addresses(context, instance_id): - return None - - -class DiskConfigTestCase(test.TestCase): - - def setUp(self): - super(DiskConfigTestCase, self).setUp() - self.flags(verbose=True) - fakes.stub_out_nw_api(self.stubs) - - FAKE_INSTANCES = [ - fakes.stub_instance(1, - uuid=MANUAL_INSTANCE_UUID, - auto_disk_config=False), - fakes.stub_instance(2, - uuid=AUTO_INSTANCE_UUID, - auto_disk_config=True) - ] - - def fake_instance_get(context, id_): - for instance in FAKE_INSTANCES: - if id_ == instance['id']: - return instance - - self.stubs.Set(nova.db.api, 'instance_get', fake_instance_get) - self.stubs.Set(nova.db, 'instance_get', fake_instance_get) - - def fake_instance_get_by_uuid(context, uuid): - for instance in FAKE_INSTANCES: - if uuid == instance['uuid']: - return instance - - self.stubs.Set(nova.db, 'instance_get_by_uuid', - fake_instance_get_by_uuid) - - def fake_instance_get_all(context, *args, **kwargs): - return FAKE_INSTANCES - - self.stubs.Set(nova.db, 'instance_get_all', fake_instance_get_all) - self.stubs.Set(nova.db.api, 'instance_get_all_by_filters', - fake_instance_get_all) - - def fake_instance_create(context, inst_, session=None): - class FakeModel(dict): - def save(self, session=None): - pass - - inst = FakeModel(**inst_) - inst['id'] = 1 - inst['uuid'] = AUTO_INSTANCE_UUID - inst['created_at'] = datetime.datetime(2010, 10, 10, 12, 0, 0) - inst['updated_at'] = datetime.datetime(2010, 10, 10, 12, 0, 0) - inst['progress'] = 0 - inst['name'] = 'instance-1' # this is a property - - def fake_instance_get_for_create(context, id_, session=None): - return inst - - self.stubs.Set(nova.db, 'instance_get', - fake_instance_get_for_create) - self.stubs.Set(nova.db.api, 'instance_get', - fake_instance_get_for_create) - self.stubs.Set(nova.db.sqlalchemy.api, 'instance_get', - fake_instance_get_for_create) - - def fake_instance_add_security_group(context, instance_id, - security_group_id): - pass - - self.stubs.Set(nova.db.sqlalchemy.api, - 'instance_add_security_group', - fake_instance_add_security_group) - - return inst - - self.stubs.Set(nova.db, 'instance_create', fake_instance_create) - - app = openstack.APIRouter() - app = extensions.ExtensionMiddleware(app) - app = wsgi.LazySerializationMiddleware(app) - self.app = app - - def assertDiskConfig(self, dict_, value): - self.assert_('RAX-DCF:diskConfig' in dict_) - self.assertEqual(dict_['RAX-DCF:diskConfig'], value) - - def test_show_server(self): - req = fakes.HTTPRequest.blank( - '/fake/servers/%s' % MANUAL_INSTANCE_UUID) - res = req.get_response(self.app) - server_dict = utils.loads(res.body)['server'] - self.assertDiskConfig(server_dict, 'MANUAL') - - req = fakes.HTTPRequest.blank( - '/fake/servers/%s' % AUTO_INSTANCE_UUID) - res = req.get_response(self.app) - server_dict = utils.loads(res.body)['server'] - self.assertDiskConfig(server_dict, 'AUTO') - - def test_detail_servers(self): - req = fakes.HTTPRequest.blank('/fake/servers/detail') - res = req.get_response(self.app) - server_dicts = utils.loads(res.body)['servers'] - - expectations = ['MANUAL', 'AUTO'] - for server_dict, expected in zip(server_dicts, expectations): - self.assertDiskConfig(server_dict, expected) - - def test_show_image(self): - req = fakes.HTTPRequest.blank( - '/fake/images/a440c04b-79fa-479c-bed1-0b816eaec379') - res = req.get_response(self.app) - image_dict = utils.loads(res.body)['image'] - self.assertDiskConfig(image_dict, 'MANUAL') - - req = fakes.HTTPRequest.blank( - '/fake/images/70a599e0-31e7-49b7-b260-868f441e862b') - res = req.get_response(self.app) - image_dict = utils.loads(res.body)['image'] - self.assertDiskConfig(image_dict, 'AUTO') - - def test_detail_image(self): - req = fakes.HTTPRequest.blank('/fake/images/detail') - res = req.get_response(self.app) - image_dicts = utils.loads(res.body)['images'] - - expectations = ['MANUAL', 'AUTO'] - for image_dict, expected in zip(image_dicts, expectations): - # NOTE(sirp): image fixtures 6 and 7 are setup for - # auto_disk_config testing - if image_dict['id'] in (6, 7): - self.assertDiskConfig(image_dict, expected) - - def test_create_server_override_auto(self): - req = fakes.HTTPRequest.blank('/fake/servers') - req.method = 'POST' - req.content_type = 'application/json' - body = {'server': { - 'name': 'server_test', - 'imageRef': 'cedef40a-ed67-4d10-800e-17455edce175', - 'flavorRef': '1', - 'RAX-DCF:diskConfig': 'AUTO' - }} - - req.body = utils.dumps(body) - res = req.get_response(self.app) - server_dict = utils.loads(res.body)['server'] - self.assertDiskConfig(server_dict, 'AUTO') - - def test_create_server_override_manual(self): - req = fakes.HTTPRequest.blank('/fake/servers') - req.method = 'POST' - req.content_type = 'application/json' - body = {'server': { - 'name': 'server_test', - 'imageRef': 'cedef40a-ed67-4d10-800e-17455edce175', - 'flavorRef': '1', - 'RAX-DCF:diskConfig': 'MANUAL' - }} - - req.body = utils.dumps(body) - res = req.get_response(self.app) - server_dict = utils.loads(res.body)['server'] - self.assertDiskConfig(server_dict, 'MANUAL') - - def test_create_server_detect_from_image(self): - """If user doesn't pass in diskConfig for server, use image metadata - to specify AUTO or MANUAL. - """ - req = fakes.HTTPRequest.blank('/fake/servers') - req.method = 'POST' - req.content_type = 'application/json' - body = {'server': { - 'name': 'server_test', - 'imageRef': 'a440c04b-79fa-479c-bed1-0b816eaec379', - 'flavorRef': '1', - }} - - req.body = utils.dumps(body) - res = req.get_response(self.app) - server_dict = utils.loads(res.body)['server'] - self.assertDiskConfig(server_dict, 'MANUAL') - - req = fakes.HTTPRequest.blank('/fake/servers') - req.method = 'POST' - req.content_type = 'application/json' - body = {'server': { - 'name': 'server_test', - 'imageRef': '70a599e0-31e7-49b7-b260-868f441e862b', - 'flavorRef': '1', - }} - - req.body = utils.dumps(body) - res = req.get_response(self.app) - server_dict = utils.loads(res.body)['server'] - self.assertDiskConfig(server_dict, 'AUTO') - - def test_update_server_invalid_disk_config(self): - """Return BadRequest if user passes an invalid diskConfig value.""" - req = fakes.HTTPRequest.blank( - '/fake/servers/%s' % MANUAL_INSTANCE_UUID) - req.method = 'PUT' - req.content_type = 'application/json' - body = {'server': {'RAX-DCF:diskConfig': 'server_test'}} - req.body = utils.dumps(body) - res = req.get_response(self.app) - self.assertEqual(res.status_int, 400) - expected_msg = '{"badRequest": {"message": "RAX-DCF:diskConfig must'\ - ' be either \'MANUAL\' or \'AUTO\'.", "code": 400}}' - self.assertEqual(res.body, expected_msg) diff --git a/nova/tests/api/openstack/contrib/test_extendedstatus.py b/nova/tests/api/openstack/contrib/test_extendedstatus.py deleted file mode 100644 index 0065f6fde..000000000 --- a/nova/tests/api/openstack/contrib/test_extendedstatus.py +++ /dev/null @@ -1,87 +0,0 @@ -# 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. - -import json -import webob - -from nova import compute -from nova import exception -from nova import flags -from nova import test -from nova.tests.api.openstack import fakes - - -FLAGS = flags.FLAGS -FLAGS.verbose = True - - -def fake_compute_get(*args, **kwargs): - return fakes.stub_instance(1, task_state="kayaking", - vm_state="slightly crunchy", - power_state="empowered") - - -class ExtendedStatusTest(test.TestCase): - - def setUp(self): - super(ExtendedStatusTest, self).setUp() - self.uuid = '70f6db34-de8d-4fbd-aafb-4065bdfa6114' - self.url = '/v1.1/openstack/servers/%s' % self.uuid - fakes.stub_out_nw_api(self.stubs) - self.stubs.Set(compute.api.API, 'routing_get', fake_compute_get) - - def _make_request(self): - req = webob.Request.blank(self.url) - req.headers['Accept'] = 'application/json' - res = req.get_response(fakes.wsgi_app()) - return res - - def assertServerStates(self, server, vm_state, power_state, task_state): - self.assertEqual(server.get('OS-EXT-STS:vm_state'), vm_state) - self.assertEqual(server.get('OS-EXT-STS:power_state'), power_state) - self.assertEqual(server.get('OS-EXT-STS:task_state'), task_state) - - def test_extended_status_with_admin(self): - self.flags(allow_admin_api=True) - res = self._make_request() - body = json.loads(res.body) - - self.assertEqual(res.status_int, 200) - self.assertServerStates(body['server'], - vm_state='slightly crunchy', - power_state='empowered', - task_state='kayaking') - - def test_extended_status_no_admin(self): - self.flags(allow_admin_api=False) - res = self._make_request() - body = json.loads(res.body) - - self.assertEqual(res.status_int, 200) - self.assertServerStates(body['server'], - vm_state=None, - power_state=None, - task_state=None) - - def test_extended_status_no_instance_fails(self): - self.flags(allow_admin_api=True) - - def fake_compute_get(*args, **kwargs): - raise exception.InstanceNotFound() - - self.stubs.Set(compute.api.API, 'routing_get', fake_compute_get) - res = self._make_request() - - self.assertEqual(res.status_int, 404) diff --git a/nova/tests/api/openstack/contrib/test_flavors_extra_specs.py b/nova/tests/api/openstack/contrib/test_flavors_extra_specs.py deleted file mode 100644 index 5784743ee..000000000 --- a/nova/tests/api/openstack/contrib/test_flavors_extra_specs.py +++ /dev/null @@ -1,171 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 University of Southern California -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import json -import stubout -import webob -import os.path - - -from nova import test -from nova.api import openstack -from nova.api.openstack import extensions -from nova.api.openstack.contrib import flavorextraspecs -from nova.tests.api.openstack import fakes -import nova.wsgi - - -def return_create_flavor_extra_specs(context, flavor_id, extra_specs): - return stub_flavor_extra_specs() - - -def return_flavor_extra_specs(context, flavor_id): - return stub_flavor_extra_specs() - - -def return_empty_flavor_extra_specs(context, flavor_id): - return {} - - -def delete_flavor_extra_specs(context, flavor_id, key): - pass - - -def stub_flavor_extra_specs(): - specs = { - "key1": "value1", - "key2": "value2", - "key3": "value3", - "key4": "value4", - "key5": "value5"} - return specs - - -class FlavorsExtraSpecsTest(test.TestCase): - - def setUp(self): - super(FlavorsExtraSpecsTest, self).setUp() - fakes.stub_out_key_pair_funcs(self.stubs) - self.controller = flavorextraspecs.FlavorExtraSpecsController() - - def test_index(self): - self.stubs.Set(nova.db, 'instance_type_extra_specs_get', - return_flavor_extra_specs) - - req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs') - res_dict = self.controller.index(req, 1) - - self.assertEqual('value1', res_dict['extra_specs']['key1']) - - def test_index_no_data(self): - self.stubs.Set(nova.db, 'instance_type_extra_specs_get', - return_empty_flavor_extra_specs) - - req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs') - res_dict = self.controller.index(req, 1) - - self.assertEqual(0, len(res_dict['extra_specs'])) - - def test_show(self): - self.stubs.Set(nova.db, 'instance_type_extra_specs_get', - return_flavor_extra_specs) - - req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs' + - '/key5') - res_dict = self.controller.show(req, 1, 'key5') - - self.assertEqual('value5', res_dict['key5']) - - def test_show_spec_not_found(self): - self.stubs.Set(nova.db, 'instance_type_extra_specs_get', - return_empty_flavor_extra_specs) - - req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs' + - '/key6') - self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, - req, 1, 'key6') - - def test_delete(self): - self.stubs.Set(nova.db, 'instance_type_extra_specs_delete', - delete_flavor_extra_specs) - - req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs' + - '/key5') - self.controller.delete(req, 1, 'key5') - - def test_create(self): - self.stubs.Set(nova.db, - 'instance_type_extra_specs_update_or_create', - return_create_flavor_extra_specs) - body = {"extra_specs": {"key1": "value1"}} - - req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs') - res_dict = self.controller.create(req, 1, body) - - self.assertEqual('value1', res_dict['extra_specs']['key1']) - - def test_create_empty_body(self): - self.stubs.Set(nova.db, - 'instance_type_extra_specs_update_or_create', - return_create_flavor_extra_specs) - - req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, 1, '') - - def test_update_item(self): - self.stubs.Set(nova.db, - 'instance_type_extra_specs_update_or_create', - return_create_flavor_extra_specs) - body = {"key1": "value1"} - - req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs' + - '/key1') - res_dict = self.controller.update(req, 1, 'key1', body) - - self.assertEqual('value1', res_dict['key1']) - - def test_update_item_empty_body(self): - self.stubs.Set(nova.db, - 'instance_type_extra_specs_update_or_create', - return_create_flavor_extra_specs) - - req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs' + - '/key1') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, - req, 1, 'key1', '') - - def test_update_item_too_many_keys(self): - self.stubs.Set(nova.db, - 'instance_type_extra_specs_update_or_create', - return_create_flavor_extra_specs) - body = {"key1": "value1", "key2": "value2"} - - req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs' + - '/key1') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, - req, 1, 'key1', body) - - def test_update_item_body_uri_mismatch(self): - self.stubs.Set(nova.db, - 'instance_type_extra_specs_update_or_create', - return_create_flavor_extra_specs) - body = {"key1": "value1"} - - req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs/bad') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, - req, 1, 'bad', body) diff --git a/nova/tests/api/openstack/contrib/test_floating_ips.py b/nova/tests/api/openstack/contrib/test_floating_ips.py deleted file mode 100644 index 63831f31f..000000000 --- a/nova/tests/api/openstack/contrib/test_floating_ips.py +++ /dev/null @@ -1,270 +0,0 @@ -# Copyright 2011 Eldar Nugaev -# 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 json -import stubout -import webob - -from nova import compute -from nova import context -from nova import db -from nova import network -from nova import rpc -from nova import test -from nova.tests.api.openstack import fakes -from nova.tests.api.openstack import test_servers - - -from nova.api.openstack.contrib import floating_ips -from nova.api.openstack.contrib.floating_ips import _translate_floating_ip_view - - -def network_api_get_floating_ip(self, context, id): - return {'id': 1, 'address': '10.10.10.10', - 'fixed_ip': None} - - -def network_api_get_floating_ip_by_address(self, context, address): - return {'id': 1, 'address': '10.10.10.10', - 'fixed_ip': {'address': '10.0.0.1', 'instance_id': 1}} - - -def network_api_get_floating_ips_by_project(self, context): - return [{'id': 1, - 'address': '10.10.10.10', - 'fixed_ip': {'address': '10.0.0.1', 'instance_id': 1}}, - {'id': 2, - 'address': '10.10.10.11'}] - - -def network_api_allocate(self, context): - return '10.10.10.10' - - -def network_api_release(self, context, address): - pass - - -def compute_api_associate(self, context, instance_id, address): - pass - - -def network_api_associate(self, context, floating_address, fixed_address): - pass - - -def network_api_disassociate(self, context, floating_address): - pass - - -def network_get_instance_nw_info(self, context, instance): - info = { - 'label': 'fake', - 'gateway': 'fake', - 'dhcp_server': 'fake', - 'broadcast': 'fake', - 'mac': 'fake', - 'vif_uuid': 'fake', - 'rxtx_cap': 'fake', - 'dns': [], - 'ips': [{'ip': '10.0.0.1'}], - 'should_create_bridge': False, - 'should_create_vlan': False} - - return [['ignore', info]] - - -def fake_instance_get(context, instance_id): - return { - "id": 1, - "name": 'fake', - "user_id": 'fakeuser', - "project_id": '123'} - - -class StubExtensionManager(object): - def register(self, *args): - pass - - -class FloatingIpTest(test.TestCase): - address = "10.10.10.10" - - def _create_floating_ip(self): - """Create a floating ip object.""" - host = "fake_host" - return db.floating_ip_create(self.context, - {'address': self.address, - 'host': host}) - - def _delete_floating_ip(self): - db.floating_ip_destroy(self.context, self.address) - - def setUp(self): - super(FloatingIpTest, self).setUp() - self.stubs.Set(network.api.API, "get_floating_ip", - network_api_get_floating_ip) - self.stubs.Set(network.api.API, "get_floating_ip_by_address", - network_api_get_floating_ip_by_address) - self.stubs.Set(network.api.API, "get_floating_ips_by_project", - network_api_get_floating_ips_by_project) - self.stubs.Set(network.api.API, "release_floating_ip", - network_api_release) - self.stubs.Set(network.api.API, "disassociate_floating_ip", - network_api_disassociate) - self.stubs.Set(network.api.API, "get_instance_nw_info", - network_get_instance_nw_info) - self.stubs.Set(db, 'instance_get', - fake_instance_get) - - self.context = context.get_admin_context() - self._create_floating_ip() - - self.controller = floating_ips.FloatingIPController() - self.manager = floating_ips.Floating_ips(StubExtensionManager()) - - def tearDown(self): - self._delete_floating_ip() - super(FloatingIpTest, self).tearDown() - - def test_translate_floating_ip_view(self): - floating_ip_address = self._create_floating_ip() - floating_ip = db.floating_ip_get_by_address(self.context, - floating_ip_address) - view = _translate_floating_ip_view(floating_ip) - self.assertTrue('floating_ip' in view) - self.assertTrue(view['floating_ip']['id']) - self.assertEqual(view['floating_ip']['ip'], self.address) - self.assertEqual(view['floating_ip']['fixed_ip'], None) - self.assertEqual(view['floating_ip']['instance_id'], None) - - def test_translate_floating_ip_view_dict(self): - floating_ip = {'id': 0, 'address': '10.0.0.10', 'fixed_ip': None} - view = _translate_floating_ip_view(floating_ip) - self.assertTrue('floating_ip' in view) - - def test_floating_ips_list(self): - req = fakes.HTTPRequest.blank('/v1.1/123/os-floating-ips') - res_dict = self.controller.index(req) - - response = {'floating_ips': [{'instance_id': 1, - 'ip': '10.10.10.10', - 'fixed_ip': '10.0.0.1', - 'id': 1}, - {'instance_id': None, - 'ip': '10.10.10.11', - 'fixed_ip': None, - 'id': 2}]} - self.assertEqual(res_dict, response) - - def test_floating_ip_show(self): - req = fakes.HTTPRequest.blank('/v1.1/123/os-floating-ips/1') - res_dict = self.controller.show(req, 1) - - self.assertEqual(res_dict['floating_ip']['id'], 1) - self.assertEqual(res_dict['floating_ip']['ip'], '10.10.10.10') - self.assertEqual(res_dict['floating_ip']['instance_id'], None) - - def test_show_associated_floating_ip(self): - def get_floating_ip(self, context, id): - return {'id': 1, 'address': '10.10.10.10', - 'fixed_ip': {'address': '10.0.0.1', 'instance_id': 1}} - self.stubs.Set(network.api.API, "get_floating_ip", get_floating_ip) - - req = fakes.HTTPRequest.blank('/v1.1/123/os-floating-ips/1') - res_dict = self.controller.show(req, 1) - - self.assertEqual(res_dict['floating_ip']['id'], 1) - self.assertEqual(res_dict['floating_ip']['ip'], '10.10.10.10') - self.assertEqual(res_dict['floating_ip']['instance_id'], 1) - -# test floating ip allocate/release(deallocate) - def test_floating_ip_allocate_no_free_ips(self): - def fake_call(*args, **kwargs): - raise(rpc.RemoteError('NoMoreFloatingIps', '', '')) - - self.stubs.Set(rpc, "call", fake_call) - - req = fakes.HTTPRequest.blank('/v1.1/123/os-floating-ips') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req) - - def test_floating_ip_allocate(self): - def fake1(*args, **kwargs): - pass - - def fake2(*args, **kwargs): - return {'id': 1, 'address': '10.10.10.10'} - - self.stubs.Set(network.api.API, "allocate_floating_ip", - fake1) - self.stubs.Set(network.api.API, "get_floating_ip_by_address", - fake2) - - req = fakes.HTTPRequest.blank('/v1.1/123/os-floating-ips') - res_dict = self.controller.create(req) - - ip = res_dict['floating_ip'] - - expected = { - "id": 1, - "instance_id": None, - "ip": "10.10.10.10", - "fixed_ip": None} - self.assertEqual(ip, expected) - - def test_floating_ip_release(self): - req = fakes.HTTPRequest.blank('/v1.1/123/os-floating-ips/1') - self.controller.delete(req, 1) - -# test floating ip add/remove -> associate/disassociate - - def test_floating_ip_associate(self): - body = dict(addFloatingIp=dict(address=self.address)) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/test_inst/action') - self.manager._add_floating_ip(body, req, 'test_inst') - - def test_floating_ip_disassociate(self): - body = dict(removeFloatingIp=dict(address='10.10.10.10')) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/test_inst/action') - self.manager._remove_floating_ip(body, req, 'test_inst') - -# these are a few bad param tests - - def test_bad_address_param_in_remove_floating_ip(self): - body = dict(removeFloatingIp=dict(badparam='11.0.0.1')) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/test_inst/action') - self.assertRaises(webob.exc.HTTPBadRequest, - self.manager._add_floating_ip, body, req, - 'test_inst') - - def test_missing_dict_param_in_remove_floating_ip(self): - body = dict(removeFloatingIp='11.0.0.1') - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/test_inst/action') - self.assertRaises(webob.exc.HTTPBadRequest, - self.manager._remove_floating_ip, body, req, - 'test_inst') - - def test_missing_dict_param_in_add_floating_ip(self): - body = dict(addFloatingIp='11.0.0.1') - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/test_inst/action') - self.assertRaises(webob.exc.HTTPBadRequest, - self.manager._add_floating_ip, body, req, - 'test_inst') diff --git a/nova/tests/api/openstack/contrib/test_keypairs.py b/nova/tests/api/openstack/contrib/test_keypairs.py deleted file mode 100644 index 92e401aac..000000000 --- a/nova/tests/api/openstack/contrib/test_keypairs.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright 2011 Eldar Nugaev -# 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 json -import webob - -from nova import context -from nova import db -from nova import test -from nova.api.openstack.contrib.keypairs import KeypairController -from nova.tests.api.openstack import fakes - - -def fake_keypair(name): - return {'public_key': 'FAKE_KEY', - 'fingerprint': 'FAKE_FINGERPRINT', - 'name': name} - - -def db_key_pair_get_all_by_user(self, user_id): - return [fake_keypair('FAKE')] - - -def db_key_pair_create(self, keypair): - pass - - -def db_key_pair_destroy(context, user_id, name): - if not (user_id and name): - raise Exception() - - -class KeypairsTest(test.TestCase): - - def setUp(self): - super(KeypairsTest, self).setUp() - self.controller = KeypairController() - fakes.stub_out_networking(self.stubs) - fakes.stub_out_rate_limiting(self.stubs) - self.stubs.Set(db, "key_pair_get_all_by_user", - db_key_pair_get_all_by_user) - self.stubs.Set(db, "key_pair_create", - db_key_pair_create) - self.stubs.Set(db, "key_pair_destroy", - db_key_pair_destroy) - self.context = context.get_admin_context() - - def test_keypair_list(self): - req = webob.Request.blank('/v1.1/123/os-keypairs') - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - res_dict = json.loads(res.body) - response = {'keypairs': [{'keypair': fake_keypair('FAKE')}]} - self.assertEqual(res_dict, response) - - def test_keypair_create(self): - body = {'keypair': {'name': 'create_test'}} - req = webob.Request.blank('/v1.1/123/os-keypairs') - req.method = 'POST' - req.body = json.dumps(body) - req.headers['Content-Type'] = 'application/json' - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - res_dict = json.loads(res.body) - self.assertTrue(len(res_dict['keypair']['fingerprint']) > 0) - self.assertTrue(len(res_dict['keypair']['private_key']) > 0) - - def test_keypair_import(self): - body = { - 'keypair': { - 'name': 'create_test', - 'public_key': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBYIznA' - 'x9D7118Q1VKGpXy2HDiKyUTM8XcUuhQpo0srqb9rboUp4' - 'a9NmCwpWpeElDLuva707GOUnfaBAvHBwsRXyxHJjRaI6Y' - 'Qj2oLJwqvaSaWUbyT1vtryRqy6J3TecN0WINY71f4uymi' - 'MZP0wby4bKBcYnac8KiCIlvkEl0ETjkOGUq8OyWRmn7lj' - 'j5SESEUdBP0JnuTFKddWTU/wD6wydeJaUhBTqOlHn0kX1' - 'GyqoNTE1UEhcM5ZRWgfUZfTjVyDF2kGj3vJLCJtJ8LoGc' - 'j7YaN4uPg1rBle+izwE/tLonRrds+cev8p6krSSrxWOwB' - 'bHkXa6OciiJDvkRzJXzf', - }, - } - - req = webob.Request.blank('/v1.1/123/os-keypairs') - req.method = 'POST' - req.body = json.dumps(body) - req.headers['Content-Type'] = 'application/json' - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - # FIXME(ja): sholud we check that public_key was sent to create? - res_dict = json.loads(res.body) - self.assertTrue(len(res_dict['keypair']['fingerprint']) > 0) - self.assertFalse('private_key' in res_dict['keypair']) - - def test_keypair_delete(self): - req = webob.Request.blank('/v1.1/123/os-keypairs/FAKE') - req.method = 'DELETE' - req.headers['Content-Type'] = 'application/json' - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 202) diff --git a/nova/tests/api/openstack/contrib/test_multinic_xs.py b/nova/tests/api/openstack/contrib/test_multinic_xs.py deleted file mode 100644 index 90999a384..000000000 --- a/nova/tests/api/openstack/contrib/test_multinic_xs.py +++ /dev/null @@ -1,113 +0,0 @@ -# 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. - -import json -import stubout -import webob - -from nova import compute -from nova import context -from nova import test -from nova.tests.api.openstack import fakes - - -UUID = '70f6db34-de8d-4fbd-aafb-4065bdfa6114' -last_add_fixed_ip = (None, None) -last_remove_fixed_ip = (None, None) - - -def compute_api_add_fixed_ip(self, context, instance, network_id): - global last_add_fixed_ip - - last_add_fixed_ip = (instance['uuid'], network_id) - - -def compute_api_remove_fixed_ip(self, context, instance, address): - global last_remove_fixed_ip - - last_remove_fixed_ip = (instance['uuid'], address) - - -def compute_api_get(self, context, instance_id): - return {'id': 1, 'uuid': instance_id} - - -class FixedIpTest(test.TestCase): - def setUp(self): - super(FixedIpTest, self).setUp() - fakes.stub_out_networking(self.stubs) - fakes.stub_out_rate_limiting(self.stubs) - self.stubs.Set(compute.api.API, "add_fixed_ip", - compute_api_add_fixed_ip) - self.stubs.Set(compute.api.API, "remove_fixed_ip", - compute_api_remove_fixed_ip) - self.stubs.Set(compute.api.API, 'get', compute_api_get) - self.context = context.get_admin_context() - - def test_add_fixed_ip(self): - global last_add_fixed_ip - last_add_fixed_ip = (None, None) - - body = dict(addFixedIp=dict(networkId='test_net')) - req = webob.Request.blank('/v1.1/123/servers/%s/action' % UUID) - req.method = 'POST' - req.body = json.dumps(body) - req.headers['content-type'] = 'application/json' - - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 202) - self.assertEqual(last_add_fixed_ip, (UUID, 'test_net')) - - def test_add_fixed_ip_no_network(self): - global last_add_fixed_ip - last_add_fixed_ip = (None, None) - - body = dict(addFixedIp=dict()) - req = webob.Request.blank('/v1.1/123/servers/%s/action' % UUID) - req.method = 'POST' - req.body = json.dumps(body) - req.headers['content-type'] = 'application/json' - - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 422) - self.assertEqual(last_add_fixed_ip, (None, None)) - - def test_remove_fixed_ip(self): - global last_remove_fixed_ip - last_remove_fixed_ip = (None, None) - - body = dict(removeFixedIp=dict(address='10.10.10.1')) - req = webob.Request.blank('/v1.1/123/servers/%s/action' % UUID) - req.method = 'POST' - req.body = json.dumps(body) - req.headers['content-type'] = 'application/json' - - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 202) - self.assertEqual(last_remove_fixed_ip, (UUID, '10.10.10.1')) - - def test_remove_fixed_ip_no_address(self): - global last_remove_fixed_ip - last_remove_fixed_ip = (None, None) - - body = dict(removeFixedIp=dict()) - req = webob.Request.blank('/v1.1/123/servers/%s/action' % UUID) - req.method = 'POST' - req.body = json.dumps(body) - req.headers['content-type'] = 'application/json' - - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 422) - self.assertEqual(last_remove_fixed_ip, (None, None)) diff --git a/nova/tests/api/openstack/contrib/test_quotas.py b/nova/tests/api/openstack/contrib/test_quotas.py deleted file mode 100644 index 6374dfd93..000000000 --- a/nova/tests/api/openstack/contrib/test_quotas.py +++ /dev/null @@ -1,134 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import json -import webob - -from nova import context -from nova import test -from nova.tests.api.openstack import fakes - -from nova.api.openstack.contrib.quotas import QuotaSetsController - - -def quota_set(id): - return {'quota_set': {'id': id, 'metadata_items': 128, 'volumes': 10, - 'gigabytes': 1000, 'ram': 51200, 'floating_ips': 10, - 'instances': 10, 'injected_files': 5, 'cores': 20, - 'injected_file_content_bytes': 10240}} - - -def quota_set_list(): - return {'quota_set_list': [quota_set('1234'), quota_set('5678'), - quota_set('update_me')]} - - -class QuotaSetsTest(test.TestCase): - - def setUp(self): - super(QuotaSetsTest, self).setUp() - self.controller = QuotaSetsController() - self.user_id = 'fake' - self.project_id = 'fake' - self.user_context = context.RequestContext(self.user_id, - self.project_id) - self.admin_context = context.RequestContext(self.user_id, - self.project_id, - is_admin=True) - - def test_format_quota_set(self): - raw_quota_set = { - 'instances': 10, - 'cores': 20, - 'ram': 51200, - 'volumes': 10, - 'floating_ips': 10, - 'metadata_items': 128, - 'gigabytes': 1000, - 'injected_files': 5, - 'injected_file_content_bytes': 10240} - - quota_set = QuotaSetsController()._format_quota_set('1234', - raw_quota_set) - qs = quota_set['quota_set'] - - self.assertEqual(qs['id'], '1234') - self.assertEqual(qs['instances'], 10) - self.assertEqual(qs['cores'], 20) - self.assertEqual(qs['ram'], 51200) - self.assertEqual(qs['volumes'], 10) - self.assertEqual(qs['gigabytes'], 1000) - self.assertEqual(qs['floating_ips'], 10) - self.assertEqual(qs['metadata_items'], 128) - self.assertEqual(qs['injected_files'], 5) - self.assertEqual(qs['injected_file_content_bytes'], 10240) - - def test_quotas_defaults(self): - uri = '/v1.1/fake_tenant/os-quota-sets/fake_tenant/defaults' - - req = fakes.HTTPRequest.blank(uri) - res_dict = self.controller.defaults(req, 'fake_tenant') - - expected = {'quota_set': { - 'id': 'fake_tenant', - 'instances': 10, - 'cores': 20, - 'ram': 51200, - 'volumes': 10, - 'gigabytes': 1000, - 'floating_ips': 10, - 'metadata_items': 128, - 'injected_files': 5, - 'injected_file_content_bytes': 10240}} - - self.assertEqual(res_dict, expected) - - def test_quotas_show_as_admin(self): - req = fakes.HTTPRequest.blank('/v1.1/1234/os-quota-sets/1234', - use_admin_context=True) - res_dict = self.controller.show(req, 1234) - - self.assertEqual(res_dict, quota_set('1234')) - - def test_quotas_show_as_unauthorized_user(self): - req = fakes.HTTPRequest.blank('/v1.1/1234/os-quota-sets/1234') - self.assertRaises(webob.exc.HTTPForbidden, self.controller.show, - req, 1234) - - def test_quotas_update_as_admin(self): - body = {'quota_set': {'instances': 50, 'cores': 50, - 'ram': 51200, 'volumes': 10, - 'gigabytes': 1000, 'floating_ips': 10, - 'metadata_items': 128, 'injected_files': 5, - 'injected_file_content_bytes': 10240}} - - req = fakes.HTTPRequest.blank('/v1.1/1234/os-quota-sets/update_me', - use_admin_context=True) - res_dict = self.controller.update(req, 'update_me', body) - - self.assertEqual(res_dict, body) - - def test_quotas_update_as_user(self): - body = {'quota_set': {'instances': 50, 'cores': 50, - 'ram': 51200, 'volumes': 10, - 'gigabytes': 1000, 'floating_ips': 10, - 'metadata_items': 128, 'injected_files': 5, - 'injected_file_content_bytes': 10240}} - - req = fakes.HTTPRequest.blank('/v1.1/1234/os-quota-sets/update_me') - self.assertRaises(webob.exc.HTTPForbidden, self.controller.update, - req, 'update_me', body) diff --git a/nova/tests/api/openstack/contrib/test_rescue.py b/nova/tests/api/openstack/contrib/test_rescue.py deleted file mode 100644 index f5b69865c..000000000 --- a/nova/tests/api/openstack/contrib/test_rescue.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright 2011 OpenStack LLC. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import json -import webob - -from nova import compute -from nova import flags -from nova import test -from nova.tests.api.openstack import fakes - -FLAGS = flags.FLAGS - - -def rescue(self, context, instance, rescue_password=None): - pass - - -def unrescue(self, context, instance): - pass - - -class RescueTest(test.TestCase): - def setUp(self): - super(RescueTest, self).setUp() - - def fake_compute_get(*args, **kwargs): - uuid = '70f6db34-de8d-4fbd-aafb-4065bdfa6114' - return {'id': 1, 'uuid': uuid} - - self.stubs.Set(compute.api.API, "get", fake_compute_get) - self.stubs.Set(compute.api.API, "rescue", rescue) - self.stubs.Set(compute.api.API, "unrescue", unrescue) - - def test_rescue_with_preset_password(self): - body = {"rescue": {"adminPass": "AABBCC112233"}} - req = webob.Request.blank('/v1.1/123/servers/test_inst/action') - req.method = "POST" - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 200) - resp_json = json.loads(resp.body) - self.assertEqual("AABBCC112233", resp_json['adminPass']) - - def test_rescue_generates_password(self): - body = dict(rescue=None) - req = webob.Request.blank('/v1.1/123/servers/test_inst/action') - req.method = "POST" - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 200) - resp_json = json.loads(resp.body) - self.assertEqual(FLAGS.password_length, len(resp_json['adminPass'])) - - def test_unrescue(self): - body = dict(unrescue=None) - req = webob.Request.blank('/v1.1/123/servers/test_inst/action') - req.method = "POST" - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 202) diff --git a/nova/tests/api/openstack/contrib/test_security_groups.py b/nova/tests/api/openstack/contrib/test_security_groups.py deleted file mode 100644 index 1e7a439b0..000000000 --- a/nova/tests/api/openstack/contrib/test_security_groups.py +++ /dev/null @@ -1,848 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import json -import mox -import nova -import unittest -import webob -from xml.dom import minidom - -from nova import exception -from nova import test -from nova.api.openstack.contrib import security_groups -from nova.tests.api.openstack import fakes - - -FAKE_UUID = 'a47ae74e-ab08-447f-8eee-ffd43fc46c16' - - -class AttrDict(dict): - def __getattr__(self, k): - return self[k] - - -def security_group_template(**kwargs): - sg = kwargs.copy() - sg.setdefault('tenant_id', '123') - sg.setdefault('name', 'test') - sg.setdefault('description', 'test-description') - return sg - - -def security_group_db(security_group, id=None): - attrs = security_group.copy() - if 'tenant_id' in attrs: - attrs['project_id'] = attrs.pop('tenant_id') - if id is not None: - attrs['id'] = id - attrs.setdefault('rules', []) - attrs.setdefault('instances', []) - return AttrDict(attrs) - - -def security_group_rule_template(**kwargs): - rule = kwargs.copy() - rule.setdefault('ip_protocol', 'tcp') - rule.setdefault('from_port', 22) - rule.setdefault('to_port', 22) - rule.setdefault('parent_group_id', 2) - return rule - - -def security_group_rule_db(rule, id=None): - attrs = rule.copy() - if 'ip_protocol' in attrs: - attrs['protocol'] = attrs.pop('ip_protocol') - return AttrDict(attrs) - - -def return_server(context, server_id): - return {'id': int(server_id), - 'power_state': 0x01, - 'host': "localhost", - 'uuid': FAKE_UUID, - 'name': 'asdf'} - - -def return_server_by_uuid(context, server_uuid): - return {'id': 1, - 'power_state': 0x01, - 'host': "localhost", - 'uuid': server_uuid, - 'name': 'asdf'} - - -def return_non_running_server(context, server_id): - return {'id': server_id, 'power_state': 0x02, - 'host': "localhost", 'name': 'asdf'} - - -def return_security_group_by_name(context, project_id, group_name): - return {'id': 1, 'name': group_name, "instances": [{'id': 1}]} - - -def return_security_group_without_instances(context, project_id, group_name): - return {'id': 1, 'name': group_name} - - -def return_server_nonexistent(context, server_id): - raise exception.InstanceNotFound(instance_id=server_id) - - -class StubExtensionManager(object): - def register(self, *args): - pass - - -class TestSecurityGroups(test.TestCase): - def setUp(self): - super(TestSecurityGroups, self).setUp() - - self.controller = security_groups.SecurityGroupController() - self.manager = security_groups.Security_groups(StubExtensionManager()) - - def tearDown(self): - super(TestSecurityGroups, self).tearDown() - - def test_create_security_group(self): - sg = security_group_template() - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') - res_dict = self.controller.create(req, {'security_group': sg}) - self.assertEqual(res_dict['security_group']['name'], 'test') - self.assertEqual(res_dict['security_group']['description'], - 'test-description') - - def test_create_security_group_with_no_name(self): - sg = security_group_template() - del sg['name'] - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') - self.assertRaises(webob.exc.HTTPUnprocessableEntity, - self.controller.create, req, sg) - - def test_create_security_group_with_no_description(self): - sg = security_group_template() - del sg['description'] - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group': sg}) - - def test_create_security_group_with_blank_name(self): - sg = security_group_template(name='') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group': sg}) - - def test_create_security_group_with_whitespace_name(self): - sg = security_group_template(name=' ') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group': sg}) - - def test_create_security_group_with_blank_description(self): - sg = security_group_template(description='') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group': sg}) - - def test_create_security_group_with_whitespace_description(self): - sg = security_group_template(description=' ') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group': sg}) - - def test_create_security_group_with_duplicate_name(self): - sg = security_group_template() - - # FIXME: Stub out _get instead of creating twice - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') - self.controller.create(req, {'security_group': sg}) - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group': sg}) - - def test_create_security_group_with_no_body(self): - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') - self.assertRaises(webob.exc.HTTPUnprocessableEntity, - self.controller.create, req, None) - - def test_create_security_group_with_no_security_group(self): - body = {'no-securityGroup': None} - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') - self.assertRaises(webob.exc.HTTPUnprocessableEntity, - self.controller.create, req, body) - - def test_create_security_group_above_255_characters_name(self): - sg = security_group_template(name='1234567890' * 26) - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group': sg}) - - def test_create_security_group_above_255_characters_description(self): - sg = security_group_template(description='1234567890' * 26) - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group': sg}) - - def test_create_security_group_non_string_name(self): - sg = security_group_template(name=12) - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group': sg}) - - def test_create_security_group_non_string_description(self): - sg = security_group_template(description=12) - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group': sg}) - - def test_get_security_group_list(self): - groups = [] - for i, name in enumerate(['default', 'test']): - sg = security_group_template(id=i + 1, - name=name, - description=name + '-desc', - rules=[]) - groups.append(sg) - expected = {'security_groups': groups} - - def return_security_groups(context, project_id): - return [security_group_db(sg) for sg in groups] - - self.stubs.Set(nova.db, 'security_group_get_by_project', - return_security_groups) - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') - res_dict = self.controller.index(req) - - self.assertEquals(res_dict, expected) - - def test_get_security_group_by_id(self): - sg = security_group_template(id=2, rules=[]) - - def return_security_group(context, group_id): - self.assertEquals(sg['id'], group_id) - return security_group_db(sg) - - self.stubs.Set(nova.db, 'security_group_get', - return_security_group) - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups/2') - res_dict = self.controller.show(req, '2') - - expected = {'security_group': sg} - self.assertEquals(res_dict, expected) - - def test_get_security_group_by_invalid_id(self): - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups/invalid') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete, - req, 'invalid') - - def test_get_security_group_by_non_existing_id(self): - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups/111111111') - self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, - req, '111111111') - - def test_delete_security_group_by_id(self): - sg = security_group_template(id=1, rules=[]) - - self.called = False - - def security_group_destroy(context, id): - self.called = True - - def return_security_group(context, group_id): - self.assertEquals(sg['id'], group_id) - return security_group_db(sg) - - self.stubs.Set(nova.db, 'security_group_destroy', - security_group_destroy) - self.stubs.Set(nova.db, 'security_group_get', - return_security_group) - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups/1') - self.controller.delete(req, '1') - - self.assertTrue(self.called) - - def test_delete_security_group_by_invalid_id(self): - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups/invalid') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete, - req, 'invalid') - - def test_delete_security_group_by_non_existing_id(self): - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups/11111111') - self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, - req, '11111111') - - def test_associate_by_non_existing_security_group_name(self): - body = dict(addSecurityGroup=dict(name='non-existing')) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') - self.assertRaises(webob.exc.HTTPNotFound, - self.manager._addSecurityGroup, body, req, '1') - - def test_associate_by_invalid_server_id(self): - body = dict(addSecurityGroup=dict(name='test')) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/invalid/action') - self.assertRaises(webob.exc.HTTPNotFound, - self.manager._addSecurityGroup, body, req, 'invalid') - - def test_associate_without_body(self): - self.stubs.Set(nova.db, 'instance_get', return_server) - body = dict(addSecurityGroup=None) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') - self.assertRaises(webob.exc.HTTPBadRequest, - self.manager._addSecurityGroup, body, req, '1') - - def test_associate_no_security_group_name(self): - self.stubs.Set(nova.db, 'instance_get', return_server) - body = dict(addSecurityGroup=dict()) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') - self.assertRaises(webob.exc.HTTPBadRequest, - self.manager._addSecurityGroup, body, req, '1') - - def test_associate_security_group_name_with_whitespaces(self): - self.stubs.Set(nova.db, 'instance_get', return_server) - body = dict(addSecurityGroup=dict(name=" ")) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') - self.assertRaises(webob.exc.HTTPBadRequest, - self.manager._addSecurityGroup, body, req, '1') - - def test_associate_non_existing_instance(self): - self.stubs.Set(nova.db, 'instance_get', return_server_nonexistent) - self.stubs.Set(nova.db, 'instance_get_by_uuid', - return_server_nonexistent) - body = dict(addSecurityGroup=dict(name="test")) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') - self.assertRaises(webob.exc.HTTPNotFound, - self.manager._addSecurityGroup, body, req, '1') - - def test_associate_non_running_instance(self): - self.stubs.Set(nova.db, 'instance_get', return_non_running_server) - self.stubs.Set(nova.db, 'instance_get_by_uuid', - return_non_running_server) - self.stubs.Set(nova.db, 'security_group_get_by_name', - return_security_group_without_instances) - body = dict(addSecurityGroup=dict(name="test")) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') - self.assertRaises(webob.exc.HTTPBadRequest, - self.manager._addSecurityGroup, body, req, '1') - - def test_associate_already_associated_security_group_to_instance(self): - self.stubs.Set(nova.db, 'instance_get', return_server) - self.stubs.Set(nova.db, 'instance_get_by_uuid', - return_server_by_uuid) - self.stubs.Set(nova.db, 'security_group_get_by_name', - return_security_group_by_name) - body = dict(addSecurityGroup=dict(name="test")) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') - self.assertRaises(webob.exc.HTTPBadRequest, - self.manager._addSecurityGroup, body, req, '1') - - def test_associate(self): - self.stubs.Set(nova.db, 'instance_get', return_server) - self.stubs.Set(nova.db, 'instance_get_by_uuid', - return_server_by_uuid) - self.mox.StubOutWithMock(nova.db, 'instance_add_security_group') - nova.db.instance_add_security_group(mox.IgnoreArg(), - mox.IgnoreArg(), - mox.IgnoreArg()) - self.stubs.Set(nova.db, 'security_group_get_by_name', - return_security_group_without_instances) - self.mox.ReplayAll() - - body = dict(addSecurityGroup=dict(name="test")) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') - self.manager._addSecurityGroup(body, req, '1') - - def test_disassociate_by_non_existing_security_group_name(self): - body = dict(removeSecurityGroup=dict(name='non-existing')) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') - self.assertRaises(webob.exc.HTTPNotFound, - self.manager._removeSecurityGroup, body, req, '1') - - def test_disassociate_by_invalid_server_id(self): - self.stubs.Set(nova.db, 'security_group_get_by_name', - return_security_group_by_name) - body = dict(removeSecurityGroup=dict(name='test')) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/invalid/action') - self.assertRaises(webob.exc.HTTPNotFound, - self.manager._removeSecurityGroup, body, req, - 'invalid') - - def test_disassociate_without_body(self): - self.stubs.Set(nova.db, 'instance_get', return_server) - body = dict(removeSecurityGroup=None) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') - self.assertRaises(webob.exc.HTTPBadRequest, - self.manager._removeSecurityGroup, body, req, '1') - - def test_disassociate_no_security_group_name(self): - self.stubs.Set(nova.db, 'instance_get', return_server) - body = dict(removeSecurityGroup=dict()) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') - self.assertRaises(webob.exc.HTTPBadRequest, - self.manager._removeSecurityGroup, body, req, '1') - - def test_disassociate_security_group_name_with_whitespaces(self): - self.stubs.Set(nova.db, 'instance_get', return_server) - body = dict(removeSecurityGroup=dict(name=" ")) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') - self.assertRaises(webob.exc.HTTPBadRequest, - self.manager._removeSecurityGroup, body, req, '1') - - def test_disassociate_non_existing_instance(self): - self.stubs.Set(nova.db, 'instance_get', return_server_nonexistent) - self.stubs.Set(nova.db, 'security_group_get_by_name', - return_security_group_by_name) - body = dict(removeSecurityGroup=dict(name="test")) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') - self.assertRaises(webob.exc.HTTPNotFound, - self.manager._removeSecurityGroup, body, req, '1') - - def test_disassociate_non_running_instance(self): - self.stubs.Set(nova.db, 'instance_get', return_non_running_server) - self.stubs.Set(nova.db, 'instance_get_by_uuid', - return_non_running_server) - self.stubs.Set(nova.db, 'security_group_get_by_name', - return_security_group_by_name) - body = dict(removeSecurityGroup=dict(name="test")) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') - self.assertRaises(webob.exc.HTTPBadRequest, - self.manager._removeSecurityGroup, body, req, '1') - - def test_disassociate_already_associated_security_group_to_instance(self): - self.stubs.Set(nova.db, 'instance_get', return_server) - self.stubs.Set(nova.db, 'instance_get_by_uuid', - return_server_by_uuid) - self.stubs.Set(nova.db, 'security_group_get_by_name', - return_security_group_without_instances) - body = dict(removeSecurityGroup=dict(name="test")) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') - self.assertRaises(webob.exc.HTTPBadRequest, - self.manager._removeSecurityGroup, body, req, '1') - - def test_disassociate(self): - self.stubs.Set(nova.db, 'instance_get', return_server) - self.stubs.Set(nova.db, 'instance_get_by_uuid', - return_server_by_uuid) - self.mox.StubOutWithMock(nova.db, 'instance_remove_security_group') - nova.db.instance_remove_security_group(mox.IgnoreArg(), - mox.IgnoreArg(), - mox.IgnoreArg()) - self.stubs.Set(nova.db, 'security_group_get_by_name', - return_security_group_by_name) - self.mox.ReplayAll() - - body = dict(removeSecurityGroup=dict(name="test")) - - req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') - self.manager._removeSecurityGroup(body, req, '1') - - -class TestSecurityGroupRules(test.TestCase): - def setUp(self): - super(TestSecurityGroupRules, self).setUp() - - controller = security_groups.SecurityGroupController() - - sg1 = security_group_template(id=1) - sg2 = security_group_template(id=2, - name='authorize_revoke', - description='authorize-revoke testing') - db1 = security_group_db(sg1) - db2 = security_group_db(sg2) - - def return_security_group(context, group_id): - if group_id == db1['id']: - return db1 - if group_id == db2['id']: - return db2 - raise exception.NotFound() - - self.stubs.Set(nova.db, 'security_group_get', - return_security_group) - - self.parent_security_group = db2 - - self.controller = security_groups.SecurityGroupRulesController() - - def tearDown(self): - super(TestSecurityGroupRules, self).tearDown() - - def test_create_by_cidr(self): - rule = security_group_rule_template(cidr='10.2.3.124/24') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - res_dict = self.controller.create(req, {'security_group_rule': rule}) - - security_group_rule = res_dict['security_group_rule'] - self.assertNotEquals(security_group_rule['id'], 0) - self.assertEquals(security_group_rule['parent_group_id'], 2) - self.assertEquals(security_group_rule['ip_range']['cidr'], - "10.2.3.124/24") - - def test_create_by_group_id(self): - rule = security_group_rule_template(group_id='1') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - res_dict = self.controller.create(req, {'security_group_rule': rule}) - - security_group_rule = res_dict['security_group_rule'] - self.assertNotEquals(security_group_rule['id'], 0) - self.assertEquals(security_group_rule['parent_group_id'], 2) - - def test_create_by_invalid_cidr_json(self): - rules = { - "security_group_rule": { - "ip_protocol": "tcp", - "from_port": "22", - "to_port": "22", - "parent_group_id": 2, - "cidr": "10.2.3.124/2433"}} - rule = security_group_rule_template( - ip_protocol="tcp", - from_port=22, - to_port=22, - parent_group_id=2, - cidr="10.2.3.124/2433") - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_by_invalid_tcp_port_json(self): - rule = security_group_rule_template( - ip_protocol="tcp", - from_port=75534, - to_port=22, - parent_group_id=2, - cidr="10.2.3.124/24") - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_by_invalid_icmp_port_json(self): - rule = security_group_rule_template( - ip_protocol="icmp", - from_port=1, - to_port=256, - parent_group_id=2, - cidr="10.2.3.124/24") - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_add_existing_rules(self): - rule = security_group_rule_template(cidr='10.0.0.0/24') - - self.parent_security_group['rules'] = [security_group_rule_db(rule)] - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_with_no_body(self): - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPUnprocessableEntity, - self.controller.create, req, None) - - def test_create_with_no_security_group_rule_in_body(self): - rules = {'test': 'test'} - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPUnprocessableEntity, - self.controller.create, req, rules) - - def test_create_with_invalid_parent_group_id(self): - rule = security_group_rule_template(parent_group_id='invalid') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_with_non_existing_parent_group_id(self): - rule = security_group_rule_template(group_id='invalid', - parent_group_id='1111111111111') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPNotFound, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_with_invalid_protocol(self): - rule = security_group_rule_template(ip_protocol='invalid-protocol', - cidr='10.2.2.0/24') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_with_no_protocol(self): - rule = security_group_rule_template(cidr='10.2.2.0/24') - del rule['ip_protocol'] - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_with_invalid_from_port(self): - rule = security_group_rule_template(from_port='666666', - cidr='10.2.2.0/24') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_with_invalid_to_port(self): - rule = security_group_rule_template(to_port='666666', - cidr='10.2.2.0/24') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_with_non_numerical_from_port(self): - rule = security_group_rule_template(from_port='invalid', - cidr='10.2.2.0/24') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_with_non_numerical_to_port(self): - rule = security_group_rule_template(to_port='invalid', - cidr='10.2.2.0/24') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_with_no_from_port(self): - rule = security_group_rule_template(cidr='10.2.2.0/24') - del rule['from_port'] - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_with_no_to_port(self): - rule = security_group_rule_template(cidr='10.2.2.0/24') - del rule['to_port'] - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_with_invalid_cidr(self): - rule = security_group_rule_template(cidr='10.2.2222.0/24') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_with_no_cidr_group(self): - rule = security_group_rule_template() - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - res_dict = self.controller.create(req, {'security_group_rule': rule}) - - security_group_rule = res_dict['security_group_rule'] - self.assertNotEquals(security_group_rule['id'], 0) - self.assertEquals(security_group_rule['parent_group_id'], - self.parent_security_group['id']) - self.assertEquals(security_group_rule['ip_range']['cidr'], - "0.0.0.0/0") - - def test_create_with_invalid_group_id(self): - rule = security_group_rule_template(group_id='invalid') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_with_empty_group_id(self): - rule = security_group_rule_template(group_id='') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_with_nonexist_group_id(self): - rule = security_group_rule_template(group_id='222222') - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_create_rule_with_same_group_parent_id(self): - rule = security_group_rule_template(group_id=2) - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, {'security_group_rule': rule}) - - def test_delete(self): - rule = security_group_rule_template(id=10) - - def security_group_rule_get(context, id): - return security_group_rule_db(rule) - - def security_group_rule_destroy(context, id): - pass - - self.stubs.Set(nova.db, 'security_group_rule_get', - security_group_rule_get) - self.stubs.Set(nova.db, 'security_group_rule_destroy', - security_group_rule_destroy) - - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules/10') - self.controller.delete(req, '10') - - def test_delete_invalid_rule_id(self): - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules' + - '/invalid') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete, - req, 'invalid') - - def test_delete_non_existing_rule_id(self): - req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules' + - '/22222222222222') - self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, - req, '22222222222222') - - -class TestSecurityGroupRulesXMLDeserializer(unittest.TestCase): - - def setUp(self): - self.deserializer = security_groups.SecurityGroupRulesXMLDeserializer() - - def test_create_request(self): - serial_request = """ - - 12 - 22 - 22 - - tcp - 10.0.0.0/24 -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = { - "security_group_rule": { - "parent_group_id": "12", - "from_port": "22", - "to_port": "22", - "ip_protocol": "tcp", - "group_id": "", - "cidr": "10.0.0.0/24", - }, - } - self.assertEquals(request['body'], expected) - - def test_create_no_protocol_request(self): - serial_request = """ - - 12 - 22 - 22 - - 10.0.0.0/24 -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = { - "security_group_rule": { - "parent_group_id": "12", - "from_port": "22", - "to_port": "22", - "group_id": "", - "cidr": "10.0.0.0/24", - }, - } - self.assertEquals(request['body'], expected) - - -class TestSecurityGroupXMLDeserializer(unittest.TestCase): - - def setUp(self): - self.deserializer = security_groups.SecurityGroupXMLDeserializer() - - def test_create_request(self): - serial_request = """ - - test -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = { - "security_group": { - "name": "test", - "description": "test", - }, - } - self.assertEquals(request['body'], expected) - - def test_create_no_description_request(self): - serial_request = """ - -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = { - "security_group": { - "name": "test", - }, - } - self.assertEquals(request['body'], expected) - - def test_create_no_name_request(self): - serial_request = """ - -test -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = { - "security_group": { - "description": "test", - }, - } - self.assertEquals(request['body'], expected) diff --git a/nova/tests/api/openstack/contrib/test_simple_tenant_usage.py b/nova/tests/api/openstack/contrib/test_simple_tenant_usage.py deleted file mode 100644 index 2430b9d51..000000000 --- a/nova/tests/api/openstack/contrib/test_simple_tenant_usage.py +++ /dev/null @@ -1,172 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import datetime -import json -import webob - -from nova import context -from nova import flags -from nova import test -from nova.compute import api -from nova.tests.api.openstack import fakes - - -FLAGS = flags.FLAGS - -SERVERS = 5 -TENANTS = 2 -HOURS = 24 -LOCAL_GB = 10 -MEMORY_MB = 1024 -VCPUS = 2 -STOP = datetime.datetime.utcnow() -START = STOP - datetime.timedelta(hours=HOURS) - - -def fake_instance_type_get(self, context, instance_type_id): - return {'id': 1, - 'vcpus': VCPUS, - 'local_gb': LOCAL_GB, - 'memory_mb': MEMORY_MB, - 'name': - 'fakeflavor'} - - -def get_fake_db_instance(start, end, instance_id, tenant_id): - return {'id': instance_id, - 'image_ref': '1', - 'project_id': tenant_id, - 'user_id': 'fakeuser', - 'display_name': 'name', - 'state_description': 'state', - 'instance_type_id': 1, - 'launched_at': start, - 'terminated_at': end} - - -def fake_instance_get_active_by_window(self, context, begin, end, project_id): - return [get_fake_db_instance(START, - STOP, - x, - "faketenant_%s" % (x / SERVERS)) - for x in xrange(TENANTS * SERVERS)] - - -class SimpleTenantUsageTest(test.TestCase): - def setUp(self): - super(SimpleTenantUsageTest, self).setUp() - self.stubs.Set(api.API, "get_instance_type", - fake_instance_type_get) - self.stubs.Set(api.API, "get_active_by_window", - fake_instance_get_active_by_window) - self.admin_context = context.RequestContext('fakeadmin_0', - 'faketenant_0', - is_admin=True) - self.user_context = context.RequestContext('fakeadmin_0', - 'faketenant_0', - is_admin=False) - self.alt_user_context = context.RequestContext('fakeadmin_0', - 'faketenant_1', - is_admin=False) - FLAGS.allow_admin_api = True - - def test_verify_index(self): - req = webob.Request.blank( - '/v1.1/123/os-simple-tenant-usage?start=%s&end=%s' % - (START.isoformat(), STOP.isoformat())) - req.method = "GET" - req.headers["content-type"] = "application/json" - - res = req.get_response(fakes.wsgi_app( - fake_auth_context=self.admin_context)) - - self.assertEqual(res.status_int, 200) - res_dict = json.loads(res.body) - usages = res_dict['tenant_usages'] - from nova import log as logging - logging.warn(usages) - for i in xrange(TENANTS): - self.assertEqual(int(usages[i]['total_hours']), - SERVERS * HOURS) - self.assertEqual(int(usages[i]['total_local_gb_usage']), - SERVERS * LOCAL_GB * HOURS) - self.assertEqual(int(usages[i]['total_memory_mb_usage']), - SERVERS * MEMORY_MB * HOURS) - self.assertEqual(int(usages[i]['total_vcpus_usage']), - SERVERS * VCPUS * HOURS) - self.assertFalse(usages[i].get('server_usages')) - - def test_verify_detailed_index(self): - req = webob.Request.blank( - '/v1.1/123/os-simple-tenant-usage?' - 'detailed=1&start=%s&end=%s' % - (START.isoformat(), STOP.isoformat())) - req.method = "GET" - req.headers["content-type"] = "application/json" - - res = req.get_response(fakes.wsgi_app( - fake_auth_context=self.admin_context)) - self.assertEqual(res.status_int, 200) - res_dict = json.loads(res.body) - usages = res_dict['tenant_usages'] - for i in xrange(TENANTS): - servers = usages[i]['server_usages'] - for j in xrange(SERVERS): - self.assertEqual(int(servers[j]['hours']), HOURS) - - def test_verify_index_fails_for_nonadmin(self): - req = webob.Request.blank( - '/v1.1/123/os-simple-tenant-usage?' - 'detailed=1&start=%s&end=%s' % - (START.isoformat(), STOP.isoformat())) - req.method = "GET" - req.headers["content-type"] = "application/json" - - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 403) - - def test_verify_show(self): - req = webob.Request.blank( - '/v1.1/faketenant_0/os-simple-tenant-usage/' - 'faketenant_0?start=%s&end=%s' % - (START.isoformat(), STOP.isoformat())) - req.method = "GET" - req.headers["content-type"] = "application/json" - - res = req.get_response(fakes.wsgi_app( - fake_auth_context=self.user_context)) - self.assertEqual(res.status_int, 200) - res_dict = json.loads(res.body) - - usage = res_dict['tenant_usage'] - servers = usage['server_usages'] - self.assertEqual(len(usage['server_usages']), SERVERS) - for j in xrange(SERVERS): - self.assertEqual(int(servers[j]['hours']), HOURS) - - def test_verify_show_cant_view_other_tenant(self): - req = webob.Request.blank( - '/v1.1/faketenant_1/os-simple-tenant-usage/' - 'faketenant_0?start=%s&end=%s' % - (START.isoformat(), STOP.isoformat())) - req.method = "GET" - req.headers["content-type"] = "application/json" - - res = req.get_response(fakes.wsgi_app( - fake_auth_context=self.alt_user_context)) - self.assertEqual(res.status_int, 403) diff --git a/nova/tests/api/openstack/contrib/test_virtual_interfaces.py b/nova/tests/api/openstack/contrib/test_virtual_interfaces.py deleted file mode 100644 index 0e3b45c10..000000000 --- a/nova/tests/api/openstack/contrib/test_virtual_interfaces.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (C) 2011 Midokura KK -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import json -import webob - -from nova import test -from nova import network -from nova.tests.api.openstack import fakes -from nova.api.openstack.contrib.virtual_interfaces import \ - ServerVirtualInterfaceController - - -def get_vifs_by_instance(self, context, server_id): - return [{'uuid': '00000000-0000-0000-0000-00000000000000000', - 'address': '00-00-00-00-00-00'}, - {'uuid': '11111111-1111-1111-1111-11111111111111111', - 'address': '11-11-11-11-11-11'}] - - -class ServerVirtualInterfaceTest(test.TestCase): - - def setUp(self): - super(ServerVirtualInterfaceTest, self).setUp() - self.controller = ServerVirtualInterfaceController() - self.stubs.Set(network.api.API, "get_vifs_by_instance", - get_vifs_by_instance) - - def tearDown(self): - super(ServerVirtualInterfaceTest, self).tearDown() - - def test_get_virtual_interfaces_list(self): - url = '/v1.1/123/servers/abcd/os-virtual-interfaces' - req = webob.Request.blank(url) - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - res_dict = json.loads(res.body) - response = {'virtual_interfaces': [ - {'id': '00000000-0000-0000-0000-00000000000000000', - 'mac_address': '00-00-00-00-00-00'}, - {'id': '11111111-1111-1111-1111-11111111111111111', - 'mac_address': '11-11-11-11-11-11'}]} - self.assertEqual(res_dict, response) diff --git a/nova/tests/api/openstack/contrib/test_volume_types.py b/nova/tests/api/openstack/contrib/test_volume_types.py deleted file mode 100644 index ec1c44854..000000000 --- a/nova/tests/api/openstack/contrib/test_volume_types.py +++ /dev/null @@ -1,165 +0,0 @@ -# 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. - -import json -import stubout -import webob - -from nova import exception -from nova import context -from nova import test -from nova import log as logging -from nova.api.openstack.contrib import volumetypes -from nova.volume import volume_types -from nova.tests.api.openstack import fakes - -LOG = logging.getLogger('nova.tests.api.openstack.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 = volumetypes.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('/v1.1/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('/v1.1/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('/v1.1/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('/v1.1/123/os-volume-types/777') - self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, - req, '777') - - def test_volume_types_delete(self): - self.stubs.Set(volume_types, 'get_volume_type', - return_volume_types_get_volume_type) - self.stubs.Set(volume_types, 'destroy', - return_volume_types_destroy) - - req = fakes.HTTPRequest.blank('/v1.1/123/os-volume-types/1') - self.controller.delete(req, 1) - - def test_volume_types_delete_not_found(self): - self.stubs.Set(volume_types, 'get_volume_type', - return_volume_types_get_volume_type) - self.stubs.Set(volume_types, 'destroy', - return_volume_types_destroy) - - req = fakes.HTTPRequest.blank('/v1.1/123/os-volume-types/777') - self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, - req, '777') - - def test_create(self): - self.stubs.Set(volume_types, 'create', - return_volume_types_create) - self.stubs.Set(volume_types, 'get_volume_type_by_name', - return_volume_types_get_by_name) - - body = {"volume_type": {"name": "vol_type_1", - "extra_specs": {"key1": "value1"}}} - req = fakes.HTTPRequest.blank('/v1.1/123/os-volume-types') - res_dict = self.controller.create(req, body) - - self.assertEqual(1, len(res_dict)) - self.assertEqual('vol_type_1', res_dict['volume_type']['name']) - - def test_create_empty_body(self): - self.stubs.Set(volume_types, 'create', - return_volume_types_create) - self.stubs.Set(volume_types, 'get_volume_type_by_name', - return_volume_types_get_by_name) - - req = fakes.HTTPRequest.blank('/v1.1/123/os-volume-types') - self.assertRaises(webob.exc.HTTPUnprocessableEntity, - self.controller.create, req, '') diff --git a/nova/tests/api/openstack/contrib/test_volume_types_extra_specs.py b/nova/tests/api/openstack/contrib/test_volume_types_extra_specs.py deleted file mode 100644 index 796478838..000000000 --- a/nova/tests/api/openstack/contrib/test_volume_types_extra_specs.py +++ /dev/null @@ -1,169 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 2011 Zadara Storage Inc. -# Copyright (c) 2011 OpenStack LLC. -# Copyright 2011 University of Southern California -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import json -import stubout -import webob -import os.path - - -from nova import test -from nova.api import openstack -from nova.api.openstack import extensions -from nova.api.openstack.contrib import volumetypes -from nova.tests.api.openstack import fakes -import nova.wsgi - - -def return_create_volume_type_extra_specs(context, volume_type_id, - extra_specs): - return stub_volume_type_extra_specs() - - -def return_volume_type_extra_specs(context, volume_type_id): - return stub_volume_type_extra_specs() - - -def return_empty_volume_type_extra_specs(context, volume_type_id): - return {} - - -def delete_volume_type_extra_specs(context, volume_type_id, key): - pass - - -def stub_volume_type_extra_specs(): - specs = { - "key1": "value1", - "key2": "value2", - "key3": "value3", - "key4": "value4", - "key5": "value5"} - return specs - - -class VolumeTypesExtraSpecsTest(test.TestCase): - - def setUp(self): - super(VolumeTypesExtraSpecsTest, self).setUp() - fakes.stub_out_key_pair_funcs(self.stubs) - self.api_path = '/v1.1/123/os-volume-types/1/extra_specs' - self.controller = volumetypes.VolumeTypeExtraSpecsController() - - def test_index(self): - self.stubs.Set(nova.db, 'volume_type_extra_specs_get', - return_volume_type_extra_specs) - - req = fakes.HTTPRequest.blank(self.api_path) - res_dict = self.controller.index(req, 1) - - self.assertEqual('value1', res_dict['extra_specs']['key1']) - - def test_index_no_data(self): - self.stubs.Set(nova.db, 'volume_type_extra_specs_get', - return_empty_volume_type_extra_specs) - - req = fakes.HTTPRequest.blank(self.api_path) - res_dict = self.controller.index(req, 1) - - self.assertEqual(0, len(res_dict['extra_specs'])) - - def test_show(self): - self.stubs.Set(nova.db, 'volume_type_extra_specs_get', - return_volume_type_extra_specs) - - req = fakes.HTTPRequest.blank(self.api_path + '/key5') - res_dict = self.controller.show(req, 1, 'key5') - - self.assertEqual('value5', res_dict['key5']) - - def test_show_spec_not_found(self): - self.stubs.Set(nova.db, 'volume_type_extra_specs_get', - return_empty_volume_type_extra_specs) - - req = fakes.HTTPRequest.blank(self.api_path + '/key6') - self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, - req, 1, 'key6') - - def test_delete(self): - self.stubs.Set(nova.db, 'volume_type_extra_specs_delete', - delete_volume_type_extra_specs) - - req = fakes.HTTPRequest.blank(self.api_path + '/key5') - self.controller.delete(req, 1, 'key5') - - def test_create(self): - self.stubs.Set(nova.db, - 'volume_type_extra_specs_update_or_create', - return_create_volume_type_extra_specs) - body = {"extra_specs": {"key1": "value1"}} - - req = fakes.HTTPRequest.blank(self.api_path) - res_dict = self.controller.create(req, 1, body) - - self.assertEqual('value1', res_dict['extra_specs']['key1']) - - def test_create_empty_body(self): - self.stubs.Set(nova.db, - 'volume_type_extra_specs_update_or_create', - return_create_volume_type_extra_specs) - - req = fakes.HTTPRequest.blank(self.api_path) - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - req, 1, '') - - def test_update_item(self): - self.stubs.Set(nova.db, - 'volume_type_extra_specs_update_or_create', - return_create_volume_type_extra_specs) - body = {"key1": "value1"} - - req = fakes.HTTPRequest.blank(self.api_path + '/key1') - res_dict = self.controller.update(req, 1, 'key1', body) - - self.assertEqual('value1', res_dict['key1']) - - def test_update_item_empty_body(self): - self.stubs.Set(nova.db, - 'volume_type_extra_specs_update_or_create', - return_create_volume_type_extra_specs) - - req = fakes.HTTPRequest.blank(self.api_path + '/key1') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, - req, 1, 'key1', '') - - def test_update_item_too_many_keys(self): - self.stubs.Set(nova.db, - 'volume_type_extra_specs_update_or_create', - return_create_volume_type_extra_specs) - body = {"key1": "value1", "key2": "value2"} - - req = fakes.HTTPRequest.blank(self.api_path + '/key1') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, - req, 1, 'key1', body) - - def test_update_item_body_uri_mismatch(self): - self.stubs.Set(nova.db, - 'volume_type_extra_specs_update_or_create', - return_create_volume_type_extra_specs) - body = {"key1": "value1"} - - req = fakes.HTTPRequest.blank(self.api_path + '/bad') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, - req, 1, 'bad', body) diff --git a/nova/tests/api/openstack/contrib/test_volumes.py b/nova/tests/api/openstack/contrib/test_volumes.py deleted file mode 100644 index a130d1140..000000000 --- a/nova/tests/api/openstack/contrib/test_volumes.py +++ /dev/null @@ -1,88 +0,0 @@ -# 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 -import webob - -import nova -from nova import flags -from nova import test -from nova.compute import instance_types -from nova.tests.api.openstack import fakes - - -FLAGS = flags.FLAGS - - -FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' -IMAGE_UUID = 'c905cedb-7281-47e4-8a62-f26bc5fc4c77' - - -def fake_compute_api_create(cls, context, instance_type, image_href, **kwargs): - global _block_device_mapping_seen - _block_device_mapping_seen = kwargs.get('block_device_mapping') - - inst_type = instance_types.get_instance_type_by_flavor_id(2) - resv_id = None - return ([{'id': 1, - 'display_name': 'test_server', - 'uuid': FAKE_UUID, - 'instance_type': dict(inst_type), - 'access_ip_v4': '1.2.3.4', - 'access_ip_v6': 'fead::1234', - 'image_ref': IMAGE_UUID, - 'user_id': 'fake', - 'project_id': 'fake', - 'created_at': datetime.datetime(2010, 10, 10, 12, 0, 0), - 'updated_at': datetime.datetime(2010, 11, 11, 11, 0, 0), - 'progress': 0, - 'fixed_ips': [] - }], resv_id) - - -class BootFromVolumeTest(test.TestCase): - - def setUp(self): - super(BootFromVolumeTest, self).setUp() - self.stubs.Set(nova.compute.API, 'create', fake_compute_api_create) - fakes.stub_out_nw_api(self.stubs) - - def test_create_root_volume(self): - body = dict(server=dict( - name='test_server', imageRef=IMAGE_UUID, - flavorRef=2, min_count=1, max_count=1, - block_device_mapping=[dict( - volume_id=1, - device_name='/dev/vda', - virtual='root', - delete_on_termination=False, - )] - )) - global _block_device_mapping_seen - _block_device_mapping_seen = None - req = webob.Request.blank('/v1.1/fake/os-volumes_boot') - req.method = 'POST' - req.body = json.dumps(body) - req.headers['content-type'] = 'application/json' - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - server = json.loads(res.body)['server'] - self.assertEqual(FAKE_UUID, server['id']) - self.assertEqual(FLAGS.password_length, len(server['adminPass'])) - self.assertEqual(len(_block_device_mapping_seen), 1) - self.assertEqual(_block_device_mapping_seen[0]['volume_id'], 1) - self.assertEqual(_block_device_mapping_seen[0]['device_name'], - '/dev/vda') diff --git a/nova/tests/api/openstack/contrib/test_vsa.py b/nova/tests/api/openstack/contrib/test_vsa.py deleted file mode 100644 index 6f6ea8e32..000000000 --- a/nova/tests/api/openstack/contrib/test_vsa.py +++ /dev/null @@ -1,450 +0,0 @@ -# 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. - -import json -import stubout -import unittest -import webob - -from nova import context -from nova import db -from nova import exception -from nova import flags -from nova import log as logging -from nova import test -from nova import volume -from nova import vsa -from nova.api import openstack -from nova.tests.api.openstack import fakes -import nova.wsgi - -from nova.api.openstack.contrib.virtual_storage_arrays import _vsa_view - -FLAGS = flags.FLAGS - -LOG = logging.getLogger('nova.tests.api.openstack.vsa') - -last_param = {} - - -def _get_default_vsa_param(): - return { - 'display_name': 'Test_VSA_name', - 'display_description': 'Test_VSA_description', - 'vc_count': 1, - 'instance_type': 'm1.small', - 'instance_type_id': 5, - 'image_name': None, - 'availability_zone': None, - 'storage': [], - 'shared': False - } - - -def stub_vsa_create(self, context, **param): - global last_param - LOG.debug(_("_create: param=%s"), param) - param['id'] = 123 - param['name'] = 'Test name' - param['instance_type_id'] = 5 - last_param = param - return param - - -def stub_vsa_delete(self, context, vsa_id): - global last_param - last_param = dict(vsa_id=vsa_id) - - LOG.debug(_("_delete: %s"), locals()) - if vsa_id != '123': - raise exception.NotFound - - -def stub_vsa_get(self, context, vsa_id): - global last_param - last_param = dict(vsa_id=vsa_id) - - LOG.debug(_("_get: %s"), locals()) - if vsa_id != '123': - raise exception.NotFound - - param = _get_default_vsa_param() - param['id'] = vsa_id - return param - - -def stub_vsa_get_all(self, context): - LOG.debug(_("_get_all: %s"), locals()) - param = _get_default_vsa_param() - param['id'] = 123 - return [param] - - -class VSAApiTest(test.TestCase): - def setUp(self): - super(VSAApiTest, 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(vsa.api.API, "create", stub_vsa_create) - self.stubs.Set(vsa.api.API, "delete", stub_vsa_delete) - self.stubs.Set(vsa.api.API, "get", stub_vsa_get) - self.stubs.Set(vsa.api.API, "get_all", stub_vsa_get_all) - - self.context = context.get_admin_context() - - def tearDown(self): - self.stubs.UnsetAll() - super(VSAApiTest, self).tearDown() - - def test_vsa_create(self): - global last_param - last_param = {} - - vsa = {"displayName": "VSA Test Name", - "displayDescription": "VSA Test Desc"} - body = dict(vsa=vsa) - req = webob.Request.blank('/v1.1/777/zadr-vsa') - req.method = 'POST' - req.body = json.dumps(body) - req.headers['content-type'] = 'application/json' - - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 200) - - # Compare if parameters were correctly passed to stub - self.assertEqual(last_param['display_name'], "VSA Test Name") - self.assertEqual(last_param['display_description'], "VSA Test Desc") - - resp_dict = json.loads(resp.body) - self.assertTrue('vsa' in resp_dict) - self.assertEqual(resp_dict['vsa']['displayName'], vsa['displayName']) - self.assertEqual(resp_dict['vsa']['displayDescription'], - vsa['displayDescription']) - - def test_vsa_create_no_body(self): - req = webob.Request.blank('/v1.1/777/zadr-vsa') - req.method = 'POST' - req.body = json.dumps({}) - req.headers['content-type'] = 'application/json' - - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 422) - - def test_vsa_delete(self): - global last_param - last_param = {} - - vsa_id = 123 - req = webob.Request.blank('/v1.1/777/zadr-vsa/%d' % vsa_id) - req.method = 'DELETE' - - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 200) - self.assertEqual(str(last_param['vsa_id']), str(vsa_id)) - - def test_vsa_delete_invalid_id(self): - global last_param - last_param = {} - - vsa_id = 234 - req = webob.Request.blank('/v1.1/777/zadr-vsa/%d' % vsa_id) - req.method = 'DELETE' - - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 404) - self.assertEqual(str(last_param['vsa_id']), str(vsa_id)) - - def test_vsa_show(self): - global last_param - last_param = {} - - vsa_id = 123 - req = webob.Request.blank('/v1.1/777/zadr-vsa/%d' % vsa_id) - req.method = 'GET' - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 200) - self.assertEqual(str(last_param['vsa_id']), str(vsa_id)) - - resp_dict = json.loads(resp.body) - self.assertTrue('vsa' in resp_dict) - self.assertEqual(resp_dict['vsa']['id'], str(vsa_id)) - - def test_vsa_show_invalid_id(self): - global last_param - last_param = {} - - vsa_id = 234 - req = webob.Request.blank('/v1.1/777/zadr-vsa/%d' % vsa_id) - req.method = 'GET' - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 404) - self.assertEqual(str(last_param['vsa_id']), str(vsa_id)) - - def test_vsa_index(self): - req = webob.Request.blank('/v1.1/777/zadr-vsa') - req.method = 'GET' - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 200) - - resp_dict = json.loads(resp.body) - - self.assertTrue('vsaSet' in resp_dict) - resp_vsas = resp_dict['vsaSet'] - self.assertEqual(len(resp_vsas), 1) - - resp_vsa = resp_vsas.pop() - self.assertEqual(resp_vsa['id'], 123) - - def test_vsa_detail(self): - req = webob.Request.blank('/v1.1/777/zadr-vsa/detail') - req.method = 'GET' - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 200) - - resp_dict = json.loads(resp.body) - - self.assertTrue('vsaSet' in resp_dict) - resp_vsas = resp_dict['vsaSet'] - self.assertEqual(len(resp_vsas), 1) - - resp_vsa = resp_vsas.pop() - self.assertEqual(resp_vsa['id'], 123) - - -def _get_default_volume_param(): - return { - 'id': 123, - 'status': 'available', - 'size': 100, - 'availability_zone': 'nova', - 'created_at': None, - 'attach_status': 'detached', - 'name': 'vol name', - 'display_name': 'Default vol name', - 'display_description': 'Default vol description', - 'volume_type_id': 1, - 'volume_metadata': [], - } - - -def stub_get_vsa_volume_type(self, context): - return {'id': 1, - 'name': 'VSA volume type', - 'extra_specs': {'type': 'vsa_volume'}} - - -def stub_volume_create(self, context, size, snapshot_id, name, description, - **param): - LOG.debug(_("_create: param=%s"), size) - vol = _get_default_volume_param() - vol['size'] = size - vol['display_name'] = name - vol['display_description'] = description - return vol - - -def stub_volume_update(self, context, **param): - LOG.debug(_("_volume_update: param=%s"), param) - pass - - -def stub_volume_delete(self, context, **param): - LOG.debug(_("_volume_delete: param=%s"), param) - pass - - -def stub_volume_get(self, context, volume_id): - LOG.debug(_("_volume_get: volume_id=%s"), volume_id) - vol = _get_default_volume_param() - vol['id'] = volume_id - meta = {'key': 'from_vsa_id', 'value': '123'} - if volume_id == '345': - meta = {'key': 'to_vsa_id', 'value': '123'} - vol['volume_metadata'].append(meta) - return vol - - -def stub_volume_get_notfound(self, context, volume_id): - raise exception.NotFound - - -def stub_volume_get_all(self, context, search_opts): - vol = stub_volume_get(self, context, '123') - vol['metadata'] = search_opts['metadata'] - return [vol] - - -def return_vsa(context, vsa_id): - return {'id': vsa_id} - - -class VSAVolumeApiTest(test.TestCase): - - def setUp(self, test_obj=None, test_objs=None): - super(VSAVolumeApiTest, 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(nova.db, 'vsa_get', return_vsa) - self.stubs.Set(vsa.api.API, "get_vsa_volume_type", - stub_get_vsa_volume_type) - - self.stubs.Set(volume.api.API, "update", stub_volume_update) - self.stubs.Set(volume.api.API, "delete", stub_volume_delete) - self.stubs.Set(volume.api.API, "get", stub_volume_get) - self.stubs.Set(volume.api.API, "get_all", stub_volume_get_all) - - self.context = context.get_admin_context() - self.test_obj = test_obj if test_obj else "volume" - self.test_objs = test_objs if test_objs else "volumes" - - def tearDown(self): - self.stubs.UnsetAll() - super(VSAVolumeApiTest, self).tearDown() - - def test_vsa_volume_create(self): - self.stubs.Set(volume.api.API, "create", stub_volume_create) - - vol = {"size": 100, - "displayName": "VSA Volume Test Name", - "displayDescription": "VSA Volume Test Desc"} - body = {self.test_obj: vol} - req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s' % self.test_objs) - req.method = 'POST' - req.body = json.dumps(body) - req.headers['content-type'] = 'application/json' - resp = req.get_response(fakes.wsgi_app()) - - if self.test_obj == "volume": - self.assertEqual(resp.status_int, 200) - - resp_dict = json.loads(resp.body) - self.assertTrue(self.test_obj in resp_dict) - self.assertEqual(resp_dict[self.test_obj]['size'], - vol['size']) - self.assertEqual(resp_dict[self.test_obj]['displayName'], - vol['displayName']) - self.assertEqual(resp_dict[self.test_obj]['displayDescription'], - vol['displayDescription']) - else: - self.assertEqual(resp.status_int, 400) - - def test_vsa_volume_create_no_body(self): - req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s' % self.test_objs) - req.method = 'POST' - req.body = json.dumps({}) - req.headers['content-type'] = 'application/json' - - resp = req.get_response(fakes.wsgi_app()) - if self.test_obj == "volume": - self.assertEqual(resp.status_int, 422) - else: - self.assertEqual(resp.status_int, 400) - - def test_vsa_volume_index(self): - req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s' % self.test_objs) - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 200) - - def test_vsa_volume_detail(self): - req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/detail' % \ - self.test_objs) - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 200) - - def test_vsa_volume_show(self): - obj_num = 234 if self.test_objs == "volumes" else 345 - req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/%s' % \ - (self.test_objs, obj_num)) - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 200) - - def test_vsa_volume_show_no_vsa_assignment(self): - req = webob.Request.blank('/v1.1/777/zadr-vsa/4/%s/333' % \ - (self.test_objs)) - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 400) - - def test_vsa_volume_show_no_volume(self): - self.stubs.Set(volume.api.API, "get", stub_volume_get_notfound) - - req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/333' % \ - (self.test_objs)) - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 404) - - def test_vsa_volume_update(self): - obj_num = 234 if self.test_objs == "volumes" else 345 - update = {"status": "available", - "displayName": "Test Display name"} - body = {self.test_obj: update} - req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/%s' % \ - (self.test_objs, obj_num)) - req.method = 'PUT' - req.body = json.dumps(body) - req.headers['content-type'] = 'application/json' - - resp = req.get_response(fakes.wsgi_app()) - if self.test_obj == "volume": - self.assertEqual(resp.status_int, 202) - else: - self.assertEqual(resp.status_int, 400) - - def test_vsa_volume_delete(self): - obj_num = 234 if self.test_objs == "volumes" else 345 - req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/%s' % \ - (self.test_objs, obj_num)) - req.method = 'DELETE' - resp = req.get_response(fakes.wsgi_app()) - if self.test_obj == "volume": - self.assertEqual(resp.status_int, 202) - else: - self.assertEqual(resp.status_int, 400) - - def test_vsa_volume_delete_no_vsa_assignment(self): - req = webob.Request.blank('/v1.1/777/zadr-vsa/4/%s/333' % \ - (self.test_objs)) - req.method = 'DELETE' - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 400) - - def test_vsa_volume_delete_no_volume(self): - self.stubs.Set(volume.api.API, "get", stub_volume_get_notfound) - - req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/333' % \ - (self.test_objs)) - req.method = 'DELETE' - resp = req.get_response(fakes.wsgi_app()) - if self.test_obj == "volume": - self.assertEqual(resp.status_int, 404) - else: - self.assertEqual(resp.status_int, 400) - - -class VSADriveApiTest(VSAVolumeApiTest): - def setUp(self): - super(VSADriveApiTest, self).setUp(test_obj="drive", - test_objs="drives") - - def tearDown(self): - self.stubs.UnsetAll() - super(VSADriveApiTest, self).tearDown() diff --git a/nova/tests/api/openstack/extensions/__init__.py b/nova/tests/api/openstack/extensions/__init__.py deleted file mode 100644 index 848908a95..000000000 --- a/nova/tests/api/openstack/extensions/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/nova/tests/api/openstack/extensions/foxinsocks.py b/nova/tests/api/openstack/extensions/foxinsocks.py deleted file mode 100644 index cbf101f47..000000000 --- a/nova/tests/api/openstack/extensions/foxinsocks.py +++ /dev/null @@ -1,94 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import json -import webob.exc - -from nova.api.openstack import extensions - - -class FoxInSocksController(object): - - def index(self, req): - return "Try to say this Mr. Knox, sir..." - - -class Foxinsocks(object): - """The Fox In Socks Extension""" - - name = "Fox In Socks" - alias = "FOXNSOX" - namespace = "http://www.fox.in.socks/api/ext/pie/v1.0" - updated = "2011-01-22T13:25:27-06:00" - - def __init__(self, ext_mgr): - ext_mgr.register(self) - - def get_resources(self): - resources = [] - resource = extensions.ResourceExtension('foxnsocks', - FoxInSocksController()) - resources.append(resource) - return resources - - def get_actions(self): - actions = [] - actions.append(extensions.ActionExtension('servers', 'add_tweedle', - self._add_tweedle)) - actions.append(extensions.ActionExtension('servers', 'delete_tweedle', - self._delete_tweedle)) - actions.append(extensions.ActionExtension('servers', 'fail', - self._fail)) - return actions - - def get_request_extensions(self): - request_exts = [] - - def _goose_handler(req, res, body): - #NOTE: This only handles JSON responses. - # You can use content type header to test for XML. - body['flavor']['googoose'] = req.GET.get('chewing') - return res - - req_ext1 = extensions.RequestExtension('GET', - '/v1.1/:(project_id)/flavors/:(id)', - _goose_handler) - request_exts.append(req_ext1) - - def _bands_handler(req, res, body): - #NOTE: This only handles JSON responses. - # You can use content type header to test for XML. - body['big_bands'] = 'Pig Bands!' - return res - - req_ext2 = extensions.RequestExtension('GET', - '/v1.1/:(project_id)/flavors/:(id)', - _bands_handler) - request_exts.append(req_ext2) - return request_exts - - def _add_tweedle(self, input_dict, req, id): - - return "Tweedle Beetle Added." - - def _delete_tweedle(self, input_dict, req, id): - - return "Tweedle Beetle Deleted." - - def _fail(self, input_dict, req, id): - - raise webob.exc.HTTPBadRequest(explanation='Tweedle fail') diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index 0f58cf55b..c12ae5eab 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -23,25 +23,25 @@ import webob.request from glance import client as glance_client -from nova import context -from nova import exception as exc -from nova import utils -from nova import wsgi -import nova.api.openstack.auth -from nova.api import openstack +import nova.api.openstack.v2.auth from nova.api import auth as api_auth -from nova.api.openstack import auth -from nova.api.openstack import extensions -from nova.api.openstack import limits -from nova.api.openstack import urlmap -from nova.api.openstack import versions +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.openstack import wsgi as os_wsgi from nova.auth.manager import User, Project from nova.compute import instance_types from nova.compute import vm_states +from nova import context from nova.db.sqlalchemy import models +from nova import exception as exc import nova.image.fake from nova.tests.glance import stubs as glance_stubs +from nova import utils +from nova import wsgi class Context(object): @@ -72,35 +72,36 @@ def fake_wsgi(self, req): return self.application -def wsgi_app(inner_app11=None, fake_auth=True, fake_auth_context=None, +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_app11: - inner_app11 = openstack.APIRouter() + if not inner_app_v2: + inner_app_v2 = v2.APIRouter() if fake_auth: if fake_auth_context is not None: ctxt = fake_auth_context else: ctxt = context.RequestContext('fake', 'fake', auth_token=True) - api11 = openstack.FaultWrapper(api_auth.InjectContext(ctxt, + api_v2 = v2.FaultWrapper(api_auth.InjectContext(ctxt, limits.RateLimitingMiddleware( serialization( - extensions.ExtensionMiddleware(inner_app11))))) + extensions.ExtensionMiddleware(inner_app_v2))))) elif use_no_auth: - api11 = openstack.FaultWrapper(auth.NoAuthMiddleware( + api_v2 = v2.FaultWrapper(auth.NoAuthMiddleware( limits.RateLimitingMiddleware( serialization( - extensions.ExtensionMiddleware(inner_app11))))) + extensions.ExtensionMiddleware(inner_app_v2))))) else: - api11 = openstack.FaultWrapper(auth.AuthMiddleware( + api_v2 = v2.FaultWrapper(auth.AuthMiddleware( limits.RateLimitingMiddleware( serialization( - extensions.ExtensionMiddleware(inner_app11))))) + extensions.ExtensionMiddleware(inner_app_v2))))) Auth = auth mapper = urlmap.URLMap() - mapper['/v1.1'] = api11 - mapper['/'] = openstack.FaultWrapper(versions.Versions()) + mapper['/v2'] = api_v2 + mapper['/v1.1'] = api_v2 + mapper['/'] = v2.FaultWrapper(versions.Versions()) return mapper @@ -136,9 +137,9 @@ def stub_out_auth(stubs): def fake_auth_init(self, app): self.application = app - stubs.Set(nova.api.openstack.auth.AuthMiddleware, + stubs.Set(nova.api.openstack.v2.auth.AuthMiddleware, '__init__', fake_auth_init) - stubs.Set(nova.api.openstack.auth.AuthMiddleware, + stubs.Set(nova.api.openstack.v2.auth.AuthMiddleware, '__call__', fake_wsgi) @@ -147,10 +148,10 @@ def stub_out_rate_limiting(stubs): super(limits.RateLimitingMiddleware, self).__init__(app) self.application = app - stubs.Set(nova.api.openstack.limits.RateLimitingMiddleware, + stubs.Set(nova.api.openstack.v2.limits.RateLimitingMiddleware, '__init__', fake_rate_init) - stubs.Set(nova.api.openstack.limits.RateLimitingMiddleware, + stubs.Set(nova.api.openstack.v2.limits.RateLimitingMiddleware, '__call__', fake_wsgi) diff --git a/nova/tests/api/openstack/test_accounts.py b/nova/tests/api/openstack/test_accounts.py deleted file mode 100644 index ea96e1348..000000000 --- a/nova/tests/api/openstack/test_accounts.py +++ /dev/null @@ -1,162 +0,0 @@ -# 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. - - -import json - -from lxml import etree -import webob - -from nova import test -from nova.api.openstack import accounts -from nova.auth.manager import User -from nova.tests.api.openstack import fakes - - -def fake_init(self): - self.manager = fakes.FakeAuthManager() - - -def fake_admin_check(self, req): - return True - - -class AccountsTest(test.TestCase): - def setUp(self): - super(AccountsTest, self).setUp() - self.flags(verbose=True, allow_admin_api=True) - self.stubs.Set(accounts.Controller, '__init__', - fake_init) - self.stubs.Set(accounts.Controller, '_check_admin', - fake_admin_check) - fakes.FakeAuthManager.clear_fakes() - fakes.FakeAuthDatabase.data = {} - fakes.stub_out_networking(self.stubs) - fakes.stub_out_rate_limiting(self.stubs) - fakes.stub_out_auth(self.stubs) - - fakemgr = fakes.FakeAuthManager() - joeuser = User('id1', 'guy1', 'acc1', 'secret1', False) - superuser = User('id2', 'guy2', 'acc2', 'secret2', True) - fakemgr.add_user(joeuser) - fakemgr.add_user(superuser) - fakemgr.create_project('test1', joeuser) - fakemgr.create_project('test2', superuser) - - def test_get_account(self): - req = webob.Request.blank('/v1.1/fake/accounts/test1') - res = req.get_response(fakes.wsgi_app()) - res_dict = json.loads(res.body) - - self.assertEqual(res.status_int, 200) - self.assertEqual(res_dict['account']['id'], 'test1') - self.assertEqual(res_dict['account']['name'], 'test1') - self.assertEqual(res_dict['account']['manager'], 'id1') - - def test_get_account_xml(self): - req = webob.Request.blank('/v1.1/fake/accounts/test1.xml') - res = req.get_response(fakes.wsgi_app()) - res_tree = etree.fromstring(res.body) - - self.assertEqual(res.status_int, 200) - self.assertEqual('account', res_tree.tag) - self.assertEqual('test1', res_tree.get('id')) - self.assertEqual('test1', res_tree.get('name')) - self.assertEqual('id1', res_tree.get('manager')) - - def test_account_delete(self): - req = webob.Request.blank('/v1.1/fake/accounts/test1') - req.method = 'DELETE' - res = req.get_response(fakes.wsgi_app()) - self.assertTrue('test1' not in fakes.FakeAuthManager.projects) - self.assertEqual(res.status_int, 200) - - def test_account_create(self): - body = dict(account=dict(description='test account', - manager='id1')) - req = webob.Request.blank('/v1.1/fake/accounts/newacct') - req.headers["Content-Type"] = "application/json" - req.method = 'PUT' - req.body = json.dumps(body) - - res = req.get_response(fakes.wsgi_app()) - res_dict = json.loads(res.body) - - self.assertEqual(res.status_int, 200) - self.assertEqual(res_dict['account']['id'], 'newacct') - self.assertEqual(res_dict['account']['name'], 'newacct') - self.assertEqual(res_dict['account']['description'], 'test account') - self.assertEqual(res_dict['account']['manager'], 'id1') - self.assertTrue('newacct' in - fakes.FakeAuthManager.projects) - self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 3) - - def test_account_create_xml(self): - body = dict(account=dict(description='test account', - manager='id1')) - req = webob.Request.blank('/v1.1/fake/accounts/newacct.xml') - req.headers["Content-Type"] = "application/json" - req.method = 'PUT' - req.body = json.dumps(body) - - res = req.get_response(fakes.wsgi_app()) - res_tree = etree.fromstring(res.body) - - self.assertEqual(res.status_int, 200) - self.assertEqual(res_tree.tag, 'account') - self.assertEqual(res_tree.get('id'), 'newacct') - self.assertEqual(res_tree.get('name'), 'newacct') - self.assertEqual(res_tree.get('description'), 'test account') - self.assertEqual(res_tree.get('manager'), 'id1') - self.assertTrue('newacct' in - fakes.FakeAuthManager.projects) - self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 3) - - def test_account_update(self): - body = dict(account=dict(description='test account', - manager='id2')) - req = webob.Request.blank('/v1.1/fake/accounts/test1') - req.headers["Content-Type"] = "application/json" - req.method = 'PUT' - req.body = json.dumps(body) - - res = req.get_response(fakes.wsgi_app()) - res_dict = json.loads(res.body) - - self.assertEqual(res.status_int, 200) - self.assertEqual(res_dict['account']['id'], 'test1') - self.assertEqual(res_dict['account']['name'], 'test1') - self.assertEqual(res_dict['account']['description'], 'test account') - self.assertEqual(res_dict['account']['manager'], 'id2') - self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 2) - - def test_account_update_xml(self): - body = dict(account=dict(description='test account', - manager='id2')) - req = webob.Request.blank('/v1.1/fake/accounts/test1.xml') - req.headers["Content-Type"] = "application/json" - req.method = 'PUT' - req.body = json.dumps(body) - - res = req.get_response(fakes.wsgi_app()) - res_tree = etree.fromstring(res.body) - - self.assertEqual(res.status_int, 200) - self.assertEqual(res_tree.tag, 'account') - self.assertEqual(res_tree.get('id'), 'test1') - self.assertEqual(res_tree.get('name'), 'test1') - self.assertEqual(res_tree.get('description'), 'test account') - self.assertEqual(res_tree.get('manager'), 'id2') - self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 2) diff --git a/nova/tests/api/openstack/test_api.py b/nova/tests/api/openstack/test_api.py deleted file mode 100644 index b7a0b01ef..000000000 --- a/nova/tests/api/openstack/test_api.py +++ /dev/null @@ -1,127 +0,0 @@ -# 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. - -import json - -import webob.exc -import webob.dec - -from lxml import etree -from webob import Request - -from nova import test -from nova.api import openstack -from nova.api.openstack import faults -from nova.tests.api.openstack import fakes - - -class APITest(test.TestCase): - - def _wsgi_app(self, inner_app): - # simpler version of the app than fakes.wsgi_app - return openstack.FaultWrapper(inner_app) - - def test_malformed_json(self): - req = webob.Request.blank('/') - req.method = 'POST' - req.body = '{' - req.headers["content-type"] = "application/json" - - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 400) - - def test_malformed_xml(self): - req = webob.Request.blank('/') - req.method = 'POST' - req.body = '' - req.headers["content-type"] = "application/xml" - - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 400) - - def test_vendor_content_type_json(self): - ctype = 'application/vnd.openstack.compute+json' - - req = webob.Request.blank('/') - req.headers['Accept'] = ctype - - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - self.assertEqual(res.content_type, ctype) - - body = json.loads(res.body) - - def test_vendor_content_type_xml(self): - ctype = 'application/vnd.openstack.compute+xml' - - req = webob.Request.blank('/') - req.headers['Accept'] = ctype - - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - self.assertEqual(res.content_type, ctype) - - body = etree.XML(res.body) - - def test_exceptions_are_converted_to_faults(self): - - @webob.dec.wsgify - def succeed(req): - return 'Succeeded' - - @webob.dec.wsgify - def raise_webob_exc(req): - raise webob.exc.HTTPNotFound(explanation='Raised a webob.exc') - - @webob.dec.wsgify - def fail(req): - raise Exception("Threw an exception") - - @webob.dec.wsgify - def raise_api_fault(req): - exc = webob.exc.HTTPNotFound(explanation='Raised a webob.exc') - return faults.Fault(exc) - - #api.application = succeed - api = self._wsgi_app(succeed) - resp = Request.blank('/').get_response(api) - self.assertFalse('cloudServersFault' in resp.body, resp.body) - self.assertEqual(resp.status_int, 200, resp.body) - - #api.application = raise_webob_exc - api = self._wsgi_app(raise_webob_exc) - resp = Request.blank('/').get_response(api) - self.assertFalse('cloudServersFault' in resp.body, resp.body) - self.assertEqual(resp.status_int, 404, resp.body) - - #api.application = raise_api_fault - api = self._wsgi_app(raise_api_fault) - resp = Request.blank('/').get_response(api) - self.assertTrue('itemNotFound' in resp.body, resp.body) - self.assertEqual(resp.status_int, 404, resp.body) - - #api.application = fail - api = self._wsgi_app(fail) - resp = Request.blank('/').get_response(api) - self.assertTrue('{"cloudServersFault' in resp.body, resp.body) - self.assertEqual(resp.status_int, 500, resp.body) - - #api.application = fail - api = self._wsgi_app(fail) - resp = Request.blank('/.xml').get_response(api) - self.assertTrue(' self.max_id: - self.max_id = id - - -def stub_instance(id, user_id='fake', project_id='fake', host=None, - vm_state=None, task_state=None, - reservation_id="", uuid=FAKE_UUID, image_ref="10", - flavor_id="1", name=None, key_name='', - access_ipv4=None, access_ipv6=None, progress=0): - - if host is not None: - host = str(host) - - if key_name: - key_data = 'FAKE' - else: - key_data = '' - - # ReservationID isn't sent back, hack it in there. - server_name = name or "server%s" % id - if reservation_id != "": - server_name = "reservation_%s" % (reservation_id, ) - - instance = { - "id": int(id), - "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), - "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), - "admin_pass": "", - "user_id": user_id, - "project_id": project_id, - "image_ref": image_ref, - "kernel_id": "", - "ramdisk_id": "", - "launch_index": 0, - "key_name": key_name, - "key_data": key_data, - "vm_state": vm_state or vm_states.BUILDING, - "task_state": task_state, - "memory_mb": 0, - "vcpus": 0, - "local_gb": 0, - "hostname": "", - "host": host, - "instance_type": {}, - "user_data": "", - "reservation_id": reservation_id, - "mac_address": "", - "scheduled_at": utils.utcnow(), - "launched_at": utils.utcnow(), - "terminated_at": utils.utcnow(), - "availability_zone": "", - "display_name": server_name, - "display_description": "", - "locked": False, - "metadata": [], - "access_ip_v4": access_ipv4, - "access_ip_v6": access_ipv6, - "uuid": uuid, - "progress": progress} - - return instance - - -class ConsolesControllerTest(test.TestCase): - def setUp(self): - super(ConsolesControllerTest, self).setUp() - self.flags(verbose=True) - self.instance_db = FakeInstanceDB() - self.stubs.Set(db, 'instance_get', - self.instance_db.return_server_by_id) - self.stubs.Set(db, 'instance_get_by_uuid', - self.instance_db.return_server_by_uuid) - self.uuid = str(utils.gen_uuid()) - self.url = '/v1.1/fake/servers/%s/consoles' % self.uuid - self.controller = consoles.Controller() - - def test_create_console(self): - def fake_create_console(cons_self, context, instance_id): - self.assertEqual(instance_id, self.uuid) - return {} - self.stubs.Set(console.API, 'create_console', fake_create_console) - - req = fakes.HTTPRequest.blank(self.url) - self.controller.create(req, self.uuid) - - def test_show_console(self): - def fake_get_console(cons_self, context, instance_id, console_id): - self.assertEqual(instance_id, self.uuid) - self.assertEqual(console_id, 20) - pool = dict(console_type='fake_type', - public_hostname='fake_hostname') - return dict(id=console_id, password='fake_password', - port='fake_port', pool=pool) - - expected = {'console': {'id': 20, - 'port': 'fake_port', - 'host': 'fake_hostname', - 'password': 'fake_password', - 'console_type': 'fake_type'}} - - self.stubs.Set(console.API, 'get_console', fake_get_console) - - req = fakes.HTTPRequest.blank(self.url + '/20') - res_dict = self.controller.show(req, self.uuid, '20') - self.assertDictMatch(res_dict, expected) - - def test_show_console_unknown_console(self): - def fake_get_console(cons_self, context, instance_id, console_id): - raise exception.ConsoleNotFound(console_id=console_id) - - self.stubs.Set(console.API, 'get_console', fake_get_console) - - req = fakes.HTTPRequest.blank(self.url + '/20') - self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, - req, self.uuid, '20') - - def test_show_console_unknown_instance(self): - def fake_get_console(cons_self, context, instance_id, console_id): - raise exception.InstanceNotFound(instance_id=instance_id) - - self.stubs.Set(console.API, 'get_console', fake_get_console) - - req = fakes.HTTPRequest.blank(self.url + '/20') - self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, - req, self.uuid, '20') - - def test_list_consoles(self): - def fake_get_consoles(cons_self, context, instance_id): - self.assertEqual(instance_id, self.uuid) - - pool1 = dict(console_type='fake_type', - public_hostname='fake_hostname') - cons1 = dict(id=10, password='fake_password', - port='fake_port', pool=pool1) - pool2 = dict(console_type='fake_type2', - public_hostname='fake_hostname2') - cons2 = dict(id=11, password='fake_password2', - port='fake_port2', pool=pool2) - return [cons1, cons2] - - expected = {'consoles': - [{'console': {'id': 10, 'console_type': 'fake_type'}}, - {'console': {'id': 11, 'console_type': 'fake_type2'}}]} - - self.stubs.Set(console.API, 'get_consoles', fake_get_consoles) - - req = fakes.HTTPRequest.blank(self.url) - res_dict = self.controller.index(req, self.uuid) - self.assertDictMatch(res_dict, expected) - - def test_delete_console(self): - def fake_get_console(cons_self, context, instance_id, console_id): - self.assertEqual(instance_id, self.uuid) - self.assertEqual(console_id, 20) - pool = dict(console_type='fake_type', - public_hostname='fake_hostname') - return dict(id=console_id, password='fake_password', - port='fake_port', pool=pool) - - def fake_delete_console(cons_self, context, instance_id, console_id): - self.assertEqual(instance_id, self.uuid) - self.assertEqual(console_id, 20) - - self.stubs.Set(console.API, 'get_console', fake_get_console) - self.stubs.Set(console.API, 'delete_console', fake_delete_console) - - req = fakes.HTTPRequest.blank(self.url + '/20') - self.controller.delete(req, self.uuid, '20') - - def test_delete_console_unknown_console(self): - def fake_delete_console(cons_self, context, instance_id, console_id): - raise exception.ConsoleNotFound(console_id=console_id) - - self.stubs.Set(console.API, 'delete_console', fake_delete_console) - - req = fakes.HTTPRequest.blank(self.url + '/20') - self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, - req, self.uuid, '20') - - def test_delete_console_unknown_instance(self): - def fake_delete_console(cons_self, context, instance_id, console_id): - raise exception.InstanceNotFound(instance_id=instance_id) - - self.stubs.Set(console.API, 'delete_console', fake_delete_console) - - req = fakes.HTTPRequest.blank(self.url + '/20') - self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, - req, self.uuid, '20') - - -class TestConsolesXMLSerializer(test.TestCase): - - serializer = consoles.ConsoleXMLSerializer() - - def test_show(self): - fixture = {'console': {'id': 20, - 'password': 'fake_password', - 'port': 'fake_port', - 'host': 'fake_hostname', - 'console_type': 'fake_type'}} - - output = self.serializer.serialize(fixture, 'show') - res_tree = etree.XML(output) - - self.assertEqual(res_tree.tag, 'console') - self.assertEqual(res_tree.xpath('id')[0].text, '20') - self.assertEqual(res_tree.xpath('port')[0].text, 'fake_port') - self.assertEqual(res_tree.xpath('host')[0].text, 'fake_hostname') - self.assertEqual(res_tree.xpath('password')[0].text, 'fake_password') - self.assertEqual(res_tree.xpath('console_type')[0].text, 'fake_type') - - def test_index(self): - fixture = {'consoles': [{'console': {'id': 10, - 'console_type': 'fake_type'}}, - {'console': {'id': 11, - 'console_type': 'fake_type2'}}]} - - output = self.serializer.serialize(fixture, 'index') - res_tree = etree.XML(output) - - self.assertEqual(res_tree.tag, 'consoles') - self.assertEqual(len(res_tree), 2) - self.assertEqual(res_tree[0].tag, 'console') - self.assertEqual(res_tree[1].tag, 'console') - self.assertEqual(len(res_tree[0]), 1) - self.assertEqual(res_tree[0][0].tag, 'console') - self.assertEqual(len(res_tree[1]), 1) - self.assertEqual(res_tree[1][0].tag, 'console') - self.assertEqual(res_tree[0][0].xpath('id')[0].text, '10') - self.assertEqual(res_tree[1][0].xpath('id')[0].text, '11') - self.assertEqual(res_tree[0][0].xpath('console_type')[0].text, - 'fake_type') - self.assertEqual(res_tree[1][0].xpath('console_type')[0].text, - 'fake_type2') diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py deleted file mode 100644 index b4fe3e730..000000000 --- a/nova/tests/api/openstack/test_extensions.py +++ /dev/null @@ -1,515 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import json -import os.path -import webob -from lxml import etree - -from nova import context -from nova import flags -from nova import test -from nova import wsgi as base_wsgi -from nova.api import openstack -from nova.api.openstack import extensions -from nova.api.openstack import flavors -from nova.api.openstack import wsgi -from nova.api.openstack import xmlutil -from nova.tests.api.openstack import fakes - -FLAGS = flags.FLAGS - -NS = "{http://docs.openstack.org/compute/api/v1.1}" -ATOMNS = "{http://www.w3.org/2005/Atom}" -response_body = "Try to say this Mr. Knox, sir..." - - -class StubController(object): - - def __init__(self, body): - self.body = body - - def index(self, req): - return self.body - - def create(self, req): - msg = 'All aboard the fail train!' - raise webob.exc.HTTPBadRequest(explanation=msg) - - def show(self, req, id): - raise webob.exc.HTTPNotFound() - - -class StubExtensionManager(object): - """Provides access to Tweedle Beetles""" - - name = "Tweedle Beetle Extension" - alias = "TWDLBETL" - - def __init__(self, resource_ext=None, action_ext=None, request_ext=None): - self.resource_ext = resource_ext - self.action_ext = action_ext - self.request_ext = request_ext - - def get_resources(self): - resource_exts = [] - if self.resource_ext: - resource_exts.append(self.resource_ext) - return resource_exts - - def get_actions(self): - action_exts = [] - if self.action_ext: - action_exts.append(self.action_ext) - return action_exts - - def get_request_extensions(self): - request_extensions = [] - if self.request_ext: - request_extensions.append(self.request_ext) - return request_extensions - - -class ExtensionTestCase(test.TestCase): - def setUp(self): - super(ExtensionTestCase, self).setUp() - ext_list = FLAGS.osapi_extension[:] - ext_list.append('nova.tests.api.openstack.extensions.' - 'foxinsocks.Foxinsocks') - self.flags(osapi_extension=ext_list) - - -class ExtensionControllerTest(ExtensionTestCase): - - def setUp(self): - super(ExtensionControllerTest, self).setUp() - self.ext_list = [ - "AdminActions", - "Createserverext", - "DeferredDelete", - "DiskConfig", - "ExtendedStatus", - "FlavorExtraSpecs", - "FlavorExtraData", - "Floating_ips", - "Fox In Socks", - "Hosts", - "Keypairs", - "Multinic", - "Quotas", - "Rescue", - "SecurityGroups", - "SimpleTenantUsage", - "VSAs", - "VirtualInterfaces", - "Volumes", - "VolumeTypes", - "Zones", - ] - self.ext_list.sort() - - def test_list_extensions_json(self): - app = openstack.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) - ser_midware = wsgi.LazySerializationMiddleware(ext_midware) - request = webob.Request.blank("/123/extensions") - response = request.get_response(ser_midware) - self.assertEqual(200, response.status_int) - - # Make sure we have all the extensions. - data = json.loads(response.body) - names = [x['name'] for x in data['extensions']] - names.sort() - self.assertEqual(names, self.ext_list) - - # Make sure that at least Fox in Sox is correct. - (fox_ext, ) = [ - x for x in data['extensions'] if x['alias'] == 'FOXNSOX'] - self.assertEqual(fox_ext, { - 'namespace': 'http://www.fox.in.socks/api/ext/pie/v1.0', - 'name': 'Fox In Socks', - 'updated': '2011-01-22T13:25:27-06:00', - 'description': 'The Fox In Socks Extension', - 'alias': 'FOXNSOX', - 'links': [] - }, - ) - - def test_get_extension_json(self): - app = openstack.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) - ser_midware = wsgi.LazySerializationMiddleware(ext_midware) - request = webob.Request.blank("/123/extensions/FOXNSOX") - response = request.get_response(ser_midware) - self.assertEqual(200, response.status_int) - - data = json.loads(response.body) - self.assertEqual(data['extension'], { - "namespace": "http://www.fox.in.socks/api/ext/pie/v1.0", - "name": "Fox In Socks", - "updated": "2011-01-22T13:25:27-06:00", - "description": "The Fox In Socks Extension", - "alias": "FOXNSOX", - "links": []}) - - def test_get_non_existing_extension_json(self): - app = openstack.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) - request = webob.Request.blank("/123/extensions/4") - response = request.get_response(ext_midware) - self.assertEqual(404, response.status_int) - - def test_list_extensions_xml(self): - app = openstack.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) - ser_midware = wsgi.LazySerializationMiddleware(ext_midware) - request = webob.Request.blank("/123/extensions") - request.accept = "application/xml" - response = request.get_response(ser_midware) - self.assertEqual(200, response.status_int) - print response.body - - root = etree.XML(response.body) - self.assertEqual(root.tag.split('extensions')[0], NS) - - # Make sure we have all the extensions. - exts = root.findall('{0}extension'.format(NS)) - self.assertEqual(len(exts), len(self.ext_list)) - - # Make sure that at least Fox in Sox is correct. - (fox_ext, ) = [x for x in exts if x.get('alias') == 'FOXNSOX'] - self.assertEqual(fox_ext.get('name'), 'Fox In Socks') - self.assertEqual(fox_ext.get('namespace'), - 'http://www.fox.in.socks/api/ext/pie/v1.0') - self.assertEqual(fox_ext.get('updated'), '2011-01-22T13:25:27-06:00') - self.assertEqual(fox_ext.findtext('{0}description'.format(NS)), - 'The Fox In Socks Extension') - - xmlutil.validate_schema(root, 'extensions') - - def test_get_extension_xml(self): - app = openstack.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) - ser_midware = wsgi.LazySerializationMiddleware(ext_midware) - request = webob.Request.blank("/123/extensions/FOXNSOX") - request.accept = "application/xml" - response = request.get_response(ser_midware) - self.assertEqual(200, response.status_int) - xml = response.body - print xml - - root = etree.XML(xml) - self.assertEqual(root.tag.split('extension')[0], NS) - self.assertEqual(root.get('alias'), 'FOXNSOX') - self.assertEqual(root.get('name'), 'Fox In Socks') - self.assertEqual(root.get('namespace'), - 'http://www.fox.in.socks/api/ext/pie/v1.0') - self.assertEqual(root.get('updated'), '2011-01-22T13:25:27-06:00') - self.assertEqual(root.findtext('{0}description'.format(NS)), - 'The Fox In Socks Extension') - - xmlutil.validate_schema(root, 'extension') - - -class ResourceExtensionTest(ExtensionTestCase): - - def test_no_extension_present(self): - manager = StubExtensionManager(None) - app = openstack.APIRouter() - ext_midware = 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)) - manager = StubExtensionManager(res_ext) - app = openstack.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app, manager) - ser_midware = wsgi.LazySerializationMiddleware(ext_midware) - request = webob.Request.blank("/123/tweedles") - response = request.get_response(ser_midware) - self.assertEqual(200, response.status_int) - self.assertEqual(response_body, response.body) - - def test_get_resources_with_controller(self): - res_ext = extensions.ResourceExtension('tweedles', - StubController(response_body)) - manager = StubExtensionManager(res_ext) - app = openstack.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app, manager) - ser_midware = wsgi.LazySerializationMiddleware(ext_midware) - request = webob.Request.blank("/123/tweedles") - response = request.get_response(ser_midware) - self.assertEqual(200, response.status_int) - self.assertEqual(response_body, response.body) - - def test_bad_request(self): - res_ext = extensions.ResourceExtension('tweedles', - StubController(response_body)) - manager = StubExtensionManager(res_ext) - app = openstack.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app, manager) - ser_midware = wsgi.LazySerializationMiddleware(ext_midware) - request = webob.Request.blank("/123/tweedles") - request.method = "POST" - response = request.get_response(ser_midware) - self.assertEqual(400, response.status_int) - self.assertEqual('application/json', response.content_type) - body = json.loads(response.body) - expected = { - "badRequest": { - "message": "All aboard the fail train!", - "code": 400 - } - } - self.assertDictMatch(expected, body) - - def test_non_exist_resource(self): - res_ext = extensions.ResourceExtension('tweedles', - StubController(response_body)) - manager = StubExtensionManager(res_ext) - app = openstack.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app, manager) - ser_midware = wsgi.LazySerializationMiddleware(ext_midware) - request = webob.Request.blank("/123/tweedles/1") - response = request.get_response(ser_midware) - self.assertEqual(404, response.status_int) - self.assertEqual('application/json', response.content_type) - body = json.loads(response.body) - expected = { - "itemNotFound": { - "message": "The resource could not be found.", - "code": 404 - } - } - self.assertDictMatch(expected, body) - - -class InvalidExtension(object): - - alias = "THIRD" - - -class ExtensionManagerTest(ExtensionTestCase): - - response_body = "Try to say this Mr. Knox, sir..." - - def test_get_resources(self): - app = openstack.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) - ser_midware = wsgi.LazySerializationMiddleware(ext_midware) - request = webob.Request.blank("/123/foxnsocks") - response = request.get_response(ser_midware) - self.assertEqual(200, response.status_int) - self.assertEqual(response_body, response.body) - - def test_invalid_extensions(self): - # Don't need the serialization middleware here because we're - # not testing any serialization - app = openstack.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) - ext_mgr = ext_midware.ext_mgr - ext_mgr.register(InvalidExtension()) - self.assertTrue('FOXNSOX' in ext_mgr.extensions) - self.assertTrue('THIRD' not in ext_mgr.extensions) - - -class ActionExtensionTest(ExtensionTestCase): - - def _send_server_action_request(self, url, body): - app = openstack.APIRouter() - ext_midware = extensions.ExtensionMiddleware(app) - ser_midware = wsgi.LazySerializationMiddleware(ext_midware) - request = webob.Request.blank(url) - request.method = 'POST' - request.content_type = 'application/json' - request.body = json.dumps(body) - response = request.get_response(ser_midware) - return response - - def test_extended_action(self): - body = dict(add_tweedle=dict(name="test")) - url = "/123/servers/abcd/action" - response = self._send_server_action_request(url, body) - self.assertEqual(200, response.status_int) - self.assertEqual("Tweedle Beetle Added.", response.body) - - body = dict(delete_tweedle=dict(name="test")) - response = self._send_server_action_request(url, body) - self.assertEqual(200, response.status_int) - self.assertEqual("Tweedle Beetle Deleted.", response.body) - - def test_invalid_action(self): - body = dict(blah=dict(name="test")) # Doesn't exist - url = "/123/servers/abcd/action" - response = self._send_server_action_request(url, body) - self.assertEqual(400, response.status_int) - self.assertEqual('application/json', response.content_type) - body = json.loads(response.body) - expected = { - "badRequest": { - "message": "There is no such server action: blah", - "code": 400 - } - } - self.assertDictMatch(expected, body) - - def test_non_exist_action(self): - body = dict(blah=dict(name="test")) - url = "/123/fdsa/1/action" - response = self._send_server_action_request(url, body) - self.assertEqual(404, response.status_int) - - def test_failed_action(self): - body = dict(fail=dict(name="test")) - url = "/123/servers/abcd/action" - response = self._send_server_action_request(url, body) - self.assertEqual(400, response.status_int) - self.assertEqual('application/json', response.content_type) - body = json.loads(response.body) - expected = { - "badRequest": { - "message": "Tweedle fail", - "code": 400 - } - } - self.assertDictMatch(expected, body) - - -class RequestExtensionTest(ExtensionTestCase): - - def test_get_resources_with_stub_mgr(self): - - def _req_handler(req, res, body): - # only handle JSON responses - body['flavor']['googoose'] = req.GET.get('chewing') - return res - - req_ext = extensions.RequestExtension('GET', - '/v1.1/123/flavors/:(id)', - _req_handler) - - manager = StubExtensionManager(None, None, req_ext) - app = fakes.wsgi_app(serialization=base_wsgi.Middleware) - ext_midware = extensions.ExtensionMiddleware(app, manager) - ser_midware = wsgi.LazySerializationMiddleware(ext_midware) - request = webob.Request.blank("/v1.1/123/flavors/1?chewing=bluegoo") - request.environ['api.version'] = '1.1' - response = request.get_response(ser_midware) - self.assertEqual(200, response.status_int) - response_data = json.loads(response.body) - self.assertEqual('bluegoo', response_data['flavor']['googoose']) - - def test_get_resources_with_mgr(self): - - app = fakes.wsgi_app(serialization=base_wsgi.Middleware) - ext_midware = extensions.ExtensionMiddleware(app) - ser_midware = wsgi.LazySerializationMiddleware(ext_midware) - request = webob.Request.blank("/v1.1/123/flavors/1?chewing=newblue") - request.environ['api.version'] = '1.1' - response = request.get_response(ser_midware) - self.assertEqual(200, response.status_int) - response_data = json.loads(response.body) - self.assertEqual('newblue', response_data['flavor']['googoose']) - self.assertEqual("Pig Bands!", response_data['big_bands']) - - -class ExtensionsXMLSerializerTest(test.TestCase): - - def test_serialize_extension(self): - serializer = extensions.ExtensionsXMLSerializer() - data = {'extension': { - 'name': 'ext1', - 'namespace': 'http://docs.rack.com/servers/api/ext/pie/v1.0', - 'alias': 'RS-PIE', - 'updated': '2011-01-22T13:25:27-06:00', - 'description': 'Adds the capability to share an image.', - 'links': [{'rel': 'describedby', - 'type': 'application/pdf', - 'href': 'http://docs.rack.com/servers/api/ext/cs.pdf'}, - {'rel': 'describedby', - 'type': 'application/vnd.sun.wadl+xml', - 'href': 'http://docs.rack.com/servers/api/ext/cs.wadl'}]}} - - xml = serializer.serialize(data, 'show') - print xml - root = etree.XML(xml) - ext_dict = data['extension'] - self.assertEqual(root.findtext('{0}description'.format(NS)), - ext_dict['description']) - - for key in ['name', 'namespace', 'alias', 'updated']: - self.assertEqual(root.get(key), ext_dict[key]) - - link_nodes = root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(ext_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - xmlutil.validate_schema(root, 'extension') - - def test_serialize_extensions(self): - serializer = extensions.ExtensionsXMLSerializer() - data = {"extensions": [{ - "name": "Public Image Extension", - "namespace": "http://foo.com/api/ext/pie/v1.0", - "alias": "RS-PIE", - "updated": "2011-01-22T13:25:27-06:00", - "description": "Adds the capability to share an image.", - "links": [{"rel": "describedby", - "type": "application/pdf", - "type": "application/vnd.sun.wadl+xml", - "href": "http://foo.com/api/ext/cs-pie.pdf"}, - {"rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://foo.com/api/ext/cs-pie.wadl"}]}, - {"name": "Cloud Block Storage", - "namespace": "http://foo.com/api/ext/cbs/v1.0", - "alias": "RS-CBS", - "updated": "2011-01-12T11:22:33-06:00", - "description": "Allows mounting cloud block storage.", - "links": [{"rel": "describedby", - "type": "application/pdf", - "href": "http://foo.com/api/ext/cs-cbs.pdf"}, - {"rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://foo.com/api/ext/cs-cbs.wadl"}]}]} - - xml = serializer.serialize(data, 'index') - print xml - root = etree.XML(xml) - ext_elems = root.findall('{0}extension'.format(NS)) - self.assertEqual(len(ext_elems), 2) - for i, ext_elem in enumerate(ext_elems): - ext_dict = data['extensions'][i] - self.assertEqual(ext_elem.findtext('{0}description'.format(NS)), - ext_dict['description']) - - for key in ['name', 'namespace', 'alias', 'updated']: - self.assertEqual(ext_elem.get(key), ext_dict[key]) - - link_nodes = ext_elem.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(ext_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - xmlutil.validate_schema(root, 'extensions') diff --git a/nova/tests/api/openstack/test_faults.py b/nova/tests/api/openstack/test_faults.py index 18bd136df..87cb2d3fe 100644 --- a/nova/tests/api/openstack/test_faults.py +++ b/nova/tests/api/openstack/test_faults.py @@ -24,7 +24,6 @@ import webob.exc from nova import test from nova.api.openstack import common -from nova.api.openstack import faults from nova.api.openstack import wsgi @@ -46,7 +45,7 @@ class TestFaults(test.TestCase): ] for request in requests: - fault = faults.Fault(webob.exc.HTTPBadRequest(explanation='scram')) + fault = wsgi.Fault(webob.exc.HTTPBadRequest(explanation='scram')) response = request.get_response(fault) expected = { @@ -69,7 +68,7 @@ class TestFaults(test.TestCase): for request in requests: exc = webob.exc.HTTPRequestEntityTooLarge - fault = faults.Fault(exc(explanation='sorry', + fault = wsgi.Fault(exc(explanation='sorry', headers={'Retry-After': 4})) response = request.get_response(fault) @@ -89,7 +88,7 @@ class TestFaults(test.TestCase): """Ensure the ability to raise `Fault`s in WSGI-ified methods.""" @webob.dec.wsgify def raiser(req): - raise faults.Fault(webob.exc.HTTPNotFound(explanation='whut?')) + raise wsgi.Fault(webob.exc.HTTPNotFound(explanation='whut?')) req = webob.Request.blank('/.xml') resp = req.get_response(raiser) @@ -99,7 +98,7 @@ class TestFaults(test.TestCase): def test_fault_has_status_int(self): """Ensure the status_int is set correctly on faults""" - fault = faults.Fault(webob.exc.HTTPBadRequest(explanation='what?')) + fault = wsgi.Fault(webob.exc.HTTPBadRequest(explanation='what?')) self.assertEqual(fault.status_int, 400) def test_xml_serializer(self): @@ -107,7 +106,7 @@ class TestFaults(test.TestCase): request = webob.Request.blank('/v1.1', headers={"Accept": "application/xml"}) - fault = faults.Fault(webob.exc.HTTPBadRequest(explanation='scram')) + fault = wsgi.Fault(webob.exc.HTTPBadRequest(explanation='scram')) response = request.get_response(fault) self.assertTrue(common.XML_NS_V11 in response.body) diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py deleted file mode 100644 index 24d81ef67..000000000 --- a/nova/tests/api/openstack/test_flavors.py +++ /dev/null @@ -1,670 +0,0 @@ -# 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. - -import json - -from lxml import etree -import webob - -from nova.api.openstack import flavors -from nova.api.openstack import xmlutil -import nova.compute.instance_types -from nova import exception -from nova import test -from nova.tests.api.openstack import fakes -from nova import wsgi - - -NS = "{http://docs.openstack.org/compute/api/v1.1}" -ATOMNS = "{http://www.w3.org/2005/Atom}" - - -FAKE_FLAVORS = { - 'flavor 1': { - "flavorid": '1', - "name": 'flavor 1', - "memory_mb": '256', - "local_gb": '10' - }, - 'flavor 2': { - "flavorid": '2', - "name": 'flavor 2', - "memory_mb": '512', - "local_gb": '20' - }, -} - - -def fake_instance_type_get_by_flavor_id(flavorid): - return FAKE_FLAVORS['flavor %s' % flavorid] - - -def fake_instance_type_get_all(inactive=False, filters=None): - def reject_min(db_attr, filter_attr): - return filter_attr in filters and\ - int(flavor[db_attr]) < int(filters[filter_attr]) - - filters = filters or {} - output = {} - for (flavor_name, flavor) in FAKE_FLAVORS.items(): - if reject_min('memory_mb', 'min_memory_mb'): - continue - elif reject_min('local_gb', 'min_local_gb'): - continue - - output[flavor_name] = flavor - - return output - - -def empty_instance_type_get_all(inactive=False, filters=None): - return {} - - -def return_instance_type_not_found(flavor_id): - raise exception.InstanceTypeNotFound(flavor_id=flavor_id) - - -class FlavorsTest(test.TestCase): - def setUp(self): - super(FlavorsTest, self).setUp() - fakes.stub_out_networking(self.stubs) - fakes.stub_out_rate_limiting(self.stubs) - self.stubs.Set(nova.compute.instance_types, "get_all_types", - fake_instance_type_get_all) - self.stubs.Set(nova.compute.instance_types, - "get_instance_type_by_flavor_id", - fake_instance_type_get_by_flavor_id) - - self.controller = flavors.Controller() - - def tearDown(self): - self.stubs.UnsetAll() - super(FlavorsTest, self).tearDown() - - def test_get_flavor_by_invalid_id(self): - self.stubs.Set(nova.compute.instance_types, - "get_instance_type_by_flavor_id", - return_instance_type_not_found) - req = fakes.HTTPRequest.blank('/v1.1/fake/flavors/asdf') - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.show, req, 'asdf') - - def test_get_flavor_by_id(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/flavors/1') - flavor = self.controller.show(req, '1') - expected = { - "flavor": { - "id": "1", - "name": "flavor 1", - "ram": "256", - "disk": "10", - "rxtx_cap": "", - "rxtx_quota": "", - "swap": "", - "vcpus": "", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/1", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/1", - }, - ], - }, - } - self.assertEqual(flavor, expected) - - def test_get_flavor_list(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/flavors') - flavor = self.controller.index(req) - expected = { - "flavors": [ - { - "id": "1", - "name": "flavor 1", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/1", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/1", - }, - ], - }, - { - "id": "2", - "name": "flavor 2", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/2", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/2", - }, - ], - }, - ], - } - self.assertEqual(flavor, expected) - - def test_get_flavor_list_detail(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/flavors/detail') - flavor = self.controller.detail(req) - expected = { - "flavors": [ - { - "id": "1", - "name": "flavor 1", - "ram": "256", - "disk": "10", - "rxtx_cap": "", - "rxtx_quota": "", - "swap": "", - "vcpus": "", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/1", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/1", - }, - ], - }, - { - "id": "2", - "name": "flavor 2", - "ram": "512", - "disk": "20", - "rxtx_cap": "", - "rxtx_quota": "", - "swap": "", - "vcpus": "", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/2", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/2", - }, - ], - }, - ], - } - self.assertEqual(flavor, expected) - - def test_get_empty_flavor_list(self): - self.stubs.Set(nova.compute.instance_types, "get_all_types", - empty_instance_type_get_all) - - req = fakes.HTTPRequest.blank('/v1.1/fake/flavors') - flavors = self.controller.index(req) - expected = {'flavors': []} - self.assertEqual(flavors, expected) - - def test_get_flavor_list_filter_min_ram(self): - """Flavor lists may be filtered by minRam""" - req = fakes.HTTPRequest.blank('/v1.1/fake/flavors?minRam=512') - flavor = self.controller.index(req) - expected = { - "flavors": [ - { - "id": "2", - "name": "flavor 2", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/2", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/2", - }, - ], - }, - ], - } - self.assertEqual(flavor, expected) - - def test_get_flavor_list_filter_min_disk(self): - """Flavor lists may be filtered by minRam""" - req = fakes.HTTPRequest.blank('/v1.1/fake/flavors?minDisk=20') - flavor = self.controller.index(req) - expected = { - "flavors": [ - { - "id": "2", - "name": "flavor 2", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/2", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/2", - }, - ], - }, - ], - } - self.assertEqual(flavor, expected) - - def test_get_flavor_list_detail_min_ram_and_min_disk(self): - """Tests that filtering work on flavor details and that minRam and - minDisk filters can be combined - """ - req = fakes.HTTPRequest.blank('/v1.1/fake/flavors/detail' - '?minRam=256&minDisk=20') - flavor = self.controller.detail(req) - expected = { - "flavors": [ - { - "id": "2", - "name": "flavor 2", - "ram": "512", - "disk": "20", - "rxtx_cap": "", - "rxtx_quota": "", - "swap": "", - "vcpus": "", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/2", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/2", - }, - ], - }, - ], - } - self.assertEqual(flavor, expected) - - def test_get_flavor_list_detail_bogus_min_ram(self): - """Tests that bogus minRam filtering values are ignored""" - req = fakes.HTTPRequest.blank('/v1.1/fake/flavors/detail?minRam=16GB') - flavor = self.controller.detail(req) - expected = { - "flavors": [ - { - "id": "1", - "name": "flavor 1", - "ram": "256", - "disk": "10", - "rxtx_cap": "", - "rxtx_quota": "", - "swap": "", - "vcpus": "", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/1", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/1", - }, - ], - }, - { - "id": "2", - "name": "flavor 2", - "ram": "512", - "disk": "20", - "rxtx_cap": "", - "rxtx_quota": "", - "swap": "", - "vcpus": "", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/2", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/2", - }, - ], - }, - ], - } - self.assertEqual(flavor, expected) - - def test_get_flavor_list_detail_bogus_min_disk(self): - """Tests that bogus minDisk filtering values are ignored""" - req = fakes.HTTPRequest.blank('/v1.1/fake/flavors/detail?minDisk=16GB') - flavor = self.controller.detail(req) - expected = { - "flavors": [ - { - "id": "1", - "name": "flavor 1", - "ram": "256", - "disk": "10", - "rxtx_cap": "", - "rxtx_quota": "", - "swap": "", - "vcpus": "", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/1", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/1", - }, - ], - }, - { - "id": "2", - "name": "flavor 2", - "ram": "512", - "disk": "20", - "rxtx_cap": "", - "rxtx_quota": "", - "swap": "", - "vcpus": "", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/2", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/2", - }, - ], - }, - ], - } - self.assertEqual(flavor, expected) - - -class FlavorsXMLSerializationTest(test.TestCase): - - def test_xml_declaration(self): - serializer = flavors.FlavorXMLSerializer() - - fixture = { - "flavor": { - "id": "12", - "name": "asdf", - "ram": "256", - "disk": "10", - "rxtx_cap": "", - "rxtx_quota": "", - "swap": "", - "vcpus": "", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/12", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/12", - }, - ], - }, - } - - output = serializer.serialize(fixture, 'show') - print output - has_dec = output.startswith("") - self.assertTrue(has_dec) - - def test_show(self): - serializer = flavors.FlavorXMLSerializer() - - fixture = { - "flavor": { - "id": "12", - "name": "asdf", - "ram": "256", - "disk": "10", - "rxtx_cap": "", - "rxtx_quota": "", - "swap": "", - "vcpus": "", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/12", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/12", - }, - ], - }, - } - - output = serializer.serialize(fixture, 'show') - print output - root = etree.XML(output) - xmlutil.validate_schema(root, 'flavor') - flavor_dict = fixture['flavor'] - - for key in ['name', 'id', 'ram', 'disk']: - self.assertEqual(root.get(key), str(flavor_dict[key])) - - link_nodes = root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(flavor_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - def test_show_handles_integers(self): - serializer = flavors.FlavorXMLSerializer() - - fixture = { - "flavor": { - "id": 12, - "name": "asdf", - "ram": 256, - "disk": 10, - "rxtx_cap": "", - "rxtx_quota": "", - "swap": "", - "vcpus": "", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/12", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/12", - }, - ], - }, - } - - output = serializer.serialize(fixture, 'show') - print output - root = etree.XML(output) - xmlutil.validate_schema(root, 'flavor') - flavor_dict = fixture['flavor'] - - for key in ['name', 'id', 'ram', 'disk']: - self.assertEqual(root.get(key), str(flavor_dict[key])) - - link_nodes = root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(flavor_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - def test_detail(self): - serializer = flavors.FlavorXMLSerializer() - - fixture = { - "flavors": [ - { - "id": "23", - "name": "flavor 23", - "ram": "512", - "disk": "20", - "rxtx_cap": "", - "rxtx_quota": "", - "swap": "", - "vcpus": "", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/23", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/23", - }, - ], - }, - { - "id": "13", - "name": "flavor 13", - "ram": "256", - "disk": "10", - "rxtx_cap": "", - "rxtx_quota": "", - "swap": "", - "vcpus": "", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/13", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/13", - }, - ], - }, - ], - } - - output = serializer.serialize(fixture, 'detail') - print output - root = etree.XML(output) - xmlutil.validate_schema(root, 'flavors') - flavor_elems = root.findall('{0}flavor'.format(NS)) - self.assertEqual(len(flavor_elems), 2) - for i, flavor_elem in enumerate(flavor_elems): - flavor_dict = fixture['flavors'][i] - - for key in ['name', 'id', 'ram', 'disk']: - self.assertEqual(flavor_elem.get(key), str(flavor_dict[key])) - - link_nodes = flavor_elem.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(flavor_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - def test_index(self): - serializer = flavors.FlavorXMLSerializer() - - fixture = { - "flavors": [ - { - "id": "23", - "name": "flavor 23", - "ram": "512", - "disk": "20", - "rxtx_cap": "", - "rxtx_quota": "", - "swap": "", - "vcpus": "", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/23", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/23", - }, - ], - }, - { - "id": "13", - "name": "flavor 13", - "ram": "256", - "disk": "10", - "rxtx_cap": "", - "rxtx_quota": "", - "swap": "", - "vcpus": "", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/flavors/13", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/flavors/13", - }, - ], - }, - ], - } - - output = serializer.serialize(fixture, 'index') - print output - root = etree.XML(output) - xmlutil.validate_schema(root, 'flavors_index') - flavor_elems = root.findall('{0}flavor'.format(NS)) - self.assertEqual(len(flavor_elems), 2) - for i, flavor_elem in enumerate(flavor_elems): - flavor_dict = fixture['flavors'][i] - - for key in ['name', 'id']: - self.assertEqual(flavor_elem.get(key), str(flavor_dict[key])) - - link_nodes = flavor_elem.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(flavor_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - def test_index_empty(self): - serializer = flavors.FlavorXMLSerializer() - - fixture = { - "flavors": [], - } - - output = serializer.serialize(fixture, 'index') - print output - root = etree.XML(output) - xmlutil.validate_schema(root, 'flavors_index') - flavor_elems = root.findall('{0}flavor'.format(NS)) - self.assertEqual(len(flavor_elems), 0) diff --git a/nova/tests/api/openstack/test_image_metadata.py b/nova/tests/api/openstack/test_image_metadata.py deleted file mode 100644 index dc0cf1671..000000000 --- a/nova/tests/api/openstack/test_image_metadata.py +++ /dev/null @@ -1,201 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import json -import webob - - -from nova.tests.api.openstack import fakes -from nova.api.openstack import image_metadata -from nova import flags -from nova import test - - -FLAGS = flags.FLAGS - - -class ImageMetaDataTest(test.TestCase): - - def setUp(self): - super(ImageMetaDataTest, self).setUp() - fakes.stub_out_glance(self.stubs) - self.controller = image_metadata.Controller() - - def test_index(self): - req = fakes.HTTPRequest.blank('/v1.1/123/images/123/metadata') - res_dict = self.controller.index(req, '123') - expected = {'metadata': {'key1': 'value1'}} - self.assertEqual(res_dict, expected) - - def test_show(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/key1') - res_dict = self.controller.show(req, '123', 'key1') - self.assertTrue('meta' in res_dict) - self.assertEqual(len(res_dict['meta']), 1) - self.assertEqual('value1', res_dict['meta']['key1']) - - def test_show_not_found(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/key9') - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.show, req, '123', 'key9') - - def test_show_image_not_found(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/images/100/metadata/key1') - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.show, req, '100', 'key9') - - def test_create(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata') - req.method = 'POST' - body = {"metadata": {"key7": "value7"}} - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - res = self.controller.create(req, '123', body) - - expected_output = {'metadata': {'key1': 'value1', 'key7': 'value7'}} - self.assertEqual(expected_output, res) - - def test_create_image_not_found(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/images/100/metadata') - req.method = 'POST' - body = {"metadata": {"key7": "value7"}} - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.create, req, '100', body) - - def test_update_all(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata') - req.method = 'PUT' - body = {"metadata": {"key9": "value9"}} - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - res = self.controller.update_all(req, '123', body) - - expected_output = {'metadata': {'key9': 'value9'}} - self.assertEqual(expected_output, res) - - def test_update_all_image_not_found(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/images/100/metadata') - req.method = 'PUT' - body = {"metadata": {"key9": "value9"}} - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.update_all, req, '100', body) - - def test_update_item(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/key1') - req.method = 'PUT' - body = {"meta": {"key1": "zz"}} - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - res = self.controller.update(req, '123', 'key1', body) - - expected_output = {'meta': {'key1': 'zz'}} - self.assertEqual(res, expected_output) - - def test_update_item_image_not_found(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/images/100/metadata/key1') - req.method = 'PUT' - body = {"meta": {"key1": "zz"}} - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.update, req, '100', 'key1', body) - - def test_update_item_bad_body(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/key1') - req.method = 'PUT' - body = {"key1": "zz"} - req.body = '' - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.update, req, '123', 'key1', body) - - def test_update_item_too_many_keys(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/key1') - req.method = 'PUT' - overload = {} - for num in range(FLAGS.quota_metadata_items + 1): - overload['key%s' % num] = 'value%s' % num - body = {'meta': overload} - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.update, req, '123', 'key1', body) - - def test_update_item_body_uri_mismatch(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/bad') - req.method = 'PUT' - body = {"meta": {"key1": "value1"}} - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.update, req, '123', 'bad', body) - - def test_delete(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/key1') - req.method = 'DELETE' - res = self.controller.delete(req, '123', 'key1') - - self.assertEqual(None, res) - - def test_delete_not_found(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/blah') - req.method = 'DELETE' - - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.delete, req, '123', 'blah') - - def test_delete_image_not_found(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/images/100/metadata/key1') - req.method = 'DELETE' - - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.delete, req, '100', 'key1') - - def test_too_many_metadata_items_on_create(self): - data = {"metadata": {}} - for num in range(FLAGS.quota_metadata_items + 1): - data['metadata']['key%i' % num] = "blah" - req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata') - req.method = 'POST' - req.body = json.dumps(data) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPRequestEntityTooLarge, - self.controller.create, req, '123', data) - self.assertRaises(webob.exc.HTTPRequestEntityTooLarge, - self.controller.create, req, '123', data) - - def test_too_many_metadata_items_on_put(self): - FLAGS.quota_metadata_items = 1 - req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/blah') - req.method = 'PUT' - body = {"meta": {"blah": "blah"}} - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPRequestEntityTooLarge, - self.controller.update, req, '123', 'blah', body) diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py deleted file mode 100644 index eb200046f..000000000 --- a/nova/tests/api/openstack/test_images.py +++ /dev/null @@ -1,1645 +0,0 @@ -# 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. - -""" -Tests of the new image services, both as a service layer, -and as a WSGI layer -""" - -from lxml import etree -import stubout -import urlparse -import webob - -from nova.api.openstack import images -from nova.api.openstack import xmlutil -from nova.api.openstack.views import images as images_view -from nova import test -from nova import utils -from nova.tests.api.openstack import fakes - - -NS = "{http://docs.openstack.org/compute/api/v1.1}" -ATOMNS = "{http://www.w3.org/2005/Atom}" -NOW_API_FORMAT = "2010-10-11T10:30:22Z" - - -class ImagesControllerTest(test.TestCase): - """ - Test of the OpenStack API /images application controller w/Glance. - """ - - def setUp(self): - """Run before each test.""" - super(ImagesControllerTest, self).setUp() - self.maxDiff = None - self.stubs = stubout.StubOutForTesting() - fakes.stub_out_networking(self.stubs) - fakes.stub_out_rate_limiting(self.stubs) - fakes.stub_out_key_pair_funcs(self.stubs) - fakes.stub_out_compute_api_snapshot(self.stubs) - fakes.stub_out_compute_api_backup(self.stubs) - fakes.stub_out_glance(self.stubs) - - self.controller = images.Controller() - - def tearDown(self): - """Run after each test.""" - self.stubs.UnsetAll() - super(ImagesControllerTest, self).tearDown() - - def test_get_image(self): - fake_req = fakes.HTTPRequest.blank('/v1.1/fake/images/123') - actual_image = self.controller.show(fake_req, '124') - - href = "http://localhost/v1.1/fake/images/124" - bookmark = "http://localhost/fake/images/124" - alternate = "%s/fake/images/124" % utils.generate_glance_url() - server_uuid = "aa640691-d1a7-4a67-9d3c-d35ee6b3cc74" - server_href = "http://localhost/v1.1/servers/" + server_uuid - server_bookmark = "http://localhost/servers/" + server_uuid - - expected_image = { - "image": { - "id": "124", - "name": "queued snapshot", - "updated": NOW_API_FORMAT, - "created": NOW_API_FORMAT, - "status": "SAVING", - "progress": 25, - "minDisk": 0, - "minRam": 0, - 'server': { - 'id': server_uuid, - "links": [{ - "rel": "self", - "href": server_href, - }, - { - "rel": "bookmark", - "href": server_bookmark, - }], - }, - "metadata": { - "instance_ref": server_href, - "user_id": "fake", - }, - "links": [{ - "rel": "self", - "href": href, - }, - { - "rel": "bookmark", - "href": bookmark, - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": alternate - }], - }, - } - - self.assertDictMatch(expected_image, actual_image) - - def test_get_image_404(self): - fake_req = fakes.HTTPRequest.blank('/v1.1/fake/images/unknown') - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.show, fake_req, 'unknown') - - def test_get_image_index(self): - fake_req = fakes.HTTPRequest.blank('/v1.1/fake/images') - response_list = self.controller.index(fake_req)['images'] - - expected_images = [ - { - "id": "123", - "name": "public image", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/images/123", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/123", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": "%s/fake/images/123" % - utils.generate_glance_url() - }, - ], - }, - { - "id": "124", - "name": "queued snapshot", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/images/124", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/124", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": "%s/fake/images/124" % - utils.generate_glance_url() - }, - ], - }, - { - "id": "125", - "name": "saving snapshot", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/images/125", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/125", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": "%s/fake/images/125" % - utils.generate_glance_url() - }, - ], - }, - { - "id": "126", - "name": "active snapshot", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/images/126", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/126", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": "%s/fake/images/126" % - utils.generate_glance_url() - }, - ], - }, - { - "id": "127", - "name": "killed snapshot", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/images/127", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/127", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": "%s/fake/images/127" % - utils.generate_glance_url() - }, - ], - }, - { - "id": "128", - "name": "deleted snapshot", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/images/128", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/128", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": "%s/fake/images/128" % - utils.generate_glance_url() - }, - ], - }, - { - "id": "129", - "name": "pending_delete snapshot", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/images/129", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/129", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": "%s/fake/images/129" % - utils.generate_glance_url() - }, - ], - }, - { - "id": "130", - "name": None, - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/images/130", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/130", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": "%s/fake/images/130" % - utils.generate_glance_url() - }, - ], - }, - ] - - self.assertDictListMatch(response_list, expected_images) - - def test_get_image_index_with_limit(self): - request = fakes.HTTPRequest.blank('/v1.1/fake/images?limit=3') - response = self.controller.index(request) - response_list = response["images"] - response_links = response["images_links"] - - alternate = "%s/fake/images/%s" - - expected_images = [ - { - "id": "123", - "name": "public image", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/images/123", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/123", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": alternate % (utils.generate_glance_url(), 123), - }, - ], - }, - { - "id": "124", - "name": "queued snapshot", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/images/124", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/124", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": alternate % (utils.generate_glance_url(), 124), - }, - ], - }, - { - "id": "125", - "name": "saving snapshot", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/images/125", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/125", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": alternate % (utils.generate_glance_url(), 125), - }, - ], - }, - ] - - self.assertDictListMatch(response_list, expected_images) - self.assertEqual(response_links[0]['rel'], 'next') - - href_parts = urlparse.urlparse(response_links[0]['href']) - self.assertEqual('/v1.1/fake/images', href_parts.path) - params = urlparse.parse_qs(href_parts.query) - self.assertDictMatch({'limit': ['3'], 'marker': ['125']}, params) - - def test_get_image_index_with_limit_and_extra_params(self): - request = fakes.HTTPRequest.blank('/v1.1/fake/images?limit=3&extra=bo') - response = self.controller.index(request) - response_links = response["images_links"] - - self.assertEqual(response_links[0]['rel'], 'next') - - href_parts = urlparse.urlparse(response_links[0]['href']) - self.assertEqual('/v1.1/fake/images', href_parts.path) - params = urlparse.parse_qs(href_parts.query) - self.assertDictMatch( - {'limit': ['3'], 'marker': ['125'], 'extra': ['bo']}, - params) - - def test_get_image_index_with_big_limit(self): - """ - Make sure we don't get images_links if limit is set - and the number of images returned is < limit - """ - request = fakes.HTTPRequest.blank('/v1.1/fake/images?limit=30') - response = self.controller.index(request) - - self.assertEqual(response.keys(), ['images']) - self.assertEqual(len(response['images']), 8) - - def test_get_image_details(self): - request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail') - response = self.controller.detail(request) - response_list = response["images"] - - server_uuid = "aa640691-d1a7-4a67-9d3c-d35ee6b3cc74" - server_href = "http://localhost/v1.1/servers/" + server_uuid - server_bookmark = "http://localhost/servers/" + server_uuid - alternate = "%s/fake/images/%s" - - expected = [{ - 'id': '123', - 'name': 'public image', - 'metadata': {'key1': 'value1'}, - 'updated': NOW_API_FORMAT, - 'created': NOW_API_FORMAT, - 'status': 'ACTIVE', - 'progress': 100, - 'minDisk': 10, - 'minRam': 128, - "links": [{ - "rel": "self", - "href": "http://localhost/v1.1/fake/images/123", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/123", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": alternate % (utils.generate_glance_url(), 123), - }], - }, - { - 'id': '124', - 'name': 'queued snapshot', - 'metadata': { - u'instance_ref': server_href, - u'user_id': u'fake', - }, - 'updated': NOW_API_FORMAT, - 'created': NOW_API_FORMAT, - 'status': 'SAVING', - 'progress': 25, - 'minDisk': 0, - 'minRam': 0, - 'server': { - 'id': server_uuid, - "links": [{ - "rel": "self", - "href": server_href, - }, - { - "rel": "bookmark", - "href": server_bookmark, - }], - }, - "links": [{ - "rel": "self", - "href": "http://localhost/v1.1/fake/images/124", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/124", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": alternate % (utils.generate_glance_url(), 124), - }], - }, - { - 'id': '125', - 'name': 'saving snapshot', - 'metadata': { - u'instance_ref': server_href, - u'user_id': u'fake', - }, - 'updated': NOW_API_FORMAT, - 'created': NOW_API_FORMAT, - 'status': 'SAVING', - 'progress': 50, - 'minDisk': 0, - 'minRam': 0, - 'server': { - 'id': server_uuid, - "links": [{ - "rel": "self", - "href": server_href, - }, - { - "rel": "bookmark", - "href": server_bookmark, - }], - }, - "links": [{ - "rel": "self", - "href": "http://localhost/v1.1/fake/images/125", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/125", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": "%s/fake/images/125" % utils.generate_glance_url() - }], - }, - { - 'id': '126', - 'name': 'active snapshot', - 'metadata': { - u'instance_ref': server_href, - u'user_id': u'fake', - }, - 'updated': NOW_API_FORMAT, - 'created': NOW_API_FORMAT, - 'status': 'ACTIVE', - 'progress': 100, - 'minDisk': 0, - 'minRam': 0, - 'server': { - 'id': server_uuid, - "links": [{ - "rel": "self", - "href": server_href, - }, - { - "rel": "bookmark", - "href": server_bookmark, - }], - }, - "links": [{ - "rel": "self", - "href": "http://localhost/v1.1/fake/images/126", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/126", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": "%s/fake/images/126" % utils.generate_glance_url() - }], - }, - { - 'id': '127', - 'name': 'killed snapshot', - 'metadata': { - u'instance_ref': server_href, - u'user_id': u'fake', - }, - 'updated': NOW_API_FORMAT, - 'created': NOW_API_FORMAT, - 'status': 'ERROR', - 'progress': 0, - 'minDisk': 0, - 'minRam': 0, - 'server': { - 'id': server_uuid, - "links": [{ - "rel": "self", - "href": server_href, - }, - { - "rel": "bookmark", - "href": server_bookmark, - }], - }, - "links": [{ - "rel": "self", - "href": "http://localhost/v1.1/fake/images/127", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/127", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": "%s/fake/images/127" % utils.generate_glance_url() - }], - }, - { - 'id': '128', - 'name': 'deleted snapshot', - 'metadata': { - u'instance_ref': server_href, - u'user_id': u'fake', - }, - 'updated': NOW_API_FORMAT, - 'created': NOW_API_FORMAT, - 'status': 'DELETED', - 'progress': 0, - 'minDisk': 0, - 'minRam': 0, - 'server': { - 'id': server_uuid, - "links": [{ - "rel": "self", - "href": server_href, - }, - { - "rel": "bookmark", - "href": server_bookmark, - }], - }, - "links": [{ - "rel": "self", - "href": "http://localhost/v1.1/fake/images/128", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/128", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": "%s/fake/images/128" % utils.generate_glance_url() - }], - }, - { - 'id': '129', - 'name': 'pending_delete snapshot', - 'metadata': { - u'instance_ref': server_href, - u'user_id': u'fake', - }, - 'updated': NOW_API_FORMAT, - 'created': NOW_API_FORMAT, - 'status': 'DELETED', - 'progress': 0, - 'minDisk': 0, - 'minRam': 0, - 'server': { - 'id': server_uuid, - "links": [{ - "rel": "self", - "href": server_href, - }, - { - "rel": "bookmark", - "href": server_bookmark, - }], - }, - "links": [{ - "rel": "self", - "href": "http://localhost/v1.1/fake/images/129", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/129", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": "%s/fake/images/129" % utils.generate_glance_url() - }], - }, - { - 'id': '130', - 'name': None, - 'metadata': {}, - 'updated': NOW_API_FORMAT, - 'created': NOW_API_FORMAT, - 'status': 'ACTIVE', - 'progress': 100, - 'minDisk': 0, - 'minRam': 0, - "links": [{ - "rel": "self", - "href": "http://localhost/v1.1/fake/images/130", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/130", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": "%s/fake/images/130" % utils.generate_glance_url() - }], - }, - ] - - self.assertDictListMatch(expected, response_list) - - def test_get_image_details_with_limit(self): - request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail?limit=2') - response = self.controller.detail(request) - response_list = response["images"] - response_links = response["images_links"] - - server_uuid = "aa640691-d1a7-4a67-9d3c-d35ee6b3cc74" - server_href = "http://localhost/v1.1/servers/" + server_uuid - server_bookmark = "http://localhost/servers/" + server_uuid - alternate = "%s/fake/images/%s" - - expected = [{ - 'id': '123', - 'name': 'public image', - 'metadata': {'key1': 'value1'}, - 'updated': NOW_API_FORMAT, - 'created': NOW_API_FORMAT, - 'status': 'ACTIVE', - 'minDisk': 10, - 'progress': 100, - 'minRam': 128, - "links": [{ - "rel": "self", - "href": "http://localhost/v1.1/fake/images/123", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/123", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": alternate % (utils.generate_glance_url(), 123), - }], - }, - { - 'id': '124', - 'name': 'queued snapshot', - 'metadata': { - u'instance_ref': server_href, - u'user_id': u'fake', - }, - 'updated': NOW_API_FORMAT, - 'created': NOW_API_FORMAT, - 'status': 'SAVING', - 'minDisk': 0, - 'progress': 25, - 'minRam': 0, - 'server': { - 'id': server_uuid, - "links": [{ - "rel": "self", - "href": server_href, - }, - { - "rel": "bookmark", - "href": server_bookmark, - }], - }, - "links": [{ - "rel": "self", - "href": "http://localhost/v1.1/fake/images/124", - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/images/124", - }, - { - "rel": "alternate", - "type": "application/vnd.openstack.image", - "href": alternate % (utils.generate_glance_url(), 124), - }], - }] - - self.assertDictListMatch(expected, response_list) - - href_parts = urlparse.urlparse(response_links[0]['href']) - self.assertEqual('/v1.1/fake/images', href_parts.path) - params = urlparse.parse_qs(href_parts.query) - - self.assertDictMatch({'limit': ['2'], 'marker': ['124']}, params) - - def test_image_filter_with_name(self): - image_service = self.mox.CreateMockAnything() - filters = {'name': 'testname'} - request = fakes.HTTPRequest.blank('/v1.1/images?name=testname') - context = request.environ['nova.context'] - image_service.index(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.index(request) - self.mox.VerifyAll() - - def test_image_filter_with_min_ram(self): - image_service = self.mox.CreateMockAnything() - filters = {'min_ram': '0'} - request = fakes.HTTPRequest.blank('/v1.1/images?minRam=0') - context = request.environ['nova.context'] - image_service.index(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.index(request) - self.mox.VerifyAll() - - def test_image_filter_with_min_disk(self): - image_service = self.mox.CreateMockAnything() - filters = {'min_disk': '7'} - request = fakes.HTTPRequest.blank('/v1.1/images?minDisk=7') - context = request.environ['nova.context'] - image_service.index(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.index(request) - self.mox.VerifyAll() - - def test_image_filter_with_status(self): - image_service = self.mox.CreateMockAnything() - filters = {'status': 'ACTIVE'} - request = fakes.HTTPRequest.blank('/v1.1/images?status=ACTIVE') - context = request.environ['nova.context'] - image_service.index(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.index(request) - self.mox.VerifyAll() - - def test_image_filter_with_property(self): - image_service = self.mox.CreateMockAnything() - filters = {'property-test': '3'} - request = fakes.HTTPRequest.blank('/v1.1/images?property-test=3') - context = request.environ['nova.context'] - image_service.index(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.index(request) - self.mox.VerifyAll() - - def test_image_filter_server(self): - image_service = self.mox.CreateMockAnything() - uuid = 'fa95aaf5-ab3b-4cd8-88c0-2be7dd051aaf' - ref = 'http://localhost:8774/servers/' + uuid - filters = {'property-instance_ref': ref} - request = fakes.HTTPRequest.blank('/v1.1/images?server=' + ref) - context = request.environ['nova.context'] - image_service.index(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.index(request) - self.mox.VerifyAll() - - def test_image_filter_changes_since(self): - image_service = self.mox.CreateMockAnything() - filters = {'changes-since': '2011-01-24T17:08Z'} - request = fakes.HTTPRequest.blank('/v1.1/images?changes-since=' - '2011-01-24T17:08Z') - context = request.environ['nova.context'] - image_service.index(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.index(request) - self.mox.VerifyAll() - - def test_image_filter_with_type(self): - image_service = self.mox.CreateMockAnything() - filters = {'property-image_type': 'BASE'} - request = fakes.HTTPRequest.blank('/v1.1/images?type=BASE') - context = request.environ['nova.context'] - image_service.index(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.index(request) - self.mox.VerifyAll() - - def test_image_filter_not_supported(self): - image_service = self.mox.CreateMockAnything() - filters = {'status': 'ACTIVE'} - request = fakes.HTTPRequest.blank('/v1.1/images?status=ACTIVE&' - 'UNSUPPORTEDFILTER=testname') - context = request.environ['nova.context'] - image_service.detail(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.detail(request) - self.mox.VerifyAll() - - def test_image_no_filters(self): - image_service = self.mox.CreateMockAnything() - filters = {} - request = fakes.HTTPRequest.blank('/v1.1/images') - context = request.environ['nova.context'] - image_service.index(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.index(request) - self.mox.VerifyAll() - - def test_image_detail_filter_with_name(self): - image_service = self.mox.CreateMockAnything() - filters = {'name': 'testname'} - request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail' - '?name=testname') - context = request.environ['nova.context'] - image_service.detail(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.detail(request) - self.mox.VerifyAll() - - def test_image_detail_filter_with_status(self): - image_service = self.mox.CreateMockAnything() - filters = {'status': 'ACTIVE'} - request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail' - '?status=ACTIVE') - context = request.environ['nova.context'] - image_service.detail(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.detail(request) - self.mox.VerifyAll() - - def test_image_detail_filter_with_property(self): - image_service = self.mox.CreateMockAnything() - filters = {'property-test': '3'} - request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail' - '?property-test=3') - context = request.environ['nova.context'] - image_service.detail(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.detail(request) - self.mox.VerifyAll() - - def test_image_detail_filter_server(self): - image_service = self.mox.CreateMockAnything() - uuid = 'fa95aaf5-ab3b-4cd8-88c0-2be7dd051aaf' - ref = 'http://localhost:8774/servers/' + uuid - url = '/v1.1/fake/images/detail?server=' + ref - filters = {'property-instance_ref': ref} - request = fakes.HTTPRequest.blank(url) - context = request.environ['nova.context'] - image_service.index(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.index(request) - self.mox.VerifyAll() - - def test_image_detail_filter_changes_since(self): - image_service = self.mox.CreateMockAnything() - filters = {'changes-since': '2011-01-24T17:08Z'} - request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail' - '?changes-since=2011-01-24T17:08Z') - context = request.environ['nova.context'] - image_service.index(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.index(request) - self.mox.VerifyAll() - - def test_image_detail_filter_with_type(self): - image_service = self.mox.CreateMockAnything() - filters = {'property-image_type': 'BASE'} - request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail?type=BASE') - context = request.environ['nova.context'] - image_service.index(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.index(request) - self.mox.VerifyAll() - - def test_image_detail_filter_not_supported(self): - image_service = self.mox.CreateMockAnything() - filters = {'status': 'ACTIVE'} - request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail?status=' - 'ACTIVE&UNSUPPORTEDFILTER=testname') - context = request.environ['nova.context'] - image_service.detail(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.detail(request) - self.mox.VerifyAll() - - def test_image_detail_no_filters(self): - image_service = self.mox.CreateMockAnything() - filters = {} - request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail') - context = request.environ['nova.context'] - image_service.detail(context, filters=filters).AndReturn([]) - self.mox.ReplayAll() - controller = images.Controller(image_service=image_service) - controller.detail(request) - self.mox.VerifyAll() - - def test_generate_alternate_link(self): - view = images_view.ViewBuilder() - request = fakes.HTTPRequest.blank('/v1.1/fake/images/1') - generated_url = view._get_alternate_link(request, 1) - actual_url = "%s/fake/images/1" % utils.generate_glance_url() - self.assertEqual(generated_url, actual_url) - - def test_delete_image(self): - request = fakes.HTTPRequest.blank('/v1.1/fake/images/124') - request.method = 'DELETE' - response = self.controller.delete(request, '124') - self.assertEqual(response.status_int, 204) - - def test_delete_image_not_found(self): - request = fakes.HTTPRequest.blank('/v1.1/fake/images/300') - request.method = 'DELETE' - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.delete, request, '300') - - -class ImageXMLSerializationTest(test.TestCase): - - TIMESTAMP = "2010-10-11T10:30:22Z" - SERVER_UUID = 'aa640691-d1a7-4a67-9d3c-d35ee6b3cc74' - SERVER_HREF = 'http://localhost/v1.1/servers/' + SERVER_UUID - SERVER_BOOKMARK = 'http://localhost/servers/' + SERVER_UUID - IMAGE_HREF = 'http://localhost/v1.1/fake/images/%s' - IMAGE_NEXT = 'http://localhost/v1.1/fake/images?limit=%s&marker=%s' - IMAGE_BOOKMARK = 'http://localhost/fake/images/%s' - - def test_xml_declaration(self): - serializer = images.ImageXMLSerializer() - - fixture = { - 'image': { - 'id': 1, - 'name': 'Image1', - 'created': self.TIMESTAMP, - 'updated': self.TIMESTAMP, - 'status': 'ACTIVE', - 'progress': 80, - 'server': { - 'id': self.SERVER_UUID, - 'links': [ - { - 'href': self.SERVER_HREF, - 'rel': 'self', - }, - { - 'href': self.SERVER_BOOKMARK, - 'rel': 'bookmark', - }, - ], - }, - 'metadata': { - 'key1': 'value1', - }, - 'links': [ - { - 'href': self.IMAGE_HREF % 1, - 'rel': 'self', - }, - { - 'href': self.IMAGE_BOOKMARK % 1, - 'rel': 'bookmark', - }, - ], - }, - } - - output = serializer.serialize(fixture, 'show') - has_dec = output.startswith("") - self.assertTrue(has_dec) - - def test_show(self): - serializer = images.ImageXMLSerializer() - - fixture = { - 'image': { - 'id': 1, - 'name': 'Image1', - 'created': self.TIMESTAMP, - 'updated': self.TIMESTAMP, - 'status': 'ACTIVE', - 'progress': 80, - 'minRam': 10, - 'minDisk': 100, - 'server': { - 'id': self.SERVER_UUID, - 'links': [ - { - 'href': self.SERVER_HREF, - 'rel': 'self', - }, - { - 'href': self.SERVER_BOOKMARK, - 'rel': 'bookmark', - }, - ], - }, - 'metadata': { - 'key1': 'value1', - }, - 'links': [ - { - 'href': self.IMAGE_HREF % 1, - 'rel': 'self', - }, - { - 'href': self.IMAGE_BOOKMARK % 1, - 'rel': 'bookmark', - }, - ], - }, - } - - output = serializer.serialize(fixture, 'show') - root = etree.XML(output) - xmlutil.validate_schema(root, 'image') - image_dict = fixture['image'] - - for key in ['name', 'id', 'updated', 'created', 'status', 'progress']: - self.assertEqual(root.get(key), str(image_dict[key])) - - link_nodes = root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(image_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - metadata_root = root.find('{0}metadata'.format(NS)) - metadata_elems = metadata_root.findall('{0}meta'.format(NS)) - self.assertEqual(len(metadata_elems), 1) - for i, metadata_elem in enumerate(metadata_elems): - (meta_key, meta_value) = image_dict['metadata'].items()[i] - self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) - self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) - - server_root = root.find('{0}server'.format(NS)) - self.assertEqual(server_root.get('id'), image_dict['server']['id']) - link_nodes = server_root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(image_dict['server']['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - def test_show_zero_metadata(self): - serializer = images.ImageXMLSerializer() - - fixture = { - 'image': { - 'id': 1, - 'name': 'Image1', - 'created': self.TIMESTAMP, - 'updated': self.TIMESTAMP, - 'status': 'ACTIVE', - 'server': { - 'id': self.SERVER_UUID, - 'links': [ - { - 'href': self.SERVER_HREF, - 'rel': 'self', - }, - { - 'href': self.SERVER_BOOKMARK, - 'rel': 'bookmark', - }, - ], - }, - 'metadata': {}, - 'links': [ - { - 'href': self.IMAGE_HREF % 1, - 'rel': 'self', - }, - { - 'href': self.IMAGE_BOOKMARK % 1, - 'rel': 'bookmark', - }, - ], - }, - } - - output = serializer.serialize(fixture, 'show') - root = etree.XML(output) - xmlutil.validate_schema(root, 'image') - image_dict = fixture['image'] - - for key in ['name', 'id', 'updated', 'created', 'status']: - self.assertEqual(root.get(key), str(image_dict[key])) - - link_nodes = root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(image_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - meta_nodes = root.findall('{0}meta'.format(ATOMNS)) - self.assertEqual(len(meta_nodes), 0) - - server_root = root.find('{0}server'.format(NS)) - self.assertEqual(server_root.get('id'), image_dict['server']['id']) - link_nodes = server_root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(image_dict['server']['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - def test_show_image_no_metadata_key(self): - serializer = images.ImageXMLSerializer() - - fixture = { - 'image': { - 'id': 1, - 'name': 'Image1', - 'created': self.TIMESTAMP, - 'updated': self.TIMESTAMP, - 'status': 'ACTIVE', - 'server': { - 'id': self.SERVER_UUID, - 'links': [ - { - 'href': self.SERVER_HREF, - 'rel': 'self', - }, - { - 'href': self.SERVER_BOOKMARK, - 'rel': 'bookmark', - }, - ], - }, - 'links': [ - { - 'href': self.IMAGE_HREF % 1, - 'rel': 'self', - }, - { - 'href': self.IMAGE_BOOKMARK % 1, - 'rel': 'bookmark', - }, - ], - }, - } - - output = serializer.serialize(fixture, 'show') - root = etree.XML(output) - xmlutil.validate_schema(root, 'image') - image_dict = fixture['image'] - - for key in ['name', 'id', 'updated', 'created', 'status']: - self.assertEqual(root.get(key), str(image_dict[key])) - - link_nodes = root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(image_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - meta_nodes = root.findall('{0}meta'.format(ATOMNS)) - self.assertEqual(len(meta_nodes), 0) - - server_root = root.find('{0}server'.format(NS)) - self.assertEqual(server_root.get('id'), image_dict['server']['id']) - link_nodes = server_root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(image_dict['server']['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - def test_show_no_server(self): - serializer = images.ImageXMLSerializer() - - fixture = { - 'image': { - 'id': 1, - 'name': 'Image1', - 'created': self.TIMESTAMP, - 'updated': self.TIMESTAMP, - 'status': 'ACTIVE', - 'metadata': { - 'key1': 'value1', - }, - 'links': [ - { - 'href': self.IMAGE_HREF % 1, - 'rel': 'self', - }, - { - 'href': self.IMAGE_BOOKMARK % 1, - 'rel': 'bookmark', - }, - ], - }, - } - - output = serializer.serialize(fixture, 'show') - root = etree.XML(output) - xmlutil.validate_schema(root, 'image') - image_dict = fixture['image'] - - for key in ['name', 'id', 'updated', 'created', 'status']: - self.assertEqual(root.get(key), str(image_dict[key])) - - link_nodes = root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(image_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - metadata_root = root.find('{0}metadata'.format(NS)) - metadata_elems = metadata_root.findall('{0}meta'.format(NS)) - self.assertEqual(len(metadata_elems), 1) - for i, metadata_elem in enumerate(metadata_elems): - (meta_key, meta_value) = image_dict['metadata'].items()[i] - self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) - self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) - - server_root = root.find('{0}server'.format(NS)) - self.assertEqual(server_root, None) - - def test_show_with_min_ram(self): - serializer = images.ImageXMLSerializer() - - fixture = { - 'image': { - 'id': 1, - 'name': 'Image1', - 'created': self.TIMESTAMP, - 'updated': self.TIMESTAMP, - 'status': 'ACTIVE', - 'progress': 80, - 'minRam': 256, - 'server': { - 'id': self.SERVER_UUID, - 'links': [ - { - 'href': self.SERVER_HREF, - 'rel': 'self', - }, - { - 'href': self.SERVER_BOOKMARK, - 'rel': 'bookmark', - }, - ], - }, - 'metadata': { - 'key1': 'value1', - }, - 'links': [ - { - 'href': self.IMAGE_HREF % 1, - 'rel': 'self', - }, - { - 'href': self.IMAGE_BOOKMARK % 1, - 'rel': 'bookmark', - }, - ], - }, - } - - output = serializer.serialize(fixture, 'show') - root = etree.XML(output) - xmlutil.validate_schema(root, 'image') - image_dict = fixture['image'] - - for key in ['name', 'id', 'updated', 'created', 'status', 'progress', - 'minRam']: - self.assertEqual(root.get(key), str(image_dict[key])) - - link_nodes = root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(image_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - metadata_root = root.find('{0}metadata'.format(NS)) - metadata_elems = metadata_root.findall('{0}meta'.format(NS)) - self.assertEqual(len(metadata_elems), 1) - for i, metadata_elem in enumerate(metadata_elems): - (meta_key, meta_value) = image_dict['metadata'].items()[i] - self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) - self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) - - server_root = root.find('{0}server'.format(NS)) - self.assertEqual(server_root.get('id'), image_dict['server']['id']) - link_nodes = server_root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(image_dict['server']['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - def test_show_with_min_disk(self): - serializer = images.ImageXMLSerializer() - - fixture = { - 'image': { - 'id': 1, - 'name': 'Image1', - 'created': self.TIMESTAMP, - 'updated': self.TIMESTAMP, - 'status': 'ACTIVE', - 'progress': 80, - 'minDisk': 5, - 'server': { - 'id': self.SERVER_UUID, - 'links': [ - { - 'href': self.SERVER_HREF, - 'rel': 'self', - }, - { - 'href': self.SERVER_BOOKMARK, - 'rel': 'bookmark', - }, - ], - }, - 'metadata': { - 'key1': 'value1', - }, - 'links': [ - { - 'href': self.IMAGE_HREF % 1, - 'rel': 'self', - }, - { - 'href': self.IMAGE_BOOKMARK % 1, - 'rel': 'bookmark', - }, - ], - }, - } - - output = serializer.serialize(fixture, 'show') - root = etree.XML(output) - xmlutil.validate_schema(root, 'image') - image_dict = fixture['image'] - - for key in ['name', 'id', 'updated', 'created', 'status', 'progress', - 'minDisk']: - self.assertEqual(root.get(key), str(image_dict[key])) - - link_nodes = root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(image_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - metadata_root = root.find('{0}metadata'.format(NS)) - metadata_elems = metadata_root.findall('{0}meta'.format(NS)) - self.assertEqual(len(metadata_elems), 1) - for i, metadata_elem in enumerate(metadata_elems): - (meta_key, meta_value) = image_dict['metadata'].items()[i] - self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) - self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) - - server_root = root.find('{0}server'.format(NS)) - self.assertEqual(server_root.get('id'), image_dict['server']['id']) - link_nodes = server_root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(image_dict['server']['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - def test_index(self): - serializer = images.ImageXMLSerializer() - - fixture = { - 'images': [ - { - 'id': 1, - 'name': 'Image1', - 'links': [ - { - 'href': self.IMAGE_HREF % 1, - 'rel': 'self', - }, - { - 'href': self.IMAGE_BOOKMARK % 1, - 'rel': 'bookmark', - }, - ], - }, - { - 'id': 2, - 'name': 'Image2', - 'links': [ - { - 'href': self.IMAGE_HREF % 2, - 'rel': 'self', - }, - { - 'href': self.IMAGE_BOOKMARK % 2, - 'rel': 'bookmark', - }, - ], - }, - ] - } - - output = serializer.serialize(fixture, 'index') - root = etree.XML(output) - xmlutil.validate_schema(root, 'images_index') - image_elems = root.findall('{0}image'.format(NS)) - self.assertEqual(len(image_elems), 2) - for i, image_elem in enumerate(image_elems): - image_dict = fixture['images'][i] - - for key in ['name', 'id']: - self.assertEqual(image_elem.get(key), str(image_dict[key])) - - link_nodes = image_elem.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(image_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - def test_index_with_links(self): - serializer = images.ImageXMLSerializer() - - fixture = { - 'images': [ - { - 'id': 1, - 'name': 'Image1', - 'links': [ - { - 'href': self.IMAGE_HREF % 1, - 'rel': 'self', - }, - { - 'href': self.IMAGE_BOOKMARK % 1, - 'rel': 'bookmark', - }, - ], - }, - { - 'id': 2, - 'name': 'Image2', - 'links': [ - { - 'href': self.IMAGE_HREF % 2, - 'rel': 'self', - }, - { - 'href': self.IMAGE_BOOKMARK % 2, - 'rel': 'bookmark', - }, - ], - }, - ], - 'images_links': [ - { - 'rel': 'next', - 'href': self.IMAGE_NEXT % (2, 2), - } - ], - } - - output = serializer.serialize(fixture, 'index') - root = etree.XML(output) - xmlutil.validate_schema(root, 'images_index') - image_elems = root.findall('{0}image'.format(NS)) - self.assertEqual(len(image_elems), 2) - for i, image_elem in enumerate(image_elems): - image_dict = fixture['images'][i] - - for key in ['name', 'id']: - self.assertEqual(image_elem.get(key), str(image_dict[key])) - - link_nodes = image_elem.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(image_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - # Check images_links - images_links = root.findall('{0}link'.format(ATOMNS)) - for i, link in enumerate(fixture['images_links']): - for key, value in link.items(): - self.assertEqual(images_links[i].get(key), value) - - def test_index_zero_images(self): - serializer = images.ImageXMLSerializer() - - fixtures = { - 'images': [], - } - - output = serializer.serialize(fixtures, 'index') - root = etree.XML(output) - xmlutil.validate_schema(root, 'images_index') - image_elems = root.findall('{0}image'.format(NS)) - self.assertEqual(len(image_elems), 0) - - def test_detail(self): - serializer = images.ImageXMLSerializer() - - fixture = { - 'images': [ - { - 'id': 1, - 'name': 'Image1', - 'created': self.TIMESTAMP, - 'updated': self.TIMESTAMP, - 'status': 'ACTIVE', - 'server': { - 'id': self.SERVER_UUID, - 'links': [ - { - 'href': self.SERVER_HREF, - 'rel': 'self', - }, - { - 'href': self.SERVER_BOOKMARK, - 'rel': 'bookmark', - }, - ], - }, - 'links': [ - { - 'href': self.IMAGE_HREF % 1, - 'rel': 'self', - }, - { - 'href': self.IMAGE_BOOKMARK % 1, - 'rel': 'bookmark', - }, - ], - }, - { - 'id': '2', - 'name': 'Image2', - 'created': self.TIMESTAMP, - 'updated': self.TIMESTAMP, - 'status': 'SAVING', - 'progress': 80, - 'metadata': { - 'key1': 'value1', - }, - 'links': [ - { - 'href': self.IMAGE_HREF % 2, - 'rel': 'self', - }, - { - 'href': self.IMAGE_BOOKMARK % 2, - 'rel': 'bookmark', - }, - ], - }, - ] - } - - output = serializer.serialize(fixture, 'detail') - root = etree.XML(output) - xmlutil.validate_schema(root, 'images') - image_elems = root.findall('{0}image'.format(NS)) - self.assertEqual(len(image_elems), 2) - for i, image_elem in enumerate(image_elems): - image_dict = fixture['images'][i] - - for key in ['name', 'id', 'updated', 'created', 'status']: - self.assertEqual(image_elem.get(key), str(image_dict[key])) - - link_nodes = image_elem.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(image_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) diff --git a/nova/tests/api/openstack/test_limits.py b/nova/tests/api/openstack/test_limits.py deleted file mode 100644 index 96e30f756..000000000 --- a/nova/tests/api/openstack/test_limits.py +++ /dev/null @@ -1,939 +0,0 @@ -# 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. - -""" -Tests dealing with HTTP rate-limiting. -""" - -import httplib -import json -from lxml import etree -import StringIO -import stubout -import time -import unittest -import webob -from xml.dom import minidom - -import nova.context -from nova.api.openstack import limits -from nova.api.openstack import views -from nova.api.openstack import wsgi -from nova.api.openstack import xmlutil -from nova import test - - -TEST_LIMITS = [ - limits.Limit("GET", "/delayed", "^/delayed", 1, limits.PER_MINUTE), - limits.Limit("POST", "*", ".*", 7, limits.PER_MINUTE), - limits.Limit("POST", "/servers", "^/servers", 3, limits.PER_MINUTE), - limits.Limit("PUT", "*", "", 10, limits.PER_MINUTE), - limits.Limit("PUT", "/servers", "^/servers", 5, limits.PER_MINUTE), -] -NS = { - 'atom': 'http://www.w3.org/2005/Atom', - 'ns': 'http://docs.openstack.org/compute/api/v1.1' -} - - -class BaseLimitTestSuite(unittest.TestCase): - """Base test suite which provides relevant stubs and time abstraction.""" - - def setUp(self): - """Run before each test.""" - self.time = 0.0 - self.stubs = stubout.StubOutForTesting() - self.stubs.Set(limits.Limit, "_get_time", self._get_time) - self.absolute_limits = {} - - def stub_get_project_quotas(context, project_id): - return self.absolute_limits - - self.stubs.Set(nova.quota, "get_project_quotas", - stub_get_project_quotas) - - def tearDown(self): - """Run after each test.""" - self.stubs.UnsetAll() - - def _get_time(self): - """Return the "time" according to this test suite.""" - return self.time - - -class LimitsControllerTest(BaseLimitTestSuite): - """ - Tests for `limits.LimitsController` class. - """ - - def setUp(self): - """Run before each test.""" - BaseLimitTestSuite.setUp(self) - self.controller = wsgi.LazySerializationMiddleware( - limits.create_resource()) - self.maxDiff = None - - def _get_index_request(self, accept_header="application/json"): - """Helper to set routing arguments.""" - request = webob.Request.blank("/") - request.accept = accept_header - request.environ["wsgiorg.routing_args"] = (None, { - "action": "index", - "controller": "", - }) - context = nova.context.RequestContext('testuser', 'testproject') - request.environ["nova.context"] = context - return request - - def _populate_limits(self, request): - """Put limit info into a request.""" - _limits = [ - limits.Limit("GET", "*", ".*", 10, 60).display(), - limits.Limit("POST", "*", ".*", 5, 60 * 60).display(), - limits.Limit("GET", "changes-since*", "changes-since", - 5, 60).display(), - ] - request.environ["nova.limits"] = _limits - return request - - def test_empty_index_json(self): - """Test getting empty limit details in JSON.""" - request = self._get_index_request() - response = request.get_response(self.controller) - expected = { - "limits": { - "rate": [], - "absolute": {}, - }, - } - body = json.loads(response.body) - self.assertEqual(expected, body) - - def test_index_json(self): - """Test getting limit details in JSON.""" - request = self._get_index_request() - request = self._populate_limits(request) - self.absolute_limits = { - 'ram': 512, - 'instances': 5, - 'cores': 21, - } - response = request.get_response(self.controller) - expected = { - "limits": { - "rate": [ - { - "regex": ".*", - "uri": "*", - "limit": [ - { - "verb": "GET", - "next-available": "1970-01-01T00:00:00Z", - "unit": "MINUTE", - "value": 10, - "remaining": 10, - }, - { - "verb": "POST", - "next-available": "1970-01-01T00:00:00Z", - "unit": "HOUR", - "value": 5, - "remaining": 5, - }, - ], - }, - { - "regex": "changes-since", - "uri": "changes-since*", - "limit": [ - { - "verb": "GET", - "next-available": "1970-01-01T00:00:00Z", - "unit": "MINUTE", - "value": 5, - "remaining": 5, - }, - ], - }, - - ], - "absolute": { - "maxTotalRAMSize": 512, - "maxTotalInstances": 5, - "maxTotalCores": 21, - }, - }, - } - body = json.loads(response.body) - self.assertEqual(expected, body) - - def _populate_limits_diff_regex(self, request): - """Put limit info into a request.""" - _limits = [ - limits.Limit("GET", "*", ".*", 10, 60).display(), - limits.Limit("GET", "*", "*.*", 10, 60).display(), - ] - request.environ["nova.limits"] = _limits - return request - - def test_index_diff_regex(self): - """Test getting limit details in JSON.""" - request = self._get_index_request() - request = self._populate_limits_diff_regex(request) - response = request.get_response(self.controller) - expected = { - "limits": { - "rate": [ - { - "regex": ".*", - "uri": "*", - "limit": [ - { - "verb": "GET", - "next-available": "1970-01-01T00:00:00Z", - "unit": "MINUTE", - "value": 10, - "remaining": 10, - }, - ], - }, - { - "regex": "*.*", - "uri": "*", - "limit": [ - { - "verb": "GET", - "next-available": "1970-01-01T00:00:00Z", - "unit": "MINUTE", - "value": 10, - "remaining": 10, - }, - ], - }, - - ], - "absolute": {}, - }, - } - body = json.loads(response.body) - self.assertEqual(expected, body) - - def _test_index_absolute_limits_json(self, expected): - request = self._get_index_request() - response = request.get_response(self.controller) - body = json.loads(response.body) - self.assertEqual(expected, body['limits']['absolute']) - - def test_index_ignores_extra_absolute_limits_json(self): - self.absolute_limits = {'unknown_limit': 9001} - self._test_index_absolute_limits_json({}) - - def test_index_absolute_ram_json(self): - self.absolute_limits = {'ram': 1024} - self._test_index_absolute_limits_json({'maxTotalRAMSize': 1024}) - - def test_index_absolute_cores_json(self): - self.absolute_limits = {'cores': 17} - self._test_index_absolute_limits_json({'maxTotalCores': 17}) - - def test_index_absolute_instances_json(self): - self.absolute_limits = {'instances': 19} - self._test_index_absolute_limits_json({'maxTotalInstances': 19}) - - def test_index_absolute_metadata_json(self): - # NOTE: both server metadata and image metadata are overloaded - # into metadata_items - self.absolute_limits = {'metadata_items': 23} - expected = { - 'maxServerMeta': 23, - 'maxImageMeta': 23, - } - self._test_index_absolute_limits_json(expected) - - def test_index_absolute_injected_files(self): - self.absolute_limits = { - 'injected_files': 17, - 'injected_file_content_bytes': 86753, - } - expected = { - 'maxPersonality': 17, - 'maxPersonalitySize': 86753, - } - self._test_index_absolute_limits_json(expected) - - -class TestLimiter(limits.Limiter): - pass - - -class LimitMiddlewareTest(BaseLimitTestSuite): - """ - Tests for the `limits.RateLimitingMiddleware` class. - """ - - @webob.dec.wsgify - def _empty_app(self, request): - """Do-nothing WSGI app.""" - pass - - def setUp(self): - """Prepare middleware for use through fake WSGI app.""" - BaseLimitTestSuite.setUp(self) - _limits = '(GET, *, .*, 1, MINUTE)' - self.app = limits.RateLimitingMiddleware(self._empty_app, _limits, - "%s.TestLimiter" % - self.__class__.__module__) - - def test_limit_class(self): - """Test that middleware selected correct limiter class.""" - assert isinstance(self.app._limiter, TestLimiter) - - def test_good_request(self): - """Test successful GET request through middleware.""" - request = webob.Request.blank("/") - response = request.get_response(self.app) - self.assertEqual(200, response.status_int) - - def test_limited_request_json(self): - """Test a rate-limited (413) GET request through middleware.""" - request = webob.Request.blank("/") - response = request.get_response(self.app) - self.assertEqual(200, response.status_int) - - request = webob.Request.blank("/") - response = request.get_response(self.app) - self.assertEqual(response.status_int, 413) - - body = json.loads(response.body) - expected = "Only 1 GET request(s) can be made to * every minute." - value = body["overLimitFault"]["details"].strip() - self.assertEqual(value, expected) - - def test_limited_request_xml(self): - """Test a rate-limited (413) response as XML""" - request = webob.Request.blank("/") - response = request.get_response(self.app) - self.assertEqual(200, response.status_int) - - request = webob.Request.blank("/") - request.accept = "application/xml" - response = request.get_response(self.app) - self.assertEqual(response.status_int, 413) - - root = minidom.parseString(response.body).childNodes[0] - expected = "Only 1 GET request(s) can be made to * every minute." - - details = root.getElementsByTagName("details") - self.assertEqual(details.length, 1) - - value = details.item(0).firstChild.data.strip() - self.assertEqual(value, expected) - - -class LimitTest(BaseLimitTestSuite): - """ - Tests for the `limits.Limit` class. - """ - - def test_GET_no_delay(self): - """Test a limit handles 1 GET per second.""" - limit = limits.Limit("GET", "*", ".*", 1, 1) - delay = limit("GET", "/anything") - self.assertEqual(None, delay) - self.assertEqual(0, limit.next_request) - self.assertEqual(0, limit.last_request) - - def test_GET_delay(self): - """Test two calls to 1 GET per second limit.""" - limit = limits.Limit("GET", "*", ".*", 1, 1) - delay = limit("GET", "/anything") - self.assertEqual(None, delay) - - delay = limit("GET", "/anything") - self.assertEqual(1, delay) - self.assertEqual(1, limit.next_request) - self.assertEqual(0, limit.last_request) - - self.time += 4 - - delay = limit("GET", "/anything") - self.assertEqual(None, delay) - self.assertEqual(4, limit.next_request) - self.assertEqual(4, limit.last_request) - - -class ParseLimitsTest(BaseLimitTestSuite): - """ - Tests for the default limits parser in the in-memory - `limits.Limiter` class. - """ - - def test_invalid(self): - """Test that parse_limits() handles invalid input correctly.""" - self.assertRaises(ValueError, limits.Limiter.parse_limits, - ';;;;;') - - def test_bad_rule(self): - """Test that parse_limits() handles bad rules correctly.""" - self.assertRaises(ValueError, limits.Limiter.parse_limits, - 'GET, *, .*, 20, minute') - - def test_missing_arg(self): - """Test that parse_limits() handles missing args correctly.""" - self.assertRaises(ValueError, limits.Limiter.parse_limits, - '(GET, *, .*, 20)') - - def test_bad_value(self): - """Test that parse_limits() handles bad values correctly.""" - self.assertRaises(ValueError, limits.Limiter.parse_limits, - '(GET, *, .*, foo, minute)') - - def test_bad_unit(self): - """Test that parse_limits() handles bad units correctly.""" - self.assertRaises(ValueError, limits.Limiter.parse_limits, - '(GET, *, .*, 20, lightyears)') - - def test_multiple_rules(self): - """Test that parse_limits() handles multiple rules correctly.""" - try: - l = limits.Limiter.parse_limits('(get, *, .*, 20, minute);' - '(PUT, /foo*, /foo.*, 10, hour);' - '(POST, /bar*, /bar.*, 5, second);' - '(Say, /derp*, /derp.*, 1, day)') - except ValueError, e: - assert False, str(e) - - # Make sure the number of returned limits are correct - self.assertEqual(len(l), 4) - - # Check all the verbs... - expected = ['GET', 'PUT', 'POST', 'SAY'] - self.assertEqual([t.verb for t in l], expected) - - # ...the URIs... - expected = ['*', '/foo*', '/bar*', '/derp*'] - self.assertEqual([t.uri for t in l], expected) - - # ...the regexes... - expected = ['.*', '/foo.*', '/bar.*', '/derp.*'] - self.assertEqual([t.regex for t in l], expected) - - # ...the values... - expected = [20, 10, 5, 1] - self.assertEqual([t.value for t in l], expected) - - # ...and the units... - expected = [limits.PER_MINUTE, limits.PER_HOUR, - limits.PER_SECOND, limits.PER_DAY] - self.assertEqual([t.unit for t in l], expected) - - -class LimiterTest(BaseLimitTestSuite): - """ - Tests for the in-memory `limits.Limiter` class. - """ - - def setUp(self): - """Run before each test.""" - BaseLimitTestSuite.setUp(self) - userlimits = {'user:user3': ''} - self.limiter = limits.Limiter(TEST_LIMITS, **userlimits) - - def _check(self, num, verb, url, username=None): - """Check and yield results from checks.""" - for x in xrange(num): - yield self.limiter.check_for_delay(verb, url, username)[0] - - def _check_sum(self, num, verb, url, username=None): - """Check and sum results from checks.""" - results = self._check(num, verb, url, username) - return sum(item for item in results if item) - - def test_no_delay_GET(self): - """ - Simple test to ensure no delay on a single call for a limit verb we - didn"t set. - """ - delay = self.limiter.check_for_delay("GET", "/anything") - self.assertEqual(delay, (None, None)) - - def test_no_delay_PUT(self): - """ - Simple test to ensure no delay on a single call for a known limit. - """ - delay = self.limiter.check_for_delay("PUT", "/anything") - self.assertEqual(delay, (None, None)) - - def test_delay_PUT(self): - """ - Ensure the 11th PUT will result in a delay of 6.0 seconds until - the next request will be granced. - """ - expected = [None] * 10 + [6.0] - results = list(self._check(11, "PUT", "/anything")) - - self.assertEqual(expected, results) - - def test_delay_POST(self): - """ - Ensure the 8th POST will result in a delay of 6.0 seconds until - the next request will be granced. - """ - expected = [None] * 7 - results = list(self._check(7, "POST", "/anything")) - self.assertEqual(expected, results) - - expected = 60.0 / 7.0 - results = self._check_sum(1, "POST", "/anything") - self.failUnlessAlmostEqual(expected, results, 8) - - def test_delay_GET(self): - """ - Ensure the 11th GET will result in NO delay. - """ - expected = [None] * 11 - results = list(self._check(11, "GET", "/anything")) - - self.assertEqual(expected, results) - - def test_delay_PUT_servers(self): - """ - Ensure PUT on /servers limits at 5 requests, and PUT elsewhere is still - OK after 5 requests...but then after 11 total requests, PUT limiting - kicks in. - """ - # First 6 requests on PUT /servers - expected = [None] * 5 + [12.0] - results = list(self._check(6, "PUT", "/servers")) - self.assertEqual(expected, results) - - # Next 5 request on PUT /anything - expected = [None] * 4 + [6.0] - results = list(self._check(5, "PUT", "/anything")) - self.assertEqual(expected, results) - - def test_delay_PUT_wait(self): - """ - Ensure after hitting the limit and then waiting for the correct - amount of time, the limit will be lifted. - """ - expected = [None] * 10 + [6.0] - results = list(self._check(11, "PUT", "/anything")) - self.assertEqual(expected, results) - - # Advance time - self.time += 6.0 - - expected = [None, 6.0] - results = list(self._check(2, "PUT", "/anything")) - self.assertEqual(expected, results) - - def test_multiple_delays(self): - """ - Ensure multiple requests still get a delay. - """ - expected = [None] * 10 + [6.0] * 10 - results = list(self._check(20, "PUT", "/anything")) - self.assertEqual(expected, results) - - self.time += 1.0 - - expected = [5.0] * 10 - results = list(self._check(10, "PUT", "/anything")) - self.assertEqual(expected, results) - - def test_user_limit(self): - """ - Test user-specific limits. - """ - self.assertEqual(self.limiter.levels['user3'], []) - - def test_multiple_users(self): - """ - Tests involving multiple users. - """ - # User1 - expected = [None] * 10 + [6.0] * 10 - results = list(self._check(20, "PUT", "/anything", "user1")) - self.assertEqual(expected, results) - - # User2 - expected = [None] * 10 + [6.0] * 5 - results = list(self._check(15, "PUT", "/anything", "user2")) - self.assertEqual(expected, results) - - # User3 - expected = [None] * 20 - results = list(self._check(20, "PUT", "/anything", "user3")) - self.assertEqual(expected, results) - - self.time += 1.0 - - # User1 again - expected = [5.0] * 10 - results = list(self._check(10, "PUT", "/anything", "user1")) - self.assertEqual(expected, results) - - self.time += 1.0 - - # User1 again - expected = [4.0] * 5 - results = list(self._check(5, "PUT", "/anything", "user2")) - self.assertEqual(expected, results) - - -class WsgiLimiterTest(BaseLimitTestSuite): - """ - Tests for `limits.WsgiLimiter` class. - """ - - def setUp(self): - """Run before each test.""" - BaseLimitTestSuite.setUp(self) - self.app = limits.WsgiLimiter(TEST_LIMITS) - - def _request_data(self, verb, path): - """Get data decribing a limit request verb/path.""" - return json.dumps({"verb": verb, "path": path}) - - def _request(self, verb, url, username=None): - """Make sure that POSTing to the given url causes the given username - to perform the given action. Make the internal rate limiter return - delay and make sure that the WSGI app returns the correct response. - """ - if username: - request = webob.Request.blank("/%s" % username) - else: - request = webob.Request.blank("/") - - request.method = "POST" - request.body = self._request_data(verb, url) - response = request.get_response(self.app) - - if "X-Wait-Seconds" in response.headers: - self.assertEqual(response.status_int, 403) - return response.headers["X-Wait-Seconds"] - - self.assertEqual(response.status_int, 204) - - def test_invalid_methods(self): - """Only POSTs should work.""" - requests = [] - for method in ["GET", "PUT", "DELETE", "HEAD", "OPTIONS"]: - request = webob.Request.blank("/", method=method) - response = request.get_response(self.app) - self.assertEqual(response.status_int, 405) - - def test_good_url(self): - delay = self._request("GET", "/something") - self.assertEqual(delay, None) - - def test_escaping(self): - delay = self._request("GET", "/something/jump%20up") - self.assertEqual(delay, None) - - def test_response_to_delays(self): - delay = self._request("GET", "/delayed") - self.assertEqual(delay, None) - - delay = self._request("GET", "/delayed") - self.assertEqual(delay, '60.00') - - def test_response_to_delays_usernames(self): - delay = self._request("GET", "/delayed", "user1") - self.assertEqual(delay, None) - - delay = self._request("GET", "/delayed", "user2") - self.assertEqual(delay, None) - - delay = self._request("GET", "/delayed", "user1") - self.assertEqual(delay, '60.00') - - delay = self._request("GET", "/delayed", "user2") - self.assertEqual(delay, '60.00') - - -class FakeHttplibSocket(object): - """ - Fake `httplib.HTTPResponse` replacement. - """ - - def __init__(self, response_string): - """Initialize new `FakeHttplibSocket`.""" - self._buffer = StringIO.StringIO(response_string) - - def makefile(self, _mode, _other): - """Returns the socket's internal buffer.""" - return self._buffer - - -class FakeHttplibConnection(object): - """ - Fake `httplib.HTTPConnection`. - """ - - def __init__(self, app, host): - """ - Initialize `FakeHttplibConnection`. - """ - self.app = app - self.host = host - - def request(self, method, path, body="", headers=None): - """ - Requests made via this connection actually get translated and routed - into our WSGI app, we then wait for the response and turn it back into - an `httplib.HTTPResponse`. - """ - if not headers: - headers = {} - - req = webob.Request.blank(path) - req.method = method - req.headers = headers - req.host = self.host - req.body = body - - resp = str(req.get_response(self.app)) - resp = "HTTP/1.0 %s" % resp - sock = FakeHttplibSocket(resp) - self.http_response = httplib.HTTPResponse(sock) - self.http_response.begin() - - def getresponse(self): - """Return our generated response from the request.""" - return self.http_response - - -def wire_HTTPConnection_to_WSGI(host, app): - """Monkeypatches HTTPConnection so that if you try to connect to host, you - are instead routed straight to the given WSGI app. - - After calling this method, when any code calls - - httplib.HTTPConnection(host) - - the connection object will be a fake. Its requests will be sent directly - to the given WSGI app rather than through a socket. - - Code connecting to hosts other than host will not be affected. - - This method may be called multiple times to map different hosts to - different apps. - """ - class HTTPConnectionDecorator(object): - """Wraps the real HTTPConnection class so that when you instantiate - the class you might instead get a fake instance.""" - - def __init__(self, wrapped): - self.wrapped = wrapped - - def __call__(self, connection_host, *args, **kwargs): - if connection_host == host: - return FakeHttplibConnection(app, host) - else: - return self.wrapped(connection_host, *args, **kwargs) - - httplib.HTTPConnection = HTTPConnectionDecorator(httplib.HTTPConnection) - - -class WsgiLimiterProxyTest(BaseLimitTestSuite): - """ - Tests for the `limits.WsgiLimiterProxy` class. - """ - - def setUp(self): - """ - Do some nifty HTTP/WSGI magic which allows for WSGI to be called - directly by something like the `httplib` library. - """ - BaseLimitTestSuite.setUp(self) - self.app = limits.WsgiLimiter(TEST_LIMITS) - wire_HTTPConnection_to_WSGI("169.254.0.1:80", self.app) - self.proxy = limits.WsgiLimiterProxy("169.254.0.1:80") - - def test_200(self): - """Successful request test.""" - delay = self.proxy.check_for_delay("GET", "/anything") - self.assertEqual(delay, (None, None)) - - def test_403(self): - """Forbidden request test.""" - delay = self.proxy.check_for_delay("GET", "/delayed") - self.assertEqual(delay, (None, None)) - - delay, error = self.proxy.check_for_delay("GET", "/delayed") - error = error.strip() - - expected = ("60.00", "403 Forbidden\n\nOnly 1 GET request(s) can be "\ - "made to /delayed every minute.") - - self.assertEqual((delay, error), expected) - - -class LimitsViewBuilderTest(test.TestCase): - - def setUp(self): - self.view_builder = views.limits.ViewBuilder() - self.rate_limits = [{"URI": "*", - "regex": ".*", - "value": 10, - "verb": "POST", - "remaining": 2, - "unit": "MINUTE", - "resetTime": 1311272226}, - {"URI": "*/servers", - "regex": "^/servers", - "value": 50, - "verb": "POST", - "remaining": 10, - "unit": "DAY", - "resetTime": 1311272226}] - self.absolute_limits = {"metadata_items": 1, - "injected_files": 5, - "injected_file_content_bytes": 5} - - def tearDown(self): - pass - - def test_build_limits(self): - expected_limits = {"limits": { - "rate": [{ - "uri": "*", - "regex": ".*", - "limit": [{"value": 10, - "verb": "POST", - "remaining": 2, - "unit": "MINUTE", - "next-available": "2011-07-21T18:17:06Z"}]}, - {"uri": "*/servers", - "regex": "^/servers", - "limit": [{"value": 50, - "verb": "POST", - "remaining": 10, - "unit": "DAY", - "next-available": "2011-07-21T18:17:06Z"}]}], - "absolute": {"maxServerMeta": 1, - "maxImageMeta": 1, - "maxPersonality": 5, - "maxPersonalitySize": 5}}} - - output = self.view_builder.build(self.rate_limits, - self.absolute_limits) - self.assertDictMatch(output, expected_limits) - - def test_build_limits_empty_limits(self): - expected_limits = {"limits": {"rate": [], - "absolute": {}}} - - abs_limits = {} - rate_limits = [] - output = self.view_builder.build(rate_limits, abs_limits) - self.assertDictMatch(output, expected_limits) - - -class LimitsXMLSerializationTest(test.TestCase): - - def setUp(self): - self.maxDiff = None - - def tearDown(self): - pass - - def test_xml_declaration(self): - serializer = limits.LimitsXMLSerializer() - - fixture = {"limits": { - "rate": [], - "absolute": {}}} - - output = serializer.serialize(fixture, 'index') - print output - has_dec = output.startswith("") - self.assertTrue(has_dec) - - def test_index(self): - serializer = limits.LimitsXMLSerializer() - fixture = { - "limits": { - "rate": [{ - "uri": "*", - "regex": ".*", - "limit": [{ - "value": 10, - "verb": "POST", - "remaining": 2, - "unit": "MINUTE", - "next-available": "2011-12-15T22:42:45Z"}]}, - {"uri": "*/servers", - "regex": "^/servers", - "limit": [{ - "value": 50, - "verb": "POST", - "remaining": 10, - "unit": "DAY", - "next-available": "2011-12-15T22:42:45Z"}]}], - "absolute": {"maxServerMeta": 1, - "maxImageMeta": 1, - "maxPersonality": 5, - "maxPersonalitySize": 10240}}} - - output = serializer.serialize(fixture, 'index') - print output - root = etree.XML(output) - xmlutil.validate_schema(root, 'limits') - - #verify absolute limits - absolutes = root.xpath('ns:absolute/ns:limit', namespaces=NS) - self.assertEqual(len(absolutes), 4) - for limit in absolutes: - name = limit.get('name') - value = limit.get('value') - self.assertEqual(value, str(fixture['limits']['absolute'][name])) - - #verify rate limits - rates = root.xpath('ns:rates/ns:rate', namespaces=NS) - self.assertEqual(len(rates), 2) - for i, rate in enumerate(rates): - for key in ['uri', 'regex']: - self.assertEqual(rate.get(key), - str(fixture['limits']['rate'][i][key])) - rate_limits = rate.xpath('ns:limit', namespaces=NS) - self.assertEqual(len(rate_limits), 1) - for j, limit in enumerate(rate_limits): - for key in ['verb', 'value', 'remaining', 'unit', - 'next-available']: - self.assertEqual(limit.get(key), - str(fixture['limits']['rate'][i]['limit'][j][key])) - - def test_index_no_limits(self): - serializer = limits.LimitsXMLSerializer() - - fixture = {"limits": { - "rate": [], - "absolute": {}}} - - output = serializer.serialize(fixture, 'index') - print output - root = etree.XML(output) - xmlutil.validate_schema(root, 'limits') - - #verify absolute limits - absolutes = root.xpath('ns:absolute/ns:limit', namespaces=NS) - self.assertEqual(len(absolutes), 0) - - #verify rate limits - rates = root.xpath('ns:rates/ns:rate', namespaces=NS) - self.assertEqual(len(rates), 0) diff --git a/nova/tests/api/openstack/test_server_actions.py b/nova/tests/api/openstack/test_server_actions.py deleted file mode 100644 index 9d50483b8..000000000 --- a/nova/tests/api/openstack/test_server_actions.py +++ /dev/null @@ -1,866 +0,0 @@ -import base64 -import datetime -import json - -import stubout -import webob - -from nova import context -from nova import utils -from nova import exception -from nova import flags -from nova.api.openstack import servers -from nova.compute import vm_states -from nova.compute import instance_types -import nova.db -from nova import test -from nova.tests.api.openstack import common -from nova.tests.api.openstack import fakes - - -FLAGS = flags.FLAGS -FAKE_UUID = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" - - -def return_server_by_id(context, id): - return stub_instance(id) - - -def return_server_by_uuid(context, uuid): - return stub_instance(1, uuid=uuid) - - -def return_server_by_uuid_not_found(context, uuid): - raise exception.NotFound() - - -def instance_update(context, instance_id, kwargs): - return stub_instance(instance_id) - - -def return_server_with_attributes(**kwargs): - def _return_server(context, id): - return stub_instance(id, **kwargs) - return _return_server - - -def return_server_with_state(vm_state, task_state=None): - return return_server_with_attributes(vm_state=vm_state, - task_state=task_state) - - -def return_server_with_uuid_and_state(vm_state, task_state=None): - def _return_server(context, id): - return return_server_with_state(vm_state, task_state) - return _return_server - - -def stub_instance(id, metadata=None, image_ref="10", flavor_id="1", - name=None, vm_state=None, task_state=None, uuid=None): - if metadata is not None: - metadata_items = [{'key':k, 'value':v} for k, v in metadata.items()] - else: - metadata_items = [{'key':'seq', 'value':id}] - - if uuid is None: - uuid = FAKE_UUID - - inst_type = instance_types.get_instance_type_by_flavor_id(int(flavor_id)) - - instance = { - "id": int(id), - "name": str(id), - "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), - "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), - "admin_pass": "", - "user_id": "fake", - "project_id": "fake", - "image_ref": image_ref, - "kernel_id": "", - "ramdisk_id": "", - "launch_index": 0, - "key_name": "", - "key_data": "", - "vm_state": vm_state or vm_states.ACTIVE, - "task_state": task_state, - "memory_mb": 0, - "vcpus": 0, - "local_gb": 0, - "hostname": "", - "host": "", - "instance_type": dict(inst_type), - "user_data": "", - "reservation_id": "", - "mac_address": "", - "scheduled_at": utils.utcnow(), - "launched_at": utils.utcnow(), - "terminated_at": utils.utcnow(), - "availability_zone": "", - "display_name": name or "server%s" % id, - "display_description": "", - "locked": False, - "metadata": metadata_items, - "access_ip_v4": "", - "access_ip_v6": "", - "uuid": uuid, - "virtual_interfaces": [], - "progress": 0, - } - - instance["fixed_ips"] = [{"address": '192.168.0.1', - "network": - {'label': 'public', 'cidr_v6': None}, - "virtual_interface": - {'address': 'aa:aa:aa:aa:aa:aa'}, - "floating_ips": []}] - - return instance - - -class MockSetAdminPassword(object): - def __init__(self): - self.instance_id = None - self.password = None - - def __call__(self, context, instance, password): - self.instance_id = instance['uuid'] - self.password = password - - -class ServerActionsControllerTest(test.TestCase): - - def setUp(self): - self.maxDiff = None - super(ServerActionsControllerTest, self).setUp() - - self.stubs = stubout.StubOutForTesting() - fakes.stub_out_auth(self.stubs) - self.stubs.Set(nova.db, 'instance_get', return_server_by_id) - self.stubs.Set(nova.db, 'instance_get_by_uuid', return_server_by_uuid) - self.stubs.Set(nova.db, 'instance_update', instance_update) - - fakes.stub_out_glance(self.stubs) - fakes.stub_out_nw_api(self.stubs) - fakes.stub_out_rate_limiting(self.stubs) - self.snapshot = fakes.stub_out_compute_api_snapshot(self.stubs) - self.backup = fakes.stub_out_compute_api_backup(self.stubs) - service_class = 'nova.image.glance.GlanceImageService' - self.service = utils.import_object(service_class) - self.context = context.RequestContext(1, None) - self.service.delete_all() - self.sent_to_glance = {} - fakes.stub_out_glance_add_image(self.stubs, self.sent_to_glance) - self.flags(allow_instance_snapshots=True) - self.uuid = FAKE_UUID - self.url = '/v1.1/fake/servers/%s/action' % self.uuid - - self.controller = servers.Controller() - - def tearDown(self): - self.stubs.UnsetAll() - super(ServerActionsControllerTest, self).tearDown() - - def test_server_bad_body(self): - body = {} - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_server_unknown_action(self): - body = {'sockTheFox': {'fakekey': '1234'}} - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_server_change_password(self): - mock_method = MockSetAdminPassword() - self.stubs.Set(nova.compute.api.API, 'set_admin_password', mock_method) - body = {'changePassword': {'adminPass': '1234pass'}} - - req = fakes.HTTPRequest.blank(self.url) - self.controller.action(req, FAKE_UUID, body) - - self.assertEqual(mock_method.instance_id, self.uuid) - self.assertEqual(mock_method.password, '1234pass') - - def test_server_change_password_not_a_string(self): - body = {'changePassword': {'adminPass': 1234}} - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_server_change_password_bad_request(self): - body = {'changePassword': {'pass': '12345'}} - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_server_change_password_empty_string(self): - body = {'changePassword': {'adminPass': ''}} - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_server_change_password_none(self): - body = {'changePassword': {'adminPass': None}} - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_reboot_hard(self): - body = dict(reboot=dict(type="HARD")) - req = fakes.HTTPRequest.blank(self.url) - self.controller.action(req, FAKE_UUID, body) - - def test_reboot_soft(self): - body = dict(reboot=dict(type="SOFT")) - req = fakes.HTTPRequest.blank(self.url) - self.controller.action(req, FAKE_UUID, body) - - def test_reboot_incorrect_type(self): - body = dict(reboot=dict(type="NOT_A_TYPE")) - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_reboot_missing_type(self): - body = dict(reboot=dict()) - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_reboot_not_found(self): - self.stubs.Set(nova.db, 'instance_get_by_uuid', - return_server_by_uuid_not_found) - - body = dict(reboot=dict(type="HARD")) - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPNotFound, self.controller.action, - req, str(utils.gen_uuid()), body) - - def test_server_rebuild_accepted_minimum(self): - new_return_server = return_server_with_attributes(image_ref='2') - self.stubs.Set(nova.db, 'instance_get', new_return_server) - - body = { - "rebuild": { - "imageRef": "http://localhost/images/2", - }, - } - - req = fakes.HTTPRequest.blank(self.url) - body = self.controller.action(req, FAKE_UUID, body) - - self.assertEqual(body['server']['image']['id'], '2') - self.assertEqual(len(body['server']['adminPass']), - FLAGS.password_length) - - def test_server_rebuild_rejected_when_building(self): - body = { - "rebuild": { - "imageRef": "http://localhost/images/2", - }, - } - - def fake_rebuild(*args, **kwargs): - raise exception.RebuildRequiresActiveInstance - - self.stubs.Set(nova.compute.api.API, 'rebuild', fake_rebuild) - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPConflict, - self.controller.action, req, FAKE_UUID, body) - - def test_server_rebuild_accepted_with_metadata(self): - metadata = {'new': 'metadata'} - - new_return_server = return_server_with_attributes(metadata=metadata) - self.stubs.Set(nova.db, 'instance_get', new_return_server) - - body = { - "rebuild": { - "imageRef": "http://localhost/images/2", - "metadata": metadata, - }, - } - - req = fakes.HTTPRequest.blank(self.url) - body = self.controller.action(req, FAKE_UUID, body) - - self.assertEqual(body['server']['metadata'], metadata) - - def test_server_rebuild_accepted_with_bad_metadata(self): - body = { - "rebuild": { - "imageRef": "http://localhost/images/2", - "metadata": "stack", - }, - } - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_server_rebuild_bad_entity(self): - body = { - "rebuild": { - "imageId": 2, - }, - } - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_server_rebuild_bad_personality(self): - body = { - "rebuild": { - "imageRef": "http://localhost/images/2", - "personality": [{ - "path": "/path/to/file", - "contents": "INVALID b64", - }] - }, - } - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_server_rebuild_personality(self): - body = { - "rebuild": { - "imageRef": "http://localhost/images/2", - "personality": [{ - "path": "/path/to/file", - "contents": base64.b64encode("Test String"), - }] - }, - } - - req = fakes.HTTPRequest.blank(self.url) - body = self.controller.action(req, FAKE_UUID, body) - - self.assertTrue('personality' not in body['server']) - - def test_server_rebuild_admin_pass(self): - new_return_server = return_server_with_attributes(image_ref='2') - self.stubs.Set(nova.db, 'instance_get', new_return_server) - - body = { - "rebuild": { - "imageRef": "http://localhost/images/2", - "adminPass": "asdf", - }, - } - - req = fakes.HTTPRequest.blank(self.url) - body = self.controller.action(req, FAKE_UUID, body) - - self.assertEqual(body['server']['image']['id'], '2') - self.assertEqual(body['server']['adminPass'], 'asdf') - - def test_server_rebuild_server_not_found(self): - def server_not_found(self, instance_id): - raise exception.InstanceNotFound(instance_id=instance_id) - self.stubs.Set(nova.db, 'instance_get', server_not_found) - - body = { - "rebuild": { - "imageRef": "http://localhost/images/2", - }, - } - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.action, req, FAKE_UUID, body) - - def test_resize_server(self): - - body = dict(resize=dict(flavorRef="http://localhost/3")) - - self.resize_called = False - - def resize_mock(*args): - self.resize_called = True - - self.stubs.Set(nova.compute.api.API, 'resize', resize_mock) - - req = fakes.HTTPRequest.blank(self.url) - body = self.controller.action(req, FAKE_UUID, body) - - self.assertEqual(self.resize_called, True) - - def test_resize_server_no_flavor(self): - body = dict(resize=dict()) - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_resize_server_no_flavor_ref(self): - body = dict(resize=dict(flavorRef=None)) - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_confirm_resize_server(self): - body = dict(confirmResize=None) - - self.confirm_resize_called = False - - def cr_mock(*args): - self.confirm_resize_called = True - - self.stubs.Set(nova.compute.api.API, 'confirm_resize', cr_mock) - - req = fakes.HTTPRequest.blank(self.url) - body = self.controller.action(req, FAKE_UUID, body) - - self.assertEqual(self.confirm_resize_called, True) - - def test_confirm_resize_migration_not_found(self): - body = dict(confirmResize=None) - - def confirm_resize_mock(*args): - raise exception.MigrationNotFoundByStatus(instance_id=1, - status='finished') - - self.stubs.Set(nova.compute.api.API, - 'confirm_resize', - confirm_resize_mock) - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_revert_resize_migration_not_found(self): - body = dict(revertResize=None) - - def revert_resize_mock(*args): - raise exception.MigrationNotFoundByStatus(instance_id=1, - status='finished') - - self.stubs.Set(nova.compute.api.API, - 'revert_resize', - revert_resize_mock) - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_revert_resize_server(self): - body = dict(revertResize=None) - - self.revert_resize_called = False - - def revert_mock(*args): - self.revert_resize_called = True - - self.stubs.Set(nova.compute.api.API, 'revert_resize', revert_mock) - - req = fakes.HTTPRequest.blank(self.url) - body = self.controller.action(req, FAKE_UUID, body) - - self.assertEqual(self.revert_resize_called, True) - - def test_create_image(self): - body = { - 'createImage': { - 'name': 'Snapshot 1', - }, - } - - req = fakes.HTTPRequest.blank(self.url) - response = self.controller.action(req, FAKE_UUID, body) - - location = response.headers['Location'] - self.assertEqual('http://localhost/v1.1/fake/images/123', location) - server_location = self.snapshot.extra_props_last_call['instance_ref'] - expected_server_location = 'http://localhost/v1.1/servers/' + self.uuid - self.assertEqual(expected_server_location, server_location) - - def test_create_image_snapshots_disabled(self): - """Don't permit a snapshot if the allow_instance_snapshots flag is - False - """ - self.flags(allow_instance_snapshots=False) - body = { - 'createImage': { - 'name': 'Snapshot 1', - }, - } - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_create_image_with_metadata(self): - body = { - 'createImage': { - 'name': 'Snapshot 1', - 'metadata': {'key': 'asdf'}, - }, - } - - req = fakes.HTTPRequest.blank(self.url) - response = self.controller.action(req, FAKE_UUID, body) - - location = response.headers['Location'] - self.assertEqual('http://localhost/v1.1/fake/images/123', location) - - def test_create_image_with_too_much_metadata(self): - body = { - 'createImage': { - 'name': 'Snapshot 1', - 'metadata': {}, - }, - } - for num in range(FLAGS.quota_metadata_items + 1): - body['createImage']['metadata']['foo%i' % num] = "bar" - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPRequestEntityTooLarge, - self.controller.action, req, FAKE_UUID, body) - - def test_create_image_no_name(self): - body = { - 'createImage': {}, - } - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_create_image_bad_metadata(self): - body = { - 'createImage': { - 'name': 'geoff', - 'metadata': 'henry', - }, - } - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_create_image_conflict_snapshot(self): - """Attempt to create image when image is already being created.""" - def snapshot(*args, **kwargs): - raise exception.InstanceSnapshotting - self.stubs.Set(nova.compute.API, 'snapshot', snapshot) - - body = { - "createImage": { - "name": "test_snapshot", - }, - } - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPConflict, - self.controller.action, req, FAKE_UUID, body) - - def test_create_backup(self): - """The happy path for creating backups""" - self.flags(allow_admin_api=True) - - body = { - 'createBackup': { - 'name': 'Backup 1', - 'backup_type': 'daily', - 'rotation': 1, - }, - } - - req = fakes.HTTPRequest.blank(self.url) - response = self.controller.action(req, FAKE_UUID, body) - - self.assertTrue(response.headers['Location']) - server_location = self.backup.extra_props_last_call['instance_ref'] - expected_server_location = 'http://localhost/v1.1/servers/' + self.uuid - self.assertEqual(expected_server_location, server_location) - - def test_create_backup_admin_api_off(self): - """The happy path for creating backups""" - self.flags(allow_admin_api=False) - - body = { - 'createBackup': { - 'name': 'Backup 1', - 'backup_type': 'daily', - 'rotation': 1, - }, - } - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_create_backup_with_metadata(self): - self.flags(allow_admin_api=True) - - body = { - 'createBackup': { - 'name': 'Backup 1', - 'backup_type': 'daily', - 'rotation': 1, - 'metadata': {'123': 'asdf'}, - }, - } - - req = fakes.HTTPRequest.blank(self.url) - response = self.controller.action(req, FAKE_UUID, body) - - self.assertTrue(response.headers['Location']) - - def test_create_backup_with_too_much_metadata(self): - self.flags(allow_admin_api=True) - - body = { - 'createBackup': { - 'name': 'Backup 1', - 'backup_type': 'daily', - 'rotation': 1, - 'metadata': {'123': 'asdf'}, - }, - } - for num in range(FLAGS.quota_metadata_items + 1): - body['createBackup']['metadata']['foo%i' % num] = "bar" - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPRequestEntityTooLarge, - self.controller.action, req, FAKE_UUID, body) - - def test_create_backup_no_name(self): - """Name is required for backups""" - self.flags(allow_admin_api=True) - - body = { - 'createBackup': { - 'backup_type': 'daily', - 'rotation': 1, - }, - } - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_create_backup_no_rotation(self): - """Rotation is required for backup requests""" - self.flags(allow_admin_api=True) - - body = { - 'createBackup': { - 'name': 'Backup 1', - 'backup_type': 'daily', - }, - } - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_create_backup_no_backup_type(self): - """Backup Type (daily or weekly) is required for backup requests""" - self.flags(allow_admin_api=True) - - body = { - 'createBackup': { - 'name': 'Backup 1', - 'rotation': 1, - }, - } - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - def test_create_backup_bad_entity(self): - self.flags(allow_admin_api=True) - - body = {'createBackup': 'go'} - - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.action, req, FAKE_UUID, body) - - -class TestServerActionXMLDeserializer(test.TestCase): - - def setUp(self): - self.deserializer = servers.ServerXMLDeserializer() - - def tearDown(self): - pass - - def test_create_image(self): - serial_request = """ -""" - request = self.deserializer.deserialize(serial_request, 'action') - expected = { - "createImage": { - "name": "new-server-test", - }, - } - self.assertEquals(request['body'], expected) - - def test_create_image_with_metadata(self): - serial_request = """ - - - value1 - -""" - request = self.deserializer.deserialize(serial_request, 'action') - expected = { - "createImage": { - "name": "new-server-test", - "metadata": {"key1": "value1"}, - }, - } - self.assertEquals(request['body'], expected) - - def test_change_pass(self): - serial_request = """ - """ - request = self.deserializer.deserialize(serial_request, 'action') - expected = { - "changePassword": { - "adminPass": "1234pass", - }, - } - self.assertEquals(request['body'], expected) - - def test_change_pass_no_pass(self): - serial_request = """ - """ - self.assertRaises(AttributeError, - self.deserializer.deserialize, - serial_request, - 'action') - - def test_reboot(self): - serial_request = """ - """ - request = self.deserializer.deserialize(serial_request, 'action') - expected = { - "reboot": { - "type": "HARD", - }, - } - self.assertEquals(request['body'], expected) - - def test_reboot_no_type(self): - serial_request = """ - """ - self.assertRaises(AttributeError, - self.deserializer.deserialize, - serial_request, - 'action') - - def test_resize(self): - serial_request = """ - """ - request = self.deserializer.deserialize(serial_request, 'action') - expected = { - "resize": {"flavorRef": "http://localhost/flavors/3"}, - } - self.assertEquals(request['body'], expected) - - def test_resize_no_flavor_ref(self): - serial_request = """ - """ - self.assertRaises(AttributeError, - self.deserializer.deserialize, - serial_request, - 'action') - - def test_confirm_resize(self): - serial_request = """ - """ - request = self.deserializer.deserialize(serial_request, 'action') - expected = { - "confirmResize": None, - } - self.assertEquals(request['body'], expected) - - def test_revert_resize(self): - serial_request = """ - """ - request = self.deserializer.deserialize(serial_request, 'action') - expected = { - "revertResize": None, - } - self.assertEquals(request['body'], expected) - - def test_rebuild(self): - serial_request = """ - - - Apache1 - - - Mg== - - """ - request = self.deserializer.deserialize(serial_request, 'action') - expected = { - "rebuild": { - "name": "new-server-test", - "imageRef": "http://localhost/images/1", - "metadata": { - "My Server Name": "Apache1", - }, - "personality": [ - {"path": "/etc/banner.txt", "contents": "Mg=="}, - ], - }, - } - self.assertDictMatch(request['body'], expected) - - def test_rebuild_minimum(self): - serial_request = """ - """ - request = self.deserializer.deserialize(serial_request, 'action') - expected = { - "rebuild": { - "imageRef": "http://localhost/images/1", - }, - } - self.assertDictMatch(request['body'], expected) - - def test_rebuild_no_imageRef(self): - serial_request = """ - - - Apache1 - - - Mg== - - """ - self.assertRaises(AttributeError, - self.deserializer.deserialize, - serial_request, - 'action') diff --git a/nova/tests/api/openstack/test_server_metadata.py b/nova/tests/api/openstack/test_server_metadata.py deleted file mode 100644 index 1e0cba541..000000000 --- a/nova/tests/api/openstack/test_server_metadata.py +++ /dev/null @@ -1,361 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import json -import webob - -from nova.api.openstack import server_metadata -import nova.db -from nova import exception -from nova import flags -from nova import test -from nova import utils -from nova.tests.api.openstack import fakes - - -FLAGS = flags.FLAGS - - -def return_create_instance_metadata_max(context, server_id, metadata, delete): - return stub_max_server_metadata() - - -def return_create_instance_metadata(context, server_id, metadata, delete): - return stub_server_metadata() - - -def return_server_metadata(context, server_id): - if not isinstance(server_id, int): - msg = 'id %s must be int in return server metadata' % server_id - raise Exception(msg) - return stub_server_metadata() - - -def return_empty_server_metadata(context, server_id): - return {} - - -def delete_server_metadata(context, server_id, key): - pass - - -def stub_server_metadata(): - metadata = { - "key1": "value1", - "key2": "value2", - "key3": "value3", - } - return metadata - - -def stub_max_server_metadata(): - metadata = {"metadata": {}} - for num in range(FLAGS.quota_metadata_items): - metadata['metadata']['key%i' % num] = "blah" - return metadata - - -def return_server(context, server_id): - return {'id': server_id, 'name': 'fake'} - - -def return_server_by_uuid(context, server_uuid): - return {'id': 1, 'name': 'fake'} - - -def return_server_nonexistant(context, server_id): - raise exception.InstanceNotFound() - - -class ServerMetaDataTest(test.TestCase): - - def setUp(self): - super(ServerMetaDataTest, self).setUp() - fakes.stub_out_key_pair_funcs(self.stubs) - self.stubs.Set(nova.db, 'instance_get', return_server) - self.stubs.Set(nova.db, 'instance_get_by_uuid', - return_server_by_uuid) - - self.stubs.Set(nova.db, 'instance_metadata_get', - return_server_metadata) - - self.controller = server_metadata.Controller() - self.uuid = str(utils.gen_uuid()) - self.url = '/v1.1/fake/servers/%s/metadata' % self.uuid - - def test_index(self): - req = fakes.HTTPRequest.blank(self.url) - res_dict = self.controller.index(req, self.uuid) - - expected = { - 'metadata': { - 'key1': 'value1', - 'key2': 'value2', - 'key3': 'value3', - }, - } - self.assertEqual(expected, res_dict) - - def test_index_nonexistant_server(self): - self.stubs.Set(nova.db, 'instance_metadata_get', - return_server_nonexistant) - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.index, req, self.url) - - def test_index_no_data(self): - self.stubs.Set(nova.db, 'instance_metadata_get', - return_empty_server_metadata) - req = fakes.HTTPRequest.blank(self.url) - res_dict = self.controller.index(req, self.uuid) - expected = {'metadata': {}} - self.assertEqual(expected, res_dict) - - def test_show(self): - req = fakes.HTTPRequest.blank(self.url + '/key2') - res_dict = self.controller.show(req, self.uuid, 'key2') - expected = {'meta': {'key2': 'value2'}} - self.assertEqual(expected, res_dict) - - def test_show_nonexistant_server(self): - self.stubs.Set(nova.db, 'instance_metadata_get', - return_server_nonexistant) - req = fakes.HTTPRequest.blank(self.url + '/key2') - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.show, req, self.uuid, 'key2') - - def test_show_meta_not_found(self): - self.stubs.Set(nova.db, 'instance_metadata_get', - return_empty_server_metadata) - req = fakes.HTTPRequest.blank(self.url + '/key6') - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.show, req, self.uuid, 'key6') - - def test_delete(self): - self.stubs.Set(nova.db, 'instance_metadata_get', - return_server_metadata) - self.stubs.Set(nova.db, 'instance_metadata_delete', - delete_server_metadata) - req = fakes.HTTPRequest.blank(self.url + '/key2') - req.method = 'DELETE' - res = self.controller.delete(req, self.uuid, 'key2') - - self.assertEqual(None, res) - - def test_delete_nonexistant_server(self): - self.stubs.Set(nova.db, 'instance_get', return_server_nonexistant) - req = fakes.HTTPRequest.blank(self.url + '/key1') - req.method = 'DELETE' - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.delete, req, self.uuid, 'key1') - - def test_delete_meta_not_found(self): - self.stubs.Set(nova.db, 'instance_metadata_get', - return_empty_server_metadata) - req = fakes.HTTPRequest.blank(self.url + '/key6') - req.method = 'DELETE' - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.delete, req, self.uuid, 'key6') - - def test_create(self): - self.stubs.Set(nova.db, 'instance_metadata_get', - return_server_metadata) - self.stubs.Set(nova.db, 'instance_metadata_update', - return_create_instance_metadata) - req = fakes.HTTPRequest.blank(self.url) - req.method = 'POST' - req.content_type = "application/json" - body = {"metadata": {"key9": "value9"}} - req.body = json.dumps(body) - res_dict = self.controller.create(req, self.uuid, body) - - body['metadata'].update({ - "key1": "value1", - "key2": "value2", - "key3": "value3", - }) - self.assertEqual(body, res_dict) - - def test_create_empty_body(self): - self.stubs.Set(nova.db, 'instance_metadata_update', - return_create_instance_metadata) - req = fakes.HTTPRequest.blank(self.url) - req.method = 'POST' - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.create, req, self.uuid, None) - - def test_create_nonexistant_server(self): - self.stubs.Set(nova.db, 'instance_get', return_server_nonexistant) - req = fakes.HTTPRequest.blank(self.url) - req.method = 'POST' - body = {"metadata": {"key1": "value1"}} - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.create, req, self.uuid, body) - - def test_update_all(self): - self.stubs.Set(nova.db, 'instance_metadata_update', - return_create_instance_metadata) - req = fakes.HTTPRequest.blank(self.url) - req.method = 'PUT' - req.content_type = "application/json" - expected = { - 'metadata': { - 'key10': 'value10', - 'key99': 'value99', - }, - } - req.body = json.dumps(expected) - res_dict = self.controller.update_all(req, self.uuid, expected) - - self.assertEqual(expected, res_dict) - - def test_update_all_empty_container(self): - self.stubs.Set(nova.db, 'instance_metadata_update', - return_create_instance_metadata) - req = fakes.HTTPRequest.blank(self.url) - req.method = 'PUT' - req.content_type = "application/json" - expected = {'metadata': {}} - req.body = json.dumps(expected) - res_dict = self.controller.update_all(req, self.uuid, expected) - - self.assertEqual(expected, res_dict) - - def test_update_all_malformed_container(self): - self.stubs.Set(nova.db, 'instance_metadata_update', - return_create_instance_metadata) - req = fakes.HTTPRequest.blank(self.url) - req.method = 'PUT' - req.content_type = "application/json" - expected = {'meta': {}} - req.body = json.dumps(expected) - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.update_all, req, self.uuid, expected) - - def test_update_all_malformed_data(self): - self.stubs.Set(nova.db, 'instance_metadata_update', - return_create_instance_metadata) - req = fakes.HTTPRequest.blank(self.url) - req.method = 'PUT' - req.content_type = "application/json" - expected = {'metadata': ['asdf']} - req.body = json.dumps(expected) - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.update_all, req, self.uuid, expected) - - def test_update_all_nonexistant_server(self): - self.stubs.Set(nova.db, 'instance_get', return_server_nonexistant) - req = fakes.HTTPRequest.blank(self.url) - req.method = 'PUT' - req.content_type = "application/json" - body = {'metadata': {'key10': 'value10'}} - req.body = json.dumps(body) - - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.update_all, req, '100', body) - - def test_update_item(self): - self.stubs.Set(nova.db, 'instance_metadata_update', - return_create_instance_metadata) - req = fakes.HTTPRequest.blank(self.url + '/key1') - req.method = 'PUT' - body = {"meta": {"key1": "value1"}} - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - res_dict = self.controller.update(req, self.uuid, 'key1', body) - expected = {'meta': {'key1': 'value1'}} - self.assertEqual(expected, res_dict) - - def test_update_item_nonexistant_server(self): - self.stubs.Set(nova.db, 'instance_get', return_server_nonexistant) - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/asdf/metadata/key1') - req.method = 'PUT' - body = {"meta": {"key1": "value1"}} - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.update, req, self.uuid, 'key1', body) - - def test_update_item_empty_body(self): - self.stubs.Set(nova.db, 'instance_metadata_update', - return_create_instance_metadata) - req = fakes.HTTPRequest.blank(self.url + '/key1') - req.method = 'PUT' - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.update, req, self.uuid, 'key1', None) - - def test_update_item_too_many_keys(self): - self.stubs.Set(nova.db, 'instance_metadata_update', - return_create_instance_metadata) - req = fakes.HTTPRequest.blank(self.url + '/key1') - req.method = 'PUT' - body = {"meta": {"key1": "value1", "key2": "value2"}} - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.update, req, self.uuid, 'key1', body) - - def test_update_item_body_uri_mismatch(self): - self.stubs.Set(nova.db, 'instance_metadata_update', - return_create_instance_metadata) - req = fakes.HTTPRequest.blank(self.url + '/bad') - req.method = 'PUT' - body = {"meta": {"key1": "value1"}} - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.update, req, self.uuid, 'bad', body) - - def test_too_many_metadata_items_on_create(self): - self.stubs.Set(nova.db, 'instance_metadata_update', - return_create_instance_metadata) - data = {"metadata": {}} - for num in range(FLAGS.quota_metadata_items + 1): - data['metadata']['key%i' % num] = "blah" - req = fakes.HTTPRequest.blank(self.url) - req.method = 'POST' - req.body = json.dumps(data) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPRequestEntityTooLarge, - self.controller.create, req, self.uuid, data) - - def test_too_many_metadata_items_on_update_item(self): - self.stubs.Set(nova.db, 'instance_metadata_update', - return_create_instance_metadata) - data = {"metadata": {}} - for num in range(FLAGS.quota_metadata_items + 1): - data['metadata']['key%i' % num] = "blah" - req = fakes.HTTPRequest.blank(self.url) - req.method = 'PUT' - req.body = json.dumps(data) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPRequestEntityTooLarge, - self.controller.update_all, req, self.uuid, data) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py deleted file mode 100644 index 7eaaaa489..000000000 --- a/nova/tests/api/openstack/test_servers.py +++ /dev/null @@ -1,3638 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010-2011 OpenStack LLC. -# Copyright 2011 Piston Cloud Computing, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import base64 -import datetime -import json -import unittest -import urlparse -from xml.dom import minidom - -from lxml import etree -import webob - -import nova.api.openstack -from nova.api.openstack import servers -from nova.api.openstack import ips -from nova.api.openstack import xmlutil -import nova.compute.api -from nova.compute import instance_types -from nova.compute import task_states -from nova.compute import vm_states -from nova import context -from nova import db -import nova.db -from nova.db.sqlalchemy.models import InstanceMetadata -from nova import exception -from nova import flags -import nova.image.fake -import nova.rpc -import nova.scheduler.api -from nova import test -from nova.tests.api.openstack import common -from nova.tests.api.openstack import fakes -from nova import utils - - -FLAGS = flags.FLAGS -FAKE_UUID = fakes.FAKE_UUID -FAKE_UUIDS = {0: FAKE_UUID} -NS = "{http://docs.openstack.org/compute/api/v1.1}" -ATOMNS = "{http://www.w3.org/2005/Atom}" -XPATH_NS = { - 'atom': 'http://www.w3.org/2005/Atom', - 'ns': 'http://docs.openstack.org/compute/api/v1.1' -} - - -def get_fake_uuid(token=0): - if not token in FAKE_UUIDS: - FAKE_UUIDS[token] = str(utils.gen_uuid()) - return FAKE_UUIDS[token] - - -def fake_gen_uuid(): - return FAKE_UUID - - -def return_server_by_id(context, id): - return fakes.stub_instance(id) - - -def return_server_by_uuid(context, uuid): - id = 1 - return fakes.stub_instance(id, uuid=uuid) - - -def return_server_with_attributes(**kwargs): - def _return_server(context, instance_id): - return fakes.stub_instance(instance_id, **kwargs) - return _return_server - - -def return_server_with_attributes_by_uuid(**kwargs): - def _return_server(context, uuid): - return fakes.stub_instance(1, uuid=uuid, **kwargs) - return _return_server - - -def return_server_with_state(vm_state, task_state=None): - def _return_server(context, uuid): - return fakes.stub_instance(1, uuid=uuid, vm_state=vm_state, - task_state=task_state) - return _return_server - - -def return_server_with_uuid_and_state(vm_state, task_state): - def _return_server(context, id): - return fakes.stub_instance(id, - uuid=FAKE_UUID, - vm_state=vm_state, - task_state=task_state) - return _return_server - - -def return_servers(context, *args, **kwargs): - servers = [] - for i in xrange(5): - server = fakes.stub_instance(i, 'fake', 'fake', uuid=get_fake_uuid(i)) - servers.append(server) - return servers - - -def return_servers_by_reservation(context, reservation_id=""): - return [fakes.stub_instance(i, reservation_id) for i in xrange(5)] - - -def return_servers_by_reservation_empty(context, reservation_id=""): - return [] - - -def return_servers_from_child_zones_empty(*args, **kwargs): - return [] - - -def return_servers_from_child_zones(*args, **kwargs): - class Server(object): - pass - - zones = [] - for zone in xrange(3): - servers = [] - for server_id in xrange(5): - server = Server() - server._info = fakes.stub_instance( - server_id, reservation_id="child") - servers.append(server) - - zones.append(("Zone%d" % zone, servers)) - return zones - - -def return_security_group(context, instance_id, security_group_id): - pass - - -def instance_update(context, instance_id, values): - return fakes.stub_instance(instance_id, name=values.get('display_name')) - - -def instance_addresses(context, instance_id): - return None - - -def fake_compute_api(cls, req, id): - return True - - -def find_host(self, context, instance_id): - return "nova" - - -class MockSetAdminPassword(object): - def __init__(self): - self.instance_id = None - self.password = None - - def __call__(self, context, instance_id, password): - self.instance_id = instance_id - self.password = password - - -class ServersControllerTest(test.TestCase): - def setUp(self): - self.maxDiff = None - super(ServersControllerTest, self).setUp() - self.flags(verbose=True, use_ipv6=False) - fakes.stub_out_networking(self.stubs) - fakes.stub_out_rate_limiting(self.stubs) - fakes.stub_out_key_pair_funcs(self.stubs) - fakes.stub_out_image_service(self.stubs) - fakes.stub_out_nw_api(self.stubs) - self.stubs.Set(nova.db, 'instance_get_all_by_filters', - return_servers) - self.stubs.Set(nova.db, 'instance_get', return_server_by_id) - self.stubs.Set(nova.db, 'instance_get_by_uuid', - return_server_by_uuid) - self.stubs.Set(nova.db, 'instance_get_all_by_project', - return_servers) - self.stubs.Set(nova.db, 'instance_add_security_group', - return_security_group) - self.stubs.Set(nova.db, 'instance_update', instance_update) - self.stubs.Set(nova.db, 'instance_get_fixed_addresses', - instance_addresses) - self.stubs.Set(nova.db, 'instance_get_floating_address', - instance_addresses) - self.stubs.Set(nova.compute.API, "get_diagnostics", fake_compute_api) - self.stubs.Set(nova.compute.API, "get_actions", fake_compute_api) - - self.config_drive = None - - self.controller = servers.Controller() - self.ips_controller = ips.Controller() - - def test_get_server_by_uuid(self): - """ - The steps involved with resolving a UUID are pretty complicated; - here's what's happening in this scenario: - - 1. Show is calling `routing_get` - - 2. `routing_get` is wrapped by `reroute_compute` which does the work - of resolving requests to child zones. - - 3. `reroute_compute` looks up the UUID by hitting the stub - (returns_server_by_uuid) - - 4. Since the stub return that the record exists, `reroute_compute` - considers the request to be 'zone local', so it replaces the UUID - in the argument list with an integer ID and then calls the inner - function ('get'). - - 5. The call to `get` hits the other stub 'returns_server_by_id` which - has the UUID set to FAKE_UUID - - So, counterintuitively, we call `get` twice on the `show` command. - """ - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) - res_dict = self.controller.show(req, FAKE_UUID) - self.assertEqual(res_dict['server']['id'], FAKE_UUID) - - def test_get_server_by_id(self): - self.flags(use_ipv6=True) - image_bookmark = "http://localhost/fake/images/10" - flavor_bookmark = "http://localhost/fake/flavors/1" - - uuid = FAKE_UUID - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % uuid) - res_dict = self.controller.show(req, uuid) - expected_server = { - "server": { - "id": uuid, - "user_id": "fake", - "tenant_id": "fake", - "updated": "2010-11-11T11:00:00Z", - "created": "2010-10-10T12:00:00Z", - "progress": 0, - "name": "server1", - "status": "BUILD", - "accessIPv4": "", - "accessIPv6": "", - "hostId": '', - "key_name": '', - "image": { - "id": "10", - "links": [ - { - "rel": "bookmark", - "href": image_bookmark, - }, - ], - }, - "flavor": { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": flavor_bookmark, - }, - ], - }, - "addresses": { - }, - "metadata": { - "seq": "1", - }, - "config_drive": None, - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/servers/%s" % uuid, - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/servers/%s" % uuid, - }, - ], - } - } - - self.assertDictMatch(res_dict, expected_server) - - def test_get_server_with_active_status_by_id(self): - image_bookmark = "http://localhost/fake/images/10" - flavor_bookmark = "http://localhost/fake/flavors/1" - - new_return_server = return_server_with_attributes( - vm_state=vm_states.ACTIVE, progress=100) - self.stubs.Set(nova.db, 'instance_get', new_return_server) - - uuid = FAKE_UUID - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % uuid) - res_dict = self.controller.show(req, uuid) - expected_server = { - "server": { - "id": uuid, - "user_id": "fake", - "tenant_id": "fake", - "updated": "2010-11-11T11:00:00Z", - "created": "2010-10-10T12:00:00Z", - "progress": 100, - "name": "server1", - "status": "ACTIVE", - "accessIPv4": "", - "accessIPv6": "", - "hostId": '', - "key_name": '', - "image": { - "id": "10", - "links": [ - { - "rel": "bookmark", - "href": image_bookmark, - }, - ], - }, - "flavor": { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": flavor_bookmark, - }, - ], - }, - "addresses": { - }, - "metadata": { - "seq": "1", - }, - "config_drive": None, - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/servers/%s" % uuid, - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/servers/%s" % uuid, - }, - ], - } - } - - self.assertDictMatch(res_dict, expected_server) - - def test_get_server_with_id_image_ref_by_id(self): - image_ref = "10" - image_bookmark = "http://localhost/fake/images/10" - flavor_id = "1" - flavor_bookmark = "http://localhost/fake/flavors/1" - - new_return_server = return_server_with_attributes( - vm_state=vm_states.ACTIVE, image_ref=image_ref, - flavor_id=flavor_id, progress=100) - self.stubs.Set(nova.db, 'instance_get', new_return_server) - - uuid = FAKE_UUID - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % uuid) - res_dict = self.controller.show(req, uuid) - expected_server = { - "server": { - "id": uuid, - "user_id": "fake", - "tenant_id": "fake", - "updated": "2010-11-11T11:00:00Z", - "created": "2010-10-10T12:00:00Z", - "progress": 100, - "name": "server1", - "status": "ACTIVE", - "accessIPv4": "", - "accessIPv6": "", - "hostId": '', - "key_name": '', - "image": { - "id": "10", - "links": [ - { - "rel": "bookmark", - "href": image_bookmark, - }, - ], - }, - "flavor": { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": flavor_bookmark, - }, - ], - }, - "addresses": { - }, - "metadata": { - "seq": "1", - }, - "config_drive": None, - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/servers/%s" % uuid, - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/servers/%s" % uuid, - }, - ], - } - } - - self.assertDictMatch(res_dict, expected_server) - - # NOTE(bcwaldon): lp830817 - def test_get_server_by_id_malformed_networks(self): - def fake_instance_get(context, instance_uuid): - instance = return_server_by_uuid(context, instance_uuid) - instance['fixed_ips'] = [dict(network=None, address='1.2.3.4')] - return instance - - self.stubs.Set(nova.db, 'instance_get_by_uuid', fake_instance_get) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) - res_dict = self.controller.show(req, FAKE_UUID) - - self.assertEqual(res_dict['server']['id'], FAKE_UUID) - self.assertEqual(res_dict['server']['name'], 'server1') - - def test_get_server_by_id_malformed_vif(self): - def fake_instance_get(context, uuid): - instance = return_server_by_uuid(context, uuid) - instance['fixed_ips'] = [dict(network={'label': 'meow'}, - address='1.2.3.4', virtual_interface=None)] - return instance - - self.stubs.Set(nova.db, 'instance_get_by_uuid', fake_instance_get) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) - res_dict = self.controller.show(req, FAKE_UUID) - - self.assertEqual(res_dict['server']['id'], FAKE_UUID) - self.assertEqual(res_dict['server']['name'], 'server1') - - def test_get_server_by_id_with_addresses(self): - self.flags(use_ipv6=True) - privates = ['192.168.0.3', '192.168.0.4'] - publics = ['172.19.0.1', '172.19.0.2'] - new_return_server = return_server_with_attributes( - public_ips=publics, private_ips=privates) - self.stubs.Set(nova.db, 'instance_get', new_return_server) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) - res_dict = self.controller.show(req, FAKE_UUID) - - self.assertEqual(res_dict['server']['id'], FAKE_UUID) - self.assertEqual(res_dict['server']['name'], 'server1') - addresses = res_dict['server']['addresses'] - expected = { - 'private': [ - {'addr': '192.168.0.3', 'version': 4}, - {'addr': '192.168.0.4', 'version': 4}, - ], - 'public': [ - {'addr': 'b33f::fdee:ddff:fecc:bbaa', 'version': 6}, - {'addr': '172.19.0.1', 'version': 4}, - {'addr': '172.19.0.2', 'version': 4}, - ], - } - self.assertDictMatch(addresses, expected) - - def test_get_server_by_id_with_addresses_ipv6_disabled(self): - # ipv6 flag is off by default - privates = ['192.168.0.3', '192.168.0.4'] - publics = ['172.19.0.1', '172.19.0.2'] - new_return_server = return_server_with_attributes( - public_ips=publics, private_ips=privates) - self.stubs.Set(nova.db, 'instance_get', new_return_server) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) - res_dict = self.controller.show(req, FAKE_UUID) - - self.assertEqual(res_dict['server']['id'], FAKE_UUID) - self.assertEqual(res_dict['server']['name'], 'server1') - addresses = res_dict['server']['addresses'] - expected = { - 'private': [ - {'addr': '192.168.0.3', 'version': 4}, - {'addr': '192.168.0.4', 'version': 4}, - ], - 'public': [ - {'addr': '172.19.0.1', 'version': 4}, - {'addr': '172.19.0.2', 'version': 4}, - ], - } - self.assertDictMatch(addresses, expected) - - def test_get_server_addresses(self): - self.flags(use_ipv6=True) - - privates = ['192.168.0.3', '192.168.0.4'] - publics = ['172.19.0.1', '1.2.3.4', '172.19.0.2'] - new_return_server = return_server_with_attributes_by_uuid( - public_ips=publics, private_ips=privates) - self.stubs.Set(nova.db, 'instance_get_by_uuid', new_return_server) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s/ips' % FAKE_UUID) - res_dict = self.ips_controller.index(req, FAKE_UUID) - - expected = { - 'addresses': { - 'private': [ - {'version': 4, 'addr': '192.168.0.3'}, - {'version': 4, 'addr': '192.168.0.4'}, - ], - 'public': [ - {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, - {'version': 4, 'addr': '172.19.0.1'}, - {'version': 4, 'addr': '1.2.3.4'}, - {'version': 4, 'addr': '172.19.0.2'}, - ], - }, - } - self.assertDictMatch(res_dict, expected) - - def test_get_server_addresses_with_floating(self): - privates = ['192.168.0.3', '192.168.0.4'] - publics = ['172.19.0.1', '1.2.3.4', '172.19.0.2'] - new_return_server = return_server_with_attributes_by_uuid( - public_ips=publics, private_ips=privates, - public_ips_are_floating=True) - self.stubs.Set(nova.db, 'instance_get_by_uuid', new_return_server) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s/ips' % FAKE_UUID) - res_dict = self.ips_controller.index(req, FAKE_UUID) - - expected = { - 'addresses': { - 'private': [ - {'version': 4, 'addr': '192.168.0.3'}, - {'version': 4, 'addr': '192.168.0.4'}, - {'version': 4, 'addr': '172.19.0.1'}, - {'version': 4, 'addr': '1.2.3.4'}, - {'version': 4, 'addr': '172.19.0.2'}, - ], - }, - } - self.assertDictMatch(res_dict, expected) - - def test_get_server_addresses_single_network(self): - self.flags(use_ipv6=True) - privates = ['192.168.0.3', '192.168.0.4'] - publics = ['172.19.0.1', '1.2.3.4', '172.19.0.2'] - new_return_server = return_server_with_attributes_by_uuid( - public_ips=publics, private_ips=privates) - self.stubs.Set(nova.db, 'instance_get_by_uuid', new_return_server) - - url = '/v1.1/fake/servers/%s/ips/public' % FAKE_UUID - req = fakes.HTTPRequest.blank(url) - res_dict = self.ips_controller.show(req, FAKE_UUID, 'public') - - expected = { - 'public': [ - {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, - {'version': 4, 'addr': '172.19.0.1'}, - {'version': 4, 'addr': '1.2.3.4'}, - {'version': 4, 'addr': '172.19.0.2'}, - ], - } - self.assertDictMatch(res_dict, expected) - - def test_get_server_addresses_nonexistant_network(self): - url = '/v1.1/fake/servers/%s/ips/network_0' % FAKE_UUID - req = fakes.HTTPRequest.blank(url) - self.assertRaises(webob.exc.HTTPNotFound, self.ips_controller.show, - req, FAKE_UUID, 'network_0') - - def test_get_server_addresses_nonexistant_server(self): - def fake_instance_get(*args, **kwargs): - raise nova.exception.InstanceNotFound() - - self.stubs.Set(nova.db, 'instance_get_by_uuid', fake_instance_get) - - server_id = str(utils.gen_uuid()) - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s/ips' % server_id) - self.assertRaises(webob.exc.HTTPNotFound, - self.ips_controller.index, req, server_id) - - def test_get_server_list_with_reservation_id(self): - self.stubs.Set(nova.db, 'instance_get_all_by_reservation', - return_servers_by_reservation) - self.stubs.Set(nova.scheduler.api, 'call_zone_method', - return_servers_from_child_zones) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?reservation_id=foo') - res_dict = self.controller.index(req) - - i = 0 - for s in res_dict['servers']: - if '_is_precooked' in s: - self.assertEqual(s.get('reservation_id'), 'child') - else: - print s - self.assertEqual(s.get('name'), 'server%d' % i) - i += 1 - - def test_get_server_list_with_reservation_id_empty(self): - self.stubs.Set(nova.db, 'instance_get_all_by_reservation', - return_servers_by_reservation_empty) - self.stubs.Set(nova.scheduler.api, 'call_zone_method', - return_servers_from_child_zones_empty) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/detail?' - 'reservation_id=foo') - res_dict = self.controller.detail(req) - - i = 0 - for s in res_dict['servers']: - if '_is_precooked' in s: - self.assertEqual(s.get('reservation_id'), 'child') - else: - self.assertEqual(s.get('name'), 'server%d' % i) - i += 1 - - def test_get_server_list_with_reservation_id_details(self): - self.stubs.Set(nova.db, 'instance_get_all_by_reservation', - return_servers_by_reservation) - self.stubs.Set(nova.scheduler.api, 'call_zone_method', - return_servers_from_child_zones) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/detail?' - 'reservation_id=foo') - res_dict = self.controller.detail(req) - - i = 0 - for s in res_dict['servers']: - if '_is_precooked' in s: - self.assertEqual(s.get('reservation_id'), 'child') - else: - self.assertEqual(s.get('name'), 'server%d' % i) - i += 1 - - def test_get_server_list(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/servers') - res_dict = self.controller.index(req) - - self.assertEqual(len(res_dict['servers']), 5) - for i, s in enumerate(res_dict['servers']): - self.assertEqual(s['id'], get_fake_uuid(i)) - self.assertEqual(s['name'], 'server%d' % i) - self.assertEqual(s.get('image', None), None) - - expected_links = [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/servers/%s" % s['id'], - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/servers/%s" % s['id'], - }, - ] - - self.assertEqual(s['links'], expected_links) - - def test_get_servers_with_limit(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?limit=3') - res_dict = self.controller.index(req) - - servers = res_dict['servers'] - self.assertEqual([s['id'] for s in servers], - [get_fake_uuid(i) for i in xrange(len(servers))]) - - servers_links = res_dict['servers_links'] - self.assertEqual(servers_links[0]['rel'], 'next') - href_parts = urlparse.urlparse(servers_links[0]['href']) - self.assertEqual('/v1.1/fake/servers', href_parts.path) - params = urlparse.parse_qs(href_parts.query) - expected_params = {'limit': ['3'], 'marker': [get_fake_uuid(2)]} - self.assertDictMatch(expected_params, params) - - def test_get_servers_with_limit_bad_value(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?limit=aaa') - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.index, req) - - def test_get_server_details_with_limit(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/detail?limit=3') - res = self.controller.detail(req) - - servers = res['servers'] - self.assertEqual([s['id'] for s in servers], - [get_fake_uuid(i) for i in xrange(len(servers))]) - - servers_links = res['servers_links'] - self.assertEqual(servers_links[0]['rel'], 'next') - - href_parts = urlparse.urlparse(servers_links[0]['href']) - self.assertEqual('/v1.1/fake/servers', href_parts.path) - params = urlparse.parse_qs(href_parts.query) - expected = {'limit': ['3'], 'marker': [get_fake_uuid(2)]} - self.assertDictMatch(expected, params) - - def test_get_server_details_with_limit_bad_value(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/detail?limit=aaa') - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.detail, req) - - def test_get_server_details_with_limit_and_other_params(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/detail' - '?limit=3&blah=2:t') - res = self.controller.detail(req) - - servers = res['servers'] - self.assertEqual([s['id'] for s in servers], - [get_fake_uuid(i) for i in xrange(len(servers))]) - - servers_links = res['servers_links'] - self.assertEqual(servers_links[0]['rel'], 'next') - - href_parts = urlparse.urlparse(servers_links[0]['href']) - self.assertEqual('/v1.1/fake/servers', href_parts.path) - params = urlparse.parse_qs(href_parts.query) - - self.assertDictMatch({'limit': ['3'], 'blah': ['2:t'], - 'marker': [get_fake_uuid(2)]}, params) - - def test_get_servers_with_too_big_limit(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?limit=30') - res_dict = self.controller.index(req) - self.assertTrue('servers_links' not in res_dict) - - def test_get_servers_with_bad_limit(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?limit=asdf') - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.index, req) - - def test_get_servers_with_marker(self): - url = '/v1.1/fake/servers?marker=%s' % get_fake_uuid(2) - req = fakes.HTTPRequest.blank(url) - servers = self.controller.index(req)['servers'] - self.assertEqual([s['name'] for s in servers], ["server3", "server4"]) - - def test_get_servers_with_limit_and_marker(self): - url = '/v1.1/fake/servers?limit=2&marker=%s' % get_fake_uuid(1) - req = fakes.HTTPRequest.blank(url) - servers = self.controller.index(req)['servers'] - self.assertEqual([s['name'] for s in servers], ['server2', 'server3']) - - def test_get_servers_with_bad_marker(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?limit=2&marker=asdf') - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.index, req) - - def test_get_servers_with_bad_option(self): - server_uuid = str(utils.gen_uuid()) - - def fake_get_all(compute_self, context, search_opts=None): - return [fakes.stub_instance(100, uuid=server_uuid)] - - self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?unknownoption=whee') - servers = self.controller.index(req)['servers'] - - self.assertEqual(len(servers), 1) - self.assertEqual(servers[0]['id'], server_uuid) - - def test_get_servers_allows_image(self): - server_uuid = str(utils.gen_uuid()) - - def fake_get_all(compute_self, context, search_opts=None): - self.assertNotEqual(search_opts, None) - self.assertTrue('image' in search_opts) - self.assertEqual(search_opts['image'], '12345') - return [fakes.stub_instance(100, uuid=server_uuid)] - - self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) - self.flags(allow_admin_api=False) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?image=12345') - servers = self.controller.index(req)['servers'] - - self.assertEqual(len(servers), 1) - self.assertEqual(servers[0]['id'], server_uuid) - - def test_tenant_id_filter_converts_to_project_id_for_admin(self): - def fake_get_all(context, filters=None, instances=None): - self.assertNotEqual(filters, None) - self.assertEqual(filters['project_id'], 'fake') - self.assertFalse(filters.get('tenant_id')) - return [fakes.stub_instance(100)] - - self.stubs.Set(nova.db, 'instance_get_all_by_filters', - fake_get_all) - self.flags(allow_admin_api=True) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?tenant_id=fake', - use_admin_context=True) - res = self.controller.index(req) - - self.assertTrue('servers' in res) - - def test_get_servers_allows_flavor(self): - server_uuid = str(utils.gen_uuid()) - - def fake_get_all(compute_self, context, search_opts=None): - self.assertNotEqual(search_opts, None) - self.assertTrue('flavor' in search_opts) - # flavor is an integer ID - self.assertEqual(search_opts['flavor'], '12345') - return [fakes.stub_instance(100, uuid=server_uuid)] - - self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) - self.flags(allow_admin_api=False) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?flavor=12345') - servers = self.controller.index(req)['servers'] - - self.assertEqual(len(servers), 1) - self.assertEqual(servers[0]['id'], server_uuid) - - def test_get_servers_allows_status(self): - server_uuid = str(utils.gen_uuid()) - - def fake_get_all(compute_self, context, search_opts=None): - self.assertNotEqual(search_opts, None) - self.assertTrue('vm_state' in search_opts) - self.assertEqual(search_opts['vm_state'], vm_states.ACTIVE) - return [fakes.stub_instance(100, uuid=server_uuid)] - - self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) - self.flags(allow_admin_api=False) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?status=active') - servers = self.controller.index(req)['servers'] - - self.assertEqual(len(servers), 1) - self.assertEqual(servers[0]['id'], server_uuid) - - def test_get_servers_invalid_status(self): - """Test getting servers by invalid status""" - self.flags(allow_admin_api=False) - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?status=unknown') - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index, req) - - def test_get_servers_allows_name(self): - server_uuid = str(utils.gen_uuid()) - - def fake_get_all(compute_self, context, search_opts=None): - self.assertNotEqual(search_opts, None) - self.assertTrue('name' in search_opts) - self.assertEqual(search_opts['name'], 'whee.*') - return [fakes.stub_instance(100, uuid=server_uuid)] - - self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) - self.flags(allow_admin_api=False) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?name=whee.*') - servers = self.controller.index(req)['servers'] - - self.assertEqual(len(servers), 1) - self.assertEqual(servers[0]['id'], server_uuid) - - def test_get_servers_allows_changes_since(self): - server_uuid = str(utils.gen_uuid()) - - def fake_get_all(compute_self, context, search_opts=None): - self.assertNotEqual(search_opts, None) - self.assertTrue('changes-since' in search_opts) - changes_since = datetime.datetime(2011, 1, 24, 17, 8, 1) - self.assertEqual(search_opts['changes-since'], changes_since) - self.assertTrue('deleted' not in search_opts) - return [fakes.stub_instance(100, uuid=server_uuid)] - - self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) - - params = 'changes-since=2011-01-24T17:08:01Z' - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?%s' % params) - servers = self.controller.index(req)['servers'] - - self.assertEqual(len(servers), 1) - self.assertEqual(servers[0]['id'], server_uuid) - - def test_get_servers_allows_changes_since_bad_value(self): - params = 'changes-since=asdf' - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?%s' % params) - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index, req) - - def test_get_servers_unknown_or_admin_options1(self): - """Test getting servers by admin-only or unknown options. - This tests when admin_api is off. Make sure the admin and - unknown options are stripped before they get to - compute_api.get_all() - """ - - self.flags(allow_admin_api=False) - - server_uuid = str(utils.gen_uuid()) - - def fake_get_all(compute_self, context, search_opts=None): - self.assertNotEqual(search_opts, None) - # Allowed by user - self.assertTrue('name' in search_opts) - self.assertTrue('status' in search_opts) - # Allowed only by admins with admin API on - self.assertFalse('ip' in search_opts) - self.assertFalse('unknown_option' in search_opts) - return [fakes.stub_instance(100, uuid=server_uuid)] - - self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) - - query_str = "name=foo&ip=10.*&status=active&unknown_option=meow" - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?%s' % query_str, - use_admin_context=True) - res = self.controller.index(req) - - servers = res['servers'] - self.assertEqual(len(servers), 1) - self.assertEqual(servers[0]['id'], server_uuid) - - def test_get_servers_unknown_or_admin_options2(self): - """Test getting servers by admin-only or unknown options. - This tests when admin_api is on, but context is a user. - Make sure the admin and unknown options are stripped before - they get to compute_api.get_all() - """ - - self.flags(allow_admin_api=True) - - server_uuid = str(utils.gen_uuid()) - - def fake_get_all(compute_self, context, search_opts=None): - self.assertNotEqual(search_opts, None) - # Allowed by user - self.assertTrue('name' in search_opts) - self.assertTrue('status' in search_opts) - # Allowed only by admins with admin API on - self.assertFalse('ip' in search_opts) - self.assertFalse('unknown_option' in search_opts) - return [fakes.stub_instance(100, uuid=server_uuid)] - - self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) - - query_str = "name=foo&ip=10.*&status=active&unknown_option=meow" - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?%s' % query_str) - res = self.controller.index(req) - - servers = res['servers'] - self.assertEqual(len(servers), 1) - self.assertEqual(servers[0]['id'], server_uuid) - - def test_get_servers_unknown_or_admin_options3(self): - """Test getting servers by admin-only or unknown options. - This tests when admin_api is on and context is admin. - All options should be passed through to compute_api.get_all() - """ - - self.flags(allow_admin_api=True) - - server_uuid = str(utils.gen_uuid()) - - def fake_get_all(compute_self, context, search_opts=None): - self.assertNotEqual(search_opts, None) - # Allowed by user - self.assertTrue('name' in search_opts) - self.assertTrue('status' in search_opts) - # Allowed only by admins with admin API on - self.assertTrue('ip' in search_opts) - self.assertTrue('unknown_option' in search_opts) - return [fakes.stub_instance(100, uuid=server_uuid)] - - self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) - - query_str = "name=foo&ip=10.*&status=active&unknown_option=meow" - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?%s' % query_str, - use_admin_context=True) - servers = self.controller.index(req)['servers'] - - self.assertEqual(len(servers), 1) - self.assertEqual(servers[0]['id'], server_uuid) - - def test_get_servers_admin_allows_ip(self): - """Test getting servers by ip with admin_api enabled and - admin context - """ - self.flags(allow_admin_api=True) - - server_uuid = str(utils.gen_uuid()) - - def fake_get_all(compute_self, context, search_opts=None): - self.assertNotEqual(search_opts, None) - self.assertTrue('ip' in search_opts) - self.assertEqual(search_opts['ip'], '10\..*') - return [fakes.stub_instance(100, uuid=server_uuid)] - - self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?ip=10\..*', - use_admin_context=True) - servers = self.controller.index(req)['servers'] - - self.assertEqual(len(servers), 1) - self.assertEqual(servers[0]['id'], server_uuid) - - def test_get_servers_admin_allows_ip6(self): - """Test getting servers by ip6 with admin_api enabled and - admin context - """ - self.flags(allow_admin_api=True) - - server_uuid = str(utils.gen_uuid()) - - def fake_get_all(compute_self, context, search_opts=None): - self.assertNotEqual(search_opts, None) - self.assertTrue('ip6' in search_opts) - self.assertEqual(search_opts['ip6'], 'ffff.*') - return [fakes.stub_instance(100, uuid=server_uuid)] - - self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers?ip6=ffff.*', - use_admin_context=True) - servers = self.controller.index(req)['servers'] - - self.assertEqual(len(servers), 1) - self.assertEqual(servers[0]['id'], server_uuid) - - def test_update_server_no_body(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) - req.method = 'PUT' - - self.assertRaises(webob.exc.HTTPUnprocessableEntity, - self.controller.update, req, FAKE_UUID, None) - - def test_update_server_all_attributes(self): - self.stubs.Set(nova.db, 'instance_get', - return_server_with_attributes(name='server_test', - access_ipv4='0.0.0.0', - access_ipv6='beef::0123')) - req = fakes.HTTPRequest.blank('/v1.1/123/servers/%s' % FAKE_UUID) - req.method = 'PUT' - req.content_type = 'application/json' - body = {'server': { - 'name': 'server_test', - 'accessIPv4': '0.0.0.0', - 'accessIPv6': 'beef::0123', - }} - req.body = json.dumps(body) - res_dict = self.controller.update(req, FAKE_UUID, body) - - self.assertEqual(res_dict['server']['id'], FAKE_UUID) - self.assertEqual(res_dict['server']['name'], 'server_test') - self.assertEqual(res_dict['server']['accessIPv4'], '0.0.0.0') - self.assertEqual(res_dict['server']['accessIPv6'], 'beef::0123') - - def test_update_server_name(self): - self.stubs.Set(nova.db, 'instance_get', - return_server_with_attributes(name='server_test')) - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) - req.method = 'PUT' - req.content_type = 'application/json' - body = {'server': {'name': 'server_test'}} - req.body = json.dumps(body) - res_dict = self.controller.update(req, FAKE_UUID, body) - - self.assertEqual(res_dict['server']['id'], FAKE_UUID) - self.assertEqual(res_dict['server']['name'], 'server_test') - - def test_update_server_access_ipv4(self): - self.stubs.Set(nova.db, 'instance_get', - return_server_with_attributes(access_ipv4='0.0.0.0')) - req = fakes.HTTPRequest.blank('/v1.1/123/servers/%s' % FAKE_UUID) - req.method = 'PUT' - req.content_type = 'application/json' - body = {'server': {'accessIPv4': '0.0.0.0'}} - req.body = json.dumps(body) - res_dict = self.controller.update(req, FAKE_UUID, body) - - self.assertEqual(res_dict['server']['id'], FAKE_UUID) - self.assertEqual(res_dict['server']['accessIPv4'], '0.0.0.0') - - def test_update_server_access_ipv6(self): - self.stubs.Set(nova.db, 'instance_get', - return_server_with_attributes(access_ipv6='beef::0123')) - req = fakes.HTTPRequest.blank('/v1.1/123/servers/%s' % FAKE_UUID) - req.method = 'PUT' - req.content_type = 'application/json' - body = {'server': {'accessIPv6': 'beef::0123'}} - req.body = json.dumps(body) - res_dict = self.controller.update(req, FAKE_UUID, body) - - self.assertEqual(res_dict['server']['id'], FAKE_UUID) - self.assertEqual(res_dict['server']['accessIPv6'], 'beef::0123') - - def test_update_server_adminPass_ignored(self): - inst_dict = dict(name='server_test', adminPass='bacon') - body = dict(server=inst_dict) - - def server_update(context, id, params): - filtered_dict = { - 'display_name': 'server_test', - } - self.assertEqual(params, filtered_dict) - return filtered_dict - - self.stubs.Set(nova.db, 'instance_update', server_update) - self.stubs.Set(nova.db, 'instance_get', - return_server_with_attributes(name='server_test')) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) - req.method = 'PUT' - req.content_type = "application/json" - req.body = json.dumps(body) - res_dict = self.controller.update(req, FAKE_UUID, body) - - self.assertEqual(res_dict['server']['id'], FAKE_UUID) - self.assertEqual(res_dict['server']['name'], 'server_test') - - def test_get_all_server_details(self): - expected_flavor = { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": 'http://localhost/fake/flavors/1', - }, - ], - } - expected_image = { - "id": "10", - "links": [ - { - "rel": "bookmark", - "href": 'http://localhost/fake/images/10', - }, - ], - } - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/detail') - res_dict = self.controller.detail(req) - - for i, s in enumerate(res_dict['servers']): - self.assertEqual(s['id'], get_fake_uuid(i)) - self.assertEqual(s['hostId'], '') - self.assertEqual(s['name'], 'server%d' % i) - self.assertEqual(s['image'], expected_image) - self.assertEqual(s['flavor'], expected_flavor) - self.assertEqual(s['status'], 'BUILD') - self.assertEqual(s['metadata']['seq'], str(i)) - - def test_get_all_server_details_with_host(self): - ''' - We want to make sure that if two instances are on the same host, then - they return the same hostId. If two instances are on different hosts, - they should return different hostId's. In this test, there are 5 - instances - 2 on one host and 3 on another. - ''' - - def return_servers_with_host(context, *args, **kwargs): - return [fakes.stub_instance(i, 'fake', 'fake', i % 2, - uuid=get_fake_uuid(i)) - for i in xrange(5)] - - self.stubs.Set(nova.db, 'instance_get_all_by_filters', - return_servers_with_host) - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/detail') - res_dict = self.controller.detail(req) - - server_list = res_dict['servers'] - host_ids = [server_list[0]['hostId'], server_list[1]['hostId']] - self.assertTrue(host_ids[0] and host_ids[1]) - self.assertNotEqual(host_ids[0], host_ids[1]) - - for i, s in enumerate(server_list): - self.assertEqual(s['id'], get_fake_uuid(i)) - self.assertEqual(s['hostId'], host_ids[i % 2]) - self.assertEqual(s['name'], 'server%d' % i) - - def test_delete_server_instance(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) - req.method = 'DELETE' - - self.server_delete_called = False - - def instance_destroy_mock(context, id): - self.server_delete_called = True - - self.stubs.Set(nova.db, 'instance_destroy', - instance_destroy_mock) - - self.controller.delete(req, FAKE_UUID) - - self.assertEqual(self.server_delete_called, True) - - -class ServerStatusTest(test.TestCase): - - def setUp(self): - super(ServerStatusTest, self).setUp() - fakes.stub_out_nw_api(self.stubs) - - self.controller = servers.Controller() - - def _get_with_state(self, vm_state, task_state=None): - new_server = return_server_with_state(vm_state, task_state) - self.stubs.Set(nova.db, 'instance_get_by_uuid', new_server) - self.stubs.Set(nova.db, 'instance_get', new_server) - - request = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) - return self.controller.show(request, FAKE_UUID) - - def test_active(self): - response = self._get_with_state(vm_states.ACTIVE) - self.assertEqual(response['server']['status'], 'ACTIVE') - - def test_reboot(self): - response = self._get_with_state(vm_states.ACTIVE, - task_states.REBOOTING) - self.assertEqual(response['server']['status'], 'REBOOT') - - def test_reboot_hard(self): - response = self._get_with_state(vm_states.ACTIVE, - task_states.REBOOTING_HARD) - self.assertEqual(response['server']['status'], 'HARD_REBOOT') - - def test_rebuild(self): - response = self._get_with_state(vm_states.REBUILDING) - self.assertEqual(response['server']['status'], 'REBUILD') - - def test_rebuild_error(self): - response = self._get_with_state(vm_states.ERROR) - self.assertEqual(response['server']['status'], 'ERROR') - - def test_resize(self): - response = self._get_with_state(vm_states.RESIZING) - self.assertEqual(response['server']['status'], 'RESIZE') - - def test_verify_resize(self): - response = self._get_with_state(vm_states.ACTIVE, - task_states.RESIZE_VERIFY) - self.assertEqual(response['server']['status'], 'VERIFY_RESIZE') - - def test_password_update(self): - response = self._get_with_state(vm_states.ACTIVE, - task_states.UPDATING_PASSWORD) - self.assertEqual(response['server']['status'], 'PASSWORD') - - def test_stopped(self): - response = self._get_with_state(vm_states.STOPPED) - self.assertEqual(response['server']['status'], 'STOPPED') - - -class ServersControllerCreateTest(test.TestCase): - - def setUp(self): - """Shared implementation for tests below that create instance""" - super(ServersControllerCreateTest, self).setUp() - - self.maxDiff = None - self.flags(verbose=True) - self.config_drive = None - self.instance_cache_num = 0 - self.instance_cache = {} - - self.controller = servers.Controller() - - def instance_create(context, inst): - inst_type = instance_types.get_instance_type_by_flavor_id(3) - image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - def_image_ref = 'http://localhost/images/%s' % image_uuid - self.instance_cache_num += 1 - instance = { - 'id': self.instance_cache_num, - 'display_name': inst['display_name'] or 'test', - 'uuid': FAKE_UUID, - 'instance_type': dict(inst_type), - 'access_ip_v4': '1.2.3.4', - 'access_ip_v6': 'fead::1234', - 'image_ref': inst.get('image_ref', def_image_ref), - 'user_id': 'fake', - 'project_id': 'fake', - 'reservation_id': inst['reservation_id'], - "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), - "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), - "config_drive": self.config_drive, - "progress": 0, - "fixed_ips": [] - } - self.instance_cache[instance['id']] = instance - return instance - - def instance_get(context, instance_id): - """Stub for compute/api create() pulling in instance after - scheduling - """ - return self.instance_cache[instance_id] - - def rpc_call_wrapper(context, topic, msg): - """Stub out the scheduler creating the instance entry""" - if topic == FLAGS.scheduler_topic and \ - msg['method'] == 'run_instance': - request_spec = msg['args']['request_spec'] - num_instances = request_spec.get('num_instances', 1) - instances = [] - for x in xrange(num_instances): - instances.append(instance_create(context, - request_spec['instance_properties'])) - return instances - - def server_update(context, instance_id, params): - inst = self.instance_cache[instance_id] - inst.update(params) - return inst - - def fake_method(*args, **kwargs): - pass - - def project_get_networks(context, user_id): - return dict(id='1', host='localhost') - - def queue_get_for(context, *args): - return 'network_topic' - - fakes.stub_out_networking(self.stubs) - fakes.stub_out_rate_limiting(self.stubs) - fakes.stub_out_key_pair_funcs(self.stubs) - fakes.stub_out_image_service(self.stubs) - fakes.stub_out_nw_api(self.stubs) - self.stubs.Set(utils, 'gen_uuid', fake_gen_uuid) - self.stubs.Set(nova.db, 'instance_add_security_group', - return_security_group) - self.stubs.Set(nova.db, 'project_get_networks', - project_get_networks) - self.stubs.Set(nova.db, 'instance_create', instance_create) - self.stubs.Set(nova.db, 'instance_get', instance_get) - self.stubs.Set(nova.rpc, 'cast', fake_method) - self.stubs.Set(nova.rpc, 'call', rpc_call_wrapper) - self.stubs.Set(nova.db, 'instance_update', server_update) - self.stubs.Set(nova.db, 'queue_get_for', queue_get_for) - self.stubs.Set(nova.network.manager.VlanManager, 'allocate_fixed_ip', - fake_method) - self.stubs.Set(nova.compute.api.API, "_find_host", find_host) - - def _test_create_instance(self): - image_uuid = 'c905cedb-7281-47e4-8a62-f26bc5fc4c77' - body = dict(server=dict( - name='server_test', imageRef=image_uuid, flavorRef=2, - metadata={'hello': 'world', 'open': 'stack'}, - personality={})) - req = fakes.HTTPRequest.blank('/v1.1/fake/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - server = self.controller.create(req, body)['server'] - - self.assertEqual(FLAGS.password_length, len(server['adminPass'])) - self.assertEqual(FAKE_UUID, server['id']) - - def test_create_multiple_instances(self): - """Test creating multiple instances but not asking for - reservation_id - """ - image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - flavor_ref = 'http://localhost/123/flavors/3' - body = { - 'server': { - 'min_count': 2, - 'name': 'server_test', - 'imageRef': image_href, - 'flavorRef': flavor_ref, - 'metadata': {'hello': 'world', - 'open': 'stack'}, - 'personality': [] - } - } - - req = fakes.HTTPRequest.blank('/v1.1/123/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - res = self.controller.create(req, body) - - self.assertEqual(FAKE_UUID, res["server"]["id"]) - self.assertEqual(12, len(res["server"]["adminPass"])) - - def test_create_multiple_instances_resv_id_return(self): - """Test creating multiple instances with asking for - reservation_id - """ - image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - flavor_ref = 'http://localhost/123/flavors/3' - body = { - 'server': { - 'min_count': 2, - 'name': 'server_test', - 'imageRef': image_href, - 'flavorRef': flavor_ref, - 'metadata': {'hello': 'world', - 'open': 'stack'}, - 'personality': [], - 'return_reservation_id': True - } - } - - req = fakes.HTTPRequest.blank('/v1.1/123/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - res = self.controller.create(req, body) - - reservation_id = res.get('reservation_id') - self.assertNotEqual(reservation_id, "") - self.assertNotEqual(reservation_id, None) - self.assertTrue(len(reservation_id) > 1) - - def test_create_instance_with_user_supplied_reservation_id(self): - """Non-admin supplied reservation_id should be ignored.""" - image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - flavor_ref = 'http://localhost/123/flavors/3' - body = { - 'server': { - 'name': 'server_test', - 'imageRef': image_href, - 'flavorRef': flavor_ref, - 'metadata': {'hello': 'world', - 'open': 'stack'}, - 'personality': [], - 'reservation_id': 'myresid', - 'return_reservation_id': True - } - } - - req = fakes.HTTPRequest.blank('/v1.1/123/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - res = self.controller.create(req, body) - - self.assertIn('reservation_id', res) - self.assertNotEqual(res['reservation_id'], 'myresid') - - def test_create_instance_with_admin_supplied_reservation_id(self): - """Admin supplied reservation_id should be honored.""" - image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - flavor_ref = 'http://localhost/123/flavors/3' - body = { - 'server': { - 'name': 'server_test', - 'imageRef': image_href, - 'flavorRef': flavor_ref, - 'metadata': {'hello': 'world', - 'open': 'stack'}, - 'personality': [], - 'reservation_id': 'myresid', - 'return_reservation_id': True - } - } - - req = fakes.HTTPRequest.blank('/v1.1/123/servers', - use_admin_context=True) - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - res = self.controller.create(req, body) - - reservation_id = res['reservation_id'] - self.assertEqual(reservation_id, "myresid") - - def test_create_instance_no_key_pair(self): - fakes.stub_out_key_pair_funcs(self.stubs, have_key_pair=False) - self._test_create_instance() - - def test_create_instance_with_access_ip(self): - # proper local hrefs must start with 'http://localhost/v1.1/' - image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - image_href = 'http://localhost/v1.1/fake/images/%s' % image_uuid - flavor_ref = 'http://localhost/fake/flavors/3' - access_ipv4 = '1.2.3.4' - access_ipv6 = 'fead::1234' - expected_flavor = { - "id": "3", - "links": [ - { - "rel": "bookmark", - "href": 'http://localhost/fake/flavors/3', - }, - ], - } - expected_image = { - "id": image_uuid, - "links": [ - { - "rel": "bookmark", - "href": 'http://localhost/fake/images/%s' % image_uuid, - }, - ], - } - body = { - 'server': { - 'name': 'server_test', - 'imageRef': image_href, - 'flavorRef': flavor_ref, - 'accessIPv4': access_ipv4, - 'accessIPv6': access_ipv6, - 'metadata': { - 'hello': 'world', - 'open': 'stack', - }, - 'personality': [ - { - "path": "/etc/banner.txt", - "contents": "MQ==", - }, - ], - }, - } - - req = fakes.HTTPRequest.blank('/v1.1/123/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - res = self.controller.create(req, body) - - server = res['server'] - self.assertEqual(FLAGS.password_length, len(server['adminPass'])) - self.assertEqual(FAKE_UUID, server['id']) - - def test_create_instance(self): - # proper local hrefs must start with 'http://localhost/v1.1/' - image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - image_href = 'http://localhost/v1.1/images/%s' % image_uuid - flavor_ref = 'http://localhost/123/flavors/3' - body = { - 'server': { - 'name': 'server_test', - 'imageRef': image_href, - 'flavorRef': flavor_ref, - 'metadata': { - 'hello': 'world', - 'open': 'stack', - }, - 'personality': [ - { - "path": "/etc/banner.txt", - "contents": "MQ==", - }, - ], - }, - } - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - res = self.controller.create(req, body) - - server = res['server'] - self.assertEqual(FLAGS.password_length, len(server['adminPass'])) - self.assertEqual(FAKE_UUID, server['id']) - - def test_create_instance_invalid_key_name(self): - image_href = 'http://localhost/v1.1/images/2' - flavor_ref = 'http://localhost/flavors/3' - body = dict(server=dict( - name='server_test', imageRef=image_href, flavorRef=flavor_ref, - key_name='nonexistentkey')) - req = fakes.HTTPRequest.blank('/v1.1/fake/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.create, req, body) - - def test_create_instance_valid_key_name(self): - image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - flavor_ref = 'http://localhost/flavors/3' - body = dict(server=dict( - name='server_test', imageRef=image_href, flavorRef=flavor_ref, - key_name='key')) - req = fakes.HTTPRequest.blank('/v1.1/fake/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - res = self.controller.create(req, body) - - self.assertEqual(FAKE_UUID, res["server"]["id"]) - self.assertEqual(12, len(res["server"]["adminPass"])) - - def test_create_instance_invalid_flavor_href(self): - image_href = 'http://localhost/v1.1/images/2' - flavor_ref = 'http://localhost/v1.1/flavors/asdf' - body = dict(server=dict( - name='server_test', imageRef=image_href, flavorRef=flavor_ref, - metadata={'hello': 'world', 'open': 'stack'}, - personality={})) - req = fakes.HTTPRequest.blank('/v1.1/fake/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.create, req, body) - - def test_create_instance_invalid_flavor_id_int(self): - image_href = 'http://localhost/v1.1/123/images/2' - flavor_ref = -1 - body = dict(server=dict( - name='server_test', imageRef=image_href, flavorRef=flavor_ref, - metadata={'hello': 'world', 'open': 'stack'}, - personality={})) - req = fakes.HTTPRequest.blank('/v1.1/123/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.create, req, body) - - def test_create_instance_bad_flavor_href(self): - image_href = 'http://localhost/v1.1/images/2' - flavor_ref = 'http://localhost/v1.1/flavors/17' - body = dict(server=dict( - name='server_test', imageRef=image_href, flavorRef=flavor_ref, - metadata={'hello': 'world', 'open': 'stack'}, - personality={})) - req = fakes.HTTPRequest.blank('/v1.1/fake/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.create, req, body) - - def test_create_instance_with_config_drive(self): - self.config_drive = True - image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - flavor_ref = 'http://localhost/v1.1/123/flavors/3' - body = { - 'server': { - 'name': 'config_drive_test', - 'imageRef': image_href, - 'flavorRef': flavor_ref, - 'metadata': { - 'hello': 'world', - 'open': 'stack', - }, - 'personality': {}, - 'config_drive': True, - }, - } - - req = fakes.HTTPRequest.blank('/v1.1/123/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - res = self.controller.create(req, body) - - server = res['server'] - self.assertEqual(FAKE_UUID, server['id']) - - def test_create_instance_with_config_drive_as_id(self): - self.config_drive = 2 - image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - flavor_ref = 'http://localhost/v1.1/123/flavors/3' - body = { - 'server': { - 'name': 'config_drive_test', - 'imageRef': image_href, - 'flavorRef': flavor_ref, - 'metadata': { - 'hello': 'world', - 'open': 'stack', - }, - 'personality': {}, - 'config_drive': image_href, - }, - } - - req = fakes.HTTPRequest.blank('/v1.1/123/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - res = self.controller.create(req, body) - - server = res['server'] - self.assertEqual(FAKE_UUID, server['id']) - - def test_create_instance_with_bad_config_drive(self): - self.config_drive = "asdf" - image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - flavor_ref = 'http://localhost/v1.1/123/flavors/3' - body = { - 'server': { - 'name': 'config_drive_test', - 'imageRef': image_href, - 'flavorRef': flavor_ref, - 'metadata': { - 'hello': 'world', - 'open': 'stack', - }, - 'personality': {}, - 'config_drive': 'asdf', - }, - } - - req = fakes.HTTPRequest.blank('/v1.1/123/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.create, req, body) - - def test_create_instance_without_config_drive(self): - image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - flavor_ref = 'http://localhost/v1.1/123/flavors/3' - body = { - 'server': { - 'name': 'config_drive_test', - 'imageRef': image_href, - 'flavorRef': flavor_ref, - 'metadata': { - 'hello': 'world', - 'open': 'stack', - }, - 'personality': {}, - 'config_drive': True, - }, - } - - req = fakes.HTTPRequest.blank('/v1.1/123/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - res = self.controller.create(req, body) - - server = res['server'] - self.assertEqual(FAKE_UUID, server['id']) - - def test_create_instance_bad_href(self): - image_href = 'asdf' - flavor_ref = 'http://localhost/v1.1/flavors/3' - body = dict(server=dict( - name='server_test', imageRef=image_href, flavorRef=flavor_ref, - metadata={'hello': 'world', 'open': 'stack'}, - personality={})) - req = fakes.HTTPRequest.blank('/v1.1/fake/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.create, req, body) - - def test_create_instance_local_href(self): - image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - flavor_ref = 'http://localhost/v1.1/flavors/3' - body = { - 'server': { - 'name': 'server_test', - 'imageRef': image_uuid, - 'flavorRef': flavor_ref, - }, - } - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - res = self.controller.create(req, body) - - server = res['server'] - self.assertEqual(FAKE_UUID, server['id']) - - def test_create_instance_admin_pass(self): - image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' - body = { - 'server': { - 'name': 'server_test', - 'imageRef': image_uuid, - 'flavorRef': 3, - 'adminPass': 'testpass', - }, - } - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers['content-type'] = "application/json" - res = self.controller.create(req, body) - - server = res['server'] - self.assertEqual(server['adminPass'], body['server']['adminPass']) - - def test_create_instance_admin_pass_empty(self): - body = { - 'server': { - 'name': 'server_test', - 'imageRef': 3, - 'flavorRef': 3, - 'adminPass': '', - }, - } - - req = fakes.HTTPRequest.blank('/v1.1/fake/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers['content-type'] = "application/json" - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.create, req, body) - - def test_create_instance_malformed_entity(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/servers') - req.method = 'POST' - body = {'server': 'string'} - req.body = json.dumps(body) - req.headers['content-type'] = "application/json" - - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.create, req, body) - - -class TestServerCreateRequestXMLDeserializer(test.TestCase): - - def setUp(self): - super(TestServerCreateRequestXMLDeserializer, self).setUp() - self.deserializer = servers.ServerXMLDeserializer() - - def test_minimal_request(self): - serial_request = """ -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = { - "server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "2", - }, - } - self.assertEquals(request['body'], expected) - - def test_access_ipv4(self): - serial_request = """ -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = { - "server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "2", - "accessIPv4": "1.2.3.4", - }, - } - self.assertEquals(request['body'], expected) - - def test_access_ipv6(self): - serial_request = """ -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = { - "server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "2", - "accessIPv6": "fead::1234", - }, - } - self.assertEquals(request['body'], expected) - - def test_access_ip(self): - serial_request = """ -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = { - "server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "2", - "accessIPv4": "1.2.3.4", - "accessIPv6": "fead::1234", - }, - } - self.assertEquals(request['body'], expected) - - def test_admin_pass(self): - serial_request = """ -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = { - "server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "2", - "adminPass": "1234", - }, - } - self.assertEquals(request['body'], expected) - - def test_image_link(self): - serial_request = """ -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = { - "server": { - "name": "new-server-test", - "imageRef": "http://localhost:8774/v1.1/images/2", - "flavorRef": "3", - }, - } - self.assertEquals(request['body'], expected) - - def test_flavor_link(self): - serial_request = """ -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = { - "server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "http://localhost:8774/v1.1/flavors/3", - }, - } - self.assertEquals(request['body'], expected) - - def test_empty_metadata_personality(self): - serial_request = """ - - - -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = { - "server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "2", - "metadata": {}, - "personality": [], - }, - } - self.assertEquals(request['body'], expected) - - def test_multiple_metadata_items(self): - serial_request = """ - - - two - snack - -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = { - "server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "2", - "metadata": {"one": "two", "open": "snack"}, - }, - } - self.assertEquals(request['body'], expected) - - def test_multiple_personality_files(self): - serial_request = """ - - - MQ== - Mg== - -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = { - "server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "2", - "personality": [ - {"path": "/etc/banner.txt", "contents": "MQ=="}, - {"path": "/etc/hosts", "contents": "Mg=="}, - ], - }, - } - self.assertDictMatch(request['body'], expected) - - def test_spec_request(self): - image_bookmark_link = "http://servers.api.openstack.org/1234/" + \ - "images/52415800-8b69-11e0-9b19-734f6f006e54" - serial_request = """ - - - Apache1 - - - Mg== - -""" % (image_bookmark_link) - request = self.deserializer.deserialize(serial_request, 'create') - expected = { - "server": { - "name": "new-server-test", - "imageRef": "http://servers.api.openstack.org/1234/" + \ - "images/52415800-8b69-11e0-9b19-734f6f006e54", - "flavorRef": "52415800-8b69-11e0-9b19-734f1195ff37", - "metadata": {"My Server Name": "Apache1"}, - "personality": [ - { - "path": "/etc/banner.txt", - "contents": "Mg==", - }, - ], - }, - } - self.assertEquals(request['body'], expected) - - def test_request_with_empty_networks(self): - serial_request = """ - - -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "1", - "networks": [], - }} - self.assertEquals(request['body'], expected) - - def test_request_with_one_network(self): - serial_request = """ - - - - -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "1", - "networks": [{"uuid": "1", "fixed_ip": "10.0.1.12"}], - }} - self.assertEquals(request['body'], expected) - - def test_request_with_two_networks(self): - serial_request = """ - - - - - -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "1", - "networks": [{"uuid": "1", "fixed_ip": "10.0.1.12"}, - {"uuid": "2", "fixed_ip": "10.0.2.12"}], - }} - self.assertEquals(request['body'], expected) - - def test_request_with_second_network_node_ignored(self): - serial_request = """ - - - - - - - -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "1", - "networks": [{"uuid": "1", "fixed_ip": "10.0.1.12"}], - }} - self.assertEquals(request['body'], expected) - - def test_request_with_one_network_missing_id(self): - serial_request = """ - - - - -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "1", - "networks": [{"fixed_ip": "10.0.1.12"}], - }} - self.assertEquals(request['body'], expected) - - def test_request_with_one_network_missing_fixed_ip(self): - serial_request = """ - - - - -""" - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "1", - "networks": [{"uuid": "1"}], - }} - self.assertEquals(request['body'], expected) - - def test_request_with_one_network_empty_id(self): - serial_request = """ - - - - - """ - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "1", - "networks": [{"uuid": "", "fixed_ip": "10.0.1.12"}], - }} - self.assertEquals(request['body'], expected) - - def test_request_with_one_network_empty_fixed_ip(self): - serial_request = """ - - - - - """ - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "1", - "networks": [{"uuid": "1", "fixed_ip": ""}], - }} - self.assertEquals(request['body'], expected) - - def test_request_with_networks_duplicate_ids(self): - serial_request = """ - - - - - - """ - request = self.deserializer.deserialize(serial_request, 'create') - expected = {"server": { - "name": "new-server-test", - "imageRef": "1", - "flavorRef": "1", - "networks": [{"uuid": "1", "fixed_ip": "10.0.1.12"}, - {"uuid": "1", "fixed_ip": "10.0.2.12"}], - }} - self.assertEquals(request['body'], expected) - - -class TestAddressesXMLSerialization(test.TestCase): - - serializer = nova.api.openstack.ips.IPXMLSerializer() - - def test_xml_declaration(self): - fixture = { - 'network_2': [ - {'addr': '192.168.0.1', 'version': 4}, - {'addr': 'fe80::beef', 'version': 6}, - ], - } - output = self.serializer.serialize(fixture, 'show') - has_dec = output.startswith("") - self.assertTrue(has_dec) - - def test_show(self): - fixture = { - 'network_2': [ - {'addr': '192.168.0.1', 'version': 4}, - {'addr': 'fe80::beef', 'version': 6}, - ], - } - output = self.serializer.serialize(fixture, 'show') - root = etree.XML(output) - network = fixture['network_2'] - self.assertEqual(str(root.get('id')), 'network_2') - ip_elems = root.findall('{0}ip'.format(NS)) - for z, ip_elem in enumerate(ip_elems): - ip = network[z] - self.assertEqual(str(ip_elem.get('version')), - str(ip['version'])) - self.assertEqual(str(ip_elem.get('addr')), - str(ip['addr'])) - - def test_index(self): - fixture = { - 'addresses': { - 'network_1': [ - {'addr': '192.168.0.3', 'version': 4}, - {'addr': '192.168.0.5', 'version': 4}, - ], - 'network_2': [ - {'addr': '192.168.0.1', 'version': 4}, - {'addr': 'fe80::beef', 'version': 6}, - ], - }, - } - output = self.serializer.serialize(fixture, 'index') - root = etree.XML(output) - xmlutil.validate_schema(root, 'addresses') - addresses_dict = fixture['addresses'] - network_elems = root.findall('{0}network'.format(NS)) - self.assertEqual(len(network_elems), 2) - for i, network_elem in enumerate(network_elems): - network = addresses_dict.items()[i] - self.assertEqual(str(network_elem.get('id')), str(network[0])) - ip_elems = network_elem.findall('{0}ip'.format(NS)) - for z, ip_elem in enumerate(ip_elems): - ip = network[1][z] - self.assertEqual(str(ip_elem.get('version')), - str(ip['version'])) - self.assertEqual(str(ip_elem.get('addr')), - str(ip['addr'])) - - -class ServersViewBuilderTest(test.TestCase): - - def setUp(self): - super(ServersViewBuilderTest, self).setUp() - self.flags(use_ipv6=True) - self.instance = fakes.stub_instance( - id=1, - image_ref="5", - uuid="deadbeef-feed-edee-beef-d0ea7beefedd", - display_name="test_server", - public_ips=["192.168.0.3"], - private_ips=["172.19.0.1"], - include_fake_metadata=False) - - self.uuid = self.instance['uuid'] - self.view_builder = nova.api.openstack.views.servers.ViewBuilder() - self.request = fakes.HTTPRequest.blank("/v1.1") - - def test_build_server(self): - self_link = "http://localhost/v1.1/fake/servers/%s" % self.uuid - bookmark_link = "http://localhost/fake/servers/%s" % self.uuid - expected_server = { - "server": { - "id": self.uuid, - "name": "test_server", - "links": [ - { - "rel": "self", - "href": self_link, - }, - { - "rel": "bookmark", - "href": bookmark_link, - }, - ], - } - } - - output = self.view_builder.basic(self.request, self.instance) - self.assertDictMatch(output, expected_server) - - def test_build_server_with_project_id(self): - expected_server = { - "server": { - "id": self.uuid, - "name": "test_server", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/fake/servers/%s" % - self.uuid, - }, - { - "rel": "bookmark", - "href": "http://localhost/fake/servers/%s" % self.uuid, - }, - ], - } - } - - output = self.view_builder.basic(self.request, self.instance) - self.assertDictMatch(output, expected_server) - - def test_build_server_detail(self): - image_bookmark = "http://localhost/fake/images/5" - flavor_bookmark = "http://localhost/fake/flavors/1" - self_link = "http://localhost/v1.1/fake/servers/%s" % self.uuid - bookmark_link = "http://localhost/fake/servers/%s" % self.uuid - expected_server = { - "server": { - "id": self.uuid, - "user_id": "fake", - "tenant_id": "fake", - "updated": "2010-11-11T11:00:00Z", - "created": "2010-10-10T12:00:00Z", - "progress": 0, - "name": "test_server", - "status": "BUILD", - "accessIPv4": "", - "accessIPv6": "", - "hostId": '', - "key_name": '', - "image": { - "id": "5", - "links": [ - { - "rel": "bookmark", - "href": image_bookmark, - }, - ], - }, - "flavor": { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": flavor_bookmark, - }, - ], - }, - "addresses": { - 'private': [ - {'version': 4, 'addr': '172.19.0.1'} - ], - 'public': [ - {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, - {'version': 4, 'addr': '192.168.0.3'}, - ], - }, - "metadata": {}, - "config_drive": None, - "links": [ - { - "rel": "self", - "href": self_link, - }, - { - "rel": "bookmark", - "href": bookmark_link, - }, - ], - } - } - - output = self.view_builder.show(self.request, self.instance) - self.assertDictMatch(output, expected_server) - - def test_build_server_detail_active_status(self): - #set the power state of the instance to running - self.instance['vm_state'] = vm_states.ACTIVE - self.instance['progress'] = 100 - image_bookmark = "http://localhost/fake/images/5" - flavor_bookmark = "http://localhost/fake/flavors/1" - self_link = "http://localhost/v1.1/fake/servers/%s" % self.uuid - bookmark_link = "http://localhost/fake/servers/%s" % self.uuid - expected_server = { - "server": { - "id": self.uuid, - "user_id": "fake", - "tenant_id": "fake", - "updated": "2010-11-11T11:00:00Z", - "created": "2010-10-10T12:00:00Z", - "progress": 100, - "name": "test_server", - "status": "ACTIVE", - "accessIPv4": "", - "accessIPv6": "", - "hostId": '', - "key_name": '', - "image": { - "id": "5", - "links": [ - { - "rel": "bookmark", - "href": image_bookmark, - }, - ], - }, - "flavor": { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": flavor_bookmark, - }, - ], - }, - "addresses": { - 'private': [ - {'version': 4, 'addr': '172.19.0.1'} - ], - 'public': [ - {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, - {'version': 4, 'addr': '192.168.0.3'}, - ], - }, - "metadata": {}, - "config_drive": None, - "links": [ - { - "rel": "self", - "href": self_link, - }, - { - "rel": "bookmark", - "href": bookmark_link, - }, - ], - } - } - - output = self.view_builder.show(self.request, self.instance) - self.assertDictMatch(output, expected_server) - - def test_build_server_detail_with_accessipv4(self): - - self.instance['access_ip_v4'] = '1.2.3.4' - - image_bookmark = "http://localhost/fake/images/5" - flavor_bookmark = "http://localhost/fake/flavors/1" - self_link = "http://localhost/v1.1/fake/servers/%s" % self.uuid - bookmark_link = "http://localhost/fake/servers/%s" % self.uuid - expected_server = { - "server": { - "id": self.uuid, - "user_id": "fake", - "tenant_id": "fake", - "updated": "2010-11-11T11:00:00Z", - "created": "2010-10-10T12:00:00Z", - "progress": 0, - "name": "test_server", - "key_name": "", - "status": "BUILD", - "hostId": '', - "image": { - "id": "5", - "links": [ - { - "rel": "bookmark", - "href": image_bookmark, - }, - ], - }, - "flavor": { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": flavor_bookmark, - }, - ], - }, - "addresses": { - 'private': [ - {'version': 4, 'addr': '172.19.0.1'} - ], - 'public': [ - {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, - {'version': 4, 'addr': '192.168.0.3'}, - ], - }, - "metadata": {}, - "config_drive": None, - "accessIPv4": "1.2.3.4", - "accessIPv6": "", - "links": [ - { - "rel": "self", - "href": self_link, - }, - { - "rel": "bookmark", - "href": bookmark_link, - }, - ], - } - } - - output = self.view_builder.show(self.request, self.instance) - self.assertDictMatch(output, expected_server) - - def test_build_server_detail_with_accessipv6(self): - - self.instance['access_ip_v6'] = 'fead::1234' - - image_bookmark = "http://localhost/fake/images/5" - flavor_bookmark = "http://localhost/fake/flavors/1" - self_link = "http://localhost/v1.1/fake/servers/%s" % self.uuid - bookmark_link = "http://localhost/fake/servers/%s" % self.uuid - expected_server = { - "server": { - "id": self.uuid, - "user_id": "fake", - "tenant_id": "fake", - "updated": "2010-11-11T11:00:00Z", - "created": "2010-10-10T12:00:00Z", - "progress": 0, - "name": "test_server", - "key_name": "", - "status": "BUILD", - "hostId": '', - "image": { - "id": "5", - "links": [ - { - "rel": "bookmark", - "href": image_bookmark, - }, - ], - }, - "flavor": { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": flavor_bookmark, - }, - ], - }, - "addresses": { - 'private': [ - {'version': 4, 'addr': '172.19.0.1'} - ], - 'public': [ - {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, - {'version': 4, 'addr': '192.168.0.3'}, - ] - }, - "metadata": {}, - "config_drive": None, - "accessIPv4": "", - "accessIPv6": "fead::1234", - "links": [ - { - "rel": "self", - "href": self_link, - }, - { - "rel": "bookmark", - "href": bookmark_link, - }, - ], - } - } - - output = self.view_builder.show(self.request, self.instance) - self.assertDictMatch(output, expected_server) - - def test_build_server_detail_with_metadata(self): - - metadata = [] - metadata.append(InstanceMetadata(key="Open", value="Stack")) - metadata.append(InstanceMetadata(key="Number", value=1)) - self.instance['metadata'] = metadata - - image_bookmark = "http://localhost/fake/images/5" - flavor_bookmark = "http://localhost/fake/flavors/1" - self_link = "http://localhost/v1.1/fake/servers/%s" % self.uuid - bookmark_link = "http://localhost/fake/servers/%s" % self.uuid - expected_server = { - "server": { - "id": self.uuid, - "user_id": "fake", - "tenant_id": "fake", - "updated": "2010-11-11T11:00:00Z", - "created": "2010-10-10T12:00:00Z", - "progress": 0, - "name": "test_server", - "status": "BUILD", - "accessIPv4": "", - "accessIPv6": "", - "hostId": '', - "key_name": '', - "image": { - "id": "5", - "links": [ - { - "rel": "bookmark", - "href": image_bookmark, - }, - ], - }, - "flavor": { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": flavor_bookmark, - }, - ], - }, - "addresses": { - 'private': [ - {'version': 4, 'addr': '172.19.0.1'} - ], - 'public': [ - {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, - {'version': 4, 'addr': '192.168.0.3'}, - ] - }, - "metadata": { - "Open": "Stack", - "Number": "1", - }, - "config_drive": None, - "links": [ - { - "rel": "self", - "href": self_link, - }, - { - "rel": "bookmark", - "href": bookmark_link, - }, - ], - } - } - - output = self.view_builder.show(self.request, self.instance) - self.assertDictMatch(output, expected_server) - - -class ServerXMLSerializationTest(test.TestCase): - - TIMESTAMP = "2010-10-11T10:30:22Z" - SERVER_HREF = 'http://localhost/v1.1/servers/%s' % FAKE_UUID - SERVER_NEXT = 'http://localhost/v1.1/servers?limit=%s&marker=%s' - SERVER_BOOKMARK = 'http://localhost/servers/%s' % FAKE_UUID - IMAGE_BOOKMARK = 'http://localhost/images/5' - FLAVOR_BOOKMARK = 'http://localhost/flavors/1' - - def setUp(self): - self.maxDiff = None - test.TestCase.setUp(self) - - def test_xml_declaration(self): - serializer = servers.ServerXMLSerializer() - - fixture = { - "server": { - 'id': FAKE_UUID, - 'user_id': 'fake_user_id', - 'tenant_id': 'fake_tenant_id', - 'created': self.TIMESTAMP, - 'updated': self.TIMESTAMP, - "progress": 0, - "name": "test_server", - "status": "BUILD", - "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', - "accessIPv4": "1.2.3.4", - "accessIPv6": "fead::1234", - "image": { - "id": "5", - "links": [ - { - "rel": "bookmark", - "href": self.IMAGE_BOOKMARK, - }, - ], - }, - "flavor": { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": self.FLAVOR_BOOKMARK, - }, - ], - }, - "addresses": { - "network_one": [ - { - "version": 4, - "addr": "67.23.10.138", - }, - { - "version": 6, - "addr": "::babe:67.23.10.138", - }, - ], - "network_two": [ - { - "version": 4, - "addr": "67.23.10.139", - }, - { - "version": 6, - "addr": "::babe:67.23.10.139", - }, - ], - }, - "metadata": { - "Open": "Stack", - "Number": "1", - }, - 'links': [ - { - 'href': self.SERVER_HREF, - 'rel': 'self', - }, - { - 'href': self.SERVER_BOOKMARK, - 'rel': 'bookmark', - }, - ], - } - } - - output = serializer.serialize(fixture, 'show') - print output - has_dec = output.startswith("") - self.assertTrue(has_dec) - - def test_show(self): - serializer = servers.ServerXMLSerializer() - - fixture = { - "server": { - "id": FAKE_UUID, - "user_id": "fake", - "tenant_id": "fake", - 'created': self.TIMESTAMP, - 'updated': self.TIMESTAMP, - "progress": 0, - "name": "test_server", - "status": "BUILD", - "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', - "key_name": '', - "accessIPv4": "1.2.3.4", - "accessIPv6": "fead::1234", - "image": { - "id": "5", - "links": [ - { - "rel": "bookmark", - "href": self.IMAGE_BOOKMARK, - }, - ], - }, - "flavor": { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": self.FLAVOR_BOOKMARK, - }, - ], - }, - "addresses": { - "network_one": [ - { - "version": 4, - "addr": "67.23.10.138", - }, - { - "version": 6, - "addr": "::babe:67.23.10.138", - }, - ], - "network_two": [ - { - "version": 4, - "addr": "67.23.10.139", - }, - { - "version": 6, - "addr": "::babe:67.23.10.139", - }, - ], - }, - "metadata": { - "Open": "Stack", - "Number": "1", - }, - 'links': [ - { - 'href': self.SERVER_HREF, - 'rel': 'self', - }, - { - 'href': self.SERVER_BOOKMARK, - 'rel': 'bookmark', - }, - ], - } - } - - output = serializer.serialize(fixture, 'show') - print output - root = etree.XML(output) - xmlutil.validate_schema(root, 'server') - - expected_server_href = self.SERVER_HREF - expected_server_bookmark = self.SERVER_BOOKMARK - expected_image_bookmark = self.IMAGE_BOOKMARK - expected_flavor_bookmark = self.FLAVOR_BOOKMARK - expected_now = self.TIMESTAMP - server_dict = fixture['server'] - - for key in ['name', 'id', 'created', 'accessIPv4', - 'updated', 'progress', 'status', 'hostId', - 'accessIPv6']: - self.assertEqual(root.get(key), str(server_dict[key])) - - link_nodes = root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(server_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - metadata_root = root.find('{0}metadata'.format(NS)) - metadata_elems = metadata_root.findall('{0}meta'.format(NS)) - self.assertEqual(len(metadata_elems), 2) - for i, metadata_elem in enumerate(metadata_elems): - (meta_key, meta_value) = server_dict['metadata'].items()[i] - self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) - self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) - - image_root = root.find('{0}image'.format(NS)) - self.assertEqual(image_root.get('id'), server_dict['image']['id']) - link_nodes = image_root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 1) - for i, link in enumerate(server_dict['image']['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - flavor_root = root.find('{0}flavor'.format(NS)) - self.assertEqual(flavor_root.get('id'), server_dict['flavor']['id']) - link_nodes = flavor_root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 1) - for i, link in enumerate(server_dict['flavor']['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - addresses_root = root.find('{0}addresses'.format(NS)) - addresses_dict = server_dict['addresses'] - network_elems = addresses_root.findall('{0}network'.format(NS)) - self.assertEqual(len(network_elems), 2) - for i, network_elem in enumerate(network_elems): - network = addresses_dict.items()[i] - self.assertEqual(str(network_elem.get('id')), str(network[0])) - ip_elems = network_elem.findall('{0}ip'.format(NS)) - for z, ip_elem in enumerate(ip_elems): - ip = network[1][z] - self.assertEqual(str(ip_elem.get('version')), - str(ip['version'])) - self.assertEqual(str(ip_elem.get('addr')), - str(ip['addr'])) - - def test_create(self): - serializer = servers.ServerXMLSerializer() - - fixture = { - "server": { - "id": FAKE_UUID, - "user_id": "fake", - "tenant_id": "fake", - 'created': self.TIMESTAMP, - 'updated': self.TIMESTAMP, - "progress": 0, - "name": "test_server", - "status": "BUILD", - "accessIPv4": "1.2.3.4", - "accessIPv6": "fead::1234", - "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0", - "adminPass": "test_password", - "image": { - "id": "5", - "links": [ - { - "rel": "bookmark", - "href": self.IMAGE_BOOKMARK, - }, - ], - }, - "flavor": { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": self.FLAVOR_BOOKMARK, - }, - ], - }, - "addresses": { - "network_one": [ - { - "version": 4, - "addr": "67.23.10.138", - }, - { - "version": 6, - "addr": "::babe:67.23.10.138", - }, - ], - "network_two": [ - { - "version": 4, - "addr": "67.23.10.139", - }, - { - "version": 6, - "addr": "::babe:67.23.10.139", - }, - ], - }, - "metadata": { - "Open": "Stack", - "Number": "1", - }, - 'links': [ - { - 'href': self.SERVER_HREF, - 'rel': 'self', - }, - { - 'href': self.SERVER_BOOKMARK, - 'rel': 'bookmark', - }, - ], - } - } - - output = serializer.serialize(fixture, 'create') - print output - root = etree.XML(output) - xmlutil.validate_schema(root, 'server') - - expected_server_href = self.SERVER_HREF - expected_server_bookmark = self.SERVER_BOOKMARK - expected_image_bookmark = self.IMAGE_BOOKMARK - expected_flavor_bookmark = self.FLAVOR_BOOKMARK - expected_now = self.TIMESTAMP - server_dict = fixture['server'] - - for key in ['name', 'id', 'created', 'accessIPv4', - 'updated', 'progress', 'status', 'hostId', - 'accessIPv6', 'adminPass']: - self.assertEqual(root.get(key), str(server_dict[key])) - - link_nodes = root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(server_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - metadata_root = root.find('{0}metadata'.format(NS)) - metadata_elems = metadata_root.findall('{0}meta'.format(NS)) - self.assertEqual(len(metadata_elems), 2) - for i, metadata_elem in enumerate(metadata_elems): - (meta_key, meta_value) = server_dict['metadata'].items()[i] - self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) - self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) - - image_root = root.find('{0}image'.format(NS)) - self.assertEqual(image_root.get('id'), server_dict['image']['id']) - link_nodes = image_root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 1) - for i, link in enumerate(server_dict['image']['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - flavor_root = root.find('{0}flavor'.format(NS)) - self.assertEqual(flavor_root.get('id'), server_dict['flavor']['id']) - link_nodes = flavor_root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 1) - for i, link in enumerate(server_dict['flavor']['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - addresses_root = root.find('{0}addresses'.format(NS)) - addresses_dict = server_dict['addresses'] - network_elems = addresses_root.findall('{0}network'.format(NS)) - self.assertEqual(len(network_elems), 2) - for i, network_elem in enumerate(network_elems): - network = addresses_dict.items()[i] - self.assertEqual(str(network_elem.get('id')), str(network[0])) - ip_elems = network_elem.findall('{0}ip'.format(NS)) - for z, ip_elem in enumerate(ip_elems): - ip = network[1][z] - self.assertEqual(str(ip_elem.get('version')), - str(ip['version'])) - self.assertEqual(str(ip_elem.get('addr')), - str(ip['addr'])) - - def test_index(self): - serializer = servers.ServerXMLSerializer() - - uuid1 = get_fake_uuid(1) - uuid2 = get_fake_uuid(2) - expected_server_href = 'http://localhost/v1.1/servers/%s' % uuid1 - expected_server_bookmark = 'http://localhost/servers/%s' % uuid1 - expected_server_href_2 = 'http://localhost/v1.1/servers/%s' % uuid2 - expected_server_bookmark_2 = 'http://localhost/servers/%s' % uuid2 - fixture = {"servers": [ - { - "id": get_fake_uuid(1), - "name": "test_server", - 'links': [ - { - 'href': expected_server_href, - 'rel': 'self', - }, - { - 'href': expected_server_bookmark, - 'rel': 'bookmark', - }, - ], - }, - { - "id": get_fake_uuid(2), - "name": "test_server_2", - 'links': [ - { - 'href': expected_server_href_2, - 'rel': 'self', - }, - { - 'href': expected_server_bookmark_2, - 'rel': 'bookmark', - }, - ], - }, - ]} - - output = serializer.serialize(fixture, 'index') - print output - root = etree.XML(output) - xmlutil.validate_schema(root, 'servers_index') - server_elems = root.findall('{0}server'.format(NS)) - self.assertEqual(len(server_elems), 2) - for i, server_elem in enumerate(server_elems): - server_dict = fixture['servers'][i] - for key in ['name', 'id']: - self.assertEqual(server_elem.get(key), str(server_dict[key])) - - link_nodes = server_elem.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(server_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - def test_index_with_servers_links(self): - serializer = servers.ServerXMLSerializer() - - uuid1 = get_fake_uuid(1) - uuid2 = get_fake_uuid(2) - expected_server_href = 'http://localhost/v1.1/servers/%s' % uuid1 - expected_server_next = self.SERVER_NEXT % (2, 2) - expected_server_bookmark = 'http://localhost/servers/%s' % uuid1 - expected_server_href_2 = 'http://localhost/v1.1/servers/%s' % uuid2 - expected_server_bookmark_2 = 'http://localhost/servers/%s' % uuid2 - fixture = {"servers": [ - { - "id": get_fake_uuid(1), - "name": "test_server", - 'links': [ - { - 'href': expected_server_href, - 'rel': 'self', - }, - { - 'href': expected_server_bookmark, - 'rel': 'bookmark', - }, - ], - }, - { - "id": get_fake_uuid(2), - "name": "test_server_2", - 'links': [ - { - 'href': expected_server_href_2, - 'rel': 'self', - }, - { - 'href': expected_server_bookmark_2, - 'rel': 'bookmark', - }, - ], - }, - ], - "servers_links": [ - { - 'rel': 'next', - 'href': expected_server_next, - }, - ]} - - output = serializer.serialize(fixture, 'index') - print output - root = etree.XML(output) - xmlutil.validate_schema(root, 'servers_index') - server_elems = root.findall('{0}server'.format(NS)) - self.assertEqual(len(server_elems), 2) - for i, server_elem in enumerate(server_elems): - server_dict = fixture['servers'][i] - for key in ['name', 'id']: - self.assertEqual(server_elem.get(key), str(server_dict[key])) - - link_nodes = server_elem.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(server_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - # Check servers_links - servers_links = root.findall('{0}link'.format(ATOMNS)) - for i, link in enumerate(fixture['servers_links']): - for key, value in link.items(): - self.assertEqual(servers_links[i].get(key), value) - - def test_detail(self): - serializer = servers.ServerXMLSerializer() - - uuid1 = get_fake_uuid(1) - expected_server_href = 'http://localhost/v1.1/servers/%s' % uuid1 - expected_server_bookmark = 'http://localhost/servers/%s' % uuid1 - expected_image_bookmark = self.IMAGE_BOOKMARK - expected_flavor_bookmark = self.FLAVOR_BOOKMARK - expected_now = self.TIMESTAMP - - uuid2 = get_fake_uuid(2) - expected_server_href_2 = 'http://localhost/v1.1/servers/%s' % uuid2 - expected_server_bookmark_2 = 'http://localhost/servers/%s' % uuid2 - fixture = {"servers": [ - { - "id": get_fake_uuid(1), - "user_id": "fake", - "tenant_id": "fake", - 'created': self.TIMESTAMP, - 'updated': self.TIMESTAMP, - "progress": 0, - "name": "test_server", - "status": "BUILD", - "accessIPv4": "1.2.3.4", - "accessIPv6": "fead::1234", - "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', - "image": { - "id": "5", - "links": [ - { - "rel": "bookmark", - "href": expected_image_bookmark, - }, - ], - }, - "flavor": { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": expected_flavor_bookmark, - }, - ], - }, - "addresses": { - "network_one": [ - { - "version": 4, - "addr": "67.23.10.138", - }, - { - "version": 6, - "addr": "::babe:67.23.10.138", - }, - ], - }, - "metadata": { - "Number": "1", - }, - "links": [ - { - "href": expected_server_href, - "rel": "self", - }, - { - "href": expected_server_bookmark, - "rel": "bookmark", - }, - ], - }, - { - "id": get_fake_uuid(2), - "user_id": 'fake', - "tenant_id": 'fake', - 'created': self.TIMESTAMP, - 'updated': self.TIMESTAMP, - "progress": 100, - "name": "test_server_2", - "status": "ACTIVE", - "accessIPv4": "1.2.3.4", - "accessIPv6": "fead::1234", - "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', - "image": { - "id": "5", - "links": [ - { - "rel": "bookmark", - "href": expected_image_bookmark, - }, - ], - }, - "flavor": { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": expected_flavor_bookmark, - }, - ], - }, - "addresses": { - "network_one": [ - { - "version": 4, - "addr": "67.23.10.138", - }, - { - "version": 6, - "addr": "::babe:67.23.10.138", - }, - ], - }, - "metadata": { - "Number": "2", - }, - "links": [ - { - "href": expected_server_href_2, - "rel": "self", - }, - { - "href": expected_server_bookmark_2, - "rel": "bookmark", - }, - ], - }, - ]} - - output = serializer.serialize(fixture, 'detail') - root = etree.XML(output) - xmlutil.validate_schema(root, 'servers') - server_elems = root.findall('{0}server'.format(NS)) - self.assertEqual(len(server_elems), 2) - for i, server_elem in enumerate(server_elems): - server_dict = fixture['servers'][i] - - for key in ['name', 'id', 'created', 'accessIPv4', - 'updated', 'progress', 'status', 'hostId', - 'accessIPv6']: - self.assertEqual(server_elem.get(key), str(server_dict[key])) - - link_nodes = server_elem.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(server_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - metadata_root = server_elem.find('{0}metadata'.format(NS)) - metadata_elems = metadata_root.findall('{0}meta'.format(NS)) - for i, metadata_elem in enumerate(metadata_elems): - (meta_key, meta_value) = server_dict['metadata'].items()[i] - self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) - self.assertEqual(str(metadata_elem.text).strip(), - str(meta_value)) - - image_root = server_elem.find('{0}image'.format(NS)) - self.assertEqual(image_root.get('id'), server_dict['image']['id']) - link_nodes = image_root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 1) - for i, link in enumerate(server_dict['image']['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - flavor_root = server_elem.find('{0}flavor'.format(NS)) - self.assertEqual(flavor_root.get('id'), - server_dict['flavor']['id']) - link_nodes = flavor_root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 1) - for i, link in enumerate(server_dict['flavor']['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - addresses_root = server_elem.find('{0}addresses'.format(NS)) - addresses_dict = server_dict['addresses'] - network_elems = addresses_root.findall('{0}network'.format(NS)) - for i, network_elem in enumerate(network_elems): - network = addresses_dict.items()[i] - self.assertEqual(str(network_elem.get('id')), str(network[0])) - ip_elems = network_elem.findall('{0}ip'.format(NS)) - for z, ip_elem in enumerate(ip_elems): - ip = network[1][z] - self.assertEqual(str(ip_elem.get('version')), - str(ip['version'])) - self.assertEqual(str(ip_elem.get('addr')), - str(ip['addr'])) - - def test_update(self): - serializer = servers.ServerXMLSerializer() - - fixture = { - "server": { - "id": FAKE_UUID, - "user_id": "fake", - "tenant_id": "fake", - 'created': self.TIMESTAMP, - 'updated': self.TIMESTAMP, - "progress": 0, - "name": "test_server", - "status": "BUILD", - "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', - "accessIPv4": "1.2.3.4", - "accessIPv6": "fead::1234", - "image": { - "id": "5", - "links": [ - { - "rel": "bookmark", - "href": self.IMAGE_BOOKMARK, - }, - ], - }, - "flavor": { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": self.FLAVOR_BOOKMARK, - }, - ], - }, - "addresses": { - "network_one": [ - { - "version": 4, - "addr": "67.23.10.138", - }, - { - "version": 6, - "addr": "::babe:67.23.10.138", - }, - ], - "network_two": [ - { - "version": 4, - "addr": "67.23.10.139", - }, - { - "version": 6, - "addr": "::babe:67.23.10.139", - }, - ], - }, - "metadata": { - "Open": "Stack", - "Number": "1", - }, - 'links': [ - { - 'href': self.SERVER_HREF, - 'rel': 'self', - }, - { - 'href': self.SERVER_BOOKMARK, - 'rel': 'bookmark', - }, - ], - } - } - - output = serializer.serialize(fixture, 'update') - print output - root = etree.XML(output) - xmlutil.validate_schema(root, 'server') - - expected_server_href = self.SERVER_HREF - expected_server_bookmark = self.SERVER_BOOKMARK - expected_image_bookmark = self.IMAGE_BOOKMARK - expected_flavor_bookmark = self.FLAVOR_BOOKMARK - expected_now = self.TIMESTAMP - server_dict = fixture['server'] - - for key in ['name', 'id', 'created', 'accessIPv4', - 'updated', 'progress', 'status', 'hostId', - 'accessIPv6']: - self.assertEqual(root.get(key), str(server_dict[key])) - - link_nodes = root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(server_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - metadata_root = root.find('{0}metadata'.format(NS)) - metadata_elems = metadata_root.findall('{0}meta'.format(NS)) - self.assertEqual(len(metadata_elems), 2) - for i, metadata_elem in enumerate(metadata_elems): - (meta_key, meta_value) = server_dict['metadata'].items()[i] - self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) - self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) - - image_root = root.find('{0}image'.format(NS)) - self.assertEqual(image_root.get('id'), server_dict['image']['id']) - link_nodes = image_root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 1) - for i, link in enumerate(server_dict['image']['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - flavor_root = root.find('{0}flavor'.format(NS)) - self.assertEqual(flavor_root.get('id'), server_dict['flavor']['id']) - link_nodes = flavor_root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 1) - for i, link in enumerate(server_dict['flavor']['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - addresses_root = root.find('{0}addresses'.format(NS)) - addresses_dict = server_dict['addresses'] - network_elems = addresses_root.findall('{0}network'.format(NS)) - self.assertEqual(len(network_elems), 2) - for i, network_elem in enumerate(network_elems): - network = addresses_dict.items()[i] - self.assertEqual(str(network_elem.get('id')), str(network[0])) - ip_elems = network_elem.findall('{0}ip'.format(NS)) - for z, ip_elem in enumerate(ip_elems): - ip = network[1][z] - self.assertEqual(str(ip_elem.get('version')), - str(ip['version'])) - self.assertEqual(str(ip_elem.get('addr')), - str(ip['addr'])) - - def test_action(self): - serializer = servers.ServerXMLSerializer() - - fixture = { - "server": { - "id": FAKE_UUID, - "user_id": "fake", - "tenant_id": "fake", - 'created': self.TIMESTAMP, - 'updated': self.TIMESTAMP, - "progress": 0, - "name": "test_server", - "status": "BUILD", - "accessIPv4": "1.2.3.4", - "accessIPv6": "fead::1234", - "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0", - "adminPass": "test_password", - "image": { - "id": "5", - "links": [ - { - "rel": "bookmark", - "href": self.IMAGE_BOOKMARK, - }, - ], - }, - "flavor": { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": self.FLAVOR_BOOKMARK, - }, - ], - }, - "addresses": { - "network_one": [ - { - "version": 4, - "addr": "67.23.10.138", - }, - { - "version": 6, - "addr": "::babe:67.23.10.138", - }, - ], - "network_two": [ - { - "version": 4, - "addr": "67.23.10.139", - }, - { - "version": 6, - "addr": "::babe:67.23.10.139", - }, - ], - }, - "metadata": { - "Open": "Stack", - "Number": "1", - }, - 'links': [ - { - 'href': self.SERVER_HREF, - 'rel': 'self', - }, - { - 'href': self.SERVER_BOOKMARK, - 'rel': 'bookmark', - }, - ], - } - } - - output = serializer.serialize(fixture, 'action') - root = etree.XML(output) - xmlutil.validate_schema(root, 'server') - - expected_server_href = self.SERVER_HREF - expected_server_bookmark = self.SERVER_BOOKMARK - expected_image_bookmark = self.IMAGE_BOOKMARK - expected_flavor_bookmark = self.FLAVOR_BOOKMARK - expected_now = self.TIMESTAMP - server_dict = fixture['server'] - - for key in ['name', 'id', 'created', 'accessIPv4', - 'updated', 'progress', 'status', 'hostId', - 'accessIPv6', 'adminPass']: - self.assertEqual(root.get(key), str(server_dict[key])) - - link_nodes = root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 2) - for i, link in enumerate(server_dict['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - metadata_root = root.find('{0}metadata'.format(NS)) - metadata_elems = metadata_root.findall('{0}meta'.format(NS)) - self.assertEqual(len(metadata_elems), 2) - for i, metadata_elem in enumerate(metadata_elems): - (meta_key, meta_value) = server_dict['metadata'].items()[i] - self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) - self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) - - image_root = root.find('{0}image'.format(NS)) - self.assertEqual(image_root.get('id'), server_dict['image']['id']) - link_nodes = image_root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 1) - for i, link in enumerate(server_dict['image']['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - flavor_root = root.find('{0}flavor'.format(NS)) - self.assertEqual(flavor_root.get('id'), server_dict['flavor']['id']) - link_nodes = flavor_root.findall('{0}link'.format(ATOMNS)) - self.assertEqual(len(link_nodes), 1) - for i, link in enumerate(server_dict['flavor']['links']): - for key, value in link.items(): - self.assertEqual(link_nodes[i].get(key), value) - - addresses_root = root.find('{0}addresses'.format(NS)) - addresses_dict = server_dict['addresses'] - network_elems = addresses_root.findall('{0}network'.format(NS)) - self.assertEqual(len(network_elems), 2) - for i, network_elem in enumerate(network_elems): - network = addresses_dict.items()[i] - self.assertEqual(str(network_elem.get('id')), str(network[0])) - ip_elems = network_elem.findall('{0}ip'.format(NS)) - for z, ip_elem in enumerate(ip_elems): - ip = network[1][z] - self.assertEqual(str(ip_elem.get('version')), - str(ip['version'])) - self.assertEqual(str(ip_elem.get('addr')), - str(ip['addr'])) diff --git a/nova/tests/api/openstack/test_urlmap.py b/nova/tests/api/openstack/test_urlmap.py deleted file mode 100644 index 3995765e5..000000000 --- a/nova/tests/api/openstack/test_urlmap.py +++ /dev/null @@ -1,84 +0,0 @@ -# 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. - -import json -import webob - -from nova import test -from nova import log as logging -from nova.tests.api.openstack import fakes - -LOG = logging.getLogger('nova.tests.api.openstack.test_urlmap') - - -class UrlmapTest(test.TestCase): - def setUp(self): - super(UrlmapTest, self).setUp() - fakes.stub_out_rate_limiting(self.stubs) - - def test_path_version_v1_1(self): - """Test URL path specifying v1.1 returns v1.1 content.""" - req = webob.Request.blank('/v1.1/') - req.accept = "application/json" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - self.assertEqual(res.content_type, "application/json") - body = json.loads(res.body) - self.assertEqual(body['version']['id'], 'v1.1') - - def test_content_type_version_v1_1(self): - """Test Content-Type specifying v1.1 returns v1.1 content.""" - req = webob.Request.blank('/') - req.content_type = "application/json;version=1.1" - req.accept = "application/json" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - self.assertEqual(res.content_type, "application/json") - body = json.loads(res.body) - self.assertEqual(body['version']['id'], 'v1.1') - - def test_accept_version_v1_1(self): - """Test Accept header specifying v1.1 returns v1.1 content.""" - req = webob.Request.blank('/') - req.accept = "application/json;version=1.1" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - self.assertEqual(res.content_type, "application/json") - body = json.loads(res.body) - self.assertEqual(body['version']['id'], 'v1.1') - - def test_path_content_type(self): - """Test URL path specifying JSON returns JSON content.""" - url = '/v1.1/foobar/images/cedef40a-ed67-4d10-800e-17455edce175.json' - req = webob.Request.blank(url) - req.accept = "application/xml" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - self.assertEqual(res.content_type, "application/json") - body = json.loads(res.body) - self.assertEqual(body['image']['id'], - 'cedef40a-ed67-4d10-800e-17455edce175') - - def test_accept_content_type(self): - """Test Accept header specifying JSON returns JSON content.""" - url = '/v1.1/foobar/images/cedef40a-ed67-4d10-800e-17455edce175' - req = webob.Request.blank(url) - req.accept = "application/xml;q=0.8, application/json" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - self.assertEqual(res.content_type, "application/json") - body = json.loads(res.body) - self.assertEqual(body['image']['id'], - 'cedef40a-ed67-4d10-800e-17455edce175') diff --git a/nova/tests/api/openstack/test_users.py b/nova/tests/api/openstack/test_users.py deleted file mode 100644 index 82fce68ff..000000000 --- a/nova/tests/api/openstack/test_users.py +++ /dev/null @@ -1,157 +0,0 @@ -# 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. - -import json - -from lxml import etree -import webob - -from nova import test -from nova import utils -from nova.api.openstack import users -from nova.auth.manager import User, Project -from nova.tests.api.openstack import fakes - - -def fake_init(self): - self.manager = fakes.FakeAuthManager() - - -def fake_admin_check(self, req): - return True - - -class UsersTest(test.TestCase): - def setUp(self): - super(UsersTest, self).setUp() - self.flags(verbose=True, allow_admin_api=True) - self.stubs.Set(users.Controller, '__init__', - fake_init) - self.stubs.Set(users.Controller, '_check_admin', - fake_admin_check) - fakes.FakeAuthManager.clear_fakes() - fakes.FakeAuthManager.projects = dict(testacct=Project('testacct', - 'testacct', - 'id1', - 'test', - [])) - fakes.FakeAuthDatabase.data = {} - fakes.stub_out_networking(self.stubs) - fakes.stub_out_rate_limiting(self.stubs) - fakes.stub_out_auth(self.stubs) - - fakemgr = fakes.FakeAuthManager() - fakemgr.add_user(User('id1', 'guy1', 'acc1', 'secret1', False)) - fakemgr.add_user(User('id2', 'guy2', 'acc2', 'secret2', True)) - - self.controller = users.Controller() - - def test_get_user_list(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/users') - res_dict = self.controller.index(req) - - self.assertEqual(len(res_dict['users']), 2) - - def test_get_user_by_id(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/users/id2') - res_dict = self.controller.show(req, 'id2') - - self.assertEqual(res_dict['user']['id'], 'id2') - self.assertEqual(res_dict['user']['name'], 'guy2') - self.assertEqual(res_dict['user']['secret'], 'secret2') - self.assertEqual(res_dict['user']['admin'], True) - - def test_user_delete(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/users/id1') - res_dict = self.controller.delete(req, 'id1') - - self.assertTrue('id1' not in [u.id for u in - fakes.FakeAuthManager.auth_data]) - - def test_user_create(self): - secret = utils.generate_password() - body = dict(user=dict(name='test_guy', - access='acc3', - secret=secret, - admin=True)) - req = fakes.HTTPRequest.blank('/v1.1/fake/users') - res_dict = self.controller.create(req, body) - - # NOTE(justinsb): This is a questionable assertion in general - # fake sets id=name, but others might not... - self.assertEqual(res_dict['user']['id'], 'test_guy') - - self.assertEqual(res_dict['user']['name'], 'test_guy') - self.assertEqual(res_dict['user']['access'], 'acc3') - self.assertEqual(res_dict['user']['secret'], secret) - self.assertEqual(res_dict['user']['admin'], True) - self.assertTrue('test_guy' in [u.id for u in - fakes.FakeAuthManager.auth_data]) - self.assertEqual(len(fakes.FakeAuthManager.auth_data), 3) - - def test_user_update(self): - new_secret = utils.generate_password() - body = dict(user=dict(name='guy2', - access='acc2', - secret=new_secret)) - - req = fakes.HTTPRequest.blank('/v1.1/fake/users/id2') - res_dict = self.controller.update(req, 'id2', body) - - self.assertEqual(res_dict['user']['id'], 'id2') - self.assertEqual(res_dict['user']['name'], 'guy2') - self.assertEqual(res_dict['user']['access'], 'acc2') - self.assertEqual(res_dict['user']['secret'], new_secret) - self.assertEqual(res_dict['user']['admin'], True) - - -class TestUsersXMLSerializer(test.TestCase): - - serializer = users.UserXMLSerializer() - - def test_index(self): - fixture = {'users': [{'id': 'id1', - 'name': 'guy1', - 'secret': 'secret1', - 'admin': False}, - {'id': 'id2', - 'name': 'guy2', - 'secret': 'secret2', - 'admin': True}]} - - output = self.serializer.serialize(fixture, 'index') - res_tree = etree.XML(output) - - self.assertEqual(res_tree.tag, 'users') - self.assertEqual(len(res_tree), 2) - self.assertEqual(res_tree[0].tag, 'user') - self.assertEqual(res_tree[0].get('id'), 'id1') - self.assertEqual(res_tree[1].tag, 'user') - self.assertEqual(res_tree[1].get('id'), 'id2') - - def test_show(self): - fixture = {'user': {'id': 'id2', - 'name': 'guy2', - 'secret': 'secret2', - 'admin': True}} - - output = self.serializer.serialize(fixture, 'show') - res_tree = etree.XML(output) - - self.assertEqual(res_tree.tag, 'user') - self.assertEqual(res_tree.get('id'), 'id2') - self.assertEqual(res_tree.get('name'), 'guy2') - self.assertEqual(res_tree.get('secret'), 'secret2') - self.assertEqual(res_tree.get('admin'), 'True') diff --git a/nova/tests/api/openstack/test_versions.py b/nova/tests/api/openstack/test_versions.py deleted file mode 100644 index a6e30187b..000000000 --- a/nova/tests/api/openstack/test_versions.py +++ /dev/null @@ -1,671 +0,0 @@ -# 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 json - -import feedparser -from lxml import etree -import stubout -import webob - -from nova.api.openstack import versions -from nova.api.openstack import views -from nova.api.openstack import wsgi -from nova.api.openstack import xmlutil -from nova import context -from nova import test -from nova.tests.api.openstack import common -from nova.tests.api.openstack import fakes -from nova import utils - - -NS = { - 'atom': 'http://www.w3.org/2005/Atom', - 'ns': 'http://docs.openstack.org/compute/api/v1.1' -} - -VERSIONS = { - "v1.1": { - "id": "v1.1", - "status": "CURRENT", - "updated": "2011-01-21T11:33:21Z", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "http://docs.rackspacecloud.com/" - "servers/api/v1.1/cs-devguide-20110125.pdf", - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://docs.rackspacecloud.com/" - "servers/api/v1.1/application.wadl", - }, - ], - "media-types": [ - { - "base": "application/xml", - "type": "application/vnd.openstack.compute+xml;version=1.1", - }, - { - "base": "application/json", - "type": "application/vnd.openstack.compute+json;version=1.1", - }, - ], - }, -} - - -class VersionsTest(test.TestCase): - def setUp(self): - super(VersionsTest, self).setUp() - self.context = context.get_admin_context() - self.stubs = stubout.StubOutForTesting() - fakes.stub_out_auth(self.stubs) - #Stub out VERSIONS - self.old_versions = versions.VERSIONS - versions.VERSIONS = VERSIONS - - def tearDown(self): - versions.VERSIONS = self.old_versions - super(VersionsTest, self).tearDown() - - def test_get_version_list(self): - req = webob.Request.blank('/') - req.accept = "application/json" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - self.assertEqual(res.content_type, "application/json") - versions = json.loads(res.body)["versions"] - expected = [ - { - "id": "v1.1", - "status": "CURRENT", - "updated": "2011-01-21T11:33:21Z", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/", - }], - }, - ] - self.assertEqual(versions, expected) - - def test_get_version_list_302(self): - req = webob.Request.blank('/v1.1') - req.accept = "application/json" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 302) - redirect_req = webob.Request.blank('/v1.1/') - self.assertEqual(res.location, redirect_req.url) - - def test_get_version_1_1_detail(self): - req = webob.Request.blank('/v1.1/') - req.accept = "application/json" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - self.assertEqual(res.content_type, "application/json") - version = json.loads(res.body) - expected = { - "version": { - "id": "v1.1", - "status": "CURRENT", - "updated": "2011-01-21T11:33:21Z", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/", - }, - { - "rel": "describedby", - "type": "application/pdf", - "href": "http://docs.rackspacecloud.com/" - "servers/api/v1.1/cs-devguide-20110125.pdf", - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://docs.rackspacecloud.com/" - "servers/api/v1.1/application.wadl", - }, - ], - "media-types": [ - { - "base": "application/xml", - "type": "application/" - "vnd.openstack.compute+xml;version=1.1", - }, - { - "base": "application/json", - "type": "application/" - "vnd.openstack.compute+json;version=1.1", - }, - ], - }, - } - self.assertEqual(expected, version) - - def test_get_version_1_1_detail_content_type(self): - req = webob.Request.blank('/') - req.accept = "application/json;version=1.1" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - self.assertEqual(res.content_type, "application/json") - version = json.loads(res.body) - expected = { - "version": { - "id": "v1.1", - "status": "CURRENT", - "updated": "2011-01-21T11:33:21Z", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/", - }, - { - "rel": "describedby", - "type": "application/pdf", - "href": "http://docs.rackspacecloud.com/" - "servers/api/v1.1/cs-devguide-20110125.pdf", - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://docs.rackspacecloud.com/" - "servers/api/v1.1/application.wadl", - }, - ], - "media-types": [ - { - "base": "application/xml", - "type": "application/" - "vnd.openstack.compute+xml;version=1.1", - }, - { - "base": "application/json", - "type": "application/" - "vnd.openstack.compute+json;version=1.1", - }, - ], - }, - } - self.assertEqual(expected, version) - - def test_get_version_1_1_detail_xml(self): - req = webob.Request.blank('/v1.1/') - req.accept = "application/xml" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - self.assertEqual(res.content_type, "application/xml") - - version = etree.XML(res.body) - xmlutil.validate_schema(version, 'version') - - expected = VERSIONS['v1.1'] - self.assertTrue(version.xpath('/ns:version', namespaces=NS)) - media_types = version.xpath('ns:media-types/ns:media-type', - namespaces=NS) - self.assertTrue(common.compare_media_types(media_types, - expected['media-types'])) - for key in ['id', 'status', 'updated']: - self.assertEqual(version.get(key), expected[key]) - links = version.xpath('atom:link', namespaces=NS) - self.assertTrue(common.compare_links(links, - [{'rel': 'self', 'href': 'http://localhost/v1.1/'}] - + expected['links'])) - - def test_get_version_list_xml(self): - req = webob.Request.blank('/') - req.accept = "application/xml" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - self.assertEqual(res.content_type, "application/xml") - - root = etree.XML(res.body) - print res.body - xmlutil.validate_schema(root, 'versions') - - self.assertTrue(root.xpath('/ns:versions', namespaces=NS)) - versions = root.xpath('ns:version', namespaces=NS) - self.assertEqual(len(versions), 1) - - for i, v in enumerate(['v1.1']): - version = versions[i] - expected = VERSIONS[v] - for key in ['id', 'status', 'updated']: - self.assertEqual(version.get(key), expected[key]) - (link,) = version.xpath('atom:link', namespaces=NS) - self.assertTrue(common.compare_links(link, - [{'rel': 'self', 'href': 'http://localhost/%s/' % v}])) - - def test_get_version_1_1_detail_atom(self): - req = webob.Request.blank('/v1.1/') - req.accept = "application/atom+xml" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - self.assertEqual("application/atom+xml", res.content_type) - - xmlutil.validate_schema(etree.XML(res.body), 'atom') - - f = feedparser.parse(res.body) - self.assertEqual(f.feed.title, 'About This Version') - self.assertEqual(f.feed.updated, '2011-01-21T11:33:21Z') - self.assertEqual(f.feed.id, 'http://localhost/v1.1/') - self.assertEqual(f.feed.author, 'Rackspace') - self.assertEqual(f.feed.author_detail.href, - 'http://www.rackspace.com/') - self.assertEqual(f.feed.links[0]['href'], 'http://localhost/v1.1/') - self.assertEqual(f.feed.links[0]['rel'], 'self') - - self.assertEqual(len(f.entries), 1) - entry = f.entries[0] - self.assertEqual(entry.id, 'http://localhost/v1.1/') - self.assertEqual(entry.title, 'Version v1.1') - self.assertEqual(entry.updated, '2011-01-21T11:33:21Z') - self.assertEqual(len(entry.content), 1) - self.assertEqual(entry.content[0].value, - 'Version v1.1 CURRENT (2011-01-21T11:33:21Z)') - self.assertEqual(len(entry.links), 3) - self.assertEqual(entry.links[0]['href'], 'http://localhost/v1.1/') - self.assertEqual(entry.links[0]['rel'], 'self') - self.assertEqual(entry.links[1], { - 'href': 'http://docs.rackspacecloud.com/servers/api/v1.1/'\ - 'cs-devguide-20110125.pdf', - 'type': 'application/pdf', - 'rel': 'describedby'}) - self.assertEqual(entry.links[2], { - 'href': 'http://docs.rackspacecloud.com/servers/api/v1.1/'\ - 'application.wadl', - 'type': 'application/vnd.sun.wadl+xml', - 'rel': 'describedby'}) - - def test_get_version_list_atom(self): - req = webob.Request.blank('/') - req.accept = "application/atom+xml" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - self.assertEqual(res.content_type, "application/atom+xml") - - f = feedparser.parse(res.body) - self.assertEqual(f.feed.title, 'Available API Versions') - self.assertEqual(f.feed.updated, '2011-01-21T11:33:21Z') - self.assertEqual(f.feed.id, 'http://localhost/') - self.assertEqual(f.feed.author, 'Rackspace') - self.assertEqual(f.feed.author_detail.href, - 'http://www.rackspace.com/') - self.assertEqual(f.feed.links[0]['href'], 'http://localhost/') - self.assertEqual(f.feed.links[0]['rel'], 'self') - - self.assertEqual(len(f.entries), 1) - entry = f.entries[0] - self.assertEqual(entry.id, 'http://localhost/v1.1/') - self.assertEqual(entry.title, 'Version v1.1') - self.assertEqual(entry.updated, '2011-01-21T11:33:21Z') - self.assertEqual(len(entry.content), 1) - self.assertEqual(entry.content[0].value, - 'Version v1.1 CURRENT (2011-01-21T11:33:21Z)') - self.assertEqual(len(entry.links), 1) - self.assertEqual(entry.links[0]['href'], 'http://localhost/v1.1/') - self.assertEqual(entry.links[0]['rel'], 'self') - - def test_multi_choice_image(self): - req = webob.Request.blank('/images/1') - req.accept = "application/json" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 300) - self.assertEqual(res.content_type, "application/json") - - expected = { - "choices": [ - { - "id": "v1.1", - "status": "CURRENT", - "links": [ - { - "href": "http://localhost/v1.1/images/1", - "rel": "self", - }, - ], - "media-types": [ - { - "base": "application/xml", - "type": "application/vnd.openstack.compute+xml" - ";version=1.1" - }, - { - "base": "application/json", - "type": "application/vnd.openstack.compute+json" - ";version=1.1" - }, - ], - }, - ], } - - self.assertDictMatch(expected, json.loads(res.body)) - - def test_multi_choice_image_xml(self): - req = webob.Request.blank('/images/1') - req.accept = "application/xml" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 300) - self.assertEqual(res.content_type, "application/xml") - - root = etree.XML(res.body) - self.assertTrue(root.xpath('/ns:choices', namespaces=NS)) - versions = root.xpath('ns:version', namespaces=NS) - self.assertEqual(len(versions), 1) - - version = versions[0] - self.assertEqual(version.get('id'), 'v1.1') - self.assertEqual(version.get('status'), 'CURRENT') - media_types = version.xpath('ns:media-types/ns:media-type', - namespaces=NS) - self.assertTrue(common.compare_media_types(media_types, - VERSIONS['v1.1']['media-types'])) - links = version.xpath('atom:link', namespaces=NS) - self.assertTrue(common.compare_links(links, - [{'rel': 'self', 'href': 'http://localhost/v1.1/images/1'}])) - - def test_multi_choice_server_atom(self): - """ - Make sure multi choice responses do not have content-type - application/atom+xml (should use default of json) - """ - req = webob.Request.blank('/servers') - req.accept = "application/atom+xml" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 300) - self.assertEqual(res.content_type, "application/json") - - def test_multi_choice_server(self): - uuid = str(utils.gen_uuid()) - req = webob.Request.blank('/servers/' + uuid) - req.accept = "application/json" - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 300) - self.assertEqual(res.content_type, "application/json") - - expected = { - "choices": [ - { - "id": "v1.1", - "status": "CURRENT", - "links": [ - { - "href": "http://localhost/v1.1/servers/" + uuid, - "rel": "self", - }, - ], - "media-types": [ - { - "base": "application/xml", - "type": "application/vnd.openstack.compute+xml" - ";version=1.1" - }, - { - "base": "application/json", - "type": "application/vnd.openstack.compute+json" - ";version=1.1" - }, - ], - }, - ], } - - self.assertDictMatch(expected, json.loads(res.body)) - - -class VersionsViewBuilderTests(test.TestCase): - def test_view_builder(self): - base_url = "http://example.org/" - - version_data = { - "v3.2.1": { - "id": "3.2.1", - "status": "CURRENT", - "updated": "2011-07-18T11:30:00Z", - } - } - - expected = { - "versions": [ - { - "id": "3.2.1", - "status": "CURRENT", - "updated": "2011-07-18T11:30:00Z", - "links": [ - { - "rel": "self", - "href": "http://example.org/3.2.1/", - }, - ], - } - ] - } - - builder = views.versions.ViewBuilder(base_url) - output = builder.build_versions(version_data) - - self.assertEqual(output, expected) - - def test_generate_href(self): - base_url = "http://example.org/app/" - version_number = "v1.4.6" - - expected = "http://example.org/app/v1.4.6/" - - builder = views.versions.ViewBuilder(base_url) - actual = builder.generate_href(version_number) - - self.assertEqual(actual, expected) - - -class VersionsSerializerTests(test.TestCase): - def test_versions_list_xml_serializer(self): - versions_data = { - 'versions': [ - { - "id": "2.7.1", - "updated": "2011-07-18T11:30:00Z", - "status": "DEPRECATED", - "links": [ - { - "rel": "self", - "href": "http://test/2.7.1", - }, - ], - }, - ] - } - - serializer = versions.VersionsXMLSerializer() - response = serializer.index(versions_data) - - root = etree.XML(response) - xmlutil.validate_schema(root, 'versions') - - self.assertTrue(root.xpath('/ns:versions', namespaces=NS)) - version_elems = root.xpath('ns:version', namespaces=NS) - self.assertEqual(len(version_elems), 1) - version = version_elems[0] - self.assertEqual(version.get('id'), versions_data['versions'][0]['id']) - self.assertEqual(version.get('status'), - versions_data['versions'][0]['status']) - - (link,) = version.xpath('atom:link', namespaces=NS) - self.assertTrue(common.compare_links(link, [{ - 'rel': 'self', - 'href': 'http://test/2.7.1', - 'type': 'application/atom+xml'}])) - - def test_versions_multi_xml_serializer(self): - versions_data = { - 'choices': [ - { - "id": "2.7.1", - "updated": "2011-07-18T11:30:00Z", - "status": "DEPRECATED", - "media-types": VERSIONS['v1.1']['media-types'], - "links": [ - { - "rel": "self", - "href": "http://test/2.7.1/images", - }, - ], - }, - ] - } - - serializer = versions.VersionsXMLSerializer() - response = serializer.multi(versions_data) - - root = etree.XML(response) - self.assertTrue(root.xpath('/ns:choices', namespaces=NS)) - (version,) = root.xpath('ns:version', namespaces=NS) - self.assertEqual(version.get('id'), versions_data['choices'][0]['id']) - self.assertEqual(version.get('status'), - versions_data['choices'][0]['status']) - - media_types = list(version)[0] - media_type_nodes = list(media_types) - self.assertEqual(media_types.tag.split('}')[1], "media-types") - - media_types = version.xpath('ns:media-types/ns:media-type', - namespaces=NS) - self.assertTrue(common.compare_media_types(media_types, - versions_data['choices'][0]['media-types'])) - - (link,) = version.xpath('atom:link', namespaces=NS) - self.assertTrue(common.compare_links(link, - versions_data['choices'][0]['links'])) - - def test_versions_list_atom_serializer(self): - versions_data = { - 'versions': [ - { - "id": "2.9.8", - "updated": "2011-07-20T11:40:00Z", - "status": "CURRENT", - "links": [ - { - "rel": "self", - "href": "http://test/2.9.8", - }, - ], - }, - ] - } - - serializer = versions.VersionsAtomSerializer() - response = serializer.index(versions_data) - f = feedparser.parse(response) - - self.assertEqual(f.feed.title, 'Available API Versions') - self.assertEqual(f.feed.updated, '2011-07-20T11:40:00Z') - self.assertEqual(f.feed.id, 'http://test/') - self.assertEqual(f.feed.author, 'Rackspace') - self.assertEqual(f.feed.author_detail.href, - 'http://www.rackspace.com/') - self.assertEqual(f.feed.links[0]['href'], 'http://test/') - self.assertEqual(f.feed.links[0]['rel'], 'self') - - self.assertEqual(len(f.entries), 1) - entry = f.entries[0] - self.assertEqual(entry.id, 'http://test/2.9.8') - self.assertEqual(entry.title, 'Version 2.9.8') - self.assertEqual(entry.updated, '2011-07-20T11:40:00Z') - self.assertEqual(len(entry.content), 1) - self.assertEqual(entry.content[0].value, - 'Version 2.9.8 CURRENT (2011-07-20T11:40:00Z)') - self.assertEqual(len(entry.links), 1) - self.assertEqual(entry.links[0]['href'], 'http://test/2.9.8') - self.assertEqual(entry.links[0]['rel'], 'self') - - def test_version_detail_atom_serializer(self): - versions_data = { - "version": { - "id": "v1.1", - "status": "CURRENT", - "updated": "2011-01-21T11:33:21Z", - "links": [ - { - "rel": "self", - "href": "http://localhost/v1.1/", - }, - { - "rel": "describedby", - "type": "application/pdf", - "href": "http://docs.rackspacecloud.com/" - "servers/api/v1.1/cs-devguide-20110125.pdf", - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://docs.rackspacecloud.com/" - "servers/api/v1.1/application.wadl", - }, - ], - "media-types": [ - { - "base": "application/xml", - "type": "application/vnd.openstack.compute+xml" - ";version=1.1", - }, - { - "base": "application/json", - "type": "application/vnd.openstack.compute+json" - ";version=1.1", - } - ], - }, - } - - serializer = versions.VersionsAtomSerializer() - response = serializer.show(versions_data) - f = feedparser.parse(response) - - self.assertEqual(f.feed.title, 'About This Version') - self.assertEqual(f.feed.updated, '2011-01-21T11:33:21Z') - self.assertEqual(f.feed.id, 'http://localhost/v1.1/') - self.assertEqual(f.feed.author, 'Rackspace') - self.assertEqual(f.feed.author_detail.href, - 'http://www.rackspace.com/') - self.assertEqual(f.feed.links[0]['href'], 'http://localhost/v1.1/') - self.assertEqual(f.feed.links[0]['rel'], 'self') - - self.assertEqual(len(f.entries), 1) - entry = f.entries[0] - self.assertEqual(entry.id, 'http://localhost/v1.1/') - self.assertEqual(entry.title, 'Version v1.1') - self.assertEqual(entry.updated, '2011-01-21T11:33:21Z') - self.assertEqual(len(entry.content), 1) - self.assertEqual(entry.content[0].value, - 'Version v1.1 CURRENT (2011-01-21T11:33:21Z)') - self.assertEqual(len(entry.links), 3) - self.assertEqual(entry.links[0]['href'], 'http://localhost/v1.1/') - self.assertEqual(entry.links[0]['rel'], 'self') - self.assertEqual(entry.links[1], { - 'rel': 'describedby', - 'type': 'application/pdf', - 'href': 'http://docs.rackspacecloud.com/' - 'servers/api/v1.1/cs-devguide-20110125.pdf'}) - self.assertEqual(entry.links[2], { - 'rel': 'describedby', - 'type': 'application/vnd.sun.wadl+xml', - 'href': 'http://docs.rackspacecloud.com/' - 'servers/api/v1.1/application.wadl', - }) diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py deleted file mode 100644 index 496c3e8e6..000000000 --- a/nova/tests/api/openstack/test_zones.py +++ /dev/null @@ -1,283 +0,0 @@ -# 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. - - -import json - -from lxml import etree -import stubout -import webob - -import nova.db -from nova import context -from nova import crypto -from nova import flags -from nova import test -from nova.api.openstack import xmlutil -from nova.api.openstack import zones -from nova.tests.api.openstack import fakes -from nova.scheduler import api - - -FLAGS = flags.FLAGS - - -def zone_get(context, zone_id): - return dict(id=1, api_url='http://example.com', username='bob', - password='xxx', weight_scale=1.0, weight_offset=0.0, - name='darksecret') - - -def zone_create(context, values): - zone = dict(id=1) - zone.update(values) - return zone - - -def zone_update(context, zone_id, values): - zone = dict(id=zone_id, api_url='http://example.com', username='bob', - password='xxx') - zone.update(values) - return zone - - -def zone_delete(context, zone_id): - pass - - -def zone_get_all_scheduler(*args): - return [ - dict(id=1, api_url='http://example.com', username='bob', - password='xxx', weight_scale=1.0, weight_offset=0.0), - dict(id=2, api_url='http://example.org', username='alice', - password='qwerty', weight_scale=1.0, weight_offset=0.0), - ] - - -def zone_get_all_scheduler_empty(*args): - return [] - - -def zone_get_all_db(context): - return [ - dict(id=1, api_url='http://example.com', username='bob', - password='xxx', weight_scale=1.0, weight_offset=0.0), - dict(id=2, api_url='http://example.org', username='alice', - password='qwerty', weight_scale=1.0, weight_offset=0.0), - ] - - -def zone_capabilities(method, context): - return dict() - - -GLOBAL_BUILD_PLAN = [ - dict(name='host1', weight=10, ip='10.0.0.1', zone='zone1'), - dict(name='host2', weight=9, ip='10.0.0.2', zone='zone2'), - dict(name='host3', weight=8, ip='10.0.0.3', zone='zone3'), - dict(name='host4', weight=7, ip='10.0.0.4', zone='zone4'), - ] - - -def zone_select(context, specs): - return GLOBAL_BUILD_PLAN - - -class ZonesTest(test.TestCase): - def setUp(self): - super(ZonesTest, self).setUp() - self.flags(verbose=True, allow_admin_api=True) - fakes.stub_out_networking(self.stubs) - fakes.stub_out_rate_limiting(self.stubs) - - self.stubs.Set(nova.db, 'zone_get', zone_get) - self.stubs.Set(nova.db, 'zone_update', zone_update) - self.stubs.Set(nova.db, 'zone_create', zone_create) - self.stubs.Set(nova.db, 'zone_delete', zone_delete) - - self.controller = zones.Controller() - - def test_get_zone_list_scheduler(self): - self.stubs.Set(api, '_call_scheduler', zone_get_all_scheduler) - - req = fakes.HTTPRequest.blank('/v1.1/fake/zones') - res_dict = self.controller.index(req) - - self.assertEqual(len(res_dict['zones']), 2) - - def test_get_zone_list_db(self): - self.stubs.Set(api, '_call_scheduler', zone_get_all_scheduler_empty) - self.stubs.Set(nova.db, 'zone_get_all', zone_get_all_db) - - req = fakes.HTTPRequest.blank('/v1.1/fake/zones') - res_dict = self.controller.index(req) - - self.assertEqual(len(res_dict['zones']), 2) - - def test_get_zone_by_id(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/zones/1') - res_dict = self.controller.show(req, 1) - - self.assertEqual(res_dict['zone']['id'], 1) - self.assertEqual(res_dict['zone']['api_url'], 'http://example.com') - self.assertFalse('password' in res_dict['zone']) - - def test_zone_delete(self): - req = fakes.HTTPRequest.blank('/v1.1/fake/zones/1') - self.controller.delete(req, 1) - - def test_zone_create(self): - body = dict(zone=dict(api_url='http://example.com', username='fred', - password='fubar')) - - req = fakes.HTTPRequest.blank('/v1.1/fake/zones') - res_dict = self.controller.create(req, body) - - self.assertEqual(res_dict['zone']['id'], 1) - self.assertEqual(res_dict['zone']['api_url'], 'http://example.com') - self.assertFalse('username' in res_dict['zone']) - - def test_zone_update(self): - body = dict(zone=dict(username='zeb', password='sneaky')) - - req = fakes.HTTPRequest.blank('/v1.1/fake/zones/1') - res_dict = self.controller.update(req, 1, body) - - self.assertEqual(res_dict['zone']['id'], 1) - self.assertEqual(res_dict['zone']['api_url'], 'http://example.com') - self.assertFalse('username' in res_dict['zone']) - - def test_zone_info(self): - caps = ['cap1=a;b', 'cap2=c;d'] - self.flags(zone_name='darksecret', zone_capabilities=caps) - self.stubs.Set(api, '_call_scheduler', zone_capabilities) - - req = fakes.HTTPRequest.blank('/v1.1/fake/zones/info') - res_dict = self.controller.info(req) - - self.assertEqual(res_dict['zone']['name'], 'darksecret') - self.assertEqual(res_dict['zone']['cap1'], 'a;b') - self.assertEqual(res_dict['zone']['cap2'], 'c;d') - - def test_zone_select(self): - key = 'c286696d887c9aa0611bbb3e2025a45a' - self.flags(build_plan_encryption_key=key) - self.stubs.Set(api, 'select', zone_select) - - # Select queries end up being JSON encoded twice. - # Once to a string and again as an HTTP POST Body - body = json.dumps({}) - - req = fakes.HTTPRequest.blank('/v1.1/fake/zones/select') - res_dict = self.controller.select(req, body) - - self.assertTrue('weights' in res_dict) - - for item in res_dict['weights']: - blob = item['blob'] - decrypt = crypto.decryptor(FLAGS.build_plan_encryption_key) - secret_item = json.loads(decrypt(blob)) - found = False - for original_item in GLOBAL_BUILD_PLAN: - if original_item['name'] != secret_item['name']: - continue - found = True - for key in ('weight', 'ip', 'zone'): - self.assertEqual(secret_item[key], original_item[key]) - - self.assertTrue(found) - self.assertEqual(len(item), 2) - self.assertTrue('weight' in item) - - -class TestZonesXMLSerializer(test.TestCase): - - serializer = zones.ZonesXMLSerializer() - - def test_select(self): - key = 'c286696d887c9aa0611bbb3e2025a45a' - - encrypt = crypto.encryptor(key) - decrypt = crypto.decryptor(key) - - item = GLOBAL_BUILD_PLAN[0] - fixture = {'weights': {'blob': encrypt(json.dumps(item)), - 'weight': item['weight']}} - - output = self.serializer.serialize(fixture, 'select') - res_tree = etree.XML(output) - - self.assertEqual(res_tree.tag, '{%s}weights' % xmlutil.XMLNS_V10) - - for item in res_tree: - self.assertEqual(item.tag, '{%s}weight' % xmlutil.XMLNS_V10) - blob = None - weight = None - for chld in item: - if chld.tag.endswith('blob'): - blob = chld.text - elif chld.tag.endswith('weight'): - weight = chld.text - - secret_item = json.loads(decrypt(blob)) - found = False - for original_item in GLOBAL_BUILD_PLAN: - if original_item['name'] != secret_item['name']: - continue - found = True - for key in ('weight', 'ip', 'zone'): - self.assertEqual(secret_item[key], original_item[key]) - - self.assertTrue(found) - self.assertEqual(len(item), 2) - self.assertTrue(weight) - - def test_index(self): - fixture = {'zones': zone_get_all_scheduler()} - - output = self.serializer.serialize(fixture, 'index') - res_tree = etree.XML(output) - - self.assertEqual(res_tree.tag, '{%s}zones' % xmlutil.XMLNS_V10) - self.assertEqual(len(res_tree), 2) - self.assertEqual(res_tree[0].tag, '{%s}zone' % xmlutil.XMLNS_V10) - self.assertEqual(res_tree[1].tag, '{%s}zone' % xmlutil.XMLNS_V10) - - def test_show(self): - zone = {'id': 1, - 'api_url': 'http://example.com', - 'name': 'darksecret', - 'cap1': 'a;b', - 'cap2': 'c;d'} - fixture = {'zone': zone} - - output = self.serializer.serialize(fixture, 'show') - print repr(output) - res_tree = etree.XML(output) - - self.assertEqual(res_tree.tag, '{%s}zone' % xmlutil.XMLNS_V10) - self.assertEqual(res_tree.get('id'), '1') - self.assertEqual(res_tree.get('api_url'), 'http://example.com') - self.assertEqual(res_tree.get('password'), None) - - self.assertEqual(res_tree.get('name'), 'darksecret') - for elem in res_tree: - self.assertEqual(elem.tag in ('{%s}cap1' % xmlutil.XMLNS_V10, - '{%s}cap2' % xmlutil.XMLNS_V10), - True) - if elem.tag == '{%s}cap1' % xmlutil.XMLNS_V10: - self.assertEqual(elem.text, 'a;b') - elif elem.tag == '{%s}cap2' % xmlutil.XMLNS_V10: - self.assertEqual(elem.text, 'c;d') diff --git a/nova/tests/api/openstack/v2/__init__.py b/nova/tests/api/openstack/v2/__init__.py new file mode 100644 index 000000000..00fcfbb00 --- /dev/null +++ b/nova/tests/api/openstack/v2/__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/v2/contrib/__init__.py b/nova/tests/api/openstack/v2/contrib/__init__.py new file mode 100644 index 000000000..848908a95 --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/__init__.py @@ -0,0 +1,15 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. diff --git a/nova/tests/api/openstack/v2/contrib/test_admin_actions.py b/nova/tests/api/openstack/v2/contrib/test_admin_actions.py new file mode 100644 index 000000000..4b62c0ba7 --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_admin_actions.py @@ -0,0 +1,86 @@ +# Copyright 2011 OpenStack LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import datetime +import json +import webob + +from nova import compute +from nova import flags +from nova import test +from nova.tests.api.openstack import fakes + + +FLAGS = flags.FLAGS + +INSTANCE = { + "id": 1, + "name": "fake", + "display_name": "test_server", + "uuid": "abcd", + "user_id": 'fake_user_id', + "tenant_id": 'fake_tenant_id', + "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), + "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), + "security_groups": [{"id": 1, "name": "test"}], + "progress": 0, + "image_ref": 'http://foo.com/123', + "fixed_ips": [], + "instance_type": {"flavorid": '124'}, + } + + +def fake_compute_api(cls, req, id): + return True + + +def fake_compute_api_get(self, context, instance_id): + return {'id': 1, 'uuid': instance_id} + + +class AdminActionsTest(test.TestCase): + + _actions = ('pause', 'unpause', 'suspend', 'resume', 'migrate', + 'resetNetwork', 'injectNetworkInfo', 'lock', 'unlock') + + _methods = ('pause', 'unpause', 'suspend', 'resume', 'resize', + 'reset_network', 'inject_network_info', 'lock', 'unlock') + + def setUp(self): + super(AdminActionsTest, self).setUp() + self.flags(allow_admin_api=True) + self.stubs.Set(compute.API, 'get', fake_compute_api_get) + for _method in self._methods: + self.stubs.Set(compute.API, _method, fake_compute_api) + + def test_admin_api_enabled(self): + app = fakes.wsgi_app() + for _action in self._actions: + req = webob.Request.blank('/v1.1/fake/servers/abcd/action') + req.method = 'POST' + req.body = json.dumps({_action: None}) + req.content_type = 'application/json' + res = req.get_response(app) + self.assertEqual(res.status_int, 202) + + def test_admin_api_disabled(self): + FLAGS.allow_admin_api = False + app = fakes.wsgi_app() + for _action in self._actions: + req = webob.Request.blank('/v1.1/fake/servers/abcd/action') + req.method = 'POST' + req.body = json.dumps({_action: None}) + req.content_type = 'application/json' + res = req.get_response(app) + self.assertEqual(res.status_int, 404) diff --git a/nova/tests/api/openstack/v2/contrib/test_createserverext.py b/nova/tests/api/openstack/v2/contrib/test_createserverext.py new file mode 100644 index 000000000..5c9e0499a --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_createserverext.py @@ -0,0 +1,431 @@ +# vim: tabstop=5 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 base64 +import datetime +import json +from xml.dom import minidom + +import webob + +import nova +from nova import db +from nova import exception +from nova import flags +from nova import rpc +from nova import test +from nova.tests.api.openstack import fakes + + +FLAGS = flags.FLAGS +FLAGS.verbose = True + +FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' + +FAKE_NETWORKS = [('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', '10.0.1.12'), + ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', '10.0.2.12')] + +DUPLICATE_NETWORKS = [('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', '10.0.1.12'), + ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', '10.0.1.12')] + +INVALID_NETWORKS = [('invalid', 'invalid-ip-address')] + +INSTANCE = { + "id": 1, + "name": "fake", + "display_name": "test_server", + "uuid": FAKE_UUID, + "user_id": 'fake_user_id', + "tenant_id": 'fake_tenant_id', + "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), + "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), + "security_groups": [{"id": 1, "name": "test"}], + "progress": 0, + "image_ref": 'http://foo.com/123', + "fixed_ips": [], + "instance_type": {"flavorid": '124'}, + } + + +def return_server_by_id(context, id, session=None): + INSTANCE['id'] = id + return INSTANCE + + +def return_security_group_non_existing(context, project_id, group_name): + raise exception.SecurityGroupNotFoundForProject(project_id=project_id, + security_group_id=group_name) + + +def return_security_group_get_by_name(context, project_id, group_name): + return {'id': 1, 'name': group_name} + + +def return_security_group_get(context, security_group_id, session): + return {'id': security_group_id} + + +def return_instance_add_security_group(context, instance_id, + security_group_id): + pass + + +class CreateserverextTest(test.TestCase): + + def setUp(self): + super(CreateserverextTest, self).setUp() + + def tearDown(self): + super(CreateserverextTest, self).tearDown() + + def _make_stub_method(self, canned_return): + def stub_method(*args, **kwargs): + return canned_return + return stub_method + + def _setup_mock_compute_api(self): + + class MockComputeAPI(nova.compute.API): + + def __init__(self): + self.injected_files = None + self.networks = None + self.user_data = None + self.db = db + + def create(self, *args, **kwargs): + if 'injected_files' in kwargs: + self.injected_files = kwargs['injected_files'] + else: + self.injected_files = None + + if 'requested_networks' in kwargs: + self.networks = kwargs['requested_networks'] + else: + self.networks = None + + if 'user_data' in kwargs: + self.user_data = kwargs['user_data'] + + resv_id = None + + return ([{'id': '1234', 'display_name': 'fakeinstance', + 'uuid': FAKE_UUID, + 'user_id': 'fake', + 'project_id': 'fake', + 'created_at': "", + 'updated_at': "", + 'fixed_ips': [], + 'progress': 0}], resv_id) + + def set_admin_password(self, *args, **kwargs): + pass + + compute_api = MockComputeAPI() + self.stubs.Set(nova.compute, 'API', + self._make_stub_method(compute_api)) + return compute_api + + def _setup_mock_network_api(self): + fakes.stub_out_nw_api(self.stubs) + + def _create_security_group_request_dict(self, security_groups): + server = {} + server['name'] = 'new-server-test' + server['imageRef'] = 'cedef40a-ed67-4d10-800e-17455edce175' + server['flavorRef'] = 1 + if security_groups is not None: + sg_list = [] + for name in security_groups: + sg_list.append({'name': name}) + server['security_groups'] = sg_list + return {'server': server} + + def _create_networks_request_dict(self, networks): + server = {} + server['name'] = 'new-server-test' + server['imageRef'] = 'cedef40a-ed67-4d10-800e-17455edce175' + server['flavorRef'] = 1 + if networks is not None: + network_list = [] + for uuid, fixed_ip in networks: + network_list.append({'uuid': uuid, 'fixed_ip': fixed_ip}) + server['networks'] = network_list + return {'server': server} + + def _create_user_data_request_dict(self, user_data): + server = {} + server['name'] = 'new-server-test' + server['imageRef'] = 'cedef40a-ed67-4d10-800e-17455edce175' + server['flavorRef'] = 1 + server['user_data'] = user_data + return {'server': server} + + def _get_create_request_json(self, body_dict): + req = webob.Request.blank('/v1.1/123/os-create-server-ext') + req.headers['Content-Type'] = 'application/json' + req.method = 'POST' + req.body = json.dumps(body_dict) + return req + + def _run_create_instance_with_mock_compute_api(self, request): + compute_api = self._setup_mock_compute_api() + self._setup_mock_network_api() + response = request.get_response(fakes.wsgi_app()) + return compute_api, response + + def _format_xml_request_body(self, body_dict): + server = body_dict['server'] + body_parts = [] + body_parts.extend([ + '', + '' % ( + server['name'], server['imageRef'], server['flavorRef'])]) + if 'metadata' in server: + metadata = server['metadata'] + body_parts.append('') + for item in metadata.iteritems(): + body_parts.append('%s' % item) + body_parts.append('') + if 'personality' in server: + personalities = server['personality'] + body_parts.append('') + for file in personalities: + item = (file['path'], file['contents']) + body_parts.append('%s' % item) + body_parts.append('') + if 'networks' in server: + networks = server['networks'] + body_parts.append('') + for network in networks: + item = (network['uuid'], network['fixed_ip']) + body_parts.append('' + % item) + body_parts.append('') + body_parts.append('') + return ''.join(body_parts) + + def _get_create_request_xml(self, body_dict): + req = webob.Request.blank('/v1.1/123/os-create-server-ext') + req.content_type = 'application/xml' + req.accept = 'application/xml' + req.method = 'POST' + req.body = self._format_xml_request_body(body_dict) + return req + + def _create_instance_with_networks_json(self, networks): + body_dict = self._create_networks_request_dict(networks) + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + return request, response, compute_api.networks + + def _create_instance_with_user_data_json(self, networks): + body_dict = self._create_user_data_request_dict(networks) + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + return request, response, compute_api.user_data + + def _create_instance_with_networks_xml(self, networks): + body_dict = self._create_networks_request_dict(networks) + request = self._get_create_request_xml(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + return request, response, compute_api.networks + + def test_create_instance_with_no_networks(self): + request, response, networks = \ + self._create_instance_with_networks_json(networks=None) + self.assertEquals(response.status_int, 202) + self.assertEquals(networks, None) + + def test_create_instance_with_no_networks_xml(self): + request, response, networks = \ + self._create_instance_with_networks_xml(networks=None) + self.assertEquals(response.status_int, 202) + self.assertEquals(networks, None) + + def test_create_instance_with_one_network(self): + request, response, networks = \ + self._create_instance_with_networks_json([FAKE_NETWORKS[0]]) + self.assertEquals(response.status_int, 202) + self.assertEquals(networks, [FAKE_NETWORKS[0]]) + + def test_create_instance_with_one_network_xml(self): + request, response, networks = \ + self._create_instance_with_networks_xml([FAKE_NETWORKS[0]]) + self.assertEquals(response.status_int, 202) + self.assertEquals(networks, [FAKE_NETWORKS[0]]) + + def test_create_instance_with_two_networks(self): + request, response, networks = \ + self._create_instance_with_networks_json(FAKE_NETWORKS) + self.assertEquals(response.status_int, 202) + self.assertEquals(networks, FAKE_NETWORKS) + + def test_create_instance_with_two_networks_xml(self): + request, response, networks = \ + self._create_instance_with_networks_xml(FAKE_NETWORKS) + self.assertEquals(response.status_int, 202) + self.assertEquals(networks, FAKE_NETWORKS) + + def test_create_instance_with_duplicate_networks(self): + request, response, networks = \ + self._create_instance_with_networks_json(DUPLICATE_NETWORKS) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_duplicate_networks_xml(self): + request, response, networks = \ + self._create_instance_with_networks_xml(DUPLICATE_NETWORKS) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_no_id(self): + body_dict = self._create_networks_request_dict([FAKE_NETWORKS[0]]) + del body_dict['server']['networks'][0]['uuid'] + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 400) + self.assertEquals(compute_api.networks, None) + + def test_create_instance_with_network_no_id_xml(self): + body_dict = self._create_networks_request_dict([FAKE_NETWORKS[0]]) + request = self._get_create_request_xml(body_dict) + uuid = ' uuid="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"' + request.body = request.body.replace(uuid, '') + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 400) + self.assertEquals(compute_api.networks, None) + + def test_create_instance_with_network_invalid_id(self): + request, response, networks = \ + self._create_instance_with_networks_json(INVALID_NETWORKS) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_invalid_id_xml(self): + request, response, networks = \ + self._create_instance_with_networks_xml(INVALID_NETWORKS) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_empty_fixed_ip(self): + networks = [('1', '')] + request, response, networks = \ + self._create_instance_with_networks_json(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_non_string_fixed_ip(self): + networks = [('1', 12345)] + request, response, networks = \ + self._create_instance_with_networks_json(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_empty_fixed_ip_xml(self): + networks = [('1', '')] + request, response, networks = \ + self._create_instance_with_networks_xml(networks) + self.assertEquals(response.status_int, 400) + self.assertEquals(networks, None) + + def test_create_instance_with_network_no_fixed_ip(self): + body_dict = self._create_networks_request_dict([FAKE_NETWORKS[0]]) + del body_dict['server']['networks'][0]['fixed_ip'] + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 202) + self.assertEquals(compute_api.networks, + [('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', None)]) + + def test_create_instance_with_network_no_fixed_ip_xml(self): + body_dict = self._create_networks_request_dict([FAKE_NETWORKS[0]]) + request = self._get_create_request_xml(body_dict) + request.body = request.body.replace(' fixed_ip="10.0.1.12"', '') + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 202) + self.assertEquals(compute_api.networks, + [('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', None)]) + + def test_create_instance_with_userdata(self): + user_data_contents = '#!/bin/bash\necho "Oh no!"\n' + user_data_contents = base64.b64encode(user_data_contents) + request, response, user_data = \ + self._create_instance_with_user_data_json(user_data_contents) + self.assertEquals(response.status_int, 202) + self.assertEquals(user_data, user_data_contents) + + def test_create_instance_with_userdata_none(self): + user_data_contents = None + request, response, user_data = \ + self._create_instance_with_user_data_json(user_data_contents) + self.assertEquals(response.status_int, 202) + self.assertEquals(user_data, user_data_contents) + + def test_create_instance_with_userdata_with_non_b64_content(self): + user_data_contents = '#!/bin/bash\necho "Oh no!"\n' + request, response, user_data = \ + self._create_instance_with_user_data_json(user_data_contents) + self.assertEquals(response.status_int, 400) + self.assertEquals(user_data, None) + + def test_create_instance_with_security_group_json(self): + security_groups = ['test', 'test1'] + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group_get_by_name) + self.stubs.Set(nova.db, 'instance_add_security_group', + return_instance_add_security_group) + self._setup_mock_network_api() + body_dict = self._create_security_group_request_dict(security_groups) + request = self._get_create_request_json(body_dict) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) + self.assertEquals(response.status_int, 202) + + def test_get_server_by_id_verify_security_groups_json(self): + self.stubs.Set(nova.db, 'instance_get', return_server_by_id) + self._setup_mock_network_api() + req = webob.Request.blank('/v1.1/123/os-create-server-ext/1') + req.headers['Content-Type'] = 'application/json' + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 200) + res_dict = json.loads(response.body) + expected_security_group = [{"name": "test"}] + self.assertEquals(res_dict['server']['security_groups'], + expected_security_group) + + def test_get_server_by_id_verify_security_groups_xml(self): + self.stubs.Set(nova.db, 'instance_get', return_server_by_id) + self._setup_mock_network_api() + req = webob.Request.blank('/v1.1/123/os-create-server-ext/1') + req.headers['Accept'] = 'application/xml' + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 200) + dom = minidom.parseString(response.body) + server = dom.childNodes[0] + sec_groups = server.getElementsByTagName('security_groups')[0] + sec_group = sec_groups.getElementsByTagName('security_group')[0] + self.assertEqual(INSTANCE['security_groups'][0]['name'], + sec_group.getAttribute("name")) diff --git a/nova/tests/api/openstack/v2/contrib/test_disk_config.py b/nova/tests/api/openstack/v2/contrib/test_disk_config.py new file mode 100644 index 000000000..ac2391a04 --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_disk_config.py @@ -0,0 +1,248 @@ +# 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. + +import datetime + +from nova.api.openstack import v2 +from nova.api.openstack.v2 import extensions +from nova.api.openstack import wsgi +import nova.db.api +from nova import flags +import nova.rpc +from nova import test +from nova.tests.api.openstack import fakes +from nova import utils + + +MANUAL_INSTANCE_UUID = fakes.FAKE_UUID +AUTO_INSTANCE_UUID = fakes.FAKE_UUID.replace('a', 'b') + +stub_instance = fakes.stub_instance +FLAGS = flags.FLAGS + + +def instance_addresses(context, instance_id): + return None + + +class DiskConfigTestCase(test.TestCase): + + def setUp(self): + super(DiskConfigTestCase, self).setUp() + self.flags(verbose=True) + fakes.stub_out_nw_api(self.stubs) + + FAKE_INSTANCES = [ + fakes.stub_instance(1, + uuid=MANUAL_INSTANCE_UUID, + auto_disk_config=False), + fakes.stub_instance(2, + uuid=AUTO_INSTANCE_UUID, + auto_disk_config=True) + ] + + def fake_instance_get(context, id_): + for instance in FAKE_INSTANCES: + if id_ == instance['id']: + return instance + + self.stubs.Set(nova.db.api, 'instance_get', fake_instance_get) + self.stubs.Set(nova.db, 'instance_get', fake_instance_get) + + def fake_instance_get_by_uuid(context, uuid): + for instance in FAKE_INSTANCES: + if uuid == instance['uuid']: + return instance + + self.stubs.Set(nova.db, 'instance_get_by_uuid', + fake_instance_get_by_uuid) + + def fake_instance_get_all(context, *args, **kwargs): + return FAKE_INSTANCES + + self.stubs.Set(nova.db, 'instance_get_all', fake_instance_get_all) + self.stubs.Set(nova.db.api, 'instance_get_all_by_filters', + fake_instance_get_all) + + def fake_instance_create(context, inst_, session=None): + class FakeModel(dict): + def save(self, session=None): + pass + + inst = FakeModel(**inst_) + inst['id'] = 1 + inst['uuid'] = AUTO_INSTANCE_UUID + inst['created_at'] = datetime.datetime(2010, 10, 10, 12, 0, 0) + inst['updated_at'] = datetime.datetime(2010, 10, 10, 12, 0, 0) + inst['progress'] = 0 + inst['name'] = 'instance-1' # this is a property + + def fake_instance_get_for_create(context, id_, session=None): + return inst + + self.stubs.Set(nova.db, 'instance_get', + fake_instance_get_for_create) + self.stubs.Set(nova.db.api, 'instance_get', + fake_instance_get_for_create) + self.stubs.Set(nova.db.sqlalchemy.api, 'instance_get', + fake_instance_get_for_create) + + def fake_instance_add_security_group(context, instance_id, + security_group_id): + pass + + self.stubs.Set(nova.db.sqlalchemy.api, + 'instance_add_security_group', + fake_instance_add_security_group) + + return inst + + self.stubs.Set(nova.db, 'instance_create', fake_instance_create) + + app = v2.APIRouter() + app = extensions.ExtensionMiddleware(app) + app = wsgi.LazySerializationMiddleware(app) + self.app = app + + def assertDiskConfig(self, dict_, value): + self.assert_('RAX-DCF:diskConfig' in dict_) + self.assertEqual(dict_['RAX-DCF:diskConfig'], value) + + def test_show_server(self): + req = fakes.HTTPRequest.blank( + '/fake/servers/%s' % MANUAL_INSTANCE_UUID) + res = req.get_response(self.app) + server_dict = utils.loads(res.body)['server'] + self.assertDiskConfig(server_dict, 'MANUAL') + + req = fakes.HTTPRequest.blank( + '/fake/servers/%s' % AUTO_INSTANCE_UUID) + res = req.get_response(self.app) + server_dict = utils.loads(res.body)['server'] + self.assertDiskConfig(server_dict, 'AUTO') + + def test_detail_servers(self): + req = fakes.HTTPRequest.blank('/fake/servers/detail') + res = req.get_response(self.app) + server_dicts = utils.loads(res.body)['servers'] + + expectations = ['MANUAL', 'AUTO'] + for server_dict, expected in zip(server_dicts, expectations): + self.assertDiskConfig(server_dict, expected) + + def test_show_image(self): + req = fakes.HTTPRequest.blank( + '/fake/images/a440c04b-79fa-479c-bed1-0b816eaec379') + res = req.get_response(self.app) + image_dict = utils.loads(res.body)['image'] + self.assertDiskConfig(image_dict, 'MANUAL') + + req = fakes.HTTPRequest.blank( + '/fake/images/70a599e0-31e7-49b7-b260-868f441e862b') + res = req.get_response(self.app) + image_dict = utils.loads(res.body)['image'] + self.assertDiskConfig(image_dict, 'AUTO') + + def test_detail_image(self): + req = fakes.HTTPRequest.blank('/fake/images/detail') + res = req.get_response(self.app) + image_dicts = utils.loads(res.body)['images'] + + expectations = ['MANUAL', 'AUTO'] + for image_dict, expected in zip(image_dicts, expectations): + # NOTE(sirp): image fixtures 6 and 7 are setup for + # auto_disk_config testing + if image_dict['id'] in (6, 7): + self.assertDiskConfig(image_dict, expected) + + def test_create_server_override_auto(self): + req = fakes.HTTPRequest.blank('/fake/servers') + req.method = 'POST' + req.content_type = 'application/json' + body = {'server': { + 'name': 'server_test', + 'imageRef': 'cedef40a-ed67-4d10-800e-17455edce175', + 'flavorRef': '1', + 'RAX-DCF:diskConfig': 'AUTO' + }} + + req.body = utils.dumps(body) + res = req.get_response(self.app) + server_dict = utils.loads(res.body)['server'] + self.assertDiskConfig(server_dict, 'AUTO') + + def test_create_server_override_manual(self): + req = fakes.HTTPRequest.blank('/fake/servers') + req.method = 'POST' + req.content_type = 'application/json' + body = {'server': { + 'name': 'server_test', + 'imageRef': 'cedef40a-ed67-4d10-800e-17455edce175', + 'flavorRef': '1', + 'RAX-DCF:diskConfig': 'MANUAL' + }} + + req.body = utils.dumps(body) + res = req.get_response(self.app) + server_dict = utils.loads(res.body)['server'] + self.assertDiskConfig(server_dict, 'MANUAL') + + def test_create_server_detect_from_image(self): + """If user doesn't pass in diskConfig for server, use image metadata + to specify AUTO or MANUAL. + """ + req = fakes.HTTPRequest.blank('/fake/servers') + req.method = 'POST' + req.content_type = 'application/json' + body = {'server': { + 'name': 'server_test', + 'imageRef': 'a440c04b-79fa-479c-bed1-0b816eaec379', + 'flavorRef': '1', + }} + + req.body = utils.dumps(body) + res = req.get_response(self.app) + server_dict = utils.loads(res.body)['server'] + self.assertDiskConfig(server_dict, 'MANUAL') + + req = fakes.HTTPRequest.blank('/fake/servers') + req.method = 'POST' + req.content_type = 'application/json' + body = {'server': { + 'name': 'server_test', + 'imageRef': '70a599e0-31e7-49b7-b260-868f441e862b', + 'flavorRef': '1', + }} + + req.body = utils.dumps(body) + res = req.get_response(self.app) + server_dict = utils.loads(res.body)['server'] + self.assertDiskConfig(server_dict, 'AUTO') + + def test_update_server_invalid_disk_config(self): + """Return BadRequest if user passes an invalid diskConfig value.""" + req = fakes.HTTPRequest.blank( + '/fake/servers/%s' % MANUAL_INSTANCE_UUID) + req.method = 'PUT' + req.content_type = 'application/json' + body = {'server': {'RAX-DCF:diskConfig': 'server_test'}} + req.body = utils.dumps(body) + res = req.get_response(self.app) + self.assertEqual(res.status_int, 400) + expected_msg = '{"badRequest": {"message": "RAX-DCF:diskConfig must'\ + ' be either \'MANUAL\' or \'AUTO\'.", "code": 400}}' + self.assertEqual(res.body, expected_msg) diff --git a/nova/tests/api/openstack/v2/contrib/test_extendedstatus.py b/nova/tests/api/openstack/v2/contrib/test_extendedstatus.py new file mode 100644 index 000000000..54246727c --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_extendedstatus.py @@ -0,0 +1,88 @@ +# 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. + +import json + +import webob + +from nova import compute +from nova import exception +from nova import flags +from nova import test +from nova.tests.api.openstack import fakes + + +FLAGS = flags.FLAGS +FLAGS.verbose = True + + +def fake_compute_get(*args, **kwargs): + return fakes.stub_instance(1, task_state="kayaking", + vm_state="slightly crunchy", + power_state="empowered") + + +class ExtendedStatusTest(test.TestCase): + + def setUp(self): + super(ExtendedStatusTest, self).setUp() + self.uuid = '70f6db34-de8d-4fbd-aafb-4065bdfa6114' + self.url = '/v1.1/openstack/servers/%s' % self.uuid + fakes.stub_out_nw_api(self.stubs) + self.stubs.Set(compute.api.API, 'routing_get', fake_compute_get) + + def _make_request(self): + req = webob.Request.blank(self.url) + req.headers['Accept'] = 'application/json' + res = req.get_response(fakes.wsgi_app()) + return res + + def assertServerStates(self, server, vm_state, power_state, task_state): + self.assertEqual(server.get('OS-EXT-STS:vm_state'), vm_state) + self.assertEqual(server.get('OS-EXT-STS:power_state'), power_state) + self.assertEqual(server.get('OS-EXT-STS:task_state'), task_state) + + def test_extended_status_with_admin(self): + self.flags(allow_admin_api=True) + res = self._make_request() + body = json.loads(res.body) + + self.assertEqual(res.status_int, 200) + self.assertServerStates(body['server'], + vm_state='slightly crunchy', + power_state='empowered', + task_state='kayaking') + + def test_extended_status_no_admin(self): + self.flags(allow_admin_api=False) + res = self._make_request() + body = json.loads(res.body) + + self.assertEqual(res.status_int, 200) + self.assertServerStates(body['server'], + vm_state=None, + power_state=None, + task_state=None) + + def test_extended_status_no_instance_fails(self): + self.flags(allow_admin_api=True) + + def fake_compute_get(*args, **kwargs): + raise exception.InstanceNotFound() + + self.stubs.Set(compute.api.API, 'routing_get', fake_compute_get) + res = self._make_request() + + self.assertEqual(res.status_int, 404) diff --git a/nova/tests/api/openstack/v2/contrib/test_flavors_extra_specs.py b/nova/tests/api/openstack/v2/contrib/test_flavors_extra_specs.py new file mode 100644 index 000000000..5f9be886f --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_flavors_extra_specs.py @@ -0,0 +1,170 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 University of Southern California +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json +import os.path + +import stubout +import webob + +from nova.api.openstack.v2 import extensions +from nova.api.openstack.v2.contrib import flavorextraspecs +from nova import test +from nova.tests.api.openstack import fakes +import nova.wsgi + + +def return_create_flavor_extra_specs(context, flavor_id, extra_specs): + return stub_flavor_extra_specs() + + +def return_flavor_extra_specs(context, flavor_id): + return stub_flavor_extra_specs() + + +def return_empty_flavor_extra_specs(context, flavor_id): + return {} + + +def delete_flavor_extra_specs(context, flavor_id, key): + pass + + +def stub_flavor_extra_specs(): + specs = { + "key1": "value1", + "key2": "value2", + "key3": "value3", + "key4": "value4", + "key5": "value5"} + return specs + + +class FlavorsExtraSpecsTest(test.TestCase): + + def setUp(self): + super(FlavorsExtraSpecsTest, self).setUp() + fakes.stub_out_key_pair_funcs(self.stubs) + self.controller = flavorextraspecs.FlavorExtraSpecsController() + + def test_index(self): + self.stubs.Set(nova.db, 'instance_type_extra_specs_get', + return_flavor_extra_specs) + + req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs') + res_dict = self.controller.index(req, 1) + + self.assertEqual('value1', res_dict['extra_specs']['key1']) + + def test_index_no_data(self): + self.stubs.Set(nova.db, 'instance_type_extra_specs_get', + return_empty_flavor_extra_specs) + + req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs') + res_dict = self.controller.index(req, 1) + + self.assertEqual(0, len(res_dict['extra_specs'])) + + def test_show(self): + self.stubs.Set(nova.db, 'instance_type_extra_specs_get', + return_flavor_extra_specs) + + req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs' + + '/key5') + res_dict = self.controller.show(req, 1, 'key5') + + self.assertEqual('value5', res_dict['key5']) + + def test_show_spec_not_found(self): + self.stubs.Set(nova.db, 'instance_type_extra_specs_get', + return_empty_flavor_extra_specs) + + req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs' + + '/key6') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, + req, 1, 'key6') + + def test_delete(self): + self.stubs.Set(nova.db, 'instance_type_extra_specs_delete', + delete_flavor_extra_specs) + + req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs' + + '/key5') + self.controller.delete(req, 1, 'key5') + + def test_create(self): + self.stubs.Set(nova.db, + 'instance_type_extra_specs_update_or_create', + return_create_flavor_extra_specs) + body = {"extra_specs": {"key1": "value1"}} + + req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs') + res_dict = self.controller.create(req, 1, body) + + self.assertEqual('value1', res_dict['extra_specs']['key1']) + + def test_create_empty_body(self): + self.stubs.Set(nova.db, + 'instance_type_extra_specs_update_or_create', + return_create_flavor_extra_specs) + + req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, 1, '') + + def test_update_item(self): + self.stubs.Set(nova.db, + 'instance_type_extra_specs_update_or_create', + return_create_flavor_extra_specs) + body = {"key1": "value1"} + + req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs' + + '/key1') + res_dict = self.controller.update(req, 1, 'key1', body) + + self.assertEqual('value1', res_dict['key1']) + + def test_update_item_empty_body(self): + self.stubs.Set(nova.db, + 'instance_type_extra_specs_update_or_create', + return_create_flavor_extra_specs) + + req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs' + + '/key1') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + req, 1, 'key1', '') + + def test_update_item_too_many_keys(self): + self.stubs.Set(nova.db, + 'instance_type_extra_specs_update_or_create', + return_create_flavor_extra_specs) + body = {"key1": "value1", "key2": "value2"} + + req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs' + + '/key1') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + req, 1, 'key1', body) + + def test_update_item_body_uri_mismatch(self): + self.stubs.Set(nova.db, + 'instance_type_extra_specs_update_or_create', + return_create_flavor_extra_specs) + body = {"key1": "value1"} + + req = fakes.HTTPRequest.blank('/v1.1/123/flavors/1/os-extra_specs/bad') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + req, 1, 'bad', body) diff --git a/nova/tests/api/openstack/v2/contrib/test_floating_ips.py b/nova/tests/api/openstack/v2/contrib/test_floating_ips.py new file mode 100644 index 000000000..ded363406 --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_floating_ips.py @@ -0,0 +1,269 @@ +# Copyright 2011 Eldar Nugaev +# 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 json +import stubout + +import webob + +from nova.api.openstack.v2.contrib import floating_ips +from nova.api.openstack.v2.contrib import floating_ips +from nova import compute +from nova import context +from nova import db +from nova import network +from nova import rpc +from nova import test +from nova.tests.api.openstack import fakes +from nova.tests.api.openstack.v2 import test_servers + + +def network_api_get_floating_ip(self, context, id): + return {'id': 1, 'address': '10.10.10.10', + 'fixed_ip': None} + + +def network_api_get_floating_ip_by_address(self, context, address): + return {'id': 1, 'address': '10.10.10.10', + 'fixed_ip': {'address': '10.0.0.1', 'instance_id': 1}} + + +def network_api_get_floating_ips_by_project(self, context): + return [{'id': 1, + 'address': '10.10.10.10', + 'fixed_ip': {'address': '10.0.0.1', 'instance_id': 1}}, + {'id': 2, + 'address': '10.10.10.11'}] + + +def network_api_allocate(self, context): + return '10.10.10.10' + + +def network_api_release(self, context, address): + pass + + +def compute_api_associate(self, context, instance_id, address): + pass + + +def network_api_associate(self, context, floating_address, fixed_address): + pass + + +def network_api_disassociate(self, context, floating_address): + pass + + +def network_get_instance_nw_info(self, context, instance): + info = { + 'label': 'fake', + 'gateway': 'fake', + 'dhcp_server': 'fake', + 'broadcast': 'fake', + 'mac': 'fake', + 'vif_uuid': 'fake', + 'rxtx_cap': 'fake', + 'dns': [], + 'ips': [{'ip': '10.0.0.1'}], + 'should_create_bridge': False, + 'should_create_vlan': False} + + return [['ignore', info]] + + +def fake_instance_get(context, instance_id): + return { + "id": 1, + "name": 'fake', + "user_id": 'fakeuser', + "project_id": '123'} + + +class StubExtensionManager(object): + def register(self, *args): + pass + + +class FloatingIpTest(test.TestCase): + address = "10.10.10.10" + + def _create_floating_ip(self): + """Create a floating ip object.""" + host = "fake_host" + return db.floating_ip_create(self.context, + {'address': self.address, + 'host': host}) + + def _delete_floating_ip(self): + db.floating_ip_destroy(self.context, self.address) + + def setUp(self): + super(FloatingIpTest, self).setUp() + self.stubs.Set(network.api.API, "get_floating_ip", + network_api_get_floating_ip) + self.stubs.Set(network.api.API, "get_floating_ip_by_address", + network_api_get_floating_ip_by_address) + self.stubs.Set(network.api.API, "get_floating_ips_by_project", + network_api_get_floating_ips_by_project) + self.stubs.Set(network.api.API, "release_floating_ip", + network_api_release) + self.stubs.Set(network.api.API, "disassociate_floating_ip", + network_api_disassociate) + self.stubs.Set(network.api.API, "get_instance_nw_info", + network_get_instance_nw_info) + self.stubs.Set(db, 'instance_get', + fake_instance_get) + + self.context = context.get_admin_context() + self._create_floating_ip() + + self.controller = floating_ips.FloatingIPController() + self.manager = floating_ips.Floating_ips(StubExtensionManager()) + + def tearDown(self): + self._delete_floating_ip() + super(FloatingIpTest, self).tearDown() + + def test_translate_floating_ip_view(self): + floating_ip_address = self._create_floating_ip() + floating_ip = db.floating_ip_get_by_address(self.context, + floating_ip_address) + view = floating_ips._translate_floating_ip_view(floating_ip) + self.assertTrue('floating_ip' in view) + self.assertTrue(view['floating_ip']['id']) + self.assertEqual(view['floating_ip']['ip'], self.address) + self.assertEqual(view['floating_ip']['fixed_ip'], None) + self.assertEqual(view['floating_ip']['instance_id'], None) + + def test_translate_floating_ip_view_dict(self): + floating_ip = {'id': 0, 'address': '10.0.0.10', 'fixed_ip': None} + view = floating_ips._translate_floating_ip_view(floating_ip) + self.assertTrue('floating_ip' in view) + + def test_floating_ips_list(self): + req = fakes.HTTPRequest.blank('/v1.1/123/os-floating-ips') + res_dict = self.controller.index(req) + + response = {'floating_ips': [{'instance_id': 1, + 'ip': '10.10.10.10', + 'fixed_ip': '10.0.0.1', + 'id': 1}, + {'instance_id': None, + 'ip': '10.10.10.11', + 'fixed_ip': None, + 'id': 2}]} + self.assertEqual(res_dict, response) + + def test_floating_ip_show(self): + req = fakes.HTTPRequest.blank('/v1.1/123/os-floating-ips/1') + res_dict = self.controller.show(req, 1) + + self.assertEqual(res_dict['floating_ip']['id'], 1) + self.assertEqual(res_dict['floating_ip']['ip'], '10.10.10.10') + self.assertEqual(res_dict['floating_ip']['instance_id'], None) + + def test_show_associated_floating_ip(self): + def get_floating_ip(self, context, id): + return {'id': 1, 'address': '10.10.10.10', + 'fixed_ip': {'address': '10.0.0.1', 'instance_id': 1}} + self.stubs.Set(network.api.API, "get_floating_ip", get_floating_ip) + + req = fakes.HTTPRequest.blank('/v1.1/123/os-floating-ips/1') + res_dict = self.controller.show(req, 1) + + self.assertEqual(res_dict['floating_ip']['id'], 1) + self.assertEqual(res_dict['floating_ip']['ip'], '10.10.10.10') + self.assertEqual(res_dict['floating_ip']['instance_id'], 1) + +# test floating ip allocate/release(deallocate) + def test_floating_ip_allocate_no_free_ips(self): + def fake_call(*args, **kwargs): + raise(rpc.RemoteError('NoMoreFloatingIps', '', '')) + + self.stubs.Set(rpc, "call", fake_call) + + req = fakes.HTTPRequest.blank('/v1.1/123/os-floating-ips') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req) + + def test_floating_ip_allocate(self): + def fake1(*args, **kwargs): + pass + + def fake2(*args, **kwargs): + return {'id': 1, 'address': '10.10.10.10'} + + self.stubs.Set(network.api.API, "allocate_floating_ip", + fake1) + self.stubs.Set(network.api.API, "get_floating_ip_by_address", + fake2) + + req = fakes.HTTPRequest.blank('/v1.1/123/os-floating-ips') + res_dict = self.controller.create(req) + + ip = res_dict['floating_ip'] + + expected = { + "id": 1, + "instance_id": None, + "ip": "10.10.10.10", + "fixed_ip": None} + self.assertEqual(ip, expected) + + def test_floating_ip_release(self): + req = fakes.HTTPRequest.blank('/v1.1/123/os-floating-ips/1') + self.controller.delete(req, 1) + +# test floating ip add/remove -> associate/disassociate + + def test_floating_ip_associate(self): + body = dict(addFloatingIp=dict(address=self.address)) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/test_inst/action') + self.manager._add_floating_ip(body, req, 'test_inst') + + def test_floating_ip_disassociate(self): + body = dict(removeFloatingIp=dict(address='10.10.10.10')) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/test_inst/action') + self.manager._remove_floating_ip(body, req, 'test_inst') + +# these are a few bad param tests + + def test_bad_address_param_in_remove_floating_ip(self): + body = dict(removeFloatingIp=dict(badparam='11.0.0.1')) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/test_inst/action') + self.assertRaises(webob.exc.HTTPBadRequest, + self.manager._add_floating_ip, body, req, + 'test_inst') + + def test_missing_dict_param_in_remove_floating_ip(self): + body = dict(removeFloatingIp='11.0.0.1') + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/test_inst/action') + self.assertRaises(webob.exc.HTTPBadRequest, + self.manager._remove_floating_ip, body, req, + 'test_inst') + + def test_missing_dict_param_in_add_floating_ip(self): + body = dict(addFloatingIp='11.0.0.1') + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/test_inst/action') + self.assertRaises(webob.exc.HTTPBadRequest, + self.manager._add_floating_ip, body, req, + 'test_inst') diff --git a/nova/tests/api/openstack/v2/contrib/test_keypairs.py b/nova/tests/api/openstack/v2/contrib/test_keypairs.py new file mode 100644 index 000000000..b2f595e5e --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_keypairs.py @@ -0,0 +1,113 @@ +# Copyright 2011 Eldar Nugaev +# 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 json + +import webob + +from nova.api.openstack.v2.contrib.keypairs import KeypairController +from nova import context +from nova import db +from nova import test +from nova.tests.api.openstack import fakes + + +def fake_keypair(name): + return {'public_key': 'FAKE_KEY', + 'fingerprint': 'FAKE_FINGERPRINT', + 'name': name} + + +def db_key_pair_get_all_by_user(self, user_id): + return [fake_keypair('FAKE')] + + +def db_key_pair_create(self, keypair): + pass + + +def db_key_pair_destroy(context, user_id, name): + if not (user_id and name): + raise Exception() + + +class KeypairsTest(test.TestCase): + + def setUp(self): + super(KeypairsTest, self).setUp() + self.controller = KeypairController() + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + self.stubs.Set(db, "key_pair_get_all_by_user", + db_key_pair_get_all_by_user) + self.stubs.Set(db, "key_pair_create", + db_key_pair_create) + self.stubs.Set(db, "key_pair_destroy", + db_key_pair_destroy) + self.context = context.get_admin_context() + + def test_keypair_list(self): + req = webob.Request.blank('/v1.1/123/os-keypairs') + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + res_dict = json.loads(res.body) + response = {'keypairs': [{'keypair': fake_keypair('FAKE')}]} + self.assertEqual(res_dict, response) + + def test_keypair_create(self): + body = {'keypair': {'name': 'create_test'}} + req = webob.Request.blank('/v1.1/123/os-keypairs') + req.method = 'POST' + req.body = json.dumps(body) + req.headers['Content-Type'] = 'application/json' + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + res_dict = json.loads(res.body) + self.assertTrue(len(res_dict['keypair']['fingerprint']) > 0) + self.assertTrue(len(res_dict['keypair']['private_key']) > 0) + + def test_keypair_import(self): + body = { + 'keypair': { + 'name': 'create_test', + 'public_key': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBYIznA' + 'x9D7118Q1VKGpXy2HDiKyUTM8XcUuhQpo0srqb9rboUp4' + 'a9NmCwpWpeElDLuva707GOUnfaBAvHBwsRXyxHJjRaI6Y' + 'Qj2oLJwqvaSaWUbyT1vtryRqy6J3TecN0WINY71f4uymi' + 'MZP0wby4bKBcYnac8KiCIlvkEl0ETjkOGUq8OyWRmn7lj' + 'j5SESEUdBP0JnuTFKddWTU/wD6wydeJaUhBTqOlHn0kX1' + 'GyqoNTE1UEhcM5ZRWgfUZfTjVyDF2kGj3vJLCJtJ8LoGc' + 'j7YaN4uPg1rBle+izwE/tLonRrds+cev8p6krSSrxWOwB' + 'bHkXa6OciiJDvkRzJXzf', + }, + } + + req = webob.Request.blank('/v1.1/123/os-keypairs') + req.method = 'POST' + req.body = json.dumps(body) + req.headers['Content-Type'] = 'application/json' + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + # FIXME(ja): sholud we check that public_key was sent to create? + res_dict = json.loads(res.body) + self.assertTrue(len(res_dict['keypair']['fingerprint']) > 0) + self.assertFalse('private_key' in res_dict['keypair']) + + def test_keypair_delete(self): + req = webob.Request.blank('/v1.1/123/os-keypairs/FAKE') + req.method = 'DELETE' + req.headers['Content-Type'] = 'application/json' + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 202) diff --git a/nova/tests/api/openstack/v2/contrib/test_multinic_xs.py b/nova/tests/api/openstack/v2/contrib/test_multinic_xs.py new file mode 100644 index 000000000..68f99d363 --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_multinic_xs.py @@ -0,0 +1,114 @@ +# 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. + +import json + +import stubout +import webob + +from nova import compute +from nova import context +from nova import test +from nova.tests.api.openstack import fakes + + +UUID = '70f6db34-de8d-4fbd-aafb-4065bdfa6114' +last_add_fixed_ip = (None, None) +last_remove_fixed_ip = (None, None) + + +def compute_api_add_fixed_ip(self, context, instance, network_id): + global last_add_fixed_ip + + last_add_fixed_ip = (instance['uuid'], network_id) + + +def compute_api_remove_fixed_ip(self, context, instance, address): + global last_remove_fixed_ip + + last_remove_fixed_ip = (instance['uuid'], address) + + +def compute_api_get(self, context, instance_id): + return {'id': 1, 'uuid': instance_id} + + +class FixedIpTest(test.TestCase): + def setUp(self): + super(FixedIpTest, self).setUp() + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + self.stubs.Set(compute.api.API, "add_fixed_ip", + compute_api_add_fixed_ip) + self.stubs.Set(compute.api.API, "remove_fixed_ip", + compute_api_remove_fixed_ip) + self.stubs.Set(compute.api.API, 'get', compute_api_get) + self.context = context.get_admin_context() + + def test_add_fixed_ip(self): + global last_add_fixed_ip + last_add_fixed_ip = (None, None) + + body = dict(addFixedIp=dict(networkId='test_net')) + req = webob.Request.blank('/v1.1/123/servers/%s/action' % UUID) + req.method = 'POST' + req.body = json.dumps(body) + req.headers['content-type'] = 'application/json' + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 202) + self.assertEqual(last_add_fixed_ip, (UUID, 'test_net')) + + def test_add_fixed_ip_no_network(self): + global last_add_fixed_ip + last_add_fixed_ip = (None, None) + + body = dict(addFixedIp=dict()) + req = webob.Request.blank('/v1.1/123/servers/%s/action' % UUID) + req.method = 'POST' + req.body = json.dumps(body) + req.headers['content-type'] = 'application/json' + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 422) + self.assertEqual(last_add_fixed_ip, (None, None)) + + def test_remove_fixed_ip(self): + global last_remove_fixed_ip + last_remove_fixed_ip = (None, None) + + body = dict(removeFixedIp=dict(address='10.10.10.1')) + req = webob.Request.blank('/v1.1/123/servers/%s/action' % UUID) + req.method = 'POST' + req.body = json.dumps(body) + req.headers['content-type'] = 'application/json' + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 202) + self.assertEqual(last_remove_fixed_ip, (UUID, '10.10.10.1')) + + def test_remove_fixed_ip_no_address(self): + global last_remove_fixed_ip + last_remove_fixed_ip = (None, None) + + body = dict(removeFixedIp=dict()) + req = webob.Request.blank('/v1.1/123/servers/%s/action' % UUID) + req.method = 'POST' + req.body = json.dumps(body) + req.headers['content-type'] = 'application/json' + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 422) + self.assertEqual(last_remove_fixed_ip, (None, None)) diff --git a/nova/tests/api/openstack/v2/contrib/test_quotas.py b/nova/tests/api/openstack/v2/contrib/test_quotas.py new file mode 100644 index 000000000..15fb20f94 --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_quotas.py @@ -0,0 +1,133 @@ +# 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. + +import json +import webob + +from nova.api.openstack.v2.contrib.quotas import QuotaSetsController +from nova import context +from nova import test +from nova.tests.api.openstack import fakes + + +def quota_set(id): + return {'quota_set': {'id': id, 'metadata_items': 128, 'volumes': 10, + 'gigabytes': 1000, 'ram': 51200, 'floating_ips': 10, + 'instances': 10, 'injected_files': 5, 'cores': 20, + 'injected_file_content_bytes': 10240}} + + +def quota_set_list(): + return {'quota_set_list': [quota_set('1234'), quota_set('5678'), + quota_set('update_me')]} + + +class QuotaSetsTest(test.TestCase): + + def setUp(self): + super(QuotaSetsTest, self).setUp() + self.controller = QuotaSetsController() + self.user_id = 'fake' + self.project_id = 'fake' + self.user_context = context.RequestContext(self.user_id, + self.project_id) + self.admin_context = context.RequestContext(self.user_id, + self.project_id, + is_admin=True) + + def test_format_quota_set(self): + raw_quota_set = { + 'instances': 10, + 'cores': 20, + 'ram': 51200, + 'volumes': 10, + 'floating_ips': 10, + 'metadata_items': 128, + 'gigabytes': 1000, + 'injected_files': 5, + 'injected_file_content_bytes': 10240} + + quota_set = QuotaSetsController()._format_quota_set('1234', + raw_quota_set) + qs = quota_set['quota_set'] + + self.assertEqual(qs['id'], '1234') + self.assertEqual(qs['instances'], 10) + self.assertEqual(qs['cores'], 20) + self.assertEqual(qs['ram'], 51200) + self.assertEqual(qs['volumes'], 10) + self.assertEqual(qs['gigabytes'], 1000) + self.assertEqual(qs['floating_ips'], 10) + self.assertEqual(qs['metadata_items'], 128) + self.assertEqual(qs['injected_files'], 5) + self.assertEqual(qs['injected_file_content_bytes'], 10240) + + def test_quotas_defaults(self): + uri = '/v1.1/fake_tenant/os-quota-sets/fake_tenant/defaults' + + req = fakes.HTTPRequest.blank(uri) + res_dict = self.controller.defaults(req, 'fake_tenant') + + expected = {'quota_set': { + 'id': 'fake_tenant', + 'instances': 10, + 'cores': 20, + 'ram': 51200, + 'volumes': 10, + 'gigabytes': 1000, + 'floating_ips': 10, + 'metadata_items': 128, + 'injected_files': 5, + 'injected_file_content_bytes': 10240}} + + self.assertEqual(res_dict, expected) + + def test_quotas_show_as_admin(self): + req = fakes.HTTPRequest.blank('/v1.1/1234/os-quota-sets/1234', + use_admin_context=True) + res_dict = self.controller.show(req, 1234) + + self.assertEqual(res_dict, quota_set('1234')) + + def test_quotas_show_as_unauthorized_user(self): + req = fakes.HTTPRequest.blank('/v1.1/1234/os-quota-sets/1234') + self.assertRaises(webob.exc.HTTPForbidden, self.controller.show, + req, 1234) + + def test_quotas_update_as_admin(self): + body = {'quota_set': {'instances': 50, 'cores': 50, + 'ram': 51200, 'volumes': 10, + 'gigabytes': 1000, 'floating_ips': 10, + 'metadata_items': 128, 'injected_files': 5, + 'injected_file_content_bytes': 10240}} + + req = fakes.HTTPRequest.blank('/v1.1/1234/os-quota-sets/update_me', + use_admin_context=True) + res_dict = self.controller.update(req, 'update_me', body) + + self.assertEqual(res_dict, body) + + def test_quotas_update_as_user(self): + body = {'quota_set': {'instances': 50, 'cores': 50, + 'ram': 51200, 'volumes': 10, + 'gigabytes': 1000, 'floating_ips': 10, + 'metadata_items': 128, 'injected_files': 5, + 'injected_file_content_bytes': 10240}} + + req = fakes.HTTPRequest.blank('/v1.1/1234/os-quota-sets/update_me') + self.assertRaises(webob.exc.HTTPForbidden, self.controller.update, + req, 'update_me', body) diff --git a/nova/tests/api/openstack/v2/contrib/test_rescue.py b/nova/tests/api/openstack/v2/contrib/test_rescue.py new file mode 100644 index 000000000..9058d101e --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_rescue.py @@ -0,0 +1,79 @@ +# Copyright 2011 OpenStack LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json + +import webob + +from nova import compute +from nova import flags +from nova import test +from nova.tests.api.openstack import fakes + +FLAGS = flags.FLAGS + + +def rescue(self, context, instance, rescue_password=None): + pass + + +def unrescue(self, context, instance): + pass + + +class RescueTest(test.TestCase): + def setUp(self): + super(RescueTest, self).setUp() + + def fake_compute_get(*args, **kwargs): + uuid = '70f6db34-de8d-4fbd-aafb-4065bdfa6114' + return {'id': 1, 'uuid': uuid} + + self.stubs.Set(compute.api.API, "get", fake_compute_get) + self.stubs.Set(compute.api.API, "rescue", rescue) + self.stubs.Set(compute.api.API, "unrescue", unrescue) + + def test_rescue_with_preset_password(self): + body = {"rescue": {"adminPass": "AABBCC112233"}} + req = webob.Request.blank('/v1.1/123/servers/test_inst/action') + req.method = "POST" + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 200) + resp_json = json.loads(resp.body) + self.assertEqual("AABBCC112233", resp_json['adminPass']) + + def test_rescue_generates_password(self): + body = dict(rescue=None) + req = webob.Request.blank('/v1.1/123/servers/test_inst/action') + req.method = "POST" + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 200) + resp_json = json.loads(resp.body) + self.assertEqual(FLAGS.password_length, len(resp_json['adminPass'])) + + def test_unrescue(self): + body = dict(unrescue=None) + req = webob.Request.blank('/v1.1/123/servers/test_inst/action') + req.method = "POST" + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 202) diff --git a/nova/tests/api/openstack/v2/contrib/test_security_groups.py b/nova/tests/api/openstack/v2/contrib/test_security_groups.py new file mode 100644 index 000000000..fd4e0841d --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_security_groups.py @@ -0,0 +1,849 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json +import unittest +from xml.dom import minidom + +import mox +import webob + +from nova.api.openstack.v2.contrib import security_groups +import nova.db +from nova import exception +from nova import test +from nova.tests.api.openstack import fakes + + +FAKE_UUID = 'a47ae74e-ab08-447f-8eee-ffd43fc46c16' + + +class AttrDict(dict): + def __getattr__(self, k): + return self[k] + + +def security_group_template(**kwargs): + sg = kwargs.copy() + sg.setdefault('tenant_id', '123') + sg.setdefault('name', 'test') + sg.setdefault('description', 'test-description') + return sg + + +def security_group_db(security_group, id=None): + attrs = security_group.copy() + if 'tenant_id' in attrs: + attrs['project_id'] = attrs.pop('tenant_id') + if id is not None: + attrs['id'] = id + attrs.setdefault('rules', []) + attrs.setdefault('instances', []) + return AttrDict(attrs) + + +def security_group_rule_template(**kwargs): + rule = kwargs.copy() + rule.setdefault('ip_protocol', 'tcp') + rule.setdefault('from_port', 22) + rule.setdefault('to_port', 22) + rule.setdefault('parent_group_id', 2) + return rule + + +def security_group_rule_db(rule, id=None): + attrs = rule.copy() + if 'ip_protocol' in attrs: + attrs['protocol'] = attrs.pop('ip_protocol') + return AttrDict(attrs) + + +def return_server(context, server_id): + return {'id': int(server_id), + 'power_state': 0x01, + 'host': "localhost", + 'uuid': FAKE_UUID, + 'name': 'asdf'} + + +def return_server_by_uuid(context, server_uuid): + return {'id': 1, + 'power_state': 0x01, + 'host': "localhost", + 'uuid': server_uuid, + 'name': 'asdf'} + + +def return_non_running_server(context, server_id): + return {'id': server_id, 'power_state': 0x02, + 'host': "localhost", 'name': 'asdf'} + + +def return_security_group_by_name(context, project_id, group_name): + return {'id': 1, 'name': group_name, "instances": [{'id': 1}]} + + +def return_security_group_without_instances(context, project_id, group_name): + return {'id': 1, 'name': group_name} + + +def return_server_nonexistent(context, server_id): + raise exception.InstanceNotFound(instance_id=server_id) + + +class StubExtensionManager(object): + def register(self, *args): + pass + + +class TestSecurityGroups(test.TestCase): + def setUp(self): + super(TestSecurityGroups, self).setUp() + + self.controller = security_groups.SecurityGroupController() + self.manager = security_groups.Security_groups(StubExtensionManager()) + + def tearDown(self): + super(TestSecurityGroups, self).tearDown() + + def test_create_security_group(self): + sg = security_group_template() + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') + res_dict = self.controller.create(req, {'security_group': sg}) + self.assertEqual(res_dict['security_group']['name'], 'test') + self.assertEqual(res_dict['security_group']['description'], + 'test-description') + + def test_create_security_group_with_no_name(self): + sg = security_group_template() + del sg['name'] + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') + self.assertRaises(webob.exc.HTTPUnprocessableEntity, + self.controller.create, req, sg) + + def test_create_security_group_with_no_description(self): + sg = security_group_template() + del sg['description'] + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group': sg}) + + def test_create_security_group_with_blank_name(self): + sg = security_group_template(name='') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group': sg}) + + def test_create_security_group_with_whitespace_name(self): + sg = security_group_template(name=' ') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group': sg}) + + def test_create_security_group_with_blank_description(self): + sg = security_group_template(description='') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group': sg}) + + def test_create_security_group_with_whitespace_description(self): + sg = security_group_template(description=' ') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group': sg}) + + def test_create_security_group_with_duplicate_name(self): + sg = security_group_template() + + # FIXME: Stub out _get instead of creating twice + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') + self.controller.create(req, {'security_group': sg}) + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group': sg}) + + def test_create_security_group_with_no_body(self): + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') + self.assertRaises(webob.exc.HTTPUnprocessableEntity, + self.controller.create, req, None) + + def test_create_security_group_with_no_security_group(self): + body = {'no-securityGroup': None} + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') + self.assertRaises(webob.exc.HTTPUnprocessableEntity, + self.controller.create, req, body) + + def test_create_security_group_above_255_characters_name(self): + sg = security_group_template(name='1234567890' * 26) + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group': sg}) + + def test_create_security_group_above_255_characters_description(self): + sg = security_group_template(description='1234567890' * 26) + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group': sg}) + + def test_create_security_group_non_string_name(self): + sg = security_group_template(name=12) + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group': sg}) + + def test_create_security_group_non_string_description(self): + sg = security_group_template(description=12) + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group': sg}) + + def test_get_security_group_list(self): + groups = [] + for i, name in enumerate(['default', 'test']): + sg = security_group_template(id=i + 1, + name=name, + description=name + '-desc', + rules=[]) + groups.append(sg) + expected = {'security_groups': groups} + + def return_security_groups(context, project_id): + return [security_group_db(sg) for sg in groups] + + self.stubs.Set(nova.db, 'security_group_get_by_project', + return_security_groups) + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups') + res_dict = self.controller.index(req) + + self.assertEquals(res_dict, expected) + + def test_get_security_group_by_id(self): + sg = security_group_template(id=2, rules=[]) + + def return_security_group(context, group_id): + self.assertEquals(sg['id'], group_id) + return security_group_db(sg) + + self.stubs.Set(nova.db, 'security_group_get', + return_security_group) + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups/2') + res_dict = self.controller.show(req, '2') + + expected = {'security_group': sg} + self.assertEquals(res_dict, expected) + + def test_get_security_group_by_invalid_id(self): + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups/invalid') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete, + req, 'invalid') + + def test_get_security_group_by_non_existing_id(self): + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups/111111111') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, + req, '111111111') + + def test_delete_security_group_by_id(self): + sg = security_group_template(id=1, rules=[]) + + self.called = False + + def security_group_destroy(context, id): + self.called = True + + def return_security_group(context, group_id): + self.assertEquals(sg['id'], group_id) + return security_group_db(sg) + + self.stubs.Set(nova.db, 'security_group_destroy', + security_group_destroy) + self.stubs.Set(nova.db, 'security_group_get', + return_security_group) + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups/1') + self.controller.delete(req, '1') + + self.assertTrue(self.called) + + def test_delete_security_group_by_invalid_id(self): + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups/invalid') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete, + req, 'invalid') + + def test_delete_security_group_by_non_existing_id(self): + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-groups/11111111') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, + req, '11111111') + + def test_associate_by_non_existing_security_group_name(self): + body = dict(addSecurityGroup=dict(name='non-existing')) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') + self.assertRaises(webob.exc.HTTPNotFound, + self.manager._addSecurityGroup, body, req, '1') + + def test_associate_by_invalid_server_id(self): + body = dict(addSecurityGroup=dict(name='test')) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/invalid/action') + self.assertRaises(webob.exc.HTTPNotFound, + self.manager._addSecurityGroup, body, req, 'invalid') + + def test_associate_without_body(self): + self.stubs.Set(nova.db, 'instance_get', return_server) + body = dict(addSecurityGroup=None) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') + self.assertRaises(webob.exc.HTTPBadRequest, + self.manager._addSecurityGroup, body, req, '1') + + def test_associate_no_security_group_name(self): + self.stubs.Set(nova.db, 'instance_get', return_server) + body = dict(addSecurityGroup=dict()) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') + self.assertRaises(webob.exc.HTTPBadRequest, + self.manager._addSecurityGroup, body, req, '1') + + def test_associate_security_group_name_with_whitespaces(self): + self.stubs.Set(nova.db, 'instance_get', return_server) + body = dict(addSecurityGroup=dict(name=" ")) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') + self.assertRaises(webob.exc.HTTPBadRequest, + self.manager._addSecurityGroup, body, req, '1') + + def test_associate_non_existing_instance(self): + self.stubs.Set(nova.db, 'instance_get', return_server_nonexistent) + self.stubs.Set(nova.db, 'instance_get_by_uuid', + return_server_nonexistent) + body = dict(addSecurityGroup=dict(name="test")) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') + self.assertRaises(webob.exc.HTTPNotFound, + self.manager._addSecurityGroup, body, req, '1') + + def test_associate_non_running_instance(self): + self.stubs.Set(nova.db, 'instance_get', return_non_running_server) + self.stubs.Set(nova.db, 'instance_get_by_uuid', + return_non_running_server) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group_without_instances) + body = dict(addSecurityGroup=dict(name="test")) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') + self.assertRaises(webob.exc.HTTPBadRequest, + self.manager._addSecurityGroup, body, req, '1') + + def test_associate_already_associated_security_group_to_instance(self): + self.stubs.Set(nova.db, 'instance_get', return_server) + self.stubs.Set(nova.db, 'instance_get_by_uuid', + return_server_by_uuid) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group_by_name) + body = dict(addSecurityGroup=dict(name="test")) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') + self.assertRaises(webob.exc.HTTPBadRequest, + self.manager._addSecurityGroup, body, req, '1') + + def test_associate(self): + self.stubs.Set(nova.db, 'instance_get', return_server) + self.stubs.Set(nova.db, 'instance_get_by_uuid', + return_server_by_uuid) + self.mox.StubOutWithMock(nova.db, 'instance_add_security_group') + nova.db.instance_add_security_group(mox.IgnoreArg(), + mox.IgnoreArg(), + mox.IgnoreArg()) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group_without_instances) + self.mox.ReplayAll() + + body = dict(addSecurityGroup=dict(name="test")) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') + self.manager._addSecurityGroup(body, req, '1') + + def test_disassociate_by_non_existing_security_group_name(self): + body = dict(removeSecurityGroup=dict(name='non-existing')) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') + self.assertRaises(webob.exc.HTTPNotFound, + self.manager._removeSecurityGroup, body, req, '1') + + def test_disassociate_by_invalid_server_id(self): + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group_by_name) + body = dict(removeSecurityGroup=dict(name='test')) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/invalid/action') + self.assertRaises(webob.exc.HTTPNotFound, + self.manager._removeSecurityGroup, body, req, + 'invalid') + + def test_disassociate_without_body(self): + self.stubs.Set(nova.db, 'instance_get', return_server) + body = dict(removeSecurityGroup=None) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') + self.assertRaises(webob.exc.HTTPBadRequest, + self.manager._removeSecurityGroup, body, req, '1') + + def test_disassociate_no_security_group_name(self): + self.stubs.Set(nova.db, 'instance_get', return_server) + body = dict(removeSecurityGroup=dict()) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') + self.assertRaises(webob.exc.HTTPBadRequest, + self.manager._removeSecurityGroup, body, req, '1') + + def test_disassociate_security_group_name_with_whitespaces(self): + self.stubs.Set(nova.db, 'instance_get', return_server) + body = dict(removeSecurityGroup=dict(name=" ")) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') + self.assertRaises(webob.exc.HTTPBadRequest, + self.manager._removeSecurityGroup, body, req, '1') + + def test_disassociate_non_existing_instance(self): + self.stubs.Set(nova.db, 'instance_get', return_server_nonexistent) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group_by_name) + body = dict(removeSecurityGroup=dict(name="test")) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') + self.assertRaises(webob.exc.HTTPNotFound, + self.manager._removeSecurityGroup, body, req, '1') + + def test_disassociate_non_running_instance(self): + self.stubs.Set(nova.db, 'instance_get', return_non_running_server) + self.stubs.Set(nova.db, 'instance_get_by_uuid', + return_non_running_server) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group_by_name) + body = dict(removeSecurityGroup=dict(name="test")) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') + self.assertRaises(webob.exc.HTTPBadRequest, + self.manager._removeSecurityGroup, body, req, '1') + + def test_disassociate_already_associated_security_group_to_instance(self): + self.stubs.Set(nova.db, 'instance_get', return_server) + self.stubs.Set(nova.db, 'instance_get_by_uuid', + return_server_by_uuid) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group_without_instances) + body = dict(removeSecurityGroup=dict(name="test")) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') + self.assertRaises(webob.exc.HTTPBadRequest, + self.manager._removeSecurityGroup, body, req, '1') + + def test_disassociate(self): + self.stubs.Set(nova.db, 'instance_get', return_server) + self.stubs.Set(nova.db, 'instance_get_by_uuid', + return_server_by_uuid) + self.mox.StubOutWithMock(nova.db, 'instance_remove_security_group') + nova.db.instance_remove_security_group(mox.IgnoreArg(), + mox.IgnoreArg(), + mox.IgnoreArg()) + self.stubs.Set(nova.db, 'security_group_get_by_name', + return_security_group_by_name) + self.mox.ReplayAll() + + body = dict(removeSecurityGroup=dict(name="test")) + + req = fakes.HTTPRequest.blank('/v1.1/123/servers/1/action') + self.manager._removeSecurityGroup(body, req, '1') + + +class TestSecurityGroupRules(test.TestCase): + def setUp(self): + super(TestSecurityGroupRules, self).setUp() + + controller = security_groups.SecurityGroupController() + + sg1 = security_group_template(id=1) + sg2 = security_group_template(id=2, + name='authorize_revoke', + description='authorize-revoke testing') + db1 = security_group_db(sg1) + db2 = security_group_db(sg2) + + def return_security_group(context, group_id): + if group_id == db1['id']: + return db1 + if group_id == db2['id']: + return db2 + raise exception.NotFound() + + self.stubs.Set(nova.db, 'security_group_get', + return_security_group) + + self.parent_security_group = db2 + + self.controller = security_groups.SecurityGroupRulesController() + + def tearDown(self): + super(TestSecurityGroupRules, self).tearDown() + + def test_create_by_cidr(self): + rule = security_group_rule_template(cidr='10.2.3.124/24') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + res_dict = self.controller.create(req, {'security_group_rule': rule}) + + security_group_rule = res_dict['security_group_rule'] + self.assertNotEquals(security_group_rule['id'], 0) + self.assertEquals(security_group_rule['parent_group_id'], 2) + self.assertEquals(security_group_rule['ip_range']['cidr'], + "10.2.3.124/24") + + def test_create_by_group_id(self): + rule = security_group_rule_template(group_id='1') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + res_dict = self.controller.create(req, {'security_group_rule': rule}) + + security_group_rule = res_dict['security_group_rule'] + self.assertNotEquals(security_group_rule['id'], 0) + self.assertEquals(security_group_rule['parent_group_id'], 2) + + def test_create_by_invalid_cidr_json(self): + rules = { + "security_group_rule": { + "ip_protocol": "tcp", + "from_port": "22", + "to_port": "22", + "parent_group_id": 2, + "cidr": "10.2.3.124/2433"}} + rule = security_group_rule_template( + ip_protocol="tcp", + from_port=22, + to_port=22, + parent_group_id=2, + cidr="10.2.3.124/2433") + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_by_invalid_tcp_port_json(self): + rule = security_group_rule_template( + ip_protocol="tcp", + from_port=75534, + to_port=22, + parent_group_id=2, + cidr="10.2.3.124/24") + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_by_invalid_icmp_port_json(self): + rule = security_group_rule_template( + ip_protocol="icmp", + from_port=1, + to_port=256, + parent_group_id=2, + cidr="10.2.3.124/24") + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_add_existing_rules(self): + rule = security_group_rule_template(cidr='10.0.0.0/24') + + self.parent_security_group['rules'] = [security_group_rule_db(rule)] + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_with_no_body(self): + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPUnprocessableEntity, + self.controller.create, req, None) + + def test_create_with_no_security_group_rule_in_body(self): + rules = {'test': 'test'} + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPUnprocessableEntity, + self.controller.create, req, rules) + + def test_create_with_invalid_parent_group_id(self): + rule = security_group_rule_template(parent_group_id='invalid') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_with_non_existing_parent_group_id(self): + rule = security_group_rule_template(group_id='invalid', + parent_group_id='1111111111111') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_with_invalid_protocol(self): + rule = security_group_rule_template(ip_protocol='invalid-protocol', + cidr='10.2.2.0/24') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_with_no_protocol(self): + rule = security_group_rule_template(cidr='10.2.2.0/24') + del rule['ip_protocol'] + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_with_invalid_from_port(self): + rule = security_group_rule_template(from_port='666666', + cidr='10.2.2.0/24') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_with_invalid_to_port(self): + rule = security_group_rule_template(to_port='666666', + cidr='10.2.2.0/24') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_with_non_numerical_from_port(self): + rule = security_group_rule_template(from_port='invalid', + cidr='10.2.2.0/24') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_with_non_numerical_to_port(self): + rule = security_group_rule_template(to_port='invalid', + cidr='10.2.2.0/24') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_with_no_from_port(self): + rule = security_group_rule_template(cidr='10.2.2.0/24') + del rule['from_port'] + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_with_no_to_port(self): + rule = security_group_rule_template(cidr='10.2.2.0/24') + del rule['to_port'] + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_with_invalid_cidr(self): + rule = security_group_rule_template(cidr='10.2.2222.0/24') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_with_no_cidr_group(self): + rule = security_group_rule_template() + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + res_dict = self.controller.create(req, {'security_group_rule': rule}) + + security_group_rule = res_dict['security_group_rule'] + self.assertNotEquals(security_group_rule['id'], 0) + self.assertEquals(security_group_rule['parent_group_id'], + self.parent_security_group['id']) + self.assertEquals(security_group_rule['ip_range']['cidr'], + "0.0.0.0/0") + + def test_create_with_invalid_group_id(self): + rule = security_group_rule_template(group_id='invalid') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_with_empty_group_id(self): + rule = security_group_rule_template(group_id='') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_with_nonexist_group_id(self): + rule = security_group_rule_template(group_id='222222') + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_create_rule_with_same_group_parent_id(self): + rule = security_group_rule_template(group_id=2) + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, {'security_group_rule': rule}) + + def test_delete(self): + rule = security_group_rule_template(id=10) + + def security_group_rule_get(context, id): + return security_group_rule_db(rule) + + def security_group_rule_destroy(context, id): + pass + + self.stubs.Set(nova.db, 'security_group_rule_get', + security_group_rule_get) + self.stubs.Set(nova.db, 'security_group_rule_destroy', + security_group_rule_destroy) + + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules/10') + self.controller.delete(req, '10') + + def test_delete_invalid_rule_id(self): + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules' + + '/invalid') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete, + req, 'invalid') + + def test_delete_non_existing_rule_id(self): + req = fakes.HTTPRequest.blank('/v1.1/123/os-security-group-rules' + + '/22222222222222') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, + req, '22222222222222') + + +class TestSecurityGroupRulesXMLDeserializer(unittest.TestCase): + + def setUp(self): + self.deserializer = security_groups.SecurityGroupRulesXMLDeserializer() + + def test_create_request(self): + serial_request = """ + + 12 + 22 + 22 + + tcp + 10.0.0.0/24 +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "security_group_rule": { + "parent_group_id": "12", + "from_port": "22", + "to_port": "22", + "ip_protocol": "tcp", + "group_id": "", + "cidr": "10.0.0.0/24", + }, + } + self.assertEquals(request['body'], expected) + + def test_create_no_protocol_request(self): + serial_request = """ + + 12 + 22 + 22 + + 10.0.0.0/24 +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "security_group_rule": { + "parent_group_id": "12", + "from_port": "22", + "to_port": "22", + "group_id": "", + "cidr": "10.0.0.0/24", + }, + } + self.assertEquals(request['body'], expected) + + +class TestSecurityGroupXMLDeserializer(unittest.TestCase): + + def setUp(self): + self.deserializer = security_groups.SecurityGroupXMLDeserializer() + + def test_create_request(self): + serial_request = """ + + test +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "security_group": { + "name": "test", + "description": "test", + }, + } + self.assertEquals(request['body'], expected) + + def test_create_no_description_request(self): + serial_request = """ + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "security_group": { + "name": "test", + }, + } + self.assertEquals(request['body'], expected) + + def test_create_no_name_request(self): + serial_request = """ + +test +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "security_group": { + "description": "test", + }, + } + self.assertEquals(request['body'], expected) diff --git a/nova/tests/api/openstack/v2/contrib/test_simple_tenant_usage.py b/nova/tests/api/openstack/v2/contrib/test_simple_tenant_usage.py new file mode 100644 index 000000000..56e7be0d2 --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_simple_tenant_usage.py @@ -0,0 +1,172 @@ +# 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. + +import datetime +import json +import webob + +from nova.compute import api +from nova import context +from nova import flags +from nova import test +from nova.tests.api.openstack import fakes + + +FLAGS = flags.FLAGS + +SERVERS = 5 +TENANTS = 2 +HOURS = 24 +LOCAL_GB = 10 +MEMORY_MB = 1024 +VCPUS = 2 +STOP = datetime.datetime.utcnow() +START = STOP - datetime.timedelta(hours=HOURS) + + +def fake_instance_type_get(self, context, instance_type_id): + return {'id': 1, + 'vcpus': VCPUS, + 'local_gb': LOCAL_GB, + 'memory_mb': MEMORY_MB, + 'name': + 'fakeflavor'} + + +def get_fake_db_instance(start, end, instance_id, tenant_id): + return {'id': instance_id, + 'image_ref': '1', + 'project_id': tenant_id, + 'user_id': 'fakeuser', + 'display_name': 'name', + 'state_description': 'state', + 'instance_type_id': 1, + 'launched_at': start, + 'terminated_at': end} + + +def fake_instance_get_active_by_window(self, context, begin, end, project_id): + return [get_fake_db_instance(START, + STOP, + x, + "faketenant_%s" % (x / SERVERS)) + for x in xrange(TENANTS * SERVERS)] + + +class SimpleTenantUsageTest(test.TestCase): + def setUp(self): + super(SimpleTenantUsageTest, self).setUp() + self.stubs.Set(api.API, "get_instance_type", + fake_instance_type_get) + self.stubs.Set(api.API, "get_active_by_window", + fake_instance_get_active_by_window) + self.admin_context = context.RequestContext('fakeadmin_0', + 'faketenant_0', + is_admin=True) + self.user_context = context.RequestContext('fakeadmin_0', + 'faketenant_0', + is_admin=False) + self.alt_user_context = context.RequestContext('fakeadmin_0', + 'faketenant_1', + is_admin=False) + FLAGS.allow_admin_api = True + + def test_verify_index(self): + req = webob.Request.blank( + '/v1.1/123/os-simple-tenant-usage?start=%s&end=%s' % + (START.isoformat(), STOP.isoformat())) + req.method = "GET" + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app( + fake_auth_context=self.admin_context)) + + self.assertEqual(res.status_int, 200) + res_dict = json.loads(res.body) + usages = res_dict['tenant_usages'] + from nova import log as logging + logging.warn(usages) + for i in xrange(TENANTS): + self.assertEqual(int(usages[i]['total_hours']), + SERVERS * HOURS) + self.assertEqual(int(usages[i]['total_local_gb_usage']), + SERVERS * LOCAL_GB * HOURS) + self.assertEqual(int(usages[i]['total_memory_mb_usage']), + SERVERS * MEMORY_MB * HOURS) + self.assertEqual(int(usages[i]['total_vcpus_usage']), + SERVERS * VCPUS * HOURS) + self.assertFalse(usages[i].get('server_usages')) + + def test_verify_detailed_index(self): + req = webob.Request.blank( + '/v1.1/123/os-simple-tenant-usage?' + 'detailed=1&start=%s&end=%s' % + (START.isoformat(), STOP.isoformat())) + req.method = "GET" + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app( + fake_auth_context=self.admin_context)) + self.assertEqual(res.status_int, 200) + res_dict = json.loads(res.body) + usages = res_dict['tenant_usages'] + for i in xrange(TENANTS): + servers = usages[i]['server_usages'] + for j in xrange(SERVERS): + self.assertEqual(int(servers[j]['hours']), HOURS) + + def test_verify_index_fails_for_nonadmin(self): + req = webob.Request.blank( + '/v1.1/123/os-simple-tenant-usage?' + 'detailed=1&start=%s&end=%s' % + (START.isoformat(), STOP.isoformat())) + req.method = "GET" + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 403) + + def test_verify_show(self): + req = webob.Request.blank( + '/v1.1/faketenant_0/os-simple-tenant-usage/' + 'faketenant_0?start=%s&end=%s' % + (START.isoformat(), STOP.isoformat())) + req.method = "GET" + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app( + fake_auth_context=self.user_context)) + self.assertEqual(res.status_int, 200) + res_dict = json.loads(res.body) + + usage = res_dict['tenant_usage'] + servers = usage['server_usages'] + self.assertEqual(len(usage['server_usages']), SERVERS) + for j in xrange(SERVERS): + self.assertEqual(int(servers[j]['hours']), HOURS) + + def test_verify_show_cant_view_other_tenant(self): + req = webob.Request.blank( + '/v1.1/faketenant_1/os-simple-tenant-usage/' + 'faketenant_0?start=%s&end=%s' % + (START.isoformat(), STOP.isoformat())) + req.method = "GET" + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app( + fake_auth_context=self.alt_user_context)) + self.assertEqual(res.status_int, 403) diff --git a/nova/tests/api/openstack/v2/contrib/test_virtual_interfaces.py b/nova/tests/api/openstack/v2/contrib/test_virtual_interfaces.py new file mode 100644 index 000000000..4285eaaaf --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_virtual_interfaces.py @@ -0,0 +1,56 @@ +# Copyright (C) 2011 Midokura KK +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json + +import webob + +from nova.api.openstack.v2.contrib.virtual_interfaces import \ + ServerVirtualInterfaceController +from nova import network +from nova import test +from nova.tests.api.openstack import fakes + + +def get_vifs_by_instance(self, context, server_id): + return [{'uuid': '00000000-0000-0000-0000-00000000000000000', + 'address': '00-00-00-00-00-00'}, + {'uuid': '11111111-1111-1111-1111-11111111111111111', + 'address': '11-11-11-11-11-11'}] + + +class ServerVirtualInterfaceTest(test.TestCase): + + def setUp(self): + super(ServerVirtualInterfaceTest, self).setUp() + self.controller = ServerVirtualInterfaceController() + self.stubs.Set(network.api.API, "get_vifs_by_instance", + get_vifs_by_instance) + + def tearDown(self): + super(ServerVirtualInterfaceTest, self).tearDown() + + def test_get_virtual_interfaces_list(self): + url = '/v1.1/123/servers/abcd/os-virtual-interfaces' + req = webob.Request.blank(url) + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + res_dict = json.loads(res.body) + response = {'virtual_interfaces': [ + {'id': '00000000-0000-0000-0000-00000000000000000', + 'mac_address': '00-00-00-00-00-00'}, + {'id': '11111111-1111-1111-1111-11111111111111111', + 'mac_address': '11-11-11-11-11-11'}]} + self.assertEqual(res_dict, response) diff --git a/nova/tests/api/openstack/v2/contrib/test_volume_types.py b/nova/tests/api/openstack/v2/contrib/test_volume_types.py new file mode 100644 index 000000000..9da77349d --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_volume_types.py @@ -0,0 +1,167 @@ +# 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. + +import json + +import stubout +import webob + +from nova.api.openstack.v2.contrib import volumetypes +from nova import exception +from nova import context +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.v2.contrib.' + '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 = volumetypes.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('/v1.1/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('/v1.1/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('/v1.1/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('/v1.1/123/os-volume-types/777') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, + req, '777') + + def test_volume_types_delete(self): + self.stubs.Set(volume_types, 'get_volume_type', + return_volume_types_get_volume_type) + self.stubs.Set(volume_types, 'destroy', + return_volume_types_destroy) + + req = fakes.HTTPRequest.blank('/v1.1/123/os-volume-types/1') + self.controller.delete(req, 1) + + def test_volume_types_delete_not_found(self): + self.stubs.Set(volume_types, 'get_volume_type', + return_volume_types_get_volume_type) + self.stubs.Set(volume_types, 'destroy', + return_volume_types_destroy) + + req = fakes.HTTPRequest.blank('/v1.1/123/os-volume-types/777') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, + req, '777') + + def test_create(self): + self.stubs.Set(volume_types, 'create', + return_volume_types_create) + self.stubs.Set(volume_types, 'get_volume_type_by_name', + return_volume_types_get_by_name) + + body = {"volume_type": {"name": "vol_type_1", + "extra_specs": {"key1": "value1"}}} + req = fakes.HTTPRequest.blank('/v1.1/123/os-volume-types') + res_dict = self.controller.create(req, body) + + self.assertEqual(1, len(res_dict)) + self.assertEqual('vol_type_1', res_dict['volume_type']['name']) + + def test_create_empty_body(self): + self.stubs.Set(volume_types, 'create', + return_volume_types_create) + self.stubs.Set(volume_types, 'get_volume_type_by_name', + return_volume_types_get_by_name) + + req = fakes.HTTPRequest.blank('/v1.1/123/os-volume-types') + self.assertRaises(webob.exc.HTTPUnprocessableEntity, + self.controller.create, req, '') diff --git a/nova/tests/api/openstack/v2/contrib/test_volume_types_extra_specs.py b/nova/tests/api/openstack/v2/contrib/test_volume_types_extra_specs.py new file mode 100644 index 000000000..9083d9d72 --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_volume_types_extra_specs.py @@ -0,0 +1,168 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Zadara Storage Inc. +# Copyright (c) 2011 OpenStack LLC. +# Copyright 2011 University of Southern California +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json +import os.path + +import stubout +import webob + +from nova.api.openstack.v2 import extensions +from nova.api.openstack.v2.contrib import volumetypes +from nova import test +from nova.tests.api.openstack import fakes +import nova.wsgi + + +def return_create_volume_type_extra_specs(context, volume_type_id, + extra_specs): + return stub_volume_type_extra_specs() + + +def return_volume_type_extra_specs(context, volume_type_id): + return stub_volume_type_extra_specs() + + +def return_empty_volume_type_extra_specs(context, volume_type_id): + return {} + + +def delete_volume_type_extra_specs(context, volume_type_id, key): + pass + + +def stub_volume_type_extra_specs(): + specs = { + "key1": "value1", + "key2": "value2", + "key3": "value3", + "key4": "value4", + "key5": "value5"} + return specs + + +class VolumeTypesExtraSpecsTest(test.TestCase): + + def setUp(self): + super(VolumeTypesExtraSpecsTest, self).setUp() + fakes.stub_out_key_pair_funcs(self.stubs) + self.api_path = '/v1.1/123/os-volume-types/1/extra_specs' + self.controller = volumetypes.VolumeTypeExtraSpecsController() + + def test_index(self): + self.stubs.Set(nova.db, 'volume_type_extra_specs_get', + return_volume_type_extra_specs) + + req = fakes.HTTPRequest.blank(self.api_path) + res_dict = self.controller.index(req, 1) + + self.assertEqual('value1', res_dict['extra_specs']['key1']) + + def test_index_no_data(self): + self.stubs.Set(nova.db, 'volume_type_extra_specs_get', + return_empty_volume_type_extra_specs) + + req = fakes.HTTPRequest.blank(self.api_path) + res_dict = self.controller.index(req, 1) + + self.assertEqual(0, len(res_dict['extra_specs'])) + + def test_show(self): + self.stubs.Set(nova.db, 'volume_type_extra_specs_get', + return_volume_type_extra_specs) + + req = fakes.HTTPRequest.blank(self.api_path + '/key5') + res_dict = self.controller.show(req, 1, 'key5') + + self.assertEqual('value5', res_dict['key5']) + + def test_show_spec_not_found(self): + self.stubs.Set(nova.db, 'volume_type_extra_specs_get', + return_empty_volume_type_extra_specs) + + req = fakes.HTTPRequest.blank(self.api_path + '/key6') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, + req, 1, 'key6') + + def test_delete(self): + self.stubs.Set(nova.db, 'volume_type_extra_specs_delete', + delete_volume_type_extra_specs) + + req = fakes.HTTPRequest.blank(self.api_path + '/key5') + self.controller.delete(req, 1, 'key5') + + def test_create(self): + self.stubs.Set(nova.db, + 'volume_type_extra_specs_update_or_create', + return_create_volume_type_extra_specs) + body = {"extra_specs": {"key1": "value1"}} + + req = fakes.HTTPRequest.blank(self.api_path) + res_dict = self.controller.create(req, 1, body) + + self.assertEqual('value1', res_dict['extra_specs']['key1']) + + def test_create_empty_body(self): + self.stubs.Set(nova.db, + 'volume_type_extra_specs_update_or_create', + return_create_volume_type_extra_specs) + + req = fakes.HTTPRequest.blank(self.api_path) + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + req, 1, '') + + def test_update_item(self): + self.stubs.Set(nova.db, + 'volume_type_extra_specs_update_or_create', + return_create_volume_type_extra_specs) + body = {"key1": "value1"} + + req = fakes.HTTPRequest.blank(self.api_path + '/key1') + res_dict = self.controller.update(req, 1, 'key1', body) + + self.assertEqual('value1', res_dict['key1']) + + def test_update_item_empty_body(self): + self.stubs.Set(nova.db, + 'volume_type_extra_specs_update_or_create', + return_create_volume_type_extra_specs) + + req = fakes.HTTPRequest.blank(self.api_path + '/key1') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + req, 1, 'key1', '') + + def test_update_item_too_many_keys(self): + self.stubs.Set(nova.db, + 'volume_type_extra_specs_update_or_create', + return_create_volume_type_extra_specs) + body = {"key1": "value1", "key2": "value2"} + + req = fakes.HTTPRequest.blank(self.api_path + '/key1') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + req, 1, 'key1', body) + + def test_update_item_body_uri_mismatch(self): + self.stubs.Set(nova.db, + 'volume_type_extra_specs_update_or_create', + return_create_volume_type_extra_specs) + body = {"key1": "value1"} + + req = fakes.HTTPRequest.blank(self.api_path + '/bad') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + req, 1, 'bad', body) diff --git a/nova/tests/api/openstack/v2/contrib/test_volumes.py b/nova/tests/api/openstack/v2/contrib/test_volumes.py new file mode 100644 index 000000000..529abf225 --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_volumes.py @@ -0,0 +1,89 @@ +# 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 + +import webob + +import nova +from nova.compute import instance_types +from nova import flags +from nova import test +from nova.tests.api.openstack import fakes + + +FLAGS = flags.FLAGS + + +FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' +IMAGE_UUID = 'c905cedb-7281-47e4-8a62-f26bc5fc4c77' + + +def fake_compute_api_create(cls, context, instance_type, image_href, **kwargs): + global _block_device_mapping_seen + _block_device_mapping_seen = kwargs.get('block_device_mapping') + + inst_type = instance_types.get_instance_type_by_flavor_id(2) + resv_id = None + return ([{'id': 1, + 'display_name': 'test_server', + 'uuid': FAKE_UUID, + 'instance_type': dict(inst_type), + 'access_ip_v4': '1.2.3.4', + 'access_ip_v6': 'fead::1234', + 'image_ref': IMAGE_UUID, + 'user_id': 'fake', + 'project_id': 'fake', + 'created_at': datetime.datetime(2010, 10, 10, 12, 0, 0), + 'updated_at': datetime.datetime(2010, 11, 11, 11, 0, 0), + 'progress': 0, + 'fixed_ips': [] + }], resv_id) + + +class BootFromVolumeTest(test.TestCase): + + def setUp(self): + super(BootFromVolumeTest, self).setUp() + self.stubs.Set(nova.compute.API, 'create', fake_compute_api_create) + fakes.stub_out_nw_api(self.stubs) + + def test_create_root_volume(self): + body = dict(server=dict( + name='test_server', imageRef=IMAGE_UUID, + flavorRef=2, min_count=1, max_count=1, + block_device_mapping=[dict( + volume_id=1, + device_name='/dev/vda', + virtual='root', + delete_on_termination=False, + )] + )) + global _block_device_mapping_seen + _block_device_mapping_seen = None + req = webob.Request.blank('/v1.1/fake/os-volumes_boot') + req.method = 'POST' + req.body = json.dumps(body) + req.headers['content-type'] = 'application/json' + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + server = json.loads(res.body)['server'] + self.assertEqual(FAKE_UUID, server['id']) + self.assertEqual(FLAGS.password_length, len(server['adminPass'])) + self.assertEqual(len(_block_device_mapping_seen), 1) + self.assertEqual(_block_device_mapping_seen[0]['volume_id'], 1) + self.assertEqual(_block_device_mapping_seen[0]['device_name'], + '/dev/vda') diff --git a/nova/tests/api/openstack/v2/contrib/test_vsa.py b/nova/tests/api/openstack/v2/contrib/test_vsa.py new file mode 100644 index 000000000..b4055a204 --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_vsa.py @@ -0,0 +1,449 @@ +# 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. + +import json +import unittest + +import stubout +import webob + +from nova.api.openstack.v2.contrib.virtual_storage_arrays import _vsa_view +from nova import context +import nova.db +from nova import exception +from nova import flags +from nova import log as logging +from nova import test +from nova.tests.api.openstack import fakes +from nova import volume +from nova import vsa + + +FLAGS = flags.FLAGS + +LOG = logging.getLogger('nova.tests.api.openstack.v2.contrib.test_vsa') + +last_param = {} + + +def _get_default_vsa_param(): + return { + 'display_name': 'Test_VSA_name', + 'display_description': 'Test_VSA_description', + 'vc_count': 1, + 'instance_type': 'm1.small', + 'instance_type_id': 5, + 'image_name': None, + 'availability_zone': None, + 'storage': [], + 'shared': False + } + + +def stub_vsa_create(self, context, **param): + global last_param + LOG.debug(_("_create: param=%s"), param) + param['id'] = 123 + param['name'] = 'Test name' + param['instance_type_id'] = 5 + last_param = param + return param + + +def stub_vsa_delete(self, context, vsa_id): + global last_param + last_param = dict(vsa_id=vsa_id) + + LOG.debug(_("_delete: %s"), locals()) + if vsa_id != '123': + raise exception.NotFound + + +def stub_vsa_get(self, context, vsa_id): + global last_param + last_param = dict(vsa_id=vsa_id) + + LOG.debug(_("_get: %s"), locals()) + if vsa_id != '123': + raise exception.NotFound + + param = _get_default_vsa_param() + param['id'] = vsa_id + return param + + +def stub_vsa_get_all(self, context): + LOG.debug(_("_get_all: %s"), locals()) + param = _get_default_vsa_param() + param['id'] = 123 + return [param] + + +class VSAApiTest(test.TestCase): + def setUp(self): + super(VSAApiTest, 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(vsa.api.API, "create", stub_vsa_create) + self.stubs.Set(vsa.api.API, "delete", stub_vsa_delete) + self.stubs.Set(vsa.api.API, "get", stub_vsa_get) + self.stubs.Set(vsa.api.API, "get_all", stub_vsa_get_all) + + self.context = context.get_admin_context() + + def tearDown(self): + self.stubs.UnsetAll() + super(VSAApiTest, self).tearDown() + + def test_vsa_create(self): + global last_param + last_param = {} + + vsa = {"displayName": "VSA Test Name", + "displayDescription": "VSA Test Desc"} + body = dict(vsa=vsa) + req = webob.Request.blank('/v1.1/777/zadr-vsa') + req.method = 'POST' + req.body = json.dumps(body) + req.headers['content-type'] = 'application/json' + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 200) + + # Compare if parameters were correctly passed to stub + self.assertEqual(last_param['display_name'], "VSA Test Name") + self.assertEqual(last_param['display_description'], "VSA Test Desc") + + resp_dict = json.loads(resp.body) + self.assertTrue('vsa' in resp_dict) + self.assertEqual(resp_dict['vsa']['displayName'], vsa['displayName']) + self.assertEqual(resp_dict['vsa']['displayDescription'], + vsa['displayDescription']) + + def test_vsa_create_no_body(self): + req = webob.Request.blank('/v1.1/777/zadr-vsa') + req.method = 'POST' + req.body = json.dumps({}) + req.headers['content-type'] = 'application/json' + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 422) + + def test_vsa_delete(self): + global last_param + last_param = {} + + vsa_id = 123 + req = webob.Request.blank('/v1.1/777/zadr-vsa/%d' % vsa_id) + req.method = 'DELETE' + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 200) + self.assertEqual(str(last_param['vsa_id']), str(vsa_id)) + + def test_vsa_delete_invalid_id(self): + global last_param + last_param = {} + + vsa_id = 234 + req = webob.Request.blank('/v1.1/777/zadr-vsa/%d' % vsa_id) + req.method = 'DELETE' + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 404) + self.assertEqual(str(last_param['vsa_id']), str(vsa_id)) + + def test_vsa_show(self): + global last_param + last_param = {} + + vsa_id = 123 + req = webob.Request.blank('/v1.1/777/zadr-vsa/%d' % vsa_id) + req.method = 'GET' + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 200) + self.assertEqual(str(last_param['vsa_id']), str(vsa_id)) + + resp_dict = json.loads(resp.body) + self.assertTrue('vsa' in resp_dict) + self.assertEqual(resp_dict['vsa']['id'], str(vsa_id)) + + def test_vsa_show_invalid_id(self): + global last_param + last_param = {} + + vsa_id = 234 + req = webob.Request.blank('/v1.1/777/zadr-vsa/%d' % vsa_id) + req.method = 'GET' + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 404) + self.assertEqual(str(last_param['vsa_id']), str(vsa_id)) + + def test_vsa_index(self): + req = webob.Request.blank('/v1.1/777/zadr-vsa') + req.method = 'GET' + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 200) + + resp_dict = json.loads(resp.body) + + self.assertTrue('vsaSet' in resp_dict) + resp_vsas = resp_dict['vsaSet'] + self.assertEqual(len(resp_vsas), 1) + + resp_vsa = resp_vsas.pop() + self.assertEqual(resp_vsa['id'], 123) + + def test_vsa_detail(self): + req = webob.Request.blank('/v1.1/777/zadr-vsa/detail') + req.method = 'GET' + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 200) + + resp_dict = json.loads(resp.body) + + self.assertTrue('vsaSet' in resp_dict) + resp_vsas = resp_dict['vsaSet'] + self.assertEqual(len(resp_vsas), 1) + + resp_vsa = resp_vsas.pop() + self.assertEqual(resp_vsa['id'], 123) + + +def _get_default_volume_param(): + return { + 'id': 123, + 'status': 'available', + 'size': 100, + 'availability_zone': 'nova', + 'created_at': None, + 'attach_status': 'detached', + 'name': 'vol name', + 'display_name': 'Default vol name', + 'display_description': 'Default vol description', + 'volume_type_id': 1, + 'volume_metadata': [], + } + + +def stub_get_vsa_volume_type(self, context): + return {'id': 1, + 'name': 'VSA volume type', + 'extra_specs': {'type': 'vsa_volume'}} + + +def stub_volume_create(self, context, size, snapshot_id, name, description, + **param): + LOG.debug(_("_create: param=%s"), size) + vol = _get_default_volume_param() + vol['size'] = size + vol['display_name'] = name + vol['display_description'] = description + return vol + + +def stub_volume_update(self, context, **param): + LOG.debug(_("_volume_update: param=%s"), param) + pass + + +def stub_volume_delete(self, context, **param): + LOG.debug(_("_volume_delete: param=%s"), param) + pass + + +def stub_volume_get(self, context, volume_id): + LOG.debug(_("_volume_get: volume_id=%s"), volume_id) + vol = _get_default_volume_param() + vol['id'] = volume_id + meta = {'key': 'from_vsa_id', 'value': '123'} + if volume_id == '345': + meta = {'key': 'to_vsa_id', 'value': '123'} + vol['volume_metadata'].append(meta) + return vol + + +def stub_volume_get_notfound(self, context, volume_id): + raise exception.NotFound + + +def stub_volume_get_all(self, context, search_opts): + vol = stub_volume_get(self, context, '123') + vol['metadata'] = search_opts['metadata'] + return [vol] + + +def return_vsa(context, vsa_id): + return {'id': vsa_id} + + +class VSAVolumeApiTest(test.TestCase): + + def setUp(self, test_obj=None, test_objs=None): + super(VSAVolumeApiTest, 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(nova.db, 'vsa_get', return_vsa) + self.stubs.Set(vsa.api.API, "get_vsa_volume_type", + stub_get_vsa_volume_type) + + self.stubs.Set(volume.api.API, "update", stub_volume_update) + self.stubs.Set(volume.api.API, "delete", stub_volume_delete) + self.stubs.Set(volume.api.API, "get", stub_volume_get) + self.stubs.Set(volume.api.API, "get_all", stub_volume_get_all) + + self.context = context.get_admin_context() + self.test_obj = test_obj if test_obj else "volume" + self.test_objs = test_objs if test_objs else "volumes" + + def tearDown(self): + self.stubs.UnsetAll() + super(VSAVolumeApiTest, self).tearDown() + + def test_vsa_volume_create(self): + self.stubs.Set(volume.api.API, "create", stub_volume_create) + + vol = {"size": 100, + "displayName": "VSA Volume Test Name", + "displayDescription": "VSA Volume Test Desc"} + body = {self.test_obj: vol} + req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s' % self.test_objs) + req.method = 'POST' + req.body = json.dumps(body) + req.headers['content-type'] = 'application/json' + resp = req.get_response(fakes.wsgi_app()) + + if self.test_obj == "volume": + self.assertEqual(resp.status_int, 200) + + resp_dict = json.loads(resp.body) + self.assertTrue(self.test_obj in resp_dict) + self.assertEqual(resp_dict[self.test_obj]['size'], + vol['size']) + self.assertEqual(resp_dict[self.test_obj]['displayName'], + vol['displayName']) + self.assertEqual(resp_dict[self.test_obj]['displayDescription'], + vol['displayDescription']) + else: + self.assertEqual(resp.status_int, 400) + + def test_vsa_volume_create_no_body(self): + req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s' % self.test_objs) + req.method = 'POST' + req.body = json.dumps({}) + req.headers['content-type'] = 'application/json' + + resp = req.get_response(fakes.wsgi_app()) + if self.test_obj == "volume": + self.assertEqual(resp.status_int, 422) + else: + self.assertEqual(resp.status_int, 400) + + def test_vsa_volume_index(self): + req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s' % self.test_objs) + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 200) + + def test_vsa_volume_detail(self): + req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/detail' % \ + self.test_objs) + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 200) + + def test_vsa_volume_show(self): + obj_num = 234 if self.test_objs == "volumes" else 345 + req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/%s' % \ + (self.test_objs, obj_num)) + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 200) + + def test_vsa_volume_show_no_vsa_assignment(self): + req = webob.Request.blank('/v1.1/777/zadr-vsa/4/%s/333' % \ + (self.test_objs)) + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 400) + + def test_vsa_volume_show_no_volume(self): + self.stubs.Set(volume.api.API, "get", stub_volume_get_notfound) + + req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/333' % \ + (self.test_objs)) + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 404) + + def test_vsa_volume_update(self): + obj_num = 234 if self.test_objs == "volumes" else 345 + update = {"status": "available", + "displayName": "Test Display name"} + body = {self.test_obj: update} + req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/%s' % \ + (self.test_objs, obj_num)) + req.method = 'PUT' + req.body = json.dumps(body) + req.headers['content-type'] = 'application/json' + + resp = req.get_response(fakes.wsgi_app()) + if self.test_obj == "volume": + self.assertEqual(resp.status_int, 202) + else: + self.assertEqual(resp.status_int, 400) + + def test_vsa_volume_delete(self): + obj_num = 234 if self.test_objs == "volumes" else 345 + req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/%s' % \ + (self.test_objs, obj_num)) + req.method = 'DELETE' + resp = req.get_response(fakes.wsgi_app()) + if self.test_obj == "volume": + self.assertEqual(resp.status_int, 202) + else: + self.assertEqual(resp.status_int, 400) + + def test_vsa_volume_delete_no_vsa_assignment(self): + req = webob.Request.blank('/v1.1/777/zadr-vsa/4/%s/333' % \ + (self.test_objs)) + req.method = 'DELETE' + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 400) + + def test_vsa_volume_delete_no_volume(self): + self.stubs.Set(volume.api.API, "get", stub_volume_get_notfound) + + req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/333' % \ + (self.test_objs)) + req.method = 'DELETE' + resp = req.get_response(fakes.wsgi_app()) + if self.test_obj == "volume": + self.assertEqual(resp.status_int, 404) + else: + self.assertEqual(resp.status_int, 400) + + +class VSADriveApiTest(VSAVolumeApiTest): + def setUp(self): + super(VSADriveApiTest, self).setUp(test_obj="drive", + test_objs="drives") + + def tearDown(self): + self.stubs.UnsetAll() + super(VSADriveApiTest, self).tearDown() diff --git a/nova/tests/api/openstack/v2/extensions/__init__.py b/nova/tests/api/openstack/v2/extensions/__init__.py new file mode 100644 index 000000000..848908a95 --- /dev/null +++ b/nova/tests/api/openstack/v2/extensions/__init__.py @@ -0,0 +1,15 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. diff --git a/nova/tests/api/openstack/v2/extensions/foxinsocks.py b/nova/tests/api/openstack/v2/extensions/foxinsocks.py new file mode 100644 index 000000000..05a995496 --- /dev/null +++ b/nova/tests/api/openstack/v2/extensions/foxinsocks.py @@ -0,0 +1,94 @@ +# 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. + +import json +import webob.exc + +from nova.api.openstack.v2 import extensions + + +class FoxInSocksController(object): + + def index(self, req): + return "Try to say this Mr. Knox, sir..." + + +class Foxinsocks(object): + """The Fox In Socks Extension""" + + name = "Fox In Socks" + alias = "FOXNSOX" + namespace = "http://www.fox.in.socks/api/ext/pie/v1.0" + updated = "2011-01-22T13:25:27-06:00" + + def __init__(self, ext_mgr): + ext_mgr.register(self) + + def get_resources(self): + resources = [] + resource = extensions.ResourceExtension('foxnsocks', + FoxInSocksController()) + resources.append(resource) + return resources + + def get_actions(self): + actions = [] + actions.append(extensions.ActionExtension('servers', 'add_tweedle', + self._add_tweedle)) + actions.append(extensions.ActionExtension('servers', 'delete_tweedle', + self._delete_tweedle)) + actions.append(extensions.ActionExtension('servers', 'fail', + self._fail)) + return actions + + def get_request_extensions(self): + request_exts = [] + + def _goose_handler(req, res, body): + #NOTE: This only handles JSON responses. + # You can use content type header to test for XML. + body['flavor']['googoose'] = req.GET.get('chewing') + return res + + req_ext1 = extensions.RequestExtension('GET', + '/v1.1/:(project_id)/flavors/:(id)', + _goose_handler) + request_exts.append(req_ext1) + + def _bands_handler(req, res, body): + #NOTE: This only handles JSON responses. + # You can use content type header to test for XML. + body['big_bands'] = 'Pig Bands!' + return res + + req_ext2 = extensions.RequestExtension('GET', + '/v1.1/:(project_id)/flavors/:(id)', + _bands_handler) + request_exts.append(req_ext2) + return request_exts + + def _add_tweedle(self, input_dict, req, id): + + return "Tweedle Beetle Added." + + def _delete_tweedle(self, input_dict, req, id): + + return "Tweedle Beetle Deleted." + + def _fail(self, input_dict, req, id): + + raise webob.exc.HTTPBadRequest(explanation='Tweedle fail') diff --git a/nova/tests/api/openstack/v2/test_accounts.py b/nova/tests/api/openstack/v2/test_accounts.py new file mode 100644 index 000000000..ab3ecafde --- /dev/null +++ b/nova/tests/api/openstack/v2/test_accounts.py @@ -0,0 +1,162 @@ +# 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. + + +import json + +from lxml import etree +import webob + +from nova import test +from nova.api.openstack.v2 import accounts +from nova.auth.manager import User +from nova.tests.api.openstack import fakes + + +def fake_init(self): + self.manager = fakes.FakeAuthManager() + + +def fake_admin_check(self, req): + return True + + +class AccountsTest(test.TestCase): + def setUp(self): + super(AccountsTest, self).setUp() + self.flags(verbose=True, allow_admin_api=True) + self.stubs.Set(accounts.Controller, '__init__', + fake_init) + self.stubs.Set(accounts.Controller, '_check_admin', + fake_admin_check) + fakes.FakeAuthManager.clear_fakes() + fakes.FakeAuthDatabase.data = {} + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + fakes.stub_out_auth(self.stubs) + + fakemgr = fakes.FakeAuthManager() + joeuser = User('id1', 'guy1', 'acc1', 'secret1', False) + superuser = User('id2', 'guy2', 'acc2', 'secret2', True) + fakemgr.add_user(joeuser) + fakemgr.add_user(superuser) + fakemgr.create_project('test1', joeuser) + fakemgr.create_project('test2', superuser) + + def test_get_account(self): + req = webob.Request.blank('/v1.1/fake/accounts/test1') + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + + self.assertEqual(res.status_int, 200) + self.assertEqual(res_dict['account']['id'], 'test1') + self.assertEqual(res_dict['account']['name'], 'test1') + self.assertEqual(res_dict['account']['manager'], 'id1') + + def test_get_account_xml(self): + req = webob.Request.blank('/v1.1/fake/accounts/test1.xml') + res = req.get_response(fakes.wsgi_app()) + res_tree = etree.fromstring(res.body) + + self.assertEqual(res.status_int, 200) + self.assertEqual('account', res_tree.tag) + self.assertEqual('test1', res_tree.get('id')) + self.assertEqual('test1', res_tree.get('name')) + self.assertEqual('id1', res_tree.get('manager')) + + def test_account_delete(self): + req = webob.Request.blank('/v1.1/fake/accounts/test1') + req.method = 'DELETE' + res = req.get_response(fakes.wsgi_app()) + self.assertTrue('test1' not in fakes.FakeAuthManager.projects) + self.assertEqual(res.status_int, 200) + + def test_account_create(self): + body = dict(account=dict(description='test account', + manager='id1')) + req = webob.Request.blank('/v1.1/fake/accounts/newacct') + req.headers["Content-Type"] = "application/json" + req.method = 'PUT' + req.body = json.dumps(body) + + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + + self.assertEqual(res.status_int, 200) + self.assertEqual(res_dict['account']['id'], 'newacct') + self.assertEqual(res_dict['account']['name'], 'newacct') + self.assertEqual(res_dict['account']['description'], 'test account') + self.assertEqual(res_dict['account']['manager'], 'id1') + self.assertTrue('newacct' in + fakes.FakeAuthManager.projects) + self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 3) + + def test_account_create_xml(self): + body = dict(account=dict(description='test account', + manager='id1')) + req = webob.Request.blank('/v1.1/fake/accounts/newacct.xml') + req.headers["Content-Type"] = "application/json" + req.method = 'PUT' + req.body = json.dumps(body) + + res = req.get_response(fakes.wsgi_app()) + res_tree = etree.fromstring(res.body) + + self.assertEqual(res.status_int, 200) + self.assertEqual(res_tree.tag, 'account') + self.assertEqual(res_tree.get('id'), 'newacct') + self.assertEqual(res_tree.get('name'), 'newacct') + self.assertEqual(res_tree.get('description'), 'test account') + self.assertEqual(res_tree.get('manager'), 'id1') + self.assertTrue('newacct' in + fakes.FakeAuthManager.projects) + self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 3) + + def test_account_update(self): + body = dict(account=dict(description='test account', + manager='id2')) + req = webob.Request.blank('/v1.1/fake/accounts/test1') + req.headers["Content-Type"] = "application/json" + req.method = 'PUT' + req.body = json.dumps(body) + + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + + self.assertEqual(res.status_int, 200) + self.assertEqual(res_dict['account']['id'], 'test1') + self.assertEqual(res_dict['account']['name'], 'test1') + self.assertEqual(res_dict['account']['description'], 'test account') + self.assertEqual(res_dict['account']['manager'], 'id2') + self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 2) + + def test_account_update_xml(self): + body = dict(account=dict(description='test account', + manager='id2')) + req = webob.Request.blank('/v1.1/fake/accounts/test1.xml') + req.headers["Content-Type"] = "application/json" + req.method = 'PUT' + req.body = json.dumps(body) + + res = req.get_response(fakes.wsgi_app()) + res_tree = etree.fromstring(res.body) + + self.assertEqual(res.status_int, 200) + self.assertEqual(res_tree.tag, 'account') + self.assertEqual(res_tree.get('id'), 'test1') + self.assertEqual(res_tree.get('name'), 'test1') + self.assertEqual(res_tree.get('description'), 'test account') + self.assertEqual(res_tree.get('manager'), 'id2') + self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 2) diff --git a/nova/tests/api/openstack/v2/test_api.py b/nova/tests/api/openstack/v2/test_api.py new file mode 100644 index 000000000..318c03a88 --- /dev/null +++ b/nova/tests/api/openstack/v2/test_api.py @@ -0,0 +1,126 @@ +# 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. + +import json + +from lxml import etree +import webob.exc +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.tests.api.openstack import fakes + + +class APITest(test.TestCase): + + def _wsgi_app(self, inner_app): + # simpler version of the app than fakes.wsgi_app + return v2.FaultWrapper(inner_app) + + def test_malformed_json(self): + req = webob.Request.blank('/') + req.method = 'POST' + req.body = '{' + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 400) + + def test_malformed_xml(self): + req = webob.Request.blank('/') + req.method = 'POST' + req.body = '' + req.headers["content-type"] = "application/xml" + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 400) + + def test_vendor_content_type_json(self): + ctype = 'application/vnd.openstack.compute+json' + + req = webob.Request.blank('/') + req.headers['Accept'] = ctype + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + self.assertEqual(res.content_type, ctype) + + body = json.loads(res.body) + + def test_vendor_content_type_xml(self): + ctype = 'application/vnd.openstack.compute+xml' + + req = webob.Request.blank('/') + req.headers['Accept'] = ctype + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + self.assertEqual(res.content_type, ctype) + + body = etree.XML(res.body) + + def test_exceptions_are_converted_to_faults(self): + + @webob.dec.wsgify + def succeed(req): + return 'Succeeded' + + @webob.dec.wsgify + def raise_webob_exc(req): + raise webob.exc.HTTPNotFound(explanation='Raised a webob.exc') + + @webob.dec.wsgify + def fail(req): + raise Exception("Threw an exception") + + @webob.dec.wsgify + def raise_api_fault(req): + exc = webob.exc.HTTPNotFound(explanation='Raised a webob.exc') + return wsgi.Fault(exc) + + #api.application = succeed + api = self._wsgi_app(succeed) + resp = Request.blank('/').get_response(api) + self.assertFalse('cloudServersFault' in resp.body, resp.body) + self.assertEqual(resp.status_int, 200, resp.body) + + #api.application = raise_webob_exc + api = self._wsgi_app(raise_webob_exc) + resp = Request.blank('/').get_response(api) + self.assertFalse('cloudServersFault' in resp.body, resp.body) + self.assertEqual(resp.status_int, 404, resp.body) + + #api.application = raise_api_fault + api = self._wsgi_app(raise_api_fault) + resp = Request.blank('/').get_response(api) + self.assertTrue('itemNotFound' in resp.body, resp.body) + self.assertEqual(resp.status_int, 404, resp.body) + + #api.application = fail + api = self._wsgi_app(fail) + resp = Request.blank('/').get_response(api) + self.assertTrue('{"cloudServersFault' in resp.body, resp.body) + self.assertEqual(resp.status_int, 500, resp.body) + + #api.application = fail + api = self._wsgi_app(fail) + resp = Request.blank('/.xml').get_response(api) + self.assertTrue(' self.max_id: + self.max_id = id + + +def stub_instance(id, user_id='fake', project_id='fake', host=None, + vm_state=None, task_state=None, + reservation_id="", uuid=FAKE_UUID, image_ref="10", + flavor_id="1", name=None, key_name='', + access_ipv4=None, access_ipv6=None, progress=0): + + if host is not None: + host = str(host) + + if key_name: + key_data = 'FAKE' + else: + key_data = '' + + # ReservationID isn't sent back, hack it in there. + server_name = name or "server%s" % id + if reservation_id != "": + server_name = "reservation_%s" % (reservation_id, ) + + instance = { + "id": int(id), + "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), + "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), + "admin_pass": "", + "user_id": user_id, + "project_id": project_id, + "image_ref": image_ref, + "kernel_id": "", + "ramdisk_id": "", + "launch_index": 0, + "key_name": key_name, + "key_data": key_data, + "vm_state": vm_state or vm_states.BUILDING, + "task_state": task_state, + "memory_mb": 0, + "vcpus": 0, + "local_gb": 0, + "hostname": "", + "host": host, + "instance_type": {}, + "user_data": "", + "reservation_id": reservation_id, + "mac_address": "", + "scheduled_at": utils.utcnow(), + "launched_at": utils.utcnow(), + "terminated_at": utils.utcnow(), + "availability_zone": "", + "display_name": server_name, + "display_description": "", + "locked": False, + "metadata": [], + "access_ip_v4": access_ipv4, + "access_ip_v6": access_ipv6, + "uuid": uuid, + "progress": progress} + + return instance + + +class ConsolesControllerTest(test.TestCase): + def setUp(self): + super(ConsolesControllerTest, self).setUp() + self.flags(verbose=True) + self.instance_db = FakeInstanceDB() + self.stubs.Set(db, 'instance_get', + self.instance_db.return_server_by_id) + self.stubs.Set(db, 'instance_get_by_uuid', + self.instance_db.return_server_by_uuid) + self.uuid = str(utils.gen_uuid()) + self.url = '/v1.1/fake/servers/%s/consoles' % self.uuid + self.controller = consoles.Controller() + + def test_create_console(self): + def fake_create_console(cons_self, context, instance_id): + self.assertEqual(instance_id, self.uuid) + return {} + self.stubs.Set(console.API, 'create_console', fake_create_console) + + req = fakes.HTTPRequest.blank(self.url) + self.controller.create(req, self.uuid) + + def test_show_console(self): + def fake_get_console(cons_self, context, instance_id, console_id): + self.assertEqual(instance_id, self.uuid) + self.assertEqual(console_id, 20) + pool = dict(console_type='fake_type', + public_hostname='fake_hostname') + return dict(id=console_id, password='fake_password', + port='fake_port', pool=pool) + + expected = {'console': {'id': 20, + 'port': 'fake_port', + 'host': 'fake_hostname', + 'password': 'fake_password', + 'console_type': 'fake_type'}} + + self.stubs.Set(console.API, 'get_console', fake_get_console) + + req = fakes.HTTPRequest.blank(self.url + '/20') + res_dict = self.controller.show(req, self.uuid, '20') + self.assertDictMatch(res_dict, expected) + + def test_show_console_unknown_console(self): + def fake_get_console(cons_self, context, instance_id, console_id): + raise exception.ConsoleNotFound(console_id=console_id) + + self.stubs.Set(console.API, 'get_console', fake_get_console) + + req = fakes.HTTPRequest.blank(self.url + '/20') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, + req, self.uuid, '20') + + def test_show_console_unknown_instance(self): + def fake_get_console(cons_self, context, instance_id, console_id): + raise exception.InstanceNotFound(instance_id=instance_id) + + self.stubs.Set(console.API, 'get_console', fake_get_console) + + req = fakes.HTTPRequest.blank(self.url + '/20') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, + req, self.uuid, '20') + + def test_list_consoles(self): + def fake_get_consoles(cons_self, context, instance_id): + self.assertEqual(instance_id, self.uuid) + + pool1 = dict(console_type='fake_type', + public_hostname='fake_hostname') + cons1 = dict(id=10, password='fake_password', + port='fake_port', pool=pool1) + pool2 = dict(console_type='fake_type2', + public_hostname='fake_hostname2') + cons2 = dict(id=11, password='fake_password2', + port='fake_port2', pool=pool2) + return [cons1, cons2] + + expected = {'consoles': + [{'console': {'id': 10, 'console_type': 'fake_type'}}, + {'console': {'id': 11, 'console_type': 'fake_type2'}}]} + + self.stubs.Set(console.API, 'get_consoles', fake_get_consoles) + + req = fakes.HTTPRequest.blank(self.url) + res_dict = self.controller.index(req, self.uuid) + self.assertDictMatch(res_dict, expected) + + def test_delete_console(self): + def fake_get_console(cons_self, context, instance_id, console_id): + self.assertEqual(instance_id, self.uuid) + self.assertEqual(console_id, 20) + pool = dict(console_type='fake_type', + public_hostname='fake_hostname') + return dict(id=console_id, password='fake_password', + port='fake_port', pool=pool) + + def fake_delete_console(cons_self, context, instance_id, console_id): + self.assertEqual(instance_id, self.uuid) + self.assertEqual(console_id, 20) + + self.stubs.Set(console.API, 'get_console', fake_get_console) + self.stubs.Set(console.API, 'delete_console', fake_delete_console) + + req = fakes.HTTPRequest.blank(self.url + '/20') + self.controller.delete(req, self.uuid, '20') + + def test_delete_console_unknown_console(self): + def fake_delete_console(cons_self, context, instance_id, console_id): + raise exception.ConsoleNotFound(console_id=console_id) + + self.stubs.Set(console.API, 'delete_console', fake_delete_console) + + req = fakes.HTTPRequest.blank(self.url + '/20') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, + req, self.uuid, '20') + + def test_delete_console_unknown_instance(self): + def fake_delete_console(cons_self, context, instance_id, console_id): + raise exception.InstanceNotFound(instance_id=instance_id) + + self.stubs.Set(console.API, 'delete_console', fake_delete_console) + + req = fakes.HTTPRequest.blank(self.url + '/20') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, + req, self.uuid, '20') + + +class TestConsolesXMLSerializer(test.TestCase): + + serializer = consoles.ConsoleXMLSerializer() + + def test_show(self): + fixture = {'console': {'id': 20, + 'password': 'fake_password', + 'port': 'fake_port', + 'host': 'fake_hostname', + 'console_type': 'fake_type'}} + + output = self.serializer.serialize(fixture, 'show') + res_tree = etree.XML(output) + + self.assertEqual(res_tree.tag, 'console') + self.assertEqual(res_tree.xpath('id')[0].text, '20') + self.assertEqual(res_tree.xpath('port')[0].text, 'fake_port') + self.assertEqual(res_tree.xpath('host')[0].text, 'fake_hostname') + self.assertEqual(res_tree.xpath('password')[0].text, 'fake_password') + self.assertEqual(res_tree.xpath('console_type')[0].text, 'fake_type') + + def test_index(self): + fixture = {'consoles': [{'console': {'id': 10, + 'console_type': 'fake_type'}}, + {'console': {'id': 11, + 'console_type': 'fake_type2'}}]} + + output = self.serializer.serialize(fixture, 'index') + res_tree = etree.XML(output) + + self.assertEqual(res_tree.tag, 'consoles') + self.assertEqual(len(res_tree), 2) + self.assertEqual(res_tree[0].tag, 'console') + self.assertEqual(res_tree[1].tag, 'console') + self.assertEqual(len(res_tree[0]), 1) + self.assertEqual(res_tree[0][0].tag, 'console') + self.assertEqual(len(res_tree[1]), 1) + self.assertEqual(res_tree[1][0].tag, 'console') + self.assertEqual(res_tree[0][0].xpath('id')[0].text, '10') + self.assertEqual(res_tree[1][0].xpath('id')[0].text, '11') + self.assertEqual(res_tree[0][0].xpath('console_type')[0].text, + 'fake_type') + self.assertEqual(res_tree[1][0].xpath('console_type')[0].text, + 'fake_type2') diff --git a/nova/tests/api/openstack/v2/test_extensions.py b/nova/tests/api/openstack/v2/test_extensions.py new file mode 100644 index 000000000..5d388ddd3 --- /dev/null +++ b/nova/tests/api/openstack/v2/test_extensions.py @@ -0,0 +1,516 @@ +# 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. + +import json +import os.path + +import webob +from lxml import etree + +from nova.api.openstack import v2 +from nova.api.openstack.v2 import extensions +from nova.api.openstack.v2 import flavors +from nova.api.openstack import wsgi +from nova.api.openstack import xmlutil +from nova import context +from nova import flags +from nova import test +from nova.tests.api.openstack import fakes +from nova import wsgi as base_wsgi + +FLAGS = flags.FLAGS + +NS = "{http://docs.openstack.org/compute/api/v1.1}" +ATOMNS = "{http://www.w3.org/2005/Atom}" +response_body = "Try to say this Mr. Knox, sir..." + + +class StubController(object): + + def __init__(self, body): + self.body = body + + def index(self, req): + return self.body + + def create(self, req): + msg = 'All aboard the fail train!' + raise webob.exc.HTTPBadRequest(explanation=msg) + + def show(self, req, id): + raise webob.exc.HTTPNotFound() + + +class StubExtensionManager(object): + """Provides access to Tweedle Beetles""" + + name = "Tweedle Beetle Extension" + alias = "TWDLBETL" + + def __init__(self, resource_ext=None, action_ext=None, request_ext=None): + self.resource_ext = resource_ext + self.action_ext = action_ext + self.request_ext = request_ext + + def get_resources(self): + resource_exts = [] + if self.resource_ext: + resource_exts.append(self.resource_ext) + return resource_exts + + def get_actions(self): + action_exts = [] + if self.action_ext: + action_exts.append(self.action_ext) + return action_exts + + def get_request_extensions(self): + request_extensions = [] + if self.request_ext: + request_extensions.append(self.request_ext) + return request_extensions + + +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) + + +class ExtensionControllerTest(ExtensionTestCase): + + def setUp(self): + super(ExtensionControllerTest, self).setUp() + self.ext_list = [ + "AdminActions", + "Createserverext", + "DeferredDelete", + "DiskConfig", + "ExtendedStatus", + "FlavorExtraSpecs", + "FlavorExtraData", + "Floating_ips", + "Fox In Socks", + "Hosts", + "Keypairs", + "Multinic", + "Quotas", + "Rescue", + "SecurityGroups", + "SimpleTenantUsage", + "VSAs", + "VirtualInterfaces", + "Volumes", + "VolumeTypes", + "Zones", + ] + self.ext_list.sort() + + def test_list_extensions_json(self): + app = v2.APIRouter() + ext_midware = extensions.ExtensionMiddleware(app) + ser_midware = wsgi.LazySerializationMiddleware(ext_midware) + request = webob.Request.blank("/123/extensions") + response = request.get_response(ser_midware) + self.assertEqual(200, response.status_int) + + # Make sure we have all the extensions. + data = json.loads(response.body) + names = [x['name'] for x in data['extensions']] + names.sort() + self.assertEqual(names, self.ext_list) + + # Make sure that at least Fox in Sox is correct. + (fox_ext, ) = [ + x for x in data['extensions'] if x['alias'] == 'FOXNSOX'] + self.assertEqual(fox_ext, { + 'namespace': 'http://www.fox.in.socks/api/ext/pie/v1.0', + 'name': 'Fox In Socks', + 'updated': '2011-01-22T13:25:27-06:00', + 'description': 'The Fox In Socks Extension', + 'alias': 'FOXNSOX', + 'links': [] + }, + ) + + def test_get_extension_json(self): + app = v2.APIRouter() + ext_midware = extensions.ExtensionMiddleware(app) + ser_midware = wsgi.LazySerializationMiddleware(ext_midware) + request = webob.Request.blank("/123/extensions/FOXNSOX") + response = request.get_response(ser_midware) + self.assertEqual(200, response.status_int) + + data = json.loads(response.body) + self.assertEqual(data['extension'], { + "namespace": "http://www.fox.in.socks/api/ext/pie/v1.0", + "name": "Fox In Socks", + "updated": "2011-01-22T13:25:27-06:00", + "description": "The Fox In Socks Extension", + "alias": "FOXNSOX", + "links": []}) + + def test_get_non_existing_extension_json(self): + app = v2.APIRouter() + ext_midware = extensions.ExtensionMiddleware(app) + request = webob.Request.blank("/123/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) + ser_midware = wsgi.LazySerializationMiddleware(ext_midware) + request = webob.Request.blank("/123/extensions") + request.accept = "application/xml" + response = request.get_response(ser_midware) + self.assertEqual(200, response.status_int) + print response.body + + root = etree.XML(response.body) + self.assertEqual(root.tag.split('extensions')[0], NS) + + # Make sure we have all the extensions. + exts = root.findall('{0}extension'.format(NS)) + self.assertEqual(len(exts), len(self.ext_list)) + + # Make sure that at least Fox in Sox is correct. + (fox_ext, ) = [x for x in exts if x.get('alias') == 'FOXNSOX'] + self.assertEqual(fox_ext.get('name'), 'Fox In Socks') + self.assertEqual(fox_ext.get('namespace'), + 'http://www.fox.in.socks/api/ext/pie/v1.0') + self.assertEqual(fox_ext.get('updated'), '2011-01-22T13:25:27-06:00') + self.assertEqual(fox_ext.findtext('{0}description'.format(NS)), + 'The Fox In Socks Extension') + + xmlutil.validate_schema(root, 'extensions') + + def test_get_extension_xml(self): + app = v2.APIRouter() + ext_midware = extensions.ExtensionMiddleware(app) + ser_midware = wsgi.LazySerializationMiddleware(ext_midware) + request = webob.Request.blank("/123/extensions/FOXNSOX") + request.accept = "application/xml" + response = request.get_response(ser_midware) + self.assertEqual(200, response.status_int) + xml = response.body + print xml + + root = etree.XML(xml) + self.assertEqual(root.tag.split('extension')[0], NS) + self.assertEqual(root.get('alias'), 'FOXNSOX') + self.assertEqual(root.get('name'), 'Fox In Socks') + self.assertEqual(root.get('namespace'), + 'http://www.fox.in.socks/api/ext/pie/v1.0') + self.assertEqual(root.get('updated'), '2011-01-22T13:25:27-06:00') + self.assertEqual(root.findtext('{0}description'.format(NS)), + 'The Fox In Socks Extension') + + xmlutil.validate_schema(root, 'extension') + + +class ResourceExtensionTest(ExtensionTestCase): + + def test_no_extension_present(self): + manager = StubExtensionManager(None) + app = v2.APIRouter() + ext_midware = 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)) + manager = StubExtensionManager(res_ext) + app = v2.APIRouter() + ext_midware = extensions.ExtensionMiddleware(app, manager) + ser_midware = wsgi.LazySerializationMiddleware(ext_midware) + request = webob.Request.blank("/123/tweedles") + response = request.get_response(ser_midware) + self.assertEqual(200, response.status_int) + self.assertEqual(response_body, response.body) + + def test_get_resources_with_controller(self): + res_ext = extensions.ResourceExtension('tweedles', + StubController(response_body)) + manager = StubExtensionManager(res_ext) + app = v2.APIRouter() + ext_midware = extensions.ExtensionMiddleware(app, manager) + ser_midware = wsgi.LazySerializationMiddleware(ext_midware) + request = webob.Request.blank("/123/tweedles") + response = request.get_response(ser_midware) + self.assertEqual(200, response.status_int) + self.assertEqual(response_body, response.body) + + def test_bad_request(self): + res_ext = extensions.ResourceExtension('tweedles', + StubController(response_body)) + manager = StubExtensionManager(res_ext) + app = v2.APIRouter() + ext_midware = extensions.ExtensionMiddleware(app, manager) + ser_midware = wsgi.LazySerializationMiddleware(ext_midware) + request = webob.Request.blank("/123/tweedles") + request.method = "POST" + response = request.get_response(ser_midware) + self.assertEqual(400, response.status_int) + self.assertEqual('application/json', response.content_type) + body = json.loads(response.body) + expected = { + "badRequest": { + "message": "All aboard the fail train!", + "code": 400 + } + } + self.assertDictMatch(expected, body) + + def test_non_exist_resource(self): + res_ext = extensions.ResourceExtension('tweedles', + StubController(response_body)) + manager = StubExtensionManager(res_ext) + app = v2.APIRouter() + ext_midware = extensions.ExtensionMiddleware(app, manager) + ser_midware = wsgi.LazySerializationMiddleware(ext_midware) + request = webob.Request.blank("/123/tweedles/1") + response = request.get_response(ser_midware) + self.assertEqual(404, response.status_int) + self.assertEqual('application/json', response.content_type) + body = json.loads(response.body) + expected = { + "itemNotFound": { + "message": "The resource could not be found.", + "code": 404 + } + } + self.assertDictMatch(expected, body) + + +class InvalidExtension(object): + + alias = "THIRD" + + +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) + ser_midware = wsgi.LazySerializationMiddleware(ext_midware) + request = webob.Request.blank("/123/foxnsocks") + response = request.get_response(ser_midware) + self.assertEqual(200, response.status_int) + self.assertEqual(response_body, response.body) + + 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) + ext_mgr = ext_midware.ext_mgr + ext_mgr.register(InvalidExtension()) + self.assertTrue('FOXNSOX' in ext_mgr.extensions) + self.assertTrue('THIRD' not in ext_mgr.extensions) + + +class ActionExtensionTest(ExtensionTestCase): + + def _send_server_action_request(self, url, body): + app = v2.APIRouter() + ext_midware = extensions.ExtensionMiddleware(app) + ser_midware = wsgi.LazySerializationMiddleware(ext_midware) + request = webob.Request.blank(url) + request.method = 'POST' + request.content_type = 'application/json' + request.body = json.dumps(body) + response = request.get_response(ser_midware) + return response + + def test_extended_action(self): + body = dict(add_tweedle=dict(name="test")) + url = "/123/servers/abcd/action" + response = self._send_server_action_request(url, body) + self.assertEqual(200, response.status_int) + self.assertEqual("Tweedle Beetle Added.", response.body) + + body = dict(delete_tweedle=dict(name="test")) + response = self._send_server_action_request(url, body) + self.assertEqual(200, response.status_int) + self.assertEqual("Tweedle Beetle Deleted.", response.body) + + def test_invalid_action(self): + body = dict(blah=dict(name="test")) # Doesn't exist + url = "/123/servers/abcd/action" + response = self._send_server_action_request(url, body) + self.assertEqual(400, response.status_int) + self.assertEqual('application/json', response.content_type) + body = json.loads(response.body) + expected = { + "badRequest": { + "message": "There is no such server action: blah", + "code": 400 + } + } + self.assertDictMatch(expected, body) + + def test_non_exist_action(self): + body = dict(blah=dict(name="test")) + url = "/123/fdsa/1/action" + response = self._send_server_action_request(url, body) + self.assertEqual(404, response.status_int) + + def test_failed_action(self): + body = dict(fail=dict(name="test")) + url = "/123/servers/abcd/action" + response = self._send_server_action_request(url, body) + self.assertEqual(400, response.status_int) + self.assertEqual('application/json', response.content_type) + body = json.loads(response.body) + expected = { + "badRequest": { + "message": "Tweedle fail", + "code": 400 + } + } + self.assertDictMatch(expected, body) + + +class RequestExtensionTest(ExtensionTestCase): + + def test_get_resources_with_stub_mgr(self): + + def _req_handler(req, res, body): + # only handle JSON responses + body['flavor']['googoose'] = req.GET.get('chewing') + return res + + req_ext = extensions.RequestExtension('GET', + '/v1.1/123/flavors/:(id)', + _req_handler) + + manager = StubExtensionManager(None, None, req_ext) + app = fakes.wsgi_app(serialization=base_wsgi.Middleware) + ext_midware = extensions.ExtensionMiddleware(app, manager) + ser_midware = wsgi.LazySerializationMiddleware(ext_midware) + request = webob.Request.blank("/v1.1/123/flavors/1?chewing=bluegoo") + request.environ['api.version'] = '1.1' + response = request.get_response(ser_midware) + self.assertEqual(200, response.status_int) + response_data = json.loads(response.body) + self.assertEqual('bluegoo', response_data['flavor']['googoose']) + + def test_get_resources_with_mgr(self): + + app = fakes.wsgi_app(serialization=base_wsgi.Middleware) + ext_midware = extensions.ExtensionMiddleware(app) + ser_midware = wsgi.LazySerializationMiddleware(ext_midware) + request = webob.Request.blank("/v1.1/123/flavors/1?chewing=newblue") + request.environ['api.version'] = '1.1' + response = request.get_response(ser_midware) + self.assertEqual(200, response.status_int) + response_data = json.loads(response.body) + self.assertEqual('newblue', response_data['flavor']['googoose']) + self.assertEqual("Pig Bands!", response_data['big_bands']) + + +class ExtensionsXMLSerializerTest(test.TestCase): + + def test_serialize_extension(self): + serializer = extensions.ExtensionsXMLSerializer() + data = {'extension': { + 'name': 'ext1', + 'namespace': 'http://docs.rack.com/servers/api/ext/pie/v1.0', + 'alias': 'RS-PIE', + 'updated': '2011-01-22T13:25:27-06:00', + 'description': 'Adds the capability to share an image.', + 'links': [{'rel': 'describedby', + 'type': 'application/pdf', + 'href': 'http://docs.rack.com/servers/api/ext/cs.pdf'}, + {'rel': 'describedby', + 'type': 'application/vnd.sun.wadl+xml', + 'href': 'http://docs.rack.com/servers/api/ext/cs.wadl'}]}} + + xml = serializer.serialize(data, 'show') + print xml + root = etree.XML(xml) + ext_dict = data['extension'] + self.assertEqual(root.findtext('{0}description'.format(NS)), + ext_dict['description']) + + for key in ['name', 'namespace', 'alias', 'updated']: + self.assertEqual(root.get(key), ext_dict[key]) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(ext_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + xmlutil.validate_schema(root, 'extension') + + def test_serialize_extensions(self): + serializer = extensions.ExtensionsXMLSerializer() + data = {"extensions": [{ + "name": "Public Image Extension", + "namespace": "http://foo.com/api/ext/pie/v1.0", + "alias": "RS-PIE", + "updated": "2011-01-22T13:25:27-06:00", + "description": "Adds the capability to share an image.", + "links": [{"rel": "describedby", + "type": "application/pdf", + "type": "application/vnd.sun.wadl+xml", + "href": "http://foo.com/api/ext/cs-pie.pdf"}, + {"rel": "describedby", + "type": "application/vnd.sun.wadl+xml", + "href": "http://foo.com/api/ext/cs-pie.wadl"}]}, + {"name": "Cloud Block Storage", + "namespace": "http://foo.com/api/ext/cbs/v1.0", + "alias": "RS-CBS", + "updated": "2011-01-12T11:22:33-06:00", + "description": "Allows mounting cloud block storage.", + "links": [{"rel": "describedby", + "type": "application/pdf", + "href": "http://foo.com/api/ext/cs-cbs.pdf"}, + {"rel": "describedby", + "type": "application/vnd.sun.wadl+xml", + "href": "http://foo.com/api/ext/cs-cbs.wadl"}]}]} + + xml = serializer.serialize(data, 'index') + print xml + root = etree.XML(xml) + ext_elems = root.findall('{0}extension'.format(NS)) + self.assertEqual(len(ext_elems), 2) + for i, ext_elem in enumerate(ext_elems): + ext_dict = data['extensions'][i] + self.assertEqual(ext_elem.findtext('{0}description'.format(NS)), + ext_dict['description']) + + for key in ['name', 'namespace', 'alias', 'updated']: + self.assertEqual(ext_elem.get(key), ext_dict[key]) + + link_nodes = ext_elem.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(ext_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + xmlutil.validate_schema(root, 'extensions') diff --git a/nova/tests/api/openstack/v2/test_flavors.py b/nova/tests/api/openstack/v2/test_flavors.py new file mode 100644 index 000000000..c62291ad1 --- /dev/null +++ b/nova/tests/api/openstack/v2/test_flavors.py @@ -0,0 +1,670 @@ +# 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. + +import json + +from lxml import etree +import webob + +from nova.api.openstack.v2 import flavors +from nova.api.openstack import xmlutil +import nova.compute.instance_types +from nova import exception +from nova import test +from nova.tests.api.openstack import fakes +from nova import wsgi + + +NS = "{http://docs.openstack.org/compute/api/v1.1}" +ATOMNS = "{http://www.w3.org/2005/Atom}" + + +FAKE_FLAVORS = { + 'flavor 1': { + "flavorid": '1', + "name": 'flavor 1', + "memory_mb": '256', + "local_gb": '10' + }, + 'flavor 2': { + "flavorid": '2', + "name": 'flavor 2', + "memory_mb": '512', + "local_gb": '20' + }, +} + + +def fake_instance_type_get_by_flavor_id(flavorid): + return FAKE_FLAVORS['flavor %s' % flavorid] + + +def fake_instance_type_get_all(inactive=False, filters=None): + def reject_min(db_attr, filter_attr): + return filter_attr in filters and\ + int(flavor[db_attr]) < int(filters[filter_attr]) + + filters = filters or {} + output = {} + for (flavor_name, flavor) in FAKE_FLAVORS.items(): + if reject_min('memory_mb', 'min_memory_mb'): + continue + elif reject_min('local_gb', 'min_local_gb'): + continue + + output[flavor_name] = flavor + + return output + + +def empty_instance_type_get_all(inactive=False, filters=None): + return {} + + +def return_instance_type_not_found(flavor_id): + raise exception.InstanceTypeNotFound(flavor_id=flavor_id) + + +class FlavorsTest(test.TestCase): + def setUp(self): + super(FlavorsTest, self).setUp() + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + self.stubs.Set(nova.compute.instance_types, "get_all_types", + fake_instance_type_get_all) + self.stubs.Set(nova.compute.instance_types, + "get_instance_type_by_flavor_id", + fake_instance_type_get_by_flavor_id) + + self.controller = flavors.Controller() + + def tearDown(self): + self.stubs.UnsetAll() + super(FlavorsTest, self).tearDown() + + def test_get_flavor_by_invalid_id(self): + self.stubs.Set(nova.compute.instance_types, + "get_instance_type_by_flavor_id", + return_instance_type_not_found) + req = fakes.HTTPRequest.blank('/v1.1/fake/flavors/asdf') + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.show, req, 'asdf') + + def test_get_flavor_by_id(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/flavors/1') + flavor = self.controller.show(req, '1') + expected = { + "flavor": { + "id": "1", + "name": "flavor 1", + "ram": "256", + "disk": "10", + "rxtx_cap": "", + "rxtx_quota": "", + "swap": "", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/1", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/1", + }, + ], + }, + } + self.assertEqual(flavor, expected) + + def test_get_flavor_list(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/flavors') + flavor = self.controller.index(req) + expected = { + "flavors": [ + { + "id": "1", + "name": "flavor 1", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/1", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/1", + }, + ], + }, + { + "id": "2", + "name": "flavor 2", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/2", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/2", + }, + ], + }, + ], + } + self.assertEqual(flavor, expected) + + def test_get_flavor_list_detail(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/flavors/detail') + flavor = self.controller.detail(req) + expected = { + "flavors": [ + { + "id": "1", + "name": "flavor 1", + "ram": "256", + "disk": "10", + "rxtx_cap": "", + "rxtx_quota": "", + "swap": "", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/1", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/1", + }, + ], + }, + { + "id": "2", + "name": "flavor 2", + "ram": "512", + "disk": "20", + "rxtx_cap": "", + "rxtx_quota": "", + "swap": "", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/2", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/2", + }, + ], + }, + ], + } + self.assertEqual(flavor, expected) + + def test_get_empty_flavor_list(self): + self.stubs.Set(nova.compute.instance_types, "get_all_types", + empty_instance_type_get_all) + + req = fakes.HTTPRequest.blank('/v1.1/fake/flavors') + flavors = self.controller.index(req) + expected = {'flavors': []} + self.assertEqual(flavors, expected) + + def test_get_flavor_list_filter_min_ram(self): + """Flavor lists may be filtered by minRam""" + req = fakes.HTTPRequest.blank('/v1.1/fake/flavors?minRam=512') + flavor = self.controller.index(req) + expected = { + "flavors": [ + { + "id": "2", + "name": "flavor 2", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/2", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/2", + }, + ], + }, + ], + } + self.assertEqual(flavor, expected) + + def test_get_flavor_list_filter_min_disk(self): + """Flavor lists may be filtered by minRam""" + req = fakes.HTTPRequest.blank('/v1.1/fake/flavors?minDisk=20') + flavor = self.controller.index(req) + expected = { + "flavors": [ + { + "id": "2", + "name": "flavor 2", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/2", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/2", + }, + ], + }, + ], + } + self.assertEqual(flavor, expected) + + def test_get_flavor_list_detail_min_ram_and_min_disk(self): + """Tests that filtering work on flavor details and that minRam and + minDisk filters can be combined + """ + req = fakes.HTTPRequest.blank('/v1.1/fake/flavors/detail' + '?minRam=256&minDisk=20') + flavor = self.controller.detail(req) + expected = { + "flavors": [ + { + "id": "2", + "name": "flavor 2", + "ram": "512", + "disk": "20", + "rxtx_cap": "", + "rxtx_quota": "", + "swap": "", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/2", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/2", + }, + ], + }, + ], + } + self.assertEqual(flavor, expected) + + def test_get_flavor_list_detail_bogus_min_ram(self): + """Tests that bogus minRam filtering values are ignored""" + req = fakes.HTTPRequest.blank('/v1.1/fake/flavors/detail?minRam=16GB') + flavor = self.controller.detail(req) + expected = { + "flavors": [ + { + "id": "1", + "name": "flavor 1", + "ram": "256", + "disk": "10", + "rxtx_cap": "", + "rxtx_quota": "", + "swap": "", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/1", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/1", + }, + ], + }, + { + "id": "2", + "name": "flavor 2", + "ram": "512", + "disk": "20", + "rxtx_cap": "", + "rxtx_quota": "", + "swap": "", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/2", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/2", + }, + ], + }, + ], + } + self.assertEqual(flavor, expected) + + def test_get_flavor_list_detail_bogus_min_disk(self): + """Tests that bogus minDisk filtering values are ignored""" + req = fakes.HTTPRequest.blank('/v1.1/fake/flavors/detail?minDisk=16GB') + flavor = self.controller.detail(req) + expected = { + "flavors": [ + { + "id": "1", + "name": "flavor 1", + "ram": "256", + "disk": "10", + "rxtx_cap": "", + "rxtx_quota": "", + "swap": "", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/1", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/1", + }, + ], + }, + { + "id": "2", + "name": "flavor 2", + "ram": "512", + "disk": "20", + "rxtx_cap": "", + "rxtx_quota": "", + "swap": "", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/2", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/2", + }, + ], + }, + ], + } + self.assertEqual(flavor, expected) + + +class FlavorsXMLSerializationTest(test.TestCase): + + def test_xml_declaration(self): + serializer = flavors.FlavorXMLSerializer() + + fixture = { + "flavor": { + "id": "12", + "name": "asdf", + "ram": "256", + "disk": "10", + "rxtx_cap": "", + "rxtx_quota": "", + "swap": "", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/12", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/12", + }, + ], + }, + } + + output = serializer.serialize(fixture, 'show') + print output + has_dec = output.startswith("") + self.assertTrue(has_dec) + + def test_show(self): + serializer = flavors.FlavorXMLSerializer() + + fixture = { + "flavor": { + "id": "12", + "name": "asdf", + "ram": "256", + "disk": "10", + "rxtx_cap": "", + "rxtx_quota": "", + "swap": "", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/12", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/12", + }, + ], + }, + } + + output = serializer.serialize(fixture, 'show') + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'flavor') + flavor_dict = fixture['flavor'] + + for key in ['name', 'id', 'ram', 'disk']: + self.assertEqual(root.get(key), str(flavor_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(flavor_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_show_handles_integers(self): + serializer = flavors.FlavorXMLSerializer() + + fixture = { + "flavor": { + "id": 12, + "name": "asdf", + "ram": 256, + "disk": 10, + "rxtx_cap": "", + "rxtx_quota": "", + "swap": "", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/12", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/12", + }, + ], + }, + } + + output = serializer.serialize(fixture, 'show') + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'flavor') + flavor_dict = fixture['flavor'] + + for key in ['name', 'id', 'ram', 'disk']: + self.assertEqual(root.get(key), str(flavor_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(flavor_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_detail(self): + serializer = flavors.FlavorXMLSerializer() + + fixture = { + "flavors": [ + { + "id": "23", + "name": "flavor 23", + "ram": "512", + "disk": "20", + "rxtx_cap": "", + "rxtx_quota": "", + "swap": "", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/23", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/23", + }, + ], + }, + { + "id": "13", + "name": "flavor 13", + "ram": "256", + "disk": "10", + "rxtx_cap": "", + "rxtx_quota": "", + "swap": "", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/13", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/13", + }, + ], + }, + ], + } + + output = serializer.serialize(fixture, 'detail') + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'flavors') + flavor_elems = root.findall('{0}flavor'.format(NS)) + self.assertEqual(len(flavor_elems), 2) + for i, flavor_elem in enumerate(flavor_elems): + flavor_dict = fixture['flavors'][i] + + for key in ['name', 'id', 'ram', 'disk']: + self.assertEqual(flavor_elem.get(key), str(flavor_dict[key])) + + link_nodes = flavor_elem.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(flavor_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_index(self): + serializer = flavors.FlavorXMLSerializer() + + fixture = { + "flavors": [ + { + "id": "23", + "name": "flavor 23", + "ram": "512", + "disk": "20", + "rxtx_cap": "", + "rxtx_quota": "", + "swap": "", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/23", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/23", + }, + ], + }, + { + "id": "13", + "name": "flavor 13", + "ram": "256", + "disk": "10", + "rxtx_cap": "", + "rxtx_quota": "", + "swap": "", + "vcpus": "", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/flavors/13", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/flavors/13", + }, + ], + }, + ], + } + + output = serializer.serialize(fixture, 'index') + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'flavors_index') + flavor_elems = root.findall('{0}flavor'.format(NS)) + self.assertEqual(len(flavor_elems), 2) + for i, flavor_elem in enumerate(flavor_elems): + flavor_dict = fixture['flavors'][i] + + for key in ['name', 'id']: + self.assertEqual(flavor_elem.get(key), str(flavor_dict[key])) + + link_nodes = flavor_elem.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(flavor_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_index_empty(self): + serializer = flavors.FlavorXMLSerializer() + + fixture = { + "flavors": [], + } + + output = serializer.serialize(fixture, 'index') + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'flavors_index') + flavor_elems = root.findall('{0}flavor'.format(NS)) + self.assertEqual(len(flavor_elems), 0) diff --git a/nova/tests/api/openstack/v2/test_image_metadata.py b/nova/tests/api/openstack/v2/test_image_metadata.py new file mode 100644 index 000000000..a4f2f155d --- /dev/null +++ b/nova/tests/api/openstack/v2/test_image_metadata.py @@ -0,0 +1,200 @@ +# 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. + +import json +import webob + +from nova.api.openstack.v2 import image_metadata +from nova import flags +from nova import test +from nova.tests.api.openstack import fakes + + +FLAGS = flags.FLAGS + + +class ImageMetaDataTest(test.TestCase): + + def setUp(self): + super(ImageMetaDataTest, self).setUp() + fakes.stub_out_glance(self.stubs) + self.controller = image_metadata.Controller() + + def test_index(self): + req = fakes.HTTPRequest.blank('/v1.1/123/images/123/metadata') + res_dict = self.controller.index(req, '123') + expected = {'metadata': {'key1': 'value1'}} + self.assertEqual(res_dict, expected) + + def test_show(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/key1') + res_dict = self.controller.show(req, '123', 'key1') + self.assertTrue('meta' in res_dict) + self.assertEqual(len(res_dict['meta']), 1) + self.assertEqual('value1', res_dict['meta']['key1']) + + def test_show_not_found(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/key9') + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.show, req, '123', 'key9') + + def test_show_image_not_found(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/images/100/metadata/key1') + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.show, req, '100', 'key9') + + def test_create(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata') + req.method = 'POST' + body = {"metadata": {"key7": "value7"}} + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res = self.controller.create(req, '123', body) + + expected_output = {'metadata': {'key1': 'value1', 'key7': 'value7'}} + self.assertEqual(expected_output, res) + + def test_create_image_not_found(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/images/100/metadata') + req.method = 'POST' + body = {"metadata": {"key7": "value7"}} + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.create, req, '100', body) + + def test_update_all(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata') + req.method = 'PUT' + body = {"metadata": {"key9": "value9"}} + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res = self.controller.update_all(req, '123', body) + + expected_output = {'metadata': {'key9': 'value9'}} + self.assertEqual(expected_output, res) + + def test_update_all_image_not_found(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/images/100/metadata') + req.method = 'PUT' + body = {"metadata": {"key9": "value9"}} + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.update_all, req, '100', body) + + def test_update_item(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/key1') + req.method = 'PUT' + body = {"meta": {"key1": "zz"}} + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res = self.controller.update(req, '123', 'key1', body) + + expected_output = {'meta': {'key1': 'zz'}} + self.assertEqual(res, expected_output) + + def test_update_item_image_not_found(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/images/100/metadata/key1') + req.method = 'PUT' + body = {"meta": {"key1": "zz"}} + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.update, req, '100', 'key1', body) + + def test_update_item_bad_body(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/key1') + req.method = 'PUT' + body = {"key1": "zz"} + req.body = '' + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.update, req, '123', 'key1', body) + + def test_update_item_too_many_keys(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/key1') + req.method = 'PUT' + overload = {} + for num in range(FLAGS.quota_metadata_items + 1): + overload['key%s' % num] = 'value%s' % num + body = {'meta': overload} + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.update, req, '123', 'key1', body) + + def test_update_item_body_uri_mismatch(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/bad') + req.method = 'PUT' + body = {"meta": {"key1": "value1"}} + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.update, req, '123', 'bad', body) + + def test_delete(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/key1') + req.method = 'DELETE' + res = self.controller.delete(req, '123', 'key1') + + self.assertEqual(None, res) + + def test_delete_not_found(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/blah') + req.method = 'DELETE' + + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.delete, req, '123', 'blah') + + def test_delete_image_not_found(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/images/100/metadata/key1') + req.method = 'DELETE' + + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.delete, req, '100', 'key1') + + def test_too_many_metadata_items_on_create(self): + data = {"metadata": {}} + for num in range(FLAGS.quota_metadata_items + 1): + data['metadata']['key%i' % num] = "blah" + req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata') + req.method = 'POST' + req.body = json.dumps(data) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPRequestEntityTooLarge, + self.controller.create, req, '123', data) + self.assertRaises(webob.exc.HTTPRequestEntityTooLarge, + self.controller.create, req, '123', data) + + def test_too_many_metadata_items_on_put(self): + FLAGS.quota_metadata_items = 1 + req = fakes.HTTPRequest.blank('/v1.1/fake/images/123/metadata/blah') + req.method = 'PUT' + body = {"meta": {"blah": "blah"}} + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPRequestEntityTooLarge, + self.controller.update, req, '123', 'blah', body) diff --git a/nova/tests/api/openstack/v2/test_images.py b/nova/tests/api/openstack/v2/test_images.py new file mode 100644 index 000000000..b02b6ff2d --- /dev/null +++ b/nova/tests/api/openstack/v2/test_images.py @@ -0,0 +1,1646 @@ +# 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. + +""" +Tests of the new image services, both as a service layer, +and as a WSGI layer +""" + +import urlparse + +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 import xmlutil +from nova import test +from nova import utils +from nova.tests.api.openstack import fakes + + +NS = "{http://docs.openstack.org/compute/api/v1.1}" +ATOMNS = "{http://www.w3.org/2005/Atom}" +NOW_API_FORMAT = "2010-10-11T10:30:22Z" + + +class ImagesControllerTest(test.TestCase): + """ + Test of the OpenStack API /images application controller w/Glance. + """ + + def setUp(self): + """Run before each test.""" + super(ImagesControllerTest, self).setUp() + self.maxDiff = None + self.stubs = stubout.StubOutForTesting() + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + fakes.stub_out_key_pair_funcs(self.stubs) + fakes.stub_out_compute_api_snapshot(self.stubs) + fakes.stub_out_compute_api_backup(self.stubs) + fakes.stub_out_glance(self.stubs) + + self.controller = images.Controller() + + def tearDown(self): + """Run after each test.""" + self.stubs.UnsetAll() + super(ImagesControllerTest, self).tearDown() + + def test_get_image(self): + fake_req = fakes.HTTPRequest.blank('/v1.1/fake/images/123') + actual_image = self.controller.show(fake_req, '124') + + href = "http://localhost/v1.1/fake/images/124" + bookmark = "http://localhost/fake/images/124" + alternate = "%s/fake/images/124" % utils.generate_glance_url() + server_uuid = "aa640691-d1a7-4a67-9d3c-d35ee6b3cc74" + server_href = "http://localhost/v1.1/servers/" + server_uuid + server_bookmark = "http://localhost/servers/" + server_uuid + + expected_image = { + "image": { + "id": "124", + "name": "queued snapshot", + "updated": NOW_API_FORMAT, + "created": NOW_API_FORMAT, + "status": "SAVING", + "progress": 25, + "minDisk": 0, + "minRam": 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "metadata": { + "instance_ref": server_href, + "user_id": "fake", + }, + "links": [{ + "rel": "self", + "href": href, + }, + { + "rel": "bookmark", + "href": bookmark, + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": alternate + }], + }, + } + + self.assertDictMatch(expected_image, actual_image) + + def test_get_image_404(self): + fake_req = fakes.HTTPRequest.blank('/v1.1/fake/images/unknown') + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.show, fake_req, 'unknown') + + def test_get_image_index(self): + fake_req = fakes.HTTPRequest.blank('/v1.1/fake/images') + response_list = self.controller.index(fake_req)['images'] + + expected_images = [ + { + "id": "123", + "name": "public image", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/images/123", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/123", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/123" % + utils.generate_glance_url() + }, + ], + }, + { + "id": "124", + "name": "queued snapshot", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/images/124", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/124", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/124" % + utils.generate_glance_url() + }, + ], + }, + { + "id": "125", + "name": "saving snapshot", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/images/125", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/125", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/125" % + utils.generate_glance_url() + }, + ], + }, + { + "id": "126", + "name": "active snapshot", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/images/126", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/126", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/126" % + utils.generate_glance_url() + }, + ], + }, + { + "id": "127", + "name": "killed snapshot", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/images/127", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/127", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/127" % + utils.generate_glance_url() + }, + ], + }, + { + "id": "128", + "name": "deleted snapshot", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/images/128", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/128", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/128" % + utils.generate_glance_url() + }, + ], + }, + { + "id": "129", + "name": "pending_delete snapshot", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/images/129", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/129", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/129" % + utils.generate_glance_url() + }, + ], + }, + { + "id": "130", + "name": None, + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/images/130", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/130", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/130" % + utils.generate_glance_url() + }, + ], + }, + ] + + self.assertDictListMatch(response_list, expected_images) + + def test_get_image_index_with_limit(self): + request = fakes.HTTPRequest.blank('/v1.1/fake/images?limit=3') + response = self.controller.index(request) + response_list = response["images"] + response_links = response["images_links"] + + alternate = "%s/fake/images/%s" + + expected_images = [ + { + "id": "123", + "name": "public image", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/images/123", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/123", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": alternate % (utils.generate_glance_url(), 123), + }, + ], + }, + { + "id": "124", + "name": "queued snapshot", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/images/124", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/124", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": alternate % (utils.generate_glance_url(), 124), + }, + ], + }, + { + "id": "125", + "name": "saving snapshot", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/images/125", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/125", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": alternate % (utils.generate_glance_url(), 125), + }, + ], + }, + ] + + self.assertDictListMatch(response_list, expected_images) + self.assertEqual(response_links[0]['rel'], 'next') + + href_parts = urlparse.urlparse(response_links[0]['href']) + self.assertEqual('/v1.1/fake/images', href_parts.path) + params = urlparse.parse_qs(href_parts.query) + self.assertDictMatch({'limit': ['3'], 'marker': ['125']}, params) + + def test_get_image_index_with_limit_and_extra_params(self): + request = fakes.HTTPRequest.blank('/v1.1/fake/images?limit=3&extra=bo') + response = self.controller.index(request) + response_links = response["images_links"] + + self.assertEqual(response_links[0]['rel'], 'next') + + href_parts = urlparse.urlparse(response_links[0]['href']) + self.assertEqual('/v1.1/fake/images', href_parts.path) + params = urlparse.parse_qs(href_parts.query) + self.assertDictMatch( + {'limit': ['3'], 'marker': ['125'], 'extra': ['bo']}, + params) + + def test_get_image_index_with_big_limit(self): + """ + Make sure we don't get images_links if limit is set + and the number of images returned is < limit + """ + request = fakes.HTTPRequest.blank('/v1.1/fake/images?limit=30') + response = self.controller.index(request) + + self.assertEqual(response.keys(), ['images']) + self.assertEqual(len(response['images']), 8) + + def test_get_image_details(self): + request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail') + response = self.controller.detail(request) + response_list = response["images"] + + server_uuid = "aa640691-d1a7-4a67-9d3c-d35ee6b3cc74" + server_href = "http://localhost/v1.1/servers/" + server_uuid + server_bookmark = "http://localhost/servers/" + server_uuid + alternate = "%s/fake/images/%s" + + expected = [{ + 'id': '123', + 'name': 'public image', + 'metadata': {'key1': 'value1'}, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'ACTIVE', + 'progress': 100, + 'minDisk': 10, + 'minRam': 128, + "links": [{ + "rel": "self", + "href": "http://localhost/v1.1/fake/images/123", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/123", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": alternate % (utils.generate_glance_url(), 123), + }], + }, + { + 'id': '124', + 'name': 'queued snapshot', + 'metadata': { + u'instance_ref': server_href, + u'user_id': u'fake', + }, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'SAVING', + 'progress': 25, + 'minDisk': 0, + 'minRam': 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "links": [{ + "rel": "self", + "href": "http://localhost/v1.1/fake/images/124", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/124", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": alternate % (utils.generate_glance_url(), 124), + }], + }, + { + 'id': '125', + 'name': 'saving snapshot', + 'metadata': { + u'instance_ref': server_href, + u'user_id': u'fake', + }, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'SAVING', + 'progress': 50, + 'minDisk': 0, + 'minRam': 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "links": [{ + "rel": "self", + "href": "http://localhost/v1.1/fake/images/125", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/125", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/125" % utils.generate_glance_url() + }], + }, + { + 'id': '126', + 'name': 'active snapshot', + 'metadata': { + u'instance_ref': server_href, + u'user_id': u'fake', + }, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'ACTIVE', + 'progress': 100, + 'minDisk': 0, + 'minRam': 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "links": [{ + "rel": "self", + "href": "http://localhost/v1.1/fake/images/126", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/126", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/126" % utils.generate_glance_url() + }], + }, + { + 'id': '127', + 'name': 'killed snapshot', + 'metadata': { + u'instance_ref': server_href, + u'user_id': u'fake', + }, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'ERROR', + 'progress': 0, + 'minDisk': 0, + 'minRam': 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "links": [{ + "rel": "self", + "href": "http://localhost/v1.1/fake/images/127", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/127", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/127" % utils.generate_glance_url() + }], + }, + { + 'id': '128', + 'name': 'deleted snapshot', + 'metadata': { + u'instance_ref': server_href, + u'user_id': u'fake', + }, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'DELETED', + 'progress': 0, + 'minDisk': 0, + 'minRam': 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "links": [{ + "rel": "self", + "href": "http://localhost/v1.1/fake/images/128", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/128", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/128" % utils.generate_glance_url() + }], + }, + { + 'id': '129', + 'name': 'pending_delete snapshot', + 'metadata': { + u'instance_ref': server_href, + u'user_id': u'fake', + }, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'DELETED', + 'progress': 0, + 'minDisk': 0, + 'minRam': 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "links": [{ + "rel": "self", + "href": "http://localhost/v1.1/fake/images/129", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/129", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/129" % utils.generate_glance_url() + }], + }, + { + 'id': '130', + 'name': None, + 'metadata': {}, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'ACTIVE', + 'progress': 100, + 'minDisk': 0, + 'minRam': 0, + "links": [{ + "rel": "self", + "href": "http://localhost/v1.1/fake/images/130", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/130", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": "%s/fake/images/130" % utils.generate_glance_url() + }], + }, + ] + + self.assertDictListMatch(expected, response_list) + + def test_get_image_details_with_limit(self): + request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail?limit=2') + response = self.controller.detail(request) + response_list = response["images"] + response_links = response["images_links"] + + server_uuid = "aa640691-d1a7-4a67-9d3c-d35ee6b3cc74" + server_href = "http://localhost/v1.1/servers/" + server_uuid + server_bookmark = "http://localhost/servers/" + server_uuid + alternate = "%s/fake/images/%s" + + expected = [{ + 'id': '123', + 'name': 'public image', + 'metadata': {'key1': 'value1'}, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'ACTIVE', + 'minDisk': 10, + 'progress': 100, + 'minRam': 128, + "links": [{ + "rel": "self", + "href": "http://localhost/v1.1/fake/images/123", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/123", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": alternate % (utils.generate_glance_url(), 123), + }], + }, + { + 'id': '124', + 'name': 'queued snapshot', + 'metadata': { + u'instance_ref': server_href, + u'user_id': u'fake', + }, + 'updated': NOW_API_FORMAT, + 'created': NOW_API_FORMAT, + 'status': 'SAVING', + 'minDisk': 0, + 'progress': 25, + 'minRam': 0, + 'server': { + 'id': server_uuid, + "links": [{ + "rel": "self", + "href": server_href, + }, + { + "rel": "bookmark", + "href": server_bookmark, + }], + }, + "links": [{ + "rel": "self", + "href": "http://localhost/v1.1/fake/images/124", + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/images/124", + }, + { + "rel": "alternate", + "type": "application/vnd.openstack.image", + "href": alternate % (utils.generate_glance_url(), 124), + }], + }] + + self.assertDictListMatch(expected, response_list) + + href_parts = urlparse.urlparse(response_links[0]['href']) + self.assertEqual('/v1.1/fake/images', href_parts.path) + params = urlparse.parse_qs(href_parts.query) + + self.assertDictMatch({'limit': ['2'], 'marker': ['124']}, params) + + def test_image_filter_with_name(self): + image_service = self.mox.CreateMockAnything() + filters = {'name': 'testname'} + request = fakes.HTTPRequest.blank('/v1.1/images?name=testname') + context = request.environ['nova.context'] + image_service.index(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.index(request) + self.mox.VerifyAll() + + def test_image_filter_with_min_ram(self): + image_service = self.mox.CreateMockAnything() + filters = {'min_ram': '0'} + request = fakes.HTTPRequest.blank('/v1.1/images?minRam=0') + context = request.environ['nova.context'] + image_service.index(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.index(request) + self.mox.VerifyAll() + + def test_image_filter_with_min_disk(self): + image_service = self.mox.CreateMockAnything() + filters = {'min_disk': '7'} + request = fakes.HTTPRequest.blank('/v1.1/images?minDisk=7') + context = request.environ['nova.context'] + image_service.index(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.index(request) + self.mox.VerifyAll() + + def test_image_filter_with_status(self): + image_service = self.mox.CreateMockAnything() + filters = {'status': 'ACTIVE'} + request = fakes.HTTPRequest.blank('/v1.1/images?status=ACTIVE') + context = request.environ['nova.context'] + image_service.index(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.index(request) + self.mox.VerifyAll() + + def test_image_filter_with_property(self): + image_service = self.mox.CreateMockAnything() + filters = {'property-test': '3'} + request = fakes.HTTPRequest.blank('/v1.1/images?property-test=3') + context = request.environ['nova.context'] + image_service.index(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.index(request) + self.mox.VerifyAll() + + def test_image_filter_server(self): + image_service = self.mox.CreateMockAnything() + uuid = 'fa95aaf5-ab3b-4cd8-88c0-2be7dd051aaf' + ref = 'http://localhost:8774/servers/' + uuid + filters = {'property-instance_ref': ref} + request = fakes.HTTPRequest.blank('/v1.1/images?server=' + ref) + context = request.environ['nova.context'] + image_service.index(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.index(request) + self.mox.VerifyAll() + + def test_image_filter_changes_since(self): + image_service = self.mox.CreateMockAnything() + filters = {'changes-since': '2011-01-24T17:08Z'} + request = fakes.HTTPRequest.blank('/v1.1/images?changes-since=' + '2011-01-24T17:08Z') + context = request.environ['nova.context'] + image_service.index(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.index(request) + self.mox.VerifyAll() + + def test_image_filter_with_type(self): + image_service = self.mox.CreateMockAnything() + filters = {'property-image_type': 'BASE'} + request = fakes.HTTPRequest.blank('/v1.1/images?type=BASE') + context = request.environ['nova.context'] + image_service.index(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.index(request) + self.mox.VerifyAll() + + def test_image_filter_not_supported(self): + image_service = self.mox.CreateMockAnything() + filters = {'status': 'ACTIVE'} + request = fakes.HTTPRequest.blank('/v1.1/images?status=ACTIVE&' + 'UNSUPPORTEDFILTER=testname') + context = request.environ['nova.context'] + image_service.detail(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.detail(request) + self.mox.VerifyAll() + + def test_image_no_filters(self): + image_service = self.mox.CreateMockAnything() + filters = {} + request = fakes.HTTPRequest.blank('/v1.1/images') + context = request.environ['nova.context'] + image_service.index(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.index(request) + self.mox.VerifyAll() + + def test_image_detail_filter_with_name(self): + image_service = self.mox.CreateMockAnything() + filters = {'name': 'testname'} + request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail' + '?name=testname') + context = request.environ['nova.context'] + image_service.detail(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.detail(request) + self.mox.VerifyAll() + + def test_image_detail_filter_with_status(self): + image_service = self.mox.CreateMockAnything() + filters = {'status': 'ACTIVE'} + request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail' + '?status=ACTIVE') + context = request.environ['nova.context'] + image_service.detail(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.detail(request) + self.mox.VerifyAll() + + def test_image_detail_filter_with_property(self): + image_service = self.mox.CreateMockAnything() + filters = {'property-test': '3'} + request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail' + '?property-test=3') + context = request.environ['nova.context'] + image_service.detail(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.detail(request) + self.mox.VerifyAll() + + def test_image_detail_filter_server(self): + image_service = self.mox.CreateMockAnything() + uuid = 'fa95aaf5-ab3b-4cd8-88c0-2be7dd051aaf' + ref = 'http://localhost:8774/servers/' + uuid + url = '/v1.1/fake/images/detail?server=' + ref + filters = {'property-instance_ref': ref} + request = fakes.HTTPRequest.blank(url) + context = request.environ['nova.context'] + image_service.index(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.index(request) + self.mox.VerifyAll() + + def test_image_detail_filter_changes_since(self): + image_service = self.mox.CreateMockAnything() + filters = {'changes-since': '2011-01-24T17:08Z'} + request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail' + '?changes-since=2011-01-24T17:08Z') + context = request.environ['nova.context'] + image_service.index(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.index(request) + self.mox.VerifyAll() + + def test_image_detail_filter_with_type(self): + image_service = self.mox.CreateMockAnything() + filters = {'property-image_type': 'BASE'} + request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail?type=BASE') + context = request.environ['nova.context'] + image_service.index(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.index(request) + self.mox.VerifyAll() + + def test_image_detail_filter_not_supported(self): + image_service = self.mox.CreateMockAnything() + filters = {'status': 'ACTIVE'} + request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail?status=' + 'ACTIVE&UNSUPPORTEDFILTER=testname') + context = request.environ['nova.context'] + image_service.detail(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.detail(request) + self.mox.VerifyAll() + + def test_image_detail_no_filters(self): + image_service = self.mox.CreateMockAnything() + filters = {} + request = fakes.HTTPRequest.blank('/v1.1/fake/images/detail') + context = request.environ['nova.context'] + image_service.detail(context, filters=filters).AndReturn([]) + self.mox.ReplayAll() + controller = images.Controller(image_service=image_service) + controller.detail(request) + self.mox.VerifyAll() + + def test_generate_alternate_link(self): + view = images_view.ViewBuilder() + request = fakes.HTTPRequest.blank('/v1.1/fake/images/1') + generated_url = view._get_alternate_link(request, 1) + actual_url = "%s/fake/images/1" % utils.generate_glance_url() + self.assertEqual(generated_url, actual_url) + + def test_delete_image(self): + request = fakes.HTTPRequest.blank('/v1.1/fake/images/124') + request.method = 'DELETE' + response = self.controller.delete(request, '124') + self.assertEqual(response.status_int, 204) + + def test_delete_image_not_found(self): + request = fakes.HTTPRequest.blank('/v1.1/fake/images/300') + request.method = 'DELETE' + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.delete, request, '300') + + +class ImageXMLSerializationTest(test.TestCase): + + TIMESTAMP = "2010-10-11T10:30:22Z" + SERVER_UUID = 'aa640691-d1a7-4a67-9d3c-d35ee6b3cc74' + SERVER_HREF = 'http://localhost/v1.1/servers/' + SERVER_UUID + SERVER_BOOKMARK = 'http://localhost/servers/' + SERVER_UUID + IMAGE_HREF = 'http://localhost/v1.1/fake/images/%s' + IMAGE_NEXT = 'http://localhost/v1.1/fake/images?limit=%s&marker=%s' + IMAGE_BOOKMARK = 'http://localhost/fake/images/%s' + + def test_xml_declaration(self): + serializer = images.ImageXMLSerializer() + + fixture = { + 'image': { + 'id': 1, + 'name': 'Image1', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'ACTIVE', + 'progress': 80, + 'server': { + 'id': self.SERVER_UUID, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + }, + 'metadata': { + 'key1': 'value1', + }, + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + } + + output = serializer.serialize(fixture, 'show') + has_dec = output.startswith("") + self.assertTrue(has_dec) + + def test_show(self): + serializer = images.ImageXMLSerializer() + + fixture = { + 'image': { + 'id': 1, + 'name': 'Image1', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'ACTIVE', + 'progress': 80, + 'minRam': 10, + 'minDisk': 100, + 'server': { + 'id': self.SERVER_UUID, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + }, + 'metadata': { + 'key1': 'value1', + }, + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + } + + output = serializer.serialize(fixture, 'show') + root = etree.XML(output) + xmlutil.validate_schema(root, 'image') + image_dict = fixture['image'] + + for key in ['name', 'id', 'updated', 'created', 'status', 'progress']: + self.assertEqual(root.get(key), str(image_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = root.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + self.assertEqual(len(metadata_elems), 1) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = image_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) + + server_root = root.find('{0}server'.format(NS)) + self.assertEqual(server_root.get('id'), image_dict['server']['id']) + link_nodes = server_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['server']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_show_zero_metadata(self): + serializer = images.ImageXMLSerializer() + + fixture = { + 'image': { + 'id': 1, + 'name': 'Image1', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'ACTIVE', + 'server': { + 'id': self.SERVER_UUID, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + }, + 'metadata': {}, + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + } + + output = serializer.serialize(fixture, 'show') + root = etree.XML(output) + xmlutil.validate_schema(root, 'image') + image_dict = fixture['image'] + + for key in ['name', 'id', 'updated', 'created', 'status']: + self.assertEqual(root.get(key), str(image_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + meta_nodes = root.findall('{0}meta'.format(ATOMNS)) + self.assertEqual(len(meta_nodes), 0) + + server_root = root.find('{0}server'.format(NS)) + self.assertEqual(server_root.get('id'), image_dict['server']['id']) + link_nodes = server_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['server']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_show_image_no_metadata_key(self): + serializer = images.ImageXMLSerializer() + + fixture = { + 'image': { + 'id': 1, + 'name': 'Image1', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'ACTIVE', + 'server': { + 'id': self.SERVER_UUID, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + }, + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + } + + output = serializer.serialize(fixture, 'show') + root = etree.XML(output) + xmlutil.validate_schema(root, 'image') + image_dict = fixture['image'] + + for key in ['name', 'id', 'updated', 'created', 'status']: + self.assertEqual(root.get(key), str(image_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + meta_nodes = root.findall('{0}meta'.format(ATOMNS)) + self.assertEqual(len(meta_nodes), 0) + + server_root = root.find('{0}server'.format(NS)) + self.assertEqual(server_root.get('id'), image_dict['server']['id']) + link_nodes = server_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['server']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_show_no_server(self): + serializer = images.ImageXMLSerializer() + + fixture = { + 'image': { + 'id': 1, + 'name': 'Image1', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'ACTIVE', + 'metadata': { + 'key1': 'value1', + }, + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + } + + output = serializer.serialize(fixture, 'show') + root = etree.XML(output) + xmlutil.validate_schema(root, 'image') + image_dict = fixture['image'] + + for key in ['name', 'id', 'updated', 'created', 'status']: + self.assertEqual(root.get(key), str(image_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = root.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + self.assertEqual(len(metadata_elems), 1) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = image_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) + + server_root = root.find('{0}server'.format(NS)) + self.assertEqual(server_root, None) + + def test_show_with_min_ram(self): + serializer = images.ImageXMLSerializer() + + fixture = { + 'image': { + 'id': 1, + 'name': 'Image1', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'ACTIVE', + 'progress': 80, + 'minRam': 256, + 'server': { + 'id': self.SERVER_UUID, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + }, + 'metadata': { + 'key1': 'value1', + }, + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + } + + output = serializer.serialize(fixture, 'show') + root = etree.XML(output) + xmlutil.validate_schema(root, 'image') + image_dict = fixture['image'] + + for key in ['name', 'id', 'updated', 'created', 'status', 'progress', + 'minRam']: + self.assertEqual(root.get(key), str(image_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = root.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + self.assertEqual(len(metadata_elems), 1) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = image_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) + + server_root = root.find('{0}server'.format(NS)) + self.assertEqual(server_root.get('id'), image_dict['server']['id']) + link_nodes = server_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['server']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_show_with_min_disk(self): + serializer = images.ImageXMLSerializer() + + fixture = { + 'image': { + 'id': 1, + 'name': 'Image1', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'ACTIVE', + 'progress': 80, + 'minDisk': 5, + 'server': { + 'id': self.SERVER_UUID, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + }, + 'metadata': { + 'key1': 'value1', + }, + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + } + + output = serializer.serialize(fixture, 'show') + root = etree.XML(output) + xmlutil.validate_schema(root, 'image') + image_dict = fixture['image'] + + for key in ['name', 'id', 'updated', 'created', 'status', 'progress', + 'minDisk']: + self.assertEqual(root.get(key), str(image_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = root.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + self.assertEqual(len(metadata_elems), 1) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = image_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) + + server_root = root.find('{0}server'.format(NS)) + self.assertEqual(server_root.get('id'), image_dict['server']['id']) + link_nodes = server_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['server']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_index(self): + serializer = images.ImageXMLSerializer() + + fixture = { + 'images': [ + { + 'id': 1, + 'name': 'Image1', + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + { + 'id': 2, + 'name': 'Image2', + 'links': [ + { + 'href': self.IMAGE_HREF % 2, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 2, + 'rel': 'bookmark', + }, + ], + }, + ] + } + + output = serializer.serialize(fixture, 'index') + root = etree.XML(output) + xmlutil.validate_schema(root, 'images_index') + image_elems = root.findall('{0}image'.format(NS)) + self.assertEqual(len(image_elems), 2) + for i, image_elem in enumerate(image_elems): + image_dict = fixture['images'][i] + + for key in ['name', 'id']: + self.assertEqual(image_elem.get(key), str(image_dict[key])) + + link_nodes = image_elem.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_index_with_links(self): + serializer = images.ImageXMLSerializer() + + fixture = { + 'images': [ + { + 'id': 1, + 'name': 'Image1', + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + { + 'id': 2, + 'name': 'Image2', + 'links': [ + { + 'href': self.IMAGE_HREF % 2, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 2, + 'rel': 'bookmark', + }, + ], + }, + ], + 'images_links': [ + { + 'rel': 'next', + 'href': self.IMAGE_NEXT % (2, 2), + } + ], + } + + output = serializer.serialize(fixture, 'index') + root = etree.XML(output) + xmlutil.validate_schema(root, 'images_index') + image_elems = root.findall('{0}image'.format(NS)) + self.assertEqual(len(image_elems), 2) + for i, image_elem in enumerate(image_elems): + image_dict = fixture['images'][i] + + for key in ['name', 'id']: + self.assertEqual(image_elem.get(key), str(image_dict[key])) + + link_nodes = image_elem.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + # Check images_links + images_links = root.findall('{0}link'.format(ATOMNS)) + for i, link in enumerate(fixture['images_links']): + for key, value in link.items(): + self.assertEqual(images_links[i].get(key), value) + + def test_index_zero_images(self): + serializer = images.ImageXMLSerializer() + + fixtures = { + 'images': [], + } + + output = serializer.serialize(fixtures, 'index') + root = etree.XML(output) + xmlutil.validate_schema(root, 'images_index') + image_elems = root.findall('{0}image'.format(NS)) + self.assertEqual(len(image_elems), 0) + + def test_detail(self): + serializer = images.ImageXMLSerializer() + + fixture = { + 'images': [ + { + 'id': 1, + 'name': 'Image1', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'ACTIVE', + 'server': { + 'id': self.SERVER_UUID, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + }, + 'links': [ + { + 'href': self.IMAGE_HREF % 1, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 1, + 'rel': 'bookmark', + }, + ], + }, + { + 'id': '2', + 'name': 'Image2', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + 'status': 'SAVING', + 'progress': 80, + 'metadata': { + 'key1': 'value1', + }, + 'links': [ + { + 'href': self.IMAGE_HREF % 2, + 'rel': 'self', + }, + { + 'href': self.IMAGE_BOOKMARK % 2, + 'rel': 'bookmark', + }, + ], + }, + ] + } + + output = serializer.serialize(fixture, 'detail') + root = etree.XML(output) + xmlutil.validate_schema(root, 'images') + image_elems = root.findall('{0}image'.format(NS)) + self.assertEqual(len(image_elems), 2) + for i, image_elem in enumerate(image_elems): + image_dict = fixture['images'][i] + + for key in ['name', 'id', 'updated', 'created', 'status']: + self.assertEqual(image_elem.get(key), str(image_dict[key])) + + link_nodes = image_elem.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(image_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) diff --git a/nova/tests/api/openstack/v2/test_limits.py b/nova/tests/api/openstack/v2/test_limits.py new file mode 100644 index 000000000..e167f450c --- /dev/null +++ b/nova/tests/api/openstack/v2/test_limits.py @@ -0,0 +1,940 @@ +# 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. + +""" +Tests dealing with HTTP rate-limiting. +""" + +import httplib +import json +import StringIO +import time +import unittest +from xml.dom import minidom + +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 import wsgi +from nova.api.openstack import xmlutil +import nova.context +from nova import test + + +TEST_LIMITS = [ + limits.Limit("GET", "/delayed", "^/delayed", 1, limits.PER_MINUTE), + limits.Limit("POST", "*", ".*", 7, limits.PER_MINUTE), + limits.Limit("POST", "/servers", "^/servers", 3, limits.PER_MINUTE), + limits.Limit("PUT", "*", "", 10, limits.PER_MINUTE), + limits.Limit("PUT", "/servers", "^/servers", 5, limits.PER_MINUTE), +] +NS = { + 'atom': 'http://www.w3.org/2005/Atom', + 'ns': 'http://docs.openstack.org/compute/api/v1.1' +} + + +class BaseLimitTestSuite(unittest.TestCase): + """Base test suite which provides relevant stubs and time abstraction.""" + + def setUp(self): + """Run before each test.""" + self.time = 0.0 + self.stubs = stubout.StubOutForTesting() + self.stubs.Set(limits.Limit, "_get_time", self._get_time) + self.absolute_limits = {} + + def stub_get_project_quotas(context, project_id): + return self.absolute_limits + + self.stubs.Set(nova.quota, "get_project_quotas", + stub_get_project_quotas) + + def tearDown(self): + """Run after each test.""" + self.stubs.UnsetAll() + + def _get_time(self): + """Return the "time" according to this test suite.""" + return self.time + + +class LimitsControllerTest(BaseLimitTestSuite): + """ + Tests for `limits.LimitsController` class. + """ + + def setUp(self): + """Run before each test.""" + BaseLimitTestSuite.setUp(self) + self.controller = wsgi.LazySerializationMiddleware( + limits.create_resource()) + self.maxDiff = None + + def _get_index_request(self, accept_header="application/json"): + """Helper to set routing arguments.""" + request = webob.Request.blank("/") + request.accept = accept_header + request.environ["wsgiorg.routing_args"] = (None, { + "action": "index", + "controller": "", + }) + context = nova.context.RequestContext('testuser', 'testproject') + request.environ["nova.context"] = context + return request + + def _populate_limits(self, request): + """Put limit info into a request.""" + _limits = [ + limits.Limit("GET", "*", ".*", 10, 60).display(), + limits.Limit("POST", "*", ".*", 5, 60 * 60).display(), + limits.Limit("GET", "changes-since*", "changes-since", + 5, 60).display(), + ] + request.environ["nova.limits"] = _limits + return request + + def test_empty_index_json(self): + """Test getting empty limit details in JSON.""" + request = self._get_index_request() + response = request.get_response(self.controller) + expected = { + "limits": { + "rate": [], + "absolute": {}, + }, + } + body = json.loads(response.body) + self.assertEqual(expected, body) + + def test_index_json(self): + """Test getting limit details in JSON.""" + request = self._get_index_request() + request = self._populate_limits(request) + self.absolute_limits = { + 'ram': 512, + 'instances': 5, + 'cores': 21, + } + response = request.get_response(self.controller) + expected = { + "limits": { + "rate": [ + { + "regex": ".*", + "uri": "*", + "limit": [ + { + "verb": "GET", + "next-available": "1970-01-01T00:00:00Z", + "unit": "MINUTE", + "value": 10, + "remaining": 10, + }, + { + "verb": "POST", + "next-available": "1970-01-01T00:00:00Z", + "unit": "HOUR", + "value": 5, + "remaining": 5, + }, + ], + }, + { + "regex": "changes-since", + "uri": "changes-since*", + "limit": [ + { + "verb": "GET", + "next-available": "1970-01-01T00:00:00Z", + "unit": "MINUTE", + "value": 5, + "remaining": 5, + }, + ], + }, + + ], + "absolute": { + "maxTotalRAMSize": 512, + "maxTotalInstances": 5, + "maxTotalCores": 21, + }, + }, + } + body = json.loads(response.body) + self.assertEqual(expected, body) + + def _populate_limits_diff_regex(self, request): + """Put limit info into a request.""" + _limits = [ + limits.Limit("GET", "*", ".*", 10, 60).display(), + limits.Limit("GET", "*", "*.*", 10, 60).display(), + ] + request.environ["nova.limits"] = _limits + return request + + def test_index_diff_regex(self): + """Test getting limit details in JSON.""" + request = self._get_index_request() + request = self._populate_limits_diff_regex(request) + response = request.get_response(self.controller) + expected = { + "limits": { + "rate": [ + { + "regex": ".*", + "uri": "*", + "limit": [ + { + "verb": "GET", + "next-available": "1970-01-01T00:00:00Z", + "unit": "MINUTE", + "value": 10, + "remaining": 10, + }, + ], + }, + { + "regex": "*.*", + "uri": "*", + "limit": [ + { + "verb": "GET", + "next-available": "1970-01-01T00:00:00Z", + "unit": "MINUTE", + "value": 10, + "remaining": 10, + }, + ], + }, + + ], + "absolute": {}, + }, + } + body = json.loads(response.body) + self.assertEqual(expected, body) + + def _test_index_absolute_limits_json(self, expected): + request = self._get_index_request() + response = request.get_response(self.controller) + body = json.loads(response.body) + self.assertEqual(expected, body['limits']['absolute']) + + def test_index_ignores_extra_absolute_limits_json(self): + self.absolute_limits = {'unknown_limit': 9001} + self._test_index_absolute_limits_json({}) + + def test_index_absolute_ram_json(self): + self.absolute_limits = {'ram': 1024} + self._test_index_absolute_limits_json({'maxTotalRAMSize': 1024}) + + def test_index_absolute_cores_json(self): + self.absolute_limits = {'cores': 17} + self._test_index_absolute_limits_json({'maxTotalCores': 17}) + + def test_index_absolute_instances_json(self): + self.absolute_limits = {'instances': 19} + self._test_index_absolute_limits_json({'maxTotalInstances': 19}) + + def test_index_absolute_metadata_json(self): + # NOTE: both server metadata and image metadata are overloaded + # into metadata_items + self.absolute_limits = {'metadata_items': 23} + expected = { + 'maxServerMeta': 23, + 'maxImageMeta': 23, + } + self._test_index_absolute_limits_json(expected) + + def test_index_absolute_injected_files(self): + self.absolute_limits = { + 'injected_files': 17, + 'injected_file_content_bytes': 86753, + } + expected = { + 'maxPersonality': 17, + 'maxPersonalitySize': 86753, + } + self._test_index_absolute_limits_json(expected) + + +class TestLimiter(limits.Limiter): + pass + + +class LimitMiddlewareTest(BaseLimitTestSuite): + """ + Tests for the `limits.RateLimitingMiddleware` class. + """ + + @webob.dec.wsgify + def _empty_app(self, request): + """Do-nothing WSGI app.""" + pass + + def setUp(self): + """Prepare middleware for use through fake WSGI app.""" + BaseLimitTestSuite.setUp(self) + _limits = '(GET, *, .*, 1, MINUTE)' + self.app = limits.RateLimitingMiddleware(self._empty_app, _limits, + "%s.TestLimiter" % + self.__class__.__module__) + + def test_limit_class(self): + """Test that middleware selected correct limiter class.""" + assert isinstance(self.app._limiter, TestLimiter) + + def test_good_request(self): + """Test successful GET request through middleware.""" + request = webob.Request.blank("/") + response = request.get_response(self.app) + self.assertEqual(200, response.status_int) + + def test_limited_request_json(self): + """Test a rate-limited (413) GET request through middleware.""" + request = webob.Request.blank("/") + response = request.get_response(self.app) + self.assertEqual(200, response.status_int) + + request = webob.Request.blank("/") + response = request.get_response(self.app) + self.assertEqual(response.status_int, 413) + + body = json.loads(response.body) + expected = "Only 1 GET request(s) can be made to * every minute." + value = body["overLimitFault"]["details"].strip() + self.assertEqual(value, expected) + + def test_limited_request_xml(self): + """Test a rate-limited (413) response as XML""" + request = webob.Request.blank("/") + response = request.get_response(self.app) + self.assertEqual(200, response.status_int) + + request = webob.Request.blank("/") + request.accept = "application/xml" + response = request.get_response(self.app) + self.assertEqual(response.status_int, 413) + + root = minidom.parseString(response.body).childNodes[0] + expected = "Only 1 GET request(s) can be made to * every minute." + + details = root.getElementsByTagName("details") + self.assertEqual(details.length, 1) + + value = details.item(0).firstChild.data.strip() + self.assertEqual(value, expected) + + +class LimitTest(BaseLimitTestSuite): + """ + Tests for the `limits.Limit` class. + """ + + def test_GET_no_delay(self): + """Test a limit handles 1 GET per second.""" + limit = limits.Limit("GET", "*", ".*", 1, 1) + delay = limit("GET", "/anything") + self.assertEqual(None, delay) + self.assertEqual(0, limit.next_request) + self.assertEqual(0, limit.last_request) + + def test_GET_delay(self): + """Test two calls to 1 GET per second limit.""" + limit = limits.Limit("GET", "*", ".*", 1, 1) + delay = limit("GET", "/anything") + self.assertEqual(None, delay) + + delay = limit("GET", "/anything") + self.assertEqual(1, delay) + self.assertEqual(1, limit.next_request) + self.assertEqual(0, limit.last_request) + + self.time += 4 + + delay = limit("GET", "/anything") + self.assertEqual(None, delay) + self.assertEqual(4, limit.next_request) + self.assertEqual(4, limit.last_request) + + +class ParseLimitsTest(BaseLimitTestSuite): + """ + Tests for the default limits parser in the in-memory + `limits.Limiter` class. + """ + + def test_invalid(self): + """Test that parse_limits() handles invalid input correctly.""" + self.assertRaises(ValueError, limits.Limiter.parse_limits, + ';;;;;') + + def test_bad_rule(self): + """Test that parse_limits() handles bad rules correctly.""" + self.assertRaises(ValueError, limits.Limiter.parse_limits, + 'GET, *, .*, 20, minute') + + def test_missing_arg(self): + """Test that parse_limits() handles missing args correctly.""" + self.assertRaises(ValueError, limits.Limiter.parse_limits, + '(GET, *, .*, 20)') + + def test_bad_value(self): + """Test that parse_limits() handles bad values correctly.""" + self.assertRaises(ValueError, limits.Limiter.parse_limits, + '(GET, *, .*, foo, minute)') + + def test_bad_unit(self): + """Test that parse_limits() handles bad units correctly.""" + self.assertRaises(ValueError, limits.Limiter.parse_limits, + '(GET, *, .*, 20, lightyears)') + + def test_multiple_rules(self): + """Test that parse_limits() handles multiple rules correctly.""" + try: + l = limits.Limiter.parse_limits('(get, *, .*, 20, minute);' + '(PUT, /foo*, /foo.*, 10, hour);' + '(POST, /bar*, /bar.*, 5, second);' + '(Say, /derp*, /derp.*, 1, day)') + except ValueError, e: + assert False, str(e) + + # Make sure the number of returned limits are correct + self.assertEqual(len(l), 4) + + # Check all the verbs... + expected = ['GET', 'PUT', 'POST', 'SAY'] + self.assertEqual([t.verb for t in l], expected) + + # ...the URIs... + expected = ['*', '/foo*', '/bar*', '/derp*'] + self.assertEqual([t.uri for t in l], expected) + + # ...the regexes... + expected = ['.*', '/foo.*', '/bar.*', '/derp.*'] + self.assertEqual([t.regex for t in l], expected) + + # ...the values... + expected = [20, 10, 5, 1] + self.assertEqual([t.value for t in l], expected) + + # ...and the units... + expected = [limits.PER_MINUTE, limits.PER_HOUR, + limits.PER_SECOND, limits.PER_DAY] + self.assertEqual([t.unit for t in l], expected) + + +class LimiterTest(BaseLimitTestSuite): + """ + Tests for the in-memory `limits.Limiter` class. + """ + + def setUp(self): + """Run before each test.""" + BaseLimitTestSuite.setUp(self) + userlimits = {'user:user3': ''} + self.limiter = limits.Limiter(TEST_LIMITS, **userlimits) + + def _check(self, num, verb, url, username=None): + """Check and yield results from checks.""" + for x in xrange(num): + yield self.limiter.check_for_delay(verb, url, username)[0] + + def _check_sum(self, num, verb, url, username=None): + """Check and sum results from checks.""" + results = self._check(num, verb, url, username) + return sum(item for item in results if item) + + def test_no_delay_GET(self): + """ + Simple test to ensure no delay on a single call for a limit verb we + didn"t set. + """ + delay = self.limiter.check_for_delay("GET", "/anything") + self.assertEqual(delay, (None, None)) + + def test_no_delay_PUT(self): + """ + Simple test to ensure no delay on a single call for a known limit. + """ + delay = self.limiter.check_for_delay("PUT", "/anything") + self.assertEqual(delay, (None, None)) + + def test_delay_PUT(self): + """ + Ensure the 11th PUT will result in a delay of 6.0 seconds until + the next request will be granced. + """ + expected = [None] * 10 + [6.0] + results = list(self._check(11, "PUT", "/anything")) + + self.assertEqual(expected, results) + + def test_delay_POST(self): + """ + Ensure the 8th POST will result in a delay of 6.0 seconds until + the next request will be granced. + """ + expected = [None] * 7 + results = list(self._check(7, "POST", "/anything")) + self.assertEqual(expected, results) + + expected = 60.0 / 7.0 + results = self._check_sum(1, "POST", "/anything") + self.failUnlessAlmostEqual(expected, results, 8) + + def test_delay_GET(self): + """ + Ensure the 11th GET will result in NO delay. + """ + expected = [None] * 11 + results = list(self._check(11, "GET", "/anything")) + + self.assertEqual(expected, results) + + def test_delay_PUT_servers(self): + """ + Ensure PUT on /servers limits at 5 requests, and PUT elsewhere is still + OK after 5 requests...but then after 11 total requests, PUT limiting + kicks in. + """ + # First 6 requests on PUT /servers + expected = [None] * 5 + [12.0] + results = list(self._check(6, "PUT", "/servers")) + self.assertEqual(expected, results) + + # Next 5 request on PUT /anything + expected = [None] * 4 + [6.0] + results = list(self._check(5, "PUT", "/anything")) + self.assertEqual(expected, results) + + def test_delay_PUT_wait(self): + """ + Ensure after hitting the limit and then waiting for the correct + amount of time, the limit will be lifted. + """ + expected = [None] * 10 + [6.0] + results = list(self._check(11, "PUT", "/anything")) + self.assertEqual(expected, results) + + # Advance time + self.time += 6.0 + + expected = [None, 6.0] + results = list(self._check(2, "PUT", "/anything")) + self.assertEqual(expected, results) + + def test_multiple_delays(self): + """ + Ensure multiple requests still get a delay. + """ + expected = [None] * 10 + [6.0] * 10 + results = list(self._check(20, "PUT", "/anything")) + self.assertEqual(expected, results) + + self.time += 1.0 + + expected = [5.0] * 10 + results = list(self._check(10, "PUT", "/anything")) + self.assertEqual(expected, results) + + def test_user_limit(self): + """ + Test user-specific limits. + """ + self.assertEqual(self.limiter.levels['user3'], []) + + def test_multiple_users(self): + """ + Tests involving multiple users. + """ + # User1 + expected = [None] * 10 + [6.0] * 10 + results = list(self._check(20, "PUT", "/anything", "user1")) + self.assertEqual(expected, results) + + # User2 + expected = [None] * 10 + [6.0] * 5 + results = list(self._check(15, "PUT", "/anything", "user2")) + self.assertEqual(expected, results) + + # User3 + expected = [None] * 20 + results = list(self._check(20, "PUT", "/anything", "user3")) + self.assertEqual(expected, results) + + self.time += 1.0 + + # User1 again + expected = [5.0] * 10 + results = list(self._check(10, "PUT", "/anything", "user1")) + self.assertEqual(expected, results) + + self.time += 1.0 + + # User1 again + expected = [4.0] * 5 + results = list(self._check(5, "PUT", "/anything", "user2")) + self.assertEqual(expected, results) + + +class WsgiLimiterTest(BaseLimitTestSuite): + """ + Tests for `limits.WsgiLimiter` class. + """ + + def setUp(self): + """Run before each test.""" + BaseLimitTestSuite.setUp(self) + self.app = limits.WsgiLimiter(TEST_LIMITS) + + def _request_data(self, verb, path): + """Get data decribing a limit request verb/path.""" + return json.dumps({"verb": verb, "path": path}) + + def _request(self, verb, url, username=None): + """Make sure that POSTing to the given url causes the given username + to perform the given action. Make the internal rate limiter return + delay and make sure that the WSGI app returns the correct response. + """ + if username: + request = webob.Request.blank("/%s" % username) + else: + request = webob.Request.blank("/") + + request.method = "POST" + request.body = self._request_data(verb, url) + response = request.get_response(self.app) + + if "X-Wait-Seconds" in response.headers: + self.assertEqual(response.status_int, 403) + return response.headers["X-Wait-Seconds"] + + self.assertEqual(response.status_int, 204) + + def test_invalid_methods(self): + """Only POSTs should work.""" + requests = [] + for method in ["GET", "PUT", "DELETE", "HEAD", "OPTIONS"]: + request = webob.Request.blank("/", method=method) + response = request.get_response(self.app) + self.assertEqual(response.status_int, 405) + + def test_good_url(self): + delay = self._request("GET", "/something") + self.assertEqual(delay, None) + + def test_escaping(self): + delay = self._request("GET", "/something/jump%20up") + self.assertEqual(delay, None) + + def test_response_to_delays(self): + delay = self._request("GET", "/delayed") + self.assertEqual(delay, None) + + delay = self._request("GET", "/delayed") + self.assertEqual(delay, '60.00') + + def test_response_to_delays_usernames(self): + delay = self._request("GET", "/delayed", "user1") + self.assertEqual(delay, None) + + delay = self._request("GET", "/delayed", "user2") + self.assertEqual(delay, None) + + delay = self._request("GET", "/delayed", "user1") + self.assertEqual(delay, '60.00') + + delay = self._request("GET", "/delayed", "user2") + self.assertEqual(delay, '60.00') + + +class FakeHttplibSocket(object): + """ + Fake `httplib.HTTPResponse` replacement. + """ + + def __init__(self, response_string): + """Initialize new `FakeHttplibSocket`.""" + self._buffer = StringIO.StringIO(response_string) + + def makefile(self, _mode, _other): + """Returns the socket's internal buffer.""" + return self._buffer + + +class FakeHttplibConnection(object): + """ + Fake `httplib.HTTPConnection`. + """ + + def __init__(self, app, host): + """ + Initialize `FakeHttplibConnection`. + """ + self.app = app + self.host = host + + def request(self, method, path, body="", headers=None): + """ + Requests made via this connection actually get translated and routed + into our WSGI app, we then wait for the response and turn it back into + an `httplib.HTTPResponse`. + """ + if not headers: + headers = {} + + req = webob.Request.blank(path) + req.method = method + req.headers = headers + req.host = self.host + req.body = body + + resp = str(req.get_response(self.app)) + resp = "HTTP/1.0 %s" % resp + sock = FakeHttplibSocket(resp) + self.http_response = httplib.HTTPResponse(sock) + self.http_response.begin() + + def getresponse(self): + """Return our generated response from the request.""" + return self.http_response + + +def wire_HTTPConnection_to_WSGI(host, app): + """Monkeypatches HTTPConnection so that if you try to connect to host, you + are instead routed straight to the given WSGI app. + + After calling this method, when any code calls + + httplib.HTTPConnection(host) + + the connection object will be a fake. Its requests will be sent directly + to the given WSGI app rather than through a socket. + + Code connecting to hosts other than host will not be affected. + + This method may be called multiple times to map different hosts to + different apps. + """ + class HTTPConnectionDecorator(object): + """Wraps the real HTTPConnection class so that when you instantiate + the class you might instead get a fake instance.""" + + def __init__(self, wrapped): + self.wrapped = wrapped + + def __call__(self, connection_host, *args, **kwargs): + if connection_host == host: + return FakeHttplibConnection(app, host) + else: + return self.wrapped(connection_host, *args, **kwargs) + + httplib.HTTPConnection = HTTPConnectionDecorator(httplib.HTTPConnection) + + +class WsgiLimiterProxyTest(BaseLimitTestSuite): + """ + Tests for the `limits.WsgiLimiterProxy` class. + """ + + def setUp(self): + """ + Do some nifty HTTP/WSGI magic which allows for WSGI to be called + directly by something like the `httplib` library. + """ + BaseLimitTestSuite.setUp(self) + self.app = limits.WsgiLimiter(TEST_LIMITS) + wire_HTTPConnection_to_WSGI("169.254.0.1:80", self.app) + self.proxy = limits.WsgiLimiterProxy("169.254.0.1:80") + + def test_200(self): + """Successful request test.""" + delay = self.proxy.check_for_delay("GET", "/anything") + self.assertEqual(delay, (None, None)) + + def test_403(self): + """Forbidden request test.""" + delay = self.proxy.check_for_delay("GET", "/delayed") + self.assertEqual(delay, (None, None)) + + delay, error = self.proxy.check_for_delay("GET", "/delayed") + error = error.strip() + + expected = ("60.00", "403 Forbidden\n\nOnly 1 GET request(s) can be "\ + "made to /delayed every minute.") + + self.assertEqual((delay, error), expected) + + +class LimitsViewBuilderTest(test.TestCase): + + def setUp(self): + self.view_builder = views.limits.ViewBuilder() + self.rate_limits = [{"URI": "*", + "regex": ".*", + "value": 10, + "verb": "POST", + "remaining": 2, + "unit": "MINUTE", + "resetTime": 1311272226}, + {"URI": "*/servers", + "regex": "^/servers", + "value": 50, + "verb": "POST", + "remaining": 10, + "unit": "DAY", + "resetTime": 1311272226}] + self.absolute_limits = {"metadata_items": 1, + "injected_files": 5, + "injected_file_content_bytes": 5} + + def tearDown(self): + pass + + def test_build_limits(self): + expected_limits = {"limits": { + "rate": [{ + "uri": "*", + "regex": ".*", + "limit": [{"value": 10, + "verb": "POST", + "remaining": 2, + "unit": "MINUTE", + "next-available": "2011-07-21T18:17:06Z"}]}, + {"uri": "*/servers", + "regex": "^/servers", + "limit": [{"value": 50, + "verb": "POST", + "remaining": 10, + "unit": "DAY", + "next-available": "2011-07-21T18:17:06Z"}]}], + "absolute": {"maxServerMeta": 1, + "maxImageMeta": 1, + "maxPersonality": 5, + "maxPersonalitySize": 5}}} + + output = self.view_builder.build(self.rate_limits, + self.absolute_limits) + self.assertDictMatch(output, expected_limits) + + def test_build_limits_empty_limits(self): + expected_limits = {"limits": {"rate": [], + "absolute": {}}} + + abs_limits = {} + rate_limits = [] + output = self.view_builder.build(rate_limits, abs_limits) + self.assertDictMatch(output, expected_limits) + + +class LimitsXMLSerializationTest(test.TestCase): + + def setUp(self): + self.maxDiff = None + + def tearDown(self): + pass + + def test_xml_declaration(self): + serializer = limits.LimitsXMLSerializer() + + fixture = {"limits": { + "rate": [], + "absolute": {}}} + + output = serializer.serialize(fixture, 'index') + print output + has_dec = output.startswith("") + self.assertTrue(has_dec) + + def test_index(self): + serializer = limits.LimitsXMLSerializer() + fixture = { + "limits": { + "rate": [{ + "uri": "*", + "regex": ".*", + "limit": [{ + "value": 10, + "verb": "POST", + "remaining": 2, + "unit": "MINUTE", + "next-available": "2011-12-15T22:42:45Z"}]}, + {"uri": "*/servers", + "regex": "^/servers", + "limit": [{ + "value": 50, + "verb": "POST", + "remaining": 10, + "unit": "DAY", + "next-available": "2011-12-15T22:42:45Z"}]}], + "absolute": {"maxServerMeta": 1, + "maxImageMeta": 1, + "maxPersonality": 5, + "maxPersonalitySize": 10240}}} + + output = serializer.serialize(fixture, 'index') + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'limits') + + #verify absolute limits + absolutes = root.xpath('ns:absolute/ns:limit', namespaces=NS) + self.assertEqual(len(absolutes), 4) + for limit in absolutes: + name = limit.get('name') + value = limit.get('value') + self.assertEqual(value, str(fixture['limits']['absolute'][name])) + + #verify rate limits + rates = root.xpath('ns:rates/ns:rate', namespaces=NS) + self.assertEqual(len(rates), 2) + for i, rate in enumerate(rates): + for key in ['uri', 'regex']: + self.assertEqual(rate.get(key), + str(fixture['limits']['rate'][i][key])) + rate_limits = rate.xpath('ns:limit', namespaces=NS) + self.assertEqual(len(rate_limits), 1) + for j, limit in enumerate(rate_limits): + for key in ['verb', 'value', 'remaining', 'unit', + 'next-available']: + self.assertEqual(limit.get(key), + str(fixture['limits']['rate'][i]['limit'][j][key])) + + def test_index_no_limits(self): + serializer = limits.LimitsXMLSerializer() + + fixture = {"limits": { + "rate": [], + "absolute": {}}} + + output = serializer.serialize(fixture, 'index') + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'limits') + + #verify absolute limits + absolutes = root.xpath('ns:absolute/ns:limit', namespaces=NS) + self.assertEqual(len(absolutes), 0) + + #verify rate limits + rates = root.xpath('ns:rates/ns:rate', namespaces=NS) + self.assertEqual(len(rates), 0) diff --git a/nova/tests/api/openstack/v2/test_server_actions.py b/nova/tests/api/openstack/v2/test_server_actions.py new file mode 100644 index 000000000..3adde3b7a --- /dev/null +++ b/nova/tests/api/openstack/v2/test_server_actions.py @@ -0,0 +1,881 @@ +# 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. + +import base64 +import datetime +import json + +import stubout +import webob + +from nova.api.openstack.v2 import servers +from nova.compute import vm_states +from nova.compute import instance_types +from nova import context +import nova.db +from nova import exception +from nova import flags +from nova import test +from nova.tests.api.openstack import common +from nova.tests.api.openstack import fakes +from nova import utils + + +FLAGS = flags.FLAGS +FAKE_UUID = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + + +def return_server_by_id(context, id): + return stub_instance(id) + + +def return_server_by_uuid(context, uuid): + return stub_instance(1, uuid=uuid) + + +def return_server_by_uuid_not_found(context, uuid): + raise exception.NotFound() + + +def instance_update(context, instance_id, kwargs): + return stub_instance(instance_id) + + +def return_server_with_attributes(**kwargs): + def _return_server(context, id): + return stub_instance(id, **kwargs) + return _return_server + + +def return_server_with_state(vm_state, task_state=None): + return return_server_with_attributes(vm_state=vm_state, + task_state=task_state) + + +def return_server_with_uuid_and_state(vm_state, task_state=None): + def _return_server(context, id): + return return_server_with_state(vm_state, task_state) + return _return_server + + +def stub_instance(id, metadata=None, image_ref="10", flavor_id="1", + name=None, vm_state=None, task_state=None, uuid=None): + if metadata is not None: + metadata_items = [{'key':k, 'value':v} for k, v in metadata.items()] + else: + metadata_items = [{'key':'seq', 'value':id}] + + if uuid is None: + uuid = FAKE_UUID + + inst_type = instance_types.get_instance_type_by_flavor_id(int(flavor_id)) + + instance = { + "id": int(id), + "name": str(id), + "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), + "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), + "admin_pass": "", + "user_id": "fake", + "project_id": "fake", + "image_ref": image_ref, + "kernel_id": "", + "ramdisk_id": "", + "launch_index": 0, + "key_name": "", + "key_data": "", + "vm_state": vm_state or vm_states.ACTIVE, + "task_state": task_state, + "memory_mb": 0, + "vcpus": 0, + "local_gb": 0, + "hostname": "", + "host": "", + "instance_type": dict(inst_type), + "user_data": "", + "reservation_id": "", + "mac_address": "", + "scheduled_at": utils.utcnow(), + "launched_at": utils.utcnow(), + "terminated_at": utils.utcnow(), + "availability_zone": "", + "display_name": name or "server%s" % id, + "display_description": "", + "locked": False, + "metadata": metadata_items, + "access_ip_v4": "", + "access_ip_v6": "", + "uuid": uuid, + "virtual_interfaces": [], + "progress": 0, + } + + instance["fixed_ips"] = [{"address": '192.168.0.1', + "network": + {'label': 'public', 'cidr_v6': None}, + "virtual_interface": + {'address': 'aa:aa:aa:aa:aa:aa'}, + "floating_ips": []}] + + return instance + + +class MockSetAdminPassword(object): + def __init__(self): + self.instance_id = None + self.password = None + + def __call__(self, context, instance, password): + self.instance_id = instance['uuid'] + self.password = password + + +class ServerActionsControllerTest(test.TestCase): + + def setUp(self): + self.maxDiff = None + super(ServerActionsControllerTest, self).setUp() + + self.stubs = stubout.StubOutForTesting() + fakes.stub_out_auth(self.stubs) + self.stubs.Set(nova.db, 'instance_get', return_server_by_id) + self.stubs.Set(nova.db, 'instance_get_by_uuid', return_server_by_uuid) + self.stubs.Set(nova.db, 'instance_update', instance_update) + + fakes.stub_out_glance(self.stubs) + fakes.stub_out_nw_api(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + self.snapshot = fakes.stub_out_compute_api_snapshot(self.stubs) + self.backup = fakes.stub_out_compute_api_backup(self.stubs) + service_class = 'nova.image.glance.GlanceImageService' + self.service = utils.import_object(service_class) + self.context = context.RequestContext(1, None) + self.service.delete_all() + self.sent_to_glance = {} + fakes.stub_out_glance_add_image(self.stubs, self.sent_to_glance) + self.flags(allow_instance_snapshots=True) + self.uuid = FAKE_UUID + self.url = '/v1.1/fake/servers/%s/action' % self.uuid + + self.controller = servers.Controller() + + def tearDown(self): + self.stubs.UnsetAll() + super(ServerActionsControllerTest, self).tearDown() + + def test_server_bad_body(self): + body = {} + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_server_unknown_action(self): + body = {'sockTheFox': {'fakekey': '1234'}} + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_server_change_password(self): + mock_method = MockSetAdminPassword() + self.stubs.Set(nova.compute.api.API, 'set_admin_password', mock_method) + body = {'changePassword': {'adminPass': '1234pass'}} + + req = fakes.HTTPRequest.blank(self.url) + self.controller.action(req, FAKE_UUID, body) + + self.assertEqual(mock_method.instance_id, self.uuid) + self.assertEqual(mock_method.password, '1234pass') + + def test_server_change_password_not_a_string(self): + body = {'changePassword': {'adminPass': 1234}} + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_server_change_password_bad_request(self): + body = {'changePassword': {'pass': '12345'}} + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_server_change_password_empty_string(self): + body = {'changePassword': {'adminPass': ''}} + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_server_change_password_none(self): + body = {'changePassword': {'adminPass': None}} + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_reboot_hard(self): + body = dict(reboot=dict(type="HARD")) + req = fakes.HTTPRequest.blank(self.url) + self.controller.action(req, FAKE_UUID, body) + + def test_reboot_soft(self): + body = dict(reboot=dict(type="SOFT")) + req = fakes.HTTPRequest.blank(self.url) + self.controller.action(req, FAKE_UUID, body) + + def test_reboot_incorrect_type(self): + body = dict(reboot=dict(type="NOT_A_TYPE")) + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_reboot_missing_type(self): + body = dict(reboot=dict()) + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_reboot_not_found(self): + self.stubs.Set(nova.db, 'instance_get_by_uuid', + return_server_by_uuid_not_found) + + body = dict(reboot=dict(type="HARD")) + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPNotFound, self.controller.action, + req, str(utils.gen_uuid()), body) + + def test_server_rebuild_accepted_minimum(self): + new_return_server = return_server_with_attributes(image_ref='2') + self.stubs.Set(nova.db, 'instance_get', new_return_server) + + body = { + "rebuild": { + "imageRef": "http://localhost/images/2", + }, + } + + req = fakes.HTTPRequest.blank(self.url) + body = self.controller.action(req, FAKE_UUID, body) + + self.assertEqual(body['server']['image']['id'], '2') + self.assertEqual(len(body['server']['adminPass']), + FLAGS.password_length) + + def test_server_rebuild_rejected_when_building(self): + body = { + "rebuild": { + "imageRef": "http://localhost/images/2", + }, + } + + def fake_rebuild(*args, **kwargs): + raise exception.RebuildRequiresActiveInstance + + self.stubs.Set(nova.compute.api.API, 'rebuild', fake_rebuild) + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPConflict, + self.controller.action, req, FAKE_UUID, body) + + def test_server_rebuild_accepted_with_metadata(self): + metadata = {'new': 'metadata'} + + new_return_server = return_server_with_attributes(metadata=metadata) + self.stubs.Set(nova.db, 'instance_get', new_return_server) + + body = { + "rebuild": { + "imageRef": "http://localhost/images/2", + "metadata": metadata, + }, + } + + req = fakes.HTTPRequest.blank(self.url) + body = self.controller.action(req, FAKE_UUID, body) + + self.assertEqual(body['server']['metadata'], metadata) + + def test_server_rebuild_accepted_with_bad_metadata(self): + body = { + "rebuild": { + "imageRef": "http://localhost/images/2", + "metadata": "stack", + }, + } + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_server_rebuild_bad_entity(self): + body = { + "rebuild": { + "imageId": 2, + }, + } + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_server_rebuild_bad_personality(self): + body = { + "rebuild": { + "imageRef": "http://localhost/images/2", + "personality": [{ + "path": "/path/to/file", + "contents": "INVALID b64", + }] + }, + } + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_server_rebuild_personality(self): + body = { + "rebuild": { + "imageRef": "http://localhost/images/2", + "personality": [{ + "path": "/path/to/file", + "contents": base64.b64encode("Test String"), + }] + }, + } + + req = fakes.HTTPRequest.blank(self.url) + body = self.controller.action(req, FAKE_UUID, body) + + self.assertTrue('personality' not in body['server']) + + def test_server_rebuild_admin_pass(self): + new_return_server = return_server_with_attributes(image_ref='2') + self.stubs.Set(nova.db, 'instance_get', new_return_server) + + body = { + "rebuild": { + "imageRef": "http://localhost/images/2", + "adminPass": "asdf", + }, + } + + req = fakes.HTTPRequest.blank(self.url) + body = self.controller.action(req, FAKE_UUID, body) + + self.assertEqual(body['server']['image']['id'], '2') + self.assertEqual(body['server']['adminPass'], 'asdf') + + def test_server_rebuild_server_not_found(self): + def server_not_found(self, instance_id): + raise exception.InstanceNotFound(instance_id=instance_id) + self.stubs.Set(nova.db, 'instance_get', server_not_found) + + body = { + "rebuild": { + "imageRef": "http://localhost/images/2", + }, + } + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.action, req, FAKE_UUID, body) + + def test_resize_server(self): + + body = dict(resize=dict(flavorRef="http://localhost/3")) + + self.resize_called = False + + def resize_mock(*args): + self.resize_called = True + + self.stubs.Set(nova.compute.api.API, 'resize', resize_mock) + + req = fakes.HTTPRequest.blank(self.url) + body = self.controller.action(req, FAKE_UUID, body) + + self.assertEqual(self.resize_called, True) + + def test_resize_server_no_flavor(self): + body = dict(resize=dict()) + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_resize_server_no_flavor_ref(self): + body = dict(resize=dict(flavorRef=None)) + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_confirm_resize_server(self): + body = dict(confirmResize=None) + + self.confirm_resize_called = False + + def cr_mock(*args): + self.confirm_resize_called = True + + self.stubs.Set(nova.compute.api.API, 'confirm_resize', cr_mock) + + req = fakes.HTTPRequest.blank(self.url) + body = self.controller.action(req, FAKE_UUID, body) + + self.assertEqual(self.confirm_resize_called, True) + + def test_confirm_resize_migration_not_found(self): + body = dict(confirmResize=None) + + def confirm_resize_mock(*args): + raise exception.MigrationNotFoundByStatus(instance_id=1, + status='finished') + + self.stubs.Set(nova.compute.api.API, + 'confirm_resize', + confirm_resize_mock) + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_revert_resize_migration_not_found(self): + body = dict(revertResize=None) + + def revert_resize_mock(*args): + raise exception.MigrationNotFoundByStatus(instance_id=1, + status='finished') + + self.stubs.Set(nova.compute.api.API, + 'revert_resize', + revert_resize_mock) + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_revert_resize_server(self): + body = dict(revertResize=None) + + self.revert_resize_called = False + + def revert_mock(*args): + self.revert_resize_called = True + + self.stubs.Set(nova.compute.api.API, 'revert_resize', revert_mock) + + req = fakes.HTTPRequest.blank(self.url) + body = self.controller.action(req, FAKE_UUID, body) + + self.assertEqual(self.revert_resize_called, True) + + def test_create_image(self): + body = { + 'createImage': { + 'name': 'Snapshot 1', + }, + } + + req = fakes.HTTPRequest.blank(self.url) + response = self.controller.action(req, FAKE_UUID, body) + + location = response.headers['Location'] + self.assertEqual('http://localhost/v1.1/fake/images/123', location) + server_location = self.snapshot.extra_props_last_call['instance_ref'] + expected_server_location = 'http://localhost/v1.1/servers/' + self.uuid + self.assertEqual(expected_server_location, server_location) + + def test_create_image_snapshots_disabled(self): + """Don't permit a snapshot if the allow_instance_snapshots flag is + False + """ + self.flags(allow_instance_snapshots=False) + body = { + 'createImage': { + 'name': 'Snapshot 1', + }, + } + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_create_image_with_metadata(self): + body = { + 'createImage': { + 'name': 'Snapshot 1', + 'metadata': {'key': 'asdf'}, + }, + } + + req = fakes.HTTPRequest.blank(self.url) + response = self.controller.action(req, FAKE_UUID, body) + + location = response.headers['Location'] + self.assertEqual('http://localhost/v1.1/fake/images/123', location) + + def test_create_image_with_too_much_metadata(self): + body = { + 'createImage': { + 'name': 'Snapshot 1', + 'metadata': {}, + }, + } + for num in range(FLAGS.quota_metadata_items + 1): + body['createImage']['metadata']['foo%i' % num] = "bar" + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPRequestEntityTooLarge, + self.controller.action, req, FAKE_UUID, body) + + def test_create_image_no_name(self): + body = { + 'createImage': {}, + } + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_create_image_bad_metadata(self): + body = { + 'createImage': { + 'name': 'geoff', + 'metadata': 'henry', + }, + } + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_create_image_conflict_snapshot(self): + """Attempt to create image when image is already being created.""" + def snapshot(*args, **kwargs): + raise exception.InstanceSnapshotting + self.stubs.Set(nova.compute.API, 'snapshot', snapshot) + + body = { + "createImage": { + "name": "test_snapshot", + }, + } + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPConflict, + self.controller.action, req, FAKE_UUID, body) + + def test_create_backup(self): + """The happy path for creating backups""" + self.flags(allow_admin_api=True) + + body = { + 'createBackup': { + 'name': 'Backup 1', + 'backup_type': 'daily', + 'rotation': 1, + }, + } + + req = fakes.HTTPRequest.blank(self.url) + response = self.controller.action(req, FAKE_UUID, body) + + self.assertTrue(response.headers['Location']) + server_location = self.backup.extra_props_last_call['instance_ref'] + expected_server_location = 'http://localhost/v1.1/servers/' + self.uuid + self.assertEqual(expected_server_location, server_location) + + def test_create_backup_admin_api_off(self): + """The happy path for creating backups""" + self.flags(allow_admin_api=False) + + body = { + 'createBackup': { + 'name': 'Backup 1', + 'backup_type': 'daily', + 'rotation': 1, + }, + } + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_create_backup_with_metadata(self): + self.flags(allow_admin_api=True) + + body = { + 'createBackup': { + 'name': 'Backup 1', + 'backup_type': 'daily', + 'rotation': 1, + 'metadata': {'123': 'asdf'}, + }, + } + + req = fakes.HTTPRequest.blank(self.url) + response = self.controller.action(req, FAKE_UUID, body) + + self.assertTrue(response.headers['Location']) + + def test_create_backup_with_too_much_metadata(self): + self.flags(allow_admin_api=True) + + body = { + 'createBackup': { + 'name': 'Backup 1', + 'backup_type': 'daily', + 'rotation': 1, + 'metadata': {'123': 'asdf'}, + }, + } + for num in range(FLAGS.quota_metadata_items + 1): + body['createBackup']['metadata']['foo%i' % num] = "bar" + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPRequestEntityTooLarge, + self.controller.action, req, FAKE_UUID, body) + + def test_create_backup_no_name(self): + """Name is required for backups""" + self.flags(allow_admin_api=True) + + body = { + 'createBackup': { + 'backup_type': 'daily', + 'rotation': 1, + }, + } + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_create_backup_no_rotation(self): + """Rotation is required for backup requests""" + self.flags(allow_admin_api=True) + + body = { + 'createBackup': { + 'name': 'Backup 1', + 'backup_type': 'daily', + }, + } + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_create_backup_no_backup_type(self): + """Backup Type (daily or weekly) is required for backup requests""" + self.flags(allow_admin_api=True) + + body = { + 'createBackup': { + 'name': 'Backup 1', + 'rotation': 1, + }, + } + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + def test_create_backup_bad_entity(self): + self.flags(allow_admin_api=True) + + body = {'createBackup': 'go'} + + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.action, req, FAKE_UUID, body) + + +class TestServerActionXMLDeserializer(test.TestCase): + + def setUp(self): + self.deserializer = servers.ServerXMLDeserializer() + + def tearDown(self): + pass + + def test_create_image(self): + serial_request = """ +""" + request = self.deserializer.deserialize(serial_request, 'action') + expected = { + "createImage": { + "name": "new-server-test", + }, + } + self.assertEquals(request['body'], expected) + + def test_create_image_with_metadata(self): + serial_request = """ + + + value1 + +""" + request = self.deserializer.deserialize(serial_request, 'action') + expected = { + "createImage": { + "name": "new-server-test", + "metadata": {"key1": "value1"}, + }, + } + self.assertEquals(request['body'], expected) + + def test_change_pass(self): + serial_request = """ + """ + request = self.deserializer.deserialize(serial_request, 'action') + expected = { + "changePassword": { + "adminPass": "1234pass", + }, + } + self.assertEquals(request['body'], expected) + + def test_change_pass_no_pass(self): + serial_request = """ + """ + self.assertRaises(AttributeError, + self.deserializer.deserialize, + serial_request, + 'action') + + def test_reboot(self): + serial_request = """ + """ + request = self.deserializer.deserialize(serial_request, 'action') + expected = { + "reboot": { + "type": "HARD", + }, + } + self.assertEquals(request['body'], expected) + + def test_reboot_no_type(self): + serial_request = """ + """ + self.assertRaises(AttributeError, + self.deserializer.deserialize, + serial_request, + 'action') + + def test_resize(self): + serial_request = """ + """ + request = self.deserializer.deserialize(serial_request, 'action') + expected = { + "resize": {"flavorRef": "http://localhost/flavors/3"}, + } + self.assertEquals(request['body'], expected) + + def test_resize_no_flavor_ref(self): + serial_request = """ + """ + self.assertRaises(AttributeError, + self.deserializer.deserialize, + serial_request, + 'action') + + def test_confirm_resize(self): + serial_request = """ + """ + request = self.deserializer.deserialize(serial_request, 'action') + expected = { + "confirmResize": None, + } + self.assertEquals(request['body'], expected) + + def test_revert_resize(self): + serial_request = """ + """ + request = self.deserializer.deserialize(serial_request, 'action') + expected = { + "revertResize": None, + } + self.assertEquals(request['body'], expected) + + def test_rebuild(self): + serial_request = """ + + + Apache1 + + + Mg== + + """ + request = self.deserializer.deserialize(serial_request, 'action') + expected = { + "rebuild": { + "name": "new-server-test", + "imageRef": "http://localhost/images/1", + "metadata": { + "My Server Name": "Apache1", + }, + "personality": [ + {"path": "/etc/banner.txt", "contents": "Mg=="}, + ], + }, + } + self.assertDictMatch(request['body'], expected) + + def test_rebuild_minimum(self): + serial_request = """ + """ + request = self.deserializer.deserialize(serial_request, 'action') + expected = { + "rebuild": { + "imageRef": "http://localhost/images/1", + }, + } + self.assertDictMatch(request['body'], expected) + + def test_rebuild_no_imageRef(self): + serial_request = """ + + + Apache1 + + + Mg== + + """ + self.assertRaises(AttributeError, + self.deserializer.deserialize, + serial_request, + 'action') diff --git a/nova/tests/api/openstack/v2/test_server_metadata.py b/nova/tests/api/openstack/v2/test_server_metadata.py new file mode 100644 index 000000000..49ff1bcd8 --- /dev/null +++ b/nova/tests/api/openstack/v2/test_server_metadata.py @@ -0,0 +1,361 @@ +# 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. + +import json +import webob + +from nova.api.openstack.v2 import server_metadata +import nova.db +from nova import exception +from nova import flags +from nova import test +from nova.tests.api.openstack import fakes +from nova import utils + + +FLAGS = flags.FLAGS + + +def return_create_instance_metadata_max(context, server_id, metadata, delete): + return stub_max_server_metadata() + + +def return_create_instance_metadata(context, server_id, metadata, delete): + return stub_server_metadata() + + +def return_server_metadata(context, server_id): + if not isinstance(server_id, int): + msg = 'id %s must be int in return server metadata' % server_id + raise Exception(msg) + return stub_server_metadata() + + +def return_empty_server_metadata(context, server_id): + return {} + + +def delete_server_metadata(context, server_id, key): + pass + + +def stub_server_metadata(): + metadata = { + "key1": "value1", + "key2": "value2", + "key3": "value3", + } + return metadata + + +def stub_max_server_metadata(): + metadata = {"metadata": {}} + for num in range(FLAGS.quota_metadata_items): + metadata['metadata']['key%i' % num] = "blah" + return metadata + + +def return_server(context, server_id): + return {'id': server_id, 'name': 'fake'} + + +def return_server_by_uuid(context, server_uuid): + return {'id': 1, 'name': 'fake'} + + +def return_server_nonexistant(context, server_id): + raise exception.InstanceNotFound() + + +class ServerMetaDataTest(test.TestCase): + + def setUp(self): + super(ServerMetaDataTest, self).setUp() + fakes.stub_out_key_pair_funcs(self.stubs) + self.stubs.Set(nova.db, 'instance_get', return_server) + self.stubs.Set(nova.db, 'instance_get_by_uuid', + return_server_by_uuid) + + self.stubs.Set(nova.db, 'instance_metadata_get', + return_server_metadata) + + self.controller = server_metadata.Controller() + self.uuid = str(utils.gen_uuid()) + self.url = '/v1.1/fake/servers/%s/metadata' % self.uuid + + def test_index(self): + req = fakes.HTTPRequest.blank(self.url) + res_dict = self.controller.index(req, self.uuid) + + expected = { + 'metadata': { + 'key1': 'value1', + 'key2': 'value2', + 'key3': 'value3', + }, + } + self.assertEqual(expected, res_dict) + + def test_index_nonexistant_server(self): + self.stubs.Set(nova.db, 'instance_metadata_get', + return_server_nonexistant) + req = fakes.HTTPRequest.blank(self.url) + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.index, req, self.url) + + def test_index_no_data(self): + self.stubs.Set(nova.db, 'instance_metadata_get', + return_empty_server_metadata) + req = fakes.HTTPRequest.blank(self.url) + res_dict = self.controller.index(req, self.uuid) + expected = {'metadata': {}} + self.assertEqual(expected, res_dict) + + def test_show(self): + req = fakes.HTTPRequest.blank(self.url + '/key2') + res_dict = self.controller.show(req, self.uuid, 'key2') + expected = {'meta': {'key2': 'value2'}} + self.assertEqual(expected, res_dict) + + def test_show_nonexistant_server(self): + self.stubs.Set(nova.db, 'instance_metadata_get', + return_server_nonexistant) + req = fakes.HTTPRequest.blank(self.url + '/key2') + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.show, req, self.uuid, 'key2') + + def test_show_meta_not_found(self): + self.stubs.Set(nova.db, 'instance_metadata_get', + return_empty_server_metadata) + req = fakes.HTTPRequest.blank(self.url + '/key6') + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.show, req, self.uuid, 'key6') + + def test_delete(self): + self.stubs.Set(nova.db, 'instance_metadata_get', + return_server_metadata) + self.stubs.Set(nova.db, 'instance_metadata_delete', + delete_server_metadata) + req = fakes.HTTPRequest.blank(self.url + '/key2') + req.method = 'DELETE' + res = self.controller.delete(req, self.uuid, 'key2') + + self.assertEqual(None, res) + + def test_delete_nonexistant_server(self): + self.stubs.Set(nova.db, 'instance_get', return_server_nonexistant) + req = fakes.HTTPRequest.blank(self.url + '/key1') + req.method = 'DELETE' + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.delete, req, self.uuid, 'key1') + + def test_delete_meta_not_found(self): + self.stubs.Set(nova.db, 'instance_metadata_get', + return_empty_server_metadata) + req = fakes.HTTPRequest.blank(self.url + '/key6') + req.method = 'DELETE' + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.delete, req, self.uuid, 'key6') + + def test_create(self): + self.stubs.Set(nova.db, 'instance_metadata_get', + return_server_metadata) + self.stubs.Set(nova.db, 'instance_metadata_update', + return_create_instance_metadata) + req = fakes.HTTPRequest.blank(self.url) + req.method = 'POST' + req.content_type = "application/json" + body = {"metadata": {"key9": "value9"}} + req.body = json.dumps(body) + res_dict = self.controller.create(req, self.uuid, body) + + body['metadata'].update({ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }) + self.assertEqual(body, res_dict) + + def test_create_empty_body(self): + self.stubs.Set(nova.db, 'instance_metadata_update', + return_create_instance_metadata) + req = fakes.HTTPRequest.blank(self.url) + req.method = 'POST' + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.create, req, self.uuid, None) + + def test_create_nonexistant_server(self): + self.stubs.Set(nova.db, 'instance_get', return_server_nonexistant) + req = fakes.HTTPRequest.blank(self.url) + req.method = 'POST' + body = {"metadata": {"key1": "value1"}} + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.create, req, self.uuid, body) + + def test_update_all(self): + self.stubs.Set(nova.db, 'instance_metadata_update', + return_create_instance_metadata) + req = fakes.HTTPRequest.blank(self.url) + req.method = 'PUT' + req.content_type = "application/json" + expected = { + 'metadata': { + 'key10': 'value10', + 'key99': 'value99', + }, + } + req.body = json.dumps(expected) + res_dict = self.controller.update_all(req, self.uuid, expected) + + self.assertEqual(expected, res_dict) + + def test_update_all_empty_container(self): + self.stubs.Set(nova.db, 'instance_metadata_update', + return_create_instance_metadata) + req = fakes.HTTPRequest.blank(self.url) + req.method = 'PUT' + req.content_type = "application/json" + expected = {'metadata': {}} + req.body = json.dumps(expected) + res_dict = self.controller.update_all(req, self.uuid, expected) + + self.assertEqual(expected, res_dict) + + def test_update_all_malformed_container(self): + self.stubs.Set(nova.db, 'instance_metadata_update', + return_create_instance_metadata) + req = fakes.HTTPRequest.blank(self.url) + req.method = 'PUT' + req.content_type = "application/json" + expected = {'meta': {}} + req.body = json.dumps(expected) + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.update_all, req, self.uuid, expected) + + def test_update_all_malformed_data(self): + self.stubs.Set(nova.db, 'instance_metadata_update', + return_create_instance_metadata) + req = fakes.HTTPRequest.blank(self.url) + req.method = 'PUT' + req.content_type = "application/json" + expected = {'metadata': ['asdf']} + req.body = json.dumps(expected) + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.update_all, req, self.uuid, expected) + + def test_update_all_nonexistant_server(self): + self.stubs.Set(nova.db, 'instance_get', return_server_nonexistant) + req = fakes.HTTPRequest.blank(self.url) + req.method = 'PUT' + req.content_type = "application/json" + body = {'metadata': {'key10': 'value10'}} + req.body = json.dumps(body) + + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.update_all, req, '100', body) + + def test_update_item(self): + self.stubs.Set(nova.db, 'instance_metadata_update', + return_create_instance_metadata) + req = fakes.HTTPRequest.blank(self.url + '/key1') + req.method = 'PUT' + body = {"meta": {"key1": "value1"}} + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res_dict = self.controller.update(req, self.uuid, 'key1', body) + expected = {'meta': {'key1': 'value1'}} + self.assertEqual(expected, res_dict) + + def test_update_item_nonexistant_server(self): + self.stubs.Set(nova.db, 'instance_get', return_server_nonexistant) + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/asdf/metadata/key1') + req.method = 'PUT' + body = {"meta": {"key1": "value1"}} + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPNotFound, + self.controller.update, req, self.uuid, 'key1', body) + + def test_update_item_empty_body(self): + self.stubs.Set(nova.db, 'instance_metadata_update', + return_create_instance_metadata) + req = fakes.HTTPRequest.blank(self.url + '/key1') + req.method = 'PUT' + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.update, req, self.uuid, 'key1', None) + + def test_update_item_too_many_keys(self): + self.stubs.Set(nova.db, 'instance_metadata_update', + return_create_instance_metadata) + req = fakes.HTTPRequest.blank(self.url + '/key1') + req.method = 'PUT' + body = {"meta": {"key1": "value1", "key2": "value2"}} + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.update, req, self.uuid, 'key1', body) + + def test_update_item_body_uri_mismatch(self): + self.stubs.Set(nova.db, 'instance_metadata_update', + return_create_instance_metadata) + req = fakes.HTTPRequest.blank(self.url + '/bad') + req.method = 'PUT' + body = {"meta": {"key1": "value1"}} + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.update, req, self.uuid, 'bad', body) + + def test_too_many_metadata_items_on_create(self): + self.stubs.Set(nova.db, 'instance_metadata_update', + return_create_instance_metadata) + data = {"metadata": {}} + for num in range(FLAGS.quota_metadata_items + 1): + data['metadata']['key%i' % num] = "blah" + req = fakes.HTTPRequest.blank(self.url) + req.method = 'POST' + req.body = json.dumps(data) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPRequestEntityTooLarge, + self.controller.create, req, self.uuid, data) + + def test_too_many_metadata_items_on_update_item(self): + self.stubs.Set(nova.db, 'instance_metadata_update', + return_create_instance_metadata) + data = {"metadata": {}} + for num in range(FLAGS.quota_metadata_items + 1): + data['metadata']['key%i' % num] = "blah" + req = fakes.HTTPRequest.blank(self.url) + req.method = 'PUT' + req.body = json.dumps(data) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPRequestEntityTooLarge, + self.controller.update_all, req, self.uuid, data) diff --git a/nova/tests/api/openstack/v2/test_servers.py b/nova/tests/api/openstack/v2/test_servers.py new file mode 100644 index 000000000..34a5dfa51 --- /dev/null +++ b/nova/tests/api/openstack/v2/test_servers.py @@ -0,0 +1,3631 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010-2011 OpenStack LLC. +# Copyright 2011 Piston Cloud Computing, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import datetime +import json +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 +from nova.api.openstack import xmlutil +import nova.compute.api +from nova.compute import instance_types +from nova.compute import task_states +from nova.compute import vm_states +import nova.db +from nova.db.sqlalchemy.models import InstanceMetadata +from nova import flags +import nova.image.fake +import nova.rpc +import nova.scheduler.api +from nova import test +from nova.tests.api.openstack import fakes +from nova import utils + + +FLAGS = flags.FLAGS +FAKE_UUID = fakes.FAKE_UUID +FAKE_UUIDS = {0: FAKE_UUID} +NS = "{http://docs.openstack.org/compute/api/v1.1}" +ATOMNS = "{http://www.w3.org/2005/Atom}" +XPATH_NS = { + 'atom': 'http://www.w3.org/2005/Atom', + 'ns': 'http://docs.openstack.org/compute/api/v1.1' +} + + +def get_fake_uuid(token=0): + if not token in FAKE_UUIDS: + FAKE_UUIDS[token] = str(utils.gen_uuid()) + return FAKE_UUIDS[token] + + +def fake_gen_uuid(): + return FAKE_UUID + + +def return_server_by_id(context, id): + return fakes.stub_instance(id) + + +def return_server_by_uuid(context, uuid): + id = 1 + return fakes.stub_instance(id, uuid=uuid) + + +def return_server_with_attributes(**kwargs): + def _return_server(context, instance_id): + return fakes.stub_instance(instance_id, **kwargs) + return _return_server + + +def return_server_with_attributes_by_uuid(**kwargs): + def _return_server(context, uuid): + return fakes.stub_instance(1, uuid=uuid, **kwargs) + return _return_server + + +def return_server_with_state(vm_state, task_state=None): + def _return_server(context, uuid): + return fakes.stub_instance(1, uuid=uuid, vm_state=vm_state, + task_state=task_state) + return _return_server + + +def return_server_with_uuid_and_state(vm_state, task_state): + def _return_server(context, id): + return fakes.stub_instance(id, + uuid=FAKE_UUID, + vm_state=vm_state, + task_state=task_state) + return _return_server + + +def return_servers(context, *args, **kwargs): + servers = [] + for i in xrange(5): + server = fakes.stub_instance(i, 'fake', 'fake', uuid=get_fake_uuid(i)) + servers.append(server) + return servers + + +def return_servers_by_reservation(context, reservation_id=""): + return [fakes.stub_instance(i, reservation_id) for i in xrange(5)] + + +def return_servers_by_reservation_empty(context, reservation_id=""): + return [] + + +def return_servers_from_child_zones_empty(*args, **kwargs): + return [] + + +def return_servers_from_child_zones(*args, **kwargs): + class Server(object): + pass + + zones = [] + for zone in xrange(3): + servers = [] + for server_id in xrange(5): + server = Server() + server._info = fakes.stub_instance( + server_id, reservation_id="child") + servers.append(server) + + zones.append(("Zone%d" % zone, servers)) + return zones + + +def return_security_group(context, instance_id, security_group_id): + pass + + +def instance_update(context, instance_id, values): + return fakes.stub_instance(instance_id, name=values.get('display_name')) + + +def instance_addresses(context, instance_id): + return None + + +def fake_compute_api(cls, req, id): + return True + + +def find_host(self, context, instance_id): + return "nova" + + +class MockSetAdminPassword(object): + def __init__(self): + self.instance_id = None + self.password = None + + def __call__(self, context, instance_id, password): + self.instance_id = instance_id + self.password = password + + +class ServersControllerTest(test.TestCase): + def setUp(self): + self.maxDiff = None + super(ServersControllerTest, self).setUp() + self.flags(verbose=True, use_ipv6=False) + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + fakes.stub_out_key_pair_funcs(self.stubs) + fakes.stub_out_image_service(self.stubs) + fakes.stub_out_nw_api(self.stubs) + self.stubs.Set(nova.db, 'instance_get_all_by_filters', + return_servers) + self.stubs.Set(nova.db, 'instance_get', return_server_by_id) + self.stubs.Set(nova.db, 'instance_get_by_uuid', + return_server_by_uuid) + self.stubs.Set(nova.db, 'instance_get_all_by_project', + return_servers) + self.stubs.Set(nova.db, 'instance_add_security_group', + return_security_group) + self.stubs.Set(nova.db, 'instance_update', instance_update) + self.stubs.Set(nova.db, 'instance_get_fixed_addresses', + instance_addresses) + self.stubs.Set(nova.db, 'instance_get_floating_address', + instance_addresses) + self.stubs.Set(nova.compute.API, "get_diagnostics", fake_compute_api) + self.stubs.Set(nova.compute.API, "get_actions", fake_compute_api) + + self.config_drive = None + + self.controller = servers.Controller() + self.ips_controller = ips.Controller() + + def test_get_server_by_uuid(self): + """ + The steps involved with resolving a UUID are pretty complicated; + here's what's happening in this scenario: + + 1. Show is calling `routing_get` + + 2. `routing_get` is wrapped by `reroute_compute` which does the work + of resolving requests to child zones. + + 3. `reroute_compute` looks up the UUID by hitting the stub + (returns_server_by_uuid) + + 4. Since the stub return that the record exists, `reroute_compute` + considers the request to be 'zone local', so it replaces the UUID + in the argument list with an integer ID and then calls the inner + function ('get'). + + 5. The call to `get` hits the other stub 'returns_server_by_id` which + has the UUID set to FAKE_UUID + + So, counterintuitively, we call `get` twice on the `show` command. + """ + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) + res_dict = self.controller.show(req, FAKE_UUID) + self.assertEqual(res_dict['server']['id'], FAKE_UUID) + + def test_get_server_by_id(self): + self.flags(use_ipv6=True) + image_bookmark = "http://localhost/fake/images/10" + flavor_bookmark = "http://localhost/fake/flavors/1" + + uuid = FAKE_UUID + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % uuid) + res_dict = self.controller.show(req, uuid) + expected_server = { + "server": { + "id": uuid, + "user_id": "fake", + "tenant_id": "fake", + "updated": "2010-11-11T11:00:00Z", + "created": "2010-10-10T12:00:00Z", + "progress": 0, + "name": "server1", + "status": "BUILD", + "accessIPv4": "", + "accessIPv6": "", + "hostId": '', + "key_name": '', + "image": { + "id": "10", + "links": [ + { + "rel": "bookmark", + "href": image_bookmark, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": flavor_bookmark, + }, + ], + }, + "addresses": { + }, + "metadata": { + "seq": "1", + }, + "config_drive": None, + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/servers/%s" % uuid, + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/servers/%s" % uuid, + }, + ], + } + } + + self.assertDictMatch(res_dict, expected_server) + + def test_get_server_with_active_status_by_id(self): + image_bookmark = "http://localhost/fake/images/10" + flavor_bookmark = "http://localhost/fake/flavors/1" + + new_return_server = return_server_with_attributes( + vm_state=vm_states.ACTIVE, progress=100) + self.stubs.Set(nova.db, 'instance_get', new_return_server) + + uuid = FAKE_UUID + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % uuid) + res_dict = self.controller.show(req, uuid) + expected_server = { + "server": { + "id": uuid, + "user_id": "fake", + "tenant_id": "fake", + "updated": "2010-11-11T11:00:00Z", + "created": "2010-10-10T12:00:00Z", + "progress": 100, + "name": "server1", + "status": "ACTIVE", + "accessIPv4": "", + "accessIPv6": "", + "hostId": '', + "key_name": '', + "image": { + "id": "10", + "links": [ + { + "rel": "bookmark", + "href": image_bookmark, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": flavor_bookmark, + }, + ], + }, + "addresses": { + }, + "metadata": { + "seq": "1", + }, + "config_drive": None, + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/servers/%s" % uuid, + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/servers/%s" % uuid, + }, + ], + } + } + + self.assertDictMatch(res_dict, expected_server) + + def test_get_server_with_id_image_ref_by_id(self): + image_ref = "10" + image_bookmark = "http://localhost/fake/images/10" + flavor_id = "1" + flavor_bookmark = "http://localhost/fake/flavors/1" + + new_return_server = return_server_with_attributes( + vm_state=vm_states.ACTIVE, image_ref=image_ref, + flavor_id=flavor_id, progress=100) + self.stubs.Set(nova.db, 'instance_get', new_return_server) + + uuid = FAKE_UUID + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % uuid) + res_dict = self.controller.show(req, uuid) + expected_server = { + "server": { + "id": uuid, + "user_id": "fake", + "tenant_id": "fake", + "updated": "2010-11-11T11:00:00Z", + "created": "2010-10-10T12:00:00Z", + "progress": 100, + "name": "server1", + "status": "ACTIVE", + "accessIPv4": "", + "accessIPv6": "", + "hostId": '', + "key_name": '', + "image": { + "id": "10", + "links": [ + { + "rel": "bookmark", + "href": image_bookmark, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": flavor_bookmark, + }, + ], + }, + "addresses": { + }, + "metadata": { + "seq": "1", + }, + "config_drive": None, + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/servers/%s" % uuid, + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/servers/%s" % uuid, + }, + ], + } + } + + self.assertDictMatch(res_dict, expected_server) + + # NOTE(bcwaldon): lp830817 + def test_get_server_by_id_malformed_networks(self): + def fake_instance_get(context, instance_uuid): + instance = return_server_by_uuid(context, instance_uuid) + instance['fixed_ips'] = [dict(network=None, address='1.2.3.4')] + return instance + + self.stubs.Set(nova.db, 'instance_get_by_uuid', fake_instance_get) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) + res_dict = self.controller.show(req, FAKE_UUID) + + self.assertEqual(res_dict['server']['id'], FAKE_UUID) + self.assertEqual(res_dict['server']['name'], 'server1') + + def test_get_server_by_id_malformed_vif(self): + def fake_instance_get(context, uuid): + instance = return_server_by_uuid(context, uuid) + instance['fixed_ips'] = [dict(network={'label': 'meow'}, + address='1.2.3.4', virtual_interface=None)] + return instance + + self.stubs.Set(nova.db, 'instance_get_by_uuid', fake_instance_get) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) + res_dict = self.controller.show(req, FAKE_UUID) + + self.assertEqual(res_dict['server']['id'], FAKE_UUID) + self.assertEqual(res_dict['server']['name'], 'server1') + + def test_get_server_by_id_with_addresses(self): + self.flags(use_ipv6=True) + privates = ['192.168.0.3', '192.168.0.4'] + publics = ['172.19.0.1', '172.19.0.2'] + new_return_server = return_server_with_attributes( + public_ips=publics, private_ips=privates) + self.stubs.Set(nova.db, 'instance_get', new_return_server) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) + res_dict = self.controller.show(req, FAKE_UUID) + + self.assertEqual(res_dict['server']['id'], FAKE_UUID) + self.assertEqual(res_dict['server']['name'], 'server1') + addresses = res_dict['server']['addresses'] + expected = { + 'private': [ + {'addr': '192.168.0.3', 'version': 4}, + {'addr': '192.168.0.4', 'version': 4}, + ], + 'public': [ + {'addr': 'b33f::fdee:ddff:fecc:bbaa', 'version': 6}, + {'addr': '172.19.0.1', 'version': 4}, + {'addr': '172.19.0.2', 'version': 4}, + ], + } + self.assertDictMatch(addresses, expected) + + def test_get_server_by_id_with_addresses_ipv6_disabled(self): + # ipv6 flag is off by default + privates = ['192.168.0.3', '192.168.0.4'] + publics = ['172.19.0.1', '172.19.0.2'] + new_return_server = return_server_with_attributes( + public_ips=publics, private_ips=privates) + self.stubs.Set(nova.db, 'instance_get', new_return_server) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) + res_dict = self.controller.show(req, FAKE_UUID) + + self.assertEqual(res_dict['server']['id'], FAKE_UUID) + self.assertEqual(res_dict['server']['name'], 'server1') + addresses = res_dict['server']['addresses'] + expected = { + 'private': [ + {'addr': '192.168.0.3', 'version': 4}, + {'addr': '192.168.0.4', 'version': 4}, + ], + 'public': [ + {'addr': '172.19.0.1', 'version': 4}, + {'addr': '172.19.0.2', 'version': 4}, + ], + } + self.assertDictMatch(addresses, expected) + + def test_get_server_addresses(self): + self.flags(use_ipv6=True) + + privates = ['192.168.0.3', '192.168.0.4'] + publics = ['172.19.0.1', '1.2.3.4', '172.19.0.2'] + new_return_server = return_server_with_attributes_by_uuid( + public_ips=publics, private_ips=privates) + self.stubs.Set(nova.db, 'instance_get_by_uuid', new_return_server) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s/ips' % FAKE_UUID) + res_dict = self.ips_controller.index(req, FAKE_UUID) + + expected = { + 'addresses': { + 'private': [ + {'version': 4, 'addr': '192.168.0.3'}, + {'version': 4, 'addr': '192.168.0.4'}, + ], + 'public': [ + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, + {'version': 4, 'addr': '172.19.0.1'}, + {'version': 4, 'addr': '1.2.3.4'}, + {'version': 4, 'addr': '172.19.0.2'}, + ], + }, + } + self.assertDictMatch(res_dict, expected) + + def test_get_server_addresses_with_floating(self): + privates = ['192.168.0.3', '192.168.0.4'] + publics = ['172.19.0.1', '1.2.3.4', '172.19.0.2'] + new_return_server = return_server_with_attributes_by_uuid( + public_ips=publics, private_ips=privates, + public_ips_are_floating=True) + self.stubs.Set(nova.db, 'instance_get_by_uuid', new_return_server) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s/ips' % FAKE_UUID) + res_dict = self.ips_controller.index(req, FAKE_UUID) + + expected = { + 'addresses': { + 'private': [ + {'version': 4, 'addr': '192.168.0.3'}, + {'version': 4, 'addr': '192.168.0.4'}, + {'version': 4, 'addr': '172.19.0.1'}, + {'version': 4, 'addr': '1.2.3.4'}, + {'version': 4, 'addr': '172.19.0.2'}, + ], + }, + } + self.assertDictMatch(res_dict, expected) + + def test_get_server_addresses_single_network(self): + self.flags(use_ipv6=True) + privates = ['192.168.0.3', '192.168.0.4'] + publics = ['172.19.0.1', '1.2.3.4', '172.19.0.2'] + new_return_server = return_server_with_attributes_by_uuid( + public_ips=publics, private_ips=privates) + self.stubs.Set(nova.db, 'instance_get_by_uuid', new_return_server) + + url = '/v1.1/fake/servers/%s/ips/public' % FAKE_UUID + req = fakes.HTTPRequest.blank(url) + res_dict = self.ips_controller.show(req, FAKE_UUID, 'public') + + expected = { + 'public': [ + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, + {'version': 4, 'addr': '172.19.0.1'}, + {'version': 4, 'addr': '1.2.3.4'}, + {'version': 4, 'addr': '172.19.0.2'}, + ], + } + self.assertDictMatch(res_dict, expected) + + def test_get_server_addresses_nonexistant_network(self): + url = '/v1.1/fake/servers/%s/ips/network_0' % FAKE_UUID + req = fakes.HTTPRequest.blank(url) + self.assertRaises(webob.exc.HTTPNotFound, self.ips_controller.show, + req, FAKE_UUID, 'network_0') + + def test_get_server_addresses_nonexistant_server(self): + def fake_instance_get(*args, **kwargs): + raise nova.exception.InstanceNotFound() + + self.stubs.Set(nova.db, 'instance_get_by_uuid', fake_instance_get) + + server_id = str(utils.gen_uuid()) + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s/ips' % server_id) + self.assertRaises(webob.exc.HTTPNotFound, + self.ips_controller.index, req, server_id) + + def test_get_server_list_with_reservation_id(self): + self.stubs.Set(nova.db, 'instance_get_all_by_reservation', + return_servers_by_reservation) + self.stubs.Set(nova.scheduler.api, 'call_zone_method', + return_servers_from_child_zones) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?reservation_id=foo') + res_dict = self.controller.index(req) + + i = 0 + for s in res_dict['servers']: + if '_is_precooked' in s: + self.assertEqual(s.get('reservation_id'), 'child') + else: + print s + self.assertEqual(s.get('name'), 'server%d' % i) + i += 1 + + def test_get_server_list_with_reservation_id_empty(self): + self.stubs.Set(nova.db, 'instance_get_all_by_reservation', + return_servers_by_reservation_empty) + self.stubs.Set(nova.scheduler.api, 'call_zone_method', + return_servers_from_child_zones_empty) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/detail?' + 'reservation_id=foo') + res_dict = self.controller.detail(req) + + i = 0 + for s in res_dict['servers']: + if '_is_precooked' in s: + self.assertEqual(s.get('reservation_id'), 'child') + else: + self.assertEqual(s.get('name'), 'server%d' % i) + i += 1 + + def test_get_server_list_with_reservation_id_details(self): + self.stubs.Set(nova.db, 'instance_get_all_by_reservation', + return_servers_by_reservation) + self.stubs.Set(nova.scheduler.api, 'call_zone_method', + return_servers_from_child_zones) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/detail?' + 'reservation_id=foo') + res_dict = self.controller.detail(req) + + i = 0 + for s in res_dict['servers']: + if '_is_precooked' in s: + self.assertEqual(s.get('reservation_id'), 'child') + else: + self.assertEqual(s.get('name'), 'server%d' % i) + i += 1 + + def test_get_server_list(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/servers') + res_dict = self.controller.index(req) + + self.assertEqual(len(res_dict['servers']), 5) + for i, s in enumerate(res_dict['servers']): + self.assertEqual(s['id'], get_fake_uuid(i)) + self.assertEqual(s['name'], 'server%d' % i) + self.assertEqual(s.get('image', None), None) + + expected_links = [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/servers/%s" % s['id'], + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/servers/%s" % s['id'], + }, + ] + + self.assertEqual(s['links'], expected_links) + + def test_get_servers_with_limit(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?limit=3') + res_dict = self.controller.index(req) + + servers = res_dict['servers'] + self.assertEqual([s['id'] for s in servers], + [get_fake_uuid(i) for i in xrange(len(servers))]) + + servers_links = res_dict['servers_links'] + self.assertEqual(servers_links[0]['rel'], 'next') + href_parts = urlparse.urlparse(servers_links[0]['href']) + self.assertEqual('/v1.1/fake/servers', href_parts.path) + params = urlparse.parse_qs(href_parts.query) + expected_params = {'limit': ['3'], 'marker': [get_fake_uuid(2)]} + self.assertDictMatch(expected_params, params) + + def test_get_servers_with_limit_bad_value(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?limit=aaa') + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.index, req) + + def test_get_server_details_with_limit(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/detail?limit=3') + res = self.controller.detail(req) + + servers = res['servers'] + self.assertEqual([s['id'] for s in servers], + [get_fake_uuid(i) for i in xrange(len(servers))]) + + servers_links = res['servers_links'] + self.assertEqual(servers_links[0]['rel'], 'next') + + href_parts = urlparse.urlparse(servers_links[0]['href']) + self.assertEqual('/v1.1/fake/servers', href_parts.path) + params = urlparse.parse_qs(href_parts.query) + expected = {'limit': ['3'], 'marker': [get_fake_uuid(2)]} + self.assertDictMatch(expected, params) + + def test_get_server_details_with_limit_bad_value(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/detail?limit=aaa') + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.detail, req) + + def test_get_server_details_with_limit_and_other_params(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/detail' + '?limit=3&blah=2:t') + res = self.controller.detail(req) + + servers = res['servers'] + self.assertEqual([s['id'] for s in servers], + [get_fake_uuid(i) for i in xrange(len(servers))]) + + servers_links = res['servers_links'] + self.assertEqual(servers_links[0]['rel'], 'next') + + href_parts = urlparse.urlparse(servers_links[0]['href']) + self.assertEqual('/v1.1/fake/servers', href_parts.path) + params = urlparse.parse_qs(href_parts.query) + + self.assertDictMatch({'limit': ['3'], 'blah': ['2:t'], + 'marker': [get_fake_uuid(2)]}, params) + + def test_get_servers_with_too_big_limit(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?limit=30') + res_dict = self.controller.index(req) + self.assertTrue('servers_links' not in res_dict) + + def test_get_servers_with_bad_limit(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?limit=asdf') + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.index, req) + + def test_get_servers_with_marker(self): + url = '/v1.1/fake/servers?marker=%s' % get_fake_uuid(2) + req = fakes.HTTPRequest.blank(url) + servers = self.controller.index(req)['servers'] + self.assertEqual([s['name'] for s in servers], ["server3", "server4"]) + + def test_get_servers_with_limit_and_marker(self): + url = '/v1.1/fake/servers?limit=2&marker=%s' % get_fake_uuid(1) + req = fakes.HTTPRequest.blank(url) + servers = self.controller.index(req)['servers'] + self.assertEqual([s['name'] for s in servers], ['server2', 'server3']) + + def test_get_servers_with_bad_marker(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?limit=2&marker=asdf') + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.index, req) + + def test_get_servers_with_bad_option(self): + server_uuid = str(utils.gen_uuid()) + + def fake_get_all(compute_self, context, search_opts=None): + return [fakes.stub_instance(100, uuid=server_uuid)] + + self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?unknownoption=whee') + servers = self.controller.index(req)['servers'] + + self.assertEqual(len(servers), 1) + self.assertEqual(servers[0]['id'], server_uuid) + + def test_get_servers_allows_image(self): + server_uuid = str(utils.gen_uuid()) + + def fake_get_all(compute_self, context, search_opts=None): + self.assertNotEqual(search_opts, None) + self.assertTrue('image' in search_opts) + self.assertEqual(search_opts['image'], '12345') + return [fakes.stub_instance(100, uuid=server_uuid)] + + self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) + self.flags(allow_admin_api=False) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?image=12345') + servers = self.controller.index(req)['servers'] + + self.assertEqual(len(servers), 1) + self.assertEqual(servers[0]['id'], server_uuid) + + def test_tenant_id_filter_converts_to_project_id_for_admin(self): + def fake_get_all(context, filters=None, instances=None): + self.assertNotEqual(filters, None) + self.assertEqual(filters['project_id'], 'fake') + self.assertFalse(filters.get('tenant_id')) + return [fakes.stub_instance(100)] + + self.stubs.Set(nova.db, 'instance_get_all_by_filters', + fake_get_all) + self.flags(allow_admin_api=True) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?tenant_id=fake', + use_admin_context=True) + res = self.controller.index(req) + + self.assertTrue('servers' in res) + + def test_get_servers_allows_flavor(self): + server_uuid = str(utils.gen_uuid()) + + def fake_get_all(compute_self, context, search_opts=None): + self.assertNotEqual(search_opts, None) + self.assertTrue('flavor' in search_opts) + # flavor is an integer ID + self.assertEqual(search_opts['flavor'], '12345') + return [fakes.stub_instance(100, uuid=server_uuid)] + + self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) + self.flags(allow_admin_api=False) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?flavor=12345') + servers = self.controller.index(req)['servers'] + + self.assertEqual(len(servers), 1) + self.assertEqual(servers[0]['id'], server_uuid) + + def test_get_servers_allows_status(self): + server_uuid = str(utils.gen_uuid()) + + def fake_get_all(compute_self, context, search_opts=None): + self.assertNotEqual(search_opts, None) + self.assertTrue('vm_state' in search_opts) + self.assertEqual(search_opts['vm_state'], vm_states.ACTIVE) + return [fakes.stub_instance(100, uuid=server_uuid)] + + self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) + self.flags(allow_admin_api=False) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?status=active') + servers = self.controller.index(req)['servers'] + + self.assertEqual(len(servers), 1) + self.assertEqual(servers[0]['id'], server_uuid) + + def test_get_servers_invalid_status(self): + """Test getting servers by invalid status""" + self.flags(allow_admin_api=False) + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?status=unknown') + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index, req) + + def test_get_servers_allows_name(self): + server_uuid = str(utils.gen_uuid()) + + def fake_get_all(compute_self, context, search_opts=None): + self.assertNotEqual(search_opts, None) + self.assertTrue('name' in search_opts) + self.assertEqual(search_opts['name'], 'whee.*') + return [fakes.stub_instance(100, uuid=server_uuid)] + + self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) + self.flags(allow_admin_api=False) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?name=whee.*') + servers = self.controller.index(req)['servers'] + + self.assertEqual(len(servers), 1) + self.assertEqual(servers[0]['id'], server_uuid) + + def test_get_servers_allows_changes_since(self): + server_uuid = str(utils.gen_uuid()) + + def fake_get_all(compute_self, context, search_opts=None): + self.assertNotEqual(search_opts, None) + self.assertTrue('changes-since' in search_opts) + changes_since = datetime.datetime(2011, 1, 24, 17, 8, 1) + self.assertEqual(search_opts['changes-since'], changes_since) + self.assertTrue('deleted' not in search_opts) + return [fakes.stub_instance(100, uuid=server_uuid)] + + self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) + + params = 'changes-since=2011-01-24T17:08:01Z' + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?%s' % params) + servers = self.controller.index(req)['servers'] + + self.assertEqual(len(servers), 1) + self.assertEqual(servers[0]['id'], server_uuid) + + def test_get_servers_allows_changes_since_bad_value(self): + params = 'changes-since=asdf' + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?%s' % params) + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index, req) + + def test_get_servers_unknown_or_admin_options1(self): + """Test getting servers by admin-only or unknown options. + This tests when admin_api is off. Make sure the admin and + unknown options are stripped before they get to + compute_api.get_all() + """ + + self.flags(allow_admin_api=False) + + server_uuid = str(utils.gen_uuid()) + + def fake_get_all(compute_self, context, search_opts=None): + self.assertNotEqual(search_opts, None) + # Allowed by user + self.assertTrue('name' in search_opts) + self.assertTrue('status' in search_opts) + # Allowed only by admins with admin API on + self.assertFalse('ip' in search_opts) + self.assertFalse('unknown_option' in search_opts) + return [fakes.stub_instance(100, uuid=server_uuid)] + + self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) + + query_str = "name=foo&ip=10.*&status=active&unknown_option=meow" + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?%s' % query_str, + use_admin_context=True) + res = self.controller.index(req) + + servers = res['servers'] + self.assertEqual(len(servers), 1) + self.assertEqual(servers[0]['id'], server_uuid) + + def test_get_servers_unknown_or_admin_options2(self): + """Test getting servers by admin-only or unknown options. + This tests when admin_api is on, but context is a user. + Make sure the admin and unknown options are stripped before + they get to compute_api.get_all() + """ + + self.flags(allow_admin_api=True) + + server_uuid = str(utils.gen_uuid()) + + def fake_get_all(compute_self, context, search_opts=None): + self.assertNotEqual(search_opts, None) + # Allowed by user + self.assertTrue('name' in search_opts) + self.assertTrue('status' in search_opts) + # Allowed only by admins with admin API on + self.assertFalse('ip' in search_opts) + self.assertFalse('unknown_option' in search_opts) + return [fakes.stub_instance(100, uuid=server_uuid)] + + self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) + + query_str = "name=foo&ip=10.*&status=active&unknown_option=meow" + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?%s' % query_str) + res = self.controller.index(req) + + servers = res['servers'] + self.assertEqual(len(servers), 1) + self.assertEqual(servers[0]['id'], server_uuid) + + def test_get_servers_unknown_or_admin_options3(self): + """Test getting servers by admin-only or unknown options. + This tests when admin_api is on and context is admin. + All options should be passed through to compute_api.get_all() + """ + + self.flags(allow_admin_api=True) + + server_uuid = str(utils.gen_uuid()) + + def fake_get_all(compute_self, context, search_opts=None): + self.assertNotEqual(search_opts, None) + # Allowed by user + self.assertTrue('name' in search_opts) + self.assertTrue('status' in search_opts) + # Allowed only by admins with admin API on + self.assertTrue('ip' in search_opts) + self.assertTrue('unknown_option' in search_opts) + return [fakes.stub_instance(100, uuid=server_uuid)] + + self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) + + query_str = "name=foo&ip=10.*&status=active&unknown_option=meow" + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?%s' % query_str, + use_admin_context=True) + servers = self.controller.index(req)['servers'] + + self.assertEqual(len(servers), 1) + self.assertEqual(servers[0]['id'], server_uuid) + + def test_get_servers_admin_allows_ip(self): + """Test getting servers by ip with admin_api enabled and + admin context + """ + self.flags(allow_admin_api=True) + + server_uuid = str(utils.gen_uuid()) + + def fake_get_all(compute_self, context, search_opts=None): + self.assertNotEqual(search_opts, None) + self.assertTrue('ip' in search_opts) + self.assertEqual(search_opts['ip'], '10\..*') + return [fakes.stub_instance(100, uuid=server_uuid)] + + self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?ip=10\..*', + use_admin_context=True) + servers = self.controller.index(req)['servers'] + + self.assertEqual(len(servers), 1) + self.assertEqual(servers[0]['id'], server_uuid) + + def test_get_servers_admin_allows_ip6(self): + """Test getting servers by ip6 with admin_api enabled and + admin context + """ + self.flags(allow_admin_api=True) + + server_uuid = str(utils.gen_uuid()) + + def fake_get_all(compute_self, context, search_opts=None): + self.assertNotEqual(search_opts, None) + self.assertTrue('ip6' in search_opts) + self.assertEqual(search_opts['ip6'], 'ffff.*') + return [fakes.stub_instance(100, uuid=server_uuid)] + + self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers?ip6=ffff.*', + use_admin_context=True) + servers = self.controller.index(req)['servers'] + + self.assertEqual(len(servers), 1) + self.assertEqual(servers[0]['id'], server_uuid) + + def test_update_server_no_body(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) + req.method = 'PUT' + + self.assertRaises(webob.exc.HTTPUnprocessableEntity, + self.controller.update, req, FAKE_UUID, None) + + def test_update_server_all_attributes(self): + self.stubs.Set(nova.db, 'instance_get', + return_server_with_attributes(name='server_test', + access_ipv4='0.0.0.0', + access_ipv6='beef::0123')) + req = fakes.HTTPRequest.blank('/v1.1/123/servers/%s' % FAKE_UUID) + req.method = 'PUT' + req.content_type = 'application/json' + body = {'server': { + 'name': 'server_test', + 'accessIPv4': '0.0.0.0', + 'accessIPv6': 'beef::0123', + }} + req.body = json.dumps(body) + res_dict = self.controller.update(req, FAKE_UUID, body) + + self.assertEqual(res_dict['server']['id'], FAKE_UUID) + self.assertEqual(res_dict['server']['name'], 'server_test') + self.assertEqual(res_dict['server']['accessIPv4'], '0.0.0.0') + self.assertEqual(res_dict['server']['accessIPv6'], 'beef::0123') + + def test_update_server_name(self): + self.stubs.Set(nova.db, 'instance_get', + return_server_with_attributes(name='server_test')) + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) + req.method = 'PUT' + req.content_type = 'application/json' + body = {'server': {'name': 'server_test'}} + req.body = json.dumps(body) + res_dict = self.controller.update(req, FAKE_UUID, body) + + self.assertEqual(res_dict['server']['id'], FAKE_UUID) + self.assertEqual(res_dict['server']['name'], 'server_test') + + def test_update_server_access_ipv4(self): + self.stubs.Set(nova.db, 'instance_get', + return_server_with_attributes(access_ipv4='0.0.0.0')) + req = fakes.HTTPRequest.blank('/v1.1/123/servers/%s' % FAKE_UUID) + req.method = 'PUT' + req.content_type = 'application/json' + body = {'server': {'accessIPv4': '0.0.0.0'}} + req.body = json.dumps(body) + res_dict = self.controller.update(req, FAKE_UUID, body) + + self.assertEqual(res_dict['server']['id'], FAKE_UUID) + self.assertEqual(res_dict['server']['accessIPv4'], '0.0.0.0') + + def test_update_server_access_ipv6(self): + self.stubs.Set(nova.db, 'instance_get', + return_server_with_attributes(access_ipv6='beef::0123')) + req = fakes.HTTPRequest.blank('/v1.1/123/servers/%s' % FAKE_UUID) + req.method = 'PUT' + req.content_type = 'application/json' + body = {'server': {'accessIPv6': 'beef::0123'}} + req.body = json.dumps(body) + res_dict = self.controller.update(req, FAKE_UUID, body) + + self.assertEqual(res_dict['server']['id'], FAKE_UUID) + self.assertEqual(res_dict['server']['accessIPv6'], 'beef::0123') + + def test_update_server_adminPass_ignored(self): + inst_dict = dict(name='server_test', adminPass='bacon') + body = dict(server=inst_dict) + + def server_update(context, id, params): + filtered_dict = { + 'display_name': 'server_test', + } + self.assertEqual(params, filtered_dict) + return filtered_dict + + self.stubs.Set(nova.db, 'instance_update', server_update) + self.stubs.Set(nova.db, 'instance_get', + return_server_with_attributes(name='server_test')) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) + req.method = 'PUT' + req.content_type = "application/json" + req.body = json.dumps(body) + res_dict = self.controller.update(req, FAKE_UUID, body) + + self.assertEqual(res_dict['server']['id'], FAKE_UUID) + self.assertEqual(res_dict['server']['name'], 'server_test') + + def test_get_all_server_details(self): + expected_flavor = { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": 'http://localhost/fake/flavors/1', + }, + ], + } + expected_image = { + "id": "10", + "links": [ + { + "rel": "bookmark", + "href": 'http://localhost/fake/images/10', + }, + ], + } + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/detail') + res_dict = self.controller.detail(req) + + for i, s in enumerate(res_dict['servers']): + self.assertEqual(s['id'], get_fake_uuid(i)) + self.assertEqual(s['hostId'], '') + self.assertEqual(s['name'], 'server%d' % i) + self.assertEqual(s['image'], expected_image) + self.assertEqual(s['flavor'], expected_flavor) + self.assertEqual(s['status'], 'BUILD') + self.assertEqual(s['metadata']['seq'], str(i)) + + def test_get_all_server_details_with_host(self): + ''' + We want to make sure that if two instances are on the same host, then + they return the same hostId. If two instances are on different hosts, + they should return different hostId's. In this test, there are 5 + instances - 2 on one host and 3 on another. + ''' + + def return_servers_with_host(context, *args, **kwargs): + return [fakes.stub_instance(i, 'fake', 'fake', i % 2, + uuid=get_fake_uuid(i)) + for i in xrange(5)] + + self.stubs.Set(nova.db, 'instance_get_all_by_filters', + return_servers_with_host) + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/detail') + res_dict = self.controller.detail(req) + + server_list = res_dict['servers'] + host_ids = [server_list[0]['hostId'], server_list[1]['hostId']] + self.assertTrue(host_ids[0] and host_ids[1]) + self.assertNotEqual(host_ids[0], host_ids[1]) + + for i, s in enumerate(server_list): + self.assertEqual(s['id'], get_fake_uuid(i)) + self.assertEqual(s['hostId'], host_ids[i % 2]) + self.assertEqual(s['name'], 'server%d' % i) + + def test_delete_server_instance(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) + req.method = 'DELETE' + + self.server_delete_called = False + + def instance_destroy_mock(context, id): + self.server_delete_called = True + + self.stubs.Set(nova.db, 'instance_destroy', + instance_destroy_mock) + + self.controller.delete(req, FAKE_UUID) + + self.assertEqual(self.server_delete_called, True) + + +class ServerStatusTest(test.TestCase): + + def setUp(self): + super(ServerStatusTest, self).setUp() + fakes.stub_out_nw_api(self.stubs) + + self.controller = servers.Controller() + + def _get_with_state(self, vm_state, task_state=None): + new_server = return_server_with_state(vm_state, task_state) + self.stubs.Set(nova.db, 'instance_get_by_uuid', new_server) + self.stubs.Set(nova.db, 'instance_get', new_server) + + request = fakes.HTTPRequest.blank('/v1.1/fake/servers/%s' % FAKE_UUID) + return self.controller.show(request, FAKE_UUID) + + def test_active(self): + response = self._get_with_state(vm_states.ACTIVE) + self.assertEqual(response['server']['status'], 'ACTIVE') + + def test_reboot(self): + response = self._get_with_state(vm_states.ACTIVE, + task_states.REBOOTING) + self.assertEqual(response['server']['status'], 'REBOOT') + + def test_reboot_hard(self): + response = self._get_with_state(vm_states.ACTIVE, + task_states.REBOOTING_HARD) + self.assertEqual(response['server']['status'], 'HARD_REBOOT') + + def test_rebuild(self): + response = self._get_with_state(vm_states.REBUILDING) + self.assertEqual(response['server']['status'], 'REBUILD') + + def test_rebuild_error(self): + response = self._get_with_state(vm_states.ERROR) + self.assertEqual(response['server']['status'], 'ERROR') + + def test_resize(self): + response = self._get_with_state(vm_states.RESIZING) + self.assertEqual(response['server']['status'], 'RESIZE') + + def test_verify_resize(self): + response = self._get_with_state(vm_states.ACTIVE, + task_states.RESIZE_VERIFY) + self.assertEqual(response['server']['status'], 'VERIFY_RESIZE') + + def test_password_update(self): + response = self._get_with_state(vm_states.ACTIVE, + task_states.UPDATING_PASSWORD) + self.assertEqual(response['server']['status'], 'PASSWORD') + + def test_stopped(self): + response = self._get_with_state(vm_states.STOPPED) + self.assertEqual(response['server']['status'], 'STOPPED') + + +class ServersControllerCreateTest(test.TestCase): + + def setUp(self): + """Shared implementation for tests below that create instance""" + super(ServersControllerCreateTest, self).setUp() + + self.maxDiff = None + self.flags(verbose=True) + self.config_drive = None + self.instance_cache_num = 0 + self.instance_cache = {} + + self.controller = servers.Controller() + + def instance_create(context, inst): + inst_type = instance_types.get_instance_type_by_flavor_id(3) + image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + def_image_ref = 'http://localhost/images/%s' % image_uuid + self.instance_cache_num += 1 + instance = { + 'id': self.instance_cache_num, + 'display_name': inst['display_name'] or 'test', + 'uuid': FAKE_UUID, + 'instance_type': dict(inst_type), + 'access_ip_v4': '1.2.3.4', + 'access_ip_v6': 'fead::1234', + 'image_ref': inst.get('image_ref', def_image_ref), + 'user_id': 'fake', + 'project_id': 'fake', + 'reservation_id': inst['reservation_id'], + "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), + "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), + "config_drive": self.config_drive, + "progress": 0, + "fixed_ips": [] + } + self.instance_cache[instance['id']] = instance + return instance + + def instance_get(context, instance_id): + """Stub for compute/api create() pulling in instance after + scheduling + """ + return self.instance_cache[instance_id] + + def rpc_call_wrapper(context, topic, msg): + """Stub out the scheduler creating the instance entry""" + if topic == FLAGS.scheduler_topic and \ + msg['method'] == 'run_instance': + request_spec = msg['args']['request_spec'] + num_instances = request_spec.get('num_instances', 1) + instances = [] + for x in xrange(num_instances): + instances.append(instance_create(context, + request_spec['instance_properties'])) + return instances + + def server_update(context, instance_id, params): + inst = self.instance_cache[instance_id] + inst.update(params) + return inst + + def fake_method(*args, **kwargs): + pass + + def project_get_networks(context, user_id): + return dict(id='1', host='localhost') + + def queue_get_for(context, *args): + return 'network_topic' + + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + fakes.stub_out_key_pair_funcs(self.stubs) + fakes.stub_out_image_service(self.stubs) + fakes.stub_out_nw_api(self.stubs) + self.stubs.Set(utils, 'gen_uuid', fake_gen_uuid) + self.stubs.Set(nova.db, 'instance_add_security_group', + return_security_group) + self.stubs.Set(nova.db, 'project_get_networks', + project_get_networks) + self.stubs.Set(nova.db, 'instance_create', instance_create) + self.stubs.Set(nova.db, 'instance_get', instance_get) + self.stubs.Set(nova.rpc, 'cast', fake_method) + self.stubs.Set(nova.rpc, 'call', rpc_call_wrapper) + self.stubs.Set(nova.db, 'instance_update', server_update) + self.stubs.Set(nova.db, 'queue_get_for', queue_get_for) + self.stubs.Set(nova.network.manager.VlanManager, 'allocate_fixed_ip', + fake_method) + self.stubs.Set(nova.compute.api.API, "_find_host", find_host) + + def _test_create_instance(self): + image_uuid = 'c905cedb-7281-47e4-8a62-f26bc5fc4c77' + body = dict(server=dict( + name='server_test', imageRef=image_uuid, flavorRef=2, + metadata={'hello': 'world', 'open': 'stack'}, + personality={})) + req = fakes.HTTPRequest.blank('/v1.1/fake/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + server = self.controller.create(req, body)['server'] + + self.assertEqual(FLAGS.password_length, len(server['adminPass'])) + self.assertEqual(FAKE_UUID, server['id']) + + def test_create_multiple_instances(self): + """Test creating multiple instances but not asking for + reservation_id + """ + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + flavor_ref = 'http://localhost/123/flavors/3' + body = { + 'server': { + 'min_count': 2, + 'name': 'server_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': {'hello': 'world', + 'open': 'stack'}, + 'personality': [] + } + } + + req = fakes.HTTPRequest.blank('/v1.1/123/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res = self.controller.create(req, body) + + self.assertEqual(FAKE_UUID, res["server"]["id"]) + self.assertEqual(12, len(res["server"]["adminPass"])) + + def test_create_multiple_instances_resv_id_return(self): + """Test creating multiple instances with asking for + reservation_id + """ + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + flavor_ref = 'http://localhost/123/flavors/3' + body = { + 'server': { + 'min_count': 2, + 'name': 'server_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': {'hello': 'world', + 'open': 'stack'}, + 'personality': [], + 'return_reservation_id': True + } + } + + req = fakes.HTTPRequest.blank('/v1.1/123/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res = self.controller.create(req, body) + + reservation_id = res.get('reservation_id') + self.assertNotEqual(reservation_id, "") + self.assertNotEqual(reservation_id, None) + self.assertTrue(len(reservation_id) > 1) + + def test_create_instance_with_user_supplied_reservation_id(self): + """Non-admin supplied reservation_id should be ignored.""" + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + flavor_ref = 'http://localhost/123/flavors/3' + body = { + 'server': { + 'name': 'server_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': {'hello': 'world', + 'open': 'stack'}, + 'personality': [], + 'reservation_id': 'myresid', + 'return_reservation_id': True + } + } + + req = fakes.HTTPRequest.blank('/v1.1/123/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res = self.controller.create(req, body) + + self.assertIn('reservation_id', res) + self.assertNotEqual(res['reservation_id'], 'myresid') + + def test_create_instance_with_admin_supplied_reservation_id(self): + """Admin supplied reservation_id should be honored.""" + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + flavor_ref = 'http://localhost/123/flavors/3' + body = { + 'server': { + 'name': 'server_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': {'hello': 'world', + 'open': 'stack'}, + 'personality': [], + 'reservation_id': 'myresid', + 'return_reservation_id': True + } + } + + req = fakes.HTTPRequest.blank('/v1.1/123/servers', + use_admin_context=True) + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res = self.controller.create(req, body) + + reservation_id = res['reservation_id'] + self.assertEqual(reservation_id, "myresid") + + def test_create_instance_no_key_pair(self): + fakes.stub_out_key_pair_funcs(self.stubs, have_key_pair=False) + self._test_create_instance() + + def test_create_instance_with_access_ip(self): + # proper local hrefs must start with 'http://localhost/v1.1/' + image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + image_href = 'http://localhost/v1.1/fake/images/%s' % image_uuid + flavor_ref = 'http://localhost/fake/flavors/3' + access_ipv4 = '1.2.3.4' + access_ipv6 = 'fead::1234' + expected_flavor = { + "id": "3", + "links": [ + { + "rel": "bookmark", + "href": 'http://localhost/fake/flavors/3', + }, + ], + } + expected_image = { + "id": image_uuid, + "links": [ + { + "rel": "bookmark", + "href": 'http://localhost/fake/images/%s' % image_uuid, + }, + ], + } + body = { + 'server': { + 'name': 'server_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'accessIPv4': access_ipv4, + 'accessIPv6': access_ipv6, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': [ + { + "path": "/etc/banner.txt", + "contents": "MQ==", + }, + ], + }, + } + + req = fakes.HTTPRequest.blank('/v1.1/123/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res = self.controller.create(req, body) + + server = res['server'] + self.assertEqual(FLAGS.password_length, len(server['adminPass'])) + self.assertEqual(FAKE_UUID, server['id']) + + def test_create_instance(self): + # proper local hrefs must start with 'http://localhost/v1.1/' + image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + image_href = 'http://localhost/v1.1/images/%s' % image_uuid + flavor_ref = 'http://localhost/123/flavors/3' + body = { + 'server': { + 'name': 'server_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': [ + { + "path": "/etc/banner.txt", + "contents": "MQ==", + }, + ], + }, + } + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res = self.controller.create(req, body) + + server = res['server'] + self.assertEqual(FLAGS.password_length, len(server['adminPass'])) + self.assertEqual(FAKE_UUID, server['id']) + + def test_create_instance_invalid_key_name(self): + image_href = 'http://localhost/v1.1/images/2' + flavor_ref = 'http://localhost/flavors/3' + body = dict(server=dict( + name='server_test', imageRef=image_href, flavorRef=flavor_ref, + key_name='nonexistentkey')) + req = fakes.HTTPRequest.blank('/v1.1/fake/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.create, req, body) + + def test_create_instance_valid_key_name(self): + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + flavor_ref = 'http://localhost/flavors/3' + body = dict(server=dict( + name='server_test', imageRef=image_href, flavorRef=flavor_ref, + key_name='key')) + req = fakes.HTTPRequest.blank('/v1.1/fake/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res = self.controller.create(req, body) + + self.assertEqual(FAKE_UUID, res["server"]["id"]) + self.assertEqual(12, len(res["server"]["adminPass"])) + + def test_create_instance_invalid_flavor_href(self): + image_href = 'http://localhost/v1.1/images/2' + flavor_ref = 'http://localhost/v1.1/flavors/asdf' + body = dict(server=dict( + name='server_test', imageRef=image_href, flavorRef=flavor_ref, + metadata={'hello': 'world', 'open': 'stack'}, + personality={})) + req = fakes.HTTPRequest.blank('/v1.1/fake/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.create, req, body) + + def test_create_instance_invalid_flavor_id_int(self): + image_href = 'http://localhost/v1.1/123/images/2' + flavor_ref = -1 + body = dict(server=dict( + name='server_test', imageRef=image_href, flavorRef=flavor_ref, + metadata={'hello': 'world', 'open': 'stack'}, + personality={})) + req = fakes.HTTPRequest.blank('/v1.1/123/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.create, req, body) + + def test_create_instance_bad_flavor_href(self): + image_href = 'http://localhost/v1.1/images/2' + flavor_ref = 'http://localhost/v1.1/flavors/17' + body = dict(server=dict( + name='server_test', imageRef=image_href, flavorRef=flavor_ref, + metadata={'hello': 'world', 'open': 'stack'}, + personality={})) + req = fakes.HTTPRequest.blank('/v1.1/fake/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.create, req, body) + + def test_create_instance_with_config_drive(self): + self.config_drive = True + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + flavor_ref = 'http://localhost/v1.1/123/flavors/3' + body = { + 'server': { + 'name': 'config_drive_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': {}, + 'config_drive': True, + }, + } + + req = fakes.HTTPRequest.blank('/v1.1/123/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res = self.controller.create(req, body) + + server = res['server'] + self.assertEqual(FAKE_UUID, server['id']) + + def test_create_instance_with_config_drive_as_id(self): + self.config_drive = 2 + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + flavor_ref = 'http://localhost/v1.1/123/flavors/3' + body = { + 'server': { + 'name': 'config_drive_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': {}, + 'config_drive': image_href, + }, + } + + req = fakes.HTTPRequest.blank('/v1.1/123/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res = self.controller.create(req, body) + + server = res['server'] + self.assertEqual(FAKE_UUID, server['id']) + + def test_create_instance_with_bad_config_drive(self): + self.config_drive = "asdf" + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + flavor_ref = 'http://localhost/v1.1/123/flavors/3' + body = { + 'server': { + 'name': 'config_drive_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': {}, + 'config_drive': 'asdf', + }, + } + + req = fakes.HTTPRequest.blank('/v1.1/123/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.create, req, body) + + def test_create_instance_without_config_drive(self): + image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + flavor_ref = 'http://localhost/v1.1/123/flavors/3' + body = { + 'server': { + 'name': 'config_drive_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': { + 'hello': 'world', + 'open': 'stack', + }, + 'personality': {}, + 'config_drive': True, + }, + } + + req = fakes.HTTPRequest.blank('/v1.1/123/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res = self.controller.create(req, body) + + server = res['server'] + self.assertEqual(FAKE_UUID, server['id']) + + def test_create_instance_bad_href(self): + image_href = 'asdf' + flavor_ref = 'http://localhost/v1.1/flavors/3' + body = dict(server=dict( + name='server_test', imageRef=image_href, flavorRef=flavor_ref, + metadata={'hello': 'world', 'open': 'stack'}, + personality={})) + req = fakes.HTTPRequest.blank('/v1.1/fake/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.create, req, body) + + def test_create_instance_local_href(self): + image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + flavor_ref = 'http://localhost/v1.1/flavors/3' + body = { + 'server': { + 'name': 'server_test', + 'imageRef': image_uuid, + 'flavorRef': flavor_ref, + }, + } + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + res = self.controller.create(req, body) + + server = res['server'] + self.assertEqual(FAKE_UUID, server['id']) + + def test_create_instance_admin_pass(self): + image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + body = { + 'server': { + 'name': 'server_test', + 'imageRef': image_uuid, + 'flavorRef': 3, + 'adminPass': 'testpass', + }, + } + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers['content-type'] = "application/json" + res = self.controller.create(req, body) + + server = res['server'] + self.assertEqual(server['adminPass'], body['server']['adminPass']) + + def test_create_instance_admin_pass_empty(self): + body = { + 'server': { + 'name': 'server_test', + 'imageRef': 3, + 'flavorRef': 3, + 'adminPass': '', + }, + } + + req = fakes.HTTPRequest.blank('/v1.1/fake/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers['content-type'] = "application/json" + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.create, req, body) + + def test_create_instance_malformed_entity(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/servers') + req.method = 'POST' + body = {'server': 'string'} + req.body = json.dumps(body) + req.headers['content-type'] = "application/json" + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.create, req, body) + + +class TestServerCreateRequestXMLDeserializer(test.TestCase): + + def setUp(self): + super(TestServerCreateRequestXMLDeserializer, self).setUp() + self.deserializer = servers.ServerXMLDeserializer() + + def test_minimal_request(self): + serial_request = """ +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + }, + } + self.assertEquals(request['body'], expected) + + def test_access_ipv4(self): + serial_request = """ +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + "accessIPv4": "1.2.3.4", + }, + } + self.assertEquals(request['body'], expected) + + def test_access_ipv6(self): + serial_request = """ +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + "accessIPv6": "fead::1234", + }, + } + self.assertEquals(request['body'], expected) + + def test_access_ip(self): + serial_request = """ +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + "accessIPv4": "1.2.3.4", + "accessIPv6": "fead::1234", + }, + } + self.assertEquals(request['body'], expected) + + def test_admin_pass(self): + serial_request = """ +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + "adminPass": "1234", + }, + } + self.assertEquals(request['body'], expected) + + def test_image_link(self): + serial_request = """ +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "http://localhost:8774/v1.1/images/2", + "flavorRef": "3", + }, + } + self.assertEquals(request['body'], expected) + + def test_flavor_link(self): + serial_request = """ +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "http://localhost:8774/v1.1/flavors/3", + }, + } + self.assertEquals(request['body'], expected) + + def test_empty_metadata_personality(self): + serial_request = """ + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + "metadata": {}, + "personality": [], + }, + } + self.assertEquals(request['body'], expected) + + def test_multiple_metadata_items(self): + serial_request = """ + + + two + snack + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + "metadata": {"one": "two", "open": "snack"}, + }, + } + self.assertEquals(request['body'], expected) + + def test_multiple_personality_files(self): + serial_request = """ + + + MQ== + Mg== + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + "personality": [ + {"path": "/etc/banner.txt", "contents": "MQ=="}, + {"path": "/etc/hosts", "contents": "Mg=="}, + ], + }, + } + self.assertDictMatch(request['body'], expected) + + def test_spec_request(self): + image_bookmark_link = "http://servers.api.openstack.org/1234/" + \ + "images/52415800-8b69-11e0-9b19-734f6f006e54" + serial_request = """ + + + Apache1 + + + Mg== + +""" % (image_bookmark_link) + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "http://servers.api.openstack.org/1234/" + \ + "images/52415800-8b69-11e0-9b19-734f6f006e54", + "flavorRef": "52415800-8b69-11e0-9b19-734f1195ff37", + "metadata": {"My Server Name": "Apache1"}, + "personality": [ + { + "path": "/etc/banner.txt", + "contents": "Mg==", + }, + ], + }, + } + self.assertEquals(request['body'], expected) + + def test_request_with_empty_networks(self): + serial_request = """ + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "1", + "networks": [], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_one_network(self): + serial_request = """ + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "1", + "networks": [{"uuid": "1", "fixed_ip": "10.0.1.12"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_two_networks(self): + serial_request = """ + + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "1", + "networks": [{"uuid": "1", "fixed_ip": "10.0.1.12"}, + {"uuid": "2", "fixed_ip": "10.0.2.12"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_second_network_node_ignored(self): + serial_request = """ + + + + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "1", + "networks": [{"uuid": "1", "fixed_ip": "10.0.1.12"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_one_network_missing_id(self): + serial_request = """ + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "1", + "networks": [{"fixed_ip": "10.0.1.12"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_one_network_missing_fixed_ip(self): + serial_request = """ + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "1", + "networks": [{"uuid": "1"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_one_network_empty_id(self): + serial_request = """ + + + + + """ + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "1", + "networks": [{"uuid": "", "fixed_ip": "10.0.1.12"}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_one_network_empty_fixed_ip(self): + serial_request = """ + + + + + """ + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "1", + "networks": [{"uuid": "1", "fixed_ip": ""}], + }} + self.assertEquals(request['body'], expected) + + def test_request_with_networks_duplicate_ids(self): + serial_request = """ + + + + + + """ + request = self.deserializer.deserialize(serial_request, 'create') + expected = {"server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "1", + "networks": [{"uuid": "1", "fixed_ip": "10.0.1.12"}, + {"uuid": "1", "fixed_ip": "10.0.2.12"}], + }} + self.assertEquals(request['body'], expected) + + +class TestAddressesXMLSerialization(test.TestCase): + + serializer = nova.api.openstack.v2.ips.IPXMLSerializer() + + def test_xml_declaration(self): + fixture = { + 'network_2': [ + {'addr': '192.168.0.1', 'version': 4}, + {'addr': 'fe80::beef', 'version': 6}, + ], + } + output = self.serializer.serialize(fixture, 'show') + has_dec = output.startswith("") + self.assertTrue(has_dec) + + def test_show(self): + fixture = { + 'network_2': [ + {'addr': '192.168.0.1', 'version': 4}, + {'addr': 'fe80::beef', 'version': 6}, + ], + } + output = self.serializer.serialize(fixture, 'show') + root = etree.XML(output) + network = fixture['network_2'] + self.assertEqual(str(root.get('id')), 'network_2') + ip_elems = root.findall('{0}ip'.format(NS)) + for z, ip_elem in enumerate(ip_elems): + ip = network[z] + self.assertEqual(str(ip_elem.get('version')), + str(ip['version'])) + self.assertEqual(str(ip_elem.get('addr')), + str(ip['addr'])) + + def test_index(self): + fixture = { + 'addresses': { + 'network_1': [ + {'addr': '192.168.0.3', 'version': 4}, + {'addr': '192.168.0.5', 'version': 4}, + ], + 'network_2': [ + {'addr': '192.168.0.1', 'version': 4}, + {'addr': 'fe80::beef', 'version': 6}, + ], + }, + } + output = self.serializer.serialize(fixture, 'index') + root = etree.XML(output) + xmlutil.validate_schema(root, 'addresses') + addresses_dict = fixture['addresses'] + network_elems = root.findall('{0}network'.format(NS)) + self.assertEqual(len(network_elems), 2) + for i, network_elem in enumerate(network_elems): + network = addresses_dict.items()[i] + self.assertEqual(str(network_elem.get('id')), str(network[0])) + ip_elems = network_elem.findall('{0}ip'.format(NS)) + for z, ip_elem in enumerate(ip_elems): + ip = network[1][z] + self.assertEqual(str(ip_elem.get('version')), + str(ip['version'])) + self.assertEqual(str(ip_elem.get('addr')), + str(ip['addr'])) + + +class ServersViewBuilderTest(test.TestCase): + + def setUp(self): + super(ServersViewBuilderTest, self).setUp() + self.flags(use_ipv6=True) + self.instance = fakes.stub_instance( + id=1, + image_ref="5", + uuid="deadbeef-feed-edee-beef-d0ea7beefedd", + display_name="test_server", + public_ips=["192.168.0.3"], + private_ips=["172.19.0.1"], + include_fake_metadata=False) + + self.uuid = self.instance['uuid'] + self.view_builder = nova.api.openstack.v2.views.servers.ViewBuilder() + self.request = fakes.HTTPRequest.blank("/v1.1") + + def test_build_server(self): + self_link = "http://localhost/v1.1/fake/servers/%s" % self.uuid + bookmark_link = "http://localhost/fake/servers/%s" % self.uuid + expected_server = { + "server": { + "id": self.uuid, + "name": "test_server", + "links": [ + { + "rel": "self", + "href": self_link, + }, + { + "rel": "bookmark", + "href": bookmark_link, + }, + ], + } + } + + output = self.view_builder.basic(self.request, self.instance) + self.assertDictMatch(output, expected_server) + + def test_build_server_with_project_id(self): + expected_server = { + "server": { + "id": self.uuid, + "name": "test_server", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/fake/servers/%s" % + self.uuid, + }, + { + "rel": "bookmark", + "href": "http://localhost/fake/servers/%s" % self.uuid, + }, + ], + } + } + + output = self.view_builder.basic(self.request, self.instance) + self.assertDictMatch(output, expected_server) + + def test_build_server_detail(self): + image_bookmark = "http://localhost/fake/images/5" + flavor_bookmark = "http://localhost/fake/flavors/1" + self_link = "http://localhost/v1.1/fake/servers/%s" % self.uuid + bookmark_link = "http://localhost/fake/servers/%s" % self.uuid + expected_server = { + "server": { + "id": self.uuid, + "user_id": "fake", + "tenant_id": "fake", + "updated": "2010-11-11T11:00:00Z", + "created": "2010-10-10T12:00:00Z", + "progress": 0, + "name": "test_server", + "status": "BUILD", + "accessIPv4": "", + "accessIPv6": "", + "hostId": '', + "key_name": '', + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": image_bookmark, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": flavor_bookmark, + }, + ], + }, + "addresses": { + 'private': [ + {'version': 4, 'addr': '172.19.0.1'} + ], + 'public': [ + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, + {'version': 4, 'addr': '192.168.0.3'}, + ], + }, + "metadata": {}, + "config_drive": None, + "links": [ + { + "rel": "self", + "href": self_link, + }, + { + "rel": "bookmark", + "href": bookmark_link, + }, + ], + } + } + + output = self.view_builder.show(self.request, self.instance) + self.assertDictMatch(output, expected_server) + + def test_build_server_detail_active_status(self): + #set the power state of the instance to running + self.instance['vm_state'] = vm_states.ACTIVE + self.instance['progress'] = 100 + image_bookmark = "http://localhost/fake/images/5" + flavor_bookmark = "http://localhost/fake/flavors/1" + self_link = "http://localhost/v1.1/fake/servers/%s" % self.uuid + bookmark_link = "http://localhost/fake/servers/%s" % self.uuid + expected_server = { + "server": { + "id": self.uuid, + "user_id": "fake", + "tenant_id": "fake", + "updated": "2010-11-11T11:00:00Z", + "created": "2010-10-10T12:00:00Z", + "progress": 100, + "name": "test_server", + "status": "ACTIVE", + "accessIPv4": "", + "accessIPv6": "", + "hostId": '', + "key_name": '', + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": image_bookmark, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": flavor_bookmark, + }, + ], + }, + "addresses": { + 'private': [ + {'version': 4, 'addr': '172.19.0.1'} + ], + 'public': [ + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, + {'version': 4, 'addr': '192.168.0.3'}, + ], + }, + "metadata": {}, + "config_drive": None, + "links": [ + { + "rel": "self", + "href": self_link, + }, + { + "rel": "bookmark", + "href": bookmark_link, + }, + ], + } + } + + output = self.view_builder.show(self.request, self.instance) + self.assertDictMatch(output, expected_server) + + def test_build_server_detail_with_accessipv4(self): + + self.instance['access_ip_v4'] = '1.2.3.4' + + image_bookmark = "http://localhost/fake/images/5" + flavor_bookmark = "http://localhost/fake/flavors/1" + self_link = "http://localhost/v1.1/fake/servers/%s" % self.uuid + bookmark_link = "http://localhost/fake/servers/%s" % self.uuid + expected_server = { + "server": { + "id": self.uuid, + "user_id": "fake", + "tenant_id": "fake", + "updated": "2010-11-11T11:00:00Z", + "created": "2010-10-10T12:00:00Z", + "progress": 0, + "name": "test_server", + "key_name": "", + "status": "BUILD", + "hostId": '', + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": image_bookmark, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": flavor_bookmark, + }, + ], + }, + "addresses": { + 'private': [ + {'version': 4, 'addr': '172.19.0.1'} + ], + 'public': [ + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, + {'version': 4, 'addr': '192.168.0.3'}, + ], + }, + "metadata": {}, + "config_drive": None, + "accessIPv4": "1.2.3.4", + "accessIPv6": "", + "links": [ + { + "rel": "self", + "href": self_link, + }, + { + "rel": "bookmark", + "href": bookmark_link, + }, + ], + } + } + + output = self.view_builder.show(self.request, self.instance) + self.assertDictMatch(output, expected_server) + + def test_build_server_detail_with_accessipv6(self): + + self.instance['access_ip_v6'] = 'fead::1234' + + image_bookmark = "http://localhost/fake/images/5" + flavor_bookmark = "http://localhost/fake/flavors/1" + self_link = "http://localhost/v1.1/fake/servers/%s" % self.uuid + bookmark_link = "http://localhost/fake/servers/%s" % self.uuid + expected_server = { + "server": { + "id": self.uuid, + "user_id": "fake", + "tenant_id": "fake", + "updated": "2010-11-11T11:00:00Z", + "created": "2010-10-10T12:00:00Z", + "progress": 0, + "name": "test_server", + "key_name": "", + "status": "BUILD", + "hostId": '', + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": image_bookmark, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": flavor_bookmark, + }, + ], + }, + "addresses": { + 'private': [ + {'version': 4, 'addr': '172.19.0.1'} + ], + 'public': [ + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, + {'version': 4, 'addr': '192.168.0.3'}, + ] + }, + "metadata": {}, + "config_drive": None, + "accessIPv4": "", + "accessIPv6": "fead::1234", + "links": [ + { + "rel": "self", + "href": self_link, + }, + { + "rel": "bookmark", + "href": bookmark_link, + }, + ], + } + } + + output = self.view_builder.show(self.request, self.instance) + self.assertDictMatch(output, expected_server) + + def test_build_server_detail_with_metadata(self): + + metadata = [] + metadata.append(InstanceMetadata(key="Open", value="Stack")) + metadata.append(InstanceMetadata(key="Number", value=1)) + self.instance['metadata'] = metadata + + image_bookmark = "http://localhost/fake/images/5" + flavor_bookmark = "http://localhost/fake/flavors/1" + self_link = "http://localhost/v1.1/fake/servers/%s" % self.uuid + bookmark_link = "http://localhost/fake/servers/%s" % self.uuid + expected_server = { + "server": { + "id": self.uuid, + "user_id": "fake", + "tenant_id": "fake", + "updated": "2010-11-11T11:00:00Z", + "created": "2010-10-10T12:00:00Z", + "progress": 0, + "name": "test_server", + "status": "BUILD", + "accessIPv4": "", + "accessIPv6": "", + "hostId": '', + "key_name": '', + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": image_bookmark, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": flavor_bookmark, + }, + ], + }, + "addresses": { + 'private': [ + {'version': 4, 'addr': '172.19.0.1'} + ], + 'public': [ + {'version': 6, 'addr': 'b33f::fdee:ddff:fecc:bbaa'}, + {'version': 4, 'addr': '192.168.0.3'}, + ] + }, + "metadata": { + "Open": "Stack", + "Number": "1", + }, + "config_drive": None, + "links": [ + { + "rel": "self", + "href": self_link, + }, + { + "rel": "bookmark", + "href": bookmark_link, + }, + ], + } + } + + output = self.view_builder.show(self.request, self.instance) + self.assertDictMatch(output, expected_server) + + +class ServerXMLSerializationTest(test.TestCase): + + TIMESTAMP = "2010-10-11T10:30:22Z" + SERVER_HREF = 'http://localhost/v1.1/servers/%s' % FAKE_UUID + SERVER_NEXT = 'http://localhost/v1.1/servers?limit=%s&marker=%s' + SERVER_BOOKMARK = 'http://localhost/servers/%s' % FAKE_UUID + IMAGE_BOOKMARK = 'http://localhost/images/5' + FLAVOR_BOOKMARK = 'http://localhost/flavors/1' + + def setUp(self): + self.maxDiff = None + test.TestCase.setUp(self) + + def test_xml_declaration(self): + serializer = servers.ServerXMLSerializer() + + fixture = { + "server": { + 'id': FAKE_UUID, + 'user_id': 'fake_user_id', + 'tenant_id': 'fake_tenant_id', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + "progress": 0, + "name": "test_server", + "status": "BUILD", + "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', + "accessIPv4": "1.2.3.4", + "accessIPv6": "fead::1234", + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": self.IMAGE_BOOKMARK, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": self.FLAVOR_BOOKMARK, + }, + ], + }, + "addresses": { + "network_one": [ + { + "version": 4, + "addr": "67.23.10.138", + }, + { + "version": 6, + "addr": "::babe:67.23.10.138", + }, + ], + "network_two": [ + { + "version": 4, + "addr": "67.23.10.139", + }, + { + "version": 6, + "addr": "::babe:67.23.10.139", + }, + ], + }, + "metadata": { + "Open": "Stack", + "Number": "1", + }, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + } + } + + output = serializer.serialize(fixture, 'show') + print output + has_dec = output.startswith("") + self.assertTrue(has_dec) + + def test_show(self): + serializer = servers.ServerXMLSerializer() + + fixture = { + "server": { + "id": FAKE_UUID, + "user_id": "fake", + "tenant_id": "fake", + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + "progress": 0, + "name": "test_server", + "status": "BUILD", + "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', + "key_name": '', + "accessIPv4": "1.2.3.4", + "accessIPv6": "fead::1234", + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": self.IMAGE_BOOKMARK, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": self.FLAVOR_BOOKMARK, + }, + ], + }, + "addresses": { + "network_one": [ + { + "version": 4, + "addr": "67.23.10.138", + }, + { + "version": 6, + "addr": "::babe:67.23.10.138", + }, + ], + "network_two": [ + { + "version": 4, + "addr": "67.23.10.139", + }, + { + "version": 6, + "addr": "::babe:67.23.10.139", + }, + ], + }, + "metadata": { + "Open": "Stack", + "Number": "1", + }, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + } + } + + output = serializer.serialize(fixture, 'show') + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'server') + + expected_server_href = self.SERVER_HREF + expected_server_bookmark = self.SERVER_BOOKMARK + expected_image_bookmark = self.IMAGE_BOOKMARK + expected_flavor_bookmark = self.FLAVOR_BOOKMARK + expected_now = self.TIMESTAMP + server_dict = fixture['server'] + + for key in ['name', 'id', 'created', 'accessIPv4', + 'updated', 'progress', 'status', 'hostId', + 'accessIPv6']: + self.assertEqual(root.get(key), str(server_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(server_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = root.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + self.assertEqual(len(metadata_elems), 2) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = server_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) + + image_root = root.find('{0}image'.format(NS)) + self.assertEqual(image_root.get('id'), server_dict['image']['id']) + link_nodes = image_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['image']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + flavor_root = root.find('{0}flavor'.format(NS)) + self.assertEqual(flavor_root.get('id'), server_dict['flavor']['id']) + link_nodes = flavor_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['flavor']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + addresses_root = root.find('{0}addresses'.format(NS)) + addresses_dict = server_dict['addresses'] + network_elems = addresses_root.findall('{0}network'.format(NS)) + self.assertEqual(len(network_elems), 2) + for i, network_elem in enumerate(network_elems): + network = addresses_dict.items()[i] + self.assertEqual(str(network_elem.get('id')), str(network[0])) + ip_elems = network_elem.findall('{0}ip'.format(NS)) + for z, ip_elem in enumerate(ip_elems): + ip = network[1][z] + self.assertEqual(str(ip_elem.get('version')), + str(ip['version'])) + self.assertEqual(str(ip_elem.get('addr')), + str(ip['addr'])) + + def test_create(self): + serializer = servers.ServerXMLSerializer() + + fixture = { + "server": { + "id": FAKE_UUID, + "user_id": "fake", + "tenant_id": "fake", + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + "progress": 0, + "name": "test_server", + "status": "BUILD", + "accessIPv4": "1.2.3.4", + "accessIPv6": "fead::1234", + "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0", + "adminPass": "test_password", + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": self.IMAGE_BOOKMARK, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": self.FLAVOR_BOOKMARK, + }, + ], + }, + "addresses": { + "network_one": [ + { + "version": 4, + "addr": "67.23.10.138", + }, + { + "version": 6, + "addr": "::babe:67.23.10.138", + }, + ], + "network_two": [ + { + "version": 4, + "addr": "67.23.10.139", + }, + { + "version": 6, + "addr": "::babe:67.23.10.139", + }, + ], + }, + "metadata": { + "Open": "Stack", + "Number": "1", + }, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + } + } + + output = serializer.serialize(fixture, 'create') + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'server') + + expected_server_href = self.SERVER_HREF + expected_server_bookmark = self.SERVER_BOOKMARK + expected_image_bookmark = self.IMAGE_BOOKMARK + expected_flavor_bookmark = self.FLAVOR_BOOKMARK + expected_now = self.TIMESTAMP + server_dict = fixture['server'] + + for key in ['name', 'id', 'created', 'accessIPv4', + 'updated', 'progress', 'status', 'hostId', + 'accessIPv6', 'adminPass']: + self.assertEqual(root.get(key), str(server_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(server_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = root.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + self.assertEqual(len(metadata_elems), 2) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = server_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) + + image_root = root.find('{0}image'.format(NS)) + self.assertEqual(image_root.get('id'), server_dict['image']['id']) + link_nodes = image_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['image']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + flavor_root = root.find('{0}flavor'.format(NS)) + self.assertEqual(flavor_root.get('id'), server_dict['flavor']['id']) + link_nodes = flavor_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['flavor']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + addresses_root = root.find('{0}addresses'.format(NS)) + addresses_dict = server_dict['addresses'] + network_elems = addresses_root.findall('{0}network'.format(NS)) + self.assertEqual(len(network_elems), 2) + for i, network_elem in enumerate(network_elems): + network = addresses_dict.items()[i] + self.assertEqual(str(network_elem.get('id')), str(network[0])) + ip_elems = network_elem.findall('{0}ip'.format(NS)) + for z, ip_elem in enumerate(ip_elems): + ip = network[1][z] + self.assertEqual(str(ip_elem.get('version')), + str(ip['version'])) + self.assertEqual(str(ip_elem.get('addr')), + str(ip['addr'])) + + def test_index(self): + serializer = servers.ServerXMLSerializer() + + uuid1 = get_fake_uuid(1) + uuid2 = get_fake_uuid(2) + expected_server_href = 'http://localhost/v1.1/servers/%s' % uuid1 + expected_server_bookmark = 'http://localhost/servers/%s' % uuid1 + expected_server_href_2 = 'http://localhost/v1.1/servers/%s' % uuid2 + expected_server_bookmark_2 = 'http://localhost/servers/%s' % uuid2 + fixture = {"servers": [ + { + "id": get_fake_uuid(1), + "name": "test_server", + 'links': [ + { + 'href': expected_server_href, + 'rel': 'self', + }, + { + 'href': expected_server_bookmark, + 'rel': 'bookmark', + }, + ], + }, + { + "id": get_fake_uuid(2), + "name": "test_server_2", + 'links': [ + { + 'href': expected_server_href_2, + 'rel': 'self', + }, + { + 'href': expected_server_bookmark_2, + 'rel': 'bookmark', + }, + ], + }, + ]} + + output = serializer.serialize(fixture, 'index') + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'servers_index') + server_elems = root.findall('{0}server'.format(NS)) + self.assertEqual(len(server_elems), 2) + for i, server_elem in enumerate(server_elems): + server_dict = fixture['servers'][i] + for key in ['name', 'id']: + self.assertEqual(server_elem.get(key), str(server_dict[key])) + + link_nodes = server_elem.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(server_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + def test_index_with_servers_links(self): + serializer = servers.ServerXMLSerializer() + + uuid1 = get_fake_uuid(1) + uuid2 = get_fake_uuid(2) + expected_server_href = 'http://localhost/v1.1/servers/%s' % uuid1 + expected_server_next = self.SERVER_NEXT % (2, 2) + expected_server_bookmark = 'http://localhost/servers/%s' % uuid1 + expected_server_href_2 = 'http://localhost/v1.1/servers/%s' % uuid2 + expected_server_bookmark_2 = 'http://localhost/servers/%s' % uuid2 + fixture = {"servers": [ + { + "id": get_fake_uuid(1), + "name": "test_server", + 'links': [ + { + 'href': expected_server_href, + 'rel': 'self', + }, + { + 'href': expected_server_bookmark, + 'rel': 'bookmark', + }, + ], + }, + { + "id": get_fake_uuid(2), + "name": "test_server_2", + 'links': [ + { + 'href': expected_server_href_2, + 'rel': 'self', + }, + { + 'href': expected_server_bookmark_2, + 'rel': 'bookmark', + }, + ], + }, + ], + "servers_links": [ + { + 'rel': 'next', + 'href': expected_server_next, + }, + ]} + + output = serializer.serialize(fixture, 'index') + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'servers_index') + server_elems = root.findall('{0}server'.format(NS)) + self.assertEqual(len(server_elems), 2) + for i, server_elem in enumerate(server_elems): + server_dict = fixture['servers'][i] + for key in ['name', 'id']: + self.assertEqual(server_elem.get(key), str(server_dict[key])) + + link_nodes = server_elem.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(server_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + # Check servers_links + servers_links = root.findall('{0}link'.format(ATOMNS)) + for i, link in enumerate(fixture['servers_links']): + for key, value in link.items(): + self.assertEqual(servers_links[i].get(key), value) + + def test_detail(self): + serializer = servers.ServerXMLSerializer() + + uuid1 = get_fake_uuid(1) + expected_server_href = 'http://localhost/v1.1/servers/%s' % uuid1 + expected_server_bookmark = 'http://localhost/servers/%s' % uuid1 + expected_image_bookmark = self.IMAGE_BOOKMARK + expected_flavor_bookmark = self.FLAVOR_BOOKMARK + expected_now = self.TIMESTAMP + + uuid2 = get_fake_uuid(2) + expected_server_href_2 = 'http://localhost/v1.1/servers/%s' % uuid2 + expected_server_bookmark_2 = 'http://localhost/servers/%s' % uuid2 + fixture = {"servers": [ + { + "id": get_fake_uuid(1), + "user_id": "fake", + "tenant_id": "fake", + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + "progress": 0, + "name": "test_server", + "status": "BUILD", + "accessIPv4": "1.2.3.4", + "accessIPv6": "fead::1234", + "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": expected_image_bookmark, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": expected_flavor_bookmark, + }, + ], + }, + "addresses": { + "network_one": [ + { + "version": 4, + "addr": "67.23.10.138", + }, + { + "version": 6, + "addr": "::babe:67.23.10.138", + }, + ], + }, + "metadata": { + "Number": "1", + }, + "links": [ + { + "href": expected_server_href, + "rel": "self", + }, + { + "href": expected_server_bookmark, + "rel": "bookmark", + }, + ], + }, + { + "id": get_fake_uuid(2), + "user_id": 'fake', + "tenant_id": 'fake', + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + "progress": 100, + "name": "test_server_2", + "status": "ACTIVE", + "accessIPv4": "1.2.3.4", + "accessIPv6": "fead::1234", + "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": expected_image_bookmark, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": expected_flavor_bookmark, + }, + ], + }, + "addresses": { + "network_one": [ + { + "version": 4, + "addr": "67.23.10.138", + }, + { + "version": 6, + "addr": "::babe:67.23.10.138", + }, + ], + }, + "metadata": { + "Number": "2", + }, + "links": [ + { + "href": expected_server_href_2, + "rel": "self", + }, + { + "href": expected_server_bookmark_2, + "rel": "bookmark", + }, + ], + }, + ]} + + output = serializer.serialize(fixture, 'detail') + root = etree.XML(output) + xmlutil.validate_schema(root, 'servers') + server_elems = root.findall('{0}server'.format(NS)) + self.assertEqual(len(server_elems), 2) + for i, server_elem in enumerate(server_elems): + server_dict = fixture['servers'][i] + + for key in ['name', 'id', 'created', 'accessIPv4', + 'updated', 'progress', 'status', 'hostId', + 'accessIPv6']: + self.assertEqual(server_elem.get(key), str(server_dict[key])) + + link_nodes = server_elem.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(server_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = server_elem.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = server_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), + str(meta_value)) + + image_root = server_elem.find('{0}image'.format(NS)) + self.assertEqual(image_root.get('id'), server_dict['image']['id']) + link_nodes = image_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['image']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + flavor_root = server_elem.find('{0}flavor'.format(NS)) + self.assertEqual(flavor_root.get('id'), + server_dict['flavor']['id']) + link_nodes = flavor_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['flavor']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + addresses_root = server_elem.find('{0}addresses'.format(NS)) + addresses_dict = server_dict['addresses'] + network_elems = addresses_root.findall('{0}network'.format(NS)) + for i, network_elem in enumerate(network_elems): + network = addresses_dict.items()[i] + self.assertEqual(str(network_elem.get('id')), str(network[0])) + ip_elems = network_elem.findall('{0}ip'.format(NS)) + for z, ip_elem in enumerate(ip_elems): + ip = network[1][z] + self.assertEqual(str(ip_elem.get('version')), + str(ip['version'])) + self.assertEqual(str(ip_elem.get('addr')), + str(ip['addr'])) + + def test_update(self): + serializer = servers.ServerXMLSerializer() + + fixture = { + "server": { + "id": FAKE_UUID, + "user_id": "fake", + "tenant_id": "fake", + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + "progress": 0, + "name": "test_server", + "status": "BUILD", + "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', + "accessIPv4": "1.2.3.4", + "accessIPv6": "fead::1234", + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": self.IMAGE_BOOKMARK, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": self.FLAVOR_BOOKMARK, + }, + ], + }, + "addresses": { + "network_one": [ + { + "version": 4, + "addr": "67.23.10.138", + }, + { + "version": 6, + "addr": "::babe:67.23.10.138", + }, + ], + "network_two": [ + { + "version": 4, + "addr": "67.23.10.139", + }, + { + "version": 6, + "addr": "::babe:67.23.10.139", + }, + ], + }, + "metadata": { + "Open": "Stack", + "Number": "1", + }, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + } + } + + output = serializer.serialize(fixture, 'update') + print output + root = etree.XML(output) + xmlutil.validate_schema(root, 'server') + + expected_server_href = self.SERVER_HREF + expected_server_bookmark = self.SERVER_BOOKMARK + expected_image_bookmark = self.IMAGE_BOOKMARK + expected_flavor_bookmark = self.FLAVOR_BOOKMARK + expected_now = self.TIMESTAMP + server_dict = fixture['server'] + + for key in ['name', 'id', 'created', 'accessIPv4', + 'updated', 'progress', 'status', 'hostId', + 'accessIPv6']: + self.assertEqual(root.get(key), str(server_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(server_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = root.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + self.assertEqual(len(metadata_elems), 2) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = server_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) + + image_root = root.find('{0}image'.format(NS)) + self.assertEqual(image_root.get('id'), server_dict['image']['id']) + link_nodes = image_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['image']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + flavor_root = root.find('{0}flavor'.format(NS)) + self.assertEqual(flavor_root.get('id'), server_dict['flavor']['id']) + link_nodes = flavor_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['flavor']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + addresses_root = root.find('{0}addresses'.format(NS)) + addresses_dict = server_dict['addresses'] + network_elems = addresses_root.findall('{0}network'.format(NS)) + self.assertEqual(len(network_elems), 2) + for i, network_elem in enumerate(network_elems): + network = addresses_dict.items()[i] + self.assertEqual(str(network_elem.get('id')), str(network[0])) + ip_elems = network_elem.findall('{0}ip'.format(NS)) + for z, ip_elem in enumerate(ip_elems): + ip = network[1][z] + self.assertEqual(str(ip_elem.get('version')), + str(ip['version'])) + self.assertEqual(str(ip_elem.get('addr')), + str(ip['addr'])) + + def test_action(self): + serializer = servers.ServerXMLSerializer() + + fixture = { + "server": { + "id": FAKE_UUID, + "user_id": "fake", + "tenant_id": "fake", + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + "progress": 0, + "name": "test_server", + "status": "BUILD", + "accessIPv4": "1.2.3.4", + "accessIPv6": "fead::1234", + "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0", + "adminPass": "test_password", + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": self.IMAGE_BOOKMARK, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": self.FLAVOR_BOOKMARK, + }, + ], + }, + "addresses": { + "network_one": [ + { + "version": 4, + "addr": "67.23.10.138", + }, + { + "version": 6, + "addr": "::babe:67.23.10.138", + }, + ], + "network_two": [ + { + "version": 4, + "addr": "67.23.10.139", + }, + { + "version": 6, + "addr": "::babe:67.23.10.139", + }, + ], + }, + "metadata": { + "Open": "Stack", + "Number": "1", + }, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + } + } + + output = serializer.serialize(fixture, 'action') + root = etree.XML(output) + xmlutil.validate_schema(root, 'server') + + expected_server_href = self.SERVER_HREF + expected_server_bookmark = self.SERVER_BOOKMARK + expected_image_bookmark = self.IMAGE_BOOKMARK + expected_flavor_bookmark = self.FLAVOR_BOOKMARK + expected_now = self.TIMESTAMP + server_dict = fixture['server'] + + for key in ['name', 'id', 'created', 'accessIPv4', + 'updated', 'progress', 'status', 'hostId', + 'accessIPv6', 'adminPass']: + self.assertEqual(root.get(key), str(server_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(server_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = root.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + self.assertEqual(len(metadata_elems), 2) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = server_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) + + image_root = root.find('{0}image'.format(NS)) + self.assertEqual(image_root.get('id'), server_dict['image']['id']) + link_nodes = image_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['image']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + flavor_root = root.find('{0}flavor'.format(NS)) + self.assertEqual(flavor_root.get('id'), server_dict['flavor']['id']) + link_nodes = flavor_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['flavor']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + addresses_root = root.find('{0}addresses'.format(NS)) + addresses_dict = server_dict['addresses'] + network_elems = addresses_root.findall('{0}network'.format(NS)) + self.assertEqual(len(network_elems), 2) + for i, network_elem in enumerate(network_elems): + network = addresses_dict.items()[i] + self.assertEqual(str(network_elem.get('id')), str(network[0])) + ip_elems = network_elem.findall('{0}ip'.format(NS)) + for z, ip_elem in enumerate(ip_elems): + ip = network[1][z] + self.assertEqual(str(ip_elem.get('version')), + str(ip['version'])) + self.assertEqual(str(ip_elem.get('addr')), + str(ip['addr'])) diff --git a/nova/tests/api/openstack/v2/test_urlmap.py b/nova/tests/api/openstack/v2/test_urlmap.py new file mode 100644 index 000000000..61a237347 --- /dev/null +++ b/nova/tests/api/openstack/v2/test_urlmap.py @@ -0,0 +1,84 @@ +# 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. + +import json +import webob + +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') + + +class UrlmapTest(test.TestCase): + def setUp(self): + super(UrlmapTest, self).setUp() + fakes.stub_out_rate_limiting(self.stubs) + + def test_path_version_v1_1(self): + """Test URL path specifying v1.1 returns v1.1 content.""" + req = webob.Request.blank('/v1.1/') + req.accept = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + self.assertEqual(res.content_type, "application/json") + body = json.loads(res.body) + self.assertEqual(body['version']['id'], 'v1.1') + + def test_content_type_version_v1_1(self): + """Test Content-Type specifying v1.1 returns v1.1 content.""" + req = webob.Request.blank('/') + req.content_type = "application/json;version=1.1" + req.accept = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + self.assertEqual(res.content_type, "application/json") + body = json.loads(res.body) + self.assertEqual(body['version']['id'], 'v1.1') + + def test_accept_version_v1_1(self): + """Test Accept header specifying v1.1 returns v1.1 content.""" + req = webob.Request.blank('/') + req.accept = "application/json;version=1.1" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + self.assertEqual(res.content_type, "application/json") + body = json.loads(res.body) + self.assertEqual(body['version']['id'], 'v1.1') + + def test_path_content_type(self): + """Test URL path specifying JSON returns JSON content.""" + url = '/v1.1/foobar/images/cedef40a-ed67-4d10-800e-17455edce175.json' + req = webob.Request.blank(url) + req.accept = "application/xml" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + self.assertEqual(res.content_type, "application/json") + body = json.loads(res.body) + self.assertEqual(body['image']['id'], + 'cedef40a-ed67-4d10-800e-17455edce175') + + def test_accept_content_type(self): + """Test Accept header specifying JSON returns JSON content.""" + url = '/v1.1/foobar/images/cedef40a-ed67-4d10-800e-17455edce175' + req = webob.Request.blank(url) + req.accept = "application/xml;q=0.8, application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + self.assertEqual(res.content_type, "application/json") + body = json.loads(res.body) + self.assertEqual(body['image']['id'], + 'cedef40a-ed67-4d10-800e-17455edce175') diff --git a/nova/tests/api/openstack/v2/test_users.py b/nova/tests/api/openstack/v2/test_users.py new file mode 100644 index 000000000..454b7cae7 --- /dev/null +++ b/nova/tests/api/openstack/v2/test_users.py @@ -0,0 +1,157 @@ +# 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. + +import json + +from lxml import etree +import webob + +from nova import test +from nova import utils +from nova.api.openstack.v2 import users +from nova.auth.manager import User, Project +from nova.tests.api.openstack import fakes + + +def fake_init(self): + self.manager = fakes.FakeAuthManager() + + +def fake_admin_check(self, req): + return True + + +class UsersTest(test.TestCase): + def setUp(self): + super(UsersTest, self).setUp() + self.flags(verbose=True, allow_admin_api=True) + self.stubs.Set(users.Controller, '__init__', + fake_init) + self.stubs.Set(users.Controller, '_check_admin', + fake_admin_check) + fakes.FakeAuthManager.clear_fakes() + fakes.FakeAuthManager.projects = dict(testacct=Project('testacct', + 'testacct', + 'id1', + 'test', + [])) + fakes.FakeAuthDatabase.data = {} + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + fakes.stub_out_auth(self.stubs) + + fakemgr = fakes.FakeAuthManager() + fakemgr.add_user(User('id1', 'guy1', 'acc1', 'secret1', False)) + fakemgr.add_user(User('id2', 'guy2', 'acc2', 'secret2', True)) + + self.controller = users.Controller() + + def test_get_user_list(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/users') + res_dict = self.controller.index(req) + + self.assertEqual(len(res_dict['users']), 2) + + def test_get_user_by_id(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/users/id2') + res_dict = self.controller.show(req, 'id2') + + self.assertEqual(res_dict['user']['id'], 'id2') + self.assertEqual(res_dict['user']['name'], 'guy2') + self.assertEqual(res_dict['user']['secret'], 'secret2') + self.assertEqual(res_dict['user']['admin'], True) + + def test_user_delete(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/users/id1') + res_dict = self.controller.delete(req, 'id1') + + self.assertTrue('id1' not in [u.id for u in + fakes.FakeAuthManager.auth_data]) + + def test_user_create(self): + secret = utils.generate_password() + body = dict(user=dict(name='test_guy', + access='acc3', + secret=secret, + admin=True)) + req = fakes.HTTPRequest.blank('/v1.1/fake/users') + res_dict = self.controller.create(req, body) + + # NOTE(justinsb): This is a questionable assertion in general + # fake sets id=name, but others might not... + self.assertEqual(res_dict['user']['id'], 'test_guy') + + self.assertEqual(res_dict['user']['name'], 'test_guy') + self.assertEqual(res_dict['user']['access'], 'acc3') + self.assertEqual(res_dict['user']['secret'], secret) + self.assertEqual(res_dict['user']['admin'], True) + self.assertTrue('test_guy' in [u.id for u in + fakes.FakeAuthManager.auth_data]) + self.assertEqual(len(fakes.FakeAuthManager.auth_data), 3) + + def test_user_update(self): + new_secret = utils.generate_password() + body = dict(user=dict(name='guy2', + access='acc2', + secret=new_secret)) + + req = fakes.HTTPRequest.blank('/v1.1/fake/users/id2') + res_dict = self.controller.update(req, 'id2', body) + + self.assertEqual(res_dict['user']['id'], 'id2') + self.assertEqual(res_dict['user']['name'], 'guy2') + self.assertEqual(res_dict['user']['access'], 'acc2') + self.assertEqual(res_dict['user']['secret'], new_secret) + self.assertEqual(res_dict['user']['admin'], True) + + +class TestUsersXMLSerializer(test.TestCase): + + serializer = users.UserXMLSerializer() + + def test_index(self): + fixture = {'users': [{'id': 'id1', + 'name': 'guy1', + 'secret': 'secret1', + 'admin': False}, + {'id': 'id2', + 'name': 'guy2', + 'secret': 'secret2', + 'admin': True}]} + + output = self.serializer.serialize(fixture, 'index') + res_tree = etree.XML(output) + + self.assertEqual(res_tree.tag, 'users') + self.assertEqual(len(res_tree), 2) + self.assertEqual(res_tree[0].tag, 'user') + self.assertEqual(res_tree[0].get('id'), 'id1') + self.assertEqual(res_tree[1].tag, 'user') + self.assertEqual(res_tree[1].get('id'), 'id2') + + def test_show(self): + fixture = {'user': {'id': 'id2', + 'name': 'guy2', + 'secret': 'secret2', + 'admin': True}} + + output = self.serializer.serialize(fixture, 'show') + res_tree = etree.XML(output) + + self.assertEqual(res_tree.tag, 'user') + self.assertEqual(res_tree.get('id'), 'id2') + self.assertEqual(res_tree.get('name'), 'guy2') + self.assertEqual(res_tree.get('secret'), 'secret2') + self.assertEqual(res_tree.get('admin'), 'True') diff --git a/nova/tests/api/openstack/v2/test_versions.py b/nova/tests/api/openstack/v2/test_versions.py new file mode 100644 index 000000000..392e31a46 --- /dev/null +++ b/nova/tests/api/openstack/v2/test_versions.py @@ -0,0 +1,671 @@ +# 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 json + +import feedparser +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 import wsgi +from nova.api.openstack import xmlutil +from nova import context +from nova import test +from nova.tests.api.openstack import common +from nova.tests.api.openstack import fakes +from nova import utils + + +NS = { + 'atom': 'http://www.w3.org/2005/Atom', + 'ns': 'http://docs.openstack.org/compute/api/v1.1' +} + +VERSIONS = { + "v1.1": { + "id": "v1.1", + "status": "CURRENT", + "updated": "2011-01-21T11:33:21Z", + "links": [ + { + "rel": "describedby", + "type": "application/pdf", + "href": "http://docs.rackspacecloud.com/" + "servers/api/v1.1/cs-devguide-20110125.pdf", + }, + { + "rel": "describedby", + "type": "application/vnd.sun.wadl+xml", + "href": "http://docs.rackspacecloud.com/" + "servers/api/v1.1/application.wadl", + }, + ], + "media-types": [ + { + "base": "application/xml", + "type": "application/vnd.openstack.compute+xml;version=1.1", + }, + { + "base": "application/json", + "type": "application/vnd.openstack.compute+json;version=1.1", + }, + ], + }, +} + + +class VersionsTest(test.TestCase): + def setUp(self): + super(VersionsTest, self).setUp() + self.context = context.get_admin_context() + self.stubs = stubout.StubOutForTesting() + fakes.stub_out_auth(self.stubs) + #Stub out VERSIONS + self.old_versions = versions.VERSIONS + versions.VERSIONS = VERSIONS + + def tearDown(self): + versions.VERSIONS = self.old_versions + super(VersionsTest, self).tearDown() + + def test_get_version_list(self): + req = webob.Request.blank('/') + req.accept = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + self.assertEqual(res.content_type, "application/json") + versions = json.loads(res.body)["versions"] + expected = [ + { + "id": "v1.1", + "status": "CURRENT", + "updated": "2011-01-21T11:33:21Z", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/", + }], + }, + ] + self.assertEqual(versions, expected) + + def test_get_version_list_302(self): + req = webob.Request.blank('/v1.1') + req.accept = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 302) + redirect_req = webob.Request.blank('/v1.1/') + self.assertEqual(res.location, redirect_req.url) + + def test_get_version_1_1_detail(self): + req = webob.Request.blank('/v1.1/') + req.accept = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + self.assertEqual(res.content_type, "application/json") + version = json.loads(res.body) + expected = { + "version": { + "id": "v1.1", + "status": "CURRENT", + "updated": "2011-01-21T11:33:21Z", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/", + }, + { + "rel": "describedby", + "type": "application/pdf", + "href": "http://docs.rackspacecloud.com/" + "servers/api/v1.1/cs-devguide-20110125.pdf", + }, + { + "rel": "describedby", + "type": "application/vnd.sun.wadl+xml", + "href": "http://docs.rackspacecloud.com/" + "servers/api/v1.1/application.wadl", + }, + ], + "media-types": [ + { + "base": "application/xml", + "type": "application/" + "vnd.openstack.compute+xml;version=1.1", + }, + { + "base": "application/json", + "type": "application/" + "vnd.openstack.compute+json;version=1.1", + }, + ], + }, + } + self.assertEqual(expected, version) + + def test_get_version_1_1_detail_content_type(self): + req = webob.Request.blank('/') + req.accept = "application/json;version=1.1" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + self.assertEqual(res.content_type, "application/json") + version = json.loads(res.body) + expected = { + "version": { + "id": "v1.1", + "status": "CURRENT", + "updated": "2011-01-21T11:33:21Z", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/", + }, + { + "rel": "describedby", + "type": "application/pdf", + "href": "http://docs.rackspacecloud.com/" + "servers/api/v1.1/cs-devguide-20110125.pdf", + }, + { + "rel": "describedby", + "type": "application/vnd.sun.wadl+xml", + "href": "http://docs.rackspacecloud.com/" + "servers/api/v1.1/application.wadl", + }, + ], + "media-types": [ + { + "base": "application/xml", + "type": "application/" + "vnd.openstack.compute+xml;version=1.1", + }, + { + "base": "application/json", + "type": "application/" + "vnd.openstack.compute+json;version=1.1", + }, + ], + }, + } + self.assertEqual(expected, version) + + def test_get_version_1_1_detail_xml(self): + req = webob.Request.blank('/v1.1/') + req.accept = "application/xml" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + self.assertEqual(res.content_type, "application/xml") + + version = etree.XML(res.body) + xmlutil.validate_schema(version, 'version') + + expected = VERSIONS['v1.1'] + self.assertTrue(version.xpath('/ns:version', namespaces=NS)) + media_types = version.xpath('ns:media-types/ns:media-type', + namespaces=NS) + self.assertTrue(common.compare_media_types(media_types, + expected['media-types'])) + for key in ['id', 'status', 'updated']: + self.assertEqual(version.get(key), expected[key]) + links = version.xpath('atom:link', namespaces=NS) + self.assertTrue(common.compare_links(links, + [{'rel': 'self', 'href': 'http://localhost/v1.1/'}] + + expected['links'])) + + def test_get_version_list_xml(self): + req = webob.Request.blank('/') + req.accept = "application/xml" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + self.assertEqual(res.content_type, "application/xml") + + root = etree.XML(res.body) + print res.body + xmlutil.validate_schema(root, 'versions') + + self.assertTrue(root.xpath('/ns:versions', namespaces=NS)) + versions = root.xpath('ns:version', namespaces=NS) + self.assertEqual(len(versions), 1) + + for i, v in enumerate(['v1.1']): + version = versions[i] + expected = VERSIONS[v] + for key in ['id', 'status', 'updated']: + self.assertEqual(version.get(key), expected[key]) + (link,) = version.xpath('atom:link', namespaces=NS) + self.assertTrue(common.compare_links(link, + [{'rel': 'self', 'href': 'http://localhost/%s/' % v}])) + + def test_get_version_1_1_detail_atom(self): + req = webob.Request.blank('/v1.1/') + req.accept = "application/atom+xml" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + self.assertEqual("application/atom+xml", res.content_type) + + xmlutil.validate_schema(etree.XML(res.body), 'atom') + + f = feedparser.parse(res.body) + self.assertEqual(f.feed.title, 'About This Version') + self.assertEqual(f.feed.updated, '2011-01-21T11:33:21Z') + self.assertEqual(f.feed.id, 'http://localhost/v1.1/') + self.assertEqual(f.feed.author, 'Rackspace') + self.assertEqual(f.feed.author_detail.href, + 'http://www.rackspace.com/') + self.assertEqual(f.feed.links[0]['href'], 'http://localhost/v1.1/') + self.assertEqual(f.feed.links[0]['rel'], 'self') + + self.assertEqual(len(f.entries), 1) + entry = f.entries[0] + self.assertEqual(entry.id, 'http://localhost/v1.1/') + self.assertEqual(entry.title, 'Version v1.1') + self.assertEqual(entry.updated, '2011-01-21T11:33:21Z') + self.assertEqual(len(entry.content), 1) + self.assertEqual(entry.content[0].value, + 'Version v1.1 CURRENT (2011-01-21T11:33:21Z)') + self.assertEqual(len(entry.links), 3) + self.assertEqual(entry.links[0]['href'], 'http://localhost/v1.1/') + self.assertEqual(entry.links[0]['rel'], 'self') + self.assertEqual(entry.links[1], { + 'href': 'http://docs.rackspacecloud.com/servers/api/v1.1/'\ + 'cs-devguide-20110125.pdf', + 'type': 'application/pdf', + 'rel': 'describedby'}) + self.assertEqual(entry.links[2], { + 'href': 'http://docs.rackspacecloud.com/servers/api/v1.1/'\ + 'application.wadl', + 'type': 'application/vnd.sun.wadl+xml', + 'rel': 'describedby'}) + + def test_get_version_list_atom(self): + req = webob.Request.blank('/') + req.accept = "application/atom+xml" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) + self.assertEqual(res.content_type, "application/atom+xml") + + f = feedparser.parse(res.body) + self.assertEqual(f.feed.title, 'Available API Versions') + self.assertEqual(f.feed.updated, '2011-01-21T11:33:21Z') + self.assertEqual(f.feed.id, 'http://localhost/') + self.assertEqual(f.feed.author, 'Rackspace') + self.assertEqual(f.feed.author_detail.href, + 'http://www.rackspace.com/') + self.assertEqual(f.feed.links[0]['href'], 'http://localhost/') + self.assertEqual(f.feed.links[0]['rel'], 'self') + + self.assertEqual(len(f.entries), 1) + entry = f.entries[0] + self.assertEqual(entry.id, 'http://localhost/v1.1/') + self.assertEqual(entry.title, 'Version v1.1') + self.assertEqual(entry.updated, '2011-01-21T11:33:21Z') + self.assertEqual(len(entry.content), 1) + self.assertEqual(entry.content[0].value, + 'Version v1.1 CURRENT (2011-01-21T11:33:21Z)') + self.assertEqual(len(entry.links), 1) + self.assertEqual(entry.links[0]['href'], 'http://localhost/v1.1/') + self.assertEqual(entry.links[0]['rel'], 'self') + + def test_multi_choice_image(self): + req = webob.Request.blank('/images/1') + req.accept = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 300) + self.assertEqual(res.content_type, "application/json") + + expected = { + "choices": [ + { + "id": "v1.1", + "status": "CURRENT", + "links": [ + { + "href": "http://localhost/v1.1/images/1", + "rel": "self", + }, + ], + "media-types": [ + { + "base": "application/xml", + "type": "application/vnd.openstack.compute+xml" + ";version=1.1" + }, + { + "base": "application/json", + "type": "application/vnd.openstack.compute+json" + ";version=1.1" + }, + ], + }, + ], } + + self.assertDictMatch(expected, json.loads(res.body)) + + def test_multi_choice_image_xml(self): + req = webob.Request.blank('/images/1') + req.accept = "application/xml" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 300) + self.assertEqual(res.content_type, "application/xml") + + root = etree.XML(res.body) + self.assertTrue(root.xpath('/ns:choices', namespaces=NS)) + versions = root.xpath('ns:version', namespaces=NS) + self.assertEqual(len(versions), 1) + + version = versions[0] + self.assertEqual(version.get('id'), 'v1.1') + self.assertEqual(version.get('status'), 'CURRENT') + media_types = version.xpath('ns:media-types/ns:media-type', + namespaces=NS) + self.assertTrue(common.compare_media_types(media_types, + VERSIONS['v1.1']['media-types'])) + links = version.xpath('atom:link', namespaces=NS) + self.assertTrue(common.compare_links(links, + [{'rel': 'self', 'href': 'http://localhost/v1.1/images/1'}])) + + def test_multi_choice_server_atom(self): + """ + Make sure multi choice responses do not have content-type + application/atom+xml (should use default of json) + """ + req = webob.Request.blank('/servers') + req.accept = "application/atom+xml" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 300) + self.assertEqual(res.content_type, "application/json") + + def test_multi_choice_server(self): + uuid = str(utils.gen_uuid()) + req = webob.Request.blank('/servers/' + uuid) + req.accept = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 300) + self.assertEqual(res.content_type, "application/json") + + expected = { + "choices": [ + { + "id": "v1.1", + "status": "CURRENT", + "links": [ + { + "href": "http://localhost/v1.1/servers/" + uuid, + "rel": "self", + }, + ], + "media-types": [ + { + "base": "application/xml", + "type": "application/vnd.openstack.compute+xml" + ";version=1.1" + }, + { + "base": "application/json", + "type": "application/vnd.openstack.compute+json" + ";version=1.1" + }, + ], + }, + ], } + + self.assertDictMatch(expected, json.loads(res.body)) + + +class VersionsViewBuilderTests(test.TestCase): + def test_view_builder(self): + base_url = "http://example.org/" + + version_data = { + "v3.2.1": { + "id": "3.2.1", + "status": "CURRENT", + "updated": "2011-07-18T11:30:00Z", + } + } + + expected = { + "versions": [ + { + "id": "3.2.1", + "status": "CURRENT", + "updated": "2011-07-18T11:30:00Z", + "links": [ + { + "rel": "self", + "href": "http://example.org/3.2.1/", + }, + ], + } + ] + } + + builder = views.versions.ViewBuilder(base_url) + output = builder.build_versions(version_data) + + self.assertEqual(output, expected) + + def test_generate_href(self): + base_url = "http://example.org/app/" + version_number = "v1.4.6" + + expected = "http://example.org/app/v1.4.6/" + + builder = views.versions.ViewBuilder(base_url) + actual = builder.generate_href(version_number) + + self.assertEqual(actual, expected) + + +class VersionsSerializerTests(test.TestCase): + def test_versions_list_xml_serializer(self): + versions_data = { + 'versions': [ + { + "id": "2.7.1", + "updated": "2011-07-18T11:30:00Z", + "status": "DEPRECATED", + "links": [ + { + "rel": "self", + "href": "http://test/2.7.1", + }, + ], + }, + ] + } + + serializer = versions.VersionsXMLSerializer() + response = serializer.index(versions_data) + + root = etree.XML(response) + xmlutil.validate_schema(root, 'versions') + + self.assertTrue(root.xpath('/ns:versions', namespaces=NS)) + version_elems = root.xpath('ns:version', namespaces=NS) + self.assertEqual(len(version_elems), 1) + version = version_elems[0] + self.assertEqual(version.get('id'), versions_data['versions'][0]['id']) + self.assertEqual(version.get('status'), + versions_data['versions'][0]['status']) + + (link,) = version.xpath('atom:link', namespaces=NS) + self.assertTrue(common.compare_links(link, [{ + 'rel': 'self', + 'href': 'http://test/2.7.1', + 'type': 'application/atom+xml'}])) + + def test_versions_multi_xml_serializer(self): + versions_data = { + 'choices': [ + { + "id": "2.7.1", + "updated": "2011-07-18T11:30:00Z", + "status": "DEPRECATED", + "media-types": VERSIONS['v1.1']['media-types'], + "links": [ + { + "rel": "self", + "href": "http://test/2.7.1/images", + }, + ], + }, + ] + } + + serializer = versions.VersionsXMLSerializer() + response = serializer.multi(versions_data) + + root = etree.XML(response) + self.assertTrue(root.xpath('/ns:choices', namespaces=NS)) + (version,) = root.xpath('ns:version', namespaces=NS) + self.assertEqual(version.get('id'), versions_data['choices'][0]['id']) + self.assertEqual(version.get('status'), + versions_data['choices'][0]['status']) + + media_types = list(version)[0] + media_type_nodes = list(media_types) + self.assertEqual(media_types.tag.split('}')[1], "media-types") + + media_types = version.xpath('ns:media-types/ns:media-type', + namespaces=NS) + self.assertTrue(common.compare_media_types(media_types, + versions_data['choices'][0]['media-types'])) + + (link,) = version.xpath('atom:link', namespaces=NS) + self.assertTrue(common.compare_links(link, + versions_data['choices'][0]['links'])) + + def test_versions_list_atom_serializer(self): + versions_data = { + 'versions': [ + { + "id": "2.9.8", + "updated": "2011-07-20T11:40:00Z", + "status": "CURRENT", + "links": [ + { + "rel": "self", + "href": "http://test/2.9.8", + }, + ], + }, + ] + } + + serializer = versions.VersionsAtomSerializer() + response = serializer.index(versions_data) + f = feedparser.parse(response) + + self.assertEqual(f.feed.title, 'Available API Versions') + self.assertEqual(f.feed.updated, '2011-07-20T11:40:00Z') + self.assertEqual(f.feed.id, 'http://test/') + self.assertEqual(f.feed.author, 'Rackspace') + self.assertEqual(f.feed.author_detail.href, + 'http://www.rackspace.com/') + self.assertEqual(f.feed.links[0]['href'], 'http://test/') + self.assertEqual(f.feed.links[0]['rel'], 'self') + + self.assertEqual(len(f.entries), 1) + entry = f.entries[0] + self.assertEqual(entry.id, 'http://test/2.9.8') + self.assertEqual(entry.title, 'Version 2.9.8') + self.assertEqual(entry.updated, '2011-07-20T11:40:00Z') + self.assertEqual(len(entry.content), 1) + self.assertEqual(entry.content[0].value, + 'Version 2.9.8 CURRENT (2011-07-20T11:40:00Z)') + self.assertEqual(len(entry.links), 1) + self.assertEqual(entry.links[0]['href'], 'http://test/2.9.8') + self.assertEqual(entry.links[0]['rel'], 'self') + + def test_version_detail_atom_serializer(self): + versions_data = { + "version": { + "id": "v1.1", + "status": "CURRENT", + "updated": "2011-01-21T11:33:21Z", + "links": [ + { + "rel": "self", + "href": "http://localhost/v1.1/", + }, + { + "rel": "describedby", + "type": "application/pdf", + "href": "http://docs.rackspacecloud.com/" + "servers/api/v1.1/cs-devguide-20110125.pdf", + }, + { + "rel": "describedby", + "type": "application/vnd.sun.wadl+xml", + "href": "http://docs.rackspacecloud.com/" + "servers/api/v1.1/application.wadl", + }, + ], + "media-types": [ + { + "base": "application/xml", + "type": "application/vnd.openstack.compute+xml" + ";version=1.1", + }, + { + "base": "application/json", + "type": "application/vnd.openstack.compute+json" + ";version=1.1", + } + ], + }, + } + + serializer = versions.VersionsAtomSerializer() + response = serializer.show(versions_data) + f = feedparser.parse(response) + + self.assertEqual(f.feed.title, 'About This Version') + self.assertEqual(f.feed.updated, '2011-01-21T11:33:21Z') + self.assertEqual(f.feed.id, 'http://localhost/v1.1/') + self.assertEqual(f.feed.author, 'Rackspace') + self.assertEqual(f.feed.author_detail.href, + 'http://www.rackspace.com/') + self.assertEqual(f.feed.links[0]['href'], 'http://localhost/v1.1/') + self.assertEqual(f.feed.links[0]['rel'], 'self') + + self.assertEqual(len(f.entries), 1) + entry = f.entries[0] + self.assertEqual(entry.id, 'http://localhost/v1.1/') + self.assertEqual(entry.title, 'Version v1.1') + self.assertEqual(entry.updated, '2011-01-21T11:33:21Z') + self.assertEqual(len(entry.content), 1) + self.assertEqual(entry.content[0].value, + 'Version v1.1 CURRENT (2011-01-21T11:33:21Z)') + self.assertEqual(len(entry.links), 3) + self.assertEqual(entry.links[0]['href'], 'http://localhost/v1.1/') + self.assertEqual(entry.links[0]['rel'], 'self') + self.assertEqual(entry.links[1], { + 'rel': 'describedby', + 'type': 'application/pdf', + 'href': 'http://docs.rackspacecloud.com/' + 'servers/api/v1.1/cs-devguide-20110125.pdf'}) + self.assertEqual(entry.links[2], { + 'rel': 'describedby', + 'type': 'application/vnd.sun.wadl+xml', + 'href': 'http://docs.rackspacecloud.com/' + 'servers/api/v1.1/application.wadl', + }) diff --git a/nova/tests/api/openstack/v2/test_zones.py b/nova/tests/api/openstack/v2/test_zones.py new file mode 100644 index 000000000..4a3e3ed47 --- /dev/null +++ b/nova/tests/api/openstack/v2/test_zones.py @@ -0,0 +1,283 @@ +# 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. + + +import json + +from lxml import etree +import stubout +import webob + +from nova.api.openstack.v2 import zones +from nova.api.openstack import xmlutil +from nova import context +from nova import crypto +import nova.db +from nova import flags +from nova.scheduler import api +from nova import test +from nova.tests.api.openstack import fakes + + +FLAGS = flags.FLAGS + + +def zone_get(context, zone_id): + return dict(id=1, api_url='http://example.com', username='bob', + password='xxx', weight_scale=1.0, weight_offset=0.0, + name='darksecret') + + +def zone_create(context, values): + zone = dict(id=1) + zone.update(values) + return zone + + +def zone_update(context, zone_id, values): + zone = dict(id=zone_id, api_url='http://example.com', username='bob', + password='xxx') + zone.update(values) + return zone + + +def zone_delete(context, zone_id): + pass + + +def zone_get_all_scheduler(*args): + return [ + dict(id=1, api_url='http://example.com', username='bob', + password='xxx', weight_scale=1.0, weight_offset=0.0), + dict(id=2, api_url='http://example.org', username='alice', + password='qwerty', weight_scale=1.0, weight_offset=0.0), + ] + + +def zone_get_all_scheduler_empty(*args): + return [] + + +def zone_get_all_db(context): + return [ + dict(id=1, api_url='http://example.com', username='bob', + password='xxx', weight_scale=1.0, weight_offset=0.0), + dict(id=2, api_url='http://example.org', username='alice', + password='qwerty', weight_scale=1.0, weight_offset=0.0), + ] + + +def zone_capabilities(method, context): + return dict() + + +GLOBAL_BUILD_PLAN = [ + dict(name='host1', weight=10, ip='10.0.0.1', zone='zone1'), + dict(name='host2', weight=9, ip='10.0.0.2', zone='zone2'), + dict(name='host3', weight=8, ip='10.0.0.3', zone='zone3'), + dict(name='host4', weight=7, ip='10.0.0.4', zone='zone4'), + ] + + +def zone_select(context, specs): + return GLOBAL_BUILD_PLAN + + +class ZonesTest(test.TestCase): + def setUp(self): + super(ZonesTest, self).setUp() + self.flags(verbose=True, allow_admin_api=True) + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + + self.stubs.Set(nova.db, 'zone_get', zone_get) + self.stubs.Set(nova.db, 'zone_update', zone_update) + self.stubs.Set(nova.db, 'zone_create', zone_create) + self.stubs.Set(nova.db, 'zone_delete', zone_delete) + + self.controller = zones.Controller() + + def test_get_zone_list_scheduler(self): + self.stubs.Set(api, '_call_scheduler', zone_get_all_scheduler) + + req = fakes.HTTPRequest.blank('/v1.1/fake/zones') + res_dict = self.controller.index(req) + + self.assertEqual(len(res_dict['zones']), 2) + + def test_get_zone_list_db(self): + self.stubs.Set(api, '_call_scheduler', zone_get_all_scheduler_empty) + self.stubs.Set(nova.db, 'zone_get_all', zone_get_all_db) + + req = fakes.HTTPRequest.blank('/v1.1/fake/zones') + res_dict = self.controller.index(req) + + self.assertEqual(len(res_dict['zones']), 2) + + def test_get_zone_by_id(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/zones/1') + res_dict = self.controller.show(req, 1) + + self.assertEqual(res_dict['zone']['id'], 1) + self.assertEqual(res_dict['zone']['api_url'], 'http://example.com') + self.assertFalse('password' in res_dict['zone']) + + def test_zone_delete(self): + req = fakes.HTTPRequest.blank('/v1.1/fake/zones/1') + self.controller.delete(req, 1) + + def test_zone_create(self): + body = dict(zone=dict(api_url='http://example.com', username='fred', + password='fubar')) + + req = fakes.HTTPRequest.blank('/v1.1/fake/zones') + res_dict = self.controller.create(req, body) + + self.assertEqual(res_dict['zone']['id'], 1) + self.assertEqual(res_dict['zone']['api_url'], 'http://example.com') + self.assertFalse('username' in res_dict['zone']) + + def test_zone_update(self): + body = dict(zone=dict(username='zeb', password='sneaky')) + + req = fakes.HTTPRequest.blank('/v1.1/fake/zones/1') + res_dict = self.controller.update(req, 1, body) + + self.assertEqual(res_dict['zone']['id'], 1) + self.assertEqual(res_dict['zone']['api_url'], 'http://example.com') + self.assertFalse('username' in res_dict['zone']) + + def test_zone_info(self): + caps = ['cap1=a;b', 'cap2=c;d'] + self.flags(zone_name='darksecret', zone_capabilities=caps) + self.stubs.Set(api, '_call_scheduler', zone_capabilities) + + req = fakes.HTTPRequest.blank('/v1.1/fake/zones/info') + res_dict = self.controller.info(req) + + self.assertEqual(res_dict['zone']['name'], 'darksecret') + self.assertEqual(res_dict['zone']['cap1'], 'a;b') + self.assertEqual(res_dict['zone']['cap2'], 'c;d') + + def test_zone_select(self): + key = 'c286696d887c9aa0611bbb3e2025a45a' + self.flags(build_plan_encryption_key=key) + self.stubs.Set(api, 'select', zone_select) + + # Select queries end up being JSON encoded twice. + # Once to a string and again as an HTTP POST Body + body = json.dumps({}) + + req = fakes.HTTPRequest.blank('/v1.1/fake/zones/select') + res_dict = self.controller.select(req, body) + + self.assertTrue('weights' in res_dict) + + for item in res_dict['weights']: + blob = item['blob'] + decrypt = crypto.decryptor(FLAGS.build_plan_encryption_key) + secret_item = json.loads(decrypt(blob)) + found = False + for original_item in GLOBAL_BUILD_PLAN: + if original_item['name'] != secret_item['name']: + continue + found = True + for key in ('weight', 'ip', 'zone'): + self.assertEqual(secret_item[key], original_item[key]) + + self.assertTrue(found) + self.assertEqual(len(item), 2) + self.assertTrue('weight' in item) + + +class TestZonesXMLSerializer(test.TestCase): + + serializer = zones.ZonesXMLSerializer() + + def test_select(self): + key = 'c286696d887c9aa0611bbb3e2025a45a' + + encrypt = crypto.encryptor(key) + decrypt = crypto.decryptor(key) + + item = GLOBAL_BUILD_PLAN[0] + fixture = {'weights': {'blob': encrypt(json.dumps(item)), + 'weight': item['weight']}} + + output = self.serializer.serialize(fixture, 'select') + res_tree = etree.XML(output) + + self.assertEqual(res_tree.tag, '{%s}weights' % xmlutil.XMLNS_V10) + + for item in res_tree: + self.assertEqual(item.tag, '{%s}weight' % xmlutil.XMLNS_V10) + blob = None + weight = None + for chld in item: + if chld.tag.endswith('blob'): + blob = chld.text + elif chld.tag.endswith('weight'): + weight = chld.text + + secret_item = json.loads(decrypt(blob)) + found = False + for original_item in GLOBAL_BUILD_PLAN: + if original_item['name'] != secret_item['name']: + continue + found = True + for key in ('weight', 'ip', 'zone'): + self.assertEqual(secret_item[key], original_item[key]) + + self.assertTrue(found) + self.assertEqual(len(item), 2) + self.assertTrue(weight) + + def test_index(self): + fixture = {'zones': zone_get_all_scheduler()} + + output = self.serializer.serialize(fixture, 'index') + res_tree = etree.XML(output) + + self.assertEqual(res_tree.tag, '{%s}zones' % xmlutil.XMLNS_V10) + self.assertEqual(len(res_tree), 2) + self.assertEqual(res_tree[0].tag, '{%s}zone' % xmlutil.XMLNS_V10) + self.assertEqual(res_tree[1].tag, '{%s}zone' % xmlutil.XMLNS_V10) + + def test_show(self): + zone = {'id': 1, + 'api_url': 'http://example.com', + 'name': 'darksecret', + 'cap1': 'a;b', + 'cap2': 'c;d'} + fixture = {'zone': zone} + + output = self.serializer.serialize(fixture, 'show') + print repr(output) + res_tree = etree.XML(output) + + self.assertEqual(res_tree.tag, '{%s}zone' % xmlutil.XMLNS_V10) + self.assertEqual(res_tree.get('id'), '1') + self.assertEqual(res_tree.get('api_url'), 'http://example.com') + self.assertEqual(res_tree.get('password'), None) + + self.assertEqual(res_tree.get('name'), 'darksecret') + for elem in res_tree: + self.assertEqual(elem.tag in ('{%s}cap1' % xmlutil.XMLNS_V10, + '{%s}cap2' % xmlutil.XMLNS_V10), + True) + if elem.tag == '{%s}cap1' % xmlutil.XMLNS_V10: + self.assertEqual(elem.text, 'a;b') + elif elem.tag == '{%s}cap2' % xmlutil.XMLNS_V10: + self.assertEqual(elem.text, 'c;d') diff --git a/nova/tests/integrated/test_extensions.py b/nova/tests/integrated/test_extensions.py index 958f584ab..4318e30f2 100644 --- a/nova/tests/integrated/test_extensions.py +++ b/nova/tests/integrated/test_extensions.py @@ -30,7 +30,7 @@ class ExtensionsTest(integrated_helpers._IntegratedTestBase): def _get_flags(self): f = super(ExtensionsTest, self)._get_flags() f['osapi_extension'] = FLAGS.osapi_extension[:] - f['osapi_extension'].append('nova.tests.api.openstack.extensions.' + f['osapi_extension'].append('nova.tests.api.openstack.v2.extensions.' 'foxinsocks.Foxinsocks') return f diff --git a/nova/tests/test_hosts.py b/nova/tests/test_hosts.py index becc74cdd..32fb5d2cf 100644 --- a/nova/tests/test_hosts.py +++ b/nova/tests/test_hosts.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import stubout import webob.exc from nova import context @@ -21,7 +20,7 @@ from nova import exception from nova import flags from nova import log as logging from nova import test -from nova.api.openstack.contrib import hosts as os_hosts +from nova.api.openstack.v2.contrib import hosts as os_hosts from nova.scheduler import api as scheduler_api -- cgit