Add description option to a rating metric definition

There is the need to add human-readable description to the metric
definition. This can then be used to create custom reports in the
`summary` GET API. The value has to be stored in the backend as
we do with the alt_name and unit of the metric as well.

Depends-On: https://review.opendev.org/c/openstack/cloudkitty/+/861786
Change-Id: Icea8d00eaf3343e59f0f7b2234754f6abcb23258
This commit is contained in:
Rafael Weingärtner 2022-10-27 10:08:48 -03:00
parent 8eb8c4931a
commit 3aa31f8719
11 changed files with 61 additions and 11 deletions

View File

@ -70,6 +70,8 @@ def MetricDict(value):
CONF_BASE_SCHEMA = {Required('metrics'): MetricDict}
METRIC_BASE_SCHEMA = {
# Human-readable description for the CloudKitty rating type
Optional('description'): All(str, Length(min=1)),
# Display unit
Required('unit'): All(str, Length(min=1)),
# Factor for unit converion
@ -249,7 +251,8 @@ class BaseCollector(object, metaclass=abc.ABCMeta):
return name, data
def _create_data_point(self, unit, qty, price, groupby, metadata, start):
def _create_data_point(self, metric, qty, price, groupby, metadata, start):
unit = metric['unit']
if not start:
start = datetime.datetime.now()
LOG.debug("Collector [%s]. No start datetime defined for "
@ -271,7 +274,8 @@ class BaseCollector(object, metaclass=abc.ABCMeta):
groupby['month'] = month_of_the_year
groupby['year'] = year
return DataPoint(unit, qty, price, groupby, metadata)
return DataPoint(unit, qty, price, groupby, metadata,
metric.get('description'))
class InvalidConfiguration(Exception):

View File

@ -517,9 +517,10 @@ class GnocchiCollector(collector.BaseCollector):
project_id, start, end, e),
)
continue
point = self._create_data_point(
met['unit'], qty, 0, groupby, metadata, start)
point = self._create_data_point(met, qty, 0, groupby,
metadata, start)
formated_resources.append(point)
return formated_resources
@staticmethod

View File

@ -231,7 +231,7 @@ class MonascaCollector(collector.BaseCollector):
metadata, groupby, qty = self._format_data(
met, d, resources_info)
point = self._create_data_point(
met['unit'], qty, 0, groupby, metadata, start)
point = self._create_data_point(met, qty, 0, groupby,
metadata, start)
formated_resources.append(point)
return formated_resources

View File

@ -241,9 +241,8 @@ class PrometheusCollector(collector.BaseCollector):
end,
item,
)
point = self._create_data_point(self.conf[metric_name]['unit'],
qty, 0, groupby, metadata, start)
point = self._create_data_point(self.conf[metric_name], qty,
0, groupby, metadata, start)
formatted_resources.append(point)
return formatted_resources

View File

@ -44,12 +44,12 @@ DATAPOINT_SCHEMA = voluptuous.Schema({
_DataPointBase = collections.namedtuple(
"DataPoint",
field_names=("unit", "qty", "price", "groupby", "metadata"))
field_names=("unit", "qty", "price", "groupby", "metadata", "description"))
class DataPoint(_DataPointBase):
def __new__(cls, unit, qty, price, groupby, metadata):
def __new__(cls, unit, qty, price, groupby, metadata, description=None):
return _DataPointBase.__new__(
cls,
unit or "undefined",
@ -58,6 +58,7 @@ class DataPoint(_DataPointBase):
decimal.Decimal(str(price) if isinstance(price, float) else price),
datastructures.ImmutableDict(groupby),
datastructures.ImmutableDict(metadata),
description
)
def set_price(self, price):

View File

@ -280,6 +280,7 @@ class ElasticsearchClient(object):
'end': end,
'type': type_,
'unit': point.unit,
'description': point.description,
'qty': point.qty,
'price': point.price,
'groupby': point.groupby,

View File

@ -138,6 +138,7 @@ class InfluxClient(object):
measurement_fields['qty'] = float(point.qty)
measurement_fields['price'] = float(point.price)
measurement_fields['unit'] = point.unit
measurement_fields['description'] = point.description
# Unfortunately, this seems to be the fastest way: Having several
# measurements would imply a high client-side workload, and this allows
# us to filter out unrequired keys

View File

@ -159,6 +159,8 @@ tests:
url: /v2/scope
method: PUT
status: 400
request_headers:
content-type: application/json
- name: Reset state with no results for parameters
url: /v2/scope

View File

@ -300,6 +300,7 @@ class TestElasticsearchClient(unittest.TestCase):
'end': end,
'type': 'awesome_type',
'unit': point.unit,
'description': point.description,
'qty': point.qty,
'price': point.price,
'groupby': point.groupby,
@ -335,6 +336,7 @@ class TestElasticsearchClient(unittest.TestCase):
'end': end,
'type': 'awesome_type',
'unit': point.unit,
'description': point.description,
'qty': point.qty,
'price': point.price,
'groupby': point.groupby,

View File

@ -278,6 +278,40 @@ option does:
metadata:
- flavor_id
Metric description
~~~~~~~~~~~~~~~~~~
Sometimes, you will want to use a more descriptive attribute to show more
details about the configured rating type. For instance, to provide more
details about the rating of operating system licenses or other software
licenses configured in the cloud. For that, we have the option called
``description``, which is a String like field (up to 64 kB) that can be
used to provide more information for a rating of a metric. When configured,
this option is persisted as rating metadata and it is available through the
summary GET API.
.. code-block:: yaml
metrics:
instance-status:
unit: license-hours
alt_name: license-hours
description: |
Operating system licenses are charged as follows: (i)
Linux distro will not be charged; (ii) All Windows up to
version 8 are charged .01 every hour, and other versions
.5; (iii) Any other operating systems will be charged .02
groupby:
- id
- operating_system_name
- operating_system_distro
- operating_system_version
- flavor_id
- flavor_name
- cores
- ram
metadata: []
Collector-specific configuration
--------------------------------

View File

@ -0,0 +1,5 @@
---
features:
- |
Add description option to a rating metric definition, which
can be used to create custom reports in the ``summary`` GET API.