Repository: libcloud Updated Branches: refs/heads/trunk 529a9cf59 -> 30bc00944
http://git-wip-us.apache.org/repos/asf/libcloud/blob/cd167194/libcloud/test/common/test_openstack_identity.py ---------------------------------------------------------------------- diff --git a/libcloud/test/common/test_openstack_identity.py b/libcloud/test/common/test_openstack_identity.py new file mode 100644 index 0000000..0353bc1 --- /dev/null +++ b/libcloud/test/common/test_openstack_identity.py @@ -0,0 +1,504 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 sys +import unittest +import datetime + +try: + import simplejson as json +except ImportError: + import json + +from mock import Mock + +from libcloud.utils.py3 import httplib +from libcloud.common.openstack import OpenStackBaseConnection +from libcloud.common.openstack import AUTH_TOKEN_EXPIRES_GRACE_SECONDS +from libcloud.common.openstack_identity import get_class_for_auth_version +from libcloud.common.openstack_identity import OpenStackIdentity_2_0_Connection +from libcloud.common.openstack_identity import OpenStackServiceCatalog +from libcloud.common.openstack_identity import OpenStackIdentity_3_0_Connection +from libcloud.compute.drivers.openstack import OpenStack_1_0_NodeDriver + +from libcloud.test import MockHttp +from libcloud.test.secrets import OPENSTACK_PARAMS +from libcloud.test.file_fixtures import ComputeFileFixtures +from libcloud.test.compute.test_openstack import OpenStackMockHttp +from libcloud.test.compute.test_openstack import OpenStack_2_0_MockHttp + + +class OpenStackIdentityConnectionTestCase(unittest.TestCase): + def setUp(self): + OpenStackBaseConnection.auth_url = None + OpenStackBaseConnection.conn_classes = (OpenStackMockHttp, + OpenStackMockHttp) + + def test_auth_url_is_correctly_assembled(self): + tuples = [ + ('1.0', OpenStackMockHttp), + ('1.1', OpenStackMockHttp), + ('2.0', OpenStack_2_0_MockHttp), + ('2.0_apikey', OpenStack_2_0_MockHttp), + ('2.0_password', OpenStack_2_0_MockHttp) + ] + + APPEND = 0 + NOTAPPEND = 1 + + auth_urls = [ + ('https://auth.api.example.com', APPEND, ''), + ('https://auth.api.example.com/', NOTAPPEND, '/'), + ('https://auth.api.example.com/foo/bar', NOTAPPEND, '/foo/bar'), + ('https://auth.api.example.com/foo/bar/', NOTAPPEND, '/foo/bar/') + ] + + actions = { + '1.0': '/v1.0', + '1.1': '/v1.1/auth', + '2.0': '/v2.0/tokens', + '2.0_apikey': '/v2.0/tokens', + '2.0_password': '/v2.0/tokens' + } + + user_id = OPENSTACK_PARAMS[0] + key = OPENSTACK_PARAMS[1] + + for (auth_version, mock_http_class) in tuples: + for (url, should_append_default_path, expected_path) in auth_urls: + connection = \ + self._get_mock_connection(mock_http_class=mock_http_class, + auth_url=url) + + auth_url = connection.auth_url + cls = get_class_for_auth_version(auth_version=auth_version) + osa = cls(auth_url=auth_url, + user_id=user_id, + key=key, + parent_conn=connection) + + try: + osa = osa.authenticate() + except: + pass + + if (should_append_default_path == APPEND): + expected_path = actions[auth_version] + + self.assertEqual(osa.action, expected_path) + + def test_basic_authentication(self): + tuples = [ + ('1.0', OpenStackMockHttp), + ('1.1', OpenStackMockHttp), + ('2.0', OpenStack_2_0_MockHttp), + ('2.0_apikey', OpenStack_2_0_MockHttp), + ('2.0_password', OpenStack_2_0_MockHttp) + ] + + user_id = OPENSTACK_PARAMS[0] + key = OPENSTACK_PARAMS[1] + + for (auth_version, mock_http_class) in tuples: + connection = \ + self._get_mock_connection(mock_http_class=mock_http_class) + auth_url = connection.auth_url + + cls = get_class_for_auth_version(auth_version=auth_version) + osa = cls(auth_url=auth_url, user_id=user_id, key=key, + parent_conn=connection) + + self.assertEqual(osa.urls, {}) + self.assertEqual(osa.auth_token, None) + self.assertEqual(osa.auth_user_info, None) + osa = osa.authenticate() + + self.assertTrue(len(osa.urls) >= 1) + self.assertTrue(osa.auth_token is not None) + + if auth_version in ['1.1', '2.0', '2.0_apikey', '2.0_password']: + self.assertTrue(osa.auth_token_expires is not None) + + if auth_version in ['2.0', '2.0_apikey', '2.0_password']: + self.assertTrue(osa.auth_user_info is not None) + + def test_token_expiration_and_force_reauthentication(self): + user_id = OPENSTACK_PARAMS[0] + key = OPENSTACK_PARAMS[1] + + connection = self._get_mock_connection(OpenStack_2_0_MockHttp) + auth_url = connection.auth_url + + yesterday = datetime.datetime.today() - datetime.timedelta(1) + tomorrow = datetime.datetime.today() + datetime.timedelta(1) + + osa = OpenStackIdentity_2_0_Connection(auth_url=auth_url, + user_id=user_id, + key=key, + parent_conn=connection) + + mocked_auth_method = Mock(wraps=osa._authenticate_2_0_with_body) + osa._authenticate_2_0_with_body = mocked_auth_method + + # Force re-auth, expired token + osa.auth_token = None + osa.auth_token_expires = yesterday + count = 5 + + for i in range(0, count): + osa.authenticate(force=True) + + self.assertEqual(mocked_auth_method.call_count, count) + + # No force reauth, expired token + osa.auth_token = None + osa.auth_token_expires = yesterday + + mocked_auth_method.call_count = 0 + self.assertEqual(mocked_auth_method.call_count, 0) + + for i in range(0, count): + osa.authenticate(force=False) + + self.assertEqual(mocked_auth_method.call_count, 1) + + # No force reauth, valid / non-expired token + osa.auth_token = None + + mocked_auth_method.call_count = 0 + self.assertEqual(mocked_auth_method.call_count, 0) + + for i in range(0, count): + osa.authenticate(force=False) + + if i == 0: + osa.auth_token_expires = tomorrow + + self.assertEqual(mocked_auth_method.call_count, 1) + + # No force reauth, valid / non-expired token which is about to expire in + # less than AUTH_TOKEN_EXPIRES_GRACE_SECONDS + soon = datetime.datetime.utcnow() + \ + datetime.timedelta(seconds=AUTH_TOKEN_EXPIRES_GRACE_SECONDS - 1) + osa.auth_token = None + + mocked_auth_method.call_count = 0 + self.assertEqual(mocked_auth_method.call_count, 0) + + for i in range(0, count): + if i == 0: + osa.auth_token_expires = soon + + osa.authenticate(force=False) + + self.assertEqual(mocked_auth_method.call_count, 1) + + def _get_mock_connection(self, mock_http_class, auth_url=None): + OpenStackBaseConnection.conn_classes = (mock_http_class, + mock_http_class) + + if auth_url is None: + auth_url = "https://auth.api.example.com" + + OpenStackBaseConnection.auth_url = auth_url + connection = OpenStackBaseConnection(*OPENSTACK_PARAMS) + + connection._ex_force_base_url = "https://www.foo.com" + connection.driver = OpenStack_1_0_NodeDriver(*OPENSTACK_PARAMS) + + return connection + + +class OpenStackIdentity_3_0_ConnectionTests(unittest.TestCase): + def setUp(self): + mock_cls = OpenStackIdentity_3_0_MockHttp + OpenStackIdentity_3_0_Connection.conn_classes = (mock_cls, mock_cls) + + self.auth_instance = OpenStackIdentity_3_0_Connection(auth_url='http://none', + user_id='test', + key='test') + self.auth_instance.auth_token = 'mock' + + def test_list_domains(self): + domains = self.auth_instance.list_domains() + self.assertEqual(len(domains), 1) + self.assertEqual(domains[0].id, 'default') + self.assertEqual(domains[0].name, 'Default') + self.assertTrue(domains[0].enabled) + + def test_list_projects(self): + projects = self.auth_instance.list_projects() + self.assertEqual(len(projects), 4) + self.assertEqual(projects[0].id, 'a') + self.assertEqual(projects[0].domain_id, 'default') + self.assertTrue(projects[0].enabled) + self.assertEqual(projects[0].description, 'Test project') + + def test_list_users(self): + users = self.auth_instance.list_users() + self.assertEqual(len(users), 12) + self.assertEqual(users[0].id, 'a') + self.assertEqual(users[0].domain_id, 'default') + self.assertEqual(users[0].enabled, True) + self.assertEqual(users[0].email, 'openstack-test@localhost') + + def test_list_roles(self): + roles = self.auth_instance.list_roles() + self.assertEqual(len(roles), 2) + self.assertEqual(roles[1].id, 'b') + self.assertEqual(roles[1].name, 'admin') + + def test_list_user_projects(self): + user = self.auth_instance.list_users()[0] + projects = self.auth_instance.list_user_projects(user=user) + self.assertEqual(len(projects), 0) + + def test_list_user_domain_roles(self): + user = self.auth_instance.list_users()[0] + domain = self.auth_instance.list_domains()[0] + roles = self.auth_instance.list_user_domain_roles(domain=domain, + user=user) + self.assertEqual(len(roles), 1) + self.assertEqual(roles[0].name, 'admin') + + def test_get_domain(self): + domain = self.auth_instance.get_domain(domain_id='default') + self.assertEqual(domain.name, 'Default') + + def test_create_user(self): + user = self.auth_instance.create_user(email='test2@localhost', password='test1', + name='test2', domain_id='default') + + self.assertEqual(user.id, 'c') + self.assertEqual(user.name, 'test2') + + def test_grant_role_to_user(self): + domain = self.auth_instance.list_domains()[0] + role = self.auth_instance.list_roles()[0] + user = self.auth_instance.list_users()[0] + + result = self.auth_instance.grant_role_to_user(domain=domain, + role=role, + user=user) + self.assertTrue(result) + + def test_revoke_role_from_user(self): + domain = self.auth_instance.list_domains()[0] + role = self.auth_instance.list_roles()[0] + user = self.auth_instance.list_users()[0] + + result = self.auth_instance.revoke_role_from_user(domain=domain, + role=role, + user=user) + self.assertTrue(result) + + +class OpenStackServiceCatalogTestCase(unittest.TestCase): + fixtures = ComputeFileFixtures('openstack') + + def test_parsing_auth_v1_1(self): + data = self.fixtures.load('_v1_1__auth.json') + data = json.loads(data) + service_catalog = data['auth']['serviceCatalog'] + + catalog = OpenStackServiceCatalog(service_catalog=service_catalog, + auth_version='1.0') + entries = catalog.get_entries() + self.assertEqual(len(entries), 3) + + entry = [e for e in entries if e.service_type == 'cloudFilesCDN'][0] + self.assertEqual(entry.service_type, 'cloudFilesCDN') + self.assertEqual(entry.service_name, None) + self.assertEqual(len(entry.endpoints), 2) + self.assertEqual(entry.endpoints[0].region, 'ORD') + self.assertEqual(entry.endpoints[0].url, + 'https://cdn2.clouddrive.com/v1/MossoCloudFS') + self.assertEqual(entry.endpoints[0].endpoint_type, 'external') + self.assertEqual(entry.endpoints[1].region, 'LON') + self.assertEqual(entry.endpoints[1].endpoint_type, 'external') + + def test_parsing_auth_v2(self): + data = self.fixtures.load('_v2_0__auth.json') + data = json.loads(data) + service_catalog = data['access']['serviceCatalog'] + + catalog = OpenStackServiceCatalog(service_catalog=service_catalog, + auth_version='2.0') + entries = catalog.get_entries() + self.assertEqual(len(entries), 6) + + entry = [e for e in entries if e.service_name == 'cloudServers'][0] + self.assertEqual(entry.service_type, 'compute') + self.assertEqual(entry.service_name, 'cloudServers') + self.assertEqual(len(entry.endpoints), 1) + self.assertEqual(entry.endpoints[0].region, None) + self.assertEqual(entry.endpoints[0].url, + 'https://servers.api.rackspacecloud.com/v1.0/1337') + self.assertEqual(entry.endpoints[0].endpoint_type, 'external') + + def test_parsing_auth_v3(self): + data = self.fixtures.load('_v3__auth.json') + data = json.loads(data) + service_catalog = data['token']['catalog'] + + catalog = OpenStackServiceCatalog(service_catalog=service_catalog, + auth_version='3.x') + entries = catalog.get_entries() + self.assertEqual(len(entries), 6) + entry = [e for e in entries if e.service_type == 'volume'][0] + self.assertEqual(entry.service_type, 'volume') + self.assertEqual(entry.service_name, None) + self.assertEqual(len(entry.endpoints), 3) + self.assertEqual(entry.endpoints[0].region, 'regionOne') + self.assertEqual(entry.endpoints[0].endpoint_type, 'external') + self.assertEqual(entry.endpoints[1].region, 'regionOne') + self.assertEqual(entry.endpoints[1].endpoint_type, 'admin') + self.assertEqual(entry.endpoints[2].region, 'regionOne') + self.assertEqual(entry.endpoints[2].endpoint_type, 'internal') + + def test_get_public_urls(self): + data = self.fixtures.load('_v2_0__auth.json') + data = json.loads(data) + service_catalog = data['access']['serviceCatalog'] + + catalog = OpenStackServiceCatalog(service_catalog=service_catalog, + auth_version='2.0') + + public_urls = catalog.get_public_urls(service_type='object-store') + expected_urls = ['https://storage101.lon1.clouddrive.com/v1/MossoCloudFS_11111-111111111-1111111111-1111111', + 'https://storage101.ord1.clouddrive.com/v1/MossoCloudFS_11111-111111111-1111111111-1111111'] + self.assertEqual(public_urls, expected_urls) + + def test_get_regions(self): + data = self.fixtures.load('_v2_0__auth.json') + data = json.loads(data) + service_catalog = data['access']['serviceCatalog'] + + catalog = OpenStackServiceCatalog(service_catalog=service_catalog, + auth_version='2.0') + + regions = catalog.get_regions(service_type='object-store') + self.assertEqual(regions, ['LON', 'ORD']) + + regions = catalog.get_regions(service_type='invalid') + self.assertEqual(regions, []) + + def test_get_service_types(self): + data = self.fixtures.load('_v2_0__auth.json') + data = json.loads(data) + service_catalog = data['access']['serviceCatalog'] + + catalog = OpenStackServiceCatalog(service_catalog=service_catalog, + auth_version='2.0') + service_types = catalog.get_service_types() + self.assertEqual(service_types, ['compute', 'object-store', + 'rax:object-cdn']) + + service_types = catalog.get_service_types(region='ORD') + self.assertEqual(service_types, ['rax:object-cdn']) + + def test_get_service_names(self): + data = self.fixtures.load('_v2_0__auth.json') + data = json.loads(data) + service_catalog = data['access']['serviceCatalog'] + + catalog = OpenStackServiceCatalog(service_catalog=service_catalog, + auth_version='2.0') + + service_names = catalog.get_service_names() + self.assertEqual(service_names, ['cloudFiles', 'cloudFilesCDN', + 'cloudServers', + 'cloudServersOpenStack', + 'cloudServersPreprod', + 'nova']) + + service_names = catalog.get_service_names(service_type='compute') + self.assertEqual(service_names, ['cloudServers', + 'cloudServersOpenStack', + 'cloudServersPreprod', + 'nova']) + + +class OpenStackIdentity_3_0_MockHttp(MockHttp): + fixtures = ComputeFileFixtures('openstack_identity') + json_content_headers = {'content-type': 'application/json; charset=UTF-8'} + + def _v3_domains(self, method, url, body, headers): + if method == 'GET': + body = self.fixtures.load('v3_domains.json') + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + raise NotImplementedError() + + def _v3_projects(self, method, url, body, headers): + if method == 'GET': + body = self.fixtures.load('v3_projects.json') + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + raise NotImplementedError() + + def _v3_users(self, method, url, body, headers): + if method == 'GET': + # list users + body = self.fixtures.load('v3_users.json') + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + elif method == 'POST': + # create user + body = self.fixtures.load('v3_create_user.json') + return (httplib.CREATED, body, self.json_content_headers, + httplib.responses[httplib.CREATED]) + raise NotImplementedError() + + def _v3_roles(self, method, url, body, headers): + if method == 'GET': + body = self.fixtures.load('v3_roles.json') + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + raise NotImplementedError() + + def _v3_domains_default_users_a_roles_a(self, method, url, body, headers): + if method == 'PUT': + # grant role + body = '' + return (httplib.NO_CONTENT, body, self.json_content_headers, + httplib.responses[httplib.NO_CONTENT]) + elif method == 'DELETE': + # revoke role + body = '' + return (httplib.NO_CONTENT, body, self.json_content_headers, + httplib.responses[httplib.NO_CONTENT]) + raise NotImplementedError() + + def _v3_domains_default(self, method, url, body, headers): + if method == 'GET': + # get domain + body = self.fixtures.load('v3_domains_default.json') + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + raise NotImplementedError() + + def _v3_users_a_projects(self, method, url, body, headers): + if method == 'GET': + # get user projects + body = self.fixtures.load('v3_users_a_projects.json') + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + raise NotImplementedError() + + def _v3_domains_default_users_a_roles(self, method, url, body, headers): + if method == 'GET': + # get user domain roles + body = self.fixtures.load('v3_domains_default_users_a_roles.json') + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + raise NotImplementedError() + + +if __name__ == '__main__': + sys.exit(unittest.main()) http://git-wip-us.apache.org/repos/asf/libcloud/blob/cd167194/libcloud/test/compute/fixtures/openstack/_v3__auth.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/openstack/_v3__auth.json b/libcloud/test/compute/fixtures/openstack/_v3__auth.json new file mode 100644 index 0000000..de78703 --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack/_v3__auth.json @@ -0,0 +1,182 @@ +{ + "token": { + "methods": [ + "password" + ], + "roles": [ + { + "id": "9fe2ff9ee4384b1894a90878d3e92bab", + "name": "_member_" + }, + { + "id": "b258b68172db4403892320f784c4d503", + "name": "admin" + } + ], + "expires_at": "2014-08-10T19:15:57.096078Z", + "project": { + "domain": { + "id": "default", + "name": "Default" + }, + "id": "9c4693dce56b493b9b83197d900f7fba", + "name": "admin" + }, + "catalog": [ + { + "endpoints": [ + { + "url": "http://controller:8774/v2/9c4693dce56b493b9b83197d900f7fba", + "region": "regionOne", + "interface": "internal", + "id": "b3bfb29033ff4add9c97e523e1022794" + }, + { + "url": "http://192.168.18.100:8774/v2/9c4693dce56b493b9b83197d900f7fba", + "region": "regionOne", + "interface": "admin", + "id": "b52ee215ded7473f94a46512cb94dbf1" + }, + { + "url": "http://192.168.18.100:8774/v2/9c4693dce56b493b9b83197d900f7fba", + "region": "regionOne", + "interface": "public", + "id": "ca8a6e39b9334300bf036c0c4226a173" + } + ], + "type": "compute", + "id": "03f123b2253e4852a86b994f86489c0a" + }, + { + "endpoints": [ + { + "url": "http://192.168.18.100:8776/v1/9c4693dce56b493b9b83197d900f7fba", + "region": "regionOne", + "interface": "public", + "id": "20bf617f334c4bcf82746820f5006599" + }, + { + "url": "http://192.168.18.100:8776/v1/9c4693dce56b493b9b83197d900f7fba", + "region": "regionOne", + "interface": "admin", + "id": "2da639c26463424fa9775e0bf4e9f29e" + }, + { + "url": "http://controller:8776/v2/9c4693dce56b493b9b83197d900f7fba", + "region": "regionOne", + "interface": "internal", + "id": "d568ed6f8c5a4649a6e68b7bcb86694b" + } + ], + "type": "volume", + "id": "47f77ba8f3864a03b66024e910ad7247" + }, + { + "endpoints": [ + { + "url": "http://192.168.18.100:9696", + "region": "regionOne", + "interface": "admin", + "id": "720303f92f81404aa80caa32cd9c7d23" + }, + { + "url": "http://192.168.18.100:9696", + "region": "regionOne", + "interface": "public", + "id": "8823b9edba354bb6bdc944a6b3bb5404" + }, + { + "url": "http://controller:9696", + "region": "regionOne", + "interface": "internal", + "id": "c2a522538037492dbec2173f271ecb32" + } + ], + "type": "network", + "id": "9bd61e09d372427f81eca9328f33c510" + }, + { + "endpoints": [ + { + "url": "http://controller:5000/v2.0", + "region": "regionOne", + "interface": "internal", + "id": "802622da0a874cac8fe2ec7a02d87c44" + }, + { + "url": "http://192.168.18.100:35357/v2.0", + "region": "regionOne", + "interface": "admin", + "id": "8a4eed85ddc748b18cc2b92e64291eb5" + }, + { + "url": "http://192.168.18.100:5000/v2.0", + "region": "regionOne", + "interface": "public", + "id": "9ef69c1600a944b9904f34efb6dc67eb" + } + ], + "type": "identity", + "id": "aef833a14f4240d0bbb699f0154add8e" + }, + { + "endpoints": [ + { + "url": "http://192.168.18.100:9292", + "region": "regionOne", + "interface": "public", + "id": "1aa84aebd3e2467e898e3c18428e3feb" + }, + { + "url": "http://192.168.18.100:9292", + "region": "regionOne", + "interface": "admin", + "id": "3f6aa4ffd0ec47d2862eee1648993bef" + }, + { + "url": "http://192.168.200.1:9292", + "region": "regionOne", + "interface": "internal", + "id": "9f66f90af36949479a6365680afabe12" + } + ], + "type": "image", + "id": "c0be10ea61e240f99567f328b9adf3d6" + }, + { + "endpoints": [ + { + "url": "http://192.168.18.100:8776/v2/9c4693dce56b493b9b83197d900f7fba", + "region": "regionOne", + "interface": "public", + "id": "6c6b0990ccf84f1890e404fddad7b6e5" + }, + { + "url": "http://192.168.18.100:8776/v2/9c4693dce56b493b9b83197d900f7fba", + "region": "regionOne", + "interface": "admin", + "id": "ab0f0bd770494d4399036867935c52ea" + }, + { + "url": "http://controller:8776/v2/9c4693dce56b493b9b83197d900f7fba", + "region": "regionOne", + "interface": "internal", + "id": "fc7c82deda034e52a544da7d00cd28de" + } + ], + "type": "volumev2", + "id": "e097b64d701e4ce29f2a69eed4e69856" + } + ], + "extras": {}, + "user": { + "domain": { + "id": "default", + "name": "Default" + }, + "id": "55fba80f022b4855acfc700ae13b2b24", + "name": "admin" + }, + "issued_at": "2014-08-10T18:15:57.096107Z" + } +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/cd167194/libcloud/test/compute/fixtures/openstack_identity/v3_create_user.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/openstack_identity/v3_create_user.json b/libcloud/test/compute/fixtures/openstack_identity/v3_create_user.json new file mode 100644 index 0000000..82445dc --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_identity/v3_create_user.json @@ -0,0 +1,12 @@ +{ + "user": { + "name": "test2", + "links": { + "self": "http://192.168.18.100:5000/v3/users/c" + }, + "domain_id": "default", + "enabled": true, + "email": "test2@localhost", + "id": "c" + } +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/cd167194/libcloud/test/compute/fixtures/openstack_identity/v3_domains.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/openstack_identity/v3_domains.json b/libcloud/test/compute/fixtures/openstack_identity/v3_domains.json new file mode 100644 index 0000000..7813acf --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_identity/v3_domains.json @@ -0,0 +1,18 @@ +{ + "domains": [ + { + "links": { + "self": "http://192.168.18.100:5000/v3/domains/default" + }, + "enabled": true, + "description": "Owns users and tenants (i.e. projects) available on Identity API v2.", + "name": "Default", + "id": "default" + } + ], + "links": { + "self": "http://192.168.18.100:5000/v3/domains", + "previous": null, + "next": null + } +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/cd167194/libcloud/test/compute/fixtures/openstack_identity/v3_domains_default.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/openstack_identity/v3_domains_default.json b/libcloud/test/compute/fixtures/openstack_identity/v3_domains_default.json new file mode 100644 index 0000000..f00230f --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_identity/v3_domains_default.json @@ -0,0 +1,11 @@ +{ + "domain": { + "links": { + "self": "http://192.168.18.100:5000/v3/domains/default" + }, + "enabled": true, + "description": "Owns users and tenants (i.e. projects) available on Identity API v2.", + "name": "Default", + "id": "default" + } +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/cd167194/libcloud/test/compute/fixtures/openstack_identity/v3_domains_default_users_a_roles.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/openstack_identity/v3_domains_default_users_a_roles.json b/libcloud/test/compute/fixtures/openstack_identity/v3_domains_default_users_a_roles.json new file mode 100644 index 0000000..0312072 --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_identity/v3_domains_default_users_a_roles.json @@ -0,0 +1,16 @@ +{ + "links": { + "self": "http://192.168.18.100:5000/v3/domains/default/users/a/roles", + "previous": null, + "next": null + }, + "roles": [ + { + "id": "d", + "links": { + "self": "http://192.168.18.100:5000/v3/roles/d" + }, + "name": "admin" + } + ] +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/cd167194/libcloud/test/compute/fixtures/openstack_identity/v3_projects.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/openstack_identity/v3_projects.json b/libcloud/test/compute/fixtures/openstack_identity/v3_projects.json new file mode 100644 index 0000000..16acbce --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_identity/v3_projects.json @@ -0,0 +1,49 @@ +{ + "links": { + "self": "http://192.168.18.100:5000/v3/projects", + "previous": null, + "next": null + }, + "projects": [ + { + "description": "Test project", + "links": { + "self": "http://192.168.18.100:5000/v3/projects/a" + }, + "enabled": true, + "id": "a", + "domain_id": "default", + "name": "divvy" + }, + { + "description": "Admin Tenant", + "links": { + "self": "http://192.168.18.100:5000/v3/projects/b" + }, + "enabled": true, + "id": "b", + "domain_id": "default", + "name": "admin" + }, + { + "description": "Initial tenant", + "links": { + "self": "http://192.168.18.100:5000/v3/projects/c" + }, + "enabled": true, + "id": "c", + "domain_id": "default", + "name": "first-tenant" + }, + { + "description": "Service Tenant", + "links": { + "self": "http://192.168.18.100:5000/v3/projects/d" + }, + "enabled": true, + "id": "d", + "domain_id": "default", + "name": "service" + } + ] +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/cd167194/libcloud/test/compute/fixtures/openstack_identity/v3_roles.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/openstack_identity/v3_roles.json b/libcloud/test/compute/fixtures/openstack_identity/v3_roles.json new file mode 100644 index 0000000..5785484 --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_identity/v3_roles.json @@ -0,0 +1,25 @@ +{ + "links": { + "self": "http://192.168.18.100:5000/v3/roles", + "previous": null, + "next": null + }, + "roles": [ + { + "links": { + "self": "http://192.168.18.100:5000/v3/roles/a" + }, + "enabled": "True", + "description": "Default role for project membership", + "name": "_member_", + "id": "a" + }, + { + "id": "b", + "links": { + "self": "http://192.168.18.100:5000/v3/roles/b" + }, + "name": "admin" + } + ] +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/cd167194/libcloud/test/compute/fixtures/openstack_identity/v3_users.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/openstack_identity/v3_users.json b/libcloud/test/compute/fixtures/openstack_identity/v3_users.json new file mode 100644 index 0000000..d4763ea --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_identity/v3_users.json @@ -0,0 +1,131 @@ +{ + "users": [ + { + "name": "cloud", + "links": { + "self": "http://192.168.18.100:5000/v3/users/a" + }, + "domain_id": "default", + "enabled": true, + "email": "openstack-test@localhost", + "id": "a" + }, + { + "name": "trove", + "links": { + "self": "http://192.168.18.100:5000/v3/users/1ec8cface8614a2786c99c87c7116f09" + }, + "domain_id": "default", + "enabled": true, + "email": "trove@localhost", + "id": "1ec8cface8614a2786c99c87c7116f09" + }, + { + "domain_id": "default", + "name": "tomaz", + "links": { + "self": "http://192.168.18.100:5000/v3/users/458f20357227462e8a17355628984515" + }, + "id": "458f20357227462e8a17355628984515", + "enabled": true, + "email": "[email protected]", + "default_project_id": "3130562cafe147f289bbb3b557f2e7ed" + }, + { + "name": "admin", + "links": { + "self": "http://192.168.18.100:5000/v3/users/55fba80f022b4855acfc700ae13b2b24" + }, + "domain_id": "default", + "enabled": true, + "email": "openstack-test@localhost", + "id": "55fba80f022b4855acfc700ae13b2b24" + }, + { + "name": "nova", + "links": { + "self": "http://192.168.18.100:5000/v3/users/679c69c6cfd049ebb3ad85734dfea61a" + }, + "domain_id": "default", + "enabled": true, + "email": "nova@localhost", + "id": "679c69c6cfd049ebb3ad85734dfea61a" + }, + { + "name": "glance", + "links": { + "self": "http://192.168.18.100:5000/v3/users/6f10443170ad4daf81bff251944da9d7" + }, + "domain_id": "default", + "enabled": true, + "email": "glance@localhost", + "id": "6f10443170ad4daf81bff251944da9d7" + }, + { + "name": "ceilometer", + "links": { + "self": "http://192.168.18.100:5000/v3/users/747cf6a2bf75453f847f307823e8eac9" + }, + "domain_id": "default", + "enabled": true, + "email": "ceilometer@localhost", + "id": "747cf6a2bf75453f847f307823e8eac9" + }, + { + "name": "swift", + "links": { + "self": "http://192.168.18.100:5000/v3/users/a0081ee2f9674458bcd7731b6dcbf5f9" + }, + "domain_id": "default", + "enabled": true, + "email": "swift@localhost", + "id": "a0081ee2f9674458bcd7731b6dcbf5f9" + }, + { + "domain_id": "default", + "name": "amann", + "links": { + "self": "http://192.168.18.100:5000/v3/users/aafb942e4c3d4fe1ba33088f3e7c5996" + }, + "id": "aafb942e4c3d4fe1ba33088f3e7c5996", + "enabled": true, + "email": "[email protected]", + "default_project_id": "3130562cafe147f289bbb3b557f2e7ed" + }, + { + "name": "heat", + "links": { + "self": "http://192.168.18.100:5000/v3/users/d16cad10e4f648e0bd9ab5a0359f6736" + }, + "domain_id": "default", + "enabled": true, + "email": "heat@localhost", + "id": "d16cad10e4f648e0bd9ab5a0359f6736" + }, + { + "name": "cinder", + "links": { + "self": "http://192.168.18.100:5000/v3/users/f3a4590b0d66497894ee9a3d73c09a95" + }, + "domain_id": "default", + "enabled": true, + "email": "cinder@localhost", + "id": "f3a4590b0d66497894ee9a3d73c09a95" + }, + { + "name": "neutron", + "links": { + "self": "http://192.168.18.100:5000/v3/users/fa9a7fedeb2844ff89e37d70a2519441" + }, + "domain_id": "default", + "enabled": true, + "email": "neutron@localhost", + "id": "fa9a7fedeb2844ff89e37d70a2519441" + } + ], + "links": { + "self": "http://192.168.18.100:5000/v3/users", + "previous": null, + "next": null + } +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/cd167194/libcloud/test/compute/fixtures/openstack_identity/v3_users_a_projects.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/openstack_identity/v3_users_a_projects.json b/libcloud/test/compute/fixtures/openstack_identity/v3_users_a_projects.json new file mode 100644 index 0000000..e117a8b --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_identity/v3_users_a_projects.json @@ -0,0 +1,8 @@ +{ + "links": { + "self": "http://192.168.18.100:5000/v3/users/a/projects", + "previous": null, + "next": null + }, + "projects": [] +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/cd167194/libcloud/test/compute/test_openstack.py ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/test_openstack.py b/libcloud/test/compute/test_openstack.py index 77a38e9..642b7a8 100644 --- a/libcloud/test/compute/test_openstack.py +++ b/libcloud/test/compute/test_openstack.py @@ -25,7 +25,7 @@ try: except ImportError: import json -from mock import Mock +from mock import Mock, patch from libcloud.utils.py3 import httplib from libcloud.utils.py3 import method_type @@ -33,9 +33,6 @@ from libcloud.utils.py3 import u from libcloud.common.types import InvalidCredsError, MalformedResponseError, \ LibcloudError -from libcloud.common.openstack import OpenStackBaseConnection -from libcloud.common.openstack import OpenStackAuthConnection -from libcloud.common.openstack import AUTH_TOKEN_EXPIRES_GRACE_SECONDS from libcloud.compute.types import Provider, KeyPairDoesNotExistError from libcloud.compute.providers import get_driver from libcloud.compute.drivers.openstack import ( @@ -85,243 +82,6 @@ class OpenStack_1_0_ResponseTestCase(unittest.TestCase): body, RESPONSE_BODY, "Non-XML body should be returned as is") -class OpenStackServiceCatalogTests(unittest.TestCase): - # TODO refactor and move into libcloud/test/common - - def setUp(self): - OpenStackBaseConnection.conn_classes = (OpenStackMockHttp, - OpenStackMockHttp) - - connection = OpenStackBaseConnection(*OPENSTACK_PARAMS) - connection.auth_url = "https://auth.api.example.com" - connection._ex_force_base_url = "https://www.foo.com" - connection.driver = OpenStack_1_0_NodeDriver(*OPENSTACK_PARAMS) - - self.service_catalog = connection.get_service_catalog() - self.catalog = self.service_catalog.get_catalog() - - def test_connection_get_service_catalog(self): - endpoints = self.service_catalog.get_endpoints('cloudFilesCDN', 'cloudFilesCDN') - public_urls = self.service_catalog.get_public_urls('cloudFilesCDN', 'cloudFilesCDN') - - expected_urls = [ - 'https://cdn2.clouddrive.com/v1/MossoCloudFS', - 'https://cdn2.clouddrive.com/v1/MossoCloudFS' - ] - - self.assertTrue('cloudFilesCDN' in self.catalog) - self.assertEqual(len(endpoints), 2) - self.assertEqual(public_urls, expected_urls) - - def test_get_regions(self): - regions = self.service_catalog.get_regions() - self.assertEqual(sorted(regions), ['LON', 'ORD']) - - def test_get_service_types(self): - service_types = self.service_catalog.get_service_types() - self.assertEqual(sorted(service_types), ['compute', 'object-store', - 'rax:object-cdn']) - - service_types = self.service_catalog.get_service_types(region='invalid') - self.assertEqual(sorted(service_types), []) - - def test_get_service_names(self): - OpenStackBaseConnection.conn_classes = (OpenStack_2_0_MockHttp, - OpenStack_2_0_MockHttp) - OpenStackBaseConnection._auth_version = '2.0' - - connection = OpenStackBaseConnection(*OPENSTACK_PARAMS) - connection.auth_url = "https://auth.api.example.com" - connection._ex_force_base_url = "https://www.foo.com" - connection.driver = OpenStack_1_0_NodeDriver(*OPENSTACK_PARAMS) - - service_catalog = connection.get_service_catalog() - - service_names = service_catalog.get_service_names(service_type='object-store') - self.assertEqual(service_names, ['cloudFiles']) - - -class OpenStackAuthConnectionTests(unittest.TestCase): - # TODO refactor and move into libcloud/test/common - - def setUp(self): - OpenStackBaseConnection.auth_url = None - OpenStackBaseConnection.conn_classes = (OpenStackMockHttp, - OpenStackMockHttp) - - def test_auth_url_is_correctly_assembled(self): - tuples = [ - ('1.0', OpenStackMockHttp), - ('1.1', OpenStackMockHttp), - ('2.0', OpenStack_2_0_MockHttp), - ('2.0_apikey', OpenStack_2_0_MockHttp), - ('2.0_password', OpenStack_2_0_MockHttp) - ] - - APPEND = 0 - NOTAPPEND = 1 - - auth_urls = [ - ('https://auth.api.example.com', APPEND, ''), - ('https://auth.api.example.com/', NOTAPPEND, '/'), - ('https://auth.api.example.com/foo/bar', NOTAPPEND, '/foo/bar'), - ('https://auth.api.example.com/foo/bar/', NOTAPPEND, '/foo/bar/') - ] - - actions = { - '1.0': '/v1.0', - '1.1': '/v1.1/auth', - '2.0': '/v2.0/tokens', - '2.0_apikey': '/v2.0/tokens', - '2.0_password': '/v2.0/tokens' - } - - user_id = OPENSTACK_PARAMS[0] - key = OPENSTACK_PARAMS[1] - - for (auth_version, mock_http_class) in tuples: - for (url, should_append_default_path, expected_path) in auth_urls: - connection = \ - self._get_mock_connection(mock_http_class=mock_http_class, - auth_url=url) - - auth_url = connection.auth_url - - osa = OpenStackAuthConnection(connection, - auth_url, - auth_version, - user_id, key) - - try: - osa = osa.authenticate() - except: - pass - - if (should_append_default_path == APPEND): - expected_path = actions[auth_version] - - self.assertEqual(osa.action, expected_path) - - def test_basic_authentication(self): - tuples = [ - ('1.0', OpenStackMockHttp), - ('1.1', OpenStackMockHttp), - ('2.0', OpenStack_2_0_MockHttp), - ('2.0_apikey', OpenStack_2_0_MockHttp), - ('2.0_password', OpenStack_2_0_MockHttp) - ] - - user_id = OPENSTACK_PARAMS[0] - key = OPENSTACK_PARAMS[1] - - for (auth_version, mock_http_class) in tuples: - connection = \ - self._get_mock_connection(mock_http_class=mock_http_class) - auth_url = connection.auth_url - - osa = OpenStackAuthConnection(connection, auth_url, auth_version, - user_id, key) - - self.assertEqual(osa.urls, {}) - self.assertEqual(osa.auth_token, None) - self.assertEqual(osa.auth_user_info, None) - osa = osa.authenticate() - - self.assertTrue(len(osa.urls) >= 1) - self.assertTrue(osa.auth_token is not None) - - if auth_version in ['1.1', '2.0', '2.0_apikey', '2.0_password']: - self.assertTrue(osa.auth_token_expires is not None) - - if auth_version in ['2.0', '2.0_apikey', '2.0_password']: - self.assertTrue(osa.auth_user_info is not None) - - def test_token_expiration_and_force_reauthentication(self): - user_id = OPENSTACK_PARAMS[0] - key = OPENSTACK_PARAMS[1] - - connection = self._get_mock_connection(OpenStack_2_0_MockHttp) - auth_url = connection.auth_url - auth_version = '2.0' - - yesterday = datetime.datetime.today() - datetime.timedelta(1) - tomorrow = datetime.datetime.today() + datetime.timedelta(1) - - osa = OpenStackAuthConnection(connection, auth_url, auth_version, - user_id, key) - - mocked_auth_method = Mock(wraps=osa.authenticate_2_0_with_body) - osa.authenticate_2_0_with_body = mocked_auth_method - - # Force re-auth, expired token - osa.auth_token = None - osa.auth_token_expires = yesterday - count = 5 - - for i in range(0, count): - osa.authenticate(force=True) - - self.assertEqual(mocked_auth_method.call_count, count) - - # No force reauth, expired token - osa.auth_token = None - osa.auth_token_expires = yesterday - - mocked_auth_method.call_count = 0 - self.assertEqual(mocked_auth_method.call_count, 0) - - for i in range(0, count): - osa.authenticate(force=False) - - self.assertEqual(mocked_auth_method.call_count, 1) - - # No force reauth, valid / non-expired token - osa.auth_token = None - - mocked_auth_method.call_count = 0 - self.assertEqual(mocked_auth_method.call_count, 0) - - for i in range(0, count): - osa.authenticate(force=False) - - if i == 0: - osa.auth_token_expires = tomorrow - - self.assertEqual(mocked_auth_method.call_count, 1) - - # No force reauth, valid / non-expired token which is about to expire in - # less than AUTH_TOKEN_EXPIRES_GRACE_SECONDS - soon = datetime.datetime.utcnow() + \ - datetime.timedelta(seconds=AUTH_TOKEN_EXPIRES_GRACE_SECONDS - 1) - osa.auth_token = None - - mocked_auth_method.call_count = 0 - self.assertEqual(mocked_auth_method.call_count, 0) - - for i in range(0, count): - if i == 0: - osa.auth_token_expires = soon - - osa.authenticate(force=False) - - self.assertEqual(mocked_auth_method.call_count, 1) - - def _get_mock_connection(self, mock_http_class, auth_url=None): - OpenStackBaseConnection.conn_classes = (mock_http_class, - mock_http_class) - - if auth_url is None: - auth_url = "https://auth.api.example.com" - - OpenStackBaseConnection.auth_url = auth_url - connection = OpenStackBaseConnection(*OPENSTACK_PARAMS) - - connection._ex_force_base_url = "https://www.foo.com" - connection.driver = OpenStack_1_0_NodeDriver(*OPENSTACK_PARAMS) - - return connection - - class OpenStack_1_0_Tests(unittest.TestCase, TestCaseMixin): should_list_locations = False should_list_volumes = False @@ -356,7 +116,8 @@ class OpenStack_1_0_Tests(unittest.TestCase, TestCaseMixin): self.driver.connection._populate_hosts_and_request_paths() clear_pricing_data() - def test_populate_hosts_and_requests_path(self): + @patch('libcloud.common.openstack.OpenStackServiceCatalog') + def test_populate_hosts_and_requests_path(self, _): tomorrow = datetime.datetime.today() + datetime.timedelta(1) cls = self.driver_klass.connectionCls @@ -364,7 +125,7 @@ class OpenStack_1_0_Tests(unittest.TestCase, TestCaseMixin): # Test authentication and token re-use con = cls('username', 'key') - osa = con._osa + osa = con.get_auth_class() mocked_auth_method = Mock() osa.authenticate = mocked_auth_method @@ -385,7 +146,7 @@ class OpenStack_1_0_Tests(unittest.TestCase, TestCaseMixin): # ex_force_auth_token provided, authenticate should never be called con = cls('username', 'key', ex_force_base_url='http://ponies', ex_force_auth_token='1234') - osa = con._osa + osa = con.get_auth_class() mocked_auth_method = Mock() osa.authenticate = mocked_auth_method http://git-wip-us.apache.org/repos/asf/libcloud/blob/cd167194/libcloud/test/storage/test_cloudfiles.py ---------------------------------------------------------------------- diff --git a/libcloud/test/storage/test_cloudfiles.py b/libcloud/test/storage/test_cloudfiles.py index 84ec615..70e52c8 100644 --- a/libcloud/test/storage/test_cloudfiles.py +++ b/libcloud/test/storage/test_cloudfiles.py @@ -138,16 +138,6 @@ class CloudFilesTests(unittest.TestCase): self.driver.connection.get_endpoint()) self.driver.connection.cdn_request = False - def test_endpoint_pointer(self): - kwargs = {'use_internal_url': False} - driver = CloudFilesStorageDriver('driver', 'dummy', **kwargs) - self.assertEquals(driver.connection._get_endpoint_key(), libcloud.storage.drivers.cloudfiles.PUBLIC_ENDPOINT_KEY) - kwargs = {'use_internal_url': True} - driver = CloudFilesStorageDriver('driver', 'dummy', **kwargs) - self.assertEquals(driver.connection._get_endpoint_key(), libcloud.storage.drivers.cloudfiles.INTERNAL_ENDPOINT_KEY) - driver.connection.cdn_request = True - self.assertEquals(driver.connection._get_endpoint_key(), libcloud.storage.drivers.cloudfiles.PUBLIC_ENDPOINT_KEY) - def test_list_containers(self): CloudFilesMockHttp.type = 'EMPTY' containers = self.driver.list_containers()
