예제 #1
0
파일: target.py 프로젝트: penglongli/bk-bcs
    def list(self, request, project_id, cluster_id):
        """按 instance_id 聚合的 targets 列表"""
        params = self.params_validate(FetchTargetsSLZ)
        result = get_targets(project_id, cluster_id).get('data') or []
        targets = self._filter_targets(result, params['show_discovered'])

        targets_dict = {}
        for instance_id, targets in itertools.groupby(
                sorted(targets, key=lambda x: x['instance_id']),
                key=lambda y: y['instance_id']):
            targets = list(targets)
            jobs = {t['job'] for t in targets}
            graph_url = self._gen_graph_url(project_id, cluster_id,
                                            jobs) if jobs else None
            targets_dict[instance_id] = {
                'targets': targets,
                'graph_url': graph_url,
                'total_count': len(targets),
                'health_count':
                len([t for t in targets if t['health'] == 'up']),
            }

        # 如果是共享集群,需要过滤出属于项目的命名空间的 Target
        # 过滤规则:targets_dict key: {namespace}/{name} 取 ns 进行检查
        if get_cluster_type(cluster_id) == ClusterType.SHARED:
            project_namespaces = get_shared_cluster_proj_namespaces(
                request.ctx_cluster, request.project.english_name)
            targets_dict = {
                inst_id: target_info
                for inst_id, target_info in targets_dict.items()
                if inst_id.split('/')[0] in project_namespaces
            }

        return Response(targets_dict)
예제 #2
0
    def create(self, request, project_id, is_validate_perm=True):
        """新建命名空间
        k8s 流程:新建namespace配置文件并下发 -> 新建包含仓库账号信息的sercret配置文件并下发 -> 在paas-cc上注册
        """
        serializer = slz.CreateNamespaceSLZ(data=request.data,
                                            context={
                                                'request': request,
                                                'project_id': project_id
                                            })
        serializer.is_valid(raise_exception=True)

        data = serializer.data

        # 判断权限
        cluster_id = data['cluster_id']
        perm_ctx = NamespacePermCtx(username=request.user.username,
                                    project_id=project_id,
                                    cluster_id=cluster_id)
        self.iam_perm.can_create(perm_ctx)

        if get_cluster_type(cluster_id) == ClusterType.SHARED:
            data[
                "name"] = f"{settings.SHARED_CLUSTER_NS_PREFIX}{request.project.project_code}-{data['name']}"

        request.audit_ctx.update_fields(
            resource=data['name'],
            description=_('集群: {}, 创建命名空间: 命名空间[{}]').format(
                cluster_id, data["name"]))
        result = self.create_flow(request, project_id, data)

        return response.Response(result)
예제 #3
0
파일: utils.py 프로젝트: penglongli/bk-bcs
def delete_hpa(request, project_id, cluster_id, ns_name, namespace_id, name):
    # 共享集群 HPA 不允许删除
    if get_cluster_type(cluster_id) == ClusterType.SHARED:
        raise DeleteResourceError(_("共享集群 HPA 不支持删除"))

    ctx_cluster = CtxCluster.create(token=request.user.token.access_token,
                                    project_id=project_id,
                                    id=cluster_id)
    client = hpa_client.HPA(ctx_cluster)
    try:
        client.delete_ignore_nonexistent(name=name, namespace=ns_name)
    except Exception as error:
        logger.error("delete hpa error, namespace: %s, name: %s, error: %s",
                     ns_name, name, error)
        raise DeleteResourceError(_("删除HPA资源失败"))

    # 删除成功则更新状态
    InstanceConfig.objects.filter(
        namespace=namespace_id,
        category=K8sResourceName.K8sHPA.value,
        name=name).update(
            updator=request.user.username,
            oper_type=application_constants.DELETE_INSTANCE,
            deleted_time=timezone.now(),
            is_deleted=True,
            is_bcs_success=True,
        )
