项目名:简单主机批量管理工具
一、需求
1、主机分组
2、可批量执行命令、发送文件,结果实时返回,执行格式如下
batch_run -h h1,h2,h3 -g web_clusters,db_servers -cmd "df -h"
batch_scp -h h1,h2,h3 -g web_clusters,db_servers -action put -local test.py -remote /tmp/
3、主机用户名密码、端口可以不同
二、设计表结构(model原型)
三、代码结构
model.py 表结构(model)

1 # -*- coding: utf-8 -*- 2 from lib import commons 3 from multiprocessing import Lock 4 5 6 class Host(object): 7 MUTEX = Lock() 8 9 def __init__(self, host, port, user, password): 10 self.MUTEX.acquire() 11 self.hostId = commons.create_id('host') 12 self.host = host 13 self.port = port 14 self.user = user 15 self.password = password 16 self.MUTEX.release() 17 18 def __eq__(self, other): 19 res = False 20 if type(other) == type(self): 21 if self.host == other.host: 22 res = True 23 return res 24 25 def __str__(self): 26 return 'hostId:%s host:%s' % (self.hostId, self.host) 27 28 29 class Group(object): 30 MUTEX = Lock() 31 32 def __init__(self, groupName): 33 self.MUTEX.acquire() 34 self.groupId = commons.create_id('group') 35 self.groupName = groupName 36 self.MUTEX.release() 37 38 def __eq__(self, other): 39 res = False 40 if type(other) == type(self): 41 if self.groupName == other.groupName: 42 res = True 43 return res 44 45 def __str__(self): 46 return 'groupId:%s group:%s' % (self.groupId, self.groupName) 47 48 49 class Group2Host(object): 50 MUTEX = Lock() 51 52 def __init__(self, hostId, groupId): 53 self.MUTEX.acquire() 54 self.g2hId = commons.create_id('group2Host') 55 self.hostId = hostId 56 self.groupId = groupId 57 self.MUTEX.release() 58 59 def __eq__(self, other): 60 res = False 61 if type(other) == type(self): 62 if self.hostId == other.hostId and self.groupId == other.groupId: 63 res = True 64 return res 65 66 def __str__(self): 67 return 'g2hId:%s group2Host:%s' % (self.g2hId, (self.hostId, self.groupId),)
main.py(这里实现了paramiko 用su - root 切换到root用户,再继续执行命令)

