diff --git a/ceilometer/transformer/conversions.py b/ceilometer/transformer/conversions.py index b8e98d2d9..faa0c8ed4 100644 --- a/ceilometer/transformer/conversions.py +++ b/ceilometer/transformer/conversions.py @@ -16,7 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. -import copy +from collections import defaultdict from ceilometer import counter as ceilocounter from ceilometer.openstack.common import log @@ -26,6 +26,29 @@ from ceilometer import transformer LOG = log.getLogger(__name__) +class Namespace(object): + """Encapsulates the namespace wrapping the evaluation of the + configured scale factor. This allows nested dicts to be + accessed in the attribute style, and missing attributes + to yield false when used in a boolean expression. + """ + def __init__(self, seed): + self.__dict__ = defaultdict(lambda: Namespace({})) + self.__dict__.update(seed) + for k, v in self.__dict__.iteritems(): + if isinstance(v, dict): + self.__dict__[k] = Namespace(v) + + def __getattr__(self, attr): + return self.__dict__[attr] + + def __getitem__(self, key): + return self.__dict__[key] + + def __nonzero__(self): + return len(self.__dict__) > 0 + + class ScalingTransformer(transformer.TransformerBase): """Transformer to apply a scaling conversion. """ @@ -54,7 +77,8 @@ class ScalingTransformer(transformer.TransformerBase): """Apply the scaling factor (either a straight multiplicative factor or else a string to be eval'd). """ - ns = copy.deepcopy(counter._asdict()) + ns = Namespace(counter._asdict()) + return ((eval(scale, {}, ns) if isinstance(scale, basestring) else counter.volume * scale) if scale else counter.volume) diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py index 951dd6732..950c3c12b 100644 --- a/tests/test_pipeline.py +++ b/tests/test_pipeline.py @@ -747,9 +747,11 @@ class TestPipeline(base.TestCase): self.assertEquals(getattr(amb_temp, 'volume'), 88.8) self.assertEquals(getattr(core_temp, 'volume'), 96.8) - def _do_test_rate_of_change_conversion(self, prev, curr, offset, - type, expected): - s = "100.0 / (10**9 * resource_metadata.get('cpu_number', 1))" + def _do_test_rate_of_change_conversion(self, prev, curr, type, expected, + offset=1, weight=None): + s = "(resource_metadata.user_metadata.autoscaling_weight or 1.0)" \ + "* (resource_metadata.non.existent or 1.0)" \ + "* (100.0 / (10**9 * (resource_metadata.cpu_number or 1)))" self.pipeline_cfg[0]['transformers'] = [ { 'name': 'rate_of_change', @@ -766,6 +768,7 @@ class TestPipeline(base.TestCase): self.pipeline_cfg[0]['counters'] = ['cpu'] now = timeutils.utcnow() later = now + datetime.timedelta(minutes=offset) + um = {'autoscaling_weight': weight} if weight else {} counters = [ counter.Counter( name='cpu', @@ -776,7 +779,8 @@ class TestPipeline(base.TestCase): project_id='test_proj', resource_id='test_resource', timestamp=now.isoformat(), - resource_metadata={'cpu_number': 4} + resource_metadata={'cpu_number': 4, + 'user_metadata': um}, ), counter.Counter( name='cpu', @@ -787,7 +791,8 @@ class TestPipeline(base.TestCase): project_id='test_proj', resource_id='test_resource', timestamp=later.isoformat(), - resource_metadata={'cpu_number': 4} + resource_metadata={'cpu_number': 4, + 'user_metadata': um}, ), ] @@ -812,30 +817,34 @@ class TestPipeline(base.TestCase): def test_rate_of_change_conversion(self): self._do_test_rate_of_change_conversion(120000000000, 180000000000, - 1, counter.TYPE_CUMULATIVE, 25.0) + def test_rate_of_change_conversion_weight(self): + self._do_test_rate_of_change_conversion(120000000000, + 180000000000, + counter.TYPE_CUMULATIVE, + 27.5, + weight=1.1) + def test_rate_of_change_conversion_negative_cumulative_delta(self): self._do_test_rate_of_change_conversion(180000000000, 120000000000, - 1, counter.TYPE_CUMULATIVE, 50.0) def test_rate_of_change_conversion_negative_gauge_delta(self): self._do_test_rate_of_change_conversion(180000000000, 120000000000, - 1, counter.TYPE_GAUGE, -25.0) def test_rate_of_change_conversion_zero_delay(self): self._do_test_rate_of_change_conversion(120000000000, 120000000000, - 0, counter.TYPE_CUMULATIVE, - 0.0) + 0.0, + offset=0) def _do_test_rate_of_change_no_predecessor(self, replace): s = "100.0 / (10**9 * resource_metadata.get('cpu_number', 1))"