diff --git a/connect.py b/connect.py index 66268d50c..945dcf30c 100644 --- a/connect.py +++ b/connect.py @@ -8,18 +8,33 @@ sys.setdefaultencoding('utf8') import os import re import time +import datetime import textwrap import getpass import readline import django from multiprocessing import Pool +import paramiko +import struct, fcntl, signal, socket, select, fnmatch os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' if django.get_version() != '1.6': django.setup() -from jumpserver.api import ServerError, User, Asset, Jtty, get_object -from jumpserver.api import logger +from jumpserver.api import ServerError, User, Asset, AssetGroup, get_object +from jumpserver.api import logger, is_dir, Log, TtyLog +from jumpserver.settings import log_dir +try: + import termios + import tty +except ImportError: + print '\033[1;31m仅支持类Unix系统 Only unix like supported.\033[0m' + time.sleep(3) + sys.exit() + +VIM_FLAG = False +VIM_COMMAND = '' +SSH_TTY = '' login_user = get_object(User, username=getpass.getuser()) @@ -81,6 +96,358 @@ def verify_connect(user, option): jtty.connect() +def check_vim_status(command, ssh): + global SSH_TTY + print command + if command == '': + return True + else: + command_str= 'ps -ef |grep "%s" | grep "%s"|grep -v grep |wc -l' % (command,SSH_TTY) + print command_str + stdin, stdout, stderr = ssh.exec_command(command_str) + ps_num = stdout.read() + print ps_num + if int(ps_num) == 0: + return True + else: + return False + + +def deal_command(str_r, ssh): + + """ + 处理命令中特殊字符 + """ + str_r = re.sub('\x07','',str_r) #删除响铃 + patch_char = re.compile('\x08\x1b\[C') #删除方向左右一起的按键 + while patch_char.search(str_r): + str_r = patch_char.sub('', str_r.rstrip()) + + result_command = '' #最后的结果 + pattern_str = '' #模式中间中的字符串 + backspace_num = 0 #光标移动的个数 + reach_backspace_flag = False #没有检测到光标键则为true + end_flag = False + while str_r: + tmp = re.match(r'\w', str_r) + if tmp: + if reach_backspace_flag: + pattern_str += str(tmp.group(0)) + str_r = str_r[1:] + continue + else: + result_command += str(tmp.group(0)) + str_r = str_r[1:] + continue + + tmp = re.match(r'\x1b\[K[\x08]*', str_r) + if tmp: + if backspace_num > 0: + if backspace_num > len(result_command) : + result_command += pattern_str + result_command = result_command[0:-backspace_num] + else: + result_command = result_command[0:-backspace_num] + result_command += pattern_str + del_len = len(str(tmp.group(0)))-3 + if del_len > 0: + result_command = result_command[0:-del_len] + reach_backspace_flag = False + backspace_num =0 + pattern_str='' + str_r = str_r[len(str(tmp.group(0))):] + continue + if re.match(r'\x08', str_r): + backspace_num += 1 + reach_backspace_flag = True + str_r = str_r[1:] + if len(str_r) == 0: + end_flag = True + continue + if reach_backspace_flag : + pattern_str += str_r[0] + else : + result_command += str_r[0] + str_r = str_r[1:] + + if backspace_num > 0 and not end_flag: + result_command = result_command[:-backspace_num] + result_command += pattern_str + + + + control_char = re.compile(r""" + \x1b[ #%()*+\-.\/]. | + \r | #匹配 回车符(CR) + (?:\x1b\[|\x9b) [ -?]* [@-~] | #匹配 控制顺序描述符(CSI)... Cmd + (?:\x1b\]|\x9d) .*? (?:\x1b\\|[\a\x9c]) | \x07 | #匹配 操作系统指令(OSC)...终止符或振铃符(ST|BEL) + (?:\x1b[P^_]|[\x90\x9e\x9f]) .*? (?:\x1b\\|\x9c) | #匹配 设备控制串或私讯或应用程序命令(DCS|PM|APC)...终止符(ST) + \x1b. #匹配 转义过后的字符 + [\x80-\x9f] #匹配 所有控制字符 + """, re.X) + result_command = control_char.sub('', result_command.strip()) + global VIM_FLAG + global VIM_COMMAND + if not VIM_FLAG: + if result_command.startswith('vim') or result_command.startswith('vi') : + VIM_FLAG = True + VIM_COMMAND = result_command + return result_command.decode('utf8',"ignore") + else: + if check_vim_status(VIM_COMMAND, ssh): + VIM_FLAG = False + VIM_COMMAND='' + return result_command.decode('utf8',"ignore") + else: + return '' + +def remove_control_char(str_r): + """ + 处理日志特殊字符 + """ + control_char = re.compile(r""" + \x1b[ #%()*+\-.\/]. | + \r | #匹配 回车符(CR) + (?:\x1b\[|\x9b) [ -?]* [@-~] | #匹配 控制顺序描述符(CSI)... Cmd + (?:\x1b\]|\x9d) .*? (?:\x1b\\|[\a\x9c]) | \x07 | #匹配 操作系统指令(OSC)...终止符或振铃符(ST|BEL) + (?:\x1b[P^_]|[\x90\x9e\x9f]) .*? (?:\x1b\\|\x9c) | #匹配 设备控制串或私讯或应用程序命令(DCS|PM|APC)...终止符(ST) + \x1b. #匹配 转义过后的字符 + [\x80-\x9f] #匹配 所有控制字符 + """, re.X) + backspace = re.compile(r"[^\b][\b]") + line_filtered = control_char.sub('', str_r.rstrip()) + while backspace.search(line_filtered): + line_filtered = backspace.sub('', line_filtered) + + return line_filtered + + +def newline_code_in(strings): + for i in ['\r', '\r\n', '\n']: + if i in strings: + #print "new line" + return True + return False + + +class Jtty(object): + """ + A virtual tty class + 一个虚拟终端类,实现连接ssh和记录日志 + """ + def __init__(self, username, ip): + self.chan = None + self.username = username + self.ip = ip + # self.user = user + # self.asset = asset + + @staticmethod + def get_win_size(): + """ + This function use to get the size of the windows! + 获得terminal窗口大小 + """ + if 'TIOCGWINSZ' in dir(termios): + TIOCGWINSZ = termios.TIOCGWINSZ + else: + TIOCGWINSZ = 1074295912L + s = struct.pack('HHHH', 0, 0, 0, 0) + x = fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ, s) + return struct.unpack('HHHH', x)[0:2] + + def set_win_size(self, sig, data): + """ + This function use to set the window size of the terminal! + 设置terminal窗口大小 + """ + try: + win_size = self.get_win_size() + self.chan.resize_pty(height=win_size[0], width=win_size[1]) + except Exception: + pass + + def log_record(self): + """ + Logging user command and output. + 记录用户的日志 + """ + tty_log_dir = os.path.join(log_dir, 'tty') + timestamp_start = int(time.time()) + date_start = time.strftime('%Y%m%d', time.localtime(timestamp_start)) + time_start = time.strftime('%H%M%S', time.localtime(timestamp_start)) + today_connect_log_dir = os.path.join(tty_log_dir, date_start) + log_file_path = os.path.join(today_connect_log_dir, '%s_%s_%s' % (self.username, self.ip, time_start)) + pid = os.getpid() + pts = os.popen("ps axu | grep %s | grep -v grep | awk '{ print $7 }'" % pid).read().strip() + ip_list = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n') + + try: + is_dir(today_connect_log_dir) + except OSError: + raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir)) + + try: + # log_file_f = open('/opt/jumpserver/logs/tty/20151102/a_b_191034.log', 'a') + log_file_f = open(log_file_path + '.log', 'a') + log_time_f = open(log_file_path + '.time', 'a') + except IOError: + raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) + + log = Log(user=self.username, host=self.ip, remote_ip=ip_list, + log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid) + log_file_f.write('Start time is %s\n' % datetime.datetime.now()) + log.save() + return log_file_f, log_time_f, ip_list, log + + def posix_shell(self,ssh): + """ + Use paramiko channel connect server interactive. + 使用paramiko模块的channel,连接后端,进入交互式 + """ + log_file_f, log_time_f, ip_list, log = self.log_record() + old_tty = termios.tcgetattr(sys.stdin) + pre_timestamp = time.time() + input_r = '' + input_mode = False + + try: + tty.setraw(sys.stdin.fileno()) + tty.setcbreak(sys.stdin.fileno()) + self.chan.settimeout(0.0) + + while True: + try: + r, w, e = select.select([self.chan, sys.stdin], [], []) + except Exception: + pass + + if self.chan in r: + try: + x = self.chan.recv(1024) + if len(x) == 0: + break + sys.stdout.write(x) + sys.stdout.flush() + log_file_f.write(x) + now_timestamp = time.time() + log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(x))) + pre_timestamp = now_timestamp + log_file_f.flush() + log_time_f.flush() + + if input_mode and not newline_code_in(x): + input_r += x + + except socket.timeout: + pass + + if sys.stdin in r: + x = os.read(sys.stdin.fileno(), 1) + if not input_mode: + input_mode = True + + if str(x) in ['\r', '\n', '\r\n']: + input_r = deal_command(input_r,ssh) + TtyLog(log=log, datetime=datetime.datetime.now(), cmd=input_r).save() + input_r = '' + input_mode = False + + if len(x) == 0: + break + self.chan.send(x) + + finally: + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) + log_file_f.write('End time is %s' % datetime.datetime.now()) + log_file_f.close() + log.is_finished = True + log.end_time = datetime.datetime.now() + log.save() + + def get_connect_item(self): + """ + get args for connect: ip, port, username, passwd + 获取连接需要的参数,也就是服务ip, 端口, 用户账号和密码 + """ + # if not self.asset.is_active: + # raise ServerError('该主机被禁用 Host %s is not active.' % self.ip) + # + # if not self.user.is_active: + # raise ServerError('该用户被禁用 User %s is not active.' % self.username) + + # password = CRYPTOR.decrypt(self.]) + # return self.username, password, self.ip, int(self.asset.port) + return 'root', 'redhat', '127.0.0.1', 22 + + def get_connection(self): + """ + Get the ssh connection for reuse + 获取连接套接字 + """ + username, password, ip, port = self.get_connect_item() + logger.debug("username: %s, password: %s, ip: %s, port: %s" % (username, password, ip, port)) + + # 发起ssh连接请求 Make a ssh connection + ssh = paramiko.SSHClient() + ssh.load_system_host_keys() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + ssh.connect(ip, port=port, username=username, password=password) + except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException: + raise ServerError('认证错误 Authentication Error.') + except socket.error: + raise ServerError('端口可能不对 Connect SSH Socket Port Error, Please Correct it.') + else: + return ssh + + def connect(self): + """ + Connect server. + 连接服务器 + """ + ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip + login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.ip + + # 发起ssh连接请求 Make a ssh connection + ssh = self.get_connection() + + # 获取连接的隧道并设置窗口大小 Make a channel and set windows size + global channel + win_size = self.get_win_size() + self.chan = channel = ssh.invoke_shell(height=win_size[0], width=win_size[1]) + try: + signal.signal(signal.SIGWINCH, self.set_win_size) + except: + pass + + # 设置PS1并提示 Set PS1 and msg it + #channel.send(ps1) + #channel.send(login_msg) + channel.send('echo ${SSH_TTY}\n') + global SSH_TTY + while not channel.recv_ready(): + time.sleep(1) + tmp = channel.recv(1024) + #print 'ok'+tmp+'ok' + SSH_TTY = re.search(r'(?<=/dev/).*', tmp).group().strip() + + channel.send('clear\n') + # Make ssh interactive tunnel + self.posix_shell(ssh) + + # Shutdown channel socket + channel.close() + ssh.close() + + def execute(self, cmd): + """ + execute cmd on the asset + 执行命令 + """ + pass + + def print_prompt(): """ Print prompt diff --git a/jumpserver/api.py b/jumpserver/api.py index 0184135f2..8f0073c15 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -8,8 +8,6 @@ import hashlib import datetime import random import subprocess -import paramiko -import struct, fcntl, signal, socket, select, fnmatch from settings import * from django.core.paginator import Paginator, EmptyPage, InvalidPage @@ -25,17 +23,7 @@ from django.shortcuts import render_to_response from django.core.mail import send_mail import json import logging -VIM_FLAG = False -VIM_COMMAND = '' -SSH_TTY = '' -try: - import termios - import tty -except ImportError: - print '\033[1;31m仅支持类Unix系统 Only unix like supported.\033[0m' - time.sleep(3) - sys.exit() def set_log(level): @@ -97,356 +85,6 @@ def pages(post_objects, request): # 所有对象, 分页器, 本页对象, 所有页码, 本页页码,是否显示第一页,是否显示最后一页 return post_objects, paginator, page_objects, page_range, current_page, show_first, show_end -def check_vim_status(command, ssh): - global SSH_TTY - print command - if command == '': - return True - else: - command_str= 'ps -ef |grep "%s" | grep "%s"|grep -v grep |wc -l' % (command,SSH_TTY) - print command_str - stdin, stdout, stderr = ssh.exec_command(command_str) - ps_num = stdout.read() - print ps_num - if int(ps_num) == 0: - return True - else: - return False - - -def deal_command(str_r, ssh): - - """ - 处理命令中特殊字符 - """ - str_r = re.sub('\x07','',str_r) #删除响铃 - patch_char = re.compile('\x08\x1b\[C') #删除方向左右一起的按键 - while patch_char.search(str_r): - str_r = patch_char.sub('', str_r.rstrip()) - - result_command = '' #最后的结果 - pattern_str = '' #模式中间中的字符串 - backspace_num = 0 #光标移动的个数 - reach_backspace_flag = False #没有检测到光标键则为true - end_flag = False - while str_r: - tmp = re.match(r'\w', str_r) - if tmp: - if reach_backspace_flag: - pattern_str += str(tmp.group(0)) - str_r = str_r[1:] - continue - else: - result_command += str(tmp.group(0)) - str_r = str_r[1:] - continue - - tmp = re.match(r'\x1b\[K[\x08]*', str_r) - if tmp: - if backspace_num > 0: - if backspace_num > len(result_command) : - result_command += pattern_str - result_command = result_command[0:-backspace_num] - else: - result_command = result_command[0:-backspace_num] - result_command += pattern_str - del_len = len(str(tmp.group(0)))-3 - if del_len > 0: - result_command = result_command[0:-del_len] - reach_backspace_flag = False - backspace_num =0 - pattern_str='' - str_r = str_r[len(str(tmp.group(0))):] - continue - if re.match(r'\x08', str_r): - backspace_num += 1 - reach_backspace_flag = True - str_r = str_r[1:] - if len(str_r) == 0: - end_flag = True - continue - if reach_backspace_flag : - pattern_str += str_r[0] - else : - result_command += str_r[0] - str_r = str_r[1:] - - if backspace_num > 0 and not end_flag: - result_command = result_command[:-backspace_num] - result_command += pattern_str - - - - control_char = re.compile(r""" - \x1b[ #%()*+\-.\/]. | - \r | #匹配 回车符(CR) - (?:\x1b\[|\x9b) [ -?]* [@-~] | #匹配 控制顺序描述符(CSI)... Cmd - (?:\x1b\]|\x9d) .*? (?:\x1b\\|[\a\x9c]) | \x07 | #匹配 操作系统指令(OSC)...终止符或振铃符(ST|BEL) - (?:\x1b[P^_]|[\x90\x9e\x9f]) .*? (?:\x1b\\|\x9c) | #匹配 设备控制串或私讯或应用程序命令(DCS|PM|APC)...终止符(ST) - \x1b. #匹配 转义过后的字符 - [\x80-\x9f] #匹配 所有控制字符 - """, re.X) - result_command = control_char.sub('', result_command.strip()) - global VIM_FLAG - global VIM_COMMAND - if not VIM_FLAG: - if result_command.startswith('vim') or result_command.startswith('vi') : - VIM_FLAG = True - VIM_COMMAND = result_command - return result_command.decode('utf8',"ignore") - else: - if check_vim_status(VIM_COMMAND, ssh): - VIM_FLAG = False - VIM_COMMAND='' - return result_command.decode('utf8',"ignore") - else: - return '' - -def remove_control_char(str_r): - """ - 处理日志特殊字符 - """ - control_char = re.compile(r""" - \x1b[ #%()*+\-.\/]. | - \r | #匹配 回车符(CR) - (?:\x1b\[|\x9b) [ -?]* [@-~] | #匹配 控制顺序描述符(CSI)... Cmd - (?:\x1b\]|\x9d) .*? (?:\x1b\\|[\a\x9c]) | \x07 | #匹配 操作系统指令(OSC)...终止符或振铃符(ST|BEL) - (?:\x1b[P^_]|[\x90\x9e\x9f]) .*? (?:\x1b\\|\x9c) | #匹配 设备控制串或私讯或应用程序命令(DCS|PM|APC)...终止符(ST) - \x1b. #匹配 转义过后的字符 - [\x80-\x9f] #匹配 所有控制字符 - """, re.X) - backspace = re.compile(r"[^\b][\b]") - line_filtered = control_char.sub('', str_r.rstrip()) - while backspace.search(line_filtered): - line_filtered = backspace.sub('', line_filtered) - - return line_filtered - - -def newline_code_in(strings): - for i in ['\r', '\r\n', '\n']: - if i in strings: - #print "new line" - return True - return False - - -class Jtty(object): - """ - A virtual tty class - 一个虚拟终端类,实现连接ssh和记录日志 - """ - def __init__(self, username, ip): - self.chan = None - self.username = username - self.ip = ip - # self.user = user - # self.asset = asset - - @staticmethod - def get_win_size(): - """ - This function use to get the size of the windows! - 获得terminal窗口大小 - """ - if 'TIOCGWINSZ' in dir(termios): - TIOCGWINSZ = termios.TIOCGWINSZ - else: - TIOCGWINSZ = 1074295912L - s = struct.pack('HHHH', 0, 0, 0, 0) - x = fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ, s) - return struct.unpack('HHHH', x)[0:2] - - def set_win_size(self, sig, data): - """ - This function use to set the window size of the terminal! - 设置terminal窗口大小 - """ - try: - win_size = self.get_win_size() - self.chan.resize_pty(height=win_size[0], width=win_size[1]) - except Exception: - pass - - def log_record(self): - """ - Logging user command and output. - 记录用户的日志 - """ - tty_log_dir = os.path.join(log_dir, 'tty') - timestamp_start = int(time.time()) - date_start = time.strftime('%Y%m%d', time.localtime(timestamp_start)) - time_start = time.strftime('%H%M%S', time.localtime(timestamp_start)) - today_connect_log_dir = os.path.join(tty_log_dir, date_start) - log_file_path = os.path.join(today_connect_log_dir, '%s_%s_%s' % (self.username, self.ip, time_start)) - pid = os.getpid() - pts = os.popen("ps axu | grep %s | grep -v grep | awk '{ print $7 }'" % pid).read().strip() - ip_list = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n') - - try: - is_dir(today_connect_log_dir) - except OSError: - raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir)) - - try: - # log_file_f = open('/opt/jumpserver/logs/tty/20151102/a_b_191034.log', 'a') - log_file_f = open(log_file_path + '.log', 'a') - log_time_f = open(log_file_path + '.time', 'a') - except IOError: - raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) - - log = Log(user=self.username, host=self.ip, remote_ip=ip_list, - log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid) - log_file_f.write('Start time is %s\n' % datetime.datetime.now()) - log.save() - return log_file_f, log_time_f, ip_list, log - - def posix_shell(self,ssh): - """ - Use paramiko channel connect server interactive. - 使用paramiko模块的channel,连接后端,进入交互式 - """ - log_file_f, log_time_f, ip_list, log = self.log_record() - old_tty = termios.tcgetattr(sys.stdin) - pre_timestamp = time.time() - input_r = '' - input_mode = False - - try: - tty.setraw(sys.stdin.fileno()) - tty.setcbreak(sys.stdin.fileno()) - self.chan.settimeout(0.0) - - while True: - try: - r, w, e = select.select([self.chan, sys.stdin], [], []) - except Exception: - pass - - if self.chan in r: - try: - x = self.chan.recv(1024) - if len(x) == 0: - break - sys.stdout.write(x) - sys.stdout.flush() - log_file_f.write(x) - now_timestamp = time.time() - log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(x))) - pre_timestamp = now_timestamp - log_file_f.flush() - log_time_f.flush() - - if input_mode and not newline_code_in(x): - input_r += x - - except socket.timeout: - pass - - if sys.stdin in r: - x = os.read(sys.stdin.fileno(), 1) - if not input_mode: - input_mode = True - - if str(x) in ['\r', '\n', '\r\n']: - input_r = deal_command(input_r,ssh) - TtyLog(log=log, datetime=datetime.datetime.now(), cmd=input_r).save() - input_r = '' - input_mode = False - - if len(x) == 0: - break - self.chan.send(x) - - finally: - termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) - log_file_f.write('End time is %s' % datetime.datetime.now()) - log_file_f.close() - log.is_finished = True - log.end_time = datetime.datetime.now() - log.save() - - def get_connect_item(self): - """ - get args for connect: ip, port, username, passwd - 获取连接需要的参数,也就是服务ip, 端口, 用户账号和密码 - """ - # if not self.asset.is_active: - # raise ServerError('该主机被禁用 Host %s is not active.' % self.ip) - # - # if not self.user.is_active: - # raise ServerError('该用户被禁用 User %s is not active.' % self.username) - - # password = CRYPTOR.decrypt(self.]) - # return self.username, password, self.ip, int(self.asset.port) - return 'root', 'redhat', '127.0.0.1', 22 - - def get_connection(self): - """ - Get the ssh connection for reuse - 获取连接套接字 - """ - username, password, ip, port = self.get_connect_item() - logger.debug("username: %s, password: %s, ip: %s, port: %s" % (username, password, ip, port)) - - # 发起ssh连接请求 Make a ssh connection - ssh = paramiko.SSHClient() - ssh.load_system_host_keys() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - try: - ssh.connect(ip, port=port, username=username, password=password) - except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException: - raise ServerError('认证错误 Authentication Error.') - except socket.error: - raise ServerError('端口可能不对 Connect SSH Socket Port Error, Please Correct it.') - else: - return ssh - - def connect(self): - """ - Connect server. - 连接服务器 - """ - ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip - login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.ip - - # 发起ssh连接请求 Make a ssh connection - ssh = self.get_connection() - - # 获取连接的隧道并设置窗口大小 Make a channel and set windows size - global channel - win_size = self.get_win_size() - self.chan = channel = ssh.invoke_shell(height=win_size[0], width=win_size[1]) - try: - signal.signal(signal.SIGWINCH, self.set_win_size) - except: - pass - - # 设置PS1并提示 Set PS1 and msg it - #channel.send(ps1) - #channel.send(login_msg) - channel.send('echo ${SSH_TTY}\n') - global SSH_TTY - while not channel.recv_ready(): - time.sleep(1) - tmp = channel.recv(1024) - #print 'ok'+tmp+'ok' - SSH_TTY = re.search(r'(?<=/dev/).*', tmp).group().strip() - - channel.send('clear\n') - # Make ssh interactive tunnel - self.posix_shell(ssh) - - # Shutdown channel socket - channel.close() - ssh.close() - - def execute(self, cmd): - """ - execute cmd on the asset - 执行命令 - """ - pass class PyCrypt(object):