Compare commits

..

7 Commits
3.1.0 ... 2.1.1

Author SHA1 Message Date
Zuul
57d4563817 Merge "Fix the rating.get_quotation method" into stable/stein 2019-04-04 14:26:54 +00:00
Luka Peschke
b465e86703 Fix the rating.get_quotation method
This updates the rating.get_quotation method of the client. Tests on this
method have been added.

Depends-On: https://review.openstack.org/#/c/648062/
Change-Id: Ie2de0162311c2d162c1573042187ac4e628bd966
(cherry picked from commit de96c61985)
2019-04-04 12:59:33 +00:00
Luka Peschke
1724798fd7 Adding a python3 functional job
Change-Id: I3bb28edc9d62e8ea622e1061cbfb50168c217f24
(cherry picked from commit aeebd64928)
2019-04-04 12:59:27 +00:00
Luka Peschke
1ee30ec19c Asserting 'summary get' returns nothing in functional tests
Since no data is available in devstack in our functional test environment,
we make the assertion that 'summary get' returns nothing. This is prone to
update if more complete test scenarios are implemented.

Change-Id: Ic80e39f0d2a75882762ebd6a0dba46033c9fd7f4
(cherry picked from commit a7e687f740)
2019-04-04 10:26:36 +00:00
Luka Peschke
8b26d758c0 Fix releasenotes generation
Change-Id: Id72ea6407350bfccf7443bca9f4880d1324812c9
2019-03-27 09:48:47 +00:00
377172f39d Update UPPER_CONSTRAINTS_FILE for stable/stein
Update the URL to the upper-constraints file to point to the redirect
rule on releases.openstack.org so that anyone working on this branch
will switch to the correct upper-constraints list automatically when
the requirements repository branches.

Until the requirements repository has as stable/stein branch, tests will
continue to use the upper-constraints list on master.

Change-Id: Ibae1e8d8710efb9796f8e89547b994d4bf42c780
2019-03-18 14:49:53 +00:00
98e177260d Update .gitreview for stable/stein
Change-Id: Ic0fc80ffa4bdf16e9aa5b36a7be7614a74ee2d73
2019-03-18 14:49:50 +00:00
65 changed files with 93 additions and 1471 deletions

View File

@@ -1,5 +1,5 @@
[gerrit]
host=review.opendev.org
host=review.openstack.org
port=29418
project=openstack/python-cloudkittyclient.git
defaultbranch=stable/train
defaultbranch=stable/stein

View File

@@ -1,5 +1,5 @@
- job:
name: cloudkittyclient-devstack-functional-base
name: cloudkittyclient-devstack-functional
parent: devstack
description: |
Job for cloudkittyclient functional tests
@@ -18,30 +18,19 @@
- ^releasenotes/.*$
vars:
devstack_plugins:
cloudkitty: https://opendev.org/openstack/cloudkitty
cloudkitty: https://git.openstack.org/openstack/cloudkitty
devstack_localrc:
CLOUDKITTY_FETCHER: keystone
devstack_services:
ck-api: true
horizon: false
tox_install_siblings: false
zuul_work_dir: src/opendev.org/openstack/python-cloudkittyclient
zuul_work_dir: src/git.openstack.org/openstack/python-cloudkittyclient
tox_envlist: functional
- 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
- job:
name: cloudkittyclient-devstack-functional-base-py3
parent: cloudkittyclient-devstack-functional-base
name: cloudkittyclient-devstack-functional-py3
parent: cloudkittyclient-devstack-functional
description: |
Job for cloudkittyclient functional tests, ran in python3.
vars:
@@ -49,44 +38,24 @@
DEVSTACK_GATE_USE_PYTHON3: "True"
USE_PYTHON3: "True"
- job:
name: cloudkittyclient-devstack-functional-v1-client-py3
parent: cloudkittyclient-devstack-functional-base-py3
vars:
tox_envlist: functional-v1
- job:
name: cloudkittyclient-devstack-functional-v2-client-py3
parent: cloudkittyclient-devstack-functional-base-py3
vars:
tox_envlist: functional-v2
- project:
templates:
- openstack-lower-constraints-jobs
- check-requirements
- openstack-cover-jobs
- openstack-python-jobs
- openstack-python3-train-jobs
- openstack-python35-jobs
- openstack-python36-jobs
- openstackclient-plugin-jobs
- publish-openstack-docs-pti
check:
jobs:
- cloudkittyclient-devstack-functional-v1-client:
- cloudkittyclient-devstack-functional:
voting: true
- cloudkittyclient-devstack-functional-v2-client:
voting: true
- cloudkittyclient-devstack-functional-v1-client-py3:
voting: true
- cloudkittyclient-devstack-functional-v2-client-py3:
- cloudkittyclient-devstack-functional-py3:
voting: true
gate:
jobs:
- cloudkittyclient-devstack-functional-v1-client:
- cloudkittyclient-devstack-functional:
voting: true
- cloudkittyclient-devstack-functional-v2-client:
voting: true
- cloudkittyclient-devstack-functional-v1-client-py3:
voting: true
- cloudkittyclient-devstack-functional-v2-client-py3:
- cloudkittyclient-devstack-functional-py3:
voting: true

