Compare commits
76 Commits
2.0.0
...
victoria-e
| Author | SHA1 | Date | |
|---|---|---|---|
| d1cb3b1e69 | |||
| 7a2670fa30 | |||
|
|
2a3dd279dc | ||
|
|
def535752f | ||
|
|
d3408517d6 | ||
|
|
37df7e1258 | ||
|
|
e9a6c22cef | ||
|
|
db10c24e25 | ||
|
|
2bcd29dcbd | ||
|
|
d5a99b511f | ||
|
|
2a475bbb4a | ||
|
|
8f9e104f84 | ||
|
|
a2f3de89b4 | ||
|
|
608cd0262f | ||
|
|
de3c4924e9 | ||
|
|
e88a3fa033 | ||
|
|
61dc82cb54 | ||
|
|
e69f9d5452 | ||
| 73cb650ee4 | |||
| 8bc96e21a9 | |||
|
|
3f97e9844a | ||
|
|
f4b1a3f224 | ||
|
|
9424e67f21 | ||
|
|
9c3bd770f2 | ||
|
|
d28c5bc4dd | ||
|
|
3e7f7a0f5d | ||
|
|
584046761a | ||
|
|
be0a9861d0 | ||
|
|
e599ac9f84 | ||
| 55b056bc8a | |||
|
|
296fd22640 | ||
|
|
c8d7a9e1c5 | ||
|
|
dd4112acea | ||
|
|
3010383f10 | ||
|
|
d660bef837 | ||
|
|
c138f409b1 | ||
|
|
a5a14a5883 | ||
|
|
7e7d25dd78 | ||
|
|
b29fefaf8d | ||
|
|
301d8cb8ba | ||
|
|
b7018031a7 | ||
|
|
0c623ef90c | ||
|
|
6d27c9bc0a | ||
|
|
a3e18a120b | ||
|
|
573908b345 | ||
|
|
d2fb83e64c | ||
|
|
d77526b42e | ||
|
|
1f1f811f1d | ||
|
|
40984c2593 | ||
|
|
75687951e0 | ||
|
|
aeebd64928 | ||
|
|
de96c61985 | ||
|
|
a7e687f740 | ||
|
|
89475e2aac | ||
|
|
658fd14155 | ||
| 454584fc7e | |||
|
|
613e698de8 | ||
|
|
e7a9f20999 | ||
|
|
dd1a421b7c | ||
|
|
419ee046f2 | ||
|
|
541d68239e | ||
|
|
8e528c8e97 | ||
|
|
3b49fbd4d0 | ||
|
|
378ee67117 | ||
|
|
e9a92a2941 | ||
|
|
1ed287c92a | ||
|
|
63ac84b165 | ||
|
|
1cf5b3aca2 | ||
|
|
503dd3247a | ||
|
|
7b9d447eb8 | ||
|
|
a9eb87c436 | ||
|
|
fff37a84fa | ||
|
|
81cdcba4f3 | ||
|
|
214083c695 | ||
|
|
c78ae05005 | ||
| 1348fd67e1 |
@@ -1,4 +1,5 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
host=review.opendev.org
|
||||
port=29418
|
||||
project=openstack/python-cloudkittyclient.git
|
||||
defaultbranch=stable/victoria
|
||||
|
||||
38
.zuul.yaml
38
.zuul.yaml
@@ -1,5 +1,5 @@
|
||||
- job:
|
||||
name: cloudkittyclient-devstack-functional
|
||||
name: cloudkittyclient-devstack-functional-base
|
||||
parent: devstack
|
||||
description: |
|
||||
Job for cloudkittyclient functional tests
|
||||
@@ -18,20 +18,46 @@
|
||||
- ^releasenotes/.*$
|
||||
vars:
|
||||
devstack_plugins:
|
||||
cloudkitty: https://git.openstack.org/openstack/cloudkitty
|
||||
cloudkitty: https://opendev.org/openstack/cloudkitty
|
||||
devstack_localrc:
|
||||
CLOUDKITTY_FETCHER: keystone
|
||||
DEVSTACK_GATE_USE_PYTHON3: "True"
|
||||
USE_PYTHON3: True
|
||||
devstack_services:
|
||||
ck-api: true
|
||||
horizon: false
|
||||
tox_install_siblings: false
|
||||
zuul_work_dir: src/git.openstack.org/openstack/python-cloudkittyclient
|
||||
tox_envlist: functional
|
||||
zuul_work_dir: src/opendev.org/openstack/python-cloudkittyclient
|
||||
|
||||
- job:
|
||||
name: cloudkittyclient-devstack-functional-v1-client
|
||||
parent: cloudkittyclient-devstack-functional-base
|
||||
vars:
|
||||
tox_envlist: functional-v1
|
||||
|
||||
- job:
|
||||
name: cloudkittyclient-devstack-functional-v2-client
|
||||
parent: cloudkittyclient-devstack-functional-base
|
||||
vars:
|
||||
tox_envlist: functional-v2
|
||||
|
||||
- project:
|
||||
templates:
|
||||
- openstack-lower-constraints-jobs
|
||||
- check-requirements
|
||||
- openstack-cover-jobs
|
||||
- openstack-python3-victoria-jobs
|
||||
- openstackclient-plugin-jobs
|
||||
- publish-openstack-docs-pti
|
||||
check:
|
||||
jobs:
|
||||
- cloudkittyclient-devstack-functional:
|
||||
- cloudkittyclient-devstack-functional-v1-client:
|
||||
voting: true
|
||||
- cloudkittyclient-devstack-functional-v2-client:
|
||||
voting: true
|
||||
gate:
|
||||
jobs:
|
||||
- cloudkittyclient-devstack-functional:
|
||||
- cloudkittyclient-devstack-functional-v1-client:
|
||||
voting: true
|
||||
- cloudkittyclient-devstack-functional-v2-client:
|
||||
voting: true
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
If you would like to contribute to the development of OpenStack,
|
||||
you must follow the steps in this page:
|
||||
|
||||
http://docs.openstack.org/infra/manual/developers.html
|
||||
https://docs.openstack.org/infra/manual/developers.html
|
||||
|
||||
Once those steps have been completed, changes to OpenStack
|
||||
should be submitted for review via the Gerrit tool, following
|
||||
the workflow documented at:
|
||||
|
||||
http://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
https://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
||||
Pull requests submitted through GitHub will be ignored.
|
||||
|
||||
Bugs should be filed on Launchpad, not GitHub:
|
||||
Bugs should be filed on Storyboard, not GitHub:
|
||||
|
||||
https://bugs.launchpad.net/cloudkitty
|
||||
https://storyboard.openstack.org/#!/project/openstack/python-cloudkittyclient
|
||||
|
||||
0
cloudkittyclient/common/__init__.py
Normal file
0
cloudkittyclient/common/__init__.py
Normal file
@@ -12,13 +12,24 @@
|
||||
# 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 six.moves.urllib.parse import urlencode
|
||||
from cloudkittyclient import utils
|
||||
|
||||
|
||||
class BaseManager(object):
|
||||
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):
|
||||
"""Base class for Endpoint Manager objects."""
|
||||
|
||||
url = ''
|
||||
47
cloudkittyclient/common/client.py
Normal file
47
cloudkittyclient/common/client.py
Normal file
@@ -0,0 +1,47 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from keystoneauth1 import adapter
|
||||
from keystoneauth1 import session as ks_session
|
||||
|
||||
|
||||
class BaseClient(object):
|
||||
|
||||
def __init__(self,
|
||||
session=None,
|
||||
adapter_options={},
|
||||
cacert=None,
|
||||
insecure=False,
|
||||
**kwargs):
|
||||
adapter_options.setdefault('service_type', 'rating')
|
||||
adapter_options.setdefault('additional_headers', {
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
if insecure:
|
||||
verify_cert = False
|
||||
else:
|
||||
if cacert:
|
||||
verify_cert = cacert
|
||||
else:
|
||||
verify_cert = True
|
||||
|
||||
self.session = session
|
||||
if self.session is None:
|
||||
self.session = ks_session.Session(
|
||||
verify=verify_cert, **kwargs)
|
||||
|
||||
self.api_client = adapter.Adapter(
|
||||
session=self.session, **adapter_options)
|
||||
@@ -19,6 +19,7 @@ API_VERSION_OPTION = 'os_rating_api_version'
|
||||
API_NAME = "rating"
|
||||
API_VERSIONS = {
|
||||
"1": "cloudkittyclient.v1.client.Client",
|
||||
"2": "cloudkittyclient.v2.client.Client",
|
||||
}
|
||||
|
||||
|
||||
@@ -30,9 +31,19 @@ def make_client(instance):
|
||||
version,
|
||||
API_VERSIONS)
|
||||
instance.setup_auth()
|
||||
return ck_client(session=instance.session)
|
||||
adapter_options = dict(
|
||||
interface=instance.interface,
|
||||
region_name=instance.region_name,
|
||||
)
|
||||
return ck_client(session=instance.session,
|
||||
adapter_options=adapter_options)
|
||||
|
||||
|
||||
def build_option_parser(parser):
|
||||
"""Hook to add global options."""
|
||||
parser.add_argument(
|
||||
'--rating-api-version', type=int, default=utils.env(
|
||||
'OS_RATING_API_VERSION',
|
||||
default=DEFAULT_API_VERSION)
|
||||
)
|
||||
return parser
|
||||
|
||||
@@ -22,6 +22,7 @@ import os_client_config
|
||||
from oslo_log import log
|
||||
|
||||
from cloudkittyclient import client
|
||||
from cloudkittyclient.osc import DEFAULT_API_VERSION
|
||||
from cloudkittyclient import utils
|
||||
|
||||
|
||||
@@ -77,13 +78,25 @@ class CloudKittyShell(cliff.app.App):
|
||||
'pyscripts-script-update',
|
||||
]
|
||||
|
||||
def _get_api_version(self, args):
|
||||
# FIXME(peschk_l): This is a hacky way to figure out the client version
|
||||
# to load. If anybody has a better idea, please fix this.
|
||||
self.deferred_help = True
|
||||
parser = self.build_option_parser('CloudKitty CLI client',
|
||||
utils.get_version())
|
||||
del self.deferred_help
|
||||
parsed_args = parser.parse_known_args(args)
|
||||
return str(parsed_args[0].os_rating_api_version or DEFAULT_API_VERSION)
|
||||
|
||||
def __init__(self, args):
|
||||
self._args = args
|
||||
self.cloud_config = os_client_config.OpenStackConfig()
|
||||
super(CloudKittyShell, self).__init__(
|
||||
description='CloudKitty CLI client',
|
||||
version=utils.get_version(),
|
||||
command_manager=CommandManager('cloudkittyclient'),
|
||||
command_manager=CommandManager('cloudkittyclient_v{}'.format(
|
||||
self._get_api_version(args[:]),
|
||||
)),
|
||||
deferred_help=True,
|
||||
)
|
||||
self._client = None
|
||||
@@ -105,9 +118,6 @@ class CloudKittyShell(cliff.app.App):
|
||||
description,
|
||||
version,
|
||||
argparse_kwargs={'allow_abbrev': False})
|
||||
parser.add_argument(
|
||||
'--ck-api-version', type=int, default=1, dest='ck_version',
|
||||
help='Cloudkitty API version (defaults to 1)')
|
||||
if 'OS_AUTH_TYPE' not in os.environ.keys() \
|
||||
and 'OS_PASSWORD' in os.environ.keys():
|
||||
os.environ['OS_AUTH_TYPE'] = 'password'
|
||||
@@ -133,9 +143,10 @@ class CloudKittyShell(cliff.app.App):
|
||||
self.options.os_rating_endpoint_override or
|
||||
self.options.os_endpoint_override),
|
||||
)
|
||||
self._client = client.Client(str(self.options.ck_version),
|
||||
session=session,
|
||||
adapter_options=adapter_options)
|
||||
self._client = client.Client(
|
||||
str(self.options.os_rating_api_version or DEFAULT_API_VERSION),
|
||||
session=session,
|
||||
adapter_options=adapter_options)
|
||||
return self._client
|
||||
|
||||
|
||||
|
||||
@@ -24,24 +24,30 @@ from cloudkittyclient.tests import utils
|
||||
class BaseFunctionalTest(utils.BaseTestCase):
|
||||
|
||||
def _run(self, executable, action,
|
||||
flags='', params='', fmt='-f json', has_output=True):
|
||||
flags='', params='', fmt='-f json', stdin=None, has_output=True):
|
||||
if not has_output:
|
||||
fmt = ''
|
||||
cmd = ' '.join([executable, flags, action, params, fmt])
|
||||
cmd = shlex.split(cmd)
|
||||
p = subprocess.Popen(cmd, env=os.environ.copy(), shell=False,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = p.communicate()
|
||||
p = subprocess.Popen(
|
||||
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)
|
||||
if p.returncode != 0:
|
||||
raise RuntimeError('"{cmd}" returned {val}: {msg}'.format(
|
||||
cmd=' '.join(cmd), val=p.returncode, msg=stderr))
|
||||
return json.loads(stdout) if has_output else None
|
||||
|
||||
def openstack(self, action,
|
||||
flags='', params='', fmt='-f json', has_output=True):
|
||||
flags='', params='', fmt='-f json',
|
||||
stdin=None, has_output=True):
|
||||
return self._run('openstack rating', action,
|
||||
flags, params, fmt, has_output)
|
||||
flags, params, fmt, stdin, has_output)
|
||||
|
||||
def cloudkitty(self, action,
|
||||
flags='', params='', fmt='-f json', has_output=True):
|
||||
return self._run('cloudkitty', action, flags, params, fmt, has_output)
|
||||
flags='', params='', fmt='-f json',
|
||||
stdin=None, has_output=True):
|
||||
return self._run('cloudkitty', action, flags, params, fmt,
|
||||
stdin, has_output)
|
||||
@@ -13,7 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient.tests.functional.v1 import base
|
||||
from cloudkittyclient.tests.functional import base
|
||||
|
||||
|
||||
class CkCollectorTest(base.BaseFunctionalTest):
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient.tests.functional.v1 import base
|
||||
from cloudkittyclient.tests.functional import base
|
||||
|
||||
|
||||
class CkHashmapTest(base.BaseFunctionalTest):
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#
|
||||
import jsonpath_rw_ext as jp
|
||||
|
||||
from cloudkittyclient.tests.functional.v1 import base
|
||||
from cloudkittyclient.tests.functional import base
|
||||
|
||||
|
||||
class CkInfoTest(base.BaseFunctionalTest):
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient.tests.functional.v1 import base
|
||||
from cloudkittyclient.tests.functional import base
|
||||
|
||||
|
||||
class CkPyscriptTest(base.BaseFunctionalTest):
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient.tests.functional.v1 import base
|
||||
from cloudkittyclient.tests.functional import base
|
||||
|
||||
|
||||
class CkRatingTest(base.BaseFunctionalTest):
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient.tests.functional.v1 import base
|
||||
from cloudkittyclient.tests.functional import base
|
||||
|
||||
|
||||
class CkReportTest(base.BaseFunctionalTest):
|
||||
@@ -22,8 +22,8 @@ class CkReportTest(base.BaseFunctionalTest):
|
||||
self.runner = self.cloudkitty
|
||||
|
||||
def test_get_summary(self):
|
||||
resp = self.runner('summary get')[0]
|
||||
self.assertEqual(resp['Resource Type'], 'ALL')
|
||||
resp = self.runner('summary get')
|
||||
self.assertEqual(len(resp), 0)
|
||||
|
||||
def test_get_summary_with_groupby(self):
|
||||
resp = self.runner('summary get', params='-g res_type tenant_id')
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient.tests.functional.v1 import base
|
||||
from cloudkittyclient.tests.functional import base
|
||||
|
||||
|
||||
class CkStorageTest(base.BaseFunctionalTest):
|
||||
|
||||
0
cloudkittyclient/tests/functional/v2/__init__.py
Normal file
0
cloudkittyclient/tests/functional/v2/__init__.py
Normal file
174
cloudkittyclient/tests/functional/v2/test_dataframes.py
Normal file
174
cloudkittyclient/tests/functional/v2/test_dataframes.py
Normal file
@@ -0,0 +1,174 @@
|
||||
# Copyright 2019 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from cloudkittyclient.tests.functional import base
|
||||
|
||||
|
||||
class CkDataframesTest(base.BaseFunctionalTest):
|
||||
dataframes_data = """
|
||||
{
|
||||
"dataframes": [
|
||||
{
|
||||
"period": {
|
||||
"begin": "20190723T122810Z",
|
||||
"end": "20190723T132810Z"
|
||||
},
|
||||
"usage": {
|
||||
"metric_one": [
|
||||
{
|
||||
"vol": {
|
||||
"unit": "GiB",
|
||||
"qty": 1.2
|
||||
},
|
||||
"rating": {
|
||||
"price": 0.04
|
||||
},
|
||||
"groupby": {
|
||||
"group_one": "one",
|
||||
"group_two": "two"
|
||||
},
|
||||
"metadata": {
|
||||
"attr_one": "one",
|
||||
"attr_two": "two"
|
||||
}
|
||||
}
|
||||
],
|
||||
"metric_two": [
|
||||
{
|
||||
"vol": {
|
||||
"unit": "MB",
|
||||
"qty": 200.4
|
||||
},
|
||||
"rating": {
|
||||
"price": 0.06
|
||||
},
|
||||
"groupby": {
|
||||
"group_one": "one",
|
||||
"group_two": "two"
|
||||
},
|
||||
"metadata": {
|
||||
"attr_one": "one",
|
||||
"attr_two": "two"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"period": {
|
||||
"begin": "20190823T122810Z",
|
||||
"end": "20190823T132810Z"
|
||||
},
|
||||
"usage": {
|
||||
"metric_one": [
|
||||
{
|
||||
"vol": {
|
||||
"unit": "GiB",
|
||||
"qty": 2.4
|
||||
},
|
||||
"rating": {
|
||||
"price": 0.08
|
||||
},
|
||||
"groupby": {
|
||||
"group_one": "one",
|
||||
"group_two": "two"
|
||||
},
|
||||
"metadata": {
|
||||
"attr_one": "one",
|
||||
"attr_two": "two"
|
||||
}
|
||||
}
|
||||
],
|
||||
"metric_two": [
|
||||
{
|
||||
"vol": {
|
||||
"unit": "MB",
|
||||
"qty": 400.8
|
||||
},
|
||||
"rating": {
|
||||
"price": 0.12
|
||||
},
|
||||
"groupby": {
|
||||
"group_one": "one",
|
||||
"group_two": "two"
|
||||
},
|
||||
"metadata": {
|
||||
"attr_one": "one",
|
||||
"attr_two": "two"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CkDataframesTest, self).__init__(*args, **kwargs)
|
||||
self.runner = self.cloudkitty
|
||||
|
||||
def setUp(self):
|
||||
super(CkDataframesTest, self).setUp()
|
||||
|
||||
self.fixture_file_name = '{}.json'.format(uuid.uuid4())
|
||||
with open(self.fixture_file_name, 'w') as f:
|
||||
f.write(self.dataframes_data)
|
||||
|
||||
def tearDown(self):
|
||||
files = os.listdir('.')
|
||||
if self.fixture_file_name in files:
|
||||
os.remove(self.fixture_file_name)
|
||||
|
||||
super(CkDataframesTest, self).tearDown()
|
||||
|
||||
def test_dataframes_add_with_no_args(self):
|
||||
self.assertRaisesRegexp(
|
||||
RuntimeError,
|
||||
'error: the following arguments are required: datafile',
|
||||
self.runner,
|
||||
'dataframes add',
|
||||
fmt='',
|
||||
has_output=False,
|
||||
)
|
||||
|
||||
def test_dataframes_add(self):
|
||||
self.runner(
|
||||
'dataframes add {}'.format(self.fixture_file_name),
|
||||
fmt='',
|
||||
has_output=False,
|
||||
)
|
||||
|
||||
def test_dataframes_add_with_hyphen_stdin(self):
|
||||
with open(self.fixture_file_name, 'r') as f:
|
||||
self.runner(
|
||||
'dataframes add -',
|
||||
fmt='',
|
||||
stdin=f.read().encode(),
|
||||
has_output=False,
|
||||
)
|
||||
|
||||
def test_dataframes_get(self):
|
||||
# TODO(jferrieu): functional tests will be added in another
|
||||
# patch for `dataframes get`
|
||||
pass
|
||||
|
||||
|
||||
class OSCDataframesTest(CkDataframesTest):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(OSCDataframesTest, self).__init__(*args, **kwargs)
|
||||
self.runner = self.openstack
|
||||
41
cloudkittyclient/tests/functional/v2/test_scope.py
Normal file
41
cloudkittyclient/tests/functional/v2/test_scope.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# Copyright 2019 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient.tests.functional import base
|
||||
|
||||
|
||||
class CkScopeTest(base.BaseFunctionalTest):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CkScopeTest, self).__init__(*args, **kwargs)
|
||||
self.runner = self.cloudkitty
|
||||
|
||||
def test_scope_state_get(self):
|
||||
return True
|
||||
# FIXME(peschk_l): Uncomment and update this once there is a way to set
|
||||
# the state of a scope through the client
|
||||
# resp = self.runner('scope state get')
|
||||
|
||||
def test_scope_state_reset(self):
|
||||
return True
|
||||
# FIXME(jferrieu): Uncomment and update this once there is a way to set
|
||||
# the state of a scope through the client
|
||||
# resp = self.runner('scope state reset')
|
||||
|
||||
|
||||
class OSCScopeTest(CkScopeTest):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(OSCScopeTest, self).__init__(*args, **kwargs)
|
||||
self.runner = self.openstack
|
||||
35
cloudkittyclient/tests/functional/v2/test_summary.py
Normal file
35
cloudkittyclient/tests/functional/v2/test_summary.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# Copyright 2019 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient.tests.functional import base
|
||||
|
||||
|
||||
class CkSummaryTest(base.BaseFunctionalTest):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CkSummaryTest, self).__init__(*args, **kwargs)
|
||||
self.runner = self.cloudkitty
|
||||
|
||||
def test_summary_get(self):
|
||||
return True
|
||||
# FIXME(peschk_l): Uncomment and update this once there is a way to set
|
||||
# the state of a summary through the client
|
||||
# resp = self.runner('summary get')
|
||||
|
||||
|
||||
class OSCSummaryTest(CkSummaryTest):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(OSCSummaryTest, self).__init__(*args, **kwargs)
|
||||
self.runner = self.openstack
|
||||
@@ -13,7 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
import mock
|
||||
from unittest import mock
|
||||
|
||||
from cloudkittyclient import exc
|
||||
from cloudkittyclient.tests.unit.v1 import base
|
||||
|
||||
48
cloudkittyclient/tests/unit/v1/test_rating.py
Normal file
48
cloudkittyclient/tests/unit/v1/test_rating.py
Normal file
@@ -0,0 +1,48 @@
|
||||
# Copyright 2019 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
import decimal
|
||||
|
||||
from cloudkittyclient import exc
|
||||
from cloudkittyclient.tests.unit.v1 import base
|
||||
|
||||
|
||||
class TestRating(base.BaseAPIEndpointTestCase):
|
||||
|
||||
def test_quote_request(self):
|
||||
res_data = [{'usage': {
|
||||
'instance': [{
|
||||
'vol': {'unit': 'undef', 'qty': '1'},
|
||||
'rating': {'price': decimal.Decimal(1)},
|
||||
'desc': {
|
||||
'disk_total_display': 1,
|
||||
'image_id': 'c43a3e7d-c4e6-45d6-8c8d-e2832a45bc0a',
|
||||
'ram': 64,
|
||||
'ephemeral': 0,
|
||||
'vcpus': 1,
|
||||
'source_type': 'image',
|
||||
'disk_total': 1,
|
||||
'flavor_id': '42',
|
||||
'flavor': 'm1.nano',
|
||||
'disk': 1,
|
||||
'source_val': 'c43a3e7d-c4e6-45d6-8c8d-e2832a45bc0a'}
|
||||
}]
|
||||
}}]
|
||||
|
||||
self.rating.get_quotation(res_data=res_data)
|
||||
self.api_client.post.assert_called_once_with(
|
||||
'/v1/rating/quote/', json={'resources': res_data})
|
||||
|
||||
def test_get_quotation_no_res_data(self):
|
||||
self.assertRaises(exc.ArgumentRequired, self.rating.get_quotation)
|
||||
63
cloudkittyclient/tests/unit/v1/test_report_cli.py
Normal file
63
cloudkittyclient/tests/unit/v1/test_report_cli.py
Normal file
@@ -0,0 +1,63 @@
|
||||
# Copyright 2018 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
import collections
|
||||
from unittest import mock
|
||||
|
||||
from cloudkittyclient.tests.unit.v1 import base
|
||||
from cloudkittyclient.v1 import report_cli
|
||||
|
||||
|
||||
class TestReportCli(base.BaseAPIEndpointTestCase):
|
||||
|
||||
def test_report_tenant_list(self):
|
||||
|
||||
class DummyAPIClient(object):
|
||||
def get_tenants(*args, **kwargs):
|
||||
return ['ee530dfc-319a-438f-9d43-346cfef501d6',
|
||||
'91743a9a-688b-4526-b568-7b501531176c',
|
||||
'4468704c-972e-4cfd-a342-9b71c493b79b']
|
||||
|
||||
class ClientWrap(object):
|
||||
report = DummyAPIClient()
|
||||
|
||||
class DummyParsedArgs(object):
|
||||
def __init__(self):
|
||||
self.begin = '2042-01-01T00:00:00'
|
||||
self.end = '2042-12-01T00:00:00'
|
||||
|
||||
class DummyCliTenantList(report_cli.CliTenantList):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def __get_client_from_osc(*args):
|
||||
return ClientWrap()
|
||||
|
||||
parsed_args = DummyParsedArgs()
|
||||
cli_class_instance = DummyCliTenantList()
|
||||
|
||||
with mock.patch('cloudkittyclient.utils.get_client_from_osc',
|
||||
new=__get_client_from_osc):
|
||||
# NOTE(peschk_l): self is only used used to get a client so just we
|
||||
# just override __init__ in order to skip class instanciation. In
|
||||
# python3 we could just have passed None
|
||||
result = report_cli.CliTenantList.take_action(
|
||||
cli_class_instance, parsed_args)
|
||||
|
||||
assert len(result) == 2
|
||||
assert result[0] == ('Tenant ID', )
|
||||
assert isinstance(result[1], collections.Iterable)
|
||||
|
||||
for res in result[1]:
|
||||
assert isinstance(res, collections.Iterable)
|
||||
0
cloudkittyclient/tests/unit/v2/__init__.py
Normal file
0
cloudkittyclient/tests/unit/v2/__init__.py
Normal file
31
cloudkittyclient/tests/unit/v2/base.py
Normal file
31
cloudkittyclient/tests/unit/v2/base.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# Copyright 2019 objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from cloudkittyclient.tests import utils
|
||||
from cloudkittyclient.v2 import dataframes
|
||||
from cloudkittyclient.v2.rating import modules
|
||||
from cloudkittyclient.v2 import scope
|
||||
from cloudkittyclient.v2 import summary
|
||||
|
||||
|
||||
class BaseAPIEndpointTestCase(utils.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(BaseAPIEndpointTestCase, self).setUp()
|
||||
self.api_client = utils.FakeHTTPClient()
|
||||
self.dataframes = dataframes.DataframesManager(self.api_client)
|
||||
self.scope = scope.ScopeManager(self.api_client)
|
||||
self.summary = summary.SummaryManager(self.api_client)
|
||||
self.rating = modules.RatingManager(self.api_client)
|
||||
172
cloudkittyclient/tests/unit/v2/test_dataframes.py
Normal file
172
cloudkittyclient/tests/unit/v2/test_dataframes.py
Normal file
@@ -0,0 +1,172 @@
|
||||
# Copyright 2019 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
import json
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from cloudkittyclient import exc
|
||||
from cloudkittyclient.tests.unit.v2 import base
|
||||
|
||||
|
||||
class TestDataframes(base.BaseAPIEndpointTestCase):
|
||||
dataframes_data = """
|
||||
{
|
||||
"dataframes": [
|
||||
{
|
||||
"period": {
|
||||
"begin": "20190723T122810Z",
|
||||
"end": "20190723T132810Z"
|
||||
},
|
||||
"usage": {
|
||||
"metric_one": [
|
||||
{
|
||||
"vol": {
|
||||
"unit": "GiB",
|
||||
"qty": 1.2
|
||||
},
|
||||
"rating": {
|
||||
"price": 0.04
|
||||
},
|
||||
"groupby": {
|
||||
"group_one": "one",
|
||||
"group_two": "two"
|
||||
},
|
||||
"metadata": {
|
||||
"attr_one": "one",
|
||||
"attr_two": "two"
|
||||
}
|
||||
}
|
||||
],
|
||||
"metric_two": [
|
||||
{
|
||||
"vol": {
|
||||
"unit": "MB",
|
||||
"qty": 200.4
|
||||
},
|
||||
"rating": {
|
||||
"price": 0.06
|
||||
},
|
||||
"groupby": {
|
||||
"group_one": "one",
|
||||
"group_two": "two"
|
||||
},
|
||||
"metadata": {
|
||||
"attr_one": "one",
|
||||
"attr_two": "two"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"period": {
|
||||
"begin": "20190823T122810Z",
|
||||
"end": "20190823T132810Z"
|
||||
},
|
||||
"usage": {
|
||||
"metric_one": [
|
||||
{
|
||||
"vol": {
|
||||
"unit": "GiB",
|
||||
"qty": 2.4
|
||||
},
|
||||
"rating": {
|
||||
"price": 0.08
|
||||
},
|
||||
"groupby": {
|
||||
"group_one": "one",
|
||||
"group_two": "two"
|
||||
},
|
||||
"metadata": {
|
||||
"attr_one": "one",
|
||||
"attr_two": "two"
|
||||
}
|
||||
}
|
||||
],
|
||||
"metric_two": [
|
||||
{
|
||||
"vol": {
|
||||
"unit": "MB",
|
||||
"qty": 400.8
|
||||
},
|
||||
"rating": {
|
||||
"price": 0.12
|
||||
},
|
||||
"groupby": {
|
||||
"group_one": "one",
|
||||
"group_two": "two"
|
||||
},
|
||||
"metadata": {
|
||||
"attr_one": "one",
|
||||
"attr_two": "two"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
|
||||
def test_add_dataframes_with_string(self):
|
||||
self.dataframes.add_dataframes(
|
||||
dataframes=self.dataframes_data,
|
||||
)
|
||||
self.api_client.post.assert_called_once_with(
|
||||
'/v2/dataframes',
|
||||
data=self.dataframes_data,
|
||||
)
|
||||
|
||||
def test_add_dataframes_with_json_object(self):
|
||||
json_data = json.loads(self.dataframes_data)
|
||||
|
||||
self.dataframes.add_dataframes(
|
||||
dataframes=json_data,
|
||||
)
|
||||
self.api_client.post.assert_called_once_with(
|
||||
'/v2/dataframes',
|
||||
data=json.dumps(json_data),
|
||||
)
|
||||
|
||||
def test_add_dataframes_with_neither_string_nor_object_raises_exc(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidArgumentError,
|
||||
self.dataframes.add_dataframes,
|
||||
dataframes=[open],
|
||||
)
|
||||
|
||||
def test_add_dataframes_with_no_args_raises_exc(self):
|
||||
self.assertRaises(
|
||||
exc.ArgumentRequired,
|
||||
self.dataframes.add_dataframes)
|
||||
|
||||
def test_get_dataframes(self):
|
||||
self.dataframes.get_dataframes()
|
||||
self.api_client.get.assert_called_once_with('/v2/dataframes')
|
||||
|
||||
def test_get_dataframes_with_pagination_args(self):
|
||||
self.dataframes.get_dataframes(offset=10, limit=10)
|
||||
try:
|
||||
self.api_client.get.assert_called_once_with(
|
||||
'/v2/dataframes?limit=10&offset=10')
|
||||
except AssertionError:
|
||||
self.api_client.get.assert_called_once_with(
|
||||
'/v2/dataframes?offset=10&limit=10')
|
||||
|
||||
def test_get_dataframes_filters(self):
|
||||
self.dataframes.get_dataframes(
|
||||
filters=OrderedDict([('one', 'two'), ('three', 'four')]))
|
||||
self.api_client.get.assert_called_once_with(
|
||||
'/v2/dataframes?filters=one%3Atwo%2Cthree%3Afour')
|
||||
38
cloudkittyclient/tests/unit/v2/test_rating.py
Normal file
38
cloudkittyclient/tests/unit/v2/test_rating.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# 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,
|
||||
})
|
||||
88
cloudkittyclient/tests/unit/v2/test_scope.py
Normal file
88
cloudkittyclient/tests/unit/v2/test_scope.py
Normal file
@@ -0,0 +1,88 @@
|
||||
# Copyright 2019 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient import exc
|
||||
from cloudkittyclient.tests.unit.v2 import base
|
||||
import datetime
|
||||
|
||||
|
||||
class TestScope(base.BaseAPIEndpointTestCase):
|
||||
|
||||
def test_get_scope(self):
|
||||
self.scope.get_scope_state()
|
||||
self.api_client.get.assert_called_once_with('/v2/scope')
|
||||
|
||||
def test_get_scope_with_args(self):
|
||||
self.scope.get_scope_state(offset=10, limit=10)
|
||||
try:
|
||||
self.api_client.get.assert_called_once_with(
|
||||
'/v2/scope?limit=10&offset=10')
|
||||
except AssertionError:
|
||||
self.api_client.get.assert_called_once_with(
|
||||
'/v2/scope?offset=10&limit=10')
|
||||
|
||||
def test_reset_scope_with_args(self):
|
||||
self.scope.reset_scope_state(
|
||||
state=datetime.datetime(2019, 5, 7),
|
||||
all_scopes=True)
|
||||
self.api_client.put.assert_called_once_with(
|
||||
'/v2/scope',
|
||||
json={
|
||||
'state': datetime.datetime(2019, 5, 7),
|
||||
'all_scopes': True,
|
||||
})
|
||||
|
||||
def test_reset_scope_with_list_args(self):
|
||||
self.scope.reset_scope_state(
|
||||
state=datetime.datetime(2019, 5, 7),
|
||||
scope_id=['id1', 'id2'],
|
||||
all_scopes=False)
|
||||
self.api_client.put.assert_called_once_with(
|
||||
'/v2/scope',
|
||||
json={
|
||||
'state': datetime.datetime(2019, 5, 7),
|
||||
'scope_id': 'id1,id2',
|
||||
})
|
||||
|
||||
def test_reset_scope_strips_none_and_false_args(self):
|
||||
self.scope.reset_scope_state(
|
||||
state=datetime.datetime(2019, 5, 7),
|
||||
all_scopes=False,
|
||||
scope_key=None,
|
||||
scope_id=['id1', 'id2'])
|
||||
self.api_client.put.assert_called_once_with(
|
||||
'/v2/scope',
|
||||
json={
|
||||
'state': datetime.datetime(2019, 5, 7),
|
||||
'scope_id': 'id1,id2',
|
||||
})
|
||||
|
||||
def test_reset_scope_with_no_args_raises_exc(self):
|
||||
self.assertRaises(
|
||||
exc.ArgumentRequired,
|
||||
self.scope.reset_scope_state)
|
||||
|
||||
def test_reset_scope_with_lacking_args_raises_exc(self):
|
||||
self.assertRaises(
|
||||
exc.ArgumentRequired,
|
||||
self.scope.reset_scope_state,
|
||||
state=datetime.datetime(2019, 5, 7))
|
||||
|
||||
def test_reset_scope_with_both_args_raises_exc(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidArgumentError,
|
||||
self.scope.reset_scope_state,
|
||||
state=datetime.datetime(2019, 5, 7),
|
||||
scope_id=['id1', 'id2'],
|
||||
all_scopes=True)
|
||||
39
cloudkittyclient/tests/unit/v2/test_summary.py
Normal file
39
cloudkittyclient/tests/unit/v2/test_summary.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# Copyright 2019 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from collections import OrderedDict
|
||||
|
||||
from cloudkittyclient.tests.unit.v2 import base
|
||||
|
||||
|
||||
class TestSummary(base.BaseAPIEndpointTestCase):
|
||||
|
||||
def test_get_summary(self):
|
||||
self.summary.get_summary()
|
||||
self.api_client.get.assert_called_once_with('/v2/summary')
|
||||
|
||||
def test_get_summary_with_pagination_args(self):
|
||||
self.summary.get_summary(offset=10, limit=10)
|
||||
try:
|
||||
self.api_client.get.assert_called_once_with(
|
||||
'/v2/summary?limit=10&offset=10')
|
||||
except AssertionError:
|
||||
self.api_client.get.assert_called_once_with(
|
||||
'/v2/summary?offset=10&limit=10')
|
||||
|
||||
def test_get_summary_filters(self):
|
||||
self.summary.get_summary(
|
||||
filters=OrderedDict([('one', 'two'), ('three', 'four')]))
|
||||
self.api_client.get.assert_called_once_with(
|
||||
'/v2/summary?filters=one%3Atwo%2Cthree%3Afour')
|
||||
@@ -12,12 +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 unittest import mock
|
||||
|
||||
import fixtures
|
||||
import testtools
|
||||
|
||||
from keystoneauth1 import adapter
|
||||
from keystoneauth1 import session
|
||||
import mock
|
||||
|
||||
|
||||
class BaseTestCase(testtools.TestCase):
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@@ -13,8 +12,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
import inspect
|
||||
import sys
|
||||
|
||||
import pbr.version
|
||||
|
||||
from keystoneauth1.exceptions import http
|
||||
from oslo_utils import timeutils
|
||||
|
||||
|
||||
@@ -56,3 +59,44 @@ def list_to_cols(list_obj, cols):
|
||||
for item in list_obj:
|
||||
values.append(dict_to_cols(item, cols))
|
||||
return values
|
||||
|
||||
|
||||
def http_error_formatter(func):
|
||||
"""This decorator catches Http Errors and re-formats them"""
|
||||
|
||||
def wrap(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except http.HttpError as e:
|
||||
raise http.HttpError(message=e.response.text,
|
||||
http_status=e.http_status)
|
||||
|
||||
return wrap
|
||||
|
||||
|
||||
def format_http_errors(ignore):
|
||||
"""Applies ``http_error_formatter`` to all methods of a class.
|
||||
|
||||
:param ignore: List of function names to ignore
|
||||
:type ignore: iterable
|
||||
"""
|
||||
|
||||
def wrap(cls):
|
||||
# If you want pretty errors, use python3.
|
||||
# __qualname__ does not exist in python 2
|
||||
if sys.version_info.major < 3:
|
||||
return cls
|
||||
|
||||
def predicate(item):
|
||||
# This avoids decorating functions of parent classes
|
||||
return (inspect.isfunction(item)
|
||||
and item.__name__ not in ignore
|
||||
and not item.__name__.startswith('_')
|
||||
and cls.__name__ in item.__qualname__)
|
||||
|
||||
for name, func in inspect.getmembers(cls, predicate):
|
||||
setattr(cls, name, http_error_formatter(func))
|
||||
|
||||
return cls
|
||||
|
||||
return wrap
|
||||
|
||||
@@ -13,9 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from keystoneauth1 import adapter
|
||||
from keystoneauth1 import session as ks_session
|
||||
|
||||
from cloudkittyclient.common import client
|
||||
from cloudkittyclient.v1 import collector
|
||||
from cloudkittyclient.v1 import info
|
||||
from cloudkittyclient.v1 import rating
|
||||
@@ -23,17 +21,22 @@ from cloudkittyclient.v1 import report
|
||||
from cloudkittyclient.v1 import storage
|
||||
|
||||
|
||||
class Client(object):
|
||||
class Client(client.BaseClient):
|
||||
|
||||
def __init__(self, session=None, adapter_options={}, **kwargs):
|
||||
adapter_options.setdefault('service_type', 'rating')
|
||||
def __init__(self,
|
||||
session=None,
|
||||
adapter_options={},
|
||||
cacert=None,
|
||||
insecure=False,
|
||||
**kwargs):
|
||||
super(Client, self).__init__(
|
||||
session=session,
|
||||
adapter_options=adapter_options,
|
||||
cacert=cacert,
|
||||
insecure=insecure,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
self.session = session
|
||||
if self.session is None:
|
||||
self.session = ks_session.Session(**kwargs)
|
||||
|
||||
self.api_client = adapter.Adapter(
|
||||
session=self.session, **adapter_options)
|
||||
self.info = info.InfoManager(self.api_client)
|
||||
self.collector = collector.CollectorManager(self.api_client)
|
||||
self.rating = rating.RatingManager(self.api_client)
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
#
|
||||
from oslo_log import log
|
||||
|
||||
from cloudkittyclient.common import base
|
||||
from cloudkittyclient import exc
|
||||
from cloudkittyclient.v1 import base
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient.v1 import base
|
||||
from cloudkittyclient.common import base
|
||||
|
||||
|
||||
class InfoManager(base.BaseManager):
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
#
|
||||
from cliff import lister
|
||||
|
||||
from cloudkittyclient.common import base
|
||||
from cloudkittyclient import exc
|
||||
from cloudkittyclient import utils
|
||||
from cloudkittyclient.v1 import base
|
||||
from cloudkittyclient.v1.rating import hashmap
|
||||
from cloudkittyclient.v1.rating import pyscripts
|
||||
|
||||
@@ -78,8 +78,10 @@ class RatingManager(base.BaseManager):
|
||||
"""
|
||||
if not kwargs.get('res_data', None):
|
||||
raise exc.ArgumentRequired("'res_data' argument is required.")
|
||||
url = self.get_url('quote')
|
||||
return self.api_client.post(url, kwargs['res_data'])
|
||||
url = self.get_url('quote', {})
|
||||
|
||||
body = {'resources': kwargs['res_data']}
|
||||
return self.api_client.post(url, json=body).json()
|
||||
|
||||
|
||||
class CliModuleGet(lister.Lister):
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient.common import base
|
||||
from cloudkittyclient import exc
|
||||
from cloudkittyclient.v1 import base
|
||||
|
||||
|
||||
class HashmapManager(base.BaseManager):
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient.common import base
|
||||
from cloudkittyclient import exc
|
||||
from cloudkittyclient.v1 import base
|
||||
|
||||
|
||||
class PyscriptManager(base.BaseManager):
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#
|
||||
from oslo_log import log
|
||||
|
||||
from cloudkittyclient.v1 import base
|
||||
from cloudkittyclient.common import base
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
@@ -114,8 +114,11 @@ class CliTenantList(lister.Lister):
|
||||
raise exc.InvalidArgumentError(
|
||||
'Invalid timestamp "{}"'.format(value))
|
||||
client = utils.get_client_from_osc(self)
|
||||
return (('Tenant ID', ),
|
||||
(client.report.get_tenants(**vars(parsed_args)), ))
|
||||
tenants = client.report.get_tenants(**vars(parsed_args))
|
||||
output = []
|
||||
for tenant in tenants:
|
||||
output.append((tenant, ))
|
||||
return (('Tenant ID', ), output)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliTenantList, self).get_parser(prog_name)
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient.v1 import base
|
||||
from cloudkittyclient.common import base
|
||||
|
||||
|
||||
class StorageManager(base.BaseManager):
|
||||
|
||||
0
cloudkittyclient/v2/__init__.py
Normal file
0
cloudkittyclient/v2/__init__.py
Normal file
44
cloudkittyclient/v2/client.py
Normal file
44
cloudkittyclient/v2/client.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient.v1 import client
|
||||
from cloudkittyclient.v2 import dataframes
|
||||
from cloudkittyclient.v2.rating import modules
|
||||
from cloudkittyclient.v2 import scope
|
||||
from cloudkittyclient.v2 import summary
|
||||
|
||||
|
||||
# NOTE(peschk_l) v2 client needs to implement v1 until the v1 API has been
|
||||
# completely ported to v2
|
||||
class Client(client.Client):
|
||||
|
||||
def __init__(self,
|
||||
session=None,
|
||||
adapter_options={},
|
||||
cacert=None,
|
||||
insecure=False,
|
||||
**kwargs):
|
||||
super(Client, self).__init__(
|
||||
session=session,
|
||||
adapter_options=adapter_options,
|
||||
cacert=cacert,
|
||||
insecure=insecure,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
self.dataframes = dataframes.DataframesManager(self.api_client)
|
||||
self.scope = scope.ScopeManager(self.api_client)
|
||||
self.summary = summary.SummaryManager(self.api_client)
|
||||
self.rating = modules.RatingManager(self.api_client)
|
||||
77
cloudkittyclient/v2/dataframes.py
Normal file
77
cloudkittyclient/v2/dataframes.py
Normal file
@@ -0,0 +1,77 @@
|
||||
# Copyright 2019 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
import json
|
||||
|
||||
from cloudkittyclient.common import base
|
||||
from cloudkittyclient import exc
|
||||
|
||||
|
||||
class DataframesManager(base.BaseManager):
|
||||
"""Class used to handle /v2/dataframes endpoint"""
|
||||
|
||||
url = '/v2/dataframes'
|
||||
|
||||
def add_dataframes(self, **kwargs):
|
||||
"""Add DataFrames to the storage backend. Returns nothing.
|
||||
|
||||
:param dataframes: List of dataframes to add to the storage backend.
|
||||
:type dataframes: list of dataframes
|
||||
"""
|
||||
|
||||
dataframes = kwargs.get('dataframes')
|
||||
|
||||
if not dataframes:
|
||||
raise exc.ArgumentRequired("'dataframes' argument is required")
|
||||
|
||||
if not isinstance(dataframes, str):
|
||||
try:
|
||||
dataframes = json.dumps(dataframes)
|
||||
except TypeError:
|
||||
raise exc.InvalidArgumentError(
|
||||
"'dataframes' must be either a string"
|
||||
"or a JSON serializable object.")
|
||||
|
||||
url = self.get_url(None, kwargs)
|
||||
return self.api_client.post(
|
||||
url,
|
||||
data=dataframes,
|
||||
)
|
||||
|
||||
def get_dataframes(self, **kwargs):
|
||||
"""Returns a paginated list of DataFrames.
|
||||
|
||||
This support filters and datetime framing.
|
||||
|
||||
:param offset: Index of the first dataframe that should be returned.
|
||||
:type offset: int
|
||||
:param limit: Maximal number of dataframes to return.
|
||||
:type limit: int
|
||||
:param filters: Optional dict of filters to select data on.
|
||||
:type filters: dict
|
||||
:param begin: Start of the period to gather data from
|
||||
:type begin: datetime.datetime
|
||||
:param end: End of the period to gather data from
|
||||
:type end: datetime.datetime
|
||||
"""
|
||||
kwargs['filters'] = ','.join(
|
||||
'{}:{}'.format(k, v) for k, v in
|
||||
(kwargs.get('filters', None) or {}).items()
|
||||
)
|
||||
|
||||
authorized_args = [
|
||||
'offset', 'limit', 'filters', 'begin', 'end']
|
||||
|
||||
url = self.get_url(None, kwargs, authorized_args=authorized_args)
|
||||
return self.api_client.get(url).json()
|
||||
116
cloudkittyclient/v2/dataframes_cli.py
Normal file
116
cloudkittyclient/v2/dataframes_cli.py
Normal file
@@ -0,0 +1,116 @@
|
||||
# Copyright 2019 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
import argparse
|
||||
|
||||
from cliff import command
|
||||
from cliff import lister
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from cloudkittyclient import utils
|
||||
|
||||
|
||||
class CliDataframesAdd(command.Command):
|
||||
"""Add one or several DataFrame objects to the storage backend."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliDataframesAdd, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'datafile',
|
||||
type=argparse.FileType('r'),
|
||||
help="File formatted as a JSON object having a DataFrame list"
|
||||
"under a 'dataframes' key."
|
||||
"'-' (hyphen) can be specified for using stdin.",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
with parsed_args.datafile as dfile:
|
||||
dataframes = dfile.read()
|
||||
utils.get_client_from_osc(self).dataframes.add_dataframes(
|
||||
dataframes=dataframes,
|
||||
)
|
||||
|
||||
|
||||
class CliDataframesGet(lister.Lister):
|
||||
"""Get dataframes from the storage backend."""
|
||||
columns = [
|
||||
('begin', 'Begin'),
|
||||
('end', 'End'),
|
||||
('metric', 'Metric Type'),
|
||||
('unit', 'Unit'),
|
||||
('qty', 'Quantity'),
|
||||
('price', 'Price'),
|
||||
('groupby', 'Group By'),
|
||||
('metadata', 'Metadata'),
|
||||
]
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliDataframesGet, self).get_parser(prog_name)
|
||||
|
||||
def filter_(elem):
|
||||
if len(elem.split(':')) != 2:
|
||||
raise TypeError
|
||||
return str(elem)
|
||||
|
||||
parser.add_argument('--offset', type=int, default=0,
|
||||
help='Index of the first dataframe')
|
||||
parser.add_argument('--limit', type=int, default=100,
|
||||
help='Maximal number of dataframes')
|
||||
parser.add_argument('--filter', type=filter_, action='append',
|
||||
help="Optional filter, in 'key:value' format. Can "
|
||||
"be specified several times.")
|
||||
parser.add_argument('-b', '--begin', type=timeutils.parse_isotime,
|
||||
help="Start of the period to query, in iso8601 "
|
||||
"format. Example: 2019-05-01T00:00:00Z.")
|
||||
parser.add_argument('-e', '--end', type=timeutils.parse_isotime,
|
||||
help="End of the period to query, in iso8601 "
|
||||
"format. Example: 2019-06-01T00:00:00Z.")
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
filters = dict(elem.split(':') for elem in (parsed_args.filter or []))
|
||||
|
||||
dataframes = utils.get_client_from_osc(self).dataframes.get_dataframes(
|
||||
offset=parsed_args.offset,
|
||||
limit=parsed_args.limit,
|
||||
begin=parsed_args.begin,
|
||||
end=parsed_args.end,
|
||||
filters=filters,
|
||||
).get('dataframes', [])
|
||||
|
||||
def format_(d):
|
||||
return ' '.join([
|
||||
'{}="{}"'.format(k, v) for k, v in (d or {}).items()])
|
||||
|
||||
values = []
|
||||
for df in dataframes:
|
||||
period = df['period']
|
||||
usage = df['usage']
|
||||
for metric_type, points in usage.items():
|
||||
for point in points:
|
||||
values.append([
|
||||
period['begin'],
|
||||
period['end'],
|
||||
metric_type,
|
||||
point['vol']['unit'],
|
||||
point['vol']['qty'],
|
||||
point['rating']['price'],
|
||||
format_(point.get('groupby', {})),
|
||||
format_(point.get('metadata', {})),
|
||||
])
|
||||
|
||||
return [col[1] for col in self.columns], values
|
||||
0
cloudkittyclient/v2/rating/__init__.py
Normal file
0
cloudkittyclient/v2/rating/__init__.py
Normal file
59
cloudkittyclient/v2/rating/modules.py
Normal file
59
cloudkittyclient/v2/rating/modules.py
Normal file
@@ -0,0 +1,59 @@
|
||||
|
||||
# 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)
|
||||
29
cloudkittyclient/v2/rating/modules_cli.py
Normal file
29
cloudkittyclient/v2/rating/modules_cli.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# 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']
|
||||
102
cloudkittyclient/v2/scope.py
Normal file
102
cloudkittyclient/v2/scope.py
Normal file
@@ -0,0 +1,102 @@
|
||||
# Copyright 2019 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient.common import base
|
||||
from cloudkittyclient import exc
|
||||
|
||||
|
||||
class ScopeManager(base.BaseManager):
|
||||
"""Class used to handle /v2/scope endpoint"""
|
||||
|
||||
url = '/v2/scope'
|
||||
|
||||
def get_scope_state(self, **kwargs):
|
||||
"""Returns a paginated list of scopes along with their state.
|
||||
|
||||
Some optional filters can be provided.
|
||||
|
||||
:param offset: Index of the first scope that should be returned.
|
||||
:type offset: int
|
||||
:param limit: Maximal number of scopes to return.
|
||||
:type limit: int
|
||||
:param collector: Optional collector to filter on.
|
||||
:type collector: str or list of str
|
||||
:param fetcher: Optional fetcher to filter on.
|
||||
:type fetcher: str or list of str
|
||||
:param scope_id: Optional scope_id to filter on.
|
||||
:type scope_id: str or list of str
|
||||
:param scope_key: Optional scope_key to filter on.
|
||||
:type scope_key: str or list of str
|
||||
"""
|
||||
|
||||
for key in ('collector', 'fetcher', 'scope_id', 'scope_key'):
|
||||
if key in kwargs.keys():
|
||||
if isinstance(kwargs[key], list):
|
||||
kwargs[key] = ','.join(kwargs[key])
|
||||
|
||||
authorized_args = [
|
||||
'offset', 'limit', 'collector', 'fetcher', 'scope_id', 'scope_key']
|
||||
url = self.get_url(None, kwargs, authorized_args=authorized_args)
|
||||
return self.api_client.get(url).json()
|
||||
|
||||
def reset_scope_state(self, **kwargs):
|
||||
"""Returns nothing.
|
||||
|
||||
Some optional filters can be provided.
|
||||
The all_scopes and the scope_id options are mutually exclusive and one
|
||||
must be provided.
|
||||
|
||||
:param state: datetime object from which the state will be reset
|
||||
:type state: datetime.datetime
|
||||
:param all_scopes: Whether all scopes must be reset
|
||||
:type all_scopes: bool
|
||||
:param collector: Optional collector to filter on.
|
||||
:type collector: str or list of str
|
||||
:param fetcher: Optional fetcher to filter on.
|
||||
:type fetcher: str or list of str
|
||||
:param scope_id: Optional scope_id to filter on.
|
||||
:type scope_id: str or list of str
|
||||
:param scope_key: Optional scope_key to filter on.
|
||||
:type scope_key: str or list of str
|
||||
"""
|
||||
|
||||
if not kwargs.get('state'):
|
||||
raise exc.ArgumentRequired("'state' argument is required")
|
||||
|
||||
if not kwargs.get('all_scopes') and not kwargs.get('scope_id'):
|
||||
raise exc.ArgumentRequired(
|
||||
"You must specify either 'scope_id' or 'all_scopes'")
|
||||
|
||||
if kwargs.get('all_scopes') and kwargs.get('scope_id'):
|
||||
raise exc.InvalidArgumentError(
|
||||
"You can't specify both 'scope_id' and 'all_scopes'")
|
||||
|
||||
for key in ('collector', 'fetcher', 'scope_id', 'scope_key'):
|
||||
if key in kwargs.keys():
|
||||
if isinstance(kwargs[key], list):
|
||||
kwargs[key] = ','.join(kwargs[key])
|
||||
|
||||
body = dict(
|
||||
state=kwargs.get('state'),
|
||||
scope_id=kwargs.get('scope_id'),
|
||||
scope_key=kwargs.get('scope_key'),
|
||||
collector=kwargs.get('collector'),
|
||||
fetcher=kwargs.get('fetcher'),
|
||||
all_scopes=kwargs.get('all_scopes'),
|
||||
)
|
||||
# Stripping None and False values
|
||||
body = dict(filter(lambda elem: bool(elem[1]), body.items()))
|
||||
|
||||
url = self.get_url(None, kwargs)
|
||||
return self.api_client.put(url, json=body)
|
||||
98
cloudkittyclient/v2/scope_cli.py
Normal file
98
cloudkittyclient/v2/scope_cli.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# Copyright 2019 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cliff import command
|
||||
from cliff import lister
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from cloudkittyclient import utils
|
||||
|
||||
|
||||
class CliScopeStateGet(lister.Lister):
|
||||
"""Get information about current state of several scopes."""
|
||||
info_columns = [
|
||||
('scope_id', 'Scope ID'),
|
||||
('scope_key', 'Scope Key'),
|
||||
('collector', 'Collector'),
|
||||
('fetcher', 'Fetcher'),
|
||||
('state', 'State')
|
||||
]
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliScopeStateGet, self).get_parser(prog_name)
|
||||
|
||||
for col in self.info_columns[:-1]:
|
||||
parser.add_argument(
|
||||
'--' + col[0].replace('_', '-'), type=str,
|
||||
action='append', help='Optional filter on ' + col[1])
|
||||
|
||||
parser.add_argument('--offset', type=int, default=0,
|
||||
help='Index of the first scope')
|
||||
parser.add_argument('--limit', type=int, default=100,
|
||||
help='Maximal number of scopes')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
resp = utils.get_client_from_osc(self).scope.get_scope_state(
|
||||
offset=parsed_args.offset,
|
||||
limit=parsed_args.limit,
|
||||
collector=parsed_args.collector,
|
||||
fetcher=parsed_args.fetcher,
|
||||
scope_id=parsed_args.scope_id,
|
||||
scope_key=parsed_args.scope_key,
|
||||
)
|
||||
values = utils.list_to_cols(resp['results'], self.info_columns)
|
||||
return [col[1] for col in self.info_columns], values
|
||||
|
||||
|
||||
class CliScopeStateReset(command.Command):
|
||||
"""Reset the state of several scopes."""
|
||||
info_columns = [
|
||||
('scope_id', 'Scope ID'),
|
||||
('scope_key', 'Scope Key'),
|
||||
('collector', 'Collector'),
|
||||
('fetcher', 'Fetcher'),
|
||||
]
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliScopeStateReset, self).get_parser(prog_name)
|
||||
|
||||
for col in self.info_columns:
|
||||
parser.add_argument(
|
||||
'--' + col[0].replace('_', '-'), type=str,
|
||||
action='append', help='Optional filter on ' + col[1])
|
||||
|
||||
parser.add_argument(
|
||||
'-a', '--all-scopes',
|
||||
action='store_true',
|
||||
help="Target all scopes at once")
|
||||
|
||||
parser.add_argument(
|
||||
'state',
|
||||
type=timeutils.parse_isotime,
|
||||
help="State iso8601 datetime to which the state should be set. "
|
||||
"Example: 2019-06-01T00:00:00Z.")
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
utils.get_client_from_osc(self).scope.reset_scope_state(
|
||||
collector=parsed_args.collector,
|
||||
fetcher=parsed_args.fetcher,
|
||||
scope_id=parsed_args.scope_id,
|
||||
scope_key=parsed_args.scope_key,
|
||||
all_scopes=parsed_args.all_scopes,
|
||||
state=parsed_args.state,
|
||||
)
|
||||
52
cloudkittyclient/v2/summary.py
Normal file
52
cloudkittyclient/v2/summary.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# Copyright 2019 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cloudkittyclient.common import base
|
||||
|
||||
|
||||
class SummaryManager(base.BaseManager):
|
||||
|
||||
url = '/v2/summary'
|
||||
|
||||
def get_summary(self, **kwargs):
|
||||
"""Returns a paginated list of summaries.
|
||||
|
||||
This support filters along with custom grouping.
|
||||
|
||||
:param offset: Index of the first scope that should be returned.
|
||||
:type offset: int
|
||||
:param limit: Maximal number of scopes to return.
|
||||
:type limit: int
|
||||
:param filters: Optional dict of filters to select data on.
|
||||
:type filters: dict
|
||||
:param groupby: Optional list of attributes to group data on.
|
||||
:type groupby: str or list of str.
|
||||
:param begin: Start of the period to gather data from
|
||||
:type begin: datetime.datetime
|
||||
:param end: End of the period to gather data from
|
||||
:type end: datetime.datetime
|
||||
"""
|
||||
if 'groupby' in kwargs.keys() and isinstance(kwargs['groupby'], list):
|
||||
kwargs['groupby'] = ','.join(kwargs['groupby'])
|
||||
|
||||
kwargs['filters'] = ','.join(
|
||||
'{}:{}'.format(k, v) for k, v in
|
||||
(kwargs.get('filters', None) or {}).items()
|
||||
)
|
||||
|
||||
authorized_args = [
|
||||
'offset', 'limit', 'filters', 'groupby', 'begin', 'end']
|
||||
|
||||
url = self.get_url(None, kwargs, authorized_args=authorized_args)
|
||||
return self.api_client.get(url).json()
|
||||
62
cloudkittyclient/v2/summary_cli.py
Normal file
62
cloudkittyclient/v2/summary_cli.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# Copyright 2019 Objectif Libre
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from cliff import lister
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from cloudkittyclient import utils
|
||||
|
||||
|
||||
class CliSummaryGet(lister.Lister):
|
||||
"""Get a summary for a given period."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliSummaryGet, self).get_parser(prog_name)
|
||||
|
||||
def filter_(elem):
|
||||
if len(elem.split(':')) != 2:
|
||||
raise TypeError
|
||||
return str(elem)
|
||||
|
||||
parser.add_argument('--offset', type=int, default=0,
|
||||
help='Index of the first element')
|
||||
parser.add_argument('--limit', type=int, default=100,
|
||||
help='Maximal number of elements')
|
||||
parser.add_argument('-g', '--groupby', type=str, action='append',
|
||||
help='Attribute to group the summary by. Can be '
|
||||
'specified several times')
|
||||
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 []))
|
||||
resp = utils.get_client_from_osc(self).summary.get_summary(
|
||||
offset=parsed_args.offset,
|
||||
limit=parsed_args.limit,
|
||||
begin=parsed_args.begin,
|
||||
end=parsed_args.end,
|
||||
filters=filters,
|
||||
groupby=parsed_args.groupby,
|
||||
)
|
||||
columns = [c.replace('_', ' ').capitalize() for c in resp['columns']]
|
||||
return columns, resp['results']
|
||||
@@ -2,6 +2,8 @@
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
openstackdocstheme>=1.18.1 # Apache-2.0
|
||||
sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
|
||||
reno>=2.5.0 # Apache2
|
||||
openstackdocstheme>=1.30.0 # Apache-2.0
|
||||
sphinx>=1.8.0,!=2.1.0 # BSD
|
||||
sphinxcontrib-svg2pdfconverter>=0.1.0 # BSD
|
||||
reno>=2.5.0 # Apache-2.0
|
||||
cliff>=2.11.0 # Apache-2.0
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
=============
|
||||
Api Reference
|
||||
API Reference
|
||||
=============
|
||||
|
||||
A ``client.Client`` instance has the following submodules (each one
|
||||
corresponding to an API endpoint):
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
report
|
||||
info
|
||||
rating
|
||||
collector
|
||||
storage
|
||||
v1/index
|
||||
v2/index
|
||||
|
||||
12
doc/source/api_reference/v1/index.rst
Normal file
12
doc/source/api_reference/v1/index.rst
Normal file
@@ -0,0 +1,12 @@
|
||||
=======================
|
||||
V1 API client reference
|
||||
=======================
|
||||
|
||||
A ``client.v1.Client`` instance has the following submodules (each one
|
||||
corresponding to an API endpoint):
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
./*
|
||||
6
doc/source/api_reference/v2/dataframes.rst
Normal file
6
doc/source/api_reference/v2/dataframes.rst
Normal file
@@ -0,0 +1,6 @@
|
||||
===========================
|
||||
dataframes (/v2/dataframes)
|
||||
===========================
|
||||
|
||||
.. automodule:: cloudkittyclient.v2.dataframes
|
||||
:members:
|
||||
16
doc/source/api_reference/v2/index.rst
Normal file
16
doc/source/api_reference/v2/index.rst
Normal file
@@ -0,0 +1,16 @@
|
||||
=======================
|
||||
V2 API client reference
|
||||
=======================
|
||||
|
||||
In addition to the modules available in a ``client.v1.Client`` instance, a
|
||||
``client.v2.Client`` instance has the following submodules (each one
|
||||
corresponding to an API endpoint):
|
||||
|
||||
.. note:: Some modules of the ``client.v2.Client`` replace v1 modules with
|
||||
their v2 alternative (for example) ``summary``.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
./*
|
||||
6
doc/source/api_reference/v2/scope.rst
Normal file
6
doc/source/api_reference/v2/scope.rst
Normal file
@@ -0,0 +1,6 @@
|
||||
=================
|
||||
scope (/v2/scope)
|
||||
=================
|
||||
|
||||
.. automodule:: cloudkittyclient.v2.scope
|
||||
:members:
|
||||
6
doc/source/api_reference/v2/summary.rst
Normal file
6
doc/source/api_reference/v2/summary.rst
Normal file
@@ -0,0 +1,6 @@
|
||||
=====================
|
||||
summary (/v2/summary)
|
||||
=====================
|
||||
|
||||
.. automodule:: cloudkittyclient.v2.summary
|
||||
:members:
|
||||
@@ -2,6 +2,20 @@
|
||||
CLI Reference
|
||||
=============
|
||||
|
||||
.. autoprogram-cliff:: cloudkittyclient
|
||||
V1 Client
|
||||
=========
|
||||
|
||||
.. autoprogram-cliff:: cloudkittyclient_v1
|
||||
:application: cloudkitty
|
||||
:ignored: --format, --column, --max-width, --fit-width, --print-empty, --format-config-file, --noindent, --quote, --sort-column
|
||||
|
||||
V2 Client
|
||||
=========
|
||||
|
||||
.. autoprogram-cliff:: cloudkittyclient_v2
|
||||
:command: dataframes add
|
||||
|
||||
.. autoprogram-cliff:: cloudkittyclient_v2
|
||||
:command: scope state get
|
||||
|
||||
.. autoprogram-cliff:: cloudkittyclient_v2
|
||||
:command: summary get
|
||||
|
||||
@@ -23,6 +23,14 @@ extensions = [
|
||||
'cliff.sphinxext',
|
||||
'sphinx.ext.autodoc',
|
||||
'openstackdocstheme',
|
||||
'sphinxcontrib.rsvgconverter',
|
||||
]
|
||||
|
||||
autoprogram_cliff_application = 'cloudkitty'
|
||||
|
||||
autoprogram_cliff_ignored = [
|
||||
"--format", "--column", "--max-width", "--fit-width", "--print-empty",
|
||||
"--format-config-file", "--noindent", "--quote", "--sort-column",
|
||||
]
|
||||
|
||||
# autodoc generation is a bit aggressive and a nuisance when doing heavy
|
||||
@@ -52,7 +60,7 @@ add_function_parentheses = True
|
||||
add_module_names = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
pygments_style = 'native'
|
||||
|
||||
# -- Options for HTML output --------------------------------------------------
|
||||
|
||||
@@ -69,14 +77,30 @@ 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',
|
||||
'%s.tex' % project,
|
||||
'doc-%s.tex' % project,
|
||||
u'%s Documentation' % project,
|
||||
u'OpenStack Foundation', 'manual'),
|
||||
u'OpenStack Foundation', 'howto', True),
|
||||
]
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
|
||||
@@ -2,6 +2,61 @@
|
||||
Usage
|
||||
=====
|
||||
|
||||
CLI
|
||||
===
|
||||
|
||||
Authentication
|
||||
--------------
|
||||
|
||||
The CloudKitty client can either be used through the standalone CLI executable
|
||||
(``cloudkitty``) or through the OpenStack Client module (``openstack rating``).
|
||||
|
||||
When using CloudKitty in standalone mode (ie without Keystone authentication),
|
||||
the API endpoint and the auth method must be specified:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
cloudkitty --os-endpoint http://cloudkitty-api:8889 --os-auth-type cloudkitty-noauth module list
|
||||
|
||||
These options can also be specified as environment variables:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
export OS_ENDPOINT=http://cloudkitty-api:8889
|
||||
export OS_AUTH_TYPE=cloudkitty-noauth
|
||||
cloudkitty module list
|
||||
|
||||
The exact same options apply when using the OpenStack Client plugin:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
# EITHER
|
||||
openstack rating --os-endpoint http://cloudkitty-api:8889 --os-auth-type cloudkitty-noauth module list
|
||||
|
||||
# OR
|
||||
export OS_ENDPOINT=http://cloudkitty-api:8889
|
||||
export OS_AUTH_TYPE=cloudkitty-noauth
|
||||
openstack rating module list
|
||||
|
||||
Version
|
||||
-------
|
||||
|
||||
Two versions of the client exist: v1 and v2. The v2 version adds support for
|
||||
v2 API endpoints. The default API version is 1. You can specify which API
|
||||
version you want to use via a CLI option:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
# EITHER
|
||||
cloudkitty --os-rating-api-version 2 summary get
|
||||
|
||||
# OR
|
||||
export OS_RATING_API_VERSION=2
|
||||
cloudkitty summary get
|
||||
|
||||
Again, the option can also be provided to the OSC plugin, both via the CLI
|
||||
flag or the environment variable.
|
||||
|
||||
Python library
|
||||
==============
|
||||
|
||||
@@ -49,6 +104,23 @@ Else, use it the same way as any other OpenStack client::
|
||||
u'res_type': u'ALL',
|
||||
u'tenant_id': u'bea6a24f77e946b0a92dca7c78b7870b'}]}
|
||||
|
||||
.. warning::
|
||||
|
||||
If you want to use SSL with the client as a python library, you need to
|
||||
provide a cert to keystone's session object. Else, two additional options
|
||||
are available if you provide an ``auth`` object to the client: ``insecure``
|
||||
and ``cacert``::
|
||||
|
||||
>>> client = ck_client.Client(
|
||||
'1', auth=auth, insecure=False, cacert='/path/to/ca')
|
||||
|
||||
|
||||
If you want to use the v2 API, you have to specify it at client instanciation
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
c = ck_client.Client('2', session=session)
|
||||
|
||||
When using the ``cloudkitty`` CLI client with keystone authentication, the
|
||||
auth plugin to use should automagically be detected. If not, you can specify
|
||||
the auth plugin to use with ``--os-auth-type/--os-auth-plugin``::
|
||||
|
||||
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
|
||||
os-client-config==1.29.0 # Apache-2.0
|
||||
osc-lib==1.12.1 # Apache-2.0
|
||||
|
||||
# test-requirements.txt
|
||||
pyflakes==2.1.1
|
||||
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
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
The client has been adapted to allow adding support for v2 API
|
||||
endpoints. The v2 client class implements all v1 endpoints, but v1
|
||||
endpoints ported to v2 will be overriden. The API version to use can be
|
||||
specified through the ``--os-rating-api-version`` option or the
|
||||
``OS_RATING_API_VERSION``.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Support for the ``/v2/dataframes`` endpoint has been added to the client.
|
||||
A new ``dataframes add`` CLI command is also available.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Support for the ``GET /v2/dataframes`` endpoint has been added
|
||||
to the client. A new ``dataframes get`` CLI command is also available.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Support for the ``/v2/summary`` endpoint has been added to the client. The
|
||||
``summary get`` CLI command as well as the ``client.summary`` object in
|
||||
the python library have been overriden in case the v2 API is used.
|
||||
7
releasenotes/notes/drop-py27-27ea9fb3e40d4987.yaml
Normal file
7
releasenotes/notes/drop-py27-27ea9fb3e40d4987.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
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.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
The ``rating.get_quotation`` method of the client has been fixed: the json
|
||||
body has been updated to match the API reference.
|
||||
@@ -16,7 +16,6 @@
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
import pbr.version
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
@@ -26,8 +25,8 @@ import pbr.version
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'oslosphinx',
|
||||
'reno.sphinxext',
|
||||
'openstackdocstheme',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
@@ -46,16 +45,12 @@ master_doc = 'index'
|
||||
project = u'Cloudkitty Client Release Notes'
|
||||
copyright = u'2016, Cloudkitty developers'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
|
||||
cloudkittyclient_version = pbr.version.VersionInfo('python-cloudkittyclient')
|
||||
# Release notes are version independent.
|
||||
# The short X.Y version.
|
||||
version = cloudkittyclient_version.canonical_version_string()
|
||||
version = ''
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = cloudkittyclient_version.version_string_with_vcs()
|
||||
release = ''
|
||||
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
@@ -87,7 +82,7 @@ exclude_patterns = []
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
pygments_style = 'native'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
@@ -100,7 +95,7 @@ pygments_style = 'sphinx'
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
html_theme = 'openstackdocs'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
|
||||
@@ -8,6 +8,10 @@ Contents
|
||||
:maxdepth: 2
|
||||
|
||||
unreleased
|
||||
ussuri
|
||||
train
|
||||
stein
|
||||
rocky
|
||||
queens
|
||||
|
||||
Indices and tables
|
||||
|
||||
6
releasenotes/source/rocky.rst
Normal file
6
releasenotes/source/rocky.rst
Normal file
@@ -0,0 +1,6 @@
|
||||
===================================
|
||||
Rocky Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/rocky
|
||||
6
releasenotes/source/stein.rst
Normal file
6
releasenotes/source/stein.rst
Normal file
@@ -0,0 +1,6 @@
|
||||
===================================
|
||||
Stein Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/stein
|
||||
6
releasenotes/source/train.rst
Normal file
6
releasenotes/source/train.rst
Normal file
@@ -0,0 +1,6 @@
|
||||
==========================
|
||||
Train Series Release Notes
|
||||
==========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/train
|
||||
6
releasenotes/source/ussuri.rst
Normal file
6
releasenotes/source/ussuri.rst
Normal file
@@ -0,0 +1,6 @@
|
||||
===========================
|
||||
Ussuri Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/ussuri
|
||||
@@ -2,12 +2,12 @@
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||
cliff>=2.11.0,<3.0 # Apache-2.0
|
||||
keystoneauth1>=3.4.0,<4.0 # Apache-2.0
|
||||
oslo.utils>=3.35,<4.0 # Apache-2.0
|
||||
oslo.log>=3.36,<4.0 # Apache-2.0
|
||||
PyYAML>=3.12,<4.0 # MIT
|
||||
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,<2.0 # MIT
|
||||
os-client-config>=1.29.0 # Apache-2.0
|
||||
osc-lib>=1.12.1 # Apache-2.0
|
||||
|
||||
152
setup.cfg
152
setup.cfg
@@ -4,8 +4,9 @@ summary = API client of cloudkitty, Rating as a Service project.
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
author-email = openstack-dev@lists.openstack.org
|
||||
home-page = http://www.openstack.org/
|
||||
author-email = openstack-discuss@lists.openstack.org
|
||||
home-page = https://docs.openstack.org/python-cloudkittyclient/latest/
|
||||
python-requires = >=3.6
|
||||
classifier =
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Information Technology
|
||||
@@ -13,10 +14,12 @@ classifier =
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3 :: Only
|
||||
Programming Language :: Python :: Implementation :: CPython
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.5
|
||||
Programming Language :: Python :: 3.6
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
|
||||
[files]
|
||||
packages =
|
||||
@@ -84,8 +87,67 @@ openstack.rating.v1 =
|
||||
rating_pyscript_update = cloudkittyclient.v1.rating.pyscripts_cli:CliUpdateScript
|
||||
rating_pyscript_delete = cloudkittyclient.v1.rating.pyscripts_cli:CliDeleteScript
|
||||
|
||||
openstack.rating.v2 =
|
||||
rating_dataframes_get = cloudkittyclient.v2.dataframes_cli:CliDataframesGet
|
||||
rating_dataframes_add = cloudkittyclient.v2.dataframes_cli:CliDataframesAdd
|
||||
|
||||
cloudkittyclient =
|
||||
rating_scope_state_get = cloudkittyclient.v2.scope_cli:CliScopeStateGet
|
||||
rating_scope_state_reset = cloudkittyclient.v2.scope_cli:CliScopeStateReset
|
||||
|
||||
rating_summary_get = cloudkittyclient.v2.summary_cli:CliSummaryGet
|
||||
|
||||
rating_report_tenant_list = cloudkittyclient.v1.report_cli:CliTenantList
|
||||
|
||||
rating_module_get = cloudkittyclient.v1.rating:CliModuleGet
|
||||
rating_module_list = cloudkittyclient.v1.rating:CliModuleList
|
||||
rating_module_enable = cloudkittyclient.v1.rating:CliModuleEnable
|
||||
rating_module_disable = cloudkittyclient.v1.rating:CliModuleDisable
|
||||
rating_module_set_priority = cloudkittyclient.v1.rating:CliModuleSetPriority
|
||||
|
||||
rating_info_config_get = cloudkittyclient.v1.info_cli:CliInfoConfigGet
|
||||
rating_info_metric_get = cloudkittyclient.v1.info_cli:CliInfoMetricGet
|
||||
rating_info_metric_list = cloudkittyclient.v1.info_cli:CliInfoMetricList
|
||||
|
||||
rating_hashmap_mapping-types_list = cloudkittyclient.v1.rating.hashmap_cli:CliGetMappingTypes
|
||||
rating_hashmap_service_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetService
|
||||
rating_hashmap_service_list = cloudkittyclient.v1.rating.hashmap_cli:CliListService
|
||||
rating_hashmap_service_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateService
|
||||
rating_hashmap_service_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteService
|
||||
rating_hashmap_field_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetField
|
||||
rating_hashmap_field_list = cloudkittyclient.v1.rating.hashmap_cli:CliListField
|
||||
rating_hashmap_field_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateField
|
||||
rating_hashmap_field_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteField
|
||||
rating_hashmap_mapping_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetMapping
|
||||
rating_hashmap_mapping_list = cloudkittyclient.v1.rating.hashmap_cli:CliListMapping
|
||||
rating_hashmap_mapping_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateMapping
|
||||
rating_hashmap_mapping_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteMapping
|
||||
rating_hashmap_mapping_update = cloudkittyclient.v1.rating.hashmap_cli:CliUpdateMapping
|
||||
rating_hashmap_group_list = cloudkittyclient.v1.rating.hashmap_cli:CliListGroup
|
||||
rating_hashmap_group_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateGroup
|
||||
rating_hashmap_group_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteGroup
|
||||
rating_hashmap_group_mappings_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetGroupMappings
|
||||
rating_hashmap_group_thresholds_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetGroupThresholds
|
||||
rating_hashmap_threshold_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetThreshold
|
||||
rating_hashmap_threshold_list = cloudkittyclient.v1.rating.hashmap_cli:CliListThreshold
|
||||
rating_hashmap_threshold_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateThreshold
|
||||
rating_hashmap_threshold_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteThreshold
|
||||
rating_hashmap_threshold_update = cloudkittyclient.v1.rating.hashmap_cli:CliUpdateThreshold
|
||||
|
||||
rating_collector-mapping_get = cloudkittyclient.v1.collector_cli:CliCollectorMappingGet
|
||||
rating_collector-mapping_list = cloudkittyclient.v1.collector_cli:CliCollectorMappingList
|
||||
rating_collector-mapping_create = cloudkittyclient.v1.collector_cli:CliCollectorMappingCreate
|
||||
rating_collector-mapping_delete = cloudkittyclient.v1.collector_cli:CliCollectorMappingDelete
|
||||
rating_collector_state_get = cloudkittyclient.v1.collector_cli:CliCollectorGetState
|
||||
rating_collector_enable = cloudkittyclient.v1.collector_cli:CliCollectorEnable
|
||||
rating_collector_disable = cloudkittyclient.v1.collector_cli:CliCollectorDisable
|
||||
|
||||
rating_pyscript_create = cloudkittyclient.v1.rating.pyscripts_cli:CliCreateScript
|
||||
rating_pyscript_list = cloudkittyclient.v1.rating.pyscripts_cli:CliListScripts
|
||||
rating_pyscript_get = cloudkittyclient.v1.rating.pyscripts_cli:CliGetScript
|
||||
rating_pyscript_update = cloudkittyclient.v1.rating.pyscripts_cli:CliUpdateScript
|
||||
rating_pyscript_delete = cloudkittyclient.v1.rating.pyscripts_cli:CliDeleteScript
|
||||
|
||||
cloudkittyclient_v1 =
|
||||
total_get = cloudkittyclient.v1.report_cli:CliTotalGet
|
||||
summary_get = cloudkittyclient.v1.report_cli:CliSummaryGet
|
||||
report_tenant_list = cloudkittyclient.v1.report_cli:CliTenantList
|
||||
@@ -141,25 +203,69 @@ cloudkittyclient =
|
||||
pyscript_update = cloudkittyclient.v1.rating.pyscripts_cli:CliUpdateScript
|
||||
pyscript_delete = cloudkittyclient.v1.rating.pyscripts_cli:CliDeleteScript
|
||||
|
||||
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
|
||||
|
||||
summary_get = cloudkittyclient.v2.summary_cli:CliSummaryGet
|
||||
|
||||
report_tenant_list = cloudkittyclient.v1.report_cli:CliTenantList
|
||||
|
||||
module_get = cloudkittyclient.v1.rating:CliModuleGet
|
||||
module_list = cloudkittyclient.v1.rating:CliModuleList
|
||||
module_enable = cloudkittyclient.v1.rating:CliModuleEnable
|
||||
module_disable = cloudkittyclient.v1.rating:CliModuleDisable
|
||||
module_set_priority = cloudkittyclient.v1.rating:CliModuleSetPriority
|
||||
|
||||
info_config_get = cloudkittyclient.v1.info_cli:CliInfoConfigGet
|
||||
info_metric_get = cloudkittyclient.v1.info_cli:CliInfoMetricGet
|
||||
info_metric_list = cloudkittyclient.v1.info_cli:CliInfoMetricList
|
||||
|
||||
hashmap_mapping-types_list = cloudkittyclient.v1.rating.hashmap_cli:CliGetMappingTypes
|
||||
hashmap_service_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetService
|
||||
hashmap_service_list = cloudkittyclient.v1.rating.hashmap_cli:CliListService
|
||||
hashmap_service_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateService
|
||||
hashmap_service_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteService
|
||||
hashmap_field_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetField
|
||||
hashmap_field_list = cloudkittyclient.v1.rating.hashmap_cli:CliListField
|
||||
hashmap_field_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateField
|
||||
hashmap_field_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteField
|
||||
hashmap_mapping_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetMapping
|
||||
hashmap_mapping_list = cloudkittyclient.v1.rating.hashmap_cli:CliListMapping
|
||||
hashmap_mapping_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateMapping
|
||||
hashmap_mapping_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteMapping
|
||||
hashmap_mapping_update = cloudkittyclient.v1.rating.hashmap_cli:CliUpdateMapping
|
||||
hashmap_group_list = cloudkittyclient.v1.rating.hashmap_cli:CliListGroup
|
||||
hashmap_group_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateGroup
|
||||
hashmap_group_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteGroup
|
||||
hashmap_group_mappings_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetGroupMappings
|
||||
hashmap_group_thresholds_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetGroupThresholds
|
||||
hashmap_threshold_get = cloudkittyclient.v1.rating.hashmap_cli:CliGetThreshold
|
||||
hashmap_threshold_list = cloudkittyclient.v1.rating.hashmap_cli:CliListThreshold
|
||||
hashmap_threshold_create = cloudkittyclient.v1.rating.hashmap_cli:CliCreateThreshold
|
||||
hashmap_threshold_delete = cloudkittyclient.v1.rating.hashmap_cli:CliDeleteThreshold
|
||||
hashmap_threshold_update = cloudkittyclient.v1.rating.hashmap_cli:CliUpdateThreshold
|
||||
|
||||
collector-mapping_get = cloudkittyclient.v1.collector_cli:CliCollectorMappingGet
|
||||
collector-mapping_list = cloudkittyclient.v1.collector_cli:CliCollectorMappingList
|
||||
|
||||
collector-mapping_create = cloudkittyclient.v1.collector_cli:CliCollectorMappingCreate
|
||||
collector-mapping_delete = cloudkittyclient.v1.collector_cli:CliCollectorMappingDelete
|
||||
collector_state_get = cloudkittyclient.v1.collector_cli:CliCollectorGetState
|
||||
collector_enable = cloudkittyclient.v1.collector_cli:CliCollectorEnable
|
||||
collector_disable = cloudkittyclient.v1.collector_cli:CliCollectorDisable
|
||||
|
||||
pyscript_create = cloudkittyclient.v1.rating.pyscripts_cli:CliCreateScript
|
||||
pyscript_list = cloudkittyclient.v1.rating.pyscripts_cli:CliListScripts
|
||||
pyscript_get = cloudkittyclient.v1.rating.pyscripts_cli:CliGetScript
|
||||
pyscript_update = cloudkittyclient.v1.rating.pyscripts_cli:CliUpdateScript
|
||||
pyscript_delete = cloudkittyclient.v1.rating.pyscripts_cli:CliDeleteScript
|
||||
|
||||
keystoneauth1.plugin =
|
||||
cloudkitty-noauth = cloudkittyclient.auth:CloudKittyNoAuthLoader
|
||||
|
||||
cliff.formatter.list =
|
||||
df-to-csv = cloudkittyclient.format:DataframeToCsvFormatter
|
||||
|
||||
[upload_sphinx]
|
||||
upload-dir = doc/build/html
|
||||
|
||||
[compile_catalog]
|
||||
directory = cloudkittyclient/locale
|
||||
domain = python-cloudkittyclient
|
||||
|
||||
[update_catalog]
|
||||
domain = python-cloudkittyclient
|
||||
output_dir = cloudkittyclient/locale
|
||||
input_file = cloudkittyclient/locale/python-cloudkittyclient.pot
|
||||
|
||||
[extract_messages]
|
||||
keywords = _ gettext ngettext l_ lazy_gettext
|
||||
mapping_file = babel.cfg
|
||||
output_file = cloudkittyclient/locale/python-cloudkittyclient.pot
|
||||
|
||||
9
setup.py
9
setup.py
@@ -13,17 +13,8 @@
|
||||
# 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)
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
# 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
|
||||
hacking>=3.0,<3.1.0 # Apache-2.0
|
||||
|
||||
coverage!=4.4,>=4.0 # Apache-2.0
|
||||
# remove this pyflakes from here once you bump the
|
||||
# hacking to 3.2.0 or above. hacking 3.2.0 takes
|
||||
# care of pyflakes version compatibilty.
|
||||
pyflakes>=2.1.1
|
||||
|
||||
coverage>=4.0,!=4.4 # Apache-2.0
|
||||
python-subunit>=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
|
||||
|
||||
66
tox.ini
66
tox.ini
@@ -1,43 +1,67 @@
|
||||
[tox]
|
||||
minversion = 1.6
|
||||
envlist = py35,py27,pypy,pep8
|
||||
minversion = 3.1.1
|
||||
envlist = py36,py37,pep8
|
||||
skipsdist = True
|
||||
ignore_basepython_conflict = True
|
||||
|
||||
[testenv]
|
||||
basepython = python3
|
||||
usedevelop = True
|
||||
install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} -U {opts} {packages}
|
||||
install_command = pip install -U {opts} {packages}
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run {posargs}
|
||||
|
||||
[testenv:debug]
|
||||
basepython = python3
|
||||
commands = oslo_debug_helper {posargs}
|
||||
[testenv:cover]
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
PYTHON=coverage run --source cloudkittyclient --parallel-mode
|
||||
commands =
|
||||
stestr run {posargs}
|
||||
coverage combine
|
||||
coverage html -d cover
|
||||
coverage xml -o cover/coverage.xml
|
||||
coverage report
|
||||
|
||||
[testenv:functional]
|
||||
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
|
||||
commands = stestr run --concurrency=1 --test-path ./cloudkittyclient/tests/functional
|
||||
[testenv:debug]
|
||||
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
|
||||
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
|
||||
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]
|
||||
basepython = python3
|
||||
deps = -r{toxinidir}/doc/requirements.txt
|
||||
commands = sphinx-build -W -b html doc/source doc/build/html
|
||||
commands = sphinx-build --keep-going -b html doc/source doc/build/html
|
||||
|
||||
[testenv:pdf-docs]
|
||||
envdir = {toxworkdir}/docs
|
||||
deps = {[testenv:docs]deps}
|
||||
whitelist_externals =
|
||||
make
|
||||
commands =
|
||||
sphinx-build --keep-going -b latex doc/source doc/build/pdf
|
||||
make -C doc/build/pdf
|
||||
|
||||
[flake8]
|
||||
# E123, E125 skipped as they are invalid PEP-8.
|
||||
|
||||
show-source = True
|
||||
ignore = E123,E125
|
||||
ignore = E123,E125,W503,W504
|
||||
builtins = _
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,releasenotes
|
||||
|
||||
@@ -45,6 +69,14 @@ exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,releasenotes
|
||||
import_exceptions = cloudkittyclient.i18n
|
||||
|
||||
[testenv:releasenotes]
|
||||
basepython = python3
|
||||
deps = -r{toxinidir}/doc/requirements.txt
|
||||
commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
|
||||
deps =
|
||||
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/victoria}
|
||||
-r{toxinidir}/doc/requirements.txt
|
||||
commands =
|
||||
sphinx-build -a -E -W -d releasenotes/build/doctrees --keep-going -b html releasenotes/source releasenotes/build/html
|
||||
|
||||
[testenv:lower-constraints]
|
||||
deps =
|
||||
-c{toxinidir}/lower-constraints.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
-r{toxinidir}/requirements.txt
|
||||
|
||||
Reference in New Issue
Block a user