Skip to content

Commit c138f40

Browse files
committed
Add support for /v2/summary to the client
This allows to get a summary through the v2 API endpoint via the client library and cli tool. Depends-On: https://review.opendev.org/#/c/660608/ Change-Id: Id63f2419fe3a1eb518a0ffa7ea5fa572b18df651 Story: 2005664 Task: 30960
1 parent a5a14a5 commit c138f40

23 files changed

Lines changed: 396 additions & 19 deletions

cloudkittyclient/osc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from osc_lib import utils
1616

17-
DEFAULT_API_VERSION = '2'
17+
DEFAULT_API_VERSION = '1'
1818
API_VERSION_OPTION = 'os_rating_api_version'
1919
API_NAME = "rating"
2020
API_VERSIONS = {

cloudkittyclient/shell.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,25 @@ class CloudKittyShell(cliff.app.App):
7878
'pyscripts-script-update',
7979
]
8080

81+
def _get_api_version(self, args):
82+
# FIXME(peschk_l): This is a hacky way to figure out the client version
83+
# to load. If anybody has a better idea, please fix this.
84+
self.deferred_help = True
85+
parser = self.build_option_parser('CloudKitty CLI client',
86+
utils.get_version())
87+
del self.deferred_help
88+
parsed_args = parser.parse_known_args(args)
89+
return str(parsed_args[0].os_rating_api_version or DEFAULT_API_VERSION)
90+
8191
def __init__(self, args):
8292
self._args = args
8393
self.cloud_config = os_client_config.OpenStackConfig()
8494
super(CloudKittyShell, self).__init__(
8595
description='CloudKitty CLI client',
8696
version=utils.get_version(),
87-
command_manager=CommandManager('cloudkittyclient'),
97+
command_manager=CommandManager('cloudkittyclient.v{}'.format(
98+
self._get_api_version(args[:]),
99+
)),
88100
deferred_help=True,
89101
)
90102
self._client = None
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Copyright 2019 Objectif Libre
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
#
15+
from cloudkittyclient.tests.functional import base
16+
17+
18+
class CkSummaryTest(base.BaseFunctionalTest):
19+
20+
def __init__(self, *args, **kwargs):
21+
super(CkSummaryTest, self).__init__(*args, **kwargs)
22+
self.runner = self.cloudkitty
23+
24+
def test_summary_get(self):
25+
return True
26+
# FIXME(peschk_l): Uncomment and update this once there is a way to set
27+
# the state of a summary through the client
28+
# resp = self.runner('summary get')
29+
30+
31+
class OSCSummaryTest(CkSummaryTest):
32+
33+
def __init__(self, *args, **kwargs):
34+
super(OSCSummaryTest, self).__init__(*args, **kwargs)
35+
self.runner = self.openstack

cloudkittyclient/tests/unit/v2/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
from cloudkittyclient.tests import utils
1616
from cloudkittyclient.v2 import scope
17+
from cloudkittyclient.v2 import summary
1718

1819

1920
class BaseAPIEndpointTestCase(utils.BaseTestCase):
@@ -22,3 +23,4 @@ def setUp(self):
2223
super(BaseAPIEndpointTestCase, self).setUp()
2324
self.api_client = utils.FakeHTTPClient()
2425
self.scope = scope.ScopeManager(self.api_client)
26+
self.summary = summary.SummaryManager(self.api_client)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Copyright 2019 Objectif Libre
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
#
15+
from collections import OrderedDict
16+
17+
from cloudkittyclient.tests.unit.v2 import base
18+
19+
20+
class TestSummary(base.BaseAPIEndpointTestCase):
21+
22+
def test_get_summary(self):
23+
self.summary.get_summary()
24+
self.api_client.get.assert_called_once_with('/v2/summary')
25+
26+
def test_get_summary_with_pagination_args(self):
27+
self.summary.get_summary(offset=10, limit=10)
28+
try:
29+
self.api_client.get.assert_called_once_with(
30+
'/v2/summary?limit=10&offset=10')
31+
except AssertionError:
32+
self.api_client.get.assert_called_once_with(
33+
'/v2/summary?offset=10&limit=10')
34+
35+
def test_get_summary_filters(self):
36+
self.summary.get_summary(
37+
filters=OrderedDict([('one', 'two'), ('three', 'four')]))
38+
self.api_client.get.assert_called_once_with(
39+
'/v2/summary?filters=one%3Atwo%2Cthree%3Afour')

cloudkittyclient/v2/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#
1616
from cloudkittyclient.v1 import client
1717
from cloudkittyclient.v2 import scope
18+
from cloudkittyclient.v2 import summary
1819

1920

2021
# NOTE(peschk_l) v2 client needs to implement v1 until the v1 API has been
@@ -36,3 +37,4 @@ def __init__(self,
3637
)
3738

3839
self.scope = scope.ScopeManager(self.api_client)
40+
self.summary = summary.SummaryManager(self.api_client)

cloudkittyclient/v2/summary.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Copyright 2019 Objectif Libre
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
#
15+
from cloudkittyclient.common import base
16+
17+
18+
class SummaryManager(base.BaseManager):
19+
20+
url = '/v2/summary'
21+
22+
def get_summary(self, **kwargs):
23+
"""Returns a paginated list of summaries.
24+
25+
This support filters along with custom grouping.
26+
27+
:param offset: Index of the first scope that should be returned.
28+
:type offset: int
29+
:param limit: Maximal number of scopes to return.
30+
:type limit: int
31+
:param filters: Optional dict of filters to select data on.
32+
:type filters: dict
33+
:param groupby: Optional list of attributes to group data on.
34+
:type groupby: str or list of str.
35+
:param begin: Start of the period to gather data from
36+
:type begin: datetime.datetime
37+
:param end: End of the period to gather data from
38+
:type end: datetime.datetime
39+
"""
40+
if 'groupby' in kwargs.keys() and isinstance(kwargs['groupby'], list):
41+
kwargs['groupby'] = ','.join(kwargs['groupby'])
42+
43+
kwargs['filters'] = ','.join(
44+
'{}:{}'.format(k, v) for k, v in
45+
(kwargs.get('filters', None) or {}).items()
46+
)
47+
48+
authorized_args = [
49+
'offset', 'limit', 'filters', 'groupby', 'begin', 'end']
50+
51+
url = self.get_url(None, kwargs, authorized_args=authorized_args)
52+
return self.api_client.get(url).json()

cloudkittyclient/v2/summary_cli.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Copyright 2019 Objectif Libre
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
#
15+
from cliff import lister
16+
from oslo_utils import timeutils
17+
18+
from cloudkittyclient import utils
19+
20+
21+
class CliSummaryGet(lister.Lister):
22+
"""Get a summary for a given period."""
23+
24+
def get_parser(self, prog_name):
25+
parser = super(CliSummaryGet, self).get_parser(prog_name)
26+
27+
def filter_(elem):
28+
if len(elem.split(':')) != 2:
29+
raise TypeError
30+
return str(elem)
31+
32+
parser.add_argument('--offset', type=int, default=0,
33+
help='Index of the first element')
34+
parser.add_argument('--limit', type=int, default=100,
35+
help='Maximal number of elements')
36+
parser.add_argument('-g', '--groupby', type=str, action='append',
37+
help='Attribute to group the summary by. Can be '
38+
'specified several times')
39+
parser.add_argument('--filter', type=filter_, action='append',
40+
help="Optional filter, in 'key:value' format. Can "
41+
"be specified several times.")
42+
parser.add_argument('-b', '--begin', type=timeutils.parse_isotime,
43+
help="Start of the period to query, in iso8601 "
44+
"format. Example: 2019-05-01T00:00:00Z.")
45+
parser.add_argument('-e', '--end', type=timeutils.parse_isotime,
46+
help="End of the period to query, in iso8601 "
47+
"format. Example: 2019-06-01T00:00:00Z.")
48+
49+
return parser
50+
51+
def take_action(self, parsed_args):
52+
filters = dict(elem.split(':') for elem in (parsed_args.filter or []))
53+
resp = utils.get_client_from_osc(self).summary.get_summary(
54+
offset=parsed_args.offset,
55+
limit=parsed_args.limit,
56+
begin=parsed_args.begin,
57+
end=parsed_args.end,
58+
filters=filters,
59+
groupby=parsed_args.groupby,
60+
)
61+
columns = [c.replace('_', ' ').capitalize() for c in resp['columns']]
62+
return columns, resp['results']

doc/source/api_reference/index.rst

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
=============
2-
Api Reference
2+
API Reference
33
=============
44

5-
A ``client.Client`` instance has the following submodules (each one
6-
corresponding to an API endpoint):
7-
85
.. toctree::
9-
:maxdepth: 2
6+
:maxdepth: 1
7+
:glob:
108

11-
report
12-
info
13-
rating
14-
collector
15-
storage
9+
v1/index
10+
v2/index

0 commit comments

Comments
 (0)