Skip to content
Merged
7 changes: 6 additions & 1 deletion netbox_interface_sync/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ class Config(PluginConfig):
author = 'Victor Golovanenko'
author_email = 'drygdryg2014@yandex.ru'
default_settings = {
'exclude_virtual_interfaces': True
'exclude_virtual_interfaces': True,
'include_interfaces_panel': False,
# Compare description during diff
# If compare is true, description will also be synced to device
# Otherwise not.
'compare_description': True
}


Expand Down
225 changes: 225 additions & 0 deletions netbox_interface_sync/comparison.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
from dataclasses import dataclass
from django.conf import settings

config = settings.PLUGINS_CONFIG["netbox_interface_sync"]


@dataclass(frozen=True)
class ParentComparison:
"""Common fields of a device component"""

id: int
name: str
label: str
description: str

def __eq__(self, other):
# Ignore some fields when comparing; ignore component name case and whitespaces
eq = (
self.name.lower().replace(" ", "") == other.name.lower().replace(" ", "")
) and (self.label == other.label)

if config["compare_description"]:
eq = eq and (self.description == other.description)

return eq

def __hash__(self):
# Ignore some fields when hashing; ignore component name case and whitespaces
return hash(self.name.lower().replace(" ", ""))

def __str__(self):
return f"Label: {self.label}\nDescription: {self.description}"


@dataclass(frozen=True)
class ParentTypedComparison(ParentComparison):
"""Common fields of a device typed component"""

type: str
type_display: str

def __eq__(self, other):
eq = (
(self.name.lower().replace(" ", "") == other.name.lower().replace(" ", ""))
and (self.label == other.label)
and (self.type == other.type)
)

if config["compare_description"]:
eq = eq and (self.description == other.description)

return eq

def __hash__(self):
return hash((self.name.lower().replace(" ", ""), self.type))

def __str__(self):
return f"{super().__str__()}\nType: {self.type_display}"


@dataclass(frozen=True)
class InterfaceComparison(ParentTypedComparison):
"""A unified way to represent the interface and interface template"""

mgmt_only: bool
is_template: bool = False

def __eq__(self, other):
eq = (
(self.name.lower().replace(" ", "") == other.name.lower().replace(" ", ""))
and (self.label == other.label)
and (self.type == other.type)
and (self.mgmt_only == other.mgmt_only)
)

if config["compare_description"]:
eq = eq and (self.description == other.description)

return eq

def __hash__(self):
return hash((self.name.lower().replace(" ", ""), self.type))

def __str__(self):
return f"{super().__str__()}\nManagement only: {self.mgmt_only}"


@dataclass(frozen=True)
class FrontPortComparison(ParentTypedComparison):
"""A unified way to represent the front port and front port template"""

color: str
# rear_port_id: int
rear_port_position: int
is_template: bool = False

def __eq__(self, other):
eq = (
(self.name.lower().replace(" ", "") == other.name.lower().replace(" ", ""))
and (self.label == other.label)
and (self.type == other.type)
and (self.color == other.color)
and (self.rear_port_position == other.rear_port_position)
)

if config["compare_description"]:
eq = eq and (self.description == other.description)

return eq

def __hash__(self):
return hash((self.name.lower().replace(" ", ""), self.type))

def __str__(self):
return f"{super().__str__()}\nColor: {self.color}\nPosition: {self.rear_port_position}"


@dataclass(frozen=True)
class RearPortComparison(ParentTypedComparison):
"""A unified way to represent the rear port and rear port template"""

color: str
positions: int
is_template: bool = False

def __eq__(self, other):
eq = (
(self.name.lower().replace(" ", "") == other.name.lower().replace(" ", ""))
and (self.label == other.label)
and (self.type == other.type)
and (self.color == other.color)
and (self.positions == other.positions)
)

if config["compare_description"]:
eq = eq and (self.description == other.description)

return eq

def __hash__(self):
return hash((self.name.lower().replace(" ", ""), self.type))

def __str__(self):
return f"{super().__str__()}\nColor: {self.color}\nPositions: {self.positions}"


@dataclass(frozen=True, eq=False)
class ConsolePortComparison(ParentTypedComparison):
"""A unified way to represent the consoleport and consoleport template"""

is_template: bool = False


@dataclass(frozen=True, eq=False)
class ConsoleServerPortComparison(ParentTypedComparison):
"""A unified way to represent the consoleserverport and consoleserverport template"""

is_template: bool = False


@dataclass(frozen=True)
class PowerPortComparison(ParentTypedComparison):
"""A unified way to represent the power port and power port template"""

maximum_draw: str
allocated_draw: str
is_template: bool = False

