2f09cf8d42
This patch adds the ability to detect and manage GPU and Disk expansion bays, and either add their internal components into the device corresponding to the blade server, or into a dedicated device. It takes advantage of the work made by @cyrinux on GPU bays management, and applies the same principle to the external disk bays, but harmonize the inventory management: - If no argument is specified on the command line, the GPU cards, RAID controllers and their attached disks are added in the blade device, and the device corresponding to an expansion device is deleted. - If the `--expansion-as-device` option is specified on the command line, a dedicated device corresponding to the expansion bay is created, and the GPUs, RAID card and attached disks are removed from the blade device and added to the expansion device.
129 lines
3.9 KiB
Python
129 lines
3.9 KiB
Python
import re
|
|
import subprocess
|
|
|
|
from netbox_agent.config import config
|
|
from netbox_agent.misc import get_vendor
|
|
from netbox_agent.raid.base import Raid, RaidController
|
|
|
|
REGEXP_CONTROLLER_HP = re.compile(r'Smart Array ([a-zA-Z0-9- ]+) in Slot ([0-9]+)')
|
|
|
|
|
|
def _parse_ctrl_output(lines):
|
|
controllers = {}
|
|
current_ctrl = None
|
|
|
|
for line in lines:
|
|
if not line or line.startswith('Note:'):
|
|
continue
|
|
ctrl = REGEXP_CONTROLLER_HP.search(line)
|
|
if ctrl is not None:
|
|
current_ctrl = ctrl.group(1)
|
|
controllers[current_ctrl] = {"Slot": ctrl.group(2)}
|
|
if "Embedded" not in line:
|
|
controllers[current_ctrl]["External"] = True
|
|
continue
|
|
attr, val = line.split(": ", 1)
|
|
attr = attr.strip()
|
|
val = val.strip()
|
|
controllers[current_ctrl][attr] = val
|
|
return controllers
|
|
|
|
|
|
def _parse_pd_output(lines):
|
|
drives = {}
|
|
current_array = None
|
|
current_drv = None
|
|
|
|
for line in lines:
|
|
line = line.strip()
|
|
if not line or line.startswith('Note:'):
|
|
continue
|
|
# Parses the Array the drives are in
|
|
if line.startswith("Array"):
|
|
current_array = line.split(None, 1)[1]
|
|
# Detects new physical drive
|
|
if line.startswith("physicaldrive"):
|
|
current_drv = line.split(None, 1)[1]
|
|
drives[current_drv] = {}
|
|
if current_array is not None:
|
|
drives[current_drv]["Array"] = current_array
|
|
continue
|
|
if ": " not in line:
|
|
continue
|
|
attr, val = line.split(": ", 1)
|
|
drives.setdefault(current_drv, {})[attr] = val
|
|
return drives
|
|
|
|
|
|
class HPRaidController(RaidController):
|
|
def __init__(self, controller_name, data):
|
|
self.controller_name = controller_name
|
|
self.data = data
|
|
self.drives = self._get_physical_disks()
|
|
|
|
def get_product_name(self):
|
|
return self.controller_name
|
|
|
|
def get_manufacturer(self):
|
|
return 'HP'
|
|
|
|
def get_serial_number(self):
|
|
return self.data['Serial Number']
|
|
|
|
def get_firmware_version(self):
|
|
return self.data['Firmware Version']
|
|
|
|
def is_external(self):
|
|
return self.data.get('External', False)
|
|
|
|
def _get_physical_disks(self):
|
|
output = subprocess.getoutput(
|
|
'ssacli ctrl slot={slot} pd all show detail'.format(slot=self.data['Slot'])
|
|
)
|
|
lines = output.split('\n')
|
|
lines = list(filter(None, lines))
|
|
drives = _parse_pd_output(lines)
|
|
ret = []
|
|
|
|
for name, attrs in drives.items():
|
|
model = attrs.get('Model', '').strip()
|
|
vendor = None
|
|
if model.startswith('HP'):
|
|
vendor = 'HP'
|
|
elif len(model.split()) > 1:
|
|
vendor = get_vendor(model.split()[1])
|
|
else:
|
|
vendor = get_vendor(model)
|
|
|
|
ret.append({
|
|
'Model': model,
|
|
'Vendor': vendor,
|
|
'SN': attrs.get('Serial Number', '').strip(),
|
|
'Size': attrs.get('Size', '').strip(),
|
|
'Type': 'SSD' if attrs.get('Interface Type') == 'Solid State SATA'
|
|
else 'HDD',
|
|
'_src': self.__class__.__name__,
|
|
})
|
|
return ret
|
|
|
|
def get_physical_disks(self):
|
|
return self.drives
|
|
|
|
|
|
class HPRaid(Raid):
|
|
def __init__(self):
|
|
self.output = subprocess.getoutput('ssacli ctrl all show detail')
|
|
self.controllers = []
|
|
self.convert_to_dict()
|
|
|
|
def convert_to_dict(self):
|
|
lines = self.output.split('\n')
|
|
lines = list(filter(None, lines))
|
|
controllers = _parse_ctrl_output(lines)
|
|
for controller, attrs in controllers.items():
|
|
self.controllers.append(
|
|
HPRaidController(controller, attrs)
|
|
)
|
|
|
|
def get_controllers(self):
|
|
return self.controllers
|