Compare commits

..

68 Commits

Author SHA1 Message Date
d1213c3c5d Update .gitreview for stable/2026.1
Change-Id: I195b739ffce2c2615a85e0ecaf865275ae28409a
Signed-off-by: OpenStack Release Bot <infra-root@openstack.org>
Generated-By: openstack/project-config:roles/copy-release-tools-scripts/files/release-tools/functions
2026-03-06 16:09:51 +00:00
Zuul
1c776112e9 Merge "Declare Python 3.13 support" 2026-02-04 10:19:16 +00:00
Zuul
8f26fed622 Merge "reno: Update master for unmaintained/2024.1" 2026-02-04 10:03:45 +00:00
Zuul
de6fcfa027 Merge "[tox] remove DEVSTACK_VENV variable" 2026-01-30 12:21:26 +00:00
Jaromir Wysoglad
f97ad45e67 [tox] remove DEVSTACK_VENV variable
This variable seems to be unused (grepping for it returns
only results from tox.ini, which are getting deleted here).

Looking at the PS where it got introduced
https://review.opendev.org/c/openstack/python-cloudkittyclient/+/893046
it seems like it was supposed to be used in the functional tests,
but there were issues and the path was hardcoded instead.

Change-Id: I8a75f5849894e5a0c327bd6e6b9ef87dd8f1f820
Signed-off-by: Jaromir Wysoglad <jwysogla@redhat.com>
2026-01-29 10:31:43 +01:00
Takashi Kajinami
d3c9d9e82e Declare Python 3.13 support
Python 3.13 is part of supported runtimes for 2026.1[1] and now is
tested.

[1] https://governance.openstack.org/tc/reference/runtimes/2026.1.html

Change-Id: I6b5bf46a8d3cf1c04269a387e37225ddce1eae85
Signed-off-by: Takashi Kajinami <kajinamit@oss.nttdata.com>
2026-01-22 00:48:54 +09:00
Zuul
dea9c3043a Merge "Use v2 API by default" 2025-12-09 14:42:16 +00:00
Juan Larriba
2d965f9459 Use v2 API by default
CloudKitty's v1 API has been deprecated for a while, but the CLI
continued to use that as a default, keeping v2 as optional. This patch
changes that behaviour, switching to use the more modern and currently
maintained v2 as default, while v1 is still available via the
--os-rating-api-version parameter.

Change-Id: I4ca8c4f69b022af53d9f7ec71f3a2efadfc9163e
Signed-off-by: Juan Larriba <jlarriba@redhat.com>
2025-12-09 12:57:43 +01:00
Zuul
5bd8a2f8fd Merge "CI: enable ceilometer plugin" 2025-12-08 16:27:21 +00:00
Zuul
a41e96b0e3 Merge "Replace os-client-config" 2025-12-08 16:24:58 +00:00
Jaromir Wysoglad
e1408eba2b Fix docs job
- Remove skipsdist which breaks automatic generation in CLI reference
  and API reference sections of the docs since tox 4

- repository_name is deprecated in favor of openstackdocs_repository_name

- bug_project and bug_tag are replaced by openstackdocs_use_storyboard

Change-Id: I28d9e87ebd59823ad0ef2b1893a711bfad700286
Signed-off-by: Jaromir Wysoglad <jwysogla@redhat.com>
2025-11-27 10:23:13 +01:00
606b47318c reno: Update master for unmaintained/2024.1
Update the 2024.1 release notes configuration to build from
unmaintained/2024.1.

Change-Id: I02a5088563ec81b900d95c2db1c6988f3b3c1372
Signed-off-by: OpenStack Release Bot <infra-root@openstack.org>
Generated-By: openstack/project-config:roles/copy-release-tools-scripts/files/release-tools/change_reno_branch_to_unmaintained.sh
2025-10-31 11:37:29 +00:00
Zuul
59dc73588f Merge "Update supported python versions" 2025-09-30 12:37:23 +00:00
Takashi Kajinami
ab5b02d84c Update supported python versions
Python 3.9 was removed from the tested runtimes in this cycle[1].
Python 3.8 should have been removed in 2024.2 release.

Also declare support for Python 3.12 which has been tested for some
time and is mandatory now.

[1] https://governance.openstack.org/tc/reference/runtimes/2025.2.html

Change-Id: Ia7cf5ffe4e4093189fdee425afadd81df862be1e
Signed-off-by: Takashi Kajinami <kajinamit@oss.nttdata.com>
2025-09-30 19:38:30 +09:00
Zuul
ec1bf47a47 Merge "Add support to rating rules with start and end" 2025-09-15 13:48:31 +00:00
c26fc952ed Update master for stable/2025.2
Add file to the reno documentation build to show release notes for
stable/2025.2.

Use pbr instruction to increment the minor version number
automatically so that master versions are higher than the versions on
stable/2025.2.

Sem-Ver: feature
Change-Id: I6b6264daba082f260aab197fce0a0a232e7cbb52
Signed-off-by: OpenStack Release Bot <infra-root@openstack.org>
Generated-By: openstack/project-config:roles/copy-release-tools-scripts/files/release-tools/add_release_note_page.sh
2025-09-04 13:48:08 +00:00
Pedro Henrique
f8da9dab78 Add support to rating rules with start and end
It was introduced the concept of start and end periods in
Cloudkitty rating rules. Therefore to make Cloudkitty CLI
compatible with the Cloudkitty REST API, we need to add
those new available attributes in the CLI as well.

Change-Id: I0cd9b61fa81232d235c959da551a7840465fae88
Signed-off-by: Pedro Henrique <phpm13@gmail.com>
2025-09-02 12:22:05 -03:00
Takashi Kajinami
2240208aa5 Replace os-client-config
It was deprecated[1] after the code was merged into openstacksdk[2].

[1] https://review.opendev.org/c/openstack/os-client-config/+/549307
[2] https://review.opendev.org/c/openstack/openstacksdk/+/518128

Change-Id: I47f1ebf037d2e93bf8901d1c9e21b75532c93918
Signed-off-by: Takashi Kajinami <kajinamit@oss.nttdata.com>
2025-07-01 01:16:40 +09:00
Zuul
c4711dfd16 Merge "Use releases.openstack.org instead of opendev.org" 2025-03-25 09:38:15 +00:00
Pierre Riteau
4fec2c3a0b Remove Python 3 variables from devstack config
The USE_PYTHON3 variable was removed earlier this year [1].

[1]  https://review.opendev.org/c/openstack/devstack/+/920658

Change-Id: I50d8c94e00c38d6ec818a99727b2829bd330fba0
2025-03-19 13:58:56 +00:00
Pierre Riteau
4058c7f379 Use releases.openstack.org instead of opendev.org
This is a more stable URL that will redirect to the correct location.

Change-Id: If5ad860a65c1d4934ad47fb06f1f3386b4550008
2025-03-07 18:49:44 +01:00
5573390afa Update master for stable/2025.1
Add file to the reno documentation build to show release notes for
stable/2025.1.

Use pbr instruction to increment the minor version number
automatically so that master versions are higher than the versions on
stable/2025.1.

Sem-Ver: feature
Change-Id: Ie0928aa18c8d69a2a01f2f594f42774b66279a91
2025-03-07 14:40:58 +00:00
Takashi Kajinami
4f375f8d23 tox: Drop envdir
tox now always recreates an env although the env is shared using envdir
options.
~~~
$ tox -e genpolicy
genpolicy: recreate env because env type changed from
{'name': 'genconfig', 'type': 'VirtualEnvRunner'} to
{'name': 'genpolicy', 'type': 'VirtualEnvRunner'}
~~~

According to the maintainer of tox, this functionality is not intended
to be supported.
https://github.com/tox-dev/tox/issues/425#issuecomment-1011944293

Change-Id: I3155eb73edb09184a29522708ef2f676bd06fb7f
2025-02-11 13:26:22 +09:00
Pierre Riteau
826297eb04 CI: enable ceilometer plugin
This should ensure Gnocchi is installed, otherwise we get the following
error in ck-proc:

    keystoneauth1.exceptions.catalog.EndpointNotFound: internalURL endpoint for metric service in RegionOne region not found

Change-Id: Ie0fe899caac481ea31675fcc17e6e849689bde77
2024-11-19 21:23:27 +01:00
9db6f56b9a reno: Update master for unmaintained/2023.1
Update the 2023.1 release notes configuration to build from
unmaintained/2023.1.

Change-Id: I2f94f7eb27f4672b9a9717f3cbd8d63e08a7d116
2024-11-14 10:01:19 +00:00
Zuul
8e6f9aee06 Merge "Replace distutils" 2024-10-14 15:18:32 +00:00
Zuul
9fda7927a1 Merge "Bump hacking" 2024-10-14 10:34:01 +00:00
Takashi Kajinami
ca7a0b1be2 Replace distutils
distutils was deprecated in Python 3.10 and was removed in Python
3.12 [1].

[1] https://docs.python.org//3.10/library/distutils.html

Change-Id: Iade3ecba97cb35f0afcb8e9fbde8dd1095b6fb47
2024-10-02 23:37:11 +09:00
Takashi Kajinami
33905a1913 Bump hacking
hacking 3.0.x is quite old. Bump it to the latest version available.

Change-Id: I31bf48ffaa8fce5d0ea4f5c1cd5031485ae729a1
2024-09-20 00:02:22 +00:00
Zuul
e2d507900a Merge "Update master for stable/2024.2" 2024-09-16 14:23:05 +00:00
acbd7ae72e Update master for stable/2024.2
Add file to the reno documentation build to show release notes for
stable/2024.2.

Use pbr instruction to increment the minor version number
automatically so that master versions are higher than the versions on
stable/2024.2.

Sem-Ver: feature
Change-Id: Idf34ecff29925203f445a87244d24ca5823ca107
2024-09-05 15:54:51 +00:00
Takashi Kajinami
247b3ee51d Remove logic for Python < 3
... because Python 2 support was removed long ago.

Change-Id: I5ac3c2d3d41651274abc98435180efadad3dffd1
2024-09-03 16:34:53 +09:00
Zuul
79a4606ce2 Merge "add group by time help to CLI" 2024-06-26 12:16:31 +00:00
Zuul
e912feb85d Merge "Fix reprocessing POST request" 2024-04-29 11:41:36 +00:00
Zuul
9bb62e0338 Merge "reno: Update master for unmaintained/victoria" 2024-04-29 10:59:58 +00:00
8d992f59dd reno: Update master for unmaintained/zed
Update the zed release notes configuration to build from
unmaintained/zed.

Change-Id: I89f8f9f24499c2aaf663f3463143a688102e78e1
2024-04-26 18:15:28 +00:00
Matt Crees
6ee36ef0e3 Fix reprocessing POST request
The POST request for triggering reprocessing needs to be made to
``/v2/task/reprocesses``. This is the same as for other requests, so we
can drop ``url_for_post``.

Change-Id: If630d4f313c875733dbe1937ff7ca625821e04af
2024-04-23 17:01:09 +01:00
Rafael Weingärtner
98c13304bd add group by time help to CLI
Change-Id: I2176ab207f94b6f986ebc32371f85c47b097a116
2024-04-11 09:41:40 -03:00
Zuul
024399e10a Merge "reno: Update master for unmaintained/xena" 2024-03-30 11:45:09 +00:00
Zuul
0698fdde3a Merge "reno: Update master for unmaintained/wallaby" 2024-03-30 11:41:09 +00:00
bb26dbc381 Update master for stable/2024.1
Add file to the reno documentation build to show release notes for
stable/2024.1.

Use pbr instruction to increment the minor version number
automatically so that master versions are higher than the versions on
stable/2024.1.

Sem-Ver: feature
Change-Id: I4bc29c5959626c2a2f55f943f6c25bb32a9dad99
2024-03-07 15:35:53 +00:00
d876b1b77f reno: Update master for unmaintained/xena
Update the xena release notes configuration to build from
unmaintained/xena.

Change-Id: Ia90b7810a48e5ecce59dcd9d86c556c2574016ad
2024-03-05 14:55:15 +00:00
2a3733e869 reno: Update master for unmaintained/wallaby
Update the wallaby release notes configuration to build from
unmaintained/wallaby.

Change-Id: If27a329a11330fa569d272224753a953c836f81f
2024-03-05 14:54:50 +00:00
00043cc23f reno: Update master for unmaintained/victoria
Update the victoria release notes configuration to build from
unmaintained/victoria.

Change-Id: I7c621c3eaca3ef115e6bbf13aef0044bbd1924b7
2024-03-05 14:54:26 +00:00
Zuul
d30991cefa Merge "reno: Update master for unmaintained/yoga" 2024-02-13 11:26:37 +00:00
36566c32fc reno: Update master for unmaintained/yoga
Update the yoga release notes configuration to build from
unmaintained/yoga.

Change-Id: I00904420fe9ef2cae1ea0d507e40f99b95bcae9e
2024-02-05 16:43:11 +00:00
Ghanshyam Mann
3e9fd5b540 Update python classifier in setup.cfg
As per the current release tested runtime, we test
till python 3.11 so updating the same in python
classifier in setup.cfg

Change-Id: I8695f7f097e4dd98850caab63ea914f0dbf5da11
2024-01-03 21:22:36 -08:00
255a871785 Update master for stable/2023.2
Add file to the reno documentation build to show release notes for
stable/2023.2.

Use pbr instruction to increment the minor version number
automatically so that master versions are higher than the versions on
stable/2023.2.

Sem-Ver: feature
Change-Id: Iae9e9a83c2dcf5edf16d86ce1ec802c6310caa1a
2023-09-07 09:35:33 +00:00
Rafael Weingärtner
244f229af7 Fix passenv declaration in tox.ini and function tests python env
While running the tests with the latest tox I was getting the following error message:
```
failed with pass_env values cannot contain whitespace, use comma to have multiple values in a single line'

```

That error is happening because of the passenv declaration. This patch is proposing a fix for that.

Besides the `tox` issue, we also needed to create a patch for the use of virtual env inside DevStack.
This patches presents a solution to run tests using the virtual env of DevStack.

Change-Id: Id8249ebb15d4047dcc6181908eae66eb39722863
2023-08-31 20:18:31 -03:00
0835706738 Update master for stable/2023.1
Add file to the reno documentation build to show release notes for
stable/2023.1.

Use pbr instruction to increment the minor version number
automatically so that master versions are higher than the versions on
stable/2023.1.

Sem-Ver: feature
Change-Id: I382ddca39bceac78b1fed648738edfaa5655117e
2023-02-21 14:50:01 +00:00
97dc4d64da Switch to 2023.1 Python3 unit tests and generic template name
This is an automatically generated patch to ensure unit testing
is in place for all the of the tested runtimes for antelope. Also,
updating the template name to generic one.

See also the PTI in governance [1].

[1]: https://governance.openstack.org/tc/reference/project-testing-interface.html

Change-Id: Ibd133ec5cb0af7c0835a9014cc44b201050bd15c
2022-09-14 09:15:01 +00:00
0c97e40e2b Update master for stable/zed
Add file to the reno documentation build to show release notes for
stable/zed.

Use pbr instruction to increment the minor version number
automatically so that master versions are higher than the versions on
stable/zed.

Sem-Ver: feature
Change-Id: Iac2443a8053991e604fdab64bced60a7030d9772
2022-09-09 11:44:57 +00:00
niuke
0ee5e17ee5 remove unicode prefix from code
Change-Id: I716442a44cdb0cd651cfed8419713c6d76f2ba14
2022-08-24 19:47:52 +08:00
Zuul
07d4e86ca4 Merge "Add Python3 zed unit tests" 2022-08-08 15:20:12 +00:00
Zuul
6bd44e7413 Merge "Introduce reprocessing task API in the CLI" 2022-08-08 15:20:10 +00:00
Zuul
c30878e414 Merge "Introduce the patch scope API in the CLI" 2022-08-08 15:01:18 +00:00
Elod Illes
eb0cac7148 Add Python3 zed unit tests
This is an automatically generated patch to ensure unit testing
is in place for all the of the tested runtimes for zed.

See also the PTI in governance [1].

[1]: https://governance.openstack.org/tc/reference/project-testing-interface.html

Change-Id: If9b21c3e9628a945d618c38b015196544a69ae8c
2022-08-08 15:51:48 +02:00
Rafael Weingärtner
187b0ce70c Introduce the patch scope API in the CLI
Change-Id: I8134020c409bc6c3e80bf996890e6609e1a763b9
2022-08-08 10:49:42 -03:00
Rafael Weingärtner
277b47779f Introduce reprocessing task API in the CLI
Change-Id: Ieab5df4deb9cbf5eddfc8eca3b028942f6303abd
2022-08-08 10:46:50 -03:00
Ghanshyam Mann
19c0ebad5b Drop lower-constraints.txt and its testing
As discussed in TC PTG[1] and TC resolution[2], we are
dropping the lower-constraints.txt file and its testing.
We will keep lower bounds in the requirements.txt file but
with a note that these are not tested lower bounds and we
try our best to keep them updated.

[1] https://etherpad.opendev.org/p/tc-zed-ptg#L326
[2] https://governance.openstack.org/tc/resolutions/20220414-drop-lower-constraints.html#proposal

Change-Id: I9261e86576e328b28a6c4442731e2ebbb414eb4d
2022-04-30 15:56:26 -05:00
de6c8b22c5 Update master for stable/yoga
Add file to the reno documentation build to show release notes for
stable/yoga.

Use pbr instruction to increment the minor version number
automatically so that master versions are higher than the versions on
stable/yoga.

Sem-Ver: feature
Change-Id: Ib59df27f2c771cb407b267fbcedee3588eb83049
2022-03-03 10:51:04 +00:00
Sam Morrison
783cc22662 Add response_format to list of available args for v2 summary API
This was added in cloudkitty with commit
6ba9d45ea6

Change-Id: Id091ee9f61c2863ed3ea1aaf5d967edf417df22a
2022-02-11 13:16:01 +11:00
Ghanshyam Mann
20003a58ce Re-add python 3.6/3.7 in classifier
We have updated the yoga testing runtime to keep the
py36 testing.

- https://review.opendev.org/c/openstack/governance/+/820195

Unit tests job template is also updated to keep python
3.6 as a voting job. So with the py3.6 and py3.9 testing as voting
job template, we are keeping python 3.6, 3.7, 3.8, and 3.8 as
tested versions in the Yoga cycle.

- https://review.opendev.org/c/openstack/openstack-zuul-jobs/+/820286

This commit re-add the python 3.6/3.7 versions in setup.cfg classifier.

Change-Id: Iaf4b4a82b5a50b3ef2dc793c50ef1b0981f37856
2021-12-13 19:18:27 -06:00
Ghanshyam Mann
719f4cff6a Updating python testing as per Yoga testing runtime
Yoga testing runtime has been updated with py38 and py39
as voting and removed the py36 testing. Unit tests update are
handled by the job template change in openstack-zuul-job

- https://review.opendev.org/c/openstack/openstack-zuul-jobs/+/818609

this commit makes other required changes in setup.cfg metadata.

[1] https://governance.openstack.org/tc/reference/runtimes/yoga.html

Change-Id: Idd8da33c954234b7c5d2d969c50da8aee4bf7904
2021-11-24 19:11:11 -06:00
wu.shiming
64ab6b412a Replace deprecated assertRaisesRegexp
The assertRaisesRegexp method has been deprecated since it was renamed
to assertRaisesRegex in Python 3.2.

https://docs.python.org/3/library/unittest.html#deprecated-aliases

Change-Id: Ibab4d14764372298cc0055f168ff44eabdc873f0
2021-11-09 09:04:34 +08:00
Pierre Riteau
85317b6773 Fix capitalisation of CloudKitty
This is really an excuse to force generating release notes which are
missing for Xena.

Change-Id: Ie8459f44142a981ee3475dd9b2688d155c852376
2021-09-24 09:48:01 +02:00
6e10961c49 Add Python3 yoga unit tests
This is an automatically generated patch to ensure unit testing
is in place for all the of the tested runtimes for yoga.

See also the PTI in governance [1].

[1]: https://governance.openstack.org/tc/reference/project-testing-interface.html

Change-Id: I8e6532689aed280ce32a6b33dbb7b26bfb6c8044
2021-09-10 14:33:13 +00:00
2454c59cb2 Update master for stable/xena
Add file to the reno documentation build to show release notes for
stable/xena.

Use pbr instruction to increment the minor version number
automatically so that master versions are higher than the versions on
stable/xena.

Sem-Ver: feature
Change-Id: I408828a8e29a418dba134bed8cd7a94206b26132
2021-09-10 14:33:08 +00:00
49 changed files with 656 additions and 124 deletions

1
.gitignore vendored
View File

@@ -16,3 +16,4 @@ 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/2026.1

View File

@@ -7,6 +7,7 @@
run: playbooks/cloudkittyclient-devstack-functional/run.yaml
post-run: playbooks/cloudkittyclient-devstack-functional/post.yaml
required-projects:
- name: openstack/ceilometer
- name: openstack/cloudkitty
- name: openstack/python-cloudkittyclient
roles:
@@ -18,11 +19,10 @@
- ^releasenotes/.*$
vars:
devstack_plugins:
ceilometer: https://opendev.org/openstack/ceilometer
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
@@ -43,10 +43,9 @@
- project:
templates:
- openstack-lower-constraints-jobs
- check-requirements
- openstack-cover-jobs
- openstack-python3-xena-jobs
- openstack-python3-jobs
- openstackclient-plugin-jobs
- publish-openstack-docs-pti
- release-notes-jobs-python3

View File

@@ -14,7 +14,7 @@
from osc_lib import utils
DEFAULT_API_VERSION = '1'
DEFAULT_API_VERSION = '2'
API_VERSION_OPTION = 'os_rating_api_version'
API_NAME = "rating"
API_VERSIONS = {

View File

@@ -18,7 +18,7 @@ from sys import argv
import cliff.app
from cliff.commandmanager import CommandManager
import os_client_config
from openstack import config as occ
from oslo_log import log
from cloudkittyclient import client
@@ -90,7 +90,7 @@ class CloudKittyShell(cliff.app.App):
def __init__(self, args):
self._args = args
self.cloud_config = os_client_config.OpenStackConfig()
self.cloud_config = occ.OpenStackConfig()
super(CloudKittyShell, self).__init__(
description='CloudKitty CLI client',
version=utils.get_version(),
@@ -128,7 +128,7 @@ class CloudKittyShell(cliff.app.App):
@property
def client(self):
if self._client is None:
self.cloud = self.cloud_config.get_one_cloud(
self.cloud = self.cloud_config.get_one(
argparse=self.options)
session = self.cloud.get_session()
adapter_options = dict(

View File

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

View File

@@ -13,6 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from datetime import datetime
from datetime import timedelta
from cloudkittyclient.tests.functional import base
@@ -136,13 +139,15 @@ class CkHashmapTest(base.BaseFunctionalTest):
self.assertEqual(len(resp), 0)
def test_create_get_update_delete_mapping_service(self):
future_date = datetime.now() + timedelta(days=1)
date_iso = future_date.isoformat()
resp = self.runner('hashmap service create', params='testservice')[0]
service_id = resp['Service ID']
self._services.append(service_id)
# Create mapping
resp = self.runner('hashmap mapping create',
params='-s {} 12'.format(service_id))[0]
params=f'-s {service_id} 12 --start {date_iso}')[0]
mapping_id = resp['Mapping ID']
self._mappings.append(mapping_id)
self.assertEqual(resp['Service ID'], service_id)
@@ -173,6 +178,8 @@ class CkHashmapTest(base.BaseFunctionalTest):
'hashmap service delete', params=service_id, has_output=False)
def test_create_get_update_delete_mapping_field(self):
future_date = datetime.now() + timedelta(days=1)
date_iso = future_date.isoformat()
resp = self.runner('hashmap service create', params='testservice')[0]
service_id = resp['Service ID']
self._services.append(service_id)
@@ -185,7 +192,8 @@ class CkHashmapTest(base.BaseFunctionalTest):
# Create mapping
resp = self.runner(
'hashmap mapping create',
params='--field-id {} 12 --value testvalue'.format(field_id))[0]
params=f'--field-id {field_id} 12 --value '
f'testvalue --start {date_iso}')[0]
mapping_id = resp['Mapping ID']
self._mappings.append(service_id)
self.assertEqual(resp['Field ID'], field_id)
@@ -203,6 +211,45 @@ class CkHashmapTest(base.BaseFunctionalTest):
params='--cost 10 {}'.format(mapping_id))[0]
self.assertEqual(float(resp['Cost']), float(10))
def test_create_get_update_delete_mapping_field_started(self):
resp = self.runner('hashmap service create',
params='testservice_date_started')[0]
service_id = resp['Service ID']
self._services.append(service_id)
resp = self.runner(
'hashmap field create',
params='{} testfield_date_started'.format(service_id))[0]
field_id = resp['Field ID']
self._fields.append(field_id)
# Create mapping
resp = self.runner(
'hashmap mapping create',
params=f'--field-id {field_id} 12 --value '
f'testvalue')[0]
mapping_id = resp['Mapping ID']
self._mappings.append(service_id)
self.assertEqual(resp['Field ID'], field_id)
self.assertEqual(float(resp['Cost']), float(12))
self.assertEqual(resp['Value'], 'testvalue')
# Get mapping
resp = self.runner(
'hashmap mapping get', params=mapping_id)[0]
self.assertEqual(resp['Mapping ID'], mapping_id)
self.assertEqual(float(resp['Cost']), float(12))
# Should not be able to update a rule that is running (start < now)
try:
self.runner('hashmap mapping update',
params='--cost 10 {}'.format(mapping_id))[0]
except RuntimeError as e:
expected_error = ("You are allowed to update only the attribute "
"[end] as this rule is already running as it "
"started on ")
self.assertIn(expected_error, str(e))
def test_group_mappings_get(self):
# Service and group
resp = self.runner('hashmap service create', params='testservice')[0]

View File

@@ -13,6 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from datetime import datetime
from datetime import timedelta
from cloudkittyclient.tests.functional import base
@@ -23,9 +26,12 @@ class CkPyscriptTest(base.BaseFunctionalTest):
self.runner = self.cloudkitty
def test_create_get_update_list_delete(self):
future_date = datetime.now() + timedelta(days=1)
date_iso = future_date.isoformat()
# Create
resp = self.runner(
'pyscript create', params="testscript 'return 0'")[0]
'pyscript create', params=f"testscript "
f"'return 0' --start {date_iso}")[0]
script_id = resp['Script ID']
self.assertEqual(resp['Name'], 'testscript')
@@ -37,8 +43,9 @@ class CkPyscriptTest(base.BaseFunctionalTest):
# Update
resp = self.runner(
'pyscript update',
params="-n newname -d 'return 1' {}".format(script_id))[0]
self.assertEqual(resp['Name'], 'newname')
params="-d 'return 1' {} --description "
"desc".format(script_id))[0]
self.assertEqual(resp['Script Description'], 'desc')
self.assertEqual(resp['Script ID'], script_id)
self.assertEqual(resp['Data'], 'return 1')
@@ -46,13 +53,49 @@ class CkPyscriptTest(base.BaseFunctionalTest):
resp = self.runner('pyscript list')
self.assertEqual(len(resp), 1)
resp = resp[0]
self.assertEqual(resp['Name'], 'newname')
self.assertEqual(resp['Script Description'], 'desc')
self.assertEqual(resp['Script ID'], script_id)
self.assertEqual(resp['Data'], 'return 1')
# Delete
self.runner('pyscript delete', params=script_id, has_output=False)
def test_create_get_update_list_delete_started(self):
# Create
resp = self.runner(
'pyscript create', params="testscript_started "
"'return 0'")[0]
script_id = resp['Script ID']
self.assertEqual(resp['Name'], 'testscript_started')
# Get
resp = self.runner('pyscript get', params=script_id)[0]
self.assertEqual(resp['Name'], 'testscript_started')
self.assertEqual(resp['Script ID'], script_id)
# Should not be able to update a rule that is running (start < now)
try:
self.runner(
'pyscript update',
params="-d 'return 1' {} --description "
"desc".format(script_id))[0]
except RuntimeError as e:
expected_error = ("You are allowed to update only the attribute "
"[end] as this rule is already running as it "
"started on ")
self.assertIn(expected_error, str(e))
# List
resp = self.runner('pyscript list')
self.assertEqual(len(resp), 1)
resp = resp[0]
self.assertEqual(resp['Script Description'], None)
self.assertEqual(resp['Script ID'], script_id)
self.assertEqual(resp['Data'], 'return 0')
# Delete
self.runner('pyscript delete', params=script_id, has_output=False)
class OSCPyscriptTest(CkPyscriptTest):

View File

@@ -137,7 +137,7 @@ class CkDataframesTest(base.BaseFunctionalTest):
super(CkDataframesTest, self).tearDown()
def test_dataframes_add_with_no_args(self):
self.assertRaisesRegexp(
self.assertRaisesRegex(
RuntimeError,
'error: the following arguments are required: datafile',
self.runner,

View File

@@ -110,7 +110,10 @@ class TestHashmap(base.BaseAPIEndpointTestCase):
self.assertRaises(exc.ArgumentRequired, self.hashmap.get_mapping)
def test_create_mapping(self):
kwargs = dict(cost=2, value='value', field_id='field_id')
kwargs = dict(cost=2, value='value', field_id='field_id',
name='name', start="2024-01-01",
end="2024-01-01",
description="description")
body = dict(
cost=kwargs.get('cost'),
value=kwargs.get('value'),
@@ -119,6 +122,10 @@ class TestHashmap(base.BaseAPIEndpointTestCase):
group_id=kwargs.get('group_id'),
tenant_id=kwargs.get('tenant_id'),
type=kwargs.get('type') or 'flat',
start="2024-01-01",
end="2024-01-01",
description="description",
name='name'
)
self.hashmap.create_mapping(**kwargs)
self.api_client.post.assert_called_once_with(

View File

@@ -38,7 +38,8 @@ class TestPyscripts(base.BaseAPIEndpointTestCase):
self.assertRaises(exc.ArgumentRequired, self.pyscripts.get_script)
def test_create_script(self):
kwargs = dict(name='name', data='data')
kwargs = dict(name='name', data='data', start=None,
end=None, description=None)
self.pyscripts.create_script(**kwargs)
self.api_client.post.assert_called_once_with(
'/v1/rating/module_config/pyscripts/scripts/', json=kwargs)

View File

@@ -13,7 +13,6 @@
# under the License.
#
import inspect
import sys
import pbr.version
@@ -82,11 +81,6 @@ def format_http_errors(ignore):
"""
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)

View File

@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
#
import uuid
from cloudkittyclient.common import base
from cloudkittyclient import exc
@@ -171,6 +173,14 @@ class HashmapManager(base.BaseManager):
:type type: str
:param value: Value of the mapping
:type value: str
:param name: Name of the mapping
:type name: str
:param start: Date the mapping starts being valid
:type start: str
:param end: Date the mapping stops being valid
:type end: str
:param description: Description of the mapping
:type description: str
"""
if kwargs.get('cost') is None:
raise exc.ArgumentRequired("'cost' argument is required")
@@ -196,6 +206,16 @@ class HashmapManager(base.BaseManager):
tenant_id=kwargs.get('tenant_id'),
type=kwargs.get('type') or 'flat',
)
if kwargs.get('description'):
body['description'] = kwargs.get('description')
if kwargs.get('start'):
body['start'] = kwargs.get('start')
if kwargs.get('end'):
body['end'] = kwargs.get('end')
if kwargs.get('name'):
body['name'] = kwargs.get('name')
else:
body['name'] = uuid.uuid4().hex[:24]
url = self.get_url('mappings', kwargs)
return self.api_client.post(url, json=body).json()

View File

@@ -257,6 +257,10 @@ class CliCreateMapping(lister.Lister):
('service_id', 'Service ID'),
('group_id', 'Group ID'),
('tenant_id', 'Project ID'),
('name', 'Mapping Name'),
('start', 'Mapping Start Date'),
('end', 'Mapping End Date'),
('Description', 'Mapping Description')
]
def take_action(self, parsed_args):
@@ -275,6 +279,11 @@ class CliCreateMapping(lister.Lister):
parser.add_argument('-t', '--type', type=str, help='Mapping type')
parser.add_argument('--value', type=str, help='Value')
parser.add_argument('cost', type=float, help='Cost')
parser.add_argument('--name', type=str, help='Mapping Name')
parser.add_argument('--start', type=str, help='Mapping Start')
parser.add_argument('--end', type=str, help='Mapping End')
parser.add_argument('--description', type=str,
help='Mapping Description')
return parser
@@ -321,6 +330,11 @@ class CliUpdateMapping(lister.Lister):
parser.add_argument('--value', type=str, help='Value')
parser.add_argument('--cost', type=str, help='Cost')
parser.add_argument('mapping_id', type=str, help='Mapping ID')
parser.add_argument('--name', type=str, help='Mapping Name')
parser.add_argument('--start', type=str, help='Mapping Start')
parser.add_argument('--end', type=str, help='Mapping End')
parser.add_argument('--description', type=str,
help='Mapping Description')
return parser

View File

@@ -50,13 +50,22 @@ class PyscriptManager(base.BaseManager):
:type name: str
:param data: Content of the script
:type data: str
:param start: Date the script starts being valid
:type start: str
:param end: Date the script stops being valid
:type end: str
:param description: Description of the script
:type description: str
"""
for arg in ('name', 'data'):
if not kwargs.get(arg):
raise exc.ArgumentRequired(
"'Argument {} is required.'".format(arg))
url = self.get_url('scripts', kwargs)
body = dict(name=kwargs['name'], data=kwargs['data'])
body = dict(name=kwargs['name'], data=kwargs['data'],
start=kwargs.get('start'),
end=kwargs.get('end'),
description=kwargs.get('description'))
return self.api_client.post(url, json=body).json()
def update_script(self, **kwargs):
@@ -68,11 +77,17 @@ class PyscriptManager(base.BaseManager):
:type name: str
:param data: Content of the script
:type data: str
:param start: Date the script starts being valid
:type start: str
:param end: Date the script stops being valid
:type end: str
:param description: Description of the script
:type description: str
"""
if not kwargs.get('script_id'):
raise exc.ArgumentRequired("Argument 'script_id' is required.")
script = self.get_script(script_id=kwargs['script_id'])
for key in ('name', 'data'):
for key in ('name', 'data', 'start', 'end', 'description'):
if kwargs.get(key):
script[key] = kwargs[key]
script.pop('checksum', None)

View File

@@ -26,6 +26,9 @@ class BaseScriptCli(lister.Lister):
('script_id', 'Script ID'),
('checksum', 'Checksum'),
('data', 'Data'),
('start', 'Script Start Date'),
('end', 'Script End Date'),
('description', 'Script Description')
]
@@ -82,6 +85,10 @@ class CliCreateScript(BaseScriptCli):
parser = super(CliCreateScript, self).get_parser(prog_name)
parser.add_argument('name', type=str, help='Script Name')
parser.add_argument('data', type=str, help='Script Data or data file')
parser.add_argument('--start', type=str, help='Script Start')
parser.add_argument('--end', type=str, help='Script End')
parser.add_argument('--description', type=str,
help='Script Description')
return parser
@@ -107,6 +114,10 @@ class CliUpdateScript(BaseScriptCli):
parser.add_argument('-n', '--name', type=str, help='Script Name')
parser.add_argument('-d', '--data', type=str,
help='Script Data or data file')
parser.add_argument('--start', type=str, help='Script Start')
parser.add_argument('--end', type=str, help='Script End')
parser.add_argument('--description', type=str,
help='Script Description')
return parser

View File

@@ -16,6 +16,7 @@
from cloudkittyclient.v1 import client
from cloudkittyclient.v2 import dataframes
from cloudkittyclient.v2.rating import modules
from cloudkittyclient.v2 import reprocessing
from cloudkittyclient.v2 import scope
from cloudkittyclient.v2 import summary
@@ -42,3 +43,4 @@ class Client(client.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

@@ -0,0 +1,76 @@
# 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

@@ -0,0 +1,95 @@
# 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

@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from oslo_utils import strutils
from cloudkittyclient.common import base
from cloudkittyclient import exc
@@ -100,3 +102,42 @@ class ScopeManager(base.BaseManager):
url = self.get_url(None, kwargs)
return self.api_client.put(url, json=body)
def update_scope(self, **kwargs):
"""Update storage scope
The `scope_id field` is mandatory, and all other are optional. Only the
attributes sent will be updated. The attributes that are not sent will
not be changed in the backend.
:param collector: collector to be used by the scope.
:type collector: str
:param fetcher: fetcher to be used by the scope.
:type fetcher: str
:param scope_id: Mandatory scope_id to update.
:type scope_id: str
:param scope_key: scope_key to be used by the scope.
:type scope_key: str
:param active: Indicates if the scope is active or not
:type active: str
"""
if not kwargs.get('scope_id'):
raise exc.ArgumentRequired("'scope_id' argument is required")
body = dict(
scope_id=kwargs.get('scope_id'),
scope_key=kwargs.get('scope_key'),
collector=kwargs.get('collector'),
fetcher=kwargs.get('fetcher')
)
if kwargs.get('active'):
body['active'] = strutils.bool_from_string(
kwargs.get('active'), strict=True)
# Stripping None
body = dict(filter(lambda elem: elem[1] is not None, body.items()))
url = self.get_url(None, kwargs)
return self.api_client.patch(url, json=body).json()

View File

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

View File

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

@@ -35,10 +35,20 @@ class CliSummaryGet(lister.Lister):
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')
'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.")
"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.")

View File

@@ -44,13 +44,12 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = u'python-cloudkittyclient'
copyright = u'2017, OpenStack Foundation'
project = 'python-cloudkittyclient'
copyright = '2017, OpenStack Foundation'
# openstackdocstheme options
repository_name = 'openstack/python-cloudkittyclient'
bug_project = 'cloudkitty'
bug_tag = 'python-cloudkittyclient'
openstackdocs_repo_name = 'openstack/python-cloudkittyclient'
openstackdocs_use_storyboard = True
# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = True
@@ -99,8 +98,8 @@ latex_elements = {
latex_documents = [
('index',
'doc-%s.tex' % project,
u'%s Documentation' % project,
u'OpenStack Foundation', 'howto', True),
'%s Documentation' % project,
'OpenStack Foundation', 'howto', True),
]
# Example configuration for intersphinx: refer to the Python standard library.

View File

@@ -22,7 +22,7 @@ Please refer the `python-cloudkittyclient Core Team
New Feature Planning
~~~~~~~~~~~~~~~~~~~~
Cloudkitty features are tracked on `Storyboard <https://storyboard.openstack.org/#!/project/895>`_.
CloudKitty features are tracked on `Storyboard <https://storyboard.openstack.org/#!/project/895>`_.
Task Tracking
~~~~~~~~~~~~~

View File