1 # Author:ton 2 # -*- coding: utf-8 -*- 3 import paramiko 4 import os 5 import threading 6 import shelve 7 import re 8 import time 9 import xlrd 10 import xlwt 11 import json 12 from conf import settings 13 from models import models 14 from core.db_handler import Db_handler 15 from core.color import Colors 16 from core import logger 17 from threading import Thread 18 19 20 # paramiko.util.log_to_file(os.path.join(settings.LOG_PATH, 'paramiko.log')) 21 class Ideploy(object): 22 def __init__(self): 23 self.initDb() 24 self.logger = logger.logger('MyIdeploy.log') 25 self.host_db = shelve.open(os.path.join(settings.HOST_TABLE_PATH, 'host')) 26 self.group_db = shelve.open(os.path.join(settings.GROUP_TABLE_PATH, 'group')) 27 self.g2h_db = shelve.open(os.path.join(settings.G2H_TABLE_PATH, 'group2Host')) 28 # 默认有default主机组 29 self.default_group_obj = Db_handler.getGroupObjByGroupName(self.group_db, 'default') 30 if not self.default_group_obj: 31 self.default_group_obj = models.Group('default') 32 Db_handler.insert_group(self.group_db, self.default_group_obj) 33 self.thread_list = [] 34 self.instructions() 35 self.run() 36 self.host_db.close() 37 self.group_db.close() 38 self.g2h_db.close() 39 40 @staticmethod 41 def exit(): 42 exit('Bye') 43 44 @staticmethod 45 def instructions(): 46 """使用说明""" 47 msg = """ 48 1、在conf/setttings下配置好需要暴力尝试密码的password_list和host_list 49 2、在Terminal终端运行工具:python bin/ideploy.py -t start 50 3、输入violentCipher,回车,完成后提示"暴力尝试密码完毕!" 51 4、输入manageHosts,回车,可以查看管理暴力尝试密码成功的主机列表,记住默认主机组(default)的groupId为1,输入b,退出 52 5、输入executeCommand,回车,输入批量执行命令:batch_run -g 1 -cmd "hostname",该hostname命令执行的主机对象为默认主机组(default)下的所有主机 53 6、输入show_task,回车,可以看到批量执行命令的结果 54 7、继续输入批量执行命令:batch_scp -g 1 -action put -local __init__.py -remote /tmp/target.py,把本地文件__init__.py上传至远端/tmp/下,并取名为target.py 55 8、输入show_task,回车,可以看到批量执行上传文件的结果 56 9、输入q,退出批量执行命令的界面,输入exit,退出程序 57 """ 58 print(msg) 59 60 @staticmethod 61 def initDb(): 62 """初始化数据库、日志目录""" 63 # 初始化各表目录及其自增长ID记录文件 64 for table_name in settings.TABLE_LIST: 65 table_path = os.path.join(settings.DATABASE_PATH, table_name) 66 table_id_file = os.path.join(table_path, 'countId') 67 if not os.path.isdir(table_path): # 创建数据文件路径 68 os.mkdir(table_path) 69 if not os.path.isfile('%s.dat' % table_id_file): # 创建自增长ID记录文件 70 data_dic = shelve.open(table_id_file) 71 data_dic['id'] = '0' 72 data_dic.close() 73 # 初始化日志目录 74 if not os.path.isdir(os.path.join(settings.BASE_PATH, 'logs')): 75 os.mkdir(os.path.join(settings.BASE_PATH, 'logs')) 76 77 def run(self): 78 while True: 79 print("欢迎来到Ideploy".center(45, '-')) 80 msg = """ 81 violentCipher暴力尝试密码 82 createGroups创建主机组 83 manageHosts管理主机 84 executeCommand批量执行命令 85 exportHostToExcel导出主机信息到excel表 86 importHostFromExcel从excel表导入主机信息 87 exit退出 88 """ 89 print(Colors(msg)) 90 print('thread number:%s' % threading.active_count()) 91 print("".center(50, '-')) 92 choice = input('输入命令>>').strip() 93 if hasattr(self, choice): 94 getattr(self, choice)() 95 96 def violentCipher(self): 97 """为每个主机分配线程尝试ssh密码""" 98 for host in settings.host_list: 99 # 多线程尝试ssh密码 100 t = Thread(target=self.tryPasswordForHost, args=(host, settings.port, settings.user)) 101 t.setDaemon(True) 102 self.thread_list.append(t) 103 # 单线程尝试ssh密码 104 # self.try_password_for_host(ip, settings.port, settings.user) 105 for t in self.thread_list: 106 t.start() 107 for t in self.thread_list: 108 t.join() 109 self.thread_list = [] 110 print(Colors('暴力尝试密码完毕!输入"manageHosts"可以管理主机', 'green')) 111 112 def tryPasswordForHost(self, host, port, user): 113 """循环ssh密码列表尝试密码,并把正确密码保存文件""" 114 password_list = settings.password_list 115 for passwd in password_list: 116 try: 117 ssh = paramiko.SSHClient() 118 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 119 ssh.connect(hostname=host, port=port, username=user, password=passwd, timeout=3) 120 msg = 'correct ip:%s user:%s password:%s' % (host, user, passwd) 121 print(Colors(msg, 'green')) 122 # 更新host、g2h表 123 host_obj = models.Host(host, port, user, passwd) 124 Db_handler.update_host(self.host_db, host_obj) 125 return_host_obj = Db_handler.check_unique_host(self.host_db, host_obj) 126 if return_host_obj: 127 host_obj = return_host_obj 128 Db_handler.insert_g2h(self.g2h_db, host_obj, self.default_group_obj) 129 break 130 except KeyError as e: 131 raise e # 调试 132 except Exception as e: 133 print(Colors(str(e), 'red')) 134 msg = 'wrong ip:%s user:%s password:%s' % (host, user, passwd) 135 print(Colors(msg, 'red')) 136 finally: 137 ssh.close() 138 139 def checkPassword(self): 140 """检查ssh密码和root密码的有效性""" 141 pass 142 143 def exportHostToExcel(self): 144 """从数据库导出所有主机信息到excel表里""" 145 # 创建workbook和sheet对象 146 workbook = xlwt.Workbook() # 注意Workbook的开头W要大写 147 sheet1 = workbook.add_sheet('sheet1', cell_overwrite_ok=True) 148 # 向sheet页中写入列名数据 149 column_names = ['host', 'groupName', 'user', 'password', 'port'] 150 for index, col in enumerate(column_names): 151 sheet1.write(0, index, col) 152 # 读取数据库,按group读取host 153 group_list = [] 154 for key in self.group_db: 155 group_list.append(self.group_db[key]) 156 group_list.sort(key=lambda group_obj: group_obj.groupId, reverse=False) # 根据groupId排序 157 for g_obj in group_list: 158 # 根据主机组Id,获取该主机组所有主机Obj列表 159 host_list = Db_handler.getHostObjListByGroupId(self.host_db, self.g2h_db, g_obj.groupId) 160 host_list.sort(key=lambda obj: obj.host, reverse=False) # 根据主机的ip排序 161 # 向sheet页中写入host数据 162 for row_num, host_obj in enumerate(host_list, 1): 163 col_values = [host_obj.host, g_obj.groupName, host_obj.user, host_obj.password, host_obj.port] 164 for col_num, col_val in enumerate(col_values): 165 sheet1.write(row_num, col_num, col_val) 166 workbook.save(os.path.join(settings.BASE_PATH, 'hosts.xls')) 167 print(Colors('导出成功!导出文件:%s' % os.path.join(settings.BASE_PATH, 'hosts.xls'), 'green')) 168 169 def importHostFromExcel(self): 170 """从excel表中导入主机信息并存入数据库""" 171 # 打开一个workbook 172 workbook = xlrd.open_workbook(os.path.join(settings.BASE_PATH, 'hosts.xls')) 173 # 定位到sheet1 174 sheet1 = workbook.sheets()[0] 175 # 遍历sheet1中所有行row 176 num_rows = sheet1.nrows 177 for row_num in range(1, num_rows): 178 row_val = sheet1.row_values(row_num) 179 group_obj = Db_handler.getGroupObjByGroupName(self.group_db, row_val[1]) 180 if group_obj: 181 host_obj = models.Host(row_val[0], int(row_val[4]), row_val[2], row_val[3]) 182 Db_handler.update_host(self.host_db, host_obj) 183 return_host_obj = Db_handler.check_unique_host(self.host_db, host_obj) 184 if return_host_obj: 185 host_obj = return_host_obj 186 Db_handler.insert_g2h(self.g2h_db, host_obj, group_obj) 187 else: 188 print(Colors('没有该主机组名[%s]' % row_val[1], 'red')) 189 else: 190 print(Colors("导入成功!请输入'manageHosts'查看导入的主机", 'green')) 191 192 def createGroups(self): 193 while True: 194 create_groupName = input('请输入需要创建的主机组名(b退出):').strip() 195 if not create_groupName: continue 196 if create_groupName == 'b': break 197 create_group = models.Group(create_groupName) 198 result_code = Db_handler.insert_group(self.group_db, create_group) 199 if result_code: 200 print(Colors('创建主机组成功', 'green')) 201 else: 202 print(Colors('该主机组已存在', 'red')) 203 204 def manageHosts(self): 205 """分配主机到主机组""" 206 # 解析分配的hostId和groupId,判断是否存在 207 while True: 208 print('所有主机信息如下:') 209 self.displayHosts() 210 choice_action = input('请输入要对主机组执行的操作(add添加,del删除,b退出):').strip() 211 if choice_action == 'b': 212 break 213 elif choice_action != 'add' and choice_action != 'del': 214 continue 215 quit_flag = True 216 while quit_flag: 217 choice_hostIds = input('请输入hostId(用空格隔开)(b退出):').strip() 218 if not choice_hostIds: continue 219 if choice_hostIds == 'b': break 220 hostId_list = choice_hostIds.split() 221 exist_flag = True 222 for hostId in hostId_list: 223 if hostId not in self.host_db: 224 exist_flag = False 225 print(Colors('输入要操作的hostId:%s 不存在' % hostId, 'red')) 226 if not exist_flag: continue 227 while quit_flag: 228 choice_groupId = input('请输入要操作的groupId(b退出):').strip() 229 if not choice_groupId: continue 230 if choice_groupId == 'b': break 231 if choice_groupId not in self.group_db: 232 print(Colors('输入的groupId:%s 不存在' % choice_groupId, 'red')) 233 continue 234 # 开始更新g2h表 235 for hostId in hostId_list: 236 if choice_action == 'add': 237 Db_handler.insert_g2h(self.g2h_db, self.host_db[hostId], self.group_db[choice_groupId]) 238 else: 239 Db_handler.delete_g2h(self.g2h_db, self.host_db[hostId], self.group_db[choice_groupId]) 240 print(Colors('配置成功', 'green')) 241 quit_flag = False 242 243 def displayHosts(self): 244 """展示管理的主机""" 245 group_list = [] 246 for key in self.group_db: 247 group_list.append(self.group_db[key]) 248 group_list.sort(key=lambda group_obj: group_obj.groupId, reverse=False) # 根据groupId排序 249 for g_obj in group_list: 250 # 根据主机组Id,获取该主机组所有主机Obj列表 251 host_list = Db_handler.getHostObjListByGroupId(self.host_db, self.g2h_db, g_obj.groupId) 252 print(Colors( 253 'Group:%s[%d](groupId:%s)' % (g_obj.groupName, len(host_list), g_obj.groupId), 'cyan')) 254 host_list.sort(key=lambda obj: obj.host, reverse=False) # 根据主机的ip排序 255 for host_obj in host_list: 256 print('\t%s' % host_obj) 257 258 def executeCommand(self): 259 """批量执行ssh命令""" 260 SSHClient(self) 261 262 263 class TaskList(list): 264 def __init__(self, item=()): 265 super().__init__(item) 266 self.logger = logger.logger('task_list') 267 268 def append(self, p_object): 269 if not isinstance(p_object, dict): 270 raise TypeError 271 super().append(p_object) 272 # with open(os.path.join(settings.BASE_PATH, 'task_list'), 'a') as f: 273 # f.write(json.dumps(p_object) + '\n') 274 self.logger.info(json.dumps(p_object)) 275 276 277 class SSHClient(object): 278 def __init__(self, ideploy_obj): 279 self.ideploy_obj = ideploy_obj 280 self.task_list = TaskList() 281 self.interactive() 282 283 @staticmethod 284 def help_msg(): 285 """ssh命令帮助信息""" 286 msg = """注意:-h 后为hostId,-g 后为groupId 287 batch_run -h 1,2,3 -g 1,2 -cmd "df -h" 288 batch_scp -h 1,2,3 -g 1,2 -action put -local test.py -remote /tmp/ 289 show_task 查看ssh命令结果 290 """ 291 print(msg) 292 293 def interactive(self): 294 while True: 295 print('\n thread number:%s' % threading.active_count()) 296 cmd = input(Colors('批量执行命令>>', 'cyan')).strip() 297 if not cmd: continue 298 if cmd == 'q': break 299 if hasattr(self, cmd[:9]): 300 getattr(self, cmd[:9])(cmd[9:].strip()) 301 else: 302 self.help_msg() 303 304 @staticmethod 305 def create_task_dic(hostId, host, command, result, date): 306 task_dic = { 307 'hostId': hostId, 308 'host': host, 309 'command': command, 310 'result': result, 311 'date': date, 312 } 313 return task_dic 314 315 def show_task(self, cmd): 316 for task_dic in self.task_list: 317 print( 318 '\033[36m(hostId:%s)host:\033[0m%s \033[36mcommand:\033[0m%s \033[36mresult:\033[0m\n%s \033[36mdate:\033[0m%s' % ( 319 task_dic['hostId'], task_dic['host'], task_dic['command'], task_dic['result'], task_dic['date'])) 320 321 def batch_run(self, cmd): 322 """批量执行batch_run命令""" 323 argv_dic = self.parse_run_command(cmd) 324 if argv_dic['cmd']: 325 self.batch('batch_run', argv_dic) 326 else: 327 self.help_msg() 328 329 def connect_batch_run(self, host_obj, argv_dic): 330 """与ssh服务端通讯执行远程ssh命令""" 331 ssh = paramiko.SSHClient() 332 try: 333 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 334 ssh.connect(hostname=host_obj.host, port=host_obj.port, username=host_obj.user, password=host_obj.password, 335 timeout=5) 336 channel = ssh.invoke_shell() 337 result = '' 338 if host_obj.user != 'root': 339 # channel.send("sudo su - \n") 340 channel.send("su - \n") 341 # 下面几行代码是发送 su - root命令时所需要输入的root密码,配置了/etc/sudoers NOPASSWD就不用密码了 342 while not re.search(r'(P|p)assword: $', result): 343 result += channel.recv(8196).decode('utf-8') 344 self.ideploy_obj.logger.info(b"++" + result.encode('utf-8') + b'++') 345 result = '' 346 channel.send('%s\n' % settings.root_pwd) 347 while not re.search(r'#[^#]{0,13}$', result): 348 result += channel.recv(8196).decode('utf-8') 349 self.ideploy_obj.logger.info(b"++" + result.encode('utf-8') + b'++') 350 result = '' 351 channel.send('%s\n' % argv_dic['cmd']) 352 while not re.search(r'#[^#]{0,13}$', result): 353 result += channel.recv(8196).decode('utf-8') 354 self.ideploy_obj.logger.info(b"++" + result.encode('utf-8') + b'++') 355 result_list = re.findall(r".*\n", result)[1:] 356 result = ''.join(result_list) 357 channel.close() 358 self.task_list.append(self.create_task_dic(host_obj.hostId, host_obj.host, 359 argv_dic['cmd'], result, 360 time.strftime("%Y-%m-%d %X", time.localtime()))) 361 except Exception as e: 362 self.task_list.append( 363 self.create_task_dic(host_obj.hostId, host_obj.host, argv_dic['cmd'], str(e), 364 time.strftime("%Y-%m-%d %X", time.localtime()))) 365 finally: 366 ssh.close() 367 368 def connect_batch_scp(self, host_obj, argv_dic): 369 """与ssh服务端建立sftp通道执行远程scp命令""" 370 try: 371 transport = paramiko.Transport((host_obj.host, host_obj.port)) 372 transport.connect(username=host_obj.user, password=host_obj.password, ) 373 sftp = paramiko.SFTPClient.from_transport(transport) 374 if argv_dic['action'] == 'put': 375 # 将location.py 上传至服务器 /tmp/test.py 376 sftp.put(argv_dic['local'], argv_dic['remote']) 377 else: 378 # 将remove_path 下载到本地 local_path 379 sftp.get(argv_dic['remote'], argv_dic['local']) 380 self.task_list.append( 381 self.create_task_dic(host_obj.hostId, host_obj.host, 382 'action[%s] local[%s] remote[%s]' % ( 383 argv_dic['action'], argv_dic['local'], argv_dic['remote']), 'OK', 384 time.strftime("%Y-%m-%d %X", time.localtime()))) 385 except Exception as e: 386 self.task_list.append( 387 self.create_task_dic(host_obj.hostId, host_obj.host, 388 'action[%s] local[%s] remote[%s]' % ( 389 argv_dic['action'], argv_dic['local'], argv_dic['remote']), str(e), 390 time.strftime("%Y-%m-%d %X", time.localtime()))) 391 finally: 392 try: 393 transport.close() 394 except UnboundLocalError: 395 pass 396 397 def batch(self, batch_type, argv_dic): 398 """为每个host分配一个线程去连接ssh服务端批量执行batch_run或batch_scp命令""" 399 finished_hostId_list = [] 400 batch_type_func = 'connect_%s' % batch_type 401 if argv_dic.get('group_list'): 402 for groupId in argv_dic['group_list']: 403 if groupId in self.ideploy_obj.group_db: 404 host_list = Db_handler.getHostObjListByGroupId(self.ideploy_obj.host_db, self.ideploy_obj.g2h_db, 405 groupId) 406 for host_obj in host_list: 407 t = Thread(target=getattr(self, batch_type_func), args=(host_obj, argv_dic)) 408 t.setDaemon(True) 409 t.start() 410 finished_hostId_list.append(host_obj.hostId) 411 else: 412 self.task_list.append(self.create_task_dic("groupId[%s]" % groupId, None, 413 None, "groupId[%s] is not exist" % groupId, 414 time.strftime("%Y-%m-%d %X", time.localtime()))) 415 if argv_dic.get('host_list'): 416 for hostId in argv_dic['host_list']: 417 if hostId in self.ideploy_obj.host_db: 418 if hostId not in finished_hostId_list: 419 t = Thread(target=getattr(self, batch_type_func), 420 args=(self.ideploy_obj.host_db[hostId], argv_dic)) 421 t.setDaemon(True) 422 t.start() 423 else: 424 self.task_list.append(self.create_task_dic(hostId, None, 425 None, "hostId[%s] is not exist" % hostId, 426 time.strftime("%Y-%m-%d %X", time.localtime()))) 427 428 def batch_scp(self, cmd): 429 """批量执行batch_scp命令""" 430 argv_dic = self.parse_scp_command(cmd) 431 if argv_dic['action'] and argv_dic['local'] and argv_dic['remote']: 432 self.batch('batch_scp', argv_dic) 433 else: 434 self.help_msg() 435 436 @staticmethod 437 def parse_target(cmd): # -h h1,h2,h3 -g web_clusters,db_servers -cmd "df -h" 438 """解析-h、-g参数,获取主机、主机组列表""" 439 host_str = re.match(r"-h[ ]+[^-]+", cmd) 440 host_list = host_str.group().strip()[2:].strip().replace(r" ", "").split(",") if host_str else None 441 group_str = re.search(r"-g[ ]+[^-]+", cmd) 442 group_list = group_str.group().strip()[2:].strip().replace(r" ", "").split(",") if group_str else None 443 return host_list, group_list 444 445 def parse_run_command(self, cmd): # -h h1,h2,h3 -g web_clusters,db_servers -cmd "df -h" 446 """解析batch_run命令参数""" 447 host_list, group_list = self.parse_target(cmd) 448 real_cmd_str = re.search(r"-cmd[ ]+(\'|\")(?P<cmd>.*)(\'|\")", cmd) 449 if real_cmd_str: 450 real_cmd_dic = real_cmd_str.groupdict() 451 real_cmd_str = real_cmd_dic['cmd'].strip() 452 else: 453 real_cmd_str = None 454 argv_dic = { 455 'host_list': host_list, 456 'group_list': group_list, 457 'cmd': real_cmd_str 458 } 459 return argv_dic 460 461 def parse_scp_command(self, cmd): # -h h1,h2,h3 -g web_clusters,db_servers -action put -local test.py -remote /tmp/ 462 """解析batch_scp命令参数""" 463 host_list, group_list = self.parse_target(cmd) 464 action_str = re.search(r"-action[ ]+[^-]+", cmd) 465 action_str = action_str.group().strip()[7:].strip() if action_str else None 466 local_str = re.search(r"-local[ ]+[^-]+", cmd) 467 local_str = local_str.group().strip()[6:].strip() if local_str else None 468 remote_str = re.search(r"-remote[ ]+[^-]+", cmd) 469 remote_str = remote_str.group().strip()[7:].strip() if remote_str else None 470 argv_dic = { 471 'host_list': host_list, 472 'group_list': group_list, 473 'action': action_str, 474 'local': local_str, 475 'remote': remote_str 476 } 477 return argv_dic
db_handler.py

