
Initial code patches for service steps have merged in ironic, and it is now time to add support into the agent which allows service steps to be raised to the service. Updates the default hardware manager version to 1.2, which has *rarely* been incremented due to oversight. Change-Id: Iabd2c6c551389ec3c24e94b71245b1250345f7a7
174 lines
6.7 KiB
Python
174 lines
6.7 KiB
Python
# Copyright 2016 Mellanox Technologies, Ltd
|
|
#
|
|
# 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 os
|
|
|
|
from oslo_log import log
|
|
|
|
from ironic_python_agent import errors
|
|
from ironic_python_agent import hardware
|
|
from ironic_python_agent.hardware_managers.nvidia import nvidia_fw_update
|
|
from ironic_python_agent import netutils
|
|
|
|
LOG = log.getLogger()
|
|
# Mellanox NIC Vendor ID
|
|
MLNX_VENDOR_ID = '0x15b3'
|
|
# Mellanox Prefix to generate InfiniBand CLient-ID
|
|
MLNX_INFINIBAND_CLIENT_ID_PREFIX = 'ff:00:00:00:00:00:02:00:00:02:c9:00:'
|
|
|
|
|
|
def _infiniband_address_to_mac(address):
|
|
"""Convert InfiniBand address to MAC
|
|
|
|
Convert InfiniBand address to MAC by Mellanox specific
|
|
translation. The InfiniBand address is 59 characters
|
|
composed from GID:GUID. The last 24 characters are the
|
|
GUID. The InfiniBand MAC is upper 10 characters and lower
|
|
9 characters from the GUID
|
|
Example:
|
|
address - a0:00:00:27:fe:80:00:00:00:00:00:00:7c:fe:90:03:00:29:26:52
|
|
GUID - 7c:fe:90:03:00:29:26:52
|
|
InfiniBand MAC - 7c:fe:90:29:26:52
|
|
|
|
:param address: InfiniBand Address.
|
|
:returns: InfiniBand MAC.
|
|
"""
|
|
return address[36:-14] + address[51:]
|
|
|
|
|
|
def _generate_client_id(address):
|
|
"""Generate client id from InfiniBand address
|
|
|
|
:param address: InfiniBand address.
|
|
:returns: client id.
|
|
"""
|
|
return MLNX_INFINIBAND_CLIENT_ID_PREFIX + address[36:]
|
|
|
|
|
|
def _detect_hardware():
|
|
"""method for detection of Mellanox NICs
|
|
|
|
:returns: Boolean value. True if the machine contain one
|
|
or more Mellanox NIC(s), False otherwise.
|
|
"""
|
|
iface_names = os.listdir('/sys/class/net')
|
|
for ifname in iface_names:
|
|
if (hardware._get_device_info(
|
|
ifname, 'net', 'vendor') == MLNX_VENDOR_ID):
|
|
return True
|
|
return False
|
|
|
|
|
|
class MellanoxDeviceHardwareManager(hardware.HardwareManager):
|
|
"""Mellanox hardware manager to support a single device"""
|
|
|
|
HARDWARE_MANAGER_NAME = 'MellanoxDeviceHardwareManager'
|
|
HARDWARE_MANAGER_VERSION = '1'
|
|
|
|
def evaluate_hardware_support(self):
|
|
"""Declare level of hardware support provided."""
|
|
|
|
if _detect_hardware():
|
|
LOG.debug('Found Mellanox device')
|
|
return hardware.HardwareSupport.MAINLINE
|
|
else:
|
|
LOG.debug('No Mellanox devices found')
|
|
return hardware.HardwareSupport.NONE
|
|
|
|
def get_interface_info(self, interface_name):
|
|
"""Return the interface information when its Mellanox and InfiniBand
|
|
|
|
In case of Mellanox and InfiniBand interface we do the following:
|
|
1. Calculate the "InfiniBand MAC" according to InfiniBand GUID
|
|
2. Calculate the client-id according to InfiniBand GUID
|
|
"""
|
|
|
|
address = netutils.get_mac_addr(interface_name)
|
|
if address is None:
|
|
raise errors.IncompatibleHardwareMethodError()
|
|
vendor = hardware._get_device_info(interface_name, 'net', 'vendor')
|
|
if (len(address) != netutils.INFINIBAND_ADDR_LEN
|
|
or vendor != MLNX_VENDOR_ID):
|
|
raise errors.IncompatibleHardwareMethodError()
|
|
|
|
mac_addr = _infiniband_address_to_mac(address)
|
|
client_id = _generate_client_id(address)
|
|
|
|
return hardware.NetworkInterface(
|
|
interface_name, mac_addr,
|
|
ipv4_address=netutils.get_ipv4_addr(interface_name),
|
|
has_carrier=netutils.interface_has_carrier(interface_name),
|
|
lldp=None,
|
|
vendor=vendor,
|
|
product=hardware._get_device_info(interface_name, 'net', 'device'),
|
|
client_id=client_id)
|
|
|
|
def get_clean_steps(self, node, ports):
|
|
"""Get a list of clean steps with priority.
|
|
|
|
:param node: The node object as provided by Ironic.
|
|
:param ports: Port objects as provided by Ironic.
|
|
:returns: A list of cleaning steps, as a list of dicts.
|
|
"""
|
|
return [{'step': 'update_nvidia_nic_firmware_image',
|
|
'priority': 0,
|
|
'interface': 'deploy',
|
|
'reboot_requested': True,
|
|
'abortable': False,
|
|
'argsinfo': {
|
|
'images': {
|
|
'description': 'Json blob contains a list of images,'
|
|
' where each image contains a map of '
|
|
'url: to firmware image (file://, '
|
|
'http://), '
|
|
'checksum: of the provided image, '
|
|
'checksumType: md5/sha512/sha256, '
|
|
'componentProfile: PSID of the nic, '
|
|
'version: of the FW',
|
|
'required': True,
|
|
}, }
|
|
},
|
|
{'step': 'update_nvidia_nic_firmware_settings',
|
|
'priority': 0,
|
|
'interface': 'deploy',
|
|
'reboot_requested': True,
|
|
'abortable': False,
|
|
'argsinfo': {
|
|
'settings': {
|
|
'description': 'Json blob contains a list of '
|
|
'settings per device ID, where each '
|
|
'settings contains a map of '
|
|
'deviceID: device ID '
|
|
'globalConfig: global config '
|
|
'function0Config: function 0 config '
|
|
'function1Config: function 1 config',
|
|
'required': True,
|
|
}, }
|
|
}
|
|
]
|
|
|
|
def get_service_steps(self, node, ports):
|
|
"""Alias wrapper for method get_clean_steps."""
|
|
# NOTE(TheJulia): Since these steps can be run upon service, why not
|
|
return self.get_clean_steps(node, ports)
|
|
|
|
# TODO(TheJulia): Should there be a get_deploy_steps handler here? Since
|
|
# flashing firmware on deploy is a valid case.
|
|
|
|
def update_nvidia_nic_firmware_image(self, node, ports, images):
|
|
nvidia_fw_update.update_nvidia_nic_firmware_image(images)
|
|
|
|
def update_nvidia_nic_firmware_settings(self, node, ports, settings):
|
|
nvidia_fw_update.update_nvidia_nic_firmware_settings(settings)
|