diff --git a/troveclient/auth.py b/troveclient/auth.py
deleted file mode 100644
index d2f69994..00000000
--- a/troveclient/auth.py
+++ /dev/null
@@ -1,255 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# 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 __future__ import print_function
-from troveclient import exceptions
-
-import six
-
-
-def get_authenticator_cls(cls_or_name):
- """Factory method to retrieve Authenticator class."""
- if isinstance(cls_or_name, type):
- return cls_or_name
- elif isinstance(cls_or_name, six.string_types):
- if cls_or_name == "keystone":
- return KeyStoneV2Authenticator
- elif cls_or_name == "rax":
- return RaxAuthenticator
- elif cls_or_name == "auth1.1":
- return Auth1_1
- elif cls_or_name == "fake":
- return FakeAuth
-
- raise ValueError("Could not determine authenticator class from the given "
- "value %r." % cls_or_name)
-
-
-class Authenticator(object):
- """
- Helper class to perform Keystone or other miscellaneous authentication.
-
- The "authenticate" method returns a ServiceCatalog, which can be used
- to obtain a token.
-
- """
-
- URL_REQUIRED = True
-
- def __init__(self, client, type, url, username, password, tenant,
- region=None, service_type=None, service_name=None,
- service_url=None):
- self.client = client
- self.type = type
- self.url = url
- self.username = username
- self.password = password
- self.tenant = tenant
- self.region = region
- self.service_type = service_type
- self.service_name = service_name
- self.service_url = service_url
-
- def _authenticate(self, url, body, root_key='access'):
- """Authenticate and extract the service catalog."""
- # Make sure we follow redirects when trying to reach Keystone
- tmp_follow_all_redirects = self.client.follow_all_redirects
- self.client.follow_all_redirects = True
-
- try:
- resp, body = self.client._time_request(url, "POST", body=body)
- finally:
- self.client.follow_all_redirects = tmp_follow_all_redirects
-
- if resp.status == 200: # content must always present
- try:
- return ServiceCatalog(body, region=self.region,
- service_type=self.service_type,
- service_name=self.service_name,
- service_url=self.service_url,
- root_key=root_key)
- except exceptions.AmbiguousEndpoints:
- print("Found more than one valid endpoint. Use a more "
- "restrictive filter")
- raise
- except KeyError:
- raise exceptions.AuthorizationFailure()
- except exceptions.EndpointNotFound:
- print("Could not find any suitable endpoint. Correct region?")
- raise
-
- elif resp.status == 305:
- return resp['location']
- else:
- raise exceptions.from_response(resp, body)
-
- def authenticate(self):
- raise NotImplementedError("Missing authenticate method.")
-
-
-class KeyStoneV2Authenticator(Authenticator):
- def authenticate(self):
- if self.url is None:
- raise exceptions.AuthUrlNotGiven()
- return self._v2_auth(self.url)
-
- def _v2_auth(self, url):
- """Authenticate against a v2.0 auth service."""
- body = {"auth": {
- "passwordCredentials": {
- "username": self.username,
- "password": self.password}
- }
- }
-
- if self.tenant:
- body['auth']['tenantName'] = self.tenant
-
- return self._authenticate(url, body)
-
-
-class Auth1_1(Authenticator):
- def authenticate(self):
- """Authenticate against a v2.0 auth service."""
- if self.url is None:
- raise exceptions.AuthUrlNotGiven()
- auth_url = self.url
- body = {
- "credentials": {
- "username": self.username,
- "key": self.password
- }}
- return self._authenticate(auth_url, body, root_key='auth')
-
-
-class RaxAuthenticator(Authenticator):
- def authenticate(self):
- if self.url is None:
- raise exceptions.AuthUrlNotGiven()
- return self._rax_auth(self.url)
-
- def _rax_auth(self, url):
- """Authenticate against the Rackspace auth service."""
- body = {'auth': {
- 'RAX-KSKEY:apiKeyCredentials': {
- 'username': self.username,
- 'apiKey': self.password,
- 'tenantName': self.tenant}
- }
- }
-
- return self._authenticate(self.url, body)
-
-
-class FakeAuth(Authenticator):
- """Useful for faking auth."""
-
- def authenticate(self):
- class FakeCatalog(object):
- def __init__(self, auth):
- self.auth = auth
-
- def get_public_url(self):
- return "%s/%s" % ('http://localhost:8779/v1.0',
- self.auth.tenant)
-
- def get_token(self):
- return self.auth.tenant
-
- return FakeCatalog(self)
-
-
-class ServiceCatalog(object):
- """Represents a Keystone Service Catalog which describes a service.
-
- This class has methods to obtain a valid token as well as a public service
- url and a management url.
-
- """
-
- def __init__(self, resource_dict, region=None, service_type=None,
- service_name=None, service_url=None, root_key='access'):
- self.catalog = resource_dict
- self.region = region
- self.service_type = service_type
- self.service_name = service_name
- self.service_url = service_url
- self.management_url = None
- self.public_url = None
- self.root_key = root_key
- self._load()
-
- def _load(self):
- if not self.service_url:
- self.public_url = self._url_for(attr='region',
- filter_value=self.region,
- endpoint_type="publicURL")
- self.management_url = self._url_for(attr='region',
- filter_value=self.region,
- endpoint_type="adminURL")
- else:
- self.public_url = self.service_url
- self.management_url = self.service_url
-
- def get_token(self):
- return self.catalog[self.root_key]['token']['id']
-
- def get_management_url(self):
- return self.management_url
-
- def get_public_url(self):
- return self.public_url
-
- def _url_for(self, attr=None, filter_value=None,
- endpoint_type='publicURL'):
- """
- Fetch the public URL from the Trove service for a particular
- endpoint attribute. If none given, return the first.
- """
- matching_endpoints = []
- if 'endpoints' in self.catalog:
- # We have a bastardized service catalog. Treat it special. :/
- for endpoint in self.catalog['endpoints']:
- if not filter_value or endpoint[attr] == filter_value:
- matching_endpoints.append(endpoint)
- if not matching_endpoints:
- raise exceptions.EndpointNotFound()
-
- # We don't always get a service catalog back ...
- if 'serviceCatalog' not in self.catalog[self.root_key]:
- raise exceptions.EndpointNotFound()
-
- # Full catalog ...
- catalog = self.catalog[self.root_key]['serviceCatalog']
-
- for service in catalog:
- if service.get("type") != self.service_type:
- continue
-
- if (self.service_name and self.service_type == 'database' and
- service.get('name') != self.service_name):
- continue
-
- endpoints = service['endpoints']
- for endpoint in endpoints:
- if not filter_value or endpoint.get(attr) == filter_value:
- endpoint["serviceName"] = service.get("name")
- matching_endpoints.append(endpoint)
-
- if not matching_endpoints:
- raise exceptions.EndpointNotFound()
- elif len(matching_endpoints) > 1:
- raise exceptions.AmbiguousEndpoints(endpoints=matching_endpoints)
- else:
- return matching_endpoints[0].get(endpoint_type, None)
diff --git a/troveclient/compat/auth.py b/troveclient/compat/auth.py
index f5bfdbdb..7884d5dc 100644
--- a/troveclient/compat/auth.py
+++ b/troveclient/compat/auth.py
@@ -13,6 +13,7 @@
# under the License.
from __future__ import print_function
+from six import string_types
from troveclient.compat import exceptions
@@ -20,7 +21,7 @@ def get_authenticator_cls(cls_or_name):
"""Factory method to retrieve Authenticator class."""
if isinstance(cls_or_name, type):
return cls_or_name
- elif isinstance(cls_or_name, basestring):
+ elif isinstance(cls_or_name, string_types):
if cls_or_name == "keystone":
return KeyStoneV2Authenticator
elif cls_or_name == "rax":
diff --git a/troveclient/tests/test_auth.py b/troveclient/compat/tests/test_auth.py
similarity index 99%
rename from troveclient/tests/test_auth.py
rename to troveclient/compat/tests/test_auth.py
index f57f8ea9..cb3b6bc2 100644
--- a/troveclient/tests/test_auth.py
+++ b/troveclient/compat/tests/test_auth.py
@@ -18,10 +18,10 @@
# under the License.
from testtools import TestCase
-from troveclient import auth
+from troveclient.compat import auth
from mock import Mock
-from troveclient import exceptions
+from troveclient.compat import exceptions
"""
Unit tests for the classes and functions in auth.py.
diff --git a/troveclient/xml.py b/troveclient/xml.py
deleted file mode 100644
index 85e30a1a..00000000
--- a/troveclient/xml.py
+++ /dev/null
@@ -1,292 +0,0 @@
-from lxml import etree
-from numbers import Number
-
-from troveclient import exceptions
-from troveclient.client import TroveHTTPClient
-
-XML_NS = {None: "http://docs.openstack.org/database/api/v1.0"}
-
-# If XML element is listed here then this searches through the ancestors.
-LISTIFY = {
- "accounts": [[]],
- "databases": [[]],
- "flavors": [[]],
- "instances": [[]],
- "links": [[]],
- "hosts": [[]],
- "devices": [[]],
- "users": [[]],
- "versions": [[]],
- "attachments": [[]],
- "limits": [[]],
- "security_groups": [[]],
- "backups": [[]]
-}
-
-
-class IntDict(object):
- pass
-
-
-TYPE_MAP = {
- "instance": {
- "volume": {
- "used": float,
- "size": int,
- },
- "deleted": bool,
- "server": {
- "local_id": int,
- "deleted": bool,
- },
- },
- "instances": {
- "deleted": bool,
- },
- "deleted": bool,
- "flavor": {
- "ram": int,
- },
- "diagnostics": {
- "vmHwm": int,
- "vmPeak": int,
- "vmSize": int,
- "threads": int,
- "vmRss": int,
- "fdSize": int,
- },
- "security_group_rule": {
- "from_port": int,
- "to_port": int,
- },
- "quotas": IntDict,
-}
-TYPE_MAP["flavors"] = TYPE_MAP["flavor"]
-
-REQUEST_AS_LIST = set(['databases', 'users'])
-
-
-def element_ancestors_match_list(element, list):
- """
- For element root at matches against
- list ["blah", "foo"].
- """
- itr_elem = element.getparent()
- for name in list:
- if itr_elem is None:
- break
- if name != normalize_tag(itr_elem):
- return False
- itr_elem = itr_elem.getparent()
- return True
-
-
-def element_must_be_list(parent_element, name):
- """Determines if an element to be created should be a dict or list."""
- if name in LISTIFY:
- list_of_lists = LISTIFY[name]
- for tag_list in list_of_lists:
- if element_ancestors_match_list(parent_element, tag_list):
- return True
- return False
-
-
-def element_to_json(name, element):
- if element_must_be_list(element, name):
- return element_to_list(element)
- else:
- return element_to_dict(element)
-
-
-def root_element_to_json(name, element):
- """Returns a tuple of the root JSON value, plus the links if found."""
- if name == "rootEnabled": # Why oh why were we inconsistent here? :'(
- if element.text.strip() == "False":
- return False, None
- elif element.text.strip() == "True":
- return True, None
- if element_must_be_list(element, name):
- return element_to_list(element, True)
- else:
- return element_to_dict(element), None
-
-
-def element_to_list(element, check_for_links=False):
- """
- For element "foo" in
- Returns [{}, {}]
- """
- links = None
- result = []
- for child_element in element:
- # The "links" element gets jammed into the root element.
- if check_for_links and normalize_tag(child_element) == "links":
- links = element_to_list(child_element)
- else:
- result.append(element_to_dict(child_element))
- if check_for_links:
- return result, links
- else:
- return result
-
-
-def element_to_dict(element):
- result = {}
- for name, value in element.items():
- result[name] = value
- for child_element in element:
- name = normalize_tag(child_element)
- result[name] = element_to_json(name, child_element)
- if len(result) == 0 and element.text:
- string_value = element.text.strip()
- if len(string_value):
- if string_value == 'None':
- return None
- return string_value
- return result
-
-
-def standardize_json_lists(json_dict):
- """
- In XML, we might see something like {'instances':{'instances':[...]}},
- which we must change to just {'instances':[...]} to be compatable with
- the true JSON format.
-
- If any items are dictionaries with only one item which is a list,
- simply remove the dictionary and insert its list directly.
- """
- found_items = []
- for key, value in json_dict.items():
- value = json_dict[key]
- if isinstance(value, dict):
- if len(value) == 1 and isinstance(value.values()[0], list):
- found_items.append(key)
- else:
- standardize_json_lists(value)
- for key in found_items:
- json_dict[key] = json_dict[key].values()[0]
-
-
-def normalize_tag(elem):
- """Given an element, returns the tag minus the XMLNS junk.
-
- IOW, .tag may sometimes return the XML namespace at the start of the
- string. This gets rids of that.
- """
- try:
- prefix = "{" + elem.nsmap[None] + "}"
- if elem.tag.startswith(prefix):
- return elem.tag[len(prefix):]
- except KeyError:
- pass
- return elem.tag
-
-
-def create_root_xml_element(name, value):
- """Create the first element using a name and a dictionary."""
- element = etree.Element(name, nsmap=XML_NS)
- if name in REQUEST_AS_LIST:
- add_subelements_from_list(element, name, value)
- else:
- populate_element_from_dict(element, value)
- return element
-
-
-def create_subelement(parent_element, name, value):
- """Attaches a new element onto the parent element."""
- if isinstance(value, dict):
- create_subelement_from_dict(parent_element, name, value)
- elif isinstance(value, list):
- create_subelement_from_list(parent_element, name, value)
- else:
- raise TypeError("Can't handle type %s." % type(value))
-
-
-def create_subelement_from_dict(parent_element, name, dict):
- element = etree.SubElement(parent_element, name)
- populate_element_from_dict(element, dict)
-
-
-def create_subelement_from_list(parent_element, name, list):
- element = etree.SubElement(parent_element, name)
- add_subelements_from_list(element, name, list)
-
-
-def add_subelements_from_list(element, name, list):
- if name.endswith("s"):
- item_name = name[:len(name) - 1]
- else:
- item_name = name
- for item in list:
- create_subelement(element, item_name, item)
-
-
-def populate_element_from_dict(element, dict):
- for key, value in dict.items():
- if isinstance(value, basestring):
- element.set(key, value)
- elif isinstance(value, Number):
- element.set(key, str(value))
- elif isinstance(value, None.__class__):
- element.set(key, '')
- else:
- create_subelement(element, key, value)
-
-
-def modify_response_types(value, type_translator):
- """
- This will convert some string in response dictionary to ints or bool
- so that our respose is compatiable with code expecting JSON style responses
- """
- if isinstance(value, str):
- if value == 'True':
- return True
- elif value == 'False':
- return False
- else:
- return type_translator(value)
- elif isinstance(value, dict):
- for k, v in value.iteritems():
- if type_translator is not IntDict:
- if v.__class__ is dict and v.__len__() == 0:
- value[k] = None
- elif k in type_translator:
- value[k] = modify_response_types(value[k],
- type_translator[k])
- else:
- value[k] = int(value[k])
- return value
- elif isinstance(value, list):
- return [modify_response_types(element, type_translator)
- for element in value]
-
-
-class TroveXmlClient(TroveHTTPClient):
-
- @classmethod
- def morph_request(self, kwargs):
- kwargs['headers']['Accept'] = 'application/xml'
- kwargs['headers']['Content-Type'] = 'application/xml'
- if 'body' in kwargs:
- body = kwargs['body']
- root_name = body.keys()[0]
- xml = create_root_xml_element(root_name, body[root_name])
- xml_string = etree.tostring(xml, pretty_print=True)
- kwargs['body'] = xml_string
-
- @classmethod
- def morph_response_body(self, body_string):
- # The root XML element always becomes a dictionary with a single
- # field, which has the same key as the elements name.
- result = {}
- try:
- root_element = etree.XML(body_string)
- except etree.XMLSyntaxError:
- raise exceptions.ResponseFormatError()
- root_name = normalize_tag(root_element)
- root_value, links = root_element_to_json(root_name, root_element)
- result = {root_name: root_value}
- if links:
- result['links'] = links
- modify_response_types(result, TYPE_MAP)
- return result