]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/cephadm: add node-proxy endpoints to the mgr
authorGuillaume Abrioux <gabrioux@ibm.com>
Thu, 14 Sep 2023 15:41:32 +0000 (15:41 +0000)
committerGuillaume Abrioux <gabrioux@ibm.com>
Thu, 25 Jan 2024 14:43:30 +0000 (14:43 +0000)
This adds 2 endpoints to the existing http agent endpoint:

- '/node_proxy/idrac': support POST requests only although this endpoint
  is intended for fetching the idrac credentials of a given node. As we pass
  sensitive details (ceph secret) I didn't want to pass it as a query parameter
  in the url. Passing it in a HTTP header is perhaps a better approach but we already
  do similar thing for endpoint '/data' (agent) so for consistency reason I stick to
  that.

- '/node_proxy/data': support GET and POST requests. A GET will return the
  aggregated data for all nodes within the cluster. node-proxy will use a POST
  request to that endpoint to push its collected data.

Signed-off-by: Guillaume Abrioux <gabrioux@ibm.com>
src/cephadm/cephadm.py
src/pybind/mgr/cephadm/agent.py

index efdd85c9fdff6b0ce003926b12a9ad708b752684..c0625a0f39bb1c2fc1044ef7535aaa01f6376135 100755 (executable)
@@ -1477,6 +1477,10 @@ class CephadmAgent(DaemonForm):
         self.pull_conf_settings()
 
         t_node_proxy = Thread(target=cephadmlib.node_proxy.server.main)
+        ssl_ctx = ssl.create_default_context()
+        ssl_ctx.check_hostname = True
+        ssl_ctx.verify_mode = ssl.CERT_REQUIRED
+        ssl_ctx.load_verify_locations(self.ca_path)
         t_node_proxy.start()
 
         try:
@@ -1499,11 +1503,6 @@ class CephadmAgent(DaemonForm):
         if not self.volume_gatherer.is_alive():
             self.volume_gatherer.start()
 
-        ssl_ctx = ssl.create_default_context()
-        ssl_ctx.check_hostname = True
-        ssl_ctx.verify_mode = ssl.CERT_REQUIRED
-        ssl_ctx.load_verify_locations(self.ca_path)
-
         while not self.stop:
             start_time = time.monotonic()
             ack = self.ack
index 93a08cb3439bdb9000af7b15fb62f067266caa30..40853f7806ef55f081946effb784bf8f587f7ef8 100644 (file)
@@ -57,6 +57,12 @@ class AgentEndpoint:
         d.connect(name='host-data', route='/data/',
                   controller=self.host_data.POST,
                   conditions=dict(method=['POST']))
+        d.connect(name='node-proxy-idrac', route='/node-proxy/idrac',
+                  controller=self.host_data.node_proxy_idrac,
+                  conditions=dict(method=['POST']))
+        d.connect(name='node-proxy-data', route='/node-proxy/data',
+                  controller=self.host_data.node_proxy_data,
+                  conditions=dict(method=['GET','POST']))
         cherrypy.tree.mount(None, '/', config={'/': {'request.dispatch': d}})
 
     def configure_tls(self, server: Server) -> None:
@@ -126,6 +132,56 @@ class HostData(Server):
             results['result'] = self.handle_metadata(data)
         return results
 
+    def validate_node_proxy_data(self, data: Dict[str, Any]) -> bool:
+        if 'host' not in data:
+            cherrypy.response.status = 400
+            self.mgr.log.warning('The field \'host\' must be provided.')
+        elif 'keyring' not in data:
+            cherrypy.response.status = 400
+            self.mgr.log.warning(f'The agent keyring must be provided.')
+        elif not self.mgr.agent_cache.agent_keys.get(data['host']):
+            cherrypy.response.status = 400
+            self.mgr.log.warning(f'Make sure the agent is running on {data["host"]}')
+        elif data['keyring'] != self.mgr.agent_cache.agent_keys[data['host']]:
+            cherrypy.response.status = 403
+            self.mgr.log.warning(f'Got wrong keyring from agent on host {data["host"]}.')
+        else:
+            cherrypy.response.status = 200
+
+        return cherrypy.response.status == 200
+
+    @cherrypy.tools.json_in()
+    @cherrypy.tools.json_out()
+    def node_proxy_idrac(self) -> Dict[str, Any]:
+        data: Dict[str, Any] = cherrypy.request.json
+        results: Dict[str, Any] = {}
+
+        if self.validate_node_proxy_data(data):
+            idrac_details = self.mgr.get_store('node_proxy/idrac')
+            idrac_details_json = json.loads(idrac_details)
+            self.mgr.log.warning(f"{idrac_details_json}")
+            results['result'] = idrac_details_json[data["host"]]
+
+        return results
+
+    @cherrypy.tools.json_in()
+    @cherrypy.tools.json_out()
+    def node_proxy_data(self) -> Dict[str, Any]:
+        results: Dict[str, Any] = {}
+
+        if cherrypy.request.method == 'POST':
+            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']))
+                self.mgr.log.warning(f"{data}")
+                results['result'] = data
+
+        if cherrypy.request.method == 'GET':
+            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
+
     def check_request_fields(self, data: Dict[str, Any]) -> None:
         fields = '{' + ', '.join([key for key in data.keys()]) + '}'
         if 'host' not in data: