2014-10-28 70 views
2

我想拖尾日誌文件,它的工作原理。但我還需要能夠分析輸出並記錄錯誤等。我在Paramiko期望的github頁面上使用基礎示例,我無法弄清楚如何執行此操作。Paramiko期望 - 尾部

import traceback 
import paramiko 
from paramikoe import SSHClientInteraction 

def main(): 

    # Set login credentials and the server prompt 
    hostname = 'server' 
    username = 'username' 
    password = 'xxxxxxxx' 
    port = 22 

    # Use SSH client to login 
    try: 

     # Create a new SSH client object 
     client = paramiko.SSHClient() 

     # Set SSH key parameters to auto accept unknown hosts 
     client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 

     # Connect to the host 
     client.connect(hostname, port, username, password) 

     # Create a client interaction class which will interact with the host 
     interact = SSHClientInteraction(client, timeout=10, display=False) 

     # Send the tail command 
     interact.send('tail -f /var/log/log') 

     # Now let the class tail the file for us 
     interact.tail(line_prefix=hostname+': ') 




    except KeyboardInterrupt: 
     print 'Ctrl+C interruption detected, stopping tail' 
    except Exception: 
     traceback.print_exc() 
    finally: 
     try: 
      client.close() 
     except: 
      pass 

if __name__ == '__main__': 
    main() 

這工作到它的點打印日誌生活時間,它是運行在控制檯,但我從這樣的paramiko想到我無法弄清楚如何能夠遍歷輸出中並尋找價值?

幫助?

https://github.com/fgimian/paramiko-expect

這也將有助於能夠日誌的輸出推到一個本地文件事件的歷史備份。

更新: 所以我看到這個信息顯示的方式是使用sys.stdout。我沒有經驗豐富的如何將輸出結果轉化爲一種可以接受的方式,或者如何將其修改爲不同類型的輸出,但仍然可以工作。我嘗試給這個模塊的創建者發郵件沒有太大的成功。

這個模塊非常接近成爲一個非常強大的工具,如果我能夠找出實際監控輸出的能力,它將使模塊成爲非常好的工具。請幫忙!的的paramiko-期待

輸出部分:尾功能:

 # Read the output one byte at a time so we can detect \n correctly 
     buffer = self.channel.recv(1) 

     # If we have an empty buffer, then the SSH session has been closed 
     if len(buffer) == 0: 
      break 

     # Strip all ugly \r (Ctrl-M making) characters from the current 
     # read 
     buffer = buffer.replace('\r', '') 

     # Add the currently read buffer to the current line output 
     current_line += buffer 

     # Display the last read line in realtime when we reach a \n 
     # character 
     if current_line.endswith('\n'): 
      if line_counter and line_prefix: 
       sys.stdout.write(line_prefix) 
      if line_counter: 
       sys.stdout.write(current_line) 
       sys.stdout.flush() 
      line_counter += 1 
      current_line = '' 

的paramiko期待模塊

# 
# Paramiko Expect 
# 
# Written by Fotis Gimian 
# http://github.com/fgimian 
# 
# This library works with a Paramiko SSH channel to provide native SSH 
# expect-like handling for servers. The library may be used to interact 
# with commands like 'configure' or Cisco IOS devices or with interactive 
# Unix scripts or commands. 
# 
# You must have Paramiko installed in order to use this library. 
# 
import sys 
import re 
import socket 
# Windows does not have termios 
try: 
    import termios 
    import tty 
    has_termios = True 
except ImportError: 
    import threading 
    has_termios = False 

import select 