1 import shelve 2 from models import models 3 from core.color import Colors 4 from multiprocessing import Lock 5 6 7 class Db_handler(object): 8 HOST_MUTEX = Lock() 9 GROUP_MUTEX = Lock() 10 G2H_MUTEX = Lock() 11 12 @staticmethod 13 # 根据主机组名称,获取该主机组Obj 14 def getGroupObjByGroupName(group_db, groupName): 15 for key in group_db: 16 if group_db[key].groupName == groupName: 17 return group_db[key] 18 return 19 20 @staticmethod 21 # 根据主机组Id,获取该主机组所有主机Obj列表 22 def getHostObjListByGroupId(host_db, g2h_db, groupId): 23 host_list = [] 24 for key in g2h_db: 25 if g2h_db[key].groupId == groupId: 26 host_list.append(host_db[g2h_db[key].hostId]) 27 return host_list 28 29 # 检查主机表唯一性(host字段唯一)(判断一个host对象是否存在) 30 @classmethod 31 def check_unique_host(cls, host_db, host_obj): 32 for key in host_db: 33 if host_db[key] == host_obj: 34 # print('存在host:%s'%host_db[key]) 35 return host_db[key] 36 return 37 38 # 检查主机组表唯一性(groupName字段唯一)(判断一个group对象是否存在) 39 @classmethod 40 def check_unique_group(cls, group_db, group_obj): 41 for key in group_db: 42 if group_db[key] == group_obj: 43 # print('存在group:%s'%group_db[key]) 44 return group_db[key] 45 return 46 47 # 检查主机组-主机表唯一性(记录中存在重复的hostId和groupId)(判断一个group2Host对象是否存在) 48 @classmethod 49 def check_unique_g2h(cls, g2h_db, g2h_obj): 50 for key in g2h_db: 51 if g2h_db[key] == g2h_obj: 52 # print('存在g2h:%s'%g2h_db[key]) 53 return g2h_db[key] 54 return 55 56 # update host table 57 @classmethod 58 def update_host(cls, host_db, host_obj): 59 cls.HOST_MUTEX.acquire() 60 return_host_obj = cls.check_unique_host(host_db, host_obj) 61 if not return_host_obj: 62 host_db[host_obj.hostId] = host_obj 63 print(Colors('insert host table 1 row', bcolor='green')) 64 else: 65 host_obj.hostId = return_host_obj.hostId 66 host_db[return_host_obj.hostId] = host_obj 67 print(Colors('update host table 1 row', bcolor='cyan')) 68 cls.HOST_MUTEX.release() 69 70 # insert group table 71 @classmethod 72 def insert_group(cls, group_db, group_obj): 73 result_code = False 74 cls.GROUP_MUTEX.acquire() 75 if not cls.check_unique_group(group_db, group_obj): 76 group_db[group_obj.groupId] = group_obj 77 result_code = True 78 print(Colors('insert group table 1 row', bcolor='green')) 79 cls.GROUP_MUTEX.release() 80 return result_code 81 82 # insert g2h table 83 @classmethod 84 def insert_g2h(cls, g2h_db, host_obj, group_obj): 85 result_code = False 86 cls.G2H_MUTEX.acquire() 87 g2h_obj = models.Group2Host(host_obj.hostId, group_obj.groupId) 88 if not cls.check_unique_g2h(g2h_db, g2h_obj): 89 g2h_db[g2h_obj.g2hId] = g2h_obj 90 result_code = True 91 print(Colors('insert g2h table 1 row', bcolor='green')) 92 cls.G2H_MUTEX.release() 93 return result_code 94 95 # delete g2h table 96 @classmethod 97 def delete_g2h(cls, g2h_db, host_obj, group_obj): 98 result_code = False 99 cls.G2H_MUTEX.acquire() 100 for key in g2h_db: 101 if g2h_db[key].hostId == host_obj.hostId and g2h_db[key].groupId == group_obj.groupId: 102 g2h_db.pop(key) 103 result_code = True 104 print(Colors('delete g2h table 1 row', bcolor='yellow')) 105 cls.G2H_MUTEX.release() 106 return result_code
logger.py

