Merge "Add resources and meters to the v2 shell"
This commit is contained in:
@@ -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)
|
||||
|
||||
31
ceilometerclient/v2/meters.py
Normal file
31
ceilometerclient/v2/meters.py
Normal file
@@ -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))
|
||||
83
ceilometerclient/v2/options.py
Normal file
83
ceilometerclient/v2/options.py
Normal file
@@ -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
|
||||
31
ceilometerclient/v2/resources.py
Normal file
31
ceilometerclient/v2/resources.py
Normal file
@@ -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))
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
68
tests/v2/test_options.py
Normal file
68
tests/v2/test_options.py
Normal file
@@ -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')
|
||||
Reference in New Issue
Block a user