~ubuntu-branches/ubuntu/vivid/ceilometer/vivid

« back to all changes in this revision

Viewing changes to ceilometer/tests/api/v2/test_alarm_scenarios.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2014-03-06 14:44:28 UTC
  • mto: (28.1.1 utopic-proposed) (1.2.1)
  • mto: This revision was merged to the branch mainline in revision 19.
  • Revision ID: package-import@ubuntu.com-20140306144428-rvphsh4igwyulzf0
Tags: upstream-2014.1~b3
ImportĀ upstreamĀ versionĀ 2014.1~b3

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
import uuid
27
27
 
28
28
import mock
 
29
 
 
30
from six import moves
29
31
import testscenarios
30
32
 
31
33
from ceilometer.storage import models
68
70
                         repeat_actions=True,
69
71
                         user_id=self.auth_headers['X-User-Id'],
70
72
                         project_id=self.auth_headers['X-Project-Id'],
 
73
                         time_constraints=[dict(name='testcons',
 
74
                                                start='0 11 * * *',
 
75
                                                duration=300)],
71
76
                         rule=dict(comparison_operator='gt',
72
77
                                   threshold=2.0,
73
78
                                   statistic='avg',
93
98
                         repeat_actions=False,
94
99
                         user_id=self.auth_headers['X-User-Id'],
95
100
                         project_id=self.auth_headers['X-Project-Id'],
 
101
                         time_constraints=[],
96
102
                         rule=dict(comparison_operator='gt',
97
103
                                   threshold=4.0,
98
104
                                   statistic='avg',
118
124
                         repeat_actions=False,
119
125
                         user_id=self.auth_headers['X-User-Id'],
120
126
                         project_id=self.auth_headers['X-Project-Id'],
 
127
                         time_constraints=[],
121
128
                         rule=dict(comparison_operator='gt',
122
129
                                   threshold=3.0,
123
130
                                   statistic='avg',
143
150
                         repeat_actions=False,
144
151
                         user_id=self.auth_headers['X-User-Id'],
145
152
                         project_id=self.auth_headers['X-Project-Id'],
 
153
                         time_constraints=[],
146
154
                         rule=dict(alarm_ids=['a', 'b'],
147
155
                                   operator='or')
148
156
                         )]:
149
157
            self.conn.update_alarm(alarm)
150
158
 
 
159
    @staticmethod
 
160
    def _add_default_threshold_rule(alarm):
 
161
        if 'exclude_outliers' not in alarm['threshold_rule']:
 
162
            alarm['threshold_rule']['exclude_outliers'] = False
 
163
 
 
164
    def _verify_alarm(self, json, alarm, expected_name=None):
 
165
        if expected_name and alarm.name != expected_name:
 
166
            self.fail("Alarm not found")
 
167
        self._add_default_threshold_rule(json)
 
168
        for key in json:
 
169
            if key.endswith('_rule'):
 
170
                storage_key = 'rule'
 
171
            else:
 
172
                storage_key = key
 
173
            self.assertEqual(getattr(alarm, storage_key),
 
174
                             json[key])
 
175
 
151
176
    def test_list_alarms(self):
152
177
        data = self.get_json('/alarms')
153
178
        self.assertEqual(4, len(data))
160
185
                             for r in data if 'combination_rule' in r),
161
186
                         set(['or']))
162
187
 
 
188
    def test_alarms_query_with_timestamp(self):
 
189
        date_time = datetime.datetime(2012, 7, 2, 10, 41)
 
190
        isotime = date_time.isoformat()
 
191
        resp = self.get_json('/alarms',
 
192
                             q=[{'field': 'timestamp',
 
193
                                 'op': 'gt',
 
194
                                 'value': isotime}],
 
195
                             expect_errors=True)
 
196
        self.assertEqual(resp.status_code, 400)
 
197
        self.assertEqual(jsonutils.loads(resp.body)['error_message']
 
198
                         ['faultstring'],
 
199
                         'Unknown argument: "timestamp": '
 
200
                         'not valid for this resource')
 
201
 
163
202
    def test_get_not_existing_alarm(self):
164
203
        resp = self.get_json('/alarms/alarm-id-3', expect_errors=True)
165
204
        self.assertEqual(resp.status_code, 404)
