Merge "Add support to rating rules with start and end"

This commit is contained in:
Zuul
2025-09-15 13:48:31 +00:00
committed by Gerrit Code Review
8 changed files with 168 additions and 10 deletions

View File

@@ -13,6 +13,9 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
# #
from datetime import datetime
from datetime import timedelta
from cloudkittyclient.tests.functional import base from cloudkittyclient.tests.functional import base
@@ -136,13 +139,15 @@ class CkHashmapTest(base.BaseFunctionalTest):
self.assertEqual(len(resp), 0) self.assertEqual(len(resp), 0)
def test_create_get_update_delete_mapping_service(self): def test_create_get_update_delete_mapping_service(self):
future_date = datetime.now() + timedelta(days=1)
date_iso = future_date.isoformat()
resp = self.runner('hashmap service create', params='testservice')[0] resp = self.runner('hashmap service create', params='testservice')[0]
service_id = resp['Service ID'] service_id = resp['Service ID']
self._services.append(service_id) self._services.append(service_id)
# Create mapping # Create mapping
resp = self.runner('hashmap mapping create', resp = self.runner('hashmap mapping create',
params='-s {} 12'.format(service_id))[0] params=f'-s {service_id} 12 --start {date_iso}')[0]
mapping_id = resp['Mapping ID'] mapping_id = resp['Mapping ID']
self._mappings.append(mapping_id) self._mappings.append(mapping_id)
self.assertEqual(resp['Service ID'], service_id) self.assertEqual(resp['Service ID'], service_id)
@@ -173,6 +178,8 @@ class CkHashmapTest(base.BaseFunctionalTest):
'hashmap service delete', params=service_id, has_output=False) 'hashmap service delete', params=service_id, has_output=False)
def test_create_get_update_delete_mapping_field(self): def test_create_get_update_delete_mapping_field(self):
future_date = datetime.now() + timedelta(days=1)
date_iso = future_date.isoformat()
resp = self.runner('hashmap service create', params='testservice')[0] resp = self.runner('hashmap service create', params='testservice')[0]
service_id = resp['Service ID'] service_id = resp['Service ID']
self._services.append(service_id) self._services.append(service_id)
@@ -185,7 +192,8 @@ class CkHashmapTest(base.BaseFunctionalTest):
# Create mapping # Create mapping
resp = self.runner( resp = self.runner(
'hashmap mapping create', 'hashmap mapping create',
params='--field-id {} 12 --value testvalue'.format(field_id))[0] params=f'--field-id {field_id} 12 --value '
f'testvalue --start {date_iso}')[0]
mapping_id = resp['Mapping ID'] mapping_id = resp['Mapping ID']
self._mappings.append(service_id) self._mappings.append(service_id)
self.assertEqual(resp['Field ID'], field_id) self.assertEqual(resp['Field ID'], field_id)
@@ -203,6 +211,45 @@ class CkHashmapTest(base.BaseFunctionalTest):
params='--cost 10 {}'.format(mapping_id))[0] params='--cost 10 {}'.format(mapping_id))[0]
self.assertEqual(float(resp['Cost']), float(10)) self.assertEqual(float(resp['Cost']), float(10))
def test_create_get_update_delete_mapping_field_started(self):
resp = self.runner('hashmap service create',
params='testservice_date_started')[0]
service_id = resp['Service ID']
self._services.append(service_id)
resp = self.runner(
'hashmap field create',
params='{} testfield_date_started'.format(service_id))[0]
field_id = resp['Field ID']
self._fields.append(field_id)
# Create mapping
resp = self.runner(
'hashmap mapping create',
params=f'--field-id {field_id} 12 --value '
f'testvalue')[0]
mapping_id = resp['Mapping ID']
self._mappings.append(service_id)
self.assertEqual(resp['Field ID'], field_id)
self.assertEqual(float(resp['Cost']), float(12))
self.assertEqual(resp['Value'], 'testvalue')
# Get mapping
resp = self.runner(
'hashmap mapping get', params=mapping_id)[0]
self.assertEqual(resp['Mapping ID'], mapping_id)
self.assertEqual(float(resp['Cost']), float(12))
# Should not be able to update a rule that is running (start < now)
try:
self.runner('hashmap mapping update',
params='--cost 10 {}'.format(mapping_id))[0]
except RuntimeError as e:
expected_error = ("You are allowed to update only the attribute "
"[end] as this rule is already running as it "
"started on ")
self.assertIn(expected_error, str(e))
def test_group_mappings_get(self): def test_group_mappings_get(self):
# Service and group # Service and group
resp = self.runner('hashmap service create', params='testservice')[0] resp = self.runner('hashmap service create', params='testservice')[0]

