Validate valueMeta for measurement
If a plugin tries to give invalid valueMeta for a Measurement, the API will reject it. Since the agent batches measurements together, this will cause rejection of the whole batch so it is better to just reject these measurements before sending them. It also makes it easier to determine which plugin is sending the invalid measurements Change-Id: Id9d14de5e7ad6a11d0c8b284a3cd59bd9ecfafbd
This commit is contained in:
parent
09810ebff5
commit
5e0319002b
@ -1,6 +1,7 @@
|
||||
# (C) Copyright 2015 Hewlett Packard Enterprise Development Company LP
|
||||
# (C) Copyright 2015-2016 Hewlett Packard Enterprise Development Company LP
|
||||
""" Aggregation classes used by the collector and statsd to batch messages sent to the forwarder.
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
from time import time
|
||||
@ -17,6 +18,9 @@ log = logging.getLogger(__name__)
|
||||
# does not support submitting values for the past, and all values get
|
||||
# submitted for the timestamp passed into the flush() function.
|
||||
RECENT_POINT_THRESHOLD_DEFAULT = 3600
|
||||
VALUE_META_MAX_NUMBER = 16
|
||||
VALUE_META_VALUE_MAX_LENGTH = 2048
|
||||
VALUE_META_NAME_MAX_LENGTH = 255
|
||||
|
||||
invalid_chars = "<>={}(),\"\\\\;&"
|
||||
restricted_dimension_chars = re.compile('[' + invalid_chars + ']')
|
||||
@ -39,6 +43,10 @@ class InvalidValue(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidValueMeta(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class MetricsAggregator(object):
|
||||
"""A metric aggregator class."""
|
||||
|
||||
@ -148,6 +156,32 @@ class MetricsAggregator(object):
|
||||
return 0
|
||||
return round(float(self.count) / interval, 2)
|
||||
|
||||
def _valid_value_meta(self, value_meta, name, dimensions):
|
||||
if len(value_meta) > VALUE_META_MAX_NUMBER:
|
||||
msg = "Too many valueMeta entries {0}, limit is {1}: {2} -> {3} valueMeta {4}"
|
||||
log.error(msg.format(len(value_meta), VALUE_META_MAX_NUMBER, name, dimensions, value_meta))
|
||||
return False
|
||||
for key, value in value_meta.iteritems():
|
||||
if not key:
|
||||
log.error("valueMeta name cannot be empty: {0} -> {1}".format(name, dimensions))
|
||||
return False
|
||||
if len(key) > VALUE_META_NAME_MAX_LENGTH:
|
||||
msg = "valueMeta name {0} must be {1} characters or less: {2} -> {3}"
|
||||
log.error(msg.format(key, VALUE_META_NAME_MAX_LENGTH, name, dimensions))
|
||||
return False
|
||||
|
||||
try:
|
||||
value_meta_json = json.dumps(value_meta)
|
||||
if len(value_meta_json) > VALUE_META_VALUE_MAX_LENGTH:
|
||||
msg = "valueMeta name value combinations must be {0} characters or less: {1} -> {2} valueMeta {3}"
|
||||
log.error(msg.format(VALUE_META_VALUE_MAX_LENGTH, name, dimensions, value_meta))
|
||||
return False
|
||||
except Exception:
|
||||
log.error("Unable to serialize valueMeta into JSON: {2} -> {3}".format(name, dimensions))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def submit_metric(self, name, value, metric_class, dimensions=None,
|
||||
delegated_tenant=None, hostname=None, device_name=None,
|
||||
value_meta=None, timestamp=None, sample_rate=1):
|
||||
@ -191,6 +225,8 @@ class MetricsAggregator(object):
|
||||
raise InvalidValue
|
||||
|
||||
if value_meta:
|
||||
if not self._valid_value_meta(value_meta, name, dimensions):
|
||||
raise InvalidValueMeta
|
||||
meta = tuple(value_meta.items())
|
||||
else:
|
||||
meta = None
|
||||
|
@ -1,3 +1,4 @@
|
||||
# (C) Copyright 2015-2016 Hewlett Packard Enterprise Development Company LP
|
||||
import unittest
|
||||
|
||||
import monasca_agent.common.aggregator as aggregator
|
||||
@ -223,3 +224,70 @@ class TestMetricsAggregator(unittest.TestCase):
|
||||
self.submit_metric('test-counter', 2,
|
||||
dimensions={'test-key': 'test{}value'.format(c)},
|
||||
exception=aggregator.InvalidDimensionValue)
|
||||
|
||||
def testTooManyValueMeta(self):
|
||||
dimensions = {'A': 'B', 'B': 'C', 'D': 'E'}
|
||||
value_meta = {}
|
||||
for i in range(0, 17):
|
||||
value_meta['key{}'.format(i)] = 'value{}'.format(i)
|
||||
self.submit_metric("Foo",
|
||||
2,
|
||||
dimensions=dimensions,
|
||||
value_meta=value_meta,
|
||||
exception=aggregator.InvalidValueMeta)
|
||||
|
||||
def testEmptyValueMetaKey(self):
|
||||
dimensions = {'A': 'B', 'B': 'C', 'D': 'E'}
|
||||
value_meta = {'': 'BBB'}
|
||||
self.submit_metric("Foo",
|
||||
2,
|
||||
dimensions=dimensions,
|
||||
value_meta=value_meta,
|
||||
exception=aggregator.InvalidValueMeta)
|
||||
|
||||
def testEmptyValueMetaKey(self):
|
||||
dimensions = {'A': 'B', 'B': 'C', 'D': 'E'}
|
||||
value_meta = {'': 'BBB'}
|
||||
self.submit_metric("Foo",
|
||||
2,
|
||||
dimensions=dimensions,
|
||||
value_meta=value_meta,
|
||||
exception=aggregator.InvalidValueMeta)
|
||||
|
||||
def testTooLongValueMetaKey(self):
|
||||
dimensions = {'A': 'B', 'B': 'C', 'D': 'E'}
|
||||
key = "K"
|
||||
for i in range(0, aggregator.VALUE_META_NAME_MAX_LENGTH):
|
||||
key = "{}{}".format(key, "1")
|
||||
value_meta = {key: 'BBB'}
|
||||
print(key)
|
||||
self.submit_metric("Foo",
|
||||
2,
|
||||
dimensions=dimensions,
|
||||
value_meta=value_meta,
|
||||
exception=aggregator.InvalidValueMeta)
|
||||
|
||||
def testEmptyValueMetaKey(self):
|
||||
dimensions = {'A': 'B', 'B': 'C', 'D': 'E'}
|
||||
value_meta = {'': 'BBB'}
|
||||
self.submit_metric("Foo",
|
||||
2,
|
||||
dimensions=dimensions,
|
||||
value_meta=value_meta,
|
||||
exception=aggregator.InvalidValueMeta)
|
||||
|
||||
def testTooLargeValueMeta(self):
|
||||
dimensions = {'A': 'B', 'B': 'C', 'D': 'E'}
|
||||
value_meta_value = ""
|
||||
num_value_meta = 10
|
||||
for i in range(0, aggregator.VALUE_META_VALUE_MAX_LENGTH/num_value_meta):
|
||||
value_meta_value = '{}{}'.format(value_meta_value, '1')
|
||||
|
||||
value_meta = {}
|
||||
for i in range(0, num_value_meta):
|
||||
value_meta['key{}'.format(i)] = value_meta_value
|
||||
self.submit_metric("Foo",
|
||||
2,
|
||||
dimensions=dimensions,
|
||||
value_meta=value_meta,
|
||||
exception=aggregator.InvalidValueMeta)
|
||||
|
Loading…
x
Reference in New Issue
Block a user