mirror of
https://github.com/drygdryg/netbox-plugin-interface-sync
synced 2024-11-25 18:10:52 +03:00
Initial commit
This commit is contained in:
commit
d0b7fb5c44
27
.gitignore
vendored
Normal file
27
.gitignore
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
41
README.md
Normal file
41
README.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# netbox-interface-sync
|
||||||
|
[Русская версия](./README_ru.md)
|
||||||
|
## Overview
|
||||||
|
This plugin allows you to compare and synchronize interfaces between devices and device types in NetBox. It can be useful for finding and correcting inconsistencies between interfaces.
|
||||||
|
Tested with NetBox versions 2.10, 2.11
|
||||||
|
## Installation
|
||||||
|
If your NetBox installation uses virtualenv, activate it like this:
|
||||||
|
```
|
||||||
|
source /opt/netbox/venv/bin/activate
|
||||||
|
```
|
||||||
|
Clone this repository, then go to the folder with it and install the plugin:
|
||||||
|
```
|
||||||
|
pip install .
|
||||||
|
```
|
||||||
|
To enable to plugin, add the plugin's name to the `PLUGINS` list in `configuration.py` (it's usually located in `/opt/netbox/netbox/netbox/`) like so:
|
||||||
|
```
|
||||||
|
PLUGINS = [
|
||||||
|
'netbox_interface_sync'
|
||||||
|
]
|
||||||
|
```
|
||||||
|
Don't forget to restart NetBox:
|
||||||
|
```
|
||||||
|
sudo systemctl restart netbox
|
||||||
|
```
|
||||||
|
## Usage
|
||||||
|
To compare the interfaces, open the page of the desired device and find the "Compare device interfaces with device type interfaces" button":
|
||||||
|
![Device page](docs/images/1_device_page.png)
|
||||||
|
Mark the required actions with the checkboxes and click "Apply".
|
||||||
|
![Interface comparison](docs/images/2_interface_comparison.png)
|
||||||
|
### Plugin settings
|
||||||
|
If you want to override the default values, configure the `PLUGINS_CONFIG` in your `configuration.py`:
|
||||||
|
```
|
||||||
|
PLUGINS_CONFIG = {
|
||||||
|
'netbox_interface_sync': {
|
||||||
|
'exclude_virtual_interfaces': True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
| Setting | Default value | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| exclude_virtual_interfaces | `True` | Exclude virtual interfaces (VLANs, LAGs) from comparison
|
40
README_ru.md
Normal file
40
README_ru.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# netbox-interface-sync
|
||||||
|
[English version](./README.md)
|
||||||
|
## Обзор
|
||||||
|
Плагин для NetBox, позволяющий сравнивать и синхронизировать интерфейсы между устройствами (devices) и типами устройств (device types). Полезен для поиска и исправления несоответствий между интерфейсами. Работа проверена с NetBox версий 2.10, 2.11
|
||||||
|
## Установка
|
||||||
|
Если NetBox использует virtualenv, то активируйте его, например, так:
|
||||||
|
```
|
||||||
|
source /opt/netbox/venv/bin/activate
|
||||||
|
```
|
||||||
|
Склонируйте этот репозиторий, затем перейдите в папку с ним и установите плагин:
|
||||||
|
```
|
||||||
|
pip install .
|
||||||
|
```
|
||||||
|
Включите плагин в файле `configuration.py` (обычно он находится в `/opt/netbox/netbox/netbox/`), добавьте его имя в список `PLUGINS`:
|
||||||
|
```
|
||||||
|
PLUGINS = [
|
||||||
|
'netbox_interface_sync'
|
||||||
|
]
|
||||||
|
```
|
||||||
|
Перезапустите NetBox:
|
||||||
|
```
|
||||||
|
sudo systemctl restart netbox
|
||||||
|
```
|
||||||
|
## Использование
|
||||||
|
Для того чтобы сравнить интерфейсы, откройте страницу нужного устройства и найдите кнопку "Compare device interfaces with device type interfaces" справа сверху:
|
||||||
|
![Device page](docs/images/1_device_page.png)
|
||||||
|
Отметьте требуемые действия напротив интерфейсов флажками и нажмите "Apply".
|
||||||
|
![Interface comparison](docs/images/2_interface_comparison.png)
|
||||||
|
### Настройки плагина
|
||||||
|
Если вы хотите переопределить значения по умолчанию, настройте переменную `PLUGINS_CONFIG` в вашем файле `configuration.py`:
|
||||||
|
```
|
||||||
|
PLUGINS_CONFIG = {
|
||||||
|
'netbox_interface_sync': {
|
||||||
|
'exclude_virtual_interfaces': True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
| Настройка | Значение по умолчанию | Описание |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| exclude_virtual_interfaces | `True` | Не учитывать виртуальные интерфейсы (VLAN, LAG) при сравнении
|
BIN
docs/images/1_device_page.png
Normal file
BIN
docs/images/1_device_page.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
BIN
docs/images/2_interface_comparison.png
Normal file
BIN
docs/images/2_interface_comparison.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 115 KiB |
16
netbox_interface_sync/__init__.py
Normal file
16
netbox_interface_sync/__init__.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
from extras.plugins import PluginConfig
|
||||||
|
|
||||||
|
|
||||||
|
class Config(PluginConfig):
|
||||||
|
name = 'netbox_interface_sync'
|
||||||
|
verbose_name = 'NetBox interface synchronization'
|
||||||
|
description = 'Syncing interfaces with the interfaces from device type for NetBox devices'
|
||||||
|
version = '0.1'
|
||||||
|
author = 'Victor Golovanenko'
|
||||||
|
author_email = 'drygdryg2014@yandex.ru'
|
||||||
|
default_settings = {
|
||||||
|
'exclude_virtual_interfaces': True
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
config = Config
|
6
netbox_interface_sync/forms.py
Normal file
6
netbox_interface_sync/forms.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceComparisonForm(forms.Form):
|
||||||
|
add_to_device = forms.BooleanField(required=False)
|
||||||
|
remove_from_device = forms.BooleanField(required=False)
|
29
netbox_interface_sync/template_content.py
Normal file
29
netbox_interface_sync/template_content.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from extras.plugins import PluginTemplateExtension
|
||||||
|
from dcim.models import Interface, InterfaceTemplate
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceViewExtension(PluginTemplateExtension):
|
||||||
|
model = "dcim.device"
|
||||||
|
|
||||||
|
def buttons(self):
|
||||||
|
"""Implements a compare interfaces button at the top of the page"""
|
||||||
|
obj = self.context['object']
|
||||||
|
return self.render("netbox_interface_sync/compare_interfaces_button.html", extra_context={
|
||||||
|
"device": obj
|
||||||
|
})
|
||||||
|
|
||||||
|
def right_page(self):
|
||||||
|
"""Implements a panel with the number of interfaces on the right side of the page"""
|
||||||
|
obj = self.context['object']
|
||||||
|
interfaces = Interface.objects.filter(device=obj)
|
||||||
|
real_interfaces = interfaces.exclude(type__in=["virtual", "lag"])
|
||||||
|
interface_templates = InterfaceTemplate.objects.filter(device_type=obj.device_type)
|
||||||
|
|
||||||
|
return self.render("netbox_interface_sync/number_of_interfaces_panel.html", extra_context={
|
||||||
|
"interfaces": interfaces,
|
||||||
|
"real_interfaces": real_interfaces,
|
||||||
|
"interface_templates": interface_templates
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
template_extensions = [DeviceViewExtension]
|
@ -0,0 +1,3 @@
|
|||||||
|
<a href="{% url 'plugins:netbox_interface_sync:interface_comparison' device_id=device.id %}" class="btn btn-primary">
|
||||||
|
Compare device interfaces with device type interfaces
|
||||||
|
</a>
|
@ -0,0 +1,153 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block header %}
|
||||||
|
<h1>{% block title %}{{ device }} - Interface comparison{% endblock %}</h1>
|
||||||
|
{% 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="background:#F5F5F5; 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 }}</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 }}</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 %}
|
@ -0,0 +1,12 @@
|
|||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>Number of interfaces</strong>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
Total interfaces: {{ interfaces|length }}<br>
|
||||||
|
{% if config.exclude_virtual_interfaces %}
|
||||||
|
Non-virtual interfaces: {{ real_interfaces|length }}<br>
|
||||||
|
{% endif %}
|
||||||
|
Interfaces in the corresponding device type: {{ interface_templates|length }}
|
||||||
|
</div>
|
||||||
|
</div>
|
10
netbox_interface_sync/urls.py
Normal file
10
netbox_interface_sync/urls.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
urlpatterns = (
|
||||||
|
path("interface-comparison/<int:device_id>/", views.InterfaceComparisonView.as_view(), name="interface_comparison"),
|
||||||
|
)
|
34
netbox_interface_sync/utils.py
Normal file
34
netbox_interface_sync/utils.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import re
|
||||||
|
from typing import Iterable
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
def split(s):
|
||||||
|
for x, y in re.findall(r'(\d*)(\D*)', s):
|
||||||
|
yield '', int(x or '0')
|
||||||
|
yield y, 0
|
||||||
|
|
||||||
|
|
||||||
|
def natural_keys(c):
|
||||||
|
return tuple(split(c))
|
||||||
|
|
||||||
|
|
||||||
|
def human_sorted(iterable: Iterable):
|
||||||
|
return sorted(iterable, key=natural_keys)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class UnifiedInterface:
|
||||||
|
"""A unified way to represent the interface and interface template"""
|
||||||
|
id: int
|
||||||
|
name: str
|
||||||
|
type: str
|
||||||
|
is_template: bool = False
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
# Ignore some fields when comparing; ignore interface name case and whitespaces
|
||||||
|
return (self.name.lower().replace(' ', '') == other.name.lower().replace(' ', '')) and (self.type == other.type)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
# Ignore some fields when hashing; ignore interface name case and whitespaces
|
||||||
|
return hash((self.name.lower().replace(' ', ''), self.type))
|
112
netbox_interface_sync/views.py
Normal file
112
netbox_interface_sync/views.py
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
from django.shortcuts import get_object_or_404, render, redirect
|
||||||
|
from django.views.generic import View
|
||||||
|
from dcim.models import Device, Interface, InterfaceTemplate
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib import messages
|
||||||
|
|
||||||
|
from .utils import UnifiedInterface, natural_keys
|
||||||
|
from .forms import InterfaceComparisonForm
|
||||||
|
|
||||||
|
config = settings.PLUGINS_CONFIG['netbox_interface_sync']
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
||||||
|
"""Comparison of interfaces between a device and a device type and beautiful visualization"""
|
||||||
|
permission_required = ("dcim.view_interface", "dcim.add_interface", "dcim.change_interface", "dcim.delete_interface")
|
||||||
|
|
||||||
|
def get(self, request, device_id):
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
interfaces = Interface.objects.filter(device=device)
|
||||||
|
if config["exclude_virtual_interfaces"]:
|
||||||
|
interfaces = list(filter(lambda i: not i.is_virtual, interfaces))
|
||||||
|
interface_templates = InterfaceTemplate.objects.filter(device_type=device.device_type)
|
||||||
|
|
||||||
|
unified_interfaces = [UnifiedInterface(i.id, i.name, i.type) for i in interfaces]
|
||||||
|
unified_interface_templates = [UnifiedInterface(i.id, i.name, i.type, is_template=True) for i in interface_templates]
|
||||||
|
|
||||||
|
# List of interfaces and interface templates presented in the unified format
|
||||||
|
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):
|
||||||
|
form = InterfaceComparisonForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
device = get_object_or_404(Device.objects.filter(id=device_id))
|
||||||
|
interfaces = Interface.objects.filter(device=device)
|
||||||
|
if config["exclude_virtual_interfaces"]:
|
||||||
|
interfaces = interfaces.exclude(type__in=["virtual", "lag"])
|
||||||
|
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")))
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
]))
|
||||||
|
|
||||||
|
# Remove selected interfaces from the device and count them
|
||||||
|
interfaces_deleted = Interface.objects.filter(id__in=remove_from_device).delete()[0]
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
# Casting interface templates into UnifiedInterface objects for proper comparison with interfaces for renaming
|
||||||
|
unified_interface_templates = [UnifiedInterface(i.id, i.name, i.type) for i in interface_templates]
|
||||||
|
|
||||||
|
# Rename selected interfaces
|
||||||
|
interfaces_fixed = 0
|
||||||
|
for interface in fix_name_interfaces:
|
||||||
|
unified_interface = UnifiedInterface(interface.id, interface.name, interface.type)
|
||||||
|
try:
|
||||||
|
# Try to extract an interface template with the corresponding name
|
||||||
|
corresponding_template = unified_interface_templates[unified_interface_templates.index(unified_interface)]
|
||||||
|
interface.name = corresponding_template.name
|
||||||
|
interface.save()
|
||||||
|
interfaces_fixed += 1
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Generating result message
|
||||||
|
message = []
|
||||||
|
if interfaces_created > 0:
|
||||||
|
message.append(f"created {interfaces_created} interfaces")
|
||||||
|
if interfaces_deleted > 0:
|
||||||
|
message.append(f"deleted {interfaces_deleted} interfaces")
|
||||||
|
if interfaces_fixed > 0:
|
||||||
|
message.append(f"fixed {interfaces_fixed} interfaces")
|
||||||
|
messages.success(request, "; ".join(message).capitalize())
|
||||||
|
|
||||||
|
return redirect(request.path)
|
13
setup.py
Normal file
13
setup.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='netbox-interface-sync',
|
||||||
|
version='0.1',
|
||||||
|
description='Syncing interfaces with the interfaces from device type for NetBox devices',
|
||||||
|
author='Victor Golovanenko',
|
||||||
|
license='GPL-3.0',
|
||||||
|
install_requires=[],
|
||||||
|
packages=["netbox_interface_sync"],
|
||||||
|
package_data={"netbox_interface_sync": ["templates/netbox_interface_sync/*.html"]},
|
||||||
|
zip_safe=False
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user