Beispiel #1
0
def _get_power_reading(username=None, password=None, ip_address=None, port=BMC_PORT):
    """ Internal wrapper for the Get Power Reading DCMI command.

    Args:
        username (str): The username to connect to BMC with.
        password (str): The password to connect to BMC with.
        ip_address (str): The IP Address of the BMC.
        port (int): BMC port

    Returns:
        dict: Power reading information from the remote system.

    Raises:
        OpenDCREException in cases where power reading is not active.
    """
    with IpmiCommand(ip_address=ip_address, username=username, password=password, port=port) as ipmicmd:
        result = ipmicmd.raw_command(netfn=0x2c, command=0x02, data=(0xdc, 0x01, 0x00, 0x00))
        if 'error' in result:
            raise OpenDCREException(
                'Error executing DCMI power reading command on {} : {}'.format(ip_address, result['error'])
            )

        # if power measurement is inactive, we may be giving back back data.
        if (result['data'][17] >> 6) & 0x01 == 0x00:
            raise OpenDCREException('Error reading DCMI power - power measurement is not active.')

        return {'input_power': float(result['data'][1] | (result['data'][2] << 8))}
Beispiel #2
0
def get_dcmi_management_controller_id(username=None, password=None, ip_address=None, port=BMC_PORT):
    """ Get DCMI management controller ID from remote BMC.

    Args:
        username (str): Username to connect to BMC with.
        password (str): Password to connect to BMC with.
        ip_address (str): BMC IP Address.
        port (int): BMC port

    Returns:
        str: The management controller ID.  Raises an Exception if error.
    """
    id_string = ''

    # read the mgmt controller ID out in blocks of 16 bytes, up to the limit of 64 bytes
    with IpmiCommand(ip_address=ip_address, username=username, password=password, port=port) as ipmicmd:
        for x in range(0, 4):
            result = ipmicmd.raw_command(netfn=0x2c, command=0x01, data=(0xdc, x * 0x10))
            if 'error' in result:
                raise OpenDCREException(
                    'Error executing get DCMI controller ID command on {} : {}'.format(ip_address, result['error'])
                )

            # byte 0 should always be 0xdc
            if result['data'][0] != 0xdc:
                raise OpenDCREException(
                    'Error in response to get DCMI controller ID command on {}: Invalid first byte.'.format(ip_address)
                )

            # byte 1 should be the string length - ignore as remaining bytes are 0x00 (null)

            # tack on the remaining characters to the id string
            id_string.join([chr(c) for c in result['data'][2:]])

    return id_string
Beispiel #3
0
def set_boot(username=None, password=None, ip_address=None, port=BMC_PORT, target=None):
    """ Set the boot target on remote host.

    Args:
        username (str): Username to connect to BMC with.
        password (str): Password to connect to BMC with.
        ip_address (str): BMC IP Address.
        port (int): BMC port
        target (str): The boot target to set the remote host to.

    Returns:
        dict: boot target as observed, or IpmiException from pyghmi.

    """
    response = dict()
    with IpmiCommand(ip_address=ip_address, username=username, password=password, port=port) as ipmicmd:
        target = dict(pxe='network', hdd='hd', no_override='default').get(target)
        result = ipmicmd.set_bootdev(bootdev=target)
        if 'error' in result:
            raise OpenDCREException(
                'Error setting boot device from IPMI BMC {} : {}'.format(ip_address, result['error'])
            )

        # FIXME (etd) - here, if bootdev doesn't exist, we use 'unknown'. in the dict `get`
        # in the following line, there is no record for unknown, so it will default to the
        # value of `None` -- is this desired?
        bootdev = result.get('bootdev', 'unknown')
        bootdev = dict(network='pxe', hd='hdd', default='no_override').get(bootdev)
        response['target'] = bootdev
        return response
Beispiel #4
0
def read_sensor(sensor_name, username=None, password=None, ip_address=None, port=BMC_PORT):
    """ Get a converted sensor reading back from the remote system for a given sensor_name.

    Args:
        username (str): Username to connect to BMC with.
        password (str): Password to connect to BMC with.
        ip_address (str): BMC IP Address.
        port (int): BMC port
        sensor_name (str): the id_string of the sensor to read.
            (NB ABC: this is a vestige of pyghmi, it would be better to read by sensor number)

    Returns:
        dict: the converted sensor reading for the given sensor_name. Will raise an IpmiException
            if the the sensor is not available (e.g. the power is off).
    """
    if sensor_name is not None:
        result = dict()
        with IpmiCommand(ip_address=ip_address, username=username, password=password, port=port) as ipmicmd:
            reading = ipmicmd.get_sensor_reading(sensor_name)
            result['sensor_reading'] = reading.value
            result['health'] = _convert_health_to_string(reading.health)
            result['states'] = reading.states
            return result

    raise ValueError('Must specify a sensor name when retrieving sensor reading via IPMI.')