View File

@@ -1,16 +1,16 @@
If you would like to contribute to the development of OpenStack,
you must follow the steps in this page:
https://docs.openstack.org/infra/manual/developers.html
http://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:
https://docs.openstack.org/infra/manual/developers.html#development-workflow
http://docs.openstack.org/infra/manual/developers.html#development-workflow
Pull requests submitted through GitHub will be ignored.
Bugs should be filed on Storyboard, not GitHub:
Bugs should be filed on Launchpad, not GitHub:
https://storyboard.openstack.org/#!/project/openstack/python-cloudkittyclient
https://bugs.launchpad.net/cloudkitty

View File

@@ -1,47 +0,0 @@
# -*- 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)

View File

@@ -19,7 +19,6 @@ API_VERSION_OPTION = 'os_rating_api_version'
API_NAME = "rating"
API_VERSIONS = {
"1": "cloudkittyclient.v1.client.Client",
"2": "cloudkittyclient.v2.client.Client",
}
@@ -41,9 +40,4 @@ def make_client(instance):
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

View File

@@ -22,7 +22,6 @@ import os_client_config
from oslo_log import log
from cloudkittyclient import client
from cloudkittyclient.osc import DEFAULT_API_VERSION
from cloudkittyclient import utils
@@ -78,25 +77,13 @@ 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.v{}'.format(
self._get_api_version(args[:]),
)),
command_manager=CommandManager('cloudkittyclient'),
deferred_help=True,
)
self._client = None
@@ -118,6 +105,9 @@ 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'
@@ -143,10 +133,9 @@ class CloudKittyShell(cliff.app.App):
self.options.os_rating_endpoint_override or
self.options.os_endpoint_override),
)
self._client = client.Client(
str(self.options.os_rating_api_version or DEFAULT_API_VERSION),
session=session,
adapter_options=adapter_options)
self._client = client.Client(str(self.options.ck_version),
session=session,
adapter_options=adapter_options)
return self._client

View File

@@ -24,30 +24,24 @@ from cloudkittyclient.tests import utils
class BaseFunctionalTest(utils.BaseTestCase):
def _run(self, executable, action,
flags='', params='', fmt='-f json', stdin=None, has_output=True):
flags='', params='', fmt='-f json', 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,
stdin=subprocess.PIPE if stdin else None,
)
stdout, stderr = p.communicate(input=stdin)
p = subprocess.Popen(cmd, env=os.environ.copy(), shell=False,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
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',
stdin=None, has_output=True):
flags='', params='', fmt='-f json', has_output=True):
return self._run('openstack rating', action,
flags, params, fmt, stdin, has_output)
flags, params, fmt, has_output)
def cloudkitty(self, action,
flags='', params='', fmt='-f json',
stdin=None, has_output=True):
return self._run('cloudkitty', action, flags, params, fmt,
stdin, has_output)
flags='', params='', fmt='-f json', has_output=True):
return self._run('cloudkitty', action, flags, params, fmt, has_output)

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.tests.functional import base
from cloudkittyclient.tests.functional.v1 import base
class CkCollectorTest(base.BaseFunctionalTest):

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.tests.functional import base
from cloudkittyclient.tests.functional.v1 import base
class CkHashmapTest(base.BaseFunctionalTest):

View File

@@ -15,7 +15,7 @@
#
import jsonpath_rw_ext as jp
from cloudkittyclient.tests.functional import base
from cloudkittyclient.tests.functional.v1 import base
class CkInfoTest(base.BaseFunctionalTest):

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.tests.functional import base
from cloudkittyclient.tests.functional.v1 import base
class CkPyscriptTest(base.BaseFunctionalTest):

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.tests.functional import base
from cloudkittyclient.tests.functional.v1 import base
class CkRatingTest(base.BaseFunctionalTest):

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.tests.functional import base
from cloudkittyclient.tests.functional.v1 import base
class CkReportTest(base.BaseFunctionalTest):

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.tests.functional import base
from cloudkittyclient.tests.functional.v1 import base
class CkStorageTest(base.BaseFunctionalTest):

View File

@@ -1,169 +0,0 @@
# Copyright 2019 Objectif Libre
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
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: too few arguments',
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,
)
class OSCDataframesTest(CkDataframesTest):
def __init__(self, *args, **kwargs):
super(OSCDataframesTest, self).__init__(*args, **kwargs)
self.runner = self.openstack

View File