182
221
                         'meter.test')
183
222
        self.assertEqual(one['alarm_id'], alarms[0]['alarm_id'])
184
223
        self.assertEqual(one['repeat_actions'], alarms[0]['repeat_actions'])
 
224
        self.assertEqual(one['time_constraints'],
 
225
                         alarms[0]['time_constraints'])
185
226
 
186
227
    def test_get_alarm_disabled(self):
187
228
        alarm = models.Alarm(name='disabled',
198
239
                             repeat_actions=False,
199
240
                             user_id=self.auth_headers['X-User-Id'],
200
241
                             project_id=self.auth_headers['X-Project-Id'],
 
242
                             time_constraints=[],
201
243
                             rule=dict(alarm_ids=['a', 'b'], operator='or'))
202
244
        self.conn.update_alarm(alarm)
203
245
 
275
317
        alarms = list(self.conn.get_alarms())
276
318
        self.assertEqual(4, len(alarms))
277
319
 
 
320
    def test_post_invalid_alarm_time_constraint_start(self):
 
321
        json = {
 
322
            'name': 'added_alarm_invalid_constraint_duration',
 
323
            'type': 'threshold',
 
324
            'time_constraints': [
 
325
                {
 
326
                    'name': 'testcons',
 
327
                    'start': '11:00am',
 
328
                    'duration': 10
 
329
                }
 
330
            ],
 
331
            'threshold_rule': {
 
332
                'meter_name': 'ameter',
 
333
                'threshold': 300.0
 
334
            }
 
335
        }
 
336
        self.post_json('/alarms', params=json, expect_errors=True, status=400,
 
337
                       headers=self.auth_headers)
 
338
        alarms = list(self.conn.get_alarms())
 
339
        self.assertEqual(4, len(alarms))
 
340
 
 
341
    def test_post_invalid_alarm_time_constraint_duration(self):
 
342
        json = {
 
343
            'name': 'added_alarm_invalid_constraint_duration',
 
344
            'type': 'threshold',
 
345
            'time_constraints': [
 
346
                {
 
347
                    'name': 'testcons',
 
348
                    'start': '* 11 * * *',
 
349
                    'duration': -1,
 
350
                }
 
351
            ],
 
352
            'threshold_rule': {
 
353
                'meter_name': 'ameter',
 
354
                'threshold': 300.0
 
355
            }
 
356
        }
 
357
        self.post_json('/alarms', params=json, expect_errors=True, status=400,
 
358
                       headers=self.auth_headers)
 
359
        alarms = list(self.conn.get_alarms())
 
360
        self.assertEqual(4, len(alarms))
 
361
 
 
362
    def test_post_invalid_alarm_time_constraint_timezone(self):
 
363
        json = {
 
364
            'name': 'added_alarm_invalid_constraint_timezone',
 
365
            'type': 'threshold',
 
366
            'time_constraints': [
 
367
                {
 
368
                    'name': 'testcons',
 
369
                    'start': '* 11 * * *',
 
370
                    'duration': 10,
 
371
                    'timezone': 'aaaa'
 
372
                }
 
373
            ],
 
374
            'threshold_rule': {
 
375
                'meter_name': 'ameter',
 
376
                'threshold': 300.0
 
377
            }
 
378
        }
 
379
        self.post_json('/alarms', params=json, expect_errors=True, status=400,
 
380
                       headers=self.auth_headers)
 
381
        alarms = list(self.conn.get_alarms())
 
382
        self.assertEqual(4, len(alarms))
 
383
 
278
384
    def test_post_invalid_alarm_period(self):
279
385
        json = {
280
386
            'name': 'added_alarm_invalid_period',
367
473
            'threshold_rule and combination_rule cannot '
368
474
            'be set at the same time')
369
475
 
 
476
    def test_post_invalid_alarm_timestamp_in_threshold_rule(self):
 
477
        date_time = datetime.datetime(2012, 7, 2, 10, 41)
 
478
        isotime = date_time.isoformat()
 
479
 
 
480
        json = {
 
481
            'name': 'invalid_alarm',
 
482
            'type': 'threshold',
 
483
            'threshold_rule': {
 
484
                'meter_name': 'ameter',
 
485
                'query': [{'field': 'timestamp',
 
486
                           'op': 'gt',
 
487
                           'value': isotime}],
 
488
                'comparison_operator': 'gt',
 
489
                'threshold': 2.0,
 
490
            }
 
491
        }
 
