Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions netbox/dcim/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -797,41 +797,49 @@ def get_extra_context(self, request, instance):
class DeviceTypeConsolePortsView(DeviceTypeComponentsView):
child_model = ConsolePortTemplate
table = tables.ConsolePortTemplateTable
filterset = filtersets.ConsolePortTemplateFilterSet


class DeviceTypeConsoleServerPortsView(DeviceTypeComponentsView):
child_model = ConsoleServerPortTemplate
table = tables.ConsoleServerPortTemplateTable
filterset = filtersets.ConsoleServerPortTemplateFilterSet


class DeviceTypePowerPortsView(DeviceTypeComponentsView):
child_model = PowerPortTemplate
table = tables.PowerPortTemplateTable
filterset = filtersets.PowerPortTemplateFilterSet


class DeviceTypePowerOutletsView(DeviceTypeComponentsView):
child_model = PowerOutletTemplate
table = tables.PowerOutletTemplateTable
filterset = filtersets.PowerOutletTemplateFilterSet


class DeviceTypeInterfacesView(DeviceTypeComponentsView):
child_model = InterfaceTemplate
table = tables.InterfaceTemplateTable
filterset = filtersets.InterfaceTemplateFilterSet


class DeviceTypeFrontPortsView(DeviceTypeComponentsView):
child_model = FrontPortTemplate
table = tables.FrontPortTemplateTable
filterset = filtersets.FrontPortTemplateFilterSet


class DeviceTypeRearPortsView(DeviceTypeComponentsView):
child_model = RearPortTemplate
table = tables.RearPortTemplateTable
filterset = filtersets.RearPortTemplateFilterSet


class DeviceTypeDeviceBaysView(DeviceTypeComponentsView):
child_model = DeviceBayTemplate
table = tables.DeviceBayTemplateTable
filterset = filtersets.DeviceBayTemplateFilterSet


class DeviceTypeEditView(generic.ObjectEditView):
Expand Down Expand Up @@ -1328,30 +1336,35 @@ def get_extra_context(self, request, instance):
class DeviceConsolePortsView(DeviceComponentsView):
child_model = ConsolePort
table = tables.DeviceConsolePortTable
filterset = filtersets.ConsolePortFilterSet
template_name = 'dcim/device/consoleports.html'


class DeviceConsoleServerPortsView(DeviceComponentsView):
child_model = ConsoleServerPort
table = tables.DeviceConsoleServerPortTable
filterset = filtersets.ConsoleServerPortFilterSet
template_name = 'dcim/device/consoleserverports.html'


class DevicePowerPortsView(DeviceComponentsView):
child_model = PowerPort
table = tables.DevicePowerPortTable
filterset = filtersets.PowerPortFilterSet
template_name = 'dcim/device/powerports.html'


class DevicePowerOutletsView(DeviceComponentsView):
child_model = PowerOutlet
table = tables.DevicePowerOutletTable
filterset = filtersets.PowerOutletFilterSet
template_name = 'dcim/device/poweroutlets.html'


class DeviceInterfacesView(DeviceComponentsView):
child_model = Interface
table = tables.DeviceInterfaceTable
filterset = filtersets.InterfaceFilterSet
template_name = 'dcim/device/interfaces.html'

def get_children(self, request, parent):
Expand All @@ -1364,24 +1377,28 @@ def get_children(self, request, parent):
class DeviceFrontPortsView(DeviceComponentsView):
child_model = FrontPort
table = tables.DeviceFrontPortTable
filterset = filtersets.FrontPortFilterSet
template_name = 'dcim/device/frontports.html'


class DeviceRearPortsView(DeviceComponentsView):
child_model = RearPort
table = tables.DeviceRearPortTable
filterset = filtersets.RearPortFilterSet
template_name = 'dcim/device/rearports.html'


class DeviceDeviceBaysView(DeviceComponentsView):
child_model = DeviceBay
table = tables.DeviceDeviceBayTable
filterset = filtersets.DeviceBayFilterSet
template_name = 'dcim/device/devicebays.html'


class DeviceInventoryView(DeviceComponentsView):
child_model = InventoryItem
table = tables.DeviceInventoryItemTable
filterset = filtersets.InventoryItemFilterSet
template_name = 'dcim/device/inventory.html'


Expand Down
6 changes: 6 additions & 0 deletions netbox/ipam/models/ip.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,12 @@ def family(self):
return self.prefix.version
return None

def get_child_prefixes(self):
"""
Return all Prefixes within this Aggregate
"""
return Prefix.objects.filter(prefix__net_contained=str(self.prefix))

def get_utilization(self):
"""
Determine the prefix utilization of the aggregate and return it as a percentage.
Expand Down
1 change: 1 addition & 0 deletions netbox/ipam/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
path('aggregates/edit/', views.AggregateBulkEditView.as_view(), name='aggregate_bulk_edit'),
path('aggregates/delete/', views.AggregateBulkDeleteView.as_view(), name='aggregate_bulk_delete'),
path('aggregates/<int:pk>/', views.AggregateView.as_view(), name='aggregate'),
path('aggregates/<int:pk>/prefixes/', views.AggregatePrefixesView.as_view(), name='aggregate_prefixes'),
path('aggregates/<int:pk>/edit/', views.AggregateEditView.as_view(), name='aggregate_edit'),
path('aggregates/<int:pk>/delete/', views.AggregateDeleteView.as_view(), name='aggregate_delete'),
path('aggregates/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='aggregate_changelog', kwargs={'model': Aggregate}),
Expand Down
64 changes: 32 additions & 32 deletions netbox/ipam/views.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
from django.contrib.contenttypes.models import ContentType
from django.db.models import Prefetch
from django.db.models.expressions import RawSQL
from django.http import Http404
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse

from dcim.filtersets import InterfaceFilterSet
from dcim.models import Device, Interface, Site
from dcim.tables import SiteTable
from netbox.views import generic
from utilities.tables import paginate_table
from utilities.utils import count_related
from virtualization.filtersets import VMInterfaceFilterSet
from virtualization.models import VirtualMachine, VMInterface
from . import filtersets, forms, tables
from .constants import *
from .models import *
from .models import ASN
from .utils import add_available_ipaddresses, add_requested_prefixes, add_available_vlans
from .utils import add_requested_prefixes, add_available_vlans


#
Expand Down Expand Up @@ -274,39 +275,32 @@ class AggregateListView(generic.ObjectListView):
class AggregateView(generic.ObjectView):
queryset = Aggregate.objects.all()

def get_extra_context(self, request, instance):
# Find all child prefixes contained in this aggregate
prefix_list = Prefix.objects.restrict(request.user, 'view').filter(
prefix__net_contained_or_equal=str(instance.prefix)
).prefetch_related(
'site', 'role'
).order_by(
'prefix'
)

# Return List of requested Prefixes
class AggregatePrefixesView(generic.ObjectChildrenView):
queryset = Aggregate.objects.all()
child_model = Prefix
table = tables.PrefixTable
filterset = filtersets.PrefixFilterSet
template_name = 'ipam/aggregate/prefixes.html'

def get_children(self, request, parent):
return Prefix.objects.restrict(request.user, 'view').filter(
prefix__net_contained_or_equal=str(parent.prefix)
).prefetch_related('site', 'role', 'tenant', 'vlan')

def prep_table_data(self, request, queryset, parent):
# Determine whether to show assigned prefixes, available prefixes, or both
show_available = bool(request.GET.get('show_available', 'true') == 'true')
show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true')
child_prefixes = add_requested_prefixes(instance.prefix, prefix_list, show_available, show_assigned)

prefix_table = tables.PrefixTable(child_prefixes, exclude=('utilization',))
if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'):
prefix_table.columns.show('pk')
paginate_table(prefix_table, request)

# Compile permissions list for rendering the object table
permissions = {
'add': request.user.has_perm('ipam.add_prefix'),
'change': request.user.has_perm('ipam.change_prefix'),
'delete': request.user.has_perm('ipam.delete_prefix'),
}
return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned)

def get_extra_context(self, request, instance):
return {
'prefix_table': prefix_table,
'permissions': permissions,
'bulk_querystring': f'within={instance.prefix}',
'show_available': show_available,
'show_assigned': show_assigned,
'active_tab': 'prefixes',
'show_available': bool(request.GET.get('show_available', 'true') == 'true'),
'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'),
}


Expand Down Expand Up @@ -457,17 +451,18 @@ class PrefixPrefixesView(generic.ObjectChildrenView):
queryset = Prefix.objects.all()
child_model = Prefix
table = tables.PrefixTable
filterset = filtersets.PrefixFilterSet
template_name = 'ipam/prefix/prefixes.html'

def get_children(self, request, parent):
child_prefixes = parent.get_child_prefixes().restrict(request.user, 'view')
return parent.get_child_prefixes().restrict(request.user, 'view')

# Add available prefixes if requested
def prep_table_data(self, request, queryset, parent):
# Determine whether to show assigned prefixes, available prefixes, or both
show_available = bool(request.GET.get('show_available', 'true') == 'true')
show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true')
child_prefixes = add_requested_prefixes(parent.prefix, child_prefixes, show_available, show_assigned)

return child_prefixes
return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned)

def get_extra_context(self, request, instance):
return {
Expand All @@ -483,6 +478,7 @@ class PrefixIPRangesView(generic.ObjectChildrenView):
queryset = Prefix.objects.all()
child_model = IPRange
table = tables.IPRangeTable
filterset = filtersets.IPRangeFilterSet
template_name = 'ipam/prefix/ip_ranges.html'

def get_children(self, request, parent):
Expand All @@ -499,6 +495,7 @@ class PrefixIPAddressesView(generic.ObjectChildrenView):
queryset = Prefix.objects.all()
child_model = IPAddress
table = tables.IPAddressTable
filterset = filtersets.IPAddressFilterSet
template_name = 'ipam/prefix/ip_addresses.html'

def get_children(self, request, parent):
Expand Down Expand Up @@ -560,6 +557,7 @@ class IPRangeIPAddressesView(generic.ObjectChildrenView):
queryset = IPRange.objects.all()
child_model = IPAddress
table = tables.IPAddressTable
filterset = filtersets.IPAddressFilterSet
template_name = 'ipam/iprange/ip_addresses.html'

def get_children(self, request, parent):
Expand Down Expand Up @@ -959,6 +957,7 @@ class VLANInterfacesView(generic.ObjectChildrenView):
queryset = VLAN.objects.all()
child_model = Interface
table = tables.VLANDevicesTable
filterset = InterfaceFilterSet
template_name = 'ipam/vlan/interfaces.html'

def get_children(self, request, parent):
Expand All @@ -974,6 +973,7 @@ class VLANVMInterfacesView(generic.ObjectChildrenView):
queryset = VLAN.objects.all()
child_model = VMInterface
table = tables.VLANVirtualMachinesTable
filterset = VMInterfaceFilterSet
template_name = 'ipam/vlan/vminterfaces.html'

def get_children(self, request, parent):
Expand Down
32 changes: 30 additions & 2 deletions netbox/netbox/views/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from utilities.forms import (
BootstrapMixin, BulkRenameForm, ConfirmationForm, CSVDataField, CSVFileField, ImportForm, restrict_form_fields,
)
from utilities.htmx import is_htmx
from utilities.permissions import get_permission_for_model
from utilities.tables import paginate_table
from utilities.utils import normalize_querydict, prepare_cloned_fields
Expand Down Expand Up @@ -83,35 +84,56 @@ class ObjectChildrenView(ObjectView):
queryset = None
child_model = None
table = None
filterset = None
template_name = None

def get_children(self, request, parent):
"""
Return a QuerySet or iterable of child objects.
Return a QuerySet of child objects.

