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:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
47
ceilometerclient/tests/v2/test_event_types.py
Normal file
47
ceilometerclient/tests/v2/test_event_types.py
Normal 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")
|
||||
189
ceilometerclient/tests/v2/test_events.py
Normal file
189
ceilometerclient/tests/v2/test_events.py
Normal 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)
|
||||
@@ -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'}])
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
55
ceilometerclient/tests/v2/test_trait_descriptions.py
Normal file
55
ceilometerclient/tests/v2/test_trait_descriptions.py
Normal 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)
|
||||
61
ceilometerclient/tests/v2/test_traits.py
Normal file
61
ceilometerclient/tests/v2/test_traits.py
Normal 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)
|
||||
@@ -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)
|
||||
|
||||
31
ceilometerclient/v2/event_types.py
Normal file
31
ceilometerclient/v2/event_types.py
Normal 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)
|
||||
37
ceilometerclient/v2/events.py
Normal file
37
ceilometerclient/v2/events.py
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
29
ceilometerclient/v2/trait_descriptions.py
Normal file
29
ceilometerclient/v2/trait_descriptions.py
Normal 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)
|
||||
29
ceilometerclient/v2/traits.py
Normal file
29
ceilometerclient/v2/traits.py
Normal 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)
|
||||
Reference in New Issue
Block a user