def post(self, request): req = get_post_params(request) os_name = req.get("os_name", "") os_id = req.get("os_id", 0) is_deleted = int(req.get("is_deleted", 0)) if not os_name and is_deleted == "": return Request.errorFcun("参数异常", data=[]) data = {"os_name": os_name, "is_deleted": is_deleted} # 如果id 存在更新 if os_id: os_obj = CheckSystemOs.objects.get(pk=os_id) os_obj.__dict__.update(**data) os_obj.save() if os_obj and is_deleted == 1: result_message = "删除成功" else: result_message = "修改成功" else: os_obj, created = CheckSystemOs.objects.get_or_create( defaults=data, os_name=os_name) if created: result_message = "创建成功" else: result_message = "数据已存在" if os_obj: return Request.succFcun(result_message, data=os_obj.to_dict()) else: return Request.errorFcun("创建/更新失败", data={})
def post(self, request): req = req_body_to_json(request) tpl_name = req.get("tpl_name", '') tpl_os = req.get("tpl_os", '') description = req.get("description", '') quotas = req.get("quotas", []) if not all([tpl_name, tpl_os, len(quotas)]): return Request.errorFcun("参数异常", data=[]) tpl_obj = Tpl.objects.filter(tpl_name=tpl_name, is_deleted=0).first() if tpl_obj: return Request.errorFcun(msg="添加失败 名称重复", data=[]) tpl = Tpl(tpl_name=tpl_name, tpl_os=tpl_os, description=description, author=request.user.username) # 批量插入 if tpl: try: # 下面的代码在一个事务中执行,一但出现异常,整个with函数内部的数据库操作都会回滚 with transaction.atomic(): tpl.save() # 准备批量插入数据 tpl_quota_list_to_insert = list() for quota in quotas: tpl_quota_list_to_insert.append( CheckSystemTplQuta(tpl_id=tpl.id, quota_id=quota['id'], quota_threshold=str( quota['quota_threshold']))) CheckSystemTplQuta.objects.bulk_create( tpl_quota_list_to_insert) field_names = Log.get_model_field(Tpl) field_names['quotas'] = "巡检指标" req['author'] = request.user.username req['tpl_os'] = CheckSystemOs.objects.get( pk=req['tpl_os']).os_name req['quotas'] = tpl_quotas(quotas) Log.operation_log(request, table_name=Tpl._meta.verbose_name, update_fields=req, operation_module_name="模板管理", field_names=field_names) data = req_body_to_json(request) data['tpl_id'] = tpl.id except: logger.error( f"新增模板失败 接口名称({request.path}) 请求参数({req_body_to_json(request)})" ) return Request.succFcun(msg="添加成功", data=data) else: return Request.errorFcun(msg="添加失败", data=[])
def post(self, request): req = req_body_to_json(request) quota_name = req.get("quota_name", "") quota_os = req.get("quota_os", 0) script_type = req.get("script_type", "") script_content = req.get("script_content", "") quota_handler = req.get("quota_handler", "cmp_show") quota_threshold = req.get("quota_threshold", "") username = req.get("username", "") if not all( [quota_name, username, quota_os, script_type, script_content]): return Request.errorFcun("参数异常", data=[]) quota_obj = Quota.objects.filter(quota_name=quota_name, is_deleted=0).first() if quota_obj: return Request.errorFcun(msg="添加失败 名称重复", data=[]) quota = Quota( quota_name=quota_name, quota_os=quota_os, script_type=script_type, script_content=script_content, quota_handler=quota_handler, quota_threshold=quota_threshold, author=username, quota_class=18, ) if quota: try: # 下面的代码在一个事务中执行,一但出现异常,整个with函数内部的数据库操作都会回滚 with transaction.atomic(): quota.save() field_names = Log.get_model_field(Quota) req['author'] = username req['quota_os'] = CheckSystemOs.objects.get( pk=quota_os).os_name req['quota_class'] = "自定义巡检" Log.operation_log(request, table_name=Quota._meta.verbose_name, update_fields=req, operation_module_name="自定义巡检", field_names=field_names) except: logger.error( f"新增自定义巡检失败 接口名称({request.path}) 请求参数({req_body_to_json(request)})" ) return Request.succFcun(msg="添加成功", data=[]) else: return Request.errorFcun(msg="添加失败", data=[])
def execute_task(request): """ 执行任务 按脚本执行 """ req = req_body_to_json(request) # req = request.GET task_id = int(req.get('task_id', 99)) # 数据校验 if task_id <= 0: return Request.errorFcun(msg='参数错误') # 校验数据库是否存在已经执行的任务 res = redis_link.hget(name="current_task", key=task_id) if not res: redis_link.hset(name="current_task", key=task_id, value="True") else: return Request.errorFcun(msg='任务已经在执行中') # 查询所在任务数据 tasks = Task.objects.filter(id=task_id) if not tasks.exists(): return Request.errorFcun(msg='参数错误,找不到这个任务') # 获取任务数据行 task = tasks.first() # 查询模板 tpl = Tpl.objects.filter(id=task.task_tpl_id) if not tpl.exists(): return Request.errorFcun(msg='参数错误,找不到这个模板ID') tplLogic = TplLogic() tpl_quota_list = tplLogic.getQuotas(task.task_tpl_id) if len(tpl_quota_list) == 0: return Request.errorFcun(msg='模板的指标是空的,请先添加模板指标') # 启动celery任务 task_celery.delay(task, task.id, request.user) field_names = Log.get_model_field(Task) Log.operation_log(request, Task._meta.verbose_name, task, update_fields={}, operation_module_name="任务管理", field_names=field_names, method="EXECUTE") return Request.succFcun()
def put(self, request): req = req_body_to_json(request) quota_id = req.get("quota_id", 0) quota_name = req.get("quota_name", "") quota_os = req.get("quota_os", 0) script_type = req.get("script_type", "") script_content = req.get("script_content", "") quota_handler = req.get("quota_handler", "cmp_show") quota_threshold = req.get("quota_threshold", "") if not all([ quota_id, quota_name, quota_os, script_type, script_content, quota_handler ]): return Request.errorFcun("参数异常", data=[]) # 如果设置了对比方式 但未设置值 if quota_handler != "cmp_show" and not quota_threshold: return Request.errorFcun("请设置阈值", data=[]) # 判断是否存在 quota_obj = Quota.objects.filter(id=quota_id) if quota_obj.exists(): quota = Quota.objects.get(pk=quota_id) try: # 下面的代码在一个事务中执行,一但出现异常,整个with函数内部的数据库操作都会回滚 with transaction.atomic(): req.pop("quota_id") Quota.objects.filter(id=quota_id).update(**req) field_names = Log.get_model_field(Quota) req['quota_threshold'] = quota.quota_threshold req['author'] = quota.author req['quota_os'] = CheckSystemOs.objects.get( pk=quota.quota_os).os_name req['quota_class'] = CheckSystemClass.objects.get( pk=quota.quota_class).class_name Log.operation_log(request, Quota._meta.verbose_name, quota, update_fields=req, operation_module_name="自定义巡检", field_names=field_names) except: logger.error( f"修改自定义巡检失败 接口名称({request.path}) 请求参数({req_body_to_json(request)})" ) return Request.succFcun(msg="修改成功", data=[]) else: return Request.errorFcun(msg="修改失败", data=[])
def task_detail(request): """ 任务详情 :param request: :return: """ task_id = request.GET.get('task_id') if not task_id: return Request.errorFcun(msg='参数获取失败') result = Task.objects.filter(id=task_id) if len(result) > 0: return Request.succFcun(data=[a.to_dict_detail() for a in result][0]) else: return Request.errorFcun()
def fetch_quota_by_tpl(request): """ 根据tpl查询指标 """ tpl_id = request.GET.get("tpl_id", 0) if tpl_id == 0: return Request.errorFcun('参数错误') tpl = Tpl.objects.filter(id=int(tpl_id)) if not tpl.exists(): return Request.errorFcun('模板不存在') tplLogic = TplLogic() data = tplLogic.getQuotas(tpl_id) return Request.succFcun('', data)
def off_button(request): task_id = int(request.GET.get('task_id', 181)) if task_id <= 0: return Request.errorFcun(msg='参数获取失败') res = redis_link.get(task_id) if res != None: res_dict = eval(res) res_dict['status'] = 'executed' redis_link.delete(task_id) redis_link.set(task_id, str(res_dict)) # 改变任务表中的状态 Task.objects.filter(id=task_id).update(exec_state=0) else: return Request.errorFcun(msg='获取失败') return Request.succFcun(msg='任务停止中')
def delete(self, request): tpl_id = request.GET.get('tpl_id') if not tpl_id: return Request.errorFcun("参数异常", data=[]) # 判断tpl_id是否存在 tpl_obj = Tpl.objects.filter(id=tpl_id).first() if not tpl_obj: return Request.errorFcun("模板ID不存在", data=[]) if tpl_obj.is_deleted: return Request.succFcun("该模板已删除", data=[]) else: # 备份对象用于记录日志 old_model_obj = copy.deepcopy(tpl_obj) try: # 下面的代码在一个事务中执行,一但出现异常,整个with函数内部的数据库操作都会回滚 with transaction.atomic(): # 更改删除状态为1 tpl_obj.is_deleted = 1 tpl_obj.save() field_names = Log.get_model_field(Tpl) field_names['quotas'] = "巡检指标" Log.operation_log(request, Tpl._meta.verbose_name, old_model_obj, update_fields={}, operation_module_name="模板管理", field_names=field_names) # 根据模板iD 查询模板关联指标中间表 tpl_quota_list = CheckSystemTplQuta.objects.filter( tpl_id=tpl_obj.id).all() for tpl_quota in tpl_quota_list: tpl_quota.is_deleted = 1 CheckSystemTplQuta.objects.bulk_update( tpl_quota_list, fields=['is_deleted']) except: logger.error( f"删除模板失败 接口名称({request.path}) 请求参数({req_body_to_json(request)})" ) return Request.succFcun("删除成功", data=[])
def fetch_user_list(request): client = get_client_by_request(request) req = {"no_page": True} result = client.usermanage.list_users(req) data = result.get("data", []) if isinstance(data, list): return Request.succFcun(data=data) else: return Request.errorFcun()
def delete(self, request): quota_id = request.GET.get('quota_id') if not quota_id: return Request.errorFcun("参数异常", data=[]) # 判断是否存在 quota_obj = Quota.objects.filter(id=quota_id) if not quota_obj.exists(): return Request.errorFcun("指标不存在", data=[]) # 判断是否存在模板关联关系 tpl_quota = CheckSystemTplQuta.objects.filter(quota_id=quota_id, is_deleted=0) if tpl_quota.exists(): return Request.succFcun("删除失败,与模板存在关联关系", data=[]) quota = quota_obj.first() if quota.is_deleted: return Request.succFcun("该指标已删除", data=[]) else: # 备份对象用于记录日志 old_model_obj = copy.deepcopy(quota) try: # 下面的代码在一个事务中执行,一但出现异常,整个with函数内部的数据库操作都会回滚 with transaction.atomic(): # 更改删除状态为1 quota.is_deleted = 1 quota.save() field_names = Log.get_model_field(Quota) Log.operation_log(request, Quota._meta.verbose_name, old_model_obj, update_fields={}, operation_module_name="自定义巡检", field_names=field_names) except: logger.error( f"删除自定义巡检失败 接口名称({request.path}) 请求参数({dict(request.GET)})" ) return Request.succFcun("删除成功", data=[])
def fetch_os_quota(request): """ 查询符合系统的指标 """ os_id = request.GET.get("os_id", 0) if not os_id: return Request.errorFcun(msg="参数错误") # 查询指标 系统id,通用id quotas = Quota.objects.filter(quota_os__in=[os_id, 99]).all() return Request.succFcun(data=quota_class(quotas))
def business_hosts(request): """ 业务下topo :param request: :return: """ bk_biz_id = int(request.GET.get('bk_biz_id', 2)) if bk_biz_id <= 0: return Request.errorFcun() kwargs = { "bk_biz_id": bk_biz_id, # 业务ID } client = get_client_by_request(request) result = client.cc.search_biz_inst_topo(kwargs) print(result) if result['message'] == "success": # 调用递归方法 res = Make_Recursion_List(result['data'][0]) return Request.succFcun(data=res) return Request.errorFcun()
def get(self, request): tpl_id = request.GET.get('tpl_id') if not tpl_id: return Request.errorFcun("参数异常", data=[]) # 判断tpl_id是否存在 tpl_obj = Tpl.objects.filter(id=tpl_id).first() if not tpl_obj or tpl_obj.is_deleted == 1: return Request.errorFcun("模板不存在", data=[]) # 根据模板iD 查询模板关联指标中间表 tpl_quota_list = CheckSystemTplQuta.objects.filter( tpl_id=tpl_obj.id).all() # 查询所有关联指标 quotas = Quota.objects.filter(id__in=[ quota.quota_id for quota in tpl_quota_list ], is_deleted=0).all() data = tpl_obj.to_dict() data['tpl_quotas'] = quota_class(quotas, tpl_quota_list) return Request.succFcun("sucess", data=data)
def task_delete(request): """ 删除任务 :param request: :return: """ task_id = request.GET.get('task_id') if not task_id: return Request.errorFcun(msg='参数获取失败') result = Task.objects.filter(id=task_id) if result.exists(): with transaction.atomic(): result.update(is_deleted=1) field_names = Log.get_model_field(Task) Log.operation_log(request, Task._meta.verbose_name, result.first(), update_fields={}, operation_module_name="任务管理", field_names=field_names, method="DELETE") return Request.succFcun(msg='删除成功') else: return Request.errorFcun()
def business_list(request): """ 业务列表。需要和用户权限关联 :param request: :return: """ kwargs = { "fields": ["bk_biz_id", "bk_biz_name"], } client = get_client_by_user(request.user.username) result = client.cc.search_business(kwargs) if result["result"]: return Request.succFcun(data=result["data"]["info"]) else: return Request.errorFcun()
def put(self, request): req = req_body_to_json(request) notify_configs = req.get("notify_configs") config_value = ",".join(notify_configs) try: # 下面的代码在一个事务中执行,一但出现异常,整个with函数内部的数据库操作都会回滚 with transaction.atomic(): SystemConfig.objects.filter(config_type="消息通知").update( **{"config_value": config_value}) except: logger.error( f"修改通知配置 接口名称({request.path}) 请求参数({req_body_to_json(request)})" ) notify_conf = SystemConfig.objects.filter(config_type="消息通知").first() if notify_conf.config_value == config_value: return Request.succFcun('更改通知配置成功', data=[]) else: return Request.errorFcun('更改通知配置失败', data=[])
def get(self, request): """ 查询系统/模板/自定义 指标 """ os_id = request.GET.get("os_id", 0) tpl_id = request.GET.get("tpl_id", 0) customize = request.GET.get("customize", {}) # 分页参数 current = request.GET.get("current", 1) limit = request.GET.get("limit", 10) if os_id: os_id = int(os_id) # 根据os_id查询指标 os_ids = [os_id] # 如果是centos 或 Ubuntu 添加99为Linux通用 if os_id == 1 or os_id == 2: os_ids.append(99) # 过滤os指标 quota_query_set = Quota.objects.filter(quota_os__in=os_ids, is_deleted=0) quotas = quota_query_set.all() return Request.succFcun('', data=quota_class(quotas)) if tpl_id: # 根据tpl_id查询指标 tpl = Tpl.objects.filter(id=int(tpl_id), is_deleted=0) if not tpl.exists(): return Request.errorFcun('模板不存在') # 根据模板iD 查询模板关联指标中间表 tpl_quota_list = CheckSystemTplQuta.objects.filter( tpl_id=tpl_id).all() # 查询模板关联指标 quotas = Quota.objects.filter( id__in=[quota.quota_id for quota in tpl_quota_list]).all() return Request.succFcun('', data=quota_class(quotas, tpl_quota_list)) # 查询巡检项 if customize: kwargs = json.loads(customize) # 如果存在指标名 变为模糊查询指标 if 'quota_name' in kwargs: kwargs['quota_name__icontains'] = kwargs.pop('quota_name') # 删除空值项 for key_str in list(kwargs.keys()): if not kwargs[key_str]: kwargs.pop(key_str) # 查询指标 quotas = Quota.objects.filter(**kwargs) paginator = Paginator( quotas.order_by("quota_os", "id", "-created_time"), int(limit)) data = [quota.to_dict() for quota in paginator.page(int(current))] return Request.succFcun('', data={ "count": paginator.count, "data": data })
def linux_host_detail(req): # 根据execute_id获取任务执行历史 host = req.get('host') execute_id = req.get('execute_id') try: execute_obj = ExecuteLog.objects.get(pk=execute_id) # 任务历史对象 except: return Request.errorFcun('巡检报告详情不存在') execute_dict = execute_obj.to_dict() # 任务历史json对象 task_host = { 'host': host, # 巡检主机 'create_time': execute_dict.get('created_time'), # 开始时间 'end_time': execute_dict.get('end_time') # 结束时间 } # 巡检概览 host_results = TaskHostResult.objects.filter(host_ip=host, execute_log_id=execute_id) host_result_errors = host_results.filter(result_status=False) # 安全配置信息 [账号安全 class_id=4, 系统安全 class_id=7, SSH安全设置 class_id=8, 用户安全 class_id=9, 网络安全 class_id=11] # 判断每一种安全配置是否存在,存在则获取数据,不存在则忽略 # 安全配置信息里面包含(账号安全,系统安全,SSH安全设置,用户安全,网络安全) # 逻辑说明,在巡检结果中先获取对应class id的巡检结果,循环放入列表中,然后将错误信息放入统计的列表 safe_config_error, system_run_status_error, system_performance_error, system_info_error, account_config, \ system_safe, ssh_safe, user_safe, inter_safe, system_run_status, system_performance, system_performance_info, \ system_config_info, base_info, custom_info_data_error, custom_info = analysis_configure_info(host_results) try: error_percent = float( format( float(host_result_errors.count()) / float(host_results.count()), '.2f')) * 100 except: error_percent = 0 inspection_info_detail = [ # 系统检查类型 # 异常个数 # 异常信息 { 'class_name': '安全配置信息', 'error_count': len(safe_config_error), 'error_info': safe_config_error, 'tag': 'safe_config' }, { 'class_name': '系统运行状态信息', 'error_count': len(system_run_status_error), 'error_info': system_run_status_error, 'tag': 'system_run_status' }, { 'class_name': '系统性能信息', 'error_count': len(system_performance_error), 'error_info': system_performance_error, 'tag': 'system_performance' }, { 'class_name': '系统信息', 'error_count': len(system_info_error), 'error_info': system_info_error, 'tag': 'system_info' }, { 'class_name': '自定义巡检信息', 'error_count': len(custom_info_data_error), 'error_info': custom_info_data_error, 'tag': 'custom_info' } ] inspection_info_detail_sorted = sorted(inspection_info_detail, key=lambda r: r['error_count'], reverse=True) inspection_info = { 'inspection_count': host_results.count(), # 巡检总指标数 'inspection_error': host_result_errors.count(), # 巡检异常数 'error_percent': int(error_percent), # 异常占比 'error_detail': inspection_info_detail_sorted } # CPU 物理内存 硬盘空间 inode(使用率) host_rate, memory_top = get_obj_by_quota(host_results) table_list = [ system_config_info, account_config, system_safe, ssh_safe, user_safe, inter_safe, system_run_status, system_performance, custom_info ] table_list_sorted = sorted(table_list, key=lambda r: r['error_num'] if r else 0, reverse=True) table_list_data = [ item for item in table_list_sorted if item and len(item['info']) ] # 将每个信息整合成一个dict data_all = { 'task_host': task_host, 'inspection_info': inspection_info, 'host_rate': host_rate, 'memory_top': memory_top, 'base_info': base_info, 'table_list_sorted': table_list_data } return Request.succFcun(data=data_all)
def detail(req): # 根据execute_id获取任务执行历史 host = req.get('host') execute_id = req.get('execute_id') # 返回结果 data = {} # 查询是否有此次日志 execute_obj = ExecuteLog.objects.filter(pk=execute_id).first() # 任务历史对象 if not execute_obj: return Request.errorFcun('巡检报告详情不存在') execute_dict = execute_obj.to_dict() # 任务历史json对象 task_host = { 'host': host, # 巡检主机 'create_time': execute_dict.get('created_time'), # 开始时间 'end_time': execute_dict.get('end_time') # 结束时间 } data['task_host'] = task_host # 查询本次详情 host_results = TaskHostResult.objects.filter(host_ip=host, execute_log_id=execute_id) quota_total = host_results.count() # 指标总数 quota_total_error = host_results.filter( result_status=False).count() # 异常指数 quota_list = host_results.values("system_class_id").annotate( c=Count("system_class_id")) # 根据class分组 # print(quota_list) # print(host_results.values("system_class_id")) system_class_ids = [] # class分类id for val in quota_list: system_class_ids.append(val['system_class_id']) # 查询class分类 system_class = CheckSystemClass.objects.filter(id__in=system_class_ids) class_name_map = {} for class_val in system_class: class_name_map[class_val.id] = class_val.class_name top_list = {} # 面板参数 for val in quota_list: temp_classname = class_name_map[val['system_class_id']] top_list[temp_classname] = val['c'] # print(top_list) data_list = {} # 参数列表页面 for val in host_results: temp = {"check_result": val.check_result} temp_classname = class_name_map.get(val.system_class_id, False) if not temp_classname: continue if temp_classname not in data_list: data_list[temp_classname] = [] data_list[temp_classname].append(temp) data = { 'quota_total': quota_total, 'quota_total_error': quota_total_error, 'top_list': top_list, 'data_list': data_list, 'task_host': task_host } # print(data.keys()) return Request.succFcun(data=data)
def TopoHost(request): """ 节点下的主机 :param request: :return: """ bk_obj_id = request.GET.get('bk_obj_id') bk_inst_id = int(request.GET.get('bk_inst_id', 0)) bk_biz_id = int(request.GET.get('bk_biz_id', 0)) # 获取对象,筛掉不符合要求的主机 # (1, centos) (2, ubuntu) (3, windows) os_id = int(request.GET.get('os_id', 0)) client = get_client_by_request(request) result = dict() if bk_biz_id <= 0 or bk_inst_id <= 0 or bk_obj_id == "": return Request.errorFcun(msg='缺少参数') if bk_obj_id == "biz": result = client.cc.list_biz_hosts({ "page": { "start": 0, "limit": 10, "sort": "bk_host_id" }, "bk_biz_id": bk_biz_id, "fields": ["bk_cloud_id", 'bk_host_innerip', "bk_os_name", "bk_host_name"], }) else: # 获取集群或者模块下的 result = client.cc.find_host_by_topo({ "bk_biz_id": bk_biz_id, "bk_obj_id": bk_obj_id, "bk_inst_id": bk_inst_id, "page": { "start": 0, "limit": 10 }, "fields": ["bk_cloud_id", 'bk_host_innerip', "bk_os_name", "bk_host_name"], }) # print(result['data']['info']) res_data = [] for os in result['data']['info']: if os_id == 1: os_res = os['bk_os_name'].find("centos") if os_res < 0: continue else: res_data.append(os) if os_id == 2: os_res = os['bk_os_name'].find("ubuntu") if os_res < 0: continue else: res_data.append(os) if os_id == 3: os_res = os['bk_os_name'].find("Windows") if os_res < 0: continue else: res_data.append(os) if result['result']: return Request.succFcun(data=res_data) return Request.errorFcun(msg='获取主机失败')
def task_add(request): """ 新增任务 """ # req = request.POST # 获取参数 req = req_body_to_json(request) task_tpl_id = int(req.get("task_tpl_id", -1)) # 模板ID ip_list = req.get("exec_hosts", []) # ip列表 exec_hosts = str(ip_list) # [{'bk_host_innerip': "127.0.0.1",....}] # 主机系统 task_os = int(req.get("task_os", 0)) # 通知人列表 notify_usernames = req.get("notify_usernames", "") # exec_schedule的注释 # 1. instant 立即执行 # 2. interval 定期执行 示例: interval,2020-12-31 (表示从什么时候开始) # 3. crontab 周期执行 示例: crontab,1 (表示1天执行一次) exec_schedule = req.get("exec_schedule", 0) exec_start_time = req.get("exec_start_time", "") exec_timece = req.get("exec_timec", 0) old_task_id = int(req.get('task_id', 0)) # 数据校验 if task_tpl_id <= 0 or exec_schedule == "" or int(req.get( "biz_id", 0)) <= 0 or exec_start_time == "" or int(task_os) <= 0: return Request.errorFcun(msg="参数错误") # 查询模板 tpl = Tpl.objects.filter(id=task_tpl_id) if not tpl.exists(): return Request.errorFcun(msg="参数错误,找不到模板ID") tpl_obj = tpl.first() # # 模板的指标列表 # tpl_quota_list = eval(tpl_obj.tpl_quotas) # 根据模板iD 查询模板关联指标中间表 tpl_quota_list = CheckSystemTplQuta.objects.filter(tpl_id=tpl_obj.id).all() if len(tpl_quota_list) == 0: return Request.errorFcun(msg="参数错误,模板指标是空") fields = { 'task_name': req.get("task_name", tpl_obj.tpl_name), 'task_tpl': tpl_obj, 'task_op': request.user.username, 'task_os': task_os, 'exec_hosts': exec_hosts, 'exec_biz_id': req.get("biz_id", 0), 'exec_acc': req.get( "exec_acc", "root"), # 此处写死了root,但后期应该修改为可以配置的 因为系统可能是windows,或者有专门的系统用户执行脚本 'exec_schedule': exec_schedule, 'exec_state': 0, 'exec_progress': 0, 'exec_quota_total': len(tpl_quota_list), 'exec_start_time': exec_start_time, 'exec_timece': exec_timece, 'start_time': datetime.datetime.now() } task_obj = {} if old_task_id > 0: with transaction.atomic(): old_task_obj = Task.objects.get(pk=old_task_id) task_obj = Task.objects.filter(id=old_task_id) task_obj.update(**fields) old_task_notifys = TaskNotify.objects.filter( task_id=old_task_id).all() old_notify_username_list = [ old_task_notify.username for old_task_notify in old_task_notifys ] to_delete_notify_username_list = [] to_add_notify_username_list = [] for old_notify_username in old_notify_username_list: if old_notify_username not in notify_usernames: to_delete_notify_username_list.append(old_notify_username) for new_notify_username in notify_usernames: if new_notify_username not in old_notify_username_list: to_add_notify_username_list.append(new_notify_username) TaskNotify.objects.filter( Q(task_id=old_task_id) & Q(username__in=to_delete_notify_username_list)).delete() to_add_task_notify_list = [] for username in to_add_notify_username_list: new_task_notify = TaskNotify(task_id=old_task_id, username=username) to_add_task_notify_list.append(new_task_notify) TaskNotify.objects.bulk_create(to_add_task_notify_list) fields['start_time'] = fields['start_time'].strftime( "%Y-%m-%d %H:%M:%S") fields['end_time'] = '' fields['task_tpl'] = fields['task_tpl'].id fields['task_os'] = CheckSystemOs.objects.get(pk=task_os).os_name field_names = Log.get_model_field(Task) Log.operation_log(request, Task._meta.verbose_name, old_task_obj, update_fields=fields, operation_module_name="任务管理", method="PUT", field_names=field_names) else: # 校验任务名称是否已经存在 task_name = Task.objects.filter(task_name=req.get("task_name", '')) if len([x for x in task_name]) >= 1: return Request.errorFcun(msg="任务名重复") with transaction.atomic(): task = Task.objects.create(**fields) task_obj = Task.objects.filter(id=task.id) task_notify_list = [] for username in notify_usernames: task_notify = TaskNotify(task_id=task.id, username=username) task_notify_list.append(task_notify) TaskNotify.objects.bulk_create(task_notify_list) fields['start_time'] = fields['start_time'].strftime( "%Y-%m-%d %H:%M:%S") fields['end_time'] = '' fields['task_tpl'] = fields['task_tpl'].id fields['task_os'] = CheckSystemOs.objects.get(pk=task_os).os_name field_names = Log.get_model_field(Task) Log.operation_log(request, table_name=Task._meta.verbose_name, update_fields=fields, operation_module_name="任务管理", field_names=field_names) return Request.succFcun(data=[info.to_dict_detail() for info in task_obj])
def get(cls, request, *args, **kwargs): """ 根据历史报告的ID获取巡检报告的详情 如果存在report_id,则获取指定ID的巡检报告 不存在ID,则获取最新的巡检报告 """ # 定义字典,存放业务,后续报告中根据biz_id获取业务名称 biz_dict = dict() execute_id = request.GET.get("id", '') # 获取所有业务 dates = utils.get_all_biz(request) # 过滤业务数据,拼装成json business_infos = dates.get('data').get('info') for info in business_infos: biz_dict[info.get('bk_biz_id')] = info.get('bk_biz_name') # 获取执行历史记录,根据execute中的task_id获取task, 如果没有指明id,则获取最新时间的log if execute_id: execute_result = ExecuteLog.objects.get(pk=execute_id) else: execute_result = ExecuteLog.objects.filter( exec_state__in=[2, 3]).order_by("-end_time") if execute_result: execute_result = execute_result.first() execute_id = execute_result.id # 巡检报告历史不存在 if not execute_result: return Request.errorFcun("巡检报告历史不存在") task_result = Task.objects.get(pk=execute_result.task_id) task_tpl_name = task_result.task_tpl.tpl_name result_dict = task_result.to_dict_detail() # 对象转json result_dict['task_os'] = task_result.task_tpl.get_os_name() # 业务名称 result_dict['bk_biz_name'] = biz_dict.get(task_result.exec_biz_id) # 匹配主机结果简报规则 # 1,从历史主机报告中获取对应执行报告ID的信息 # 2, 去重获取所有host # 3,过滤历史报告执行结果为false, 并获取IP,在去重 异常主机 # 4,过滤历史报告执行结果为false, 异常指标数 # 5,在host去重的基础上,判断是否有powerOff, 巡检失败主机数 result_dict['hosts'] = [] task_host_results = TaskHostResult.objects.filter( execute_log_id=execute_id) all_ips = task_host_results.values_list('host_ip', flat=True).distinct() # ip去重 # 巡检主机数 result_dict['all_ips'] = len(all_ips) all_error_hosts = task_host_results.filter( check_result__icontains='powerOff/') # all_error_hosts = task_host_results.filter(check_result='powerOff') result_dict['failed_host'] = len(all_error_hosts) # 巡检失败主机数 # 巡检失败主机IP error_host_list = all_error_hosts.values_list('host_ip', flat=True) # 异常指标 error_quotas = task_host_results.exclude( check_result__icontains='powerOff/').filter(result_status=False) # error_quotas = task_host_results.exclude(check_result='powerOff').filter(result_status=False) result_dict['error_num'] = len(error_quotas) # 异常指标数 # 异常主机数 error_hosts = error_quotas.values_list('host_ip', flat=True).distinct() result_dict['error_host'] = len(error_hosts) # 将记录和每个主机拼装 for host in all_ips: # 收集主机异常指标 host_quota = [] single_host_quotas = error_quotas.filter(host_ip=host) for single_host_quota in single_host_quotas: host_quota.append({ 'quota_name': single_host_quota.quota_id.quota_name, 'recommend_value': single_host_quota.recommend_value, 'check_result': single_host_quota.check_result }) failed_info = '' if host in error_host_list: error_info = all_error_hosts.filter(host_ip=host).first() failed_info = error_info.check_result.split('/')[-1] result_dict['hosts'].append({ 'host': host, 'host_status': 'failed' if host in error_host_list else 'success', 'bk_biz_name': result_dict['bk_biz_name'], 'task_name': result_dict['task_name'], 'task_os': result_dict['task_os'], 'task_tpl': result_dict['task_tpl'], 'task_tpl_name': task_tpl_name, 'errors': host_quota, # 这里的error是对应机器的异常的列表 # 'failed_info': failed_info }) execute_dict = execute_result.to_dict() result_dict['start_time'] = execute_dict['updated_time'] result_dict['end_time'] = execute_dict['end_time'] result_dict['execute_log_id'] = execute_dict['id'] return Request.succFcun(data=result_dict)
def fetch_task_list(request): """ 任务列表 :param request: :return: """ exec_biz_id = request.GET.get('biz_id', "") task_os = request.GET.get('os_id', "") exec_state = request.GET.get('exec_state', "") task_tpl_id = request.GET.get('tpl_id', "") # 分页 # 获取当前页 page = int(request.GET.get('page', 1)) # 获取总页数 size = int(request.GET.get('size', 10)) # 模糊查询 keywords = request.GET.get('keywords', '') # 日期查询 date = request.GET.get('date', '') old_filed = { "is_deleted": 0, "exec_biz_id": exec_biz_id, "exec_state": exec_state, "task_os": task_os, "task_name__contains": keywords, "task_tpl_id": task_tpl_id, "start_time__range": date.split(',') if date != "" else "" } filed = {} for key, value in old_filed.items(): if value != "": filed[key] = value result = Task.objects.filter(**filed).order_by('-created_time') if result.exists(): try: # 获取前端传来的页码数(需要分几页) paginator = Paginator(result, int(size)) # 获取分好后数据的某一页 scripts_ = paginator.page(int(page)) except Exception as err: # 没传参则默认分页 paginator = Paginator(result, 10) scripts_ = paginator.page(1) # 获取对应的模板数据,OS数据,业务数据 kwargs = { # 'bk_token': 'G6nuRLYKeKF2SKV2toXRNQVL9GWhOetUafkIMcsSSOM', "fields": ["bk_biz_id", "bk_biz_name"], } client = get_client_by_request(request) res = client.cc.search_business(kwargs) # 业务模板 bk_info_lists = { bk['bk_biz_id']: bk['bk_biz_name'] for bk in res['data']['info'] } # 查询OS模板 os_info_lists = { os.id: os.to_dict()['os_name'] for os in CheckSystemOs.objects.all() } # 查询指标模板 tpl_info_lists = { tpl.id: tpl.to_dict_name()['tpl_name'] for tpl in Tpl.objects.all() } data = [ x.to_dict( bk_info_lists=bk_info_lists, os_info_lists=os_info_lists, tpl_info_lists=tpl_info_lists, ) for x in scripts_ ] return Request.succFcun(data={'data': data, 'count': paginator.count}) else: return Request.errorFcun()
def windows_host_detail(req): # 根据execute_id获取任务执行历史 host = req.get("host") execute_id = req.get("execute_id") # 查询是否有此次日志 # 任务历史对象 execute_obj = ExecuteLog.objects.filter(pk=execute_id).first() if not execute_obj: return Request.errorFcun("巡检报告详情不存在") # 基本信息 execute_dict = execute_obj.to_dict() basic_info = { # 巡检主机 "host": host, # 开始时间 "create_time": execute_dict.get("created_time"), # 结束时间 "end_time": execute_dict.get("end_time") } # 巡检概览信息 # 查询本次详情 host_results = TaskHostResult.objects.filter(host_ip=host, execute_log_id=execute_id) # 指标总数 quota_total_num = host_results.count() # 异常指标数量 quota_error_num = host_results.filter(result_status=False).count() # 将查询结果转化为 [{}, {}] host_result_dicts = [host_result.to_json() for host_result in host_results] # 根据system_class_id, system_class_name进行排序 grouper = itemgetter("system_class_id", "system_class_name", "system_class_sort") sorted_host_result_dicts = sorted(host_result_dicts, key=grouper) # 根据system_class_id进行分组 grouped_host_result_dicts = groupby(sorted_host_result_dicts, key=grouper) # 指标分类 quota_class_details = [] for groupby_field_list, items in grouped_host_result_dicts: system_class_id, system_class_name, system_class_sort = groupby_field_list temp_quota_error_num = 0 temp_quota_details = [] for item in items: temp_quota_detail = { # 指标名称 "quota_name": item.get("quota_name"), # 指标推荐值 "recommend_value": item.get("recommend_value"), # 检查结果 "check_result": item.get("check_result"), # 结果比对状态 "result_status": item.get("result_status") } temp_quota_details.append(temp_quota_detail) if not item.get("result_status"): temp_quota_error_num += 1 temp = { # 巡检分类id "quota_class_id": system_class_id, # 巡检分类名称 "quota_class_name": system_class_name, # 巡检分类排序值 "quota_class_sort": system_class_sort, # 巡检分类中异常指标数 "quota_error_num": temp_quota_error_num, # 巡检分类中所有指标详情 "quota_details": temp_quota_details } quota_class_details.append(temp) # 对指标分类按照异常指标数从大到小进行排序(用户巡检概览TOP4) sorted_quota_class_details_by_quota_error_num = sorted( quota_class_details, key=lambda x: x.get("quota_error_num"), reverse=True) # 截取TOP4 if len(sorted_quota_class_details_by_quota_error_num) >= 4: quota_error_class_top4 = sorted_quota_class_details_by_quota_error_num[: 4] else: quota_error_class_top4 = sorted_quota_class_details_by_quota_error_num # 计算异常指标占比(百分比整数) if quota_total_num > 0: quota_error_percent = int( float( format(float(quota_error_num) / float(quota_total_num), '.2f')) * 100) else: quota_error_percent = 0 overview_info = { # 巡检总指标数 "quota_total_num": quota_total_num, # 巡检异常指标数 "quota_error_num": quota_error_num, # 异常指标占比 "quota_error_percent": quota_error_percent, # 异常指标分类TOP4 "quota_error_class_top4": quota_error_class_top4 } # 对指标分类按照数据库设定的排序值进行排序(用于全部展示) sorted_quota_class_details_by_quota_class_sort = sorted( quota_class_details, key=lambda x: x.get("quota_class_sort")) # 处理特殊的指标类 # 12 磁盘分区信息 # 5 系统异常服务 # 14 近七天应用错误日志 # 16 近七天系统错误日志 special_system_class_ids = [12, 5, 16, 14] for item in sorted_quota_class_details_by_quota_class_sort: quota_class_id = item.get("quota_class_id") if quota_class_id in special_system_class_ids: quota_details = item.get("quota_details") if len(quota_details) == 1: check_result = quota_details[0].get("check_result") try: new_quota_details = json.loads(check_result) if isinstance(new_quota_details, dict): new_quota_details = [new_quota_details] except Exception as e: new_quota_details = [] error_message = "loads check_result to json failed: %s , result: %s" % ( repr(e), check_result) logger.warning(error_message) # 磁盘分区信息 # 盘符输出去掉 : # 单位转换 b => GB # 计算可用空间百分比 if quota_class_id in [12]: for new_quota_detail in new_quota_details: device_id = new_quota_detail.get("DeviceID") new_quota_detail["DeviceID"] = device_id.replace( ":", "") if device_id else device_id size = new_quota_detail.get("Size") free_space = new_quota_detail.get("FreeSpace") if isinstance(size, int) and isinstance( free_space, int): new_quota_detail["Size"] = round(size / 1024 / 1024 / 1024) new_quota_detail["FreeSpace"] = round( free_space / 1024 / 1024 / 1024) new_quota_detail["FreeSpacePercent"] = int( round(free_space / size, 2) * 100) item["quota_details"] = new_quota_details # 近七天应用错误日志、近七天系统错误日志 需要将时间戳格式化为 %Y-%m-%d %H:%M:%S 格式时间, 同时生成统计数据 elif quota_class_id in [14, 16]: # 定义 EntryType 的映射 entry_type_map = {1: "Error", 2: "Warning"} # 第一遍遍历,生成 Source 和 Count的映射 source_and_count_map = {} for new_quota_detail in new_quota_details: source = new_quota_detail.get("Source") if source: if source not in source_and_count_map: source_and_count_map[source] = 1 else: source_and_count_map[source] += 1 # 第二遍遍历,格式化TimeGenerated, 插入Count for new_quota_detail in new_quota_details: new_quota_detail["Count"] = source_and_count_map.get( new_quota_detail.get("Source"), 1) new_quota_detail["EntryType"] = entry_type_map.get( new_quota_detail.get("EntryType"), new_quota_detail.get("EntryType")) # time_generated = new_quota_detail.get("TimeGenerated") # try: # if isinstance(time_generated, str): # new_time_generated = datetime.datetime.fromtimestamp( # int(time_generated) / 1000).strftime("%Y-%m-%d %H:%M:%S") # else: # new_time_generated = time_generated # except Exception as e: # new_time_generated = time_generated # error_message = "format TimeGenerated failed: %s, time_generated: %s" % (repr(e), time_generated) # logger.warning(error_message) # new_quota_detail["TimeGenerated"] = new_time_generated # 根据Count, TimeGenerated倒序排序 sorted_new_quota_details = sorted( new_quota_details, key=lambda x: (x["Count"], x["TimeGenerated"]), reverse=True) item["quota_details"] = sorted_new_quota_details else: item["quota_details"] = new_quota_details data = { # 基本信息 "basic_info": basic_info, # 巡检概览 "overview_info": overview_info, # 巡检详情 "quota_class_details": sorted_quota_class_details_by_quota_class_sort } return Request.succFcun(data=data)
def put(self, request): req = req_body_to_json(request) tpl_id = req.get("tpl_id", '') tpl_name = req.get("tpl_name", '') tpl_os = req.get("tpl_os", '') description = req.get("description", '') quotas = req.get("quotas", '') if not all([tpl_id, tpl_name, tpl_os, len(quotas)]): return Request.errorFcun("参数异常", data=[]) tpl_obj = Tpl.objects.filter(id=tpl_id).first() if not tpl_obj: return Request.errorFcun(msg="模板不存在", data=[]) quota_objs = list() for quota in quotas: # 新添加 quota_objs.append( CheckSystemTplQuta(tpl_id=tpl_obj.id, quota_id=quota['id'], quota_threshold=str( quota['quota_threshold']))) if len(quota_objs): # 备份对象用于记录日志 old_model_obj = copy.deepcopy(tpl_obj) # 如果系统更改 tasks = list() if tpl_os != tpl_obj.tpl_os: for task in tpl_obj.task_set.all(): task.task_os = tpl_os tasks.append(task) tpl_obj.tpl_os = tpl_os tpl_obj.tpl_name = tpl_name tpl_obj.description = description try: # 下面的代码在一个事务中执行,一但出现异常,整个with函数内部的数据库操作都会回滚 with transaction.atomic(): # 删除旧系统指标 CheckSystemTplQuta.objects.filter( tpl_id=tpl_obj.id).delete() # 批量创建新系统指标 CheckSystemTplQuta.objects.bulk_create(quota_objs) if tasks: Task.objects.bulk_update(tasks, fields=["task_os"]) tpl_obj.save() dict_update_fields = { "tpl_name": tpl_name, "tpl_os": CheckSystemOs.objects.get(pk=tpl_os).os_name, "author": tpl_obj.author, "description": description, "quotas": tpl_quotas(quotas) } field_names = Log.get_model_field(Tpl) field_names['quotas'] = "巡检指标" Log.operation_log(request, Tpl._meta.verbose_name, old_model_obj, update_fields=dict_update_fields, operation_module_name="模板管理", field_names=field_names) except: logger.error( f"修改模板失败 接口名称({request.path}) 请求参数({req_body_to_json(request)})" ) return Request.succFcun(msg="修改成功", data=[]) else: return Request.errorFcun(msg="修改失败", data=[])