summaryrefslogtreecommitdiff
path: root/ceilometerclient/tests/unit/v2
diff options
context:
space:
mode:
Diffstat (limited to 'ceilometerclient/tests/unit/v2')
-rw-r--r--ceilometerclient/tests/unit/v2/__init__.py0
-rw-r--r--ceilometerclient/tests/unit/v2/test_alarms.py540
-rw-r--r--ceilometerclient/tests/unit/v2/test_event_types.py50
-rw-r--r--ceilometerclient/tests/unit/v2/test_events.py198
-rw-r--r--ceilometerclient/tests/unit/v2/test_options.py239
-rw-r--r--ceilometerclient/tests/unit/v2/test_query_alarm_history.py66
-rw-r--r--ceilometerclient/tests/unit/v2/test_query_alarms.py74
-rw-r--r--ceilometerclient/tests/unit/v2/test_query_samples.py67
-rw-r--r--ceilometerclient/tests/unit/v2/test_resources.py118
-rw-r--r--ceilometerclient/tests/unit/v2/test_samples.py198
-rw-r--r--ceilometerclient/tests/unit/v2/test_shell.py1175
-rw-r--r--ceilometerclient/tests/unit/v2/test_statistics.py224
-rw-r--r--ceilometerclient/tests/unit/v2/test_trait_descriptions.py56
-rw-r--r--ceilometerclient/tests/unit/v2/test_traits.py62
14 files changed, 3067 insertions, 0 deletions
diff --git a/ceilometerclient/tests/unit/v2/__init__.py b/ceilometerclient/tests/unit/v2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ceilometerclient/tests/unit/v2/__init__.py
diff --git a/ceilometerclient/tests/unit/v2/test_alarms.py b/ceilometerclient/tests/unit/v2/test_alarms.py
new file mode 100644
index 0000000..4239d2f
--- /dev/null
+++ b/ceilometerclient/tests/unit/v2/test_alarms.py
@@ -0,0 +1,540 @@
+#
+# 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.
+
+import copy
+
+import six
+from six.moves import xrange # noqa
+import testtools
+
+from ceilometerclient import exc
+from ceilometerclient.openstack.common.apiclient import client
+from ceilometerclient.openstack.common.apiclient import fake_client
+from ceilometerclient.v2 import alarms
+
+AN_ALARM = {u'alarm_actions': [u'http://site:8000/alarm'],
+ u'ok_actions': [u'http://site:8000/ok'],
+ u'description': u'An alarm',
+ u'type': u'threshold',
+ u'severity': 'low',
+ u'threshold_rule': {
+ u'meter_name': u'storage.objects',
+ u'query': [{u'field': u'key_name',
+ u'op': u'eq',
+ u'value': u'key_value'}],
+ u'evaluation_periods': 2,
+ u'period': 240.0,
+ u'statistic': u'avg',
+ u'threshold': 200.0,
+ u'comparison_operator': 'gt'},
+ u'time_constraints': [
+ {
+ u'name': u'cons1',
+ u'description': u'desc1',
+ u'start': u'0 11 * * *',
+ u'duration': 300,
+ u'timezone': u''},
+ {
+ u'name': u'cons2',
+ u'description': u'desc2',
+ u'start': u'0 23 * * *',
+ u'duration': 600,
+ u'timezone': ''}],
+ u'timestamp': u'2013-05-09T13:41:23.085000',
+ u'enabled': True,
+ u'alarm_id': u'alarm-id',
+ u'state': u'ok',
+ u'insufficient_data_actions': [u'http://site:8000/nodata'],
+ u'user_id': u'user-id',
+ u'project_id': u'project-id',
+ u'state_timestamp': u'2013-05-09T13:41:23.085000',
+ u'repeat_actions': False,
+ u'name': 'SwiftObjectAlarm'}
+CREATE_ALARM = copy.deepcopy(AN_ALARM)
+del CREATE_ALARM['timestamp']
+del CREATE_ALARM['state_timestamp']
+del CREATE_ALARM['alarm_id']
+CREATE_ALARM_WITHOUT_TC = copy.deepcopy(CREATE_ALARM)
+del CREATE_ALARM_WITHOUT_TC['time_constraints']
+DELTA_ALARM = {u'alarm_actions': ['url1', 'url2']}
+DELTA_ALARM_RULE = {u'comparison_operator': u'lt',
+ u'threshold': 42.1,
+ u'meter_name': u'foobar',
+ u'query': [{u'field': u'key_name',
+ u'op': u'eq',
+ u'value': u'key_value'}]}
+DELTA_ALARM_TC = [{u'name': u'cons1',
+ u'duration': 500}]
+DELTA_ALARM['time_constraints'] = DELTA_ALARM_TC
+UPDATED_ALARM = copy.deepcopy(AN_ALARM)
+UPDATED_ALARM.update(DELTA_ALARM)
+UPDATED_ALARM['threshold_rule'].update(DELTA_ALARM_RULE)
+DELTA_ALARM['remove_time_constraints'] = 'cons2'
+UPDATED_ALARM['time_constraints'] = [{u'name': u'cons1',
+ u'description': u'desc1',
+ u'start': u'0 11 * * *',
+ u'duration': 500,
+ u'timezone': u''}]
+DELTA_ALARM['threshold_rule'] = DELTA_ALARM_RULE
+UPDATE_ALARM = copy.deepcopy(UPDATED_ALARM)
+UPDATE_ALARM['remove_time_constraints'] = 'cons2'
+del UPDATE_ALARM['user_id']
+del UPDATE_ALARM['project_id']
+del UPDATE_ALARM['name']
+del UPDATE_ALARM['alarm_id']
+del UPDATE_ALARM['timestamp']
+del UPDATE_ALARM['state_timestamp']
+
+AN_LEGACY_ALARM = {u'alarm_actions': [u'http://site:8000/alarm'],
+ u'ok_actions': [u'http://site:8000/ok'],
+ u'description': u'An alarm',
+ u'matching_metadata': {u'key_name': u'key_value'},
+ u'evaluation_periods': 2,
+ u'timestamp': u'2013-05-09T13:41:23.085000',
+ u'enabled': True,
+ u'meter_name': u'storage.objects',
+ u'period': 240.0,
+ u'alarm_id': u'alarm-id',
+ u'state': u'ok',
+ u'severity': u'low',
+ u'insufficient_data_actions': [u'http://site:8000/nodata'],
+ u'statistic': u'avg',
+ u'threshold': 200.0,
+ u'user_id': u'user-id',
+ u'project_id': u'project-id',
+ u'state_timestamp': u'2013-05-09T13:41:23.085000',
+ u'comparison_operator': 'gt',
+ u'repeat_actions': False,
+ u'name': 'SwiftObjectAlarm'}
+CREATE_LEGACY_ALARM = copy.deepcopy(AN_LEGACY_ALARM)
+del CREATE_LEGACY_ALARM['timestamp']
+del CREATE_LEGACY_ALARM['state_timestamp']
+del CREATE_LEGACY_ALARM['alarm_id']
+DELTA_LEGACY_ALARM = {u'alarm_actions': ['url1', 'url2'],
+ u'comparison_operator': u'lt',
+ u'meter_name': u'foobar',
+ u'threshold': 42.1}
+DELTA_LEGACY_ALARM['time_constraints'] = [{u'name': u'cons1',
+ u'duration': 500}]
+DELTA_LEGACY_ALARM['remove_time_constraints'] = 'cons2'
+UPDATED_LEGACY_ALARM = copy.deepcopy(AN_LEGACY_ALARM)
+UPDATED_LEGACY_ALARM.update(DELTA_LEGACY_ALARM)
+UPDATE_LEGACY_ALARM = copy.deepcopy(UPDATED_LEGACY_ALARM)
+del UPDATE_LEGACY_ALARM['user_id']
+del UPDATE_LEGACY_ALARM['project_id']
+del UPDATE_LEGACY_ALARM['name']
+del UPDATE_LEGACY_ALARM['alarm_id']
+del UPDATE_LEGACY_ALARM['timestamp']
+del UPDATE_LEGACY_ALARM['state_timestamp']
+
+FULL_DETAIL = ('{"alarm_actions": [], '
+ '"user_id": "8185aa72421a4fd396d4122cba50e1b5", '
+ '"name": "scombo", '
+ '"timestamp": "2013-10-03T08:58:33.647912", '
+ '"enabled": true, '
+ '"state_timestamp": "2013-10-03T08:58:33.647912", '
+ '"rule": {"operator": "or", "alarm_ids": '
+ '["062cc907-3a9f-4867-ab3b-fa83212b39f7"]}, '
+ '"alarm_id": "alarm-id, '
+ '"state": "insufficient data", '
+ '"insufficient_data_actions": [], '
+ '"repeat_actions": false, '
+ '"ok_actions": [], '
+ '"project_id": "57d04f24d0824b78b1ea9bcecedbda8f", '
+ '"type": "combination", '
+ '"description": "Combined state of alarms '
+ '062cc907-3a9f-4867-ab3b-fa83212b39f7"}')
+ALARM_HISTORY = [{'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f',
+ 'user_id': '8185aa72421a4fd396d4122cba50e1b5',
+ 'event_id': 'c74a8611-6553-4764-a860-c15a6aabb5d0',
+ 'timestamp': '2013-10-03T08:59:28.326000',
+ 'detail': '{"state": "alarm"}',
+ 'alarm_id': 'alarm-id',
+ 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f',
+ 'type': 'state transition'},
+ {'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f',
+ 'user_id': '8185aa72421a4fd396d4122cba50e1b5',
+ 'event_id': 'c74a8611-6553-4764-a860-c15a6aabb5d0',
+ 'timestamp': '2013-10-03T08:59:28.326000',
+ 'detail': '{"description": "combination of one"}',
+ 'alarm_id': 'alarm-id',
+ 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f',
+ 'type': 'rule change'},
+ {'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f',
+ 'user_id': '8185aa72421a4fd396d4122cba50e1b5',
+ 'event_id': '4fd7df9e-190d-4471-8884-dc5a33d5d4bb',
+ 'timestamp': '2013-10-03T08:58:33.647000',
+ 'detail': FULL_DETAIL,
+ 'alarm_id': 'alarm-id',
+ 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f',
+ 'type': 'creation'}]
+
+fixtures = {
+ '/v2/alarms':
+ {
+ 'GET': (
+ {},
+ [AN_ALARM],
+ ),
+ 'POST': (
+ {},
+ CREATE_ALARM,
+ ),
+ },
+ '/v2/alarms/alarm-id':
+ {
+ 'GET': (
+ {},
+ AN_ALARM,
+ ),
+ 'PUT': (
+ {},
+ UPDATED_ALARM,
+ ),
+ 'DELETE': (
+ {},
+ None,
+ ),
+ },
+ '/v2/alarms/unk-alarm-id':
+ {
+ 'GET': (
+ {},
+ None,
+ ),
+ 'PUT': (
+ {},
+ None,
+ ),
+ },
+ '/v2/alarms/alarm-id/state':
+ {
+ 'PUT': (
+ {},
+ {'alarm': 'alarm'}
+ ),
+ 'GET': (
+ {},
+ {'alarm': 'alarm'}
+ ),
+
+ },
+ '/v2/alarms?q.field=project_id&q.field=name&q.op=&q.op='
+ '&q.type=&q.type=&q.value=project-id&q.value=SwiftObjectAlarm':
+ {
+ 'GET': (
+ {},
+ [AN_ALARM],
+ ),
+ },
+ '/v2/alarms/victim-id':
+ {
+ 'DELETE': (
+ {},
+ None,
+ ),
+ },
+ '/v2/alarms/alarm-id/history':
+ {
+ 'GET': (
+ {},
+ ALARM_HISTORY,
+ ),
+ },
+ '/v2/alarms/alarm-id/history?q.field=timestamp&q.op=&q.type=&q.value=NOW':
+ {
+ 'GET': (
+ {},
+ ALARM_HISTORY,
+ ),
+ },
+}
+
+
+class AlarmManagerTest(testtools.TestCase):
+
+ def setUp(self):
+ super(AlarmManagerTest, self).setUp()
+ self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
+ self.api = client.BaseClient(self.http_client)
+ self.mgr = alarms.AlarmManager(self.api)
+
+ def test_list_all(self):
+ alarms = list(self.mgr.list())
+ expect = [
+ 'GET', '/v2/alarms'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(alarms), 1)
+ self.assertEqual(alarms[0].alarm_id, 'alarm-id')
+
+ def test_list_with_query(self):
+ alarms = list(self.mgr.list(q=[{"field": "project_id",
+ "value": "project-id"},
+ {"field": "name",
+ "value": "SwiftObjectAlarm"}]))
+ expect = [
+ 'GET',
+ '/v2/alarms?q.field=project_id&q.field=name&q.op=&q.op='
+ '&q.type=&q.type=&q.value=project-id&q.value=SwiftObjectAlarm',
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(alarms), 1)
+ self.assertEqual(alarms[0].alarm_id, 'alarm-id')
+
+ def test_get(self):
+ alarm = self.mgr.get(alarm_id='alarm-id')
+ expect = [
+ 'GET', '/v2/alarms/alarm-id'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertIsNotNone(alarm)
+ self.assertEqual(alarm.alarm_id, 'alarm-id')
+ self.assertEqual(alarm.rule, alarm.threshold_rule)
+
+ def test_create(self):
+ alarm = self.mgr.create(**CREATE_ALARM)
+ expect = [
+ 'POST', '/v2/alarms'
+ ]
+ self.http_client.assert_called(*expect, body=CREATE_ALARM)
+ self.assertIsNotNone(alarm)
+
+ def test_update(self):
+ alarm = self.mgr.update(alarm_id='alarm-id', **UPDATE_ALARM)
+ expect_get = [
+ 'GET', '/v2/alarms/alarm-id'
+ ]
+ expect_put = [
+ 'PUT', '/v2/alarms/alarm-id', UPDATED_ALARM
+ ]
+ self.http_client.assert_called(*expect_get, pos=0)
+ self.http_client.assert_called(*expect_put, pos=1)
+ self.assertIsNotNone(alarm)
+ self.assertEqual(alarm.alarm_id, 'alarm-id')
+ for (key, value) in six.iteritems(UPDATED_ALARM):
+ self.assertEqual(getattr(alarm, key), value)
+
+ def test_update_delta(self):
+ alarm = self.mgr.update(alarm_id='alarm-id', **DELTA_ALARM)
+ expect_get = [
+ 'GET', '/v2/alarms/alarm-id'
+ ]
+ expect_put = [
+ 'PUT', '/v2/alarms/alarm-id', UPDATED_ALARM
+ ]
+ self.http_client.assert_called(*expect_get, pos=0)
+ self.http_client.assert_called(*expect_put, pos=1)
+ self.assertIsNotNone(alarm)
+ self.assertEqual(alarm.alarm_id, 'alarm-id')
+ for (key, value) in six.iteritems(UPDATED_ALARM):
+ self.assertEqual(getattr(alarm, key), value)
+
+ def test_set_state(self):
+ state = self.mgr.set_state(alarm_id='alarm-id', state='alarm')
+ expect = [
+ 'PUT', '/v2/alarms/alarm-id/state'
+ ]
+ self.http_client.assert_called(*expect, body='alarm')
+ self.assertEqual(state, {'alarm': 'alarm'})
+
+ def test_get_state(self):
+ state = self.mgr.get_state(alarm_id='alarm-id')
+ expect = [
+ 'GET', '/v2/alarms/alarm-id/state'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(state, {'alarm': 'alarm'})
+
+ def test_delete(self):
+ deleted = self.mgr.delete(alarm_id='victim-id')
+ expect = [
+ 'DELETE', '/v2/alarms/victim-id'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertIsNone(deleted)
+
+ def test_get_from_alarm_class(self):
+ alarm = self.mgr.get(alarm_id='alarm-id')
+ self.assertIsNotNone(alarm)
+ alarm.get()
+ expect = [
+ 'GET', '/v2/alarms/alarm-id'
+ ]
+ self.http_client.assert_called(*expect, pos=0)
+ self.http_client.assert_called(*expect, pos=1)
+ self.assertEqual('alarm-id', alarm.alarm_id)
+ self.assertEqual(alarm.threshold_rule, alarm.rule)
+
+ def test_get_state_from_alarm_class(self):
+ alarm = self.mgr.get(alarm_id='alarm-id')
+ self.assertIsNotNone(alarm)
+ state = alarm.get_state()
+ expect_get_1 = [
+ 'GET', '/v2/alarms/alarm-id'
+ ]
+ expect_get_2 = [
+ 'GET', '/v2/alarms/alarm-id/state'
+ ]
+ self.http_client.assert_called(*expect_get_1, pos=0)
+ self.http_client.assert_called(*expect_get_2, pos=1)
+ self.assertEqual('alarm', state)
+
+ def test_update_missing(self):
+ alarm = None
+ try:
+ alarm = self.mgr.update(alarm_id='unk-alarm-id', **UPDATE_ALARM)
+ except exc.CommandError:
+ pass
+ self.assertEqual(alarm, None)
+
+ def test_delete_from_alarm_class(self):
+ alarm = self.mgr.get(alarm_id='alarm-id')
+ self.assertIsNotNone(alarm)
+ deleted = alarm.delete()
+ expect_get = [
+ 'GET', '/v2/alarms/alarm-id'
+ ]
+ expect_delete = [
+ 'DELETE', '/v2/alarms/alarm-id'
+ ]
+ self.http_client.assert_called(*expect_get, pos=0)
+ self.http_client.assert_called(*expect_delete, pos=1)
+ self.assertIsNone(deleted)
+
+ def _do_test_get_history(self, q, url):
+ history = self.mgr.get_history(q=q, alarm_id='alarm-id')
+ expect = ['GET', url]
+ self.http_client.assert_called(*expect)
+ for i in xrange(len(history)):
+ change = history[i]
+ self.assertIsInstance(change, alarms.AlarmChange)
+ for k, v in six.iteritems(ALARM_HISTORY[i]):
+ self.assertEqual(getattr(change, k), v)
+
+ def test_get_all_history(self):
+ url = '/v2/alarms/alarm-id/history'
+ self._do_test_get_history(None, url)
+
+ def test_get_constrained_history(self):
+ q = [dict(field='timestamp', value='NOW')]
+ url = ('/v2/alarms/alarm-id/history?q.field=timestamp'
+ '&q.op=&q.type=&q.value=NOW')
+ self._do_test_get_history(q, url)
+
+
+class AlarmLegacyManagerTest(testtools.TestCase):
+
+ def setUp(self):
+ super(AlarmLegacyManagerTest, self).setUp()
+ self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
+ self.api = client.BaseClient(self.http_client)
+ self.mgr = alarms.AlarmManager(self.api)
+
+ def test_create(self):
+ alarm = self.mgr.create(**CREATE_LEGACY_ALARM)
+ expect = [
+ 'POST', '/v2/alarms', CREATE_ALARM_WITHOUT_TC,
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertIsNotNone(alarm)
+
+ def test_create_counter_name(self):
+ create = {}
+ create.update(CREATE_LEGACY_ALARM)
+ create['counter_name'] = CREATE_LEGACY_ALARM['meter_name']
+ del create['meter_name']
+ alarm = self.mgr.create(**create)
+ expect = [
+ 'POST', '/v2/alarms', CREATE_ALARM_WITHOUT_TC,
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertIsNotNone(alarm)
+
+ def test_update(self):
+ alarm = self.mgr.update(alarm_id='alarm-id', **DELTA_LEGACY_ALARM)
+ expect_put = [
+ 'PUT', '/v2/alarms/alarm-id', UPDATED_ALARM
+ ]
+ self.http_client.assert_called(*expect_put)
+ self.assertIsNotNone(alarm)
+ self.assertEqual(alarm.alarm_id, 'alarm-id')
+ for (key, value) in six.iteritems(UPDATED_ALARM):
+ self.assertEqual(getattr(alarm, key), value)
+
+ def test_update_counter_name(self):
+ updated = {}
+ updated.update(UPDATE_LEGACY_ALARM)
+ updated['counter_name'] = UPDATED_LEGACY_ALARM['meter_name']
+ del updated['meter_name']
+ alarm = self.mgr.update(alarm_id='alarm-id', **updated)
+ expect_put = [
+ 'PUT', '/v2/alarms/alarm-id', UPDATED_ALARM
+ ]
+ self.http_client.assert_called(*expect_put)
+ self.assertIsNotNone(alarm)
+ self.assertEqual(alarm.alarm_id, 'alarm-id')
+ for (key, value) in six.iteritems(UPDATED_ALARM):
+ self.assertEqual(getattr(alarm, key), value)
+
+
+class AlarmTimeConstraintTest(testtools.TestCase):
+
+ def setUp(self):
+ super(AlarmTimeConstraintTest, self).setUp()
+ self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
+ self.api = client.BaseClient(self.http_client)
+ self.mgr = alarms.AlarmManager(self.api)
+
+ def test_add_new(self):
+ new_constraint = dict(name='cons3',
+ start='0 0 * * *',
+ duration=500)
+ kwargs = dict(time_constraints=[new_constraint])
+ self.mgr.update(alarm_id='alarm-id', **kwargs)
+ body = copy.deepcopy(AN_ALARM)
+ body[u'time_constraints'] = \
+ AN_ALARM[u'time_constraints'] + [new_constraint]
+ expect = [
+ 'PUT', '/v2/alarms/alarm-id', body
+ ]
+ self.http_client.assert_called(*expect)
+
+ def test_update_existing(self):
+ updated_constraint = dict(name='cons2',
+ duration=500)
+ kwargs = dict(time_constraints=[updated_constraint])
+ self.mgr.update(alarm_id='alarm-id', **kwargs)
+ body = copy.deepcopy(AN_ALARM)
+ body[u'time_constraints'][1] = dict(name='cons2',
+ description='desc2',
+ start='0 23 * * *',
+ duration=500,
+ timezone='')
+
+ expect = [
+ 'PUT', '/v2/alarms/alarm-id', body
+ ]
+ self.http_client.assert_called(*expect)
+
+ def test_remove(self):
+ kwargs = dict(remove_time_constraints=['cons2'])
+ self.mgr.update(alarm_id='alarm-id', **kwargs)
+ body = copy.deepcopy(AN_ALARM)
+ body[u'time_constraints'] = AN_ALARM[u'time_constraints'][:1]
+ expect = [
+ 'PUT', '/v2/alarms/alarm-id', body
+ ]
+ self.http_client.assert_called(*expect)
diff --git a/ceilometerclient/tests/unit/v2/test_event_types.py b/ceilometerclient/tests/unit/v2/test_event_types.py
new file mode 100644
index 0000000..7dc1508
--- /dev/null
+++ b/ceilometerclient/tests/unit/v2/test_event_types.py
@@ -0,0 +1,50 @@
+# 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.openstack.common.apiclient import client
+from ceilometerclient.openstack.common.apiclient import fake_client
+from ceilometerclient.tests.unit 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.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
+ self.api = client.BaseClient(self.http_client)
+ self.mgr = ceilometerclient.v2.event_types.EventTypeManager(self.api)
+
+ def test_list(self):
+ event_types = list(self.mgr.list())
+ expect = [
+ 'GET', '/v2/event_types/'
+ ]
+ self.http_client.assert_called(*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")
diff --git a/ceilometerclient/tests/unit/v2/test_events.py b/ceilometerclient/tests/unit/v2/test_events.py
new file mode 100644
index 0000000..6cb1c1e
--- /dev/null
+++ b/ceilometerclient/tests/unit/v2/test_events.py
@@ -0,0 +1,198 @@
+# 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.openstack.common.apiclient import client
+from ceilometerclient.openstack.common.apiclient import fake_client
+from ceilometerclient.tests.unit import utils
+import ceilometerclient.v2.events
+
+
+fixtures = {
+ '/v2/events': {
+ 'GET': (
+ {},
+ [
+ {
+ 'message_id': '1',
+ 'event_type': 'Foo',
+ 'generated': '1970-01-01T00:00:00',
+ 'traits': {'trait_A': 'abc'},
+ },
+ {
+ 'message_id': '2',
+ 'event_type': 'Foo',
+ 'generated': '1970-01-01T00:00:00',
+ 'traits': {'trait_A': 'def'},
+ },
+ {
+ 'message_id': '3',
+ 'event_type': 'Bar',
+ 'generated': '1970-01-01T00:00:00',
+ 'traits': {'trait_B': 'bartrait'},
+ },
+ ]
+ ),
+ },
+ '/v2/events?q.field=hostname&q.op=&q.type=string&q.value=localhost':
+ {
+ 'GET': (
+ {},
+ [
+ {
+ 'message_id': '1',
+ 'event_type': 'Foo',
+ 'generated': '1970-01-01T00:00:00',
+ 'traits': {'trait_A': 'abc',
+ 'hostname': 'localhost'},
+ },
+ {
+ 'message_id': '2',
+ 'event_type': 'Foo',
+ 'generated': '1970-01-01T00:00:00',
+ 'traits': {'trait_A': 'def',
+ 'hostname': 'localhost'},
+ }
+ ]
+ ),
+ },
+ '/v2/events?q.field=hostname&q.op=&q.type=&q.value=foreignhost':
+ {
+ 'GET': (
+ {},
+ [
+ {
+ 'message_id': '1',
+ 'event_type': 'Foo',
+ 'generated': '1970-01-01T00:00:00',
+ 'traits': {'trait_A': 'abc',
+ 'hostname': 'foreignhost'},
+ },
+ {
+ 'message_id': '2',
+ 'event_type': 'Foo',
+ 'generated': '1970-01-01T00:00:00',
+ 'traits': {'trait_A': 'def',
+ 'hostname': 'foreignhost'},
+ }
+ ]
+ ),
+ },
+ '/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': (
+ {},
+ [
+ {
+ 'message_id': '1',
+ 'event_type': 'Bar',
+ 'generated': '1970-01-01T00:00:00',
+ 'traits': {'trait_A': 'abc',
+ 'hostname': 'localhost',
+ 'num_cpus': '5'},
+ },
+ ]
+ ),
+ },
+
+ '/v2/events/2':
+ {
+ 'GET': (
+ {},
+ {
+ 'message_id': '2',
+ 'event_type': 'Foo',
+ 'generated': '1970-01-01T00:00:00',
+ 'traits': {'trait_A': 'def',
+ 'intTrait': '42'},
+ }
+ ),
+ },
+}
+
+
+class EventManagerTest(utils.BaseTestCase):
+
+ def setUp(self):
+ super(EventManagerTest, self).setUp()
+ self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
+ self.api = client.BaseClient(self.http_client)
+ self.mgr = ceilometerclient.v2.events.EventManager(self.api)
+
+ def test_list_all(self):
+ events = list(self.mgr.list())
+ expect = [
+ 'GET', '/v2/events'
+ ]
+ self.http_client.assert_called(*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'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertIsNotNone(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'
+ ]
+ self.http_client.assert_called(*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'
+ ]
+ self.http_client.assert_called(*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'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(events), 1)
+
+ def test_get_from_event_class(self):
+ event = self.mgr.get(2)
+ self.assertIsNotNone(event)
+ event.get()
+ expect = [
+ 'GET', '/v2/events/2'
+ ]
+ self.http_client.assert_called(*expect, pos=0)
+ self.http_client.assert_called(*expect, pos=1)
+ self.assertEqual('Foo', event.event_type)
diff --git a/ceilometerclient/tests/unit/v2/test_options.py b/ceilometerclient/tests/unit/v2/test_options.py
new file mode 100644
index 0000000..1a9ec24
--- /dev/null
+++ b/ceilometerclient/tests/unit/v2/test_options.py
@@ -0,0 +1,239 @@
+#
+# 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.unit import utils
+from ceilometerclient.v2 import options
+
+
+class BuildUrlTest(utils.BaseTestCase):
+
+ def test_one(self):
+ url = options.build_url('/', [{'field': 'this',
+ 'op': 'gt',
+ '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',
+ 'op': 'gt',
+ 'value': 43},
+ {'field': 'that',
+ 'op': 'lt',
+ '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&%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.type=&q.value=43')
+
+ def test_one_param(self):
+ url = options.build_url('/', None, ['period=60'])
+ self.assertEqual(url, '/?period=60')
+
+ def test_two_params(self):
+ url = options.build_url('/', None, ['period=60',
+ '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', 'type': ''}])
+
+ def test_two(self):
+ ar = options.cli_to_array('this<=34;that!=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', 'type': ''}])
+
+ def test_float(self):
+ ar = options.cli_to_array('this<=283.347')
+ self.assertEqual(ar, [{'field': 'this',
+ 'op': 'le', 'value': '283.347',
+ 'type': ''}])
+
+ def test_comma(self):
+ ar = options.cli_to_array('this=2.4,fooo=doof')
+ self.assertEqual([{'field': 'this',
+ 'op': 'eq',
+ 'value': '2.4,fooo=doof',
+ 'type': ''}],
+ ar)
+
+ def test_special_character(self):
+ ar = options.cli_to_array('key~123=value!123')
+ self.assertEqual([{'field': 'key~123',
+ 'op': 'eq',
+ 'value': 'value!123',
+ 'type': ''}],
+ ar)
+
+ def _do_test_typed_float_op(self, op, op_str):
+ ar = options.cli_to_array('that%sfloat::283.347' % op)
+ self.assertEqual([{'field': 'that',
+ 'type': 'float',
+ 'value': '283.347',
+ 'op': op_str}],
+ ar)
+
+ def test_typed_float_eq(self):
+ self._do_test_typed_float_op('<', 'lt')
+
+ def test_typed_float_le(self):
+ self._do_test_typed_float_op('<=', 'le')
+
+ def test_typed_string_whitespace(self):
+ ar = options.cli_to_array('state=string::insufficient data')
+ self.assertEqual([{'field': 'state',
+ 'op': 'eq',
+ 'type': 'string',
+ 'value': 'insufficient data'}],
+ ar)
+
+ def test_typed_string_whitespace_complex(self):
+ ar = options.cli_to_array(
+ 'that>=float::99.9999;state=string::insufficient data'
+ )
+ self.assertEqual([{'field': 'that',
+ 'op': 'ge',
+ 'type': 'float',
+ 'value': '99.9999'},
+ {'field': 'state',
+ 'op': 'eq',
+ 'type': 'string',
+ 'value': 'insufficient data'}],
+ ar)
+
+ def test_invalid_operator(self):
+ self.assertRaises(ValueError, options.cli_to_array,
+ 'this=2.4;fooo-doof')
+
+ def test_with_dot(self):
+ ar = options.cli_to_array('metadata.this<=34')
+ self.assertEqual(ar, [{'field': 'metadata.this',
+ 'op': 'le', 'value': '34',
+ 'type': ''}])
+
+ def test_single_char_field_or_value(self):
+ ar = options.cli_to_array('m<=34;large.thing>s;x!=y')
+ self.assertEqual([{'field': 'm',
+ 'op': 'le',
+ 'value': '34',
+ 'type': ''},
+ {'field': 'large.thing',
+ 'op': 'gt',
+ 'value': 's',
+ 'type': ''},
+ {'field': 'x',
+ 'op': 'ne',
+ 'value': 'y',
+ 'type': ''}],
+ ar)
+
+ 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'}])
+
+ def test_missing_key(self):
+ self.assertRaises(ValueError, options.cli_to_array,
+ 'average=float::1234.0;>=string::hello')
+
+ def test_missing_value(self):
+ self.assertRaises(ValueError, options.cli_to_array,
+ 'average=float::1234.0;house>=')
+
+ def test_timestamp_value(self):
+ ar = options.cli_to_array(
+ 'project=cow;timestamp>=datetime::2014-03-11T16:02:58'
+ )
+ self.assertEqual([{'field': 'project',
+ 'op': 'eq',
+ 'type': '',
+ 'value': 'cow'},
+ {'field': 'timestamp',
+ 'op': 'ge',
+ 'type': 'datetime',
+ 'value': '2014-03-11T16:02:58'}],
+ ar)
diff --git a/ceilometerclient/tests/unit/v2/test_query_alarm_history.py b/ceilometerclient/tests/unit/v2/test_query_alarm_history.py
new file mode 100644
index 0000000..014b894
--- /dev/null
+++ b/ceilometerclient/tests/unit/v2/test_query_alarm_history.py
@@ -0,0 +1,66 @@
+# Copyright Ericsson AB 2014. All rights reserved
+#
+# Authors: Ildiko Vancsa <ildiko.vancsa@ericsson.com>
+#
+# 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.openstack.common.apiclient import client
+from ceilometerclient.openstack.common.apiclient import fake_client
+from ceilometerclient.tests.unit import utils
+from ceilometerclient.v2 import query
+
+
+ALARMCHANGE = {"alarm_id": "e8ff32f772a44a478182c3fe1f7cad6a",
+ "event_id": "c74a8611-6553-4764-a860-c15a6aabb5d0",
+ "detail": "{\"threshold\": 42.0, \"evaluation_periods\": 4}",
+ "on_behalf_of": "92159030020611e3b26dde429e99ee8c",
+ "project_id": "b6f16144010811e387e4de429e99ee8c",
+ "timestamp": "2014-03-11T16:02:58.376261",
+ "type": "rule change",
+ "user_id": "3e5d11fda79448ac99ccefb20be187ca"
+ }
+
+QUERY = {"filter": {"and": [{">": {"timestamp": "2014-03-11T16:02:58"}},
+ {"=": {"type": "rule change"}}]},
+ "orderby": [{"timestamp": "desc"}],
+ "limit": 10}
+
+base_url = '/v2/query/alarms/history'
+fixtures = {
+ base_url:
+ {
+ 'POST': (
+ {},
+ [ALARMCHANGE],
+ ),
+ },
+}
+
+
+class QueryAlarmsManagerTest(utils.BaseTestCase):
+
+ def setUp(self):
+ super(QueryAlarmsManagerTest, self).setUp()
+ self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
+ self.api = client.BaseClient(self.http_client)
+ self.mgr = query.QueryAlarmHistoryManager(self.api)
+
+ def test_query(self):
+ alarm_history = self.mgr.query(**QUERY)
+ expect = [
+
+ 'POST', '/v2/query/alarms/history', QUERY,
+
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(1, len(alarm_history))
diff --git a/ceilometerclient/tests/unit/v2/test_query_alarms.py b/ceilometerclient/tests/unit/v2/test_query_alarms.py
new file mode 100644
index 0000000..e633938
--- /dev/null
+++ b/ceilometerclient/tests/unit/v2/test_query_alarms.py
@@ -0,0 +1,74 @@
+# Copyright Ericsson AB 2014. 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
+# 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.openstack.common.apiclient import client
+from ceilometerclient.openstack.common.apiclient import fake_client
+from ceilometerclient.tests.unit import utils
+from ceilometerclient.v2 import query
+
+
+ALARM = {"alarm_actions": ["http://site:8000/alarm"],
+ "alarm_id": None,
+ "combination_rule": {
+ "alarm_ids": [
+ "739e99cb-c2ec-4718-b900-332502355f38",
+ "153462d0-a9b8-4b5b-8175-9e4b05e9b856"],
+ "operator": "or"},
+ "description": "An alarm",
+ "enabled": True,
+ "insufficient_data_actions": ["http://site:8000/nodata"],
+ "name": "SwiftObjectAlarm",
+ "ok_actions": ["http://site:8000/ok"],
+ "project_id": "c96c887c216949acbdfbd8b494863567",
+ "repeat_actions": False,
+ "state": "ok",
+ "state_timestamp": "2014-02-20T10:37:15.589860",
+ "threshold_rule": None,
+ "timestamp": "2014-02-20T10:37:15.589856",
+ "type": "combination",
+ "user_id": "c96c887c216949acbdfbd8b494863567"}
+
+QUERY = {"filter": {"and": [{"!=": {"state": "ok"}},
+ {"=": {"type": "combination"}}]},
+ "orderby": [{"state_timestamp": "desc"}],
+ "limit": 10}
+
+base_url = '/v2/query/alarms'
+fixtures = {
+ base_url:
+ {
+ 'POST': (
+ {},
+ [ALARM],
+ ),
+ },
+}
+
+
+class QueryAlarmsManagerTest(utils.BaseTestCase):
+
+ def setUp(self):
+ super(QueryAlarmsManagerTest, self).setUp()
+ self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
+ self.api = client.BaseClient(self.http_client)
+ self.mgr = query.QueryAlarmsManager(self.api)
+
+ def test_query(self):
+ alarms = self.mgr.query(**QUERY)
+ expect = [
+ 'POST', '/v2/query/alarms', QUERY,
+ ]
+
+ self.http_client.assert_called(*expect)
+ self.assertEqual(1, len(alarms))
diff --git a/ceilometerclient/tests/unit/v2/test_query_samples.py b/ceilometerclient/tests/unit/v2/test_query_samples.py
new file mode 100644
index 0000000..16828fe
--- /dev/null
+++ b/ceilometerclient/tests/unit/v2/test_query_samples.py
@@ -0,0 +1,67 @@
+# Copyright Ericsson AB 2014. 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
+# 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.openstack.common.apiclient import client
+from ceilometerclient.openstack.common.apiclient import fake_client
+from ceilometerclient.tests.unit import utils
+from ceilometerclient.v2 import query
+
+
+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.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
+ self.api = client.BaseClient(self.http_client)
+ self.mgr = query.QuerySamplesManager(self.api)
+
+ def test_query(self):
+ samples = self.mgr.query(**QUERY)
+ expect = [
+
+ 'POST', '/v2/query/samples', QUERY,
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(1, len(samples))
diff --git a/ceilometerclient/tests/unit/v2/test_resources.py b/ceilometerclient/tests/unit/v2/test_resources.py
new file mode 100644
index 0000000..4aa9131
--- /dev/null
+++ b/ceilometerclient/tests/unit/v2/test_resources.py
@@ -0,0 +1,118 @@
+# Copyright 2012 OpenStack Foundation
+# 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
+# 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.openstack.common.apiclient import client
+from ceilometerclient.openstack.common.apiclient import fake_client
+from ceilometerclient.tests.unit import utils
+import ceilometerclient.v2.resources
+
+
+fixtures = {
+ '/v2/resources': {
+ 'GET': (
+ {},
+ [
+ {
+ 'resource_id': 'a',
+ 'project_id': 'project_bla',
+ 'user_id': 'freddy',
+ 'metadata': {'zxc_id': 'bla'},
+ },
+ {
+ 'resource_id': 'b',
+ 'project_id': 'dig_the_ditch',
+ 'user_id': 'joey',
+ 'metadata': {'zxc_id': 'foo'},
+ },
+ ]
+ ),
+ },
+ '/v2/resources?q.field=resource_id&q.op=&q.type=&q.value=a':
+ {
+ 'GET': (
+ {},
+ [
+ {
+ 'resource_id': 'a',
+ 'project_id': 'project_bla',
+ 'user_id': 'freddy',
+ 'metadata': {'zxc_id': 'bla'},
+ },
+ ]
+ ),
+ },
+ '/v2/resources/a':
+ {
+ 'GET': (
+ {},
+ {
+ 'resource_id': 'a',
+ 'project_id': 'project_bla',
+ 'user_id': 'freddy',
+ 'metadata': {'zxc_id': 'bla'},
+ },
+ ),
+ },
+}
+
+
+class ResourceManagerTest(utils.BaseTestCase):
+
+ def setUp(self):
+ super(ResourceManagerTest, self).setUp()
+ self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
+ self.api = client.BaseClient(self.http_client)
+ self.mgr = ceilometerclient.v2.resources.ResourceManager(self.api)
+
+ def test_list_all(self):
+ resources = list(self.mgr.list())
+ expect = [
+ 'GET', '/v2/resources'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(resources), 2)
+ self.assertEqual(resources[0].resource_id, 'a')
+ self.assertEqual(resources[1].resource_id, 'b')
+
+ def test_list_one(self):
+ resource = self.mgr.get(resource_id='a')
+ expect = [
+ 'GET', '/v2/resources/a'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertIsNotNone(resource)
+ self.assertEqual(resource.resource_id, 'a')
+
+ def test_list_by_query(self):
+ resources = list(self.mgr.list(q=[{"field": "resource_id",
+ "value": "a"},
+ ]))
+ expect = [
+ 'GET', '/v2/resources?q.field=resource_id&q.op='
+ '&q.type=&q.value=a'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(resources), 1)
+ self.assertEqual(resources[0].resource_id, 'a')
+
+ def test_get_from_resource_class(self):
+ resource = self.mgr.get(resource_id='a')
+ self.assertIsNotNone(resource)
+ resource.get()
+ expect = [
+ 'GET', '/v2/resources/a'
+ ]
+ self.http_client.assert_called(*expect, pos=0)
+ self.http_client.assert_called(*expect, pos=1)
+ self.assertEqual('a', resource.resource_id)
diff --git a/ceilometerclient/tests/unit/v2/test_samples.py b/ceilometerclient/tests/unit/v2/test_samples.py
new file mode 100644
index 0000000..1d42fb4
--- /dev/null
+++ b/ceilometerclient/tests/unit/v2/test_samples.py
@@ -0,0 +1,198 @@
+# Copyright 2012 OpenStack Foundation
+# 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
+# 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 copy
+
+from ceilometerclient.openstack.common.apiclient import client
+from ceilometerclient.openstack.common.apiclient import fake_client
+from ceilometerclient.tests.unit import utils
+import ceilometerclient.v2.samples
+
+GET_OLD_SAMPLE = {u'counter_name': u'instance',
+ u'user_id': u'user-id',
+ u'resource_id': u'resource-id',
+ u'timestamp': u'2012-07-02T10:40:00',
+ u'source': u'test_source',
+ u'message_id': u'54558a1c-6ef3-11e2-9875-5453ed1bbb5f',
+ u'counter_unit': u'',
+ u'counter_volume': 1.0,
+ u'project_id': u'project1',
+ u'resource_metadata': {u'tag': u'self.counter',
+ u'display_name': u'test-server'},
+ u'counter_type': u'cumulative'}
+CREATE_SAMPLE = copy.deepcopy(GET_OLD_SAMPLE)
+del CREATE_SAMPLE['message_id']
+del CREATE_SAMPLE['source']
+
+GET_SAMPLE = {
+ "user_id": None,
+ "resource_id": "9b651dfd-7d30-402b-972e-212b2c4bfb05",
+ "timestamp": "2014-11-03T13:37:46",
+ "meter": "image",
+ "volume": 1.0,
+ "source": "openstack",
+ "recorded_at": "2014-11-03T13:37:46.994458",
+ "project_id": "2cc3a7bb859b4bacbeab0aa9ca673033",
+ "type": "gauge",
+ "id": "98b5f258-635e-11e4-8bdd-0025647390c1",
+ "unit": "image",
+ "resource_metadata": {},
+}
+
+METER_URL = '/v2/meters/instance'
+SAMPLE_URL = '/v2/samples'
+QUERIES = ('q.field=resource_id&q.field=source&q.op=&q.op='
+ '&q.type=&q.type=&q.value=foo&q.value=bar')
+LIMIT = 'limit=1'
+
+OLD_SAMPLE_FIXTURES = {
+ METER_URL: {
+ 'GET': (
+ {},
+ [GET_OLD_SAMPLE]
+ ),
+ 'POST': (
+ {},
+ [CREATE_SAMPLE],
+ ),
+ },
+ '%s?%s' % (METER_URL, QUERIES): {
+ 'GET': (
+ {},
+ [],
+ ),
+ },
+ '%s?%s' % (METER_URL, LIMIT): {
+ 'GET': (
+ {},
+ [GET_OLD_SAMPLE]
+ ),
+ }
+}
+SAMPLE_FIXTURES = {
+ SAMPLE_URL: {
+ 'GET': (
+ (),
+ [GET_SAMPLE]
+ ),
+ },
+ '%s?%s' % (SAMPLE_URL, QUERIES): {
+ 'GET': (
+ {},
+ [],
+ ),
+ },
+ '%s?%s' % (SAMPLE_URL, LIMIT): {
+ 'GET': (
+ {},
+ [GET_SAMPLE],
+ ),
+ },
+ '%s/%s' % (SAMPLE_URL, GET_SAMPLE['id']): {
+ 'GET': (
+ {},
+ GET_SAMPLE,
+ ),
+ },
+}
+
+
+class OldSampleManagerTest(utils.BaseTestCase):
+
+ def setUp(self):
+ super(OldSampleManagerTest, self).setUp()
+ self.http_client = fake_client.FakeHTTPClient(
+ fixtures=OLD_SAMPLE_FIXTURES)
+ self.api = client.BaseClient(self.http_client)
+ self.mgr = ceilometerclient.v2.samples.OldSampleManager(self.api)
+
+ def test_list_by_meter_name(self):
+ samples = list(self.mgr.list(meter_name='instance'))
+ expect = [
+ 'GET', '/v2/meters/instance'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(samples), 1)
+ self.assertEqual(samples[0].resource_id, 'resource-id')
+
+ def test_list_by_meter_name_extended(self):
+ samples = list(self.mgr.list(meter_name='instance',
+ q=[
+ {"field": "resource_id",
+ "value": "foo"},
+ {"field": "source",
+ "value": "bar"},
+ ]))
+ expect = ['GET', '%s?%s' % (METER_URL, QUERIES)]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(samples), 0)
+
+ def test_create(self):
+ sample = self.mgr.create(**CREATE_SAMPLE)
+ expect = [
+ 'POST', '/v2/meters/instance'
+ ]
+ self.http_client.assert_called(*expect, body=[CREATE_SAMPLE])
+ self.assertIsNotNone(sample)
+
+ def test_limit(self):
+ samples = list(self.mgr.list(meter_name='instance', limit=1))
+ expect = ['GET', '/v2/meters/instance?limit=1']
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(samples), 1)
+
+
+class SampleManagerTest(utils.BaseTestCase):
+
+ def setUp(self):
+ super(SampleManagerTest, self).setUp()
+ self.http_client = fake_client.FakeHTTPClient(
+ fixtures=SAMPLE_FIXTURES)
+ self.api = client.BaseClient(self.http_client)
+ self.mgr = ceilometerclient.v2.samples.SampleManager(self.api)
+
+ def test_sample_list(self):
+ samples = list(self.mgr.list())
+ expect = [
+ 'GET', '/v2/samples'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(1, len(samples))
+ self.assertEqual('9b651dfd-7d30-402b-972e-212b2c4bfb05',
+ samples[0].resource_id)
+
+ def test_sample_list_with_queries(self):
+ queries = [
+ {"field": "resource_id",
+ "value": "foo"},
+ {"field": "source",
+ "value": "bar"},
+ ]
+ samples = list(self.mgr.list(q=queries))
+ expect = ['GET', '%s?%s' % (SAMPLE_URL, QUERIES)]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(0, len(samples))
+
+ def test_sample_list_with_limit(self):
+ samples = list(self.mgr.list(limit=1))
+ expect = ['GET', '/v2/samples?limit=1']
+ self.http_client.assert_called(*expect)
+ self.assertEqual(1, len(samples))
+
+ def test_sample_get(self):
+ sample = self.mgr.get(GET_SAMPLE['id'])
+ expect = ['GET', '/v2/samples/' + GET_SAMPLE['id']]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(GET_SAMPLE, sample.to_dict())
diff --git a/ceilometerclient/tests/unit/v2/test_shell.py b/ceilometerclient/tests/unit/v2/test_shell.py
new file mode 100644
index 0000000..a4b9f33
--- /dev/null
+++ b/ceilometerclient/tests/unit/v2/test_shell.py
@@ -0,0 +1,1175 @@
+# Copyright Ericsson AB 2014. All rights reserved
+#
+# Authors: Balazs Gibizer <balazs.gibizer@ericsson.com>
+# Ildiko Vancsa <ildiko.vancsa@ericsson.com>
+#
+# 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 sys
+
+import mock
+import six
+from testtools import matchers
+
+from ceilometerclient import exc
+from ceilometerclient import shell as base_shell
+from ceilometerclient.tests.unit import test_shell
+from ceilometerclient.tests.unit import utils
+from ceilometerclient.v2 import alarms
+from ceilometerclient.v2 import events
+from ceilometerclient.v2 import samples
+from ceilometerclient.v2 import shell as ceilometer_shell
+from ceilometerclient.v2 import statistics
+
+
+class ShellAlarmStateCommandsTest(utils.BaseTestCase):
+
+ ALARM_ID = 'foobar'
+
+ def setUp(self):
+ super(ShellAlarmStateCommandsTest, self).setUp()
+ self.cc = mock.Mock()
+ self.cc.alarms = mock.Mock()
+ self.args = mock.Mock()
+ self.args.alarm_id = self.ALARM_ID
+
+ def test_alarm_state_get(self):
+ ceilometer_shell.do_alarm_state_get(self.cc, self.args)
+ self.cc.alarms.get_state.assert_called_once_with(self.ALARM_ID)
+ self.assertFalse(self.cc.alarms.set_state.called)
+
+ def test_alarm_state_set(self):
+ self.args.state = 'ok'
+ ceilometer_shell.do_alarm_state_set(self.cc, self.args)
+ self.cc.alarms.set_state.assert_called_once_with(self.ALARM_ID, 'ok')
+ self.assertFalse(self.cc.alarms.get_state.called)
+
+
+class ShellAlarmHistoryCommandTest(utils.BaseTestCase):
+
+ ALARM_ID = '768ff714-8cfb-4db9-9753-d484cb33a1cc'
+ FULL_DETAIL = ('{"alarm_actions": [], '
+ '"user_id": "8185aa72421a4fd396d4122cba50e1b5", '
+ '"name": "scombo", '
+ '"timestamp": "2013-10-03T08:58:33.647912", '
+ '"enabled": true, '
+ '"state_timestamp": "2013-10-03T08:58:33.647912", '
+ '"rule": {"operator": "or", "alarm_ids": '
+ '["062cc907-3a9f-4867-ab3b-fa83212b39f7"]}, '
+ '"alarm_id": "768ff714-8cfb-4db9-9753-d484cb33a1cc", '
+ '"state": "insufficient data", '
+ '"insufficient_data_actions": [], '
+ '"repeat_actions": false, '
+ '"ok_actions": [], '
+ '"project_id": "57d04f24d0824b78b1ea9bcecedbda8f", '
+ '"type": "combination", '
+ '"description": "Combined state of alarms '
+ '062cc907-3a9f-4867-ab3b-fa83212b39f7"}')
+ ALARM_HISTORY = [{'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f',
+ 'user_id': '8185aa72421a4fd396d4122cba50e1b5',
+ 'event_id': 'c74a8611-6553-4764-a860-c15a6aabb5d0',
+ 'timestamp': '2013-10-03T08:59:28.326000',
+ 'detail': '{"state": "alarm"}',
+ 'alarm_id': '768ff714-8cfb-4db9-9753-d484cb33a1cc',
+ 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f',
+ 'type': 'state transition'},
+ {'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f',
+ 'user_id': '8185aa72421a4fd396d4122cba50e1b5',
+ 'event_id': 'c74a8611-6553-4764-a860-c15a6aabb5d0',
+ 'timestamp': '2013-10-03T08:59:28.326000',
+ 'detail': '{"description": "combination of one"}',
+ 'alarm_id': '768ff714-8cfb-4db9-9753-d484cb33a1cc',
+ 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f',
+ 'type': 'rule change'},
+ {'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f',
+ 'user_id': '8185aa72421a4fd396d4122cba50e1b5',
+ 'event_id': '4fd7df9e-190d-4471-8884-dc5a33d5d4bb',
+ 'timestamp': '2013-10-03T08:58:33.647000',
+ 'detail': FULL_DETAIL,
+ 'alarm_id': '768ff714-8cfb-4db9-9753-d484cb33a1cc',
+ 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f',
+ 'type': 'creation'}]
+ TIMESTAMP_RE = (' +\| (\d{4})-(\d{2})-(\d{2})T'
+ '(\d{2})\:(\d{2})\:(\d{2})\.(\d{6}) \| +')
+
+ def setUp(self):
+ super(ShellAlarmHistoryCommandTest, self).setUp()
+ self.cc = mock.Mock()
+ self.cc.alarms = mock.Mock()
+ self.args = mock.Mock()
+ self.args.alarm_id = self.ALARM_ID
+
+ @mock.patch('sys.stdout', new=six.StringIO())
+ def _do_test_alarm_history(self, raw_query=None, parsed_query=None):
+ self.args.query = raw_query
+ history = [alarms.AlarmChange(mock.Mock(), change)
+ for change in self.ALARM_HISTORY]
+ self.cc.alarms.get_history.return_value = history
+
+ ceilometer_shell.do_alarm_history(self.cc, self.args)
+ self.cc.alarms.get_history.assert_called_once_with(
+ q=parsed_query,
+ alarm_id=self.ALARM_ID
+ )
+ out = sys.stdout.getvalue()
+ required = [
+ '.*creation%sname: scombo.*' % self.TIMESTAMP_RE,
+ '.*rule change%sdescription: combination of one.*' %
+ self.TIMESTAMP_RE,
+ '.*state transition%sstate: alarm.*' % self.TIMESTAMP_RE,
+ ]
+ for r in required:
+ self.assertThat(out, matchers.MatchesRegex(r, re.DOTALL))
+
+ def test_alarm_all_history(self):
+ self._do_test_alarm_history()
+
+ def test_alarm_constrained_history(self):
+ parsed_query = [dict(field='timestamp',
+ value='2013-10-03T08:59:28',
+ op='gt',
+ type='')]
+ self._do_test_alarm_history(raw_query='timestamp>2013-10-03T08:59:28',
+ parsed_query=parsed_query)
+
+
+class ShellAlarmCommandTest(utils.BaseTestCase):
+
+ ALARM_ID = '768ff714-8cfb-4db9-9753-d484cb33a1cc'
+ ALARM = {"alarm_actions": ["log://"],
+ "ok_actions": [],
+ "description": "instance running hot",
+ "timestamp": "2013-11-20T10:38:42.206952",
+ "enabled": True,
+ "state_timestamp": "2013-11-19T17:20:44",
+ "threshold_rule": {"meter_name": "cpu_util",
+ "evaluation_periods": 3,
+ "period": 600,
+ "statistic": "avg",
+ "threshold": 99.0,
+ "query": [{"field": "resource_id",
+ "value": "INSTANCE_ID",
+ "op": "eq"}],
+ "comparison_operator": "gt"},
+ "time_constraints": [{"name": "cons1",
+ "description": "desc1",
+ "start": "0 11 * * *",
+ "duration": 300,
+ "timezone": ""},
+ {"name": "cons2",
+ "description": "desc2",
+ "start": "0 23 * * *",
+ "duration": 600,
+ "timezone": ""}],
+ "alarm_id": ALARM_ID,
+ "state": "insufficient data",
+ "severity": "low",
+ "insufficient_data_actions": [],
+ "repeat_actions": True,
+ "user_id": "528d9b68fa774689834b5c04b4564f8a",
+ "project_id": "ed9d4e2be2a748bc80108053cf4598f5",
+ "type": "threshold",
+ "name": "cpu_high"}
+
+ THRESHOLD_ALARM_CLI_ARGS = [
+ '--name', 'cpu_high',
+ '--description', 'instance running hot',
+ '--meter-name', 'cpu_util',
+ '--threshold', '70.0',
+ '--comparison-operator', 'gt',
+ '--statistic', 'avg',
+ '--period', '600',
+ '--evaluation-periods', '3',
+ '--alarm-action', 'log://',
+ '--alarm-action', 'http://example.com/alarm/state',
+ '--query', 'resource_id=INSTANCE_ID'
+ ]
+
+ def setUp(self):
+ super(ShellAlarmCommandTest, self).setUp()
+ self.cc = mock.Mock()
+ self.cc.alarms = mock.Mock()
+ self.args = mock.Mock()
+ self.args.alarm_id = self.ALARM_ID
+
+ @mock.patch('sys.stdout', new=six.StringIO())
+ def _do_test_alarm_update_repeat_actions(self, method, repeat_actions):
+ self.args.threshold = 42.0
+ if repeat_actions is not None:
+ self.args.repeat_actions = repeat_actions
+ alarm = [alarms.Alarm(mock.Mock(), self.ALARM)]
+ self.cc.alarms.get.return_value = alarm
+ self.cc.alarms.update.return_value = alarm[0]
+
+ method(self.cc, self.args)
+ args, kwargs = self.cc.alarms.update.call_args
+ self.assertEqual(self.ALARM_ID, args[0])
+ self.assertEqual(42.0, kwargs.get('threshold'))
+ if repeat_actions is not None:
+ self.assertEqual(repeat_actions, kwargs.get('repeat_actions'))
+ else:
+ self.assertNotIn('repeat_actions', kwargs)
+
+ def test_alarm_update_repeat_actions_untouched(self):
+ method = ceilometer_shell.do_alarm_update
+ self._do_test_alarm_update_repeat_actions(method, None)
+
+ def test_alarm_update_repeat_actions_set(self):
+ method = ceilometer_shell.do_alarm_update
+ self._do_test_alarm_update_repeat_actions(method, True)
+
+ def test_alarm_update_repeat_actions_clear(self):
+ method = ceilometer_shell.do_alarm_update
+ self._do_test_alarm_update_repeat_actions(method, False)
+
+ def test_alarm_combination_update_repeat_actions_untouched(self):
+ method = ceilometer_shell.do_alarm_combination_update
+ self._do_test_alarm_update_repeat_actions(method, None)
+
+ def test_alarm_combination_update_repeat_actions_set(self):
+ method = ceilometer_shell.do_alarm_combination_update
+ self._do_test_alarm_update_repeat_actions(method, True)
+
+ def test_alarm_combination_update_repeat_actions_clear(self):
+ method = ceilometer_shell.do_alarm_combination_update
+ self._do_test_alarm_update_repeat_actions(method, False)
+
+ def test_alarm_threshold_update_repeat_actions_untouched(self):
+ method = ceilometer_shell.do_alarm_threshold_update
+ self._do_test_alarm_update_repeat_actions(method, None)
+
+ def test_alarm_threshold_update_repeat_actions_set(self):
+ method = ceilometer_shell.do_alarm_threshold_update
+ self._do_test_alarm_update_repeat_actions(method, True)
+
+ def test_alarm_threshold_update_repeat_actions_clear(self):
+ method = ceilometer_shell.do_alarm_threshold_update
+ self._do_test_alarm_update_repeat_actions(method, False)
+
+ @mock.patch('sys.stdout', new=six.StringIO())
+ def test_alarm_threshold_create_args(self):
+ argv = ['alarm-threshold-create'] + self.THRESHOLD_ALARM_CLI_ARGS
+ self._test_alarm_threshold_action_args('create', argv)
+
+ def test_alarm_threshold_update_args(self):
+ argv = ['alarm-threshold-update', 'x'] + self.THRESHOLD_ALARM_CLI_ARGS
+ self._test_alarm_threshold_action_args('update', argv)
+
+ @mock.patch('sys.stdout', new=six.StringIO())
+ def _test_alarm_threshold_action_args(self, action, argv):
+ shell = base_shell.CeilometerShell()
+ _, args = shell.parse_args(argv)
+
+ alarm = alarms.Alarm(mock.Mock(), self.ALARM)
+ getattr(self.cc.alarms, action).return_value = alarm
+
+ func = getattr(ceilometer_shell, 'do_alarm_threshold_' + action)
+ func(self.cc, args)
+ _, kwargs = getattr(self.cc.alarms, action).call_args
+ self._check_alarm_threshold_args(kwargs)
+
+ def _check_alarm_threshold_args(self, kwargs):
+ self.assertEqual('cpu_high', kwargs.get('name'))
+ self.assertEqual('instance running hot', kwargs.get('description'))
+ actions = ['log://', 'http://example.com/alarm/state']
+ self.assertEqual(actions, kwargs.get('alarm_actions'))
+ self.assertIn('threshold_rule', kwargs)
+ rule = kwargs['threshold_rule']
+ self.assertEqual('cpu_util', rule.get('meter_name'))
+ self.assertEqual(70.0, rule.get('threshold'))
+ self.assertEqual('gt', rule.get('comparison_operator'))
+ self.assertEqual('avg', rule.get('statistic'))
+ self.assertEqual(600, rule.get('period'))
+ self.assertEqual(3, rule.get('evaluation_periods'))
+ query = dict(field='resource_id', type='',
+ value='INSTANCE_ID', op='eq')
+ self.assertEqual([query], rule['query'])
+
+ @mock.patch('sys.stdout', new=six.StringIO())
+ def test_alarm_create_time_constraints(self):
+ shell = base_shell.CeilometerShell()
+ argv = ['alarm-threshold-create',
+ '--name', 'cpu_high',
+ '--meter-name', 'cpu_util',
+ '--threshold', '70.0',
+ '--time-constraint',
+ 'name=cons1;start="0 11 * * *";duration=300',
+ '--time-constraint',
+ 'name=cons2;start="0 23 * * *";duration=600',
+ ]
+ _, args = shell.parse_args(argv)
+
+ alarm = alarms.Alarm(mock.Mock(), self.ALARM)
+ self.cc.alarms.create.return_value = alarm
+
+ ceilometer_shell.do_alarm_threshold_create(self.cc, args)
+ _, kwargs = self.cc.alarms.create.call_args
+ time_constraints = [dict(name='cons1', start='0 11 * * *',
+ duration='300'),
+ dict(name='cons2', start='0 23 * * *',
+ duration='600')]
+ self.assertEqual(time_constraints, kwargs['time_constraints'])
+
+
+class ShellSampleListCommandTest(utils.BaseTestCase):
+
+ METER = 'cpu_util'
+ SAMPLE_VALUES = (
+ ("cpu_util",
+ "5dcf5537-3161-4e25-9235-407e1385bd35",
+ "2013-10-15T05:50:30",
+ "%",
+ 0.261666666667,
+ "gauge",
+ "86536501-b2c9-48f6-9c6a-7a5b14ba7482"),
+ ("cpu_util",
+ "87d197e9-9cf6-4c25-bc66-1b1f4cedb52f",
+ "2013-10-15T05:50:29",
+ "%",
+ 0.261666666667,
+ "gauge",
+ "fe2a91ec-602b-4b55-8cba-5302ce3b916e",),
+ ("cpu_util",
+ "5dcf5537-3161-4e25-9235-407e1385bd35",
+ "2013-10-15T05:40:30",
+ "%",
+ 0.251247920133,
+ "gauge",
+ "52768bcb-b4e9-4db9-a30c-738c758b6f43"),
+ ("cpu_util",
+ "87d197e9-9cf6-4c25-bc66-1b1f4cedb52f",
+ "2013-10-15T05:40:29",
+ "%",
+ 0.26,
+ "gauge",
+ "31ae614a-ac6b-4fb9-b106-4667bae03308"),
+ )
+
+ OLD_SAMPLES = [
+ dict(counter_name=s[0],
+ resource_id=s[1],
+ timestamp=s[2],
+ counter_unit=s[3],
+ counter_volume=s[4],
+ counter_type=s[5])
+ for s in SAMPLE_VALUES
+ ]
+
+ SAMPLES = [
+ dict(meter=s[0],
+ resource_id=s[1],
+ timestamp=s[2],
+ unit=s[3],
+ volume=s[4],
+ type=s[5],
+ id=s[6])
+ for s in SAMPLE_VALUES
+ ]
+
+ def setUp(self):
+ super(ShellSampleListCommandTest, self).setUp()
+ self.cc = mock.Mock()
+ self.cc.samples = mock.Mock()
+ self.cc.new_samples = mock.Mock()
+ self.args = mock.Mock()
+ self.args.query = None
+ self.args.limit = None
+
+ @mock.patch('sys.stdout', new=six.StringIO())
+ def test_old_sample_list(self):
+ self.args.meter = self.METER
+ sample_list = [samples.OldSample(mock.Mock(), sample)
+ for sample in self.OLD_SAMPLES]
+ self.cc.samples.list.return_value = sample_list
+
+ ceilometer_shell.do_sample_list(self.cc, self.args)
+ self.cc.samples.list.assert_called_once_with(
+ meter_name=self.METER,
+ q=None,
+ limit=None)
+
+ self.assertEqual('''\
++--------------------------------------+----------+-------+----------------\
++------+---------------------+
+| Resource ID | Name | Type | Volume \
+| Unit | Timestamp |
++--------------------------------------+----------+-------+----------------\
++------+---------------------+
+| 5dcf5537-3161-4e25-9235-407e1385bd35 | cpu_util | gauge | 0.261666666667 \
+| % | 2013-10-15T05:50:30 |
+| 87d197e9-9cf6-4c25-bc66-1b1f4cedb52f | cpu_util | gauge | 0.261666666667 \
+| % | 2013-10-15T05:50:29 |
+| 5dcf5537-3161-4e25-9235-407e1385bd35 | cpu_util | gauge | 0.251247920133 \
+| % | 2013-10-15T05:40:30 |
+| 87d197e9-9cf6-4c25-bc66-1b1f4cedb52f | cpu_util | gauge | 0.26 \
+| % | 2013-10-15T05:40:29 |
++--------------------------------------+----------+-------+----------------\
++------+---------------------+
+''', sys.stdout.getvalue())
+
+ @mock.patch('sys.stdout', new=six.StringIO())
+ def test_sample_list(self):
+ self.args.meter = None
+ sample_list = [samples.Sample(mock.Mock(), sample)
+ for sample in self.SAMPLES]
+ self.cc.new_samples.list.return_value = sample_list
+
+ ceilometer_shell.do_sample_list(self.cc, self.args)
+ self.cc.new_samples.list.assert_called_once_with(
+ q=None,
+ limit=None)
+
+ self.assertEqual('''\
++--------------------------------------+--------------------------------------\
++----------+-------+----------------+------+---------------------+
+| ID | Resource ID \
+| Name | Type | Volume | Unit | Timestamp |
++--------------------------------------+--------------------------------------\
++----------+-------+----------------+------+---------------------+
+| 86536501-b2c9-48f6-9c6a-7a5b14ba7482 | 5dcf5537-3161-4e25-9235-407e1385bd35 \
+| cpu_util | gauge | 0.261666666667 | % | 2013-10-15T05:50:30 |
+| fe2a91ec-602b-4b55-8cba-5302ce3b916e | 87d197e9-9cf6-4c25-bc66-1b1f4cedb52f \
+| cpu_util | gauge | 0.261666666667 | % | 2013-10-15T05:50:29 |
+| 52768bcb-b4e9-4db9-a30c-738c758b6f43 | 5dcf5537-3161-4e25-9235-407e1385bd35 \
+| cpu_util | gauge | 0.251247920133 | % | 2013-10-15T05:40:30 |
+| 31ae614a-ac6b-4fb9-b106-4667bae03308 | 87d197e9-9cf6-4c25-bc66-1b1f4cedb52f \
+| cpu_util | gauge | 0.26 | % | 2013-10-15T05:40:29 |
++--------------------------------------+--------------------------------------\
++----------+-------+----------------+------+---------------------+
+''', sys.stdout.getvalue())
+
+
+class ShellSampleShowCommandTest(utils.BaseTestCase):
+
+ SAMPLE = {
+ "user_id": None,
+ "resource_id": "9b651dfd-7d30-402b-972e-212b2c4bfb05",
+ "timestamp": "2014-11-03T13:37:46",
+ "meter": "image",
+ "volume": 1.0,
+ "source": "openstack",
+ "recorded_at": "2014-11-03T13:37:46.994458",
+ "project_id": "2cc3a7bb859b4bacbeab0aa9ca673033",
+ "type": "gauge",
+ "id": "98b5f258-635e-11e4-8bdd-0025647390c1",
+ "unit": "image",
+ "metadata": {
+ "name": "cirros-0.3.2-x86_64-uec",
+ }
+ }
+
+ def setUp(self):
+ super(ShellSampleShowCommandTest, self).setUp()
+ self.cc = mock.Mock()
+ self.cc.new_samples = mock.Mock()
+ self.args = mock.Mock()
+ self.args.sample_id = "98b5f258-635e-11e4-8bdd-0025647390c1"
+
+ @mock.patch('sys.stdout', new=six.StringIO())
+ def test_sample_show(self):
+ sample = samples.Sample(mock.Mock(), self.SAMPLE)
+ self.cc.new_samples.get.return_value = sample
+
+ ceilometer_shell.do_sample_show(self.cc, self.args)
+ self.cc.new_samples.get.assert_called_once_with(
+ "98b5f258-635e-11e4-8bdd-0025647390c1")
+
+ self.assertEqual('''\
++-------------+--------------------------------------+
+| Property | Value |
++-------------+--------------------------------------+
+| id | 98b5f258-635e-11e4-8bdd-0025647390c1 |
+| metadata | {"name": "cirros-0.3.2-x86_64-uec"} |
+| meter | image |
+| project_id | 2cc3a7bb859b4bacbeab0aa9ca673033 |
+| recorded_at | 2014-11-03T13:37:46.994458 |
+| resource_id | 9b651dfd-7d30-402b-972e-212b2c4bfb05 |
+| source | openstack |
+| timestamp | 2014-11-03T13:37:46 |
+| type | gauge |
+| unit | image |
+| user_id | None |
+| volume | 1.0 |
++-------------+--------------------------------------+
+''', sys.stdout.getvalue())
+
+
+class ShellSampleCreateCommandTest(utils.BaseTestCase):
+
+ METER = 'instance'
+ METER_TYPE = 'gauge'
+ RESOURCE_ID = '0564c64c-3545-4e34-abfb-9d18e5f2f2f9'
+ SAMPLE_VOLUME = '1'
+ METER_UNIT = 'instance'
+ SAMPLE = [{
+ u'counter_name': u'instance',
+ u'user_id': u'21b442b8101d407d8242b6610e0ed0eb',
+ u'resource_id': u'0564c64c-3545-4e34-abfb-9d18e5f2f2f9',
+ u'timestamp': u'2014-01-10T03: 05: 33.951170',
+ u'message_id': u'1247cbe6-79a4-11e3-a296-000c294c58e2',
+ u'source': u'384260c6987b451d8290e66e1f108082: openstack',
+ u'counter_unit': u'instance',
+ u'counter_volume': 1.0,
+ u'project_id': u'384260c6987b451d8290e66e1f108082',
+ u'resource_metadata': {},
+ u'counter_type': u'gauge'
+ }]
+
+ def setUp(self):
+ super(ShellSampleCreateCommandTest, self).setUp()
+ self.cc = mock.Mock()
+ self.cc.samples = mock.Mock()
+ self.args = mock.Mock()
+ self.args.meter_name = self.METER
+ self.args.meter_type = self.METER_TYPE
+ self.args.meter_unit = self.METER_UNIT
+ self.args.resource_id = self.RESOURCE_ID
+ self.args.sample_volume = self.SAMPLE_VOLUME
+
+ @mock.patch('sys.stdout', new=six.StringIO())
+ def test_sample_create(self):
+ ret_sample = [samples.OldSample(mock.Mock(), sample)
+ for sample in self.SAMPLE]
+ self.cc.samples.create.return_value = ret_sample
+
+ ceilometer_shell.do_sample_create(self.cc, self.args)
+
+ self.assertEqual('''\
++-------------------+---------------------------------------------+
+| Property | Value |
++-------------------+---------------------------------------------+
+| message_id | 1247cbe6-79a4-11e3-a296-000c294c58e2 |
+| name | instance |
+| project_id | 384260c6987b451d8290e66e1f108082 |
+| resource_id | 0564c64c-3545-4e34-abfb-9d18e5f2f2f9 |
+| resource_metadata | {} |
+| source | 384260c6987b451d8290e66e1f108082: openstack |
+| timestamp | 2014-01-10T03: 05: 33.951170 |
+| type | gauge |
+| unit | instance |
+| user_id | 21b442b8101d407d8242b6610e0ed0eb |
+| volume | 1.0 |
++-------------------+---------------------------------------------+
+''', sys.stdout.getvalue())
+
+
+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"]
+
+ @mock.patch('sys.stdout', new=six.StringIO())
+ 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
+
+ ceilometer_shell.do_query_samples(self.cc, self.args)
+
+ self.assertEqual('''\
++--------------------------------------+----------+-------+--------+---------\
+-+----------------------------+
+| Resource ID | Meter | Type | Volume | Unit \
+ | Timestamp |
++--------------------------------------+----------+-------+--------+---------\
+-+----------------------------+
+| bd9431c1-8d69-4ad3-803a-8d4a6b89fd36 | instance | gauge | 1 | instance\
+ | 2014-02-19T05:50:16.673604 |
++--------------------------------------+----------+-------+--------+---------\
+-+----------------------------+
+''', sys.stdout.getvalue())
+
+
+class ShellQueryAlarmsCommandTest(utils.BaseTestCase):
+
+ ALARM = [{"alarm_actions": ["http://site:8000/alarm"],
+ "alarm_id": "768ff714-8cfb-4db9-9753-d484cb33a1cc",
+ "combination_rule": {
+ "alarm_ids": [
+ "739e99cb-c2ec-4718-b900-332502355f38",
+ "153462d0-a9b8-4b5b-8175-9e4b05e9b856"],
+ "operator": "or"},
+ "description": "An alarm",
+ "enabled": True,
+ "insufficient_data_actions": ["http://site:8000/nodata"],
+ "name": "SwiftObjectAlarm",
+ "ok_actions": ["http://site:8000/ok"],
+ "project_id": "c96c887c216949acbdfbd8b494863567",
+ "repeat_actions": False,
+ "state": "ok",
+ "severity": "critical",
+ "state_timestamp": "2014-02-20T10:37:15.589860",
+ "threshold_rule": None,
+ "timestamp": "2014-02-20T10:37:15.589856",
+ "time_constraints": [{"name": "test", "start": "0 23 * * *",
+ "duration": 10800}],
+ "type": "combination",
+ "user_id": "c96c887c216949acbdfbd8b494863567"}]
+
+ QUERY = {"filter": {"and": [{"!=": {"state": "ok"}},
+ {"=": {"type": "combination"}}]},
+ "orderby": [{"state_timestamp": "desc"}],
+ "limit": 10}
+
+ def setUp(self):
+ super(ShellQueryAlarmsCommandTest, 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"]
+
+ @mock.patch('sys.stdout', new=six.StringIO())
+ def test_query(self):
+ ret_alarm = [alarms.Alarm(mock.Mock(), alarm)
+ for alarm in self.ALARM]
+ self.cc.query_alarms.query.return_value = ret_alarm
+
+ ceilometer_shell.do_query_alarms(self.cc, self.args)
+
+ self.assertEqual('''\
++--------------------------------------+------------------+-------+----------+\
+---------+------------+-------------------------------------------------------\
+-----------------------------------------------+-------------------------------\
+-+
+| Alarm ID | Name | State | Severity \
+| Enabled | Continuous | Alarm condition \
+ | Time constraints \
+ |
++--------------------------------------+------------------+-------+----------+\
+---------+------------+-------------------------------------------------------\
+-----------------------------------------------+--------------------------------+
+| 768ff714-8cfb-4db9-9753-d484cb33a1cc | SwiftObjectAlarm | ok | critical \
+| True | False | combinated states (OR) of \
+739e99cb-c2ec-4718-b900-332502355f38, 153462d0-a9b8-4b5b-8175-9e4b05e9b856 |\
+ test at 0 23 * * * for 10800s |
++--------------------------------------+------------------+-------+----------+\
+---------+------------+-------------------------------------------------------\
+-----------------------------------------------+------------------------------\
+--+
+''', sys.stdout.getvalue())
+
+ @mock.patch('sys.stdout', new=six.StringIO())
+ def test_time_constraints_compatibility(self):
+ # client should be backwards compatible
+ alarm_without_tc = dict(self.ALARM[0])
+ del alarm_without_tc['time_constraints']
+
+ # NOTE(nsaje): Since we're accessing a nonexisting key in the resource,
+ # the resource is looking it up with the manager (which is a mock).
+ manager_mock = mock.Mock()
+ del manager_mock.get
+ ret_alarm = [alarms.Alarm(manager_mock, alarm_without_tc)]
+ self.cc.query_alarms.query.return_value = ret_alarm
+
+ ceilometer_shell.do_query_alarms(self.cc, self.args)
+
+ self.assertEqual('''\
++--------------------------------------+------------------+-------+----------+\
+---------+------------+-------------------------------------------------------\
+-----------------------------------------------+------------------+
+| Alarm ID | Name | State | Severity \
+| Enabled | Continuous | Alarm condition \
+ | Time constraints |
++--------------------------------------+------------------+-------+----------+\
+---------+------------+-------------------------------------------------------\
+-----------------------------------------------+------------------+
+| 768ff714-8cfb-4db9-9753-d484cb33a1cc | SwiftObjectAlarm | ok | critical \
+| True | False | combinated states (OR) of \
+739e99cb-c2ec-4718-b900-332502355f38, 153462d0-a9b8-4b5b-8175-9e4b05e9b856 \
+| None |
++--------------------------------------+------------------+-------+----------+\
+---------+------------+-------------------------------------------------------\
+-----------------------------------------------+------------------+
+''', sys.stdout.getvalue())
+
+
+class ShellQueryAlarmHistoryCommandTest(utils.BaseTestCase):
+
+ ALARM_HISTORY = [{"alarm_id": "e8ff32f772a44a478182c3fe1f7cad6a",
+ "event_id": "c74a8611-6553-4764-a860-c15a6aabb5d0",
+ "detail":
+ "{\"threshold\": 42.0, \"evaluation_periods\": 4}",
+ "on_behalf_of": "92159030020611e3b26dde429e99ee8c",
+ "project_id": "b6f16144010811e387e4de429e99ee8c",
+ "timestamp": "2014-03-11T16:02:58.376261",
+ "type": "rule change",
+ "user_id": "3e5d11fda79448ac99ccefb20be187ca"
+ }]
+
+ QUERY = {"filter": {"and": [{">": {"timestamp": "2014-03-11T16:02:58"}},
+ {"=": {"type": "rule change"}}]},
+ "orderby": [{"timestamp": "desc"}],
+ "limit": 10}
+
+ def setUp(self):
+ super(ShellQueryAlarmHistoryCommandTest, 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"]
+
+ @mock.patch('sys.stdout', new=six.StringIO())
+ def test_query(self):
+ ret_alarm_history = [alarms.AlarmChange(mock.Mock(), alarm_history)
+ for alarm_history in self.ALARM_HISTORY]
+ self.cc.query_alarm_history.query.return_value = ret_alarm_history
+
+ ceilometer_shell.do_query_alarm_history(self.cc, self.args)
+
+ self.assertEqual('''\
++----------------------------------+--------------------------------------+-\
+------------+----------------------------------------------+----------------\
+------------+
+| Alarm ID | Event ID | \
+Type | Detail | Timestamp \
+ |
++----------------------------------+--------------------------------------+-\
+------------+----------------------------------------------+----------------\
+------------+
+| e8ff32f772a44a478182c3fe1f7cad6a | c74a8611-6553-4764-a860-c15a6aabb5d0 | \
+rule change | {"threshold": 42.0, "evaluation_periods": 4} | 2014-03-11T16:0\
+2:58.376261 |
++----------------------------------+--------------------------------------+-\
+------------+----------------------------------------------+----------------\
+------------+
+''', sys.stdout.getvalue())
+
+
+class ShellStatisticsTest(utils.BaseTestCase):
+ def setUp(self):
+ super(ShellStatisticsTest, self).setUp()
+ self.cc = mock.Mock()
+ self.displays = {
+ 'duration': 'Duration',
+ 'duration_end': 'Duration End',
+ 'duration_start': 'Duration Start',
+ 'period': 'Period',
+ 'period_end': 'Period End',
+ 'period_start': 'Period Start',
+ 'groupby': 'Group By',
+ 'avg': 'Avg',
+ 'count': 'Count',
+ 'max': 'Max',
+ 'min': 'Min',
+ 'sum': 'Sum',
+ 'stddev': 'Standard deviation',
+ 'cardinality': 'Cardinality'
+ }
+ self.args = mock.Mock()
+ self.args.meter_name = 'instance'
+ self.args.aggregate = []
+ self.args.groupby = None
+ self.args.query = None
+
+ def test_statistics_list_simple(self):
+ samples = [
+ {u'count': 135,
+ u'duration_start': u'2013-02-04T10:51:42',
+ u'min': 1.0,
+ u'max': 1.0,
+ u'duration_end':
+ u'2013-02-05T15:46:09',
+ u'duration': 1734.0,
+ u'avg': 1.0,
+ u'sum': 135.0},
+ ]
+ fields = [
+ 'period',
+ 'period_start',
+ 'period_end',
+ 'max',
+ 'min',
+ 'avg',
+ 'sum',
+ 'count',
+ 'duration',
+ 'duration_start',
+ 'duration_end',
+ ]
+ statistics_ret = [
+ statistics.Statistics(mock.Mock(), sample) for sample in samples
+ ]
+ self.cc.statistics.list.return_value = statistics_ret
+ with mock.patch('ceilometerclient.v2.shell.utils.print_list') as pmock:
+ ceilometer_shell.do_statistics(self.cc, self.args)
+ pmock.assert_called_with(
+ statistics_ret,
+ fields,
+ [self.displays[f] for f in fields]
+ )
+
+ def test_statistics_list_groupby(self):
+ samples = [
+ {u'count': 135,
+ u'duration_start': u'2013-02-04T10:51:42',
+ u'min': 1.0,
+ u'max': 1.0,
+ u'duration_end':
+ u'2013-02-05T15:46:09',
+ u'duration': 1734.0,
+ u'avg': 1.0,
+ u'sum': 135.0,
+ u'groupby': {u'resource_id': u'foo'}
+ },
+ {u'count': 12,
+ u'duration_start': u'2013-02-04T10:51:42',
+ u'min': 1.0,
+ u'max': 1.0,
+ u'duration_end':
+ u'2013-02-05T15:46:09',
+ u'duration': 1734.0,
+ u'avg': 1.0,
+ u'sum': 12.0,
+ u'groupby': {u'resource_id': u'bar'}
+ },
+ ]
+ fields = [
+ 'period',
+ 'period_start',
+ 'period_end',
+ 'groupby',
+ 'max',
+ 'min',
+ 'avg',
+ 'sum',
+ 'count',
+ 'duration',
+ 'duration_start',
+ 'duration_end',
+ ]
+ self.args.groupby = 'resource_id'
+ statistics_ret = [
+ statistics.Statistics(mock.Mock(), sample) for sample in samples
+ ]
+ self.cc.statistics.list.return_value = statistics_ret
+ with mock.patch('ceilometerclient.v2.shell.utils.print_list') as pmock:
+ ceilometer_shell.do_statistics(self.cc, self.args)
+ pmock.assert_called_with(
+ statistics_ret,
+ fields,
+ [self.displays[f] for f in fields],
+ )
+
+ def test_statistics_list_aggregates(self):
+ samples = [
+ {u'aggregate': {u'cardinality/resource_id': 4.0, u'count': 2.0},
+ u'count': 2,
+ u'duration': 0.442451,
+ u'duration_end': u'2014-03-12T14:00:21.774154',
+ u'duration_start': u'2014-03-12T14:00:21.331703',
+ u'groupby': None,
+ u'period': 0,
+ u'period_end': u'2014-03-12T14:00:21.774154',
+ u'period_start': u'2014-03-12T14:00:21.331703',
+ u'unit': u'instance',
+ },
+ ]
+ fields = [
+ 'period',
+ 'period_start',
+ 'period_end',
+ 'count',
+ 'cardinality/resource_id',
+ 'duration',
+ 'duration_start',
+ 'duration_end',
+ ]
+ self.args.aggregate = ['count', 'cardinality<-resource_id']
+ statistics_ret = [
+ statistics.Statistics(mock.Mock(), sample) for sample in samples
+ ]
+ self.cc.statistics.list.return_value = statistics_ret
+ with mock.patch('ceilometerclient.v2.shell.utils.print_list') as pmock:
+ ceilometer_shell.do_statistics(self.cc, self.args)
+ pmock.assert_called_with(
+ statistics_ret,
+ fields,
+ [self.displays.get(f, f) for f in fields],
+ )
+
+
+class ShellEmptyIdTest(utils.BaseTestCase):
+ """Test empty field which will cause calling incorrect rest uri."""
+
+ def _test_entity_action_with_empty_values(self, entity,
+ *args, **kwargs):
+ positional = kwargs.pop('positional', False)
+ for value in ('', ' ', ' ', '\t'):
+ self._test_entity_action_with_empty_value(entity, value,
+ positional, *args)
+
+ def _test_entity_action_with_empty_value(self, entity, value,
+ positional, *args):
+ new_args = [value] if positional else ['--%s' % entity, value]
+ argv = list(args) + new_args
+ shell = base_shell.CeilometerShell()
+ with mock.patch('ceilometerclient.exc.CommandError') as e:
+ e.return_value = exc.BaseException()
+ self.assertRaises(exc.BaseException, shell.parse_args, argv)
+ entity = entity.replace('-', '_')
+ e.assert_called_with('%s should not be empty' % entity)
+
+ def _test_alarm_action_with_empty_ids(self, method, *args):
+ args = [method] + list(args)
+ self._test_entity_action_with_empty_values('alarm_id',
+ positional=True, *args)
+
+ def test_alarm_show_with_empty_id(self):
+ self._test_alarm_action_with_empty_ids('alarm-show')
+
+ def test_alarm_update_with_empty_id(self):
+ self._test_alarm_action_with_empty_ids('alarm-update')
+
+ def test_alarm_threshold_update_with_empty_id(self):
+ self._test_alarm_action_with_empty_ids('alarm-threshold-update')
+
+ def test_alarm_combination_update_with_empty_id(self):
+ self._test_alarm_action_with_empty_ids('alarm-combination-update')
+
+ def test_alarm_delete_with_empty_id(self):
+ self._test_alarm_action_with_empty_ids('alarm-delete')
+
+ def test_alarm_state_get_with_empty_id(self):
+ self._test_alarm_action_with_empty_ids('alarm-state-get')
+
+ def test_alarm_state_set_with_empty_id(self):
+ args = ['alarm-state-set', '--state', 'ok']
+ self._test_alarm_action_with_empty_ids(*args)
+
+ def test_alarm_history_with_empty_id(self):
+ self._test_alarm_action_with_empty_ids('alarm-history')
+
+ def test_event_show_with_empty_message_id(self):
+ args = ['event-show']
+ self._test_entity_action_with_empty_values('message_id', *args)
+
+ def test_resource_show_with_empty_id(self):
+ args = ['resource-show']
+ self._test_entity_action_with_empty_values('resource_id', *args)
+
+ def test_sample_list_with_empty_meter(self):
+ args = ['sample-list']
+ self._test_entity_action_with_empty_values('meter', *args)
+
+ def test_sample_create_with_empty_meter(self):
+ args = ['sample-create', '-r', 'x', '--meter-type', 'gauge',
+ '--meter-unit', 'B', '--sample-volume', '1']
+ self._test_entity_action_with_empty_values('meter-name', *args)
+
+ def test_statistics_with_empty_meter(self):
+ args = ['statistics']
+ self._test_entity_action_with_empty_values('meter', *args)
+
+ def test_trait_description_list_with_empty_event_type(self):
+ args = ['trait-description-list']
+ self._test_entity_action_with_empty_values('event_type', *args)
+
+ def test_trait_list_with_empty_event_type(self):
+ args = ['trait-list', '--trait_name', 'x']
+ self._test_entity_action_with_empty_values('event_type', *args)
+
+ def test_trait_list_with_empty_trait_name(self):
+ args = ['trait-list', '--event_type', 'x']
+ self._test_entity_action_with_empty_values('trait_name', *args)
+
+
+class ShellObsoletedArgsTest(utils.BaseTestCase):
+ """Test arguments that have been obsoleted."""
+
+ def _test_entity_obsoleted(self, entity, value, positional, *args):
+ new_args = [value] if positional else ['--%s' % entity, value]
+ argv = list(args) + new_args
+ shell = base_shell.CeilometerShell()
+ with mock.patch('sys.stdout', new_callable=six.StringIO) as stdout:
+ shell.parse_args(argv)
+ self.assertIn('obsolete', stdout.getvalue())
+
+ def test_obsolete_alarm_id(self):
+ for method in ['alarm-show', 'alarm-update', 'alarm-threshold-update',
+ 'alarm-combination-update', 'alarm-delete',
+ 'alarm-state-get', 'alarm-history']:
+ self._test_entity_obsoleted('alarm_id', 'abcde', False, method)
+
+
+class ShellEventListCommandTest(utils.BaseTestCase):
+
+ EVENTS = [
+ {
+ "traits": [],
+ "generated": "2015-01-12T04:03:25.741471",
+ "message_id": "fb2bef58-88af-4380-8698-e0f18fcf452d",
+ "event_type": "compute.instance.create.start",
+ "traits": [{
+ "name": "state",
+ "type": "string",
+ "value": "building",
+ }],
+ },
+ {
+ "traits": [],
+ "generated": "2015-01-12T04:03:28.452495",
+ "message_id": "9b20509a-576b-4995-acfa-1a24ee5cf49f",
+ "event_type": "compute.instance.create.end",
+ "traits": [{
+ "name": "state",
+ "type": "string",
+ "value": "active",
+ }],
+ },
+ ]
+
+ def setUp(self):
+ super(ShellEventListCommandTest, self).setUp()
+ self.cc = mock.Mock()
+ self.args = mock.Mock()
+ self.args.query = None
+ self.args.no_traits = None
+
+ @mock.patch('sys.stdout', new=six.StringIO())
+ def test_event_list(self):
+ ret_events = [events.Event(mock.Mock(), event)
+ for event in self.EVENTS]
+ self.cc.events.list.return_value = ret_events
+ ceilometer_shell.do_event_list(self.cc, self.args)
+ self.assertEqual('''\
++--------------------------------------+-------------------------------+\
+----------------------------+-------------------------------+
+| Message ID | Event Type |\
+ Generated | Traits |
++--------------------------------------+-------------------------------+\
+----------------------------+-------------------------------+
+| fb2bef58-88af-4380-8698-e0f18fcf452d | compute.instance.create.start |\
+ 2015-01-12T04:03:25.741471 | +-------+--------+----------+ |
+| | |\
+ | | name | type | value | |
+| | |\
+ | +-------+--------+----------+ |
+| | |\
+ | | state | string | building | |
+| | |\
+ | +-------+--------+----------+ |
+| 9b20509a-576b-4995-acfa-1a24ee5cf49f | compute.instance.create.end |\
+ 2015-01-12T04:03:28.452495 | +-------+--------+--------+ |
+| | |\
+ | | name | type | value | |
+| | |\
+ | +-------+--------+--------+ |
+| | |\
+ | | state | string | active | |
+| | |\
+ | +-------+--------+--------+ |
++--------------------------------------+-------------------------------+\
+----------------------------+-------------------------------+
+''', sys.stdout.getvalue())
+
+ @mock.patch('sys.stdout', new=six.StringIO())
+ def test_event_list_no_traits(self):
+ self.args.no_traits = True
+ ret_events = [events.Event(mock.Mock(), event)
+ for event in self.EVENTS]
+ self.cc.events.list.return_value = ret_events
+ ceilometer_shell.do_event_list(self.cc, self.args)
+ self.assertEqual('''\
++--------------------------------------+-------------------------------\
++----------------------------+
+| Message ID | Event Type \
+| Generated |
++--------------------------------------+-------------------------------\
++----------------------------+
+| fb2bef58-88af-4380-8698-e0f18fcf452d | compute.instance.create.start \
+| 2015-01-12T04:03:25.741471 |
+| 9b20509a-576b-4995-acfa-1a24ee5cf49f | compute.instance.create.end \
+| 2015-01-12T04:03:28.452495 |
++--------------------------------------+-------------------------------\
++----------------------------+
+''', sys.stdout.getvalue())
+
+
+class ShellShadowedArgsTest(test_shell.ShellTestBase):
+
+ def _test_project_id_alarm(self, command, args, method):
+ self.make_env(test_shell.FAKE_V2_ENV)
+ cli_args = [
+ '--os-project-id', '0ba30185ddf44834914a0b859d244c56',
+ '--debug', command,
+ '--project-id', 'the-project-id-i-want-to-set',
+ '--name', 'project-id-test'] + args
+ with mock.patch.object(alarms.AlarmManager, method) as mocked:
+ base_shell.main(cli_args)
+ args, kwargs = mocked.call_args
+ self.assertEqual('the-project-id-i-want-to-set',
+ kwargs.get('project_id'))
+
+ def test_project_id_threshold_alarm(self):
+ cli_args = ['--meter-name', 'cpu', '--threshold', '90']
+ self._test_project_id_alarm('alarm-create', cli_args, 'create')
+ self._test_project_id_alarm('alarm-threshold-create',
+ cli_args, 'create')
+ cli_args += ['--alarm_id', '437b7ed0-3733-4054-a877-e9a297b8be85']
+ self._test_project_id_alarm('alarm-update', cli_args, 'update')
+ self._test_project_id_alarm('alarm-threshold-update',
+ cli_args, 'update')
+
+ def test_project_id_combination_alarm(self):
+ cli_args = ['--alarm_ids', 'fb16a05a-669d-414e-8bbe-93aa381df6a8',
+ '--alarm_ids', 'b189bcca-0a7b-49a9-a244-a927ac291881']
+ self._test_project_id_alarm('alarm-combination-create',
+ cli_args, 'create')
+ cli_args += ['--alarm_id', '437b7ed0-3733-4054-a877-e9a297b8be85']
+ self._test_project_id_alarm('alarm-combination-update',
+ cli_args, 'update')
+
+ @mock.patch.object(samples.OldSampleManager, 'create')
+ def test_project_id_sample_create(self, mocked):
+ self.make_env(test_shell.FAKE_V2_ENV)
+ cli_args = [
+ '--os-project-id', '0ba30185ddf44834914a0b859d244c56',
+ '--debug', 'sample-create',
+ '--project-id', 'the-project-id-i-want-to-set',
+ '--resource-id', 'b666633d-9bb6-4e05-89c0-ee5a8752fb0b',
+ '--meter-name', 'cpu',
+ '--meter-type', 'cumulative',
+ '--meter-unit', 'ns',
+ '--sample-volume', '10086',
+ ]
+ base_shell.main(cli_args)
+ args, kwargs = mocked.call_args
+ self.assertEqual('the-project-id-i-want-to-set',
+ kwargs.get('project_id'))
diff --git a/ceilometerclient/tests/unit/v2/test_statistics.py b/ceilometerclient/tests/unit/v2/test_statistics.py
new file mode 100644
index 0000000..d2f7aee
--- /dev/null
+++ b/ceilometerclient/tests/unit/v2/test_statistics.py
@@ -0,0 +1,224 @@
+# Copyright 2012 OpenStack Foundation
+# 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
+# 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.openstack.common.apiclient import client
+from ceilometerclient.openstack.common.apiclient import fake_client
+from ceilometerclient.tests.unit 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.type=&q.type=&q.value=foo&q.value=bar')
+period = '&period=60'
+groupby = '&groupby=resource_id'
+aggregate_query = ("aggregate.func=cardinality&aggregate.param=resource_id"
+ "&aggregate.func=count")
+samples = [
+ {u'count': 135,
+ u'duration_start': u'2013-02-04T10:51:42',
+ u'min': 1.0,
+ u'max': 1.0,
+ u'duration_end':
+ u'2013-02-05T15:46:09',
+ u'duration': 1734.0,
+ u'avg': 1.0,
+ u'sum': 135.0},
+]
+groupby_samples = [
+ {u'count': 135,
+ u'duration_start': u'2013-02-04T10:51:42',
+ u'min': 1.0,
+ u'max': 1.0,
+ u'duration_end':
+ u'2013-02-05T15:46:09',
+ u'duration': 1734.0,
+ u'avg': 1.0,
+ u'sum': 135.0,
+ u'groupby': {u'resource_id': u'foo'}
+ },
+ {u'count': 12,
+ u'duration_start': u'2013-02-04T10:51:42',
+ u'min': 1.0,
+ u'max': 1.0,
+ u'duration_end':
+ u'2013-02-05T15:46:09',
+ u'duration': 1734.0,
+ u'avg': 1.0,
+ u'sum': 12.0,
+ u'groupby': {u'resource_id': u'bar'}
+ },
+]
+aggregate_samples = [
+ {u'aggregate': {u'cardinality/resource_id': 4.0, u'count': 2.0},
+ u'count': 2,
+ u'duration': 0.442451,
+ u'duration_end': u'2014-03-12T14:00:21.774154',
+ u'duration_start': u'2014-03-12T14:00:21.331703',
+ u'groupby': None,
+ u'period': 0,
+ u'period_end': u'2014-03-12T14:00:21.774154',
+ u'period_start': u'2014-03-12T14:00:21.331703',
+ u'unit': u'instance',
+ },
+]
+fixtures = {
+ base_url:
+ {
+ 'GET': (
+ {},
+ samples
+ ),
+ },
+ '%s?%s' % (base_url, qry):
+ {
+ 'GET': (
+ {},
+ samples
+ ),
+ },
+ '%s?%s%s' % (base_url, qry, period):
+ {
+ 'GET': (
+ {},
+ samples
+ ),
+ },
+ '%s?%s%s' % (base_url, qry, groupby):
+ {
+ 'GET': (
+ {},
+ groupby_samples
+ ),
+ },
+ '%s?%s' % (base_url, aggregate_query):
+ {
+ 'GET': (
+ {},
+ aggregate_samples
+ ),
+ }
+}
+
+
+class StatisticsManagerTest(utils.BaseTestCase):
+
+ def setUp(self):
+ super(StatisticsManagerTest, self).setUp()
+ self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
+ self.api = client.BaseClient(self.http_client)
+ self.mgr = ceilometerclient.v2.statistics.StatisticsManager(self.api)
+
+ def test_list_by_meter_name(self):
+ stats = list(self.mgr.list(meter_name='instance'))
+ expect = [
+ 'GET', '/v2/meters/instance/statistics'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(stats), 1)
+ self.assertEqual(stats[0].count, 135)
+
+ def test_list_by_meter_name_extended(self):
+ stats = list(self.mgr.list(meter_name='instance',
+ q=[
+ {"field": "resource_id",
+ "value": "foo"},
+ {"field": "source",
+ "value": "bar"},
+ ]))
+ expect = [
+ 'GET', '%s?%s' % (base_url, qry)
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(stats), 1)
+ self.assertEqual(stats[0].count, 135)
+
+ def test_list_by_meter_name_with_period(self):
+ stats = list(self.mgr.list(meter_name='instance',
+ q=[
+ {"field": "resource_id",
+ "value": "foo"},
+ {"field": "source",
+ "value": "bar"},
+ ],
+ period=60))
+ expect = [
+ 'GET', '%s?%s%s' % (base_url, qry, period)
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(stats), 1)
+ self.assertEqual(stats[0].count, 135)
+
+ def test_list_by_meter_name_with_groupby(self):
+ stats = list(self.mgr.list(meter_name='instance',
+ q=[
+ {"field": "resource_id",
+ "value": "foo"},
+ {"field": "source",
+ "value": "bar"},
+ ],
+ groupby=['resource_id']))
+ expect = [
+ 'GET',
+ '%s?%s%s' % (base_url, qry, groupby)
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(stats), 2)
+ self.assertEqual(stats[0].count, 135)
+ self.assertEqual(stats[1].count, 12)
+ self.assertEqual(stats[0].groupby.get('resource_id'), 'foo')
+ self.assertEqual(stats[1].groupby.get('resource_id'), 'bar')
+
+ def test_list_by_meter_name_with_groupby_as_str(self):
+ stats = list(self.mgr.list(meter_name='instance',
+ q=[
+ {"field": "resource_id",
+ "value": "foo"},
+ {"field": "source",
+ "value": "bar"},
+ ],
+ groupby='resource_id'))
+ expect = [
+ 'GET',
+ '%s?%s%s' % (base_url, qry, groupby)
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(2, len(stats))
+ self.assertEqual(135, stats[0].count)
+ self.assertEqual(12, stats[1].count)
+ self.assertEqual('foo', stats[0].groupby.get('resource_id'))
+ self.assertEqual('bar', stats[1].groupby.get('resource_id'))
+
+ def test_list_by_meter_name_with_aggregates(self):
+ aggregates = [
+ {
+ 'func': 'count',
+ },
+ {
+ 'func': 'cardinality',
+ 'param': 'resource_id',
+ },
+ ]
+ stats = list(self.mgr.list(meter_name='instance',
+ aggregates=aggregates))
+ expect = [
+ 'GET',
+ '%s?%s' % (base_url, aggregate_query)
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(1, len(stats))
+ self.assertEqual(2, stats[0].count)
+ self.assertEqual(2.0, stats[0].aggregate.get('count'))
+ self.assertEqual(4.0, stats[0].aggregate.get(
+ 'cardinality/resource_id',
+ ))
diff --git a/ceilometerclient/tests/unit/v2/test_trait_descriptions.py b/ceilometerclient/tests/unit/v2/test_trait_descriptions.py
new file mode 100644
index 0000000..abe8288
--- /dev/null
+++ b/ceilometerclient/tests/unit/v2/test_trait_descriptions.py
@@ -0,0 +1,56 @@
+# 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.openstack.common.apiclient import client
+from ceilometerclient.openstack.common.apiclient import fake_client
+from ceilometerclient.tests.unit 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.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
+ self.api = client.BaseClient(self.http_client)
+ 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'
+ ]
+ self.http_client.assert_called(*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)
diff --git a/ceilometerclient/tests/unit/v2/test_traits.py b/ceilometerclient/tests/unit/v2/test_traits.py
new file mode 100644
index 0000000..7b44a69
--- /dev/null
+++ b/ceilometerclient/tests/unit/v2/test_traits.py
@@ -0,0 +1,62 @@
+# 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.openstack.common.apiclient import client
+from ceilometerclient.openstack.common.apiclient import fake_client
+from ceilometerclient.tests.unit 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.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
+ self.api = client.BaseClient(self.http_client)
+ 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'
+ ]
+ self.http_client.assert_called(*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)