Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
# -*- coding: utf-8 -*-
import suds
import sys
import xml
import argparse
import getpass
from alarm import alarm
from time import sleep
import datetime
from Email import getMailAddresses, sendEmails
from Environment import readSettings
def main(args):
####################################################################################################################
### Check all arguments
####################################################################################################################
errorMessage = "No path to SETTINGS file specified. Provide one via '-f my_settingsFile' or {..., 'settingsFile':'my_settingsFile', ...}"
try:
if args['settingsFile'] is None or not args['settingsFile']:
print(errorMessage)
sys.exit(1)
else:
settingsFile = args['settingsFile']
except KeyError:
print(errorMessage)
sys.exit(1)
settings = readSettings(settingsFile)
errorMessage = "No username specified. Provide one via '-u my_username' or {..., 'username':'my_username', ...} or WC_USERNAME=my_username"
try:
if args['username'] is None or not args['username']:
if not settings['WC_USERNAME']:
print(errorMessage)
sys.exit(1)
else:
username = settings['WC_USERNAME']
else:
username = str(args['username'])
except KeyError:
print(errorMessage)
sys.exit(1)
# The password cannot be given via environment variable
errorMessage = "No password specified. Provide one via {..., 'password':'my_password', ...}"
try:
if args['password'] is None or not args['password']:
print(errorMessage)
sys.exit(1)
else:
password = args['password']
except KeyError:
print(errorMessage)
sys.exit(1)
verbosityDefault = False
errorMessage = "No verbosity specified. Provide one via '-v my_verbosity' or {..., 'verbosity':'my_verbosity', ...} or WC_VERBOSITY=my_verbosity. " \
"Using a default now: '" + str(verbosityDefault) + "'"
try:
if args['verbosity'] is None or not args['verbosity']:
if not settings['WC_VERBOSITY']:
print(errorMessage)
verbosity = verbosityDefault
else:
verbosity = settings['WC_VERBOSITY']
else:
verbosity = str(args['verbosity'])
except KeyError:
print(errorMessage)
verbosity = verbosityDefault
nodeDefault = '/trees/geographic'
errorMessage = "No node specified. Provide one via '-n my_node' or {..., 'node':'my_node', ...} or WC_NODE=my_node. " \
"Using a default now: '" + nodeDefault + "'"
try:
if args['node'] is None or not args['node']:
if not settings['WC_NODE']:
print(errorMessage)
node = nodeDefault
else:
node = settings['WC_NODE']
else:
node = str(args['node'])
except KeyError:
print(errorMessage)
node = nodeDefault
urlDefault = 'https://webctrl.rz-berlin.mpg.de'
errorMessage = "No URL specified. Provide one via '-url my_url' or {..., 'url':'my_url', ...} or WC_URL=my_url. " \
"Using a default now: '" + urlDefault + "'"
try:
if args['url'] is None or not args['url']:
if not settings['WC_URL']:
print(errorMessage)
url = urlDefault
else:
url = settings['WC_URL']
else:
url = str(args['url'])
except KeyError:
print(errorMessage)
url = urlDefault
wsdlFile = url + '/_common/webservices/Report?wsdl'
intervalDefault = 5.0
errorMessage = "No interval specified. Provide one via '-i my_interval' or {..., 'interval':'my_interval', ...} or " \
"WC_ALARMS_INTERVAL=my_interval. Using a default now: " + str(intervalDefault) + " minutes"
try:
if args['interval'] is None or not args['interval']:
if not settings['WC_ALARMS_INTERVAL']:
print(errorMessage)
interval = intervalDefault
else:
interval = float(settings['WC_ALARMS_INTERVAL'])
else:
interval = args['interval']
except KeyError:
print(errorMessage)
interval = intervalDefault
if interval <= 0.0:
print 'The interval must be bigger than 0.0.'
sys.exit(1)
checkForNewAlarmsInterval = interval
takeActionDefault = 20.0
errorMessage = "No time to take action specified. Provide one via '-a my_takeAction' or {..., 'takeAction':'my_takeAction', ...} " \
"or WC_ALARMS_TAKEACTION=my_takeAction. Using a default now: " + str(takeActionDefault) + " minutes"
try:
if args['takeAction'] is None or not args['takeAction']:
if not settings['WC_ALARMS_TAKEACTION']:
print(errorMessage)
takeAction = takeActionDefault
else:
takeAction = float(settings['WC_ALARMS_TAKEACTION'])
else:
takeAction = args['takeAction']
except KeyError:
print(errorMessage)
takeAction = takeActionDefault
if takeAction <= 0.0:
print 'The time to take action must be bigger than 0.0.'
sys.exit(1)
takeActionTime = takeAction
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) + \
'The take action time should be bigger or equal to the checking for new alarms time.Exiting now...'
sys.exit(1)
errorMessage = "Either (or both) information about alarm time periods or e-mails is missing. Please set them in " \
"the settings file."
try:
if not settings['WC_ALARMS_ALERT_PERIODS'] or not settings['WC_ALARMS_ALERT_EMAILS']:
print(errorMessage)
sys.exit(1)
except KeyError:
print(errorMessage)
sys.exit(1)
# Setting two further "defaults":
numberOfReminders = 2
loopThroughHierarchiesInterval = 60.0 # unit: minutes
####################################################################################################################
### Connect to the webCTRL server
####################################################################################################################
try:
client = suds.client.Client(wsdlFile, username=username, password=password)
except AttributeError:
print 'Error: Incorrect username and/or password'
except xml.sax._exceptions.SAXParseException:
print 'Error: Incorrect/Misspelled WSDL file. It should be: http(s)://URL?wsdl'
sys.exit(1)
except:
print("Unexpected error:", sys.exc_info()[0])
print('Perhaps your URL to the WSDL file, ' + wsdlFile + ', is not correct.')
sys.exit(1)
####################################################################################################################
### Get the alarms and process them for as long as this script runs
####################################################################################################################
alarmsActive = [] # active alarms waiting to be checked/acknowledged and, eventually, switched off
alarmsChecked = [] # alarms that are already acknowledged 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
# managed in objects that will be deleted once the status of an alarm has switched to "Normal". It will be checked
# if an alarm is handled - at least acknowledged - in a certain amount of time and, if not, consequences follow as
# e.g. e-mail or SMS to a given address or telephone number.
while True:
# Get a report about all current alarms in the network or in parts of the network (depending on the node setting)
try:
report = client.service.runReport(node, '~alarms', 'csv')
except suds.WebFault as fault:
print fault
sys.exit(1)
lines = report.split("\n")
lines = lines[1:]
# Comment above two lines and uncomment below two lines in order to test the program
#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.
for line in reversed(lines):
if 'Aktueller Status' in line:
status = line[18:]
indexOfQuote = status.index('"')
status = status[:indexOfQuote]
elif 'Kategorie' in line:
kategorie = line[11:]
elif 'Besttigt' in line:
bestaetigtVon = line[10:]
elif 'Quelle' in line:
quelle = line[8:]
elif '","' in line:
parts = line.split(",")
dateAndTime = parts[0].replace('"', '').split(" ")
standort = parts[1].replace('"', '')
message = parts[2].replace('"', '')
date = dateAndTime[0]
parts = date.split(".")
day = int(parts[0])
month = int(parts[1])
year = int(parts[2])
time = dateAndTime[1]
parts = time.split(":")
hours = int(parts[0])
minutes = int(parts[1])
seconds = int(parts[2])
date = datetime.datetime(year, month, day, hours, minutes, seconds)
# Create the alarm object and add the unchecked/unsolved alarms to a list for observation
if status == 'Normal':
# 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 alarmsChecked:
if a.quelle == quelle and a.date == date:
# This alarm has already been checked; move on to the next alarm
alarmAlreadyKnown = True
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 alarmsActive:
if a.quelle == quelle and a.date == date:
# Remove alarm from running and do not append to checked
removedAlarm = True
alarmsActive.remove(a)
if verbosity: print('Removing alarm ' + str(a.date) + ' ' + a.quelle + ' from alarmsActive')
if not removedAlarm:
# Ignore the alarms that have a 'Normal' status without another matching alarm with 'Unnormal'
# status because it's already solved
pass
else:
# Alarms with "Unnormal" status; 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 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
a.stillActive = True
if not alarmAlreadyKnown:
if verbosity: print('Appending alarm ' + str(date) + ' ' + quelle + ' to alarmsActive')
age = 0.0
a = alarm(date, age, standort, message, quelle, bestaetigtVon, kategorie, status)
a.stillActive = True
alarmsActive.append(a)
if verbosity:
if alarmsChecked:
for a in alarmsChecked:
print(a.date, a.standort, a.message, a.quelle, a.bestaetigtVon, a.kategorie, a.status)
else:
print('No alarms in list alarmsChecked')
# Handling of active alarms, e.g., send a notification e-mail depending on the age of an alarm
if alarmsActive:
for a in alarmsActive:
# Remove "Zombi alarms", that is, alarms that are still marked as active but do not appear in the retrieved
# alarms list anymore. So, these alarms were acknowledged/solved AND deleted between two alarms retrievals.
if not a.stillActive:
if verbosity:
print("This alarm seems to have been solved and deleted so we're removing it from the list of active alarms:")
print(a.date, a.standort, a.message, a.quelle, a.bestaetigtVon, a.kategorie, a.status)
alarmsActive.remove(a)
continue
if verbosity:
print(a.date, a.standort, a.message, a.quelle, a.bestaetigtVon, a.kategorie, a.status)
print 'Age: ' + str(a.age)
print 'a.reminderNotification= ' + str(a.reminderNotification)
print 'a.firstNotification= ' + str(a.firstNotification)
# Initialisations in case there hasn't been any e-mail yet about this alarm
if not a.firstNotification:
a.firstNotification = True
a.reminderNotification = 0
# Check the age of the still active alarms and take action in case of, for example, the age is greater than e.g. 20 min
sourceMailAddress = 'ppb@fhi-berlin.mpg.de'
currentHour = datetime.datetime.now().hour
allMailAddressesForCurrentPeriod = getMailAddresses(currentHour, settings['WC_ALARMS_ALERT_EMAILS'], settings['WC_ALARMS_ALERT_PERIODS'])
if allMailAddressesForCurrentPeriod.startswith("ERROR"):
print(allMailAddressesForCurrentPeriod)
print("Exiting now...")
sys.exit(1)
addresses = allMailAddressesForCurrentPeriod.split("#")
# Select the correct mail addresses depending on the hierarchy:
if a.reminderNotification < numberOfReminders+1:
if a.age >= takeActionTime and (a.age % takeActionTime == 0):
mailAddresses = addresses[a.notificationHierarchy]
if verbosity: print 'Sending an e-mail to ' + mailAddresses
sendEmails(sourceMailAddress, mailAddresses, a.date, a.standort, a.message, a.quelle)
else:
if (a.age - takeActionTime * numberOfReminders) % loopThroughHierarchiesInterval == 0:
a.notificationHierarchy += 1
try:
mailAddresses = addresses[a.notificationHierarchy]
except IndexError:
print("Index Error! a.notificationHierarchy = " + str(a.notificationHierarchy))
# get the addresses of the highest hierarchy no matter what the index is at the moment
mailAddresses = addresses[-1]
if verbosity: print("Sending an e-mail at age = " + str(a.age) + " to: " + mailAddresses)
sendEmails(sourceMailAddress, mailAddresses, a.date, a.standort, a.message, a.quelle)
if a.firstNotification and (a.age % takeActionTime == 0) and 0 <= a.reminderNotification < numberOfReminders + 1:
# Only send a small amount of reminder mails about an alarm because we don't want the recipient to get spamed
a.reminderNotification += 1
for a in alarmsActive:
a.stillActive = False
else:
if verbosity: print('No alarms in list alarmsActive')
if verbosity: print('-----------------------------------------------------------------------------------------')
# sleep for n minutes
sleep(checkForNewAlarmsInterval*60)
# The argument parser is only called if this script is called as a script/executable (via command line) but not when
# imported by another script
if __name__ == '__main__':
if len(sys.argv) < 2:
print "You haven't specified any arguments. Use -h to get more details on how to use this command."
sys.exit(1)
parser = argparse.ArgumentParser()
parser._action_groups.pop()
required = parser.add_argument_group('required arguments')
optional = parser.add_argument_group('optional arguments (can also be specified via SETTINGS file)')
required.add_argument('--password', '-p', type=str, default=None, help='Password for the login to the WebCTRL server')
required.add_argument('--settingsFile', '-f', type=str, default=None, help='File name of settings containing WC_* variables')
required.add_argument('--username', '-u', type=str, default=None, help='Username for the login to the WebCTRL server')
optional.add_argument('--verbosity', '-v', action='store_true', default=None, help="If '-v' is selected you'll get verbose output")
optional.add_argument('--url', '-url', type=str, default=None, help="URL of the webctrl server like 'http(s)://webctrl.de'.")
optional.add_argument('--node', '-n', type=str, default=None,
help="Path to the point or node for which you want to retrieve all alarms. If '-n /trees/geographic' you'll get all alarms in the network.")
optional.add_argument('-interval', '-i', type=int, default=None, help="Interval in MINUTES in which the script checks for new alarms")
optional.add_argument('-takeAction', '-a', type=int, default=None, 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
if args.password is None or not args.password:
args.password = getpass.getpass('No password specified via -p. Please enter your WebCTRL login password now: ')
# Convert the argparse.Namespace to a dictionary via vars(args)
main(vars(args))
sys.exit(0)