diff -Nru python-gnocchiclient-2.2.0/debian/changelog python-gnocchiclient-2.5.0/debian/changelog --- python-gnocchiclient-2.2.0/debian/changelog 2016-06-08 14:31:23.000000000 +0000 +++ python-gnocchiclient-2.5.0/debian/changelog 2016-09-02 15:49:40.000000000 +0000 @@ -1,8 +1,23 @@ -python-gnocchiclient (2.2.0-2~cloud0) xenial-newton; urgency=medium +python-gnocchiclient (2.5.0-0ubuntu1~cloud0) xenial-newton; urgency=medium * New update for the Ubuntu Cloud Archive. - -- Openstack Ubuntu Testing Bot Wed, 08 Jun 2016 14:31:23 +0000 + -- Openstack Ubuntu Testing Bot Fri, 02 Sep 2016 15:49:40 +0000 + +python-gnocchiclient (2.5.0-0ubuntu1) yakkety; urgency=medium + + [ Ondřej Nový ] + * Standards-Version is 3.9.8 now (no change) + * d/rules: Removed UPSTREAM_GIT with default value + + [ Corey Bryant ] + * New upstream release. + * d/gbp.conf: Update debian-branch for Newton. + * d/control: Align (Build-)Depends with upstream. + * d/rules: Enable py3 tests. + * d/p/use-default-spinx-theme.patch: Added because openstack theme not found. + + -- Corey Bryant Thu, 01 Sep 2016 09:44:19 -0400 python-gnocchiclient (2.2.0-2) unstable; urgency=medium diff -Nru python-gnocchiclient-2.2.0/debian/control python-gnocchiclient-2.5.0/debian/control --- python-gnocchiclient-2.2.0/debian/control 2016-04-04 13:44:21.000000000 +0000 +++ python-gnocchiclient-2.5.0/debian/control 2016-09-01 13:44:19.000000000 +0000 @@ -1,7 +1,8 @@ Source: python-gnocchiclient Section: python Priority: optional -Maintainer: PKG OpenStack +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: PKG OpenStack Uploaders: Thomas Goirand , Build-Depends: debhelper (>= 9), dh-python, @@ -13,44 +14,49 @@ python3-all, python3-pbr (>= 1.8), python3-setuptools, -Build-Depends-Indep: python-cliff (>= 1.14.0), +Build-Depends-Indep: python-cliff (>= 1.16.0), python-coverage, python-futurist, python-hacking (>= 0.10.0), python-keystoneauth1 (>= 1.0.0), python-keystonemiddleware, + python-openstackdocstheme, + python-osc-lib (>= 0.3.0), python-oslo.serialization (>= 1.4.0), python-oslo.utils (>= 2.0.0), python-oslosphinx (>= 2.5.0), python-oslotest (>= 1.10.0), python-six, - python-tempest-lib (>= 0.6.1), + python-tempest (>= 1:10), python-testscenarios, python-testtools (>= 1.4.0), - python3-cliff (>= 1.14.0), + python3-cliff (>= 1.16.0), python3-futurist, python3-keystoneauth1 (>= 1.0.0), python3-keystonemiddleware, + python3-openstackdocstheme, + python3-osc-lib (>= 0.3.0), python3-oslo.serialization (>= 1.4.0), python3-oslo.utils (>= 2.0.0), python3-oslotest (>= 1.10.0), python3-six, python3-subunit, - python3-tempest-lib (>= 0.6.1), + python3-tempest (>= 1:10), python3-testscenarios, python3-testtools (>= 1.4.0), subunit, testrepository, -Standards-Version: 3.9.7 +Standards-Version: 3.9.8 Vcs-Browser: https://anonscm.debian.org/cgit/openstack/python-gnocchiclient.git/ Vcs-Git: https://anonscm.debian.org/git/openstack/python-gnocchiclient.git Homepage: http://www.openstack.org/ Package: python-gnocchiclient Architecture: all -Depends: python-cliff (>= 1.14.0), +Depends: python-cliff (>= 1.16.0), python-futurist, python-keystoneauth1 (>= 1.0.0), + python-osc-lib (>= 0.3.0), python-oslo.serialization (>= 1.4.0), python-oslo.utils (>= 2.0.0), python-pbr (>= 1.8), @@ -71,9 +77,10 @@ Package: python3-gnocchiclient Architecture: all -Depends: python3-cliff (>= 1.14.0), +Depends: python3-cliff (>= 1.16.0), python3-futurist, python3-keystoneauth1 (>= 1.0.0), + python3-osc-lib (>= 0.3.0), python3-oslo.serialization (>= 1.4.0), python3-oslo.utils (>= 2.0.0), python3-pbr (>= 1.8), diff -Nru python-gnocchiclient-2.2.0/debian/gbp.conf python-gnocchiclient-2.5.0/debian/gbp.conf --- python-gnocchiclient-2.2.0/debian/gbp.conf 2016-04-04 13:44:21.000000000 +0000 +++ python-gnocchiclient-2.5.0/debian/gbp.conf 2016-09-01 13:44:19.000000000 +0000 @@ -1,6 +1,6 @@ [DEFAULT] upstream-branch = master -debian-branch = debian/mitaka +debian-branch = debian/newton upstream-tag = %(version)s compression = xz diff -Nru python-gnocchiclient-2.2.0/debian/patches/series python-gnocchiclient-2.5.0/debian/patches/series --- python-gnocchiclient-2.2.0/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ python-gnocchiclient-2.5.0/debian/patches/series 2016-09-01 13:44:19.000000000 +0000 @@ -0,0 +1 @@ +use-default-sphinx-theme.patch diff -Nru python-gnocchiclient-2.2.0/debian/patches/use-default-sphinx-theme.patch python-gnocchiclient-2.5.0/debian/patches/use-default-sphinx-theme.patch --- python-gnocchiclient-2.2.0/debian/patches/use-default-sphinx-theme.patch 1970-01-01 00:00:00.000000000 +0000 +++ python-gnocchiclient-2.5.0/debian/patches/use-default-sphinx-theme.patch 2016-09-01 13:44:19.000000000 +0000 @@ -0,0 +1,12 @@ +--- a/doc/source/conf.py ++++ b/doc/source/conf.py +@@ -61,7 +61,8 @@ + # html_theme_path = ["."] + # html_theme = '_theme' + # html_static_path = ['static'] +-html_theme = os.getenv("SPHINX_HTML_THEME", 'openstack') ++# html_theme = os.getenv("SPHINX_HTML_THEME", 'openstack') ++html_theme = 'default' + + if html_theme == "sphinx_rtd_theme": + import sphinx_rtd_theme diff -Nru python-gnocchiclient-2.2.0/debian/rules python-gnocchiclient-2.5.0/debian/rules --- python-gnocchiclient-2.2.0/debian/rules 2016-04-04 13:44:21.000000000 +0000 +++ python-gnocchiclient-2.5.0/debian/rules 2016-09-01 13:44:19.000000000 +0000 @@ -3,7 +3,6 @@ PYTHONS:=$(shell pyversions -vr) PYTHON3S:=$(shell py3versions -vr) -UPSTREAM_GIT = git://github.com/openstack/python-gnocchiclient.git include /usr/share/openstack-pkg-tools/pkgos.make export OSLO_PACKAGE_VERSION=$(shell dpkg-parsechangelog | grep Version: | cut -d' ' -f2 | sed -e 's/^[[:digit:]]*://' -e 's/[-].*//' -e 's/~/.0/' | head -n 1) @@ -26,7 +25,7 @@ override_dh_auto_test: ifeq (,$(findstring nocheck, $(DEB_BUILD_OPTIONS))) @echo "===> Running tests" - set -e ; set -x ; for i in 2.7 ; do \ + set -e ; set -x ; for i in 2.7 $(PYTHON3S) ; do \ PYMAJOR=`echo $$i | cut -d'.' -f1` ; \ echo "===> Testing with python$$i (python$$PYMAJOR)" ; \ rm -rf .testrepository ; \ diff -Nru python-gnocchiclient-2.2.0/doc/source/index.rst python-gnocchiclient-2.5.0/doc/source/index.rst --- python-gnocchiclient-2.2.0/doc/source/index.rst 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/doc/source/index.rst 2016-08-05 11:26:44.000000000 +0000 @@ -3,22 +3,22 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Python bindings to the Gnocchi API +Python bindings and command line tool to the Gnocchi API ======================================================== -This is a client for gnocchi API. There's :doc:`a Python API -` (the :mod:`gnocchiclient` module), and a :doc:`command-line script -` (installed as :program:`gnocchi`). Each implements the entire -Gnocchi API. +This is a client for `Gnocchi`_. There's :doc:`a Python API ` (the +:mod:`gnocchiclient` module), and a :doc:`command-line script ` +(installed as :program:`gnocchi`). Each implements the entire Gnocchi API. .. seealso:: - You may want to read the `Gnocchi Developer Guide`__ -- the overview, at - least -- to get an idea of the concepts. By understanding the concepts - this library should make more sense. + You may want to read the `Gnocchi documentation`__ to get an idea of the + concepts. By understanding the concepts this library and client should make + more sense. - __ http://docs.openstack.org/developer/gnocchi/ + __ http://gnocchi.xyz +.. _Gnocchi: http://gnocchi.xyz Contents: diff -Nru python-gnocchiclient-2.2.0/doc/source/shell.rst python-gnocchiclient-2.5.0/doc/source/shell.rst --- python-gnocchiclient-2.2.0/doc/source/shell.rst 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/doc/source/shell.rst 2016-08-05 11:26:44.000000000 +0000 @@ -4,17 +4,19 @@ .. program:: gnocchi .. highlight:: bash -The :program:`gnocchi` shell utility interacts with Gnocchi API -from the command line. It supports the entirety of the Gnocchi API. +The :program:`gnocchi` shell utility interacts with Gnocchi from the command +line. It supports the entirety of the Gnocchi API. + +You'll need to provide the authentication method and your credentials to +:program:`gnocchi`. -You'll need to provide :program:`gnocchi` 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: +: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. + Your username. .. envvar:: OS_PASSWORD @@ -26,19 +28,35 @@ .. envvar:: OS_AUTH_URL - The OpenStack auth server URL (keystone). + The OpenStack auth server URL (Keystone). + +No authentication +~~~~~~~~~~~~~~~~~ + +If you're using Gnocchi with no authentication, export the following variables +in your environment:: -For example, in Bash you would use:: + export OS_AUTH_TYPE=gnocchi-noauth + export GNOCCHI_USER_ID= + export GNOCCHI_PROJECT_ID= + export GNOCCHI_ENDPOINT=http://urlofgnocchi + +OpenStack Keystone authentication +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you're using Gnocchi with Keystone authentication, export the following +variables in your environment with the appropriate values:: 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:`--endpoint` and :option:`--os-auth-token`. You can alternatively -set these environment variables:: +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:`--endpoint` and +:option:`--os-auth-token`. You can alternatively set these environment +variables:: export GNOCCHI_ENDPOINT=http://gnocchi.example.org:8041 export OS_AUTH_PLUGIN=token @@ -54,7 +72,10 @@ export GNOCCHI_USER_ID=99aae-4dc2-4fbc-b5b8-9688c470d9cc export GNOCCHI_PROJECT_ID=c8d27445-48af-457c-8e0d-1de7103eae1f -From there, all shell commands take the form:: +Usage +===== + +Once authentication is set up, all shell commands take the form:: gnocchi [arguments...] diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/benchmark.py python-gnocchiclient-2.5.0/gnocchiclient/benchmark.py --- python-gnocchiclient-2.2.0/gnocchiclient/benchmark.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/benchmark.py 2016-08-05 11:26:44.000000000 +0000 @@ -77,9 +77,9 @@ def map_job(self, fn, iterable, **kwargs): self.sw = timeutils.StopWatch() - self.sw.start() r = [] self.times = 0 + self.sw.start() for item in iterable: r.append(self.submit(measure_job, fn, item, **kwargs)) self.times += 1 @@ -98,10 +98,10 @@ def wait_job(self, verb, futures): while self.statistics.executed != self.times: self._log_progress(verb) - time.sleep(1) + time.sleep(0.2) + runtime = self.sw.elapsed() self._log_progress(verb) self.shutdown(wait=True) - runtime = self.sw.elapsed() results = [] latencies = [] for f in futures: @@ -245,6 +245,10 @@ default=timeutils.utcnow(True), type=timeutils.parse_isotime, help="Last timestamp to use") + parser.add_argument("--wait", + default=False, + action='store_true', + help="Wait for all measures to be processed") return parser def take_action(self, parsed_args): @@ -292,6 +296,22 @@ ) ) + if parsed_args.wait: + with timeutils.StopWatch() as sw: + while True: + status = self.app.client.status.get() + remaining = int(status['storage']['summary']['measures']) + if remaining == 0: + stats['extra wait to process measures'] = ( + "%s seconds" % sw.elapsed() + ) + break + else: + LOG.info( + "Remaining measures to be processed: %d" + % remaining) + time.sleep(1) + return self.dict2columns(stats) diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/exceptions.py python-gnocchiclient-2.5.0/gnocchiclient/exceptions.py --- python-gnocchiclient-2.2.0/gnocchiclient/exceptions.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/exceptions.py 2016-08-05 11:26:44.000000000 +0000 @@ -18,7 +18,7 @@ """The base exception class for all exceptions this library raises.""" message = 'Unknown Error' - def __init__(self, code, message=None, request_id=None, + def __init__(self, code=None, message=None, request_id=None, url=None, method=None): self.code = code self.message = message or self.__class__.message @@ -27,10 +27,11 @@ self.method = method def __str__(self): - formatted_string = "%s (HTTP %s)" % (self.message, self.code) + formatted_string = "%s" % self.message + if self.code: + formatted_string += " (HTTP %s)" % self.code if self.request_id: formatted_string += " (Request-ID: %s)" % self.request_id - return formatted_string diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/osc.py python-gnocchiclient-2.5.0/gnocchiclient/osc.py --- python-gnocchiclient-2.2.0/gnocchiclient/osc.py 1970-01-01 00:00:00.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/osc.py 2016-08-05 11:26:44.000000000 +0000 @@ -0,0 +1,54 @@ +# Copyright 2014 OpenStack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from osc_lib import utils + + +DEFAULT_METRICS_API_VERSION = '1' +API_VERSION_OPTION = 'os_metrics_api_version' +API_NAME = "metric" +API_VERSIONS = { + "1": "gnocchiclient.v1.client.Client", +} + + +def make_client(instance): + """Returns a metrics service client.""" + version = instance._api_version[API_NAME] + try: + version = int(version) + except ValueError: + version = float(version) + + gnocchi_client = utils.get_client_class( + API_NAME, + version, + API_VERSIONS) + # NOTE(sileht): ensure setup of the session is done + instance.setup_auth() + return gnocchi_client(session=instance.session) + + +def build_option_parser(parser): + """Hook to add global options.""" + parser.add_argument( + '--os-metrics-api-version', + metavar='', + default=utils.env( + 'OS_METRICS_API_VERSION', + default=DEFAULT_METRICS_API_VERSION), + help=('Metrics API version, default=' + + DEFAULT_METRICS_API_VERSION + + ' (Env: OS_METRICS_API_VERSION)')) + return parser diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/shell.py python-gnocchiclient-2.5.0/gnocchiclient/shell.py --- python-gnocchiclient-2.2.0/gnocchiclient/shell.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/shell.py 2016-08-05 11:26:44.000000000 +0000 @@ -31,6 +31,7 @@ from gnocchiclient.v1 import capabilities_cli from gnocchiclient.v1 import metric_cli from gnocchiclient.v1 import resource_cli +from gnocchiclient.v1 import resource_type_cli from gnocchiclient.v1 import status_cli from gnocchiclient.version import __version__ @@ -45,10 +46,14 @@ "resource create": resource_cli.CliResourceCreate, "resource update": resource_cli.CliResourceUpdate, "resource delete": resource_cli.CliResourceDelete, - "resource list-types": resource_cli.CliResourceTypeList, + "resource-type list": resource_type_cli.CliResourceTypeList, + "resource-type create": resource_type_cli.CliResourceTypeCreate, + "resource-type show": resource_type_cli.CliResourceTypeShow, + "resource-type delete": resource_type_cli.CliResourceTypeDelete, "archive-policy list": archive_policy_cli.CliArchivePolicyList, "archive-policy show": archive_policy_cli.CliArchivePolicyShow, "archive-policy create": archive_policy_cli.CliArchivePolicyCreate, + "archive-policy update": archive_policy_cli.CliArchivePolicyUpdate, "archive-policy delete": archive_policy_cli.CliArchivePolicyDelete, "archive-policy-rule list": ap_rule_cli.CliArchivePolicyRuleList, "archive-policy-rule show": ap_rule_cli.CliArchivePolicyRuleShow, @@ -99,8 +104,10 @@ :param version: version number for the application :paramtype version: str """ - parser = super(GnocchiShell, self).build_option_parser(description, - version) + parser = super(GnocchiShell, self).build_option_parser( + description, + version, + argparse_kwargs={'allow_abbrev': False}) # Global arguments, one day this should go to keystoneauth1 parser.add_argument( '--os-region-name', diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/tests/functional/base.py python-gnocchiclient-2.5.0/gnocchiclient/tests/functional/base.py --- python-gnocchiclient-2.2.0/gnocchiclient/tests/functional/base.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/tests/functional/base.py 2016-08-05 11:26:44.000000000 +0000 @@ -17,8 +17,8 @@ import time import uuid -from tempest_lib.cli import base -from tempest_lib import exceptions +from tempest.lib.cli import base +from tempest.lib import exceptions class GnocchiClient(object): @@ -29,7 +29,7 @@ def __init__(self): self.cli_dir = os.environ.get('GNOCCHI_CLIENT_EXEC_DIR') - self.endpoint = os.environ.get('GNOCCHI_ENDPOINT') + self.endpoint = os.environ.get('PIFPAF_GNOCCHI_HTTP_URL') self.user_id = str(uuid.uuid4()) self.project_id = str(uuid.uuid4()) diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/tests/functional/test_archive_policy.py python-gnocchiclient-2.5.0/gnocchiclient/tests/functional/test_archive_policy.py --- python-gnocchiclient-2.2.0/gnocchiclient/tests/functional/test_archive_policy.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/tests/functional/test_archive_policy.py 2016-08-05 11:26:44.000000000 +0000 @@ -9,6 +9,7 @@ # 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 uuid from gnocchiclient.tests.functional import base @@ -16,55 +17,73 @@ class ArchivePolicyClientTest(base.ClientTestBase): def test_archive_policy_scenario(self): # CREATE + apname = str(uuid.uuid4()) result = self.gnocchi( - u'archive-policy', params=u"create low" + u'archive-policy', params=u"create %s" u" --back-window 0" - u" -d granularity:1s,points:86400") + u" -d granularity:1s,points:86400" % apname) policy = self.details_multiple(result)[0] - self.assertEqual('low', policy["name"]) + self.assertEqual(apname, policy["name"]) # CREATE FAIL result = self.gnocchi( - u'archive-policy', params=u"create low" + u'archive-policy', params=u"create %s" u" --back-window 0" - u" -d granularity:1s,points:86400", + u" -d granularity:1s,points:86400" % apname, fail_ok=True, merge_stderr=True) self.assertFirstLineStartsWith( result.split('\n'), - "Archive policy low already exists (HTTP 409)") + "Archive policy %s already exists (HTTP 409)" % apname) # GET result = self.gnocchi( - 'archive-policy', params="show low") + 'archive-policy', params="show %s" % apname) policy = self.details_multiple(result)[0] - self.assertEqual("low", policy["name"]) + self.assertEqual(apname, policy["name"]) # LIST result = self.gnocchi( 'archive-policy', params="list") policies = self.parser.listing(result) policy_from_list = [p for p in policies - if p['name'] == 'low'][0] + if p['name'] == apname][0] for field in ["back_window", "definition", "aggregation_methods"]: self.assertEqual(policy[field], policy_from_list[field]) + # UPDATE + result = self.gnocchi( + 'archive-policy', params='update %s' + ' -d granularity:1s,points:60' % apname) + policy = self.details_multiple(result)[0] + self.assertEqual(apname, policy["name"]) + + # UPDATE FAIL + result = self.gnocchi( + 'archive-policy', params='update %s' + ' -d granularity:5s,points:86400' % apname, + fail_ok=True, merge_stderr=True) + self.assertFirstLineStartsWith( + result.split('\n'), + "Archive policy %s does not support change: 1.0 granularity " + "interval was changed (HTTP 400)" % apname) + # DELETE result = self.gnocchi('archive-policy', - params="delete low") + params="delete %s" % apname) self.assertEqual("", result) # GET FAIL result = self.gnocchi('archive-policy', - params="show low", + params="show %s" % apname, fail_ok=True, merge_stderr=True) self.assertFirstLineStartsWith( result.split('\n'), - "Archive policy low does not exist (HTTP 404)") + "Archive policy %s does not exist (HTTP 404)" % apname) # DELETE FAIL result = self.gnocchi('archive-policy', - params="delete low", + params="delete %s" % apname, fail_ok=True, merge_stderr=True) self.assertFirstLineStartsWith( result.split('\n'), - "Archive policy low does not exist (HTTP 404)") + "Archive policy %s does not exist (HTTP 404)" % apname) diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/tests/functional/test_benchmark.py python-gnocchiclient-2.5.0/gnocchiclient/tests/functional/test_benchmark.py --- python-gnocchiclient-2.2.0/gnocchiclient/tests/functional/test_benchmark.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/tests/functional/test_benchmark.py 2016-08-05 11:26:44.000000000 +0000 @@ -82,6 +82,15 @@ self.assertEqual(2, int(result['push executed'])) self.assertLessEqual(int(result['push failures']), 2) + result = self.gnocchi( + u'benchmark', + params=u"measures add --wait -s 2010-01-01 -n 10 -b 4 %s" + % metric['id']) + result = self.details_multiple(result)[0] + self.assertEqual(2, int(result['push executed'])) + self.assertLessEqual(int(result['push failures']), 2) + self.assertIn("extra wait to process measures", result) + def test_benchmark_measures_show(self): apname = str(uuid.uuid4()) # PREPARE AN ARCHIVE POLICY diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/tests/functional/test_metric.py python-gnocchiclient-2.5.0/gnocchiclient/tests/functional/test_metric.py --- python-gnocchiclient-2.2.0/gnocchiclient/tests/functional/test_metric.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/tests/functional/test_metric.py 2016-08-05 11:26:44.000000000 +0000 @@ -56,7 +56,7 @@ self.gnocchi("archive-policy", params="create metric-test " "--back-window 0 -d granularity:1s,points:86400") - # CREATE WITH NAME + # CREATE WITH NAME AND WITHOUT UNIT result = self.gnocchi( u'metric', params=u"create" u" --archive-policy-name metric-test some-name") @@ -66,19 +66,22 @@ metric["created_by_project_id"]) self.assertEqual(self.clients.user_id, metric["created_by_user_id"]) self.assertEqual('some-name', metric["name"]) + self.assertEqual('None', metric["unit"]) self.assertEqual('None', metric["resource/id"]) self.assertIn("metric-test", metric["archive_policy/name"]) - # CREATE WITHOUT NAME + # CREATE WITHOUT NAME AND WITH UNIT result = self.gnocchi( u'metric', params=u"create" - u" --archive-policy-name metric-test") + u" --archive-policy-name metric-test" + u" --unit some-unit") metric = self.details_multiple(result)[0] self.assertIsNotNone(metric["id"]) self.assertEqual(self.clients.project_id, metric["created_by_project_id"]) self.assertEqual(self.clients.user_id, metric["created_by_user_id"]) self.assertEqual('None', metric["name"]) + self.assertEqual('some-unit', metric["unit"]) self.assertEqual('None', metric["resource/id"]) self.assertIn("metric-test", metric["archive_policy/name"]) @@ -116,6 +119,7 @@ 'measures', params=("aggregation " "--metric %s " "--aggregation mean " + "--granularity 1 " "--start 2015-03-06T14:32:00 " "--stop 2015-03-06T14:36:00" ) % metric["id"]) @@ -136,7 +140,7 @@ with tmpfile as f: f.write(measures.encode('utf8')) self.gnocchi('measures', params=("batch-metrics %s" % tmpfile.name)) - self.gnocchi('measures', params=("batch-metrics -"), + self.gnocchi('measures', params="batch-metrics -", input=measures.encode('utf8')) # LIST @@ -176,13 +180,15 @@ # CREATE result = self.gnocchi( u'metric', params=u"create" - u" --archive-policy-name metric-test2 -r metric-res metric-name") + u" --archive-policy-name metric-test2 -r metric-res metric-name" + u" --unit some-unit") metric = self.details_multiple(result)[0] self.assertIsNotNone(metric["id"]) self.assertEqual(self.clients.project_id, metric["created_by_project_id"]) self.assertEqual(self.clients.user_id, metric["created_by_user_id"]) self.assertEqual('metric-name', metric["name"]) + self.assertEqual('some-unit', metric["unit"]) self.assertNotEqual('None', metric["resource/id"]) self.assertIn("metric-test", metric["archive_policy/name"]) @@ -241,6 +247,28 @@ 'timestamp': '2015-03-06T14:34:12+00:00', 'value': '12.0'}], measures) + # MEASURES AGGREGATION GROUPBY + result = self.gnocchi( + 'measures', params=("aggregation " + "--groupby project_id " + "--groupby user_id " + "--query \"id='metric-res'\" " + "--resource-type \"generic\" " + "-m metric-name " + "--aggregation mean " + "--needed-overlap 0 " + "--start 2015-03-06T14:32:00 " + "--stop 2015-03-06T14:36:00")) + measures = self.parser.listing(result) + self.assertEqual([{'group': 'project_id: None, user_id: None', + 'granularity': '1.0', + 'timestamp': '2015-03-06T14:33:57+00:00', + 'value': '43.11'}, + {'group': 'project_id: None, user_id: None', + 'granularity': '1.0', + 'timestamp': '2015-03-06T14:34:12+00:00', + 'value': '12.0'}], measures) + # BATCHING measures = json.dumps({'metric-res': {'metric-name': [{ 'timestamp': '2015-03-06T14:34:12', 'value': 12 @@ -252,7 +280,7 @@ self.gnocchi('measures', params=("batch-resources-metrics %s" % tmpfile.name)) - self.gnocchi('measures', params=("batch-resources-metrics -"), + self.gnocchi('measures', params="batch-resources-metrics -", input=measures.encode('utf8')) # LIST diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/tests/functional/test_resource.py python-gnocchiclient-2.5.0/gnocchiclient/tests/functional/test_resource.py --- python-gnocchiclient-2.2.0/gnocchiclient/tests/functional/test_resource.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/tests/functional/test_resource.py 2016-08-05 11:26:44.000000000 +0000 @@ -71,7 +71,7 @@ self.assertEqual(self.RESOURCE_ID, resource_got["id"]) self.assertEqual(self.PROJECT_ID, resource_got["project_id"]) self.assertEqual(resource["started_at"], resource_got["started_at"]) - self.assertIn("temperature", resource_updated["metrics"]) + self.assertIn("temperature", resource_got["metrics"]) # HISTORY result = self.gnocchi( @@ -111,6 +111,13 @@ resource_updated = self.details_multiple(result)[0] self.assertNotIn("temperature", resource_updated["metrics"]) + result = self.gnocchi( + 'resource', params=("update %s -d temperature" % self.RESOURCE_ID), + fail_ok=True, merge_stderr=True) + self.assertFirstLineStartsWith( + result.split('\n'), + "Metric name temperature not found") + # CREATE 2 result = self.gnocchi( 'resource', params=("create %s -t generic " @@ -167,14 +174,3 @@ resource_ids = [r['id'] for r in self.parser.listing(result)] self.assertNotIn(self.RESOURCE_ID, resource_ids) self.assertNotIn(self.RESOURCE_ID2, resource_ids) - - # LIST THE RESOUCES TYPES - resource_type = ('instance', 'generic', 'volume', - 'instance_disk', 'stack', 'identity') - - result = self.gnocchi( - 'resource', params="list-types") - result_list = self.parser.listing(result) - type_from_list = [t['resource_type'] for t in result_list] - for one_type in resource_type: - self.assertIn(one_type, type_from_list) diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/tests/functional/test_resource_type.py python-gnocchiclient-2.5.0/gnocchiclient/tests/functional/test_resource_type.py --- python-gnocchiclient-2.2.0/gnocchiclient/tests/functional/test_resource_type.py 1970-01-01 00:00:00.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/tests/functional/test_resource_type.py 2016-08-05 11:26:44.000000000 +0000 @@ -0,0 +1,69 @@ +# 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 uuid + +from gnocchiclient.tests.functional import base + + +class ResourceTypeClientTest(base.ClientTestBase): + RESOURCE_TYPE = str(uuid.uuid4()) + + def test_help(self): + self.gnocchi("help", params="resource list") + + def test_resource_type_scenario(self): + # LIST + result = self.gnocchi('resource-type', params="list") + r = self.parser.listing(result) + self.assertEqual([{'attributes': '', 'name': 'generic'}], r) + + # CREATE + result = self.gnocchi( + u'resource-type', + params=u"create -a foo:string:1:max_length=16 " + "-a bar:number:no:max=32 %s" % self.RESOURCE_TYPE) + resource = self.details_multiple(result)[0] + self.assertEqual(self.RESOURCE_TYPE, resource["name"]) + self.assertEqual( + "max_length=16, min_length=0, required=True, type=string", + resource["attributes/foo"]) + + # SHOW + result = self.gnocchi( + u'resource-type', params=u"show %s" % self.RESOURCE_TYPE) + resource = self.details_multiple(result)[0] + self.assertEqual(self.RESOURCE_TYPE, resource["name"]) + self.assertEqual( + "max_length=16, min_length=0, required=True, type=string", + resource["attributes/foo"]) + + # DELETE + result = self.gnocchi('resource-type', + params="delete %s" % self.RESOURCE_TYPE) + self.assertEqual("", result) + + # DELETE AGAIN + result = self.gnocchi('resource-type', + params="delete %s" % self.RESOURCE_TYPE, + fail_ok=True, merge_stderr=True) + self.assertFirstLineStartsWith( + result.split('\n'), + "Resource type %s does not exist (HTTP 404)" % self.RESOURCE_TYPE) + + # SHOW AGAIN + result = self.gnocchi(u'resource-type', + params=u"show %s" % self.RESOURCE_TYPE, + fail_ok=True, merge_stderr=True) + self.assertFirstLineStartsWith( + result.split('\n'), + "Resource type %s does not exist (HTTP 404)" % self.RESOURCE_TYPE) diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/tests/unit/test_utils.py python-gnocchiclient-2.5.0/gnocchiclient/tests/unit/test_utils.py --- python-gnocchiclient-2.2.0/gnocchiclient/tests/unit/test_utils.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/tests/unit/test_utils.py 2016-08-05 11:26:44.000000000 +0000 @@ -96,3 +96,18 @@ {"start": "2016-02-10T13:54:53+00:00", "stop": "2016-02-10T13:56:42+02:00"}), expected) + + self.assertEqual( + "groupby=foo&groupby=bar", + utils.dict_to_querystring({ + "groupby": ["foo", "bar"] + }), + ) + + self.assertEqual( + "groupby=foo&groupby=bar&overlap=0", + utils.dict_to_querystring({ + "groupby": ["foo", "bar"], + "overlap": 0, + }), + ) diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/utils.py python-gnocchiclient-2.5.0/gnocchiclient/utils.py --- python-gnocchiclient-2.2.0/gnocchiclient/utils.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/utils.py 2016-08-05 11:26:44.000000000 +0000 @@ -90,6 +90,20 @@ "Malformed Query: %s" % reason) +def add_query_argument(cmd, parser): + return parser.add_argument( + cmd, + help="A query to filter resource. " + "The syntax is a combination of attribute, operator and value. " + "For example: id=90d58eea-70d7-4294-a49a-170dcdf44c3c would filter " + "resource with a certain id. More complex queries can be built, " + "e.g.: not (flavor_id!=\"1\" and memory>=24). " + "Use \"\" to force data to be interpreted as string. " + "Supported operators are: not, and, ∧ or, ∨, >=, <=, !=, >, <, =, ==, " + "eq, ne, lt, gt, ge, le, in, like, ≠, ≥, ≤, like, in.", + type=search_query_builder) + + def search_query_builder(query): try: parsed_query = expr.parseString(query, parseAll=True)[0] @@ -103,15 +117,22 @@ for o in objs] -def format_string_list(objs, field): - objs[field] = ", ".join(objs[field]) +def format_string_list(l): + return ", ".join(l) -def format_dict_list(objs, field): - objs[field] = "\n".join( +def format_dict_list(l): + return "\n".join( "- " + ", ".join("%s: %s" % (k, v) for k, v in elem.items()) - for elem in objs[field]) + for elem in l) + + +def format_dict_dict(value): + return "\n".join( + "- %s: " % name + " , ".join("%s: %s" % (k, v) + for k, v in elem.items()) + for name, elem in value.items()) def format_move_dict_to_root(obj, field): @@ -120,9 +141,17 @@ del obj[field] +def format_resource_type(rt): + format_move_dict_to_root(rt, "attributes") + for key in rt: + if key.startswith("attributes"): + rt[key] = ", ".join( + "%s=%s" % (k, v) for k, v in sorted(rt[key].items())) + + def format_archive_policy(ap): - format_dict_list(ap, "definition") - format_string_list(ap, "aggregation_methods") + ap['definition'] = format_dict_list(ap['definition']) + ap['aggregation_methods'] = format_string_list(ap['aggregation_methods']) def format_resource_for_metric(metric): @@ -147,9 +176,16 @@ def dict_to_querystring(objs): - return "&".join(["%s=%s" % (k, urllib_parse.quote(six.text_type(v))) - for k, v in objs.items() - if v is not None]) + strings = [] + for k, values in sorted(objs.items()): + if values is not None: + if not isinstance(values, (list, tuple)): + values = [values] + strings.append("&".join( + ("%s=%s" % (k, v) + for v in map(urllib_parse.quote, + map(six.text_type, values))))) + return "&".join(strings) # uuid5 namespace for id transformation. @@ -171,3 +207,13 @@ 'transformable resource id >255 max allowed characters') except Exception as e: raise ValueError(e) + + +def get_client(obj): + if hasattr(obj.app, 'client_manager'): + # NOTE(sileht): cliff objects loaded by OSC + return obj.app.client_manager.metric + else: + # TODO(sileht): Remove this when OSC is able + # to install the gnocchi client binary itself + return obj.app.client diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/v1/archive_policy_cli.py python-gnocchiclient-2.5.0/gnocchiclient/v1/archive_policy_cli.py --- python-gnocchiclient-2.2.0/gnocchiclient/v1/archive_policy_cli.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/v1/archive_policy_cli.py 2016-08-05 11:26:44.000000000 +0000 @@ -25,9 +25,10 @@ 'back_window', 'definition', 'aggregation_methods') def take_action(self, parsed_args): - policies = self.app.client.archive_policy.list() - for ap in policies: - utils.format_archive_policy(ap) + policies = utils.get_client(self).archive_policy.list() + if parsed_args.formatter == 'table': + for ap in policies: + utils.format_archive_policy(ap) return utils.list2cols(self.COLS, policies) @@ -41,9 +42,10 @@ return parser def take_action(self, parsed_args): - ap = self.app.client.archive_policy.get( + ap = utils.get_client(self).archive_policy.get( name=parsed_args.name) - utils.format_archive_policy(ap) + if parsed_args.formatter == 'table': + utils.format_archive_policy(ap) return self.dict2columns(ap) @@ -61,34 +63,54 @@ return defs -class CliArchivePolicyCreate(show.ShowOne): +class CliArchivePolicyWriteBase(show.ShowOne): + def get_parser(self, prog_name): + parser = super(CliArchivePolicyWriteBase, self).get_parser(prog_name) + parser.add_argument("name", help="name of the archive policy") + parser.add_argument("-d", "--definition", action='append', + required=True, type=archive_policy_definition, + metavar="", + help=("two attributes (separated by ',') of an " + "archive policy definition with its name " + "and value separated with a ':'")) + return parser + + +class CliArchivePolicyCreate(CliArchivePolicyWriteBase): """Create an archive policy""" def get_parser(self, prog_name): parser = super(CliArchivePolicyCreate, self).get_parser(prog_name) - parser.add_argument("name", help=("name of the archive policy")) parser.add_argument("-b", "--back-window", dest="back_window", type=int, - help=("back window of the archive policy")) + help="back window of the archive policy") parser.add_argument("-m", "--aggregation-method", action="append", dest="aggregation_methods", - help=("aggregation method of the archive policy")) - parser.add_argument("-d", "--definition", action='append', - required=True, type=archive_policy_definition, - metavar="", - help=("two attributes (separated by ',') of an " - "archive policy definition with its name " - "and value separated with a ':'")) + help="aggregation method of the archive policy") return parser def take_action(self, parsed_args): archive_policy = utils.dict_from_parsed_args( parsed_args, ['name', 'back_window', 'aggregation_methods', 'definition']) - ap = self.app.client.archive_policy.create( + ap = utils.get_client(self).archive_policy.create( archive_policy=archive_policy) - utils.format_archive_policy(ap) + if parsed_args.formatter == 'table': + utils.format_archive_policy(ap) + return self.dict2columns(ap) + + +class CliArchivePolicyUpdate(CliArchivePolicyWriteBase): + """Update an archive policy""" + + def take_action(self, parsed_args): + archive_policy = utils.dict_from_parsed_args( + parsed_args, ['definition']) + ap = self.app.client.archive_policy.update( + name=parsed_args.name, archive_policy=archive_policy) + if parsed_args.formatter == 'table': + utils.format_archive_policy(ap) return self.dict2columns(ap) @@ -102,4 +124,4 @@ return parser def take_action(self, parsed_args): - self.app.client.archive_policy.delete(name=parsed_args.name) + utils.get_client(self).archive_policy.delete(name=parsed_args.name) diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/v1/archive_policy.py python-gnocchiclient-2.5.0/gnocchiclient/v1/archive_policy.py --- python-gnocchiclient-2.2.0/gnocchiclient/v1/archive_policy.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/v1/archive_policy.py 2016-08-05 11:26:44.000000000 +0000 @@ -45,6 +45,20 @@ self.url, headers={'Content-Type': "application/json"}, data=jsonutils.dumps(archive_policy)).json() + def update(self, name, archive_policy): + """Update an archive policy + + :param name: the name of archive policy + :type name: str + :param archive_policy: the archive policy + :type archive_policy: dict + + """ + return self._patch( + self.url + '/' + name, + headers={'Content-Type': "application/json"}, + data=jsonutils.dumps(archive_policy)).json() + def delete(self, name): """Delete an archive policy diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/v1/archive_policy_rule_cli.py python-gnocchiclient-2.5.0/gnocchiclient/v1/archive_policy_rule_cli.py --- python-gnocchiclient-2.2.0/gnocchiclient/v1/archive_policy_rule_cli.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/v1/archive_policy_rule_cli.py 2016-08-05 11:26:44.000000000 +0000 @@ -24,7 +24,7 @@ COLS = ('name', 'archive_policy_name', 'metric_pattern') def take_action(self, parsed_args): - ap_rules = self.app.client.archive_policy_rule.list() + ap_rules = utils.get_client(self).archive_policy_rule.list() return utils.list2cols(self.COLS, ap_rules) @@ -38,7 +38,7 @@ return parser def take_action(self, parsed_args): - ap_rule = self.app.client.archive_policy_rule.get( + ap_rule = utils.get_client(self).archive_policy_rule.get( name=parsed_args.name) return self.dict2columns(ap_rule) @@ -49,20 +49,20 @@ def get_parser(self, prog_name): parser = super(CliArchivePolicyRuleCreate, self).get_parser(prog_name) parser.add_argument("name", - help=("Rule name")) + help="Rule name") parser.add_argument("-a", "--archive-policy-name", dest="archive_policy_name", required=True, - help=("Archive policy name")) + help="Archive policy name") parser.add_argument("-m", "--metric-pattern", dest="metric_pattern", required=True, - help=("Wildcard of metric name to match")) + help="Wildcard of metric name to match") return parser def take_action(self, parsed_args): rule = utils.dict_from_parsed_args( parsed_args, ["name", "metric_pattern", "archive_policy_name"]) - policy = self.app.client.archive_policy_rule.create(rule) + policy = utils.get_client(self).archive_policy_rule.create(rule) return self.dict2columns(policy) @@ -76,4 +76,4 @@ return parser def take_action(self, parsed_args): - self.app.client.archive_policy_rule.delete(parsed_args.name) + utils.get_client(self).archive_policy_rule.delete(parsed_args.name) diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/v1/capabilities_cli.py python-gnocchiclient-2.5.0/gnocchiclient/v1/capabilities_cli.py --- python-gnocchiclient-2.2.0/gnocchiclient/v1/capabilities_cli.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/v1/capabilities_cli.py 2016-08-05 11:26:44.000000000 +0000 @@ -13,10 +13,12 @@ from cliff import show +from gnocchiclient import utils + class CliCapabilitiesList(show.ShowOne): """List capabilities""" def take_action(self, parsed_args): - caps = self.app.client.capabilities.list() + caps = utils.get_client(self).capabilities.list() return self.dict2columns(caps) diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/v1/client.py python-gnocchiclient-2.5.0/gnocchiclient/v1/client.py --- python-gnocchiclient-2.2.0/gnocchiclient/v1/client.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/v1/client.py 2016-08-05 11:26:44.000000000 +0000 @@ -19,6 +19,7 @@ from gnocchiclient.v1 import capabilities from gnocchiclient.v1 import metric from gnocchiclient.v1 import resource +from gnocchiclient.v1 import resource_type from gnocchiclient.v1 import status @@ -34,6 +35,7 @@ self.api = client.SessionClient(session, service_type=service_type, **kwargs) self.resource = resource.ResourceManager(self) + self.resource_type = resource_type.ResourceTypeManager(self) self.archive_policy = archive_policy.ArchivePolicyManager(self) self.archive_policy_rule = ( archive_policy_rule.ArchivePolicyRuleManager(self)) diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/v1/metric_cli.py python-gnocchiclient-2.5.0/gnocchiclient/v1/metric_cli.py --- python-gnocchiclient-2.2.0/gnocchiclient/v1/metric_cli.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/v1/metric_cli.py 2016-08-05 11:26:44.000000000 +0000 @@ -32,10 +32,10 @@ class CliMetricList(lister.Lister): """List metrics""" - COLS = ('id', 'archive_policy/name', 'name', 'resource_id') + COLS = ('id', 'archive_policy/name', 'name', 'unit', 'resource_id') def take_action(self, parsed_args): - metrics = self.app.client.metric.list() + metrics = utils.get_client(self).metric.list() for metric in metrics: utils.format_archive_policy(metric["archive_policy"]) utils.format_move_dict_to_root(metric, "archive_policy") @@ -52,7 +52,7 @@ return parser def take_action(self, parsed_args): - metric = self.app.client.metric.get( + metric = utils.get_client(self).metric.get( metric=parsed_args.metric, resource_id=parsed_args.resource_id) utils.format_archive_policy(metric["archive_policy"]) @@ -66,7 +66,7 @@ parser = super(CliMetricCreateBase, self).get_parser(prog_name) parser.add_argument("--archive-policy-name", "-a", dest="archive_policy_name", - help=("name of the archive policy")) + help="name of the archive policy") return parser def take_action(self, parsed_args): @@ -84,12 +84,16 @@ parser.add_argument("name", nargs='?', metavar="METRIC_NAME", help="Name of the metric") + parser.add_argument("--unit", "-u", + help="unit of the metric") return parser def _take_action(self, metric, parsed_args): if parsed_args.name: metric['name'] = parsed_args.name - metric = self.app.client.metric.create(metric) + if parsed_args.unit: + metric['unit'] = parsed_args.unit + metric = utils.get_client(self).metric.create(metric) utils.format_archive_policy(metric["archive_policy"]) utils.format_move_dict_to_root(metric, "archive_policy") utils.format_resource_for_metric(metric) @@ -107,8 +111,8 @@ def take_action(self, parsed_args): for metric in parsed_args.metric: - self.app.client.metric.delete(metric=metric, - resource_id=parsed_args.resource_id) + utils.get_client(self).metric.delete( + metric=metric, resource_id=parsed_args.resource_id) class CliMeasuresShow(CliMetricWithResourceID, lister.Lister): @@ -131,7 +135,7 @@ return parser def take_action(self, parsed_args): - measures = self.app.client.metric.get_measures( + measures = utils.get_client(self).metric.get_measures( metric=parsed_args.metric, resource_id=parsed_args.resource_id, aggregation=parsed_args.aggregation, @@ -165,7 +169,7 @@ return parser def take_action(self, parsed_args): - self.app.client.metric.add_measures( + utils.get_client(self).metric.add_measures( metric=parsed_args.metric, resource_id=parsed_args.resource_id, measures=parsed_args.measure, @@ -191,13 +195,13 @@ class CliMetricsMeasuresBatch(CliMeasuresBatch): def take_action(self, parsed_args): with parsed_args.file as f: - self.app.client.metric.batch_metrics_measures(json.load(f)) + utils.get_client(self).metric.batch_metrics_measures(json.load(f)) class CliResourcesMetricsMeasuresBatch(CliMeasuresBatch): def take_action(self, parsed_args): with parsed_args.file as f: - self.app.client.metric.batch_resources_metrics_measures( + utils.get_client(self).metric.batch_resources_metrics_measures( json.load(f)) @@ -216,29 +220,44 @@ help="beginning of the period") parser.add_argument("--stop", help="end of the period") + parser.add_argument("--granularity", + help="granularity to retrieve (in seconds)") parser.add_argument("--needed-overlap", type=float, help=("percent of datapoints in each " "metrics required")) - parser.add_argument("--query", help="Query"), + utils.add_query_argument("--query", parser) parser.add_argument("--resource-type", default="generic", help="Resource type to query"), + parser.add_argument("--groupby", + action='append', + help="Attribute to use to group resources"), return parser def take_action(self, parsed_args): metrics = parsed_args.metric - query = None if parsed_args.query: - query = utils.search_query_builder(parsed_args.query) if len(parsed_args.metric) != 1: - raise ValueError("One metric is required if query is provied") + raise ValueError("One metric is required if query is provided") metrics = parsed_args.metric[0] - measures = self.app.client.metric.aggregation( + measures = utils.get_client(self).metric.aggregation( metrics=metrics, - query=query, + query=parsed_args.query, aggregation=parsed_args.aggregation, start=parsed_args.start, stop=parsed_args.stop, + granularity=parsed_args.granularity, needed_overlap=parsed_args.needed_overlap, resource_type=parsed_args.resource_type, + groupby=parsed_args.groupby, ) + if parsed_args.groupby: + ms = [] + for g in measures: + group_name = ", ".join("%s: %s" % (k, g['group'][k]) + for k in sorted(g['group'])) + for m in g['measures']: + i = [group_name] + i.extend(m) + ms.append(i) + return ('group',) + self.COLS, ms return self.COLS, measures diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/v1/metric.py python-gnocchiclient-2.5.0/gnocchiclient/v1/metric.py --- python-gnocchiclient-2.2.0/gnocchiclient/v1/metric.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/v1/metric.py 2016-08-05 11:26:44.000000000 +0000 @@ -62,7 +62,7 @@ """Create an metric :param metric: The metric - :type metric: str + :type metric: dict """ resource_id = metric.get('resource_id') @@ -193,8 +193,9 @@ def aggregation(self, metrics, query=None, start=None, stop=None, aggregation=None, - needed_overlap=None, resource_type="generic"): - """Get measurements of a aggregated metrics + granularity=None, needed_overlap=None, + resource_type="generic", groupby=None): + """Get measurements of an aggregated metrics :param metrics: IDs of metric or metric name :type metric: list or str @@ -206,8 +207,14 @@ :type stop: timestamp :param aggregation: aggregation to retrieve :type aggregation: str + :param granularity: granularity to retrieve (in seconds) + :type granularity: int + :param needed_overlap: percent of datapoints in each metrics required + :type needed_overlap: float :param resource_type: type of resource for the query :type resource_type: str + :param groupby: list of attribute to group by + :type groupby: list See Gnocchi REST API documentation for the format of *query dictionary* @@ -220,7 +227,8 @@ stop = stop.isoformat() params = dict(start=start, stop=stop, aggregation=aggregation, - needed_overlap=needed_overlap) + granularity=granularity, needed_overlap=needed_overlap, + groupby=groupby) if query is None: for metric in metrics: self._ensure_metric_is_uuid(metric) diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/v1/resource_cli.py python-gnocchiclient-2.5.0/gnocchiclient/v1/resource_cli.py --- python-gnocchiclient-2.2.0/gnocchiclient/v1/resource_cli.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/v1/resource_cli.py 2016-08-05 11:26:44.000000000 +0000 @@ -14,6 +14,7 @@ from cliff import lister from cliff import show +from gnocchiclient import exceptions from gnocchiclient import utils @@ -47,7 +48,7 @@ return parser def take_action(self, parsed_args): - resources = self.app.client.resource.list( + resources = utils.get_client(self).resource.list( resource_type=parsed_args.resource_type, **self._get_pagination_options(parsed_args)) return utils.list2cols(self.COLS, resources) @@ -76,11 +77,14 @@ return parser def take_action(self, parsed_args): - resources = self.app.client.resource.history( + resources = utils.get_client(self).resource.history( resource_type=parsed_args.resource_type, resource_id=parsed_args.resource_id, **self._get_pagination_options(parsed_args)) - return utils.list2cols(self.COLS, resources) + cols = resources[0].keys() if resources else self.COLS + if parsed_args.formatter == 'table': + return utils.list2cols(cols, map(normalize_metrics, resources)) + return utils.list2cols(cols, resources) class CliResourceSearch(CliResourceList): @@ -88,13 +92,13 @@ def get_parser(self, prog_name): parser = super(CliResourceSearch, self).get_parser(prog_name) - parser.add_argument("query", help="Query") + utils.add_query_argument("query", parser) return parser def take_action(self, parsed_args): - resources = self.app.client.resource.search( + resources = utils.get_client(self).resource.search( resource_type=parsed_args.resource_type, - query=utils.search_query_builder(parsed_args.query), + query=parsed_args.query, **self._get_pagination_options(parsed_args)) return utils.list2cols(self.COLS, resources) @@ -103,6 +107,7 @@ res['metrics'] = "\n".join(sorted( ["%s: %s" % (name, _id) for name, _id in res['metrics'].items()])) + return res class CliResourceShow(show.ShowOne): @@ -117,10 +122,11 @@ return parser def take_action(self, parsed_args): - res = self.app.client.resource.get( + res = utils.get_client(self).resource.get( resource_type=parsed_args.resource_type, resource_id=parsed_args.resource_id) - normalize_metrics(res) + if parsed_args.formatter == 'table': + normalize_metrics(res) return self.dict2columns(res) @@ -135,7 +141,7 @@ help="ID of the resource") parser.add_argument("-a", "--attribute", action='append', default=[], - help=("name and value of a attribute " + help=("name and value of an attribute " "separated with a ':'")) parser.add_argument("-m", "--add-metric", action='append', default=[], @@ -157,11 +163,16 @@ or parsed_args.create_metric or (update and parsed_args.delete_metric)): if update: - r = self.app.client.resource.get(parsed_args.resource_type, - parsed_args.resource_id) + r = utils.get_client(self).resource.get( + parsed_args.resource_type, + parsed_args.resource_id) default = r['metrics'] for metric_name in parsed_args.delete_metric: - default.pop(metric_name, None) + try: + del default[metric_name] + except KeyError: + raise exceptions.MetricNotFound( + message="Metric name %s not found" % metric_name) else: default = {} resource['metrics'] = default @@ -179,9 +190,10 @@ def take_action(self, parsed_args): resource = self._resource_from_args(parsed_args) - res = self.app.client.resource.create( + res = utils.get_client(self).resource.create( resource_type=parsed_args.resource_type, resource=resource) - normalize_metrics(res) + if parsed_args.formatter == 'table': + normalize_metrics(res) return self.dict2columns(res) @@ -197,11 +209,12 @@ def take_action(self, parsed_args): resource = self._resource_from_args(parsed_args, update=True) - res = self.app.client.resource.update( + res = utils.get_client(self).resource.update( resource_type=parsed_args.resource_type, resource_id=parsed_args.resource_id, resource=resource) - normalize_metrics(res) + if parsed_args.formatter == 'table': + normalize_metrics(res) return self.dict2columns(res) @@ -215,15 +228,4 @@ return parser def take_action(self, parsed_args): - self.app.client.resource.delete(parsed_args.resource_id) - - -class CliResourceTypeList(lister.Lister): - """List the resource types that gnocchi supports""" - - COLS = ('resource_type', - 'resource_controller_url') - - def take_action(self, parsed_args): - resources = self.app.client.resource.list_types() - return self.COLS, list(resources.items()) + utils.get_client(self).resource.delete(parsed_args.resource_id) diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/v1/resource.py python-gnocchiclient-2.5.0/gnocchiclient/v1/resource.py --- python-gnocchiclient-2.2.0/gnocchiclient/v1/resource.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/v1/resource.py 2016-08-05 11:26:44.000000000 +0000 @@ -176,10 +176,4 @@ def list_types(self): """List the resource types supported by gnocchi""" - # (Note/jzl)Based on the discussion result, keep the keyword - # 'resource-type' and use the command 'resource list-types' to - # list the types supported by gnocchi. - # Opened a reminding bug to me to handle with it, - # when resource-type is ready - # https://bugs.launchpad.net/python-gnocchiclient/+bug/1535176 return self._get(self.url).json() diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/v1/resource_type_cli.py python-gnocchiclient-2.5.0/gnocchiclient/v1/resource_type_cli.py --- python-gnocchiclient-2.2.0/gnocchiclient/v1/resource_type_cli.py 1970-01-01 00:00:00.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/v1/resource_type_cli.py 2016-08-05 11:26:44.000000000 +0000 @@ -0,0 +1,107 @@ +# +# 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 cliff import command +from cliff import lister +from cliff import show +from oslo_utils import strutils + +from gnocchiclient import utils + + +class CliResourceTypeList(lister.Lister): + """List resource types""" + + COLS = ('name', 'attributes') + + def take_action(self, parsed_args): + resource_types = utils.get_client(self).resource_type.list() + for resource_type in resource_types: + resource_type['attributes'] = utils.format_dict_dict( + resource_type['attributes']) + return utils.list2cols(self.COLS, resource_types) + + +class CliResourceTypeCreate(show.ShowOne): + """Create a resource type""" + + def get_parser(self, prog_name): + parser = super(CliResourceTypeCreate, self).get_parser(prog_name) + parser.add_argument("name", help="name of the resource type") + parser.add_argument("-a", "--attribute", action='append', + type=self._resource_attribute, + default=[], + help=(u"attribute definition, " + u"attribute_name:" + u"attribute_type:" + u"attribute_is_required:" + u"attribute_type_option_name=" + u"attribute_type_option_value:\u2026 " + u"For example: " + u"display_name:string:true:max_length=255")) + return parser + + @classmethod + def _resource_attribute(cls, value): + config = value.split(":") + name = config.pop(0) + attrs = {} + if config: + attrs["type"] = config.pop(0) + if config: + attrs["required"] = strutils.bool_from_string(config.pop(0), + strict=True) + while config: + param, _, value = config.pop(0).partition("=") + try: + attrs[param] = int(value) + except ValueError: + try: + attrs[param] = float(value) + except ValueError: + attrs[param] = value + return (name, attrs) + + def take_action(self, parsed_args): + resource_type = {'name': parsed_args.name} + if parsed_args.attribute: + resource_type['attributes'] = dict(parsed_args.attribute) + res = utils.get_client(self).resource_type.create( + resource_type=resource_type) + utils.format_resource_type(res) + return self.dict2columns(res) + + +class CliResourceTypeShow(show.ShowOne): + """Show a resource type""" + + def get_parser(self, prog_name): + parser = super(CliResourceTypeShow, self).get_parser(prog_name) + parser.add_argument("name", help="name of the resource type") + return parser + + def take_action(self, parsed_args): + res = utils.get_client(self).resource_type.get(name=parsed_args.name) + utils.format_resource_type(res) + return self.dict2columns(res) + + +class CliResourceTypeDelete(command.Command): + """Delete a resource type""" + + def get_parser(self, prog_name): + parser = super(CliResourceTypeDelete, self).get_parser(prog_name) + parser.add_argument("name", help="name of the resource type") + return parser + + def take_action(self, parsed_args): + utils.get_client(self).resource_type.delete(parsed_args.name) diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/v1/resource_type.py python-gnocchiclient-2.5.0/gnocchiclient/v1/resource_type.py --- python-gnocchiclient-2.2.0/gnocchiclient/v1/resource_type.py 1970-01-01 00:00:00.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/v1/resource_type.py 2016-08-05 11:26:44.000000000 +0000 @@ -0,0 +1,52 @@ +# +# 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 oslo_serialization import jsonutils + +from gnocchiclient.v1 import base + + +class ResourceTypeManager(base.Manager): + url = "v1/resource_type/" + + def list(self): + """List resource types.""" + return self._get(self.url).json() + + def create(self, resource_type): + """Create a resource type + + :param resource_type: resource type + :type resource_type: dict + """ + return self._post( + self.url, + headers={'Content-Type': "application/json"}, + data=jsonutils.dumps(resource_type)).json() + + def get(self, name): + """Get a resource type + + :param name: name of the resource type + :type name: str + """ + return self._get(self.url + name, + headers={'Content-Type': "application/json"}).json() + + def delete(self, name): + """Delete a resource type + + :param resource_type: Resource type + :type resource_type: dict + """ + self._delete(self.url + name) diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/v1/status_cli.py python-gnocchiclient-2.5.0/gnocchiclient/v1/status_cli.py --- python-gnocchiclient-2.2.0/gnocchiclient/v1/status_cli.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/v1/status_cli.py 2016-08-05 11:26:44.000000000 +0000 @@ -13,19 +13,18 @@ from cliff import show +from gnocchiclient import utils + class CliStatusShow(show.ShowOne): """Show the status of measurements processing""" def take_action(self, parsed_args): - status = self.app.client.status.get() - - nb_metric = len(status['storage']['measures_to_process']) - nb_measures = ( - sum(status['storage']['measures_to_process'].values()) - ) + status = utils.get_client(self).status.get() return self.dict2columns({ - "storage/total number of measures to process": nb_measures, - "storage/number of metric having measures to process": nb_metric, + "storage/total number of measures to process": + status['storage']['summary']['measures'], + "storage/number of metric having measures to process": + status['storage']['summary']['metrics'], }) diff -Nru python-gnocchiclient-2.2.0/gnocchiclient/v1/status.py python-gnocchiclient-2.5.0/gnocchiclient/v1/status.py --- python-gnocchiclient-2.2.0/gnocchiclient/v1/status.py 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/gnocchiclient/v1/status.py 2016-08-05 11:26:44.000000000 +0000 @@ -16,6 +16,6 @@ class StatusManager(base.Manager): url = "v1/status" - def get(self): + def get(self, details=False): """Get Gnocchi status.""" - return self._get(self.url).json() + return self._get(self.url + '?details=%s' % details).json() diff -Nru python-gnocchiclient-2.2.0/requirements.txt python-gnocchiclient-2.5.0/requirements.txt --- python-gnocchiclient-2.2.0/requirements.txt 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/requirements.txt 2016-08-05 11:26:44.000000000 +0000 @@ -3,7 +3,8 @@ # process, which may cause wedges in the gate later. pbr<2.0,>=1.4 -cliff>=1.14.0 # Apache-2.0 +cliff>1.16.0 # Apache-2.0 +osc-lib>=0.3.0 # Apache-2.0 oslo.serialization>=1.4.0 # Apache-2.0 oslo.utils>=2.0.0 # Apache-2.0 keystoneauth1>=1.0.0 diff -Nru python-gnocchiclient-2.2.0/setup.cfg python-gnocchiclient-2.5.0/setup.cfg --- python-gnocchiclient-2.2.0/setup.cfg 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/setup.cfg 2016-08-05 11:26:44.000000000 +0000 @@ -5,7 +5,7 @@ README.rst author = OpenStack author-email = openstack-dev@lists.openstack.org -home-page = http://www.openstack.org/ +home-page = http://docs.openstack.org/developer/python-gnocchiclient/ classifier = Environment :: OpenStack Intended Audience :: Information Technology @@ -17,6 +17,7 @@ Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.4 + Programming Language :: Python :: 3.5 [files] packages = @@ -29,12 +30,53 @@ keystoneauth1.plugin = gnocchi-noauth = gnocchiclient.noauth:GnocchiNoAuthLoader +openstack.cli.extension = + metric = gnocchiclient.osc + +openstack.metric.v1 = + # FIXME(sileht): don't duplicate entry with the one in shell.py + metric_status = gnocchiclient.v1.status_cli:CliStatusShow + metric_resource_list = gnocchiclient.v1.resource_cli:CliResourceList + metric_resource_show = gnocchiclient.v1.resource_cli:CliResourceShow + metric_resource_history = gnocchiclient.v1.resource_cli:CliResourceHistory + metric_resource_search = gnocchiclient.v1.resource_cli:CliResourceSearch + metric_resource_create = gnocchiclient.v1.resource_cli:CliResourceCreate + metric_resource_update = gnocchiclient.v1.resource_cli:CliResourceUpdate + metric_resource_delete = gnocchiclient.v1.resource_cli:CliResourceDelete + metric_resource-type_list = gnocchiclient.v1.resource_type_cli:CliResourceTypeList + metric_resource-type_create = gnocchiclient.v1.resource_type_cli:CliResourceTypeCreate + metric_resource-type_show = gnocchiclient.v1.resource_type_cli:CliResourceTypeShow + metric_resource-type_delete = gnocchiclient.v1.resource_type_cli:CliResourceTypeDelete + metric_archive-policy_list = gnocchiclient.v1.archive_policy_cli:CliArchivePolicyList + metric_archive-policy_show = gnocchiclient.v1.archive_policy_cli:CliArchivePolicyShow + metric_archive-policy_create = gnocchiclient.v1.archive_policy_cli:CliArchivePolicyCreate + metric_archive-policy_delete = gnocchiclient.v1.archive_policy_cli:CliArchivePolicyDelete + metric_archive-policy-rule_list = gnocchiclient.v1.archive_policy_rule_cli:CliArchivePolicyRuleList + metric_archive-policy-rule_show = gnocchiclient.v1.archive_policy_rule_cli:CliArchivePolicyRuleShow + metric_archive-policy-rule_create = gnocchiclient.v1.archive_policy_rule_cli:CliArchivePolicyRuleCreate + metric_archive-policy-rule_delete = gnocchiclient.v1.archive_policy_rule_cli:CliArchivePolicyRuleDelete + # FIXME(sileht): "openstack metric metric list" is not really sexy + metric_metric_list = gnocchiclient.v1.metric_cli:CliMetricList + metric_metric_show = gnocchiclient.v1.metric_cli:CliMetricShow + metric_metric_create = gnocchiclient.v1.metric_cli:CliMetricCreate + metric_metric_delete = gnocchiclient.v1.metric_cli:CliMetricDelete + metric_measures_show = gnocchiclient.v1.metric_cli:CliMeasuresShow + metric_measures_add = gnocchiclient.v1.metric_cli:CliMeasuresAdd + metric_measures_batch-metrics = gnocchiclient.v1.metric_cli:CliMetricsMeasuresBatch + metric_measures_batch-resources-metrics = gnocchiclient.v1.metric_cli:CliResourcesMetricsMeasuresBatch + metric_measures aggregation = gnocchiclient.v1.metric_cli:CliMeasuresAggregation + metric_capabilities list = gnocchiclient.v1.capabilities_cli:CliCapabilitiesList + metric_benchmark metric create = gnocchiclient.benchmark:CliBenchmarkMetricCreate + metric_benchmark metric show = gnocchiclient.benchmark:CliBenchmarkMetricShow + metric_benchmark measures add = gnocchiclient.benchmark:CliBenchmarkMeasuresAdd + metric_benchmark measures show = gnocchiclient.benchmark:CliBenchmarkMeasuresShow + [extras] test = coverage>=3.6 python-subunit>=0.0.18 oslotest>=1.10.0 # Apache-2.0 - tempest-lib>=0.6.1 + tempest>=10 testrepository>=0.0.18 testtools>=1.4.0 diff -Nru python-gnocchiclient-2.2.0/setup-tests.sh python-gnocchiclient-2.5.0/setup-tests.sh --- python-gnocchiclient-2.2.0/setup-tests.sh 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/setup-tests.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -#!/bin/bash -set -e -x - -wait_for_line () { - while read line - do - echo "$line" | grep -q "$1" && break - done < "$2" - # Read the fifo for ever otherwise process would block - cat "$2" & -} - -clean_exit () { - local error_code="$?" - kill $(jobs -p) - rm -rf "$@" - return $error_code -} - -GNOCCHI_DATA=`mktemp -d /tmp/gnocchi-data-XXXXX` -trap "clean_exit \"$GNOCCHI_DATA\"" EXIT - -source $(which overtest) mysql - -mkfifo ${GNOCCHI_DATA}/out -cat > ${GNOCCHI_DATA}/gnocchi.conf </dev/null & -gnocchi-api --config-file ${GNOCCHI_DATA}/gnocchi.conf &> ${GNOCCHI_DATA}/out & -# Wait for Gnocchi to start -wait_for_line "Running on http://0.0.0.0:8041/" ${GNOCCHI_DATA}/out -export GNOCCHI_ENDPOINT=http://localhost:8041/ - -$* diff -Nru python-gnocchiclient-2.2.0/tox.ini python-gnocchiclient-2.5.0/tox.ini --- python-gnocchiclient-2.2.0/tox.ini 2016-02-18 10:26:20.000000000 +0000 +++ python-gnocchiclient-2.5.0/tox.ini 2016-08-05 11:26:44.000000000 +0000 @@ -1,6 +1,6 @@ [tox] minversion = 1.6 -envlist = py34,py27,pypy,pep8 +envlist = py34,py35,py27,pypy,pep8 skipsdist = True [testenv] @@ -10,12 +10,12 @@ GNOCCHI_CLIENT_EXEC_DIR={envdir}/bin passenv = GNOCCHI_* OS_TEST_TIMEOUT OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_LOG_CAPTURE deps = .[test] - http://tarballs.openstack.org/gnocchi/gnocchi-master.tar.gz#egg=gnocchi[mysql,file] - overtest -commands = {toxinidir}/setup-tests.sh python setup.py test --slowest --testr-args='{posargs}' + http://tarballs.openstack.org/gnocchi/gnocchi-master.tar.gz#egg=gnocchi[postgresql,file] + pifpaf +commands = pifpaf run gnocchi -- python setup.py test --slowest --testr-args='{posargs}' [testenv:pep8] -deps = hacking<0.11,>=0.10.0 +deps = hacking<0.12,>=0.11.0 commands = flake8 [testenv:venv] @@ -38,9 +38,9 @@ commands = python setup.py build_sphinx [testenv:debug] -commands = {toxinidir}/setup-tests.sh oslo_debug_helper {posargs} +commands = pifpaf --debug run gnocchi -- oslo_debug_helper {posargs} [flake8] show-source = True ignore = -exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build +exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build