#!/usr/bin/env python3 #************************************************************************** # Copyright (C) 2011-2019 by Walter Brisken * # * # This program is free software; you can redistribute it and/or modify * # it under the terms of the GNU General Public License as published by * # the Free Software Foundation; either version 3 of the License, or * # (at your option) any later version. * # * # This program is distributed in the hope that it will be useful, * # but WITHOUT ANY WARRANTY; without even the implied warranty of * # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * # GNU General Public License for more details. * # * # You should have received a copy of the GNU General Public License * # along with this program; if not, write to the * # Free Software Foundation, Inc., * # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * #************************************************************************** #=========================================================================== # SVN properties (DO NOT CHANGE) # # $Id$ # $HeadURL: $ # $LastChangedRevision$ # $Author$ # $LastChangedDate$ # #============================================================================ # Note: this utility can run under python2.7 or python3 from sys import argv, exit, stdout from os import popen, getenv from glob import glob import socket import struct from xml.parsers import expat from copy import deepcopy from time import asctime import signal def signal_handler(signal, frame): print('You pressed Ctrl+C!') exit(0) program = 'statemon' author = 'Walter Brisken' version = '0.6' verdate = '20191115' def usage(prog): print('%s ver. %s %s %s\n' % (program, version, author, verdate)) print('Usage: %s [options]\n' % prog) print('options can include:') print(' --help') print(' -h print help information and quit\n') print(' -c use color output\n') class Parser: class colorcodes: DEFAULT = '\033[95m' GREEN = '\033[92m' ORANGE = '\033[93m' RED = '\033[91m' ENDC = '\033[0m' def __init__(self): self._parser = expat.ParserCreate() self._parser.StartElementHandler = self.start self._parser.EndElementHandler = self.end self._parser.CharacterDataHandler = self.data self.message = '' self.mjd = 0.0 self.mjdStart = 0.0 self.mjdStop = 0.0 self.state = '' self.tmp = '' self.weight = {} self.maxant = 0 self.ok = False self.unit = '' self.mpiid = -1 self.id = '' self.tag = '' def _weightToColor(self, w): wlimits = [0.8, 0.95, 1.01, 9999] wcolors = [self.colorcodes.RED, self.colorcodes.ORANGE, self.colorcodes.ENDC, self.colorcodes.RED] for (lim,c) in zip(wlimits, wcolors): if w < lim: return c return self.colorcodes.ENDC def feed(self, data): self._parser.Parse(data, 0) def close(self): self._parser.Parse("", 1) # end of data del self._parser # get rid of circular references def start(self, tag, attrs): self.tag = tag self.tmp = '' if tag == 'difxStatus': self.ok = True self.weight = {} self.maxant = 0 self.mjdStart = 0.0 self.mjdStop = 0.0 elif tag == 'weight': ant = int(attrs['ant']) self.weight[ant] = float(attrs['wt']) if ant > self.maxant: self.maxant = ant def end(self, tag): if tag == 'message' and self.ok: self.message = self.tmp elif tag == 'state': self.state = self.tmp elif tag == 'visibilityMJD': self.mjd = float(self.tmp) elif tag == 'jobstartMJD': self.mjdStart = float(self.tmp) elif tag == 'jobstopMJD': self.mjdStop = float(self.tmp) elif tag == 'from': self.unit = self.tmp.lower() elif tag == 'identifier': self.id = self.tmp elif tag == 'mpiProcessId': self.mpiid = int(self.tmp) def data(self, data): if self.tag == 'message': self.tmp = self.tmp + data else: self.tmp = data def getinfo(self, useColor=False): if self.ok: wtstr = '' if self.state == 'Running': for i in range(0, self.maxant+1): if not useColor: if not i in self.weight or self.weight[i] <= 0.0001: wtstr = wtstr + " .000" else: wtstr = wtstr + " %5.3f" % self.weight[i] else: if not i in self.weight or self.weight[i] <= 0.0001: wtstr = wtstr + self.colorcodes.RED + " .000" else: wtstr = wtstr + self._weightToColor(self.weight[i]) + " %5.3f" % (self.weight[i]) wtstr += self.colorcodes.ENDC if self.state == 'Running' and self.mjdStart > 50000.0 and self.mjdStop > self.mjdStart: statestr = '%5.2f%%' % ( 100.0*(self.mjd - self.mjdStart)/(self.mjdStop - self.mjdStart) ) else: statestr = self.state return 'MPI[%2d] %-9s %-12s %-7s %s%s' % (self.mpiid, self.unit, self.id, statestr, self.message, wtstr) else: return '' def run(useColor=False): port = getenv('DIFX_MESSAGE_PORT') if port == None: print('DIFX_MESSAGE_PORT needs to be defined') exit(0) else: port = int(port) group = getenv('DIFX_MESSAGE_GROUP') if group == None: print('DIFX_MESSAGE_GROUP needs to be defined') exit(0) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 768000) s.bind(('', port)) mreq = struct.pack("4sl", socket.inet_aton(group), socket.INADDR_ANY) s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) try: while 1: try: message = s.recv(8000).decode('utf-8') if len(message) > 0 and message[0] == '<': p = Parser() p.feed(message) info = p.getinfo(useColor) p.close() if p.ok: print('%s %s' % (asctime(), info)) stdout.flush() except socket.timeout: pass except expat.ExpatError: print('%s *** Unparsable message received ***' % asctime()) print(message) exit(0) except KeyboardInterrupt: pass signal.signal(signal.SIGINT, signal_handler) useColor = False if len(argv) > 1: if argv[1] in ['-h', '--help']: usage(argv[0]) exit(0) if argv[1] in ['-c', '--color']: useColor = True run(useColor)