def query_cluster_nodes(ctx_cluster: CtxCluster, exclude_master: bool = True) -> Dict: """查询节点数据 包含标签、污点、状态等供前端展示数据 """ # 获取集群中的节点列表 node_client = Node(ctx_cluster) try: cluster_node_list = node_client.list(is_format=False) except ApiException: # 查询集群内节点异常,返回空字典 return {} nodes = {} for node in cluster_node_list.items: labels = node.labels # 现阶段节点页面展示及操作,需要排除master if exclude_master and labels.get( node_constants.K8S_NODE_ROLE_MASTER) == "true": continue # 使用inner_ip作为key,主要是方便匹配及获取值 nodes[node.inner_ip] = { "inner_ip": node.inner_ip, "name": node.name, "status": node.node_status, "labels": labels, "taints": node.taints, "unschedulable": node.data.spec.unschedulable or False, } return nodes
def query_cluster_nodes(ctx_cluster: CtxCluster) -> Dict[str, Dict]: # 查询集群下node信息 client = Node(ctx_cluster) nodes = client.list() # 根据传入的inner_ip过滤节点信息 data = {} for node in nodes: node_data = node.data # 解析数据用于前端展示 metadata = node_data.get("metadata", {}) labels = metadata.get("labels", {}) # 过滤掉master if labels.get("node-role.kubernetes.io/master") == "true": continue taints = getitems(node_data, ["spec", "taints"], []) # 组装数据,用于展示 data[node.inner_ip] = { "inner_ip": node.inner_ip, "name": node.name, "labels": labels, "taints": taints, "status": get_node_status(getitems(node_data, ["status", "conditions"], [])), "unschedulable": getitems(node_data, ["spec", "unschedulable"], False), } return data
def query_taints(self, request, project_id, cluster_id): """查询node的污点""" params = self.params_validate(slz.QueryNodeListSLZ) node_client = Node(request.ctx_cluster) return Response( node_client.filter_nodes_field_data("taints", params["node_name_list"]))
class NodeRespBuilder: """构造节点 API 返回 TODO: 现阶段返回先方便前端处理,拆分后,调整返回为{manifest: xxx, manifest_ext: xxx} manifest中放置原始node数据, manifest_ext中存放处理的状态等数据 """ def __init__(self, ctx_cluster: CtxCluster): self.client = Node(ctx_cluster) def list_nodes(self) -> Dict: """查询节点列表""" nodes = self.client.list(is_format=False) return { "manifest": nodes.data.to_dict(), "manifest_ext": { node.metadata["uid"]: { "status": node.node_status, "labels": { key: "readonly" for key in filter_label_keys(node.labels.keys()) }, } for node in nodes.items }, } def query_labels(self, node_names: List[str]) -> Dict[str, Dict]: """查询节点标签 TODO: 这里是兼容处理,方便前端使用,后续前端直接通过列表获取数据 """ node_labels = self.client.filter_nodes_field_data( "labels", filter_node_names=node_names, default_data={}) return node_labels
class LBController: def __init__(self, ctx_cluster: CtxCluster): self.node_client = Node(ctx_cluster) def add_labels(self, ip_list: List[str]): """添加lb的标签""" node_label_list = self._construct_node_label_list(ip_list) self.node_client.set_labels_for_multi_nodes(node_label_list) def delete_labels(self, ip_list: List[str]): node_label_list = self._construct_node_label_list(ip_list, LBLabelOp.DELETE) self.node_client.set_labels_for_multi_nodes(node_label_list) def _construct_node_label_list(self, ip_list: List[str], op: str = LBLabelOp.ADD) -> List: """查询节点的标签""" node_list = self.node_client.list(is_format=False) # 获取节点标签 node_label_list = [] for node in node_list.items: if node.inner_ip not in ip_list: continue labels = self._construct_labels_by_op(node.labels, op) node_label_list.append({"node_name": node.name, "labels": labels}) return node_label_list def _construct_labels_by_op(self, labels: Dict, op: str = LBLabelOp.ADD) -> Dict: if op == LBLabelOp.ADD: labels.update(K8S_LB_LABEL) else: for key in K8S_LB_LABEL: labels[key] = None return labels
def set_taints(self, request, project_id, cluster_id): """设置污点""" params = self.params_validate(slz.NodeTaintListSLZ) node_client = Node(request.ctx_cluster) node_client.set_taints_for_multi_nodes(params["node_taint_list"]) # 获取节点名称,用于审计 node_names = get_nodes_repr([n["node_name"] for n in params["node_taint_list"]]) request.audit_ctx.update_fields(resource=node_names, extra=params, description=_("节点设置污点")) return Response()
def _get_cluster_masters(self) -> List[Dict]: """查询集群中的master ip和name""" node_client = Node(self.ctx_cluster) # NOTE: 返回节点出现异常,直接报错 cluster_nodes = node_client.list(is_format=False) # 过滤 master 信息 masters = [] for node in cluster_nodes.items: if not node.is_master(): return masters.append({"inner_ip": node.inner_ip, "host_name": node.name}) return masters
def query_cluster_nodes(ctx_cluster: CtxCluster, exclude_master: bool = True) -> Dict: """查询节点数据 包含标签、污点、状态等供前端展示数据 """ # 获取集群中的节点列表 # NOTE: 现阶段会有两个agent,新版agent上报集群信息到bcs api中,可能会有时延,导致bcs api侧找不到集群信息;处理方式: # 1. 初始化流程调整,创建集群时,注册一次集群信息 # 2. 应用侧,兼容处理异常 try: cluster_node_list = Node(ctx_cluster).list(is_format=False) except Exception as e: # 兼容处理现阶段kube-agent没有注册时,连接不上集群的异常 logger.error("query cluster nodes error, %s", e) # 查询集群内节点异常,返回空字典 return {} nodes = {} for node in cluster_node_list.items: labels = node.labels # 现阶段节点页面展示及操作,需要排除master if exclude_master and node.is_master(): continue # 使用inner_ip作为key,主要是方便匹配及获取值 nodes[node.inner_ip] = { "inner_ip": node.inner_ip, "name": node.name, "status": node.node_status, "labels": labels, "taints": node.taints, "unschedulable": node.data.spec.unschedulable or False, } return nodes
def set_schedule_status(self, request, project_id, cluster_id): """设置节点调度状态 通过传递状态, 设置节点的调度状态 """ params = self.params_validate(slz.NodeStatusSLZ) # NOTE: 如果状态为REMOVABLE,则期望的是停止调度状态, 以便于进一步操作 unschedulable = True if params["status"] == node_status.REMOVABLE else False client = Node(request.ctx_cluster) client.set_nodes_schedule_status(unschedulable, params["node_name_list"]) request.audit_ctx.update_fields( resource=get_nodes_repr(params["node_name_list"]), extra=params, description=_("节点停止调度") if unschedulable else _("节点允许调度"), ) return Response()
def create_and_delete_master(ctx_cluster): client = Node(ctx_cluster) client.update_or_create( body={ "apiVersion": "v1", "kind": "Node", "metadata": {"name": fake_node_name, "labels": {"node-role.kubernetes.io/master": "true"}}, "spec": {}, "status": { "addresses": [ {"address": fake_inner_ip, "type": "InternalIP"}, ], "conditions": [ { "lastHeartbeatTime": "2021-10-25T04:13:48Z", "lastTransitionTime": "2020-10-25T05:24:53Z", "message": "kubelet is posting ready status", "reason": "KubeletReady", "status": "True", "type": "Ready", } ], }, }, name=fake_node_name, ) yield client.delete_wait_finished(fake_node_name)
def set_labels(ctx_cluster: CtxCluster, label_list: List): """节点设置标签 ctx_cluster: 集群模型数据 taint_list: 节点的污点内容,格式: [{"node_name": "demo", "labels": {"key1": "value1", "key2": "value2"}] """ client = Node(ctx_cluster) # 下发的body格式: {"metadata": {"labels": {"demo": "demo"}}} tasks = [ functools.partial(client.patch, {"metadata": { "labels": l["labels"] }}, l["node_name"]) for l in label_list ] # 当有操作失败的,抛出异常 async_run(tasks)
def set_taints(ctx_cluster: CtxCluster, taint_list: List): """节点设置污点,因为可能有多个节点分别调用接口完成打污点,使用asyncio处理,减少耗时 ctx_cluster: 集群模型数据 taint_list: 节点的污点内容,格式: [{"node_name": "demo", "taints": [{"key": xxx, "value": xxx, "effect": xxx}]] """ client = Node(ctx_cluster) # 下发的body格式: {"spec": {"taints": [{"key": xxx, "value": xxx, "effect": xxx}]}} tasks = [ functools.partial(client.patch, {"spec": { "taints": t["taints"] }}, t["node_name"]) for t in taint_list ] # 当有操作失败的,抛出异常 async_run(tasks)
def get_cluster_nodes(access_token, project_id, cluster_id): """获取集群下的node信息 NOTE: 节点数据通过集群中获取,避免数据不一致 """ ctx_cluster = CtxCluster.create( id=cluster_id, project_id=project_id, token=access_token, ) try: cluster_nodes = Node(ctx_cluster).list(is_format=False) except Exception as e: logger.error("查询集群内节点数据异常, %s", e) return [] return [{ "inner_ip": node.inner_ip, "status": node.node_status } for node in cluster_nodes.items]
def set_labels(self, request, project_id, cluster_id): """设置节点标签""" params = self.params_validate(slz.NodeLabelListSLZ) node_client = Node(request.ctx_cluster) node_client.set_labels_for_multi_nodes(params["node_label_list"]) return Response()
def detail(self) -> Dict[str, Any]: # 通过集群获取 client = Node(self.ctx_cluster) node_data = client.get(self.name).get("data", {}) # 过滤需要的信息: 包含节点名称、IP、版本、OS、运行时、镜像、标签、污点、pods return self._detail(node_data)
def client(self, ctx_cluster): return Node(ctx_cluster)
def __init__(self, ctx_cluster: CtxCluster): self.node_client = Node(ctx_cluster)
def set_taints(self, request, project_id, cluster_id): """设置污点""" params = self.params_validate(slz.NodeTaintListSLZ) node_client = Node(request.ctx_cluster) node_client.set_taints_for_multi_nodes(params["node_taint_list"]) return Response()