492
        resp = self.post_json('/alarms', params=json, expect_errors=True,
 
493
                              status=400, headers=self.auth_headers)
 
494
        alarms = list(self.conn.get_alarms())
 
495
        self.assertEqual(4, len(alarms))
 
496
        self.assertEqual(
 
497
            'Unknown argument: "timestamp": '
 
498
            'not valid for this resource',
 
499
            resp.json['error_message']['faultstring'])
 
500
 
370
501
    def test_post_alarm_defaults(self):
371
502
        to_check = {
372
503
            'enabled': True,
392
523
            }
393
524
 
394
525
        }
 
526
        self._add_default_threshold_rule(to_check)
395
527
 
396
528
        json = {
397
529
            'name': 'added_alarm_defaults',
418
550
        else:
419
551
            self.fail("Alarm not found")
420
552
 
421
 
    def test_post_alarm(self):
422
 
        json = {
423
 
            'enabled': False,
424
 
            'name': 'added_alarm',
425
 
            'state': 'ok',
426
 
            'type': 'threshold',
427
 
            'ok_actions': ['http://something/ok'],
428
 
            'alarm_actions': ['http://something/alarm'],
429
 
            'insufficient_data_actions': ['http://something/no'],
430
 
            'repeat_actions': True,
431
 
            'threshold_rule': {
432
 
                'meter_name': 'ameter',
433
 
                'query': [{'field': 'metadata.field',
434
 
                           'op': 'eq',
435
 
                           'value': '5',
436
 
                           'type': 'string'}],
437
 
                'comparison_operator': 'le',
438
 
                'statistic': 'count',
439
 
                'threshold': 50,
440
 
                'evaluation_periods': '3',
441
 
                'period': '180',
442
 
            }
443
 
        }
 
553
    def test_post_conflict(self):
 
554
        json = {
 
555
            'enabled': False,
 
556
            'name': 'added_alarm',
 
557
            'state': 'ok',
 
558
            'type': 'threshold',
 
559
            'ok_actions': ['http://something/ok'],
 
560
            'alarm_actions': ['http://something/alarm'],
 
561
            'insufficient_data_actions': ['http://something/no'],
 
562
            'repeat_actions': True,
 
563
            'threshold_rule': {
 
564
                'meter_name': 'ameter',
 
565
                'query': [{'field': 'metadata.field',
 
566
                           'op': 'eq',
 
567
                           'value': '5',
 
568
                           'type': 'string'}],
 
569
                'comparison_operator': 'le',
 
570
                'statistic': 'count',
 
571
                'threshold': 50,
 
572
                'evaluation_periods': '3',
 
573
                'period': '180',
 
574
            }
 
575
        }
 
576
 
 
577
        self.post_json('/alarms', params=json, status=201,
 
578
                       headers=self.auth_headers)
 
579
        self.post_json('/alarms', params=json, status=409,
 
580
                       headers=self.auth_headers)
 
581
 
 
582
    def _do_test_post_alarm(self, exclude_outliers=None):
 
583
        json = {
 
584
            'enabled': False,
 
585
            'name': 'added_alarm',
 
586
            'state': 'ok',
 
587
            'type': 'threshold',
 
588
            'ok_actions': ['http://something/ok'],
 
589
            'alarm_actions': ['http://something/alarm'],
 
590
            'insufficient_data_actions': ['http://something/no'],
 
591
            'repeat_actions': True,
 
592
            'threshold_rule': {
 
593
                'meter_name': 'ameter',
 
594
                'query': [{'field': 'metadata.field',
 
595
                           'op': 'eq',
 
596
                           'value': '5',
 
597
                           'type': 'string'}],
 
598
                'comparison_operator': 'le',
 
599
                'statistic': 'count',
 
600
                'threshold': 50,
 
601
                'evaluation_periods': '3',
 
602
                'period': '180',
 
603
            }
 
604
        }
 
605
        if exclude_outliers is not None:
 
606
            json['threshold_rule']['exclude_outliers'] = exclude_outliers
 
607
 
444
608
        self.post_json('/alarms', params=json, status=201,
445
609
                       headers=self.auth_headers)
446
610
        alarms = list(self.conn.get_alarms(enabled=False))
448
612
        json['threshold_rule']['query'].append({
449
613
            'field': 'project_id', 'op': 'eq',
450
614
            'value': self.auth_headers['X-Project-Id']})
 
615
        # to check to IntegerType type conversion
 
616
        json['threshold_rule']['evaluation_periods'] = 3
 
617
        json['threshold_rule']['period'] = 180
 
618
        self._verify_alarm(json, alarms[0], 'added_alarm')
 
619
 
 
620
    def test_post_alarm_outlier_exclusion_set(self):
 
621
        self._do_test_post_alarm(True)
 
622
 
 
623
    def test_post_alarm_outlier_exclusion_clear(self):
 
624
        self._do_test_post_alarm(False)
 
625
 
 
626
    def test_post_alarm_outlier_exclusion_defaulted(self):
 
627
        self._do_test_post_alarm()
 
628
 
 
629
    def test_post_alarm_noauth(self):
 
630
        json = {
 
631
            'enabled': False,
 
632
            'name': 'added_alarm',
 
633
            'state': 'ok',
 
634
            'type': 'threshold',
 
635
            'ok_actions': ['http://something/ok'],
 
636
            'alarm_actions': ['http://something/alarm'],
 
637
            'insufficient_data_actions': ['http://something/no'],
 
638
            'repeat_actions': True,
 
639
            'threshold_rule': {
 
640
                'meter_name': 'ameter',
 
641
                'query': [{'field': 'metadata.field',
 
642
                           'op': 'eq',
 
643
                           'value': '5',
 
644
                           'type': 'string'}],
 
645
                'comparison_operator': 'le',
 
646
                'statistic': 'count',
 
647
                'threshold': 50,
 
648
                'evaluation_periods': '3',
 
649
                'exclude_outliers': False,
 
650
                'period': '180',
 
651
            }
 
652
        }
 
653
        self.post_json('/alarms', params=json, status=201)
 
654
        alarms = list(self.conn.get_alarms(enabled=False))
 
655
        self.assertEqual(1, len(alarms))
451
656
        # to check to BoundedInt type conversion
452
657
        json['threshold_rule']['evaluation_periods'] = 3
453
658
        json['threshold_rule']['period'] = 180
497
702
        self.assertEqual(1, len(alarms))
498
703
        self.assertEqual(alarms[0].user_id, 'auseridthatisnotmine')
499
704
        self.assertEqual(alarms[0].project_id, 'aprojectidthatisnotmine')
 
705
        self._add_default_threshold_rule(json)
500
706
        if alarms[0].name == 'added_alarm':
501
707
            for key in json:
502
708
                if key.endswith('_rule'):
564
770
        self.assertEqual(1, len(alarms))
565
771
        self.assertEqual(alarms[0].user_id, self.auth_headers['X-User-Id'])
566
772
        self.assertEqual(alarms[0].project_id, 'aprojectidthatisnotmine')
567
 
        if alarms[0].name == 'added_alarm':
568
 
            for key in json:
569
 
                if key.endswith('_rule'):
570
 
                    storage_key = 'rule'
571
 
                else:
572
 
                    storage_key = key
573
 
                self.assertEqual(getattr(alarms[0], storage_key),
574
 
                                 json[key])
575
 
        else:
576
 
            self.fail("Alarm not found")
 
773
        self._verify_alarm(json, alarms[0], 'added_alarm')
577
774
 
578
775
    def test_post_alarm_as_admin_no_project(self):
579
776
        """Test the creation of an alarm as admin for another project but
610
807
        self.assertEqual(alarms[0].user_id, 'auseridthatisnotmine')
611
808
        self.assertEqual(alarms[0].project_id,
612
809
                         self.auth_headers['X-Project-Id'])
613
 
        if alarms[0].name == 'added_alarm':
614
 
            for key in json:
615
 
                if key.endswith('_rule'):
616
 
                    storage_key = 'rule'
617
 
                else:
618
 
                    storage_key = key
619
 
                self.assertEqual(getattr(alarms[0], storage_key),
620
 
                                 json[key])
621
 
        else:
622
 
            self.fail("Alarm not found")
 
810
        self._verify_alarm(json, alarms[0], 'added_alarm')
623
811
 
624
812
    def test_post_alarm_combination(self):
625
813
        json = {
654
842
 
655
843
    def test_post_combination_alarm_as_user_with_unauthorized_alarm(self):
656
844
        """Test that post a combination alarm as normal user/project
657
 
        with a alarm_id unauthorized for this project/user
 
845
        with an alarm_id unauthorized for this project/user
658
846
        """
659
847
        json = {
660
848
            'enabled': False,
673
861
        }
674
862
        an_other_user_auth = {'X-User-Id': str(uuid.uuid4()),
675
863
                              'X-Project-Id': str(uuid.uuid4())}
676
 
        resp = self.post_json('/alarms', params=json, status=400,
 
864
        resp = self.post_json('/alarms', params=json, status=404,
677
865
                              headers=an_other_user_auth)
678
 
        self.assertEqual(jsonutils.loads(resp.body)['error_message']
679
 
                         ['faultstring'],
680
 
                         "Alarm a doesn't exist")
 
866
        self.assertEqual("Alarm a Not Found",
 
867
                         jsonutils.loads(resp.body)['error_message']
 
868
                         ['faultstring'])
681
869
 
682
870
    def test_post_combination_alarm_as_admin_on_behalf_of_an_other_user(self):
683
871
        """Test that post a combination alarm as admin on behalf of an other
684
 
        user/project with a alarm_id unauthorized for this project/user
 
872
        user/project with an alarm_id unauthorized for this project/user
685
873
        """
686
874
        json = {
687
875
            'enabled': False,
704
892
        headers = {}
705
893
        headers.update(self.auth_headers)
706
894
        headers['X-Roles'] = 'admin'
707
 
        resp = self.post_json('/alarms', params=json, status=400,
 
895
        resp = self.post_json('/alarms', params=json, status=404,
708
896
                              headers=headers)
709
 
        self.assertEqual(jsonutils.loads(resp.body)['error_message']
710
 
                         ['faultstring'],
711
 
                         "Alarm a doesn't exist")
 
897
        self.assertEqual("Alarm a Not Found",
 
898
                         jsonutils.loads(resp.body)['error_message']
 
899
                         ['faultstring'])
 
900
 
 
901
    def test_post_combination_alarm_with_reasonable_description(self):
 
902
        """Test that post a combination alarm with two blanks around the
 
903
        operator in alarm description.
 
904
        """
 
905
        json = {
 
906
            'enabled': False,
 
907
            'name': 'added_alarm',
 
908
            'state': 'ok',
 
909
            'type': 'combination',
 
910
            'ok_actions': ['http://something/ok'],
 
911
            'alarm_actions': ['http://something/alarm'],
 
912
            'insufficient_data_actions': ['http://something/no'],
 
913
            'repeat_actions': True,
 
914
            'combination_rule': {
 
915
                'alarm_ids': ['a',
 
916
                              'b'],
 
917
                'operator': 'and',
 
918
            }
 
919
        }
 
920
        self.post_json('/alarms', params=json, status=201,
 
921
                       headers=self.auth_headers)
 
922
        alarms = list(self.conn.get_alarms(enabled=False))
 
923
        self.assertEqual(1, len(alarms))
 
924
        self.assertEqual(u'Combined state of alarms a and b',
 
925
                         alarms[0].description)
712
926
 
713
927
    def test_post_combination_alarm_as_admin_success_owner_unset(self):
714
928
        self._do_post_combination_alarm_as_admin_success(False)
718
932
 
719
933
    def _do_post_combination_alarm_as_admin_success(self, owner_is_set):
720
934
        """Test that post a combination alarm as admin on behalf of nobody
721
 
        with a alarm_id of someone else, with owner set or not
 
935
        with an alarm_id of someone else, with owner set or not
722
936
        """
723
937
        json = {
724
938
            'enabled': False,
774
988
                'operator': 'and',
775
989
            }
776
990
        }
777
 
        self.post_json('/alarms', params=json, status=400,
 
991
        self.post_json('/alarms', params=json, status=404,
778
992
                       headers=self.auth_headers)
779
993
        alarms = list(self.conn.get_alarms(enabled=False))
780
994
        self.assertEqual(0, len(alarms))
816
1030
        json['threshold_rule']['query'].append({
817
1031
            'field': 'project_id', 'op': 'eq',
818
1032
            'value': self.auth_headers['X-Project-Id']})
819
 
        for key in json:
820
 
            if key.endswith('_rule'):
821
 
                storage_key = 'rule'
822
 
            else:
823
 
                storage_key = key
824
 
            self.assertEqual(getattr(alarm, storage_key), json[key])
 
1033
        self._verify_alarm(json, alarm)
825
1034
 
826
1035
    def test_put_alarm_as_admin(self):
827
1036
        json = {
868
1077
        alarm = list(self.conn.get_alarms(alarm_id=alarm_id, enabled=False))[0]
869
1078
        self.assertEqual(alarm.user_id, 'myuserid')
870
1079
        self.assertEqual(alarm.project_id, 'myprojectid')
871
 
        for key in json:
872
 
            if key.endswith('_rule'):
873
 
                storage_key = 'rule'
874
 
            else:
875
 
                storage_key = key
876
 
            self.assertEqual(getattr(alarm, storage_key), json[key])
 
1080
        self._verify_alarm(json, alarm)
877
1081
 
878
1082
    def test_put_alarm_wrong_field(self):
879
1083
        # Note: wsme will ignore unknown fields so will just not appear in
909
1113
        alarm_id = data[0]['alarm_id']
910
1114
 
911
1115
        resp = self.put_json('/alarms/%s' % alarm_id,
912
 
                             expect_errors=True,
913
1116
                             params=json,
914
1117
                             headers=self.auth_headers)
915
 
 
916
 
        json['threshold_rule']['query'].append({
917
 
            'field': 'project_id', 'op': 'eq',
918
 
            'value': self.auth_headers['X-Project-Id']})
919
1118
        self.assertEqual(resp.status_code, 200)
920
1119
 
921
1120
    def test_delete_alarm(self):
994
1193
        self.assertIsNotNone(actual['event_id'])
995
1194
 
996
1195
    def _assert_in_json(self, expected, actual):
 
1196
        actual = jsonutils.dumps(jsonutils.loads(actual), sort_keys=True)
997
1197
        for k, v in expected.iteritems():
998
 
            fragment = jsonutils.dumps({k: v})[1:-1]
 
1198
            fragment = jsonutils.dumps({k: v}, sort_keys=True)[1:-1]
999
1199
            self.assertTrue(fragment in actual,
1000
1200
                            '%s not in %s' % (fragment, actual))
1001
1201
 
1044
1244
                                    type='creation',
1045
1245
                                    user_id=alarm['user_id']),
1046
1246
                               history[0])
 
1247
        self._add_default_threshold_rule(new_alarm)
1047
1248
        new_alarm['rule'] = new_alarm['threshold_rule']
1048
1249
        del new_alarm['threshold_rule']
1049
1250
        new_alarm['rule']['query'].append({
1116
1317
        data = dict(state='alarm')
1117
1318
        self._update_alarm(alarm, data, auth_headers=admin_auth)
1118
1319
 
 
1320
        self._add_default_threshold_rule(new_alarm)
1119
1321
        new_alarm['rule'] = new_alarm['threshold_rule']
1120
1322
        del new_alarm['threshold_rule']
1121
1323
 
1191
1393
 
1192
1394
    def test_get_alarm_history_ordered_by_recentness(self):
1193
1395
        alarm = self._get_alarm('a')
1194
 
        for i in xrange(10):
 
1396
        for i in moves.xrange(10):
1195
1397
            self._update_alarm(alarm, dict(name='%s' % i))
1196
1398
        alarm = self._get_alarm('a')
1197
1399
        self._delete_alarm(alarm)
1203
1405
        alarm['rule'] = alarm['threshold_rule']
1204
1406
        del alarm['threshold_rule']
1205
1407
        self._assert_in_json(alarm, history[0]['detail'])
1206
 
        for i in xrange(1, 10):
 
1408
        for i in moves.xrange(1, 10):
1207
1409
            detail = '{"name": "%s"}' % (10 - i)
1208
1410
            self._assert_is_subset(dict(alarm_id=alarm['alarm_id'],
1209
1411
                                        detail=detail,