Пример #1
0
 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
Пример #2
0
    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)
Пример #3
0
    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()
Пример #4
0
 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)
Пример #5
0
    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()
Пример #6
0
    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()
Пример #7
0
    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": {}
        })
Пример #8
0
    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()
Пример #9
0
    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)
Пример #10
0
    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()
Пример #11
0
    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()
Пример #12
0
    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()
Пример #13
0
    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({})
Пример #14
0
    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": {}})
Пример #15
0
    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": {}})
Пример #16
0
    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)
Пример #17
0
    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失败!"))
Пример #18
0
    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": {}})
Пример #19
0
    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,
                },
            }
        )