Merge "Support aggregation accross metrics and resources"
This commit is contained in:
commit
2a84d7337f
|
@ -60,6 +60,22 @@ class MetricClientTest(base.ClientTestBase):
|
|||
'timestamp': '2015-03-06T14:34:12+00:00',
|
||||
'value': '12.0'}], measures)
|
||||
|
||||
# MEASURES AGGREGATION
|
||||
result = self.gnocchi(
|
||||
'measures', params=("aggregation "
|
||||
"--metric %s "
|
||||
"--aggregation mean "
|
||||
"--start 2015-03-06T14:32:00 "
|
||||
"--end 2015-03-06T14:36:00"
|
||||
) % metric["id"])
|
||||
measures = self.parser.listing(result)
|
||||
self.assertEqual([{'granularity': '1.0',
|
||||
'timestamp': '2015-03-06T14:33:57+00:00',
|
||||
'value': '43.11'},
|
||||
{'granularity': '1.0',
|
||||
'timestamp': '2015-03-06T14:34:12+00:00',
|
||||
'value': '12.0'}], measures)
|
||||
|
||||
# LIST
|
||||
result = self.gnocchi('metric', params="list")
|
||||
metrics = self.parser.listing(result)
|
||||
|
@ -132,6 +148,23 @@ class MetricClientTest(base.ClientTestBase):
|
|||
'timestamp': '2015-03-06T14:34:12+00:00',
|
||||
'value': '12.0'}], measures)
|
||||
|
||||
# MEASURES AGGREGATION
|
||||
result = self.gnocchi(
|
||||
'measures', params=("--debug aggregation "
|
||||
"--query \"id='metric-res'\" "
|
||||
"-m metric-name "
|
||||
"--aggregation mean "
|
||||
"--needed-overlap 0 "
|
||||
"--start 2015-03-06T14:32:00 "
|
||||
"--end 2015-03-06T14:36:00"))
|
||||
measures = self.parser.listing(result)
|
||||
self.assertEqual([{'granularity': '1.0',
|
||||
'timestamp': '2015-03-06T14:33:57+00:00',
|
||||
'value': '43.11'},
|
||||
{'granularity': '1.0',
|
||||
'timestamp': '2015-03-06T14:34:12+00:00',
|
||||
'value': '12.0'}], measures)
|
||||
|
||||
# LIST
|
||||
result = self.gnocchi('metric', params="list")
|
||||
metrics = self.parser.listing(result)
|
||||
|
|
|
@ -119,3 +119,9 @@ def dict_from_parsed_args(parsed_args, attrs):
|
|||
if value is not None:
|
||||
d[attr] = value
|
||||
return d
|
||||
|
||||
|
||||
def dict_to_querystring(objs):
|
||||
return "&".join(["%s=%s" % (k, v)
|
||||
for k, v in objs.items()
|
||||
if v is not None])
|
||||
|
|
|
@ -16,6 +16,7 @@ import uuid
|
|||
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from gnocchiclient import utils
|
||||
from gnocchiclient.v1 import base
|
||||
|
||||
|
||||
|
@ -29,11 +30,12 @@ class MetricManager(base.Manager):
|
|||
return self.client.api.get(url).json()
|
||||
|
||||
@staticmethod
|
||||
def _ensure_metric_is_uuid(metric):
|
||||
def _ensure_metric_is_uuid(metric, attribute="resource_id"):
|
||||
try:
|
||||
uuid.UUID(metric)
|
||||
except ValueError:
|
||||
raise TypeError("resource_id is required to get a metric by name")
|
||||
raise TypeError("%s is required to get a metric by name" %
|
||||
attribute)
|
||||
|
||||
def get(self, metric, resource_id=None):
|
||||
"""Get an metric
|
||||
|
@ -156,3 +158,45 @@ class MetricManager(base.Manager):
|
|||
"resource/generic/%s/metric/%s/measures" % (
|
||||
resource_id, metric))
|
||||
return self.client.api.get(url, params=params).json()
|
||||
|
||||
def aggregation(self, metrics, query=None,
|
||||
start=None, end=None, aggregation=None,
|
||||
needed_overlap=None):
|
||||
"""Get measurements of a aggregated metrics
|
||||
|
||||
:param metrics: IDs of metric or metric name
|
||||
:type metric: list or str
|
||||
:param query: The query dictionary
|
||||
:type query: dict
|
||||
:param start: begin of the period
|
||||
:type start: timestamp
|
||||
:param end: end of the period
|
||||
:type end: timestamp
|
||||
:param aggregation: aggregation to retrieve
|
||||
:type aggregation: str
|
||||
|
||||
See Gnocchi REST API documentation for the format
|
||||
of *query dictionary*
|
||||
http://docs.openstack.org/developer/gnocchi/rest.html#searching-for-resources
|
||||
"""
|
||||
|
||||
if isinstance(start, datetime.datetime):
|
||||
start = start.isoformat()
|
||||
if isinstance(end, datetime.datetime):
|
||||
end = end.isoformat()
|
||||
|
||||
params = dict(start=start, end=end, aggregation=aggregation,
|
||||
needed_overlap=needed_overlap)
|
||||
if query is None:
|
||||
for metric in metrics:
|
||||
self._ensure_metric_is_uuid(metric)
|
||||
params['metric'] = metrics
|
||||
url = self.client._build_url("aggregation/metric")
|
||||
return self.client.api.get(url, params=params).json()
|
||||
else:
|
||||
url = self.client._build_url(
|
||||
"aggregation/resource/generic/metric/%s?%s" % (
|
||||
metrics, utils.dict_to_querystring(params)))
|
||||
return self.client.api.post(
|
||||
url, headers={'Content-Type': "application/json"},
|
||||
data=jsonutils.dumps(query)).json()
|
||||
|
|
|
@ -138,3 +138,41 @@ class CliMeasuresAdd(command.Command):
|
|||
resource_id=parsed_args.resource_id,
|
||||
measures=parsed_args.measure,
|
||||
)
|
||||
|
||||
|
||||
class CliMeasuresAggregation(lister.Lister):
|
||||
COLS = ('timestamp', 'granularity', 'value')
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliMeasuresAggregation, self).get_parser(prog_name)
|
||||
parser.add_argument("-m", "--metric", nargs='+', required=True,
|
||||
help="metrics IDs or metric name")
|
||||
parser.add_argument("--aggregation",
|
||||
help="aggregation to retrieve")
|
||||
parser.add_argument("--start",
|
||||
help="start of the period")
|
||||
parser.add_argument("--end",
|
||||
help="end of the period")
|
||||
parser.add_argument("--needed-overlap", type=float,
|
||||
help=("percent of datapoints in each "
|
||||
"metrics required"))
|
||||
parser.add_argument("--query", help="Query"),
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
metrics = parsed_args.metric
|
||||
query = None
|
||||
if parsed_args.query:
|
||||
query = utils.search_query_builder(parsed_args.query)
|
||||
if len(parsed_args.metric) != 1:
|
||||
raise ValueError("One metric is required if query is provied")
|
||||
metrics = parsed_args.metric[0]
|
||||
measures = self.app.client.metric.aggregation(
|
||||
metrics=metrics,
|
||||
query=query,
|
||||
aggregation=parsed_args.aggregation,
|
||||
start=parsed_args.start,
|
||||
end=parsed_args.end,
|
||||
needed_overlap=parsed_args.needed_overlap,
|
||||
)
|
||||
return self.COLS, measures
|
||||
|
|
|
@ -133,14 +133,14 @@ class ResourceManager(base.Manager):
|
|||
url = self.client._build_url("resource/generic/%s" % (resource_id))
|
||||
self.client.api.delete(url)
|
||||
|
||||
def search(self, resource_type="generic", request=None, details=False,
|
||||
def search(self, resource_type="generic", query=None, details=False,
|
||||
history=False, limit=None, marker=None, sorts=None):
|
||||
"""List resources
|
||||
|
||||
:param resource_type: Type of the resource
|
||||
:param resource_type: str
|
||||
:param request: The search request dictionary
|
||||
:type resource_type: dict
|
||||
:param query: The query dictionary
|
||||
:type query: dict
|
||||
:param details: Show all attributes of resources
|
||||
:type details: bool
|
||||
:param history: Show the history of resources
|
||||
|
@ -154,14 +154,14 @@ class ResourceManager(base.Manager):
|
|||
:type sorts: list of str
|
||||
|
||||
See Gnocchi REST API documentation for the format
|
||||
of *request dictionary*
|
||||
of *query dictionary*
|
||||
http://docs.openstack.org/developer/gnocchi/rest.html#searching-for-resources
|
||||
"""
|
||||
|
||||
request = request or {}
|
||||
query = query or {}
|
||||
qs = _get_pagination_options(details, False, limit, marker, sorts)
|
||||
url = self.client._build_url(
|
||||
"search/resource/%s?%s" % (resource_type, qs))
|
||||
return self.client.api.post(
|
||||
url, headers={'Content-Type': "application/json"},
|
||||
data=jsonutils.dumps(request)).json()
|
||||
data=jsonutils.dumps(query)).json()
|
||||
|
|
|
@ -92,7 +92,7 @@ class CliResourceSearch(CliResourceList):
|
|||
def take_action(self, parsed_args):
|
||||
resources = self.app.client.resource.search(
|
||||
resource_type=parsed_args.resource_type,
|
||||
request=utils.search_query_builder(parsed_args.query),
|
||||
query=utils.search_query_builder(parsed_args.query),
|
||||
**self._get_pagination_options(parsed_args))
|
||||
return utils.list2cols(self.COLS, resources)
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ gnocchi.cli.v1 =
|
|||
metric_delete = gnocchiclient.v1.metric_cli:CliMetricDelete
|
||||
measures_get = gnocchiclient.v1.metric_cli:CliMeasuresGet
|
||||
measures_add = gnocchiclient.v1.metric_cli:CliMeasuresAdd
|
||||
measures_aggregation = gnocchiclient.v1.metric_cli:CliMeasuresAggregation
|
||||
|
||||
keystoneclient.auth.plugin =
|
||||
gnocchi-noauth = gnocchiclient.noauth:GnocchiNoAuthPlugin
|
||||
|
|
Loading…
Reference in New Issue