diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/bmc.py b/platform/mellanox/mlnx-platform-api/sonic_platform/bmc.py index 706d68f514d..3eb62339da5 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/bmc.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/bmc.py @@ -413,6 +413,11 @@ def request_cpu_reset(self): return self.rf_client.redfish_api_request_system_reset( sytem_reset_type=RedfishClient.SYSTEM_RESET_TYPE_CPU_RESET, immediate=True) + @with_credential_restore + def request_bmc_reset(self, graceful=True): + bmc_reset_type = RedfishClient.BMC_RESET_TYPE_GRACEFUL_RESTART if graceful else RedfishClient.BMC_RESET_TYPE_FORCE_RESTART + return self.rf_client.redfish_api_request_bmc_reset(bmc_reset_type=bmc_reset_type) + @with_credential_restore def update_components_firmware(self, fw_image, fw_ids=None, force_update=False, progress_callback=None, timeout=1800): logger.log_notice(f'Installing BMC firmware image {fw_image}') diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py index f873ec80008..2afcd1b1cb8 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py @@ -947,7 +947,7 @@ def install_firmware(self, image_path): if ret == 0: print('Successfully upgraded BMC firmware') - # TODO(BMC): Check if power cycle is required for apply the installation + # TODO(BMC): Check if self.bmc.request_bmc_reset is required for apply the installation return True else: print(f'Fail to upgrade BMC firmware. Error {ret}: {error_msg}') @@ -956,7 +956,7 @@ def install_firmware(self, image_path): def get_firmware_version(self): return self.bmc.get_version() - # TODO(BMC): Add get_firmware_update_notification if power cycle is needed + # TODO(BMC): Add get_firmware_update_notification if self.bmc.request_bmc_reset is needed class ComponentBMC(ComponentBMCObj): COMPONENT_NAME = 'BMC' diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/redfish_client.py b/platform/mellanox/mlnx-platform-api/sonic_platform/redfish_client.py index 929b54adce3..19bedc5176f 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/redfish_client.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/redfish_client.py @@ -101,6 +101,7 @@ class RedfishClient: REDFISH_DEBUG_TOKEN = '/redfish/v1/Systems/System_0/LogServices/DebugTokenService' REDFISH_BMC_LOG_DUMP = '/redfish/v1/Managers/BMC_0/LogServices/Dump/Actions' REDFISH_REQUEST_SYSTEM_RESET = '/redfish/v1/Systems/System_0/Actions/ComputerSystem.Reset' + REDFISH_REQUEST_BMC_RESET = '/redfish/v1/Managers/BMC_0/Actions/Manager.Reset' REDFISH_URI_CHASSIS = '/redfish/v1/Chassis' # For now we have only 1 command for doing power cycle, @@ -111,6 +112,10 @@ class RedfishClient: REDFISH_GRACEFULL_POWER_CYCLE = 'PowerCycle' REDFISH_POWER_CYCLE_BYPASS = 'PowerCycleBypass' REDFISH_FORCE_RESTART = 'ForceRestart' + + # BMC reset types + REDFISH_BMC_GRACEFUL_RESTART = 'GracefulRestart' + REDFISH_BMC_FORCE_RESTART = 'ForceRestart' # Error code definitions ERR_CODE_OK = 0 @@ -153,6 +158,15 @@ class RedfishClient: 'PowerCycleBypass' ] + # BMC reset type + BMC_RESET_TYPE_GRACEFUL_RESTART = 0 + BMC_RESET_TYPE_FORCE_RESTART = 1 + + BMC_RESET_TYPE_MAP = [ + 'GracefulRestart', + 'ForceRestart' + ] + ''' Constructor A password_callback parameter is provoided because: @@ -327,6 +341,23 @@ def __build_request_system_reset_cmd(self, system_reset_type, immediate): return cmd + ''' + Build the POST command to request BMC reset + ''' + def __build_request_bmc_reset_cmd(self, bmc_reset_type): + if bmc_reset_type == RedfishClient.BMC_RESET_TYPE_FORCE_RESTART: + reset_type = RedfishClient.REDFISH_BMC_FORCE_RESTART + else: + reset_type = RedfishClient.REDFISH_BMC_GRACEFUL_RESTART + + cmd = f'{self.__curl_path} -k -H "X-Auth-Token: {self.__token}" ' \ + f'-H "Content-Type: application/json" ' \ + f'-X POST https://{self.__svr_ip}' \ + f'{RedfishClient.REDFISH_REQUEST_BMC_RESET} ' \ + f'-d \'{{"ResetType": "{reset_type}"}}\'' + + return cmd + ''' Build the PATCH command to change login password ''' @@ -1613,3 +1644,58 @@ def redfish_api_request_system_reset(self, sytem_reset_type, immediate): err_msg = "Missing 'message' field" return (ret, err_msg) + + ''' + Request BMC to reset itself + + Parameters: + bmc_reset_type BMC_RESET_TYPE_GRACEFUL_RESTART or BMC_RESET_TYPE_FORCE_RESTART + + Return value: (ret, error_msg) + ret return code + error_msg error message string + ''' + def redfish_api_request_bmc_reset(self, bmc_reset_type=None): + if bmc_reset_type is None: + bmc_reset_type = RedfishClient.BMC_RESET_TYPE_GRACEFUL_RESTART + + cmd = self.__build_request_bmc_reset_cmd(bmc_reset_type) + ret, _, response, err_msg = self.exec_curl_cmd(cmd) + json_response = None + + if (ret != RedfishClient.ERR_CODE_OK): + self.log_notice(f'Reset BMC return not OK, ret {ret}, response {response}, err msg {err_msg}') + return (ret, err_msg) + + # When action succeds, doesn't return any response. + # If we got a response, probably it is an error. + # Try to parse it + if response is None or len(response) == 0: + self.log_notice(f'Reset BMC return OK, ret {ret}, err msg {err_msg}') + return (RedfishClient.ERR_CODE_OK, '') + + reset_type = RedfishClient.BMC_RESET_TYPE_MAP[bmc_reset_type] + self.log_notice(f"After requesting BMC {reset_type}, got response {response} and error {err_msg}") + + try: + json_response = json.loads(response) + except json.JSONDecodeError as e: + msg = 'Error: Invalid JSON format' + return (RedfishClient.ERR_CODE_INVALID_JSON_FORMAT, msg) + except Exception as e: + msg = 'Error: unexpected response' + return (RedfishClient.ERR_CODE_UNEXPECTED_RESPONSE, msg) + + if 'error' in json_response: # Error found + err = json_response['error'] + if 'message' in err: + err_msg = err['message'] + ret = RedfishClient.ERR_CODE_GENERIC_ERROR + + if 'ActionParameterUnknown' in err.get('code', ''): + ret = RedfishClient.ERR_CODE_UNSUPPORTED_PARAMETER + else: + ret = RedfishClient.ERR_CODE_UNEXPECTED_RESPONSE + err_msg = "Missing 'message' field" + + return (ret, err_msg)