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:
parent
a93c611e71
commit
d97bba1d07
|
@ -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;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue