def fetch_host_ip_with_property_filter(self): """ 获取业务下过滤后的主机IP列表 """ for property_filter_type in self.PROPERTY_FILTER_TYPES: key = f"{property_filter_type}_property_filter" if not self.property_filters[key]["rules"]: self.property_filters.pop(key) host_info = cmdb.get_business_host_topo( self.username, self.bk_biz_id, self.bk_supplier_account, [ "bk_host_id", "bk_host_innerip", "bk_host_outerip", "bk_host_name", "bk_cloud_id" ], property_filters=self.property_filters, ) logger.info( "[fetch_host_info] cmdb.get_business_host_topo return: {host_info}" .format(host_info=host_info)) host_info = self.format_host_info(host_info) return { "result": True, "code": NO_ERROR, "data": host_info, "message": "" }
def test__get_with_ip_list(self): mock_batch_request = MagicMock( return_value=self.list_biz_hosts_topo_return) with patch("gcloud.utils.cmdb.batch_request", mock_batch_request): hosts_topo = get_business_host_topo(self.username, self.bk_biz_id, self.supplier_account, self.host_fields, self.ip_list) self.assertEqual(hosts_topo, self.get_business_host_topo_expect_return) mock_batch_request.assert_called_once_with( "list_biz_hosts_topo", { "bk_biz_id": self.bk_biz_id, "bk_supplier_account": self.supplier_account, "fields": self.host_fields, "host_property_filter": { "condition": "AND", "rules": [{ "field": "bk_host_innerip", "operator": "in", "value": self.ip_list }], }, }, )
def cc_get_inner_ip_by_module_id(username, biz_cc_id, module_id_list, supplier_account=0, host_fields=None): """根据模块列表过滤业务下主机 :param username: 请求用户名 :type username: str :param biz_cc_id: 业务 CC ID :type biz_cc_id: int :param module_id_list: 过滤模块 ID 列表 :type module_id_list: list[int] :param supplier_account: 开发商账号, defaults to 0 :type supplier_account: int, optional :param host_fields: 主机过滤字段, defaults to None :type host_fields: list[str], optional :return: [ { "host": { "bk_host_id": 4, "bk_host_innerip": "127.0.0.1", "bk_cloud_id": 0, ... }, "module": [ { "bk_module_id": 2, "bk_module_name": "module_name" }, ... ], "set": [ { "bk_set_name": "set_name", "bk_set_id": 1 }, ... ] } ] :rtype: list """ host_info_list = cmdb.get_business_host_topo(username, biz_cc_id, supplier_account, host_fields) # filter host filtered = [] target_modules = {int(mid) for mid in module_id_list} for host_info in host_info_list: parent_module_id_set = {m["bk_module_id"] for m in host_info["module"]} if target_modules.intersection(parent_module_id_set): filtered.append(host_info) return filtered
def test__get_without_ip_list(self): mock_batch_request = MagicMock( return_value=self.list_biz_hosts_topo_return) with patch("gcloud.utils.cmdb.batch_request", mock_batch_request): hosts_topo = get_business_host_topo(self.username, self.bk_biz_id, self.supplier_account, self.host_fields) self.assertEqual(hosts_topo, self.get_business_host_topo_expect_return) mock_batch_request.assert_called_once_with( "list_biz_hosts_topo", { "bk_biz_id": self.bk_biz_id, "bk_supplier_account": self.supplier_account, "fields": self.host_fields }, )
def cmdb_search_host(request, bk_biz_id, bk_supplier_account="", bk_supplier_id=0): """ @summary: 获取 CMDB 上业务的 IP 列表,以及 agent 状态等信息 @param request: @param bk_biz_id: 业务 CMDB ID @param bk_supplier_account: 业务开发商账号 @param bk_supplier_id: 业务开发商ID @params fields: list 查询字段,默认只返回 bk_host_innerip、bk_host_name、bk_host_id, 可以查询主机的任意字段,也可以查询 set、module、cloud、agent等信息 @return: """ default_host_fields = ["bk_host_id", "bk_host_name", "bk_cloud_id", "bk_host_innerip"] fields = set(default_host_fields + json.loads(request.GET.get("fields", "[]"))) client = get_client_by_user(request.user.username) topo_modules_id = set() # get filter module id if request.GET.get("topo", None): topo = json.loads(request.GET.get("topo")) topo_result = get_cmdb_topo_tree(request.user.username, bk_biz_id, bk_supplier_account) if not topo_result["result"]: return JsonResponse(topo_result) biz_topo_tree = topo_result["data"][0] topo_dict = {} for tp in topo: topo_dict.setdefault(tp["bk_obj_id"], []).append(int(tp["bk_inst_id"])) topo_objects = get_objects_of_topo_tree(biz_topo_tree, topo_dict) topo_modules = [] for obj in topo_objects: topo_modules += get_modules_of_bk_obj(obj) topo_modules_id = set(get_modules_id(topo_modules)) cloud_area_result = client.cc.search_cloud_area({}) if not cloud_area_result["result"]: message = handle_api_error(_("配置平台(CMDB)"), "cc.search_cloud_area", {}, cloud_area_result) result = {"result": False, "code": ERROR_CODES.API_GSE_ERROR, "message": message} return JsonResponse(result) raw_host_info_list = cmdb.get_business_host_topo(request.user.username, bk_biz_id, bk_supplier_account, fields) # map cloud_area_id to cloud_area cloud_area_dict = {} for cloud_area in cloud_area_result["data"]["info"]: cloud_area_dict[cloud_area["bk_cloud_id"]] = cloud_area # module filtered host_info_list = [] if topo_modules_id: for host_info in raw_host_info_list: parent_module_id_set = {m["bk_module_id"] for m in host_info["module"]} if topo_modules_id.intersection(parent_module_id_set): host_info_list.append(host_info) else: host_info_list = raw_host_info_list data = [] if host_info_list: for host in host_info_list: host_detail = {field: host["host"][field] for field in fields if field in host["host"]} host_detail["bk_host_innerip"] = format_sundry_ip(host_detail["bk_host_innerip"]) if "set" in fields: host_detail["set"] = host["set"] if "module" in fields: host_detail["module"] = host["module"] if "cloud" in fields or "agent" in fields: cloud_id = host["host"].get("bk_cloud_id") host_detail["cloud"] = [ {"id": str(cloud_id), "bk_inst_name": cloud_area_dict.get(cloud_id, {}).get("bk_cloud_name", "")} ] data.append(host_detail) if "agent" in fields: agent_kwargs = { "bk_biz_id": bk_biz_id, "bk_supplier_id": bk_supplier_id, "hosts": [ {"bk_cloud_id": host["bk_cloud_id"], "ip": host["bk_host_innerip"]} for host in data if host["bk_host_innerip"] != "" ], } agent_result = client.gse.get_agent_status(agent_kwargs) if not agent_result["result"]: message = handle_api_error(_("管控平台(GSE)"), "gse.get_agent_status", agent_kwargs, agent_result) result = {"result": False, "code": ERROR_CODES.API_GSE_ERROR, "message": message} return JsonResponse(result) agent_data = agent_result["data"] for host in data: # agent在线状态,0为不在线,1为在线,-1为未知 agent_info = agent_data.get( "{cloud}:{ip}".format(cloud=host["bk_cloud_id"], ip=host["bk_host_innerip"]), {} ) host["agent"] = agent_info.get("bk_agent_alive", -1) # search host lock status if request.GET.get("search_host_lock", None): bk_host_id_list = [host_detail["bk_host_id"] for host_detail in data] host_lock_status_result = client.cc.search_host_lock({"id_list": bk_host_id_list}) if not host_lock_status_result["result"]: message = handle_api_error(_("配置平台(CMDB)"), "cc.search_host_lock", {}, host_lock_status_result) result = {"result": False, "code": ERROR_CODES.API_GSE_ERROR, "message": message} return JsonResponse(result) host_lock_status_data = {int(k): v for k, v in host_lock_status_result["data"].items()} for host_detail in data: host_lock_status = host_lock_status_data.get(host_detail["bk_host_id"]) if host_lock_status is not None: host_detail["bk_host_lock_status"] = host_lock_status result = {"result": True, "code": NO_ERROR, "data": data} return JsonResponse(result)
def execute(self, data, parent_data): executor = parent_data.get_one_of_inputs("executor") client = get_client_by_user(executor) if parent_data.get_one_of_inputs("language"): setattr(client, "language", parent_data.get_one_of_inputs("language")) translation.activate(parent_data.get_one_of_inputs("language")) biz_cc_id = data.get_one_of_inputs("biz_cc_id", parent_data.inputs.biz_cc_id) supplier_account = supplier_account_for_business(biz_cc_id) cc_hosts = data.get_one_of_inputs("cc_host_replace_detail") copy_attrs = data.get_one_of_inputs("copy_attributes", True) # 查询主机可编辑属性 search_attr_kwargs = { "bk_obj_id": "host", "bk_supplier_account": supplier_account } search_attr_result = client.cc.search_object_attribute( search_attr_kwargs) if not search_attr_result["result"]: message = cc_handle_api_error("cc.search_object_attribute", search_attr_kwargs, search_attr_result) self.logger.error(message) data.outputs.ex_data = message return False editable_attrs = [] for item in search_attr_result["data"]: if item["editable"]: editable_attrs.append(item["bk_property_id"]) # 拉取所有主机信息 fault_replace_ip_map = {} for item in cc_hosts: fault_replace_ip_map["".join(get_ip_by_regex( item["cc_fault_ip"]))] = "".join( get_ip_by_regex(item["cc_new_ip"])) all_hosts = [] all_hosts.extend(list(fault_replace_ip_map.keys())) all_hosts.extend(list(fault_replace_ip_map.values())) host_attrs = editable_attrs + ["bk_host_innerip"] hosts_topo = cmdb.get_business_host_topo(executor, biz_cc_id, supplier_account, host_attrs, all_hosts) if not hosts_topo: data.outputs.ex_data = "fetch host topo for {} failed".format( all_hosts) return False # 只有复制故障机属性时才用到 batch_update_kwargs = { "bk_obj_id": "host", "bk_supplier_account": supplier_account, "update": [] } host_dict = { host_info["host"]["bk_host_innerip"]: host_info["host"] for host_info in hosts_topo } host_id_to_ip = { host_info["host"]["bk_host_id"]: host_info["host"]["bk_host_innerip"] for host_info in hosts_topo } fault_replace_id_map = {} for fault_ip, new_ip in list(fault_replace_ip_map.items()): fault_host = host_dict.get(fault_ip) new_host = host_dict.get(new_ip) if not fault_host: data.outputs.ex_data = _( "无法查询到 %s 机器信息,请确认该机器是否在当前业务下") % fault_ip return False if not new_host: data.outputs.ex_data = _( "无法查询到 %s 机器信息,请确认该机器是否在当前业务下") % new_ip return False if copy_attrs: update_item = { "properties": {}, "bk_host_id": new_host["bk_host_id"] } for attr in [ attr for attr in editable_attrs if attr in fault_host ]: update_item["properties"][attr] = fault_host[attr] batch_update_kwargs["update"].append(update_item) fault_replace_id_map[ fault_host["bk_host_id"]] = new_host["bk_host_id"] # 更新替换机信息 if copy_attrs: update_result = client.cc.batch_update_host(batch_update_kwargs) if not update_result["result"]: message = cc_handle_api_error("cc.batch_update_host", batch_update_kwargs, update_result) self.logger.error(message) data.outputs.ex_data = message return False # 将主机上交至故障机模块 fault_transfer_kwargs = { "bk_supplier_account": supplier_account, "bk_biz_id": biz_cc_id, "bk_host_id": list(fault_replace_id_map.keys()), } fault_transfer_result = client.cc.transfer_host_to_faultmodule( fault_transfer_kwargs) if not fault_transfer_result["result"]: message = cc_handle_api_error("cc.transfer_host_to_faultmodule", fault_transfer_kwargs, fault_transfer_result) self.logger.error(message) data.set_outputs("ex_data", message) return False # 转移主机模块 transfer_kwargs_list = [] for host_info in hosts_topo: new_host_id = fault_replace_id_map.get( host_info["host"]["bk_host_id"]) if new_host_id: transfer_kwargs_list.append({ "bk_biz_id": biz_cc_id, "bk_supplier_account": supplier_account, "bk_host_id": [new_host_id], "bk_module_id": [ module_info["bk_module_id"] for module_info in host_info["module"] ], "is_increment": True, }) success = [] for kwargs in transfer_kwargs_list: transfer_result = client.cc.transfer_host_module(kwargs) if not transfer_result["result"]: message = cc_handle_api_error("cc.transfer_host_module", kwargs, transfer_result) self.logger.error(message) data.outputs.ex_data = "{msg}\n{success}".format( msg=message, success=_("成功替换的机器: %s") % ",".join(success)) return False success.append(host_id_to_ip[kwargs["bk_host_id"][0]])
def cc_get_ips_info_by_str(username, biz_cc_id, ip_str, use_cache=True): """ @summary: 从ip_str中匹配出IP信息 @param username @param biz_cc_id @param ip_str @param use_cache(deprecated) @note: 需要兼容的ip_str格式有 1: IP,纯IP格式 2: 集群名称|模块名称|IP,集群名称|模块名称|IP 这种格式可以唯一定位到一 个IP(如果业务把相同IP放到同一模块,还是有问题) 3: 云区域ID:IP,云区域ID:IP 这种格式可以唯一定位到一个IP,主要是兼容Job组件 传参需要和获取Job作业模板步骤参数 @return: {'result': True or False, 'data': [{'InnerIP': ,'HostID': , 'Source': , 'SetID': , 'SetName': , 'ModuleID': , 'ModuleName': , 'Sets': , 'Module': },{}]} """ ip_input_list = get_ip_by_regex(ip_str) supplier_account = supplier_account_for_business(biz_cc_id) ip_list = cmdb.get_business_host_topo( username=username, bk_biz_id=biz_cc_id, supplier_account=supplier_account, host_fields=["bk_host_innerip", "bk_host_id", "bk_cloud_id"], ip_list=ip_input_list, ) ip_result = [] # 如果是格式2 集群名称|模块名称|IP,暂时不支持这种格式bk_host_innerip有多个值的情况 if set_module_ip_reg.match(ip_str): set_module_ip_list = [] for match in set_module_ip_reg.finditer(ip_str): set_module_ip_list.append(match.group()) for ip_info in ip_list: match = False for parent_set in ip_info["set"]: if match: break for parent_module in ip_info["module"]: if match: break topo_ip = "{set}|{module}|{ip}".format( set=parent_set["bk_set_name"], module=parent_module["bk_module_name"], ip=ip_info["host"].get("bk_host_innerip", ""), ) if topo_ip in set_module_ip_list: match = True ip_result.append({ "InnerIP": ip_info["host"].get("bk_host_innerip", ""), "HostID": ip_info["host"]["bk_host_id"], "Source": ip_info["host"].get("bk_cloud_id", -1), "SetID": parent_set["bk_set_id"], "SetName": parent_set["bk_set_name"], "ModuleID": parent_module["bk_module_id"], "ModuleName": parent_module["bk_module_name"], "Sets": ip_info["set"], "Modules": ip_info["module"], }) # 格式3 云区域ID:IP elif plat_ip_reg.match(ip_str): plat_ip = [] for match in plat_ip_reg.finditer(ip_str): plat_ip.append(match.group()) for ip_info in ip_list: valid_hosts = [ x for x in ip_info["host"].get("bk_host_innerip", "").split(",") if x and f'{ip_info["host"].get("bk_cloud_id", -1)}:{x}' in plat_ip ] if valid_hosts: ip_result.append({ "InnerIP": valid_hosts[0], # 即使多个host命中,也都是同一个主机id,这里以第一个合法host为标识 "HostID": ip_info["host"]["bk_host_id"], "Source": ip_info["host"].get("bk_cloud_id", -1), "Sets": ip_info["set"], "Modules": ip_info["module"], }) # 格式1 纯IP格式 else: ip = [] for match in ip_pattern.finditer(ip_str): ip.append(match.group()) proccessed = set() for ip_info in ip_list: valid_hosts = [ x for x in ip_info["host"].get("bk_host_innerip", "").split(",") if x and x in ip ] if valid_hosts and ip_info["host"]["bk_host_id"] not in proccessed: ip_result.append({ "InnerIP": valid_hosts[0], # 即使多个host命中,也都是同一个主机id,这里以第一个合法host为标识 "HostID": ip_info["host"]["bk_host_id"], "Source": ip_info["host"].get("bk_cloud_id", -1), "Sets": ip_info["set"], "Modules": ip_info["module"], }) proccessed.add(ip_info["host"]["bk_host_id"]) valid_ip = [ip_info["InnerIP"] for ip_info in ip_result] invalid_ip = list(set(ip_input_list) - set(valid_ip)) result = { "result": True, "ip_result": ip_result, "ip_count": len(ip_result), "invalid_ip": invalid_ip, } return result