def create(self, request, *args, **kwargs): jid = request.data.get('jid') try: with requests.Session() as s: saltapi = SaltAPI(session=s) response_data = saltapi.jid_api(jid=jid) # 当调用api失败的时候会返回false if response_data['status'] is False: logger.error(response_data) return Response(response_data) else: try: response_data = response_data['results']['return'][0] return Response({ 'results': response_data, 'status': True }) except Exception as e: return Response({ 'results': '\n' + 'salt快捷工具命令执行任务结果查询失败_error(1):' + str(response_data), 'status': False }) except Exception as e: logger.error('salt快捷工具命令执行任务结果查询后台出错_error(2):' + str(e)) return Response({ 'results': 'salt快捷工具命令执行任务结果查询后台出错_error(3):' + str(e), 'status': False })
def file_rename(self, request): serializer = saltstack_serializers.FileManageRenameSerializer( data=request.data) if not serializer.is_valid(): response_data = {'results': serializer.errors, 'status': False} return Response(response_data) old_name = serializer.validated_data.get('old_name') new_name = serializer.validated_data.get('new_name') with requests.Session() as s: saltapi = SaltAPI(session=s) response_data = saltapi.file_rename_api( tgt=settings.SITE_SALT_MASTER, arg=[old_name, new_name]) # 当调用api失败的时候会返回false if response_data['status'] is False: logger.error(response_data) return Response(response_data) else: try: response_data = response_data['results']['return'][0][ settings.SITE_SALT_MASTER] if response_data is True: return Response({'results': '重命名成功', 'status': True}) else: return Response({ 'results': '重命名失败,error:%s' % response_data, 'status': False }) except Exception as e: return Response({ 'results': '重命名失败_error(1):' + str(response_data), 'status': False })
def create(self, request, *args, **kwargs): # 删除denied里的key操作 serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) minions = request.data.get('minion_id') # 删除denied里的key比较特殊无法通过saltkey_delete_api来删除因为denied的产生是在已接受key中已经存在了同名的minion_id,然后原来 # 应该存在于未认证列表中的key就会被salt存放到denied里,而通过salt-key -d删除key会把已接受的key一起删除,官方没有提出解决办法,所以 # 只能通过命令行cmd的方式用rm删除实际存放的文件来销毁denied里的key minions = ' '.join(minions) with requests.Session() as s: saltapi = SaltAPI(session=s) # 注意master的minion_id没有设置错误一般删除没都问题 response_data = saltapi.cmd_run_api( tgt=settings.SITE_SALT_MASTER, arg='cd /etc/salt/pki/master/minions_denied/ && rm -rf %s' % minions) if response_data['status'] is False: response_data = {'results': '删除key失败', 'status': False} return Response(response_data) # 命令rm删除返回值为空,所以return值是[{}]这个值不是空哟所以没出现api失败就表示成功 else: if tasks.saltkey_list(): response_data = {'results': '删除key成功', 'status': True} return Response(response_data) else: logger.error('删除denied的key在执行刷新saltkey操作即cron.py里的方法时候出错了') response_data = {'results': '删除key失败', 'status': False} return Response(response_data)
def retrieve(self, request, *args, **kwargs): instance = self.get_object() minion_id = instance.minion_id with requests.Session() as s: saltapi = SaltAPI(session=s) response_data = saltapi.test_api(tgt=minion_id) # 失败或者成功都返回给前端,前端通过status判断执行是否完成, # 通过results内容{'192.168.68.51': False, '192.168.68.1': True}判断ping成功还是失败 return Response(response_data)
def create(self, request, *args, **kwargs): # 执行salt命令操作 client = request.data.get('client') tgt = request.data.get('tgt') tgt_type = request.data.get('tgt_type') fun = request.data.get('fun') arg = request.data.get('arg') try: # 这是判断arg是否传输值过来,如果没有前端会传个['']过来,这是由于我前端设置了的 if not len(arg): arg = None if tgt_type == 'list': tgt = [tgt] if client != 'runner': data = { 'client': client, 'tgt': tgt, 'tgt_type': tgt_type, 'fun': fun, 'arg': arg } else: data = {'client': client, 'fun': fun, 'arg': arg} with requests.Session() as s: saltapi = SaltAPI(session=s) response_data = saltapi.public(data=data) # 当调用api失败的时候会返回false if response_data is False: return Response({ 'results': '\nsalt命令执行后台出错_error(1),请联系管理员', 'status': False }) else: try: response_data = response_data['return'][0] return Response({ 'results': response_data, 'status': True }) except Exception as e: return Response({ 'results': '\n' + 'salt命令执行失败_error(2):' + str(response_data), 'status': False }) except Exception as e: logger.error('salt命令执行后台出错_error(2):' + str(e)) return Response({ 'results': 'salt命令执行后台出错_error(2):' + str(e), 'status': False })
def cmd(self, periodic_name='未命名', tgt='*', tgt_type='glob', execute_cmd=''): # periodic_name参数是为了给signals.py里结果入库之前修改task_name使得生成的结果task_name个性化使用的 with requests.Session() as s: saltapi = SaltAPI(session=s) response_data = saltapi.cmd_run_api( tgt=tgt, tgt_type=tgt_type, arg=[execute_cmd, "shell='/bin/bash'", "runas='root'"]) # 当调用api失败的时候会返回false if response_data['status'] is False: return '任务执行后台出错_error(1),请联系管理员' else: response_data = response_data['results']['return'][0] return response_data
def cmd(self, periodic_name='未命名', tgt='*', tgt_type='glob', execute_cmd=''): if tgt_type == 'list': tgt = [tgt] with requests.Session() as s: saltapi = SaltAPI(session=s) response_data = saltapi.cmd_run_api(tgt=tgt, tgt_type=tgt_type, arg=[execute_cmd, "shell='/bin/bash'", "runas='root'"]) # 当调用api失败的时候会返回false if response_data['status'] is False: return '任务执行后台出错_error(1),请联系管理员' else: response_data = response_data['results']['return'][0] return response_data
def saltkey_list(): salt_list = SaltKeyList.objects.values_list('minion_id', 'certification_status') minion_list = [] with requests.Session() as s: saltapi = SaltAPI(session=s) response_data = saltapi.saltkey_listall_api() if not response_data['status']: logger.error('saltkey_list调用api失败,请检查api接口状态') return False try: data_source = response_data['results']['return'][0]['data']['return'] minions_pre = data_source['minions_pre'] minions_denied = data_source['minions_denied'] minions = data_source['minions'] minions_rejected = data_source['minions_rejected'] if minions_pre: for i in minions_pre: minion_list.append((i, 'unaccepted')) updated_values = {'minion_id': i, 'certification_status': 'unaccepted', 'update_time': datetime.datetime.now()} SaltKeyList.objects.update_or_create(minion_id=i, certification_status='unaccepted', defaults=updated_values) if minions_denied: for i in minions_denied: minion_list.append((i, 'denied')) updated_values = {'minion_id': i, 'certification_status': 'denied', 'update_time': datetime.datetime.now()} SaltKeyList.objects.update_or_create(minion_id=i, certification_status='denied', defaults=updated_values) if minions: for i in minions: minion_list.append((i, 'accepted')) updated_values = {'minion_id': i, 'certification_status': 'accepted', 'update_time': datetime.datetime.now()} SaltKeyList.objects.update_or_create(minion_id=i, certification_status='accepted', defaults=updated_values) if minions_rejected: for i in minions_rejected: minion_list.append((i, 'rejected')) updated_values = {'minion_id': i, 'certification_status': 'rejected', 'update_time': datetime.datetime.now()} SaltKeyList.objects.update_or_create(minion_id=i, certification_status='rejected', defaults=updated_values) # 删除原表中不在本次查询结果里的记录,因为如果你删除了一个minion那么查询结果就没有这个minion了所以要从表中删除 for i in salt_list: if i not in minion_list: SaltKeyList.objects.filter(minion_id=i[0], certification_status=i[1]).delete() return True except Exception as e: logger.error('saltkey_list在执行数据库操作时候出错了:' + str(e)) return False
def salt_key_action(minions, action, message): with requests.Session() as s: saltapi = SaltAPI(session=s) action_method = getattr(saltapi, action) response_data = action_method(match=minions) if response_data['status']: if tasks.saltkey_list(): response_data = { 'results': '%s成功' % message, 'status': True } return response_data else: logger.error('%s在执行刷新saltkey操作即tasks.py里的方法时候出错了' % message) response_data = { 'results': '%s失败' % message, 'status': False } return response_data else: return response_data
def create(self, request, *args, **kwargs): # 更新minion状态 serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) minion_list = MinionList.objects.values_list('minion_id', flat=True) id_list = [] with requests.Session() as s: saltapi = SaltAPI(session=s) # salt检测minion最准的方法salt-run manage.status response_data = saltapi.saltrun_manage_status_api() if response_data['status'] is False: logger.error(response_data) return Response(response_data) else: status_up = response_data['results']['return'][0]['up'] for minion_id in status_up: updated_values = { 'minion_id': minion_id, 'minion_status': '在线', 'update_time': datetime.datetime.now() } MinionList.objects.update_or_create( minion_id=minion_id, defaults=updated_values) status_down = response_data['results']['return'][0]['down'] for minion_id in status_down: updated_values = { 'minion_id': minion_id, 'minion_status': '离线', 'update_time': datetime.datetime.now() } MinionList.objects.update_or_create( minion_id=minion_id, defaults=updated_values) id_list.extend(status_up) id_list.extend(status_down) for minion_id in minion_list: if minion_id not in id_list: MinionList.objects.filter(minion_id=minion_id).delete() return Response({'results': '更新成功', 'status': True})
def file_create(self, request): serializer = saltstack_serializers.FileManageCreateSerializer( data=request.data) if not serializer.is_valid(): response_data = {'results': serializer.errors, 'status': False} return Response(response_data) file_path = serializer.validated_data.get('file_path') file_type = serializer.validated_data.get('file_type') with requests.Session() as s: saltapi = SaltAPI(session=s) if file_type == 'd': response_data = saltapi.file_mkdir_api( tgt=settings.SITE_SALT_MASTER, arg=file_path) else: response_data = saltapi.file_touch_api( tgt=settings.SITE_SALT_MASTER, arg=file_path) # 当调用api失败的时候会返回false if response_data['status'] is False: logger.error(response_data) return Response(response_data) else: try: response_data = response_data['results']['return'][0][ settings.SITE_SALT_MASTER] if response_data is True: return Response({'results': '创建成功', 'status': True}) else: return Response({ 'results': '创建失败,error:%s' % response_data, 'status': False }) except Exception as e: return Response({ 'results': '文件读取失败_error(1):' + str(response_data), 'status': False })
def file_content(self, request): serializer = saltstack_serializers.FileManageContentSerializer( data=request.data) if not serializer.is_valid(): response_data = {'results': serializer.errors, 'status': False} return Response(response_data) file_path = serializer.validated_data.get('file_path') file_size = serializer.validated_data.get('file_size') file_type = serializer.validated_data.get('file_type') # 返回的是btyes换算成兆M就是下面,大于5M限制打开,如果后期频繁修改建议入库弄个表记录大小,然后弄个页面调整打开大小 if str(file_size).isdigit() and int(str(file_size)) > 5242880: return Response({ 'results': '文件超过5M太大无法打开,需调整上限请联系管理员', 'status': False }) elif file_type != 'f': return Response({'results': '文件读取失败,请确认是文件夹还是文件', 'status': False}) with requests.Session() as s: saltapi = SaltAPI(session=s) response_data = saltapi.file_read_api( tgt=settings.SITE_SALT_MASTER, arg=file_path) # 当调用api失败的时候会返回false if response_data['status'] is False: logger.error(response_data) return Response(response_data) else: try: file_content = response_data['results']['return'][0][ settings.SITE_SALT_MASTER] except Exception as e: return Response({ 'results': '文件读取失败_error(1):' + str(response_data), 'status': False }) return Response({'results': file_content, 'status': True})
def nginx_upstream(request): reg1 = re.compile(r'\nupstream[^\}]+}') reg2 = re.compile(r'\nupstream[^\{]+') reg3 = re.compile(r'\#* *server \d[^;]+;') reg4 = re.compile(r'\#+ *') upstreamlist = [] try: if request.method == 'GET': minionid = request.GET.get('minionid') nginxip = request.GET.get('nginxip') path = request.GET.get('path') with requests.Session() as s: saltapi = SaltAPI(session=s) if saltapi.get_token() is False: logger.error( 'nginx_upstream页get操作获取SaltAPI调用get_token请求出错') return render(request, 'nginx/nginx_upstream.html') else: response_data = saltapi.cmd_run_api(tgt=minionid, arg='cat %s' % path) if response_data is False: logger.error( '获取upstream列表失败可能代入的参数有问题,SaltAPI调用cmd_run_api请求出错' ) return render(request, 'nginx/nginx_upstream.html') # 判断upstream_data如果返回值如果为[{}]表明没有这个minionid elif response_data['return'] != [{}]: data_source = response_data['return'][0][minionid] data_list = re.findall(reg1, data_source) for i in data_list: # 获取upstream name b2 = re.search(reg2, i) # 获取upstream server列表 b3 = re.findall(reg3, i) # 用空格切割字符串取第二个就是servername了 namekey = b2.group().split(' ')[1] # 下面这个如果直接赋值b3会有一些问题,就是出现'## server'这样的也会被前端输出,所以用了 # 正则把这种出现'### '是全部替换成#这样不仅显示正常,在下面post中获取的前端upstream_server也会正确,很重要 upstreamlist.append([ namekey, [re.sub(reg4, '#', x.strip()) for x in b3] ]) return render( request, 'nginx/nginx_upstream.html', { 'upstreamlist': upstreamlist, 'minionid': minionid, 'nginxip': nginxip, 'path': path }) else: logger.error('获取upstream列表失败,请确认minion是否存在。。') return render(request, 'nginx/nginx_upstream.html') # 切换里面server的up/down状态,其实就是注释加#和不注释而已 else: upstream_name = request.POST.get('upstream_name') upstream_server = request.POST.get('upstream_server') upstream_status = request.POST.get('upstream_status') minionid = request.POST.get('minionid') path = request.POST.get('path') with requests.Session() as s: saltapi = SaltAPI(session=s) if saltapi.get_token() is False: logger.error( 'nginx_upstream页状态变更操作获取SaltAPI调用get_token请求出错') return JsonResponse({'result': '失败,后台问题', 'status': False}) else: if upstream_status == 'down': arg = "sed -i '/^upstream *%s/,/^}$/{s/%s/#%s/g}' %s&&free -m" % ( upstream_name, upstream_server, upstream_server, path) else: arg = "sed -i '/^upstream *%s/,/^}$/{s/#\+ *%s/%s/g}' %s&&free -m" % ( upstream_name, upstream_server, upstream_server, path) response_data = saltapi.cmd_run_api(tgt=minionid, arg=arg) if response_data is False: logger.error( 'nginx_upstream页状态变更SaltAPI调用cmd_run_api请求出错') return JsonResponse({ 'result': '失败,后台问题', 'status': False }) # 返回值如果为[{}]表明没有这个minionid elif response_data['return'] == [{}]: logger.error( 'nginx_upstream页状态变更SaltAPI调用cmd_run_api获取到空值了') return JsonResponse({ 'result': '失败,后台问题', 'status': False }) else: return JsonResponse({'result': '成功', 'status': True}) except Exception as e: logger.error('获取upstream列表失败' + str(e)) return render(request, 'nginx/nginx_upstream.html')
def list(self, request, *args, **kwargs): # 这里留了一个口,可以传递目录进来查询,不过实际前端并不需要传递,下面直接通过salt获取到目录了 base_path = request.query_params.get('base_path') # 获取file_roots的base目录列表,正常是返回{'return': [['/srv/salt']]} if not hasattr(settings, 'SITE_SALT_FILE_ROOTS'): with requests.Session() as s: saltapi = SaltAPI(session=s) response_data = saltapi.saltrun_file_roots_api() # 当调用api失败的时候会返回false if response_data['status'] is False: logger.error(response_data) return Response(response_data) else: try: # 结果是一个目录的列表 settings.SITE_SALT_FILE_ROOTS = response_data[ 'results']['return'][0] except Exception as e: return Response({ 'results': '文件管理执行文件目录查询失败_error(1):' + str(response_data), 'status': False }) file_roots_base = settings.SITE_SALT_FILE_ROOTS if not base_path: base_path = file_roots_base[0] elif base_path.rstrip('/') not in file_roots_base: return Response({'results': '非法目录', 'status': False}) with requests.Session() as s: saltapi = SaltAPI(session=s) response_data = saltapi.find_find_api( tgt=settings.SITE_SALT_MASTER, arg=[ 'path=%s' % base_path.rstrip('/'), 'print=path,type,size' ]) # 当调用api失败的时候会返回false if response_data['status'] is False: logger.error(response_data) return Response(response_data) else: try: response_path = response_data['results']['return'][0][ settings.SITE_SALT_MASTER] except Exception as e: return Response({ 'results': '文件管理执行文件目录查询失败_error(2):' + str(response_data), 'status': False }) # 返回的树状目录列表,下面是按照salt的find命令得到的内容做了处理最终变成一个树状列表,太难了奶奶的搞了好久才想出来 b = len(response_path) for i in range(b): path = response_path[i][0] repath = re.sub(r"^%s" % base_path.rstrip('/'), "", path, 1) data = repath.split('/')[1:] response_path[i] = { 'label': data[-1] if data else data, 'type': response_path[i][1], 'id': i + 1, 'size': response_path[i][2], 'floor': len(data), 'full_path': path } if response_path[i]['floor'] == 0: response_path[i]['label'] = base_path.rstrip('/') else: floor = response_path[i]['floor'] - 1 check = 1 count = i while count: if response_path[i - check]['floor'] == floor: if response_path[i - check].get('children'): response_path[i - check]['children'].append( response_path[i]) else: response_path[i - check]['children'] = [ response_path[i] ] count = 0 else: check += 1 count -= 1 # 多返回一个max_id,主要是前端创建文件或者文件夹的时候需要用到id,避免下重复 return Response({ 'results': [response_path[0]], 'max_id': b, 'status': True })
def saltkey_list(): print('开始更新SaltKeyList表' + time.strftime('%Y年%m月%d日 %X')) salt_list = SaltKeyList.objects.values_list('minion_id', 'certification_status') minion_list = [] with requests.Session() as s: saltapi = SaltAPI(session=s) if saltapi.get_token() is False: logger.error('saltkey_list定时操作获取SaltAPI调用get_token请求出错') print('saltkey_list定时操作获取SaltAPI调用get_token请求出错') return False else: response_data = saltapi.saltkey_listall_api() try: data_source = response_data['return'][0]['data']['return'] minions_pre = data_source['minions_pre'] minions_denied = data_source['minions_denied'] minions = data_source['minions'] minions_rejected = data_source['minions_rejected'] if minions_pre: for i in minions_pre: minion_list.append((i, 'unaccepted')) updated_values = { 'minion_id': i, 'certification_status': 'unaccepted', 'update_time': time.strftime('%Y年%m月%d日 %X') } SaltKeyList.objects.update_or_create( minion_id=i, certification_status='unaccepted', defaults=updated_values) if minions_denied: for i in minions_denied: minion_list.append((i, 'denied')) updated_values = { 'minion_id': i, 'certification_status': 'denied', 'update_time': time.strftime('%Y年%m月%d日 %X') } SaltKeyList.objects.update_or_create( minion_id=i, certification_status='denied', defaults=updated_values) if minions: for i in minions: minion_list.append((i, 'accepted')) updated_values = { 'minion_id': i, 'certification_status': 'accepted', 'update_time': time.strftime('%Y年%m月%d日 %X') } SaltKeyList.objects.update_or_create( minion_id=i, certification_status='accepted', defaults=updated_values) if minions_rejected: for i in minions_rejected: minion_list.append((i, 'rejected')) updated_values = { 'minion_id': i, 'certification_status': 'rejected', 'update_time': time.strftime('%Y年%m月%d日 %X') } SaltKeyList.objects.update_or_create( minion_id=i, certification_status='rejected', defaults=updated_values) # 删除原表中不在本次查询结果里的记录,因为如果你删除了一个minion那么查询结果就没有这个minion了所以要从表中删除 for i in salt_list: if i not in minion_list: SaltKeyList.objects.filter( minion_id=i[0], certification_status=i[1]).delete() print('saltkey_list表更新完成' + time.strftime('%Y年%m月%d日 %X')) return True except Exception as e: logger.error('saltkey_list在执行数据库操作时候出错了:' + str(e)) print('saltkey_list表更新出错,请检查' + time.strftime('%Y年%m月%d日 %X'), e) return False
def old2_minion_status(): # 用values_list配合flat=True得到minion_id的列表,用values_list获取的不是列表是QuerySet对象 # 如果要执行append或者remove等list操作无法执行 minion_list = MinionList.objects.values_list('minion_id', flat=True) id_list = [] print('开始更新Minion列表' + time.strftime('%Y年%m月%d日 %X')) with requests.Session() as s: saltapi = SaltAPI(session=s) if saltapi.get_token() is False: logger.error('minion_status定时操作获取SaltAPI调用get_token请求出错') print('minion_status定时操作获取SaltAPI调用get_token请求出错') return False else: # salt检测minion最快的方法,不访问minion而是直接通过和端口4505保持连接的ip来检测,和我旧版手动写功能一样,官方也出了 # 目前这个方法有个不算BUG的BUG就是环境必须是内网,如果出现用nat端口转发则因为这个命令本身获取是master的端口连接装态通过 # nat过来的连接ip都会是一个nat出口ip,导致判断错误,所以如果确认都是内网环境才可以使用 online_data = saltapi.saltrun_manage_alive_api( arg='show_ipv4=True') if online_data is False: print('saltrun_manage_alive_api调用API失败了') return False elif online_data['return'] == [[]]: print('没有在线的minion,搞笑') else: try: id_list.extend([key for key in online_data['return'][0]]) grains_data = saltapi.grains_itmes_api(tgt=id_list, tgt_type='list') # 这里获取了所有minion的grains内容,如果以后表字段有增加就从这里取方便 for key, value in grains_data['return'][0].items(): minion_id = key ip = online_data['return'][0][key] os = value['os'] + value['osrelease'] saltversion = value['saltversion'] sn = value['serialnumber'] cpu_num = value['num_cpus'] cpu_model = value['cpu_model'] sys = value['kernel'] kernel = value['kernelrelease'] productname = value['productname'] ipv4_addr = value['ip4_interfaces'] mac_addr = value['hwaddr_interfaces'] localhost = value['localhost'] mem_total = value['mem_total'] updated_values = { 'minion_id': minion_id, 'minionstatus': '在线', 'ip': ip, 'sn': sn, 'cpu_num': cpu_num, 'cpu_model': cpu_model, 'sys': sys, 'kernel': kernel, 'productname': productname, 'ipv4_addr': ipv4_addr, 'mac_addr': mac_addr, 'localhost': localhost, 'mem_total': mem_total, 'minionversion': saltversion, 'systemissue': os, 'updatetime': time.strftime('%Y年%m月%d日 %X') } MinionList.objects.update_or_create( minion_id=key, defaults=updated_values) except Exception as e: print( 'minion列表更新在线数据出错1,请检查' + time.strftime('%Y年%m月%d日 %X'), e) return False offline_data = saltapi.saltrun_manage_notalive_api() if offline_data is False: print('saltrun_manage_notalive_api调用API失败了') return False elif offline_data['return'] == [[]]: print('恭喜有没离线的minion') else: try: # 获取不在线结果的结构是这样的:{'return': [['10.10.10.100', '10.10.10.116']]} for key in offline_data['return'][0]: id_list.append(key) updated_values = { 'minion_id': key, 'minionstatus': '离线', 'updatetime': time.strftime('%Y年%m月%d日 %X') } MinionList.objects.update_or_create( minion_id=key, defaults=updated_values) except Exception as e: print( 'minion列表更新离线数据出错2,请检查' + time.strftime('%Y年%m月%d日 %X'), e) return False # 清理表中多出来的条目 try: for i in minion_list: if i not in id_list: MinionList.objects.filter(minion_id=i).delete() print('minion列表更新完成' + time.strftime('%Y年%m月%d日 %X')) return True except Exception as e: print('minion列表更新出错,请检查' + time.strftime('%Y年%m月%d日 %X'), e) return False
def create(self, request, *args, **kwargs): # 收集salt命令集操作 # salt接受多个minion不是正常的列表['a','b','c']而是'a,b,c' minions = request.data.get('minions') collection_style = request.data.get('collection_style') try: with requests.Session() as s: saltapi = SaltAPI(session=s) if collection_style == 'state': response_data = saltapi.sys_state_doc_api(tgt=minions, tgt_type='list') elif collection_style == 'runner': response_data = saltapi.sys_runner_doc_api(tgt=minions, tgt_type='list') else: response_data = saltapi.sys_doc_api(tgt=minions, tgt_type='list') # 当调用api失败的时候会返回false if response_data['status'] is False: logger.error(response_data) return Response(response_data) else: response_data = response_data['results']['return'][0] try: # 用来存放掉线或者访问不到的minion_id信息 info = '' # state的使用帮助特殊,比如cmd.run会有一个头cmd的说明,所以要对cmd这样做一个处理把他加入到cmd.run的使用帮助中 if collection_style == 'state': a = {} b = {} for minion_id, cmd_dict in response_data.items(): if isinstance(cmd_dict, dict): for salt_cmd, salt_cmd_doc in cmd_dict.items( ): if len(salt_cmd.split('.')) == 1: a[salt_cmd] = salt_cmd_doc else: b[salt_cmd] = salt_cmd_doc for salt_cmd in b.keys(): try: b[salt_cmd] = salt_cmd.split( '.' )[0] + ':\n' + str( a[salt_cmd.split('.')[0]] ).replace( '\n', '\n ' ) + '\n\n' + salt_cmd + ':\n' + str( b[salt_cmd]) except Exception as e: logger.error('state采集后台错误:' + str(e)) return Response({ 'results': 'state采集后台错误:' + str(e), 'status': False }) updated_values = { 'salt_cmd': salt_cmd, 'salt_cmd_type': collection_style, 'salt_cmd_module': salt_cmd.split('.')[0], 'salt_cmd_source': minion_id, 'salt_cmd_doc': b[salt_cmd], 'update_time': datetime.datetime.now() } SaltCmdInfo.objects.update_or_create( salt_cmd=salt_cmd, salt_cmd_type=collection_style, defaults=updated_values) elif isinstance(cmd_dict, bool): info += ' 不过minion_id:' + minion_id + '掉线了没有从它采集到数据' return Response({ 'results': '采集完成' + info, 'status': True }) else: for minion_id, cmd_dict in response_data.items(): if isinstance(cmd_dict, dict): for salt_cmd, salt_cmd_doc in cmd_dict.items( ): salt_cmd_doc = str( salt_cmd) + ':\n' + str( salt_cmd_doc) updated_values = { 'salt_cmd': salt_cmd, 'salt_cmd_type': collection_style, 'salt_cmd_module': salt_cmd.split('.')[0], 'salt_cmd_source': minion_id, 'salt_cmd_doc': salt_cmd_doc, 'update_time': datetime.datetime.now() } SaltCmdInfo.objects.update_or_create( salt_cmd=salt_cmd, salt_cmd_type=collection_style, defaults=updated_values) elif isinstance(cmd_dict, bool): info += ' 不过minion_id:' + minion_id + '掉线了没有从它采集到数据' return Response({ 'results': '采集完成' + info, 'status': True }) except Exception as e: logger.error('采集后台错误:' + str(e)) return Response({ 'results': '采集后台错误:' + str(e), 'status': False }) except Exception as e: logger.error('采集信息出错:' + str(e)) return Response({'results': '采集信息出错:' + str(e), 'status': False})
def create(self, request, *args, **kwargs): # 单minion更新 serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) minion_id = request.data.get('minion_id') with requests.Session() as s: saltapi = SaltAPI(session=s) response_data = saltapi.test_api(tgt=minion_id) if response_data['status'] is False: logger.error(response_data) return Response(response_data) else: # 结果类似:{'192.168.68.51': False, '192.168.68.1': True, '192.168.68.50-master': True} # 所以直接if判断true false if response_data['results'][minion_id]: try: grains_data = saltapi.grains_itmes_api(tgt=minion_id) # 这里获取了所有minion的grains内容,如果以后表字段有增加就从这里取方便 value = grains_data['results']['return'][0][minion_id] try: value['ipv4'].remove('127.0.0.1') except Exception as e: pass try: MinionList.objects.filter( minion_id=minion_id).update( minion_status='在线', ip=value.get('ipv4'), sn=value.get('serialnumber'), cpu_num=value.get('num_cpus'), cpu_model=value.get('cpu_model'), sys=value.get('kernel'), kernel=value.get('kernelrelease'), product_name=value.get('productname'), ipv4_address=value.get('ip4_interfaces'), mac_address=value.get('hwaddr_interfaces'), localhost=value.get('localhost'), mem_total=value.get('mem_total'), minion_version=value.get('saltversion'), system_issue=value.get('os') + value.get('osrelease'), update_time=datetime.datetime.now()) except Exception as e: # 有出现过某个minion的依赖文件被删除了但是minion进程还在,导致grains.items没有结果返回 # 这样就会出现vlaue不是一个字典而是是一个str正常value内容是{'ipv4':'xxxxx'}异常时候会是'grains.items is false' # 具体是什么str没记住哈哈,不过由于不少字典而又用了get来获取字典值所以会触发try的错误,也就有了下面的操作 MinionList.objects.filter( minion_id=minion_id).update( minion_status='异常', update_time=datetime.datetime.now()) except Exception as e: logger.error('单minion更新数据出错1,请检查' + str(e)) return Response({ 'results': '单minion更新数据出错1,请检查' + str(e), 'status': False }) else: try: # minion离线 MinionList.objects.filter(minion_id=minion_id).update( minion_status='离线', update_time=datetime.datetime.now()) except Exception as e: logger.error('单minion更新数据出错2,请检查' + str(e)) return Response({ 'results': '单minion更新数据出错2,请检查' + str(e), 'status': False }) return Response({'results': '更新成功', 'status': True})
def minion_status(): # 用values_list配合flat=True得到minion_id的列表,用values_list获取的不是列表是QuerySet对象 # 如果要执行append或者remove等list操作无法执行 minion_list = MinionList.objects.values_list('minion_id', flat=True) id_list = [] print('开始更新Minion列表' + time.strftime('%Y年%m月%d日 %X')) with requests.Session() as s: saltapi = SaltAPI(session=s) if saltapi.get_token() is False: logger.error('minion_status定时操作获取SaltAPI调用get_token请求出错') print('minion_status定时操作获取SaltAPI调用get_token请求出错') return False else: # salt检测minion最准的方法salt-run manage.status minion_data = saltapi.saltrun_manage_status_api() if minion_data is False: print('saltrun_manage_status_api调用API失败了') return False else: try: id_list.extend(minion_data['return'][0]['up']) grains_data = saltapi.grains_itmes_api(tgt=id_list, tgt_type='list') # 这里获取了所有minion的grains内容,如果以后表字段有增加就从这里取方便 for key, value in grains_data['return'][0].items(): minion_id = key try: value['ipv4'].remove('127.0.0.1') except Exception as e: pass try: # 下面这段代码之前都是直接用cpu_model = value['cpu_model'] 后面发现centos6和7有的有这个key有的没有导致会 # 报错,所以改成用get来获取key安全哈哈 ip = value.get('ipv4') os = value.get('os') + value.get('osrelease') saltversion = value.get('saltversion') sn = value.get('serialnumber') cpu_num = value.get('num_cpus') cpu_model = value.get('cpu_model') sys = value.get('kernel') kernel = value.get('kernelrelease') productname = value.get('productname') ipv4_addr = value.get('ip4_interfaces') mac_addr = value.get('hwaddr_interfaces') localhost = value.get('localhost') mem_total = value.get('mem_total') except Exception as e: # 有出现过某个minion的依赖文件被删除了但是minion进程还在,导致grains.items没有结果返回 # 这样就会出现vlaue不是一个字典而是是一个str正常value内容是{'ipv4':'xxxxx'}异常时候会是'grains.items is false' # 具体是什么str没记住哈哈,不过由于不少字典而又用了get来获取字典值所以会触发try的错误,也就有了下面的操作 updated_values = { 'minion_id': key, 'minion_status': '异常', 'update_time': time.strftime('%Y年%m月%d日 %X') } MinionList.objects.update_or_create( minion_id=key, defaults=updated_values) else: updated_values = { 'minion_id': minion_id, 'minion_status': '在线', 'ip': ip, 'sn': sn, 'cpu_num': cpu_num, 'cpu_model': cpu_model, 'sys': sys, 'kernel': kernel, 'product_name': productname, 'ipv4_address': ipv4_addr, 'mac_address': mac_addr, 'localhost': localhost, 'mem_total': mem_total, 'minion_version': saltversion, 'system_issue': os, 'update_time': time.strftime('%Y年%m月%d日 %X') } MinionList.objects.update_or_create( minion_id=key, defaults=updated_values) except Exception as e: print( 'minion列表更新在线数据出错1,请检查' + time.strftime('%Y年%m月%d日 %X'), e) return False try: # 更新离线minion状态 for key in minion_data['return'][0]['down']: id_list.append(key) updated_values = { 'minion_id': key, 'minion_status': '离线', 'update_time': time.strftime('%Y年%m月%d日 %X') } MinionList.objects.update_or_create( minion_id=key, defaults=updated_values) except Exception as e: print( 'minion列表更新离线数据出错2,请检查' + time.strftime('%Y年%m月%d日 %X'), e) return False # 清理表中多出来的条目 try: for i in minion_list: if i not in id_list: MinionList.objects.filter(minion_id=i).delete() # 下面这些本来是用来操作清理minion表后一些关联了minion的业务表也删除,但是后面想想我不动声响的后台去删除这些 # 表中的数据,对于使用人来说是很坑爹的事情,等下人家都不知道怎么minion就消失了,然后可能还会忘了到底原来是关联 # 那一个minion_id的,所以最后想了想还是不删除;业务逻辑中写判断minion是否存在,这样还有一个问题就是如果minion # 清理后再重新添加回来,假设加回来的是另一台服务器那会造成业务系统之前绑定了这个minion的在操作的时候会操作错误 # 因为minion实际的后端服务器换了一台,所以要在规范上面来避免这问题,尽量小心删除salt-key操作,检查是否有关联 # 业务,或者后期看下需不需要下面的删除操作改成类似添加备注说明下被删除了 # # 对AppRelease中的minion_id做删除操作,因为这个表关联了minion表,不过我没用外键,所以要手动来 # # 下面是用正则匹配minion_id只有一个或者多个时候在前面在中间在最后的情况 # app_data_list = AppRelease.objects.filter( # minion_id__regex=r'^%s$|^%s,|,%s$|,%s,' % (i, i, i, i)) # for app_data in app_data_list: # app_name = app_data.app_name # minion_id = app_data.minion_id # minion_id = minion_id.split(',') # minion_id.remove(i) # minion_id = ','.join(minion_id) # AppRelease.objects.filter(app_name=app_name).update(minion_id=minion_id) print('minion列表更新完成' + time.strftime('%Y年%m月%d日 %X')) return True except Exception as e: logger.error('minion列表更新出错,请检查' + time.strftime('%Y年%m月%d日 %X') + str(e)) print('minion列表更新出错,请检查' + time.strftime('%Y年%m月%d日 %X'), e) return False
def file_update(self, request): serializer = saltstack_serializers.FileManageUpdateSerializer( data=request.data) if not serializer.is_valid(): response_data = {'results': serializer.errors, 'status': False} return Response(response_data) file = serializer.validated_data.get('file') file_name = serializer.validated_data.get('file_name') file_path = serializer.validated_data.get('file_path') file_name = file_name + '_' + datetime.datetime.now().strftime( '%Y%m%d%H%M%S') tmp_path = os.path.join(settings.SITE_BASE_TMP_PATH, '%s' % file_name) try: with requests.Session() as s: saltapi = SaltAPI(session=s) # 如果master和rsync服务器和web服务器是同一台则直接保存到对应的文件位置即可 if settings.SITE_WEB_MINION == settings.SITE_RSYNC_MINION == settings.SITE_SALT_MASTER: try: with open('%s' % file_path, 'w') as f: f.write(file) except FileNotFoundError: os.makedirs(os.path.dirname(file_path)) with open('%s' % file_path, 'w') as f: f.write(file) else: try: with open('%s' % tmp_path, 'w') as f: f.write(file) except FileNotFoundError: os.makedirs(settings.SITE_BASE_TMP_PATH) with open('%s' % tmp_path, 'w') as f: f.write(file) # rsync服务器和web服务器是同一台则先存入rsync的daemon指定的目录中即tmp_path里然后同步到master if settings.SITE_WEB_MINION == settings.SITE_RSYNC_MINION: # 下面有一个xpgg_tmp是rsync定义的文件源,记得要和rsync服务端daemo里一致 response_data = saltapi.rsync_rsync_api( tgt=settings.SITE_SALT_MASTER, arg=[ 'src=rsync://{ip}:{port}/xpgg_tmp/{file_name}'. format(ip=settings.SITE_RSYNC_IP, port=settings.SITE_RSYNC_PORT, file_name=file_name, file_path=file_path), 'dst={file_path}'.format(file_path=file_path) ]) # 如果rsync服务和web服务器和master服务器3个都是独立的,则web服务器也需要开启rsync的daemon,然后把文件先存入web服务器的 # rsync对应目录中,master从web服务器去拉取这个文件即可 else: # 下面有一个xpgg_tmp是rsync定义的文件源,记得要和rsync服务端daemo里一致 response_data = saltapi.rsync_rsync_api( tgt=settings.SITE_SALT_MASTER, arg=[ 'src=rsync://{ip}:{port}/xpgg_tmp/{file_name}'. format(ip=settings.SITE_WEB_RSYNC_IP, port=settings.SITE_WEB_RSYNC_PORT, file_name=file_name, file_path=file_path), 'dst={file_path}'.format(file_path=file_path) ]) # 当调用api失败的时候会返回false if response_data['status'] is False: logger.error(response_data) return Response(response_data) else: try: data = response_data['results']['return'][0][ settings.SITE_SALT_MASTER] if data.get('retcode') != 0: return Response({ 'results': '更新文件在同步时失败_error(0):' + str(data['stderr']), 'status': False }) except Exception as e: return Response({ 'results': '更新文件在同步时失败_error(1):' + str(e), 'status': False }) finally: os.remove(tmp_path) except Exception as e: return Response({ 'results': '更新文件失败_error(2):' + str(e), 'status': False }) return Response({'results': '更新成功', 'status': True})