From 06131c9c093371fd2f5aa09d62b303dbca39063a Mon Sep 17 00:00:00 2001 From: Stefan Weiher Date: Wed, 19 Jul 2017 16:24:44 +0200 Subject: [PATCH] Defined UTF-8 encoding in all files; now the report type in wc_getReport can be given as argument; added the new tool wc_checkAlarms --- README | 2 +- alarm.py | 15 ++++ wc_checkAlarms.py | 207 ++++++++++++++++++++++++++++++++++++++++++++++ wc_getReport.py | 18 +++- wc_getTrend.py | 2 + wc_getValue.py | 2 + wc_monitor.py | 2 + wc_query.py | 2 + 8 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 alarm.py create mode 100644 wc_checkAlarms.py diff --git a/README b/README index c408c9a..463b51e 100644 --- a/README +++ b/README @@ -54,7 +54,7 @@ import wc_query myDictonary = {'username':'wsdl', 'password':'seife', 'node':'/trees/geographic', 'url':'https://webctrl.rz-berlin.mpg.de'} wc_query.main(myDictonary) -To be able to import the wc_* tools append to the PYTHONPATH the webctrl directory. There are two possible ways: +To be able to import the wc_* tools append to the PYTHONPATH the webctrl directory. To do that there are two possible ways: 1) Set the PYTHONPATH environment variable in the shell in which you run your python script that uses the tool(s): export PYTHONPATH="$PYTHONPATH:/path/to/webctrl" 2) Set the python path inside the python script before the import statement: diff --git a/alarm.py b/alarm.py new file mode 100644 index 0000000..f0bfdaf --- /dev/null +++ b/alarm.py @@ -0,0 +1,15 @@ +# -*- coding: UTF-8 -*- + + +class alarm: + + def __init__(self, date, age, standort, message, quelle, bestaetigtVon, kategorie, status): + self.date = date + #self.time = time + self.age = age + self.standort = standort + self.message = message + self.quelle = quelle + self.bestaetigtVon = bestaetigtVon + self.kategorie = kategorie + self.status = status diff --git a/wc_checkAlarms.py b/wc_checkAlarms.py new file mode 100644 index 0000000..2621cb5 --- /dev/null +++ b/wc_checkAlarms.py @@ -0,0 +1,207 @@ +# -*- coding: utf-8 -*- + +import suds +import sys +import xml +import argparse +import getpass +from alarm import alarm +from time import sleep +import datetime + + +def main(args): + # Check all arguments + try: + if args['username'] is None: + print 'No username specified. Login to WebCTRL needs a username and a password. Check all options for this command via -h' + sys.exit(1) + else: + username = args['username'] + except KeyError: + print 'No "username" key specified. Please provide the key-value pair: \'username\':\'myUsername\'' + sys.exit(1) + + try: + if args['password'] is None: + print 'No password specified. Login to WebCTRL needs a username and a password. Check all options for this command via -h' + sys.exit(1) + else: + password = args['password'] + except KeyError: + print 'No "password" key specified. Please provide the key-value pair: \'password\':\'myPassword\'' + sys.exit(1) + + try: + if args['node'] is None: + print 'No path to a node specified. Check all options for this command via -h' + sys.exit(1) + except KeyError: + print 'No "node" key specified. Please provide the key-value pair: \'node\':\'/my/node\'' + sys.exit(1) + + try: + if args['url'] is None: + print 'No URL given. Specify the URL to the WebCTRL server analogous to http://google.de' + sys.exit(1) + else: + wsdlFile = args['url'] + '/_common/webservices/Report?wsdl' + except KeyError: + print 'No "url" key specified. Please provide the key-value pair: \'url\':\'http://myURL.de\'' + sys.exit(1) + + + # 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 is not correct.') + sys.exit(1) + + checkForNewAlarmsInterval = args['interval'] + alarmsRunning = [] # 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 + # 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(args['node'], '~alarms', 'csv') + except suds.WebFault as fault: + print fault + sys.exit(1) + + print report + + 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. + 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]) + #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) + + # 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 is already known; 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: + 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') + 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)) + 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: + 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') + age = 0 + alarmsRunning.append(alarm(date, age, standort, message, quelle, bestaetigtVon, kategorie, status)) + + 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') + + if alarmsRunning: + for a in alarmsRunning: + 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 ... + else: + print('No alarms in list alarmsRunning') + + sleep(checkForNewAlarmsInterval) + + +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.add_argument('--username', '-u', type=str, default=None, help='Username for the login to the WebCTRL server') + parser.add_argument('--password', '-p', type=str, default=None, help='Password for the login to the WebCTRL server') + parser.add_argument('--node', '-n', type=str, default='trees/geographic', + 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") + args = parser.parse_args() + + # Get the password if it hasn't been passed as argument + if args.password is None: + args.password = getpass.getpass('No password specified via -p. Please enter your WebCTRL login password: ') + + # Convert the argparse.Namespace to a dictionary via vars(args) + main(vars(args)) + sys.exit(0) diff --git a/wc_getReport.py b/wc_getReport.py index 688e67b..14283c5 100644 --- a/wc_getReport.py +++ b/wc_getReport.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + import suds import sys import numpy @@ -46,6 +48,15 @@ def main(args): print 'No "url" key specified. Please provide the key-value pair: \'url\':\'http://myURL.de\'' sys.exit(1) + try: + if args['reportType'] is None: + print 'No report type specified. Using a default: ~point-list-report' + else: + reportType = args['reportType'] + except KeyError: + print 'No "reportType" key specified. Please provide the key-value pair, e.g.: \'reportType\':\'~point-list-report\'' + sys.exit(1) + # Connect to the webCTRL server try: @@ -63,11 +74,12 @@ def main(args): # Get the report try: - report = client.service.runReport(args['node'], '~point-list-report', 'csv') + report = client.service.runReport(args['node'], reportType, 'csv') except suds.WebFault as fault: print fault sys.exit(1) + print report # Split the report into separate lines and calculate the number of columns lines = report.split("\n") @@ -123,6 +135,10 @@ 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('-reportType', '-t', type=str, default='~point-list-report', + help="The type of the report: ~schedule-instance, ~effective-schedule, ~point-list-report, ~locked-value, \ + ~network-io, ~test-and-balance, ~equipment-checkout, ~audit-log, ~alarms, ~alarm-source, ~network-status, \ + ~module-version, ~security-assignment, ~alarm-messages, ~alarm-actions, ~trend-usage, ~parameter-mismatch") args = parser.parse_args() # Get the password if it hasn't been passed as argument diff --git a/wc_getTrend.py b/wc_getTrend.py index dd2f3dc..e063a0a 100644 --- a/wc_getTrend.py +++ b/wc_getTrend.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + import suds import sys import xml diff --git a/wc_getValue.py b/wc_getValue.py index a271d8c..e78b92e 100644 --- a/wc_getValue.py +++ b/wc_getValue.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + import suds import sys import xml diff --git a/wc_monitor.py b/wc_monitor.py index 477ee95..d4e0d6e 100644 --- a/wc_monitor.py +++ b/wc_monitor.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + import suds import sys import xml diff --git a/wc_query.py b/wc_query.py index 5b43833..36774a0 100644 --- a/wc_query.py +++ b/wc_query.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + import suds import sys import xml