Skip to content

Commit f8da9da

Browse files
committed
Add support to rating rules with start and end
It was introduced the concept of start and end periods in Cloudkitty rating rules. Therefore to make Cloudkitty CLI compatible with the Cloudkitty REST API, we need to add those new available attributes in the CLI as well. Change-Id: I0cd9b61fa81232d235c959da551a7840465fae88 Signed-off-by: Pedro Henrique <phpm13@gmail.com>
1 parent c4711df commit f8da9da

8 files changed

Lines changed: 168 additions & 10 deletions

File tree

cloudkittyclient/tests/functional/v1/test_hashmap.py

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
# License for the specific language governing permissions and limitations
1414
# under the License.
1515
#
16+
from datetime import datetime
17+
from datetime import timedelta
18+
1619
from cloudkittyclient.tests.functional import base
1720

1821

@@ -136,13 +139,15 @@ def test_create_get_delete_field(self):
136139
self.assertEqual(len(resp), 0)
137140

138141
def test_create_get_update_delete_mapping_service(self):
142+
future_date = datetime.now() + timedelta(days=1)
143+
date_iso = future_date.isoformat()
139144
resp = self.runner('hashmap service create', params='testservice')[0]
140145
service_id = resp['Service ID']
141146
self._services.append(service_id)
142147

143148
# Create mapping
144149
resp = self.runner('hashmap mapping create',
145-
params='-s {} 12'.format(service_id))[0]
150+
params=f'-s {service_id} 12 --start {date_iso}')[0]
146151
mapping_id = resp['Mapping ID']
147152
self._mappings.append(mapping_id)
148153
self.assertEqual(resp['Service ID'], service_id)
@@ -173,6 +178,8 @@ def test_create_get_update_delete_mapping_service(self):
173178
'hashmap service delete', params=service_id, has_output=False)
174179

175180
def test_create_get_update_delete_mapping_field(self):
181+
future_date = datetime.now() + timedelta(days=1)
182+
date_iso = future_date.isoformat()
176183
resp = self.runner('hashmap service create', params='testservice')[0]
177184
service_id = resp['Service ID']
178185
self._services.append(service_id)
@@ -185,7 +192,8 @@ def test_create_get_update_delete_mapping_field(self):
185192
# Create mapping
186193
resp = self.runner(
187194
'hashmap mapping create',
188-
params='--field-id {} 12 --value testvalue'.format(field_id))[0]
195+
params=f'--field-id {field_id} 12 --value '
196+
f'testvalue --start {date_iso}')[0]
189197
mapping_id = resp['Mapping ID']
190198
self._mappings.append(service_id)
191199
self.assertEqual(resp['Field ID'], field_id)
@@ -203,6 +211,45 @@ def test_create_get_update_delete_mapping_field(self):
203211
params='--cost 10 {}'.format(mapping_id))[0]
204212
self.assertEqual(float(resp['Cost']), float(10))
205213

214+
def test_create_get_update_delete_mapping_field_started(self):
215+
resp = self.runner('hashmap service create',
216+
params='testservice_date_started')[0]
217+
service_id = resp['Service ID']
218+
self._services.append(service_id)
219+
220+
resp = self.runner(
221+
'hashmap field create',
222+
params='{} testfield_date_started'.format(service_id))[0]
223+
field_id = resp['Field ID']
224+
self._fields.append(field_id)
225+
226+
# Create mapping
227+
resp = self.runner(
228+
'hashmap mapping create',
229+
params=f'--field-id {field_id} 12 --value '
230+
f'testvalue')[0]
231+
mapping_id = resp['Mapping ID']
232+
self._mappings.append(service_id)
233+
self.assertEqual(resp['Field ID'], field_id)
234+
self.assertEqual(float(resp['Cost']), float(12))
235+
self.assertEqual(resp['Value'], 'testvalue')
236+
237+
# Get mapping
238+
resp = self.runner(
239+
'hashmap mapping get', params=mapping_id)[0]
240+
self.assertEqual(resp['Mapping ID'], mapping_id)
241+
self.assertEqual(float(resp['Cost']), float(12))
242+
243+
# Should not be able to update a rule that is running (start < now)
244+
try:
245+
self.runner('hashmap mapping update',
246+
params='--cost 10 {}'.format(mapping_id))[0]
247+
except RuntimeError as e:
248+
expected_error = ("You are allowed to update only the attribute "
249+
"[end] as this rule is already running as it "
250+
"started on ")
251+
self.assertIn(expected_error, str(e))
252+
206253
def test_group_mappings_get(self):
207254
# Service and group
208255
resp = self.runner('hashmap service create', params='testservice')[0]

cloudkittyclient/tests/functional/v1/test_pyscripts.py

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
# License for the specific language governing permissions and limitations
1414
# under the License.
1515
#
16+
from datetime import datetime
17+
from datetime import timedelta
18+
1619
from cloudkittyclient.tests.functional import base
1720

1821

@@ -23,9 +26,12 @@ def __init__(self, *args, **kwargs):
2326
self.runner = self.cloudkitty
2427

2528
def test_create_get_update_list_delete(self):
29+
future_date = datetime.now() + timedelta(days=1)
30+
date_iso = future_date.isoformat()
2631
# Create
2732
resp = self.runner(
28-
'pyscript create', params="testscript 'return 0'")[0]
33+
'pyscript create', params=f"testscript "
34+
f"'return 0' --start {date_iso}")[0]
2935
script_id = resp['Script ID']
3036
self.assertEqual(resp['Name'], 'testscript')
3137

@@ -37,22 +43,59 @@ def test_create_get_update_list_delete(self):
3743
# Update
3844
resp = self.runner(
3945
'pyscript update',
40-
params="-n newname -d 'return 1' {}".format(script_id))[0]
41-
self.assertEqual(resp['Name'], 'newname')
46+
params="-d 'return 1' {} --description "
47+
"desc".format(script_id))[0]
48+
self.assertEqual(resp['Script Description'], 'desc')
4249
self.assertEqual(resp['Script ID'], script_id)
4350
self.assertEqual(resp['Data'], 'return 1')
4451

4552
# List
4653
resp = self.runner('pyscript list')
4754
self.assertEqual(len(resp), 1)
4855
resp = resp[0]
49-
self.assertEqual(resp['Name'], 'newname')
56+
self.assertEqual(resp['Script Description'], 'desc')
5057
self.assertEqual(resp['Script ID'], script_id)
5158
self.assertEqual(resp['Data'], 'return 1')
5259

5360
# Delete
5461
self.runner('pyscript delete', params=script_id, has_output=False)
5562

63+
def test_create_get_update_list_delete_started(self):
64+
# Create
65+
resp = self.runner(
66+
'pyscript create', params="testscript_started "
67+
"'return 0'")[0]
68+
script_id = resp['Script ID']
69+
self.assertEqual(resp['Name'], 'testscript_started')
70+
71+
# Get
72+
resp = self.runner('pyscript get', params=script_id)[0]
73+
self.assertEqual(resp['Name'], 'testscript_started')
74+
self.assertEqual(resp['Script ID'], script_id)
75+
76+
# Should not be able to update a rule that is running (start < now)
77+
try:
78+
self.runner(
79+
'pyscript update',
80+
params="-d 'return 1' {} --description "
81+
"desc".format(script_id))[0]
82+
except RuntimeError as e:
83+
expected_error = ("You are allowed to update only the attribute "
84+
"[end] as this rule is already running as it "
85+
"started on ")
86+
self.assertIn(expected_error, str(e))
87+
88+
# List
89+
resp = self.runner('pyscript list')
90+
self.assertEqual(len(resp), 1)
91+
resp = resp[0]
92+
self.assertEqual(resp['Script Description'], None)
93+
self.assertEqual(resp['Script ID'], script_id)
94+
self.assertEqual(resp['Data'], 'return 0')
95+
96+
# Delete
97+
self.runner('pyscript delete', params=script_id, has_output=False)
98+
5699

57100
class OSCPyscriptTest(CkPyscriptTest):
58101

cloudkittyclient/tests/unit/v1/test_hashmap.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,10 @@ def test_get_mapping_no_args(self):
110110
self.assertRaises(exc.ArgumentRequired, self.hashmap.get_mapping)
111111

112112
def test_create_mapping(self):
113-
kwargs = dict(cost=2, value='value', field_id='field_id')
113+
kwargs = dict(cost=2, value='value', field_id='field_id',
114+
name='name', start="2024-01-01",
115+
end="2024-01-01",
116+
description="description")
114117
body = dict(
115118
cost=kwargs.get('cost'),
116119
value=kwargs.get('value'),
@@ -119,6 +122,10 @@ def test_create_mapping(self):
119122
group_id=kwargs.get('group_id'),
120123
tenant_id=kwargs.get('tenant_id'),
121124
type=kwargs.get('type') or 'flat',
125+
start="2024-01-01",
126+
end="2024-01-01",
127+
description="description",
128+
name='name'
122129
)
123130
self.hashmap.create_mapping(**kwargs)
124131
self.api_client.post.assert_called_once_with(

cloudkittyclient/tests/unit/v1/test_pyscripts.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ def test_get_script_no_arg(self):
3838
self.assertRaises(exc.ArgumentRequired, self.pyscripts.get_script)
3939

4040
def test_create_script(self):
41-
kwargs = dict(name='name', data='data')
41+
kwargs = dict(name='name', data='data', start=None,
42+
end=None, description=None)
4243
self.pyscripts.create_script(**kwargs)
4344
self.api_client.post.assert_called_once_with(
4445
'/v1/rating/module_config/pyscripts/scripts/', json=kwargs)

cloudkittyclient/v1/rating/hashmap.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
# License for the specific language governing permissions and limitations
1414
# under the License.
1515
#
16+
import uuid
17+
1618
from cloudkittyclient.common import base
1719
from cloudkittyclient import exc
1820

@@ -171,6 +173,14 @@ def create_mapping(self, **kwargs):
171173
:type type: str
172174
:param value: Value of the mapping
173175
:type value: str
176+
:param name: Name of the mapping
177+
:type name: str
178+
:param start: Date the mapping starts being valid
179+
:type start: str
180+
:param end: Date the mapping stops being valid
181+
:type end: str
182+
:param description: Description of the mapping
183+
:type description: str
174184
"""
175185
if kwargs.get('cost') is None:
176186
raise exc.ArgumentRequired("'cost' argument is required")
@@ -196,6 +206,16 @@ def create_mapping(self, **kwargs):
196206
tenant_id=kwargs.get('tenant_id'),
197207
type=kwargs.get('type') or 'flat',
198208
)
209+
if kwargs.get('description'):
210+
body['description'] = kwargs.get('description')
211+
if kwargs.get('start'):
212+
body['start'] = kwargs.get('start')
213+
if kwargs.get('end'):
214+
body['end'] = kwargs.get('end')
215+
if kwargs.get('name'):
216+
body['name'] = kwargs.get('name')
217+
else:
218+
body['name'] = uuid.uuid4().hex[:24]
199219
url = self.get_url('mappings', kwargs)
200220
return self.api_client.post(url, json=body).json()
201221

cloudkittyclient/v1/rating/hashmap_cli.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@ class CliCreateMapping(lister.Lister):
257257
('service_id', 'Service ID'),
258258
('group_id', 'Group ID'),
259259
('tenant_id', 'Project ID'),
260+
('name', 'Mapping Name'),
261+
('start', 'Mapping Start Date'),
262+
('end', 'Mapping End Date'),
263+
('Description', 'Mapping Description')
260264
]
261265

262266
def take_action(self, parsed_args):
@@ -275,6 +279,11 @@ def get_parser(self, prog_name):
275279
parser.add_argument('-t', '--type', type=str, help='Mapping type')
276280
parser.add_argument('--value', type=str, help='Value')
277281
parser.add_argument('cost', type=float, help='Cost')
282+
parser.add_argument('--name', type=str, help='Mapping Name')
283+
parser.add_argument('--start', type=str, help='Mapping Start')
284+
parser.add_argument('--end', type=str, help='Mapping End')
285+
parser.add_argument('--description', type=str,
286+
help='Mapping Description')
278287
return parser
279288

280289

@@ -321,6 +330,11 @@ def get_parser(self, prog_name):
321330
parser.add_argument('--value', type=str, help='Value')
322331
parser.add_argument('--cost', type=str, help='Cost')
323332
parser.add_argument('mapping_id', type=str, help='Mapping ID')
333+
parser.add_argument('--name', type=str, help='Mapping Name')
334+
parser.add_argument('--start', type=str, help='Mapping Start')
335+
parser.add_argument('--end', type=str, help='Mapping End')
336+
parser.add_argument('--description', type=str,
337+
help='Mapping Description')
324338
return parser
325339

326340

cloudkittyclient/v1/rating/pyscripts.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,22 @@ def create_script(self, **kwargs):
5050
:type name: str
5151
:param data: Content of the script
5252
:type data: str
53+
:param start: Date the script starts being valid
54+
:type start: str
55+
:param end: Date the script stops being valid
56+
:type end: str
57+
:param description: Description of the script
58+
:type description: str
5359
"""
5460
for arg in ('name', 'data'):
5561
if not kwargs.get(arg):
5662
raise exc.ArgumentRequired(
5763
"'Argument {} is required.'".format(arg))
5864
url = self.get_url('scripts', kwargs)
59-
body = dict(name=kwargs['name'], data=kwargs['data'])
65+
body = dict(name=kwargs['name'], data=kwargs['data'],
66+
start=kwargs.get('start'),
67+
end=kwargs.get('end'),
68+
description=kwargs.get('description'))
6069
return self.api_client.post(url, json=body).json()
6170

6271
def update_script(self, **kwargs):
@@ -68,11 +77,17 @@ def update_script(self, **kwargs):
6877
:type name: str
6978
:param data: Content of the script
7079
:type data: str
80+
:param start: Date the script starts being valid
81+
:type start: str
82+
:param end: Date the script stops being valid
83+
:type end: str
84+
:param description: Description of the script
85+
:type description: str
7186
"""
7287
if not kwargs.get('script_id'):
7388
raise exc.ArgumentRequired("Argument 'script_id' is required.")
7489
script = self.get_script(script_id=kwargs['script_id'])
75-
for key in ('name', 'data'):
90+
for key in ('name', 'data', 'start', 'end', 'description'):
7691
if kwargs.get(key):
7792
script[key] = kwargs[key]
7893
script.pop('checksum', None)

cloudkittyclient/v1/rating/pyscripts_cli.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ class BaseScriptCli(lister.Lister):
2626
('script_id', 'Script ID'),
2727
('checksum', 'Checksum'),
2828
('data', 'Data'),
29+
('start', 'Script Start Date'),
30+
('end', 'Script End Date'),
31+
('description', 'Script Description')
2932
]
3033

3134

@@ -82,6 +85,10 @@ def get_parser(self, prog_name):
8285
parser = super(CliCreateScript, self).get_parser(prog_name)
8386
parser.add_argument('name', type=str, help='Script Name')
8487
parser.add_argument('data', type=str, help='Script Data or data file')
88+
parser.add_argument('--start', type=str, help='Script Start')
89+
parser.add_argument('--end', type=str, help='Script End')
90+
parser.add_argument('--description', type=str,
91+
help='Script Description')
8592
return parser
8693

8794

@@ -107,6 +114,10 @@ def get_parser(self, prog_name):
107114
parser.add_argument('-n', '--name', type=str, help='Script Name')
108115
parser.add_argument('-d', '--data', type=str,
109116
help='Script Data or data file')
117+
parser.add_argument('--start', type=str, help='Script Start')
118+
parser.add_argument('--end', type=str, help='Script End')
119+
parser.add_argument('--description', type=str,
120+
help='Script Description')
110121
return parser
111122

112123

0 commit comments

Comments
 (0)