Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5d1526dec | ||
|
|
729ab402a5 | ||
|
|
3bcb6949b7 | ||
|
|
71fa216168 | ||
|
|
41fecfcd1e | ||
|
|
5c31e34202 | ||
| 22e9ffdb8b | |||
| e251e5c451 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -16,4 +16,3 @@ dist
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
releasenotes/build
|
||||
.idea/
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
host=review.opendev.org
|
||||
port=29418
|
||||
project=openstack/python-cloudkittyclient.git
|
||||
defaultbranch=stable/train
|
||||
|
||||
37
.zuul.yaml
37
.zuul.yaml
@@ -7,7 +7,6 @@
|
||||
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:
|
||||
@@ -19,7 +18,6 @@
|
||||
- ^releasenotes/.*$
|
||||
vars:
|
||||
devstack_plugins:
|
||||
ceilometer: https://opendev.org/openstack/ceilometer
|
||||
cloudkitty: https://opendev.org/openstack/cloudkitty
|
||||
devstack_localrc:
|
||||
CLOUDKITTY_FETCHER: keystone
|
||||
@@ -41,23 +39,54 @@
|
||||
vars:
|
||||
tox_envlist: functional-v2
|
||||
|
||||
- job:
|
||||
name: cloudkittyclient-devstack-functional-base-py3
|
||||
parent: cloudkittyclient-devstack-functional-base
|
||||
description: |
|
||||
Job for cloudkittyclient functional tests, ran in python3.
|
||||
vars:
|
||||
devstack_localrc:
|
||||
DEVSTACK_GATE_USE_PYTHON3: "True"
|
||||
USE_PYTHON3: "True"
|
||||
|
||||
- job:
|
||||
name: cloudkittyclient-devstack-functional-v1-client-py3
|
||||
parent: cloudkittyclient-devstack-functional-base-py3
|
||||
vars:
|
||||
tox_envlist: functional-v1
|
||||
|
||||
- job:
|
||||
name: cloudkittyclient-devstack-functional-v2-client-py3
|
||||
parent: cloudkittyclient-devstack-functional-base-py3
|
||||
vars:
|
||||
tox_envlist: functional-v2
|
||||
|
||||
- project:
|
||||
templates:
|
||||
- openstack-lower-constraints-jobs
|
||||
- check-requirements
|
||||
- openstack-cover-jobs
|
||||
- openstack-python3-jobs
|
||||
- openstack-python-jobs
|
||||
- openstack-python3-train-jobs
|
||||
- openstackclient-plugin-jobs
|
||||
- publish-openstack-docs-pti
|
||||
- release-notes-jobs-python3
|
||||
check:
|
||||
jobs:
|
||||
- cloudkittyclient-devstack-functional-v1-client:
|
||||
voting: true
|
||||
- cloudkittyclient-devstack-functional-v2-client:
|
||||
voting: true
|
||||
- cloudkittyclient-devstack-functional-v1-client-py3:
|
||||
voting: true
|
||||
- cloudkittyclient-devstack-functional-v2-client-py3:
|
||||
voting: true
|
||||
gate:
|
||||
jobs:
|
||||
- cloudkittyclient-devstack-functional-v1-client:
|
||||
voting: true
|
||||
- cloudkittyclient-devstack-functional-v2-client:
|
||||
voting: true
|
||||
- cloudkittyclient-devstack-functional-v1-client-py3:
|
||||
voting: true
|
||||
- cloudkittyclient-devstack-functional-v2-client-py3:
|
||||
voting: true
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
The source repository for this project can be found at:
|
||||
|
||||
https://opendev.org/openstack/python-cloudkittyclient
|
||||
If you would like to contribute to the development of OpenStack,
|
||||
you must follow the steps in this page:
|
||||
|
||||
Pull requests submitted through GitHub are not monitored.
|
||||
https://docs.openstack.org/infra/manual/developers.html
|
||||
|
||||
To start contributing to OpenStack, follow the steps in the contribution guide
|
||||
to set up and use Gerrit:
|
||||
Once those steps have been completed, changes to OpenStack
|
||||
should be submitted for review via the Gerrit tool, following
|
||||
the workflow documented at:
|
||||
|
||||
https://docs.openstack.org/contributors/code-and-documentation/quick-start.html
|
||||
https://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
||||
Bugs should be filed on Storyboard:
|
||||
Pull requests submitted through GitHub will be ignored.
|
||||
|
||||
https://storyboard.openstack.org/#!/project/895
|
||||
Bugs should be filed on Storyboard, not GitHub:
|
||||
|
||||
For more specific information about contributing to this repository, see the
|
||||
python-cloudkittyclient contributor guide:
|
||||
|
||||
https://docs.openstack.org/python-cloudkittyclient/latest/contributor/contributing.html
|
||||
https://storyboard.openstack.org/#!/project/openstack/python-cloudkittyclient
|
||||
|
||||
@@ -12,24 +12,13 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
#
|
||||
from string import Formatter as StringFormatter
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from cloudkittyclient import utils
|
||||
from six.moves.urllib.parse import urlencode
|
||||
|
||||
|
||||
class HttpDecoratorMeta(type):
|
||||
|
||||
ignore = ('get_url', )
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
return utils.format_http_errors(HttpDecoratorMeta.ignore)(
|
||||
super(HttpDecoratorMeta, cls).__new__(cls, *args, **kwargs)
|
||||
)
|
||||
|
||||
|
||||
class BaseManager(object, metaclass=HttpDecoratorMeta):
|
||||
class BaseManager(object):
|
||||
"""Base class for Endpoint Manager objects."""
|
||||
|
||||
url = ''
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
from osc_lib import utils
|
||||
|
||||
DEFAULT_API_VERSION = '2'
|
||||
DEFAULT_API_VERSION = '1'
|
||||
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
|
||||
from openstack import config as occ
|
||||
import os_client_config
|
||||
from oslo_log import log
|
||||
|
||||
from cloudkittyclient import client
|
||||
@@ -90,11 +90,11 @@ class CloudKittyShell(cliff.app.App):
|
||||
|
||||
def __init__(self, args):
|
||||
self._args = args
|
||||
self.cloud_config = occ.OpenStackConfig()
|
||||
self.cloud_config = os_client_config.OpenStackConfig()
|
||||
super(CloudKittyShell, self).__init__(
|
||||
description='CloudKitty CLI client',
|
||||
version=utils.get_version(),
|
||||
command_manager=CommandManager('cloudkittyclient_v{}'.format(
|
||||
command_manager=CommandManager('cloudkittyclient.v{}'.format(
|
||||
self._get_api_version(args[:]),
|
||||
)),
|
||||
deferred_help=True,
|
||||
@@ -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(
|
||||
self.cloud = self.cloud_config.get_one_cloud(
|
||||
argparse=self.options)
|
||||
session = self.cloud.get_session()
|
||||
adapter_options = dict(
|
||||
|
||||
@@ -15,57 +15,26 @@
|
||||
#
|
||||
import json
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
|
||||
from cloudkittyclient.tests import utils
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseFunctionalTest(utils.BaseTestCase):
|
||||
|
||||
# DevStack is using VENV by default. Therefore, to execute the commands,
|
||||
# we need to activate the VENV. And, to do that, we need the VENV path.
|
||||
# This path is hardcoded here because we could not find a variable in this
|
||||
# Python code to retrieve the VENV variable from the test machine.
|
||||
# It seems that because of the stack TOX -> stestr -> this python code, and
|
||||
# so on, we are not able to access the DevStack variables here.
|
||||
#
|
||||
# If somebody finds a solution, we can remove the hardcoded path here.
|
||||
DEV_STACK_VENV_BASE_PATH = "/opt/stack/data/venv"
|
||||
|
||||
BASE_COMMAND_WITH_VENV = "source %s/bin/activate && %s "
|
||||
|
||||
def _run(self, executable, action,
|
||||
flags='', params='', fmt='-f json', stdin=None, has_output=True):
|
||||
if not has_output:
|
||||
fmt = ''
|
||||
|
||||
does_venv_exist = not os.system("ls -lah /opt/stack/data/venv")
|
||||
LOG.info("Test to check if the VENV file exist returned: [%s].",
|
||||
does_venv_exist)
|
||||
|
||||
system_variables = os.environ.copy()
|
||||
LOG.info("System variables [%s] found when executing the tests.",
|
||||
system_variables)
|
||||
|
||||
cmd = ' '.join([executable, flags, action, params, fmt])
|
||||
|
||||
actual_command_with_venv = self.BASE_COMMAND_WITH_VENV % (
|
||||
self.DEV_STACK_VENV_BASE_PATH, cmd)
|
||||
|
||||
LOG.info("Command being executed: [%s].", actual_command_with_venv)
|
||||
|
||||
cmd = shlex.split(cmd)
|
||||
p = subprocess.Popen(
|
||||
["bash", "-c", actual_command_with_venv],
|
||||
env=os.environ.copy(), shell=False, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, stdin=subprocess.PIPE if stdin else None
|
||||
cmd, env=os.environ.copy(), shell=False,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
stdin=subprocess.PIPE if stdin else None,
|
||||
)
|
||||
stdout, stderr = p.communicate(input=stdin)
|
||||
LOG.info("Standard output [%s] and error output [%s] for command "
|
||||
"[%s]. ", stdout, stderr, actual_command_with_venv)
|
||||
if p.returncode != 0:
|
||||
raise RuntimeError('"{cmd}" returned {val}: {msg}'.format(
|
||||
cmd=' '.join(cmd), val=p.returncode, msg=stderr))
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
# 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
|
||||
|
||||
|
||||
@@ -139,15 +136,13 @@ 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=f'-s {service_id} 12 --start {date_iso}')[0]
|
||||
params='-s {} 12'.format(service_id))[0]
|
||||
mapping_id = resp['Mapping ID']
|
||||
self._mappings.append(mapping_id)
|
||||
self.assertEqual(resp['Service ID'], service_id)
|
||||
@@ -178,8 +173,6 @@ 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)
|
||||
@@ -192,8 +185,7 @@ class CkHashmapTest(base.BaseFunctionalTest):
|
||||
# Create mapping
|
||||
resp = self.runner(
|
||||
'hashmap mapping create',
|
||||
params=f'--field-id {field_id} 12 --value '
|
||||
f'testvalue --start {date_iso}')[0]
|
||||
params='--field-id {} 12 --value testvalue'.format(field_id))[0]
|
||||
mapping_id = resp['Mapping ID']
|
||||
self._mappings.append(service_id)
|
||||
self.assertEqual(resp['Field ID'], field_id)
|
||||
@@ -211,45 +203,6 @@ 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,9 +13,6 @@
|
||||
# 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
|
||||
|
||||
|
||||
@@ -26,12 +23,9 @@ 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=f"testscript "
|
||||
f"'return 0' --start {date_iso}")[0]
|
||||
'pyscript create', params="testscript 'return 0'")[0]
|
||||
script_id = resp['Script ID']
|
||||
self.assertEqual(resp['Name'], 'testscript')
|
||||
|
||||
@@ -43,9 +37,8 @@ class CkPyscriptTest(base.BaseFunctionalTest):
|
||||
# Update
|
||||
resp = self.runner(
|
||||
'pyscript update',
|
||||
params="-d 'return 1' {} --description "
|
||||
"desc".format(script_id))[0]
|
||||
self.assertEqual(resp['Script Description'], 'desc')
|
||||
params="-n newname -d 'return 1' {}".format(script_id))[0]
|
||||
self.assertEqual(resp['Name'], 'newname')
|
||||
self.assertEqual(resp['Script ID'], script_id)
|
||||
self.assertEqual(resp['Data'], 'return 1')
|
||||
|
||||
@@ -53,49 +46,13 @@ class CkPyscriptTest(base.BaseFunctionalTest):
|
||||
resp = self.runner('pyscript list')
|
||||
self.assertEqual(len(resp), 1)
|
||||
resp = resp[0]
|
||||
self.assertEqual(resp['Script Description'], 'desc')
|
||||
self.assertEqual(resp['Name'], 'newname')
|
||||
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):
|
||||
|
||||
|
||||
@@ -137,9 +137,9 @@ class CkDataframesTest(base.BaseFunctionalTest):
|
||||
super(CkDataframesTest, self).tearDown()
|
||||
|
||||
def test_dataframes_add_with_no_args(self):
|
||||
self.assertRaisesRegex(
|
||||
self.assertRaisesRegexp(
|
||||
RuntimeError,
|
||||
'error: the following arguments are required: datafile',
|
||||
'error: too few arguments',
|
||||
self.runner,
|
||||
'dataframes add',
|
||||
fmt='',
|
||||
@@ -162,11 +162,6 @@ class CkDataframesTest(base.BaseFunctionalTest):
|
||||
has_output=False,
|
||||
)
|
||||
|
||||
def test_dataframes_get(self):
|
||||
# TODO(jferrieu): functional tests will be added in another
|
||||
# patch for `dataframes get`
|
||||
pass
|
||||
|
||||
|
||||
class OSCDataframesTest(CkDataframesTest):
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from unittest import mock
|
||||
import mock
|
||||
|
||||
from cloudkittyclient import exc
|
||||
from cloudkittyclient.tests.unit.v1 import base
|
||||
@@ -110,10 +110,7 @@ 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',
|
||||
name='name', start="2024-01-01",
|
||||
end="2024-01-01",
|
||||
description="description")
|
||||
kwargs = dict(cost=2, value='value', field_id='field_id')
|
||||
body = dict(
|
||||
cost=kwargs.get('cost'),
|
||||
value=kwargs.get('value'),
|
||||
@@ -122,10 +119,6 @@ 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,8 +38,7 @@ class TestPyscripts(base.BaseAPIEndpointTestCase):
|
||||
self.assertRaises(exc.ArgumentRequired, self.pyscripts.get_script)
|
||||
|
||||
def test_create_script(self):
|
||||
kwargs = dict(name='name', data='data', start=None,
|
||||
end=None, description=None)
|
||||
kwargs = dict(name='name', data='data')
|
||||
self.pyscripts.create_script(**kwargs)
|
||||
self.api_client.post.assert_called_once_with(
|
||||
'/v1/rating/module_config/pyscripts/scripts/', json=kwargs)
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from collections import abc
|
||||
from unittest import mock
|
||||
import collections
|
||||
import mock
|
||||
|
||||
from cloudkittyclient.tests.unit.v1 import base
|
||||
from cloudkittyclient.v1 import report_cli
|
||||
@@ -57,7 +57,7 @@ class TestReportCli(base.BaseAPIEndpointTestCase):
|
||||
|
||||
assert len(result) == 2
|
||||
assert result[0] == ('Tenant ID', )
|
||||
assert isinstance(result[1], abc.Iterable)
|
||||
assert isinstance(result[1], collections.Iterable)
|
||||
|
||||
for res in result[1]:
|
||||
assert isinstance(res, abc.Iterable)
|
||||
assert isinstance(res, collections.Iterable)
|
||||
|
||||
@@ -12,10 +12,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from cloudkittyclient.tests import utils
|
||||
from cloudkittyclient.v2 import dataframes
|
||||
from cloudkittyclient.v2.rating import modules
|
||||
from cloudkittyclient.v2 import scope
|
||||
from cloudkittyclient.v2 import summary
|
||||
|
||||
@@ -28,4 +26,3 @@ class BaseAPIEndpointTestCase(utils.BaseTestCase):
|
||||
self.dataframes = dataframes.DataframesManager(self.api_client)
|
||||
self.scope = scope.ScopeManager(self.api_client)
|
||||
self.summary = summary.SummaryManager(self.api_client)
|
||||
self.rating = modules.RatingManager(self.api_client)
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
#
|
||||
import json
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from cloudkittyclient import exc
|
||||
from cloudkittyclient.tests.unit.v2 import base
|
||||
|
||||
@@ -151,22 +149,3 @@ class TestDataframes(base.BaseAPIEndpointTestCase):
|
||||
self.assertRaises(
|
||||
exc.ArgumentRequired,
|
||||
self.dataframes.add_dataframes)
|
||||
|
||||
def test_get_dataframes(self):
|
||||
self.dataframes.get_dataframes()
|
||||
self.api_client.get.assert_called_once_with('/v2/dataframes')
|
||||
|
||||
def test_get_dataframes_with_pagination_args(self):
|
||||
self.dataframes.get_dataframes(offset=10, limit=10)
|
||||
try:
|
||||
self.api_client.get.assert_called_once_with(
|
||||
'/v2/dataframes?limit=10&offset=10')
|
||||
except AssertionError:
|
||||
self.api_client.get.assert_called_once_with(
|
||||
'/v2/dataframes?offset=10&limit=10')
|
||||
|
||||
def test_get_dataframes_filters(self):
|
||||
self.dataframes.get_dataframes(
|
||||
filters=OrderedDict([('one', 'two'), ('three', 'four')]))
|
||||
self.api_client.get.assert_called_once_with(
|
||||
'/v2/dataframes?filters=one%3Atwo%2Cthree%3Afour')
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
# Copyright 2019 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from cloudkittyclient.tests.unit.v2 import base
|
||||
|
||||
|
||||
class TestRating(base.BaseAPIEndpointTestCase):
|
||||
|
||||
def test_get_modules(self):
|
||||
self.rating.get_module()
|
||||
self.api_client.get.assert_called_once_with('/v2/rating/modules')
|
||||
|
||||
def test_get_one_module(self):
|
||||
self.rating.get_module(module_id="moduleidtest")
|
||||
self.api_client.get.assert_called_once_with(
|
||||
'/v2/rating/modules/moduleidtest')
|
||||
|
||||
def test_update_one_module(self):
|
||||
self.rating.update_module(module_id="moduleidtest",
|
||||
enabled=False, priority=42)
|
||||
self.api_client.put.assert_called_once_with(
|
||||
'/v2/rating/modules/moduleidtest',
|
||||
json={
|
||||
'enabled': False,
|
||||
'priority': 42,
|
||||
})
|
||||
@@ -12,13 +12,12 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from unittest import mock
|
||||
|
||||
import fixtures
|
||||
import testtools
|
||||
|
||||
from keystoneauth1 import adapter
|
||||
from keystoneauth1 import session
|
||||
import mock
|
||||
|
||||
|
||||
class BaseTestCase(testtools.TestCase):
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@@ -12,11 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
import inspect
|
||||
|
||||
import pbr.version
|
||||
|
||||
from keystoneauth1.exceptions import http
|
||||
from oslo_utils import timeutils
|
||||
|
||||
|
||||
@@ -58,39 +56,3 @@ def list_to_cols(list_obj, cols):
|
||||
for item in list_obj:
|
||||
values.append(dict_to_cols(item, cols))
|
||||
return values
|
||||
|
||||
|
||||
def http_error_formatter(func):
|
||||
"""This decorator catches Http Errors and re-formats them"""
|
||||
|
||||
def wrap(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except http.HttpError as e:
|
||||
raise http.HttpError(message=e.response.text,
|
||||
http_status=e.http_status)
|
||||
|
||||
return wrap
|
||||
|
||||
|
||||
def format_http_errors(ignore):
|
||||
"""Applies ``http_error_formatter`` to all methods of a class.
|
||||
|
||||
:param ignore: List of function names to ignore
|
||||
:type ignore: iterable
|
||||
"""
|
||||
|
||||
def wrap(cls):
|
||||
def predicate(item):
|
||||
# This avoids decorating functions of parent classes
|
||||
return (inspect.isfunction(item)
|
||||
and item.__name__ not in ignore
|
||||
and not item.__name__.startswith('_')
|
||||
and cls.__name__ in item.__qualname__)
|
||||
|
||||
for name, func in inspect.getmembers(cls, predicate):
|
||||
setattr(cls, name, http_error_formatter(func))
|
||||
|
||||
return cls
|
||||
|
||||
return wrap
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
import uuid
|
||||
|
||||
from cloudkittyclient.common import base
|
||||
from cloudkittyclient import exc
|
||||
|
||||
@@ -173,16 +171,8 @@ 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:
|
||||
if not kwargs.get('cost'):
|
||||
raise exc.ArgumentRequired("'cost' argument is required")
|
||||
if not kwargs.get('value'):
|
||||
if not kwargs.get('service_id'):
|
||||
@@ -206,16 +196,6 @@ 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()
|
||||
|
||||
@@ -393,7 +373,7 @@ class HashmapManager(base.BaseManager):
|
||||
:type level: str
|
||||
"""
|
||||
for arg in ['cost', 'level']:
|
||||
if kwargs.get(arg) is None:
|
||||
if not kwargs.get(arg):
|
||||
raise exc.ArgumentRequired(
|
||||
"'{}' argument is required".format(arg))
|
||||
if not kwargs.get('service_id') and not kwargs.get('field_id'):
|
||||
|
||||
@@ -257,10 +257,6 @@ 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):
|
||||
@@ -279,11 +275,6 @@ 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
|
||||
|
||||
|
||||
@@ -330,11 +321,6 @@ 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,22 +50,13 @@ 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'],
|
||||
start=kwargs.get('start'),
|
||||
end=kwargs.get('end'),
|
||||
description=kwargs.get('description'))
|
||||
body = dict(name=kwargs['name'], data=kwargs['data'])
|
||||
return self.api_client.post(url, json=body).json()
|
||||
|
||||
def update_script(self, **kwargs):
|
||||
@@ -77,17 +68,11 @@ 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', 'start', 'end', 'description'):
|
||||
for key in ('name', 'data'):
|
||||
if kwargs.get(key):
|
||||
script[key] = kwargs[key]
|
||||
script.pop('checksum', None)
|
||||
|
||||
@@ -26,9 +26,6 @@ class BaseScriptCli(lister.Lister):
|
||||
('script_id', 'Script ID'),
|
||||
('checksum', 'Checksum'),
|
||||
('data', 'Data'),
|
||||
('start', 'Script Start Date'),
|
||||
('end', 'Script End Date'),
|
||||
('description', 'Script Description')
|
||||
]
|
||||
|
||||
|
||||
@@ -85,10 +82,6 @@ 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
|
||||
|
||||
|
||||
@@ -114,10 +107,6 @@ 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
|
||||
|
||||
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
#
|
||||
from cloudkittyclient.v1 import client
|
||||
from cloudkittyclient.v2 import dataframes
|
||||
from cloudkittyclient.v2.rating import modules
|
||||
from cloudkittyclient.v2 import reprocessing
|
||||
from cloudkittyclient.v2 import scope
|
||||
from cloudkittyclient.v2 import summary
|
||||
|
||||
@@ -42,5 +40,3 @@ class Client(client.Client):
|
||||
self.dataframes = dataframes.DataframesManager(self.api_client)
|
||||
self.scope = scope.ScopeManager(self.api_client)
|
||||
self.summary = summary.SummaryManager(self.api_client)
|
||||
self.rating = modules.RatingManager(self.api_client)
|
||||
self.reprocessing = reprocessing.ReprocessingManager(self.api_client)
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
# under the License.
|
||||
#
|
||||
import json
|
||||
import six
|
||||
|
||||
from cloudkittyclient.common import base
|
||||
from cloudkittyclient import exc
|
||||
@@ -35,7 +36,7 @@ class DataframesManager(base.BaseManager):
|
||||
if not dataframes:
|
||||
raise exc.ArgumentRequired("'dataframes' argument is required")
|
||||
|
||||
if not isinstance(dataframes, str):
|
||||
if not isinstance(dataframes, six.string_types):
|
||||
try:
|
||||
dataframes = json.dumps(dataframes)
|
||||
except TypeError:
|
||||
@@ -48,30 +49,3 @@ class DataframesManager(base.BaseManager):
|
||||
url,
|
||||
data=dataframes,
|
||||
)
|
||||
|
||||
def get_dataframes(self, **kwargs):
|
||||
"""Returns a paginated list of DataFrames.
|
||||
|
||||
This support filters and datetime framing.
|
||||
|
||||
:param offset: Index of the first dataframe that should be returned.
|
||||
:type offset: int
|
||||
:param limit: Maximal number of dataframes to return.
|
||||
:type limit: int
|
||||
:param filters: Optional dict of filters to select data on.
|
||||
:type filters: dict
|
||||
:param begin: Start of the period to gather data from
|
||||
:type begin: datetime.datetime
|
||||
:param end: End of the period to gather data from
|
||||
:type end: datetime.datetime
|
||||
"""
|
||||
kwargs['filters'] = ','.join(
|
||||
'{}:{}'.format(k, v) for k, v in
|
||||
(kwargs.get('filters', None) or {}).items()
|
||||
)
|
||||
|
||||
authorized_args = [
|
||||
'offset', 'limit', 'filters', 'begin', 'end']
|
||||
|
||||
url = self.get_url(None, kwargs, authorized_args=authorized_args)
|
||||
return self.api_client.get(url).json()
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
import argparse
|
||||
|
||||
from cliff import command
|
||||
from cliff import lister
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from cloudkittyclient import utils
|
||||
|
||||
@@ -42,75 +40,3 @@ class CliDataframesAdd(command.Command):
|
||||
utils.get_client_from_osc(self).dataframes.add_dataframes(
|
||||
dataframes=dataframes,
|
||||
)
|
||||
|
||||
|
||||
class CliDataframesGet(lister.Lister):
|
||||
"""Get dataframes from the storage backend."""
|
||||
columns = [
|
||||
('begin', 'Begin'),
|
||||
('end', 'End'),
|
||||
('metric', 'Metric Type'),
|
||||
('unit', 'Unit'),
|
||||
('qty', 'Quantity'),
|
||||
('price', 'Price'),
|
||||
('groupby', 'Group By'),
|
||||
('metadata', 'Metadata'),
|
||||
]
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliDataframesGet, self).get_parser(prog_name)
|
||||
|
||||
def filter_(elem):
|
||||
if len(elem.split(':')) != 2:
|
||||
raise TypeError
|
||||
return str(elem)
|
||||
|
||||
parser.add_argument('--offset', type=int, default=0,
|
||||
help='Index of the first dataframe')
|
||||
parser.add_argument('--limit', type=int, default=100,
|
||||
help='Maximal number of dataframes')
|
||||
parser.add_argument('--filter', type=filter_, action='append',
|
||||
help="Optional filter, in 'key:value' format. Can "
|
||||
"be specified several times.")
|
||||
parser.add_argument('-b', '--begin', type=timeutils.parse_isotime,
|
||||
help="Start of the period to query, in iso8601 "
|
||||
"format. Example: 2019-05-01T00:00:00Z.")
|
||||
parser.add_argument('-e', '--end', type=timeutils.parse_isotime,
|
||||
help="End of the period to query, in iso8601 "
|
||||
"format. Example: 2019-06-01T00:00:00Z.")
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
filters = dict(elem.split(':') for elem in (parsed_args.filter or []))
|
||||
|
||||
dataframes = utils.get_client_from_osc(self).dataframes.get_dataframes(
|
||||
offset=parsed_args.offset,
|
||||
limit=parsed_args.limit,
|
||||
begin=parsed_args.begin,
|
||||
end=parsed_args.end,
|
||||
filters=filters,
|
||||
).get('dataframes', [])
|
||||
|
||||
def format_(d):
|
||||
return ' '.join([
|
||||
'{}="{}"'.format(k, v) for k, v in (d or {}).items()])
|
||||
|
||||
values = []
|
||||
for df in dataframes:
|
||||
period = df['period']
|
||||
usage = df['usage']
|
||||
for metric_type, points in usage.items():
|
||||
for point in points:
|
||||
values.append([
|
||||
period['begin'],
|
||||
period['end'],
|
||||
metric_type,
|
||||
point['vol']['unit'],
|
||||
point['vol']['qty'],
|
||||
point['rating']['price'],
|
||||
format_(point.get('groupby', {})),
|
||||
format_(point.get('metadata', {})),
|
||||
])
|
||||
|
||||
return [col[1] for col in self.columns], values
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
|
||||
# Copyright 2019 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient import exc
|
||||
from cloudkittyclient.v1.client import rating
|
||||
|
||||
|
||||
class RatingManager(rating.RatingManager):
|
||||
"""Class used to handle /v2/rating/modules endpoint"""
|
||||
|
||||
url = '/v2/rating/modules'
|
||||
|
||||
def get_module(self, **kwargs):
|
||||
"""Returns the given module.
|
||||
|
||||
If module_id is not specified, returns the list of loaded modules.
|
||||
|
||||
:param module_id: ID of the module on which you want information.
|
||||
:type module_id: str
|
||||
"""
|
||||
module_id = kwargs.get('module_id', None)
|
||||
if module_id is not None:
|
||||
url = "{}/{}".format(self.url, module_id)
|
||||
else:
|
||||
url = self.url
|
||||
return self.api_client.get(url).json()
|
||||
|
||||
def update_module(self, **kwargs):
|
||||
"""Update the given module.
|
||||
|
||||
:param module_id: Id of the module to update.
|
||||
:type module_id: str
|
||||
:param enabled: Set to True to enable the module, False to disable it.
|
||||
:type enabled: bool
|
||||
:param priority: New priority of the module.
|
||||
:type priority: int
|
||||
"""
|
||||
if not kwargs.get('module_id', None):
|
||||
raise exc.ArgumentRequired("'module_id' argument is required.")
|
||||
mutable_fields = ['enabled', 'priority']
|
||||
changes = {}
|
||||
for key, value in kwargs.items():
|
||||
if value is not None and key in mutable_fields:
|
||||
changes[key] = value
|
||||
self.api_client.put("{}/{}".format(self.url, kwargs['module_id']),
|
||||
json=changes)
|
||||
return self.get_module(**kwargs)
|
||||
@@ -1,29 +0,0 @@
|
||||
# Copyright 2019 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cliff import lister
|
||||
|
||||
from cloudkittyclient import utils
|
||||
|
||||
|
||||
class CliModuleList(lister.Lister):
|
||||
"""Get loaded rating modules list"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliModuleList, self).get_parser(prog_name)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
resp = utils.get_client_from_osc(self).ratingmodules.get_modules_list()
|
||||
return resp['modules']
|
||||
@@ -1,76 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient.common import base
|
||||
from cloudkittyclient import exc
|
||||
|
||||
|
||||
class ReprocessingManager(base.BaseManager):
|
||||
"""Class used to handle /v2/task/reprocesses endpoint"""
|
||||
|
||||
url = '/v2/task/reprocesses'
|
||||
|
||||
def get_reprocessing_tasks(self, offset=0, limit=100, scope_ids=[],
|
||||
order="DESC", **kwargs):
|
||||
"""Returns a paginated list of reprocessing tasks.
|
||||
|
||||
Some optional filters can be provided.
|
||||
|
||||
:param offset: Index of the first reprocessing task
|
||||
that should be returned.
|
||||
:type offset: int
|
||||
:param limit: Maximal number of reprocessing task to return.
|
||||
:type limit: int
|
||||
:param scope_ids: Optional scope_ids to filter on.
|
||||
:type scope_ids: list of str
|
||||
:param order: Optional order (asc/desc) to sort tasks.
|
||||
:type order: str
|
||||
"""
|
||||
kwargs = kwargs or {}
|
||||
kwargs['order'] = order
|
||||
kwargs['offset'] = offset
|
||||
kwargs['limit'] = limit
|
||||
|
||||
authorized_args = ['offset', 'limit', 'order']
|
||||
url = self.get_url(None, kwargs, authorized_args=authorized_args)
|
||||
|
||||
if scope_ids:
|
||||
url += "&scope_ids=%s" % (",".join(scope_ids))
|
||||
return self.api_client.get(url).json()
|
||||
|
||||
def post_reprocessing_task(self, scope_ids=[], start=None, end=None,
|
||||
reason=None, **kwargs):
|
||||
"""Creates a reprocessing task
|
||||
|
||||
:param start: The start date of the reprocessing task
|
||||
:type start: timeutils.parse_isotime
|
||||
:param end: The end date of the reprocessing task
|
||||
:type end: timeutils.parse_isotime
|
||||
:param scope_ids: The scope IDs to create the reprocessing task to
|
||||
:type scope_ids: list of str
|
||||
:param reason: The reason for the reprocessing task
|
||||
:type reason: str
|
||||
"""
|
||||
|
||||
if not scope_ids:
|
||||
raise exc.ArgumentRequired("'scope-id' argument is required")
|
||||
|
||||
body = dict(
|
||||
scope_ids=scope_ids,
|
||||
start_reprocess_time=start,
|
||||
end_reprocess_time=end,
|
||||
reason=reason
|
||||
)
|
||||
|
||||
body = dict(filter(lambda elem: bool(elem[1]), body.items()))
|
||||
|
||||
return self.api_client.post(self.url, json=body).json()
|
||||
@@ -1,95 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cliff import lister
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from cloudkittyclient import utils
|
||||
|
||||
|
||||
class CliReprocessingTasksGet(lister.Lister):
|
||||
"""Get reprocessing tasks."""
|
||||
|
||||
result_columns = [
|
||||
('scope_id', 'Scope ID'),
|
||||
('reason', 'Reason'),
|
||||
('start_reprocess_time', 'Start reprocessing time'),
|
||||
('end_reprocess_time', 'End reprocessing time'),
|
||||
('current_reprocess_time', 'Current reprocessing time'),
|
||||
]
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliReprocessingTasksGet, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('--scope-id', type=str, default=[],
|
||||
action='append', help='Optional filter on scope '
|
||||
'IDs. This filter can be '
|
||||
'used multiple times.')
|
||||
|
||||
parser.add_argument('--offset', type=int, default=0,
|
||||
help='Index of the first scope. '
|
||||
'The default value is 0.')
|
||||
parser.add_argument('--limit', type=int, default=100,
|
||||
help='Maximal number of scopes. '
|
||||
'The default value is 100.')
|
||||
parser.add_argument('--order', type=str, default="DESC",
|
||||
help='The order to sort the reprocessing tasks '
|
||||
'(ASC or DESC).')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
resp = utils.get_client_from_osc(
|
||||
self).reprocessing.get_reprocessing_tasks(
|
||||
scope_ids=parsed_args.scope_id, offset=parsed_args.offset,
|
||||
limit=parsed_args.limit, order=parsed_args.order
|
||||
)
|
||||
|
||||
values = utils.list_to_cols(resp['results'], self.result_columns)
|
||||
return [col[1] for col in self.result_columns], values
|
||||
|
||||
|
||||
class CliReprocessingTasksPost(lister.Lister):
|
||||
"""Create a reprocessing task."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliReprocessingTasksPost, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('--scope-id', type=str, default=[],
|
||||
action='append',
|
||||
help='The scope IDs to reprocess. This option can '
|
||||
'be used multiple times to execute the same '
|
||||
'reprocessing task for different scope IDs.')
|
||||
|
||||
parser.add_argument('--start-reprocess-time',
|
||||
type=timeutils.parse_isotime,
|
||||
help="Start of the period to reprocess in ISO8601 "
|
||||
"format. Example: '2022-04-22T00:00:00Z.'")
|
||||
|
||||
parser.add_argument('--end-reprocess-time',
|
||||
type=timeutils.parse_isotime,
|
||||
help="End of the period to reprocess in ISO8601 "
|
||||
"format. Example: '2022-04-22T00:00:00Z.'")
|
||||
|
||||
parser.add_argument('--reason', type=str,
|
||||
help="The reason to create the reprocessing task.")
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
return ["Result"], utils.get_client_from_osc(
|
||||
self).reprocessing.post_reprocessing_task(
|
||||
scope_ids=parsed_args.scope_id,
|
||||
start=parsed_args.start_reprocess_time,
|
||||
end=parsed_args.end_reprocess_time,
|
||||
reason=parsed_args.reason
|
||||
)
|
||||
@@ -12,8 +12,6 @@
|
||||
# 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
|
||||
|
||||
@@ -102,42 +100,3 @@ class ScopeManager(base.BaseManager):
|
||||
|
||||
url = self.get_url(None, kwargs)
|
||||
return self.api_client.put(url, json=body)
|
||||
|
||||
def update_scope(self, **kwargs):
|
||||
"""Update storage scope
|
||||
|
||||
The `scope_id field` is mandatory, and all other are optional. Only the
|
||||
attributes sent will be updated. The attributes that are not sent will
|
||||
not be changed in the backend.
|
||||
|
||||
:param collector: collector to be used by the scope.
|
||||
:type collector: str
|
||||
:param fetcher: fetcher to be used by the scope.
|
||||
:type fetcher: str
|
||||
:param scope_id: Mandatory scope_id to update.
|
||||
:type scope_id: str
|
||||
:param scope_key: scope_key to be used by the scope.
|
||||
:type scope_key: str
|
||||
:param active: Indicates if the scope is active or not
|
||||
:type active: str
|
||||
"""
|
||||
|
||||
if not kwargs.get('scope_id'):
|
||||
raise exc.ArgumentRequired("'scope_id' argument is required")
|
||||
|
||||
body = dict(
|
||||
scope_id=kwargs.get('scope_id'),
|
||||
scope_key=kwargs.get('scope_key'),
|
||||
collector=kwargs.get('collector'),
|
||||
fetcher=kwargs.get('fetcher')
|
||||
)
|
||||
|
||||
if kwargs.get('active'):
|
||||
body['active'] = 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()
|
||||
|
||||
@@ -96,36 +96,3 @@ class CliScopeStateReset(command.Command):
|
||||
all_scopes=parsed_args.all_scopes,
|
||||
state=parsed_args.state,
|
||||
)
|
||||
|
||||
|
||||
class CliPatchScope(command.Command):
|
||||
"""Update scope attributes."""
|
||||
|
||||
info_columns = [
|
||||
('scope_key', 'Scope Key'),
|
||||
('collector', 'Collector'),
|
||||
('fetcher', 'Fetcher'),
|
||||
('active', 'Active'),
|
||||
]
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliPatchScope, self).get_parser(prog_name)
|
||||
|
||||
for col in self.info_columns:
|
||||
parser.add_argument(
|
||||
'--' + col[0].replace('_', '-'), type=str,
|
||||
help='Optional filter on ' + col[1])
|
||||
|
||||
parser.add_argument(
|
||||
'-id', '--scope-id', required=True, type=str,
|
||||
help="The scope ID to be updated")
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
return utils.get_client_from_osc(self).scope.update_scope(
|
||||
collector=parsed_args.collector,
|
||||
fetcher=parsed_args.fetcher,
|
||||
scope_id=parsed_args.scope_id,
|
||||
scope_key=parsed_args.scope_key,
|
||||
active=parsed_args.active)
|
||||
|
||||
@@ -36,8 +36,6 @@ class SummaryManager(base.BaseManager):
|
||||
:type begin: datetime.datetime
|
||||
:param end: End of the period to gather data from
|
||||
:type end: datetime.datetime
|
||||
:param response_format: Either 'table' or 'object' defaults to 'table'
|
||||
:type response_format: str
|
||||
"""
|
||||
if 'groupby' in kwargs.keys() and isinstance(kwargs['groupby'], list):
|
||||
kwargs['groupby'] = ','.join(kwargs['groupby'])
|
||||
@@ -48,8 +46,7 @@ class SummaryManager(base.BaseManager):
|
||||
)
|
||||
|
||||
authorized_args = [
|
||||
'offset', 'limit', 'filters', 'groupby', 'begin', 'end',
|
||||
'response_format']
|
||||
'offset', 'limit', 'filters', 'groupby', 'begin', 'end']
|
||||
|
||||
url = self.get_url(None, kwargs, authorized_args=authorized_args)
|
||||
return self.api_client.get(url).json()
|
||||
|
||||
@@ -35,20 +35,10 @@ 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. 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.')
|
||||
'specified several times')
|
||||
parser.add_argument('--filter', type=filter_, action='append',
|
||||
help="Optional filter, in 'key:value' format. Can "
|
||||
"be specified several times. It is also "
|
||||
"possible to filter data using the group by "
|
||||
"values. However, one needs to group by as "
|
||||
"well; for instance, if one wants to filter "
|
||||
"by resource id (id), then we need to group "
|
||||
"by id via the option '-g id'.")
|
||||
"be specified several times.")
|
||||
parser.add_argument('-b', '--begin', type=timeutils.parse_isotime,
|
||||
help="Start of the period to query, in iso8601 "
|
||||
"format. Example: 2019-05-01T00:00:00Z.")
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
openstackdocstheme>=1.30.0 # Apache-2.0
|
||||
sphinx>=1.8.0,!=2.1.0 # BSD
|
||||
sphinxcontrib-svg2pdfconverter>=0.1.0 # BSD
|
||||
sphinx>=1.6.2,!=1.6.6,!=1.6.7,<2.0.0;python_version=='2.7' # BSD
|
||||
sphinx>=1.6.2,!=1.6.6,!=1.6.7,!=2.1.0;python_version>='3.4' # BSD
|
||||
reno>=2.5.0 # Apache-2.0
|
||||
cliff>=2.11.0 # Apache-2.0
|
||||
|
||||
@@ -5,17 +5,18 @@ CLI Reference
|
||||
V1 Client
|
||||
=========
|
||||
|
||||
.. autoprogram-cliff:: cloudkittyclient_v1
|
||||
.. autoprogram-cliff:: cloudkittyclient.v1
|
||||
:application: cloudkitty
|
||||
|
||||
|
||||
V2 Client
|
||||
=========
|
||||
|
||||
.. autoprogram-cliff:: cloudkittyclient_v2
|
||||
.. autoprogram-cliff:: cloudkittyclient.v2
|
||||
:command: dataframes add
|
||||
|
||||
.. autoprogram-cliff:: cloudkittyclient_v2
|
||||
.. autoprogram-cliff:: cloudkittyclient.v2
|
||||
:command: scope state get
|
||||
|
||||
.. autoprogram-cliff:: cloudkittyclient_v2
|
||||
.. autoprogram-cliff:: cloudkittyclient.v2
|
||||
:command: summary get
|
||||
|
||||
@@ -23,11 +23,8 @@ extensions = [
|
||||
'cliff.sphinxext',
|
||||
'sphinx.ext.autodoc',
|
||||
'openstackdocstheme',
|
||||
'sphinxcontrib.rsvgconverter',
|
||||
]
|
||||
|
||||
autoprogram_cliff_application = 'cloudkitty'
|
||||
|
||||
autoprogram_cliff_ignored = [
|
||||
"--format", "--column", "--max-width", "--fit-width", "--print-empty",
|
||||
"--format-config-file", "--noindent", "--quote", "--sort-column",
|
||||
@@ -44,12 +41,13 @@ source_suffix = '.rst'
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'python-cloudkittyclient'
|
||||
copyright = '2017, OpenStack Foundation'
|
||||
project = u'python-cloudkittyclient'
|
||||
copyright = u'2017, OpenStack Foundation'
|
||||
|
||||
# openstackdocstheme options
|
||||
openstackdocs_repo_name = 'openstack/python-cloudkittyclient'
|
||||
openstackdocs_use_storyboard = True
|
||||
repository_name = 'openstack/python-cloudkittyclient'
|
||||
bug_project = 'cloudkitty'
|
||||
bug_tag = 'python-cloudkittyclient'
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
add_function_parentheses = True
|
||||
@@ -59,7 +57,7 @@ add_function_parentheses = True
|
||||
add_module_names = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'native'
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# -- Options for HTML output --------------------------------------------------
|
||||
|
||||
@@ -76,30 +74,14 @@ html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
# Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664
|
||||
latex_use_xindy = False
|
||||
|
||||
latex_domain_indices = False
|
||||
|
||||
latex_elements = {
|
||||
'makeindex': '',
|
||||
'printindex': '',
|
||||
'preamble': r'\setcounter{tocdepth}{3}',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass
|
||||
# [howto/manual]).
|
||||
# NOTE: Specify toctree_only=True for a better document structure of
|
||||
# the generated PDF file.
|
||||
latex_documents = [
|
||||
('index',
|
||||
'doc-%s.tex' % project,
|
||||
'%s Documentation' % project,
|
||||
'OpenStack Foundation', 'howto', True),
|
||||
'%s.tex' % project,
|
||||
u'%s Documentation' % project,
|
||||
u'OpenStack Foundation', 'manual'),
|
||||
]
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
|
||||
5
doc/source/contributor.rst
Normal file
5
doc/source/contributor.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
============
|
||||
Contributing
|
||||
============
|
||||
|
||||
.. include:: ../../CONTRIBUTING.rst
|
||||
@@ -1,47 +0,0 @@
|
||||
============================
|
||||
So You Want to Contribute...
|
||||
============================
|
||||
For general information on contributing to OpenStack, please check out the
|
||||
`contributor guide <https://docs.openstack.org/contributors/>`_ to get started.
|
||||
It covers all the basics that are common to all OpenStack projects: the accounts
|
||||
you need, the basics of interacting with our Gerrit review system, how we
|
||||
communicate as a community, etc.
|
||||
Below will cover the more project specific information you need to get started
|
||||
with python-cloudkittyclient.
|
||||
|
||||
Communication
|
||||
~~~~~~~~~~~~~
|
||||
* IRC channel #cloudkitty at `OFTC <http://oftc.net>`_
|
||||
* Mailing list (prefix subjects with ``[cloudkitty]`` for faster responses)
|
||||
http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss
|
||||
|
||||
Contacting the Core Team
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Please refer the `python-cloudkittyclient Core Team
|
||||
<https://review.opendev.org/admin/groups/4ac765c35f985b3ad9226da07fdcc205c1ce4fe1,members>`_ contacts.
|
||||
|
||||
New Feature Planning
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
CloudKitty features are tracked on `Storyboard <https://storyboard.openstack.org/#!/project/895>`_.
|
||||
|
||||
Task Tracking
|
||||
~~~~~~~~~~~~~
|
||||
We track our tasks in `Storyboard <https://storyboard.openstack.org/#!/project/895>`_.
|
||||
If you're looking for some smaller, easier work item to pick up and get started
|
||||
on, search for the 'low-hanging-fruit' tag.
|
||||
|
||||
Reporting a Bug
|
||||
~~~~~~~~~~~~~~~
|
||||
You found an issue and want to make sure we are aware of it? You can do so on
|
||||
`StoryBoard <https://storyboard.openstack.org/#!/project/895>`_.
|
||||
|
||||
Getting Your Patch Merged
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
All changes proposed to the python-cloudkittyclient project require one or two +2 votes
|
||||
from python-cloudkittyclient core reviewers before one of the core reviewers can approve
|
||||
patch by giving ``Workflow +1`` vote.
|
||||
|
||||
Project Team Lead Duties
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
All common PTL duties are enumerated in the `PTL guide
|
||||
<https://docs.openstack.org/project-team-guide/ptl.html>`_.
|
||||
@@ -12,19 +12,10 @@ Contents:
|
||||
|
||||
install
|
||||
usage
|
||||
contributor
|
||||
cli_reference
|
||||
api_reference/index
|
||||
|
||||
For Contributors
|
||||
================
|
||||
|
||||
* If you are a new contributor to python-cloudkittyclient please refer: :doc:`contributor/contributing`
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
contributor/contributing
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
|
||||
@@ -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 2. You can specify which API
|
||||
v2 API endpoints. The default API version is 1. You can specify which API
|
||||
version you want to use via a CLI option:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
# EITHER
|
||||
cloudkitty --os-rating-api-version 1 module list
|
||||
cloudkitty --os-rating-api-version 2 summary get
|
||||
|
||||
# OR
|
||||
export OS_RATING_API_VERSION=1
|
||||
cloudkitty module list
|
||||
export OS_RATING_API_VERSION=2
|
||||
cloudkitty summary get
|
||||
|
||||
Again, the option can also be provided to the OSC plugin, both via the CLI
|
||||
flag or the environment variable.
|
||||
@@ -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('2', auth=auth)
|
||||
>>> client = ck_client.Client('1', 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('2', session=ck_session)
|
||||
>>> c = ck_client.Client('1', 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(
|
||||
'2', auth=auth, insecure=False, cacert='/path/to/ca')
|
||||
'1', auth=auth, insecure=False, cacert='/path/to/ca')
|
||||
|
||||
|
||||
If you want to use the v1 API, you have to specify it at client instanciation
|
||||
If you want to use the v2 API, you have to specify it at client instanciation
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
c = ck_client.Client('1', session=session)
|
||||
c = ck_client.Client('2', session=session)
|
||||
|
||||
When using the ``cloudkitty`` CLI client with keystone authentication, the
|
||||
auth plugin to use should automagically be detected. If not, you can specify
|
||||
the auth plugin to use with ``--os-auth-type/--os-auth-plugin``::
|
||||
|
||||
$ cloudkitty --debug --os-auth-type cloudkitty-noauth summary get
|
||||
+---------------------------+---------------------------+------------+-------------------+
|
||||
| Begin | End | Qty | Rate |
|
||||
+---------------------------+---------------------------+------------+-------------------+
|
||||
| 2025-12-01T00:00:00+01:00 | 2026-01-01T00:00:00+01:00 | 21662194.0 | 3618130.211340219 |
|
||||
+---------------------------+---------------------------+------------+-------------------+
|
||||
+------------+---------------+------------+---------------------+---------------------+
|
||||
| Project ID | Resource Type | Rate | Begin Time | End Time |
|
||||
+------------+---------------+------------+---------------------+---------------------+
|
||||
| ALL | ALL | 1676.95499 | 2018-03-01T00:00:00 | 2018-04-01T00:00:00 |
|
||||
+------------+---------------+------------+---------------------+---------------------+
|
||||
|
||||
|
||||
CSV report generation
|
||||
|
||||
24
lower-constraints.txt
Normal file
24
lower-constraints.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
# requirements
|
||||
pbr==2.0.0 # Apache-2.0
|
||||
cliff==2.11.0 # Apache-2.0
|
||||
keystoneauth1==3.4.0 # Apache-2.0
|
||||
oslo.utils==3.35 # Apache-2.0
|
||||
oslo.log==3.36 # Apache-2.0
|
||||
PyYAML==3.12 # MIT
|
||||
jsonpath-rw-ext==1.0 # Apache-2.0
|
||||
six==1.11 # MIT
|
||||
os-client-config==1.29.0 # Apache-2.0
|
||||
osc-lib==1.12.1 # Apache-2.0
|
||||
|
||||
# test-requirements.txt
|
||||
coverage==4.0 # Apache-2.0
|
||||
python-subunit==0.0.18 # Apache-2.0/BSD
|
||||
oslotest==1.10.0 # Apache-2.0
|
||||
stestr==2.0 # Apache-2.0
|
||||
mock==2.0 # BSD
|
||||
python-openstackclient==3.14 # Apache-2.0
|
||||
|
||||
# doc/requirements.txt
|
||||
openstackdocstheme==1.30.0 # Apache-2.0
|
||||
sphinx==1.6.2 # BSD
|
||||
reno==2.5.0 # Apache2
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Introduce the patch scope API in the CLI. The command "rating scope
|
||||
patch" is added to the OpenStack CLI with this patch, and the command
|
||||
"scope patch" is added to the CloudKitty python client.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Introduce reprocessing task API in the CLI. The following new commands
|
||||
are added to the OpenStack CLI "rating tasks reprocessing get" and
|
||||
"rating tasks reprocessing create". For CloudKitty CLI, we added the
|
||||
following new commands "tasks reprocessing get" and "tasks reprocessing
|
||||
create". Both command sets work in a similar fashion, but one is
|
||||
targetting the OpenStack CLI integration, whereas the other is
|
||||
targetting CloudKitty client only.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Support for the ``GET /v2/dataframes`` endpoint has been added
|
||||
to the client. A new ``dataframes get`` CLI command is also available.
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
Python 2.7 support has been dropped. The last release of
|
||||
``cloudkittyclient`` to support python 2.7 is OpenStack Train (3.1.0).
|
||||
The minimum version of Python now supported by ``cloudkittyclient``
|
||||
is Python 3.6.
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Fix `create_threshold` method when using cost as 0.
|
||||
When using 0 as the cost, the `create_threshold` method
|
||||
throws an exception. That happens because 0 (zero) is evaluated
|
||||
to False. Therefore, we need to change the validation method to
|
||||
check if the values are None.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Fixed a bug where creating a reprocessing task would fail due to sending a
|
||||
POST request to the wrong endpoint.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Fixes creation of hashmap mappings with a zero cost.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
Support for Python 3.9 has been removed. Now Python 3.10 is the minimum
|
||||
version supported.
|
||||
@@ -1,6 +0,0 @@
|
||||
===========================
|
||||
2023.1 Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: unmaintained/2023.1
|
||||
@@ -1,6 +0,0 @@
|
||||
===========================
|
||||
2023.2 Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/2023.2
|
||||
@@ -1,6 +0,0 @@
|
||||
===========================
|
||||
2024.1 Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: unmaintained/2024.1
|
||||
@@ -1,6 +0,0 @@
|
||||
===========================
|
||||
2024.2 Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/2024.2
|
||||
@@ -1,6 +0,0 @@
|
||||
===========================
|
||||
2025.1 Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/2025.1
|
||||
@@ -1,6 +0,0 @@
|
||||
===========================
|
||||
2025.2 Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/2025.2
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# CloudKitty Client Release Notes documentation build configuration file.
|
||||
# Cloudkitty Release Notes documentation build configuration file.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
@@ -42,8 +42,8 @@ source_suffix = '.rst'
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'CloudKitty Client Release Notes'
|
||||
copyright = '2016, CloudKitty developers'
|
||||
project = u'Cloudkitty Client Release Notes'
|
||||
copyright = u'2016, Cloudkitty developers'
|
||||
|
||||
# Release notes are version independent.
|
||||
# The short X.Y version.
|
||||
@@ -82,7 +82,7 @@ exclude_patterns = []
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'native'
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
@@ -173,7 +173,7 @@ html_static_path = ['_static']
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'CloudKittyClientReleaseNotestdoc'
|
||||
htmlhelp_basename = 'CloudkittyReleaseNotestdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
@@ -193,9 +193,8 @@ latex_elements = {
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'PythonCloudKittyClient.tex',
|
||||
'CloudKitty Client Release Notes Documentation',
|
||||
'CloudKitty developers', 'manual'),
|
||||
('index', 'PythonCloudkitty.tex', u'Cloudkitty Release Notes Documentation',
|
||||
u'Cloudkitty developers', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
@@ -225,8 +224,8 @@ latex_documents = [
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'cloudkittyclient',
|
||||
'CloudKitty Client Release Notes Documentation',
|
||||
['CloudKitty developers'], 1)
|
||||
u'Cloudkitty Client Release Notes Documentation',
|
||||
[u'Cloudkitty developers'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
@@ -240,8 +239,8 @@ man_pages = [
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'cloudkittyclient',
|
||||
'CloudKitty Client Release Notes Documentation',
|
||||
'CloudKitty Client developers', 'CloudKittyClient',
|
||||
u'Cloudkitty Client Release Notes Documentation',
|
||||
u'Cloudkitty Client developers', 'Cloudkittyclient',
|
||||
'One line description of project.', 'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Welcome to CloudKitty Client Release Notes documentation!
|
||||
Welcome to Cloudkitty Client Release Notes documentation!
|
||||
=========================================================
|
||||
|
||||
Contents
|
||||
@@ -8,19 +8,6 @@ Contents
|
||||
:maxdepth: 2
|
||||
|
||||
unreleased
|
||||
2025.2
|
||||
2025.1
|
||||
2024.2
|
||||
2024.1
|
||||
2023.2
|
||||
2023.1
|
||||
zed
|
||||
yoga
|
||||
xena
|
||||
wallaby
|
||||
victoria
|
||||
ussuri
|
||||
train
|
||||
stein
|
||||
rocky
|
||||
queens
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
==========================
|
||||
Train Series Release Notes
|
||||
==========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/train
|
||||
@@ -1,6 +0,0 @@
|
||||
===========================
|
||||
Ussuri Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/ussuri
|
||||
@@ -1,6 +0,0 @@
|
||||
=============================
|
||||
Victoria Series Release Notes
|
||||
=============================
|
||||
|
||||
.. release-notes::
|
||||
:branch: unmaintained/victoria
|
||||
@@ -1,6 +0,0 @@
|
||||
============================
|
||||
Wallaby Series Release Notes
|
||||
============================
|
||||
|
||||
.. release-notes::
|
||||
:branch: unmaintained/wallaby
|
||||
@@ -1,6 +0,0 @@
|
||||
=========================
|
||||
Xena Series Release Notes
|
||||
=========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: unmaintained/xena
|
||||
@@ -1,6 +0,0 @@
|
||||
=========================
|
||||
Yoga Series Release Notes
|
||||
=========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: unmaintained/yoga
|
||||
@@ -1,6 +0,0 @@
|
||||
========================
|
||||
Zed Series Release Notes
|
||||
========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: unmaintained/zed
|
||||
@@ -1,13 +1,14 @@
|
||||
# Requirements lower bounds listed here are our best effort to keep them up to
|
||||
# date but we do not test them so no guarantee of having them all correct. If
|
||||
# you find any incorrect lower bounds, let us know or propose a fix.
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
pbr>=5.5.1 # Apache-2.0
|
||||
cliff>=3.5.0 # Apache-2.0
|
||||
keystoneauth1>=4.3.0 # Apache-2.0
|
||||
oslo.utils>=4.7.0 # Apache-2.0
|
||||
oslo.log>=4.4.0 # Apache-2.0
|
||||
PyYAML>=5.3.1 # MIT
|
||||
jsonpath-rw-ext>=1.2.0 # Apache-2.0
|
||||
openstacksdk>=0.10.0 # Apache-2.0
|
||||
osc-lib>=2.3.0 # Apache-2.0
|
||||
pbr>=2.0.0,!=2.1.0 # Apache-2.0
|
||||
cliff>=2.11.0 # Apache-2.0
|
||||
keystoneauth1>=3.4.0 # Apache-2.0
|
||||
oslo.utils>=3.35 # Apache-2.0
|
||||
oslo.log>=3.36 # Apache-2.0
|
||||
PyYAML>=3.12 # MIT
|
||||
jsonpath-rw-ext>=1.0 # Apache-2.0
|
||||
six>=1.11 # MIT
|
||||
os-client-config>=1.29.0 # Apache-2.0
|
||||
osc-lib>=1.12.1 # Apache-2.0
|
||||
|
||||
50
setup.cfg
50
setup.cfg
@@ -1,12 +1,11 @@
|
||||
[metadata]
|
||||
name = python-cloudkittyclient
|
||||
summary = API client of cloudkitty, Rating as a Service project.
|
||||
description_file =
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
author_email = openstack-discuss@lists.openstack.org
|
||||
home_page = https://docs.openstack.org/python-cloudkittyclient/latest/
|
||||
python_requires = >=3.10
|
||||
author-email = openstack-discuss@lists.openstack.org
|
||||
home-page = https://docs.openstack.org/python-cloudkittyclient/latest/
|
||||
classifier =
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Information Technology
|
||||
@@ -14,13 +13,11 @@ classifier =
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 3 :: Only
|
||||
Programming Language :: Python :: Implementation :: CPython
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.10
|
||||
Programming Language :: Python :: 3.11
|
||||
Programming Language :: Python :: 3.12
|
||||
Programming Language :: Python :: 3.13
|
||||
Programming Language :: Python :: 3.6
|
||||
Programming Language :: Python :: 3.7
|
||||
|
||||
[files]
|
||||
packages =
|
||||
@@ -89,15 +86,10 @@ openstack.rating.v1 =
|
||||
rating_pyscript_delete = cloudkittyclient.v1.rating.pyscripts_cli:CliDeleteScript
|
||||
|
||||
openstack.rating.v2 =
|
||||
rating_tasks_reprocessing_get = cloudkittyclient.v2.reprocessing_cli:CliReprocessingTasksGet
|
||||
rating_tasks_reprocessing_create = cloudkittyclient.v2.reprocessing_cli:CliReprocessingTasksPost
|
||||
|
||||
rating_dataframes_get = cloudkittyclient.v2.dataframes_cli:CliDataframesGet
|
||||
rating_dataframes_add = cloudkittyclient.v2.dataframes_cli:CliDataframesAdd
|
||||
|
||||
rating_scope_state_get = cloudkittyclient.v2.scope_cli:CliScopeStateGet
|
||||
rating_scope_state_reset = cloudkittyclient.v2.scope_cli:CliScopeStateReset
|
||||
rating_scope_patch = cloudkittyclient.v2.scope_cli:CliPatchScope
|
||||
|
||||
rating_summary_get = cloudkittyclient.v2.summary_cli:CliSummaryGet
|
||||
|
||||
@@ -145,6 +137,7 @@ openstack.rating.v2 =
|
||||
rating_collector_state_get = cloudkittyclient.v1.collector_cli:CliCollectorGetState
|
||||
rating_collector_enable = cloudkittyclient.v1.collector_cli:CliCollectorEnable
|
||||
rating_collector_disable = cloudkittyclient.v1.collector_cli:CliCollectorDisable
|
||||
rating_dataframes_get = cloudkittyclient.v1.storage_cli:CliGetDataframes
|
||||
|
||||
rating_pyscript_create = cloudkittyclient.v1.rating.pyscripts_cli:CliCreateScript
|
||||
rating_pyscript_list = cloudkittyclient.v1.rating.pyscripts_cli:CliListScripts
|
||||
@@ -152,7 +145,7 @@ openstack.rating.v2 =
|
||||
rating_pyscript_update = cloudkittyclient.v1.rating.pyscripts_cli:CliUpdateScript
|
||||
rating_pyscript_delete = cloudkittyclient.v1.rating.pyscripts_cli:CliDeleteScript
|
||||
|
||||
cloudkittyclient_v1 =
|
||||
cloudkittyclient.v1 =
|
||||
total_get = cloudkittyclient.v1.report_cli:CliTotalGet
|
||||
summary_get = cloudkittyclient.v1.report_cli:CliSummaryGet
|
||||
report_tenant_list = cloudkittyclient.v1.report_cli:CliTenantList
|
||||
@@ -208,16 +201,11 @@ cloudkittyclient_v1 =
|
||||
pyscript_update = cloudkittyclient.v1.rating.pyscripts_cli:CliUpdateScript
|
||||
pyscript_delete = cloudkittyclient.v1.rating.pyscripts_cli:CliDeleteScript
|
||||
|
||||
cloudkittyclient_v2 =
|
||||
tasks_reprocessing_get = cloudkittyclient.v2.reprocessing_cli:CliReprocessingTasksGet
|
||||
tasks_reprocessing_create = cloudkittyclient.v2.reprocessing_cli:CliReprocessingTasksPost
|
||||
|
||||
cloudkittyclient.v2 =
|
||||
dataframes_add = cloudkittyclient.v2.dataframes_cli:CliDataframesAdd
|
||||
dataframes_get = cloudkittyclient.v2.dataframes_cli:CliDataframesGet
|
||||
|
||||
scope_state_get = cloudkittyclient.v2.scope_cli:CliScopeStateGet
|
||||
scope_state_reset = cloudkittyclient.v2.scope_cli:CliScopeStateReset
|
||||
scope_patch = cloudkittyclient.v2.scope_cli:CliPatchScope
|
||||
|
||||
summary_get = cloudkittyclient.v2.summary_cli:CliSummaryGet
|
||||
|
||||
@@ -266,6 +254,7 @@ cloudkittyclient_v2 =
|
||||
collector_state_get = cloudkittyclient.v1.collector_cli:CliCollectorGetState
|
||||
collector_enable = cloudkittyclient.v1.collector_cli:CliCollectorEnable
|
||||
collector_disable = cloudkittyclient.v1.collector_cli:CliCollectorDisable
|
||||
dataframes_get = cloudkittyclient.v1.storage_cli:CliGetDataframes
|
||||
|
||||
pyscript_create = cloudkittyclient.v1.rating.pyscripts_cli:CliCreateScript
|
||||
pyscript_list = cloudkittyclient.v1.rating.pyscripts_cli:CliListScripts
|
||||
@@ -278,3 +267,20 @@ keystoneauth1.plugin =
|
||||
|
||||
cliff.formatter.list =
|
||||
df-to-csv = cloudkittyclient.format:DataframeToCsvFormatter
|
||||
|
||||
[upload_sphinx]
|
||||
upload-dir = doc/build/html
|
||||
|
||||
[compile_catalog]
|
||||
directory = cloudkittyclient/locale
|
||||
domain = python-cloudkittyclient
|
||||
|
||||
[update_catalog]
|
||||
domain = python-cloudkittyclient
|
||||
output_dir = cloudkittyclient/locale
|
||||
input_file = cloudkittyclient/locale/python-cloudkittyclient.pot
|
||||
|
||||
[extract_messages]
|
||||
keywords = _ gettext ngettext l_ lazy_gettext
|
||||
mapping_file = babel.cfg
|
||||
output_file = cloudkittyclient/locale/python-cloudkittyclient.pot
|
||||
|
||||
9
setup.py
9
setup.py
@@ -13,8 +13,17 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
||||
import setuptools
|
||||
|
||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
||||
# setuptools if some other modules registered functions in `atexit`.
|
||||
# solution from: http://bugs.python.org/issue15881#msg170215
|
||||
try:
|
||||
import multiprocessing # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr>=2.0.0'],
|
||||
pbr=True)
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
hacking>=7.0.0,<7.1.0 # Apache-2.0
|
||||
# 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!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
|
||||
|
||||
coverage>=4.0,!=4.4 # Apache-2.0
|
||||
python-subunit>=1.4.0 # Apache-2.0/BSD
|
||||
python-subunit>=0.0.18 # Apache-2.0/BSD
|
||||
oslotest>=1.10.0 # Apache-2.0
|
||||
stestr>=2.0 # Apache-2.0
|
||||
mock>=2.0 # BSD
|
||||
python-openstackclient>=3.14 # Apache-2.0
|
||||
|
||||
76
tox.ini
76
tox.ini
@@ -1,18 +1,21 @@
|
||||
[tox]
|
||||
minversion = 3.18.0
|
||||
envlist = py3,pep8
|
||||
ignore_basepython_conflict = True
|
||||
minversion = 2.0
|
||||
envlist = py27,py36,py37,pypy,pep8
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
basepython = python3
|
||||
usedevelop = True
|
||||
install_command = pip install -U {opts} {packages}
|
||||
install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/train} -U {opts} {packages}
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run {posargs}
|
||||
|
||||
[testenv:cover]
|
||||
basepython = python3
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
PYTHON=coverage run --source cloudkittyclient --parallel-mode
|
||||
commands =
|
||||
stestr run {posargs}
|
||||
@@ -22,69 +25,37 @@ commands =
|
||||
coverage report
|
||||
|
||||
[testenv:debug]
|
||||
basepython = python3
|
||||
commands = oslo_debug_helper -t cloudkittyclient/tests {posargs}
|
||||
|
||||
[testenv:functional-v1]
|
||||
passenv =
|
||||
OS_CLOUD
|
||||
OS_PROJECT_DOMAIN_ID
|
||||
OS_USER_DOMAIN_ID
|
||||
OS_PROJECT_DOMAIN_NAME
|
||||
OS_USER_DOMAIN_NAME
|
||||
OS_PROJECT_NAME
|
||||
OS_IDENTITY_API_VERSION
|
||||
OS_PASSWORD
|
||||
OS_AUTH_TYPE
|
||||
OS_AUTH_URL
|
||||
OS_USERNAME
|
||||
OS_ENDPOINT
|
||||
VIRTUAL_ENV
|
||||
passenv = OS_CLOUD OS_PROJECT_DOMAIN_ID OS_USER_DOMAIN_ID OS_PROJECT_DOMAIN_NAME OS_USER_DOMAIN_NAME OS_PROJECT_NAME OS_IDENTITY_API_VERSION OS_PASSWORD OS_AUTH_TYPE OS_AUTH_URL OS_USERNAME OS_ENDPOINT
|
||||
setenv = OS_RATING_API_VERSION=1
|
||||
commands = stestr run --concurrency=1 --test-path ./cloudkittyclient/tests/functional/v1
|
||||
|
||||
[testenv:functional-v2]
|
||||
passenv =
|
||||
OS_CLOUD
|
||||
OS_PROJECT_DOMAIN_ID
|
||||
OS_USER_DOMAIN_ID
|
||||
OS_PROJECT_DOMAIN_NAME
|
||||
OS_USER_DOMAIN_NAME
|
||||
OS_PROJECT_NAME
|
||||
OS_IDENTITY_API_VERSION
|
||||
OS_PASSWORD
|
||||
OS_AUTH_TYPE
|
||||
OS_AUTH_URL
|
||||
OS_USERNAME
|
||||
OS_ENDPOINT
|
||||
VIRTUAL_ENV
|
||||
passenv = OS_CLOUD OS_PROJECT_DOMAIN_ID OS_USER_DOMAIN_ID OS_PROJECT_DOMAIN_NAME OS_USER_DOMAIN_NAME OS_PROJECT_NAME OS_IDENTITY_API_VERSION OS_PASSWORD OS_AUTH_TYPE OS_AUTH_URL OS_USERNAME OS_ENDPOINT
|
||||
setenv = OS_RATING_API_VERSION=2
|
||||
commands = stestr run --concurrency=1 --test-path ./cloudkittyclient/tests/functional/v2
|
||||
|
||||
[testenv:pep8]
|
||||
basepython = python3
|
||||
commands = flake8
|
||||
|
||||
[testenv:venv]
|
||||
basepython = python3
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:docs]
|
||||
deps =
|
||||
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
|
||||
-r{toxinidir}/doc/requirements.txt
|
||||
commands = sphinx-build --keep-going -b html doc/source doc/build/html
|
||||
|
||||
[testenv:pdf-docs]
|
||||
deps = {[testenv:docs]deps}
|
||||
allowlist_externals =
|
||||
make
|
||||
commands =
|
||||
sphinx-build --keep-going -b latex doc/source doc/build/pdf
|
||||
make -C doc/build/pdf
|
||||
basepython = python3
|
||||
deps = -r{toxinidir}/doc/requirements.txt
|
||||
commands = sphinx-build -W -b html doc/source doc/build/html
|
||||
|
||||
[flake8]
|
||||
# E123, E125 skipped as they are invalid PEP-8.
|
||||
|
||||
show-source = True
|
||||
ignore = E123,E125,W503,W504
|
||||
ignore = E123,E125
|
||||
builtins = _
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,releasenotes
|
||||
|
||||
@@ -92,8 +63,15 @@ exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,releasenotes
|
||||
import_exceptions = cloudkittyclient.i18n
|
||||
|
||||
[testenv:releasenotes]
|
||||
basepython = python3
|
||||
deps =
|
||||
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
|
||||
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/train}
|
||||
-r{toxinidir}/doc/requirements.txt
|
||||
commands =
|
||||
sphinx-build -a -E -W -d releasenotes/build/doctrees --keep-going -b html releasenotes/source releasenotes/build/html
|
||||
commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
|
||||
|
||||
[testenv:lower-constraints]
|
||||
basepython = python3
|
||||
deps =
|
||||
-c{toxinidir}/lower-constraints.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
-r{toxinidir}/requirements.txt
|
||||
|
||||
Reference in New Issue
Block a user