diff --git a/openstack/auth/service_filter.py b/openstack/auth/service_filter.py index bcd62c815..2565e2120 100644 --- a/openstack/auth/service_filter.py +++ b/openstack/auth/service_filter.py @@ -25,12 +25,25 @@ filter to match a service. from openstack import exceptions +class ValidVersion(object): + + def __init__(self, module, path=None): + """" Valid service version. + + :param string module: Module associated with version. + :param string path: URL path version. + """ + self.module = module + self.path = path or module + + class ServiceFilter(object): ANY = 'any' PUBLIC = 'public' INTERNAL = 'internal' ADMIN = 'admin' VISIBILITY = [PUBLIC, INTERNAL, ADMIN] + valid_versions = [] def __init__(self, service_type=ANY, visibility=PUBLIC, region=None, service_name=None, version=None): @@ -115,3 +128,20 @@ class ServiceFilter(object): msg = "Visibility <%s> not in %s" % (visibility, self.VISIBILITY) raise exceptions.SDKException(msg) self.visibility = visibility + + def get_module(self): + """Get the full module name associated with the service.""" + module = self.__class__.__module__.split('.') + module = ".".join(module[:-1]) + # NOTE(thowe): Only support for one valid version right now. + module = module + "." + self.valid_versions[0].module + return module + + def get_service_module(self): + """Get the module version of the service name. + + This would often be the same as the service type except in cases like + object store where the service type is `object-store` and the module + is `object_store`. + """ + return self.__class__.__module__.split('.')[1] diff --git a/openstack/compute/compute_service.py b/openstack/compute/compute_service.py index c8f1fd039..c85d2017e 100644 --- a/openstack/compute/compute_service.py +++ b/openstack/compute/compute_service.py @@ -16,6 +16,8 @@ from openstack.auth import service_filter class ComputeService(service_filter.ServiceFilter): """The compute service.""" + valid_versions = [service_filter.ValidVersion('v2')] + def __init__(self): """Create an compute service.""" super(ComputeService, self).__init__(service_type='compute') diff --git a/openstack/compute/v2/_proxy.py b/openstack/compute/v2/_proxy.py new file mode 100644 index 000000000..47dbfd889 --- /dev/null +++ b/openstack/compute/v2/_proxy.py @@ -0,0 +1,22 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from openstack.compute.v2 import flavor + + +class Proxy(object): + + def __init__(self, session): + self.session = session + + def list_flavors(self, **params): + return flavor.Flavor.list(self.session, **params) diff --git a/openstack/connection.py b/openstack/connection.py index 9b8b781e3..341fa75b4 100644 --- a/openstack/connection.py +++ b/openstack/connection.py @@ -58,6 +58,8 @@ try to find it and if that fails, you would create it:: network = conn.network.create_network({"name": "jenkins"}) """ +import logging +import sys from stevedore import driver @@ -68,6 +70,7 @@ from openstack import transport as xport USER_AGENT = 'OSPythonSDK' """Default value for the HTTP User-Agent header""" +_logger = logging.getLogger(__name__) class Connection(object): @@ -128,6 +131,7 @@ class Connection(object): **auth_args) self.session = session.Session(self.transport, self.authenticator, preference) + self._open() def _create_transport(self, transport, verify, user_agent): if transport: @@ -157,3 +161,21 @@ class Connection(object): valid_list = plugin.valid_options args = dict((n, auth_args[n]) for n in valid_list if n in auth_args) return plugin(**args) + + def _open(self): + """Open the connection. + + NOTE(thowe): Have this set up some lazy loader instead. + """ + for service in self.session.get_services(): + self._load(service) + + def _load(self, service): + attr_name = service.get_service_module() + module = service.get_module() + "._proxy" + try: + __import__(module) + proxy = getattr(sys.modules[module], "Proxy") + setattr(self, attr_name, proxy(self.session)) + except Exception as e: + _logger.warn("Unable to load %s: %s" % (module, e)) diff --git a/openstack/database/database_service.py b/openstack/database/database_service.py index b4dc94e76..04d31b12e 100644 --- a/openstack/database/database_service.py +++ b/openstack/database/database_service.py @@ -16,6 +16,8 @@ from openstack.auth import service_filter class DatabaseService(service_filter.ServiceFilter): """The database service.""" + valid_versions = [service_filter.ValidVersion('v1')] + def __init__(self): """Create an database service.""" super(DatabaseService, self).__init__(service_type='database') diff --git a/openstack/database/v1/_proxy.py b/openstack/database/v1/_proxy.py new file mode 100644 index 000000000..025230cf3 --- /dev/null +++ b/openstack/database/v1/_proxy.py @@ -0,0 +1,17 @@ +# 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. + + +class Proxy(object): + + def __init__(self, session): + self.session = session diff --git a/openstack/identity/identity_service.py b/openstack/identity/identity_service.py index 7a5fd5665..de4282a06 100644 --- a/openstack/identity/identity_service.py +++ b/openstack/identity/identity_service.py @@ -16,6 +16,11 @@ from openstack.auth import service_filter class IdentityService(service_filter.ServiceFilter): """The identity service.""" + valid_versions = [ + service_filter.ValidVersion('v3'), + service_filter.ValidVersion('v2'), + ] + def __init__(self, **kwargs): """Create an identity service.""" kwargs['service_type'] = 'identity' diff --git a/openstack/identity/v2/_proxy.py b/openstack/identity/v2/_proxy.py new file mode 100644 index 000000000..025230cf3 --- /dev/null +++ b/openstack/identity/v2/_proxy.py @@ -0,0 +1,17 @@ +# 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. + + +class Proxy(object): + + def __init__(self, session): + self.session = session diff --git a/openstack/identity/v3/_proxy.py b/openstack/identity/v3/_proxy.py new file mode 100644 index 000000000..7246e0043 --- /dev/null +++ b/openstack/identity/v3/_proxy.py @@ -0,0 +1,43 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from openstack.identity.v3 import project + + +class Proxy(object): + + def __init__(self, session): + self.session = session + + def create_project(self, **data): + obj = project.Project(**data) + obj.create(self.session) + return obj + + def get_project(self, r_id): + obj = project.Project({'id': r_id}) + obj.get(self.session) + return obj + + def update_project(self, **data): + obj = project.Project(**data) + obj.update(self.session) + + def delete_project(self, r_id): + obj = project.Project({'id': r_id}) + obj.delete(self.session) + + def list_projects(self, **params): + return project.Project.list(self.session, **params) + + def find_project(self, name_or_id): + return project.Project.find(self.session, name_or_id) diff --git a/openstack/image/image_service.py b/openstack/image/image_service.py index 94e8560e2..8eb6a8fbc 100644 --- a/openstack/image/image_service.py +++ b/openstack/image/image_service.py @@ -16,6 +16,8 @@ from openstack.auth import service_filter class ImageService(service_filter.ServiceFilter): """The image service.""" + valid_versions = [service_filter.ValidVersion('v1')] + def __init__(self): """Create an image service.""" super(ImageService, self).__init__(service_type='image') diff --git a/openstack/image/v1/_proxy.py b/openstack/image/v1/_proxy.py new file mode 100644 index 000000000..025230cf3 --- /dev/null +++ b/openstack/image/v1/_proxy.py @@ -0,0 +1,17 @@ +# 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. + + +class Proxy(object): + + def __init__(self, session): + self.session = session diff --git a/openstack/network/network_service.py b/openstack/network/network_service.py index c98d4293e..1708ee171 100644 --- a/openstack/network/network_service.py +++ b/openstack/network/network_service.py @@ -16,6 +16,8 @@ from openstack.auth import service_filter class NetworkService(service_filter.ServiceFilter): """The network service.""" + valid_versions = [service_filter.ValidVersion('v2', 'v2.0')] + def __init__(self): """Create an network service.""" super(NetworkService, self).__init__(service_type='network') diff --git a/openstack/network/v2/_proxy.py b/openstack/network/v2/_proxy.py new file mode 100644 index 000000000..025230cf3 --- /dev/null +++ b/openstack/network/v2/_proxy.py @@ -0,0 +1,17 @@ +# 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. + + +class Proxy(object): + + def __init__(self, session): + self.session = session diff --git a/openstack/object_store/object_store_service.py b/openstack/object_store/object_store_service.py index 0757e7ad3..f3c8469a3 100644 --- a/openstack/object_store/object_store_service.py +++ b/openstack/object_store/object_store_service.py @@ -16,6 +16,8 @@ from openstack.auth import service_filter class ObjectStoreService(service_filter.ServiceFilter): """The object store service.""" + valid_versions = [service_filter.ValidVersion('v1')] + def __init__(self): """Create an object store service.""" super(ObjectStoreService, self).__init__(service_type='object-store') diff --git a/openstack/object_store/v1/_proxy.py b/openstack/object_store/v1/_proxy.py new file mode 100644 index 000000000..025230cf3 --- /dev/null +++ b/openstack/object_store/v1/_proxy.py @@ -0,0 +1,17 @@ +# 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. + + +class Proxy(object): + + def __init__(self, session): + self.session = session diff --git a/openstack/orchestration/orchestration_service.py b/openstack/orchestration/orchestration_service.py index 1e581a525..bb85f45cb 100644 --- a/openstack/orchestration/orchestration_service.py +++ b/openstack/orchestration/orchestration_service.py @@ -16,6 +16,8 @@ from openstack.auth import service_filter class OrchestrationService(service_filter.ServiceFilter): """The orchestration service.""" + valid_versions = [service_filter.ValidVersion('v1')] + def __init__(self): """Create an orchestration service.""" super(OrchestrationService, self).__init__( diff --git a/openstack/orchestration/v1/_proxy.py b/openstack/orchestration/v1/_proxy.py new file mode 100644 index 000000000..025230cf3 --- /dev/null +++ b/openstack/orchestration/v1/_proxy.py @@ -0,0 +1,17 @@ +# 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. + + +class Proxy(object): + + def __init__(self, session): + self.session = session diff --git a/openstack/telemetry/telemetry_service.py b/openstack/telemetry/telemetry_service.py index dfd5df5e0..6a156abb0 100644 --- a/openstack/telemetry/telemetry_service.py +++ b/openstack/telemetry/telemetry_service.py @@ -16,6 +16,8 @@ from openstack.auth import service_filter class TelemetryService(service_filter.ServiceFilter): """The telemetry service.""" + valid_versions = [service_filter.ValidVersion('v2')] + def __init__(self): """Create a telemetry service.""" super(TelemetryService, self).__init__(service_type='metering') diff --git a/openstack/telemetry/v2/_proxy.py b/openstack/telemetry/v2/_proxy.py new file mode 100644 index 000000000..025230cf3 --- /dev/null +++ b/openstack/telemetry/v2/_proxy.py @@ -0,0 +1,17 @@ +# 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. + + +class Proxy(object): + + def __init__(self, session): + self.session = session diff --git a/openstack/tests/auth/test_service_filter.py b/openstack/tests/auth/test_service_filter.py index 5b690c07a..399711dc2 100644 --- a/openstack/tests/auth/test_service_filter.py +++ b/openstack/tests/auth/test_service_filter.py @@ -15,6 +15,7 @@ import testtools from openstack.auth import service_filter as filt from openstack import exceptions +from openstack.identity import identity_service class TestServiceFilter(testtools.TestCase): @@ -124,3 +125,15 @@ class TestServiceFilter(testtools.TestCase): self.assertEqual('internal', sot.visibility) sot.set_visibility("ADMINURL") self.assertEqual('admin', sot.visibility) + + def test_get_module(self): + sot = identity_service.IdentityService() + self.assertEqual('openstack.identity.v3', sot.get_module()) + self.assertEqual('identity', sot.get_service_module()) + + +class TestValidVersion(testtools.TestCase): + def test_constructor(self): + sot = filt.ValidVersion('v1.0', 'v1') + self.assertEqual('v1.0', sot.module) + self.assertEqual('v1', sot.path) diff --git a/openstack/tests/compute/test_compute_service.py b/openstack/tests/compute/test_compute_service.py index 115cf9d8d..449f813dd 100644 --- a/openstack/tests/compute/test_compute_service.py +++ b/openstack/tests/compute/test_compute_service.py @@ -23,3 +23,6 @@ class TestComputeService(testtools.TestCase): self.assertEqual('public', sot.visibility) self.assertIsNone(sot.region) self.assertIsNone(sot.service_name) + self.assertEqual(1, len(sot.valid_versions)) + self.assertEqual('v2', sot.valid_versions[0].module) + self.assertEqual('v2', sot.valid_versions[0].path) diff --git a/openstack/tests/database/test_database_service.py b/openstack/tests/database/test_database_service.py index 676c572ac..236670f6c 100644 --- a/openstack/tests/database/test_database_service.py +++ b/openstack/tests/database/test_database_service.py @@ -23,3 +23,6 @@ class TestDatabaseService(testtools.TestCase): self.assertEqual('public', sot.visibility) self.assertIsNone(sot.region) self.assertIsNone(sot.service_name) + self.assertEqual(1, len(sot.valid_versions)) + self.assertEqual('v1', sot.valid_versions[0].module) + self.assertEqual('v1', sot.valid_versions[0].path) diff --git a/openstack/tests/identity/test_identity_service.py b/openstack/tests/identity/test_identity_service.py index a095dcbe8..d13560b83 100644 --- a/openstack/tests/identity/test_identity_service.py +++ b/openstack/tests/identity/test_identity_service.py @@ -23,6 +23,11 @@ class TestIdentityService(testtools.TestCase): self.assertEqual('public', sot.visibility) self.assertIsNone(sot.region) self.assertIsNone(sot.service_name) + self.assertEqual(2, len(sot.valid_versions)) + self.assertEqual('v3', sot.valid_versions[0].module) + self.assertEqual('v3', sot.valid_versions[0].path) + self.assertEqual('v2', sot.valid_versions[1].module) + self.assertEqual('v2', sot.valid_versions[1].path) def test_admin_service(self): sot = identity_service.AdminService() diff --git a/openstack/tests/image/test_image_service.py b/openstack/tests/image/test_image_service.py index cfe763213..8346888a3 100644 --- a/openstack/tests/image/test_image_service.py +++ b/openstack/tests/image/test_image_service.py @@ -23,3 +23,6 @@ class TestImageService(testtools.TestCase): self.assertEqual('public', sot.visibility) self.assertIsNone(sot.region) self.assertIsNone(sot.service_name) + self.assertEqual(1, len(sot.valid_versions)) + self.assertEqual('v1', sot.valid_versions[0].module) + self.assertEqual('v1', sot.valid_versions[0].path) diff --git a/openstack/tests/network/test_network_service.py b/openstack/tests/network/test_network_service.py index 78a72a530..b888c1043 100644 --- a/openstack/tests/network/test_network_service.py +++ b/openstack/tests/network/test_network_service.py @@ -23,3 +23,6 @@ class TestNetworkService(testtools.TestCase): self.assertEqual('public', sot.visibility) self.assertIsNone(sot.region) self.assertIsNone(sot.service_name) + self.assertEqual(1, len(sot.valid_versions)) + self.assertEqual('v2', sot.valid_versions[0].module) + self.assertEqual('v2.0', sot.valid_versions[0].path) diff --git a/openstack/tests/object_store/test_object_store_service.py b/openstack/tests/object_store/test_object_store_service.py index b607bfaa4..96b2af666 100644 --- a/openstack/tests/object_store/test_object_store_service.py +++ b/openstack/tests/object_store/test_object_store_service.py @@ -23,3 +23,6 @@ class TestObjectStoreService(testtools.TestCase): self.assertEqual('public', sot.visibility) self.assertIsNone(sot.region) self.assertIsNone(sot.service_name) + self.assertEqual(1, len(sot.valid_versions)) + self.assertEqual('v1', sot.valid_versions[0].module) + self.assertEqual('v1', sot.valid_versions[0].path) diff --git a/openstack/tests/orchestration/test_orchestration_service.py b/openstack/tests/orchestration/test_orchestration_service.py index 91900d078..54ca77795 100644 --- a/openstack/tests/orchestration/test_orchestration_service.py +++ b/openstack/tests/orchestration/test_orchestration_service.py @@ -23,3 +23,6 @@ class TestOrchestrationService(testtools.TestCase): self.assertEqual('public', sot.visibility) self.assertIsNone(sot.region) self.assertIsNone(sot.service_name) + self.assertEqual(1, len(sot.valid_versions)) + self.assertEqual('v1', sot.valid_versions[0].module) + self.assertEqual('v1', sot.valid_versions[0].path) diff --git a/openstack/tests/telemetry/test_telemetry_service.py b/openstack/tests/telemetry/test_telemetry_service.py index e4defb7d5..56e0f0d6d 100644 --- a/openstack/tests/telemetry/test_telemetry_service.py +++ b/openstack/tests/telemetry/test_telemetry_service.py @@ -23,3 +23,6 @@ class TestTelemetryService(testtools.TestCase): self.assertEqual('public', sot.visibility) self.assertIsNone(sot.region) self.assertIsNone(sot.service_name) + self.assertEqual(1, len(sot.valid_versions)) + self.assertEqual('v2', sot.valid_versions[0].module) + self.assertEqual('v2', sot.valid_versions[0].path) diff --git a/openstack/tests/test_connection.py b/openstack/tests/test_connection.py index 586a0454a..77e2e7fea 100644 --- a/openstack/tests/test_connection.py +++ b/openstack/tests/test_connection.py @@ -10,12 +10,21 @@ # License for the specific language governing permissions and limitations # under the License. +from openstack.auth.identity import v2 from openstack import connection from openstack import exceptions from openstack.tests import base +from openstack import transport +from openstack import user_preference class TestConnection(base.TestCase): + def setUp(self): + super(TestConnection, self).setUp() + self.xport = transport.Transport() + self.auth = v2.Auth(auth_url='http://127.0.0.1/v2', token='b') + self.pref = user_preference.UserPreference() + def test_create_transport(self): conn = connection.Connection(authenticator='2', verify=True, user_agent='1') @@ -73,8 +82,28 @@ class TestConnection(base.TestCase): ) def test_create_session(self): - args = {'transport': '0', 'authenticator': '1', 'preference': '2'} + args = { + 'transport': self.xport, + 'authenticator': self.auth, + 'preference': self.pref, + } conn = connection.Connection(**args) - self.assertEqual('0', conn.session.transport) - self.assertEqual('1', conn.session.authenticator) - self.assertEqual('2', conn.session.preference) + self.assertEqual(self.xport, conn.session.transport) + self.assertEqual(self.auth, conn.session.authenticator) + self.assertEqual(self.pref, conn.session.preference) + self.assertEqual('openstack.compute.v2._proxy', + conn.compute.__class__.__module__) + self.assertEqual('openstack.database.v1._proxy', + conn.database.__class__.__module__) + self.assertEqual('openstack.identity.v3._proxy', + conn.identity.__class__.__module__) + self.assertEqual('openstack.image.v1._proxy', + conn.image.__class__.__module__) + self.assertEqual('openstack.network.v2._proxy', + conn.network.__class__.__module__) + self.assertEqual('openstack.object_store.v1._proxy', + conn.object_store.__class__.__module__) + self.assertEqual('openstack.orchestration.v1._proxy', + conn.orchestration.__class__.__module__) + self.assertEqual('openstack.telemetry.v2._proxy', + conn.telemetry.__class__.__module__)