Python para emular la cola remota -f?

Tenemos varios servidores de aplicaciones, y un servidor de monitoreo central.

Actualmente estamos ejecutando ssh con “tail -f” desde el servidor de monitoreo para transmitir varios archivos de texto en tiempo real desde los servidores de aplicaciones.

El problema, aparte de la fragilidad de todo el enfoque, es que matar el proceso ssh a veces puede dejar atrás procesos zombie tail. Nos hemos cagado con el uso de -t para crear pseudo-terminales, pero a veces todavía deja a los procesos zombie, y aparentemente también está causando problemas en otros lugares con el producto de progtwigción de trabajos que estamos usando.

Como una solución barata y sucia hasta que podamos obtener el registro centralizado adecuado (Logstash y RabbitMQ, con suerte), espero escribir un contenedor de Python simple que comience con ssh y “tail -f”, aún capture la salida, pero almacene el PID en un archivo de texto en el disco para que podamos eliminar el proceso de cola apropiado más adelante si es necesario.

Al principio intenté usar subprocess.Popen, pero luego encontré problemas para obtener la salida “tail -f” en tiempo real (que luego debe ser redirigida a un archivo) – aparentemente va a haber una gran cantidad de lockings / problemas de búfer

    Algunas fonts parecían recomendar el uso de pexpect, o pxssh o algo así. Idealmente, me gustaría usar solo Python y se incluyen las bibliotecas, si es posible; sin embargo, si una biblioteca es realmente la única forma de hacerlo, entonces estoy abierto a eso.

    ¿Existe una forma fácil y sencilla de hacer que Python inicie ssh con “tail -f”, imprima la salida en tiempo real en STDOUT local aquí (para que pueda redirigir a un archivo local) y también guarde el PID en un archivo para matar mas tarde? O incluso si no uso ssh con tail -f, ¿existe alguna forma de seguir transmitiendo un archivo remoto (casi) en tiempo real que incluya guardar el PID en un archivo?

    Saludos, Victor

    EDITAR: Solo para aclarar, queremos que el proceso final se muera cuando eliminamos el proceso SSH.

    Queremos iniciar ssh y “tail -f” desde el servidor de monitoreo, luego, cuando pulsamos Ctlr-C, el proceso de cola en el cuadro remoto también debería morir , no queremos que se quede atrás. Normalmente, ssh con -t debería solucionarlo, pero no es completamente confiable, por razones que no entiendo, y no funciona bien con nuestra progtwigción de trabajos.

    Por lo tanto, usar la pantalla para mantener vivo el proceso en el otro extremo no es lo que queremos.

    El módulo paramiko admite la conexión con via ssh con python.

    http://www.lag.net/paramiko/

    El pysftp tiene algunos ejemplos de uso y el método de comando de ejecución puede ser lo que está buscando. Creará un archivo como objeto del comando que ejecute. Sin embargo, no puedo decir si te da datos en vivo.

    http://code.google.com/p/pysftp/

    Sé que esto no responde a tus preguntas, pero …

    Tal vez podrías intentar usar la pantalla. Si su sesión se cae, siempre puede volver a unirse y la cola seguirá ejecutándose. También admite multiusuario, por lo que 2 usuarios pueden ver el mismo comando de cola.

    http://en.wikipedia.org/wiki/GNU_Screen

    crear con el nombre “log”:

     screen -S log 

    desconectar:

     [CTRL]+AD 

    volver a colocar

     screen -r log 

    Lista cuando puedes recordar el nombre

     screen -list 

    Para deshacerse de la sesión, simplemente escriba exit mientras esté en ella.

    Creo que la idea de la pantalla es la mejor, pero si no quieres ssh y quieres un script de python para hacerlo. Aquí hay una forma simple en XMLRPC de obtener la información. Solo se actualizará cuando se haya agregado algo al archivo en cuestión.

    Este es el archivo del cliente. Dígale a esto de qué archivo quiere leer y en qué computadora está encendido.

     #!/usr/bin/python # This should be run on the computer you want to output the files # You must pass a filename and a location # filename must be the full path from the root directory, or relative path # from the directory the server is running # location must be in the form of http://location:port (ie http:localhost:8000) import xmlrpclib, time, sys, os def tail(filename, location): # connect to server s = xmlrpclib.ServerProxy(location) # get starting length of file curSeek = s.GetSize(filename) # constantly check while 1: time.sleep(1) # make sure to sleep # get a new length of file and check for changes prevSeek = curSeek # some times it fails if the file is being writter to, # we'll wait another second for it to finish try: curSeek = s.GetSize(filename) except: pass # if file length has changed print it if prevSeek != curSeek: print s.tail(filename, prevSeek), def main(): # check that we got a file passed to us if len(sys.argv) != 3 or not os.path.isfile(sys.argv[1]): print 'Must give a valid filename.' return # run tail function tail(sys.argv[1], sys.argv[2]) main() 

    Este es el servidor que ejecutará en cada computadora que tenga un archivo que desee ver. No es nada lujoso. Puedes demonizarlo si quieres. Simplemente ejecútelo y su cliente debe conectarse a él si le dice al cliente dónde está y tiene los puertos correctos abiertos.

     #!/usr/bin/python # This runs on the computer(s) you want to read the file from # Make sure to change out the HOST and PORT variables HOST = 'localhost' PORT = 8000 from SimpleXMLRPCServer import SimpleXMLRPCServer from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler import time, os def GetSize(filename): # get file size return os.stat(filename)[6] def tail(filename, seek): #Set the filename and open the file f = open(filename,'r') #Find the size of the file and move to the end f.seek(seek) return f.read() def CreateServer(): # Create server server = SimpleXMLRPCServer((HOST, PORT), requestHandler=SimpleXMLRPCRequestHandler) # register functions server.register_function(tail, 'tail') server.register_function(GetSize, 'GetSize') # Run the server's main loop server.serve_forever() # start server CreateServer() 

    Lo ideal es que ejecute el servidor una vez, luego desde el cliente ejecute “python client.py sample.log http: // somehost: 8000 ” y debería comenzar a funcionar. Espero que ayude.

    He publicado una pregunta sobre algo como esto con código (paramiko)

    tail -f over ssh con Paramiko tiene un retraso creciente

    Escribí una función que hace eso:

     import paramiko import time import json DEFAULT_MACHINE_USERNAME="USERNAME" DEFAULT_KEY_PATH="DEFAULT_KEY_PATH" def ssh_connect(machine, username=DEFAULT_MACHINE_USERNAME, key_filename=DEFAULT_KEY_PATH): ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(hostname=machine, username=username, key_filename=key_filename) return ssh def tail_remote_file(hostname, filepath, key_path=DEFAULT_KEY_PATH, close_env_variable="CLOSE_TAIL_F", env_file='~/.profile'): ssh = ssh_connect(hostname, key_filename=key_path) def set_env_variable(to_value): to_value_str = "true" if to_value else "false" from_value_str = "false" if to_value else "true" ssh.exec_command('sed -i \'s/export %s=%s/export %s=%s/g\' %s' % (close_env_variable, from_value_str, close_env_variable, to_value_str, env_file)) time.sleep(1) def get_env_variable(): command = "source .profile; echo $%s" % close_env_variable stdin, stdout_i, stderr = ssh.exec_command(command) print(command) out = stdout_i.read().replace('\n', '') return out def get_last_line_number(lines_i, line_num): return int(lines_i[-1].split('\t')[0]) + 1 if lines_i else line_num def execute_command(line_num): command = "cat -n %s | tail --lines=+%d" % (filepath, line_num) stdin, stdout_i, stderr = ssh.exec_command(command) stderr = stderr.read() if stderr: print(stderr) return stdout_i.readlines() stdout = get_env_variable() if not stdout: ssh.exec_command("echo 'export %s=false' >> %s" % (close_env_variable, env_file)) else: ssh.exec_command( 'sed -i \'s/export %s=true/export %s=false/g\' %s' % (close_env_variable, close_env_variable, env_file)) set_env_variable(False) lines = execute_command(0) last_line_num = get_last_line_number(lines, 0) while not json.loads(get_env_variable()): for l in lines: print('\t'.join(t.replace('\n', '') for t in l.split('\t')[1:])) last_line_num = get_last_line_number(lines, last_line_num) lines = execute_command(last_line_num) time.sleep(1) ssh.close() 

    He escrito una biblioteca que te permite hacer esto: echa un vistazo a la función “remota” de PimpedSubprocess (en github) o PimpedSubprocess (en PyPI)