diff --git a/ceilometerclient/tests/v2/test_query_samples.py b/ceilometerclient/tests/v2/test_query_samples.py new file mode 100644 index 0000000..854056c --- /dev/null +++ b/ceilometerclient/tests/v2/test_query_samples.py @@ -0,0 +1,65 @@ +# Copyright Ericsson AB 2014. All rights reserved +# +# Author: Balazs Gibizer +# +# 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 +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from ceilometerclient.tests import utils +from ceilometerclient.v2 import query_samples + + +SAMPLE = {u'id': u'b55d1526-9929-11e3-a3f6-02163e5df1e6', + u'metadata': { + u'name1': u'value1', + u'name2': u'value2'}, + u'meter': 'instance', + u'project_id': u'35b17138-b364-4e6a-a131-8f3099c5be68', + u'resource_id': u'bd9431c1-8d69-4ad3-803a-8d4a6b89fd36', + u'source': u'openstack', + u'timestamp': u'2014-02-19T05:50:16.673604', + u'type': u'gauge', + u'unit': u'instance', + u'volume': 1, + u'user_id': 'efd87807-12d2-4b38-9c70-5f5c2ac427ff'} + +QUERY = {"filter": {"and": [{"=": {"source": "openstack"}}, + {">": {"timestamp": "2014-02-19T05:50:16"}}]}, + "orderby": [{"timestamp": "desc"}, {"volume": "asc"}], + "limit": 10} + +base_url = '/v2/query/samples' +fixtures = { + base_url: + { + 'POST': ( + {}, + [SAMPLE], + ), + }, +} + + +class QuerySamplesManagerTest(utils.BaseTestCase): + + def setUp(self): + super(QuerySamplesManagerTest, self).setUp() + self.api = utils.FakeAPI(fixtures) + self.mgr = query_samples.QuerySamplesManager(self.api) + + def test_query(self): + samples = self.mgr.query(**QUERY) + expect = [ + ('POST', '/v2/query/samples', {}, QUERY), + ] + self.assertEqual(expect, self.api.calls) + self.assertEqual(1, len(samples)) diff --git a/ceilometerclient/tests/v2/test_shell.py b/ceilometerclient/tests/v2/test_shell.py index 4d45a0b..47ad578 100644 --- a/ceilometerclient/tests/v2/test_shell.py +++ b/ceilometerclient/tests/v2/test_shell.py @@ -1,3 +1,7 @@ +# Copyright Ericsson AB 2014. All rights reserved +# +# Author: Balazs Gibizer +# # 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 # a copy of the License at @@ -410,3 +414,58 @@ class ShellSampleCreateCommandTest(utils.BaseTestCase): | volume | 1.0 | +-------------------+---------------------------------------------+ ''') + + +class ShellQuerySamplesCommandTest(utils.BaseTestCase): + + SAMPLE = [{u'id': u'b55d1526-9929-11e3-a3f6-02163e5df1e6', + u'metadata': { + u'name1': u'value1', + u'name2': u'value2'}, + u'meter': 'instance', + u'project_id': u'35b17138-b364-4e6a-a131-8f3099c5be68', + u'resource_id': u'bd9431c1-8d69-4ad3-803a-8d4a6b89fd36', + u'source': u'openstack', + u'timestamp': u'2014-02-19T05:50:16.673604', + u'type': u'gauge', + u'unit': u'instance', + u'volume': 1, + u'user_id': 'efd87807-12d2-4b38-9c70-5f5c2ac427ff'}] + + QUERY = {"filter": {"and": [{"=": {"source": "openstack"}}, + {">": {"timestamp": "2014-02-19T05:50:16"}}]}, + "orderby": [{"timestamp": "desc"}, {"volume": "asc"}], + "limit": 10} + + def setUp(self): + super(ShellQuerySamplesCommandTest, self).setUp() + self.cc = mock.Mock() + self.args = mock.Mock() + self.args.filter = self.QUERY["filter"] + self.args.orderby = self.QUERY["orderby"] + self.args.limit = self.QUERY["limit"] + + def test_query(self): + + ret_sample = [samples.Sample(mock.Mock(), sample) + for sample in self.SAMPLE] + self.cc.query_samples.query.return_value = ret_sample + org_stdout = sys.stdout + try: + sys.stdout = output = six.StringIO() + ceilometer_shell.do_query_samples(self.cc, self.args) + finally: + sys.stdout = org_stdout + + self.assertEqual('''\ ++--------------------------------------+----------+-------+--------+---------\ +-+----------------------------+ +| Resource ID | Meter | Type | Volume | Unit \ + | Timestamp | ++--------------------------------------+----------+-------+--------+---------\ +-+----------------------------+ +| bd9431c1-8d69-4ad3-803a-8d4a6b89fd36 | instance | gauge | 1 | instance\ + | 2014-02-19T05:50:16.673604 | ++--------------------------------------+----------+-------+--------+---------\ +-+----------------------------+ +''', output.getvalue()) diff --git a/ceilometerclient/v2/client.py b/ceilometerclient/v2/client.py index 0693f5d..bf76deb 100644 --- a/ceilometerclient/v2/client.py +++ b/ceilometerclient/v2/client.py @@ -1,5 +1,6 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. +# Copyright Ericsson AB 2014. All rights reserved +# +# Author: Balazs Gibizer # # 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 @@ -18,6 +19,7 @@ from ceilometerclient.v2 import alarms from ceilometerclient.v2 import event_types from ceilometerclient.v2 import events from ceilometerclient.v2 import meters +from ceilometerclient.v2 import query_samples from ceilometerclient.v2 import resources from ceilometerclient.v2 import samples from ceilometerclient.v2 import statistics @@ -48,3 +50,5 @@ class Client(object): self.traits = traits.TraitManager(self.http_client) self.trait_info = trait_descriptions.\ TraitDescriptionManager(self.http_client) + self.query_samples = query_samples.QuerySamplesManager( + self.http_client) diff --git a/ceilometerclient/v2/query_samples.py b/ceilometerclient/v2/query_samples.py new file mode 100644 index 0000000..d0b1f30 --- /dev/null +++ b/ceilometerclient/v2/query_samples.py @@ -0,0 +1,44 @@ +# Copyright Ericsson AB 2014. All rights reserved +# +# Author: Balazs Gibizer +# +# 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 +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from ceilometerclient.common import base +from ceilometerclient.v2 import samples + + +class QuerySamplesManager(base.Manager): + resource_class = samples.Sample + + @staticmethod + def _path(): + return '/v2/query/samples' + + def query(self, filter, orderby, limit): + query = {} + if filter: + query["filter"] = filter + if orderby: + query["orderby"] = orderby + if limit: + query["limit"] = limit + + url = self._path() + resp, body = self.api.json_request('POST', + url, + body=query) + if body: + return [self.resource_class(self, b) for b in body] + else: + return [] diff --git a/ceilometerclient/v2/shell.py b/ceilometerclient/v2/shell.py index 628a3d9..2eeef27 100644 --- a/ceilometerclient/v2/shell.py +++ b/ceilometerclient/v2/shell.py @@ -1,8 +1,10 @@ # -*- encoding: utf-8 -*- # # Copyright © 2013 Red Hat, Inc +# Copyright Ericsson AB 2014. All rights reserved # -# Author: Angus Salkeld +# Authors: Angus Salkeld +# Balazs Gibizer # # 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 @@ -33,6 +35,9 @@ STATISTICS = ['max', 'min', 'avg', 'sum', 'count'] OPERATORS_STRING = dict(gt='>', ge='>=', lt='<', le="<=", eq='==', ne='!=') +ORDER_DIRECTIONS = ['asc', 'desc'] +COMPLEX_OPERATORS = ['and', 'or'] +SIMPLE_OPERATORS = ["=", "!=", "<", "<=", '>', '>='] @utils.arg('-q', '--query', metavar='', @@ -639,3 +644,30 @@ def do_trait_list(cc, args={}): field_labels = ['Trait Name', 'Value', 'Data Type'] fields = ['name', 'value', 'type'] utils.print_list(traits, fields, field_labels) + + +@utils.arg('-f', '--filter', metavar='', + help=('{complex_op: [{simple_op: {field_name: value}}]} ' + 'The complex_op is one of: ' + str(COMPLEX_OPERATORS) + ', ' + 'simple_op is one of: ' + str(SIMPLE_OPERATORS) + '.')) +@utils.arg('-o', '--orderby', metavar='', + help=('[{field_name: direction}, {field_name: direction}] ' + 'The direction is one of: ' + str(ORDER_DIRECTIONS) + '.')) +@utils.arg('-l', '--limit', metavar='', + help='Maximum number of samples to return.') +def do_query_samples(cc, args): + '''Query samples.''' + fields = {'filter': args.filter, + 'orderby': args.orderby, + 'limit': args.limit} + try: + samples = cc.query_samples.query(**fields) + except exc.HTTPNotFound: + raise exc.CommandError('Samples not found') + else: + field_labels = ['Resource ID', 'Meter', 'Type', 'Volume', 'Unit', + 'Timestamp'] + fields = ['resource_id', 'meter', 'type', + 'volume', 'unit', 'timestamp'] + utils.print_list(samples, fields, field_labels, + sortby=None)