1 # -*- coding: utf-8 -*- 2 import logging 3 import os 4 from logging import handlers 5 from conf import settings 6 7 8 def logger(log_file): 9 log_level = settings.log_level 10 if log_level == 'debug': 11 log_level = logging.DEBUG 12 elif log_level == 'info': 13 log_level = logging.INFO 14 elif log_level == 'warning': 15 log_level = logging.WARNING 16 elif log_level == 'error': 17 log_level = logging.ERROR 18 else: 19 log_level = logging.CRITICAL 20 # 1.生成logger对象 21 logger = logging.getLogger(log_file) 22 logger.setLevel(logging.DEBUG) 23 # 2.生成handler对象 24 fh = handlers.TimedRotatingFileHandler(filename=os.path.join(settings.LOG_PATH, log_file), 25 when='D', interval=1, backupCount=3) 26 fh.setLevel(log_level) 27 # 2.1 把handler对象绑定到logger 28 if not logger.handlers: 29 logger.addHandler(fh) 30 # 3.生成formatter对象 31 f = logging.Formatter(fmt='%(asctime)s %(name)s [%(levelname)s] %(message)s', datefmt=None) 32 # 3.1 把formatter对象绑定到handler 33 fh.setFormatter(f) 34 return logger
color.py

1 def Colors(text, fcolor=None, bcolor=None, style=None): 2 """自定义字体样式及颜色""" 3 # 字体颜色 4 fg = { 5 'black': '\033[30m', # 字体黑 6 'red': '\033[31m', # 字体红 7 'green': '\033[32m', # 字体绿 8 'yellow': '\033[33m', # 字体黄 9 'blue': '\033[34m', # 字体蓝 10 'magenta': '\033[35m', # 字体紫 11 'cyan': '\033[36m', # 字体青 12 'white': '\033[37m', # 字体白 13 'end': '\033[0m' # 默认色 14 } 15 # 背景颜色 16 bg = { 17 'black': '\033[40m', # 背景黑 18 'red': '\033[41m', # 背景红 19 'green': '\033[42m', # 背景绿 20 'yellow': '\033[43m', # 背景黄 21 'blue': '\033[44m', # 背景蓝 22 'magenta': '\033[45m', # 背景紫 23 'cyan': '\033[46m', # 背景青 24 'white': '\033[47m', # 背景白 25 } 26 # 内容样式 27 st = { 28 'bold': '\033[1m', # 高亮 29 'url': '\033[4m', # 下划线 30 'blink': '\033[5m', # 闪烁 31 'seleted': '\033[7m', # 反显 32 } 33 34 if fcolor in fg: 35 text = fg[fcolor] + text + fg['end'] 36 if bcolor in bg: 37 text = bg[bcolor] + text + fg['end'] 38 if style in st: 39 text = st[style] + text + fg['end'] 40 return text
settings.py

