Compare commits

..

6 Commits

Author SHA1 Message Date
Luka Peschke
37c6bb0c8b Fix the rating.get_quotation method
This updates the rating.get_quotation method of the client. Tests on this
method have been added.

Depends-On: https://review.openstack.org/#/c/648062/
Change-Id: Ie2de0162311c2d162c1573042187ac4e628bd966
(cherry picked from commit de96c61985)
2020-03-20 14:44:13 +01:00
OpenDev Sysadmins
1e0ed2f044 OpenDev Migration Patch
This commit was bulk generated and pushed by the OpenDev sysadmins
as a part of the Git hosting and code review systems migration
detailed in these mailing list posts:

http://lists.openstack.org/pipermail/openstack-discuss/2019-March/003603.html
http://lists.openstack.org/pipermail/openstack-discuss/2019-April/004920.html

Attempts have been made to correct repository namespaces and
hostnames based on simple pattern matching, but it's possible some
were updated incorrectly or missed entirely. Please reach out to us
via the contact information listed at https://opendev.org/ with any
questions you may have.
2019-04-19 19:42:57 +00:00
Luka Peschke
c1596862ae Fix "cloudkitty report tenant list" command
This fixes the "cloudkitty report tenant list": command, by transforming each
element of the list returned by CliTenantList's take_action method
into a tuple.

Change-Id: Iba1401b0cb4319a668d449139c8d20fc011cf178
Story: 2004149
Task: 27622
2018-10-23 13:41:43 +02:00
Doug Hellmann
56c5badada import zuul job settings from project-config
This is a mechanically generated patch to complete step 1 of moving
the zuul job settings out of project-config and into each project
repository.

Because there will be a separate patch on each branch, the branch
specifiers for branch-specific jobs have been removed.

Because this patch is generated by a script, there may be some
cosmetic changes to the layout of the YAML file(s) as the contents are
normalized.

See the python3-first goal document for details:
https://governance.openstack.org/tc/goals/stein/python3-first.html

Change-Id: I219503b8a87e4d9fde94cbb832134a16adaeb8c0
Story: #2002586
Task: #24289
2018-08-31 08:59:42 -04:00
97e2a29496 Update UPPER_CONSTRAINTS_FILE for stable/rocky
The new stable upper-constraints file is only available
after the openstack/requirements repository is branched.
This will happen around the RC1 timeframe.

Recheck and merge this change once the requirements
repository has been branched.

The CI system will work with this patch before the requirements
repository is branched because zuul configues the job to run
with a local copy of the file and defaults to the master branch.
However, accepting the patch will break the test configuration
on developers' local systems, so please wait until after the
requirements repository is branched to merge the patch.

Change-Id: If5fc68369bb8fc629e35e926008254e864100d6f
2018-08-08 22:09:55 +00:00
ce7aef7912 Update .gitreview for stable/rocky
Change-Id: I5739630144929ebfd1b0919d302cb23f0f03a1c2
2018-08-08 22:09:53 +00:00
101 changed files with 230 additions and 2450 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/rocky

View File

@@ -1,5 +1,5 @@
- job:
name: cloudkittyclient-devstack-functional-base
name: cloudkittyclient-devstack-functional
parent: devstack
description: |
Job for cloudkittyclient functional tests
@@ -19,45 +19,26 @@
vars:
devstack_plugins:
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
tox_install_siblings: false
zuul_work_dir: src/opendev.org/openstack/python-cloudkittyclient
- job:
name: cloudkittyclient-devstack-functional-v1-client
parent: cloudkittyclient-devstack-functional-base
vars:
tox_envlist: functional-v1
- job:
name: cloudkittyclient-devstack-functional-v2-client
parent: cloudkittyclient-devstack-functional-base
vars:
tox_envlist: functional-v2
tox_envlist: functional
- project:
templates:
- check-requirements
- openstack-cover-jobs
- openstack-python3-jobs
- openstack-python-jobs
- openstack-python35-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:
- cloudkittyclient-devstack-functional:
voting: true
gate:
jobs:
- cloudkittyclient-devstack-functional-v1-client:
voting: true
- cloudkittyclient-devstack-functional-v2-client:
- cloudkittyclient-devstack-functional:
voting: true
post:
jobs:
- openstack-tox-cover

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.
http://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
http://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 Launchpad, 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://bugs.launchpad.net/cloudkitty

2
babel.cfg Normal file
View File

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

View File

@@ -1,47 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2018 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 keystoneauth1 import adapter
from keystoneauth1 import session as ks_session
class BaseClient(object):
def __init__(self,
session=None,
adapter_options={},
cacert=None,
insecure=False,
**kwargs):
adapter_options.setdefault('service_type', 'rating')
adapter_options.setdefault('additional_headers', {
'Content-Type': 'application/json',
})
if insecure:
verify_cert = False
else:
if cacert:
verify_cert = cacert
else:
verify_cert = True
self.session = session
if self.session is None:
self.session = ks_session.Session(
verify=verify_cert, **kwargs)
self.api_client = adapter.Adapter(
session=self.session, **adapter_options)

View File

@@ -19,7 +19,6 @@ API_VERSION_OPTION = 'os_rating_api_version'
API_NAME = "rating"
API_VERSIONS = {
"1": "cloudkittyclient.v1.client.Client",
"2": "cloudkittyclient.v2.client.Client",
}
@@ -31,19 +30,9 @@ def make_client(instance):
version,
API_VERSIONS)
instance.setup_auth()
adapter_options = dict(
interface=instance.interface,
region_name=instance.region_name,
)
return ck_client(session=instance.session,
adapter_options=adapter_options)
return ck_client(session=instance.session)
def build_option_parser(parser):
"""Hook to add global options."""
parser.add_argument(
'--rating-api-version', type=int, default=utils.env(
'OS_RATING_API_VERSION',
default=DEFAULT_API_VERSION)
)
return parser

View File