request: The current request
parent: The parent object
"""
raise NotImplementedError(f'{self.__class__.__name__} must implement get_children()')

def prep_table_data(self, request, queryset, parent):
"""
Provides a hook for subclassed views to modify data before initializing the table.

:param request: The current request
:param queryset: The filtered queryset of child objects
:param parent: The parent object
"""
return queryset

def get(self, request, *args, **kwargs):
"""
GET handler for rendering child objects.
"""
instance = get_object_or_404(self.queryset, **kwargs)
child_objects = self.get_children(request, instance)

if self.filterset:
child_objects = self.filterset(request.GET, child_objects).qs

permissions = {}
for action in ('change', 'delete'):
perm_name = get_permission_for_model(self.child_model, action)
permissions[action] = request.user.has_perm(perm_name)

table = self.table(child_objects, user=request.user)
table = self.table(self.prep_table_data(request, child_objects, instance), user=request.user)
# Determine whether to display bulk action checkboxes
if 'pk' in table.base_columns and (permissions['change'] or permissions['delete']):
table.columns.show('pk')
paginate_table(table, request)

# If this is an HTMX request, return only the rendered table HTML
if is_htmx(request):
return render(request, 'htmx/table.html', {
'object': instance,
'table': table,
})

return render(request, self.get_template_name(), {
'object': instance,
'table': table,
Expand Down Expand Up @@ -233,6 +255,12 @@ def get(self, request):
table = self.get_table(request, permissions)
paginate_table(table, request)

# If this is an HTMX request, return only the rendered table HTML
if is_htmx(request):
return render(request, 'htmx/table.html', {
'table': table,
})

context = {
'content_type': content_type,
'table': table,
Expand Down
2 changes: 1 addition & 1 deletion netbox/project-static/dist/netbox-dark.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion netbox/project-static/dist/netbox-light.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion netbox/project-static/dist/netbox-print.css

Large diffs are not rendered by default.

24 changes: 12 additions & 12 deletions netbox/project-static/dist/netbox.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions netbox/project-static/dist/netbox.js.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions netbox/project-static/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"cookie": "^0.4.1",
"dayjs": "^1.10.4",
"flatpickr": "4.6.3",
"htmx.org": "^1.6.1",
"just-debounce-it": "^1.4.0",
"masonry-layout": "^4.2.2",
"query-string": "^6.14.1",
Expand Down
2 changes: 0 additions & 2 deletions netbox/project-static/src/buttons/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { initConnectionToggle } from './connectionToggle';
import { initDepthToggle } from './depthToggle';
import { initMoveButtons } from './moveOptions';
import { initPerPage } from './pagination';
import { initPreferenceUpdate } from './preferences';
import { initReslug } from './reslug';
import { initSelectAll } from './selectAll';
Expand All @@ -13,7 +12,6 @@ export function initButtons(): void {
initReslug,
initSelectAll,
initPreferenceUpdate,
initPerPage,
initMoveButtons,
]) {
func();
Expand Down
Loading