def delete_all(self, request, data, project_id, muster_id, show_version_name, res_name, muster_name): """删除所有类型""" ns_id_list = data.get("namespace_list") id_list = data.get("id_list") if not (ns_id_list and id_list): raise error_codes.CheckFailed(_("参数不能为空!")) # 获取项目信息 flag, project_kind = self.get_project_kind(request, project_id) if not flag: return project_kind # 获取所有模板名称 tmpl_category_name_map = {} for info in id_list: category_name = info["id"] tmpl_category_name_map[category_name] = info["category"] # 获取要删除的实例信息 inst_info = self.get_instance_info(ns_id_list, tmpl_category_name_map.keys()) # instance_version_ids = [val["info"].instance_id for key, val in inst_info.items()] with client.ContextActivityLogClient( project_id=project_id, user=request.user.username, resource_type="instance", resource=muster_name, resource_id=muster_id, extra=json.dumps({ "muster_id": muster_id, "show_version_name": show_version_name, "tmpl_category_name_map": tmpl_category_name_map, }), description=_("删除模板集实例"), ).log_delete(): resp = self.delete_handler(request, inst_info, project_id, project_kind) return resp
def create(self): """集群初始化流程 1. 申请集群ID 2. 创建set及module 3. 触发OPS api """ self.check_data() # 权限校验 self.check_cluster_perm() cluster_data = copy.deepcopy(self.data) cluster_data['master_ips'] = [{'inner_ip': ip} for ip in self.data['master_ips']] cluster_data["state"] = cluster_data["cluster_state"] # 添加类型参数 cluster_data["type"] = cluster_data["coes"] self.cluster_name = self.data['name'] # 创建set with client.ContextActivityLogClient( project_id=self.project_id, user=self.username, resource_type=ACTIVITY_RESOURCE_TYPE, resource=cluster_data['name'], ).log_add() as ual: resp = paas_cc.create_cluster(self.access_token, self.project_id, cluster_data) if resp.get('code') != ErrorCode.NoError or not resp.get('data'): raise error_codes.APIError(resp.get('message', _("创建集群失败"))) cluster_info = resp.get('data') # 现阶段平台侧不主动创建CMDB set&module,赋值为空列表 module_id_list = [] # 兼容不创建set后,提取出区域名称 self.area_name = self.area_info['name'] ual.update_log(resource_id=cluster_info.get("cluster_id")) log = self.create_cluster_via_bcs(cluster_info['cluster_id'], module_id_list) if not log.is_finished and log.is_polling: log.polling_task() # 注册集群信息到AUTH, 便于申请权限 self.register_cluster(cluster_info) return Response(cluster_info)
def delete_record(self, request, project_id, cluster_id, namespace, name): lb_queryset = self.get_lb_queryset(project_id, cluster_id, namespace, name) # 仅允许LB处于未部署状态时,才允许删除 if lb_queryset.first().status not in [ mesos_lb_constants.MESOS_LB_STATUS.NOT_DEPLOYED.value, mesos_lb_constants.MESOS_LB_STATUS.STOPPED.value, ]: raise ValidationError(_("LB必须处于未部署状态或停用状态,当前状态不允许更新")) # 删除记录 with activity_client.ContextActivityLogClient( project_id=project_id, user=request.user.username, resource_type="lb", resource=name, description=_("删除Mesos LB, 集群: {},命名空间: {},名称: {}").format( cluster_id, namespace, name), ).log_delete(): lb_queryset.delete() return Response()
def _activity_log( self, project_id: str, username: str, resource_name: str, description: str, activity_type: ActivityType, activity_status: ActivityStatus, ) -> None: """操作记录方法""" client = activity_client.ContextActivityLogClient( project_id=project_id, user=username, resource_type=ResourceType.Metric, resource=resource_name) # 根据不同的操作类型,使用不同的记录方法 log_func = { ActivityType.Add: client.log_add, ActivityType.Delete: client.log_delete, ActivityType.Retrieve: client.log_note, }[activity_type] log_func(activity_status=activity_status, description=description)
def reinstall_nodes(self, request, project_id, cluster_id): """当初始化失败时,允许用户批量重装 1. 检测节点必须为当前项目下的同一集群 2. 检测节点状态必须为初始化失败状态 3. 下发配置,并更改节点状态 """ # 校验集群的编辑权限 self.can_edit_cluster(request, project_id, cluster_id) # 获取集群下的节点 cluster_nodes = self.get_cluster_nodes(request, project_id, cluster_id) # 获取请求参数 slz = node_serializers.BatchReinstallNodesSLZ(data=request.data, context={'cluster_nodes': cluster_nodes}) slz.is_valid(raise_exception=True) req_data = slz.validated_data # 获取集群信息 cluster_info = self.get_cluster_info(request, project_id, cluster_id) # 获取节点IP node_id_ip_map = {id: cluster_nodes[id]['inner_ip'] for id in req_data['node_id_list']} # 下发继续初始化流程 node_ip_list = node_id_ip_map.values() node_ips = ','.join(node_ip_list) with client.ContextActivityLogClient( project_id=project_id, user=request.user.username, resource_type='node', resource=node_ips[:512], resource_id=','.join([str(id) for id in node_id_ip_map.keys()])[:256], description=_("集群: {}, batch reinstall nodes: {}").format(cluster_id, node_ips), ).log_modify(): # 更改节点状态为初始化中 self.update_nodes_in_cluster( request.user.token.access_token, project_id, cluster_id, node_ip_list, CommonStatus.Initializing ) # 下发流程,触发重试任务 node_client = node.BatchReinstallNodes(request, project_id, cluster_info, node_id_ip_map) node_client.reinstall() return response.Response()
def update(self, request, project_id, instance_id): """回滚上一版本,只有模板集实例化的才会 1. 判断当前实例允许回滚 2. 对应实例的配置 3. 下发更新操作 """ # 检查是否来源于模板集 self.from_template(instance_id) instance_detail = self.get_instance_info(instance_id).first() # 校验权限 self.can_use_instance(request, project_id, instance_detail.namespace) # 获取实例的config current_config = self.get_current_config(instance_detail) last_config = self.get_last_config(instance_detail) # 兼容annotation和label cluster_id = getitems( current_config, ['metadata', 'annotations', 'io.tencent.bcs.cluster'], '') if not cluster_id: cluster_id = getitems( current_config, ['metadata', 'labels', 'io.tencent.bcs.cluster'], '') namespace = getitems(current_config, ['metadata', 'namespace'], '') desc = _("集群:{}, 命名空间:{}, 应用:[{}] 回滚上一版本").format( cluster_id, namespace, instance_detail.name) # 下发配置 with client.ContextActivityLogClient( project_id=project_id, user=request.user.username, resource_type="instance", resource=instance_detail.name, resource_id=instance_id, description=desc, ).log_modify(): self.update_resource(request, project_id, cluster_id, namespace, last_config, instance_detail) return response.Response()
def delete_resource(self, request, project_id, cluster_id, namespace, name): username = request.user.username # 检查用户是否有命名空间的使用权限 namespace_id = app_utils.get_namespace_id( request.user.token.access_token, project_id, (cluster_id, namespace), cluster_id=cluster_id) app_utils.can_use_namespace(request, project_id, cluster_id, namespace) resp = self.delete_single_resource(request, project_id, cluster_id, namespace, namespace_id, name) # 添加操作审计 activity_client.ContextActivityLogClient( project_id=project_id, user=username, resource_type="instance", resource=name, resource_id=0, extra=json.dumps({}), description=self.desc.format(cluster_id=cluster_id, namespace=namespace, resource_name=self.category, name=name), ).log_modify(activity_status="succeed" if resp.get("code") == ErrorCode.NoError else "failed") # 已经删除的,需要将错误信息翻译一下 message = resp.get('message', '') is_delete_before = True if 'node does not exist' in message or 'not found' in message else False if is_delete_before: message = _("{}[命名空间:{}]已经被删除,请手动刷新数据").format(name, namespace) return Response({ "code": resp.get("code"), "message": message, "data": {} })
def stop(self, request, project_id, cluster_id, namespace, name): """LB停止服务 删除对应的deployment和service """ lb_queryset = self.get_lb_queryset(project_id, cluster_id, namespace, name) lb = lb_queryset.first() access_token = request.user.token.access_token # 创建lb,包含service和deployment with activity_client.ContextActivityLogClient( project_id=project_id, user=request.user.username, resource_type="lb", resource=name, description=_("启动Mesos LoadBalancer"), ).log_stop(): lb_utils.stop_mesos_lb(access_token, project_id, cluster_id, lb.namespace, lb.name) lb_queryset.update( status=mesos_lb_constants.MESOS_LB_STATUS.STOPPING.value) return Response()
def delete_single(self, request, data, project_id, muster_id, show_version_name, res_name, muster_name): """删除单个类型""" ns_id_list = data.get("namespace_list") category = data.get("category") if not (ns_id_list and category): raise error_codes.CheckFailed(_("参数不能为空!")) project_kind = request.project.kind category = to_bcs_res_name(project_kind, category) if category not in MODULE_DICT: raise error_codes.CheckFailed( f'category: {category} does not exist') # 获取要删除的实例的信息 inst_info = self.get_instance_info(ns_id_list, [res_name], category=category) # 获取项目信息 flag, project_kind = self.get_project_kind(request, project_id) if not flag: return project_kind with client.ContextActivityLogClient( project_id=project_id, user=request.user.username, resource_type="instance", resource=res_name, resource_id=res_name, extra=json.dumps({ "muster_id": muster_id, "show_version_name": show_version_name, "res_name": res_name, "category": category, }), description=_("删除模板集实例"), ).log_delete(): return self.delete_handler(request, inst_info, project_id, project_kind)
def destroy(self, request, project_id, pk): """删除nginx ingress 1. 标识LB配置 2. 删除节点标签nodetype 3. 删除helm记录 """ lb_conf = self.get_object() # 标识LB被删除 self.delete_lb_conf(lb_conf) # 删除节点标签 delete_node_id_list = [ node_id for node_id in json.loads(lb_conf.ip_info) ] self.delete_node_label(request, delete_node_id_list, project_id, lb_conf) # 删除helm release release = self.get_helm_release(lb_conf.cluster_id, K8S_LB_CHART_NAME, namespace_id=lb_conf.namespace_id, namespace=lb_conf.namespace) if not release: return Response() user_log = log_client.ContextActivityLogClient( project_id=project_id, user=request.user.username, resource_type='lb', resource="%s:%s" % (lb_conf.cluster_id, lb_conf.namespace_id), resource_id=pk, ) release.destroy(username=request.user.username, access_token=request.user.token.access_token) user_log.log_delete(activity_status="succeed") return Response()
def delete(self, request, project_id, cluster_id, namespace, name): namespace_id = self.get_namespace_id(request, project_id, cluster_id, namespace, name) # 校验查看权限 self.can_use(request, project_id, cluster_id, namespace_id) with log_client.ContextActivityLogClient( project_id=project_id, user=request.user.username, resource_type='ingress', resource=name, description=_("集群:{}, 删除mesos ingress:{}").format(cluster_id, name), ).log_delete(): client = mesos.MesosClient(request.user.token.access_token, project_id, cluster_id, env=None) client.delete_custom_resource(name, namespace) # 删除成功则更新记录 now_time = timezone.now() InstanceConfig.objects.filter(namespace=namespace_id, category='ingress', name=name).update( updator=request.user.username, updated=now_time, deleted_time=now_time, is_deleted=True, is_bcs_success=True, ) return response.Response()
def delete(self, request, project_id, clb_id): record = CloudLoadBlancer.objects.retrieve_record(clb_id) # 校验使用集群权限 clb_utils.can_use_cluster(request, project_id, record['cluster_id']) with client.ContextActivityLogClient( project_id=project_id, user=request.user.username, resource_type='lb', resource=record['resource_name'], description=_("集群:{}, 删除clb关联deployment:{}").format( record['cluster_id'], record['resource_name']), ).log_delete(): clb_utils.delete_mesos_deployment( request.user.token.access_token, project_id, record['cluster_id'], record['namespace'], record['resource_name'], ) # 更新状态 self.update_clb_status(clb_id, clb_constants.CLB_DELETED_STATUS) return response.Response()
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 update_resource(self, request, project_id, cluster_id, namespace, name): """更新""" if get_cluster_type(cluster_id) == ClusterType.SHARED: return Response({"code": 400, "message": _("无法操作共享集群资源")}) access_token = request.user.token.access_token if namespace in K8S_SYS_NAMESPACE: return Response({ "code": 400, "message": _("不允许操作系统命名空间[{}]").format(','.join(K8S_SYS_NAMESPACE)), "data": {} }) request_data = request.data or {} request_data['project_id'] = project_id # 验证请求参数 slz = self.slz(data=request.data) slz.is_valid(raise_exception=True) data = slz.data try: config = json.loads(data["config"]) except Exception: config = data["config"] namespace_id = data['namespace_id'] username = request.user.username # 检查是否有命名空间的使用权限 perm_ctx = NamespaceScopedPermCtx(username=username, project_id=project_id, cluster_id=cluster_id, name=namespace) NamespaceScopedPermission().can_use(perm_ctx) # 对配置文件做处理 gparams = { "access_token": access_token, "project_id": project_id, "username": username } generator = GENERATOR_DICT.get(self.cate)(0, namespace_id, **gparams) config = generator.handle_db_config(db_config=config) # 获取上下文信息 context = generator.context now_time = context.get('SYS_UPDATE_TIME') instance_id = data.get('instance_id', 0) context.update({ 'SYS_CREATOR': data.get('creator', ''), 'SYS_CREATE_TIME': data.get('create_time', ''), 'SYS_INSTANCE_ID': instance_id, }) # 生成配置文件 sys_config = copy.deepcopy(self.sys_config) resource_config = update_nested_dict(config, sys_config) resource_config = json.dumps(resource_config) try: config_profile = render_mako_context(resource_config, context) except Exception: logger.exception(u"配置文件变量替换出错\nconfig:%s\ncontext:%s" % (resource_config, context)) raise ValidationError(_("配置文件中有未替换的变量")) config_profile = generator.format_config_profile(config_profile) service_name = config.get('metadata', {}).get('name') _config_content = { 'name': service_name, 'config': json.loads(config_profile), 'context': context } # 更新db中的数据 config_objs = InstanceConfig.objects.filter( namespace=namespace_id, category=self.cate, name=service_name, ) if config_objs.exists(): config_objs.update( creator=username, updator=username, oper_type='update', updated=now_time, is_deleted=False, ) _instance_config = config_objs.first() else: _instance_config = InstanceConfig.objects.create( namespace=namespace_id, category=self.cate, name=service_name, config=config_profile, instance_id=instance_id, creator=username, updator=username, oper_type='update', updated=now_time, is_deleted=False, ) _config_content['instance_config_id'] = _instance_config.id configuration = {namespace_id: {self.cate: [_config_content]}} driver = get_scheduler_driver(access_token, project_id, configuration, request.project.kind) result = driver.instantiation(is_update=True) failed = [] if isinstance(result, dict): failed = result.get('failed') or [] # 添加操作审计 activity_client.ContextActivityLogClient( project_id=project_id, user=username, resource_type="instance", resource=service_name, resource_id=_instance_config.id, extra=json.dumps(configuration), description=_("更新{}[{}]命名空间[{}]").format(self.category, service_name, namespace), ).log_modify(activity_status="failed" if failed else "succeed") if failed: return Response({ "code": 400, "message": _("{}[{}]在命名空间[{}]更新失败,请联系集群管理员解决").format( self.category, service_name, namespace), "data": {}, }) return Response({"code": 0, "message": "OK", "data": {}})
def batch_delete_resource(self, request, project_id): """批量删除资源""" username = request.user.username slz = BatchResourceSLZ(data=request.data) slz.is_valid(raise_exception=True) data = slz.data['data'] namespace_list = [(ns['cluster_id'], ns.get('namespace')) for ns in data] namespace_list = set(namespace_list) # 检查用户是否有命名空间的使用权限 app_utils.can_use_namespaces(request, project_id, namespace_list) # namespace_dict format: {(cluster_id, ns_name): ns_id} namespace_dict = app_utils.get_ns_id_map( request.user.token.access_token, project_id) success_list = [] failed_list = [] for _d in data: cluster_id = _d.get('cluster_id') name = _d.get('name') namespace = _d.get('namespace') namespace_id = namespace_dict.get((cluster_id, namespace)) # 删除service resp = self.delete_single_resource(request, project_id, cluster_id, namespace, namespace_id, name) # 处理已经删除,但是storage上报数据延迟的问题 message = resp.get('message', '') is_delete_before = True if 'node does not exist' in message or 'not found' in message else False if resp.get("code") == ErrorCode.NoError: success_list.append({ 'name': name, 'desc': self.desc.format(cluster_id=cluster_id, namespace=namespace, resource_name=self.category, name=name), }) else: if is_delete_before: message = _('已经被删除,请手动刷新数据') desc = self.desc.format(cluster_id=cluster_id, namespace=namespace, resource_name=self.category, name=name) failed_list.append({ 'name': name, 'desc': f'{desc}, message: {message}', }) code = 0 message = '' # 添加操作审计 if success_list: name_list = [_s.get('name') for _s in success_list] desc_list = [_s.get('desc') for _s in success_list] message = _("以下{}删除成功:{}").format(self.category, ";".join(desc_list)) activity_client.ContextActivityLogClient( project_id=project_id, user=username, resource_type="instance", resource=';'.join(name_list), resource_id=0, extra=json.dumps({}), description=";".join(desc_list), ).log_modify(activity_status="succeed") if failed_list: name_list = [_s.get('name') for _s in failed_list] desc_list = [_s.get('desc') for _s in failed_list] code = 4004 message = _("以下{}删除失败:{}").format(self.category, ";".join(desc_list)) activity_client.ContextActivityLogClient( project_id=project_id, user=username, resource_type="instance", resource=';'.join(name_list), resource_id=0, extra=json.dumps({}), description=message, ).log_modify(activity_status="failed") return Response({"code": code, "message": message, "data": {}})
def update(self, request, project_id, pk): """ 更新LB配置,包含下面几种场景 1. 增加/减少LB协议类型 2. 增加/减少节点数量(标签+replica) """ serializer = UpdateK8SLoadBalancerSLZ(data=request.data) serializer.is_valid(raise_exception=True) data = serializer.data username = request.user.username data.update({"id": pk, "updator": username}) lb_conf = self.get_k8s_lb_info(data["id"]) del_labels_ip_list, add_labels_ip_list = self.get_ip_list( request, data, lb_conf) ctx_cluster = CtxCluster.create(token=request.user.token.access_token, id=lb_conf.cluster_id, project_id=project_id) client = LBController(ctx_cluster) # 删除节点配置 if del_labels_ip_list: client.delete_labels(del_labels_ip_list) # 添加节点配置 if add_labels_ip_list: client.add_labels(add_labels_ip_list) # 更新lb self.update_lb_conf(lb_conf, data["ip_info"], data["protocol_type"], username) release = self.get_helm_release(lb_conf.cluster_id, lb_conf.name, namespace_id=lb_conf.namespace_id) if not release: raise error_codes.ResNotFoundError(_("没有查询到对应的release信息")) data["namespace_id"] = lb_conf.namespace_id user_log = log_client.ContextActivityLogClient( project_id=project_id, user=request.user.username, resource_type='lb', resource="%s:%s" % (lb_conf.cluster_id, lb_conf.namespace_id), resource_id=pk, extra=json.dumps(data), ) # release 对应的版本为"(current-unchanged) v1.1.2" version = data["version"].split( constants.RELEASE_VERSION_PREFIX)[-1].strip() chart_version = self.get_chart_version(project_id, version) access_token = request.user.token.access_token sys_variables = self.collect_system_variable(access_token, project_id, data["namespace_id"]) updated_instance = release.upgrade_app( access_token=access_token, chart_version_id=chart_version.id, answers=[], customs=[], valuefile=data["values_content"], updator=username, sys_variables=sys_variables, ) if updated_instance.transitioning_result: user_log.log_modify(activity_status="succeed") return Response() user_log.log_modify(activity_status="failed") raise error_codes.APIError(updated_instance.transitioning_message)
def create(self, request, project_id): """针对nginx的实例化,主要有下面几步: 1. 存储用户设置的配置 2. 根据用户选择的节点打标签 3. 根据透露给用户的选择,渲染values.yaml文件 4. 实例化controller相关配置 """ slz = serializers.CreateK8SLoadBalancerSLZ(data=request.data) slz.is_valid(raise_exception=True) data = slz.validated_data data.update({ "project_id": project_id, "creator": request.user.username, "updator": request.user.username, "name": K8S_LB_CHART_NAME, "ip_info": json.dumps(data["ip_info"]), "ip_list": list(data["ip_info"].keys()), }) # 检查命名空间是否被占用 self.validate_lb(data["cluster_id"]) created_data = self.pre_create(request, data) ns_info = created_data["ns_info"] user_log = log_client.ContextActivityLogClient( project_id=project_id, user=request.user.username, resource_type='lb', resource="%s:%s" % (data["cluster_id"], data["namespace_id"]), extra=json.dumps(data), ) # 4. helm apply try: access_token = request.user.token.access_token sys_variables = self.collect_system_variable( access_token, project_id, ns_info["namespace_id"]) helm_app_info = App.objects.initialize_app( access_token=access_token, name=K8S_LB_CHART_NAME, project_id=project_id, cluster_id=data["cluster_id"], namespace_id=ns_info["namespace_id"], namespace=ns_info["name"], chart_version=self.get_chart_version(project_id, data["version"]), answers=[], customs=[], cmd_flags=[], valuefile=data["values_content"], creator=request.user.username, updator=request.user.username, sys_variables=sys_variables, ) except Exception as err: logger.exception('Create helm app error, detail: %s' % err) helm_app_info = None if helm_app_info: if helm_app_info.transitioning_result: user_log.log_add(activity_status="succeed") return Response() else: user_log.log_add(activity_status="failed") raise error_codes.APIError(helm_app_info.transitioning_message) else: # 5. 如果失败删除k8s lb实例 K8SLoadBlance.objects.filter(cluster_id=data["cluster_id"], namespace_id=data["namespace_id"], name=data["name"]).delete() user_log.log_add(activity_status="failed") raise error_codes.APIError(_("创建LB失败!"))
def update_services(self, request, project_id, cluster_id, namespace, name): """更新 service""" access_token = request.user.token.access_token flag, project_kind = self.get_project_kind(request, project_id) if not flag: return project_kind if project_kind == MESOS_VALUE: # mesos 相关数据 slz_class = ServiceCreateOrUpdateSLZ s_sys_con = SEVICE_SYS_CONFIG s_cate = 'service' else: if namespace in K8S_SYS_NAMESPACE: return Response( {"code": 400, "message": _("不允许操作系统命名空间[{}]").format(','.join(K8S_SYS_NAMESPACE)), "data": {}} ) # k8s 相关数据 slz_class = K8sServiceCreateOrUpdateSLZ s_sys_con = K8S_SEVICE_SYS_CONFIG s_cate = 'K8sService' request_data = request.data or {} request_data['version_id'] = request_data['version'] request_data['item_id'] = 0 request_data['project_id'] = project_id show_version_name = request_data.get('show_version_name', '') # 验证请求参数 slz = slz_class(data=request.data) slz.is_valid(raise_exception=True) data = slz.data namespace_id = data['namespace_id'] # 检查是否有命名空间的使用权限 perm = bcs_perm.Namespace(request, project_id, namespace_id) perm.can_use(raise_exception=True) config = json.loads(data['config']) # 获取关联的应用列表 version_id = data['version_id'] version_entity = VersionedEntity.objects.get(id=version_id) entity = version_entity.get_entity() # 实例化时后台需要做的处理 if project_kind == MESOS_VALUE: app_weight = json.loads(data['app_id']) apps = entity.get('application') if entity else None application_id_list = apps.split(',') if apps else [] app_id_list = app_weight.keys() service_app_list = Application.objects.filter(id__in=application_id_list, app_id__in=app_id_list) lb_name = data.get('lb_name', '') handel_service_db_config(config, service_app_list, app_weight, lb_name, version_id) else: logger.exception(f"deploy_tag_list {type(data['deploy_tag_list'])}") handel_k8s_service_db_config(config, data['deploy_tag_list'], version_id, is_upadte=True) resource_version = data['resource_version'] config['metadata']['resourceVersion'] = resource_version cluster_version = get_cluster_version(access_token, project_id, cluster_id) config = handle_k8s_api_version(config, cluster_id, cluster_version, 'Service') # 前端的缓存数据储存到备注中 config = handle_webcache_config(config) # 获取上下文信息 username = request.user.username now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') context = { 'SYS_CLUSTER_ID': cluster_id, 'SYS_NAMESPACE': namespace, 'SYS_VERSION_ID': version_id, 'SYS_PROJECT_ID': project_id, 'SYS_OPERATOR': username, 'SYS_TEMPLATE_ID': version_entity.template_id, 'SYS_VERSION': show_version_name, 'LABLE_VERSION': show_version_name, 'SYS_INSTANCE_ID': data['instance_id'], 'SYS_CREATOR': data.get('creator', ''), 'SYS_CREATE_TIME': data.get('create_time', ''), 'SYS_UPDATOR': username, 'SYS_UPDATE_TIME': now_time, } bcs_context = get_bcs_context(access_token, project_id) context.update(bcs_context) # 生成配置文件 sys_config = copy.deepcopy(s_sys_con) resource_config = update_nested_dict(config, sys_config) resource_config = json.dumps(resource_config) try: config_profile = render_mako_context(resource_config, context) except Exception: logger.exception(u"配置文件变量替换出错\nconfig:%s\ncontext:%s" % (resource_config, context)) raise ValidationError(_("配置文件中有未替换的变量")) service_name = config.get('metadata', {}).get('name') _config_content = {'name': service_name, 'config': json.loads(config_profile), 'context': context} # 更新 service config_objs = InstanceConfig.objects.filter( namespace=namespace_id, category=s_cate, name=service_name, ) if config_objs.exists(): config_objs.update( creator=username, updator=username, oper_type='update', updated=now_time, is_deleted=False, ) _instance_config = config_objs.first() else: _instance_config = InstanceConfig.objects.create( namespace=namespace_id, category=s_cate, name=service_name, config=config_profile, instance_id=data.get('instance_id', 0), creator=username, updator=username, oper_type='update', updated=now_time, is_deleted=False, ) _config_content['instance_config_id'] = _instance_config.id configuration = {namespace_id: {s_cate: [_config_content]}} driver = get_scheduler_driver(access_token, project_id, configuration, request.project.kind) result = driver.instantiation(is_update=True) failed = [] if isinstance(result, dict): failed = result.get('failed') or [] # 添加操作审计 activity_client.ContextActivityLogClient( project_id=project_id, user=username, resource_type="instance", resource=service_name, resource_id=_instance_config.id, extra=json.dumps(configuration), description=_("更新Service[{}]命名空间[{}]").format(service_name, namespace), ).log_modify(activity_status="failed" if failed else "succeed") if failed: return Response( { "code": 400, "message": _("Service[{}]在命名空间[{}]更新失败,请联系集群管理员解决").format(service_name, namespace), "data": {}, } ) return Response({"code": 0, "message": "OK", "data": {}})
def post(self, request, project_id): """实例化模板""" # 参数验证 self.project_id = project_id version_id = request.data.get('version_id') show_version_id = request.data.get('show_version_id') template, version_entity = validate_version_id( project_id, version_id, is_return_all=True, show_version_id=show_version_id ) # 验证用户是否有使用权限 perm = bcs_perm.Templates(request, project_id, template.id, template.name) perm.can_use(raise_exception=True) self.template_id = version_entity.template_id tem_instance_entity = version_entity.get_version_instance_resource_ids project_kind = request.project.kind self.slz = VersionInstanceCreateOrUpdateSLZ(data=request.data, context={'project_kind': project_kind}) self.slz.is_valid(raise_exception=True) slz_data = self.slz.data # 验证前端传过了的预览资源是否在该版本的资源 req_instance_entity = slz_data.get('instance_entity') or {} self.instance_entity = validate_instance_entity(req_instance_entity, tem_instance_entity) namespaces = slz_data['namespaces'] ns_list = namespaces.split(',') if namespaces else [] access_token = self.request.user.token.access_token username = self.request.user.username # 验证关联lb情况下,lb 是否都已经选中 service_id_list = self.instance_entity.get('service') or [] v_res, err_list, err_msg = validate_lb_info_by_version_id( access_token, project_id, version_entity, ns_list, slz_data.get('lb_info', {}), service_id_list ) if not v_res: return Response({"code": 400, "message": err_msg, "data": err_list}) # 判断 template 下 前台传过来的 namespace 是否已经实例化过 res, ns_name_list, namespace_dict = validate_ns_by_tempalte_id( self.template_id, ns_list, access_token, project_id, req_instance_entity ) if not res: return Response( { "code": 400, "message": _("以下命名空间已经实例化过,不能再实例化\n{}").format("\n".join(ns_name_list)), "data": ns_name_list, } ) slz_data['ns_list'] = ns_list slz_data['instance_entity'] = self.instance_entity slz_data['template_id'] = self.template_id slz_data['project_id'] = project_id slz_data['version_id'] = version_id slz_data['show_version_id'] = show_version_id result = handle_all_config(slz_data, access_token, username, project_kind=request.project.kind) instance_entity = slz_data.get("instance_entity") all_tmpl_name_dict = self.get_tmpl_name(instance_entity) # 添加操作记录 temp_name = version_entity.get_template_name() for i in result['success']: client.ContextActivityLogClient( project_id=project_id, user=username, resource_type="template", resource=temp_name, resource_id=self.template_id, extra=json.dumps(self.instance_entity), description=_("实例化模板集[{}]命名空间[{}]").format(temp_name, i['ns_name']), ).log_add(activity_status="succeed") failed_ns_name_list = [] failed_msg = [] is_show_failed_msg = False # 针对createError的触发后台任务轮训 if result.get('failed'): check_instance_status.delay( request.user.token.access_token, project_id, request.project.get("kind"), all_tmpl_name_dict, result['failed'], ) for i in result['failed']: if i['res_type']: description = _("实例化模板集[{}]命名空间[{}],在实例化{}时失败,错误消息:{}").format( temp_name, i['ns_name'], i['res_type'], i['err_msg'] ) failed_ns_name_list.append(_("{}(实例化{}时)").format(i['ns_name'], i['res_type'])) else: description = _("实例化模板集[{}]命名空间[{}]失败,错误消息:{}").format(temp_name, i['ns_name'], i['err_msg']) failed_ns_name_list.append(i['ns_name']) if i.get('show_err_msg'): failed_msg.append(i['err_msg']) is_show_failed_msg = True client.ContextActivityLogClient( project_id=project_id, user=username, resource_type="template", resource=temp_name, resource_id=self.template_id, extra=json.dumps(self.instance_entity), description=description, ).log_add(activity_status="failed") if is_show_failed_msg: msg = '\n'.join(failed_msg) else: msg = _("以下命名空间实例化失败,\n{},请联系集群管理员解决").format("\n".join(failed_ns_name_list)) if failed_ns_name_list: return Response({"code": 400, "message": msg, "data": failed_ns_name_list}) return Response( { "code": 0, "message": "OK", "data": { "version_id": version_id, "template_id": self.template_id, }, } )