Пример #1
0
    def _scan(self, command):
        """ Get the scan information for a given board.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the scan response.
        """
        with self._lock:
            bus = self._get_bus()

            # get the command data out from the incoming command
            board_id = command.data['board_id']

            request = DumpCommand(
                board_id=board_id,
                sequence=command.sequence
            )

            try:
                response_dict = self._vapor_scan(request, bus)

            except Exception:
                raise OpenDCREException('Scan: Error when scanning board {}'.format(board_id)), None, sys.exc_info()[2]

            return Response(command=command, response_data=response_dict)
Пример #2
0
    def _scan_all(self, command):
        """ Get the scan information from a 'broadcast' (e.g. scan all) command.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the scan all response.
        """
        # get the command data out from the incoming command
        force = command.data.get('force', False)

        if force:
            self.board_record = self._get_board_record()

            if self.board_record is not None:
                # get FRU information for use in determining OEM support
                self.fru_info = self._get_fru_info()
                self.dcmi_supported = self._get_dcmi_power_capabilities()

        boards = [] if self.board_record is None else [self.board_record]
        scan_results = {
            'racks': [{
                'rack_id': self.bmc_rack,
                'boards': boards
            }]
        }

        return Response(command=command, response_data=scan_results)
Пример #3
0
    def _asset(self, command):
        """ Asset info command for a given board and device.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the asset response.
        """
        # get the command data out from the incoming command
        device_id = command.data['device_id']

        try:
            # validate that asset is supported for the device
            self._get_device_by_id(device_id, 'system')
            asset_data = vapor_ipmi.get_inventory(**self._ipmi_kwargs)
            asset_data['bmc_ip'] = self.bmc_ip

            if asset_data is not None:
                return Response(command=command, response_data=asset_data)

            logger.error('No response getting asset info for {}'.format(
                command.data))
            raise OpenDCREException(
                'No response from BMC when retrieving asset information via IPMI.'
            )

        except Exception:
            raise OpenDCREException(
                'Error getting IPMI asset info (device id: {})'.format(
                    hex(device_id))), None, sys.exc_info()[2]
Пример #4
0
    def _scan_all(self, command):
        """ Get the scan information from a 'broadcast' (e.g. scan all) command.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the scan all response.
        """
        # get the command data out from the incoming command
        force = command.data.get('force', False)

        if force:
            self._redfish_links = find_links(self.redfish_ip,
                                             self.redfish_port,
                                             **self._redfish_request_kwargs)
            self.board_record = self._get_board_record()

        boards = [] if self.board_record is None else [self.board_record]
        scan_results = {
            'racks': [{
                'rack_id': self.server_rack,
                'boards': boards
            }]
        }

        return Response(command=command, response_data=scan_results)
Пример #5
0
    def _scan_all(self, command):
        """ Get the scan information from a 'broadcast' (e.g. scan all) command.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the scan all response.
        """
        response_dict = {'racks': []}

        with self._lock:
            bus = self._get_bus()

            mac_addr = str(get_mac_addr())
            id_bytes = [int(mac_addr[i:i + 2], 16) for i in range(len(mac_addr) - 4, len(mac_addr), 2)]
            board_id = SCAN_ALL_BOARD_ID + (id_bytes[0] << 16) + (id_bytes[1] << 8) + self.time_slice

            request = DumpCommand(
                board_id=board_id,
                sequence=command.sequence
            )

            try:
                plc_rack = self._vapor_scan(request, bus)
                plc_rack['rack_id'] = PLC_RACK_ID
                response_dict['racks'].append(plc_rack)

            except Exception:
                raise OpenDCREException('Scan All: Error when scanning all boards.'), None, sys.exc_info()[2]

            return Response(command=command, response_data=response_dict)
Пример #6
0
    def _led(self, command):
        """ LED command for a given board and device.

        If an LED state is specified, this will attempt to set the LED state
        of the given device. Otherwise, it will just retrieve the currently
        configured LED state.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the LED response.
        """
        # get the command data from the incoming command:
        device_id = command.data['device_id']
        led_state = command.data['led_state']

        try:
            # check if the device raises an exception
            self._get_device_by_id(device_id, 'led')

            links_list = [self._redfish_links['chassis']]

            if led_state is not None and led_state.lower() != 'no_override':
                if led_state.lower() in ['on', 'off']:
                    led_state = 'Off' if led_state.lower() == 'off' else 'Lit'
                    response = vapor_redfish.set_led(
                        led_state=led_state,
                        links=links_list,
                        **self._redfish_request_kwargs)
                else:
                    logger.error(
                        'LED state unsupported for LED control operation: {}'.
                        format(led_state))
                    raise OpenDCREException(
                        'Unsupported state change to Redfish server on LED operation.'
                    )
            else:
                response = vapor_redfish.get_led(
                    links=links_list, **self._redfish_request_kwargs)

            if response:
                return Response(command=command, response_data=response)

            logger.error('No response for LED control operation: {}'.format(
                command.data))
            raise OpenDCREException(
                'No response from Redfish server on LED operation.')

        except ValueError as e:
            logger.error('Error with LED control: {}'.format(command.data))
            raise OpenDCREException(
                'Error returned from Redfish server during LED operation via Redfish ({}).'
                .format(e))