def __eq__(self, other):
eq = (
(self.name.lower().replace(" ", "") == other.name.lower().replace(" ", ""))
and (self.label == other.label)
and (self.type == other.type)
and (self.maximum_draw == other.maximum_draw)
and (self.allocated_draw == other.allocated_draw)
)

if config["compare_description"]:
eq = eq and (self.description == other.description)

return eq

def __hash__(self):
return hash((self.name.lower().replace(" ", ""), self.type))

def __str__(self):
return f"{super().__str__()}\nMaximum draw: {self.maximum_draw}\nAllocated draw: {self.allocated_draw}"


@dataclass(frozen=True)
class PowerOutletComparison(ParentTypedComparison):
"""A unified way to represent the power outlet and power outlet template"""

power_port_name: str = ""
feed_leg: str = ""
is_template: bool = False

def __eq__(self, other):
eq = (
(self.name.lower().replace(" ", "") == other.name.lower().replace(" ", ""))
and (self.label == other.label)
and (self.type == other.type)
and (self.power_port_name == other.power_port_name)
and (self.feed_leg == other.feed_leg)
)

if config["compare_description"]:
eq = eq and (self.description == other.description)

return eq

def __hash__(self):
return hash(
(self.name.lower().replace(" ", ""), self.type, self.power_port_name)
)

def __str__(self):
return f"{super().__str__()}\nPower port name: {self.power_port_name}\nFeed leg: {self.feed_leg}"


@dataclass(frozen=True, eq=False)
class DeviceBayComparison(ParentComparison):
"""A unified way to represent the device bay and device bay template"""

is_template: bool = False
2 changes: 1 addition & 1 deletion netbox_interface_sync/forms.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django import forms


class InterfaceComparisonForm(forms.Form):
class ComponentComparisonForm(forms.Form):
add_to_device = forms.BooleanField(required=False)
remove_from_device = forms.BooleanField(required=False)
4 changes: 2 additions & 2 deletions netbox_interface_sync/template_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ class DeviceViewExtension(PluginTemplateExtension):
model = "dcim.device"

def buttons(self):
"""Implements a compare interfaces button at the top of the page"""
"""Implements a compare button at the top of the page"""
obj = self.context['object']
return self.render("netbox_interface_sync/compare_interfaces_button.html", extra_context={
return self.render("netbox_interface_sync/compare_components_button.html", extra_context={
"device": obj
})

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{% if perms.dcim.change_device %}
<div class="dropdown">
<button id="add-device-components" type="button" class="btn btn-sm btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
Device type sync
</button>
<ul class="dropdown-menu" aria-labeled-by="add-device-components">
{% if perms.dcim.add_consoleport %}
<li>
<a class="dropdown-item" href="{% url 'plugins:netbox_interface_sync:consoleport_comparison' device_id=device.id %}">
Console Ports
</a>
</li>
{% endif %}
{% if perms.dcim.add_consoleserverport %}
<li>
<a class="dropdown-item" href="{% url 'plugins:netbox_interface_sync:consoleserverport_comparison' device_id=device.id %}">
Console Server Ports
</a>
</li>
{% endif %}
{% if perms.dcim.add_powerport %}
<li>
<a class="dropdown-item" href="{% url 'plugins:netbox_interface_sync:powerport_comparison' device_id=device.id %}">
Power Ports
</a>
</li>
{% endif %}
{% if perms.dcim.add_poweroutlet %}
<li>
<a class="dropdown-item" href="{% url 'plugins:netbox_interface_sync:poweroutlet_comparison' device_id=device.id %}">
Power Outlets
</a>
</li>
{% endif %}
{% if perms.dcim.add_interface %}
<li>
<a class="dropdown-item" href="{% url 'plugins:netbox_interface_sync:interface_comparison' device_id=device.id %}">
Interfaces
</a>
</li>
{% endif %}
{% if perms.dcim.add_frontport %}
<li>
<a class="dropdown-item" href="{% url 'plugins:netbox_interface_sync:frontport_comparison' device_id=device.id %}">
Front Ports
</a>
</li>
{% endif %}
{% if perms.dcim.add_rearport %}
<li>
<a class="dropdown-item" href="{% url 'plugins:netbox_interface_sync:rearport_comparison' device_id=device.id %}">
Rear Ports
</a>
</li>
{% endif %}
{% if perms.dcim.add_devicebay %}
<li>
<a class="dropdown-item" href="{% url 'plugins:netbox_interface_sync:devicebay_comparison' device_id=device.id %}">
Device Bays
</a>
</li>
{% endif %}
</ul>
</div>
{% endif %}

This file was deleted.

Loading