kontron/python-ipmi

View on GitHub
pyipmi/fields.py

Summary

Maintainability
B
5 hrs
Test Coverage
#!/usr/bin/env python
# -*- coding: utf-8 -*-


import array

from .errors import DecodingError
from .utils import py3_array_tobytes


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))


def _unpack6bitascii(data):
    """Unpack the 6bit ascii encoded string."""
    string = ''
    for i in range(0, len(data), 3):
        d = data[i:i+3]
        string += chr(0x20 + (d[0] & 0x3f))
        string += chr(0x20 + (((d[0] & 0xc0) >> 6) | ((d[1] & 0xf) << 2)))
        string += chr(0x20 + (((d[1] & 0xf0) >> 4) | ((d[2] & 0x3) << 4)))
        string += chr(0x20 + ((d[2] & 0xfc) >> 2))
    return string


class TypeLengthString(object):
    """
    This is the TYPE/LENGTH BYTE FORMAT field represenation according the
    Platform Management FRU Information Storage Definition v1.0.

    In addition the difference to the 'FRU Information Storage Definition' to
    the variant used in Type/Length for the Device ID String used in the SDR.
    """

    TYPE_FRU_BINARY = 0
    TYPE_SDR_UNICODE = 0
    TYPE_BCD_PLUS = 1
    TYPE_6BIT_ASCII = 2
    TYPE_ASCII_OR_UTF16 = 3

    def __init__(self, data=None, offset=0, force_lang_eng=False, sdr=False):
        if data:
            self._from_data(data, offset, force_lang_eng)

    def __str__(self):
        if self.field_type is self.TYPE_FRU_BINARY:
            return ' '.join('%02x' % b for b in self.raw)
        else:
            return self.string.replace('\x00', '')

    def _from_data(self, data, offset=0, force_lang_eng=False):
        self.offset = offset
        self.field_type = data[offset] >> 6 & 0x3
        self.length = data[offset] & 0x3f

        self.raw = data[offset+1:offset+1+self.length]

        if self.field_type == self.TYPE_BCD_PLUS:
            self.string = self.raw.decode('bcd+')
        elif self.field_type == self.TYPE_6BIT_ASCII:
            self.string = _unpack6bitascii(self.raw)
        else:
            chr_data = ''.join([chr(c) for c in self.raw])
            self.string = chr_data


class FruTypeLengthString(TypeLengthString):

    def __init__(self, data=None, offset=0, force_lang_eng=False):
        super(FruTypeLengthString, self).__init__(data, offset,
                                                  force_lang_eng,
                                                  sdr=False)


class SdrTypeLengthString(TypeLengthString):

    def __init__(self, data=None, offset=0, force_lang_eng=False):
        super(SdrTypeLengthString, self).__init__(data, sdr=True)