@@ -22,7 +22,6 @@ import os_client_config
from oslo_log import log
from cloudkittyclient import client
from cloudkittyclient.osc import DEFAULT_API_VERSION
from cloudkittyclient import utils
@@ -78,25 +77,13 @@ class CloudKittyShell(cliff.app.App):
'pyscripts-script-update',
]
def _get_api_version(self, args):
# FIXME(peschk_l): This is a hacky way to figure out the client version
# to load. If anybody has a better idea, please fix this.
self.deferred_help = True
parser = self.build_option_parser('CloudKitty CLI client',
utils.get_version())
del self.deferred_help
parsed_args = parser.parse_known_args(args)
return str(parsed_args[0].os_rating_api_version or DEFAULT_API_VERSION)
def __init__(self, args):
self._args = args
self.cloud_config = os_client_config.OpenStackConfig()
super(CloudKittyShell, self).__init__(
description='CloudKitty CLI client',
version=utils.get_version(),
command_manager=CommandManager('cloudkittyclient_v{}'.format(
self._get_api_version(args[:]),
)),
command_manager=CommandManager('cloudkittyclient'),
deferred_help=True,
)
self._client = None
@@ -118,6 +105,9 @@ class CloudKittyShell(cliff.app.App):
description,
version,
argparse_kwargs={'allow_abbrev': False})
parser.add_argument(
'--ck-api-version', type=int, default=1, dest='ck_version',
help='Cloudkitty API version (defaults to 1)')
if 'OS_AUTH_TYPE' not in os.environ.keys() \
and 'OS_PASSWORD' in os.environ.keys():
os.environ['OS_AUTH_TYPE'] = 'password'
@@ -143,10 +133,9 @@ class CloudKittyShell(cliff.app.App):
self.options.os_rating_endpoint_override or
self.options.os_endpoint_override),
)
self._client = client.Client(
str(self.options.os_rating_api_version or DEFAULT_API_VERSION),
session=session,
adapter_options=adapter_options)
self._client = client.Client(str(self.options.ck_version),
session=session,
adapter_options=adapter_options)
return self._client

View File

@@ -1,84 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2018 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.
#
import json
import os
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)
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
)
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))
return json.loads(stdout) if has_output else None
def openstack(self, action,
flags='', params='', fmt='-f json',
stdin=None, has_output=True):
return self._run('openstack rating', action,
flags, params, fmt, stdin, has_output)
def cloudkitty(self, action,
flags='', params='', fmt='-f json',
stdin=None, has_output=True):
return self._run('cloudkitty', action, flags, params, fmt,
stdin, has_output)

View File

