diff options
author | Julien Danjou <julien@danjou.info> | 2017-11-22 10:08:58 +0100 |
---|---|---|
committer | Julien Danjou <julien@danjou.info> | 2017-11-22 10:09:10 +0100 |
commit | cc6639ad63e80f50012b24695619e5d771462031 (patch) | |
tree | a1a93f5ffce81ad930abca6e0c11e904b7b362d8 | |
parent | 3ff6bac3da348bad0b220ab26eab6ad6bfbeb675 (diff) | |
download | python-ceilometerclient-cc6639ad63e80f50012b24695619e5d771462031.tar.gz |
Retire ceilometerclient
Depends-On: Ic2947fd066a2df685d52539d0756cd981cc16113
Change-Id: I65738a55db82053643f7580eee20b63df52b957f
90 files changed, 10 insertions, 14032 deletions
diff --git a/.gitignore b/.gitignore deleted file mode 100644 index f7e0f31..0000000 --- a/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -*.pyc -*.egg-info -build -.coverage -.tox -cover -.testrepository -.venv -doc/build -releasenotes/build -doc/source/ref -subunit.log -AUTHORS -ChangeLog -dist -*.egg diff --git a/.gitreview b/.gitreview deleted file mode 100644 index c4b5cc4..0000000 --- a/.gitreview +++ /dev/null @@ -1,4 +0,0 @@ -[gerrit] -host=review.openstack.org -port=29418 -project=openstack/python-ceilometerclient.git diff --git a/.testr.conf b/.testr.conf deleted file mode 100644 index 77a2a70..0000000 --- a/.testr.conf +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./ceilometerclient/tests/unit} $LISTOPT $IDOPTION -test_id_option=--load-list $IDFILE -test_list_option=--list diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index b5abcbd..0000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,16 +0,0 @@ -If you would like to contribute to the development of OpenStack, -you must follow the steps documented at: - - https://docs.openstack.org/infra/manual/developers.html#development-workflow - -Once those steps have been completed, changes to OpenStack -should be submitted for review via the Gerrit tool, following -the workflow documented at: - - https://docs.openstack.org/infra/manual/developers.html#development-workflow - -Pull requests submitted through GitHub will be ignored. - -Bugs should be filed on Launchpad, not GitHub: - - https://bugs.launchpad.net/python-ceilometerclient diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 67db858..0000000 --- a/LICENSE +++ /dev/null @@ -1,175 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. @@ -0,0 +1,10 @@ +This project is no longer maintained. + +The contents of this repository are still available in the Git +source code management system. To see the contents of this +repository before it reached its end of life, please check out the +previous commit with "git checkout HEAD^1". + +For any further questions, please email +openstack-dev@lists.openstack.org or join #openstack-dev on +Freenode. diff --git a/README.rst b/README.rst deleted file mode 100644 index d1dccd5..0000000 --- a/README.rst +++ /dev/null @@ -1,28 +0,0 @@ -Python bindings to the Ceilometer API -===================================== - -.. image:: https://img.shields.io/pypi/v/python-ceilometerclient.svg - :target: https://pypi.python.org/pypi/python-ceilometerclient/ - :alt: Latest Version - -.. image:: https://img.shields.io/pypi/dm/python-ceilometerclient.svg - :target: https://pypi.python.org/pypi/python-ceilometerclient/ - :alt: Downloads - -This is a client library for Ceilometer built on the Ceilometer API. It -provides a Python API (the ``ceilometerclient`` module) and a command-line tool -(``ceilometer``). - -* `PyPi`_ - package installation -* `Online Documentation`_ -* `Launchpad project`_ - release management -* `Blueprints`_ - feature specifications -* `Bugs`_ - issue tracking -* `Source`_ - -.. _PyPi: https://pypi.python.org/pypi/python-ceilometerclient -.. _Online Documentation: https://docs.openstack.org/python-ceilometerclient/latest/ -.. _Launchpad project: https://launchpad.net/python-ceilometerclient -.. _Blueprints: https://blueprints.launchpad.net/python-ceilometerclient -.. _Bugs: https://bugs.launchpad.net/python-ceilometerclient -.. _Source: https://git.openstack.org/cgit/openstack/python-ceilometerclient diff --git a/ceilometerclient/__init__.py b/ceilometerclient/__init__.py deleted file mode 100644 index d6efb69..0000000 --- a/ceilometerclient/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# 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. - -__all__ = ['__version__'] - -import pbr.version - -version_info = pbr.version.VersionInfo('python-ceilometerclient') -try: - __version__ = version_info.version_string() -except AttributeError: - __version__ = None diff --git a/ceilometerclient/apiclient/__init__.py b/ceilometerclient/apiclient/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/ceilometerclient/apiclient/__init__.py +++ /dev/null diff --git a/ceilometerclient/apiclient/auth.py b/ceilometerclient/apiclient/auth.py deleted file mode 100644 index 41d118f..0000000 --- a/ceilometerclient/apiclient/auth.py +++ /dev/null @@ -1,231 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# Copyright 2013 Spanish National Research Council. -# 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. - -# E0202: An attribute inherited from %s hide this method -# pylint: disable=E0202 - -######################################################################## -# -# THIS MODULE IS DEPRECATED -# -# Please refer to -# https://etherpad.openstack.org/p/kilo-ceilometerclient-library-proposals for -# the discussion leading to this deprecation. -# -# We recommend checking out the python-openstacksdk project -# (https://launchpad.net/python-openstacksdk) instead. -# -######################################################################## - -import abc -import argparse -import os - -import six -from stevedore import extension - -from ceilometerclient.apiclient import exceptions - - -_discovered_plugins = {} - - -def discover_auth_systems(): - """Discover the available auth-systems. - - This won't take into account the old style auth-systems. - """ - global _discovered_plugins - _discovered_plugins = {} - - def add_plugin(ext): - _discovered_plugins[ext.name] = ext.plugin - - ep_namespace = "ceilometerclient.apiclient.auth" - mgr = extension.ExtensionManager(ep_namespace) - mgr.map(add_plugin) - - -def load_auth_system_opts(parser): - """Load options needed by the available auth-systems into a parser. - - This function will try to populate the parser with options from the - available plugins. - """ - group = parser.add_argument_group("Common auth options") - BaseAuthPlugin.add_common_opts(group) - for name, auth_plugin in six.iteritems(_discovered_plugins): - group = parser.add_argument_group( - "Auth-system '%s' options" % name, - conflict_handler="resolve") - auth_plugin.add_opts(group) - - -def load_plugin(auth_system): - try: - plugin_class = _discovered_plugins[auth_system] - except KeyError: - raise exceptions.AuthSystemNotFound(auth_system) - return plugin_class(auth_system=auth_system) - - -def load_plugin_from_args(args): - """Load required plugin and populate it with options. - - Try to guess auth system if it is not specified. Systems are tried in - alphabetical order. - - :type args: argparse.Namespace - :raises: AuthPluginOptionsMissing - """ - auth_system = args.os_auth_system - if auth_system: - plugin = load_plugin(auth_system) - plugin.parse_opts(args) - plugin.sufficient_options() - return plugin - - for plugin_auth_system in sorted(six.iterkeys(_discovered_plugins)): - plugin_class = _discovered_plugins[plugin_auth_system] - plugin = plugin_class() - plugin.parse_opts(args) - try: - plugin.sufficient_options() - except exceptions.AuthPluginOptionsMissing: - continue - return plugin - raise exceptions.AuthPluginOptionsMissing(["auth_system"]) - - -@six.add_metaclass(abc.ABCMeta) -class BaseAuthPlugin(object): - """Base class for authentication plugins. - - An authentication plugin needs to override at least the authenticate - method to be a valid plugin. - """ - - auth_system = None - opt_names = [] - common_opt_names = [ - "auth_system", - "username", - "password", - "tenant_name", - "token", - "auth_url", - ] - - def __init__(self, auth_system=None, **kwargs): - self.auth_system = auth_system or self.auth_system - self.opts = dict((name, kwargs.get(name)) - for name in self.opt_names) - - @staticmethod - def _parser_add_opt(parser, opt): - """Add an option to parser in two variants. - - :param opt: option name (with underscores) - """ - dashed_opt = opt.replace("_", "-") - env_var = "OS_%s" % opt.upper() - arg_default = os.environ.get(env_var, "") - arg_help = "Defaults to env[%s]." % env_var - parser.add_argument( - "--os-%s" % dashed_opt, - metavar="<%s>" % dashed_opt, - default=arg_default, - help=arg_help) - parser.add_argument( - "--os_%s" % opt, - metavar="<%s>" % dashed_opt, - help=argparse.SUPPRESS) - - @classmethod - def add_opts(cls, parser): - """Populate the parser with the options for this plugin.""" - for opt in cls.opt_names: - # use `BaseAuthPlugin.common_opt_names` since it is never - # changed in child classes - if opt not in BaseAuthPlugin.common_opt_names: - cls._parser_add_opt(parser, opt) - - @classmethod - def add_common_opts(cls, parser): - """Add options that are common for several plugins.""" - for opt in cls.common_opt_names: - cls._parser_add_opt(parser, opt) - - @staticmethod - def get_opt(opt_name, args): - """Return option name and value. - - :param opt_name: name of the option, e.g., "username" - :param args: parsed arguments - """ - return (opt_name, getattr(args, "os_%s" % opt_name, None)) - - def parse_opts(self, args): - """Parse the actual auth-system options if any. - - This method is expected to populate the attribute `self.opts` with a - dict containing the options and values needed to make authentication. - """ - self.opts.update(dict(self.get_opt(opt_name, args) - for opt_name in self.opt_names)) - - def authenticate(self, http_client): - """Authenticate using plugin defined method. - - The method usually analyses `self.opts` and performs - a request to authentication server. - - :param http_client: client object that needs authentication - :type http_client: HTTPClient - :raises: AuthorizationFailure - """ - self.sufficient_options() - self._do_authenticate(http_client) - - @abc.abstractmethod - def _do_authenticate(self, http_client): - """Protected method for authentication.""" - - def sufficient_options(self): - """Check if all required options are present. - - :raises: AuthPluginOptionsMissing - """ - missing = [opt - for opt in self.opt_names - if not self.opts.get(opt)] - if missing: - raise exceptions.AuthPluginOptionsMissing(missing) - - @abc.abstractmethod - def token_and_endpoint(self, endpoint_type, service_type): - """Return token and endpoint. - - :param service_type: Service type of the endpoint - :type service_type: string - :param endpoint_type: Type of endpoint. - Possible values: public or publicURL, - internal or internalURL, - admin or adminURL - :type endpoint_type: string - :returns: tuple of token and endpoint strings - :raises: EndpointException - """ diff --git a/ceilometerclient/apiclient/base.py b/ceilometerclient/apiclient/base.py deleted file mode 100644 index 6e9990a..0000000 --- a/ceilometerclient/apiclient/base.py +++ /dev/null @@ -1,535 +0,0 @@ -# Copyright 2010 Jacob Kaplan-Moss -# Copyright 2011 OpenStack Foundation -# Copyright 2012 Grid Dynamics -# Copyright 2013 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. - -""" -Base utilities to build API operation managers and objects on top of. -""" - -######################################################################## -# -# THIS MODULE IS DEPRECATED -# -# Please refer to -# https://etherpad.openstack.org/p/kilo-ceilometerclient-library-proposals for -# the discussion leading to this deprecation. -# -# We recommend checking out the python-openstacksdk project -# (https://launchpad.net/python-openstacksdk) instead. -# -######################################################################## - - -# E1102: %s is not callable -# pylint: disable=E1102 - -import abc -import copy - -from oslo_utils import reflection -from oslo_utils import strutils -import six -from six.moves.urllib import parse - -from ceilometerclient.apiclient import exceptions -from ceilometerclient.i18n import _ - - -def getid(obj): - """Return id if argument is a Resource. - - Abstracts the common pattern of allowing both an object or an object's ID - (UUID) as a parameter when dealing with relationships. - """ - try: - if obj.uuid: - return obj.uuid - except AttributeError: - pass - try: - return obj.id - except AttributeError: - return obj - - -# TODO(aababilov): call run_hooks() in HookableMixin's child classes -class HookableMixin(object): - """Mixin so classes can register and run hooks.""" - _hooks_map = {} - - @classmethod - def add_hook(cls, hook_type, hook_func): - """Add a new hook of specified type. - - :param cls: class that registers hooks - :param hook_type: hook type, e.g., '__pre_parse_args__' - :param hook_func: hook function - """ - if hook_type not in cls._hooks_map: - cls._hooks_map[hook_type] = [] - - cls._hooks_map[hook_type].append(hook_func) - - @classmethod - def run_hooks(cls, hook_type, *args, **kwargs): - """Run all hooks of specified type. - - :param cls: class that registers hooks - :param hook_type: hook type, e.g., '__pre_parse_args__' - :param args: args to be passed to every hook function - :param kwargs: kwargs to be passed to every hook function - """ - hook_funcs = cls._hooks_map.get(hook_type) or [] - for hook_func in hook_funcs: - hook_func(*args, **kwargs) - - -class BaseManager(HookableMixin): - """Basic manager type providing common operations. - - Managers interact with a particular type of API (servers, flavors, images, - etc.) and provide CRUD operations for them. - """ - resource_class = None - - def __init__(self, client): - """Initializes BaseManager with `client`. - - :param client: instance of BaseClient descendant for HTTP requests - """ - super(BaseManager, self).__init__() - self.client = client - - def _list(self, url, response_key=None, obj_class=None, json=None): - """List the collection. - - :param url: a partial URL, e.g., '/servers' - :param response_key: the key to be looked up in response dictionary, - e.g., 'servers'. If response_key is None - all response body - will be used. - :param obj_class: class for constructing the returned objects - (self.resource_class will be used by default) - :param json: data that will be encoded as JSON and passed in POST - request (GET will be sent by default) - """ - if json: - body = self.client.post(url, json=json).json() - else: - body = self.client.get(url).json() - - if obj_class is None: - obj_class = self.resource_class - - data = body[response_key] if response_key is not None else body - # NOTE(ja): keystone returns values as list as {'values': [ ... ]} - # unlike other services which just return the list... - try: - data = data['values'] - except (KeyError, TypeError): - pass - - return [obj_class(self, res, loaded=True) for res in data if res] - - def _get(self, url, response_key=None): - """Get an object from collection. - - :param url: a partial URL, e.g., '/servers' - :param response_key: the key to be looked up in response dictionary, - e.g., 'server'. If response_key is None - all response body - will be used. - """ - body = self.client.get(url).json() - data = body[response_key] if response_key is not None else body - return self.resource_class(self, data, loaded=True) - - def _head(self, url): - """Retrieve request headers for an object. - - :param url: a partial URL, e.g., '/servers' - """ - resp = self.client.head(url) - return resp.status_code == 204 - - def _post(self, url, json, response_key=None, return_raw=False): - """Create an object. - - :param url: a partial URL, e.g., '/servers' - :param json: data that will be encoded as JSON and passed in POST - request (GET will be sent by default) - :param response_key: the key to be looked up in response dictionary, - e.g., 'server'. If response_key is None - all response body - will be used. - :param return_raw: flag to force returning raw JSON instead of - Python object of self.resource_class - """ - body = self.client.post(url, json=json).json() - data = body[response_key] if response_key is not None else body - if return_raw: - return data - return self.resource_class(self, data) - - def _put(self, url, json=None, response_key=None): - """Update an object with PUT method. - - :param url: a partial URL, e.g., '/servers' - :param json: data that will be encoded as JSON and passed in POST - request (GET will be sent by default) - :param response_key: the key to be looked up in response dictionary, - e.g., 'servers'. If response_key is None - all response body - will be used. - """ - resp = self.client.put(url, json=json) - # PUT requests may not return a body - if resp.content: - body = resp.json() - if response_key is not None: - return self.resource_class(self, body[response_key]) - else: - return self.resource_class(self, body) - - def _patch(self, url, json=None, response_key=None): - """Update an object with PATCH method. - - :param url: a partial URL, e.g., '/servers' - :param json: data that will be encoded as JSON and passed in POST - request (GET will be sent by default) - :param response_key: the key to be looked up in response dictionary, - e.g., 'servers'. If response_key is None - all response body - will be used. - """ - body = self.client.patch(url, json=json).json() - if response_key is not None: - return self.resource_class(self, body[response_key]) - else: - return self.resource_class(self, body) - - def _delete(self, url): - """Delete an object. - - :param url: a partial URL, e.g., '/servers/my-server' - """ - return self.client.delete(url) - - -@six.add_metaclass(abc.ABCMeta) -class ManagerWithFind(BaseManager): - """Manager with additional `find()`/`findall()` methods.""" - - @abc.abstractmethod - def list(self): - pass - - def find(self, **kwargs): - """Find a single item with attributes matching ``**kwargs``. - - This isn't very efficient: it loads the entire list then filters on - the Python side. - """ - matches = self.findall(**kwargs) - num_matches = len(matches) - if num_matches == 0: - msg = _("No %(name)s matching %(args)s.") % { - 'name': self.resource_class.__name__, - 'args': kwargs - } - raise exceptions.NotFound(msg) - elif num_matches > 1: - raise exceptions.NoUniqueMatch() - else: - return matches[0] - - def findall(self, **kwargs): - """Find all items with attributes matching ``**kwargs``. - - This isn't very efficient: it loads the entire list then filters on - the Python side. - """ - found = [] - searches = kwargs.items() - - for obj in self.list(): - try: - if all(getattr(obj, attr) == value - for (attr, value) in searches): - found.append(obj) - except AttributeError: - continue - - return found - - -class CrudManager(BaseManager): - """Base manager class for manipulating entities. - - Children of this class are expected to define a `collection_key` and `key`. - - - `collection_key`: Usually a plural noun by convention (e.g. `entities`); - used to refer collections in both URL's (e.g. `/v3/entities`) and JSON - objects containing a list of member resources (e.g. `{'entities': [{}, - {}, {}]}`). - - `key`: Usually a singular noun by convention (e.g. `entity`); used to - refer to an individual member of the collection. - - """ - collection_key = None - key = None - - def build_url(self, base_url=None, **kwargs): - """Builds a resource URL for the given kwargs. - - Given an example collection where `collection_key = 'entities'` and - `key = 'entity'`, the following URL's could be generated. - - By default, the URL will represent a collection of entities, e.g.:: - - /entities - - If kwargs contains an `entity_id`, then the URL will represent a - specific member, e.g.:: - - /entities/{entity_id} - - :param base_url: if provided, the generated URL will be appended to it - """ - url = base_url if base_url is not None else '' - - url += '/%s' % self.collection_key - - # do we have a specific entity? - entity_id = kwargs.get('%s_id' % self.key) - if entity_id is not None: - url += '/%s' % entity_id - - return url - - def _filter_kwargs(self, kwargs): - """Drop null values and handle ids.""" - for key, ref in six.iteritems(kwargs.copy()): - if ref is None: - kwargs.pop(key) - else: - if isinstance(ref, Resource): - kwargs.pop(key) - kwargs['%s_id' % key] = getid(ref) - return kwargs - - def create(self, **kwargs): - kwargs = self._filter_kwargs(kwargs) - return self._post( - self.build_url(**kwargs), - {self.key: kwargs}, - self.key) - - def get(self, **kwargs): - kwargs = self._filter_kwargs(kwargs) - return self._get( - self.build_url(**kwargs), - self.key) - - def head(self, **kwargs): - kwargs = self._filter_kwargs(kwargs) - return self._head(self.build_url(**kwargs)) - - def list(self, base_url=None, **kwargs): - """List the collection. - - :param base_url: if provided, the generated URL will be appended to it - """ - kwargs = self._filter_kwargs(kwargs) - - return self._list( - '%(base_url)s%(query)s' % { - 'base_url': self.build_url(base_url=base_url, **kwargs), - 'query': '?%s' % parse.urlencode(kwargs) if kwargs else '', - }, - self.collection_key) - - def put(self, base_url=None, **kwargs): - """Update an element. - - :param base_url: if provided, the generated URL will be appended to it - """ - kwargs = self._filter_kwargs(kwargs) - - return self._put(self.build_url(base_url=base_url, **kwargs)) - - def update(self, **kwargs): - kwargs = self._filter_kwargs(kwargs) - params = kwargs.copy() - params.pop('%s_id' % self.key) - - return self._patch( - self.build_url(**kwargs), - {self.key: params}, - self.key) - - def delete(self, **kwargs): - kwargs = self._filter_kwargs(kwargs) - - return self._delete( - self.build_url(**kwargs)) - - def find(self, base_url=None, **kwargs): - """Find a single item with attributes matching ``**kwargs``. - - :param base_url: if provided, the generated URL will be appended to it - """ - kwargs = self._filter_kwargs(kwargs) - - rl = self._list( - '%(base_url)s%(query)s' % { - 'base_url': self.build_url(base_url=base_url, **kwargs), - 'query': '?%s' % parse.urlencode(kwargs) if kwargs else '', - }, - self.collection_key) - num = len(rl) - - if num == 0: - msg = _("No %(name)s matching %(args)s.") % { - 'name': self.resource_class.__name__, - 'args': kwargs - } - raise exceptions.NotFound(404, msg) - elif num > 1: - raise exceptions.NoUniqueMatch - else: - return rl[0] - - -class Extension(HookableMixin): - """Extension descriptor.""" - - SUPPORTED_HOOKS = ('__pre_parse_args__', '__post_parse_args__') - manager_class = None - - def __init__(self, name, module): - super(Extension, self).__init__() - self.name = name - self.module = module - self._parse_extension_module() - - def _parse_extension_module(self): - self.manager_class = None - for attr_name, attr_value in self.module.__dict__.items(): - if attr_name in self.SUPPORTED_HOOKS: - self.add_hook(attr_name, attr_value) - else: - try: - if issubclass(attr_value, BaseManager): - self.manager_class = attr_value - except TypeError: - pass - - def __repr__(self): - return "<Extension '%s'>" % self.name - - -class Resource(object): - """Base class for OpenStack resources (tenant, user, etc.). - - This is pretty much just a bag for attributes. - """ - - HUMAN_ID = False - NAME_ATTR = 'name' - - def __init__(self, manager, info, loaded=False): - """Populate and bind to a manager. - - :param manager: BaseManager object - :param info: dictionary representing resource attributes - :param loaded: prevent lazy-loading if set to True - """ - self.manager = manager - self._info = info - self._add_details(info) - self._loaded = loaded - - def __repr__(self): - reprkeys = sorted(k - for k in self.__dict__.keys() - if k[0] != '_' and k != 'manager') - info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) - self_cls_name = reflection.get_class_name(self, - fully_qualified=False) - return "<%s %s>" % (self_cls_name, info) - - @property - def human_id(self): - """Human-readable ID which can be used for bash completion.""" - if self.HUMAN_ID: - name = getattr(self, self.NAME_ATTR, None) - if name is not None: - return strutils.to_slug(name) - return None - - def _add_details(self, info): - for (k, v) in six.iteritems(info): - try: - setattr(self, k, v) - self._info[k] = v - except AttributeError: - # In this case we already defined the attribute on the class - pass - - def __getattr__(self, k): - if k not in self.__dict__: - # NOTE(bcwaldon): disallow lazy-loading if already loaded once - if not self.is_loaded(): - self.get() - return self.__getattr__(k) - - raise AttributeError(k) - else: - return self.__dict__[k] - - def get(self): - """Support for lazy loading details. - - Some clients, such as novaclient have the option to lazy load the - details, details which can be loaded with this function. - """ - # set_loaded() first ... so if we have to bail, we know we tried. - self.set_loaded(True) - if not hasattr(self.manager, 'get'): - return - - new = self.manager.get(self.id) - if new: - self._add_details(new._info) - self._add_details( - {'x_request_id': self.manager.client.last_request_id}) - - def __eq__(self, other): - if not isinstance(other, Resource): - return NotImplemented - # two resources of different types are not equal - if not isinstance(other, self.__class__): - return False - return self._info == other._info - - def __ne__(self, other): - return not self.__eq__(other) - - def is_loaded(self): - return self._loaded - - def set_loaded(self, val): - self._loaded = val - - def to_dict(self): - return copy.deepcopy(self._info) diff --git a/ceilometerclient/apiclient/client.py b/ceilometerclient/apiclient/client.py deleted file mode 100644 index df33159..0000000 --- a/ceilometerclient/apiclient/client.py +++ /dev/null @@ -1,388 +0,0 @@ -# Copyright 2010 Jacob Kaplan-Moss -# Copyright 2011 OpenStack Foundation -# Copyright 2011 Piston Cloud Computing, Inc. -# Copyright 2013 Alessio Ababilov -# Copyright 2013 Grid Dynamics -# Copyright 2013 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. - -""" -OpenStack Client interface. Handles the REST calls and responses. -""" - -# E0202: An attribute inherited from %s hide this method -# pylint: disable=E0202 - -import hashlib -import logging -import time - -try: - import simplejson as json -except ImportError: - import json - -from oslo_utils import encodeutils -from oslo_utils import importutils -import requests - -from ceilometerclient.apiclient import exceptions -from ceilometerclient.i18n import _ - -_logger = logging.getLogger(__name__) -SENSITIVE_HEADERS = ('X-Auth-Token', 'X-Subject-Token',) - - -class HTTPClient(object): - """This client handles sending HTTP requests to OpenStack servers. - - Features: - - - share authentication information between several clients to different - services (e.g., for compute and image clients); - - reissue authentication request for expired tokens; - - encode/decode JSON bodies; - - raise exceptions on HTTP errors; - - pluggable authentication; - - store authentication information in a keyring; - - store time spent for requests; - - register clients for particular services, so one can use - `http_client.identity` or `http_client.compute`; - - log requests and responses in a format that is easy to copy-and-paste - into terminal and send the same request with curl. - """ - - user_agent = "ceilometerclient.apiclient" - - def __init__(self, - auth_plugin, - region_name=None, - endpoint_type="publicURL", - original_ip=None, - verify=True, - cert=None, - timeout=None, - timings=False, - keyring_saver=None, - debug=False, - user_agent=None, - http=None): - self.auth_plugin = auth_plugin - - self.endpoint_type = endpoint_type - self.region_name = region_name - - self.original_ip = original_ip - self.timeout = timeout - self.verify = verify - self.cert = cert - - self.keyring_saver = keyring_saver - self.debug = debug - self.user_agent = user_agent or self.user_agent - - self.times = [] # [("item", starttime, endtime), ...] - self.timings = timings - - # requests within the same session can reuse TCP connections from pool - self.http = http or requests.Session() - - self.cached_token = None - self.last_request_id = None - - def _safe_header(self, name, value): - if name in SENSITIVE_HEADERS: - # because in python3 byte string handling is ... ug - v = value.encode('utf-8') - h = hashlib.sha1(v) - d = h.hexdigest() - return encodeutils.safe_decode(name), "{SHA1}%s" % d - else: - return (encodeutils.safe_decode(name), - encodeutils.safe_decode(value)) - - def _http_log_req(self, method, url, kwargs): - if not self.debug: - return - - string_parts = [ - "curl -g -i", - "-X '%s'" % method, - "'%s'" % url, - ] - - for element in kwargs['headers']: - header = ("-H '%s: %s'" % - self._safe_header(element, kwargs['headers'][element])) - string_parts.append(header) - - _logger.debug("REQ: %s", " ".join(string_parts)) - if 'data' in kwargs: - _logger.debug("REQ BODY: %s\n", kwargs['data']) - - def _http_log_resp(self, resp): - if not self.debug: - return - _logger.debug( - "RESP: [%s] %s\n", - resp.status_code, - resp.headers) - if resp._content_consumed: - _logger.debug( - "RESP BODY: %s\n", - resp.text) - - def serialize(self, kwargs): - if kwargs.get('json') is not None: - kwargs['headers']['Content-Type'] = 'application/json' - kwargs['data'] = json.dumps(kwargs['json']) - try: - del kwargs['json'] - except KeyError: - pass - - def get_timings(self): - return self.times - - def reset_timings(self): - self.times = [] - - def request(self, method, url, **kwargs): - """Send an http request with the specified characteristics. - - Wrapper around `requests.Session.request` to handle tasks such as - setting headers, JSON encoding/decoding, and error handling. - - :param method: method of HTTP request - :param url: URL of HTTP request - :param kwargs: any other parameter that can be passed to - requests.Session.request (such as `headers`) or `json` - that will be encoded as JSON and used as `data` argument - """ - kwargs.setdefault("headers", {}) - kwargs["headers"]["User-Agent"] = self.user_agent - if self.original_ip: - kwargs["headers"]["Forwarded"] = "for=%s;by=%s" % ( - self.original_ip, self.user_agent) - if self.timeout is not None: - kwargs.setdefault("timeout", self.timeout) - kwargs.setdefault("verify", self.verify) - if self.cert is not None: - kwargs.setdefault("cert", self.cert) - self.serialize(kwargs) - - self._http_log_req(method, url, kwargs) - if self.timings: - start_time = time.time() - resp = self.http.request(method, url, **kwargs) - if self.timings: - self.times.append(("%s %s" % (method, url), - start_time, time.time())) - self._http_log_resp(resp) - - self.last_request_id = resp.headers.get('x-openstack-request-id') - - if resp.status_code >= 400: - _logger.debug( - "Request returned failure status: %s", - resp.status_code) - raise exceptions.from_response(resp, method, url) - - return resp - - @staticmethod - def concat_url(endpoint, url): - """Concatenate endpoint and final URL. - - E.g., "http://keystone/v2.0/" and "/tokens" are concatenated to - "http://keystone/v2.0/tokens". - - :param endpoint: the base URL - :param url: the final URL - """ - return "%s/%s" % (endpoint.rstrip("/"), url.strip("/")) - - def client_request(self, client, method, url, **kwargs): - """Send an http request using `client`'s endpoint and specified `url`. - - If request was rejected as unauthorized (possibly because the token is - expired), issue one authorization attempt and send the request once - again. - - :param client: instance of BaseClient descendant - :param method: method of HTTP request - :param url: URL of HTTP request - :param kwargs: any other parameter that can be passed to - `HTTPClient.request` - """ - - filter_args = { - "endpoint_type": client.endpoint_type or self.endpoint_type, - "service_type": client.service_type, - } - token, endpoint = (self.cached_token, client.cached_endpoint) - just_authenticated = False - if not (token and endpoint): - try: - token, endpoint = self.auth_plugin.token_and_endpoint( - **filter_args) - except exceptions.EndpointException: - pass - if not (token and endpoint): - self.authenticate() - just_authenticated = True - token, endpoint = self.auth_plugin.token_and_endpoint( - **filter_args) - if not (token and endpoint): - raise exceptions.AuthorizationFailure( - _("Cannot find endpoint or token for request")) - - old_token_endpoint = (token, endpoint) - kwargs.setdefault("headers", {})["X-Auth-Token"] = token - self.cached_token = token - client.cached_endpoint = endpoint - # Perform the request once. If we get Unauthorized, then it - # might be because the auth token expired, so try to - # re-authenticate and try again. If it still fails, bail. - try: - return self.request( - method, self.concat_url(endpoint, url), **kwargs) - except exceptions.Unauthorized as unauth_ex: - if just_authenticated: - raise - self.cached_token = None - client.cached_endpoint = None - if self.auth_plugin.opts.get('token'): - self.auth_plugin.opts['token'] = None - if self.auth_plugin.opts.get('endpoint'): - self.auth_plugin.opts['endpoint'] = None - self.authenticate() - try: - token, endpoint = self.auth_plugin.token_and_endpoint( - **filter_args) - except exceptions.EndpointException: - raise unauth_ex - if (not (token and endpoint) or - old_token_endpoint == (token, endpoint)): - raise unauth_ex - self.cached_token = token - client.cached_endpoint = endpoint - kwargs["headers"]["X-Auth-Token"] = token - return self.request( - method, self.concat_url(endpoint, url), **kwargs) - - def add_client(self, base_client_instance): - """Add a new instance of :class:`BaseClient` descendant. - - `self` will store a reference to `base_client_instance`. - - Example: - - >>> def test_clients(): - ... from keystoneclient.auth import keystone - ... from openstack.common.apiclient import client - ... auth = keystone.KeystoneAuthPlugin( - ... username="user", password="pass", tenant_name="tenant", - ... auth_url="http://auth:5000/v2.0") - ... openstack_client = client.HTTPClient(auth) - ... # create nova client - ... from novaclient.v1_1 import client - ... client.Client(openstack_client) - ... # create keystone client - ... from keystoneclient.v2_0 import client - ... client.Client(openstack_client) - ... # use them - ... openstack_client.identity.tenants.list() - ... openstack_client.compute.servers.list() - """ - service_type = base_client_instance.service_type - if service_type and not hasattr(self, service_type): - setattr(self, service_type, base_client_instance) - - def authenticate(self): - self.auth_plugin.authenticate(self) - # Store the authentication results in the keyring for later requests - if self.keyring_saver: - self.keyring_saver.save(self) - - -class BaseClient(object): - """Top-level object to access the OpenStack API. - - This client uses :class:`HTTPClient` to send requests. :class:`HTTPClient` - will handle a bunch of issues such as authentication. - """ - - service_type = None - endpoint_type = None # "publicURL" will be used - cached_endpoint = None - - def __init__(self, http_client, extensions=None): - self.http_client = http_client - http_client.add_client(self) - - # Add in any extensions... - if extensions: - for extension in extensions: - if extension.manager_class: - setattr(self, extension.name, - extension.manager_class(self)) - - def client_request(self, method, url, **kwargs): - return self.http_client.client_request( - self, method, url, **kwargs) - - @property - def last_request_id(self): - return self.http_client.last_request_id - - def head(self, url, **kwargs): - return self.client_request("HEAD", url, **kwargs) - - def get(self, url, **kwargs): - return self.client_request("GET", url, **kwargs) - - def post(self, url, **kwargs): - return self.client_request("POST", url, **kwargs) - - def put(self, url, **kwargs): - return self.client_request("PUT", url, **kwargs) - - def delete(self, url, **kwargs): - return self.client_request("DELETE", url, **kwargs) - - def patch(self, url, **kwargs): - return self.client_request("PATCH", url, **kwargs) - - @staticmethod - def get_class(api_name, version, version_map): - """Returns the client class for the requested API version - - :param api_name: the name of the API, e.g. 'compute', 'image', etc - :param version: the requested API version - :param version_map: a dict of client classes keyed by version - :rtype: a client class for the requested API version - """ - try: - client_path = version_map[str(version)] - except (KeyError, ValueError): - msg = _("Invalid %(api_name)s client version '%(version)s'. " - "Must be one of: %(version_map)s") % { - 'api_name': api_name, - 'version': version, - 'version_map': ', '.join(version_map.keys())} - raise exceptions.UnsupportedVersion(msg) - - return importutils.import_class(client_path) diff --git a/ceilometerclient/apiclient/exceptions.py b/ceilometerclient/apiclient/exceptions.py deleted file mode 100644 index 68b98ca..0000000 --- a/ceilometerclient/apiclient/exceptions.py +++ /dev/null @@ -1,477 +0,0 @@ -# Copyright 2010 Jacob Kaplan-Moss -# Copyright 2011 Nebula, Inc. -# Copyright 2013 Alessio Ababilov -# Copyright 2013 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. - -""" -Exception definitions. -""" - -######################################################################## -# -# THIS MODULE IS DEPRECATED -# -# Please refer to -# https://etherpad.openstack.org/p/kilo-ceilometerclient-library-proposals for -# the discussion leading to this deprecation. -# -# We recommend checking out the python-openstacksdk project -# (https://launchpad.net/python-openstacksdk) instead. -# -######################################################################## - -import inspect -import sys - -import six - -from ceilometerclient.i18n import _ - - -class ClientException(Exception): - """The base exception class for all exceptions this library raises.""" - pass - - -class ValidationError(ClientException): - """Error in validation on API client side.""" - pass - - -class UnsupportedVersion(ClientException): - """User is trying to use an unsupported version of the API.""" - pass - - -class CommandError(ClientException): - """Error in CLI tool.""" - pass - - -class AuthorizationFailure(ClientException): - """Cannot authorize API client.""" - pass - - -class ConnectionError(ClientException): - """Cannot connect to API service.""" - pass - - -class ConnectionRefused(ConnectionError): - """Connection refused while trying to connect to API service.""" - pass - - -class AuthPluginOptionsMissing(AuthorizationFailure): - """Auth plugin misses some options.""" - def __init__(self, opt_names): - super(AuthPluginOptionsMissing, self).__init__( - _("Authentication failed. Missing options: %s") % - ", ".join(opt_names)) - self.opt_names = opt_names - - -class AuthSystemNotFound(AuthorizationFailure): - """User has specified an AuthSystem that is not installed.""" - def __init__(self, auth_system): - super(AuthSystemNotFound, self).__init__( - _("AuthSystemNotFound: %s") % repr(auth_system)) - self.auth_system = auth_system - - -class NoUniqueMatch(ClientException): - """Multiple entities found instead of one.""" - pass - - -class EndpointException(ClientException): - """Something is rotten in Service Catalog.""" - pass - - -class EndpointNotFound(EndpointException): - """Could not find requested endpoint in Service Catalog.""" - pass - - -class AmbiguousEndpoints(EndpointException): - """Found more than one matching endpoint in Service Catalog.""" - def __init__(self, endpoints=None): - super(AmbiguousEndpoints, self).__init__( - _("AmbiguousEndpoints: %s") % repr(endpoints)) - self.endpoints = endpoints - - -class HttpError(ClientException): - """The base exception class for all HTTP exceptions.""" - http_status = 0 - message = _("HTTP Error") - - def __init__(self, message=None, details=None, - response=None, request_id=None, - url=None, method=None, http_status=None): - self.http_status = http_status or self.http_status - self.message = message or self.message - self.details = details - self.request_id = request_id - self.response = response - self.url = url - self.method = method - formatted_string = "%s (HTTP %s)" % (self.message, self.http_status) - if request_id: - formatted_string += " (Request-ID: %s)" % request_id - super(HttpError, self).__init__(formatted_string) - - -class HTTPRedirection(HttpError): - """HTTP Redirection.""" - message = _("HTTP Redirection") - - -class HTTPClientError(HttpError): - """Client-side HTTP error. - - Exception for cases in which the client seems to have erred. - """ - message = _("HTTP Client Error") - - -class HttpServerError(HttpError): - """Server-side HTTP error. - - Exception for cases in which the server is aware that it has - erred or is incapable of performing the request. - """ - message = _("HTTP Server Error") - - -class MultipleChoices(HTTPRedirection): - """HTTP 300 - Multiple Choices. - - Indicates multiple options for the resource that the client may follow. - """ - - http_status = 300 - message = _("Multiple Choices") - - -class BadRequest(HTTPClientError): - """HTTP 400 - Bad Request. - - The request cannot be fulfilled due to bad syntax. - """ - http_status = 400 - message = _("Bad Request") - - -class Unauthorized(HTTPClientError): - """HTTP 401 - Unauthorized. - - Similar to 403 Forbidden, but specifically for use when authentication - is required and has failed or has not yet been provided. - """ - http_status = 401 - message = _("Unauthorized") - - -class PaymentRequired(HTTPClientError): - """HTTP 402 - Payment Required. - - Reserved for future use. - """ - http_status = 402 - message = _("Payment Required") - - -class Forbidden(HTTPClientError): - """HTTP 403 - Forbidden. - - The request was a valid request, but the server is refusing to respond - to it. - """ - http_status = 403 - message = _("Forbidden") - - -class NotFound(HTTPClientError): - """HTTP 404 - Not Found. - - The requested resource could not be found but may be available again - in the future. - """ - http_status = 404 - message = _("Not Found") - - -class MethodNotAllowed(HTTPClientError): - """HTTP 405 - Method Not Allowed. - - A request was made of a resource using a request method not supported - by that resource. - """ - http_status = 405 - message = _("Method Not Allowed") - - -class NotAcceptable(HTTPClientError): - """HTTP 406 - Not Acceptable. - - The requested resource is only capable of generating content not - acceptable according to the Accept headers sent in the request. - """ - http_status = 406 - message = _("Not Acceptable") - - -class ProxyAuthenticationRequired(HTTPClientError): - """HTTP 407 - Proxy Authentication Required. - - The client must first authenticate itself with the proxy. - """ - http_status = 407 - message = _("Proxy Authentication Required") - - -class RequestTimeout(HTTPClientError): - """HTTP 408 - Request Timeout. - - The server timed out waiting for the request. - """ - http_status = 408 - message = _("Request Timeout") - - -class Conflict(HTTPClientError): - """HTTP 409 - Conflict. - - Indicates that the request could not be processed because of conflict - in the request, such as an edit conflict. - """ - http_status = 409 - message = _("Conflict") - - -class Gone(HTTPClientError): - """HTTP 410 - Gone. - - Indicates that the resource requested is no longer available and will - not be available again. - """ - http_status = 410 - message = _("Gone") - - -class LengthRequired(HTTPClientError): - """HTTP 411 - Length Required. - - The request did not specify the length of its content, which is - required by the requested resource. - """ - http_status = 411 - message = _("Length Required") - - -class PreconditionFailed(HTTPClientError): - """HTTP 412 - Precondition Failed. - - The server does not meet one of the preconditions that the requester - put on the request. - """ - http_status = 412 - message = _("Precondition Failed") - - -class RequestEntityTooLarge(HTTPClientError): - """HTTP 413 - Request Entity Too Large. - - The request is larger than the server is willing or able to process. - """ - http_status = 413 - message = _("Request Entity Too Large") - - def __init__(self, *args, **kwargs): - try: - self.retry_after = int(kwargs.pop('retry_after')) - except (KeyError, ValueError): - self.retry_after = 0 - - super(RequestEntityTooLarge, self).__init__(*args, **kwargs) - - -class RequestUriTooLong(HTTPClientError): - """HTTP 414 - Request-URI Too Long. - - The URI provided was too long for the server to process. - """ - http_status = 414 - message = _("Request-URI Too Long") - - -class UnsupportedMediaType(HTTPClientError): - """HTTP 415 - Unsupported Media Type. - - The request entity has a media type which the server or resource does - not support. - """ - http_status = 415 - message = _("Unsupported Media Type") - - -class RequestedRangeNotSatisfiable(HTTPClientError): - """HTTP 416 - Requested Range Not Satisfiable. - - The client has asked for a portion of the file, but the server cannot - supply that portion. - """ - http_status = 416 - message = _("Requested Range Not Satisfiable") - - -class ExpectationFailed(HTTPClientError): - """HTTP 417 - Expectation Failed. - - The server cannot meet the requirements of the Expect request-header field. - """ - http_status = 417 - message = _("Expectation Failed") - - -class UnprocessableEntity(HTTPClientError): - """HTTP 422 - Unprocessable Entity. - - The request was well-formed but was unable to be followed due to semantic - errors. - """ - http_status = 422 - message = _("Unprocessable Entity") - - -class InternalServerError(HttpServerError): - """HTTP 500 - Internal Server Error. - - A generic error message, given when no more specific message is suitable. - """ - http_status = 500 - message = _("Internal Server Error") - - -# NotImplemented is a python keyword. -class HttpNotImplemented(HttpServerError): - """HTTP 501 - Not Implemented. - - The server either does not recognize the request method, or it lacks - the ability to fulfill the request. - """ - http_status = 501 - message = _("Not Implemented") - - -class BadGateway(HttpServerError): - """HTTP 502 - Bad Gateway. - - The server was acting as a gateway or proxy and received an invalid - response from the upstream server. - """ - http_status = 502 - message = _("Bad Gateway") - - -class ServiceUnavailable(HttpServerError): - """HTTP 503 - Service Unavailable. - - The server is currently unavailable. - """ - http_status = 503 - message = _("Service Unavailable") - - -class GatewayTimeout(HttpServerError): - """HTTP 504 - Gateway Timeout. - - The server was acting as a gateway or proxy and did not receive a timely - response from the upstream server. - """ - http_status = 504 - message = _("Gateway Timeout") - - -class HttpVersionNotSupported(HttpServerError): - """HTTP 505 - HttpVersion Not Supported. - - The server does not support the HTTP protocol version used in the request. - """ - http_status = 505 - message = _("HTTP Version Not Supported") - - -# _code_map contains all the classes that have http_status attribute. -_code_map = dict( - (getattr(obj, 'http_status', None), obj) - for name, obj in six.iteritems(vars(sys.modules[__name__])) - if inspect.isclass(obj) and getattr(obj, 'http_status', False) -) - - -def from_response(response, method, url): - """Returns an instance of :class:`HttpError` or subclass based on response. - - :param response: instance of `requests.Response` class - :param method: HTTP method used for request - :param url: URL used for request - """ - - req_id = response.headers.get("x-openstack-request-id") - # NOTE(hdd) true for older versions of nova and cinder - if not req_id: - req_id = response.headers.get("x-compute-request-id") - kwargs = { - "http_status": response.status_code, - "response": response, - "method": method, - "url": url, - "request_id": req_id, - } - if "retry-after" in response.headers: - kwargs["retry_after"] = response.headers["retry-after"] - - content_type = response.headers.get("Content-Type", "") - if content_type.startswith("application/json"): - try: - body = response.json() - except ValueError: - pass - else: - if isinstance(body, dict): - error = body.get(list(body)[0]) - if isinstance(error, dict): - kwargs["message"] = (error.get("message") or - error.get("faultstring")) - kwargs["details"] = (error.get("details") or - six.text_type(body)) - elif content_type.startswith("text/"): - kwargs["details"] = response.text - - try: - cls = _code_map[response.status_code] - except KeyError: - if 500 <= response.status_code < 600: - cls = HttpServerError - elif 400 <= response.status_code < 500: - cls = HTTPClientError - else: - cls = HttpError - return cls(**kwargs) diff --git a/ceilometerclient/apiclient/fake_client.py b/ceilometerclient/apiclient/fake_client.py deleted file mode 100644 index d897cc5..0000000 --- a/ceilometerclient/apiclient/fake_client.py +++ /dev/null @@ -1,190 +0,0 @@ -# Copyright 2013 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. - -""" -A fake server that "responds" to API methods with pre-canned responses. - -All of these responses come from the spec, so if for some reason the spec's -wrong the tests might raise AssertionError. I've indicated in comments the -places where actual behavior differs from the spec. -""" - -######################################################################## -# -# THIS MODULE IS DEPRECATED -# -# Please refer to -# https://etherpad.openstack.org/p/kilo-ceilometerclient-library-proposals for -# the discussion leading to this deprecation. -# -# We recommend checking out the python-openstacksdk project -# (https://launchpad.net/python-openstacksdk) instead. -# -######################################################################## - -# W0102: Dangerous default value %s as argument -# pylint: disable=W0102 - -import json - -import requests -import six -from six.moves.urllib import parse - -from ceilometerclient.apiclient import client - - -def assert_has_keys(dct, required=None, optional=None): - required = required or [] - optional = optional or [] - for k in required: - try: - assert k in dct - except AssertionError: - extra_keys = set(dct.keys()).difference(set(required + optional)) - raise AssertionError("found unexpected keys: %s" % - list(extra_keys)) - - -class TestResponse(requests.Response): - """Wrap requests.Response and provide a convenient initialization.""" - - def __init__(self, data): - super(TestResponse, self).__init__() - self._content_consumed = True - if isinstance(data, dict): - self.status_code = data.get('status_code', 200) - # Fake the text attribute to streamline Response creation - text = data.get('text', "") - if isinstance(text, (dict, list)): - self._content = json.dumps(text) - default_headers = { - "Content-Type": "application/json", - } - else: - self._content = text - default_headers = {} - if six.PY3 and isinstance(self._content, six.string_types): - self._content = self._content.encode('utf-8', 'strict') - self.headers = data.get('headers') or default_headers - else: - self.status_code = data - - def __eq__(self, other): - return (self.status_code == other.status_code and - self.headers == other.headers and - self._content == other._content) - - def __ne__(self, other): - return not self.__eq__(other) - - -class FakeHTTPClient(client.HTTPClient): - - def __init__(self, *args, **kwargs): - self.callstack = [] - self.fixtures = kwargs.pop("fixtures", None) or {} - if not args and "auth_plugin" not in kwargs: - args = (None, ) - super(FakeHTTPClient, self).__init__(*args, **kwargs) - - def assert_called(self, method, url, body=None, pos=-1): - """Assert than an API method was just called.""" - expected = (method, url) - called = self.callstack[pos][0:2] - assert self.callstack, \ - "Expected %s %s but no calls were made." % expected - - assert expected == called, 'Expected %s %s; got %s %s' % \ - (expected + called) - - if body is not None: - if self.callstack[pos][3] != body: - raise AssertionError('%r != %r' % - (self.callstack[pos][3], body)) - - def assert_called_anytime(self, method, url, body=None): - """Assert than an API method was called anytime in the test.""" - expected = (method, url) - - assert self.callstack, \ - "Expected %s %s but no calls were made." % expected - - found = False - entry = None - for entry in self.callstack: - if expected == entry[0:2]: - found = True - break - - assert found, 'Expected %s %s; got %s' % \ - (method, url, self.callstack) - if body is not None: - assert entry[3] == body, "%s != %s" % (entry[3], body) - - self.callstack = [] - - def clear_callstack(self): - self.callstack = [] - - def authenticate(self): - pass - - def client_request(self, client, method, url, **kwargs): - # Check that certain things are called correctly - if method in ["GET", "DELETE"]: - assert "json" not in kwargs - - # Note the call - self.callstack.append( - (method, - url, - kwargs.get("headers") or {}, - kwargs.get("json") or kwargs.get("data"))) - try: - fixture = self.fixtures[url][method] - except KeyError: - pass - else: - return TestResponse({"headers": fixture[0], - "text": fixture[1]}) - - # Call the method - args = parse.parse_qsl(parse.urlparse(url)[4]) - kwargs.update(args) - munged_url = url.rsplit('?', 1)[0] - munged_url = munged_url.strip('/').replace('/', '_').replace('.', '_') - munged_url = munged_url.replace('-', '_') - - callback = "%s_%s" % (method.lower(), munged_url) - - if not hasattr(self, callback): - raise AssertionError('Called unknown API method: %s %s, ' - 'expected fakes method name: %s' % - (method, url, callback)) - - resp = getattr(self, callback)(**kwargs) - if len(resp) == 3: - status, headers, body = resp - else: - status, body = resp - headers = {} - self.last_request_id = headers.get('x-openstack-request-id', - 'req-test') - return TestResponse({ - "status_code": status, - "text": body, - "headers": headers, - }) diff --git a/ceilometerclient/apiclient/utils.py b/ceilometerclient/apiclient/utils.py deleted file mode 100644 index 2f45b92..0000000 --- a/ceilometerclient/apiclient/utils.py +++ /dev/null @@ -1,100 +0,0 @@ -# -# 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. - -######################################################################## -# -# THIS MODULE IS DEPRECATED -# -# Please refer to -# https://etherpad.openstack.org/p/kilo-ceilometerclient-library-proposals for -# the discussion leading to this deprecation. -# -# We recommend checking out the python-openstacksdk project -# (https://launchpad.net/python-openstacksdk) instead. -# -######################################################################## - -from oslo_utils import encodeutils -from oslo_utils import uuidutils -import six - -from ceilometerclient.apiclient import exceptions -from ceilometerclient.i18n import _ - - -def find_resource(manager, name_or_id, **find_args): - """Look for resource in a given manager. - - Used as a helper for the _find_* methods. - Example: - - .. code-block:: python - - def _find_hypervisor(cs, hypervisor): - #Get a hypervisor by name or ID. - return cliutils.find_resource(cs.hypervisors, hypervisor) - """ - # first try to get entity as integer id - try: - return manager.get(int(name_or_id)) - except (TypeError, ValueError, exceptions.NotFound): - pass - - # now try to get entity as uuid - try: - if six.PY2: - tmp_id = encodeutils.safe_encode(name_or_id) - else: - tmp_id = encodeutils.safe_decode(name_or_id) - - if uuidutils.is_uuid_like(tmp_id): - return manager.get(tmp_id) - except (TypeError, ValueError, exceptions.NotFound): - pass - - # for str id which is not uuid - if getattr(manager, 'is_alphanum_id_allowed', False): - try: - return manager.get(name_or_id) - except exceptions.NotFound: - pass - - try: - try: - return manager.find(human_id=name_or_id, **find_args) - except exceptions.NotFound: - pass - - # finally try to find entity by name - try: - resource = getattr(manager, 'resource_class', None) - name_attr = resource.NAME_ATTR if resource else 'name' - kwargs = {name_attr: name_or_id} - kwargs.update(find_args) - return manager.find(**kwargs) - except exceptions.NotFound: - msg = _("No %(name)s with a name or " - "ID of '%(name_or_id)s' exists.") % \ - { - "name": manager.resource_class.__name__.lower(), - "name_or_id": name_or_id - } - raise exceptions.CommandError(msg) - except exceptions.NoUniqueMatch: - msg = _("Multiple %(name)s matches found for " - "'%(name_or_id)s', use an ID to be more specific.") % \ - { - "name": manager.resource_class.__name__.lower(), - "name_or_id": name_or_id - } - raise exceptions.CommandError(msg) diff --git a/ceilometerclient/client.py b/ceilometerclient/client.py deleted file mode 100644 index 2c61f34..0000000 --- a/ceilometerclient/client.py +++ /dev/null @@ -1,480 +0,0 @@ -# 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 contextlib -import time - -from keystoneauth1 import adapter -from keystoneauth1 import discover -from keystoneauth1 import exceptions as ka_exc -from keystoneauth1.identity import v2 as v2_auth -from keystoneauth1.identity import v3 as v3_auth -from keystoneauth1 import session -from oslo_utils import importutils -from oslo_utils import strutils -import six.moves.urllib.parse as urlparse - -from ceilometerclient.apiclient import auth -from ceilometerclient.apiclient import client -from ceilometerclient.apiclient import exceptions -from ceilometerclient import exc - - -def _discover_auth_versions(session, auth_url): - # discover the API versions the server is supporting based on the - # given URL - v2_auth_url = None - v3_auth_url = None - try: - ks_discover = discover.Discover(session=session, url=auth_url) - v2_auth_url = ks_discover.url_for('2.0') - v3_auth_url = ks_discover.url_for('3.0') - except ka_exc.DiscoveryFailure: - raise - except exceptions.ClientException: - # Identity service may not support discovery. In that case, - # try to determine version from auth_url - url_parts = urlparse.urlparse(auth_url) - (scheme, netloc, path, params, query, fragment) = url_parts - path = path.lower() - if path.startswith('/v3'): - v3_auth_url = auth_url - elif path.startswith('/v2'): - v2_auth_url = auth_url - else: - raise exc.CommandError('Unable to determine the Keystone ' - 'version to authenticate with ' - 'using the given auth_url.') - return v2_auth_url, v3_auth_url - - -def _get_keystone_session(**kwargs): - # TODO(fabgia): the heavy lifting here should be really done by Keystone. - # Unfortunately Keystone does not support a richer method to perform - # discovery and return a single viable URL. A bug against Keystone has - # been filed: https://bugs.launchpad.net/python-keystoneclient/+bug/1330677 - - # first create a Keystone session - cacert = kwargs.pop('cacert', None) - cert = kwargs.pop('cert', None) - key = kwargs.pop('key', None) - insecure = kwargs.pop('insecure', False) - auth_url = kwargs.pop('auth_url', None) - project_id = kwargs.pop('project_id', None) - project_name = kwargs.pop('project_name', None) - token = kwargs['token'] - timeout = kwargs.get('timeout') - - if insecure: - verify = False - else: - verify = cacert or True - - if cert and key: - # passing cert and key together is deprecated in favour of the - # requests lib form of having the cert and key as a tuple - cert = (cert, key) - - # create the keystone client session - ks_session = session.Session(verify=verify, cert=cert, timeout=timeout) - v2_auth_url, v3_auth_url = _discover_auth_versions(ks_session, auth_url) - username = kwargs.pop('username', None) - user_id = kwargs.pop('user_id', None) - user_domain_name = kwargs.pop('user_domain_name', None) - user_domain_id = kwargs.pop('user_domain_id', None) - project_domain_name = kwargs.pop('project_domain_name', None) - project_domain_id = kwargs.pop('project_domain_id', None) - if v3_auth_url: - if not user_domain_name: - user_domain_name = 'Default' - if not project_domain_name: - project_domain_name = 'Default' - auth = None - - use_domain = (user_domain_id or user_domain_name or - project_domain_id or project_domain_name) - use_v3 = v3_auth_url and (use_domain or (not v2_auth_url)) - use_v2 = v2_auth_url and not use_domain - - if use_v3 and token: - auth = v3_auth.Token( - v3_auth_url, - token=token, - project_name=project_name, - project_id=project_id, - project_domain_name=project_domain_name, - project_domain_id=project_domain_id) - elif use_v2 and token: - auth = v2_auth.Token( - v2_auth_url, - token=token, - tenant_id=project_id, - tenant_name=project_name) - elif use_v3: - # the auth_url as v3 specified - # e.g. http://no.where:5000/v3 - # Keystone will return only v3 as viable option - auth = v3_auth.Password( - v3_auth_url, - username=username, - password=kwargs.pop('password', None), - user_id=user_id, - user_domain_name=user_domain_name, - user_domain_id=user_domain_id, - project_name=project_name, - project_id=project_id, - project_domain_name=project_domain_name, - project_domain_id=project_domain_id) - elif use_v2: - # the auth_url as v2 specified - # e.g. http://no.where:5000/v2.0 - # Keystone will return only v2 as viable option - auth = v2_auth.Password( - v2_auth_url, - username, - kwargs.pop('password', None), - tenant_id=project_id, - tenant_name=project_name) - - else: - raise exc.CommandError('Unable to determine the Keystone version ' - 'to authenticate with using the given ' - 'auth_url.') - - ks_session.auth = auth - return ks_session - - -def _get_endpoint(ks_session, **kwargs): - """Get an endpoint using the provided keystone session.""" - - # set service specific endpoint types - endpoint_type = kwargs.get('endpoint_type') or 'publicURL' - service_type = kwargs.get('service_type') or 'metering' - - endpoint = ks_session.get_endpoint(service_type=service_type, - interface=endpoint_type, - region_name=kwargs.get('region_name')) - - return endpoint - - -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', 'project_domain_id', 'project_domain_name', - 'user_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) - # NOTE(sileht): backward compat - if self.opts.get('auth_token') and not self.opts.get('token'): - self.opts['token'] = self.opts.get('auth_token') - - def _do_authenticate(self, http_client): - token = self.opts.get('token') - endpoint = self.opts.get('endpoint') - if not (endpoint and token): - ks_kwargs = self._get_ks_kwargs(http_timeout=http_client.timeout) - ks_session = _get_keystone_session(**ks_kwargs) - if not token: - token = lambda: ks_session.get_token() - if not endpoint: - endpoint = _get_endpoint(ks_session, **ks_kwargs) - self.opts['token'] = token - self.opts['endpoint'] = endpoint - - def _get_ks_kwargs(self, http_timeout): - 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')) - token = self.opts.get('token') - 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': strutils.bool_from_string( - self.opts.get('insecure')), - 'endpoint_type': self.opts.get('endpoint_type'), - 'service_type': self.opts.get('service_type'), - 'region_name': self.opts.get('region_name'), - 'timeout': http_timeout, - 'token': token() if callable(token) else token, - } - return ks_kwargs - - 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 - """ - has_token = self.opts.get('token') - has_project_domain_or_tenant = (self.opts.get('project_id') or - (self.opts.get('project_name') and - (self.opts.get('user_domain_name') or - self.opts.get('user_domain_id'))) or - (self.opts.get('tenant_id') or - self.opts.get('tenant_name'))) - has_credential = (self.opts.get('username') - and has_project_domain_or_tenant - and self.opts.get('password') - and self.opts.get('auth_url')) - missing = not (has_token or has_credential) - 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 _adjust_kwargs(kwargs): - client_kwargs = { - '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'), - 'insecure': kwargs.get('os_insecure'), - 'cacert': kwargs.get('os_cacert'), - 'cert_file': kwargs.get('os_cert'), - 'key_file': kwargs.get('os_key'), - 'token': kwargs.get('os_token') or kwargs.get('os_auth_token'), - 'user_domain_name': kwargs.get('os_user_domain_name'), - 'user_domain_id': kwargs.get('os_user_domain_id'), - 'project_domain_name': kwargs.get('os_project_domain_name'), - 'project_domain_id': kwargs.get('os_project_domain_id'), - } - - client_kwargs.update(kwargs) - client_kwargs['token'] = (client_kwargs.get('token') or - kwargs.get('token') or - kwargs.get('auth_token')) - - timeout = kwargs.get('timeout') - if timeout is not None: - timeout = int(timeout) - if timeout <= 0: - timeout = None - - insecure = strutils.bool_from_string(client_kwargs.get('insecure')) - verify = kwargs.get('verify') - if verify is None: - if insecure: - verify = False - else: - verify = client_kwargs.get('cacert') or True - - cert = client_kwargs.get('cert_file') - key = client_kwargs.get('key_file') - if cert and key: - cert = cert, key - - client_kwargs.update({'verify': verify, 'cert': cert, 'timeout': timeout}) - return client_kwargs - - -def Client(version, *args, **kwargs): - client_kwargs = _adjust_kwargs(kwargs) - - module = importutils.import_versioned_module('ceilometerclient', - version, 'client') - client_class = getattr(module, 'Client') - return client_class(*args, **client_kwargs) - - -def get_client(version, **kwargs): - """Get an authenticated client, based on the credentials in the kwargs. - - :param version: the API version to use ('1' or '2') - :param kwargs: keyword args containing credentials, either: - - * session: a keystoneauth session object - * service_type: The default service_type for URL discovery - * service_name: The default service_name for URL discovery - * interface: The default interface for URL discovery - (Default: public) - * region_name: The default region_name for URL discovery - * endpoint_override: Always use this endpoint URL for requests - for this ceiloclient - * auth: An auth plugin to use instead of the session one - * user_agent: The User-Agent string to set - (Default is python-ceilometer-client) - * connect_retries: the maximum number of retries that should be - attempted for connection errors - * logger: A logging object - - or (DEPRECATED): - - * os_auth_token: (DEPRECATED) pre-existing token to re-use, - use os_token instead - * os_token: pre-existing token to re-use - * ceilometer_url: (DEPRECATED) Ceilometer API endpoint, - use os_endpoint instead - * os_endpoint: Ceilometer API endpoint - - or (DEPRECATED): - - * os_username: name of user - * os_password: user's password - * os_user_id: user's id - * os_user_domain_id: the domain id of the user - * os_user_domain_name: the domain name of the user - * os_project_id: the user project id - * os_tenant_id: V2 alternative to os_project_id - * os_project_name: the user project name - * os_tenant_name: V2 alternative to os_project_name - * os_project_domain_name: domain name for the user project - * os_project_domain_id: domain id for the user project - * os_auth_url: endpoint to authenticate against - * os_cert|os_cacert: path of CA TLS certificate - * os_key: SSL private key - * os_insecure: allow insecure SSL (no cert verification) - """ - endpoint = kwargs.get('os_endpoint') or kwargs.get('ceilometer_url') - - return Client(version, endpoint, **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'), - insecure=kwargs.get('insecure'), - region_name=kwargs.get('region_name'), - cacert=kwargs.get('cacert'), - 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('project_name') or kwargs.get('tenant_name'), - user_domain_name=kwargs.get('user_domain_name'), - user_domain_id=kwargs.get('user_domain_id'), - project_domain_name=kwargs.get('project_domain_name'), - project_domain_id=kwargs.get('project_domain_id') - ) - return auth_plugin - - -LEGACY_OPTS = ('auth_plugin', 'auth_url', 'token', 'insecure', 'cacert', - 'tenant_id', 'project_id', 'username', 'password', - 'project_name', 'tenant_name', - 'user_domain_name', 'user_domain_id', - 'project_domain_name', 'project_domain_id', - 'key_file', 'cert_file', 'verify', 'timeout', 'cert') - - -def _construct_http_client(**kwargs): - kwargs = kwargs.copy() - if kwargs.get('session') is not None: - # Drop legacy options - for opt in LEGACY_OPTS: - kwargs.pop(opt, None) - # Drop redirect endpoints from kwargs - kwargs.pop('aodh_endpoint', None) - kwargs.pop('panko_endpoint', None) - - return SessionClient( - session=kwargs.pop('session'), - service_type=kwargs.pop('service_type', 'metering') or 'metering', - interface=kwargs.pop('interface', kwargs.pop('endpoint_type', - 'publicURL')), - region_name=kwargs.pop('region_name', None), - user_agent=kwargs.pop('user_agent', 'python-ceilometerclient'), - auth=kwargs.get('auth'), - timings=kwargs.pop('timings', None), - **kwargs) - else: - return client.BaseClient(client.HTTPClient( - auth_plugin=kwargs.get('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('cert'), - 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') - )) - - -@contextlib.contextmanager -def record_time(times, enabled, *args): - """Record the time of a specific action. - - :param times: A list of tuples holds time data. - :type times: list - :param enabled: Whether timing is enabled. - :type enabled: bool - :param args: Other data to be stored besides time data, these args - will be joined to a string. - """ - if not enabled: - yield - else: - start = time.time() - yield - end = time.time() - times.append((' '.join(args), start, end)) - - -class SessionClient(adapter.LegacyJsonAdapter): - def __init__(self, *args, **kwargs): - self.times = [] - self.timings = kwargs.pop('timings', False) - super(SessionClient, self).__init__(*args, **kwargs) - - def request(self, url, method, **kwargs): - kwargs.setdefault('headers', kwargs.get('headers', {})) - # NOTE(sileht): The standard call raises errors from - # keystoneauth, where we need to raise the ceilometerclient errors. - raise_exc = kwargs.pop('raise_exc', True) - with record_time(self.times, self.timings, method, url): - resp, body = super(SessionClient, self).request(url, - method, - raise_exc=False, - **kwargs) - - if raise_exc and resp.status_code >= 400: - raise exc.from_response(resp, body) - return resp diff --git a/ceilometerclient/common/__init__.py b/ceilometerclient/common/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/ceilometerclient/common/__init__.py +++ /dev/null diff --git a/ceilometerclient/common/base.py b/ceilometerclient/common/base.py deleted file mode 100644 index 5df8344..0000000 --- a/ceilometerclient/common/base.py +++ /dev/null @@ -1,106 +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. - -""" -Base utilities to build API operation managers and objects on top of. -""" - -import copy - -from ceilometerclient.apiclient import base -from ceilometerclient.apiclient import exceptions -from ceilometerclient import exc - - -def getid(obj): - """Extracts object ID. - - Abstracts the common pattern of allowing both an object or an - object's ID (UUID) as a parameter when dealing with relationships. - """ - try: - return obj.id - except AttributeError: - return obj - - -class Manager(object): - """Managers interact with a particular type of API. - - It works with samples, meters, alarms, etc. and provide CRUD operations for - them. - """ - resource_class = None - - def __init__(self, api): - self.api = api - - @property - def client(self): - """Compatible with latest oslo-incubator.apiclient code.""" - return self.api - - def _create(self, url, 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): - try: - resp = self.api.get(url) - except exceptions.NotFound: - raise exc.HTTPNotFound - if not resp.content: - raise exc.HTTPNotFound - body = resp.json() - - if obj_class is None: - obj_class = self.resource_class - - if response_key: - try: - data = body[response_key] - except KeyError: - return [] - else: - data = body - if expect_single: - data = [data] - return [obj_class(self, res, loaded=True) for res in data if res] - - def _update(self, url, body, response_key=None): - 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.delete(url) - - -class Resource(base.Resource): - """A resource represents a particular instance of an object. - - Resource might be tenant, user, etc. - This is pretty much just a bag for attributes. - - :param manager: Manager object - :param info: dictionary representing resource attributes - :param loaded: prevent lazy-loading if set to True - """ - - def to_dict(self): - return copy.deepcopy(self._info) diff --git a/ceilometerclient/common/utils.py b/ceilometerclient/common/utils.py deleted file mode 100644 index 720691d..0000000 --- a/ceilometerclient/common/utils.py +++ /dev/null @@ -1,204 +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. - -from __future__ import print_function - -import os -import textwrap - -from oslo_serialization import jsonutils -from oslo_utils import encodeutils -import prettytable -import six - -from ceilometerclient import exc - - -# Decorator for cli-args -def arg(*args, **kwargs): - def _decorator(func): - if 'help' in kwargs: - if 'default' in kwargs: - kwargs['help'] += " Defaults to %s." % kwargs['default'] - required = kwargs.get('required', False) - if required: - kwargs['help'] += " Required." - - # Because of the sematics of decorator composition if we just append - # to the options list positional options will appear to be backwards. - func.__dict__.setdefault('arguments', []).insert(0, (args, kwargs)) - return func - return _decorator - - -def print_list(objs, fields, field_labels, formatters=None, sortby=0): - """Print a list of objects as a table, one row per object. - - :param objs: Iterable of :class:`Resource` - :param fields: Attributes that correspond to columns, in order - :param field_labels: Labels to use in the heading of the table, default to - fields. - :param formatters: `dict` of callables for field formatting - :param sortby: Index of the field for sorting table rows - """ - formatters = formatters or {} - - if len(field_labels) != len(fields): - raise ValueError(("Field labels list %(labels)s has different number " - "of elements than fields list %(fields)s"), - {'labels': field_labels, 'fields': fields}) - - def _make_default_formatter(field): - return lambda o: getattr(o, field, '') - - new_formatters = {} - for field, field_label in six.moves.zip(fields, field_labels): - if field in formatters: - new_formatters[field_label] = formatters[field] - else: - new_formatters[field_label] = _make_default_formatter(field) - - kwargs = {} if sortby is None else {'sortby': field_labels[sortby]} - pt = prettytable.PrettyTable(field_labels) - pt.align = 'l' - - for o in objs: - row = [] - for field in field_labels: - if field in new_formatters: - row.append(new_formatters[field](o)) - else: - field_name = field.lower().replace(' ', '_') - data = getattr(o, field_name, '') - row.append(data) - pt.add_row(row) - - if six.PY3: - print(encodeutils.safe_encode(pt.get_string(**kwargs)).decode()) - else: - print(encodeutils.safe_encode(pt.get_string(**kwargs))) - - -def nested_list_of_dict_formatter(field, column_names): - # (TMaddox) Because the formatting scheme actually drops the whole object - # into the formatter, rather than just the specified field, we have to - # extract it and then pass the value. - return lambda o: format_nested_list_of_dict(getattr(o, field), - column_names) - - -def format_nested_list_of_dict(l, column_names): - pt = prettytable.PrettyTable(caching=False, print_empty=False, - header=True, hrules=prettytable.FRAME, - field_names=column_names) - # Sort by values of first column - if l is not None: - l.sort(key=lambda k: k.get(column_names[0])) - for d in l: - pt.add_row(list(map(lambda k: d[k], column_names))) - return pt.get_string() - - -def print_dict(d, dict_property="Property", wrap=0): - pt = prettytable.PrettyTable([dict_property, 'Value'], print_empty=False) - pt.align = 'l' - for k, v in sorted(six.iteritems(d)): - # convert dict to str to check length - if isinstance(v, (list, dict)): - v = jsonutils.dumps(v) - # if value has a newline, add in multiple rows - # e.g. fault with stacktrace - if v and isinstance(v, six.string_types) and r'\n' in v: - lines = v.strip().split(r'\n') - col1 = k - for line in lines: - if wrap > 0: - line = textwrap.fill(six.text_type(line), wrap) - pt.add_row([col1, line]) - col1 = '' - else: - if wrap > 0: - v = textwrap.fill(six.text_type(v), wrap) - pt.add_row([k, v]) - encoded = encodeutils.safe_encode(pt.get_string()) - # FIXME(gordc): https://bugs.launchpad.net/oslo-incubator/+bug/1370710 - if six.PY3: - encoded = encoded.decode() - print(encoded) - - -def args_array_to_dict(kwargs, key_to_convert): - values_to_convert = kwargs.get(key_to_convert) - if values_to_convert: - try: - kwargs[key_to_convert] = dict(v.split("=", 1) - for v in values_to_convert) - except ValueError: - raise exc.CommandError( - '%s must be a list of key=value not "%s"' % ( - key_to_convert, values_to_convert)) - return kwargs - - -def args_array_to_list_of_dicts(kwargs, key_to_convert): - """Converts ['a=1;b=2','c=3;d=4'] to [{a:1,b:2},{c:3,d:4}].""" - values_to_convert = kwargs.get(key_to_convert) - if values_to_convert: - try: - kwargs[key_to_convert] = [] - for lst in values_to_convert: - pairs = lst.split(";") - dct = dict() - for pair in pairs: - kv = pair.split("=", 1) - dct[kv[0]] = kv[1].strip(" \"'") # strip spaces and quotes - kwargs[key_to_convert].append(dct) - except Exception: - raise exc.CommandError( - '%s must be a list of key1=value1;key2=value2;... not "%s"' % ( - key_to_convert, values_to_convert)) - return kwargs - - -def key_with_slash_to_nested_dict(kwargs): - nested_kwargs = {} - for k in list(kwargs): - keys = k.split('/', 1) - if len(keys) == 2: - nested_kwargs.setdefault(keys[0], {})[keys[1]] = kwargs[k] - del kwargs[k] - kwargs.update(nested_kwargs) - return kwargs - - -def merge_nested_dict(dest, source, depth=0): - for (key, value) in six.iteritems(source): - if isinstance(value, dict) and depth: - merge_nested_dict(dest[key], value, - depth=(depth - 1)) - else: - dest[key] = value - - -def env(*args, **kwargs): - """Returns the first environment variable set. - - If all are empty, defaults to '' or keyword arg `default`. - """ - for arg in args: - value = os.environ.get(arg) - if value: - return value - return kwargs.get('default', '') diff --git a/ceilometerclient/exc.py b/ceilometerclient/exc.py deleted file mode 100644 index 8d56038..0000000 --- a/ceilometerclient/exc.py +++ /dev/null @@ -1,144 +0,0 @@ -# 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 json -import sys - - -class BaseException(Exception): - """An error occurred.""" - def __init__(self, message=None): - self.message = message - - def __str__(self): - return self.message or self.__class__.__doc__ - - -class CommandError(BaseException): - """Invalid usage of CLI.""" - - -class InvalidEndpoint(BaseException): - """The provided endpoint is invalid.""" - - -class CommunicationError(BaseException): - """Unable to communicate with server.""" - - -class HTTPException(BaseException): - """Base exception for all HTTP-derived exceptions.""" - code = 'N/A' - - def __init__(self, details=None): - self.details = details - - def __str__(self): - message = "" - if self.details: - message = self.details - try: - data = json.loads(self.details) - message = data.get("error_message", "") - if isinstance(message, dict) and "faultstring" in message: - message = "ERROR %s" % message["faultstring"] - except (ValueError, TypeError, AttributeError): - pass - - if message: - message = " %s" % message - return "%s (HTTP %s)%s" % (self.__class__.__name__, self.code, - message) - - -class HTTPMultipleChoices(HTTPException): - code = 300 - - def __str__(self): - self.details = ("Requested version of OpenStack Images API is not" - "available.") - return "%s (HTTP %s) %s" % (self.__class__.__name__, self.code, - self.details) - - -class HTTPBadRequest(HTTPException): - code = 400 - - -class HTTPUnauthorized(HTTPException): - code = 401 - - -class HTTPForbidden(HTTPException): - code = 403 - - -class HTTPNotFound(HTTPException): - code = 404 - - -class HTTPMethodNotAllowed(HTTPException): - code = 405 - - -class HTTPConflict(HTTPException): - code = 409 - - -class HTTPOverLimit(HTTPException): - code = 413 - - -class HTTPInternalServerError(HTTPException): - code = 500 - - -class HTTPNotImplemented(HTTPException): - code = 501 - - -class HTTPBadGateway(HTTPException): - code = 502 - - -class HTTPServiceUnavailable(HTTPException): - code = 503 - - -# NOTE(bcwaldon): Build a mapping of HTTP codes to corresponding exception -# classes -_code_map = {} -for obj_name in dir(sys.modules[__name__]): - if obj_name.startswith('HTTP'): - obj = getattr(sys.modules[__name__], obj_name) - _code_map[obj.code] = obj - - -def from_response(response, details=None): - """Return an instance of an HTTPException based on http response.""" - if hasattr(response, "status"): - # it is response from HTTPClient (httplib) - code = response.status - elif hasattr(response, "status_code"): - # it is response from SessionClient (requests) - code = response.status_code - else: - # it is something unexpected - raise TypeError("Function 'from_response' expects only response object" - " from httplib or requests libraries.") - cls = _code_map.get(code) - if cls is None: - exc = HTTPException(details) - exc.code = code - return exc - else: - return cls(details) diff --git a/ceilometerclient/i18n.py b/ceilometerclient/i18n.py deleted file mode 100644 index a567932..0000000 --- a/ceilometerclient/i18n.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2014 IBM Corp. -# -# 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. - -"""oslo.i18n integration module. - -See https://docs.openstack.org/oslo.i18n/latest/user/usage.html. - -""" - -import oslo_i18n - - -_translators = oslo_i18n.TranslatorFactory(domain='ceilometerclient') - -# The primary translation function using the well-known name "_" -_ = _translators.primary diff --git a/ceilometerclient/shell.py b/ceilometerclient/shell.py deleted file mode 100644 index d096f3b..0000000 --- a/ceilometerclient/shell.py +++ /dev/null @@ -1,287 +0,0 @@ -# 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. - -""" -Command-line interface to the OpenStack Telemetry API. -""" - -from __future__ import print_function - -import argparse -import logging -import sys -import warnings - -from oslo_utils import encodeutils -from oslo_utils import importutils -import six - -import ceilometerclient -from ceilometerclient import client as ceiloclient -from ceilometerclient.common import utils -from ceilometerclient import exc - - -def _positive_non_zero_int(argument_value): - if argument_value is None: - return None - try: - value = int(argument_value) - except ValueError: - msg = "%s must be an integer" % argument_value - raise argparse.ArgumentTypeError(msg) - if value <= 0: - msg = "%s must be greater than 0" % argument_value - raise argparse.ArgumentTypeError(msg) - return value - - -class CeilometerShell(object): - - def __init__(self): - self.auth_plugin = ceiloclient.AuthPlugin() - - def get_base_parser(self): - parser = argparse.ArgumentParser( - prog='ceilometer', - description=__doc__.strip(), - epilog='See "ceilometer help COMMAND" ' - 'for help on a specific command.', - add_help=False, - formatter_class=HelpFormatter, - ) - - # Global arguments - parser.add_argument('-h', '--help', - action='store_true', - help=argparse.SUPPRESS, - ) - - parser.add_argument('--version', - action='version', - version=ceilometerclient.__version__) - - parser.add_argument('-d', '--debug', - default=bool(utils.env('CEILOMETERCLIENT_DEBUG') - ), - action='store_true', - help='Defaults to env[CEILOMETERCLIENT_DEBUG].') - - parser.add_argument('-v', '--verbose', - default=False, action="store_true", - help="Print more verbose output.") - - parser.add_argument('--timeout', - default=600, - type=_positive_non_zero_int, - help='Number of seconds to wait for a response.') - - parser.add_argument('--ceilometer-url', metavar='<CEILOMETER_URL>', - dest='os_endpoint', - default=utils.env('CEILOMETER_URL'), - help=("DEPRECATED, use --os-endpoint instead. " - "Defaults to env[CEILOMETER_URL].")) - - parser.add_argument('--ceilometer_url', - dest='os_endpoint', - help=argparse.SUPPRESS) - - parser.add_argument('--ceilometer-api-version', - default=utils.env( - 'CEILOMETER_API_VERSION', default='2'), - help='Defaults to env[CEILOMETER_API_VERSION] ' - 'or 2.') - - parser.add_argument('--ceilometer_api_version', - help=argparse.SUPPRESS) - - self.auth_plugin.add_opts(parser) - self.auth_plugin.add_common_opts(parser) - - return parser - - def get_subcommand_parser(self, version): - parser = self.get_base_parser() - - self.subcommands = {} - subparsers = parser.add_subparsers(metavar='<subcommand>') - submodule = importutils.import_versioned_module('ceilometerclient', - version, 'shell') - self._find_actions(subparsers, submodule) - self._find_actions(subparsers, self) - - return parser - - def _find_actions(self, subparsers, actions_module): - for attr in (a for a in dir(actions_module) if a.startswith('do_')): - # I prefer to be hypen-separated instead of underscores. - command = attr[3:].replace('_', '-') - callback = getattr(actions_module, attr) - desc = callback.__doc__ or '' - help = desc.strip().split('\n')[0] - arguments = getattr(callback, 'arguments', []) - - subparser = subparsers.add_parser(command, help=help, - description=desc, - add_help=False, - formatter_class=HelpFormatter) - subparser.add_argument('-h', '--help', action='help', - help=argparse.SUPPRESS) - self.subcommands[command] = subparser - for (args, kwargs) in arguments: - subparser.add_argument(*args, **kwargs) - subparser.set_defaults(func=callback) - - @staticmethod - def _setup_logging(debug): - 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 - 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 - api_version = options.ceilometer_api_version - subcommand_parser = self.get_subcommand_parser(api_version) - self.parser = subcommand_parser - - # Handle top-level --help/-h before attempting to parse - # a command off the command line - if options.help or not argv: - self.do_help(options) - return 0 - - # Return parsed args - return api_version, subcommand_parser.parse_args(argv) - - def main(self, argv): - warnings.warn( - "ceilometerclient is now deprecated as the Ceilometer API has " - "been deprecated. Please use either aodhclient, pankoclient or " - "gnocchiclient.") - parsed = self.parse_args(argv) - if parsed == 0: - return 0 - api_version, args = parsed - - # Short-circuit and deal with help command right away. - if args.func == self.do_help: - self.do_help(args) - return 0 - elif args.func == self.do_bash_completion: - self.do_bash_completion(args) - return 0 - - if not ((self.auth_plugin.opts.get('token') - or self.auth_plugin.opts.get('auth_token')) - and self.auth_plugin.opts['endpoint']): - 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 self.auth_plugin.opts['password']: - raise exc.CommandError("You must provide a password via " - "either --os-password or via " - "env[OS_PASSWORD]") - - if not (args.os_project_id or args.os_project_name - or args.os_tenant_id or args.os_tenant_name): - # steer users towards Keystone V3 API - raise exc.CommandError("You must provide a project_id " - "(or name) via either --os-project-id " - "or via env[OS_PROJECT_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_kwargs = vars(args) - client_kwargs.update(self.auth_plugin.opts) - client_kwargs['auth_plugin'] = self.auth_plugin - client = ceiloclient.get_client(api_version, **client_kwargs) - # call whatever callback was selected - try: - args.func(client, args) - except exc.HTTPUnauthorized: - raise exc.CommandError("Invalid OpenStack Identity credentials.") - - def do_bash_completion(self, args): - """Prints all of the commands and options to stdout. - - The ceilometer.bash_completion script doesn't have to hard code them. - """ - commands = set() - options = set() - for sc_str, sc in self.subcommands.items(): - commands.add(sc_str) - for option in list(sc._optionals._option_string_actions): - options.add(option) - - commands.remove('bash-completion') - print(' '.join(commands | options)) - - @utils.arg('command', metavar='<subcommand>', nargs='?', - help='Display help for <subcommand>') - def do_help(self, args): - """Display help about this program or one of its subcommands.""" - if getattr(args, 'command', None): - if args.command in self.subcommands: - self.subcommands[args.command].print_help() - else: - raise exc.CommandError("'%s' is not a valid subcommand" % - args.command) - else: - self.parser.print_help() - - -class HelpFormatter(argparse.HelpFormatter): - def __init__(self, prog, indent_increment=2, max_help_position=32, - width=None): - super(HelpFormatter, self).__init__(prog, indent_increment, - max_help_position, width) - - def start_section(self, heading): - # Title-case the headings - heading = '%s%s' % (heading[0].upper(), heading[1:]) - super(HelpFormatter, self).start_section(heading) - - -def main(args=None): - try: - if args is None: - args = sys.argv[1:] - - CeilometerShell().main(args) - - except Exception as e: - if '--debug' in args or '-d' in args: - raise - else: - print(encodeutils.safe_encode(six.text_type(e)), file=sys.stderr) - sys.exit(1) - except KeyboardInterrupt: - print("Stopping Ceilometer Client", file=sys.stderr) - sys.exit(130) - -if __name__ == "__main__": - main() diff --git a/ceilometerclient/tests/__init__.py b/ceilometerclient/tests/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/ceilometerclient/tests/__init__.py +++ /dev/null diff --git a/ceilometerclient/tests/functional/__init__.py b/ceilometerclient/tests/functional/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/ceilometerclient/tests/functional/__init__.py +++ /dev/null diff --git a/ceilometerclient/tests/functional/base.py b/ceilometerclient/tests/functional/base.py deleted file mode 100644 index 524114c..0000000 --- a/ceilometerclient/tests/functional/base.py +++ /dev/null @@ -1,38 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import os - -from tempest.lib.cli import base - - -class ClientTestBase(base.ClientTestBase): - """Base class for ceilometerclient tests. - - Establishes the ceilometer client and retrieves the essential environment - information. - """ - - def _get_clients(self): - cli_dir = os.environ.get( - 'OS_CEILOMETER_CLIENT_EXEC_DIR', - os.path.join(os.path.abspath('.'), '.tox/functional/bin')) - - return base.CLIClient( - username=os.environ.get('OS_USERNAME'), - password=os.environ.get('OS_PASSWORD'), - tenant_name=os.environ.get('OS_TENANT_NAME'), - uri=os.environ.get('OS_AUTH_URL'), - cli_dir=cli_dir) - - def ceilometer(self, *args, **kwargs): - return self.clients.ceilometer(*args, **kwargs) diff --git a/ceilometerclient/tests/functional/hooks/post_test_hook.sh b/ceilometerclient/tests/functional/hooks/post_test_hook.sh deleted file mode 100755 index 385d314..0000000 --- a/ceilometerclient/tests/functional/hooks/post_test_hook.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -xe - -# 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. - -# This script is executed inside post_test_hook function in devstack gate. - -function generate_testr_results { - if [ -f .testrepository/0 ]; then - sudo .tox/functional/bin/testr last --subunit > $WORKSPACE/testrepository.subunit - sudo mv $WORKSPACE/testrepository.subunit $BASE/logs/testrepository.subunit - sudo /usr/os-testr-env/bin/subunit2html $BASE/logs/testrepository.subunit $BASE/logs/testr_results.html - sudo gzip -9 $BASE/logs/testrepository.subunit - sudo gzip -9 $BASE/logs/testr_results.html - sudo chown $USER:$USER $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz - sudo chmod a+r $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz - fi -} - -export CEILOMETERCLIENT_DIR="$BASE/new/python-ceilometerclient" - -# Get admin credentials -cd $BASE/new/devstack -source openrc admin admin - -# Go to the ceilometerclient dir -cd $CEILOMETERCLIENT_DIR - -sudo chown -R $USER:stack $CEILOMETERCLIENT_DIR - -# Run tests -echo "Running ceilometerclient functional test suite" -set +e -# Preserve env for OS_ credentials -sudo -E -H -u $USER tox -efunctional -EXIT_CODE=$? -set -e - -# Collect and parse result -generate_testr_results -exit $EXIT_CODE diff --git a/ceilometerclient/tests/functional/test_readonly_ceilometer.py b/ceilometerclient/tests/functional/test_readonly_ceilometer.py deleted file mode 100644 index 53e2192..0000000 --- a/ceilometerclient/tests/functional/test_readonly_ceilometer.py +++ /dev/null @@ -1,67 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from ceilometerclient.tests.functional import base -import re - - -class SimpleReadOnlyCeilometerClientTest(base.ClientTestBase): - """Basic, read-only tests for Ceilometer CLI client. - - Checks return values and output of read-only commands. - These tests do not presume any content, nor do they create - their own. They only verify the structure of output if present. - """ - - def test_ceilometer_meter_list(self): - result = self.ceilometer('meter-list') - meters = self.parser.listing(result) - self.assertTableStruct(meters, ['Name', 'Type', 'Unit', - 'Resource ID', 'Project ID']) - - def test_ceilometer_resource_list(self): - result = self.ceilometer('resource-list') - resources = self.parser.listing(result) - self.assertTableStruct(resources, ['Resource ID', 'Source', - 'User ID', 'Project ID']) - - def test_ceilometer_alarm_list(self): - result = self.ceilometer('alarm-list') - alarm = self.parser.listing(result) - self.assertTableStruct(alarm, ['Alarm ID', 'Name', 'State', - 'Enabled', 'Continuous']) - - def test_admin_help(self): - help_text = self.ceilometer('help') - lines = help_text.split('\n') - self.assertFirstLineStartsWith(lines, 'usage: ceilometer') - - commands = [] - cmds_start = lines.index('Positional arguments:') - cmds_end = lines.index('Optional arguments:') - command_pattern = re.compile('^ {4}([a-z0-9\-\_]+)') - for line in lines[cmds_start:cmds_end]: - match = command_pattern.match(line) - if match: - commands.append(match.group(1)) - commands = set(commands) - wanted_commands = set(('alarm-combination-create', 'alarm-create', - 'help', 'alarm-delete', 'event-list')) - self.assertFalse(wanted_commands - commands) - - def test_ceilometer_bash_completion(self): - self.ceilometer('bash-completion') - - # Optional arguments - - def test_ceilometer_debug_list(self): - self.ceilometer('meter-list', flags='--debug') diff --git a/ceilometerclient/tests/unit/__init__.py b/ceilometerclient/tests/unit/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/ceilometerclient/tests/unit/__init__.py +++ /dev/null diff --git a/ceilometerclient/tests/unit/test_client.py b/ceilometerclient/tests/unit/test_client.py deleted file mode 100644 index 400e997..0000000 --- a/ceilometerclient/tests/unit/test_client.py +++ /dev/null @@ -1,430 +0,0 @@ -# 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 types - -from keystoneauth1 import exceptions as ks_exc -from keystoneauth1.identity import v2 as v2_auth -from keystoneauth1.identity import v3 as v3_auth -from keystoneauth1 import session as ks_session -import mock -import requests - -from ceilometerclient.apiclient import exceptions -from ceilometerclient import client -from ceilometerclient import exc -from ceilometerclient.tests.unit import utils -from ceilometerclient.v2 import client as v2client - -FAKE_ENV = { - 'username': 'username', - 'password': 'password', - 'tenant_name': 'tenant_name', - 'auth_url': 'http://no.where', - 'auth_plugin': mock.Mock(), - 'ceilometer_url': 'http://no.where', - 'token': '1234', - 'user_domain_name': 'default', - 'project_domain_name': 'default', -} - - -class ClientTest(utils.BaseTestCase): - @staticmethod - def create_client(env, api_version=2, endpoint=None, exclude=[]): - env = dict((k, v) for k, v in env.items() - if k not in exclude) - with mock.patch( - 'ceilometerclient.v2.client.Client._get_redirect_client', - return_value=None): - return client.get_client(api_version, **env) - - def test_client_v2_with_session(self): - resp = mock.Mock(status_code=200, text=b'') - resp.json.return_value = [] - session = mock.Mock() - session.request.return_value = resp - c = client.get_client(2, session=session) - c.resources.list() - self.assertTrue(session.request.called) - self.assertTrue(resp.json.called) - - def test_client_version(self): - c2 = self.create_client(env=FAKE_ENV, api_version=2) - self.assertIsInstance(c2, v2client.Client) - - 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_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) - - 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_without_auth_plugin_keystone_v3(self): - env = FAKE_ENV.copy() - del env['auth_plugin'] - expected = { - 'username': 'username', - 'endpoint': 'http://no.where', - 'tenant_name': 'tenant_name', - 'service_type': None, - 'token': '1234', - 'endpoint_type': None, - 'region_name': None, - 'auth_url': 'http://no.where', - 'tenant_id': None, - 'insecure': None, - 'cacert': None, - 'password': 'password', - 'user_domain_name': 'default', - 'user_domain_id': None, - 'project_domain_name': 'default', - 'project_domain_id': None, - } - with mock.patch('ceilometerclient.client.AuthPlugin') as auth_plugin: - self.create_client(env, api_version=2, endpoint='http://no.where') - self.assertEqual(mock.call(**expected), - auth_plugin.mock_calls[0]) - - def test_v2_client_timeout_invalid_value(self): - env = FAKE_ENV.copy() - env['timeout'] = 'abc' - self.assertRaises(ValueError, self.create_client, env) - env['timeout'] = '1.5' - self.assertRaises(ValueError, self.create_client, env) - - def _test_v2_client_timeout_integer(self, timeout, expected_value): - env = FAKE_ENV.copy() - env['timeout'] = timeout - expected = { - 'auth_plugin': mock.ANY, - 'timeout': expected_value, - 'original_ip': None, - 'http': None, - 'region_name': None, - 'verify': True, - 'timings': None, - 'keyring_saver': None, - 'cert': None, - 'endpoint_type': None, - 'user_agent': None, - 'debug': None, - } - cls = 'ceilometerclient.apiclient.client.HTTPClient' - with mock.patch(cls) as mocked: - self.create_client(env) - mocked.assert_called_with(**expected) - - def test_v2_client_timeout_zero(self): - self._test_v2_client_timeout_integer(0, None) - - def test_v2_client_timeout_valid_value(self): - self._test_v2_client_timeout_integer(30, 30) - - @mock.patch.object(ks_session, 'Session') - def test_v2_client_timeout_keystone_session(self, mocked_session): - mocked_session.side_effect = RuntimeError('Stop!') - env = FAKE_ENV.copy() - env['timeout'] = 5 - del env['auth_plugin'] - del env['token'] - client = self.create_client(env) - self.assertRaises(RuntimeError, client.alarms.list) - args, kwargs = mocked_session.call_args - self.assertEqual(5, kwargs['timeout']) - - def test_v2_client_cacert_in_verify(self): - env = FAKE_ENV.copy() - env['cacert'] = '/path/to/cacert' - client = self.create_client(env) - self.assertEqual('/path/to/cacert', - client.http_client.http_client.verify) - - def test_v2_client_certfile_and_keyfile(self): - env = FAKE_ENV.copy() - env['cert_file'] = '/path/to/cert' - env['key_file'] = '/path/to/keycert' - client = self.create_client(env) - self.assertEqual(('/path/to/cert', '/path/to/keycert'), - client.http_client.http_client.cert) - - def test_v2_client_insecure(self): - env = FAKE_ENV.copy() - env.pop('auth_plugin') - env['os_insecure'] = 'True' - client = self.create_client(env) - self.assertIn('insecure', client.auth_plugin.opts) - self.assertEqual('True', client.auth_plugin.opts['insecure']) - - -class ClientTest2(ClientTest): - @staticmethod - def create_client(env, api_version=2, endpoint=None, exclude=[]): - env = dict((k, v) for k, v in env.items() - if k not in exclude) - with mock.patch( - 'ceilometerclient.v2.client.Client._get_redirect_client', - return_value=None): - return client.Client(api_version, endpoint, **env) - - -class ClientTestWithAodh(ClientTest): - @staticmethod - def create_client(env, api_version=2, endpoint=None, exclude=[]): - env = dict((k, v) for k, v in env.items() - if k not in exclude) - with mock.patch('ceilometerclient.apiclient.client.' - 'HTTPClient.client_request', - return_value=mock.MagicMock()): - return client.get_client(api_version, **env) - - 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.alarm_client.http_client.auth_plugin, - client.AuthPlugin) - - def test_v2_client_insecure(self): - env = FAKE_ENV.copy() - env.pop('auth_plugin') - env['insecure'] = 'True' - client = self.create_client(env) - self.assertIn('insecure', - client.alarm_client.http_client.auth_plugin.opts) - self.assertEqual('True', (client.alarm_client.http_client. - auth_plugin.opts['insecure'])) - - def test_ceilometerclient_available_without_aodh_services_running(self): - env = FAKE_ENV.copy() - env.pop('auth_plugin', None) - with mock.patch('ceilometerclient.apiclient.client.' - 'HTTPClient.client_request') as mocked_request: - mocked_request.side_effect = requests.exceptions.ConnectionError - ceiloclient = client.get_client(2, **env) - self.assertIsInstance(ceiloclient, v2client.Client) - - @mock.patch('ceilometerclient.client.SessionClient') - def test_http_client_with_session_and_aodh(self, mock_sc): - session = mock.Mock() - kwargs = {"session": session, - "service_type": "metering", - "user_agent": "python-ceilometerclient"} - expected = { - "auth": None, - "interface": 'publicURL', - "region_name": None, - "timings": None, - "session": session, - "service_type": "metering", - "user_agent": "python-ceilometerclient"} - kwargs['aodh_endpoint'] = 'http://aodh.where' - client._construct_http_client(**kwargs) - mock_sc.assert_called_with(**expected) - - -class ClientAuthTest(utils.BaseTestCase): - - @staticmethod - def create_client(env, api_version=2, endpoint=None, exclude=[]): - env = dict((k, v) for k, v in env.items() - if k not in exclude) - with mock.patch('ceilometerclient.apiclient.client.' - 'HTTPClient.client_request', - return_value=mock.MagicMock()): - return client.get_client(api_version, **env) - - @mock.patch('keystoneauth1.discover.Discover') - @mock.patch('keystoneauth1.session.Session') - def test_discover_auth_versions(self, session, discover_mock): - env = FAKE_ENV.copy() - env.pop('auth_plugin', None) - - mock_session_instance = mock.MagicMock() - session.return_value = mock_session_instance - - client = self.create_client(env) - client.auth_plugin.opts.pop('token', None) - client.auth_plugin._do_authenticate(mock.MagicMock()) - - self.assertEqual([mock.call(url='http://no.where', - session=mock_session_instance)], - discover_mock.call_args_list) - self.assertIsInstance(mock_session_instance.auth, v3_auth.Password) - - @mock.patch('keystoneauth1.discover.Discover') - @mock.patch('keystoneauth1.session.Session') - def test_discover_auth_versions_v2_only(self, session, discover): - env = FAKE_ENV.copy() - env.pop('auth_plugin', None) - env.pop('user_domain_name', None) - env.pop('user_domain_id', None) - env.pop('project_domain_name', None) - env.pop('project_domain_id', None) - - session_instance_mock = mock.MagicMock() - session.return_value = session_instance_mock - - discover_instance_mock = mock.MagicMock() - discover_instance_mock.url_for.side_effect = (lambda v: v - if v == '2.0' else None) - discover.return_value = discover_instance_mock - - client = self.create_client(env) - client.auth_plugin.opts.pop('token', None) - client.auth_plugin._do_authenticate(mock.MagicMock()) - self.assertEqual([mock.call(url='http://no.where', - session=session_instance_mock)], - discover.call_args_list) - - self.assertIsInstance(session_instance_mock.auth, v2_auth.Password) - - @mock.patch('keystoneauth1.discover.Discover') - @mock.patch('keystoneauth1.session.Session') - def test_discover_auth_versions_raise_discovery_failure(self, - session, - discover): - env = FAKE_ENV.copy() - env.pop('auth_plugin', None) - env.pop('token', None) - - session_instance_mock = mock.MagicMock() - session.return_value = session_instance_mock - - discover_instance_mock = mock.MagicMock() - discover_instance_mock.url_for.side_effect = (lambda v: v - if v == '2.0' else None) - discover.side_effect = ks_exc.DiscoveryFailure - client = self.create_client(env) - self.assertRaises(ks_exc.DiscoveryFailure, - client.auth_plugin._do_authenticate, - mock.Mock()) - discover.side_effect = mock.MagicMock() - client = self.create_client(env) - discover.side_effect = ks_exc.DiscoveryFailure - client.auth_plugin.opts.pop('token', None) - - self.assertRaises(ks_exc.DiscoveryFailure, - client.auth_plugin._do_authenticate, - mock.Mock()) - self.assertEqual([mock.call(url='http://no.where', - session=session_instance_mock), - mock.call(url='http://no.where', - session=session_instance_mock)], - discover.call_args_list) - - @mock.patch('ceilometerclient.client._get_keystone_session') - def test_get_endpoint(self, session): - env = FAKE_ENV.copy() - env.pop('auth_plugin', None) - env.pop('endpoint', None) - - session_instance_mock = mock.MagicMock() - session.return_value = session_instance_mock - - client = self.create_client(env) - client.auth_plugin.opts.pop('endpoint') - client.auth_plugin.opts.pop('token', None) - alarm_auth_plugin = client.alarm_client.http_client.auth_plugin - alarm_auth_plugin.opts.pop('endpoint') - alarm_auth_plugin.opts.pop('token', None) - - self.assertNotEqual(client.auth_plugin, alarm_auth_plugin) - - client.auth_plugin._do_authenticate(mock.MagicMock()) - alarm_auth_plugin._do_authenticate(mock.MagicMock()) - - self.assertEqual([ - mock.call(interface='publicURL', region_name=None, - service_type='metering'), - mock.call(interface='publicURL', region_name=None, - service_type='alarming'), - ], session_instance_mock.get_endpoint.mock_calls) - - def test_http_client_with_session(self): - session = mock.Mock() - session.request.return_value = mock.Mock(status_code=404, - text=b'') - env = {"session": session, - "service_type": "metering", - "user_agent": "python-ceilometerclient"} - c = client.SessionClient(**env) - self.assertRaises(exc.HTTPException, c.get, "/") - - def test_get_aodh_endpoint_without_auth_url(self): - env = FAKE_ENV.copy() - env.pop('auth_plugin', None) - env.pop('endpoint', None) - env.pop('auth_url', None) - client = self.create_client(env, endpoint='fake_endpoint') - self.assertEqual(client.alarm_client.http_client.auth_plugin.opts, - client.auth_plugin.opts) - - @mock.patch('ceilometerclient.client._get_keystone_session') - def test_get_different_endpoint_type(self, session): - env = FAKE_ENV.copy() - env.pop('auth_plugin', None) - env.pop('endpoint', None) - env['endpoint_type'] = 'internal' - - session_instance_mock = mock.MagicMock() - session.return_value = session_instance_mock - - client = self.create_client(env) - client.auth_plugin.opts.pop('endpoint') - client.auth_plugin.opts.pop('token', None) - alarm_auth_plugin = client.alarm_client.http_client.auth_plugin - alarm_auth_plugin.opts.pop('endpoint') - alarm_auth_plugin.opts.pop('token', None) - - self.assertNotEqual(client.auth_plugin, alarm_auth_plugin) - - client.auth_plugin._do_authenticate(mock.MagicMock()) - alarm_auth_plugin._do_authenticate(mock.MagicMock()) - - self.assertEqual([ - mock.call(interface='internal', region_name=None, - service_type='metering'), - mock.call(interface='internal', region_name=None, - service_type='alarming'), - ], session_instance_mock.get_endpoint.mock_calls) - - @mock.patch('ceilometerclient.client._get_keystone_session') - def test_get_sufficient_options_missing(self, session): - env = FAKE_ENV.copy() - env.pop('auth_plugin', None) - env.pop('password', None) - env.pop('endpoint', None) - env.pop('auth_token', None) - env.pop('tenant_name', None) - env.pop('username', None) - - session_instance_mock = mock.MagicMock() - session.return_value = session_instance_mock - client = self.create_client(env) - client.auth_plugin.opts.pop('token', None) - self.assertRaises(exceptions.AuthPluginOptionsMissing, - client.auth_plugin.sufficient_options) diff --git a/ceilometerclient/tests/unit/test_exc.py b/ceilometerclient/tests/unit/test_exc.py deleted file mode 100644 index e67cfc8..0000000 --- a/ceilometerclient/tests/unit/test_exc.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright 2013 eNovance -# 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 json - -from ceilometerclient import exc -from ceilometerclient.tests.unit import utils - -HTTPEXCEPTIONS = {'HTTPBadRequest': exc.HTTPBadRequest, - 'HTTPUnauthorized': exc.HTTPUnauthorized, - 'HTTPForbidden': exc.HTTPForbidden, - 'HTTPNotFound': exc.HTTPNotFound, - 'HTTPMethodNotAllowed': exc.HTTPMethodNotAllowed, - 'HTTPConflict': exc.HTTPConflict, - 'HTTPOverLimit': exc.HTTPOverLimit, - 'HTTPInternalServerError': exc.HTTPInternalServerError, - 'HTTPNotImplemented': exc.HTTPNotImplemented, - 'HTTPBadGateway': exc.HTTPBadGateway, - 'HTTPServiceUnavailable': exc.HTTPServiceUnavailable} - - -class HTTPExceptionsTest(utils.BaseTestCase): - def test_str_no_details(self): - for k, v in HTTPEXCEPTIONS.items(): - exception = v() - ret_str = k + " (HTTP " + str(exception.code) + ")" - self.assertEqual(ret_str, str(exception)) - - def test_str_no_json(self): - for k, v in HTTPEXCEPTIONS.items(): - exception = v(details="foo") - ret_str = k + " (HTTP " + str(exception.code) + ") foo" - self.assertEqual(ret_str, str(exception)) - - def test_str_no_error_message(self): - for k, v in HTTPEXCEPTIONS.items(): - exception = v(details=json.dumps({})) - ret_str = k + " (HTTP " + str(exception.code) + ")" - self.assertEqual(ret_str, str(exception)) - - def test_str_no_faultstring(self): - for k, v in HTTPEXCEPTIONS.items(): - exception = v( - details=json.dumps({"error_message": {"foo": "bar"}})) - ret_str = (k + " (HTTP " + str(exception.code) + ") " + - str({u'foo': u'bar'})) - self.assertEqual(ret_str, str(exception)) - - def test_str_error_message_unknown_format(self): - for k, v in HTTPEXCEPTIONS.items(): - exception = v(details=json.dumps({"error_message": "oops"})) - ret_str = k + " (HTTP " + str(exception.code) + ") oops" - self.assertEqual(ret_str, str(exception)) - - def test_str_faultstring(self): - for k, v in HTTPEXCEPTIONS.items(): - exception = v(details=json.dumps( - {"error_message": {"faultstring": "oops"}})) - ret_str = k + " (HTTP " + str(exception.code) + ") ERROR oops" - self.assertEqual(ret_str, str(exception)) - - def test_from_response(self): - class HTTPLibLikeResponse(object): - status = 400 - - class RequestsLikeResponse(object): - status_code = 401 - - class UnexpectedResponse(object): - code = 200 - - self.assertEqual(HTTPLibLikeResponse.status, - exc.from_response(HTTPLibLikeResponse).code) - self.assertEqual(RequestsLikeResponse.status_code, - exc.from_response(RequestsLikeResponse).code) - self.assertRaises(TypeError, exc.from_response, UnexpectedResponse) diff --git a/ceilometerclient/tests/unit/test_openstack_common.py b/ceilometerclient/tests/unit/test_openstack_common.py deleted file mode 100644 index 04a94e2..0000000 --- a/ceilometerclient/tests/unit/test_openstack_common.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2015 Huawei. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from ceilometerclient.common import base -from ceilometerclient.tests.unit import utils -from ceilometerclient.v2 import events - - -class BaseTest(utils.BaseTestCase): - - def test_two_resources_with_same_id_are_not_equal(self): - # Two resources with same ID: never equal if their info is not equal - r1 = base.Resource(None, {'id': 1, 'name': 'hi'}) - r2 = base.Resource(None, {'id': 1, 'name': 'hello'}) - self.assertNotEqual(r1, r2) - - def test_two_resources_with_same_id_and_info_are_equal(self): - # Two resources with same ID: equal if their info is equal - r1 = base.Resource(None, {'id': 1, 'name': 'hello'}) - r2 = base.Resource(None, {'id': 1, 'name': 'hello'}) - self.assertEqual(r1, r2) - - def test_two_resources_with_diff_type_are_not_equal(self): - # Two resources of different types: never equal - r1 = base.Resource(None, {'id': 1}) - r2 = events.Event(None, {'id': 1}) - self.assertNotEqual(r1, r2) - - def test_two_resources_with_no_id_are_equal(self): - # Two resources with no ID: equal if their info is equal - r1 = base.Resource(None, {'name': 'joe', 'age': 12}) - r2 = base.Resource(None, {'name': 'joe', 'age': 12}) - self.assertEqual(r1, r2) diff --git a/ceilometerclient/tests/unit/test_shell.py b/ceilometerclient/tests/unit/test_shell.py deleted file mode 100644 index d666594..0000000 --- a/ceilometerclient/tests/unit/test_shell.py +++ /dev/null @@ -1,287 +0,0 @@ -# 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 re -import sys - -import fixtures -from keystoneauth1 import session as ks_session -import mock -import six -from testtools import matchers - -from ceilometerclient.apiclient import client as api_client -from ceilometerclient import client -from ceilometerclient import exc -from ceilometerclient import shell as ceilometer_shell -from ceilometerclient.tests.unit import utils - -FAKE_V2_ENV = {'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password', - 'OS_TENANT_NAME': 'tenant_name', - 'OS_AUTH_URL': 'http://localhost:5000/v2.0'} - -FAKE_V3_ENV = {'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password', - 'OS_USER_DOMAIN_NAME': 'domain_name', - 'OS_PROJECT_ID': '1234567890', - 'OS_AUTH_URL': 'http://localhost:5000/v3'} - - -class ShellTestBase(utils.BaseTestCase): - - @mock.patch('sys.stdout', new=six.StringIO()) - @mock.patch.object(ks_session, 'Session', mock.MagicMock()) - @mock.patch.object(client.client.HTTPClient, - 'client_request', mock.MagicMock()) - def shell(self, argstr): - try: - _shell = ceilometer_shell.CeilometerShell() - _shell.main(argstr.split()) - except SystemExit: - exc_type, exc_value, exc_traceback = sys.exc_info() - self.assertEqual(0, exc_value.code) - - return sys.stdout.getvalue() - - # Patch os.environ to avoid required auth info. - def make_env(self, env_version, exclude=None): - env = dict((k, v) for k, v in env_version.items() if k != exclude) - self.useFixture(fixtures.MonkeyPatch('os.environ', env)) - - -class ShellHelpTest(ShellTestBase): - RE_OPTIONS = re.DOTALL | re.MULTILINE - - def test_help_unknown_command(self): - self.assertRaises(exc.CommandError, self.shell, 'help foofoo') - - def test_help(self): - required = [ - '.*?^usage: ceilometer', - '.*?^See "ceilometer help COMMAND" ' - 'for help on a specific command', - ] - for argstr in ['--help', 'help']: - help_text = self.shell(argstr) - for r in required: - self.assertThat(help_text, - matchers.MatchesRegex(r, - self.RE_OPTIONS)) - - def test_help_on_subcommand(self): - required = [ - '.*?^usage: ceilometer meter-list', - ".*?^List the user's meter", - ] - argstrings = [ - 'help meter-list', - ] - for argstr in argstrings: - help_text = self.shell(argstr) - for r in required: - self.assertThat(help_text, - matchers.MatchesRegex(r, self.RE_OPTIONS)) - - def test_get_base_parser(self): - standalone_shell = ceilometer_shell.CeilometerShell() - parser = standalone_shell.get_base_parser() - self.assertEqual(600, parser.get_default('timeout')) - - -class ShellBashCompletionTest(ShellTestBase): - - def test_bash_completion(self): - completion_commands = self.shell("bash-completion") - options = completion_commands.split(' ') - self.assertNotIn('bash_completion', options) - for option in options: - self.assertThat(option, - matchers.MatchesRegex(r'[a-z0-9-]')) - - -class ShellKeystoneV2Test(ShellTestBase): - - @mock.patch.object(ks_session, 'Session') - @mock.patch('ceilometerclient.v2.client.Client._get_redirect_client', - mock.Mock(return_value=None)) - def test_debug_switch_raises_error(self, mock_ksclient): - mock_ksclient.side_effect = exc.HTTPUnauthorized - self.make_env(FAKE_V2_ENV) - args = ['--debug', 'event-list'] - self.assertRaises(exc.CommandError, ceilometer_shell.main, args) - - @mock.patch.object(ks_session, 'Session') - @mock.patch('ceilometerclient.v2.client.Client._get_redirect_client', - mock.Mock(return_value=None)) - def test_dash_d_switch_raises_error(self, mock_ksclient): - mock_ksclient.side_effect = exc.CommandError("FAIL") - self.make_env(FAKE_V2_ENV) - args = ['-d', 'event-list'] - self.assertRaises(exc.CommandError, ceilometer_shell.main, args) - - @mock.patch('sys.stderr') - @mock.patch.object(ks_session, 'Session') - def test_no_debug_switch_no_raises_errors(self, mock_ksclient, __): - mock_ksclient.side_effect = exc.HTTPUnauthorized("FAIL") - self.make_env(FAKE_V2_ENV) - args = ['event-list'] - self.assertRaises(SystemExit, ceilometer_shell.main, args) - - -class ShellKeystoneV3Test(ShellTestBase): - - @mock.patch.object(ks_session, 'Session') - @mock.patch('ceilometerclient.v2.client.Client._get_redirect_client', - mock.Mock(return_value=None)) - def test_debug_switch_raises_error(self, mock_ksclient): - mock_ksclient.side_effect = exc.HTTPUnauthorized - self.make_env(FAKE_V3_ENV) - args = ['--debug', 'event-list'] - self.assertRaises(exc.CommandError, ceilometer_shell.main, args) - - @mock.patch.object(ks_session, 'Session') - def test_dash_d_switch_raises_error(self, mock_ksclient): - mock_ksclient.side_effect = exc.CommandError("FAIL") - self.make_env(FAKE_V3_ENV) - args = ['-d', 'event-list'] - self.assertRaises(exc.CommandError, ceilometer_shell.main, args) - - @mock.patch('sys.stderr') - @mock.patch.object(ks_session, 'Session') - def test_no_debug_switch_no_raises_errors(self, mock_ksclient, __): - mock_ksclient.side_effect = exc.HTTPUnauthorized("FAIL") - self.make_env(FAKE_V3_ENV) - args = ['event-list'] - self.assertRaises(SystemExit, ceilometer_shell.main, args) - - -class ShellTimeoutTest(ShellTestBase): - - @mock.patch('sys.stderr', new=six.StringIO()) - def _test_timeout(self, timeout, expected_msg): - args = ['--timeout', timeout, 'alarm-list'] - self.assertRaises(SystemExit, ceilometer_shell.main, args) - self.assertEqual(expected_msg, sys.stderr.getvalue().splitlines()[-1]) - - def test_timeout_invalid_value(self): - expected_msg = ('ceilometer: error: argument --timeout: ' - 'abc must be an integer') - self._test_timeout('abc', expected_msg) - - def test_timeout_negative_value(self): - expected_msg = ('ceilometer: error: argument --timeout: ' - '-1 must be greater than 0') - self._test_timeout('-1', expected_msg) - - def test_timeout_float_value(self): - expected_msg = ('ceilometer: error: argument --timeout: ' - '1.5 must be an integer') - self._test_timeout('1.5', expected_msg) - - def test_timeout_zero(self): - expected_msg = ('ceilometer: error: argument --timeout: ' - '0 must be greater than 0') - self._test_timeout('0', expected_msg) - - @mock.patch.object(ks_session, 'Session') - @mock.patch('ceilometerclient.v2.client.Client._get_redirect_client', - mock.Mock(return_value=None)) - def test_timeout_keystone_session(self, mocked_session): - mocked_session.side_effect = exc.HTTPUnauthorized("FAIL") - self.make_env(FAKE_V2_ENV) - args = ['--debug', '--timeout', '5', 'alarm-list'] - self.assertRaises(exc.CommandError, ceilometer_shell.main, args) - args, kwargs = mocked_session.call_args - self.assertEqual(5, kwargs.get('timeout')) - - -class ShellInsecureTest(ShellTestBase): - - @mock.patch.object(api_client, 'HTTPClient') - @mock.patch('ceilometerclient.v2.client.Client._get_redirect_client', - mock.Mock(return_value=None)) - def test_insecure_true_ceilometer(self, mocked_client): - self.make_env(FAKE_V2_ENV) - args = ['--debug', '--os-insecure', 'true', 'alarm-list'] - self.assertIsNone(ceilometer_shell.main(args)) - args, kwargs = mocked_client.call_args - self.assertFalse(kwargs.get('verify')) - - @mock.patch.object(ks_session, 'Session') - @mock.patch('ceilometerclient.v2.client.Client._get_redirect_client', - mock.Mock(return_value=None)) - def test_insecure_true_keystone(self, mocked_session): - mocked_session.side_effect = exc.HTTPUnauthorized("FAIL") - self.make_env(FAKE_V2_ENV) - args = ['--debug', '--os-insecure', 'true', 'alarm-list'] - self.assertRaises(exc.CommandError, ceilometer_shell.main, args) - args, kwargs = mocked_session.call_args - self.assertFalse(kwargs.get('verify')) - - @mock.patch.object(api_client, 'HTTPClient') - @mock.patch('ceilometerclient.v2.client.Client._get_redirect_client', - mock.Mock(return_value=None)) - def test_insecure_false_ceilometer(self, mocked_client): - self.make_env(FAKE_V2_ENV) - args = ['--debug', '--os-insecure', 'false', 'alarm-list'] - self.assertIsNone(ceilometer_shell.main(args)) - args, kwargs = mocked_client.call_args - self.assertTrue(kwargs.get('verify')) - - @mock.patch.object(ks_session, 'Session') - @mock.patch('ceilometerclient.v2.client.Client._get_redirect_client', - mock.Mock(return_value=None)) - def test_insecure_false_keystone(self, mocked_session): - mocked_session.side_effect = exc.HTTPUnauthorized("FAIL") - self.make_env(FAKE_V2_ENV) - args = ['--debug', '--os-insecure', 'false', 'alarm-list'] - self.assertRaises(exc.CommandError, ceilometer_shell.main, args) - args, kwargs = mocked_session.call_args - self.assertTrue(kwargs.get('verify')) - - -class ShellEndpointTest(ShellTestBase): - - @mock.patch('ceilometerclient.v2.client.Client') - def _test_endpoint_and_token(self, token_name, endpoint_name, mocked): - args = ['--debug', token_name, 'fake-token', - endpoint_name, 'http://fake-url', 'alarm-list'] - self.assertIsNone(ceilometer_shell.main(args)) - args, kwargs = mocked.call_args - self.assertEqual('http://fake-url', kwargs.get('endpoint')) - self.assertEqual('fake-token', kwargs.get('token')) - - def test_endpoint_and_token(self): - self._test_endpoint_and_token('--os-auth-token', '--ceilometer-url') - self._test_endpoint_and_token('--os-auth-token', '--os-endpoint') - self._test_endpoint_and_token('--os-token', '--ceilometer-url') - self._test_endpoint_and_token('--os-token', '--os-endpoint') - - -class ShellAlarmUpdateRepeatAction(ShellTestBase): - @mock.patch('ceilometerclient.v2.alarms.AlarmManager.update') - @mock.patch('ceilometerclient.v2.client.Client._get_redirect_client', - mock.Mock()) - def test_repeat_action_not_specified(self, mocked): - self.make_env(FAKE_V2_ENV) - - def _test(method): - args = ['--debug', method, '--state', 'alarm', '123'] - ceilometer_shell.main(args) - args, kwargs = mocked.call_args - self.assertIsNone(kwargs.get('repeat_actions')) - - _test('alarm-update') - _test('alarm-threshold-update') - _test('alarm-combination-update') - _test('alarm-event-update') diff --git a/ceilometerclient/tests/unit/test_utils.py b/ceilometerclient/tests/unit/test_utils.py deleted file mode 100644 index 5b20437..0000000 --- a/ceilometerclient/tests/unit/test_utils.py +++ /dev/null @@ -1,298 +0,0 @@ -# Copyright 2013 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 itertools - -import mock -import six - -from ceilometerclient.common import utils -from ceilometerclient.tests.unit import utils as test_utils - - -class UtilsTest(test_utils.BaseTestCase): - - def test_prettytable(self): - class Struct(object): - def __init__(self, **entries): - self.__dict__.update(entries) - - # test that the prettytable output is wellformatted (left-aligned) - with mock.patch('sys.stdout', new=six.StringIO()) as stdout: - utils.print_dict({'K': 'k', 'Key': 'Value'}) - self.assertEqual('''\ -+----------+-------+ -| Property | Value | -+----------+-------+ -| K | k | -| Key | Value | -+----------+-------+ -''', stdout.getvalue()) - - with mock.patch('sys.stdout', new=six.StringIO()) as stdout: - utils.print_dict({'alarm_id': '262567fd-d79a-4bbb-a9d0-59d879b6', - 'name': u'\u6d4b\u8bd5', - 'description': u'\u6d4b\u8bd5', - 'state': 'insufficient data', - 'repeat_actions': 'False', - 'type': 'threshold', - 'threshold': '1.0', - 'statistic': 'avg', - 'alarm_actions': [u'http://something/alarm1', - u'http://something/alarm2'], - 'ok_actions': [{"get_attr1": - [u"web_server_scaleup_policy1", - u"alarm_url1"]}, - {"get_attr2": - [u"web_server_scaleup_policy2", - u"alarm_url2"]}], - 'time_constraints': '[{name: c1,' - '\\n description: test,' - '\\n start: 0 18 * * *,' - '\\n duration: 1,' - '\\n timezone: US}]'}, - wrap=72) - expected = u'''\ -+------------------+-------------------------------------------------------\ ---------+ -| Property | Value \ - | -+------------------+-------------------------------------------------------\ ---------+ -| alarm_actions | ["http://something/alarm1", "http://something/alarm2"]\ - | -| alarm_id | 262567fd-d79a-4bbb-a9d0-59d879b6 \ - | -| description | \u6d4b\u8bd5 \ - | -| name | \u6d4b\u8bd5 \ - | -| ok_actions | [{"get_attr1": ["web_server_scaleup_policy1", "alarm_u\ -rl1"]}, | -| | {"get_attr2": ["web_server_scaleup_policy2", "alarm_ur\ -l2"]}] | -| repeat_actions | False \ - | -| state | insufficient data \ - | -| statistic | avg \ - | -| threshold | 1.0 \ - | -| time_constraints | [{name: c1, \ - | -| | description: test, \ - | -| | start: 0 18 * * *, \ - | -| | duration: 1, \ - | -| | timezone: US}] \ - | -| type | threshold \ - | -+------------------+-------------------------------------------------------\ ---------+ -''' - # py2 prints str type, py3 prints unicode type - if six.PY2: - expected = expected.encode('utf-8') - self.assertEqual(expected, stdout.getvalue()) - - def test_print_list(self): - class Foo(object): - def __init__(self, one, two, three): - self.one = one - self.two = two - self.three = three - - foo_list = [ - Foo(10, 'a', 'B'), - Foo(8, 'c', 'c'), - Foo(12, '0', 'Z')] - - def do_print_list(sortby): - with mock.patch('sys.stdout', new=six.StringIO()) as stdout: - utils.print_list(foo_list, - ['one', 'two', 'three'], - ['1st', '2nd', '3rd'], - {'one': lambda o: o.one * 10}, - sortby) - return stdout.getvalue() - - printed = do_print_list(None) - self.assertEqual('''\ -+-----+-----+-----+ -| 1st | 2nd | 3rd | -+-----+-----+-----+ -| 100 | a | B | -| 80 | c | c | -| 120 | 0 | Z | -+-----+-----+-----+ -''', printed) - - printed = do_print_list(0) - self.assertEqual('''\ -+-----+-----+-----+ -| 1st | 2nd | 3rd | -+-----+-----+-----+ -| 80 | c | c | -| 100 | a | B | -| 120 | 0 | Z | -+-----+-----+-----+ -''', printed) - - printed = do_print_list(1) - self.assertEqual('''\ -+-----+-----+-----+ -| 1st | 2nd | 3rd | -+-----+-----+-----+ -| 120 | 0 | Z | -| 100 | a | B | -| 80 | c | c | -+-----+-----+-----+ -''', printed) - - def test_args_array_to_dict(self): - my_args = { - 'matching_metadata': ['metadata.key=metadata_value'], - 'other': 'value' - } - cleaned_dict = utils.args_array_to_dict(my_args, - "matching_metadata") - self.assertEqual({ - 'matching_metadata': {'metadata.key': 'metadata_value'}, - 'other': 'value' - }, cleaned_dict) - - def test_args_array_to_list_of_dicts(self): - starts = ['0 11 * * *', '"0 11 * * *"', '\'0 11 * * *\''] - timezones = [None, 'US/Eastern', '"US/Eastern"', '\'US/Eastern\''] - descs = [None, 'de sc', '"de sc"', '\'de sc\''] - for start, tz, desc in itertools.product(starts, timezones, descs): - my_args = { - 'time_constraints': ['name=const1;start=%s;duration=1' - % start], - 'other': 'value' - } - expected = { - 'time_constraints': [dict(name='const1', - start='0 11 * * *', - duration='1')], - 'other': 'value' - } - if tz: - my_args['time_constraints'][0] += ';timezone=%s' % tz - expected['time_constraints'][0]['timezone'] = 'US/Eastern' - if desc: - my_args['time_constraints'][0] += ';description=%s' % desc - expected['time_constraints'][0]['description'] = 'de sc' - - cleaned = utils.args_array_to_list_of_dicts(my_args, - 'time_constraints') - self.assertEqual(expected, cleaned) - - def test_key_with_slash_to_nested_dict(self): - my_args = { - 'combination_rule/alarm_ids': ['id1', 'id2'], - 'combination_rule/operator': 'and', - 'threshold_rule/threshold': 400, - 'threshold_rule/statictic': 'avg', - 'threshold_rule/comparison_operator': 'or', - } - nested_dict = utils.key_with_slash_to_nested_dict(my_args) - self.assertEqual({ - 'combination_rule': {'alarm_ids': ['id1', 'id2'], - 'operator': 'and'}, - 'threshold_rule': {'threshold': 400, - 'statictic': 'avg', - 'comparison_operator': 'or'}, - }, nested_dict) - - def test_arg(self): - @utils.arg(help="not_required_no_default.") - def not_required_no_default(): - pass - _, args = not_required_no_default.__dict__['arguments'][0] - self.assertEqual("not_required_no_default.", args['help']) - - @utils.arg(required=True, help="required_no_default.") - def required_no_default(): - pass - _, args = required_no_default.__dict__['arguments'][0] - self.assertEqual("required_no_default. Required.", args['help']) - - @utils.arg(default=42, help="not_required_default.") - def not_required_default(): - pass - _, args = not_required_default.__dict__['arguments'][0] - self.assertEqual("not_required_default. Defaults to 42.", args['help']) - - def test_merge_nested_dict(self): - dest = {'key': 'value', - 'nested': {'key2': 'value2', - 'key3': 'value3', - 'nested2': {'key': 'value', - 'some': 'thing'}}} - source = {'key': 'modified', - 'nested': {'key3': 'modified3', - 'nested2': {'key5': 'value5'}}} - utils.merge_nested_dict(dest, source, depth=1) - - self.assertEqual({'key': 'modified', - 'nested': {'key2': 'value2', - 'key3': 'modified3', - 'nested2': {'key5': 'value5'}}}, dest) - - def test_merge_nested_dict_no_depth(self): - dest = {'key': 'value', - 'nested': {'key2': 'value2', - 'key3': 'value3', - 'nested2': {'key': 'value', - 'some': 'thing'}}} - source = {'key': 'modified', - 'nested': {'key3': 'modified3', - 'nested2': {'key5': 'value5'}}} - utils.merge_nested_dict(dest, source) - - self.assertEqual({'key': 'modified', - 'nested': {'key3': 'modified3', - 'nested2': {'key5': 'value5'}}}, dest) - - @mock.patch('prettytable.PrettyTable') - def test_format_nested_list_of_dict(self, pt_mock): - actual_rows = [] - - def mock_add_row(row): - actual_rows.append(row) - - table = mock.Mock() - table.add_row = mock_add_row - table.get_string.return_value = "the table" - - test_data = [ - {'column_1': 'value_c', 'column_2': 'value_23'}, - {'column_1': 'value_b', 'column_2': 'value_22'}, - {'column_1': 'value_a', 'column_2': 'value_21'} - ] - columns = ['column_1', 'column_2'] - pt_mock.return_value = table - - rval = utils.format_nested_list_of_dict(test_data, columns) - self.assertEqual("the table", rval) - self.assertEqual([['value_a', 'value_21'], - ['value_b', 'value_22'], - ['value_c', 'value_23']], - actual_rows) diff --git a/ceilometerclient/tests/unit/utils.py b/ceilometerclient/tests/unit/utils.py deleted file mode 100644 index 57bc276..0000000 --- a/ceilometerclient/tests/unit/utils.py +++ /dev/null @@ -1,24 +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 fixtures -import testtools - - -class BaseTestCase(testtools.TestCase): - - def setUp(self): - super(BaseTestCase, self).setUp() - self.useFixture(fixtures.FakeLogger()) diff --git a/ceilometerclient/tests/unit/v2/__init__.py b/ceilometerclient/tests/unit/v2/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/ceilometerclient/tests/unit/v2/__init__.py +++ /dev/null diff --git a/ceilometerclient/tests/unit/v2/test_alarms.py b/ceilometerclient/tests/unit/v2/test_alarms.py deleted file mode 100644 index 20fa118..0000000 --- a/ceilometerclient/tests/unit/v2/test_alarms.py +++ /dev/null @@ -1,559 +0,0 @@ -# -# Copyright 2013 Red Hat, Inc -# -# 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 six -from six.moves import xrange # noqa -import testtools - -from ceilometerclient.apiclient import client -from ceilometerclient.apiclient import fake_client -from ceilometerclient import exc -from ceilometerclient.v2 import alarms - -AN_ALARM = {u'alarm_actions': [u'http://site:8000/alarm'], - u'ok_actions': [u'http://site:8000/ok'], - u'description': u'An alarm', - u'type': u'threshold', - u'severity': 'low', - u'threshold_rule': { - u'meter_name': u'storage.objects', - u'query': [{u'field': u'key_name', - u'op': u'eq', - u'value': u'key_value'}], - u'evaluation_periods': 2, - u'period': 240.0, - u'statistic': u'avg', - u'threshold': 200.0, - u'comparison_operator': 'gt'}, - u'time_constraints': [ - { - u'name': u'cons1', - u'description': u'desc1', - u'start': u'0 11 * * *', - u'duration': 300, - u'timezone': u''}, - { - u'name': u'cons2', - u'description': u'desc2', - u'start': u'0 23 * * *', - u'duration': 600, - u'timezone': ''}], - u'timestamp': u'2013-05-09T13:41:23.085000', - u'enabled': True, - u'alarm_id': u'alarm-id', - u'state': u'ok', - u'insufficient_data_actions': [u'http://site:8000/nodata'], - u'user_id': u'user-id', - u'project_id': u'project-id', - u'state_timestamp': u'2013-05-09T13:41:23.085000', - u'repeat_actions': False, - u'name': 'SwiftObjectAlarm'} -CREATE_ALARM = copy.deepcopy(AN_ALARM) -del CREATE_ALARM['timestamp'] -del CREATE_ALARM['state_timestamp'] -del CREATE_ALARM['alarm_id'] -CREATE_ALARM_WITHOUT_TC = copy.deepcopy(CREATE_ALARM) -del CREATE_ALARM_WITHOUT_TC['time_constraints'] -DELTA_ALARM = {u'alarm_actions': ['url1', 'url2']} -DELTA_ALARM_RULE = {u'comparison_operator': u'lt', - u'threshold': 42.1, - u'meter_name': u'foobar', - u'query': [{u'field': u'key_name', - u'op': u'eq', - u'value': u'key_value'}]} -DELTA_ALARM_TC = [{u'name': u'cons1', - u'duration': 500}] -DELTA_ALARM['time_constraints'] = DELTA_ALARM_TC -DELTA_ALARM['user_id'] = u'new-user-id' -UPDATED_ALARM = copy.deepcopy(AN_ALARM) -UPDATED_ALARM.update(DELTA_ALARM) -UPDATED_ALARM['threshold_rule'].update(DELTA_ALARM_RULE) -DELTA_ALARM['remove_time_constraints'] = 'cons2' -UPDATED_ALARM['time_constraints'] = [{u'name': u'cons1', - u'description': u'desc1', - u'start': u'0 11 * * *', - u'duration': 500, - u'timezone': u''}] -DELTA_ALARM['threshold_rule'] = DELTA_ALARM_RULE -UPDATE_ALARM = copy.deepcopy(UPDATED_ALARM) -UPDATE_ALARM['remove_time_constraints'] = 'cons2' -UPDATE_ALARM['user_id'] = u'new-user-id' -del UPDATE_ALARM['project_id'] -del UPDATE_ALARM['name'] -del UPDATE_ALARM['alarm_id'] -del UPDATE_ALARM['timestamp'] -del UPDATE_ALARM['state_timestamp'] - -AN_LEGACY_ALARM = {u'alarm_actions': [u'http://site:8000/alarm'], - u'ok_actions': [u'http://site:8000/ok'], - u'description': u'An alarm', - u'matching_metadata': {u'key_name': u'key_value'}, - u'evaluation_periods': 2, - u'timestamp': u'2013-05-09T13:41:23.085000', - u'enabled': True, - u'meter_name': u'storage.objects', - u'period': 240.0, - u'alarm_id': u'alarm-id', - u'state': u'ok', - u'severity': u'low', - u'insufficient_data_actions': [u'http://site:8000/nodata'], - u'statistic': u'avg', - u'threshold': 200.0, - u'user_id': u'user-id', - u'project_id': u'project-id', - u'state_timestamp': u'2013-05-09T13:41:23.085000', - u'comparison_operator': 'gt', - u'repeat_actions': False, - u'name': 'SwiftObjectAlarm'} -CREATE_LEGACY_ALARM = copy.deepcopy(AN_LEGACY_ALARM) -del CREATE_LEGACY_ALARM['timestamp'] -del CREATE_LEGACY_ALARM['state_timestamp'] -del CREATE_LEGACY_ALARM['alarm_id'] -DELTA_LEGACY_ALARM = {u'alarm_actions': ['url1', 'url2'], - u'comparison_operator': u'lt', - u'meter_name': u'foobar', - u'threshold': 42.1} -DELTA_LEGACY_ALARM['time_constraints'] = [{u'name': u'cons1', - u'duration': 500}] -DELTA_LEGACY_ALARM['user_id'] = u'new-user-id' -DELTA_LEGACY_ALARM['remove_time_constraints'] = 'cons2' -UPDATED_LEGACY_ALARM = copy.deepcopy(AN_LEGACY_ALARM) -UPDATED_LEGACY_ALARM.update(DELTA_LEGACY_ALARM) -UPDATE_LEGACY_ALARM = copy.deepcopy(UPDATED_LEGACY_ALARM) -UPDATE_LEGACY_ALARM['user_id'] = u'new-user-id' -del UPDATE_LEGACY_ALARM['project_id'] -del UPDATE_LEGACY_ALARM['name'] -del UPDATE_LEGACY_ALARM['alarm_id'] -del UPDATE_LEGACY_ALARM['timestamp'] -del UPDATE_LEGACY_ALARM['state_timestamp'] - -FULL_DETAIL = ('{"alarm_actions": [], ' - '"user_id": "8185aa72421a4fd396d4122cba50e1b5", ' - '"name": "scombo", ' - '"timestamp": "2013-10-03T08:58:33.647912", ' - '"enabled": true, ' - '"state_timestamp": "2013-10-03T08:58:33.647912", ' - '"rule": {"operator": "or", "alarm_ids": ' - '["062cc907-3a9f-4867-ab3b-fa83212b39f7"]}, ' - '"alarm_id": "alarm-id, ' - '"state": "insufficient data", ' - '"insufficient_data_actions": [], ' - '"repeat_actions": false, ' - '"ok_actions": [], ' - '"project_id": "57d04f24d0824b78b1ea9bcecedbda8f", ' - '"type": "combination", ' - '"description": "Combined state of alarms ' - '062cc907-3a9f-4867-ab3b-fa83212b39f7"}') -ALARM_HISTORY = [{'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f', - 'user_id': '8185aa72421a4fd396d4122cba50e1b5', - 'event_id': 'c74a8611-6553-4764-a860-c15a6aabb5d0', - 'timestamp': '2013-10-03T08:59:28.326000', - 'detail': '{"state": "alarm"}', - 'alarm_id': 'alarm-id', - 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f', - 'type': 'state transition'}, - {'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f', - 'user_id': '8185aa72421a4fd396d4122cba50e1b5', - 'event_id': 'c74a8611-6553-4764-a860-c15a6aabb5d0', - 'timestamp': '2013-10-03T08:59:28.326000', - 'detail': '{"description": "combination of one"}', - 'alarm_id': 'alarm-id', - 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f', - 'type': 'rule change'}, - {'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f', - 'user_id': '8185aa72421a4fd396d4122cba50e1b5', - 'event_id': '4fd7df9e-190d-4471-8884-dc5a33d5d4bb', - 'timestamp': '2013-10-03T08:58:33.647000', - 'detail': FULL_DETAIL, - 'alarm_id': 'alarm-id', - 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f', - 'type': 'creation'}] - -fixtures = { - '/v2/alarms': - { - 'GET': ( - {}, - [AN_ALARM], - ), - 'POST': ( - {}, - CREATE_ALARM, - ), - }, - '/v2/alarms/alarm-id': - { - 'GET': ( - {}, - AN_ALARM, - ), - 'PUT': ( - {}, - UPDATED_ALARM, - ), - 'DELETE': ( - {}, - None, - ), - }, - '/v2/alarms/unk-alarm-id': - { - 'GET': ( - {}, - None, - ), - 'PUT': ( - {}, - None, - ), - }, - '/v2/alarms/alarm-id/state': - { - 'PUT': ( - {}, - {'alarm': 'alarm'} - ), - 'GET': ( - {}, - {'alarm': 'alarm'} - ), - - }, - '/v2/alarms?q.field=project_id&q.field=name&q.op=&q.op=' - '&q.type=&q.type=&q.value=project-id&q.value=SwiftObjectAlarm': - { - 'GET': ( - {}, - [AN_ALARM], - ), - }, - '/v2/alarms/victim-id': - { - 'DELETE': ( - {}, - None, - ), - }, - '/v2/alarms/alarm-id/history': - { - 'GET': ( - {}, - ALARM_HISTORY, - ), - }, - '/v2/alarms/alarm-id/history?q.field=timestamp&q.op=&q.type=&q.value=NOW': - { - 'GET': ( - {}, - ALARM_HISTORY, - ), - }, -} - - -class AlarmManagerTest(testtools.TestCase): - - def setUp(self): - super(AlarmManagerTest, self).setUp() - 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' - ] - self.http_client.assert_called(*expect) - self.assertEqual(1, len(alarms)) - self.assertEqual('alarm-id', alarms[0].alarm_id) - - def test_list_with_query(self): - alarms = list(self.mgr.list(q=[{"field": "project_id", - "value": "project-id"}, - {"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', - ] - self.http_client.assert_called(*expect) - self.assertEqual(1, len(alarms)) - self.assertEqual('alarm-id', alarms[0].alarm_id) - - def test_get(self): - alarm = self.mgr.get(alarm_id='alarm-id') - expect = [ - 'GET', '/v2/alarms/alarm-id' - ] - self.http_client.assert_called(*expect) - self.assertIsNotNone(alarm) - self.assertEqual('alarm-id', alarm.alarm_id) - self.assertEqual(alarm.rule, alarm.threshold_rule) - - def test_create(self): - alarm = self.mgr.create(**CREATE_ALARM) - expect = [ - 'POST', '/v2/alarms' - ] - 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 = [ - 'GET', '/v2/alarms/alarm-id' - ] - 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-id', alarm.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 = [ - 'GET', '/v2/alarms/alarm-id' - ] - 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-id', alarm.alarm_id) - for (key, value) in six.iteritems(UPDATED_ALARM): - self.assertEqual(getattr(alarm, key), value) - - def test_set_state(self): - state = self.mgr.set_state(alarm_id='alarm-id', state='alarm') - expect = [ - 'PUT', '/v2/alarms/alarm-id/state' - ] - self.http_client.assert_called(*expect, body='alarm') - self.assertEqual({'alarm': 'alarm'}, state) - - def test_get_state(self): - state = self.mgr.get_state(alarm_id='alarm-id') - expect = [ - 'GET', '/v2/alarms/alarm-id/state' - ] - self.http_client.assert_called(*expect) - self.assertEqual({'alarm': 'alarm'}, state) - - def test_delete(self): - deleted = self.mgr.delete(alarm_id='victim-id') - expect = [ - 'DELETE', '/v2/alarms/victim-id' - ] - 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.assertIsNotNone(alarm) - alarm.get() - expect = [ - 'GET', '/v2/alarms/alarm-id' - ] - 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.assertIsNotNone(alarm) - state = alarm.get_state() - expect_get_1 = [ - 'GET', '/v2/alarms/alarm-id' - ] - 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_update_missing(self): - alarm = None - try: - alarm = self.mgr.update(alarm_id='unk-alarm-id', **UPDATE_ALARM) - except exc.CommandError: - pass - self.assertIsNone(alarm) - - def test_delete_from_alarm_class(self): - alarm = self.mgr.get(alarm_id='alarm-id') - self.assertIsNotNone(alarm) - deleted = alarm.delete() - expect_get = [ - 'GET', '/v2/alarms/alarm-id' - ] - 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] - self.http_client.assert_called(*expect) - for i in xrange(len(history)): - change = history[i] - self.assertIsInstance(change, alarms.AlarmChange) - for k, v in six.iteritems(ALARM_HISTORY[i]): - self.assertEqual(getattr(change, k), v) - - def test_get_all_history(self): - url = '/v2/alarms/alarm-id/history' - self._do_test_get_history(None, url) - - def test_get_constrained_history(self): - q = [dict(field='timestamp', value='NOW')] - url = ('/v2/alarms/alarm-id/history?q.field=timestamp' - '&q.op=&q.type=&q.value=NOW') - self._do_test_get_history(q, url) - - -class AlarmLegacyManagerTest(testtools.TestCase): - - def setUp(self): - super(AlarmLegacyManagerTest, self).setUp() - 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, - ] - self.http_client.assert_called(*expect) - self.assertIsNotNone(alarm) - - def test_create_counter_name(self): - create = {} - create.update(CREATE_LEGACY_ALARM) - create['counter_name'] = CREATE_LEGACY_ALARM['meter_name'] - del create['meter_name'] - alarm = self.mgr.create(**create) - expect = [ - 'POST', '/v2/alarms', CREATE_ALARM_WITHOUT_TC, - ] - 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_put = [ - 'PUT', '/v2/alarms/alarm-id', UPDATED_ALARM - ] - self.http_client.assert_called(*expect_put) - self.assertIsNotNone(alarm) - self.assertEqual('alarm-id', alarm.alarm_id) - for (key, value) in six.iteritems(UPDATED_ALARM): - self.assertEqual(getattr(alarm, key), value) - - def test_update_counter_name(self): - updated = {} - updated.update(UPDATE_LEGACY_ALARM) - updated['counter_name'] = UPDATED_LEGACY_ALARM['meter_name'] - del updated['meter_name'] - alarm = self.mgr.update(alarm_id='alarm-id', **updated) - expect_put = [ - 'PUT', '/v2/alarms/alarm-id', UPDATED_ALARM - ] - self.http_client.assert_called(*expect_put) - self.assertIsNotNone(alarm) - self.assertEqual('alarm-id', alarm.alarm_id) - for (key, value) in six.iteritems(UPDATED_ALARM): - self.assertEqual(getattr(alarm, key), value) - - -class AlarmTimeConstraintTest(testtools.TestCase): - - def setUp(self): - super(AlarmTimeConstraintTest, self).setUp() - 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): - new_constraint = dict(name='cons3', - start='0 0 * * *', - duration=500) - kwargs = dict(time_constraints=[new_constraint]) - self.mgr.update(alarm_id='alarm-id', **kwargs) - 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) - 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_update_time_constraint_no_name(self): - updated_constraint = { - 'start': '0 23 * * *', - 'duration': 500 - } - kwargs = dict(time_constraints=[updated_constraint]) - self.mgr.update(alarm_id='alarm-id', **kwargs) - body = copy.deepcopy(AN_ALARM) - body[u'time_constraints'].append({ - 'start': '0 23 * * *', - 'duration': 500, - }) - 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) - 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/unit/v2/test_capabilities.py b/ceilometerclient/tests/unit/v2/test_capabilities.py deleted file mode 100644 index 884a34f..0000000 --- a/ceilometerclient/tests/unit/v2/test_capabilities.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright 2014 Huawei Technologies Co., Ltd. -# -# 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 testtools - -from ceilometerclient.apiclient import client -from ceilometerclient.apiclient import fake_client -from ceilometerclient.v2 import capabilities - - -CAPABILITIES = { - "alarm_storage": { - "storage:production_ready": True - }, - "api": { - "alarms:query:complex": True, - "alarms:query:simple": True - }, - "event_storage": { - "storage:production_ready": True - }, - "storage": { - "storage:production_ready": True - }, -} - -FIXTURES = { - '/v2/capabilities': { - 'GET': ( - {}, - CAPABILITIES - ), - }, -} - - -class CapabilitiesManagerTest(testtools.TestCase): - def setUp(self): - super(CapabilitiesManagerTest, self).setUp() - self.http_client = fake_client.FakeHTTPClient(fixtures=FIXTURES) - self.api = client.BaseClient(self.http_client) - self.mgr = capabilities.CapabilitiesManager(self.api) - - def test_capabilities_get(self): - capabilities = self.mgr.get() - self.http_client.assert_called('GET', '/v2/capabilities') - self.assertTrue(capabilities.api['alarms:query:complex']) diff --git a/ceilometerclient/tests/unit/v2/test_event_types.py b/ceilometerclient/tests/unit/v2/test_event_types.py deleted file mode 100644 index 3bccbae..0000000 --- a/ceilometerclient/tests/unit/v2/test_event_types.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2014 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -from ceilometerclient.apiclient import client -from ceilometerclient.apiclient import fake_client -from ceilometerclient.tests.unit import utils -import ceilometerclient.v2.event_types - - -fixtures = { - '/v2/event_types': { - 'GET': ( - {}, - ['Foo', 'Bar', 'Sna', 'Fu'] - ), - } -} - - -class EventTypesManagerTest(utils.BaseTestCase): - - def setUp(self): - super(EventTypesManagerTest, self).setUp() - 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' - ] - self.http_client.assert_called(*expect) - self.assertEqual(4, len(event_types)) - self.assertEqual("Foo", event_types[0].event_type) - self.assertEqual("Bar", event_types[1].event_type) - self.assertEqual("Sna", event_types[2].event_type) - self.assertEqual("Fu", event_types[3].event_type) diff --git a/ceilometerclient/tests/unit/v2/test_events.py b/ceilometerclient/tests/unit/v2/test_events.py deleted file mode 100644 index 9cc94fd..0000000 --- a/ceilometerclient/tests/unit/v2/test_events.py +++ /dev/null @@ -1,198 +0,0 @@ -# Copyright 2014 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -from ceilometerclient.apiclient import client -from ceilometerclient.apiclient import fake_client -from ceilometerclient.tests.unit import utils -import ceilometerclient.v2.events - - -fixtures = { - '/v2/events': { - 'GET': ( - {}, - [ - { - 'message_id': '1', - 'event_type': 'Foo', - 'generated': '1970-01-01T00:00:00', - 'traits': {'trait_A': 'abc'}, - }, - { - 'message_id': '2', - 'event_type': 'Foo', - 'generated': '1970-01-01T00:00:00', - 'traits': {'trait_A': 'def'}, - }, - { - 'message_id': '3', - 'event_type': 'Bar', - 'generated': '1970-01-01T00:00:00', - 'traits': {'trait_B': 'bartrait'}, - }, - ] - ), - }, - '/v2/events?q.field=hostname&q.op=&q.type=string&q.value=localhost': - { - 'GET': ( - {}, - [ - { - 'message_id': '1', - 'event_type': 'Foo', - 'generated': '1970-01-01T00:00:00', - 'traits': {'trait_A': 'abc', - 'hostname': 'localhost'}, - }, - { - 'message_id': '2', - 'event_type': 'Foo', - 'generated': '1970-01-01T00:00:00', - 'traits': {'trait_A': 'def', - 'hostname': 'localhost'}, - } - ] - ), - }, - '/v2/events?q.field=hostname&q.op=&q.type=&q.value=foreignhost': - { - 'GET': ( - {}, - [ - { - 'message_id': '1', - 'event_type': 'Foo', - 'generated': '1970-01-01T00:00:00', - 'traits': {'trait_A': 'abc', - 'hostname': 'foreignhost'}, - }, - { - 'message_id': '2', - 'event_type': 'Foo', - 'generated': '1970-01-01T00:00:00', - 'traits': {'trait_A': 'def', - 'hostname': 'foreignhost'}, - } - ] - ), - }, - '/v2/events?q.field=hostname&q.field=num_cpus&q.op=&q.op=' - '&q.type=&q.type=integer&q.value=localhost&q.value=5': - { - 'GET': ( - {}, - [ - { - 'message_id': '1', - 'event_type': 'Bar', - 'generated': '1970-01-01T00:00:00', - 'traits': {'trait_A': 'abc', - 'hostname': 'localhost', - 'num_cpus': '5'}, - }, - ] - ), - }, - - '/v2/events/2': - { - 'GET': ( - {}, - { - 'message_id': '2', - 'event_type': 'Foo', - 'generated': '1970-01-01T00:00:00', - 'traits': {'trait_A': 'def', - 'intTrait': '42'}, - } - ), - }, -} - - -class EventManagerTest(utils.BaseTestCase): - - def setUp(self): - super(EventManagerTest, self).setUp() - 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' - ] - self.http_client.assert_called(*expect) - self.assertEqual(3, len(events)) - self.assertEqual('Foo', events[0].event_type) - self.assertEqual('Foo', events[1].event_type) - self.assertEqual('Bar', events[2].event_type) - - def test_list_one(self): - event = self.mgr.get(2) - expect = [ - 'GET', '/v2/events/2' - ] - self.http_client.assert_called(*expect) - self.assertIsNotNone(event) - self.assertEqual('Foo', event.event_type) - - def test_list_with_query(self): - events = list(self.mgr.list(q=[{"field": "hostname", - "value": "localhost", - "type": "string"}])) - expect = [ - 'GET', '/v2/events?q.field=hostname&q.op=&q.type=string' - '&q.value=localhost' - ] - self.http_client.assert_called(*expect) - self.assertEqual(2, len(events)) - self.assertEqual('Foo', events[0].event_type) - - def test_list_with_query_no_type(self): - events = list(self.mgr.list(q=[{"field": "hostname", - "value": "foreignhost"}])) - expect = [ - 'GET', '/v2/events?q.field=hostname&q.op=' - '&q.type=&q.value=foreignhost' - ] - self.http_client.assert_called(*expect) - self.assertEqual(2, len(events)) - self.assertEqual('Foo', events[0].event_type) - - def test_list_with_multiple_filters(self): - events = list(self.mgr.list(q=[{"field": "hostname", - "value": "localhost"}, - {"field": "num_cpus", - "value": "5", - "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' - ] - self.http_client.assert_called(*expect) - self.assertEqual(1, len(events)) - - def test_get_from_event_class(self): - event = self.mgr.get(2) - self.assertIsNotNone(event) - event.get() - expect = [ - 'GET', '/v2/events/2' - ] - 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/unit/v2/test_options.py b/ceilometerclient/tests/unit/v2/test_options.py deleted file mode 100644 index 8de17d4..0000000 --- a/ceilometerclient/tests/unit/v2/test_options.py +++ /dev/null @@ -1,253 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -from ceilometerclient.tests.unit import utils -from ceilometerclient.v2 import options - - -class BuildUrlTest(utils.BaseTestCase): - - def test_one(self): - url = options.build_url('/', [{'field': 'this', - 'op': 'gt', - 'value': 43}]) - self.assertEqual(url, '/?q.field=this&q.op=gt&q.type=&q.value=43') - - def test_two(self): - url = options.build_url('/', [{'field': 'this', - 'op': 'gt', - 'value': 43}, - {'field': 'that', - 'op': 'lt', - 'value': 88}]) - ops = 'q.op=gt&q.op=lt' - vals = 'q.value=43&q.value=88' - types = 'q.type=&q.type=' - fields = 'q.field=this&q.field=that' - self.assertEqual(url, '/?%s&%s&%s&%s' % (fields, ops, types, vals)) - - def test_default_op(self): - url = options.build_url('/', [{'field': 'this', - 'value': 43}]) - self.assertEqual(url, '/?q.field=this&q.op=&q.type=&q.value=43') - - def test_one_param(self): - url = options.build_url('/', None, ['period=60']) - self.assertEqual(url, '/?period=60') - - def test_two_params(self): - url = options.build_url('/', None, ['period=60', - 'others=value']) - self.assertEqual(url, '/?period=60&others=value') - - def test_with_data_type(self): - url = options.build_url('/', [{'field': 'f1', - 'value': '10', - 'type': 'integer'}]) - - self.assertEqual('/?q.field=f1&q.op=&q.type=integer&q.value=10', url) - - -class CliTest(utils.BaseTestCase): - - def test_one(self): - ar = options.cli_to_array('this<=34') - self.assertEqual(ar, [{'field': 'this', 'op': 'le', - 'value': '34', 'type': ''}]) - - def test_two(self): - ar = options.cli_to_array('this<=34;that!=foo') - self.assertEqual(ar, [{'field': 'this', 'op': 'le', - 'value': '34', 'type': ''}, - {'field': 'that', 'op': 'ne', - 'value': 'foo', 'type': ''}]) - - def test_negative(self): - ar = options.cli_to_array('this>=-783') - self.assertEqual(ar, [{'field': 'this', 'op': 'ge', - 'value': '-783', 'type': ''}]) - - def test_float(self): - ar = options.cli_to_array('this<=283.347') - self.assertEqual(ar, [{'field': 'this', - 'op': 'le', 'value': '283.347', - 'type': ''}]) - - def test_comma(self): - ar = options.cli_to_array('this=2.4,fooo=doof') - self.assertEqual([{'field': 'this', - 'op': 'eq', - 'value': '2.4,fooo=doof', - 'type': ''}], - ar) - - def test_special_character(self): - ar = options.cli_to_array('key~123=value!123') - self.assertEqual([{'field': 'key~123', - 'op': 'eq', - 'value': 'value!123', - 'type': ''}], - ar) - - def _do_test_typed_float_op(self, op, op_str): - ar = options.cli_to_array('that%sfloat::283.347' % op) - self.assertEqual([{'field': 'that', - 'type': 'float', - 'value': '283.347', - 'op': op_str}], - ar) - - def test_typed_float_eq(self): - self._do_test_typed_float_op('<', 'lt') - - def test_typed_float_le(self): - self._do_test_typed_float_op('<=', 'le') - - def test_typed_string_whitespace(self): - ar = options.cli_to_array('state=string::insufficient data') - self.assertEqual([{'field': 'state', - 'op': 'eq', - 'type': 'string', - 'value': 'insufficient data'}], - ar) - - def test_typed_string_whitespace_complex(self): - ar = options.cli_to_array( - 'that>=float::99.9999;state=string::insufficient data' - ) - self.assertEqual([{'field': 'that', - 'op': 'ge', - 'type': 'float', - 'value': '99.9999'}, - {'field': 'state', - 'op': 'eq', - 'type': 'string', - 'value': 'insufficient data'}], - ar) - - def test_invalid_operator(self): - self.assertRaises(ValueError, options.cli_to_array, - 'this=2.4;fooo-doof') - - def test_with_dot(self): - ar = options.cli_to_array('metadata.this<=34') - self.assertEqual(ar, [{'field': 'metadata.this', - 'op': 'le', 'value': '34', - 'type': ''}]) - - def test_single_char_field_or_value(self): - ar = options.cli_to_array('m<=34;large.thing>s;x!=y') - self.assertEqual([{'field': 'm', - 'op': 'le', - 'value': '34', - 'type': ''}, - {'field': 'large.thing', - 'op': 'gt', - 'value': 's', - 'type': ''}, - {'field': 'x', - 'op': 'ne', - 'value': 'y', - 'type': ''}], - ar) - - def test_without_data_type(self): - ar = options.cli_to_array('hostname=localhost') - self.assertEqual(ar, [{'field': 'hostname', - 'op': 'eq', - 'value': 'localhost', - 'type': ''}]) - - def test_with_string_data_type(self): - ar = options.cli_to_array('hostname=string::localhost') - self.assertEqual(ar, [{'field': 'hostname', - 'op': 'eq', - 'type': 'string', - 'value': 'localhost'}]) - - def test_with_int_data_type(self): - ar = options.cli_to_array('port=integer::1234') - self.assertEqual(ar, [{'field': 'port', - 'op': 'eq', - 'type': 'integer', - 'value': '1234'}]) - - def test_with_bool_data_type(self): - ar = options.cli_to_array('port=boolean::true') - self.assertEqual(ar, [{'field': 'port', - 'op': 'eq', - 'type': 'boolean', - 'value': 'true'}]) - - def test_with_float_data_type(self): - ar = options.cli_to_array('average=float::1234.5678') - self.assertEqual(ar, [{'field': 'average', - 'op': 'eq', - 'type': 'float', - 'value': '1234.5678'}]) - - def test_with_datetime_data_type(self): - ar = options.cli_to_array('timestamp=datetime::sometimestamp') - self.assertEqual(ar, [{'field': 'timestamp', - 'op': 'eq', - 'type': 'datetime', - 'value': 'sometimestamp'}]) - - def test_with_incorrect_type(self): - ar = options.cli_to_array('timestamp=invalid::sometimestamp') - self.assertEqual(ar, [{'field': 'timestamp', - 'op': 'eq', - 'type': '', - 'value': 'invalid::sometimestamp'}]) - - def test_with_single_colon(self): - ar = options.cli_to_array('timestamp=datetime:sometimestamp') - self.assertEqual(ar, [{'field': 'timestamp', - 'op': 'eq', - 'type': '', - 'value': 'datetime:sometimestamp'}]) - - def test_missing_key(self): - self.assertRaises(ValueError, options.cli_to_array, - 'average=float::1234.0;>=string::hello') - - def test_missing_value(self): - self.assertRaises(ValueError, options.cli_to_array, - 'average=float::1234.0;house>=') - - def test_timestamp_value(self): - ar = options.cli_to_array( - 'project=cow;timestamp>=datetime::2014-03-11T16:02:58' - ) - self.assertEqual([{'field': 'project', - 'op': 'eq', - 'type': '', - 'value': 'cow'}, - {'field': 'timestamp', - 'op': 'ge', - 'type': 'datetime', - 'value': '2014-03-11T16:02:58'}], - ar) - - def test_with_whitespace(self): - ar = options.cli_to_array('start_timestamp= 2015-01-01T00:00:00;' - ' end_timestamp =2015-06-20T14:01:59 ') - - self.assertEqual([{'field': 'start_timestamp', - 'op': 'eq', - 'type': '', - 'value': '2015-01-01T00:00:00'}, - {'field': 'end_timestamp', - 'op': 'eq', - 'type': '', - 'value': '2015-06-20T14:01:59'}], - ar) diff --git a/ceilometerclient/tests/unit/v2/test_query_alarm_history.py b/ceilometerclient/tests/unit/v2/test_query_alarm_history.py deleted file mode 100644 index b37eb5d..0000000 --- a/ceilometerclient/tests/unit/v2/test_query_alarm_history.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright Ericsson AB 2014. All rights reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from ceilometerclient.apiclient import client -from ceilometerclient.apiclient import fake_client -from ceilometerclient.tests.unit import utils -from ceilometerclient.v2 import query - - -ALARMCHANGE = {"alarm_id": "e8ff32f772a44a478182c3fe1f7cad6a", - "event_id": "c74a8611-6553-4764-a860-c15a6aabb5d0", - "detail": "{\"threshold\": 42.0, \"evaluation_periods\": 4}", - "on_behalf_of": "92159030020611e3b26dde429e99ee8c", - "project_id": "b6f16144010811e387e4de429e99ee8c", - "timestamp": "2014-03-11T16:02:58.376261", - "type": "rule change", - "user_id": "3e5d11fda79448ac99ccefb20be187ca" - } - -QUERY = {"filter": {"and": [{">": {"timestamp": "2014-03-11T16:02:58"}}, - {"=": {"type": "rule change"}}]}, - "orderby": [{"timestamp": "desc"}], - "limit": 10} - -base_url = '/v2/query/alarms/history' -fixtures = { - base_url: - { - 'POST': ( - {}, - [ALARMCHANGE], - ), - }, -} - - -class QueryAlarmsManagerTest(utils.BaseTestCase): - - def setUp(self): - super(QueryAlarmsManagerTest, self).setUp() - 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, - - ] - self.http_client.assert_called(*expect) - self.assertEqual(1, len(alarm_history)) diff --git a/ceilometerclient/tests/unit/v2/test_query_alarms.py b/ceilometerclient/tests/unit/v2/test_query_alarms.py deleted file mode 100644 index 495bbd4..0000000 --- a/ceilometerclient/tests/unit/v2/test_query_alarms.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright Ericsson AB 2014. All rights reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from ceilometerclient.apiclient import client -from ceilometerclient.apiclient import fake_client -from ceilometerclient.tests.unit import utils -from ceilometerclient.v2 import query - - -ALARM = {"alarm_actions": ["http://site:8000/alarm"], - "alarm_id": None, - "combination_rule": { - "alarm_ids": [ - "739e99cb-c2ec-4718-b900-332502355f38", - "153462d0-a9b8-4b5b-8175-9e4b05e9b856"], - "operator": "or"}, - "description": "An alarm", - "enabled": True, - "insufficient_data_actions": ["http://site:8000/nodata"], - "name": "SwiftObjectAlarm", - "ok_actions": ["http://site:8000/ok"], - "project_id": "c96c887c216949acbdfbd8b494863567", - "repeat_actions": False, - "state": "ok", - "state_timestamp": "2014-02-20T10:37:15.589860", - "threshold_rule": None, - "timestamp": "2014-02-20T10:37:15.589856", - "type": "combination", - "user_id": "c96c887c216949acbdfbd8b494863567"} - -QUERY = {"filter": {"and": [{"!=": {"state": "ok"}}, - {"=": {"type": "combination"}}]}, - "orderby": [{"state_timestamp": "desc"}], - "limit": 10} - -base_url = '/v2/query/alarms' -fixtures = { - base_url: - { - 'POST': ( - {}, - [ALARM], - ), - }, -} - - -class QueryAlarmsManagerTest(utils.BaseTestCase): - - def setUp(self): - super(QueryAlarmsManagerTest, self).setUp() - 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, - ] - - self.http_client.assert_called(*expect) - self.assertEqual(1, len(alarms)) diff --git a/ceilometerclient/tests/unit/v2/test_query_samples.py b/ceilometerclient/tests/unit/v2/test_query_samples.py deleted file mode 100644 index 217c35a..0000000 --- a/ceilometerclient/tests/unit/v2/test_query_samples.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright Ericsson AB 2014. All rights reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from ceilometerclient.apiclient import client -from ceilometerclient.apiclient import fake_client -from ceilometerclient.tests.unit import utils -from ceilometerclient.v2 import query - - -SAMPLE = {u'id': u'b55d1526-9929-11e3-a3f6-02163e5df1e6', - u'metadata': { - u'name1': u'value1', - u'name2': u'value2'}, - u'meter': 'instance', - u'project_id': u'35b17138-b364-4e6a-a131-8f3099c5be68', - u'resource_id': u'bd9431c1-8d69-4ad3-803a-8d4a6b89fd36', - u'source': u'openstack', - u'timestamp': u'2014-02-19T05:50:16.673604', - u'type': u'gauge', - u'unit': u'instance', - u'volume': 1, - u'user_id': 'efd87807-12d2-4b38-9c70-5f5c2ac427ff'} - -QUERY = {"filter": {"and": [{"=": {"source": "openstack"}}, - {">": {"timestamp": "2014-02-19T05:50:16"}}]}, - "orderby": [{"timestamp": "desc"}, {"volume": "asc"}], - "limit": 10} - -base_url = '/v2/query/samples' -fixtures = { - base_url: - { - 'POST': ( - {}, - [SAMPLE], - ), - }, -} - - -class QuerySamplesManagerTest(utils.BaseTestCase): - - def setUp(self): - super(QuerySamplesManagerTest, self).setUp() - 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, - ] - self.http_client.assert_called(*expect) - self.assertEqual(1, len(samples)) diff --git a/ceilometerclient/tests/unit/v2/test_resources.py b/ceilometerclient/tests/unit/v2/test_resources.py deleted file mode 100644 index ca88aa5..0000000 --- a/ceilometerclient/tests/unit/v2/test_resources.py +++ /dev/null @@ -1,147 +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. -from ceilometerclient.apiclient import client -from ceilometerclient.apiclient import fake_client -from ceilometerclient.tests.unit import utils -import ceilometerclient.v2.resources - - -fixtures = { - '/v2/resources?meter_links=0': { - 'GET': ( - {}, - [ - { - 'resource_id': 'a', - 'project_id': 'project_bla', - 'user_id': 'freddy', - 'metadata': {'zxc_id': 'bla'}, - }, - { - 'resource_id': 'b', - 'project_id': 'dig_the_ditch', - 'user_id': 'joey', - 'metadata': {'zxc_id': 'foo'}, - }, - ] - ), - }, - '/v2/resources?q.field=resource_id&q.op=&q.type=&q.value=a&meter_links=0': - { - 'GET': ( - {}, - [ - { - 'resource_id': 'a', - 'project_id': 'project_bla', - 'user_id': 'freddy', - 'metadata': {'zxc_id': 'bla'}, - }, - ] - ), - }, - '/v2/resources?meter_links=1': { - 'GET': ( - {}, - [ - { - 'resource_id': 'c', - 'project_id': 'project_blah', - 'user_id': 'fred', - 'metadata': {'zxc_id': 'blah'}, - }, - { - 'resource_id': 'd', - 'project_id': 'bury_the_ditch', - 'user_id': 'jack', - 'metadata': {'zxc_id': 'foobar'}, - }, - ] - ), - }, - '/v2/resources/a': - { - 'GET': ( - {}, - { - 'resource_id': 'a', - 'project_id': 'project_bla', - 'user_id': 'freddy', - 'metadata': {'zxc_id': 'bla'}, - }, - ), - }, -} - - -class ResourceManagerTest(utils.BaseTestCase): - - def setUp(self): - super(ResourceManagerTest, self).setUp() - 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?meter_links=0' - ] - self.http_client.assert_called(*expect) - self.assertEqual(2, len(resources)) - self.assertEqual('a', resources[0].resource_id) - self.assertEqual('b', resources[1].resource_id) - - def test_list_all_with_links_enabled(self): - resources = list(self.mgr.list(links=True)) - expect = [ - 'GET', '/v2/resources?meter_links=1' - ] - self.http_client.assert_called(*expect) - self.assertEqual(2, len(resources)) - self.assertEqual('c', resources[0].resource_id) - self.assertEqual('d', resources[1].resource_id) - - def test_list_one(self): - resource = self.mgr.get(resource_id='a') - expect = [ - 'GET', '/v2/resources/a' - ] - self.http_client.assert_called(*expect) - self.assertIsNotNone(resource) - self.assertEqual('a', resource.resource_id) - - def test_list_by_query(self): - resources = list(self.mgr.list(q=[{"field": "resource_id", - "value": "a"}, - ])) - expect = [ - 'GET', '/v2/resources?q.field=resource_id&q.op=' - '&q.type=&q.value=a&meter_links=0' - ] - self.http_client.assert_called(*expect) - self.assertEqual(1, len(resources)) - self.assertEqual('a', resources[0].resource_id) - - def test_get_from_resource_class(self): - resource = self.mgr.get(resource_id='a') - self.assertIsNotNone(resource) - resource.get() - expect = [ - 'GET', '/v2/resources/a' - ] - 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/unit/v2/test_samples.py b/ceilometerclient/tests/unit/v2/test_samples.py deleted file mode 100644 index 864ac36..0000000 --- a/ceilometerclient/tests/unit/v2/test_samples.py +++ /dev/null @@ -1,247 +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 - -from ceilometerclient.apiclient import client -from ceilometerclient.apiclient import fake_client -from ceilometerclient.tests.unit import utils -import ceilometerclient.v2.samples - -GET_OLD_SAMPLE = {u'counter_name': u'instance', - u'user_id': u'user-id', - u'resource_id': u'resource-id', - u'timestamp': u'2012-07-02T10:40:00', - u'source': u'test_source', - u'message_id': u'54558a1c-6ef3-11e2-9875-5453ed1bbb5f', - u'counter_unit': u'', - u'counter_volume': 1.0, - u'project_id': u'project1', - u'resource_metadata': {u'tag': u'self.counter', - u'display_name': u'test-server'}, - u'counter_type': u'cumulative'} -CREATE_SAMPLE = copy.deepcopy(GET_OLD_SAMPLE) -del CREATE_SAMPLE['message_id'] -del CREATE_SAMPLE['source'] -CREATE_LIST_SAMPLE = copy.deepcopy(CREATE_SAMPLE) -CREATE_LIST_SAMPLE['counter_name'] = 'image' - -GET_SAMPLE = { - "user_id": None, - "resource_id": "9b651dfd-7d30-402b-972e-212b2c4bfb05", - "timestamp": "2014-11-03T13:37:46", - "meter": "image", - "volume": 1.0, - "source": "openstack", - "recorded_at": "2014-11-03T13:37:46.994458", - "project_id": "2cc3a7bb859b4bacbeab0aa9ca673033", - "type": "gauge", - "id": "98b5f258-635e-11e4-8bdd-0025647390c1", - "unit": "image", - "resource_metadata": {}, -} - -METER_URL = '/v2/meters/instance' -METER_URL_DIRECT = '/v2/meters/instance?direct=True' -SECOND_METER_URL = '/v2/meters/image' -SECOND_METER_URL_DIRECT = '/v2/meters/image?direct=True' -SAMPLE_URL = '/v2/samples' -QUERIES = ('q.field=resource_id&q.field=source&q.op=&q.op=' - '&q.type=&q.type=&q.value=foo&q.value=bar') -LIMIT = 'limit=1' - -OLD_SAMPLE_FIXTURES = { - METER_URL: { - 'GET': ( - {}, - [GET_OLD_SAMPLE] - ), - 'POST': ( - {}, - [CREATE_SAMPLE], - ), - }, - METER_URL_DIRECT: { - 'POST': ( - {}, - [CREATE_SAMPLE], - ) - }, - SECOND_METER_URL: { - 'POST': ( - {}, - [CREATE_LIST_SAMPLE] * 10, - ), - }, - SECOND_METER_URL_DIRECT: { - 'POST': ( - {}, - [CREATE_LIST_SAMPLE] * 10, - ) - }, - '%s?%s' % (METER_URL, QUERIES): { - 'GET': ( - {}, - [], - ), - }, - '%s?%s' % (METER_URL, LIMIT): { - 'GET': ( - {}, - [GET_OLD_SAMPLE] - ), - } -} -SAMPLE_FIXTURES = { - SAMPLE_URL: { - 'GET': ( - (), - [GET_SAMPLE] - ), - }, - '%s?%s' % (SAMPLE_URL, QUERIES): { - 'GET': ( - {}, - [], - ), - }, - '%s?%s' % (SAMPLE_URL, LIMIT): { - 'GET': ( - {}, - [GET_SAMPLE], - ), - }, - '%s/%s' % (SAMPLE_URL, GET_SAMPLE['id']): { - 'GET': ( - {}, - GET_SAMPLE, - ), - }, -} - - -class OldSampleManagerTest(utils.BaseTestCase): - - def setUp(self): - super(OldSampleManagerTest, self).setUp() - self.http_client = fake_client.FakeHTTPClient( - fixtures=OLD_SAMPLE_FIXTURES) - self.api = client.BaseClient(self.http_client) - self.mgr = ceilometerclient.v2.samples.OldSampleManager(self.api) - - def test_list_by_meter_name(self): - samples = list(self.mgr.list(meter_name='instance')) - expect = [ - 'GET', '/v2/meters/instance' - ] - self.http_client.assert_called(*expect) - self.assertEqual(1, len(samples)) - self.assertEqual('resource-id', samples[0].resource_id) - - def test_list_by_meter_name_extended(self): - samples = list(self.mgr.list(meter_name='instance', - q=[ - {"field": "resource_id", - "value": "foo"}, - {"field": "source", - "value": "bar"}, - ])) - expect = ['GET', '%s?%s' % (METER_URL, QUERIES)] - self.http_client.assert_called(*expect) - self.assertEqual(0, len(samples)) - - def test_create(self): - sample = self.mgr.create(**CREATE_SAMPLE) - expect = [ - 'POST', '/v2/meters/instance' - ] - self.http_client.assert_called(*expect, body=[CREATE_SAMPLE]) - self.assertIsNotNone(sample) - - def test_create_directly(self): - sample = self.mgr.create(direct=True, **CREATE_SAMPLE) - expect = [ - 'POST', '/v2/meters/instance?direct=True' - ] - self.http_client.assert_called(*expect, body=[CREATE_SAMPLE]) - self.assertIsNotNone(sample) - - def test_create_list(self): - test_samples = [CREATE_LIST_SAMPLE] * 10 - samples = self.mgr.create_list(test_samples) - expect = [ - 'POST', '/v2/meters/image' - ] - self.http_client.assert_called(*expect, body=test_samples) - self.assertEqual(10, len(samples)) - - def test_create_list_directly(self): - test_samples = [CREATE_LIST_SAMPLE] * 10 - samples = self.mgr.create_list(test_samples, direct=True) - expect = [ - 'POST', '/v2/meters/image?direct=True' - ] - self.http_client.assert_called(*expect, body=test_samples) - self.assertEqual(10, len(samples)) - - def test_limit(self): - samples = list(self.mgr.list(meter_name='instance', limit=1)) - expect = ['GET', '/v2/meters/instance?limit=1'] - self.http_client.assert_called(*expect) - self.assertEqual(1, len(samples)) - - -class SampleManagerTest(utils.BaseTestCase): - - def setUp(self): - super(SampleManagerTest, self).setUp() - self.http_client = fake_client.FakeHTTPClient( - fixtures=SAMPLE_FIXTURES) - self.api = client.BaseClient(self.http_client) - self.mgr = ceilometerclient.v2.samples.SampleManager(self.api) - - def test_sample_list(self): - samples = list(self.mgr.list()) - expect = [ - 'GET', '/v2/samples' - ] - self.http_client.assert_called(*expect) - self.assertEqual(1, len(samples)) - self.assertEqual('9b651dfd-7d30-402b-972e-212b2c4bfb05', - samples[0].resource_id) - - def test_sample_list_with_queries(self): - queries = [ - {"field": "resource_id", - "value": "foo"}, - {"field": "source", - "value": "bar"}, - ] - samples = list(self.mgr.list(q=queries)) - expect = ['GET', '%s?%s' % (SAMPLE_URL, QUERIES)] - self.http_client.assert_called(*expect) - self.assertEqual(0, len(samples)) - - def test_sample_list_with_limit(self): - samples = list(self.mgr.list(limit=1)) - expect = ['GET', '/v2/samples?limit=1'] - self.http_client.assert_called(*expect) - self.assertEqual(1, len(samples)) - - def test_sample_get(self): - sample = self.mgr.get(GET_SAMPLE['id']) - expect = ['GET', '/v2/samples/' + GET_SAMPLE['id']] - self.http_client.assert_called(*expect) - self.assertEqual(GET_SAMPLE, sample.to_dict()) diff --git a/ceilometerclient/tests/unit/v2/test_shell.py b/ceilometerclient/tests/unit/v2/test_shell.py deleted file mode 100644 index eca15e3..0000000 --- a/ceilometerclient/tests/unit/v2/test_shell.py +++ /dev/null @@ -1,1977 +0,0 @@ -# Copyright Ericsson AB 2014. 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 json -import re -import sys - -import mock -import six -from testtools import matchers - -from ceilometerclient import exc -from ceilometerclient import shell as base_shell -from ceilometerclient.tests.unit import test_shell -from ceilometerclient.tests.unit import utils -from ceilometerclient.v2 import alarms -from ceilometerclient.v2 import capabilities -from ceilometerclient.v2 import event_types -from ceilometerclient.v2 import events -from ceilometerclient.v2 import meters -from ceilometerclient.v2 import resources -from ceilometerclient.v2 import samples -from ceilometerclient.v2 import shell as ceilometer_shell -from ceilometerclient.v2 import statistics -from ceilometerclient.v2 import trait_descriptions -from ceilometerclient.v2 import traits -from keystoneauth1 import exceptions - - -class ShellAlarmStateCommandsTest(utils.BaseTestCase): - - ALARM_ID = 'foobar' - - def setUp(self): - super(ShellAlarmStateCommandsTest, self).setUp() - self.cc = mock.Mock() - self.cc.alarms = mock.Mock() - self.args = mock.Mock() - self.args.alarm_id = self.ALARM_ID - - def test_alarm_state_get(self): - ceilometer_shell.do_alarm_state_get(self.cc, self.args) - self.cc.alarms.get_state.assert_called_once_with(self.ALARM_ID) - self.assertFalse(self.cc.alarms.set_state.called) - - def test_alarm_state_set(self): - self.args.state = 'ok' - ceilometer_shell.do_alarm_state_set(self.cc, self.args) - self.cc.alarms.set_state.assert_called_once_with(self.ALARM_ID, 'ok') - self.assertFalse(self.cc.alarms.get_state.called) - - -class ShellAlarmHistoryCommandTest(utils.BaseTestCase): - - ALARM_ID = '768ff714-8cfb-4db9-9753-d484cb33a1cc' - FULL_DETAIL = ('{"alarm_actions": [], ' - '"user_id": "8185aa72421a4fd396d4122cba50e1b5", ' - '"name": "scombo", ' - '"timestamp": "2013-10-03T08:58:33.647912", ' - '"enabled": true, ' - '"state_timestamp": "2013-10-03T08:58:33.647912", ' - '"rule": {"operator": "or", "alarm_ids": ' - '["062cc907-3a9f-4867-ab3b-fa83212b39f7"]}, ' - '"alarm_id": "768ff714-8cfb-4db9-9753-d484cb33a1cc", ' - '"state": "insufficient data", ' - '"insufficient_data_actions": [], ' - '"repeat_actions": false, ' - '"ok_actions": [], ' - '"project_id": "57d04f24d0824b78b1ea9bcecedbda8f", ' - '"type": "combination", ' - '"severity": "low", ' - '"description": "Combined state of alarms ' - '062cc907-3a9f-4867-ab3b-fa83212b39f7"}') - ALARM_HISTORY = [{'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f', - 'user_id': '8185aa72421a4fd396d4122cba50e1b5', - 'event_id': 'c74a8611-6553-4764-a860-c15a6aabb5d0', - 'timestamp': '2013-10-03T08:59:28.326000', - 'detail': '{"state": "alarm"}', - 'alarm_id': '768ff714-8cfb-4db9-9753-d484cb33a1cc', - 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f', - 'type': 'state transition'}, - {'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f', - 'user_id': '8185aa72421a4fd396d4122cba50e1b5', - 'event_id': 'c74a8611-6553-4764-a860-c15a6aabb5d0', - 'timestamp': '2013-10-03T08:59:28.326000', - 'detail': '{"description": "combination of one"}', - 'alarm_id': '768ff714-8cfb-4db9-9753-d484cb33a1cc', - 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f', - 'type': 'rule change'}, - {'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f', - 'user_id': '8185aa72421a4fd396d4122cba50e1b5', - 'event_id': '4fd7df9e-190d-4471-8884-dc5a33d5d4bb', - 'timestamp': '2013-10-03T08:58:33.647000', - 'detail': FULL_DETAIL, - 'alarm_id': '768ff714-8cfb-4db9-9753-d484cb33a1cc', - 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f', - 'type': 'creation'}] - TIMESTAMP_RE = (' +\| (\d{4})-(\d{2})-(\d{2})T' - '(\d{2})\:(\d{2})\:(\d{2})\.(\d{6}) \| +') - - def setUp(self): - super(ShellAlarmHistoryCommandTest, self).setUp() - self.cc = mock.Mock() - self.cc.alarms = mock.Mock() - self.args = mock.Mock() - self.args.alarm_id = self.ALARM_ID - - @mock.patch('sys.stdout', new=six.StringIO()) - def _do_test_alarm_history(self, raw_query=None, parsed_query=None): - self.args.query = raw_query - history = [alarms.AlarmChange(mock.Mock(), change) - for change in self.ALARM_HISTORY] - self.cc.alarms.get_history.return_value = history - - ceilometer_shell.do_alarm_history(self.cc, self.args) - self.cc.alarms.get_history.assert_called_once_with( - q=parsed_query, - alarm_id=self.ALARM_ID - ) - out = sys.stdout.getvalue() - required = [ - '.*creation%sname: scombo.*' % self.TIMESTAMP_RE, - '.*rule change%sdescription: combination of one.*' % - self.TIMESTAMP_RE, - '.*state transition%sstate: alarm.*' % self.TIMESTAMP_RE, - ] - for r in required: - self.assertThat(out, matchers.MatchesRegex(r, re.DOTALL)) - - def test_alarm_all_history(self): - self._do_test_alarm_history() - - def test_alarm_constrained_history(self): - parsed_query = [dict(field='timestamp', - value='2013-10-03T08:59:28', - op='gt', - type='')] - self._do_test_alarm_history(raw_query='timestamp>2013-10-03T08:59:28', - parsed_query=parsed_query) - - -class ShellAlarmCommandTest(utils.BaseTestCase): - - ALARM_ID = '768ff714-8cfb-4db9-9753-d484cb33a1cc' - ALARM = {"alarm_actions": ["log://"], - "ok_actions": [], - "description": "instance running hot", - "timestamp": "2013-11-20T10:38:42.206952", - "enabled": True, - "state_timestamp": "2013-11-19T17:20:44", - "threshold_rule": {"meter_name": "cpu_util", - "evaluation_periods": 3, - "period": 600, - "statistic": "avg", - "threshold": 99.0, - "query": [{"field": "resource_id", - "value": "INSTANCE_ID", - "op": "eq"}], - "comparison_operator": "gt"}, - "time_constraints": [{"name": "cons1", - "description": "desc1", - "start": "0 11 * * *", - "duration": 300, - "timezone": ""}, - {"name": "cons2", - "description": "desc2", - "start": "0 23 * * *", - "duration": 600, - "timezone": ""}], - "alarm_id": ALARM_ID, - "state": "insufficient data", - "severity": "low", - "insufficient_data_actions": [], - "repeat_actions": True, - "user_id": "528d9b68fa774689834b5c04b4564f8a", - "project_id": "ed9d4e2be2a748bc80108053cf4598f5", - "type": "threshold", - "name": "cpu_high"} - - THRESHOLD_ALARM_CLI_ARGS = [ - '--name', 'cpu_high', - '--description', 'instance running hot', - '--meter-name', 'cpu_util', - '--threshold', '70.0', - '--comparison-operator', 'gt', - '--statistic', 'avg', - '--period', '600', - '--evaluation-periods', '3', - '--alarm-action', 'log://', - '--alarm-action', 'http://example.com/alarm/state', - '--query', 'resource_id=INSTANCE_ID' - ] - - def setUp(self): - super(ShellAlarmCommandTest, self).setUp() - self.cc = mock.Mock() - self.cc.alarms = mock.Mock() - self.args = mock.Mock() - self.args.alarm_id = self.ALARM_ID - - @mock.patch('sys.stdout', new=six.StringIO()) - def _do_test_alarm_update_repeat_actions(self, method, repeat_actions): - self.args.threshold = 42.0 - if repeat_actions is not None: - self.args.repeat_actions = repeat_actions - alarm = [alarms.Alarm(mock.Mock(), self.ALARM)] - self.cc.alarms.get.return_value = alarm - self.cc.alarms.update.return_value = alarm[0] - - method(self.cc, self.args) - args, kwargs = self.cc.alarms.update.call_args - self.assertEqual(self.ALARM_ID, args[0]) - self.assertEqual(42.0, kwargs.get('threshold')) - if repeat_actions is not None: - self.assertEqual(repeat_actions, kwargs.get('repeat_actions')) - else: - self.assertNotIn('repeat_actions', kwargs) - - def test_alarm_update_repeat_actions_untouched(self): - method = ceilometer_shell.do_alarm_update - self._do_test_alarm_update_repeat_actions(method, None) - - def test_alarm_update_repeat_actions_set(self): - method = ceilometer_shell.do_alarm_update - self._do_test_alarm_update_repeat_actions(method, True) - - def test_alarm_update_repeat_actions_clear(self): - method = ceilometer_shell.do_alarm_update - self._do_test_alarm_update_repeat_actions(method, False) - - def test_alarm_combination_update_repeat_actions_untouched(self): - method = ceilometer_shell.do_alarm_combination_update - self._do_test_alarm_update_repeat_actions(method, None) - - def test_alarm_combination_update_repeat_actions_set(self): - method = ceilometer_shell.do_alarm_combination_update - self._do_test_alarm_update_repeat_actions(method, True) - - def test_alarm_combination_update_repeat_actions_clear(self): - method = ceilometer_shell.do_alarm_combination_update - self._do_test_alarm_update_repeat_actions(method, False) - - def test_alarm_threshold_update_repeat_actions_untouched(self): - method = ceilometer_shell.do_alarm_threshold_update - self._do_test_alarm_update_repeat_actions(method, None) - - def test_alarm_threshold_update_repeat_actions_set(self): - method = ceilometer_shell.do_alarm_threshold_update - self._do_test_alarm_update_repeat_actions(method, True) - - def test_alarm_threshold_update_repeat_actions_clear(self): - method = ceilometer_shell.do_alarm_threshold_update - self._do_test_alarm_update_repeat_actions(method, False) - - def test_alarm_event_upadte_repeat_action_untouched(self): - method = ceilometer_shell.do_alarm_event_update - self._do_test_alarm_update_repeat_actions(method, None) - - def test_alarm_event_upadte_repeat_action_set(self): - method = ceilometer_shell.do_alarm_event_update - self._do_test_alarm_update_repeat_actions(method, True) - - def test_alarm_event_upadte_repeat_action_clear(self): - method = ceilometer_shell.do_alarm_event_update - self._do_test_alarm_update_repeat_actions(method, False) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_alarm_threshold_create_args(self): - argv = ['alarm-threshold-create'] + self.THRESHOLD_ALARM_CLI_ARGS - self._test_alarm_threshold_action_args('create', argv) - - def test_alarm_threshold_update_args(self): - argv = ['alarm-threshold-update', 'x'] + self.THRESHOLD_ALARM_CLI_ARGS - self._test_alarm_threshold_action_args('update', argv) - - @mock.patch('sys.stdout', new=six.StringIO()) - def _test_alarm_threshold_action_args(self, action, argv): - shell = base_shell.CeilometerShell() - _, args = shell.parse_args(argv) - - alarm = alarms.Alarm(mock.Mock(), self.ALARM) - getattr(self.cc.alarms, action).return_value = alarm - - func = getattr(ceilometer_shell, 'do_alarm_threshold_' + action) - func(self.cc, args) - _, kwargs = getattr(self.cc.alarms, action).call_args - self._check_alarm_threshold_args(kwargs) - - def _check_alarm_threshold_args(self, kwargs): - self.assertEqual('cpu_high', kwargs.get('name')) - self.assertEqual('instance running hot', kwargs.get('description')) - actions = ['log://', 'http://example.com/alarm/state'] - self.assertEqual(actions, kwargs.get('alarm_actions')) - self.assertIn('threshold_rule', kwargs) - rule = kwargs['threshold_rule'] - self.assertEqual('cpu_util', rule.get('meter_name')) - self.assertEqual(70.0, rule.get('threshold')) - self.assertEqual('gt', rule.get('comparison_operator')) - self.assertEqual('avg', rule.get('statistic')) - self.assertEqual(600, rule.get('period')) - self.assertEqual(3, rule.get('evaluation_periods')) - query = dict(field='resource_id', type='', - value='INSTANCE_ID', op='eq') - self.assertEqual([query], rule['query']) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_alarm_create_time_constraints(self): - shell = base_shell.CeilometerShell() - argv = ['alarm-threshold-create', - '--name', 'cpu_high', - '--meter-name', 'cpu_util', - '--threshold', '70.0', - '--time-constraint', - 'name=cons1;start="0 11 * * *";duration=300', - '--time-constraint', - 'name=cons2;start="0 23 * * *";duration=600', - ] - _, args = shell.parse_args(argv) - - alarm = alarms.Alarm(mock.Mock(), self.ALARM) - self.cc.alarms.create.return_value = alarm - - ceilometer_shell.do_alarm_threshold_create(self.cc, args) - _, kwargs = self.cc.alarms.create.call_args - time_constraints = [dict(name='cons1', start='0 11 * * *', - duration='300'), - dict(name='cons2', start='0 23 * * *', - duration='600')] - self.assertEqual(time_constraints, kwargs['time_constraints']) - - -class ShellAlarmGnocchiCommandTest(test_shell.ShellTestBase): - - ALARM_ID = 'b69ecdb9-f19b-4fb5-950f-5eb53938b718' - TIME_CONSTRAINTS = [{ - u'duration': 300, - u'start': u'0 11 * * *', - u'description': u'desc1', - u'name': u'cons1', - u'timezone': u''}, { - u'duration': 600, - u'start': u'0 23 * * *', - u'name': u'cons2', - u'description': u'desc2', - u'timezone': u''}] - - ALARM1 = { - u'name': u'name_gnocchi_alarm', - u'description': u'description_gnocchi_alarm', - u'enabled': True, - u'ok_actions': [u'http://something/ok'], - u'alarm_actions': [u'http://something/alarm'], - u'timestamp': u'2015-12-21T03:10:32.305133', - u'state_timestamp': u'2015-12-21T03:10:32.305133', - u'gnocchi_resources_threshold_rule': { - u'evaluation_periods': 3, - u'metric': u'cpu_util', - u'resource_id': u'768ff714-8cfb-4db9-9753-d484cb33a1cc', - u'threshold': 70.0, - u'granularity': 60, - u'aggregation_method': u'count', - u'comparison_operator': u'le', - u'resource_type': u'instance', - }, - u'time_constraints': TIME_CONSTRAINTS, - u'alarm_id': ALARM_ID, - u'state': u'ok', - u'insufficient_data_actions': [u'http://something/insufficient'], - u'repeat_actions': True, - u'user_id': u'f28735621ee84f329144eb467c91fce6', - u'project_id': u'97fcad0402ce4f65ac3bd42a0c6a7e74', - u'type': u'gnocchi_resources_threshold', - u'severity': u'critical', - } - - ALARM2 = { - u'name': u'name_gnocchi_alarm', - u'description': u'description_gnocchi_alarm', - u'enabled': True, - u'ok_actions': [u'http://something/ok'], - u'alarm_actions': [u'http://something/alarm'], - u'timestamp': u'2015-12-21T03:10:32.305133', - u'state_timestamp': u'2015-12-21T03:10:32.305133', - u'gnocchi_aggregation_by_metrics_threshold_rule': { - u'evaluation_periods': 3, - u'metrics': [u'b3d9d8ab-05e8-439f-89ad-5e978dd2a5eb', - u'009d4faf-c275-46f0-8f2d-670b15bac2b0'], - u'threshold': 70.0, - u'granularity': 60, - u'aggregation_method': u'count', - u'comparison_operator': u'le', - }, - u'time_constraints': TIME_CONSTRAINTS, - u'alarm_id': ALARM_ID, - u'state': u'ok', - u'insufficient_data_actions': [u'http://something/insufficient'], - u'repeat_actions': True, - u'user_id': u'f28735621ee84f329144eb467c91fce6', - u'project_id': u'97fcad0402ce4f65ac3bd42a0c6a7e74', - u'type': u'gnocchi_aggregation_by_metrics_threshold', - u'severity': u'critical', - } - - ALARM3 = { - u'name': u'name_gnocchi_alarm', - u'description': u'description_gnocchi_alarm', - u'enabled': True, - u'ok_actions': [u'http://something/ok'], - u'alarm_actions': [u'http://something/alarm'], - u'timestamp': u'2015-12-21T03:10:32.305133', - u'state_timestamp': u'2015-12-21T03:10:32.305133', - u'gnocchi_aggregation_by_resources_threshold_rule': { - u'evaluation_periods': 3, - u'metric': u'cpu_util', - u'threshold': 70.0, - u'granularity': 60, - u'aggregation_method': u'count', - u'comparison_operator': u'le', - u'resource_type': u'instance', - u'query': u'{"=": {"server_group":"my_autoscaling_group"}}', - }, - u'time_constraints': TIME_CONSTRAINTS, - u'alarm_id': ALARM_ID, - u'state': u'ok', - u'insufficient_data_actions': [u'http://something/insufficient'], - u'repeat_actions': True, - u'user_id': u'f28735621ee84f329144eb467c91fce6', - u'project_id': u'97fcad0402ce4f65ac3bd42a0c6a7e74', - u'type': u'gnocchi_aggregation_by_resources_threshold', - u'severity': u'critical', - } - - COMMON_CLI_ARGS = [ - '--name', 'name_gnocchi_alarm', - '--description', 'description_gnocchi_alarm', - '--enabled', 'True', - '--state', 'ok', - '--severity', 'critical', - '--ok-action', 'http://something/ok', - '--alarm-action', 'http://something/alarm', - '--insufficient-data-action', 'http://something/insufficient', - '--repeat-actions', 'True', - '--comparison-operator', 'le', - '--aggregation-method', 'count', - '--threshold', '70', - '--evaluation-periods', '3', - '--granularity', '60', - '--time-constraint', - 'name=cons1;start="0 11 * * *";duration=300;description="desc1"', - '--time-constraint', - 'name=cons2;start="0 23 * * *";duration=600;description="desc2"', - '--user-id', 'f28735621ee84f329144eb467c91fce6', - '--project-id', '97fcad0402ce4f65ac3bd42a0c6a7e74', - ] - - GNOCCHI_RESOURCES_CLI_ARGS = COMMON_CLI_ARGS + [ - '--metric', 'cpu_util', - '--resource-type', 'instance', - '--resource-id', '768ff714-8cfb-4db9-9753-d484cb33a1cc', - ] - - GNOCCHI_AGGR_BY_METRICS_CLI_ARGS = COMMON_CLI_ARGS + [ - '-m', 'b3d9d8ab-05e8-439f-89ad-5e978dd2a5eb', - '-m', '009d4faf-c275-46f0-8f2d-670b15bac2b0', - ] - - GNOCCHI_AGGR_BY_RESOURCES_CLI_ARGS = COMMON_CLI_ARGS + [ - '--metric', 'cpu_util', - '--resource-type', 'instance', - '--query', '{"=": {"server_group":"my_autoscaling_group"}}' - ] - - def setUp(self): - super(ShellAlarmGnocchiCommandTest, self).setUp() - self.cc = mock.Mock() - self.cc.alarms = mock.Mock() - self.args = mock.Mock() - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_do_alarm_gnocchi_resources_threshold_create(self): - - alarm = alarms.Alarm(mock.Mock(), self.ALARM1) - self.cc.alarms.create.return_value = alarm - ceilometer_shell.do_alarm_gnocchi_resources_threshold_create(self.cc, - self.args) - self.assertEqual('''\ -+---------------------------+--------------------------------------+ -| Property | Value | -+---------------------------+--------------------------------------+ -| aggregation_method | count | -| alarm_actions | ["http://something/alarm"] | -| alarm_id | b69ecdb9-f19b-4fb5-950f-5eb53938b718 | -| comparison_operator | le | -| description | description_gnocchi_alarm | -| enabled | True | -| evaluation_periods | 3 | -| granularity | 60 | -| insufficient_data_actions | ["http://something/insufficient"] | -| metric | cpu_util | -| name | name_gnocchi_alarm | -| ok_actions | ["http://something/ok"] | -| project_id | 97fcad0402ce4f65ac3bd42a0c6a7e74 | -| repeat_actions | True | -| resource_id | 768ff714-8cfb-4db9-9753-d484cb33a1cc | -| resource_type | instance | -| severity | critical | -| state | ok | -| threshold | 70.0 | -| time_constraints | [{name: cons1, | -| | description: desc1, | -| | start: 0 11 * * *, | -| | duration: 300}, | -| | {name: cons2, | -| | description: desc2, | -| | start: 0 23 * * *, | -| | duration: 600}] | -| type | gnocchi_resources_threshold | -| user_id | f28735621ee84f329144eb467c91fce6 | -+---------------------------+--------------------------------------+ -''', sys.stdout.getvalue()) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_do_alarm_gnocchi_aggr_by_metrics_threshold_create(self): - - alarm = alarms.Alarm(mock.Mock(), self.ALARM2) - self.cc.alarms.create.return_value = alarm - ceilometer_shell.\ - do_alarm_gnocchi_aggregation_by_metrics_threshold_create( - self.cc, self.args) - stdout = sys.stdout.getvalue() - self.assertIn("b69ecdb9-f19b-4fb5-950f-5eb53938b718", stdout) - self.assertIn("[\"http://something/alarm\"]", stdout) - self.assertIn("description_gnocchi_alarm", stdout) - self.assertIn("gnocchi_aggregation_by_metrics_threshold", stdout) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_do_alarm_gnocchi_aggr_by_resources_threshold_create(self): - - alarm = alarms.Alarm(mock.Mock(), self.ALARM3) - self.cc.alarms.create.return_value = alarm - ceilometer_shell.\ - do_alarm_gnocchi_aggregation_by_resources_threshold_create( - self.cc, self.args) - self.assertEqual('''\ -+---------------------------+------------------------------------------------+ -| Property | Value | -+---------------------------+------------------------------------------------+ -| aggregation_method | count | -| alarm_actions | ["http://something/alarm"] | -| alarm_id | b69ecdb9-f19b-4fb5-950f-5eb53938b718 | -| comparison_operator | le | -| description | description_gnocchi_alarm | -| enabled | True | -| evaluation_periods | 3 | -| granularity | 60 | -| insufficient_data_actions | ["http://something/insufficient"] | -| metric | cpu_util | -| name | name_gnocchi_alarm | -| ok_actions | ["http://something/ok"] | -| project_id | 97fcad0402ce4f65ac3bd42a0c6a7e74 | -| query | {"=": {"server_group":"my_autoscaling_group"}} | -| repeat_actions | True | -| resource_type | instance | -| severity | critical | -| state | ok | -| threshold | 70.0 | -| time_constraints | [{name: cons1, | -| | description: desc1, | -| | start: 0 11 * * *, | -| | duration: 300}, | -| | {name: cons2, | -| | description: desc2, | -| | start: 0 23 * * *, | -| | duration: 600}] | -| type | gnocchi_aggregation_by_resources_threshold | -| user_id | f28735621ee84f329144eb467c91fce6 | -+---------------------------+------------------------------------------------+ -''', sys.stdout.getvalue()) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_do_alarm_gnocchi_resources_threshold_create_args(self): - argv = ['alarm-gnocchi-resources-threshold-create'] - argv.extend(self.GNOCCHI_RESOURCES_CLI_ARGS) - self._test_alarm_gnocchi_resources_arguments('create', argv) - - def test_do_alarm_gnocchi_resources_threshold_update_args(self): - argv = ['alarm-gnocchi-resources-threshold-update'] - argv.extend(self.GNOCCHI_RESOURCES_CLI_ARGS) - argv.append(self.ALARM_ID) - self._test_alarm_gnocchi_resources_arguments('update', argv) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_do_alarm_gnocchi_aggr_by_metrics_threshold_create_args(self): - argv = ['alarm-gnocchi-aggregation-by-metrics-threshold-create'] - argv.extend(self.GNOCCHI_AGGR_BY_METRICS_CLI_ARGS) - self._test_alarm_gnocchi_aggr_by_metrics_arguments('create', argv) - - def test_do_alarm_gnocchi_aggr_by_metrics_threshold_update_args(self): - argv = ['alarm-gnocchi-aggregation-by-metrics-threshold-update'] - argv.extend(self.GNOCCHI_AGGR_BY_METRICS_CLI_ARGS) - argv.append(self.ALARM_ID) - self._test_alarm_gnocchi_aggr_by_metrics_arguments('update', argv) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_do_alarm_gnocchi_aggr_by_resources_threshold_create_args(self): - argv = ['alarm-gnocchi-aggregation-by-resources-threshold-create'] - argv.extend(self.GNOCCHI_AGGR_BY_RESOURCES_CLI_ARGS) - self._test_alarm_gnocchi_aggr_by_resources_arguments('create', argv) - - def test_do_alarm_gnocchi_aggr_by_resources_threshold_update_args(self): - argv = ['alarm-gnocchi-aggregation-by-resources-threshold-update'] - argv.extend(self.GNOCCHI_AGGR_BY_RESOURCES_CLI_ARGS) - argv.append(self.ALARM_ID) - self._test_alarm_gnocchi_aggr_by_resources_arguments('update', argv) - - @mock.patch('sys.stdout', new=six.StringIO()) - def _test_common_alarm_gnocchi_arguments(self, kwargs): - self.assertEqual('97fcad0402ce4f65ac3bd42a0c6a7e74', - kwargs.get('project_id')) - self.assertEqual('f28735621ee84f329144eb467c91fce6', - kwargs.get('user_id')) - self.assertEqual('name_gnocchi_alarm', kwargs.get('name')) - self.assertEqual('description_gnocchi_alarm', - kwargs.get('description')) - self.assertEqual(['http://something/alarm'], - kwargs.get('alarm_actions')) - self.assertEqual(['http://something/ok'], kwargs.get('ok_actions')) - self.assertEqual(['http://something/insufficient'], - kwargs.get('insufficient_data_actions')) - self.assertEqual('critical', kwargs.get('severity')) - self.assertEqual('ok', kwargs.get('state')) - self.assertEqual(True, kwargs.get('enabled')) - self.assertEqual(True, kwargs.get('repeat_actions')) - time_constraints = [dict(name='cons1', start='0 11 * * *', - duration='300', description='desc1'), - dict(name='cons2', start='0 23 * * *', - duration='600', description='desc2')] - self.assertEqual(time_constraints, kwargs['time_constraints']) - - def _test_alarm_gnocchi_resources_arguments(self, action, argv): - self.make_env(test_shell.FAKE_V2_ENV) - with mock.patch.object(alarms.AlarmManager, action) as mocked: - with mock.patch('ceilometerclient.apiclient.' - 'client.HTTPClient.client_request') as request: - request.site_effect = exceptions.EndpointNotFound - base_shell.main(argv) - args, kwargs = mocked.call_args - self.assertEqual('gnocchi_resources_threshold', kwargs.get('type')) - self.assertIn('gnocchi_resources_threshold_rule', kwargs) - rule = kwargs['gnocchi_resources_threshold_rule'] - self.assertEqual('cpu_util', rule.get('metric')) - self.assertEqual(70.0, rule.get('threshold')) - self.assertEqual(60, rule.get('granularity')) - self.assertEqual('count', rule.get('aggregation_method')) - self.assertEqual('le', rule.get('comparison_operator')) - self.assertEqual(3, rule.get('evaluation_periods')) - self.assertEqual('768ff714-8cfb-4db9-9753-d484cb33a1cc', - rule.get('resource_id')) - self.assertEqual('instance', rule.get('resource_type')) - self._test_common_alarm_gnocchi_arguments(kwargs) - - def _test_alarm_gnocchi_aggr_by_metrics_arguments(self, action, argv): - self.make_env(test_shell.FAKE_V2_ENV) - with mock.patch.object(alarms.AlarmManager, action) as mocked: - with mock.patch('ceilometerclient.apiclient.' - 'client.HTTPClient.client_request') as request: - request.site_effect = exceptions.EndpointNotFound - base_shell.main(argv) - args, kwargs = mocked.call_args - self.assertEqual('gnocchi_aggregation_by_metrics_threshold', - kwargs.get('type')) - self.assertIn('gnocchi_aggregation_by_metrics_threshold_rule', kwargs) - rule = kwargs['gnocchi_aggregation_by_metrics_threshold_rule'] - self.assertEqual(['b3d9d8ab-05e8-439f-89ad-5e978dd2a5eb', - '009d4faf-c275-46f0-8f2d-670b15bac2b0'], - rule.get('metrics')) - self.assertEqual(70.0, rule.get('threshold')) - self.assertEqual(60, rule.get('granularity')) - self.assertEqual('count', rule.get('aggregation_method')) - self.assertEqual('le', rule.get('comparison_operator')) - self.assertEqual(3, rule.get('evaluation_periods')) - self._test_common_alarm_gnocchi_arguments(kwargs) - - def _test_alarm_gnocchi_aggr_by_resources_arguments(self, action, argv): - self.make_env(test_shell.FAKE_V2_ENV) - with mock.patch.object(alarms.AlarmManager, action) as mocked: - with mock.patch('ceilometerclient.apiclient.' - 'client.HTTPClient.client_request') as request: - request.site_effect = exceptions.EndpointNotFound - base_shell.main(argv) - args, kwargs = mocked.call_args - self.assertEqual('gnocchi_aggregation_by_resources_threshold', - kwargs.get('type')) - self.assertIn('gnocchi_aggregation_by_resources_threshold_rule', - kwargs) - rule = kwargs['gnocchi_aggregation_by_resources_threshold_rule'] - self.assertEqual('cpu_util', rule.get('metric')) - self.assertEqual(70.0, rule.get('threshold')) - self.assertEqual(60, rule.get('granularity')) - self.assertEqual('count', rule.get('aggregation_method')) - self.assertEqual('le', rule.get('comparison_operator')) - self.assertEqual(3, rule.get('evaluation_periods')) - self.assertEqual('instance', rule.get('resource_type')) - self.assertEqual('{"=": {"server_group":"my_autoscaling_group"}}', - rule.get('query')) - self._test_common_alarm_gnocchi_arguments(kwargs) - - -class ShellSampleListCommandTest(utils.BaseTestCase): - - METER = 'cpu_util' - SAMPLE_VALUES = ( - ("cpu_util", - "5dcf5537-3161-4e25-9235-407e1385bd35", - "2013-10-15T05:50:30", - "%", - 0.261666666667, - "gauge", - "86536501-b2c9-48f6-9c6a-7a5b14ba7482"), - ("cpu_util", - "87d197e9-9cf6-4c25-bc66-1b1f4cedb52f", - "2013-10-15T05:50:29", - "%", - 0.261666666667, - "gauge", - "fe2a91ec-602b-4b55-8cba-5302ce3b916e",), - ("cpu_util", - "5dcf5537-3161-4e25-9235-407e1385bd35", - "2013-10-15T05:40:30", - "%", - 0.251247920133, - "gauge", - "52768bcb-b4e9-4db9-a30c-738c758b6f43"), - ("cpu_util", - "87d197e9-9cf6-4c25-bc66-1b1f4cedb52f", - "2013-10-15T05:40:29", - "%", - 0.26, - "gauge", - "31ae614a-ac6b-4fb9-b106-4667bae03308"), - ) - - OLD_SAMPLES = [ - dict(counter_name=s[0], - resource_id=s[1], - timestamp=s[2], - counter_unit=s[3], - counter_volume=s[4], - counter_type=s[5]) - for s in SAMPLE_VALUES - ] - - SAMPLES = [ - dict(meter=s[0], - resource_id=s[1], - timestamp=s[2], - unit=s[3], - volume=s[4], - type=s[5], - id=s[6]) - for s in SAMPLE_VALUES - ] - - def setUp(self): - super(ShellSampleListCommandTest, self).setUp() - self.cc = mock.Mock() - self.cc.samples = mock.Mock() - self.cc.new_samples = mock.Mock() - self.args = mock.Mock() - self.args.query = None - self.args.limit = None - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_old_sample_list(self): - self.args.meter = self.METER - sample_list = [samples.OldSample(mock.Mock(), sample) - for sample in self.OLD_SAMPLES] - self.cc.samples.list.return_value = sample_list - - ceilometer_shell.do_sample_list(self.cc, self.args) - self.cc.samples.list.assert_called_once_with( - meter_name=self.METER, - q=None, - limit=None) - - self.assertEqual('''\ -+--------------------------------------+----------+-------+----------------\ -+------+---------------------+ -| Resource ID | Name | Type | Volume \ -| Unit | Timestamp | -+--------------------------------------+----------+-------+----------------\ -+------+---------------------+ -| 5dcf5537-3161-4e25-9235-407e1385bd35 | cpu_util | gauge | 0.261666666667 \ -| % | 2013-10-15T05:50:30 | -| 87d197e9-9cf6-4c25-bc66-1b1f4cedb52f | cpu_util | gauge | 0.261666666667 \ -| % | 2013-10-15T05:50:29 | -| 5dcf5537-3161-4e25-9235-407e1385bd35 | cpu_util | gauge | 0.251247920133 \ -| % | 2013-10-15T05:40:30 | -| 87d197e9-9cf6-4c25-bc66-1b1f4cedb52f | cpu_util | gauge | 0.26 \ -| % | 2013-10-15T05:40:29 | -+--------------------------------------+----------+-------+----------------\ -+------+---------------------+ -''', sys.stdout.getvalue()) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_sample_list(self): - self.args.meter = None - sample_list = [samples.Sample(mock.Mock(), sample) - for sample in self.SAMPLES] - self.cc.new_samples.list.return_value = sample_list - - ceilometer_shell.do_sample_list(self.cc, self.args) - self.cc.new_samples.list.assert_called_once_with( - q=None, - limit=None) - - self.assertEqual('''\ -+--------------------------------------+--------------------------------------\ -+----------+-------+----------------+------+---------------------+ -| ID | Resource ID \ -| Name | Type | Volume | Unit | Timestamp | -+--------------------------------------+--------------------------------------\ -+----------+-------+----------------+------+---------------------+ -| 86536501-b2c9-48f6-9c6a-7a5b14ba7482 | 5dcf5537-3161-4e25-9235-407e1385bd35 \ -| cpu_util | gauge | 0.261666666667 | % | 2013-10-15T05:50:30 | -| fe2a91ec-602b-4b55-8cba-5302ce3b916e | 87d197e9-9cf6-4c25-bc66-1b1f4cedb52f \ -| cpu_util | gauge | 0.261666666667 | % | 2013-10-15T05:50:29 | -| 52768bcb-b4e9-4db9-a30c-738c758b6f43 | 5dcf5537-3161-4e25-9235-407e1385bd35 \ -| cpu_util | gauge | 0.251247920133 | % | 2013-10-15T05:40:30 | -| 31ae614a-ac6b-4fb9-b106-4667bae03308 | 87d197e9-9cf6-4c25-bc66-1b1f4cedb52f \ -| cpu_util | gauge | 0.26 | % | 2013-10-15T05:40:29 | -+--------------------------------------+--------------------------------------\ -+----------+-------+----------------+------+---------------------+ -''', sys.stdout.getvalue()) - - -class ShellSampleShowCommandTest(utils.BaseTestCase): - - SAMPLE = { - "user_id": None, - "resource_id": "9b651dfd-7d30-402b-972e-212b2c4bfb05", - "timestamp": "2014-11-03T13:37:46", - "meter": "image", - "volume": 1.0, - "source": "openstack", - "recorded_at": "2014-11-03T13:37:46.994458", - "project_id": "2cc3a7bb859b4bacbeab0aa9ca673033", - "type": "gauge", - "id": "98b5f258-635e-11e4-8bdd-0025647390c1", - "unit": "image", - "metadata": { - "name": "cirros-0.3.2-x86_64-uec", - } - } - - def setUp(self): - super(ShellSampleShowCommandTest, self).setUp() - self.cc = mock.Mock() - self.cc.new_samples = mock.Mock() - self.args = mock.Mock() - self.args.sample_id = "98b5f258-635e-11e4-8bdd-0025647390c1" - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_sample_show(self): - sample = samples.Sample(mock.Mock(), self.SAMPLE) - self.cc.new_samples.get.return_value = sample - - ceilometer_shell.do_sample_show(self.cc, self.args) - self.cc.new_samples.get.assert_called_once_with( - "98b5f258-635e-11e4-8bdd-0025647390c1") - - self.assertEqual('''\ -+-------------+--------------------------------------+ -| Property | Value | -+-------------+--------------------------------------+ -| id | 98b5f258-635e-11e4-8bdd-0025647390c1 | -| metadata | {"name": "cirros-0.3.2-x86_64-uec"} | -| meter | image | -| project_id | 2cc3a7bb859b4bacbeab0aa9ca673033 | -| recorded_at | 2014-11-03T13:37:46.994458 | -| resource_id | 9b651dfd-7d30-402b-972e-212b2c4bfb05 | -| source | openstack | -| timestamp | 2014-11-03T13:37:46 | -| type | gauge | -| unit | image | -| user_id | None | -| volume | 1.0 | -+-------------+--------------------------------------+ -''', sys.stdout.getvalue()) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_sample_show_raises_command_err(self): - self.cc.new_samples.get.side_effect = exc.HTTPNotFound - - self.assertRaises(exc.CommandError, ceilometer_shell.do_sample_show, - self.cc, self.args) - - -class ShellSampleCreateCommandTest(utils.BaseTestCase): - - METER = 'instance' - METER_TYPE = 'gauge' - RESOURCE_ID = '0564c64c-3545-4e34-abfb-9d18e5f2f2f9' - SAMPLE_VOLUME = '1' - METER_UNIT = 'instance' - SAMPLE = [{ - u'counter_name': u'instance', - u'user_id': u'21b442b8101d407d8242b6610e0ed0eb', - u'resource_id': u'0564c64c-3545-4e34-abfb-9d18e5f2f2f9', - u'timestamp': u'2014-01-10T03: 05: 33.951170', - u'message_id': u'1247cbe6-79a4-11e3-a296-000c294c58e2', - u'source': u'384260c6987b451d8290e66e1f108082: openstack', - u'counter_unit': u'instance', - u'counter_volume': 1.0, - u'project_id': u'384260c6987b451d8290e66e1f108082', - u'counter_type': u'gauge', - u'resource_metadata': {u'display_name': u'test_name'} - }] - - def setUp(self): - super(ShellSampleCreateCommandTest, self).setUp() - self.cc = mock.Mock() - self.cc.samples = mock.Mock() - self.args = mock.Mock() - self.args.meter_name = self.METER - self.args.meter_type = self.METER_TYPE - self.args.meter_unit = self.METER_UNIT - self.args.resource_id = self.RESOURCE_ID - self.args.sample_volume = self.SAMPLE_VOLUME - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_sample_create(self): - ret_sample = [samples.OldSample(mock.Mock(), sample) - for sample in self.SAMPLE] - self.cc.samples.create.return_value = ret_sample - - ceilometer_shell.do_sample_create(self.cc, self.args) - - self.assertEqual('''\ -+-------------------+---------------------------------------------+ -| Property | Value | -+-------------------+---------------------------------------------+ -| message_id | 1247cbe6-79a4-11e3-a296-000c294c58e2 | -| name | instance | -| project_id | 384260c6987b451d8290e66e1f108082 | -| resource_id | 0564c64c-3545-4e34-abfb-9d18e5f2f2f9 | -| resource_metadata | {"display_name": "test_name"} | -| source | 384260c6987b451d8290e66e1f108082: openstack | -| timestamp | 2014-01-10T03: 05: 33.951170 | -| type | gauge | -| unit | instance | -| user_id | 21b442b8101d407d8242b6610e0ed0eb | -| volume | 1.0 | -+-------------------+---------------------------------------------+ -''', sys.stdout.getvalue()) - - def test_sample_create_with_invalid_resource_metadata(self): - self.args.resource_metadata = 'foo=bar' - with mock.patch('ceilometerclient.exc.CommandError') as e: - e.return_value = exc.BaseException() - self.assertRaises(exc.BaseException, - ceilometer_shell.do_sample_create, - self.cc, self.args) - e.assert_called_with('Invalid resource metadata, it should be a' - ' json string, like: \'{"foo":"bar"}\'') - - -class ShellSampleCreateListCommandTest(utils.BaseTestCase): - - SAMPLE = { - u'counter_name': u'image', - u'user_id': u'21b442b8101d407d8242b6610e0ed0eb', - u'resource_id': u'0564c64c-3545-4e34-abfb-9d18e5f2f2f9', - u'timestamp': u'2015-05-19T12:00:08.368574', - u'source': u'384260c6987b451d8290e66e1f108082: openstack', - u'counter_unit': u'image', - u'counter_volume': 1.0, - u'project_id': u'384260c6987b451d8290e66e1f108082', - u'resource_metadata': {}, - u'counter_type': u'cumulative' - } - - def setUp(self): - super(ShellSampleCreateListCommandTest, self).setUp() - self.cc = mock.Mock() - self.cc.samples = mock.Mock() - self.cc.samples.create_list = mock.Mock() - self.args = mock.Mock() - self.samples = [self.SAMPLE] * 5 - self.args.samples_list = json.dumps(self.samples) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_sample_create_list(self): - ret_samples = [samples.OldSample(mock.Mock(), - sample) for sample in self.samples] - self.cc.samples.create_list.return_value = ret_samples - ceilometer_shell.do_sample_create_list(self.cc, self.args) - self.cc.samples.create_list.assert_called_with(self.samples, - direct=mock.ANY) - self.assertEqual('''\ -+--------------------------------------+-------+------------+--------+-------\ -+----------------------------+ -| Resource ID | Name | Type | Volume | Unit \ -| Timestamp | -+--------------------------------------+-------+------------+--------+-------\ -+----------------------------+ -| 0564c64c-3545-4e34-abfb-9d18e5f2f2f9 | image | cumulative | 1.0 | image \ -| 2015-05-19T12:00:08.368574 | -| 0564c64c-3545-4e34-abfb-9d18e5f2f2f9 | image | cumulative | 1.0 | image \ -| 2015-05-19T12:00:08.368574 | -| 0564c64c-3545-4e34-abfb-9d18e5f2f2f9 | image | cumulative | 1.0 | image \ -| 2015-05-19T12:00:08.368574 | -| 0564c64c-3545-4e34-abfb-9d18e5f2f2f9 | image | cumulative | 1.0 | image \ -| 2015-05-19T12:00:08.368574 | -| 0564c64c-3545-4e34-abfb-9d18e5f2f2f9 | image | cumulative | 1.0 | image \ -| 2015-05-19T12:00:08.368574 | -+--------------------------------------+-------+------------+--------+-------\ -+----------------------------+ -''', sys.stdout.getvalue()) - - -class ShellQuerySamplesCommandTest(utils.BaseTestCase): - - SAMPLE = [{u'id': u'b55d1526-9929-11e3-a3f6-02163e5df1e6', - u'metadata': { - u'name1': u'value1', - u'name2': u'value2'}, - u'meter': 'instance', - u'project_id': u'35b17138-b364-4e6a-a131-8f3099c5be68', - u'resource_id': u'bd9431c1-8d69-4ad3-803a-8d4a6b89fd36', - u'source': u'openstack', - u'timestamp': u'2014-02-19T05:50:16.673604', - u'type': u'gauge', - u'unit': u'instance', - u'volume': 1, - u'user_id': 'efd87807-12d2-4b38-9c70-5f5c2ac427ff'}] - - QUERY = {"filter": {"and": [{"=": {"source": "openstack"}}, - {">": {"timestamp": "2014-02-19T05:50:16"}}]}, - "orderby": [{"timestamp": "desc"}, {"volume": "asc"}], - "limit": 10} - - def setUp(self): - super(ShellQuerySamplesCommandTest, self).setUp() - self.cc = mock.Mock() - self.args = mock.Mock() - self.args.filter = self.QUERY["filter"] - self.args.orderby = self.QUERY["orderby"] - self.args.limit = self.QUERY["limit"] - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_query(self): - ret_sample = [samples.Sample(mock.Mock(), sample) - for sample in self.SAMPLE] - self.cc.query_samples.query.return_value = ret_sample - - ceilometer_shell.do_query_samples(self.cc, self.args) - - self.assertEqual('''\ -+--------------------------------------+--------------------------------------\ -+----------+-------+--------+----------+----------------------------+ -| ID | Resource ID \ -| Meter | Type | Volume | Unit | Timestamp | -+--------------------------------------+--------------------------------------\ -+----------+-------+--------+----------+----------------------------+ -| b55d1526-9929-11e3-a3f6-02163e5df1e6 | bd9431c1-8d69-4ad3-803a-8d4a6b89fd36 \ -| instance | gauge | 1 | instance | 2014-02-19T05:50:16.673604 | -+--------------------------------------+--------------------------------------\ -+----------+-------+--------+----------+----------------------------+ -''', sys.stdout.getvalue()) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_query_raises_command_error(self): - self.cc.query_samples.query.side_effect = exc.HTTPNotFound - - self.assertRaises(exc.CommandError, - ceilometer_shell.do_query_samples, - self.cc, self.args) - - -class ShellQueryAlarmsCommandTest(utils.BaseTestCase): - - ALARM = [{"alarm_actions": ["http://site:8000/alarm"], - "alarm_id": "768ff714-8cfb-4db9-9753-d484cb33a1cc", - "combination_rule": { - "alarm_ids": [ - "739e99cb-c2ec-4718-b900-332502355f38", - "153462d0-a9b8-4b5b-8175-9e4b05e9b856"], - "operator": "or"}, - "description": "An alarm", - "enabled": True, - "insufficient_data_actions": ["http://site:8000/nodata"], - "name": "SwiftObjectAlarm", - "ok_actions": ["http://site:8000/ok"], - "project_id": "c96c887c216949acbdfbd8b494863567", - "repeat_actions": False, - "state": "ok", - "severity": "critical", - "state_timestamp": "2014-02-20T10:37:15.589860", - "threshold_rule": None, - "timestamp": "2014-02-20T10:37:15.589856", - "time_constraints": [{"name": "test", "start": "0 23 * * *", - "duration": 10800}], - "type": "combination", - "user_id": "c96c887c216949acbdfbd8b494863567"}] - - QUERY = {"filter": {"and": [{"!=": {"state": "ok"}}, - {"=": {"type": "combination"}}]}, - "orderby": [{"state_timestamp": "desc"}], - "limit": 10} - - def setUp(self): - super(ShellQueryAlarmsCommandTest, self).setUp() - self.cc = mock.Mock() - self.args = mock.Mock() - self.args.filter = self.QUERY["filter"] - self.args.orderby = self.QUERY["orderby"] - self.args.limit = self.QUERY["limit"] - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_query(self): - ret_alarm = [alarms.Alarm(mock.Mock(), alarm) - for alarm in self.ALARM] - self.cc.query_alarms.query.return_value = ret_alarm - - ceilometer_shell.do_query_alarms(self.cc, self.args) - - self.assertEqual('''\ -+--------------------------------------+------------------+-------+----------+\ ----------+------------+-------------------------------------------------------\ ------------------------------------------------+-------------------------------\ --+ -| Alarm ID | Name | State | Severity \ -| Enabled | Continuous | Alarm condition \ - | Time constraints \ - | -+--------------------------------------+------------------+-------+----------+\ ----------+------------+-------------------------------------------------------\ ------------------------------------------------+--------------------------------+ -| 768ff714-8cfb-4db9-9753-d484cb33a1cc | SwiftObjectAlarm | ok | critical \ -| True | False | combinated states (OR) of \ -739e99cb-c2ec-4718-b900-332502355f38, 153462d0-a9b8-4b5b-8175-9e4b05e9b856 |\ - test at 0 23 * * * for 10800s | -+--------------------------------------+------------------+-------+----------+\ ----------+------------+-------------------------------------------------------\ ------------------------------------------------+------------------------------\ ---+ -''', sys.stdout.getvalue()) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_time_constraints_compatibility(self): - # client should be backwards compatible - alarm_without_tc = dict(self.ALARM[0]) - del alarm_without_tc['time_constraints'] - - # NOTE(nsaje): Since we're accessing a nonexisting key in the resource, - # the resource is looking it up with the manager (which is a mock). - manager_mock = mock.Mock() - del manager_mock.get - ret_alarm = [alarms.Alarm(manager_mock, alarm_without_tc)] - self.cc.query_alarms.query.return_value = ret_alarm - - ceilometer_shell.do_query_alarms(self.cc, self.args) - - self.assertEqual('''\ -+--------------------------------------+------------------+-------+----------+\ ----------+------------+-------------------------------------------------------\ ------------------------------------------------+------------------+ -| Alarm ID | Name | State | Severity \ -| Enabled | Continuous | Alarm condition \ - | Time constraints | -+--------------------------------------+------------------+-------+----------+\ ----------+------------+-------------------------------------------------------\ ------------------------------------------------+------------------+ -| 768ff714-8cfb-4db9-9753-d484cb33a1cc | SwiftObjectAlarm | ok | critical \ -| True | False | combinated states (OR) of \ -739e99cb-c2ec-4718-b900-332502355f38, 153462d0-a9b8-4b5b-8175-9e4b05e9b856 \ -| None | -+--------------------------------------+------------------+-------+----------+\ ----------+------------+-------------------------------------------------------\ ------------------------------------------------+------------------+ -''', sys.stdout.getvalue()) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_query_raises_command_err(self): - self.cc.query_alarms.query.side_effect = exc.HTTPNotFound - self.assertRaises(exc.CommandError, - ceilometer_shell.do_query_alarms, - self.cc, self.args) - - -class ShellQueryAlarmHistoryCommandTest(utils.BaseTestCase): - - ALARM_HISTORY = [{"alarm_id": "e8ff32f772a44a478182c3fe1f7cad6a", - "event_id": "c74a8611-6553-4764-a860-c15a6aabb5d0", - "detail": - "{\"threshold\": 42.0, \"evaluation_periods\": 4}", - "on_behalf_of": "92159030020611e3b26dde429e99ee8c", - "project_id": "b6f16144010811e387e4de429e99ee8c", - "timestamp": "2014-03-11T16:02:58.376261", - "type": "rule change", - "user_id": "3e5d11fda79448ac99ccefb20be187ca" - }] - - QUERY = {"filter": {"and": [{">": {"timestamp": "2014-03-11T16:02:58"}}, - {"=": {"type": "rule change"}}]}, - "orderby": [{"timestamp": "desc"}], - "limit": 10} - - def setUp(self): - super(ShellQueryAlarmHistoryCommandTest, self).setUp() - self.cc = mock.Mock() - self.args = mock.Mock() - self.args.filter = self.QUERY["filter"] - self.args.orderby = self.QUERY["orderby"] - self.args.limit = self.QUERY["limit"] - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_query(self): - ret_alarm_history = [alarms.AlarmChange(mock.Mock(), alarm_history) - for alarm_history in self.ALARM_HISTORY] - self.cc.query_alarm_history.query.return_value = ret_alarm_history - - ceilometer_shell.do_query_alarm_history(self.cc, self.args) - - self.assertEqual('''\ -+----------------------------------+--------------------------------------+-\ -------------+----------------------------------------------+----------------\ -------------+ -| Alarm ID | Event ID | \ -Type | Detail | Timestamp \ - | -+----------------------------------+--------------------------------------+-\ -------------+----------------------------------------------+----------------\ -------------+ -| e8ff32f772a44a478182c3fe1f7cad6a | c74a8611-6553-4764-a860-c15a6aabb5d0 | \ -rule change | {"threshold": 42.0, "evaluation_periods": 4} | 2014-03-11T16:0\ -2:58.376261 | -+----------------------------------+--------------------------------------+-\ -------------+----------------------------------------------+----------------\ -------------+ -''', sys.stdout.getvalue()) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_query_raises_command_err(self): - self.cc.query_alarm_history.query.side_effect = exc.HTTPNotFound - self.assertRaises(exc.CommandError, - ceilometer_shell.do_query_alarm_history, - self.cc, self.args) - - -class ShellStatisticsTest(utils.BaseTestCase): - def setUp(self): - super(ShellStatisticsTest, self).setUp() - self.cc = mock.Mock() - self.displays = { - 'duration': 'Duration', - 'duration_end': 'Duration End', - 'duration_start': 'Duration Start', - 'period': 'Period', - 'period_end': 'Period End', - 'period_start': 'Period Start', - 'groupby': 'Group By', - 'avg': 'Avg', - 'count': 'Count', - 'max': 'Max', - 'min': 'Min', - 'sum': 'Sum', - 'stddev': 'Standard deviation', - 'cardinality': 'Cardinality' - } - self.args = mock.Mock() - self.args.meter_name = 'instance' - self.args.aggregate = [] - self.args.groupby = None - self.args.query = None - - def test_statistics_list_simple(self): - samples = [ - {u'count': 135, - u'duration_start': u'2013-02-04T10:51:42', - u'min': 1.0, - u'max': 1.0, - u'duration_end': - u'2013-02-05T15:46:09', - u'duration': 1734.0, - u'avg': 1.0, - u'sum': 135.0}, - ] - fields = [ - 'period', - 'period_start', - 'period_end', - 'max', - 'min', - 'avg', - 'sum', - 'count', - 'duration', - 'duration_start', - 'duration_end', - ] - statistics_ret = [ - statistics.Statistics(mock.Mock(), sample) for sample in samples - ] - self.cc.statistics.list.return_value = statistics_ret - with mock.patch('ceilometerclient.v2.shell.utils.print_list') as pmock: - ceilometer_shell.do_statistics(self.cc, self.args) - pmock.assert_called_with( - statistics_ret, - fields, - [self.displays[f] for f in fields] - ) - - def test_statistics_list_groupby(self): - samples = [ - {u'count': 135, - u'duration_start': u'2013-02-04T10:51:42', - u'min': 1.0, - u'max': 1.0, - u'duration_end': - u'2013-02-05T15:46:09', - u'duration': 1734.0, - u'avg': 1.0, - u'sum': 135.0, - u'groupby': {u'resource_id': u'foo'} - }, - {u'count': 12, - u'duration_start': u'2013-02-04T10:51:42', - u'min': 1.0, - u'max': 1.0, - u'duration_end': - u'2013-02-05T15:46:09', - u'duration': 1734.0, - u'avg': 1.0, - u'sum': 12.0, - u'groupby': {u'resource_id': u'bar'} - }, - ] - fields = [ - 'period', - 'period_start', - 'period_end', - 'groupby', - 'max', - 'min', - 'avg', - 'sum', - 'count', - 'duration', - 'duration_start', - 'duration_end', - ] - self.args.groupby = 'resource_id' - statistics_ret = [ - statistics.Statistics(mock.Mock(), sample) for sample in samples - ] - self.cc.statistics.list.return_value = statistics_ret - with mock.patch('ceilometerclient.v2.shell.utils.print_list') as pmock: - ceilometer_shell.do_statistics(self.cc, self.args) - pmock.assert_called_with( - statistics_ret, - fields, - [self.displays[f] for f in fields], - ) - - def test_statistics_list_aggregates(self): - samples = [ - {u'aggregate': {u'cardinality/resource_id': 4.0, u'count': 2.0}, - u'count': 2, - u'duration': 0.442451, - u'duration_end': u'2014-03-12T14:00:21.774154', - u'duration_start': u'2014-03-12T14:00:21.331703', - u'groupby': None, - u'period': 0, - u'period_end': u'2014-03-12T14:00:21.774154', - u'period_start': u'2014-03-12T14:00:21.331703', - u'unit': u'instance', - }, - ] - fields = [ - 'period', - 'period_start', - 'period_end', - 'count', - 'cardinality/resource_id', - 'duration', - 'duration_start', - 'duration_end', - ] - self.args.aggregate = ['count', 'cardinality<-resource_id'] - statistics_ret = [ - statistics.Statistics(mock.Mock(), sample) for sample in samples - ] - self.cc.statistics.list.return_value = statistics_ret - with mock.patch('ceilometerclient.v2.shell.utils.print_list') as pmock: - ceilometer_shell.do_statistics(self.cc, self.args) - pmock.assert_called_with( - statistics_ret, - 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, **kwargs): - positional = kwargs.pop('positional', False) - for value in ('', ' ', ' ', '\t'): - self._test_entity_action_with_empty_value(entity, value, - positional, *args) - - def _test_entity_action_with_empty_value(self, entity, value, - positional, *args): - new_args = [value] if positional else ['--%s' % entity, value] - argv = list(args) + new_args - shell = base_shell.CeilometerShell() - 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', - positional=True, *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_gnocchi_resources_update_with_empty_id(self): - self._test_alarm_action_with_empty_ids( - 'alarm-gnocchi-resources-threshold-update') - - def test_alarm_gnocchi_aggr_by_resources_update_with_empty_id(self): - self._test_alarm_action_with_empty_ids( - 'alarm-gnocchi-aggregation-by-resources-threshold-update') - - def test_alarm_gnocchi_aggr_by_metrics_update_with_empty_id(self): - self._test_alarm_action_with_empty_ids( - 'alarm-gnocchi-aggregation-by-metrics-threshold-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) - - -class ShellObsoletedArgsTest(utils.BaseTestCase): - """Test arguments that have been obsoleted.""" - - def _test_entity_obsoleted(self, entity, value, positional, *args): - new_args = [value] if positional else ['--%s' % entity, value] - argv = list(args) + new_args - shell = base_shell.CeilometerShell() - with mock.patch('sys.stdout', new_callable=six.StringIO) as stdout: - shell.parse_args(argv) - self.assertIn('obsolete', stdout.getvalue()) - - def test_obsolete_alarm_id(self): - for method in ['alarm-show', 'alarm-update', 'alarm-threshold-update', - 'alarm-combination-update', 'alarm-delete', - 'alarm-state-get', 'alarm-history']: - self._test_entity_obsoleted('alarm_id', 'abcde', False, method) - - -class ShellEventListCommandTest(utils.BaseTestCase): - - EVENTS = [ - { - "generated": "2015-01-12T04:03:25.741471", - "message_id": "fb2bef58-88af-4380-8698-e0f18fcf452d", - "event_type": "compute.instance.create.start", - "traits": [{ - "name": "state", - "type": "string", - "value": "building", - }], - }, - { - "generated": "2015-01-12T04:03:28.452495", - "message_id": "9b20509a-576b-4995-acfa-1a24ee5cf49f", - "event_type": "compute.instance.create.end", - "traits": [{ - "name": "state", - "type": "string", - "value": "active", - }], - }, - ] - - def setUp(self): - super(ShellEventListCommandTest, self).setUp() - self.cc = mock.Mock() - self.args = mock.Mock() - self.args.query = None - self.args.no_traits = None - self.args.limit = None - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_event_list(self): - ret_events = [events.Event(mock.Mock(), event) - for event in self.EVENTS] - self.cc.events.list.return_value = ret_events - ceilometer_shell.do_event_list(self.cc, self.args) - self.assertEqual('''\ -+--------------------------------------+-------------------------------+\ -----------------------------+-------------------------------+ -| Message ID | Event Type |\ - Generated | Traits | -+--------------------------------------+-------------------------------+\ -----------------------------+-------------------------------+ -| fb2bef58-88af-4380-8698-e0f18fcf452d | compute.instance.create.start |\ - 2015-01-12T04:03:25.741471 | +-------+--------+----------+ | -| | |\ - | | name | type | value | | -| | |\ - | +-------+--------+----------+ | -| | |\ - | | state | string | building | | -| | |\ - | +-------+--------+----------+ | -| 9b20509a-576b-4995-acfa-1a24ee5cf49f | compute.instance.create.end |\ - 2015-01-12T04:03:28.452495 | +-------+--------+--------+ | -| | |\ - | | name | type | value | | -| | |\ - | +-------+--------+--------+ | -| | |\ - | | state | string | active | | -| | |\ - | +-------+--------+--------+ | -+--------------------------------------+-------------------------------+\ -----------------------------+-------------------------------+ -''', sys.stdout.getvalue()) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_event_list_no_traits(self): - self.args.no_traits = True - ret_events = [events.Event(mock.Mock(), event) - for event in self.EVENTS] - self.cc.events.list.return_value = ret_events - ceilometer_shell.do_event_list(self.cc, self.args) - self.assertEqual('''\ -+--------------------------------------+-------------------------------\ -+----------------------------+ -| Message ID | Event Type \ -| Generated | -+--------------------------------------+-------------------------------\ -+----------------------------+ -| fb2bef58-88af-4380-8698-e0f18fcf452d | compute.instance.create.start \ -| 2015-01-12T04:03:25.741471 | -| 9b20509a-576b-4995-acfa-1a24ee5cf49f | compute.instance.create.end \ -| 2015-01-12T04:03:28.452495 | -+--------------------------------------+-------------------------------\ -+----------------------------+ -''', sys.stdout.getvalue()) - - -class ShellShadowedArgsTest(test_shell.ShellTestBase): - - def _test_shadowed_args_alarm(self, command, args, method): - self.make_env(test_shell.FAKE_V2_ENV) - cli_args = [ - '--os-project-id', '0ba30185ddf44834914a0b859d244c56', - '--os-user-id', '85f59b3b17484ccb974c50596023bf8c', - '--debug', command, - '--project-id', 'the-project-id-i-want-to-set', - '--user-id', 'the-user-id-i-want-to-set', - '--name', 'project-id-test'] + args - with mock.patch.object(alarms.AlarmManager, method) as mocked: - with mock.patch('ceilometerclient.apiclient.' - 'client.HTTPClient.client_request') as request: - request.site_effect = exceptions.EndpointNotFound - base_shell.main(cli_args) - args, kwargs = mocked.call_args - self.assertEqual('the-project-id-i-want-to-set', - kwargs.get('project_id')) - self.assertEqual('the-user-id-i-want-to-set', - kwargs.get('user_id')) - - def test_shadowed_args_threshold_alarm(self): - cli_args = ['--meter-name', 'cpu', '--threshold', '90'] - self._test_shadowed_args_alarm('alarm-create', cli_args, 'create') - self._test_shadowed_args_alarm('alarm-threshold-create', - cli_args, 'create') - cli_args += ['--alarm_id', '437b7ed0-3733-4054-a877-e9a297b8be85'] - self._test_shadowed_args_alarm('alarm-update', cli_args, 'update') - self._test_shadowed_args_alarm('alarm-threshold-update', - cli_args, 'update') - - def test_shadowed_args_combination_alarm(self): - cli_args = ['--alarm_ids', 'fb16a05a-669d-414e-8bbe-93aa381df6a8', - '--alarm_ids', 'b189bcca-0a7b-49a9-a244-a927ac291881'] - self._test_shadowed_args_alarm('alarm-combination-create', - cli_args, 'create') - cli_args += ['--alarm_id', '437b7ed0-3733-4054-a877-e9a297b8be85'] - self._test_shadowed_args_alarm('alarm-combination-update', - cli_args, 'update') - - def test_shadowed_args_gnocchi_resources_threshold_alarm(self): - cli_args = [ - '--metric', 'cpu', - '--threshold', '80', - '--resource-type', 'instance', - '--resource-id', 'fb16a05a-669d-414e-8bbe-93aa381df6a8', - '--aggregation-method', 'last', - ] - self._test_shadowed_args_alarm('alarm-gnocchi-resources-' - 'threshold-create', - cli_args, 'create') - cli_args += ['--alarm_id', '437b7ed0-3733-4054-a877-e9a297b8be85'] - self._test_shadowed_args_alarm('alarm-gnocchi-resources-' - 'threshold-update', - cli_args, 'update') - - def test_shadowed_args_gnocchi_aggr_by_resources_threshold_alarm(self): - cli_args = [ - '--metric', 'cpu', - '--threshold', '80', - '--resource-type', 'instance', - '--aggregation-method', 'last', - '--query', '"server_group":"my_autoscaling_group"', - ] - self._test_shadowed_args_alarm('alarm-gnocchi-aggregation-' - 'by-resources-threshold-create', - cli_args, 'create') - cli_args += ['--alarm_id', '437b7ed0-3733-4054-a877-e9a297b8be85'] - self._test_shadowed_args_alarm('alarm-gnocchi-aggregation-' - 'by-resources-threshold-update', - cli_args, 'update') - - def test_shadowed_args_gnocchi_aggr_by_metrics_threshold_alarm(self): - cli_args = [ - '-m', 'b3d9d8ab-05e8-439f-89ad-5e978dd2a5eb', - '-m', '009d4faf-c275-46f0-8f2d-670b15bac2b0', - '--threshold', '80', - '--aggregation-method', 'last', - ] - self._test_shadowed_args_alarm('alarm-gnocchi-aggregation-' - 'by-metrics-threshold-create', - cli_args, 'create') - cli_args += ['--alarm_id', '437b7ed0-3733-4054-a877-e9a297b8be85'] - self._test_shadowed_args_alarm('alarm-gnocchi-aggregation-' - 'by-metrics-threshold-update', - cli_args, 'update') - - @mock.patch.object(samples.OldSampleManager, 'create') - def test_shadowed_args_sample_create(self, mocked): - self.make_env(test_shell.FAKE_V2_ENV) - cli_args = [ - '--os-project-id', '0ba30185ddf44834914a0b859d244c56', - '--os-user-id', '85f59b3b17484ccb974c50596023bf8c', - '--debug', 'sample-create', - '--project-id', 'the-project-id-i-want-to-set', - '--user-id', 'the-user-id-i-want-to-set', - '--resource-id', 'b666633d-9bb6-4e05-89c0-ee5a8752fb0b', - '--meter-name', 'cpu', - '--meter-type', 'cumulative', - '--meter-unit', 'ns', - '--sample-volume', '10086', - ] - with mock.patch('ceilometerclient.apiclient.client.' - 'HTTPClient.client_request') as client_request: - client_request.site_effect = exceptions.EndpointNotFound - base_shell.main(cli_args) - args, kwargs = mocked.call_args - self.assertEqual('the-project-id-i-want-to-set', - kwargs.get('project_id')) - self.assertEqual('the-user-id-i-want-to-set', - kwargs.get('user_id')) - - -class ShellCapabilityShowTest(utils.BaseTestCase): - - CAPABILITIES = { - "alarm_storage": { - "storage:production_ready": True - }, - "api": { - "alarms:query:complex": True, - "alarms:query:simple": True - }, - "event_storage": { - "storage:production_ready": True - }, - "storage": { - "storage:production_ready": True - }, - } - - def setUp(self): - super(ShellCapabilityShowTest, self).setUp() - self.cc = mock.Mock() - self.args = mock.Mock() - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_capability_show(self): - _cap = capabilities.Capabilities(mock.Mock, self.CAPABILITIES) - self.cc.capabilities.get.return_value = _cap - - ceilometer_shell.do_capabilities(self.cc, self.args) - self.assertEqual('''\ -+---------------+----------------------------------+ -| Property | Value | -+---------------+----------------------------------+ -| alarm_storage | "storage:production_ready": true | -| api | "alarms:query:complex": true, | -| | "alarms:query:simple": true | -| event_storage | "storage:production_ready": true | -| storage | "storage:production_ready": true | -+---------------+----------------------------------+ -''', sys.stdout.getvalue()) - - -class ShellMeterListCommandTest(utils.BaseTestCase): - - METER = { - "name": 'image', - "resource_id": "resource-id", - "meter": "image", - "project_id": "project", - "type": "gauge", - "unit": "image", - } - - def setUp(self): - super(ShellMeterListCommandTest, self).setUp() - self.cc = mock.Mock() - self.cc.meters.list = mock.Mock() - self.args = mock.MagicMock() - self.args.limit = None - self.args.unique = False - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_meter_list(self): - meter = meters.Meter(mock.Mock(), self.METER) - self.cc.meters.list.return_value = [meter] - - ceilometer_shell.do_meter_list(self.cc, self.args) - self.cc.meters.list.assert_called_once_with(q=[], limit=None, - unique=False) - - self.assertEqual('''\ -+-------+-------+-------+-------------+---------+------------+ -| Name | Type | Unit | Resource ID | User ID | Project ID | -+-------+-------+-------+-------------+---------+------------+ -| image | gauge | image | resource-id | | project | -+-------+-------+-------+-------------+---------+------------+ -''', sys.stdout.getvalue()) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_unique_meter_list(self): - self.args.unique = True - meter = meters.Meter(mock.Mock(), self.METER) - self.cc.meters.list.return_value = [meter] - - ceilometer_shell.do_meter_list(self.cc, self.args) - self.cc.meters.list.assert_called_once_with(q=[], limit=None, - unique=True) - - self.assertEqual('''\ -+-------+-------+-------+-------------+---------+------------+ -| Name | Type | Unit | Resource ID | User ID | Project ID | -+-------+-------+-------+-------------+---------+------------+ -| image | gauge | image | resource-id | | project | -+-------+-------+-------+-------------+---------+------------+ -''', sys.stdout.getvalue()) - - -class ShellResourceListCommandTest(utils.BaseTestCase): - - RESOURCE = { - "source": "openstack", - "resource_id": "resource-id", - "project_id": "project", - "user_id": "user" - } - - def setUp(self): - super(ShellResourceListCommandTest, self).setUp() - self.cc = mock.Mock() - self.cc.resources.list = mock.Mock() - self.args = mock.MagicMock() - self.args.limit = None - self.args.meter_links = None - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_resource_list(self): - resource = resources.Resource(mock.Mock(), self.RESOURCE) - self.cc.resources.list.return_value = [resource] - ceilometer_shell.do_resource_list(self.cc, self.args) - self.cc.resources.list.assert_called_once_with(q=[], - limit=None) - - self.assertEqual('''\ -+-------------+-----------+---------+------------+ -| Resource ID | Source | User ID | Project ID | -+-------------+-----------+---------+------------+ -| resource-id | openstack | user | project | -+-------------+-----------+---------+------------+ -''', sys.stdout.getvalue()) - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_resource_list_with_links(self): - resource = resources.Resource(mock.Mock(), self.RESOURCE) - self.cc.resources.list.return_value = [resource] - ceilometer_shell.do_resource_list(self.cc, self.args) - self.cc.resources.list.assert_called_once_with(q=[], - limit=None) - self.assertEqual('''\ -+-------------+-----------+---------+------------+ -| Resource ID | Source | User ID | Project ID | -+-------------+-----------+---------+------------+ -| resource-id | openstack | user | project | -+-------------+-----------+---------+------------+ -''', sys.stdout.getvalue()) - - -class ShellEventTypeListCommandTest(utils.BaseTestCase): - - EVENT_TYPE = { - "event_type": "test_event" - } - - def setUp(self): - super(ShellEventTypeListCommandTest, self).setUp() - self.cc = mock.Mock() - self.cc.event_types.list = mock.Mock() - self.args = mock.Mock() - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_sample_show(self): - event_type = event_types.EventType(mock.Mock(), self.EVENT_TYPE) - self.cc.event_types.list.return_value = [event_type] - - ceilometer_shell.do_event_type_list(self.cc, self.args) - self.cc.event_types.list.assert_called_once_with() - - self.assertEqual('''\ -+------------+ -| Event Type | -+------------+ -| test_event | -+------------+ -''', sys.stdout.getvalue()) - - -class ShellTraitsListCommandTest(utils.BaseTestCase): - - TRAIT = { - "name": "test", - "value": "test", - "type": "string", - } - - def setUp(self): - super(ShellTraitsListCommandTest, self).setUp() - self.cc = mock.Mock() - self.cc.traits.list = mock.Mock() - self.args = mock.Mock() - self.args.event_type = "test" - self.args.trait_name = "test" - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_trait_list(self): - trait = traits.Trait(mock.Mock(), self.TRAIT) - self.cc.traits.list.return_value = [trait] - - ceilometer_shell.do_trait_list(self.cc, self.args) - self.cc.traits.list.assert_called_once_with(self.args.event_type, - self.args.trait_name) - - self.assertEqual('''\ -+------------+-------+-----------+ -| Trait Name | Value | Data Type | -+------------+-------+-----------+ -| test | test | string | -+------------+-------+-----------+ -''', sys.stdout.getvalue()) - - -class ShellTraitsDescriptionListCommandTest(utils.BaseTestCase): - - TRAIT_DESCRIPTION = { - "name": "test", - "type": "string", - } - - def setUp(self): - super(ShellTraitsDescriptionListCommandTest, self).setUp() - self.cc = mock.Mock() - self.cc.trait_descriptions.list = mock.Mock() - self.args = mock.Mock() - self.args.event_type = "test" - - @mock.patch('sys.stdout', new=six.StringIO()) - def test_traits_description_list(self): - trait_desc = trait_descriptions.TraitDescription( - mock.Mock(), self.TRAIT_DESCRIPTION) - self.cc.trait_descriptions.list.return_value = [trait_desc] - - ceilometer_shell.do_trait_description_list(self.cc, self.args) - self.cc.trait_descriptions.list.assert_called_once_with( - self.args.event_type) - - self.assertEqual('''\ -+------------+-----------+ -| Trait Name | Data Type | -+------------+-----------+ -| test | string | -+------------+-----------+ -''', sys.stdout.getvalue()) diff --git a/ceilometerclient/tests/unit/v2/test_statistics.py b/ceilometerclient/tests/unit/v2/test_statistics.py deleted file mode 100644 index b7924cc..0000000 --- a/ceilometerclient/tests/unit/v2/test_statistics.py +++ /dev/null @@ -1,224 +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. -from ceilometerclient.apiclient import client -from ceilometerclient.apiclient import fake_client -from ceilometerclient.tests.unit import utils -import ceilometerclient.v2.statistics - -base_url = '/v2/meters/instance/statistics' -qry = ('q.field=resource_id&q.field=source&q.op=&q.op=' - '&q.type=&q.type=&q.value=foo&q.value=bar') -period = '&period=60' -groupby = '&groupby=resource_id' -aggregate_query = ("aggregate.func=cardinality&aggregate.param=resource_id" - "&aggregate.func=count") -samples = [ - {u'count': 135, - u'duration_start': u'2013-02-04T10:51:42', - u'min': 1.0, - u'max': 1.0, - u'duration_end': - u'2013-02-05T15:46:09', - u'duration': 1734.0, - u'avg': 1.0, - u'sum': 135.0}, -] -groupby_samples = [ - {u'count': 135, - u'duration_start': u'2013-02-04T10:51:42', - u'min': 1.0, - u'max': 1.0, - u'duration_end': - u'2013-02-05T15:46:09', - u'duration': 1734.0, - u'avg': 1.0, - u'sum': 135.0, - u'groupby': {u'resource_id': u'foo'} - }, - {u'count': 12, - u'duration_start': u'2013-02-04T10:51:42', - u'min': 1.0, - u'max': 1.0, - u'duration_end': - u'2013-02-05T15:46:09', - u'duration': 1734.0, - u'avg': 1.0, - u'sum': 12.0, - u'groupby': {u'resource_id': u'bar'} - }, -] -aggregate_samples = [ - {u'aggregate': {u'cardinality/resource_id': 4.0, u'count': 2.0}, - u'count': 2, - u'duration': 0.442451, - u'duration_end': u'2014-03-12T14:00:21.774154', - u'duration_start': u'2014-03-12T14:00:21.331703', - u'groupby': None, - u'period': 0, - u'period_end': u'2014-03-12T14:00:21.774154', - u'period_start': u'2014-03-12T14:00:21.331703', - u'unit': u'instance', - }, -] -fixtures = { - base_url: - { - 'GET': ( - {}, - samples - ), - }, - '%s?%s' % (base_url, qry): - { - 'GET': ( - {}, - samples - ), - }, - '%s?%s%s' % (base_url, qry, period): - { - 'GET': ( - {}, - samples - ), - }, - '%s?%s%s' % (base_url, qry, groupby): - { - 'GET': ( - {}, - groupby_samples - ), - }, - '%s?%s' % (base_url, aggregate_query): - { - 'GET': ( - {}, - aggregate_samples - ), - } -} - - -class StatisticsManagerTest(utils.BaseTestCase): - - def setUp(self): - super(StatisticsManagerTest, self).setUp() - 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' - ] - self.http_client.assert_called(*expect) - self.assertEqual(1, len(stats)) - self.assertEqual(135, stats[0].count) - - def test_list_by_meter_name_extended(self): - stats = list(self.mgr.list(meter_name='instance', - q=[ - {"field": "resource_id", - "value": "foo"}, - {"field": "source", - "value": "bar"}, - ])) - expect = [ - 'GET', '%s?%s' % (base_url, qry) - ] - self.http_client.assert_called(*expect) - self.assertEqual(1, len(stats)) - self.assertEqual(135, stats[0].count) - - def test_list_by_meter_name_with_period(self): - stats = list(self.mgr.list(meter_name='instance', - q=[ - {"field": "resource_id", - "value": "foo"}, - {"field": "source", - "value": "bar"}, - ], - period=60)) - expect = [ - 'GET', '%s?%s%s' % (base_url, qry, period) - ] - self.http_client.assert_called(*expect) - self.assertEqual(1, len(stats)) - self.assertEqual(135, stats[0].count) - - def test_list_by_meter_name_with_groupby(self): - stats = list(self.mgr.list(meter_name='instance', - q=[ - {"field": "resource_id", - "value": "foo"}, - {"field": "source", - "value": "bar"}, - ], - groupby=['resource_id'])) - expect = [ - 'GET', - '%s?%s%s' % (base_url, qry, groupby) - ] - self.http_client.assert_called(*expect) - self.assertEqual(2, len(stats)) - self.assertEqual(135, stats[0].count) - self.assertEqual(12, stats[1].count) - self.assertEqual('foo', stats[0].groupby.get('resource_id')) - self.assertEqual('bar', stats[1].groupby.get('resource_id')) - - def test_list_by_meter_name_with_groupby_as_str(self): - stats = list(self.mgr.list(meter_name='instance', - q=[ - {"field": "resource_id", - "value": "foo"}, - {"field": "source", - "value": "bar"}, - ], - groupby='resource_id')) - expect = [ - 'GET', - '%s?%s%s' % (base_url, qry, groupby) - ] - self.http_client.assert_called(*expect) - self.assertEqual(2, len(stats)) - self.assertEqual(135, stats[0].count) - self.assertEqual(12, stats[1].count) - self.assertEqual('foo', stats[0].groupby.get('resource_id')) - self.assertEqual('bar', stats[1].groupby.get('resource_id')) - - def test_list_by_meter_name_with_aggregates(self): - aggregates = [ - { - 'func': 'count', - }, - { - 'func': 'cardinality', - 'param': 'resource_id', - }, - ] - stats = list(self.mgr.list(meter_name='instance', - aggregates=aggregates)) - expect = [ - 'GET', - '%s?%s' % (base_url, aggregate_query) - ] - 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')) - self.assertEqual(4.0, stats[0].aggregate.get( - 'cardinality/resource_id', - )) diff --git a/ceilometerclient/tests/unit/v2/test_trait_descriptions.py b/ceilometerclient/tests/unit/v2/test_trait_descriptions.py deleted file mode 100644 index cd3e4fe..0000000 --- a/ceilometerclient/tests/unit/v2/test_trait_descriptions.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright 2014 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -from ceilometerclient.apiclient import client -from ceilometerclient.apiclient import fake_client -from ceilometerclient.tests.unit import utils -import ceilometerclient.v2.trait_descriptions - - -fixtures = { - '/v2/event_types/Foo/traits': { - 'GET': ( - {}, - [ - {'name': 'trait_1', 'type': 'string'}, - {'name': 'trait_2', 'type': 'integer'}, - {'name': 'trait_3', 'type': 'datetime'} - ] - ), - } -} - - -class TraitDescriptionManagerTest(utils.BaseTestCase): - - def setUp(self): - super(TraitDescriptionManagerTest, self).setUp() - 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' - ] - self.http_client.assert_called(*expect) - self.assertEqual(3, len(trait_descriptions)) - for i, vals in enumerate([('trait_1', 'string'), - ('trait_2', 'integer'), - ('trait_3', 'datetime')]): - - name, type = vals - self.assertEqual(trait_descriptions[i].name, name) - self.assertEqual(trait_descriptions[i].type, type) diff --git a/ceilometerclient/tests/unit/v2/test_traits.py b/ceilometerclient/tests/unit/v2/test_traits.py deleted file mode 100644 index 351760b..0000000 --- a/ceilometerclient/tests/unit/v2/test_traits.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright 2014 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -from ceilometerclient.apiclient import client -from ceilometerclient.apiclient import fake_client -from ceilometerclient.tests.unit import utils -import ceilometerclient.v2.traits - - -fixtures = { - '/v2/event_types/Foo/traits/trait_1': { - 'GET': ( - {}, - [ - {'name': 'trait_1', - 'type': 'datetime', - 'value': '2014-01-07T17:22:10.925553'}, - {'name': 'trait_1', - 'type': 'datetime', - 'value': '2014-01-07T17:23:10.925553'} - ] - ), - } -} - - -class TraitManagerTest(utils.BaseTestCase): - - def setUp(self): - super(TraitManagerTest, self).setUp() - 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' - ] - self.http_client.assert_called(*expect) - self.assertEqual(2, len(traits)) - for i, vals in enumerate([('trait_1', - 'datetime', - '2014-01-07T17:22:10.925553'), - ('trait_1', - 'datetime', - '2014-01-07T17:23:10.925553')]): - - name, type, value = vals - self.assertEqual(traits[i].name, name) - self.assertEqual(traits[i].type, type) - self.assertEqual(traits[i].value, value) diff --git a/ceilometerclient/v2/__init__.py b/ceilometerclient/v2/__init__.py deleted file mode 100644 index e8f350d..0000000 --- a/ceilometerclient/v2/__init__.py +++ /dev/null @@ -1,16 +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. - -from ceilometerclient.v2.client import Client # noqa diff --git a/ceilometerclient/v2/alarms.py b/ceilometerclient/v2/alarms.py deleted file mode 100644 index f669624..0000000 --- a/ceilometerclient/v2/alarms.py +++ /dev/null @@ -1,185 +0,0 @@ -# -# Copyright 2013 Red Hat, Inc -# -# 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 warnings - -from ceilometerclient.common import base -from ceilometerclient.common import utils -from ceilometerclient import exc -from ceilometerclient.v2 import options - - -UPDATABLE_ATTRIBUTES = [ - 'name', - 'description', - 'type', - 'state', - 'severity', - 'enabled', - 'alarm_actions', - 'ok_actions', - 'insufficient_data_actions', - 'repeat_actions', - 'project_id', - 'user_id' -] -CREATION_ATTRIBUTES = UPDATABLE_ATTRIBUTES + ['time_constraints'] - - -class Alarm(base.Resource): - def __repr__(self): - return "<Alarm %s>" % self._info - - def __getattr__(self, k): - # Alias to have the Alarm client object - # that look like the Alarm storage object - if k == 'rule': - k = '%s_rule' % self.type - if k == 'id': - return self.alarm_id - return super(Alarm, self).__getattr__(k) - - def delete(self): - return self.manager.delete(self.alarm_id) - - def get_state(self): - state = self.manager.get_state(self.alarm_id) - return state.get('alarm') - - -class AlarmChange(base.Resource): - def __repr__(self): - return "<AlarmChange %s>" % self._info - - def __getattr__(self, k): - return super(AlarmChange, self).__getattr__(k) - - -class AlarmManager(base.Manager): - resource_class = Alarm - - def _path(self, id=None): - return '/v2/alarms/%s' % id if id else '/v2/alarms' - - def list(self, q=None): - return self._list(options.build_url(self._path(), q)) - - def get(self, alarm_id): - try: - return self._list(self._path(alarm_id), expect_single=True)[0] - except IndexError: - return None - - except exc.HTTPNotFound: - # When we try to get a deleted alarm, or - # when an alarm doesn't exist, HTTPNotFound exception occurs. - # Since scenario tests at the time of cleanUp() will not know - # how to handle it, we only return None. - return None - - @classmethod - def _compat_legacy_alarm_kwargs(cls, kwargs, create=False): - cls._compat_counter_rename_kwargs(kwargs, create) - cls._compat_alarm_before_rule_type_kwargs(kwargs, create) - - @staticmethod - def _compat_counter_rename_kwargs(kwargs, create=False): - # NOTE(jd) Compatibility with Havana-2 API - if 'counter_name' in kwargs: - warnings.warn("counter_name has been renamed to meter_name", - DeprecationWarning) - kwargs['meter_name'] = kwargs['counter_name'] - - @staticmethod - def _compat_alarm_before_rule_type_kwargs(kwargs, create=False): - # NOTE(sileht) Compatibility with Havana-3 API - if create and 'type' not in kwargs: - warnings.warn("alarm without type set is deprecated", - DeprecationWarning) - kwargs['type'] = 'threshold' - - for field in ['period', 'evaluation_periods', 'threshold', - 'statistic', 'comparison_operator', 'meter_name']: - if field in kwargs: - kwargs.setdefault('threshold_rule', {})[field] = kwargs[field] - del kwargs[field] - - if 'matching_metadata' in kwargs: - query = [] - for key in kwargs['matching_metadata']: - query.append({'field': key, - 'op': 'eq', - 'value': kwargs['matching_metadata'][key]}) - del kwargs['matching_metadata'] - kwargs['threshold_rule']['query'] = query - - @staticmethod - def _merge_time_constraints(existing_tcs, kwargs): - new_tcs = kwargs.get('time_constraints', []) - if not existing_tcs: - updated_tcs = new_tcs - else: - updated_tcs = [dict(tc) for tc in existing_tcs] - for tc in new_tcs: - for i, old_tc in enumerate(updated_tcs): - # if names match, merge - if old_tc['name'] == tc.get('name'): - utils.merge_nested_dict(updated_tcs[i], tc) - break - else: - updated_tcs.append(tc) - tcs_to_remove = kwargs.get('remove_time_constraints', []) - for tc in updated_tcs: - if tc.get('name') in tcs_to_remove: - updated_tcs.remove(tc) - return updated_tcs - - def create(self, **kwargs): - self._compat_legacy_alarm_kwargs(kwargs, create=True) - new = dict((key, value) for (key, value) in kwargs.items() - if (key in CREATION_ATTRIBUTES - or key.endswith('_rule'))) - return self._create(self._path(), new) - - def update(self, alarm_id, **kwargs): - self._compat_legacy_alarm_kwargs(kwargs) - alarm = self.get(alarm_id) - if alarm is None: - raise exc.CommandError('Alarm not found: %s' % alarm_id) - updated = alarm.to_dict() - updated['time_constraints'] = self._merge_time_constraints( - updated.get('time_constraints', []), kwargs) - kwargs = dict((k, v) for k, v in kwargs.items() - if k in updated and (k in UPDATABLE_ATTRIBUTES - or k.endswith('_rule'))) - utils.merge_nested_dict(updated, kwargs, depth=1) - return self._update(self._path(alarm_id), updated) - - def delete(self, alarm_id): - return self._delete(self._path(alarm_id)) - - def set_state(self, alarm_id, state): - body = self.api.put("%s/state" % self._path(alarm_id), - json=state).json() - return body - - def get_state(self, alarm_id): - body = self.api.get("%s/state" % self._path(alarm_id)).json() - return body - - def get_history(self, alarm_id, q=None): - path = '%s/history' % self._path(alarm_id) - url = options.build_url(path, q) - return self._list(url, obj_class=AlarmChange) diff --git a/ceilometerclient/v2/capabilities.py b/ceilometerclient/v2/capabilities.py deleted file mode 100644 index 9a0f528..0000000 --- a/ceilometerclient/v2/capabilities.py +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2014 Huawei, Inc -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from ceilometerclient.common import base - - -class Capabilities(base.Resource): - def __repr__(self): - return "<Capabilities %s>" % self._info - - -class CapabilitiesManager(base.Manager): - resource_class = Capabilities - - def get(self): - path = "/v2/capabilities" - return Capabilities(self, self.api.get(path).json()) diff --git a/ceilometerclient/v2/client.py b/ceilometerclient/v2/client.py deleted file mode 100644 index e61e43f..0000000 --- a/ceilometerclient/v2/client.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright Ericsson AB 2014. 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 requests - -from ceilometerclient import client as ceiloclient -from ceilometerclient.v2 import alarms -from ceilometerclient.v2 import capabilities -from ceilometerclient.v2 import event_types -from ceilometerclient.v2 import events -from ceilometerclient.v2 import meters -from ceilometerclient.v2 import query -from ceilometerclient.v2 import resources -from ceilometerclient.v2 import samples -from ceilometerclient.v2 import statistics -from ceilometerclient.v2 import trait_descriptions -from ceilometerclient.v2 import traits -from keystoneauth1 import exceptions as ka_exc - - -class Client(object): - """Client for the Ceilometer v2 API. - - :param session: a keystoneauth session object - :type session: keystoneauth1.session.Session - :param str service_type: The default service_type for URL discovery - :param str service_name: The default service_name for URL discovery - :param str interface: The default interface for URL discovery - (Default: public) - :param str region_name: The default region_name for URL discovery - :param str endpoint_override: Always use this endpoint URL for requests - for this ceiloclient - :param auth: An auth plugin to use instead of the session one - :type auth: keystoneauth1.plugin.BaseAuthPlugin - :param str user_agent: The User-Agent string to set - (Default is python-ceilometer-client) - :param int connect_retries: the maximum number of retries that should be - attempted for connection errors - :param logger: A logging object - :type logger: logging.Logger - """ - - def __init__(self, *args, **kwargs): - """Initialize a new client for the Ceilometer v2 API.""" - - if not kwargs.get('auth_plugin') and not kwargs.get('session'): - kwargs['auth_plugin'] = ceiloclient.get_auth_plugin(*args, - **kwargs) - - self.auth_plugin = kwargs.get('auth_plugin') - - self.http_client = ceiloclient._construct_http_client(**kwargs) - self.alarm_client = self._get_redirect_client( - 'alarming', 'aodh', **kwargs) - aodh_enabled = self.alarm_client is not None - if not aodh_enabled: - self.alarm_client = self.http_client - self.event_client = self._get_redirect_client( - 'event', 'panko', **kwargs) - panko_enabled = self.event_client is not None - if not panko_enabled: - self.event_client = self.http_client - - self.meters = meters.MeterManager(self.http_client) - self.samples = samples.OldSampleManager(self.http_client) - self.new_samples = samples.SampleManager(self.http_client) - self.statistics = statistics.StatisticsManager(self.http_client) - self.resources = resources.ResourceManager(self.http_client) - self.alarms = alarms.AlarmManager(self.alarm_client) - self.events = events.EventManager(self.event_client) - self.event_types = event_types.EventTypeManager(self.event_client) - self.traits = traits.TraitManager(self.event_client) - self.trait_descriptions = trait_descriptions.\ - TraitDescriptionManager(self.event_client) - - self.query_samples = query.QuerySamplesManager( - self.http_client) - self.query_alarms = query.QueryAlarmsManager(self.alarm_client) - self.query_alarm_history = query.QueryAlarmHistoryManager( - self.alarm_client) - self.capabilities = capabilities.CapabilitiesManager(self.http_client) - - @staticmethod - def _get_redirect_client(new_service_type, new_service, **ceilo_kwargs): - """Get client for new service manager to redirect to.""" - # NOTE(sileht): the auth_plugin/keystone session cannot be copied - # because they rely on threading module. - auth_plugin = ceilo_kwargs.pop('auth_plugin', None) - session = ceilo_kwargs.pop('session', None) - - kwargs = copy.deepcopy(ceilo_kwargs) - kwargs["service_type"] = new_service_type - endpoint = ceilo_kwargs.get('%s_endpoint' % new_service) - - if session: - # keystone session can be shared between client - ceilo_kwargs['session'] = kwargs['session'] = session - if endpoint: - kwargs['endpoint_override'] = endpoint - elif auth_plugin and kwargs.get('auth_url'): - ceilo_kwargs['auth_plugin'] = auth_plugin - kwargs.pop('endpoint', None) - kwargs['auth_plugin'] = ceiloclient.get_auth_plugin( - endpoint, **kwargs) - else: - # Users may just provide ceilometer endpoint and token, and no - # auth_url, in this case, we need 'aodh_endpoint' also to be - # provided, otherwise we cannot get aodh endpoint from - # keystone, and assume aodh is unavailable. Same applies to panko. - return None - - try: - # NOTE(sileht): try to use redirect - c = ceiloclient._construct_http_client(**kwargs) - c.get("/") - return c - except ka_exc.EndpointNotFound: - return None - except requests.exceptions.ConnectionError: - return None diff --git a/ceilometerclient/v2/event_types.py b/ceilometerclient/v2/event_types.py deleted file mode 100644 index 0f44931..0000000 --- a/ceilometerclient/v2/event_types.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2014 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from ceilometerclient.common import base - - -class EventType(base.Resource): - def __repr__(self): - return "<EventType %s>" % self._info - - -def object_class_str(mgr, value, loaded): - return EventType(mgr, {"event_type": value}, loaded) - - -class EventTypeManager(base.Manager): - - def list(self): - return self._list('/v2/event_types', obj_class=object_class_str) diff --git a/ceilometerclient/v2/events.py b/ceilometerclient/v2/events.py deleted file mode 100644 index 6cd46d2..0000000 --- a/ceilometerclient/v2/events.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright 2014 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from ceilometerclient.common import base -from ceilometerclient.v2 import options - - -class Event(base.Resource): - def __repr__(self): - return "<Event %s>" % self._info - - def __getattr__(self, k): - if k == 'id': - return self.message_id - return super(Event, self).__getattr__(k) - - -class EventManager(base.Manager): - resource_class = Event - - def list(self, q=None, limit=None): - path = '/v2/events' - params = ['limit=%s' % limit] if limit else None - return self._list(options.build_url(path, q, params)) - - def get(self, message_id): - path = '/v2/events/%s' - try: - return self._list(path % message_id, expect_single=True)[0] - except IndexError: - return None diff --git a/ceilometerclient/v2/meters.py b/ceilometerclient/v2/meters.py deleted file mode 100644 index 015c04c..0000000 --- a/ceilometerclient/v2/meters.py +++ /dev/null @@ -1,38 +0,0 @@ -# -# Copyright 2013 Red Hat, Inc -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from ceilometerclient.common import base -from ceilometerclient.v2 import options - - -class Meter(base.Resource): - def __repr__(self): - return "<Meter %s>" % self._info - - -class MeterManager(base.Manager): - resource_class = Meter - - def list(self, q=None, limit=None, unique=False): - path = '/v2/meters' - params = [] - - if limit: - params.append('limit=%s' % limit) - - if unique: - params.append('unique=%s' % str(unique)) - - return self._list(options.build_url(path, q, params)) diff --git a/ceilometerclient/v2/options.py b/ceilometerclient/v2/options.py deleted file mode 100644 index b586542..0000000 --- a/ceilometerclient/v2/options.py +++ /dev/null @@ -1,129 +0,0 @@ -# -# 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 re - -from six.moves import urllib - -OP_LOOKUP = {'!=': 'ne', - '>=': 'ge', - '<=': 'le', - '>': 'gt', - '<': 'lt', - '=': 'eq'} - -OP_LOOKUP_KEYS = '|'.join(sorted(OP_LOOKUP.keys(), key=len, reverse=True)) -OP_SPLIT_RE = re.compile(r'(%s)' % OP_LOOKUP_KEYS) - -DATA_TYPE_RE = re.compile(r'^(string|integer|float|datetime|boolean)(::)(.+)$') - - -def build_url(path, q, params=None): - """Convert list of dicts and a list of params to query url format. - - This will convert the following: - "[{field=this,op=le,value=34}, - {field=that,op=eq,value=foo,type=string}], - ['foo=bar','sna=fu']" - to: - "?q.field=this&q.field=that& - q.op=le&q.op=eq& - q.type=&q.type=string& - q.value=34&q.value=foo& - foo=bar&sna=fu" - """ - if q: - query_params = {'q.field': [], - 'q.value': [], - 'q.op': [], - 'q.type': []} - - for query in q: - for name in ['field', 'op', 'value', 'type']: - query_params['q.%s' % name].append(query.get(name, '')) - - # Transform the dict to a sequence of two-element tuples in fixed - # order, then the encoded string will be consistent in Python 2&3. - new_qparams = sorted(query_params.items(), key=lambda x: x[0]) - path += "?" + urllib.parse.urlencode(new_qparams, doseq=True) - - if params: - for p in params: - path += '&%s' % p - elif params: - path += '?%s' % params[0] - for p in params[1:]: - path += '&%s' % p - return path - - -def cli_to_array(cli_query): - """Convert CLI list of queries to the Python API format. - - This will convert the following: - "this<=34;that=string::foo" - to - "[{field=this,op=le,value=34,type=''}, - {field=that,op=eq,value=foo,type=string}]" - - """ - - if cli_query is None: - return None - - def split_by_op(query): - """Split a single query string to field, operator, value.""" - - def _value_error(message): - raise ValueError('invalid query %(query)s: missing %(message)s' % - {'query': query, 'message': message}) - - try: - field, operator, value = OP_SPLIT_RE.split(query, maxsplit=1) - except ValueError: - _value_error('operator') - - if not len(field): - _value_error('field') - - if not len(value): - _value_error('value') - - return field.strip(), operator, value.strip() - - def split_by_data_type(query_value): - frags = DATA_TYPE_RE.match(query_value) - - # The second match is the separator. Return a list without it if - # a type identifier was found. - return frags.group(1, 3) if frags else None - - opts = [] - queries = cli_query.split(';') - for q in queries: - query = split_by_op(q) - opt = {} - opt['field'] = query[0] - opt['op'] = OP_LOOKUP[query[1]] - - # Allow the data type of the value to be specified via <type>::<value>, - # where type can be one of integer, string, float, datetime, boolean - value_frags = split_by_data_type(query[2]) - if not value_frags: - opt['value'] = query[2] - opt['type'] = '' - else: - opt['type'] = value_frags[0] - opt['value'] = value_frags[1] - opts.append(opt) - return opts diff --git a/ceilometerclient/v2/query.py b/ceilometerclient/v2/query.py deleted file mode 100644 index 9d540b2..0000000 --- a/ceilometerclient/v2/query.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright Ericsson AB 2014. All rights reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from ceilometerclient.common import base -from ceilometerclient.v2 import alarms -from ceilometerclient.v2 import samples - - -class QueryManager(base.Manager): - path_suffix = None - - def query(self, filter=None, orderby=None, limit=None): - query = {} - if filter: - query["filter"] = filter - if orderby: - query["orderby"] = orderby - if limit: - query["limit"] = limit - - url = '/v2/query%s' % self.path_suffix - - body = self.api.post(url, json=query).json() - - if body: - return [self.resource_class(self, b) for b in body] - else: - return [] - - -class QuerySamplesManager(QueryManager): - resource_class = samples.Sample - path_suffix = '/samples' - - -class QueryAlarmsManager(QueryManager): - resource_class = alarms.Alarm - path_suffix = '/alarms' - - -class QueryAlarmHistoryManager(QueryManager): - resource_class = alarms.AlarmChange - path_suffix = '/alarms/history' diff --git a/ceilometerclient/v2/resources.py b/ceilometerclient/v2/resources.py deleted file mode 100644 index d516ee1..0000000 --- a/ceilometerclient/v2/resources.py +++ /dev/null @@ -1,45 +0,0 @@ -# -# Copyright 2013 Red Hat, Inc -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from ceilometerclient.common import base -from ceilometerclient.v2 import options - - -class Resource(base.Resource): - def __repr__(self): - return "<Resource %s>" % self._info - - def __getattr__(self, k): - if k == 'id': - return self.resource_id - return super(Resource, self).__getattr__(k) - - -class ResourceManager(base.Manager): - resource_class = Resource - - def list(self, q=None, links=None, limit=None): - path = '/v2/resources' - params = ['meter_links=%d' % (1 if links else 0)] - if limit: - params.append('limit=%s' % limit) - return self._list(options.build_url(path, q, params)) - - def get(self, resource_id): - path = '/v2/resources/%s' % resource_id - try: - return self._list(path, expect_single=True)[0] - except IndexError: - return None diff --git a/ceilometerclient/v2/samples.py b/ceilometerclient/v2/samples.py deleted file mode 100644 index 8752a0d..0000000 --- a/ceilometerclient/v2/samples.py +++ /dev/null @@ -1,106 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from ceilometerclient.common import base -from ceilometerclient.v2 import options - -CREATION_ATTRIBUTES = ('source', - 'counter_name', - 'counter_type', - 'counter_unit', - 'counter_volume', - 'user_id', - 'project_id', - 'resource_id', - 'timestamp', - 'resource_metadata') - - -class OldSample(base.Resource): - """Represents API v2 OldSample object. - - Model definition: - https://docs.openstack.org/ceilometer/latest/webapi/v2.html#OldSample - """ - def __repr__(self): - return "<OldSample %s>" % self._info - - -class OldSampleManager(base.Manager): - resource_class = OldSample - - @staticmethod - def _path(counter_name=None): - return '/v2/meters/%s' % counter_name if counter_name else '/v2/meters' - - def list(self, meter_name=None, q=None, limit=None): - path = self._path(counter_name=meter_name) - params = ['limit=%s' % str(limit)] if limit else None - return self._list(options.build_url(path, q, params)) - - def create(self, **kwargs): - direct = kwargs.pop('direct', False) - new = dict((key, value) for (key, value) in kwargs.items() - if key in CREATION_ATTRIBUTES) - url = self._path(counter_name=kwargs['counter_name'])+( - '?direct=%s' % (str(direct)) if direct else '') - body = self.api.post(url, json=[new]).json() - if body: - return [OldSample(self, b) for b in body] - - def create_list(self, sample_list=None, **kwargs): - sample_dict = {} - direct = kwargs.pop('direct', False) - - for sample_body in sample_list: - sample = dict((key, value) for (key, value) in sample_body.items() - if key in CREATION_ATTRIBUTES) - sample_dict.setdefault( - sample_body["counter_name"], [] - ).append(sample) - - sample_return_list = [] - - for (counter_name, sample_body) in sample_dict.items(): - url = self._path(counter_name=counter_name)+( - '?direct=%s' % (str(direct)) if direct else '') - body = self.api.post(url, json=sample_body).json() - - if body: - sample_return_list.extend([OldSample(self, b) for b in body]) - return sample_return_list - - -class Sample(base.Resource): - """Represents API v2 Sample object. - - Model definition: - https://docs.openstack.org/ceilometer/latest/webapi/v2.html#Sample - """ - def __repr__(self): - return "<Sample %s>" % self._info - - -class SampleManager(base.Manager): - resource_class = Sample - - def list(self, q=None, limit=None): - params = ['limit=%s' % str(limit)] if limit else None - return self._list(options.build_url("/v2/samples", q, params)) - - def get(self, sample_id): - path = "/v2/samples/" + sample_id - try: - return self._list(path, expect_single=True)[0] - except IndexError: - return None diff --git a/ceilometerclient/v2/shell.py b/ceilometerclient/v2/shell.py deleted file mode 100644 index ca9e264..0000000 --- a/ceilometerclient/v2/shell.py +++ /dev/null @@ -1,1285 +0,0 @@ -# -# Copyright 2013-2016 Red Hat, Inc -# Copyright Ericsson AB 2014. 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 argparse -import functools -import json -import warnings - -from oslo_serialization import jsonutils -from oslo_utils import strutils -import six -from six import moves - -from ceilometerclient.common import utils -from ceilometerclient import exc -from ceilometerclient.v2 import options - - -ALARM_STATES = ['ok', 'alarm', 'insufficient data'] -ALARM_SEVERITY = ['low', 'moderate', 'critical'] -ALARM_OPERATORS = ['lt', 'le', 'eq', 'ne', 'ge', 'gt'] -ALARM_COMBINATION_OPERATORS = ['and', 'or'] -STATISTICS = ['max', 'min', 'avg', 'sum', 'count'] -GNOCCHI_AGGREGATION = ['last', 'min', 'median', 'sum', - 'std', 'first', 'mean', 'count', - 'moving-average', 'max'] -GNOCCHI_AGGREGATION.extend(['%spct' % num for num in moves.xrange(1, 100)]) - -AGGREGATES = {'avg': 'Avg', - 'count': 'Count', - 'max': 'Max', - 'min': 'Min', - 'sum': 'Sum', - 'stddev': 'Standard deviation', - 'cardinality': 'Cardinality'} -OPERATORS_STRING = dict(gt='>', ge='>=', - lt='<', le="<=", - eq='==', ne='!=') -ORDER_DIRECTIONS = ['asc', 'desc'] -COMPLEX_OPERATORS = ['and', 'or'] -SIMPLE_OPERATORS = ["=", "!=", "<", "<=", '>', '>='] - -DEFAULT_API_LIMIT = ('API server limits result to <default_api_return_limit> ' - 'rows if no limit provided. Option is configured in ' - 'ceilometer.conf [api] group') - - -class NotEmptyAction(argparse.Action): - def __call__(self, parser, namespace, values, option_string=None): - values = values or getattr(namespace, self.dest) - if not values or values.isspace(): - raise exc.CommandError('%s should not be empty' % self.dest) - setattr(namespace, self.dest, values) - - -def obsoleted_by(new_dest): - class ObsoletedByAction(argparse.Action): - def __call__(self, parser, namespace, values, option_string=None): - old_dest = option_string or self.dest - print('%s is obsolete! See help for more details.' % old_dest) - setattr(namespace, new_dest, values) - return ObsoletedByAction - - -@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, - action=NotEmptyAction, help='Name of meter to list statistics for.') -@utils.arg('-p', '--period', metavar='<PERIOD>', - help='Period in seconds over which to group samples.') -@utils.arg('-g', '--groupby', metavar='<FIELD>', action='append', - help='Field for group by.') -@utils.arg('-a', '--aggregate', metavar='<FUNC>[<-<PARAM>]', action='append', - default=[], help=('Function for data aggregation. ' - 'Available aggregates are: ' - '%s.' % ", ".join(AGGREGATES.keys()))) -def do_statistics(cc, args): - """List the statistics for a meter.""" - aggregates = [] - for a in args.aggregate: - aggregates.append(dict(zip(('func', 'param'), a.split("<-")))) - api_args = {'meter_name': args.meter, - 'q': options.cli_to_array(args.query), - 'period': args.period, - 'groupby': args.groupby, - 'aggregates': aggregates} - try: - statistics = cc.statistics.list(**api_args) - except exc.HTTPNotFound: - raise exc.CommandError('Samples not found: %s' % args.meter) - else: - fields_display = {'duration': 'Duration', - 'duration_end': 'Duration End', - 'duration_start': 'Duration Start', - 'period': 'Period', - 'period_end': 'Period End', - 'period_start': 'Period Start', - 'groupby': 'Group By'} - fields_display.update(AGGREGATES) - fields = ['period', 'period_start', 'period_end'] - if args.groupby: - fields.append('groupby') - if args.aggregate: - for a in aggregates: - if 'param' in a: - fields.append("%(func)s/%(param)s" % a) - else: - fields.append(a['func']) - for stat in statistics: - stat.__dict__.update(stat.aggregate) - else: - fields.extend(['max', 'min', 'avg', 'sum', 'count']) - fields.extend(['duration', 'duration_start', 'duration_end']) - cols = [fields_display.get(f, f) for f in fields] - utils.print_list(statistics, fields, cols) - - -@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>', - action=NotEmptyAction, help='Name of meter to show samples for.') -@utils.arg('-l', '--limit', metavar='<NUMBER>', - help='Maximum number of samples to return. %s' % DEFAULT_API_LIMIT) -def do_sample_list(cc, args): - """List the samples (return OldSample objects if -m/--meter is set).""" - if not args.meter: - return _do_sample_list(cc, args) - else: - return _do_old_sample_list(cc, args) - - -def _do_old_sample_list(cc, args): - fields = {'meter_name': args.meter, - 'q': options.cli_to_array(args.query), - 'limit': args.limit} - try: - samples = cc.samples.list(**fields) - except exc.HTTPNotFound: - raise exc.CommandError('Samples not found: %s' % args.meter) - else: - field_labels = ['Resource ID', 'Name', 'Type', 'Volume', 'Unit', - 'Timestamp'] - fields = ['resource_id', 'counter_name', 'counter_type', - 'counter_volume', 'counter_unit', 'timestamp'] - utils.print_list(samples, fields, field_labels, sortby=None) - - -def _do_sample_list(cc, args): - fields = { - 'q': options.cli_to_array(args.query), - 'limit': args.limit - } - samples = cc.new_samples.list(**fields) - field_labels = ['ID', 'Resource ID', 'Name', 'Type', 'Volume', 'Unit', - 'Timestamp'] - fields = ['id', 'resource_id', 'meter', 'type', 'volume', 'unit', - 'timestamp'] - utils.print_list(samples, fields, field_labels, sortby=None) - - -@utils.arg('sample_id', metavar='<SAMPLE_ID>', action=NotEmptyAction, - help='ID (aka message ID) of the sample to show.') -def do_sample_show(cc, args): - '''Show a sample.''' - try: - sample = cc.new_samples.get(args.sample_id) - except exc.HTTPNotFound: - raise exc.CommandError('Sample not found: %s' % args.sample_id) - - fields = ['id', 'meter', 'volume', 'type', 'unit', 'source', - 'resource_id', 'user_id', 'project_id', - 'timestamp', 'recorded_at', 'metadata'] - data = dict((f, getattr(sample, f, '')) for f in fields) - utils.print_dict(data, wrap=72) - - -def _restore_shadowed_arg(shadowed, observed): - def wrapper(func): - @functools.wraps(func) - def wrapped(cc, args): - v = getattr(args, observed, None) - setattr(args, shadowed, v) - return func(cc, args) - return wrapped - return wrapper - - -@utils.arg('--project-id', metavar='<SAMPLE_PROJECT_ID>', - dest='sample_project_id', - help='Tenant to associate with sample ' - '(configurable by admin users only).') -@utils.arg('--user-id', metavar='<SAMPLE_USER_ID>', - dest='sample_user_id', - help='User to associate with sample ' - '(configurable by admin users only).') -@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, - 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, - help='The meter unit.') -@utils.arg('--sample-volume', metavar='<SAMPLE_VOLUME>', required=True, - help='The sample volume.') -@utils.arg('--resource-metadata', metavar='<RESOURCE_METADATA>', - help='Resource metadata. Provided value should be a set of ' - 'key-value pairs e.g. {"key":"value"}.') -@utils.arg('--timestamp', metavar='<TIMESTAMP>', - help='The sample timestamp.') -@utils.arg('--direct', metavar='<DIRECT>', default=False, - help='Post sample to storage directly.') -@_restore_shadowed_arg('project_id', 'sample_project_id') -@_restore_shadowed_arg('user_id', 'sample_user_id') -def do_sample_create(cc, args={}): - """Create a sample.""" - arg_to_field_mapping = { - 'meter_name': 'counter_name', - 'meter_unit': 'counter_unit', - 'meter_type': 'counter_type', - 'sample_volume': 'counter_volume', - } - fields = {} - for var in vars(args).items(): - k, v = var[0], var[1] - if v is not None: - if k == 'resource_metadata': - try: - fields[k] = json.loads(v) - except ValueError: - msg = ('Invalid resource metadata, it should be a json' - ' string, like: \'{"foo":"bar"}\'') - raise exc.CommandError(msg) - else: - fields[arg_to_field_mapping.get(k, k)] = v - sample = cc.samples.create(**fields) - fields = ['counter_name', 'user_id', 'resource_id', - 'timestamp', 'message_id', 'source', 'counter_unit', - 'counter_volume', 'project_id', 'resource_metadata', - 'counter_type'] - data = dict([(f.replace('counter_', ''), getattr(sample[0], f, '')) - for f in fields]) - utils.print_dict(data, wrap=72) - - -@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('-l', '--limit', metavar='<NUMBER>', - help='Maximum number of meters to return. %s' % DEFAULT_API_LIMIT) -@utils.arg('--unique', dest='unique', - metavar='{True|False}', - type=lambda v: strutils.bool_from_string(v, True), - help='Retrieves unique list of meters.') -def do_meter_list(cc, args={}): - """List the user's meters.""" - meters = cc.meters.list(q=options.cli_to_array(args.query), - limit=args.limit, unique=args.unique) - field_labels = ['Name', 'Type', 'Unit', 'Resource ID', 'User ID', - 'Project ID'] - fields = ['name', 'type', 'unit', 'resource_id', 'user_id', - 'project_id'] - utils.print_list(meters, fields, field_labels, - sortby=0) - - -@utils.arg('samples_list', metavar='<SAMPLES_LIST>', action=NotEmptyAction, - help='Json array with samples to create.') -@utils.arg('--direct', metavar='<DIRECT>', default=False, - help='Post samples to storage directly.') -def do_sample_create_list(cc, args={}): - """Create a sample list.""" - sample_list_array = json.loads(args.samples_list) - samples = cc.samples.create_list(sample_list_array, direct=args.direct) - field_labels = ['Resource ID', 'Name', 'Type', 'Volume', 'Unit', - 'Timestamp'] - fields = ['resource_id', 'counter_name', 'counter_type', - 'counter_volume', 'counter_unit', 'timestamp'] - utils.print_list(samples, fields, field_labels, sortby=None) - - -def _display_alarm_list(alarms, sortby=None): - # omit action initially to keep output width sane - # (can switch over to vertical formatting when available from CLIFF) - field_labels = ['Alarm ID', 'Name', 'State', 'Severity', 'Enabled', - 'Continuous', 'Alarm condition', 'Time constraints'] - fields = ['alarm_id', 'name', 'state', 'severity', 'enabled', - 'repeat_actions', 'rule', 'time_constraints'] - utils.print_list( - alarms, fields, field_labels, - formatters={'rule': alarm_rule_formatter, - 'time_constraints': time_constraints_formatter_brief}, - sortby=sortby) - - -def _display_rule(type, rule): - if type == 'threshold': - return ('%(statistic)s(%(meter_name)s) %(comparison_operator)s ' - '%(threshold)s during %(evaluation_periods)s x %(period)ss' % - { - 'meter_name': rule['meter_name'], - 'threshold': rule['threshold'], - 'statistic': rule['statistic'], - 'evaluation_periods': rule['evaluation_periods'], - 'period': rule['period'], - 'comparison_operator': OPERATORS_STRING.get( - rule['comparison_operator']) - }) - elif type == 'combination': - return ('combinated states (%(operator)s) of %(alarms)s' % { - 'operator': rule['operator'].upper(), - 'alarms': ", ".join(rule['alarm_ids'])}) - else: - # just dump all - return "\n".join(["%s: %s" % (f, v) - for f, v in six.iteritems(rule)]) - - -def alarm_rule_formatter(alarm): - return _display_rule(alarm.type, alarm.rule) - - -def _display_time_constraints_brief(time_constraints): - if time_constraints: - return ', '.join('%(name)s at %(start)s %(timezone)s for %(duration)ss' - % { - 'name': tc['name'], - 'start': tc['start'], - 'duration': tc['duration'], - 'timezone': tc.get('timezone', '') - } - for tc in time_constraints) - else: - return 'None' - - -def time_constraints_formatter_brief(alarm): - return _display_time_constraints_brief(getattr(alarm, - 'time_constraints', - None)) - - -def _infer_type(detail): - if 'type' in detail: - return detail['type'] - elif 'meter_name' in detail['rule']: - return 'threshold' - elif 'alarms' in detail['rule']: - return 'combination' - else: - return 'unknown' - - -def alarm_change_detail_formatter(change): - detail = json.loads(change.detail) - fields = [] - if change.type == 'state transition': - fields.append('state: %s' % detail['state']) - elif change.type == 'creation' or change.type == 'deletion': - for k in ['name', 'description', 'type', 'rule', 'severity']: - if k == 'rule': - fields.append('rule: %s' % _display_rule(detail['type'], - detail[k])) - else: - fields.append('%s: %s' % (k, detail[k])) - if 'time_constraints' in detail: - fields.append('time_constraints: %s' % - _display_time_constraints_brief( - detail['time_constraints'])) - elif change.type == 'rule change': - for k, v in six.iteritems(detail): - if k == 'rule': - fields.append('rule: %s' % _display_rule(_infer_type(detail), - v)) - else: - fields.append('%s: %s' % (k, v)) - return '\n'.join(fields) - - -@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.') -def do_alarm_list(cc, args={}): - """List the user's alarms.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - alarms = cc.alarms.list(q=options.cli_to_array(args.query)) - _display_alarm_list(alarms, sortby=0) - - -def alarm_query_formater(alarm): - qs = [] - for q in alarm.rule['query']: - qs.append('%s %s %s' % ( - q['field'], OPERATORS_STRING.get(q['op']), q['value'])) - return r' AND\n'.join(qs) - - -def time_constraints_formatter_full(alarm): - time_constraints = [] - for tc in alarm.time_constraints: - lines = [] - for k in ['name', 'description', 'start', 'duration', 'timezone']: - if k in tc and tc[k]: - lines.append(r'%s: %s' % (k, tc[k])) - time_constraints.append('{' + r',\n '.join(lines) + '}') - return '[' + r',\n '.join(time_constraints) + ']' - - -def _display_alarm(alarm): - fields = ['name', 'description', 'type', - 'state', 'severity', 'enabled', 'alarm_id', 'user_id', - 'project_id', 'alarm_actions', 'ok_actions', - 'insufficient_data_actions', 'repeat_actions'] - data = dict([(f, getattr(alarm, f, '')) for f in fields]) - data.update(alarm.rule) - if alarm.type == 'threshold': - data['query'] = alarm_query_formater(alarm) - if alarm.time_constraints: - data['time_constraints'] = time_constraints_formatter_full(alarm) - utils.print_dict(data, wrap=72) - - -@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', - action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS, - dest='alarm_id_deprecated') -@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?', - action=NotEmptyAction, help='ID of the alarm to show.') -def do_alarm_show(cc, args={}): - """Show an alarm.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - alarm = cc.alarms.get(args.alarm_id) - # alarm.get actually catches the HTTPNotFound exception and turns the - # result into None if the alarm wasn't found. - if alarm is None: - raise exc.CommandError('Alarm not found: %s' % args.alarm_id) - else: - _display_alarm(alarm) - - -def common_alarm_arguments(create=False): - def _wrapper(func): - @utils.arg('--name', metavar='<NAME>', required=create, - help='Name of the alarm (must be unique per tenant).') - @utils.arg('--project-id', metavar='<ALARM_PROJECT_ID>', - dest='alarm_project_id', - help='Tenant to associate with alarm ' - '(configurable by admin users only).') - @utils.arg('--user-id', metavar='<ALARM_USER_ID>', - dest='alarm_user_id', - help='User to associate with alarm ' - '(configurable by admin users only).') - @utils.arg('--description', metavar='<DESCRIPTION>', - help='Free text description of the alarm.') - @utils.arg('--state', metavar='<STATE>', - help='State of the alarm, one of: ' + str(ALARM_STATES)) - @utils.arg('--severity', metavar='<SEVERITY>', - help='Severity of the alarm, one of: ' - + str(ALARM_SEVERITY)) - @utils.arg('--enabled', - type=lambda v: strutils.bool_from_string(v, True), - metavar='{True|False}', - help='True if alarm evaluation/actioning is enabled.') - @utils.arg('--alarm-action', dest='alarm_actions', - metavar='<Webhook URL>', action='append', default=None, - help=('URL to invoke when state transitions to alarm. ' - 'May be used multiple times.')) - @utils.arg('--ok-action', dest='ok_actions', - metavar='<Webhook URL>', action='append', default=None, - help=('URL to invoke when state transitions to OK. ' - 'May be used multiple times.')) - @utils.arg('--insufficient-data-action', - dest='insufficient_data_actions', - metavar='<Webhook URL>', action='append', default=None, - help=('URL to invoke when state transitions to ' - 'insufficient data. May be used multiple times.')) - @utils.arg('--time-constraint', dest='time_constraints', - metavar='<Time Constraint>', action='append', - default=None, - help=('Only evaluate the alarm if the time at evaluation ' - 'is within this time constraint. Start point(s) of ' - 'the constraint are specified with a cron expression' - ', whereas its duration is given in seconds. ' - 'Can be specified multiple times for multiple ' - 'time constraints, format is: ' - 'name=<CONSTRAINT_NAME>;start=<CRON>;' - 'duration=<SECONDS>;[description=<DESCRIPTION>;' - '[timezone=<IANA Timezone>]]')) - @utils.arg('--repeat-actions', dest='repeat_actions', - metavar='{True|False}', type=strutils.bool_from_string, - help=('True if actions should be repeatedly notified ' - 'while alarm remains in target state.')) - @functools.wraps(func) - def _wrapped(*args, **kwargs): - return func(*args, **kwargs) - return _wrapped - return _wrapper - - -def common_alarm_gnocchi_arguments(rule_namespace, create=False): - def _wrapper(func): - @utils.arg('--granularity', type=int, metavar='<GRANULARITY>', - dest=rule_namespace + '/granularity', - help='Length of each period (seconds) to evaluate over.') - @utils.arg('--evaluation-periods', type=int, metavar='<COUNT>', - dest=rule_namespace + '/evaluation_periods', - help='Number of periods to evaluate over.') - @utils.arg('--aggregation-method', metavar='<AGGREATION>', - dest=rule_namespace + '/aggregation_method', - required=create, - help=('Aggregation method to use, one of: ' + - str(GNOCCHI_AGGREGATION) + '.')) - @utils.arg('--comparison-operator', metavar='<OPERATOR>', - dest=rule_namespace + '/comparison_operator', - help=('Operator to compare with, one of: ' + - str(ALARM_OPERATORS) + '.')) - @utils.arg('--threshold', type=float, metavar='<THRESHOLD>', - dest=rule_namespace + '/threshold', - required=create, - help='Threshold to evaluate against.') - @functools.wraps(func) - def _wrapped(*args, **kwargs): - return func(*args, **kwargs) - return _wrapped - return _wrapper - - -def common_alarm_gnocchi_aggregation_by_metrics_arguments(create=False): - def _wrapper(func): - @utils.arg('-m', '--metrics', metavar='<METRICS>', - dest=('gnocchi_aggregation_by_metrics_threshold_rule/' - 'metrics'), - action='append', required=create, - help='Metric to evaluate against.') - @functools.wraps(func) - def _wrapped(*args, **kwargs): - return func(*args, **kwargs) - return _wrapped - return _wrapper - - -def common_alarm_gnocchi_aggregation_by_resources_arguments(create=False): - def _wrapper(func): - @utils.arg('-m', '--metric', metavar='<METRIC>', - dest=('gnocchi_aggregation_by_resources_threshold_rule/' - 'metric'), - required=create, - help='Metric to evaluate against.') - @utils.arg('--resource-type', metavar='<RESOURCE_TYPE>', - dest=('gnocchi_aggregation_by_resources_threshold_rule/' - 'resource_type'), - required=create, - help='Resource_type to evaluate against.') - @utils.arg('--query', metavar='<QUERY>', - dest=('gnocchi_aggregation_by_resources_threshold_rule/' - 'query'), - required=create, - help=('Gnocchi resources search query filter')) - @functools.wraps(func) - def _wrapped(*args, **kwargs): - return func(*args, **kwargs) - return _wrapped - return _wrapper - - -def common_alarm_gnocchi_resources_arguments(create=False): - def _wrapper(func): - @utils.arg('-m', '--metric', metavar='<METRIC>', - dest='gnocchi_resources_threshold_rule/metric', - required=create, - help='Metric to evaluate against.') - @utils.arg('--resource-type', metavar='<RESOURCE_TYPE>', - dest='gnocchi_resources_threshold_rule/resource_type', - required=create, - help='Resource_type to evaluate against.') - @utils.arg('--resource-id', metavar='<RESOURCE_ID>', - dest='gnocchi_resources_threshold_rule/resource_id', - required=create, - help=('Resource id to evaluate against')) - @functools.wraps(func) - def _wrapped(*args, **kwargs): - return func(*args, **kwargs) - return _wrapped - return _wrapper - - -def common_alarm_event_arguments(): - def _wrapper(func): - @utils.arg('--event-type', dest='event_rule/event_type', - metavar='<EVENT_TYPE>', - help='Event type for event alarm.') - @utils.arg('-q', '--query', dest='event_rule/query', metavar='<QUERY>', - help=('key[op]data_type::value; list for filtering events. ' - 'data_type is optional, but if supplied must be ' - 'string, integer, float or datetime.')) - @functools.wraps(func) - def _wrapped(*args, **kwargs): - return func(*args, **kwargs) - return _wrapped - return _wrapper - - -@common_alarm_arguments(create=True) -@utils.arg('--period', type=int, metavar='<PERIOD>', - help='Length of each period (seconds) to evaluate over.') -@utils.arg('--evaluation-periods', type=int, metavar='<COUNT>', - help='Number of periods to evaluate over.') -@utils.arg('-m', '--meter-name', metavar='<METRIC>', required=True, - help='Metric to evaluate against.') -@utils.arg('--statistic', metavar='<STATISTIC>', - help='Statistic to evaluate, one of: ' + str(STATISTICS) + '.') -@utils.arg('--comparison-operator', metavar='<OPERATOR>', - help='Operator to compare with, one of: ' + str(ALARM_OPERATORS) + - '.') -@utils.arg('--threshold', type=float, metavar='<THRESHOLD>', required=True, - help='Threshold to evaluate against.') -@utils.arg('--matching-metadata', dest='matching_metadata', - metavar='<Matching Metadata>', action='append', default=None, - help=('A meter should match this resource metadata (key=value) ' - 'additionally to the meter_name.')) -@_restore_shadowed_arg('project_id', 'alarm_project_id') -@_restore_shadowed_arg('user_id', 'alarm_user_id') -def do_alarm_create(cc, args={}): - """Create a new alarm (Deprecated). Use alarm-threshold-create instead.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) - fields = utils.args_array_to_list_of_dicts(fields, "time_constraints") - fields = utils.args_array_to_dict(fields, "matching_metadata") - alarm = cc.alarms.create(**fields) - _display_alarm(alarm) - - -@common_alarm_arguments(create=True) -@common_alarm_gnocchi_arguments('gnocchi_resources_threshold_rule', - create=True) -@common_alarm_gnocchi_resources_arguments(create=True) -@_restore_shadowed_arg('project_id', 'alarm_project_id') -@_restore_shadowed_arg('user_id', 'alarm_user_id') -def do_alarm_gnocchi_resources_threshold_create(cc, args={}): - """Create a new alarm based on computed statistics.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) - fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints') - fields = utils.key_with_slash_to_nested_dict(fields) - fields['type'] = 'gnocchi_resources_threshold' - alarm = cc.alarms.create(**fields) - _display_alarm(alarm) - - -@common_alarm_arguments(create=True) -@common_alarm_gnocchi_arguments( - 'gnocchi_aggregation_by_metrics_threshold_rule', create=True) -@common_alarm_gnocchi_aggregation_by_metrics_arguments(create=True) -@_restore_shadowed_arg('project_id', 'alarm_project_id') -@_restore_shadowed_arg('user_id', 'alarm_user_id') -def do_alarm_gnocchi_aggregation_by_metrics_threshold_create(cc, args={}): - """Create a new alarm based on computed statistics.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) - fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints') - fields = utils.key_with_slash_to_nested_dict(fields) - fields['type'] = 'gnocchi_aggregation_by_metrics_threshold' - alarm = cc.alarms.create(**fields) - _display_alarm(alarm) - - -@common_alarm_arguments(create=True) -@common_alarm_gnocchi_arguments( - 'gnocchi_aggregation_by_resources_threshold_rule', create=True) -@common_alarm_gnocchi_aggregation_by_resources_arguments(create=True) -@_restore_shadowed_arg('project_id', 'alarm_project_id') -@_restore_shadowed_arg('user_id', 'alarm_user_id') -def do_alarm_gnocchi_aggregation_by_resources_threshold_create(cc, args={}): - """Create a new alarm based on computed statistics.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) - fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints') - fields = utils.key_with_slash_to_nested_dict(fields) - fields['type'] = 'gnocchi_aggregation_by_resources_threshold' - alarm = cc.alarms.create(**fields) - _display_alarm(alarm) - - -@common_alarm_arguments(create=True) -@utils.arg('-m', '--meter-name', metavar='<METRIC>', required=True, - dest='threshold_rule/meter_name', - help='Metric to evaluate against.') -@utils.arg('--period', type=int, metavar='<PERIOD>', - dest='threshold_rule/period', - help='Length of each period (seconds) to evaluate over.') -@utils.arg('--evaluation-periods', type=int, metavar='<COUNT>', - dest='threshold_rule/evaluation_periods', - help='Number of periods to evaluate over.') -@utils.arg('--statistic', metavar='<STATISTIC>', - dest='threshold_rule/statistic', - help='Statistic to evaluate, one of: ' + str(STATISTICS) + '.') -@utils.arg('--comparison-operator', metavar='<OPERATOR>', - dest='threshold_rule/comparison_operator', - help='Operator to compare with, one of: ' + str(ALARM_OPERATORS) + - '.') -@utils.arg('--threshold', type=float, metavar='<THRESHOLD>', required=True, - dest='threshold_rule/threshold', - help='Threshold to evaluate against.') -@utils.arg('-q', '--query', metavar='<QUERY>', - dest='threshold_rule/query', - help='key[op]data_type::value; list. data_type is optional, ' - 'but if supplied must be string, integer, float, or boolean.') -@_restore_shadowed_arg('project_id', 'alarm_project_id') -@_restore_shadowed_arg('user_id', 'alarm_user_id') -def do_alarm_threshold_create(cc, args={}): - """Create a new alarm based on computed statistics.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) - fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints') - fields = utils.key_with_slash_to_nested_dict(fields) - fields['type'] = 'threshold' - if 'query' in fields['threshold_rule']: - fields['threshold_rule']['query'] = options.cli_to_array( - fields['threshold_rule']['query']) - alarm = cc.alarms.create(**fields) - _display_alarm(alarm) - - -@common_alarm_arguments(create=True) -@utils.arg('--alarm_ids', action='append', metavar='<ALARM IDS>', - required=True, dest='combination_rule/alarm_ids', - help='List of alarm IDs.') -@utils.arg('--operator', metavar='<OPERATOR>', - dest='combination_rule/operator', - help='Operator to compare with, one of: ' + str( - ALARM_COMBINATION_OPERATORS) + '.') -@_restore_shadowed_arg('project_id', 'alarm_project_id') -@_restore_shadowed_arg('user_id', 'alarm_user_id') -def do_alarm_combination_create(cc, args={}): - """Create a new alarm based on state of other alarms.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) - fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints') - fields = utils.key_with_slash_to_nested_dict(fields) - fields['type'] = 'combination' - alarm = cc.alarms.create(**fields) - _display_alarm(alarm) - - -@common_alarm_arguments(create=True) -@common_alarm_event_arguments() -@_restore_shadowed_arg('project_id', 'alarm_project_id') -@_restore_shadowed_arg('user_id', 'alarm_user_id') -def do_alarm_event_create(cc, args={}): - """Create a new alarm based on events.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - fields = dict(filter(lambda x: x[1] is not None, vars(args).items())) - fields = utils.key_with_slash_to_nested_dict(fields) - fields['type'] = 'event' - fields['event_rule'] = fields.get('event_rule', {}) - if 'query' in fields['event_rule']: - fields['event_rule']['query'] = options.cli_to_array( - fields['event_rule']['query']) - alarm = cc.alarms.create(**fields) - _display_alarm(alarm) - - -@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', - action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS, - dest='alarm_id_deprecated') -@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?', - action=NotEmptyAction, help='ID of the alarm to update.') -@common_alarm_arguments() -@utils.arg('--remove-time-constraint', action='append', - metavar='<Constraint names>', - dest='remove_time_constraints', - help='Name or list of names of the time constraints to remove.') -@utils.arg('--period', type=int, metavar='<PERIOD>', - help='Length of each period (seconds) to evaluate over.') -@utils.arg('--evaluation-periods', type=int, metavar='<COUNT>', - help='Number of periods to evaluate over.') -@utils.arg('-m', '--meter-name', metavar='<METRIC>', - help='Metric to evaluate against.') -@utils.arg('--statistic', metavar='<STATISTIC>', - help='Statistic to evaluate, one of: ' + str(STATISTICS)) -@utils.arg('--comparison-operator', metavar='<OPERATOR>', - help='Operator to compare with, one of: ' + str(ALARM_OPERATORS) + - '.') -@utils.arg('--threshold', type=float, metavar='<THRESHOLD>', - help='Threshold to evaluate against.') -@utils.arg('--matching-metadata', dest='matching_metadata', - metavar='<Matching Metadata>', action='append', default=None, - help=('A meter should match this resource metadata (key=value) ' - 'additionally to the meter_name.')) -@_restore_shadowed_arg('project_id', 'alarm_project_id') -@_restore_shadowed_arg('user_id', 'alarm_user_id') -def do_alarm_update(cc, args={}): - """Update an existing alarm (Deprecated).""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) - fields = utils.args_array_to_list_of_dicts(fields, "time_constraints") - fields = utils.args_array_to_dict(fields, "matching_metadata") - fields.pop('alarm_id') - try: - alarm = cc.alarms.update(args.alarm_id, **fields) - except exc.HTTPNotFound: - raise exc.CommandError('Alarm not found: %s' % args.alarm_id) - _display_alarm(alarm) - - -@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', - action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS, - dest='alarm_id_deprecated') -@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?', - action=NotEmptyAction, help='ID of the alarm to update.') -@common_alarm_arguments() -@utils.arg('--remove-time-constraint', action='append', - metavar='<Constraint names>', - dest='remove_time_constraints', - help='Name or list of names of the time constraints to remove.') -@utils.arg('-m', '--meter-name', metavar='<METRIC>', - dest='threshold_rule/meter_name', - help='Metric to evaluate against.') -@utils.arg('--period', type=int, metavar='<PERIOD>', - dest='threshold_rule/period', - help='Length of each period (seconds) to evaluate over.') -@utils.arg('--evaluation-periods', type=int, metavar='<COUNT>', - dest='threshold_rule/evaluation_periods', - help='Number of periods to evaluate over.') -@utils.arg('--statistic', metavar='<STATISTIC>', - dest='threshold_rule/statistic', - help='Statistic to evaluate, one of: ' + str(STATISTICS) + - '.') -@utils.arg('--comparison-operator', metavar='<OPERATOR>', - dest='threshold_rule/comparison_operator', - help='Operator to compare with, one of: ' + str(ALARM_OPERATORS) + - '.') -@utils.arg('--threshold', type=float, metavar='<THRESHOLD>', - dest='threshold_rule/threshold', - help='Threshold to evaluate against.') -@utils.arg('-q', '--query', metavar='<QUERY>', - dest='threshold_rule/query', - help='key[op]data_type::value; list. data_type is optional, ' - 'but if supplied must be string, integer, float, or boolean.') -@_restore_shadowed_arg('project_id', 'alarm_project_id') -@_restore_shadowed_arg('user_id', 'alarm_user_id') -def do_alarm_threshold_update(cc, args={}): - """Update an existing alarm based on computed statistics.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) - fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints') - fields = utils.key_with_slash_to_nested_dict(fields) - fields.pop('alarm_id') - fields['type'] = 'threshold' - if 'threshold_rule' in fields and 'query' in fields['threshold_rule']: - fields['threshold_rule']['query'] = options.cli_to_array( - fields['threshold_rule']['query']) - try: - alarm = cc.alarms.update(args.alarm_id, **fields) - except exc.HTTPNotFound: - raise exc.CommandError('Alarm not found: %s' % args.alarm_id) - _display_alarm(alarm) - - -@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', - action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS, - dest='alarm_id_deprecated') -@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?', - action=NotEmptyAction, help='ID of the alarm to update.') -@common_alarm_arguments() -@common_alarm_gnocchi_arguments('gnocchi_resources_threshold_rule') -@common_alarm_gnocchi_resources_arguments() -@utils.arg('--remove-time-constraint', action='append', - metavar='<Constraint names>', - dest='remove_time_constraints', - help='Name or list of names of the time constraints to remove.') -@_restore_shadowed_arg('project_id', 'alarm_project_id') -@_restore_shadowed_arg('user_id', 'alarm_user_id') -def do_alarm_gnocchi_resources_threshold_update(cc, args={}): - """Update an existing alarm based on computed statistics.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) - fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints') - fields = utils.key_with_slash_to_nested_dict(fields) - fields.pop('alarm_id') - fields['type'] = 'gnocchi_resources_threshold' - try: - alarm = cc.alarms.update(args.alarm_id, **fields) - except exc.HTTPNotFound: - raise exc.CommandError('Alarm not found: %s' % args.alarm_id) - _display_alarm(alarm) - - -@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', - action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS, - dest='alarm_id_deprecated') -@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?', - action=NotEmptyAction, help='ID of the alarm to update.') -@common_alarm_arguments() -@common_alarm_gnocchi_arguments( - 'gnocchi_aggregation_by_metrics_threshold_rule') -@common_alarm_gnocchi_aggregation_by_metrics_arguments() -@utils.arg('--remove-time-constraint', action='append', - metavar='<Constraint names>', - dest='remove_time_constraints', - help='Name or list of names of the time constraints to remove.') -@_restore_shadowed_arg('project_id', 'alarm_project_id') -@_restore_shadowed_arg('user_id', 'alarm_user_id') -def do_alarm_gnocchi_aggregation_by_metrics_threshold_update(cc, args={}): - """Update an existing alarm based on computed statistics.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) - fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints') - fields = utils.key_with_slash_to_nested_dict(fields) - fields.pop('alarm_id') - fields['type'] = 'gnocchi_aggregation_by_metrics_threshold' - try: - alarm = cc.alarms.update(args.alarm_id, **fields) - except exc.HTTPNotFound: - raise exc.CommandError('Alarm not found: %s' % args.alarm_id) - _display_alarm(alarm) - - -@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', - action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS, - dest='alarm_id_deprecated') -@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?', - action=NotEmptyAction, help='ID of the alarm to update.') -@common_alarm_arguments() -@common_alarm_gnocchi_arguments( - 'gnocchi_aggregation_by_resources_threshold_rule') -@common_alarm_gnocchi_aggregation_by_resources_arguments() -@utils.arg('--remove-time-constraint', action='append', - metavar='<Constraint names>', - dest='remove_time_constraints', - help='Name or list of names of the time constraints to remove.') -@_restore_shadowed_arg('project_id', 'alarm_project_id') -@_restore_shadowed_arg('user_id', 'alarm_user_id') -def do_alarm_gnocchi_aggregation_by_resources_threshold_update(cc, args={}): - """Update an existing alarm based on computed statistics.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) - fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints') - fields = utils.key_with_slash_to_nested_dict(fields) - fields.pop('alarm_id') - fields['type'] = 'gnocchi_aggregation_by_resources_threshold' - try: - alarm = cc.alarms.update(args.alarm_id, **fields) - except exc.HTTPNotFound: - raise exc.CommandError('Alarm not found: %s' % args.alarm_id) - _display_alarm(alarm) - - -@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', - action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS, - dest='alarm_id_deprecated') -@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?', - action=NotEmptyAction, help='ID of the alarm to update.') -@common_alarm_arguments() -@utils.arg('--remove-time-constraint', action='append', - metavar='<Constraint names>', - dest='remove_time_constraints', - help='Name or list of names of the time constraints to remove.') -@utils.arg('--alarm_ids', action='append', metavar='<ALARM IDS>', - dest='combination_rule/alarm_ids', - help='List of alarm IDs.') -@utils.arg('--operator', metavar='<OPERATOR>', - dest='combination_rule/operator', - help='Operator to compare with, one of: ' + str( - ALARM_COMBINATION_OPERATORS) + '.') -@_restore_shadowed_arg('project_id', 'alarm_project_id') -@_restore_shadowed_arg('user_id', 'alarm_user_id') -def do_alarm_combination_update(cc, args={}): - """Update an existing alarm based on state of other alarms.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) - fields = utils.args_array_to_list_of_dicts(fields, 'time_constraints') - fields = utils.key_with_slash_to_nested_dict(fields) - fields.pop('alarm_id') - fields['type'] = 'combination' - try: - alarm = cc.alarms.update(args.alarm_id, **fields) - except exc.HTTPNotFound: - raise exc.CommandError('Alarm not found: %s' % args.alarm_id) - _display_alarm(alarm) - - -@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', - action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS, - dest='alarm_id_deprecated') -@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?', - action=NotEmptyAction, help='ID of the alarm to update.') -@common_alarm_arguments() -@common_alarm_event_arguments() -@_restore_shadowed_arg('project_id', 'alarm_project_id') -@_restore_shadowed_arg('user_id', 'alarm_user_id') -def do_alarm_event_update(cc, args={}): - """Update an existing alarm based on events.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - fields = dict(filter(lambda x: x[1] is not None, vars(args).items())) - fields = utils.key_with_slash_to_nested_dict(fields) - fields.pop('alarm_id') - fields['type'] = 'event' - if fields.get('event_rule') and 'query' in fields['event_rule']: - fields['event_rule']['query'] = options.cli_to_array( - fields['event_rule']['query']) - try: - alarm = cc.alarms.update(args.alarm_id, **fields) - except exc.HTTPNotFound: - raise exc.CommandError('Alarm not found: %s' % args.alarm_id) - _display_alarm(alarm) - - -@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', - action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS, - dest='alarm_id_deprecated') -@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?', - action=NotEmptyAction, help='ID of the alarm to delete.') -def do_alarm_delete(cc, args={}): - """Delete an alarm.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - try: - cc.alarms.delete(args.alarm_id) - except exc.HTTPNotFound: - raise exc.CommandError('Alarm not found: %s' % args.alarm_id) - - -@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', - action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS, - dest='alarm_id_deprecated') -@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?', - 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) + - '.') -def do_alarm_state_set(cc, args={}): - """Set the state of an alarm.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - try: - state = cc.alarms.set_state(args.alarm_id, args.state) - except exc.HTTPNotFound: - raise exc.CommandError('Alarm not found: %s' % args.alarm_id) - utils.print_dict({'state': state}, wrap=72) - - -@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', - action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS, - dest='alarm_id_deprecated') -@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?', - action=NotEmptyAction, help='ID of the alarm state to show.') -def do_alarm_state_get(cc, args={}): - """Get the state of an alarm.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - try: - state = cc.alarms.get_state(args.alarm_id) - except exc.HTTPNotFound: - raise exc.CommandError('Alarm not found: %s' % args.alarm_id) - utils.print_dict({'state': state}, wrap=72) - - -@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', - action=obsoleted_by('alarm_id'), help=argparse.SUPPRESS, - dest='alarm_id_deprecated') -@utils.arg('alarm_id', metavar='<ALARM_ID>', nargs='?', 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, ' - 'but if supplied must be string, integer, float, or boolean.') -def do_alarm_history(cc, args={}): - """Display the change history of an alarm.""" - warnings.warn("Alarm commands are deprecated, please use aodhclient") - kwargs = dict(alarm_id=args.alarm_id, - q=options.cli_to_array(args.query)) - try: - history = cc.alarms.get_history(**kwargs) - except exc.HTTPNotFound: - raise exc.CommandError('Alarm not found: %s' % args.alarm_id) - field_labels = ['Type', 'Timestamp', 'Detail'] - fields = ['type', 'timestamp', 'detail'] - # We're using sortby=None as the alarm history returned from the Ceilometer - # is already sorted in the "the newer state is the earlier one in the - # list". If we'll pass any field as a sortby param, it'll be sorted in the - # ASC way by the PrettyTable - utils.print_list(history, fields, field_labels, - formatters={'detail': alarm_change_detail_formatter}, - sortby=None) - - -@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('-l', '--limit', metavar='<NUMBER>', - help='Maximum number of resources to return. %s' % - DEFAULT_API_LIMIT) -def do_resource_list(cc, args={}): - """List the resources.""" - resources = cc.resources.list(q=options.cli_to_array(args.query), - limit=args.limit) - - field_labels = ['Resource ID', 'Source', 'User ID', 'Project ID'] - fields = ['resource_id', 'source', 'user_id', 'project_id'] - utils.print_list(resources, fields, field_labels, - sortby=1) - - -@utils.arg('resource_id', metavar='<RESOURCE_ID>', - action=NotEmptyAction, help='ID of the resource to show.') -def do_resource_show(cc, args={}): - """Show the resource.""" - try: - resource = cc.resources.get(args.resource_id) - except exc.HTTPNotFound: - raise exc.CommandError('Resource not found: %s' % args.resource_id) - else: - fields = ['resource_id', 'source', 'user_id', - 'project_id', 'metadata'] - data = dict([(f, getattr(resource, f, '')) for f in fields]) - utils.print_dict(data, wrap=72) - - -@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 datetime.') -@utils.arg('--no-traits', dest='no_traits', action='store_true', - help='If specified, traits will not be printed.') -@utils.arg('-l', '--limit', metavar='<NUMBER>', - help='Maximum number of events to return. %s' % DEFAULT_API_LIMIT) -def do_event_list(cc, args={}): - """List events.""" - events = cc.events.list(q=options.cli_to_array(args.query), - limit=args.limit) - field_labels = ['Message ID', 'Event Type', 'Generated', 'Traits'] - fields = ['message_id', 'event_type', 'generated', 'traits'] - if args.no_traits: - field_labels.pop() - fields.pop() - utils.print_list(events, fields, field_labels, - formatters={ - 'traits': utils.nested_list_of_dict_formatter( - 'traits', ['name', 'type', 'value'] - )}, - sortby=None) - - -@utils.arg('message_id', metavar='<message_id>', action=NotEmptyAction, - help='The ID of the event. Should be a UUID.') -def do_event_show(cc, args={}): - """Show a particular event.""" - try: - event = cc.events.get(args.message_id) - except exc.HTTPNotFound: - raise exc.CommandError('Event not found: %s' % args.message_id) - - fields = ['event_type', 'generated', 'traits', 'raw'] - data = dict([(f, getattr(event, f, '')) for f in fields]) - utils.print_dict(data, wrap=72) - - -def do_event_type_list(cc, args={}): - """List event types.""" - event_types = cc.event_types.list() - utils.print_list(event_types, ['event_type'], ['Event Type']) - - -@utils.arg('-e', '--event_type', metavar='<EVENT_TYPE>', - help='Type of the event for which traits will be shown.', - 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) - field_labels = ['Trait Name', 'Data Type'] - fields = ['name', 'type'] - utils.print_list(trait_descriptions, fields, field_labels) - - -@utils.arg('-e', '--event_type', metavar='<EVENT_TYPE>', - help='Type of the event for which traits will listed.', - required=True, action=NotEmptyAction) -@utils.arg('-t', '--trait_name', metavar='<TRAIT_NAME>', - help='The name of the trait to list.', - required=True, action=NotEmptyAction) -def do_trait_list(cc, args={}): - """List all traits with name <trait_name> for Event Type <event_type>.""" - traits = cc.traits.list(args.event_type, args.trait_name) - field_labels = ['Trait Name', 'Value', 'Data Type'] - fields = ['name', 'value', 'type'] - utils.print_list(traits, fields, field_labels) - - -@utils.arg('-f', '--filter', metavar='<FILTER>', - help=('{complex_op: [{simple_op: {field_name: value}}]} ' - 'The complex_op is one of: ' + str(COMPLEX_OPERATORS) + ', ' - 'simple_op is one of: ' + str(SIMPLE_OPERATORS) + '.')) -@utils.arg('-o', '--orderby', metavar='<ORDERBY>', - help=('[{field_name: direction}, {field_name: direction}] ' - 'The direction is one of: ' + str(ORDER_DIRECTIONS) + '.')) -@utils.arg('-l', '--limit', metavar='<LIMIT>', - help='Maximum number of samples to return. %s' % DEFAULT_API_LIMIT) -def do_query_samples(cc, args): - """Query samples.""" - fields = {'filter': args.filter, - 'orderby': args.orderby, - 'limit': args.limit} - try: - samples = cc.query_samples.query(**fields) - except exc.HTTPNotFound: - raise exc.CommandError('Samples not found') - else: - field_labels = ['ID', 'Resource ID', 'Meter', 'Type', 'Volume', - 'Unit', 'Timestamp'] - fields = ['id', 'resource_id', 'meter', 'type', - 'volume', 'unit', 'timestamp'] - utils.print_list(samples, fields, field_labels, - sortby=None) - - -@utils.arg('-f', '--filter', metavar='<FILTER>', - help=('{complex_op: [{simple_op: {field_name: value}}]} ' - 'The complex_op is one of: ' + str(COMPLEX_OPERATORS) + ', ' - 'simple_op is one of: ' + str(SIMPLE_OPERATORS) + '.')) -@utils.arg('-o', '--orderby', metavar='<ORDERBY>', - help=('[{field_name: direction}, {field_name: direction}] ' - 'The direction is one of: ' + str(ORDER_DIRECTIONS) + '.')) -@utils.arg('-l', '--limit', metavar='<LIMIT>', - help='Maximum number of alarms to return. %s' % DEFAULT_API_LIMIT) -def do_query_alarms(cc, args): - """Query Alarms.""" - fields = {'filter': args.filter, - 'orderby': args.orderby, - 'limit': args.limit} - try: - alarms = cc.query_alarms.query(**fields) - except exc.HTTPNotFound: - raise exc.CommandError('Alarms not found') - else: - _display_alarm_list(alarms, sortby=None) - - -@utils.arg('-f', '--filter', metavar='<FILTER>', - help=('{complex_op: [{simple_op: {field_name: value}}]} ' - 'The complex_op is one of: ' + str(COMPLEX_OPERATORS) + ', ' - 'simple_op is one of: ' + str(SIMPLE_OPERATORS) + '.')) -@utils.arg('-o', '--orderby', metavar='<ORDERBY>', - help=('[{field_name: direction}, {field_name: direction}] ' - 'The direction is one of: ' + str(ORDER_DIRECTIONS) + '.')) -@utils.arg('-l', '--limit', metavar='<LIMIT>', - help='Maximum number of alarm history items to return. %s' % - DEFAULT_API_LIMIT) -def do_query_alarm_history(cc, args): - """Query Alarm History.""" - fields = {'filter': args.filter, - 'orderby': args.orderby, - 'limit': args.limit} - try: - alarm_history = cc.query_alarm_history.query(**fields) - except exc.HTTPNotFound: - raise exc.CommandError('Alarm history not found') - else: - field_labels = ['Alarm ID', 'Event ID', 'Type', 'Detail', 'Timestamp'] - fields = ['alarm_id', 'event_id', 'type', 'detail', 'timestamp'] - utils.print_list(alarm_history, fields, field_labels, - formatters={'rule': alarm_change_detail_formatter}, - sortby=None) - - -def do_capabilities(cc, args): - """Print Ceilometer capabilities.""" - capabilities = cc.capabilities.get().to_dict() - # Capability is a nested dict, and has no user defined data, - # so it is safe to format here with json tools. - for key in capabilities: - # remove the leading and trailing pair of {} - capabilities[key] = jsonutils.dumps(capabilities[key], - sort_keys=True, indent=0)[2:-2] - utils.print_dict(capabilities) diff --git a/ceilometerclient/v2/statistics.py b/ceilometerclient/v2/statistics.py deleted file mode 100644 index 4775238..0000000 --- a/ceilometerclient/v2/statistics.py +++ /dev/null @@ -1,58 +0,0 @@ -# -# 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 six - -from ceilometerclient.common import base -from ceilometerclient.v2 import options - - -class Statistics(base.Resource): - def __repr__(self): - return "<Statistics %s>" % self._info - - -class StatisticsManager(base.Manager): - resource_class = Statistics - - @staticmethod - def _build_aggregates(aggregates): - url_aggregates = [] - for aggregate in aggregates: - if 'param' in aggregate: - url_aggregates.insert( - 0, - "aggregate.param=%(param)s" % aggregate - ) - url_aggregates.insert( - 0, - "aggregate.func=%(func)s" % aggregate - ) - else: - url_aggregates.append( - "aggregate.func=%(func)s" % aggregate - ) - return url_aggregates - - def list(self, meter_name, q=None, period=None, groupby=None, - aggregates=None): - groupby = groupby or [] - aggregates = aggregates or [] - p = ['period=%s' % period] if period else [] - if isinstance(groupby, six.string_types): - groupby = [groupby] - p.extend(['groupby=%s' % g for g in groupby] if groupby else []) - p.extend(self._build_aggregates(aggregates)) - return self._list(options.build_url( - '/v2/meters/' + meter_name + '/statistics', - q, p)) diff --git a/ceilometerclient/v2/trait_descriptions.py b/ceilometerclient/v2/trait_descriptions.py deleted file mode 100644 index 418647a..0000000 --- a/ceilometerclient/v2/trait_descriptions.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2013 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from ceilometerclient.common import base - - -class TraitDescription(base.Resource): - def __repr__(self): - return "<Trait %s>" % self._info - - -class TraitDescriptionManager(base.Manager): - resource_class = TraitDescription - - def list(self, event_type): - path = '/v2/event_types/%s/traits' % event_type - return self._list(path) diff --git a/ceilometerclient/v2/traits.py b/ceilometerclient/v2/traits.py deleted file mode 100644 index 55f8960..0000000 --- a/ceilometerclient/v2/traits.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2013 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from ceilometerclient.common import base - - -class Trait(base.Resource): - def __repr__(self): - return "<Trait %s>" % self._info - - -class TraitManager(base.Manager): - resource_class = Trait - - def list(self, event_type, trait_name): - path = '/v2/event_types/%s/traits/%s' % (event_type, trait_name) - return self._list(path) diff --git a/doc/ext/gen_ref.py b/doc/ext/gen_ref.py deleted file mode 100644 index f8560b3..0000000 --- a/doc/ext/gen_ref.py +++ /dev/null @@ -1,54 +0,0 @@ -# -*- coding: utf-8 -*- -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import os -import sys - -BASE_DIR = os.path.dirname(os.path.abspath(__file__)) -ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", "..", "..")) - -sys.path.insert(0, ROOT) -sys.path.insert(0, BASE_DIR) - - -def gen_ref(ver, title, names): - refdir = os.path.join(BASE_DIR, "ref") - pkg = "ceilometerclient" - if ver: - pkg = "%s.%s" % (pkg, ver) - refdir = os.path.join(refdir, ver) - if not os.path.exists(refdir): - os.makedirs(refdir) - idxpath = os.path.join(refdir, "index.rst") - with open(idxpath, "w") as idx: - idx.write(("%(title)s\n" - "%(signs)s\n" - "\n" - ".. toctree::\n" - " :maxdepth: 1\n" - "\n") % {"title": title, "signs": "=" * len(title)}) - for name in names: - idx.write(" %s\n" % name) - rstpath = os.path.join(refdir, "%s.rst" % name) - with open(rstpath, "w") as rst: - rst.write(("%(title)s\n" - "%(signs)s\n" - "\n" - ".. automodule:: %(pkg)s.%(name)s\n" - " :members:\n" - " :undoc-members:\n" - " :show-inheritance:\n" - " :noindex:\n") - % {"title": name.capitalize(), - "signs": "=" * len(name), - "pkg": pkg, "name": name}) diff --git a/doc/source/api.rst b/doc/source/api.rst deleted file mode 100644 index df6eaf5..0000000 --- a/doc/source/api.rst +++ /dev/null @@ -1,47 +0,0 @@ -The :mod:`ceilometerclient` Python API -====================================== - -.. module:: ceilometerclient - :synopsis: A client for the OpenStack Ceilometer API. - -.. currentmodule:: ceilometerclient - -Usage ------ - -First create a client instance with your credentials:: - - >>> import ceilometerclient.client - >>> cclient = ceilometerclient.client.get_client(VERSION, os_username=USERNAME, os_password=PASSWORD, os_tenant_name=PROJECT_NAME, os_auth_url=AUTH_URL) - -Here ``VERSION`` should be: ``2``. - -Then call methods on its managers:: - - >>> cclient.meters.list() - [<Meter ...>, ...] - - >>> cclient.new_samples.list() - [<Sample ...>, ...] - -V2 client tips -++++++++++++++ - -Use queries to narrow your search (more info at `Ceilometer V2 API reference`__):: - - >>> query = [dict(field='resource_id', op='eq', value='5a301761-f78b-46e2-8900-8b4f6fe6675a'), dict(field='meter',op='eq',value='cpu_util')] - >>> cclient.new_samples.list(q=query, limit=10) - [<Sample ...>, ...] - -__ https://docs.openstack.org/ceilometer/latest/webapi/v2.html#Query - -Reference ---------- - -For more information, see the reference: - -.. toctree:: - :maxdepth: 2 - - ref/index - ref/v2/index diff --git a/doc/source/cli.rst b/doc/source/cli.rst deleted file mode 100644 index aa0c8c2..0000000 --- a/doc/source/cli.rst +++ /dev/null @@ -1,2202 +0,0 @@ -.. ################################################### -.. ## WARNING ###################################### -.. ############## WARNING ########################## -.. ########################## WARNING ############## -.. ###################################### WARNING ## -.. ################################################### -.. ################################################### -.. ## -.. This file is tool-generated. Do not edit manually. -.. http://docs.openstack.org/contributor-guide/ -.. doc-tools/cli-reference.html -.. ## -.. ## WARNING ###################################### -.. ############## WARNING ########################## -.. ########################## WARNING ############## -.. ###################################### WARNING ## -.. ################################################### - -================================================================== -Telemetry Data Collection service (ceilometer) command-line client -================================================================== - -The ceilometer client is the command-line interface (CLI) for -the Telemetry Data Collection service (ceilometer) API -and its extensions. - -This chapter documents :command:`ceilometer` version ``2.9.0``. - -For help on a specific :command:`ceilometer` command, enter: - -.. code-block:: console - - $ ceilometer help COMMAND - -.. _ceilometer_command_usage: - -ceilometer usage -~~~~~~~~~~~~~~~~ - -.. code-block:: console - - usage: ceilometer [--version] [-d] [-v] [--timeout TIMEOUT] - [--ceilometer-url <CEILOMETER_URL>] - [--ceilometer-api-version CEILOMETER_API_VERSION] - [--os-tenant-id <tenant-id>] - [--os-region-name <region-name>] - [--os-auth-token <auth-token>] - [--os-service-type <service-type>] - [--os-endpoint-type <endpoint-type>] [--os-cacert <cacert>] - [--os-insecure <insecure>] [--os-cert-file <cert-file>] - [--os-key-file <key-file>] [--os-cert <cert>] - [--os-key <key>] [--os-project-name <project-name>] - [--os-project-id <project-id>] - [--os-project-domain-id <project-domain-id>] - [--os-project-domain-name <project-domain-name>] - [--os-user-id <user-id>] - [--os-user-domain-id <user-domain-id>] - [--os-user-domain-name <user-domain-name>] - [--os-endpoint <endpoint>] [--os-auth-system <auth-system>] - [--os-username <username>] [--os-password <password>] - [--os-tenant-name <tenant-name>] [--os-token <token>] - [--os-auth-url <auth-url>] - <subcommand> ... - -**Subcommands:** - -``alarm-combination-create`` - Create a new alarm based on state of other - alarms. - -``alarm-combination-update`` - Update an existing alarm based on state of - other alarms. - -``alarm-create`` - Create a new alarm (Deprecated). Use alarm-threshold-create instead. - -``alarm-delete`` - Delete an alarm. - -``alarm-event-create`` - Create a new alarm based on events. - -``alarm-event-update`` - Update an existing alarm based on events. - -``alarm-gnocchi-aggregation-by-metrics-threshold-create`` - Create a new alarm based on computed - statistics. - -``alarm-gnocchi-aggregation-by-metrics-threshold-update`` - Update an existing alarm based on computed - statistics. - -``alarm-gnocchi-aggregation-by-resources-threshold-create`` - Create a new alarm based on computed - statistics. - -``alarm-gnocchi-aggregation-by-resources-threshold-update`` - Update an existing alarm based on computed - statistics. - -``alarm-gnocchi-resources-threshold-create`` - Create a new alarm based on computed - statistics. - -``alarm-gnocchi-resources-threshold-update`` - Update an existing alarm based on computed - statistics. - -``alarm-history`` - Display the change history of an alarm. - -``alarm-list`` - List the user's alarms. - -``alarm-show`` - Show an alarm. - -``alarm-state-get`` - Get the state of an alarm. - -``alarm-state-set`` - Set the state of an alarm. - -``alarm-threshold-create`` - Create a new alarm based on computed - statistics. - -``alarm-threshold-update`` - Update an existing alarm based on computed - statistics. - -``alarm-update`` - Update an existing alarm (Deprecated). - -``capabilities`` - Print Ceilometer capabilities. - -``event-list`` - List events. - -``event-show`` - Show a particular event. - -``event-type-list`` - List event types. - -``meter-list`` - List the user's meters. - -``query-alarm-history`` - Query Alarm History. - -``query-alarms`` - Query Alarms. - -``query-samples`` - Query samples. - -``resource-list`` - List the resources. - -``resource-show`` - Show the resource. - -``sample-create`` - Create a sample. - -``sample-create-list`` - Create a sample list. - -``sample-list`` - List the samples (return OldSample objects if - -m/--meter is set). - -``sample-show`` - Show a sample. - -``statistics`` - List the statistics for a meter. - -``trait-description-list`` - List trait info for an event type. - -``trait-list`` - List all traits with name <trait_name> for - Event Type <event_type>. - -``bash-completion`` - Prints all of the commands and options to - stdout. - -``help`` - Display help about this program or one of its - subcommands. - -.. _ceilometer_command_options: - -ceilometer optional arguments -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``--version`` - show program's version number and exit - -``-d, --debug`` - Defaults to ``env[CEILOMETERCLIENT_DEBUG]``. - -``-v, --verbose`` - Print more verbose output. - -``--timeout TIMEOUT`` - Number of seconds to wait for a response. - -``--ceilometer-url <CEILOMETER_URL>`` - **DEPRECATED**, use --os-endpoint instead. - Defaults to ``env[CEILOMETER_URL]``. - -``--ceilometer-api-version CEILOMETER_API_VERSION`` - Defaults to ``env[CEILOMETER_API_VERSION]`` or 2. - -``--os-tenant-id <tenant-id>`` - Defaults to ``env[OS_TENANT_ID]``. - -``--os-region-name <region-name>`` - Defaults to ``env[OS_REGION_NAME]``. - -``--os-auth-token <auth-token>`` - Defaults to ``env[OS_AUTH_TOKEN]``. - -``--os-service-type <service-type>`` - Defaults to ``env[OS_SERVICE_TYPE]``. - -``--os-endpoint-type <endpoint-type>`` - Defaults to ``env[OS_ENDPOINT_TYPE]``. - -``--os-cacert <cacert>`` - Defaults to ``env[OS_CACERT]``. - -``--os-insecure <insecure>`` - Defaults to ``env[OS_INSECURE]``. - -``--os-cert-file <cert-file>`` - Defaults to ``env[OS_CERT_FILE]``. - -``--os-key-file <key-file>`` - Defaults to ``env[OS_KEY_FILE]``. - -``--os-cert <cert>`` - Defaults to ``env[OS_CERT]``. - -``--os-key <key>`` - Defaults to ``env[OS_KEY]``. - -``--os-project-name <project-name>`` - Defaults to ``env[OS_PROJECT_NAME]``. - -``--os-project-id <project-id>`` - Defaults to ``env[OS_PROJECT_ID]``. - -``--os-project-domain-id <project-domain-id>`` - Defaults to ``env[OS_PROJECT_DOMAIN_ID]``. - -``--os-project-domain-name <project-domain-name>`` - Defaults to ``env[OS_PROJECT_DOMAIN_NAME]``. - -``--os-user-id <user-id>`` - Defaults to ``env[OS_USER_ID]``. - -``--os-user-domain-id <user-domain-id>`` - Defaults to ``env[OS_USER_DOMAIN_ID]``. - -``--os-user-domain-name <user-domain-name>`` - Defaults to ``env[OS_USER_DOMAIN_NAME]``. - -``--os-endpoint <endpoint>`` - Defaults to ``env[OS_ENDPOINT]``. - -``--os-auth-system <auth-system>`` - Defaults to ``env[OS_AUTH_SYSTEM]``. - -``--os-username <username>`` - Defaults to ``env[OS_USERNAME]``. - -``--os-password <password>`` - Defaults to ``env[OS_PASSWORD]``. - -``--os-tenant-name <tenant-name>`` - Defaults to ``env[OS_TENANT_NAME]``. - -``--os-token <token>`` - Defaults to ``env[OS_TOKEN]``. - -``--os-auth-url <auth-url>`` - Defaults to ``env[OS_AUTH_URL]``. - -.. _ceilometer_alarm-combination-create: - -ceilometer alarm-combination-create ------------------------------------ - -.. code-block:: console - - usage: ceilometer alarm-combination-create --name <NAME> - [--project-id <ALARM_PROJECT_ID>] - [--user-id <ALARM_USER_ID>] - [--description <DESCRIPTION>] - [--state <STATE>] - [--severity <SEVERITY>] - [--enabled {True|False}] - [--alarm-action <Webhook URL>] - [--ok-action <Webhook URL>] - [--insufficient-data-action <Webhook URL>] - [--time-constraint <Time Constraint>] - [--repeat-actions {True|False}] - --alarm_ids <ALARM IDS> - [--operator <OPERATOR>] - -Create a new alarm based on state of other alarms. - -**Optional arguments:** - -``--name <NAME>`` - Name of the alarm (must be unique per tenant). - Required. - -``--project-id <ALARM_PROJECT_ID>`` - Tenant to associate with alarm (configurable - by admin users only). - -``--user-id <ALARM_USER_ID>`` - User to associate with alarm (configurable by - admin users only). - -``--description <DESCRIPTION>`` - Free text description of the alarm. - -``--state <STATE>`` - State of the alarm, one of: ['ok', 'alarm', - 'insufficient data'] - -``--severity <SEVERITY>`` - Severity of the alarm, one of: ['low', - 'moderate', 'critical'] - -``--enabled {True|False}`` - True if alarm evaluation/actioning is enabled. - -``--alarm-action <Webhook URL>`` - URL to invoke when state transitions to alarm. - May be used multiple times. Defaults to None. - -``--ok-action <Webhook URL>`` - URL to invoke when state transitions to OK. - May be used multiple times. Defaults to None. - -``--insufficient-data-action <Webhook URL>`` - URL to invoke when state transitions to - insufficient data. May be used multiple times. - Defaults to None. - -``--time-constraint <Time Constraint>`` - Only evaluate the alarm if the time at - evaluation is within this time constraint. - Start point(s) of the constraint are specified - with a cron expression, whereas its duration - is given in seconds. Can be specified multiple - times for multiple time constraints, format - is: name=<CONSTRAINT_NAME>;start=<CRON>;durati - on=<SECONDS>;[description=<DESCRIPTION>;[timez - one=<IANA Timezone>]] Defaults to None. - -``--repeat-actions {True|False}`` - True if actions should be repeatedly notified - while alarm remains in target state. - -``--alarm_ids <ALARM IDS>`` - List of alarm IDs. Required. - -``--operator <OPERATOR>`` - Operator to compare with, one of: ['and', - 'or']. - -.. _ceilometer_alarm-combination-update: - -ceilometer alarm-combination-update ------------------------------------ - -.. code-block:: console - - usage: ceilometer alarm-combination-update [--name <NAME>] - [--project-id <ALARM_PROJECT_ID>] - [--user-id <ALARM_USER_ID>] - [--description <DESCRIPTION>] - [--state <STATE>] - [--severity <SEVERITY>] - [--enabled {True|False}] - [--alarm-action <Webhook URL>] - [--ok-action <Webhook URL>] - [--insufficient-data-action <Webhook URL>] - [--time-constraint <Time Constraint>] - [--repeat-actions {True|False}] - [--remove-time-constraint <Constraint names>] - [--alarm_ids <ALARM IDS>] - [--operator <OPERATOR>] - [<ALARM_ID>] - -Update an existing alarm based on state of other alarms. - -**Positional arguments:** - -``<ALARM_ID>`` - ID of the alarm to update. - -**Optional arguments:** - -``--name <NAME>`` - Name of the alarm (must be unique per tenant). - -``--project-id <ALARM_PROJECT_ID>`` - Tenant to associate with alarm (configurable - by admin users only). - -``--user-id <ALARM_USER_ID>`` - User to associate with alarm (configurable by - admin users only). - -``--description <DESCRIPTION>`` - Free text description of the alarm. - -``--state <STATE>`` - State of the alarm, one of: ['ok', 'alarm', - 'insufficient data'] - -``--severity <SEVERITY>`` - Severity of the alarm, one of: ['low', - 'moderate', 'critical'] - -``--enabled {True|False}`` - True if alarm evaluation/actioning is enabled. - -``--alarm-action <Webhook URL>`` - URL to invoke when state transitions to alarm. - May be used multiple times. Defaults to None. - -``--ok-action <Webhook URL>`` - URL to invoke when state transitions to OK. - May be used multiple times. Defaults to None. - -``--insufficient-data-action <Webhook URL>`` - URL to invoke when state transitions to - insufficient data. May be used multiple times. - Defaults to None. - -``--time-constraint <Time Constraint>`` - Only evaluate the alarm if the time at - evaluation is within this time constraint. - Start point(s) of the constraint are specified - with a cron expression, whereas its duration - is given in seconds. Can be specified multiple - times for multiple time constraints, format - is: name=<CONSTRAINT_NAME>;start=<CRON>;durati - on=<SECONDS>;[description=<DESCRIPTION>;[timez - one=<IANA Timezone>]] Defaults to None. - -``--repeat-actions {True|False}`` - True if actions should be repeatedly notified - while alarm remains in target state. - -``--remove-time-constraint <Constraint names>`` - Name or list of names of the time constraints - to remove. - -``--alarm_ids <ALARM IDS>`` - List of alarm IDs. - -``--operator <OPERATOR>`` - Operator to compare with, one of: ['and', - 'or']. - -.. _ceilometer_alarm-delete: - -ceilometer alarm-delete ------------------------ - -.. code-block:: console - - usage: ceilometer alarm-delete [<ALARM_ID>] - -Delete an alarm. - -**Positional arguments:** - -``<ALARM_ID>`` - ID of the alarm to delete. - -.. _ceilometer_alarm-event-create: - -ceilometer alarm-event-create ------------------------------ - -.. code-block:: console - - usage: ceilometer alarm-event-create --name <NAME> - [--project-id <ALARM_PROJECT_ID>] - [--user-id <ALARM_USER_ID>] - [--description <DESCRIPTION>] - [--state <STATE>] [--severity <SEVERITY>] - [--enabled {True|False}] - [--alarm-action <Webhook URL>] - [--ok-action <Webhook URL>] - [--insufficient-data-action <Webhook URL>] - [--time-constraint <Time Constraint>] - [--repeat-actions {True|False}] - [--event-type <EVENT_TYPE>] [-q <QUERY>] - -Create a new alarm based on events. - -**Optional arguments:** - -``--name <NAME>`` - Name of the alarm (must be unique per tenant). - Required. - -``--project-id <ALARM_PROJECT_ID>`` - Tenant to associate with alarm (configurable - by admin users only). - -``--user-id <ALARM_USER_ID>`` - User to associate with alarm (configurable by - admin users only). - -``--description <DESCRIPTION>`` - Free text description of the alarm. - -``--state <STATE>`` - State of the alarm, one of: ['ok', 'alarm', - 'insufficient data'] - -``--severity <SEVERITY>`` - Severity of the alarm, one of: ['low', - 'moderate', 'critical'] - -``--enabled {True|False}`` - True if alarm evaluation/actioning is enabled. - -``--alarm-action <Webhook URL>`` - URL to invoke when state transitions to alarm. - May be used multiple times. Defaults to None. - -``--ok-action <Webhook URL>`` - URL to invoke when state transitions to OK. - May be used multiple times. Defaults to None. - -``--insufficient-data-action <Webhook URL>`` - URL to invoke when state transitions to - insufficient data. May be used multiple times. - Defaults to None. - -``--time-constraint <Time Constraint>`` - Only evaluate the alarm if the time at - evaluation is within this time constraint. - Start point(s) of the constraint are specified - with a cron expression, whereas its duration - is given in seconds. Can be specified multiple - times for multiple time constraints, format - is: name=<CONSTRAINT_NAME>;start=<CRON>;durati - on=<SECONDS>;[description=<DESCRIPTION>;[timez - one=<IANA Timezone>]] Defaults to None. - -``--repeat-actions {True|False}`` - True if actions should be repeatedly notified - while alarm remains in target state. - -``--event-type <EVENT_TYPE>`` - Event type for event alarm. - -``-q <QUERY>, --query <QUERY>`` - key[op]data_type::value; list for filtering - events. data_type is optional, but if supplied - must be string, integer, float or datetime. - -.. _ceilometer_alarm-event-update: - -ceilometer alarm-event-update ------------------------------ - -.. code-block:: console - - usage: ceilometer alarm-event-update [--name <NAME>] - [--project-id <ALARM_PROJECT_ID>] - [--user-id <ALARM_USER_ID>] - [--description <DESCRIPTION>] - [--state <STATE>] [--severity <SEVERITY>] - [--enabled {True|False}] - [--alarm-action <Webhook URL>] - [--ok-action <Webhook URL>] - [--insufficient-data-action <Webhook URL>] - [--time-constraint <Time Constraint>] - [--repeat-actions {True|False}] - [--event-type <EVENT_TYPE>] [-q <QUERY>] - [<ALARM_ID>] - -Update an existing alarm based on events. - -**Positional arguments:** - -``<ALARM_ID>`` - ID of the alarm to update. - -**Optional arguments:** - -``--name <NAME>`` - Name of the alarm (must be unique per tenant). - -``--project-id <ALARM_PROJECT_ID>`` - Tenant to associate with alarm (configurable - by admin users only). - -``--user-id <ALARM_USER_ID>`` - User to associate with alarm (configurable by - admin users only). - -``--description <DESCRIPTION>`` - Free text description of the alarm. - -``--state <STATE>`` - State of the alarm, one of: ['ok', 'alarm', - 'insufficient data'] - -``--severity <SEVERITY>`` - Severity of the alarm, one of: ['low', - 'moderate', 'critical'] - -``--enabled {True|False}`` - True if alarm evaluation/actioning is enabled. - -``--alarm-action <Webhook URL>`` - URL to invoke when state transitions to alarm. - May be used multiple times. Defaults to None. - -``--ok-action <Webhook URL>`` - URL to invoke when state transitions to OK. - May be used multiple times. Defaults to None. - -``--insufficient-data-action <Webhook URL>`` - URL to invoke when state transitions to - insufficient data. May be used multiple times. - Defaults to None. - -``--time-constraint <Time Constraint>`` - Only evaluate the alarm if the time at - evaluation is within this time constraint. - Start point(s) of the constraint are specified - with a cron expression, whereas its duration - is given in seconds. Can be specified multiple - times for multiple time constraints, format - is: name=<CONSTRAINT_NAME>;start=<CRON>;durati - on=<SECONDS>;[description=<DESCRIPTION>;[timez - one=<IANA Timezone>]] Defaults to None. - -``--repeat-actions {True|False}`` - True if actions should be repeatedly notified - while alarm remains in target state. - -``--event-type <EVENT_TYPE>`` - Event type for event alarm. - -``-q <QUERY>, --query <QUERY>`` - key[op]data_type::value; list for filtering - events. data_type is optional, but if supplied - must be string, integer, float or datetime. - -.. _ceilometer_alarm-gnocchi-aggregation-by-metrics-threshold-create: - -ceilometer alarm-gnocchi-aggregation-by-metrics-threshold-create ----------------------------------------------------------------- - -.. code-block:: console - - usage: ceilometer alarm-gnocchi-aggregation-by-metrics-threshold-create - --name <NAME> [--project-id <ALARM_PROJECT_ID>] - [--user-id <ALARM_USER_ID>] [--description <DESCRIPTION>] - [--state <STATE>] [--severity <SEVERITY>] [--enabled {True|False}] - [--alarm-action <Webhook URL>] [--ok-action <Webhook URL>] - [--insufficient-data-action <Webhook URL>] - [--time-constraint <Time Constraint>] [--repeat-actions {True|False}] - [--granularity <GRANULARITY>] [--evaluation-periods <COUNT>] - --aggregation-method <AGGREATION> [--comparison-operator <OPERATOR>] - --threshold <THRESHOLD> -m <METRICS> - -Create a new alarm based on computed statistics. - -**Optional arguments:** - -``--name <NAME>`` - Name of the alarm (must be unique per tenant). - Required. - -``--project-id <ALARM_PROJECT_ID>`` - Tenant to associate with alarm (configurable - by admin users only). - -``--user-id <ALARM_USER_ID>`` - User to associate with alarm (configurable by - admin users only). - -``--description <DESCRIPTION>`` - Free text description of the alarm. - -``--state <STATE>`` - State of the alarm, one of: ['ok', 'alarm', - 'insufficient data'] - -``--severity <SEVERITY>`` - Severity of the alarm, one of: ['low', - 'moderate', 'critical'] - -``--enabled {True|False}`` - True if alarm evaluation/actioning is enabled. - -``--alarm-action <Webhook URL>`` - URL to invoke when state transitions to alarm. - May be used multiple times. Defaults to None. - -``--ok-action <Webhook URL>`` - URL to invoke when state transitions to OK. - May be used multiple times. Defaults to None. - -``--insufficient-data-action <Webhook URL>`` - URL to invoke when state transitions to - insufficient data. May be used multiple times. - Defaults to None. - -``--time-constraint <Time Constraint>`` - Only evaluate the alarm if the time at - evaluation is within this time constraint. - Start point(s) of the constraint are specified - with a cron expression, whereas its duration - is given in seconds. Can be specified multiple - times for multiple time constraints, format - is: name=<CONSTRAINT_NAME>;start=<CRON>;durati - on=<SECONDS>;[description=<DESCRIPTION>;[timez - one=<IANA Timezone>]] Defaults to None. - -``--repeat-actions {True|False}`` - True if actions should be repeatedly notified - while alarm remains in target state. - -``--granularity <GRANULARITY>`` - Length of each period (seconds) to evaluate - over. - -``--evaluation-periods <COUNT>`` - Number of periods to evaluate over. - -``--aggregation-method <AGGREATION>`` - Aggregation method to use, one of: ['last', - 'min', 'median', 'sum', 'std', 'first', - 'mean', 'count', 'moving-average', 'max', - '1pct', '2pct', '3pct', '4pct', '5pct', - '6pct', '7pct', '8pct', '9pct', '10pct', - '11pct', '12pct', '13pct', '14pct', '15pct', - '16pct', '17pct', '18pct', '19pct', '20pct', - '21pct', '22pct', '23pct', '24pct', '25pct', - '26pct', '27pct', '28pct', '29pct', '30pct', - '31pct', '32pct', '33pct', '34pct', '35pct', - '36pct', '37pct', '38pct', '39pct', '40pct', - '41pct', '42pct', '43pct', '44pct', '45pct', - '46pct', '47pct', '48pct', '49pct', '50pct', - '51pct', '52pct', '53pct', '54pct', '55pct', - '56pct', '57pct', '58pct', '59pct', '60pct', - '61pct', '62pct', '63pct', '64pct', '65pct', - '66pct', '67pct', '68pct', '69pct', '70pct', - '71pct', '72pct', '73pct', '74pct', '75pct', - '76pct', '77pct', '78pct', '79pct', '80pct', - '81pct', '82pct', '83pct', '84pct', '85pct', - '86pct', '87pct', '88pct', '89pct', '90pct', - '91pct', '92pct', '93pct', '94pct', '95pct', - '96pct', '97pct', '98pct', '99pct']. Required. - -``--comparison-operator <OPERATOR>`` - Operator to compare with, one of: ['lt', 'le', - 'eq', 'ne', 'ge', 'gt']. - -``--threshold <THRESHOLD>`` - Threshold to evaluate against. Required. - -``-m <METRICS>, --metrics <METRICS>`` - Metric to evaluate against. Required. - -.. _ceilometer_alarm-gnocchi-aggregation-by-metrics-threshold-update: - -ceilometer alarm-gnocchi-aggregation-by-metrics-threshold-update ----------------------------------------------------------------- - -.. code-block:: console - - usage: ceilometer alarm-gnocchi-aggregation-by-metrics-threshold-update - [--name <NAME>] [--project-id <ALARM_PROJECT_ID>] - [--user-id <ALARM_USER_ID>] [--description <DESCRIPTION>] - [--state <STATE>] [--severity <SEVERITY>] [--enabled {True|False}] - [--alarm-action <Webhook URL>] [--ok-action <Webhook URL>] - [--insufficient-data-action <Webhook URL>] - [--time-constraint <Time Constraint>] [--repeat-actions {True|False}] - [--granularity <GRANULARITY>] [--evaluation-periods <COUNT>] - [--aggregation-method <AGGREATION>] [--comparison-operator <OPERATOR>] - [--threshold <THRESHOLD>] [-m <METRICS>] - [--remove-time-constraint <Constraint names>] - [<ALARM_ID>] - -Update an existing alarm based on computed statistics. - -**Positional arguments:** - -``<ALARM_ID>`` - ID of the alarm to update. - -**Optional arguments:** - -``--name <NAME>`` - Name of the alarm (must be unique per tenant). - -``--project-id <ALARM_PROJECT_ID>`` - Tenant to associate with alarm (configurable - by admin users only). - -``--user-id <ALARM_USER_ID>`` - User to associate with alarm (configurable by - admin users only). - -``--description <DESCRIPTION>`` - Free text description of the alarm. - -``--state <STATE>`` - State of the alarm, one of: ['ok', 'alarm', - 'insufficient data'] - -``--severity <SEVERITY>`` - Severity of the alarm, one of: ['low', - 'moderate', 'critical'] - -``--enabled {True|False}`` - True if alarm evaluation/actioning is enabled. - -``--alarm-action <Webhook URL>`` - URL to invoke when state transitions to alarm. - May be used multiple times. Defaults to None. - -``--ok-action <Webhook URL>`` - URL to invoke when state transitions to OK. - May be used multiple times. Defaults to None. - -``--insufficient-data-action <Webhook URL>`` - URL to invoke when state transitions to - insufficient data. May be used multiple times. - Defaults to None. - -``--time-constraint <Time Constraint>`` - Only evaluate the alarm if the time at - evaluation is within this time constraint. - Start point(s) of the constraint are specified - with a cron expression, whereas its duration - is given in seconds. Can be specified multiple - times for multiple time constraints, format - is: name=<CONSTRAINT_NAME>;start=<CRON>;durati - on=<SECONDS>;[description=<DESCRIPTION>;[timez - one=<IANA Timezone>]] Defaults to None. - -``--repeat-actions {True|False}`` - True if actions should be repeatedly notified - while alarm remains in target state. - -``--granularity <GRANULARITY>`` - Length of each period (seconds) to evaluate - over. - -``--evaluation-periods <COUNT>`` - Number of periods to evaluate over. - -``--aggregation-method <AGGREATION>`` - Aggregation method to use, one of: ['last', - 'min', 'median', 'sum', 'std', 'first', - 'mean', 'count', 'moving-average', 'max', - '1pct', '2pct', '3pct', '4pct', '5pct', - '6pct', '7pct', '8pct', '9pct', '10pct', - '11pct', '12pct', '13pct', '14pct', '15pct', - '16pct', '17pct', '18pct', '19pct', '20pct', - '21pct', '22pct', '23pct', '24pct', '25pct', - '26pct', '27pct', '28pct', '29pct', '30pct', - '31pct', '32pct', '33pct', '34pct', '35pct', - '36pct', '37pct', '38pct', '39pct', '40pct', - '41pct', '42pct', '43pct', '44pct', '45pct', - '46pct', '47pct', '48pct', '49pct', '50pct', - '51pct', '52pct', '53pct', '54pct', '55pct', - '56pct', '57pct', '58pct', '59pct', '60pct', - '61pct', '62pct', '63pct', '64pct', '65pct', - '66pct', '67pct', '68pct', '69pct', '70pct', - '71pct', '72pct', '73pct', '74pct', '75pct', - '76pct', '77pct', '78pct', '79pct', '80pct', - '81pct', '82pct', '83pct', '84pct', '85pct', - '86pct', '87pct', '88pct', '89pct', '90pct', - '91pct', '92pct', '93pct', '94pct', '95pct', - '96pct', '97pct', '98pct', '99pct']. - -``--comparison-operator <OPERATOR>`` - Operator to compare with, one of: ['lt', 'le', - 'eq', 'ne', 'ge', 'gt']. - -``--threshold <THRESHOLD>`` - Threshold to evaluate against. - -``-m <METRICS>, --metrics <METRICS>`` - Metric to evaluate against. - -``--remove-time-constraint <Constraint names>`` - Name or list of names of the time constraints - to remove. - -.. _ceilometer_alarm-gnocchi-aggregation-by-resources-threshold-create: - -ceilometer alarm-gnocchi-aggregation-by-resources-threshold-create ------------------------------------------------------------------- - -.. code-block:: console - - usage: ceilometer alarm-gnocchi-aggregation-by-resources-threshold-create - --name <NAME> [--project-id <ALARM_PROJECT_ID>] - [--user-id <ALARM_USER_ID>] [--description <DESCRIPTION>] - [--state <STATE>] [--severity <SEVERITY>] [--enabled {True|False}] - [--alarm-action <Webhook URL>] [--ok-action <Webhook URL>] - [--insufficient-data-action <Webhook URL>] - [--time-constraint <Time Constraint>] [--repeat-actions {True|False}] - [--granularity <GRANULARITY>] [--evaluation-periods <COUNT>] - --aggregation-method <AGGREATION> [--comparison-operator <OPERATOR>] - --threshold <THRESHOLD> -m <METRIC> --resource-type <RESOURCE_TYPE> - --query <QUERY> - -Create a new alarm based on computed statistics. - -**Optional arguments:** - -``--name <NAME>`` - Name of the alarm (must be unique per tenant). - Required. - -``--project-id <ALARM_PROJECT_ID>`` - Tenant to associate with alarm (configurable - by admin users only). - -``--user-id <ALARM_USER_ID>`` - User to associate with alarm (configurable by - admin users only). - -``--description <DESCRIPTION>`` - Free text description of the alarm. - -``--state <STATE>`` - State of the alarm, one of: ['ok', 'alarm', - 'insufficient data'] - -``--severity <SEVERITY>`` - Severity of the alarm, one of: ['low', - 'moderate', 'critical'] - -``--enabled {True|False}`` - True if alarm evaluation/actioning is enabled. - -``--alarm-action <Webhook URL>`` - URL to invoke when state transitions to alarm. - May be used multiple times. Defaults to None. - -``--ok-action <Webhook URL>`` - URL to invoke when state transitions to OK. - May be used multiple times. Defaults to None. - -``--insufficient-data-action <Webhook URL>`` - URL to invoke when state transitions to - insufficient data. May be used multiple times. - Defaults to None. - -``--time-constraint <Time Constraint>`` - Only evaluate the alarm if the time at - evaluation is within this time constraint. - Start point(s) of the constraint are specified - with a cron expression, whereas its duration - is given in seconds. Can be specified multiple - times for multiple time constraints, format - is: name=<CONSTRAINT_NAME>;start=<CRON>;durati - on=<SECONDS>;[description=<DESCRIPTION>;[timez - one=<IANA Timezone>]] Defaults to None. - -``--repeat-actions {True|False}`` - True if actions should be repeatedly notified - while alarm remains in target state. - -``--granularity <GRANULARITY>`` - Length of each period (seconds) to evaluate - over. - -``--evaluation-periods <COUNT>`` - Number of periods to evaluate over. - -``--aggregation-method <AGGREATION>`` - Aggregation method to use, one of: ['last', - 'min', 'median', 'sum', 'std', 'first', - 'mean', 'count', 'moving-average', 'max', - '1pct', '2pct', '3pct', '4pct', '5pct', - '6pct', '7pct', '8pct', '9pct', '10pct', - '11pct', '12pct', '13pct', '14pct', '15pct', - '16pct', '17pct', '18pct', '19pct', '20pct', - '21pct', '22pct', '23pct', '24pct', '25pct', - '26pct', '27pct', '28pct', '29pct', '30pct', - '31pct', '32pct', '33pct', '34pct', '35pct', - '36pct', '37pct', '38pct', '39pct', '40pct', - '41pct', '42pct', '43pct', '44pct', '45pct', - '46pct', '47pct', '48pct', '49pct', '50pct', - '51pct', '52pct', '53pct', '54pct', '55pct', - '56pct', '57pct', '58pct', '59pct', '60pct', - '61pct', '62pct', '63pct', '64pct', '65pct', - '66pct', '67pct', '68pct', '69pct', '70pct', - '71pct', '72pct', '73pct', '74pct', '75pct', - '76pct', '77pct', '78pct', '79pct', '80pct', - '81pct', '82pct', '83pct', '84pct', '85pct', - '86pct', '87pct', '88pct', '89pct', '90pct', - '91pct', '92pct', '93pct', '94pct', '95pct', - '96pct', '97pct', '98pct', '99pct']. Required. - -``--comparison-operator <OPERATOR>`` - Operator to compare with, one of: ['lt', 'le', - 'eq', 'ne', 'ge', 'gt']. - -``--threshold <THRESHOLD>`` - Threshold to evaluate against. Required. - -``-m <METRIC>, --metric <METRIC>`` - Metric to evaluate against. Required. - -``--resource-type <RESOURCE_TYPE>`` - Resource_type to evaluate against. Required. - -``--query <QUERY>`` - Gnocchi resources search query filter - Required. - -.. _ceilometer_alarm-gnocchi-aggregation-by-resources-threshold-update: - -ceilometer alarm-gnocchi-aggregation-by-resources-threshold-update ------------------------------------------------------------------- - -.. code-block:: console - - usage: ceilometer alarm-gnocchi-aggregation-by-resources-threshold-update - [--name <NAME>] [--project-id <ALARM_PROJECT_ID>] - [--user-id <ALARM_USER_ID>] [--description <DESCRIPTION>] - [--state <STATE>] [--severity <SEVERITY>] [--enabled {True|False}] - [--alarm-action <Webhook URL>] [--ok-action <Webhook URL>] - [--insufficient-data-action <Webhook URL>] - [--time-constraint <Time Constraint>] [--repeat-actions {True|False}] - [--granularity <GRANULARITY>] [--evaluation-periods <COUNT>] - [--aggregation-method <AGGREATION>] [--comparison-operator <OPERATOR>] - [--threshold <THRESHOLD>] [-m <METRIC>] - [--resource-type <RESOURCE_TYPE>] [--query <QUERY>] - [--remove-time-constraint <Constraint names>] - [<ALARM_ID>] - -Update an existing alarm based on computed statistics. - -**Positional arguments:** - -``<ALARM_ID>`` - ID of the alarm to update. - -**Optional arguments:** - -``--name <NAME>`` - Name of the alarm (must be unique per tenant). - -``--project-id <ALARM_PROJECT_ID>`` - Tenant to associate with alarm (configurable - by admin users only). - -``--user-id <ALARM_USER_ID>`` - User to associate with alarm (configurable by - admin users only). - -``--description <DESCRIPTION>`` - Free text description of the alarm. - -``--state <STATE>`` - State of the alarm, one of: ['ok', 'alarm', - 'insufficient data'] - -``--severity <SEVERITY>`` - Severity of the alarm, one of: ['low', - 'moderate', 'critical'] - -``--enabled {True|False}`` - True if alarm evaluation/actioning is enabled. - -``--alarm-action <Webhook URL>`` - URL to invoke when state transitions to alarm. - May be used multiple times. Defaults to None. - -``--ok-action <Webhook URL>`` - URL to invoke when state transitions to OK. - May be used multiple times. Defaults to None. - -``--insufficient-data-action <Webhook URL>`` - URL to invoke when state transitions to - insufficient data. May be used multiple times. - Defaults to None. - -``--time-constraint <Time Constraint>`` - Only evaluate the alarm if the time at - evaluation is within this time constraint. - Start point(s) of the constraint are specified - with a cron expression, whereas its duration - is given in seconds. Can be specified multiple - times for multiple time constraints, format - is: name=<CONSTRAINT_NAME>;start=<CRON>;durati - on=<SECONDS>;[description=<DESCRIPTION>;[timez - one=<IANA Timezone>]] Defaults to None. - -``--repeat-actions {True|False}`` - True if actions should be repeatedly notified - while alarm remains in target state. - -``--granularity <GRANULARITY>`` - Length of each period (seconds) to evaluate - over. - -``--evaluation-periods <COUNT>`` - Number of periods to evaluate over. - -``--aggregation-method <AGGREATION>`` - Aggregation method to use, one of: ['last', - 'min', 'median', 'sum', 'std', 'first', - 'mean', 'count', 'moving-average', 'max', - '1pct', '2pct', '3pct', '4pct', '5pct', - '6pct', '7pct', '8pct', '9pct', '10pct', - '11pct', '12pct', '13pct', '14pct', '15pct', - '16pct', '17pct', '18pct', '19pct', '20pct', - '21pct', '22pct', '23pct', '24pct', '25pct', - '26pct', '27pct', '28pct', '29pct', '30pct', - '31pct', '32pct', '33pct', '34pct', '35pct', - '36pct', '37pct', '38pct', '39pct', '40pct', - '41pct', '42pct', '43pct', '44pct', '45pct', - '46pct', '47pct', '48pct', '49pct', '50pct', - '51pct', '52pct', '53pct', '54pct', '55pct', - '56pct', '57pct', '58pct', '59pct', '60pct', - '61pct', '62pct', '63pct', '64pct', '65pct', - '66pct', '67pct', '68pct', '69pct', '70pct', - '71pct', '72pct', '73pct', '74pct', '75pct', - '76pct', '77pct', '78pct', '79pct', '80pct', - '81pct', '82pct', '83pct', '84pct', '85pct', - '86pct', '87pct', '88pct', '89pct', '90pct', - '91pct', '92pct', '93pct', '94pct', '95pct', - '96pct', '97pct', '98pct', '99pct']. - -``--comparison-operator <OPERATOR>`` - Operator to compare with, one of: ['lt', 'le', - 'eq', 'ne', 'ge', 'gt']. - -``--threshold <THRESHOLD>`` - Threshold to evaluate against. - -``-m <METRIC>, --metric <METRIC>`` - Metric to evaluate against. - -``--resource-type <RESOURCE_TYPE>`` - Resource_type to evaluate against. - -``--query <QUERY>`` - Gnocchi resources search query filter - -``--remove-time-constraint <Constraint names>`` - Name or list of names of the time constraints - to remove. - -.. _ceilometer_alarm-gnocchi-resources-threshold-create: - -ceilometer alarm-gnocchi-resources-threshold-create ---------------------------------------------------- - -.. code-block:: console - - usage: ceilometer alarm-gnocchi-resources-threshold-create --name <NAME> - [--project-id <ALARM_PROJECT_ID>] - [--user-id <ALARM_USER_ID>] - [--description <DESCRIPTION>] - [--state <STATE>] - [--severity <SEVERITY>] - [--enabled {True|False}] - [--alarm-action <Webhook URL>] - [--ok-action <Webhook URL>] - [--insufficient-data-action <Webhook URL>] - [--time-constraint <Time Constraint>] - [--repeat-actions {True|False}] - [--granularity <GRANULARITY>] - [--evaluation-periods <COUNT>] - --aggregation-method - <AGGREATION> - [--comparison-operator <OPERATOR>] - --threshold - <THRESHOLD> -m - <METRIC> - --resource-type - <RESOURCE_TYPE> - --resource-id - <RESOURCE_ID> - -Create a new alarm based on computed statistics. - -**Optional arguments:** - -``--name <NAME>`` - Name of the alarm (must be unique per tenant). - Required. - -``--project-id <ALARM_PROJECT_ID>`` - Tenant to associate with alarm (configurable - by admin users only). - -``--user-id <ALARM_USER_ID>`` - User to associate with alarm (configurable by - admin users only). - -``--description <DESCRIPTION>`` - Free text description of the alarm. - -``--state <STATE>`` - State of the alarm, one of: ['ok', 'alarm', - 'insufficient data'] - -``--severity <SEVERITY>`` - Severity of the alarm, one of: ['low', - 'moderate', 'critical'] - -``--enabled {True|False}`` - True if alarm evaluation/actioning is enabled. - -``--alarm-action <Webhook URL>`` - URL to invoke when state transitions to alarm. - May be used multiple times. Defaults to None. - -``--ok-action <Webhook URL>`` - URL to invoke when state transitions to OK. - May be used multiple times. Defaults to None. - -``--insufficient-data-action <Webhook URL>`` - URL to invoke when state transitions to - insufficient data. May be used multiple times. - Defaults to None. - -``--time-constraint <Time Constraint>`` - Only evaluate the alarm if the time at - evaluation is within this time constraint. - Start point(s) of the constraint are specified - with a cron expression, whereas its duration - is given in seconds. Can be specified multiple - times for multiple time constraints, format - is: name=<CONSTRAINT_NAME>;start=<CRON>;durati - on=<SECONDS>;[description=<DESCRIPTION>;[timez - one=<IANA Timezone>]] Defaults to None. - -``--repeat-actions {True|False}`` - True if actions should be repeatedly notified - while alarm remains in target state. - -``--granularity <GRANULARITY>`` - Length of each period (seconds) to evaluate - over. - -``--evaluation-periods <COUNT>`` - Number of periods to evaluate over. - -``--aggregation-method <AGGREATION>`` - Aggregation method to use, one of: ['last', - 'min', 'median', 'sum', 'std', 'first', - 'mean', 'count', 'moving-average', 'max', - '1pct', '2pct', '3pct', '4pct', '5pct', - '6pct', '7pct', '8pct', '9pct', '10pct', - '11pct', '12pct', '13pct', '14pct', '15pct', - '16pct', '17pct', '18pct', '19pct', '20pct', - '21pct', '22pct', '23pct', '24pct', '25pct', - '26pct', '27pct', '28pct', '29pct', '30pct', - '31pct', '32pct', '33pct', '34pct', '35pct', - '36pct', '37pct', '38pct', '39pct', '40pct', - '41pct', '42pct', '43pct', '44pct', '45pct', - '46pct', '47pct', '48pct', '49pct', '50pct', - '51pct', '52pct', '53pct', '54pct', '55pct', - '56pct', '57pct', '58pct', '59pct', '60pct', - '61pct', '62pct', '63pct', '64pct', '65pct', - '66pct', '67pct', '68pct', '69pct', '70pct', - '71pct', '72pct', '73pct', '74pct', '75pct', - '76pct', '77pct', '78pct', '79pct', '80pct', - '81pct', '82pct', '83pct', '84pct', '85pct', - '86pct', '87pct', '88pct', '89pct', '90pct', - '91pct', '92pct', '93pct', '94pct', '95pct', - '96pct', '97pct', '98pct', '99pct']. Required. - -``--comparison-operator <OPERATOR>`` - Operator to compare with, one of: ['lt', 'le', - 'eq', 'ne', 'ge', 'gt']. - -``--threshold <THRESHOLD>`` - Threshold to evaluate against. Required. - -``-m <METRIC>, --metric <METRIC>`` - Metric to evaluate against. Required. - -``--resource-type <RESOURCE_TYPE>`` - Resource_type to evaluate against. Required. - -``--resource-id <RESOURCE_ID>`` - Resource id to evaluate against Required. - -.. _ceilometer_alarm-gnocchi-resources-threshold-update: - -ceilometer alarm-gnocchi-resources-threshold-update ---------------------------------------------------- - -.. code-block:: console - - usage: ceilometer alarm-gnocchi-resources-threshold-update [--name <NAME>] - [--project-id <ALARM_PROJECT_ID>] - [--user-id <ALARM_USER_ID>] - [--description <DESCRIPTION>] - [--state <STATE>] - [--severity <SEVERITY>] - [--enabled {True|False}] - [--alarm-action <Webhook URL>] - [--ok-action <Webhook URL>] - [--insufficient-data-action <Webhook URL>] - [--time-constraint <Time Constraint>] - [--repeat-actions {True|False}] - [--granularity <GRANULARITY>] - [--evaluation-periods <COUNT>] - [--aggregation-method <AGGREATION>] - [--comparison-operator <OPERATOR>] - [--threshold <THRESHOLD>] - [-m <METRIC>] - [--resource-type <RESOURCE_TYPE>] - [--resource-id <RESOURCE_ID>] - [--remove-time-constraint <Constraint names>] - [<ALARM_ID>] - -Update an existing alarm based on computed statistics. - -**Positional arguments:** - -``<ALARM_ID>`` - ID of the alarm to update. - -**Optional arguments:** - -``--name <NAME>`` - Name of the alarm (must be unique per tenant). - -``--project-id <ALARM_PROJECT_ID>`` - Tenant to associate with alarm (configurable - by admin users only). - -``--user-id <ALARM_USER_ID>`` - User to associate with alarm (configurable by - admin users only). - -``--description <DESCRIPTION>`` - Free text description of the alarm. - -``--state <STATE>`` - State of the alarm, one of: ['ok', 'alarm', - 'insufficient data'] - -``--severity <SEVERITY>`` - Severity of the alarm, one of: ['low', - 'moderate', 'critical'] - -``--enabled {True|False}`` - True if alarm evaluation/actioning is enabled. - -``--alarm-action <Webhook URL>`` - URL to invoke when state transitions to alarm. - May be used multiple times. Defaults to None. - -``--ok-action <Webhook URL>`` - URL to invoke when state transitions to OK. - May be used multiple times. Defaults to None. - -``--insufficient-data-action <Webhook URL>`` - URL to invoke when state transitions to - insufficient data. May be used multiple times. - Defaults to None. - -``--time-constraint <Time Constraint>`` - Only evaluate the alarm if the time at - evaluation is within this time constraint. - Start point(s) of the constraint are specified - with a cron expression, whereas its duration - is given in seconds. Can be specified multiple - times for multiple time constraints, format - is: name=<CONSTRAINT_NAME>;start=<CRON>;durati - on=<SECONDS>;[description=<DESCRIPTION>;[timez - one=<IANA Timezone>]] Defaults to None. - -``--repeat-actions {True|False}`` - True if actions should be repeatedly notified - while alarm remains in target state. - -``--granularity <GRANULARITY>`` - Length of each period (seconds) to evaluate - over. - -``--evaluation-periods <COUNT>`` - Number of periods to evaluate over. - -``--aggregation-method <AGGREATION>`` - Aggregation method to use, one of: ['last', - 'min', 'median', 'sum', 'std', 'first', - 'mean', 'count', 'moving-average', 'max', - '1pct', '2pct', '3pct', '4pct', '5pct', - '6pct', '7pct', '8pct', '9pct', '10pct', - '11pct', '12pct', '13pct', '14pct', '15pct', - '16pct', '17pct', '18pct', '19pct', '20pct', - '21pct', '22pct', '23pct', '24pct', '25pct', - '26pct', '27pct', '28pct', '29pct', '30pct', - '31pct', '32pct', '33pct', '34pct', '35pct', - '36pct', '37pct', '38pct', '39pct', '40pct', - '41pct', '42pct', '43pct', '44pct', '45pct', - '46pct', '47pct', '48pct', '49pct', '50pct', - '51pct', '52pct', '53pct', '54pct', '55pct', - '56pct', '57pct', '58pct', '59pct', '60pct', - '61pct', '62pct', '63pct', '64pct', '65pct', - '66pct', '67pct', '68pct', '69pct', '70pct', - '71pct', '72pct', '73pct', '74pct', '75pct', - '76pct', '77pct', '78pct', '79pct', '80pct', - '81pct', '82pct', '83pct', '84pct', '85pct', - '86pct', '87pct', '88pct', '89pct', '90pct', - '91pct', '92pct', '93pct', '94pct', '95pct', - '96pct', '97pct', '98pct', '99pct']. - -``--comparison-operator <OPERATOR>`` - Operator to compare with, one of: ['lt', 'le', - 'eq', 'ne', 'ge', 'gt']. - -``--threshold <THRESHOLD>`` - Threshold to evaluate against. - -``-m <METRIC>, --metric <METRIC>`` - Metric to evaluate against. - -``--resource-type <RESOURCE_TYPE>`` - Resource_type to evaluate against. - -``--resource-id <RESOURCE_ID>`` - Resource id to evaluate against - -``--remove-time-constraint <Constraint names>`` - Name or list of names of the time constraints - to remove. - -.. _ceilometer_alarm-history: - -ceilometer alarm-history ------------------------- - -.. code-block:: console - - usage: ceilometer alarm-history [-q <QUERY>] [<ALARM_ID>] - -Display the change history of an alarm. - -**Positional arguments:** - -``<ALARM_ID>`` - ID of the alarm for which history is shown. - -**Optional arguments:** - -``-q <QUERY>, --query <QUERY>`` - key[op]data_type::value; list. data_type is - optional, but if supplied must be string, - integer, float, or boolean. - -.. _ceilometer_alarm-list: - -ceilometer alarm-list ---------------------- - -.. code-block:: console - - usage: ceilometer alarm-list [-q <QUERY>] - -List the user's alarms. - -**Optional arguments:** - -``-q <QUERY>, --query <QUERY>`` - key[op]data_type::value; list. data_type is - optional, but if supplied must be string, - integer, float, or boolean. - -.. _ceilometer_alarm-show: - -ceilometer alarm-show ---------------------- - -.. code-block:: console - - usage: ceilometer alarm-show [<ALARM_ID>] - -Show an alarm. - -**Positional arguments:** - -``<ALARM_ID>`` - ID of the alarm to show. - -.. _ceilometer_alarm-state-get: - -ceilometer alarm-state-get --------------------------- - -.. code-block:: console - - usage: ceilometer alarm-state-get [<ALARM_ID>] - -Get the state of an alarm. - -**Positional arguments:** - -``<ALARM_ID>`` - ID of the alarm state to show. - -.. _ceilometer_alarm-state-set: - -ceilometer alarm-state-set --------------------------- - -.. code-block:: console - - usage: ceilometer alarm-state-set --state <STATE> [<ALARM_ID>] - -Set the state of an alarm. - -**Positional arguments:** - -``<ALARM_ID>`` - ID of the alarm state to set. - -**Optional arguments:** - -``--state <STATE>`` - State of the alarm, one of: ['ok', 'alarm', 'insufficient - data']. Required. - -.. _ceilometer_alarm-threshold-create: - -ceilometer alarm-threshold-create ---------------------------------- - -.. code-block:: console - - usage: ceilometer alarm-threshold-create --name <NAME> - [--project-id <ALARM_PROJECT_ID>] - [--user-id <ALARM_USER_ID>] - [--description <DESCRIPTION>] - [--state <STATE>] - [--severity <SEVERITY>] - [--enabled {True|False}] - [--alarm-action <Webhook URL>] - [--ok-action <Webhook URL>] - [--insufficient-data-action <Webhook URL>] - [--time-constraint <Time Constraint>] - [--repeat-actions {True|False}] -m - <METRIC> [--period <PERIOD>] - [--evaluation-periods <COUNT>] - [--statistic <STATISTIC>] - [--comparison-operator <OPERATOR>] - --threshold <THRESHOLD> [-q <QUERY>] - -Create a new alarm based on computed statistics. - -**Optional arguments:** - -``--name <NAME>`` - Name of the alarm (must be unique per tenant). - Required. - -``--project-id <ALARM_PROJECT_ID>`` - Tenant to associate with alarm (configurable - by admin users only). - -``--user-id <ALARM_USER_ID>`` - User to associate with alarm (configurable by - admin users only). - -``--description <DESCRIPTION>`` - Free text description of the alarm. - -``--state <STATE>`` - State of the alarm, one of: ['ok', 'alarm', - 'insufficient data'] - -``--severity <SEVERITY>`` - Severity of the alarm, one of: ['low', - 'moderate', 'critical'] - -``--enabled {True|False}`` - True if alarm evaluation/actioning is enabled. - -``--alarm-action <Webhook URL>`` - URL to invoke when state transitions to alarm. - May be used multiple times. Defaults to None. - -``--ok-action <Webhook URL>`` - URL to invoke when state transitions to OK. - May be used multiple times. Defaults to None. - -``--insufficient-data-action <Webhook URL>`` - URL to invoke when state transitions to - insufficient data. May be used multiple times. - Defaults to None. - -``--time-constraint <Time Constraint>`` - Only evaluate the alarm if the time at - evaluation is within this time constraint. - Start point(s) of the constraint are specified - with a cron expression, whereas its duration - is given in seconds. Can be specified multiple - times for multiple time constraints, format - is: name=<CONSTRAINT_NAME>;start=<CRON>;durati - on=<SECONDS>;[description=<DESCRIPTION>;[timez - one=<IANA Timezone>]] Defaults to None. - -``--repeat-actions {True|False}`` - True if actions should be repeatedly notified - while alarm remains in target state. - -``-m <METRIC>, --meter-name <METRIC>`` - Metric to evaluate against. Required. - -``--period <PERIOD>`` - Length of each period (seconds) to evaluate - over. - -``--evaluation-periods <COUNT>`` - Number of periods to evaluate over. - -``--statistic <STATISTIC>`` - Statistic to evaluate, one of: ['max', 'min', - 'avg', 'sum', 'count']. - -``--comparison-operator <OPERATOR>`` - Operator to compare with, one of: ['lt', 'le', - 'eq', 'ne', 'ge', 'gt']. - -``--threshold <THRESHOLD>`` - Threshold to evaluate against. Required. - -``-q <QUERY>, --query <QUERY>`` - key[op]data_type::value; list. data_type is - optional, but if supplied must be string, - integer, float, or boolean. - -.. _ceilometer_alarm-threshold-update: - -ceilometer alarm-threshold-update ---------------------------------- - -.. code-block:: console - - usage: ceilometer alarm-threshold-update [--name <NAME>] - [--project-id <ALARM_PROJECT_ID>] - [--user-id <ALARM_USER_ID>] - [--description <DESCRIPTION>] - [--state <STATE>] - [--severity <SEVERITY>] - [--enabled {True|False}] - [--alarm-action <Webhook URL>] - [--ok-action <Webhook URL>] - [--insufficient-data-action <Webhook URL>] - [--time-constraint <Time Constraint>] - [--repeat-actions {True|False}] - [--remove-time-constraint <Constraint names>] - [-m <METRIC>] [--period <PERIOD>] - [--evaluation-periods <COUNT>] - [--statistic <STATISTIC>] - [--comparison-operator <OPERATOR>] - [--threshold <THRESHOLD>] - [-q <QUERY>] - [<ALARM_ID>] - -Update an existing alarm based on computed statistics. - -**Positional arguments:** - -``<ALARM_ID>`` - ID of the alarm to update. - -**Optional arguments:** - -``--name <NAME>`` - Name of the alarm (must be unique per tenant). - -``--project-id <ALARM_PROJECT_ID>`` - Tenant to associate with alarm (configurable - by admin users only). - -``--user-id <ALARM_USER_ID>`` - User to associate with alarm (configurable by - admin users only). - -``--description <DESCRIPTION>`` - Free text description of the alarm. - -``--state <STATE>`` - State of the alarm, one of: ['ok', 'alarm', - 'insufficient data'] - -``--severity <SEVERITY>`` - Severity of the alarm, one of: ['low', - 'moderate', 'critical'] - -``--enabled {True|False}`` - True if alarm evaluation/actioning is enabled. - -``--alarm-action <Webhook URL>`` - URL to invoke when state transitions to alarm. - May be used multiple times. Defaults to None. - -``--ok-action <Webhook URL>`` - URL to invoke when state transitions to OK. - May be used multiple times. Defaults to None. - -``--insufficient-data-action <Webhook URL>`` - URL to invoke when state transitions to - insufficient data. May be used multiple times. - Defaults to None. - -``--time-constraint <Time Constraint>`` - Only evaluate the alarm if the time at - evaluation is within this time constraint. - Start point(s) of the constraint are specified - with a cron expression, whereas its duration - is given in seconds. Can be specified multiple - times for multiple time constraints, format - is: name=<CONSTRAINT_NAME>;start=<CRON>;durati - on=<SECONDS>;[description=<DESCRIPTION>;[timez - one=<IANA Timezone>]] Defaults to None. - -``--repeat-actions {True|False}`` - True if actions should be repeatedly notified - while alarm remains in target state. - -``--remove-time-constraint <Constraint names>`` - Name or list of names of the time constraints - to remove. - -``-m <METRIC>, --meter-name <METRIC>`` - Metric to evaluate against. - -``--period <PERIOD>`` - Length of each period (seconds) to evaluate - over. - -``--evaluation-periods <COUNT>`` - Number of periods to evaluate over. - -``--statistic <STATISTIC>`` - Statistic to evaluate, one of: ['max', 'min', - 'avg', 'sum', 'count']. - -``--comparison-operator <OPERATOR>`` - Operator to compare with, one of: ['lt', 'le', - 'eq', 'ne', 'ge', 'gt']. - -``--threshold <THRESHOLD>`` - Threshold to evaluate against. - -``-q <QUERY>, --query <QUERY>`` - key[op]data_type::value; list. data_type is - optional, but if supplied must be string, - integer, float, or boolean. - -.. _ceilometer_capabilities: - -ceilometer capabilities ------------------------ - -.. code-block:: console - - usage: ceilometer capabilities - -Print Ceilometer capabilities. - -.. _ceilometer_event-list: - -ceilometer event-list ---------------------- - -.. code-block:: console - - usage: ceilometer event-list [-q <QUERY>] [--no-traits] [-l <NUMBER>] - -List events. - -**Optional arguments:** - -``-q <QUERY>, --query <QUERY>`` - key[op]data_type::value; list. data_type is - optional, but if supplied must be string, - integer, float or datetime. - -``--no-traits`` - If specified, traits will not be printed. - -``-l <NUMBER>, --limit <NUMBER>`` - Maximum number of events to return. API server - limits result to <default_api_return_limit> - rows if no limit provided. Option is - configured in ceilometer.conf [api] group - -.. _ceilometer_event-show: - -ceilometer event-show ---------------------- - -.. code-block:: console - - usage: ceilometer event-show <message_id> - -Show a particular event. - -**Positional arguments:** - -``<message_id>`` - The ID of the event. Should be a UUID. - -.. _ceilometer_event-type-list: - -ceilometer event-type-list --------------------------- - -.. code-block:: console - - usage: ceilometer event-type-list - -List event types. - -.. _ceilometer_meter-list: - -ceilometer meter-list ---------------------- - -.. code-block:: console - - usage: ceilometer meter-list [-q <QUERY>] [-l <NUMBER>] - [--unique {True|False}] - -List the user's meters. - -**Optional arguments:** - -``-q <QUERY>, --query <QUERY>`` - key[op]data_type::value; list. data_type is - optional, but if supplied must be string, - integer, float, or boolean. - -``-l <NUMBER>, --limit <NUMBER>`` - Maximum number of meters to return. API server - limits result to <default_api_return_limit> - rows if no limit provided. Option is - configured in ceilometer.conf [api] group - -``--unique {True|False}`` - Retrieves unique list of meters. - -.. _ceilometer_query-alarm-history: - -ceilometer query-alarm-history ------------------------------- - -.. code-block:: console - - usage: ceilometer query-alarm-history [-f <FILTER>] [-o <ORDERBY>] - [-l <LIMIT>] - -Query Alarm History. - -**Optional arguments:** - -``-f <FILTER>, --filter <FILTER>`` - {complex_op: [{simple_op: {field_name: - value}}]} The complex_op is one of: ['and', - 'or'], simple_op is one of: ['=', '!=', '<', - '<=', '>', '>=']. - -``-o <ORDERBY>, --orderby <ORDERBY>`` - [{field_name: direction}, {field_name: - direction}] The direction is one of: ['asc', - 'desc']. - -``-l <LIMIT>, --limit <LIMIT>`` - Maximum number of alarm history items to - return. API server limits result to - <default_api_return_limit> rows if no limit - provided. Option is configured in - ceilometer.conf [api] group - -.. _ceilometer_query-alarms: - -ceilometer query-alarms ------------------------ - -.. code-block:: console - - usage: ceilometer query-alarms [-f <FILTER>] [-o <ORDERBY>] [-l <LIMIT>] - -Query Alarms. - -**Optional arguments:** - -``-f <FILTER>, --filter <FILTER>`` - {complex_op: [{simple_op: {field_name: - value}}]} The complex_op is one of: ['and', - 'or'], simple_op is one of: ['=', '!=', '<', - '<=', '>', '>=']. - -``-o <ORDERBY>, --orderby <ORDERBY>`` - [{field_name: direction}, {field_name: - direction}] The direction is one of: ['asc', - 'desc']. - -``-l <LIMIT>, --limit <LIMIT>`` - Maximum number of alarms to return. API server - limits result to <default_api_return_limit> - rows if no limit provided. Option is - configured in ceilometer.conf [api] group - -.. _ceilometer_query-samples: - -ceilometer query-samples ------------------------- - -.. code-block:: console - - usage: ceilometer query-samples [-f <FILTER>] [-o <ORDERBY>] [-l <LIMIT>] - -Query samples. - -**Optional arguments:** - -``-f <FILTER>, --filter <FILTER>`` - {complex_op: [{simple_op: {field_name: - value}}]} The complex_op is one of: ['and', - 'or'], simple_op is one of: ['=', '!=', '<', - '<=', '>', '>=']. - -``-o <ORDERBY>, --orderby <ORDERBY>`` - [{field_name: direction}, {field_name: - direction}] The direction is one of: ['asc', - 'desc']. - -``-l <LIMIT>, --limit <LIMIT>`` - Maximum number of samples to return. API - server limits result to - <default_api_return_limit> rows if no limit - provided. Option is configured in - ceilometer.conf [api] group - -.. _ceilometer_resource-list: - -ceilometer resource-list ------------------------- - -.. code-block:: console - - usage: ceilometer resource-list [-q <QUERY>] [-l <NUMBER>] - -List the resources. - -**Optional arguments:** - -``-q <QUERY>, --query <QUERY>`` - key[op]data_type::value; list. data_type is - optional, but if supplied must be string, - integer, float, or boolean. - -``-l <NUMBER>, --limit <NUMBER>`` - Maximum number of resources to return. API - server limits result to - <default_api_return_limit> rows if no limit - provided. Option is configured in - ceilometer.conf [api] group - -.. _ceilometer_resource-show: - -ceilometer resource-show ------------------------- - -.. code-block:: console - - usage: ceilometer resource-show <RESOURCE_ID> - -Show the resource. - -**Positional arguments:** - -``<RESOURCE_ID>`` - ID of the resource to show. - -.. _ceilometer_sample-create: - -ceilometer sample-create ------------------------- - -.. code-block:: console - - usage: ceilometer sample-create [--project-id <SAMPLE_PROJECT_ID>] - [--user-id <SAMPLE_USER_ID>] -r <RESOURCE_ID> - -m <METER_NAME> --meter-type <METER_TYPE> - --meter-unit <METER_UNIT> --sample-volume - <SAMPLE_VOLUME> - [--resource-metadata <RESOURCE_METADATA>] - [--timestamp <TIMESTAMP>] [--direct <DIRECT>] - -Create a sample. - -**Optional arguments:** - -``--project-id <SAMPLE_PROJECT_ID>`` - Tenant to associate with sample (configurable - by admin users only). - -``--user-id <SAMPLE_USER_ID>`` - User to associate with sample (configurable by - admin users only). - -``-r <RESOURCE_ID>, --resource-id <RESOURCE_ID>`` - ID of the resource. Required. - -``-m <METER_NAME>, --meter-name <METER_NAME>`` - The meter name. Required. - -``--meter-type <METER_TYPE>`` - The meter type. Required. - -``--meter-unit <METER_UNIT>`` - The meter unit. Required. - -``--sample-volume <SAMPLE_VOLUME>`` - The sample volume. Required. - -``--resource-metadata <RESOURCE_METADATA>`` - Resource metadata. Provided value should be a - set of key-value pairs e.g. {"key":"value"}. - -``--timestamp <TIMESTAMP>`` - The sample timestamp. - -``--direct <DIRECT>`` - Post sample to storage directly. Defaults to - False. - -.. _ceilometer_sample-create-list: - -ceilometer sample-create-list ------------------------------ - -.. code-block:: console - - usage: ceilometer sample-create-list [--direct <DIRECT>] <SAMPLES_LIST> - -Create a sample list. - -**Positional arguments:** - -``<SAMPLES_LIST>`` - Json array with samples to create. - -**Optional arguments:** - -``--direct <DIRECT>`` - Post samples to storage directly. Defaults to False. - -.. _ceilometer_sample-list: - -ceilometer sample-list ----------------------- - -.. code-block:: console - - usage: ceilometer sample-list [-q <QUERY>] [-m <NAME>] [-l <NUMBER>] - -List the samples (return OldSample objects if -m/--meter is set). - -**Optional arguments:** - -``-q <QUERY>, --query <QUERY>`` - key[op]data_type::value; list. data_type is - optional, but if supplied must be string, - integer, float, or boolean. - -``-m <NAME>, --meter <NAME>`` - Name of meter to show samples for. - -``-l <NUMBER>, --limit <NUMBER>`` - Maximum number of samples to return. API - server limits result to - <default_api_return_limit> rows if no limit - provided. Option is configured in - ceilometer.conf [api] group - -.. _ceilometer_sample-show: - -ceilometer sample-show ----------------------- - -.. code-block:: console - - usage: ceilometer sample-show <SAMPLE_ID> - -Show a sample. - -**Positional arguments:** - -``<SAMPLE_ID>`` - ID (aka message ID) of the sample to show. - -.. _ceilometer_statistics: - -ceilometer statistics ---------------------- - -.. code-block:: console - - usage: ceilometer statistics [-q <QUERY>] -m <NAME> [-p <PERIOD>] [-g <FIELD>] - [-a <FUNC>[<-<PARAM>]] - -List the statistics for a meter. - -**Optional arguments:** - -``-q <QUERY>, --query <QUERY>`` - key[op]data_type::value; list. data_type is - optional, but if supplied must be string, - integer, float, or boolean. - -``-m <NAME>, --meter <NAME>`` - Name of meter to list statistics for. - Required. - -``-p <PERIOD>, --period <PERIOD>`` - Period in seconds over which to group samples. - -``-g <FIELD>, --groupby <FIELD>`` - Field for group by. - -``-a <FUNC>[<-<PARAM>], --aggregate <FUNC>[<-<PARAM>]`` - Function for data aggregation. Available - aggregates are: count, cardinality, min, max, - sum, stddev, avg. Defaults to []. - -.. _ceilometer_trait-description-list: - -ceilometer trait-description-list ---------------------------------- - -.. code-block:: console - - usage: ceilometer trait-description-list -e <EVENT_TYPE> - -List trait info for an event type. - -**Optional arguments:** - -``-e <EVENT_TYPE>, --event_type <EVENT_TYPE>`` - Type of the event for which traits will be - shown. Required. - -.. _ceilometer_trait-list: - -ceilometer trait-list ---------------------- - -.. code-block:: console - - usage: ceilometer trait-list -e <EVENT_TYPE> -t <TRAIT_NAME> - -List all traits with name <trait_name> for Event Type <event_type>. - -**Optional arguments:** - -``-e <EVENT_TYPE>, --event_type <EVENT_TYPE>`` - Type of the event for which traits will - listed. Required. - -``-t <TRAIT_NAME>, --trait_name <TRAIT_NAME>`` - The name of the trait to list. Required. - diff --git a/doc/source/conf.py b/doc/source/conf.py deleted file mode 100644 index 00d4a79..0000000 --- a/doc/source/conf.py +++ /dev/null @@ -1,85 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import os -execfile(os.path.join("..", "ext", "gen_ref.py")) - -project = 'python-ceilometerclient' - -gen_ref("", "Client Reference", ["client", "exc"]) -gen_ref("v2", "Version 2 API Reference", - ["meters", "samples", "statistics", "resources", "query", "alarms", - "events", "event_types", "traits", "trait_descriptions"]) - -# -- General configuration ---------------------------------------------------- - -# 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', 'openstackdocstheme'] - -# autodoc generation is a bit aggressive and a nuisance when doing heavy -# text edit cycles. -# execute "export SPHINX_DEBUG=1" in your terminal to disable - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -copyright = u'OpenStack Foundation' - -# If true, '()' will be appended to :func: etc. cross-reference text. -add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -add_module_names = True - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# -- Options for HTML output -------------------------------------------------- - -# 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 = 'openstackdocs' - -# openstackdocstheme options -repository_name = 'openstack/python-ceilometerclient' -bug_project = 'python-ceilometerclient' -bug_tag = '' - -# Must set this variable to include year, month, day, hours, and minutes. -html_last_updated_fmt = '%Y-%m-%d %H:%M' - -# Output file base name for HTML help builder. -htmlhelp_basename = '%sdoc' % project - - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass -# [howto/manual]). -latex_documents = [ - ( - 'index', - '%s.tex' % project, - u'%s Documentation' % project, - u'OpenStack Foundation', - 'manual' - ), -] diff --git a/doc/source/index.rst b/doc/source/index.rst deleted file mode 100644 index eabd851..0000000 --- a/doc/source/index.rst +++ /dev/null @@ -1,56 +0,0 @@ -Python bindings to the OpenStack Ceilometer API -================================================== - -This is a client for OpenStack Ceilometer API. There's :doc:`a Python API -<api>` (the :mod:`ceilometerclient` module), and a :doc:`command-line script -<shell>` (installed as :program:`ceilometer`). Each implements the entire -OpenStack Ceilometer API. - -.. seealso:: - - You may want to read the `OpenStack Ceilometer Developer Guide`__ -- the overview, at - least -- to get an idea of the concepts. By understanding the concepts - this library should make more sense. - - __ https://docs.openstack.org/ceilometer/latest/ - -Contents: - -.. toctree:: - :maxdepth: 2 - - shell - api - ref/index - ref/v2/index - -The command-line client -======================= - -The ceilometer client is the command-line interface (CLI) for -the Telemetry Data Collection service (ceilometer) API -and its extensions. - -.. toctree:: - - cli - -Contributing -============ - -Code is hosted at `git.openstack.org`_. Submit bugs to the Ceilometer project on -`Launchpad`_. Submit code to the openstack/python-ceilometerclient project using -`Gerrit`_. - -.. _git.openstack.org: https://git.openstack.org/cgit/openstack/python-ceilometerclient -.. _Launchpad: https://launchpad.net/ceilometer -.. _Gerrit: https://docs.openstack.org/infra/manual/developers.html#development-workflow - -Run tests with ``python setup.py test``. - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/doc/source/shell.rst b/doc/source/shell.rst deleted file mode 100644 index a35a9a1..0000000 --- a/doc/source/shell.rst +++ /dev/null @@ -1,61 +0,0 @@ -The :program:`ceilometer` shell utility -========================================= - -.. program:: ceilometer -.. highlight:: bash - -The :program:`ceilometer` shell utility interacts with OpenStack Ceilometer API -from the command line. It supports the entirety of the OpenStack Ceilometer API. - -You'll need to provide :program:`ceilometer` with your OpenStack credentials. -You can do this with the :option:`--os-username`, :option:`--os-password`, -:option:`--os-tenant-id` and :option:`--os-auth-url` options, but it's easier to -just set them as environment variables: - -.. envvar:: OS_USERNAME - - Your OpenStack username. - -.. envvar:: OS_PASSWORD - - Your password. - -.. envvar:: OS_TENANT_NAME - - Project to work on. - -.. envvar:: OS_AUTH_URL - - The OpenStack auth server URL (keystone). - -For example, in Bash you would use:: - - export OS_USERNAME=user - export OS_PASSWORD=pass - export OS_TENANT_NAME=myproject - export OS_AUTH_URL=http://auth.example.com:5000/v2.0 - -The command line tool will attempt to reauthenticate using your provided credentials -for every request. You can override this behavior by manually supplying an auth -token using :option:`--os-ceilometer-url` and :option:`--os-auth-token`. You can alternatively -set these environment variables:: - - export OS_CEILOMETER_URL=http://ceilometer.example.org:8777 - export OS_AUTH_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155 - -From there, all shell commands take the form:: - - ceilometer <command> [arguments...] - -Run :program:`ceilometer help` to get a full list of all possible commands, -and run :program:`ceilometer help <command>` to get detailed help for that -command. - -V2 client tips -++++++++++++++ - -Use queries to narrow your search (more info at `Ceilometer V2 API reference`__):: - - ceilometer sample-list --meter cpu_util --query 'resource_id=5a301761-f78b-46e2-8900-8b4f6fe6675a' --limit 10 - -__ https://docs.openstack.org/ceilometer/latest/webapi/v2.html#Query diff --git a/releasenotes/notes/.placeholder b/releasenotes/notes/.placeholder deleted file mode 100644 index e69de29..0000000 --- a/releasenotes/notes/.placeholder +++ /dev/null diff --git a/releasenotes/notes/alarm_deprecated-74363d70d48a20e2.yaml b/releasenotes/notes/alarm_deprecated-74363d70d48a20e2.yaml deleted file mode 100644 index f50901b..0000000 --- a/releasenotes/notes/alarm_deprecated-74363d70d48a20e2.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -deprecations: - - Alarm commands are deprecated in favor of aodhclient. diff --git a/releasenotes/notes/deprecation-44ae455f4ef3a81e.yaml b/releasenotes/notes/deprecation-44ae455f4ef3a81e.yaml deleted file mode 100644 index 8a182a5..0000000 --- a/releasenotes/notes/deprecation-44ae455f4ef3a81e.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -deprecations: - - | - As the Ceilometer API has been deprecated, this client is also now marked - as deprecated and will print a warning when used as a command-line - interface tool. diff --git a/releasenotes/notes/panko-redirect-9d03598dbf51f8fd.yaml b/releasenotes/notes/panko-redirect-9d03598dbf51f8fd.yaml deleted file mode 100644 index 36f1c8e..0000000 --- a/releasenotes/notes/panko-redirect-9d03598dbf51f8fd.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -prelude: > - Panko replaces the API and storage of events previously in Ceilometer -features: - - | - Similar to aodh redirect support, specify `panko_endpoint` as a redirect - to Panko API. diff --git a/releasenotes/source/_static/.placeholder b/releasenotes/source/_static/.placeholder deleted file mode 100644 index e69de29..0000000 --- a/releasenotes/source/_static/.placeholder +++ /dev/null diff --git a/releasenotes/source/_templates/.placeholder b/releasenotes/source/_templates/.placeholder deleted file mode 100644 index e69de29..0000000 --- a/releasenotes/source/_templates/.placeholder +++ /dev/null diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py deleted file mode 100644 index 9ddac03..0000000 --- a/releasenotes/source/conf.py +++ /dev/null @@ -1,284 +0,0 @@ -# -*- coding: utf-8 -*- -# 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. -# -# Ceilometer Client Release Notes documentation build configuration file, -# created by sphinx-quickstart on Mon Nov 23 20:38:38 2015. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'reno.sphinxext', - 'openstackdocstheme' -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -# source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'Ceilometer Client Release Notes' -copyright = u'2015-present, Ceilometer developers' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -import pbr.version -ceilometer_version = pbr.version.VersionInfo('python-ceilometerclient') -# The short X.Y version. -version = ceilometer_version.canonical_version_string() -# The full version, including alpha/beta/rc tags. -release = ceilometer_version.version_string_with_vcs() - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# today = '' -# Else, today_fmt is used as the format for a strftime call. -# today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -# default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -# keep_warnings = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'openstackdocs' - -# openstackdocstheme options -repository_name = 'openstack/python-ceilometerclient' -bug_project = 'python-ceilometerclient' -bug_tag = '' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -# html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# "<project> v<release> documentation". -# html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -# html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' -html_last_updated_fmt = '%Y-%m-%d %H:%M' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_domain_indices = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a <link> tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'CeilometerClientReleaseNotestdoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # 'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ('index', 'PythonCeilometerClient.tex', - u'Ceilometer Client Release Notes Documentation', - u'Ceilometer developers', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# latex_use_parts = False - -# If true, show page references after internal links. -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# latex_appendices = [] - -# If false, no module index is generated. -# latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'pythonceilometerclient', - u'Ceilometer Client Release Notes Documentation', - [u'Ceilometer developers'], 1) -] - -# If true, show URL addresses after external links. -# man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'PythonCeilometerClient', - u'Ceilometer Client Release Notes Documentation', - u'Ceilometer developers', 'PythonCeilometerClient', - 'One line description of project.', 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -# texinfo_appendices = [] - -# If false, no module index is generated. -# texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -# texinfo_no_detailmenu = False - -# -- Options for Internationalization output ------------------------------ -locale_dirs = ['locale/'] diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst deleted file mode 100644 index 17fb7a5..0000000 --- a/releasenotes/source/index.rst +++ /dev/null @@ -1,21 +0,0 @@ -Welcome to Ceilometer Client Release Notes documentation! -========================================================= - -Contents -======== - -.. toctree:: - :maxdepth: 2 - - unreleased - pike - ocata - newton - mitaka - liberty - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`search` diff --git a/releasenotes/source/liberty.rst b/releasenotes/source/liberty.rst deleted file mode 100644 index 36217be..0000000 --- a/releasenotes/source/liberty.rst +++ /dev/null @@ -1,6 +0,0 @@ -============================== - Liberty Series Release Notes -============================== - -.. release-notes:: - :branch: origin/stable/liberty diff --git a/releasenotes/source/mitaka.rst b/releasenotes/source/mitaka.rst deleted file mode 100644 index e545609..0000000 --- a/releasenotes/source/mitaka.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Mitaka Series Release Notes -=================================== - -.. release-notes:: - :branch: origin/stable/mitaka diff --git a/releasenotes/source/newton.rst b/releasenotes/source/newton.rst deleted file mode 100644 index 97036ed..0000000 --- a/releasenotes/source/newton.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Newton Series Release Notes -=================================== - -.. release-notes:: - :branch: origin/stable/newton diff --git a/releasenotes/source/ocata.rst b/releasenotes/source/ocata.rst deleted file mode 100644 index ebe62f4..0000000 --- a/releasenotes/source/ocata.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Ocata Series Release Notes -=================================== - -.. release-notes:: - :branch: origin/stable/ocata diff --git a/releasenotes/source/pike.rst b/releasenotes/source/pike.rst deleted file mode 100644 index e43bfc0..0000000 --- a/releasenotes/source/pike.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Pike Series Release Notes -=================================== - -.. release-notes:: - :branch: stable/pike diff --git a/releasenotes/source/unreleased.rst b/releasenotes/source/unreleased.rst deleted file mode 100644 index 875030f..0000000 --- a/releasenotes/source/unreleased.rst +++ /dev/null @@ -1,5 +0,0 @@ -============================ -Current Series Release Notes -============================ - -.. release-notes:: diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index f13ff9b..0000000 --- a/requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. -pbr>=1.6 # Apache-2.0 -iso8601>=0.1.11 # MIT -keystoneauth1>=2.1.0 # Apache-2.0 -oslo.i18n>=2.1.0 # Apache-2.0 -oslo.serialization>=1.10.0 # Apache-2.0 -oslo.utils>=3.17.0 # Apache-2.0 -PrettyTable<0.8,>=0.7 # BSD -requests!=2.9.0,>=2.8.1 # Apache-2.0 -six>=1.9.0 # MIT -stevedore>=1.10.0 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 5df04f3..0000000 --- a/setup.cfg +++ /dev/null @@ -1,42 +0,0 @@ -[metadata] -name = python-ceilometerclient -summary = OpenStack Telemetry API Client Library -description-file = - README.rst -author = OpenStack -author-email = openstack-dev@lists.openstack.org -home-page = https://docs.openstack.org/python-ceilometerclient/latest/ -classifier = - Environment :: OpenStack - Intended Audience :: Information Technology - Intended Audience :: System Administrators - License :: OSI Approved :: Apache Software License - Operating System :: POSIX :: Linux - Programming Language :: Python - Programming Language :: Python :: 2 - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - -[files] -packages = - ceilometerclient - -[global] -setup-hooks = - pbr.hooks.setup_hook - -[entry_points] -console_scripts = - ceilometer = ceilometerclient.shell:main - -[build_sphinx] -source-dir = doc/source -build-dir = doc/build -all_files = 1 - -[upload_sphinx] -upload-dir = doc/build/html - -[wheel] -universal = 1 diff --git a/setup.py b/setup.py deleted file mode 100644 index 782bb21..0000000 --- a/setup.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. -# -# 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. - -# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT -import setuptools - -# In python < 2.7.4, a lazy loading of package `pbr` will break -# setuptools if some other modules registered functions in `atexit`. -# solution from: http://bugs.python.org/issue15881#msg170215 -try: - import multiprocessing # noqa -except ImportError: - pass - -setuptools.setup( - setup_requires=['pbr>=1.8'], - pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index b033e82..0000000 --- a/test-requirements.txt +++ /dev/null @@ -1,14 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. -# Hacking already pins down pep8, pyflakes and flake8 -coverage>=3.6 # Apache-2.0 -fixtures<2.0,>=1.3.1 # Apache-2.0/BSD -mock>=1.2 # BSD -openstackdocstheme>=1.11.0 # Apache-2.0 -reno>=1.6.2 # Apache2 -python-subunit>=0.0.18 # Apache-2.0/BSD -sphinx>=1.6.2 # BSD -tempest>=11.0.0 # Apache-2.0 -testrepository>=0.0.18 # Apache-2.0/BSD -testtools>=1.4.0 # MIT diff --git a/tools/ceilometer.bash_completion b/tools/ceilometer.bash_completion deleted file mode 100644 index b2b48b2..0000000 --- a/tools/ceilometer.bash_completion +++ /dev/null @@ -1,27 +0,0 @@ -# bash completion for openstack ceilometer - -_ceilometer_opts="" # lazy init -_ceilometer_flags="" # lazy init -_ceilometer_opts_exp="" # lazy init -_ceilometer() -{ - local cur prev kbc - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - - if [ "x$_ceilometer_opts" == "x" ] ; then - kbc="`ceilometer bash-completion | sed -e "s/ -h / /"`" - _ceilometer_opts="`echo "$kbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" - _ceilometer_flags="`echo " $kbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" - _ceilometer_opts_exp="`echo $_ceilometer_opts | sed -e "s/[ ]/|/g"`" - fi - - if [[ " ${COMP_WORDS[@]} " =~ " "($_ceilometer_opts_exp)" " && "$prev" != "help" ]] ; then - COMPREPLY=($(compgen -W "${_ceilometer_flags}" -- ${cur})) - else - COMPREPLY=($(compgen -W "${_ceilometer_opts}" -- ${cur})) - fi - return 0 -} -complete -F _ceilometer ceilometer diff --git a/tox.ini b/tox.ini deleted file mode 100644 index dccf6b5..0000000 --- a/tox.ini +++ /dev/null @@ -1,45 +0,0 @@ -[tox] -envlist = py35,py27,pypy,pep8 -minversion = 1.6 -skipsdist = True - -[testenv] -usedevelop = True -install_command = pip install -U {opts} {packages} -setenv = VIRTUAL_ENV={envdir} -deps = -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt -# NOTE(tonyb): This project has chosen to *NOT* consume upper-constraints.txt -commands = - python setup.py testr --slowest --testr-args='{posargs}' - -[testenv:pep8] -deps = hacking<0.13,>=0.12 -commands = flake8 - -[testenv:cover] -commands = - python setup.py testr --coverage --testr-args='{posargs}' - coverage report - -[testenv:venv] -commands = {posargs} - -[testenv:functional] -setenv = OS_TEST_PATH=./ceilometerclient/tests/functional -passenv = OS_* - -[testenv:docs] -commands= - python setup.py build_sphinx - -[testenv:releasenotes] -commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html - -[flake8] -show-source = True -exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools - -[hacking] -import_exceptions = - ceilometerclient.i18n |