예제 #4
0
    def list(self, request, project_id, cluster_id):
        """获取 ServiceMonitor 列表"""
        cluster_map = self._get_cluster_map(project_id)
        namespace_map = self._get_namespace_map(project_id)

        if cluster_id not in cluster_map:
            raise error_codes.APIError(_('集群 ID {} 不合法').format(cluster_id))

        client = K8SClient(request.user.token.access_token,
                           project_id,
                           cluster_id,
                           env=None)
        manifest = client.list_service_monitor()
        service_monitors = self._handle_items(cluster_id, cluster_map,
                                              namespace_map, manifest)

        # 共享集群需要再过滤下属于当前项目的命名空间
        if get_cluster_type(cluster_id) == ClusterType.SHARED:
            project_namespaces = get_shared_cluster_proj_namespaces(
                request.ctx_cluster, request.project.english_name)
            service_monitors = [
                sm for sm in service_monitors
                if sm['namespace'] in project_namespaces
            ]

        for m in service_monitors:
            m['is_system'] = m['namespace'] in constants.SM_NO_PERM_NAMESPACE
            m['iam_ns_id'] = calc_iam_ns_id(m['cluster_id'], m['namespace'])

        return PermsResponse(
            service_monitors,
            resource_request=NamespaceRequest(project_id=project_id,
                                              cluster_id=cluster_id),
        )
예제 #5
0
    def sync_namespace(self, request, project_id):
        """同步命名空间
        用来同步线上和本地存储的数据,并进行secret等的处理,保证数据一致
        """
        resp = paas_cc.get_all_clusters(request.user.token.access_token,
                                        project_id,
                                        desire_all_data=1)
        if resp.get('code') != ErrorCode.NoError:
            raise error_codes.APIError(
                f'get cluster error {resp.get("message")}')
        data = resp.get('data') or {}
        results = data.get('results')
        if not results:
            raise error_codes.ResNotFoundError(
                f'not found cluster in project: {project_id}')

        # 共享集群的命名空间只能通过产品创建,不允许同步
        cluster_id_list = [
            info['cluster_id'] for info in results
            if get_cluster_type(info['cluster_id']) != ClusterType.SHARED
        ]
        # 触发后台任务进行同步数据
        sync_ns_task.delay(
            request.user.token.access_token,
            project_id,
            request.project.project_code,
            request.project.kind,
            cluster_id_list,
            request.user.username,
        )
        request.audit_ctx.update_fields(description=_("同步项目下所有集群的命名空间"))
        return response.Response({'code': 0, 'message': 'task is running'})
예제 #6
0
파일: views.py 프로젝트: penglongli/bk-bcs
    def list(self, request, project_id, cluster_id):
        """获取指定资源某resource_version后变更记录"""
        params = self.params_validate(FetchResourceWatchResultSLZ, context={'ctx_cluster': request.ctx_cluster})

        res_kind, res_version, namespace = params['kind'], params['resource_version'], params.get('namespace')
        watch_kwargs = {
            'namespace': namespace,
            'resource_version': res_version,
            'timeout': DEFAULT_SUBSCRIBE_TIMEOUT,
        }
        if is_native_kind(res_kind):
            # 根据 Kind 获取对应的 K8S Resource Client 并初始化
            resource_client = get_native_kind_resource_client(res_kind)(request.ctx_cluster)
            # 对于命名空间,watch_kwargs 需要补充 cluster_type,project_code 以支持共享集群的需求
            if res_kind == K8sResourceKind.Namespace.value:
                watch_kwargs.update(
                    {'cluster_type': get_cluster_type(cluster_id), 'project_code': request.project.english_name}
                )
        else:
            # 自定义资源类型走特殊的获取 ResourceClient 逻辑 且 需要指定 Formatter
            resource_client = CustomObject(request.ctx_cluster, kind=res_kind, api_version=params['api_version'])
            watch_kwargs['formatter'] = CustomObjectCommonFormatter()

        try:
            events = resource_client.watch(**watch_kwargs)
        except ApiException as e:
            if e.status == K8S_API_GONE_STATUS_CODE:
                raise ResourceVersionExpired(_('ResourceVersion {} 已过期,请重新获取').format(res_version))
            raise

        # events 默认按时间排序,取最后一个 ResourceVersion 即为最新值
        latest_rv = getitems(events[-1], 'manifest.metadata.resourceVersion') if events else None
        response_data = {'events': events, 'latest_rv': latest_rv}
        return Response(response_data)
