From 1f2f2befc82bf3dee3f09e85ce509bd6a3eb3f49 Mon Sep 17 00:00:00 2001 From: Guillaume Abrioux Date: Mon, 16 Feb 2026 14:24:36 +0100 Subject: [PATCH] mgr/cephadm: validate hostname in NodeProxyCache This adds a _resolve_hosts() method to resolve hostname from kwargs and raise OrchestratorError when the host has no node-proxy data. Fixes: https://tracker.ceph.com/issues/74749 Signed-off-by: Guillaume Abrioux --- src/pybind/mgr/cephadm/agent.py | 22 ++++++++++---------- src/pybind/mgr/cephadm/inventory.py | 31 ++++++++++++++++++----------- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/pybind/mgr/cephadm/agent.py b/src/pybind/mgr/cephadm/agent.py index 6f52ad47514..e6aafce4c78 100644 --- a/src/pybind/mgr/cephadm/agent.py +++ b/src/pybind/mgr/cephadm/agent.py @@ -13,7 +13,7 @@ import ssl import threading import time -from orchestrator import DaemonDescriptionStatus +from orchestrator import DaemonDescriptionStatus, OrchestratorError from orchestrator._interface import daemon_type_to_service from ceph.utils import datetime_now, http_req from ceph.deployment.inventory import Devices @@ -418,7 +418,7 @@ class NodeProxyEndpoint: """ try: results = self.mgr.node_proxy_cache.fullreport(**kw) - except KeyError: + except (KeyError, OrchestratorError): raise cherrypy.HTTPError(404, f"{kw.get('hostname')} not found.") return results @@ -442,7 +442,7 @@ class NodeProxyEndpoint: """ try: results = self.mgr.node_proxy_cache.criticals(**kw) - except KeyError: + except (KeyError, OrchestratorError): raise cherrypy.HTTPError(404, f"{kw.get('hostname')} not found.") return results @@ -466,7 +466,7 @@ class NodeProxyEndpoint: """ try: results = self.mgr.node_proxy_cache.summary(**kw) - except KeyError: + except (KeyError, OrchestratorError): raise cherrypy.HTTPError(404, f"{kw.get('hostname')} not found.") return results @@ -491,7 +491,7 @@ class NodeProxyEndpoint: """ try: results = self.mgr.node_proxy_cache.common('memory', **kw) - except KeyError: + except (KeyError, OrchestratorError): raise cherrypy.HTTPError(404, f"{kw.get('hostname')} not found.") return results @@ -516,7 +516,7 @@ class NodeProxyEndpoint: """ try: results = self.mgr.node_proxy_cache.common('network', **kw) - except KeyError: + except (KeyError, OrchestratorError): raise cherrypy.HTTPError(404, f"{kw.get('hostname')} not found.") return results @@ -541,7 +541,7 @@ class NodeProxyEndpoint: """ try: results = self.mgr.node_proxy_cache.common('processors', **kw) - except KeyError: + except (KeyError, OrchestratorError): raise cherrypy.HTTPError(404, f"{kw.get('hostname')} not found.") return results @@ -566,7 +566,7 @@ class NodeProxyEndpoint: """ try: results = self.mgr.node_proxy_cache.common('storage', **kw) - except KeyError: + except (KeyError, OrchestratorError): raise cherrypy.HTTPError(404, f"{kw.get('hostname')} not found.") return results @@ -591,7 +591,7 @@ class NodeProxyEndpoint: """ try: results = self.mgr.node_proxy_cache.common('power', **kw) - except KeyError: + except (KeyError, OrchestratorError): raise cherrypy.HTTPError(404, f"{kw.get('hostname')} not found.") return results @@ -616,7 +616,7 @@ class NodeProxyEndpoint: """ try: results = self.mgr.node_proxy_cache.common('fans', **kw) - except KeyError: + except (KeyError, OrchestratorError): raise cherrypy.HTTPError(404, f"{kw.get('hostname')} not found.") return results @@ -640,7 +640,7 @@ class NodeProxyEndpoint: """ try: results = self.mgr.node_proxy_cache.firmwares(**kw) - except KeyError: + except (KeyError, OrchestratorError): raise cherrypy.HTTPError(404, f"{kw.get('hostname')} not found.") return results diff --git a/src/pybind/mgr/cephadm/inventory.py b/src/pybind/mgr/cephadm/inventory.py index 37e346845c3..aed6ba03efa 100644 --- a/src/pybind/mgr/cephadm/inventory.py +++ b/src/pybind/mgr/cephadm/inventory.py @@ -1669,6 +1669,16 @@ class NodeProxyCache: def _is_unknown_status(self, statuses: ValuesView) -> bool: return self._has_health_value(statuses, 'unknown') and not self._is_error_status(statuses) + def _resolve_hosts(self, **kw: Any) -> List[str]: + hostname = kw.get('hostname') + if hostname is None: + return list(self.data.keys()) + if hostname not in self.data: + raise OrchestratorError( + f"Host '{hostname}' has no node-proxy data (unknown host or node-proxy not running)." + ) + return [hostname] + def fullreport(self, **kw: Any) -> Dict[str, Any]: """ Retrieves the full report for the specified hostname. @@ -1683,8 +1693,7 @@ class NodeProxyCache: :return: The full report data for the specified hostname(s). :rtype: dict """ - hostname = kw.get('hostname') - hosts = [hostname] if hostname else self.data.keys() + hosts = self._resolve_hosts(**kw) return {host: self.data[host] for host in hosts} def summary(self, **kw: Any) -> Dict[str, Any]: @@ -1703,9 +1712,7 @@ class NodeProxyCache: host or all hosts and their components. :rtype: Dict[str, Dict[str, str]] """ - hostname = kw.get('hostname') - hosts = [hostname] if hostname else self.data.keys() - + hosts = self._resolve_hosts(**kw) _result: Dict[str, Any] = {} for host in hosts: @@ -1751,9 +1758,8 @@ class NodeProxyCache: :return: Endpoint information for the specified host(s). :rtype: Union[Dict[str, Any], Any] """ - hostname = kw.get('hostname') + hosts = self._resolve_hosts(**kw) _result = {} - hosts = [hostname] if hostname else self.data.keys() for host in hosts: try: @@ -1776,12 +1782,14 @@ class NodeProxyCache: :return: A dictionary containing firmware information for each host. :rtype: Dict[str, Any] """ - hostname = kw.get('hostname') - hosts = [hostname] if hostname else self.data.keys() - + hosts = self._resolve_hosts(**kw) return {host: self.data[host]['firmwares'] for host in hosts} def get_critical_from_host(self, hostname: str) -> Dict[str, Any]: + if hostname not in self.data: + raise OrchestratorError( + f"Host '{hostname}' has no node-proxy data (unknown host or node-proxy not running)." + ) results: Dict[str, Any] = {} for component, component_data in self.data[hostname]['status'].items(): @@ -1812,10 +1820,9 @@ class NodeProxyCache: :return: A dictionary containing critical information for each host. :rtype: List[Dict[str, Any]] """ - hostname = kw.get('hostname') + hosts = self._resolve_hosts(**kw) results: Dict[str, Any] = {} - hosts = [hostname] if hostname else self.data.keys() for host in hosts: results[host] = self.get_critical_from_host(host) return results -- 2.47.3