Пример #7
0
    def _fan(self, command):
        """ Fan speed control command for a given board and device.

        Gets the fan speed for a given device. Since this is not NOT a Vapor
        Fan, we can not control the fan speed, so any attempts to set the
        fan speed are met with an OpenDCREException being raised.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the fan response.
        """
        device_id = command.data['device_id']
        device_name = command.data['device_name']
        fan_speed = command.data['fan_speed']

        try:
            if fan_speed is not None:
                raise OpenDCREException(
                    'Setting of fan speed is not permitted for this device.')

            else:
                # check if the device raises an exception
                device = self._get_device_by_id(device_id, 'fan_speed')
                if device['device_type'] != 'fan_speed':
                    raise OpenDCREException(
                        "Attempt to get fan speed for non-fan device.")

                links_list = [self._redfish_links['thermal']]

                response = vapor_redfish.get_thermal_sensor(
                    device_type='Fans',
                    device_name=device_name,
                    links=links_list,
                    **self._redfish_request_kwargs)

                if response:
                    return Response(command=command, response_data=response)

            logger.error('No response for fan control operation: {}'.format(
                command.data))
            raise OpenDCREException(
                'No response from Redfish server on fan operation via Redfish.'
            )

        except ValueError as e:
            logger.error('Error with fan control: {}'.format(command.data))
            raise OpenDCREException(
                'Error returned from Redfish server during fan operation via Redfish ({}).'
                .format(e))
Пример #8
0
    def _boot_target(self, command):
        """ Boot target command for a given board and device.

        If a boot target is specified, this will attempt to set the boot device.
        Otherwise, it will just retrieve the currently configured boot device.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the boot target response.
        """
        # get the command data out from the incoming command
        device_id = command.data['device_id']
        boot_target = command.data['boot_target']

        try:
            # check if the device raises an exception
            self._get_device_by_id(device_id, 'system')

            links_list = [self._redfish_links['system']]

            if boot_target in [None, 'status']:
                response = vapor_redfish.get_boot(
                    links=links_list, **self._redfish_request_kwargs)
            else:
                boot_target = 'no_override' if boot_target not in [
                    'pxe', 'hdd'
                ] else boot_target
                response = vapor_redfish.set_boot(
                    target=boot_target,
                    links=links_list,
                    **self._redfish_request_kwargs)

            if response:
                return Response(command=command, response_data=response)

            logger.error('No response for boot target operation: {}'.format(
                command.data))
            raise OpenDCREException(
                'No response from Redfish server on boot target operation via Redfish.'
            )

        except ValueError as e:
            logger.error(
                'Error getting or setting Redfish boot target: {}'.format(
                    command.data))
            raise OpenDCREException(
                'Error returned from Redfish server during boot target operation via Redfish ({}).'
                .format(e))
Пример #9
0
    def _scan(self, command):
        """ Get the scan information for a given board.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the scan response.
        """
        boards = [] if self.board_record is None else [self.board_record]
        return Response(command=command, response_data={'boards': boards})
Пример #10
0
    def _power(self, command):
        """ Power control command for a given board and device.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the power response.
        """
        # get the command data out from the incoming command
        device_id = command.data['device_id']
        power_action = command.data['power_action']

        try:
            # validate device supports power control
            self._get_device_by_id(device_id, 'power')

            if power_action not in ['on', 'off', 'cycle', 'status']:
                raise ValueError(
                    'Invalid Redfish power action {} for board {} device {}.'.
                    format(power_action, hex(self.board_id), hex(device_id)))
            else:
                links_list = [
                    self._redfish_links['system'], self._redfish_links['power']
                ]
                if power_action == 'status':
                    response = vapor_redfish.get_power(
                        links=links_list, **self._redfish_request_kwargs)
                else:
                    response = vapor_redfish.set_power(
                        power_action,
                        links=links_list,
                        **self._redfish_request_kwargs)

            if response:
                return Response(command=command, response_data=response)

            # if we get here there is either no device found, or response received
            logger.error('Power control attempt returned no data: {}'.format(
                command.data))
            raise OpenDCREException(
                'No response from Redfish server for power control action.')

        except ValueError as e:
            logger.error('Error controlling Redfish power: {}'.format(
                command.data))
            raise OpenDCREException(
                'Error returned from Redfish server in controlling power via Redfish ({}).'
                .format(e))
Пример #11
0
    def _fan(self, command):
        """ Fan speed control command for a given board and device.

        Gets the fan speed for a given device. Since this is not NOT a Vapor
        Fan, we can not control the fan speed, so any attempts to set the
        fan speed are met with an OpenDCREException being raised.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the fan response.
        """
        # get the command data out from the incoming command
        device_id = command.data['device_id']
        device_name = command.data['device_name']
        fan_speed = command.data['fan_speed']

        try:
            if fan_speed is not None:
                raise OpenDCREException(
                    'Setting of fan speed is not permitted for this device.')
            else:
                device = self._get_device_by_id(device_id, 'fan_speed')
                reading = vapor_ipmi.read_sensor(sensor_name=device_name,
                                                 **self._ipmi_kwargs)
                response = dict()
                if device['device_type'] == 'fan_speed':
                    response['speed_rpm'] = reading['sensor_reading']
                else:
                    raise OpenDCREException(
                        'Attempt to get fan speed for non-fan device.')

                response['health'] = reading['health']
                response['states'] = reading['states']

                if response is not None:
                    return Response(command=command, response_data=response)

            logger.error('No response for fan control operation: {}'.format(
                command.data))
            raise OpenDCREException(
                'No response from BMC on fan operation via IPMI.')

        except Exception:
            raise OpenDCREException(
                'Error with fan control (device id: {})'.format(
                    hex(device_id))), None, sys.exc_info()[2]
Пример #12
0
    def _read(self, command):
        """ Read the data off of a given board's device.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the read response.
        """
        # get the command data out from the incoming command
        device_id = command.data['device_id']
        device_type_string = command.data['device_type_string']

        try:
            device = self._get_device_by_id(device_id, device_type_string)

            reading = vapor_ipmi.read_sensor(sensor_name=device['device_info'],
                                             **self._ipmi_kwargs)
            response = dict()

            # TODO (etd) - this could be consolidated a bit if we had a helper fn which did device type -> reading
            #   measure lookup, e.g. lookup('temperature') --> 'temperature_c' ; could also be useful in other places
            if device['device_type'] == const.DEVICE_TEMPERATURE:
                response['temperature_c'] = reading['sensor_reading']

            elif device['device_type'] == const.DEVICE_FAN_SPEED:
                response['speed_rpm'] = reading['sensor_reading']

            elif device['device_type'] == const.DEVICE_VOLTAGE:
                response['voltage'] = reading['sensor_reading']

            response['health'] = reading['health']
            response['states'] = reading['states']

            if response is not None:
                return Response(command=command, response_data=response)

            # if we get here, there was no sensor device found, so we must raise
            logger.error(
                'No response for sensor reading for command: {}'.format(
                    command.data))
            raise OpenDCREException('No sensor reading returned from BMC.')

        except Exception:
            raise OpenDCREException(
                'Error reading IPMI sensor (device id: {})'.format(
                    hex(device_id))), None, sys.exc_info()[2]
Пример #13
0
    def _version(self, command):
        """ Get the version information for a given board.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the version response.
        """
        return Response(command=command,
                        response_data={
                            'api_version': __api_version__,
                            'opendcre_version': __version__,
                            'firmware_version': 'OpenDCRE Redfish Bridge v1.0'
                        })
Пример #14
0
    def _led(self, command):
        """ LED command for a given board and device.

        If an LED state is specified, this will attempt to set the LED state
        of the given device. Otherwise, it will just retrieve the currently
        configured LED state.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the LED response.
        """
        # get the command data out from the incoming command
        device_id = command.data['device_id']
        led_state = command.data['led_state']

        try:
            if led_state is not None:
                self._get_device_by_id(device_id, 'led')
                led_state = 0 if led_state.lower() == 'off' else 1
                led_response = vapor_ipmi.set_identify(led_state=led_state,
                                                       **self._ipmi_kwargs)
                led_response['led_state'] = 'off' if led_response[
                    'led_state'] == 0 else 'on'
            else:
                self._get_device_by_id(device_id, 'led')
                led_response = vapor_ipmi.get_identify(**self._ipmi_kwargs)

            if led_response is not None:
                return Response(command=command, response_data=led_response)

            logger.error('No response for LED control operation: {}'.format(
                command.data))
            raise OpenDCREException(
                'No response from BMC on LED operation via IPMI.')

        except Exception:
            raise OpenDCREException(
                'Error with LED control (device id: {})'.format(
                    device_id)), None, sys.exc_info()[2]
Пример #15
0
    def _host_info(self, command):
        """ Get the host information for a given board.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the host info response.
        """
        return Response(
            command=command,
            response_data={
                'hostnames':
                self.hostnames,
                'ip_addresses':
                self.ip_addresses if self.ip_addresses else [self.redfish_ip]
            })
Пример #16
0
    def _boot_target(self, command):
        """ Boot target command for a given board and device.

        If a boot target is specified, this will attempt to set the boot device.
        Otherwise, it will just retrieve the currently configured boot device.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the boot target response.
        """
        # get the command data out from the incoming command
        device_id = command.data['device_id']
        boot_target = command.data['boot_target']

        try:
            if boot_target is None or boot_target == 'status':
                self._get_device_by_id(device_id, 'system')
                boot_info = vapor_ipmi.get_boot(**self._ipmi_kwargs)
            else:
                boot_target = 'no_override' if boot_target not in [
                    'pxe', 'hdd'
                ] else boot_target
                self._get_device_by_id(device_id, 'system')
                boot_info = vapor_ipmi.set_boot(target=boot_target,
                                                **self._ipmi_kwargs)

            if boot_info is not None:
                return Response(command=command, response_data=boot_info)

            logger.error('No response for boot target operation: {}'.format(
                command.data))
            raise OpenDCREException(
                'No response from BMC on boot target operation via IPMI.')

        except Exception:
            raise OpenDCREException(
                'Error getting or setting IPMI boot target (device id: {})'.
                format(hex(device_id))), None, sys.exc_info()[2]
Пример #17
0
    def _version(self, command):
        """ Get the version information for a given board.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the version response.
        """
        with self._lock:
            bus = self._get_bus()

            # get the command data out from the incoming command
            board_id = command.data['board_id']

            request = VersionCommand(
                board_id=board_id,
                sequence=command.sequence
            )
            bus.write(request.serialize())
            logger.debug('>>Version: {}'.format([hex(x) for x in request.serialize()]))

            try:
                response = VersionResponse(
                    serial_reader=bus,
                    expected_sequence=request.sequence
                )
            except BusTimeoutException:
                raise OpenDCREException('Version Command timeout'), None, sys.exc_info()[2]
            except (BusDataException, ChecksumException):
                response = self._retry_command(bus, request, VersionResponse)

            return Response(
                command=command,
                response_data={
                    'firmware_version': response.versionString,
                    'opendcre_version': __version__,
                    'api_version': __api_version__
                }
            )
Пример #18
0
    def _asset(self, command):
        """ Asset info command for a given board and device.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the asset response.
        """
        # get the command data out from the incoming command
        device_id = command.data['device_id']

        try:
            # validate that asset is supported for the device
            self._get_device_by_id(device_id, 'system')

            links_list = [
                self._redfish_links['chassis'], self._redfish_links['system'],
                self._redfish_links['bmc']
            ]

            response = vapor_redfish.get_asset(links=links_list,
                                               **self._redfish_request_kwargs)
            response['redfish_ip'] = self.redfish_ip

            if 'chassis_info' in response:
                return Response(command=command, response_data=response)

            logger.error('No response getting asset info for {}'.format(
                command.data))
            raise OpenDCREException(
                'No response from Redfish server when retrieving asset information via Redfish.'
            )
        except ValueError as e:
            logger.error('Error getting Redfish asset info: {}'.format(
                command.data))
            raise OpenDCREException(
                'Error returned from Redfish server when retrieving asset info via Redfish ({}).'
                .format(e))
