Merge "Increase cost fields to 28 digits precision"

This commit is contained in:
Zuul 2020-12-22 17:18:54 +00:00 committed by Gerrit Code Review
commit 84ad7377e8
6 changed files with 120 additions and 43 deletions

View File

@ -0,0 +1,45 @@
# Copyright 2018 OpenStack Foundation
#
# 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.
#
"""Increase cost fields to 30 digits
Revision ID: Ifbf5b2515c7
Revises: 644faa4491fd
Create Date: 2020-09-29 14:22:00.000000
"""
from alembic import op
import importlib
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'Ifbf5b2515c7'
down_revision = '644faa4491fd'
def upgrade():
down_version_module = importlib.import_module(
"cloudkitty.rating.hash.db.sqlalchemy.alembic.versions."
"644faa4491fd_update_tenant_id_type_from_uuid_to_text")
for table_name in ('hashmap_mappings', 'hashmap_thresholds'):
with op.batch_alter_table(
table_name, reflect_args=down_version_module.get_reflect(
table_name)) as batch_op:
batch_op.alter_column('cost',
type_=sa.Numeric(precision=30, scale=28))

View File

@ -230,7 +230,7 @@ class HashMapMapping(Base, HashMapBase):
sqlalchemy.String(255), sqlalchemy.String(255),
nullable=True) nullable=True)
cost = sqlalchemy.Column( cost = sqlalchemy.Column(
sqlalchemy.Numeric(20, 8), sqlalchemy.Numeric(30, 28),
nullable=False) nullable=False)
map_type = sqlalchemy.Column( map_type = sqlalchemy.Column(
sqlalchemy.Enum( sqlalchemy.Enum(
@ -309,7 +309,7 @@ class HashMapThreshold(Base, HashMapBase):
sqlalchemy.Numeric(20, 8), sqlalchemy.Numeric(20, 8),
nullable=True) nullable=True)
cost = sqlalchemy.Column( cost = sqlalchemy.Column(
sqlalchemy.Numeric(20, 8), sqlalchemy.Numeric(30, 28),
nullable=False) nullable=False)
map_type = sqlalchemy.Column( map_type = sqlalchemy.Column(
sqlalchemy.Enum( sqlalchemy.Enum(

View File

@ -28,13 +28,13 @@ tests:
data: data:
service_id: "6c1b8a30-797f-4b7e-ad66-9879b79059fb" service_id: "6c1b8a30-797f-4b7e-ad66-9879b79059fb"
type: "flat" type: "flat"
cost: "0.10000000" cost: "0.1000000000000000055511151231"
status: 201 status: 201
response_json_paths: response_json_paths:
$.mapping_id: "6c1b8a30-797f-4b7e-ad66-9879b79059fb" $.mapping_id: "6c1b8a30-797f-4b7e-ad66-9879b79059fb"
$.service_id: "6c1b8a30-797f-4b7e-ad66-9879b79059fb" $.service_id: "6c1b8a30-797f-4b7e-ad66-9879b79059fb"
$.type: "flat" $.type: "flat"
$.cost: "0.10000000" $.cost: "0.1000000000000000055511151231"
response_headers: response_headers:
location: '$SCHEME://$NETLOC/v1/rating/module_config/hashmap/mappings/6c1b8a30-797f-4b7e-ad66-9879b79059fb' location: '$SCHEME://$NETLOC/v1/rating/module_config/hashmap/mappings/6c1b8a30-797f-4b7e-ad66-9879b79059fb'
@ -60,7 +60,7 @@ tests:
$.service_id: "6c1b8a30-797f-4b7e-ad66-9879b79059fb" $.service_id: "6c1b8a30-797f-4b7e-ad66-9879b79059fb"
$.level: "2.00000000" $.level: "2.00000000"
$.type: "flat" $.type: "flat"
$.cost: "0.10000000" $.cost: "0.1000000000000000055511151231"
response_headers: response_headers:
location: '$SCHEME://$NETLOC/v1/rating/module_config/hashmap/thresholds/6c1b8a30-797f-4b7e-ad66-9879b79059fb' location: '$SCHEME://$NETLOC/v1/rating/module_config/hashmap/thresholds/6c1b8a30-797f-4b7e-ad66-9879b79059fb'
@ -103,7 +103,7 @@ tests:
$.field_id: "6c1b8a30-797f-4b7e-ad66-9879b79059fb" $.field_id: "6c1b8a30-797f-4b7e-ad66-9879b79059fb"
$.value: "04774238-fcad-11e5-a90e-6391fd56aab2" $.value: "04774238-fcad-11e5-a90e-6391fd56aab2"
$.type: "flat" $.type: "flat"
$.cost: "0.10000000" $.cost: "0.1000000000000000055511151231"
response_headers: response_headers:
location: '$SCHEME://$NETLOC/v1/rating/module_config/hashmap/mappings/6c1b8a30-797f-4b7e-ad66-9879b79059fb' location: '$SCHEME://$NETLOC/v1/rating/module_config/hashmap/mappings/6c1b8a30-797f-4b7e-ad66-9879b79059fb'
@ -124,7 +124,7 @@ tests:
$.field_id: "6c1b8a30-797f-4b7e-ad66-9879b79059fb" $.field_id: "6c1b8a30-797f-4b7e-ad66-9879b79059fb"
$.level: "2.00000000" $.level: "2.00000000"
$.type: "flat" $.type: "flat"
$.cost: "0.10000000" $.cost: "0.1000000000000000055511151231"
response_headers: response_headers:
location: '$SCHEME://$NETLOC/v1/rating/module_config/hashmap/thresholds/6c1b8a30-797f-4b7e-ad66-9879b79059fb' location: '$SCHEME://$NETLOC/v1/rating/module_config/hashmap/thresholds/6c1b8a30-797f-4b7e-ad66-9879b79059fb'

View File

@ -48,7 +48,7 @@ tests:
response_json_paths: response_json_paths:
$.service_id: $RESPONSE['$.service_id'] $.service_id: $RESPONSE['$.service_id']
$.type: "flat" $.type: "flat"
$.cost: "0.10000000" $.cost: "0.1000000000000000055511151231"
- name: delete a flat service mapping - name: delete a flat service mapping
url: /v1/rating/module_config/hashmap/mappings/$RESPONSE['$.mapping_id'] url: /v1/rating/module_config/hashmap/mappings/$RESPONSE['$.mapping_id']
@ -76,7 +76,7 @@ tests:
response_json_paths: response_json_paths:
$.service_id: $RESPONSE['$.services[0].service_id'] $.service_id: $RESPONSE['$.services[0].service_id']
$.type: "rate" $.type: "rate"
$.cost: "0.20000000" $.cost: "0.2000000000000000111022302463"
- name: create a flat service mapping for a tenant - name: create a flat service mapping for a tenant
url: /v1/rating/module_config/hashmap/mappings url: /v1/rating/module_config/hashmap/mappings
@ -93,7 +93,7 @@ tests:
response_json_paths: response_json_paths:
$.service_id: $ENVIRON['hash_service_id'] $.service_id: $ENVIRON['hash_service_id']
$.type: "flat" $.type: "flat"
$.cost: "0.20000000" $.cost: "0.2000000000000000111022302463"
$.tenant_id: "24a7fdae-27ff-11e6-8c4f-6b725a05bf50" $.tenant_id: "24a7fdae-27ff-11e6-8c4f-6b725a05bf50"
- name: list service mappings no tenant filtering - name: list service mappings no tenant filtering
@ -131,7 +131,7 @@ tests:
$.service_id: $ENVIRON['hash_service_id'] $.service_id: $ENVIRON['hash_service_id']
$.level: "2.00000000" $.level: "2.00000000"
$.type: "flat" $.type: "flat"
$.cost: "0.20000000" $.cost: "0.2000000000000000111022302463"
$.tenant_id: "24a7fdae-27ff-11e6-8c4f-6b725a05bf50" $.tenant_id: "24a7fdae-27ff-11e6-8c4f-6b725a05bf50"
- name: list service thresholds no tenant filtering - name: list service thresholds no tenant filtering
@ -191,7 +191,7 @@ tests:
response_json_paths: response_json_paths:
$.field_id: $RESPONSE['$.field_id'] $.field_id: $RESPONSE['$.field_id']
$.type: "rate" $.type: "rate"
$.cost: "0.20000000" $.cost: "0.2000000000000000111022302463"
- name: delete a flat field mapping - name: delete a flat field mapping
url: /v1/rating/module_config/hashmap/mappings/$RESPONSE['$.mapping_id'] url: /v1/rating/module_config/hashmap/mappings/$RESPONSE['$.mapping_id']
@ -222,7 +222,7 @@ tests:
response_json_paths: response_json_paths:
$.field_id: $RESPONSE['$.fields[0].field_id'] $.field_id: $RESPONSE['$.fields[0].field_id']
$.type: "rate" $.type: "rate"
$.cost: "0.20000000" $.cost: "0.2000000000000000111022302463"
response_store_environ: response_store_environ:
hash_rate_mapping_id: $.mapping_id hash_rate_mapping_id: $.mapping_id
@ -245,7 +245,7 @@ tests:
$.mapping_id: $ENVIRON['hash_rate_mapping_id'] $.mapping_id: $ENVIRON['hash_rate_mapping_id']
$.field_id: $ENVIRON['hash_field_id'] $.field_id: $ENVIRON['hash_field_id']
$.type: "rate" $.type: "rate"
$.cost: "0.30000000" $.cost: "0.2999999999999999888977697537"
$.value: "f17a0674-0004-11e6-a16b-cf941f4668c4" $.value: "f17a0674-0004-11e6-a16b-cf941f4668c4"
- name: delete a field - name: delete a field

View File

@ -343,7 +343,8 @@ class HashMapRatingTest(tests.TestCase):
mapping = self._db_api.get_mapping(mapping_db.mapping_id) mapping = self._db_api.get_mapping(mapping_db.mapping_id)
self.assertEqual('flat', mapping.map_type) self.assertEqual('flat', mapping.map_type)
self.assertEqual('m1.tiny', mapping.value) self.assertEqual('m1.tiny', mapping.value)
self.assertEqual(decimal.Decimal('1.337'), mapping.cost) self.assertEqual(
decimal.Decimal('1.3369999999999999662492200514'), mapping.cost)
self.assertEqual(field_db.id, mapping.field_id) self.assertEqual(field_db.id, mapping.field_id)
def test_list_mappings_from_services(self): def test_list_mappings_from_services(self):
@ -524,7 +525,8 @@ class HashMapRatingTest(tests.TestCase):
threshold = self._db_api.get_threshold(threshold_db.threshold_id) threshold = self._db_api.get_threshold(threshold_db.threshold_id)
self.assertEqual('rate', threshold.map_type) self.assertEqual('rate', threshold.map_type)
self.assertEqual(decimal.Decimal('64'), threshold.level) self.assertEqual(decimal.Decimal('64'), threshold.level)
self.assertEqual(decimal.Decimal('0.1337'), threshold.cost) self.assertEqual(
decimal.Decimal('0.1337000000000000132782673745'), threshold.cost)
self.assertEqual(field_db.id, threshold.field_id) self.assertEqual(field_db.id, threshold.field_id)
def test_list_thresholds_from_only_group(self): def test_list_thresholds_from_only_group(self):
@ -768,11 +770,13 @@ class HashMapRatingTest(tests.TestCase):
'mappings': { 'mappings': {
'_DEFAULT_': { '_DEFAULT_': {
'm1.tiny': { 'm1.tiny': {
'cost': decimal.Decimal('2'), 'cost': decimal.Decimal(
'2.0000000000000000000000000000'),
'type': 'flat'}}, 'type': 'flat'}},
'test_group': { 'test_group': {
'm1.large': { 'm1.large': {
'cost': decimal.Decimal('13.37'), 'cost': decimal.Decimal(
'13.3699999999999992184029906639'),
'type': 'rate'}}}, 'type': 'rate'}}},
'thresholds': {}}, 'thresholds': {}},
'memory': { 'memory': {
@ -780,14 +784,17 @@ class HashMapRatingTest(tests.TestCase):
'thresholds': { 'thresholds': {
'test_group': { 'test_group': {
64: { 64: {
'cost': decimal.Decimal('0.03'), 'cost': decimal.Decimal(
'0.0299999999999999988897769754'),
'type': 'flat'}, 'type': 'flat'},
128: { 128: {
'cost': decimal.Decimal('0.03'), 'cost': decimal.Decimal(
'0.0299999999999999988897769754'),
'type': 'flat'}}}}}, 'type': 'flat'}}}}},
'mappings': { 'mappings': {
'_DEFAULT_': { '_DEFAULT_': {
'cost': decimal.Decimal('1.42'), 'cost': decimal.Decimal(
'1.4199999999999999289457264240'),
'type': 'rate'}}, 'type': 'rate'}},
'thresholds': {}}} 'thresholds': {}}}
self.assertEqual(expect, self.assertEqual(expect,
@ -817,11 +824,11 @@ class HashMapRatingTest(tests.TestCase):
expected_result = { expected_result = {
'_DEFAULT_': { '_DEFAULT_': {
'm1.tiny': { 'm1.tiny': {
'cost': decimal.Decimal('1.337'), 'cost': decimal.Decimal('1.3369999999999999662492200514'),
'type': 'flat'}}, 'type': 'flat'}},
'test_group': { 'test_group': {
'm1.large': { 'm1.large': {
'cost': decimal.Decimal('13.37'), 'cost': decimal.Decimal('13.3699999999999992184029906639'),
'type': 'rate'}}} 'type': 'rate'}}}
self.assertEqual(expected_result, result) self.assertEqual(expected_result, result)
@ -844,7 +851,7 @@ class HashMapRatingTest(tests.TestCase):
expected_result = { expected_result = {
'test_group': { 'test_group': {
1000: { 1000: {
'cost': decimal.Decimal('3.1337'), 'cost': decimal.Decimal('3.1337000000000001520561454527'),
'type': 'flat'}}} 'type': 'flat'}}}
self.assertEqual(expected_result, result) self.assertEqual(expected_result, result)
@ -873,10 +880,14 @@ class HashMapRatingTest(tests.TestCase):
actual_data = [actual_data] actual_data = [actual_data]
df_dicts = [d.as_dict(mutable=True) for d in expected_data] df_dicts = [d.as_dict(mutable=True) for d in expected_data]
compute_list = df_dicts[0]['usage']['compute'] compute_list = df_dicts[0]['usage']['compute']
compute_list[0]['rating'] = {'price': decimal.Decimal('2.757')} compute_list[0]['rating'] = {'price': decimal.Decimal(
compute_list[1]['rating'] = {'price': decimal.Decimal('5.514')} '2.756999999999999895194946475')}
compute_list[2]['rating'] = {'price': decimal.Decimal('5.514')} compute_list[1]['rating'] = {'price': decimal.Decimal(
compute_list[3]['rating'] = {'price': decimal.Decimal('2.757')} '5.513999999999999790389892950')}
compute_list[2]['rating'] = {'price': decimal.Decimal(
'5.513999999999999790389892950')}
compute_list[3]['rating'] = {'price': decimal.Decimal(
'2.756999999999999895194946475')}
self.assertEqual(df_dicts, [d.as_dict(mutable=True) self.assertEqual(df_dicts, [d.as_dict(mutable=True)
for d in actual_data]) for d in actual_data])
@ -917,10 +928,13 @@ class HashMapRatingTest(tests.TestCase):
actual_data = [actual_data] actual_data = [actual_data]
df_dicts = [d.as_dict(mutable=True) for d in expected_data] df_dicts = [d.as_dict(mutable=True) for d in expected_data]
compute_list = df_dicts[0]['usage']['compute'] compute_list = df_dicts[0]['usage']['compute']
compute_list[0]['rating'] = {'price': decimal.Decimal('1.337')} compute_list[0]['rating'] = {'price': decimal.Decimal(
compute_list[1]['rating'] = {'price': decimal.Decimal('2.84')} '1.336999999999999966249220051')}
compute_list[1]['rating'] = {'price': decimal.Decimal(
'2.839999999999999857891452848')}
compute_list[2]['rating'] = {'price': decimal.Decimal('0')} compute_list[2]['rating'] = {'price': decimal.Decimal('0')}
compute_list[3]['rating'] = {'price': decimal.Decimal('1.47070')} compute_list[3]['rating'] = {'price': decimal.Decimal(
'1.470700000000000081623596770')}
self.assertEqual(df_dicts, [d.as_dict(mutable=True) self.assertEqual(df_dicts, [d.as_dict(mutable=True)
for d in actual_data]) for d in actual_data])
@ -975,10 +989,14 @@ class HashMapRatingTest(tests.TestCase):
actual_data = [actual_data] actual_data = [actual_data]
df_dicts = [d.as_dict(mutable=True) for d in expected_data] df_dicts = [d.as_dict(mutable=True) for d in expected_data]
compute_list = df_dicts[0]['usage']['compute'] compute_list = df_dicts[0]['usage']['compute']
compute_list[0]['rating'] = {'price': decimal.Decimal('0.1337')} compute_list[0]['rating'] = {'price': decimal.Decimal(
compute_list[1]['rating'] = {'price': decimal.Decimal('0.4')} '0.1337000000000000132782673745')}
compute_list[2]['rating'] = {'price': decimal.Decimal('0.4')} compute_list[1]['rating'] = {'price': decimal.Decimal(
compute_list[3]['rating'] = {'price': decimal.Decimal('0.1337')} '0.4000000000000000222044604926')}
compute_list[2]['rating'] = {'price': decimal.Decimal(
'0.4000000000000000222044604926')}
compute_list[3]['rating'] = {'price': decimal.Decimal(
'0.1337000000000000132782673745')}
self.assertEqual(df_dicts, [d.as_dict(mutable=True) self.assertEqual(df_dicts, [d.as_dict(mutable=True)
for d in actual_data]) for d in actual_data])
@ -1030,10 +1048,14 @@ class HashMapRatingTest(tests.TestCase):
actual_data = [actual_data] actual_data = [actual_data]
df_dicts = [d.as_dict(mutable=True) for d in expected_data] df_dicts = [d.as_dict(mutable=True) for d in expected_data]
compute_list = df_dicts[0]['usage']['compute'] compute_list = df_dicts[0]['usage']['compute']
compute_list[0]['rating'] = {'price': decimal.Decimal('0.1')} compute_list[0]['rating'] = {'price': decimal.Decimal(
compute_list[1]['rating'] = {'price': decimal.Decimal('0.15')} '0.1000000000000000055511151231')}
compute_list[2]['rating'] = {'price': decimal.Decimal('0.15')} compute_list[1]['rating'] = {'price': decimal.Decimal(
compute_list[3]['rating'] = {'price': decimal.Decimal('0.1')} '0.1499999999999999944488848769')}
compute_list[2]['rating'] = {'price': decimal.Decimal(
'0.1499999999999999944488848769')}
compute_list[3]['rating'] = {'price': decimal.Decimal(
'0.1000000000000000055511151231')}
self.assertEqual(df_dicts, [d.as_dict(mutable=True) self.assertEqual(df_dicts, [d.as_dict(mutable=True)
for d in actual_data]) for d in actual_data])
@ -1175,11 +1197,15 @@ class HashMapRatingTest(tests.TestCase):
end=expected_data[0].end) end=expected_data[0].end)
df_dicts = [d.as_dict(mutable=True) for d in expected_data] df_dicts = [d.as_dict(mutable=True) for d in expected_data]
compute_list = df_dicts[0]['usage']['compute'] compute_list = df_dicts[0]['usage']['compute']
compute_list[0]['rating'] = {'price': decimal.Decimal('2.487')} compute_list[0]['rating'] = {'price': decimal.Decimal(
compute_list[1]['rating'] = {'price': decimal.Decimal('5.564')} '2.486999999999999960698104928')}
compute_list[1]['rating'] = {'price': decimal.Decimal(
'5.564000000000000155875312656')}
# 8vcpu mapping * 2 + service_mapping * 1 + 128m ram threshold * 2 # 8vcpu mapping * 2 + service_mapping * 1 + 128m ram threshold * 2
compute_list[2]['rating'] = {'price': decimal.Decimal('34.40')} compute_list[2]['rating'] = {'price': decimal.Decimal(
compute_list[3]['rating'] = {'price': decimal.Decimal('2.6357')} '34.40000000000000002220446049')}
compute_list[3]['rating'] = {'price': decimal.Decimal(
'2.635700000000000088840046430')}
actual_data = [self._hash.process(d) for d in expected_data] actual_data = [self._hash.process(d) for d in expected_data]
self.assertEqual(df_dicts, [d.as_dict(mutable=True) self.assertEqual(df_dicts, [d.as_dict(mutable=True)
for d in actual_data]) for d in actual_data])

View File

@ -157,6 +157,12 @@ Apart from that, it works the same way as a mapping.
As for mappings, a threshold can be tied to a specific scope/project. As for mappings, a threshold can be tied to a specific scope/project.
Cost
----
The cost option is the actual cost for the rating period. It has a precision of
28 decimal digits (on the right side of the number), and 30 digits on the left
side of the number.
Examples Examples
======== ========