Merge "Validate value meta key and value in python api"
This commit is contained in:
commit
5697d16f42
|
@ -361,7 +361,7 @@ Optionally, a measurement may also contain extra data about the value which is k
|
|||
For an example of how value meta is used, imagine this metric: http_status{url: http://localhost:8080/healthcheck, hostname=devstack, service=object-storage}. The measurements for this metric have a value of either 1 or 0 depending if the status check succeeded. If the check fails, it would be helpful to have the actual http status code and error message if possible. So instead of just a value, the measurement will be something like:
|
||||
{Timestamp=now(), value=1, value_meta{http_rc=500, error=“Error accessing MySQL”}}
|
||||
|
||||
Up to 16 separate key/value pairs of value meta are allowed per measurement. The keys are required and are trimmed of leading and trailing whitespace and have a maximum length of 255 characters. The value is a string and has a maximum length of 2048 characters. The value can be an empty string. Whitespace is not trimmed from the values.
|
||||
Up to 16 separate key/value pairs of value meta are allowed per measurement. The keys are required and are trimmed of leading and trailing whitespace and have a maximum length of 255 characters. The value is a string and value meta (with key, value and '{"":""}' combined) has a maximum length of 2048 characters. The value can be an empty string. Whitespace is not trimmed from the values.
|
||||
|
||||
## Alarm Definitions and Alarms
|
||||
|
||||
|
@ -881,7 +881,7 @@ Consists of a single metric object or an array of metric objects. A metric has t
|
|||
* dimensions ({string(255): string(255)}, optional) - A dictionary consisting of (key, value) pairs used to uniquely identify a metric.
|
||||
* timestamp (string, required) - The timestamp in milliseconds from the Epoch.
|
||||
* value (float, required) - Value of the metric. Values with base-10 exponents greater than 126 or less than -130 are truncated.
|
||||
* value_meta ({string(255): string(2048)}, optional) - A dictionary consisting of (key, value) pairs used to add information about the value.
|
||||
* value_meta ({string(255): string}(2048), optional) - A dictionary consisting of (key, value) pairs used to add information about the value. Value_meta key value combinations must be 2048 characters or less including '{"":""}' 7 characters total from every json string.
|
||||
|
||||
The name and dimensions are used to uniquely identify a metric.
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# Copyright 2015 Hewlett-Packard
|
||||
# (C) Copyright 2015-2016 Hewlett Packard Enterprise Development Company LP
|
||||
# Copyright 2015 Cray Inc. All Rights Reserved.
|
||||
# Copyright 2016 Hewlett Packard Enterprise Development Company, L.P.
|
||||
#
|
||||
# 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
|
||||
|
@ -99,6 +98,44 @@ class TestDimensionValidation(unittest.TestCase):
|
|||
self.assertRaises(AssertionError, validation.dimension_value, dim_value)
|
||||
|
||||
|
||||
class TestValueMetaValidation(unittest.TestCase):
|
||||
def test_valid_name(self):
|
||||
value_meta_name = "this.is_a.valid-name"
|
||||
value_meta = {value_meta_name: 'value_meta_value'}
|
||||
validation.validate_value_meta(value_meta)
|
||||
self.assertTrue(True)
|
||||
|
||||
def test_nonstring_name(self):
|
||||
value_meta_name = 123456
|
||||
value_meta = {value_meta_name: 'value_meta_value'}
|
||||
self.assertRaises(AssertionError, validation.validate_value_meta,
|
||||
value_meta)
|
||||
|
||||
def test_long_name(self):
|
||||
value_meta_name = "x" * 256
|
||||
value_meta = {value_meta_name: 'value_meta_value'}
|
||||
self.assertRaises(AssertionError, validation.validate_value_meta,
|
||||
value_meta)
|
||||
|
||||
def test_valid_value(self):
|
||||
value_meta_value = "this.is_a.valid-value"
|
||||
value_meta = {'value_meta_name': value_meta_value}
|
||||
validation.validate_value_meta(value_meta)
|
||||
self.assertTrue(True)
|
||||
|
||||
def test_nonstring_value(self):
|
||||
value_meta_value = 123456
|
||||
value_meta = {'value_meta_name': value_meta_value}
|
||||
self.assertRaises(AssertionError, validation.validate_value_meta,
|
||||
value_meta)
|
||||
|
||||
def test_long_value_meta(self):
|
||||
value_meta_value = "x" * 2048
|
||||
value_meta = {'value_meta_name': value_meta_value}
|
||||
self.assertRaises(AssertionError, validation.validate_value_meta,
|
||||
value_meta)
|
||||
|
||||
|
||||
class TestRoleValidation(unittest.TestCase):
|
||||
|
||||
def test_role_valid(self):
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
from monasca_api.v2.common.exceptions import HTTPUnprocessableEntityError
|
||||
|
||||
import json
|
||||
import re
|
||||
|
||||
invalid_chars = "<>={}(),\"\\\\|;&"
|
||||
|
@ -23,6 +24,12 @@ VALID_ALARM_STATES = ["ALARM", "OK", "UNDETERMINED"]
|
|||
|
||||
VALID_ALARM_DEFINITION_SEVERITIES = ["LOW", "MEDIUM", "HIGH", "CRITICAL"]
|
||||
|
||||
VALUE_META_MAX_NUMBER = 16
|
||||
|
||||
VALUE_META_MAX_LENGTH = 2048
|
||||
|
||||
VALUE_META_NAME_MAX_LENGTH = 255
|
||||
|
||||
|
||||
def metric_name(name):
|
||||
assert isinstance(name, (str, unicode)), "Metric name must be a string"
|
||||
|
@ -75,3 +82,23 @@ def validate_sort_by(sort_by_list, allowed_sort_by):
|
|||
raise HTTPUnprocessableEntityError("Unprocessable Entity",
|
||||
"sort_by value {} must be 'asc' or 'desc'".format(
|
||||
sort_by_values[1]))
|
||||
|
||||
|
||||
def validate_value_meta(value_meta):
|
||||
value_meta_string = json.dumps(value_meta)
|
||||
# entries
|
||||
assert len(value_meta) <= VALUE_META_MAX_NUMBER, "ValueMeta entries must be {} or less".format(
|
||||
VALUE_META_MAX_NUMBER)
|
||||
# total length
|
||||
assert len(value_meta_string) <= VALUE_META_MAX_LENGTH, \
|
||||
"ValueMeta name value combinations must be {} characters or less".format(
|
||||
VALUE_META_MAX_LENGTH)
|
||||
for name in value_meta:
|
||||
# name
|
||||
assert isinstance(name, (str, unicode)), "ValueMeta name must be a string"
|
||||
assert len(name) <= VALUE_META_NAME_MAX_LENGTH, "ValueMeta name must be {} characters or less".format(
|
||||
VALUE_META_NAME_MAX_LENGTH)
|
||||
assert len(name) >= 1, "ValueMeta name cannot be empty"
|
||||
# value
|
||||
assert isinstance(value_meta[name], (str, unicode)), "ValueMeta value must be a string"
|
||||
assert len(value_meta[name]) >= 1, "ValueMeta value cannot be empty"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2014 Hewlett-Packard
|
||||
# (C) Copyright 2014, 2016 Hewlett Packard Enterprise Development Company LP
|
||||
#
|
||||
# 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
|
||||
|
@ -78,7 +78,7 @@ class Metrics(metrics_api_v2.MetricsV2API):
|
|||
else:
|
||||
self._validate_single_metric(metrics)
|
||||
except Exception as ex:
|
||||
LOG.debug(ex)
|
||||
LOG.exception(ex)
|
||||
raise HTTPUnprocessableEntityError('Unprocessable Entity', ex.message)
|
||||
|
||||
def _validate_single_metric(self, metric):
|
||||
|
@ -89,9 +89,10 @@ class Metrics(metrics_api_v2.MetricsV2API):
|
|||
for dimension_key in metric['dimensions']:
|
||||
validation.dimension_key(dimension_key)
|
||||
validation.dimension_value(metric['dimensions'][dimension_key])
|
||||
if "value_meta" in metric:
|
||||
validation.validate_value_meta(metric['value_meta'])
|
||||
|
||||
def _send_metrics(self, metrics):
|
||||
|
||||
try:
|
||||
self._message_queue.send_message_batch(metrics)
|
||||
except message_queue_exceptions.MessageQueueException as ex:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# (C) Copyright 2015 Hewlett Packard Enterprise Development Company LP
|
||||
# (C) Copyright 2015-2016 Hewlett Packard Enterprise Development Company LP
|
||||
#
|
||||
# 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
|
||||
|
@ -39,3 +39,6 @@ MAX_ALARM_METRIC_NAME_LENGTH = 255
|
|||
MAX_ALARM_METRIC_DIMENSIONS_KEY_LENGTH = 255
|
||||
MAX_ALARM_METRIC_DIMENSIONS_VALUE_LENGTH = 255
|
||||
MAX_ALARM_LINK_LENGTH = 512
|
||||
|
||||
MAX_VALUE_META_NAME_LENGTH = 255
|
||||
MAX_VALUE_META_TOTAL_LENGTH = 2048
|
||||
|
|
|
@ -282,6 +282,32 @@ class TestMetrics(base.BaseMonascaTest):
|
|||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
|
||||
@test.attr(type='gate')
|
||||
@test.attr(type=['negative'])
|
||||
def test_create_metric_with_value_meta_name_exceeds_max_length(self):
|
||||
long_value_meta_name = "x" * (constants.MAX_VALUE_META_NAME_LENGTH + 1)
|
||||
metric = helpers.create_metric(name='name',
|
||||
value_meta=
|
||||
{long_value_meta_name:
|
||||
"value_meta_value"}
|
||||
)
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
|
||||
@test.attr(type='gate')
|
||||
@test.attr(type=['negative'])
|
||||
def test_create_metric_with_value_meta_exceeds_max_length(self):
|
||||
value_meta_name = "x"
|
||||
long_value_meta_value = "y" * constants.MAX_VALUE_META_TOTAL_LENGTH
|
||||
metric = helpers.create_metric(name='name',
|
||||
value_meta=
|
||||
{value_meta_name: long_value_meta_value}
|
||||
)
|
||||
self.assertRaises(exceptions.UnprocessableEntity,
|
||||
self.monasca_client.create_metrics,
|
||||
metric)
|
||||
|
||||
@test.attr(type='gate')
|
||||
def test_list_metrics(self):
|
||||
resp, response_body = self.monasca_client.list_metrics()
|
||||
|
|
Loading…
Reference in New Issue