Beispiel #5
0
def _get_temperature_readings(username=None, password=None, ip_address=None, port=BMC_PORT, entity=None):
    """ Internal wrapper for Get Temperature Reading DCMI command.

    Args:
        username (str): The username to connect to BMC with.
        password (str): The password to connect to BMC with.
        ip_address (str): The IP address of the BMC to connect to.
        port (int): BMC port
        entity (str): Which entity to get temperature readings for:
            'inlet', 'cpu', and 'system_board'.

    Returns:
        dict: A dictionary of 'readings' containing a list of readings retrieved for
            the given entity type. These readings include entity and instance IDs for
            use elsewhere, in addition to a temperature_c reading.
    """
    with IpmiCommand(ip_address=ip_address, username=username, password=password, port=port) as ipmicmd:
        cmd_data = [0xdc, 0x01]
        if entity == 'inlet':
            cmd_data.append(0x40)       # entity ID (air inlet)
            cmd_data.append(0x00)       # instance ID (0x00 means return all instances)
            cmd_data.append(0x01)       # starting instance ID (0x01 means start with the first)
        elif entity == 'cpu':
            cmd_data.append(0x41)
            cmd_data.append(0x00)
            cmd_data.append(0x01)
        elif entity == 'system_board':
            cmd_data.append(0x41)
            cmd_data.append(0x00)
            cmd_data.append(0x01)
        else:
            raise ValueError('Invalid DCMI entity type specified for temperature reading command.')

        result = ipmicmd.raw_command(netfn=0x2c, command=0x10, data=tuple(cmd_data))
        if 'error' in result:
            raise OpenDCREException(
                'Error executing DCMI temperature readings command on {} : {}'.format(ip_address, result['error'])
            )

        readings = {'readings': []}

        """ TODO: read byte 1 instead which has the actual total number of responses, as opposed to
         byte 2 which has the total in the response for a single command (no more than 8).  This means
         we will iterate via the starting instance id through all of the possible instances until none remain.
        """
        start_byte = 3  # start on first byte of reading
        for x in range(0, result['data'][2]):
            # get the reading from the lower 7 bits
            temperature = float(result['data'][start_byte] & 0b01111111)
            # if the upper bit is a 1, the sign is negative
            if result['data'][start_byte] >> 7 & 0b01 == 0b01:
                temperature *= -1.0
            instance = result['data'][start_byte + 1]
            start_byte += 2
            readings['readings'].append([{'temperature_c': temperature, 'entity': entity, 'instance': instance}])
        return readings
def get_flex_victoria_power_reading(username=None, password=None, ip_address=None, port=BMC_PORT):
    """ Flex Ciii Victoria 2508 power reading retrieval.

    Uses master r/w command to retrieve the power status from the two PSUs; the readings are then summed together
    and returned.  As with many other commands, a 'good' power reading is only produced when chassis power is 'on'.
    Otherwise, we end up seeing readings that are just 0W.  If a PSU is missing, we still read both PSUs, and sum
    what we've got.  Even if only 1/2 of the PSUs are present, we can still get an accurate reading.

    Args:
        username (str): The username to connect to BMC with.
        password (str): The password to connect to BMC with.
        ip_address (str): The IP Address of the BMC.
        port (int): BMC port

    Returns:
        dict: Power reading information from the remote system.

    Raises:
        OpenDCRException: in cases where BMC is unreachable or an error is encountered processing the command.
    """
    psu0_power = 0
    psu1_power = 0

    with IpmiCommand(ip_address=ip_address, username=username, password=password, port=port) as ipmicmd:
        # get PSU0 consumption
        try:
            result = ipmicmd.raw_command(netfn=0x06, command=0x52, data=(0xa0, 0xb0, 0x02, 0x96))
            if 'error' in result:
                raise OpenDCREException(
                    'Error executing master r/w command on {} : {}'.format(ip_address, result['error'])
                )
            psu0_power = _convert_linear_11((result['data'][1] << 8) | result['data'][0])
        except Exception:
            # PSU 0 or 1 may be missing, which is fine, so no action needed
            pass

        # get PSU1 consumption
        try:
            result = ipmicmd.raw_command(netfn=0x06, command=0x52, data=(0xa0, 0xb2, 0x02, 0x96))
            if 'error' in result:
                raise OpenDCREException(
                    'Error executing master r/w command on {} : {}'.format(ip_address, result['error'])
                )
            psu1_power = _convert_linear_11((result['data'][1] << 8) | result['data'][0])
        except Exception:
            # PSU 0 or 1 may be missing, which is fine, so no action needed
            pass

    return {'input_power': float(psu0_power + psu1_power)}