Пример #19
0
    def _read(self, command):
        """ Read the data off of a given board's device.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the read response.
        """
        with self._lock:
            bus = self._get_bus()

            # get the command data out from the incoming command
            board_id = command.data['board_id']
            device_id = command.data['device_id']
            device_type = command.data['device_type']
            device_type_string = command.data['device_type_string']

            request = DeviceReadCommand(
                board_id=board_id,
                device_id=device_id,
                device_type=device_type,
                sequence=command.sequence
            )
            bus.write(request.serialize())
            bus.flush()

            logger.debug('>>Read: {}'.format([hex(x) for x in request.serialize()]))
            response = None

            try:
                response = DeviceReadResponse(
                    serial_reader=bus,
                    expected_sequence=request.sequence
                )
                logger.debug('<<Read: {}'.format([hex(x) for x in response.serialize()]))

            except BusTimeoutException:
                raise OpenDCREException('No response from bus on sensor read.'), None, sys.exc_info()[2]
            except (BusDataException, ChecksumException):
                response = self._retry_command(bus, request, DeviceReadResponse)

        try:
            device_type_string = device_type_string.lower()

            # for now, temperature and pressure are just a string->float, all else require int conversion
            device_raw = float(''.join([chr(x) for x in response.data]))

            if device_type_string == const.DEVICE_TEMPERATURE:
                response_data = {'temperature_c': device_raw}

            elif device_type_string == const.DEVICE_PRESSURE:
                response_data = {'pressure_kpa': device_raw}

            else:
                # for all other sensors get raw value as integer
                device_raw = int(''.join([chr(x) for x in response.data]))

                # convert raw value and jsonify the device reading
                if device_type_string == const.DEVICE_THERMISTOR:
                    response_data = {'temperature_c': convert_thermistor(device_raw)}

                elif device_type_string == const.DEVICE_HUMIDITY:
                    response_data = dict(zip(('humidity', 'temperature_c'), convert_humidity(device_raw)))

                elif device_type_string == const.DEVICE_FAN_SPEED:
                    # TODO: retrieve fan mode from the auto_fan controller
                    response_data = {'speed_rpm': device_raw, 'fan_mode': 'auto'}

                elif device_type_string == const.DEVICE_VAPOR_FAN:
                    # TODO: retrieve fan mode from the auto_fan controller
                    response_data = {'speed_rpm': device_raw, 'fan_mode': 'auto'}

                elif device_type_string == const.DEVICE_LED:
                    if device_raw not in [1, 0]:
                        raise ValueError('Invalid raw value returned: {}'.format(device_raw))
                    response_data = {'led_state': 'on' if device_raw == 1 else 'off'}

                # default - for anything we don't convert, send back raw data
                # for invalid device types / device mismatches, that gets
                # caught when the request is sent over the bus
                else:
                    response_data = {'device_raw': device_raw}

            return Response(command=command, response_data=response_data)

        except (ValueError, TypeError):
            # abort if unable to convert to int (ValueError), unable to convert to chr (TypeError)
            raise OpenDCREException('Read: Error converting device reading.'), None, sys.exc_info()[2]
        except Exception:
            # if something bad happened - all we can do is abort
            raise OpenDCREException('Read: Error converting raw value.'), None, sys.exc_info()[2]
Пример #20
0
    def _power(self, command):
        """ Power control command for a given board and device.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the power response.
        """
        with self._lock:
            bus = self._get_bus()

            # get the command data out from the incoming command
            power_action = command.data['power_action']
            board_id = command.data['board_id']
            device_id = command.data['device_id']
            device_type = command.data['device_type']

            request = PowerControlCommand(
                board_id=board_id,
                device_id=device_id,
                device_type=device_type,
                power_action=power_action,
                sequence=command.sequence
            )
            bus.write(request.serialize())
            logger.debug('>>Power: {}'.format([hex(x) for x in request.serialize()]))

            try:
                response = PowerControlResponse(
                    serial_reader=bus,
                    expected_sequence=request.sequence
                )
                logger.debug('<<Power: {}'.format([hex(x) for x in response.serialize()]))

            except BusTimeoutException:
                raise OpenDCREException('Power command bus timeout.'), None, sys.exc_info()[2]
            except (BusDataException, ChecksumException):
                response = self._retry_command(bus, request, PowerControlResponse)

            # get raw value as string
            try:
                pmbus_raw = ''
                for x in response.data:
                    pmbus_raw += chr(x)
                # here, we should have a comma-separated string that looks like:
                # nstatus,npower,nvoltage,ncurrent - or sstatus,ncharge
                pmbus_values = pmbus_raw.split(',')
                if len(pmbus_values) == 4:
                    status_raw = int(pmbus_values[0])
                    power_raw = int(pmbus_values[1])
                    voltage_raw = int(pmbus_values[2])
                    current_raw = int(pmbus_values[3])

                    def convert_power_status(raw):
                        return 'off' if (raw >> 6 & 0x01) == 0x01 else 'on'

                    def bit_to_bool(raw):
                        return raw == 1

                    # now, convert raw reading into subfields
                    status_converted = {
                        'pmbus_raw': pmbus_raw,
                        'power_status': convert_power_status(status_raw),
                        'power_ok': not bit_to_bool((status_raw >> 11) & 0x01),
                        'over_current': bit_to_bool((status_raw >> 4) & 0x01),
                        'under_voltage': bit_to_bool((status_raw >> 3) & 0x01),
                        'input_power': convert_direct_pmbus(power_raw, 'power'),
                        'input_voltage': convert_direct_pmbus(voltage_raw, 'voltage'),
                        'output_current': convert_direct_pmbus(current_raw, 'current')
                    }
                    return Response(command=command, response_data=status_converted)

                elif len(pmbus_values) == 2:
                    # TODO: vapor_battery - this is a shim until actual PLC protocol determined
                    status_converted = {
                        'battery_status': pmbus_values[0],
                        'battery_charge_percent': int(pmbus_values[1])
                    }
                    return Response(command=command, response_data=status_converted)
                else:
                    raise OpenDCREException('Invalid power data returned: {}'.format(pmbus_raw))

            except (ValueError, TypeError, IndexError):
                # abort if unable to convert to int (ValueError), unable to convert
                # to chr (TypeError), or if expected pmbus_values don't exist (IndexError)
                raise OpenDCREException('Power: Error converting PMBUS data.'), None, sys.exc_info()[2]
            except Exception:
                raise OpenDCREException('Power: Unexpected error when converting PMBUS data.'), None, sys.exc_info()[2]
