From 998975651ac2f2df7a3f8af16d62d197f451180f Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Thu, 10 Mar 2011 13:53:27 -0800 Subject: Test login. Uncovered bug732866 --- nova/tests/integrated/integrated_helpers.py | 182 ++++++++++++++++++++++++++++ nova/tests/integrated/test_login.py | 77 ++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 nova/tests/integrated/integrated_helpers.py create mode 100644 nova/tests/integrated/test_login.py diff --git a/nova/tests/integrated/integrated_helpers.py b/nova/tests/integrated/integrated_helpers.py new file mode 100644 index 000000000..691ead6e1 --- /dev/null +++ b/nova/tests/integrated/integrated_helpers.py @@ -0,0 +1,182 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Justin Santa Barbara +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Provides common functionality for integrated unit tests +""" + +import random +import string + +from nova import exception +from nova import flags +from nova import service +from nova import test # For the flags +from nova.auth import manager +from nova.exception import Error +from nova.log import logging +from nova.tests.integrated.api import client + + +FLAGS = flags.FLAGS + +LOG = logging.getLogger('nova.tests.integrated') + + +def generate_random_alphanumeric(length): + """Creates a random alphanumeric string of specified length""" + return ''.join(random.choice(string.ascii_uppercase + string.digits) + for _x in range(length)) + + +def generate_random_numeric(length): + """Creates a random numeric string of specified length""" + return ''.join(random.choice(string.digits) + for _x in range(length)) + + +def generate_new_element(items, prefix, numeric=False): + """Creates a random string with prefix, that is not in 'items' list""" + while True: + if numeric: + candidate = prefix + generate_random_numeric(8) + else: + candidate = prefix + generate_random_alphanumeric(8) + if not candidate in items: + return candidate + print "Random collision on %s" % candidate + + +class TestUser(object): + def __init__(self, name, secret, auth_url): + self.name = name + self.secret = secret + self.auth_url = auth_url + + if not auth_url: + raise exception.Error("auth_url is required") + self.openstack_api = client.TestOpenStackClient(self.name, + self.secret, + self.auth_url) + + +class IntegratedUnitTestContext(object): + __INSTANCE = None + + def __init__(self): + self.auth_manager = manager.AuthManager() + + self.wsgi_server = None + self.wsgi_apps = [] + self.api_service = None + + self.services = [] + self.auth_url = None + self.project_name = None + + self.setup() + + def setup(self): + self._start_services() + + self._create_test_user() + + def _create_test_user(self): + self.test_user = self._create_unittest_user() + + # No way to currently pass this through the OpenStack API + self.project_name = 'openstack' + self._configure_project(self.project_name, self.test_user) + + def _start_services(self): + # WSGI shutdown broken :-( + if not self.api_service: + self._start_api_service() + + def cleanup(self): + for service in self.services: + service.kill() + self.services = [] + # TODO(justinsb): Shutdown WSGI & anything else we startup + # WSGI shutdown broken :-( + # self.wsgi_server.terminate() + # self.wsgi_server = None + self.test_user = None + + def _create_unittest_user(self): + users = self.auth_manager.get_users() + user_names = [user.name for user in users] + auth_name = generate_new_element(user_names, 'unittest_user_') + auth_key = generate_random_alphanumeric(16) + + # Right now there's a bug where auth_name and auth_key are reversed + auth_key = auth_name + + self.auth_manager.create_user(auth_name, auth_name, auth_key, False) + return TestUser(auth_name, auth_key, self.auth_url) + + def _configure_project(self, project_name, user): + projects = self.auth_manager.get_projects() + project_names = [project.name for project in projects] + if not project_name in project_names: + project = self.auth_manager.create_project(project_name, + user.name, + description=None, + member_users=None) + else: + self.auth_manager.add_to_project(user.name, project_name) + + def _start_api_service(self): + api_service = service.ApiService.create() + api_service.start() + + if not api_service: + raise Exception("API Service was None") + + # WSGI shutdown broken :-( + #self.services.append(volume_service) + self.api_service = api_service + + self.auth_url = 'http://localhost:8774/v1.0' + + return api_service + + # WSGI shutdown broken :-( + #@staticmethod + #def get(): + # if not IntegratedUnitTestContext.__INSTANCE: + # IntegratedUnitTestContext.startup() + # #raise Error("Must call IntegratedUnitTestContext::startup") + # return IntegratedUnitTestContext.__INSTANCE + + @staticmethod + def startup(): + # Because WSGI shutdown is broken at the moment, we have to recycle + if IntegratedUnitTestContext.__INSTANCE: + #raise Error("Multiple calls to IntegratedUnitTestContext.startup") + IntegratedUnitTestContext.__INSTANCE.setup() + else: + IntegratedUnitTestContext.__INSTANCE = IntegratedUnitTestContext() + return IntegratedUnitTestContext.__INSTANCE + + @staticmethod + def shutdown(): + if not IntegratedUnitTestContext.__INSTANCE: + raise Error("Must call IntegratedUnitTestContext::startup") + IntegratedUnitTestContext.__INSTANCE.cleanup() + # WSGI shutdown broken :-( + #IntegratedUnitTestContext.__INSTANCE = None diff --git a/nova/tests/integrated/test_login.py b/nova/tests/integrated/test_login.py new file mode 100644 index 000000000..990dcaaf4 --- /dev/null +++ b/nova/tests/integrated/test_login.py @@ -0,0 +1,77 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Justin Santa Barbara +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import unittest + +from nova import flags +from nova.log import logging +from nova.tests.integrated import integrated_helpers +from nova.tests.integrated.api import client + +LOG = logging.getLogger('nova.tests.integrated') + +FLAGS = flags.FLAGS +FLAGS.verbose = True + + +class LoginTest(unittest.TestCase): + def setUp(self): + super(LoginTest, self).setUp() + context = integrated_helpers.IntegratedUnitTestContext.startup() + self.user = context.test_user + self.api = self.user.openstack_api + + def tearDown(self): + integrated_helpers.IntegratedUnitTestContext.shutdown() + super(LoginTest, self).tearDown() + + def test_login(self): + """Simple check - we list flavors - so we know we're logged in""" + flavors = self.api.get_flavors() + for flavor in flavors: + LOG.debug(_("flavor: %s") % flavor) + + def test_bad_login_password(self): + """Test that I get a 401 with a bad username""" + bad_credentials_api = client.TestOpenStackClient(self.user.name, + "notso_password", + self.user.auth_url) + + self.assertRaises(client.OpenstackApiAuthenticationException, + bad_credentials_api.get_flavors) + + def test_bad_login_username(self): + """Test that I get a 401 with a bad password""" + bad_credentials_api = client.TestOpenStackClient("notso_username", + self.user.secret, + self.user.auth_url) + + self.assertRaises(client.OpenstackApiAuthenticationException, + bad_credentials_api.get_flavors) + + + def test_bad_login_both_bad(self): + """Test that I get a 401 with both bad username and bad password""" + bad_credentials_api = client.TestOpenStackClient("notso_username", + "notso_password", + self.user.auth_url) + + self.assertRaises(client.OpenstackApiAuthenticationException, + bad_credentials_api.get_flavors) + +if __name__ == "__main__": + unittest.main() -- cgit From 3d6430ecd114daa21c72c3d215daaa94f0e87e62 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Thu, 10 Mar 2011 14:12:41 -0800 Subject: Re-removed the code that was deleted upstream but somehow didn't get merged in. Bizarre! --- nova/tests/integrated/api/client.py | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py index 0ce480ae7..da8d87e07 100644 --- a/nova/tests/integrated/api/client.py +++ b/nova/tests/integrated/api/client.py @@ -172,27 +172,6 @@ class TestOpenStackClient(object): kwargs.setdefault('check_response_status', [200, 202]) return self.api_request(relative_uri, **kwargs) - def get_keys_detail(self): - return self.api_get('/keys/detail')['keys'] - - def post_key(self, key): - return self.api_post('/keys', key)['key'] - - def delete_key(self, key_id): - return self.api_delete('/keys/%s' % key_id) - - def get_volume(self, volume_id): - return self.api_get('/volumes/%s' % volume_id)['volume'] - - def get_volumes_detail(self): - return self.api_get('/volumes/detail')['volumes'] - - def post_volume(self, volume): - return self.api_post('/volumes', volume)['volume'] - - def delete_volume(self, volume_id): - return self.api_delete('/volumes/%s' % volume_id) - def get_server(self, server_id): return self.api_get('/servers/%s' % server_id)['server'] -- cgit From 29bc4f5074ca3ada98a25a745077b998b4c5509c Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Thu, 10 Mar 2011 14:14:01 -0800 Subject: Pep8 / Style --- nova/tests/integrated/test_login.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/integrated/test_login.py b/nova/tests/integrated/test_login.py index 990dcaaf4..e362f92d6 100644 --- a/nova/tests/integrated/test_login.py +++ b/nova/tests/integrated/test_login.py @@ -22,6 +22,7 @@ from nova.log import logging from nova.tests.integrated import integrated_helpers from nova.tests.integrated.api import client + LOG = logging.getLogger('nova.tests.integrated') FLAGS = flags.FLAGS @@ -63,7 +64,6 @@ class LoginTest(unittest.TestCase): self.assertRaises(client.OpenstackApiAuthenticationException, bad_credentials_api.get_flavors) - def test_bad_login_both_bad(self): """Test that I get a 401 with both bad username and bad password""" bad_credentials_api = client.TestOpenStackClient("notso_username", -- cgit From 0d3e950ed4b0c8abbd619d4ac8724b4c3ce45bf1 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Thu, 10 Mar 2011 14:21:36 -0800 Subject: Document known bug numbers by the code which is degraded until the bugs are fixed --- nova/tests/integrated/api/client.py | 1 + nova/tests/integrated/integrated_helpers.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py index da8d87e07..6fba2930a 100644 --- a/nova/tests/integrated/api/client.py +++ b/nova/tests/integrated/api/client.py @@ -109,6 +109,7 @@ class TestOpenStackClient(object): LOG.debug(_("%(auth_uri)s => code %(http_status)s") % locals()) # Until bug732866 is fixed, we can't check this properly... + # bug732866 #if http_status == 401: if http_status != 204: raise OpenstackApiAuthenticationException(response=response) diff --git a/nova/tests/integrated/integrated_helpers.py b/nova/tests/integrated/integrated_helpers.py index 691ead6e1..47093636e 100644 --- a/nova/tests/integrated/integrated_helpers.py +++ b/nova/tests/integrated/integrated_helpers.py @@ -104,6 +104,7 @@ class IntegratedUnitTestContext(object): def _start_services(self): # WSGI shutdown broken :-( + # bug731668 if not self.api_service: self._start_api_service() @@ -112,6 +113,7 @@ class IntegratedUnitTestContext(object): service.kill() self.services = [] # TODO(justinsb): Shutdown WSGI & anything else we startup + # bug731668 # WSGI shutdown broken :-( # self.wsgi_server.terminate() # self.wsgi_server = None @@ -124,6 +126,7 @@ class IntegratedUnitTestContext(object): auth_key = generate_random_alphanumeric(16) # Right now there's a bug where auth_name and auth_key are reversed + # bug732907 auth_key = auth_name self.auth_manager.create_user(auth_name, auth_name, auth_key, False) @@ -156,6 +159,7 @@ class IntegratedUnitTestContext(object): return api_service # WSGI shutdown broken :-( + # bug731668 #@staticmethod #def get(): # if not IntegratedUnitTestContext.__INSTANCE: @@ -166,6 +170,7 @@ class IntegratedUnitTestContext(object): @staticmethod def startup(): # Because WSGI shutdown is broken at the moment, we have to recycle + # bug731668 if IntegratedUnitTestContext.__INSTANCE: #raise Error("Multiple calls to IntegratedUnitTestContext.startup") IntegratedUnitTestContext.__INSTANCE.setup() @@ -179,4 +184,5 @@ class IntegratedUnitTestContext(object): raise Error("Must call IntegratedUnitTestContext::startup") IntegratedUnitTestContext.__INSTANCE.cleanup() # WSGI shutdown broken :-( + # bug731668 #IntegratedUnitTestContext.__INSTANCE = None -- cgit From 2b20306fcaddcb6b9bc57fb55b17230d709cd1ce Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Mon, 14 Mar 2011 22:23:38 -0700 Subject: Derive unit test from standard nova.test.TestCase --- nova/tests/integrated/test_login.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nova/tests/integrated/test_login.py b/nova/tests/integrated/test_login.py index e362f92d6..5fa558bdf 100644 --- a/nova/tests/integrated/test_login.py +++ b/nova/tests/integrated/test_login.py @@ -18,6 +18,7 @@ import unittest from nova import flags +from nova import test from nova.log import logging from nova.tests.integrated import integrated_helpers from nova.tests.integrated.api import client @@ -29,7 +30,7 @@ FLAGS = flags.FLAGS FLAGS.verbose = True -class LoginTest(unittest.TestCase): +class LoginTest(test.TestCase): def setUp(self): super(LoginTest, self).setUp() context = integrated_helpers.IntegratedUnitTestContext.startup() @@ -73,5 +74,6 @@ class LoginTest(unittest.TestCase): self.assertRaises(client.OpenstackApiAuthenticationException, bad_credentials_api.get_flavors) + if __name__ == "__main__": unittest.main() -- cgit From db8beffc9acd90c748512c1fa9c127d39756232c Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Mon, 14 Mar 2011 22:36:30 -0700 Subject: Reapplied rename of Openstack -> OpenStack. Easier to do it by hand than to ask Bazaar to do it. --- nova/tests/integrated/api/client.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py index 6fba2930a..568e8c17e 100644 --- a/nova/tests/integrated/api/client.py +++ b/nova/tests/integrated/api/client.py @@ -24,7 +24,7 @@ from nova import log as logging LOG = logging.getLogger('nova.tests.api') -class OpenstackApiException(Exception): +class OpenStackApiException(Exception): def __init__(self, message=None, response=None): self.response = response if not message: @@ -37,22 +37,22 @@ class OpenstackApiException(Exception): message = _('%(message)s\nStatus Code: %(_status)s\n' 'Body: %(_body)s') % locals() - super(OpenstackApiException, self).__init__(message) + super(OpenStackApiException, self).__init__(message) -class OpenstackApiAuthenticationException(OpenstackApiException): +class OpenStackApiAuthenticationException(OpenStackApiException): def __init__(self, response=None, message=None): if not message: message = _("Authentication error") - super(OpenstackApiAuthenticationException, self).__init__(message, + super(OpenStackApiAuthenticationException, self).__init__(message, response) -class OpenstackApiNotFoundException(OpenstackApiException): +class OpenStackApiNotFoundException(OpenStackApiException): def __init__(self, response=None, message=None): if not message: message = _("Item not found") - super(OpenstackApiNotFoundException, self).__init__(message, response) + super(OpenStackApiNotFoundException, self).__init__(message, response) class TestOpenStackClient(object): @@ -82,7 +82,7 @@ class TestOpenStackClient(object): conn = httplib.HTTPSConnection(hostname, port=port) else: - raise OpenstackApiException("Unknown scheme: %s" % url) + raise OpenStackApiException("Unknown scheme: %s" % url) relative_url = parsed_url.path if parsed_url.query: @@ -112,7 +112,7 @@ class TestOpenStackClient(object): # bug732866 #if http_status == 401: if http_status != 204: - raise OpenstackApiAuthenticationException(response=response) + raise OpenStackApiAuthenticationException(response=response) auth_headers = {} for k, v in response.getheaders(): @@ -139,9 +139,9 @@ class TestOpenStackClient(object): if check_response_status: if not http_status in check_response_status: if http_status == 404: - raise OpenstackApiNotFoundException(response=response) + raise OpenStackApiNotFoundException(response=response) else: - raise OpenstackApiException( + raise OpenStackApiException( message=_("Unexpected status code"), response=response) -- cgit From e0563f49792441af106c52e662bdada3c7997feb Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Mon, 14 Mar 2011 22:43:21 -0700 Subject: Reapplied rename to another file. --- nova/tests/integrated/test_login.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/tests/integrated/test_login.py b/nova/tests/integrated/test_login.py index 5fa558bdf..501f8c919 100644 --- a/nova/tests/integrated/test_login.py +++ b/nova/tests/integrated/test_login.py @@ -53,7 +53,7 @@ class LoginTest(test.TestCase): "notso_password", self.user.auth_url) - self.assertRaises(client.OpenstackApiAuthenticationException, + self.assertRaises(client.OpenStackApiAuthenticationException, bad_credentials_api.get_flavors) def test_bad_login_username(self): @@ -62,7 +62,7 @@ class LoginTest(test.TestCase): self.user.secret, self.user.auth_url) - self.assertRaises(client.OpenstackApiAuthenticationException, + self.assertRaises(client.OpenStackApiAuthenticationException, bad_credentials_api.get_flavors) def test_bad_login_both_bad(self): @@ -71,7 +71,7 @@ class LoginTest(test.TestCase): "notso_password", self.user.auth_url) - self.assertRaises(client.OpenstackApiAuthenticationException, + self.assertRaises(client.OpenStackApiAuthenticationException, bad_credentials_api.get_flavors) -- cgit From f1acc3d199a1a92b531a3e74ed54a8b2fcdb999c Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Tue, 15 Mar 2011 13:52:03 -0700 Subject: Now that the fix for 732866, stop working around the bug --- nova/tests/integrated/api/client.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py index 568e8c17e..fc7c344e7 100644 --- a/nova/tests/integrated/api/client.py +++ b/nova/tests/integrated/api/client.py @@ -108,10 +108,7 @@ class TestOpenStackClient(object): http_status = response.status LOG.debug(_("%(auth_uri)s => code %(http_status)s") % locals()) - # Until bug732866 is fixed, we can't check this properly... - # bug732866 - #if http_status == 401: - if http_status != 204: + if http_status == 401: raise OpenStackApiAuthenticationException(response=response) auth_headers = {} -- cgit