Beispiel #7
0
def set_identify(username=None, password=None, ip_address=None, port=BMC_PORT, led_state=None):
    """ Turn the remote system LED on or off.

    Args:
        username (str): Username to connect to BMC with.
        password (str): Password to connect to BMC with.
        ip_address (str): BMC IP Address.
        port (int): BMC port
        led_state (int): 1 == Force on, 0 == Force off.

    Returns:
        int: LED State as set.
    """
    # Force on if True, Force off if False (indefinite duration)
    state = led_state == 1
    with IpmiCommand(ip_address=ip_address, username=username, password=password, port=port) as ipmicmd:
        ipmicmd.set_identify(on=state)
        return {'led_state': led_state}
Beispiel #8
0
def sensors(username=None, password=None, ip_address=None, port=BMC_PORT):
    """ Get list of sensors from remote system.

    Args:
        username (str): The username to use to connect to the remote BMC.
        password (str): The password to use to connect to the remote BMC.
        ip_address (str): The IP Address of the BMC.
        port (int): BMC port

    Returns:
        list: Sensor number, id string, and type for each sensor available.
    """
    with IpmiCommand(ip_address=ip_address, username=username, password=password, port=port) as ipmicmd:
        sdr = ipmicmd.init_sdr()
        if sdr is None:
            raise OpenDCREException('Error initializing SDR from IPMI BMC {}'.format(ip_address))
        response = [
            {
                'sensor_number': sensor.sensor_number,
                'id_string': sensor.name,
                'sensor_type': sensor.sensor_type
            } for sensor in sdr.sensors.values()]
        return response
Beispiel #9
0
def get_inventory(username=None, password=None, ip_address=None, port=BMC_PORT):
    """ Get inventory information from the FRU of the remote system.

    Args:
        username (str): The username to connect to BMC with.
        password (str): The password to connect to BMC with.
        ip_address (str): The IP Address of the BMC.
        port (int): BMC port

    Returns:
        dict: inventory information from the remote system.
    """
    response = dict()
    with IpmiCommand(ip_address=ip_address, username=username, password=password, port=port) as ipmicmd:
        result = ipmicmd.get_inventory_of_component('System')
        if 'error' in result:
            raise OpenDCREException(
                'Error retrieving System inventory from IPMI BMC {} : {}'.format(ip_address, result['error'])
            )

        response['chassis_info'] = dict()
        response['chassis_info']['chassis_type'] = result.get('Chassis type', '')
        response['chassis_info']['part_number'] = result.get('Chassis part number', '')
        response['chassis_info']['serial_number'] = result.get('Chassis serial number', '')
        response['board_info'] = dict()
        response['board_info']['manufacturer'] = result.get('Board manufacturer', '')
        response['board_info']['part_number'] = result.get('Board part number', '')
        response['board_info']['product_name'] = result.get('Board product name', '')
        response['board_info']['serial_number'] = result.get('Board serial number', '')
        response['product_info'] = dict()
        response['product_info']['asset_tag'] = result.get('Asset Number', '')
        response['product_info']['part_number'] = result.get('Model', '')
        response['product_info']['manufacturer'] = result.get('Manufacturer', '')
        response['product_info']['product_name'] = result.get('Product name', '')
        response['product_info']['serial_number'] = result.get('Serial Number', '')
        response['product_info']['version'] = result.get('Hardware Version', '')
        return response