1 # Author:ton 2 # -*- coding: utf-8 -*- 3 import os 4 5 BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 6 LOG_PATH = os.path.join(BASE_PATH, 'logs') 7 DATABASE_PATH = os.path.join(BASE_PATH, 'db') 8 HOST_TABLE_PATH = os.path.join(DATABASE_PATH, 'host') 9 GROUP_TABLE_PATH = os.path.join(DATABASE_PATH, 'group') 10 G2H_TABLE_PATH = os.path.join(DATABASE_PATH, 'group2Host') 11 TABLE_LIST = ['host', 'group', 'group2Host'] 12 13 14 # 暴力破解配置项 15 user = 'sshusr' 16 password_list = ['123', 'sshusr123', '123456', ] 17 host_list = ['10.0.0.11', '10.0.0.12', '10.0.0.13', '10.0.0.14'] 18 port = 22 19 root_pwd = '123456' 20 21 # 日志记录配置项 22 log_level = 'debug'
四、使用说明
1、在conf/setttings下配置好需要暴力尝试密码的password_list和host_list
2、在Terminal终端运行工具:python bin/ideploy.py -t start
3、输入violentCipher,回车,完成后提示"暴力尝试密码完毕!"
4、输入manageHosts,回车,可以查看管理暴力尝试密码成功的主机列表,记住默认主机组(default)的groupId为1,输入b,退出
5、输入executeCommand,回车,输入批量执行命令:batch_run -g 1 -cmd "hostname",该hostname命令执行的主机对象为默认主机组(default)下的所有主机
6、输入show_task,回车,可以看到批量执行命令的结果
7、继续输入批量执行命令:batch_scp -g 1 -action put -local __init__.py -remote /tmp/target.py,把本地文件__init__.py上传至远端/tmp/下,并取名为target.py
8、输入show_task,回车,可以看到批量执行上传文件的结果
9、输入q,退出批量执行命令的界面,输入exit,退出程序
截图:
PS:任务列表(show_task)会同时记录到logs/task_list文件,永久保存