From 9f3fd4c60ff47bc655e39d6a8599e085843ac6bb Mon Sep 17 00:00:00 2001 From: Guillaume Abrioux Date: Fri, 6 Oct 2023 11:10:39 +0000 Subject: [PATCH] mgr/cephadm: add NodeProxyCache class This is for tracking and caching any node-proxy data. The node-proxy API now uses this class to serve its data. Signed-off-by: Guillaume Abrioux (cherry picked from commit a48c34ef0034de335c1ec5d599272fc9d958a506) --- src/pybind/mgr/cephadm/agent.py | 71 ++++--------------------- src/pybind/mgr/cephadm/inventory.py | 80 +++++++++++++++++++++++++++++ src/pybind/mgr/cephadm/module.py | 5 +- src/pybind/mgr/cephadm/serve.py | 3 ++ 4 files changed, 98 insertions(+), 61 deletions(-) diff --git a/src/pybind/mgr/cephadm/agent.py b/src/pybind/mgr/cephadm/agent.py index 0228d73f8285b..648b3b58d49b4 100644 --- a/src/pybind/mgr/cephadm/agent.py +++ b/src/pybind/mgr/cephadm/agent.py @@ -198,85 +198,36 @@ class NodeProxy: data: Dict[str, Any] = cherrypy.request.json if self.validate_node_proxy_data(data): - self.mgr.set_store(f'node_proxy/data/{data["host"]}', json.dumps(data['data'])) + host = data['host'] + self.mgr.node_proxy.save(host, data['data']) self.raise_alert(data) results["result"] = self.validate_msg return results - def get_full_report(self) -> Dict[str, Any]: - results: Dict[str, Any] = {} - - for k, v in self.mgr.get_store_prefix('node_proxy/data').items(): - host = k.split('/')[-1:][0] - results[host] = json.loads(v) - return results - @cherrypy.expose @cherrypy.tools.allow(methods=['GET']) - @cherrypy.tools.json_in() @cherrypy.tools.json_out() def fullreport(self, **kw: Any) -> Dict[str, Any]: - results: Dict[str, Any] = {} - results = self.get_full_report() - hostname = kw.get('hostname',) + return self.mgr.node_proxy.fullreport(**kw) - if hostname not in results.keys(): - return results - else: - return results[hostname] + @cherrypy.expose + @cherrypy.tools.allow(methods=['GET']) + @cherrypy.tools.json_out() + def criticals(self, **kw: Any) -> Dict[str, Any]: + return self.mgr.node_proxy.criticals() @cherrypy.expose @cherrypy.tools.allow(methods=['GET']) - @cherrypy.tools.json_in() @cherrypy.tools.json_out() def summary(self, **kw: Any) -> Dict[str, Any]: - results: Dict[str, Any] = {} - status: List[str] = [] - hostname = kw.get('hostname',) - - results = self.get_full_report() + return self.mgr.node_proxy.summary(**kw) - mapper: Dict[bool, str] = { - True: 'error', - False: 'ok' - } - - _result = {} - - for host, data in results.items(): - _result[host] = {} - for component, details in data.items(): - res = any([member['status']['health'].lower() != 'ok' for member in data[component].values()]) - _result[host][component] = mapper[res] - - if hostname and hostname in results.keys(): - return _result[hostname] - else: - return _result - - @cherrypy.tools.json_in() + @cherrypy.tools.allow(methods=['GET']) @cherrypy.tools.json_out() def common(self, **kw) -> Dict[str, Any]: - results: Dict[str, Any] = {} - status: List[str] = [] - hostname = kw.get('hostname',) - cmd = kw.get('cmd',) - results = self.get_full_report() - - _result = {} - - for host, data in results.items(): - try: - _result[host] = data[cmd] - except KeyError: - raise RuntimeError(f'invalid endpoint {cmd}') - - if hostname and hostname in results.keys(): - return _result[hostname] - else: - return _result + return self.mgr.node_proxy.common(**kw) def dispatch(self, hostname='', cmd=''): kw = dict(hostname=hostname, cmd=cmd) diff --git a/src/pybind/mgr/cephadm/inventory.py b/src/pybind/mgr/cephadm/inventory.py index 7153ca6dcde37..c19099af6014f 100644 --- a/src/pybind/mgr/cephadm/inventory.py +++ b/src/pybind/mgr/cephadm/inventory.py @@ -29,6 +29,7 @@ logger = logging.getLogger(__name__) HOST_CACHE_PREFIX = "host." SPEC_STORE_PREFIX = "spec." AGENT_CACHE_PREFIX = 'agent.' +NODE_PROXY_CACHE_PREFIX = 'node_proxy/data' class HostCacheStatus(enum.Enum): @@ -1405,6 +1406,85 @@ class HostCache(): return self.scheduled_daemon_actions.get(host, {}).get(daemon) +class NodeProxyCache: + def __init__(self, mgr: "CephadmOrchestrator") -> None: + self.mgr = mgr + self.data: Dict[str, Any] = {} + self.idrac = {} + + def load(self) -> None: + _idrac = self.mgr.get_store('node_proxy/idrac', "{}") + self.idrac = json.loads(_idrac) + + for k, v in self.mgr.get_store_prefix(NODE_PROXY_CACHE_PREFIX).items(): + host = k.split('/')[-1:][0] + + if host not in self.mgr.inventory.keys(): + # remove entry for host that no longer exists + self.mgr.set_store(f"{NODE_PROXY_CACHE_PREFIX}/{host}", None) + try: + self.idrac.pop(host) + self.data.pop(host) + except KeyError: + pass + continue + + self.data[host] = json.loads(v) + + def save(self, + host: str = '', + data: Dict[str, Any] = {}) -> None: + self.mgr.set_store(f"{NODE_PROXY_CACHE_PREFIX}/{host}", json.dumps(data)) + + def fullreport(self, **kw: Any) -> Dict[str, Any]: + hostname = kw.get('hostname') + if hostname not in self.data.keys(): + return self.data + else: + return self.data[hostname] + + def summary(self, **kw: Any) -> Dict[str, Any]: + hostname = kw.get('hostname') + results = self.data + + mapper: Dict[bool, str] = { + True: 'error', + False: 'ok' + } + + _result: Dict[str, Any] = {} + + for host, data in results.items(): + _result[host] = {} + for component, details in data.items(): + res = any([member['status']['health'].lower() != 'ok' for member in data[component].values()]) + _result[host][component] = mapper[res] + + if hostname and hostname in results.keys(): + return _result[hostname] + else: + return _result + + def common(self, **kw): + hostname = kw.get('hostname',) + cmd = kw.get('cmd',) + _result = {} + + for host, data in self.data.items(): + try: + _result[host] = data[cmd] + except KeyError: + raise RuntimeError(f'Invalid node-proxy category {cmd}') + + if hostname and hostname in self.data.keys(): + return _result[hostname] + else: + return _result + + def criticals(self, **kw): + return {} + + class AgentCache(): """ AgentCache is used for storing metadata about agent daemons that must be kept diff --git a/src/pybind/mgr/cephadm/module.py b/src/pybind/mgr/cephadm/module.py index 010aca85fa085..c0f04ecf1cbe2 100644 --- a/src/pybind/mgr/cephadm/module.py +++ b/src/pybind/mgr/cephadm/module.py @@ -66,7 +66,7 @@ from .services.monitoring import GrafanaService, AlertmanagerService, Prometheus from .services.jaeger import ElasticSearchService, JaegerAgentService, JaegerCollectorService, JaegerQueryService from .schedule import HostAssignment from .inventory import Inventory, SpecStore, HostCache, AgentCache, EventStore, \ - ClientKeyringStore, ClientKeyringSpec, TunedProfileStore + ClientKeyringStore, ClientKeyringSpec, TunedProfileStore, NodeProxyCache from .upgrade import CephadmUpgrade from .template import TemplateMgr from .utils import CEPH_IMAGE_TYPES, RESCHEDULE_FROM_OFFLINE_HOSTS_TYPES, forall_hosts, \ @@ -584,6 +584,9 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule, self.cache = HostCache(self) self.cache.load() + self.node_proxy = NodeProxyCache(self) + self.node_proxy.load() + self.agent_cache = AgentCache(self) self.agent_cache.load() diff --git a/src/pybind/mgr/cephadm/serve.py b/src/pybind/mgr/cephadm/serve.py index 5dfdc27a3ff50..2a95e5a9a1f38 100644 --- a/src/pybind/mgr/cephadm/serve.py +++ b/src/pybind/mgr/cephadm/serve.py @@ -116,6 +116,9 @@ class CephadmServe: if self.mgr.upgrade.continue_upgrade(): continue + # refresh node-proxy cache + self.mgr.node_proxy.load() + except OrchestratorError as e: if e.event_subject: self.mgr.events.from_orch_error(e) -- 2.39.5