diff options
38 files changed, 646 insertions, 916 deletions
@@ -6,6 +6,7 @@ build cover .testrepository .venv +doc/build subunit.log AUTHORS ChangeLog diff --git a/ceilometerclient/client.py b/ceilometerclient/client.py index b66e547..e4568a4 100644 --- a/ceilometerclient/client.py +++ b/ceilometerclient/client.py @@ -14,10 +14,11 @@ from keystoneclient.auth.identity import v2 as v2_auth from keystoneclient.auth.identity import v3 as v3_auth from keystoneclient import discover from keystoneclient import session -import six from ceilometerclient.common import utils from ceilometerclient import exc +from ceilometerclient.openstack.common.apiclient import auth +from ceilometerclient.openstack.common.apiclient import exceptions def _get_keystone_session(**kwargs): @@ -139,12 +140,94 @@ def _get_endpoint(ks_session, **kwargs): return endpoint -def get_client(api_version, **kwargs): +class AuthPlugin(auth.BaseAuthPlugin): + opt_names = ['tenant_id', 'region_name', 'auth_token', + 'service_type', 'endpoint_type', 'cacert', + 'auth_url', 'insecure', 'cert_file', 'key_file', + 'cert', 'key', 'tenant_name', 'project_name', + 'project_id', 'user_domain_id', 'user_domain_name', + 'password', 'username', 'endpoint'] + + def __init__(self, auth_system=None, **kwargs): + self.opt_names.extend(self.common_opt_names) + super(AuthPlugin, self).__init__(auth_system, **kwargs) + + def _do_authenticate(self, http_client): + if self.opts.get('token') and self.opts.get('endpoint'): + token = self.opts.get('token') + endpoint = self.opts.get('endpoint') + else: + project_id = self.opts.get('project_id') \ + or self.opts.get('tenant_id') + project_name = (self.opts.get('project_name') or + self.opts.get('tenant_name')) + ks_kwargs = { + 'username': self.opts.get('username'), + 'password': self.opts.get('password'), + 'user_id': self.opts.get('user_id'), + 'user_domain_id': self.opts.get('user_domain_id'), + 'user_domain_name': self.opts.get('user_domain_name'), + 'project_id': project_id, + 'project_name': project_name, + 'project_domain_name': self.opts.get('project_domain_name'), + 'project_domain_id': self.opts.get('project_domain_id'), + 'auth_url': self.opts.get('auth_url'), + 'cacert': self.opts.get('cacert'), + 'cert': self.opts.get('cert'), + 'key': self.opts.get('key'), + 'insecure': self.opts.get('insecure') + } + + # retrieve session + ks_session = _get_keystone_session(**ks_kwargs) + token = lambda: ks_session.get_token() + endpoint = self.opts.get('endpoint') or \ + _get_endpoint(ks_session, **ks_kwargs) + self.opts['token'] = token() + self.opts['endpoint'] = endpoint + + def token_and_endpoint(self, endpoint_type, service_type): + token = self.opts.get('token') + if callable(token): + token = token() + return token, self.opts.get('endpoint') + + def sufficient_options(self): + """Check if all required options are present. + + :raises: AuthPluginOptionsMissing + """ + missing = not ((self.opts.get('token') and + self.opts.get('endpoint')) or + (self.opts.get('username') + and self.opts.get('password') + and self.opts.get('auth_url') and + (self.opts.get('tenant_id') + or self.opts.get('tenant_name')))) + + if missing: + missing_opts = [] + opts = ['token', 'endpoint', 'username', 'password', 'auth_url', + 'tenant_id', 'tenant_name'] + for opt in opts: + if not self.opts.get(opt): + missing_opts.append(opt) + raise exceptions.AuthPluginOptionsMissing(missing_opts) + + +def Client(version, *args, **kwargs): + module = utils.import_versioned_module(version, 'client') + client_class = getattr(module, 'Client') + return client_class(*args, **kwargs) + + +def get_client(version, **kwargs): """Get an authtenticated client, based on the credentials - in the keyword args. + in the keyword args. :param api_version: the API version to use ('1' or '2') :param kwargs: keyword args containing credentials, either: + * os_auth_token: pre-existing token to re-use * ceilometer_url: ceilometer API endpoint or: @@ -164,53 +247,38 @@ def get_client(api_version, **kwargs): * os_key: SSL private key * insecure: allow insecure SSL (no cert verification) """ - token = kwargs.get('os_auth_token') - if token and not six.callable(token): - token = lambda: kwargs.get('os_auth_token') - - if token and kwargs.get('ceilometer_url'): - endpoint = kwargs.get('ceilometer_url') - else: - project_id = kwargs.get('os_project_id') or kwargs.get('os_tenant_id') - project_name = (kwargs.get('os_project_name') or - kwargs.get('os_tenant_name')) - ks_kwargs = { - 'username': kwargs.get('os_username'), - 'password': kwargs.get('os_password'), - 'user_id': kwargs.get('os_user_id'), - 'user_domain_id': kwargs.get('os_user_domain_id'), - 'user_domain_name': kwargs.get('os_user_domain_name'), - 'project_id': project_id, - 'project_name': project_name, - 'project_domain_name': kwargs.get('os_project_domain_name'), - 'project_domain_id': kwargs.get('os_project_domain_id'), - 'auth_url': kwargs.get('os_auth_url'), - 'cacert': kwargs.get('os_cacert'), - 'cert': kwargs.get('os_cert'), - 'key': kwargs.get('os_key'), - 'insecure': kwargs.get('insecure') - } - - # retrieve session - ks_session = _get_keystone_session(**ks_kwargs) - token = token or (lambda: ks_session.get_token()) - - endpoint = kwargs.get('ceilometer_url') or \ - _get_endpoint(ks_session, **ks_kwargs) + endpoint = kwargs.get('ceilometer_url') cli_kwargs = { - 'token': token, - 'insecure': kwargs.get('insecure'), - 'timeout': kwargs.get('timeout'), + 'username': kwargs.get('os_username'), + 'password': kwargs.get('os_password'), + 'tenant_id': kwargs.get('os_tenant_id'), + 'tenant_name': kwargs.get('os_tenant_name'), + 'auth_url': kwargs.get('os_auth_url'), + 'region_name': kwargs.get('os_region_name'), + 'service_type': kwargs.get('os_service_type'), + 'endpoint_type': kwargs.get('os_endpoint_type'), 'cacert': kwargs.get('os_cacert'), 'cert_file': kwargs.get('os_cert'), - 'key_file': kwargs.get('os_key') + 'key_file': kwargs.get('os_key'), + 'token': kwargs.get('os_auth_token'), } - return Client(api_version, endpoint, **cli_kwargs) + cli_kwargs.update(kwargs) + return Client(version, endpoint, **cli_kwargs) -def Client(version, *args, **kwargs): - module = utils.import_versioned_module(version, 'client') - client_class = getattr(module, 'Client') - return client_class(*args, **kwargs) +def get_auth_plugin(endpoint, **kwargs): + auth_plugin = AuthPlugin( + auth_url=kwargs.get('auth_url'), + service_type=kwargs.get('service_type'), + token=kwargs.get('token'), + endpoint_type=kwargs.get('endpoint_type'), + cacert=kwargs.get('ca_file'), + tenant_id=kwargs.get('project_id') or kwargs.get('tenant_id'), + endpoint=endpoint, + username=kwargs.get('username'), + password=kwargs.get('password'), + tenant_name=kwargs.get('tenant_name'), + ) + return auth_plugin diff --git a/ceilometerclient/common/base.py b/ceilometerclient/common/base.py index a01adbf..7d4be24 100644 --- a/ceilometerclient/common/base.py +++ b/ceilometerclient/common/base.py @@ -49,13 +49,13 @@ class Manager(object): self.api = api def _create(self, url, body): - resp, body = self.api.json_request('POST', url, body=body) + body = self.api.post(url, json=body).json() if body: return self.resource_class(self, body) def _list(self, url, response_key=None, obj_class=None, body=None, expect_single=False): - resp, body = self.api.json_request('GET', url) + body = self.api.get(url).json() if obj_class is None: obj_class = self.resource_class @@ -72,13 +72,13 @@ class Manager(object): return [obj_class(self, res, loaded=True) for res in data if res] def _update(self, url, body, response_key=None): - resp, body = self.api.json_request('PUT', url, body=body) + body = self.api.put(url, json=body).json() # PUT requests may not return a body if body: return self.resource_class(self, body) def _delete(self, url): - self.api.raw_request('DELETE', url) + self.api.delete(url) class Resource(base.Resource): diff --git a/ceilometerclient/common/http.py b/ceilometerclient/common/http.py deleted file mode 100644 index f440abe..0000000 --- a/ceilometerclient/common/http.py +++ /dev/null @@ -1,301 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# 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 copy -import logging -import os -import socket - -try: - import ssl -except ImportError: - # TODO(bcwaldon): Handle this failure more gracefully - pass - -try: - import json -except ImportError: - import simplejson as json - -import six -from six.moves import http_client as httplib # noqa -from six.moves import urllib - -from ceilometerclient import exc - - -LOG = logging.getLogger(__name__) -USER_AGENT = 'python-ceilometerclient' -CHUNKSIZE = 1024 * 64 # 64kB - - -class HTTPClient(object): - - def __init__(self, endpoint, **kwargs): - self.endpoint = endpoint - self.auth_token = kwargs.get('token') - self.connection_params = self.get_connection_params(endpoint, **kwargs) - self.proxy_url = self.get_proxy_url() - - @staticmethod - def get_connection_params(endpoint, **kwargs): - parts = urllib.parse.urlparse(endpoint) - - _args = (parts.hostname, parts.port, parts.path) - _kwargs = {'timeout': (float(kwargs.get('timeout')) - if kwargs.get('timeout') else 600)} - - if parts.scheme == 'https': - _class = VerifiedHTTPSConnection - _kwargs['cacert'] = kwargs.get('cacert', None) - _kwargs['cert_file'] = kwargs.get('cert_file', None) - _kwargs['key_file'] = kwargs.get('key_file', None) - _kwargs['insecure'] = kwargs.get('insecure', False) - elif parts.scheme == 'http': - _class = httplib.HTTPConnection - else: - msg = 'Unsupported scheme: %s' % parts.scheme - raise exc.InvalidEndpoint(msg) - - return (_class, _args, _kwargs) - - def get_connection(self): - _class = self.connection_params[0] - try: - if self.proxy_url: - proxy_parts = urllib.parse.urlparse(self.proxy_url) - return _class(proxy_parts.hostname, proxy_parts.port, - **self.connection_params[2]) - else: - return _class(*self.connection_params[1][0:2], - **self.connection_params[2]) - except httplib.InvalidURL: - raise exc.InvalidEndpoint() - - def log_curl_request(self, method, url, kwargs): - curl = ['curl -i -X %s' % method] - - for (key, value) in kwargs['headers'].items(): - header = '-H \'%s: %s\'' % (key, value) - curl.append(header) - - conn_params_fmt = [ - ('key_file', '--key %s'), - ('cert_file', '--cert %s'), - ('cacert', '--cacert %s'), - ] - for (key, fmt) in conn_params_fmt: - value = self.connection_params[2].get(key) - if value: - curl.append(fmt % value) - - if self.connection_params[2].get('insecure'): - curl.append('-k') - - if 'body' in kwargs: - curl.append('-d \'%s\'' % kwargs['body']) - - curl.append('%s/%s' % (self.endpoint.rstrip('/'), url.lstrip('/'))) - LOG.debug(' '.join(curl)) - - @staticmethod - def log_http_response(resp, body=None): - status = (resp.version / 10.0, resp.status, resp.reason) - dump = ['\nHTTP/%.1f %s %s' % status] - dump.extend(['%s: %s' % (k, v) for k, v in resp.getheaders()]) - dump.append('') - if body: - dump.extend([body, '']) - LOG.debug('\n'.join(dump)) - - def _make_connection_url(self, url): - (_class, _args, _kwargs) = self.connection_params - base_url = _args[2] - return '%s/%s' % (base_url.rstrip('/'), url.lstrip('/')) - - def _http_request(self, url, method, **kwargs): - """Send an http request with the specified characteristics. - - Wrapper around httplib.HTTP(S)Connection.request to handle tasks such - as setting headers and error handling. - """ - # Copy the kwargs so we can reuse the original in case of redirects - kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {})) - kwargs['headers'].setdefault('User-Agent', USER_AGENT) - auth_token = self.auth_token() - if auth_token: - kwargs['headers'].setdefault('X-Auth-Token', auth_token) - - self.log_curl_request(method, url, kwargs) - conn = self.get_connection() - - try: - if self.proxy_url: - conn_url = (self.endpoint.rstrip('/') + - self._make_connection_url(url)) - else: - conn_url = self._make_connection_url(url) - conn.request(method, conn_url, **kwargs) - resp = conn.getresponse() - except socket.gaierror as e: - message = ("Error finding address for %(url)s: %(e)s" - % dict(url=url, e=e)) - raise exc.InvalidEndpoint(message=message) - except (socket.error, socket.timeout) as e: - endpoint = self.endpoint - message = ("Error communicating with %(endpoint)s %(e)s" - % dict(endpoint=endpoint, e=e)) - raise exc.CommunicationError(message=message) - - body_iter = ResponseBodyIterator(resp) - - # Read body into string if it isn't obviously image data - if resp.getheader('content-type', None) != 'application/octet-stream': - body_str = ''.join([chunk for chunk in body_iter]) - self.log_http_response(resp, body_str) - body_iter = six.StringIO(body_str) - else: - self.log_http_response(resp) - - if 400 <= resp.status < 600: - LOG.warn("Request returned failure status.") - raise exc.from_response(resp, ''.join(body_iter)) - elif resp.status in (301, 302, 305): - # Redirected. Reissue the request to the new location. - return self._http_request(resp['location'], method, **kwargs) - elif resp.status == 300: - raise exc.from_response(resp) - - return resp, body_iter - - def json_request(self, method, url, **kwargs): - kwargs.setdefault('headers', {}) - kwargs['headers'].setdefault('Content-Type', 'application/json') - kwargs['headers'].setdefault('Accept', 'application/json') - - if 'body' in kwargs: - kwargs['body'] = json.dumps(kwargs['body']) - - resp, body_iter = self._http_request(url, method, **kwargs) - content_type = resp.getheader('content-type', None) - - if resp.status == 204 or resp.status == 205 or content_type is None: - return resp, list() - - if 'application/json' in content_type: - body = ''.join([chunk for chunk in body_iter]) - try: - body = json.loads(body) - except ValueError: - LOG.error('Could not decode response body as JSON') - else: - body = None - - return resp, body - - def raw_request(self, method, url, **kwargs): - kwargs.setdefault('headers', {}) - kwargs['headers'].setdefault('Content-Type', - 'application/octet-stream') - return self._http_request(url, method, **kwargs) - - def get_proxy_url(self): - scheme = urllib.parse.urlparse(self.endpoint).scheme - if scheme == 'https': - return os.environ.get('https_proxy') - elif scheme == 'http': - return os.environ.get('http_proxy') - msg = 'Unsupported scheme: %s' % scheme - raise exc.InvalidEndpoint(msg) - - -class VerifiedHTTPSConnection(httplib.HTTPSConnection): - """httplib-compatibile connection using client-side SSL authentication - - :see http://code.activestate.com/recipes/ - 577548-https-httplib-client-connection-with-certificate-v/ - """ - - def __init__(self, host, port, key_file=None, cert_file=None, - cacert=None, timeout=None, insecure=False): - httplib.HTTPSConnection.__init__(self, host, port, key_file=key_file, - cert_file=cert_file) - self.key_file = key_file - self.cert_file = cert_file - if cacert is not None: - self.cacert = cacert - else: - self.cacert = self.get_system_ca_file() - self.timeout = timeout - self.insecure = insecure - - def connect(self): - """Connect to a host on a given (SSL) port. - If cacert is pointing somewhere, use it to check Server Certificate. - - Redefined/copied and extended from httplib.py:1105 (Python 2.6.x). - This is needed to pass cert_reqs=ssl.CERT_REQUIRED as parameter to - ssl.wrap_socket(), which forces SSL to check server certificate against - our client certificate. - """ - sock = socket.create_connection((self.host, self.port), self.timeout) - - if self._tunnel_host: - self.sock = sock - self._tunnel() - - if self.insecure is True: - kwargs = {'cert_reqs': ssl.CERT_NONE} - else: - kwargs = {'cert_reqs': ssl.CERT_REQUIRED, 'ca_certs': self.cacert} - - if self.cert_file: - kwargs['certfile'] = self.cert_file - if self.key_file: - kwargs['keyfile'] = self.key_file - - self.sock = ssl.wrap_socket(sock, **kwargs) - - @staticmethod - def get_system_ca_file(): - """Return path to system default CA file.""" - # Standard CA file locations for Debian/Ubuntu, RedHat/Fedora, - # Suse, FreeBSD/OpenBSD - ca_path = ['/etc/ssl/certs/ca-certificates.crt', - '/etc/pki/tls/certs/ca-bundle.crt', - '/etc/ssl/ca-bundle.pem', - '/etc/ssl/cert.pem'] - for ca in ca_path: - if os.path.exists(ca): - return ca - return None - - -class ResponseBodyIterator(object): - """A class that acts as an iterator over an HTTP response.""" - - def __init__(self, resp): - self.resp = resp - - def __iter__(self): - while True: - yield self.next() - - def next(self): - chunk = self.resp.read(CHUNKSIZE) - if chunk: - return chunk - else: - raise StopIteration() diff --git a/ceilometerclient/shell.py b/ceilometerclient/shell.py index 46a460c..138dd62 100644 --- a/ceilometerclient/shell.py +++ b/ceilometerclient/shell.py @@ -32,157 +32,6 @@ from ceilometerclient.openstack.common import strutils class CeilometerShell(object): - def _append_identity_args(self, parser): - # FIXME(fabgia): identity related parameters should be passed by the - # Keystone client itself to avoid constant update in all the services - # clients. When this fix is merged this method can be made obsolete. - # Bug: https://bugs.launchpad.net/python-keystoneclient/+bug/1332337 - parser.add_argument('-k', '--insecure', - default=False, - action='store_true', - help="Explicitly allow ceilometerclient to " - "perform \"insecure\" SSL (https) requests. " - "The server's certificate will " - "not be verified against any certificate " - "authorities. This option should be used with " - "caution.") - - # User related options - parser.add_argument('--os-username', - default=cliutils.env('OS_USERNAME'), - help='Defaults to env[OS_USERNAME].') - - parser.add_argument('--os_username', - help=argparse.SUPPRESS) - - parser.add_argument('--os-user-id', - default=cliutils.env('OS_USER_ID'), - help='Defaults to env[OS_USER_ID].') - - parser.add_argument('--os-password', - default=cliutils.env('OS_PASSWORD'), - help='Defaults to env[OS_PASSWORD].') - - parser.add_argument('--os_password', - help=argparse.SUPPRESS) - - # Domain related options - parser.add_argument('--os-user-domain-id', - default=cliutils.env('OS_USER_DOMAIN_ID'), - help='Defaults to env[OS_USER_DOMAIN_ID].') - - parser.add_argument('--os-user-domain-name', - default=cliutils.env('OS_USER_DOMAIN_NAME'), - help='Defaults to env[OS_USER_DOMAIN_NAME].') - - parser.add_argument('--os-project-domain-id', - default=cliutils.env('OS_PROJECT_DOMAIN_ID'), - help='Defaults to env[OS_PROJECT_DOMAIN_ID].') - - parser.add_argument('--os-project-domain-name', - default=cliutils.env('OS_PROJECT_DOMAIN_NAME'), - help='Defaults to env[OS_PROJECT_DOMAIN_NAME].') - - # Project V3 or Tenant V2 related options - parser.add_argument('--os-project-id', - default=cliutils.env('OS_PROJECT_ID'), - help='Another way to specify tenant ID. ' - 'This option is mutually exclusive with ' - ' --os-tenant-id. ' - 'Defaults to env[OS_PROJECT_ID].') - - parser.add_argument('--os-project-name', - default=cliutils.env('OS_PROJECT_NAME'), - help='Another way to specify tenant name. ' - 'This option is mutually exclusive with ' - ' --os-tenant-name. ' - 'Defaults to env[OS_PROJECT_NAME].') - - parser.add_argument('--os-tenant-id', - default=cliutils.env('OS_TENANT_ID'), - help='This option is mutually exclusive with ' - ' --os-project-id. ' - 'Defaults to env[OS_PROJECT_ID].') - - parser.add_argument('--os_tenant_id', - help=argparse.SUPPRESS) - - parser.add_argument('--os-tenant-name', - default=cliutils.env('OS_TENANT_NAME'), - help='Defaults to env[OS_TENANT_NAME].') - - parser.add_argument('--os_tenant_name', - help=argparse.SUPPRESS) - - # Auth related options - parser.add_argument('--os-auth-url', - default=cliutils.env('OS_AUTH_URL'), - help='Defaults to env[OS_AUTH_URL].') - - parser.add_argument('--os_auth_url', - help=argparse.SUPPRESS) - - parser.add_argument('--os-auth-token', - default=cliutils.env('OS_AUTH_TOKEN'), - help='Defaults to env[OS_AUTH_TOKEN].') - - parser.add_argument('--os_auth_token', - help=argparse.SUPPRESS) - - parser.add_argument('--os-cacert', - metavar='<ca-certificate-file>', - dest='os_cacert', - default=cliutils.env('OS_CACERT'), - help='Path of CA TLS certificate(s) used to verify' - 'the remote server\'s certificate. Without this ' - 'option ceilometer looks for the default system ' - 'CA certificates.') - - parser.add_argument('--os-cert', - help='Path of certificate file to use in SSL ' - 'connection. This file can optionally be ' - 'prepended with the private key.') - - parser.add_argument('--os-key', - help='Path of client key to use in SSL ' - 'connection. This option is not necessary ' - 'if your key is prepended to your cert file.') - - # Service Catalog related options - parser.add_argument('--os-service-type', - default=cliutils.env('OS_SERVICE_TYPE'), - help='Defaults to env[OS_SERVICE_TYPE].') - - parser.add_argument('--os_service_type', - help=argparse.SUPPRESS) - - parser.add_argument('--os-endpoint-type', - default=cliutils.env('OS_ENDPOINT_TYPE'), - help='Defaults to env[OS_ENDPOINT_TYPE].') - - parser.add_argument('--os_endpoint_type', - help=argparse.SUPPRESS) - - parser.add_argument('--os-region-name', - default=cliutils.env('OS_REGION_NAME'), - help='Defaults to env[OS_REGION_NAME].') - - parser.add_argument('--os_region_name', - help=argparse.SUPPRESS) - - # Deprecated options - parser.add_argument('--ca-file', - dest='os_cacert', - help='DEPRECATED! Use --os-cacert.') - - parser.add_argument('--cert-file', - dest='os_cert', - help='DEPRECATED! Use --os-cert.') - - parser.add_argument('--key-file', - dest='os_key', - help='DEPRECATED! Use --os-key.') - def get_base_parser(self): parser = argparse.ArgumentParser( prog='ceilometer', @@ -233,9 +82,8 @@ class CeilometerShell(object): parser.add_argument('--ceilometer_api_version', help=argparse.SUPPRESS) - # FIXME(fabgia): identity related parameters should be passed by the - # Keystone client itself. - self._append_identity_args(parser) + self.auth_plugin.add_opts(parser) + self.auth_plugin.add_common_opts(parser) return parser @@ -281,16 +129,20 @@ class CeilometerShell(object): subparser.set_defaults(func=callback) def _setup_logging(self, debug): - format = '%(levelname)s (%(module)s:%(lineno)d) %(message)s' + format = '%(levelname)s (%(module)s) %(message)s' if debug: logging.basicConfig(format=format, level=logging.DEBUG) else: logging.basicConfig(format=format, level=logging.WARN) + logging.getLogger('iso8601').setLevel(logging.WARNING) + logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING) def parse_args(self, argv): # Parse args once to find version + self.auth_plugin = ceiloclient.AuthPlugin(argv) parser = self.get_base_parser() (options, args) = parser.parse_known_args(argv) + self.auth_plugin.parse_opts(options) self._setup_logging(options.debug) # build available subcommands based on version @@ -329,13 +181,13 @@ class CeilometerShell(object): self.do_bash_completion(args) return 0 - if not (args.os_auth_token and args.ceilometer_url): - if not args.os_username: + if not (self.auth_plugin.opts['auth_token'] and args.ceilometer_url): + if not self.auth_plugin.opts['username']: raise exc.CommandError("You must provide a username via " "either --os-username or via " "env[OS_USERNAME]") - if not args.os_password: + if not self.auth_plugin.opts['password']: raise exc.CommandError("You must provide a password via " "either --os-password or via " "env[OS_PASSWORD]") @@ -352,13 +204,21 @@ class CeilometerShell(object): "--os-user-domain-id or via " "env[OS_USER_DOMAIN_ID]") - if not args.os_auth_url: + if not (self.auth_plugin.opts['tenant_id'] + or self.auth_plugin.opts['tenant_name']): + raise exc.CommandError("You must provide a tenant_id via " + "either --os-tenant-id or via " + "env[OS_TENANT_ID]") + + if not self.auth_plugin.opts['auth_url']: raise exc.CommandError("You must provide an auth url via " "either --os-auth-url or via " "env[OS_AUTH_URL]") - client = ceiloclient.get_client(api_version, **(args.__dict__)) - + client_kwargs = vars(args) + client_kwargs.update(self.auth_plugin.opts) + client_kwargs['auth_plugin'] = self.auth_plugin + client = ceiloclient.Client(api_version, **client_kwargs) # call whatever callback was selected try: args.func(client, args) @@ -396,6 +256,18 @@ class CeilometerShell(object): class HelpFormatter(argparse.HelpFormatter): + INDENT_BEFORE_ARGUMENTS = 6 + MAX_WIDTH_ARGUMENTS = 32 + + def add_arguments(self, actions): + for action in filter(lambda x: not x.option_strings, actions): + for choice in action.choices: + length = len(choice) + self.INDENT_BEFORE_ARGUMENTS + if(length > self._max_help_position and + length <= self.MAX_WIDTH_ARGUMENTS): + self._max_help_position = length + super(HelpFormatter, self).add_arguments(actions) + def start_section(self, heading): # Title-case the headings heading = '%s%s' % (heading[0].upper(), heading[1:]) diff --git a/ceilometerclient/tests/test_client.py b/ceilometerclient/tests/test_client.py index 14aaca6..faed8ee 100644 --- a/ceilometerclient/tests/test_client.py +++ b/ceilometerclient/tests/test_client.py @@ -12,46 +12,63 @@ import types +import mock + from ceilometerclient import client +from ceilometerclient.tests import fakes from ceilometerclient.tests import utils from ceilometerclient.v1 import client as v1client from ceilometerclient.v2 import client as v2client -FAKE_ENV = {'os_username': 'username', - 'os_password': 'password', - 'os_tenant_name': 'tenant_name', - 'os_auth_url': 'http://no.where:5000/', - 'os_auth_token': '1234', - 'ceilometer_url': 'http://no.where'} +FAKE_ENV = {'username': 'username', + 'password': 'password', + 'tenant_name': 'tenant_name', + 'auth_url': 'http://no.where', + 'ceilometer_url': 'http://no.where', + 'auth_plugin': 'fake_auth', + 'token': '1234'} class ClientTest(utils.BaseTestCase): - def create_client(self, api_version=2, exclude=[]): - env = dict((k, v) for k, v in FAKE_ENV.items() if k not in exclude) - return client.get_client(api_version, **env) + def create_client(self, env, api_version=2, endpoint=None, exclude=[]): + env = dict((k, v) for k, v in env.items() + if k not in exclude) + + return client.Client(api_version, endpoint, **env) def setUp(self): super(ClientTest, self).setUp() def test_client_version(self): - c1 = self.create_client(api_version=1) + c1 = self.create_client(env=FAKE_ENV, api_version=1) self.assertIsInstance(c1, v1client.Client) - c2 = self.create_client(api_version=2) + c2 = self.create_client(env=FAKE_ENV, api_version=2) self.assertIsInstance(c2, v2client.Client) - def test_client_auth_token_lambda(self): - FAKE_ENV['os_auth_token'] = lambda: '1234' - self._test_client_auth_token() - - def test_client_auth_token_non_lambda(self): - FAKE_ENV['os_auth_token'] = "1234" - self._test_client_auth_token() + def test_client_auth_lambda(self): + env = FAKE_ENV.copy() + env['token'] = lambda: env['token'] + self.assertIsInstance(env['token'], + types.FunctionType) + c2 = self.create_client(env) + self.assertIsInstance(c2, v2client.Client) - def _test_client_auth_token(self): - c2 = self.create_client() + def test_client_auth_non_lambda(self): + env = FAKE_ENV.copy() + env['token'] = "1234" + self.assertIsInstance(env['token'], str) + c2 = self.create_client(env) self.assertIsInstance(c2, v2client.Client) - self.assertIsInstance(c2.http_client.auth_token, - types.FunctionType) - self.assertEqual('1234', c2.http_client.auth_token()) + + @mock.patch('keystoneclient.v2_0.client', fakes.FakeKeystone) + def test_client_without_auth_plugin(self): + env = FAKE_ENV.copy() + del env['auth_plugin'] + c = self.create_client(env, api_version=2, endpoint='fake_endpoint') + self.assertIsInstance(c.auth_plugin, client.AuthPlugin) + + def test_client_with_auth_plugin(self): + c = self.create_client(FAKE_ENV, api_version=2) + self.assertIsInstance(c.auth_plugin, str) diff --git a/ceilometerclient/tests/test_http.py b/ceilometerclient/tests/test_http.py deleted file mode 100644 index c7a6404..0000000 --- a/ceilometerclient/tests/test_http.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# 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 mock - -from ceilometerclient.common import http -from ceilometerclient.tests import utils - - -class HttpClientTest(utils.BaseTestCase): - url = 'http://localhost' - - def test_url_generation_trailing_slash_in_base_prefix_in_path(self): - client = http.HTTPClient("%s/" % self.url) - url = client._make_connection_url('/v1/resources') - self.assertEqual(url, '/v1/resources') - - def test_url_generation_no_trailing_slash_in_base_prefix_in_path(self): - client = http.HTTPClient(self.url) - url = client._make_connection_url('/v1/resources') - self.assertEqual(url, '/v1/resources') - - def test_url_generation_trailing_slash_in_base_no_prefix_in_path(self): - client = http.HTTPClient("%s/" % self.url) - url = client._make_connection_url('v1/resources') - self.assertEqual(url, '/v1/resources') - - def test_url_generation_no_trailing_slash_in_base_no_prefix_in_path(self): - client = http.HTTPClient(self.url) - url = client._make_connection_url('v1/resources') - self.assertEqual(url, '/v1/resources') - - def test_get_connection(self): - client = http.HTTPClient(self.url) - self.assertIsNotNone(client.get_connection()) - - @mock.patch.object(http.HTTPClient, 'get_connection') - def test_url_generation_with_proxy(self, get_conn): - client = http.HTTPClient(self.url, token=lambda: 'token') - client.proxy_url = "http://localhost:3128/" - conn = mock.MagicMock() - conn.request.side_effect = Exception("stop") - get_conn.return_value = conn - try: - client._http_request('/v1/resources', 'GET') - except Exception: - pass - conn.request.assert_called_once_with('GET', (self.url.rstrip('/') + - '/v1/resources'), - headers=mock.ANY) - - -class HttpsClientTest(HttpClientTest): - url = 'https://localhost' - - -class HttpEndingSlashClientTest(HttpClientTest): - url = 'http://localhost/' diff --git a/ceilometerclient/tests/test_shell.py b/ceilometerclient/tests/test_shell.py index 5f0ffba..cadfcc8 100644 --- a/ceilometerclient/tests/test_shell.py +++ b/ceilometerclient/tests/test_shell.py @@ -48,10 +48,10 @@ class ShellTest(utils.BaseTestCase): super(ShellTest, self).setUp() @mock.patch('sys.stdout', new=six.StringIO()) - @mock.patch.object(ks_session, 'Session') - @mock.patch.object(v1client.http.HTTPClient, 'json_request') - @mock.patch.object(v1client.http.HTTPClient, 'raw_request') - def shell(self, argstr, mock_ksclient, mock_json, mock_raw): + @mock.patch.object(ks_session, 'Session', mock.MagicMock()) + @mock.patch.object(v1client.client.HTTPClient, + 'client_request', mock.MagicMock()) + def shell(self, argstr): try: _shell = ceilometer_shell.CeilometerShell() _shell.main(argstr.split()) @@ -103,7 +103,7 @@ class ShellKeystoneV2Test(ShellTest): mock_ksclient.side_effect = exc.HTTPUnauthorized self.make_env(FAKE_V2_ENV) args = ['--debug', 'event-list'] - self.assertRaises(exc.HTTPUnauthorized, ceilometer_shell.main, args) + self.assertRaises(exc.CommandError, ceilometer_shell.main, args) @mock.patch.object(ks_session, 'Session') def test_dash_d_switch_raises_error(self, mock_ksclient): @@ -132,7 +132,7 @@ class ShellKeystoneV3Test(ShellTest): mock_ksclient.side_effect = exc.HTTPUnauthorized self.make_env(FAKE_V3_ENV) args = ['--debug', 'event-list'] - self.assertRaises(exc.HTTPUnauthorized, ceilometer_shell.main, args) + self.assertRaises(exc.CommandError, ceilometer_shell.main, args) @mock.patch.object(ks_session, 'Session') def test_dash_d_switch_raises_error(self, mock_ksclient): diff --git a/ceilometerclient/tests/utils.py b/ceilometerclient/tests/utils.py index a3e6a38..57bc276 100644 --- a/ceilometerclient/tests/utils.py +++ b/ceilometerclient/tests/utils.py @@ -13,55 +13,12 @@ # License for the specific language governing permissions and limitations # under the License. -import copy - import fixtures -import six import testtools -from ceilometerclient.common import http - class BaseTestCase(testtools.TestCase): def setUp(self): super(BaseTestCase, self).setUp() self.useFixture(fixtures.FakeLogger()) - - -class FakeAPI(object): - def __init__(self, fixtures): - self.fixtures = fixtures - self.calls = [] - - def _request(self, method, url, headers=None, body=None): - call = (method, url, headers or {}, body) - self.calls.append(call) - return self.fixtures[url][method] - - def raw_request(self, *args, **kwargs): - fixture = self._request(*args, **kwargs) - body_iter = http.ResponseBodyIterator(six.StringIO(fixture[1])) - return FakeResponse(fixture[0]), body_iter - - def json_request(self, *args, **kwargs): - fixture = self._request(*args, **kwargs) - return FakeResponse(fixture[0]), fixture[1] - - -class FakeResponse(object): - def __init__(self, headers, body=None, version=None): - """:param headers: dict representing HTTP response headers - :param body: file-like object - """ - self.headers = headers - self.body = body - - def getheaders(self): - return copy.deepcopy(self.headers).items() - - def getheader(self, key, default): - return self.headers.get(key, default) - - def read(self, amt): - return self.body.read(amt) diff --git a/ceilometerclient/tests/v1/test_meters.py b/ceilometerclient/tests/v1/test_meters.py index f97c18b..1c883b7 100644 --- a/ceilometerclient/tests/v1/test_meters.py +++ b/ceilometerclient/tests/v1/test_meters.py @@ -12,7 +12,8 @@ # 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 ceilometerclient.openstack.common.apiclient import client +from ceilometerclient.openstack.common.apiclient import fake_client from ceilometerclient.tests import utils import ceilometerclient.v1.meters @@ -109,15 +110,16 @@ class MeterManagerTest(utils.BaseTestCase): def setUp(self): super(MeterManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = ceilometerclient.v1.meters.MeterManager(self.api) def test_list_all(self): resources = list(self.mgr.list()) expect = [ - ('GET', '/v1/meters', {}, None), + 'GET', '/v1/meters' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(resources), 2) self.assertEqual(resources[0].resource_id, 'a') self.assertEqual(resources[1].resource_id, 'b') @@ -125,9 +127,9 @@ class MeterManagerTest(utils.BaseTestCase): def test_list_by_source(self): resources = list(self.mgr.list(source='openstack')) expect = [ - ('GET', '/v1/sources/openstack/meters', {}, None), + 'GET', '/v1/sources/openstack/meters' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(resources), 2) self.assertEqual(resources[0].resource_id, 'b') self.assertEqual(resources[1].resource_id, 'q') @@ -135,26 +137,26 @@ class MeterManagerTest(utils.BaseTestCase): def test_list_by_user(self): resources = list(self.mgr.list(user_id='joey')) expect = [ - ('GET', '/v1/users/joey/meters', {}, None), + 'GET', '/v1/users/joey/meters' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, 'b') def test_list_by_project(self): resources = list(self.mgr.list(project_id='dig_the_ditch')) expect = [ - ('GET', '/v1/projects/dig_the_ditch/meters', {}, None), + 'GET', '/v1/projects/dig_the_ditch/meters' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, 'b') def test_list_by_metaquery(self): resources = list(self.mgr.list(metaquery='metadata.zxc_id=foo')) expect = [ - ('GET', '/v1/meters?metadata.zxc_id=foo', {}, None), + 'GET', '/v1/meters?metadata.zxc_id=foo' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, 'b') diff --git a/ceilometerclient/tests/v1/test_projects.py b/ceilometerclient/tests/v1/test_projects.py index a1faaf0..2f34a5c 100644 --- a/ceilometerclient/tests/v1/test_projects.py +++ b/ceilometerclient/tests/v1/test_projects.py @@ -12,7 +12,8 @@ # 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 ceilometerclient.openstack.common.apiclient import client +from ceilometerclient.openstack.common.apiclient import fake_client from ceilometerclient.tests import utils import ceilometerclient.v1.meters @@ -40,15 +41,16 @@ class ProjectManagerTest(utils.BaseTestCase): def setUp(self): super(ProjectManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = ceilometerclient.v1.meters.ProjectManager(self.api) def test_list_all(self): projects = list(self.mgr.list()) expect = [ - ('GET', '/v1/projects', {}, None), + 'GET', '/v1/projects' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(projects), 2) self.assertEqual(projects[0].project_id, 'a') self.assertEqual(projects[1].project_id, 'b') @@ -56,8 +58,8 @@ class ProjectManagerTest(utils.BaseTestCase): def test_list_by_source(self): projects = list(self.mgr.list(source='source_b')) expect = [ - ('GET', '/v1/sources/source_b/projects', {}, None), + 'GET', '/v1/sources/source_b/projects' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(projects), 1) self.assertEqual(projects[0].project_id, 'b') diff --git a/ceilometerclient/tests/v1/test_resources.py b/ceilometerclient/tests/v1/test_resources.py index 00cea8c..4eac3fe 100644 --- a/ceilometerclient/tests/v1/test_resources.py +++ b/ceilometerclient/tests/v1/test_resources.py @@ -12,7 +12,8 @@ # 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 ceilometerclient.openstack.common.apiclient import client +from ceilometerclient.openstack.common.apiclient import fake_client from ceilometerclient.tests import utils import ceilometerclient.v1.meters @@ -108,15 +109,16 @@ class ResourceManagerTest(utils.BaseTestCase): def setUp(self): super(ResourceManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = ceilometerclient.v1.meters.ResourceManager(self.api) def test_list_all(self): resources = list(self.mgr.list()) expect = [ - ('GET', '/v1/resources', {}, None), + 'GET', '/v1/resources' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(resources), 2) self.assertEqual(resources[0].resource_id, 'a') self.assertEqual(resources[1].resource_id, 'b') @@ -124,27 +126,27 @@ class ResourceManagerTest(utils.BaseTestCase): def test_list_by_user(self): resources = list(self.mgr.list(user_id='joey')) expect = [ - ('GET', '/v1/users/joey/resources', {}, None), + 'GET', '/v1/users/joey/resources' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, 'b') def test_list_by_metaquery(self): resources = list(self.mgr.list(metaquery='metadata.zxc_id=foo')) expect = [ - ('GET', '/v1/resources?metadata.zxc_id=foo', {}, None), + 'GET', '/v1/resources?metadata.zxc_id=foo' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, 'b') def test_list_by_project(self): resources = list(self.mgr.list(project_id='project_bla')) expect = [ - ('GET', '/v1/projects/project_bla/resources', {}, None), + 'GET', '/v1/projects/project_bla/resources' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, 'a') @@ -152,9 +154,8 @@ class ResourceManagerTest(utils.BaseTestCase): resources = list(self.mgr.list(start_timestamp='now', end_timestamp='now')) expect = [ - ('GET', '/v1/resources?start_timestamp=now&end_timestamp=now', - {}, None), + 'GET', '/v1/resources?start_timestamp=now&end_timestamp=now' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, 'b') diff --git a/ceilometerclient/tests/v1/test_samples.py b/ceilometerclient/tests/v1/test_samples.py index 9e59b46..61f064e 100644 --- a/ceilometerclient/tests/v1/test_samples.py +++ b/ceilometerclient/tests/v1/test_samples.py @@ -12,7 +12,8 @@ # 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 ceilometerclient.openstack.common.apiclient import client +from ceilometerclient.openstack.common.apiclient import fake_client from ceilometerclient.tests import utils import ceilometerclient.v1.meters @@ -122,24 +123,25 @@ class SampleManagerTest(utils.BaseTestCase): def setUp(self): super(SampleManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = ceilometerclient.v1.meters.SampleManager(self.api) def test_list_all(self): samples = list(self.mgr.list(counter_name=None)) expect = [ - ('GET', '/v1/meters', {}, None), + 'GET', '/v1/meters' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(samples), 0) def test_list_by_source(self): samples = list(self.mgr.list(source='openstack', counter_name='this')) expect = [ - ('GET', '/v1/sources/openstack/meters/this', {}, None), + 'GET', '/v1/sources/openstack/meters/this' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(samples), 1) self.assertEqual(samples[0].resource_id, 'b') @@ -147,9 +149,9 @@ class SampleManagerTest(utils.BaseTestCase): samples = list(self.mgr.list(user_id='freddy', counter_name='balls')) expect = [ - ('GET', '/v1/users/freddy/meters/balls', {}, None), + 'GET', '/v1/users/freddy/meters/balls' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(samples), 1) self.assertEqual(samples[0].project_id, 'melbourne_open') self.assertEqual(samples[0].user_id, 'freddy') @@ -159,9 +161,9 @@ class SampleManagerTest(utils.BaseTestCase): samples = list(self.mgr.list(project_id='dig_the_ditch', counter_name='meters')) expect = [ - ('GET', '/v1/projects/dig_the_ditch/meters/meters', {}, None), + 'GET', '/v1/projects/dig_the_ditch/meters/meters' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(samples), 1) self.assertEqual(samples[0].project_id, 'dig_the_ditch') self.assertEqual(samples[0].volume, 345) @@ -171,9 +173,9 @@ class SampleManagerTest(utils.BaseTestCase): samples = list(self.mgr.list(metaquery='metadata.zxc_id=foo', counter_name='this')) expect = [ - ('GET', '/v1/meters?metadata.zxc_id=foo', {}, None), + 'GET', '/v1/meters?metadata.zxc_id=foo' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(samples), 1) self.assertEqual(samples[0].resource_metadata['zxc_id'], 'foo') @@ -183,12 +185,11 @@ class SampleManagerTest(utils.BaseTestCase): start_timestamp='now', end_timestamp='now')) expect = [ - ('GET', - '/v1/users/freddy/meters/balls?' + - 'start_timestamp=now&end_timestamp=now', - {}, None), + 'GET', + '/v1/users/freddy/meters/balls?' + + 'start_timestamp=now&end_timestamp=now' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(samples), 1) self.assertEqual(samples[0].project_id, 'melbourne_open') self.assertEqual(samples[0].user_id, 'freddy') diff --git a/ceilometerclient/tests/v1/test_users.py b/ceilometerclient/tests/v1/test_users.py index df208c5..541139b 100644 --- a/ceilometerclient/tests/v1/test_users.py +++ b/ceilometerclient/tests/v1/test_users.py @@ -12,7 +12,8 @@ # 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 ceilometerclient.openstack.common.apiclient import client +from ceilometerclient.openstack.common.apiclient import fake_client from ceilometerclient.tests import utils import ceilometerclient.v1.meters @@ -40,15 +41,16 @@ class UserManagerTest(utils.BaseTestCase): def setUp(self): super(UserManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = ceilometerclient.v1.meters.UserManager(self.api) def test_list_all(self): users = list(self.mgr.list()) expect = [ - ('GET', '/v1/users', {}, None), + 'GET', '/v1/users' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(users), 2) self.assertEqual(users[0].user_id, 'a') self.assertEqual(users[1].user_id, 'b') @@ -56,8 +58,8 @@ class UserManagerTest(utils.BaseTestCase): def test_list_by_source(self): users = list(self.mgr.list(source='source_b')) expect = [ - ('GET', '/v1/sources/source_b/users', {}, None), + 'GET', '/v1/sources/source_b/users' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(users), 1) self.assertEqual(users[0].user_id, 'b') diff --git a/ceilometerclient/tests/v2/test_alarms.py b/ceilometerclient/tests/v2/test_alarms.py index ad6a811..c3aa6d5 100644 --- a/ceilometerclient/tests/v2/test_alarms.py +++ b/ceilometerclient/tests/v2/test_alarms.py @@ -21,7 +21,8 @@ import six from six.moves import xrange # noqa import testtools -from ceilometerclient.tests import utils +from ceilometerclient.openstack.common.apiclient import client +from ceilometerclient.openstack.common.apiclient import fake_client from ceilometerclient.v2 import alarms AN_ALARM = {u'alarm_actions': [u'http://site:8000/alarm'], @@ -210,11 +211,11 @@ fixtures = { { 'PUT': ( {}, - 'alarm' + {'alarm': 'alarm'} ), 'GET': ( {}, - 'alarm' + {'alarm': 'alarm'} ), }, @@ -254,15 +255,16 @@ class AlarmManagerTest(testtools.TestCase): def setUp(self): super(AlarmManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = alarms.AlarmManager(self.api) def test_list_all(self): alarms = list(self.mgr.list()) expect = [ - ('GET', '/v2/alarms', {}, None), + 'GET', '/v2/alarms' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(alarms), 1) self.assertEqual(alarms[0].alarm_id, 'alarm-id') @@ -272,53 +274,58 @@ class AlarmManagerTest(testtools.TestCase): {"field": "name", "value": "SwiftObjectAlarm"}])) expect = [ - ('GET', - '/v2/alarms?q.field=project_id&q.field=name&q.op=&q.op=' - '&q.type=&q.type=&q.value=project-id&q.value=SwiftObjectAlarm', - {}, None), + 'GET', + '/v2/alarms?q.field=project_id&q.field=name&q.op=&q.op=' + '&q.type=&q.type=&q.value=project-id&q.value=SwiftObjectAlarm', ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(alarms), 1) self.assertEqual(alarms[0].alarm_id, 'alarm-id') def test_get(self): alarm = self.mgr.get(alarm_id='alarm-id') expect = [ - ('GET', '/v2/alarms/alarm-id', {}, None), + 'GET', '/v2/alarms/alarm-id' ] - self.assertEqual(self.api.calls, expect) - self.assertTrue(alarm) + self.http_client.assert_called(*expect) + self.assertIsNotNone(alarm) self.assertEqual(alarm.alarm_id, 'alarm-id') self.assertEqual(alarm.rule, alarm.threshold_rule) def test_create(self): alarm = self.mgr.create(**CREATE_ALARM) expect = [ - ('POST', '/v2/alarms', {}, CREATE_ALARM), + 'POST', '/v2/alarms' ] - self.assertEqual(self.api.calls, expect) - self.assertTrue(alarm) + self.http_client.assert_called(*expect, body=CREATE_ALARM) + self.assertIsNotNone(alarm) def test_update(self): alarm = self.mgr.update(alarm_id='alarm-id', **UPDATE_ALARM) - expect = [ - ('GET', '/v2/alarms/alarm-id', {}, None), - ('PUT', '/v2/alarms/alarm-id', {}, UPDATED_ALARM), + expect_get = [ + 'GET', '/v2/alarms/alarm-id' + ] + expect_put = [ + 'PUT', '/v2/alarms/alarm-id', UPDATED_ALARM ] - self.assertEqual(self.api.calls, expect) - self.assertTrue(alarm) + self.http_client.assert_called(*expect_get, pos=0) + self.http_client.assert_called(*expect_put, pos=1) + self.assertIsNotNone(alarm) self.assertEqual(alarm.alarm_id, 'alarm-id') for (key, value) in six.iteritems(UPDATED_ALARM): self.assertEqual(getattr(alarm, key), value) def test_update_delta(self): alarm = self.mgr.update(alarm_id='alarm-id', **DELTA_ALARM) - expect = [ - ('GET', '/v2/alarms/alarm-id', {}, None), - ('PUT', '/v2/alarms/alarm-id', {}, UPDATED_ALARM), + expect_get = [ + 'GET', '/v2/alarms/alarm-id' ] - self.assertEqual(self.api.calls, expect) - self.assertTrue(alarm) + expect_put = [ + 'PUT', '/v2/alarms/alarm-id', UPDATED_ALARM + ] + self.http_client.assert_called(*expect_get, pos=0) + self.http_client.assert_called(*expect_put, pos=1) + self.assertIsNotNone(alarm) self.assertEqual(alarm.alarm_id, 'alarm-id') for (key, value) in six.iteritems(UPDATED_ALARM): self.assertEqual(getattr(alarm, key), value) @@ -326,65 +333,71 @@ class AlarmManagerTest(testtools.TestCase): def test_set_state(self): state = self.mgr.set_state(alarm_id='alarm-id', state='alarm') expect = [ - ('PUT', '/v2/alarms/alarm-id/state', {}, 'alarm'), + 'PUT', '/v2/alarms/alarm-id/state' ] - self.assertEqual(self.api.calls, expect) - self.assertEqual(state, 'alarm') + self.http_client.assert_called(*expect, body='alarm') + self.assertEqual(state, {'alarm': 'alarm'}) def test_get_state(self): state = self.mgr.get_state(alarm_id='alarm-id') expect = [ - ('GET', '/v2/alarms/alarm-id/state', {}, None), + 'GET', '/v2/alarms/alarm-id/state' ] - self.assertEqual(self.api.calls, expect) - self.assertEqual(state, 'alarm') + self.http_client.assert_called(*expect) + self.assertEqual(state, {'alarm': 'alarm'}) def test_delete(self): deleted = self.mgr.delete(alarm_id='victim-id') expect = [ - ('DELETE', '/v2/alarms/victim-id', {}, None), + 'DELETE', '/v2/alarms/victim-id' ] - self.assertEqual(self.api.calls, expect) - self.assertTrue(deleted is None) + self.http_client.assert_called(*expect) + self.assertIsNone(deleted) def test_get_from_alarm_class(self): alarm = self.mgr.get(alarm_id='alarm-id') - self.assertTrue(alarm) + self.assertIsNotNone(alarm) alarm.get() expect = [ - ('GET', '/v2/alarms/alarm-id', {}, None), - ('GET', '/v2/alarms/alarm-id', {}, None) + 'GET', '/v2/alarms/alarm-id' ] - self.assertEqual(expect, self.api.calls) + self.http_client.assert_called(*expect, pos=0) + self.http_client.assert_called(*expect, pos=1) self.assertEqual('alarm-id', alarm.alarm_id) self.assertEqual(alarm.threshold_rule, alarm.rule) def test_get_state_from_alarm_class(self): alarm = self.mgr.get(alarm_id='alarm-id') - self.assertTrue(alarm) + self.assertIsNotNone(alarm) state = alarm.get_state() - expect = [ - ('GET', '/v2/alarms/alarm-id', {}, None), - ('GET', '/v2/alarms/alarm-id/state', {}, None) + expect_get_1 = [ + 'GET', '/v2/alarms/alarm-id' ] - self.assertEqual(expect, self.api.calls) + expect_get_2 = [ + 'GET', '/v2/alarms/alarm-id/state' + ] + self.http_client.assert_called(*expect_get_1, pos=0) + self.http_client.assert_called(*expect_get_2, pos=1) self.assertEqual('alarm', state) def test_delete_from_alarm_class(self): alarm = self.mgr.get(alarm_id='alarm-id') - self.assertTrue(alarm) + self.assertIsNotNone(alarm) deleted = alarm.delete() - expect = [ - ('GET', '/v2/alarms/alarm-id', {}, None), - ('DELETE', '/v2/alarms/alarm-id', {}, None) + expect_get = [ + 'GET', '/v2/alarms/alarm-id' ] - self.assertEqual(expect, self.api.calls) - self.assertTrue(deleted is None) + expect_delete = [ + 'DELETE', '/v2/alarms/alarm-id' + ] + self.http_client.assert_called(*expect_get, pos=0) + self.http_client.assert_called(*expect_delete, pos=1) + self.assertIsNone(deleted) def _do_test_get_history(self, q, url): history = self.mgr.get_history(q=q, alarm_id='alarm-id') - expect = [('GET', url, {}, None)] - self.assertEqual(self.api.calls, expect) + expect = ['GET', url] + self.http_client.assert_called(*expect) for i in xrange(len(history)): change = history[i] self.assertIsInstance(change, alarms.AlarmChange) @@ -406,16 +419,17 @@ class AlarmLegacyManagerTest(testtools.TestCase): def setUp(self): super(AlarmLegacyManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = alarms.AlarmManager(self.api) def test_create(self): alarm = self.mgr.create(**CREATE_LEGACY_ALARM) expect = [ - ('POST', '/v2/alarms', {}, CREATE_ALARM_WITHOUT_TC), + 'POST', '/v2/alarms', CREATE_ALARM_WITHOUT_TC, ] - self.assertEqual(self.api.calls, expect) - self.assertTrue(alarm) + self.http_client.assert_called(*expect) + self.assertIsNotNone(alarm) def test_create_counter_name(self): create = {} @@ -424,19 +438,18 @@ class AlarmLegacyManagerTest(testtools.TestCase): del create['meter_name'] alarm = self.mgr.create(**create) expect = [ - ('POST', '/v2/alarms', {}, CREATE_ALARM_WITHOUT_TC), + 'POST', '/v2/alarms', CREATE_ALARM_WITHOUT_TC, ] - self.assertEqual(self.api.calls, expect) - self.assertTrue(alarm) + self.http_client.assert_called(*expect) + self.assertIsNotNone(alarm) def test_update(self): alarm = self.mgr.update(alarm_id='alarm-id', **DELTA_LEGACY_ALARM) - expect = [ - ('GET', '/v2/alarms/alarm-id', {}, None), - ('PUT', '/v2/alarms/alarm-id', {}, UPDATED_ALARM), + expect_put = [ + 'PUT', '/v2/alarms/alarm-id', UPDATED_ALARM ] - self.assertEqual(self.api.calls, expect) - self.assertTrue(alarm) + self.http_client.assert_called(*expect_put) + self.assertIsNotNone(alarm) self.assertEqual(alarm.alarm_id, 'alarm-id') for (key, value) in six.iteritems(UPDATED_ALARM): self.assertEqual(getattr(alarm, key), value) @@ -447,12 +460,11 @@ class AlarmLegacyManagerTest(testtools.TestCase): updated['counter_name'] = UPDATED_LEGACY_ALARM['meter_name'] del updated['meter_name'] alarm = self.mgr.update(alarm_id='alarm-id', **updated) - expect = [ - ('GET', '/v2/alarms/alarm-id', {}, None), - ('PUT', '/v2/alarms/alarm-id', {}, UPDATED_ALARM), + expect_put = [ + 'PUT', '/v2/alarms/alarm-id', UPDATED_ALARM ] - self.assertEqual(self.api.calls, expect) - self.assertTrue(alarm) + self.http_client.assert_called(*expect_put) + self.assertIsNotNone(alarm) self.assertEqual(alarm.alarm_id, 'alarm-id') for (key, value) in six.iteritems(UPDATED_ALARM): self.assertEqual(getattr(alarm, key), value) @@ -462,7 +474,8 @@ class AlarmTimeConstraintTest(testtools.TestCase): def setUp(self): super(AlarmTimeConstraintTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = alarms.AlarmManager(self.api) def test_add_new(self): @@ -471,26 +484,37 @@ class AlarmTimeConstraintTest(testtools.TestCase): duration=500) kwargs = dict(time_constraints=[new_constraint]) self.mgr.update(alarm_id='alarm-id', **kwargs) - actual = self.api.calls[1][3]['time_constraints'] - expected = AN_ALARM[u'time_constraints'] + [new_constraint] - self.assertEqual(expected, actual) + body = copy.deepcopy(AN_ALARM) + body[u'time_constraints'] = \ + AN_ALARM[u'time_constraints'] + [new_constraint] + expect = [ + 'PUT', '/v2/alarms/alarm-id', body + ] + self.http_client.assert_called(*expect) def test_update_existing(self): updated_constraint = dict(name='cons2', duration=500) kwargs = dict(time_constraints=[updated_constraint]) self.mgr.update(alarm_id='alarm-id', **kwargs) - actual = self.api.calls[1][3]['time_constraints'] - expected = [AN_ALARM[u'time_constraints'][0], dict(name='cons2', - description='desc2', - start='0 23 * * *', - duration=500, - timezone='')] - self.assertEqual(expected, actual) + body = copy.deepcopy(AN_ALARM) + body[u'time_constraints'][1] = dict(name='cons2', + description='desc2', + start='0 23 * * *', + duration=500, + timezone='') + + expect = [ + 'PUT', '/v2/alarms/alarm-id', body + ] + self.http_client.assert_called(*expect) def test_remove(self): kwargs = dict(remove_time_constraints=['cons2']) self.mgr.update(alarm_id='alarm-id', **kwargs) - actual = self.api.calls[1][3]['time_constraints'] - expected = [AN_ALARM[u'time_constraints'][0]] - self.assertEqual(expected, actual) + body = copy.deepcopy(AN_ALARM) + body[u'time_constraints'] = AN_ALARM[u'time_constraints'][:1] + expect = [ + 'PUT', '/v2/alarms/alarm-id', body + ] + self.http_client.assert_called(*expect) diff --git a/ceilometerclient/tests/v2/test_event_types.py b/ceilometerclient/tests/v2/test_event_types.py index 08a25a4..8d5e4a0 100644 --- a/ceilometerclient/tests/v2/test_event_types.py +++ b/ceilometerclient/tests/v2/test_event_types.py @@ -12,6 +12,9 @@ # License for the specific language governing permissions and limitations # under the License. + +from ceilometerclient.openstack.common.apiclient import client +from ceilometerclient.openstack.common.apiclient import fake_client from ceilometerclient.tests import utils import ceilometerclient.v2.event_types @@ -30,15 +33,16 @@ class EventTypesManagerTest(utils.BaseTestCase): def setUp(self): super(EventTypesManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = ceilometerclient.v2.event_types.EventTypeManager(self.api) def test_list(self): event_types = list(self.mgr.list()) expect = [ - ('GET', '/v2/event_types/', {}, None), + 'GET', '/v2/event_types/' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(event_types), 4) self.assertEqual(event_types[0].event_type, "Foo") self.assertEqual(event_types[1].event_type, "Bar") diff --git a/ceilometerclient/tests/v2/test_events.py b/ceilometerclient/tests/v2/test_events.py index 4935d8b..6b1e74b 100644 --- a/ceilometerclient/tests/v2/test_events.py +++ b/ceilometerclient/tests/v2/test_events.py @@ -11,7 +11,8 @@ # 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 ceilometerclient.openstack.common.apiclient import client +from ceilometerclient.openstack.common.apiclient import fake_client from ceilometerclient.tests import utils import ceilometerclient.v2.events @@ -124,15 +125,16 @@ class EventManagerTest(utils.BaseTestCase): def setUp(self): super(EventManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = ceilometerclient.v2.events.EventManager(self.api) def test_list_all(self): events = list(self.mgr.list()) expect = [ - ('GET', '/v2/events', {}, None), + 'GET', '/v2/events' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(events), 3) self.assertEqual(events[0].event_type, 'Foo') self.assertEqual(events[1].event_type, 'Foo') @@ -141,10 +143,10 @@ class EventManagerTest(utils.BaseTestCase): def test_list_one(self): event = self.mgr.get(2) expect = [ - ('GET', '/v2/events/2', {}, None), + 'GET', '/v2/events/2' ] - self.assertEqual(self.api.calls, expect) - self.assertTrue(event) + self.http_client.assert_called(*expect) + self.assertIsNotNone(event) self.assertEqual(event.event_type, 'Foo') def test_list_with_query(self): @@ -152,11 +154,10 @@ class EventManagerTest(utils.BaseTestCase): "value": "localhost", "type": "string"}])) expect = [ - ('GET', '/v2/events?q.field=hostname&q.op=&q.type=string' - '&q.value=localhost', - {}, None), + 'GET', '/v2/events?q.field=hostname&q.op=&q.type=string' + '&q.value=localhost' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(events), 2) self.assertEqual(events[0].event_type, 'Foo') @@ -164,11 +165,10 @@ class EventManagerTest(utils.BaseTestCase): events = list(self.mgr.list(q=[{"field": "hostname", "value": "foreignhost"}])) expect = [ - ('GET', '/v2/events?q.field=hostname&q.op=' - '&q.type=&q.value=foreignhost', - {}, None), + 'GET', '/v2/events?q.field=hostname&q.op=' + '&q.type=&q.value=foreignhost' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(events), 2) self.assertEqual(events[0].event_type, 'Foo') @@ -180,20 +180,19 @@ class EventManagerTest(utils.BaseTestCase): "type": "integer"}])) expect = [ - ('GET', '/v2/events?q.field=hostname&q.field=num_cpus&q.op=&q.op=' - '&q.type=&q.type=integer&q.value=localhost&q.value=5', - {}, None), + 'GET', '/v2/events?q.field=hostname&q.field=num_cpus&q.op=&q.op=' + '&q.type=&q.type=integer&q.value=localhost&q.value=5' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(events), 1) def test_get_from_event_class(self): event = self.mgr.get(2) - self.assertTrue(event) + self.assertIsNotNone(event) event.get() expect = [ - ('GET', '/v2/events/2', {}, None), - ('GET', '/v2/events/2', {}, None), + 'GET', '/v2/events/2' ] - self.assertEqual(expect, self.api.calls) + self.http_client.assert_called(*expect, pos=0) + self.http_client.assert_called(*expect, pos=1) self.assertEqual('Foo', event.event_type) diff --git a/ceilometerclient/tests/v2/test_options.py b/ceilometerclient/tests/v2/test_options.py index e9dee54..6318d5c 100644 --- a/ceilometerclient/tests/v2/test_options.py +++ b/ceilometerclient/tests/v2/test_options.py @@ -10,7 +10,6 @@ # 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 ceilometerclient.tests import utils from ceilometerclient.v2 import options diff --git a/ceilometerclient/tests/v2/test_query_alarm_history.py b/ceilometerclient/tests/v2/test_query_alarm_history.py index 2a6cfb4..60d437d 100644 --- a/ceilometerclient/tests/v2/test_query_alarm_history.py +++ b/ceilometerclient/tests/v2/test_query_alarm_history.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +from ceilometerclient.openstack.common.apiclient import client +from ceilometerclient.openstack.common.apiclient import fake_client from ceilometerclient.tests import utils from ceilometerclient.v2 import query @@ -49,13 +51,16 @@ class QueryAlarmsManagerTest(utils.BaseTestCase): def setUp(self): super(QueryAlarmsManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = query.QueryAlarmHistoryManager(self.api) def test_query(self): alarm_history = self.mgr.query(**QUERY) expect = [ - ('POST', '/v2/query/alarms/history', {}, QUERY), + + 'POST', '/v2/query/alarms/history', QUERY, + ] - self.assertEqual(expect, self.api.calls) + self.http_client.assert_called(*expect) self.assertEqual(1, len(alarm_history)) diff --git a/ceilometerclient/tests/v2/test_query_alarms.py b/ceilometerclient/tests/v2/test_query_alarms.py index 199caa7..7e7a108 100644 --- a/ceilometerclient/tests/v2/test_query_alarms.py +++ b/ceilometerclient/tests/v2/test_query_alarms.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +from ceilometerclient.openstack.common.apiclient import client +from ceilometerclient.openstack.common.apiclient import fake_client from ceilometerclient.tests import utils from ceilometerclient.v2 import query @@ -60,13 +62,15 @@ class QueryAlarmsManagerTest(utils.BaseTestCase): def setUp(self): super(QueryAlarmsManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = query.QueryAlarmsManager(self.api) def test_query(self): alarms = self.mgr.query(**QUERY) expect = [ - ('POST', '/v2/query/alarms', {}, QUERY), + 'POST', '/v2/query/alarms', QUERY, ] - self.assertEqual(expect, self.api.calls) + + self.http_client.assert_called(*expect) self.assertEqual(1, len(alarms)) diff --git a/ceilometerclient/tests/v2/test_query_samples.py b/ceilometerclient/tests/v2/test_query_samples.py index b12a508..7f6e7d9 100644 --- a/ceilometerclient/tests/v2/test_query_samples.py +++ b/ceilometerclient/tests/v2/test_query_samples.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +from ceilometerclient.openstack.common.apiclient import client +from ceilometerclient.openstack.common.apiclient import fake_client from ceilometerclient.tests import utils from ceilometerclient.v2 import query @@ -53,13 +55,15 @@ class QuerySamplesManagerTest(utils.BaseTestCase): def setUp(self): super(QuerySamplesManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = query.QuerySamplesManager(self.api) def test_query(self): samples = self.mgr.query(**QUERY) expect = [ - ('POST', '/v2/query/samples', {}, QUERY), + + 'POST', '/v2/query/samples', QUERY, ] - self.assertEqual(expect, self.api.calls) + self.http_client.assert_called(*expect) self.assertEqual(1, len(samples)) diff --git a/ceilometerclient/tests/v2/test_resources.py b/ceilometerclient/tests/v2/test_resources.py index d62ce40..090ce8c 100644 --- a/ceilometerclient/tests/v2/test_resources.py +++ b/ceilometerclient/tests/v2/test_resources.py @@ -12,7 +12,8 @@ # 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 ceilometerclient.openstack.common.apiclient import client +from ceilometerclient.openstack.common.apiclient import fake_client from ceilometerclient.tests import utils import ceilometerclient.v2.resources @@ -70,15 +71,16 @@ class ResourceManagerTest(utils.BaseTestCase): def setUp(self): super(ResourceManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = ceilometerclient.v2.resources.ResourceManager(self.api) def test_list_all(self): resources = list(self.mgr.list()) expect = [ - ('GET', '/v2/resources', {}, None), + 'GET', '/v2/resources' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(resources), 2) self.assertEqual(resources[0].resource_id, 'a') self.assertEqual(resources[1].resource_id, 'b') @@ -86,10 +88,10 @@ class ResourceManagerTest(utils.BaseTestCase): def test_list_one(self): resource = self.mgr.get(resource_id='a') expect = [ - ('GET', '/v2/resources/a', {}, None), + 'GET', '/v2/resources/a' ] - self.assertEqual(self.api.calls, expect) - self.assertTrue(resource) + self.http_client.assert_called(*expect) + self.assertIsNotNone(resource) self.assertEqual(resource.resource_id, 'a') def test_list_by_query(self): @@ -97,21 +99,20 @@ class ResourceManagerTest(utils.BaseTestCase): "value": "a"}, ])) expect = [ - ('GET', '/v2/resources?q.field=resource_id&q.op=' - '&q.type=&q.value=a', - {}, None), + 'GET', '/v2/resources?q.field=resource_id&q.op=' + '&q.type=&q.value=a' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, 'a') def test_get_from_resource_class(self): resource = self.mgr.get(resource_id='a') - self.assertTrue(resource) + self.assertIsNotNone(resource) resource.get() expect = [ - ('GET', '/v2/resources/a', {}, None), - ('GET', '/v2/resources/a', {}, None), + 'GET', '/v2/resources/a' ] - self.assertEqual(expect, self.api.calls) + self.http_client.assert_called(*expect, pos=0) + self.http_client.assert_called(*expect, pos=1) self.assertEqual('a', resource.resource_id) diff --git a/ceilometerclient/tests/v2/test_samples.py b/ceilometerclient/tests/v2/test_samples.py index 7b0b878..5804c7e 100644 --- a/ceilometerclient/tests/v2/test_samples.py +++ b/ceilometerclient/tests/v2/test_samples.py @@ -15,6 +15,8 @@ import copy +from ceilometerclient.openstack.common.apiclient import client +from ceilometerclient.openstack.common.apiclient import fake_client from ceilometerclient.tests import utils import ceilometerclient.v2.samples @@ -71,15 +73,16 @@ class SampleManagerTest(utils.BaseTestCase): def setUp(self): super(SampleManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = ceilometerclient.v2.samples.SampleManager(self.api) def test_list_by_meter_name(self): samples = list(self.mgr.list(meter_name='instance')) expect = [ - ('GET', '/v2/meters/instance', {}, None), + 'GET', '/v2/meters/instance' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(samples), 1) self.assertEqual(samples[0].resource_id, 'resource-id') @@ -91,20 +94,20 @@ class SampleManagerTest(utils.BaseTestCase): {"field": "source", "value": "bar"}, ])) - expect = [('GET', '%s?%s' % (base_url, args), {}, None)] - self.assertEqual(self.api.calls, expect) + expect = ['GET', '%s?%s' % (base_url, args)] + self.http_client.assert_called(*expect) self.assertEqual(len(samples), 0) def test_create(self): sample = self.mgr.create(**CREATE_SAMPLE) expect = [ - ('POST', '/v2/meters/instance', {}, [CREATE_SAMPLE]), + 'POST', '/v2/meters/instance' ] - self.assertEqual(self.api.calls, expect) - self.assertTrue(sample) + self.http_client.assert_called(*expect, body=CREATE_SAMPLE) + self.assertIsNotNone(sample) def test_limit(self): samples = list(self.mgr.list(meter_name='instance', limit=1)) - expect = [('GET', '/v2/meters/instance?limit=1', {}, None)] - self.assertEqual(self.api.calls, expect) + expect = ['GET', '/v2/meters/instance?limit=1'] + self.http_client.assert_called(*expect) self.assertEqual(len(samples), 1) diff --git a/ceilometerclient/tests/v2/test_shell.py b/ceilometerclient/tests/v2/test_shell.py index ea362aa..7da088a 100644 --- a/ceilometerclient/tests/v2/test_shell.py +++ b/ceilometerclient/tests/v2/test_shell.py @@ -22,6 +22,7 @@ import mock import six from testtools import matchers +from ceilometerclient import exc from ceilometerclient import shell as base_shell from ceilometerclient.tests import utils from ceilometerclient.v2 import alarms @@ -215,7 +216,7 @@ class ShellAlarmCommandTest(utils.BaseTestCase): if repeat_actions is not None: self.assertEqual(repeat_actions, kwargs.get('repeat_actions')) else: - self.assertFalse('repeat_actions' in kwargs) + self.assertNotIn('repeat_actions', kwargs) def test_alarm_update_repeat_actions_untouched(self): method = ceilometer_shell.do_alarm_update @@ -281,7 +282,7 @@ class ShellAlarmCommandTest(utils.BaseTestCase): self.assertEqual('instance running hot', kwargs.get('description')) actions = ['log://', 'http://example.com/alarm/state'] self.assertEqual(actions, kwargs.get('alarm_actions')) - self.assertTrue('threshold_rule' in kwargs) + self.assertIn('threshold_rule', kwargs) rule = kwargs['threshold_rule'] self.assertEqual('cpu_util', rule.get('meter_name')) self.assertEqual(70.0, rule.get('threshold')) @@ -798,3 +799,83 @@ class ShellStatisticsTest(utils.BaseTestCase): fields, [self.displays.get(f, f) for f in fields], ) + + +class ShellEmptyIdTest(utils.BaseTestCase): + """Test empty field which will cause calling incorrect rest uri.""" + + def _test_entity_action_with_empty_values(self, entity, *args): + for value in ('', ' ', ' ', '\t'): + self._test_entity_action_with_empty_value(entity, value, *args) + + def _test_entity_action_with_empty_value(self, entity, value, *args): + shell = base_shell.CeilometerShell() + argv = list(args) + ['--%s' % entity, value] + + with mock.patch('ceilometerclient.exc.CommandError') as e: + e.return_value = exc.BaseException() + self.assertRaises(exc.BaseException, shell.parse_args, argv) + entity = entity.replace('-', '_') + e.assert_called_with('%s should not be empty' % entity) + + def _test_alarm_action_with_empty_ids(self, method, *args): + args = [method] + list(args) + self._test_entity_action_with_empty_values('alarm_id', *args) + + def test_alarm_show_with_empty_id(self): + self._test_alarm_action_with_empty_ids('alarm-show') + + def test_alarm_update_with_empty_id(self): + self._test_alarm_action_with_empty_ids('alarm-update') + + def test_alarm_threshold_update_with_empty_id(self): + self._test_alarm_action_with_empty_ids('alarm-threshold-update') + + def test_alarm_combination_update_with_empty_id(self): + self._test_alarm_action_with_empty_ids('alarm-combination-update') + + def test_alarm_delete_with_empty_id(self): + self._test_alarm_action_with_empty_ids('alarm-delete') + + def test_alarm_state_get_with_empty_id(self): + self._test_alarm_action_with_empty_ids('alarm-state-get') + + def test_alarm_state_set_with_empty_id(self): + args = ['alarm-state-set', '--state', 'ok'] + self._test_alarm_action_with_empty_ids(*args) + + def test_alarm_history_with_empty_id(self): + self._test_alarm_action_with_empty_ids('alarm-history') + + def test_event_show_with_empty_message_id(self): + args = ['event-show'] + self._test_entity_action_with_empty_values('message_id', *args) + + def test_resource_show_with_empty_id(self): + args = ['resource-show'] + self._test_entity_action_with_empty_values('resource_id', *args) + + def test_sample_list_with_empty_meter(self): + args = ['sample-list'] + self._test_entity_action_with_empty_values('meter', *args) + + def test_sample_create_with_empty_meter(self): + args = ['sample-create', '-r', 'x', '--meter-type', 'gauge', + '--meter-unit', 'B', '--sample-volume', '1'] + self._test_entity_action_with_empty_values('meter-name', *args) + + def test_statistics_with_empty_meter(self): + args = ['statistics'] + self._test_entity_action_with_empty_values('meter', *args) + + def test_trait_description_list_with_empty_event_type(self): + args = ['trait-description-list'] + self._test_entity_action_with_empty_values('event_type', *args) + + def test_trait_list_with_empty_event_type(self): + args = ['trait-list', '--trait_name', 'x'] + self._test_entity_action_with_empty_values('event_type', *args) + + def test_trait_list_with_empty_trait_name(self): + args = ['trait-list', '--event_type', 'x'] + self._test_entity_action_with_empty_values('trait_name', *args) diff --git a/ceilometerclient/tests/v2/test_statistics.py b/ceilometerclient/tests/v2/test_statistics.py index ed3465c..65f633e 100644 --- a/ceilometerclient/tests/v2/test_statistics.py +++ b/ceilometerclient/tests/v2/test_statistics.py @@ -12,7 +12,8 @@ # 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 ceilometerclient.openstack.common.apiclient import client +from ceilometerclient.openstack.common.apiclient import fake_client from ceilometerclient.tests import utils import ceilometerclient.v2.statistics @@ -114,15 +115,16 @@ class StatisticsManagerTest(utils.BaseTestCase): def setUp(self): super(StatisticsManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = ceilometerclient.v2.statistics.StatisticsManager(self.api) def test_list_by_meter_name(self): stats = list(self.mgr.list(meter_name='instance')) expect = [ - ('GET', '/v2/meters/instance/statistics', {}, None), + 'GET', '/v2/meters/instance/statistics' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(stats), 1) self.assertEqual(stats[0].count, 135) @@ -135,10 +137,9 @@ class StatisticsManagerTest(utils.BaseTestCase): "value": "bar"}, ])) expect = [ - ('GET', - '%s?%s' % (base_url, qry), {}, None), + 'GET', '%s?%s' % (base_url, qry) ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(stats), 1) self.assertEqual(stats[0].count, 135) @@ -152,10 +153,9 @@ class StatisticsManagerTest(utils.BaseTestCase): ], period=60)) expect = [ - ('GET', - '%s?%s%s' % (base_url, qry, period), {}, None), + 'GET', '%s?%s%s' % (base_url, qry, period) ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(stats), 1) self.assertEqual(stats[0].count, 135) @@ -169,10 +169,10 @@ class StatisticsManagerTest(utils.BaseTestCase): ], groupby=['resource_id'])) expect = [ - ('GET', - '%s?%s%s' % (base_url, qry, groupby), {}, None), + 'GET', + '%s?%s%s' % (base_url, qry, groupby) ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(stats), 2) self.assertEqual(stats[0].count, 135) self.assertEqual(stats[1].count, 12) @@ -189,10 +189,10 @@ class StatisticsManagerTest(utils.BaseTestCase): ], groupby='resource_id')) expect = [ - ('GET', - '%s?%s%s' % (base_url, qry, groupby), {}, None), + 'GET', + '%s?%s%s' % (base_url, qry, groupby) ] - self.assertEqual(expect, self.api.calls) + self.http_client.assert_called(*expect) self.assertEqual(2, len(stats)) self.assertEqual(135, stats[0].count) self.assertEqual(12, stats[1].count) @@ -212,10 +212,10 @@ class StatisticsManagerTest(utils.BaseTestCase): stats = list(self.mgr.list(meter_name='instance', aggregates=aggregates)) expect = [ - ('GET', - '%s?%s' % (base_url, aggregate_query), {}, None), + 'GET', + '%s?%s' % (base_url, aggregate_query) ] - self.assertEqual(expect, self.api.calls) + self.http_client.assert_called(*expect) self.assertEqual(1, len(stats)) self.assertEqual(2, stats[0].count) self.assertEqual(2.0, stats[0].aggregate.get('count')) diff --git a/ceilometerclient/tests/v2/test_trait_descriptions.py b/ceilometerclient/tests/v2/test_trait_descriptions.py index 0b273f0..7913124 100644 --- a/ceilometerclient/tests/v2/test_trait_descriptions.py +++ b/ceilometerclient/tests/v2/test_trait_descriptions.py @@ -11,7 +11,8 @@ # 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 ceilometerclient.openstack.common.apiclient import client +from ceilometerclient.openstack.common.apiclient import fake_client from ceilometerclient.tests import utils import ceilometerclient.v2.trait_descriptions @@ -34,16 +35,17 @@ class TraitDescriptionManagerTest(utils.BaseTestCase): def setUp(self): super(TraitDescriptionManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = (ceilometerclient.v2.trait_descriptions. TraitDescriptionManager(self.api)) def test_list(self): trait_descriptions = list(self.mgr.list('Foo')) expect = [ - ('GET', '/v2/event_types/Foo/traits', {}, None), + 'GET', '/v2/event_types/Foo/traits' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(trait_descriptions), 3) for i, vals in enumerate([('trait_1', 'string'), ('trait_2', 'integer'), diff --git a/ceilometerclient/tests/v2/test_traits.py b/ceilometerclient/tests/v2/test_traits.py index f8239a0..1d7dde0 100644 --- a/ceilometerclient/tests/v2/test_traits.py +++ b/ceilometerclient/tests/v2/test_traits.py @@ -11,7 +11,8 @@ # 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 ceilometerclient.openstack.common.apiclient import client +from ceilometerclient.openstack.common.apiclient import fake_client from ceilometerclient.tests import utils import ceilometerclient.v2.traits @@ -37,15 +38,16 @@ class TraitManagerTest(utils.BaseTestCase): def setUp(self): super(TraitManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures) + self.api = client.BaseClient(self.http_client) self.mgr = ceilometerclient.v2.traits.TraitManager(self.api) def test_list(self): traits = list(self.mgr.list('Foo', 'trait_1')) expect = [ - ('GET', '/v2/event_types/Foo/traits/trait_1', {}, None), + 'GET', '/v2/event_types/Foo/traits/trait_1' ] - self.assertEqual(self.api.calls, expect) + self.http_client.assert_called(*expect) self.assertEqual(len(traits), 2) for i, vals in enumerate([('trait_1', 'datetime', diff --git a/ceilometerclient/v1/client.py b/ceilometerclient/v1/client.py index 89a5b44..e8827e2 100644 --- a/ceilometerclient/v1/client.py +++ b/ceilometerclient/v1/client.py @@ -13,7 +13,9 @@ # License for the specific language governing permissions and limitations # under the License. -from ceilometerclient.common import http + +from ceilometerclient import client as ceiloclient +from ceilometerclient.openstack.common.apiclient import client from ceilometerclient.v1 import meters @@ -29,7 +31,24 @@ class Client(object): def __init__(self, *args, **kwargs): """Initialize a new client for the Ceilometer v1 API.""" - self.http_client = http.HTTPClient(*args, **kwargs) + self.auth_plugin = kwargs.get('auth_plugin') \ + or ceiloclient.get_auth_plugin(*args, **kwargs) + self.client = client.HTTPClient( + auth_plugin=self.auth_plugin, + region_name=kwargs.get('region_name'), + endpoint_type=kwargs.get('endpoint_type'), + original_ip=kwargs.get('original_ip'), + verify=kwargs.get('verify'), + cert=kwargs.get('cacert'), + timeout=kwargs.get('timeout'), + timings=kwargs.get('timings'), + keyring_saver=kwargs.get('keyring_saver'), + debug=kwargs.get('debug'), + user_agent=kwargs.get('user_agent'), + http=kwargs.get('http') + ) + + self.http_client = client.BaseClient(self.client) self.meters = meters.MeterManager(self.http_client) self.samples = meters.SampleManager(self.http_client) self.users = meters.UserManager(self.http_client) diff --git a/ceilometerclient/v2/alarms.py b/ceilometerclient/v2/alarms.py index 7c46fb2..341cbb7 100644 --- a/ceilometerclient/v2/alarms.py +++ b/ceilometerclient/v2/alarms.py @@ -57,7 +57,8 @@ class Alarm(base.Resource): return self.manager.delete(self.alarm_id) def get_state(self): - return self.manager.get_state(self.alarm_id) + state = self.manager.get_state(self.alarm_id) + return state.get('alarm') class AlarmChange(base.Resource): @@ -167,14 +168,12 @@ class AlarmManager(base.Manager): return self._delete(self._path(alarm_id)) def set_state(self, alarm_id, state): - resp, body = self.api.json_request('PUT', - "%s/state" % self._path(alarm_id), - body=state) + body = self.api.put("%s/state" % self._path(alarm_id), + json=state).json() return body def get_state(self, alarm_id): - resp, body = self.api.json_request('GET', - "%s/state" % self._path(alarm_id)) + body = self.api.get("%s/state" % self._path(alarm_id)).json() return body def get_history(self, alarm_id, q=None): diff --git a/ceilometerclient/v2/client.py b/ceilometerclient/v2/client.py index ba5ca43..95be8f5 100644 --- a/ceilometerclient/v2/client.py +++ b/ceilometerclient/v2/client.py @@ -15,7 +15,8 @@ # License for the specific language governing permissions and limitations # under the License. -from ceilometerclient.common import http +from ceilometerclient import client as ceiloclient +from ceilometerclient.openstack.common.apiclient import client from ceilometerclient.v2 import alarms from ceilometerclient.v2 import event_types from ceilometerclient.v2 import events @@ -39,8 +40,26 @@ class Client(object): """ def __init__(self, *args, **kwargs): + """Initialize a new client for the Ceilometer v2 API.""" - self.http_client = http.HTTPClient(*args, **kwargs) + self.auth_plugin = kwargs.get('auth_plugin') \ + or ceiloclient.get_auth_plugin(*args, **kwargs) + self.client = client.HTTPClient( + auth_plugin=self.auth_plugin, + region_name=kwargs.get('region_name'), + endpoint_type=kwargs.get('endpoint_type'), + original_ip=kwargs.get('original_ip'), + verify=kwargs.get('verify'), + cert=kwargs.get('cacert'), + timeout=kwargs.get('timeout'), + timings=kwargs.get('timings'), + keyring_saver=kwargs.get('keyring_saver'), + debug=kwargs.get('debug'), + user_agent=kwargs.get('user_agent'), + http=kwargs.get('http') + ) + + self.http_client = client.BaseClient(self.client) self.meters = meters.MeterManager(self.http_client) self.samples = samples.SampleManager(self.http_client) self.statistics = statistics.StatisticsManager(self.http_client) @@ -51,6 +70,7 @@ class Client(object): self.traits = traits.TraitManager(self.http_client) self.trait_descriptions = trait_descriptions.\ TraitDescriptionManager(self.http_client) + self.query_samples = query.QuerySamplesManager( self.http_client) self.query_alarms = query.QueryAlarmsManager( diff --git a/ceilometerclient/v2/query.py b/ceilometerclient/v2/query.py index 85d2f6c..a11a56d 100644 --- a/ceilometerclient/v2/query.py +++ b/ceilometerclient/v2/query.py @@ -33,9 +33,9 @@ class QueryManager(base.Manager): query["limit"] = limit url = '/v2/query%s' % self.path_suffix - resp, body = self.api.json_request('POST', - url, - body=query) + + body = self.api.post(url, json=query).json() + if body: return [self.resource_class(self, b) for b in body] else: diff --git a/ceilometerclient/v2/samples.py b/ceilometerclient/v2/samples.py index cd0c3c2..b0ff3b5 100644 --- a/ceilometerclient/v2/samples.py +++ b/ceilometerclient/v2/samples.py @@ -47,8 +47,6 @@ class SampleManager(base.Manager): new = dict((key, value) for (key, value) in kwargs.items() if key in CREATION_ATTRIBUTES) url = self._path(counter_name=kwargs['counter_name']) - resp, body = self.api.json_request('POST', - url, - body=[new]) + body = self.api.post(url, json=new).json() if body: return [Sample(self, b) for b in body] diff --git a/ceilometerclient/v2/shell.py b/ceilometerclient/v2/shell.py index d0d2f75..72f780d 100644 --- a/ceilometerclient/v2/shell.py +++ b/ceilometerclient/v2/shell.py @@ -18,6 +18,7 @@ # License for the specific language governing permissions and limitations # under the License. +import argparse import functools import json @@ -48,11 +49,18 @@ COMPLEX_OPERATORS = ['and', 'or'] SIMPLE_OPERATORS = ["=", "!=", "<", "<=", '>', '>='] +class NotEmptyAction(argparse.Action): + def __call__(self, parser, namespace, values, option_string=None): + if not values or values.isspace(): + raise exc.CommandError('%s should not be empty' % self.dest) + setattr(namespace, self.dest, values) + + @utils.arg('-q', '--query', metavar='<QUERY>', help='key[op]data_type::value; list. data_type is optional, ' 'but if supplied must be string, integer, float, or boolean.') @utils.arg('-m', '--meter', metavar='<NAME>', required=True, - help='Name of meter to show samples for.') + action=NotEmptyAction, help='Name of meter to show samples for.') @utils.arg('-p', '--period', metavar='<PERIOD>', help='Period in seconds over which to group samples.') @utils.arg('-g', '--groupby', metavar='<FIELD>', action='append', @@ -106,7 +114,7 @@ def do_statistics(cc, args): help='key[op]data_type::value; list. data_type is optional, ' 'but if supplied must be string, integer, float, or boolean.') @utils.arg('-m', '--meter', metavar='<NAME>', required=True, - help='Name of meter to show samples for.') + action=NotEmptyAction, help='Name of meter to show samples for.') @utils.arg('-l', '--limit', metavar='<NUMBER>', help='Maximum number of samples to return.') def do_sample_list(cc, args): @@ -136,7 +144,7 @@ def do_sample_list(cc, args): @utils.arg('-r', '--resource-id', metavar='<RESOURCE_ID>', required=True, help='ID of the resource.') @utils.arg('-m', '--meter-name', metavar='<METER_NAME>', required=True, - help='The meter name.') + action=NotEmptyAction, help='The meter name.') @utils.arg('--meter-type', metavar='<METER_TYPE>', required=True, help='The meter type.') @utils.arg('--meter-unit', metavar='<METER_UNIT>', required=True, @@ -326,7 +334,7 @@ def _display_alarm(alarm): @utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True, - help='ID of the alarm to show.') + action=NotEmptyAction, help='ID of the alarm to show.') def do_alarm_show(cc, args={}): '''Show an alarm.''' alarm = cc.alarms.get(args.alarm_id) @@ -483,7 +491,7 @@ def do_alarm_combination_create(cc, args={}): @utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True, - help='ID of the alarm to update.') + action=NotEmptyAction, help='ID of the alarm to update.') @common_alarm_arguments() @utils.arg('--remove-time-constraint', action='append', metavar='<Constraint names>', @@ -524,7 +532,7 @@ def do_alarm_update(cc, args={}): @utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True, - help='ID of the alarm to update.') + action=NotEmptyAction, help='ID of the alarm to update.') @common_alarm_arguments() @utils.arg('--remove-time-constraint', action='append', metavar='<Constraint names>', @@ -576,7 +584,7 @@ def do_alarm_threshold_update(cc, args={}): @utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True, - help='ID of the alarm to update.') + action=NotEmptyAction, help='ID of the alarm to update.') @common_alarm_arguments() @utils.arg('--remove-time-constraint', action='append', metavar='<Constraint names>', @@ -608,7 +616,7 @@ def do_alarm_combination_update(cc, args={}): @utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True, - help='ID of the alarm to delete.') + action=NotEmptyAction, help='ID of the alarm to delete.') def do_alarm_delete(cc, args={}): '''Delete an alarm.''' try: @@ -618,7 +626,7 @@ def do_alarm_delete(cc, args={}): @utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True, - help='ID of the alarm state to set.') + action=NotEmptyAction, help='ID of the alarm state to set.') @utils.arg('--state', metavar='<STATE>', required=True, help='State of the alarm, one of: ' + str(ALARM_STATES) + '.') @@ -632,7 +640,7 @@ def do_alarm_state_set(cc, args={}): @utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True, - help='ID of the alarm state to show.') + action=NotEmptyAction, help='ID of the alarm state to show.') def do_alarm_state_get(cc, args={}): '''Get the state of an alarm.''' try: @@ -643,6 +651,7 @@ def do_alarm_state_get(cc, args={}): @utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True, + action=NotEmptyAction, help='ID of the alarm for which history is shown.') @utils.arg('-q', '--query', metavar='<QUERY>', help='key[op]data_type::value; list. data_type is optional, ' @@ -680,7 +689,7 @@ def do_resource_list(cc, args={}): @utils.arg('-r', '--resource_id', metavar='<RESOURCE_ID>', required=True, - help='ID of the resource to show.') + action=NotEmptyAction, help='ID of the resource to show.') def do_resource_show(cc, args={}): '''Show the resource.''' try: @@ -712,7 +721,7 @@ def do_event_list(cc, args={}): @utils.arg('-m', '--message_id', metavar='<message_id>', help='The ID of the event. Should be a UUID.', - required=True) + required=True, action=NotEmptyAction) def do_event_show(cc, args={}): '''Show a particular event.''' event = cc.events.get(args.message_id) @@ -729,7 +738,7 @@ def do_event_type_list(cc, args={}): @utils.arg('-e', '--event_type', metavar='<EVENT_TYPE>', help='Type of the event for which traits will be shown.', - required=True) + required=True, action=NotEmptyAction) def do_trait_description_list(cc, args={}): '''List trait info for an event type.''' trait_descriptions = cc.trait_descriptions.list(args.event_type) @@ -740,10 +749,10 @@ def do_trait_description_list(cc, args={}): @utils.arg('-e', '--event_type', metavar='<EVENT_TYPE>', help='Type of the event for which traits will listed.', - required=True) + required=True, action=NotEmptyAction) @utils.arg('-t', '--trait_name', metavar='<TRAIT_NAME>', help='The name of the trait to list.', - required=True) + required=True, action=NotEmptyAction) def do_trait_list(cc, args={}): '''List trait all traits with name <trait_name> for Event Type <event_type>. diff --git a/doc/source/conf.py b/doc/source/conf.py index 0d030d1..185f758 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -6,7 +6,7 @@ project = 'python-ceilometerclient' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx'] +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'oslosphinx' ] # autodoc generation is a bit aggressive and a nuisance when doing heavy # text edit cycles. @@ -38,7 +38,7 @@ pygments_style = 'sphinx' # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. -html_theme = 'nature' +#html_theme = 'nature' # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project diff --git a/requirements.txt b/requirements.txt index 6123791..cb199b5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,5 +2,6 @@ pbr>=0.6,!=0.7,<1.0 argparse iso8601>=0.1.9 PrettyTable>=0.7,<0.8 -python-keystoneclient>=0.9.0 +python-keystoneclient>=0.10.0 six>=1.7.0 +stevedore>=0.14 diff --git a/test-requirements.txt b/test-requirements.txt index bb5e7e6..41a8186 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,6 +4,7 @@ coverage>=3.6 discover fixtures>=0.3.14 mock>=1.0 +oslosphinx>=2.2.0.0a2 python-subunit>=0.0.18 sphinx>=1.1.2,!=1.2.0,<1.3 testrepository>=0.0.18 diff --git a/tools/install_venv.py b/tools/install_venv.py index 5010527..1bca03e 100644 --- a/tools/install_venv.py +++ b/tools/install_venv.py @@ -65,7 +65,6 @@ def main(argv): install.check_dependencies() install.create_virtualenv(no_site_packages=options.no_site_packages) install.install_dependencies() - install.post_process() print_help(venv, root) if __name__ == '__main__': @@ -24,6 +24,10 @@ commands = python setup.py testr --coverage --testr-args='{posargs}' [testenv:venv] commands = {posargs} +[testenv:docs] +commands= + python setup.py build_sphinx + [flake8] # H405 multi line docstring summary not separated with an empty line # H904 Wrap long lines in parentheses instead of a backslash |