netbox-agent/netbox_agent/raid/storcli.py

153 lines
5.2 KiB
Python

import json
import logging
import os
import re
import subprocess
from netbox_agent.config import config
from netbox_agent.misc import get_mount_points, get_vendor
from netbox_agent.raid.base import Raid, RaidController
class StorcliControllerError(Exception):
pass
def storecli(sub_command):
command = ["storcli"]
command.extend(sub_command.split())
command.append("J")
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, stderr = p.communicate()
if stderr:
mesg = "Failed to execute command '{}':\n{}".format(" ".join(command), stdout)
raise StorcliControllerError(mesg)
stdout = stdout.decode("utf-8")
data = json.loads(stdout)
controllers = dict(
[
(c["Command Status"]["Controller"], c["Response Data"])
for c in data["Controllers"]
if c["Command Status"]["Status"] == "Success"
]
)
if not controllers:
logging.error(
"Failed to execute command '{}'. "
"Ignoring data.".format(" ".join(command))
)
return {}
return controllers
class StorcliController(RaidController):
def __init__(self, controller_index, data):
self.data = data
self.controller_index = controller_index
def get_product_name(self):
return self.data["Product Name"]
def get_manufacturer(self):
return None
def get_serial_number(self):
return self.data["Serial Number"]
def get_firmware_version(self):
return self.data["FW Package Build"]
def _get_physical_disks(self):
pds = {}
cmd = "/c{}/eall/sall show all".format(self.controller_index)
controllers = storecli(cmd)
pd_info = controllers[self.controller_index]
pd_re = re.compile(r"^Drive (/c\d+/e\d+/s\d+)$")
for section, attrs in pd_info.items():
reg = pd_re.search(section)
if reg is None:
continue
pd_name = reg.group(1)
pd_attr = attrs[0]
pd_identifier = pd_attr["EID:Slt"]
size = pd_attr.get("Size", "").strip()
media_type = pd_attr.get("Med", "").strip()
pd_details = pd_info["{} - Detailed Information".format(section)]
pd_dev_attr = pd_details["{} Device attributes".format(section)]
model = pd_dev_attr.get("Model Number", "").strip()
pd = {
"Model": model,
"Vendor": get_vendor(model),
"SN": pd_dev_attr.get("SN", "").strip(),
"Size": size,
"Type": media_type,
"_src": self.__class__.__name__,
}
if config.process_virtual_drives:
pd.setdefault("custom_fields", {})["pd_identifier"] = pd_name
pds[pd_identifier] = pd
return pds
def _get_virtual_drives_map(self):
vds = {}
cmd = "/c{}/vall show all".format(self.controller_index)
controllers = storecli(cmd)
vd_info = controllers[self.controller_index]
mount_points = get_mount_points()
for vd_identifier, vd_attrs in vd_info.items():
if not vd_identifier.startswith("/c{}/v".format(self.controller_index)):
continue
volume = vd_identifier.split("/")[-1].lstrip("v")
vd_attr = vd_attrs[0]
vd_pd_identifier = "PDs for VD {}".format(volume)
vd_pds = vd_info[vd_pd_identifier]
vd_prop_identifier = "VD{} Properties".format(volume)
vd_properties = vd_info[vd_prop_identifier]
for pd in vd_pds:
pd_identifier = pd["EID:Slt"]
wwn = vd_properties["SCSI NAA Id"]
wwn_path = "/dev/disk/by-id/wwn-0x{}".format(wwn)
device = os.path.realpath(wwn_path)
mp = mount_points.get(device, "n/a")
vds[pd_identifier] = {
"vd_array": vd_identifier,
"vd_size": vd_attr["Size"],
"vd_consistency": vd_attr["Consist"],
"vd_raid_type": vd_attr["TYPE"],
"vd_device": device,
"mount_point": ", ".join(sorted(mp)),
}
return vds
def get_physical_disks(self):
# Parses physical disks information
pds = self._get_physical_disks()
# Parses virtual drives information and maps them to physical disks
vds = self._get_virtual_drives_map()
for pd_identifier, vd in vds.items():
if pd_identifier not in pds:
logging.error(
"Physical drive {} listed in virtual drive {} not "
"found in drives list".format(pd_identifier, vd["vd_array"])
)
continue
pds[pd_identifier].setdefault("custom_fields", {}).update(vd)
return list(pds.values())
class StorcliRaid(Raid):
def __init__(self):
self.controllers = []
controllers = storecli("/call show")
for controller_id, controller_data in controllers.items():
self.controllers.append(StorcliController(controller_id, controller_data))
def get_controllers(self):
return self.controllers