Support the Event API

This patch adds support for the currently available
Event API, including events, event types, and traits.

Add an optional data type to the query, ex:
   ceilometer list-events -q 'hostname=string::localhost"

bp extend-client-operations

Change-Id: Icea9bd67f8ee4ff2bf9da9ff6894218689580eb3
This commit is contained in:
John Herndon
2013-10-30 10:44:53 -06:00
parent d8ffceeeb7
commit c74231e744
18 changed files with 692 additions and 35 deletions

View File

@@ -66,6 +66,21 @@ def print_list(objs, fields, field_labels, formatters={}, sortby=0):
sortby_index=sortby)
def nested_dict_formatter(field):
# (TMaddox) Because the formatting scheme actually drops the whole object
# into the formatter, rather than just the specified field, we have to
# extract it and then pass the value.
return lambda o: format_nested_dict(getattr(o, field))
def format_nested_dict(d):
pt = prettytable.PrettyTable(caching=False, print_empty=False,
header=False, hrules=prettytable.FRAME)
for k, v in six.iteritems(d):
pt.add_row([k, format_nested_dict(v) if isinstance(v, dict) else v])
return pt.get_string()
def print_dict(d, dict_property="Property", wrap=0):
pt = prettytable.PrettyTable([dict_property, 'Value'],
caching=False, print_empty=False)

View File

