Updated email notification
Change-Id: I7e8482b8c1f908ebe505a2f85eeca8397a12b9bf
This commit is contained in:
parent
634965a6dc
commit
ebc3c7fc89
@ -17,7 +17,8 @@ import json
|
||||
|
||||
|
||||
class Notification(object):
|
||||
"""An abstract base class used to define the notification interface and common functions
|
||||
"""An abstract base class used to define the notification interface
|
||||
and common functions
|
||||
"""
|
||||
__slots__ = (
|
||||
'address',
|
||||
@ -31,12 +32,14 @@ class Notification(object):
|
||||
'src_offset',
|
||||
'state',
|
||||
'tenant_id',
|
||||
'type'
|
||||
'type',
|
||||
'metrics'
|
||||
)
|
||||
|
||||
def __init__(self, ntype, src_partition, src_offset, name, address, alarm):
|
||||
"""Setup the notification object
|
||||
The src_partition and src_offset allow the notification to be linked to the alarm that it came from
|
||||
The src_partition and src_offset allow the notification
|
||||
to be linked to the alarm that it came from.
|
||||
ntype - The notification type
|
||||
name - Name used in sending
|
||||
address - to send the notification to
|
||||
@ -55,8 +58,10 @@ class Notification(object):
|
||||
self.message = alarm['stateChangeReason']
|
||||
self.state = alarm['newState']
|
||||
self.tenant_id = alarm['tenantId']
|
||||
self.metrics = alarm['metrics']
|
||||
|
||||
self.notification_timestamp = None # to be updated on actual notification send time
|
||||
# to be updated on actual notification send time
|
||||
self.notification_timestamp = None
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Notification):
|
||||
@ -82,5 +87,6 @@ class Notification(object):
|
||||
'state',
|
||||
'tenant_id'
|
||||
]
|
||||
notification_data = {name: getattr(self, name) for name in notification_fields}
|
||||
notification_data = {name: getattr(self, name)
|
||||
for name in notification_fields}
|
||||
return json.dumps(notification_data)
|
||||
|
@ -43,20 +43,57 @@ class NotificationProcessor(BaseProcessor):
|
||||
self.monascastatsd = mstatsd.Client(name='monasca',
|
||||
dimensions=BaseProcessor.dimensions)
|
||||
|
||||
def _create_msg(self, hostname, notification):
|
||||
"""Create two kind of messages:
|
||||
1. Notifications that include metrics with a hostname as a dimension. There may be more than one hostname.
|
||||
We will only report the hostname if there is only one.
|
||||
2. Notifications that do not include metrics and therefore no hostname. Example: API initiated changes.
|
||||
* A third notification type which include metrics but do not include a hostname will
|
||||
be treated as type #2.
|
||||
"""
|
||||
if len(hostname) == 1: # Type 1
|
||||
msg = email.mime.text.MIMEText("On host \"%s\" %s\n\nAlarm \"%s\" transitioned to the %s state at %s UTC"
|
||||
% (hostname[0],
|
||||
notification.message.lower(),
|
||||
notification.alarm_name,
|
||||
notification.state,
|
||||
time.asctime(time.gmtime(notification.alarm_timestamp))) +
|
||||
"\nalarm_id: %s" % notification.alarm_id)
|
||||
|
||||
msg['Subject'] = "%s \"%s\" for Host: %s" % (notification.state, notification.alarm_name, hostname[0])
|
||||
|
||||
else: # Type 2
|
||||
msg = email.mime.text.MIMEText("%s\n\nAlarm \"%s\" transitioned to the %s state at %s UTC\nAlarm_id: %s"
|
||||
% (notification.message,
|
||||
notification.alarm_name,
|
||||
notification.state,
|
||||
time.asctime(time.gmtime(notification.alarm_timestamp)),
|
||||
notification.alarm_id))
|
||||
msg['Subject'] = "%s \"%s\" " % (notification.state, notification.alarm_name)
|
||||
|
||||
msg['From'] = self.email_config['from_addr']
|
||||
msg['To'] = notification.address
|
||||
|
||||
return msg
|
||||
|
||||
def _send_email(self, notification):
|
||||
"""Send the notification via email
|
||||
Returns the notification upon success, None upon failure
|
||||
"""
|
||||
msg = email.mime.text.MIMEText("%s\nAlarm %s transitioned to the %s state at %s UTC\nFull Data:\n%s"
|
||||
% (notification.message,
|
||||
notification.alarm_name,
|
||||
notification.state,
|
||||
time.asctime(time.gmtime(notification.alarm_timestamp)),
|
||||
notification.to_json()))
|
||||
msg['Subject'] = '%s: %s' % (notification.state, notification.alarm_name)
|
||||
msg['From'] = self.email_config['from_addr']
|
||||
msg['To'] = notification.address
|
||||
|
||||
# Get the "hostname" from the notification metrics if there is one
|
||||
hostname = []
|
||||
|
||||
for metric in notification.metrics:
|
||||
for dimension in metric['dimensions']:
|
||||
if 'hostname' in dimension:
|
||||
if not metric['dimensions']['%s' % dimension] in hostname[:]:
|
||||
hostname.append(metric['dimensions']['%s' % dimension])
|
||||
|
||||
# Generate the message
|
||||
msg = self._create_msg(hostname, notification)
|
||||
|
||||
# email the notification
|
||||
try:
|
||||
self.smtp.sendmail(self.email_config['from_addr'], notification.address, msg.as_string())
|
||||
log.debug('Sent email to %s, notification %s' % (notification.address, notification.to_json()))
|
||||
|
8
tests/test_alarm_processor.py
Normal file → Executable file
8
tests/test_alarm_processor.py
Normal file → Executable file
@ -89,7 +89,7 @@ class TestAlarmProcessor(unittest.TestCase):
|
||||
"""Should cause the alarm_ttl to fire log a warning and push to finished queue."""
|
||||
alarm_dict = {"tenantId": "0", "alarmDefinitionId": "0", "alarmId": "1", "alarmName": "test Alarm",
|
||||
"oldState": "OK", "newState": "ALARM", "stateChangeReason": "I am alarming!",
|
||||
"timestamp": 1375346830, "actionsEnabled": 1}
|
||||
"timestamp": 1375346830, "actionsEnabled": 1, "metrics": "cpu_util"}
|
||||
self.alarm_queue.put(self._create_raw_alarm(0, 2, alarm_dict))
|
||||
finished, log_msg = self._run_alarm_processor(self.finished_queue, None)
|
||||
|
||||
@ -101,7 +101,7 @@ class TestAlarmProcessor(unittest.TestCase):
|
||||
"""
|
||||
alarm_dict = {"tenantId": "0", "alarmDefinitionId": "0", "alarmId": "1", "alarmName": "test Alarm",
|
||||
"oldState": "OK", "newState": "ALARM", "stateChangeReason": "I am alarming!",
|
||||
"timestamp": time.time(), "actionsEnabled": 1}
|
||||
"timestamp": time.time(), "actionsEnabled": 1, "metrics": "cpu_util"}
|
||||
self.alarm_queue.put(self._create_raw_alarm(0, 3, alarm_dict))
|
||||
finished, log_msg = self._run_alarm_processor(self.finished_queue, None)
|
||||
|
||||
@ -112,7 +112,7 @@ class TestAlarmProcessor(unittest.TestCase):
|
||||
"""
|
||||
alarm_dict = {"tenantId": "0", "alarmDefinitionId": "0", "alarmId": "1", "alarmName": "test Alarm",
|
||||
"oldState": "OK", "newState": "ALARM", "stateChangeReason": "I am alarming!",
|
||||
"timestamp": time.time(), "actionsEnabled": 1}
|
||||
"timestamp": time.time(), "actionsEnabled": 1, "metrics": "cpu_util"}
|
||||
self.alarm_queue.put(self._create_raw_alarm(0, 4, alarm_dict))
|
||||
sql_response = [['test notification', 'EMAIL', 'me@here.com']]
|
||||
finished, log_msg = self._run_alarm_processor(self.notification_queue, sql_response)
|
||||
@ -124,7 +124,7 @@ class TestAlarmProcessor(unittest.TestCase):
|
||||
def test_two_valid_notifications(self):
|
||||
alarm_dict = {"tenantId": "0", "alarmDefinitionId": "0", "alarmId": "1", "alarmName": "test Alarm",
|
||||
"oldState": "OK", "newState": "ALARM", "stateChangeReason": "I am alarming!",
|
||||
"timestamp": time.time(), "actionsEnabled": 1}
|
||||
"timestamp": time.time(), "actionsEnabled": 1, "metrics": "cpu_util"}
|
||||
self.alarm_queue.put(self._create_raw_alarm(0, 5, alarm_dict))
|
||||
sql_response = [['test notification', 'EMAIL', 'me@here.com'], ['test notification2', 'EMAIL', 'me@here.com']]
|
||||
finished, log_msg = self._run_alarm_processor(self.notification_queue, sql_response)
|
||||
|
@ -27,7 +27,8 @@ def test_json():
|
||||
'timestamp': 'timestamp',
|
||||
'stateChangeReason': 'stateChangeReason',
|
||||
'newState': 'newState',
|
||||
'tenantId': 'tenantId'}
|
||||
'tenantId': 'tenantId',
|
||||
'metrics': 'cpu_util'}
|
||||
test_notification = notification.Notification('ntype', 'src_partition', 'src_offset', 'name', 'address', alarm)
|
||||
|
||||
expected_dict = {u'name': u'name',
|
||||
@ -49,7 +50,8 @@ def test_equal():
|
||||
'timestamp': 'timestamp',
|
||||
'stateChangeReason': 'stateChangeReason',
|
||||
'newState': 'newState',
|
||||
'tenantId': 'tenantId'}
|
||||
'tenantId': 'tenantId',
|
||||
'metrics': 'cpu_util'}
|
||||
test_notification = notification.Notification('ntype', 'src_partition', 'src_offset', 'name', 'address', alarm)
|
||||
test_notification2 = notification.Notification('ntype', 'src_partition', 'src_offset', 'name', 'address', alarm)
|
||||
|
||||
@ -62,7 +64,8 @@ def test_unequal():
|
||||
'timestamp': 'timestamp',
|
||||
'stateChangeReason': 'stateChangeReason',
|
||||
'newState': 'newState',
|
||||
'tenantId': 'tenantId'}
|
||||
'tenantId': 'tenantId',
|
||||
'metrics': 'cpu_util'}
|
||||
test_notification = notification.Notification('ntype', 'src_partition', 'src_offset', 'name', 'address', alarm)
|
||||
test_notification2 = notification.Notification('ntype', 'src_partition', 'src_offset', 'name', 'address', alarm)
|
||||
test_notification2.alarm_id = None
|
||||
|
@ -60,7 +60,7 @@ class TestStateTracker(unittest.TestCase):
|
||||
"""Verify invalid notification type is rejected.
|
||||
"""
|
||||
alarm_dict = {"tenantId": "0", "alarmId": "0", "alarmName": "test Alarm", "oldState": "OK", "newState": "ALARM",
|
||||
"stateChangeReason": "I am alarming!", "timestamp": time.time()}
|
||||
"stateChangeReason": "I am alarming!", "timestamp": time.time(), "metrics": "cpu_util"}
|
||||
invalid_notification = Notification('invalid', 0, 1, 'test notification', 'me@here.com', alarm_dict)
|
||||
|
||||
self.notification_queue.put([invalid_notification])
|
||||
|
Loading…
x
Reference in New Issue
Block a user