예제 #7
0
    def validate_cluster_id(self, cluster_id):
        access_token = self.context['request'].user.token.access_token
        project_id = self.context['project_id']
        data = app_utils.get_project_cluster_info(access_token, project_id)

        # 校验共享集群
        if get_cluster_type(cluster_id) == ClusterType.SHARED:
            return cluster_id
        for cluster in data['results']:
            if cluster_id == cluster['cluster_id']:
                return cluster_id
        raise ValidationError('not found cluster, please check cluster info')
예제 #8
0
    def has_permission(self, request, view):
        cluster_type = get_cluster_type(view.kwargs['cluster_id'])
        if cluster_type == ClusterType.SINGLE:
            return True

        # create 使用 request.data 中的 namespace
        if view.action == 'create':
            request_ns = request.data.get('namespace')
        # retrieve, update, destroy 方法使用路径参数中的 namespace
        else:
            request_ns = view.kwargs.get('namespace')

        return is_proj_ns_in_shared_cluster(request.ctx_cluster, request_ns,
                                            request.project.english_name)
예제 #9
0
    def has_permission(self, request, view):
        cluster_type = get_cluster_type(view.kwargs['cluster_id'])
        if cluster_type == ClusterType.SINGLE:
            return True

        # list, retrieve, update, destroy 方法使用路径参数中的 namespace,create 方法需要解析 request.data
        if view.action == 'create':
            request_ns = getitems(request.data, 'manifest.metadata.namespace')
        else:
            request_ns = view.kwargs.get(
                'namespace') or request.query_params.get('namespace')

        return is_proj_ns_in_shared_cluster(request.ctx_cluster, request_ns,
                                            request.project.english_name)
예제 #10
0
    def get_services_by_cluster_id(self, request, params, project_id,
                                   cluster_id):
        """查询services"""
        if get_cluster_type(cluster_id) == ClusterType.SHARED:
            return ErrorCode.NoError, []

        access_token = request.user.token.access_token
        client = k8s.K8SClient(access_token, project_id, cluster_id, env=None)
        resp = client.get_service(params)

        if resp.get("code") != ErrorCode.NoError:
            logger.error(u"bcs_api error: %s" % resp.get("message", ""))
            return resp.get("code", DEFAULT_ERROR_CODE), resp.get(
                "message", _("请求出现异常!"))

        return ErrorCode.NoError, resp.get("data", [])
예제 #11
0
    def get_ingress_by_cluser_id(self, request, params, project_id,
                                 cluster_id):
        """查询configmaps"""
        if get_cluster_type(cluster_id) == ClusterType.SHARED:
            return 0, []

        access_token = request.user.token.access_token
        client = k8s.K8SClient(access_token, project_id, cluster_id, env=None)
        resp = client.get_ingress(params)

        if resp.get("code") != ErrorCode.NoError:
            logger.error(u"bcs_api error: %s" % resp.get("message", ""))
            return resp.get("code", ErrorCode.UnknownError), resp.get(
                "message", _("请求出现异常!"))
        data = resp.get("data") or []
        return 0, data
예제 #12
0
파일: secret.py 프로젝트: penglongli/bk-bcs
    def get_secrets_by_cluster_id(self, request, params, project_id, cluster_id):
        """查询secrets"""
        if get_cluster_type(cluster_id) == ClusterType.SHARED:
            return 0, []

        search_fields = copy.deepcopy(DEFAULT_SEARCH_FIELDS)

        search_fields.append("data.data")
        params.update({"field": ",".join(search_fields)})
        client = k8s.K8SClient(request.user.token.access_token, project_id, cluster_id, env=None)
        resp = client.get_secret(params)

        if resp.get("code") != ErrorCode.NoError:
            logger.error(u"bcs_api error: %s" % resp.get("message", ""))
            return resp.get("code", ErrorCode.UnknownError), resp.get("message", _("请求出现异常!"))
        data = resp.get("data") or []
        return 0, data
예제 #13
0
    def get(self, request, project_id, ns_id):
        cluster_type, app_status, app_id, filter_ns_id, request_cluster_id = self.get_filter_params(
            request, project_id)
        if filter_ns_id and str(ns_id) != str(filter_ns_id):
            return APIResponse({"data": {}})
        # 获取项目下集群类型
        cluster_env_map = self.get_cluster_id_env(request, project_id)
        # 检查命名空间属于项目
        cluster_id, ns_name = self.check_ns_with_project(
            request, project_id, ns_id, cluster_type, cluster_env_map)
        # 共享集群不允许通过该接口查询应用
        if get_cluster_type(cluster_id) == ClusterType.SHARED:
            return APIResponse({"data": {}})

        inst_name = None
        if app_id:
            inst_name = self.get_inst_name(app_id)
        ns_name_id = self.get_namespace_name_id(request, project_id)
        # 根据类型进行过滤数据
        category = request.GET.get("category")
        if not category or category not in CATEGORY_MAP.keys():
            raise error_codes.CheckFailed(_("类型不正确"))
        client = k8s_views.GetInstances()
        ret_data = client.get(
            request,
            project_id,
            ns_id,
            category,
            inst_name,
            app_status,
            cluster_env_map,
            cluster_id,
            ns_name,
            ns_name_id,
        )

        iam_ns_id = calc_iam_ns_id(cluster_id, ns_name)
        for inst in ret_data["instance_list"]:
            inst['iam_ns_id'] = iam_ns_id

        return PermsResponse(
            ret_data,
            NamespaceRequest(project_id=project_id, cluster_id=cluster_id),
            resource_data={'iam_ns_id': iam_ns_id},
        )
예제 #14
0
    def list(self, request, project_id, cluster_id):
        """获取可选 Service 列表"""
        client = K8SClient(request.user.token.access_token,
                           project_id,
                           cluster_id,
                           env=None)
        resp = client.get_service({'env': 'k8s'})
        services = self._slim_down_service(resp.get('data') or [])

        # 共享集群需要再过滤下属于当前项目的命名空间
        if get_cluster_type(cluster_id) == ClusterType.SHARED:
            project_namespaces = get_shared_cluster_proj_namespaces(
                request.ctx_cluster, request.project.english_name)
            services = [
                svc for svc in services
                if svc['namespace'] in project_namespaces
            ]

        return Response(services)
예제 #15
0
 def get_clusters_without_ns(self, clusters, cluster_ids_with_ns):
     """获取不带有ns的集群"""
     clusters_without_ns = []
     for cluster_id in clusters:
         if cluster_id in cluster_ids_with_ns:
             continue
         cluster = clusters[cluster_id]
         item = {
             "environment_name":
             get_cluster_env_name(cluster["environment"]),
             "environment": cluster["environment"],
             "cluster_id": cluster_id,
             "name": cluster["name"],
             "results": [],
         }
         if get_cluster_type(cluster_id) == ClusterType.SHARED:
             item["is_shared"] = True
         clusters_without_ns.append(item)
     return clusters_without_ns
예제 #16
0
    def delete_single_service(self, request, project_id, project_kind,
                              cluster_id, namespace, namespace_id, name):
        if get_cluster_type(cluster_id) == ClusterType.SHARED:
            return {"code": 400, "message": _("无法操作共享集群资源")}

        username = request.user.username
        access_token = request.user.token.access_token

        if namespace in K8S_SYS_NAMESPACE:
            return {
                "code":
                400,
                "message":
                _("不允许操作系统命名空间[{}]").format(','.join(K8S_SYS_NAMESPACE)),
            }
        client = k8s.K8SClient(access_token, project_id, cluster_id, env=None)
        resp = client.delete_service(namespace, name)
        s_cate = 'K8sService'

        delete_svc_extended_routes(request, project_id, cluster_id, namespace,
                                   name)

        if resp.get("code") == ErrorCode.NoError:
            # 删除成功则更新状态
            now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            InstanceConfig.objects.filter(
                namespace=namespace_id,
                category=s_cate,
                name=name,
            ).update(
                creator=username,
                updator=username,
                oper_type=DELETE_INSTANCE,
                updated=now_time,
                deleted_time=now_time,
                is_deleted=True,
                is_bcs_success=True,
            )

        return {
            "code": resp.get("code"),
            "message": resp.get("message"),
        }
예제 #17
0
    def has_permission(self, request, view):
        project_id, cluster_id = view.kwargs['project_id'], view.kwargs[
            'cluster_id']
        cluster_type = get_cluster_type(cluster_id)
        if cluster_type == ClusterType.SINGLE:
            return True

        # 只有指定的数类资源可以执行订阅功能
        res_kind = request.query_params.get('kind')
        if res_kind not in SHARED_CLUSTER_SUBSCRIBEABLE_RESOURCE_KINDS:
            return False

        # 命名空间可以直接查询,但是不属于项目的需要被过滤掉
        if res_kind == K8sResourceKind.Namespace.value:
            return True

        # 可以执行订阅功能的资源,也需要检查命名空间是否属于指定的项目
        request_ns = request.query_params.get('namespace')
        return is_proj_ns_in_shared_cluster(request.ctx_cluster, request_ns,
                                            request.project.english_name)
예제 #18
0
파일: utils.py 프로젝트: penglongli/bk-bcs
def get_cluster_hpa_list(request, project_id, cluster_id, namespace=None):
    """获取基础hpa列表"""
    # 共享集群 HPA 不展示
    if get_cluster_type(cluster_id) == ClusterType.SHARED:
        return []

    project_code = request.project.english_name
    hpa_list = []

    try:
        ctx_cluster = CtxCluster.create(token=request.user.token.access_token,
                                        project_id=project_id,
                                        id=cluster_id)
        client = hpa_client.HPA(ctx_cluster)
        formatter = HPAFormatter(cluster_id, project_code)
        hpa_list = client.list(formatter=formatter, namespace=namespace)
    except Exception as error:
        logger.error("get hpa list error, %s", error)

    return hpa_list
예제 #19
0
    def has_permission(self, request, view):
        # 普通独立集群无需检查
        if get_cluster_type(view.kwargs['cluster_id']) == ClusterType.SINGLE:
            return True

        # 共享集群等暂时只允许查询部分自定义资源
        if view.kwargs['crd_name'] not in SHARED_CLUSTER_ENABLED_CRDS:
            return False

        # 检查命名空间是否属于项目且在共享集群中
        # list, retrieve, destroy 方法使用路径参数中的 namespace,create, update 方法需要解析 request.data
        if view.action == 'create':
            request_ns = getitems(request.data, 'manifest.metadata.namespace')
        elif view.action == 'update':
            request_ns = request.data.get('namespace')
        else:
            request_ns = request.query_params.get('namespace')

        return is_proj_ns_in_shared_cluster(request.ctx_cluster, request_ns,
                                            request.project.english_name)
예제 #20
0
    def get(self, request, project_id, cluster_id, namespace, name):
        """获取项目下所有的endpoints"""
        if get_cluster_type(cluster_id) == ClusterType.SHARED:
            return APIResponse({"code": 400, "message": _("无法查看共享集群资源")})

        params = {"name": name, "namespace": namespace}
        client = k8s.K8SClient(request.user.token.access_token,
                               project_id,
                               cluster_id,
                               env=None)
        resp = client.get_endpoints(params)

        if resp.get("code") != ErrorCode.NoError:
            return APIResponse({
                "code": resp.get("code", ErrorCode.UnknownError),
                "message": resp.get("message", _("请求出现异常!"))
            })

        return APIResponse({
            "code": ErrorCode.NoError,
            "data": resp.get("data"),
            "message": "ok"
        })
