2018-05-04 13:53:29 +02:00
#!/usr/bin/env python3
2017-01-16 12:18:23 +01:00
# -*-coding:UTF-8 -*
from asciimatics . widgets import Frame , ListBox , Layout , Divider , Text , \
2017-02-15 09:35:03 +01:00
Button , Label
2017-01-16 12:18:23 +01:00
from asciimatics . scene import Scene
from asciimatics . screen import Screen
from asciimatics . exceptions import ResizeScreenError , NextScene , StopApplication
from asciimatics . event import Event
2017-01-17 14:31:06 +01:00
from asciimatics . event import KeyboardEvent , MouseEvent
2017-01-16 12:18:23 +01:00
import sys , os
import time , datetime
2018-04-16 14:50:04 +02:00
import argparse , configparser
2017-01-16 12:18:23 +01:00
import json
import redis
2017-01-16 17:08:48 +01:00
import psutil
2017-01-17 14:31:06 +01:00
from subprocess import PIPE , Popen
2017-02-14 16:05:59 +01:00
from packages import Paste
2017-01-16 12:18:23 +01:00
# CONFIG VARIABLES
kill_retry_threshold = 60 #1m
log_filename = " ../logs/moduleInfo.log "
command_search_pid = " ps a -o pid,cmd | grep {} "
command_search_name = " ps a -o pid,cmd | grep {} "
command_restart_module = " screen -S \" Script \" -X screen -t \" {} \" bash -c \" ./ {} .py; read x \" "
2017-02-15 10:57:41 +01:00
printarrayLog = [ None ] * 14
2017-01-16 12:18:23 +01:00
lastTimeKillCommand = { }
2017-01-17 14:31:06 +01:00
2017-02-15 10:04:51 +01:00
# Used to pass information through scenes
2017-01-17 14:31:06 +01:00
current_selected_value = 0
current_selected_queue = " "
2017-01-17 15:58:16 +01:00
current_selected_action = " "
2019-02-22 19:22:20 +01:00
current_selected_amount = 0
2017-01-17 14:31:06 +01:00
2017-02-15 10:04:51 +01:00
# Map PID to Queue name (For restart and killing)
2017-01-17 14:31:06 +01:00
PID_NAME_DICO = { }
2017-02-15 10:04:51 +01:00
# Tables containing info for the dashboad
2017-01-17 14:31:06 +01:00
TABLES = { " running " : [ ] , " idle " : [ ] , " notRunning " : [ ] , " logs " : [ ( " No events recorded yet " , 0 ) ] }
TABLES_TITLES = { " running " : " " , " idle " : " " , " notRunning " : " " , " logs " : " " }
TABLES_PADDING = { " running " : [ 12 , 23 , 8 , 8 , 23 , 10 , 55 , 11 , 11 , 12 ] , " idle " : [ 9 , 23 , 8 , 12 , 50 ] , " notRunning " : [ 9 , 23 , 35 ] , " logs " : [ 15 , 23 , 8 , 50 ] }
2017-02-15 10:04:51 +01:00
# Indicator for the health of a queue (green(0), red(2), yellow(1))
2017-01-16 15:30:33 +01:00
QUEUE_STATUS = { }
2017-02-15 10:04:51 +01:00
# Maintain the state of the CPU objects
2018-04-16 14:50:04 +02:00
CPU_TABLE = { }
2017-01-16 17:08:48 +01:00
CPU_OBJECT_TABLE = { }
2017-01-16 12:18:23 +01:00
2017-02-15 10:04:51 +01:00
# Path of the current paste for a pid
2017-02-14 16:05:59 +01:00
COMPLETE_PASTE_PATH_PER_PID = { }
2017-02-15 09:43:18 +01:00
'''
ASCIIMATICS WIDGETS EXTENSION
'''
2017-02-15 10:04:51 +01:00
# Custom listbox
2017-01-16 12:18:23 +01:00
class CListBox ( ListBox ) :
2017-01-16 14:41:02 +01:00
2017-01-16 12:18:23 +01:00
def __init__ ( self , queue_name , * args , * * kwargs ) :
self . queue_name = queue_name
super ( CListBox , self ) . __init__ ( * args , * * kwargs )
def update ( self , frame_no ) :
self . _options = TABLES [ self . queue_name ]
2017-01-16 14:41:02 +01:00
self . _draw_label ( )
# Calculate new visible limits if needed.
width = self . _w - self . _offset
height = self . _h
dx = dy = 0
# Clear out the existing box content
( colour , attr , bg ) = self . _frame . palette [ " field " ]
for i in range ( height ) :
self . _frame . canvas . print_at (
" " * width ,
self . _x + self . _offset + dx ,
self . _y + i + dy ,
colour , attr , bg )
# Don't bother with anything else if there are no options to render.
if len ( self . _options ) < = 0 :
return
# Render visible portion of the text.
self . _start_line = max ( 0 , max ( self . _line - height + 1 ,
min ( self . _start_line , self . _line ) ) )
2017-01-16 15:30:33 +01:00
for i , ( text , pid ) in enumerate ( self . _options ) :
2017-01-17 14:31:06 +01:00
if self . _start_line < = i < self . _start_line + height :
2017-01-16 14:41:02 +01:00
colour , attr , bg = self . _pick_colours ( " field " , i == self . _line )
self . _frame . canvas . print_at (
" { : {width} } " . format ( text , width = width ) ,
self . _x + self . _offset + dx ,
self . _y + i + dy - self . _start_line ,
colour , attr , bg )
2017-02-15 10:04:51 +01:00
# Pick color depending on queue health
2017-01-16 15:30:33 +01:00
if self . queue_name == " running " :
if QUEUE_STATUS [ pid ] == 2 :
queueStatus = Screen . COLOUR_RED
elif QUEUE_STATUS [ pid ] == 1 :
queueStatus = Screen . COLOUR_YELLOW
else :
queueStatus = Screen . COLOUR_GREEN
self . _frame . canvas . print_at ( " " ,
2017-01-16 17:08:48 +01:00
self . _x + 9 + dx ,
2017-01-16 15:30:33 +01:00
self . _y + i + dy - self . _start_line ,
colour , attr , queueStatus )
2017-01-16 14:41:02 +01:00
2017-01-16 12:18:23 +01:00
2017-01-17 14:31:06 +01:00
def process_event ( self , event ) :
if isinstance ( event , KeyboardEvent ) :
if len ( self . _options ) > 0 and event . key_code == Screen . KEY_UP :
# Move up one line in text - use value to trigger on_select.
self . _line = max ( 0 , self . _line - 1 )
2017-02-28 11:11:17 +01:00
self . _line = min ( self . _line , len ( self . _options ) - 1 ) #If we move a line cursor from a line that has dissapear
2017-01-17 14:31:06 +01:00
self . value = self . _options [ self . _line ] [ 1 ]
2017-02-15 10:04:51 +01:00
2017-01-17 14:31:06 +01:00
elif len ( self . _options ) > 0 and event . key_code == Screen . KEY_DOWN :
# Move down one line in text - use value to trigger on_select.
self . _line = min ( len ( self . _options ) - 1 , self . _line + 1 )
self . value = self . _options [ self . _line ] [ 1 ]
2017-02-15 10:04:51 +01:00
2017-02-15 11:01:11 +01:00
elif len ( self . _options ) > 0 and event . key_code in [ ord ( ' ' ) , ord ( ' \n ' ) ] :
2017-01-17 14:31:06 +01:00
global current_selected_value , current_selected_queue
2017-02-14 17:21:52 +01:00
if self . queue_name == " logs " :
return event
2017-01-17 14:31:06 +01:00
current_selected_value = self . value
current_selected_queue = self . queue_name
2017-01-17 15:58:16 +01:00
self . _frame . save ( )
raise NextScene ( " action_choice " )
2017-02-15 09:35:03 +01:00
# Quit if press q
elif event . key_code == ord ( ' q ' ) :
2017-02-15 09:43:18 +01:00
Dashboard . _quit ( )
2018-04-16 14:50:04 +02:00
2017-01-17 14:31:06 +01:00
else :
# Ignore any other key press.
return event
2017-02-15 10:04:51 +01:00
2017-01-17 14:31:06 +01:00
elif isinstance ( event , MouseEvent ) :
# Mouse event - rebase coordinates to Frame context.
new_event = self . _frame . rebase_event ( event )
if event . buttons != 0 :
if ( len ( self . _options ) > 0 and
self . is_mouse_over ( new_event , include_label = False ) ) :
# Use property to trigger events.
self . _line = min ( new_event . y - self . _y ,
len ( self . _options ) - 1 )
self . value = self . _options [ self . _line ] [ 1 ]
# If clicked on button <k>, kill the queue
if self . _x + 2 < = new_event . x < self . _x + 4 :
if self . queue_name in [ " running " , " idle " ] :
2017-01-17 15:58:16 +01:00
kill_module ( PID_NAME_DICO [ int ( self . value ) ] , self . value )
2017-01-17 14:31:06 +01:00
else :
restart_module ( self . value )
return
# Ignore other mouse events.
return event
else :
# Ignore other events
return event
2017-02-15 10:04:51 +01:00
# Custom label centered in the middle
2017-01-16 12:18:23 +01:00
class CLabel ( Label ) :
2017-01-17 14:31:06 +01:00
def __init__ ( self , label , listTitle = False ) :
2017-01-16 12:18:23 +01:00
super ( Label , self ) . __init__ ( None , tab_stop = False )
# Although this is a label, we don't want it to contribute to the layout
# tab calculations, so leave internal `_label` value as None.
self . _text = label
2017-01-17 14:31:06 +01:00
self . listTitle = listTitle
2018-02-16 14:35:09 +01:00
self . _required_height = 1
2017-01-16 12:18:23 +01:00
def set_layout ( self , x , y , offset , w , h ) :
# Do the usual layout work. then recalculate exact x/w values for the
# rendered button.
super ( Label , self ) . set_layout ( x , y , offset , w , h )
2017-01-17 14:31:06 +01:00
self . _x + = max ( 0 , ( self . _w - self . _offset - len ( self . _text ) ) / / 2 ) if not self . listTitle else 0
2017-01-16 12:18:23 +01:00
self . _w = min ( self . _w , len ( self . _text ) )
def update ( self , frame_no ) :
( colour , attr , bg ) = self . _frame . palette [ " title " ]
2017-01-17 14:31:06 +01:00
colour = Screen . COLOUR_YELLOW if not self . listTitle else colour
2017-01-16 12:18:23 +01:00
self . _frame . canvas . print_at (
self . _text , self . _x , self . _y , colour , attr , bg )
2017-02-15 09:43:18 +01:00
'''
END EXTENSION
'''
'''
SCENE DEFINITION
2018-04-16 14:50:04 +02:00
'''
2017-02-15 09:43:18 +01:00
class Dashboard ( Frame ) :
2017-01-16 12:18:23 +01:00
def __init__ ( self , screen ) :
2017-02-15 09:43:18 +01:00
super ( Dashboard , self ) . __init__ ( screen ,
2017-01-16 12:18:23 +01:00
screen . height ,
screen . width ,
2017-01-16 14:41:02 +01:00
hover_focus = True ,
reduce_cpu = True )
2017-01-16 12:18:23 +01:00
self . _list_view_run_queue = CListBox (
" running " ,
screen . height / / 2 ,
2017-01-17 14:31:06 +01:00
[ ] , name = " LIST " )
2017-01-16 12:18:23 +01:00
self . _list_view_idle_queue = CListBox (
" idle " ,
screen . height / / 2 ,
2017-01-17 14:31:06 +01:00
[ ] , name = " LIST " )
2017-01-16 12:18:23 +01:00
self . _list_view_noRunning = CListBox (
" notRunning " ,
2017-01-17 14:31:06 +01:00
screen . height / / 5 ,
[ ] , name = " LIST " )
2017-01-16 12:18:23 +01:00
self . _list_view_Log = CListBox (
" logs " ,
screen . height / / 4 ,
2017-01-17 14:31:06 +01:00
[ ] , name = " LIST " )
#self._list_view_Log.disabled = True
2017-01-16 12:18:23 +01:00
#Running Queues
layout = Layout ( [ 100 ] )
self . add_layout ( layout )
text_rq = CLabel ( " Running Queues " )
layout . add_widget ( text_rq )
2017-01-17 14:31:06 +01:00
layout . add_widget ( CLabel ( TABLES_TITLES [ " running " ] , listTitle = True ) )
2017-01-16 12:18:23 +01:00
layout . add_widget ( self . _list_view_run_queue )
layout . add_widget ( Divider ( ) )
#Idling Queues
layout2 = Layout ( [ 1 , 1 ] )
self . add_layout ( layout2 )
text_iq = CLabel ( " Idling Queues " )
2017-01-17 14:31:06 +01:00
layout2 . add_widget ( text_iq , 0 )
layout2 . add_widget ( CLabel ( TABLES_TITLES [ " idle " ] , listTitle = True ) , 0 )
2017-01-16 12:18:23 +01:00
layout2 . add_widget ( self . _list_view_idle_queue , 0 )
#Non Running Queues
2017-02-15 13:42:21 +01:00
text_nq = CLabel ( " Queues not running " )
2017-01-16 12:18:23 +01:00
layout2 . add_widget ( text_nq , 1 )
2017-01-17 14:31:06 +01:00
layout2 . add_widget ( CLabel ( TABLES_TITLES [ " notRunning " ] , listTitle = True ) , 1 )
2017-01-16 12:18:23 +01:00
layout2 . add_widget ( self . _list_view_noRunning , 1 )
layout2 . add_widget ( Divider ( ) , 1 )
#Log
text_l = CLabel ( " Logs " )
layout2 . add_widget ( text_l , 1 )
2017-01-17 14:31:06 +01:00
layout2 . add_widget ( CLabel ( TABLES_TITLES [ " logs " ] , listTitle = True ) , 1 )
2017-01-16 12:18:23 +01:00
layout2 . add_widget ( self . _list_view_Log , 1 )
self . fix ( )
@staticmethod
def _quit ( ) :
raise StopApplication ( " User pressed quit " )
2017-01-17 14:31:06 +01:00
class Confirm ( Frame ) :
def __init__ ( self , screen ) :
super ( Confirm , self ) . __init__ ( screen ,
screen . height * 1 / / 8 ,
2017-02-14 17:21:52 +01:00
screen . width * 1 / / 3 ,
2017-01-17 14:31:06 +01:00
hover_focus = True ,
on_load = self . _setValue ,
title = " Confirm action " ,
reduce_cpu = True )
# Create the form for displaying the list of contacts.
layout = Layout ( [ 100 ] , fill_frame = True )
self . add_layout ( layout )
2017-01-17 15:58:16 +01:00
self . label = CLabel ( " {} module {} {} ? " )
layout . add_widget ( Label ( " " ) )
2017-01-17 14:31:06 +01:00
layout . add_widget ( self . label )
layout2 = Layout ( [ 1 , 1 ] )
self . add_layout ( layout2 )
layout2 . add_widget ( Button ( " Ok " , self . _ok ) , 0 )
layout2 . add_widget ( Button ( " Cancel " , self . _cancel ) , 1 )
self . fix ( )
def _ok ( self ) :
2017-01-17 15:58:16 +01:00
global current_selected_value , current_selected_queue , current_selected_action , current_selected_amount
if current_selected_action == " KILL " :
kill_module ( PID_NAME_DICO [ int ( current_selected_value ) ] , current_selected_value )
2017-01-17 14:31:06 +01:00
else :
2017-02-15 10:04:51 +01:00
count = int ( current_selected_amount ) #Number of queue to start
2017-01-17 15:58:16 +01:00
if current_selected_queue in [ " running " , " idle " ] :
restart_module ( PID_NAME_DICO [ int ( current_selected_value ) ] , count )
else :
restart_module ( current_selected_value , count )
2017-01-17 14:31:06 +01:00
current_selected_value = 0
2017-01-17 15:58:16 +01:00
current_selected_amount = 0
current_selected_action = " "
self . label . _text = " {} module {} {} ? "
self . save ( )
2017-01-17 14:31:06 +01:00
raise NextScene ( " dashboard " )
def _cancel ( self ) :
global current_selected_value
current_selected_value = 0
2017-01-17 15:58:16 +01:00
current_selected_amount = 0
current_selected_action = " "
self . label . _text = " {} module {} {} ? "
self . save ( )
2017-01-17 14:31:06 +01:00
raise NextScene ( " dashboard " )
def _setValue ( self ) :
2017-01-17 15:58:16 +01:00
global current_selected_value , current_selected_queue , current_selected_action , current_selected_amount
2017-01-17 14:31:06 +01:00
if current_selected_queue in [ " running " , " idle " ] :
2017-01-17 15:58:16 +01:00
action = current_selected_action if current_selected_action == " KILL " else current_selected_action + " " + str ( current_selected_amount ) + " x "
modulename = PID_NAME_DICO [ int ( current_selected_value ) ]
2017-01-17 14:31:06 +01:00
pid = current_selected_value
else :
2017-01-17 15:58:16 +01:00
action = current_selected_action + " " + str ( current_selected_amount ) + " x "
2017-01-17 14:31:06 +01:00
modulename = current_selected_value
pid = " "
self . label . _text = self . label . _text . format ( action , modulename , pid )
2017-01-17 15:58:16 +01:00
class Action_choice ( Frame ) :
def __init__ ( self , screen ) :
super ( Action_choice , self ) . __init__ ( screen ,
screen . height * 1 / / 8 ,
screen . width * 1 / / 2 ,
hover_focus = True ,
on_load = self . _setValue ,
title = " Confirm action " ,
reduce_cpu = True )
2017-01-17 14:31:06 +01:00
2017-01-17 15:58:16 +01:00
# Create the form for displaying the list of contacts.
layout = Layout ( [ 100 ] , fill_frame = True )
self . add_layout ( layout )
self . label = CLabel ( " Choose action on module {} {} " )
layout . add_widget ( self . label )
2017-02-14 16:05:59 +01:00
layout2 = Layout ( [ 1 , 1 , 1 , 1 ] )
2017-01-17 15:58:16 +01:00
self . add_layout ( layout2 )
layout2 . add_widget ( Button ( " Cancel " , self . _cancel ) , 0 )
2017-02-14 17:21:52 +01:00
self . _ShowPasteBtn = Button ( " Show current paste " , self . _showpaste )
layout2 . add_widget ( self . _ShowPasteBtn , 1 )
2017-01-17 15:58:16 +01:00
self . _killBtn = Button ( " KILL " , self . _kill )
2017-02-14 16:05:59 +01:00
layout2 . add_widget ( self . _killBtn , 2 )
layout2 . add_widget ( Button ( " START " , self . _start ) , 3 )
layout3 = Layout ( [ 1 , 1 , 1 , 1 ] )
2017-01-17 15:58:16 +01:00
self . add_layout ( layout3 )
self . textEdit = Text ( " Amount " , " amount " )
2017-02-14 16:05:59 +01:00
layout3 . add_widget ( self . textEdit , 3 )
2017-01-17 15:58:16 +01:00
self . fix ( )
def _kill ( self ) :
global current_selected_action
current_selected_action = " KILL "
self . label . _text = " Choose action on module {} {} "
self . save ( )
raise NextScene ( " confirm " )
def _start ( self ) :
global current_selected_action , current_selected_amount
current_selected_action = " START "
try :
count = int ( self . textEdit . value )
count = count if count < 20 else 1
except Exception :
count = 1
current_selected_amount = count
self . label . _text = " Choose action on module {} {} "
self . save ( )
raise NextScene ( " confirm " )
def _cancel ( self ) :
global current_selected_value
current_selected_value = 0
self . label . _text = " Choose action on module {} {} "
self . save ( )
raise NextScene ( " dashboard " )
2017-02-14 16:05:59 +01:00
def _showpaste ( self ) :
self . label . _text = " Choose action on module {} {} "
self . save ( )
raise NextScene ( " show_paste " )
2017-01-17 15:58:16 +01:00
def _setValue ( self ) :
self . _killBtn . disabled = False
2017-02-14 17:21:52 +01:00
self . _ShowPasteBtn . disabled = False
2017-01-17 15:58:16 +01:00
global current_selected_value , current_selected_queue
if current_selected_queue in [ " running " , " idle " ] :
modulename = PID_NAME_DICO [ int ( current_selected_value ) ]
pid = current_selected_value
else :
self . _killBtn . disabled = True
2017-02-14 17:21:52 +01:00
self . _ShowPasteBtn . disabled = True
2017-01-17 15:58:16 +01:00
modulename = current_selected_value
pid = " "
self . label . _text = self . label . _text . format ( modulename , pid )
2017-01-16 12:18:23 +01:00
2017-02-14 16:05:59 +01:00
class Show_paste ( Frame ) :
def __init__ ( self , screen ) :
super ( Show_paste , self ) . __init__ ( screen ,
screen . height ,
screen . width ,
hover_focus = True ,
on_load = self . _setValue ,
title = " Show current paste " ,
reduce_cpu = True )
layout = Layout ( [ 100 ] , fill_frame = True )
self . layout = layout
self . add_layout ( layout )
self . label_list = [ ]
2017-02-15 10:04:51 +01:00
self . num_label = 42 # Number of line available for displaying the paste
2017-02-14 16:05:59 +01:00
for i in range ( self . num_label ) :
self . label_list + = [ Label ( " THE PASTE CONTENT " + str ( i ) ) ]
layout . add_widget ( self . label_list [ i ] )
layout2 = Layout ( [ 100 ] )
self . add_layout ( layout2 )
layout2 . add_widget ( Button ( " Ok " , self . _ok ) , 0 )
self . fix ( )
def _ok ( self ) :
global current_selected_value , current_selected_queue , current_selected_action , current_selected_amount
current_selected_value = 0
current_selected_amount = 0
current_selected_action = " "
self . save ( )
raise NextScene ( " dashboard " )
def _setValue ( self ) :
try :
#Verify that the module have a paste
if COMPLETE_PASTE_PATH_PER_PID [ current_selected_value ] is None :
self . label_list [ 0 ] . _text = " No paste for this module "
for i in range ( 1 , self . num_label ) :
self . label_list [ i ] . _text = " "
return
paste = Paste . Paste ( COMPLETE_PASTE_PATH_PER_PID [ current_selected_value ] )
2017-02-15 10:04:51 +01:00
old_content = paste . get_p_content ( ) [ 0 : 4000 ] # Limit number of char to be displayed
2017-02-14 16:05:59 +01:00
#Replace unprintable char by ?
content = " "
for i , c in enumerate ( old_content ) :
2017-02-15 10:04:51 +01:00
if ord ( c ) > 127 : # Used to avoid printing unprintable char
2017-02-14 16:05:59 +01:00
content + = ' ? '
2017-02-15 10:04:51 +01:00
elif c == " \t " : # Replace tab by 4 spaces
2017-02-15 08:26:48 +01:00
content + = " "
2017-02-14 16:05:59 +01:00
else :
content + = c
2017-02-15 10:04:51 +01:00
#Print in the correct label, END or more
2017-02-14 16:05:59 +01:00
to_print = " "
i = 0
for line in content . split ( " \n " ) :
2017-02-15 08:26:48 +01:00
if i > self . num_label - 2 :
2017-02-14 16:05:59 +01:00
break
self . label_list [ i ] . _text = str ( i ) + " . " + line . replace ( " \r " , " " )
i + = 1
2017-02-15 08:26:48 +01:00
if i > self . num_label - 2 :
2017-02-15 10:04:51 +01:00
self . label_list [ i ] . _text = " - ALL PASTE NOT DISPLAYED - "
2017-02-15 08:26:48 +01:00
i + = 1
else :
2017-02-15 10:04:51 +01:00
self . label_list [ i ] . _text = " - END of PASTE - "
2017-02-15 08:26:48 +01:00
i + = 1
2017-02-15 10:04:51 +01:00
while i < self . num_label : #Clear out remaining lines
2017-02-14 16:05:59 +01:00
self . label_list [ i ] . _text = " "
i + = 1
except OSError as e :
self . label_list [ 0 ] . _text = " Error during parsing the filepath. Please, check manually "
2017-02-14 17:21:52 +01:00
self . label_list [ 1 ] . _text = COMPLETE_PASTE_PATH_PER_PID [ current_selected_value ]
for i in range ( 2 , self . num_label ) :
2017-02-14 16:05:59 +01:00
self . label_list [ i ] . _text = " "
except Exception as e :
2019-02-22 19:22:20 +01:00
if current_selected_value in COMPLETE_PASTE_PATH_PER_PID :
self . label_list [ 0 ] . _text = " Error while displaying the paste: " + COMPLETE_PASTE_PATH_PER_PID [ current_selected_value ]
else :
self . label_list [ 0 ] . _text = " Error Generic exception caught "
2017-02-14 16:05:59 +01:00
self . label_list [ 1 ] . _text = str ( e )
for i in range ( 2 , self . num_label ) :
self . label_list [ i ] . _text = " "
2017-02-15 09:43:18 +01:00
'''
END SCENES DEFINITION
'''
2017-02-14 16:05:59 +01:00
2017-01-16 12:18:23 +01:00
2017-02-15 09:43:18 +01:00
'''
MANAGE MODULES AND GET INFOS
'''
2017-01-16 12:18:23 +01:00
def getPid ( module ) :
p = Popen ( [ command_search_pid . format ( module + " .py " ) ] , stdin = PIPE , stdout = PIPE , bufsize = 1 , shell = True )
for line in p . stdout :
splittedLine = line . split ( )
2018-04-17 09:37:58 +02:00
if ' python3 ' in splittedLine :
2017-01-16 12:18:23 +01:00
return int ( splittedLine [ 0 ] )
return None
def clearRedisModuleInfo ( ) :
for k in server . keys ( " MODULE_* " ) :
server . delete ( k )
inst_time = datetime . datetime . fromtimestamp ( int ( time . time ( ) ) )
2017-02-15 10:57:41 +01:00
log ( ( [ str ( inst_time ) . split ( ' ' ) [ 1 ] , " * " , " - " , " Cleared redis module info " ] , 0 ) )
2017-01-16 12:18:23 +01:00
def cleanRedis ( ) :
for k in server . keys ( " MODULE_TYPE_* " ) :
2018-05-04 13:53:29 +02:00
moduleName = k [ 12 : ] . split ( ' _ ' ) [ 0 ]
2017-01-16 12:18:23 +01:00
for pid in server . smembers ( k ) :
flag_pid_valid = False
proc = Popen ( [ command_search_name . format ( pid ) ] , stdin = PIPE , stdout = PIPE , bufsize = 1 , shell = True )
2017-02-15 08:26:48 +01:00
try :
for line in proc . stdout :
2018-04-17 09:37:58 +02:00
line = line . decode ( ' utf8 ' )
2017-02-15 08:26:48 +01:00
splittedLine = line . split ( )
2018-04-17 09:43:45 +02:00
if ( ' python3.5 ' in splittedLine or ' python3 ' in splittedLine or ' python ' in splittedLine ) :
2018-04-17 09:37:58 +02:00
moduleCommand = " ./ " + moduleName + " .py "
moduleCommand2 = moduleName + " .py "
if ( moduleCommand in splittedLine or moduleCommand2 in splittedLine ) :
flag_pid_valid = True
2017-02-15 08:26:48 +01:00
if not flag_pid_valid :
#print flag_pid_valid, 'cleaning', pid, 'in', k
server . srem ( k , pid )
inst_time = datetime . datetime . fromtimestamp ( int ( time . time ( ) ) )
2018-05-04 13:53:29 +02:00
log ( ( [ str ( inst_time ) . split ( ' ' ) [ 1 ] , moduleName , pid , " Cleared invalid pid in " + ( k ) ] , 0 ) )
2017-02-15 08:26:48 +01:00
#Error due to resize, interrupted sys call
except IOError as e :
2017-01-16 12:18:23 +01:00
inst_time = datetime . datetime . fromtimestamp ( int ( time . time ( ) ) )
2017-02-15 10:57:41 +01:00
log ( ( [ str ( inst_time ) . split ( ' ' ) [ 1 ] , " - " , " - " , " Cleaning fail due to resize. " ] , 0 ) )
2017-02-15 08:26:48 +01:00
2017-01-16 12:18:23 +01:00
2017-01-17 15:58:16 +01:00
def restart_module ( module , count = 1 ) :
for i in range ( count ) :
p2 = Popen ( [ command_restart_module . format ( module , module ) ] , stdin = PIPE , stdout = PIPE , bufsize = 1 , shell = True )
time . sleep ( 0.2 )
2017-01-17 14:31:06 +01:00
inst_time = datetime . datetime . fromtimestamp ( int ( time . time ( ) ) )
2017-02-15 10:57:41 +01:00
log ( ( [ str ( inst_time ) . split ( ' ' ) [ 1 ] , module , " ? " , " Restarted " + str ( count ) + " x " ] , 0 ) )
2017-01-17 14:31:06 +01:00
2017-01-16 12:18:23 +01:00
def kill_module ( module , pid ) :
2017-01-17 14:31:06 +01:00
#print '-> trying to kill module:', module
2017-01-16 12:18:23 +01:00
if pid is None :
2017-01-17 14:31:06 +01:00
#print 'pid was None'
inst_time = datetime . datetime . fromtimestamp ( int ( time . time ( ) ) )
2017-02-15 10:57:41 +01:00
log ( ( [ str ( inst_time ) . split ( ' ' ) [ 1 ] , module , pid , " PID was None " ] , 0 ) )
2017-01-16 12:18:23 +01:00
pid = getPid ( module )
else : #Verify that the pid is at least in redis
if server . exists ( " MODULE_ " + module + " _ " + str ( pid ) ) == 0 :
return
lastTimeKillCommand [ pid ] = int ( time . time ( ) )
if pid is not None :
try :
2017-01-17 14:31:06 +01:00
p = psutil . Process ( int ( pid ) )
2017-01-16 17:08:48 +01:00
p . terminate ( )
2017-01-17 14:31:06 +01:00
except Exception as e :
#print pid, 'already killed'
2017-01-16 12:18:23 +01:00
inst_time = datetime . datetime . fromtimestamp ( int ( time . time ( ) ) )
2017-02-15 10:57:41 +01:00
log ( ( [ str ( inst_time ) . split ( ' ' ) [ 1 ] , module , pid , " Already killed " ] , 0 ) )
2017-01-16 12:18:23 +01:00
return
2017-01-17 14:31:06 +01:00
time . sleep ( 0.2 )
if not p . is_running ( ) :
#print module, 'has been killed'
#print 'restarting', module, '...'
2017-01-16 12:18:23 +01:00
inst_time = datetime . datetime . fromtimestamp ( int ( time . time ( ) ) )
2017-02-15 10:57:41 +01:00
log ( ( [ str ( inst_time ) . split ( ' ' ) [ 1 ] , module , pid , " Killed " ] , 0 ) )
2017-01-17 15:58:16 +01:00
#restart_module(module)
2017-01-16 12:18:23 +01:00
else :
2017-01-17 14:31:06 +01:00
#print 'killing failed, retrying...'
2017-01-16 12:18:23 +01:00
inst_time = datetime . datetime . fromtimestamp ( int ( time . time ( ) ) )
2017-02-15 10:57:41 +01:00
log ( ( [ str ( inst_time ) . split ( ' ' ) [ 1 ] , module , pid , " Killing #1 failed. " ] , 0 ) )
2017-01-16 12:18:23 +01:00
2017-01-16 17:08:48 +01:00
p . terminate ( )
2017-01-17 14:31:06 +01:00
if not p . is_running ( ) :
#print module, 'has been killed'
#print 'restarting', module, '...'
2017-01-16 12:18:23 +01:00
inst_time = datetime . datetime . fromtimestamp ( int ( time . time ( ) ) )
2017-02-15 10:57:41 +01:00
log ( ( [ str ( inst_time ) . split ( ' ' ) [ 1 ] , module , pid , " Killed " ] , 0 ) )
2017-01-17 15:58:16 +01:00
#restart_module(module)
2017-01-16 12:18:23 +01:00
else :
2017-01-17 14:31:06 +01:00
#print 'killing failed!'
2017-01-16 12:18:23 +01:00
inst_time = datetime . datetime . fromtimestamp ( int ( time . time ( ) ) )
2017-02-15 10:57:41 +01:00
log ( ( [ str ( inst_time ) . split ( ' ' ) [ 1 ] , module , pid , " Killing failed! " ] , 0 ) )
2017-01-16 12:18:23 +01:00
else :
2017-01-17 14:31:06 +01:00
#print 'Module does not exist'
2017-01-16 12:18:23 +01:00
inst_time = datetime . datetime . fromtimestamp ( int ( time . time ( ) ) )
2017-02-15 10:57:41 +01:00
log ( ( [ str ( inst_time ) . split ( ' ' ) [ 1 ] , module , pid , " Killing failed, module not found " ] , 0 ) )
2017-01-16 12:18:23 +01:00
cleanRedis ( )
2017-02-15 10:19:03 +01:00
2017-02-15 10:37:59 +01:00
# Fetch the data for all queue
2017-01-16 12:18:23 +01:00
def fetchQueueData ( ) :
all_queue = set ( )
2017-02-15 10:37:59 +01:00
printarray_running = [ ]
printarray_idle = [ ]
printarray_notrunning = [ ]
2018-04-16 14:50:04 +02:00
for queue , card in iter ( server . hgetall ( " queues " ) . items ( ) ) :
2017-01-16 12:18:23 +01:00
all_queue . add ( queue )
key = " MODULE_ " + queue + " _ "
keySet = " MODULE_TYPE_ " + queue
array_module_type = [ ]
2018-04-16 14:50:04 +02:00
2017-01-16 12:18:23 +01:00
for moduleNum in server . smembers ( keySet ) :
2018-05-04 13:53:29 +02:00
value = server . get ( key + str ( moduleNum ) )
2018-05-02 17:07:10 +02:00
complete_paste_path = ( server . get ( key + str ( moduleNum ) + " _PATH " ) )
if ( complete_paste_path is not None ) :
2018-05-04 13:53:29 +02:00
complete_paste_path = complete_paste_path
2017-02-14 16:05:59 +01:00
COMPLETE_PASTE_PATH_PER_PID [ moduleNum ] = complete_paste_path
2017-02-15 10:37:59 +01:00
2017-01-16 12:18:23 +01:00
if value is not None :
timestamp , path = value . split ( " , " )
if timestamp is not None and path is not None :
2017-02-15 10:37:59 +01:00
# Queue health
2017-01-16 12:18:23 +01:00
startTime_readable = datetime . datetime . fromtimestamp ( int ( timestamp ) )
processed_time_readable = str ( ( datetime . datetime . now ( ) - startTime_readable ) ) . split ( ' . ' ) [ 0 ]
2017-01-16 15:30:33 +01:00
if ( ( datetime . datetime . now ( ) - startTime_readable ) . total_seconds ( ) ) > args . treshold :
QUEUE_STATUS [ moduleNum ] = 2
elif ( ( datetime . datetime . now ( ) - startTime_readable ) . total_seconds ( ) ) > args . treshold / 2 :
QUEUE_STATUS [ moduleNum ] = 1
else :
QUEUE_STATUS [ moduleNum ] = 0
2018-04-16 14:50:04 +02:00
2017-02-15 10:37:59 +01:00
# Queue contain elements
2017-01-16 12:18:23 +01:00
if int ( card ) > 0 :
2017-02-15 10:37:59 +01:00
# Queue need to be killed
2017-01-16 12:18:23 +01:00
if int ( ( datetime . datetime . now ( ) - startTime_readable ) . total_seconds ( ) ) > args . treshold :
2017-02-28 10:21:27 +01:00
log ( ( [ str ( time . time ( ) ) , queue , " - " , " ST: " + str ( timestamp ) + " PT: " + str ( time . time ( ) - float ( timestamp ) ) ] , 0 ) , True , show_in_board = False )
2017-01-16 12:18:23 +01:00
try :
last_kill_try = time . time ( ) - lastTimeKillCommand [ moduleNum ]
except KeyError :
last_kill_try = kill_retry_threshold + 1
if args . autokill == 1 and last_kill_try > kill_retry_threshold :
kill_module ( queue , int ( moduleNum ) )
2018-04-16 14:50:04 +02:00
2017-02-15 10:37:59 +01:00
# Create CPU objects
2017-01-16 17:08:48 +01:00
try :
cpu_percent = CPU_OBJECT_TABLE [ int ( moduleNum ) ] . cpu_percent ( )
CPU_TABLE [ moduleNum ] . insert ( 1 , cpu_percent )
cpu_avg = sum ( CPU_TABLE [ moduleNum ] ) / len ( CPU_TABLE [ moduleNum ] )
2017-01-17 14:31:06 +01:00
if len ( CPU_TABLE [ moduleNum ] ) > args . refresh * 10 :
CPU_TABLE [ moduleNum ] . pop ( )
2018-04-17 09:37:58 +02:00
2017-01-17 14:31:06 +01:00
mem_percent = CPU_OBJECT_TABLE [ int ( moduleNum ) ] . memory_percent ( )
2017-02-28 16:13:01 +01:00
except psutil . NoSuchProcess :
del CPU_OBJECT_TABLE [ int ( moduleNum ) ]
del CPU_TABLE [ moduleNum ]
cpu_percent = 0
cpu_avg = cpu_percent
mem_percent = 0
2017-01-16 17:08:48 +01:00
except KeyError :
2018-04-17 09:37:58 +02:00
#print('key error2')
2017-01-17 14:31:06 +01:00
try :
CPU_OBJECT_TABLE [ int ( moduleNum ) ] = psutil . Process ( int ( moduleNum ) )
cpu_percent = CPU_OBJECT_TABLE [ int ( moduleNum ) ] . cpu_percent ( )
CPU_TABLE [ moduleNum ] = [ ]
cpu_avg = cpu_percent
mem_percent = CPU_OBJECT_TABLE [ int ( moduleNum ) ] . memory_percent ( )
except psutil . NoSuchProcess :
cpu_percent = 0
cpu_avg = cpu_percent
mem_percent = 0
2017-01-16 17:08:48 +01:00
2017-02-15 10:37:59 +01:00
array_module_type . append ( ( [ " <K> [ ] " , str ( queue ) , str ( moduleNum ) , str ( card ) , str ( startTime_readable ) ,
2018-04-16 14:50:04 +02:00
str ( processed_time_readable ) , str ( path ) , " {0:.2f} " . format ( cpu_percent ) + " % " ,
2017-02-15 10:37:59 +01:00
" {0:.2f} " . format ( mem_percent ) + " % " , " {0:.2f} " . format ( cpu_avg ) + " % " ] , moduleNum ) )
2018-04-16 14:50:04 +02:00
2017-01-16 12:18:23 +01:00
else :
2017-02-15 10:37:59 +01:00
printarray_idle . append ( ( [ " <K> " , str ( queue ) , str ( moduleNum ) , str ( processed_time_readable ) , str ( path ) ] , moduleNum ) )
2017-01-17 15:58:16 +01:00
PID_NAME_DICO [ int ( moduleNum ) ] = str ( queue )
2018-04-17 09:37:58 +02:00
#array_module_type.sort(lambda x,y: cmp(x[0][4], y[0][4]), reverse=True) #Sort by num of pastes
2017-01-16 12:18:23 +01:00
for e in array_module_type :
2017-02-15 10:37:59 +01:00
printarray_running . append ( e )
2018-04-16 14:50:04 +02:00
2017-01-16 12:18:23 +01:00
for curr_queue in module_file_array :
2017-02-15 10:37:59 +01:00
if curr_queue not in all_queue : #Module not running by default
printarray_notrunning . append ( ( [ " <S> " , curr_queue , " Not running by default " ] , curr_queue ) )
else : #Module did not process anything yet
2017-01-16 12:18:23 +01:00
if len ( list ( server . smembers ( ' MODULE_TYPE_ ' + curr_queue ) ) ) == 0 :
if curr_queue not in no_info_modules :
no_info_modules [ curr_queue ] = int ( time . time ( ) )
2017-02-15 10:37:59 +01:00
printarray_notrunning . append ( ( [ " <S> " , curr_queue , " No data " ] , curr_queue ) )
2017-01-16 12:18:23 +01:00
else :
#If no info since long time, try to kill
if args . autokill == 1 :
if int ( time . time ( ) ) - no_info_modules [ curr_queue ] > args . treshold :
kill_module ( curr_queue , None )
no_info_modules [ curr_queue ] = int ( time . time ( ) )
2017-02-15 10:37:59 +01:00
printarray_notrunning . append ( ( [ " <S> " , curr_queue , " Stuck or idle, restarting in " + str ( abs ( args . treshold - ( int ( time . time ( ) ) - no_info_modules [ curr_queue ] ) ) ) + " s " ] , curr_queue ) )
2017-01-16 12:18:23 +01:00
else :
2017-02-15 10:37:59 +01:00
printarray_notrunning . append ( ( [ " <S> " , curr_queue , " Stuck or idle, restarting disabled " ] , curr_queue ) )
2018-04-16 14:50:04 +02:00
2017-02-15 10:37:59 +01:00
printarray_running . sort ( key = lambda x : x [ 0 ] , reverse = False )
printarray_idle . sort ( key = lambda x : x [ 0 ] , reverse = False )
2017-02-15 13:42:21 +01:00
printarray_notrunning . sort ( key = lambda x : x [ 0 ] [ 1 ] , reverse = False )
2017-01-16 12:18:23 +01:00
2017-02-15 10:37:59 +01:00
printstring_running = format_string ( printarray_running , TABLES_PADDING [ " running " ] )
printstring_idle = format_string ( printarray_idle , TABLES_PADDING [ " idle " ] )
printstring_notrunning = format_string ( printarray_notrunning , TABLES_PADDING [ " notRunning " ] )
2017-01-16 12:18:23 +01:00
2017-02-15 10:37:59 +01:00
return { " running " : printstring_running , " idle " : printstring_idle , " notRunning " : printstring_notrunning }
2017-01-16 12:18:23 +01:00
2017-02-15 10:19:03 +01:00
# Format the input string with its related padding to have collumn like text in CListBox
2017-01-17 14:31:06 +01:00
def format_string ( tab , padding_row ) :
printstring = [ ]
for row in tab :
2017-01-16 17:08:48 +01:00
if row is None :
continue
2017-01-17 14:31:06 +01:00
the_array = row [ 0 ]
the_pid = row [ 1 ]
text = " "
for ite , elem in enumerate ( the_array ) :
2018-04-16 14:50:04 +02:00
2018-09-19 15:45:06 +02:00
if elem is not None and type ( elem ) is str :
if len ( elem ) > padding_row [ ite ] :
text + = " * " + elem [ - padding_row [ ite ] + 6 : ]
padd_off = " " * 5
else :
text + = elem
padd_off = " " * 0
text + = ( padding_row [ ite ] - len ( elem ) ) * " " + padd_off
2017-01-17 14:31:06 +01:00
printstring . append ( ( text , the_pid ) )
return printstring
2017-01-16 17:08:48 +01:00
2017-02-28 10:21:27 +01:00
def log ( data , write_on_disk = False , show_in_board = True ) :
if show_in_board :
printarrayLog . insert ( 0 , data )
printarrayLog . pop ( )
2017-02-15 10:57:41 +01:00
if write_on_disk :
with open ( log_filename , ' a ' ) as log :
log . write ( json . dumps ( data [ 0 ] ) + " \n " )
2017-02-15 09:43:18 +01:00
'''
END MANAGE
'''
def demo ( screen ) :
dashboard = Dashboard ( screen )
confirm = Confirm ( screen )
action_choice = Action_choice ( screen )
show_paste = Show_paste ( screen )
scenes = [
Scene ( [ dashboard ] , - 1 , name = " dashboard " ) ,
Scene ( [ action_choice ] , - 1 , name = " action_choice " ) ,
Scene ( [ confirm ] , - 1 , name = " confirm " ) ,
Scene ( [ show_paste ] , - 1 , name = " show_paste " ) ,
]
2017-02-15 10:19:03 +01:00
2017-02-15 09:43:18 +01:00
screen . set_scenes ( scenes )
2017-02-15 10:19:03 +01:00
time_cooldown = time . time ( ) # Cooldown before refresh
2017-02-15 09:43:18 +01:00
global TABLES
while True :
#Stop on resize
if screen . has_resized ( ) :
screen . _scenes [ screen . _scene_index ] . exit ( )
raise ResizeScreenError ( " Screen resized " , screen . _scenes [ screen . _scene_index ] )
if time . time ( ) - time_cooldown > args . refresh :
cleanRedis ( )
2018-04-16 14:50:04 +02:00
for key , val in iter ( fetchQueueData ( ) . items ( ) ) : #fetch data and put it into the tables
2017-02-15 09:43:18 +01:00
TABLES [ key ] = val
2017-02-15 10:57:41 +01:00
TABLES [ " logs " ] = format_string ( printarrayLog , TABLES_PADDING [ " logs " ] )
2017-02-15 10:19:03 +01:00
#refresh dashboad only if the scene is active (no value selected)
2017-02-15 09:43:18 +01:00
if current_selected_value == 0 :
dashboard . _update ( None )
screen . refresh ( )
time_cooldown = time . time ( )
screen . draw_next_frame ( )
2017-02-15 10:19:03 +01:00
time . sleep ( 0.02 ) #time between screen refresh (For UI navigation, not data actualisation)
2017-02-15 09:43:18 +01:00
2017-01-16 12:18:23 +01:00
if __name__ == " __main__ " :
parser = argparse . ArgumentParser ( description = ' Show info concerning running modules and log suspected stucked modules. May be use to automatically kill and restart stucked one. ' )
2017-01-17 14:31:06 +01:00
parser . add_argument ( ' -r ' , ' --refresh ' , type = int , required = False , default = 5 , help = ' Refresh rate ' )
2017-01-16 12:18:23 +01:00
parser . add_argument ( ' -t ' , ' --treshold ' , type = int , required = False , default = 60 * 10 * 1 , help = ' Refresh rate ' )
parser . add_argument ( ' -k ' , ' --autokill ' , type = int , required = False , default = 0 , help = ' Enable auto kill option (1 for TRUE, anything else for FALSE) ' )
parser . add_argument ( ' -c ' , ' --clear ' , type = int , required = False , default = 0 , help = ' Clear the current module information (Used to clear data from old launched modules) ' )
args = parser . parse_args ( )
configfile = os . path . join ( os . environ [ ' AIL_BIN ' ] , ' packages/config.cfg ' )
if not os . path . exists ( configfile ) :
raise Exception ( ' Unable to find the configuration file. \
Did you set environment variables ? \
Or activate the virtualenv . ' )
2018-04-16 14:50:04 +02:00
cfg = configparser . ConfigParser ( )
2017-01-16 12:18:23 +01:00
cfg . read ( configfile )
# REDIS #
server = redis . StrictRedis (
host = cfg . get ( " Redis_Queues " , " host " ) ,
port = cfg . getint ( " Redis_Queues " , " port " ) ,
2018-05-04 13:53:29 +02:00
db = cfg . getint ( " Redis_Queues " , " db " ) ,
decode_responses = True )
2017-01-16 12:18:23 +01:00
if args . clear == 1 :
clearRedisModuleInfo ( )
lastTime = datetime . datetime . now ( )
module_file_array = set ( )
no_info_modules = { }
path_allmod = os . path . join ( os . environ [ ' AIL_HOME ' ] , ' doc/all_modules.txt ' )
2017-02-28 10:23:22 +01:00
try :
with open ( path_allmod , ' r ' ) as module_file :
for line in module_file :
module_file_array . add ( line [ : - 1 ] )
except IOError as e :
if e . errno == 2 : #module_file not found, creating a new one
print ( path_allmod + " not found. \n Creating a new one. " )
os . system ( " ./../doc/generate_modules_data_flow_graph.sh " )
with open ( path_allmod , ' r ' ) as module_file :
for line in module_file :
module_file_array . add ( line [ : - 1 ] )
2017-01-17 14:31:06 +01:00
cleanRedis ( )
2017-01-16 12:18:23 +01:00
2018-04-16 14:50:04 +02:00
2017-01-17 14:31:06 +01:00
TABLES_TITLES [ " running " ] = format_string ( [ ( [ " Action " , " Queue name " , " PID " , " # " , " S Time " , " R Time " , " Processed element " , " CPU % " , " Mem % " , " Avg CPU % " ] , 0 ) ] , TABLES_PADDING [ " running " ] ) [ 0 ] [ 0 ]
TABLES_TITLES [ " idle " ] = format_string ( [ ( [ " Action " , " Queue " , " PID " , " Idle Time " , " Last paste hash " ] , 0 ) ] , TABLES_PADDING [ " idle " ] ) [ 0 ] [ 0 ]
TABLES_TITLES [ " notRunning " ] = format_string ( [ ( [ " Action " , " Queue " , " State " ] , 0 ) ] , TABLES_PADDING [ " notRunning " ] ) [ 0 ] [ 0 ]
TABLES_TITLES [ " logs " ] = format_string ( [ ( [ " Time " , " Module " , " PID " , " Info " ] , 0 ) ] , TABLES_PADDING [ " logs " ] ) [ 0 ] [ 0 ]
2017-01-16 12:18:23 +01:00
2017-02-14 17:21:52 +01:00
try :
input ( " Press < ENTER > to launch the manager... " )
except SyntaxError :
pass
2017-01-16 12:18:23 +01:00
2017-02-15 08:26:48 +01:00
last_scene = None
2017-01-16 12:18:23 +01:00
while True :
2017-02-15 08:26:48 +01:00
try :
Screen . wrapper ( demo )
sys . exit ( 0 )
except ResizeScreenError as e :
pass
2017-02-15 09:35:03 +01:00
except StopApplication :
sys . exit ( 0 )