Paramiko实现远程访问配置
#!/usr/bin/env python
import paramiko import logging import socket import time import datetime #=====================# # class MySSH # #=====================# class MySSH: ''' Create an SSH connection to a server and execute commands. Here is a typical usage: ssh = MySSH() ssh.connect('host', 'user', 'password', port=22) if ssh.connected() is False: sys.exit('Connection failed') # Run a command that does not require input. status, output = ssh.run('uname -a') print 'status = %d' % (status) print 'output (%d):' % (len(output)) print '%s' % (output) # Run a command that does requires input. status, output = ssh.run('sudo uname -a', 'sudo-password') print 'status = %d' % (status) print 'output (%d):' % (len(output)) print '%s' % (output) ''' def __init__(self, compress=True, verbose=False): ''' Setup the initial verbosity level and the logger. @param compress Enable/disable compression. @param verbose Enable/disable verbose messages. ''' self.ssh = None self.transport = None self.compress = compress self.bufsize = 65536 # Setup the logger self.logger = logging.getLogger('MySSH') self.set_verbosity(verbose) fmt = '%(asctime)s MySSH:%(funcName)s:%(lineno)d %(message)s' format = logging.Formatter(fmt) handler = logging.StreamHandler() handler.setFormatter(format) self.logger.addHandler(handler) self.info = self.logger.info def __del__(self): if self.transport is not None: self.transport.close() self.transport = None def connect(self, hostname, username, password, port=22): ''' Connect to the host. @param hostname The hostname. @param username The username. @param password The password. @param port The port (default=22). @returns True if the connection succeeded or false otherwise. ''' self.info('connecting %s@%s:%d' % (username, hostname, port)) self.hostname = hostname self.username = username self.port = port self.ssh = paramiko.SSHClient() self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: self.ssh.connect(hostname=hostname, port=port, username=username, password=password) self.transport = self.ssh.get_transport() self.transport.use_compression(self.compress) self.info('succeeded: %s@%s:%d' % (username, hostname, port)) except socket.error as e: self.transport = None self.info('failed: %s@%s:%d: %s' % (username, hostname, port, str(e))) except paramiko.BadAuthenticationType as e: self.transport = None self.info('failed: %s@%s:%d: %s' % (username, hostname, port, str(e))) return self.transport is not None def run(self, cmd, input_data=None, timeout=10): ''' Run a command with optional input data. Here is an example that shows how to run commands with no input: ssh = MySSH() ssh.connect('host', 'user', 'password') status, output = ssh.run('uname -a') status, output = ssh.run('uptime') Here is an example that shows how to run commands that require input: ssh = MySSH() ssh.connect('host', 'user', 'password') status, output = ssh.run('sudo uname -a', '<sudo-password>') @param cmd The command to run. @param input_data The input data (default is None). @param timeout The timeout in seconds (default is 10 seconds). @returns The status and the output (stdout and stderr combined). ''' self.info('running command: (%d) %s' % (timeout, cmd)) if self.transport is None: self.info('no connection to %s@%s:%s' % (str(self.username), str(self.hostname), str(self.port))) return -1, 'ERROR: connection not established\n' # Fix the input data. input_data = self._run_fix_input_data(input_data) # Initialize the session. self.info('initializing the session') session = self.transport.open_session() session.set_combine_stderr(True) session.get_pty() session.exec_command(cmd) output = self._run_poll(session, timeout, input_data) status = session.recv_exit_status() self.info('output size %d' % (len(output))) self.info('status %d' % (status)) return status, output def connected(self): ''' Am I connected to a host? @returns True if connected or false otherwise. ''' return self.transport is not None def set_verbosity(self, verbose): ''' Turn verbose messages on or off. @param verbose Enable/disable verbose messages. ''' if verbose > 0: self.logger.setLevel(logging.INFO) else: self.logger.setLevel(logging.ERROR) def _run_fix_input_data(self, input_data): ''' Fix the input data supplied by the user for a command. @param input_data The input data (default is None). @returns the fixed input data. ''' if input_data is not None: if len(input_data) > 0: if '\\n' in input_data: # Convert \n in the input into new lines. lines = input_data.split('\\n') input_data = '\n'.join(lines) return input_data.split('\n') return [] def _run_send_input(self, session, stdin, input_data): ''' Send the input data. @param session The session. @param stdin The stdin stream for the session. @param input_data The input data (default is None). ''' if input_data is not None: self.info('session.exit_status_ready() %s' % str(session.exit_status_ready())) self.info('stdin.channel.closed %s' % str(stdin.channel.closed)) if stdin.channel.closed is False: self.info('sending input data') stdin.write(input_data) def _run_poll(self, session, timeout, input_data): ''' Poll until the command completes. @param session The session. @param timeout The timeout in seconds. @param input_data The input data. @returns the output ''' interval = 0.1 maxseconds = timeout maxcount = maxseconds / interval # Poll until completion or timeout # Note that we cannot directly use the stdout file descriptor # because it stalls at 64K bytes (65536). input_idx = 0 timeout_flag = False self.info('polling (%d, %d)' % (maxseconds, maxcount)) start = datetime.datetime.now() start_secs = time.mktime(start.timetuple()) output = '' session.setblocking(0) while True: if session.recv_ready(): data = session.recv(self.bufsize) output += data self.info('read %d bytes, total %d' % (len(data), len(output))) if session.send_ready(): # We received a potential prompt. # In the future this could be made to work more like # pexpect with pattern matching. if input_idx < len(input_data): data = input_data[input_idx] + '\n' input_idx += 1 self.info('sending input data %d' % (len(data))) session.send(data) self.info('session.exit_status_ready() = %s' % (str(session.exit_status_ready()))) if session.exit_status_ready(): break # Timeout check now = datetime.datetime.now() now_secs = time.mktime(now.timetuple()) et_secs = now_secs - start_secs self.info('timeout check %d %d' % (et_secs, maxseconds)) if et_secs > maxseconds: self.info('polling finished - timeout') timeout_flag = True break time.sleep(0.200) self.info('polling loop ended') if session.recv_ready(): data = session.recv(self.bufsize) output += data self.info('read %d bytes, total %d' % (len(data), len(output))) self.info('polling finished - %d output bytes' % (len(output))) if timeout_flag: self.info('appending timeout message') output += '\nERROR: timeout after %d seconds\n' % (timeout) session.close() return output # ================================================================ # MAIN # ================================================================ if __name__ == '__main__': import sys # Access variables. hostname = 'hostname' port = 22 username = 'username' password = 'password' sudo_password = password # assume that it is the same password # Create the SSH connection ssh = MySSH() ssh.set_verbosity(False) ssh.connect(hostname=hostname, username=username, password=password, port=port) if ssh.connected() is False: print 'ERROR: connection failed.' sys.exit(1) def run_cmd(cmd, indata=None): ''' Run a command with optional input. @param cmd The command to execute. @param indata The input data. @returns The command exit status and output. Stdout and stderr are combined. ''' print print '=' * 64 print 'command: %s' % (cmd) status, output = ssh.run(cmd, indata) print 'status : %d' % (status) print 'output : %d bytes' % (len(output)) print '=' * 64 print '%s' % (output) run_cmd('uname -a') run_cmd('sudo ls -ltrh /var/log | tail', sudo_password) # sudo command