def show_help(): print( """ 直接运行 {easy_deploy} 添加管理待操作的服务器 命令行参数: 按 {tab} 键自动显示所有的操作命令 Usage: {easy_deploy} [options] [command] [keyword] [keyword] [keyword].. {options}: -h, --help 显示帮助 push 本地服务器推送文件到远程 pull 拉取远程服务器文件到本地 run 运行命令 info 显示操作服务器列表 {command}: local[:remote] 搭配push操作, 如果remote远程不指定,则默认推送到远程的用户目录 remote[:local] 搭配pull操作, 如果local本地不指定,则默认拉取文件到当前目录 'shell command' 搭配run操作, 运行的命令必须用英文单引号或双引号包括 {keyword}: 作用于包含指定关键字的服务器,不指定keyword则表示作用于所有服务器; keyword输入可以不完整, 只要匹配到包含的服务器keyword即可生效作用; 可以指定多个keyword(空格隔开), 第三个及之后的传参都认为keyword; pull操作必须指定关键字, 且匹配到的服务器只能一个 """.format(options=color_str(Color.YELLOW, 'Options'),command=color_str(Color.YELLOW, 'Command'), keyword=color_str(Color.YELLOW, 'Keyword'), easy_deploy=color_str(Color.CYAN, 'easy_deploy'), tab=color_str(Color.CYAN, 'Tab')))
def run_command(self, command, select_server=None): """ 运行命令 """ select_server = select_server if select_server else self.server_list if 'tar ' in command: thread_list = [] print("\n多线程运行命令: %s" % color_str(Color.CYAN, command)) for server in select_server: if not server.is_ok: print("{} 无法连接, 直接跳过".format( color_str(Color.RED, server.ip))) continue thread = threading.Thread(target=self._thread_run, args=(server, command)) thread_list.append(thread) thread.start() for thread in thread_list: thread.join() else: for server in select_server: print("\n{color_ip} ==> 运行结果:".format( color_ip=color_str(Color.FUCHSIA, server.ip))) Connection(server.ip, server.user, server.port).run(command)
def transfer_file(self, transfer_file, transfer_location=None): transfer_file = transfer_file.strip() # Remove redundant spaces if os.path.isdir(transfer_file): print "{}: '{}'".format(color_str('[Sending directory]', color='y'), transfer_file) else: print "{}: '{}' of size: {} bytes".format(color_str('[Sending file]', color='y'), transfer_file, os.path.getsize(transfer_file)) # When transfer location is None, use home directory. if transfer_location is None: transfer_location = self.get_user_dir() else: # Ensure transfer directory exists self.execute_cmd("mkdir -p {}".format(transfer_location)) # Create archive to be send. temp_name = create_archive(transfer_file) sftp = self.client.open_sftp() sftp.put(temp_name, self.get_user_dir() + os.sep + temp_name) os.remove(temp_name) self.unarchive_on_server(temp_name, transfer_location) sftp.close()
def link(self, ip, port, tls): if tls == "tls": return color_str(Color.RED, "HTTPS的Socks5不支持tg的分享连接. 请自行配合设置BifrostV等软件使用") else: return color_str( Color.GREEN, "tg://socks?server={0}&port={1}&user={2}&pass={3}".format( ip, port, self.user_info, self.password))
def __str__(self): format_date = time.strftime("%Y-%m-%d %H:%M", time.localtime(int(self.create_date))) color_status = color_str(Color.GREEN if self.is_ok else Color.RED, self.is_ok) return "{info}, keyword:{color_keyword}, status: {color_status}, createDate:{format_date}\n".format( self=self, color_keyword=color_str(Color.CYAN, self.keyword), info=self._get_server_command(True), color_status=color_status, format_date=format_date)
def copy_file(self, local_path, remote_path=None, select_server=None, reverse=False): """ scp方式复制文件,reverse=True表示从远程复制到本地 """ select_server = select_server if select_server else self.server_list pool = Pool(cpu_count() + 1) for server in select_server: if not server.is_ok: print("{} 无法连接, 直接跳过".format(color_str(Color.RED, server.ip))) continue # 如果remote_path为空则自动获取 if not remote_path: if reverse: remote_path = '.' else: result = Connection(server.ip, server.user, server.port).run('echo $HOME', hide=True) remote_path = result.stdout else: # 传输文件前先确保目录远程存在, 自动创建目录 if remote_path.count('/') > 1: if reverse: os.system('mkdir -p {}'.format( remote_path[:remote_path.rfind('/')])) else: Connection(server.ip, server.user, server.port).run('mkdir -p {}'.format( remote_path[:remote_path.rfind('/')]), hide=True) if reverse: scp_command = 'scp -P {server.port} -r {server.user}@{server.ip}:{local} {remote}'.format( server=server, local=local_path, remote=remote_path) else: scp_command = 'scp -P {server.port} -r {local} {server.user}@{server.ip}:{remote}'.format( server=server, local=local_path, remote=remote_path) if server.is_ok: print("\n多进程模式传输文件: %s" % color_str(Color.CYAN, local_path)) pool.apply_async(self._process_copy, args=(server, scp_command, local_path, remote_path, reverse)) pool.close() #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束 pool.join()
def print_stats(self): print(''' downlink: {0} uplink: {1} total: {2} '''.format( color_str(Color.CYAN, bytes_2_human_readable(self.downlink_value, 2)), color_str(Color.CYAN, bytes_2_human_readable(self.uplink_value, 2)), color_str( Color.CYAN, bytes_2_human_readable(self.downlink_value + self.uplink_value, 2))))
def _process_copy(self, server, scp_command, local_path, remote_path, reverse): if reverse: print("\n{0} {color_ip}:{local} to {remote}".format( color_str(Color.YELLOW, 'Downloading...'), local=local_path, remote=remote_path, color_ip=color_str(Color.FUCHSIA, server.ip))) else: print("\n{0} {local} to {color_ip}:{remote}".format( color_str(Color.YELLOW, 'Uploading...'), local=local_path, remote=remote_path, color_ip=color_str(Color.FUCHSIA, server.ip))) os.system(scp_command)
def add_server(self, input_str): if not input_str: raise ValueError("无传参, 请重新输入") argv_list = input_str.split(" ") user, port = 'root', 22 ip = argv_list[0] if not check_ip(ip): print("{0} 不是ip".format(color_str(Color.RED, ip))) return try: opts = getopt(argv_list[1:], "u:p:") for cmd, arg in opts[0]: if cmd in ("-u", "--username"): user = arg elif cmd in ("-p", "--port"): port = arg except GetoptError: print( "输入错误, 输入格式为 ip [ -u ][ -p ]\nexample: 192.168.35.62 -u root -p 22" ) server = Server(ip, user=user, port=port) #测试能否连接 server.test_ssh() #无法连接则复制密钥 if not server.is_ok: generate_rsa() server.copy_ssh_id() self.modify_server_list(server=server)
def modify_server_list(self, server=None, clean=False, delete_index=None, orderBy=None, keyword_dict={}): if server: for exist_server in self.server_list: if exist_server.ip == server.ip and exist_server.is_ok: print("{0} 已存在".format(color_str(Color.FUCHSIA, server.ip))) return self._server_list.append(server) if keyword_dict: self._server_list[keyword_dict.get( 'index')].keyword = keyword_dict.get('keyword') if clean: self._server_list = list( filter(lambda server: server.is_ok, self.server_list)) if delete_index: del self._server_list[delete_index] if orderBy == 'asc': self._server_list = sorted(self.server_list, key=lambda server: server.create_date) elif orderBy == 'desc': self._server_list = sorted(self.server_list, key=lambda server: server.create_date, reverse=True)
def execute_cmd(self, command, sudo=False, print_output=True, return_streams=False, debug=True): if sudo: command = "sudo -S -p '' {}".format(command) if debug: print "{}: {}".format(color_str('[Executing]', color='y'), command) stdin, stdout, stderr = self.client.exec_command(command, get_pty=True) if sudo and self.password is not None: stdin.write(self.password + '\n') stdin.flush() # The password and a new line are echo'ed when a Sudo command is executed. stdout.readline() stdout.readline() if return_streams: return stdin, stdout, stderr elif not print_output: return stdout.readlines(), stderr.readlines() out = "" for line in stdout: if out == "": print "Output:" out += line print line, error = "" for line in stderr: if out == "": print "Errors:" error += line print line, return out, error
def __str__(self): tls = "开启" if self.tls == "tls" else "关闭" tfo = "TcpFastOpen: {}".format(self.tfo) if self.tfo != None else "" dyp = "DynamicPort: {}".format(self.dyp) if self.dyp.status else "" port_way = "-{}".format(self.end_port) if self.end_port else "" result = "" for node in self.node_list: temp = ''' {node.user_number}. Group: {self.tag} IP: {color_ip} Port: {self.port}{port_way} TLS: {tls} {node}{tfo} {dyp} '''.format(self=self, color_ip=color_str(Color.FUCHSIA, self.ip), node=node, tfo=tfo, dyp=dyp, tls=tls, port_way=port_way) result = "{0}{1}\n\n{2}\n\n".format( result, temp.strip(), node.link(self.ip, int(self.port), self.tls)) return result
def _insert_and_verify_cmd(parse_data, db_session, queries): if 'insert_data' in parse_data and parse_data['insert_data'] == True: insert_data(db_session, parse_data['data_to_insert']) query_results = [] verification_db = SQLiteDB() verification_db.drop_table() verification_db.setup() query_id = 0 hash_data = False if 'files' in parse_data['data_to_insert']: hash_data = True for query in queries: print "Querying: {}".format(query) query_res = db_session.query_db(query, hash_files=hash_data, time_out=300) query_res_count = 0 # Row result is expected to contain: # [[ID, file hashed by query_db function, file name], ...] for row in query_res['result']: verification_db.insert(query_id, query_res_count, row[-1], str(row[1])) query_res_count += 1 if query_res_count == 0: warning = color_str("WARNING: ", color='y') print warning, "Invalid query occurred, no results retrieved (may be intentional)." print warning, "QUERY {}".format(query) print warning, "RESULT {}".format(query_res) query_results.append(query_res) query_id += 1 print print_json({"results": query_results})
def __init__(self, action): super(GroupSelector, self).__init__(action) if "删除" in action and len(self.group_list) == 1: print(color_str(Color.RED, "仅剩最后一个端口无法删除!!!")) self.group = None elif len(self.group_list) > 1: self.select_group() else: self.group = self.group_list[0]
def select_group(self): print(self.profile) choice = input("请输入要{}的节点Group字母: ".format(self.action)).upper() if len(choice) != 1 or not choice.isalpha( ) or choice > self.group_list[-1].tag: print(color_str(Color.RED, '输入有误,请检查是否为字母且范围中')) self.group = None else: self.group = [x for x in self.group_list if x.tag == choice][0]
def select_group(self): print(self.profile) choice = input("请输入要{}的节点Group字母: ".format(self.action)) group_list = [x for x in self.group_list if x.tag == choice] if len(group_list) == 0: print(color_str(Color.RED, '输入有误,请检查 {} Group是否存在'.format(choice))) self.group = None else: self.group = group_list[0]
def loop_manage_server(): dp.test_server() while True: dp.print_server() show_text = ("1. 新增服务器", "2.删除服务器", "3. 清除无法连接的服务器", "4. 修改服务器Keyword", "5. 按创建时间升序排列", "6. 按创建时间降序排列") for index, text in enumerate(show_text): if index % 2 == 0: print('{:<25}'.format(text), end="") else: print(text) choice = loop_input_choice_number("\n请输入功能序号(回车退出):", len(show_text)) if choice == 1: print("输入格式为 ip [ -u ][ -p ]\nexample: 192.168.35.62 -u root -p 22, 其中-u, -p可选, 默认值root, 22\n") info = input("请输入服务器信息: ") if info: dp.add_server(info) else: print("输入为空!") elif choice == 2: delete_index=loop_input_choice_number("请输入要删除的服务器序号(回车退出): ", len(dp.server_list)) dp.modify_server_list(delete_index=delete_index-1) elif choice == 3: dp.modify_server_list(clean=True) elif choice == 4: index=loop_input_choice_number("请输入要改Keyword的服务器序号(回车退出): ", len(dp.server_list)) print("选择的服务器info: ") print(dp.server_list[index - 1]) new_keyword = input("请输入新的keyword信息(不能包含空格, 回车退出): ") if not new_keyword: break if " " in new_keyword: print("Keyword{0}包含{1}!\n".format(color_str(Color.RED, '不能'),color_str(Color.YELLOW, '空格'))) break keyword_dict={'index':index-1, 'keyword':new_keyword} dp.modify_server_list(keyword_dict=keyword_dict) elif choice == 5: dp.modify_server_list(orderBy='asc') elif choice == 6: dp.modify_server_list(orderBy='desc') print("操作成功!")
def run_command_controller(args_list): if not args_list: print(color_str(Color.RED, '传参有误!')) show_help() elif len(args_list) == 1: dp.test_server() dp.run_command(args_list[0]) else: server_list = deal_select_server(args_list[1:]) dp.run_command(args_list[0], server_list)
def __init__(self, action): super(ClientSelector, self).__init__(action) self.list_size = self.group_list[-1].node_list[-1].user_number if "删除" in action and self.list_size == 1: print(color_str(Color.RED, "仅剩最后一个节点无法删除!!!")) self.group = None elif self.list_size > 1: self.select_client() else: self.group = self.group_list[0] self.client_index = 0
def pull_file_controller(args_list): if not args_list: print(color_str(Color.RED, '传参有误!')) show_help() elif len(args_list) == 1 and len(dp.server_list) == 1: dp.test_server() pull_paths = args_list[0].split(':') local = pull_paths[0] remote = pull_paths[1] if len(pull_paths) == 2 else None dp.copy_file(local, remote, reverse=True) else: pull_paths = args_list[0].split(':') local = pull_paths[0] remote = pull_paths[1] if len(pull_paths) == 2 else None server_list=deal_select_server(args_list[1:]) if len(server_list) != 1: raise ValueError(color_str(Color.RED, '有且仅有一个服务器来传输文件')) dp.copy_file(local, remote, server_list, reverse=True)
def select_client(self): print(self.profile) self.group = None choice = input("请输入要{}的节点序号数字: ".format(self.action)) if not choice.isnumeric(): print(color_str(Color.RED, '输入错误,请检查是否为数字')) return choice = int(choice) if choice < 1 or choice > self.list_size: print(color_str(Color.RED, '输入错误,请检查是否符合范围中')) else: find = False for group in self.group_list: if find: break for index, node in enumerate(group.node_list): if node.user_number == choice: self.client_index = index self.group = group find = True break
def deal_select_server(select_server_list): select_server=set() for keyword_frag in select_server_list: frag_server=[] for server in dp.server_list: if keyword_frag in server.keyword: frag_server.append(server) if not frag_server: print("{} Keyword没匹配到服务器".format(color_str(Color.RED, keyword_frag))) else: #集合的并集 select_server = select_server | set(frag_server) if select_server: server_list = list(select_server) print("\n匹配到的服务器列表: {}\n\n".format(color_str(Color.FUCHSIA, [x.ip for x in server_list])), end="") else: server_list = dp.server_list # 测试服务器连接性 for server in server_list: server.test_ssh() return server_list
def test_ssh(self): """ 测试能否连接到服务器 """ # 调试模式,设置超时 # process = pexpect.spawnu('ssh %s' % self._get_server_command(), timeout=10, logfile=sys.stdout) process = pexpect.spawnu('ssh %s' % self._get_server_command(), timeout=10) response = [ '(?i)yes/no', self.user, '(?i)Permission denied', '(?i)password', '(?i)failed', '(?i)not known', '(?i)Connection refused', pexpect.TIMEOUT, pexpect.EOF ] while True: # 待终端回显项, (?i)表示忽略大小写 index = process.expect(response) # print("index ==> %d" % index) if index == 0: process.sendline('yes') elif index == 1: #发送结束登陆状态 process.sendcontrol('d') # process.sendeof() elif index == 2 or index == 3 or index == 4 or index == 5 or index == 6 or index == 7: self.is_ok = False print("{0} test {1}".format(color_str(Color.FUCHSIA, self.ip), color_str(Color.RED, 'fail'))) break else: self.is_ok = True print("{0} test {1}".format(color_str(Color.FUCHSIA, self.ip), color_str(Color.GREEN, 'success'))) break
def show_node(self, index): tls = "开启"if self.tls else "关闭" tfo = "TcpFastOpen: {}".format(self.tfo) if self.tfo != None else "" dyp = "DynamicPort: {}".format(self.dyp) if self.dyp.status else "" node = self.node_list[index] result = ''' {node.user_number}. Group: {self.tag} IP: {color_ip} Port: {self.port} TLS: {tls} {node}{tfo} {dyp} {link} '''.format(self=self, color_ip=color_str(Color.FUCHSIA, self.ip), node=node,tfo=tfo,dyp=dyp,tls=tls, link=node.link(self.ip, self.port, self.tls)) return result
def link(self, ip, port, tls): json_dict = { "v": "2", "ps": "", "add": ip, "port": port, "aid": self.alter_id, "type": self.header, "net": self.network, "path": self.path, "host": self.host, "id": self.password, "tls": tls } json_data = json.dumps(json_dict) return color_str(Color.GREEN, "vmess://{}".format(bytes.decode(base64.b64encode(bytes(json_data, 'utf-8')))))
def push_file_controller(args_list): if not args_list: print(color_str(Color.RED, '传参有误!')) show_help() elif len(args_list) == 1: dp.test_server() upload_paths = args_list[0].split(':') local = upload_paths[0] remote = upload_paths[1] if len(upload_paths) == 2 else None dp.copy_file(local, remote) else: upload_paths = args_list[0].split(':') local = upload_paths[0] remote = upload_paths[1] if len(upload_paths) == 2 else None server_list=deal_select_server(args_list[1:]) dp.copy_file(local, remote, server_list)
def _read_file_to_list(self, file_path): """ 配置文件每一行为一个服务器 格式为 `user@ip port Timestamp` example: [email protected] 22 123242343 """ if not os.path.exists(file_path): path_tuple = os.path.split(file_path) if not os.path.exists(path_tuple[0]): os.makedirs(path_tuple[0]) with open(file_path, 'w'): return [] server_list = [] with open(file_path, 'r') as profile: content_list = profile.readlines() if content_list: for line in content_list: frag_info = line.split("@") user = frag_info[0] frag_info = frag_info[1].split(" ") if len(frag_info) != 4: raise ValueError('配置格式不正确, 请执行:{}, 再重新运行'.format( color_str(Color.CYAN, 'rm -f ' + file_path))) ip = frag_info[0] port = frag_info[1] keyword = frag_info[2] create_date = int(frag_info[3]) one_server = Server(ip, user=user, port=port, create_date=create_date, keyword=keyword) server_list.append(one_server) return server_list
loader = Loader() profile = loader.profile group_list = profile.group_list while True: print("Iptables 端口流量统计") print("") print("1.查看流量统计\n") print("2.重置流量统计\n") print("tip: v2ray功能端口默认自动开启iptables的流量统计\n") choice = input("请输入数字选择功能:") if choice == "1": print("") for group in group_list: print(calcul_iptables_traffic(group.port)) print("") elif choice == "2": port = input("请输入要重置流量的端口:") if port and is_number(port): os.system( "bash /usr/local/multi-v2ray/global_setting/clean_traffic.sh {}" .format(str(port))) else: print(color_str(Color.RED, "输入有误!")) else: break
def link(self, ip, port, tls): ss_origin_url = "{0}:{1}@{2}:{3}".format(self.method, self.password, ip, port) return color_str( Color.GREEN, "ss://{}".format( bytes.decode(base64.b64encode(bytes(ss_origin_url, 'utf-8')))))
def link(self, ip, port, tls): return color_str( Color.GREEN, "tg://proxy?server={0}&port={1}&secret={2}".format( ip, port, self.password))