@@ -1,41 +0,0 @@
# Copyright 2019 Objectif Libre
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.tests.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

View File

@@ -1,35 +0,0 @@
# Copyright 2019 Objectif Libre
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.tests.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

View File

@@ -1,28 +0,0 @@
# Copyright 2019 objectif Libre
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from cloudkittyclient.tests import utils
from cloudkittyclient.v2 import dataframes
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)

View File

@@ -1,151 +0,0 @@
# Copyright 2019 Objectif Libre
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import json
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)

View File

@@ -1,88 +0,0 @@
# Copyright 2019 Objectif Libre
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient import exc
from cloudkittyclient.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)

View File

@@ -1,39 +0,0 @@
# Copyright 2019 Objectif Libre
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from 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')

View File

@@ -13,7 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.common import client
from keystoneauth1 import adapter
from keystoneauth1 import session as ks_session
from cloudkittyclient.v1 import collector
from cloudkittyclient.v1 import info
from cloudkittyclient.v1 import rating
@@ -21,7 +23,7 @@ from cloudkittyclient.v1 import report
from cloudkittyclient.v1 import storage
class Client(client.BaseClient):
class Client(object):
def __init__(self,
session=None,
@@ -29,14 +31,23 @@ class Client(client.BaseClient):
cacert=None,
insecure=False,
**kwargs):
super(Client, self).__init__(
session=session,
adapter_options=adapter_options,
cacert=cacert,
insecure=insecure,
**kwargs
)
adapter_options.setdefault('service_type', 'rating')
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)
self.info = info.InfoManager(self.api_client)
self.collector = collector.CollectorManager(self.api_client)
self.rating = rating.RatingManager(self.api_client)

View File

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

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.common import base
from cloudkittyclient.v1 import base
class InfoManager(base.BaseManager):

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,7 @@
#
from oslo_log import log
from cloudkittyclient.common import base
from cloudkittyclient.v1 import base
LOG = log.getLogger(__name__)

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.common import base
from cloudkittyclient.v1 import base
class StorageManager(base.BaseManager):

View File

@@ -1,42 +0,0 @@
# -*- 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 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)

View File

@@ -1,51 +0,0 @@
# Copyright 2019 Objectif Libre
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import json
import six
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, six.string_types):
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,
)

View File

@@ -1,42 +0,0 @@
# Copyright 2019 Objectif Libre
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import argparse
from cliff import command
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,
)

View File

@@ -1,102 +0,0 @@
# Copyright 2019 Objectif Libre
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.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)

View File

@@ -1,98 +0,0 @@
# Copyright 2019 Objectif Libre
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from cliff import 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,
)

View File

@@ -1,52 +0,0 @@
# Copyright 2019 Objectif Libre
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from cloudkittyclient.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()

View File

@@ -1,62 +0,0 @@
# Copyright 2019 Objectif Libre
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from cliff import lister
from 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']

View File

@@ -2,7 +2,6 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
openstackdocstheme>=1.30.0 # Apache-2.0
sphinx>=1.6.2,!=1.6.6,!=1.6.7,<2.0.0;python_version=='2.7' # BSD
sphinx>=1.6.2,!=1.6.6,!=1.6.7,!=2.1.0;python_version>='3.4' # BSD
openstackdocstheme>=1.18.1 # Apache-2.0
sphinx>=1.6.2,!=1.6.6,!=1.6.7 # BSD
reno>=2.5.0 # Apache-2.0

View File

@@ -1,10 +1,15 @@
=============
API Reference
Api Reference
=============
A ``client.Client`` instance has the following submodules (each one
corresponding to an API endpoint):
.. toctree::
:maxdepth: 1
:glob:
:maxdepth: 2
v1/index
v2/index
report
info
rating
collector
storage

View File

