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 173 174
|
# 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)
def get_deploy_steps(self, node, ports):
"""Alias wrapper for method get_clean_steps."""
return self.get_clean_steps(node, ports)
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)
|