Avoid discarding precision of metering data

Fixes bug 1241467

Narrow-casting statistical aggregates to int before inserting
into the resource usage line charts had the effect of unnaturally
smoothening the trend. This was especially apparent for meters
defined over a narrow range (such as cpu_util ranging from 0.0%
to 100.0%).

We now maintain floating point precision for these data.

Change-Id: If6141a4f66aa0078d38aa9f53d913aca5684ad94
(cherry picked from commit 4b973c2fb4)
This commit is contained in:
Eoghan Glynn 2013-10-18 10:53:44 +01:00
parent a93c611e71
commit d97bba1d07
3 changed files with 61 additions and 64 deletions

View File

@ -241,7 +241,7 @@ horizon.d3_line_chart = {
formatter: function(series, x, y) {
var date = '<span class="date">' + new Date(x * 1000).toUTCString() + '</span>';
var swatch = '<span class="detail_swatch" style="background-color: ' + series.color + '"></span>';
var content = swatch + series.name + ": " + parseInt(y) + " " + series.unit + '<br>' + date;
var content = swatch + series.name + ": " + parseFloat(y).toFixed(2) + " " + series.unit + '<br>' + date;
return content;
}
});

View File

@ -11,6 +11,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
from django.core.urlresolvers import reverse # noqa
from django import http # noqa
@ -230,6 +231,21 @@ class MeteringViewTests(test.APITestCase, test.BaseAdminViewTests):
self.assertTemplateUsed(res, 'admin/metering/index.html')
self.assertTemplateUsed(res, 'admin/metering/stats.html')
def _verify_series(self, series, value, date, expected_names):
expected_names.reverse()
data = json.loads(series)
self.assertTrue('series' in data)
self.assertEqual(len(data['series']), len(expected_names))
for d in data['series']:
self.assertTrue('data' in d)
self.assertEqual(len(d['data']), 1)
self.assertAlmostEqual(d['data'][0].get('y'), value)
self.assertEqual(d['data'][0].get('x'), date)
self.assertEqual(d.get('name'), expected_names.pop())
self.assertEqual(d.get('unit'), '')
self.assertEquals(data.get('settings'), {})
@test.create_stubs({api.keystone: ('tenant_list',)})
def test_stats_for_line_chart(self):
statistics = self.statistics.list()
@ -256,19 +272,11 @@ class MeteringViewTests(test.APITestCase, test.BaseAdminViewTests):
self.assertEqual(res._headers['content-type'],
('Content-Type', 'application/json'))
self.assertEqual(res._container,
['{"series": [{"data": [{"y": 4, '
'"x": "2012-12-21T11:00:55"}], '
'"name": "test_tenant", "unit": ""}, '
'{"data": [{"y": 4, '
'"x": "2012-12-21T11:00:55"}], '
'"name": "disabled_tenant", '
'"unit": ""}, '
'{"data": [{"y": 4, '
'"x": "2012-12-21T11:00:55"}], '
'"name": "\\u4e91\\u89c4\\u5219", '
'"unit": ""}], '
'"settings": {}}'])
expected_names = ['test_tenant',
'disabled_tenant',
u'\u4e91\u89c4\u5219']
self._verify_series(res._container[0], 4.55, '2012-12-21T11:00:55',
expected_names)
@test.create_stubs({api.keystone: ('tenant_list',)})
def test_stats_for_line_chart_attr_max(self):
@ -296,19 +304,11 @@ class MeteringViewTests(test.APITestCase, test.BaseAdminViewTests):
self.assertEqual(res._headers['content-type'],
('Content-Type', 'application/json'))
self.assertEqual(res._container,
['{"series": [{"data": [{"y": 9, '
'"x": "2012-12-21T11:00:55"}], '
'"name": "test_tenant", "unit": ""}, '
'{"data": [{"y": 9, '
'"x": "2012-12-21T11:00:55"}], '
'"name": "disabled_tenant", '
'"unit": ""}, '
'{"data": [{"y": 9, '
'"x": "2012-12-21T11:00:55"}], '
'"name": "\\u4e91\\u89c4\\u5219", '
'"unit": ""}], '
'"settings": {}}'])
expected_names = ['test_tenant',
'disabled_tenant',
u'\u4e91\u89c4\u5219']
self._verify_series(res._container[0], 9.0, '2012-12-21T11:00:55',
expected_names)
def test_stats_for_line_chart_no_group_by(self):
resources = self.resources.list()
@ -333,13 +333,7 @@ class MeteringViewTests(test.APITestCase, test.BaseAdminViewTests):
self.assertEqual(res._headers['content-type'],
('Content-Type', 'application/json'))
self.assertEqual(res._container,
['{"series": [{"data": [{"y": 4, '
'"x": "2012-12-21T11:00:55"}], '
'"name": "fake_resource_id", '
'"unit": ""}, '
'{"data": [{"y": 4, '
'"x": "2012-12-21T11:00:55"}], '
'"name": "fake_resource_id2", '
'"unit": ""}], '
'"settings": {}}'])
expected_names = ['fake_resource_id',
'fake_resource_id2']
self._verify_series(res._container[0], 4.55, '2012-12-21T11:00:55',
expected_names)

View File

@ -39,13 +39,32 @@ class IndexView(tabs.TabbedTableView):
class SamplesView(TemplateView):
template_name = "admin/metering/samples.csv"
@staticmethod
def _series_for_meter(aggregates,
resource_name,
meter_name,
stats_name,
unit):
"""Construct datapoint series for a meter from resource aggregates."""
series = []
for resource in aggregates:
if getattr(resource, meter_name):
point = {'unit': unit,
'name': getattr(resource, resource_name),
'data': []}
for statistic in getattr(resource, meter_name):
date = statistic.duration_end[:19]
value = float(getattr(statistic, stats_name))
point['data'].append({'x': date, 'y': value})
series.append(point)
return series
def get(self, request, *args, **kwargs):
meter = request.GET.get('meter', None)
meter_name = meter.replace(".", "_")
date_options = request.GET.get('date_options', None)
date_from = request.GET.get('date_from', None)
date_to = request.GET.get('date_to', None)
resource = request.GET.get('resource', None)
stats_attr = request.GET.get('stats_attr', 'avg')
# TODO(lsmola) all timestamps should probably work with
@ -150,20 +169,11 @@ class SamplesView(TemplateView):
queries, [meter], period=period, stats_attr=None,
additional_query=additional_query)
series = []
for resource in resources:
name = resource.id
if getattr(resource, meter_name):
serie = {'unit': unit,
'name': name,
'data': []}
for statistic in getattr(resource, meter_name):
date = statistic.duration_end[:19]
value = int(getattr(statistic, stats_attr))
serie['data'].append({'x': date, 'y': value})
series.append(serie)
series = self._series_for_meter(resources,
'id',
meter_name,
stats_attr,
unit)
else:
ceilometer_usage = ceilometer.CeilometerUsage(request)
try:
@ -175,18 +185,11 @@ class SamplesView(TemplateView):
exceptions.handle(request,
_('Unable to retrieve statistics.'))
series = []
for resource in resources:
if getattr(resource, meter_name):
serie = {'unit': unit,
'name': resource.resource_id,
'data': []}
for statistic in getattr(resource, meter_name):
date = statistic.duration_end[:19]
value = int(getattr(statistic, stats_attr))
serie['data'].append({'x': date, 'y': value})
series.append(serie)
series = self._series_for_meter(resources,
'resource_id',
meter_name,
stats_attr,
unit)
ret = {}
ret['series'] = series