def get_deployment_rs_name_list(cls, client, ns_name, inst_name, extra=None): """获取deployment关联的rs名称 主要是用作查询关联的pod信息 """ extra = extra or {constants.REFERENCE_RESOURCE_LABEL: inst_name} extra = utils.base64_encode_params(extra) rs_resp = client.get_rs({'extra': extra, 'namespace': ns_name, 'field': 'resourceName'}) if rs_resp.get('code') != ErrorCode.NoError: raise error_codes.APIError(rs_resp.get('message')) data = rs_resp.get('data') or [] return [info['resourceName'] for info in data if info.get('resourceName')]
def validate(self, data): context = get_mesos_context(self.context['client'], data['container_id']) if not context: raise error_codes.APIError( _("container_id不正确,请检查参数{}").format( settings.COMMON_EXCEPTION_MSG)) data.update(context) return data
def get_master_ips(self, request, project_id, cluster_id): """get master inner ip info""" master_resp = paas_cc.get_master_node_list( request.user.token.access_token, project_id, cluster_id) if master_resp.get("code") != ErrorCode.NoError: raise error_codes.APIError(master_resp.get("message")) data = master_resp.get("data") or {} master_ip_info = data.get("results") or [] return [ info["inner_ip"] for info in master_ip_info if info.get("inner_ip") ]
def get_zk_config(access_token, project_id, cluster_id, environment=None): if not environment: cluster = get_cluster(access_token, project_id, cluster_id) if cluster.get("code") != 0: raise error_codes.APIError(cluster.get("message")) environment = cluster["data"]["environment"] url = f"{BCS_CC_API_PRE_URL}/zk_config/" params = {"access_token": access_token, "environment": environment} zk_config = http_get(url, params=params, timeout=20) return zk_config
def mesos_container_num(self): client = bcs_mesos.MesosClient(self.access_token, self.project_id, self.cluster_id, None) host_pod_info = client.get_taskgroup( [self.node_ip], fields=','.join(['data.containerStatuses.containerID', 'data.hostIP']) ) if host_pod_info.get('code') != ErrorCode.NoError: raise error_codes.APIError(host_pod_info.get('message')) count = 0 for i in host_pod_info.get('data', []): count += len(i.get('data', {}).get('containerStatuses', [])) return count
def delete_via_bcs(self, request, project_id, cluster_id, kind_name, node_info): self.ip_list = list(node_info.keys()) params = { 'project_id': project_id, 'cluster_id': cluster_id, 'username': request.user.username, 'node_info': node_info, 'ip_list': self.ip_list, 'kind': request.project['kind'], 'kind_name': kind_name, 'cc_app_id': request.project['cc_app_id'], } log = NodeUpdateLog.objects.create( project_id=project_id, cluster_id=cluster_id, token=request.user.token.access_token, node_id=','.join(node_info.values()), params=json.dumps(params), operator=request.user.username, oper_type=NodeOperType.NodeRemove, is_polling=True, is_finished=False, ) config = self.get_request_config(op_type=constants.OpType.DELETE_NODE.value) control_ip = config.pop('control_ip', []) websvr = config.pop('websvr', []) try: task_info = ops.delete_cluster_node( request.user.token.access_token, project_id, kind_name, cluster_id, self.master_ip_list, self.ip_list, config, control_ip, request.project['cc_app_id'], request.user.username, websvr, ) except Exception as err: logger.exception('request bcs ops error, detail: %s', err) task_info = {'code': ErrorCode.UnknownError} if task_info.get('code') != ErrorCode.NoError: log.set_finish_polling_status(True, False, CommonStatus.RemoveFailed) # 更改节点状态 self.update_nodes(self.ip_list, status=CommonStatus.RemoveFailed) data = task_info.get('data') or {} task_id = data.get('task_id') if not task_id: raise error_codes.APIError(_("获取标准运维任务ID失败,返回任务为{},请联系管理员处理").format(task_id)) log.set_task_id(task_id) self.save_task_url(log, data) return log
def get_unit_info_by_name(cls, client, ns_name, pod_name, field): params = {'namespace': ns_name, 'name': pod_name} # 组装field field = field or constants.STORAGE_FIELD_LIST pod_resp = client.get_pod(extra=None, field=','.join(field), params=params) if pod_resp.get('code') != ErrorCode.NoError: raise error_codes.APIError(pod_resp.get('message')) return pod_resp.get('data')
def has_cluster(self, request, project_id): """判断项目下是否有集群 """ resp = paas_cc.get_all_clusters(request.user.token.access_token, project_id) if resp.get('code') != ErrorCode.NoError: raise error_codes.APIError(resp.get('message')) # 存在集群时,不允许修改 if resp.get('data', {}).get('count') > 0: return True return False
def update_nodes_in_cluster(self, access_token, project_id, cluster_id, node_ips, status): """update nodes status in the same cluster""" data = [{'inner_ip': ip, 'status': status} for ip in node_ips] resp = paas_cc.update_node_list(access_token, project_id, cluster_id, data=data) if resp.get('code') != ErrorCode.NoError: raise error_codes.APIError(resp.get('message')) return resp.get('data') or []
def get_zk_config(access_token, project_id, cluster_id, environment=None): if not environment: cluster = get_cluster(access_token, project_id, cluster_id) if cluster.get('code') != 0: raise error_codes.APIError(cluster.get('message')) environment = cluster['data']['environment'] url = f'{CC_HOST}/zk_config/' params = {'access_token': access_token, 'environment': environment} zk_config = http_get(url, params=params, timeout=20) return zk_config
def get_namespace(access_token, project_id, cluster_id): """ NOTE: mesos没有命名空间的概念,这样命名空间被应用等占用才会查询到命名空间 """ client = MesosClient(access_token, project_id, cluster_id, env=None) resp = client.get_used_namespace() if resp.get('code') != ErrorCode.NoError: raise error_codes.APIError( f'get namespace error, {resp.get("message")}') return resp.get('data') or []
def reschedule_pod(self, pod_info, raise_exception=True): resp = self.client.rescheduler_mesos_taskgroup( pod_info['namespace'], pod_info['app_name'], pod_info['taskgroup_name']) if resp.get('code') != ErrorCode.NoError: logger.error("request rescheduler taskgroup api error, %s", resp.get("message")) if raise_exception: raise error_codes.APIError(resp.get('message')) return resp
def info(self, request, area_id): """get the area info""" resp = paas_cc.get_area_info(request.user.token.access_token, area_id) if resp.get('code') != ErrorCode.NoError: raise error_codes.APIError( f'request bcs cc area info api error, {resp.get("message")}') data = resp.get('data') or {} data['configuration'] = json.loads(data.pop('configuration', '{}')) return Response(data)
def _delete_version(self, username: str, pwd: str, project_code: str, name: str, version: str): # 兼容harbor中chart仓库项目名称 project_name = DEFAULT_CHART_REPO_PROJECT_NAME or project_code try: client = bk_repo.BkRepoClient(username=username, password=pwd) client.delete_chart_version(project_name, project_code, name, version) except bk_repo.BkRepoDeleteVersionError as e: raise error_codes.APIError( f"delete chart: {name} version: {version} failed, {e}")
def check_port_associated_with_service(self, request, project_id, version_id, port_id): """检查指定的 port 是否被 service 关联""" ventity = self.get_versioned_entity(project_id, version_id) svc_id_list = ventity.get_resource_id_list(MesosResourceName.service.value) svc_qsets = models.Service.objects.filter(id__in=svc_id_list) for svc in svc_qsets: ports = svc.get_ports_config() for p in ports: if str(p.get('id')) == str(port_id): raise error_codes.APIError(_("端口在 Service[{}] 中已经被关联,不能删除").format(svc.name)) return Response({})
def update_cluster_nodes(self, node_ips, status=CommonStatus.Initializing): """更新阶段状态,并返回更新后的信息 """ data = [{'inner_ip': ip, 'status': status} for ip in node_ips] resp = paas_cc.update_node_list(self.access_token, self.project_id, self.cluster_id, data=data) if resp.get('code') != ErrorCode.NoError: raise error_codes.APIError(resp.get('message')) return resp.get('data') or []
def get(self, request, project_id): """check cluster name exist """ name = request.GET.get("name") cluster_resp = paas_cc.get_cluster_by_name( request.user.token.access_token, project_id, name) if cluster_resp.get('code') != ErrorCode.NoError: raise error_codes.APIError(cluster_resp.get('message')) data = cluster_resp.get('data') or {} return response.Response( {'is_exist': True if data.get('count') else False})
def delete(access_token, project_id, cluster_id, ns_name): client = K8SClient(access_token, project_id, cluster_id, env=None) resp = client.delete_namespace(ns_name) if resp.get('code') == ErrorCode.NoError: return if 'not found' in resp.get('message'): return # k8s 删除资源配额 quota_client = NamespaceQuota(access_token, project_id, cluster_id) quota_client.delete_namespace_quota(ns_name) raise error_codes.APIError(f'delete namespace error, name: {ns_name}, {resp.get("message")}')
def is_manager(self, request, project_id): """判断用户是否为项目管理员 """ resp = paas_auth.get_project_user(request.user.token.access_token, project_id, group_code='Manager') if resp.get('code') != ErrorCode.NoError: raise error_codes.APIError(resp.get('message')) data = resp.get('data') or [] if request.user.username in data: return True return False
def update_or_create_resource_quota(self, name, namespace, body): try: return self.update_resource_quota(name, namespace, body) except ApiException as e: logger.error("update resource quota error, %s", e) if e.status == 404: return self.create_resource_quota(namespace, body) raise except Exception as e: err_msg = f"request k8s resource quota api error, {e}" logger.error(err_msg) raise error_codes.APIError(err_msg)
def delete_namespaced_custom_object(self, namespace, name): try: return self.api_instance.delete_namespaced_custom_object( self.api_group, self.api_version, namespace, self.api_plural, name, body=client.V1DeleteOptions()) except Exception as e: raise error_codes.APIError( f"delete_namespaced_custom_object error: {e}")
def handle(*args, **kwargs): try: resp = func(*args, **kwargs) return resp except ComponentError as error: response = getattr(error, "response", None) if response and keyword and keyword in response.text: raise error_codes.APIError( _("当前集群API版本过低, 还不支持{},请{}").format( module, settings.COMMON_CUSTOMER_SUPPORT_MSG)) else: raise error
def validate(self, data): # 默认是sh命令 data.setdefault("command", "sh") # 优先使用container_id container_id = data.get("container_id") if container_id: # 有container_id才检查 context = get_k8s_context(self.context["client"], container_id) if not context: raise error_codes.APIError( _("container_id不正确或者容器不是运行状态,请检查参数{}").format(settings.COMMON_EXCEPTION_MSG) ) data.update(context) return data # 其他使用namespace, pod, container if not all([data.get("namespace"), data.get("pod_name"), data.get("container_name")]): raise error_codes.APIError(_("container_id或namespace/pod_name/container_name不能同时为空")) return data
def validate_ns_by_tempalte_id(template_id, ns_list, access_token, project_id, instance_entity={}): """实例化,参数 ns_list 不能与 db 中已经实例化过的 ns 重复 """ namespace = paas_cc.get_namespace_list(access_token, project_id, limit=ALL_LIMIT) namespace = namespace.get('data', {}).get('results') or [] namespace_dict = {str(i['id']): i['name'] for i in namespace} # hpa白名单控制 cluster_id_list = list(set([i['cluster_id'] for i in namespace])) if K8sResourceName.K8sHPA.value in instance_entity: if not enabled_hpa_feature(cluster_id_list): raise error_codes.APIError( f"当前实例化包含HPA资源,{settings.GRAYSCALE_FEATURE_MSG}") # 查看模板下已经实例化过的 ns exist_instance_id = VersionInstance.objects.filter( template_id=template_id, is_deleted=False).values_list('id', flat=True) filter_ns = InstanceConfig.objects.filter( instance_id__in=exist_instance_id, is_deleted=False, is_bcs_success=True).exclude( ins_state=InsState.NO_INS.value).values_list('namespace', flat=True) exist_ns = [] # 查询每类资源已经实例化的ns,求合集,这些已经实例化过的ns不能再被实例化 for cate in instance_entity: # HPA 编辑以模板集为准, 可以重复实例化 if cate == K8sResourceName.K8sHPA.value: continue cate_data = instance_entity[cate] cate_name_list = [i.get('name') for i in cate_data if i.get('name')] cate_ns = filter_ns.filter( category=cate, name__in=cate_name_list).values_list('namespace', flat=True) exist_ns.extend(list(cate_ns)) new_ns_list = [str(_i) for _i in ns_list] # 列表的交集 intersection_list = list(set(exist_ns).intersection(set(new_ns_list))) if not intersection_list: return True, [], namespace_dict ns_name_list = [] for _n_id in intersection_list: _ns_name = namespace_dict.get(_n_id, _n_id) ns_name_list.append(_ns_name) return False, ns_name_list, namespace_dict
def _validate_namespace_use_perm(self, request, project_id, namespace_list): """检查是否有命名空间的使用权限 """ namespace_map = self._get_namespace_map(project_id) for namespace in namespace_list: if namespace in self.NO_PERM_NS: raise error_codes.APIError(_("namespace operation is not allowed")) namespace_id = namespace_map.get(namespace) # 检查是否有命名空间的使用权限 perm = bcs_perm.Namespace(request, project_id, namespace_id) perm.can_use(raise_exception=True)
def _validate_namespace_use_perm(self, project_id: str, cluster_id: str, namespaces: List): """ 检查是否有命名空间的使用权限 """ namespace_map = self._get_namespace_map(project_id) for ns in namespaces: if ns in constants.SM_NO_PERM_NAMESPACE: raise error_codes.APIError(_('不允许操作命名空间 {}').format(ns)) namespace_id = namespace_map.get((cluster_id, ns)) # 检查是否有命名空间的使用权限 perm = bcs_perm.Namespace(self.request, project_id, namespace_id) perm.can_use(raise_exception=True)
def get_cluster_base_config(self, cluster_id, version, environment='prod'): params = {'ver_id': version, 'environment': environment, 'kind': self.kind_name} base_cluster_config = paas_cc.get_base_cluster_config(self.access_token, self.project_id, params) if base_cluster_config.get('code') != ErrorCode.NoError: # delete created cluster record self.delete_cluster(cluster_id) raise error_codes.APIError(base_cluster_config.get('message')) config = json.loads(base_cluster_config.get('data', {}).get('configure', '{}')) if not config: raise error_codes.CheckFailed(_("获取集群基本配置失败")) config['version'] = version self.config = config
def get_cluster_node(self, request, project_id, cluster_id): """get cluster node list """ cluster_node_resp = paas_cc.get_node_list( request.user.token.access_token, project_id, cluster_id, params={'limit': cluster_constants.DEFAULT_NODE_LIMIT}) if cluster_node_resp.get('code') != ErrorCode.NoError: raise error_codes.APIError(cluster_node_resp.get('message')) data = cluster_node_resp.get('data') or {} return data.get('results') or []
def validate(self, data): container_id = data.get('container_id') if not container_id: return data # 有container_id才检查 context = get_k8s_context(self.context['client'], container_id) if not context: raise error_codes.APIError(_("container_id不正确或者容器不是运行状态,请检查参数{}").format(settings.COMMON_EXCEPTION_MSG)) data.update(context) return data
def delete_cluster_via_bcs(self): # 获取集群下对应的master ip master_ip_list = self.get_cluster_master() params = { 'project_id': self.project_id, 'cluster_id': self.cluster_id, 'cc_app_id': self.cc_app_id, 'host_ips': master_ip_list, 'project_code': self.project_info['english_name'], 'username': self.username } # 创建记录 log = ClusterInstallLog.objects.create( project_id=self.project_id, cluster_id=self.cluster_id, token=self.access_token, status=CommonStatus.Removing, params=json.dumps(params), operator=self.username, oper_type=ClusterOperType.ClusterRemove, is_polling=True, ) task_info = ops.delete_cluster(self.access_token, self.project_id, self.kind_name, self.cluster_id, master_ip_list, self.control_ip, self.cc_app_id, self.username, self.websvr, self.config) if task_info.get('code') != ErrorCode.NoError: log.set_finish_polling_status(True, False, CommonStatus.RemoveFailed) self.update_cluster_status(status=CommonStatus.RemoveFailed) raise error_codes.APIError(task_info.get('message')) data = task_info.get('data') or {} task_id = data.get('task_id') if not task_id: raise error_codes.APIError( _("获取标准运维任务ID失败,返回任务ID为{},请联系管理员处理").format(task_id)) log.set_task_id(task_id) self.save_task_url(log, data) return log