class CompletionCodeError(Exception):
    """IPMI completion code not OK."""

    def __init__(self, cc, cmdid=None):
        self.cc = cc
        self.cc_desc = self.find_cc_desc(cc)

    def __str__(self):
        return "%s cc=0x%02x desc=%s" \
            % (self.__class__.__name__, self.cc, self.cc_desc)

    @staticmethod
    def find_cc_desc(error_cc):
        for cc in COMPLETION_CODE_DESCR:
            if error_cc == cc[0]:
                return cc[1]
        return "Unknown error description" class HpmError(Exception):
    """HPM.1 error."""
    pass class IpmiTimeoutError(Exception):
    """Timeout occurred."""
    pass def create_request_by_name(self, name, *args, **kwargs):
    return self.registry[name + "Req"](*args, **kwargs) CC_OK = 0x00
CC_NODE_BUSY = 0xc0
CC_INV_CMD = 0xc1
CC_INV_CMD_FOR_LUN = 0xc2
CC_TIMEOUT = 0xc3
CC_OUT_OF_SPACE = 0xc4
CC_RES_CANCELED = 0xc5
CC_REQ_DATA_TRUNC = 0xc6
CC_REQ_DATA_INV_LENGTH = 0xc7
CC_REQ_DATA_FIELD_EXCEED = 0xc8
CC_PARAM_OUT_OF_RANGE = 0xc9
CC_CANT_RET_NUM_REQ_BYTES = 0xca
CC_REQ_DATA_NOT_PRESENT = 0xcb
CC_INV_DATA_FIELD_IN_REQ = 0xcc
CC_ILL_SENSOR_OR_RECORD = 0xcd
CC_RESP_COULD_NOT_BE_PRV = 0xce
CC_CANT_RESP_DUPLI_REQ = 0xcf
CC_CANT_RESP_SDRR_UPDATE = 0xd0
CC_CANT_RESP_FIRM_UPDATE = 0xd1
CC_CANT_RESP_BMC_INIT = 0xd2
CC_DESTINATION_UNAVAILABLE = 0xd3
CC_INSUFFICIENT_PRIVILEGES = 0xd4
CC_NOT_SUPPORTED_PRESENT_STATE = 0xd5
CC_ILLEGAL_COMMAND_DISABLED = 0xd6
CC_UNSPECIFIED_ERROR = 0xff
COMPLETION_CODE_DESCR = (
    (CC_OK, 'Command Completed Normally'),
    (CC_NODE_BUSY, 'Node Busy'),
    (CC_INV_CMD, 'Invalid Command'),
    (CC_INV_CMD_FOR_LUN, 'Command invalid for given LUN'),
    (CC_TIMEOUT, 'Timeout while processing command'),
    (CC_OUT_OF_SPACE, 'Out of space'),
    (CC_RES_CANCELED, 'Reservation Canceled or Invalid Reservation ID'),
    (CC_REQ_DATA_TRUNC, 'Request data truncated'),
    (CC_REQ_DATA_INV_LENGTH, 'Request data length invalid'),
    (CC_REQ_DATA_FIELD_EXCEED, 'Request data field length limit exceeded'),
    (CC_PARAM_OUT_OF_RANGE, 'Parameter out of range'),
    (CC_CANT_RET_NUM_REQ_BYTES,
     'Cannot return number of requested data bytes'),
    (CC_REQ_DATA_NOT_PRESENT, 'Requested data not present'),
    (CC_INV_DATA_FIELD_IN_REQ, 'Invalid data field in Request'),
    (CC_ILL_SENSOR_OR_RECORD,
     'Command illegal for specified sensor or record'),
    (CC_RESP_COULD_NOT_BE_PRV, 'Command response could not be provided'),
    (CC_CANT_RESP_DUPLI_REQ, 'Cannot execute duplicated request'),
    (CC_CANT_RESP_SDRR_UPDATE,
     'Command response could not be provided. SDRR in update mode'),
    (CC_CANT_RESP_FIRM_UPDATE,
     'Command response could not be provided. Device in firmware '
     'update mode'),
    (CC_CANT_RESP_BMC_INIT,
     'Command response could not be provided. BMC initialization or '
     'initialization agent in progress'),
    (CC_DESTINATION_UNAVAILABLE, 'Destination unavailable'),
    (CC_INSUFFICIENT_PRIVILEGES,
     'Cannot execute command due to insufficient privilege level'),
    (CC_NOT_SUPPORTED_PRESENT_STATE,
     'Cannot execute command. Not supported in present state'),
    (CC_ILLEGAL_COMMAND_DISABLED,
     'Cannot execute command. Command sub-function has been disabled '
     'or is unavailable'),
    (CC_UNSPECIFIED_ERROR, 'Unspecified error'),
)
NETFN_CHASSIS = 0x00
NETFN_BRIDGE = 0x02
NETFN_SENSOR_EVENT = 0x04
NETFN_APP = 0x06
NETFN_FIRMWARE = 0x08
NETFN_STORAGE = 0x0a
NETFN_TRANSPORT = 0x0c
NETFN_GROUP_EXTENSION = 0x2c
NETFN_OEM = 0x2e
NETFN_DESCR = (
    (NETFN_CHASSIS, 'Chassis'),
    (NETFN_BRIDGE, 'Bridge'),
    (NETFN_SENSOR_EVENT, 'Sensor/Event'),
    (NETFN_APP, 'App'),
    (NETFN_FIRMWARE, 'Firmware'),
    (NETFN_STORAGE, 'Storage'),
    (NETFN_TRANSPORT, 'Transport'),
    (NETFN_GROUP_EXTENSION, 'Group Extension'),
    (NETFN_OEM, 'IANA'),
)
CMDID_GET_DEVICE_ID = 0x01
CMDID_COLD_RESET = 0x02
CMDID_WARM_RESET = 0x03
CMDID_GET_SELF_TEST_RESULTS = 0x04
CMDID_MANUFACTURING_TEST_ON = 0x05
CMDID_SET_ACPI_POWER_STATE = 0x06
CMDID_GET_ACPI_POWER_STATE = 0x07
CMDID_GET_DEVICE_GUID = 0x08
CMDID_GET_NETFN_SUPPORT = 0x09
CMDID_GET_COMMAND_SUPPORT = 0x0a
CMDID_GET_COMMAND_SUBFUNCTION_SUPPORT = 0x0b
CMDID_GET_CONFIGURATION_COMMANDS = 0x0c
CMDID_GET_CONFIGURATION_SUBFUNCTION_COMMANDS = 0x0d
CMDID_SET_COMMAND_ENABLES = 0x60
CMDID_GET_COMMAND_ENABLES = 0x61
CMDID_SET_COMMAND_SUBFUNCTION_ENABLES = 0x62
CMDID_GET_COMMAND_SUBFUNCTION_ENABLES = 0x63
CMDID_GET_OEM_NETFN_IANA_SUPPORT = 0x64
CMDID_RESET_WATCHDOG_TIMER = 0x22
CMDID_SET_WATCHDOG_TIMER = 0x24
CMDID_GET_WATCHDOG_TIMER = 0x25
CMDID_SET_BMC_GLOBAL_ENABLES = 0x2e
CMDID_GET_BMC_GLOBAL_ENABLES = 0x2f
CMDID_CLEAR_MESSAGE_FLAGS = 0x30
CMDID_GET_MESSAGE_FLAGS = 0x31
CMDID_ENABLE_MESSAGE_CHANNEL_RECEIVE = 0x32
CMDID_GET_MESSAGE = 0x33
CMDID_SEND_MESSAGE = 0x34
CMDID_READ_EVENT_MESSAGE_BUFFER = 0x35
CMDID_GET_BT_INTERFACE_CAPABILITIES = 0x36
CMDID_GET_SYSTEM_GUID = 0x37
CMDID_GET_CHANNEL_AUTHENTICATION_CAPABILITIES = 0x38
CMDID_GET_SESSION_CHALLENGE = 0x39
CMDID_ACTIVATE_SESSION = 0x3a
CMDID_SET_SESSION_PRIVILEGE_LEVEL = 0x3b
CMDID_CLOSE_SESSION = 0x3c
CMDID_GET_SESSION_INFO = 0x3d
CMDID_GET_AUTHCODE = 0x3f
CMDID_SET_CHANNEL_ACCESS = 0x40
CMDID_GET_CHANNEL_ACCESS = 0x41
CMDID_GET_CHANNEL_INFO = 0x42
CMDID_SET_USER_ACCESS = 0x43
CMDID_GET_USER_ACCESS = 0x44
CMDID_SET_USER_NAME = 0x45
CMDID_GET_USER_NAME = 0x46
CMDID_SET_USER_PASSWORD = 0x47
CMDID_ACTIVATE_PAYLOAD = 0x48
CMDID_DEACTIVATE_PAYLOAD = 0x49
CMDID_GET_PAYLOAD_ACTITVATION_STATUS = 0x4a
CMDID_GET_PAYLOAD_INSTANCE_INFO = 0x4b
CMDID_SET_USER_PAYLOAD_ACCESS = 0x4c
CMDID_GET_USER_PAYLOAD_ACCESS = 0x4d
CMDID_GET_CHANNEL_PAYLOAD_SUPPORT = 0x4e
CMDID_GET_CHANNEL_PAYLOAD_VERSION = 0x4f
CMDID_GET_CHANNEL_OEM_PAYLOAD_INFO = 0x50
CMDID_MASTER_WRITE_READ = 0x52
CMDID_GET_CHANNEL_CIPHER_SUITES = 0x54
CMDID_SUSPEND_RESUME_PAYLOAD_ENCRYPTION = 0x55
CMDID_SET_CHANNEL_SECURITY_KEYS = 0x56
CMDID_GET_SYSTEM_INTERFACE_CAPABILITIES = 0x57
CMDID_SET_SYSTEM_INFO_PARAMETERS = 0x58
CMDID_GET_SYSTEM_INFO_PARAMETERS = 0x59
CMDID_GET_CHASSIS_CAPABILITIES = 0x00
CMDID_GET_CHASSIS_STATUS = 0x01
CMDID_CHASSIS_CONTROL = 0x02
CMDID_CHASSIS_RESET = 0x03
CMDID_CHASSIS_IDENTIFY = 0x04
CMDID_SET_CHASSIS_CAPABILITIES = 0x05
CMDID_SET_POWER_RESTORE_POLICY = 0x06
CMDID_GET_SYSTEM_RESTART_CAUSE = 0x07
CMDID_SET_SYSTEM_BOOT_OPTIONS = 0x08
CMDID_GET_SYSTEM_BOOT_OPTIONS = 0x09
CMDID_GET_POH_COUNTER = 0x0f
CMDID_SET_EVENT_RECEIVER = 0x00
CMDID_GET_EVENT_RECEIVER = 0x01
CMDID_PLATFORM_EVENT = 0x02
CMDID_GET_PEF_CAPABILITIES = 0x10
CMDID_ARM_PEF_POSTPONE_TIMER = 0x11
CMDID_SET_PEFCONFIGURATION_PARAMETERS = 0x12
CMDID_GET_PEFCONFIGURATION_PARAMETERS = 0x13
CMDID_SET_LAST_PROCESSED_EVENT_ID = 0x14
CMDID_GET_LAST_PROCESSED_EVENT_ID = 0x15
CMDID_ALERT_IMMEDIATE = 0x16
CMDID_PET_ACKNOWLEDGE = 0x17
CMDID_GET_DEVICE_SDR_INFO = 0x20
CMDID_GET_DEVICE_SDR = 0x21
CMDID_RESERVE_DEVICE_SDR_REPOSITORY = 0x22
CMDID_GET_SENSOR_READING_FACTOR = 0x23
CMDID_SET_SENSOR_HYSTERESIS = 0x24
CMDID_GET_SENSOR_HYSTERESIS = 0x25
CMDID_SET_SENSOR_THRESHOLD = 0x26
CMDID_GET_SENSOR_THRESHOLD = 0x27
CMDID_SET_SENSOR_EVENT_ENABLE = 0x28
CMDID_GET_SENSOR_EVENT_ENABLE = 0x29
CMDID_RE_ARM_SENSOR = 0x2a
CMDID_GET_SENSOR_EVENT_STATUS = 0x2b
CMDID_GET_SENSOR_READING = 0x2d
CMDID_SET_SENSOR_TYPE = 0x2e
CMDID_GET_SENSOR_TYPE = 0x2f
CMDID_SET_SENSOR_READING_AND_EVENT_STATUS = 0x30
CMDID_GET_FRU_INVENTORY_AREA_INFO = 0x10
CMDID_READ_FRU_DATA = 0x11
CMDID_WRITE_FRU_DATA = 0x12
CMDID_GET_SDR_REPOSITORY_INFO = 0x20
CMDID_GET_SDR_REPOSITORY_ALLOCATION_INFO = 0x21
CMDID_RESERVE_SDR_REPOSITORY = 0x22
CMDID_GET_SDR = 0x23
CMDID_ADD_SDR = 0x24
CMDID_PARTIAL_ADD_SDR = 0x25
CMDID_DELETE_SDR = 0x26
CMDID_CLEAR_SDR_REPOSITORY = 0x27
CMDID_GET_SDR_REPOSITORY_TIME = 0x28
CMDID_SET_SDR_REPOSITORY_TIME = 0x29
CMDID_ENTER_SDR_REPOSITORY_UPDATE_MODE = 0x2a
CMDID_EXIT_SDR_REPOSITORY_UPDATE_MODE = 0x2b
CMDID_RUN_INITIALIZATION_AGENT = 0x2c
CMDID_GET_SEL_INFO = 0x40
CMDID_GET_SEL_ALLOCATION_INFO = 0x41
CMDID_RESERVE_SEL = 0x42
CMDID_GET_SEL_ENTRY = 0x43
CMDID_ADD_SEL_ENTRY = 0x44
CMDID_PARTIAL_ADD_SEL_ENTRY = 0x45
CMDID_DELETE_SEL_ENTRY = 0x46
CMDID_CLEAR_SEL = 0x47
CMDID_GET_SEL_TIME = 0x48
CMDID_SET_SEL_TIME = 0x49
CMDID_GET_AUXILIARY_LOG_STATUS = 0x5a
CMDID_SET_AUXILIARY_LOG_STATUS = 0x5b
CMDID_GET_SEL_TIME_UTC_OFFSET = 0x5c
CMDID_SET_SEL_TIME_UTC_OFFSET = 0x5d
CMDID_SET_LAN_CONFIGURATION_PARAMETERS = 0x01
CMDID_GET_LAN_CONFIGURATION_PARAMETERS = 0x02
CMDID_SUSPEND_BMC_ARPS = 0x03
CMDID_GET_IP_UDP_RMCP_STATISTICS = 0x04
CMDID_GET_PICMG_PROPERTIES = 0x00
CMDID_GET_ADDRESS_INFO = 0x01
CMDID_GET_SHELF_ADDRESS_INFO = 0x02
CMDID_SET_SHELF_ADDRESS_INFO = 0x03
CMDID_FRU_CONTROL = 0x04
CMDID_GET_FRU_LED_PROPERTIES = 0x05
CMDID_GET_FRU_LED_COLOR_CAPABILITIES = 0x06
CMDID_SET_FRU_LED_STATE = 0x07
CMDID_GET_FRU_LED_STATE = 0x08
CMDID_SET_IPMB_STATE = 0x09
CMDID_SET_FRU_ACTIVATION_POLICY = 0x0a
CMDID_GET_FRU_ACTIVATION_POLICY = 0x0b
CMDID_SET_FRU_ACTIVATION = 0x0c
CMDID_GET_DEVLOC_RECORD_ID = 0x0d
CMDID_SET_PORT_STATE = 0x0e
CMDID_GET_PORT_STATE = 0x0f
CMDID_COMPUTE_POWER_PROPERTIES = 0x10
CMDID_SET_POWER_LEVEL = 0x11
CMDID_GET_POWER_LEVEL = 0x12
CMDID_RENEGOTIATE_POWER = 0x13
CMDID_GET_FAN_SPEED_PROPERTIES = 0x14
CMDID_SET_FAN_LEVEL = 0x15
CMDID_GET_FAN_LEVEL = 0x16
CMDID_BUSED_RESOURCE = 0x17
CMDID_GET_IPMB_LINK_INFO = 0x18
CMDID_GET_SHELF_MANAGER_IPMB_ADDRESS = 0x1b
CMDID_SET_FAN_POLICY = 0x1c
CMDID_GET_FAN_POLICY = 0x1d
CMDID_FRU_CONTROL_CAPABILITIES = 0x1e
CMDID_FRU_INVENTORY_DEVICE_LOCK_CONTROL = 0x1f
CMDID_FRU_INVENTORY_DEVICE_WRITE = 0x20
CMDID_GET_SHELF_MANAGER_IP_ADDRESSES = 0x21
CMDID_GET_SHELF_POWER_ALLOCATION = 0x22
CMDID_SET_CHANNEL_SIGNALING_CLASS = 0x3b
CMDID_GET_CHANNEL_SIGNALING_CLASS = 0x3c
CMDID_SET_AMC_PORT_STATE = 0x19
CMDID_GET_AMC_PORT_STATE = 0x1a
CMDID_GET_LOCATION_INFO = 0x23
CMDID_POWER_CHANNEL_CONTROL = 0x24
CMDID_GET_POWER_CHANNEL_STATUS = 0x25
CMDID_PM_RESET = 0x26
CMDID_GET_PM_STATUS = 0x27
CMDID_PM_HEARTBEAT = 0x28
CMDID_GET_TELCO_ALARM_CAPABILITY = 0x29
CMDID_SET_TELCO_ALARM_STATE = 0x2a
CMDID_GET_TELCO_ALARM_STATE = 0x2b
CMDID_HPM_GET_TARGET_UPGRADE_CAPABILITIES = 0x2e
CMDID_HPM_GET_COMPONENT_PROPERTIES = 0x2f
CMDID_HPM_ABORT_FIRMWARE_UPGRADE = 0x30
CMDID_HPM_INITIATE_UPGRADE_ACTION = 0x31
CMDID_HPM_UPLOAD_FIRMWARE_BLOCK = 0x32
CMDID_HPM_FINISH_FIRMWARE_UPLOAD = 0x33
CMDID_HPM_GET_UPGRADE_STATUS = 0x34
CMDID_HPM_ACTIVATE_FIRMWARE = 0x35
CMDID_HPM_QUERY_SELFTEST_RESULTS = 0x36
CMDID_HPM_QUERY_ROLLBACK_STATUS = 0x37
CMDID_HPM_INITIATE_MANUAL_ROLLBACK = 0x38
CMDID_VITA_GET_VSO_CAPABILITIES = 0x00
CMDID_GET_DCMI_CAPABILITIES_INFO = 0x01
CMDID_GET_POWER_READING = 0x02
CMDID_GET_POWER_LIMIT = 0x03
CMDID_SET_POWER_LIMIT = 0x04
CMDID_ACTIVATE_DEACTIVATE_POWER_LIMIT = 0x05
CMDID_GET_ASSET_TAG = 0x06
CMDID_GET_DCMI_SENSOR_INFO = 0x07
CMDID_SET_ASSET_TAG = 0x08
CMDID_GET_MANAGEMENT_CONTROLLER_ID_STRING = 0x09
CMDID_SET_MANAGEMENT_CONTROLLER_ID_STRING = 0x0a
REPOSITORY_INITIATE_ERASE = 0xaa
REPOSITORY_GET_ERASE_STATUS = 0x00
REPOSITORY_ERASURE_IN_PROGRESS = 0x0
REPOSITORY_ERASURE_COMPLETED = 0x1 def check_completion_code(cc):
    if cc != constants.CC_OK:
        raise CompletionCodeError(cc) def bcd_search(name):
    # Python 3.9 normalizes 'bcd+' as 'bcd_'
    if name not in ('bcd+', 'bcd'):
        return None
    return codecs.CodecInfo(name='bcd+', encode=bcd_encode, decode=bcd_decode) def chunks(data, count):
    for i in range(0, len(data), count):
        yield data[i:i+count] def py3dec_unic_bytes_fix(dat):
    # python 3 unicode fix
    if _PY3:
        return dat.decode('raw_unicode_escape')
    return dat def py3_array_tobytes(msg):
    if _PY3:
        return msg.tobytes()
    else:
        return msg.tostring() class State(DefaultProperties, ResponseDecoder):
    """This is a container that represents a state.

    The state can have default
    properties that are created by init.
    """

    def __init__(self, rsp=None):
        DefaultProperties.__init__(self)
        ResponseDecoder.__init__(self, rsp) class VersionField(object):
    """This class represent the Version fields defines by IPMI.

    Introduced with HPM the version field can hold additional auxiliary bytes.
    """

    VERSION_FIELD_LEN = 2
    VERSION_WITH_AUX_FIELD_LEN = 6

    def __init__(self, data=None):
        self.major = None
        self.minor = None
        if data:
            self._from_data(data)

    def _from_data(self, data):
        if isinstance(data, str):
            data = [ord(c) for c in data]

        data = array.array('B', data)
        self.version = self._decode_data(data[0:2])
        if len(data) == self.VERSION_WITH_AUX_FIELD_LEN:
            self.auxiliary = data[2:6]

    def __str__(self):
        return self.version_to_string()

    def _decode_data(self, data):
        """`data` is array.array."""
        self.major = data[0]

        if data[1] == 0xff:
            self.minor = data[1]
        elif data[1] <= 0x99:
            self.minor = int(py3_array_tobytes(data[1:2]).decode('bcd+'))
        else:
            raise DecodingError()

    def version_to_string(self):
        return ''.join("%s.%s" % (self.major, self.minor))
