同时封装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