Пример #21
0
    def _asset(self, command):
        """ Asset info command for a given board and device.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the asset response.
        """
        with self._lock:
            bus = self._get_bus()

            # get the command data out from the incoming command
            board_id = command.data['board_id']
            device_id = command.data['device_id']
            device_type = command.data['device_type']

            request = AssetInfoCommand(
                board_id=board_id,
                device_id=device_id,
                device_type=device_type,
                sequence=command.sequence
            )
            bus.write(request.serialize())
            logger.debug('>>Asset Info: {}'.format([hex(x) for x in request.serialize()]))

            try:
                response = AssetInfoResponse(
                    serial_reader=bus,
                    expected_sequence=request.sequence
                )
                logger.debug('<<Asset Info: {}'.format([hex(x) for x in response.serialize()]))

            except BusTimeoutException as e:
                raise OpenDCREException('Asset info command bus timeout.'), None, sys.exc_info()[2]
            except (BusDataException, ChecksumException):
                response = self._retry_command(bus, request, AssetInfoResponse)

            # get raw value as string
            try:
                asset_data = ''
                for x in response.data:
                    asset_data += chr(x)

                def convert_asset_info(raw):
                    """ Convert raw asset info to JSON.  Expects a delimited string (delimiter TBD) with 13 fields.

                    Args:
                        raw: Raw data to convert.

                    Returns: JSON dictionary from string.
                    """
                    info_raw = raw.split(',')
                    if len(info_raw) == 13:
                        return {
                            'board_info': {
                                'manufacturer': info_raw[0],
                                'part_number': info_raw[1],
                                'product_name': info_raw[2],
                                'serial_number': info_raw[3]
                            },
                            'chassis_info': {
                                'chassis_type': info_raw[4],
                                'part_number': info_raw[5],
                                'serial_number': info_raw[6]
                            },
                            'product_info': {
                                'asset_tag': info_raw[7],
                                'manufacturer': info_raw[8],
                                'part_number': info_raw[9],
                                'product_name': info_raw[10],
                                'serial_number': info_raw[11],
                                'version': info_raw[12]
                            }
                        }
                    else:
                        raise ValueError('Invalid raw asset info returned {}'.format(raw))

                return Response(command=command, response_data=convert_asset_info(asset_data))

            except (ValueError, TypeError):
                # abort if unable to convert to info string (ValueError), unable to convert to chr (TypeError)
                raise OpenDCREException('Asset Info: Error converting asset info data.'), None, sys.exc_info()[2]
            except Exception:
                raise OpenDCREException('Asset Info: Unexpected exception when converting asset info'), None, sys.exc_info()[2]
Пример #22
0
    def _boot_target(self, command):
        """ Boot target command for a given board and device.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the boot target response.
        """
        with self._lock:
            bus = self._get_bus()

            # get the command data out from the incoming command
            board_id = command.data['board_id']
            device_id = command.data['device_id']
            device_type = command.data['device_type']
            boot_target = command.data['boot_target']

            request = BootTargetCommand(
                board_id=board_id,
                device_id=device_id,
                device_type=device_type,
                boot_target=boot_target,
                sequence=command.sequence
            )
            bus.write(request.serialize())
            logger.debug('>>Boot Target: {}'.format([hex(x) for x in request.serialize()]))

            try:
                response = BootTargetResponse(
                    serial_reader=bus,
                    expected_sequence=request.sequence
                )
                logger.debug('<<Boot Target: {}'.format([hex(x) for x in response.serialize()]))

            except BusTimeoutException:
                raise OpenDCREException('Boot target command bus timeout.'), None, sys.exc_info()[2]
            except (BusDataException, ChecksumException):
                response = self._retry_command(bus, request, BootTargetResponse)

            # get raw value as string
            try:
                target_raw = ''
                for x in response.data:
                    target_raw += chr(x)
                # here, we should have a value in { B0, B1, B2}

                def convert_boot_target(raw):
                    if raw == 'B0':
                        return 'no_override'
                    elif raw == 'B1':
                        return 'hdd'
                    elif raw == 'B2':
                        return 'pxe'
                    else:
                        raise ValueError('Invalid raw boot target returned {}'.format(raw))

                # now, convert raw reading into subfields
                target_response = {
                    'target': convert_boot_target(target_raw)
                }
                return Response(command=command, response_data=target_response)

            except (ValueError, TypeError):
                # abort if unable to convert to target string (ValueError), unable to convert to chr (TypeError)
                raise OpenDCREException('Boot Target: Error converting boot target data.'), None, sys.exc_info()[2]
            except Exception:
                raise OpenDCREException('Boot Target: Unexpected exception when converting boot target data'), None, sys.exc_info()[2]
