summaryrefslogtreecommitdiff
path: root/troveclient/client.py
diff options
context:
space:
mode:
authorMichael Basnight <mbasnight@gmail.com>2013-06-17 23:34:27 -0700
committerMichael Basnight <mbasnight@gmail.com>2013-06-21 20:15:23 +0000
commit9916c8f2733b683d859770d05dacd2c9c82912d7 (patch)
tree084a0d53580cbbd34ed8f28de9302d6c78f7050d /troveclient/client.py
parentbc90b3e088d3d4b83b5b3de0f9f83d9b6956947d (diff)
downloadpython-troveclient-9916c8f2733b683d859770d05dacd2c9c82912d7.tar.gz
Rename from reddwarf to trove.0.1.3
Implements Blueprint reddwarf-trove-rename Change-Id: Ib2d694c7466887ca297bea4250eca17cdc06b7bf
Diffstat (limited to 'troveclient/client.py')
-rw-r--r--troveclient/client.py370
1 files changed, 370 insertions, 0 deletions
diff --git a/troveclient/client.py b/troveclient/client.py
new file mode 100644
index 0000000..1409448
--- /dev/null
+++ b/troveclient/client.py
@@ -0,0 +1,370 @@
+# Copyright (c) 2011 OpenStack, LLC.
+# 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.
+
+import httplib2
+import logging
+import os
+import time
+import urlparse
+import sys
+
+try:
+ import json
+except ImportError:
+ import simplejson as json
+
+# Python 2.5 compat fix
+if not hasattr(urlparse, 'parse_qsl'):
+ import cgi
+ urlparse.parse_qsl = cgi.parse_qsl
+
+from troveclient import auth
+from troveclient import exceptions
+
+
+_logger = logging.getLogger(__name__)
+RDC_PP = os.environ.get("RDC_PP", "False") == "True"
+
+
+expected_errors = (400, 401, 403, 404, 408, 409, 413, 422, 500, 501)
+
+
+def log_to_streamhandler(stream=None):
+ stream = stream or sys.stderr
+ ch = logging.StreamHandler(stream)
+ _logger.setLevel(logging.DEBUG)
+ _logger.addHandler(ch)
+
+
+if 'REDDWARFCLIENT_DEBUG' in os.environ and os.environ['REDDWARFCLIENT_DEBUG']:
+ log_to_streamhandler()
+
+
+class TroveHTTPClient(httplib2.Http):
+
+ USER_AGENT = 'python-troveclient'
+
+ def __init__(self, user, password, tenant, auth_url, service_name,
+ service_url=None,
+ auth_strategy=None, insecure=False,
+ timeout=None, proxy_tenant_id=None,
+ proxy_token=None, region_name=None,
+ endpoint_type='publicURL', service_type=None,
+ timings=False):
+
+ super(TroveHTTPClient, self).__init__(timeout=timeout)
+
+ self.username = user
+ self.password = password
+ self.tenant = tenant
+ if auth_url:
+ self.auth_url = auth_url.rstrip('/')
+ else:
+ self.auth_url = None
+ self.region_name = region_name
+ self.endpoint_type = endpoint_type
+ self.service_url = service_url
+ self.service_type = service_type
+ self.service_name = service_name
+ self.timings = timings
+
+ self.times = [] # [("item", starttime, endtime), ...]
+
+ self.auth_token = None
+ self.proxy_token = proxy_token
+ self.proxy_tenant_id = proxy_tenant_id
+
+ # httplib2 overrides
+ self.force_exception_to_status_code = True
+ self.disable_ssl_certificate_validation = insecure
+
+ auth_cls = auth.get_authenticator_cls(auth_strategy)
+
+ self.authenticator = auth_cls(self, auth_strategy,
+ self.auth_url, self.username,
+ self.password, self.tenant,
+ region=region_name,
+ service_type=service_type,
+ service_name=service_name,
+ service_url=service_url)
+
+ def get_timings(self):
+ return self.times
+
+ def http_log(self, args, kwargs, resp, body):
+ if not RDC_PP:
+ self.simple_log(args, kwargs, resp, body)
+ else:
+ self.pretty_log(args, kwargs, resp, body)
+
+ def simple_log(self, args, kwargs, resp, body):
+ if not _logger.isEnabledFor(logging.DEBUG):
+ return
+
+ string_parts = ['curl -i']
+ for element in args:
+ if element in ('GET', 'POST'):
+ string_parts.append(' -X %s' % element)
+ else:
+ string_parts.append(' %s' % element)
+
+ for element in kwargs['headers']:
+ header = ' -H "%s: %s"' % (element, kwargs['headers'][element])
+ string_parts.append(header)
+
+ _logger.debug("REQ: %s\n" % "".join(string_parts))
+ if 'body' in kwargs:
+ _logger.debug("REQ BODY: %s\n" % (kwargs['body']))
+ _logger.debug("RESP:%s %s\n", resp, body)
+
+ def pretty_log(self, args, kwargs, resp, body):
+ from troveclient import common
+ if not _logger.isEnabledFor(logging.DEBUG):
+ return
+
+ string_parts = ['curl -i']
+ for element in args:
+ if element in ('GET', 'POST'):
+ string_parts.append(' -X %s' % element)
+ else:
+ string_parts.append(' %s' % element)
+
+ for element in kwargs['headers']:
+ header = ' -H "%s: %s"' % (element, kwargs['headers'][element])
+ string_parts.append(header)
+
+ curl_cmd = "".join(string_parts)
+ _logger.debug("REQUEST:")
+ if 'body' in kwargs:
+ _logger.debug("%s -d '%s'" % (curl_cmd, kwargs['body']))
+ try:
+ req_body = json.dumps(json.loads(kwargs['body']),
+ sort_keys=True, indent=4)
+ except:
+ req_body = kwargs['body']
+ _logger.debug("BODY: %s\n" % (req_body))
+ else:
+ _logger.debug(curl_cmd)
+
+ try:
+ resp_body = json.dumps(json.loads(body), sort_keys=True, indent=4)
+ except:
+ resp_body = body
+ _logger.debug("RESPONSE HEADERS: %s" % resp)
+ _logger.debug("RESPONSE BODY : %s" % resp_body)
+
+ def request(self, *args, **kwargs):
+ kwargs.setdefault('headers', kwargs.get('headers', {}))
+ kwargs['headers']['User-Agent'] = self.USER_AGENT
+ self.morph_request(kwargs)
+
+ resp, body = super(TroveHTTPClient, self).request(*args, **kwargs)
+
+ # Save this in case anyone wants it.
+ self.last_response = (resp, body)
+ self.http_log(args, kwargs, resp, body)
+
+ if body:
+ try:
+ body = self.morph_response_body(body)
+ except exceptions.ResponseFormatError:
+ # Acceptable only if the response status is an error code.
+ # Otherwise its the API or client misbehaving.
+ self.raise_error_from_status(resp, None)
+ raise # Not accepted!
+ else:
+ body = None
+
+ if resp.status in expected_errors:
+ raise exceptions.from_response(resp, body)
+
+ return resp, body
+
+ def raise_error_from_status(self, resp, body):
+ if resp.status in expected_errors:
+ raise exceptions.from_response(resp, body)
+
+ def morph_request(self, kwargs):
+ kwargs['headers']['Accept'] = 'application/json'
+ kwargs['headers']['Content-Type'] = 'application/json'
+ if 'body' in kwargs:
+ kwargs['body'] = json.dumps(kwargs['body'])
+
+ def morph_response_body(self, body_string):
+ try:
+ return json.loads(body_string)
+ except ValueError:
+ raise exceptions.ResponseFormatError()
+
+ def _time_request(self, url, method, **kwargs):
+ start_time = time.time()
+ resp, body = self.request(url, method, **kwargs)
+ self.times.append(("%s %s" % (method, url),
+ start_time, time.time()))
+ return resp, body
+
+ def _cs_request(self, url, method, **kwargs):
+ def request():
+ kwargs.setdefault('headers', {})['X-Auth-Token'] = self.auth_token
+ if self.tenant:
+ kwargs['headers']['X-Auth-Project-Id'] = self.tenant
+
+ resp, body = self._time_request(self.service_url + url, method,
+ **kwargs)
+ return resp, body
+
+ if not self.auth_token or not self.service_url:
+ self.authenticate()
+
+ # Perform the request once. If we get a 401 back then it
+ # might be because the auth token expired, so try to
+ # re-authenticate and try again. If it still fails, bail.
+ try:
+ return request()
+ except exceptions.Unauthorized, ex:
+ self.authenticate()
+ return request()
+
+ def get(self, url, **kwargs):
+ return self._cs_request(url, 'GET', **kwargs)
+
+ def post(self, url, **kwargs):
+ return self._cs_request(url, 'POST', **kwargs)
+
+ def put(self, url, **kwargs):
+ return self._cs_request(url, 'PUT', **kwargs)
+
+ def delete(self, url, **kwargs):
+ return self._cs_request(url, 'DELETE', **kwargs)
+
+ def authenticate(self):
+ """Auths the client and gets a token. May optionally set a service url.
+
+ The client will get auth errors until the authentication step
+ occurs. Additionally, if a service_url was not explicitly given in
+ the clients __init__ method, one will be obtained from the auth
+ service.
+
+ """
+ catalog = self.authenticator.authenticate()
+ if self.service_url:
+ possible_service_url = None
+ else:
+ if self.endpoint_type == "publicURL":
+ possible_service_url = catalog.get_public_url()
+ elif self.endpoint_type == "adminURL":
+ possible_service_url = catalog.get_management_url()
+ self.authenticate_with_token(catalog.get_token(), possible_service_url)
+
+ def authenticate_with_token(self, token, service_url=None):
+ self.auth_token = token
+ if not self.service_url:
+ if not service_url:
+ raise exceptions.ServiceUrlNotGiven()
+ else:
+ self.service_url = service_url
+
+
+class Dbaas(object):
+ """
+ Top-level object to access the Rackspace Database as a Service API.
+
+ Create an instance with your creds::
+
+ >>> red = Dbaas(USERNAME, API_KEY, TENANT, AUTH_URL, SERVICE_NAME,
+ SERVICE_URL)
+
+ Then call methods on its managers::
+
+ >>> red.instances.list()
+ ...
+ >>> red.flavors.list()
+ ...
+
+ &c.
+ """
+
+ def __init__(self, username, api_key, tenant=None, auth_url=None,
+ service_type='database', service_name='trove',
+ service_url=None, insecure=False, auth_strategy='keystone',
+ region_name=None, client_cls=TroveHTTPClient):
+ from troveclient.versions import Versions
+ from troveclient.databases import Databases
+ from troveclient.flavors import Flavors
+ from troveclient.instances import Instances
+ from troveclient.limits import Limits
+ from troveclient.users import Users
+ from troveclient.root import Root
+ from troveclient.hosts import Hosts
+ from troveclient.quota import Quotas
+ from troveclient.backups import Backups
+ from troveclient.security_groups import SecurityGroups
+ from troveclient.security_groups import SecurityGroupRules
+ from troveclient.storage import StorageInfo
+ from troveclient.management import Management
+ from troveclient.accounts import Accounts
+ from troveclient.diagnostics import DiagnosticsInterrogator
+ from troveclient.diagnostics import HwInfoInterrogator
+
+ self.client = client_cls(username, api_key, tenant, auth_url,
+ service_type=service_type,
+ service_name=service_name,
+ service_url=service_url,
+ insecure=insecure,
+ auth_strategy=auth_strategy,
+ region_name=region_name)
+ self.versions = Versions(self)
+ self.databases = Databases(self)
+ self.flavors = Flavors(self)
+ self.instances = Instances(self)
+ self.limits = Limits(self)
+ self.users = Users(self)
+ self.root = Root(self)
+ self.hosts = Hosts(self)
+ self.quota = Quotas(self)
+ self.backups = Backups(self)
+ self.security_groups = SecurityGroups(self)
+ self.security_group_rules = SecurityGroupRules(self)
+ self.storage = StorageInfo(self)
+ self.management = Management(self)
+ self.accounts = Accounts(self)
+ self.diagnostics = DiagnosticsInterrogator(self)
+ self.hwinfo = HwInfoInterrogator(self)
+
+ class Mgmt(object):
+ def __init__(self, dbaas):
+ self.instances = dbaas.management
+ self.hosts = dbaas.hosts
+ self.accounts = dbaas.accounts
+ self.storage = dbaas.storage
+
+ self.mgmt = Mgmt(self)
+
+ def set_management_url(self, url):
+ self.client.management_url = url
+
+ def get_timings(self):
+ return self.client.get_timings()
+
+ def authenticate(self):
+ """
+ Authenticate against the server.
+
+ This is called to perform an authentication to retrieve a token.
+
+ Returns on success; raises :exc:`exceptions.Unauthorized` if the
+ credentials are wrong.
+ """
+ self.client.authenticate()