diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml
new file mode 100644
index 0000000..82f8dbd
--- /dev/null
+++ b/.github/workflows/python-publish.yml
@@ -0,0 +1,70 @@
+# This workflow will upload a Python Package to PyPI when a release is created
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
+
+# This workflow uses actions that are not certified by GitHub.
+# They are provided by a third-party and are governed by
+# separate terms of service, privacy policy, and support
+# documentation.
+
+name: Upload Python Package
+
+on:
+ release:
+ types: [published]
+
+permissions:
+ contents: read
+
+jobs:
+ release-build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.x"
+
+ - name: Build release distributions
+ run: |
+ # NOTE: put your own distribution build steps here.
+ python -m pip install build
+ python -m build
+
+ - name: Upload distributions
+ uses: actions/upload-artifact@v4
+ with:
+ name: release-dists
+ path: dist/
+
+ pypi-publish:
+ runs-on: ubuntu-latest
+ needs:
+ - release-build
+ permissions:
+ # IMPORTANT: this permission is mandatory for trusted publishing
+ id-token: write
+
+ # Dedicated environments with protections for publishing are strongly recommended.
+ # For more information, see: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#deployment-protection-rules
+ environment:
+ name: pypi
+ # OPTIONAL: uncomment and update to include your PyPI project URL in the deployment status:
+ # url: https://pypi.org/p/YOURPROJECT
+ #
+ # ALTERNATIVE: if your GitHub Release name is the PyPI project version string
+ # ALTERNATIVE: exactly, uncomment the following line instead:
+ # url: https://pypi.org/project/YOURPROJECT/${{ github.event.release.name }}
+
+ steps:
+ - name: Retrieve release distributions
+ uses: actions/download-artifact@v4
+ with:
+ name: release-dists
+ path: dist/
+
+ - name: Publish release distributions to PyPI
+ uses: pypa/gh-action-pypi-publish@release/v1
+ with:
+ packages-dir: dist/
diff --git a/.gitignore b/.gitignore
index 86feca0..c697d6b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,4 @@ share/python-wheels/
.installed.cfg
*.egg
MANIFEST
+.vscode/
diff --git a/README.md b/README.md
index 7070215..8a36157 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,7 @@ The following functionality is currently offered by this plugin:
* The ability to view/create/update/delete *Multi-Chassis Link Aggregation Group* objects through the web UI and by API.
* Button added to Device view to "Show MC Domain" if a device is associated with an MC Domain
* Button added to Interface view to "Show MC-LAG" if an interface is associated with an MC-LAG
+ * Extra Column added in the Interface Table to see the related MC-LAG Domain and its Group
## Configuration template example
@@ -85,7 +86,7 @@ exit
## Supported versions
-The current version of the `netbox-plugin-mclag` plugin has been developed for Netbox 4.2. and is not supported on older versions.
+Version 0.3 and newer of the `netbox-plugin-mclag` plugin have added compatibility for NetBox 4.3 and should have backwards compatibility with NetBox 4.2. It is not supported on older versions.
The plugin has been developed for Python 3.12.3, but I expect it to work with any Python version supported by Netbox.
@@ -139,6 +140,20 @@ PLUGINS_CONFIG = {
}
```
+The default list of 'LAG' interface types is limited. This can be extended using the FIELD_CHOICES in the configuration.py:
+See https://netbox.readthedocs.io/en/feature/configuration/data-validation/#field_choices
+
+```python
+FIELD_CHOICES = {
+ 'netbox_plugin_mclag.McLag.type+': (
+ ('value', 'Display', 'color'),
+ )
+}
+```
+Remark:
+ - Remove the + after 'netbox_plugin_mclag.McLag.type' to replace the default list, keep the + to add to the list.
+ - Make sure to have a comma after the last entry !
+
## Uninstallation
If you decide the plugin isn't for you and you want to remove it, this is how.
diff --git a/netbox_plugin_mclag/__init__.py b/netbox_plugin_mclag/__init__.py
index f21b306..d6aa11b 100644
--- a/netbox_plugin_mclag/__init__.py
+++ b/netbox_plugin_mclag/__init__.py
@@ -5,8 +5,11 @@ class NetBoxMcLagConfig(PluginConfig):
name = "netbox_plugin_mclag"
verbose_name = "Multi-Chassis LAG"
description = "Manage Multi-Chassis Link Aggregation Groups in Netbox (MC-LAG / MLAG / vPC / etc)"
- version = "0.2.0"
+ author = "Pieter Lambrecht"
+ author_email = "pieter.lambrecht@gmail.com"
+ version = "0.3.0"
base_url = "mclag"
+# based original code of pv2b: https://github.com/pv2b/netbox-plugin-mclag
config = NetBoxMcLagConfig
diff --git a/netbox_plugin_mclag/api/serializers.py b/netbox_plugin_mclag/api/serializers.py
index 7ebe898..76ba138 100644
--- a/netbox_plugin_mclag/api/serializers.py
+++ b/netbox_plugin_mclag/api/serializers.py
@@ -2,9 +2,10 @@
from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
from dcim.api.serializers import DeviceSerializer, InterfaceSerializer
-from ..models import McLag, McDomain
from dcim.models import Interface
-from ..util import get_interface_label
+
+from netbox_plugin_mclag.models import McLag, McDomain
+from netbox_plugin_mclag.util import get_interface_label
#
# Nested serializers
@@ -15,10 +16,11 @@ class NestedMcDomainSerializer(WritableNestedSerializer):
view_name='plugins-api:netbox_plugin_mclag-api:mcdomain-detail'
)
devices = DeviceSerializer(nested=True, many=True)
+ display = serializers.SerializerMethodField(read_only=True)
class Meta:
model = McDomain
- fields = ('id', 'url', 'name', 'domain_id', 'devices')
+ fields = ('id', 'url', 'name', 'display', 'domain_id', 'devices')
class NestedMcLagSerializer(WritableNestedSerializer):
@@ -26,10 +28,11 @@ class NestedMcLagSerializer(WritableNestedSerializer):
view_name='plugins-api:netbox_plugin_mclag-api:mclag-detail'
)
interfaces = InterfaceSerializer(nested=True, many=True)
+ display = serializers.SerializerMethodField(read_only=True)
class Meta:
model = McLag
- fields = ('id', 'name', 'url', 'name', 'lag_id', 'interfaces')
+ fields = ('id', 'name', 'display', 'url', 'name', 'lag_id', 'type', 'interfaces')
#
# Regular serializers
@@ -41,11 +44,12 @@ class McDomainSerializer(NetBoxModelSerializer):
)
mc_lags = NestedMcLagSerializer(many=True)
devices = DeviceSerializer(nested=True, many=True)
+ display = serializers.SerializerMethodField(read_only=True)
class Meta:
model = McDomain
fields = (
- 'id', 'url', 'name', 'domain_id', 'devices', 'description', 'mc_lags', 'tags', 'custom_fields',
+ 'id', 'url', 'name', 'display', 'domain_id', 'devices', 'description', 'mc_lags', 'tags', 'custom_fields',
'created', 'last_updated',
)
@@ -56,11 +60,12 @@ class McLagSerializer(NetBoxModelSerializer):
)
mc_domain = NestedMcDomainSerializer()
interfaces = InterfaceSerializer(nested=True, many=True)
+ display = serializers.SerializerMethodField(read_only=True)
class Meta:
model = McLag
fields = (
- 'id', 'url', 'name', 'lag_id', 'description', 'mc_domain', 'interfaces', 'tags', 'custom_fields',
+ 'id', 'url', 'name', 'display', 'lag_id', 'type', 'description', 'mc_domain', 'interfaces', 'tags', 'custom_fields',
'created', 'last_updated',
)
diff --git a/netbox_plugin_mclag/api/urls.py b/netbox_plugin_mclag/api/urls.py
index 3768eb2..6f6858f 100644
--- a/netbox_plugin_mclag/api/urls.py
+++ b/netbox_plugin_mclag/api/urls.py
@@ -1,6 +1,6 @@
from netbox.api.routers import NetBoxRouter
-from . import views
+from netbox_plugin_mclag.api import views
app_name = 'netbox_plugin_mclag'
diff --git a/netbox_plugin_mclag/api/views.py b/netbox_plugin_mclag/api/views.py
index 676ed96..09fbc39 100644
--- a/netbox_plugin_mclag/api/views.py
+++ b/netbox_plugin_mclag/api/views.py
@@ -1,17 +1,21 @@
+from dcim.models import Interface
from netbox.api.viewsets import NetBoxModelViewSet, NetBoxReadOnlyModelViewSet
-from .. import models
-from .serializers import McLagSerializer, McDomainSerializer, McInterfaceSerializer
-from ..filtersets import McInterfaceFilterSet
-from dcim.models import Interface
+from netbox_plugin_mclag import models
+from netbox_plugin_mclag.api.serializers import McLagSerializer, McDomainSerializer, McInterfaceSerializer
+from netbox_plugin_mclag.filtersets import McInterfaceFilterSet
class McDomainViewSet(NetBoxModelViewSet):
queryset = models.McDomain.objects.prefetch_related('devices', 'tags')
serializer_class = McDomainSerializer
+ ordering_fields = ['name', 'domain_id']
+ ordering = ['name']
class McLagViewSet(NetBoxModelViewSet):
queryset = models.McLag.objects.prefetch_related('mc_domain', 'tags')
serializer_class = McLagSerializer
+ ordering_fields = ['lag_id', 'name', 'type']
+ ordering = ['lag_id', 'name']
class McInterfaceViewSet(NetBoxReadOnlyModelViewSet):
# Force disabling of brief mode that is implemented in the BriefModeMixin.
@@ -26,3 +30,5 @@ def initialize_request(self, request, *args, **kwargs):
queryset = Interface.objects.filter(type='lag').prefetch_related('device')
serializer_class = McInterfaceSerializer
filterset_class = McInterfaceFilterSet
+ ordering_fields = ['name', 'device__name']
+ ordering = ['name']
diff --git a/netbox_plugin_mclag/choices.py b/netbox_plugin_mclag/choices.py
new file mode 100644
index 0000000..2cc980c
--- /dev/null
+++ b/netbox_plugin_mclag/choices.py
@@ -0,0 +1,21 @@
+from utilities.choices import ChoiceSet
+
+class LagTypeChoices(ChoiceSet):
+ key = 'McLag.type'
+
+ # a data channel part of the MC-LAG
+ LAGTYPE_CHANNEL = 'channel'
+
+ # control and management protocol channes of the MC-LAG
+ LAGTYPE_ICCP = 'iccp'
+ LAGTYPE_MCLAG = 'mclag'
+ LAGTYPE_PEERLINK = 'peerlink'
+ LAGTYPE_PEERKEEPALIVE = 'peerkeepalive'
+
+ CHOICES = [
+ (LAGTYPE_CHANNEL, 'Channel', 'green'),
+ (LAGTYPE_ICCP, 'ICCP', 'blue'),
+ (LAGTYPE_MCLAG, 'MC-LAG', 'blue'),
+ (LAGTYPE_PEERLINK, 'Peer-Link', 'blue'),
+ (LAGTYPE_PEERKEEPALIVE, 'Peer-Keepalive', 'blue'),
+ ]
\ No newline at end of file
diff --git a/netbox_plugin_mclag/filtersets.py b/netbox_plugin_mclag/filtersets.py
index 9e38067..809b59b 100644
--- a/netbox_plugin_mclag/filtersets.py
+++ b/netbox_plugin_mclag/filtersets.py
@@ -1,7 +1,8 @@
-from netbox.filtersets import NetBoxModelFilterSet
-from dcim.models import Interface
-from .models import McDomain, McLag
from django_filters import ModelMultipleChoiceFilter
+from dcim.models import Interface
+from netbox.filtersets import NetBoxModelFilterSet
+
+from netbox_plugin_mclag.models import McDomain, McLag
class McInterfaceFilterSet(NetBoxModelFilterSet):
mc_domain = ModelMultipleChoiceFilter(
diff --git a/netbox_plugin_mclag/forms.py b/netbox_plugin_mclag/forms.py
index 9cd25f0..956e74f 100644
--- a/netbox_plugin_mclag/forms.py
+++ b/netbox_plugin_mclag/forms.py
@@ -1,18 +1,40 @@
from django import forms
-from netbox.forms import NetBoxModelForm
-from utilities.forms.fields import DynamicModelMultipleChoiceField
+from dcim.models import Interface, Device
+from netbox.forms import NetBoxModelForm, NetBoxModelBulkEditForm
+from utilities.forms.fields import DynamicModelMultipleChoiceField, DynamicModelChoiceField, CommentField
+from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import APISelectMultiple
-from dcim.models import Interface
-from .models import McDomain, McLag
-from django.db.models.functions import Concat
-from .util import get_interface_label
+
+from netbox_plugin_mclag.models import McDomain, McLag
+from netbox_plugin_mclag.util import get_interface_label
class McDomainForm(NetBoxModelForm):
class Meta:
model = McDomain
fields = ('name', 'domain_id', 'description', 'devices', 'tags')
+class McDomainBulkEditForm(NetBoxModelBulkEditForm):
+ domain_id = forms.CharField(
+ required=False,
+ )
+ devices = DynamicModelChoiceField(
+ queryset=Device.objects.all(),
+ required=False,
+ )
+ description = forms.CharField(
+ required=False,
+ widget=forms.Textarea,
+ )
+
+ model = McDomain
+ nullable_fields = ('description', 'tags',)
+ fields = ('domain_id', 'devices', 'description', 'tags')
+ field_order = ('domain_id', 'devices', 'description', 'tags')
+ fieldsets = (
+ FieldSet('domain_id', 'devices', 'description', name='Multi-Chassis Domain Group'),
+ )
+
class McInterfaceMultipleChoiceField(DynamicModelMultipleChoiceField):
def label_from_instance(self, interface):
return get_interface_label(interface)
@@ -31,4 +53,30 @@ class McLagForm(NetBoxModelForm):
)
class Meta:
model = McLag
- fields = ('name', 'lag_id', 'description', 'mc_domain', 'tags', 'interfaces')
\ No newline at end of file
+ fields = ('name', 'mc_domain', 'type', 'lag_id', 'interfaces', 'description', 'tags')
+
+class McLagBulkEditForm(NetBoxModelBulkEditForm):
+ type = forms.ChoiceField(
+ choices=[('', '---------'),] + McLag._meta.get_field('type').choices,
+ required=False,
+ initial=''
+ )
+ lag_id = forms.CharField(
+ required=False,
+ )
+ mc_domain = DynamicModelChoiceField(
+ queryset=McDomain.objects.all(),
+ required=False,
+ )
+ description = forms.CharField(
+ required=False,
+ widget=forms.Textarea,
+ )
+
+ model = McLag
+ nullable_fields = ('description', 'tags',)
+ fields = ('name', 'mc_domain', 'type', 'lag_id', 'interfaces', 'description', 'tags')
+ field_order = ('mc_domain', 'type', 'lag_id', 'description', 'tags')
+ fieldsets = (
+ FieldSet('type', 'mc_domain', 'description', name='Multi-Chassis Link Aggregation Group'),
+ )
diff --git a/netbox_plugin_mclag/migrations/0003_mclag_type.py b/netbox_plugin_mclag/migrations/0003_mclag_type.py
new file mode 100644
index 0000000..d199068
--- /dev/null
+++ b/netbox_plugin_mclag/migrations/0003_mclag_type.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.2.5 on 2025-09-23 07:53
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('netbox_plugin_mclag', '0002_alter_mcdomain_options_alter_mclag_options_and_more'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='mclag',
+ name='type',
+ field=models.CharField(default='channel', max_length=20),
+ ),
+ ]
diff --git a/netbox_plugin_mclag/models.py b/netbox_plugin_mclag/models.py
index c3dd8d7..4ed20c7 100644
--- a/netbox_plugin_mclag/models.py
+++ b/netbox_plugin_mclag/models.py
@@ -1,8 +1,11 @@
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.urls import reverse
+
from netbox.models import NetBoxModel
+from netbox_plugin_mclag.choices import LagTypeChoices
+
class McDomain(NetBoxModel):
name = models.CharField(max_length=100)
domain_id = models.CharField(max_length=20, blank=True, null=True, verbose_name="Domain ID")
@@ -23,6 +26,11 @@ class McLag(NetBoxModel):
name = models.CharField(max_length=100)
lag_id = models.CharField(max_length=20, blank=True, null=True, verbose_name="Group ID")
description = models.TextField(max_length=200, blank=True, null=True)
+ type = models.CharField(
+ max_length=20,
+ choices=LagTypeChoices,
+ default=LagTypeChoices.LAGTYPE_CHANNEL
+ )
mc_domain = models.ForeignKey(
to=McDomain,
on_delete=models.CASCADE,
@@ -37,6 +45,8 @@ def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('plugins:netbox_plugin_mclag:mclag', args=[self.pk])
+ def get_type_color(self):
+ return LagTypeChoices.colors.get(self.type)
class Meta:
verbose_name="Multi-Chassis Link Aggregation Group"
verbose_name_plural="Multi-Chassis Link Aggregation Groups"
diff --git a/netbox_plugin_mclag/search.py b/netbox_plugin_mclag/search.py
new file mode 100644
index 0000000..1e267d9
--- /dev/null
+++ b/netbox_plugin_mclag/search.py
@@ -0,0 +1,20 @@
+from netbox.search import SearchIndex, register_search
+from netbox_plugin_mclag.models import McDomain, McLag
+
+@register_search
+class McDomainIndex(SearchIndex):
+ model = McDomain
+ fields = (
+ ('name', 100),
+ ('description', 500),
+ )
+ display_attrs = ('domain_id', 'description')
+
+@register_search
+class McLagIndex(SearchIndex):
+ model = McLag
+ fields = (
+ ('name', 100),
+ ('description', 500),
+ )
+ display_attrs = ('lag_id', 'mc_domain', 'description')
\ No newline at end of file
diff --git a/netbox_plugin_mclag/tables.py b/netbox_plugin_mclag/tables.py
index df83fe5..2678cda 100644
--- a/netbox_plugin_mclag/tables.py
+++ b/netbox_plugin_mclag/tables.py
@@ -1,7 +1,11 @@
+from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
-from netbox.tables import NetBoxTable, ChoiceFieldColumn
-from .models import McDomain, McLag
+from dcim.tables import DeviceInterfaceTable
+from netbox.tables import NetBoxTable, columns
+from utilities.tables import register_table_column
+
+from netbox_plugin_mclag.models import McDomain, McLag
class McDomainTable(NetBoxTable):
name = tables.Column(linkify=True)
@@ -13,7 +17,24 @@ class Meta(NetBoxTable.Meta):
class McLagTable(NetBoxTable):
name = tables.Column(linkify=True)
mc_domain = tables.Column(linkify=True)
+ type = columns.ChoiceFieldColumn()
class Meta(NetBoxTable.Meta):
model = McLag
- fields = ('pk', 'id', 'name', 'lag_id', 'description', 'mc_domain', 'actions')
- default_columns = ('name', 'mc_domain', 'lag_id')
+ fields = ('pk', 'id', 'name', 'lag_id', 'type', 'description', 'mc_domain', 'actions')
+ default_columns = ('name', 'mc_domain', 'type', 'lag_id')
+ orderable = True
+ order_by = ('mc_domain', 'name')
+
+MCLAG_LINK = """
+{% if record.mc_lags %}{% if record.mc_lags.all %}{% for mc_lag in record.mc_lags.all %}
+ {{ mc_lag.mc_domain.name }}:{{ mc_lag.name }}{% if not loop.last %}
{% endif %}
+{% endfor %}{% endif %}{% endif %}
+"""
+
+mclag_column = tables.TemplateColumn(
+ verbose_name=_('MC LAG'),
+ template_code=MCLAG_LINK,
+ attrs={'td': {'class': 'text-nowrap'}}
+)
+
+register_table_column(mclag_column, 'mclag_column', DeviceInterfaceTable)
\ No newline at end of file
diff --git a/netbox_plugin_mclag/template_content.py b/netbox_plugin_mclag/template_content.py
index 2e4c546..d50adf6 100644
--- a/netbox_plugin_mclag/template_content.py
+++ b/netbox_plugin_mclag/template_content.py
@@ -1,10 +1,13 @@
-from netbox.plugins import PluginTemplateExtension
-from .models import McDomain, McLag
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
+from netbox.plugins import PluginTemplateExtension
+
+from netbox_plugin_mclag.models import McDomain, McLag
+
class McLagInterfaceExtensions(PluginTemplateExtension):
- model = "dcim.interface"
+ model = "dcim.interface" # NB = 4.2
+ models = ("dcim.interface",) # NB >= 4.3
def buttons(self):
interface = self.context["object"]
@@ -33,7 +36,8 @@ def buttons(self):
class McLagDeviceExtensions(PluginTemplateExtension):
- model = "dcim.device"
+ model = "dcim.device" # NB = 4.2
+ models = ("dcim.device",) # NB >= 4.3
def buttons(self):
device = self.context["object"]
diff --git a/netbox_plugin_mclag/templates/netbox_plugin_mclag/mclag.html b/netbox_plugin_mclag/templates/netbox_plugin_mclag/mclag.html
index cfd0b6e..c56a31a 100644
--- a/netbox_plugin_mclag/templates/netbox_plugin_mclag/mclag.html
+++ b/netbox_plugin_mclag/templates/netbox_plugin_mclag/mclag.html
@@ -1,4 +1,5 @@
{% extends 'generic/object.html' %}
+{% load helpers %}
{% load render_table from django_tables2 %}
{% block content %}
@@ -6,26 +7,28 @@
-
-
- | Name |
- {{ object.name }} |
-
-
- | Description |
- {{ object.description | placeholder }} |
-
-
- | Multi-Chassis Domain |
-
- {{ object.mc_domain }}
- |
-
-
- | Group ID |
- {{ object.lag_id }} |
-
-
+
+
+ | Name |
+ {{ object.name }} |
+
+
+ | Multi-Chassis Domain |
+ {{ object.mc_domain }} |
+
+
+ | Type |
+ {% badge object.get_type_display bg_color=object.get_type_color %} |
+
+
+ | Group ID |
+ {{ object.lag_id }} |
+
+
+ | Description |
+ {{ object.description | placeholder }} |
+
+
diff --git a/netbox_plugin_mclag/urls.py b/netbox_plugin_mclag/urls.py
index 698cd76..2849340 100644
--- a/netbox_plugin_mclag/urls.py
+++ b/netbox_plugin_mclag/urls.py
@@ -1,28 +1,46 @@
from django.urls import path
from netbox.views.generic import ObjectChangeLogView
-from . import models, views
+from netbox_plugin_mclag.models import McDomain,McLag
+from netbox_plugin_mclag.views import (
+ McDomainView,
+ McDomainListView,
+ McDomainEditView,
+ McDomainBulkEditView,
+ McDomainDeleteView,
+ McDomainBulkDeleteView,
+ McLagView,
+ McLagListView,
+ McLagEditView,
+ McLagDeleteView,
+ McLagBulkEditView,
+ McLagBulkDeleteView,
+)
urlpatterns = (
# McDomain
- path('domains/', views.McDomainListView.as_view(), name='mcdomain_list'),
- path('domains/add/', views.McDomainEditView.as_view(), name='mcdomain_add'),
- path('domains//', views.McDomainView.as_view(), name='mcdomain'),
- path('domains//edit/', views.McDomainEditView.as_view(), name='mcdomain_edit'),
- path('domains//delete/', views.McDomainDeleteView.as_view(), name='mcdomain_delete'),
+ path('domains/', McDomainListView.as_view(), name='mcdomain_list'),
+ path('domains/add/', McDomainEditView.as_view(), name='mcdomain_add'),
+ path('domains/edit/', McDomainBulkEditView.as_view(), name='mcdomain_bulk_edit'),
+ path('domains/delete/', McDomainBulkDeleteView.as_view(), name='mcdomain_bulk_delete'),
+ path('domains//', McDomainView.as_view(), name='mcdomain'),
+ path('domains//edit/', McDomainEditView.as_view(), name='mcdomain_edit'),
+ path('domains//delete/', McDomainDeleteView.as_view(), name='mcdomain_delete'),
path('domains//changelog/', ObjectChangeLogView.as_view(), name='mcdomain_changelog', kwargs={
- 'model': models.McDomain
+ 'model': McDomain
}),
# McLag
- path('mclags/', views.McLagListView.as_view(), name='mclag_list'),
- path('mclags/add/', views.McLagEditView.as_view(), name='mclag_add'),
- path('mclags//', views.McLagView.as_view(), name='mclag'),
- path('mclags//edit/', views.McLagEditView.as_view(), name='mclag_edit'),
- path('mclags//delete/', views.McLagDeleteView.as_view(), name='mclag_delete'),
+ path('mclags/', McLagListView.as_view(), name='mclag_list'),
+ path('mclags/add/', McLagEditView.as_view(), name='mclag_add'),
+ path('mclags/edit/', McLagBulkEditView.as_view(), name='mclag_bulk_edit'),
+ path('mclags/delete/', McLagBulkDeleteView.as_view(), name='mclag_bulk_delete'),
+ path('mclags//', McLagView.as_view(), name='mclag'),
+ path('mclags//edit/', McLagEditView.as_view(), name='mclag_edit'),
+ path('mclags//delete/', McLagDeleteView.as_view(), name='mclag_delete'),
path('mclags//changelog/', ObjectChangeLogView.as_view(), name='mclag_changelog', kwargs={
- 'model': models.McLag
+ 'model': McLag
}),
)
\ No newline at end of file
diff --git a/netbox_plugin_mclag/views.py b/netbox_plugin_mclag/views.py
index 592f1a3..7e8b42e 100644
--- a/netbox_plugin_mclag/views.py
+++ b/netbox_plugin_mclag/views.py
@@ -1,12 +1,24 @@
-from netbox.views import generic
-from . import forms, models, tables
from dcim.models import Interface
from dcim.tables.devices import InterfaceTable
-from .filtersets import McDomainFilterSet, McLagFilterSet
-from .tables import McLagTable
+from netbox.views.generic import (
+ ObjectListView,
+ ObjectEditView,
+ ObjectDeleteView,
+ ObjectView,
+ ObjectChildrenView,
+ BulkEditView,
+ BulkDeleteView,
+)
+from utilities.views import register_model_view
-class McDomainView(generic.ObjectView):
- queryset = models.McDomain.objects.all()
+from netbox_plugin_mclag.models import McDomain, McLag
+from netbox_plugin_mclag.forms import McDomainForm, McDomainBulkEditForm, McLagForm, McLagBulkEditForm
+from netbox_plugin_mclag.tables import McDomainTable, McLagTable
+from netbox_plugin_mclag.filtersets import McDomainFilterSet, McLagFilterSet
+
+@register_model_view(McDomain)
+class McDomainView(ObjectView):
+ queryset = McDomain.objects.all()
def get_extra_context(self, request, instance):
mclag_table = McLagTable(instance.mc_lags.all())
mclag_table.configure(request)
@@ -14,21 +26,37 @@ def get_extra_context(self, request, instance):
return {
'mclag_table': mclag_table,
}
+@register_model_view(McDomain, name='list')
+class McDomainListView(ObjectListView):
+ queryset = McDomain.objects.all()
+ table = McDomainTable
+ filterset = McDomainFilterSet
-class McDomainListView(generic.ObjectListView):
- queryset = models.McDomain.objects.all()
- table = tables.McDomainTable
+@register_model_view(McDomain, name='edit')
+class McDomainEditView(ObjectEditView):
+ queryset = McDomain.objects.all()
+ form = McDomainForm
+
+@register_model_view(McDomain, name='bulk_edit')
+class McDomainBulkEditView(BulkEditView):
+ queryset = McDomain.objects.all()
+ table = McDomainTable
+ form = McDomainBulkEditForm
filterset = McDomainFilterSet
-class McDomainEditView(generic.ObjectEditView):
- queryset = models.McDomain.objects.all()
- form = forms.McDomainForm
+@register_model_view(McDomain, name='delete')
+class McDomainDeleteView(ObjectDeleteView):
+ queryset = McDomain.objects.all()
-class McDomainDeleteView(generic.ObjectDeleteView):
- queryset = models.McDomain.objects.all()
+@register_model_view(McDomain, name='bulk_delete')
+class McDomainBulkDeleteView(BulkDeleteView):
+ queryset = McDomain.objects.all()
+ table = McDomainTable
+ filterset = McDomainFilterSet
-class McLagView(generic.ObjectView):
- queryset = models.McLag.objects.all()
+@register_model_view(McLag)
+class McLagView(ObjectView):
+ queryset = McLag.objects.all()
def get_extra_context(self, request, instance):
lag_interfaces_table = InterfaceTable(instance.interfaces.all())
lag_interfaces_table.configure(request)
@@ -40,15 +68,31 @@ def get_extra_context(self, request, instance):
'physical_interfaces_table': physical_interfaces_table,
}
-class McLagListView(generic.ObjectListView):
- queryset = models.McLag.objects.all()
- table = tables.McLagTable
+@register_model_view(McLag, name='list')
+class McLagListView(ObjectListView):
+ queryset = McLag.objects.all()
+ table = McLagTable
filterset = McLagFilterSet
-class McLagEditView(generic.ObjectEditView):
- queryset = models.McLag.objects.all()
- form = forms.McLagForm
+@register_model_view(McLag, name='edit')
+class McLagEditView(ObjectEditView):
+ queryset = McLag.objects.all()
+ form = McLagForm
-class McLagDeleteView(generic.ObjectDeleteView):
- queryset = models.McLag.objects.all()
+@register_model_view(McLag, name='bulk_edit')
+class McLagBulkEditView(BulkEditView):
+ queryset = McLag.objects.all()
+ table = McLagTable
+ form = McLagBulkEditForm
+ filterset = McLagFilterSet
+
+@register_model_view(McLag, name='delete')
+class McLagDeleteView(ObjectDeleteView):
+ queryset = McLag.objects.all()
+
+@register_model_view(McLag, name='bulk_delete')
+class McLagBulkDeleteView(BulkDeleteView):
+ queryset = McLag.objects.all()
+ table = McLagTable
+ filterset = McLagFilterSet
diff --git a/setup.py b/setup.py
index cc1ba9e..386c65e 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@
setup(
name='netbox-plugin-mclag',
- version='0.2.1',
+ version='0.3.0',
description='Manage Multi-Chassis Link Aggregation Groups in Netbox (MC-LAG / MLAG / vPC / etc)',
install_requires=[],
packages=find_packages(),