Compare commits
32 Commits
stable/202
...
stable/202
| Author | SHA1 | Date | |
|---|---|---|---|
| d1213c3c5d | |||
|
|
1c776112e9 | ||
|
|
8f26fed622 | ||
|
|
de6fcfa027 | ||
|
|
f97ad45e67 | ||
|
|
d3c9d9e82e | ||
|
|
dea9c3043a | ||
|
|
2d965f9459 | ||
|
|
5bd8a2f8fd | ||
|
|
a41e96b0e3 | ||
|
|
e1408eba2b | ||
| 606b47318c | |||
|
|
59dc73588f | ||
|
|
ab5b02d84c | ||
|
|
ec1bf47a47 | ||
| c26fc952ed | |||
|
|
f8da9dab78 | ||
|
|
2240208aa5 | ||
|
|
c4711dfd16 | ||
|
|
4fec2c3a0b | ||
|
|
4058c7f379 | ||
| 5573390afa | |||
|
|
4f375f8d23 | ||
|
|
826297eb04 | ||
| 9db6f56b9a | |||
|
|
8e6f9aee06 | ||
|
|
9fda7927a1 | ||
|
|
ca7a0b1be2 | ||
|
|
33905a1913 | ||
|
|
e2d507900a | ||
| acbd7ae72e | |||
|
|
247b3ee51d |
@@ -2,3 +2,4 @@
|
||||
host=review.opendev.org
|
||||
port=29418
|
||||
project=openstack/python-cloudkittyclient.git
|
||||
defaultbranch=stable/2026.1
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
# 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
|
||||
|
||||
from distutils.util import strtobool
|
||||
|
||||
|
||||
class ScopeManager(base.BaseManager):
|
||||
"""Class used to handle /v2/scope endpoint"""
|
||||
@@ -133,7 +133,8 @@ class ScopeManager(base.BaseManager):
|
||||
)
|
||||
|
||||
if kwargs.get('active'):
|
||||
body['active'] = strtobool(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()))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
5
releasenotes/notes/remove-py39-8c71acfb47b84070.yaml
Normal file
5
releasenotes/notes/remove-py39-8c71acfb47b84070.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
Support for Python 3.9 has been removed. Now Python 3.10 is the minimum
|
||||
version supported.
|
||||
@@ -3,4 +3,4 @@
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/2023.1
|
||||
:branch: unmaintained/2023.1
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/2024.1
|
||||
:branch: unmaintained/2024.1
|
||||
|
||||
6
releasenotes/source/2024.2.rst
Normal file
6
releasenotes/source/2024.2.rst
Normal file
@@ -0,0 +1,6 @@
|
||||
===========================
|
||||
2024.2 Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/2024.2
|
||||
6
releasenotes/source/2025.1.rst
Normal file
6
releasenotes/source/2025.1.rst
Normal file
@@ -0,0 +1,6 @@
|
||||
===========================
|
||||
2025.1 Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/2025.1
|
||||
6
releasenotes/source/2025.2.rst
Normal file
6
releasenotes/source/2025.2.rst
Normal file
@@ -0,0 +1,6 @@
|
||||
===========================
|
||||
2025.2 Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/2025.2
|
||||
@@ -8,6 +8,9 @@ Contents
|
||||
:maxdepth: 2
|
||||
|
||||
unreleased
|
||||
2025.2
|
||||
2025.1
|
||||
2024.2
|
||||
2024.1
|
||||
2023.2
|
||||
2023.1
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
# 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
|
||||
@@ -13,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
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
10
tox.ini
10
tox.ini
@@ -1,23 +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 =
|
||||
DEVSTACK_VENV={env:DEVSTACK_VENV}
|
||||
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}
|
||||
@@ -43,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
|
||||
@@ -62,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
|
||||
@@ -80,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
|
||||
@@ -101,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
|
||||
|
||||
Reference in New Issue
Block a user