File: ipmi.py

package info (click to toggle)
python-scciclient 0.16.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 800 kB
  • sloc: python: 7,935; xml: 2,377; makefile: 24; sh: 2
file content (172 lines) | stat: -rwxr-xr-x 5,503 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# Copyright 2017 FUJITSU LIMITED
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import functools
import itertools

from pyghmi import exceptions as ipmi_exception
from pyghmi.ipmi import command as ipmi_command

# F1 1A - Get the number of specific PCI devices on baremetal
GET_PCI = '0x2E 0xF1 0x80 0x28 0x00 0x1A %s 0x00'

# F5 81 - GET TPM STATUS
GET_TPM_STATUS = '0x2E 0xF5 0x80 0x28 0x00 0x81 0xC0'


class IPMIFailure(Exception):
    """IPMI Failure

    This exception is used when IPMI operation failed.
    """
    def __init__(self, message):
        super(IPMIFailure, self).__init__(message)


class InvalidParameterValue(IPMIFailure):
    """Invalid Parameter Value Failure

    This exception is used when invalid parameter values are passed to
    the APIs exposed by this module.
    """
    def __init__(self, message):
        super(InvalidParameterValue, self).__init__(message)


def _parse_raw_bytes(raw_bytes):
    """Convert a string of hexadecimal values to decimal values parameters

    Example: '0x2E 0xF1 0x80 0x28 0x00 0x1A 0x01 0x00' is converted to:
              46, 241, [128, 40, 0, 26, 1, 0]

    :param raw_bytes: string of hexadecimal values
    :returns: 3 decimal values
    """
    bytes_list = [int(x, base=16) for x in raw_bytes.split()]
    return bytes_list[0], bytes_list[1], bytes_list[2:]


def _send_raw_command(ipmicmd, raw_bytes):
    """Use IPMI command object to send raw ipmi command to BMC

    :param ipmicmd: IPMI command object
    :param raw_bytes: string of hexadecimal values. This is commonly used
        for certain vendor specific commands.
    :returns: dict -- The response from IPMI device
    """

    netfn, command, data = _parse_raw_bytes(raw_bytes)
    response = ipmicmd.raw_command(netfn, command, data=data)

    return response


def get_tpm_status(d_info):
    """Get the TPM support status.

    Get the TPM support status of the node.

    :param d_info: the list of ipmitool parameters for accessing a node.
    :returns: TPM support status
    """

    # note:
    # Get TPM support status : ipmi cmd '0xF5', valid flags '0xC0'
    #
    # $ ipmitool raw 0x2E 0xF5 0x80 0x28 0x00 0x81 0xC0
    #
    # Raw response:
    # 80 28 00 C0 C0: True
    # 80 28 00 -- --: False (other values than 'C0 C0')

    ipmicmd = ipmi_command.Command(
        bmc=d_info['irmc_address'],
        userid=d_info['irmc_username'].encode('utf-8'),
        password=d_info['irmc_password'].encode('utf-8'))
    try:
        response = _send_raw_command(ipmicmd, GET_TPM_STATUS)
        if response['code'] != 0:
            raise IPMIFailure(
                "IPMI operation '%(operation)s' failed: %(error)s" %
                {'operation': "GET TMP status",
                 'error': response.get('error')})
        out = ' '.join('{:02X}'.format(x) for x in response['data'])
        return out is not None and out[-5:] == 'C0 C0'

    except ipmi_exception.IpmiException as e:
        raise IPMIFailure(
            "IPMI operation '%(operation)s' failed: %(error)s" %
            {'operation': "GET TMP status", 'error': e})


def _pci_seq(ipmicmd):
    """Get output of ipmiraw command and the ordinal numbers.

    :param ipmicmd: IPMI command object.
    :returns: List of tuple contain ordinal number and output of ipmiraw
    command.
    """
    for i in range(1, 0xff + 1):
        try:
            res = _send_raw_command(ipmicmd, GET_PCI % hex(i))
            yield i, res
        except ipmi_exception.IpmiException as e:
            raise IPMIFailure(
                "IPMI operation '%(operation)s' failed: %(error)s" %
                {'operation': "GET PCI device quantity", 'error': e})


def get_pci_device(d_info, pci_device_ids):
    """Get quantity of PCI devices.

    Get quantity of PCI devices of the node.

    :param d_info: the list of ipmitool parameters for accessing a node.
    :param pci_device_ids: the list contains pairs of <vendorID>/<deviceID> for
    PCI devices.
    :returns: the number of PCI devices.
    """

    # note:
    # Get quantity of PCI devices:
    # ipmi cmd '0xF1'
    #
    # $ ipmitool raw 0x2E 0xF1 0x80 0x28 0x00 0x1A 0x01 0x00
    #
    # Raw response:
    # 80 28 00 00 00 05 data1 data2 34 17 76 11 00 04
    # 01

    # data1: 2 octet of VendorID
    # data2: 2 octet of DeviceID

    ipmicmd = ipmi_command.Command(
        bmc=d_info['irmc_address'],
        userid=d_info['irmc_username'].encode('utf-8'),
        password=d_info['irmc_password'].encode('utf-8'))

    response = itertools.takewhile(
        lambda y: (y[1]['code'] != 0xC9 and y[1].get('error') is None),
        _pci_seq(ipmicmd))

    def _pci_count(accm, v):
        out = v[1]['data']
        # if system returns value, record id will be increased.
        pci_id = "0x{:02x}{:02x}/0x{:02x}{:02x}".format(
            out[7], out[6], out[9], out[8])
        return accm + 1 if pci_id in pci_device_ids else accm

    device_count = functools.reduce(_pci_count, response, 0)

    return device_count