def check_resource(self, request, project_id, config, cluster_id, req_instance_num): """校验资源是否满足 """ conf = config.copy() pre_instance_num = int(conf["spec"]["instance"]) if int(req_instance_num) > pre_instance_num: result = paas_cc.get_cluster(request.user.token.access_token, project_id, cluster_id) if result.get("code") != 0: raise error_codes.APIError(_("获取资源失败,请联系蓝鲸管理员解决")) data = result.get('data') or {} remain_cpu = data.get('remain_cpu') or 0 remain_mem = data.get('remain_mem') or 0 cpu_require = 0 mem_require = 0 # CPU单位是核心,有小数点,内存单位是M,和paas-cc返回一致 for info in conf["spec"]["template"]["spec"]["containers"]: cpu_require += float(info["resources"]["limits"]["cpu"]) mem_require += float(info["resources"]["limits"]["memory"]) diff_instance_num = int(req_instance_num) - pre_instance_num if remain_cpu < (diff_instance_num * cpu_require): raise error_codes.CheckFailed(_("没有足够的CPU资源,请添加node或释放资源!")) if remain_mem < (diff_instance_num * mem_require): raise error_codes.CheckFailed(_("没有足够的内存资源,请添加node或释放资源!"))
def allow_oper_node(self, node_info, curr_node_status): not_allow_msg = "some nodes of the selected nodes do not allow operation, please check the nodes status!" if node_info['status'] == NodeStatus.ToRemoved and curr_node_status in [ node_info['status'], NodeStatus.Removable, ]: raise error_codes.CheckFailed(not_allow_msg) if node_info['status'] == NodeStatus.Normal and curr_node_status in [node_info['status'], NodeStatus.Normal]: raise error_codes.CheckFailed(not_allow_msg)
def get_application_staff(username, bk_biz_id, fields=None): """获取业务的成员列表 """ if not fields: fields = ["bk_biz_developer", "bk_biz_maintainer", "bk_biz_tester", "bk_biz_productor"] resp = get_application_with_page(username, fields=fields, condition={"bk_biz_id": bk_biz_id}) if resp.get("code") != ErrorCode.NoError: raise error_codes.CheckFailed(_("该项目关联的业务不正确,请确认后重试")) data = resp.get("data") or {} info = data.get("info") or [] if not info: raise error_codes.CheckFailed(_("查询项目信息为空")) return info[0]
def get_filter_params(self, request): """获取过滤参数""" cluster_type = request.GET.get("cluster_type") if not cluster_type or cluster_type not in constants.CLUSTER_TYPE: raise error_codes.CheckFailed(_("集群类型不正确,请确认后重试!")) app_status = request.GET.get("app_status") if app_status and app_status not in constants.APP_STATUS: raise error_codes.CheckFailed(_("应用状态不正确,请确认后重试!")) tmpl_set_id = request.GET.get("muster_id") app_id = request.GET.get("app_id") ns_id = request.GET.get("ns_id") return cluster_type, app_status, tmpl_set_id, app_id, ns_id
def get_category(self, request, kind): """获取类型""" category = request.GET.get("category") if kind == constants.K8S_KIND: if not category: raise error_codes.CheckFailed(_("应用类型不能为空")) else: if category not in constants.CATEGORY_MAP: raise error_codes.CheckFailed(_("类型不正确,请确认")) category = [constants.CATEGORY_MAP[category]] else: category = constants.MESOS_APPLICATION_TYPE return category
def get_cluster_last_log(self): log = ClusterInstallLog.objects.filter( project_id=self.project_id, cluster_id=self.cluster_id, ).last() if not log: raise error_codes.CheckFailed(_("没有查询对应的任务日志")) return log
def get( self, request, project_id, all_muster_list, muster_id_list, category, cluster_type, app_status, app_name, ns_id, cluster_env_map, ): if category not in CATEGORY_MAP: raise error_codes.CheckFailed(_("类型不正确,请确认")) # 获取模板ID和名称的对应关系 muster_id_name_map = { info["id"]: info["name"] for info in all_muster_list } # 获取version instance,用于展示模板集下是否有实例 muster_num_map = self.get_version_instance(muster_id_list, category, cluster_type, app_name, ns_id, cluster_env_map) return self.muster_tmpl_handler(muster_id_name_map, muster_num_map)
def validate_view_perms(self, request, project_id, muster_id, ns_id, source_type="模板集"): """查询资源时, 校验用户的查询权限""" resp = paas_cc.get_namespace(request.user.token.access_token, project_id, ns_id) data = resp.get('data') perm_ctx = NamespaceScopedPermCtx( username=request.user.username, project_id=project_id, cluster_id=data.get('cluster_id'), name=data.get('name'), ) NamespaceScopedPermission().can_view(perm_ctx) if source_type == "模板集": muster_info = Template.objects.filter(id=muster_id, is_deleted=False).first() if not muster_info: raise error_codes.CheckFailed(_("没有查询到模板集信息")) perm_ctx = TemplatesetPermCtx(username=request.user.username, project_id=project_id, template_id=muster_id) TemplatesetPermission().can_view(perm_ctx)
def retrieve(self, request, project_id, pk): details = self.queryset.filter(id=pk, project_id=project_id, is_deleted=False).values() if not details: raise error_codes.CheckFailed(_("没有查询到实例信息,请联系管理员处理")) data = details[0] perm = bcs_perm.Namespace(request, project_id, data["namespace_id"]) perm.can_use(raise_exception=True) access_token = request.user.token.access_token cluster_id_name_map = self.get_cluster_id_name_map( access_token, project_id) data["cluster_name"] = cluster_id_name_map[data["cluster_id"]]["name"] ip_info = json.loads(data["ip_info"]) node_id_info_map = self.get_node_info(access_token, project_id, data["cluster_id"]) render_ip_info = [] for info in ip_info: item = { "id": info, "inner_ip": node_id_info_map[int(info)]["inner_ip"], "unshared": ip_info[info] } render_ip_info.append(item) data["ip_info"] = json.dumps(render_ip_info) return Response(data)
def get_params_for_client(self, request): name = request.GET.get("name") namespace = request.GET.get("namespace") category = request.GET.get("category") if not (name and namespace and category): raise error_codes.CheckFailed(_("参数[name]、[namespace]、[category]不能为空")) return name, namespace, category
def update(self, request, project_id): """更新项目信息""" if not self.can_edit(request, project_id): raise error_codes.CheckFailed(_("请确认有项目管理员权限,并且项目下无集群")) data = self.validate_update_project_data(request) access_token = request.user.token.access_token data["updator"] = request.user.username # 添加操作日志 ual_client = client.UserActivityLogClient( project_id=project_id, user=request.user.username, resource_type="project", resource=request.project.project_name, resource_id=project_id, description="{}: {}".format(_("更新项目"), request.project.project_name), ) resp = paas_cc.update_project_new(access_token, project_id, data) if resp.get("code") != ErrorCode.NoError: ual_client.log_modify(activity_status="failed") raise error_codes.APIError(_("更新项目信息失败,错误详情: {}").format(resp.get("message"))) ual_client.log_modify(activity_status="succeed") project_data = resp.get("data") if project_data: project_data["created_at"], project_data["updated_at"] = self.normalize_create_update_time( project_data["created_at"], project_data["updated_at"] ) # 主动令缓存失效 self.invalid_project_cache(project_id) # 创建或更新依赖服务,包含data、template、helm update_bcs_service_for_project(request, project_id, data) return Response(project_data)
def update(self, request, project_id): """更新项目信息 """ if not self.can_edit(request, project_id): raise error_codes.CheckFailed("请确认有项目管理员权限,并且项目下无集群", replace=True) data = self.validate_update_project_data(request) access_token = request.user.token.access_token data['updator'] = request.user.username # 添加操作日志 ual_client = client.UserActivityLogClient( project_id=project_id, user=request.user.username, resource_type='project', resource=request.project.project_name, resource_id=project_id, description="更新项目: %s" % request.project.project_name, ) project = paas_cc.update_project_new(access_token, project_id, data) if project.get('code') != 0: ual_client.log_modify(activity_status='failed') raise error_codes.APIError(project.get('message', "更新项目成功")) ual_client.log_modify(activity_status='succeed') project_data = project.get('data') if project_data: project_data['created_at'], project_data['updated_at'] = self.normalize_create_update_time( project_data['created_at'], project_data['updated_at']) # 主动令缓存失效 self.invalid_project_cache(project_id) # 创建或更新依赖服务,包含data、template、helm update_bcs_service_for_project(request, project_id, data) return Response(project_data)
def check_namespace_used(self, cluster_id, namespace_id): """校验集群下命名空间是否已经被占用,如果占用则提示已经被使用 """ if K8SLoadBlance.objects.filter(cluster_id=cluster_id, namespace_id=namespace_id, name=K8S_LB_NAME).exists(): raise error_codes.CheckFailed(_("命名空间已经被占用,请选择其他命名空间"))
def get_instance_info(self, id): """获取instance info """ inst_info = InstanceConfig.objects.filter(id=id, is_deleted=False) if not inst_info: raise error_codes.CheckFailed(_("没有查询到相应的记录")) return inst_info
def can_use_instance(cls, request, project_id, ns_id, tmpl_set_id=None, source_type=SourceType.TEMPLATE): # 继承命名空间的权限 resp = paas_cc.get_namespace(request.user.token.access_token, project_id, ns_id) data = resp.get('data') perm_ctx = NamespaceScopedPermCtx( username=request.user.username, project_id=project_id, cluster_id=data.get('cluster_id'), name=data.get('name'), ) NamespaceScopedPermission().can_use(perm_ctx) if source_type == SourceType.TEMPLATE: tmpl_set_info = Template.objects.filter(id=tmpl_set_id).first() if not tmpl_set_info: raise error_codes.CheckFailed( f"template:{tmpl_set_id} not found") # 继承模板集的权限 perm_ctx = TemplatesetPermCtx(username=request.user.username, project_id=project_id, template_id=tmpl_set_id) TemplatesetPermission().can_instantiate(perm_ctx)
def get_vpc_id(self, request, region, clb_name): data = clb_utils.describe_clb_detail(request.user.token.access_token, request.user.username, request.project.cc_app_id, region) if clb_name not in data: raise error_codes.CheckFailed(f'clb:[{clb_name}] not found') return data[clb_name]['vpc_id']
def check_node_ip(self): project_node_list = [ info['inner_ip'] for info in self.project_nodes if info['status'] not in [CommonStatus.Removed] ] intersection = set(project_node_list) & set(self.ip_list) if intersection: raise error_codes.CheckFailed(_("部分主机已经使用,IP为{}").format(','.join(intersection)))
def reinstall(self): self.ratelimit() # 校验集群编辑权限 self.check_perm() # 通过node id获取Ip信息 node_info = self.get_node_ip() log = self.get_node_last_log() node_ip = node_info.get('inner_ip') params = json.loads(log.params) # 校验权限 if node_info.get('status') not in [NodeStatus.Removed, NodeStatus.InitialFailed]: raise error_codes.CheckFailed(_("IP: {}正在操作中,请勿重复操作").format(node_ip)) with client.ContextActivityLogClient( project_id=self.project_id, user=self.username, resource_type=ACTIVITY_RESOURCE_TYPE, resource=node_ip, resource_id=self.node_id, ).log_modify(): self.update_nodes([node_ip]) # 调用OPS api self.need_nat = params['need_nat'] self.master_ip_list = params['master_ip_list'] self.module_id_list = params['module_id_list'] self.ip_list = [node_ip] log = self.create_node_by_bcs( [node_info], control_ip=params['control_ip'], config=params['config'], websvr=params['websvr'] ) if not log.is_finished and log.is_polling: log.polling_task() return Response({})
def bcs_single_app_perm_handler(self, request, project_id, muster_id, ns_id, source_type="模板集"): """针对具体资源的权限处理""" # 继承命名空间的权限 resp = paas_cc.get_namespace(request.user.token.access_token, project_id, ns_id) data = resp.get('data') perm_ctx = NamespaceScopedPermCtx( username=request.user.username, project_id=project_id, cluster_id=data.get('cluster_id'), name=data.get('name'), ) NamespaceScopedPermission().can_use(perm_ctx) if source_type == "模板集": muster_info = Template.objects.filter(id=muster_id, is_deleted=False).first() if not muster_info: raise error_codes.CheckFailed(_("没有查询到模板集信息")) # 继承模板集的权限 perm_ctx = TemplatesetPermCtx(username=request.user.username, project_id=project_id, template_id=muster_id) TemplatesetPermission().can_instantiate(perm_ctx)
def get_application_staff(username, bk_biz_id, fields=None): """获取业务的成员列表""" if not fields: fields = [ 'bk_biz_developer', 'bk_biz_maintainer', 'bk_biz_tester', 'bk_biz_productor' ] resp = cmdb.get_application_with_page(username, fields=fields, condition={'bk_biz_id': bk_biz_id}) if resp.get('code') != ErrorCode.NoError: raise error_codes.CheckFailed(_('该项目关联的业务不正确,请确认后重试')) data = resp.get('data') or {} info = data.get('info') or [] if not info: raise error_codes.CheckFailed(_('查询项目信息为空')) return info[0]
def get_request_category(self, request, project_kind): """获取请求类型""" category = None if project_kind == constants.K8S_KIND: category = request.GET.get("category") if not category: raise error_codes.CheckFailed(_("应用类型不能为空")) return category
def project_kind(self, request): """ 处理项目project info """ kind = request.project.get("kind") if kind not in [1, 2]: raise error_codes.CheckFailed(_("项目类型必须为k8s/mesos, 请确认后重试!")) return kind
def node_handler(self, request, project_id, cluster_id, node_info): driver = K8SDriver(request, project_id, cluster_id) if node_info['status'] == NodeStatus.ToRemoved: driver.disable_node(node_info['inner_ip']) elif node_info['status'] == NodeStatus.Normal: driver.enable_node(node_info['inner_ip']) else: raise error_codes.CheckFailed(f'node of the {node_info["status"]} does not allow operation')
def check_host_stop_scheduler(self, node_info): status = [ NodeStatus.ToRemoved, NodeStatus.Removable, NodeStatus.RemoveFailed, CommonStatus.ScheduleFailed, NodeStatus.InitialFailed ] if node_info.get("status") not in status: raise error_codes.CheckFailed(_("节点必须要先停用,才可以删除,请确认!"))
def get_cluster_by_ns_name(self, request, project_id, ns_name): """通过命名空间获取集群""" ok, all_ns_info = self.get_namespaces(request, project_id) if not ok: raise error_codes.APIError.f(all_ns_info.data.get("message")) for info in (all_ns_info.get("results") or []): if info["name"] == ns_name: return info["cluster_id"] raise error_codes.CheckFailed(_("没有查询到命名空间对应的集群ID"))
def validate_instance(self, instance): """当instance和ip_list都存在时,要校验两者数量相同""" data = self._kwargs.get('data', {}) ip_list = data.get('ip_list') if not ip_list: return instance if len(ip_list) != instance: raise error_codes.CheckFailed(_("参数[ip_list]和[instance]必须相同")) return instance
def ratelimit(self): rate_limiter = RateLimiter(rd_client, '%s_%s' % (self.cluster_id, self.node_id)) rate_limiter.add_rule(1, {"second": 15}) try: resp = rate_limiter.acquire() except Exception as error: logger.error('%s, %s' % (bk_error_codes.ConfigError.code, "获取token出现异常,详情:%s" % error)) if not resp.get('allowed'): raise error_codes.CheckFailed(_("已经触发操作,请勿重复操作"))
def create_node_labels(self, request, project_id): """添加节点标签""" # 解析参数 node_id_list, node_label_info = self.get_create_label_params(request) # 校验label中key和value self.label_regex(node_label_info) # 获取数据库中节点的label # NOTE: 节点为正常状态时,才允许设置标签 project_node_info = self.get_node_list(request, project_id, None).get('results') or [] if not project_node_info: raise error_codes.APIError(_("当前项目下节点为空,请确认")) all_node_id_list = [] all_node_id_ip_map = {} for info in project_node_info: all_node_id_list.append(info["id"]) all_node_id_ip_map[info["id"]] = {"inner_ip": info["inner_ip"], "cluster_id": info["cluster_id"]} if info['id'] in node_id_list and info['status'] != CommonStatus.Normal: raise error_codes.CheckFailed(_("节点不是正常状态时,不允许设置标签")) diff_node_id_list = set(node_id_list) - set(all_node_id_list) if diff_node_id_list: raise error_codes.CheckFailed(_("节点ID [{}] 不属于当前项目,请确认").format(",".join(diff_node_id_list))) # 校验权限 self.check_perm(request, project_id, all_node_id_ip_map, node_id_list) # 匹配数据 pre_node_labels = self.get_labels_by_node(request, project_id, node_id_list) label_operation_map = self.get_label_operation( pre_node_labels, node_label_info, node_id_list, all_node_id_ip_map ) # k8s 是以节点为维度 self.create_node_label_via_k8s(request, project_id, label_operation_map) # 写入数据库 self.create_or_update(request, project_id, label_operation_map) client.ContextActivityLogClient( project_id=project_id, user=request.user.username, resource_type="node", resource=str(node_id_list), resource_id=str(node_id_list), extra=json.dumps(node_label_info), description=_("节点打标签"), ).log_add(activity_status="succeed") return Response({"code": 0, "message": _("创建成功!")})
def delete(self, request, project_id, cluster_id, node_id): access_token = request.user.token.access_token node_info = self.get_node_by_id(access_token, project_id, cluster_id, node_id) # check node allow operation if node_info.get('status') not in NotReadyNodeStatus: raise error_codes.CheckFailed( 'current node does not allow delete operation, please check node status!') # set current node status is removed self.update_nodes_in_cluster( access_token, project_id, cluster_id, [node_info['inner_ip']], CommonStatus.Removed) return response.Response()
def get_oper_node_info(self, node_list, req_node_id_list, req_status): exist_node_list = [] for info in node_list: curr_node_status = info.get('status') info['status'] = req_status # check node belong to the cluster and allow to operate if info['id'] in req_node_id_list: exist_node_list.append(info) self.allow_oper_node(info, curr_node_status) if len(exist_node_list) != len(req_node_id_list): raise error_codes.CheckFailed('many nodes do not belong the cluster') return exist_node_list