diff --git a/tuskarclient/openstack/common/apiclient/base.py b/tuskarclient/openstack/common/apiclient/base.py index 04349b5..0027d8e 100644 --- a/tuskarclient/openstack/common/apiclient/base.py +++ b/tuskarclient/openstack/common/apiclient/base.py @@ -27,9 +27,9 @@ import abc import copy import six +from six.moves.urllib import parse from tuskarclient.openstack.common.apiclient import exceptions -from tuskarclient.openstack.common.py3kcompat import urlutils from tuskarclient.openstack.common import strutils @@ -328,7 +328,7 @@ class CrudManager(BaseManager): return self._list( '%(base_url)s%(query)s' % { 'base_url': self.build_url(base_url=base_url, **kwargs), - 'query': '?%s' % urlutils.urlencode(kwargs) if kwargs else '', + 'query': '?%s' % parse.urlencode(kwargs) if kwargs else '', }, self.collection_key) @@ -367,7 +367,7 @@ class CrudManager(BaseManager): rl = self._list( '%(base_url)s%(query)s' % { 'base_url': self.build_url(base_url=base_url, **kwargs), - 'query': '?%s' % urlutils.urlencode(kwargs) if kwargs else '', + 'query': '?%s' % parse.urlencode(kwargs) if kwargs else '', }, self.collection_key) num = len(rl) @@ -456,18 +456,23 @@ class Resource(object): def __getattr__(self, k): if k not in self.__dict__: - #NOTE(bcwaldon): disallow lazy-loading if already loaded once - if not self.is_loaded: - self._get() + # NOTE(bcwaldon): disallow lazy-loading if already loaded once + if not self.is_loaded(): + self.get() return self.__getattr__(k) raise AttributeError(k) else: return self.__dict__[k] - def _get(self): - # set _loaded first ... so if we have to bail, we know we tried. - self._loaded = True + def get(self): + """Support for lazy loading details. + + Some clients, such as novaclient have the option to lazy load the + details, details which can be loaded with this function. + """ + # set_loaded() first ... so if we have to bail, we know we tried. + self.set_loaded(True) if not hasattr(self.manager, 'get'): return @@ -485,9 +490,11 @@ class Resource(object): return self.id == other.id return self._info == other._info - @property def is_loaded(self): return self._loaded + def set_loaded(self, val): + self._loaded = val + def to_dict(self): return copy.deepcopy(self._info) diff --git a/tuskarclient/openstack/common/apiclient/exceptions.py b/tuskarclient/openstack/common/apiclient/exceptions.py index 4776d58..ada1344 100644 --- a/tuskarclient/openstack/common/apiclient/exceptions.py +++ b/tuskarclient/openstack/common/apiclient/exceptions.py @@ -127,6 +127,11 @@ class HttpError(ClientException): super(HttpError, self).__init__(formatted_string) +class HTTPRedirection(HttpError): + """HTTP Redirection.""" + message = "HTTP Redirection" + + class HTTPClientError(HttpError): """Client-side HTTP error. @@ -144,6 +149,16 @@ class HttpServerError(HttpError): message = "HTTP Server Error" +class MultipleChoices(HTTPRedirection): + """HTTP 300 - Multiple Choices. + + Indicates multiple options for the resource that the client may follow. + """ + + http_status = 300 + message = "Multiple Choices" + + class BadRequest(HTTPClientError): """HTTP 400 - Bad Request. @@ -425,10 +440,10 @@ def from_response(response, method, url): except ValueError: pass else: - if hasattr(body, "keys"): - error = body[body.keys()[0]] - kwargs["message"] = error.get("message", None) - kwargs["details"] = error.get("details", None) + if isinstance(body, dict): + error = list(body.values())[0] + kwargs["message"] = error.get("message") + kwargs["details"] = error.get("details") elif content_type.startswith("text/"): kwargs["details"] = response.text diff --git a/tuskarclient/openstack/common/apiclient/fake_client.py b/tuskarclient/openstack/common/apiclient/fake_client.py index 0f6ba0b..7033f23 100644 --- a/tuskarclient/openstack/common/apiclient/fake_client.py +++ b/tuskarclient/openstack/common/apiclient/fake_client.py @@ -28,9 +28,9 @@ import json import requests import six +from six.moves.urllib import parse from tuskarclient.openstack.common.apiclient import client -from tuskarclient.openstack.common.py3kcompat import urlutils def assert_has_keys(dct, required=[], optional=[]): @@ -79,7 +79,7 @@ class FakeHTTPClient(client.HTTPClient): def __init__(self, *args, **kwargs): self.callstack = [] self.fixtures = kwargs.pop("fixtures", None) or {} - if not args and not "auth_plugin" in kwargs: + if not args and "auth_plugin" not in kwargs: args = (None, ) super(FakeHTTPClient, self).__init__(*args, **kwargs) @@ -147,7 +147,7 @@ class FakeHTTPClient(client.HTTPClient): "text": fixture[1]}) # Call the method - args = urlutils.parse_qsl(urlutils.urlparse(url)[4]) + args = parse.parse_qsl(parse.urlparse(url)[4]) kwargs.update(args) munged_url = url.rsplit('?', 1)[0] munged_url = munged_url.strip('/').replace('/', '_').replace('.', '_') diff --git a/tuskarclient/openstack/common/py3kcompat/__init__.py b/tuskarclient/openstack/common/py3kcompat/__init__.py deleted file mode 100644 index 97ae4e3..0000000 --- a/tuskarclient/openstack/common/py3kcompat/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright 2013 Canonical Ltd. -# 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. -# diff --git a/tuskarclient/openstack/common/py3kcompat/urlutils.py b/tuskarclient/openstack/common/py3kcompat/urlutils.py deleted file mode 100644 index 6200271..0000000 --- a/tuskarclient/openstack/common/py3kcompat/urlutils.py +++ /dev/null @@ -1,65 +0,0 @@ -# -# Copyright 2013 Canonical Ltd. -# 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. -# - -""" -Python2/Python3 compatibility layer for OpenStack -""" - -import six - -if six.PY3: - # python3 - import urllib.error - import urllib.parse - import urllib.request - - urlencode = urllib.parse.urlencode - urljoin = urllib.parse.urljoin - quote = urllib.parse.quote - parse_qsl = urllib.parse.parse_qsl - unquote = urllib.parse.unquote - unquote_plus = urllib.parse.unquote_plus - urlparse = urllib.parse.urlparse - urlsplit = urllib.parse.urlsplit - urlunsplit = urllib.parse.urlunsplit - SplitResult = urllib.parse.SplitResult - - urlopen = urllib.request.urlopen - URLError = urllib.error.URLError - pathname2url = urllib.request.pathname2url -else: - # python2 - import urllib - import urllib2 - import urlparse - - urlencode = urllib.urlencode - quote = urllib.quote - unquote = urllib.unquote - unquote_plus = urllib.unquote_plus - - parse = urlparse - parse_qsl = parse.parse_qsl - urljoin = parse.urljoin - urlparse = parse.urlparse - urlsplit = parse.urlsplit - urlunsplit = parse.urlunsplit - SplitResult = parse.SplitResult - - urlopen = urllib2.urlopen - URLError = urllib2.URLError - pathname2url = urllib.pathname2url