Merge "Validate value meta key and value in python api"

This commit is contained in:
Jenkins 2016-04-15 17:16:47 +00:00 committed by Gerrit Code Review
commit 5697d16f42
6 changed files with 102 additions and 8 deletions

View File

@ -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.

View File

@ -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):

View File

@ -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"

View File

@ -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:

View File

@ -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

View File

@ -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()