@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
# Copyright 2018 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.
#
import json
import os
import shlex
import subprocess
from cloudkittyclient.tests import utils
class BaseFunctionalTest(utils.BaseTestCase):
def _run(self, executable, action,
flags='', params='', fmt='-f json', has_output=True):
if not has_output:
fmt = ''
cmd = ' '.join([executable, flags, action, params, fmt])
cmd = shlex.split(cmd)
p = subprocess.Popen(cmd, env=os.environ.copy(), shell=False,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.returncode != 0:
raise RuntimeError('"{cmd}" returned {val}: {msg}'.format(
cmd=' '.join(cmd), val=p.returncode, msg=stderr))
return json.loads(stdout.decode('ascii')) if has_output else None
def openstack(self, action,
flags='', params='', fmt='-f json', has_output=True):
return self._run('openstack rating', action,
flags, params, fmt, has_output)
def cloudkitty(self, action,
flags='', params='', fmt='-f json', has_output=True):
return self._run('cloudkitty', action, flags, params, fmt, has_output)

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.tests.functional import base
from cloudkittyclient.tests.functional.v1 import base
class CkCollectorTest(base.BaseFunctionalTest):

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.tests.functional import base
from cloudkittyclient.tests.functional.v1 import base
class CkHashmapTest(base.BaseFunctionalTest):

View File

@@ -15,7 +15,7 @@
#
import jsonpath_rw_ext as jp
from cloudkittyclient.tests.functional import base
from cloudkittyclient.tests.functional.v1 import base
class CkInfoTest(base.BaseFunctionalTest):

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.tests.functional import base
from cloudkittyclient.tests.functional.v1 import base
class CkPyscriptTest(base.BaseFunctionalTest):

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.tests.functional import base
from cloudkittyclient.tests.functional.v1 import base
class CkRatingTest(base.BaseFunctionalTest):

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.tests.functional import base
from cloudkittyclient.tests.functional.v1 import base
class CkReportTest(base.BaseFunctionalTest):
@@ -22,8 +22,8 @@ class CkReportTest(base.BaseFunctionalTest):
self.runner = self.cloudkitty
def test_get_summary(self):
resp = self.runner('summary get')
self.assertEqual(len(resp), 0)
resp = self.runner('summary get')[0]
self.assertEqual(resp['Resource Type'], 'ALL')
def test_get_summary_with_groupby(self):
resp = self.runner('summary get', params='-g res_type tenant_id')

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.tests.functional import base
from cloudkittyclient.tests.functional.v1 import base
class CkStorageTest(base.BaseFunctionalTest):

View File

@@ -1,174 +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.
#
import os
import uuid
from cloudkittyclient.tests.functional import base
class CkDataframesTest(base.BaseFunctionalTest):
dataframes_data = """
{
"dataframes": [
{
"period": {
"begin": "20190723T122810Z",
"end": "20190723T132810Z"
},
"usage": {
"metric_one": [
{
"vol": {
"unit": "GiB",
"qty": 1.2
},
"rating": {
"price": 0.04
},
"groupby": {
"group_one": "one",
"group_two": "two"
},
"metadata": {
"attr_one": "one",
"attr_two": "two"
}
}
],
"metric_two": [
{
"vol": {
"unit": "MB",
"qty": 200.4
},
"rating": {
"price": 0.06
},
"groupby": {
"group_one": "one",
"group_two": "two"
},
"metadata": {
"attr_one": "one",
"attr_two": "two"
}
}
]
}
},
{
"period": {
"begin": "20190823T122810Z",
"end": "20190823T132810Z"
},
"usage": {
"metric_one": [
{
"vol": {
"unit": "GiB",
"qty": 2.4
},
"rating": {
"price": 0.08
},
"groupby": {
"group_one": "one",
"group_two": "two"
},
"metadata": {
"attr_one": "one",
"attr_two": "two"
}
}
],
"metric_two": [
{
"vol": {
"unit": "MB",
"qty": 400.8
},
"rating": {
"price": 0.12
},
"groupby": {
"group_one": "one",
"group_two": "two"
},
"metadata": {
"attr_one": "one",
"attr_two": "two"
}
}
]
}
}
]
}
"""
def __init__(self, *args, **kwargs):
super(CkDataframesTest, self).__init__(*args, **kwargs)
self.runner = self.cloudkitty
def setUp(self):
super(CkDataframesTest, self).setUp()
self.fixture_file_name = '{}.json'.format(uuid.uuid4())
with open(self.fixture_file_name, 'w') as f:
f.write(self.dataframes_data)
def tearDown(self):
files = os.listdir('.')
if self.fixture_file_name in files:
os.remove(self.fixture_file_name)
super(CkDataframesTest, self).tearDown()
def test_dataframes_add_with_no_args(self):
self.assertRaisesRegex(
RuntimeError,
'error: the following arguments are required: datafile',
self.runner,
'dataframes add',
fmt='',
has_output=False,
)
def test_dataframes_add(self):
self.runner(
'dataframes add {}'.format(self.fixture_file_name),
fmt='',
has_output=False,
)
def test_dataframes_add_with_hyphen_stdin(self):
with open(self.fixture_file_name, 'r') as f:
self.runner(
'dataframes add -',
fmt='',
stdin=f.read().encode(),
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):
super(OSCDataframesTest, self).__init__(*args, **kwargs)
self.runner = self.openstack

View File

@@ -1,41 +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.functional import base
class CkScopeTest(base.BaseFunctionalTest):
def __init__(self, *args, **kwargs):
super(CkScopeTest, self).__init__(*args, **kwargs)
self.runner = self.cloudkitty
def test_scope_state_get(self):
return True
# FIXME(peschk_l): Uncomment and update this once there is a way to set
# the state of a scope through the client
# resp = self.runner('scope state get')
def test_scope_state_reset(self):
return True
# FIXME(jferrieu): Uncomment and update this once there is a way to set
# the state of a scope through the client
# resp = self.runner('scope state reset')
class OSCScopeTest(CkScopeTest):
def __init__(self, *args, **kwargs):
super(OSCScopeTest, self).__init__(*args, **kwargs)
self.runner = self.openstack

View File

@@ -1,35 +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.functional import base
class CkSummaryTest(base.BaseFunctionalTest):
def __init__(self, *args, **kwargs):
super(CkSummaryTest, self).__init__(*args, **kwargs)
self.runner = self.cloudkitty
def test_summary_get(self):
return True
# FIXME(peschk_l): Uncomment and update this once there is a way to set
# the state of a summary through the client
# resp = self.runner('summary get')
class OSCSummaryTest(CkSummaryTest):
def __init__(self, *args, **kwargs):
super(OSCSummaryTest, self).__init__(*args, **kwargs)
self.runner = self.openstack

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

@@ -1,31 +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 import utils
from cloudkittyclient.v2 import dataframes
from cloudkittyclient.v2.rating import modules
from cloudkittyclient.v2 import scope
from cloudkittyclient.v2 import summary
class BaseAPIEndpointTestCase(utils.BaseTestCase):
def setUp(self):
super(BaseAPIEndpointTestCase, self).setUp()
self.api_client = utils.FakeHTTPClient()
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

@@ -1,172 +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.
#
import json
from collections import OrderedDict
from cloudkittyclient import exc
from cloudkittyclient.tests.unit.v2 import base
class TestDataframes(base.BaseAPIEndpointTestCase):
dataframes_data = """
{
"dataframes": [
{
"period": {
"begin": "20190723T122810Z",
"end": "20190723T132810Z"
},
"usage": {
"metric_one": [
{
"vol": {
"unit": "GiB",
"qty": 1.2
},
"rating": {
"price": 0.04
},
"groupby": {
"group_one": "one",
"group_two": "two"
},
"metadata": {
"attr_one": "one",
"attr_two": "two"
}
}
],
"metric_two": [
{
"vol": {
"unit": "MB",
"qty": 200.4
},
"rating": {
"price": 0.06
},
"groupby": {
"group_one": "one",
"group_two": "two"
},
"metadata": {
"attr_one": "one",
"attr_two": "two"
}
}
]
}
},
{
"period": {
"begin": "20190823T122810Z",
"end": "20190823T132810Z"
},
"usage": {
"metric_one": [
{
"vol": {
"unit": "GiB",
"qty": 2.4
},
"rating": {
"price": 0.08
},
"groupby": {
"group_one": "one",
"group_two": "two"
},
"metadata": {
"attr_one": "one",
"attr_two": "two"
}
}
],
"metric_two": [
{
"vol": {
"unit": "MB",
"qty": 400.8
},
"rating": {
"price": 0.12
},
"groupby": {
"group_one": "one",
"group_two": "two"
},
"metadata": {
"attr_one": "one",
"attr_two": "two"
}
}
]
}
}
]
}
"""
def test_add_dataframes_with_string(self):
self.dataframes.add_dataframes(
dataframes=self.dataframes_data,
)
self.api_client.post.assert_called_once_with(
'/v2/dataframes',
data=self.dataframes_data,
)
def test_add_dataframes_with_json_object(self):
json_data = json.loads(self.dataframes_data)
self.dataframes.add_dataframes(
dataframes=json_data,
)
self.api_client.post.assert_called_once_with(
'/v2/dataframes',
data=json.dumps(json_data),
)
def test_add_dataframes_with_neither_string_nor_object_raises_exc(self):
self.assertRaises(
exc.InvalidArgumentError,
self.dataframes.add_dataframes,
dataframes=[open],
)
def test_add_dataframes_with_no_args_raises_exc(self):
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

@@ -1,88 +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.tests.unit.v2 import base
import datetime
class TestScope(base.BaseAPIEndpointTestCase):
def test_get_scope(self):
self.scope.get_scope_state()
self.api_client.get.assert_called_once_with('/v2/scope')
def test_get_scope_with_args(self):
self.scope.get_scope_state(offset=10, limit=10)
try:
self.api_client.get.assert_called_once_with(
'/v2/scope?limit=10&offset=10')
except AssertionError:
self.api_client.get.assert_called_once_with(
'/v2/scope?offset=10&limit=10')
def test_reset_scope_with_args(self):
self.scope.reset_scope_state(
state=datetime.datetime(2019, 5, 7),
all_scopes=True)
self.api_client.put.assert_called_once_with(
'/v2/scope',
json={
'state': datetime.datetime(2019, 5, 7),
'all_scopes': True,
})
def test_reset_scope_with_list_args(self):
self.scope.reset_scope_state(
state=datetime.datetime(2019, 5, 7),
scope_id=['id1', 'id2'],
all_scopes=False)
self.api_client.put.assert_called_once_with(
'/v2/scope',
json={
'state': datetime.datetime(2019, 5, 7),
'scope_id': 'id1,id2',
})
def test_reset_scope_strips_none_and_false_args(self):
self.scope.reset_scope_state(
state=datetime.datetime(2019, 5, 7),
all_scopes=False,
scope_key=None,
scope_id=['id1', 'id2'])
self.api_client.put.assert_called_once_with(
'/v2/scope',
json={
'state': datetime.datetime(2019, 5, 7),
'scope_id': 'id1,id2',
})
def test_reset_scope_with_no_args_raises_exc(self):
self.assertRaises(
exc.ArgumentRequired,
self.scope.reset_scope_state)
def test_reset_scope_with_lacking_args_raises_exc(self):
self.assertRaises(
exc.ArgumentRequired,
self.scope.reset_scope_state,
state=datetime.datetime(2019, 5, 7))
def test_reset_scope_with_both_args_raises_exc(self):
self.assertRaises(
exc.InvalidArgumentError,
self.scope.reset_scope_state,
state=datetime.datetime(2019, 5, 7),
scope_id=['id1', 'id2'],
all_scopes=True)

View File

@@ -1,39 +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 collections import OrderedDict
from cloudkittyclient.tests.unit.v2 import base
class TestSummary(base.BaseAPIEndpointTestCase):
def test_get_summary(self):
self.summary.get_summary()
self.api_client.get.assert_called_once_with('/v2/summary')
def test_get_summary_with_pagination_args(self):
self.summary.get_summary(offset=10, limit=10)
try:
self.api_client.get.assert_called_once_with(
'/v2/summary?limit=10&offset=10')
except AssertionError:
self.api_client.get.assert_called_once_with(
'/v2/summary?offset=10&limit=10')
def test_get_summary_filters(self):
self.summary.get_summary(
filters=OrderedDict([('one', 'two'), ('three', 'four')]))
self.api_client.get.assert_called_once_with(
'/v2/summary?filters=one%3Atwo%2Cthree%3Afour')

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

@@ -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

@@ -13,7 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.common import client
from keystoneauth1 import adapter
from keystoneauth1 import session as ks_session
from cloudkittyclient.v1 import collector
from cloudkittyclient.v1 import info
from cloudkittyclient.v1 import rating
@@ -21,22 +23,17 @@ from cloudkittyclient.v1 import report
from cloudkittyclient.v1 import storage
class Client(client.BaseClient):
class Client(object):
def __init__(self,
session=None,
adapter_options={},
cacert=None,
insecure=False,
**kwargs):
super(Client, self).__init__(
session=session,
adapter_options=adapter_options,
cacert=cacert,
insecure=insecure,
**kwargs
)
def __init__(self, session=None, adapter_options={}, **kwargs):
adapter_options.setdefault('service_type', 'rating')
self.session = session
if self.session is None:
self.session = ks_session.Session(**kwargs)
self.api_client = adapter.Adapter(
session=self.session, **adapter_options)
self.info = info.InfoManager(self.api_client)
self.collector = collector.CollectorManager(self.api_client)
self.rating = rating.RatingManager(self.api_client)

View File

@@ -15,8 +15,8 @@
#
from oslo_log import log
from cloudkittyclient.common import base
from cloudkittyclient import exc
from cloudkittyclient.v1 import base
LOG = log.getLogger(__name__)

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.common import base
from cloudkittyclient.v1 import base
class InfoManager(base.BaseManager):

View File

@@ -15,9 +15,9 @@
#
from cliff import lister
from cloudkittyclient.common import base
from cloudkittyclient import exc
from cloudkittyclient import utils
from cloudkittyclient.v1 import base
from cloudkittyclient.v1.rating import hashmap
from cloudkittyclient.v1.rating import pyscripts

View File

@@ -13,8 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.common import base
from cloudkittyclient import exc
from cloudkittyclient.v1 import base
class HashmapManager(base.BaseManager):
@@ -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

@@ -13,8 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.common import base
from cloudkittyclient import exc
from cloudkittyclient.v1 import base
class PyscriptManager(base.BaseManager):

View File

@@ -15,7 +15,7 @@
#
from oslo_log import log
from cloudkittyclient.common import base
from cloudkittyclient.v1 import base
LOG = log.getLogger(__name__)

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.common import base
from cloudkittyclient.v1 import base
class StorageManager(base.BaseManager):

View File

@@ -1,46 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2018 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.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
# NOTE(peschk_l) v2 client needs to implement v1 until the v1 API has been
# completely ported to v2
class Client(client.Client):
def __init__(self,
session=None,
adapter_options={},
cacert=None,
insecure=False,
**kwargs):
super(Client, self).__init__(
session=session,
adapter_options=adapter_options,
cacert=cacert,
insecure=insecure,
**kwargs
)
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

@@ -1,77 +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.
#
import json
from cloudkittyclient.common import base
from cloudkittyclient import exc
class DataframesManager(base.BaseManager):
"""Class used to handle /v2/dataframes endpoint"""
url = '/v2/dataframes'
def add_dataframes(self, **kwargs):
"""Add DataFrames to the storage backend. Returns nothing.
:param dataframes: List of dataframes to add to the storage backend.
:type dataframes: list of dataframes
"""
dataframes = kwargs.get('dataframes')
if not dataframes:
raise exc.ArgumentRequired("'dataframes' argument is required")
if not isinstance(dataframes, str):
try:
dataframes = json.dumps(dataframes)
except TypeError:
raise exc.InvalidArgumentError(
"'dataframes' must be either a string"
"or a JSON serializable object.")
url = self.get_url(None, kwargs)
return self.api_client.post(
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

@@ -1,116 +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.
#
import argparse
from cliff import command
from cliff import lister
from oslo_utils import timeutils
from cloudkittyclient import utils
class CliDataframesAdd(command.Command):
"""Add one or several DataFrame objects to the storage backend."""
def get_parser(self, prog_name):
parser = super(CliDataframesAdd, self).get_parser(prog_name)
parser.add_argument(
'datafile',
type=argparse.FileType('r'),
help="File formatted as a JSON object having a DataFrame list"
"under a 'dataframes' key."
"'-' (hyphen) can be specified for using stdin.",
)
return parser
def take_action(self, parsed_args):
with parsed_args.datafile as dfile:
dataframes = dfile.read()
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,76 +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'
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, 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

@@ -1,142 +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.common import base
from cloudkittyclient import exc
from distutils.util import strtobool
class ScopeManager(base.BaseManager):
"""Class used to handle /v2/scope endpoint"""
url = '/v2/scope'
def get_scope_state(self, **kwargs):
"""Returns a paginated list of scopes along with their state.
Some optional filters can be provided.
:param offset: Index of the first scope that should be returned.
:type offset: int
:param limit: Maximal number of scopes to return.
:type limit: int
:param collector: Optional collector to filter on.
:type collector: str or list of str
:param fetcher: Optional fetcher to filter on.
:type fetcher: str or list of str
:param scope_id: Optional scope_id to filter on.
:type scope_id: str or list of str
:param scope_key: Optional scope_key to filter on.
:type scope_key: str or list of str
"""
for key in ('collector', 'fetcher', 'scope_id', 'scope_key'):
if key in kwargs.keys():
if isinstance(kwargs[key], list):
kwargs[key] = ','.join(kwargs[key])
authorized_args = [
'offset', 'limit', 'collector', 'fetcher', 'scope_id', 'scope_key']
url = self.get_url(None, kwargs, authorized_args=authorized_args)
return self.api_client.get(url).json()
def reset_scope_state(self, **kwargs):
"""Returns nothing.
Some optional filters can be provided.
The all_scopes and the scope_id options are mutually exclusive and one
must be provided.
:param state: datetime object from which the state will be reset
:type state: datetime.datetime
:param all_scopes: Whether all scopes must be reset
:type all_scopes: bool
:param collector: Optional collector to filter on.
:type collector: str or list of str
:param fetcher: Optional fetcher to filter on.
:type fetcher: str or list of str
:param scope_id: Optional scope_id to filter on.
:type scope_id: str or list of str
:param scope_key: Optional scope_key to filter on.
:type scope_key: str or list of str
"""
if not kwargs.get('state'):
raise exc.ArgumentRequired("'state' argument is required")
if not kwargs.get('all_scopes') and not kwargs.get('scope_id'):
raise exc.ArgumentRequired(
"You must specify either 'scope_id' or 'all_scopes'")
if kwargs.get('all_scopes') and kwargs.get('scope_id'):
raise exc.InvalidArgumentError(
"You can't specify both 'scope_id' and 'all_scopes'")
for key in ('collector', 'fetcher', 'scope_id', 'scope_key'):
if key in kwargs.keys():
if isinstance(kwargs[key], list):
kwargs[key] = ','.join(kwargs[key])
body = dict(
state=kwargs.get('state'),
scope_id=kwargs.get('scope_id'),
scope_key=kwargs.get('scope_key'),
collector=kwargs.get('collector'),
fetcher=kwargs.get('fetcher'),
all_scopes=kwargs.get('all_scopes'),
)
# Stripping None and False values
body = dict(filter(lambda elem: bool(elem[1]), body.items()))
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

@@ -1,131 +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 command
from cliff import lister
from oslo_utils import timeutils
from cloudkittyclient import utils
class CliScopeStateGet(lister.Lister):
"""Get information about current state of several scopes."""
info_columns = [
('scope_id', 'Scope ID'),
('scope_key', 'Scope Key'),
('collector', 'Collector'),
('fetcher', 'Fetcher'),
('state', 'State')
]
def get_parser(self, prog_name):
parser = super(CliScopeStateGet, self).get_parser(prog_name)
for col in self.info_columns[:-1]:
parser.add_argument(
'--' + col[0].replace('_', '-'), type=str,
action='append', help='Optional filter on ' + col[1])
parser.add_argument('--offset', type=int, default=0,
help='Index of the first scope')
parser.add_argument('--limit', type=int, default=100,
help='Maximal number of scopes')
return parser
def take_action(self, parsed_args):
resp = utils.get_client_from_osc(self).scope.get_scope_state(
offset=parsed_args.offset,
limit=parsed_args.limit,
collector=parsed_args.collector,
fetcher=parsed_args.fetcher,
scope_id=parsed_args.scope_id,
scope_key=parsed_args.scope_key,
)
values = utils.list_to_cols(resp['results'], self.info_columns)
return [col[1] for col in self.info_columns], values
class CliScopeStateReset(command.Command):
"""Reset the state of several scopes."""
info_columns = [
('scope_id', 'Scope ID'),
('scope_key', 'Scope Key'),
('collector', 'Collector'),
('fetcher', 'Fetcher'),
]
def get_parser(self, prog_name):
parser = super(CliScopeStateReset, self).get_parser(prog_name)
for col in self.info_columns:
parser.add_argument(
'--' + col[0].replace('_', '-'), type=str,
action='append', help='Optional filter on ' + col[1])
parser.add_argument(
'-a', '--all-scopes',
action='store_true',
help="Target all scopes at once")
parser.add_argument(
'state',
type=timeutils.parse_isotime,
help="State iso8601 datetime to which the state should be set. "
"Example: 2019-06-01T00:00:00Z.")
return parser
def take_action(self, parsed_args):
utils.get_client_from_osc(self).scope.reset_scope_state(
collector=parsed_args.collector,
fetcher=parsed_args.fetcher,
scope_id=parsed_args.scope_id,
scope_key=parsed_args.scope_key,
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

@@ -1,55 +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.common import base
class SummaryManager(base.BaseManager):
url = '/v2/summary'
def get_summary(self, **kwargs):
"""Returns a paginated list of summaries.
This support filters along with custom grouping.
:param offset: Index of the first scope that should be returned.
:type offset: int
:param limit: Maximal number of scopes to return.
:type limit: int
:param filters: Optional dict of filters to select data on.
:type filters: dict
:param groupby: Optional list of attributes to group data on.
:type groupby: str or list of str.
: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
: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'])
kwargs['filters'] = ','.join(
'{}:{}'.format(k, v) for k, v in
(kwargs.get('filters', None) or {}).items()
)
authorized_args = [
'offset', 'limit', 'filters', 'groupby', 'begin', 'end',
'response_format']
url = self.get_url(None, kwargs, authorized_args=authorized_args)
return self.api_client.get(url).json()

View File

@@ -1,72 +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 oslo_utils import timeutils
from cloudkittyclient import utils
class CliSummaryGet(lister.Lister):
"""Get a summary for a given period."""
def get_parser(self, prog_name):
parser = super(CliSummaryGet, 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 element')
parser.add_argument('--limit', type=int, default=100,
help='Maximal number of elements')
parser.add_argument('-g', '--groupby', type=str, action='append',
help='Attribute to group the summary by. Can be '
'specified several times. One can also group '
'by different time options such as: "time-d" '
'to group by day of the year, "time-w" to '
'group by week of the year, "time-m" to '
'group by month, and "time-y" to group data '
'by year.')
parser.add_argument('--filter', type=filter_, action='append',
help="Optional filter, in 'key:value' format. Can "
"be specified several times. It is also "
"possible to filter data using the group by "
"values. However, one needs to group by as "
"well; for instance, if one wants to filter "
"by resource id (id), then we need to group "
"by id via the option '-g id'.")
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 []))
resp = utils.get_client_from_osc(self).summary.get_summary(
offset=parsed_args.offset,
limit=parsed_args.limit,
begin=parsed_args.begin,
end=parsed_args.end,
filters=filters,
groupby=parsed_args.groupby,
)
columns = [c.replace('_', ' ').capitalize() for c in resp['columns']]
return columns, resp['results']

View File

@@ -2,8 +2,6 @@
# of appearance. Changing the order has an impact on the overall integration
# 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
reno>=2.5.0 # Apache-2.0
cliff>=2.11.0 # Apache-2.0
openstackdocstheme>=1.18.1 # Apache-2.0
sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
reno>=2.5.0 # Apache2

View File

@@ -1,10 +1,15 @@
=============
API Reference
Api Reference
=============
A ``client.Client`` instance has the following submodules (each one
corresponding to an API endpoint):
.. toctree::
:maxdepth: 1
:glob:
:maxdepth: 2
v1/index
v2/index
report
info
rating
collector
storage

View File

@@ -1,12 +0,0 @@
=======================
V1 API client reference
=======================
A ``client.v1.Client`` instance has the following submodules (each one
corresponding to an API endpoint):
.. toctree::
:maxdepth: 1
:glob:
./*

View File

@@ -1,6 +0,0 @@
===========================
dataframes (/v2/dataframes)
===========================
.. automodule:: cloudkittyclient.v2.dataframes
:members:

View File

@@ -1,16 +0,0 @@
=======================
V2 API client reference
=======================
In addition to the modules available in a ``client.v1.Client`` instance, a
``client.v2.Client`` instance has the following submodules (each one
corresponding to an API endpoint):
.. note:: Some modules of the ``client.v2.Client`` replace v1 modules with
their v2 alternative (for example) ``summary``.
.. toctree::
:maxdepth: 1
:glob:
./*

View File

@@ -1,6 +0,0 @@
=================
scope (/v2/scope)
=================
.. automodule:: cloudkittyclient.v2.scope
:members:

View File

@@ -1,6 +0,0 @@
=====================
summary (/v2/summary)
=====================
.. automodule:: cloudkittyclient.v2.summary
:members:

View File

@@ -2,20 +2,6 @@
CLI Reference
=============
V1 Client
=========
.. autoprogram-cliff:: cloudkittyclient_v1
.. autoprogram-cliff:: cloudkittyclient
:application: cloudkitty
V2 Client
=========
.. autoprogram-cliff:: cloudkittyclient_v2
:command: dataframes add
.. autoprogram-cliff:: cloudkittyclient_v2
:command: scope state get
.. autoprogram-cliff:: cloudkittyclient_v2
:command: summary get
:ignored: --format, --column, --max-width, --fit-width, --print-empty, --format-config-file, --noindent, --quote, --sort-column

View File

@@ -23,14 +23,6 @@ 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",
]
# autodoc generation is a bit aggressive and a nuisance when doing heavy
@@ -44,8 +36,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 +52,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 +69,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
==================

View File

@@ -2,61 +2,6 @@
Usage
=====
CLI
===
Authentication
--------------
The CloudKitty client can either be used through the standalone CLI executable
(``cloudkitty``) or through the OpenStack Client module (``openstack rating``).
When using CloudKitty in standalone mode (ie without Keystone authentication),
the API endpoint and the auth method must be specified:
.. code-block:: shell
cloudkitty --os-endpoint http://cloudkitty-api:8889 --os-auth-type cloudkitty-noauth module list
These options can also be specified as environment variables:
.. code-block:: shell
export OS_ENDPOINT=http://cloudkitty-api:8889
export OS_AUTH_TYPE=cloudkitty-noauth
cloudkitty module list
The exact same options apply when using the OpenStack Client plugin:
.. code-block:: shell
# EITHER
openstack rating --os-endpoint http://cloudkitty-api:8889 --os-auth-type cloudkitty-noauth module list
# OR
export OS_ENDPOINT=http://cloudkitty-api:8889
export OS_AUTH_TYPE=cloudkitty-noauth
openstack rating module list
Version
-------
Two versions of the client exist: v1 and v2. The v2 version adds support for
v2 API endpoints. The default API version is 1. You can specify which API
version you want to use via a CLI option:
.. code-block:: shell
# EITHER
cloudkitty --os-rating-api-version 2 summary get
# OR
export OS_RATING_API_VERSION=2
cloudkitty summary get
Again, the option can also be provided to the OSC plugin, both via the CLI
flag or the environment variable.
Python library
==============
@@ -104,23 +49,6 @@ Else, use it the same way as any other OpenStack client::
u'res_type': u'ALL',
u'tenant_id': u'bea6a24f77e946b0a92dca7c78b7870b'}]}
.. warning::
If you want to use SSL with the client as a python library, you need to
provide a cert to keystone's session object. Else, two additional options
are available if you provide an ``auth`` object to the client: ``insecure``
and ``cacert``::
>>> client = ck_client.Client(
'1', auth=auth, insecure=False, cacert='/path/to/ca')
If you want to use the v2 API, you have to specify it at client instanciation
.. code-block:: python
c = ck_client.Client('2', session=session)
When using the ``cloudkitty`` CLI client with keystone authentication, the
auth plugin to use should automagically be detected. If not, you can specify
the auth plugin to use with ``--os-auth-type/--os-auth-plugin``::

View File

@@ -1,8 +0,0 @@
---
upgrade:
- |
The client has been adapted to allow adding support for v2 API
endpoints. The v2 client class implements all v1 endpoints, but v1
endpoints ported to v2 will be overriden. The API version to use can be
specified through the ``--os-rating-api-version`` option or the
``OS_RATING_API_VERSION``.

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 ``/v2/dataframes`` endpoint has been added to the client.
A new ``dataframes add`` CLI command is also available.

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,6 +0,0 @@
---
features:
- |
Support for the ``/v2/summary`` endpoint has been added to the client. The
``summary get`` CLI command as well as the ``client.summary`` object in
the python library have been overriden in case the v2 API is used.

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,5 +0,0 @@
---
fixes:
- |
Fixed a bug where creating a reprocessing task would fail due to sending a
POST request to the wrong endpoint.

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 +0,0 @@
===========================
2024.1 Series Release Notes
===========================
.. release-notes::
:branch: stable/2024.1

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.
@@ -16,6 +16,7 @@
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
import pbr.version
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
@@ -25,8 +26,8 @@
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'oslosphinx',
'reno.sphinxext',
'openstackdocstheme',
]
# Add any paths that contain templates here, relative to this directory.
@@ -42,15 +43,19 @@ 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 version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
cloudkittyclient_version = pbr.version.VersionInfo('python-cloudkittyclient')
# The short X.Y version.
version = ''
version = cloudkittyclient_version.canonical_version_string()
# The full version, including alpha/beta/rc tags.
release = ''
release = cloudkittyclient_version.version_string_with_vcs()
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -82,7 +87,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 = []
@@ -95,7 +100,7 @@ pygments_style = 'native'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'openstackdocs'
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -173,7 +178,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 +198,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 +229,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 +244,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,18 +8,6 @@ Contents
:maxdepth: 2
unreleased
2024.1
2023.2
2023.1
zed
yoga
xena
wallaby
victoria
ussuri
train
stein
rocky
queens
Indices and tables

View File

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

View File

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

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: unmaintained/victoria

View File

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

View File

@@ -1,6 +0,0 @@
=========================
Xena Series Release Notes
=========================
.. release-notes::
:branch: unmaintained/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: unmaintained/zed

View File

@@ -1,17 +1,13 @@
# 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.1.0,>=2.0.0 # Apache-2.0
cliff>=2.11.0,<3.0 # Apache-2.0
keystoneauth1>=3.4.0,<4.0 # Apache-2.0
oslo.utils>=3.35,<4.0 # Apache-2.0
oslo.log>=3.36,<4.0 # Apache-2.0
PyYAML>=3.12,<4.0 # MIT
jsonpath-rw-ext>=1.0 # Apache-2.0
six>=1.11,<2.0 # MIT
os-client-config>=1.29.0 # Apache-2.0

163
setup.cfg
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-dev@lists.openstack.org
home-page = http://www.openstack.org/
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
@@ -14,13 +13,10 @@ 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.5
[files]
packages =
@@ -88,71 +84,8 @@ openstack.rating.v1 =
rating_pyscript_update = cloudkittyclient.v1.rating.pyscripts_cli:CliUpdateScript
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
rating_report_tenant_list = cloudkittyclient.v1.report_cli:CliTenantList
rating_module_get = cloudkittyclient.v1.rating:CliModuleGet
rating_module_list = cloudkittyclient.v1.rating:CliModuleList
rating_module_enable = cloudkittyclient.v1.rating:CliModuleEnable
rating_module_disable = cloudkittyclient.v1.rating:CliModuleDisable
rating_module_set_priority = cloudkittyclient.v1.rating:CliModuleSetPriority
rating_info_config_get = cloudkittyclient.v1.info_cli:CliInfoConfigGet
rating_info_metric_get = cloudkittyclient.v1.info_cli:CliInfoMetricGet
rating_info_metric_list = cloudkittyclient.v1.info_cli:CliInfoMetricList
rating_hashmap_mapping-types_list = cloudkittyclient.v1.rating.hashmap_cli:CliGetMappingTypes
rating_hashmap_service_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetService
rating_hashmap_service_list = cloudkittyclient.v1.rating.hashmap_cli:CliListService
rating_hashmap_service_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateService
rating_hashmap_service_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteService
rating_hashmap_field_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetField
rating_hashmap_field_list = cloudkittyclient.v1.rating.hashmap_cli:CliListField
rating_hashmap_field_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateField
rating_hashmap_field_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteField
rating_hashmap_mapping_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetMapping
rating_hashmap_mapping_list = cloudkittyclient.v1.rating.hashmap_cli:CliListMapping
rating_hashmap_mapping_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateMapping
rating_hashmap_mapping_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteMapping
rating_hashmap_mapping_update = cloudkittyclient.v1.rating.hashmap_cli:CliUpdateMapping
rating_hashmap_group_list = cloudkittyclient.v1.rating.hashmap_cli:CliListGroup
rating_hashmap_group_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateGroup
rating_hashmap_group_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteGroup
rating_hashmap_group_mappings_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetGroupMappings
rating_hashmap_group_thresholds_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetGroupThresholds
rating_hashmap_threshold_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetThreshold
rating_hashmap_threshold_list = cloudkittyclient.v1.rating.hashmap_cli:CliListThreshold
rating_hashmap_threshold_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateThreshold
rating_hashmap_threshold_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteThreshold
rating_hashmap_threshold_update = cloudkittyclient.v1.rating.hashmap_cli:CliUpdateThreshold
rating_collector-mapping_get = cloudkittyclient.v1.collector_cli:CliCollectorMappingGet
rating_collector-mapping_list = cloudkittyclient.v1.collector_cli:CliCollectorMappingList
rating_collector-mapping_create = cloudkittyclient.v1.collector_cli:CliCollectorMappingCreate
rating_collector-mapping_delete = cloudkittyclient.v1.collector_cli:CliCollectorMappingDelete
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_pyscript_create = cloudkittyclient.v1.rating.pyscripts_cli:CliCreateScript
rating_pyscript_list = cloudkittyclient.v1.rating.pyscripts_cli:CliListScripts
rating_pyscript_get = cloudkittyclient.v1.rating.pyscripts_cli:CliGetScript
rating_pyscript_update = cloudkittyclient.v1.rating.pyscripts_cli:CliUpdateScript
rating_pyscript_delete = cloudkittyclient.v1.rating.pyscripts_cli:CliDeleteScript
cloudkittyclient_v1 =
cloudkittyclient =
total_get = cloudkittyclient.v1.report_cli:CliTotalGet
summary_get = cloudkittyclient.v1.report_cli:CliSummaryGet
report_tenant_list = cloudkittyclient.v1.report_cli:CliTenantList
@@ -208,73 +141,25 @@ 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
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
report_tenant_list = cloudkittyclient.v1.report_cli:CliTenantList
module_get = cloudkittyclient.v1.rating:CliModuleGet
module_list = cloudkittyclient.v1.rating:CliModuleList
module_enable = cloudkittyclient.v1.rating:CliModuleEnable
module_disable = cloudkittyclient.v1.rating:CliModuleDisable
module_set_priority = cloudkittyclient.v1.rating:CliModuleSetPriority
info_config_get = cloudkittyclient.v1.info_cli:CliInfoConfigGet
info_metric_get = cloudkittyclient.v1.info_cli:CliInfoMetricGet
info_metric_list = cloudkittyclient.v1.info_cli:CliInfoMetricList
hashmap_mapping-types_list = cloudkittyclient.v1.rating.hashmap_cli:CliGetMappingTypes
hashmap_service_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetService
hashmap_service_list = cloudkittyclient.v1.rating.hashmap_cli:CliListService
hashmap_service_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateService
hashmap_service_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteService
hashmap_field_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetField
hashmap_field_list = cloudkittyclient.v1.rating.hashmap_cli:CliListField
hashmap_field_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateField
hashmap_field_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteField
hashmap_mapping_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetMapping
hashmap_mapping_list = cloudkittyclient.v1.rating.hashmap_cli:CliListMapping
hashmap_mapping_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateMapping
hashmap_mapping_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteMapping
hashmap_mapping_update = cloudkittyclient.v1.rating.hashmap_cli:CliUpdateMapping
hashmap_group_list = cloudkittyclient.v1.rating.hashmap_cli:CliListGroup
hashmap_group_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateGroup
hashmap_group_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteGroup
hashmap_group_mappings_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetGroupMappings
hashmap_group_thresholds_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetGroupThresholds
hashmap_threshold_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetThreshold
hashmap_threshold_list = cloudkittyclient.v1.rating.hashmap_cli:CliListThreshold
hashmap_threshold_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateThreshold
hashmap_threshold_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteThreshold
hashmap_threshold_update = cloudkittyclient.v1.rating.hashmap_cli:CliUpdateThreshold
collector-mapping_get = cloudkittyclient.v1.collector_cli:CliCollectorMappingGet
collector-mapping_list = cloudkittyclient.v1.collector_cli:CliCollectorMappingList
collector-mapping_create = cloudkittyclient.v1.collector_cli:CliCollectorMappingCreate
collector-mapping_delete = cloudkittyclient.v1.collector_cli:CliCollectorMappingDelete
collector_state_get = cloudkittyclient.v1.collector_cli:CliCollectorGetState
collector_enable = cloudkittyclient.v1.collector_cli:CliCollectorEnable
collector_disable = cloudkittyclient.v1.collector_cli:CliCollectorDisable
pyscript_create = cloudkittyclient.v1.rating.pyscripts_cli:CliCreateScript
pyscript_list = cloudkittyclient.v1.rating.pyscripts_cli:CliListScripts
pyscript_get = cloudkittyclient.v1.rating.pyscripts_cli:CliGetScript
pyscript_update = cloudkittyclient.v1.rating.pyscripts_cli:CliUpdateScript
pyscript_delete = cloudkittyclient.v1.rating.pyscripts_cli:CliDeleteScript
keystoneauth1.plugin =
cloudkitty-noauth = cloudkittyclient.auth:CloudKittyNoAuthLoader
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
hacking!=0.13.0,<0.14,>=0.12.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
coverage>=4.0,!=4.4 # Apache-2.0
python-subunit>=1.4.0 # Apache-2.0/BSD
coverage!=4.4,>=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

Some files were not shown because too many files have changed in this diff Show More