예제 #21
0
    def batch_delete(self, request, project_id, cluster_id):
        """批量删除 ServiceMonitor"""
        params = self.params_validate(ServiceMonitorBatchDeleteSLZ)
        svc_monitors = params['service_monitors']

        # 共享集群,过滤掉不属于项目的命名空间的
        if get_cluster_type(cluster_id) == ClusterType.SHARED:
            project_namespaces = get_shared_cluster_proj_namespaces(
                request.ctx_cluster, request.project.english_name)
            svc_monitors = [
                sm for sm in svc_monitors
                if sm['namespace'] in project_namespaces
            ]

        self._validate_namespace_use_perm(
            project_id, cluster_id, [sm['namespace'] for sm in svc_monitors])
        client = K8SClient(request.user.token.access_token,
                           project_id,
                           cluster_id,
                           env=None)
        for m in svc_monitors:
            self._single_service_monitor_operate_handler(
                client.delete_service_monitor, _('删除'), project_id,
                ActivityType.Delete, m['namespace'], m['name'])

        metrics_names = ','.join(
            [f"{sm['namespace']}/{sm['name']}" for sm in svc_monitors])
        message = _('删除 Metrics: {} 成功').format(metrics_names)
        self._activity_log(
            project_id,
            request.user.username,
            metrics_names,
            message,
            ActivityType.Delete,
            ActivityStatus.Succeed,
        )
        return Response({'successes': svc_monitors})
예제 #22
0
    def list(self, request, project_id):
        """命名空间列表
        权限控制: 必须有对应集群的使用权限
        """
        access_token = request.user.token.access_token
        valid_group_by = ['env_type', 'cluster_id', 'cluster_name']

        group_by = request.GET.get('group_by')
        cluster_id = request.GET.get('cluster_id')

        # 获取全部namespace,前台分页
        result = paas_cc.get_namespace_list(access_token,
                                            project_id,
                                            limit=LIMIT_FOR_ALL_DATA)
        if result.get('code') != 0:
            raise error_codes.APIError.f(result.get('message', ''))

        results = result["data"]["results"] or []
        # 针对k8s集群过滤掉平台命名空间
        results = self._ignore_ns_for_k8s(results)

        # 补充cluster_name字段
        cluster_list = get_clusters(access_token, project_id)
        # 添加共享集群
        cluster_list = append_shared_clusters(cluster_list)
        # TODO: 后续发现cluster_id不存在时,再处理
        cluster_dict = {i["cluster_id"]: i for i in (cluster_list or [])}

        # no_vars=1 不显示变量
        no_vars = request.GET.get('no_vars')
        if no_vars == '1':
            project_var = []
        else:
            project_var = NameSpaceVariable.get_project_ns_vars(project_id)

        for i in results:
            # ns_vars = NameSpaceVariable.get_ns_vars(i['id'], project_id)
            ns_id = i['id']
            ns_vars = []
            for _var in project_var:
                _ns_values = _var['ns_values']
                _ns_value_ids = _ns_values.keys()
                ns_vars.append({
                    'id':
                    _var['id'],
                    'key':
                    _var['key'],
                    'name':
                    _var['name'],
                    'value':
                    _ns_values.get(ns_id)
                    if ns_id in _ns_value_ids else _var['default_value'],
                })
            i['ns_vars'] = ns_vars

            if i['cluster_id'] in cluster_dict:
                i['cluster_name'] = cluster_dict[i['cluster_id']]['name']
                i['environment'] = cluster_dict[i['cluster_id']]['environment']
            else:
                i['cluster_name'] = i['cluster_id']
                i['environment'] = None

        if cluster_id:
            results = filter(lambda x: x['cluster_id'] == cluster_id, results)

        if group_by and group_by in valid_group_by:
            # 分组, 排序
            results = [{
                'name':
                k,
                'results':
                sorted(list(v), key=lambda x: x['id'], reverse=True)
            } for k, v in groupby(sorted(results, key=lambda x: x[group_by]),
                                  key=lambda x: x[group_by])]
            if group_by == 'env_type':
                ordering = [i.value for i in EnvType]
                results = sorted(results,
                                 key=lambda x: ordering.index(x['name']))
            else:
                results = sorted(results,
                                 key=lambda x: x['name'],
                                 reverse=True)
                # 过滤带有ns的集群id
                cluster_ids_with_ns = []
                # 按集群分组时,添加集群环境信息
                for r in results:
                    r_ns_list = r.get('results') or []
                    r_ns = r_ns_list[0] if r_ns_list else {}
                    r['environment'] = r_ns.get('environment', '')
                    r['environment_name'] = get_cluster_env_name(
                        r['environment'])
                    r["cluster_id"] = r_ns.get("cluster_id")
                    if get_cluster_type(r["cluster_id"]) == ClusterType.SHARED:
                        r["is_shared"] = True
                    cluster_ids_with_ns.append(r_ns.get("cluster_id"))

                # 添加无命名空间集群ID
                results.extend(
                    self.get_clusters_without_ns(cluster_dict,
                                                 cluster_ids_with_ns))
        else:
            results = sorted(results, key=lambda x: x['id'], reverse=True)

        with_perms = str2bool(request.query_params.get('with_perms', True))
        if not with_perms:
            return response.Response(results)

        namespace_list = []
        for namespace in results:
            namespace['iam_ns_id'] = calc_iam_ns_id(namespace['cluster_id'],
                                                    namespace['name'])
            namespace_list.append(namespace)

        return PermsResponse(
            namespace_list,
            NamespaceRequest(project_id=project_id, cluster_id=cluster_id))
