Compare commits

..

23 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
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
23 changed files with 224 additions and 44 deletions

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

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

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

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

@@ -48,9 +48,8 @@ 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

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

@@ -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 @@
---
upgrade:
- |
Support for Python 3.9 has been removed. Now Python 3.10 is the minimum
version supported.

View File

@@ -3,4 +3,4 @@
===========================
.. release-notes::
:branch: stable/2024.1
:branch: unmaintained/2024.1

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

@@ -8,6 +8,8 @@ Contents
:maxdepth: 2
unreleased
2025.2
2025.1
2024.2
2024.1
2023.2

View File

@@ -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.8
python_requires = >=3.10
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
@@ -17,10 +17,10 @@ classifier =
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: Implementation :: CPython
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.12
Programming Language :: Python :: 3.13
[files]
packages =

View File

@@ -1,15 +1,12 @@
[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 =
DEVSTACK_VENV={env:DEVSTACK_VENV}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands = stestr run {posargs}
@@ -41,7 +38,6 @@ passenv =
OS_AUTH_URL
OS_USERNAME
OS_ENDPOINT
DEVSTACK_VENV
VIRTUAL_ENV
setenv = OS_RATING_API_VERSION=1
commands = stestr run --concurrency=1 --test-path ./cloudkittyclient/tests/functional/v1
@@ -60,7 +56,6 @@ passenv =
OS_AUTH_URL
OS_USERNAME
OS_ENDPOINT
DEVSTACK_VENV
VIRTUAL_ENV
setenv = OS_RATING_API_VERSION=2
commands = stestr run --concurrency=1 --test-path ./cloudkittyclient/tests/functional/v2
@@ -98,7 +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