diff --git a/netbox/dcim/models/__init__.py b/netbox/dcim/models/__init__.py index 096065cab..1716208fd 100644 --- a/netbox/dcim/models/__init__.py +++ b/netbox/dcim/models/__init__.py @@ -1722,7 +1722,7 @@ class VirtualChassis(ChangeLoggedModel): return str(self.master) if hasattr(self, 'master') else 'New Virtual Chassis' def get_absolute_url(self): - return self.master.get_absolute_url() + return reverse('dcim:virtualchassis', kwargs={'pk': self.pk}) def clean(self): diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index 7131a6be3..0e5e9dc7a 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -165,15 +165,6 @@ UTILIZATION_GRAPH = """ {% utilization_graph value %} """ -VIRTUALCHASSIS_ACTIONS = """ - - - -{% if perms.dcim.change_virtualchassis %} - -{% endif %} -""" - CABLE_TERMINATION_PARENT = """ {% if value.device %} {{ value.device }} @@ -1050,17 +1041,17 @@ class InventoryItemTable(BaseTable): class VirtualChassisTable(BaseTable): pk = ToggleColumn() - master = tables.LinkColumn() - member_count = tables.Column(verbose_name='Members') - actions = tables.TemplateColumn( - template_code=VIRTUALCHASSIS_ACTIONS, - attrs={'td': {'class': 'text-right noprint'}}, - verbose_name='' + name = tables.Column( + accessor=Accessor('master__name'), + linkify=True + ) + member_count = tables.Column( + verbose_name='Members' ) class Meta(BaseTable.Meta): model = VirtualChassis - fields = ('pk', 'master', 'domain', 'member_count', 'actions') + fields = ('pk', 'name', 'domain', 'member_count') # diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index 36a272cf8..cbaea4b76 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -321,6 +321,7 @@ urlpatterns = [ # Virtual chassis path('virtual-chassis/', views.VirtualChassisListView.as_view(), name='virtualchassis_list'), path('virtual-chassis/add/', views.VirtualChassisCreateView.as_view(), name='virtualchassis_add'), + path('virtual-chassis//', views.VirtualChassisView.as_view(), name='virtualchassis'), path('virtual-chassis//edit/', views.VirtualChassisEditView.as_view(), name='virtualchassis_edit'), path('virtual-chassis//delete/', views.VirtualChassisDeleteView.as_view(), name='virtualchassis_delete'), path('virtual-chassis//changelog/', ObjectChangeLogView.as_view(), name='virtualchassis_changelog', kwargs={'model': VirtualChassis}), diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 9ca4c2edc..78b54789d 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -32,7 +32,6 @@ from virtualization.models import VirtualMachine from . import filters, forms, tables from .choices import DeviceFaceChoices from .constants import NONCONNECTABLE_IFACE_TYPES -from .exceptions import CableTraceSplit from .models import ( Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay, DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate, @@ -2368,6 +2367,17 @@ class VirtualChassisListView(PermissionRequiredMixin, ObjectListView): action_buttons = ('export',) +class VirtualChassisView(PermissionRequiredMixin, View): + permission_required = 'dcim.view_virtualchassis' + + def get(self, request, pk): + virtualchassis = get_object_or_404(VirtualChassis.objects.prefetch_related('members'), pk=pk) + + return render(request, 'dcim/virtualchassis.html', { + 'virtualchassis': virtualchassis, + }) + + class VirtualChassisCreateView(PermissionRequiredMixin, View): permission_required = 'dcim.add_virtualchassis' diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 250774022..ef1a301e2 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -232,19 +232,9 @@ {% endfor %} {% endif %} diff --git a/netbox/templates/dcim/virtualchassis.html b/netbox/templates/dcim/virtualchassis.html new file mode 100644 index 000000000..a97c42e4f --- /dev/null +++ b/netbox/templates/dcim/virtualchassis.html @@ -0,0 +1,111 @@ +{% extends 'base.html' %} +{% load buttons %} +{% load custom_links %} +{% load helpers %} +{% load plugins %} + +{% block header %} +
+
+ +
+
+
+
+ + + + +
+
+
+
+
+ {% plugin_buttons virtualchassis %} + {% if perms.dcim.change_virtualchassis %} + {% edit_button virtualchassis %} + {% endif %} + {% if perms.dcim.delete_virtualchassis %} + {% delete_button virtualchassis %} + {% endif %} +
+

{% block title %}{{ virtualchassis }}{% endblock %}

+ {% include 'inc/created_updated.html' with obj=virtualchassis %} +
+ {% custom_links virtualchassis %} +
+ +{% endblock %} + +{% block content %} +
+
+
+
+ Virtual Chassis +
+ + + + + +
Domain{{ virtualchassis.domain|placeholder }}
+
+ {% include 'extras/inc/tags_panel.html' with tags=virtualchassis.tags.all url='dcim:virtualchassis_list' %} + {% plugin_left_page virtualchassis %} +
+
+
+
+ Members +
+ + + + + + + + {% for vc_member in virtualchassis.members.all %} + + + + + + + {% endfor %} +
DevicePositionMasterPriority
+ {{ vc_member }} + {{ vc_member.vc_position }}{% if virtualchassis.master == vc_member %}{% endif %}{{ vc_member.vc_priority|placeholder }}
+ {% if perms.dcim.change_virtualchassis %} + + {% endif %} +
+ {% plugin_right_page virtualchassis %} +
+
+
+
+ {% plugin_full_width_page virtualchassis %} +
+
+{% endblock %}