From fe205412fbef92f1aeade17498d12228823932bc Mon Sep 17 00:00:00 2001 From: Guillaume Abrioux Date: Tue, 19 Sep 2023 07:55:54 +0000 Subject: [PATCH] node-proxy: drop redfish library dependency 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 (cherry picked from commit 9da37815ad48f00088ae028041b4621e91725985) --- .../cephadmlib/node_proxy/baseclient.py | 6 +- .../cephadmlib/node_proxy/redfish_client.py | 121 +++++++++++------- .../cephadmlib/node_proxy/redfish_system.py | 4 +- 3 files changed, 83 insertions(+), 48 deletions(-) diff --git a/src/cephadm/cephadmlib/node_proxy/baseclient.py b/src/cephadm/cephadmlib/node_proxy/baseclient.py index 735dd11e96d94..1415bc3fe1942 100644 --- a/src/cephadm/cephadmlib/node_proxy/baseclient.py +++ b/src/cephadm/cephadmlib/node_proxy/baseclient.py @@ -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: diff --git a/src/cephadm/cephadmlib/node_proxy/redfish_client.py b/src/cephadm/cephadmlib/node_proxy/redfish_client.py index 26f71358593b6..437e4be131a4e 100644 --- a/src/cephadm/cephadmlib/node_proxy/redfish_client.py +++ b/src/cephadm/cephadmlib/node_proxy/redfish_client.py @@ -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 diff --git a/src/cephadm/cephadmlib/node_proxy/redfish_system.py b/src/cephadm/cephadmlib/node_proxy/redfish_system.py index ad556ab8dea96..95c82960eae1c 100644 --- a/src/cephadm/cephadmlib/node_proxy/redfish_system.py +++ b/src/cephadm/cephadmlib/node_proxy/redfish_system.py @@ -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]]: -- 2.39.5