class SSHClientInteraction: 
    """This class allows an expect-like interface to Paramiko which allows 
    coders to interact with applications and the shell of the connected 
    device. 
    """ 

    def __init__(self, client, timeout=60, newline='\r', buffer_size=1024, 
       display=False): 
     """The constructor for our SSHClientInteraction class. 

     Arguments: 
     client -- A Paramiko SSHClient object 

     Keyword arguments: 
     timeout -- THe connection timeout in seconds 
     newline -- The newline character to send after each command 
     buffer_size -- The amount of data (in bytes) that will be read at a 
         time after a command is run 
     display -- Whether or not the output should be displayed in real-time 
        as it is being performed (especially useful when debugging) 

     """ 
     self.channel = client.invoke_shell() 
     self.newline = newline 
     self.buffer_size = buffer_size 
     self.display = display 
     self.timeout = timeout 
     self.current_output = '' 
     self.current_output_clean = '' 
     self.current_send_string = '' 
     self.last_match = '' 

    def __del__(self): 
     """The destructor for our SSHClientInteraction class.""" 
     self.close() 

    def close(self): 
     """Attempts to close the channel for clean completion.""" 
     try: 
      self.channel.close() 
     except: 
      pass 

    def expect(self, re_strings=''): 
     """This function takes in a regular expression (or regular expressions) 
     that represent the last line of output from the server. The function 
     waits for one or more of the terms to be matched. The regexes are 
     matched using expression \n<regex>$ so you'll need to provide an 
     easygoing regex such as '.*server.*' if you wish to have a fuzzy match. 

     Keyword arguments: 
     re_strings -- Either a regex string or list of regex strings that 
         we should expect. If this is not specified, then 
         EOF is expected (i.e. the shell is completely closed 
         after the exit command is issued) 

     Returns: 
     - EOF: Returns -1 
     - Regex String: When matched, returns 0 
     - List of Regex Strings: Returns the index of the matched string as 
           an integer 
     """ 

     # Set the channel timeout 
     self.channel.settimeout(self.timeout) 

     # Create an empty output buffer 
     self.current_output = '' 

     # This function needs all regular expressions to be in the form of a 
     # list, so if the user provided a string, let's convert it to a 1 
     # item list. 
     if len(re_strings) != 0 and isinstance(re_strings, str): 
      re_strings = [re_strings] 

     # Loop until one of the expressions is matched or loop forever if 
     # nothing is expected (usually used for exit) 
     while (
      len(re_strings) == 0 or 
      not [re_string 
       for re_string in re_strings 
       if re.match('.*\n' + re_string + '$', 
          self.current_output, re.DOTALL)] 
     ): 

      # Read some of the output 
      buffer = self.channel.recv(self.buffer_size) 

      # If we have an empty buffer, then the SSH session has been closed 
      if len(buffer) == 0: 
       break 

      # Strip all ugly \r (Ctrl-M making) characters from the current 
      # read 
      buffer = buffer.replace('\r', '') 

      # Display the current buffer in realtime if requested to do so 
      # (good for debugging purposes) 
      if self.display: 
       sys.stdout.write(buffer) 
       sys.stdout.flush() 

      # Add the currently read buffer to the output 
      self.current_output += buffer 

     # Grab the first pattern that was matched 
     if len(re_strings) != 0: 
      found_pattern = [(re_index, re_string) 
          for re_index, re_string in enumerate(re_strings) 
          if re.match('.*\n' + re_string + '$', 
             self.current_output, re.DOTALL)] 

     self.current_output_clean = self.current_output 

     # Clean the output up by removing the sent command 
     if len(self.current_send_string) != 0: 
      self.current_output_clean = (
       self.current_output_clean.replace(
        self.current_send_string + '\n', '')) 

     # Reset the current send string to ensure that multiple expect calls 
     # don't result in bad output cleaning 
     self.current_send_string = '' 

     # Clean the output up by removing the expect output from the end if 
     # requested and save the details of the matched pattern 
     if len(re_strings) != 0: 
      self.current_output_clean = (
       re.sub(found_pattern[0][1] + '$', '', 
         self.current_output_clean)) 
      self.last_match = found_pattern[0][1] 
      return found_pattern[0][0] 
     else: 
      # We would socket timeout before getting here, but for good 
      # measure, let's send back a -1 
      return -1 

    def send(self, send_string): 
     """Saves and sends the send string provided""" 
     self.current_send_string = send_string 
     self.channel.send(send_string + self.newline) 

    def tail(self, line_prefix=None): 
     """This function takes control of an SSH channel and displays line 
     by line of output as \n is recieved. This function is specifically 
     made for tail-like commands. 

     Keyword arguments: 
     line_prefix -- Text to append to the left of each line of output. 
         This is especially useful if you are using my 
         MultiSSH class to run tail commands over multiple 
         servers. 

     """ 

     # Set the channel timeout to the maximum integer the server allows, 
     # setting this to None breaks the KeyboardInterrupt exception and 
     # won't allow us to Ctrl+C out of teh script 
     self.channel.settimeout(sys.maxint) 

     # Create an empty line buffer and a line counter 
     current_line = '' 
     line_counter = 0 

     # Loop forever, Ctrl+C (KeyboardInterrupt) is used to break the tail 
     while True: 

      # Read the output one byte at a time so we can detect \n correctly 
      buffer = self.channel.recv(1) 

      # If we have an empty buffer, then the SSH session has been closed 
      if len(buffer) == 0: 
       break 

      # Strip all ugly \r (Ctrl-M making) characters from the current 
      # read 
      buffer = buffer.replace('\r', '') 

      # Add the currently read buffer to the current line output 
      current_line += buffer 

      # Display the last read line in realtime when we reach a \n 
      # character 
      if current_line.endswith('\n'): 
       if line_counter and line_prefix: 
        sys.stdout.write(line_prefix) 
       if line_counter: 
        sys.stdout.write(current_line) 
        sys.stdout.flush() 
       line_counter += 1 
       current_line = '' 

    def take_control(self): 
     """This function is a better documented and touched up version of the 
     posix_shell function found in the interactive.py demo script that 
     ships with Paramiko""" 

     if has_termios: 
      # Get attributes of the shell you were in before going to the 
      # new one 
      original_tty = termios.tcgetattr(sys.stdin) 
      try: 
       tty.setraw(sys.stdin.fileno()) 
       tty.setcbreak(sys.stdin.fileno()) 

       # We must set the timeout to 0 so that we can bypass times when 
       # there is no available text to receive 
       self.channel.settimeout(0) 

       # Loop forever until the user exits (i.e. read buffer is empty) 
       while True: 
        select_read, select_write, select_exception = (
         select.select([self.channel, sys.stdin], [], [])) 
        # Read any output from the terminal and print it to the 
        # screen. With timeout set to 0, we just can ignore times 
        # when there's nothing to receive. 
        if self.channel in select_read: 
         try: 
          buffer = self.channel.recv(self.buffer_size) 
          if len(buffer) == 0: 
           break 
          sys.stdout.write(buffer) 
          sys.stdout.flush() 
         except socket.timeout: 
          pass 
        # Send any keyboard input to the terminal one byte at a 
        # time 
        if sys.stdin in select_read: 
         buffer = sys.stdin.read(1) 
         if len(buffer) == 0: 
          break 
         self.channel.send(buffer) 
      finally: 
       # Restore the attributes of the shell you were in 
       termios.tcsetattr(sys.stdin, termios.TCSADRAIN, original_tty) 
     else: 
      def writeall(sock): 
       while True: 
        buffer = sock.recv(self.buffer_size) 
        if len(buffer) == 0: 
         break 
        sys.stdout.write(buffer) 
        sys.stdout.flush() 

      writer = threading.Thread(target=writeall, args=(self.channel,)) 
      writer.start() 

      try: 
       while True: 
        buffer = sys.stdin.read(1) 
        if len(buffer) == 0: 
         break 
        self.channel.send(buffer) 
      # User has hit Ctrl+Z or F6 
      except EOFError: 
       pass 
