python自动化运维封装paramiko和pexpect

"本文介绍了一个Python自动化运维框架,该框架结合了paramiko和pexpect库,用于在Windows和Linux上登录设备并执行交互式命令。通过处理控制字符,如x1b[?2004h,确保命令执行和输出的可靠性。框架包含了连接设备、清除屏幕、读取设备响应和执行命令等功能,适用于SSH连接。同时,提供了处理设备输出中的控制字符的方法,以确保在打印和期待模式匹配时的正确性。"
摘要由CSDN通过智能技术生成

同时封装paramiko和pexpect的2个类实现代码:在windows上没有pexpect可以用paramiko,在linux上Pexpect随处可见大部分情况下不用额外安装就可以使用。

paramiko或者pexpect登录到设备并打开shell执行交互式命令。完整示例python自动化运维框架封装paramiko和pexpect-其它文档类资源-CSDN下载

字符串中的格式字符

使用print输出调试信息时可能掩盖格式控制字符(非ascii字符不可见),但是控制字符是实际存在的,需要使用re.sub进行字符串替换escape控制字符:

>>> test="\x1b[?2004hroot"
>>> print(test)
root
>>> alist=[test, "control string"]
>>> print(alist)
['\x1b[?2004hroot', 'control string']
>>> 

>>> import re
>>> print(re.sub("\\x1b\[\?2004h","", test))
root
>>> 

>>> print([re.sub("\\x1b\[\?2004h","", test), "control string"])
['root', 'control string']
>>> prompt=re.sub("\\x1b\[\?2004h","", test)
>>> alist=[prompt,"control string"]
>>> print(alist)
['root', 'control string']
>>> 

expect()方法中可以使用可能提示符的列表来提高可靠性,pattern为列表的使用:

remote = pexpect.spawn('xxx')
index = remote.expect(['good','bad',pexpect.EOF,pexpect.TIMEOUT])
if index  == 0:
    fun1()
elif index ==1:
    fun2()
elif index ==2:
    fun3()
elif index==3:
    fun4()

