diff --git a/apps/backend/components/collections/agent_new/install.py b/apps/backend/components/collections/agent_new/install.py index 8052fe914..92df5b7a3 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 @@ -822,11 +822,13 @@ def _schedule(self, data, parent_data, callback_data=None): left_scheduling_sub_inst_ids = [] cpu_arch__host_id_map = defaultdict(list) os_version__host_id_map = defaultdict(list) + cpu_arch__sub_inst_id_map = defaultdict(list) host_id__agent_id_map: Dict[int, str] = {} for result in results: # 对于未完成的实例,记录下来到下一次schedule中继续检查 if not result["is_finished"]: left_scheduling_sub_inst_ids.append(result["sub_inst_id"]) + cpu_arch__sub_inst_id_map[result["cpu_arch"]].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) @@ -839,10 +841,8 @@ 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 + self.update_db_and_report_cpu_arch(cpu_arch__host_id_map, cpu_arch__sub_inst_id_map) # 批量更新主机操作系统版本号 for os_version, bk_host_ids in os_version__host_id_map.items(): @@ -873,3 +873,28 @@ 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 + + def update_db_and_report_cpu_arch(self, cpu_arch__host_id_map: defaultdict, cpu_arch__sub_inst_id_map: defaultdict): + """ + :param cpu_arch__host_id_map: key -> cpu arch; value -> list of bk_host_id + :param cpu_arch__sub_inst_id_map: key -> cpu arch; value -> list of sub_inst_id + return: + """ + update_list: List[Dict[str, Any]] = [] + 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) + for host_id in bk_host_ids: + update_params: Dict[str, Any] = { + "bk_host_id": host_id, + "properties": { + "bk_cpu_architecture": constants.CmdbCpuArchType.cpu_type__arch_map().get(cpu_arch), + "bk_os_bit": constants.OsBitType.cpu_type__os_bit_map().get(cpu_arch), + } + } + self.log_info( + sub_inst_ids=cpu_arch__sub_inst_id_map[cpu_arch], + log_content=_("更新 CMDB 主机信息:\n {params}").format(params=json.dumps(update_params, indent=2)), + ) + update_list.append(update_params) + 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..5fecdf8e1 100644 --- a/apps/node_man/constants.py +++ b/apps/node_man/constants.py @@ -503,6 +503,11 @@ def _get_member__alias_map(cls) -> Dict[Enum, str]: } CMDB_CPU_MAP = {"x86": CpuType.x86, "arm": CpuType.aarch64} +CMDB_CPU_ARCH = ("x86", "arm") +CPU_ARCH_CHOICES = tuple_choices(CMDB_CPU_ARCH) +CpuArchType = choices_to_namedtuple(CPU_ARCH_CHOICES) +CMDB_CPU_ARCH_MAP = {"x86": CpuArchType.x86, "x86_64": CpuArchType.x86, "aarch64": CpuArchType.arm} + PACKAGE_PATH_RE = re.compile( f"(?Pexternal_)?plugins_(?P({'|'.join(map(str, PLUGIN_OS_TUPLE))}))" f"_(?P({'|'.join(map(str, CPU_TUPLE))})?$)" @@ -1137,3 +1142,47 @@ 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" + AARCH64 = "arm" + + @classmethod + def _get_member__alias_map(cls) -> Dict[Enum, str]: + return { + cls.X86: _("CPU架构:x86"), + cls.X86_64: _("CPU架构:x86_64"), + cls.AARCH64: _("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.AARCH64.value + } + + +class OsBitType(EnhanceEnum): + BIT32 = "32-bit" + BIT64 = "64-bit" + ARM64 = "arm-64bit" + + @classmethod + def _get_member__alias_map(cls) -> Dict[Enum, str]: + return { + cls.BIT32: _("操作系统位数:32-bit"), + cls.BIT64: _("操作系统位数:64-bit"), + cls.ARM64: _("操作系统位数: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.ARM64.value + }