예제 #23
0
    def get_service_info(self, request, project_id, cluster_id, namespace,
                         name):  # noqa
        """获取单个 service 的信息"""
        if get_cluster_type(cluster_id) == ClusterType.SHARED:
            return APIResponse({"code": 400, "message": _("无法查看共享集群资源")})

        access_token = request.user.token.access_token
        params = {
            "env": "k8s",
            "namespace": namespace,
            "name": name,
        }
        client = k8s.K8SClient(access_token, project_id, cluster_id, env=None)
        resp = client.get_service(params)
        template_cate = 'k8s'
        relate_app_cate = 'deployment'

        if resp.get("code") != ErrorCode.NoError:
            raise ComponentError(resp.get("message"))

        resp_data = resp.get("data", [])
        if not resp_data:
            return APIResponse({
                "code":
                400,
                "message":
                _("查询不到 Service[{}] 的信息").format(name)
            })
        s_data = resp_data[0].get('data', {})
        labels = s_data.get('metadata', {}).get('labels') or {}

        # 获取命名空间的id
        namespace_id = app_utils.get_namespace_id(access_token,
                                                  project_id,
                                                  (cluster_id, namespace),
                                                  cluster_id=cluster_id)

        instance_id = labels.get(LABLE_INSTANCE_ID)

        # 是否关联LB
        lb_balance = labels.get('BCSBALANCE')
        if lb_balance:
            s_data['isLinkLoadBalance'] = True
            s_data['metadata']['lb_labels'] = {'BCSBALANCE': lb_balance}
        else:
            s_data['isLinkLoadBalance'] = False
        lb_name = labels.get('BCSGROUP')

        # 获取模板集信息
        template_id = labels.get(LABLE_TEMPLATE_ID)
        try:
            lasetest_ver = ShowVersion.objects.filter(
                template_id=template_id).order_by('-updated').first()
            show_version_name = lasetest_ver.name
            version_id = lasetest_ver.real_version_id
            version_entity = VersionedEntity.objects.get(id=version_id)
        except Exception:
            return APIResponse({
                "code":
                400,
                "message":
                _("模板集[id:{}]没有可用的版本,无法更新service").format(template_id)
            })

        entity = version_entity.get_entity()

        # 获取更新人和创建人
        annotations = s_data.get('metadata', {}).get('annotations', {})
        creator = annotations.get(ANNOTATIONS_CREATOR, '')
        updator = annotations.get(ANNOTATIONS_UPDATOR, '')
        create_time = annotations.get(ANNOTATIONS_CREATE_TIME, '')
        update_time = annotations.get(ANNOTATIONS_UPDATE_TIME, '')

        # k8s 更新需要获取版本号
        resource_version = s_data.get('metadata',
                                      {}).get('resourceVersion') or ''

        web_cache = annotations.get(ANNOTATIONS_WEB_CACHE)
        if not web_cache:
            # 备注中无,则从模板中获取
            _services = entity.get('service') if entity else None
            _services_id_list = _services.split(',') if _services else []
            _s = Service.objects.filter(id__in=_services_id_list,
                                        name=name).first()
            try:
                web_cache = _s.get_config.get('webCache')
            except Exception:
                pass
        else:
            try:
                web_cache = json.loads(web_cache)
            except Exception:
                pass
        s_data['webCache'] = web_cache
        deploy_tag_list = web_cache.get('deploy_tag_list') or []

        app_weight = {}
        # 处理 k8s 中Service的关联数据
        if not deploy_tag_list:
            _servs = entity.get('K8sService') if entity else None
            _serv_id_list = _servs.split(',') if _servs else []
            _k8s_s = K8sService.objects.filter(id__in=_serv_id_list,
                                               name=name).first()
            if _k8s_s:
                deploy_tag_list = _k8s_s.get_deploy_tag_list()

        # 标签 和 备注 去除后台自动添加的
        or_annotations = s_data.get('metadata', {}).get('annotations', {})
        or_labels = s_data.get('metadata', {}).get('labels', {})
        if or_labels:
            pub_keys = PUBLIC_LABELS.keys()
            show_labels = {
                key: or_labels[key]
                for key in or_labels if key not in pub_keys
            }
            s_data['metadata']['labels'] = show_labels
        if or_annotations:
            pub_an_keys = PUBLIC_ANNOTATIONS.keys()
            show_annotations = {
                key: or_annotations[key]
                for key in or_annotations if key not in pub_an_keys
            }
            remove_key(show_annotations, ANNOTATIONS_WEB_CACHE)
            s_data['metadata']['annotations'] = show_annotations

        return APIResponse({
            "data": {
                'service': [{
                    'name': name,
                    'app_id': app_weight.keys(),
                    'app_weight': app_weight,
                    'deploy_tag_list': deploy_tag_list,
                    'config': s_data,
                    'version': version_id,
                    'lb_name': lb_name,
                    'instance_id': instance_id,
                    'namespace_id': namespace_id,
                    'cluster_id': cluster_id,
                    'namespace': namespace,
                    'creator': creator,
                    'updator': updator,
                    'create_time': create_time,
                    'update_time': update_time,
                    'show_version_name': show_version_name,
                    'resource_version': resource_version,
                    'template_id': template_id,
                    'template_cate': template_cate,
                    'relate_app_cate': relate_app_cate,
                }]
            }
        })