@@ -1,12 +0,0 @@
=======================
V1 API client reference
=======================
A ``client.v1.Client`` instance has the following submodules (each one
corresponding to an API endpoint):
.. toctree::
:maxdepth: 1
:glob:
./*

View File

@@ -1,6 +0,0 @@
===========================
dataframes (/v2/dataframes)
===========================
.. automodule:: cloudkittyclient.v2.dataframes
:members:

View File

@@ -1,16 +0,0 @@
=======================
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:
./*

View File

@@ -1,6 +0,0 @@
=================
scope (/v2/scope)
=================
.. automodule:: cloudkittyclient.v2.scope
:members:

View File

@@ -1,6 +0,0 @@
=====================
summary (/v2/summary)
=====================
.. automodule:: cloudkittyclient.v2.summary
:members:

View File

@@ -2,21 +2,6 @@
CLI Reference
=============
V1 Client
=========
.. autoprogram-cliff:: cloudkittyclient.v1
.. autoprogram-cliff:: cloudkittyclient
:application: cloudkitty
V2 Client
=========
.. autoprogram-cliff:: cloudkittyclient.v2
:command: dataframes add
.. autoprogram-cliff:: cloudkittyclient.v2
:command: scope state get
.. autoprogram-cliff:: cloudkittyclient.v2
:command: summary get
:ignored: --format, --column, --max-width, --fit-width, --print-empty, --format-config-file, --noindent, --quote, --sort-column

View File

@@ -25,11 +25,6 @@ extensions = [
'openstackdocstheme',
]
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
# text edit cycles.
# execute "export SPHINX_DEBUG=1" in your terminal to disable

View File

@@ -2,61 +2,6 @@
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
==============
@@ -114,13 +59,6 @@ Else, use it the same way as any other OpenStack client::
>>> 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``::

View File

@@ -8,7 +8,6 @@ PyYAML==3.12 # MIT
jsonpath-rw-ext==1.0 # Apache-2.0
six==1.11 # MIT
os-client-config==1.29.0 # Apache-2.0
osc-lib==1.12.1 # Apache-2.0
# test-requirements.txt
coverage==4.0 # Apache-2.0
@@ -19,6 +18,6 @@ mock==2.0 # BSD
python-openstackclient==3.14 # Apache-2.0
# doc/requirements.txt
openstackdocstheme==1.30.0 # Apache-2.0
openstackdocstheme==1.18.1 # Apache-2.0
sphinx==1.6.2 # BSD
reno==2.5.0 # Apache2

View File

@@ -1,8 +0,0 @@
---
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``.

View File

@@ -1,5 +0,0 @@
---
features:
- |
Support for the ``/v2/dataframes`` endpoint has been added to the client.
A new ``dataframes add`` CLI command is also available.

View File

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

View File

@@ -8,7 +8,6 @@ Contents
:maxdepth: 2
unreleased
stein
rocky
queens

View File

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

View File

@@ -11,4 +11,3 @@ PyYAML>=3.12 # MIT
jsonpath-rw-ext>=1.0 # Apache-2.0
six>=1.11 # MIT
os-client-config>=1.29.0 # Apache-2.0
osc-lib>=1.12.1 # Apache-2.0

129
setup.cfg
View File

@@ -4,8 +4,8 @@ summary = API client of cloudkitty, Rating as a Service project.
description-file =
README.rst
author = OpenStack
author-email = openstack-discuss@lists.openstack.org
home-page = https://docs.openstack.org/python-cloudkittyclient/latest/
author-email = openstack-dev@lists.openstack.org
home-page = http://www.openstack.org/
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
@@ -16,8 +16,7 @@ classifier =
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.5
[files]
packages =
@@ -85,67 +84,8 @@ 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_add = cloudkittyclient.v2.dataframes_cli:CliDataframesAdd
rating_scope_state_get = cloudkittyclient.v2.scope_cli:CliScopeStateGet
rating_scope_state_reset = cloudkittyclient.v2.scope_cli:CliScopeStateReset
rating_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_dataframes_get = cloudkittyclient.v1.storage_cli:CliGetDataframes
rating_pyscript_create = cloudkittyclient.v1.rating.pyscripts_cli:CliCreateScript
rating_pyscript_list = cloudkittyclient.v1.rating.pyscripts_cli:CliListScripts
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 =
cloudkittyclient =
total_get = cloudkittyclient.v1.report_cli:CliTotalGet
summary_get = cloudkittyclient.v1.report_cli:CliSummaryGet
report_tenant_list = cloudkittyclient.v1.report_cli:CliTenantList
@@ -201,67 +141,6 @@ cloudkittyclient.v1 =
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
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
dataframes_get = cloudkittyclient.v1.storage_cli:CliGetDataframes
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

29
tox.ini
View File

@@ -1,11 +1,11 @@
[tox]
minversion = 2.0
envlist = py27,py36,py37,pypy,pep8
envlist = py35,py27,pypy,pep8
skipsdist = True
[testenv]
usedevelop = True
install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/train} -U {opts} {packages}
install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/stein} -U {opts} {packages}
setenv =
VIRTUAL_ENV={envdir}
deps = -r{toxinidir}/requirements.txt
@@ -26,17 +26,11 @@ commands =
[testenv:debug]
basepython = python3
commands = oslo_debug_helper -t cloudkittyclient/tests {posargs}
commands = oslo_debug_helper {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: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:pep8]
basepython = python3
@@ -64,14 +58,5 @@ import_exceptions = cloudkittyclient.i18n
[testenv:releasenotes]
basepython = python3
deps =
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/train}
-r{toxinidir}/doc/requirements.txt
deps = -r{toxinidir}/doc/requirements.txt
commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
[testenv:lower-constraints]
basepython = python3
deps =
-c{toxinidir}/lower-constraints.txt
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/requirements.txt