2011-10-06 30 views
9

Chúng tôi có một số máy chủ ứng dụng và máy chủ giám sát trung tâm.Python để mô phỏng đuôi từ xa -f?

Hiện tại, chúng tôi đang chạy ssh với "tail -f" từ máy chủ giám sát để truyền một số nhật ký văn bản trong thời gian thực từ các máy chủ ứng dụng.

Vấn đề, ngoài sự lộng lẫy của toàn bộ cách tiếp cận là việc giết chết quá trình ssh đôi khi có thể khiến cho quá trình đuôi zombie bị trễ. Chúng ta đã bỏ qua việc sử dụng -t để tạo các terminal giả, nhưng đôi khi nó vẫn để lại các tiến trình zombie xung quanh, và -t dường như cũng gây ra các vấn đề ở nơi khác với sản phẩm lập kế hoạch công việc mà chúng ta đang sử dụng.

Là giải pháp rẻ và bẩn cho đến khi chúng tôi có thể khai thác gỗ tập trung thích hợp (Logstash và RabbitMQ, hy vọng), tôi hy vọng sẽ viết một trình bao Python đơn giản sẽ bắt đầu ssh và "tail -f", vẫn capture đầu ra, nhưng lưu trữ PID vào một textfile trên đĩa để chúng ta có thể giết chết quá trình đuôi thích hợp sau này nếu cần thiết. Lúc đầu tôi đã thử sử dụng subprocess.Popen, nhưng sau đó tôi nhấn các vấn đề với thực sự nhận được đầu ra "tail -f" trong thời gian thực (sau đó cần được chuyển hướng đến một tệp) - dường như sẽ có một máy chủ của các vấn đề chặn/đệm.

Một số nguồn dường như khuyên bạn nên sử dụng pexpect, hoặc pxssh hoặc một cái gì đó tương tự. Lý tưởng nhất là tôi muốn sử dụng chỉ Python và nó bao gồm các thư viện, nếu có thể - tuy nhiên, nếu một thư viện thực sự là cách duy nhất để làm điều này, thì tôi mở nó ra. Có một cách dễ dàng để bắt Python khởi động ssh với "tail -f", lấy đầu ra trong thời gian thực được in tới STDOUT cục bộ tại đây (vì vậy tôi có thể chuyển hướng đến một tệp cục bộ), và cũng có thể lưu PID vào một tập tin để giết sau này? Hoặc thậm chí nếu tôi không sử dụng ssh với tail -f, một số cách vẫn còn streaming một tập tin từ xa trong (gần) thời gian thực bao gồm tiết kiệm PID vào một tập tin?

Chúc mừng, Victor

EDIT: Chỉ cần làm rõ - chúng tôi muốn quá trình đuôi chết khi chúng ta giết quá trình SSH.

Chúng tôi muốn bắt đầu ssh và "tail -f" từ máy chủ giám sát, khi đó Ctlr-C, quá trình đuôi trên hộp điều khiển từ xa cũng sẽ chết - chúng tôi không muốn nó ở lại phía sau. Thông thường ssh với -t nên sửa chữa nó, nhưng nó không phải là hoàn toàn đáng tin cậy, vì lý do tôi không hiểu, và nó không chơi độc đáo với lịch trình công việc của chúng tôi.

Do đó, việc sử dụng màn hình để giữ cho quá trình hoạt động ở đầu kia không phải là điều chúng tôi muốn.

+0

Xem thêm http://stackoverflow.com/questions/136168/get-last-n-lines-of-a-file-with-python-similar-to-tail – unmounted

+0

@bmvou, mà câu hỏi không có gì về 'tail -f' –

+0

Có lẽ http://stackoverflow.com/questions/1703640/how-to-implement-a-pythonic-equivalent-of-tail-f? – agf

Trả lời

0

Mô-đun paramiko hỗ trợ kết nối thông qua ssh với python.

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

Các pysftp có một số ví dụ của việc sử dụng nó và phương thức execute lệnh có thể là những gì bạn đang tìm kiếm. Nó sẽ tạo một tệp như đối tượng của lệnh bạn thực hiện. Tôi không thể nói nếu nó cung cấp cho bạn dữ liệu trực tiếp.

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

6

Tôi biết điều này không trả lời câu hỏi của bạn, nhưng ...

Có thể bạn có thể thử sử dụng màn hình. Nếu phiên của bạn giảm xuống, bạn luôn có thể lắp lại và đuôi sẽ vẫn chạy. Nó cũng hỗ trợ multiuser, do đó, 2 người dùng có thể xem cùng một lệnh đuôi.

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

tạo với tên "log":

screen -S log 

ngắt kết nối:

[CTRL]+A D 

lắp lại danh sách

screen -r log 

khi bạn có thể nhớ tên

screen -list 

Để loại bỏ phiên, chỉ cần nhập exit khi ở trong đó.

+5

+1 để sử dụng đúng công cụ cho công việc –

+0

màn hình và vải – Tom

2

Tôi nghĩ rằng ý tưởng màn hình là ý tưởng tốt nhất, nhưng nếu bạn không muốn ssh và bạn muốn có một kịch bản python để làm điều đó. Đây là một cách XMLRPC pythonic đơn giản để nhận thông tin. Nó sẽ chỉ cập nhật khi một cái gì đó đã được nối thêm vào tập tin trong câu hỏi.

Đây là tệp khách hàng. Bạn cho biết tệp nào bạn muốn đọc và máy tính của nó đang bật.

#!/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() 

Đây là máy chủ bạn sẽ chạy trên mỗi máy tính có tệp bạn muốn xem. Không có gì lạ mắt. Bạn có thể daemonize nó nếu bạn muốn. Bạn chỉ cần chạy nó, và khách hàng của bạn nên kết nối với nó nếu bạn nói với khách hàng nơi nó được và bạn có các cổng bên phải mở.

#!/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() 

Lý tưởng nhất là bạn chạy các máy chủ cùng một lúc, sau đó từ chạy client "trăn client.py sample.log http://somehost:8000" và nó sẽ bắt đầu đi. Hy vọng rằng sẽ giúp.

0

Tôi đã viết một chức năng mà làm điều đó:

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() 
0

tôi đã viết một thư viện cho phép bạn làm điều này - hãy kiểm tra tính năng "từ xa" của PimpedSubprocess (trên github) hoặc PimpedSubprocess (trên PyPI)

Các vấn đề liên quan