Beispiel #10
0
def get_dcmi_capabilities(username=None, password=None, ip_address=None, port=BMC_PORT, parameter_selector=None):
    """ Get DCMI capabilities from the remote BMC.

    Args:
        username (str): Username to connect to BMC with.
        password (str): Password to connect to BMC with.
        ip_address (str): BMC IP Address.
        port (int): BMC port
        parameter_selector (int): The ID of the parameter to retrieve.

    Returns:
        dict: The formatted DCMI capabilities.  Raises an OpenDCREException if error.
    """
    if parameter_selector is None or (parameter_selector < 1 or parameter_selector > 5):
        raise ValueError('Invalid parameter selector provided to get_dcmi_capabilities.')

    with IpmiCommand(ip_address=ip_address, username=username, password=password, port=port) as ipmicmd:
        result = ipmicmd.raw_command(netfn=0x2c, command=0x01, data=(0xdc, parameter_selector))
        if 'error' in result:
            raise OpenDCREException(
                'Error executing get DCMI capabilities command on {} : {}'.format(ip_address, result['error'])
            )

        # byte 0 should always be 0xdc
        if result['data'][0] != 0xdc:
            raise OpenDCREException(
                'Error in response to get DCMI capabilities command on {}: Invalid first byte.'.format(ip_address)
            )

        # to know power reading command is truly supported, we require DCMI 1.5
        if result['data'][1] != 0x01 or result['data'][2] != 0x05:
            raise OpenDCREException('DCMI Version 1.5 is required for support by OpenDCRE.')

        # byte 3 must be 0x02 in DCMI 1.5
        if result['data'][3] != 0x02:
            raise OpenDCREException(
                'Error in response to get DCMI capabilities command on {}: Parameter revision must be 0x02.'.format(ip_address)
            )

        response = dict()

        if parameter_selector == 0x01:
            # byte 1 is reserved
            response['power_management'] = bool(result['data'][5] & 0x01)
            response['secondary_lan_available'] = bool((result['data'][6] >> 2) & 0x01)
            response['serial_tmode_available'] = bool((result['data'][6] >> 1) & 0x01)
            response['in_band_channel_available'] = bool(result['data'][6] & 0x01)

        elif parameter_selector == 0x02:
            response['number_sel_entries'] = (result['data'][5] & 0b1111) << 8 | result['data'][6]
            response['record_flush_on_sel_rollover'] = bool((result['data'][6] >> 6) & 0x01)
            response['entire_sel_flush_on_sel_rollover'] = bool((result['data'][6] >> 7) & 0x01)
            response['sel_rollover_flush_enabled'] = bool((result['data'][6] >> 8) & 0x01)
            # bytes 7..8 reserved
            response['temp_monitoring_sample_freq_sec'] = result['data'][9]

        elif parameter_selector == 0x03:
            response['power_mgmt_device_address'] = result['data'][5]
            response['power_mgmt_channel_number'] = result['data'][6] >> 4
            response['power_mgmt_device_revision'] = result['data'][6] & 0x0f

        elif parameter_selector == 0x04:
            response['primary_lan_channel_number'] = result['data'][5]
            response['secondary_lan_channel_number'] = result['data'][6]
            response['serial_oob_channel_number'] = result['data'][7]

        elif parameter_selector == 0x05:
            response['num_supported_time_periods'] = result['data'][5]
            response['time_periods'] = list()
            for x in range(0, response['num_supported_time_periods']):
                duration_units = 'seconds'
                if (result['data'][6 + x] >> 6) & 0b01:
                    duration_units = 'minutes'
                elif (result['data'][6 + x] >> 6) & 0b10:
                    duration_units = 'hours'
                elif (result['data'][6 + x] >> 6) & 0b11:
                    duration_units = 'days'
                response['time_periods'].append(
                    {
                        'duration': (result['data'][6 + x] & 0b111111),
                        'duration_units': duration_units
                    }
                )

        return response
Beispiel #11
0
def power(username=None, password=None, ip_address=None, port=BMC_PORT, cmd=None, reading_method=None):
    """ Get/set power status for remote host.

    Args:
        username (str): Username to connect to BMC with
        password (str): Password to connect to BMC with
        ip_address (str): BMC IP Address
        port (int): BMC port
        cmd (str): 'on', 'off', 'cycle', 'status'
        reading_method (str): designation for routing power consumption readings.
            If 'flex-victoria', then the Flex OEM reading method is used.
            If 'None', then no power reading is used.
            If 'dcmi', then DCMI power reading is used.

    Returns:
        dict: power status after command execution, or raises IpmiException or
            InvalidParameterValue from pyghmi
    """
    # power cycle is hard reset in ipmi/pyghmi
    response = dict()
    with IpmiCommand(ip_address=ip_address, username=username, password=password, port=port) as ipmicmd:
        if cmd == 'status':
            result = ipmicmd.get_power()
            response['power_status'] = result['powerstate']
        elif cmd == 'cycle':
            result = ipmicmd.get_power()
            # only power cycle if the machine is on
            if result['powerstate'] == 'on':
                ipmicmd.set_power('off', wait=15)
                result = ipmicmd.set_power('on', wait=True)
            response['power_status'] = result['powerstate']
        else:
            result = ipmicmd.set_power(cmd, wait=15)
            response['power_status'] = result['powerstate']
        if 'error' in result:
            raise OpenDCREException('Error executing IPMI power command on {} : {}'.format(ip_address, result['error']))
        try:
            # first get additional information about the power health
            chassis_status = _get_chassis_status(username, password, ip_address)
            response['over_current'] = chassis_status.get('power_overload', 'unknown')
            # FIXME (etd): for the below, if there is no 'power_fault', we are evaluating "not 'unknown'" which
            # will be False -- is this expected/desired?
            response['power_ok'] = not chassis_status.get('power_fault', 'unknown')  # invert 'fault' for 'ok'
        except Exception as e:
            response['over_current'] = 'unknown'
            response['power_ok'] = 'unknown'
            logger.error('Error getting chassis status on power command for {} : {}'.format(ip_address, e.message))

        try:
            # next, attempt to get input power reading, and if unavailable, set the response to 'unknown'
            response['input_power'] = 'unknown'
            if reading_method is not None:
                if reading_method == 'flex-victoria':
                    response['input_power'] = get_flex_victoria_power_reading(
                        username, password, ip_address)['input_power']
                elif reading_method == 'dcmi':
                    response['input_power'] = _get_power_reading(username, password, ip_address)['input_power']
        except Exception as e:
            response['input_power'] = 'unknown'
            logger.error('Error getting power reading on power command for {} : {}'.format(ip_address, e.message))

        return response