예제 #24
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": {}})
예제 #25
0
 def has_permission(self, request, view):
     cluster_id = view.kwargs.get('cluster_id') or request.query_params.get(
         'cluster_id')
     return get_cluster_type(cluster_id) != ClusterType.SHARED
예제 #26
0
파일: views.py 프로젝트: penglongli/bk-bcs
 def list(self, request, project_id, cluster_id):
     client = Namespace(request.ctx_cluster)
     response_data = ListApiRespBuilder(
         client, cluster_type=get_cluster_type(cluster_id), project_code=request.project.english_name
     ).build()
     return Response(response_data)
예제 #27
0
    def update_services(self, request, project_id, cluster_id, namespace,
                        name):
        """更新 service"""
        if get_cluster_type(cluster_id) == ClusterType.SHARED:
            return Response({"code": 400, "message": _("无法操作共享集群资源")})

        access_token = request.user.token.access_token
        flag, project_kind = self.get_project_kind(request, project_id)
        if not flag:
            return project_kind

        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_ctx = NamespaceScopedPermCtx(username=request.user.username,
                                          project_id=project_id,
                                          cluster_id=cluster_id,
                                          name=namespace)
        NamespaceScopedPermission().can_use(perm_ctx)

        config = json.loads(data['config'])
        #  获取关联的应用列表
        version_id = data['version_id']
        version_entity = VersionedEntity.objects.get(id=version_id)

        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": {}})