Notification Engine for Monasca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

test_alarm_processor.py 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. # (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP
  2. # Copyright 2017 Fujitsu LIMITED
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  13. # implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. """Tests the AlarmProcessor"""
  17. import collections
  18. import json
  19. import mock
  20. import time
  21. from monasca_notification import notification as m_notification
  22. from monasca_notification.processors import alarm_processor
  23. from tests import base
  24. alarm_tuple = collections.namedtuple('alarm_tuple', ['offset', 'message'])
  25. message_tuple = collections.namedtuple('message_tuple', ['value'])
  26. class TestAlarmProcessor(base.BaseTestCase):
  27. def setUp(self):
  28. super(TestAlarmProcessor, self).setUp()
  29. self.trap = []
  30. def _create_raw_alarm(self, partition, offset, message):
  31. """Create a raw alarm, with the given message dictionary.
  32. """
  33. json_msg = json.dumps({'alarm-transitioned': message})
  34. msg_tuple = message_tuple(json_msg)
  35. return [partition, alarm_tuple(offset, msg_tuple)]
  36. @mock.patch('pymysql.connect')
  37. @mock.patch('monasca_notification.processors.alarm_processor.log')
  38. def _run_alarm_processor(self, alarm, sql_response, mock_log, mock_mysql):
  39. """Runs a mocked alarm processor reading from queue while running,
  40. returns (queue_message, log_message)
  41. """
  42. # Since the log runs in another thread I can mock it directly, instead
  43. # change the methods to put to a queue
  44. mock_log.warn = self.trap.append
  45. mock_log.error = self.trap.append
  46. mock_log.exception = self.trap.append
  47. # Setup the sql response
  48. if sql_response is not None:
  49. mock_mysql.return_value = mock_mysql
  50. mock_mysql.cursor.return_value = mock_mysql
  51. mock_mysql.__iter__.return_value = sql_response
  52. self.conf_override(group='mysql', ssl=None,
  53. host='localhost', port='3306',
  54. user='mysql_user', db='dbname',
  55. passwd='mysql_passwd')
  56. self.conf_override(group='statsd', host='localhost',
  57. port=8125)
  58. processor = alarm_processor.AlarmProcessor()
  59. return processor.to_notification(alarm)
  60. def test_invalid_alarm(self):
  61. """Invalid Alarms, should log and error and push to the finished queue."""
  62. alarm = self._create_raw_alarm(0, 1, {'invalid': 'invalid_alarm'})
  63. notifications, partition, offset = self._run_alarm_processor(alarm, None)
  64. self.assertEqual(notifications, [])
  65. self.assertEqual(partition, 0)
  66. self.assertEqual(offset, 1)
  67. invalid_msg = ('Invalid Alarm format skipping partition 0, offset 1\n'
  68. 'ErrorAlarm data missing field actionsEnabled')
  69. self.assertIn(invalid_msg, self.trap)
  70. def test_old_timestamp(self):
  71. """Should cause the alarm_ttl to fire log a warning and push to finished queue."""
  72. timestamp = 1375346830042
  73. alarm_dict = {
  74. "tenantId": "0",
  75. "alarmDefinitionId": "0",
  76. "alarmId": "1",
  77. "alarmName": "test Alarm",
  78. "oldState": "OK",
  79. "newState": "ALARM",
  80. "stateChangeReason": "I am alarming!",
  81. "timestamp": timestamp,
  82. "actionsEnabled": 1,
  83. "metrics": "cpu_util",
  84. "severity": "LOW",
  85. "link": "http://some-place.com",
  86. "lifecycleState": "OPEN"}
  87. alarm = self._create_raw_alarm(0, 2, alarm_dict)
  88. expected_datetime = time.ctime(timestamp / 1000)
  89. notifications, partition, offset = self._run_alarm_processor(alarm, None)
  90. self.assertEqual(notifications, [])
  91. self.assertEqual(partition, 0)
  92. self.assertEqual(offset, 2)
  93. old_msg = ('Received alarm older than the ttl, skipping. '
  94. 'Alarm from {datetime}'.format(datetime=expected_datetime))
  95. self.assertIn(old_msg, self.trap)
  96. def test_no_notifications(self):
  97. """Test an alarm with no defined notifications
  98. """
  99. alarm_dict = {
  100. "tenantId": "0",
  101. "alarmDefinitionId": "0",
  102. "alarmId": "1",
  103. "alarmName": "test Alarm",
  104. "oldState": "OK",
  105. "newState": "ALARM",
  106. "stateChangeReason": "I am alarming!",
  107. "timestamp": time.time() * 1000,
  108. "actionsEnabled": 1,
  109. "metrics": "cpu_util",
  110. "severity": "LOW",
  111. "link": "http://some-place.com",
  112. "lifecycleState": "OPEN"}
  113. alarm = self._create_raw_alarm(0, 3, alarm_dict)
  114. notifications, partition, offset = self._run_alarm_processor(alarm, None)
  115. self.assertEqual(notifications, [])
  116. self.assertEqual(partition, 0)
  117. self.assertEqual(offset, 3)
  118. def test_valid_notification(self):
  119. """Test a valid notification, being put onto the notification_queue
  120. """
  121. alarm_dict = {
  122. "tenantId": "0",
  123. "alarmDefinitionId": "0",
  124. "alarmId": "1",
  125. "alarmName": "test Alarm",
  126. "oldState": "OK",
  127. "newState": "ALARM",
  128. "stateChangeReason": "I am alarming!",
  129. "timestamp": time.time() * 1000,
  130. "actionsEnabled": 1,
  131. "metrics": "cpu_util",
  132. "severity": "LOW",
  133. "link": "http://some-place.com",
  134. "lifecycleState": "OPEN"}
  135. alarm = self._create_raw_alarm(0, 4, alarm_dict)
  136. sql_response = [[1, 'EMAIL', 'test notification', 'me@here.com', 0]]
  137. notifications, partition, offset = self._run_alarm_processor(alarm, sql_response)
  138. test_notification = m_notification.Notification(1, 'email', 'test notification',
  139. 'me@here.com', 0, 0, alarm_dict)
  140. self.assertEqual(notifications, [test_notification])
  141. self.assertEqual(partition, 0)
  142. self.assertEqual(offset, 4)
  143. def test_two_valid_notifications(self):
  144. alarm_dict = {
  145. "tenantId": "0",
  146. "alarmDefinitionId": "0",
  147. "alarmId": "1",
  148. "alarmName": "test Alarm",
  149. "oldState": "OK",
  150. "newState": "ALARM",
  151. "stateChangeReason": "I am alarming!",
  152. "timestamp": time.time() * 1000,
  153. "actionsEnabled": 1,
  154. "metrics": "cpu_util",
  155. "severity": "LOW",
  156. "link": "http://some-place.com",
  157. "lifecycleState": "OPEN"}
  158. alarm = self._create_raw_alarm(0, 5, alarm_dict)
  159. sql_response = [[1, 'EMAIL', 'test notification', 'me@here.com', 0],
  160. [2, 'EMAIL', 'test notification2', 'me@here.com', 0]]
  161. notifications, partition, offset = self._run_alarm_processor(alarm, sql_response)
  162. test_notification = m_notification.Notification(1, 'email', 'test notification',
  163. 'me@here.com', 0, 0, alarm_dict)
  164. test_notification2 = m_notification.Notification(2, 'email', 'test notification2',
  165. 'me@here.com', 0, 0, alarm_dict)
  166. self.assertEqual(notifications, [test_notification, test_notification2])
  167. self.assertEqual(partition, 0)
  168. self.assertEqual(offset, 5)