Пример #23
0
    def _read(self, command):
        """ Read the data off of a given board's device.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the read response.
        """
        # get the command data out from the incoming command
        device_id = command.data['device_id']
        device_type_string = command.data['device_type_string']
        response = dict()

        if device_type_string.lower() == 'fan_speed':
            device_type = 'Fans'
        elif device_type_string.lower() == 'temperature':
            device_type = 'Temperatures'
        elif device_type_string.lower() == 'voltage':
            device_type = 'Voltages'
        elif device_type_string.lower() == 'power_supply':
            device_type = 'PowerSupplies'
        else:
            logger.error(
                'Unsupported device type for Redfish device: {}'.format(
                    device_type_string))
            raise OpenDCREException(
                '{} not a supported device type for Redfish.'.format(
                    device_type_string))

        try:
            # check if the device raises an exception
            device = self._get_device_by_id(device_id, device_type_string)
            device_name = device['device_info']

            if device_type_string.lower() in ['fan_speed', 'temperature']:
                links_list = [self._redfish_links['thermal']]
                response = vapor_redfish.get_thermal_sensor(
                    device_type=device_type,
                    device_name=device_name,
                    links=links_list,
                    **self._redfish_request_kwargs)
            elif device_type_string.lower() in ['voltage', 'power_supply']:
                links_list = [self._redfish_links['power']]
                response = vapor_redfish.get_power_sensor(
                    device_type=device_type,
                    device_name=device_name,
                    links=links_list,
                    **self._redfish_request_kwargs)

            if response:
                return Response(command=command, response_data=response)

            # if we get here, there was no sensor device found, so we must raise
            logger.error(
                'No response for sensor reading for command: {}'.format(
                    command.data))
            raise OpenDCREException(
                'No sensor reading returned from Redfish server.')
        except ValueError as e:
            logger.error('Error reading Redfish sensor: {}'.format(e.message))
            logger.exception(e)
            raise OpenDCREException('Error reading Redfish sensor: {}'.format(
                e.message))
Пример #24
0
    def _chamber_led(self, command):
        """ Chamber LED control command.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the chamber LED response.
        """
        with self._lock:
            bus = self._get_bus()

            # get the command data out from the incoming command
            board_id = command.data['board_id']
            device_id = command.data['device_id']
            device_type = command.data['device_type']
            rack_id = command.data['rack_id']
            led_state = command.data['led_state']
            led_color = command.data['led_color']
            blink_state = command.data['blink_state']

            request = ChamberLedControlCommand(
                board_id=board_id,
                device_id=device_id,
                device_type=device_type,
                rack_id=rack_id,
                led_state=led_state,
                led_color=led_color,
                blink_state=blink_state,
                sequence=command.sequence
            )
            bus.write(request.serialize())
            bus.flush()

            logger.debug('>>Vapor_LED: {}'.format([hex(x) for x in request.serialize()]))

            try:
                response = ChamberLedControlResponse(
                    serial_reader=bus,
                    expected_sequence=request.sequence
                )
                logger.debug('<<Vapor_LED: {}'.format([hex(x) for x in request.serialize()]))

            except BusTimeoutException:
                raise OpenDCREException('Chamber LED command bus timeout.'), None, sys.exc_info()[2]
            except (BusDataException, ChecksumException):
                response = self._retry_command(bus, request, ChamberLedControlResponse)

            # get raw value to ensure remote device took the write.
            try:
                device_raw = str(''.join([chr(x) for x in response.data]))
            except (ValueError, TypeError) as e:
                # abort if unable to convert to int (ValueError), unable to convert to chr (TypeError)
                raise OpenDCREException('Chamber LED: Error converting device response.'), None, sys.exc_info()[2]

            # TODO: temporary response format until PLC comms finalized
            if len(device_raw.split(',')) == 3:
                control_data = device_raw.split(',')
                led_response = dict()
                led_response['led_state'] = 'on' if control_data[0] == '1' else 'off'
                led_response['led_color'] = control_data[1]
                led_response['blink_state'] = 'blink' if control_data[2] == '1' else 'steady'
                return Response(command=command, response_data=led_response)
            else:
                raise OpenDCREException('Invalid Chamber LED response data.')
