Compare commits

..

8 Commits

Author SHA1 Message Date
Zuul
c5d1526dec Merge "Add support for POST /v2/dataframes API endpoint to the client" into stable/train 2019-09-23 07:52:15 +00:00
Zuul
729ab402a5 Merge "Add functional test jobs for the v2 client" into stable/train 2019-09-23 07:51:04 +00:00
Zuul
3bcb6949b7 Merge "Add lower-constraints job" into stable/train 2019-09-23 07:51:03 +00:00
Justin Ferrieu
71fa216168 Add support for POST /v2/dataframes API endpoint to the client
Support for the ``/v2/dataframes`` endpoint has been added to the client.
A new ``dataframes add`` CLI command is also available.

Change-Id: I7fe9072d7280f251edc865a653a0b9ed2ab26c90
Story: 2005890
Task: 35970
(cherry picked from commit c8d7a9e1c5)
2019-09-20 14:34:45 -05:00
Luka Peschke
41fecfcd1e Add functional test jobs for the v2 client
This adds test jobs for the v2 client, in python2 and python3.

Work items:

* Remove the "functional" tox environment and introduce the "functional-v1"
  and "functional-v2" environments.

* Add zuul base jobs for python2 and python 3 testing. Two jobs inherit from
  each of these new jobs: one for the v1 client and one for the v2 client.

* Add "OS_ENDPOINT" to the list of environment variables forwarded to the
  functional test environments in order to ease local testing.

Change-Id: I54a43a1e844e92730afbf87316b9efe73a08d850
(cherry picked from commit dd4112acea)
2019-09-20 14:34:38 -05:00
pengyuesheng
5c31e34202 Add lower-constraints job
create a tox environment for running the unit tests against the lower
bounds of the dependencies.

Add openstack-tox-lower-constraints job to the zuul configuration.

See http://lists.openstack.org/pipermail/openstack-dev/2018-March/128352.html
for more details.

Change-Id: Iae676c4bbd00836cc6dce0f083f7aa308bbfc372
(cherry picked from commit 3010383f10)
2019-09-20 14:34:30 -05:00
22e9ffdb8b Update TOX/UPPER_CONSTRAINTS_FILE for stable/train
Update the URL to the upper-constraints file to point to the redirect
rule on releases.openstack.org so that anyone working on this branch
will switch to the correct upper-constraints list automatically when
the requirements repository branches.

Until the requirements repository has as stable/train branch, tests will
continue to use the upper-constraints list on master.

Change-Id: I2bc755e71ad06ebbb4aef23b25aa1cf310d0b897
2019-09-20 16:44:53 +00:00
e251e5c451 Update .gitreview for stable/train
Change-Id: Ia90ce3ac73c87953fc379d43c8005e24e57c5db5
2019-09-20 16:44:48 +00:00
57 changed files with 197 additions and 936 deletions

1
.gitignore vendored
View File

@@ -16,4 +16,3 @@ dist
AUTHORS
ChangeLog
releasenotes/build
.idea/

View File

@@ -2,3 +2,4 @@
host=review.opendev.org
port=29418
project=openstack/python-cloudkittyclient.git
defaultbranch=stable/train

View File

@@ -21,8 +21,6 @@
cloudkitty: https://opendev.org/openstack/cloudkitty
devstack_localrc:
CLOUDKITTY_FETCHER: keystone
DEVSTACK_GATE_USE_PYTHON3: "True"
USE_PYTHON3: True
devstack_services:
ck-api: true
horizon: false
@@ -41,23 +39,54 @@
vars:
tox_envlist: functional-v2
- job:
name: cloudkittyclient-devstack-functional-base-py3
parent: cloudkittyclient-devstack-functional-base
description: |
Job for cloudkittyclient functional tests, ran in python3.
vars:
devstack_localrc:
DEVSTACK_GATE_USE_PYTHON3: "True"
USE_PYTHON3: "True"
- job:
name: cloudkittyclient-devstack-functional-v1-client-py3
parent: cloudkittyclient-devstack-functional-base-py3
vars:
tox_envlist: functional-v1
- job:
name: cloudkittyclient-devstack-functional-v2-client-py3
parent: cloudkittyclient-devstack-functional-base-py3
vars:
tox_envlist: functional-v2
- project:
templates:
- openstack-lower-constraints-jobs
- check-requirements
- openstack-cover-jobs
- openstack-python3-jobs
- openstack-python-jobs
- openstack-python3-train-jobs
- openstackclient-plugin-jobs
- publish-openstack-docs-pti
- release-notes-jobs-python3
check:
jobs:
- cloudkittyclient-devstack-functional-v1-client:
voting: true
- cloudkittyclient-devstack-functional-v2-client:
voting: true
- cloudkittyclient-devstack-functional-v1-client-py3:
voting: true
- cloudkittyclient-devstack-functional-v2-client-py3:
voting: true
gate:
jobs:
- cloudkittyclient-devstack-functional-v1-client:
voting: true
- cloudkittyclient-devstack-functional-v2-client:
voting: true
- cloudkittyclient-devstack-functional-v1-client-py3:
voting: true
- cloudkittyclient-devstack-functional-v2-client-py3:
voting: true

View File

@@ -1,19 +1,16 @@
The source repository for this project can be found at:
https://opendev.org/openstack/python-cloudkittyclient
If you would like to contribute to the development of OpenStack,
you must follow the steps in this page:
Pull requests submitted through GitHub are not monitored.
https://docs.openstack.org/infra/manual/developers.html
To start contributing to OpenStack, follow the steps in the contribution guide
to set up and use Gerrit:
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/contributors/code-and-documentation/quick-start.html
https://docs.openstack.org/infra/manual/developers.html#development-workflow
Bugs should be filed on Storyboard:
Pull requests submitted through GitHub will be ignored.
https://storyboard.openstack.org/#!/project/895
Bugs should be filed on Storyboard, not GitHub:
For more specific information about contributing to this repository, see the
python-cloudkittyclient contributor guide:
https://docs.openstack.org/python-cloudkittyclient/latest/contributor/contributing.html
https://storyboard.openstack.org/#!/project/openstack/python-cloudkittyclient

2
babel.cfg Normal file
View File

@@ -0,0 +1,2 @@
[python: **.py]

View File

@@ -12,24 +12,13 @@
# 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 string import Formatter as StringFormatter
from urllib.parse import urlencode
from cloudkittyclient import utils
from six.moves.urllib.parse import urlencode
class HttpDecoratorMeta(type):
ignore = ('get_url', )
def __new__(cls, *args, **kwargs):
return utils.format_http_errors(HttpDecoratorMeta.ignore)(
super(HttpDecoratorMeta, cls).__new__(cls, *args, **kwargs)
)
class BaseManager(object, metaclass=HttpDecoratorMeta):
class BaseManager(object):
"""Base class for Endpoint Manager objects."""
url = ''

View File

@@ -94,7 +94,7 @@ class CloudKittyShell(cliff.app.App):
super(CloudKittyShell, self).__init__(
description='CloudKitty CLI client',
version=utils.get_version(),
command_manager=CommandManager('cloudkittyclient_v{}'.format(
command_manager=CommandManager('cloudkittyclient.v{}'.format(
self._get_api_version(args[:]),
)),
deferred_help=True,

View File

@@ -15,57 +15,26 @@
#
import json
import os
import shlex
import subprocess
from cloudkittyclient.tests import utils
from oslo_log import log
LOG = log.getLogger(__name__)
class BaseFunctionalTest(utils.BaseTestCase):
# DevStack is using VENV by default. Therefore, to execute the commands,
# we need to activate the VENV. And, to do that, we need the VENV path.
# This path is hardcoded here because we could not find a variable in this
# Python code to retrieve the VENV variable from the test machine.
# It seems that because of the stack TOX -> stestr -> this python code, and
# so on, we are not able to access the DevStack variables here.
#
# If somebody finds a solution, we can remove the hardcoded path here.
DEV_STACK_VENV_BASE_PATH = "/opt/stack/data/venv"
BASE_COMMAND_WITH_VENV = "source %s/bin/activate && %s "
def _run(self, executable, action,
flags='', params='', fmt='-f json', stdin=None, has_output=True):
if not has_output:
fmt = ''
does_venv_exist = not os.system("ls -lah /opt/stack/data/venv")
LOG.info("Test to check if the VENV file exist returned: [%s].",
does_venv_exist)
system_variables = os.environ.copy()
LOG.info("System variables [%s] found when executing the tests.",
system_variables)
cmd = ' '.join([executable, flags, action, params, fmt])
actual_command_with_venv = self.BASE_COMMAND_WITH_VENV % (
self.DEV_STACK_VENV_BASE_PATH, cmd)
LOG.info("Command being executed: [%s].", actual_command_with_venv)
cmd = shlex.split(cmd)
p = subprocess.Popen(
["bash", "-c", actual_command_with_venv],
env=os.environ.copy(), shell=False, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stdin=subprocess.PIPE if stdin else None
cmd, env=os.environ.copy(), shell=False,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
stdin=subprocess.PIPE if stdin else None,
)
stdout, stderr = p.communicate(input=stdin)
LOG.info("Standard output [%s] and error output [%s] for command "
"[%s]. ", stdout, stderr, actual_command_with_venv)
if p.returncode != 0:
raise RuntimeError('"{cmd}" returned {val}: {msg}'.format(
cmd=' '.join(cmd), val=p.returncode, msg=stderr))

View File

@@ -137,9 +137,9 @@ class CkDataframesTest(base.BaseFunctionalTest):
super(CkDataframesTest, self).tearDown()
def test_dataframes_add_with_no_args(self):
self.assertRaisesRegex(
self.assertRaisesRegexp(
RuntimeError,
'error: the following arguments are required: datafile',
'error: too few arguments',
self.runner,
'dataframes add',
fmt='',
@@ -162,11 +162,6 @@ class CkDataframesTest(base.BaseFunctionalTest):
has_output=False,
)
def test_dataframes_get(self):
# TODO(jferrieu): functional tests will be added in another
# patch for `dataframes get`
pass
class OSCDataframesTest(CkDataframesTest):
def __init__(self, *args, **kwargs):

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from unittest import mock
import mock
from cloudkittyclient import exc
from cloudkittyclient.tests.unit.v1 import base

View File

@@ -12,8 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from collections import abc
from unittest import mock
import collections
import mock
from cloudkittyclient.tests.unit.v1 import base
from cloudkittyclient.v1 import report_cli
@@ -57,7 +57,7 @@ class TestReportCli(base.BaseAPIEndpointTestCase):
assert len(result) == 2
assert result[0] == ('Tenant ID', )
assert isinstance(result[1], abc.Iterable)
assert isinstance(result[1], collections.Iterable)
for res in result[1]:
assert isinstance(res, abc.Iterable)
assert isinstance(res, collections.Iterable)

View File

@@ -12,10 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
from cloudkittyclient.tests import utils
from cloudkittyclient.v2 import dataframes
from cloudkittyclient.v2.rating import modules
from cloudkittyclient.v2 import scope
from cloudkittyclient.v2 import summary
@@ -28,4 +26,3 @@ class BaseAPIEndpointTestCase(utils.BaseTestCase):
self.dataframes = dataframes.DataframesManager(self.api_client)
self.scope = scope.ScopeManager(self.api_client)
self.summary = summary.SummaryManager(self.api_client)
self.rating = modules.RatingManager(self.api_client)

View File

@@ -14,8 +14,6 @@
#
import json
from collections import OrderedDict
from cloudkittyclient import exc
from cloudkittyclient.tests.unit.v2 import base
@@ -151,22 +149,3 @@ class TestDataframes(base.BaseAPIEndpointTestCase):
self.assertRaises(
exc.ArgumentRequired,
self.dataframes.add_dataframes)
def test_get_dataframes(self):
self.dataframes.get_dataframes()
self.api_client.get.assert_called_once_with('/v2/dataframes')
def test_get_dataframes_with_pagination_args(self):
self.dataframes.get_dataframes(offset=10, limit=10)
try:
self.api_client.get.assert_called_once_with(
'/v2/dataframes?limit=10&offset=10')
except AssertionError:
self.api_client.get.assert_called_once_with(
'/v2/dataframes?offset=10&limit=10')
def test_get_dataframes_filters(self):
self.dataframes.get_dataframes(
filters=OrderedDict([('one', 'two'), ('three', 'four')]))
self.api_client.get.assert_called_once_with(
'/v2/dataframes?filters=one%3Atwo%2Cthree%3Afour')

View File

@@ -1,38 +0,0 @@
# Copyright 2019 Objectif Libre
#
# 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 cloudkittyclient.tests.unit.v2 import base
class TestRating(base.BaseAPIEndpointTestCase):
def test_get_modules(self):
self.rating.get_module()
self.api_client.get.assert_called_once_with('/v2/rating/modules')
def test_get_one_module(self):
self.rating.get_module(module_id="moduleidtest")
self.api_client.get.assert_called_once_with(
'/v2/rating/modules/moduleidtest')
def test_update_one_module(self):
self.rating.update_module(module_id="moduleidtest",
enabled=False, priority=42)
self.api_client.put.assert_called_once_with(
'/v2/rating/modules/moduleidtest',
json={
'enabled': False,
'priority': 42,
})

View File

@@ -12,13 +12,12 @@
# 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 unittest import mock
import fixtures
import testtools
from keystoneauth1 import adapter
from keystoneauth1 import session
import mock
class BaseTestCase(testtools.TestCase):

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Objectif Libre
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -12,12 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
#
import inspect
import sys
import pbr.version
from keystoneauth1.exceptions import http
from oslo_utils import timeutils
@@ -59,44 +56,3 @@ def list_to_cols(list_obj, cols):
for item in list_obj:
values.append(dict_to_cols(item, cols))
return values
def http_error_formatter(func):
"""This decorator catches Http Errors and re-formats them"""
def wrap(*args, **kwargs):
try:
return func(*args, **kwargs)
except http.HttpError as e:
raise http.HttpError(message=e.response.text,
http_status=e.http_status)
return wrap
def format_http_errors(ignore):
"""Applies ``http_error_formatter`` to all methods of a class.
:param ignore: List of function names to ignore
:type ignore: iterable
"""
def wrap(cls):
# If you want pretty errors, use python3.
# __qualname__ does not exist in python 2
if sys.version_info.major < 3:
return cls
def predicate(item):
# This avoids decorating functions of parent classes
return (inspect.isfunction(item)
and item.__name__ not in ignore
and not item.__name__.startswith('_')
and cls.__name__ in item.__qualname__)
for name, func in inspect.getmembers(cls, predicate):
setattr(cls, name, http_error_formatter(func))
return cls
return wrap

View File

@@ -172,7 +172,7 @@ class HashmapManager(base.BaseManager):
:param value: Value of the mapping
:type value: str
"""
if kwargs.get('cost') is None:
if not kwargs.get('cost'):
raise exc.ArgumentRequired("'cost' argument is required")
if not kwargs.get('value'):
if not kwargs.get('service_id'):
@@ -373,7 +373,7 @@ class HashmapManager(base.BaseManager):
:type level: str
"""
for arg in ['cost', 'level']:
if kwargs.get(arg) is None:
if not kwargs.get(arg):
raise exc.ArgumentRequired(
"'{}' argument is required".format(arg))
if not kwargs.get('service_id') and not kwargs.get('field_id'):

View File

@@ -15,8 +15,6 @@
#
from cloudkittyclient.v1 import client
from cloudkittyclient.v2 import dataframes
from cloudkittyclient.v2.rating import modules
from cloudkittyclient.v2 import reprocessing
from cloudkittyclient.v2 import scope
from cloudkittyclient.v2 import summary
@@ -42,5 +40,3 @@ class Client(client.Client):
self.dataframes = dataframes.DataframesManager(self.api_client)
self.scope = scope.ScopeManager(self.api_client)
self.summary = summary.SummaryManager(self.api_client)
self.rating = modules.RatingManager(self.api_client)
self.reprocessing = reprocessing.ReprocessingManager(self.api_client)

View File

@@ -13,6 +13,7 @@
# under the License.
#
import json
import six
from cloudkittyclient.common import base
from cloudkittyclient import exc
@@ -35,7 +36,7 @@ class DataframesManager(base.BaseManager):
if not dataframes:
raise exc.ArgumentRequired("'dataframes' argument is required")
if not isinstance(dataframes, str):
if not isinstance(dataframes, six.string_types):
try:
dataframes = json.dumps(dataframes)
except TypeError:
@@ -48,30 +49,3 @@ class DataframesManager(base.BaseManager):
url,
data=dataframes,
)
def get_dataframes(self, **kwargs):
"""Returns a paginated list of DataFrames.
This support filters and datetime framing.
:param offset: Index of the first dataframe that should be returned.
:type offset: int
:param limit: Maximal number of dataframes to return.
:type limit: int
:param filters: Optional dict of filters to select data on.
:type filters: dict
:param begin: Start of the period to gather data from
:type begin: datetime.datetime
:param end: End of the period to gather data from
:type end: datetime.datetime
"""
kwargs['filters'] = ','.join(
'{}:{}'.format(k, v) for k, v in
(kwargs.get('filters', None) or {}).items()
)
authorized_args = [
'offset', 'limit', 'filters', 'begin', 'end']
url = self.get_url(None, kwargs, authorized_args=authorized_args)
return self.api_client.get(url).json()

View File

@@ -15,8 +15,6 @@
import argparse
from cliff import command
from cliff import lister
from oslo_utils import timeutils
from cloudkittyclient import utils
@@ -42,75 +40,3 @@ class CliDataframesAdd(command.Command):
utils.get_client_from_osc(self).dataframes.add_dataframes(
dataframes=dataframes,
)
class CliDataframesGet(lister.Lister):
"""Get dataframes from the storage backend."""
columns = [
('begin', 'Begin'),
('end', 'End'),
('metric', 'Metric Type'),
('unit', 'Unit'),
('qty', 'Quantity'),
('price', 'Price'),
('groupby', 'Group By'),
('metadata', 'Metadata'),
]
def get_parser(self, prog_name):
parser = super(CliDataframesGet, self).get_parser(prog_name)
def filter_(elem):
if len(elem.split(':')) != 2:
raise TypeError
return str(elem)
parser.add_argument('--offset', type=int, default=0,
help='Index of the first dataframe')
parser.add_argument('--limit', type=int, default=100,
help='Maximal number of dataframes')
parser.add_argument('--filter', type=filter_, action='append',
help="Optional filter, in 'key:value' format. Can "
"be specified several times.")
parser.add_argument('-b', '--begin', type=timeutils.parse_isotime,
help="Start of the period to query, in iso8601 "
"format. Example: 2019-05-01T00:00:00Z.")
parser.add_argument('-e', '--end', type=timeutils.parse_isotime,
help="End of the period to query, in iso8601 "
"format. Example: 2019-06-01T00:00:00Z.")
return parser
def take_action(self, parsed_args):
filters = dict(elem.split(':') for elem in (parsed_args.filter or []))
dataframes = utils.get_client_from_osc(self).dataframes.get_dataframes(
offset=parsed_args.offset,
limit=parsed_args.limit,
begin=parsed_args.begin,
end=parsed_args.end,
filters=filters,
).get('dataframes', [])
def format_(d):
return ' '.join([
'{}="{}"'.format(k, v) for k, v in (d or {}).items()])
values = []
for df in dataframes:
period = df['period']
usage = df['usage']
for metric_type, points in usage.items():
for point in points:
values.append([
period['begin'],
period['end'],
metric_type,
point['vol']['unit'],
point['vol']['qty'],
point['rating']['price'],
format_(point.get('groupby', {})),
format_(point.get('metadata', {})),
])
return [col[1] for col in self.columns], values

View File

@@ -1,59 +0,0 @@
# Copyright 2019 Objectif Libre
#
# 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 cloudkittyclient import exc
from cloudkittyclient.v1.client import rating
class RatingManager(rating.RatingManager):
"""Class used to handle /v2/rating/modules endpoint"""
url = '/v2/rating/modules'
def get_module(self, **kwargs):
"""Returns the given module.
If module_id is not specified, returns the list of loaded modules.
:param module_id: ID of the module on which you want information.
:type module_id: str
"""
module_id = kwargs.get('module_id', None)
if module_id is not None:
url = "{}/{}".format(self.url, module_id)
else:
url = self.url
return self.api_client.get(url).json()
def update_module(self, **kwargs):
"""Update the given module.
:param module_id: Id of the module to update.
:type module_id: str
:param enabled: Set to True to enable the module, False to disable it.
:type enabled: bool
:param priority: New priority of the module.
:type priority: int
"""
if not kwargs.get('module_id', None):
raise exc.ArgumentRequired("'module_id' argument is required.")
mutable_fields = ['enabled', 'priority']
changes = {}
for key, value in kwargs.items():
if value is not None and key in mutable_fields:
changes[key] = value
self.api_client.put("{}/{}".format(self.url, kwargs['module_id']),
json=changes)
return self.get_module(**kwargs)

View File

@@ -1,29 +0,0 @@
# Copyright 2019 Objectif Libre
#
# 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 lister
from cloudkittyclient import utils
class CliModuleList(lister.Lister):
"""Get loaded rating modules list"""
def get_parser(self, prog_name):
parser = super(CliModuleList, self).get_parser(prog_name)
return parser
def take_action(self, parsed_args):
resp = utils.get_client_from_osc(self).ratingmodules.get_modules_list()
return resp['modules']

View File

@@ -1,78 +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 cloudkittyclient.common import base
from cloudkittyclient import exc
class ReprocessingManager(base.BaseManager):
"""Class used to handle /v2/task/reprocesses endpoint"""
url = '/v2/task/reprocesses'
url_to_post = '/v2/task/reprocess'
def get_reprocessing_tasks(self, offset=0, limit=100, scope_ids=[],
order="DESC", **kwargs):
"""Returns a paginated list of reprocessing tasks.
Some optional filters can be provided.
:param offset: Index of the first reprocessing task
that should be returned.
:type offset: int
:param limit: Maximal number of reprocessing task to return.
:type limit: int
:param scope_ids: Optional scope_ids to filter on.
:type scope_ids: list of str
:param order: Optional order (asc/desc) to sort tasks.
:type order: str
"""
kwargs = kwargs or {}
kwargs['order'] = order
kwargs['offset'] = offset
kwargs['limit'] = limit
authorized_args = ['offset', 'limit', 'order']
url = self.get_url(None, kwargs, authorized_args=authorized_args)
if scope_ids:
url += "&scope_ids=%s" % (",".join(scope_ids))
return self.api_client.get(url).json()
def post_reprocessing_task(self, scope_ids=[], start=None, end=None,
reason=None, **kwargs):
"""Creates a reprocessing task
:param start: The start date of the reprocessing task
:type start: timeutils.parse_isotime
:param end: The end date of the reprocessing task
:type end: timeutils.parse_isotime
:param scope_ids: The scope IDs to create the reprocessing task to
:type scope_ids: list of str
:param reason: The reason for the reprocessing task
:type reason: str
"""
if not scope_ids:
raise exc.ArgumentRequired("'scope-id' argument is required")
body = dict(
scope_ids=scope_ids,
start_reprocess_time=start,
end_reprocess_time=end,
reason=reason
)
body = dict(filter(lambda elem: bool(elem[1]), body.items()))
return self.api_client.post(self.url_to_post, json=body).json()

View File

@@ -1,95 +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 cliff import lister
from oslo_utils import timeutils
from cloudkittyclient import utils
class CliReprocessingTasksGet(lister.Lister):
"""Get reprocessing tasks."""
result_columns = [
('scope_id', 'Scope ID'),
('reason', 'Reason'),
('start_reprocess_time', 'Start reprocessing time'),
('end_reprocess_time', 'End reprocessing time'),
('current_reprocess_time', 'Current reprocessing time'),
]
def get_parser(self, prog_name):
parser = super(CliReprocessingTasksGet, self).get_parser(prog_name)
parser.add_argument('--scope-id', type=str, default=[],
action='append', help='Optional filter on scope '
'IDs. This filter can be '
'used multiple times.')
parser.add_argument('--offset', type=int, default=0,
help='Index of the first scope. '
'The default value is 0.')
parser.add_argument('--limit', type=int, default=100,
help='Maximal number of scopes. '
'The default value is 100.')
parser.add_argument('--order', type=str, default="DESC",
help='The order to sort the reprocessing tasks '
'(ASC or DESC).')
return parser
def take_action(self, parsed_args):
resp = utils.get_client_from_osc(
self).reprocessing.get_reprocessing_tasks(
scope_ids=parsed_args.scope_id, offset=parsed_args.offset,
limit=parsed_args.limit, order=parsed_args.order
)
values = utils.list_to_cols(resp['results'], self.result_columns)
return [col[1] for col in self.result_columns], values
class CliReprocessingTasksPost(lister.Lister):
"""Create a reprocessing task."""
def get_parser(self, prog_name):
parser = super(CliReprocessingTasksPost, self).get_parser(prog_name)
parser.add_argument('--scope-id', type=str, default=[],
action='append',
help='The scope IDs to reprocess. This option can '
'be used multiple times to execute the same '
'reprocessing task for different scope IDs.')
parser.add_argument('--start-reprocess-time',
type=timeutils.parse_isotime,
help="Start of the period to reprocess in ISO8601 "
"format. Example: '2022-04-22T00:00:00Z.'")
parser.add_argument('--end-reprocess-time',
type=timeutils.parse_isotime,
help="End of the period to reprocess in ISO8601 "
"format. Example: '2022-04-22T00:00:00Z.'")
parser.add_argument('--reason', type=str,
help="The reason to create the reprocessing task.")
return parser
def take_action(self, parsed_args):
return ["Result"], utils.get_client_from_osc(
self).reprocessing.post_reprocessing_task(
scope_ids=parsed_args.scope_id,
start=parsed_args.start_reprocess_time,
end=parsed_args.end_reprocess_time,
reason=parsed_args.reason
)

View File

@@ -15,8 +15,6 @@
from cloudkittyclient.common import base
from cloudkittyclient import exc
from distutils.util import strtobool
class ScopeManager(base.BaseManager):
"""Class used to handle /v2/scope endpoint"""
@@ -102,41 +100,3 @@ class ScopeManager(base.BaseManager):
url = self.get_url(None, kwargs)
return self.api_client.put(url, json=body)
def update_scope(self, **kwargs):
"""Update storage scope
The `scope_id field` is mandatory, and all other are optional. Only the
attributes sent will be updated. The attributes that are not sent will
not be changed in the backend.
:param collector: collector to be used by the scope.
:type collector: str
:param fetcher: fetcher to be used by the scope.
:type fetcher: str
:param scope_id: Mandatory scope_id to update.
:type scope_id: str
:param scope_key: scope_key to be used by the scope.
:type scope_key: str
:param active: Indicates if the scope is active or not
:type active: str
"""
if not kwargs.get('scope_id'):
raise exc.ArgumentRequired("'scope_id' argument is required")
body = dict(
scope_id=kwargs.get('scope_id'),
scope_key=kwargs.get('scope_key'),
collector=kwargs.get('collector'),
fetcher=kwargs.get('fetcher')
)
if kwargs.get('active'):
body['active'] = strtobool(kwargs.get('active'))
# Stripping None
body = dict(filter(lambda elem: elem[1] is not None, body.items()))
url = self.get_url(None, kwargs)
return self.api_client.patch(url, json=body).json()

View File

@@ -96,36 +96,3 @@ class CliScopeStateReset(command.Command):
all_scopes=parsed_args.all_scopes,
state=parsed_args.state,
)
class CliPatchScope(command.Command):
"""Update scope attributes."""
info_columns = [
('scope_key', 'Scope Key'),
('collector', 'Collector'),
('fetcher', 'Fetcher'),
('active', 'Active'),
]
def get_parser(self, prog_name):
parser = super(CliPatchScope, self).get_parser(prog_name)
for col in self.info_columns:
parser.add_argument(
'--' + col[0].replace('_', '-'), type=str,
help='Optional filter on ' + col[1])
parser.add_argument(
'-id', '--scope-id', required=True, type=str,
help="The scope ID to be updated")
return parser
def take_action(self, parsed_args):
return utils.get_client_from_osc(self).scope.update_scope(
collector=parsed_args.collector,
fetcher=parsed_args.fetcher,
scope_id=parsed_args.scope_id,
scope_key=parsed_args.scope_key,
active=parsed_args.active)

View File

@@ -36,8 +36,6 @@ class SummaryManager(base.BaseManager):
:type begin: datetime.datetime
:param end: End of the period to gather data from
:type end: datetime.datetime
:param response_format: Either 'table' or 'object' defaults to 'table'
:type response_format: str
"""
if 'groupby' in kwargs.keys() and isinstance(kwargs['groupby'], list):
kwargs['groupby'] = ','.join(kwargs['groupby'])
@@ -48,8 +46,7 @@ class SummaryManager(base.BaseManager):
)
authorized_args = [
'offset', 'limit', 'filters', 'groupby', 'begin', 'end',
'response_format']
'offset', 'limit', 'filters', 'groupby', 'begin', 'end']
url = self.get_url(None, kwargs, authorized_args=authorized_args)
return self.api_client.get(url).json()

View File

@@ -3,7 +3,6 @@
# process, which may cause wedges in the gate later.
openstackdocstheme>=1.30.0 # Apache-2.0
sphinx>=1.8.0,!=2.1.0 # BSD
sphinxcontrib-svg2pdfconverter>=0.1.0 # BSD
sphinx>=1.6.2,!=1.6.6,!=1.6.7,<2.0.0;python_version=='2.7' # BSD
sphinx>=1.6.2,!=1.6.6,!=1.6.7,!=2.1.0;python_version>='3.4' # BSD
reno>=2.5.0 # Apache-2.0
cliff>=2.11.0 # Apache-2.0

View File

@@ -5,17 +5,18 @@ CLI Reference
V1 Client
=========
.. autoprogram-cliff:: cloudkittyclient_v1
.. autoprogram-cliff:: cloudkittyclient.v1
:application: cloudkitty
V2 Client
=========
.. autoprogram-cliff:: cloudkittyclient_v2
.. autoprogram-cliff:: cloudkittyclient.v2
:command: dataframes add
.. autoprogram-cliff:: cloudkittyclient_v2
.. autoprogram-cliff:: cloudkittyclient.v2
:command: scope state get
.. autoprogram-cliff:: cloudkittyclient_v2
.. autoprogram-cliff:: cloudkittyclient.v2
:command: summary get

View File

@@ -23,11 +23,8 @@ extensions = [
'cliff.sphinxext',
'sphinx.ext.autodoc',
'openstackdocstheme',
'sphinxcontrib.rsvgconverter',
]
autoprogram_cliff_application = 'cloudkitty'
autoprogram_cliff_ignored = [
"--format", "--column", "--max-width", "--fit-width", "--print-empty",
"--format-config-file", "--noindent", "--quote", "--sort-column",
@@ -44,8 +41,8 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = 'python-cloudkittyclient'
copyright = '2017, OpenStack Foundation'
project = u'python-cloudkittyclient'
copyright = u'2017, OpenStack Foundation'
# openstackdocstheme options
repository_name = 'openstack/python-cloudkittyclient'
@@ -60,7 +57,7 @@ add_function_parentheses = True
add_module_names = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'native'
pygments_style = 'sphinx'
# -- Options for HTML output --------------------------------------------------
@@ -77,30 +74,14 @@ html_last_updated_fmt = '%Y-%m-%d %H:%M'
# Output file base name for HTML help builder.
htmlhelp_basename = '%sdoc' % project
# -- Options for LaTeX output ---------------------------------------------
# Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664
latex_use_xindy = False
latex_domain_indices = False
latex_elements = {
'makeindex': '',
'printindex': '',
'preamble': r'\setcounter{tocdepth}{3}',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass
# [howto/manual]).
# NOTE: Specify toctree_only=True for a better document structure of
# the generated PDF file.
latex_documents = [
('index',
'doc-%s.tex' % project,
'%s Documentation' % project,
'OpenStack Foundation', 'howto', True),
'%s.tex' % project,
u'%s Documentation' % project,
u'OpenStack Foundation', 'manual'),
]
# Example configuration for intersphinx: refer to the Python standard library.

View File

@@ -0,0 +1,5 @@
============
Contributing
============
.. include:: ../../CONTRIBUTING.rst

View File

@@ -1,47 +0,0 @@
============================
So You Want to Contribute...
============================
For general information on contributing to OpenStack, please check out the
`contributor guide <https://docs.openstack.org/contributors/>`_ to get started.
It covers all the basics that are common to all OpenStack projects: the accounts
you need, the basics of interacting with our Gerrit review system, how we
communicate as a community, etc.
Below will cover the more project specific information you need to get started
with python-cloudkittyclient.
Communication
~~~~~~~~~~~~~
* IRC channel #cloudkitty at `OFTC <http://oftc.net>`_
* Mailing list (prefix subjects with ``[cloudkitty]`` for faster responses)
http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss
Contacting the Core Team
~~~~~~~~~~~~~~~~~~~~~~~~
Please refer the `python-cloudkittyclient Core Team
<https://review.opendev.org/admin/groups/4ac765c35f985b3ad9226da07fdcc205c1ce4fe1,members>`_ contacts.
New Feature Planning
~~~~~~~~~~~~~~~~~~~~
CloudKitty features are tracked on `Storyboard <https://storyboard.openstack.org/#!/project/895>`_.
Task Tracking
~~~~~~~~~~~~~
We track our tasks in `Storyboard <https://storyboard.openstack.org/#!/project/895>`_.
If you're looking for some smaller, easier work item to pick up and get started
on, search for the 'low-hanging-fruit' tag.
Reporting a Bug
~~~~~~~~~~~~~~~
You found an issue and want to make sure we are aware of it? You can do so on
`StoryBoard <https://storyboard.openstack.org/#!/project/895>`_.
Getting Your Patch Merged
~~~~~~~~~~~~~~~~~~~~~~~~~
All changes proposed to the python-cloudkittyclient project require one or two +2 votes
from python-cloudkittyclient core reviewers before one of the core reviewers can approve
patch by giving ``Workflow +1`` vote.
Project Team Lead Duties
~~~~~~~~~~~~~~~~~~~~~~~~
All common PTL duties are enumerated in the `PTL guide
<https://docs.openstack.org/project-team-guide/ptl.html>`_.

View File

@@ -12,19 +12,10 @@ Contents:
install
usage
contributor
cli_reference
api_reference/index
For Contributors
================
* If you are a new contributor to python-cloudkittyclient please refer: :doc:`contributor/contributing`
.. toctree::
:hidden:
contributor/contributing
Indices and tables
==================

24
lower-constraints.txt Normal file
View File

@@ -0,0 +1,24 @@
# requirements
pbr==2.0.0 # Apache-2.0
cliff==2.11.0 # Apache-2.0
keystoneauth1==3.4.0 # Apache-2.0
oslo.utils==3.35 # Apache-2.0
oslo.log==3.36 # Apache-2.0
PyYAML==3.12 # MIT
jsonpath-rw-ext==1.0 # Apache-2.0
six==1.11 # MIT
os-client-config==1.29.0 # Apache-2.0
osc-lib==1.12.1 # Apache-2.0
# test-requirements.txt
coverage==4.0 # Apache-2.0
python-subunit==0.0.18 # Apache-2.0/BSD
oslotest==1.10.0 # Apache-2.0
stestr==2.0 # Apache-2.0
mock==2.0 # BSD
python-openstackclient==3.14 # Apache-2.0
# doc/requirements.txt
openstackdocstheme==1.30.0 # Apache-2.0
sphinx==1.6.2 # BSD
reno==2.5.0 # Apache2

View File

@@ -1,6 +0,0 @@
---
features:
- |
Introduce the patch scope API in the CLI. The command "rating scope
patch" is added to the OpenStack CLI with this patch, and the command
"scope patch" is added to the CloudKitty python client.

View File

@@ -1,10 +0,0 @@
---
features:
- |
Introduce reprocessing task API in the CLI. The following new commands
are added to the OpenStack CLI "rating tasks reprocessing get" and
"rating tasks reprocessing create". For CloudKitty CLI, we added the
following new commands "tasks reprocessing get" and "tasks reprocessing
create". Both command sets work in a similar fashion, but one is
targetting the OpenStack CLI integration, whereas the other is
targetting CloudKitty client only.

View File

@@ -1,5 +0,0 @@
---
features:
- |
Support for the ``GET /v2/dataframes`` endpoint has been added
to the client. A new ``dataframes get`` CLI command is also available.

View File

@@ -1,7 +0,0 @@
---
upgrade:
- |
Python 2.7 support has been dropped. The last release of
``cloudkittyclient`` to support python 2.7 is OpenStack Train (3.1.0).
The minimum version of Python now supported by ``cloudkittyclient``
is Python 3.6.

View File

@@ -1,8 +0,0 @@
---
fixes:
- |
Fix `create_threshold` method when using cost as 0.
When using 0 as the cost, the `create_threshold` method
throws an exception. That happens because 0 (zero) is evaluated
to False. Therefore, we need to change the validation method to
check if the values are None.

View File

@@ -1,4 +0,0 @@
---
fixes:
- |
Fixes creation of hashmap mappings with a zero cost.

View File

@@ -1,6 +0,0 @@
===========================
2023.1 Series Release Notes
===========================
.. release-notes::
:branch: stable/2023.1

View File

@@ -1,6 +0,0 @@
===========================
2023.2 Series Release Notes
===========================
.. release-notes::
:branch: stable/2023.2

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# CloudKitty Client Release Notes documentation build configuration file.
# Cloudkitty Release Notes documentation build configuration file.
#
# This file is execfile()d with the current directory set to its
# containing dir.
@@ -42,8 +42,8 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = 'CloudKitty Client Release Notes'
copyright = '2016, CloudKitty developers'
project = u'Cloudkitty Client Release Notes'
copyright = u'2016, Cloudkitty developers'
# Release notes are version independent.
# The short X.Y version.
@@ -82,7 +82,7 @@ exclude_patterns = []
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'native'
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
@@ -173,7 +173,7 @@ html_static_path = ['_static']
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'CloudKittyClientReleaseNotestdoc'
htmlhelp_basename = 'CloudkittyReleaseNotestdoc'
# -- Options for LaTeX output ---------------------------------------------
@@ -193,9 +193,8 @@ latex_elements = {
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'PythonCloudKittyClient.tex',
'CloudKitty Client Release Notes Documentation',
'CloudKitty developers', 'manual'),
('index', 'PythonCloudkitty.tex', u'Cloudkitty Release Notes Documentation',
u'Cloudkitty developers', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -225,8 +224,8 @@ latex_documents = [
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'cloudkittyclient',
'CloudKitty Client Release Notes Documentation',
['CloudKitty developers'], 1)
u'Cloudkitty Client Release Notes Documentation',
[u'Cloudkitty developers'], 1)
]
# If true, show URL addresses after external links.
@@ -240,8 +239,8 @@ man_pages = [
# dir menu entry, description, category)
texinfo_documents = [
('index', 'cloudkittyclient',
'CloudKitty Client Release Notes Documentation',
'CloudKitty Client developers', 'CloudKittyClient',
u'Cloudkitty Client Release Notes Documentation',
u'Cloudkitty Client developers', 'Cloudkittyclient',
'One line description of project.', 'Miscellaneous'),
]

View File

@@ -1,4 +1,4 @@
Welcome to CloudKitty Client Release Notes documentation!
Welcome to Cloudkitty Client Release Notes documentation!
=========================================================
Contents
@@ -8,15 +8,6 @@ Contents
:maxdepth: 2
unreleased
2023.2
2023.1
zed
yoga
xena
wallaby
victoria
ussuri
train
stein
rocky
queens

View File

@@ -1,6 +0,0 @@
==========================
Train Series Release Notes
==========================
.. release-notes::
:branch: stable/train

View File

@@ -1,6 +0,0 @@
===========================
Ussuri Series Release Notes
===========================
.. release-notes::
:branch: stable/ussuri

View File

@@ -1,6 +0,0 @@
=============================
Victoria Series Release Notes
=============================
.. release-notes::
:branch: stable/victoria

View File

@@ -1,6 +0,0 @@
============================
Wallaby Series Release Notes
============================
.. release-notes::
:branch: stable/wallaby

View File

@@ -1,6 +0,0 @@
=========================
Xena Series Release Notes
=========================
.. release-notes::
:branch: stable/xena

View File

@@ -1,6 +0,0 @@
=========================
Yoga Series Release Notes
=========================
.. release-notes::
:branch: unmaintained/yoga

View File

@@ -1,6 +0,0 @@
========================
Zed Series Release Notes
========================
.. release-notes::
:branch: stable/zed

View File

@@ -1,17 +1,14 @@
# Requirements lower bounds listed here are our best effort to keep them up to
# date but we do not test them so no guarantee of having them all correct. If
# you find any incorrect lower bounds, let us know or propose a fix.
# 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>=5.5.1 # Apache-2.0
cliff>=3.5.0 # Apache-2.0
keystoneauth1>=4.3.0 # Apache-2.0
oslo.utils>=4.7.0 # Apache-2.0
oslo.log>=4.4.0 # Apache-2.0
PyYAML>=5.3.1 # MIT
jsonpath-rw-ext>=1.2.0 # Apache-2.0
os-client-config>=2.1.0 # Apache-2.0
osc-lib>=2.3.0 # Apache-2.0
pbr>=2.0.0,!=2.1.0 # Apache-2.0
cliff>=2.11.0 # Apache-2.0
keystoneauth1>=3.4.0 # Apache-2.0
oslo.utils>=3.35 # Apache-2.0
oslo.log>=3.36 # Apache-2.0
PyYAML>=3.12 # MIT
jsonpath-rw-ext>=1.0 # Apache-2.0
six>=1.11 # MIT
os-client-config>=1.29.0 # Apache-2.0
osc-lib>=1.12.1 # Apache-2.0

View File

@@ -1,12 +1,11 @@
[metadata]
name = python-cloudkittyclient
summary = API client of cloudkitty, Rating as a Service project.
description_file =
description-file =
README.rst
author = OpenStack
author_email = openstack-discuss@lists.openstack.org
home_page = https://docs.openstack.org/python-cloudkittyclient/latest/
python_requires = >=3.8
author-email = openstack-discuss@lists.openstack.org
home-page = https://docs.openstack.org/python-cloudkittyclient/latest/
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
@@ -14,13 +13,11 @@ classifier =
License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux
Programming Language :: Python
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
[files]
packages =
@@ -89,15 +86,10 @@ openstack.rating.v1 =
rating_pyscript_delete = cloudkittyclient.v1.rating.pyscripts_cli:CliDeleteScript
openstack.rating.v2 =
rating_tasks_reprocessing_get = cloudkittyclient.v2.reprocessing_cli:CliReprocessingTasksGet
rating_tasks_reprocessing_create = cloudkittyclient.v2.reprocessing_cli:CliReprocessingTasksPost
rating_dataframes_get = cloudkittyclient.v2.dataframes_cli:CliDataframesGet
rating_dataframes_add = cloudkittyclient.v2.dataframes_cli:CliDataframesAdd
rating_scope_state_get = cloudkittyclient.v2.scope_cli:CliScopeStateGet
rating_scope_state_reset = cloudkittyclient.v2.scope_cli:CliScopeStateReset
rating_scope_patch = cloudkittyclient.v2.scope_cli:CliPatchScope
rating_summary_get = cloudkittyclient.v2.summary_cli:CliSummaryGet
@@ -145,6 +137,7 @@ openstack.rating.v2 =
rating_collector_state_get = cloudkittyclient.v1.collector_cli:CliCollectorGetState
rating_collector_enable = cloudkittyclient.v1.collector_cli:CliCollectorEnable
rating_collector_disable = cloudkittyclient.v1.collector_cli:CliCollectorDisable
rating_dataframes_get = cloudkittyclient.v1.storage_cli:CliGetDataframes
rating_pyscript_create = cloudkittyclient.v1.rating.pyscripts_cli:CliCreateScript
rating_pyscript_list = cloudkittyclient.v1.rating.pyscripts_cli:CliListScripts
@@ -152,7 +145,7 @@ openstack.rating.v2 =
rating_pyscript_update = cloudkittyclient.v1.rating.pyscripts_cli:CliUpdateScript
rating_pyscript_delete = cloudkittyclient.v1.rating.pyscripts_cli:CliDeleteScript
cloudkittyclient_v1 =
cloudkittyclient.v1 =
total_get = cloudkittyclient.v1.report_cli:CliTotalGet
summary_get = cloudkittyclient.v1.report_cli:CliSummaryGet
report_tenant_list = cloudkittyclient.v1.report_cli:CliTenantList
@@ -208,16 +201,11 @@ cloudkittyclient_v1 =
pyscript_update = cloudkittyclient.v1.rating.pyscripts_cli:CliUpdateScript
pyscript_delete = cloudkittyclient.v1.rating.pyscripts_cli:CliDeleteScript
cloudkittyclient_v2 =
tasks_reprocessing_get = cloudkittyclient.v2.reprocessing_cli:CliReprocessingTasksGet
tasks_reprocessing_create = cloudkittyclient.v2.reprocessing_cli:CliReprocessingTasksPost
cloudkittyclient.v2 =
dataframes_add = cloudkittyclient.v2.dataframes_cli:CliDataframesAdd
dataframes_get = cloudkittyclient.v2.dataframes_cli:CliDataframesGet
scope_state_get = cloudkittyclient.v2.scope_cli:CliScopeStateGet
scope_state_reset = cloudkittyclient.v2.scope_cli:CliScopeStateReset
scope_patch = cloudkittyclient.v2.scope_cli:CliPatchScope
summary_get = cloudkittyclient.v2.summary_cli:CliSummaryGet
@@ -266,6 +254,7 @@ cloudkittyclient_v2 =
collector_state_get = cloudkittyclient.v1.collector_cli:CliCollectorGetState
collector_enable = cloudkittyclient.v1.collector_cli:CliCollectorEnable
collector_disable = cloudkittyclient.v1.collector_cli:CliCollectorDisable
dataframes_get = cloudkittyclient.v1.storage_cli:CliGetDataframes
pyscript_create = cloudkittyclient.v1.rating.pyscripts_cli:CliCreateScript
pyscript_list = cloudkittyclient.v1.rating.pyscripts_cli:CliListScripts
@@ -278,3 +267,20 @@ keystoneauth1.plugin =
cliff.formatter.list =
df-to-csv = cloudkittyclient.format:DataframeToCsvFormatter
[upload_sphinx]
upload-dir = doc/build/html
[compile_catalog]
directory = cloudkittyclient/locale
domain = python-cloudkittyclient
[update_catalog]
domain = python-cloudkittyclient
output_dir = cloudkittyclient/locale
input_file = cloudkittyclient/locale/python-cloudkittyclient.pot
[extract_messages]
keywords = _ gettext ngettext l_ lazy_gettext
mapping_file = babel.cfg
output_file = cloudkittyclient/locale/python-cloudkittyclient.pot

View File

@@ -13,8 +13,17 @@
# 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>=2.0.0'],
pbr=True)

View File

@@ -2,15 +2,11 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
hacking>=3.0.1,<3.1.0 # Apache-2.0
# remove this pyflakes from here once you bump the
# hacking to 3.2.0 or above. hacking 3.2.0 takes
# care of pyflakes version compatibilty.
pyflakes>=2.1.1
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
coverage>=4.0,!=4.4 # Apache-2.0
python-subunit>=1.4.0 # Apache-2.0/BSD
python-subunit>=0.0.18 # Apache-2.0/BSD
oslotest>=1.10.0 # Apache-2.0
stestr>=2.0 # Apache-2.0
mock>=2.0 # BSD
python-openstackclient>=3.14 # Apache-2.0

76
tox.ini
View File

@@ -1,21 +1,19 @@
[tox]
minversion = 3.18.0
envlist = py3,pep8
minversion = 2.0
envlist = py27,py36,py37,pypy,pep8
skipsdist = True
ignore_basepython_conflict = True
[testenv]
basepython = python3
usedevelop = True
install_command = pip install -U {opts} {packages}
install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/train} -U {opts} {packages}
setenv =
DEVSTACK_VENV={env:DEVSTACK_VENV}
VIRTUAL_ENV={envdir}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands = stestr run {posargs}
[testenv:cover]
basepython = python3
setenv =
VIRTUAL_ENV={envdir}
PYTHON=coverage run --source cloudkittyclient --parallel-mode
@@ -27,72 +25,37 @@ commands =
coverage report
[testenv:debug]
basepython = python3
commands = oslo_debug_helper -t cloudkittyclient/tests {posargs}
[testenv:functional-v1]
passenv =
OS_CLOUD
OS_PROJECT_DOMAIN_ID
OS_USER_DOMAIN_ID
OS_PROJECT_DOMAIN_NAME
OS_USER_DOMAIN_NAME
OS_PROJECT_NAME
OS_IDENTITY_API_VERSION
OS_PASSWORD
OS_AUTH_TYPE
OS_AUTH_URL
OS_USERNAME
OS_ENDPOINT
DEVSTACK_VENV
VIRTUAL_ENV
passenv = OS_CLOUD OS_PROJECT_DOMAIN_ID OS_USER_DOMAIN_ID OS_PROJECT_DOMAIN_NAME OS_USER_DOMAIN_NAME OS_PROJECT_NAME OS_IDENTITY_API_VERSION OS_PASSWORD OS_AUTH_TYPE OS_AUTH_URL OS_USERNAME OS_ENDPOINT
setenv = OS_RATING_API_VERSION=1
commands = stestr run --concurrency=1 --test-path ./cloudkittyclient/tests/functional/v1
[testenv:functional-v2]
passenv =
OS_CLOUD
OS_PROJECT_DOMAIN_ID
OS_USER_DOMAIN_ID
OS_PROJECT_DOMAIN_NAME
OS_USER_DOMAIN_NAME
OS_PROJECT_NAME
OS_IDENTITY_API_VERSION
OS_PASSWORD
OS_AUTH_TYPE
OS_AUTH_URL
OS_USERNAME
OS_ENDPOINT
DEVSTACK_VENV
VIRTUAL_ENV
passenv = OS_CLOUD OS_PROJECT_DOMAIN_ID OS_USER_DOMAIN_ID OS_PROJECT_DOMAIN_NAME OS_USER_DOMAIN_NAME OS_PROJECT_NAME OS_IDENTITY_API_VERSION OS_PASSWORD OS_AUTH_TYPE OS_AUTH_URL OS_USERNAME OS_ENDPOINT
setenv = OS_RATING_API_VERSION=2
commands = stestr run --concurrency=1 --test-path ./cloudkittyclient/tests/functional/v2
[testenv:pep8]
basepython = python3
commands = flake8
[testenv:venv]
basepython = python3
commands = {posargs}
[testenv:docs]
deps =
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/doc/requirements.txt
commands = sphinx-build --keep-going -b html doc/source doc/build/html
[testenv:pdf-docs]
envdir = {toxworkdir}/docs
deps = {[testenv:docs]deps}
allowlist_externals =
make
commands =
sphinx-build --keep-going -b latex doc/source doc/build/pdf
make -C doc/build/pdf
basepython = python3
deps = -r{toxinidir}/doc/requirements.txt
commands = sphinx-build -W -b html doc/source doc/build/html
[flake8]
# E123, E125 skipped as they are invalid PEP-8.
show-source = True
ignore = E123,E125,W503,W504
ignore = E123,E125
builtins = _
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,releasenotes
@@ -100,8 +63,15 @@ exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,releasenotes
import_exceptions = cloudkittyclient.i18n
[testenv:releasenotes]
basepython = python3
deps =
-c{env:TOX_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt}
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/train}
-r{toxinidir}/doc/requirements.txt
commands =
sphinx-build -a -E -W -d releasenotes/build/doctrees --keep-going -b html releasenotes/source releasenotes/build/html
commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
[testenv:lower-constraints]
basepython = python3
deps =
-c{toxinidir}/lower-constraints.txt
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/requirements.txt