+0

爲什麼不遍歷''scp'ing在/ var /日誌/ log'到本地主機,並在這裏進行分析呢?您可以使用diffutils來查找新的零件。 – 2014-10-28 16:46:17

回答

1

所以這個問題已經運行了幾乎24小時在3個不同的網站並沒有一個真正的答案是與此相關的。我有些震驚。我嘗試將腳本作爲子進程運行,然後將輸出管道輸出到stdout。它的工作原理:

import subprocess 
proc = subprocess.Popen(['python','tail_try.py'],stdout=subprocess.PIPE) 
for line in iter(proc.stdout.readline, ''): 
     print line 

我現在可以控制每行打印輸出proc.stdout.readline

真的很簡單。

2

我是paramiko-expect的作者。

我剛剛在0.2模塊中實現了一個新功能,它允許您指定一個回調方法,以便您可以隨意處理當前行。在顯示輸出之前,您可以使用它來grep輸出或進一步處理它。預計回調函數將返回要完成操作當前行後發送到sys.stdout.write的字符串。

下面是一個例子:

import traceback 
import paramiko 
from paramikoe import SSHClientInteraction 


def process_tail(line_prefix, current_line): 
    if current_line.startswith('hello'): 
     return current_line 
    else: 
     return '' 


def main(): 

    # Set login credentials and the server prompt 
    hostname = 'localhost' 
    username = 'fots' 
    password = 'password123' 
    port = 22 

    # Use SSH client to login 
    try: 

     # Create a new SSH client object 
     client = paramiko.SSHClient() 

     # Set SSH key parameters to auto accept unknown hosts 
     client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 

     # Connect to the host 
     client.connect(hostname, port, username, password) 

     # Create a client interaction class which will interact with the host 
     interact = SSHClientInteraction(client, timeout=10, display=False) 

     # Send the tail command 
     interact.send('tail -f /home/fots/something.log') 

     # Now let the class tail the file for us 
     interact.tail(line_prefix=hostname+': ', callback=process_tail) 

    except KeyboardInterrupt: 
     print 'Ctrl+C interruption detected, stopping tail' 
    except Exception: 
     traceback.print_exc() 
    finally: 
     try: 
      client.close() 
     except: 
      pass 

if __name__ == '__main__': 
    main() 
+0

不錯!以編程方式停止尾巴的正確方法是什麼?在回調中引發異常? – monkut 2016-03-14 03:43:32

+0

@monkut好問題。現在,唯一的方法是引發一個異常來中斷tail函數中的while循環。但是,如果你這樣做,你需要確保關閉連接。我認爲我可以實現一些更優雅的做法,但是,如果您發現引發異常無效,歡迎您在https://github.com/fgimian/paramiko-expect/issues上創建一個問題足夠好:) – 2016-03-15 04:54:30

+0

PS:很明顯,當我編寫paramiko期望時,我對tail函數有一個非常具體的用例,並且所有內容都寫入stdout。但是,如果您希望看到某種方法來避免這種情況發生,以便您可以在一臺或多臺遠程服務器上實時處理文件的輸出,請提出問題。我認爲這將是一個非常好的主意,但這並不是我在開始寫模塊時所做的。 – 2016-03-15 05:00:33