summaryrefslogtreecommitdiff
path: root/reddwarfclient
diff options
context:
space:
mode:
Diffstat (limited to 'reddwarfclient')
-rw-r--r--reddwarfclient/__init__.py31
-rw-r--r--reddwarfclient/accounts.py67
-rw-r--r--reddwarfclient/auth.py269
-rw-r--r--reddwarfclient/backups.py71
-rw-r--r--reddwarfclient/base.py293
-rw-r--r--reddwarfclient/cli.py385
-rw-r--r--reddwarfclient/client.py370
-rw-r--r--reddwarfclient/common.py406
-rw-r--r--reddwarfclient/databases.py79
-rw-r--r--reddwarfclient/diagnostics.py58
-rw-r--r--reddwarfclient/exceptions.py179
-rw-r--r--reddwarfclient/flavors.py62
-rw-r--r--reddwarfclient/hosts.py78
-rw-r--r--reddwarfclient/instances.py185
-rw-r--r--reddwarfclient/limits.py50
-rw-r--r--reddwarfclient/management.py136
-rw-r--r--reddwarfclient/mcli.py246
-rw-r--r--reddwarfclient/quota.py51
-rw-r--r--reddwarfclient/root.py44
-rw-r--r--reddwarfclient/security_groups.py120
-rw-r--r--reddwarfclient/storage.py45
-rw-r--r--reddwarfclient/tests/test_accounts.py84
-rw-r--r--reddwarfclient/tests/test_auth.py414
-rw-r--r--reddwarfclient/tests/test_base.py447
-rw-r--r--reddwarfclient/tests/test_client.py322
-rw-r--r--reddwarfclient/tests/test_common.py395
-rw-r--r--reddwarfclient/tests/test_instances.py176
-rw-r--r--reddwarfclient/tests/test_limits.py79
-rw-r--r--reddwarfclient/tests/test_management.py144
-rw-r--r--reddwarfclient/tests/test_secgroups.py102
-rw-r--r--reddwarfclient/tests/test_users.py126
-rw-r--r--reddwarfclient/tests/test_utils.py41
-rw-r--r--reddwarfclient/tests/test_xml.py241
-rw-r--r--reddwarfclient/users.py127
-rw-r--r--reddwarfclient/utils.py68
-rw-r--r--reddwarfclient/versions.py41
-rw-r--r--reddwarfclient/xml.py293
37 files changed, 0 insertions, 6325 deletions
diff --git a/reddwarfclient/__init__.py b/reddwarfclient/__init__.py
deleted file mode 100644
index 0383828..0000000
--- a/reddwarfclient/__init__.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# 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.
-
-
-from reddwarfclient.accounts import Accounts
-from reddwarfclient.databases import Databases
-from reddwarfclient.flavors import Flavors
-from reddwarfclient.instances import Instances
-from reddwarfclient.hosts import Hosts
-from reddwarfclient.management import Management
-from reddwarfclient.management import RootHistory
-from reddwarfclient.root import Root
-from reddwarfclient.storage import StorageInfo
-from reddwarfclient.users import Users
-from reddwarfclient.versions import Versions
-from reddwarfclient.diagnostics import DiagnosticsInterrogator
-from reddwarfclient.diagnostics import HwInfoInterrogator
-from reddwarfclient.client import Dbaas
-from reddwarfclient.client import ReddwarfHTTPClient
diff --git a/reddwarfclient/accounts.py b/reddwarfclient/accounts.py
deleted file mode 100644
index 43e9135..0000000
--- a/reddwarfclient/accounts.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# 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.
-
-from reddwarfclient import base
-from reddwarfclient.common import check_for_exceptions
-
-
-class Account(base.Resource):
- """
- Account is an opaque instance used to hold account information.
- """
- def __repr__(self):
- return "<Account: %s>" % self.name
-
-
-class Accounts(base.ManagerWithFind):
- """
- Manage :class:`Account` information.
- """
-
- resource_class = Account
-
- def _list(self, url, response_key):
- resp, body = self.api.client.get(url)
- if not body:
- raise Exception("Call to " + url + " did not return a body.")
- return self.resource_class(self, body[response_key])
-
- def index(self):
- """Get a list of all accounts with non-deleted instances"""
-
- url = "/mgmt/accounts"
- resp, body = self.api.client.get(url)
- check_for_exceptions(resp, body)
- if not body:
- raise Exception("Call to " + url + " did not return a body.")
- return base.Resource(self, body)
-
- def show(self, account):
- """
- Get details of one account.
-
- :rtype: :class:`Account`.
- """
-
- acct_name = self._get_account_name(account)
- return self._list("/mgmt/accounts/%s" % acct_name, 'account')
-
- @staticmethod
- def _get_account_name(account):
- try:
- if account.name:
- return account.name
- except AttributeError:
- return account
diff --git a/reddwarfclient/auth.py b/reddwarfclient/auth.py
deleted file mode 100644
index 4494447..0000000
--- a/reddwarfclient/auth.py
+++ /dev/null
@@ -1,269 +0,0 @@
-# Copyright 2012 OpenStack LLC
-#
-# 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 reddwarfclient import exceptions
-
-
-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):
- 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')
-
- try:
- print(resp_body)
- self.auth_token = resp_body['auth']['token']['id']
- except KeyError:
- raise nova_exceptions.AuthorizationFailure()
-
- catalog = resp_body['auth']['serviceCatalog']
- if 'cloudDatabases' not in catalog:
- raise nova_exceptions.EndpointNotFound()
- endpoints = catalog['cloudDatabases']
- for endpoint in endpoints:
- if self.region_name is None or \
- endpoint['region'] == self.region_name:
- self.management_url = endpoint['publicURL']
- return
- raise nova_exceptions.EndpointNotFound()
-
-
-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 Reddwarf 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 not 'serviceCatalog' 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/reddwarfclient/backups.py b/reddwarfclient/backups.py
deleted file mode 100644
index c78b840..0000000
--- a/reddwarfclient/backups.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# 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.
-
-from reddwarfclient import base
-import exceptions
-
-
-class Backup(base.Resource):
- """
- Backup is a resource used to hold backup information.
- """
- def __repr__(self):
- return "<Backup: %s>" % self.name
-
-
-class Backups(base.ManagerWithFind):
- """
- Manage :class:`Backups` information.
- """
-
- resource_class = Backup
-
- def get(self, backup):
- """
- Get a specific backup.
-
- :rtype: :class:`Backups`
- """
- return self._get("/backups/%s" % base.getid(backup),
- "backup")
-
- def list(self, limit=None, marker=None):
- """
- Get a list of all backups.
-
- :rtype: list of :class:`Backups`.
- """
- return self._list("/backups", "backups", limit, marker)
-
- def create(self, name, instance, description=None):
- """
- Create a new backup from the given instance.
- """
- body = {"backup": {
- "name": name,
- "instance": instance,
- "description": description,
- }}
- return self._create("/backups", body, "backup")
-
- def delete(self, backup_id):
- """
- Delete the specified backup.
-
- :param backup_id: The backup id to delete
- """
- resp, body = self.api.client.delete("/backups/%s" % backup_id)
- if resp.status in (422, 500):
- raise exceptions.from_response(resp, body)
diff --git a/reddwarfclient/base.py b/reddwarfclient/base.py
deleted file mode 100644
index 6660ff4..0000000
--- a/reddwarfclient/base.py
+++ /dev/null
@@ -1,293 +0,0 @@
-# Copyright 2010 Jacob Kaplan-Moss
-
-# Copyright 2012 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.
-
-"""
-Base utilities to build API operation managers and objects on top of.
-"""
-
-import contextlib
-import hashlib
-import os
-from reddwarfclient import exceptions
-from reddwarfclient import utils
-
-
-# Python 2.4 compat
-try:
- all
-except NameError:
- def all(iterable):
- return True not in (not x for x in iterable)
-
-
-def getid(obj):
- """
- Abstracts the common pattern of allowing both an object or an object's ID
- as a parameter when dealing with relationships.
- """
- try:
- return obj.id
- except AttributeError:
- return obj
-
-
-class Manager(utils.HookableMixin):
- """
- Managers interact with a particular type of API (servers, flavors, images,
- etc.) and provide CRUD operations for them.
- """
- resource_class = None
-
- def __init__(self, api):
- self.api = api
-
- def _list(self, url, response_key, obj_class=None, body=None):
- resp = None
- if body:
- resp, body = self.api.client.post(url, body=body)
- else:
- resp, body = self.api.client.get(url)
-
- if obj_class is None:
- obj_class = self.resource_class
-
- data = body[response_key]
- # NOTE(ja): keystone returns values as list as {'values': [ ... ]}
- # unlike other services which just return the list...
- if isinstance(data, dict):
- try:
- data = data['values']
- except KeyError:
- pass
-
- with self.completion_cache('human_id', obj_class, mode="w"):
- with self.completion_cache('uuid', obj_class, mode="w"):
- return [obj_class(self, res, loaded=True)
- for res in data if res]
-
- @contextlib.contextmanager
- def completion_cache(self, cache_type, obj_class, mode):
- """
- The completion cache store items that can be used for bash
- autocompletion, like UUIDs or human-friendly IDs.
-
- A resource listing will clear and repopulate the cache.
-
- A resource create will append to the cache.
-
- Delete is not handled because listings are assumed to be performed
- often enough to keep the cache reasonably up-to-date.
- """
- base_dir = utils.env('REDDWARFCLIENT_ID_CACHE_DIR',
- default="~/.reddwarfclient")
-
- # NOTE(sirp): Keep separate UUID caches for each username + endpoint
- # pair
- username = utils.env('OS_USERNAME', 'USERNAME')
- url = utils.env('OS_URL', 'SERVICE_URL')
- uniqifier = hashlib.md5(username + url).hexdigest()
-
- cache_dir = os.path.expanduser(os.path.join(base_dir, uniqifier))
-
- try:
- os.makedirs(cache_dir, 0755)
- except OSError:
- # NOTE(kiall): This is typicaly either permission denied while
- # attempting to create the directory, or the directory
- # already exists. Either way, don't fail.
- pass
-
- resource = obj_class.__name__.lower()
- filename = "%s-%s-cache" % (resource, cache_type.replace('_', '-'))
- path = os.path.join(cache_dir, filename)
-
- cache_attr = "_%s_cache" % cache_type
-
- try:
- setattr(self, cache_attr, open(path, mode))
- except IOError:
- # NOTE(kiall): This is typicaly a permission denied while
- # attempting to write the cache file.
- pass
-
- try:
- yield
- finally:
- cache = getattr(self, cache_attr, None)
- if cache:
- cache.close()
- delattr(self, cache_attr)
-
- def write_to_completion_cache(self, cache_type, val):
- cache = getattr(self, "_%s_cache" % cache_type, None)
- if cache:
- cache.write("%s\n" % val)
-
- def _get(self, url, response_key=None):
- resp, body = self.api.client.get(url)
- if response_key:
- return self.resource_class(self, body[response_key], loaded=True)
- else:
- return self.resource_class(self, body, loaded=True)
-
- def _create(self, url, body, response_key, return_raw=False, **kwargs):
- self.run_hooks('modify_body_for_create', body, **kwargs)
- resp, body = self.api.client.post(url, body=body)
- if return_raw:
- return body[response_key]
-
- with self.completion_cache('human_id', self.resource_class, mode="a"):
- with self.completion_cache('uuid', self.resource_class, mode="a"):
- return self.resource_class(self, body[response_key])
-
- def _delete(self, url):
- resp, body = self.api.client.delete(url)
-
- def _update(self, url, body, **kwargs):
- self.run_hooks('modify_body_for_update', body, **kwargs)
- resp, body = self.api.client.put(url, body=body)
- return body
-
-
-class ManagerWithFind(Manager):
- """
- Like a `Manager`, but with additional `find()`/`findall()` methods.
- """
- def find(self, **kwargs):
- """
- Find a single item with attributes matching ``**kwargs``.
-
- This isn't very efficient: it loads the entire list then filters on
- the Python side.
- """
- matches = self.findall(**kwargs)
- num_matches = len(matches)
- if num_matches == 0:
- msg = "No %s matching %s." % (self.resource_class.__name__, kwargs)
- raise exceptions.NotFound(404, msg)
- elif num_matches > 1:
- raise exceptions.NoUniqueMatch
- else:
- return matches[0]
-
- def findall(self, **kwargs):
- """
- Find all items with attributes matching ``**kwargs``.
-
- This isn't very efficient: it loads the entire list then filters on
- the Python side.
- """
- found = []
- searches = kwargs.items()
-
- for obj in self.list():
- try:
- if all(getattr(obj, attr) == value
- for (attr, value) in searches):
- found.append(obj)
- except AttributeError:
- continue
-
- return found
-
- def list(self):
- raise NotImplementedError
-
-
-class Resource(object):
- """
- A resource represents a particular instance of an object (server, flavor,
- etc). This is pretty much just a bag for attributes.
-
- :param manager: Manager object
- :param info: dictionary representing resource attributes
- :param loaded: prevent lazy-loading if set to True
- """
- HUMAN_ID = False
-
- def __init__(self, manager, info, loaded=False):
- self.manager = manager
- self._info = info
- self._add_details(info)
- self._loaded = loaded
-
- # NOTE(sirp): ensure `id` is already present because if it isn't we'll
- # enter an infinite loop of __getattr__ -> get -> __init__ ->
- # __getattr__ -> ...
- if 'id' in self.__dict__ and len(str(self.id)) == 36:
- self.manager.write_to_completion_cache('uuid', self.id)
-
- human_id = self.human_id
- if human_id:
- self.manager.write_to_completion_cache('human_id', human_id)
-
- @property
- def human_id(self):
- """Subclasses may override this provide a pretty ID which can be used
- for bash completion.
- """
- if 'name' in self.__dict__ and self.HUMAN_ID:
- return utils.slugify(self.name)
- return None
-
- def _add_details(self, info):
- for (k, v) in info.iteritems():
- try:
- setattr(self, k, v)
- except AttributeError:
- # In this case we already defined the attribute on the class
- pass
-
- 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()
- return self.__getattr__(k)
-
- raise AttributeError(k)
- else:
- return self.__dict__[k]
-
- def __repr__(self):
- reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and
- k != 'manager')
- info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
- return "<%s %s>" % (self.__class__.__name__, info)
-
- def get(self):
- # set_loaded() first ... so if we have to bail, we know we tried.
- self.set_loaded(True)
- if not hasattr(self.manager, 'get'):
- return
-
- new = self.manager.get(self.id)
- if new:
- self._add_details(new._info)
-
- def __eq__(self, other):
- if not isinstance(other, self.__class__):
- return False
- if hasattr(self, 'id') and hasattr(other, 'id'):
- return self.id == other.id
- return self._info == other._info
-
- def is_loaded(self):
- return self._loaded
-
- def set_loaded(self, val):
- self._loaded = val
diff --git a/reddwarfclient/cli.py b/reddwarfclient/cli.py
deleted file mode 100644
index f83de6d..0000000
--- a/reddwarfclient/cli.py
+++ /dev/null
@@ -1,385 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright 2011 OpenStack LLC
-#
-# 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.
-
-"""
-Reddwarf Command line tool
-"""
-
-#TODO(tim.simpson): optparse is deprecated. Replace with argparse.
-import optparse
-import os
-import sys
-
-
-# If ../reddwarf/__init__.py exists, add ../ to Python search path, so that
-# it will override what happens to be installed in /usr/(local/)lib/python...
-possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
- os.pardir,
- os.pardir))
-if os.path.exists(os.path.join(possible_topdir, 'reddwarfclient',
- '__init__.py')):
- sys.path.insert(0, possible_topdir)
-
-
-from reddwarfclient import common
-
-
-class InstanceCommands(common.AuthedCommandsBase):
- """Commands to perform various instances operations and actions"""
-
- params = [
- 'flavor',
- 'id',
- 'limit',
- 'marker',
- 'name',
- 'size',
- 'backup'
- ]
-
- def create(self):
- """Create a new instance"""
- self._require('name', 'flavor')
- volume = None
- if self.size is not None:
- volume = {"size": self.size}
- restorePoint = None
- if self.backup is not None:
- restorePoint = {"backupRef": self.backup}
- self._pretty_print(self.dbaas.instances.create, self.name,
- self.flavor, volume, restorePoint=restorePoint)
-
- def delete(self):
- """Delete the specified instance"""
- self._require('id')
- print self.dbaas.instances.delete(self.id)
-
- def get(self):
- """Get details for the specified instance"""
- self._require('id')
- self._pretty_print(self.dbaas.instances.get, self.id)
-
- def backups(self):
- """Get a list of backups for the specified instance"""
- self._require('id')
- self._pretty_list(self.dbaas.instances.backups, self.id)
-
- def list(self):
- """List all instances for account"""
- # limit and marker are not required.
- limit = self.limit or None
- if limit:
- limit = int(limit, 10)
- self._pretty_paged(self.dbaas.instances.list)
-
- def resize_volume(self):
- """Resize an instance volume"""
- self._require('id', 'size')
- self._pretty_print(self.dbaas.instances.resize_volume, self.id,
- self.size)
-
- def resize_instance(self):
- """Resize an instance flavor"""
- self._require('id', 'flavor')
- self._pretty_print(self.dbaas.instances.resize_instance, self.id,
- self.flavor)
-
- def restart(self):
- """Restart the database"""
- self._require('id')
- self._pretty_print(self.dbaas.instances.restart, self.id)
-
- def reset_password(self):
- """Reset the root user Password"""
- self._require('id')
- self._pretty_print(self.dbaas.instances.reset_password, self.id)
-
-
-class FlavorsCommands(common.AuthedCommandsBase):
- """Commands for listing Flavors"""
-
- params = []
-
- def list(self):
- """List the available flavors"""
- self._pretty_list(self.dbaas.flavors.list)
-
-
-class DatabaseCommands(common.AuthedCommandsBase):
- """Database CRUD operations on an instance"""
-
- params = [
- 'name',
- 'id',
- 'limit',
- 'marker',
- ]
-
- def create(self):
- """Create a database"""
- self._require('id', 'name')
- databases = [{'name': self.name}]
- print self.dbaas.databases.create(self.id, databases)
-
- def delete(self):
- """Delete a database"""
- self._require('id', 'name')
- print self.dbaas.databases.delete(self.id, self.name)
-
- def list(self):
- """List the databases"""
- self._require('id')
- self._pretty_paged(self.dbaas.databases.list, self.id)
-
-
-class UserCommands(common.AuthedCommandsBase):
- """User CRUD operations on an instance"""
- params = [
- 'id',
- 'database',
- 'databases',
- 'hostname',
- 'name',
- 'password',
- ]
-
- def create(self):
- """Create a user in instance, with access to one or more databases"""
- self._require('id', 'name', 'password', 'databases')
- self._make_list('databases')
- databases = [{'name': dbname} for dbname in self.databases]
- users = [{'name': self.name, 'host': self.hostname,
- 'password': self.password, 'databases': databases}]
- self.dbaas.users.create(self.id, users)
-
- def delete(self):
- """Delete the specified user"""
- self._require('id', 'name')
- self.dbaas.users.delete(self.id, self.name, self.hostname)
-
- def get(self):
- """Get a single user."""
- self._require('id', 'name')
- self._pretty_print(self.dbaas.users.get, self.id,
- self.name, self.hostname)
-
- def list(self):
- """List all the users for an instance"""
- self._require('id')
- self._pretty_paged(self.dbaas.users.list, self.id)
-
- def access(self):
- """Show all databases the user has access to."""
- self._require('id', 'name')
- self._pretty_list(self.dbaas.users.list_access, self.id,
- self.name, self.hostname)
-
- def grant(self):
- """Allow an existing user permissions to access one or more
- databases."""
- self._require('id', 'name', 'databases')
- self._make_list('databases')
- self.dbaas.users.grant(self.id, self.name, self.databases,
- self.hostname)
-
- def revoke(self):
- """Revoke from an existing user access permissions to a database."""
- self._require('id', 'name', 'database')
- self.dbaas.users.revoke(self.id, self.name, self.database,
- self.hostname)
-
- def change_password(self):
- """Change the password of a single user."""
- self._require('id', 'name', 'password')
- users = [{'name': self.name,
- 'host': self.hostname,
- 'password': self.password}]
- self.dbaas.users.change_passwords(self.id, users)
-
-
-class RootCommands(common.AuthedCommandsBase):
- """Root user related operations on an instance"""
-
- params = [
- 'id',
- ]
-
- def create(self):
- """Enable the instance's root user."""
- self._require('id')
- try:
- user, password = self.dbaas.root.create(self.id)
- print "User:\t\t%s\nPassword:\t%s" % (user, password)
- except:
- print sys.exc_info()[1]
-
- def enabled(self):
- """Check the instance for root access"""
- self._require('id')
- self._pretty_print(self.dbaas.root.is_root_enabled, self.id)
-
-
-class VersionCommands(common.AuthedCommandsBase):
- """List available versions"""
-
- params = [
- 'url',
- ]
-
- def list(self):
- """List all the supported versions"""
- self._require('url')
- self._pretty_list(self.dbaas.versions.index, self.url)
-
-
-class LimitsCommands(common.AuthedCommandsBase):
- """Show the rate limits and absolute limits"""
-
- def list(self):
- """List the rate limits and absolute limits"""
- self._pretty_list(self.dbaas.limits.list)
-
-
-class BackupsCommands(common.AuthedCommandsBase):
- """Command to manage and show backups"""
- params = ['name', 'instance', 'description']
-
- def get(self):
- """Get details for the specified backup"""
- self._require('id')
- self._pretty_print(self.dbaas.backups.get, self.id)
-
- def list(self):
- """List backups"""
- self._pretty_list(self.dbaas.backups.list)
-
- def create(self):
- """Create a new backup"""
- self._require('name', 'instance')
- self._pretty_print(self.dbaas.backups.create, self.name,
- self.instance, self.description)
-
- def delete(self):
- """Delete a backup"""
- self._require('id')
- self._pretty_print(self.dbaas.backups.delete, self.id)
-
-
-class SecurityGroupCommands(common.AuthedCommandsBase):
- """Commands to list and show Security Groups For an Instance and """
- """create and delete security group rules for them. """
- params = ['id',
- 'secgroup_id',
- 'protocol',
- 'from_port',
- 'to_port',
- 'cidr'
- ]
-
- def get(self):
- """Get a security group associated with an instance."""
- self._require('id')
- self._pretty_print(self.dbaas.security_groups.get, self.id)
-
- def list(self):
- """List all the Security Groups and the rules"""
- self._pretty_paged(self.dbaas.security_groups.list)
-
- def add_rule(self):
- """Add a security group rule"""
- self._require('secgroup_id', 'protocol',
- 'from_port', 'to_port', 'cidr')
- self.dbaas.security_group_rules.create(self.secgroup_id, self.protocol,
- self.from_port, self.to_port,
- self.cidr)
-
- def delete_rule(self):
- """Delete a security group rule"""
- self._require('id')
- self.dbaas.security_group_rules.delete(self.id)
-
-
-COMMANDS = {'auth': common.Auth,
- 'instance': InstanceCommands,
- 'flavor': FlavorsCommands,
- 'database': DatabaseCommands,
- 'limit': LimitsCommands,
- 'backup': BackupsCommands,
- 'user': UserCommands,
- 'root': RootCommands,
- 'version': VersionCommands,
- 'secgroup': SecurityGroupCommands,
- }
-
-
-def main():
- # Parse arguments
- load_file = True
- for index, arg in enumerate(sys.argv):
- if (arg == "auth" and len(sys.argv) > (index + 1)
- and sys.argv[index + 1] == "login"):
- load_file = False
-
- oparser = common.CliOptions.create_optparser(load_file)
- for k, v in COMMANDS.items():
- v._prepare_parser(oparser)
- (options, args) = oparser.parse_args()
-
- if not args:
- common.print_commands(COMMANDS)
-
- if options.verbose:
- os.environ['RDC_PP'] = "True"
- os.environ['REDDWARFCLIENT_DEBUG'] = "True"
-
- # Pop the command and check if it's in the known commands
- cmd = args.pop(0)
- if cmd in COMMANDS:
- fn = COMMANDS.get(cmd)
- command_object = None
- try:
- command_object = fn(oparser)
- except Exception as ex:
- if options.debug:
- raise
- print(ex)
-
- # Get a list of supported actions for the command
- actions = common.methods_of(command_object)
-
- if len(args) < 1:
- common.print_actions(cmd, actions)
-
- # Check for a valid action and perform that action
- action = args.pop(0)
- if action in actions:
- if not options.debug:
- try:
- getattr(command_object, action)()
- except Exception as ex:
- if options.debug:
- raise
- print ex
- else:
- getattr(command_object, action)()
- else:
- common.print_actions(cmd, actions)
- else:
- common.print_commands(COMMANDS)
-
-
-if __name__ == '__main__':
- main()
diff --git a/reddwarfclient/client.py b/reddwarfclient/client.py
deleted file mode 100644
index f602afb..0000000
--- a/reddwarfclient/client.py
+++ /dev/null
@@ -1,370 +0,0 @@
-# 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 reddwarfclient import auth
-from reddwarfclient 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 ReddwarfHTTPClient(httplib2.Http):
-
- USER_AGENT = 'python-reddwarfclient'
-
- 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(ReddwarfHTTPClient, 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 reddwarfclient 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(ReddwarfHTTPClient, 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='reddwarf',
- service_url=None, insecure=False, auth_strategy='keystone',
- region_name=None, client_cls=ReddwarfHTTPClient):
- from reddwarfclient.versions import Versions
- from reddwarfclient.databases import Databases
- from reddwarfclient.flavors import Flavors
- from reddwarfclient.instances import Instances
- from reddwarfclient.limits import Limits
- from reddwarfclient.users import Users
- from reddwarfclient.root import Root
- from reddwarfclient.hosts import Hosts
- from reddwarfclient.quota import Quotas
- from reddwarfclient.backups import Backups
- from reddwarfclient.security_groups import SecurityGroups
- from reddwarfclient.security_groups import SecurityGroupRules
- from reddwarfclient.storage import StorageInfo
- from reddwarfclient.management import Management
- from reddwarfclient.accounts import Accounts
- from reddwarfclient.diagnostics import DiagnosticsInterrogator
- from reddwarfclient.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()
diff --git a/reddwarfclient/common.py b/reddwarfclient/common.py
deleted file mode 100644
index 9c66c49..0000000
--- a/reddwarfclient/common.py
+++ /dev/null
@@ -1,406 +0,0 @@
-# Copyright 2011 OpenStack LLC
-#
-# 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 copy
-import json
-import optparse
-import os
-import pickle
-import sys
-
-from reddwarfclient import client
-from reddwarfclient.xml import ReddwarfXmlClient
-from reddwarfclient import exceptions
-
-from urllib import quote
-
-
-def methods_of(obj):
- """Get all callable methods of an object that don't start with underscore
- returns a list of tuples of the form (method_name, method)"""
- result = {}
- for i in dir(obj):
- if callable(getattr(obj, i)) and not i.startswith('_'):
- result[i] = getattr(obj, i)
- return result
-
-
-def check_for_exceptions(resp, body):
- if resp.status in (400, 422, 500):
- raise exceptions.from_response(resp, body)
-
-
-def print_actions(cmd, actions):
- """Print help for the command with list of options and description"""
- print ("Available actions for '%s' cmd:") % cmd
- for k, v in actions.iteritems():
- print "\t%-20s%s" % (k, v.__doc__)
- sys.exit(2)
-
-
-def print_commands(commands):
- """Print the list of available commands and description"""
-
- print "Available commands"
- for k, v in commands.iteritems():
- print "\t%-20s%s" % (k, v.__doc__)
- sys.exit(2)
-
-
-def limit_url(url, limit=None, marker=None):
- if not limit and not marker:
- return url
- query = []
- if marker:
- query.append("marker=%s" % marker)
- if limit:
- query.append("limit=%s" % limit)
- query = '?' + '&'.join(query)
- return url + query
-
-
-def quote_user_host(user, host):
- quoted = ''
- if host:
- quoted = quote("%s@%s" % (user, host))
- else:
- quoted = quote("%s" % user)
- return quoted.replace('.', '%2e')
-
-
-class CliOptions(object):
- """A token object containing the user, apikey and token which
- is pickleable."""
-
- APITOKEN = os.path.expanduser("~/.apitoken")
-
- DEFAULT_VALUES = {
- 'username': None,
- 'apikey': None,
- 'tenant_id': None,
- 'auth_url': None,
- 'auth_type': 'keystone',
- 'service_type': 'database',
- 'service_name': 'reddwarf',
- 'region': 'RegionOne',
- 'service_url': None,
- 'insecure': False,
- 'verbose': False,
- 'debug': False,
- 'token': None,
- 'xml': None,
- }
-
- def __init__(self, **kwargs):
- for key, value in self.DEFAULT_VALUES.items():
- setattr(self, key, value)
-
- @classmethod
- def default(cls):
- kwargs = copy.deepcopy(cls.DEFAULT_VALUES)
- return cls(**kwargs)
-
- @classmethod
- def load_from_file(cls):
- try:
- with open(cls.APITOKEN, 'rb') as token:
- return pickle.load(token)
- except IOError:
- pass # File probably not found.
- except:
- print("ERROR: Token file found at %s was corrupt." % cls.APITOKEN)
- return cls.default()
-
- @classmethod
- def save_from_instance_fields(cls, instance):
- apitoken = cls.default()
- for key, default_value in cls.DEFAULT_VALUES.items():
- final_value = getattr(instance, key, default_value)
- setattr(apitoken, key, final_value)
- with open(cls.APITOKEN, 'wb') as token:
- pickle.dump(apitoken, token, protocol=2)
-
- @classmethod
- def create_optparser(cls, load_file):
- oparser = optparse.OptionParser(
- usage="%prog [options] <cmd> <action> <args>",
- version='1.0', conflict_handler='resolve')
- if load_file:
- file = cls.load_from_file()
- else:
- file = cls.default()
-
- def add_option(*args, **kwargs):
- if len(args) == 1:
- name = args[0]
- else:
- name = args[1]
- kwargs['default'] = getattr(file, name, cls.DEFAULT_VALUES[name])
- oparser.add_option("--%s" % name, **kwargs)
-
- add_option("verbose", action="store_true",
- help="Show equivalent curl statement along "
- "with actual HTTP communication.")
- add_option("debug", action="store_true",
- help="Show the stack trace on errors.")
- add_option("auth_url", help="Auth API endpoint URL with port and "
- "version. Default: http://localhost:5000/v2.0")
- add_option("username", help="Login username")
- add_option("apikey", help="Api key")
- add_option("tenant_id",
- help="Tenant Id associated with the account")
- add_option("auth_type",
- help="Auth type to support different auth environments, \
- Supported values are 'keystone', 'rax'.")
- add_option("service_type",
- help="Service type is a name associated for the catalog")
- add_option("service_name",
- help="Service name as provided in the service catalog")
- add_option("service_url",
- help="Service endpoint to use if the catalog doesn't have one.")
- add_option("region", help="Region the service is located in")
- add_option("insecure", action="store_true",
- help="Run in insecure mode for https endpoints.")
- add_option("token", help="Token from a prior login.")
- add_option("xml", action="store_true", help="Changes format to XML.")
-
- oparser.add_option("--secure", action="store_false", dest="insecure",
- help="Run in insecure mode for https endpoints.")
- oparser.add_option("--json", action="store_false", dest="xml",
- help="Changes format to JSON.")
- oparser.add_option("--terse", action="store_false", dest="verbose",
- help="Toggles verbose mode off.")
- oparser.add_option("--hide-debug", action="store_false", dest="debug",
- help="Toggles debug mode off.")
- return oparser
-
-
-class ArgumentRequired(Exception):
- def __init__(self, param):
- self.param = param
-
- def __str__(self):
- return 'Argument "--%s" required.' % self.param
-
-
-class CommandsBase(object):
- params = []
-
- def __init__(self, parser):
- self._parse_options(parser)
-
- def _get_client(self):
- """Creates the all important client object."""
- try:
- if self.xml:
- client_cls = ReddwarfXmlClient
- else:
- client_cls = client.ReddwarfHTTPClient
- if self.verbose:
- client.log_to_streamhandler(sys.stdout)
- client.RDC_PP = True
- return client.Dbaas(self.username, self.apikey, self.tenant_id,
- auth_url=self.auth_url,
- auth_strategy=self.auth_type,
- service_type=self.service_type,
- service_name=self.service_name,
- region_name=self.region,
- service_url=self.service_url,
- insecure=self.insecure,
- client_cls=client_cls)
- except:
- if self.debug:
- raise
- print sys.exc_info()[1]
-
- def _safe_exec(self, func, *args, **kwargs):
- if not self.debug:
- try:
- return func(*args, **kwargs)
- except:
- print(sys.exc_info()[1])
- return None
- else:
- return func(*args, **kwargs)
-
- @classmethod
- def _prepare_parser(cls, parser):
- for param in cls.params:
- parser.add_option("--%s" % param)
-
- def _parse_options(self, parser):
- opts, args = parser.parse_args()
- for param in opts.__dict__:
- value = getattr(opts, param)
- setattr(self, param, value)
-
- def _require(self, *params):
- for param in params:
- if not hasattr(self, param):
- raise ArgumentRequired(param)
- if not getattr(self, param):
- raise ArgumentRequired(param)
-
- def _make_list(self, *params):
- # Convert the listed params to lists.
- for param in params:
- raw = getattr(self, param)
- if isinstance(raw, list):
- return
- raw = [item.strip() for item in raw.split(',')]
- setattr(self, param, raw)
-
- def _pretty_print(self, func, *args, **kwargs):
- if self.verbose:
- self._safe_exec(func, *args, **kwargs)
- return # Skip this, since the verbose stuff will show up anyway.
-
- def wrapped_func():
- result = func(*args, **kwargs)
- if result:
- print(json.dumps(result._info, sort_keys=True, indent=4))
- else:
- print("OK")
- self._safe_exec(wrapped_func)
-
- def _dumps(self, item):
- return json.dumps(item, sort_keys=True, indent=4)
-
- def _pretty_list(self, func, *args, **kwargs):
- result = self._safe_exec(func, *args, **kwargs)
- if self.verbose:
- return
- if result and len(result) > 0:
- for item in result:
- print(self._dumps(item._info))
- else:
- print("OK")
-
- def _pretty_paged(self, func, *args, **kwargs):
- try:
- limit = self.limit
- if limit:
- limit = int(limit, 10)
- result = func(*args, limit=limit, marker=self.marker, **kwargs)
- if self.verbose:
- return # Verbose already shows the output, so skip this.
- if result and len(result) > 0:
- for item in result:
- print self._dumps(item._info)
- if result.links:
- print("Links:")
- for link in result.links:
- print self._dumps((link))
- else:
- print("OK")
- except:
- if self.debug:
- raise
- print sys.exc_info()[1]
-
-
-class Auth(CommandsBase):
- """Authenticate with your username and api key"""
- params = [
- 'apikey',
- 'auth_strategy',
- 'auth_type',
- 'auth_url',
- 'options',
- 'region',
- 'service_name',
- 'service_type',
- 'service_url',
- 'tenant_id',
- 'username',
- ]
-
- def __init__(self, parser):
- super(Auth, self).__init__(parser)
- self.dbaas = None
-
- def login(self):
- """Login to retrieve an auth token to use for other api calls"""
- self._require('username', 'apikey', 'tenant_id', 'auth_url')
- try:
- self.dbaas = self._get_client()
- self.dbaas.authenticate()
- self.token = self.dbaas.client.auth_token
- self.service_url = self.dbaas.client.service_url
- CliOptions.save_from_instance_fields(self)
- print("Token aquired! Saving to %s..." % CliOptions.APITOKEN)
- print(" service_url = %s" % self.service_url)
- print(" token = %s" % self.token)
- except:
- if self.debug:
- raise
- print sys.exc_info()[1]
-
-
-class AuthedCommandsBase(CommandsBase):
- """Commands that work only with an authicated client."""
-
- def __init__(self, parser):
- """Makes sure a token is available somehow and logs in."""
- super(AuthedCommandsBase, self).__init__(parser)
- try:
- self._require('token')
- except ArgumentRequired:
- if self.debug:
- raise
- print('No token argument supplied. Use the "auth login" command '
- 'to log in and get a token.\n')
- sys.exit(1)
- try:
- self._require('service_url')
- except ArgumentRequired:
- if self.debug:
- raise
- print('No service_url given.\n')
- sys.exit(1)
- self.dbaas = self._get_client()
- # Actually set the token to avoid a re-auth.
- self.dbaas.client.auth_token = self.token
- self.dbaas.client.authenticate_with_token(self.token, self.service_url)
-
-
-class Paginated(object):
- """ Pretends to be a list if you iterate over it, but also keeps a
- next property you can use to get the next page of data. """
-
- def __init__(self, items=[], next_marker=None, links=[]):
- self.items = items
- self.next = next_marker
- self.links = links
-
- def __len__(self):
- return len(self.items)
-
- def __iter__(self):
- return self.items.__iter__()
-
- def __getitem__(self, key):
- return self.items[key]
-
- def __setitem__(self, key, value):
- self.items[key] = value
-
- def __delitem__(self, key):
- del self.items[key]
-
- def __reversed__(self):
- return reversed(self.items)
-
- def __contains__(self, needle):
- return needle in self.items
diff --git a/reddwarfclient/databases.py b/reddwarfclient/databases.py
deleted file mode 100644
index d7f31e1..0000000
--- a/reddwarfclient/databases.py
+++ /dev/null
@@ -1,79 +0,0 @@
-from reddwarfclient import base
-from reddwarfclient.common import check_for_exceptions
-from reddwarfclient.common import limit_url
-from reddwarfclient.common import Paginated
-import exceptions
-import urlparse
-
-
-class Database(base.Resource):
- """
- According to Wikipedia, "A database is a system intended to organize,
- store, and retrieve
- large amounts of data easily."
- """
- def __repr__(self):
- return "<Database: %s>" % self.name
-
-
-class Databases(base.ManagerWithFind):
- """
- Manage :class:`Databases` resources.
- """
- resource_class = Database
-
- def create(self, instance_id, databases):
- """
- Create new databases within the specified instance
- """
- body = {"databases": databases}
- url = "/instances/%s/databases" % instance_id
- resp, body = self.api.client.post(url, body=body)
- check_for_exceptions(resp, body)
-
- def delete(self, instance_id, dbname):
- """Delete an existing database in the specified instance"""
- url = "/instances/%s/databases/%s" % (instance_id, dbname)
- resp, body = self.api.client.delete(url)
- check_for_exceptions(resp, body)
-
- def _list(self, url, response_key, limit=None, marker=None):
- resp, body = self.api.client.get(limit_url(url, limit, marker))
- check_for_exceptions(resp, body)
- if not body:
- raise Exception("Call to " + url +
- " did not return a body.")
- links = body.get('links', [])
- next_links = [link['href'] for link in links if link['rel'] == 'next']
- next_marker = None
- for link in next_links:
- # Extract the marker from the url.
- parsed_url = urlparse.urlparse(link)
- query_dict = dict(urlparse.parse_qsl(parsed_url.query))
- next_marker = query_dict.get('marker', None)
- databases = body[response_key]
- databases = [self.resource_class(self, res) for res in databases]
- return Paginated(databases, next_marker=next_marker, links=links)
-
- def list(self, instance, limit=None, marker=None):
- """
- Get a list of all Databases from the instance.
-
- :rtype: list of :class:`Database`.
- """
- return self._list("/instances/%s/databases" % base.getid(instance),
- "databases", limit, marker)
-
-# def get(self, instance, database):
-# """
-# Get a specific instances.
-#
-# :param flavor: The ID of the :class:`Database` to get.
-# :rtype: :class:`Database`
-# """
-# assert isinstance(instance, Instance)
-# assert isinstance(database, (Database, int))
-# instance_id = base.getid(instance)
-# db_id = base.getid(database)
-# url = "/instances/%s/databases/%s" % (instance_id, db_id)
-# return self._get(url, "database")
diff --git a/reddwarfclient/diagnostics.py b/reddwarfclient/diagnostics.py
deleted file mode 100644
index 06da06c..0000000
--- a/reddwarfclient/diagnostics.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# 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.
-
-from reddwarfclient import base
-import exceptions
-
-
-class Diagnostics(base.Resource):
- """
- Account is an opaque instance used to hold account information.
- """
- def __repr__(self):
- return "<Diagnostics: %s>" % self.version
-
-
-class DiagnosticsInterrogator(base.ManagerWithFind):
- """
- Manager class for Interrogator resource
- """
- resource_class = Diagnostics
-
- def get(self, instance):
- """
- Get the diagnostics of the guest on the instance.
- """
- return self._get("/mgmt/instances/%s/diagnostics" %
- base.getid(instance), "diagnostics")
-
-
-class HwInfo(base.Resource):
-
- def __repr__(self):
- return "<HwInfo: %s>" % self.version
-
-
-class HwInfoInterrogator(base.ManagerWithFind):
- """
- Manager class for HwInfo
- """
- resource_class = HwInfo
-
- def get(self, instance):
- """
- Get the hardware information of the instance.
- """
- return self._get("/mgmt/instances/%s/hwinfo" % base.getid(instance))
diff --git a/reddwarfclient/exceptions.py b/reddwarfclient/exceptions.py
deleted file mode 100644
index 88c3f7e..0000000
--- a/reddwarfclient/exceptions.py
+++ /dev/null
@@ -1,179 +0,0 @@
-# Copyright 2011 OpenStack LLC
-#
-# 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 UnsupportedVersion(Exception):
- """Indicates that the user is trying to use an unsupported
- version of the API"""
- pass
-
-
-class CommandError(Exception):
- pass
-
-
-class AuthorizationFailure(Exception):
- pass
-
-
-class NoUniqueMatch(Exception):
- pass
-
-
-class NoTokenLookupException(Exception):
- """This form of authentication does not support looking up
- endpoints from an existing token."""
- pass
-
-
-class EndpointNotFound(Exception):
- """Could not find Service or Region in Service Catalog."""
- pass
-
-
-class AuthUrlNotGiven(EndpointNotFound):
- """The auth url was not given."""
- pass
-
-
-class ServiceUrlNotGiven(EndpointNotFound):
- """The service url was not given."""
- pass
-
-
-class ResponseFormatError(Exception):
- """Could not parse the response format."""
- pass
-
-
-class AmbiguousEndpoints(Exception):
- """Found more than one matching endpoint in Service Catalog."""
- def __init__(self, endpoints=None):
- self.endpoints = endpoints
-
- def __str__(self):
- return "AmbiguousEndpoints: %s" % repr(self.endpoints)
-
-
-class ClientException(Exception):
- """
- The base exception class for all exceptions this library raises.
- """
- def __init__(self, code, message=None, details=None, request_id=None):
- self.code = code
- self.message = message or self.__class__.message
- self.details = details
- self.request_id = request_id
-
- def __str__(self):
- formatted_string = "%s (HTTP %s)" % (self.message, self.code)
- if self.request_id:
- formatted_string += " (Request-ID: %s)" % self.request_id
-
- return formatted_string
-
-
-class BadRequest(ClientException):
- """
- HTTP 400 - Bad request: you sent some malformed data.
- """
- http_status = 400
- message = "Bad request"
-
-
-class Unauthorized(ClientException):
- """
- HTTP 401 - Unauthorized: bad credentials.
- """
- http_status = 401
- message = "Unauthorized"
-
-
-class Forbidden(ClientException):
- """
- HTTP 403 - Forbidden: your credentials don't give you access to this
- resource.
- """
- http_status = 403
- message = "Forbidden"
-
-
-class NotFound(ClientException):
- """
- HTTP 404 - Not found
- """
- http_status = 404
- message = "Not found"
-
-
-class OverLimit(ClientException):
- """
- HTTP 413 - Over limit: you're over the API limits for this time period.
- """
- http_status = 413
- message = "Over limit"
-
-
-# NotImplemented is a python keyword.
-class HTTPNotImplemented(ClientException):
- """
- HTTP 501 - Not Implemented: the server does not support this operation.
- """
- http_status = 501
- message = "Not Implemented"
-
-
-class UnprocessableEntity(ClientException):
- """
- HTTP 422 - Unprocessable Entity: The request cannot be processed.
- """
- http_status = 422
- message = "Unprocessable Entity"
-
-
-# In Python 2.4 Exception is old-style and thus doesn't have a __subclasses__()
-# so we can do this:
-# _code_map = dict((c.http_status, c)
-# for c in ClientException.__subclasses__())
-#
-# Instead, we have to hardcode it:
-_code_map = dict((c.http_status, c) for c in [BadRequest, Unauthorized,
- Forbidden, NotFound, OverLimit,
- HTTPNotImplemented,
- UnprocessableEntity])
-
-
-def from_response(response, body):
- """
- Return an instance of an ClientException or subclass
- based on an httplib2 response.
-
- Usage::
-
- resp, body = http.request(...)
- if resp.status != 200:
- raise exception_from_response(resp, body)
- """
- cls = _code_map.get(response.status, ClientException)
- if body:
- message = "n/a"
- details = "n/a"
- if hasattr(body, 'keys'):
- error = body[body.keys()[0]]
- message = error.get('message', None)
- details = error.get('details', None)
- return cls(code=response.status, message=message, details=details)
- else:
- request_id = response.get('x-compute-request-id')
- return cls(code=response.status, request_id=request_id)
diff --git a/reddwarfclient/flavors.py b/reddwarfclient/flavors.py
deleted file mode 100644
index ba01a5f..0000000
--- a/reddwarfclient/flavors.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# Copyright (c) 2012 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.
-
-
-from reddwarfclient import base
-
-import exceptions
-
-from reddwarfclient.common import check_for_exceptions
-
-
-class Flavor(base.Resource):
- """
- A Flavor is an Instance type, specifying among other things, RAM size.
- """
- def __repr__(self):
- return "<Flavor: %s>" % self.name
-
-
-class Flavors(base.ManagerWithFind):
- """
- Manage :class:`Flavor` resources.
- """
- resource_class = Flavor
-
- def __repr__(self):
- return "<Flavors Manager at %s>" % id(self)
-
- def _list(self, url, response_key):
- resp, body = self.api.client.get(url)
- if not body:
- raise Exception("Call to " + url + " did not return a body.")
- return [self.resource_class(self, res) for res in body[response_key]]
-
- def list(self):
- """
- Get a list of all flavors.
-
- :rtype: list of :class:`Flavor`.
- """
- return self._list("/flavors", "flavors")
-
- def get(self, flavor):
- """
- Get a specific flavor.
-
- :rtype: :class:`Flavor`
- """
- return self._get("/flavors/%s" % base.getid(flavor),
- "flavor")
diff --git a/reddwarfclient/hosts.py b/reddwarfclient/hosts.py
deleted file mode 100644
index fb03d37..0000000
--- a/reddwarfclient/hosts.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# 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.
-
-from reddwarfclient import base
-
-from reddwarfclient.common import check_for_exceptions
-
-
-class Host(base.Resource):
- """
- A Hosts is an opaque instance used to store Host instances.
- """
- def __repr__(self):
- return "<Host: %s>" % self.name
-
-
-class Hosts(base.ManagerWithFind):
- """
- Manage :class:`Host` resources.
- """
- resource_class = Host
-
- def _list(self, url, response_key):
- resp, body = self.api.client.get(url)
- if not body:
- raise Exception("Call to " + url + " did not return a body.")
- return [self.resource_class(self, res) for res in body[response_key]]
-
- def _action(self, host_id, body):
- """
- Perform a host "action" -- update
- """
- url = "/mgmt/hosts/%s/instances/action" % host_id
- resp, body = self.api.client.post(url, body=body)
- check_for_exceptions(resp, body)
-
- def update_all(self, host_id):
- """
- Update all instances on a host.
- """
- body = {'update': ''}
- self._action(host_id, body)
-
- def index(self):
- """
- Get a list of all hosts.
-
- :rtype: list of :class:`Hosts`.
- """
- return self._list("/mgmt/hosts", "hosts")
-
- def get(self, host):
- """
- Get a specific host.
-
- :rtype: :class:`host`
- """
- return self._get("/mgmt/hosts/%s" % self._get_host_name(host), "host")
-
- @staticmethod
- def _get_host_name(host):
- try:
- if host.name:
- return host.name
- except AttributeError:
- return host
diff --git a/reddwarfclient/instances.py b/reddwarfclient/instances.py
deleted file mode 100644
index 66b091c..0000000
--- a/reddwarfclient/instances.py
+++ /dev/null
@@ -1,185 +0,0 @@
-# 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.
-
-from reddwarfclient import base
-
-import exceptions
-import urlparse
-
-from reddwarfclient.common import check_for_exceptions
-from reddwarfclient.common import limit_url
-from reddwarfclient.common import Paginated
-
-
-REBOOT_SOFT, REBOOT_HARD = 'SOFT', 'HARD'
-
-
-class Instance(base.Resource):
- """
- An Instance is an opaque instance used to store Database instances.
- """
- def __repr__(self):
- return "<Instance: %s>" % self.name
-
- def list_databases(self):
- return self.manager.databases.list(self)
-
- def delete(self):
- """
- Delete the instance.
- """
- self.manager.delete(self)
-
- def restart(self):
- """
- Restart the database instance
- """
- self.manager.restart(self.id)
-
-
-class Instances(base.ManagerWithFind):
- """
- Manage :class:`Instance` resources.
- """
- resource_class = Instance
-
- def create(self, name, flavor_id, volume=None, databases=None, users=None,
- restorePoint=None):
- """
- Create (boot) a new instance.
- """
- body = {"instance": {
- "name": name,
- "flavorRef": flavor_id
- }}
- if volume:
- body["instance"]["volume"] = volume
- if databases:
- body["instance"]["databases"] = databases
- if users:
- body["instance"]["users"] = users
- if restorePoint:
- body["instance"]["restorePoint"] = restorePoint
-
- return self._create("/instances", body, "instance")
-
- def _list(self, url, response_key, limit=None, marker=None):
- resp, body = self.api.client.get(limit_url(url, limit, marker))
- if not body:
- raise Exception("Call to " + url + " did not return a body.")
- links = body.get('links', [])
- next_links = [link['href'] for link in links if link['rel'] == 'next']
- next_marker = None
- for link in next_links:
- # Extract the marker from the url.
- parsed_url = urlparse.urlparse(link)
- query_dict = dict(urlparse.parse_qsl(parsed_url.query))
- next_marker = query_dict.get('marker', None)
- instances = body[response_key]
- instances = [self.resource_class(self, res) for res in instances]
- return Paginated(instances, next_marker=next_marker, links=links)
-
- def list(self, limit=None, marker=None):
- """
- Get a list of all instances.
-
- :rtype: list of :class:`Instance`.
- """
- return self._list("/instances", "instances", limit, marker)
-
- def get(self, instance):
- """
- Get a specific instances.
-
- :rtype: :class:`Instance`
- """
- return self._get("/instances/%s" % base.getid(instance),
- "instance")
-
- def backups(self, instance):
- """
- Get the list of backups for a specific instance.
-
- :rtype: list of :class:`Backups`.
- """
- return self._list("/instances/%s/backups" % base.getid(instance),
- "backups")
-
- def delete(self, instance):
- """
- Delete the specified instance.
-
- :param instance_id: The instance id to delete
- """
- resp, body = self.api.client.delete("/instances/%s" %
- base.getid(instance))
- if resp.status in (422, 500):
- raise exceptions.from_response(resp, body)
-
- def _action(self, instance_id, body):
- """
- Perform a server "action" -- reboot/rebuild/resize/etc.
- """
- url = "/instances/%s/action" % instance_id
- resp, body = self.api.client.post(url, body=body)
- check_for_exceptions(resp, body)
- if body:
- return self.resource_class(self, body, loaded=True)
- return body
-
- def resize_volume(self, instance_id, volume_size):
- """
- Resize the volume on an existing instances
- """
- body = {"resize": {"volume": {"size": volume_size}}}
- self._action(instance_id, body)
-
- def resize_instance(self, instance_id, flavor_id):
- """
- Resize the volume on an existing instances
- """
- body = {"resize": {"flavorRef": flavor_id}}
- self._action(instance_id, body)
-
- def restart(self, instance_id):
- """
- Restart the database instance.
-
- :param instance_id: The :class:`Instance` (or its ID) to share onto.
- """
- body = {'restart': {}}
- self._action(instance_id, body)
-
- def reset_password(self, instance_id):
- """
- Resets the database instance root password.
-
- :param instance_id: The :class:`Instance` (or its ID) to share onto.
- """
- body = {'reset-password': {}}
- return self._action(instance_id, body)
-
-Instances.resize_flavor = Instances.resize_instance
-
-
-class InstanceStatus(object):
-
- ACTIVE = "ACTIVE"
- BLOCKED = "BLOCKED"
- BUILD = "BUILD"
- FAILED = "FAILED"
- REBOOT = "REBOOT"
- RESIZE = "RESIZE"
- SHUTDOWN = "SHUTDOWN"
diff --git a/reddwarfclient/limits.py b/reddwarfclient/limits.py
deleted file mode 100644
index f2b8703..0000000
--- a/reddwarfclient/limits.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (c) 2013 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.
-
-from reddwarfclient import base
-import exceptions
-
-
-class Limit(base.Resource):
-
- def __repr__(self):
- return "<Limit: %s>" % self.verb
-
-
-class Limits(base.ManagerWithFind):
- """
- Manages :class `Limit` resources
- """
- resource_class = Limit
-
- def __repr__(self):
- return "<Limit Manager at %s>" % id(self)
-
- def _list(self, url, response_key):
- resp, body = self.api.client.get(url)
-
- if resp is None or resp.status != 200:
- raise exceptions.from_response(resp, body)
-
- if not body:
- raise Exception("Call to " + url + " did not return a body.")
-
- return [self.resource_class(self, res) for res in body[response_key]]
-
- def list(self):
- """
- Retrieve the limits
- """
- return self._list("/limits", "limits")
diff --git a/reddwarfclient/management.py b/reddwarfclient/management.py
deleted file mode 100644
index 931c0d5..0000000
--- a/reddwarfclient/management.py
+++ /dev/null
@@ -1,136 +0,0 @@
-# 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.
-
-from reddwarfclient import base
-import urlparse
-
-from reddwarfclient.common import check_for_exceptions
-from reddwarfclient.common import limit_url
-from reddwarfclient.common import Paginated
-from reddwarfclient.instances import Instance
-
-
-class RootHistory(base.Resource):
- def __repr__(self):
- return ("<Root History: Instance %s enabled at %s by %s>"
- % (self.id, self.created, self.user))
-
-
-class Management(base.ManagerWithFind):
- """
- Manage :class:`Instances` resources.
- """
- resource_class = Instance
-
- def _list(self, url, response_key, limit=None, marker=None):
- resp, body = self.api.client.get(limit_url(url, limit, marker))
- if not body:
- raise Exception("Call to " + url + " did not return a body.")
- links = body.get('links', [])
- next_links = [link['href'] for link in links if link['rel'] == 'next']
- next_marker = None
- for link in next_links:
- # Extract the marker from the url.
- parsed_url = urlparse.urlparse(link)
- query_dict = dict(urlparse.parse_qsl(parsed_url.query))
- next_marker = query_dict.get('marker', None)
- instances = body[response_key]
- instances = [self.resource_class(self, res) for res in instances]
- return Paginated(instances, next_marker=next_marker, links=links)
-
- def show(self, instance):
- """
- Get details of one instance.
-
- :rtype: :class:`Instance`.
- """
-
- return self._get("/mgmt/instances/%s" % base.getid(instance),
- 'instance')
-
- def index(self, deleted=None, limit=None, marker=None):
- """
- Show an overview of all local instances.
- Optionally, filter by deleted status.
-
- :rtype: list of :class:`Instance`.
- """
- form = ''
- if deleted is not None:
- if deleted:
- form = "?deleted=true"
- else:
- form = "?deleted=false"
-
- url = "/mgmt/instances%s" % form
- return self._list(url, "instances", limit, marker)
-
- def root_enabled_history(self, instance):
- """
- Get root access history of one instance.
-
- """
- url = "/mgmt/instances/%s/root" % base.getid(instance)
- resp, body = self.api.client.get(url)
- if not body:
- raise Exception("Call to " + url + " did not return a body.")
- return RootHistory(self, body['root_history'])
-
- def _action(self, instance_id, body):
- """
- Perform a server "action" -- reboot/rebuild/resize/etc.
- """
- url = "/mgmt/instances/%s/action" % instance_id
- resp, body = self.api.client.post(url, body=body)
- check_for_exceptions(resp, body)
-
- def stop(self, instance_id):
- body = {'stop': {}}
- self._action(instance_id, body)
-
- def reboot(self, instance_id):
- """
- Reboot the underlying OS.
-
- :param instance_id: The :class:`Instance` (or its ID) to share onto.
- """
- body = {'reboot': {}}
- self._action(instance_id, body)
-
- def migrate(self, instance_id, host=None):
- """
- Migrate the instance.
-
- :param instance_id: The :class:`Instance` (or its ID) to share onto.
- """
- if host:
- body = {'migrate': {'host': host}}
- else:
- body = {'migrate': {}}
- self._action(instance_id, body)
-
- def update(self, instance_id):
- """
- Update the guest agent via apt-get.
- """
- body = {'update': {}}
- self._action(instance_id, body)
-
- def reset_task_status(self, instance_id):
- """
- Set the task status to NONE.
- """
- body = {'reset-task-status': {}}
- self._action(instance_id, body)
diff --git a/reddwarfclient/mcli.py b/reddwarfclient/mcli.py
deleted file mode 100644
index 62371cc..0000000
--- a/reddwarfclient/mcli.py
+++ /dev/null
@@ -1,246 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright 2011 OpenStack LLC
-#
-# 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.
-
-"""
-Reddwarf Management Command line tool
-"""
-
-import json
-import optparse
-import os
-import sys
-
-
-# If ../reddwarf/__init__.py exists, add ../ to Python search path, so that
-# it will override what happens to be installed in /usr/(local/)lib/python...
-possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
- os.pardir,
- os.pardir))
-if os.path.exists(os.path.join(possible_topdir, 'reddwarfclient',
- '__init__.py')):
- sys.path.insert(0, possible_topdir)
-
-
-from reddwarfclient import common
-
-
-oparser = None
-
-
-def _pretty_print(info):
- print json.dumps(info, sort_keys=True, indent=4)
-
-
-class HostCommands(common.AuthedCommandsBase):
- """Commands to list info on hosts"""
-
- params = [
- 'name',
- ]
-
- def update_all(self):
- """Update all instances on a host"""
- self._require('name')
- self.dbaas.hosts.update_all(self.name)
-
- def get(self):
- """List details for the specified host"""
- self._require('name')
- self._pretty_print(self.dbaas.hosts.get, self.name)
-
- def list(self):
- """List all compute hosts"""
- self._pretty_list(self.dbaas.hosts.index)
-
-
-class QuotaCommands(common.AuthedCommandsBase):
- """List and update quota limits for a tenant."""
-
- params = ['id',
- 'instances',
- 'volumes',
- 'backups']
-
- def list(self):
- """List all quotas for a tenant"""
- self._require('id')
- self._pretty_print(self.dbaas.quota.show, self.id)
-
- def update(self):
- """Update quota limits for a tenant"""
- self._require('id')
- self._pretty_print(self.dbaas.quota.update, self.id,
- dict((param, getattr(self, param))
- for param in self.params if param != 'id'))
-
-
-class RootCommands(common.AuthedCommandsBase):
- """List details about the root info for an instance."""
-
- params = [
- 'id',
- ]
-
- def history(self):
- """List root history for the instance."""
- self._require('id')
- self._pretty_print(self.dbaas.management.root_enabled_history, self.id)
-
-
-class AccountCommands(common.AuthedCommandsBase):
- """Commands to list account info"""
-
- params = [
- 'id',
- ]
-
- def list(self):
- """List all accounts with non-deleted instances"""
- self._pretty_print(self.dbaas.accounts.index)
-
- def get(self):
- """List details for the account provided"""
- self._require('id')
- self._pretty_print(self.dbaas.accounts.show, self.id)
-
-
-class InstanceCommands(common.AuthedCommandsBase):
- """List details about an instance."""
-
- params = [
- 'deleted',
- 'id',
- 'limit',
- 'marker',
- 'host',
- ]
-
- def get(self):
- """List details for the instance."""
- self._require('id')
- self._pretty_print(self.dbaas.management.show, self.id)
-
- def list(self):
- """List all instances for account"""
- deleted = None
- if self.deleted is not None:
- if self.deleted.lower() in ['true']:
- deleted = True
- elif self.deleted.lower() in ['false']:
- deleted = False
- self._pretty_paged(self.dbaas.management.index, deleted=deleted)
-
- def hwinfo(self):
- """Show hardware information details about an instance."""
- self._require('id')
- self._pretty_print(self.dbaas.hwinfo.get, self.id)
-
- def diagnostic(self):
- """List diagnostic details about an instance."""
- self._require('id')
- self._pretty_print(self.dbaas.diagnostics.get, self.id)
-
- def stop(self):
- """Stop MySQL on the given instance."""
- self._require('id')
- self._pretty_print(self.dbaas.management.stop, self.id)
-
- def reboot(self):
- """Reboot the instance."""
- self._require('id')
- self._pretty_print(self.dbaas.management.reboot, self.id)
-
- def migrate(self):
- """Migrate the instance."""
- self._require('id')
- self._pretty_print(self.dbaas.management.migrate, self.id, self.host)
-
- def reset_task_status(self):
- """Set the instance's task status to NONE."""
- self._require('id')
- self._pretty_print(self.dbaas.management.reset_task_status, self.id)
-
-
-class StorageCommands(common.AuthedCommandsBase):
- """Commands to list devices info"""
-
- params = []
-
- def list(self):
- """List details for the storage device"""
- self._pretty_list(self.dbaas.storage.index)
-
-
-def config_options(oparser):
- oparser.add_option("-u", "--url", default="http://localhost:5000/v1.1",
- help="Auth API endpoint URL with port and version. \
- Default: http://localhost:5000/v1.1")
-
-
-COMMANDS = {'account': AccountCommands,
- 'host': HostCommands,
- 'instance': InstanceCommands,
- 'root': RootCommands,
- 'storage': StorageCommands,
- 'quota': QuotaCommands,
- }
-
-
-def main():
- # Parse arguments
- oparser = common.CliOptions.create_optparser(True)
- for k, v in COMMANDS.items():
- v._prepare_parser(oparser)
- (options, args) = oparser.parse_args()
-
- if not args:
- common.print_commands(COMMANDS)
-
- # Pop the command and check if it's in the known commands
- cmd = args.pop(0)
- if cmd in COMMANDS:
- fn = COMMANDS.get(cmd)
- command_object = None
- try:
- command_object = fn(oparser)
- except Exception as ex:
- if options.debug:
- raise
- print(ex)
-
- # Get a list of supported actions for the command
- actions = common.methods_of(command_object)
-
- if len(args) < 1:
- common.print_actions(cmd, actions)
-
- # Check for a valid action and perform that action
- action = args.pop(0)
- if action in actions:
- try:
- getattr(command_object, action)()
- except Exception as ex:
- if options.debug:
- raise
- print ex
- else:
- common.print_actions(cmd, actions)
- else:
- common.print_commands(COMMANDS)
-
-
-if __name__ == '__main__':
- main()
diff --git a/reddwarfclient/quota.py b/reddwarfclient/quota.py
deleted file mode 100644
index e5a8f74..0000000
--- a/reddwarfclient/quota.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# 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.
-
-from reddwarfclient import base
-from reddwarfclient.common import check_for_exceptions
-
-
-class Quotas(base.ManagerWithFind):
- """
- Manage :class:`Quota` information.
- """
-
- resource_class = base.Resource
-
- def show(self, tenant_id):
- """Get a list of all quotas for a tenant id"""
-
- url = "/mgmt/quotas/%s" % tenant_id
- resp, body = self.api.client.get(url)
- check_for_exceptions(resp, body)
- if not body:
- raise Exception("Call to " + url + " did not return a body.")
- if 'quotas' not in body:
- raise Exception("Missing key value 'quotas' in response body.")
- return body['quotas']
-
- def update(self, id, quotas):
- """
- Set limits for quotas
- """
- url = "/mgmt/quotas/%s" % id
- body = {"quotas": quotas}
- resp, body = self.api.client.put(url, body=body)
- check_for_exceptions(resp, body)
- if not body:
- raise Exception("Call to " + url + " did not return a body.")
- if 'quotas' not in body:
- raise Exception("Missing key value 'quotas' in response body.")
- return body['quotas']
diff --git a/reddwarfclient/root.py b/reddwarfclient/root.py
deleted file mode 100644
index 33b0da7..0000000
--- a/reddwarfclient/root.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# 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.
-
-from reddwarfclient import base
-
-from reddwarfclient import users
-from reddwarfclient.common import check_for_exceptions
-import exceptions
-
-
-class Root(base.ManagerWithFind):
- """
- Manager class for Root resource
- """
- resource_class = users.User
- url = "/instances/%s/root"
-
- def create(self, instance_id):
- """
- Enable the root user and return the root password for the
- sepcified db instance
- """
- resp, body = self.api.client.post(self.url % instance_id)
- check_for_exceptions(resp, body)
- return body['user']['name'], body['user']['password']
-
- def is_root_enabled(self, instance_id):
- """ Return True if root is enabled for the instance;
- False otherwise"""
- resp, body = self.api.client.get(self.url % instance_id)
- check_for_exceptions(resp, body)
- return body['rootEnabled']
diff --git a/reddwarfclient/security_groups.py b/reddwarfclient/security_groups.py
deleted file mode 100644
index d26cc86..0000000
--- a/reddwarfclient/security_groups.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# Copyright 2013 Hewlett-Packard Development Company, L.P.
-# 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.
-#
-
-from reddwarfclient import base
-
-import exceptions
-import urlparse
-
-from reddwarfclient.common import limit_url
-from reddwarfclient.common import Paginated
-
-
-class SecurityGroup(base.Resource):
- """
- Security Group is a resource used to hold security group information.
- """
- def __repr__(self):
- return "<SecurityGroup: %s>" % self.name
-
-
-class SecurityGroups(base.ManagerWithFind):
- """
- Manage :class:`SecurityGroup` resources.
- """
- resource_class = SecurityGroup
-
- def _list(self, url, response_key, limit=None, marker=None):
- resp, body = self.api.client.get(limit_url(url, limit, marker))
- if not body:
- raise Exception("Call to " + url + " did not return a body.")
- links = body.get('links', [])
- next_links = [link['href'] for link in links if link['rel'] == 'next']
- next_marker = None
- for link in next_links:
- # Extract the marker from the url.
- parsed_url = urlparse.urlparse(link)
- query_dict = dict(urlparse.parse_qsl(parsed_url.query))
- next_marker = query_dict.get('marker', None)
- instances = body[response_key]
- instances = [self.resource_class(self, res) for res in instances]
- return Paginated(instances, next_marker=next_marker, links=links)
-
- def list(self, limit=None, marker=None):
- """
- Get a list of all security groups.
-
- :rtype: list of :class:`SecurityGroup`.
- """
- return self._list("/security-groups", "security_groups", limit,
- marker)
-
- def get(self, security_group):
- """
- Get a specific security group.
-
- :rtype: :class:`SecurityGroup`
- """
- return self._get("/security-groups/%s" % base.getid(security_group),
- "security_group")
-
-
-class SecurityGroupRule(base.Resource):
- """
- Security Group Rule is a resource used to hold security group
- rule related information.
- """
- def __repr__(self):
- return \
- "<SecurityGroupRule: ( \
- Security Group id: %d, \
- Protocol: %s, \
- From_Port: %d, \
- To_Port: %d, \
- CIDR: %s )>" % (self.group_id, self.protocol, self.from_port,
- self.to_port, self.cidr)
-
-
-class SecurityGroupRules(base.ManagerWithFind):
- """
- Manage :class:`SecurityGroupRules` resources.
- """
- resource_class = SecurityGroupRule
-
- def create(self, group_id, protocol, from_port, to_port, cidr):
- """
- Create a new security group rule.
- """
- body = {"security_group_rule": {
- "group_id": group_id,
- "protocol": protocol,
- "from_port": from_port,
- "to_port": to_port,
- "cidr": cidr
- }}
- return self._create("/security-group-rules", body,
- "security_group_rule")
-
- def delete(self, security_group_rule):
- """
- Delete the specified security group rule.
-
- :param security_group_rule: The security group rule to delete
- """
- resp, body = self.api.client.delete("/security-group-rules/%s" %
- base.getid(security_group_rule))
- if resp.status in (422, 500):
- raise exceptions.from_response(resp, body)
diff --git a/reddwarfclient/storage.py b/reddwarfclient/storage.py
deleted file mode 100644
index 653096e..0000000
--- a/reddwarfclient/storage.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# 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.
-
-from reddwarfclient import base
-
-
-class Device(base.Resource):
- """
- Storage is an opaque instance used to hold storage information.
- """
- def __repr__(self):
- return "<Device: %s>" % self.name
-
-
-class StorageInfo(base.ManagerWithFind):
- """
- Manage :class:`Storage` resources.
- """
- resource_class = Device
-
- def _list(self, url, response_key):
- resp, body = self.api.client.get(url)
- if not body:
- raise Exception("Call to " + url + " did not return a body.")
- return [self.resource_class(self, res) for res in body[response_key]]
-
- def index(self):
- """
- Get a list of all storages.
-
- :rtype: list of :class:`Storages`.
- """
- return self._list("/mgmt/storage", "devices")
diff --git a/reddwarfclient/tests/test_accounts.py b/reddwarfclient/tests/test_accounts.py
deleted file mode 100644
index ec76f13..0000000
--- a/reddwarfclient/tests/test_accounts.py
+++ /dev/null
@@ -1,84 +0,0 @@
-from testtools import TestCase
-from mock import Mock
-
-from reddwarfclient import accounts
-from reddwarfclient import base
-
-"""
-Unit tests for accounts.py
-"""
-
-
-class AccountTest(TestCase):
-
- def setUp(self):
- super(AccountTest, self).setUp()
- self.orig__init = accounts.Account.__init__
- accounts.Account.__init__ = Mock(return_value=None)
- self.account = accounts.Account()
-
- def tearDown(self):
- super(AccountTest, self).tearDown()
- accounts.Account.__init__ = self.orig__init
-
- def test___repr__(self):
- self.account.name = "account-1"
- self.assertEqual('<Account: account-1>', self.account.__repr__())
-
-
-class AccountsTest(TestCase):
-
- def setUp(self):
- super(AccountsTest, self).setUp()
- self.orig__init = accounts.Accounts.__init__
- accounts.Accounts.__init__ = Mock(return_value=None)
- self.accounts = accounts.Accounts()
- self.accounts.api = Mock()
- self.accounts.api.client = Mock()
-
- def tearDown(self):
- super(AccountsTest, self).tearDown()
- accounts.Accounts.__init__ = self.orig__init
-
- def test__list(self):
- def side_effect_func(self, val):
- return val
-
- self.accounts.resource_class = Mock(side_effect=side_effect_func)
- key_ = 'key'
- body_ = {key_: "test-value"}
- self.accounts.api.client.get = Mock(return_value=('resp', body_))
- self.assertEqual("test-value", self.accounts._list('url', key_))
-
- self.accounts.api.client.get = Mock(return_value=('resp', None))
- self.assertRaises(Exception, self.accounts._list, 'url', None)
-
- def test_index(self):
- resp = Mock()
- resp.status = 400
- body = {"Accounts": {}}
- self.accounts.api.client.get = Mock(return_value=(resp, body))
- self.assertRaises(Exception, self.accounts.index)
- resp.status = 200
- self.assertTrue(isinstance(self.accounts.index(), base.Resource))
- self.accounts.api.client.get = Mock(return_value=(resp, None))
- self.assertRaises(Exception, self.accounts.index)
-
- def test_show(self):
- def side_effect_func(acct_name, acct):
- return acct_name, acct
-
- account_ = Mock()
- account_.name = "test-account"
- self.accounts._list = Mock(side_effect=side_effect_func)
- self.assertEqual(('/mgmt/accounts/test-account', 'account'),
- self.accounts.show(account_))
-
- def test__get_account_name(self):
- account_ = 'account with no name'
- self.assertEqual(account_,
- accounts.Accounts._get_account_name(account_))
- account_ = Mock()
- account_.name = "account-name"
- self.assertEqual("account-name",
- accounts.Accounts._get_account_name(account_))
diff --git a/reddwarfclient/tests/test_auth.py b/reddwarfclient/tests/test_auth.py
deleted file mode 100644
index f1c4b59..0000000
--- a/reddwarfclient/tests/test_auth.py
+++ /dev/null
@@ -1,414 +0,0 @@
-import contextlib
-
-from testtools import TestCase
-from reddwarfclient import auth
-from mock import Mock
-
-from reddwarfclient import exceptions
-
-"""
-Unit tests for the classes and functions in auth.py.
-"""
-
-
-def check_url_none(test_case, auth_class):
- # url is None, it must throw exception
- authObj = auth_class(url=None, type=auth_class, client=None,
- username=None, password=None, tenant=None)
- try:
- authObj.authenticate()
- test_case.fail("AuthUrlNotGiven exception expected")
- except exceptions.AuthUrlNotGiven:
- pass
-
-
-class AuthenticatorTest(TestCase):
-
- def setUp(self):
- super(AuthenticatorTest, self).setUp()
- self.orig_load = auth.ServiceCatalog._load
- self.orig__init = auth.ServiceCatalog.__init__
-
- def tearDown(self):
- super(AuthenticatorTest, self).tearDown()
- auth.ServiceCatalog._load = self.orig_load
- auth.ServiceCatalog.__init__ = self.orig__init
-
- def test_get_authenticator_cls(self):
- class_list = (auth.KeyStoneV2Authenticator,
- auth.RaxAuthenticator,
- auth.Auth1_1,
- auth.FakeAuth)
-
- for c in class_list:
- self.assertEqual(c, auth.get_authenticator_cls(c))
-
- class_names = {"keystone": auth.KeyStoneV2Authenticator,
- "rax": auth.RaxAuthenticator,
- "auth1.1": auth.Auth1_1,
- "fake": auth.FakeAuth}
-
- for cn in class_names.keys():
- self.assertEqual(class_names[cn], auth.get_authenticator_cls(cn))
-
- cls_or_name = "_unknown_"
- self.assertRaises(ValueError, auth.get_authenticator_cls, cls_or_name)
-
- def test__authenticate(self):
- authObj = auth.Authenticator(Mock(), auth.KeyStoneV2Authenticator,
- Mock(), Mock(), Mock(), Mock())
- # test response code 200
- resp = Mock()
- resp.status = 200
- body = "test_body"
-
- auth.ServiceCatalog._load = Mock(return_value=1)
- authObj.client._time_request = Mock(return_value=(resp, body))
-
- sc = authObj._authenticate(Mock(), Mock())
- self.assertEqual(body, sc.catalog)
-
- # test AmbiguousEndpoints exception
- auth.ServiceCatalog.__init__ = \
- Mock(side_effect=exceptions.AmbiguousEndpoints)
- self.assertRaises(exceptions.AmbiguousEndpoints,
- authObj._authenticate, Mock(), Mock())
-
- # test handling KeyError and raising AuthorizationFailure exception
- auth.ServiceCatalog.__init__ = Mock(side_effect=KeyError)
- self.assertRaises(exceptions.AuthorizationFailure,
- authObj._authenticate, Mock(), Mock())
-
- # test EndpointNotFound exception
- mock = Mock(side_effect=exceptions.EndpointNotFound)
- auth.ServiceCatalog.__init__ = mock
- self.assertRaises(exceptions.EndpointNotFound,
- authObj._authenticate, Mock(), Mock())
- mock.side_effect = None
-
- # test response code 305
- resp.__getitem__ = Mock(return_value='loc')
- resp.status = 305
- body = "test_body"
- authObj.client._time_request = Mock(return_value=(resp, body))
-
- l = authObj._authenticate(Mock(), Mock())
- self.assertEqual('loc', l)
-
- # test any response code other than 200 and 305
- resp.status = 404
- exceptions.from_response = Mock(side_effect=ValueError)
- self.assertRaises(ValueError, authObj._authenticate, Mock(), Mock())
-
- def test_authenticate(self):
- authObj = auth.Authenticator(Mock(), auth.KeyStoneV2Authenticator,
- Mock(), Mock(), Mock(), Mock())
- self.assertRaises(NotImplementedError, authObj.authenticate)
-
-
-class KeyStoneV2AuthenticatorTest(TestCase):
-
- def test_authenticate(self):
- # url is None
- check_url_none(self, auth.KeyStoneV2Authenticator)
-
- # url is not None, so it must not throw exception
- url = "test_url"
- cls_type = auth.KeyStoneV2Authenticator
- authObj = auth.KeyStoneV2Authenticator(url=url, type=cls_type,
- client=None, username=None,
- password=None, tenant=None)
-
- def side_effect_func(url):
- return url
-
- mock = Mock()
- mock.side_effect = side_effect_func
- authObj._v2_auth = mock
- r = authObj.authenticate()
- self.assertEqual(url, r)
-
- def test__v2_auth(self):
- username = "reddwarf_user"
- password = "reddwarf_password"
- tenant = "tenant"
- cls_type = auth.KeyStoneV2Authenticator
- authObj = auth.KeyStoneV2Authenticator(url=None, type=cls_type,
- client=None,
- username=username,
- password=password,
- tenant=tenant)
-
- def side_effect_func(url, body):
- return body
- mock = Mock()
- mock.side_effect = side_effect_func
- authObj._authenticate = mock
- body = authObj._v2_auth(Mock())
- self.assertEqual(username,
- body['auth']['passwordCredentials']['username'])
- self.assertEqual(password,
- body['auth']['passwordCredentials']['password'])
- self.assertEqual(tenant, body['auth']['tenantName'])
-
-
-class Auth1_1Test(TestCase):
-
- def test_authenticate(self):
- # handle when url is None
- check_url_none(self, auth.Auth1_1)
-
- # url is not none
- username = "reddwarf_user"
- password = "reddwarf_password"
- url = "test_url"
- authObj = auth.Auth1_1(url=url,
- type=auth.Auth1_1,
- client=None, username=username,
- password=password, tenant=None)
-
- def side_effect_func(auth_url, body, root_key):
- return auth_url, body, root_key
-
- mock = Mock()
- mock.side_effect = side_effect_func
- authObj._authenticate = mock
- auth_url, body, root_key = authObj.authenticate()
-
- self.assertEqual(username, body['credentials']['username'])
- self.assertEqual(password, body['credentials']['key'])
- self.assertEqual(auth_url, url)
- self.assertEqual('auth', root_key)
-
-
-class RaxAuthenticatorTest(TestCase):
-
- def test_authenticate(self):
- # url is None
- check_url_none(self, auth.RaxAuthenticator)
-
- # url is not None, so it must not throw exception
- url = "test_url"
- authObj = auth.RaxAuthenticator(url=url,
- type=auth.RaxAuthenticator,
- client=None, username=None,
- password=None, tenant=None)
-
- def side_effect_func(url):
- return url
-
- mock = Mock()
- mock.side_effect = side_effect_func
- authObj._rax_auth = mock
- r = authObj.authenticate()
- self.assertEqual(url, r)
-
- def test__rax_auth(self):
- username = "reddwarf_user"
- password = "reddwarf_password"
- tenant = "tenant"
- authObj = auth.RaxAuthenticator(url=None,
- type=auth.RaxAuthenticator,
- client=None, username=username,
- password=password, tenant=tenant)
-
- def side_effect_func(url, body):
- return body
-
- mock = Mock()
- mock.side_effect = side_effect_func
- authObj._authenticate = mock
- body = authObj._rax_auth(Mock())
-
- v = body['auth']['RAX-KSKEY:apiKeyCredentials']['username']
- self.assertEqual(username, v)
-
- v = body['auth']['RAX-KSKEY:apiKeyCredentials']['apiKey']
- self.assertEqual(password, v)
-
- v = body['auth']['RAX-KSKEY:apiKeyCredentials']['tenantName']
- self.assertEqual(tenant, v)
-
-
-class FakeAuthTest(TestCase):
-
- def test_authenticate(self):
- tenant = "tenant"
- authObj = auth.FakeAuth(url=None,
- type=auth.FakeAuth,
- client=None, username=None,
- password=None, tenant=tenant)
-
- fc = authObj.authenticate()
- public_url = "%s/%s" % ('http://localhost:8779/v1.0', tenant)
- self.assertEqual(public_url, fc.get_public_url())
- self.assertEqual(tenant, fc.get_token())
-
-
-class ServiceCatalogTest(TestCase):
-
- def setUp(self):
- super(ServiceCatalogTest, self).setUp()
- self.orig_url_for = auth.ServiceCatalog._url_for
- self.orig__init__ = auth.ServiceCatalog.__init__
- auth.ServiceCatalog.__init__ = Mock(return_value=None)
- self.test_url = "http://localhost:1234/test"
-
- def tearDown(self):
- super(ServiceCatalogTest, self).tearDown()
- auth.ServiceCatalog._url_for = self.orig_url_for
- auth.ServiceCatalog.__init__ = self.orig__init__
-
- def test__load(self):
- url = "random_url"
- auth.ServiceCatalog._url_for = Mock(return_value=url)
-
- # when service_url is None
- scObj = auth.ServiceCatalog()
- scObj.region = None
- scObj.service_url = None
- scObj._load()
- self.assertEqual(url, scObj.public_url)
- self.assertEqual(url, scObj.management_url)
-
- # service url is not None
- service_url = "service_url"
- scObj = auth.ServiceCatalog()
- scObj.region = None
- scObj.service_url = service_url
- scObj._load()
- self.assertEqual(service_url, scObj.public_url)
- self.assertEqual(service_url, scObj.management_url)
-
- def test_get_token(self):
- test_id = "test_id"
- scObj = auth.ServiceCatalog()
- scObj.root_key = "root_key"
- scObj.catalog = dict()
- scObj.catalog[scObj.root_key] = dict()
- scObj.catalog[scObj.root_key]['token'] = dict()
- scObj.catalog[scObj.root_key]['token']['id'] = test_id
- self.assertEqual(test_id, scObj.get_token())
-
- def test_get_management_url(self):
- test_mng_url = "test_management_url"
- scObj = auth.ServiceCatalog()
- scObj.management_url = test_mng_url
- self.assertEqual(test_mng_url, scObj.get_management_url())
-
- def test_get_public_url(self):
- test_public_url = "test_public_url"
- scObj = auth.ServiceCatalog()
- scObj.public_url = test_public_url
- self.assertEqual(test_public_url, scObj.get_public_url())
-
- def test__url_for(self):
- scObj = auth.ServiceCatalog()
-
- # case for no endpoint found
- self.case_no_endpoint_match(scObj)
-
- # case for empty service catalog
- self.case_endpoing_with_empty_catalog(scObj)
-
- # more than one matching endpoints
- self.case_ambiguous_endpoint(scObj)
-
- # happy case
- self.case_unique_endpoint(scObj)
-
- # testing if-statements in for-loop to iterate services in catalog
- self.case_iterating_services_in_catalog(scObj)
-
- def case_no_endpoint_match(self, scObj):
- # empty endpoint list
- scObj.catalog = dict()
- scObj.catalog['endpoints'] = list()
- self.assertRaises(exceptions.EndpointNotFound, scObj._url_for)
-
- def side_effect_func_ep(attr):
- return "test_attr_value"
-
- # simulating dict
- endpoint = Mock()
- mock = Mock()
- mock.side_effect = side_effect_func_ep
- endpoint.__getitem__ = mock
- scObj.catalog['endpoints'].append(endpoint)
-
- # not-empty list but not matching endpoint
- filter_value = "not_matching_value"
- self.assertRaises(exceptions.EndpointNotFound, scObj._url_for,
- attr="test_attr", filter_value=filter_value)
-
- filter_value = "test_attr_value" # so that we have an endpoint match
- scObj.root_key = "access"
- scObj.catalog[scObj.root_key] = dict()
- self.assertRaises(exceptions.EndpointNotFound, scObj._url_for,
- attr="test_attr", filter_value=filter_value)
-
- def case_endpoing_with_empty_catalog(self, scObj):
- # first, test with empty catalog, this should pass since
- # there is already enpoint added
- scObj.catalog[scObj.root_key]['serviceCatalog'] = list()
-
- endpoint = scObj.catalog['endpoints'][0]
- endpoint.get = Mock(return_value=self.test_url)
- r_url = scObj._url_for(attr="test_attr",
- filter_value="test_attr_value")
- self.assertEqual(self.test_url, r_url)
-
- def case_ambiguous_endpoint(self, scObj):
- scObj.service_type = "reddwarf"
- scObj.service_name = "test_service_name"
-
- def side_effect_func_service(key):
- if key == "type":
- return "reddwarf"
- elif key == "name":
- return "test_service_name"
- return None
-
- mock1 = Mock()
- mock1.side_effect = side_effect_func_service
- service1 = Mock()
- service1.get = mock1
-
- endpoint2 = {"test_attr": "test_attr_value"}
- service1.__getitem__ = Mock(return_value=[endpoint2])
- scObj.catalog[scObj.root_key]['serviceCatalog'] = [service1]
- self.assertRaises(exceptions.AmbiguousEndpoints, scObj._url_for,
- attr="test_attr", filter_value="test_attr_value")
-
- def case_unique_endpoint(self, scObj):
- # changing the endpoint2 attribute to pass the filter
- service1 = scObj.catalog[scObj.root_key]['serviceCatalog'][0]
- endpoint2 = service1[0][0]
- endpoint2["test_attr"] = "new value not matching filter"
- r_url = scObj._url_for(attr="test_attr",
- filter_value="test_attr_value")
- self.assertEqual(self.test_url, r_url)
-
- def case_iterating_services_in_catalog(self, scObj):
- service1 = scObj.catalog[scObj.root_key]['serviceCatalog'][0]
-
- scObj.catalog = dict()
- scObj.root_key = "access"
- scObj.catalog[scObj.root_key] = dict()
- scObj.service_type = "no_match"
-
- scObj.catalog[scObj.root_key]['serviceCatalog'] = [service1]
- self.assertRaises(exceptions.EndpointNotFound, scObj._url_for)
-
- scObj.service_type = "database"
- scObj.service_name = "no_match"
- self.assertRaises(exceptions.EndpointNotFound, scObj._url_for)
-
- # no endpoints and no 'serviceCatalog' in catalog => raise exception
- scObj = auth.ServiceCatalog()
- scObj.catalog = dict()
- scObj.root_key = "access"
- scObj.catalog[scObj.root_key] = dict()
- scObj.catalog[scObj.root_key]['serviceCatalog'] = []
- self.assertRaises(exceptions.EndpointNotFound, scObj._url_for,
- attr="test_attr", filter_value="test_attr_value")
diff --git a/reddwarfclient/tests/test_base.py b/reddwarfclient/tests/test_base.py
deleted file mode 100644
index 5cbd590..0000000
--- a/reddwarfclient/tests/test_base.py
+++ /dev/null
@@ -1,447 +0,0 @@
-import contextlib
-import os
-
-from testtools import TestCase
-from mock import Mock
-
-from reddwarfclient import base
-from reddwarfclient import exceptions
-from reddwarfclient import utils
-
-"""
-Unit tests for base.py
-"""
-
-
-def obj_class(self, res, loaded=True):
- return res
-
-
-class BaseTest(TestCase):
-
- def test_getid(self):
- obj = "test"
- r = base.getid(obj)
- self.assertEqual(obj, r)
-
- test_id = "test_id"
- obj = Mock()
- obj.id = test_id
- r = base.getid(obj)
- self.assertEqual(test_id, r)
-
-
-class ManagerTest(TestCase):
-
- def setUp(self):
- super(ManagerTest, self).setUp()
- self.orig__init = base.Manager.__init__
- base.Manager.__init__ = Mock(return_value=None)
- self.orig_os_makedirs = os.makedirs
-
- def tearDown(self):
- super(ManagerTest, self).tearDown()
- base.Manager.__init__ = self.orig__init
- os.makedirs = self.orig_os_makedirs
-
- def test___init__(self):
- api = Mock()
- base.Manager.__init__ = self.orig__init
- manager = base.Manager(api)
- self.assertEqual(api, manager.api)
-
- def test_completion_cache(self):
- manager = base.Manager()
-
- # handling exceptions
- mode = "w"
- cache_type = "unittest"
- obj_class = Mock
- with manager.completion_cache(cache_type, obj_class, mode):
- pass
-
- os.makedirs = Mock(side_effect=OSError)
- with manager.completion_cache(cache_type, obj_class, mode):
- pass
-
- def test_write_to_completion_cache(self):
- manager = base.Manager()
-
- # no cache object, nothing should happen
- manager.write_to_completion_cache("non-exist", "val")
-
- def side_effect_func(val):
- return val
-
- manager._mock_cache = Mock()
- manager._mock_cache.write = Mock(return_value=None)
- manager.write_to_completion_cache("mock", "val")
- self.assertEqual(1, manager._mock_cache.write.call_count)
-
- def _get_mock(self):
- manager = base.Manager()
- manager.api = Mock()
- manager.api.client = Mock()
-
- def side_effect_func(self, body, loaded=True):
- return body
-
- manager.resource_class = Mock(side_effect=side_effect_func)
- return manager
-
- def test__get_with_response_key_none(self):
- manager = self._get_mock()
- url_ = "test-url"
- body_ = "test-body"
- resp_ = "test-resp"
- manager.api.client.get = Mock(return_value=(resp_, body_))
- r = manager._get(url=url_, response_key=None)
- self.assertEqual(body_, r)
-
- def test__get_with_response_key(self):
- manager = self._get_mock()
- response_key = "response_key"
- body_ = {response_key: "test-resp-key-body"}
- url_ = "test_url_get"
- manager.api.client.get = Mock(return_value=(url_, body_))
- r = manager._get(url=url_, response_key=response_key)
- self.assertEqual(body_[response_key], r)
-
- def test__create(self):
- manager = base.Manager()
- manager.api = Mock()
- manager.api.client = Mock()
-
- response_key = "response_key"
- data_ = "test-data"
- body_ = {response_key: data_}
- url_ = "test_url_post"
- manager.api.client.post = Mock(return_value=(url_, body_))
-
- return_raw = True
- r = manager._create(url_, body_, response_key, return_raw)
- self.assertEqual(data_, r)
-
- return_raw = False
-
- @contextlib.contextmanager
- def completion_cache_mock(*arg, **kwargs):
- yield
-
- mock = Mock()
- mock.side_effect = completion_cache_mock
- manager.completion_cache = mock
-
- manager.resource_class = Mock(return_value="test-class")
- r = manager._create(url_, body_, response_key, return_raw)
- self.assertEqual("test-class", r)
-
- def get_mock_mng_api_client(self):
- manager = base.Manager()
- manager.api = Mock()
- manager.api.client = Mock()
- return manager
-
- def test__delete(self):
- resp_ = "test-resp"
- body_ = "test-body"
-
- manager = self.get_mock_mng_api_client()
- manager.api.client.delete = Mock(return_value=(resp_, body_))
- # _delete just calls api.client.delete, and does nothing
- # the correctness should be tested in api class
- manager._delete("test-url")
- pass
-
- def test__update(self):
- resp_ = "test-resp"
- body_ = "test-body"
-
- manager = self.get_mock_mng_api_client()
- manager.api.client.put = Mock(return_value=(resp_, body_))
- body = manager._update("test-url", body_)
- self.assertEqual(body_, body)
-
-
-class ManagerListTest(ManagerTest):
-
- def setUp(self):
- super(ManagerListTest, self).setUp()
-
- @contextlib.contextmanager
- def completion_cache_mock(*arg, **kwargs):
- yield
-
- self.manager = base.Manager()
- self.manager.api = Mock()
- self.manager.api.client = Mock()
-
- self.response_key = "response_key"
- self.data_p = ["p1", "p2"]
- self.body_p = {self.response_key: self.data_p}
- self.url_p = "test_url_post"
- self.manager.api.client.post = Mock(return_value=(self.url_p,
- self.body_p))
-
- self.data_g = ["g1", "g2", "g3"]
- self.body_g = {self.response_key: self.data_g}
- self.url_g = "test_url_get"
- self.manager.api.client.get = Mock(return_value=(self.url_g,
- self.body_g))
-
- mock = Mock()
- mock.side_effect = completion_cache_mock
- self.manager.completion_cache = mock
-
- def tearDown(self):
- super(ManagerListTest, self).tearDown()
-
- def obj_class(self, res, loaded=True):
- return res
-
- def test_list_with_body_none(self):
- body = None
- l = self.manager._list("url", self.response_key, obj_class, body)
- self.assertEqual(len(self.data_g), len(l))
- for i in range(0, len(l)):
- self.assertEqual(self.data_g[i], l[i])
-
- def test_list_body_not_none(self):
- body = "something"
- l = self.manager._list("url", self.response_key, obj_class, body)
- self.assertEqual(len(self.data_p), len(l))
- for i in range(0, len(l)):
- self.assertEqual(self.data_p[i], l[i])
-
- def test_list_key_mapping(self):
- data_ = {"values": ["p1", "p2"]}
- body_ = {self.response_key: data_}
- url_ = "test_url_post"
- self.manager.api.client.post = Mock(return_value=(url_, body_))
- l = self.manager._list("url", self.response_key,
- obj_class, "something")
- data = data_["values"]
- self.assertEqual(len(data), len(l))
- for i in range(0, len(l)):
- self.assertEqual(data[i], l[i])
-
- def test_list_without_key_mapping(self):
- data_ = {"v1": "1", "v2": "2"}
- body_ = {self.response_key: data_}
- url_ = "test_url_post"
- self.manager.api.client.post = Mock(return_value=(url_, body_))
- l = self.manager._list("url", self.response_key,
- obj_class, "something")
- self.assertEqual(len(data_), len(l))
-
-
-class ManagerWithFind(TestCase):
-
- def setUp(self):
- super(ManagerWithFind, self).setUp()
- self.orig__init = base.ManagerWithFind.__init__
- base.ManagerWithFind.__init__ = Mock(return_value=None)
- self.manager = base.ManagerWithFind()
-
- def tearDown(self):
- super(ManagerWithFind, self).tearDown()
- base.ManagerWithFind.__init__ = self.orig__init
-
- def test_find(self):
- obj1 = Mock()
- obj1.attr1 = "v1"
- obj1.attr2 = "v2"
- obj1.attr3 = "v3"
-
- obj2 = Mock()
- obj2.attr1 = "v1"
- obj2.attr2 = "v2"
-
- self.manager.list = Mock(return_value=[obj1, obj2])
- self.manager.resource_class = Mock
-
- # exactly one match case
- found = self.manager.find(attr1="v1", attr2="v2", attr3="v3")
- self.assertEqual(obj1, found)
-
- # no match case
- self.assertRaises(exceptions.NotFound, self.manager.find,
- attr1="v2", attr2="v2", attr3="v3")
-
- # multiple matches case
- obj2.attr3 = "v3"
- self.assertRaises(exceptions.NoUniqueMatch, self.manager.find,
- attr1="v1", attr2="v2", attr3="v3")
-
- def test_findall(self):
- obj1 = Mock()
- obj1.attr1 = "v1"
- obj1.attr2 = "v2"
- obj1.attr3 = "v3"
-
- obj2 = Mock()
- obj2.attr1 = "v1"
- obj2.attr2 = "v2"
-
- self.manager.list = Mock(return_value=[obj1, obj2])
-
- found = self.manager.findall(attr1="v1", attr2="v2", attr3="v3")
- self.assertEqual(1, len(found))
- self.assertEqual(obj1, found[0])
-
- found = self.manager.findall(attr1="v2", attr2="v2", attr3="v3")
- self.assertEqual(0, len(found))
-
- found = self.manager.findall(attr7="v1", attr2="v2")
- self.assertEqual(0, len(found))
-
- def test_list(self):
- # this method is not yet implemented, exception expected
- self.assertRaises(NotImplementedError, self.manager.list)
-
-
-class ResourceTest(TestCase):
-
- def setUp(self):
- super(ResourceTest, self).setUp()
- self.orig___init__ = base.Resource.__init__
-
- def tearDown(self):
- super(ResourceTest, self).tearDown()
- base.Resource.__init__ = self.orig___init__
-
- def test___init__(self):
- manager = Mock()
- manager.write_to_completion_cache = Mock(return_value=None)
-
- info_ = {}
- robj = base.Resource(manager, info_)
- self.assertEqual(0, manager.write_to_completion_cache.call_count)
-
- info_ = {"id": "id-with-less-than-36-char"}
- robj = base.Resource(manager, info_)
- self.assertEqual(info_["id"], robj.id)
- self.assertEqual(0, manager.write_to_completion_cache.call_count)
-
- id_ = "id-with-36-char-"
- for i in range(36 - len(id_)):
- id_ = id_ + "-"
- info_ = {"id": id_}
- robj = base.Resource(manager, info_)
- self.assertEqual(info_["id"], robj.id)
- self.assertEqual(1, manager.write_to_completion_cache.call_count)
-
- info_["name"] = "test-human-id"
- # Resource.HUMAN_ID is False
- robj = base.Resource(manager, info_)
- self.assertEqual(info_["id"], robj.id)
- self.assertEqual(None, robj.human_id)
- self.assertEqual(2, manager.write_to_completion_cache.call_count)
-
- # base.Resource.HUMAN_ID = True
- info_["HUMAN_ID"] = True
- robj = base.Resource(manager, info_)
- self.assertEqual(info_["id"], robj.id)
- self.assertEqual(info_["name"], robj.human_id)
- self.assertEqual(4, manager.write_to_completion_cache.call_count)
-
- def test_human_id(self):
- manager = Mock()
- manager.write_to_completion_cache = Mock(return_value=None)
-
- info_ = {"name": "test-human-id"}
- robj = base.Resource(manager, info_)
- self.assertEqual(None, robj.human_id)
-
- info_["HUMAN_ID"] = True
- robj = base.Resource(manager, info_)
- self.assertEqual(info_["name"], robj.human_id)
- robj.name = "new-human-id"
- self.assertEqual("new-human-id", robj.human_id)
-
- def get_mock_resource_obj(self):
- base.Resource.__init__ = Mock(return_value=None)
- robj = base.Resource()
- return robj
-
- def test__add_details(self):
- robj = self.get_mock_resource_obj()
- info_ = {"name": "test-human-id", "test_attr": 5}
- robj._add_details(info_)
- self.assertEqual(info_["name"], robj.name)
- self.assertEqual(info_["test_attr"], robj.test_attr)
-
- def test___getattr__(self):
- robj = self.get_mock_resource_obj()
- info_ = {"name": "test-human-id", "test_attr": 5}
- robj._add_details(info_)
- self.assertEqual(info_["test_attr"], robj.__getattr__("test_attr"))
-
- # TODO: looks like causing infinite recursive calls
- #robj.__getattr__("test_non_exist_attr")
-
- def test___repr__(self):
- robj = self.get_mock_resource_obj()
- info_ = {"name": "test-human-id", "test_attr": 5}
- robj._add_details(info_)
-
- expected = "<Resource name=test-human-id, test_attr=5>"
- self.assertEqual(expected, robj.__repr__())
-
- def test_get(self):
- robj = self.get_mock_resource_obj()
- manager = Mock()
- manager.get = None
-
- robj.manager = object()
- robj.get()
-
- manager = Mock()
- robj.manager = Mock()
-
- robj.id = "id"
- new = Mock()
- new._info = {"name": "test-human-id", "test_attr": 5}
- robj.manager.get = Mock(return_value=new)
- robj.get()
- self.assertEqual("test-human-id", robj.name)
- self.assertEqual(5, robj.test_attr)
-
- def tes___eq__(self):
- robj = self.get_mock_resource_obj()
- other = base.Resource()
-
- info_ = {"name": "test-human-id", "test_attr": 5}
- robj._info = info_
- other._info = {}
- self.assertNotTrue(robj.__eq__(other))
-
- robj._info = info_
- self.assertTrue(robj.__eq__(other))
-
- robj.id = "rid"
- other.id = "oid"
- self.assertNotTrue(robj.__eq__(other))
-
- other.id = "rid"
- self.assertTrue(robj.__eq__(other))
-
- # not instance of the same class
- other = Mock()
- self.assertNotTrue(robj.__eq__(other))
-
- def test_is_loaded(self):
- robj = self.get_mock_resource_obj()
- robj._loaded = True
- self.assertTrue(robj.is_loaded())
-
- robj._loaded = False
- self.assertFalse(robj.is_loaded())
-
- def test_set_loaded(self):
- robj = self.get_mock_resource_obj()
- robj.set_loaded(True)
- self.assertTrue(robj._loaded)
-
- robj.set_loaded(False)
- self.assertFalse(robj._loaded)
diff --git a/reddwarfclient/tests/test_client.py b/reddwarfclient/tests/test_client.py
deleted file mode 100644
index d52331f..0000000
--- a/reddwarfclient/tests/test_client.py
+++ /dev/null
@@ -1,322 +0,0 @@
-import contextlib
-import os
-import logging
-import httplib2
-import time
-
-from testtools import TestCase
-from mock import Mock
-
-from reddwarfclient import client
-from reddwarfclient import exceptions
-from reddwarfclient import utils
-
-"""
-Unit tests for client.py
-"""
-
-
-class ClientTest(TestCase):
-
- def test_log_to_streamhandler(self):
- client.log_to_streamhandler()
- self.assertTrue(client._logger.level == logging.DEBUG)
-
-
-class ReddwarfHTTPClientTest(TestCase):
-
- def setUp(self):
- super(ReddwarfHTTPClientTest, self).setUp()
- self.orig__init = client.ReddwarfHTTPClient.__init__
- client.ReddwarfHTTPClient.__init__ = Mock(return_value=None)
- self.hc = client.ReddwarfHTTPClient()
- self.hc.auth_token = "test-auth-token"
- self.hc.service_url = "test-service-url/"
- self.hc.tenant = "test-tenant"
-
- self.__debug_lines = list()
-
- self.orig_client__logger = client._logger
- client._logger = Mock()
-
- self.orig_time = time.time
- self.orig_htttp_request = httplib2.Http.request
-
- def tearDown(self):
- super(ReddwarfHTTPClientTest, self).tearDown()
- client.ReddwarfHTTPClient.__init__ = self.orig__init
- client._logger = self.orig_client__logger
- time.time = self.orig_time
- httplib2.Http.request = self.orig_htttp_request
-
- def side_effect_func_for_moc_debug(self, s, *args):
- self.__debug_lines.append(s)
-
- def test___init__(self):
- client.ReddwarfHTTPClient.__init__ = self.orig__init
-
- user = "test-user"
- password = "test-password"
- tenant = "test-tenant"
- auth_url = "http://test-auth-url/"
- service_name = None
-
- # when there is no auth_strategy provided
- self.assertRaises(ValueError, client.ReddwarfHTTPClient, user,
- password, tenant, auth_url, service_name)
-
- hc = client.ReddwarfHTTPClient(user, password, tenant, auth_url,
- service_name, auth_strategy="fake")
- self.assertEqual("http://test-auth-url", hc.auth_url)
-
- # auth_url is none
- hc = client.ReddwarfHTTPClient(user, password, tenant, None,
- service_name, auth_strategy="fake")
- self.assertEqual(None, hc.auth_url)
-
- def test_get_timings(self):
- self.hc.times = ["item1", "item2"]
- self.assertEqual(2, len(self.hc.get_timings()))
- self.assertEqual("item1", self.hc.get_timings()[0])
- self.assertEqual("item2", self.hc.get_timings()[1])
-
- def test_http_log(self):
- self.hc.simple_log = Mock(return_value=None)
- self.hc.pretty_log = Mock(return_value=None)
-
- client.RDC_PP = False
- self.hc.http_log(None, None, None, None)
- self.assertEqual(1, self.hc.simple_log.call_count)
-
- client.RDC_PP = True
- self.hc.http_log(None, None, None, None)
- self.assertEqual(1, self.hc.pretty_log.call_count)
-
- def test_simple_log(self):
- client._logger.isEnabledFor = Mock(return_value=False)
- self.hc.simple_log(None, None, None, None)
- self.assertEqual(0, len(self.__debug_lines))
-
- client._logger.isEnabledFor = Mock(return_value=True)
- se = self.side_effect_func_for_moc_debug
- client._logger.debug = Mock(side_effect=se)
- self.hc.simple_log(['item1', 'GET', 'item3', 'POST', 'item5'],
- {'headers': {'e1': 'e1-v', 'e2': 'e2-v'},
- 'body': 'body'}, None, None)
- self.assertEqual(3, len(self.__debug_lines))
- self.assertTrue(self.__debug_lines[0].startswith('REQ: curl -i'))
- self.assertTrue(self.__debug_lines[1].startswith('REQ BODY:'))
- self.assertTrue(self.__debug_lines[2].startswith('RESP:'))
-
- def test_pretty_log(self):
- client._logger.isEnabledFor = Mock(return_value=False)
- self.hc.pretty_log(None, None, None, None)
- self.assertEqual(0, len(self.__debug_lines))
-
- client._logger.isEnabledFor = Mock(return_value=True)
- se = self.side_effect_func_for_moc_debug
- client._logger.debug = Mock(side_effect=se)
- self.hc.pretty_log(['item1', 'GET', 'item3', 'POST', 'item5'],
- {'headers': {'e1': 'e1-v', 'e2': 'e2-v'},
- 'body': 'body'}, None, None)
- self.assertEqual(5, len(self.__debug_lines))
- self.assertTrue(self.__debug_lines[0].startswith('REQUEST:'))
- self.assertTrue(self.__debug_lines[1].startswith('curl -i'))
- self.assertTrue(self.__debug_lines[2].startswith('BODY:'))
- self.assertTrue(self.__debug_lines[3].startswith('RESPONSE HEADERS:'))
- self.assertTrue(self.__debug_lines[4].startswith('RESPONSE BODY'))
-
- # no body case
- self.__debug_lines = list()
- self.hc.pretty_log(['item1', 'GET', 'item3', 'POST', 'item5'],
- {'headers': {'e1': 'e1-v', 'e2': 'e2-v'}},
- None, None)
- self.assertEqual(4, len(self.__debug_lines))
- self.assertTrue(self.__debug_lines[0].startswith('REQUEST:'))
- self.assertTrue(self.__debug_lines[1].startswith('curl -i'))
- self.assertTrue(self.__debug_lines[2].startswith('RESPONSE HEADERS:'))
- self.assertTrue(self.__debug_lines[3].startswith('RESPONSE BODY'))
-
- def test_request(self):
- self.hc.USER_AGENT = "user-agent"
- resp = Mock()
- body = Mock()
- resp.status = 200
- httplib2.Http.request = Mock(return_value=(resp, body))
- self.hc.morph_response_body = Mock(return_value=body)
- r, b = self.hc.request()
- self.assertEqual(resp, r)
- self.assertEqual(body, b)
- self.assertEqual((resp, body), self.hc.last_response)
-
- httplib2.Http.request = Mock(return_value=(resp, None))
- r, b = self.hc.request()
- self.assertEqual(resp, r)
- self.assertEqual(None, b)
-
- status_list = [400, 401, 403, 404, 408, 409, 413, 500, 501]
- for status in status_list:
- resp.status = status
- self.assertRaises(ValueError, self.hc.request)
-
- exception = exceptions.ResponseFormatError
- self.hc.morph_response_body = Mock(side_effect=exception)
- self.assertRaises(Exception, self.hc.request)
-
- def test_raise_error_from_status(self):
- resp = Mock()
- resp.status = 200
- self.hc.raise_error_from_status(resp, Mock())
-
- status_list = [400, 401, 403, 404, 408, 409, 413, 500, 501]
- for status in status_list:
- resp.status = status
- self.assertRaises(ValueError,
- self.hc.raise_error_from_status, resp, Mock())
-
- def test_morph_request(self):
- kwargs = dict()
- kwargs['headers'] = dict()
- kwargs['body'] = ['body', {'item1': 'value1'}]
- self.hc.morph_request(kwargs)
- expected = {'body': '["body", {"item1": "value1"}]',
- 'headers': {'Content-Type': 'application/json',
- 'Accept': 'application/json'}}
- self.assertEqual(expected, kwargs)
-
- def test_morph_response_body(self):
- body_string = '["body", {"item1": "value1"}]'
- expected = ['body', {'item1': 'value1'}]
- self.assertEqual(expected, self.hc.morph_response_body(body_string))
- body_string = '["body", {"item1": }]'
- self.assertRaises(exceptions.ResponseFormatError,
- self.hc.morph_response_body, body_string)
-
- def test__time_request(self):
- self.__time = 0
-
- def side_effect_func():
- self.__time = self.__time + 1
- return self.__time
-
- time.time = Mock(side_effect=side_effect_func)
- self.hc.request = Mock(return_value=("mock-response", "mock-body"))
- self.hc.times = list()
- resp, body = self.hc._time_request("test-url", "Get")
- self.assertEqual(("mock-response", "mock-body"), (resp, body))
- self.assertEqual([('Get test-url', 1, 2)], self.hc.times)
-
- def mock_time_request_func(self):
- def side_effect_func(url, method, **kwargs):
- return url, method
- self.hc._time_request = Mock(side_effect=side_effect_func)
-
- def test__cs_request(self):
- self.mock_time_request_func()
- resp, body = self.hc._cs_request("test-url", "GET")
- self.assertEqual(('test-service-url/test-url', 'GET'), (resp, body))
-
- self.hc.authenticate = Mock(side_effect=ValueError)
- self.hc.auth_token = None
- self.hc.service_url = None
- self.assertRaises(ValueError, self.hc._cs_request, "test-url", "GET")
-
- self.hc.authenticate = Mock(return_value=None)
- self.hc.service_url = "test-service-url/"
-
- def side_effect_func_time_req(url, method, **kwargs):
- raise exceptions.Unauthorized(None)
-
- self.hc._time_request = Mock(side_effect=side_effect_func_time_req)
- self.assertRaises(exceptions.Unauthorized,
- self.hc._cs_request, "test-url", "GET")
-
- def test_get(self):
- self.mock_time_request_func()
- resp, body = self.hc.get("test-url")
- self.assertEqual(("test-service-url/test-url", "GET"), (resp, body))
-
- def test_post(self):
- self.mock_time_request_func()
- resp, body = self.hc.post("test-url")
- self.assertEqual(("test-service-url/test-url", "POST"), (resp, body))
-
- def test_put(self):
- self.mock_time_request_func()
- resp, body = self.hc.put("test-url")
- self.assertEqual(("test-service-url/test-url", "PUT"), (resp, body))
-
- def test_delete(self):
- self.mock_time_request_func()
- resp, body = self.hc.delete("test-url")
- self.assertEqual(("test-service-url/test-url", "DELETE"), (resp, body))
-
- def test_authenticate(self):
- self.hc.authenticator = Mock()
- catalog = Mock()
- catalog.get_public_url = Mock(return_value="public-url")
- catalog.get_management_url = Mock(return_value="mng-url")
- catalog.get_token = Mock(return_value="test-token")
-
- self.__auth_calls = []
-
- def side_effect_func(token, url):
- self.__auth_calls = [token, url]
-
- self.hc.authenticate_with_token = Mock(side_effect=side_effect_func)
- self.hc.authenticator.authenticate = Mock(return_value=catalog)
- self.hc.endpoint_type = "publicURL"
- self.hc.authenticate()
- self.assertEqual(["test-token", None],
- self.__auth_calls)
-
- self.__auth_calls = []
- self.hc.service_url = None
- self.hc.authenticate()
- self.assertEqual(["test-token", "public-url"], self.__auth_calls)
-
- self.__auth_calls = []
- self.hc.endpoint_type = "adminURL"
- self.hc.authenticate()
- self.assertEqual(["test-token", "mng-url"], self.__auth_calls)
-
- def test_authenticate_with_token(self):
- self.hc.service_url = None
- self.assertRaises(exceptions.ServiceUrlNotGiven,
- self.hc.authenticate_with_token, "token", None)
- self.hc.authenticate_with_token("token", "test-url")
- self.assertEqual("test-url", self.hc.service_url)
- self.assertEqual("token", self.hc.auth_token)
-
-
-class DbaasTest(TestCase):
-
- def setUp(self):
- super(DbaasTest, self).setUp()
- self.orig__init = client.ReddwarfHTTPClient.__init__
- client.ReddwarfHTTPClient.__init__ = Mock(return_value=None)
- self.dbaas = client.Dbaas("user", "api-key")
-
- def tearDown(self):
- super(DbaasTest, self).tearDown()
- client.ReddwarfHTTPClient.__init__ = self.orig__init
-
- def test___init__(self):
- client.ReddwarfHTTPClient.__init__ = Mock(return_value=None)
- self.assertNotEqual(None, self.dbaas.mgmt)
-
- def test_set_management_url(self):
- self.dbaas.set_management_url("test-management-url")
- self.assertEqual("test-management-url",
- self.dbaas.client.management_url)
-
- def test_get_timings(self):
- __timings = {'start': 1, 'end': 2}
- self.dbaas.client.get_timings = Mock(return_value=__timings)
- self.assertEqual(__timings, self.dbaas.get_timings())
-
- def test_authenticate(self):
- mock_auth = Mock(return_value=None)
- self.dbaas.client.authenticate = mock_auth
- self.dbaas.authenticate()
- self.assertEqual(1, mock_auth.call_count)
diff --git a/reddwarfclient/tests/test_common.py b/reddwarfclient/tests/test_common.py
deleted file mode 100644
index b038a05..0000000
--- a/reddwarfclient/tests/test_common.py
+++ /dev/null
@@ -1,395 +0,0 @@
-import sys
-import optparse
-import json
-import collections
-
-from testtools import TestCase
-from mock import Mock
-
-from reddwarfclient import common
-from reddwarfclient import client
-
-"""
- unit tests for common.py
-"""
-
-
-class CommonTest(TestCase):
-
- def setUp(self):
- super(CommonTest, self).setUp()
- self.orig_sys_exit = sys.exit
- sys.exit = Mock(return_value=None)
-
- def tearDown(self):
- super(CommonTest, self).tearDown()
- sys.exit = self.orig_sys_exit
-
- def test_methods_of(self):
- class DummyClass:
- def dummyMethod(self):
- print("just for test")
-
- obj = DummyClass()
- result = common.methods_of(obj)
- self.assertEqual(1, len(result))
- method = result['dummyMethod']
- self.assertIsNotNone(method)
-
- def test_check_for_exceptions(self):
- status = [400, 422, 500]
- for s in status:
- resp = Mock()
- resp.status = s
- self.assertRaises(ValueError,
- common.check_for_exceptions, resp, "body")
-
- # a no-exception case
- resp = Mock()
- resp.status = 200
- common.check_for_exceptions(resp, "body")
-
- def test_print_actions(self):
- cmd = "test-cmd"
- actions = {"test": "test action", "help": "help action"}
- common.print_actions(cmd, actions)
- pass
-
- def test_print_commands(self):
- commands = {"cmd-1": "cmd 1", "cmd-2": "cmd 2"}
- common.print_commands(commands)
- pass
-
- def test_limit_url(self):
- url_ = "test-url"
- limit_ = None
- marker_ = None
- self.assertEqual(url_, common.limit_url(url_))
-
- limit_ = "test-limit"
- marker_ = "test-marker"
- expected = "test-url?marker=test-marker&limit=test-limit"
- self.assertEqual(expected,
- common.limit_url(url_, limit=limit_, marker=marker_))
-
-
-class CliOptionsTest(TestCase):
-
- def check_default_options(self, co):
- self.assertEqual(None, co.username)
- self.assertEqual(None, co.apikey)
- self.assertEqual(None, co.tenant_id)
- self.assertEqual(None, co.auth_url)
- self.assertEqual('keystone', co.auth_type)
- self.assertEqual('database', co.service_type)
- self.assertEqual('reddwarf', co.service_name)
- self.assertEqual('RegionOne', co.region)
- self.assertEqual(None, co.service_url)
- self.assertFalse(co.insecure)
- self.assertFalse(co.verbose)
- self.assertFalse(co.debug)
- self.assertEqual(None, co.token)
- self.assertEqual(None, co.xml)
-
- def check_option(self, oparser, option_name):
- option = oparser.get_option("--%s" % option_name)
- self.assertNotEqual(None, option)
- if option_name in common.CliOptions.DEFAULT_VALUES:
- self.assertEqual(common.CliOptions.DEFAULT_VALUES[option_name],
- option.default)
-
- def test___init__(self):
- co = common.CliOptions()
- self.check_default_options(co)
-
- def test_deafult(self):
- co = common.CliOptions.default()
- self.check_default_options(co)
-
- def test_load_from_file(self):
- co = common.CliOptions.load_from_file()
- self.check_default_options(co)
-
- def test_create_optparser(self):
- option_names = ["verbose", "debug", "auth_url", "username", "apikey",
- "tenant_id", "auth_type", "service_type",
- "service_name", "service_type", "service_name",
- "service_url", "region", "insecure", "token",
- "xml", "secure", "json", "terse", "hide-debug"]
-
- oparser = common.CliOptions.create_optparser(True)
- for option_name in option_names:
- self.check_option(oparser, option_name)
-
- oparser = common.CliOptions.create_optparser(False)
- for option_name in option_names:
- self.check_option(oparser, option_name)
-
-
-class ArgumentRequiredTest(TestCase):
-
- def setUp(self):
- super(ArgumentRequiredTest, self).setUp()
- self.param = "test-param"
- self.arg_req = common.ArgumentRequired(self.param)
-
- def test___init__(self):
- self.assertEqual(self.param, self.arg_req.param)
-
- def test___str__(self):
- expected = 'Argument "--%s" required.' % self.param
- self.assertEqual(expected, self.arg_req.__str__())
-
-
-class CommandsBaseTest(TestCase):
-
- def setUp(self):
- super(CommandsBaseTest, self).setUp()
- self.orig_sys_exit = sys.exit
- sys.exit = Mock(return_value=None)
- parser = common.CliOptions().create_optparser(False)
- self.cmd_base = common.CommandsBase(parser)
-
- def tearDown(self):
- super(CommandsBaseTest, self).tearDown()
- sys.exit = self.orig_sys_exit
-
- def test___init__(self):
- self.assertNotEqual(None, self.cmd_base)
-
- def test__get_client(self):
- client.log_to_streamhandler = Mock(return_value=None)
- expected = Mock()
- client.Dbaas = Mock(return_value=expected)
-
- self.cmd_base.xml = Mock()
- self.cmd_base.verbose = False
- r = self.cmd_base._get_client()
- self.assertEqual(expected, r)
-
- self.cmd_base.xml = None
- self.cmd_base.verbose = True
- r = self.cmd_base._get_client()
- self.assertEqual(expected, r)
-
- # test debug true
- self.cmd_base.debug = True
- client.Dbaas = Mock(side_effect=ValueError)
- self.assertRaises(ValueError, self.cmd_base._get_client)
-
- def test__safe_exec(self):
- func = Mock(return_value="test")
- self.cmd_base.debug = True
- r = self.cmd_base._safe_exec(func)
- self.assertEqual("test", r)
-
- self.cmd_base.debug = False
- r = self.cmd_base._safe_exec(func)
- self.assertEqual("test", r)
-
- func = Mock(side_effect=ValueError) # an arbitrary exception
- r = self.cmd_base._safe_exec(func)
- self.assertEqual(None, r)
-
- def test__prepare_parser(self):
- parser = optparse.OptionParser()
- common.CommandsBase.params = ["test_1", "test_2"]
- self.cmd_base._prepare_parser(parser)
- option = parser.get_option("--%s" % "test_1")
- self.assertNotEqual(None, option)
- option = parser.get_option("--%s" % "test_2")
- self.assertNotEqual(None, option)
-
- def test__parse_options(self):
- parser = optparse.OptionParser()
- parser.add_option("--%s" % "test_1", default="test_1v")
- parser.add_option("--%s" % "test_2", default="test_2v")
- self.cmd_base._parse_options(parser)
- self.assertEqual("test_1v", self.cmd_base.test_1)
- self.assertEqual("test_2v", self.cmd_base.test_2)
-
- def test__require(self):
- self.assertRaises(common.ArgumentRequired,
- self.cmd_base._require, "attr_1")
- self.cmd_base.attr_1 = None
- self.assertRaises(common.ArgumentRequired,
- self.cmd_base._require, "attr_1")
- self.cmd_base.attr_1 = "attr_v1"
- self.cmd_base._require("attr_1")
-
- def test__make_list(self):
- self.assertRaises(AttributeError, self.cmd_base._make_list, "attr1")
- self.cmd_base.attr1 = "v1,v2"
- self.cmd_base._make_list("attr1")
- self.assertEqual(["v1", "v2"], self.cmd_base.attr1)
- self.cmd_base.attr1 = ["v3"]
- self.cmd_base._make_list("attr1")
- self.assertEqual(["v3"], self.cmd_base.attr1)
-
- def test__pretty_print(self):
- func = Mock(return_value=None)
- self.cmd_base.verbose = True
- self.assertEqual(None, self.cmd_base._pretty_print(func))
- self.cmd_base.verbose = False
- self.assertEqual(None, self.cmd_base._pretty_print(func))
-
- def test__dumps(self):
- json.dumps = Mock(return_value="test-dump")
- self.assertEqual("test-dump", self.cmd_base._dumps("item"))
-
- def test__pretty_list(self):
- func = Mock(return_value=None)
- self.cmd_base.verbose = True
- self.assertEqual(None, self.cmd_base._pretty_list(func))
- self.cmd_base.verbose = False
- self.assertEqual(None, self.cmd_base._pretty_list(func))
- item = Mock(return_value="test")
- item._info = "info"
- func = Mock(return_value=[item])
- self.assertEqual(None, self.cmd_base._pretty_list(func))
-
- def test__pretty_paged(self):
- self.cmd_base.limit = "5"
- func = Mock(return_value=None)
- self.cmd_base.verbose = True
- self.assertEqual(None, self.cmd_base._pretty_paged(func))
-
- self.cmd_base.verbose = False
-
- class MockIterable(collections.Iterable):
- links = ["item"]
- count = 1
-
- def __iter__(self):
- return ["item1"]
-
- def __len__(self):
- return count
-
- ret = MockIterable()
- func = Mock(return_value=ret)
- self.assertEqual(None, self.cmd_base._pretty_paged(func))
-
- ret.count = 0
- self.assertEqual(None, self.cmd_base._pretty_paged(func))
-
- func = Mock(side_effect=ValueError)
- self.assertEqual(None, self.cmd_base._pretty_paged(func))
- self.cmd_base.debug = True
- self.cmd_base.marker = Mock()
- self.assertRaises(ValueError, self.cmd_base._pretty_paged, func)
-
-
-class AuthTest(TestCase):
-
- def setUp(self):
- super(AuthTest, self).setUp()
- self.orig_sys_exit = sys.exit
- sys.exit = Mock(return_value=None)
- self.parser = common.CliOptions().create_optparser(False)
- self.auth = common.Auth(self.parser)
-
- def tearDown(self):
- super(AuthTest, self).tearDown()
- sys.exit = self.orig_sys_exit
-
- def test___init__(self):
- self.assertEqual(None, self.auth.dbaas)
- self.assertEqual(None, self.auth.apikey)
-
- def test_login(self):
- self.auth.username = "username"
- self.auth.apikey = "apikey"
- self.auth.tenant_id = "tenant_id"
- self.auth.auth_url = "auth_url"
- dbaas = Mock()
- dbaas.authenticate = Mock(return_value=None)
- dbaas.client = Mock()
- dbaas.client.auth_token = Mock()
- dbaas.client.service_url = Mock()
- self.auth._get_client = Mock(return_value=dbaas)
- self.auth.login()
-
- self.auth.debug = True
- self.auth._get_client = Mock(side_effect=ValueError)
- self.assertRaises(ValueError, self.auth.login)
-
- self.auth.debug = False
- self.auth.login()
-
-
-class AuthedCommandsBaseTest(TestCase):
-
- def setUp(self):
- super(AuthedCommandsBaseTest, self).setUp()
- self.orig_sys_exit = sys.exit
- sys.exit = Mock(return_value=None)
-
- def tearDown(self):
- super(AuthedCommandsBaseTest, self).tearDown()
- sys.exit = self.orig_sys_exit
-
- def test___init__(self):
- parser = common.CliOptions().create_optparser(False)
- common.AuthedCommandsBase.debug = True
- dbaas = Mock()
- dbaas.authenticate = Mock(return_value=None)
- dbaas.client = Mock()
- dbaas.client.auth_token = Mock()
- dbaas.client.service_url = Mock()
- dbaas.client.authenticate_with_token = Mock()
- common.AuthedCommandsBase._get_client = Mock(return_value=dbaas)
- authed_cmd = common.AuthedCommandsBase(parser)
-
-
-class PaginatedTest(TestCase):
-
- def setUp(self):
- super(PaginatedTest, self).setUp()
- self.items_ = ["item1", "item2"]
- self.next_marker_ = "next-marker"
- self.links_ = ["link1", "link2"]
- self.pgn = common.Paginated(self.items_, self.next_marker_,
- self.links_)
-
- def tearDown(self):
- super(PaginatedTest, self).tearDown()
-
- def test___init__(self):
- self.assertEqual(self.items_, self.pgn.items)
- self.assertEqual(self.next_marker_, self.pgn.next)
- self.assertEqual(self.links_, self.pgn.links)
-
- def test___len__(self):
- self.assertEqual(len(self.items_), self.pgn.__len__())
-
- def test___iter__(self):
- itr_expected = self.items_.__iter__()
- itr = self.pgn.__iter__()
- self.assertEqual(itr_expected.next(), itr.next())
- self.assertEqual(itr_expected.next(), itr.next())
- self.assertRaises(StopIteration, itr_expected.next)
- self.assertRaises(StopIteration, itr.next)
-
- def test___getitem__(self):
- self.assertEqual(self.items_[0], self.pgn.__getitem__(0))
-
- def test___setitem__(self):
- self.pgn.__setitem__(0, "new-item")
- self.assertEqual("new-item", self.pgn.items[0])
-
- def test___delitem(self):
- del self.pgn[0]
- self.assertEqual(1, self.pgn.__len__())
-
- def test___reversed__(self):
- itr = self.pgn.__reversed__()
- expected = ["item2", "item1"]
- self.assertEqual("item2", itr.next())
- self.assertEqual("item1", itr.next())
- self.assertRaises(StopIteration, itr.next)
-
- def test___contains__(self):
- self.assertTrue(self.pgn.__contains__("item1"))
- self.assertTrue(self.pgn.__contains__("item2"))
- self.assertFalse(self.pgn.__contains__("item3"))
diff --git a/reddwarfclient/tests/test_instances.py b/reddwarfclient/tests/test_instances.py
deleted file mode 100644
index 2131d26..0000000
--- a/reddwarfclient/tests/test_instances.py
+++ /dev/null
@@ -1,176 +0,0 @@
-from testtools import TestCase
-from mock import Mock
-
-from reddwarfclient import instances
-from reddwarfclient import base
-
-"""
-Unit tests for instances.py
-"""
-
-
-class InstanceTest(TestCase):
-
- def setUp(self):
- super(InstanceTest, self).setUp()
- self.orig__init = instances.Instance.__init__
- instances.Instance.__init__ = Mock(return_value=None)
- self.instance = instances.Instance()
- self.instance.manager = Mock()
-
- def tearDown(self):
- super(InstanceTest, self).tearDown()
- instances.Instance.__init__ = self.orig__init
-
- def test___repr__(self):
- self.instance.name = "instance-1"
- self.assertEqual('<Instance: instance-1>', self.instance.__repr__())
-
- def test_list_databases(self):
- db_list = ['database1', 'database2']
- self.instance.manager.databases = Mock()
- self.instance.manager.databases.list = Mock(return_value=db_list)
- self.assertEqual(db_list, self.instance.list_databases())
-
- def test_delete(self):
- db_delete_mock = Mock(return_value=None)
- self.instance.manager.delete = db_delete_mock
- self.instance.delete()
- self.assertEqual(1, db_delete_mock.call_count)
-
- def test_restart(self):
- db_restart_mock = Mock(return_value=None)
- self.instance.manager.restart = db_restart_mock
- self.instance.id = 1
- self.instance.restart()
- self.assertEqual(1, db_restart_mock.call_count)
-
-
-class InstancesTest(TestCase):
-
- def setUp(self):
- super(InstancesTest, self).setUp()
- self.orig__init = instances.Instances.__init__
- instances.Instances.__init__ = Mock(return_value=None)
- self.instances = instances.Instances()
- self.instances.api = Mock()
- self.instances.api.client = Mock()
- self.instances.resource_class = Mock(return_value="instance-1")
-
- self.orig_base_getid = base.getid
- base.getid = Mock(return_value="instance1")
-
- def tearDown(self):
- super(InstancesTest, self).tearDown()
- instances.Instances.__init__ = self.orig__init
- base.getid = self.orig_base_getid
-
- def test_create(self):
- def side_effect_func(path, body, inst):
- return path, body, inst
-
- self.instances._create = Mock(side_effect=side_effect_func)
- p, b, i = self.instances.create("test-name", 103, "test-volume",
- ['db1', 'db2'], ['u1', 'u2'])
- self.assertEqual("/instances", p)
- self.assertEqual("instance", i)
- self.assertEqual(['db1', 'db2'], b["instance"]["databases"])
- self.assertEqual(['u1', 'u2'], b["instance"]["users"])
- self.assertEqual("test-name", b["instance"]["name"])
- self.assertEqual("test-volume", b["instance"]["volume"])
- self.assertEqual(103, b["instance"]["flavorRef"])
-
- def test__list(self):
- self.instances.api.client.get = Mock(return_value=('resp', None))
- self.assertRaises(Exception, self.instances._list, "url", None)
-
- body = Mock()
- body.get = Mock(return_value=[{'href': 'http://test.net/test_file',
- 'rel': 'next'}])
- body.__getitem__ = Mock(return_value='instance1')
- #self.instances.resource_class = Mock(return_value="instance-1")
- self.instances.api.client.get = Mock(return_value=('resp', body))
- _expected = [{'href': 'http://test.net/test_file', 'rel': 'next'}]
- self.assertEqual(_expected, self.instances._list("url", None).links)
-
- def test_list(self):
- def side_effect_func(path, inst, limit, marker):
- return path, inst, limit, marker
-
- self.instances._list = Mock(side_effect=side_effect_func)
- limit_ = "test-limit"
- marker_ = "test-marker"
- expected = ("/instances", "instances", limit_, marker_)
- self.assertEqual(expected, self.instances.list(limit_, marker_))
-
- def test_get(self):
- def side_effect_func(path, inst):
- return path, inst
-
- self.instances._get = Mock(side_effect=side_effect_func)
- self.assertEqual(('/instances/instance1', 'instance'),
- self.instances.get(1))
-
- def test_delete(self):
- resp = Mock()
- resp.status = 200
- body = None
- self.instances.api.client.delete = Mock(return_value=(resp, body))
- self.instances.delete('instance1')
- resp.status = 500
- self.assertRaises(ValueError, self.instances.delete, 'instance1')
-
- def test__action(self):
- body = Mock()
- resp = Mock()
- resp.status = 200
- self.instances.api.client.post = Mock(return_value=(resp, body))
- self.assertEqual('instance-1', self.instances._action(1, body))
-
- self.instances.api.client.post = Mock(return_value=(resp, None))
- self.assertEqual(None, self.instances._action(1, body))
-
- def _set_action_mock(self):
- def side_effect_func(instance_id, body):
- self._instance_id = instance_id
- self._body = body
-
- self._instance_id = None
- self._body = None
- self.instances._action = Mock(side_effect=side_effect_func)
-
- def test_resize_volume(self):
- self._set_action_mock()
- self.instances.resize_volume(152, 512)
- self.assertEqual(152, self._instance_id)
- self.assertEqual({"resize": {"volume": {"size": 512}}}, self._body)
-
- def test_resize_instance(self):
- self._set_action_mock()
- self.instances.resize_instance(4725, 103)
- self.assertEqual(4725, self._instance_id)
- self.assertEqual({"resize": {"flavorRef": 103}}, self._body)
-
- def test_restart(self):
- self._set_action_mock()
- self.instances.restart(253)
- self.assertEqual(253, self._instance_id)
- self.assertEqual({'restart': {}}, self._body)
-
- def test_reset_password(self):
- self._set_action_mock()
- self.instances.reset_password(634)
- self.assertEqual(634, self._instance_id)
- self.assertEqual({'reset-password': {}}, self._body)
-
-
-class InstanceStatusTest(TestCase):
-
- def test_constants(self):
- self.assertEqual("ACTIVE", instances.InstanceStatus.ACTIVE)
- self.assertEqual("BLOCKED", instances.InstanceStatus.BLOCKED)
- self.assertEqual("BUILD", instances.InstanceStatus.BUILD)
- self.assertEqual("FAILED", instances.InstanceStatus.FAILED)
- self.assertEqual("REBOOT", instances.InstanceStatus.REBOOT)
- self.assertEqual("RESIZE", instances.InstanceStatus.RESIZE)
- self.assertEqual("SHUTDOWN", instances.InstanceStatus.SHUTDOWN)
diff --git a/reddwarfclient/tests/test_limits.py b/reddwarfclient/tests/test_limits.py
deleted file mode 100644
index fda182c..0000000
--- a/reddwarfclient/tests/test_limits.py
+++ /dev/null
@@ -1,79 +0,0 @@
-from testtools import TestCase
-from mock import Mock
-from reddwarfclient import limits
-
-
-class LimitsTest(TestCase):
- """
- This class tests the calling code for the Limits API
- """
-
- def setUp(self):
- super(LimitsTest, self).setUp()
- self.limits = limits.Limits(Mock())
- self.limits.api.client = Mock()
-
- def tearDown(self):
- super(LimitsTest, self).tearDown()
-
- def test_list(self):
- resp = Mock()
- resp.status = 200
- body = {"limits":
- [
- {'maxTotalInstances': 55,
- 'verb': 'ABSOLUTE',
- 'maxTotalVolumes': 100},
- {'regex': '.*',
- 'nextAvailable': '2011-07-21T18:17:06Z',
- 'uri': '*',
- 'value': 10,
- 'verb': 'POST',
- 'remaining': 2, 'unit': 'MINUTE'},
- {'regex': '.*',
- 'nextAvailable': '2011-07-21T18:17:06Z',
- 'uri': '*',
- 'value': 10,
- 'verb': 'PUT',
- 'remaining': 2,
- 'unit': 'MINUTE'},
- {'regex': '.*',
- 'nextAvailable': '2011-07-21T18:17:06Z',
- 'uri': '*',
- 'value': 10,
- 'verb': 'DELETE',
- 'remaining': 2,
- 'unit': 'MINUTE'},
- {'regex': '.*',
- 'nextAvailable': '2011-07-21T18:17:06Z',
- 'uri': '*',
- 'value': 10,
- 'verb': 'GET',
- 'remaining': 2, 'unit': 'MINUTE'}]}
- response = (resp, body)
-
- mock_get = Mock(return_value=response)
- self.limits.api.client.get = mock_get
- self.assertIsNotNone(self.limits.list())
- mock_get.assert_called_once_with("/limits")
-
- def test_list_errors(self):
- status_list = [400, 401, 403, 404, 408, 409, 413, 500, 501]
- for status_code in status_list:
- self._check_error_response(status_code)
-
- def _check_error_response(self, status_code):
- RESPONSE_KEY = "limits"
-
- resp = Mock()
- resp.status = status_code
- body = {RESPONSE_KEY: {
- 'absolute': {},
- 'rate': [
- {'limit': []
- }]}}
- response = (resp, body)
-
- mock_get = Mock(return_value=response)
- self.limits.api.client.get = mock_get
- self.assertRaises(Exception, self.limits.list)
diff --git a/reddwarfclient/tests/test_management.py b/reddwarfclient/tests/test_management.py
deleted file mode 100644
index 810961a..0000000
--- a/reddwarfclient/tests/test_management.py
+++ /dev/null
@@ -1,144 +0,0 @@
-from testtools import TestCase
-from mock import Mock
-
-from reddwarfclient import management
-from reddwarfclient import base
-
-"""
-Unit tests for management.py
-"""
-
-
-class RootHistoryTest(TestCase):
-
- def setUp(self):
- super(RootHistoryTest, self).setUp()
- self.orig__init = management.RootHistory.__init__
- management.RootHistory.__init__ = Mock(return_value=None)
-
- def tearDown(self):
- super(RootHistoryTest, self).tearDown()
- management.RootHistory.__init__ = self.orig__init
-
- def test___repr__(self):
- root_history = management.RootHistory()
- root_history.id = "1"
- root_history.created = "ct"
- root_history.user = "tu"
- self.assertEqual('<Root History: Instance 1 enabled at ct by tu>',
- root_history.__repr__())
-
-
-class ManagementTest(TestCase):
-
- def setUp(self):
- super(ManagementTest, self).setUp()
- self.orig__init = management.Management.__init__
- management.Management.__init__ = Mock(return_value=None)
- self.management = management.Management()
- self.management.api = Mock()
- self.management.api.client = Mock()
-
- self.orig_hist__init = management.RootHistory.__init__
- self.orig_base_getid = base.getid
- base.getid = Mock(return_value="instance1")
-
- def tearDown(self):
- super(ManagementTest, self).tearDown()
- management.Management.__init__ = self.orig__init
- management.RootHistory.__init__ = self.orig_hist__init
- base.getid = self.orig_base_getid
-
- def test__list(self):
- self.management.api.client.get = Mock(return_value=('resp', None))
- self.assertRaises(Exception, self.management._list, "url", None)
-
- body = Mock()
- body.get = Mock(return_value=[{'href': 'http://test.net/test_file',
- 'rel': 'next'}])
- body.__getitem__ = Mock(return_value='instance1')
- self.management.resource_class = Mock(return_value="instance-1")
- self.management.api.client.get = Mock(return_value=('resp', body))
- _expected = [{'href': 'http://test.net/test_file', 'rel': 'next'}]
- self.assertEqual(_expected, self.management._list("url", None).links)
-
- def test_show(self):
- def side_effect_func(path, instance):
- return path, instance
- self.management._get = Mock(side_effect=side_effect_func)
- p, i = self.management.show(1)
- self.assertEqual(('/mgmt/instances/instance1', 'instance'), (p, i))
-
- def test_index(self):
- def side_effect_func(url, name, limit, marker):
- return url
-
- self.management._list = Mock(side_effect=side_effect_func)
- self.assertEqual('/mgmt/instances?deleted=true',
- self.management.index(deleted=True))
- self.assertEqual('/mgmt/instances?deleted=false',
- self.management.index(deleted=False))
-
- def test_root_enabled_history(self):
- self.management.api.client.get = Mock(return_value=('resp', None))
- self.assertRaises(Exception,
- self.management.root_enabled_history, "instance")
- body = {'root_history': 'rh'}
- self.management.api.client.get = Mock(return_value=('resp', body))
- management.RootHistory.__init__ = Mock(return_value=None)
- rh = self.management.root_enabled_history("instance")
- self.assertTrue(isinstance(rh, management.RootHistory))
-
- def test__action(self):
- resp = Mock()
- self.management.api.client.post = Mock(return_value=(resp, 'body'))
- resp.status = 200
- self.management._action(1, 'body')
- self.assertEqual(1, self.management.api.client.post.call_count)
- resp.status = 400
- self.assertRaises(ValueError, self.management._action, 1, 'body')
- self.assertEqual(2, self.management.api.client.post.call_count)
-
- def _mock_action(self):
- self.body_ = ""
-
- def side_effect_func(instance_id, body):
- self.body_ = body
- self.management._action = Mock(side_effect=side_effect_func)
-
- def test_stop(self):
- self._mock_action()
- self.management.stop(1)
- self.assertEqual(1, self.management._action.call_count)
- self.assertEqual({'stop': {}}, self.body_)
-
- def test_reboot(self):
- self._mock_action()
- self.management.reboot(1)
- self.assertEqual(1, self.management._action.call_count)
- self.assertEqual({'reboot': {}}, self.body_)
-
- def test_migrate(self):
- self._mock_action()
- self.management.migrate(1)
- self.assertEqual(1, self.management._action.call_count)
- self.assertEqual({'migrate': {}}, self.body_)
-
- def test_migrate_to_host(self):
- hostname = 'hostname2'
- self._mock_action()
- self.management.migrate(1, host=hostname)
- self.assertEqual(1, self.management._action.call_count)
- self.assertEqual({'migrate': {'host': hostname}}, self.body_)
-
- def test_update(self):
- self._mock_action()
- self.management.update(1)
- self.assertEqual(1, self.management._action.call_count)
- self.assertEqual({'update': {}}, self.body_)
-
- def test_reset_task_status(self):
- self._mock_action()
- self.management.reset_task_status(1)
- self.assertEqual(1, self.management._action.call_count)
- self.assertEqual({'reset-task-status': {}}, self.body_)
diff --git a/reddwarfclient/tests/test_secgroups.py b/reddwarfclient/tests/test_secgroups.py
deleted file mode 100644
index 25b4de8..0000000
--- a/reddwarfclient/tests/test_secgroups.py
+++ /dev/null
@@ -1,102 +0,0 @@
-from testtools import TestCase
-from mock import Mock
-
-from reddwarfclient import security_groups
-from reddwarfclient import base
-
-"""
-Unit tests for security_groups.py
-"""
-
-
-class SecGroupTest(TestCase):
-
- def setUp(self):
- super(SecGroupTest, self).setUp()
- self.orig__init = security_groups.SecurityGroup.__init__
- security_groups.SecurityGroup.__init__ = Mock(return_value=None)
- self.security_group = security_groups.SecurityGroup()
- self.security_groups = security_groups.SecurityGroups(1)
-
- def tearDown(self):
- super(SecGroupTest, self).tearDown()
- security_groups.SecurityGroup.__init__ = self.orig__init
-
- def test___repr__(self):
- self.security_group.name = "security_group-1"
- self.assertEqual('<SecurityGroup: security_group-1>',
- self.security_group.__repr__())
-
- def test_list(self):
- sec_group_list = ['secgroup1', 'secgroup2']
- self.security_groups.list = Mock(return_value=sec_group_list)
- self.assertEqual(sec_group_list, self.security_groups.list())
-
- def test_get(self):
- def side_effect_func(path, inst):
- return path, inst
-
- self.security_groups._get = Mock(side_effect=side_effect_func)
- self.security_group.id = 1
- self.assertEqual(('/security-groups/1', 'security_group'),
- self.security_groups.get(self.security_group))
-
-
-class SecGroupRuleTest(TestCase):
-
- def setUp(self):
- super(SecGroupRuleTest, self).setUp()
- self.orig__init = security_groups.SecurityGroupRule.__init__
- security_groups.SecurityGroupRule.__init__ = Mock(return_value=None)
- security_groups.SecurityGroupRules.__init__ = Mock(return_value=None)
- self.security_group_rule = security_groups.SecurityGroupRule()
- self.security_group_rules = security_groups.SecurityGroupRules()
-
- def tearDown(self):
- super(SecGroupRuleTest, self).tearDown()
- security_groups.SecurityGroupRule.__init__ = self.orig__init
-
- def test___repr__(self):
- self.security_group_rule.group_id = 1
- self.security_group_rule.protocol = "tcp"
- self.security_group_rule.from_port = 80
- self.security_group_rule.to_port = 80
- self.security_group_rule.cidr = "0.0.0.0//0"
- representation = \
- "<SecurityGroupRule: ( \
- Security Group id: %d, \
- Protocol: %s, \
- From_Port: %d, \
- To_Port: %d, \
- CIDR: %s )>" % (1, "tcp", 80, 80, "0.0.0.0//0")
-
- self.assertEqual(representation,
- self.security_group_rule.__repr__())
-
- def test_create(self):
- def side_effect_func(path, body, inst):
- return path, body, inst
-
- self.security_group_rules._create = Mock(side_effect=side_effect_func)
- p, b, i = self.security_group_rules.create(1, "tcp",
- 80, 80, "0.0.0.0//0")
- self.assertEqual("/security-group-rules", p)
- self.assertEqual("security_group_rule", i)
- self.assertEqual(1, b["security_group_rule"]["group_id"])
- self.assertEqual("tcp", b["security_group_rule"]["protocol"])
- self.assertEqual(80, b["security_group_rule"]["from_port"])
- self.assertEqual(80, b["security_group_rule"]["to_port"])
- self.assertEqual("0.0.0.0//0", b["security_group_rule"]["cidr"])
-
- def test_delete(self):
- resp = Mock()
- resp.status = 200
- body = None
- self.security_group_rules.api = Mock()
- self.security_group_rules.api.client = Mock()
- self.security_group_rules.api.client.delete = \
- Mock(return_value=(resp, body))
- self.security_group_rules.delete(self.id)
- resp.status = 500
- self.assertRaises(ValueError, self.security_group_rules.delete,
- self.id)
diff --git a/reddwarfclient/tests/test_users.py b/reddwarfclient/tests/test_users.py
deleted file mode 100644
index 80be05f..0000000
--- a/reddwarfclient/tests/test_users.py
+++ /dev/null
@@ -1,126 +0,0 @@
-from testtools import TestCase
-from mock import Mock
-
-from reddwarfclient import users
-from reddwarfclient import base
-
-"""
-Unit tests for users.py
-"""
-
-
-class UserTest(TestCase):
-
- def setUp(self):
- super(UserTest, self).setUp()
- self.orig__init = users.User.__init__
- users.User.__init__ = Mock(return_value=None)
- self.user = users.User()
-
- def tearDown(self):
- super(UserTest, self).tearDown()
- users.User.__init__ = self.orig__init
-
- def test___repr__(self):
- self.user.name = "user-1"
- self.assertEqual('<User: user-1>', self.user.__repr__())
-
-
-class UsersTest(TestCase):
-
- def setUp(self):
- super(UsersTest, self).setUp()
- self.orig__init = users.Users.__init__
- users.Users.__init__ = Mock(return_value=None)
- self.users = users.Users()
- self.users.api = Mock()
- self.users.api.client = Mock()
-
- self.orig_base_getid = base.getid
- base.getid = Mock(return_value="instance1")
-
- def tearDown(self):
- super(UsersTest, self).tearDown()
- users.Users.__init__ = self.orig__init
- base.getid = self.orig_base_getid
-
- def _get_mock_method(self):
- self._resp = Mock()
- self._body = None
- self._url = None
-
- def side_effect_func(url, body=None):
- self._body = body
- self._url = url
- return (self._resp, body)
-
- return Mock(side_effect=side_effect_func)
-
- def _build_fake_user(self, name, hostname=None, password=None,
- databases=None):
- return {'name': name,
- 'password': password if password else 'password',
- 'host': hostname,
- 'databases': databases if databases else [],
- }
-
- def test_create(self):
- self.users.api.client.post = self._get_mock_method()
- self._resp.status = 200
- user = self._build_fake_user('user1')
-
- self.users.create(23, [user])
- self.assertEqual('/instances/23/users', self._url)
- self.assertEqual({"users": [user]}, self._body)
-
- # Even if host isn't supplied originally,
- # the default is supplied.
- del user['host']
- self.users.create(23, [user])
- self.assertEqual('/instances/23/users', self._url)
- user['host'] = '%'
- self.assertEqual({"users": [user]}, self._body)
-
- # If host is supplied, of course it's put into the body.
- user['host'] = '127.0.0.1'
- self.users.create(23, [user])
- self.assertEqual({"users": [user]}, self._body)
-
- # Make sure that response of 400 is recognized as an error.
- user['host'] = '%'
- self._resp.status = 400
- self.assertRaises(Exception, self.users.create, 12, [user])
-
- def test_delete(self):
- self.users.api.client.delete = self._get_mock_method()
- self._resp.status = 200
- self.users.delete(27, 'user1')
- self.assertEqual('/instances/27/users/user1', self._url)
- self._resp.status = 400
- self.assertRaises(Exception, self.users.delete, 34, 'user1')
-
- def test__list(self):
- def side_effect_func(self, val):
- return val
-
- key = 'key'
- body = Mock()
- body.get = Mock(return_value=[{'href': 'http://test.net/test_file',
- 'rel': 'next'}])
- body.__getitem__ = Mock(return_value=["test-value"])
-
- resp = Mock()
- resp.status = 200
- self.users.resource_class = Mock(side_effect=side_effect_func)
- self.users.api.client.get = Mock(return_value=(resp, body))
- self.assertEqual(["test-value"], self.users._list('url', key).items)
-
- self.users.api.client.get = Mock(return_value=(resp, None))
- self.assertRaises(Exception, self.users._list, 'url', None)
-
- def test_list(self):
- def side_effect_func(path, user, limit, marker):
- return path
-
- self.users._list = Mock(side_effect=side_effect_func)
- self.assertEqual('/instances/instance1/users', self.users.list(1))
diff --git a/reddwarfclient/tests/test_utils.py b/reddwarfclient/tests/test_utils.py
deleted file mode 100644
index c3a000d..0000000
--- a/reddwarfclient/tests/test_utils.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import os
-from testtools import TestCase
-from reddwarfclient import utils
-from reddwarfclient import versions
-
-
-class UtilsTest(TestCase):
-
- def test_add_hookable_mixin(self):
- def func():
- pass
-
- hook_type = "hook_type"
- mixin = utils.HookableMixin()
- mixin.add_hook(hook_type, func)
- self.assertTrue(hook_type in mixin._hooks_map)
- self.assertTrue(func in mixin._hooks_map[hook_type])
-
- def test_run_hookable_mixin(self):
- def func():
- pass
-
- hook_type = "hook_type"
- mixin = utils.HookableMixin()
- mixin.add_hook(hook_type, func)
- mixin.run_hooks(hook_type)
-
- def test_environment(self):
- self.assertEqual('', utils.env())
- self.assertEqual('passing', utils.env(default='passing'))
-
- os.environ['test_abc'] = 'passing'
- self.assertEqual('passing', utils.env('test_abc'))
- self.assertEqual('', utils.env('test_abcd'))
-
- def test_slugify(self):
- import unicodedata
-
- self.assertEqual('not_unicode', utils.slugify('not_unicode'))
- self.assertEqual('unicode', utils.slugify(unicode('unicode')))
- self.assertEqual('slugify-test', utils.slugify('SLUGIFY% test!'))
diff --git a/reddwarfclient/tests/test_xml.py b/reddwarfclient/tests/test_xml.py
deleted file mode 100644
index cda382b..0000000
--- a/reddwarfclient/tests/test_xml.py
+++ /dev/null
@@ -1,241 +0,0 @@
-from testtools import TestCase
-from lxml import etree
-from reddwarfclient import xml
-
-
-class XmlTest(TestCase):
-
- ELEMENT = '''
- <instances>
- <instance>
- <flavor>
- <links>
- </links>
- <value value="5"/>
- </flavor>
- </instance>
- </instances>
- '''
- ROOT = etree.fromstring(ELEMENT)
-
- JSON = {'instances':
- {'instances': ['1', '2', '3']}, 'dummy': {'dict': True}}
-
- def test_element_ancestors_match_list(self):
- # Test normal operation:
- self.assertTrue(xml.element_ancestors_match_list(self.ROOT[0][0],
- ['instance',
- 'instances']))
-
- # Test itr_elem is None:
- self.assertTrue(xml.element_ancestors_match_list(self.ROOT,
- ['instances']))
-
- # Test that the first parent element does not match the first list
- # element:
- self.assertFalse(xml.element_ancestors_match_list(self.ROOT[0][0],
- ['instances',
- 'instance']))
-
- def test_populate_element_from_dict(self):
- # Test populate_element_from_dict with a None in the data
- ele = '''
- <instance>
- <volume>
- <value size="5"/>
- </volume>
- </instance>
- '''
- rt = etree.fromstring(ele)
-
- self.assertEqual(None, xml.populate_element_from_dict(rt,
- {'size': None}))
-
- def test_element_must_be_list(self):
- # Test for when name isn't in the dictionary
- self.assertFalse(xml.element_must_be_list(self.ROOT, "not_in_list"))
-
- # Test when name is in the dictionary but list is empty
- self.assertTrue(xml.element_must_be_list(self.ROOT, "accounts"))
-
- # Test when name is in the dictionary but list is not empty
- self.assertTrue(xml.element_must_be_list(self.ROOT[0][0][0], "links"))
-
- def test_element_to_json(self):
- # Test when element must be list:
- self.assertEqual([{'flavor': {'links': [], 'value': {'value': '5'}}}],
- xml.element_to_json("accounts", self.ROOT))
-
- # Test when element must not be list:
- exp = {'instance': {'flavor': {'links': [], 'value': {'value': '5'}}}}
- self.assertEqual(exp, xml.element_to_json("not_in_list", self.ROOT))
-
- def test_root_element_to_json(self):
- # Test when element must be list:
- exp = ([{'flavor': {'links': [], 'value': {'value': '5'}}}], None)
- self.assertEqual(exp, xml.root_element_to_json("accounts", self.ROOT))
-
- # Test when element must not be list:
- exp = {'instance': {'flavor': {'links': [], 'value': {'value': '5'}}}}
- self.assertEqual((exp, None),
- xml.root_element_to_json("not_in_list", self.ROOT))
-
- # Test rootEnabled True:
- t_element = etree.fromstring('''<rootEnabled> True </rootEnabled>''')
- self.assertEqual((True, None),
- xml.root_element_to_json("rootEnabled", t_element))
-
- # Test rootEnabled False:
- f_element = etree.fromstring('''<rootEnabled> False </rootEnabled>''')
- self.assertEqual((False, None),
- xml.root_element_to_json("rootEnabled", f_element))
-
- def test_element_to_list(self):
- # Test w/ no child elements
- self.assertEqual([], xml.element_to_list(self.ROOT[0][0][0]))
-
- # Test w/ no child elements and check_for_links = True
- self.assertEqual(([], None),
- xml.element_to_list(self.ROOT[0][0][0],
- check_for_links=True))
-
- # Test w/ child elements
- self.assertEqual([{}, {'value': '5'}],
- xml.element_to_list(self.ROOT[0][0]))
-
- # Test w/ child elements and check_for_links = True
- self.assertEqual(([{'value': '5'}], []),
- xml.element_to_list(self.ROOT[0][0],
- check_for_links=True))
-
- def test_element_to_dict(self):
- # Test when there is not a None
- exp = {'instance': {'flavor': {'links': [], 'value': {'value': '5'}}}}
- self.assertEqual(exp, xml.element_to_dict(self.ROOT))
-
- # Test when there is a None
- element = '''
- <server>
- None
- </server>
- '''
- rt = etree.fromstring(element)
- self.assertEqual(None, xml.element_to_dict(rt))
-
- def test_standarize_json(self):
- xml.standardize_json_lists(self.JSON)
- self.assertEqual({'instances': ['1', '2', '3'],
- 'dummy': {'dict': True}}, self.JSON)
-
- def test_normalize_tag(self):
- ELEMENT_NS = '''
- <instances xmlns="http://www.w3.org/1999/xhtml">
- <instance>
- <flavor>
- <links>
- </links>
- <value value="5"/>
- </flavor>
- </instance>
- </instances>
- '''
- ROOT_NS = etree.fromstring(ELEMENT_NS)
-
- # Test normalizing without namespace info
- self.assertEqual('instances', xml.normalize_tag(self.ROOT))
-
- # Test normalizing with namespace info
- self.assertEqual('instances', xml.normalize_tag(ROOT_NS))
-
- def test_create_root_xml_element(self):
- # Test creating when name is not in REQUEST_AS_LIST
- element = xml.create_root_xml_element("root", {"root": "value"})
- exp = '<root xmlns="http://docs.openstack.org/database/api/v1.0" ' \
- 'root="value"/>'
- self.assertEqual(exp, etree.tostring(element))
-
- # Test creating when name is in REQUEST_AS_LIST
- element = xml.create_root_xml_element("users", [])
- exp = '<users xmlns="http://docs.openstack.org/database/api/v1.0"/>'
- self.assertEqual(exp, etree.tostring(element))
-
- def test_creating_subelements(self):
- # Test creating a subelement as a dictionary
- element = xml.create_root_xml_element("root", {"root": 5})
- xml.create_subelement(element, "subelement", {"subelement": "value"})
- exp = '<root xmlns="http://docs.openstack.org/database/api/v1.0" ' \
- 'root="5"><subelement subelement="value"/></root>'
- self.assertEqual(exp, etree.tostring(element))
-
- # Test creating a subelement as a list
- element = xml.create_root_xml_element("root",
- {"root": {"value": "nested"}})
- xml.create_subelement(element, "subelement", [{"subelement": "value"}])
- exp = '<root xmlns="http://docs.openstack.org/database/api/v1.0">' \
- '<root value="nested"/><subelement><subelement subelement=' \
- '"value"/></subelement></root>'
- self.assertEqual(exp, etree.tostring(element))
-
- # Test creating a subelement as a string (should raise TypeError)
- element = xml.create_root_xml_element("root", {"root": "value"})
- try:
- xml.create_subelement(element, "subelement", ["value"])
- self.fail("TypeError exception expected")
- except TypeError:
- pass
-
- def test_modify_response_types(self):
- TYPE_MAP = {
- "Int": int,
- "Bool": bool
- }
- #Is a string True
- self.assertEqual(True, xml.modify_response_types('True', TYPE_MAP))
-
- #Is a string False
- self.assertEqual(False, xml.modify_response_types('False', TYPE_MAP))
-
- #Is a dict
- test_dict = {"Int": "5"}
- test_dict = xml.modify_response_types(test_dict, TYPE_MAP)
- self.assertEqual(int, test_dict["Int"].__class__)
-
- #Is a list
- test_list = {"a_list": [{"Int": "5"}, {"Str": "A"}]}
- test_list = xml.modify_response_types(test_list["a_list"], TYPE_MAP)
- self.assertEqual([{'Int': 5}, {'Str': 'A'}], test_list)
-
- def test_reddwarfxmlclient(self):
- from reddwarfclient import exceptions
-
- client = xml.ReddwarfXmlClient("user", "password", "tenant",
- "auth_url", "service_name",
- auth_strategy="fake")
- request = {'headers': {}}
-
- # Test morph_request, no body
- client.morph_request(request)
- self.assertEqual('application/xml', request['headers']['Accept'])
- self.assertEqual('application/xml', request['headers']['Content-Type'])
-
- # Test morph_request, with body
- request['body'] = {'root': {'test': 'test'}}
- client.morph_request(request)
- body = '<root xmlns="http://docs.openstack.org/database/api/v1.0" ' \
- 'test="test"/>\n'
- exp = {'body': body,
- 'headers': {'Content-Type': 'application/xml',
- 'Accept': 'application/xml'}}
- self.assertEqual(exp, request)
-
- # Test morph_response_body
- request = "<users><links><user href='value'/></links></users>"
- result = client.morph_response_body(request)
- self.assertEqual({'users': [], 'links': [{'href': 'value'}]}, result)
-
- # Test morph_response_body with improper input
- try:
- client.morph_response_body("value")
- self.fail("ResponseFormatError exception expected")
- except exceptions.ResponseFormatError:
- pass
diff --git a/reddwarfclient/users.py b/reddwarfclient/users.py
deleted file mode 100644
index 344252c..0000000
--- a/reddwarfclient/users.py
+++ /dev/null
@@ -1,127 +0,0 @@
-# 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.
-
-from reddwarfclient import base
-from reddwarfclient import databases
-from reddwarfclient.common import check_for_exceptions
-from reddwarfclient.common import limit_url
-from reddwarfclient.common import Paginated
-from reddwarfclient.common import quote_user_host
-import exceptions
-import urlparse
-
-
-class User(base.Resource):
- """
- A database user
- """
- def __repr__(self):
- return "<User: %s>" % self.name
-
-
-class Users(base.ManagerWithFind):
- """
- Manage :class:`Users` resources.
- """
- resource_class = User
-
- def create(self, instance_id, users):
- """
- Create users with permissions to the specified databases
- """
- body = {"users": users}
- url = "/instances/%s/users" % instance_id
- resp, body = self.api.client.post(url, body=body)
- check_for_exceptions(resp, body)
-
- def delete(self, instance_id, username, hostname=None):
- """Delete an existing user in the specified instance"""
- user = quote_user_host(username, hostname)
- url = "/instances/%s/users/%s" % (instance_id, user)
- resp, body = self.api.client.delete(url)
- check_for_exceptions(resp, body)
-
- def _list(self, url, response_key, limit=None, marker=None):
- resp, body = self.api.client.get(limit_url(url, limit, marker))
- check_for_exceptions(resp, body)
- if not body:
- raise Exception("Call to " + url +
- " did not return a body.")
- links = body.get('links', [])
- next_links = [link['href'] for link in links if link['rel'] == 'next']
- next_marker = None
- for link in next_links:
- # Extract the marker from the url.
- parsed_url = urlparse.urlparse(link)
- query_dict = dict(urlparse.parse_qsl(parsed_url.query))
- next_marker = query_dict.get('marker', None)
- users = [self.resource_class(self, res) for res in body[response_key]]
- return Paginated(users, next_marker=next_marker, links=links)
-
- def list(self, instance, limit=None, marker=None):
- """
- Get a list of all Users from the instance's Database.
-
- :rtype: list of :class:`User`.
- """
- return self._list("/instances/%s/users" % base.getid(instance),
- "users", limit, marker)
-
- def get(self, instance_id, username, hostname=None):
- """
- Get a single User from the instance's Database.
-
- :rtype: :class:`User`.
- """
- user = quote_user_host(username, hostname)
- url = "/instances/%s/users/%s" % (instance_id, user)
- return self._get(url, "user")
-
- def list_access(self, instance, username, hostname=None):
- """Show all databases the given user has access to. """
- instance_id = base.getid(instance)
- user = quote_user_host(username, hostname)
- url = "/instances/%(instance_id)s/users/%(user)s/databases"
- resp, body = self.api.client.get(url % locals())
- check_for_exceptions(resp, body)
- if not body:
- raise Exception("Call to %s did not return to a body" % url)
- return [databases.Database(self, db) for db in body['databases']]
-
- def grant(self, instance, username, databases, hostname=None):
- """Allow an existing user permissions to access a database."""
- instance_id = base.getid(instance)
- user = quote_user_host(username, hostname)
- url = "/instances/%(instance_id)s/users/%(user)s/databases"
- dbs = {'databases': [{'name': db} for db in databases]}
- resp, body = self.api.client.put(url % locals(), body=dbs)
- check_for_exceptions(resp, body)
-
- def revoke(self, instance, username, database, hostname=None):
- """Revoke from an existing user access permissions to a database."""
- instance_id = base.getid(instance)
- user = quote_user_host(username, hostname)
- url = ("/instances/%(instance_id)s/users/%(user)s/"
- "databases/%(database)s")
- resp, body = self.api.client.delete(url % locals())
- check_for_exceptions(resp, body)
-
- def change_passwords(self, instance, users):
- """Change the password for one or more users."""
- instance_id = base.getid(instance)
- user_dict = {"users": users}
- url = "/instances/%s/users" % instance_id
- resp, body = self.api.client.put(url, body=user_dict)
- check_for_exceptions(resp, body)
diff --git a/reddwarfclient/utils.py b/reddwarfclient/utils.py
deleted file mode 100644
index 3deb806..0000000
--- a/reddwarfclient/utils.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# Copyright 2012 OpenStack LLC
-#
-# 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 os
-import re
-import sys
-
-
-class HookableMixin(object):
- """Mixin so classes can register and run hooks."""
- _hooks_map = {}
-
- @classmethod
- def add_hook(cls, hook_type, hook_func):
- if hook_type not in cls._hooks_map:
- cls._hooks_map[hook_type] = []
-
- cls._hooks_map[hook_type].append(hook_func)
-
- @classmethod
- def run_hooks(cls, hook_type, *args, **kwargs):
- hook_funcs = cls._hooks_map.get(hook_type) or []
- for hook_func in hook_funcs:
- hook_func(*args, **kwargs)
-
-
-def env(*vars, **kwargs):
- """
- returns the first environment variable set
- if none are non-empty, defaults to '' or keyword arg default
- """
- for v in vars:
- value = os.environ.get(v, None)
- if value:
- return value
- return kwargs.get('default', '')
-
-
-_slugify_strip_re = re.compile(r'[^\w\s-]')
-_slugify_hyphenate_re = re.compile(r'[-\s]+')
-
-
-# http://code.activestate.com/recipes/
-# 577257-slugify-make-a-string-usable-in-a-url-or-filename/
-def slugify(value):
- """
- Normalizes string, converts to lowercase, removes non-alpha characters,
- and converts spaces to hyphens.
-
- From Django's "django/template/defaultfilters.py".
- """
- import unicodedata
- if not isinstance(value, unicode):
- value = unicode(value)
- value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
- value = unicode(_slugify_strip_re.sub('', value).strip().lower())
- return _slugify_hyphenate_re.sub('-', value)
diff --git a/reddwarfclient/versions.py b/reddwarfclient/versions.py
deleted file mode 100644
index f7b52c4..0000000
--- a/reddwarfclient/versions.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# 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.
-
-from reddwarfclient import base
-
-
-class Version(base.Resource):
- """
- Version is an opaque instance used to hold version information.
- """
- def __repr__(self):
- return "<Version: %s>" % self.id
-
-
-class Versions(base.ManagerWithFind):
- """
- Manage :class:`Versions` information.
- """
-
- resource_class = Version
-
- def index(self, url):
- """
- Get a list of all versions.
-
- :rtype: list of :class:`Versions`.
- """
- resp, body = self.api.client.request(url, "GET")
- return [self.resource_class(self, res) for res in body['versions']]
diff --git a/reddwarfclient/xml.py b/reddwarfclient/xml.py
deleted file mode 100644
index fe94f19..0000000
--- a/reddwarfclient/xml.py
+++ /dev/null
@@ -1,293 +0,0 @@
-from lxml import etree
-import json
-from numbers import Number
-
-from reddwarfclient import exceptions
-from reddwarfclient.client import ReddwarfHTTPClient
-
-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 <foo><blah><root></blah></foo> 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 <foos><foo/><foo/></foos>
- 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 ReddwarfXmlClient(ReddwarfHTTPClient):
-
- @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