From 7d464bc72e5db1f20fdf82b881156f832e0a35a8 Mon Sep 17 00:00:00 2001 From: Tobias Wolf Date: Mon, 21 Oct 2024 16:32:40 +0200 Subject: [PATCH] Use data of `ceph report` for analysis instead of multiple sources Signed-off-by: Tobias Wolf --- src/rookify/modules/analyze_ceph/main.py | 63 +++++++++++-------- src/rookify/modules/cephx_auth_config/main.py | 2 +- .../modules/create_rook_resources/main.py | 4 +- src/rookify/modules/migrate_mds_pools/main.py | 4 +- src/rookify/modules/migrate_mons/main.py | 6 +- src/rookify/modules/migrate_osd_pools/main.py | 2 +- src/rookify/modules/migrate_osds/main.py | 13 +++- src/rookify/modules/migrate_rgw_pools/main.py | 14 +++-- src/rookify/modules/migrate_rgws/main.py | 8 ++- 9 files changed, 71 insertions(+), 45 deletions(-) diff --git a/src/rookify/modules/analyze_ceph/main.py b/src/rookify/modules/analyze_ceph/main.py index 25fc206..f134a45 100644 --- a/src/rookify/modules/analyze_ceph/main.py +++ b/src/rookify/modules/analyze_ceph/main.py @@ -7,36 +7,45 @@ class AnalyzeCephHandler(ModuleHandler): - def _process_command( + def _process_command_result( self, state_data: Any, command: str, value: Optional[Any] = None ) -> bool: - """Helper method to process commands by either setting or checking state data.""" - parts = command.split(" ") - current_level = state_data # the root of the data structure + """ + Helper method to process commands by either setting or checking state data. + """ + + command_parts = command.split(" ") + state_structure_data = state_data # the root of the data structure # Traverse the dictionary structure based on command parts - for idx, part in enumerate(parts): - if len(parts) == idx + 1: # Last part of the command - if value is not None: - current_level[part] = value - else: - return part in current_level + for idx, key in enumerate(command_parts): + # Last part of the command + if len(command_parts) == idx + 1: + if value is None: + return key in state_structure_data + + state_structure_data[key] = value else: - if part not in current_level: - current_level[part] = {} - current_level = current_level[part] + if key not in state_structure_data: + state_structure_data[key] = {} + + state_structure_data = state_structure_data[key] return True def preflight(self) -> Any: - commands = ["mon dump", "osd dump", "device ls", "fs ls", "node ls"] state = self.machine.get_preflight_state("AnalyzeCephHandler") + + if getattr(state, "data", None) is not None: + return + + commands = ["fs ls", "node ls", "report"] state.data = {} # Execute each command and store the result for command in commands: result = self.ceph.mon_command(command) - self._process_command(state.data, command, result) + self._process_command_result(state.data, command, result) self.logger.info("AnalyzeCephHandler ran successfully.") @@ -45,25 +54,25 @@ def get_readable_key_value_state(self) -> Dict[str, str]: kv_state_data = OrderedDict() - if "mon" not in state.data or "dump" not in state.data["mon"]: - kv_state_data["ceph mon dump"] = "Not analyzed yet" + if "report" not in state.data: + kv_state_data["Ceph report"] = "Not analyzed yet" else: - kv_state_data["ceph mon dump"] = self._get_readable_json_dump( - state.data["mon"]["dump"] + kv_state_data["Ceph report"] = self._get_readable_json_dump( + state.data["report"] ) - if "osd" not in state.data or "dump" not in state.data["osd"]: - kv_state_data["ceph osd dump"] = "Not analyzed yet" + if "node" not in state.data or "ls" not in state.data["node"]: + kv_state_data["Ceph node ls"] = "Not analyzed yet" else: - kv_state_data["ceph osd dump"] = self._get_readable_json_dump( - state.data["osd"]["dump"] + kv_state_data["Ceph node ls"] = self._get_readable_json_dump( + state.data["node"]["ls"] ) - if "device" not in state.data or "ls" not in state.data["device"]: - kv_state_data["OSD devices"] = "Not analyzed yet" + if "fs" not in state.data or "ls" not in state.data["fs"]: + kv_state_data["Ceph fs ls"] = "Not analyzed yet" else: - kv_state_data["OSD devices"] = self._get_readable_json_dump( - state.data["device"]["ls"] + kv_state_data["Ceph fs ls"] = self._get_readable_json_dump( + state.data["fs"]["ls"] ) return kv_state_data diff --git a/src/rookify/modules/cephx_auth_config/main.py b/src/rookify/modules/cephx_auth_config/main.py index fc7f4fe..50b7bca 100644 --- a/src/rookify/modules/cephx_auth_config/main.py +++ b/src/rookify/modules/cephx_auth_config/main.py @@ -7,7 +7,7 @@ class CephXAuthHandler(ModuleHandler): - def preflight(self) -> Any: + def preflight(self) -> None: if not self.is_cephx_set(self.ceph.conf_get("auth_cluster_required")): raise ModuleException( "Ceph config value auth_cluster_required does not contain cephx" diff --git a/src/rookify/modules/create_rook_resources/main.py b/src/rookify/modules/create_rook_resources/main.py index 5ab018b..2cceccd 100644 --- a/src/rookify/modules/create_rook_resources/main.py +++ b/src/rookify/modules/create_rook_resources/main.py @@ -19,7 +19,7 @@ def __create_configmap_definition(self) -> None: configmap_mon_list = "" mapping = {} - for mon in state_data["mon"]["dump"]["mons"]: + for mon in state_data["report"]["monmap"]["mons"]: if configmap_mon_list != "": configmap_mon_list += "," @@ -120,7 +120,7 @@ def execute(self) -> None: secret_data = { "admin-secret": admin_auth["key"], "cluster-name": self._config["rook"]["cluster"]["name"], - "fsid": state_data["mon"]["dump"]["fsid"], + "fsid": state_data["report"]["monmap"]["fsid"], "mon-secret": mon_auth["key"], } diff --git a/src/rookify/modules/migrate_mds_pools/main.py b/src/rookify/modules/migrate_mds_pools/main.py index 51e847b..bcae3b4 100644 --- a/src/rookify/modules/migrate_mds_pools/main.py +++ b/src/rookify/modules/migrate_mds_pools/main.py @@ -77,7 +77,9 @@ def get_readable_key_value_state(self) -> Dict[str, str]: "MigrateMdsPoolsHandler", "migrated_mds_pools", default_value=[] ) - pools = self.machine.get_preflight_state("MigrateMdsPoolsHandler").pools + pools = self.machine.get_preflight_state_data( + "MigrateMdsPoolsHandler", "pools", default_value={} + ) kv_state_data = OrderedDict() diff --git a/src/rookify/modules/migrate_mons/main.py b/src/rookify/modules/migrate_mons/main.py index 881fddd..92e08a0 100644 --- a/src/rookify/modules/migrate_mons/main.py +++ b/src/rookify/modules/migrate_mons/main.py @@ -26,10 +26,10 @@ def execute(self) -> None: "MigrateMonsHandler", "migrated_mons", default_value=[] ) - if len(migrated_mons) >= len(state_data["mon"]["dump"]["mons"]): + if len(migrated_mons) >= len(state_data["report"]["monmap"]["mons"]): return - for mon in state_data["mon"]["dump"]["mons"]: + for mon in state_data["report"]["monmap"]["mons"]: self._migrate_mon(mon) def get_readable_key_value_state(self) -> Dict[str, str]: @@ -37,7 +37,7 @@ def get_readable_key_value_state(self) -> Dict[str, str]: return { "ceph mon daemons": self._get_readable_json_dump( - state_data["mon"]["dump"]["mons"] + state_data["report"]["monmap"]["mons"] ) } diff --git a/src/rookify/modules/migrate_osd_pools/main.py b/src/rookify/modules/migrate_osd_pools/main.py index 9da3253..406a0f5 100644 --- a/src/rookify/modules/migrate_osd_pools/main.py +++ b/src/rookify/modules/migrate_osd_pools/main.py @@ -33,7 +33,7 @@ def _get_filtered_osd_pools_list(self) -> List[Dict[str, Any]]: state_data = self.machine.get_preflight_state("AnalyzeCephHandler").data osd_pool_configurations = self.ceph.get_osd_pool_configurations_from_osd_dump( - state_data["osd"]["dump"] + state_data["report"]["osdmap"] ) pools = [] diff --git a/src/rookify/modules/migrate_osds/main.py b/src/rookify/modules/migrate_osds/main.py index acebdf5..b6efb86 100644 --- a/src/rookify/modules/migrate_osds/main.py +++ b/src/rookify/modules/migrate_osds/main.py @@ -9,11 +9,15 @@ class MigrateOSDsHandler(ModuleHandler): - REQUIRES = ["migrate_mons"] + REQUIRES = ["analyze_ceph", "migrate_mons"] def _get_devices_of_hosts(self) -> Dict[str, Dict[str, str]]: state_data = self.machine.get_preflight_state("AnalyzeCephHandler").data + osd_devices: Dict[str, Dict[str, str]] = {} + osd_metadata = { + str(osd_data["id"]): osd_data for osd_data in state_data["osd_metadata"] + } for osd_host, osds in state_data["node"]["ls"]["osd"].items(): osd_devices[osd_host] = {} @@ -23,7 +27,12 @@ def _get_devices_of_hosts(self) -> Dict[str, Dict[str, str]]: From there we try to get the encrypted volume partition UUID for later use in Rook. """ for osd_id in osds: - osd_data = self.ceph.mon_command("osd metadata", id=osd_id) + if osd_id not in osd_metadata: + raise ModuleException( + "Found Ceph OSD ID {0} without metadata".format(osd_id) + ) + + osd_data = osd_metadata[osd_id] result = self.ssh.command( osd_host, diff --git a/src/rookify/modules/migrate_rgw_pools/main.py b/src/rookify/modules/migrate_rgw_pools/main.py index dddd3c5..e3076e9 100644 --- a/src/rookify/modules/migrate_rgw_pools/main.py +++ b/src/rookify/modules/migrate_rgw_pools/main.py @@ -20,9 +20,11 @@ def preflight(self) -> None: if len(zones) > 0: return - service_data = self.ceph.mon_command("service dump") - - rgw_daemons = service_data["services"].get("rgw", {}).get("daemons", {}) + rgw_daemons = ( + state_data["report"]["servicemap"]["services"] + .get("rgw", {}) + .get("daemons", {}) + ) for rgw_daemon in rgw_daemons.values(): if not isinstance(rgw_daemon, dict): @@ -36,7 +38,7 @@ def preflight(self) -> None: zones[zone_name] = {"osd_pools": {}, "rgw_count": 1} osd_pools = self.ceph.get_osd_pool_configurations_from_osd_dump( - state_data["osd"]["dump"] + state_data["report"]["osdmap"] ) for zone_name in zones: @@ -76,7 +78,9 @@ def get_readable_key_value_state(self) -> Dict[str, str]: "MigrateRgwPoolsHandler", "migrated_pools", default_value=[] ) - zones = self.machine.get_preflight_state("MigrateRgwPoolsHandler").zones + zones = self.machine.get_preflight_state_data( + "MigrateRgwPoolsHandler", "zones", default_value={} + ) kv_state_data = OrderedDict() diff --git a/src/rookify/modules/migrate_rgws/main.py b/src/rookify/modules/migrate_rgws/main.py index 9dea10c..caf7309 100644 --- a/src/rookify/modules/migrate_rgws/main.py +++ b/src/rookify/modules/migrate_rgws/main.py @@ -8,13 +8,14 @@ class MigrateRgwsHandler(ModuleHandler): - REQUIRES = ["migrate_rgw_pools"] + REQUIRES = ["analyze_ceph", "migrate_rgw_pools"] def _get_rgw_daemon_hosts(self) -> List[str]: - ceph_status = self.ceph.mon_command("status") + state_data = self.machine.get_preflight_state("AnalyzeCephHandler").data - rgw_daemons = ceph_status["servicemap"]["services"]["rgw"]["daemons"] + rgw_daemons = state_data["report"]["servicemap"]["services"]["rgw"]["daemons"] rgw_daemon_hosts = [] + if "summary" in rgw_daemons: del rgw_daemons["summary"] @@ -23,6 +24,7 @@ def _get_rgw_daemon_hosts(self) -> List[str]: raise ModuleException( "Unexpected ceph-rgw daemon metadata: {0}".format(rgw_daemon) ) + if rgw_daemon["metadata"]["hostname"] not in rgw_daemon_hosts: rgw_daemon_hosts.append(rgw_daemon["metadata"]["hostname"])