Allow simpler scale exprs in transformer.conversions

For convenience of deployers, allow scale expression to be
expressed as for example:

  100.0 / (10**9 * (resource_metadata.cpu_number or 1))

instead of requiring that the full dict lookup syntax with
default value is used:

  100.0 / (10**9 * (resource_metadata.get('cpu_number', 1))

Unknown items evaluate to false in boolean expressions such
as:

  resource_metadata.non.existent or 1.0

Change-Id: I2f1f84641222057b4d6213dcfd1c16f23ae9f273
This commit is contained in:
Eoghan Glynn 2013-07-15 09:16:00 +00:00
parent 65c4790303
commit ac2b21b722
2 changed files with 45 additions and 12 deletions

View File

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

View File

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