173 lines
5.4 KiB
Python
Executable File
173 lines
5.4 KiB
Python
Executable File
# 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
|