2024-10-21 12:55:54 +02:00
|
|
|
import json
|
|
|
|
import logging
|
|
|
|
import re
|
|
|
|
import sys
|
|
|
|
import traceback
|
|
|
|
|
|
|
|
import pynetbox
|
|
|
|
|
2020-02-02 20:08:56 +01:00
|
|
|
from netbox_agent.config import config
|
|
|
|
from netbox_agent.config import netbox_instance as nb
|
|
|
|
from netbox_agent.lshw import LSHW
|
|
|
|
from netbox_agent.misc import get_vendor, is_tool
|
2019-08-26 16:54:48 +02:00
|
|
|
from netbox_agent.raid.hp import HPRaid
|
2019-09-10 11:44:23 +02:00
|
|
|
from netbox_agent.raid.omreport import OmreportRaid
|
2019-08-26 16:54:48 +02:00
|
|
|
from netbox_agent.raid.storcli import StorcliRaid
|
|
|
|
|
|
|
|
INVENTORY_TAG = {
|
2024-10-21 12:55:54 +02:00
|
|
|
"cpu": {"name": "hw:cpu", "slug": "hw-cpu"},
|
|
|
|
"gpu": {"name": "hw:gpu", "slug": "hw-gpu"},
|
|
|
|
"disk": {"name": "hw:disk", "slug": "hw-disk"},
|
|
|
|
"interface": {"name": "hw:interface", "slug": "hw-interface"},
|
|
|
|
"memory": {"name": "hw:memory", "slug": "hw-memory"},
|
|
|
|
"motherboard": {"name": "hw:motherboard", "slug": "hw-motherboard"},
|
|
|
|
"raid_card": {"name": "hw:raid_card", "slug": "hw-raid-card"},
|
2020-02-02 20:08:56 +01:00
|
|
|
}
|
2019-08-26 16:54:48 +02:00
|
|
|
|
|
|
|
|
2024-10-21 12:55:54 +02:00
|
|
|
class Inventory:
|
2019-08-26 16:54:48 +02:00
|
|
|
"""
|
|
|
|
Better Inventory items coming, see:
|
|
|
|
- https://github.com/netbox-community/netbox/issues/3087
|
|
|
|
- https://github.com/netbox-community/netbox/issues/3333
|
|
|
|
|
|
|
|
This class implements for:
|
|
|
|
* memory
|
|
|
|
* cpu
|
|
|
|
* raid cards
|
|
|
|
* disks
|
2020-09-18 12:29:05 +02:00
|
|
|
* gpus
|
2019-08-26 16:54:48 +02:00
|
|
|
|
|
|
|
methods that:
|
|
|
|
* get local item
|
|
|
|
* get netbox item
|
|
|
|
* create netbox item
|
|
|
|
* update netbox item
|
|
|
|
|
|
|
|
Known issues:
|
|
|
|
- no scan of non-raid devices
|
|
|
|
- no scan of NVMe devices
|
|
|
|
"""
|
|
|
|
|
Manage blade expansions as independent devices
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.
2022-02-11 18:22:13 +01:00
|
|
|
def __init__(self, server, update_expansion=False):
|
2019-09-03 13:16:37 +02:00
|
|
|
self.create_netbox_tags()
|
2019-08-26 16:54:48 +02:00
|
|
|
self.server = server
|
Manage blade expansions as independent devices
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.
2022-02-11 18:22:13 +01:00
|
|
|
self.update_expansion = update_expansion
|
|
|
|
netbox_server = self.server.get_netbox_server(update_expansion)
|
2019-09-05 15:13:36 +02:00
|
|
|
|
2019-09-03 13:16:37 +02:00
|
|
|
self.device_id = netbox_server.id if netbox_server else None
|
2019-08-26 16:54:48 +02:00
|
|
|
self.raid = None
|
|
|
|
self.disks = []
|
|
|
|
|
2019-09-05 15:13:36 +02:00
|
|
|
self.lshw = LSHW()
|
|
|
|
|
2019-09-04 11:18:44 +02:00
|
|
|
def create_netbox_tags(self):
|
2021-05-11 21:45:00 +02:00
|
|
|
ret = []
|
2019-09-03 13:16:37 +02:00
|
|
|
for key, tag in INVENTORY_TAG.items():
|
2024-10-21 12:55:54 +02:00
|
|
|
nb_tag = nb.extras.tags.get(name=tag["name"])
|
2019-09-03 13:16:37 +02:00
|
|
|
if not nb_tag:
|
|
|
|
nb_tag = nb.extras.tags.create(
|
2024-10-21 12:55:54 +02:00
|
|
|
name=tag["name"],
|
|
|
|
slug=tag["slug"],
|
|
|
|
comments=tag["name"],
|
2019-09-03 13:16:37 +02:00
|
|
|
)
|
2021-05-11 21:45:00 +02:00
|
|
|
ret.append(nb_tag)
|
|
|
|
return ret
|
2019-09-03 13:16:37 +02:00
|
|
|
|
2019-09-05 15:13:36 +02:00
|
|
|
def find_or_create_manufacturer(self, name):
|
|
|
|
if name is None:
|
|
|
|
return None
|
|
|
|
|
|
|
|
manufacturer = nb.dcim.manufacturers.get(
|
|
|
|
name=name,
|
|
|
|
)
|
|
|
|
if not manufacturer:
|
2024-10-21 12:55:54 +02:00
|
|
|
logging.info("Creating missing manufacturer {name}".format(name=name))
|
2019-09-05 15:13:36 +02:00
|
|
|
manufacturer = nb.dcim.manufacturers.create(
|
|
|
|
name=name,
|
2024-10-21 12:55:54 +02:00
|
|
|
slug=re.sub("[^A-Za-z0-9]+", "-", name).lower(),
|
2019-09-05 15:13:36 +02:00
|
|
|
)
|
|
|
|
|
2024-10-21 12:55:54 +02:00
|
|
|
logging.info("Creating missing manufacturer {name}".format(name=name))
|
2019-08-26 16:54:48 +02:00
|
|
|
|
2019-09-05 15:13:36 +02:00
|
|
|
return manufacturer
|
|
|
|
|
|
|
|
def get_netbox_inventory(self, device_id, tag):
|
|
|
|
try:
|
2024-10-21 12:55:54 +02:00
|
|
|
items = nb.dcim.inventory_items.filter(device_id=device_id, tag=tag)
|
2019-09-05 15:13:36 +02:00
|
|
|
except pynetbox.core.query.RequestError:
|
2024-10-21 12:55:54 +02:00
|
|
|
logging.info("Tag {tag} is missing, returning empty array.".format(tag=tag))
|
2019-09-05 15:13:36 +02:00
|
|
|
items = []
|
|
|
|
|
2021-05-11 21:45:00 +02:00
|
|
|
return list(items)
|
2019-09-05 15:13:36 +02:00
|
|
|
|
2024-10-21 12:55:54 +02:00
|
|
|
def create_netbox_inventory_item(
|
|
|
|
self, device_id, tags, vendor, name, serial, description
|
|
|
|
):
|
2019-09-05 15:13:36 +02:00
|
|
|
manufacturer = self.find_or_create_manufacturer(vendor)
|
|
|
|
|
|
|
|
_ = nb.dcim.inventory_items.create(
|
|
|
|
device=device_id,
|
|
|
|
manufacturer=manufacturer.id,
|
|
|
|
discovered=True,
|
|
|
|
tags=tags,
|
2024-10-21 12:55:54 +02:00
|
|
|
name="{}".format(name),
|
|
|
|
serial="{}".format(serial),
|
|
|
|
description=description,
|
2019-09-05 15:13:36 +02:00
|
|
|
)
|
|
|
|
|
2024-10-21 12:55:54 +02:00
|
|
|
logging.info(
|
|
|
|
"Creating inventory item {} {}/{} {} ".format(
|
|
|
|
vendor, name, serial, description
|
|
|
|
)
|
2019-09-05 15:13:36 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
def get_hw_motherboards(self):
|
|
|
|
motherboards = []
|
|
|
|
|
|
|
|
m = {}
|
2024-10-21 12:55:54 +02:00
|
|
|
m["serial"] = self.lshw.motherboard_serial
|
|
|
|
m["vendor"] = self.lshw.vendor
|
|
|
|
m["name"] = "{} {}".format(self.lshw.vendor, self.lshw.motherboard)
|
|
|
|
m["description"] = "{} Motherboard".format(self.lshw.motherboard)
|
2019-09-05 15:13:36 +02:00
|
|
|
|
|
|
|
motherboards.append(m)
|
|
|
|
|
|
|
|
return motherboards
|
|
|
|
|
|
|
|
def do_netbox_motherboard(self):
|
|
|
|
|
|
|
|
motherboards = self.get_hw_motherboards()
|
|
|
|
nb_motherboards = self.get_netbox_inventory(
|
2024-10-21 12:55:54 +02:00
|
|
|
device_id=self.device_id, tag=INVENTORY_TAG["motherboard"]["slug"]
|
|
|
|
)
|
2019-09-05 15:13:36 +02:00
|
|
|
|
|
|
|
for nb_motherboard in nb_motherboards:
|
2024-10-21 12:55:54 +02:00
|
|
|
if nb_motherboard.serial not in [x["serial"] for x in motherboards]:
|
|
|
|
logging.info(
|
|
|
|
"Deleting unknown motherboard {motherboard}/{serial}".format(
|
|
|
|
motherboard=self.lshw.motherboard,
|
|
|
|
serial=nb_motherboard.serial,
|
|
|
|
)
|
|
|
|
)
|
2019-09-05 15:13:36 +02:00
|
|
|
nb_motherboard.delete()
|
|
|
|
|
|
|
|
# create interfaces that are not in netbox
|
|
|
|
for motherboard in motherboards:
|
2024-10-21 12:55:54 +02:00
|
|
|
if motherboard.get("serial") not in [x.serial for x in nb_motherboards]:
|
2019-09-05 15:13:36 +02:00
|
|
|
self.create_netbox_inventory_item(
|
|
|
|
device_id=self.device_id,
|
2024-10-21 12:55:54 +02:00
|
|
|
tags=[{"name": INVENTORY_TAG["motherboard"]["name"]}],
|
|
|
|
vendor="{}".format(motherboard.get("vendor", "N/A")),
|
|
|
|
serial="{}".format(motherboard.get("serial", "No SN")),
|
|
|
|
name="{}".format(motherboard.get("name")),
|
|
|
|
description="{}".format(motherboard.get("description")),
|
2019-09-05 15:13:36 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
def create_netbox_interface(self, iface):
|
|
|
|
manufacturer = self.find_or_create_manufacturer(iface["vendor"])
|
|
|
|
_ = nb.dcim.inventory_items.create(
|
|
|
|
device=self.device_id,
|
|
|
|
manufacturer=manufacturer.id,
|
|
|
|
discovered=True,
|
2024-10-21 12:55:54 +02:00
|
|
|
tags=[{"name": INVENTORY_TAG["interface"]["name"]}],
|
|
|
|
name="{}".format(iface["product"]),
|
|
|
|
serial="{}".format(iface["serial"]),
|
|
|
|
description="{} {}".format(iface["description"], iface["name"]),
|
2019-09-05 15:13:36 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
def do_netbox_interfaces(self):
|
|
|
|
nb_interfaces = self.get_netbox_inventory(
|
2024-10-21 12:55:54 +02:00
|
|
|
device_id=self.device_id, tag=INVENTORY_TAG["interface"]["slug"]
|
|
|
|
)
|
2019-09-05 15:13:36 +02:00
|
|
|
interfaces = self.lshw.interfaces
|
|
|
|
|
|
|
|
# delete interfaces that are in netbox but not locally
|
|
|
|
# use the serial_number has the comparison element
|
|
|
|
for nb_interface in nb_interfaces:
|
2024-10-21 12:55:54 +02:00
|
|
|
if nb_interface.serial not in [x["serial"] for x in interfaces]:
|
|
|
|
logging.info(
|
|
|
|
"Deleting unknown interface {serial}".format(
|
|
|
|
serial=nb_interface.serial,
|
|
|
|
)
|
|
|
|
)
|
2019-09-05 15:13:36 +02:00
|
|
|
nb_interface.delete()
|
|
|
|
|
|
|
|
# create interfaces that are not in netbox
|
|
|
|
for iface in interfaces:
|
2024-10-21 12:55:54 +02:00
|
|
|
if iface.get("serial") not in [x.serial for x in nb_interfaces]:
|
2019-09-05 15:13:36 +02:00
|
|
|
self.create_netbox_interface(iface)
|
2019-08-26 16:54:48 +02:00
|
|
|
|
|
|
|
def create_netbox_cpus(self):
|
2024-10-21 12:55:54 +02:00
|
|
|
for cpu in self.lshw.get_hw_linux("cpu"):
|
2019-09-05 15:13:36 +02:00
|
|
|
manufacturer = self.find_or_create_manufacturer(cpu["vendor"])
|
2019-08-26 16:54:48 +02:00
|
|
|
_ = nb.dcim.inventory_items.create(
|
|
|
|
device=self.device_id,
|
2019-09-05 15:13:36 +02:00
|
|
|
manufacturer=manufacturer.id,
|
2019-08-26 16:54:48 +02:00
|
|
|
discovered=True,
|
2024-10-21 12:55:54 +02:00
|
|
|
tags=[{"name": INVENTORY_TAG["cpu"]["name"]}],
|
|
|
|
name=cpu["product"],
|
|
|
|
description="CPU {}".format(cpu["location"]),
|
2019-09-05 15:13:36 +02:00
|
|
|
# asset_tag=cpu['location']
|
2019-08-26 16:54:48 +02:00
|
|
|
)
|
|
|
|
|
2024-10-21 12:55:54 +02:00
|
|
|
logging.info("Creating CPU model {}".format(cpu["product"]))
|
2019-09-05 15:13:36 +02:00
|
|
|
|
|
|
|
def do_netbox_cpus(self):
|
2024-10-21 12:55:54 +02:00
|
|
|
cpus = self.lshw.get_hw_linux("cpu")
|
2019-09-05 15:13:36 +02:00
|
|
|
nb_cpus = self.get_netbox_inventory(
|
2019-08-26 16:54:48 +02:00
|
|
|
device_id=self.device_id,
|
2024-10-21 12:55:54 +02:00
|
|
|
tag=INVENTORY_TAG["cpu"]["slug"],
|
2019-08-26 16:54:48 +02:00
|
|
|
)
|
|
|
|
|
2024-10-21 12:55:54 +02:00
|
|
|
if not len(nb_cpus) or len(nb_cpus) and len(cpus) != len(nb_cpus):
|
2019-08-26 16:54:48 +02:00
|
|
|
for x in nb_cpus:
|
|
|
|
x.delete()
|
2019-09-05 15:13:36 +02:00
|
|
|
|
2019-08-26 16:54:48 +02:00
|
|
|
self.create_netbox_cpus()
|
|
|
|
|
Manage blade expansions as independent devices
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.
2022-02-11 18:22:13 +01:00
|
|
|
def get_raid_cards(self, filter_cards=False):
|
2019-09-12 11:12:32 +02:00
|
|
|
raid_class = None
|
2024-10-21 12:55:54 +02:00
|
|
|
if self.server.manufacturer in ("Dell", "Huawei"):
|
|
|
|
if is_tool("omreport"):
|
2019-09-12 11:12:32 +02:00
|
|
|
raid_class = OmreportRaid
|
2024-10-21 12:55:54 +02:00
|
|
|
if is_tool("storcli"):
|
2019-09-12 11:12:32 +02:00
|
|
|
raid_class = StorcliRaid
|
2024-10-21 12:55:54 +02:00
|
|
|
elif self.server.manufacturer in ("HP", "HPE"):
|
|
|
|
if is_tool("ssacli"):
|
2019-09-12 11:12:32 +02:00
|
|
|
raid_class = HPRaid
|
2019-08-26 16:54:48 +02:00
|
|
|
|
2019-09-12 11:12:32 +02:00
|
|
|
if not raid_class:
|
2019-08-29 17:33:27 +02:00
|
|
|
return []
|
2019-08-26 16:54:48 +02:00
|
|
|
|
2019-09-12 11:12:32 +02:00
|
|
|
self.raid = raid_class()
|
Manage blade expansions as independent devices
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.
2022-02-11 18:22:13 +01:00
|
|
|
|
2024-10-21 12:55:54 +02:00
|
|
|
if (
|
|
|
|
filter_cards
|
|
|
|
and config.expansion_as_device
|
|
|
|
and self.server.own_expansion_slot()
|
|
|
|
):
|
Manage blade expansions as independent devices
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.
2022-02-11 18:22:13 +01:00
|
|
|
return [
|
2024-10-21 12:55:54 +02:00
|
|
|
c
|
|
|
|
for c in self.raid.get_controllers()
|
Manage blade expansions as independent devices
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.
2022-02-11 18:22:13 +01:00
|
|
|
if c.is_external() is self.update_expansion
|
|
|
|
]
|
|
|
|
else:
|
|
|
|
return self.raid.get_controllers()
|
2019-08-26 16:54:48 +02:00
|
|
|
|
|
|
|
def create_netbox_raid_card(self, raid_card):
|
2024-10-21 12:55:54 +02:00
|
|
|
manufacturer = self.find_or_create_manufacturer(raid_card.get_manufacturer())
|
2019-09-05 15:13:36 +02:00
|
|
|
|
2019-08-26 16:54:48 +02:00
|
|
|
name = raid_card.get_product_name()
|
|
|
|
serial = raid_card.get_serial_number()
|
|
|
|
nb_raid_card = nb.dcim.inventory_items.create(
|
|
|
|
device=self.device_id,
|
|
|
|
discovered=True,
|
|
|
|
manufacturer=manufacturer.id if manufacturer else None,
|
2024-10-21 12:55:54 +02:00
|
|
|
tags=[{"name": INVENTORY_TAG["raid_card"]["name"]}],
|
|
|
|
name="{}".format(name),
|
|
|
|
serial="{}".format(serial),
|
|
|
|
description="RAID Card",
|
|
|
|
)
|
|
|
|
logging.info(
|
|
|
|
"Creating RAID Card {name} (SN: {serial})".format(
|
|
|
|
name=name,
|
|
|
|
serial=serial,
|
|
|
|
)
|
2019-08-26 16:54:48 +02:00
|
|
|
)
|
|
|
|
return nb_raid_card
|
|
|
|
|
2019-09-10 11:51:05 +02:00
|
|
|
def do_netbox_raid_cards(self):
|
2019-08-26 16:54:48 +02:00
|
|
|
"""
|
|
|
|
Update raid cards in netbobx
|
|
|
|
Since we only push:
|
|
|
|
* Name
|
|
|
|
* Manufacturer
|
|
|
|
* Serial
|
|
|
|
|
|
|
|
We only need to handle destroy and new cards
|
|
|
|
"""
|
|
|
|
|
2019-09-05 15:13:36 +02:00
|
|
|
nb_raid_cards = self.get_netbox_inventory(
|
2024-10-21 12:55:54 +02:00
|
|
|
device_id=self.device_id, tag=[INVENTORY_TAG["raid_card"]["slug"]]
|
2020-02-02 20:08:56 +01:00
|
|
|
)
|
Manage blade expansions as independent devices
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.
2022-02-11 18:22:13 +01:00
|
|
|
raid_cards = self.get_raid_cards(filter_cards=True)
|
2019-08-26 16:54:48 +02:00
|
|
|
|
|
|
|
# delete cards that are in netbox but not locally
|
|
|
|
# use the serial_number has the comparison element
|
|
|
|
for nb_raid_card in nb_raid_cards:
|
|
|
|
if nb_raid_card.serial not in [x.get_serial_number() for x in raid_cards]:
|
2024-10-21 12:55:54 +02:00
|
|
|
logging.info(
|
|
|
|
"Deleting unknown locally RAID Card {serial}".format(
|
|
|
|
serial=nb_raid_card.serial,
|
|
|
|
)
|
|
|
|
)
|
2019-08-26 16:54:48 +02:00
|
|
|
nb_raid_card.delete()
|
|
|
|
|
|
|
|
# create card that are not in netbox
|
|
|
|
for raid_card in raid_cards:
|
|
|
|
if raid_card.get_serial_number() not in [x.serial for x in nb_raid_cards]:
|
|
|
|
self.create_netbox_raid_card(raid_card)
|
|
|
|
|
2022-02-25 18:43:09 +01:00
|
|
|
def is_virtual_disk(self, disk, raid_devices):
|
2024-10-21 12:55:54 +02:00
|
|
|
disk_type = disk.get("type")
|
|
|
|
logicalname = disk.get("logicalname")
|
|
|
|
description = disk.get("description")
|
|
|
|
size = disk.get("size")
|
|
|
|
product = disk.get("product")
|
|
|
|
if (
|
|
|
|
logicalname in raid_devices
|
|
|
|
or disk_type is None
|
|
|
|
or product is None
|
|
|
|
or description is None
|
|
|
|
):
|
2022-02-25 18:43:09 +01:00
|
|
|
return True
|
2019-09-05 15:13:36 +02:00
|
|
|
non_raid_disks = [
|
2024-10-21 12:55:54 +02:00
|
|
|
"MR9361-8i",
|
2020-02-02 20:08:56 +01:00
|
|
|
]
|
2019-09-05 15:13:36 +02:00
|
|
|
|
2024-10-21 12:55:54 +02:00
|
|
|
if (
|
|
|
|
logicalname in raid_devices
|
|
|
|
or product in non_raid_disks
|
|
|
|
or "virtual" in product.lower()
|
|
|
|
or "logical" in product.lower()
|
|
|
|
or "volume" in description.lower()
|
|
|
|
or "dvd-ram" in description.lower()
|
|
|
|
or description == "SCSI Enclosure"
|
|
|
|
or (size is None and logicalname is None)
|
|
|
|
):
|
2019-09-05 15:13:36 +02:00
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def get_hw_disks(self):
|
|
|
|
disks = []
|
|
|
|
|
2022-02-25 18:43:09 +01:00
|
|
|
for raid_card in self.get_raid_cards(filter_cards=True):
|
|
|
|
disks.extend(raid_card.get_physical_disks())
|
|
|
|
|
|
|
|
raid_devices = [
|
2024-10-21 12:55:54 +02:00
|
|
|
d.get("custom_fields", {}).get("vd_device")
|
2022-02-25 18:43:09 +01:00
|
|
|
for d in disks
|
2024-10-21 12:55:54 +02:00
|
|
|
if d.get("custom_fields", {}).get("vd_device")
|
2022-02-25 18:43:09 +01:00
|
|
|
]
|
|
|
|
|
2019-09-05 15:13:36 +02:00
|
|
|
for disk in self.lshw.get_hw_linux("storage"):
|
2022-02-25 18:43:09 +01:00
|
|
|
if self.is_virtual_disk(disk, raid_devices):
|
2019-09-05 15:13:36 +02:00
|
|
|
continue
|
2024-10-12 15:48:40 +02:00
|
|
|
size = round(int(disk.get("size", 0)) / 1073741824, 1)
|
2022-08-23 08:55:02 +02:00
|
|
|
d = {
|
|
|
|
"name": "",
|
2024-10-21 12:55:54 +02:00
|
|
|
"Size": "{} GB".format(size),
|
|
|
|
"logicalname": disk.get("logicalname"),
|
|
|
|
"description": disk.get("description"),
|
|
|
|
"SN": disk.get("serial"),
|
|
|
|
"Model": disk.get("product"),
|
|
|
|
"Type": disk.get("type"),
|
2022-08-23 08:55:02 +02:00
|
|
|
}
|
2024-10-21 12:55:54 +02:00
|
|
|
if disk.get("vendor"):
|
|
|
|
d["Vendor"] = disk["vendor"]
|
2022-08-23 08:55:02 +02:00
|
|
|
else:
|
2024-10-21 12:55:54 +02:00
|
|
|
d["Vendor"] = get_vendor(disk["product"])
|
2022-08-23 08:55:02 +02:00
|
|
|
disks.append(d)
|
2019-08-26 16:54:48 +02:00
|
|
|
|
2019-09-05 15:13:36 +02:00
|
|
|
# remove duplicate serials
|
|
|
|
seen = set()
|
2024-10-21 12:55:54 +02:00
|
|
|
uniq = [x for x in disks if x["SN"] not in seen and not seen.add(x["SN"])]
|
2019-09-05 15:13:36 +02:00
|
|
|
return uniq
|
|
|
|
|
|
|
|
def create_netbox_disk(self, disk):
|
|
|
|
manufacturer = None
|
|
|
|
if "Vendor" in disk:
|
|
|
|
manufacturer = self.find_or_create_manufacturer(disk["Vendor"])
|
|
|
|
|
2024-10-21 12:55:54 +02:00
|
|
|
name = "{} ({})".format(disk["Model"], disk["Size"])
|
|
|
|
description = disk["Type"]
|
|
|
|
sn = disk.get("SN", "unknown")
|
2022-02-25 18:43:09 +01:00
|
|
|
|
|
|
|
parms = {
|
2024-10-21 12:55:54 +02:00
|
|
|
"device": self.device_id,
|
|
|
|
"discovered": True,
|
|
|
|
"tags": [{"name": INVENTORY_TAG["disk"]["name"]}],
|
|
|
|
"name": name,
|
|
|
|
"serial": sn,
|
|
|
|
"part_id": disk["Model"],
|
|
|
|
"description": description,
|
|
|
|
"manufacturer": getattr(manufacturer, "id", None),
|
2022-02-25 18:43:09 +01:00
|
|
|
}
|
|
|
|
if config.process_virtual_drives:
|
2024-10-21 12:55:54 +02:00
|
|
|
parms["custom_fields"] = disk.get("custom_fields", {})
|
2022-02-25 18:43:09 +01:00
|
|
|
|
|
|
|
_ = nb.dcim.inventory_items.create(**parms)
|
2019-08-26 16:54:48 +02:00
|
|
|
|
2024-10-21 12:55:54 +02:00
|
|
|
logging.info(
|
|
|
|
"Creating Disk {model} {serial}".format(
|
|
|
|
model=disk["Model"],
|
|
|
|
serial=sn,
|
|
|
|
)
|
|
|
|
)
|
2019-08-26 16:54:48 +02:00
|
|
|
|
2022-02-25 18:43:09 +01:00
|
|
|
def dump_disks_map(self, disks):
|
2024-10-21 12:55:54 +02:00
|
|
|
disk_map = [d["custom_fields"] for d in disks if "custom_fields" in d]
|
2022-02-25 18:43:09 +01:00
|
|
|
if config.dump_disks_map == "-":
|
|
|
|
f = sys.stdout
|
|
|
|
else:
|
|
|
|
f = open(config.dump_disks_map, "w")
|
2024-10-21 12:55:54 +02:00
|
|
|
f.write(json.dumps(disk_map, separators=(",", ":"), indent=4, sort_keys=True))
|
2022-02-25 18:43:09 +01:00
|
|
|
if config.dump_disks_map != "-":
|
|
|
|
f.close()
|
|
|
|
|
2019-09-05 15:13:36 +02:00
|
|
|
def do_netbox_disks(self):
|
|
|
|
nb_disks = self.get_netbox_inventory(
|
2024-10-21 12:55:54 +02:00
|
|
|
device_id=self.device_id, tag=INVENTORY_TAG["disk"]["slug"]
|
2022-02-25 18:43:09 +01:00
|
|
|
)
|
2019-09-05 15:13:36 +02:00
|
|
|
disks = self.get_hw_disks()
|
2022-02-25 18:43:09 +01:00
|
|
|
if config.dump_disks_map:
|
|
|
|
try:
|
|
|
|
self.dump_disks_map(disks)
|
|
|
|
except Exception as e:
|
|
|
|
logging.error("Failed to dump disks map: {}".format(e))
|
|
|
|
logging.debug(traceback.format_exc())
|
2024-10-21 12:55:54 +02:00
|
|
|
disk_serials = [d["SN"] for d in disks if "SN" in d]
|
2019-08-26 16:54:48 +02:00
|
|
|
|
|
|
|
# delete disks that are in netbox but not locally
|
|
|
|
# use the serial_number has the comparison element
|
|
|
|
for nb_disk in nb_disks:
|
2024-10-21 12:55:54 +02:00
|
|
|
if nb_disk.serial not in disk_serials or config.force_disk_refresh:
|
|
|
|
logging.info(
|
|
|
|
"Deleting unknown locally Disk {serial}".format(
|
|
|
|
serial=nb_disk.serial,
|
|
|
|
)
|
|
|
|
)
|
2019-08-26 16:54:48 +02:00
|
|
|
nb_disk.delete()
|
|
|
|
|
2022-02-25 18:43:09 +01:00
|
|
|
if config.force_disk_refresh:
|
|
|
|
nb_disks = self.get_netbox_inventory(
|
2024-10-21 12:55:54 +02:00
|
|
|
device_id=self.device_id, tag=INVENTORY_TAG["disk"]["slug"]
|
2022-02-25 18:43:09 +01:00
|
|
|
)
|
|
|
|
|
2019-08-26 16:54:48 +02:00
|
|
|
# create disks that are not in netbox
|
|
|
|
for disk in disks:
|
2024-10-21 12:55:54 +02:00
|
|
|
if disk.get("SN") not in [d.serial for d in nb_disks]:
|
2019-09-05 15:13:36 +02:00
|
|
|
self.create_netbox_disk(disk)
|
2019-08-26 16:54:48 +02:00
|
|
|
|
|
|
|
def create_netbox_memory(self, memory):
|
2024-10-21 12:55:54 +02:00
|
|
|
manufacturer = self.find_or_create_manufacturer(memory["vendor"])
|
|
|
|
name = "Slot {} ({}GB)".format(memory["slot"], memory["size"])
|
2019-08-26 16:54:48 +02:00
|
|
|
nb_memory = nb.dcim.inventory_items.create(
|
|
|
|
device=self.device_id,
|
|
|
|
discovered=True,
|
|
|
|
manufacturer=manufacturer.id,
|
2024-10-21 12:55:54 +02:00
|
|
|
tags=[{"name": INVENTORY_TAG["memory"]["name"]}],
|
2019-09-09 16:52:25 +02:00
|
|
|
name=name,
|
2024-10-21 12:55:54 +02:00
|
|
|
part_id=memory["product"],
|
|
|
|
serial=memory["serial"],
|
|
|
|
description=memory["description"],
|
2019-08-26 16:54:48 +02:00
|
|
|
)
|
2019-09-05 15:13:36 +02:00
|
|
|
|
2024-10-21 12:55:54 +02:00
|
|
|
logging.info(
|
|
|
|
"Creating Memory {location} {type} {size}GB".format(
|
|
|
|
location=memory["slot"],
|
|
|
|
type=memory["product"],
|
|
|
|
size=memory["size"],
|
|
|
|
)
|
|
|
|
)
|
2019-08-26 16:54:48 +02:00
|
|
|
|
2019-09-05 15:13:36 +02:00
|
|
|
return nb_memory
|
2019-08-26 16:54:48 +02:00
|
|
|
|
2019-09-05 15:13:36 +02:00
|
|
|
def do_netbox_memories(self):
|
|
|
|
memories = self.lshw.memories
|
|
|
|
nb_memories = self.get_netbox_inventory(
|
2024-10-21 12:55:54 +02:00
|
|
|
device_id=self.device_id, tag=INVENTORY_TAG["memory"]["slug"]
|
2020-02-02 20:08:56 +01:00
|
|
|
)
|
2019-08-26 16:54:48 +02:00
|
|
|
|
|
|
|
for nb_memory in nb_memories:
|
2024-10-21 12:55:54 +02:00
|
|
|
if nb_memory.serial not in [x["serial"] for x in memories]:
|
|
|
|
logging.info(
|
|
|
|
"Deleting unknown locally Memory {serial}".format(
|
|
|
|
serial=nb_memory.serial,
|
|
|
|
)
|
|
|
|
)
|
2019-08-26 16:54:48 +02:00
|
|
|
nb_memory.delete()
|
2019-09-05 15:13:36 +02:00
|
|
|
|
2019-08-26 16:54:48 +02:00
|
|
|
for memory in memories:
|
2024-10-21 12:55:54 +02:00
|
|
|
if memory.get("serial") not in [x.serial for x in nb_memories]:
|
2019-08-26 16:54:48 +02:00
|
|
|
self.create_netbox_memory(memory)
|
|
|
|
|
2022-02-21 18:12:16 +01:00
|
|
|
def create_netbox_gpus(self, gpus):
|
|
|
|
for gpu in gpus:
|
2024-10-21 12:55:54 +02:00
|
|
|
if "product" in gpu and len(gpu["product"]) > 50:
|
|
|
|
gpu["product"] = gpu["product"][:48] + ".."
|
2022-02-21 18:12:16 +01:00
|
|
|
|
2020-09-18 12:29:05 +02:00
|
|
|
manufacturer = self.find_or_create_manufacturer(gpu["vendor"])
|
|
|
|
_ = nb.dcim.inventory_items.create(
|
|
|
|
device=self.device_id,
|
|
|
|
manufacturer=manufacturer.id,
|
|
|
|
discovered=True,
|
2024-10-21 12:55:54 +02:00
|
|
|
tags=[{"name": INVENTORY_TAG["gpu"]["name"]}],
|
|
|
|
name=gpu["product"],
|
|
|
|
description=gpu["description"],
|
2020-09-18 12:29:05 +02:00
|
|
|
)
|
|
|
|
|
2024-10-21 12:55:54 +02:00
|
|
|
logging.info("Creating GPU model {}".format(gpu["product"]))
|
2020-09-18 12:29:05 +02:00
|
|
|
|
2022-02-21 18:12:16 +01:00
|
|
|
def is_external_gpu(self, gpu):
|
2024-10-21 12:55:54 +02:00
|
|
|
is_3d_gpu = gpu["description"].startswith("3D")
|
|
|
|
return (
|
|
|
|
self.server.is_blade()
|
|
|
|
and self.server.own_gpu_expansion_slot()
|
|
|
|
and is_3d_gpu
|
|
|
|
)
|
2022-02-21 18:12:16 +01:00
|
|
|
|
2020-09-18 12:29:05 +02:00
|
|
|
def do_netbox_gpus(self):
|
2022-02-21 18:12:16 +01:00
|
|
|
gpus = []
|
|
|
|
gpu_models = {}
|
2024-10-21 12:55:54 +02:00
|
|
|
for gpu in self.lshw.get_hw_linux("gpu"):
|
2022-02-21 18:12:16 +01:00
|
|
|
# Filters GPU if an expansion bay is detected:
|
|
|
|
# The internal (VGA) GPU only goes into the blade inventory,
|
|
|
|
# the external (3D) GPU goes into the expansion blade.
|
2024-10-21 12:55:54 +02:00
|
|
|
if (
|
|
|
|
config.expansion_as_device
|
|
|
|
and self.update_expansion ^ self.is_external_gpu(gpu)
|
|
|
|
):
|
2022-02-21 18:12:16 +01:00
|
|
|
continue
|
|
|
|
gpus.append(gpu)
|
|
|
|
gpu_models.setdefault(gpu["product"], 0)
|
|
|
|
gpu_models[gpu["product"]] += 1
|
|
|
|
|
2020-09-18 12:29:05 +02:00
|
|
|
nb_gpus = self.get_netbox_inventory(
|
|
|
|
device_id=self.device_id,
|
2024-10-21 12:55:54 +02:00
|
|
|
tag=INVENTORY_TAG["gpu"]["slug"],
|
2020-09-18 12:29:05 +02:00
|
|
|
)
|
2022-02-21 18:12:16 +01:00
|
|
|
nb_gpu_models = {}
|
|
|
|
for gpu in nb_gpus:
|
|
|
|
nb_gpu_models.setdefault(str(gpu), 0)
|
|
|
|
nb_gpu_models[str(gpu)] += 1
|
|
|
|
up_to_date = set(gpu_models) == set(nb_gpu_models)
|
|
|
|
if not gpus or not up_to_date:
|
2020-09-18 12:29:05 +02:00
|
|
|
for x in nb_gpus:
|
|
|
|
x.delete()
|
2022-02-21 18:12:16 +01:00
|
|
|
if gpus and not up_to_date:
|
|
|
|
self.create_netbox_gpus(gpus)
|
2020-09-18 12:29:05 +02:00
|
|
|
|
2020-04-19 12:28:49 +02:00
|
|
|
def create_or_update(self):
|
2019-09-03 13:16:37 +02:00
|
|
|
if config.inventory is None or config.update_inventory is None:
|
2019-08-26 16:54:48 +02:00
|
|
|
return False
|
Manage blade expansions as independent devices
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.
2022-02-11 18:22:13 +01:00
|
|
|
if self.update_expansion is False:
|
|
|
|
self.do_netbox_cpus()
|
|
|
|
self.do_netbox_memories()
|
|
|
|
self.do_netbox_interfaces()
|
|
|
|
self.do_netbox_motherboard()
|
2020-09-18 12:29:05 +02:00
|
|
|
self.do_netbox_gpus()
|
Manage blade expansions as independent devices
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.
2022-02-11 18:22:13 +01:00
|
|
|
self.do_netbox_disks()
|
|
|
|
self.do_netbox_raid_cards()
|
2019-08-26 16:54:48 +02:00
|
|
|
return True
|