From 83a552095c4afba1b9e75c99cc52ed5cad26edd3 Mon Sep 17 00:00:00 2001
From: Jose Castillo <jcastillo@redhat.com>
Date: Mon, 24 Mar 2025 10:38:38 +0000
Subject: [PATCH] [ceph*] Failback to 'cephadm shell' if 'ceph' is not present

This commit enables the use of `cephadm shell`
when `ceph` as a command is not available.

Collecting sos reports for ceph on nodes where ceph-common
is not installed results in missing ceph data files.
Users may not want to have ceph-common packages installed
on the hosts because since ceph 5, the proper execution
path should be to run cephadm shell which executes
ceph inside a container.

Related: RHEL-76334

Signed-off-by: Jose Castillo <jcastillo@redhat.com>
---
 sos/report/plugins/ceph_mds.py |  6 ++-
 sos/report/plugins/ceph_mgr.py | 12 +++--
 sos/report/plugins/ceph_mon.py | 81 ++++++++++++++++++----------------
 sos/report/plugins/ceph_osd.py |  5 ++-
 4 files changed, 59 insertions(+), 45 deletions(-)

diff --git a/sos/report/plugins/ceph_mds.py b/sos/report/plugins/ceph_mds.py
index abbf2ba6..b9b4ff6e 100644
--- a/sos/report/plugins/ceph_mds.py
+++ b/sos/report/plugins/ceph_mds.py
@@ -102,9 +102,13 @@ class CephMDS(Plugin, RedHatPlugin, UbuntuPlugin):
         directory = '/var/snap/microceph/current/run' if microceph \
             else '/var/run/ceph'
 
+        # If ceph binary is not available, use 'cephadm shell'
+        ceph_bin = 'ceph' if self.exec_cmd("ceph -v")['status'] == 0\
+            else 'cephadm shell'
+
         # common add_cmd_output for ceph and microceph
         self.add_cmd_output([
-            f"ceph daemon {i} {c}" for i in
+            f"{ceph_bin} daemon {i} {c}" for i in
             self.get_socks(directory) for c in cmds],
              container=cname
         )
diff --git a/sos/report/plugins/ceph_mgr.py b/sos/report/plugins/ceph_mgr.py
index e89606d5..ea1cb82c 100644
--- a/sos/report/plugins/ceph_mgr.py
+++ b/sos/report/plugins/ceph_mgr.py
@@ -46,6 +46,10 @@ class CephMGR(Plugin, RedHatPlugin, UbuntuPlugin):
         all_logs = self.get_option("all_logs")
         microceph_pkg = self.policy.package_manager.pkg_by_name('microceph')
 
+        # If ceph binary is not available, use 'cephadm shell'
+        ceph_bin = 'ceph' if self.exec_cmd("ceph -v")['status'] == 0\
+            else 'cephadm shell'
+
         ceph_mgr_cmds = ([
             "balancer eval",
             "balancer status",  # For pre-Squid releases
@@ -61,7 +65,7 @@ class CephMGR(Plugin, RedHatPlugin, UbuntuPlugin):
         ])
 
         # if orchestrator is configured
-        orch_configured = self.exec_cmd('ceph orch status')
+        orch_configured = self.exec_cmd('{ceph_bin} orch status')
         if orch_configured['status'] == 0:
             ceph_mgr_cmds += ([
                 "orch host ls",
@@ -146,16 +150,16 @@ class CephMGR(Plugin, RedHatPlugin, UbuntuPlugin):
                 ])
 
         self.add_cmd_output(
-            [f"ceph {cmd}" for cmd in ceph_mgr_cmds])
+            [f"{ceph_bin} {cmd}" for cmd in ceph_mgr_cmds])
 
         # get ceph_cmds again as json for easier automation parsing
         self.add_cmd_output(
-            [f"ceph {cmd} --format json-pretty" for cmd in ceph_mgr_cmds],
+            [f"{ceph_bin} {cmd} --format json-pretty" for cmd in ceph_mgr_cmds],
             subdir="json_output",
         )
 
         self.add_cmd_output([
-            f"ceph daemon {m} {cmd}" for m in self.get_socks(directory)
+            f"{ceph_bin} daemon {m} {cmd}" for m in self.get_socks(directory)
             for cmd in cmds]
         )
 
diff --git a/sos/report/plugins/ceph_mon.py b/sos/report/plugins/ceph_mon.py
index dadb96e3..2dc47ebd 100644
--- a/sos/report/plugins/ceph_mon.py
+++ b/sos/report/plugins/ceph_mon.py
@@ -44,6 +44,9 @@ class CephMON(Plugin, RedHatPlugin, UbuntuPlugin):
     def setup(self):
         all_logs = self.get_option("all_logs")
         self.ceph_version = self.get_ceph_version()
+        # If ceph binary is not available, use 'cephadm shell'
+        ceph_bin = 'ceph' if self.exec_cmd("ceph -v")['status'] == 0\
+            else 'cephadm shell'
 
         microceph_pkg = self.policy.package_manager.pkg_by_name('microceph')
         if not microceph_pkg:
@@ -97,51 +100,51 @@ class CephMON(Plugin, RedHatPlugin, UbuntuPlugin):
                 "/var/snap/microceph/current/conf/*",
             ])
 
