Skip to content

Commit d660bef

Browse files
committed
Add support for PUT /v2/scope API endpoint to the client
This allows to reset the state of one or several scopes through the API via the client library and cli tool. Change-Id: I69ce9a1c2ee0d8a6dd191a39e5c843e0baa1290f Story: 2005395 Task: 30794
1 parent c138f40 commit d660bef

5 files changed

Lines changed: 162 additions & 0 deletions

File tree

cloudkittyclient/tests/functional/v2/test_scope.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ def test_scope_state_get(self):
2727
# the state of a scope through the client
2828
# resp = self.runner('scope state get')
2929

30+
def test_scope_state_reset(self):
31+
return True
32+
# FIXME(jferrieu): Uncomment and update this once there is a way to set
33+
# the state of a scope through the client
34+
# resp = self.runner('scope state reset')
35+
3036

3137
class OSCScopeTest(CkScopeTest):
3238

cloudkittyclient/tests/unit/v2/test_scope.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
# License for the specific language governing permissions and limitations
1313
# under the License.
1414
#
15+
from cloudkittyclient import exc
1516
from cloudkittyclient.tests.unit.v2 import base
17+
import datetime
1618

1719

1820
class TestScope(base.BaseAPIEndpointTestCase):
@@ -29,3 +31,58 @@ def test_get_scope_with_args(self):
2931
except AssertionError:
3032
self.api_client.get.assert_called_once_with(
3133
'/v2/scope?offset=10&limit=10')
34+
35+
def test_reset_scope_with_args(self):
36+
self.scope.reset_scope_state(
37+
state=datetime.datetime(2019, 5, 7),
38+
all_scopes=True)
39+
self.api_client.put.assert_called_once_with(
40+
'/v2/scope',
41+
json={
42+
'state': datetime.datetime(2019, 5, 7),
43+
'all_scopes': True,
44+
})
45+
46+
def test_reset_scope_with_list_args(self):
47+
self.scope.reset_scope_state(
48+
state=datetime.datetime(2019, 5, 7),
49+
scope_id=['id1', 'id2'],
50+
all_scopes=False)
51+
self.api_client.put.assert_called_once_with(
52+
'/v2/scope',
53+
json={
54+
'state': datetime.datetime(2019, 5, 7),
55+
'scope_id': 'id1,id2',
56+
})
57+
58+
def test_reset_scope_strips_none_and_false_args(self):
59+
self.scope.reset_scope_state(
60+
state=datetime.datetime(2019, 5, 7),
61+
all_scopes=False,
62+
scope_key=None,
63+
scope_id=['id1', 'id2'])
64+
self.api_client.put.assert_called_once_with(
65+
'/v2/scope',
66+
json={
67+
'state': datetime.datetime(2019, 5, 7),
68+
'scope_id': 'id1,id2',
69+
})
70+
71+
def test_reset_scope_with_no_args_raises_exc(self):
72+
self.assertRaises(
73+
exc.ArgumentRequired,
74+
self.scope.reset_scope_state)
75+
76+
def test_reset_scope_with_lacking_args_raises_exc(self):
77+
self.assertRaises(
78+
exc.ArgumentRequired,
79+
self.scope.reset_scope_state,
80+
state=datetime.datetime(2019, 5, 7))
81+
82+
def test_reset_scope_with_both_args_raises_exc(self):
83+
self.assertRaises(
84+
exc.InvalidArgumentError,
85+
self.scope.reset_scope_state,
86+
state=datetime.datetime(2019, 5, 7),
87+
scope_id=['id1', 'id2'],
88+
all_scopes=True)

cloudkittyclient/v2/scope.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# under the License.
1414
#
1515
from cloudkittyclient.common import base
16+
from cloudkittyclient import exc
1617

1718