Пример #25
0
    def _host_info(self, command):
        """ Get the host information for a given board and device.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the host info response.
        """
        with self._lock:
            bus = self._get_bus()

            # get the command data out from the incoming command
            board_id = command.data['board_id']
            device_id = command.data['device_id']
            device_type = command.data['device_type']

            request = HostInfoCommand(
                board_id=board_id,
                device_id=device_id,
                device_type=device_type,
                sequence=command.sequence
            )
            bus.write(request.serialize())
            bus.flush()

            logger.debug('>>Host_Info: {}'.format([hex(x) for x in request.serialize()]))
            response = None

            try:
                response = HostInfoResponse(
                    serial_reader=bus,
                    expected_sequence=request.sequence
                )
                logger.debug('<<Host_Info: {}'.format([hex(x) for x in response.serialize()]))

            except BusTimeoutException:
                raise OpenDCREException('Host Info command bus timeout.'), None, sys.exc_info()[2]
            except (BusDataException, ChecksumException):
                response = self._retry_command(bus, request, HostInfoResponse)

        # get raw value to ensure remote device took the write.
        try:
            device_raw = str(''.join([chr(x) for x in response.data]))
        except (ValueError, TypeError):
            # abort if unable to convert to int (ValueError), unable to convert to chr (TypeError)
            raise OpenDCREException('Host Info: Error converting device response.'), None, sys.exc_info()[2]
        # convert to json dictionary
        try:
            host_information = dict()
            host_information['ip_addresses'] = []
            host_information['hostnames'] = []
            host_fields = device_raw.split(',')
            for field in host_fields:
                if field[0] == 'i':
                    host_information['ip_addresses'].append(field[1:])
                elif field[0] == 'h':
                    host_information['hostnames'].append(field[1:])
                else:
                    raise ValueError('Invalid field in host information: {}.'.format(field))
            return Response(command=command, response_data=host_information)

        except ValueError as e:
            raise OpenDCREException(e.message)
        except Exception:
            raise OpenDCREException('Invalid host information returned'), None, sys.exc_info()[2]
Пример #26
0
    def _power(self, command):
        """ Power control command for a given board and device.

        Args:
            command (Command): the command issued by the OpenDCRE endpoint
                containing the data and sequence for the request.

        Returns:
            Response: a Response object corresponding to the incoming Command
                object, containing the data from the power response.
        """
        # get the command data out from the incoming command
        device_id = command.data['device_id']
        power_action = command.data['power_action']

        try:
            if power_action not in ['on', 'off', 'cycle', 'status']:
                raise ValueError(
                    'Invalid IPMI power action {} for board {} device {}.'.
                    format(power_action, hex(self.board_id), hex(device_id)))
            # determine if there are OEM considerations
            # for reading power
            reading_method = None

            if self.dcmi_supported:
                reading_method = 'dcmi'

            if self.fru_info is not None:
                if self.fru_info['board_info'][
                        'manufacturer'] == 'Ciii USA Inc':
                    # flex OEM support should override dcmi support
                    reading_method = 'flex-victoria'

            # validate device supports power control
            self._get_device_by_id(device_id, 'power')

            power_status = vapor_ipmi.power(cmd=power_action,
                                            reading_method=reading_method,
                                            **self._ipmi_kwargs)
            """
            NB(ABC): disabled this but it could be re-enabled - if the DCMI Power Reading command is giving
                     trouble elsewhere, we could re-enable this, but the checks done at startup should
                     obviate the need for this logic for now.
            # check reading method, and if 'dcmi' and input_power is 'unknown', set reading_method to
            # 'None' in the future; 'unknown' indicates an exception occurred, which we do not wish to repeat
            if reading_method == 'dcmi' and power_status['input_power'] == 'uknown':
                self.dcmi_supported = False
            """

            if power_status is not None:
                return Response(command=command, response_data=power_status)

            # if we get here there is either no device found, or response received
            logger.error('Power control attempt returned no data: {}'.format(
                command.data))
            raise OpenDCREException(
                'No response from BMC for power control action.')

        except Exception:
            raise OpenDCREException(
                'Error for power control via IPMI (device id: {}).'.format(
                    hex(device_id))), None, sys.exc_info()[2]