Skip to content

Use permanent MAC address as serial number on inventory network interfaces #361

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions netbox_agent/ethtool.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,18 @@ def _parse_ethtool_module_output(self):
return {"form_factor": r.groups()[0]}
return {}

def _parse_ethtool_permanent_address_output(self):
status, output = subprocess.getstatusoutput("ethtool -P {}".format(self.interface))
if status == 0:
prefix = "Permanent address: "
if output.startswith(prefix):
return {"mac": output.removeprefix(prefix).strip()}
return {}

def parse(self):
if which("ethtool") is None:
return None
output = self._parse_ethtool_output()
output.update(self._parse_ethtool_module_output())
output.update(self._parse_ethtool_permanent_address_output())
return output
68 changes: 64 additions & 4 deletions netbox_agent/inventory.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from netbox_agent.config import config
from netbox_agent.config import netbox_instance as nb
from netbox_agent.ethtool import Ethtool
from netbox_agent.lshw import LSHW
from netbox_agent.misc import get_vendor, is_tool
from netbox_agent.raid.hp import HPRaid
Expand Down Expand Up @@ -157,7 +158,12 @@ def do_netbox_motherboard(self):
description="{}".format(motherboard.get("description")),
)

def create_netbox_interface(self, iface):
def create_netbox_interface(self, iface, component_type=None, component_id=None):
component_args = {}
if component_type and component_id:
component_args["component_type"] = component_type
component_args["component_id"] = component_id

manufacturer = self.find_or_create_manufacturer(iface["vendor"])
_ = nb.dcim.inventory_items.create(
device=self.device_id,
Expand All @@ -166,7 +172,8 @@ def create_netbox_interface(self, iface):
tags=[{"name": INVENTORY_TAG["interface"]["name"]}],
name="{}".format(iface["product"]),
serial="{}".format(iface["serial"]),
description="{} {}".format(iface["description"], iface["name"]),
description="{}".format(iface["description"]),
**component_args,
)

def do_netbox_interfaces(self):
Expand All @@ -175,6 +182,13 @@ def do_netbox_interfaces(self):
)
interfaces = self.lshw.interfaces

for iface in interfaces:
ethtool = Ethtool(iface["name"]).parse()

# Override interface serial number from lshw with permanent MAC address from ethtool, if available.
if ethtool and "mac" in ethtool and ethtool.get("mac") != "00:00:00:00:00:00":
iface["serial"] = ethtool["mac"]

# delete interfaces that are in netbox but not locally
# use the serial_number has the comparison element
for nb_interface in nb_interfaces:
Expand All @@ -186,10 +200,56 @@ def do_netbox_interfaces(self):
)
nb_interface.delete()

# create interfaces that are not in netbox
for iface in interfaces:
# Override interface description from lshw.
iface["description"] = "{} {}".format(iface["description"], iface["name"])

# Try to find the network interface component id.
component_type = None
component_id = None
nb_interface_component = nb.dcim.interfaces.get(name=iface["name"], device_id=self.device_id)
if hasattr(nb_interface_component, "id"):
component_type = "dcim.interface"
component_id = nb_interface_component.id

if iface.get("serial") not in [x.serial for x in nb_interfaces]:
self.create_netbox_interface(iface)
# create interfaces that are not in netbox
self.create_netbox_interface(iface, component_type, component_id)
else:
# update interfaces that are in netbox if necessary
update = False
nb_interface = [x for x in nb_interfaces if x.serial == iface.get("serial")][0]

if nb_interface.description != iface["description"]:
logging.info(
"Updating interface {} description from {} to {}".format(
nb_interface.serial,
nb_interface.description,
iface["description"],
)
)
nb_interface.description = iface["description"]
update = True

if (
nb_interface.component_type != component_type
or nb_interface.component_id != component_id
):
logging.info(
"Updating interface {} component_type/component_id from {}/{} to {}/{}".format(
nb_interface.serial,
nb_interface.component_type,
nb_interface.component_id,
component_type,
component_id,
)
)
nb_interface.component_type = component_type
nb_interface.component_id = component_id
update = True

if update:
nb_interface.save()

def create_netbox_cpus(self):
for cpu in self.lshw.get_hw_linux("cpu"):
Expand Down
Loading