From 8fb9f78a313a43f333d20c7cc600a5085eb68915 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 4 Oct 2010 21:53:22 +0200 Subject: Replace the embarrasingly crude string based tests for to_xml with some more sensible ElementTree based stuff. --- nova/tests/virt_unittest.py | 63 ++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 21 deletions(-) (limited to 'nova/tests') diff --git a/nova/tests/virt_unittest.py b/nova/tests/virt_unittest.py index 2aab16809..998cc07db 100644 --- a/nova/tests/virt_unittest.py +++ b/nova/tests/virt_unittest.py @@ -14,36 +14,49 @@ # License for the specific language governing permissions and limitations # under the License. +from xml.etree.ElementTree import fromstring as parseXml + from nova import flags from nova import test from nova.virt import libvirt_conn FLAGS = flags.FLAGS - class LibvirtConnTestCase(test.TrialTestCase): def test_get_uri_and_template(self): - class MockDataModel(object): - def __init__(self): - self.datamodel = { 'name' : 'i-cafebabe', - 'memory_kb' : '1024000', - 'basepath' : '/some/path', - 'bridge_name' : 'br100', - 'mac_address' : '02:12:34:46:56:67', - 'vcpus' : 2 } + instance = { 'name' : 'i-cafebabe', + 'id' : 'i-cafebabe', + 'memory_kb' : '1024000', + 'basepath' : '/some/path', + 'bridge_name' : 'br100', + 'mac_address' : '02:12:34:46:56:67', + 'vcpus' : 2, + 'project_id' : 'fake', + 'ip_address' : '10.11.12.13', + 'bridge' : 'br101', + 'instance_type' : 'm1.small'} type_uri_map = { 'qemu' : ('qemu:///system', - [lambda s: '' in s, - lambda s: 'type>hvm/usr/bin/kvm' not in s]), + [(lambda t: t.find('.').tag, 'domain'), + (lambda t: t.find('.').get('type'), 'qemu'), + (lambda t: t.find('./os/type').text, 'hvm'), + (lambda t: t.find('./devices/emulator'), None)]), 'kvm' : ('qemu:///system', - [lambda s: '' in s, - lambda s: 'type>hvm/usr/bin/qemu<' not in s]), + [(lambda t: t.find('.').tag, 'domain'), + (lambda t: t.find('.').get('type'), 'kvm'), + (lambda t: t.find('./os/type').text, 'hvm'), + (lambda t: t.find('./devices/emulator'), None)]), 'uml' : ('uml:///system', - [lambda s: '' in s, - lambda s: 'type>uml Date: Tue, 5 Oct 2010 10:07:37 +0200 Subject: Create and destroy user appropriately. Remove security group related tests (since they haven't been merged yet). --- nova/tests/virt_unittest.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'nova/tests') diff --git a/nova/tests/virt_unittest.py b/nova/tests/virt_unittest.py index 998cc07db..730928f39 100644 --- a/nova/tests/virt_unittest.py +++ b/nova/tests/virt_unittest.py @@ -18,11 +18,20 @@ from xml.etree.ElementTree import fromstring as parseXml from nova import flags from nova import test +from nova.auth import manager +# Needed to get FLAGS.instances_path defined: +from nova.compute import manager as compute_manager from nova.virt import libvirt_conn FLAGS = flags.FLAGS class LibvirtConnTestCase(test.TrialTestCase): + def setUp(self): + self.manager = manager.AuthManager() + self.user = self.manager.create_user('fake', 'fake', 'fake', admin=True) + self.project = self.manager.create_project('fake', 'fake', 'fake') + FLAGS.instances_path = '' + def test_get_uri_and_template(self): instance = { 'name' : 'i-cafebabe', 'id' : 'i-cafebabe', @@ -51,12 +60,6 @@ class LibvirtConnTestCase(test.TrialTestCase): (lambda t: t.find('.').get('type'), 'uml'), (lambda t: t.find('./os/type').text, 'uml')]), } - common_checks = [(lambda t: \ - t.find('./devices/interface/filterref/parameter') \ - .get('name'), 'IP'), - (lambda t: \ - t.find('./devices/interface/filterref/parameter') \ - .get('value'), '10.11.12.13')] for (libvirt_type,(expected_uri, checks)) in type_uri_map.iteritems(): FLAGS.libvirt_type = libvirt_type @@ -72,11 +75,6 @@ class LibvirtConnTestCase(test.TrialTestCase): expected_result, '%s failed check %d' % (xml, i)) - for i, (check, expected_result) in enumerate(common_checks): - self.assertEqual(check(tree), - expected_result, - '%s failed common check %d' % (xml, i)) - # Deliberately not just assigning this string to FLAGS.libvirt_uri and # checking against that later on. This way we make sure the # implementation doesn't fiddle around with the FLAGS. @@ -88,3 +86,7 @@ class LibvirtConnTestCase(test.TrialTestCase): uri, template = conn.get_uri_and_template() self.assertEquals(uri, testuri) + + def tearDown(self): + self.manager.delete_project(self.project) + self.manager.delete_user(self.user) -- cgit From db87fd5a8145d045c4767a8d02cde5a0750113f8 Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Fri, 8 Oct 2010 12:21:26 -0400 Subject: Remove redis dependency from Images controller. LocalImageService now works with integer ids, so there's no need for the translator. Once Glance exists we'll have to revisit this. --- nova/tests/api/rackspace/fakes.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/tests') diff --git a/nova/tests/api/rackspace/fakes.py b/nova/tests/api/rackspace/fakes.py index b5fba2dfa..6a25720a9 100644 --- a/nova/tests/api/rackspace/fakes.py +++ b/nova/tests/api/rackspace/fakes.py @@ -28,7 +28,6 @@ from nova import utils from nova import flags from nova import exception as exc import nova.api.rackspace.auth -import nova.api.rackspace._id_translator from nova.image import service from nova.wsgi import Router -- cgit From 90f38090ecd586a39257b3efd2c86c2c60b7fdb9 Mon Sep 17 00:00:00 2001 From: mdietz Date: Fri, 8 Oct 2010 20:39:00 +0000 Subject: Mass renaming --- nova/tests/api/__init__.py | 6 +- nova/tests/api/openstack/__init__.py | 108 ++++++++++ nova/tests/api/openstack/fakes.py | 205 +++++++++++++++++++ nova/tests/api/openstack/test_auth.py | 108 ++++++++++ nova/tests/api/openstack/test_faults.py | 40 ++++ nova/tests/api/openstack/test_flavors.py | 48 +++++ nova/tests/api/openstack/test_images.py | 141 ++++++++++++++ nova/tests/api/openstack/test_ratelimiting.py | 237 ++++++++++++++++++++++ nova/tests/api/openstack/test_servers.py | 249 ++++++++++++++++++++++++ nova/tests/api/openstack/test_sharedipgroups.py | 39 ++++ nova/tests/api/rackspace/__init__.py | 108 ---------- nova/tests/api/rackspace/fakes.py | 205 ------------------- nova/tests/api/rackspace/test_auth.py | 108 ---------- nova/tests/api/rackspace/test_faults.py | 40 ---- nova/tests/api/rackspace/test_flavors.py | 48 ----- nova/tests/api/rackspace/test_images.py | 141 -------------- nova/tests/api/rackspace/test_servers.py | 249 ------------------------ nova/tests/api/rackspace/test_sharedipgroups.py | 39 ---- 18 files changed, 1178 insertions(+), 941 deletions(-) create mode 100644 nova/tests/api/openstack/__init__.py create mode 100644 nova/tests/api/openstack/fakes.py create mode 100644 nova/tests/api/openstack/test_auth.py create mode 100644 nova/tests/api/openstack/test_faults.py create mode 100644 nova/tests/api/openstack/test_flavors.py create mode 100644 nova/tests/api/openstack/test_images.py create mode 100644 nova/tests/api/openstack/test_ratelimiting.py create mode 100644 nova/tests/api/openstack/test_servers.py create mode 100644 nova/tests/api/openstack/test_sharedipgroups.py delete mode 100644 nova/tests/api/rackspace/__init__.py delete mode 100644 nova/tests/api/rackspace/fakes.py delete mode 100644 nova/tests/api/rackspace/test_auth.py delete mode 100644 nova/tests/api/rackspace/test_faults.py delete mode 100644 nova/tests/api/rackspace/test_flavors.py delete mode 100644 nova/tests/api/rackspace/test_images.py delete mode 100644 nova/tests/api/rackspace/test_servers.py delete mode 100644 nova/tests/api/rackspace/test_sharedipgroups.py (limited to 'nova/tests') diff --git a/nova/tests/api/__init__.py b/nova/tests/api/__init__.py index ec76aa827..2c7f7fd3e 100644 --- a/nova/tests/api/__init__.py +++ b/nova/tests/api/__init__.py @@ -44,8 +44,8 @@ class Test(unittest.TestCase): req = webob.Request.blank(url, environ_keys) return req.get_response(api.API()) - def test_rackspace(self): - self.stubs.Set(api.rackspace, 'API', APIStub) + def test_openstack(self): + self.stubs.Set(api.openstack, 'API', APIStub) result = self._request('/v1.0/cloud', 'rs') self.assertEqual(result.body, "/cloud") @@ -56,7 +56,7 @@ class Test(unittest.TestCase): def test_not_found(self): self.stubs.Set(api.ec2, 'API', APIStub) - self.stubs.Set(api.rackspace, 'API', APIStub) + self.stubs.Set(api.openstack, 'API', APIStub) result = self._request('/test/cloud', 'ec2') self.assertNotEqual(result.body, "/cloud") diff --git a/nova/tests/api/openstack/__init__.py b/nova/tests/api/openstack/__init__.py new file mode 100644 index 000000000..b534897f5 --- /dev/null +++ b/nova/tests/api/openstack/__init__.py @@ -0,0 +1,108 @@ +# 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 unittest + +from nova.api.openstack import limited +from nova.api.openstack import RateLimitingMiddleware +from nova.tests.api.fakes import APIStub +from webob import Request + + +class RateLimitingMiddlewareTest(unittest.TestCase): + + def test_get_action_name(self): + middleware = RateLimitingMiddleware(APIStub()) + def verify(method, url, action_name): + req = Request.blank(url) + req.method = method + action = middleware.get_action_name(req) + self.assertEqual(action, action_name) + verify('PUT', '/servers/4', 'PUT') + verify('DELETE', '/servers/4', 'DELETE') + verify('POST', '/images/4', 'POST') + verify('POST', '/servers/4', 'POST servers') + verify('GET', '/foo?a=4&changes-since=never&b=5', 'GET changes-since') + verify('GET', '/foo?a=4&monkeys-since=never&b=5', None) + verify('GET', '/servers/4', None) + verify('HEAD', '/servers/4', None) + + def exhaust(self, middleware, method, url, username, times): + req = Request.blank(url, dict(REQUEST_METHOD=method), + headers={'X-Auth-User': username}) + for i in range(times): + resp = req.get_response(middleware) + self.assertEqual(resp.status_int, 200) + resp = req.get_response(middleware) + self.assertEqual(resp.status_int, 413) + self.assertTrue('Retry-After' in resp.headers) + + def test_single_action(self): + middleware = RateLimitingMiddleware(APIStub()) + self.exhaust(middleware, 'DELETE', '/servers/4', 'usr1', 100) + self.exhaust(middleware, 'DELETE', '/servers/4', 'usr2', 100) + + def test_POST_servers_action_implies_POST_action(self): + middleware = RateLimitingMiddleware(APIStub()) + self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 10) + self.exhaust(middleware, 'POST', '/images/4', 'usr2', 10) + self.assertTrue(set(middleware.limiter._levels) == + set(['usr1:POST', 'usr1:POST servers', 'usr2:POST'])) + + def test_POST_servers_action_correctly_ratelimited(self): + middleware = RateLimitingMiddleware(APIStub()) + # Use up all of our "POST" allowance for the minute, 5 times + for i in range(5): + self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 10) + # Reset the 'POST' action counter. + del middleware.limiter._levels['usr1:POST'] + # All 50 daily "POST servers" actions should be all used up + self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 0) + + def test_proxy_ctor_works(self): + middleware = RateLimitingMiddleware(APIStub()) + self.assertEqual(middleware.limiter.__class__.__name__, "Limiter") + middleware = RateLimitingMiddleware(APIStub(), service_host='foobar') + self.assertEqual(middleware.limiter.__class__.__name__, "WSGIAppProxy") + + +class LimiterTest(unittest.TestCase): + + def test_limiter(self): + items = range(2000) + req = Request.blank('/') + self.assertEqual(limited(items, req), items[ :1000]) + req = Request.blank('/?offset=0') + self.assertEqual(limited(items, req), items[ :1000]) + req = Request.blank('/?offset=3') + self.assertEqual(limited(items, req), items[3:1003]) + req = Request.blank('/?offset=2005') + self.assertEqual(limited(items, req), []) + req = Request.blank('/?limit=10') + self.assertEqual(limited(items, req), items[ :10]) + req = Request.blank('/?limit=0') + self.assertEqual(limited(items, req), items[ :1000]) + req = Request.blank('/?limit=3000') + self.assertEqual(limited(items, req), items[ :1000]) + req = Request.blank('/?offset=1&limit=3') + self.assertEqual(limited(items, req), items[1:4]) + req = Request.blank('/?offset=3&limit=0') + self.assertEqual(limited(items, req), items[3:1003]) + req = Request.blank('/?offset=3&limit=1500') + self.assertEqual(limited(items, req), items[3:1003]) + req = Request.blank('/?offset=3000&limit=10') + self.assertEqual(limited(items, req), []) diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py new file mode 100644 index 000000000..1119fa714 --- /dev/null +++ b/nova/tests/api/openstack/fakes.py @@ -0,0 +1,205 @@ +# 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 datetime +import json +import random +import string + +import webob +import webob.dec + +from nova import auth +from nova import utils +from nova import flags +from nova import exception as exc +import nova.api.openstack.auth +from nova.image import service +from nova.wsgi import Router + + +FLAGS = flags.FLAGS + + +class Context(object): + pass + + +class FakeRouter(Router): + def __init__(self): + pass + + @webob.dec.wsgify + def __call__(self, req): + res = webob.Response() + res.status = '200' + res.headers['X-Test-Success'] = 'True' + return res + + +def fake_auth_init(self): + self.db = FakeAuthDatabase() + self.context = Context() + self.auth = FakeAuthManager() + self.host = 'foo' + + +@webob.dec.wsgify +def fake_wsgi(self, req): + req.environ['nova.context'] = dict(user=dict(id=1)) + if req.body: + req.environ['inst_dict'] = json.loads(req.body) + return self.application + + +def stub_out_key_pair_funcs(stubs): + def key_pair(context, user_id): + return [dict(name='key', public_key='public_key')] + stubs.Set(nova.db.api, 'key_pair_get_all_by_user', + key_pair) + + +def stub_out_image_service(stubs): + def fake_image_show(meh, id): + return dict(kernelId=1, ramdiskId=1) + + stubs.Set(nova.image.service.LocalImageService, 'show', fake_image_show) + +def stub_out_auth(stubs): + def fake_auth_init(self, app): + self.application = app + + stubs.Set(nova.api.openstack.AuthMiddleware, + '__init__', fake_auth_init) + stubs.Set(nova.api.openstack.AuthMiddleware, + '__call__', fake_wsgi) + + +def stub_out_rate_limiting(stubs): + def fake_rate_init(self, app): + super(nova.api.openstack.RateLimitingMiddleware, self).__init__(app) + self.application = app + + stubs.Set(nova.api.openstack.RateLimitingMiddleware, + '__init__', fake_rate_init) + + stubs.Set(nova.api.openstack.RateLimitingMiddleware, + '__call__', fake_wsgi) + + +def stub_out_networking(stubs): + def get_my_ip(): + return '127.0.0.1' + stubs.Set(nova.utils, 'get_my_ip', get_my_ip) + FLAGS.FAKE_subdomain = 'rs' + + +def stub_out_glance(stubs): + + class FakeParallaxClient: + + def __init__(self): + self.fixtures = {} + + def fake_get_images(self): + return self.fixtures + + def fake_get_image_metadata(self, image_id): + for k, f in self.fixtures.iteritems(): + if k == image_id: + return f + return None + + def fake_add_image_metadata(self, image_data): + id = ''.join(random.choice(string.letters) for _ in range(20)) + image_data['id'] = id + self.fixtures[id] = image_data + return id + + def fake_update_image_metadata(self, image_id, image_data): + + if image_id not in self.fixtures.keys(): + raise exc.NotFound + + self.fixtures[image_id].update(image_data) + + def fake_delete_image_metadata(self, image_id): + + if image_id not in self.fixtures.keys(): + raise exc.NotFound + + del self.fixtures[image_id] + + def fake_delete_all(self): + self.fixtures = {} + + fake_parallax_client = FakeParallaxClient() + stubs.Set(nova.image.service.ParallaxClient, 'get_images', + fake_parallax_client.fake_get_images) + stubs.Set(nova.image.service.ParallaxClient, 'get_image_metadata', + fake_parallax_client.fake_get_image_metadata) + stubs.Set(nova.image.service.ParallaxClient, 'add_image_metadata', + fake_parallax_client.fake_add_image_metadata) + stubs.Set(nova.image.service.ParallaxClient, 'update_image_metadata', + fake_parallax_client.fake_update_image_metadata) + stubs.Set(nova.image.service.ParallaxClient, 'delete_image_metadata', + fake_parallax_client.fake_delete_image_metadata) + stubs.Set(nova.image.service.GlanceImageService, 'delete_all', + fake_parallax_client.fake_delete_all) + + +class FakeAuthDatabase(object): + data = {} + + @staticmethod + def auth_get_token(context, token_hash): + return FakeAuthDatabase.data.get(token_hash, None) + + @staticmethod + def auth_create_token(context, token): + token['created_at'] = datetime.datetime.now() + FakeAuthDatabase.data[token['token_hash']] = token + + @staticmethod + def auth_destroy_token(context, token): + if FakeAuthDatabase.data.has_key(token['token_hash']): + del FakeAuthDatabase.data['token_hash'] + + +class FakeAuthManager(object): + auth_data = {} + + def add_user(self, key, user): + FakeAuthManager.auth_data[key] = user + + def get_user(self, uid): + for k, v in FakeAuthManager.auth_data.iteritems(): + if v['uid'] == uid: + return v + return None + + def get_user_from_access_key(self, key): + return FakeAuthManager.auth_data.get(key, None) + + +class FakeRateLimiter(object): + def __init__(self, application): + self.application = application + + @webob.dec.wsgify + def __call__(self, req): + return self.application diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py new file mode 100644 index 000000000..d2ba80243 --- /dev/null +++ b/nova/tests/api/openstack/test_auth.py @@ -0,0 +1,108 @@ +import datetime +import unittest + +import stubout +import webob +import webob.dec + +import nova.api +import nova.api.openstack.auth +from nova import auth +from nova.tests.api.openstack import fakes + +class Test(unittest.TestCase): + def setUp(self): + self.stubs = stubout.StubOutForTesting() + self.stubs.Set(nova.api.openstack.auth.BasicApiAuthManager, + '__init__', fakes.fake_auth_init) + fakes.FakeAuthManager.auth_data = {} + fakes.FakeAuthDatabase.data = {} + fakes.stub_out_rate_limiting(self.stubs) + fakes.stub_out_networking(self.stubs) + + def tearDown(self): + self.stubs.UnsetAll() + fakes.fake_data_store = {} + + def test_authorize_user(self): + f = fakes.FakeAuthManager() + f.add_user('derp', { 'uid': 1, 'name':'herp' } ) + + req = webob.Request.blank('/v1.0/') + req.headers['X-Auth-User'] = 'herp' + req.headers['X-Auth-Key'] = 'derp' + result = req.get_response(nova.api.API()) + self.assertEqual(result.status, '204 No Content') + self.assertEqual(len(result.headers['X-Auth-Token']), 40) + self.assertEqual(result.headers['X-CDN-Management-Url'], + "") + self.assertEqual(result.headers['X-Storage-Url'], "") + + def test_authorize_token(self): + f = fakes.FakeAuthManager() + f.add_user('derp', { 'uid': 1, 'name':'herp' } ) + + req = webob.Request.blank('/v1.0/') + req.headers['X-Auth-User'] = 'herp' + req.headers['X-Auth-Key'] = 'derp' + result = req.get_response(nova.api.API()) + self.assertEqual(result.status, '204 No Content') + self.assertEqual(len(result.headers['X-Auth-Token']), 40) + self.assertEqual(result.headers['X-Server-Management-Url'], + "https://foo/v1.0/") + self.assertEqual(result.headers['X-CDN-Management-Url'], + "") + self.assertEqual(result.headers['X-Storage-Url'], "") + + token = result.headers['X-Auth-Token'] + self.stubs.Set(nova.api.openstack, 'APIRouter', + fakes.FakeRouter) + req = webob.Request.blank('/v1.0/fake') + req.headers['X-Auth-Token'] = token + result = req.get_response(nova.api.API()) + self.assertEqual(result.status, '200 OK') + self.assertEqual(result.headers['X-Test-Success'], 'True') + + def test_token_expiry(self): + self.destroy_called = False + token_hash = 'bacon' + + def destroy_token_mock(meh, context, token): + self.destroy_called = True + + def bad_token(meh, context, token_hash): + return { 'token_hash':token_hash, + 'created_at':datetime.datetime(1990, 1, 1) } + + self.stubs.Set(fakes.FakeAuthDatabase, 'auth_destroy_token', + destroy_token_mock) + + self.stubs.Set(fakes.FakeAuthDatabase, 'auth_get_token', + bad_token) + + req = webob.Request.blank('/v1.0/') + req.headers['X-Auth-Token'] = 'bacon' + result = req.get_response(nova.api.API()) + self.assertEqual(result.status, '401 Unauthorized') + self.assertEqual(self.destroy_called, True) + + def test_bad_user(self): + req = webob.Request.blank('/v1.0/') + req.headers['X-Auth-User'] = 'herp' + req.headers['X-Auth-Key'] = 'derp' + result = req.get_response(nova.api.API()) + self.assertEqual(result.status, '401 Unauthorized') + + def test_no_user(self): + req = webob.Request.blank('/v1.0/') + result = req.get_response(nova.api.API()) + self.assertEqual(result.status, '401 Unauthorized') + + def test_bad_token(self): + req = webob.Request.blank('/v1.0/') + req.headers['X-Auth-Token'] = 'baconbaconbacon' + result = req.get_response(nova.api.API()) + self.assertEqual(result.status, '401 Unauthorized') + +if __name__ == '__main__': + unittest.main() diff --git a/nova/tests/api/openstack/test_faults.py b/nova/tests/api/openstack/test_faults.py new file mode 100644 index 000000000..70a811469 --- /dev/null +++ b/nova/tests/api/openstack/test_faults.py @@ -0,0 +1,40 @@ +import unittest +import webob +import webob.dec +import webob.exc + +from nova.api.openstack import faults + +class TestFaults(unittest.TestCase): + + def test_fault_parts(self): + req = webob.Request.blank('/.xml') + f = faults.Fault(webob.exc.HTTPBadRequest(explanation='scram')) + resp = req.get_response(f) + + first_two_words = resp.body.strip().split()[:2] + self.assertEqual(first_two_words, ['']) + body_without_spaces = ''.join(resp.body.split()) + self.assertTrue('scram' in body_without_spaces) + + def test_retry_header(self): + req = webob.Request.blank('/.xml') + exc = webob.exc.HTTPRequestEntityTooLarge(explanation='sorry', + headers={'Retry-After': 4}) + f = faults.Fault(exc) + resp = req.get_response(f) + first_two_words = resp.body.strip().split()[:2] + self.assertEqual(first_two_words, ['']) + body_sans_spaces = ''.join(resp.body.split()) + self.assertTrue('sorry' in body_sans_spaces) + self.assertTrue('4' in body_sans_spaces) + self.assertEqual(resp.headers['Retry-After'], 4) + + def test_raise(self): + @webob.dec.wsgify + def raiser(req): + raise faults.Fault(webob.exc.HTTPNotFound(explanation='whut?')) + req = webob.Request.blank('/.xml') + resp = req.get_response(raiser) + self.assertEqual(resp.status_int, 404) + self.assertTrue('whut?' in resp.body) diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py new file mode 100644 index 000000000..8dd4d1f29 --- /dev/null +++ b/nova/tests/api/openstack/test_flavors.py @@ -0,0 +1,48 @@ +# 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 unittest + +import stubout +import webob + +import nova.api +from nova.api.openstack import flavors +from nova.tests.api.openstack import fakes + + +class FlavorsTest(unittest.TestCase): + def setUp(self): + self.stubs = stubout.StubOutForTesting() + fakes.FakeAuthManager.auth_data = {} + fakes.FakeAuthDatabase.data = {} + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + fakes.stub_out_auth(self.stubs) + + def tearDown(self): + self.stubs.UnsetAll() + + def test_get_flavor_list(self): + req = webob.Request.blank('/v1.0/flavors') + res = req.get_response(nova.api.API()) + + def test_get_flavor_by_id(self): + pass + +if __name__ == '__main__': + unittest.main() diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py new file mode 100644 index 000000000..505fea3e2 --- /dev/null +++ b/nova/tests/api/openstack/test_images.py @@ -0,0 +1,141 @@ +# 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 logging +import unittest + +import stubout + +from nova import exception +from nova import utils +from nova.api.openstack import images +from nova.tests.api.openstack import fakes + + +class BaseImageServiceTests(): + + """Tasks to test for all image services""" + + def test_create(self): + + fixture = {'name': 'test image', + 'updated': None, + 'created': None, + 'status': None, + 'serverId': None, + 'progress': None} + + num_images = len(self.service.index()) + + id = self.service.create(fixture) + + self.assertNotEquals(None, id) + self.assertEquals(num_images + 1, len(self.service.index())) + + def test_create_and_show_non_existing_image(self): + + fixture = {'name': 'test image', + 'updated': None, + 'created': None, + 'status': None, + 'serverId': None, + 'progress': None} + + num_images = len(self.service.index()) + + id = self.service.create(fixture) + + self.assertNotEquals(None, id) + + self.assertRaises(exception.NotFound, + self.service.show, + 'bad image id') + + def test_update(self): + + fixture = {'name': 'test image', + 'updated': None, + 'created': None, + 'status': None, + 'serverId': None, + 'progress': None} + + id = self.service.create(fixture) + + fixture['status'] = 'in progress' + + self.service.update(id, fixture) + new_image_data = self.service.show(id) + self.assertEquals('in progress', new_image_data['status']) + + def test_delete(self): + + fixtures = [ + {'name': 'test image 1', + 'updated': None, + 'created': None, + 'status': None, + 'serverId': None, + 'progress': None}, + {'name': 'test image 2', + 'updated': None, + 'created': None, + 'status': None, + 'serverId': None, + 'progress': None}] + + ids = [] + for fixture in fixtures: + new_id = self.service.create(fixture) + ids.append(new_id) + + num_images = len(self.service.index()) + self.assertEquals(2, num_images) + + self.service.delete(ids[0]) + + num_images = len(self.service.index()) + self.assertEquals(1, num_images) + + +class LocalImageServiceTest(unittest.TestCase, + BaseImageServiceTests): + + """Tests the local image service""" + + def setUp(self): + self.stubs = stubout.StubOutForTesting() + self.service = utils.import_object('nova.image.service.LocalImageService') + + def tearDown(self): + self.service.delete_all() + self.stubs.UnsetAll() + + +class GlanceImageServiceTest(unittest.TestCase, + BaseImageServiceTests): + + """Tests the local image service""" + + def setUp(self): + self.stubs = stubout.StubOutForTesting() + fakes.stub_out_glance(self.stubs) + self.service = utils.import_object('nova.image.service.GlanceImageService') + + def tearDown(self): + self.service.delete_all() + self.stubs.UnsetAll() diff --git a/nova/tests/api/openstack/test_ratelimiting.py b/nova/tests/api/openstack/test_ratelimiting.py new file mode 100644 index 000000000..ad9e67454 --- /dev/null +++ b/nova/tests/api/openstack/test_ratelimiting.py @@ -0,0 +1,237 @@ +import httplib +import StringIO +import time +import unittest +import webob + +import nova.api.openstack.ratelimiting as ratelimiting + +class LimiterTest(unittest.TestCase): + + def setUp(self): + self.limits = { + 'a': (5, ratelimiting.PER_SECOND), + 'b': (5, ratelimiting.PER_MINUTE), + 'c': (5, ratelimiting.PER_HOUR), + 'd': (1, ratelimiting.PER_SECOND), + 'e': (100, ratelimiting.PER_SECOND)} + self.rl = ratelimiting.Limiter(self.limits) + + def exhaust(self, action, times_until_exhausted, **kwargs): + for i in range(times_until_exhausted): + when = self.rl.perform(action, **kwargs) + self.assertEqual(when, None) + num, period = self.limits[action] + delay = period * 1.0 / num + # Verify that we are now thoroughly delayed + for i in range(10): + when = self.rl.perform(action, **kwargs) + self.assertAlmostEqual(when, delay, 2) + + def test_second(self): + self.exhaust('a', 5) + time.sleep(0.2) + self.exhaust('a', 1) + time.sleep(1) + self.exhaust('a', 5) + + def test_minute(self): + self.exhaust('b', 5) + + def test_one_per_period(self): + def allow_once_and_deny_once(): + when = self.rl.perform('d') + self.assertEqual(when, None) + when = self.rl.perform('d') + self.assertAlmostEqual(when, 1, 2) + return when + time.sleep(allow_once_and_deny_once()) + time.sleep(allow_once_and_deny_once()) + allow_once_and_deny_once() + + def test_we_can_go_indefinitely_if_we_spread_out_requests(self): + for i in range(200): + when = self.rl.perform('e') + self.assertEqual(when, None) + time.sleep(0.01) + + def test_users_get_separate_buckets(self): + self.exhaust('c', 5, username='alice') + self.exhaust('c', 5, username='bob') + self.exhaust('c', 5, username='chuck') + self.exhaust('c', 0, username='chuck') + self.exhaust('c', 0, username='bob') + self.exhaust('c', 0, username='alice') + + +class FakeLimiter(object): + """Fake Limiter class that you can tell how to behave.""" + def __init__(self, test): + self._action = self._username = self._delay = None + self.test = test + def mock(self, action, username, delay): + self._action = action + self._username = username + self._delay = delay + def perform(self, action, username): + self.test.assertEqual(action, self._action) + self.test.assertEqual(username, self._username) + return self._delay + + +class WSGIAppTest(unittest.TestCase): + + def setUp(self): + self.limiter = FakeLimiter(self) + self.app = ratelimiting.WSGIApp(self.limiter) + + def test_invalid_methods(self): + requests = [] + for method in ['GET', 'PUT', 'DELETE']: + req = webob.Request.blank('/limits/michael/breakdance', + dict(REQUEST_METHOD=method)) + requests.append(req) + for req in requests: + self.assertEqual(req.get_response(self.app).status_int, 405) + + def test_invalid_urls(self): + requests = [] + for prefix in ['limit', '', 'limiter2', 'limiter/limits', 'limiter/1']: + req = webob.Request.blank('/%s/michael/breakdance' % prefix, + dict(REQUEST_METHOD='POST')) + requests.append(req) + for req in requests: + self.assertEqual(req.get_response(self.app).status_int, 404) + + def verify(self, url, username, action, delay=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. + """ + req = webob.Request.blank(url, dict(REQUEST_METHOD='POST')) + self.limiter.mock(action, username, delay) + resp = req.get_response(self.app) + if not delay: + self.assertEqual(resp.status_int, 200) + else: + self.assertEqual(resp.status_int, 403) + self.assertEqual(resp.headers['X-Wait-Seconds'], "%.2f" % delay) + + def test_good_urls(self): + self.verify('/limiter/michael/hoot', 'michael', 'hoot') + + def test_escaping(self): + self.verify('/limiter/michael/jump%20up', 'michael', 'jump up') + + def test_response_to_delays(self): + self.verify('/limiter/michael/hoot', 'michael', 'hoot', 1) + self.verify('/limiter/michael/hoot', 'michael', 'hoot', 1.56) + self.verify('/limiter/michael/hoot', 'michael', 'hoot', 1000) + + +class FakeHttplibSocket(object): + """a fake socket implementation for httplib.HTTPResponse, trivial""" + + def __init__(self, response_string): + self._buffer = StringIO.StringIO(response_string) + + def makefile(self, _mode, _other): + """Returns the socket's internal buffer""" + return self._buffer + + +class FakeHttplibConnection(object): + """A fake httplib.HTTPConnection + + 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. + """ + def __init__(self, app, host, is_secure=False): + self.app = app + self.host = host + + def request(self, method, path, data='', headers={}): + req = webob.Request.blank(path) + req.method = method + req.body = data + req.headers = headers + req.host = self.host + # Call the WSGI app, get the HTTP response + resp = str(req.get_response(self.app)) + # For some reason, the response doesn't have "HTTP/1.0 " prepended; I + # guess that's a function the web server usually provides. + resp = "HTTP/1.0 %s" % resp + sock = FakeHttplibSocket(resp) + self.http_response = httplib.HTTPResponse(sock) + self.http_response.begin() + + def getresponse(self): + 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 WSGIAppProxyTest(unittest.TestCase): + + def setUp(self): + """Our WSGIAppProxy is going to call across an HTTPConnection to a + WSGIApp running a limiter. The proxy will send input, and the proxy + should receive that same input, pass it to the limiter who gives a + result, and send the expected result back. + + The HTTPConnection isn't real -- it's monkeypatched to point straight + at the WSGIApp. And the limiter isn't real -- it's a fake that + behaves the way we tell it to. + """ + self.limiter = FakeLimiter(self) + app = ratelimiting.WSGIApp(self.limiter) + wire_HTTPConnection_to_WSGI('100.100.100.100:80', app) + self.proxy = ratelimiting.WSGIAppProxy('100.100.100.100:80') + + def test_200(self): + self.limiter.mock('conquer', 'caesar', None) + when = self.proxy.perform('conquer', 'caesar') + self.assertEqual(when, None) + + def test_403(self): + self.limiter.mock('grumble', 'proletariat', 1.5) + when = self.proxy.perform('grumble', 'proletariat') + self.assertEqual(when, 1.5) + + def test_failure(self): + def shouldRaise(): + self.limiter.mock('murder', 'brutus', None) + self.proxy.perform('stab', 'brutus') + self.assertRaises(AssertionError, shouldRaise) + + +if __name__ == '__main__': + unittest.main() diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py new file mode 100644 index 000000000..d1ee533b6 --- /dev/null +++ b/nova/tests/api/openstack/test_servers.py @@ -0,0 +1,249 @@ +# 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 unittest + +import stubout +import webob + +from nova import db +from nova import flags +import nova.api.openstack +from nova.api.openstack import servers +import nova.db.api +from nova.db.sqlalchemy.models import Instance +import nova.rpc +from nova.tests.api.openstack import fakes + + +FLAGS = flags.FLAGS + +FLAGS.verbose = True + +def return_server(context, id): + return stub_instance(id) + + +def return_servers(context, user_id=1): + return [stub_instance(i, user_id) for i in xrange(5)] + + +def stub_instance(id, user_id=1): + return Instance( + id=id, state=0, image_id=10, server_name='server%s'%id, + user_id=user_id + ) + + +class ServersTest(unittest.TestCase): + def setUp(self): + self.stubs = stubout.StubOutForTesting() + fakes.FakeAuthManager.auth_data = {} + fakes.FakeAuthDatabase.data = {} + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + fakes.stub_out_auth(self.stubs) + fakes.stub_out_key_pair_funcs(self.stubs) + fakes.stub_out_image_service(self.stubs) + self.stubs.Set(nova.db.api, 'instance_get_all', return_servers) + self.stubs.Set(nova.db.api, 'instance_get_by_internal_id', return_server) + self.stubs.Set(nova.db.api, 'instance_get_all_by_user', + return_servers) + + def tearDown(self): + self.stubs.UnsetAll() + + def test_get_server_by_id(self): + req = webob.Request.blank('/v1.0/servers/1') + res = req.get_response(nova.api.API()) + res_dict = json.loads(res.body) + self.assertEqual(res_dict['server']['id'], 1) + self.assertEqual(res_dict['server']['name'], 'server1') + + def test_get_server_list(self): + req = webob.Request.blank('/v1.0/servers') + res = req.get_response(nova.api.API()) + res_dict = json.loads(res.body) + + i = 0 + for s in res_dict['servers']: + self.assertEqual(s['id'], i) + self.assertEqual(s['name'], 'server%d'%i) + self.assertEqual(s.get('imageId', None), None) + i += 1 + + def test_create_instance(self): + def server_update(context, id, params): + pass + + def instance_create(context, inst): + class Foo(object): + internal_id = 1 + return Foo() + + def fake_method(*args, **kwargs): + pass + + def project_get_network(context, user_id): + return dict(id='1', host='localhost') + + def queue_get_for(context, *args): + return 'network_topic' + + self.stubs.Set(nova.db.api, 'project_get_network', project_get_network) + self.stubs.Set(nova.db.api, 'instance_create', instance_create) + self.stubs.Set(nova.rpc, 'cast', fake_method) + self.stubs.Set(nova.rpc, 'call', fake_method) + self.stubs.Set(nova.db.api, 'instance_update', + server_update) + self.stubs.Set(nova.db.api, 'queue_get_for', queue_get_for) + self.stubs.Set(nova.network.manager.VlanManager, 'allocate_fixed_ip', + fake_method) + + body = dict(server=dict( + name='server_test', imageId=2, flavorId=2, metadata={}, + personality = {} + )) + req = webob.Request.blank('/v1.0/servers') + req.method = 'POST' + req.body = json.dumps(body) + + res = req.get_response(nova.api.API()) + + self.assertEqual(res.status_int, 200) + + def test_update_no_body(self): + req = webob.Request.blank('/v1.0/servers/1') + req.method = 'PUT' + res = req.get_response(nova.api.API()) + self.assertEqual(res.status_int, 422) + + def test_update_bad_params(self): + """ Confirm that update is filtering params """ + inst_dict = dict(cat='leopard', name='server_test', adminPass='bacon') + self.body = json.dumps(dict(server=inst_dict)) + + def server_update(context, id, params): + self.update_called = True + filtered_dict = dict(name='server_test', admin_pass='bacon') + self.assertEqual(params, filtered_dict) + + self.stubs.Set(nova.db.api, 'instance_update', + server_update) + + req = webob.Request.blank('/v1.0/servers/1') + req.method = 'PUT' + req.body = self.body + req.get_response(nova.api.API()) + + def test_update_server(self): + inst_dict = dict(name='server_test', adminPass='bacon') + self.body = json.dumps(dict(server=inst_dict)) + + def server_update(context, id, params): + filtered_dict = dict(name='server_test', admin_pass='bacon') + self.assertEqual(params, filtered_dict) + + self.stubs.Set(nova.db.api, 'instance_update', + server_update) + + req = webob.Request.blank('/v1.0/servers/1') + req.method = 'PUT' + req.body = self.body + req.get_response(nova.api.API()) + + def test_create_backup_schedules(self): + req = webob.Request.blank('/v1.0/servers/1/backup_schedules') + req.method = 'POST' + res = req.get_response(nova.api.API()) + self.assertEqual(res.status, '404 Not Found') + + def test_delete_backup_schedules(self): + req = webob.Request.blank('/v1.0/servers/1/backup_schedules') + req.method = 'DELETE' + res = req.get_response(nova.api.API()) + self.assertEqual(res.status, '404 Not Found') + + def test_get_server_backup_schedules(self): + req = webob.Request.blank('/v1.0/servers/1/backup_schedules') + res = req.get_response(nova.api.API()) + self.assertEqual(res.status, '404 Not Found') + + def test_get_all_server_details(self): + req = webob.Request.blank('/v1.0/servers/detail') + res = req.get_response(nova.api.API()) + res_dict = json.loads(res.body) + + i = 0 + for s in res_dict['servers']: + self.assertEqual(s['id'], i) + self.assertEqual(s['name'], 'server%d'%i) + self.assertEqual(s['imageId'], 10) + i += 1 + + def test_server_reboot(self): + body = dict(server=dict( + name='server_test', imageId=2, flavorId=2, metadata={}, + personality = {} + )) + req = webob.Request.blank('/v1.0/servers/1/action') + req.method = 'POST' + req.content_type= 'application/json' + req.body = json.dumps(body) + res = req.get_response(nova.api.API()) + + def test_server_rebuild(self): + body = dict(server=dict( + name='server_test', imageId=2, flavorId=2, metadata={}, + personality = {} + )) + req = webob.Request.blank('/v1.0/servers/1/action') + req.method = 'POST' + req.content_type= 'application/json' + req.body = json.dumps(body) + res = req.get_response(nova.api.API()) + + def test_server_resize(self): + body = dict(server=dict( + name='server_test', imageId=2, flavorId=2, metadata={}, + personality = {} + )) + req = webob.Request.blank('/v1.0/servers/1/action') + req.method = 'POST' + req.content_type= 'application/json' + req.body = json.dumps(body) + res = req.get_response(nova.api.API()) + + def test_delete_server_instance(self): + req = webob.Request.blank('/v1.0/servers/1') + req.method = 'DELETE' + + self.server_delete_called = False + def instance_destroy_mock(context, id): + self.server_delete_called = True + + self.stubs.Set(nova.db.api, 'instance_destroy', + instance_destroy_mock) + + res = req.get_response(nova.api.API()) + self.assertEqual(res.status, '202 Accepted') + self.assertEqual(self.server_delete_called, True) + + +if __name__ == "__main__": + unittest.main() diff --git a/nova/tests/api/openstack/test_sharedipgroups.py b/nova/tests/api/openstack/test_sharedipgroups.py new file mode 100644 index 000000000..d199951d8 --- /dev/null +++ b/nova/tests/api/openstack/test_sharedipgroups.py @@ -0,0 +1,39 @@ +# 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 unittest + +import stubout + +from nova.api.openstack import sharedipgroups + + +class SharedIpGroupsTest(unittest.TestCase): + def setUp(self): + self.stubs = stubout.StubOutForTesting() + + def tearDown(self): + self.stubs.UnsetAll() + + def test_get_shared_ip_groups(self): + pass + + def test_create_shared_ip_group(self): + pass + + def test_delete_shared_ip_group(self): + pass diff --git a/nova/tests/api/rackspace/__init__.py b/nova/tests/api/rackspace/__init__.py deleted file mode 100644 index 1834f91b1..000000000 --- a/nova/tests/api/rackspace/__init__.py +++ /dev/null @@ -1,108 +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 unittest - -from nova.api.rackspace import limited -from nova.api.rackspace import RateLimitingMiddleware -from nova.tests.api.fakes import APIStub -from webob import Request - - -class RateLimitingMiddlewareTest(unittest.TestCase): - - def test_get_action_name(self): - middleware = RateLimitingMiddleware(APIStub()) - def verify(method, url, action_name): - req = Request.blank(url) - req.method = method - action = middleware.get_action_name(req) - self.assertEqual(action, action_name) - verify('PUT', '/servers/4', 'PUT') - verify('DELETE', '/servers/4', 'DELETE') - verify('POST', '/images/4', 'POST') - verify('POST', '/servers/4', 'POST servers') - verify('GET', '/foo?a=4&changes-since=never&b=5', 'GET changes-since') - verify('GET', '/foo?a=4&monkeys-since=never&b=5', None) - verify('GET', '/servers/4', None) - verify('HEAD', '/servers/4', None) - - def exhaust(self, middleware, method, url, username, times): - req = Request.blank(url, dict(REQUEST_METHOD=method), - headers={'X-Auth-User': username}) - for i in range(times): - resp = req.get_response(middleware) - self.assertEqual(resp.status_int, 200) - resp = req.get_response(middleware) - self.assertEqual(resp.status_int, 413) - self.assertTrue('Retry-After' in resp.headers) - - def test_single_action(self): - middleware = RateLimitingMiddleware(APIStub()) - self.exhaust(middleware, 'DELETE', '/servers/4', 'usr1', 100) - self.exhaust(middleware, 'DELETE', '/servers/4', 'usr2', 100) - - def test_POST_servers_action_implies_POST_action(self): - middleware = RateLimitingMiddleware(APIStub()) - self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 10) - self.exhaust(middleware, 'POST', '/images/4', 'usr2', 10) - self.assertTrue(set(middleware.limiter._levels) == - set(['usr1:POST', 'usr1:POST servers', 'usr2:POST'])) - - def test_POST_servers_action_correctly_ratelimited(self): - middleware = RateLimitingMiddleware(APIStub()) - # Use up all of our "POST" allowance for the minute, 5 times - for i in range(5): - self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 10) - # Reset the 'POST' action counter. - del middleware.limiter._levels['usr1:POST'] - # All 50 daily "POST servers" actions should be all used up - self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 0) - - def test_proxy_ctor_works(self): - middleware = RateLimitingMiddleware(APIStub()) - self.assertEqual(middleware.limiter.__class__.__name__, "Limiter") - middleware = RateLimitingMiddleware(APIStub(), service_host='foobar') - self.assertEqual(middleware.limiter.__class__.__name__, "WSGIAppProxy") - - -class LimiterTest(unittest.TestCase): - - def test_limiter(self): - items = range(2000) - req = Request.blank('/') - self.assertEqual(limited(items, req), items[ :1000]) - req = Request.blank('/?offset=0') - self.assertEqual(limited(items, req), items[ :1000]) - req = Request.blank('/?offset=3') - self.assertEqual(limited(items, req), items[3:1003]) - req = Request.blank('/?offset=2005') - self.assertEqual(limited(items, req), []) - req = Request.blank('/?limit=10') - self.assertEqual(limited(items, req), items[ :10]) - req = Request.blank('/?limit=0') - self.assertEqual(limited(items, req), items[ :1000]) - req = Request.blank('/?limit=3000') - self.assertEqual(limited(items, req), items[ :1000]) - req = Request.blank('/?offset=1&limit=3') - self.assertEqual(limited(items, req), items[1:4]) - req = Request.blank('/?offset=3&limit=0') - self.assertEqual(limited(items, req), items[3:1003]) - req = Request.blank('/?offset=3&limit=1500') - self.assertEqual(limited(items, req), items[3:1003]) - req = Request.blank('/?offset=3000&limit=10') - self.assertEqual(limited(items, req), []) diff --git a/nova/tests/api/rackspace/fakes.py b/nova/tests/api/rackspace/fakes.py deleted file mode 100644 index 6a25720a9..000000000 --- a/nova/tests/api/rackspace/fakes.py +++ /dev/null @@ -1,205 +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 datetime -import json -import random -import string - -import webob -import webob.dec - -from nova import auth -from nova import utils -from nova import flags -from nova import exception as exc -import nova.api.rackspace.auth -from nova.image import service -from nova.wsgi import Router - - -FLAGS = flags.FLAGS - - -class Context(object): - pass - - -class FakeRouter(Router): - def __init__(self): - pass - - @webob.dec.wsgify - def __call__(self, req): - res = webob.Response() - res.status = '200' - res.headers['X-Test-Success'] = 'True' - return res - - -def fake_auth_init(self): - self.db = FakeAuthDatabase() - self.context = Context() - self.auth = FakeAuthManager() - self.host = 'foo' - - -@webob.dec.wsgify -def fake_wsgi(self, req): - req.environ['nova.context'] = dict(user=dict(id=1)) - if req.body: - req.environ['inst_dict'] = json.loads(req.body) - return self.application - - -def stub_out_key_pair_funcs(stubs): - def key_pair(context, user_id): - return [dict(name='key', public_key='public_key')] - stubs.Set(nova.db.api, 'key_pair_get_all_by_user', - key_pair) - - -def stub_out_image_service(stubs): - def fake_image_show(meh, id): - return dict(kernelId=1, ramdiskId=1) - - stubs.Set(nova.image.service.LocalImageService, 'show', fake_image_show) - -def stub_out_auth(stubs): - def fake_auth_init(self, app): - self.application = app - - stubs.Set(nova.api.rackspace.AuthMiddleware, - '__init__', fake_auth_init) - stubs.Set(nova.api.rackspace.AuthMiddleware, - '__call__', fake_wsgi) - - -def stub_out_rate_limiting(stubs): - def fake_rate_init(self, app): - super(nova.api.rackspace.RateLimitingMiddleware, self).__init__(app) - self.application = app - - stubs.Set(nova.api.rackspace.RateLimitingMiddleware, - '__init__', fake_rate_init) - - stubs.Set(nova.api.rackspace.RateLimitingMiddleware, - '__call__', fake_wsgi) - - -def stub_out_networking(stubs): - def get_my_ip(): - return '127.0.0.1' - stubs.Set(nova.utils, 'get_my_ip', get_my_ip) - FLAGS.FAKE_subdomain = 'rs' - - -def stub_out_glance(stubs): - - class FakeParallaxClient: - - def __init__(self): - self.fixtures = {} - - def fake_get_images(self): - return self.fixtures - - def fake_get_image_metadata(self, image_id): - for k, f in self.fixtures.iteritems(): - if k == image_id: - return f - return None - - def fake_add_image_metadata(self, image_data): - id = ''.join(random.choice(string.letters) for _ in range(20)) - image_data['id'] = id - self.fixtures[id] = image_data - return id - - def fake_update_image_metadata(self, image_id, image_data): - - if image_id not in self.fixtures.keys(): - raise exc.NotFound - - self.fixtures[image_id].update(image_data) - - def fake_delete_image_metadata(self, image_id): - - if image_id not in self.fixtures.keys(): - raise exc.NotFound - - del self.fixtures[image_id] - - def fake_delete_all(self): - self.fixtures = {} - - fake_parallax_client = FakeParallaxClient() - stubs.Set(nova.image.service.ParallaxClient, 'get_images', - fake_parallax_client.fake_get_images) - stubs.Set(nova.image.service.ParallaxClient, 'get_image_metadata', - fake_parallax_client.fake_get_image_metadata) - stubs.Set(nova.image.service.ParallaxClient, 'add_image_metadata', - fake_parallax_client.fake_add_image_metadata) - stubs.Set(nova.image.service.ParallaxClient, 'update_image_metadata', - fake_parallax_client.fake_update_image_metadata) - stubs.Set(nova.image.service.ParallaxClient, 'delete_image_metadata', - fake_parallax_client.fake_delete_image_metadata) - stubs.Set(nova.image.service.GlanceImageService, 'delete_all', - fake_parallax_client.fake_delete_all) - - -class FakeAuthDatabase(object): - data = {} - - @staticmethod - def auth_get_token(context, token_hash): - return FakeAuthDatabase.data.get(token_hash, None) - - @staticmethod - def auth_create_token(context, token): - token['created_at'] = datetime.datetime.now() - FakeAuthDatabase.data[token['token_hash']] = token - - @staticmethod - def auth_destroy_token(context, token): - if FakeAuthDatabase.data.has_key(token['token_hash']): - del FakeAuthDatabase.data['token_hash'] - - -class FakeAuthManager(object): - auth_data = {} - - def add_user(self, key, user): - FakeAuthManager.auth_data[key] = user - - def get_user(self, uid): - for k, v in FakeAuthManager.auth_data.iteritems(): - if v['uid'] == uid: - return v - return None - - def get_user_from_access_key(self, key): - return FakeAuthManager.auth_data.get(key, None) - - -class FakeRateLimiter(object): - def __init__(self, application): - self.application = application - - @webob.dec.wsgify - def __call__(self, req): - return self.application diff --git a/nova/tests/api/rackspace/test_auth.py b/nova/tests/api/rackspace/test_auth.py deleted file mode 100644 index 374cfe42b..000000000 --- a/nova/tests/api/rackspace/test_auth.py +++ /dev/null @@ -1,108 +0,0 @@ -import datetime -import unittest - -import stubout -import webob -import webob.dec - -import nova.api -import nova.api.rackspace.auth -from nova import auth -from nova.tests.api.rackspace import fakes - -class Test(unittest.TestCase): - def setUp(self): - self.stubs = stubout.StubOutForTesting() - self.stubs.Set(nova.api.rackspace.auth.BasicApiAuthManager, - '__init__', fakes.fake_auth_init) - fakes.FakeAuthManager.auth_data = {} - fakes.FakeAuthDatabase.data = {} - fakes.stub_out_rate_limiting(self.stubs) - fakes.stub_out_networking(self.stubs) - - def tearDown(self): - self.stubs.UnsetAll() - fakes.fake_data_store = {} - - def test_authorize_user(self): - f = fakes.FakeAuthManager() - f.add_user('derp', { 'uid': 1, 'name':'herp' } ) - - req = webob.Request.blank('/v1.0/') - req.headers['X-Auth-User'] = 'herp' - req.headers['X-Auth-Key'] = 'derp' - result = req.get_response(nova.api.API()) - self.assertEqual(result.status, '204 No Content') - self.assertEqual(len(result.headers['X-Auth-Token']), 40) - self.assertEqual(result.headers['X-CDN-Management-Url'], - "") - self.assertEqual(result.headers['X-Storage-Url'], "") - - def test_authorize_token(self): - f = fakes.FakeAuthManager() - f.add_user('derp', { 'uid': 1, 'name':'herp' } ) - - req = webob.Request.blank('/v1.0/') - req.headers['X-Auth-User'] = 'herp' - req.headers['X-Auth-Key'] = 'derp' - result = req.get_response(nova.api.API()) - self.assertEqual(result.status, '204 No Content') - self.assertEqual(len(result.headers['X-Auth-Token']), 40) - self.assertEqual(result.headers['X-Server-Management-Url'], - "https://foo/v1.0/") - self.assertEqual(result.headers['X-CDN-Management-Url'], - "") - self.assertEqual(result.headers['X-Storage-Url'], "") - - token = result.headers['X-Auth-Token'] - self.stubs.Set(nova.api.rackspace, 'APIRouter', - fakes.FakeRouter) - req = webob.Request.blank('/v1.0/fake') - req.headers['X-Auth-Token'] = token - result = req.get_response(nova.api.API()) - self.assertEqual(result.status, '200 OK') - self.assertEqual(result.headers['X-Test-Success'], 'True') - - def test_token_expiry(self): - self.destroy_called = False - token_hash = 'bacon' - - def destroy_token_mock(meh, context, token): - self.destroy_called = True - - def bad_token(meh, context, token_hash): - return { 'token_hash':token_hash, - 'created_at':datetime.datetime(1990, 1, 1) } - - self.stubs.Set(fakes.FakeAuthDatabase, 'auth_destroy_token', - destroy_token_mock) - - self.stubs.Set(fakes.FakeAuthDatabase, 'auth_get_token', - bad_token) - - req = webob.Request.blank('/v1.0/') - req.headers['X-Auth-Token'] = 'bacon' - result = req.get_response(nova.api.API()) - self.assertEqual(result.status, '401 Unauthorized') - self.assertEqual(self.destroy_called, True) - - def test_bad_user(self): - req = webob.Request.blank('/v1.0/') - req.headers['X-Auth-User'] = 'herp' - req.headers['X-Auth-Key'] = 'derp' - result = req.get_response(nova.api.API()) - self.assertEqual(result.status, '401 Unauthorized') - - def test_no_user(self): - req = webob.Request.blank('/v1.0/') - result = req.get_response(nova.api.API()) - self.assertEqual(result.status, '401 Unauthorized') - - def test_bad_token(self): - req = webob.Request.blank('/v1.0/') - req.headers['X-Auth-Token'] = 'baconbaconbacon' - result = req.get_response(nova.api.API()) - self.assertEqual(result.status, '401 Unauthorized') - -if __name__ == '__main__': - unittest.main() diff --git a/nova/tests/api/rackspace/test_faults.py b/nova/tests/api/rackspace/test_faults.py deleted file mode 100644 index b2931bc98..000000000 --- a/nova/tests/api/rackspace/test_faults.py +++ /dev/null @@ -1,40 +0,0 @@ -import unittest -import webob -import webob.dec -import webob.exc - -from nova.api.rackspace import faults - -class TestFaults(unittest.TestCase): - - def test_fault_parts(self): - req = webob.Request.blank('/.xml') - f = faults.Fault(webob.exc.HTTPBadRequest(explanation='scram')) - resp = req.get_response(f) - - first_two_words = resp.body.strip().split()[:2] - self.assertEqual(first_two_words, ['']) - body_without_spaces = ''.join(resp.body.split()) - self.assertTrue('scram' in body_without_spaces) - - def test_retry_header(self): - req = webob.Request.blank('/.xml') - exc = webob.exc.HTTPRequestEntityTooLarge(explanation='sorry', - headers={'Retry-After': 4}) - f = faults.Fault(exc) - resp = req.get_response(f) - first_two_words = resp.body.strip().split()[:2] - self.assertEqual(first_two_words, ['']) - body_sans_spaces = ''.join(resp.body.split()) - self.assertTrue('sorry' in body_sans_spaces) - self.assertTrue('4' in body_sans_spaces) - self.assertEqual(resp.headers['Retry-After'], 4) - - def test_raise(self): - @webob.dec.wsgify - def raiser(req): - raise faults.Fault(webob.exc.HTTPNotFound(explanation='whut?')) - req = webob.Request.blank('/.xml') - resp = req.get_response(raiser) - self.assertEqual(resp.status_int, 404) - self.assertTrue('whut?' in resp.body) diff --git a/nova/tests/api/rackspace/test_flavors.py b/nova/tests/api/rackspace/test_flavors.py deleted file mode 100644 index affdd2406..000000000 --- a/nova/tests/api/rackspace/test_flavors.py +++ /dev/null @@ -1,48 +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 unittest - -import stubout -import webob - -import nova.api -from nova.api.rackspace import flavors -from nova.tests.api.rackspace import fakes - - -class FlavorsTest(unittest.TestCase): - def setUp(self): - self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.auth_data = {} - fakes.FakeAuthDatabase.data = {} - fakes.stub_out_networking(self.stubs) - fakes.stub_out_rate_limiting(self.stubs) - fakes.stub_out_auth(self.stubs) - - def tearDown(self): - self.stubs.UnsetAll() - - def test_get_flavor_list(self): - req = webob.Request.blank('/v1.0/flavors') - res = req.get_response(nova.api.API()) - - def test_get_flavor_by_id(self): - pass - -if __name__ == '__main__': - unittest.main() diff --git a/nova/tests/api/rackspace/test_images.py b/nova/tests/api/rackspace/test_images.py deleted file mode 100644 index a7f320b46..000000000 --- a/nova/tests/api/rackspace/test_images.py +++ /dev/null @@ -1,141 +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 logging -import unittest - -import stubout - -from nova import exception -from nova import utils -from nova.api.rackspace import images -from nova.tests.api.rackspace import fakes - - -class BaseImageServiceTests(): - - """Tasks to test for all image services""" - - def test_create(self): - - fixture = {'name': 'test image', - 'updated': None, - 'created': None, - 'status': None, - 'serverId': None, - 'progress': None} - - num_images = len(self.service.index()) - - id = self.service.create(fixture) - - self.assertNotEquals(None, id) - self.assertEquals(num_images + 1, len(self.service.index())) - - def test_create_and_show_non_existing_image(self): - - fixture = {'name': 'test image', - 'updated': None, - 'created': None, - 'status': None, - 'serverId': None, - 'progress': None} - - num_images = len(self.service.index()) - - id = self.service.create(fixture) - - self.assertNotEquals(None, id) - - self.assertRaises(exception.NotFound, - self.service.show, - 'bad image id') - - def test_update(self): - - fixture = {'name': 'test image', - 'updated': None, - 'created': None, - 'status': None, - 'serverId': None, - 'progress': None} - - id = self.service.create(fixture) - - fixture['status'] = 'in progress' - - self.service.update(id, fixture) - new_image_data = self.service.show(id) - self.assertEquals('in progress', new_image_data['status']) - - def test_delete(self): - - fixtures = [ - {'name': 'test image 1', - 'updated': None, - 'created': None, - 'status': None, - 'serverId': None, - 'progress': None}, - {'name': 'test image 2', - 'updated': None, - 'created': None, - 'status': None, - 'serverId': None, - 'progress': None}] - - ids = [] - for fixture in fixtures: - new_id = self.service.create(fixture) - ids.append(new_id) - - num_images = len(self.service.index()) - self.assertEquals(2, num_images) - - self.service.delete(ids[0]) - - num_images = len(self.service.index()) - self.assertEquals(1, num_images) - - -class LocalImageServiceTest(unittest.TestCase, - BaseImageServiceTests): - - """Tests the local image service""" - - def setUp(self): - self.stubs = stubout.StubOutForTesting() - self.service = utils.import_object('nova.image.service.LocalImageService') - - def tearDown(self): - self.service.delete_all() - self.stubs.UnsetAll() - - -class GlanceImageServiceTest(unittest.TestCase, - BaseImageServiceTests): - - """Tests the local image service""" - - def setUp(self): - self.stubs = stubout.StubOutForTesting() - fakes.stub_out_glance(self.stubs) - self.service = utils.import_object('nova.image.service.GlanceImageService') - - def tearDown(self): - self.service.delete_all() - self.stubs.UnsetAll() diff --git a/nova/tests/api/rackspace/test_servers.py b/nova/tests/api/rackspace/test_servers.py deleted file mode 100644 index 57040621b..000000000 --- a/nova/tests/api/rackspace/test_servers.py +++ /dev/null @@ -1,249 +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 unittest - -import stubout -import webob - -from nova import db -from nova import flags -import nova.api.rackspace -from nova.api.rackspace import servers -import nova.db.api -from nova.db.sqlalchemy.models import Instance -import nova.rpc -from nova.tests.api.rackspace import fakes - - -FLAGS = flags.FLAGS - -FLAGS.verbose = True - -def return_server(context, id): - return stub_instance(id) - - -def return_servers(context, user_id=1): - return [stub_instance(i, user_id) for i in xrange(5)] - - -def stub_instance(id, user_id=1): - return Instance( - id=id, state=0, image_id=10, server_name='server%s'%id, - user_id=user_id - ) - - -class ServersTest(unittest.TestCase): - def setUp(self): - self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.auth_data = {} - fakes.FakeAuthDatabase.data = {} - fakes.stub_out_networking(self.stubs) - fakes.stub_out_rate_limiting(self.stubs) - fakes.stub_out_auth(self.stubs) - fakes.stub_out_key_pair_funcs(self.stubs) - fakes.stub_out_image_service(self.stubs) - self.stubs.Set(nova.db.api, 'instance_get_all', return_servers) - self.stubs.Set(nova.db.api, 'instance_get_by_internal_id', return_server) - self.stubs.Set(nova.db.api, 'instance_get_all_by_user', - return_servers) - - def tearDown(self): - self.stubs.UnsetAll() - - def test_get_server_by_id(self): - req = webob.Request.blank('/v1.0/servers/1') - res = req.get_response(nova.api.API()) - res_dict = json.loads(res.body) - self.assertEqual(res_dict['server']['id'], 1) - self.assertEqual(res_dict['server']['name'], 'server1') - - def test_get_server_list(self): - req = webob.Request.blank('/v1.0/servers') - res = req.get_response(nova.api.API()) - res_dict = json.loads(res.body) - - i = 0 - for s in res_dict['servers']: - self.assertEqual(s['id'], i) - self.assertEqual(s['name'], 'server%d'%i) - self.assertEqual(s.get('imageId', None), None) - i += 1 - - def test_create_instance(self): - def server_update(context, id, params): - pass - - def instance_create(context, inst): - class Foo(object): - internal_id = 1 - return Foo() - - def fake_method(*args, **kwargs): - pass - - def project_get_network(context, user_id): - return dict(id='1', host='localhost') - - def queue_get_for(context, *args): - return 'network_topic' - - self.stubs.Set(nova.db.api, 'project_get_network', project_get_network) - self.stubs.Set(nova.db.api, 'instance_create', instance_create) - self.stubs.Set(nova.rpc, 'cast', fake_method) - self.stubs.Set(nova.rpc, 'call', fake_method) - self.stubs.Set(nova.db.api, 'instance_update', - server_update) - self.stubs.Set(nova.db.api, 'queue_get_for', queue_get_for) - self.stubs.Set(nova.network.manager.VlanManager, 'allocate_fixed_ip', - fake_method) - - body = dict(server=dict( - name='server_test', imageId=2, flavorId=2, metadata={}, - personality = {} - )) - req = webob.Request.blank('/v1.0/servers') - req.method = 'POST' - req.body = json.dumps(body) - - res = req.get_response(nova.api.API()) - - self.assertEqual(res.status_int, 200) - - def test_update_no_body(self): - req = webob.Request.blank('/v1.0/servers/1') - req.method = 'PUT' - res = req.get_response(nova.api.API()) - self.assertEqual(res.status_int, 422) - - def test_update_bad_params(self): - """ Confirm that update is filtering params """ - inst_dict = dict(cat='leopard', name='server_test', adminPass='bacon') - self.body = json.dumps(dict(server=inst_dict)) - - def server_update(context, id, params): - self.update_called = True - filtered_dict = dict(name='server_test', admin_pass='bacon') - self.assertEqual(params, filtered_dict) - - self.stubs.Set(nova.db.api, 'instance_update', - server_update) - - req = webob.Request.blank('/v1.0/servers/1') - req.method = 'PUT' - req.body = self.body - req.get_response(nova.api.API()) - - def test_update_server(self): - inst_dict = dict(name='server_test', adminPass='bacon') - self.body = json.dumps(dict(server=inst_dict)) - - def server_update(context, id, params): - filtered_dict = dict(name='server_test', admin_pass='bacon') - self.assertEqual(params, filtered_dict) - - self.stubs.Set(nova.db.api, 'instance_update', - server_update) - - req = webob.Request.blank('/v1.0/servers/1') - req.method = 'PUT' - req.body = self.body - req.get_response(nova.api.API()) - - def test_create_backup_schedules(self): - req = webob.Request.blank('/v1.0/servers/1/backup_schedules') - req.method = 'POST' - res = req.get_response(nova.api.API()) - self.assertEqual(res.status, '404 Not Found') - - def test_delete_backup_schedules(self): - req = webob.Request.blank('/v1.0/servers/1/backup_schedules') - req.method = 'DELETE' - res = req.get_response(nova.api.API()) - self.assertEqual(res.status, '404 Not Found') - - def test_get_server_backup_schedules(self): - req = webob.Request.blank('/v1.0/servers/1/backup_schedules') - res = req.get_response(nova.api.API()) - self.assertEqual(res.status, '404 Not Found') - - def test_get_all_server_details(self): - req = webob.Request.blank('/v1.0/servers/detail') - res = req.get_response(nova.api.API()) - res_dict = json.loads(res.body) - - i = 0 - for s in res_dict['servers']: - self.assertEqual(s['id'], i) - self.assertEqual(s['name'], 'server%d'%i) - self.assertEqual(s['imageId'], 10) - i += 1 - - def test_server_reboot(self): - body = dict(server=dict( - name='server_test', imageId=2, flavorId=2, metadata={}, - personality = {} - )) - req = webob.Request.blank('/v1.0/servers/1/action') - req.method = 'POST' - req.content_type= 'application/json' - req.body = json.dumps(body) - res = req.get_response(nova.api.API()) - - def test_server_rebuild(self): - body = dict(server=dict( - name='server_test', imageId=2, flavorId=2, metadata={}, - personality = {} - )) - req = webob.Request.blank('/v1.0/servers/1/action') - req.method = 'POST' - req.content_type= 'application/json' - req.body = json.dumps(body) - res = req.get_response(nova.api.API()) - - def test_server_resize(self): - body = dict(server=dict( - name='server_test', imageId=2, flavorId=2, metadata={}, - personality = {} - )) - req = webob.Request.blank('/v1.0/servers/1/action') - req.method = 'POST' - req.content_type= 'application/json' - req.body = json.dumps(body) - res = req.get_response(nova.api.API()) - - def test_delete_server_instance(self): - req = webob.Request.blank('/v1.0/servers/1') - req.method = 'DELETE' - - self.server_delete_called = False - def instance_destroy_mock(context, id): - self.server_delete_called = True - - self.stubs.Set(nova.db.api, 'instance_destroy', - instance_destroy_mock) - - res = req.get_response(nova.api.API()) - self.assertEqual(res.status, '202 Accepted') - self.assertEqual(self.server_delete_called, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/nova/tests/api/rackspace/test_sharedipgroups.py b/nova/tests/api/rackspace/test_sharedipgroups.py deleted file mode 100644 index 31ce967d0..000000000 --- a/nova/tests/api/rackspace/test_sharedipgroups.py +++ /dev/null @@ -1,39 +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 unittest - -import stubout - -from nova.api.rackspace import sharedipgroups - - -class SharedIpGroupsTest(unittest.TestCase): - def setUp(self): - self.stubs = stubout.StubOutForTesting() - - def tearDown(self): - self.stubs.UnsetAll() - - def test_get_shared_ip_groups(self): - pass - - def test_create_shared_ip_group(self): - pass - - def test_delete_shared_ip_group(self): - pass -- cgit From f447e1a3a2234e0ab3a5e281442659626f8d99bd Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 11 Oct 2010 13:39:33 +0200 Subject: Rename ec2 get_console_output's instance ID argument to 'instance_id'. It's passed as a kwarg, based on key in the http query, so it must be named this way. --- nova/tests/cloud_unittest.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) (limited to 'nova/tests') diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py index 615e589cf..8e5881edb 100644 --- a/nova/tests/cloud_unittest.py +++ b/nova/tests/cloud_unittest.py @@ -16,6 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. +from base64 import b64decode import json import logging from M2Crypto import BIO @@ -63,11 +64,17 @@ class CloudTestCase(test.TrialTestCase): self.cloud = cloud.CloudController() # set up a service - self.compute = utils.import_class(FLAGS.compute_manager) + self.compute = utils.import_class(FLAGS.compute_manager)() self.compute_consumer = rpc.AdapterConsumer(connection=self.conn, topic=FLAGS.compute_topic, proxy=self.compute) - self.compute_consumer.attach_to_twisted() + self.compute_consumer.attach_to_eventlet() + self.network = utils.import_class(FLAGS.network_manager)() + self.network_consumer = rpc.AdapterConsumer(connection=self.conn, + topic=FLAGS.network_topic, + proxy=self.network) + self.network_consumer.attach_to_eventlet() + self.manager = manager.AuthManager() self.user = self.manager.create_user('admin', 'admin', 'admin', True) @@ -85,15 +92,17 @@ class CloudTestCase(test.TrialTestCase): return cloud._gen_key(self.context, self.context.user.id, name) def test_console_output(self): - if FLAGS.connection_type == 'fake': - logging.debug("Can't test instances without a real virtual env.") - return - instance_id = 'foo' - inst = yield self.compute.run_instance(instance_id) - output = yield self.cloud.get_console_output(self.context, [instance_id]) - logging.debug(output) - self.assert_(output) - rv = yield self.compute.terminate_instance(instance_id) + image_id = FLAGS.default_image + instance_type = FLAGS.default_instance_type + max_count = 1 + kwargs = {'image_id': image_id, + 'instance_type': instance_type, + 'max_count': max_count } + rv = yield self.cloud.run_instances(self.context, **kwargs) + instance_id = rv['instancesSet'][0]['instanceId'] + output = yield self.cloud.get_console_output(context=self.context, instance_id=[instance_id]) + self.assertEquals(b64decode(output['output']), 'FAKE CONSOLE OUTPUT') + rv = yield self.cloud.terminate_instances(self.context, [instance_id]) def test_key_generation(self): -- cgit From 3ca549942e96e4ff769e914f227919f3a4d98686 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 11 Oct 2010 14:09:24 +0200 Subject: If machine manifest includes a kernel and/or ramdisk id, include it in the image's metadata. --- nova/tests/bundle/1mb.manifest.xml | 2 +- .../bundle/1mb.no_kernel_or_ramdisk.manifest.xml | 1 + nova/tests/objectstore_unittest.py | 29 ++++++++++++++++++---- 3 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 nova/tests/bundle/1mb.no_kernel_or_ramdisk.manifest.xml (limited to 'nova/tests') diff --git a/nova/tests/bundle/1mb.manifest.xml b/nova/tests/bundle/1mb.manifest.xml index dc3315957..01648a544 100644 --- a/nova/tests/bundle/1mb.manifest.xml +++ b/nova/tests/bundle/1mb.manifest.xml @@ -1 +1 @@ -2007-10-10euca-tools1.231337x86_641mb42machineda39a3ee5e6b4b0d3255bfef95601890afd807091048576113633a2ea00dc64083dd9a10eb5e233635b42a7beb1670ab75452087d9de74c60aba1cd27c136fda56f62beb581de128fb1f10d072b9e556fd25e903107a57827c21f6ee8a93a4ff55b11311fcef217e3eefb07e81f71e88216f43b4b54029c1f2549f2925a839a73947d2d5aeecec4a62ece4af9156d557ae907978298296d99154c11147fd8caf92447e90ce339928933d7579244c2f8ffb07cc0ea35f8738da8b90eff6c7a49671a84500e993e9462e4c36d5c19c0b3a2b397d035b4c0cce742b58e12552175d81d129b0425e9f71ebacb9aeb539fa9dd2ac36749fb82876f6902e5fb24b6ec19f35ec4c20acd50437fd30966e99c4d9a0647577970a8fa302314bd082c9715f071160c69bbfb070f51d2ba1076775f1d988ccde150e515088156b248e4b5a64e46c4fe064feeeedfe14511f7fde478a51acb89f9b2f6c84b60593e5c3f792ba6b01fed9bf2158fdac03086374883b39d13a3ca74497eeaaf579fc3f26effc73bfd9446a2a8c4061f0874bfaca058905180e22d3d8881551cb38f7606f19f00e4e19535dd234b66b31b77e9c7bad3885d9c9efa75c863631fd4f82a009e17d789066d9cc6032a436f05384832f6d9a3283d3e63eab04fa0da5c8c87db9b17e854e842c3fb416507d067a266b44538125ce732e486098e8ebd1ca91fa3079f007fce7d14957a9b7e57282407ead3c6eb68fe975df3d83190021b1mb.part.0c4413423cf7a57e71187e19bfd5cd4b514a642831mb.part.19d4262e6589393d09a11a0332af169887bc2e57d4e00b5ba28114dda4a9df7eeae94be847ec46117a09a1cbe41e578660642f0660dda1776b39fb3bf826b6cfec019e2a5e9c566728d186b7400ebc989a30670eb1db26ce01e68bd9d3f31290370077a85b81c66b63c1e0d5499bac115c06c17a21a81b6d3a67ebbce6c17019095af7ab07f3796c708cc843e58efc12ddc788c5e \ No newline at end of file +2007-10-10euca-tools1.231337x86_64aki-testari-test1mb42machineda39a3ee5e6b4b0d3255bfef95601890afd807091048576113633a2ea00dc64083dd9a10eb5e233635b42a7beb1670ab75452087d9de74c60aba1cd27c136fda56f62beb581de128fb1f10d072b9e556fd25e903107a57827c21f6ee8a93a4ff55b11311fcef217e3eefb07e81f71e88216f43b4b54029c1f2549f2925a839a73947d2d5aeecec4a62ece4af9156d557ae907978298296d99154c11147fd8caf92447e90ce339928933d7579244c2f8ffb07cc0ea35f8738da8b90eff6c7a49671a84500e993e9462e4c36d5c19c0b3a2b397d035b4c0cce742b58e12552175d81d129b0425e9f71ebacb9aeb539fa9dd2ac36749fb82876f6902e5fb24b6ec19f35ec4c20acd50437fd30966e99c4d9a0647577970a8fa302314bd082c9715f071160c69bbfb070f51d2ba1076775f1d988ccde150e515088156b248e4b5a64e46c4fe064feeeedfe14511f7fde478a51acb89f9b2f6c84b60593e5c3f792ba6b01fed9bf2158fdac03086374883b39d13a3ca74497eeaaf579fc3f26effc73bfd9446a2a8c4061f0874bfaca058905180e22d3d8881551cb38f7606f19f00e4e19535dd234b66b31b77e9c7bad3885d9c9efa75c863631fd4f82a009e17d789066d9cc6032a436f05384832f6d9a3283d3e63eab04fa0da5c8c87db9b17e854e842c3fb416507d067a266b44538125ce732e486098e8ebd1ca91fa3079f007fce7d14957a9b7e57282407ead3c6eb68fe975df3d83190021b1mb.part.0c4413423cf7a57e71187e19bfd5cd4b514a642831mb.part.19d4262e6589393d09a11a0332af169887bc2e57d4e00b5ba28114dda4a9df7eeae94be847ec46117a09a1cbe41e578660642f0660dda1776b39fb3bf826b6cfec019e2a5e9c566728d186b7400ebc989a30670eb1db26ce01e68bd9d3f31290370077a85b81c66b63c1e0d5499bac115c06c17a21a81b6d3a67ebbce6c17019095af7ab07f3796c708cc843e58efc12ddc788c5e diff --git a/nova/tests/bundle/1mb.no_kernel_or_ramdisk.manifest.xml b/nova/tests/bundle/1mb.no_kernel_or_ramdisk.manifest.xml new file mode 100644 index 000000000..73d7ace00 --- /dev/null +++ b/nova/tests/bundle/1mb.no_kernel_or_ramdisk.manifest.xml @@ -0,0 +1 @@ +2007-10-10euca-tools1.231337x86_641mb42machineda39a3ee5e6b4b0d3255bfef95601890afd807091048576113633a2ea00dc64083dd9a10eb5e233635b42a7beb1670ab75452087d9de74c60aba1cd27c136fda56f62beb581de128fb1f10d072b9e556fd25e903107a57827c21f6ee8a93a4ff55b11311fcef217e3eefb07e81f71e88216f43b4b54029c1f2549f2925a839a73947d2d5aeecec4a62ece4af9156d557ae907978298296d99154c11147fd8caf92447e90ce339928933d7579244c2f8ffb07cc0ea35f8738da8b90eff6c7a49671a84500e993e9462e4c36d5c19c0b3a2b397d035b4c0cce742b58e12552175d81d129b0425e9f71ebacb9aeb539fa9dd2ac36749fb82876f6902e5fb24b6ec19f35ec4c20acd50437fd30966e99c4d9a0647577970a8fa302314bd082c9715f071160c69bbfb070f51d2ba1076775f1d988ccde150e515088156b248e4b5a64e46c4fe064feeeedfe14511f7fde478a51acb89f9b2f6c84b60593e5c3f792ba6b01fed9bf2158fdac03086374883b39d13a3ca74497eeaaf579fc3f26effc73bfd9446a2a8c4061f0874bfaca058905180e22d3d8881551cb38f7606f19f00e4e19535dd234b66b31b77e9c7bad3885d9c9efa75c863631fd4f82a009e17d789066d9cc6032a436f05384832f6d9a3283d3e63eab04fa0da5c8c87db9b17e854e842c3fb416507d067a266b44538125ce732e486098e8ebd1ca91fa3079f007fce7d14957a9b7e57282407ead3c6eb68fe975df3d83190021b1mb.part.0c4413423cf7a57e71187e19bfd5cd4b514a642831mb.part.19d4262e6589393d09a11a0332af169887bc2e57d4e00b5ba28114dda4a9df7eeae94be847ec46117a09a1cbe41e578660642f0660dda1776b39fb3bf826b6cfec019e2a5e9c566728d186b7400ebc989a30670eb1db26ce01e68bd9d3f31290370077a85b81c66b63c1e0d5499bac115c06c17a21a81b6d3a67ebbce6c17019095af7ab07f3796c708cc843e58efc12ddc788c5e diff --git a/nova/tests/objectstore_unittest.py b/nova/tests/objectstore_unittest.py index 5a599ff3a..eb2ee0406 100644 --- a/nova/tests/objectstore_unittest.py +++ b/nova/tests/objectstore_unittest.py @@ -133,13 +133,22 @@ class ObjectStoreTestCase(test.TrialTestCase): self.assertRaises(NotFound, objectstore.bucket.Bucket, 'new_bucket') def test_images(self): + self.do_test_images('1mb.manifest.xml', True, + 'image_bucket1', 'i-testing1') + + def test_images_no_kernel_or_ramdisk(self): + self.do_test_images('1mb.no_kernel_or_ramdisk.manifest.xml', + False, 'image_bucket2', 'i-testing2') + + def do_test_images(self, manifest_file, expect_kernel_and_ramdisk, + image_bucket, image_name): "Test the image API." self.context.user = self.auth_manager.get_user('user1') self.context.project = self.auth_manager.get_project('proj1') # create a bucket for our bundle - objectstore.bucket.Bucket.create('image_bucket', self.context) - bucket = objectstore.bucket.Bucket('image_bucket') + objectstore.bucket.Bucket.create(image_bucket, self.context) + bucket = objectstore.bucket.Bucket(image_bucket) # upload an image manifest/parts bundle_path = os.path.join(os.path.dirname(__file__), 'bundle') @@ -147,18 +156,28 @@ class ObjectStoreTestCase(test.TrialTestCase): bucket[os.path.basename(path)] = open(path, 'rb').read() # register an image - image.Image.register_aws_image('i-testing', - 'image_bucket/1mb.manifest.xml', + image.Image.register_aws_image(image_name, + '%s/%s' % (image_bucket, manifest_file), self.context) # verify image - my_img = image.Image('i-testing') + my_img = image.Image(image_name) result_image_file = os.path.join(my_img.path, 'image') self.assertEqual(os.stat(result_image_file).st_size, 1048576) sha = hashlib.sha1(open(result_image_file).read()).hexdigest() self.assertEqual(sha, '3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3') + if expect_kernel_and_ramdisk: + # Verify the default kernel and ramdisk are set + self.assertEqual(my_img.metadata['kernelId'], 'aki-test') + self.assertEqual(my_img.metadata['ramdiskId'], 'ari-test') + else: + # Verify that the default kernel and ramdisk (the one from FLAGS) + # doesn't get embedded in the metadata + self.assertFalse('kernelId' in my_img.metadata) + self.assertFalse('ramdiskId' in my_img.metadata) + # verify image permissions self.context.user = self.auth_manager.get_user('user2') self.context.project = self.auth_manager.get_project('proj2') -- cgit From f9b2f70f22bdc8a9cf08ada5f7ec45eea6060866 Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Mon, 11 Oct 2010 11:43:58 -0400 Subject: Rename rsapi to osapi, and make the default subdomain for OpenStack API calls be 'api' instead of 'rs'. --- nova/tests/api/__init__.py | 4 ++-- nova/tests/api/openstack/fakes.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'nova/tests') diff --git a/nova/tests/api/__init__.py b/nova/tests/api/__init__.py index 2c7f7fd3e..f051e2390 100644 --- a/nova/tests/api/__init__.py +++ b/nova/tests/api/__init__.py @@ -46,7 +46,7 @@ class Test(unittest.TestCase): def test_openstack(self): self.stubs.Set(api.openstack, 'API', APIStub) - result = self._request('/v1.0/cloud', 'rs') + result = self._request('/v1.0/cloud', 'api') self.assertEqual(result.body, "/cloud") def test_ec2(self): @@ -61,7 +61,7 @@ class Test(unittest.TestCase): self.assertNotEqual(result.body, "/cloud") def test_query_api_versions(self): - result = self._request('/', 'rs') + result = self._request('/', 'api') self.assertTrue('CURRENT' in result.body) def test_metadata(self): diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index 1119fa714..34bc1f2a9 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -105,7 +105,7 @@ def stub_out_networking(stubs): def get_my_ip(): return '127.0.0.1' stubs.Set(nova.utils, 'get_my_ip', get_my_ip) - FLAGS.FAKE_subdomain = 'rs' + FLAGS.FAKE_subdomain = 'api' def stub_out_glance(stubs): -- cgit