]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
node-proxy: drop redfish library dependency
authorGuillaume Abrioux <gabrioux@ibm.com>
Tue, 19 Sep 2023 07:55:54 +0000 (07:55 +0000)
committerGuillaume Abrioux <gabrioux@ibm.com>
Thu, 25 Jan 2024 14:54:40 +0000 (14:54 +0000)
Given that this library isn't packaged for both
upstream and downstream and we can achieve what it was used for
directly with a lib such `urllib` (basically just auth), let's
drop this dependency.

Signed-off-by: Guillaume Abrioux <gabrioux@ibm.com>
(cherry picked from commit 9da37815ad48f00088ae028041b4621e91725985)

src/cephadm/cephadmlib/node_proxy/baseclient.py
src/cephadm/cephadmlib/node_proxy/redfish_client.py
src/cephadm/cephadmlib/node_proxy/redfish_system.py

index 735dd11e96d9422931438948ccaee4e9c3e75f86..1415bc3fe19428c6ceccb3fa9ee49e8df03a8c78 100644 (file)
@@ -1,4 +1,4 @@
-from typing import Dict
+from typing import Dict, Any
 
 
 class BaseClient:
@@ -10,10 +10,10 @@ class BaseClient:
         self.username = username
         self.password = password
 
-    def login(self) -> None:
+    def login(self) -> Dict[str, Any]:
         raise NotImplementedError()
 
-    def logout(self) -> None:
+    def logout(self) -> Dict[str, Any]:
         raise NotImplementedError()
 
     def get_path(self, path: str) -> Dict:
index 26f71358593b6723635ec2c7932704431581a5c2..437e4be131a4e3f4889ecfb85c07132a4df00431 100644 (file)
@@ -1,54 +1,89 @@
-from redfish.rest.v1 import ServerDownOrUnreachableError, \
-    SessionCreationError, \
-    InvalidCredentialsError
-import redfish
-import sys
-from .util import Logger
+import time
+import datetime
+import ssl
+import json
+from urllib.error import HTTPError, URLError
+from urllib.request import urlopen, Request
 from .baseclient import BaseClient
-from typing import Dict
-
-log = Logger(__name__)
+from .util import Logger
+from typing import Dict, Any, Tuple, Optional
 
 
 class RedFishClient(BaseClient):
-
-    PREFIX = '/redfish/v1'
-
+    PREFIX = '/redfish/v1/'
     def __init__(self,
-                 host: str,
-                 username: str,
-                 password: str) -> None:
-        log.logger.info("redfish client initialization...")
+                 host: str = "",
+                 port: str = "443",
+                 username: str = "",
+                 password: str = ""):
         super().__init__(host, username, password)
-        self.redfish_obj: 'redfish.redfish_client' = None
+        self.log: Logger = Logger(__name__)
+        self.log.logger.info(f"Initializing redfish client {__name__}")
+        self.host: str = f"https://{host}:{port}"
+        self.token: Dict[str, str] = {}
+        self.location: str = ''
+
+    def login(self) -> Dict[str, Any]:
+        self.log.logger.info(f"Logging in to {self.host} as '{self.username}'")
+        idrac_credentials = json.dumps({"UserName": self.username, "Password": self.password})
+        headers = {"Content-Type": "application/json"}
 
-    def login(self) -> 'redfish.redfish_client':
-        self.redfish_obj = redfish.redfish_client(base_url=self.host,
-                                                  username=self.username,
-                                                  password=self.password,
-                                                  default_prefix=self.PREFIX)
         try:
-            # TODO: add a retry? check for a timeout setting
-            self.redfish_obj.login(auth="session")
-            log.logger.info(f"Logging to redfish api at {self.host} with user: {self.username}")
-            return self.redfish_obj
-        except InvalidCredentialsError as e:
-            log.logger.error(f"Invalid credentials for {self.username} at {self.host}:\n{e}")
-        except (SessionCreationError, ServerDownOrUnreachableError) as e:
-            log.logger.error(f"Server not reachable or does not support RedFish:\n{e}")
-        sys.exit(1)
-
-    def get_path(self, path: str) -> Dict:
+            _headers, _data = self.query(data=idrac_credentials,
+                                         headers=headers,
+                                         endpoint='/redfish/v1/SessionService/Sessions/')
+        except URLError as e:
+            self.log.logger.error(f"Can't log in to {self.host} as '{self.username}'.\n{e}")
+            return {}
+        self.token = {"X-Auth-Token": _headers['X-Auth-Token']}
+        self.location = _headers['Location']
+
+        return json.loads(_data)
+
+    def logout(self) -> Dict[str, Any]:
         try:
-            if self.PREFIX not in path:
-                path = f"{self.PREFIX}{path}"
-            log.logger.debug(f"getting: {path}")
-            response = self.redfish_obj.get(path)
-            return response.dict
-        except Exception as e:
-            log.logger.error(f"Error getting path:\n{e}")
+            _, _data = self.query(method='DELETE', headers=self.token, endpoint=self.location)
+        except URLError as e:
+            self.log.logger.error(f"Can't log out from {self.host}")
             return {}
 
-    def logout(self) -> None:
-        log.logger.info('logging out...')
-        self.redfish_obj.logout()
+        response_str = _data
+
+        return json.loads(response_str)
+
+    def get_path(self, path: str) -> Dict[str, Any]:
+        if self.PREFIX not in path:
+            path = f"{self.PREFIX}{path}"
+        try:
+            _, result = self.query(headers=self.token, endpoint=path)
+            result_json = json.loads(result)
+            return result_json
+        except URLError as e:
+            self.log.logger.error(f"Can't get path {path}:\n{e}")
+            return {}
+
+    def query(self,
+              data: Optional[str] = None,
+              headers: Dict[str, str] = {},
+              method: Optional[str] = None,
+              endpoint: str = '') -> Tuple[Dict[str, str], str]:
+        url = f'{self.host}{endpoint}'
+
+        # ssl_ctx = ssl.create_default_context()
+        # ssl_ctx.check_hostname = True
+        # ssl_ctx.verify_mode = ssl.CERT_REQUIRED
+        ssl_ctx = ssl._create_unverified_context()
+        _data = bytes(data, 'ascii') if data else None
+        try:
+            req = Request(url, _data, headers=headers, method=method)
+            send_time = time.monotonic()
+            with urlopen(req, context=ssl_ctx) as response:
+                response_str = response.read()
+                response_headers = response.headers
+                response_json = json.loads(response_str)
+                total_request_time = datetime.timedelta(seconds=(time.monotonic() - send_time)).total_seconds()
+        except Exception as e:
+            self.log.logger.error(f"{e}")
+            raise
+
+        return response_headers, response_str
index ad556ab8dea96bbdb40185e70bc230cd71a51e55..95c82960eae1ca2c4fd8e8bd9530885b5f1211e1 100644 (file)
@@ -14,7 +14,7 @@ class RedfishSystem(BaseSystem):
         self.username: str = kw['username']
         self.password: str = kw['password']
         self.system_endpoint = kw.get('system_endpoint', '/Systems/1')
-        self.client = RedFishClient(self.host, self.username, self.password)
+        self.client = RedFishClient(host=self.host, username=self.username, password=self.password)
         self.log.logger.info(f"redfish system initialization, host: {self.host}, user: {self.username}")
 
         self._system: Dict[str, Dict[str, Any]] = {}
@@ -55,7 +55,7 @@ class RedfishSystem(BaseSystem):
 
     def start_client(self) -> None:
         if not self.client:
-            self.client = RedFishClient(self.host, self.username, self.password)
+            self.client = RedFishClient(host=self.host, username=self.username, password=self.password)
         self.client.login()
 
     def get_system(self) -> Dict[str, Dict[str, Dict]]: