High level interface

High level interface using the connection classes to access the
various services.  Only identity projects and list_flavors in
compute are implemented:

    projects = conn.identity.list_projects()

The service filter has a valid_versions added to it to map the
module name to the version component of the endpoint URL.  For
example, the v2.0 endpoint path is supported by the v2 SDK
network module:

https://region-a.geo-1.network.hpcloudsvc.com/v2.0/

is supported by the SDK module openstack.network.v2.

Implements: blueprint highlevel-interface

Change-Id: Ie52986a9da84c8f3395757c08de7a01be30e0f2a
This commit is contained in:
Terry Howe 2014-10-15 13:36:07 -06:00
parent 76313512a4
commit 8baa156ea6
29 changed files with 327 additions and 4 deletions

View File

@ -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]

View File

@ -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')

View File

@ -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)

View File

@ -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))

View File

@ -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')

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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)

View File

@ -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')

View File

@ -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

View File

@ -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')

View File

@ -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

View File

@ -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')

View File

@ -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

View File

@ -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__(

View File

@ -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

View File

@ -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')

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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__)