View File

@@ -13,6 +13,9 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
# #
from datetime import datetime
from datetime import timedelta
from cloudkittyclient.tests.functional import base from cloudkittyclient.tests.functional import base
@@ -23,9 +26,12 @@ class CkPyscriptTest(base.BaseFunctionalTest):
self.runner = self.cloudkitty self.runner = self.cloudkitty
def test_create_get_update_list_delete(self): def test_create_get_update_list_delete(self):
future_date = datetime.now() + timedelta(days=1)
date_iso = future_date.isoformat()
# Create # Create
resp = self.runner( resp = self.runner(
'pyscript create', params="testscript 'return 0'")[0] 'pyscript create', params=f"testscript "
f"'return 0' --start {date_iso}")[0]
script_id = resp['Script ID'] script_id = resp['Script ID']
self.assertEqual(resp['Name'], 'testscript') self.assertEqual(resp['Name'], 'testscript')
@@ -37,8 +43,9 @@ class CkPyscriptTest(base.BaseFunctionalTest):
# Update # Update
resp = self.runner( resp = self.runner(
'pyscript update', 'pyscript update',
params="-n newname -d 'return 1' {}".format(script_id))[0] params="-d 'return 1' {} --description "
self.assertEqual(resp['Name'], 'newname') "desc".format(script_id))[0]
self.assertEqual(resp['Script Description'], 'desc')
self.assertEqual(resp['Script ID'], script_id) self.assertEqual(resp['Script ID'], script_id)
self.assertEqual(resp['Data'], 'return 1') self.assertEqual(resp['Data'], 'return 1')
@@ -46,13 +53,49 @@ class CkPyscriptTest(base.BaseFunctionalTest):
resp = self.runner('pyscript list') resp = self.runner('pyscript list')
self.assertEqual(len(resp), 1) self.assertEqual(len(resp), 1)
resp = resp[0] resp = resp[0]
self.assertEqual(resp['Name'], 'newname') self.assertEqual(resp['Script Description'], 'desc')
self.assertEqual(resp['Script ID'], script_id) self.assertEqual(resp['Script ID'], script_id)
self.assertEqual(resp['Data'], 'return 1') self.assertEqual(resp['Data'], 'return 1')
# Delete # Delete
self.runner('pyscript delete', params=script_id, has_output=False) self.runner('pyscript delete', params=script_id, has_output=False)
def test_create_get_update_list_delete_started(self):
# Create
resp = self.runner(
'pyscript create', params="testscript_started "
"'return 0'")[0]
script_id = resp['Script ID']
self.assertEqual(resp['Name'], 'testscript_started')
# Get
resp = self.runner('pyscript get', params=script_id)[0]
self.assertEqual(resp['Name'], 'testscript_started')
self.assertEqual(resp['Script ID'], script_id)
# Should not be able to update a rule that is running (start < now)
try:
self.runner(
'pyscript update',
params="-d 'return 1' {} --description "
"desc".format(script_id))[0]
except RuntimeError as e:
expected_error = ("You are allowed to update only the attribute "
"[end] as this rule is already running as it "
"started on ")
self.assertIn(expected_error, str(e))
# List
resp = self.runner('pyscript list')
self.assertEqual(len(resp), 1)
resp = resp[0]
self.assertEqual(resp['Script Description'], None)
self.assertEqual(resp['Script ID'], script_id)
self.assertEqual(resp['Data'], 'return 0')
# Delete
self.runner('pyscript delete', params=script_id, has_output=False)
class OSCPyscriptTest(CkPyscriptTest): class OSCPyscriptTest(CkPyscriptTest):

View File

@@ -110,7 +110,10 @@ class TestHashmap(base.BaseAPIEndpointTestCase):
self.assertRaises(exc.ArgumentRequired, self.hashmap.get_mapping) self.assertRaises(exc.ArgumentRequired, self.hashmap.get_mapping)
def test_create_mapping(self): def test_create_mapping(self):
kwargs = dict(cost=2, value='value', field_id='field_id') kwargs = dict(cost=2, value='value', field_id='field_id',
name='name', start="2024-01-01",
end="2024-01-01",
description="description")
body = dict( body = dict(
cost=kwargs.get('cost'), cost=kwargs.get('cost'),
value=kwargs.get('value'), value=kwargs.get('value'),
@@ -119,6 +122,10 @@ class TestHashmap(base.BaseAPIEndpointTestCase):
group_id=kwargs.get('group_id'), group_id=kwargs.get('group_id'),
tenant_id=kwargs.get('tenant_id'), tenant_id=kwargs.get('tenant_id'),
type=kwargs.get('type') or 'flat', type=kwargs.get('type') or 'flat',
start="2024-01-01",
end="2024-01-01",
description="description",
name='name'
) )
self.hashmap.create_mapping(**kwargs) self.hashmap.create_mapping(**kwargs)
self.api_client.post.assert_called_once_with( self.api_client.post.assert_called_once_with(

View File

@@ -38,7 +38,8 @@ class TestPyscripts(base.BaseAPIEndpointTestCase):
self.assertRaises(exc.ArgumentRequired, self.pyscripts.get_script) self.assertRaises(exc.ArgumentRequired, self.pyscripts.get_script)
def test_create_script(self): def test_create_script(self):
kwargs = dict(name='name', data='data') kwargs = dict(name='name', data='data', start=None,
end=None, description=None)
self.pyscripts.create_script(**kwargs) self.pyscripts.create_script(**kwargs)
self.api_client.post.assert_called_once_with( self.api_client.post.assert_called_once_with(
'/v1/rating/module_config/pyscripts/scripts/', json=kwargs) '/v1/rating/module_config/pyscripts/scripts/', json=kwargs)

View File

@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
# #
import uuid
from cloudkittyclient.common import base from cloudkittyclient.common import base
from cloudkittyclient import exc from cloudkittyclient import exc
@@ -171,6 +173,14 @@ class HashmapManager(base.BaseManager):
:type type: str :type type: str
:param value: Value of the mapping :param value: Value of the mapping
:type value: str :type value: str
:param name: Name of the mapping
:type name: str
:param start: Date the mapping starts being valid
:type start: str
:param end: Date the mapping stops being valid
:type end: str
:param description: Description of the mapping
:type description: str
""" """
if kwargs.get('cost') is None: if kwargs.get('cost') is None:
raise exc.ArgumentRequired("'cost' argument is required") raise exc.ArgumentRequired("'cost' argument is required")
@@ -196,6 +206,16 @@ class HashmapManager(base.BaseManager):
tenant_id=kwargs.get('tenant_id'), tenant_id=kwargs.get('tenant_id'),
type=kwargs.get('type') or 'flat', type=kwargs.get('type') or 'flat',
) )
if kwargs.get('description'):
body['description'] = kwargs.get('description')
if kwargs.get('start'):
body['start'] = kwargs.get('start')
if kwargs.get('end'):
body['end'] = kwargs.get('end')
if kwargs.get('name'):
body['name'] = kwargs.get('name')
else:
body['name'] = uuid.uuid4().hex[:24]
url = self.get_url('mappings', kwargs) url = self.get_url('mappings', kwargs)
return self.api_client.post(url, json=body).json() return self.api_client.post(url, json=body).json()

View File

@@ -257,6 +257,10 @@ class CliCreateMapping(lister.Lister):
('service_id', 'Service ID'), ('service_id', 'Service ID'),
('group_id', 'Group ID'), ('group_id', 'Group ID'),
('tenant_id', 'Project ID'), ('tenant_id', 'Project ID'),
('name', 'Mapping Name'),
('start', 'Mapping Start Date'),
('end', 'Mapping End Date'),
('Description', 'Mapping Description')
] ]
def take_action(self, parsed_args): def take_action(self, parsed_args):
@@ -275,6 +279,11 @@ class CliCreateMapping(lister.Lister):
parser.add_argument('-t', '--type', type=str, help='Mapping type') parser.add_argument('-t', '--type', type=str, help='Mapping type')
parser.add_argument('--value', type=str, help='Value') parser.add_argument('--value', type=str, help='Value')
parser.add_argument('cost', type=float, help='Cost') parser.add_argument('cost', type=float, help='Cost')
parser.add_argument('--name', type=str, help='Mapping Name')
parser.add_argument('--start', type=str, help='Mapping Start')
parser.add_argument('--end', type=str, help='Mapping End')
parser.add_argument('--description', type=str,
help='Mapping Description')
return parser return parser
@@ -321,6 +330,11 @@ class CliUpdateMapping(lister.Lister):
parser.add_argument('--value', type=str, help='Value') parser.add_argument('--value', type=str, help='Value')
parser.add_argument('--cost', type=str, help='Cost') parser.add_argument('--cost', type=str, help='Cost')
parser.add_argument('mapping_id', type=str, help='Mapping ID') parser.add_argument('mapping_id', type=str, help='Mapping ID')
parser.add_argument('--name', type=str, help='Mapping Name')
parser.add_argument('--start', type=str, help='Mapping Start')
parser.add_argument('--end', type=str, help='Mapping End')
parser.add_argument('--description', type=str,
help='Mapping Description')
return parser return parser

