mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 20:12:00 -06:00
Merge pull request #6062 from netbox-community/5971-org-object-views
Closes #5971: Dedicated views for organizational models
This commit is contained in:
commit
b793ee3aff
@ -175,7 +175,7 @@ class CircuitType(OrganizationalModel):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?type={}".format(reverse('circuits:circuit_list'), self.slug)
|
||||
return reverse('circuits:circuittype', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
|
@ -38,6 +38,7 @@ urlpatterns = [
|
||||
path('circuit-types/import/', views.CircuitTypeBulkImportView.as_view(), name='circuittype_import'),
|
||||
path('circuit-types/edit/', views.CircuitTypeBulkEditView.as_view(), name='circuittype_bulk_edit'),
|
||||
path('circuit-types/delete/', views.CircuitTypeBulkDeleteView.as_view(), name='circuittype_bulk_delete'),
|
||||
path('circuit-types/<int:pk>/', views.CircuitTypeView.as_view(), name='circuittype'),
|
||||
path('circuit-types/<int:pk>/edit/', views.CircuitTypeEditView.as_view(), name='circuittype_edit'),
|
||||
path('circuit-types/<int:pk>/delete/', views.CircuitTypeDeleteView.as_view(), name='circuittype_delete'),
|
||||
path('circuit-types/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='circuittype_changelog', kwargs={'model': CircuitType}),
|
||||
|
@ -147,6 +147,23 @@ class CircuitTypeListView(generic.ObjectListView):
|
||||
table = tables.CircuitTypeTable
|
||||
|
||||
|
||||
class CircuitTypeView(generic.ObjectView):
|
||||
queryset = CircuitType.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
circuits = Circuit.objects.restrict(request.user, 'view').filter(
|
||||
type=instance
|
||||
)
|
||||
|
||||
circuits_table = tables.CircuitTable(circuits)
|
||||
circuits_table.columns.hide('type')
|
||||
paginate_table(circuits_table, request)
|
||||
|
||||
return {
|
||||
'circuits_table': circuits_table,
|
||||
}
|
||||
|
||||
|
||||
class CircuitTypeEditView(generic.ObjectEditView):
|
||||
queryset = CircuitType.objects.all()
|
||||
model_form = forms.CircuitTypeForm
|
||||
|
@ -65,7 +65,7 @@ class Manufacturer(OrganizationalModel):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?manufacturer={}".format(reverse('dcim:devicetype_list'), self.slug)
|
||||
return reverse('dcim:manufacturer', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
@ -375,6 +375,9 @@ class DeviceRole(OrganizationalModel):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('dcim:devicerole', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.name,
|
||||
@ -436,7 +439,7 @@ class Platform(OrganizationalModel):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?platform={}".format(reverse('dcim:device_list'), self.slug)
|
||||
return reverse('dcim:platform', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
|
@ -67,7 +67,7 @@ class RackRole(OrganizationalModel):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?role={}".format(reverse('dcim:rack_list'), self.slug)
|
||||
return reverse('dcim:rackrole', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
|
@ -56,7 +56,7 @@ class Region(NestedGroupModel):
|
||||
csv_headers = ['name', 'slug', 'parent', 'description']
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?region={}".format(reverse('dcim:site_list'), self.slug)
|
||||
return reverse('dcim:region', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
@ -108,7 +108,7 @@ class SiteGroup(NestedGroupModel):
|
||||
csv_headers = ['name', 'slug', 'parent', 'description']
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?group={}".format(reverse('dcim:site_list'), self.slug)
|
||||
return reverse('dcim:sitegroup', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
@ -324,7 +324,7 @@ class Location(NestedGroupModel):
|
||||
]
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?location_id={}".format(reverse('dcim:rack_list'), self.pk)
|
||||
return reverse('dcim:location', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
|
@ -50,6 +50,9 @@ __all__ = (
|
||||
|
||||
class DeviceRoleTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = tables.Column(
|
||||
linkify=True
|
||||
)
|
||||
device_count = LinkedCountColumn(
|
||||
viewname='dcim:device_list',
|
||||
url_params={'role': 'slug'},
|
||||
@ -76,6 +79,9 @@ class DeviceRoleTable(BaseTable):
|
||||
|
||||
class PlatformTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = tables.Column(
|
||||
linkify=True
|
||||
)
|
||||
device_count = LinkedCountColumn(
|
||||
viewname='dcim:device_list',
|
||||
url_params={'platform': 'slug'},
|
||||
|
@ -19,12 +19,14 @@ __all__ = (
|
||||
|
||||
|
||||
#
|
||||
# Rack groups
|
||||
# Locations
|
||||
#
|
||||
|
||||
class LocationTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = MPTTColumn()
|
||||
name = MPTTColumn(
|
||||
linkify=True
|
||||
)
|
||||
site = tables.Column(
|
||||
linkify=True
|
||||
)
|
||||
|
@ -17,7 +17,9 @@ __all__ = (
|
||||
|
||||
class RegionTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = MPTTColumn()
|
||||
name = MPTTColumn(
|
||||
linkify=True
|
||||
)
|
||||
site_count = tables.Column(
|
||||
verbose_name='Sites'
|
||||
)
|
||||
@ -35,7 +37,9 @@ class RegionTable(BaseTable):
|
||||
|
||||
class SiteGroupTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = MPTTColumn()
|
||||
name = MPTTColumn(
|
||||
linkify=True
|
||||
)
|
||||
site_count = tables.Column(
|
||||
verbose_name='Sites'
|
||||
)
|
||||
|
@ -14,6 +14,7 @@ urlpatterns = [
|
||||
path('regions/import/', views.RegionBulkImportView.as_view(), name='region_import'),
|
||||
path('regions/edit/', views.RegionBulkEditView.as_view(), name='region_bulk_edit'),
|
||||
path('regions/delete/', views.RegionBulkDeleteView.as_view(), name='region_bulk_delete'),
|
||||
path('regions/<int:pk>/', views.RegionView.as_view(), name='region'),
|
||||
path('regions/<int:pk>/edit/', views.RegionEditView.as_view(), name='region_edit'),
|
||||
path('regions/<int:pk>/delete/', views.RegionDeleteView.as_view(), name='region_delete'),
|
||||
path('regions/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='region_changelog', kwargs={'model': Region}),
|
||||
@ -24,6 +25,7 @@ urlpatterns = [
|
||||
path('site-groups/import/', views.SiteGroupBulkImportView.as_view(), name='sitegroup_import'),
|
||||
path('site-groups/edit/', views.SiteGroupBulkEditView.as_view(), name='sitegroup_bulk_edit'),
|
||||
path('site-groups/delete/', views.SiteGroupBulkDeleteView.as_view(), name='sitegroup_bulk_delete'),
|
||||
path('site-groups/<int:pk>/', views.SiteGroupView.as_view(), name='sitegroup'),
|
||||
path('site-groups/<int:pk>/edit/', views.SiteGroupEditView.as_view(), name='sitegroup_edit'),
|
||||
path('site-groups/<int:pk>/delete/', views.SiteGroupDeleteView.as_view(), name='sitegroup_delete'),
|
||||
path('site-groups/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='sitegroup_changelog', kwargs={'model': SiteGroup}),
|
||||
@ -47,6 +49,7 @@ urlpatterns = [
|
||||
path('locations/import/', views.LocationBulkImportView.as_view(), name='location_import'),
|
||||
path('locations/edit/', views.LocationBulkEditView.as_view(), name='location_bulk_edit'),
|
||||
path('locations/delete/', views.LocationBulkDeleteView.as_view(), name='location_bulk_delete'),
|
||||
path('locations/<int:pk>/', views.LocationView.as_view(), name='location'),
|
||||
path('locations/<int:pk>/edit/', views.LocationEditView.as_view(), name='location_edit'),
|
||||
path('locations/<int:pk>/delete/', views.LocationDeleteView.as_view(), name='location_delete'),
|
||||
path('locations/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='location_changelog', kwargs={'model': Location}),
|
||||
@ -57,6 +60,7 @@ urlpatterns = [
|
||||
path('rack-roles/import/', views.RackRoleBulkImportView.as_view(), name='rackrole_import'),
|
||||
path('rack-roles/edit/', views.RackRoleBulkEditView.as_view(), name='rackrole_bulk_edit'),
|
||||
path('rack-roles/delete/', views.RackRoleBulkDeleteView.as_view(), name='rackrole_bulk_delete'),
|
||||
path('rack-roles/<int:pk>/', views.RackRoleView.as_view(), name='rackrole'),
|
||||
path('rack-roles/<int:pk>/edit/', views.RackRoleEditView.as_view(), name='rackrole_edit'),
|
||||
path('rack-roles/<int:pk>/delete/', views.RackRoleDeleteView.as_view(), name='rackrole_delete'),
|
||||
path('rack-roles/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rackrole_changelog', kwargs={'model': RackRole}),
|
||||
@ -93,6 +97,7 @@ urlpatterns = [
|
||||
path('manufacturers/import/', views.ManufacturerBulkImportView.as_view(), name='manufacturer_import'),
|
||||
path('manufacturers/edit/', views.ManufacturerBulkEditView.as_view(), name='manufacturer_bulk_edit'),
|
||||
path('manufacturers/delete/', views.ManufacturerBulkDeleteView.as_view(), name='manufacturer_bulk_delete'),
|
||||
path('manufacturers/<int:pk>/', views.ManufacturerView.as_view(), name='manufacturer'),
|
||||
path('manufacturers/<int:pk>/edit/', views.ManufacturerEditView.as_view(), name='manufacturer_edit'),
|
||||
path('manufacturers/<int:pk>/delete/', views.ManufacturerDeleteView.as_view(), name='manufacturer_delete'),
|
||||
path('manufacturers/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='manufacturer_changelog', kwargs={'model': Manufacturer}),
|
||||
@ -179,6 +184,7 @@ urlpatterns = [
|
||||
path('device-roles/import/', views.DeviceRoleBulkImportView.as_view(), name='devicerole_import'),
|
||||
path('device-roles/edit/', views.DeviceRoleBulkEditView.as_view(), name='devicerole_bulk_edit'),
|
||||
path('device-roles/delete/', views.DeviceRoleBulkDeleteView.as_view(), name='devicerole_bulk_delete'),
|
||||
path('device-roles/<int:pk>/', views.DeviceRoleView.as_view(), name='devicerole'),
|
||||
path('device-roles/<int:pk>/edit/', views.DeviceRoleEditView.as_view(), name='devicerole_edit'),
|
||||
path('device-roles/<int:pk>/delete/', views.DeviceRoleDeleteView.as_view(), name='devicerole_delete'),
|
||||
path('device-roles/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='devicerole_changelog', kwargs={'model': DeviceRole}),
|
||||
@ -189,6 +195,7 @@ urlpatterns = [
|
||||
path('platforms/import/', views.PlatformBulkImportView.as_view(), name='platform_import'),
|
||||
path('platforms/edit/', views.PlatformBulkEditView.as_view(), name='platform_bulk_edit'),
|
||||
path('platforms/delete/', views.PlatformBulkDeleteView.as_view(), name='platform_bulk_delete'),
|
||||
path('platforms/<int:pk>/', views.PlatformView.as_view(), name='platform'),
|
||||
path('platforms/<int:pk>/edit/', views.PlatformEditView.as_view(), name='platform_edit'),
|
||||
path('platforms/<int:pk>/delete/', views.PlatformDeleteView.as_view(), name='platform_delete'),
|
||||
path('platforms/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='platform_changelog', kwargs={'model': Platform}),
|
||||
|
@ -20,6 +20,7 @@ from secrets.models import Secret
|
||||
from utilities.forms import ConfirmationForm
|
||||
from utilities.paginator import EnhancedPaginator, get_paginate_count
|
||||
from utilities.permissions import get_permission_for_model
|
||||
from utilities.tables import paginate_table
|
||||
from utilities.utils import csv_format, count_related
|
||||
from utilities.views import GetReturnURLMixin, ObjectPermissionRequiredMixin
|
||||
from virtualization.models import VirtualMachine
|
||||
@ -111,6 +112,23 @@ class RegionListView(generic.ObjectListView):
|
||||
table = tables.RegionTable
|
||||
|
||||
|
||||
class RegionView(generic.ObjectView):
|
||||
queryset = Region.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
sites = Site.objects.restrict(request.user, 'view').filter(
|
||||
region=instance
|
||||
)
|
||||
|
||||
sites_table = tables.SiteTable(sites)
|
||||
sites_table.columns.hide('region')
|
||||
paginate_table(sites_table, request)
|
||||
|
||||
return {
|
||||
'sites_table': sites_table,
|
||||
}
|
||||
|
||||
|
||||
class RegionEditView(generic.ObjectEditView):
|
||||
queryset = Region.objects.all()
|
||||
model_form = forms.RegionForm
|
||||
@ -168,6 +186,23 @@ class SiteGroupListView(generic.ObjectListView):
|
||||
table = tables.SiteGroupTable
|
||||
|
||||
|
||||
class SiteGroupView(generic.ObjectView):
|
||||
queryset = SiteGroup.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
sites = Site.objects.restrict(request.user, 'view').filter(
|
||||
group=instance
|
||||
)
|
||||
|
||||
sites_table = tables.SiteTable(sites)
|
||||
sites_table.columns.hide('group')
|
||||
paginate_table(sites_table, request)
|
||||
|
||||
return {
|
||||
'sites_table': sites_table,
|
||||
}
|
||||
|
||||
|
||||
class SiteGroupEditView(generic.ObjectEditView):
|
||||
queryset = SiteGroup.objects.all()
|
||||
model_form = forms.SiteGroupForm
|
||||
@ -290,6 +325,23 @@ class LocationListView(generic.ObjectListView):
|
||||
table = tables.LocationTable
|
||||
|
||||
|
||||
class LocationView(generic.ObjectView):
|
||||
queryset = Location.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
devices = Device.objects.restrict(request.user, 'view').filter(
|
||||
location=instance
|
||||
)
|
||||
|
||||
devices_table = tables.DeviceTable(devices)
|
||||
devices_table.columns.hide('location')
|
||||
paginate_table(devices_table, request)
|
||||
|
||||
return {
|
||||
'devices_table': devices_table,
|
||||
}
|
||||
|
||||
|
||||
class LocationEditView(generic.ObjectEditView):
|
||||
queryset = Location.objects.all()
|
||||
model_form = forms.LocationForm
|
||||
@ -341,6 +393,23 @@ class RackRoleListView(generic.ObjectListView):
|
||||
table = tables.RackRoleTable
|
||||
|
||||
|
||||
class RackRoleView(generic.ObjectView):
|
||||
queryset = RackRole.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
racks = Rack.objects.restrict(request.user, 'view').filter(
|
||||
role=instance
|
||||
)
|
||||
|
||||
racks_table = tables.RackTable(racks)
|
||||
racks_table.columns.hide('role')
|
||||
paginate_table(racks_table, request)
|
||||
|
||||
return {
|
||||
'racks_table': racks_table,
|
||||
}
|
||||
|
||||
|
||||
class RackRoleEditView(generic.ObjectEditView):
|
||||
queryset = RackRole.objects.all()
|
||||
model_form = forms.RackRoleForm
|
||||
@ -567,6 +636,23 @@ class ManufacturerListView(generic.ObjectListView):
|
||||
table = tables.ManufacturerTable
|
||||
|
||||
|
||||
class ManufacturerView(generic.ObjectView):
|
||||
queryset = Manufacturer.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
devicetypes = DeviceType.objects.restrict(request.user, 'view').filter(
|
||||
manufacturer=instance
|
||||
)
|
||||
|
||||
devicetypes_table = tables.DeviceTypeTable(devicetypes)
|
||||
devicetypes_table.columns.hide('manufacturer')
|
||||
paginate_table(devicetypes_table, request)
|
||||
|
||||
return {
|
||||
'devicetypes_table': devicetypes_table,
|
||||
}
|
||||
|
||||
|
||||
class ManufacturerEditView(generic.ObjectEditView):
|
||||
queryset = Manufacturer.objects.all()
|
||||
model_form = forms.ManufacturerForm
|
||||
@ -1017,6 +1103,23 @@ class DeviceRoleListView(generic.ObjectListView):
|
||||
table = tables.DeviceRoleTable
|
||||
|
||||
|
||||
class DeviceRoleView(generic.ObjectView):
|
||||
queryset = DeviceRole.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
devices = Device.objects.restrict(request.user, 'view').filter(
|
||||
device_role=instance
|
||||
)
|
||||
|
||||
devices_table = tables.DeviceTable(devices)
|
||||
devices_table.columns.hide('device_role')
|
||||
paginate_table(devices_table, request)
|
||||
|
||||
return {
|
||||
'devices_table': devices_table,
|
||||
}
|
||||
|
||||
|
||||
class DeviceRoleEditView(generic.ObjectEditView):
|
||||
queryset = DeviceRole.objects.all()
|
||||
model_form = forms.DeviceRoleForm
|
||||
@ -1056,6 +1159,23 @@ class PlatformListView(generic.ObjectListView):
|
||||
table = tables.PlatformTable
|
||||
|
||||
|
||||
class PlatformView(generic.ObjectView):
|
||||
queryset = Platform.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
devices = Device.objects.restrict(request.user, 'view').filter(
|
||||
platform=instance
|
||||
)
|
||||
|
||||
devices_table = tables.DeviceTable(devices)
|
||||
devices_table.columns.hide('platform')
|
||||
paginate_table(devices_table, request)
|
||||
|
||||
return {
|
||||
'devices_table': devices_table,
|
||||
}
|
||||
|
||||
|
||||
class PlatformEditView(generic.ObjectEditView):
|
||||
queryset = Platform.objects.all()
|
||||
model_form = forms.PlatformForm
|
||||
|
@ -1,4 +1,5 @@
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils.text import slugify
|
||||
from taggit.models import TagBase, GenericTaggedItemBase
|
||||
|
||||
@ -30,6 +31,9 @@ class Tag(ChangeLoggedModel, TagBase):
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('extras:tag', args=[self.pk])
|
||||
|
||||
def slugify(self, tag, i=None):
|
||||
# Allow Unicode in Tag slugs (avoids empty slugs for Tags with all-Unicode names)
|
||||
slug = slugify(tag, allow_unicode=True)
|
||||
|
@ -38,6 +38,9 @@ OBJECTCHANGE_REQUEST_ID = """
|
||||
|
||||
class TagTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = tables.Column(
|
||||
linkify=True
|
||||
)
|
||||
color = ColorColumn()
|
||||
actions = ButtonsColumn(Tag)
|
||||
|
||||
|
@ -13,6 +13,7 @@ urlpatterns = [
|
||||
path('tags/import/', views.TagBulkImportView.as_view(), name='tag_import'),
|
||||
path('tags/edit/', views.TagBulkEditView.as_view(), name='tag_bulk_edit'),
|
||||
path('tags/delete/', views.TagBulkDeleteView.as_view(), name='tag_bulk_delete'),
|
||||
path('tags/<int:pk>/', views.TagView.as_view(), name='tag'),
|
||||
path('tags/<int:pk>/edit/', views.TagEditView.as_view(), name='tag_edit'),
|
||||
path('tags/<int:pk>/delete/', views.TagDeleteView.as_view(), name='tag_delete'),
|
||||
path('tags/<int:pk>/changelog/', views.ObjectChangeLogView.as_view(), name='tag_changelog', kwargs={'model': Tag}),
|
||||
|
@ -34,6 +34,17 @@ class TagListView(generic.ObjectListView):
|
||||
table = tables.TagTable
|
||||
|
||||
|
||||
class TagView(generic.ObjectView):
|
||||
queryset = Tag.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
tagged_items = TaggedItem.objects.filter(tag=instance)
|
||||
|
||||
return {
|
||||
'tagged_item_count': tagged_items.count(),
|
||||
}
|
||||
|
||||
|
||||
class TagEditView(generic.ObjectEditView):
|
||||
queryset = Tag.objects.all()
|
||||
model_form = forms.TagForm
|
||||
@ -235,11 +246,6 @@ class ObjectChangeLogView(View):
|
||||
# fall back to using base.html.
|
||||
if self.base_template is None:
|
||||
self.base_template = f"{model._meta.app_label}/{model._meta.model_name}.html"
|
||||
# TODO: This can be removed once an object view has been established for every model.
|
||||
try:
|
||||
template.loader.get_template(self.base_template)
|
||||
except template.TemplateDoesNotExist:
|
||||
self.base_template = 'base.html'
|
||||
|
||||
return render(request, 'extras/object_changelog.html', {
|
||||
'object': obj,
|
||||
@ -368,11 +374,6 @@ class ObjectJournalView(View):
|
||||
# fall back to using base.html.
|
||||
if self.base_template is None:
|
||||
self.base_template = f"{model._meta.app_label}/{model._meta.model_name}.html"
|
||||
# TODO: This can be removed once an object view has been established for every model.
|
||||
try:
|
||||
template.loader.get_template(self.base_template)
|
||||
except template.TemplateDoesNotExist:
|
||||
self.base_template = 'base.html'
|
||||
|
||||
return render(request, 'extras/object_journal.html', {
|
||||
'object': obj,
|
||||
|
@ -66,7 +66,7 @@ class RIR(OrganizationalModel):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?rir={}".format(reverse('ipam:aggregate_list'), self.slug)
|
||||
return reverse('ipam:rir', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
@ -216,6 +216,9 @@ class Role(OrganizationalModel):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('ipam:role', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.name,
|
||||
|
@ -70,7 +70,7 @@ class VLANGroup(OrganizationalModel):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('ipam:vlangroup_vlans', args=[self.pk])
|
||||
return reverse('ipam:vlangroup', args=[self.pk])
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
@ -224,6 +224,9 @@ class AggregateDetailTable(AggregateTable):
|
||||
|
||||
class RoleTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = tables.Column(
|
||||
linkify=True
|
||||
)
|
||||
prefix_count = LinkedCountColumn(
|
||||
viewname='ipam:prefix_list',
|
||||
url_params={'role': 'slug'},
|
||||
@ -450,9 +453,8 @@ class VLANTable(BaseTable):
|
||||
site = tables.Column(
|
||||
linkify=True
|
||||
)
|
||||
group = tables.LinkColumn(
|
||||
viewname='ipam:vlangroup_vlans',
|
||||
args=[Accessor('group__pk')]
|
||||
group = tables.Column(
|
||||
linkify=True
|
||||
)
|
||||
tenant = TenantColumn()
|
||||
status = ChoiceFieldColumn(
|
||||
|
@ -37,6 +37,7 @@ urlpatterns = [
|
||||
path('rirs/import/', views.RIRBulkImportView.as_view(), name='rir_import'),
|
||||
path('rirs/edit/', views.RIRBulkEditView.as_view(), name='rir_bulk_edit'),
|
||||
path('rirs/delete/', views.RIRBulkDeleteView.as_view(), name='rir_bulk_delete'),
|
||||
path('rirs/<int:pk>/', views.RIRView.as_view(), name='rir'),
|
||||
path('rirs/<int:pk>/edit/', views.RIREditView.as_view(), name='rir_edit'),
|
||||
path('rirs/<int:pk>/delete/', views.RIRDeleteView.as_view(), name='rir_delete'),
|
||||
path('rirs/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rir_changelog', kwargs={'model': RIR}),
|
||||
@ -59,6 +60,7 @@ urlpatterns = [
|
||||
path('roles/import/', views.RoleBulkImportView.as_view(), name='role_import'),
|
||||
path('roles/edit/', views.RoleBulkEditView.as_view(), name='role_bulk_edit'),
|
||||
path('roles/delete/', views.RoleBulkDeleteView.as_view(), name='role_bulk_delete'),
|
||||
path('roles/<int:pk>/', views.RoleView.as_view(), name='role'),
|
||||
path('roles/<int:pk>/edit/', views.RoleEditView.as_view(), name='role_edit'),
|
||||
path('roles/<int:pk>/delete/', views.RoleDeleteView.as_view(), name='role_delete'),
|
||||
path('roles/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='role_changelog', kwargs={'model': Role}),
|
||||
@ -97,9 +99,9 @@ urlpatterns = [
|
||||
path('vlan-groups/import/', views.VLANGroupBulkImportView.as_view(), name='vlangroup_import'),
|
||||
path('vlan-groups/edit/', views.VLANGroupBulkEditView.as_view(), name='vlangroup_bulk_edit'),
|
||||
path('vlan-groups/delete/', views.VLANGroupBulkDeleteView.as_view(), name='vlangroup_bulk_delete'),
|
||||
path('vlan-groups/<int:pk>/', views.VLANGroupView.as_view(), name='vlangroup'),
|
||||
path('vlan-groups/<int:pk>/edit/', views.VLANGroupEditView.as_view(), name='vlangroup_edit'),
|
||||
path('vlan-groups/<int:pk>/delete/', views.VLANGroupDeleteView.as_view(), name='vlangroup_delete'),
|
||||
path('vlan-groups/<int:pk>/vlans/', views.VLANGroupVLANsView.as_view(), name='vlangroup_vlans'),
|
||||
path('vlan-groups/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='vlangroup_changelog', kwargs={'model': VLANGroup}),
|
||||
|
||||
# VLANs
|
||||
|
@ -148,6 +148,23 @@ class RIRListView(generic.ObjectListView):
|
||||
template_name = 'ipam/rir_list.html'
|
||||
|
||||
|
||||
class RIRView(generic.ObjectView):
|
||||
queryset = RIR.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
aggregates = Aggregate.objects.restrict(request.user, 'view').filter(
|
||||
rir=instance
|
||||
)
|
||||
|
||||
aggregates_table = tables.AggregateTable(aggregates)
|
||||
aggregates_table.columns.hide('rir')
|
||||
paginate_table(aggregates_table, request)
|
||||
|
||||
return {
|
||||
'aggregates_table': aggregates_table,
|
||||
}
|
||||
|
||||
|
||||
class RIREditView(generic.ObjectEditView):
|
||||
queryset = RIR.objects.all()
|
||||
model_form = forms.RIRForm
|
||||
@ -286,6 +303,23 @@ class RoleListView(generic.ObjectListView):
|
||||
table = tables.RoleTable
|
||||
|
||||
|
||||
class RoleView(generic.ObjectView):
|
||||
queryset = Role.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
prefixes = Prefix.objects.restrict(request.user, 'view').filter(
|
||||
role=instance
|
||||
)
|
||||
|
||||
prefixes_table = tables.PrefixTable(prefixes)
|
||||
prefixes_table.columns.hide('role')
|
||||
paginate_table(prefixes_table, request)
|
||||
|
||||
return {
|
||||
'prefixes_table': prefixes_table,
|
||||
}
|
||||
|
||||
|
||||
class RoleEditView(generic.ObjectEditView):
|
||||
queryset = Role.objects.all()
|
||||
model_form = forms.RoleForm
|
||||
@ -633,6 +667,29 @@ class VLANGroupListView(generic.ObjectListView):
|
||||
table = tables.VLANGroupTable
|
||||
|
||||
|
||||
class VLANGroupView(generic.ObjectView):
|
||||
queryset = VLANGroup.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
vlans = VLAN.objects.restrict(request.user, 'view').filter(group=instance).prefetch_related(
|
||||
Prefetch('prefixes', queryset=Prefix.objects.restrict(request.user))
|
||||
)
|
||||
vlans_count = vlans.count()
|
||||
vlans = add_available_vlans(instance, vlans)
|
||||
|
||||
vlans_table = tables.VLANDetailTable(vlans)
|
||||
if request.user.has_perm('ipam.change_vlan') or request.user.has_perm('ipam.delete_vlan'):
|
||||
vlans_table.columns.show('pk')
|
||||
vlans_table.columns.hide('site')
|
||||
vlans_table.columns.hide('group')
|
||||
paginate_table(vlans_table, request)
|
||||
|
||||
return {
|
||||
'vlans_count': vlans_count,
|
||||
'vlans_table': vlans_table,
|
||||
}
|
||||
|
||||
|
||||
class VLANGroupEditView(generic.ObjectEditView):
|
||||
queryset = VLANGroup.objects.all()
|
||||
model_form = forms.VLANGroupForm
|
||||
@ -666,38 +723,6 @@ class VLANGroupBulkDeleteView(generic.BulkDeleteView):
|
||||
table = tables.VLANGroupTable
|
||||
|
||||
|
||||
class VLANGroupVLANsView(generic.ObjectView):
|
||||
queryset = VLANGroup.objects.all()
|
||||
template_name = 'ipam/vlangroup_vlans.html'
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
vlans = VLAN.objects.restrict(request.user, 'view').filter(group=instance).prefetch_related(
|
||||
Prefetch('prefixes', queryset=Prefix.objects.restrict(request.user))
|
||||
)
|
||||
vlans = add_available_vlans(instance, vlans)
|
||||
|
||||
vlan_table = tables.VLANDetailTable(vlans)
|
||||
if request.user.has_perm('ipam.change_vlan') or request.user.has_perm('ipam.delete_vlan'):
|
||||
vlan_table.columns.show('pk')
|
||||
vlan_table.columns.hide('site')
|
||||
vlan_table.columns.hide('group')
|
||||
paginate_table(vlan_table, request)
|
||||
|
||||
# Compile permissions list for rendering the object table
|
||||
permissions = {
|
||||
'add': request.user.has_perm('ipam.add_vlan'),
|
||||
'change': request.user.has_perm('ipam.change_vlan'),
|
||||
'delete': request.user.has_perm('ipam.delete_vlan'),
|
||||
}
|
||||
|
||||
return {
|
||||
'first_available_vlan': instance.get_next_available_vid(),
|
||||
'bulk_querystring': f'group_id={instance.pk}',
|
||||
'vlan_table': vlan_table,
|
||||
'permissions': permissions,
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# VLANs
|
||||
#
|
||||
|
@ -263,7 +263,7 @@ class SecretRole(OrganizationalModel):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?role={}".format(reverse('secrets:secret_list'), self.slug)
|
||||
return reverse('secrets:secretrole', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
|
@ -13,6 +13,7 @@ urlpatterns = [
|
||||
path('secret-roles/import/', views.SecretRoleBulkImportView.as_view(), name='secretrole_import'),
|
||||
path('secret-roles/edit/', views.SecretRoleBulkEditView.as_view(), name='secretrole_bulk_edit'),
|
||||
path('secret-roles/delete/', views.SecretRoleBulkDeleteView.as_view(), name='secretrole_bulk_delete'),
|
||||
path('secret-roles/<int:pk>/', views.SecretRoleView.as_view(), name='secretrole'),
|
||||
path('secret-roles/<int:pk>/edit/', views.SecretRoleEditView.as_view(), name='secretrole_edit'),
|
||||
path('secret-roles/<int:pk>/delete/', views.SecretRoleDeleteView.as_view(), name='secretrole_delete'),
|
||||
path('secret-roles/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='secretrole_changelog', kwargs={'model': SecretRole}),
|
||||
|
@ -7,6 +7,7 @@ from django.utils.html import escape
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from netbox.views import generic
|
||||
from utilities.tables import paginate_table
|
||||
from utilities.utils import count_related
|
||||
from . import filters, forms, tables
|
||||
from .models import SecretRole, Secret, SessionKey, UserKey
|
||||
@ -33,6 +34,23 @@ class SecretRoleListView(generic.ObjectListView):
|
||||
table = tables.SecretRoleTable
|
||||
|
||||
|
||||
class SecretRoleView(generic.ObjectView):
|
||||
queryset = SecretRole.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
secrets = Secret.objects.restrict(request.user, 'view').filter(
|
||||
role=instance
|
||||
)
|
||||
|
||||
secrets_table = tables.SecretTable(secrets)
|
||||
secrets_table.columns.hide('role')
|
||||
paginate_table(secrets_table, request)
|
||||
|
||||
return {
|
||||
'secrets_table': secrets_table,
|
||||
}
|
||||
|
||||
|
||||
class SecretRoleEditView(generic.ObjectEditView):
|
||||
queryset = SecretRole.objects.all()
|
||||
model_form = forms.SecretRoleForm
|
||||
|
60
netbox/templates/circuits/circuittype.html
Normal file
60
netbox/templates/circuits/circuittype.html
Normal file
@ -0,0 +1,60 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'circuits:circuittype_list' %}">Circuit Types</a></li>
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Circuit Type</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Circuits</td>
|
||||
<td>
|
||||
<a href="{% url 'circuits:circuit_list' %}?type_id={{ object.pk }}">{{ circuits_table.rows|length }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/custom_fields_panel.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Circuits</strong>
|
||||
</div>
|
||||
{% include 'inc/table.html' with table=circuits_table %}
|
||||
{% if perms.circuits.add_circuit %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'circuits:circuit_add' %}?type={{ object.pk }}" class="btn btn-xs btn-primary">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add circuit
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'inc/paginator.html' with paginator=circuits_table.paginator page=circuits_table.page %}
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
76
netbox/templates/dcim/devicerole.html
Normal file
76
netbox/templates/dcim/devicerole.html
Normal file
@ -0,0 +1,76 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'dcim:devicerole_list' %}">Device Roles</a></li>
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Device Role</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Color</td>
|
||||
<td>
|
||||
<span class="label color-block" style="background-color: #{{ object.color }}"> </span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>VM Role</td>
|
||||
<td>
|
||||
{% if object.vm_role %}
|
||||
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
|
||||
{% else %}
|
||||
<i class="mdi mdi-close-thick text-danger" title="No"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Devices</td>
|
||||
<td>
|
||||
<a href="{% url 'dcim:device_list' %}?role_id={{ object.pk }}">{{ devices_table.rows|length }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/custom_fields_panel.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Devices</strong>
|
||||
</div>
|
||||
{% include 'inc/table.html' with table=devices_table %}
|
||||
{% if perms.dcim.add_device %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'dcim:device_add' %}?device_role={{ object.pk }}" class="btn btn-xs btn-primary">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add device
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'inc/paginator.html' with paginator=devices_table.paginator page=devices_table.page %}
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
73
netbox/templates/dcim/location.html
Normal file
73
netbox/templates/dcim/location.html
Normal file
@ -0,0 +1,73 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'dcim:location_list' %}">Location</a></li>
|
||||
{% for location in object.get_ancestors %}
|
||||
<li><a href="{{ location.get_absolute_url }}">{{ location }}</a></li>
|
||||
{% endfor %}
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Location</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Parent</td>
|
||||
<td>
|
||||
{% if object.parent %}
|
||||
<a href="{{ object.parent.get_absolute_url }}">{{ object.parent }}</a>
|
||||
{% else %}
|
||||
<span class="text-muted">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Devices</td>
|
||||
<td>
|
||||
<a href="{% url 'dcim:device_list' %}?location_id={{ object.pk }}">{{ devices_table.rows|length }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/custom_fields_panel.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Devices</strong>
|
||||
</div>
|
||||
{% include 'inc/table.html' with table=devices_table %}
|
||||
{% if perms.dcim.add_device %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'dcim:device_add' %}?location={{ object.pk }}" class="btn btn-xs btn-primary">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add device
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'inc/paginator.html' with paginator=devices_table.paginator page=devices_table.page %}
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
60
netbox/templates/dcim/manufacturer.html
Normal file
60
netbox/templates/dcim/manufacturer.html
Normal file
@ -0,0 +1,60 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'dcim:manufacturer_list' %}">Manufacturers</a></li>
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Manufacturer</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Device types</td>
|
||||
<td>
|
||||
<a href="{% url 'dcim:devicetype_list' %}?manufacturer_id={{ object.pk }}">{{ devicetypes_table.rows|length }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/custom_fields_panel.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Device Types</strong>
|
||||
</div>
|
||||
{% include 'inc/table.html' with table=devicetypes_table %}
|
||||
{% if perms.dcim.add_devicetype %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'dcim:devicetype_add' %}?manufacturer={{ object.pk }}" class="btn btn-xs btn-primary">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add device type
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'inc/paginator.html' with paginator=devicetypes_table.paginator page=devicetypes_table.page %}
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
68
netbox/templates/dcim/platform.html
Normal file
68
netbox/templates/dcim/platform.html
Normal file
@ -0,0 +1,68 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'dcim:platform_list' %}">Platforms</a></li>
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Platform</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NAPALM Driver</td>
|
||||
<td>{{ object.napalm_driver }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NAPALM Arguments</td>
|
||||
<td><pre>{{ object.napalm_args }}</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Devices</td>
|
||||
<td>
|
||||
<a href="{% url 'dcim:device_list' %}?platform_id={{ object.pk }}">{{ devices_table.rows|length }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/custom_fields_panel.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Devices</strong>
|
||||
</div>
|
||||
{% include 'inc/table.html' with table=devices_table %}
|
||||
{% if perms.dcim.add_device %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'dcim:device_add' %}?device_role={{ object.pk }}" class="btn btn-xs btn-primary">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add device
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'inc/paginator.html' with paginator=devices_table.paginator page=devices_table.page %}
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
66
netbox/templates/dcim/rackrole.html
Normal file
66
netbox/templates/dcim/rackrole.html
Normal file
@ -0,0 +1,66 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'dcim:rackrole_list' %}">Rack Roles</a></li>
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Rack Role</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Color</td>
|
||||
<td>
|
||||
<span class="label color-block" style="background-color: #{{ object.color }}"> </span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Racks</td>
|
||||
<td>
|
||||
<a href="{% url 'dcim:rack_list' %}?role_id={{ object.pk }}">{{ racks_table.rows|length }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/custom_fields_panel.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Racks</strong>
|
||||
</div>
|
||||
{% include 'inc/table.html' with table=racks_table %}
|
||||
{% if perms.dcim.add_rack %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'dcim:rack_add' %}?role={{ object.pk }}" class="btn btn-xs btn-primary">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add rack
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'inc/paginator.html' with paginator=racks_table.paginator page=racks_table.page %}
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
73
netbox/templates/dcim/region.html
Normal file
73
netbox/templates/dcim/region.html
Normal file
@ -0,0 +1,73 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'dcim:region_list' %}">Region</a></li>
|
||||
{% for region in object.get_ancestors %}
|
||||
<li><a href="{{ region.get_absolute_url }}">{{ region }}</a></li>
|
||||
{% endfor %}
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Region</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Parent</td>
|
||||
<td>
|
||||
{% if object.parent %}
|
||||
<a href="{{ object.parent.get_absolute_url }}">{{ object.parent }}</a>
|
||||
{% else %}
|
||||
<span class="text-muted">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sites</td>
|
||||
<td>
|
||||
<a href="{% url 'dcim:site_list' %}?region_id={{ object.pk }}">{{ sites_table.rows|length }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/custom_fields_panel.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Sites</strong>
|
||||
</div>
|
||||
{% include 'inc/table.html' with table=sites_table %}
|
||||
{% if perms.dcim.add_site %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'dcim:site_add' %}?region={{ object.pk }}" class="btn btn-xs btn-primary">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add site
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'inc/paginator.html' with paginator=sites_table.paginator page=sites_table.page %}
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
73
netbox/templates/dcim/sitegroup.html
Normal file
73
netbox/templates/dcim/sitegroup.html
Normal file
@ -0,0 +1,73 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'dcim:sitegroup_list' %}">Site Groups</a></li>
|
||||
{% for sitegroup in object.get_ancestors %}
|
||||
<li><a href="{{ sitegroup.get_absolute_url }}">{{ sitegroup }}</a></li>
|
||||
{% endfor %}
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Site Group</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Parent</td>
|
||||
<td>
|
||||
{% if object.parent %}
|
||||
<a href="{{ object.parent.get_absolute_url }}">{{ object.parent }}</a>
|
||||
{% else %}
|
||||
<span class="text-muted">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sites</td>
|
||||
<td>
|
||||
<a href="{% url 'dcim:site_list' %}?group_id={{ object.pk }}">{{ sites_table.rows|length }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/custom_fields_panel.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Sites</strong>
|
||||
</div>
|
||||
{% include 'inc/table.html' with table=sites_table %}
|
||||
{% if perms.dcim.add_site %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'dcim:site_add' %}?group={{ object.pk }}" class="btn btn-xs btn-primary">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add site
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'inc/paginator.html' with paginator=sites_table.paginator page=sites_table.page %}
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,98 +1,51 @@
|
||||
{% extends 'base.html' %}
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block header %}
|
||||
<div class="row">
|
||||
<div class="col-sm-8 col-md-9">
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{% url 'extras:tag_list' %}">Tags</a></li>
|
||||
<li>{{ object }}</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="col-sm-4 col-md-3">
|
||||
<form action="{% url 'extras:tag_list' %}" method="get">
|
||||
<div class="input-group">
|
||||
<input type="text" name="q" class="form-control" />
|
||||
<span class="input-group-btn">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<span class="mdi mdi-magnify" aria-hidden="true"></span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
{% if perms.taggit.change_tag %}
|
||||
<a href="{% url 'extras:tag_edit' slug=object.slug %}" class="btn btn-warning">
|
||||
<span class="mdi mdi-pencil" aria-hidden="true"></span>
|
||||
Edit this tag
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if perms.taggit.delete_tag %}
|
||||
<a href="{% url 'extras:tag_delete' slug=object.slug %}" class="btn btn-danger">
|
||||
<span class="mdi mdi-trash-can-outline" aria-hidden="true"></span>
|
||||
Delete this tag
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<h1>{% block title %}Tag: {{ object }}{% endblock %}</h1>
|
||||
{% include 'inc/created_updated.html' %}
|
||||
<ul class="nav nav-tabs">
|
||||
<li role="presentation"{% if not active_tab %} class="active"{% endif %}>
|
||||
<a href="{{ object.get_absolute_url }}">Tag</a>
|
||||
</li>
|
||||
{% if perms.extras.view_objectchange %}
|
||||
<li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
|
||||
<a href="{% url 'extras:tag_changelog' pk=object.pk %}">Change Log</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'extras:tag_list' %}">Tags</a></li>
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Tag</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>
|
||||
{{ object.name }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Slug</td>
|
||||
<td>
|
||||
{{ object.slug }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Tagged Items</td>
|
||||
<td>
|
||||
{{ items_count }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Color</td>
|
||||
<td>
|
||||
<span class="label color-block" style="background-color: #{{ object.color }}"> </span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>
|
||||
{{ object.description|placeholder }}
|
||||
</td>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'panel_table.html' with table=items_table heading='Tagged Objects' %}
|
||||
{% include 'inc/paginator.html' with paginator=items_table.paginator page=items_table.page %}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Tag</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Color</td>
|
||||
<td>
|
||||
<span class="label color-block" style="background-color: #{{ object.color }}"> </span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Tagged Items</td>
|
||||
<td>
|
||||
{{ tagged_item_count }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
70
netbox/templates/ipam/rir.html
Normal file
70
netbox/templates/ipam/rir.html
Normal file
@ -0,0 +1,70 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'ipam:rir_list' %}">RIRs</a></li>
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>RIR</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Private</td>
|
||||
<td>
|
||||
{% if object.is_private %}
|
||||
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
|
||||
{% else %}
|
||||
<i class="mdi mdi-close-thick text-danger" title="No"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Aggregates</td>
|
||||
<td>
|
||||
<a href="{% url 'ipam:aggregate_list' %}?rir_id={{ object.pk }}">{{ aggregates_table.rows|length }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/custom_fields_panel.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Aggregates</strong>
|
||||
</div>
|
||||
{% include 'inc/table.html' with table=aggregates_table %}
|
||||
{% if perms.ipam.add_aggregate %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'ipam:aggregate_add' %}?rir={{ object.pk }}" class="btn btn-xs btn-primary">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add aggregate
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'inc/paginator.html' with paginator=aggregates_table.paginator page=aggregates_table.page %}
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
64
netbox/templates/ipam/role.html
Normal file
64
netbox/templates/ipam/role.html
Normal file
@ -0,0 +1,64 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'ipam:role_list' %}">Roles</a></li>
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Role</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Weight</td>
|
||||
<td>{{ object.weight }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Prefixes</td>
|
||||
<td>
|
||||
<a href="{% url 'ipam:prefix_list' %}?role_id={{ object.pk }}">{{ prefixes_table.rows|length }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/custom_fields_panel.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Prefixes</strong>
|
||||
</div>
|
||||
{% include 'inc/table.html' with table=prefixes_table %}
|
||||
{% if perms.ipam.add_prefix %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'ipam:prefix_add' %}?role={{ object.pk }}" class="btn btn-xs btn-primary">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add prefix
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'inc/paginator.html' with paginator=prefixes_table.paginator page=prefixes_table.page %}
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
72
netbox/templates/ipam/vlangroup.html
Normal file
72
netbox/templates/ipam/vlangroup.html
Normal file
@ -0,0 +1,72 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'ipam:vlangroup_list' %}">VLAN Groups</a></li>
|
||||
{% if object.scope %}
|
||||
<li><a href="{{ object.scope.get_absolute_url }}">{{ object.scope }}</a></li>
|
||||
{% endif %}
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>VLAN Group</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Scope</td>
|
||||
<td>
|
||||
{% if object.scope %}
|
||||
<a href="{{ object.scope.get_absolute_url }}">{{ object.scope }}</a>
|
||||
{% else %}
|
||||
<span class="text-muted">—</span>
|
||||
{% endif %}
|
||||
</tr>
|
||||
<tr>
|
||||
<td>VLANs</td>
|
||||
<td>
|
||||
<a href="{% url 'ipam:vlan_list' %}?group_id={{ object.pk }}">{{ vlans_count }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/custom_fields_panel.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>VLANs</strong>
|
||||
</div>
|
||||
{% include 'inc/table.html' with table=vlans_table %}
|
||||
{% if perms.ipam.add_vlan %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'ipam:vlan_add' %}?role={{ object.pk }}" class="btn btn-xs btn-primary">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add VLAN
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'inc/paginator.html' with paginator=vlans_table.paginator page=vlans_table.page %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -1,24 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}{{ object }} - VLANs{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row noprint">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{% url 'ipam:vlangroup_list' %}">VLAN Groups</a></li>
|
||||
{% if object.site %}
|
||||
<li><a href="{{ object.site.get_absolute_url }}">{{ object.site }}</a></li>
|
||||
{% endif %}
|
||||
<li>{{ object }}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'ipam/inc/vlangroup_header.html' %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{% include 'utilities/obj_table.html' with table=vlan_table table_template='panel_table.html' heading='VLANs' bulk_edit_url='ipam:vlan_bulk_edit' bulk_delete_url='ipam:vlan_bulk_delete' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
60
netbox/templates/secrets/secretrole.html
Normal file
60
netbox/templates/secrets/secretrole.html
Normal file
@ -0,0 +1,60 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'secrets:secretrole_list' %}">Secret Roles</a></li>
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Secret Role</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Secrets</td>
|
||||
<td>
|
||||
<a href="{% url 'secrets:secret_list' %}?role_id={{ object.pk }}">{{ secrets_table.rows|length }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/custom_fields_panel.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Secrets</strong>
|
||||
</div>
|
||||
{% include 'inc/table.html' with table=secrets_table %}
|
||||
{% if perms.secrets.add_secret %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'secrets:secret_add' %}?role={{ object.pk }}" class="btn btn-xs btn-primary">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add secret
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'inc/paginator.html' with paginator=secrets_table.paginator page=secrets_table.page %}
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
73
netbox/templates/tenancy/tenantgroup.html
Normal file
73
netbox/templates/tenancy/tenantgroup.html
Normal file
@ -0,0 +1,73 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'tenancy:tenantgroup_list' %}">Tenant Groups</a></li>
|
||||
{% for tenantgroup in object.get_ancestors %}
|
||||
<li><a href="{{ tenantgroup.get_absolute_url }}">{{ tenantgroup }}</a></li>
|
||||
{% endfor %}
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Tenant Group</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Parent</td>
|
||||
<td>
|
||||
{% if object.parent %}
|
||||
<a href="{{ object.parent.get_absolute_url }}">{{ object.parent }}</a>
|
||||
{% else %}
|
||||
<span class="text-muted">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sites</td>
|
||||
<td>
|
||||
<a href="{% url 'tenancy:tenant_list' %}?group_id={{ object.pk }}">{{ tenants_table.rows|length }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/custom_fields_panel.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Tenants</strong>
|
||||
</div>
|
||||
{% include 'inc/table.html' with table=tenants_table %}
|
||||
{% if perms.tenancy.add_tenant %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'tenancy:tenant_add' %}?group={{ object.pk }}" class="btn btn-xs btn-primary">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add tenant
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'inc/paginator.html' with paginator=tenants_table.paginator page=tenants_table.page %}
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
60
netbox/templates/virtualization/clustergroup.html
Normal file
60
netbox/templates/virtualization/clustergroup.html
Normal file
@ -0,0 +1,60 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'virtualization:clustertype_list' %}">Cluster Groups</a></li>
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Cluster Group</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Clusters</td>
|
||||
<td>
|
||||
<a href="{% url 'virtualization:cluster_list' %}?group_id={{ object.pk }}">{{ clusters_table.rows|length }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/custom_fields_panel.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Clusters</strong>
|
||||
</div>
|
||||
{% include 'inc/table.html' with table=clusters_table %}
|
||||
{% if perms.virtualization.add_cluster %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'virtualization:cluster_add' %}?type={{ object.pk }}" class="btn btn-xs btn-primary">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add cluster
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'inc/paginator.html' with paginator=clusters_table.paginator page=clusters_table.page %}
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
60
netbox/templates/virtualization/clustertype.html
Normal file
60
netbox/templates/virtualization/clustertype.html
Normal file
@ -0,0 +1,60 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li><a href="{% url 'virtualization:clustertype_list' %}">Cluster Types</a></li>
|
||||
<li>{{ object }}</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Cluster Type</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Clusters</td>
|
||||
<td>
|
||||
<a href="{% url 'virtualization:cluster_list' %}?type_id={{ object.pk }}">{{ clusters_table.rows|length }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/custom_fields_panel.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Clusters</strong>
|
||||
</div>
|
||||
{% include 'inc/table.html' with table=clusters_table %}
|
||||
{% if perms.virtualization.add_cluster %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'virtualization:cluster_add' %}?type={{ object.pk }}" class="btn btn-xs btn-primary">
|
||||
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add cluster
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'inc/paginator.html' with paginator=clusters_table.paginator page=clusters_table.page %}
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -45,7 +45,7 @@ class TenantGroup(NestedGroupModel):
|
||||
ordering = ['name']
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?group={}".format(reverse('tenancy:tenant_list'), self.slug)
|
||||
return reverse('tenancy:tenantgroup', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
|
@ -35,7 +35,9 @@ class TenantColumn(tables.TemplateColumn):
|
||||
|
||||
class TenantGroupTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = MPTTColumn()
|
||||
name = MPTTColumn(
|
||||
linkify=True
|
||||
)
|
||||
tenant_count = LinkedCountColumn(
|
||||
viewname='tenancy:tenant_list',
|
||||
url_params={'group': 'slug'},
|
||||
|
@ -13,6 +13,7 @@ urlpatterns = [
|
||||
path('tenant-groups/import/', views.TenantGroupBulkImportView.as_view(), name='tenantgroup_import'),
|
||||
path('tenant-groups/edit/', views.TenantGroupBulkEditView.as_view(), name='tenantgroup_bulk_edit'),
|
||||
path('tenant-groups/delete/', views.TenantGroupBulkDeleteView.as_view(), name='tenantgroup_bulk_delete'),
|
||||
path('tenant-groups/<int:pk>/', views.TenantGroupView.as_view(), name='tenantgroup'),
|
||||
path('tenant-groups/<int:pk>/edit/', views.TenantGroupEditView.as_view(), name='tenantgroup_edit'),
|
||||
path('tenant-groups/<int:pk>/delete/', views.TenantGroupDeleteView.as_view(), name='tenantgroup_delete'),
|
||||
path('tenant-groups/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='tenantgroup_changelog', kwargs={'model': TenantGroup}),
|
||||
|
@ -1,9 +1,8 @@
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
|
||||
from circuits.models import Circuit
|
||||
from dcim.models import Site, Rack, Device, RackReservation
|
||||
from ipam.models import IPAddress, Prefix, VLAN, VRF
|
||||
from netbox.views import generic
|
||||
from utilities.tables import paginate_table
|
||||
from virtualization.models import VirtualMachine, Cluster
|
||||
from . import filters, forms, tables
|
||||
from .models import Tenant, TenantGroup
|
||||
@ -24,6 +23,23 @@ class TenantGroupListView(generic.ObjectListView):
|
||||
table = tables.TenantGroupTable
|
||||
|
||||
|
||||
class TenantGroupView(generic.ObjectView):
|
||||
queryset = TenantGroup.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
tenants = Tenant.objects.restrict(request.user, 'view').filter(
|
||||
group=instance
|
||||
)
|
||||
|
||||
tenants_table = tables.TenantTable(tenants)
|
||||
tenants_table.columns.hide('group')
|
||||
paginate_table(tenants_table, request)
|
||||
|
||||
return {
|
||||
'tenants_table': tenants_table,
|
||||
}
|
||||
|
||||
|
||||
class TenantGroupEditView(generic.ObjectEditView):
|
||||
queryset = TenantGroup.objects.all()
|
||||
model_form = forms.TenantGroupForm
|
||||
|
@ -1018,6 +1018,7 @@ class ViewTestCases:
|
||||
maxDiff = None
|
||||
|
||||
class OrganizationalObjectViewTestCase(
|
||||
GetObjectViewTestCase,
|
||||
GetObjectChangelogViewTestCase,
|
||||
CreateObjectViewTestCase,
|
||||
EditObjectViewTestCase,
|
||||
|
@ -59,7 +59,7 @@ class ClusterType(OrganizationalModel):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?type={}".format(reverse('virtualization:cluster_list'), self.slug)
|
||||
return reverse('virtualization:clustertype', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
@ -102,7 +102,7 @@ class ClusterGroup(OrganizationalModel):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "{}?group={}".format(reverse('virtualization:cluster_list'), self.slug)
|
||||
return reverse('virtualization:clustergroup', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
|
@ -14,6 +14,7 @@ urlpatterns = [
|
||||
path('cluster-types/import/', views.ClusterTypeBulkImportView.as_view(), name='clustertype_import'),
|
||||
path('cluster-types/edit/', views.ClusterTypeBulkEditView.as_view(), name='clustertype_bulk_edit'),
|
||||
path('cluster-types/delete/', views.ClusterTypeBulkDeleteView.as_view(), name='clustertype_bulk_delete'),
|
||||
path('cluster-types/<int:pk>/', views.ClusterTypeView.as_view(), name='clustertype'),
|
||||
path('cluster-types/<int:pk>/edit/', views.ClusterTypeEditView.as_view(), name='clustertype_edit'),
|
||||
path('cluster-types/<int:pk>/delete/', views.ClusterTypeDeleteView.as_view(), name='clustertype_delete'),
|
||||
path('cluster-types/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='clustertype_changelog', kwargs={'model': ClusterType}),
|
||||
@ -24,6 +25,7 @@ urlpatterns = [
|
||||
path('cluster-groups/import/', views.ClusterGroupBulkImportView.as_view(), name='clustergroup_import'),
|
||||
path('cluster-groups/edit/', views.ClusterGroupBulkEditView.as_view(), name='clustergroup_bulk_edit'),
|
||||
path('cluster-groups/delete/', views.ClusterGroupBulkDeleteView.as_view(), name='clustergroup_bulk_delete'),
|
||||
path('cluster-groups/<int:pk>/', views.ClusterGroupView.as_view(), name='clustergroup'),
|
||||
path('cluster-groups/<int:pk>/edit/', views.ClusterGroupEditView.as_view(), name='clustergroup_edit'),
|
||||
path('cluster-groups/<int:pk>/delete/', views.ClusterGroupDeleteView.as_view(), name='clustergroup_delete'),
|
||||
path('cluster-groups/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='clustergroup_changelog', kwargs={'model': ClusterGroup}),
|
||||
|
@ -11,6 +11,7 @@ from ipam.models import IPAddress, Service
|
||||
from ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable
|
||||
from netbox.views import generic
|
||||
from secrets.models import Secret
|
||||
from utilities.tables import paginate_table
|
||||
from utilities.utils import count_related
|
||||
from . import filters, forms, tables
|
||||
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
|
||||
@ -27,6 +28,23 @@ class ClusterTypeListView(generic.ObjectListView):
|
||||
table = tables.ClusterTypeTable
|
||||
|
||||
|
||||
class ClusterTypeView(generic.ObjectView):
|
||||
queryset = ClusterType.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
clusters = Cluster.objects.restrict(request.user, 'view').filter(
|
||||
type=instance
|
||||
)
|
||||
|
||||
clusters_table = tables.ClusterTable(clusters)
|
||||
clusters_table.columns.hide('type')
|
||||
paginate_table(clusters_table, request)
|
||||
|
||||
return {
|
||||
'clusters_table': clusters_table,
|
||||
}
|
||||
|
||||
|
||||
class ClusterTypeEditView(generic.ObjectEditView):
|
||||
queryset = ClusterType.objects.all()
|
||||
model_form = forms.ClusterTypeForm
|
||||
@ -69,6 +87,23 @@ class ClusterGroupListView(generic.ObjectListView):
|
||||
table = tables.ClusterGroupTable
|
||||
|
||||
|
||||
class ClusterGroupView(generic.ObjectView):
|
||||
queryset = ClusterGroup.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
clusters = Cluster.objects.restrict(request.user, 'view').filter(
|
||||
group=instance
|
||||
)
|
||||
|
||||
clusters_table = tables.ClusterTable(clusters)
|
||||
clusters_table.columns.hide('group')
|
||||
paginate_table(clusters_table, request)
|
||||
|
||||
return {
|
||||
'clusters_table': clusters_table,
|
||||
}
|
||||
|
||||
|
||||
class ClusterGroupEditView(generic.ObjectEditView):
|
||||
queryset = ClusterGroup.objects.all()
|
||||
model_form = forms.ClusterGroupForm
|
||||
|
Loading…
Reference in New Issue
Block a user