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
This commit is contained in:
parent
b1ceedd83e
commit
4b973c2fb4
|
@ -241,7 +241,7 @@ horizon.d3_line_chart = {
|
||||||
formatter: function(series, x, y) {
|
formatter: function(series, x, y) {
|
||||||
var date = '<span class="date">' + new Date(x * 1000).toUTCString() + '</span>';
|
var date = '<span class="date">' + new Date(x * 1000).toUTCString() + '</span>';
|
||||||
var swatch = '<span class="detail_swatch" style="background-color: ' + series.color + '"></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;
|
return content;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
import json
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse # noqa
|
from django.core.urlresolvers import reverse # noqa
|
||||||
from django import http # 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/index.html')
|
||||||
self.assertTemplateUsed(res, 'admin/metering/stats.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',)})
|
@test.create_stubs({api.keystone: ('tenant_list',)})
|
||||||
def test_stats_for_line_chart(self):
|
def test_stats_for_line_chart(self):
|
||||||
statistics = self.statistics.list()
|
statistics = self.statistics.list()
|
||||||
|
@ -256,19 +272,11 @@ class MeteringViewTests(test.APITestCase, test.BaseAdminViewTests):
|
||||||
|
|
||||||
self.assertEqual(res._headers['content-type'],
|
self.assertEqual(res._headers['content-type'],
|
||||||
('Content-Type', 'application/json'))
|
('Content-Type', 'application/json'))
|
||||||
self.assertEqual(res._container,
|
expected_names = ['test_tenant',
|
||||||
['{"series": [{"data": [{"y": 4, '
|
'disabled_tenant',
|
||||||
'"x": "2012-12-21T11:00:55"}], '
|
u'\u4e91\u89c4\u5219']
|
||||||
'"name": "test_tenant", "unit": ""}, '
|
self._verify_series(res._container[0], 4.55, '2012-12-21T11:00:55',
|
||||||
'{"data": [{"y": 4, '
|
expected_names)
|
||||||
'"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": {}}'])
|
|
||||||
|
|
||||||
@test.create_stubs({api.keystone: ('tenant_list',)})
|
@test.create_stubs({api.keystone: ('tenant_list',)})
|
||||||
def test_stats_for_line_chart_attr_max(self):
|
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'],
|
self.assertEqual(res._headers['content-type'],
|
||||||
('Content-Type', 'application/json'))
|
('Content-Type', 'application/json'))
|
||||||
self.assertEqual(res._container,
|
expected_names = ['test_tenant',
|
||||||
['{"series": [{"data": [{"y": 9, '
|
'disabled_tenant',
|
||||||
'"x": "2012-12-21T11:00:55"}], '
|
u'\u4e91\u89c4\u5219']
|
||||||
'"name": "test_tenant", "unit": ""}, '
|
self._verify_series(res._container[0], 9.0, '2012-12-21T11:00:55',
|
||||||
'{"data": [{"y": 9, '
|
expected_names)
|
||||||
'"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": {}}'])
|
|
||||||
|
|
||||||
def test_stats_for_line_chart_no_group_by(self):
|
def test_stats_for_line_chart_no_group_by(self):
|
||||||
resources = self.resources.list()
|
resources = self.resources.list()
|
||||||
|
@ -333,13 +333,7 @@ class MeteringViewTests(test.APITestCase, test.BaseAdminViewTests):
|
||||||
|
|
||||||
self.assertEqual(res._headers['content-type'],
|
self.assertEqual(res._headers['content-type'],
|
||||||
('Content-Type', 'application/json'))
|
('Content-Type', 'application/json'))
|
||||||
self.assertEqual(res._container,
|
expected_names = ['fake_resource_id',
|
||||||
['{"series": [{"data": [{"y": 4, '
|
'fake_resource_id2']
|
||||||
'"x": "2012-12-21T11:00:55"}], '
|
self._verify_series(res._container[0], 4.55, '2012-12-21T11:00:55',
|
||||||
'"name": "fake_resource_id", '
|
expected_names)
|
||||||
'"unit": ""}, '
|
|
||||||
'{"data": [{"y": 4, '
|
|
||||||
'"x": "2012-12-21T11:00:55"}], '
|
|
||||||
'"name": "fake_resource_id2", '
|
|
||||||
'"unit": ""}], '
|
|
||||||
'"settings": {}}'])
|
|
||||||
|
|
|
@ -39,13 +39,32 @@ class IndexView(tabs.TabbedTableView):
|
||||||
class SamplesView(TemplateView):
|
class SamplesView(TemplateView):
|
||||||
template_name = "admin/metering/samples.csv"
|
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):
|
def get(self, request, *args, **kwargs):
|
||||||
meter = request.GET.get('meter', None)
|
meter = request.GET.get('meter', None)
|
||||||
meter_name = meter.replace(".", "_")
|
meter_name = meter.replace(".", "_")
|
||||||
date_options = request.GET.get('date_options', None)
|
date_options = request.GET.get('date_options', None)
|
||||||
date_from = request.GET.get('date_from', None)
|
date_from = request.GET.get('date_from', None)
|
||||||
date_to = request.GET.get('date_to', None)
|
date_to = request.GET.get('date_to', None)
|
||||||
resource = request.GET.get('resource', None)
|
|
||||||
stats_attr = request.GET.get('stats_attr', 'avg')
|
stats_attr = request.GET.get('stats_attr', 'avg')
|
||||||
|
|
||||||
# TODO(lsmola) all timestamps should probably work with
|
# TODO(lsmola) all timestamps should probably work with
|
||||||
|
@ -150,20 +169,11 @@ class SamplesView(TemplateView):
|
||||||
queries, [meter], period=period, stats_attr=None,
|
queries, [meter], period=period, stats_attr=None,
|
||||||
additional_query=additional_query)
|
additional_query=additional_query)
|
||||||
|
|
||||||
series = []
|
series = self._series_for_meter(resources,
|
||||||
for resource in resources:
|
'id',
|
||||||
name = resource.id
|
meter_name,
|
||||||
if getattr(resource, meter_name):
|
stats_attr,
|
||||||
serie = {'unit': 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)
|
|
||||||
else:
|
else:
|
||||||
ceilometer_usage = ceilometer.CeilometerUsage(request)
|
ceilometer_usage = ceilometer.CeilometerUsage(request)
|
||||||
try:
|
try:
|
||||||
|
@ -175,18 +185,11 @@ class SamplesView(TemplateView):
|
||||||
exceptions.handle(request,
|
exceptions.handle(request,
|
||||||
_('Unable to retrieve statistics.'))
|
_('Unable to retrieve statistics.'))
|
||||||
|
|
||||||
series = []
|
series = self._series_for_meter(resources,
|
||||||
for resource in resources:
|
'resource_id',
|
||||||
if getattr(resource, meter_name):
|
meter_name,
|
||||||
serie = {'unit': unit,
|
stats_attr,
|
||||||
'name': resource.resource_id,
|
unit)
|
||||||
'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)
|
|
||||||
|
|
||||||
ret = {}
|
ret = {}
|
||||||
ret['series'] = series
|
ret['series'] = series
|
||||||
|
|
Loading…
Reference in New Issue