@@ -189,7 +189,7 @@ fixtures = {
},
'/v2/alarms?q.field=project_id&q.field=name&q.op=&q.op='
'&q.value=project-id&q.value=SwiftObjectAlarm':
'&q.type=&q.type=&q.value=project-id&q.value=SwiftObjectAlarm':
{
'GET': (
{},
@@ -210,7 +210,7 @@ fixtures = {
ALARM_HISTORY,
),
},
'/v2/alarms/alarm-id/history?q.field=timestamp&q.op=&q.value=NOW':
'/v2/alarms/alarm-id/history?q.field=timestamp&q.op=&q.type=&q.value=NOW':
{
'GET': (
{},
@@ -244,7 +244,7 @@ class AlarmManagerTest(testtools.TestCase):
expect = [
('GET',
'/v2/alarms?q.field=project_id&q.field=name&q.op=&q.op='
'&q.value=project-id&q.value=SwiftObjectAlarm',
'&q.type=&q.type=&q.value=project-id&q.value=SwiftObjectAlarm',
{}, None),
]
self.assertEqual(self.api.calls, expect)
@@ -334,7 +334,7 @@ class AlarmManagerTest(testtools.TestCase):
def test_get_constrained_history(self):
q = [dict(field='timestamp', value='NOW')]
url = ('/v2/alarms/alarm-id/history?q.field=timestamp'
'&q.op=&q.value=NOW')
'&q.op=&q.type=&q.value=NOW')
self._do_test_get_history(q, url)

View File

@@ -0,0 +1,47 @@
# -*- encoding: utf-8 -*-
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# 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
import ceilometerclient.v2.event_types
fixtures = {
'/v2/event_types/': {
'GET': (
{},
['Foo', 'Bar', 'Sna', 'Fu']
),
}
}
class EventTypesManagerTest(utils.BaseTestCase):
def setUp(self):
super(EventTypesManagerTest, self).setUp()
self.api = utils.FakeAPI(fixtures)
self.mgr = ceilometerclient.v2.event_types.EventTypeManager(self.api)
def test_list(self):
event_types = list(self.mgr.list())
expect = [
('GET', '/v2/event_types/', {}, None),
]
self.assertEqual(self.api.calls, expect)
self.assertEqual(len(event_types), 4)
self.assertEqual(event_types[0].event_type, "Foo")
self.assertEqual(event_types[1].event_type, "Bar")
self.assertEqual(event_types[2].event_type, "Sna")
self.assertEqual(event_types[3].event_type, "Fu")

View File

@@ -0,0 +1,189 @@
# -*- encoding: utf-8 -*-
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# 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
import ceilometerclient.v2.events
fixtures = {
'/v2/events': {
'GET': (
{},
[
{
'event_type': 'Foo',
'generated': '1970-01-01T00:00:00',
'traits': {'trait_A': 'abc',
'message_id': '1'},
},
{
'event_type': 'Foo',
'generated': '1970-01-01T00:00:00',
'traits': {'trait_A': 'def',
'message_id': '2'},
},
{
'event_type': 'Bar',
'generated': '1970-01-01T00:00:00',
'traits': {'trait_B': 'bartrait',
'message_id': '3'},
},
]
),
},
'/v2/events?q.field=hostname&q.op=&q.type=string&q.value=localhost':
{
'GET': (
{},
[
{
'event_type': 'Foo',
'generated': '1970-01-01T00:00:00',
'traits': {'trait_A': 'abc',
'hostname': 'localhost',
'message_id': '1'},
},
{
'event_type': 'Foo',
'generated': '1970-01-01T00:00:00',
'traits': {'trait_A': 'def',
'hostname': 'localhost',
'message_id': '2'},
}
]
),
},
'/v2/events?q.field=hostname&q.op=&q.type=&q.value=foreignhost':
{
'GET': (
{},
[
{
'event_type': 'Foo',
'generated': '1970-01-01T00:00:00',
'traits': {'trait_A': 'abc',
'hostname': 'foreignhost',
'message_id': '1'},
},
{
'event_type': 'Foo',
'generated': '1970-01-01T00:00:00',
'traits': {'trait_A': 'def',
'hostname': 'foreignhost',
'message_id': '2'},
}
]
),
},
'/v2/events?q.field=hostname&q.field=num_cpus&q.op=&q.op='
'&q.type=&q.type=integer&q.value=localhost&q.value=5':
{
'GET': (
{},
[
{
'event_type': 'Bar',
'generated': '1970-01-01T00:00:00',
'traits': {'trait_A': 'abc',
'hostname': 'localhost',
'num_cpus': '5',
'message_id': '1'},
},
]
),
},
'/v2/events/2':
{
'GET': (
{},
{
'event_type': 'Foo',
'generated': '1970-01-01T00:00:00',
'traits': {'trait_A': 'def',
'message_id': '2',
'intTrait': '42'},
}
),
},
}
class EventManagerTest(utils.BaseTestCase):
def setUp(self):
super(EventManagerTest, self).setUp()
self.api = utils.FakeAPI(fixtures)
self.mgr = ceilometerclient.v2.events.EventManager(self.api)
def test_list_all(self):
events = list(self.mgr.list())
expect = [
('GET', '/v2/events', {}, None),
]
self.assertEqual(self.api.calls, expect)
self.assertEqual(len(events), 3)
self.assertEqual(events[0].event_type, 'Foo')
self.assertEqual(events[1].event_type, 'Foo')
self.assertEqual(events[2].event_type, 'Bar')
def test_list_one(self):
event = self.mgr.get(2)
expect = [
('GET', '/v2/events/2', {}, None),
]
self.assertEqual(self.api.calls, expect)
self.assertTrue(event)
self.assertEqual(event.event_type, 'Foo')
def test_list_with_query(self):
events = list(self.mgr.list(q=[{"field": "hostname",
"value": "localhost",
"type": "string"}]))
expect = [
('GET', '/v2/events?q.field=hostname&q.op=&q.type=string'
'&q.value=localhost',
{}, None),
]
self.assertEqual(self.api.calls, expect)
self.assertEqual(len(events), 2)
self.assertEqual(events[0].event_type, 'Foo')
def test_list_with_query_no_type(self):
events = list(self.mgr.list(q=[{"field": "hostname",
"value": "foreignhost"}]))
expect = [
('GET', '/v2/events?q.field=hostname&q.op='
'&q.type=&q.value=foreignhost',
{}, None),
]
self.assertEqual(self.api.calls, expect)
self.assertEqual(len(events), 2)
self.assertEqual(events[0].event_type, 'Foo')
def test_list_with_multiple_filters(self):
events = list(self.mgr.list(q=[{"field": "hostname",
"value": "localhost"},
{"field": "num_cpus",
"value": "5",
"type": "integer"}]))
expect = [
('GET', '/v2/events?q.field=hostname&q.field=num_cpus&q.op=&q.op='
'&q.type=&q.type=integer&q.value=localhost&q.value=5',
{}, None),
]
self.assertEqual(self.api.calls, expect)
self.assertEqual(len(events), 1)

View File

@@ -21,7 +21,7 @@ class BuildUrlTest(utils.BaseTestCase):
url = options.build_url('/', [{'field': 'this',
'op': 'gt',
'value': 43}])
self.assertEqual(url, '/?q.field=this&q.op=gt&q.value=43')
self.assertEqual(url, '/?q.field=this&q.op=gt&q.type=&q.value=43')
def test_two(self):
url = options.build_url('/', [{'field': 'this',
@@ -32,13 +32,14 @@ class BuildUrlTest(utils.BaseTestCase):
'value': 88}])
ops = 'q.op=gt&q.op=lt'
vals = 'q.value=43&q.value=88'
types = 'q.type=&q.type='
fields = 'q.field=this&q.field=that'
self.assertEqual(url, '/?%s&%s&%s' % (fields, ops, vals))
self.assertEqual(url, '/?%s&%s&%s&%s' % (fields, ops, types, vals))
def test_default_op(self):
url = options.build_url('/', [{'field': 'this',
'value': 43}])
self.assertEqual(url, '/?q.field=this&q.op=&q.value=43')
self.assertEqual(url, '/?q.field=this&q.op=&q.type=&q.value=43')
def test_one_param(self):
url = options.build_url('/', None, ['period=60'])
@@ -49,26 +50,38 @@ class BuildUrlTest(utils.BaseTestCase):
'others=value'])
self.assertEqual(url, '/?period=60&others=value')
def test_with_data_type(self):
url = options.build_url('/', [{'field': 'f1',
'value': '10',
'type': 'integer'}])
self.assertEqual('/?q.field=f1&q.op=&q.type=integer&q.value=10', url)
class CliTest(utils.BaseTestCase):
def test_one(self):
ar = options.cli_to_array('this<=34')
self.assertEqual(ar, [{'field': 'this', 'op': 'le', 'value': '34'}])
self.assertEqual(ar, [{'field': 'this', 'op': 'le',
'value': '34', 'type': ''}])
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'}])
self.assertEqual(ar, [{'field': 'this', 'op': 'le',
'value': '34', 'type': ''},
{'field': 'that', 'op': 'ne',
'value': 'foo', 'type': ''}])
def test_negative(self):
ar = options.cli_to_array('this>=-783')
self.assertEqual(ar, [{'field': 'this', 'op': 'ge', 'value': '-783'}])
self.assertEqual(ar, [{'field': 'this', 'op': 'ge',
'value': '-783', 'type': ''}])
def test_float(self):
ar = options.cli_to_array('this<=283.347')
self.assertEqual(ar, [{'field': 'this',
'op': 'le', 'value': '283.347'}])
'op': 'le', 'value': '283.347',
'type': ''}])
def test_invalid_seperator(self):
self.assertRaises(ValueError, options.cli_to_array,
@@ -81,4 +94,61 @@ class CliTest(utils.BaseTestCase):
def test_with_dot(self):
ar = options.cli_to_array('metadata.this<=34')
self.assertEqual(ar, [{'field': 'metadata.this',
'op': 'le', 'value': '34'}])
'op': 'le', 'value': '34',
'type': ''}])
def test_without_data_type(self):
ar = options.cli_to_array('hostname=localhost')
self.assertEqual(ar, [{'field': 'hostname',
'op': 'eq',
'value': 'localhost',
'type': ''}])
def test_with_string_data_type(self):
ar = options.cli_to_array('hostname=string::localhost')
self.assertEqual(ar, [{'field': 'hostname',
'op': 'eq',
'type': 'string',
'value': 'localhost'}])
def test_with_int_data_type(self):
ar = options.cli_to_array('port=integer::1234')
self.assertEqual(ar, [{'field': 'port',
'op': 'eq',
'type': 'integer',
'value': '1234'}])
def test_with_bool_data_type(self):
ar = options.cli_to_array('port=boolean::true')
self.assertEqual(ar, [{'field': 'port',
'op': 'eq',
'type': 'boolean',
'value': 'true'}])
def test_with_float_data_type(self):
ar = options.cli_to_array('average=float::1234.5678')
self.assertEqual(ar, [{'field': 'average',
'op': 'eq',
'type': 'float',
'value': '1234.5678'}])
def test_with_datetime_data_type(self):
ar = options.cli_to_array('timestamp=datetime::sometimestamp')
self.assertEqual(ar, [{'field': 'timestamp',
'op': 'eq',
'type': 'datetime',
'value': 'sometimestamp'}])
def test_with_incorrect_type(self):
ar = options.cli_to_array('timestamp=invalid::sometimestamp')
self.assertEqual(ar, [{'field': 'timestamp',
'op': 'eq',
'type': '',
'value': 'invalid::sometimestamp'}])
def test_with_single_colon(self):
ar = options.cli_to_array('timestamp=datetime:sometimestamp')
self.assertEqual(ar, [{'field': 'timestamp',
'op': 'eq',
'type': '',
'value': 'datetime:sometimestamp'}])

View File

@@ -37,7 +37,7 @@ fixtures = {
]
),
},
'/v2/resources?q.field=resource_id&q.op=&q.value=a':
'/v2/resources?q.field=resource_id&q.op=&q.type=&q.value=a':
{
'GET': (
{},
@@ -97,7 +97,8 @@ class ResourceManagerTest(utils.BaseTestCase):
"value": "a"},
]))
expect = [
('GET', '/v2/resources?q.field=resource_id&q.op=&q.value=a',
('GET', '/v2/resources?q.field=resource_id&q.op='
'&q.type=&q.value=a',
{}, None),
]
self.assertEqual(self.api.calls, expect)

View File

@@ -35,7 +35,8 @@ del CREATE_SAMPLE['message_id']
del CREATE_SAMPLE['source']
base_url = '/v2/meters/instance'
args = 'q.field=resource_id&q.field=source&q.op=&q.op=&q.value=foo&q.value=bar'
args = ('q.field=resource_id&q.field=source&q.op=&q.op='
'&q.type=&q.type=&q.value=foo&q.value=bar')
args_limit = 'limit=1'
fixtures = {
base_url:

View File

@@ -133,7 +133,8 @@ class ShellAlarmHistoryCommandTest(utils.BaseTestCase):
def test_alarm_constrained_history(self):
parsed_query = [dict(field='timestamp',
value='2013-10-03T08:59:28',
op='gt')]
op='gt',
type='')]
self._do_test_alarm_history(raw_query='timestamp>2013-10-03T08:59:28',
parsed_query=parsed_query)

View File

@@ -17,7 +17,8 @@ from ceilometerclient.tests import utils
import ceilometerclient.v2.statistics
base_url = '/v2/meters/instance/statistics'
qry = 'q.field=resource_id&q.field=source&q.op=&q.op=&q.value=foo&q.value=bar'
qry = ('q.field=resource_id&q.field=source&q.op=&q.op='
'&q.type=&q.type=&q.value=foo&q.value=bar')
period = '&period=60'
samples = [
{u'count': 135,

View File

@@ -0,0 +1,55 @@
# -*- encoding: utf-8 -*-
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# 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
import ceilometerclient.v2.trait_descriptions
fixtures = {
'/v2/event_types/Foo/traits': {
'GET': (
{},
[
{'name': 'trait_1', 'type': 'string'},
{'name': 'trait_2', 'type': 'integer'},
{'name': 'trait_3', 'type': 'datetime'}
]
),
}
}
class TraitDescriptionManagerTest(utils.BaseTestCase):
def setUp(self):
super(TraitDescriptionManagerTest, self).setUp()
self.api = utils.FakeAPI(fixtures)
self.mgr = (ceilometerclient.v2.trait_descriptions.
TraitDescriptionManager(self.api))
def test_list(self):
trait_descriptions = list(self.mgr.list('Foo'))
expect = [
('GET', '/v2/event_types/Foo/traits', {}, None),
]
self.assertEqual(self.api.calls, expect)
self.assertEqual(len(trait_descriptions), 3)
for i, vals in enumerate([('trait_1', 'string'),
('trait_2', 'integer'),
('trait_3', 'datetime')]):
name, type = vals
self.assertEqual(trait_descriptions[i].name, name)
self.assertEqual(trait_descriptions[i].type, type)

View File

@@ -0,0 +1,61 @@
# -*- encoding: utf-8 -*-
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# 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
import ceilometerclient.v2.traits
fixtures = {
'/v2/event_types/Foo/traits/trait_1': {
'GET': (
{},
[
{'name': 'trait_1',
'type': 'datetime',
'value': '2014-01-07T17:22:10.925553'},
{'name': 'trait_1',
'type': 'datetime',
'value': '2014-01-07T17:23:10.925553'}
]
),
}
}
class TraitManagerTest(utils.BaseTestCase):
def setUp(self):
super(TraitManagerTest, self).setUp()
self.api = utils.FakeAPI(fixtures)
self.mgr = ceilometerclient.v2.traits.TraitManager(self.api)
def test_list(self):
traits = list(self.mgr.list('Foo', 'trait_1'))
expect = [
('GET', '/v2/event_types/Foo/traits/trait_1', {}, None),
]
self.assertEqual(self.api.calls, expect)
self.assertEqual(len(traits), 2)
for i, vals in enumerate([('trait_1',
'datetime',
'2014-01-07T17:22:10.925553'),
('trait_1',
'datetime',
'2014-01-07T17:23:10.925553')]):
name, type, value = vals
self.assertEqual(traits[i].name, name)
self.assertEqual(traits[i].type, type)
self.assertEqual(traits[i].value, value)

View File

@@ -15,10 +15,14 @@
from ceilometerclient.common import http
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 resources
from ceilometerclient.v2 import samples
from ceilometerclient.v2 import statistics
from ceilometerclient.v2 import trait_descriptions
from ceilometerclient.v2 import traits
class Client(http.HTTPClient):
@@ -39,3 +43,7 @@ class Client(http.HTTPClient):
self.statistics = statistics.StatisticsManager(self)
self.resources = resources.ResourceManager(self)
self.alarms = alarms.AlarmManager(self)
self.events = events.EventManager(self)
self.event_types = event_types.EventTypeManager(self)
self.traits = traits.TraitManager(self)
self.trait_info = trait_descriptions.TraitDescriptionManager(self)

View File

@@ -0,0 +1,31 @@
# -*- encoding: utf-8 -*-
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# 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
class EventType(base.Resource):
def __repr__(self):
return "<Event %s>" % self._info
def object_class_str(mgr, value, loaded):
return EventType(mgr, {"event_type": value}, loaded)
class EventTypeManager(base.Manager):
def list(self):
return self._list('/v2/event_types/', obj_class=object_class_str)

View File

@@ -0,0 +1,37 @@
# -*- encoding: utf-8 -*-
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# 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 Event(base.Resource):
def __repr__(self):
return "<Event %s>" % self._info
class EventManager(base.Manager):
resource_class = Event
def list(self, q=None):
path = '/v2/events'
return self._list(options.build_url(path, q))
def get(self, message_id):
path = '/v2/events/%s'
try:
return self._list(path % message_id, expect_single=True)[0]
except IndexError:
return None

View File

@@ -28,10 +28,11 @@ def build_url(path, q, params=None):
if q:
query_params = {'q.field': [],
'q.value': [],
'q.op': []}
'q.op': [],
'q.type': []}
for query in q:
for name in ['field', 'op', 'value']:
for name in ['field', 'op', 'value', 'type']:
query_params['q.%s' % name].append(query.get(name, ''))
# Transform the dict to a sequence of two-element tuples in fixed
@@ -50,13 +51,15 @@ def build_url(path, q, params=None):
def cli_to_array(cli_query):
'''This converts from the cli list of queries to what is required
"""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
@@ -77,6 +80,14 @@ def cli_to_array(cli_query):
string)
return frags
def split_by_data_type(string):
frags = re.findall(r'^(string|integer|float|datetime|boolean)(::)'
r'([^ -,\t\n\r\f\v]+)$', string)
# frags[1] is the separator. Return a list without it if the type
# identifier was found.
return [frags[0][0], frags[0][2]] if frags else None
opts = []
queries = cli_query.split(';')
for q in queries:
@@ -90,6 +101,15 @@ def cli_to_array(cli_query):
opt = {}
opt['field'] = query[0]
opt['op'] = op_lookup[query[1]]
opt['value'] = query[2]
# Allow the data type of the value to be specified via <type>::<value>,
# where type can be one of integer, string, float, datetime, boolean
value_frags = split_by_data_type(query[2])
if not value_frags:
opt['value'] = query[2]
opt['type'] = ''
else:
opt['type'] = value_frags[0]
opt['value'] = value_frags[1]
opts.append(opt)
return opts

View File

@@ -35,7 +35,8 @@ OPERATORS_STRING = dict(gt='>', ge='>=',
@utils.arg('-q', '--query', metavar='<QUERY>',
help='key[op]value; list.')
help='key[op]data_type::value; list. data_type is optional, '
'but if supplied must be string, integer, float, or boolean')
@utils.arg('-m', '--meter', metavar='<NAME>', required=True,
help='Name of meter to show samples for.')
@utils.arg('-p', '--period', metavar='<PERIOD>',
@@ -60,7 +61,8 @@ def do_statistics(cc, args):
@utils.arg('-q', '--query', metavar='<QUERY>',
help='key[op]value; list.')
help='key[op]data_type::value; list. data_type is optional, '
'but if supplied must be string, integer, float, or boolean')
@utils.arg('-m', '--meter', metavar='<NAME>', required=True,
help='Name of meter to show samples for.')
@utils.arg('-l', '--limit', metavar='<NUMBER>',
@@ -121,7 +123,8 @@ def do_sample_create(cc, args={}):
@utils.arg('-q', '--query', metavar='<QUERY>',
help='key[op]value; list.')
help='key[op]data_type::value; list. data_type is optional, '
'but if supplied must be string, integer, float, or boolean')
def do_meter_list(cc, args={}):
'''List the user's meters.'''
meters = cc.meters.list(q=options.cli_to_array(args.query))
@@ -193,7 +196,8 @@ def alarm_change_detail_formatter(change):
@utils.arg('-q', '--query', metavar='<QUERY>',
help='key[op]value; list.')
help='key[op]data_type::value; list. data_type is optional, '
'but if supplied must be string, integer, float, or boolean')
def do_alarm_list(cc, args={}):
'''List the user's alarms.'''
alarms = cc.alarms.list(q=options.cli_to_array(args.query))
@@ -326,9 +330,8 @@ def do_alarm_create(cc, args={}):
dest='threshold_rule/threshold',
help='Threshold to evaluate against')
@utils.arg('-q', '--query', metavar='<QUERY>',
dest='threshold_rule/query',
help='The query to find the data for computing statistics '
'(key[op]value; list.)')
help='key[op]data_type::value; list. data_type is optional, '
'but if supplied must be string, integer, float, or boolean')
@utils.arg('--repeat-actions', dest='repeat_actions',
metavar='{True|False}', type=utils.string_to_bool,
default=False,
@@ -425,9 +428,8 @@ def do_alarm_update(cc, args={}):
dest='threshold_rule/threshold',
help='Threshold to evaluate against')
@utils.arg('-q', '--query', metavar='<QUERY>',
dest='threshold_rule/query',
help='The query to find the data for computing statistics '
'(key[op]value; list.)')
help='key[op]data_type::value; list. data_type is optional, '
'but if supplied must be string, integer, float, or boolean')
@utils.arg('--repeat-actions', dest='repeat_actions',
metavar='{True|False}', type=utils.string_to_bool,
help=('True if actions should be repeatedly notified '
@@ -512,7 +514,8 @@ def do_alarm_state_get(cc, args={}):
@utils.arg('-a', '--alarm_id', metavar='<ALARM_ID>', required=True,
help='ID of the alarm for which history is shown.')
@utils.arg('-q', '--query', metavar='<QUERY>',
help='key[op]value; list.')
help='key[op]data_type::value; list. data_type is optional, '
'but if supplied must be string, integer, float, or boolean')
def do_alarm_history(cc, args={}):
'''Display the change history of an alarm.'''
kwargs = dict(alarm_id=args.alarm_id,
@@ -529,7 +532,8 @@ def do_alarm_history(cc, args={}):
@utils.arg('-q', '--query', metavar='<QUERY>',
help='key[op]value; list.')
help='key[op]data_type::value; list. data_type is optional, '
'but if supplied must be string, integer, float, or boolean.')
def do_resource_list(cc, args={}):
'''List the resources.'''
resources = cc.resources.list(q=options.cli_to_array(args.query))
@@ -553,3 +557,61 @@ def do_resource_show(cc, args={}):
'project_id', 'metadata']
data = dict([(f, getattr(resource, f, '')) for f in fields])
utils.print_dict(data, wrap=72)
@utils.arg('-q', '--query', metavar='<QUERY>',
help='key[op]data_type::value; list. data_type is optional, '
'but if supplied must be string, integer, float'
'or datetime.')
def do_event_list(cc, args={}):
'''List events.'''
events = cc.events.list(q=options.cli_to_array(args.query))
field_labels = ['Message ID', 'Event Type', 'Generated', 'Traits']
fields = ['message_id', 'event_type', 'generated', 'traits']
utils.print_list(events, fields, field_labels,
formatters={
'traits': utils.nested_dict_formatter('traits')})
@utils.arg('-m', '--message_id', metavar='<message_id>',
help='The id of the event. Should be a UUID',
required=True)
def do_event_show(cc, args={}):
'''Show a particular event.'''
event = cc.events.get(args.message_id)
fields = ['event_type', 'generated', 'traits']
data = dict([(f, getattr(event, f, '')) for f in fields])
utils.print_dict(data, wrap=72)
def do_event_type_list(cc, args={}):
'''List event types.'''
event_types = cc.event_types.list()
utils.print_list(event_types, ['event_type'], ['Event Type'])
@utils.arg('-e', '--event_type', metavar='<EVENT_TYPE>',
help='Type of the event for which traits will be shown',
required=True)
def do_trait_description_list(cc, args={}):
'''List trait info for an event type.'''
trait_descriptions = cc.trait_descriptions.list(args.event_type)
field_labels = ['Trait Name', 'Data Type']
fields = ['name', 'type']
utils.print_list(trait_descriptions, fields, field_labels)
@utils.arg('-e', '--event_type', metavar='<EVENT_TYPE>',
help='Type of the event for which traits will listed',
required=True)
@utils.arg('-t', '--trait_name', metavar='<TRAIT_NAME>',
help='The name of the trait to list',
required=True)
def do_trait_list(cc, args={}):
'''List trait all traits with name <trait_name> for Event Type
<event_type>.
'''
traits = cc.traits.list(args.event_type, args.trait_name)
field_labels = ['Trait Name', 'Value', 'Data Type']
fields = ['name', 'value', 'type']
utils.print_list(traits, fields, field_labels)

View File

@@ -0,0 +1,29 @@
# -*- encoding: utf-8 -*-
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# 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
class TraitDescription(base.Resource):
def __repr__(self):
return "<Trait %s>" % self._info
class TraitDescriptionManager(base.Manager):
resource_class = TraitDescription
def list(self, event_type):
path = '/v2/event_types/%s/traits' % event_type
return self._list(path)

View File

@@ -0,0 +1,29 @@
# -*- encoding: utf-8 -*-
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# 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
class Trait(base.Resource):
def __repr__(self):
return "<Trait %s>" % self._info
class TraitManager(base.Manager):
resource_class = Trait
def list(self, event_type, trait_name):
path = '/v2/event_types/%s/traits/%s' % (event_type, trait_name)
return self._list(path)