-        self.add_cmd_output("ceph report", tags="ceph_report", stderr=False)
+        self.add_cmd_output(f"{ceph_bin} report", tags="ceph_report", stderr=False)
         self.add_cmd_output([
             # The ceph_mon plugin will collect all the "ceph ..." commands
             # which typically require the keyring.
 
-            "ceph config generate-minimal-conf",
-            "ceph config log",
-            "ceph config-key dump",
-            "ceph crash ls",
-            "ceph crash stat",
-            "ceph features",
-            "ceph health detail",
-            "ceph insights",
-            "ceph log last 10000 debug audit",
-            "ceph log last 10000 debug cluster",
-            "ceph mgr dump",
-            "ceph mgr metadata",
-            "ceph mgr module ls",
-            "ceph mgr services",
-            "ceph mgr versions",
-            "ceph mon stat",
-            "ceph mon features ls",
-            "ceph node ls",
-            "ceph osd crush class ls",
-            "ceph osd crush dump",
-            "ceph osd crush rule ls",
-            "ceph osd crush show-tunables",
-            "ceph osd crush tree --show-shadow",
-            "ceph osd erasure-code-profile ls",
-            "ceph osd metadata",
-            "ceph osd utilization",
-            "ceph telemetry channel ls",
-            "ceph telemetry collection ls",
-            "ceph telemetry ls",
-            "ceph quorum_status",
-            "ceph versions",
+            f"{ceph_bin} config generate-minimal-conf",
+            f"{ceph_bin} config log",
+            f"{ceph_bin} config-key dump",
+            f"{ceph_bin} crash ls",
+            f"{ceph_bin} crash stat",
+            f"{ceph_bin} features",
+            f"{ceph_bin} health detail",
+            f"{ceph_bin} insights",
+            f"{ceph_bin} log last 10000 debug audit",
+            f"{ceph_bin} log last 10000 debug cluster",
+            f"{ceph_bin} mgr dump",
+            f"{ceph_bin} mgr metadata",
+            f"{ceph_bin} mgr module ls",
+            f"{ceph_bin} mgr services",
+            f"{ceph_bin} mgr versions",
+            f"{ceph_bin} mon stat",
+            f"{ceph_bin} mon features ls",
+            f"{ceph_bin} node ls",
+            f"{ceph_bin} osd crush class ls",
+            f"{ceph_bin} osd crush dump",
+            f"{ceph_bin} osd crush rule ls",
+            f"{ceph_bin} osd crush show-tunables",
+            f"{ceph_bin} osd crush tree --show-shadow",
+            f"{ceph_bin} osd erasure-code-profile ls",
+            f"{ceph_bin} osd metadata",
+            f"{ceph_bin} osd utilization",
+            f"{ceph_bin} telemetry channel ls",
+            f"{ceph_bin} telemetry collection ls",
+            f"{ceph_bin} telemetry ls",
+            f"{ceph_bin} quorum_status",
+            f"{ceph_bin} versions",
             "ceph-disk list",
         ])
 
-        crashes = self.collect_cmd_output('ceph crash ls')
+        crashes = self.collect_cmd_output(f'{ceph_bin} crash ls')
         if crashes['status'] == 0:
             for crashln in crashes['output'].splitlines():
                 if crashln.endswith('*'):
                     cid = crashln.split()[0]
-                    self.add_cmd_output(f"ceph crash info {cid}")
+                    self.add_cmd_output(f"{ceph_bin} crash info {cid}")
 
         ceph_cmds = [
             "config dump",
@@ -169,22 +172,22 @@ class CephMON(Plugin, RedHatPlugin, UbuntuPlugin):
             "time-sync-status",
         ]
 
-        self.add_cmd_output("ceph health detail --format json-pretty",
+        self.add_cmd_output(f"{ceph_bin} health detail --format json-pretty",
                             subdir="json_output",
                             tags="ceph_health_detail")
-        self.add_cmd_output("ceph osd tree --format json-pretty",
+        self.add_cmd_output(f"{ceph_bin} osd tree --format json-pretty",
                             subdir="json_output",
                             tags="ceph_osd_tree")
         self.add_cmd_output(
-            [f"ceph tell mon.{mid} mon_status" for mid in self.get_ceph_ids()],
+            [f"{ceph_bin} tell mon.{mid} mon_status" for mid in self.get_ceph_ids()],
             subdir="json_output",
         )
 
-        self.add_cmd_output([f"ceph {cmd}" for cmd in ceph_cmds])
+        self.add_cmd_output([f"{ceph_bin} {cmd}" for cmd in ceph_cmds])
 
         # get ceph_cmds again as json for easier automation parsing
         self.add_cmd_output(
-            [f"ceph {cmd} --format json-pretty" for cmd in ceph_cmds],
+            [f"{ceph_bin} {cmd} --format json-pretty" for cmd in ceph_cmds],
             subdir="json_output",
             stderr=False
         )
diff --git a/sos/report/plugins/ceph_osd.py b/sos/report/plugins/ceph_osd.py
index 843e6004..90d97320 100644
--- a/sos/report/plugins/ceph_osd.py
+++ b/sos/report/plugins/ceph_osd.py
@@ -40,6 +40,9 @@ class CephOSD(Plugin, RedHatPlugin, UbuntuPlugin):
         all_logs = self.get_option("all_logs")
         directory = ''
         microceph_pkg = self.policy.package_manager.pkg_by_name('microceph')
+        # If ceph binary is not available, use 'cephadm shell'
+        ceph_bin = 'ceph' if self.exec_cmd("ceph -v")['status'] == 0\
+            else 'cephadm shell'
         cmds = [
             # will work pre quincy
             "bluestore bluefs available",
@@ -132,7 +135,7 @@ class CephOSD(Plugin, RedHatPlugin, UbuntuPlugin):
 
         # common add_cmd_output for ceph and microceph
         self.add_cmd_output([
-            f"ceph daemon {i} {c}" for i in
+            f"{ceph_bin} daemon {i} {c}" for i in
             self.get_socks(directory) for c in cmds]
         )
 
-- 
2.49.0

