mirror of
https://github.com/drygdryg/netbox-plugin-interface-sync
synced 2024-11-25 18:10:52 +03:00
Merge pull request #7 from rizlas/master
Sync for all components not only interfaces
This commit is contained in:
commit
de4e730514
@ -9,7 +9,12 @@ class Config(PluginConfig):
|
|||||||
author = 'Victor Golovanenko'
|
author = 'Victor Golovanenko'
|
||||||
author_email = 'drygdryg2014@yandex.ru'
|
author_email = 'drygdryg2014@yandex.ru'
|
||||||
default_settings = {
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
225
netbox_interface_sync/comparison.py
Normal file
225
netbox_interface_sync/comparison.py
Normal file
@ -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
|
@ -1,6 +1,6 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
class InterfaceComparisonForm(forms.Form):
|
class ComponentComparisonForm(forms.Form):
|
||||||
add_to_device = forms.BooleanField(required=False)
|
add_to_device = forms.BooleanField(required=False)
|
||||||
remove_from_device = forms.BooleanField(required=False)
|
remove_from_device = forms.BooleanField(required=False)
|
||||||
|
@ -6,9 +6,9 @@ class DeviceViewExtension(PluginTemplateExtension):
|
|||||||
model = "dcim.device"
|
model = "dcim.device"
|
||||||
|
|
||||||
def buttons(self):
|
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']
|
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
|
"device": obj
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -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 %}
|
@ -1,3 +0,0 @@
|
|||||||
<a href="{% url 'plugins:netbox_interface_sync:interface_comparison' device_id=device.id %}" class="btn btn-sm btn-primary">
|
|
||||||
Interface sync
|
|
||||||
</a>
|
|
@ -0,0 +1,158 @@
|
|||||||
|
{% extends 'base/layout.html' %}
|
||||||
|
|
||||||
|
{% block title %}{{ device }} - {{ component_type }} comparison{% endblock %}
|
||||||
|
{% block header %}
|
||||||
|
<nav class="breadcrumb-container px-3" aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="{% url 'dcim:device_list' %}">Devices</a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="{% url 'dcim:device_list' %}?site={{ device.site.slug }}">{{ device.site }}</a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="{% url 'dcim:device' pk=device.id %}">{{ device }}</a></li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
{{ block.super }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<style>
|
||||||
|
.caption-red {
|
||||||
|
caption-side: top;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.caption-green {
|
||||||
|
caption-side: top;
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
function toggle(event) {
|
||||||
|
event = event || window.event;
|
||||||
|
var src = event.target || event.srcElement || event;
|
||||||
|
checkboxes = document.getElementsByName(src.id);
|
||||||
|
for(var checkbox of checkboxes) checkbox.checked = src.checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
function uncheck(event) {
|
||||||
|
event = event || window.event;
|
||||||
|
var src = event.target || event.srcElement || event;
|
||||||
|
if (src.checked == false) {
|
||||||
|
document.getElementById(src.name).checked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="table-responsive-xl">
|
||||||
|
<table class="table table-hover table-bordered">
|
||||||
|
{% if templates_count == components_count %}
|
||||||
|
<caption class="caption-green">
|
||||||
|
The device and device type have the same number of {{ component_type }}.
|
||||||
|
</caption>
|
||||||
|
{% else %}
|
||||||
|
<caption class="caption-red">
|
||||||
|
The device and device type have different number of {{ component_type|lower }}.<br>
|
||||||
|
Device: {{ components_count }}<br>
|
||||||
|
Device type: {{ templates_count }}
|
||||||
|
</caption>
|
||||||
|
{% endif %}
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col" colspan="2">Device type</th>
|
||||||
|
<th scope="col">Actions</th>
|
||||||
|
<th scope="col" colspan="2">Device</th>
|
||||||
|
<th scope="col" colspan="2">Actions</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Name</th>
|
||||||
|
<th scope="col">Attributes</th>
|
||||||
|
<th scope="col">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="add_to_device" onclick="toggle(this)">
|
||||||
|
Add to the device
|
||||||
|
</label>
|
||||||
|
</th>
|
||||||
|
<th scope="col">Name</th>
|
||||||
|
<th scope="col">Attributes</th>
|
||||||
|
<th scope="col">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="remove_from_device" onclick="toggle(this)">
|
||||||
|
Remove
|
||||||
|
</label>
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="fix_name" onclick="toggle(this)">
|
||||||
|
Fix the name
|
||||||
|
</label>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for component_template, component in comparison_items %}
|
||||||
|
<tr>
|
||||||
|
{% if component_template %}
|
||||||
|
<th scope="row" {% if not component %}class="table-danger"{% endif %}>
|
||||||
|
{% if component and component_template.name != component.name %}
|
||||||
|
<span style="background-color: #eab2b2">{{ component_template.name }}</span>
|
||||||
|
{% else %}
|
||||||
|
{{ component_template.name }}
|
||||||
|
{% endif %}
|
||||||
|
</th>
|
||||||
|
<td style="white-space:pre" {% if not component %}class="table-danger"{% endif %}>{{ component_template }}</td>
|
||||||
|
<td {% if not component %}class="table-danger"{% endif %}>
|
||||||
|
{% if not component %}
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="add_to_device" value="{{ component_template.id }}" onclick="uncheck(this)">
|
||||||
|
Add to device
|
||||||
|
</label>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
{% else %}
|
||||||
|
<th scope="row"> </th>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if component %}
|
||||||
|
<th scope="row" {% if not component_template %}class="table-success"{% endif %}>
|
||||||
|
{% if component_template and component_template.name != component.name %}
|
||||||
|
<span style="background-color: #cde8c2">{{ component.name }}</span>
|
||||||
|
{% else %}
|
||||||
|
{{ component.name }}
|
||||||
|
{% endif %}
|
||||||
|
</th>
|
||||||
|
<td style="white-space:pre" {% if not component_template %}class="table-success"{% endif %}>{{ component }}</td>
|
||||||
|
<td {% if not component_template %}class="table-success"{% endif %}>
|
||||||
|
{% if not component_template %}
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="remove_from_device" value="{{ component.id }}" onclick="uncheck(this)">
|
||||||
|
Remove
|
||||||
|
</label>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td {% if not component_template %}class="table-success"{% endif %}>
|
||||||
|
{% if component_template and component_template.name != component.name %}
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="fix_name" value="{{ component.id }}" onclick="uncheck(this)">
|
||||||
|
Fix name
|
||||||
|
</label>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
{% else %}
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="submit" value="Apply" class="btn btn-primary" style="float: right;">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -1,161 +0,0 @@
|
|||||||
{% extends 'base/layout.html' %}
|
|
||||||
|
|
||||||
{% block title %}{{ device }} - Interface comparison{% endblock %}
|
|
||||||
{% block header %}
|
|
||||||
<nav class="breadcrumb-container px-3" aria-label="breadcrumb">
|
|
||||||
<ol class="breadcrumb">
|
|
||||||
<li class="breadcrumb-item"><a href="{% url 'dcim:device_list' %}">Devices</a></li>
|
|
||||||
<li class="breadcrumb-item"><a href="{% url 'dcim:device_list' %}?site={{ device.site.slug }}">{{ device.site }}</a></li>
|
|
||||||
<li class="breadcrumb-item"><a href="{% url 'dcim:device' pk=device.id %}">{{ device }}</a></li>
|
|
||||||
</ol>
|
|
||||||
</nav>
|
|
||||||
{{ block.super }}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<style>
|
|
||||||
.checkbox-group {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script>
|
|
||||||
function toggle(event) {
|
|
||||||
event = event || window.event;
|
|
||||||
var src = event.target || event.srcElement || event;
|
|
||||||
checkboxes = document.getElementsByName(src.id);
|
|
||||||
for(var checkbox of checkboxes) checkbox.checked = src.checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
function uncheck(event) {
|
|
||||||
event = event || window.event;
|
|
||||||
var src = event.target || event.srcElement || event;
|
|
||||||
if (src.checked == false) {
|
|
||||||
document.getElementById(src.name).checked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
{% if templates_count == interfaces_count %}
|
|
||||||
The device and device type have the same number of interfaces.
|
|
||||||
{% else %}
|
|
||||||
The device and device type have different number of interfaces.<br>
|
|
||||||
Device: {{ interfaces_count }}<br>
|
|
||||||
Device type: {{ templates_count }}
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form method="post">
|
|
||||||
<!-- Interface templates -->
|
|
||||||
{% csrf_token %}
|
|
||||||
<table class="table" style="width: 50%; float: left;">
|
|
||||||
<tr>
|
|
||||||
<th colspan="2">Device type</th>
|
|
||||||
<th>Actions</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Type</th>
|
|
||||||
<th>
|
|
||||||
<label class="checkbox-group">
|
|
||||||
<input type="checkbox" id="add_to_device" onclick="toggle(this)">
|
|
||||||
Add to the device
|
|
||||||
</label>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
{% for template, interface in comparison_items %}
|
|
||||||
{% if template %}
|
|
||||||
<tr {% if not interface %}class="danger"{% endif %}>
|
|
||||||
<td>
|
|
||||||
{% if interface and template.name != interface.name %}
|
|
||||||
<span style="background-color: #eab2b2">{{ template.name }}</span>
|
|
||||||
{% else %}
|
|
||||||
{{ template.name }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>{{ template.type_display }}</td>
|
|
||||||
<td>
|
|
||||||
{% if not interface %}
|
|
||||||
<label class="checkbox-group">
|
|
||||||
<input type="checkbox" name="add_to_device" value="{{ template.id }}" onclick="uncheck(this)">
|
|
||||||
Add to device
|
|
||||||
</label>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% else %}
|
|
||||||
<tr>
|
|
||||||
<td> </td>
|
|
||||||
<td> </td>
|
|
||||||
<td> </td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<table class="table" style="width: 50%; float: right;">
|
|
||||||
<!-- Interfaces -->
|
|
||||||
<tr>
|
|
||||||
<th colspan="2">Device</th>
|
|
||||||
<th colspan="2">Actions</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Type</th>
|
|
||||||
<th>
|
|
||||||
<label class="checkbox-group">
|
|
||||||
<input type="checkbox" id="remove_from_device" onclick="toggle(this)">
|
|
||||||
Remove
|
|
||||||
</label>
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
<label class="checkbox-group">
|
|
||||||
<input type="checkbox" id="fix_name" onclick="toggle(this)">
|
|
||||||
Fix the name
|
|
||||||
</label>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
{% for template, interface in comparison_items %}
|
|
||||||
{% if interface %}
|
|
||||||
<tr {% if not template %}class="success"{% endif %}>
|
|
||||||
<td>
|
|
||||||
{% if template and template.name != interface.name %}
|
|
||||||
<span style="background-color: #cde8c2">{{ interface.name }}</span>
|
|
||||||
{% else %}
|
|
||||||
{{ interface.name }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>{{ interface.type_display }}</td>
|
|
||||||
<td>
|
|
||||||
{% if not template %}
|
|
||||||
<label class="checkbox-group">
|
|
||||||
<input type="checkbox" name="remove_from_device" value="{{ interface.id }}" onclick="uncheck(this)">
|
|
||||||
Remove
|
|
||||||
</label>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% if template and template.name != interface.name %}
|
|
||||||
<label class="checkbox-group">
|
|
||||||
<input type="checkbox" name="fix_name" value="{{ interface.id }}" onclick="uncheck(this)">
|
|
||||||
Fix name
|
|
||||||
</label>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% else %}
|
|
||||||
<tr>
|
|
||||||
<td> </td>
|
|
||||||
<td> </td>
|
|
||||||
<td> </td>
|
|
||||||
<td> </td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
<div class="text-right">
|
|
||||||
<input type="submit" value="Apply" class="btn btn-primary">
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@ -1,3 +1,4 @@
|
|||||||
|
{% if config.include_interfaces_panel %}
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h5 class="card-header">Number of interfaces</h5>
|
<h5 class="card-header">Number of interfaces</h5>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@ -8,3 +9,4 @@
|
|||||||
Interfaces in the assigned device type: {{ interface_templates|length }}
|
Interfaces in the assigned device type: {{ interface_templates|length }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
@ -6,5 +6,44 @@ from . import views
|
|||||||
# Define a list of URL patterns to be imported by NetBox. Each pattern maps a URL to
|
# Define a list of URL patterns to be imported by NetBox. Each pattern maps a URL to
|
||||||
# a specific view so that it can be accessed by users.
|
# a specific view so that it can be accessed by users.
|
||||||
urlpatterns = (
|
urlpatterns = (
|
||||||
path("interface-comparison/<int:device_id>/", views.InterfaceComparisonView.as_view(), name="interface_comparison"),
|
path(
|
||||||
|
"interface-comparison/<int:device_id>/",
|
||||||
|
views.InterfaceComparisonView.as_view(),
|
||||||
|
name="interface_comparison",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"powerport-comparison/<int:device_id>/",
|
||||||
|
views.PowerPortComparisonView.as_view(),
|
||||||
|
name="powerport_comparison",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"consoleport-comparison/<int:device_id>/",
|
||||||
|
views.ConsolePortComparisonView.as_view(),
|
||||||
|
name="consoleport_comparison",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"consoleserverport-comparison/<int:device_id>/",
|
||||||
|
views.ConsoleServerPortComparisonView.as_view(),
|
||||||
|
name="consoleserverport_comparison",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"poweroutlet-comparison/<int:device_id>/",
|
||||||
|
views.PowerOutletComparisonView.as_view(),
|
||||||
|
name="poweroutlet_comparison",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"frontport-comparison/<int:device_id>/",
|
||||||
|
views.FrontPortComparisonView.as_view(),
|
||||||
|
name="frontport_comparison",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"rearport-comparison/<int:device_id>/",
|
||||||
|
views.RearPortComparisonView.as_view(),
|
||||||
|
name="rearport_comparison",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"devicebay-comparison/<int:device_id>/",
|
||||||
|
views.DeviceBayComparisonView.as_view(),
|
||||||
|
name="devicebay_comparison",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
import re
|
import re
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
from dataclasses import dataclass
|
from django.shortcuts import render, redirect
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
config = settings.PLUGINS_CONFIG['netbox_interface_sync']
|
||||||
|
|
||||||
|
|
||||||
def split(s):
|
def split(s):
|
||||||
for x, y in re.findall(r'(\d*)(\D*)', s):
|
for x, y in re.findall(r"(\d*)(\D*)", s):
|
||||||
yield '', int(x or '0')
|
yield "", int(x or "0")
|
||||||
yield y, 0
|
yield y, 0
|
||||||
|
|
||||||
|
|
||||||
@ -17,19 +22,117 @@ def human_sorted(iterable: Iterable):
|
|||||||
return sorted(iterable, key=natural_keys)
|
return sorted(iterable, key=natural_keys)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
def get_components(request, device, components, unified_components, unified_component_templates, component_type):
|
||||||
class UnifiedInterface:
|
# List of components and components templates presented in the unified format
|
||||||
"""A unified way to represent the interface and interface template"""
|
overall_powers = list(set(unified_component_templates + unified_components))
|
||||||
id: int
|
overall_powers.sort(key=lambda o: natural_keys(o.name))
|
||||||
name: str
|
|
||||||
type: str
|
|
||||||
type_display: str
|
|
||||||
is_template: bool = False
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
comparison_templates = []
|
||||||
# Ignore some fields when comparing; ignore interface name case and whitespaces
|
comparison_components = []
|
||||||
return (self.name.lower().replace(' ', '') == other.name.lower().replace(' ', '')) and (self.type == other.type)
|
for i in overall_powers:
|
||||||
|
try:
|
||||||
|
comparison_templates.append(
|
||||||
|
unified_component_templates[unified_component_templates.index(i)]
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
comparison_templates.append(None)
|
||||||
|
|
||||||
def __hash__(self):
|
try:
|
||||||
# Ignore some fields when hashing; ignore interface name case and whitespaces
|
comparison_components.append(
|
||||||
return hash((self.name.lower().replace(' ', ''), self.type))
|
unified_components[unified_components.index(i)]
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
comparison_components.append(None)
|
||||||
|
|
||||||
|
comparison_items = list(zip(comparison_templates, comparison_components))
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"netbox_interface_sync/components_comparison.html",
|
||||||
|
{
|
||||||
|
"component_type": component_type,
|
||||||
|
"comparison_items": comparison_items,
|
||||||
|
"templates_count": len(unified_component_templates),
|
||||||
|
"components_count": len(components),
|
||||||
|
"device": device,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def post_components(
|
||||||
|
request, device, components, component_templates, ObjectType, ObjectTemplateType, unified_component, unified_component_templates, component_type
|
||||||
|
):
|
||||||
|
# Manually validating components and component templates lists
|
||||||
|
add_to_device = filter(
|
||||||
|
lambda i: i in component_templates.values_list("id", flat=True),
|
||||||
|
map(int, filter(lambda x: x.isdigit(), request.POST.getlist("add_to_device"))),
|
||||||
|
)
|
||||||
|
remove_from_device = filter(
|
||||||
|
lambda i: i in components.values_list("id", flat=True),
|
||||||
|
map(
|
||||||
|
int,
|
||||||
|
filter(lambda x: x.isdigit(), request.POST.getlist("remove_from_device")),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Remove selected component from the device and count them
|
||||||
|
deleted = ObjectType.objects.filter(id__in=remove_from_device).delete()[0]
|
||||||
|
|
||||||
|
# Add selected components to the device and count them
|
||||||
|
add_to_device_component = ObjectTemplateType.objects.filter(id__in=add_to_device)
|
||||||
|
|
||||||
|
bulk_create = []
|
||||||
|
updated = 0
|
||||||
|
keys_to_avoid = ["id"]
|
||||||
|
|
||||||
|
if not config["compare_description"]:
|
||||||
|
keys_to_avoid.append("description")
|
||||||
|
|
||||||
|
for i in add_to_device_component.values():
|
||||||
|
to_create = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
tmp = components.get(name=i["name"])
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
tmp = ObjectType()
|
||||||
|
tmp.device = device
|
||||||
|
to_create = True
|
||||||
|
|
||||||
|
for k in i.keys():
|
||||||
|
if k not in keys_to_avoid:
|
||||||
|
setattr(tmp, k, i[k])
|
||||||
|
|
||||||
|
if to_create:
|
||||||
|
bulk_create.append(tmp)
|
||||||
|
else:
|
||||||
|
tmp.save()
|
||||||
|
updated += 1
|
||||||
|
|
||||||
|
created = len(ObjectType.objects.bulk_create(bulk_create))
|
||||||
|
|
||||||
|
# Rename selected components
|
||||||
|
fixed = 0
|
||||||
|
for component, component_comparison in unified_component:
|
||||||
|
try:
|
||||||
|
# Try to extract a component template with the corresponding name
|
||||||
|
corresponding_template = unified_component_templates[
|
||||||
|
unified_component_templates.index(component_comparison)
|
||||||
|
]
|
||||||
|
component.name = corresponding_template.name
|
||||||
|
component.save()
|
||||||
|
fixed += 1
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Generating result message
|
||||||
|
message = []
|
||||||
|
if created > 0:
|
||||||
|
message.append(f"created {created} {component_type}")
|
||||||
|
if updated > 0:
|
||||||
|
message.append(f"updated {updated} {component_type}")
|
||||||
|
if deleted > 0:
|
||||||
|
message.append(f"deleted {deleted} {component_type}")
|
||||||
|
if fixed > 0:
|
||||||
|
message.append(f"fixed {fixed} {component_type}")
|
||||||
|
messages.success(request, "; ".join(message).capitalize())
|
||||||
|
|
||||||
|
return redirect(request.path)
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
from django.shortcuts import get_object_or_404, render, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
from dcim.models import Device, Interface, InterfaceTemplate
|
from dcim.models import Device, Interface, InterfaceTemplate, PowerPort, PowerPortTemplate, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, DeviceBay, DeviceBayTemplate, FrontPort, FrontPortTemplate,PowerOutlet, PowerOutletTemplate, RearPort, RearPortTemplate
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
|
||||||
from .utils import UnifiedInterface, natural_keys
|
from .utils import get_components, post_components
|
||||||
from .forms import InterfaceComparisonForm
|
from .comparison import FrontPortComparison, PowerPortComparison, PowerOutletComparison, InterfaceComparison, ConsolePortComparison, ConsoleServerPortComparison, DeviceBayComparison, RearPortComparison
|
||||||
|
from .forms import ComponentComparisonForm
|
||||||
|
|
||||||
config = settings.PLUGINS_CONFIG['netbox_interface_sync']
|
config = settings.PLUGINS_CONFIG['netbox_interface_sync']
|
||||||
|
|
||||||
@ -22,40 +23,14 @@ class InterfaceComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View)
|
|||||||
interfaces = list(filter(lambda i: not i.is_virtual, interfaces))
|
interfaces = list(filter(lambda i: not i.is_virtual, interfaces))
|
||||||
interface_templates = InterfaceTemplate.objects.filter(device_type=device.device_type)
|
interface_templates = InterfaceTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
unified_interfaces = [UnifiedInterface(i.id, i.name, i.type, i.get_type_display()) for i in interfaces]
|
unified_interfaces = [InterfaceComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), i.mgmt_only) for i in interfaces]
|
||||||
unified_interface_templates = [
|
unified_interface_templates = [
|
||||||
UnifiedInterface(i.id, i.name, i.type, i.get_type_display(), is_template=True) for i in interface_templates]
|
InterfaceComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), i.mgmt_only, is_template=True) for i in interface_templates]
|
||||||
|
|
||||||
# List of interfaces and interface templates presented in the unified format
|
return get_components(request, device, interfaces, unified_interfaces, unified_interface_templates, "Interfaces")
|
||||||
overall_interfaces = list(set(unified_interface_templates + unified_interfaces))
|
|
||||||
overall_interfaces.sort(key=lambda o: natural_keys(o.name))
|
|
||||||
|
|
||||||
comparison_templates = []
|
|
||||||
comparison_interfaces = []
|
|
||||||
for i in overall_interfaces:
|
|
||||||
try:
|
|
||||||
comparison_templates.append(unified_interface_templates[unified_interface_templates.index(i)])
|
|
||||||
except ValueError:
|
|
||||||
comparison_templates.append(None)
|
|
||||||
|
|
||||||
try:
|
|
||||||
comparison_interfaces.append(unified_interfaces[unified_interfaces.index(i)])
|
|
||||||
except ValueError:
|
|
||||||
comparison_interfaces.append(None)
|
|
||||||
|
|
||||||
comparison_items = list(zip(comparison_templates, comparison_interfaces))
|
|
||||||
return render(
|
|
||||||
request, "netbox_interface_sync/interface_comparison.html",
|
|
||||||
{
|
|
||||||
"comparison_items": comparison_items,
|
|
||||||
"templates_count": len(interface_templates),
|
|
||||||
"interfaces_count": len(interfaces),
|
|
||||||
"device": device
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
def post(self, request, device_id):
|
def post(self, request, device_id):
|
||||||
form = InterfaceComparisonForm(request.POST)
|
form = ComponentComparisonForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
device = get_object_or_404(Device.objects.filter(id=device_id))
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
interfaces = device.vc_interfaces()
|
interfaces = device.vc_interfaces()
|
||||||
@ -63,52 +38,534 @@ class InterfaceComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View)
|
|||||||
interfaces = interfaces.exclude(type__in=["virtual", "lag"])
|
interfaces = interfaces.exclude(type__in=["virtual", "lag"])
|
||||||
interface_templates = InterfaceTemplate.objects.filter(device_type=device.device_type)
|
interface_templates = InterfaceTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
# Manually validating interfaces and interface templates lists
|
|
||||||
add_to_device = filter(
|
|
||||||
lambda i: i in interface_templates.values_list("id", flat=True),
|
|
||||||
map(int, filter(lambda x: x.isdigit(), request.POST.getlist("add_to_device")))
|
|
||||||
)
|
|
||||||
remove_from_device = filter(
|
|
||||||
lambda i: i in interfaces.values_list("id", flat=True),
|
|
||||||
map(int, filter(lambda x: x.isdigit(), request.POST.getlist("remove_from_device")))
|
|
||||||
)
|
|
||||||
|
|
||||||
# Remove selected interfaces from the device and count them
|
|
||||||
interfaces_deleted = Interface.objects.filter(id__in=remove_from_device).delete()[0]
|
|
||||||
|
|
||||||
# Add selected interfaces to the device and count them
|
|
||||||
add_to_device_interfaces = InterfaceTemplate.objects.filter(id__in=add_to_device)
|
|
||||||
interfaces_created = len(Interface.objects.bulk_create([
|
|
||||||
Interface(device=device, name=i.name, type=i.type) for i in add_to_device_interfaces
|
|
||||||
]))
|
|
||||||
|
|
||||||
# Getting and validating a list of interfaces to rename
|
# Getting and validating a list of interfaces to rename
|
||||||
fix_name_interfaces = filter(lambda i: str(i.id) in request.POST.getlist("fix_name"), interfaces)
|
fix_name_components = filter(
|
||||||
# Casting interface templates into UnifiedInterface objects for proper comparison with interfaces for renaming
|
lambda i: str(i.id) in request.POST.getlist("fix_name"), interfaces
|
||||||
unified_interface_templates = [
|
)
|
||||||
UnifiedInterface(i.id, i.name, i.type, i.get_type_display()) for i in interface_templates]
|
|
||||||
|
|
||||||
# Rename selected interfaces
|
unified_interface_templates = [
|
||||||
interfaces_fixed = 0
|
InterfaceComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), i.mgmt_only, is_template=True) for i in interface_templates]
|
||||||
for interface in fix_name_interfaces:
|
|
||||||
unified_interface = UnifiedInterface(interface.id, interface.name, interface.type, interface.get_type_display())
|
unified_interfaces = []
|
||||||
try:
|
|
||||||
# Try to extract an interface template with the corresponding name
|
for component in fix_name_components:
|
||||||
corresponding_template = unified_interface_templates[unified_interface_templates.index(unified_interface)]
|
unified_interfaces.append((component, InterfaceComparison(
|
||||||
interface.name = corresponding_template.name
|
component.id,
|
||||||
interface.save()
|
component.name,
|
||||||
interfaces_fixed += 1
|
component.label,
|
||||||
except ValueError:
|
component.description,
|
||||||
pass
|
component.type,
|
||||||
|
component.get_type_display(),
|
||||||
|
component.mgmt_only)))
|
||||||
|
|
||||||
|
return post_components(request, device, interfaces, interface_templates, Interface, InterfaceTemplate, unified_interfaces, unified_interface_templates, "interfaces")
|
||||||
|
|
||||||
|
class PowerPortComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||||
|
"""Comparison of power ports between a device and a device type and beautiful visualization"""
|
||||||
|
permission_required = ("dcim.view_powerport", "dcim.add_powerport", "dcim.change_powerport", "dcim.delete_powerport")
|
||||||
|
|
||||||
|
def get(self, request, device_id):
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
|
||||||
|
powerports = device.powerports.all()
|
||||||
|
powerports_templates = PowerPortTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
|
unified_powerports = [PowerPortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), i.maximum_draw, i.allocated_draw) for i in powerports]
|
||||||
|
unified_powerport_templates = [
|
||||||
|
PowerPortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), i.maximum_draw, i.allocated_draw, is_template=True) for i in powerports_templates]
|
||||||
|
|
||||||
|
return get_components(request, device, powerports, unified_powerports, unified_powerport_templates, "Power ports")
|
||||||
|
|
||||||
|
def post(self, request, device_id):
|
||||||
|
form = ComponentComparisonForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
|
||||||
|
powerports = device.powerports.all()
|
||||||
|
powerports_templates = PowerPortTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
|
# Getting and validating a list of power ports to rename
|
||||||
|
fix_name_components = filter(
|
||||||
|
lambda i: str(i.id) in request.POST.getlist("fix_name"), powerports
|
||||||
|
)
|
||||||
|
|
||||||
|
unified_powerport_templates = [
|
||||||
|
PowerPortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), i.maximum_draw, i.allocated_draw, is_template=True) for i in powerports_templates]
|
||||||
|
|
||||||
|
unified_powerports = []
|
||||||
|
|
||||||
|
for component in fix_name_components:
|
||||||
|
unified_powerports.append((component, PowerPortComparison(
|
||||||
|
component.id,
|
||||||
|
component.name,
|
||||||
|
component.label,
|
||||||
|
component.description,
|
||||||
|
component.type,
|
||||||
|
component.get_type_display(),
|
||||||
|
component.maximum_draw,
|
||||||
|
component.allocated_draw)))
|
||||||
|
|
||||||
|
return post_components(request, device, powerports, powerports_templates, PowerPort, PowerPortTemplate, unified_powerports, unified_powerport_templates, "power ports")
|
||||||
|
|
||||||
|
class ConsolePortComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||||
|
"""Comparison of console ports between a device and a device type and beautiful visualization"""
|
||||||
|
permission_required = ("dcim.view_consoleport", "dcim.add_consoleport", "dcim.change_consoleport", "dcim.delete_consoleport")
|
||||||
|
|
||||||
|
def get(self, request, device_id):
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
|
||||||
|
consoleports = device.consoleports.all()
|
||||||
|
consoleports_templates = ConsolePortTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
|
unified_consoleports = [ConsolePortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display()) for i in consoleports]
|
||||||
|
unified_consoleport_templates = [
|
||||||
|
ConsolePortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), is_template=True) for i in consoleports_templates]
|
||||||
|
|
||||||
|
return get_components(request, device, consoleports, unified_consoleports, unified_consoleport_templates, "Console ports")
|
||||||
|
|
||||||
|
def post(self, request, device_id):
|
||||||
|
form = ComponentComparisonForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
|
||||||
|
consoleports = device.consoleports.all()
|
||||||
|
consoleports_templates = ConsolePortTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
|
# Getting and validating a list of console ports to rename
|
||||||
|
fix_name_components = filter(
|
||||||
|
lambda i: str(i.id) in request.POST.getlist("fix_name"), consoleports
|
||||||
|
)
|
||||||
|
|
||||||
|
unified_consoleport_templates = [
|
||||||
|
ConsolePortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), is_template=True) for i in consoleports_templates]
|
||||||
|
|
||||||
|
unified_consoleports = []
|
||||||
|
|
||||||
|
for component in fix_name_components:
|
||||||
|
unified_consoleports.append((component, ConsolePortComparison(
|
||||||
|
component.id,
|
||||||
|
component.name,
|
||||||
|
component.label,
|
||||||
|
component.description,
|
||||||
|
component.type,
|
||||||
|
component.get_type_display())))
|
||||||
|
|
||||||
|
return post_components(request, device, consoleports, consoleports_templates, ConsolePort, ConsolePortTemplate, unified_consoleports, unified_consoleport_templates, "console ports")
|
||||||
|
|
||||||
|
class ConsoleServerPortComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||||
|
"""Comparison of console server ports between a device and a device type and beautiful visualization"""
|
||||||
|
permission_required = ("dcim.view_consoleserverport", "dcim.add_consoleserverport", "dcim.change_consoleserverport", "dcim.delete_consoleserverport")
|
||||||
|
|
||||||
|
def get(self, request, device_id):
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
|
||||||
|
consoleserverports = device.consoleserverports.all()
|
||||||
|
consoleserverports_templates = ConsoleServerPortTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
|
unified_consoleserverports = [ConsoleServerPortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display()) for i in consoleserverports]
|
||||||
|
unified_consoleserverport_templates = [
|
||||||
|
ConsoleServerPortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), is_template=True) for i in consoleserverports_templates]
|
||||||
|
|
||||||
|
return get_components(request, device, consoleserverports, unified_consoleserverports, unified_consoleserverport_templates, "Console server ports")
|
||||||
|
|
||||||
|
def post(self, request, device_id):
|
||||||
|
form = ComponentComparisonForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
|
||||||
|
consoleserverports = device.consoleserverports.all()
|
||||||
|
consoleserverports_templates = ConsoleServerPortTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
|
# Getting and validating a list of console server ports to rename
|
||||||
|
fix_name_components = filter(
|
||||||
|
lambda i: str(i.id) in request.POST.getlist("fix_name"), consoleserverports
|
||||||
|
)
|
||||||
|
|
||||||
|
unified_consoleserverport_templates = [
|
||||||
|
ConsoleServerPortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), is_template=True) for i in consoleserverports_templates]
|
||||||
|
|
||||||
|
unified_consoleserverports = []
|
||||||
|
|
||||||
|
for component in fix_name_components:
|
||||||
|
unified_consoleserverports.append((component, ConsoleServerPortComparison(
|
||||||
|
component.id,
|
||||||
|
component.name,
|
||||||
|
component.label,
|
||||||
|
component.description,
|
||||||
|
component.type,
|
||||||
|
component.get_type_display())))
|
||||||
|
|
||||||
|
return post_components(request, device, consoleserverports, consoleserverports_templates, ConsoleServerPort, ConsoleServerPortTemplate, unified_consoleserverports, unified_consoleserverport_templates, "console server ports")
|
||||||
|
|
||||||
|
class PowerOutletComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||||
|
"""Comparison of power outlets between a device and a device type and beautiful visualization"""
|
||||||
|
permission_required = ("dcim.view_poweroutlet", "dcim.add_poweroutlet", "dcim.change_poweroutlet", "dcim.delete_poweroutlet")
|
||||||
|
|
||||||
|
def get(self, request, device_id):
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
|
||||||
|
poweroutlets = device.poweroutlets.all()
|
||||||
|
poweroutlets_templates = PowerOutletTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
|
unified_poweroutlets = [PowerOutletComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), power_port_name=PowerPort.objects.get(id=i.power_port_id).name if i.power_port_id is not None else "", feed_leg=i.feed_leg) for i in poweroutlets]
|
||||||
|
unified_poweroutlet_templates = [
|
||||||
|
PowerOutletComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), power_port_name=PowerPortTemplate.objects.get(id=i.power_port_id).name if i.power_port_id is not None else "", feed_leg=i.feed_leg, is_template=True) for i in poweroutlets_templates]
|
||||||
|
|
||||||
|
return get_components(request, device, poweroutlets, unified_poweroutlets, unified_poweroutlet_templates, "Power outlets")
|
||||||
|
|
||||||
|
def post(self, request, device_id):
|
||||||
|
form = ComponentComparisonForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
|
||||||
|
poweroutlets = device.poweroutlets.all()
|
||||||
|
poweroutlets_templates = PowerOutletTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
# Generating result message
|
# Generating result message
|
||||||
message = []
|
message = []
|
||||||
if interfaces_created > 0:
|
created = 0
|
||||||
message.append(f"created {interfaces_created} interfaces")
|
updated = 0
|
||||||
if interfaces_deleted > 0:
|
fixed = 0
|
||||||
message.append(f"deleted {interfaces_deleted} interfaces")
|
|
||||||
if interfaces_fixed > 0:
|
remove_from_device = filter(
|
||||||
message.append(f"fixed {interfaces_fixed} interfaces")
|
lambda i: i in poweroutlets.values_list("id", flat=True),
|
||||||
messages.success(request, "; ".join(message).capitalize())
|
map(int, filter(lambda x: x.isdigit(), request.POST.getlist("remove_from_device")))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Remove selected power outlets from the device and count them
|
||||||
|
deleted = PowerOutlet.objects.filter(id__in=remove_from_device).delete()[0]
|
||||||
|
|
||||||
|
# Get device power ports to check dependency between power outlets
|
||||||
|
device_pp = PowerPort.objects.filter(device_id=device.id)
|
||||||
|
|
||||||
|
matching = {}
|
||||||
|
mismatch = False
|
||||||
|
for i in poweroutlets_templates:
|
||||||
|
found = False
|
||||||
|
if i.power_port_id is not None:
|
||||||
|
ppt = PowerPortTemplate.objects.get(id=i.power_port_id)
|
||||||
|
for pp in device_pp:
|
||||||
|
if pp.name == ppt.name:
|
||||||
|
# Save matching to add the correct power port later
|
||||||
|
matching[i.id] = pp.id
|
||||||
|
found = True
|
||||||
|
|
||||||
|
# If at least one power port is not found in device there is a dependency
|
||||||
|
# Better not to sync at all
|
||||||
|
if not found:
|
||||||
|
mismatch = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not mismatch:
|
||||||
|
add_to_device = filter(
|
||||||
|
lambda i: i in poweroutlets_templates.values_list("id", flat=True),
|
||||||
|
map(int, filter(lambda x: x.isdigit(), request.POST.getlist("add_to_device")))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add selected component to the device and count them
|
||||||
|
add_to_device_component = PowerOutletTemplate.objects.filter(id__in=add_to_device)
|
||||||
|
|
||||||
|
bulk_create = []
|
||||||
|
updated = 0
|
||||||
|
keys_to_avoid = ["id"]
|
||||||
|
|
||||||
|
if not config["compare_description"]:
|
||||||
|
keys_to_avoid.append("description")
|
||||||
|
|
||||||
|
for i in add_to_device_component.values():
|
||||||
|
to_create = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# If power outlets already exists, update and do not recreate
|
||||||
|
po = device.poweroutlets.get(name=i["name"])
|
||||||
|
except PowerOutlet.DoesNotExist:
|
||||||
|
po = PowerOutlet()
|
||||||
|
po.device = device
|
||||||
|
to_create = True
|
||||||
|
|
||||||
|
# Copy all fields from template
|
||||||
|
for k in i.keys():
|
||||||
|
if k not in keys_to_avoid:
|
||||||
|
setattr(po, k, i[k])
|
||||||
|
po.power_port_id = matching.get(i["id"], None)
|
||||||
|
|
||||||
|
if to_create:
|
||||||
|
bulk_create.append(po)
|
||||||
|
else:
|
||||||
|
po.save()
|
||||||
|
updated += 1
|
||||||
|
|
||||||
|
created = len(PowerOutlet.objects.bulk_create(bulk_create))
|
||||||
|
|
||||||
|
# Getting and validating a list of components to rename
|
||||||
|
fix_name_components = filter(lambda i: str(i.id) in request.POST.getlist("fix_name"), poweroutlets)
|
||||||
|
|
||||||
|
# Casting component templates into Unified objects for proper comparison with component for renaming
|
||||||
|
unified_component_templates = [
|
||||||
|
PowerOutletComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), power_port_name=PowerPortTemplate.objects.get(id=i.power_port_id).name if i.power_port_id is not None else "", feed_leg=i.feed_leg, is_template=True) for i in poweroutlets_templates]
|
||||||
|
|
||||||
|
# Rename selected power outlets
|
||||||
|
fixed = 0
|
||||||
|
for component in fix_name_components:
|
||||||
|
unified_poweroutlet = PowerOutletComparison(component.id, component.name, component.label, component.description, component.type, component.get_type_display(), power_port_name=PowerPort.objects.get(id=component.power_port_id).name if component.power_port_id is not None else "", feed_leg=component.feed_leg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try to extract a component template with the corresponding name
|
||||||
|
corresponding_template = unified_component_templates[unified_component_templates.index(unified_poweroutlet)]
|
||||||
|
component.name = corresponding_template.name
|
||||||
|
component.save()
|
||||||
|
fixed += 1
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
messages.error(request, "Dependecy detected, sync power ports first!")
|
||||||
|
|
||||||
|
if created > 0:
|
||||||
|
message.append(f"created {created} power outlets")
|
||||||
|
if updated > 0:
|
||||||
|
message.append(f"updated {updated} power outlets")
|
||||||
|
if deleted > 0:
|
||||||
|
message.append(f"deleted {deleted} power outlets")
|
||||||
|
if fixed > 0:
|
||||||
|
message.append(f"fixed {fixed} power outlets")
|
||||||
|
|
||||||
|
messages.info(request, "; ".join(message).capitalize())
|
||||||
|
|
||||||
return redirect(request.path)
|
return redirect(request.path)
|
||||||
|
|
||||||
|
class FrontPortComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||||
|
"""Comparison of front ports between a device and a device type and beautiful visualization"""
|
||||||
|
permission_required = ("dcim.view_frontport", "dcim.add_frontport", "dcim.change_frontport", "dcim.delete_frontport")
|
||||||
|
|
||||||
|
def get(self, request, device_id):
|
||||||
|
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
|
||||||
|
frontports = device.frontports.all()
|
||||||
|
frontports_templates = FrontPortTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
|
unified_frontports = [FrontPortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), i.color, i.rear_port_position) for i in frontports]
|
||||||
|
unified_frontports_templates = [
|
||||||
|
FrontPortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), i.color, i.rear_port_position, is_template=True) for i in frontports_templates]
|
||||||
|
|
||||||
|
return get_components(request, device, frontports, unified_frontports, unified_frontports_templates, "Front ports")
|
||||||
|
|
||||||
|
def post(self, request, device_id):
|
||||||
|
form = ComponentComparisonForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
|
||||||
|
frontports = device.frontports.all()
|
||||||
|
frontports_templates = FrontPortTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
|
# Generating result message
|
||||||
|
message = []
|
||||||
|
created = 0
|
||||||
|
updated = 0
|
||||||
|
fixed = 0
|
||||||
|
|
||||||
|
remove_from_device = filter(
|
||||||
|
lambda i: i in frontports.values_list("id", flat=True),
|
||||||
|
map(int, filter(lambda x: x.isdigit(), request.POST.getlist("remove_from_device")))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Remove selected front ports from the device and count them
|
||||||
|
deleted = FrontPort.objects.filter(id__in=remove_from_device).delete()[0]
|
||||||
|
|
||||||
|
# Get device rear ports to check dependency between front ports
|
||||||
|
device_rp = RearPort.objects.filter(device_id=device.id)
|
||||||
|
|
||||||
|
matching = {}
|
||||||
|
mismatch = False
|
||||||
|
for i in frontports_templates:
|
||||||
|
found = False
|
||||||
|
if i.rear_port_id is not None:
|
||||||
|
rpt = RearPortTemplate.objects.get(id=i.rear_port_id)
|
||||||
|
for rp in device_rp:
|
||||||
|
if rp.name == rpt.name:
|
||||||
|
# Save matching to add the correct rear port later
|
||||||
|
matching[i.id] = rp.id
|
||||||
|
found = True
|
||||||
|
|
||||||
|
# If at least one rear port is not found in device there is a dependency
|
||||||
|
# Better not to sync at all
|
||||||
|
if not found:
|
||||||
|
mismatch = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not mismatch:
|
||||||
|
add_to_device = filter(
|
||||||
|
lambda i: i in frontports_templates.values_list("id", flat=True),
|
||||||
|
map(int, filter(lambda x: x.isdigit(), request.POST.getlist("add_to_device")))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add selected component to the device and count them
|
||||||
|
add_to_device_component = FrontPortTemplate.objects.filter(id__in=add_to_device)
|
||||||
|
|
||||||
|
bulk_create = []
|
||||||
|
updated = 0
|
||||||
|
keys_to_avoid = ["id"]
|
||||||
|
|
||||||
|
if not config["compare_description"]:
|
||||||
|
keys_to_avoid.append("description")
|
||||||
|
|
||||||
|
for i in add_to_device_component.values():
|
||||||
|
to_create = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# If fron port already exists, update and do not recreate
|
||||||
|
fp = device.frontports.get(name=i["name"])
|
||||||
|
except FrontPort.DoesNotExist:
|
||||||
|
fp = FrontPort()
|
||||||
|
fp.device = device
|
||||||
|
to_create = True
|
||||||
|
|
||||||
|
# Copy all fields from template
|
||||||
|
for k in i.keys():
|
||||||
|
if k not in keys_to_avoid:
|
||||||
|
setattr(fp, k, i[k])
|
||||||
|
fp.rear_port_id = matching.get(i["id"], None)
|
||||||
|
|
||||||
|
if to_create:
|
||||||
|
bulk_create.append(fp)
|
||||||
|
else:
|
||||||
|
fp.save()
|
||||||
|
updated += 1
|
||||||
|
|
||||||
|
created = len(FrontPort.objects.bulk_create(bulk_create))
|
||||||
|
|
||||||
|
# Getting and validating a list of components to rename
|
||||||
|
fix_name_components = filter(lambda i: str(i.id) in request.POST.getlist("fix_name"), frontports)
|
||||||
|
|
||||||
|
# Casting component templates into Unified objects for proper comparison with component for renaming
|
||||||
|
unified_frontports_templates = [
|
||||||
|
FrontPortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), i.color, i.rear_port_position, is_template=True) for i in frontports_templates]
|
||||||
|
# Rename selected front ports
|
||||||
|
fixed = 0
|
||||||
|
for component in fix_name_components:
|
||||||
|
unified_frontport = FrontPortComparison(component.id, component.name, component.label, component.description, component.type, component.get_type_display(), component.color, component.rear_port_position)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try to extract a component template with the corresponding name
|
||||||
|
corresponding_template = unified_frontports_templates[unified_frontports_templates.index(unified_frontport)]
|
||||||
|
component.name = corresponding_template.name
|
||||||
|
component.save()
|
||||||
|
fixed += 1
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
messages.error(request, "Dependecy detected, sync rear ports first!")
|
||||||
|
|
||||||
|
if created > 0:
|
||||||
|
message.append(f"created {created} front ports")
|
||||||
|
if updated > 0:
|
||||||
|
message.append(f"updated {updated} front ports")
|
||||||
|
if deleted > 0:
|
||||||
|
message.append(f"deleted {deleted} front ports")
|
||||||
|
if fixed > 0:
|
||||||
|
message.append(f"fixed {fixed} front ports")
|
||||||
|
|
||||||
|
messages.info(request, "; ".join(message).capitalize())
|
||||||
|
|
||||||
|
return redirect(request.path)
|
||||||
|
|
||||||
|
class RearPortComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||||
|
"""Comparison of rear ports between a device and a device type and beautiful visualization"""
|
||||||
|
permission_required = ("dcim.view_rearport", "dcim.add_rearport", "dcim.change_rearport", "dcim.delete_rearport")
|
||||||
|
|
||||||
|
def get(self, request, device_id):
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
|
||||||
|
rearports = device.rearports.all()
|
||||||
|
rearports_templates = RearPortTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
|
unified_rearports = [RearPortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), i.color, i.positions) for i in rearports]
|
||||||
|
unified_rearports_templates = [
|
||||||
|
RearPortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), i.color, i.positions, is_template=True) for i in rearports_templates]
|
||||||
|
|
||||||
|
return get_components(request, device, rearports, unified_rearports, unified_rearports_templates, "Rear ports")
|
||||||
|
|
||||||
|
def post(self, request, device_id):
|
||||||
|
form = ComponentComparisonForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
|
||||||
|
rearports = device.rearports.all()
|
||||||
|
rearports_templates = RearPortTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
|
# Getting and validating a list of rear ports to rename
|
||||||
|
fix_name_components = filter(
|
||||||
|
lambda i: str(i.id) in request.POST.getlist("fix_name"), rearports
|
||||||
|
)
|
||||||
|
|
||||||
|
unified_rearports_templates = [
|
||||||
|
RearPortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), i.color, i.positions, is_template=True) for i in rearports_templates]
|
||||||
|
|
||||||
|
unified_rearports = []
|
||||||
|
|
||||||
|
for component in fix_name_components:
|
||||||
|
unified_rearports.append((component, RearPortComparison(
|
||||||
|
component.id,
|
||||||
|
component.name,
|
||||||
|
component.label,
|
||||||
|
component.description,
|
||||||
|
component.type,
|
||||||
|
component.get_type_display(),
|
||||||
|
component.color,
|
||||||
|
component.positions)))
|
||||||
|
|
||||||
|
return post_components(request, device, rearports, rearports_templates, RearPort, RearPortTemplate, unified_rearports, unified_rearports_templates, "rear ports")
|
||||||
|
|
||||||
|
|
||||||
|
form = ComponentComparisonForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
|
||||||
|
rearports = device.rearports.all()
|
||||||
|
rearports_templates = RearPortTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
|
return post_components(request, device, rearports, rearports_templates, RearPort, RearPortTemplate)
|
||||||
|
|
||||||
|
class DeviceBayComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||||
|
"""Comparison of device bays between a device and a device type and beautiful visualization"""
|
||||||
|
permission_required = ("dcim.view_devicebay", "dcim.add_devicebay", "dcim.change_devicebay", "dcim.delete_devicebay")
|
||||||
|
|
||||||
|
def get(self, request, device_id):
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
|
||||||
|
devicebays = device.devicebays.all()
|
||||||
|
devicebays_templates = DeviceBayTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
|
unified_devicebays = [DeviceBayComparison(i.id, i.name, i.label, i.description) for i in devicebays]
|
||||||
|
unified_devicebay_templates = [
|
||||||
|
DeviceBayComparison(i.id, i.name, i.label, i.description, is_template=True) for i in devicebays_templates]
|
||||||
|
|
||||||
|
return get_components(request, device, devicebays, unified_devicebays, unified_devicebay_templates, "Device bays")
|
||||||
|
|
||||||
|
def post(self, request, device_id):
|
||||||
|
form = ComponentComparisonForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
|
||||||
|
devicebays = device.devicebays.all()
|
||||||
|
devicebays_templates = DeviceBayTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
|
# Getting and validating a list of devicebays to rename
|
||||||
|
fix_name_components = filter(
|
||||||
|
lambda i: str(i.id) in request.POST.getlist("fix_name"), devicebays
|
||||||
|
)
|
||||||
|
|
||||||
|
unified_devicebay_templates = [
|
||||||
|
DeviceBayComparison(i.id, i.name, i.label, i.description, is_template=True) for i in devicebays_templates]
|
||||||
|
|
||||||
|
unified_devicebays = []
|
||||||
|
|
||||||
|
for component in fix_name_components:
|
||||||
|
unified_devicebays.append((component, DeviceBayComparison(
|
||||||
|
component.id,
|
||||||
|
component.name,
|
||||||
|
component.label,
|
||||||
|
component.description
|
||||||
|
)))
|
||||||
|
|
||||||
|
return post_components(request, device, devicebays, devicebays_templates, DeviceBay, DeviceBayTemplate, unified_devicebays, unified_devicebay_templates, "device bays")
|
||||||
|
Loading…
Reference in New Issue
Block a user