You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by de...@apache.org on 2016/08/11 18:37:54 UTC

svn commit: r1756025 - /uima/uima-ducc/trunk/src/main/admin/ducc_watcher

Author: degenaro
Date: Thu Aug 11 18:37:54 2016
New Revision: 1756025

URL: http://svn.apache.org/viewvc?rev=1756025&view=rev
Log:
UIMA-5053 DUCC ducc_watcher optional admin script to determine status and send notifications

Added:
    uima/uima-ducc/trunk/src/main/admin/ducc_watcher   (with props)

Added: uima/uima-ducc/trunk/src/main/admin/ducc_watcher
URL: http://svn.apache.org/viewvc/uima/uima-ducc/trunk/src/main/admin/ducc_watcher?rev=1756025&view=auto
==============================================================================
--- uima/uima-ducc/trunk/src/main/admin/ducc_watcher (added)
+++ uima/uima-ducc/trunk/src/main/admin/ducc_watcher Thu Aug 11 18:37:54 2016
@@ -0,0 +1,264 @@
+#! /usr/bin/env python
+# -----------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# -----------------------------------------------------------------------
+
+# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+# +
+# + ducc_watcher
+# +
+# + purpose: send e-mail notification when overall DUCC state changes
+# +
+# + configuration: 
+# + 
+# +  - add ducc.watcher.notification.list = user1@host1 user2@host2...
+# +    to ducc.properties
+# +  - add this script as a cron job on a host that:
+# +      > has access to $DUCC_HOME directory
+# +      > has access to DUCC WebServer URL
+# +      > has 'localhost' email server
+# +
+# + files:
+# +
+# +  - $DUCC_HOME/state/watcher 
+# +      > comprises the last recorded overall state of DUCC
+# +
+# +  - $DUCC_HOME/logs/watcher.log
+# +      > comprises a state change events log
+# + 
+# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+import datetime
+import getpass
+import os
+import socket
+import stat
+import smtplib
+import sys
+import time
+import urllib
+from HTMLParser import HTMLParser
+from ducc_util import DuccUtil
+from properties import Properties
+
+# amount of time to give DUCC WebServer 
+# to boot and determine daemon states
+boot_time_seconds = 90
+
+# email server host name
+email_host = 'localhost'
+
+# for debugging only
+def debug(text):
+    debug = False
+    if(debug):
+        print text
+        
+# determine age of the specified file in seconds
+def file_age_in_seconds(filename):
+    return time.time() - os.stat(filename)[stat.ST_MTIME]
+
+# produce a time stamp
+def get_timestamp():
+    tod = time.time()
+    timestamp = datetime.datetime.fromtimestamp(tod).strftime('%Y-%m-%d %H:%M:%S')         
+    return timestamp
+
+# get the host running this script
+def get_host():
+    host = socket.gethostname()
+    return host
+
+# get the user running this script
+def get_user():
+    user = getpass.getuser()
+    return user
+
+# send email
+def email(sender,receivers,mail_host,text):
+    smtpObj = smtplib.SMTP(mail_host)
+    smtpObj.sendmail(sender,receivers,text)
+
+# log a message
+def logger(filename,text):
+    watcher_host = socket.gethostname()
+    line = get_timestamp()+' '+get_user()+'@'+get_host()+' '+text+'\n'
+    with open(filename, 'a') as f:
+        f.write(line)
+
+# the possible states of DUCC
+class DuccState:
+    
+    up   = ':-) All DUCC daemons up'
+    down = ':-( One or more DUCC daemons down'
+    lost = ';-( Lost contact with DUCC WebServer'
+
+# parser for the system.daemons WS page
+class DuccHtmlParser(HTMLParser):
+    
+    tr_state = False
+    daemon_state = None
+    daemon_name = None  
+    daemons = {}
+    
+    def get_daemons(self):
+        return self.daemons
+    
+    def handle_starttag(self, tag, attrs):
+        if(tag == 'tr' ):
+            self.tr_state = True
+        
+    def handle_endtag(self, tag):   
+        if(tag == 'tr'):
+            self.tr_state = False
+            self.daemon_state = None
+            self.daemon_name = None
+        
+    def handle_data(self, data):
+        if(self.tr_state):
+            if(self.daemon_state == None):
+                self.daemon_state = data
+            elif(self.daemon_name == None):
+                self.daemon_name = data
+                self.daemons[self.daemon_name] = self.daemon_state
+                for key in self.daemons:
+                    value = self.daemons[key]
+
+# determine current DUCC state & take action (send email)                    
+class DuccWatcher(DuccUtil):
+    
+    log_file = '?'
+    state_file = '?'
+    state_prev = DuccState.lost
+    state_curr = DuccState.lost
+    port = ''
+    head = ''
+    host = ''
+    url = ''
+    daemons = {}
+    
+    # initialize persistent state, if necessary
+    def init_state_prev(self):
+        filename = self.state_file
+        if(not os.path.isfile(filename)):
+            self.put_state_prev(DuccState.lost)
+    
+    # change persistent state
+    def put_state_prev(self,state):
+        filename = self.state_file
+        with open(filename, 'w') as f:
+            f.seek(0)
+            f.write(state+'\n')
+            f.truncate()
+    
+    # fetch persistent state
+    def get_state_prev(self):
+        filename = self.state_file
+        with open(filename) as f: 
+            self.state_prev = f.read().strip()
+    
+    # fetch a property from ducc.properties
+    def get_property(self,key,default):
+        value = self.ducc_properties.get(key)
+        if (value == None):
+            value = default
+        return value
+    
+    # fetch the notification list from ducc.properties
+    def get_notification_list(self):
+        notification_list = []
+        key = 'ducc.watcher.notification.list'
+        value = self.get_property(key,'')
+        if(value != None):
+            value = value.replace(","," ")
+            list = value.split(" ")
+            for entry in list:
+                token = ''.join(entry.split())
+                if len(token) > 0:
+                    notification_list.append(token)
+        return notification_list
+    
+    # initialize variables
+    def initialize(self):
+        ducc_home = self.DUCC_HOME
+        if not ducc_home.endswith('/'):
+            ducc_home = ducc_home+'/'
+        self.ws_boot = ducc_home+'state'+'/'+'daemons'+'/'+'Webserver-boot.properties'
+        self.log_file = ducc_home+'logs'+'/'+'watcher.log'
+        self.state_file = ducc_home+'state'+'/'+'watcher'
+        self.init_state_prev()
+        self.get_state_prev()
+        self.ducc_port = self.get_property('ducc.ws.port','42133')
+        self.ducc_head = self.get_property('ducc.head','localhost')
+        self.ducc_host = self.get_property('ducc.ws.node',self.ducc_head)    
+        self.ducc_url = 'http://'+self.ducc_host+':'+self.ducc_port+'/ducc-servlet/classic-system-daemons-data'
+    
+    # perform watcher function
+    def run(self):
+        # fetch daemons page from DUCC WebServer
+        try:
+            response = urllib.urlopen(self.ducc_url)
+            data = response.read()
+        except:
+            data = None
+        # parse the daemons page date into 
+        # a dict of ( daemon, status ) pairs
+        if(not data == None):
+            parser = DuccHtmlParser()
+            parser.feed(data)
+            daemons = parser.get_daemons()
+            if(daemons != None):
+                self.state_curr = DuccState.up
+                for key in daemons:
+                    value = daemons[key]
+                    if(value == 'down'):
+                        self.state_curr = DuccState.down
+            self.daemons = daemons
+        debug('state'+' '+'curr='+self.state_curr+' '+'prev='+self.state_prev)
+        # if the state has changed, send email to
+        # addresses in ducc.watcher.notification.list
+        # and record to $DUCC_HOME/logs/watcher.log
+        if(self.state_curr != self.state_prev):
+            text = self.state_curr
+            if(self.state_curr == DuccState.down):
+                # allow DUCC WebServer sufficient time
+                # after booting to determine daemons states
+                age = file_age_in_seconds(self.ws_boot)
+                debug('Webserver-boot.properties age='+str(age))
+                if(age < boot_time_seconds):
+                    return
+            self.put_state_prev(self.state_curr)
+            text = 'Subject: '+'['+self.ducc_host+']'+' '+text+' '+'</eom>'
+            sent_to = 'nobody'
+            notification_list = self.get_notification_list()
+            if(notification_list != None):
+                if(len(notification_list) > 0):
+                    sender = get_user()+'@'+get_host()
+                    receivers = notification_list
+                    email(sender,receivers,email_host,text)
+                    sent_to = ' '.join(notification_list)
+            logmsg = '"'+text+'"'+' sent to: '+sent_to
+            logger(self.log_file,logmsg)
+            
+    def main(self, argv):
+        self.initialize()
+        self.run()
+            
+if __name__ == "__main__":
+    instance = DuccWatcher()
+    instance.main(sys.argv[1:])

Propchange: uima/uima-ducc/trunk/src/main/admin/ducc_watcher
------------------------------------------------------------------------------
    svn:executable = *