diff --git a/apps/backend/components/collections/agent_new/install.py b/apps/backend/components/collections/agent_new/install.py index 8052fe914..08c98d7c2 100644 --- a/apps/backend/components/collections/agent_new/install.py +++ b/apps/backend/components/collections/agent_new/install.py @@ -44,7 +44,7 @@ from apps.prometheus.helper import SetupObserve from apps.utils import concurrent, sync from apps.utils.exc import ExceptionHandler -from common.api import JobApi +from common.api import CCApi, JobApi from common.log import logger from pipeline.core.flow import Service, StaticIntervalGenerator @@ -829,7 +829,8 @@ def _schedule(self, data, parent_data, callback_data=None): left_scheduling_sub_inst_ids.append(result["sub_inst_id"]) # 按 CPU 架构对主机进行分组 bk_host_id = common_data.sub_inst_id__host_id_map.get(result["sub_inst_id"]) - cpu_arch__host_id_map[result["cpu_arch"]].append(bk_host_id) + if result["cpu_arch"]: + cpu_arch__host_id_map[result["cpu_arch"]].append(bk_host_id) # 记录不为空的 agent_id 和 bk_host_id 的对应关系 agent_id: str = result.get("agent_id") or "" agent_id = agent_id.split(":")[-1].strip() @@ -839,10 +840,13 @@ def _schedule(self, data, parent_data, callback_data=None): os_version = result.get("os_version", "") if os_version is not None: os_version__host_id_map[os_version].append(bk_host_id) - # 批量更新CPU架构 - for cpu_arch, bk_host_ids in cpu_arch__host_id_map.items(): - if cpu_arch: - models.Host.objects.filter(bk_host_id__in=bk_host_ids).update(cpu_arch=cpu_arch) + # 批量更新CPU架构并且上报至CMDB + hosts_info = [ + {"bk_host_id": value, "report_cpu_arch": key} + for key, values in cpu_arch__host_id_map.items() + for value in values + ] + self.update_db_and_report_cpu_arch(hosts_info, host_id__sub_inst_map) # 批量更新主机操作系统版本号 for os_version, bk_host_ids in os_version__host_id_map.items(): @@ -873,3 +877,45 @@ def _schedule(self, data, parent_data, callback_data=None): self.move_insts_to_failed(left_scheduling_sub_inst_ids, _("安装超时")) self.finish_schedule() data.outputs.polling_time = polling_time + POLLING_INTERVAL + + @controller.ConcurrentController( + data_list_name="hosts_info", + batch_call_func=concurrent.batch_call, + get_config_dict_func=core.get_config_dict, + get_config_dict_kwargs={"config_name": core.ServiceCCConfigName.HOST_WRITE.value}, + ) + @ExceptionHandler(exc_handler=core.default_sub_insts_task_exc_handler) + def update_db_and_report_cpu_arch( + self, hosts_info: List[Dict[str, any]], host_id__sub_inst_map: Dict[int, models.SubscriptionInstanceRecord] + ): + """ + :param hosts_info: 包含bk_host_id与cpu_arch字段的主机信息列表 + :param host_id__sub_inst_map:主机ID与订阅实例的映射 + return: + """ + update_list: List[Dict[str, Any]] = [] + cpu_arch__host_id_map = defaultdict(list) + for host_info in hosts_info: + report_cpu_arch = host_info["report_cpu_arch"] + if report_cpu_arch not in constants.CmdbCpuArchType.cpu_type__arch_map(): + continue + host_id = host_info["bk_host_id"] + sub_inst = host_id__sub_inst_map.get(host_id) + update_params: Dict[str, Any] = { + "bk_host_id": host_id, + "properties": { + "bk_cpu_architecture": constants.CmdbCpuArchType.cpu_type__arch_map().get(report_cpu_arch), + "bk_os_bit": constants.OsBitType.cpu_type__os_bit_map().get(report_cpu_arch), + }, + } + self.log_info( + sub_inst_ids=sub_inst.id, + log_content=_("更新 CMDB 主机信息:\n {params}").format(params=json.dumps(update_params, indent=2)), + ) + update_list.append(update_params) + cpu_arch__host_id_map[report_cpu_arch].append(host_id) + + for cpu_arch, bk_host_ids in cpu_arch__host_id_map.items(): + models.Host.objects.filter(bk_host_id__in=bk_host_ids).update(cpu_arch=cpu_arch) + + CCApi.batch_update_host({"update": update_list}) diff --git a/apps/backend/tests/components/collections/agent_new/test_install.py b/apps/backend/tests/components/collections/agent_new/test_install.py index 877d05755..e3cfd4f09 100644 --- a/apps/backend/tests/components/collections/agent_new/test_install.py +++ b/apps/backend/tests/components/collections/agent_new/test_install.py @@ -51,6 +51,7 @@ class InstallBaseTestCase(utils.AgentServiceBaseTestCase): NODE_TYPE = constants.NodeType.AGENT DOWNLOAD_PATH = "/tmp/data/bkee/public/bknodeman/download" JOB_API_MOCK_PATH = "apps.backend.components.collections.agent_new.install.JobApi" + CMDB_API_MOCK_PATH = "apps.backend.components.collections.agent_new.install.CCApi" EXECUTE_CMD_MOCK_PATH = "apps.backend.components.collections.agent_new.install.execute_cmd" PUT_FILE_MOCK_PATH = "apps.backend.components.collections.agent_new.install.put_file" CUSTOM_DATAIPC_DIR = "/var/run/gse_test" @@ -70,6 +71,11 @@ def init_mock_clients(self): return_type=mock_data_utils.MockReturnType.RETURN_VALUE.value, return_obj={"job_instance_id": 1} ), ) + self.cmdb_mock_client = api_mkd.cmdb.utils.CCApiMockClient( + batch_update_host=mock_data_utils.MockReturn( + return_type=mock_data_utils.MockReturnType.RETURN_VALUE.value, return_obj={"message": "success"} + ), + ) def init_redis_data(self): # 初始化redis数据,用于schedule时读取解析 @@ -147,6 +153,7 @@ def update_common_inputs(self): def start_patch(self): mock.patch(self.JOB_API_MOCK_PATH, self.job_mock_client).start() + mock.patch(self.CMDB_API_MOCK_PATH, self.cmdb_mock_client).start() mock.patch(target=self.EXECUTE_CMD_MOCK_PATH, return_value="").start() mock.patch(target=self.PUT_FILE_MOCK_PATH, return_value="").start() base.get_asyncssh_connect_mock_patch().start() @@ -1031,3 +1038,14 @@ def _test_shell_solution(self, validate_encrypted_password: bool): run_cmd, ], ) + + +class ReportCpuArchTestCase(LinuxInstallTestCase): + def tearDown(self) -> None: + mock_call_obj = self.cmdb_mock_client.batch_update_host.call_args + if mock_call_obj: + call_args = mock_call_obj[0][0] + self.assertEqual(call_args["update"][0]["bk_host_id"], self.obj_factory.bk_host_ids[0]) + self.assertEqual(call_args["update"][0]["properties"]["bk_cpu_architecture"], "arm") + self.assertEqual(call_args["update"][0]["properties"]["bk_os_bit"], "arm-64bit") + super().tearDown() diff --git a/apps/node_man/constants.py b/apps/node_man/constants.py index aaeb6cd64..5adf1cbf2 100644 --- a/apps/node_man/constants.py +++ b/apps/node_man/constants.py @@ -1137,3 +1137,31 @@ class CommonExecutionSolutionStepType(EnhanceEnum): @classmethod def _get_member__alias_map(cls) -> Dict[Enum, str]: return {cls.DEPENDENCIES: _("依赖文件"), cls.COMMANDS: _("命令")} + + +class CmdbCpuArchType(EnhanceEnum): + X86 = "x86" + X86_64 = "x86" + ARM = "arm" + + @classmethod + def _get_member__alias_map(cls) -> Dict[Enum, str]: + return {cls.X86: _("CPU架构:x86"), cls.X86_64: _("CPU架构:x86_64"), cls.ARM: _("CPU架构:arm")} + + @classmethod + def cpu_type__arch_map(cls): + return {CpuType.x86: cls.X86.value, CpuType.x86_64: cls.X86_64.value, CpuType.aarch64: cls.ARM.value} + + +class OsBitType(EnhanceEnum): + BIT32 = "32-bit" + BIT64 = "64-bit" + ARM = "arm-64bit" + + @classmethod + def _get_member__alias_map(cls) -> Dict[Enum, str]: + return {cls.BIT32: _("操作系统位数:32-bit"), cls.BIT64: _("操作系统位数:64-bit"), cls.ARM: _("操作系统位数:arm-64bit")} + + @classmethod + def cpu_type__os_bit_map(cls): + return {CpuType.x86: cls.BIT32.value, CpuType.x86_64: cls.BIT64.value, CpuType.aarch64: cls.ARM.value}