1# Copyright Ericsson AB 2014. All rights reserved 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may 4# not use this file except in compliance with the License. You may obtain 5# a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations 13# under the License. 14 15import json 16import re 17import sys 18 19import mock 20import six 21from testtools import matchers 22 23from ceilometerclient import exc 24from ceilometerclient import shell as base_shell 25from ceilometerclient.tests.unit import test_shell 26from ceilometerclient.tests.unit import utils 27from ceilometerclient.v2 import alarms 28from ceilometerclient.v2 import capabilities 29from ceilometerclient.v2 import event_types 30from ceilometerclient.v2 import events 31from ceilometerclient.v2 import meters 32from ceilometerclient.v2 import resources 33from ceilometerclient.v2 import samples 34from ceilometerclient.v2 import shell as ceilometer_shell 35from ceilometerclient.v2 import statistics 36from ceilometerclient.v2 import trait_descriptions 37from ceilometerclient.v2 import traits 38from keystoneauth1 import exceptions 39 40 41class ShellAlarmStateCommandsTest(utils.BaseTestCase): 42 43 ALARM_ID = 'foobar' 44 45 def setUp(self): 46 super(ShellAlarmStateCommandsTest, self).setUp() 47 self.cc = mock.Mock() 48 self.cc.alarms = mock.Mock() 49 self.args = mock.Mock() 50 self.args.alarm_id = self.ALARM_ID 51 52 def test_alarm_state_get(self): 53 ceilometer_shell.do_alarm_state_get(self.cc, self.args) 54 self.cc.alarms.get_state.assert_called_once_with(self.ALARM_ID) 55 self.assertFalse(self.cc.alarms.set_state.called) 56 57 def test_alarm_state_set(self): 58 self.args.state = 'ok' 59 ceilometer_shell.do_alarm_state_set(self.cc, self.args) 60 self.cc.alarms.set_state.assert_called_once_with(self.ALARM_ID, 'ok') 61 self.assertFalse(self.cc.alarms.get_state.called) 62 63 64class ShellAlarmHistoryCommandTest(utils.BaseTestCase): 65 66 ALARM_ID = '768ff714-8cfb-4db9-9753-d484cb33a1cc' 67 FULL_DETAIL = ('{"alarm_actions": [], ' 68 '"user_id": "8185aa72421a4fd396d4122cba50e1b5", ' 69 '"name": "scombo", ' 70 '"timestamp": "2013-10-03T08:58:33.647912", ' 71 '"enabled": true, ' 72 '"state_timestamp": "2013-10-03T08:58:33.647912", ' 73 '"rule": {"operator": "or", "alarm_ids": ' 74 '["062cc907-3a9f-4867-ab3b-fa83212b39f7"]}, ' 75 '"alarm_id": "768ff714-8cfb-4db9-9753-d484cb33a1cc", ' 76 '"state": "insufficient data", ' 77 '"insufficient_data_actions": [], ' 78 '"repeat_actions": false, ' 79 '"ok_actions": [], ' 80 '"project_id": "57d04f24d0824b78b1ea9bcecedbda8f", ' 81 '"type": "combination", ' 82 '"severity": "low", ' 83 '"description": "Combined state of alarms ' 84 '062cc907-3a9f-4867-ab3b-fa83212b39f7"}') 85 ALARM_HISTORY = [{'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f', 86 'user_id': '8185aa72421a4fd396d4122cba50e1b5', 87 'event_id': 'c74a8611-6553-4764-a860-c15a6aabb5d0', 88 'timestamp': '2013-10-03T08:59:28.326000', 89 'detail': '{"state": "alarm"}', 90 'alarm_id': '768ff714-8cfb-4db9-9753-d484cb33a1cc', 91 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f', 92 'type': 'state transition'}, 93 {'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f', 94 'user_id': '8185aa72421a4fd396d4122cba50e1b5', 95 'event_id': 'c74a8611-6553-4764-a860-c15a6aabb5d0', 96 'timestamp': '2013-10-03T08:59:28.326000', 97 'detail': '{"description": "combination of one"}', 98 'alarm_id': '768ff714-8cfb-4db9-9753-d484cb33a1cc', 99 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f', 100 'type': 'rule change'}, 101 {'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f', 102 'user_id': '8185aa72421a4fd396d4122cba50e1b5', 103 'event_id': '4fd7df9e-190d-4471-8884-dc5a33d5d4bb', 104 'timestamp': '2013-10-03T08:58:33.647000', 105 'detail': FULL_DETAIL, 106 'alarm_id': '768ff714-8cfb-4db9-9753-d484cb33a1cc', 107 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f', 108 'type': 'creation'}] 109 TIMESTAMP_RE = (' +\| (\d{4})-(\d{2})-(\d{2})T' 110 '(\d{2})\:(\d{2})\:(\d{2})\.(\d{6}) \| +') 111 112 def setUp(self): 113 super(ShellAlarmHistoryCommandTest, self).setUp() 114 self.cc = mock.Mock() 115 self.cc.alarms = mock.Mock() 116 self.args = mock.Mock() 117 self.args.alarm_id = self.ALARM_ID 118 119 @mock.patch('sys.stdout', new=six.StringIO()) 120 def _do_test_alarm_history(self, raw_query=None, parsed_query=None): 121 self.args.query = raw_query 122 history = [alarms.AlarmChange(mock.Mock(), change) 123 for change in self.ALARM_HISTORY] 124 self.cc.alarms.get_history.return_value = history 125 126 ceilometer_shell.do_alarm_history(self.cc, self.args) 127 self.cc.alarms.get_history.assert_called_once_with( 128 q=parsed_query, 129 alarm_id=self.ALARM_ID 130 ) 131 out = sys.stdout.getvalue() 132 required = [ 133 '.*creation%sname: scombo.*' % self.TIMESTAMP_RE, 134 '.*rule change%sdescription: combination of one.*' % 135 self.TIMESTAMP_RE, 136 '.*state transition%sstate: alarm.*' % self.TIMESTAMP_RE, 137 ] 138 for r in required: 139 self.assertThat(out, matchers.MatchesRegex(r, re.DOTALL)) 140 141 def test_alarm_all_history(self): 142 self._do_test_alarm_history() 143 144 def test_alarm_constrained_history(self): 145 parsed_query = [dict(field='timestamp', 146 value='2013-10-03T08:59:28', 147 op='gt', 148 type='')] 149 self._do_test_alarm_history(raw_query='timestamp>2013-10-03T08:59:28', 150 parsed_query=parsed_query) 151 152 153class ShellAlarmCommandTest(utils.BaseTestCase): 154 155 ALARM_ID = '768ff714-8cfb-4db9-9753-d484cb33a1cc' 156 ALARM = {"alarm_actions": ["log://"], 157 "ok_actions": [], 158 "description": "instance running hot", 159 "timestamp": "2013-11-20T10:38:42.206952", 160 "enabled": True, 161 "state_timestamp": "2013-11-19T17:20:44", 162 "threshold_rule": {"meter_name": "cpu_util", 163 "evaluation_periods": 3, 164 "period": 600, 165 "statistic": "avg", 166 "threshold": 99.0, 167 "query": [{"field": "resource_id", 168 "value": "INSTANCE_ID", 169 "op": "eq"}], 170 "comparison_operator": "gt"}, 171 "time_constraints": [{"name": "cons1", 172 "description": "desc1", 173 "start": "0 11 * * *", 174 "duration": 300, 175 "timezone": ""}, 176 {"name": "cons2", 177 "description": "desc2", 178 "start": "0 23 * * *", 179 "duration": 600, 180 "timezone": ""}], 181 "alarm_id": ALARM_ID, 182 "state": "insufficient data", 183 "severity": "low", 184 "insufficient_data_actions": [], 185 "repeat_actions": True, 186 "user_id": "528d9b68fa774689834b5c04b4564f8a", 187 "project_id": "ed9d4e2be2a748bc80108053cf4598f5", 188 "type": "threshold", 189 "name": "cpu_high"} 190 191 THRESHOLD_ALARM_CLI_ARGS = [ 192 '--name', 'cpu_high', 193 '--description', 'instance running hot', 194 '--meter-name', 'cpu_util', 195 '--threshold', '70.0', 196 '--comparison-operator', 'gt', 197 '--statistic', 'avg', 198 '--period', '600', 199 '--evaluation-periods', '3', 200 '--alarm-action', 'log://', 201 '--alarm-action', 'http://example.com/alarm/state', 202 '--query', 'resource_id=INSTANCE_ID' 203 ] 204 205 def setUp(self): 206 super(ShellAlarmCommandTest, self).setUp() 207 self.cc = mock.Mock() 208 self.cc.alarms = mock.Mock() 209 self.args = mock.Mock() 210 self.args.alarm_id = self.ALARM_ID 211 212 @mock.patch('sys.stdout', new=six.StringIO()) 213 def _do_test_alarm_update_repeat_actions(self, method, repeat_actions): 214 self.args.threshold = 42.0 215 if repeat_actions is not None: 216 self.args.repeat_actions = repeat_actions 217 alarm = [alarms.Alarm(mock.Mock(), self.ALARM)] 218 self.cc.alarms.get.return_value = alarm 219 self.cc.alarms.update.return_value = alarm[0] 220 221 method(self.cc, self.args) 222 args, kwargs = self.cc.alarms.update.call_args 223 self.assertEqual(self.ALARM_ID, args[0]) 224 self.assertEqual(42.0, kwargs.get('threshold')) 225 if repeat_actions is not None: 226 self.assertEqual(repeat_actions, kwargs.get('repeat_actions')) 227 else: 228 self.assertNotIn('repeat_actions', kwargs) 229 230 def test_alarm_update_repeat_actions_untouched(self): 231 method = ceilometer_shell.do_alarm_update 232 self._do_test_alarm_update_repeat_actions(method, None) 233 234 def test_alarm_update_repeat_actions_set(self): 235 method = ceilometer_shell.do_alarm_update 236 self._do_test_alarm_update_repeat_actions(method, True) 237 238 def test_alarm_update_repeat_actions_clear(self): 239 method = ceilometer_shell.do_alarm_update 240 self._do_test_alarm_update_repeat_actions(method, False) 241 242 def test_alarm_combination_update_repeat_actions_untouched(self): 243 method = ceilometer_shell.do_alarm_combination_update 244 self._do_test_alarm_update_repeat_actions(method, None) 245 246 def test_alarm_combination_update_repeat_actions_set(self): 247 method = ceilometer_shell.do_alarm_combination_update 248 self._do_test_alarm_update_repeat_actions(method, True) 249 250 def test_alarm_combination_update_repeat_actions_clear(self): 251 method = ceilometer_shell.do_alarm_combination_update 252 self._do_test_alarm_update_repeat_actions(method, False) 253 254 def test_alarm_threshold_update_repeat_actions_untouched(self): 255 method = ceilometer_shell.do_alarm_threshold_update 256 self._do_test_alarm_update_repeat_actions(method, None) 257 258 def test_alarm_threshold_update_repeat_actions_set(self): 259 method = ceilometer_shell.do_alarm_threshold_update 260 self._do_test_alarm_update_repeat_actions(method, True) 261 262 def test_alarm_threshold_update_repeat_actions_clear(self): 263 method = ceilometer_shell.do_alarm_threshold_update 264 self._do_test_alarm_update_repeat_actions(method, False) 265 266 def test_alarm_event_upadte_repeat_action_untouched(self): 267 method = ceilometer_shell.do_alarm_event_update 268 self._do_test_alarm_update_repeat_actions(method, None) 269 270 def test_alarm_event_upadte_repeat_action_set(self): 271 method = ceilometer_shell.do_alarm_event_update 272 self._do_test_alarm_update_repeat_actions(method, True) 273 274 def test_alarm_event_upadte_repeat_action_clear(self): 275 method = ceilometer_shell.do_alarm_event_update 276 self._do_test_alarm_update_repeat_actions(method, False) 277 278 @mock.patch('sys.stdout', new=six.StringIO()) 279 def test_alarm_threshold_create_args(self): 280 argv = ['alarm-threshold-create'] + self.THRESHOLD_ALARM_CLI_ARGS 281 self._test_alarm_threshold_action_args('create', argv) 282 283 def test_alarm_threshold_update_args(self): 284 argv = ['alarm-threshold-update', 'x'] + self.THRESHOLD_ALARM_CLI_ARGS 285 self._test_alarm_threshold_action_args('update', argv) 286 287 @mock.patch('sys.stdout', new=six.StringIO()) 288 def _test_alarm_threshold_action_args(self, action, argv): 289 shell = base_shell.CeilometerShell() 290 _, args = shell.parse_args(argv) 291 292 alarm = alarms.Alarm(mock.Mock(), self.ALARM) 293 getattr(self.cc.alarms, action).return_value = alarm 294 295 func = getattr(ceilometer_shell, 'do_alarm_threshold_' + action) 296 func(self.cc, args) 297 _, kwargs = getattr(self.cc.alarms, action).call_args 298 self._check_alarm_threshold_args(kwargs) 299 300 def _check_alarm_threshold_args(self, kwargs): 301 self.assertEqual('cpu_high', kwargs.get('name')) 302 self.assertEqual('instance running hot', kwargs.get('description')) 303 actions = ['log://', 'http://example.com/alarm/state'] 304 self.assertEqual(actions, kwargs.get('alarm_actions')) 305 self.assertIn('threshold_rule', kwargs) 306 rule = kwargs['threshold_rule'] 307 self.assertEqual('cpu_util', rule.get('meter_name')) 308 self.assertEqual(70.0, rule.get('threshold')) 309 self.assertEqual('gt', rule.get('comparison_operator')) 310 self.assertEqual('avg', rule.get('statistic')) 311 self.assertEqual(600, rule.get('period')) 312 self.assertEqual(3, rule.get('evaluation_periods')) 313 query = dict(field='resource_id', type='', 314 value='INSTANCE_ID', op='eq') 315 self.assertEqual([query], rule['query']) 316 317 @mock.patch('sys.stdout', new=six.StringIO()) 318 def test_alarm_create_time_constraints(self): 319 shell = base_shell.CeilometerShell() 320 argv = ['alarm-threshold-create', 321 '--name', 'cpu_high', 322 '--meter-name', 'cpu_util', 323 '--threshold', '70.0', 324 '--time-constraint', 325 'name=cons1;start="0 11 * * *";duration=300', 326 '--time-constraint', 327 'name=cons2;start="0 23 * * *";duration=600', 328 ] 329 _, args = shell.parse_args(argv) 330 331 alarm = alarms.Alarm(mock.Mock(), self.ALARM) 332 self.cc.alarms.create.return_value = alarm 333 334 ceilometer_shell.do_alarm_threshold_create(self.cc, args) 335 _, kwargs = self.cc.alarms.create.call_args 336 time_constraints = [dict(name='cons1', start='0 11 * * *', 337 duration='300'), 338 dict(name='cons2', start='0 23 * * *', 339 duration='600')] 340 self.assertEqual(time_constraints, kwargs['time_constraints']) 341 342 343class ShellAlarmGnocchiCommandTest(test_shell.ShellTestBase): 344 345 ALARM_ID = 'b69ecdb9-f19b-4fb5-950f-5eb53938b718' 346 TIME_CONSTRAINTS = [{ 347 u'duration': 300, 348 u'start': u'0 11 * * *', 349 u'description': u'desc1', 350 u'name': u'cons1', 351 u'timezone': u''}, { 352 u'duration': 600, 353 u'start': u'0 23 * * *', 354 u'name': u'cons2', 355 u'description': u'desc2', 356 u'timezone': u''}] 357 358 ALARM1 = { 359 u'name': u'name_gnocchi_alarm', 360 u'description': u'description_gnocchi_alarm', 361 u'enabled': True, 362 u'ok_actions': [u'http://something/ok'], 363 u'alarm_actions': [u'http://something/alarm'], 364 u'timestamp': u'2015-12-21T03:10:32.305133', 365 u'state_timestamp': u'2015-12-21T03:10:32.305133', 366 u'gnocchi_resources_threshold_rule': { 367 u'evaluation_periods': 3, 368 u'metric': u'cpu_util', 369 u'resource_id': u'768ff714-8cfb-4db9-9753-d484cb33a1cc', 370 u'threshold': 70.0, 371 u'granularity': 60, 372 u'aggregation_method': u'count', 373 u'comparison_operator': u'le', 374 u'resource_type': u'instance', 375 }, 376 u'time_constraints': TIME_CONSTRAINTS, 377 u'alarm_id': ALARM_ID, 378 u'state': u'ok', 379 u'insufficient_data_actions': [u'http://something/insufficient'], 380 u'repeat_actions': True, 381 u'user_id': u'f28735621ee84f329144eb467c91fce6', 382 u'project_id': u'97fcad0402ce4f65ac3bd42a0c6a7e74', 383 u'type': u'gnocchi_resources_threshold', 384 u'severity': u'critical', 385 } 386 387 ALARM2 = { 388 u'name': u'name_gnocchi_alarm', 389 u'description': u'description_gnocchi_alarm', 390 u'enabled': True, 391 u'ok_actions': [u'http://something/ok'], 392 u'alarm_actions': [u'http://something/alarm'], 393 u'timestamp': u'2015-12-21T03:10:32.305133', 394 u'state_timestamp': u'2015-12-21T03:10:32.305133', 395 u'gnocchi_aggregation_by_metrics_threshold_rule': { 396 u'evaluation_periods': 3, 397 u'metrics': [u'b3d9d8ab-05e8-439f-89ad-5e978dd2a5eb', 398 u'009d4faf-c275-46f0-8f2d-670b15bac2b0'], 399 u'threshold': 70.0, 400 u'granularity': 60, 401 u'aggregation_method': u'count', 402 u'comparison_operator': u'le', 403 }, 404 u'time_constraints': TIME_CONSTRAINTS, 405 u'alarm_id': ALARM_ID, 406 u'state': u'ok', 407 u'insufficient_data_actions': [u'http://something/insufficient'], 408 u'repeat_actions': True, 409 u'user_id': u'f28735621ee84f329144eb467c91fce6', 410 u'project_id': u'97fcad0402ce4f65ac3bd42a0c6a7e74', 411 u'type': u'gnocchi_aggregation_by_metrics_threshold', 412 u'severity': u'critical', 413 } 414 415 ALARM3 = { 416 u'name': u'name_gnocchi_alarm', 417 u'description': u'description_gnocchi_alarm', 418 u'enabled': True, 419 u'ok_actions': [u'http://something/ok'], 420 u'alarm_actions': [u'http://something/alarm'], 421 u'timestamp': u'2015-12-21T03:10:32.305133', 422 u'state_timestamp': u'2015-12-21T03:10:32.305133', 423 u'gnocchi_aggregation_by_resources_threshold_rule': { 424 u'evaluation_periods': 3, 425 u'metric': u'cpu_util', 426 u'threshold': 70.0, 427 u'granularity': 60, 428 u'aggregation_method': u'count', 429 u'comparison_operator': u'le', 430 u'resource_type': u'instance', 431 u'query': u'{"=": {"server_group":"my_autoscaling_group"}}', 432 }, 433 u'time_constraints': TIME_CONSTRAINTS, 434 u'alarm_id': ALARM_ID, 435 u'state': u'ok', 436 u'insufficient_data_actions': [u'http://something/insufficient'], 437 u'repeat_actions': True, 438 u'user_id': u'f28735621ee84f329144eb467c91fce6', 439 u'project_id': u'97fcad0402ce4f65ac3bd42a0c6a7e74', 440 u'type': u'gnocchi_aggregation_by_resources_threshold', 441 u'severity': u'critical', 442 } 443 444 COMMON_CLI_ARGS = [ 445 '--name', 'name_gnocchi_alarm', 446 '--description', 'description_gnocchi_alarm', 447 '--enabled', 'True', 448 '--state', 'ok', 449 '--severity', 'critical', 450 '--ok-action', 'http://something/ok', 451 '--alarm-action', 'http://something/alarm', 452 '--insufficient-data-action', 'http://something/insufficient', 453 '--repeat-actions', 'True', 454 '--comparison-operator', 'le', 455 '--aggregation-method', 'count', 456 '--threshold', '70', 457 '--evaluation-periods', '3', 458 '--granularity', '60', 459 '--time-constraint', 460 'name=cons1;start="0 11 * * *";duration=300;description="desc1"', 461 '--time-constraint', 462 'name=cons2;start="0 23 * * *";duration=600;description="desc2"', 463 '--user-id', 'f28735621ee84f329144eb467c91fce6', 464 '--project-id', '97fcad0402ce4f65ac3bd42a0c6a7e74', 465 ] 466 467 GNOCCHI_RESOURCES_CLI_ARGS = COMMON_CLI_ARGS + [ 468 '--metric', 'cpu_util', 469 '--resource-type', 'instance', 470 '--resource-id', '768ff714-8cfb-4db9-9753-d484cb33a1cc', 471 ] 472 473 GNOCCHI_AGGR_BY_METRICS_CLI_ARGS = COMMON_CLI_ARGS + [ 474 '-m', 'b3d9d8ab-05e8-439f-89ad-5e978dd2a5eb', 475 '-m', '009d4faf-c275-46f0-8f2d-670b15bac2b0', 476 ] 477 478 GNOCCHI_AGGR_BY_RESOURCES_CLI_ARGS = COMMON_CLI_ARGS + [ 479 '--metric', 'cpu_util', 480 '--resource-type', 'instance', 481 '--query', '{"=": {"server_group":"my_autoscaling_group"}}' 482 ] 483 484 def setUp(self): 485 super(ShellAlarmGnocchiCommandTest, self).setUp() 486 self.cc = mock.Mock() 487 self.cc.alarms = mock.Mock() 488 self.args = mock.Mock() 489 490 @mock.patch('sys.stdout', new=six.StringIO()) 491 def test_do_alarm_gnocchi_resources_threshold_create(self): 492 493 alarm = alarms.Alarm(mock.Mock(), self.ALARM1) 494 self.cc.alarms.create.return_value = alarm 495 ceilometer_shell.do_alarm_gnocchi_resources_threshold_create(self.cc, 496 self.args) 497 self.assertEqual('''\ 498+---------------------------+--------------------------------------+ 499| Property | Value | 500+---------------------------+--------------------------------------+ 501| aggregation_method | count | 502| alarm_actions | ["http://something/alarm"] | 503| alarm_id | b69ecdb9-f19b-4fb5-950f-5eb53938b718 | 504| comparison_operator | le | 505| description | description_gnocchi_alarm | 506| enabled | True | 507| evaluation_periods | 3 | 508| granularity | 60 | 509| insufficient_data_actions | ["http://something/insufficient"] | 510| metric | cpu_util | 511| name | name_gnocchi_alarm | 512| ok_actions | ["http://something/ok"] | 513| project_id | 97fcad0402ce4f65ac3bd42a0c6a7e74 | 514| repeat_actions | True | 515| resource_id | 768ff714-8cfb-4db9-9753-d484cb33a1cc | 516| resource_type | instance | 517| severity | critical | 518| state | ok | 519| threshold | 70.0 | 520| time_constraints | [{name: cons1, | 521| | description: desc1, | 522| | start: 0 11 * * *, | 523| | duration: 300}, | 524| | {name: cons2, | 525| | description: desc2, | 526| | start: 0 23 * * *, | 527| | duration: 600}] | 528| type | gnocchi_resources_threshold | 529| user_id | f28735621ee84f329144eb467c91fce6 | 530+---------------------------+--------------------------------------+ 531''', sys.stdout.getvalue()) 532 533 @mock.patch('sys.stdout', new=six.StringIO()) 534 def test_do_alarm_gnocchi_aggr_by_metrics_threshold_create(self): 535 536 alarm = alarms.Alarm(mock.Mock(), self.ALARM2) 537 self.cc.alarms.create.return_value = alarm 538 ceilometer_shell.\ 539 do_alarm_gnocchi_aggregation_by_metrics_threshold_create( 540 self.cc, self.args) 541 stdout = sys.stdout.getvalue() 542 self.assertIn("b69ecdb9-f19b-4fb5-950f-5eb53938b718", stdout) 543 self.assertIn("[\"http://something/alarm\"]", stdout) 544 self.assertIn("description_gnocchi_alarm", stdout) 545 self.assertIn("gnocchi_aggregation_by_metrics_threshold", stdout) 546 547 @mock.patch('sys.stdout', new=six.StringIO()) 548 def test_do_alarm_gnocchi_aggr_by_resources_threshold_create(self): 549 550 alarm = alarms.Alarm(mock.Mock(), self.ALARM3) 551 self.cc.alarms.create.return_value = alarm 552 ceilometer_shell.\ 553 do_alarm_gnocchi_aggregation_by_resources_threshold_create( 554 self.cc, self.args) 555 self.assertEqual('''\ 556+---------------------------+------------------------------------------------+ 557| Property | Value | 558+---------------------------+------------------------------------------------+ 559| aggregation_method | count | 560| alarm_actions | ["http://something/alarm"] | 561| alarm_id | b69ecdb9-f19b-4fb5-950f-5eb53938b718 | 562| comparison_operator | le | 563| description | description_gnocchi_alarm | 564| enabled | True | 565| evaluation_periods | 3 | 566| granularity | 60 | 567| insufficient_data_actions | ["http://something/insufficient"] | 568| metric | cpu_util | 569| name | name_gnocchi_alarm | 570| ok_actions | ["http://something/ok"] | 571| project_id | 97fcad0402ce4f65ac3bd42a0c6a7e74 | 572| query | {"=": {"server_group":"my_autoscaling_group"}} | 573| repeat_actions | True | 574| resource_type | instance | 575| severity | critical | 576| state | ok | 577| threshold | 70.0 | 578| time_constraints | [{name: cons1, | 579| | description: desc1, | 580| | start: 0 11 * * *, | 581| | duration: 300}, | 582| | {name: cons2, | 583| | description: desc2, | 584| | start: 0 23 * * *, | 585| | duration: 600}] | 586| type | gnocchi_aggregation_by_resources_threshold | 587| user_id | f28735621ee84f329144eb467c91fce6 | 588+---------------------------+------------------------------------------------+ 589''', sys.stdout.getvalue()) 590 591 @mock.patch('sys.stdout', new=six.StringIO()) 592 def test_do_alarm_gnocchi_resources_threshold_create_args(self): 593 argv = ['alarm-gnocchi-resources-threshold-create'] 594 argv.extend(self.GNOCCHI_RESOURCES_CLI_ARGS) 595 self._test_alarm_gnocchi_resources_arguments('create', argv) 596 597 def test_do_alarm_gnocchi_resources_threshold_update_args(self): 598 argv = ['alarm-gnocchi-resources-threshold-update'] 599 argv.extend(self.GNOCCHI_RESOURCES_CLI_ARGS) 600 argv.append(self.ALARM_ID) 601 self._test_alarm_gnocchi_resources_arguments('update', argv) 602 603 @mock.patch('sys.stdout', new=six.StringIO()) 604 def test_do_alarm_gnocchi_aggr_by_metrics_threshold_create_args(self): 605 argv = ['alarm-gnocchi-aggregation-by-metrics-threshold-create'] 606 argv.extend(self.GNOCCHI_AGGR_BY_METRICS_CLI_ARGS) 607 self._test_alarm_gnocchi_aggr_by_metrics_arguments('create', argv) 608 609 def test_do_alarm_gnocchi_aggr_by_metrics_threshold_update_args(self): 610 argv = ['alarm-gnocchi-aggregation-by-metrics-threshold-update'] 611 argv.extend(self.GNOCCHI_AGGR_BY_METRICS_CLI_ARGS) 612 argv.append(self.ALARM_ID) 613 self._test_alarm_gnocchi_aggr_by_metrics_arguments('update', argv) 614 615 @mock.patch('sys.stdout', new=six.StringIO()) 616 def test_do_alarm_gnocchi_aggr_by_resources_threshold_create_args(self): 617 argv = ['alarm-gnocchi-aggregation-by-resources-threshold-create'] 618 argv.extend(self.GNOCCHI_AGGR_BY_RESOURCES_CLI_ARGS) 619 self._test_alarm_gnocchi_aggr_by_resources_arguments('create', argv) 620 621 def test_do_alarm_gnocchi_aggr_by_resources_threshold_update_args(self): 622 argv = ['alarm-gnocchi-aggregation-by-resources-threshold-update'] 623 argv.extend(self.GNOCCHI_AGGR_BY_RESOURCES_CLI_ARGS) 624 argv.append(self.ALARM_ID) 625 self._test_alarm_gnocchi_aggr_by_resources_arguments('update', argv) 626 627 @mock.patch('sys.stdout', new=six.StringIO()) 628 def _test_common_alarm_gnocchi_arguments(self, kwargs): 629 self.assertEqual('97fcad0402ce4f65ac3bd42a0c6a7e74', 630 kwargs.get('project_id')) 631 self.assertEqual('f28735621ee84f329144eb467c91fce6', 632 kwargs.get('user_id')) 633 self.assertEqual('name_gnocchi_alarm', kwargs.get('name')) 634 self.assertEqual('description_gnocchi_alarm', 635 kwargs.get('description')) 636 self.assertEqual(['http://something/alarm'], 637 kwargs.get('alarm_actions')) 638 self.assertEqual(['http://something/ok'], kwargs.get('ok_actions')) 639 self.assertEqual(['http://something/insufficient'], 640 kwargs.get('insufficient_data_actions')) 641 self.assertEqual('critical', kwargs.get('severity')) 642 self.assertEqual('ok', kwargs.get('state')) 643 self.assertEqual(True, kwargs.get('enabled')) 644 self.assertEqual(True, kwargs.get('repeat_actions')) 645 time_constraints = [dict(name='cons1', start='0 11 * * *', 646 duration='300', description='desc1'), 647 dict(name='cons2', start='0 23 * * *', 648 duration='600', description='desc2')] 649 self.assertEqual(time_constraints, kwargs['time_constraints']) 650 651 def _test_alarm_gnocchi_resources_arguments(self, action, argv): 652 self.make_env(test_shell.FAKE_V2_ENV) 653 with mock.patch.object(alarms.AlarmManager, action) as mocked: 654 with mock.patch('ceilometerclient.apiclient.' 655 'client.HTTPClient.client_request') as request: 656 request.site_effect = exceptions.EndpointNotFound 657 base_shell.main(argv) 658 args, kwargs = mocked.call_args 659 self.assertEqual('gnocchi_resources_threshold', kwargs.get('type')) 660 self.assertIn('gnocchi_resources_threshold_rule', kwargs) 661 rule = kwargs['gnocchi_resources_threshold_rule'] 662 self.assertEqual('cpu_util', rule.get('metric')) 663 self.assertEqual(70.0, rule.get('threshold')) 664 self.assertEqual(60, rule.get('granularity')) 665 self.assertEqual('count', rule.get('aggregation_method')) 666 self.assertEqual('le', rule.get('comparison_operator')) 667 self.assertEqual(3, rule.get('evaluation_periods')) 668 self.assertEqual('768ff714-8cfb-4db9-9753-d484cb33a1cc', 669 rule.get('resource_id')) 670 self.assertEqual('instance', rule.get('resource_type')) 671 self._test_common_alarm_gnocchi_arguments(kwargs) 672 673 def _test_alarm_gnocchi_aggr_by_metrics_arguments(self, action, argv): 674 self.make_env(test_shell.FAKE_V2_ENV) 675 with mock.patch.object(alarms.AlarmManager, action) as mocked: 676 with mock.patch('ceilometerclient.apiclient.' 677 'client.HTTPClient.client_request') as request: 678 request.site_effect = exceptions.EndpointNotFound 679 base_shell.main(argv) 680 args, kwargs = mocked.call_args 681 self.assertEqual('gnocchi_aggregation_by_metrics_threshold', 682 kwargs.get('type')) 683 self.assertIn('gnocchi_aggregation_by_metrics_threshold_rule', kwargs) 684 rule = kwargs['gnocchi_aggregation_by_metrics_threshold_rule'] 685 self.assertEqual(['b3d9d8ab-05e8-439f-89ad-5e978dd2a5eb', 686 '009d4faf-c275-46f0-8f2d-670b15bac2b0'], 687 rule.get('metrics')) 688 self.assertEqual(70.0, rule.get('threshold')) 689 self.assertEqual(60, rule.get('granularity')) 690 self.assertEqual('count', rule.get('aggregation_method')) 691 self.assertEqual('le', rule.get('comparison_operator')) 692 self.assertEqual(3, rule.get('evaluation_periods')) 693 self._test_common_alarm_gnocchi_arguments(kwargs) 694 695 def _test_alarm_gnocchi_aggr_by_resources_arguments(self, action, argv): 696 self.make_env(test_shell.FAKE_V2_ENV) 697 with mock.patch.object(alarms.AlarmManager, action) as mocked: 698 with mock.patch('ceilometerclient.apiclient.' 699 'client.HTTPClient.client_request') as request: 700 request.site_effect = exceptions.EndpointNotFound 701 base_shell.main(argv) 702 args, kwargs = mocked.call_args 703 self.assertEqual('gnocchi_aggregation_by_resources_threshold', 704 kwargs.get('type')) 705 self.assertIn('gnocchi_aggregation_by_resources_threshold_rule', 706 kwargs) 707 rule = kwargs['gnocchi_aggregation_by_resources_threshold_rule'] 708 self.assertEqual('cpu_util', rule.get('metric')) 709 self.assertEqual(70.0, rule.get('threshold')) 710 self.assertEqual(60, rule.get('granularity')) 711 self.assertEqual('count', rule.get('aggregation_method')) 712 self.assertEqual('le', rule.get('comparison_operator')) 713 self.assertEqual(3, rule.get('evaluation_periods')) 714 self.assertEqual('instance', rule.get('resource_type')) 715 self.assertEqual('{"=": {"server_group":"my_autoscaling_group"}}', 716 rule.get('query')) 717 self._test_common_alarm_gnocchi_arguments(kwargs) 718 719 720class ShellSampleListCommandTest(utils.BaseTestCase): 721 722 METER = 'cpu_util' 723 SAMPLE_VALUES = ( 724 ("cpu_util", 725 "5dcf5537-3161-4e25-9235-407e1385bd35", 726 "2013-10-15T05:50:30", 727 "%", 728 0.261666666667, 729 "gauge", 730 "86536501-b2c9-48f6-9c6a-7a5b14ba7482"), 731 ("cpu_util", 732 "87d197e9-9cf6-4c25-bc66-1b1f4cedb52f", 733 "2013-10-15T05:50:29", 734 "%", 735 0.261666666667, 736 "gauge", 737 "fe2a91ec-602b-4b55-8cba-5302ce3b916e",), 738 ("cpu_util", 739 "5dcf5537-3161-4e25-9235-407e1385bd35", 740 "2013-10-15T05:40:30", 741 "%", 742 0.251247920133, 743 "gauge", 744 "52768bcb-b4e9-4db9-a30c-738c758b6f43"), 745 ("cpu_util", 746 "87d197e9-9cf6-4c25-bc66-1b1f4cedb52f", 747 "2013-10-15T05:40:29", 748 "%", 749 0.26, 750 "gauge", 751 "31ae614a-ac6b-4fb9-b106-4667bae03308"), 752 ) 753 754 OLD_SAMPLES = [ 755 dict(counter_name=s[0], 756 resource_id=s[1], 757 timestamp=s[2], 758 counter_unit=s[3], 759 counter_volume=s[4], 760 counter_type=s[5]) 761 for s in SAMPLE_VALUES 762 ] 763 764 SAMPLES = [ 765 dict(meter=s[0], 766 resource_id=s[1], 767 timestamp=s[2], 768 unit=s[3], 769 volume=s[4], 770 type=s[5], 771 id=s[6]) 772 for s in SAMPLE_VALUES 773 ] 774 775 def setUp(self): 776 super(ShellSampleListCommandTest, self).setUp() 777 self.cc = mock.Mock() 778 self.cc.samples = mock.Mock() 779 self.cc.new_samples = mock.Mock() 780 self.args = mock.Mock() 781 self.args.query = None 782 self.args.limit = None 783 784 @mock.patch('sys.stdout', new=six.StringIO()) 785 def test_old_sample_list(self): 786 self.args.meter = self.METER 787 sample_list = [samples.OldSample(mock.Mock(), sample) 788 for sample in self.OLD_SAMPLES] 789 self.cc.samples.list.return_value = sample_list 790 791 ceilometer_shell.do_sample_list(self.cc, self.args) 792 self.cc.samples.list.assert_called_once_with( 793 meter_name=self.METER, 794 q=None, 795 limit=None) 796 797 self.assertEqual('''\ 798+--------------------------------------+----------+-------+----------------\ 799+------+---------------------+ 800| Resource ID | Name | Type | Volume \ 801| Unit | Timestamp | 802+--------------------------------------+----------+-------+----------------\ 803+------+---------------------+ 804| 5dcf5537-3161-4e25-9235-407e1385bd35 | cpu_util | gauge | 0.261666666667 \ 805| % | 2013-10-15T05:50:30 | 806| 87d197e9-9cf6-4c25-bc66-1b1f4cedb52f | cpu_util | gauge | 0.261666666667 \ 807| % | 2013-10-15T05:50:29 | 808| 5dcf5537-3161-4e25-9235-407e1385bd35 | cpu_util | gauge | 0.251247920133 \ 809| % | 2013-10-15T05:40:30 | 810| 87d197e9-9cf6-4c25-bc66-1b1f4cedb52f | cpu_util | gauge | 0.26 \ 811| % | 2013-10-15T05:40:29 | 812+--------------------------------------+----------+-------+----------------\ 813+------+---------------------+ 814''', sys.stdout.getvalue()) 815 816 @mock.patch('sys.stdout', new=six.StringIO()) 817 def test_sample_list(self): 818 self.args.meter = None 819 sample_list = [samples.Sample(mock.Mock(), sample) 820 for sample in self.SAMPLES] 821 self.cc.new_samples.list.return_value = sample_list 822 823 ceilometer_shell.do_sample_list(self.cc, self.args) 824 self.cc.new_samples.list.assert_called_once_with( 825 q=None, 826 limit=None) 827 828 self.assertEqual('''\ 829+--------------------------------------+--------------------------------------\ 830+----------+-------+----------------+------+---------------------+ 831| ID | Resource ID \ 832| Name | Type | Volume | Unit | Timestamp | 833+--------------------------------------+--------------------------------------\ 834+----------+-------+----------------+------+---------------------+ 835| 86536501-b2c9-48f6-9c6a-7a5b14ba7482 | 5dcf5537-3161-4e25-9235-407e1385bd35 \ 836| cpu_util | gauge | 0.261666666667 | % | 2013-10-15T05:50:30 | 837| fe2a91ec-602b-4b55-8cba-5302ce3b916e | 87d197e9-9cf6-4c25-bc66-1b1f4cedb52f \ 838| cpu_util | gauge | 0.261666666667 | % | 2013-10-15T05:50:29 | 839| 52768bcb-b4e9-4db9-a30c-738c758b6f43 | 5dcf5537-3161-4e25-9235-407e1385bd35 \ 840| cpu_util | gauge | 0.251247920133 | % | 2013-10-15T05:40:30 | 841| 31ae614a-ac6b-4fb9-b106-4667bae03308 | 87d197e9-9cf6-4c25-bc66-1b1f4cedb52f \ 842| cpu_util | gauge | 0.26 | % | 2013-10-15T05:40:29 | 843+--------------------------------------+--------------------------------------\ 844+----------+-------+----------------+------+---------------------+ 845''', sys.stdout.getvalue()) 846 847 848class ShellSampleShowCommandTest(utils.BaseTestCase): 849 850 SAMPLE = { 851 "user_id": None, 852 "resource_id": "9b651dfd-7d30-402b-972e-212b2c4bfb05", 853 "timestamp": "2014-11-03T13:37:46", 854 "meter": "image", 855 "volume": 1.0, 856 "source": "openstack", 857 "recorded_at": "2014-11-03T13:37:46.994458", 858 "project_id": "2cc3a7bb859b4bacbeab0aa9ca673033", 859 "type": "gauge", 860 "id": "98b5f258-635e-11e4-8bdd-0025647390c1", 861 "unit": "image", 862 "metadata": { 863 "name": "cirros-0.3.2-x86_64-uec", 864 } 865 } 866 867 def setUp(self): 868 super(ShellSampleShowCommandTest, self).setUp() 869 self.cc = mock.Mock() 870 self.cc.new_samples = mock.Mock() 871 self.args = mock.Mock() 872 self.args.sample_id = "98b5f258-635e-11e4-8bdd-0025647390c1" 873 874 @mock.patch('sys.stdout', new=six.StringIO()) 875 def test_sample_show(self): 876 sample = samples.Sample(mock.Mock(), self.SAMPLE) 877 self.cc.new_samples.get.return_value = sample 878 879 ceilometer_shell.do_sample_show(self.cc, self.args) 880 self.cc.new_samples.get.assert_called_once_with( 881 "98b5f258-635e-11e4-8bdd-0025647390c1") 882 883 self.assertEqual('''\ 884+-------------+--------------------------------------+ 885| Property | Value | 886+-------------+--------------------------------------+ 887| id | 98b5f258-635e-11e4-8bdd-0025647390c1 | 888| metadata | {"name": "cirros-0.3.2-x86_64-uec"} | 889| meter | image | 890| project_id | 2cc3a7bb859b4bacbeab0aa9ca673033 | 891| recorded_at | 2014-11-03T13:37:46.994458 | 892| resource_id | 9b651dfd-7d30-402b-972e-212b2c4bfb05 | 893| source | openstack | 894| timestamp | 2014-11-03T13:37:46 | 895| type | gauge | 896| unit | image | 897| user_id | None | 898| volume | 1.0 | 899+-------------+--------------------------------------+ 900''', sys.stdout.getvalue()) 901 902 @mock.patch('sys.stdout', new=six.StringIO()) 903 def test_sample_show_raises_command_err(self): 904 self.cc.new_samples.get.side_effect = exc.HTTPNotFound 905 906 self.assertRaises(exc.CommandError, ceilometer_shell.do_sample_show, 907 self.cc, self.args) 908 909 910class ShellSampleCreateCommandTest(utils.BaseTestCase): 911 912 METER = 'instance' 913 METER_TYPE = 'gauge' 914 RESOURCE_ID = '0564c64c-3545-4e34-abfb-9d18e5f2f2f9' 915 SAMPLE_VOLUME = '1' 916 METER_UNIT = 'instance' 917 SAMPLE = [{ 918 u'counter_name': u'instance', 919 u'user_id': u'21b442b8101d407d8242b6610e0ed0eb', 920 u'resource_id': u'0564c64c-3545-4e34-abfb-9d18e5f2f2f9', 921 u'timestamp': u'2014-01-10T03: 05: 33.951170', 922 u'message_id': u'1247cbe6-79a4-11e3-a296-000c294c58e2', 923 u'source': u'384260c6987b451d8290e66e1f108082: openstack', 924 u'counter_unit': u'instance', 925 u'counter_volume': 1.0, 926 u'project_id': u'384260c6987b451d8290e66e1f108082', 927 u'counter_type': u'gauge', 928 u'resource_metadata': {u'display_name': u'test_name'} 929 }] 930 931 def setUp(self): 932 super(ShellSampleCreateCommandTest, self).setUp() 933 self.cc = mock.Mock() 934 self.cc.samples = mock.Mock() 935 self.args = mock.Mock() 936 self.args.meter_name = self.METER 937 self.args.meter_type = self.METER_TYPE 938 self.args.meter_unit = self.METER_UNIT 939 self.args.resource_id = self.RESOURCE_ID 940 self.args.sample_volume = self.SAMPLE_VOLUME 941 942 @mock.patch('sys.stdout', new=six.StringIO()) 943 def test_sample_create(self): 944 ret_sample = [samples.OldSample(mock.Mock(), sample) 945 for sample in self.SAMPLE] 946 self.cc.samples.create.return_value = ret_sample 947 948 ceilometer_shell.do_sample_create(self.cc, self.args) 949 950 self.assertEqual('''\ 951+-------------------+---------------------------------------------+ 952| Property | Value | 953+-------------------+---------------------------------------------+ 954| message_id | 1247cbe6-79a4-11e3-a296-000c294c58e2 | 955| name | instance | 956| project_id | 384260c6987b451d8290e66e1f108082 | 957| resource_id | 0564c64c-3545-4e34-abfb-9d18e5f2f2f9 | 958| resource_metadata | {"display_name": "test_name"} | 959| source | 384260c6987b451d8290e66e1f108082: openstack | 960| timestamp | 2014-01-10T03: 05: 33.951170 | 961| type | gauge | 962| unit | instance | 963| user_id | 21b442b8101d407d8242b6610e0ed0eb | 964| volume | 1.0 | 965+-------------------+---------------------------------------------+ 966''', sys.stdout.getvalue()) 967 968 def test_sample_create_with_invalid_resource_metadata(self): 969 self.args.resource_metadata = 'foo=bar' 970 with mock.patch('ceilometerclient.exc.CommandError') as e: 971 e.return_value = exc.BaseException() 972 self.assertRaises(exc.BaseException, 973 ceilometer_shell.do_sample_create, 974 self.cc, self.args) 975 e.assert_called_with('Invalid resource metadata, it should be a' 976 ' json string, like: \'{"foo":"bar"}\'') 977 978 979class ShellSampleCreateListCommandTest(utils.BaseTestCase): 980 981 SAMPLE = { 982 u'counter_name': u'image', 983 u'user_id': u'21b442b8101d407d8242b6610e0ed0eb', 984 u'resource_id': u'0564c64c-3545-4e34-abfb-9d18e5f2f2f9', 985 u'timestamp': u'2015-05-19T12:00:08.368574', 986 u'source': u'384260c6987b451d8290e66e1f108082: openstack', 987 u'counter_unit': u'image', 988 u'counter_volume': 1.0, 989 u'project_id': u'384260c6987b451d8290e66e1f108082', 990 u'resource_metadata': {}, 991 u'counter_type': u'cumulative' 992 } 993 994 def setUp(self): 995 super(ShellSampleCreateListCommandTest, self).setUp() 996 self.cc = mock.Mock() 997 self.cc.samples = mock.Mock() 998 self.cc.samples.create_list = mock.Mock() 999 self.args = mock.Mock() 1000 self.samples = [self.SAMPLE] * 5 1001 self.args.samples_list = json.dumps(self.samples) 1002 1003 @mock.patch('sys.stdout', new=six.StringIO()) 1004 def test_sample_create_list(self): 1005 ret_samples = [samples.OldSample(mock.Mock(), 1006 sample) for sample in self.samples] 1007 self.cc.samples.create_list.return_value = ret_samples 1008 ceilometer_shell.do_sample_create_list(self.cc, self.args) 1009 self.cc.samples.create_list.assert_called_with(self.samples, 1010 direct=mock.ANY) 1011 self.assertEqual('''\ 1012+--------------------------------------+-------+------------+--------+-------\ 1013+----------------------------+ 1014| Resource ID | Name | Type | Volume | Unit \ 1015| Timestamp | 1016+--------------------------------------+-------+------------+--------+-------\ 1017+----------------------------+ 1018| 0564c64c-3545-4e34-abfb-9d18e5f2f2f9 | image | cumulative | 1.0 | image \ 1019| 2015-05-19T12:00:08.368574 | 1020| 0564c64c-3545-4e34-abfb-9d18e5f2f2f9 | image | cumulative | 1.0 | image \ 1021| 2015-05-19T12:00:08.368574 | 1022| 0564c64c-3545-4e34-abfb-9d18e5f2f2f9 | image | cumulative | 1.0 | image \ 1023| 2015-05-19T12:00:08.368574 | 1024| 0564c64c-3545-4e34-abfb-9d18e5f2f2f9 | image | cumulative | 1.0 | image \ 1025| 2015-05-19T12:00:08.368574 | 1026| 0564c64c-3545-4e34-abfb-9d18e5f2f2f9 | image | cumulative | 1.0 | image \ 1027| 2015-05-19T12:00:08.368574 | 1028+--------------------------------------+-------+------------+--------+-------\ 1029+----------------------------+ 1030''', sys.stdout.getvalue()) 1031 1032 1033class ShellQuerySamplesCommandTest(utils.BaseTestCase): 1034 1035 SAMPLE = [{u'id': u'b55d1526-9929-11e3-a3f6-02163e5df1e6', 1036 u'metadata': { 1037 u'name1': u'value1', 1038 u'name2': u'value2'}, 1039 u'meter': 'instance', 1040 u'project_id': u'35b17138-b364-4e6a-a131-8f3099c5be68', 1041 u'resource_id': u'bd9431c1-8d69-4ad3-803a-8d4a6b89fd36', 1042 u'source': u'openstack', 1043 u'timestamp': u'2014-02-19T05:50:16.673604', 1044 u'type': u'gauge', 1045 u'unit': u'instance', 1046 u'volume': 1, 1047 u'user_id': 'efd87807-12d2-4b38-9c70-5f5c2ac427ff'}] 1048 1049 QUERY = {"filter": {"and": [{"=": {"source": "openstack"}}, 1050 {">": {"timestamp": "2014-02-19T05:50:16"}}]}, 1051 "orderby": [{"timestamp": "desc"}, {"volume": "asc"}], 1052 "limit": 10} 1053 1054 def setUp(self): 1055 super(ShellQuerySamplesCommandTest, self).setUp() 1056 self.cc = mock.Mock() 1057 self.args = mock.Mock() 1058 self.args.filter = self.QUERY["filter"] 1059 self.args.orderby = self.QUERY["orderby"] 1060 self.args.limit = self.QUERY["limit"] 1061 1062 @mock.patch('sys.stdout', new=six.StringIO()) 1063 def test_query(self): 1064 ret_sample = [samples.Sample(mock.Mock(), sample) 1065 for sample in self.SAMPLE] 1066 self.cc.query_samples.query.return_value = ret_sample 1067 1068 ceilometer_shell.do_query_samples(self.cc, self.args) 1069 1070 self.assertEqual('''\ 1071+--------------------------------------+--------------------------------------\ 1072+----------+-------+--------+----------+----------------------------+ 1073| ID | Resource ID \ 1074| Meter | Type | Volume | Unit | Timestamp | 1075+--------------------------------------+--------------------------------------\ 1076+----------+-------+--------+----------+----------------------------+ 1077| b55d1526-9929-11e3-a3f6-02163e5df1e6 | bd9431c1-8d69-4ad3-803a-8d4a6b89fd36 \ 1078| instance | gauge | 1 | instance | 2014-02-19T05:50:16.673604 | 1079+--------------------------------------+--------------------------------------\ 1080+----------+-------+--------+----------+----------------------------+ 1081''', sys.stdout.getvalue()) 1082 1083 @mock.patch('sys.stdout', new=six.StringIO()) 1084 def test_query_raises_command_error(self): 1085 self.cc.query_samples.query.side_effect = exc.HTTPNotFound 1086 1087 self.assertRaises(exc.CommandError, 1088 ceilometer_shell.do_query_samples, 1089 self.cc, self.args) 1090 1091 1092class ShellQueryAlarmsCommandTest(utils.BaseTestCase): 1093 1094 ALARM = [{"alarm_actions": ["http://site:8000/alarm"], 1095 "alarm_id": "768ff714-8cfb-4db9-9753-d484cb33a1cc", 1096 "combination_rule": { 1097 "alarm_ids": [ 1098 "739e99cb-c2ec-4718-b900-332502355f38", 1099 "153462d0-a9b8-4b5b-8175-9e4b05e9b856"], 1100 "operator": "or"}, 1101 "description": "An alarm", 1102 "enabled": True, 1103 "insufficient_data_actions": ["http://site:8000/nodata"], 1104 "name": "SwiftObjectAlarm", 1105 "ok_actions": ["http://site:8000/ok"], 1106 "project_id": "c96c887c216949acbdfbd8b494863567", 1107 "repeat_actions": False, 1108 "state": "ok", 1109 "severity": "critical", 1110 "state_timestamp": "2014-02-20T10:37:15.589860", 1111 "threshold_rule": None, 1112 "timestamp": "2014-02-20T10:37:15.589856", 1113 "time_constraints": [{"name": "test", "start": "0 23 * * *", 1114 "duration": 10800}], 1115 "type": "combination", 1116 "user_id": "c96c887c216949acbdfbd8b494863567"}] 1117 1118 QUERY = {"filter": {"and": [{"!=": {"state": "ok"}}, 1119 {"=": {"type": "combination"}}]}, 1120 "orderby": [{"state_timestamp": "desc"}], 1121 "limit": 10} 1122 1123 def setUp(self): 1124 super(ShellQueryAlarmsCommandTest, self).setUp() 1125 self.cc = mock.Mock() 1126 self.args = mock.Mock() 1127 self.args.filter = self.QUERY["filter"] 1128 self.args.orderby = self.QUERY["orderby"] 1129 self.args.limit = self.QUERY["limit"] 1130 1131 @mock.patch('sys.stdout', new=six.StringIO()) 1132 def test_query(self): 1133 ret_alarm = [alarms.Alarm(mock.Mock(), alarm) 1134 for alarm in self.ALARM] 1135 self.cc.query_alarms.query.return_value = ret_alarm 1136 1137 ceilometer_shell.do_query_alarms(self.cc, self.args) 1138 1139 self.assertEqual('''\ 1140+--------------------------------------+------------------+-------+----------+\ 1141---------+------------+-------------------------------------------------------\ 1142-----------------------------------------------+-------------------------------\ 1143-+ 1144| Alarm ID | Name | State | Severity \ 1145| Enabled | Continuous | Alarm condition \ 1146 | Time constraints \ 1147 | 1148+--------------------------------------+------------------+-------+----------+\ 1149---------+------------+-------------------------------------------------------\ 1150-----------------------------------------------+--------------------------------+ 1151| 768ff714-8cfb-4db9-9753-d484cb33a1cc | SwiftObjectAlarm | ok | critical \ 1152| True | False | combinated states (OR) of \ 1153739e99cb-c2ec-4718-b900-332502355f38, 153462d0-a9b8-4b5b-8175-9e4b05e9b856 |\ 1154 test at 0 23 * * * for 10800s | 1155+--------------------------------------+------------------+-------+----------+\ 1156---------+------------+-------------------------------------------------------\ 1157-----------------------------------------------+------------------------------\ 1158--+ 1159''', sys.stdout.getvalue()) 1160 1161 @mock.patch('sys.stdout', new=six.StringIO()) 1162 def test_time_constraints_compatibility(self): 1163 # client should be backwards compatible 1164 alarm_without_tc = dict(self.ALARM[0]) 1165 del alarm_without_tc['time_constraints'] 1166 1167 # NOTE(nsaje): Since we're accessing a nonexisting key in the resource, 1168 # the resource is looking it up with the manager (which is a mock). 1169 manager_mock = mock.Mock() 1170 del manager_mock.get 1171 ret_alarm = [alarms.Alarm(manager_mock, alarm_without_tc)] 1172 self.cc.query_alarms.query.return_value = ret_alarm 1173 1174 ceilometer_shell.do_query_alarms(self.cc, self.args) 1175 1176 self.assertEqual('''\ 1177+--------------------------------------+------------------+-------+----------+\ 1178---------+------------+-------------------------------------------------------\ 1179-----------------------------------------------+------------------+ 1180| Alarm ID | Name | State | Severity \ 1181| Enabled | Continuous | Alarm condition \ 1182 | Time constraints | 1183+--------------------------------------+------------------+-------+----------+\ 1184---------+------------+-------------------------------------------------------\ 1185-----------------------------------------------+------------------+ 1186| 768ff714-8cfb-4db9-9753-d484cb33a1cc | SwiftObjectAlarm | ok | critical \ 1187| True | False | combinated states (OR) of \ 1188739e99cb-c2ec-4718-b900-332502355f38, 153462d0-a9b8-4b5b-8175-9e4b05e9b856 \ 1189| None | 1190+--------------------------------------+------------------+-------+----------+\ 1191---------+------------+-------------------------------------------------------\ 1192-----------------------------------------------+------------------+ 1193''', sys.stdout.getvalue()) 1194 1195 @mock.patch('sys.stdout', new=six.StringIO()) 1196 def test_query_raises_command_err(self): 1197 self.cc.query_alarms.query.side_effect = exc.HTTPNotFound 1198 self.assertRaises(exc.CommandError, 1199 ceilometer_shell.do_query_alarms, 1200 self.cc, self.args) 1201 1202 1203class ShellQueryAlarmHistoryCommandTest(utils.BaseTestCase): 1204 1205 ALARM_HISTORY = [{"alarm_id": "e8ff32f772a44a478182c3fe1f7cad6a", 1206 "event_id": "c74a8611-6553-4764-a860-c15a6aabb5d0", 1207 "detail": 1208 "{\"threshold\": 42.0, \"evaluation_periods\": 4}", 1209 "on_behalf_of": "92159030020611e3b26dde429e99ee8c", 1210 "project_id": "b6f16144010811e387e4de429e99ee8c", 1211 "timestamp": "2014-03-11T16:02:58.376261", 1212 "type": "rule change", 1213 "user_id": "3e5d11fda79448ac99ccefb20be187ca" 1214 }] 1215 1216 QUERY = {"filter": {"and": [{">": {"timestamp": "2014-03-11T16:02:58"}}, 1217 {"=": {"type": "rule change"}}]}, 1218 "orderby": [{"timestamp": "desc"}], 1219 "limit": 10} 1220 1221 def setUp(self): 1222 super(ShellQueryAlarmHistoryCommandTest, self).setUp() 1223 self.cc = mock.Mock() 1224 self.args = mock.Mock() 1225 self.args.filter = self.QUERY["filter"] 1226 self.args.orderby = self.QUERY["orderby"] 1227 self.args.limit = self.QUERY["limit"] 1228 1229 @mock.patch('sys.stdout', new=six.StringIO()) 1230 def test_query(self): 1231 ret_alarm_history = [alarms.AlarmChange(mock.Mock(), alarm_history) 1232 for alarm_history in self.ALARM_HISTORY] 1233 self.cc.query_alarm_history.query.return_value = ret_alarm_history 1234 1235 ceilometer_shell.do_query_alarm_history(self.cc, self.args) 1236 1237 self.assertEqual('''\ 1238+----------------------------------+--------------------------------------+-\ 1239------------+----------------------------------------------+----------------\ 1240------------+ 1241| Alarm ID | Event ID | \ 1242Type | Detail | Timestamp \ 1243 | 1244+----------------------------------+--------------------------------------+-\ 1245------------+----------------------------------------------+----------------\ 1246------------+ 1247| e8ff32f772a44a478182c3fe1f7cad6a | c74a8611-6553-4764-a860-c15a6aabb5d0 | \ 1248rule change | {"threshold": 42.0, "evaluation_periods": 4} | 2014-03-11T16:0\ 12492:58.376261 | 1250+----------------------------------+--------------------------------------+-\ 1251------------+----------------------------------------------+----------------\ 1252------------+ 1253''', sys.stdout.getvalue()) 1254 1255 @mock.patch('sys.stdout', new=six.StringIO()) 1256 def test_query_raises_command_err(self): 1257 self.cc.query_alarm_history.query.side_effect = exc.HTTPNotFound 1258 self.assertRaises(exc.CommandError, 1259 ceilometer_shell.do_query_alarm_history, 1260 self.cc, self.args) 1261 1262 1263class ShellStatisticsTest(utils.BaseTestCase): 1264 def setUp(self): 1265 super(ShellStatisticsTest, self).setUp() 1266 self.cc = mock.Mock() 1267 self.displays = { 1268 'duration': 'Duration', 1269 'duration_end': 'Duration End', 1270 'duration_start': 'Duration Start', 1271 'period': 'Period', 1272 'period_end': 'Period End', 1273 'period_start': 'Period Start', 1274 'groupby': 'Group By', 1275 'avg': 'Avg', 1276 'count': 'Count', 1277 'max': 'Max', 1278 'min': 'Min', 1279 'sum': 'Sum', 1280 'stddev': 'Standard deviation', 1281 'cardinality': 'Cardinality' 1282 } 1283 self.args = mock.Mock() 1284 self.args.meter_name = 'instance' 1285 self.args.aggregate = [] 1286 self.args.groupby = None 1287 self.args.query = None 1288 1289 def test_statistics_list_simple(self): 1290 samples = [ 1291 {u'count': 135, 1292 u'duration_start': u'2013-02-04T10:51:42', 1293 u'min': 1.0, 1294 u'max': 1.0, 1295 u'duration_end': 1296 u'2013-02-05T15:46:09', 1297 u'duration': 1734.0, 1298 u'avg': 1.0, 1299 u'sum': 135.0}, 1300 ] 1301 fields = [ 1302 'period', 1303 'period_start', 1304 'period_end', 1305 'max', 1306 'min', 1307 'avg', 1308 'sum', 1309 'count', 1310 'duration', 1311 'duration_start', 1312 'duration_end', 1313 ] 1314 statistics_ret = [ 1315 statistics.Statistics(mock.Mock(), sample) for sample in samples 1316 ] 1317 self.cc.statistics.list.return_value = statistics_ret 1318 with mock.patch('ceilometerclient.v2.shell.utils.print_list') as pmock: 1319 ceilometer_shell.do_statistics(self.cc, self.args) 1320 pmock.assert_called_with( 1321 statistics_ret, 1322 fields, 1323 [self.displays[f] for f in fields] 1324 ) 1325 1326 def test_statistics_list_groupby(self): 1327 samples = [ 1328 {u'count': 135, 1329 u'duration_start': u'2013-02-04T10:51:42', 1330 u'min': 1.0, 1331 u'max': 1.0, 1332 u'duration_end': 1333 u'2013-02-05T15:46:09', 1334 u'duration': 1734.0, 1335 u'avg': 1.0, 1336 u'sum': 135.0, 1337 u'groupby': {u'resource_id': u'foo'} 1338 }, 1339 {u'count': 12, 1340 u'duration_start': u'2013-02-04T10:51:42', 1341 u'min': 1.0, 1342 u'max': 1.0, 1343 u'duration_end': 1344 u'2013-02-05T15:46:09', 1345 u'duration': 1734.0, 1346 u'avg': 1.0, 1347 u'sum': 12.0, 1348 u'groupby': {u'resource_id': u'bar'} 1349 }, 1350 ] 1351 fields = [ 1352 'period', 1353 'period_start', 1354 'period_end', 1355 'groupby', 1356 'max', 1357 'min', 1358 'avg', 1359 'sum', 1360 'count', 1361 'duration', 1362 'duration_start', 1363 'duration_end', 1364 ] 1365 self.args.groupby = 'resource_id' 1366 statistics_ret = [ 1367 statistics.Statistics(mock.Mock(), sample) for sample in samples 1368 ] 1369 self.cc.statistics.list.return_value = statistics_ret 1370 with mock.patch('ceilometerclient.v2.shell.utils.print_list') as pmock: 1371 ceilometer_shell.do_statistics(self.cc, self.args) 1372 pmock.assert_called_with( 1373 statistics_ret, 1374 fields, 1375 [self.displays[f] for f in fields], 1376 ) 1377 1378 def test_statistics_list_aggregates(self): 1379 samples = [ 1380 {u'aggregate': {u'cardinality/resource_id': 4.0, u'count': 2.0}, 1381 u'count': 2, 1382 u'duration': 0.442451, 1383 u'duration_end': u'2014-03-12T14:00:21.774154', 1384 u'duration_start': u'2014-03-12T14:00:21.331703', 1385 u'groupby': None, 1386 u'period': 0, 1387 u'period_end': u'2014-03-12T14:00:21.774154', 1388 u'period_start': u'2014-03-12T14:00:21.331703', 1389 u'unit': u'instance', 1390 }, 1391 ] 1392 fields = [ 1393 'period', 1394 'period_start', 1395 'period_end', 1396 'count', 1397 'cardinality/resource_id', 1398 'duration', 1399 'duration_start', 1400 'duration_end', 1401 ] 1402 self.args.aggregate = ['count', 'cardinality<-resource_id'] 1403 statistics_ret = [ 1404 statistics.Statistics(mock.Mock(), sample) for sample in samples 1405 ] 1406 self.cc.statistics.list.return_value = statistics_ret 1407 with mock.patch('ceilometerclient.v2.shell.utils.print_list') as pmock: 1408 ceilometer_shell.do_statistics(self.cc, self.args) 1409 pmock.assert_called_with( 1410 statistics_ret, 1411 fields, 1412 [self.displays.get(f, f) for f in fields], 1413 ) 1414 1415 1416class ShellEmptyIdTest(utils.BaseTestCase): 1417 """Test empty field which will cause calling incorrect rest uri.""" 1418 1419 def _test_entity_action_with_empty_values(self, entity, 1420 *args, **kwargs): 1421 positional = kwargs.pop('positional', False) 1422 for value in ('', ' ', ' ', '\t'): 1423 self._test_entity_action_with_empty_value(entity, value, 1424 positional, *args) 1425 1426 def _test_entity_action_with_empty_value(self, entity, value, 1427 positional, *args): 1428 new_args = [value] if positional else ['--%s' % entity, value] 1429 argv = list(args) + new_args 1430 shell = base_shell.CeilometerShell() 1431 with mock.patch('ceilometerclient.exc.CommandError') as e: 1432 e.return_value = exc.BaseException() 1433 self.assertRaises(exc.BaseException, shell.parse_args, argv) 1434 entity = entity.replace('-', '_') 1435 e.assert_called_with('%s should not be empty' % entity) 1436 1437 def _test_alarm_action_with_empty_ids(self, method, *args): 1438 args = [method] + list(args) 1439 self._test_entity_action_with_empty_values('alarm_id', 1440 positional=True, *args) 1441 1442 def test_alarm_show_with_empty_id(self): 1443 self._test_alarm_action_with_empty_ids('alarm-show') 1444 1445 def test_alarm_update_with_empty_id(self): 1446 self._test_alarm_action_with_empty_ids('alarm-update') 1447 1448 def test_alarm_threshold_update_with_empty_id(self): 1449 self._test_alarm_action_with_empty_ids('alarm-threshold-update') 1450 1451 def test_alarm_combination_update_with_empty_id(self): 1452 self._test_alarm_action_with_empty_ids('alarm-combination-update') 1453 1454 def test_alarm_gnocchi_resources_update_with_empty_id(self): 1455 self._test_alarm_action_with_empty_ids( 1456 'alarm-gnocchi-resources-threshold-update') 1457 1458 def test_alarm_gnocchi_aggr_by_resources_update_with_empty_id(self): 1459 self._test_alarm_action_with_empty_ids( 1460 'alarm-gnocchi-aggregation-by-resources-threshold-update') 1461 1462 def test_alarm_gnocchi_aggr_by_metrics_update_with_empty_id(self): 1463 self._test_alarm_action_with_empty_ids( 1464 'alarm-gnocchi-aggregation-by-metrics-threshold-update') 1465 1466 def test_alarm_delete_with_empty_id(self): 1467 self._test_alarm_action_with_empty_ids('alarm-delete') 1468 1469 def test_alarm_state_get_with_empty_id(self): 1470 self._test_alarm_action_with_empty_ids('alarm-state-get') 1471 1472 def test_alarm_state_set_with_empty_id(self): 1473 args = ['alarm-state-set', '--state', 'ok'] 1474 self._test_alarm_action_with_empty_ids(*args) 1475 1476 def test_alarm_history_with_empty_id(self): 1477 self._test_alarm_action_with_empty_ids('alarm-history') 1478 1479 def test_event_show_with_empty_message_id(self): 1480 args = ['event-show'] 1481 self._test_entity_action_with_empty_values('message_id', *args) 1482 1483 def test_resource_show_with_empty_id(self): 1484 args = ['resource-show'] 1485 self._test_entity_action_with_empty_values('resource_id', *args) 1486 1487 def test_sample_list_with_empty_meter(self): 1488 args = ['sample-list'] 1489 self._test_entity_action_with_empty_values('meter', *args) 1490 1491 def test_sample_create_with_empty_meter(self): 1492 args = ['sample-create', '-r', 'x', '--meter-type', 'gauge', 1493 '--meter-unit', 'B', '--sample-volume', '1'] 1494 self._test_entity_action_with_empty_values('meter-name', *args) 1495 1496 def test_statistics_with_empty_meter(self): 1497 args = ['statistics'] 1498 self._test_entity_action_with_empty_values('meter', *args) 1499 1500 def test_trait_description_list_with_empty_event_type(self): 1501 args = ['trait-description-list'] 1502 self._test_entity_action_with_empty_values('event_type', *args) 1503 1504 def test_trait_list_with_empty_event_type(self): 1505 args = ['trait-list', '--trait_name', 'x'] 1506 self._test_entity_action_with_empty_values('event_type', *args) 1507 1508 def test_trait_list_with_empty_trait_name(self): 1509 args = ['trait-list', '--event_type', 'x'] 1510 self._test_entity_action_with_empty_values('trait_name', *args) 1511 1512 1513class ShellObsoletedArgsTest(utils.BaseTestCase): 1514 """Test arguments that have been obsoleted.""" 1515 1516 def _test_entity_obsoleted(self, entity, value, positional, *args): 1517 new_args = [value] if positional else ['--%s' % entity, value] 1518 argv = list(args) + new_args 1519 shell = base_shell.CeilometerShell() 1520 with mock.patch('sys.stdout', new_callable=six.StringIO) as stdout: 1521 shell.parse_args(argv) 1522 self.assertIn('obsolete', stdout.getvalue()) 1523 1524 def test_obsolete_alarm_id(self): 1525 for method in ['alarm-show', 'alarm-update', 'alarm-threshold-update', 1526 'alarm-combination-update', 'alarm-delete', 1527 'alarm-state-get', 'alarm-history']: 1528 self._test_entity_obsoleted('alarm_id', 'abcde', False, method) 1529 1530 1531class ShellEventListCommandTest(utils.BaseTestCase): 1532 1533 EVENTS = [ 1534 { 1535 "generated": "2015-01-12T04:03:25.741471", 1536 "message_id": "fb2bef58-88af-4380-8698-e0f18fcf452d", 1537 "event_type": "compute.instance.create.start", 1538 "traits": [{ 1539 "name": "state", 1540 "type": "string", 1541 "value": "building", 1542 }], 1543 }, 1544 { 1545 "generated": "2015-01-12T04:03:28.452495", 1546 "message_id": "9b20509a-576b-4995-acfa-1a24ee5cf49f", 1547 "event_type": "compute.instance.create.end", 1548 "traits": [{ 1549 "name": "state", 1550 "type": "string", 1551 "value": "active", 1552 }], 1553 }, 1554 ] 1555 1556 def setUp(self): 1557 super(ShellEventListCommandTest, self).setUp() 1558 self.cc = mock.Mock() 1559 self.args = mock.Mock() 1560 self.args.query = None 1561 self.args.no_traits = None 1562 self.args.limit = None 1563 1564 @mock.patch('sys.stdout', new=six.StringIO()) 1565 def test_event_list(self): 1566 ret_events = [events.Event(mock.Mock(), event) 1567 for event in self.EVENTS] 1568 self.cc.events.list.return_value = ret_events 1569 ceilometer_shell.do_event_list(self.cc, self.args) 1570 self.assertEqual('''\ 1571+--------------------------------------+-------------------------------+\ 1572----------------------------+-------------------------------+ 1573| Message ID | Event Type |\ 1574 Generated | Traits | 1575+--------------------------------------+-------------------------------+\ 1576----------------------------+-------------------------------+ 1577| fb2bef58-88af-4380-8698-e0f18fcf452d | compute.instance.create.start |\ 1578 2015-01-12T04:03:25.741471 | +-------+--------+----------+ | 1579| | |\ 1580 | | name | type | value | | 1581| | |\ 1582 | +-------+--------+----------+ | 1583| | |\ 1584 | | state | string | building | | 1585| | |\ 1586 | +-------+--------+----------+ | 1587| 9b20509a-576b-4995-acfa-1a24ee5cf49f | compute.instance.create.end |\ 1588 2015-01-12T04:03:28.452495 | +-------+--------+--------+ | 1589| | |\ 1590 | | name | type | value | | 1591| | |\ 1592 | +-------+--------+--------+ | 1593| | |\ 1594 | | state | string | active | | 1595| | |\ 1596 | +-------+--------+--------+ | 1597+--------------------------------------+-------------------------------+\ 1598----------------------------+-------------------------------+ 1599''', sys.stdout.getvalue()) 1600 1601 @mock.patch('sys.stdout', new=six.StringIO()) 1602 def test_event_list_no_traits(self): 1603 self.args.no_traits = True 1604 ret_events = [events.Event(mock.Mock(), event) 1605 for event in self.EVENTS] 1606 self.cc.events.list.return_value = ret_events 1607 ceilometer_shell.do_event_list(self.cc, self.args) 1608 self.assertEqual('''\ 1609+--------------------------------------+-------------------------------\ 1610+----------------------------+ 1611| Message ID | Event Type \ 1612| Generated | 1613+--------------------------------------+-------------------------------\ 1614+----------------------------+ 1615| fb2bef58-88af-4380-8698-e0f18fcf452d | compute.instance.create.start \ 1616| 2015-01-12T04:03:25.741471 | 1617| 9b20509a-576b-4995-acfa-1a24ee5cf49f | compute.instance.create.end \ 1618| 2015-01-12T04:03:28.452495 | 1619+--------------------------------------+-------------------------------\ 1620+----------------------------+ 1621''', sys.stdout.getvalue()) 1622 1623 1624class ShellShadowedArgsTest(test_shell.ShellTestBase): 1625 1626 def _test_shadowed_args_alarm(self, command, args, method): 1627 self.make_env(test_shell.FAKE_V2_ENV) 1628 cli_args = [ 1629 '--os-project-id', '0ba30185ddf44834914a0b859d244c56', 1630 '--os-user-id', '85f59b3b17484ccb974c50596023bf8c', 1631 '--debug', command, 1632 '--project-id', 'the-project-id-i-want-to-set', 1633 '--user-id', 'the-user-id-i-want-to-set', 1634 '--name', 'project-id-test'] + args 1635 with mock.patch.object(alarms.AlarmManager, method) as mocked: 1636 with mock.patch('ceilometerclient.apiclient.' 1637 'client.HTTPClient.client_request') as request: 1638 request.site_effect = exceptions.EndpointNotFound 1639 base_shell.main(cli_args) 1640 args, kwargs = mocked.call_args 1641 self.assertEqual('the-project-id-i-want-to-set', 1642 kwargs.get('project_id')) 1643 self.assertEqual('the-user-id-i-want-to-set', 1644 kwargs.get('user_id')) 1645 1646 def test_shadowed_args_threshold_alarm(self): 1647 cli_args = ['--meter-name', 'cpu', '--threshold', '90'] 1648 self._test_shadowed_args_alarm('alarm-create', cli_args, 'create') 1649 self._test_shadowed_args_alarm('alarm-threshold-create', 1650 cli_args, 'create') 1651 cli_args += ['--alarm_id', '437b7ed0-3733-4054-a877-e9a297b8be85'] 1652 self._test_shadowed_args_alarm('alarm-update', cli_args, 'update') 1653 self._test_shadowed_args_alarm('alarm-threshold-update', 1654 cli_args, 'update') 1655 1656 def test_shadowed_args_combination_alarm(self): 1657 cli_args = ['--alarm_ids', 'fb16a05a-669d-414e-8bbe-93aa381df6a8', 1658 '--alarm_ids', 'b189bcca-0a7b-49a9-a244-a927ac291881'] 1659 self._test_shadowed_args_alarm('alarm-combination-create', 1660 cli_args, 'create') 1661 cli_args += ['--alarm_id', '437b7ed0-3733-4054-a877-e9a297b8be85'] 1662 self._test_shadowed_args_alarm('alarm-combination-update', 1663 cli_args, 'update') 1664 1665 def test_shadowed_args_gnocchi_resources_threshold_alarm(self): 1666 cli_args = [ 1667 '--metric', 'cpu', 1668 '--threshold', '80', 1669 '--resource-type', 'instance', 1670 '--resource-id', 'fb16a05a-669d-414e-8bbe-93aa381df6a8', 1671 '--aggregation-method', 'last', 1672 ] 1673 self._test_shadowed_args_alarm('alarm-gnocchi-resources-' 1674 'threshold-create', 1675 cli_args, 'create') 1676 cli_args += ['--alarm_id', '437b7ed0-3733-4054-a877-e9a297b8be85'] 1677 self._test_shadowed_args_alarm('alarm-gnocchi-resources-' 1678 'threshold-update', 1679 cli_args, 'update') 1680 1681 def test_shadowed_args_gnocchi_aggr_by_resources_threshold_alarm(self): 1682 cli_args = [ 1683 '--metric', 'cpu', 1684 '--threshold', '80', 1685 '--resource-type', 'instance', 1686 '--aggregation-method', 'last', 1687 '--query', '"server_group":"my_autoscaling_group"', 1688 ] 1689 self._test_shadowed_args_alarm('alarm-gnocchi-aggregation-' 1690 'by-resources-threshold-create', 1691 cli_args, 'create') 1692 cli_args += ['--alarm_id', '437b7ed0-3733-4054-a877-e9a297b8be85'] 1693 self._test_shadowed_args_alarm('alarm-gnocchi-aggregation-' 1694 'by-resources-threshold-update', 1695 cli_args, 'update') 1696 1697 def test_shadowed_args_gnocchi_aggr_by_metrics_threshold_alarm(self): 1698 cli_args = [ 1699 '-m', 'b3d9d8ab-05e8-439f-89ad-5e978dd2a5eb', 1700 '-m', '009d4faf-c275-46f0-8f2d-670b15bac2b0', 1701 '--threshold', '80', 1702 '--aggregation-method', 'last', 1703 ] 1704 self._test_shadowed_args_alarm('alarm-gnocchi-aggregation-' 1705 'by-metrics-threshold-create', 1706 cli_args, 'create') 1707 cli_args += ['--alarm_id', '437b7ed0-3733-4054-a877-e9a297b8be85'] 1708 self._test_shadowed_args_alarm('alarm-gnocchi-aggregation-' 1709 'by-metrics-threshold-update', 1710 cli_args, 'update') 1711 1712 @mock.patch.object(samples.OldSampleManager, 'create') 1713 def test_shadowed_args_sample_create(self, mocked): 1714 self.make_env(test_shell.FAKE_V2_ENV) 1715 cli_args = [ 1716 '--os-project-id', '0ba30185ddf44834914a0b859d244c56', 1717 '--os-user-id', '85f59b3b17484ccb974c50596023bf8c', 1718 '--debug', 'sample-create', 1719 '--project-id', 'the-project-id-i-want-to-set', 1720 '--user-id', 'the-user-id-i-want-to-set', 1721 '--resource-id', 'b666633d-9bb6-4e05-89c0-ee5a8752fb0b', 1722 '--meter-name', 'cpu', 1723 '--meter-type', 'cumulative', 1724 '--meter-unit', 'ns', 1725 '--sample-volume', '10086', 1726 ] 1727 with mock.patch('ceilometerclient.apiclient.client.' 1728 'HTTPClient.client_request') as client_request: 1729 client_request.site_effect = exceptions.EndpointNotFound 1730 base_shell.main(cli_args) 1731 args, kwargs = mocked.call_args 1732 self.assertEqual('the-project-id-i-want-to-set', 1733 kwargs.get('project_id')) 1734 self.assertEqual('the-user-id-i-want-to-set', 1735 kwargs.get('user_id')) 1736 1737 1738class ShellCapabilityShowTest(utils.BaseTestCase): 1739 1740 CAPABILITIES = { 1741 "alarm_storage": { 1742 "storage:production_ready": True 1743 }, 1744 "api": { 1745 "alarms:query:complex": True, 1746 "alarms:query:simple": True 1747 }, 1748 "event_storage": { 1749 "storage:production_ready": True 1750 }, 1751 "storage": { 1752 "storage:production_ready": True 1753 }, 1754 } 1755 1756 def setUp(self): 1757 super(ShellCapabilityShowTest, self).setUp() 1758 self.cc = mock.Mock() 1759 self.args = mock.Mock() 1760 1761 @mock.patch('sys.stdout', new=six.StringIO()) 1762 def test_capability_show(self): 1763 _cap = capabilities.Capabilities(mock.Mock, self.CAPABILITIES) 1764 self.cc.capabilities.get.return_value = _cap 1765 1766 ceilometer_shell.do_capabilities(self.cc, self.args) 1767 self.assertEqual('''\ 1768+---------------+----------------------------------+ 1769| Property | Value | 1770+---------------+----------------------------------+ 1771| alarm_storage | "storage:production_ready": true | 1772| api | "alarms:query:complex": true, | 1773| | "alarms:query:simple": true | 1774| event_storage | "storage:production_ready": true | 1775| storage | "storage:production_ready": true | 1776+---------------+----------------------------------+ 1777''', sys.stdout.getvalue()) 1778 1779 1780class ShellMeterListCommandTest(utils.BaseTestCase): 1781 1782 METER = { 1783 "name": 'image', 1784 "resource_id": "resource-id", 1785 "meter": "image", 1786 "project_id": "project", 1787 "type": "gauge", 1788 "unit": "image", 1789 } 1790 1791 def setUp(self): 1792 super(ShellMeterListCommandTest, self).setUp() 1793 self.cc = mock.Mock() 1794 self.cc.meters.list = mock.Mock() 1795 self.args = mock.MagicMock() 1796 self.args.limit = None 1797 self.args.unique = False 1798 1799 @mock.patch('sys.stdout', new=six.StringIO()) 1800 def test_meter_list(self): 1801 meter = meters.Meter(mock.Mock(), self.METER) 1802 self.cc.meters.list.return_value = [meter] 1803 1804 ceilometer_shell.do_meter_list(self.cc, self.args) 1805 self.cc.meters.list.assert_called_once_with(q=[], limit=None, 1806 unique=False) 1807 1808 self.assertEqual('''\ 1809+-------+-------+-------+-------------+---------+------------+ 1810| Name | Type | Unit | Resource ID | User ID | Project ID | 1811+-------+-------+-------+-------------+---------+------------+ 1812| image | gauge | image | resource-id | | project | 1813+-------+-------+-------+-------------+---------+------------+ 1814''', sys.stdout.getvalue()) 1815 1816 @mock.patch('sys.stdout', new=six.StringIO()) 1817 def test_unique_meter_list(self): 1818 self.args.unique = True 1819 meter = meters.Meter(mock.Mock(), self.METER) 1820 self.cc.meters.list.return_value = [meter] 1821 1822 ceilometer_shell.do_meter_list(self.cc, self.args) 1823 self.cc.meters.list.assert_called_once_with(q=[], limit=None, 1824 unique=True) 1825 1826 self.assertEqual('''\ 1827+-------+-------+-------+-------------+---------+------------+ 1828| Name | Type | Unit | Resource ID | User ID | Project ID | 1829+-------+-------+-------+-------------+---------+------------+ 1830| image | gauge | image | resource-id | | project | 1831+-------+-------+-------+-------------+---------+------------+ 1832''', sys.stdout.getvalue()) 1833 1834 1835class ShellResourceListCommandTest(utils.BaseTestCase): 1836 1837 RESOURCE = { 1838 "source": "openstack", 1839 "resource_id": "resource-id", 1840 "project_id": "project", 1841 "user_id": "user" 1842 } 1843 1844 def setUp(self): 1845 super(ShellResourceListCommandTest, self).setUp() 1846 self.cc = mock.Mock() 1847 self.cc.resources.list = mock.Mock() 1848 self.args = mock.MagicMock() 1849 self.args.limit = None 1850 self.args.meter_links = None 1851 1852 @mock.patch('sys.stdout', new=six.StringIO()) 1853 def test_resource_list(self): 1854 resource = resources.Resource(mock.Mock(), self.RESOURCE) 1855 self.cc.resources.list.return_value = [resource] 1856 ceilometer_shell.do_resource_list(self.cc, self.args) 1857 self.cc.resources.list.assert_called_once_with(q=[], 1858 limit=None) 1859 1860 self.assertEqual('''\ 1861+-------------+-----------+---------+------------+ 1862| Resource ID | Source | User ID | Project ID | 1863+-------------+-----------+---------+------------+ 1864| resource-id | openstack | user | project | 1865+-------------+-----------+---------+------------+ 1866''', sys.stdout.getvalue()) 1867 1868 @mock.patch('sys.stdout', new=six.StringIO()) 1869 def test_resource_list_with_links(self): 1870 resource = resources.Resource(mock.Mock(), self.RESOURCE) 1871 self.cc.resources.list.return_value = [resource] 1872 ceilometer_shell.do_resource_list(self.cc, self.args) 1873 self.cc.resources.list.assert_called_once_with(q=[], 1874 limit=None) 1875 self.assertEqual('''\ 1876+-------------+-----------+---------+------------+ 1877| Resource ID | Source | User ID | Project ID | 1878+-------------+-----------+---------+------------+ 1879| resource-id | openstack | user | project | 1880+-------------+-----------+---------+------------+ 1881''', sys.stdout.getvalue()) 1882 1883 1884class ShellEventTypeListCommandTest(utils.BaseTestCase): 1885 1886 EVENT_TYPE = { 1887 "event_type": "test_event" 1888 } 1889 1890 def setUp(self): 1891 super(ShellEventTypeListCommandTest, self).setUp() 1892 self.cc = mock.Mock() 1893 self.cc.event_types.list = mock.Mock() 1894 self.args = mock.Mock() 1895 1896 @mock.patch('sys.stdout', new=six.StringIO()) 1897 def test_sample_show(self): 1898 event_type = event_types.EventType(mock.Mock(), self.EVENT_TYPE) 1899 self.cc.event_types.list.return_value = [event_type] 1900 1901 ceilometer_shell.do_event_type_list(self.cc, self.args) 1902 self.cc.event_types.list.assert_called_once_with() 1903 1904 self.assertEqual('''\ 1905+------------+ 1906| Event Type | 1907+------------+ 1908| test_event | 1909+------------+ 1910''', sys.stdout.getvalue()) 1911 1912 1913class ShellTraitsListCommandTest(utils.BaseTestCase): 1914 1915 TRAIT = { 1916 "name": "test", 1917 "value": "test", 1918 "type": "string", 1919 } 1920 1921 def setUp(self): 1922 super(ShellTraitsListCommandTest, self).setUp() 1923 self.cc = mock.Mock() 1924 self.cc.traits.list = mock.Mock() 1925 self.args = mock.Mock() 1926 self.args.event_type = "test" 1927 self.args.trait_name = "test" 1928 1929 @mock.patch('sys.stdout', new=six.StringIO()) 1930 def test_trait_list(self): 1931 trait = traits.Trait(mock.Mock(), self.TRAIT) 1932 self.cc.traits.list.return_value = [trait] 1933 1934 ceilometer_shell.do_trait_list(self.cc, self.args) 1935 self.cc.traits.list.assert_called_once_with(self.args.event_type, 1936 self.args.trait_name) 1937 1938 self.assertEqual('''\ 1939+------------+-------+-----------+ 1940| Trait Name | Value | Data Type | 1941+------------+-------+-----------+ 1942| test | test | string | 1943+------------+-------+-----------+ 1944''', sys.stdout.getvalue()) 1945 1946 1947class ShellTraitsDescriptionListCommandTest(utils.BaseTestCase): 1948 1949 TRAIT_DESCRIPTION = { 1950 "name": "test", 1951 "type": "string", 1952 } 1953 1954 def setUp(self): 1955 super(ShellTraitsDescriptionListCommandTest, self).setUp() 1956 self.cc = mock.Mock() 1957 self.cc.trait_descriptions.list = mock.Mock() 1958 self.args = mock.Mock() 1959 self.args.event_type = "test" 1960 1961 @mock.patch('sys.stdout', new=six.StringIO()) 1962 def test_traits_description_list(self): 1963 trait_desc = trait_descriptions.TraitDescription( 1964 mock.Mock(), self.TRAIT_DESCRIPTION) 1965 self.cc.trait_descriptions.list.return_value = [trait_desc] 1966 1967 ceilometer_shell.do_trait_description_list(self.cc, self.args) 1968 self.cc.trait_descriptions.list.assert_called_once_with( 1969 self.args.event_type) 1970 1971 self.assertEqual('''\ 1972+------------+-----------+ 1973| Trait Name | Data Type | 1974+------------+-----------+ 1975| test | string | 1976+------------+-----------+ 1977''', sys.stdout.getvalue()) 1978