1819
class ScopeManager(base.BaseManager):
@@ -48,3 +49,54 @@ def get_scope_state(self, **kwargs):
4849
'offset', 'limit', 'collector', 'fetcher', 'scope_id', 'scope_key']
4950
url = self.get_url(None, kwargs, authorized_args=authorized_args)
5051
return self.api_client.get(url).json()
52+
53+
def reset_scope_state(self, **kwargs):
54+
"""Returns nothing.
55+
56+
Some optional filters can be provided.
57+
The all_scopes and the scope_id options are mutually exclusive and one
58+
must be provided.
59+
60+
:param state: datetime object from which the state will be reset
61+
:type state: datetime.datetime
62+
:param all_scopes: Whether all scopes must be reset
63+
:type all_scopes: bool
64+
:param collector: Optional collector to filter on.
65+
:type collector: str or list of str
66+
:param fetcher: Optional fetcher to filter on.
67+
:type fetcher: str or list of str
68+
:param scope_id: Optional scope_id to filter on.
69+
:type scope_id: str or list of str
70+
:param scope_key: Optional scope_key to filter on.
71+
:type scope_key: str or list of str
72+
"""
73+
74+
if not kwargs.get('state'):
75+
raise exc.ArgumentRequired("'state' argument is required")
76+
77+
if not kwargs.get('all_scopes') and not kwargs.get('scope_id'):
78+
raise exc.ArgumentRequired(
79+
"You must specify either 'scope_id' or 'all_scopes'")
80+
81+
if kwargs.get('all_scopes') and kwargs.get('scope_id'):
82+
raise exc.InvalidArgumentError(
83+
"You can't specify both 'scope_id' and 'all_scopes'")
84+
85+
for key in ('collector', 'fetcher', 'scope_id', 'scope_key'):
86+
if key in kwargs.keys():
87+
if isinstance(kwargs[key], list):
88+
kwargs[key] = ','.join(kwargs[key])
89+
90+
body = dict(
91+
state=kwargs.get('state'),
92+
scope_id=kwargs.get('scope_id'),
93+
scope_key=kwargs.get('scope_key'),
94+
collector=kwargs.get('collector'),
95+
fetcher=kwargs.get('fetcher'),
96+
all_scopes=kwargs.get('all_scopes'),
97+
)
98+
# Stripping None and False values
99+
body = dict(filter(lambda elem: bool(elem[1]), body.items()))
100+
101+
url = self.get_url(None, kwargs)
102+
return self.api_client.put(url, json=body)

cloudkittyclient/v2/scope_cli.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
# License for the specific language governing permissions and limitations
1313
# under the License.
1414
#
15+
from cliff import command
1516
from cliff import lister
17+
from oslo_utils import timeutils
1618

1719
from cloudkittyclient import utils
1820

@@ -53,3 +55,44 @@ def take_action(self, parsed_args):
5355
)
5456
values = utils.list_to_cols(resp['results'], self.info_columns)
5557
return [col[1] for col in self.info_columns], values
58+
59+
60+
class CliScopeStateReset(command.Command):
61+
"""Reset the state of several scopes."""
62+
info_columns = [
63+
('scope_id', 'Scope ID'),
64+
('scope_key', 'Scope Key'),
65+
('collector', 'Collector'),
66+
('fetcher', 'Fetcher'),
67+
]
68+
69+
def get_parser(self, prog_name):
70+
parser = super(CliScopeStateReset, self).get_parser(prog_name)
71+
72+
for col in self.info_columns:
73+
parser.add_argument(
74+
'--' + col[0].replace('_', '-'), type=str,
75+
action='append', help='Optional filter on ' + col[1])
76+
77+
parser.add_argument(
78+
'-a', '--all-scopes',
79+
action='store_true',
80+
help="Target all scopes at once")
81+
82+
parser.add_argument(
83+
'state',
84+
type=timeutils.parse_isotime,
85+
help="State iso8601 datetime to which the state should be set. "
86+
"Example: 2019-06-01T00:00:00Z.")
87+
88+
return parser
89+
90+
def take_action(self, parsed_args):
91+
utils.get_client_from_osc(self).scope.reset_scope_state(
92+
collector=parsed_args.collector,
93+
fetcher=parsed_args.fetcher,
94+
scope_id=parsed_args.scope_id,
95+
scope_key=parsed_args.scope_key,
96+
all_scopes=parsed_args.all_scopes,
97+
state=parsed_args.state,
98+
)

setup.cfg

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ openstack.rating.v1 =
8787

8888
openstack.rating.v2 =
8989
rating_scope_state_get = cloudkittyclient.v2.scope_cli:CliScopeStateGet
90+
rating_scope_state_reset = cloudkittyclient.v2.scope_cli:CliScopeStateReset
91+
9092
rating_summary_get = cloudkittyclient.v2.summary_cli:CliSummaryGet
9193

9294
rating_report_tenant_list = cloudkittyclient.v1.report_cli:CliTenantList
@@ -199,6 +201,8 @@ cloudkittyclient.v1 =
199201

200202
cloudkittyclient.v2 =
201203
scope_state_get = cloudkittyclient.v2.scope_cli:CliScopeStateGet
204+
scope_state_reset = cloudkittyclient.v2.scope_cli:CliScopeStateReset
205+
202206
summary_get = cloudkittyclient.v2.summary_cli:CliSummaryGet
203207

204208
report_tenant_list = cloudkittyclient.v1.report_cli:CliTenantList

0 commit comments

Comments
 (0)