Beispiel #12
0
def _get_chassis_status(username=None, password=None, ip_address=None, port=BMC_PORT):
    """ Internal wrapper for the Get Chassis Status IPMI command.

    Args:
        username (str): The username to connect to BMC with.
        password (str): The password to connect to BMC with.
        ip_address (str): The IP Address of the BMC.
        port (int): BMC port

    Returns:
        dict: Chassis status information from the remote system.
    """
    response = dict()
    with IpmiCommand(ip_address=ip_address, username=username, password=password, port=port) as ipmicmd:
        result = ipmicmd.raw_command(netfn=0, command=1, data=[])
        if 'error' in result:
            raise OpenDCREException(
                'Error executing chassis status command on {} : {}'.format(ip_address, result['error'])
            )

        if result['command'] != 1 or result['netfn'] != 1 or result['code'] != 0:
            raise OpenDCREException(
                'Error receiving chassis status response on {} : rc {}'.format(ip_address, hex(result['code]']))
            )

        # process result and stick into fields of response
        # first: power restore policy
        response['power_restore_policy'] = 'unknown'
        if (result['data'][0] >> 6) & 0b11 == 0b00:
            response['power_restore_policy'] = 'power_off'
        elif (result['data'][0] >> 6) & 0b11 == 0b01:
            response['power_restore_policy'] = 'previous_state'
        elif (result['data'][0] >> 6) & 0b11 == 0b10:
            response['power_restore_policy'] = 'power_on'

        # next, power control fault
        response['power_control_fault'] = False if (result['data'][0] >> 4 & 0b1) == 0b00 else True

        # then power fault
        response['power_fault'] = False if (result['data'][0] >> 3 & 0b1) == 0b00 else True

        # then interlock
        response['interlock_active'] = False if (result['data'][0] >> 2 & 0b1) == 0b00 else True

        # overload
        response['power_overload'] = False if (result['data'][0] >> 1 & 0b1) == 0b00 else True

        # power on?
        response['power_status'] = 'off' if result['data'][0] & 0b1 == 0b00 else 'on'

        # last power event
        response['last_power_event'] = 'unknown'
        if (result['data'][1] >> 4) & 0b01 == 0b01:
            response['last_power_event'] = 'power_on_by_ipmi'
        elif (result['data'][1] >> 3) & 0b01 == 0b01:
            response['last_power_event'] = 'power_down_by_power_fault'
        elif (result['data'][1] >> 2) & 0b01 == 0b01:
            response['last_power_event'] = 'power_down_by_interlock'
        elif (result['data'][1] >> 1) & 0b01 == 0b01:
            response['last_power_event'] = 'power_down_by_overload'
        elif result['data'][1] & 0b01 == 0b01:
            response['last_power_event'] = 'ac_failed'

        # led command supported
        response['chassis_identify_supported'] = True if (result['data'][2] >> 6 & 0b1) == 0b01 else 'unspecified'

        # led state
        if (result['data'][2] >> 5) & 0x01 or (result['data'][2] >> 4) & 0x01:
            response['led_state'] = 'on'
        else:
            response['led_state'] = 'off'

        # fan fault
        response['fan_fault'] = (result['data'][2] >> 3 & 0b1) == 0b01

        # drive fault
        response['drive_fault'] = (result['data'][2] >> 2 & 0b1) == 0b01

        # lockout active
        response['lockout_active'] = (result['data'][2] >> 1 & 0b1) == 0b01

        # chassis intrusion active
        response['chassis_intrusion'] = (result['data'][2] & 0b1) == 0b01

    return response