pexpect返回远程主机字符串包含的控制字符\x1b[?2004h在print时不显示,但是将字符串放进expectation数组中时有显示:
        debug_log('device prompt: %s<<<END\n'%self.def_prompt, location = sys._getframe().f_code.co_name,lineno = sys
._getframe().f_lineno, debug=debug)
         prompts = [prompt, self.def_prompt, pexpect.EOF, pexpect.TIMEOUT,'--More--'] if prompt else [self.def_prompt, pexpect.EOF, pexpect.TIMEOUT,'--More--']
        debug_log('command: %s, \nprompt: %s<<<END\n'%(command, prompts),location=sys._getframe().f_code.co_name,lineno = sys._getframe
().f_lineno, debug=debug)

运行后输出调试信息如下,可以看到print('%s')输出self.def_prompt为:root@localhost:~# <<<END,但是数组输出结果为'\x1b[?2004hroot@localhost:~# '
debug line: 1357 inside function: execute_command_on_device():
device prompt: root@localhost:~# <<<END

debug line: 1360 inside function: execute_command_on_device():
command: ls /root, 
prompt: ['\x1b[?2004hroot@localhost:~# ', <class 'pexpect.exceptions.EOF'>, <class 'pexpect.exceptions.TIMEOUT'>, '--More--']<<<END

用规则表达式胡re.sub函数删除控制字即可:

self.def_prompt = re.sub('\\x1b\[\?2004h', '', self.def_prompt)

class PMDeivce(object):
    def __init__(self, ip, username, password, logfile='', timeout = 200):
        
        self.ip = ip
        self.username = username
        self.password = password
        self.shell = None
        self.logfile = ''
        #print("new to device: %s OK\n"%self.ip) 
        self.def_prompt = ''
        self.logfile = ''
        if not self.logfile:
            t=datetime.now().strftime("%Y-%m-%d-%H%M%S")
            tmp_dir = os.environ['TEMP'] if os.name == 'nt' else os.environ['TMP']
            self.logfile= os.path.join(tmp_dir, 'sshclient_' + re.sub('\.','_', self.ip) + '_' + t + '.log')
            
            print("logfile:%s\n"%self.logfile)
            
        paramiko.util.log_to_file(self.logfile)
        
        self.timeout = timeout
        self.wait_factor = 0.5

    def connect_to_device(self, connection='SSH', debug=False):
        '''
      connect to targtet host with username and password
      '''
        if not self.shell:
            try:
                #print(f"login to {self.ip} with username: {self.username}, password: {self.password}: \n")
                self.ssh = paramiko.SSHClient() 
                self.ssh.load_system_host_keys()
                self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
                self.ssh.connect(hostname=self.ip, port=22, username=self.username, password=self.password,timeout=60,allow_agent=False,look_for_keys=False)
                #break
                #self.sshclient  = self.ssh
                #self.ssh.get_pty()
                self.shell = self.ssh.invoke_shell()
                self.shell.setblocking(False)
                self.shell.settimeout(self.timeout)
                
                
                ticks = 0
                resp = ''
                while True: 
                    recv_flag = self.shell.recv_ready() 
                    if recv_flag:
                        resp = strip_ansi_codes(self.shell.recv(1024).decode('utf-8')).lstrip();
                        break
                    else:
                        time.sleep(self.wait_factor)
                        ticks += self.wait_factor
                        if ticks > self.timeout:
                            return None
                
                time.sleep(1)                
                resp = strip_ansi_codes(self.shell.recv(1024).decode('utf-8')).lstrip() if not resp else resp
                debug_log("connected to shell of device: %s OK, server respond: \n%s<<<<<END\n"%(self.ip,resp), location=sys._getframe().f_code.co_name,debug=debug)

                #set default prompt
                self.def_prompt = resp
                
                #self.execute_command_on_device(command='date',debug=True)
                #self._clear(debug=debug)     
                
                return self.shell
            except Exception as e:
                print ('Failed to connect to shell {} with username:{}, password: {}:\n{}<<<END'.format(self.ip,self.username,self.password, e))
                self.ssh.close()
                return None                
                
    def _clear(self,debug=False):
        try:
            self.shell.send('\n')
            self.read_device(debug=True)
        except (PipeTimeout, socket.timeout):
            pass

    def read_device(self, prompt=None, alt_prompt='--More--', timeout=90, debug=False, verbose=False, wait_factor = None, strip_prompt=True, strip_command=True):
        '''
      '''

        wait_factor = wait_factor if wait_factor else self.wait_factor       
        results = ''
        ticks = 0
        #print("inside read_device ():got prompt value: %s\n"%prompt)
        prompt = self.def_prompt if not prompt else prompt
        #debug_log("inside execute_command_on_device(): got prompt: %s,alt_prompt: %s\n"%(prompt,alt_prompt),location=sys._getframe().f_code.co_name,debug=debug)
         
        while True: 
           recv_flag = self.shell.recv_ready() 
                        
           if recv_flag:            
                #debug_log("paramiko shell receive flag is ready:\n",location=sys._getframe().f_code.co_name,debug=debug)
                #ret = strip_ansi_codes(self.shell.recv(65535).decode('utf-8')).lstrip()
                ret = strip_ansi_codes(self.shell.recv(65535).decode('utf-8'))
                debug_log("read from device: \n%s<<<<<END\n"%(ret), location=sys._getframe().f_code.co_name,debug=debug)                
                #results += ret
                                
                #if re.search(alt_prompt, ret):
                if ret.endswith(alt_prompt):
                    ret = re.sub(alt_prompt,'', ret)                   
                    #results += ret.rstrip()
                    results += ret
                    debug_log("shell got alt_prompt: {},response:\n{}<<<<END\ncontinue...".format(alt_prompt,ret), location=sys._getframe().f_code.co_name, debug=debug)
                    
                    self.shell.send(' ')                    
                    continue
                    
                #elif re.search(prompt, ret):
                elif ret.endswith(prompt):
                    results += ret
                    debug_log("shell got prompt:{}, response:\n{}<<<<END\nbreak!".format(prompt, ret), location=sys._getframe().f_code.co_name, debug=debug)
                    break
                #else:
                #    raise Exception("ERROR: paramiko recv() failed to find any prompt: %s"%results)
                            
           #wait                
           time.sleep(wait_factor)
           ticks += wait_factor
           debug_log("paramiko shell receive flag is NOT ready:\nWAITING...{}s".format(ticks), location=sys._getframe().f_code.co_name, debug=debug)
           if ticks >= timeout:
               break 
        #debug_log("paramiko shell receive response:\n{}<<<<END".format(results), location=sys._getframe().f_code.co_name, debug=debug) 
        return results  

    def login_to_cli(self, username='root', password='passwd', prompt='# ', debug=False,verbose=False, timeout=160):
        debug_log("login to CLI as username: {},password: {}, prompt: {}<<<<\n".format(username, password, prompt),debug=debug)
        self.shell.send('login {}\n'.format(username)) 
        #self.child.sendline(command + '\n')
        self.read_device(prompt='assword: ', timeout=timeout, debug=debug, verbose=verbose)

        self.shell.send(password+'\n')
        ret = self.read_device(prompt='# ', timeout=timeout, debug=debug, verbose=verbose).lstrip()
        debug_log("Done login to CLI as username: {},password: {}, prompt: {}<<<<\n".format(username, password, ret),debug=debug)
        
        #set default prompt
        self.def_prompt = ret
        
        return ret
     
    
    #end of: def login_to_cli(self, username='root', password='passwd', prompt='# ', debug=False, timeout=60):

    
    def execute_command_on_device(self, command, prompt=None, alt_prompt='--More--', delay=None, timeout=160, wait_factor = None, debug=False, verbose=False, strip_prompt=True, strip_command=True):
        '''
      '''
        wait_factor = wait_factor if wait_factor else self.wait_factor
        #print("inside execute_command_on_device(): got default prompt value: %s\n"%self.def_prompt)
        prompt = self.def_prompt if not prompt else prompt        
        debug_log("inside execute_command_on_device(): got prompt value: %s,alt_prompt: %s, cmd: %s\n"%(prompt,alt_prompt,command),location=sys._getframe().f_code.co_name,debug=debug)
        
        self.shell.send(command+'\n')
        
        if delay:
            time.sleep(int(delay))        
            #debug_log("send command: %s and delay to read: %s OK\n"%(command, delay), location=sys._getframe().f_code.co_name,debug=debug)
        
        results = self.read_device(debug=debug, verbose=verbose, alt_prompt='--More--', timeout=timeout)        
        
        if strip_prompt:
            return  re.sub(prompt,'', results)       # 鏈€鍚庝竴琛屾槸linux鐨凷P1杈撳叆鎻愮ず绗︼紝娌$敤涓㈠純      
        else:
            return results  
        #execute_command_on_device()

    def execute_cmd(self, cmd): #澶氭潯鍛戒护涔嬮棿浠ラ€楀彿闅斿紑
        stdin, stdout, stderr = self.ssh.exec_command(cmd)
        res, err = stdout.read(), stderr.read()
        result = res if res else err
        return result.decode()

    def ssh_close(self):
        self.shell.close()
        self.ssh.close()

    def sftp_connect(self):
        '''
         sftp 
        '''
        try:
            self.t = paramiko.Transport((self.ip, 22))
            self.t.connect(username=self.username, password=self.password)
            self.sftp = paramiko.SFTPClient.from_transport(self.t)
        except Exception as e:
            print (self.ip, e)

    def sftp_close(self):
        self.close()

    def get_localfile(self, local_dir):
        allfiles = list()
        for filepath, dirs, files in os.walk(local_dir, topdown=True):
            for file in files:
                filename = os.path.join(filepath, file)
                allfiles.append(filename)
        return allfiles

    def sfpt_putfile(self, local_dir, remote_dir):
        try:
            if remote_dir[-1] != '/':
                remote_dir = remote_dir + "/"
            all_files = self.get_localfile(local_dir)
            for file in all_files:
                file1 = os.path.basename(file)
                remote_filename = os.path.join(remote_dir, file1)
                print (remote_filename)
                try:
                    self.sftp.stat(remote_path)
                except:
                    print ("杩滅▼鏈嶅姟鍣ㄦ棤鏂囦欢澶癸紝璇峰厛鍒涘缓...")
                self.sftp.put(file, remote_filename)
        except:
            print(traceback.format_exc())

    def __del__(self):
        self.shell.close()
        self.ssh.close()

class Device():
    def __init__(self, ip, username, password, connection='SSH', timeout=180):
        '''
        connect: SSH|TELNET
        '''
        self.ip = ip
        self.username = username
        self.password = password
        self.timeout = timeout if timeout else 120
        self.logfile = ''

    def connect_to_device(self, connection='SSH', debug = False):
        '''
        connection: SSH|TELNET

        '''
        print("Connecting to: {} with username: {}, password: {} via {}".format(self.ip, self.username, self.password, connection))

        self.connection = connection

        result_str = ""
        if self.connection == 'SSH':
            os.system('rm -rf  /root/.ssh/known_hosts')
            login_str = 'ssh %s@%s' % (self.username,self.ip)
        elif self.connection == 'TELNET':
            login_str = 'telnet %s' % (self.ip)

        #connect to device
        child = pexpect.spawn(login_str, timeout=self.timeout)
        if debug:
            t=datetime.now().strftime("%Y-%m-%d-%H%M%S")
            tmp_dir = os.environ['TEMP'] if os.name == 'nt' else os.environ['TMP']
            self.logfile= os.path.join(tmp_dir, 'Exp_' + re.sub('\.','_', self.ip) + '_' + t + '.log')
            child.logfile =  open(self.logfile,'w')
            print("logfile:%s\n"%self.logfile)

        try:
            i = child.expect(['[Pp]assword:', 'continue connecting (yes/no)?','login:'],timeout = 600)
            if i == 0 :
                child.sendline(self.password)
            elif i == 1:
                child.sendline('yes')  
                child.expect('[Pp]assword: ')  
                child.sendline(self.password)
            elif i == 2 :
                child.sendline(self.username)
                child.expect('[Pp]assword: ')
                child.sendline(self.password)
            child.setwinsize(24,500)
            i= child.expect(['[$#>]\\s+', pexpect.EOF, pexpect.TIMEOUT])

            if i == 0:
                #print("got default prompt id: %d, %s<<<END\n"%(i, child.before +  child.after))
                self.def_prompt = re.sub(r'\x1b\[\?\d\d\d\dh','',child.before +  child.after).lstrip()

            else:
                print("not got prompt, id: %d, %s<<<END\n"%(i, child.before +  child.after))
                child = None
                
        except:
            print("login device: {} failed with: {}/{}, due to TIMEOUT or EOF".format(self.ip, self.username,self.password))
            print("{}, {}\n".format(child.before, child.after))
            child = None
        finally:
            self.child = child
        return child

    def escape_ansi(self, line):
        #?2004hroot@localhost:~# 
        ansi_escape = re.compile(r'\x1b\[\?\d\d\d\d\dh')
        return ansi_escape.sub('', line)
    
    def set_default_prompt(self, prompt):
        self.def_prompt = prompt
        return self.def_prompt
         
    def login_to_cli(self, username='root', password='passwd', prompt='# ', debug=False, timeout=60):

        debug_log("login to CLI as username: {},password: {}, prompt: {}<<<<\n".format(username, password, prompt),debug=debug)
        self.child.sendline('login {}'.format(username)) 
        #self.child.sendline(command + '\n')
        self.child.expect('assword: ', timeout=timeout)

        self.child.sendline(password)
        j = self.child.expect(['# ', pexpect.EOF, pexpect.TIMEOUT], timeout=timeout)
        if j == 0:
             debug_log("login and get cli prompt: %s<<<<END"%(self.child.before + self.child.after).lstrip(),debug=debug)
             self.def_prompt = (self.child.before + self.child.after).lstrip()
             return True
        else:
             debug_log("failed to get cli prompt: %s<<<<END"%(self.child.before + self.child.after).lstrip(),debug=debug)
             return False
 

 
    def execute_command_on_device(self, command, prompt='', timeout=180, debug=False, verbose=False):
        '''
        execute_command_on_device: execute command on device

        '''

        if prompt:
            prompt = [prompt, pexpect.EOF, pexpect.TIMEOUT,'--More--'] 
        else:
            prompt = [self.def_prompt, pexpect.EOF, pexpect.TIMEOUT,'--More--']

        if debug:
            print('execute_command_on_device: command: {}, prompt:{}<<<END\n'.format(command, prompt[0]))


        if self.child:
            t = 0
            flag = 0

            self.child.sendline(command) 
            #self.child.sendline(command + '\n')
            j = self.child.expect(prompt, timeout=timeout)

            #result_str =  self.child.before
            result_str =  ''
            if debug and verbose:
                print("execute_command_on_device() command: {},id: {}\n{}<<<<\n".format(command, j, result_str))

            while True:
                if t == timeout:
                    flag = 1
            
                if j == 0:
                    result_str += self.child.before + self.child.after
                    break
                elif j == 0 and flag == 1:
                    print("There is no result in seconds " + str(t))
                    result_str += self.child.before + self.child.after
                    #print("command: {}, response:\n{}<<<<\n".format(command, result_str))
                    break
                elif j == 3:

                    #result_str += self.child.before + self.child.after
                    result_str += self.child.before 
                    t += 1
                    time.sleep(1)
                    self.child.send(' ') 
                    j = self.child.expect(prompt, timeout=timeout)
                    continue
                elif j == 1:
                    result_str = 'command running error:session closed!!!'
                    break
                elif j == 2:
                    result_str = 'command running error:timeout!!!'
                    break

        return result_str
    def __del__(self):
        pass
    
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值