diff --git a/java/src/main/java/monasca/api/infrastructure/persistence/influxdb/InfluxV9MeasurementRepo.java b/java/src/main/java/monasca/api/infrastructure/persistence/influxdb/InfluxV9MeasurementRepo.java index 60ab2a187..8b6a6d59c 100644 --- a/java/src/main/java/monasca/api/infrastructure/persistence/influxdb/InfluxV9MeasurementRepo.java +++ b/java/src/main/java/monasca/api/infrastructure/persistence/influxdb/InfluxV9MeasurementRepo.java @@ -91,38 +91,32 @@ public class InfluxV9MeasurementRepo implements MeasurementRepo { Boolean mergeMetricsFlag, String groupBy) throws Exception { String q; - if (Boolean.TRUE.equals(mergeMetricsFlag)) { + String groupByStr = ""; + if ("*".equals(groupBy)) { - // The time column is automatically included in the results before all other columns. - q = String.format("select value, value_meta %1$s " - + "where %2$s %3$s %4$s %5$s %6$s", - this.influxV9Utils.namePart(name, true), - this.influxV9Utils.privateTenantIdPart(tenantId), - this.influxV9Utils.privateRegionPart(this.region), - this.influxV9Utils.startTimePart(startTime), - this.influxV9Utils.dimPart(dimensions), - this.influxV9Utils.endTimePart(endTime)); + groupByStr = " group by * "; } else { + if (Boolean.FALSE.equals(mergeMetricsFlag)) { + if (!this.influxV9MetricDefinitionRepo.isAtMostOneSeries(tenantId, name, dimensions)) { + throw new MultipleMetricsException(name, dimensions); + } - if (!"*".equals(groupBy) && - !this.influxV9MetricDefinitionRepo.isAtMostOneSeries(tenantId, name, dimensions)) { - - throw new MultipleMetricsException(name, dimensions); - + groupByStr = this.influxV9Utils.groupByPart(); } - // The time column is automatically included in the results before all other columns. - q = String.format("select value, value_meta %1$s " - + "where %2$s %3$s %4$s %5$s %6$s %7$s", //slimit 1 - this.influxV9Utils.namePart(name, true), - this.influxV9Utils.privateTenantIdPart(tenantId), - this.influxV9Utils.privateRegionPart(this.region), - this.influxV9Utils.startTimePart(startTime), - this.influxV9Utils.dimPart(dimensions), - this.influxV9Utils.endTimePart(endTime), - this.influxV9Utils.groupByPart()); } + // The time column is automatically included in the results before all other columns. + q = String.format("select value, value_meta %1$s " + + "where %2$s %3$s %4$s %5$s %6$s %7$s", + this.influxV9Utils.namePart(name, true), + this.influxV9Utils.privateTenantIdPart(tenantId), + this.influxV9Utils.privateRegionPart(this.region), + this.influxV9Utils.startTimePart(startTime), + this.influxV9Utils.dimPart(dimensions), + this.influxV9Utils.endTimePart(endTime), + groupByStr); + logger.debug("Measurements query: {}", q); return q; diff --git a/java/src/main/java/monasca/api/infrastructure/persistence/influxdb/InfluxV9StatisticRepo.java b/java/src/main/java/monasca/api/infrastructure/persistence/influxdb/InfluxV9StatisticRepo.java index 224d3a008..8ef04debf 100644 --- a/java/src/main/java/monasca/api/infrastructure/persistence/influxdb/InfluxV9StatisticRepo.java +++ b/java/src/main/java/monasca/api/infrastructure/persistence/influxdb/InfluxV9StatisticRepo.java @@ -116,24 +116,22 @@ public class InfluxV9StatisticRepo implements StatisticRepo { String q; - if (Boolean.TRUE.equals(mergeMetricsFlag)) { + if ("*".equals(groupBy) ) { q = String.format("select %1$s %2$s " - + "where %3$s %4$s %5$s %6$s %7$s %8$s %9$s %10$s", - funcPart(statistics), - this.influxV9Utils.namePart(name, true), - this.influxV9Utils.privateTenantIdPart(tenantId), - this.influxV9Utils.privateRegionPart(this.region), - this.influxV9Utils.startTimePart(startTime), - this.influxV9Utils.dimPart(dimensions), - this.influxV9Utils.endTimePart(endTime), - this.influxV9Utils.timeOffsetPart(offsetTimePart), - this.influxV9Utils.periodPart(period), - this.influxV9Utils.limitPart(limit)); + + "where %3$s %4$s %5$s %6$s %7$s %8$s", + funcPart(statistics), + this.influxV9Utils.namePart(name, true), + this.influxV9Utils.privateTenantIdPart(tenantId), + this.influxV9Utils.privateRegionPart(this.region), + this.influxV9Utils.startTimePart(startTime), + this.influxV9Utils.dimPart(dimensions), + this.influxV9Utils.endTimePart(endTime), + this.influxV9Utils.periodPartWithGroupBy(period)); } else { - if (!"*".equals(groupBy) && + if (Boolean.FALSE.equals(mergeMetricsFlag) && !this.influxV9MetricDefinitionRepo.isAtMostOneSeries(tenantId, name, dimensions)) { throw new MultipleMetricsException(name, dimensions); @@ -141,15 +139,18 @@ public class InfluxV9StatisticRepo implements StatisticRepo { } q = String.format("select %1$s %2$s " - + "where %3$s %4$s %5$s %6$s %7$s %8$s", - funcPart(statistics), - this.influxV9Utils.namePart(name, true), - this.influxV9Utils.privateTenantIdPart(tenantId), - this.influxV9Utils.privateRegionPart(this.region), - this.influxV9Utils.startTimePart(startTime), - this.influxV9Utils.dimPart(dimensions), - this.influxV9Utils.endTimePart(endTime), - this.influxV9Utils.periodPartWithGroupBy(period)); + + "where %3$s %4$s %5$s %6$s %7$s %8$s %9$s %10$s", + funcPart(statistics), + this.influxV9Utils.namePart(name, true), + this.influxV9Utils.privateTenantIdPart(tenantId), + this.influxV9Utils.privateRegionPart(this.region), + this.influxV9Utils.startTimePart(startTime), + this.influxV9Utils.dimPart(dimensions), + this.influxV9Utils.endTimePart(endTime), + this.influxV9Utils.timeOffsetPart(offsetTimePart), + this.influxV9Utils.periodPart(period, mergeMetricsFlag), + this.influxV9Utils.limitPart(limit)); + } logger.debug("Statistics query: {}", q); diff --git a/java/src/main/java/monasca/api/infrastructure/persistence/influxdb/InfluxV9Utils.java b/java/src/main/java/monasca/api/infrastructure/persistence/influxdb/InfluxV9Utils.java index 1d4f128dd..3d56c8d90 100644 --- a/java/src/main/java/monasca/api/infrastructure/persistence/influxdb/InfluxV9Utils.java +++ b/java/src/main/java/monasca/api/infrastructure/persistence/influxdb/InfluxV9Utils.java @@ -259,10 +259,12 @@ public class InfluxV9Utils { : " group by time(300s), *"; } - public String periodPart(int period) { - - return period > 0 ? String.format(" group by time(%1$ds)", period) + public String periodPart(int period, Boolean mergeMetricsFlag) { + String periodStr = period > 0 ? String.format(" group by time(%1$ds)", period) : " group by time(300s)"; + periodStr += mergeMetricsFlag ? "" : ", *"; + + return periodStr; } Map filterPrivateTags(Map tagMap) { diff --git a/monasca_api/common/repositories/influxdb/metrics_repository.py b/monasca_api/common/repositories/influxdb/metrics_repository.py index b0112f4e1..b57b146ec 100644 --- a/monasca_api/common/repositories/influxdb/metrics_repository.py +++ b/monasca_api/common/repositories/influxdb/metrics_repository.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 Hewlett-Packard -# (C) Copyright 2015,2016 Hewlett Packard Enterprise Development LP +# (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP # Copyright 2015 Cray Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -61,21 +60,27 @@ class MetricsRepository(metrics_repository.AbstractMetricsRepository): def _build_select_measurement_query(self, dimensions, name, tenant_id, region, start_timestamp, end_timestamp, - offset, limit): + offset, group_by, limit): from_clause = self._build_from_clause(dimensions, name, tenant_id, region, start_timestamp, end_timestamp) - offset_clause = self._build_offset_clause(offset, limit) + offset_clause = self._build_offset_clause(offset) - query = 'select value, value_meta ' + from_clause + offset_clause + group_by_clause = self._build_group_by_clause(group_by) + + limit_clause = self._build_limit_clause(limit) + + query = 'select value, value_meta '\ + + from_clause + offset_clause\ + + group_by_clause + limit_clause return query def _build_statistics_query(self, dimensions, name, tenant_id, region, start_timestamp, end_timestamp, - statistics, period, offset, limit): + statistics, period, offset, group_by, limit): from_clause = self._build_from_clause(dimensions, name, tenant_id, region, start_timestamp, @@ -104,9 +109,9 @@ class MetricsRepository(metrics_repository.AbstractMetricsRepository): query = 'select ' + statistic_string + ' ' + from_clause - query += " group by time(" + period + "s)" + query += self._build_group_by_clause(group_by, period) - limit_clause = " limit {}".format(str(limit + 1)) + limit_clause = self._build_limit_clause(limit) query += limit_clause @@ -316,7 +321,7 @@ class MetricsRepository(metrics_repository.AbstractMetricsRepository): def measurement_list(self, tenant_id, region, name, dimensions, start_timestamp, end_timestamp, offset, - limit, merge_metrics_flag): + limit, merge_metrics_flag, group_by): json_measurement_list = [] @@ -326,9 +331,10 @@ class MetricsRepository(metrics_repository.AbstractMetricsRepository): region, start_timestamp, end_timestamp, - offset, limit) + offset, group_by, + limit) - if not merge_metrics_flag: + if not group_by and not merge_metrics_flag: dimensions = self._get_dimensions(tenant_id, region, name, dimensions) query += " slimit 1" @@ -352,11 +358,16 @@ class MetricsRepository(metrics_repository.AbstractMetricsRepository): measurement = {u'name': serie['name'], u'id': measurements_list[-1][0], - u'dimensions': dimensions, u'columns': [u'timestamp', u'value', u'value_meta'], u'measurements': measurements_list} + if not group_by: + measurement[u'dimensions'] = dimensions + else: + measurement[u'dimensions'] = {key: value for key, value in serie['tags'].iteritems() + if not key.startswith('_')} + json_measurement_list.append(measurement) return json_measurement_list @@ -407,20 +418,19 @@ class MetricsRepository(metrics_repository.AbstractMetricsRepository): raise exceptions.RepositoryException(ex) def metrics_statistics(self, tenant_id, region, name, dimensions, - start_timestamp, - end_timestamp, statistics, period, offset, limit, - merge_metrics_flag): + start_timestamp, end_timestamp, statistics, + period, offset, limit, merge_metrics_flag, + group_by): json_statistics_list = [] try: query = self._build_statistics_query(dimensions, name, tenant_id, - region, - start_timestamp, + region, start_timestamp, end_timestamp, statistics, - period, offset, limit) + period, offset, group_by, limit) - if not merge_metrics_flag: + if not group_by and not merge_metrics_flag: dimensions = self._get_dimensions(tenant_id, region, name, dimensions) query += " slimit 1" @@ -446,10 +456,15 @@ class MetricsRepository(metrics_repository.AbstractMetricsRepository): statistic = {u'name': serie['name'], u'id': stats_list[-1][0], - u'dimensions': dimensions, u'columns': columns, u'statistics': stats_list} + if not group_by: + statistic[u'dimensions'] = dimensions + else: + statistic[u'dimensions'] = {key: value for key, value in serie['tags'].iteritems() + if not key.startswith('_')} + json_statistics_list.append(statistic) return json_statistics_list @@ -485,18 +500,31 @@ class MetricsRepository(metrics_repository.AbstractMetricsRepository): raise exceptions.RepositoryException(ex) - def _build_offset_clause(self, offset, limit): + def _build_offset_clause(self, offset): if offset: - - offset_clause = ( - " and time > '{}' limit {}".format(offset, str(limit + 1))) + offset_clause = " and time > '{}'".format(offset) else: - - offset_clause = " limit {}".format(str(limit + 1)) + offset_clause = "" return offset_clause + def _build_group_by_clause(self, group_by, period=None): + if group_by or period: + items = [] + if period: + items.append("time(" + str(period) + "s)") + if group_by: + items.append('*') + clause = " group by " + ','.join(items) + else: + clause = "" + + return clause + + def _build_limit_clause(self, limit): + return " limit {} ".format(str(limit + 1)) + def _has_measurements(self, tenant_id, region, name, dimensions, start_timestamp, end_timestamp): @@ -521,7 +549,8 @@ class MetricsRepository(metrics_repository.AbstractMetricsRepository): end_timestamp, 0, 1, - False) + False, + None) if len(measurements) == 0: has_measurements = False @@ -571,9 +600,11 @@ class MetricsRepository(metrics_repository.AbstractMetricsRepository): time_clause += " and time <= " + str(int(end_timestamp * 1000000)) + "u " - offset_clause = self._build_offset_clause(offset, limit) + offset_clause = self._build_offset_clause(offset) - query += where_clause + time_clause + offset_clause + limit_clause = self._build_limit_clause(limit) + + query += where_clause + time_clause + offset_clause + limit_clause result = self.influxdb_client.query(query) diff --git a/monasca_api/common/repositories/metrics_repository.py b/monasca_api/common/repositories/metrics_repository.py index 7a76d557e..e40e3b1b0 100644 --- a/monasca_api/common/repositories/metrics_repository.py +++ b/monasca_api/common/repositories/metrics_repository.py @@ -1,4 +1,4 @@ -# Copyright 2014 Hewlett-Packard +# (C) Copyright 2014,2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -41,13 +41,15 @@ class AbstractMetricsRepository(object): @abc.abstractmethod def measurement_list(self, tenant_id, region, name, dimensions, start_timestamp, end_timestamp, offset, limit, - merge_metrics_flag): + merge_metrics_flag, + group_by): pass @abc.abstractmethod def metrics_statistics(self, tenant_id, region, name, dimensions, start_timestamp, end_timestamp, statistics, - period, offset, limit, merge_metrics_flag): + period, offset, limit, merge_metrics_flag, + group_by): pass @abc.abstractmethod diff --git a/monasca_api/tests/test_repositories.py b/monasca_api/tests/test_repositories.py index 1c46b8129..84b5555f7 100644 --- a/monasca_api/tests/test_repositories.py +++ b/monasca_api/tests/test_repositories.py @@ -63,7 +63,8 @@ class TestRepoMetricsInfluxDB(unittest.TestCase): end_timestamp=2, offset=None, limit=1, - merge_metrics_flag=True) + merge_metrics_flag=True, + group_by=None) self.assertEqual(len(result), 1) self.assertIsNone(result[0]['dimensions']) diff --git a/monasca_api/v2/reference/helpers.py b/monasca_api/v2/reference/helpers.py index 222fc56cf..96dc8d9b0 100644 --- a/monasca_api/v2/reference/helpers.py +++ b/monasca_api/v2/reference/helpers.py @@ -1,5 +1,5 @@ # Copyright 2015 Cray Inc. All Rights Reserved. -# Copyright 2014,2016 Hewlett Packard Enterprise Development LP +# (C) Copyright 2014,2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -463,7 +463,62 @@ def paginate_alarming(resource, uri, limit): return resource -def paginate_measurement(measurement, uri, limit): +def paginate_dimension_values(dimvals, uri, offset, limit): + + parsed_uri = urlparse.urlparse(uri) + self_link = build_base_uri(parsed_uri) + old_query_params = _get_old_query_params(parsed_uri) + + if old_query_params: + self_link += '?' + '&'.join(old_query_params) + + if (dimvals and dimvals[u'values']): + have_more, truncated_values = _truncate_dimension_values(dimvals[u'values'], + limit, + offset) + + links = [{u'rel': u'self', u'href': self_link.decode('utf8')}] + if have_more: + new_offset = truncated_values[limit - 1] + next_link = build_base_uri(parsed_uri) + new_query_params = [u'offset' + '=' + urlparse.quote( + new_offset.encode('utf8'), safe='')] + + _get_old_query_params_except_offset(new_query_params, parsed_uri) + + if new_query_params: + next_link += '?' + '&'.join(new_query_params) + + links.append({u'rel': u'next', u'href': next_link.decode('utf8')}) + + truncated_dimvals = {u'id': dimvals[u'id'], + u'dimension_name': dimvals[u'dimension_name'], + u'values': truncated_values} + # + # Only return metric name if one was provided + # + if u'metric_name' in dimvals: + truncated_dimvals[u'metric_name'] = dimvals[u'metric_name'] + + resource = {u'links': links, + u'elements': [truncated_dimvals]} + else: + resource = {u'links': ([{u'rel': u'self', + u'href': self_link.decode('utf8')}]), + u'elements': [dimvals]} + + return resource + + +def _truncate_dimension_values(values, limit, offset): + if offset and offset in values: + next_value_pos = values.index(offset) + 1 + values = values[next_value_pos:] + have_more = len(values) > limit + return have_more, values[:limit] + + +def paginate_measurements(measurements, uri, limit): parsed_uri = urlparse.urlparse(uri) self_link = build_base_uri(parsed_uri) @@ -473,41 +528,48 @@ def paginate_measurement(measurement, uri, limit): if old_query_params: self_link += '?' + '&'.join(old_query_params) - if (measurement - and measurement[0] - and measurement[0]['measurements'] - and len(measurement[0]['measurements']) > limit): + if measurements: + measurement_elements = [] + resource = {u'links': [{u'rel': u'self', + u'href': self_link.decode('utf8')}, + ]} + for measurement in measurements: + if len(measurement['measurements']) >= limit: - new_offset = measurement[0]['measurements'][limit - 1][0] + new_offset = measurement['measurements'][limit - 1][0] - next_link = build_base_uri(parsed_uri) + next_link = build_base_uri(parsed_uri) - new_query_params = [u'offset' + '=' + urlparse.quote( - new_offset.encode('utf8'), safe='')] + new_query_params = [u'offset' + '=' + urlparse.quote( + new_offset.encode('utf8'), safe='')] - _get_old_query_params_except_offset(new_query_params, parsed_uri) + _get_old_query_params_except_offset(new_query_params, parsed_uri) - if new_query_params: - next_link += '?' + '&'.join(new_query_params) + if new_query_params: + next_link += '?' + '&'.join(new_query_params) - truncated_measurement = [{u'dimensions': measurement[0]['dimensions'], - u'measurements': (measurement[0] - ['measurements'][:limit]), - u'name': measurement[0]['name'], - u'columns': measurement[0]['columns'], - u'id': new_offset}] + resource[u'links'].append({u'rel': u'next', + u'href': next_link.decode('utf8')}) - resource = {u'links': ([{u'rel': u'self', - u'href': self_link.decode('utf8')}, - {u'rel': u'next', - u'href': next_link.decode('utf8')}]), - u'elements': truncated_measurement} + truncated_measurement = {u'dimensions': measurement['dimensions'], + u'measurements': (measurement + ['measurements'][:limit]), + u'name': measurement['name'], + u'columns': measurement['columns'], + u'id': new_offset} + measurement_elements.append(truncated_measurement) + break + else: + limit -= len(measurement['measurements']) + measurement_elements.append(measurement) + + resource[u'elements'] = measurement_elements else: resource = {u'links': ([{u'rel': u'self', u'href': self_link.decode('utf8')}]), - u'elements': measurement} + u'elements': []} return resource @@ -541,7 +603,7 @@ def _get_old_query_params_except_offset(new_query_params, parsed_uri): 'utf8'), safe='')) -def paginate_statistics(statistic, uri, limit): +def paginate_statistics(statistics, uri, limit): parsed_uri = urlparse.urlparse(uri) self_link = build_base_uri(parsed_uri) @@ -551,41 +613,49 @@ def paginate_statistics(statistic, uri, limit): if old_query_params: self_link += '?' + '&'.join(old_query_params) - if (statistic - and statistic[0] - and statistic[0]['statistics'] - and len(statistic[0]['statistics']) > limit): + if statistics: + statistic_elements = [] + resource = {u'links': [{u'rel': u'self', + u'href': self_link.decode('utf8')}]} - new_offset = ( - statistic[0]['statistics'][limit - 1][0]) + for statistic in statistics: + if len(statistic['statistics']) >= limit: - next_link = build_base_uri(parsed_uri) + new_offset = ( + statistic['statistics'][limit - 1][0]) - new_query_params = [u'offset' + '=' + urlparse.quote( - new_offset.encode('utf8'), safe='')] + next_link = build_base_uri(parsed_uri) - _get_old_query_params_except_offset(new_query_params, parsed_uri) + new_query_params = [u'offset' + '=' + urlparse.quote( + new_offset.encode('utf8'), safe='')] - if new_query_params: - next_link += '?' + '&'.join(new_query_params) + _get_old_query_params_except_offset(new_query_params, parsed_uri) - truncated_statistic = [{u'dimensions': statistic[0]['dimensions'], - u'statistics': (statistic[0]['statistics'][:limit]), - u'name': statistic[0]['name'], - u'columns': statistic[0]['columns'], - u'id': new_offset}] + if new_query_params: + next_link += '?' + '&'.join(new_query_params) - resource = {u'links': ([{u'rel': u'self', - u'href': self_link.decode('utf8')}, - {u'rel': u'next', - u'href': next_link.decode('utf8')}]), - u'elements': truncated_statistic} + resource[u'links'].append({u'rel': u'next', + u'href': next_link.decode('utf8')}) + + truncated_statistic = {u'dimensions': statistic['dimensions'], + u'statistics': (statistic['statistics'][:limit]), + u'name': statistic['name'], + u'columns': statistic['columns'], + u'id': new_offset} + + statistic_elements.append(truncated_statistic) + break + else: + limit -= len(statistic['statistics']) + statistic_elements.append(statistic) + + resource[u'elements'] = statistic_elements else: resource = {u'links': ([{u'rel': u'self', u'href': self_link.decode('utf8')}]), - u'elements': statistic} + u'elements': []} return resource diff --git a/monasca_api/v2/reference/metrics.py b/monasca_api/v2/reference/metrics.py index c9cbeadb0..125a47828 100644 --- a/monasca_api/v2/reference/metrics.py +++ b/monasca_api/v2/reference/metrics.py @@ -182,11 +182,13 @@ class MetricsMeasurements(metrics_api_v2.MetricsMeasurementsV2API): offset = helpers.get_query_param(req, 'offset') limit = helpers.get_limit(req) merge_metrics_flag = get_merge_metrics_flag(req) + group_by = helpers.get_query_param(req, "group_by") result = self._measurement_list(tenant_id, name, dimensions, start_timestamp, end_timestamp, req.uri, offset, - limit, merge_metrics_flag) + limit, merge_metrics_flag, + group_by) res.body = helpers.dumpit_utf8(result) res.status = falcon.HTTP_200 @@ -194,7 +196,7 @@ class MetricsMeasurements(metrics_api_v2.MetricsMeasurementsV2API): @resource.resource_try_catch_block def _measurement_list(self, tenant_id, name, dimensions, start_timestamp, end_timestamp, req_uri, offset, - limit, merge_metrics_flag): + limit, merge_metrics_flag, group_by): result = self._metrics_repo.measurement_list(tenant_id, self._region, @@ -204,9 +206,10 @@ class MetricsMeasurements(metrics_api_v2.MetricsMeasurementsV2API): end_timestamp, offset, limit, - merge_metrics_flag) + merge_metrics_flag, + group_by) - return helpers.paginate_measurement(result, req_uri, limit) + return helpers.paginate_measurements(result, req_uri, limit) class MetricsStatistics(metrics_api_v2.MetricsStatisticsV2API): @@ -240,11 +243,13 @@ class MetricsStatistics(metrics_api_v2.MetricsStatisticsV2API): offset = helpers.get_query_param(req, 'offset') limit = helpers.get_limit(req) merge_metrics_flag = get_merge_metrics_flag(req) + group_by = helpers.get_query_param(req, "group_by") result = self._metric_statistics(tenant_id, name, dimensions, start_timestamp, end_timestamp, statistics, period, req.uri, - offset, limit, merge_metrics_flag) + offset, limit, merge_metrics_flag, + group_by) res.body = helpers.dumpit_utf8(result) res.status = falcon.HTTP_200 @@ -252,7 +257,7 @@ class MetricsStatistics(metrics_api_v2.MetricsStatisticsV2API): @resource.resource_try_catch_block def _metric_statistics(self, tenant_id, name, dimensions, start_timestamp, end_timestamp, statistics, period, req_uri, - offset, limit, merge_metrics_flag): + offset, limit, merge_metrics_flag, group_by): result = self._metrics_repo.metrics_statistics(tenant_id, self._region, @@ -263,7 +268,8 @@ class MetricsStatistics(metrics_api_v2.MetricsStatisticsV2API): statistics, period, offset, limit, - merge_metrics_flag) + merge_metrics_flag, + group_by) return helpers.paginate_statistics(result, req_uri, limit) diff --git a/monasca_tempest_tests/tests/api/test_measurements.py b/monasca_tempest_tests/tests/api/test_measurements.py index 743aa387a..12d6f5bd0 100644 --- a/monasca_tempest_tests/tests/api/test_measurements.py +++ b/monasca_tempest_tests/tests/api/test_measurements.py @@ -1,4 +1,4 @@ -# (C) Copyright 2015-2016 Hewlett Packard Enterprise Development Company LP +# (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -258,6 +258,33 @@ class TestMeasurements(base.BaseMonascaTest): query_parms) self.assertEqual(200, resp.status) + @test.attr(type="gate") + def test_list_measurements_with_group_by(self): + query_parms = '?name=' + str(self._names_list[1]) + \ + '&group_by=*' + \ + '&start_time=' + str(self._start_time) + \ + '&end_time=' + str(self._end_time) + resp, response_body = self.monasca_client.list_measurements( + query_parms) + self.assertEqual(200, resp.status) + elements = response_body['elements'] + self.assertEqual(len(elements), 4) + self._verify_list_measurements_elements(elements, None, None) + + @test.attr(type="gate") + def test_list_measurements_with_group_by_and_merge(self): + query_parms = '?name=' + str(self._names_list[1]) + \ + '&group_by=*' + \ + '&merge_metrics=true' + \ + '&start_time=' + str(self._start_time) + \ + '&end_time=' + str(self._end_time) + resp, response_body = self.monasca_client.list_measurements( + query_parms) + self.assertEqual(200, resp.status) + elements = response_body['elements'] + self.assertEqual(len(elements), 4) + self._verify_list_measurements_elements(elements, None, None) + @test.attr(type="gate") @test.attr(type=['negative']) def test_list_measurements_with_name_exceeds_max_length(self): @@ -320,8 +347,13 @@ class TestMeasurements(base.BaseMonascaTest): def _verify_list_measurements_elements(self, elements, test_key, test_value): - if elements: - element = elements[0] + if not elements: + error_msg = "Failed: at least one element is needed. " \ + "Number of element = 0." + self.fail(error_msg) + + for element in elements: + # element = elements[0] self.assertEqual(set(element), set(['columns', 'dimensions', 'id', 'measurements', 'name'])) @@ -335,10 +367,6 @@ class TestMeasurements(base.BaseMonascaTest): if test_key is not None and test_value is not None: self.assertEqual(str(element['dimensions'][test_key]), test_value) - else: - error_msg = "Failed: at least one element is needed. " \ - "Number of element = 0." - self.fail(error_msg) def _verify_list_measurements_meas_len(self, measurements, test_len): if measurements: diff --git a/monasca_tempest_tests/tests/api/test_statistics.py b/monasca_tempest_tests/tests/api/test_statistics.py index 1d38fde59..53bc35e30 100644 --- a/monasca_tempest_tests/tests/api/test_statistics.py +++ b/monasca_tempest_tests/tests/api/test_statistics.py @@ -241,8 +241,7 @@ class TestStatistics(base.BaseMonascaTest): ('start_time', str(start_time)), ('end_time', str(end_time)), ('period', 1), - ('limit', limit) - ] + ('limit', limit)] offset = None while True: num_expected_elements = limit @@ -271,7 +270,6 @@ class TestStatistics(base.BaseMonascaTest): # Get the next set offset = self._get_offset(response_body) - @test.attr(type="gate") @test.attr(type=['negative']) def test_list_statistics_with_no_merge_metrics(self):