diff --git a/EXAMPLES b/EXAMPLES index 2d6d5f6..22ff81b 100644 --- a/EXAMPLES +++ b/EXAMPLES @@ -133,3 +133,27 @@ No password specified via -p. Please enter your WebCTRL login password: 05/30/2017 03:58:00 AM 60.07319 05/30/2017 03:59:00 AM 60.07319 05/30/2017 04:00:00 AM 60.07319 + + +### wc_checkAlarms - check all alarms of a specified node +$ python wc_checkAlarms.py -u wsdl -n /trees/geographic -i 1 -a 1 +No password specified via -p. Please enter your WebCTRL login password: +Appending alarm 2017-07-25 12:24:25 Geb.G KKUK Sekundr Pumpe Ansteuerung Strung to alarmsActive +Removing alarm 2017-07-25 12:24:37 Geb.G KKUK Sekundr Pumpe Ansteuerung Strung from alarmsActive +Appending alarm 2017-07-25 13:14:21 DI_Wasser_Sensor_Lichtschacht_TZ to alarmsActive +No alarms in list alarmsChecked +(datetime.datetime(2017, 7, 25, 13, 14, 21), '/Fritz-Haber-Institut/Bauteil R/Meldungen/Geb.R Meldungen', 'Geb.R Wasser-Sensor Lichtschacht Technikzentrale Keller ausgelst!', 'DI_Wasser_Sensor_Lichtschacht_TZ', 'Nicht besttigt', 'Heizung, Lftung, Klimatechnik - Allgemein', 'Unnormal') +Age: 0 +Appending alarm 2017-07-25 12:24:25 Geb.G KKUK Sekundr Pumpe Ansteuerung Strung to alarmsActive +Removing alarm 2017-07-25 12:24:37 Geb.G KKUK Sekundr Pumpe Ansteuerung Strung from alarmsActive +No alarms in list alarmsChecked +(datetime.datetime(2017, 7, 25, 13, 14, 21), '/Fritz-Haber-Institut/Bauteil R/Meldungen/Geb.R Meldungen', 'Geb.R Wasser-Sensor Lichtschacht Technikzentrale Keller ausgelst!', 'DI_Wasser_Sensor_Lichtschacht_TZ', 'Nicht besttigt', 'Heizung, Lftung, Klimatechnik - Allgemein', 'Unnormal') +Age: 1 +Sending an e-mail to weiher@fhi-berlin.mpg.de +Appending alarm 2017-07-25 12:24:25 Geb.G KKUK Sekundr Pumpe Ansteuerung Strung to alarmsActive +Removing alarm 2017-07-25 12:24:37 Geb.G KKUK Sekundr Pumpe Ansteuerung Strung from alarmsActive +No alarms in list alarmsChecked +(datetime.datetime(2017, 7, 25, 13, 14, 21), '/Fritz-Haber-Institut/Bauteil R/Meldungen/Geb.R Meldungen', 'Geb.R Wasser-Sensor Lichtschacht Technikzentrale Keller ausgelst!', 'DI_Wasser_Sensor_Lichtschacht_TZ', 'Nicht besttigt', 'Heizung, Lftung, Klimatechnik - Allgemein', 'Unnormal') +Age: 2 +Sending an e-mail to weiher@fhi-berlin.mpg.de + diff --git a/Email.py b/Email.py new file mode 100644 index 0000000..c6f1e9f --- /dev/null +++ b/Email.py @@ -0,0 +1,22 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import smtplib +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText + +def sendEmail (address, alarmDate, alarmLocation, alarmMessage, alarmSource): + + msg = MIMEMultipart() + msg['Subject'] = alarmMessage + msg['From'] = address + msg['To'] = address + content = alarmMessage + '\n' + \ + 'Date: ' + str(alarmDate) + '\n' + \ + 'Location: ' + alarmLocation + '\n' + \ + 'Source: ' + alarmSource + msg.attach(MIMEText(content, 'plain')) + + s = smtplib.SMTP('mail.fhi-berlin.mpg.de') + s.sendmail(address, address, msg.as_string()) + s.quit() \ No newline at end of file diff --git a/README b/README index 463b51e..49b7479 100644 --- a/README +++ b/README @@ -41,6 +41,9 @@ wc_getTrend : Retrieve data and date for a given time period and device wc_monitor : Monitor a specific controller; every time the value changes time and value are displayed +wc_checkAlarms : Check the alarms of a specified node for alarms; send an e-mail in case an alarm is not + acknowledged/solved after a given amount of time + Usage of the tools in another python script ------------------------------------------- diff --git a/testAlarms.txt b/testAlarms.txt new file mode 100644 index 0000000..d855a78 --- /dev/null +++ b/testAlarms.txt @@ -0,0 +1,18 @@ +"25.7.2017 13:14:21","/Fritz-Haber-Institut/Bauteil R/Meldungen/Geb.R Meldungen","Geb.R Wasser-Sensor Lichtschacht Technikzentrale Keller ausgelst! +Quelle :DI_Wasser_Sensor_Lichtschacht_TZ +Besttigt :Nicht besttigt +Ausfhren :Besttigen +Kategorie :Heizung, Lftung, Klimatechnik - Allgemein +Aktueller Status :Unnormal" x x +"25.7.2017 12:24:37","/Fritz-Haber-Institut/Bauteil G/Kaltwasser/Umluftkhlwasser/Geb.G Umluftkhlwasser"xx,"Geb.G Umluftkhlwasser Sekundr Pumpe Ansteuerung Strung gehend. +Quelle :Geb.G KKUK Sekundr Pumpe Ansteuerung Strung +Besttigt :Nicht besttigt +Ausfhren :Besttigen +Kategorie :Heizung, Lftung, Klimatechnik - Allgemein +Aktueller Status :Normal"x x x +"25.7.2017 12:24:25","/Fritz-Haber-Institut/Bauteil G/Kaltwasser/Umluftkhlwasser/Geb.G Umluftkhlwasser"xx,"Geb.G Umluftkhlwasser Sekundr Pumpe Ansteuerung Strung! +Quelle :Geb.G KKUK Sekundr Pumpe Ansteuerung Strung +Besttigt :Nicht besttigt +Ausfhren :Besttigen +Kategorie :Heizung, Lftung, Klimatechnik - Allgemein +Aktueller Status :Unnormal"x x x diff --git a/wc_checkAlarms.py b/wc_checkAlarms.py index 2621cb5..778b949 100644 --- a/wc_checkAlarms.py +++ b/wc_checkAlarms.py @@ -8,6 +8,7 @@ from alarm import alarm from time import sleep import datetime +from Email import sendEmail def main(args): @@ -50,6 +51,31 @@ def main(args): print 'No "url" key specified. Please provide the key-value pair: \'url\':\'http://myURL.de\'' sys.exit(1) + try: + if args['interval'] is None: + print 'No interval (in minutes) for checking for new alarms specified. Using a default.' + checkForNewAlarmsInterval = 1 + else: + checkForNewAlarmsInterval = args['interval'] + except KeyError: + print 'No "interval" key specified. Please provide the key-value pair: \'interval\':\'time in minutes\'' + sys.exit(1) + + try: + if args['takeAction'] is None: + print 'No time (in minutes) specified after which an action is taken in case an alarm is not acknowledged. Using a default.' + takeActionTime = 5 + else: + takeActionTime = args['takeAction'] + except KeyError: + print 'No "takeAction" key specified. Please provide the key-value pair: \'takeAction\':\'time in minutes\'' + sys.exit(1) + if takeActionTime < checkForNewAlarmsInterval: + print 'The time after which action is taken for not acknowledged alarms is smaller than the interval for checking' + \ + 'for new alarms: ' + str(takeActionTime) + ' < ' + str(checkForNewAlarmsInterval) + print 'The take action time should be bigger or equal to the checking for new alarms time. Exiting now...' + sys.exit(1) + # Connect to the webCTRL server try: @@ -64,8 +90,8 @@ def main(args): print('Perhaps your URL to the WSDL file is not correct.') sys.exit(1) - checkForNewAlarmsInterval = args['interval'] - alarmsRunning = [] # running alarms waiting to be checked/acknowledged and, eventually, switched off + + alarmsActive = [] # running alarms waiting to be checked/acknowledged and, eventually, switched off alarmsChecked = [] # alarms that are already acknowloedged or switched off # As long as this script runs get a report about all current alarms and their status every X seconds. The alarms are @@ -80,12 +106,12 @@ def main(args): print fault sys.exit(1) - print report + #print report - lines = report.split("\n") - lines = lines[1:] - #file = open("testAlarms.txt", "r") - #lines = file.read().split("\n") + #lines = report.split("\n") + #lines = lines[1:] + file = open("testAlarms.txt", "r") + lines = file.read().split("\n") # Go through the report and create alarm objects distinguishing by their current alarm status. # Start looping from the last line in order to process the oldest alarms first. @@ -112,14 +138,12 @@ def main(args): day = int(parts[0]) month = int(parts[1]) year = int(parts[2]) - #print(year,month,day) time = dateAndTime[1] parts = time.split(":") hours = int(parts[0]) minutes = int(parts[1]) seconds = int(parts[2]) - #print(hours,minutes,seconds) date = datetime.datetime(year, month, day, hours, minutes, seconds) @@ -130,39 +154,41 @@ def main(args): alarmAlreadyKnown = False for a in alarmsChecked: if a.quelle == quelle and a.date == date: - # This alarm is already known; move on to the next alarm + # This alarm has already been checked; move on to the next alarm alarmAlreadyKnown = True - break if not alarmAlreadyKnown: # Before appending the checked/solved alarm to the list, first check if this alarm is already # the check/solution to a prior alarm with status e.g. 'unnormal': removedAlarm = False - for a in alarmsRunning: + for a in alarmsActive: if a.quelle == quelle: # Remove alarm from running and do not append to checked removedAlarm = True - alarmsRunning.remove(a) - print('Removing alarm ' + str(date) + quelle + ' from alarmsRunning') + alarmsActive.remove(a) + print('Removing alarm ' + str(date) + ' ' + quelle + ' from alarmsActive') if not removedAlarm: - # This should actually not happen: to have an alarm with status 'Normal' but to not find an - # appropriate alarm with status 'Unnormal' - age = 0 - alarmsChecked.append(alarm(date, age, standort, message, quelle, bestaetigtVon, kategorie, status)) + # Ignore the alarms that have a 'Normal' status without another matching alarm with 'Unnormal' + # status because it's already solved + pass + ## This should actually not happen: to have an alarm with status 'Normal' but to not find an + ## appropriate alarm with status 'Unnormal' + ##age = 0 + ##alarmsChecked.append(alarm(date, age, standort, message, quelle, bestaetigtVon, kategorie, status)) + ##print "This shouldn't have happened..." else: # Before appending the current alarm to the list of alarms check if this alarm is already contained # in the list: alarmAlreadyKnown = False - for a in alarmsRunning: + for a in alarmsActive: if a.quelle == quelle and a.date == date: # This alarm is already known; move on to the next alarm alarmAlreadyKnown = True # Observe the age of the alarm, i.e, increase the age by the time interval in which new alarms are caught a.age += checkForNewAlarmsInterval - break if not alarmAlreadyKnown: - print('Appending alarm '+str(date)+quelle+' to alarmsRunning') + print('Appending alarm ' + str(date) + ' ' + quelle + ' to alarmsActive') age = 0 - alarmsRunning.append(alarm(date, age, standort, message, quelle, bestaetigtVon, kategorie, status)) + alarmsActive.append(alarm(date, age, standort, message, quelle, bestaetigtVon, kategorie, status)) if alarmsChecked: for a in alarmsChecked: @@ -170,17 +196,19 @@ def main(args): else: print('No alarms in list alarmsChecked') - if alarmsRunning: - for a in alarmsRunning: + if alarmsActive: + for a in alarmsActive: print(a.date, a.standort, a.message, a.quelle, a.bestaetigtVon, a.kategorie, a.status) # Check the age of the still running alarms and take action in case of, for example, the age is greater than e.g. 20 min - if a.age / 60 > 20: - pass - # send e-mail / SMS notification to ... + print 'Age: ' + str(a.age) + if a.age >= takeActionTime: + mailAddress = 'weiher@fhi-berlin.mpg.de' + print 'Sending an e-mail to ' + mailAddress + sendEmail(mailAddress, a.date, a.standort, a.message, a.quelle) else: - print('No alarms in list alarmsRunning') + print('No alarms in list alarmsActive') - sleep(checkForNewAlarmsInterval) + sleep(checkForNewAlarmsInterval*60) if __name__=='__main__': @@ -195,7 +223,8 @@ def main(args): help='Path to the point or node whose children you want to retrieve. Start querying at the lowest level with "-n /trees/geographic"') parser.add_argument('-url', type=str, default='https://webctrl.rz-berlin.mpg.de', help="URL of the WebCTRL server as e.g. http://google.de") - parser.add_argument('-interval', '-i', type=int, default=60, help="Interval in SECONDS in which the script checks for new alarms") + parser.add_argument('-interval', '-i', type=int, default=1, help="Interval in MINUTES in which the script checks for new alarms") + parser.add_argument('-takeAction', '-a', type=int, default=1, help="Time in MINUTES after which an action is taken if an alarm still hasn't been acknowledged") args = parser.parse_args() # Get the password if it hasn't been passed as argument