From 4a046a2930ab2facca6eb45ce6ba927f6ef75784 Mon Sep 17 00:00:00 2001 From: Flavio Percoco Date: Wed, 25 Sep 2013 17:43:05 +0200 Subject: [PATCH] Add a base transport class This patch adds a base transport class which should be used by all transports implementations. The patch also adds a Response class, which implements a common response object to use between the client API and the transport API. Change-Id: I9dff8ed9592076e7bc7da55d7717de2a5829f5f3 --- marconiclient/errors.py | 19 ++++++++ marconiclient/transport/__init__.py | 67 +++++++++++++++++++++++++++++ marconiclient/transport/base.py | 33 ++++++++++++++ marconiclient/transport/response.py | 37 ++++++++++++++++ requirements.txt | 1 + 5 files changed, 157 insertions(+) create mode 100644 marconiclient/errors.py create mode 100644 marconiclient/transport/base.py create mode 100644 marconiclient/transport/response.py diff --git a/marconiclient/errors.py b/marconiclient/errors.py new file mode 100644 index 00000000..6b23fc3f --- /dev/null +++ b/marconiclient/errors.py @@ -0,0 +1,19 @@ +# Copyright 2013 Red Hat, Inc. +# +# 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. + +__all__ = ['MarconiError'] + + +class MarconiError(Exception): + """Base class for errors.""" diff --git a/marconiclient/transport/__init__.py b/marconiclient/transport/__init__.py index e69de29b..26e39494 100644 --- a/marconiclient/transport/__init__.py +++ b/marconiclient/transport/__init__.py @@ -0,0 +1,67 @@ +# Copyright (c) 2013 Red Hat, Inc. +# +# 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 six +from six.moves.urllib import parse +from stevedore import driver + +from marconiclient import errors + + +class DriverLoadFailure(errors.MarconiError): + """Raised if a transport driver can't be loaded.""" + + def __init__(self, driver, ex): + msg = 'Failed to load transport driver "%s": %s' % (driver, ex) + super(DriverLoadFailure, self).__init__(msg) + self.driver = driver + self.ex = ex + + +def get_transport(conf, url_or_request, module='queues'): + """Gets a transport for a given url. + + An example transport URL might be:: + + zmq://example.org:8888/v1/ + + :param conf: the user configuration + :type conf: cfg.ConfigOpts + :param url_or_request: a transport URL + :type url_or_request: `six.string_types` or + `marconiclient.transport.request.Request` + :param module: Module the target transport belongs to. + :type module: str + + :returns: A `Transport` instance. + :rtype: `marconiclient.transport.Transport` + """ + + url = url_or_request + if not isinstance(url_or_request, six.string_types): + url = url_or_request.endpoint + + parsed = parse.urlparse(url) + + try: + namespace = 'marconiclient.{0}.transport'.format(module) + mgr = driver.DriverManager(namespace, + parsed.scheme, + invoke_on_load=True, + invoke_args=[conf]) + except RuntimeError as ex: + raise DriverLoadFailure(parsed.scheme, ex) + + return mgr.driver diff --git a/marconiclient/transport/base.py b/marconiclient/transport/base.py new file mode 100644 index 00000000..e507958d --- /dev/null +++ b/marconiclient/transport/base.py @@ -0,0 +1,33 @@ +# Copyright (c) 2013 Red Hat, Inc. +# +# 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 abc + +import six + + +@six.add_metaclass(abc.ABCMeta) +class Transport(object): + + def __init__(self, conf): + self.conf = conf + + @abc.abstractmethod + def send(self, request): + """Returns the response. + + :returns: The final response + :rtype: `marconiclient.transport.response.Response` + """ diff --git a/marconiclient/transport/response.py b/marconiclient/transport/response.py new file mode 100644 index 00000000..e74b6bf4 --- /dev/null +++ b/marconiclient/transport/response.py @@ -0,0 +1,37 @@ +# Copyright (c) 2013 Red Hat, Inc. +# +# 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 Response(object): + """Common response class for Marconiclient. + + All `marconiclient.transport.base.Transport` implementations + will return this to the higher level API which will then build + an object out of it. + + :param request: The request sent to the server. + :type: `marconiclient.transport.request.Request` + :param content: Response's content + :type: `six.string_types` + :param headers: Optional headers returned in the response. + :type: dict + """ + + __slots__ = ('request', 'content', 'headers') + + def __init__(self, request, content, headers=None): + self.request = request + self.content = content + self.headers = headers or {} diff --git a/requirements.txt b/requirements.txt index da58af48..1754a0b2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ pbr>=0.5.21,<1.0 six>=1.3.0 +stevedore>=0.10 python-keystoneclient