2011-10-06 14 views
9

Tenemos varios servidores de aplicaciones y un servidor de supervisión central.Python para emular la cola remota -f?

Actualmente estamos ejecutando ssh con "tail -f" desde el servidor de supervisión para transmitir varios archivos de registro de texto en tiempo real desde los servidores de la aplicación.

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

Como una solución barata y sucia hasta que podamos obtener el registro centralizado correcto (Logstash y RabbitMQ, con suerte), espero escribir un envoltorio simple de Python que comience ssh y "tail -f", aún capturar el resultado, pero almacena el PID en un archivo de texto en el disco para que podamos matar el proceso de cola apropiado más adelante si es necesario.

Al principio intenté usar el subproceso.Popen, pero luego tuve problemas para recuperar la salida "tail -f" en tiempo real (que luego debe redireccionarse a un archivo). Aparentemente habrá una host de problemas de bloqueo/búfer.

Algunas fuentes parecían recomendar el uso de pexpect o pxssh o algo así. Idealmente, me gustaría utilizar solo Python y sus bibliotecas incluidas, si es posible; sin embargo, si una biblioteca es realmente la única manera de hacerlo, entonces estoy abierto a eso.

¿Hay alguna manera fácil y fácil de hacer que Python inicie ssh con "tail -f", obtenga la salida impresa en tiempo real en STDOUT local aquí (para que pueda redirigir a un archivo local) y también guarde el PID a un archivo para matar más tarde? O incluso si no uso ssh con tail -f, ¿alguna forma de seguir transmitiendo un archivo remoto en (casi) en tiempo real que incluye guardar el PID en un archivo?

Saludos, Victor

EDIT: Solo para aclarar - nosotros queremos el proceso de la cola de morir cuando matamos el proceso de SSH.

Queremos empezar ssh y "tail-f" del servidor de supervisión, entonces cuando Ctlr-C que, el proceso de la cola en la caja remota debe morir, así - que No quiere que se quede detrás. Normalmente ssh con -t debería arreglarlo, pero no es totalmente confiable, por razones que no entiendo, y no funciona bien con nuestra programación de trabajo.

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

+0

Vea también http://stackoverflow.com/questions/136168/get-last-n-lines-of-a-file-with-python-similar-to-tail – unmounted

+0

@bmvou, que pregunta no tiene nada sobre 'tail -f' –

+0

Tal vez http://stackoverflow.com/questions/1703640/how-to-implement-a-pythonic-equivalent-of-tail-f? – agf

Respuesta

0

El módulo paramiko admite la conexión a través de ssh con python.

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

El pysftp tiene algunos ejemplos de su uso y el método de comando de ejecución podría ser lo que tu buscas. Creará un archivo como objeto del comando que ejecuta. Sin embargo, no puedo decir si te da datos en vivo.

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

6

Sé que esto no responde a sus preguntas, pero ...

Tal vez usted podría tratar de usar la pantalla. Si su sesión cae, siempre puede volver a conectar y la cola seguirá ejecutándose. También es compatible con multiusuario, por lo que 2 usuarios pueden ver el mismo comando de cola.

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

crear con el nombre de "log":

screen -S log 

desconexión:

[CTRL]+A D 

vuelva a conectar lista

screen -r log 

cuando se puede recordar el nombre

screen -list 

para deshacerse de la sesión, sólo tiene que escribir exit mientras que en el mismo.

+5

+1 para usar la herramienta correcta para el trabajo –

+0

pantalla y tela – Tom

2

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

Este es el archivo del cliente. Usted le dice a qué archivo desea leer y en qué computadora está.

#!/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 (i.e. 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 donde ejecutará esto en cada computadora que tenga un archivo que desee examinar. No es nada lujoso. Puedes demonizarlo si quieres. Simplemente ejecute, 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 sería que se ejecuta el servidor una vez, y luego de la carrera cliente "pitón client.py sample.log http://somehost:8000" y debería empezar a ir. Espero que ayude.

0

escribí una función que haga 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() 
Cuestiones relacionadas