import os
import codecs
import struct
import collections
import hashlib
import time
from array import array
from .errors import CompletionCodeError, HpmError, IpmiTimeoutError
from .msgs import create_request_by_name
from .msgs import constants
from .utils import check_completion_code, bcd_search, chunks
from .utils import py3dec_unic_bytes_fix, py3_array_tobytes
from .state import State
from .fields import VersionField
        except CompletionCodeError as e:
            if e.cc == CC_LONG_DURATION_CMD_IN_PROGRESS:
                self.wait_for_long_duration_command(
                            constants.CMDID_HPM_FINISH_FIRMWARE_UPLOAD,
                            timeout, interval)
            else:
                raise HpmError('finish_firmware_upload CC=0x%02x' % e.cc)

    def get_upgrade_status(self):
        return UpgradeStatus(self.send_message_with_name('GetUpgradeStatus'))

    def wait_for_long_duration_command(self, expected_cmd, timeout, interval):

        start_time = time.time()
        while time.time() < start_time + timeout:
            try:
                status = self.get_upgrade_status()
                if status.command_in_progress is not expected_cmd \
                        and status.command_in_progress != 0x34:
                    pass
                if status.last_completion_code \
                        == CC_LONG_DURATION_CMD_IN_PROGRESS:
                    time.sleep(interval)
                else:
                    return
            except IpmiTimeoutError:
                time.sleep(interval)
            except IOError:
                time.sleep(interval)

    def activate_firmware(self, rollback_override=None):
        req = create_request_by_name('ActivateFirmware')
        if rollback_override is not None:
            req.rollback_override_policy = rollback_override
        rsp = self.send_message(req)
        check_completion_code(rsp.completion_code)

    def activate_firmware_and_wait(self, rollback_override=None,
                                   timeout=2, interval=1):
        """Activate and wait for the new uploaded firmware."""
        try:
            self.activate_firmware(rollback_override)
        except CompletionCodeError as e:
            if e.cc == CC_LONG_DURATION_CMD_IN_PROGRESS:
                self.wait_for_long_duration_command(
                            constants.CMDID_HPM_ACTIVATE_FIRMWARE,
                            timeout, interval)
            else:
                raise HpmError('activate_firmware CC=0x%02x' % e.cc)
        except IpmiTimeoutError:
            # controller is in reset and flashed new firmware
            pass

    def query_selftest_results(self):
        return SelfTestResult(
            self.send_message_with_name('QuerySelftestResults'))

    def query_rollback_status(self):
        return RollbackStatus(
            self.send_message_with_name('QueryRollbackStatus'))

    def initiate_manual_rollback(self):
        return RollbackStatus(
            self.send_message_with_name('InitiateManualRollback'))

    def initiate_manual_rollback_and_wait(self, timeout=2, interval=0.1):
        try:
            self.initiate_manual_rollback()
        except CompletionCodeError as e:
            if e.cc == CC_LONG_DURATION_CMD_IN_PROGRESS:
                self.wait_for_long_duration_command(
                            constants.CMDID_HPM_INITIATE_MANUAL_ROLLBACK,
                            60, interval)
            else:
                raise HpmError('activate_firmware CC=0x%02x' % e.cc)
        except IpmiTimeoutError:
            # controller is in reset and flashed new firmware
            pass

    @staticmethod
    def open_upgrade_image(filename):
        return UpgradeImage(filename)

    @staticmethod
    def get_upgrade_version_from_file(filename):
        image = UpgradeImage(filename)
        for action in image.actions:
            if isinstance(action, UpgradeActionRecordUploadForUpgrade):
                return action.firmware_version
        return None

    @staticmethod
    def _do_upgrade_action_backup(image):
        for action in image.actions:
            if isinstance(action, UpgradeActionRecordBackup):
                pass

    @staticmethod
    def _do_upgrade_action_prepare(image):
        for action in image.actions:
            if isinstance(action, UpgradeActionRecordPrepare):
                print("do ACTION_PREPARE_COMPONENT")

    @staticmethod
    def _do_upgrade_action_upload(image):
        for action in image.actions:
            if isinstance(action, UpgradeActionRecordUploadForUpgrade):
                print("do ACTION_UPLOAD_FOR_UPGRADE")

    def preparation_stage(self, image):
        ####################################################
        # match device ID, manfuacturer ID, etc.
        device_id = self.get_device_id()

        header = image.header

        if header.device_id != device_id.device_id:
            raise HpmError('Device ID: image=0x%x device=0x%x'
                           % (header.device_id, device_id.device_id))
        if header.manufacturer_id != device_id.manufacturer_id:

            raise HpmError('Manufacturer ID: image=0x%x device=0x%x'