View File

@@ -50,13 +50,22 @@ class PyscriptManager(base.BaseManager):
:type name: str :type name: str
:param data: Content of the script :param data: Content of the script
:type data: str :type data: str
:param start: Date the script starts being valid
:type start: str
:param end: Date the script stops being valid
:type end: str
:param description: Description of the script
:type description: str
""" """
for arg in ('name', 'data'): for arg in ('name', 'data'):
if not kwargs.get(arg): if not kwargs.get(arg):
raise exc.ArgumentRequired( raise exc.ArgumentRequired(
"'Argument {} is required.'".format(arg)) "'Argument {} is required.'".format(arg))
url = self.get_url('scripts', kwargs) url = self.get_url('scripts', kwargs)
body = dict(name=kwargs['name'], data=kwargs['data']) body = dict(name=kwargs['name'], data=kwargs['data'],
start=kwargs.get('start'),
end=kwargs.get('end'),
description=kwargs.get('description'))
return self.api_client.post(url, json=body).json() return self.api_client.post(url, json=body).json()
def update_script(self, **kwargs): def update_script(self, **kwargs):
@@ -68,11 +77,17 @@ class PyscriptManager(base.BaseManager):
:type name: str :type name: str
:param data: Content of the script :param data: Content of the script
:type data: str :type data: str
:param start: Date the script starts being valid
:type start: str
:param end: Date the script stops being valid
:type end: str
:param description: Description of the script
:type description: str
""" """
if not kwargs.get('script_id'): if not kwargs.get('script_id'):
raise exc.ArgumentRequired("Argument 'script_id' is required.") raise exc.ArgumentRequired("Argument 'script_id' is required.")
script = self.get_script(script_id=kwargs['script_id']) script = self.get_script(script_id=kwargs['script_id'])
for key in ('name', 'data'): for key in ('name', 'data', 'start', 'end', 'description'):
if kwargs.get(key): if kwargs.get(key):
script[key] = kwargs[key] script[key] = kwargs[key]
script.pop('checksum', None) script.pop('checksum', None)

View File

@@ -26,6 +26,9 @@ class BaseScriptCli(lister.Lister):
('script_id', 'Script ID'), ('script_id', 'Script ID'),
('checksum', 'Checksum'), ('checksum', 'Checksum'),
('data', 'Data'), ('data', 'Data'),
('start', 'Script Start Date'),
('end', 'Script End Date'),
('description', 'Script Description')
] ]
@@ -82,6 +85,10 @@ class CliCreateScript(BaseScriptCli):
parser = super(CliCreateScript, self).get_parser(prog_name) parser = super(CliCreateScript, self).get_parser(prog_name)
parser.add_argument('name', type=str, help='Script Name') parser.add_argument('name', type=str, help='Script Name')
parser.add_argument('data', type=str, help='Script Data or data file') parser.add_argument('data', type=str, help='Script Data or data file')
parser.add_argument('--start', type=str, help='Script Start')
parser.add_argument('--end', type=str, help='Script End')
parser.add_argument('--description', type=str,
help='Script Description')
return parser return parser
@@ -107,6 +114,10 @@ class CliUpdateScript(BaseScriptCli):
parser.add_argument('-n', '--name', type=str, help='Script Name') parser.add_argument('-n', '--name', type=str, help='Script Name')
parser.add_argument('-d', '--data', type=str, parser.add_argument('-d', '--data', type=str,
help='Script Data or data file') help='Script Data or data file')
parser.add_argument('--start', type=str, help='Script Start')
parser.add_argument('--end', type=str, help='Script End')
parser.add_argument('--description', type=str,
help='Script Description')
return parser return parser