@@ -42,17 +42,17 @@ 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
v2 API endpoints. The default API version is 2. 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
cloudkitty --os-rating-api-version 1 module list
# OR
export OS_RATING_API_VERSION=2
cloudkitty summary get
export OS_RATING_API_VERSION=1
cloudkitty module list
Again, the option can also be provided to the OSC plugin, both via the CLI
flag or the environment variable.
@@ -68,7 +68,7 @@ to use it without keystone authentication, cloudkittyclient provides the
>>> from cloudkittyclient import auth as ck_auth
>>> auth = ck_auth.CloudKittyNoAuthPlugin(endpoint='http://127.0.0.1:8889')
>>> client = ck_client.Client('1', auth=auth)
>>> client = ck_client.Client('2', auth=auth)
>>> client.report.get_summary()
{u'summary': [{u'begin': u'2018-03-01T00:00:00',
u'end': u'2018-04-01T00:00:00',
@@ -95,7 +95,7 @@ Else, use it the same way as any other OpenStack client::
>>> ck_session = session.Session(auth=auth)
>>> c = ck_client.Client('1', session=ck_session)
>>> c = ck_client.Client('2', session=ck_session)
>>> c.report.get_summary()
{u'summary': [{u'begin': u'2018-03-01T00:00:00',
@@ -112,25 +112,25 @@ Else, use it the same way as any other OpenStack client::
and ``cacert``::
>>> client = ck_client.Client(
'1', auth=auth, insecure=False, cacert='/path/to/ca')
'2', auth=auth, insecure=False, cacert='/path/to/ca')
If you want to use the v2 API, you have to specify it at client instanciation
If you want to use the v1 API, you have to specify it at client instanciation
.. code-block:: python
c = ck_client.Client('2', session=session)
c = ck_client.Client('1', 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``::
$ cloudkitty --debug --os-auth-type cloudkitty-noauth summary get
+------------+---------------+------------+---------------------+---------------------+
| Project ID | Resource Type | Rate | Begin Time | End Time |
+------------+---------------+------------+---------------------+---------------------+
| ALL | ALL | 1676.95499 | 2018-03-01T00:00:00 | 2018-04-01T00:00:00 |
+------------+---------------+------------+---------------------+---------------------+
+---------------------------+---------------------------+------------+-------------------+
| Begin | End | Qty | Rate |
+---------------------------+---------------------------+------------+-------------------+
| 2025-12-01T00:00:00+01:00 | 2026-01-01T00:00:00+01:00 | 21662194.0 | 3618130.211340219 |
+---------------------------+---------------------------+------------+-------------------+
CSV report generation

View File

@@ -1,23 +0,0 @@
# requirements
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
# test-requirements.txt
pyflakes==2.1.1
coverage==4.0 # Apache-2.0
python-subunit==1.4.0 # Apache-2.0/BSD
oslotest==1.10.0 # Apache-2.0
stestr==2.0 # Apache-2.0
python-openstackclient==3.14 # Apache-2.0
# doc/requirements.txt
openstackdocstheme==1.30.0 # Apache-2.0
sphinx==1.6.2 # BSD
reno==2.5.0 # Apache2

View File

@@ -0,0 +1,6 @@
---
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

@@ -0,0 +1,10 @@
---
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

@@ -0,0 +1,8 @@
---
upgrade:
- |
The default API version has been changed from v1 to v2. Users who want
to continue using the v1 API must now explicitly specify the API version
using the ``--os-rating-api-version 1`` CLI option or by setting the
``OS_RATING_API_VERSION=1`` environment variable. If no version is
specified, the client will now use the v2 API by default.

View File

@@ -0,0 +1,5 @@
---
fixes:
- |
Fixed a bug where creating a reprocessing task would fail due to sending a
POST request to the wrong endpoint.

View File

@@ -0,0 +1,5 @@
---
upgrade:
- |
Support for Python 3.9 has been removed. Now Python 3.10 is the minimum
version supported.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Cloudkitty Release Notes documentation build configuration file.
# CloudKitty Client Release Notes documentation build configuration file.
#
# This file is execfile()d with the current directory set to its
# containing dir.
@@ -42,8 +42,8 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = u'Cloudkitty Client Release Notes'
copyright = u'2016, Cloudkitty developers'
project = 'CloudKitty Client Release Notes'
copyright = '2016, CloudKitty developers'
# Release notes are version independent.
# The short X.Y version.
@@ -173,7 +173,7 @@ html_static_path = ['_static']
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'CloudkittyReleaseNotestdoc'
htmlhelp_basename = 'CloudKittyClientReleaseNotestdoc'
# -- Options for LaTeX output ---------------------------------------------
@@ -193,8 +193,9 @@ latex_elements = {
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'PythonCloudkitty.tex', u'Cloudkitty Release Notes Documentation',
u'Cloudkitty developers', 'manual'),
('index', 'PythonCloudKittyClient.tex',
'CloudKitty Client Release Notes Documentation',
'CloudKitty developers', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -224,8 +225,8 @@ latex_documents = [
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'cloudkittyclient',
u'Cloudkitty Client Release Notes Documentation',
[u'Cloudkitty developers'], 1)
'CloudKitty Client Release Notes Documentation',
['CloudKitty developers'], 1)
]
# If true, show URL addresses after external links.
@@ -239,8 +240,8 @@ man_pages = [
# dir menu entry, description, category)
texinfo_documents = [
('index', 'cloudkittyclient',
u'Cloudkitty Client Release Notes Documentation',
u'Cloudkitty Client developers', 'Cloudkittyclient',
'CloudKitty Client Release Notes Documentation',
'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,6 +8,15 @@ Contents
:maxdepth: 2
unreleased
2025.2
2025.1
2024.2
2024.1
2023.2
2023.1
zed
yoga
xena
wallaby
victoria
ussuri

View File

@@ -3,4 +3,4 @@ Victoria Series Release Notes
=============================
.. release-notes::
:branch: stable/victoria
:branch: unmaintained/victoria

View File

@@ -3,4 +3,4 @@ Wallaby Series Release Notes
============================
.. release-notes::
:branch: stable/wallaby
:branch: unmaintained/wallaby

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
# 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.
# 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.
pbr>=5.5.1 # Apache-2.0
cliff>=3.5.0 # Apache-2.0
@@ -9,5 +9,5 @@ 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
openstacksdk>=0.10.0 # Apache-2.0
osc-lib>=2.3.0 # Apache-2.0

View File

@@ -6,7 +6,7 @@ description_file =
author = OpenStack
author_email = openstack-discuss@lists.openstack.org
home_page = https://docs.openstack.org/python-cloudkittyclient/latest/
python_requires = >=3.6
python_requires = >=3.10
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
@@ -17,9 +17,10 @@ classifier =
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: 3
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12
Programming Language :: Python :: 3.13
[files]
packages =
@@ -88,11 +89,15 @@ openstack.rating.v1 =
rating_pyscript_delete = cloudkittyclient.v1.rating.pyscripts_cli:CliDeleteScript
openstack.rating.v2 =
rating_tasks_reprocessing_get = cloudkittyclient.v2.reprocessing_cli:CliReprocessingTasksGet
rating_tasks_reprocessing_create = cloudkittyclient.v2.reprocessing_cli:CliReprocessingTasksPost
rating_dataframes_get = cloudkittyclient.v2.dataframes_cli:CliDataframesGet
rating_dataframes_add = cloudkittyclient.v2.dataframes_cli:CliDataframesAdd
rating_scope_state_get = cloudkittyclient.v2.scope_cli:CliScopeStateGet
rating_scope_state_reset = cloudkittyclient.v2.scope_cli:CliScopeStateReset
rating_scope_patch = cloudkittyclient.v2.scope_cli:CliPatchScope
rating_summary_get = cloudkittyclient.v2.summary_cli:CliSummaryGet
@@ -204,11 +209,15 @@ cloudkittyclient_v1 =
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

View File

@@ -1,14 +1,4 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
hacking>=3.0.1,<3.1.0 # Apache-2.0
# remove this pyflakes from here once you bump the
# hacking to 3.2.0 or above. hacking 3.2.0 takes
# care of pyflakes version compatibilty.
pyflakes>=2.1.1
hacking>=7.0.0,<7.1.0 # Apache-2.0
coverage>=4.0,!=4.4 # Apache-2.0
python-subunit>=1.4.0 # Apache-2.0/BSD
oslotest>=1.10.0 # Apache-2.0

43
tox.ini
View File

@@ -1,22 +1,18 @@
[tox]
minversion = 3.18.0
envlist = py3,pep8
skipsdist = True
ignore_basepython_conflict = True
[testenv]
basepython = python3
usedevelop = True
install_command = pip install -U {opts} {packages}
setenv =
VIRTUAL_ENV={envdir}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands = stestr run {posargs}
[testenv:cover]
setenv =
VIRTUAL_ENV={envdir}
PYTHON=coverage run --source cloudkittyclient --parallel-mode
commands =
stestr run {posargs}
@@ -29,12 +25,38 @@ commands =
commands = oslo_debug_helper -t cloudkittyclient/tests {posargs}
[testenv:functional-v1]
passenv = OS_CLOUD OS_PROJECT_DOMAIN_ID OS_USER_DOMAIN_ID OS_PROJECT_DOMAIN_NAME OS_USER_DOMAIN_NAME OS_PROJECT_NAME OS_IDENTITY_API_VERSION OS_PASSWORD OS_AUTH_TYPE OS_AUTH_URL OS_USERNAME OS_ENDPOINT
passenv =
OS_CLOUD
OS_PROJECT_DOMAIN_ID
OS_USER_DOMAIN_ID
OS_PROJECT_DOMAIN_NAME
OS_USER_DOMAIN_NAME
OS_PROJECT_NAME
OS_IDENTITY_API_VERSION
OS_PASSWORD
OS_AUTH_TYPE
OS_AUTH_URL
OS_USERNAME
OS_ENDPOINT
VIRTUAL_ENV
setenv = OS_RATING_API_VERSION=1
commands = stestr run --concurrency=1 --test-path ./cloudkittyclient/tests/functional/v1
[testenv:functional-v2]
passenv = OS_CLOUD OS_PROJECT_DOMAIN_ID OS_USER_DOMAIN_ID OS_PROJECT_DOMAIN_NAME OS_USER_DOMAIN_NAME OS_PROJECT_NAME OS_IDENTITY_API_VERSION OS_PASSWORD OS_AUTH_TYPE OS_AUTH_URL OS_USERNAME OS_ENDPOINT
passenv =
OS_CLOUD
OS_PROJECT_DOMAIN_ID
OS_USER_DOMAIN_ID
OS_PROJECT_DOMAIN_NAME
OS_USER_DOMAIN_NAME
OS_PROJECT_NAME
OS_IDENTITY_API_VERSION
OS_PASSWORD
OS_AUTH_TYPE
OS_AUTH_URL
OS_USERNAME
OS_ENDPOINT
VIRTUAL_ENV
setenv = OS_RATING_API_VERSION=2
commands = stestr run --concurrency=1 --test-path ./cloudkittyclient/tests/functional/v2
@@ -51,7 +73,6 @@ deps =
commands = sphinx-build --keep-going -b html doc/source doc/build/html
[testenv:pdf-docs]
envdir = {toxworkdir}/docs
deps = {[testenv:docs]deps}
allowlist_externals =
make
@@ -72,13 +93,7 @@ import_exceptions = cloudkittyclient.i18n
[testenv:releasenotes]
deps =
-c{env:TOX_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt}
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/doc/requirements.txt
commands =
sphinx-build -a -E -W -d releasenotes/build/doctrees --keep-going -b html releasenotes/source releasenotes/build/html
[testenv:lower-constraints]
deps =
-c{toxinidir}/lower-constraints.txt
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/requirements.txt