summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-03-04 12:58:16 +0000
committerGerrit Code Review <review@openstack.org>2013-03-04 12:58:16 +0000
commita550dcfa4971df5ef517aa73d2ebc7a6675c72c6 (patch)
treec3b2f140a758cb2cb13928736a8fe818b94537c0
parentb81481c826b49fc9acf3b3e1d66e1563b138a810 (diff)
parent987f1ec20a429d4ebcccacd2bc17d6f4759c2ad8 (diff)
downloadpython-ceilometerclient-a550dcfa4971df5ef517aa73d2ebc7a6675c72c6.tar.gz
Merge "Add resources and meters to the v2 shell"
-rw-r--r--ceilometerclient/v2/client.py4
-rw-r--r--ceilometerclient/v2/meters.py31
-rw-r--r--ceilometerclient/v2/options.py83
-rw-r--r--ceilometerclient/v2/resources.py31
-rw-r--r--ceilometerclient/v2/samples.py22
-rw-r--r--ceilometerclient/v2/shell.py33
-rw-r--r--ceilometerclient/v2/statistics.py8
-rw-r--r--tests/v2/test_options.py68
8 files changed, 252 insertions, 28 deletions
diff --git a/ceilometerclient/v2/client.py b/ceilometerclient/v2/client.py
index 82b29e0..71622a6 100644
--- a/ceilometerclient/v2/client.py
+++ b/ceilometerclient/v2/client.py
@@ -14,6 +14,8 @@
# under the License.
from ceilometerclient.common import http
+from ceilometerclient.v2 import meters
+from ceilometerclient.v2 import resources
from ceilometerclient.v2 import samples
from ceilometerclient.v2 import statistics
@@ -31,5 +33,7 @@ class Client(http.HTTPClient):
def __init__(self, *args, **kwargs):
""" Initialize a new client for the Ceilometer v1 API. """
super(Client, self).__init__(*args, **kwargs)
+ self.meters = meters.MeterManager(self)
self.samples = samples.SampleManager(self)
self.statistics = statistics.StatisticsManager(self)
+ self.resources = resources.ResourceManager(self)
diff --git a/ceilometerclient/v2/meters.py b/ceilometerclient/v2/meters.py
new file mode 100644
index 0000000..baa7cbd
--- /dev/null
+++ b/ceilometerclient/v2/meters.py
@@ -0,0 +1,31 @@
+# -*- encoding: utf-8 -*-
+#
+# Copyright © 2013 Red Hat, Inc
+#
+# 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 options
+
+
+class Meter(base.Resource):
+ def __repr__(self):
+ return "<Meter %s>" % self._info
+
+
+class MeterManager(base.Manager):
+ resource_class = Meter
+
+ def list(self, q=None):
+ path = '/v2/meters'
+ return self._list(options.build_url(path, q))
diff --git a/ceilometerclient/v2/options.py b/ceilometerclient/v2/options.py
new file mode 100644
index 0000000..debf6b2
--- /dev/null
+++ b/ceilometerclient/v2/options.py
@@ -0,0 +1,83 @@
+#
+# 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.
+
+import re
+import urllib
+
+
+def build_url(path, q):
+ '''
+ This converts from a list of dict's to what the rest api needs
+ so from:
+ "[{field=this,op=le,value=34},{field=that,op=eq,value=foo}]"
+ to:
+ "?q.field=this&q.op=le&q.value=34&
+ q.field=that&q.op=eq&q.value=foo"
+ '''
+ if q:
+ query_params = {'q.field': [],
+ 'q.value': [],
+ 'q.op': []}
+
+ for query in q:
+ for name in ['field', 'op', 'value']:
+ query_params['q.%s' % name].append(query.get(name, ''))
+
+ path += "?" + urllib.urlencode(query_params, doseq=True)
+
+ return path
+
+
+def cli_to_array(cli_query):
+ '''
+ This converts from the cli list of queries to what is required
+ by the python api.
+ so from:
+ "this<=34;that=foo"
+ to
+ "[{field=this,op=le,value=34},{field=that,op=eq,value=foo}]"
+ '''
+ if cli_query is None:
+ return None
+
+ op_lookup = {'!=': 'ne',
+ '>=': 'ge',
+ '<=': 'le',
+ '>': 'gt',
+ '<': 'lt',
+ '=': 'eq'}
+
+ def split_by_op(string):
+ # two character split (<=,!=)
+ fragments = re.findall(r'(\w+)([><!]=)([^ -,\t\n\r\f\v]+)', string)
+ if len(fragments) == 0:
+ #single char split (<,=)
+ fragments = re.findall(r'(\w+)([><=])([^ -,\t\n\r\f\v]+)', string)
+ return fragments
+
+ opts = []
+ queries = cli_query.split(';')
+ for q in queries:
+ frag = split_by_op(q)
+ if len(frag) > 1:
+ raise ValueError('incorrect seperator %s in query "%s"' %
+ ('(should be ";")', q))
+ if len(frag) == 0:
+ raise ValueError('invalid query %s' % q)
+ query = frag[0]
+ opt = {}
+ opt['field'] = query[0]
+ opt['op'] = op_lookup[query[1]]
+ opt['value'] = query[2]
+ opts.append(opt)
+ return opts
diff --git a/ceilometerclient/v2/resources.py b/ceilometerclient/v2/resources.py
new file mode 100644
index 0000000..9b80671
--- /dev/null
+++ b/ceilometerclient/v2/resources.py
@@ -0,0 +1,31 @@
+# -*- encoding: utf-8 -*-
+#
+# Copyright © 2013 Red Hat, Inc
+#
+# 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 options
+
+
+class Resource(base.Resource):
+ def __repr__(self):
+ return "<Resource %s>" % self._info
+
+
+class ResourceManager(base.Manager):
+ resource_class = Resource
+
+ def list(self, q=None):
+ path = '/v2/resources'
+ return self._list(options.build_url(path, q))
diff --git a/ceilometerclient/v2/samples.py b/ceilometerclient/v2/samples.py
index f7e4268..11258a5 100644
--- a/ceilometerclient/v2/samples.py
+++ b/ceilometerclient/v2/samples.py
@@ -1,5 +1,3 @@
-# Copyright 2012 OpenStack LLC.
-# All Rights Reserved.
#
# 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
@@ -13,9 +11,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-import urllib
-
from ceilometerclient.common import base
+from ceilometerclient.v2 import options
class Sample(base.Resource):
@@ -26,23 +23,8 @@ class Sample(base.Resource):
class SampleManager(base.Manager):
resource_class = Sample
- @staticmethod
- def build_url(path, q):
- if q:
- query_params = {'q.field': [],
- 'q.value': [],
- 'q.op': []}
-
- for query in q:
- for name in ['field', 'op', 'value']:
- query_params['q.%s' % name].append(query.get(name, ''))
-
- path += "?" + urllib.urlencode(query_params, doseq=True)
-
- return path
-
def list(self, meter_name=None, q=None):
path = '/v2/meters'
if meter_name:
path += '/' + meter_name
- return self._list(self.build_url(path, q))
+ return self._list(options.build_url(path, q))
diff --git a/ceilometerclient/v2/shell.py b/ceilometerclient/v2/shell.py
index 524d46a..1f11dc5 100644
--- a/ceilometerclient/v2/shell.py
+++ b/ceilometerclient/v2/shell.py
@@ -1,5 +1,6 @@
+# -*- encoding: utf-8 -*-
#
-# Copyright © 2013 Red Hat
+# Copyright © 2013 Red Hat, Inc
#
# Author: Angus Salkeld <asalkeld@redhat.com>
#
@@ -16,6 +17,7 @@
# under the License.
from ceilometerclient.common import utils
+from ceilometerclient.v2 import options
import ceilometerclient.exc as exc
@@ -26,7 +28,7 @@ import ceilometerclient.exc as exc
def do_statistics(cc, args):
'''List the statistics for this meters'''
fields = {'meter_name': args.meter,
- 'q': args.query}
+ 'q': options.cli_to_array(args.query)}
if args.meter is None:
raise exc.CommandError('Meter name not provided (-m <meter name>)')
try:
@@ -50,7 +52,7 @@ def do_statistics(cc, args):
def do_sample_list(cc, args):
'''List the samples for this meters'''
fields = {'meter_name': args.meter,
- 'q': args.query}
+ 'q': options.cli_to_array(args.query)}
if args.meter is None:
raise exc.CommandError('Meter name not provided (-m <meter name>)')
try:
@@ -64,3 +66,28 @@ def do_sample_list(cc, args):
'counter_volume', 'counter_unit', 'timestamp']
utils.print_list(samples, fields, field_labels,
sortby=0)
+
+
+@utils.arg('-q', '--query', metavar='<QUERY>',
+ help='key[op]value; list.')
+def do_meter_list(cc, args={}):
+ '''List the user's meter'''
+ meters = cc.meters.list(q=options.cli_to_array(args.query))
+ field_labels = ['Name', 'Type', 'Unit', 'Resource ID', 'User ID',
+ 'Project ID']
+ fields = ['name', 'type', 'unit', 'resource_id', 'user_id',
+ 'project_id']
+ utils.print_list(meters, fields, field_labels,
+ sortby=0)
+
+
+@utils.arg('-q', '--query', metavar='<QUERY>',
+ help='key[op]value; list.')
+def do_resource_list(cc, args={}):
+ '''List the resources'''
+ resources = cc.resources.list(q=options.cli_to_array(args.query))
+
+ field_labels = ['Resource ID', 'Source', 'User ID', 'Project ID']
+ fields = ['resource_id', 'source', 'user_id', 'project_id']
+ utils.print_list(resources, fields, field_labels,
+ sortby=1)
diff --git a/ceilometerclient/v2/statistics.py b/ceilometerclient/v2/statistics.py
index 9cf4198..f663942 100644
--- a/ceilometerclient/v2/statistics.py
+++ b/ceilometerclient/v2/statistics.py
@@ -1,5 +1,3 @@
-# Copyright 2012 OpenStack LLC.
-# All Rights Reserved.
#
# 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
@@ -13,8 +11,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-from ceilometerclient.v2 import samples
from ceilometerclient.common import base
+from ceilometerclient.v2 import options
class Statistics(base.Resource):
@@ -22,10 +20,10 @@ class Statistics(base.Resource):
return "<Statistics %s>" % self._info
-class StatisticsManager(samples.SampleManager):
+class StatisticsManager(base.Manager):
resource_class = Statistics
def list(self, meter_name, q=None):
- return self._list(self.build_url(
+ return self._list(options.build_url(
'/v2/meters/' + meter_name + '/statistics',
q))
diff --git a/tests/v2/test_options.py b/tests/v2/test_options.py
new file mode 100644
index 0000000..33b1a2b
--- /dev/null
+++ b/tests/v2/test_options.py
@@ -0,0 +1,68 @@
+#
+# 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.
+
+import unittest
+
+from ceilometerclient.v2 import options
+
+
+class BuildUrlTest(unittest.TestCase):
+
+ def test_one(self):
+ url = options.build_url('/', [{'field': 'this',
+ 'op': 'gt',
+ 'value': 43}])
+ self.assertEqual(url, '/?q.op=gt&q.value=43&q.field=this')
+
+ def test_two(self):
+ url = options.build_url('/', [{'field': 'this',
+ 'op': 'gt',
+ 'value': 43},
+ {'field': 'that',
+ 'op': 'lt',
+ 'value': 88}])
+ ops = 'q.op=gt&q.op=lt'
+ vals = 'q.value=43&q.value=88'
+ fields = 'q.field=this&q.field=that'
+ self.assertEqual(url, '/?%s&%s&%s' % (ops, vals, fields))
+
+ def test_default_op(self):
+ url = options.build_url('/', [{'field': 'this',
+ 'value': 43}])
+ self.assertEqual(url, '/?q.op=&q.value=43&q.field=this')
+
+
+class CliTest(unittest.TestCase):
+
+ def test_one(self):
+ ar = options.cli_to_array('this<=34')
+ self.assertEqual(ar, [{'field': 'this','op': 'le','value': '34'}])
+
+ def test_two(self):
+ ar = options.cli_to_array('this<=34;that!=foo')
+ self.assertEqual(ar, [{'field': 'this','op': 'le','value': '34'},
+ {'field': 'that','op': 'ne','value': 'foo'}])
+
+ def test_negative(self):
+ ar = options.cli_to_array('this>=-783')
+ self.assertEqual(ar, [{'field': 'this','op': 'ge','value': '-783'}])
+
+ def test_float(self):
+ ar = options.cli_to_array('this<=283.347')
+ self.assertEqual(ar, [{'field': 'this','op': 'le','value': '283.347'}])
+
+ def test_invalid_seperator(self):
+ self.assertRaises(ValueError, options.cli_to_array, 'this=2.4,fooo=doof')
+
+ def test_invalid_operator(self):
+ self.assertRaises(ValueError, options.cli_to_array, 'this=2.4;fooo-doof')