summaryrefslogtreecommitdiff
path: root/ceilometerclient/tests/unit
diff options
context:
space:
mode:
authorChris Dent <chdent@redhat.com>2015-03-12 13:31:06 +0000
committerChris Dent <chdent@redhat.com>2015-03-13 12:01:04 +0000
commitd9d8cc614eccaffefdfd4aa4f536745f0aabc5e3 (patch)
tree8ab56748455532a93e06fe98edddad60a423db15 /ceilometerclient/tests/unit
parente727d22bf1fc4ff613699560717e4aa460f60b73 (diff)
downloadpython-ceilometerclient-d9d8cc614eccaffefdfd4aa4f536745f0aabc5e3.tar.gz
Move unit tests into their own subdirectory
This is in preparation of moving functional tests currently in tempest in-tree. No surprises in this change: * move files * update the local imports Same number of tests run, all still pass. Change-Id: Ibfeac44421c5f36740b1377868f5ec469b2893f5
Diffstat (limited to 'ceilometerclient/tests/unit')
-rw-r--r--ceilometerclient/tests/unit/__init__.py0
-rw-r--r--ceilometerclient/tests/unit/fakes.py64
-rw-r--r--ceilometerclient/tests/unit/test_client.py158
-rw-r--r--ceilometerclient/tests/unit/test_exc.py71
-rw-r--r--ceilometerclient/tests/unit/test_shell.py225
-rw-r--r--ceilometerclient/tests/unit/test_utils.py265
-rw-r--r--ceilometerclient/tests/unit/utils.py24
-rw-r--r--ceilometerclient/tests/unit/v1/__init__.py0
-rw-r--r--ceilometerclient/tests/unit/v1/test_meters.py162
-rw-r--r--ceilometerclient/tests/unit/v1/test_projects.py65
-rw-r--r--ceilometerclient/tests/unit/v1/test_resources.py161
-rw-r--r--ceilometerclient/tests/unit/v1/test_samples.py196
-rw-r--r--ceilometerclient/tests/unit/v1/test_users.py65
-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
27 files changed, 4523 insertions, 0 deletions
diff --git a/ceilometerclient/tests/unit/__init__.py b/ceilometerclient/tests/unit/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ceilometerclient/tests/unit/__init__.py
diff --git a/ceilometerclient/tests/unit/fakes.py b/ceilometerclient/tests/unit/fakes.py
new file mode 100644
index 0000000..10d8d8c
--- /dev/null
+++ b/ceilometerclient/tests/unit/fakes.py
@@ -0,0 +1,64 @@
+# 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 keystoneclient.v2_0 import client as ksclient
+
+
+def script_keystone_client():
+ ksclient.Client(auth_url='http://no.where',
+ insecure=False,
+ password='password',
+ tenant_id='',
+ tenant_name='tenant_name',
+ username='username').AndReturn(FakeKeystone('abcd1234'))
+
+
+def fake_headers():
+ return {'X-Auth-Token': 'abcd1234',
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json',
+ 'User-Agent': 'python-ceilometerclient'}
+
+
+class FakeServiceCatalog(object):
+ @staticmethod
+ def url_for(endpoint_type, service_type):
+ return 'http://192.168.1.5:8004/v1/f14b41234'
+
+
+class FakeKeystone(object):
+ service_catalog = FakeServiceCatalog()
+
+ def __init__(self, auth_token):
+ self.auth_token = auth_token
+
+
+class FakeHTTPResponse(object):
+
+ version = 1.1
+
+ def __init__(self, status, reason, headers, body):
+ self.headers = headers
+ self.body = body
+ self.status = status
+ self.reason = reason
+
+ def getheader(self, name, default=None):
+ return self.headers.get(name, default)
+
+ def getheaders(self):
+ return self.headers.items()
+
+ def read(self, amt=None):
+ b = self.body
+ self.body = None
+ return b
diff --git a/ceilometerclient/tests/unit/test_client.py b/ceilometerclient/tests/unit/test_client.py
new file mode 100644
index 0000000..d582d93
--- /dev/null
+++ b/ceilometerclient/tests/unit/test_client.py
@@ -0,0 +1,158 @@
+# 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 types
+
+import mock
+
+from ceilometerclient import client
+from ceilometerclient.tests.unit import fakes
+from ceilometerclient.tests.unit import utils
+from ceilometerclient.v1 import client as v1client
+from ceilometerclient.v2 import client as v2client
+
+FAKE_ENV = {
+ 'username': 'username',
+ 'password': 'password',
+ 'tenant_name': 'tenant_name',
+ 'auth_url': 'http://no.where',
+ 'ceilometer_url': 'http://no.where',
+ 'auth_plugin': 'fake_auth',
+ 'token': '1234',
+ 'user_domain_name': 'default',
+ 'project_domain_name': 'default',
+}
+
+
+class ClientTest(utils.BaseTestCase):
+
+ @staticmethod
+ def create_client(env, api_version=2, endpoint=None, exclude=[]):
+ env = dict((k, v) for k, v in env.items()
+ if k not in exclude)
+
+ return client.get_client(api_version, **env)
+
+ def test_client_version(self):
+ c1 = self.create_client(env=FAKE_ENV, api_version=1)
+ self.assertIsInstance(c1, v1client.Client)
+
+ c2 = self.create_client(env=FAKE_ENV, api_version=2)
+ self.assertIsInstance(c2, v2client.Client)
+
+ def test_client_auth_lambda(self):
+ env = FAKE_ENV.copy()
+ env['token'] = lambda: env['token']
+ self.assertIsInstance(env['token'],
+ types.FunctionType)
+ c2 = self.create_client(env)
+ self.assertIsInstance(c2, v2client.Client)
+
+ def test_client_auth_non_lambda(self):
+ env = FAKE_ENV.copy()
+ env['token'] = "1234"
+ self.assertIsInstance(env['token'], str)
+ c2 = self.create_client(env)
+ self.assertIsInstance(c2, v2client.Client)
+
+ @mock.patch('keystoneclient.v2_0.client', fakes.FakeKeystone)
+ def test_client_without_auth_plugin(self):
+ env = FAKE_ENV.copy()
+ del env['auth_plugin']
+ c = self.create_client(env, api_version=2, endpoint='fake_endpoint')
+ self.assertIsInstance(c.auth_plugin, client.AuthPlugin)
+
+ def test_client_without_auth_plugin_keystone_v3(self):
+ env = FAKE_ENV.copy()
+ del env['auth_plugin']
+ expected = {
+ 'username': 'username',
+ 'endpoint': 'http://no.where',
+ 'tenant_name': 'tenant_name',
+ 'service_type': None,
+ 'token': '1234',
+ 'endpoint_type': None,
+ 'auth_url': 'http://no.where',
+ 'tenant_id': None,
+ 'cacert': None,
+ 'password': 'password',
+ 'user_domain_name': 'default',
+ 'user_domain_id': None,
+ 'project_domain_name': 'default',
+ 'project_domain_id': None,
+ }
+ with mock.patch('ceilometerclient.client.AuthPlugin') as auth_plugin:
+ self.create_client(env, api_version=2, endpoint='http://no.where')
+ auth_plugin.assert_called_with(**expected)
+
+ def test_client_with_auth_plugin(self):
+ c = self.create_client(FAKE_ENV, api_version=2)
+ self.assertIsInstance(c.auth_plugin, str)
+
+ def test_v2_client_timeout_invalid_value(self):
+ env = FAKE_ENV.copy()
+ env['timeout'] = 'abc'
+ self.assertRaises(ValueError, self.create_client, env)
+ env['timeout'] = '1.5'
+ self.assertRaises(ValueError, self.create_client, env)
+
+ def _test_v2_client_timeout_integer(self, timeout, expected_value):
+ env = FAKE_ENV.copy()
+ env['timeout'] = timeout
+ expected = {
+ 'auth_plugin': 'fake_auth',
+ 'timeout': expected_value,
+ 'original_ip': None,
+ 'http': None,
+ 'region_name': None,
+ 'verify': True,
+ 'timings': None,
+ 'keyring_saver': None,
+ 'cert': None,
+ 'endpoint_type': None,
+ 'user_agent': None,
+ 'debug': None,
+ }
+ cls = 'ceilometerclient.openstack.common.apiclient.client.HTTPClient'
+ with mock.patch(cls) as mocked:
+ self.create_client(env)
+ mocked.assert_called_with(**expected)
+
+ def test_v2_client_timeout_zero(self):
+ self._test_v2_client_timeout_integer(0, None)
+
+ def test_v2_client_timeout_valid_value(self):
+ self._test_v2_client_timeout_integer(30, 30)
+
+ def test_v2_client_cacert_in_verify(self):
+ env = FAKE_ENV.copy()
+ env['cacert'] = '/path/to/cacert'
+ client = self.create_client(env)
+ self.assertEqual('/path/to/cacert', client.client.verify)
+
+ def test_v2_client_certfile_and_keyfile(self):
+ env = FAKE_ENV.copy()
+ env['cert_file'] = '/path/to/cert'
+ env['key_file'] = '/path/to/keycert'
+ client = self.create_client(env)
+ self.assertEqual(('/path/to/cert', '/path/to/keycert'),
+ client.client.cert)
+
+
+class ClientTest2(ClientTest):
+ @staticmethod
+ def create_client(env, api_version=2, endpoint=None, exclude=[]):
+ env = dict((k, v) for k, v in env.items()
+ if k not in exclude)
+
+ # Run the same tests with direct instantiation of the Client
+ return client.Client(api_version, endpoint, **env)
diff --git a/ceilometerclient/tests/unit/test_exc.py b/ceilometerclient/tests/unit/test_exc.py
new file mode 100644
index 0000000..db8df32
--- /dev/null
+++ b/ceilometerclient/tests/unit/test_exc.py
@@ -0,0 +1,71 @@
+# Copyright 2013 eNovance
+# 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 json
+
+from ceilometerclient import exc
+from ceilometerclient.tests.unit import utils
+
+HTTPEXCEPTIONS = {'HTTPBadRequest': exc.HTTPBadRequest,
+ 'HTTPUnauthorized': exc.HTTPUnauthorized,
+ 'HTTPForbidden': exc.HTTPForbidden,
+ 'HTTPNotFound': exc.HTTPNotFound,
+ 'HTTPMethodNotAllowed': exc.HTTPMethodNotAllowed,
+ 'HTTPConflict': exc.HTTPConflict,
+ 'HTTPOverLimit': exc.HTTPOverLimit,
+ 'HTTPInternalServerError': exc.HTTPInternalServerError,
+ 'HTTPNotImplemented': exc.HTTPNotImplemented,
+ 'HTTPBadGateway': exc.HTTPBadGateway,
+ 'HTTPServiceUnavailable': exc.HTTPServiceUnavailable}
+
+
+class HTTPExceptionsTest(utils.BaseTestCase):
+ def test_str_no_details(self):
+ for k, v in HTTPEXCEPTIONS.items():
+ exception = v()
+ ret_str = k + " (HTTP " + str(exception.code) + ")"
+ self.assertEqual(ret_str, str(exception))
+
+ def test_str_no_json(self):
+ for k, v in HTTPEXCEPTIONS.items():
+ exception = v(details="foo")
+ ret_str = k + " (HTTP " + str(exception.code) + ")"
+ self.assertEqual(ret_str, str(exception))
+
+ def test_str_no_error_message(self):
+ for k, v in HTTPEXCEPTIONS.items():
+ exception = v(details=json.dumps({}))
+ ret_str = k + " (HTTP " + str(exception.code) + ")"
+ self.assertEqual(ret_str, str(exception))
+
+ def test_str_no_faultstring(self):
+ for k, v in HTTPEXCEPTIONS.items():
+ exception = v(
+ details=json.dumps({"error_message": {"foo": "bar"}}))
+ ret_str = k + " (HTTP " + str(exception.code) + ")"
+ self.assertEqual(ret_str, str(exception))
+
+ def test_str_error_message_unknown_format(self):
+ for k, v in HTTPEXCEPTIONS.items():
+ exception = v(details=json.dumps({"error_message": "oops"}))
+ ret_str = k + " (HTTP " + str(exception.code) + ")"
+ self.assertEqual(ret_str, str(exception))
+
+ def test_str_faultstring(self):
+ for k, v in HTTPEXCEPTIONS.items():
+ exception = v(details=json.dumps(
+ {"error_message": {"faultstring": "oops"}}))
+ ret_str = k + " (HTTP " + str(exception.code) + ") ERROR oops"
+ self.assertEqual(ret_str, str(exception))
diff --git a/ceilometerclient/tests/unit/test_shell.py b/ceilometerclient/tests/unit/test_shell.py
new file mode 100644
index 0000000..7d06ab7
--- /dev/null
+++ b/ceilometerclient/tests/unit/test_shell.py
@@ -0,0 +1,225 @@
+# 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 fixtures
+from keystoneclient import session as ks_session
+import mock
+import six
+from testtools import matchers
+
+from ceilometerclient import exc
+from ceilometerclient.openstack.common.apiclient import client as api_client
+from ceilometerclient import shell as ceilometer_shell
+from ceilometerclient.tests.unit import utils
+from ceilometerclient.v2 import client as v2client
+
+FAKE_V2_ENV = {'OS_USERNAME': 'username',
+ 'OS_PASSWORD': 'password',
+ 'OS_TENANT_NAME': 'tenant_name',
+ 'OS_AUTH_URL': 'http://localhost:5000/v2.0'}
+
+FAKE_V3_ENV = {'OS_USERNAME': 'username',
+ 'OS_PASSWORD': 'password',
+ 'OS_USER_DOMAIN_NAME': 'domain_name',
+ 'OS_PROJECT_ID': '1234567890',
+ 'OS_AUTH_URL': 'http://localhost:5000/v3'}
+
+
+class ShellTestBase(utils.BaseTestCase):
+
+ # Patch os.environ to avoid required auth info.
+ def make_env(self, env_version, exclude=None):
+ env = dict((k, v) for k, v in env_version.items() if k != exclude)
+ self.useFixture(fixtures.MonkeyPatch('os.environ', env))
+
+
+class ShellHelpTest(ShellTestBase):
+ RE_OPTIONS = re.DOTALL | re.MULTILINE
+
+ @mock.patch('sys.stdout', new=six.StringIO())
+ @mock.patch.object(ks_session, 'Session', mock.MagicMock())
+ @mock.patch.object(v2client.client.HTTPClient,
+ 'client_request', mock.MagicMock())
+ def shell(self, argstr):
+ try:
+ _shell = ceilometer_shell.CeilometerShell()
+ _shell.main(argstr.split())
+ except SystemExit:
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+ self.assertEqual(exc_value.code, 0)
+
+ return sys.stdout.getvalue()
+
+ def test_help_unknown_command(self):
+ self.assertRaises(exc.CommandError, self.shell, 'help foofoo')
+
+ def test_help(self):
+ required = [
+ '.*?^usage: ceilometer',
+ '.*?^See "ceilometer help COMMAND" '
+ 'for help on a specific command',
+ ]
+ for argstr in ['--help', 'help']:
+ help_text = self.shell(argstr)
+ for r in required:
+ self.assertThat(help_text,
+ matchers.MatchesRegex(r,
+ self.RE_OPTIONS))
+
+ def test_help_on_subcommand(self):
+ required = [
+ '.*?^usage: ceilometer meter-list',
+ ".*?^List the user's meter",
+ ]
+ argstrings = [
+ 'help meter-list',
+ ]
+ for argstr in argstrings:
+ help_text = self.shell(argstr)
+ for r in required:
+ self.assertThat(help_text,
+ matchers.MatchesRegex(r, self.RE_OPTIONS))
+
+
+class ShellKeystoneV2Test(ShellTestBase):
+
+ @mock.patch.object(ks_session, 'Session')
+ def test_debug_switch_raises_error(self, mock_ksclient):
+ mock_ksclient.side_effect = exc.HTTPUnauthorized
+ self.make_env(FAKE_V2_ENV)
+ args = ['--debug', 'event-list']
+ self.assertRaises(exc.CommandError, ceilometer_shell.main, args)
+
+ @mock.patch.object(ks_session, 'Session')
+ def test_dash_d_switch_raises_error(self, mock_ksclient):
+ mock_ksclient.side_effect = exc.CommandError("FAIL")
+ self.make_env(FAKE_V2_ENV)
+ args = ['-d', 'event-list']
+ self.assertRaises(exc.CommandError, ceilometer_shell.main, args)
+
+ @mock.patch('sys.stderr')
+ @mock.patch.object(ks_session, 'Session')
+ def test_no_debug_switch_no_raises_errors(self, mock_ksclient, __):
+ mock_ksclient.side_effect = exc.HTTPUnauthorized("FAIL")
+ self.make_env(FAKE_V2_ENV)
+ args = ['event-list']
+ self.assertRaises(SystemExit, ceilometer_shell.main, args)
+
+
+class ShellKeystoneV3Test(ShellTestBase):
+
+ @mock.patch.object(ks_session, 'Session')
+ def test_debug_switch_raises_error(self, mock_ksclient):
+ mock_ksclient.side_effect = exc.HTTPUnauthorized
+ self.make_env(FAKE_V3_ENV)
+ args = ['--debug', 'event-list']
+ self.assertRaises(exc.CommandError, ceilometer_shell.main, args)
+
+ @mock.patch.object(ks_session, 'Session')
+ def test_dash_d_switch_raises_error(self, mock_ksclient):
+ mock_ksclient.side_effect = exc.CommandError("FAIL")
+ self.make_env(FAKE_V3_ENV)
+ args = ['-d', 'event-list']
+ self.assertRaises(exc.CommandError, ceilometer_shell.main, args)
+
+ @mock.patch('sys.stderr')
+ @mock.patch.object(ks_session, 'Session')
+ def test_no_debug_switch_no_raises_errors(self, mock_ksclient, __):
+ mock_ksclient.side_effect = exc.HTTPUnauthorized("FAIL")
+ self.make_env(FAKE_V3_ENV)
+ args = ['event-list']
+ self.assertRaises(SystemExit, ceilometer_shell.main, args)
+
+
+class ShellTimeoutTest(ShellTestBase):
+
+ @mock.patch('sys.stderr', new=six.StringIO())
+ def _test_timeout(self, timeout, expected_msg):
+ args = ['--timeout', timeout, 'alarm-list']
+ self.assertRaises(SystemExit, ceilometer_shell.main, args)
+ self.assertEqual(expected_msg, sys.stderr.getvalue().splitlines()[-1])
+
+ def test_timeout_invalid_value(self):
+ expected_msg = ('ceilometer: error: argument --timeout: '
+ 'abc must be an integer')
+ self._test_timeout('abc', expected_msg)
+
+ def test_timeout_negative_value(self):
+ expected_msg = ('ceilometer: error: argument --timeout: '
+ '-1 must be greater than 0')
+ self._test_timeout('-1', expected_msg)
+
+ def test_timeout_float_value(self):
+ expected_msg = ('ceilometer: error: argument --timeout: '
+ '1.5 must be an integer')
+ self._test_timeout('1.5', expected_msg)
+
+ def test_timeout_zero(self):
+ expected_msg = ('ceilometer: error: argument --timeout: '
+ '0 must be greater than 0')
+ self._test_timeout('0', expected_msg)
+
+
+class ShellInsecureTest(ShellTestBase):
+
+ @mock.patch.object(api_client, 'HTTPClient')
+ def test_insecure_true_ceilometer(self, mocked_client):
+ self.make_env(FAKE_V2_ENV)
+ args = ['--debug', '--os-insecure', 'true', 'alarm-list']
+ self.assertIsNone(ceilometer_shell.main(args))
+ args, kwargs = mocked_client.call_args
+ self.assertEqual(False, kwargs.get('verify'))
+
+ @mock.patch.object(ks_session, 'Session')
+ def test_insecure_true_keystone(self, mocked_session):
+ mocked_session.side_effect = exc.HTTPUnauthorized("FAIL")
+ self.make_env(FAKE_V2_ENV)
+ args = ['--debug', '--os-insecure', 'true', 'alarm-list']
+ self.assertRaises(exc.CommandError, ceilometer_shell.main, args)
+ mocked_session.assert_called_with(verify=False, cert='')
+
+ @mock.patch.object(api_client, 'HTTPClient')
+ def test_insecure_false_ceilometer(self, mocked_client):
+ self.make_env(FAKE_V2_ENV)
+ args = ['--debug', '--os-insecure', 'false', 'alarm-list']
+ self.assertIsNone(ceilometer_shell.main(args))
+ args, kwargs = mocked_client.call_args
+ self.assertEqual(True, kwargs.get('verify'))
+
+ @mock.patch.object(ks_session, 'Session')
+ def test_insecure_false_keystone(self, mocked_session):
+ mocked_session.side_effect = exc.HTTPUnauthorized("FAIL")
+ self.make_env(FAKE_V2_ENV)
+ args = ['--debug', '--os-insecure', 'false', 'alarm-list']
+ self.assertRaises(exc.CommandError, ceilometer_shell.main, args)
+ mocked_session.assert_called_with(verify=True, cert='')
+
+
+class ShellEndpointTest(ShellTestBase):
+
+ @mock.patch('ceilometerclient.v2.client.Client')
+ def _test_endpoint_and_token(self, token_name, endpoint_name, mocked):
+ args = ['--debug', token_name, 'fake-token',
+ endpoint_name, 'http://fake-url', 'alarm-list']
+ self.assertEqual(None, ceilometer_shell.main(args))
+ args, kwargs = mocked.call_args
+ self.assertEqual('http://fake-url', kwargs.get('endpoint'))
+ self.assertEqual('fake-token', kwargs.get('token'))
+
+ def test_endpoint_and_token(self):
+ self._test_endpoint_and_token('--os-auth-token', '--ceilometer-url')
+ self._test_endpoint_and_token('--os-auth-token', '--os-endpoint')
+ self._test_endpoint_and_token('--os-token', '--ceilometer-url')
+ self._test_endpoint_and_token('--os-token', '--os-endpoint')
diff --git a/ceilometerclient/tests/unit/test_utils.py b/ceilometerclient/tests/unit/test_utils.py
new file mode 100644
index 0000000..00bd416
--- /dev/null
+++ b/ceilometerclient/tests/unit/test_utils.py
@@ -0,0 +1,265 @@
+# Copyright 2013 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 itertools
+
+import mock
+import six
+
+from ceilometerclient.common import utils
+from ceilometerclient.tests.unit import utils as test_utils
+
+
+class UtilsTest(test_utils.BaseTestCase):
+
+ def test_prettytable(self):
+ class Struct(object):
+ def __init__(self, **entries):
+ self.__dict__.update(entries)
+
+ # test that the prettytable output is wellformatted (left-aligned)
+ with mock.patch('sys.stdout', new=six.StringIO()) as stdout:
+ utils.print_dict({'K': 'k', 'Key': 'Value'})
+ self.assertEqual('''\
++----------+-------+
+| Property | Value |
++----------+-------+
+| K | k |
+| Key | Value |
++----------+-------+
+''', stdout.getvalue())
+
+ with mock.patch('sys.stdout', new=six.StringIO()) as stdout:
+ utils.print_dict({'alarm_id': '262567fd-d79a-4bbb-a9d0-59d879b6',
+ 'name': u'\u6d4b\u8bd5',
+ 'description': u'\u6d4b\u8bd5',
+ 'state': 'insufficient data',
+ 'repeat_actions': 'False',
+ 'type': 'threshold',
+ 'threshold': '1.0',
+ 'statistic': 'avg',
+ 'time_constraints': '[{name: c1,'
+ '\\n description: test,'
+ '\\n start: 0 18 * * *,'
+ '\\n duration: 1,'
+ '\\n timezone: US}]'},
+ wrap=72)
+ expected = u'''\
++------------------+----------------------------------+
+| Property | Value |
++------------------+----------------------------------+
+| alarm_id | 262567fd-d79a-4bbb-a9d0-59d879b6 |
+| description | \u6d4b\u8bd5 |
+| name | \u6d4b\u8bd5 |
+| repeat_actions | False |
+| state | insufficient data |
+| statistic | avg |
+| threshold | 1.0 |
+| time_constraints | [{name: c1, |
+| | description: test, |
+| | start: 0 18 * * *, |
+| | duration: 1, |
+| | timezone: US}] |
+| type | threshold |
++------------------+----------------------------------+
+'''
+ # py2 prints str type, py3 prints unicode type
+ if six.PY2:
+ expected = expected.encode('utf-8')
+ self.assertEqual(expected, stdout.getvalue())
+
+ def test_print_list(self):
+ class Foo(object):
+ def __init__(self, one, two, three):
+ self.one = one
+ self.two = two
+ self.three = three
+
+ foo_list = [
+ Foo(10, 'a', 'B'),
+ Foo(8, 'c', 'c'),
+ Foo(12, '0', 'Z')]
+
+ def do_print_list(sortby):
+ with mock.patch('sys.stdout', new=six.StringIO()) as stdout:
+ utils.print_list(foo_list,
+ ['one', 'two', 'three'],
+ ['1st', '2nd', '3rd'],
+ {'one': lambda o: o.one * 10},
+ sortby)
+ return stdout.getvalue()
+
+ printed = do_print_list(None)
+ self.assertEqual(printed, '''\
++-----+-----+-----+
+| 1st | 2nd | 3rd |
++-----+-----+-----+
+| 100 | a | B |
+| 80 | c | c |
+| 120 | 0 | Z |
++-----+-----+-----+
+''')
+
+ printed = do_print_list(0)
+ self.assertEqual(printed, '''\
++-----+-----+-----+
+| 1st | 2nd | 3rd |
++-----+-----+-----+
+| 80 | c | c |
+| 100 | a | B |
+| 120 | 0 | Z |
++-----+-----+-----+
+''')
+
+ printed = do_print_list(1)
+ self.assertEqual(printed, '''\
++-----+-----+-----+
+| 1st | 2nd | 3rd |
++-----+-----+-----+
+| 120 | 0 | Z |
+| 100 | a | B |
+| 80 | c | c |
++-----+-----+-----+
+''')
+
+ def test_args_array_to_dict(self):
+ my_args = {
+ 'matching_metadata': ['metadata.key=metadata_value'],
+ 'other': 'value'
+ }
+ cleaned_dict = utils.args_array_to_dict(my_args,
+ "matching_metadata")
+ self.assertEqual(cleaned_dict, {
+ 'matching_metadata': {'metadata.key': 'metadata_value'},
+ 'other': 'value'
+ })
+
+ def test_args_array_to_list_of_dicts(self):
+ starts = ['0 11 * * *', '"0 11 * * *"', '\'0 11 * * *\'']
+ timezones = [None, 'US/Eastern', '"US/Eastern"', '\'US/Eastern\'']
+ descs = [None, 'de sc', '"de sc"', '\'de sc\'']
+ for start, tz, desc in itertools.product(starts, timezones, descs):
+ my_args = {
+ 'time_constraints': ['name=const1;start=%s;duration=1'
+ % start],
+ 'other': 'value'
+ }
+ expected = {
+ 'time_constraints': [dict(name='const1',
+ start='0 11 * * *',
+ duration='1')],
+ 'other': 'value'
+ }
+ if tz:
+ my_args['time_constraints'][0] += ';timezone=%s' % tz
+ expected['time_constraints'][0]['timezone'] = 'US/Eastern'
+ if desc:
+ my_args['time_constraints'][0] += ';description=%s' % desc
+ expected['time_constraints'][0]['description'] = 'de sc'
+
+ cleaned = utils.args_array_to_list_of_dicts(my_args,
+ 'time_constraints')
+ self.assertEqual(expected, cleaned)
+
+ def test_key_with_slash_to_nested_dict(self):
+ my_args = {
+ 'combination_rule/alarm_ids': ['id1', 'id2'],
+ 'combination_rule/operator': 'and',
+ 'threshold_rule/threshold': 400,
+ 'threshold_rule/statictic': 'avg',
+ 'threshold_rule/comparison_operator': 'or',
+ }
+ nested_dict = utils.key_with_slash_to_nested_dict(my_args)
+ self.assertEqual(nested_dict, {
+ 'combination_rule': {'alarm_ids': ['id1', 'id2'],
+ 'operator': 'and'},
+ 'threshold_rule': {'threshold': 400,
+ 'statictic': 'avg',
+ 'comparison_operator': 'or'},
+ })
+
+ def test_arg(self):
+ @utils.arg(help="not_required_no_default.")
+ def not_required_no_default():
+ pass
+ _, args = not_required_no_default.__dict__['arguments'][0]
+ self.assertEqual(args['help'], "not_required_no_default.")
+
+ @utils.arg(required=True, help="required_no_default.")
+ def required_no_default():
+ pass
+ _, args = required_no_default.__dict__['arguments'][0]
+ self.assertEqual(args['help'], "required_no_default. Required.")
+
+ @utils.arg(default=42, help="not_required_default.")
+ def not_required_default():
+ pass
+ _, args = not_required_default.__dict__['arguments'][0]
+ self.assertEqual(args['help'], "not_required_default. Defaults to 42.")
+
+ def test_merge_nested_dict(self):
+ dest = {'key': 'value',
+ 'nested': {'key2': 'value2',
+ 'key3': 'value3',
+ 'nested2': {'key': 'value',
+ 'some': 'thing'}}}
+ source = {'key': 'modified',
+ 'nested': {'key3': 'modified3',
+ 'nested2': {'key5': 'value5'}}}
+ utils.merge_nested_dict(dest, source, depth=1)
+
+ self.assertEqual(dest, {'key': 'modified',
+ 'nested': {'key2': 'value2',
+ 'key3': 'modified3',
+ 'nested2': {'key5': 'value5'}}})
+
+ def test_merge_nested_dict_no_depth(self):
+ dest = {'key': 'value',
+ 'nested': {'key2': 'value2',
+ 'key3': 'value3',
+ 'nested2': {'key': 'value',
+ 'some': 'thing'}}}
+ source = {'key': 'modified',
+ 'nested': {'key3': 'modified3',
+ 'nested2': {'key5': 'value5'}}}
+ utils.merge_nested_dict(dest, source)
+
+ self.assertEqual(dest, {'key': 'modified',
+ 'nested': {'key3': 'modified3',
+ 'nested2': {'key5': 'value5'}}})
+
+ @mock.patch('prettytable.PrettyTable')
+ def test_format_nested_list_of_dict(self, pt_mock):
+ actual_rows = []
+
+ def mock_add_row(row):
+ actual_rows.append(row)
+
+ table = mock.Mock()
+ table.add_row = mock_add_row
+ table.get_string.return_value = "the table"
+
+ test_data = [
+ {'column_1': 'value_11', 'column_2': 'value_21'},
+ {'column_1': 'value_12', 'column_2': 'value_22'}
+
+ ]
+ columns = ['column_1', 'column_2']
+ pt_mock.return_value = table
+
+ rval = utils.format_nested_list_of_dict(test_data, columns)
+ self.assertEqual("the table", rval)
+ self.assertEqual([['value_11', 'value_21'], ['value_12', 'value_22']],
+ actual_rows)
diff --git a/ceilometerclient/tests/unit/utils.py b/ceilometerclient/tests/unit/utils.py
new file mode 100644
index 0000000..57bc276
--- /dev/null
+++ b/ceilometerclient/tests/unit/utils.py
@@ -0,0 +1,24 @@
+# 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 fixtures
+import testtools
+
+
+class BaseTestCase(testtools.TestCase):
+
+ def setUp(self):
+ super(BaseTestCase, self).setUp()
+ self.useFixture(fixtures.FakeLogger())
diff --git a/ceilometerclient/tests/unit/v1/__init__.py b/ceilometerclient/tests/unit/v1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ceilometerclient/tests/unit/v1/__init__.py
diff --git a/ceilometerclient/tests/unit/v1/test_meters.py b/ceilometerclient/tests/unit/v1/test_meters.py
new file mode 100644
index 0000000..afd9a1b
--- /dev/null
+++ b/ceilometerclient/tests/unit/v1/test_meters.py
@@ -0,0 +1,162 @@
+# 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.v1.meters
+
+
+fixtures = {
+ '/v1/meters': {
+ 'GET': (
+ {},
+ {'meters': [
+ {
+ 'resource_id': 'a',
+ 'project_id': 'dig_the_ditch',
+ 'user_id': 'freddy',
+ 'name': 'this',
+ 'type': 'counter',
+ },
+ {
+ 'resource_id': 'b',
+ 'project_id': 'dig_the_ditch',
+ 'user_id': 'joey',
+ 'name': 'this',
+ 'type': 'counter',
+ },
+ ]},
+ ),
+ },
+ '/v1/users/joey/meters': {
+ 'GET': (
+ {},
+ {'meters': [
+ {
+ 'resource_id': 'b',
+ 'project_id': 'dig_the_ditch',
+ 'user_id': 'joey',
+ 'name': 'this',
+ 'type': 'counter',
+ },
+ ]},
+ ),
+ },
+ '/v1/projects/dig_the_ditch/meters': {
+ 'GET': (
+ {},
+ {'meters': [
+ {
+ 'resource_id': 'b',
+ 'project_id': 'dig_the_ditch',
+ 'user_id': 'joey',
+ 'name': 'this',
+ 'type': 'counter',
+ },
+ ]},
+ ),
+ },
+ '/v1/sources/openstack/meters': {
+ 'GET': (
+ {},
+ {'meters': [
+ {
+ 'resource_id': 'b',
+ 'project_id': 'dig_the_ditch',
+ 'user_id': 'joey',
+ 'name': 'this',
+ 'type': 'counter',
+ },
+ {
+ 'resource_id': 'q',
+ 'project_id': 'dig_the_trench',
+ 'user_id': 'joey',
+ 'name': 'this',
+ 'type': 'counter',
+ },
+ ]},
+ ),
+ },
+ '/v1/meters?metadata.zxc_id=foo': {
+ 'GET': (
+ {},
+ {'meters': [
+ {
+ 'resource_id': 'b',
+ 'project_id': 'dig_the_ditch',
+ 'user_id': 'joey',
+ 'name': 'this',
+ 'type': 'counter',
+ },
+ ]},
+ ),
+ },
+}
+
+
+class MeterManagerTest(utils.BaseTestCase):
+
+ def setUp(self):
+ super(MeterManagerTest, self).setUp()
+ self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
+ self.api = client.BaseClient(self.http_client)
+ self.mgr = ceilometerclient.v1.meters.MeterManager(self.api)
+
+ def test_list_all(self):
+ resources = list(self.mgr.list())
+ expect = [
+ 'GET', '/v1/meters'
+ ]
+ 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_by_source(self):
+ resources = list(self.mgr.list(source='openstack'))
+ expect = [
+ 'GET', '/v1/sources/openstack/meters'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(resources), 2)
+ self.assertEqual(resources[0].resource_id, 'b')
+ self.assertEqual(resources[1].resource_id, 'q')
+
+ def test_list_by_user(self):
+ resources = list(self.mgr.list(user_id='joey'))
+ expect = [
+ 'GET', '/v1/users/joey/meters'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(resources), 1)
+ self.assertEqual(resources[0].resource_id, 'b')
+
+ def test_list_by_project(self):
+ resources = list(self.mgr.list(project_id='dig_the_ditch'))
+ expect = [
+ 'GET', '/v1/projects/dig_the_ditch/meters'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(resources), 1)
+ self.assertEqual(resources[0].resource_id, 'b')
+
+ def test_list_by_metaquery(self):
+ resources = list(self.mgr.list(metaquery='metadata.zxc_id=foo'))
+ expect = [
+ 'GET', '/v1/meters?metadata.zxc_id=foo'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(resources), 1)
+ self.assertEqual(resources[0].resource_id, 'b')
diff --git a/ceilometerclient/tests/unit/v1/test_projects.py b/ceilometerclient/tests/unit/v1/test_projects.py
new file mode 100644
index 0000000..7b31bce
--- /dev/null
+++ b/ceilometerclient/tests/unit/v1/test_projects.py
@@ -0,0 +1,65 @@
+# 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.v1.meters
+
+
+fixtures = {
+ '/v1/projects': {
+ 'GET': (
+ {},
+ {'projects': [
+ 'a',
+ 'b',
+ ]},
+ ),
+ },
+ '/v1/sources/source_b/projects': {
+ 'GET': (
+ {},
+ {'projects': ['b']},
+ ),
+ },
+}
+
+
+class ProjectManagerTest(utils.BaseTestCase):
+
+ def setUp(self):
+ super(ProjectManagerTest, self).setUp()
+ self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
+ self.api = client.BaseClient(self.http_client)
+ self.mgr = ceilometerclient.v1.meters.ProjectManager(self.api)
+
+ def test_list_all(self):
+ projects = list(self.mgr.list())
+ expect = [
+ 'GET', '/v1/projects'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(projects), 2)
+ self.assertEqual(projects[0].project_id, 'a')
+ self.assertEqual(projects[1].project_id, 'b')
+
+ def test_list_by_source(self):
+ projects = list(self.mgr.list(source='source_b'))
+ expect = [
+ 'GET', '/v1/sources/source_b/projects'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(projects), 1)
+ self.assertEqual(projects[0].project_id, 'b')
diff --git a/ceilometerclient/tests/unit/v1/test_resources.py b/ceilometerclient/tests/unit/v1/test_resources.py
new file mode 100644
index 0000000..cfc4f6c
--- /dev/null
+++ b/ceilometerclient/tests/unit/v1/test_resources.py
@@ -0,0 +1,161 @@
+# 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.v1.meters
+
+
+fixtures = {
+ '/v1/resources': {
+ 'GET': (
+ {},
+ {'resources': [
+ {
+ 'resource_id': 'a',
+ 'project_id': 'project_bla',
+ 'user_id': 'freddy',
+ 'timestamp': 'now',
+ 'meter': ['this', 'that'],
+ 'metadata': {'zxc_id': 'bla'},
+ },
+ {
+ 'resource_id': 'b',
+ 'project_id': 'dig_the_ditch',
+ 'user_id': 'joey',
+ 'timestamp': 'now',
+ 'meter': ['this', 'that'],
+ 'metadata': {'zxc_id': 'foo'},
+ },
+ ]},
+ ),
+ },
+ '/v1/users/joey/resources': {
+ 'GET': (
+ {},
+ {'resources': [
+ {
+ 'resource_id': 'b',
+ 'project_id': 'dig_the_ditch',
+ 'user_id': 'joey',
+ 'timestamp': 'now',
+ 'meter': ['this', 'that'],
+ 'metadata': {'zxc_id': 'foo'},
+ },
+ ]},
+ ),
+ },
+ '/v1/resources?metadata.zxc_id=foo': {
+ 'GET': (
+ {},
+ {'resources': [
+ {
+ 'resource_id': 'b',
+ 'project_id': 'dig_the_ditch',
+ 'user_id': 'joey',
+ 'timestamp': 'now',
+ 'meter': ['this', 'that'],
+ 'metadata': {'zxc_id': 'foo'},
+ },
+ ]},
+ ),
+ },
+ '/v1/projects/project_bla/resources': {
+ 'GET': (
+ {},
+ {'resources': [
+ {
+ 'resource_id': 'a',
+ 'project_id': 'project_bla',
+ 'user_id': 'freddy',
+ 'timestamp': 'now',
+ 'meter': ['this', 'that'],
+ 'metadata': {'zxc_id': 'bla'},
+ },
+ ]},
+ ),
+ },
+ '/v1/resources?start_timestamp=now&end_timestamp=now': {
+ 'GET': (
+ {},
+ {'resources': [
+ {
+ 'resource_id': 'b',
+ 'project_id': 'dig_the_ditch',
+ 'user_id': 'joey',
+ 'timestamp': 'now',
+ 'meter': ['this', 'that'],
+ 'metadata': {'zxc_id': 'foo'},
+ },
+ ]},
+ ),
+ },
+}
+
+
+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.v1.meters.ResourceManager(self.api)
+
+ def test_list_all(self):
+ resources = list(self.mgr.list())
+ expect = [
+ 'GET', '/v1/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_by_user(self):
+ resources = list(self.mgr.list(user_id='joey'))
+ expect = [
+ 'GET', '/v1/users/joey/resources'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(resources), 1)
+ self.assertEqual(resources[0].resource_id, 'b')
+
+ def test_list_by_metaquery(self):
+ resources = list(self.mgr.list(metaquery='metadata.zxc_id=foo'))
+ expect = [
+ 'GET', '/v1/resources?metadata.zxc_id=foo'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(resources), 1)
+ self.assertEqual(resources[0].resource_id, 'b')
+
+ def test_list_by_project(self):
+ resources = list(self.mgr.list(project_id='project_bla'))
+ expect = [
+ 'GET', '/v1/projects/project_bla/resources'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(resources), 1)
+ self.assertEqual(resources[0].resource_id, 'a')
+
+ def test_list_by_timestamp(self):
+ resources = list(self.mgr.list(start_timestamp='now',
+ end_timestamp='now'))
+ expect = [
+ 'GET', '/v1/resources?start_timestamp=now&end_timestamp=now'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(resources), 1)
+ self.assertEqual(resources[0].resource_id, 'b')
diff --git a/ceilometerclient/tests/unit/v1/test_samples.py b/ceilometerclient/tests/unit/v1/test_samples.py
new file mode 100644
index 0000000..0f1a029
--- /dev/null
+++ b/ceilometerclient/tests/unit/v1/test_samples.py
@@ -0,0 +1,196 @@
+# 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.v1.meters
+
+
+fixtures = {
+ '/v1/users/freddy/meters/balls': {
+ 'GET': (
+ {},
+ {'events': [
+ {
+ 'resource_id': 'inst-0045',
+ 'project_id': 'melbourne_open',
+ 'user_id': 'freddy',
+ 'name': 'tennis',
+ 'type': 'counter',
+ 'unit': 'balls',
+ 'volume': 3,
+ 'timestamp': None,
+ 'resource_metadata': None,
+ },
+ ]},
+ ),
+ },
+ '/v1/sources/openstack/meters/this': {
+ 'GET': (
+ {},
+ {'events': [
+ {
+ 'resource_id': 'b',
+ 'project_id': 'dig_the_ditch',
+ 'user_id': 'joey',
+ 'name': 'this',
+ 'type': 'counter',
+ 'unit': 'b',
+ 'volume': 45,
+ 'timestamp': None,
+ 'resource_metadata': None,
+ },
+ ]},
+ ),
+ },
+ '/v1/projects/dig_the_ditch/meters/meters': {
+ 'GET': (
+ {},
+ {'events': [
+ {
+ 'resource_id': 'b',
+ 'project_id': 'dig_the_ditch',
+ 'user_id': 'joey',
+ 'name': 'meters',
+ 'type': 'counter',
+ 'unit': 'meters',
+ 'volume': 345,
+ 'timestamp': None,
+ 'resource_metadata': None,
+ },
+ ]},
+ ),
+ },
+ '/v1/meters?metadata.zxc_id=foo': {
+ 'GET': (
+ {},
+ {'events': [
+ {
+ 'resource_id': 'b',
+ 'project_id': 'dig_the_ditch',
+ 'user_id': 'joey',
+ 'name': 'this',
+ 'type': 'counter',
+ 'unit': 'meters',
+ 'volume': 98,
+ 'timestamp': None,
+ 'resource_metadata': {'zxc_id': 'foo'},
+ },
+ ]},
+ ),
+ },
+ '/v1/users/freddy/meters/balls?start_timestamp=now&end_timestamp=now': {
+ 'GET': (
+ {},
+ {'events': [
+ {
+ 'resource_id': 'inst-0045',
+ 'project_id': 'melbourne_open',
+ 'user_id': 'freddy',
+ 'name': 'tennis',
+ 'type': 'counter',
+ 'unit': 'balls',
+ 'volume': 3,
+ 'timestamp': 'now',
+ 'resource_metadata': None,
+ },
+
+ ]},
+ ),
+ },
+ '/v1/meters': {
+ 'GET': (
+ {},
+ {'meters': []},
+ ),
+ },
+}
+
+
+class SampleManagerTest(utils.BaseTestCase):
+
+ def setUp(self):
+ super(SampleManagerTest, self).setUp()
+ self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
+ self.api = client.BaseClient(self.http_client)
+ self.mgr = ceilometerclient.v1.meters.SampleManager(self.api)
+
+ def test_list_all(self):
+ samples = list(self.mgr.list(counter_name=None))
+ expect = [
+ 'GET', '/v1/meters'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(samples), 0)
+
+ def test_list_by_source(self):
+ samples = list(self.mgr.list(source='openstack',
+ counter_name='this'))
+ expect = [
+ 'GET', '/v1/sources/openstack/meters/this'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(samples), 1)
+ self.assertEqual(samples[0].resource_id, 'b')
+
+ def test_list_by_user(self):
+ samples = list(self.mgr.list(user_id='freddy',
+ counter_name='balls'))
+ expect = [
+ 'GET', '/v1/users/freddy/meters/balls'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(samples), 1)
+ self.assertEqual(samples[0].project_id, 'melbourne_open')
+ self.assertEqual(samples[0].user_id, 'freddy')
+ self.assertEqual(samples[0].volume, 3)
+
+ def test_list_by_project(self):
+ samples = list(self.mgr.list(project_id='dig_the_ditch',
+ counter_name='meters'))
+ expect = [
+ 'GET', '/v1/projects/dig_the_ditch/meters/meters'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(samples), 1)
+ self.assertEqual(samples[0].project_id, 'dig_the_ditch')
+ self.assertEqual(samples[0].volume, 345)
+ self.assertEqual(samples[0].unit, 'meters')
+
+ def test_list_by_metaquery(self):
+ samples = list(self.mgr.list(metaquery='metadata.zxc_id=foo',
+ counter_name='this'))
+ expect = [
+ 'GET', '/v1/meters?metadata.zxc_id=foo'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(samples), 1)
+ self.assertEqual(samples[0].resource_metadata['zxc_id'], 'foo')
+
+ def test_list_by_timestamp(self):
+ samples = list(self.mgr.list(user_id='freddy',
+ counter_name='balls',
+ start_timestamp='now',
+ end_timestamp='now'))
+ expect = [
+ 'GET',
+ '/v1/users/freddy/meters/balls?' +
+ 'start_timestamp=now&end_timestamp=now'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(samples), 1)
+ self.assertEqual(samples[0].project_id, 'melbourne_open')
+ self.assertEqual(samples[0].user_id, 'freddy')
+ self.assertEqual(samples[0].volume, 3)
diff --git a/ceilometerclient/tests/unit/v1/test_users.py b/ceilometerclient/tests/unit/v1/test_users.py
new file mode 100644
index 0000000..e8e98a1
--- /dev/null
+++ b/ceilometerclient/tests/unit/v1/test_users.py
@@ -0,0 +1,65 @@
+# 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.v1.meters
+
+
+fixtures = {
+ '/v1/users': {
+ 'GET': (
+ {},
+ {'users': [
+ 'a',
+ 'b',
+ ]},
+ ),
+ },
+ '/v1/sources/source_b/users': {
+ 'GET': (
+ {},
+ {'users': ['b']},
+ ),
+ },
+}
+
+
+class UserManagerTest(utils.BaseTestCase):
+
+ def setUp(self):
+ super(UserManagerTest, self).setUp()
+ self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
+ self.api = client.BaseClient(self.http_client)
+ self.mgr = ceilometerclient.v1.meters.UserManager(self.api)
+
+ def test_list_all(self):
+ users = list(self.mgr.list())
+ expect = [
+ 'GET', '/v1/users'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(users), 2)
+ self.assertEqual(users[0].user_id, 'a')
+ self.assertEqual(users[1].user_id, 'b')
+
+ def test_list_by_source(self):
+ users = list(self.mgr.list(source='source_b'))
+ expect = [
+ 'GET', '/v1/sources/source_b/users'
+ ]
+ self.http_client.assert_called(*expect)
+ self.assertEqual(len(users), 1)
+ self.assertEqual(users[0].user_id, 'b')
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)