def resume_update_deployment(self, request, project_id, cluster_id, ns, deployment_name, kind=2, category=None): """重启更新 """ if kind == 2: client = MesosClient(request.user.token.access_token, project_id, cluster_id, None) resp = client.resume_update_deployment(ns, deployment_name) else: client = K8SClient(request.user.token.access_token, project_id, cluster_id, None) curr_func = getattr(client, FUNC_MAP[category] % "patch") params = {"spec": {"paused": False}} resp = curr_func(ns, deployment_name, params) if resp.get("code") != ErrorCode.NoError: return APIResponse({ "code": resp.get("code", DEFAULT_ERROR_CODE), "message": resp.get("message", _("请求出现异常!")) }) return APIResponse({"message": _("重启更新成功!")})
def rescheduler_taskgroup(self, request, project_id, cluster_id, ns, instance_name, taskgroup_name, kind=2): """重启更新 """ if kind == 2: client = MesosClient(request.user.token.access_token, project_id, cluster_id, None) resp = client.rescheduler_mesos_taskgroup(ns, instance_name, taskgroup_name) else: client = K8SClient(request.user.token.access_token, project_id, cluster_id, None) resp = client.delete_pod(ns, taskgroup_name) if resp.get("code") != ErrorCode.NoError: return APIResponse({ "code": resp.get("code", DEFAULT_ERROR_CODE), "message": resp.get("message", _("请求出现异常!")) }) return APIResponse({"message": _("重新调度成功!")})
def status(self, request, cc_app_id, project_id, instance_id): self.get_data(request) self.init_handler(request, cc_app_id, project_id) inst_info = self.get_instance_info(instance_id)[0] self.category = inst_info.category conf = self.get_common_instance_conf(inst_info) self.get_config_detail(conf) client = MesosClient(self.data["access_token"], project_id, self.cluster_id, None) resp = client.get_command_status(self.category, self.namespace, self.name, {"id": self.data["task_id"]}) if resp.get("code") != ErrorCode.NoError: raise error_codes.APIError.f(resp.get("message")) resp_data = resp.get("data") or {} if not resp_data: raise error_codes.CheckFailed.f("请求BCS接口返回为空,请联系管理员处理") # 由于流水线现在仅维护,不调整逻辑,只能中间层兼容去掉返回中的多余字段 cmd_target_ref = getitems(resp_data, ["spec", "commandTargetRef"], default={}) if cmd_target_ref: cmd_target_ref.pop("image", None) resp_data["spec"]["commandTargetRef"] = cmd_target_ref return Response(resp_data)
def get_metric_instances(access_token: str, project_id: str, metric_name: str, env: str, cluster_id_list: list, ns_dict: dict, instance_info) -> list: metric_instance_list = [] stag = settings.BCS_API_ENV[env] client = MesosClient(access_token=access_token, project_id=project_id, cluster_id=None, env=stag) try: metric_data = client.get_metrics(metric_name, cluster_id_list).get('data') or [] except Exception as e: logger.exception("get_metrics error: %s", e) metric_data = [] for _m in metric_data: namespace = _m.get('namespace') selector = _m.get('selector') s_key = f'{LABLE_METRIC_SELECTOR_LABEL}.{metric_name}' if s_key not in selector: continue selector_str = f'"{s_key}": "{metric_name}"' # 查询跟metric相关的应用 ns_id = ns_dict.get(namespace) ins_list = instance_info.filter( namespace=ns_id, config__contains=selector_str).values_list('name', 'category') for _ins in ins_list: metric_instance_list.append({ 'ns_id': ns_id, 'namespace': namespace, 'name': _ins[0], 'category': CATE_SHOW_NAME.get(_ins[1], _ins[1]) }) return metric_instance_list
def create_instance(self, request, project_id, cluster_id, ns, data, category="application", kind=2): """创建实例 """ if kind == 2: client = MesosClient(request.user.token.access_token, project_id, cluster_id, None) if category == "application": resp = client.create_application(ns, data) else: resp = client.create_deployment(ns, data) else: client = K8SClient(request.user.token.access_token, project_id, cluster_id, None) curr_func = getattr(client, FUNC_MAP[category] % "create") resp = curr_func(ns, data) if resp.get("code") != ErrorCode.NoError: return APIResponse({ "code": resp.get("code") or DEFAULT_ERROR_CODE, "message": resp.get("message") }) return APIResponse({ "code": resp.get("code", DEFAULT_ERROR_CODE), "message": resp.get("message") })
def get_pod_or_taskgroup( self, request, project_id, cluster_id, field=None, app_name=None, taskgroup_name=None, ns_name=None, kind=2, category=None, ): """获取taskgroup或者pod""" if kind == 2: client = MesosClient(request.user.token.access_token, project_id, cluster_id, None) resp = client.get_mesos_app_taskgroup( field=field or "data.containerStatuses.containerID,namespace,data.rcname", app_name=app_name, taskgroup_name=taskgroup_name, namespace=ns_name, ) else: # 添加owner reference的kind属性,用来过滤pod关联的deployment/sts等类型 owner_ref_kind = OWENER_REFERENCE_MAP.get(category) if taskgroup_name: pod_name = taskgroup_name rs_name = None elif app_name: pod_name = None rs_name = app_name if category in ["deployment", "K8sDeployment"]: rs_name = self.get_k8s_rs_info(request, project_id, cluster_id, ns_name, app_name) if not rs_name: return True, [] else: pod_name = None rs_name = None resp = self.get_k8s_pod_info( request, project_id, cluster_id, ns_name, owner_ref_name=rs_name, field=field, pod_name=pod_name, owner_ref_kind=owner_ref_kind, ) if resp.get("code") != ErrorCode.NoError: return False, APIResponse({ "code": resp.get("code", DEFAULT_ERROR_CODE), "message": resp.get("message") }) return True, resp["data"]
def create_imagepullsecret(access_token, project_id, project_code, cluster_id, namespace): # get dept domain dept_domain = paas_cc.get_jfrog_domain(access_token, project_id, cluster_id) # 判断是否为研发仓库,正式环境分为:研发仓库、生产仓库,这2个仓库的账号要分开申请 is_bk_dept = True if dept_domain.startswith(settings.BK_JFROG_ACCOUNT_DOMAIN) else False dept_account = get_jfrog_account(access_token, project_code, project_id, is_bk_dept) # get user or pwd by dept account user = dept_account.get('user', '') pwd = dept_account.get('password', '') # compose config secret_config = { "kind": "secret", "metadata": { "name": MESOS_IMAGE_SECRET, "namespace": namespace }, "datas": { "user": { "content": base64.b64encode(user.encode(encoding="utf-8")).decode() }, "pwd": { "content": base64.b64encode(pwd.encode(encoding="utf-8")).decode() } }, "apiVersion": "v4" } client = MesosClient(access_token, project_id, cluster_id, env=None) resp = client.create_secret(namespace, secret_config) if (resp.get('code') != ErrorCode.NoError) and ('already exists' not in resp.get('message', '')): raise error_codes.APIError(f'create secret error, result.get("message")')
def update_instance(self, request, project_id, cluster_id, ns, instance_num, conf, kind=2, category=None, name=None): # noqa if kind == 2: client = MesosClient(request.user.token.access_token, project_id, cluster_id, None) resp = client.update_mesos_app_instance(ns, instance_num, conf) else: client = K8SClient(request.user.token.access_token, project_id, cluster_id, None) curr_func = getattr(client, FUNC_MAP[category] % "update") resp = curr_func(ns, name, conf) if resp.get("code") != ErrorCode.NoError: return False, APIResponse({ "code": resp.get("code", DEFAULT_ERROR_CODE), "message": resp.get("message", _("请求出现异常!")) }) return True, APIResponse({"message": _("更新成功!")})
def delete_metric(access_token, project_id, project_kind, metric_id, op_type=None, ns_id_list=[]): """后台下发删除metric任务""" active_metric = MetricConfig.get_active_metric(metric_id, ns_id_list=ns_id_list) if not active_metric: logger.info("delete_metric task: not have active metric for %s, %s, just ignore", project_id, metric_id) return # 判断项目的类型 cluster_type_dict = dict(MetricProjectKind._choices_labels.get_choices()) cluster_type = cluster_type_dict.get(int(project_kind)) active_metric_conf = [json.loads(i.config) for i in active_metric] for ns_cluster, metrics in groupby( sorted(active_metric_conf, key=lambda x: (x['namespace'], x['clusterID'])), key=lambda x: (x['namespace'], x['clusterID']), ): namespace = ns_cluster[0] cluster_id = ns_cluster[1] client = MesosClient(access_token, project_id, cluster_id, None) metric_name = [i['name'] for i in metrics] result = client.delete_metrics(namespace, metric_name, cluster_type) logger.info("delete_metric task result: %s", result) if op_type == 'pause': instance_status = InsState.METRIC_UPDATED.value else: instance_status = InsState.METRIC_DELETED.value MetricConfig.objects.filter(pk__in=[i.id for i in active_metric]).update(ins_state=instance_status) logger.info("delete_metric task: %s, %s, done", project_id, metric_id)
def get_mesos_app_deploy_with_post(self, request, project_id, cluster_id, instance_name=None, category="application", field=None, namespace=None): """查询mesos下application和deployment的状态 """ client = MesosClient(request.user.token.access_token, project_id, cluster_id, None) if category == "application": resp = client.get_application_with_post( name=instance_name, field=field or "data.metadata.name,data.metadata.namespace,data.status,data.message", # noqa namespace=namespace, ) else: resp = client.get_deployment_with_post( name=instance_name, field=field or "data.metadata.name,data.metadata.namespace,data.status,data.message", # noqa namespace=namespace, ) if resp.get("code") != ErrorCode.NoError: return False, APIResponse({ "code": resp.get("code") or DEFAULT_ERROR_CODE, "message": resp.get("message") }) return True, resp
def get_mesos_context(self, request, project_id, cluster_id): """获取mesos context """ client = MesosClient(request.user.token.access_token, project_id, cluster_id, None) slz = MesosWebConsoleSLZ(data=request.query_params, context={'client': client}) slz.is_valid(raise_exception=True) context = { 'short_container_id': slz.validated_data['container_id'][:12], 'project_kind': request.project.kind, 'server_address': client._bcs_server_host, 'user_pod_name': slz.validated_data['container_name'], } context.update(slz.validated_data) exec_id = client.get_container_exec_id(context['host_ip'], context['short_container_id']) if not exec_id: utils.activity_log( project_id, self.cluster_name, request.user.username, False, f'连接{context["user_pod_name"]}失败') raise error_codes.APIError(f'连接 {context["user_pod_name"]} 失败,请检查容器状态是否正常{settings.COMMON_EXCEPTION_MSG}') context['exec_id'] = exec_id context['mode'] = mesos.ContainerDirectClient.MODE client_context = { 'access_token': request.user.token.access_token, 'project_id': project_id, 'cluster_id': cluster_id, 'env': client._bcs_server_stag } context['client_context'] = client_context return context
def scale_instance(self, request, project_id, cluster_id, ns, app_name, instance_num, kind=2, category=None, data=None): # noqa """扩缩容 """ if kind == 2: client = MesosClient(request.user.token.access_token, project_id, cluster_id, None) resp = client.scale_mesos_app_instance(ns, app_name, instance_num) else: client = K8SClient(request.user.token.access_token, project_id, cluster_id, None) data["spec"]["replicas"] = int(instance_num) curr_func = getattr(client, FUNC_MAP[category] % "update") resp = curr_func(ns, app_name, data) if resp.get("code") != ErrorCode.NoError: return APIResponse({ "code": resp.get("code", DEFAULT_ERROR_CODE), "message": resp.get("message", _("请求出现异常!")) }) return APIResponse({"message": _("更新成功!")})
def update_deployment(self, request, project_id, cluster_id, ns, data, kind=2, category=None, app_name=None): """滚动升级 """ if kind == 2: client = MesosClient(request.user.token.access_token, project_id, cluster_id, None) resp = client.update_deployment(ns, data) else: client = K8SClient(request.user.token.access_token, project_id, cluster_id, None) curr_func = getattr(client, FUNC_MAP[category] % "update") resp = curr_func(ns, app_name, data) if resp.get("code") != ErrorCode.NoError: return APIResponse({ "code": resp.get("code", DEFAULT_ERROR_CODE), "message": resp.get("message", _("请求出现异常!")) }) return APIResponse({"message": _("更新成功!")})
def update_mesos_instance(self, access_token: str, project_id: str, cluster_id: str, ns: str, data: Dict, kind: str) -> Dict: """更新 mesos 应用""" client = MesosClient(access_token, project_id, cluster_id, None) if kind == MesosResourceName.deployment.value: return client.update_deployment(ns, data) return client.update_application(ns, data)
def get_mesos_application_deployment_config(self, access_token, project_id, cluster_id, name, namespace, category): """获取线上application和deployment配置""" client = MesosClient(access_token, project_id, cluster_id, None) if category == "application": return client.get_application_conf(namespace, name) return client.get_deployment_conf(namespace, name)
def create_mesos_deployment(config, access_token=None, project_id=None, cluster_id=None, client=None): if not client: client = MesosClient(access_token, project_id, cluster_id, None) return client.create_deployment(config["metadata"]["namespace"], config)
def set_metric(access_token, project_id, project_kind, metric_id, ns_id_list=[]): """后台下发metric任务 """ active_metric = MetricConfig.get_active_metric(metric_id, ns_id_list=ns_id_list) if not active_metric: logger.info( "set_metric task: not have active metric for %s, %s, just ignore", project_id, metric_id) return metric = Metric.objects.filter(pk=metric_id).first() if not metric: logger.info( "set_metric task: get metric failed for %s, %s, just ignore", project_id, metric_id) return _json = metric.to_json() if metric.http_method == 'GET': parameters = _json['http_body'] if not isinstance(parameters, dict): parameters = {} else: parameters = {'body': metric.http_body} # 判断项目的类型 cluster_type_dict = dict(MetricProjectKind._choices_labels.get_choices()) cluster_type = cluster_type_dict.get(int(project_kind)) active_metric_conf = [json.loads(i.config) for i in active_metric] for cluster_id, metrics in groupby(sorted(active_metric_conf, key=lambda x: x['clusterID']), key=lambda x: x['clusterID']): metrics = [i for i in metrics] for m in metrics: m['version'] = metric.version m['port'] = metric.port m['uri'] = metric.uri m['method'] = metric.http_method m['frequency'] = metric.frequency m['head'] = _json['http_headers'] m["parameters"] = parameters # 新增兼容 prometheus 采集的数据 m["metricType"] = metric.metric_type m["constLabels"] = metric.get_const_labels client = MesosClient(access_token, project_id, cluster_id, None) result = client.set_metrics(metrics, cluster_type) logger.info("set_metric task result: %s", result) MetricConfig.objects.filter(pk__in=[i.id for i in active_metric]).update( ins_state=InsState.METRIC_UPDATED.value) logger.info("set_metric task: %s, %s, done", project_id, metric_id)
def delete_mesos_service(namespace, name, access_token=None, project_id=None, cluster_id=None, client=None): if not client: client = MesosClient(access_token, project_id, cluster_id, None) return client.delete_service(namespace, name)
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 __init__(self, request, project_id, cluster_id): self.request = request self.project_id = project_id self.cluster_id = cluster_id self.client = MesosClient( self.request.user.token.access_token, self.project_id, self.cluster_id, None )
def get_instances(self, request, project_id, cluster_id, kind=2): """拉取项目下的所有Instance""" if kind == 2: client = MesosClient(request.user.token.access_token, project_id, cluster_id, None) resp = client.get_mesos_app_instances() else: resp = DEFAULT_RESPONSE if resp.get("code") != ErrorCode.NoError: return False, APIResponse({"code": resp.get("code", DEFAULT_ERROR_CODE), "message": resp.get("message")}) return True, resp["data"]
def request_signal(self, access_token, project_id, cluster_id, namespace, name, data): client = MesosClient(access_token, project_id, cluster_id, None) resp = client.send_application_signal(namespace, name, data) error_message_list = [] data = resp.get("data") or [] if data: error_message_list = [ "taskgroup id:%s, status:%s" % (info.get("ID"), info.get("Status")) for info in data ] return error_message_list
def cancel_update_deployment(self, request, project_id, cluster_id, ns, deployment_name, kind=2): """取消更新""" if kind == 2: client = MesosClient(request.user.token.access_token, project_id, cluster_id, None) resp = client.cancel_update_deployment(ns, deployment_name) else: resp = DEFAULT_RESPONSE if resp.get("code") != ErrorCode.NoError: return APIResponse( {"code": resp.get("code", DEFAULT_ERROR_CODE), "message": resp.get("message", _("请求出现异常!"))} ) return APIResponse({"message": _("取消更新成功!")})
def delete_mesos_deployment(namespace, name, access_token=None, project_id=None, cluster_id=None, client=None): if not client: client = MesosClient(access_token, project_id, cluster_id, None) resp = client.delete_deployment(namespace, name) if resp.get("code") != ErrorCode.NoError and "not exist" not in resp.get( "message"): raise error_codes.APIError( _("删除Mesos deployment失败,{}").format(resp.get("message")))
def delete(access_token, project_id, cluster_id, ns_name): # 删除平台创建的secret,用于拉取image # TODO: 后续多次使用时,可以放置到resources中 client = MesosClient(access_token, project_id, cluster_id, env=None) # 兼容ZK和etcd存储的不同的secret名称 for secret_name in [MESOS_IMAGE_SECRET, OLD_MESOS_IMAGE_SECRET]: resp = client.delete_secret(ns_name, secret_name) if resp.get("code") == ErrorCode.NoError: continue msg = resp.get("message") or "" # TODO: 现阶段只能通过message判断secret不存在,并且忽略不存在的情况 if ("not found" in msg) or ("not exist" in msg): continue raise error_codes.APIError(_("删除secret异常,{}").format(msg))
def delete_command(self, request, cc_app_id, project_id, instance_id): self.get_data(request) self.init_handler(request, cc_app_id, project_id) inst_info = self.get_instance_info(instance_id)[0] self.category = inst_info.category conf = self.get_common_instance_conf(inst_info) self.get_config_detail(conf) client = MesosClient(self.data["access_token"], project_id, self.cluster_id, None) resp = client.delete_command(self.category, self.namespace, self.name, {"id": self.data["task_id"]}) if resp.get("code") != ErrorCode.NoError: raise error_codes.APIError.f(resp.get("message")) return Response()
def _get_resources(self, request, project_id, namespace_id): access_token = request.user.token.access_token # 查询namespace ns_info = get_namespace_by_id(access_token, project_id, namespace_id) client = MesosClient(access_token, project_id, ns_info["cluster_id"], env=None) # 根据类型查询资源,如果有接口调用失败,先忽略 res_names = {} ns_name = ns_info["name"] # 请求bcs api,获取数据 deployment_resp = client.get_deployment(namespace=ns_name) application_resp = client.get_mesos_app_instances(namespace=ns_name) configmap_resp = client.get_configmaps(params={"namespace": ns_name}) service_resp = client.get_services(params={"namespace": ns_name}) secret_resp = client.get_secrets(params={"namespace": ns_name}) res_data = { MesosResourceName.deployment.value: deployment_resp["data"], MesosResourceName.application.value: application_resp["data"], MesosResourceName.configmap.value: configmap_resp["data"], MesosResourceName.service.value: service_resp["data"], MesosResourceName.secret.value: secret_resp["data"], } res_names = self._compose_res_names(res_data) return res_names
def init_mesos_ns_by_bcs(self, access_token, project_id, project_code, cluster_id, ns_name): """新建包含仓库账号信息的sercret配置文件并下发""" # 获取镜像仓库地址 jfrog_domain = paas_cc.get_jfrog_domain(access_token, project_id, cluster_id) # 按项目申请仓库的账号信息 # 判断是否为研发仓库,正式环境分为:研发仓库、生产仓库,这2个仓库的账号要分开申请 if jfrog_domain.startswith(settings.BK_JFROG_ACCOUNT_DOMAIN): is_bk_jfrog = True else: is_bk_jfrog = False jfrog_account = get_jfrog_account(access_token, project_code, project_id, is_bk_jfrog) _user = jfrog_account.get('user', '') _pwd = jfrog_account.get('password', '') jfrog_config = { "kind": "secret", "metadata": {"name": MESOS_IMAGE_SECRET, "namespace": ns_name}, "datas": { "user": {"content": base64.b64encode(_user.encode(encoding="utf-8")).decode()}, "pwd": {"content": base64.b64encode(_pwd.encode(encoding="utf-8")).decode()}, }, "apiVersion": "v4", } # 下发secret配置文件 client = MesosClient(access_token, project_id, cluster_id, env=None) result = client.create_secret(ns_name, jfrog_config) if result.get('code') != 0: client.delete_secret(ns_name, MESOS_IMAGE_SECRET) raise error_codes.ComponentError.f(_("创建registry secret失败,{}").format(result.get('message')))
def get_mesos_application_deploy_status( self, request, project_id, cluster_id, instance_name, category="application", field=None, namespace=None): """查询mesos下application和deployment的状态 """ client = MesosClient( request.user.token.access_token, project_id, cluster_id, None ) if category == "application": resp = retry_requests( client.get_mesos_app_instances, data={ "app_name": instance_name, "field": field or "data.metadata.name,data.metadata.namespace,data.status,data.message", "namespace": namespace, } ) else: resp = retry_requests( client.get_deployment, data={ "name": instance_name, "field": field or "data.metadata.name,data.metadata.namespace,data.status,data.message", "namespace": namespace, } ) if resp.get("code") != ErrorCode.NoError: return False, APIResponse({ "code": resp.get("code") or DEFAULT_ERROR_CODE, "message": resp.get("message") }) return True, resp
def get_application(namespace, names, access_token=None, project_id=None, cluster_id=None, client=None): if not client: client = MesosClient(access_token, project_id, cluster_id, None) resp = client.get_mesos_app_instances(app_name=names, namespace=namespace, field="data.status,data.message") if resp.get("code") != ErrorCode.NoError and "not exist" not in resp.get( "message"): raise error_codes.APIError( _("查询Mesos application异常,{}").format(resp.get("message"))) return resp["data"]