from collections import OrderedDict from django.conf import settings from django.contrib import messages from django.contrib.contenttypes.models import ContentType from django.core.paginator import EmptyPage, PageNotAnInteger from django.db import transaction from django.db.models import Count, F, Prefetch from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, modelformset_factory from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils.html import escape from django.utils.safestring import mark_safe from django.views.generic import View from circuits.models import Circuit from extras.models import Graph from extras.views import ObjectConfigContextView from ipam.models import IPAddress, Prefix, Service, VLAN from ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable from secrets.models import Secret from utilities.forms import ConfirmationForm from utilities.paginator import EnhancedPaginator from utilities.permissions import get_permission_for_model from utilities.utils import csv_format, get_subquery from utilities.views import ( BulkComponentCreateView, BulkDeleteView, BulkEditView, BulkImportView, BulkRenameView, ComponentCreateView, GetReturnURLMixin, ObjectView, ObjectImportView, ObjectDeleteView, ObjectEditView, ObjectListView, ObjectPermissionRequiredMixin, ) from virtualization.models import VirtualMachine from . import filters, forms, tables from .choices import DeviceFaceChoices from .constants import NONCONNECTABLE_IFACE_TYPES from .models import ( Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay, DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate, InventoryItem, Manufacturer, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis, ) class BulkDisconnectView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): """ An extendable view for disconnection console/power/interface components in bulk. """ queryset = None template_name = 'dcim/bulk_disconnect.html' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Create a new Form class from ConfirmationForm class _Form(ConfirmationForm): pk = ModelMultipleChoiceField( queryset=self.queryset, widget=MultipleHiddenInput() ) self.form = _Form def get_required_permission(self): return get_permission_for_model(self.queryset.model, 'change') def post(self, request): selected_objects = [] return_url = self.get_return_url(request) if '_confirm' in request.POST: form = self.form(request.POST) if form.is_valid(): with transaction.atomic(): count = 0 for obj in self.queryset.filter(pk__in=form.cleaned_data['pk']): if obj.cable is None: continue obj.cable.delete() count += 1 messages.success(request, "Disconnected {} {}".format( count, self.queryset.model._meta.verbose_name_plural )) return redirect(return_url) else: form = self.form(initial={'pk': request.POST.getlist('pk')}) selected_objects = self.queryset.filter(pk__in=form.initial['pk']) return render(request, self.template_name, { 'form': form, 'obj_type_plural': self.queryset.model._meta.verbose_name_plural, 'selected_objects': selected_objects, 'return_url': return_url, }) # # Regions # class RegionListView(ObjectListView): queryset = Region.objects.add_related_count( Region.objects.all(), Site, 'region', 'site_count', cumulative=True ) filterset = filters.RegionFilterSet filterset_form = forms.RegionFilterForm table = tables.RegionTable class RegionEditView(ObjectEditView): queryset = Region.objects.all() model_form = forms.RegionForm class RegionDeleteView(ObjectDeleteView): queryset = Region.objects.all() class RegionBulkImportView(BulkImportView): queryset = Region.objects.all() model_form = forms.RegionCSVForm table = tables.RegionTable class RegionBulkDeleteView(BulkDeleteView): queryset = Region.objects.add_related_count( Region.objects.all(), Site, 'region', 'site_count', cumulative=True ) filterset = filters.RegionFilterSet table = tables.RegionTable # # Sites # class SiteListView(ObjectListView): queryset = Site.objects.prefetch_related('region', 'tenant') filterset = filters.SiteFilterSet filterset_form = forms.SiteFilterForm table = tables.SiteTable class SiteView(ObjectView): queryset = Site.objects.prefetch_related('region', 'tenant__group') def get(self, request, slug): site = get_object_or_404(self.queryset, slug=slug) stats = { 'rack_count': Rack.objects.restrict(request.user, 'view').filter(site=site).count(), 'device_count': Device.objects.restrict(request.user, 'view').filter(site=site).count(), 'prefix_count': Prefix.objects.restrict(request.user, 'view').filter(site=site).count(), 'vlan_count': VLAN.objects.restrict(request.user, 'view').filter(site=site).count(), 'circuit_count': Circuit.objects.restrict(request.user, 'view').filter(terminations__site=site).count(), 'vm_count': VirtualMachine.objects.restrict(request.user, 'view').filter(cluster__site=site).count(), } rack_groups = RackGroup.objects.restrict(request.user, 'view').filter(site=site).annotate( rack_count=Count('racks') ) show_graphs = Graph.objects.filter(type__model='site').exists() return render(request, 'dcim/site.html', { 'site': site, 'stats': stats, 'rack_groups': rack_groups, 'show_graphs': show_graphs, }) class SiteEditView(ObjectEditView): queryset = Site.objects.all() model_form = forms.SiteForm template_name = 'dcim/site_edit.html' class SiteDeleteView(ObjectDeleteView): queryset = Site.objects.all() class SiteBulkImportView(BulkImportView): queryset = Site.objects.all() model_form = forms.SiteCSVForm table = tables.SiteTable class SiteBulkEditView(BulkEditView): queryset = Site.objects.prefetch_related('region', 'tenant') filterset = filters.SiteFilterSet table = tables.SiteTable form = forms.SiteBulkEditForm class SiteBulkDeleteView(BulkDeleteView): queryset = Site.objects.prefetch_related('region', 'tenant') filterset = filters.SiteFilterSet table = tables.SiteTable # # Rack groups # class RackGroupListView(ObjectListView): queryset = RackGroup.objects.add_related_count( RackGroup.objects.all(), Rack, 'group', 'rack_count', cumulative=True ).prefetch_related('site') filterset = filters.RackGroupFilterSet filterset_form = forms.RackGroupFilterForm table = tables.RackGroupTable class RackGroupEditView(ObjectEditView): queryset = RackGroup.objects.all() model_form = forms.RackGroupForm class RackGroupDeleteView(ObjectDeleteView): queryset = RackGroup.objects.all() class RackGroupBulkImportView(BulkImportView): queryset = RackGroup.objects.all() model_form = forms.RackGroupCSVForm table = tables.RackGroupTable class RackGroupBulkDeleteView(BulkDeleteView): queryset = RackGroup.objects.add_related_count( RackGroup.objects.all(), Rack, 'group', 'rack_count', cumulative=True ).prefetch_related('site') filterset = filters.RackGroupFilterSet table = tables.RackGroupTable # # Rack roles # class RackRoleListView(ObjectListView): queryset = RackRole.objects.annotate(rack_count=Count('racks')).order_by(*RackRole._meta.ordering) table = tables.RackRoleTable class RackRoleEditView(ObjectEditView): queryset = RackRole.objects.all() model_form = forms.RackRoleForm class RackRoleDeleteView(ObjectDeleteView): queryset = RackRole.objects.all() class RackRoleBulkImportView(BulkImportView): queryset = RackRole.objects.all() model_form = forms.RackRoleCSVForm table = tables.RackRoleTable class RackRoleBulkDeleteView(BulkDeleteView): queryset = RackRole.objects.annotate(rack_count=Count('racks')).order_by(*RackRole._meta.ordering) table = tables.RackRoleTable # # Racks # class RackListView(ObjectListView): queryset = Rack.objects.prefetch_related( 'site', 'group', 'tenant', 'role', 'devices__device_type' ).annotate( device_count=Count('devices') ).order_by(*Rack._meta.ordering) filterset = filters.RackFilterSet filterset_form = forms.RackFilterForm table = tables.RackDetailTable class RackElevationListView(ObjectListView): """ Display a set of rack elevations side-by-side. """ queryset = Rack.objects.prefetch_related('role') def get(self, request): racks = filters.RackFilterSet(request.GET, self.queryset).qs total_count = racks.count() # Pagination per_page = request.GET.get('per_page', settings.PAGINATE_COUNT) page_number = request.GET.get('page', 1) paginator = EnhancedPaginator(racks, per_page) try: page = paginator.page(page_number) except PageNotAnInteger: page = paginator.page(1) except EmptyPage: page = paginator.page(paginator.num_pages) # Determine rack face rack_face = request.GET.get('face', DeviceFaceChoices.FACE_FRONT) if rack_face not in DeviceFaceChoices.values(): rack_face = DeviceFaceChoices.FACE_FRONT return render(request, 'dcim/rack_elevation_list.html', { 'paginator': paginator, 'page': page, 'total_count': total_count, 'rack_face': rack_face, 'filter_form': forms.RackElevationFilterForm(request.GET), }) class RackView(ObjectView): queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'group', 'role') def get(self, request, pk): rack = get_object_or_404(self.queryset, pk=pk) # Get 0U and child devices located within the rack nonracked_devices = Device.objects.filter( rack=rack, position__isnull=True ).prefetch_related('device_type__manufacturer') peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=rack.site) if rack.group: peer_racks = peer_racks.filter(group=rack.group) else: peer_racks = peer_racks.filter(group__isnull=True) next_rack = peer_racks.filter(name__gt=rack.name).order_by('name').first() prev_rack = peer_racks.filter(name__lt=rack.name).order_by('-name').first() reservations = RackReservation.objects.restrict(request.user, 'view').filter(rack=rack) power_feeds = PowerFeed.objects.restrict(request.user, 'view').filter(rack=rack).prefetch_related('power_panel') return render(request, 'dcim/rack.html', { 'rack': rack, 'device_count': Device.objects.restrict(request.user, 'view').filter(rack=rack).count(), 'reservations': reservations, 'power_feeds': power_feeds, 'nonracked_devices': nonracked_devices, 'next_rack': next_rack, 'prev_rack': prev_rack, }) class RackEditView(ObjectEditView): queryset = Rack.objects.all() model_form = forms.RackForm template_name = 'dcim/rack_edit.html' class RackDeleteView(ObjectDeleteView): queryset = Rack.objects.all() class RackBulkImportView(BulkImportView): queryset = Rack.objects.all() model_form = forms.RackCSVForm table = tables.RackTable class RackBulkEditView(BulkEditView): queryset = Rack.objects.prefetch_related('site', 'group', 'tenant', 'role') filterset = filters.RackFilterSet table = tables.RackTable form = forms.RackBulkEditForm class RackBulkDeleteView(BulkDeleteView): queryset = Rack.objects.prefetch_related('site', 'group', 'tenant', 'role') filterset = filters.RackFilterSet table = tables.RackTable # # Rack reservations # class RackReservationListView(ObjectListView): queryset = RackReservation.objects.prefetch_related('rack__site') filterset = filters.RackReservationFilterSet filterset_form = forms.RackReservationFilterForm table = tables.RackReservationTable action_buttons = ('export',) class RackReservationView(ObjectView): queryset = RackReservation.objects.prefetch_related('rack') def get(self, request, pk): rackreservation = get_object_or_404(self.queryset, pk=pk) return render(request, 'dcim/rackreservation.html', { 'rackreservation': rackreservation, }) class RackReservationEditView(ObjectEditView): queryset = RackReservation.objects.all() model_form = forms.RackReservationForm template_name = 'dcim/rackreservation_edit.html' def alter_obj(self, obj, request, args, kwargs): if not obj.pk: if 'rack' in request.GET: obj.rack = get_object_or_404(Rack, pk=request.GET.get('rack')) obj.user = request.user return obj class RackReservationDeleteView(ObjectDeleteView): queryset = RackReservation.objects.all() class RackReservationImportView(BulkImportView): queryset = RackReservation.objects.all() model_form = forms.RackReservationCSVForm table = tables.RackReservationTable def _save_obj(self, obj_form, request): """ Assign the currently authenticated user to the RackReservation. """ instance = obj_form.save(commit=False) instance.user = request.user instance.save() return instance class RackReservationBulkEditView(BulkEditView): queryset = RackReservation.objects.prefetch_related('rack', 'user') filterset = filters.RackReservationFilterSet table = tables.RackReservationTable form = forms.RackReservationBulkEditForm class RackReservationBulkDeleteView(BulkDeleteView): queryset = RackReservation.objects.prefetch_related('rack', 'user') filterset = filters.RackReservationFilterSet table = tables.RackReservationTable # # Manufacturers # class ManufacturerListView(ObjectListView): queryset = Manufacturer.objects.annotate( devicetype_count=get_subquery(DeviceType, 'manufacturer'), inventoryitem_count=get_subquery(InventoryItem, 'manufacturer'), platform_count=get_subquery(Platform, 'manufacturer') ) table = tables.ManufacturerTable class ManufacturerEditView(ObjectEditView): queryset = Manufacturer.objects.all() model_form = forms.ManufacturerForm class ManufacturerDeleteView(ObjectDeleteView): queryset = Manufacturer.objects.all() class ManufacturerBulkImportView(BulkImportView): queryset = Manufacturer.objects.all() model_form = forms.ManufacturerCSVForm table = tables.ManufacturerTable class ManufacturerBulkDeleteView(BulkDeleteView): queryset = Manufacturer.objects.annotate( devicetype_count=Count('device_types') ).order_by(*Manufacturer._meta.ordering) table = tables.ManufacturerTable # # Device types # class DeviceTypeListView(ObjectListView): queryset = DeviceType.objects.prefetch_related('manufacturer').annotate( instance_count=Count('instances') ).order_by(*DeviceType._meta.ordering) filterset = filters.DeviceTypeFilterSet filterset_form = forms.DeviceTypeFilterForm table = tables.DeviceTypeTable class DeviceTypeView(ObjectView): queryset = DeviceType.objects.prefetch_related('manufacturer') def get(self, request, pk): devicetype = get_object_or_404(self.queryset, pk=pk) instance_count = Device.objects.restrict(request.user).filter(device_type=devicetype).count() # Component tables consoleport_table = tables.ConsolePortTemplateTable( ConsolePortTemplate.objects.restrict(request.user, 'view').filter(device_type=devicetype), orderable=False ) consoleserverport_table = tables.ConsoleServerPortTemplateTable( ConsoleServerPortTemplate.objects.restrict(request.user, 'view').filter(device_type=devicetype), orderable=False ) powerport_table = tables.PowerPortTemplateTable( PowerPortTemplate.objects.restrict(request.user, 'view').filter(device_type=devicetype), orderable=False ) poweroutlet_table = tables.PowerOutletTemplateTable( PowerOutletTemplate.objects.restrict(request.user, 'view').filter(device_type=devicetype), orderable=False ) interface_table = tables.InterfaceTemplateTable( list(InterfaceTemplate.objects.restrict(request.user, 'view').filter(device_type=devicetype)), orderable=False ) front_port_table = tables.FrontPortTemplateTable( FrontPortTemplate.objects.restrict(request.user, 'view').filter(device_type=devicetype), orderable=False ) rear_port_table = tables.RearPortTemplateTable( RearPortTemplate.objects.restrict(request.user, 'view').filter(device_type=devicetype), orderable=False ) devicebay_table = tables.DeviceBayTemplateTable( DeviceBayTemplate.objects.restrict(request.user, 'view').filter(device_type=devicetype), orderable=False ) if request.user.has_perm('dcim.change_devicetype'): consoleport_table.columns.show('pk') consoleserverport_table.columns.show('pk') powerport_table.columns.show('pk') poweroutlet_table.columns.show('pk') interface_table.columns.show('pk') front_port_table.columns.show('pk') rear_port_table.columns.show('pk') devicebay_table.columns.show('pk') return render(request, 'dcim/devicetype.html', { 'devicetype': devicetype, 'instance_count': instance_count, 'consoleport_table': consoleport_table, 'consoleserverport_table': consoleserverport_table, 'powerport_table': powerport_table, 'poweroutlet_table': poweroutlet_table, 'interface_table': interface_table, 'front_port_table': front_port_table, 'rear_port_table': rear_port_table, 'devicebay_table': devicebay_table, }) class DeviceTypeEditView(ObjectEditView): queryset = DeviceType.objects.all() model_form = forms.DeviceTypeForm template_name = 'dcim/devicetype_edit.html' class DeviceTypeDeleteView(ObjectDeleteView): queryset = DeviceType.objects.all() class DeviceTypeImportView(ObjectImportView): additional_permissions = [ 'dcim.add_devicetype', 'dcim.add_consoleporttemplate', 'dcim.add_consoleserverporttemplate', 'dcim.add_powerporttemplate', 'dcim.add_poweroutlettemplate', 'dcim.add_interfacetemplate', 'dcim.add_frontporttemplate', 'dcim.add_rearporttemplate', 'dcim.add_devicebaytemplate', ] queryset = DeviceType.objects.all() model_form = forms.DeviceTypeImportForm related_object_forms = OrderedDict(( ('console-ports', forms.ConsolePortTemplateImportForm), ('console-server-ports', forms.ConsoleServerPortTemplateImportForm), ('power-ports', forms.PowerPortTemplateImportForm), ('power-outlets', forms.PowerOutletTemplateImportForm), ('interfaces', forms.InterfaceTemplateImportForm), ('rear-ports', forms.RearPortTemplateImportForm), ('front-ports', forms.FrontPortTemplateImportForm), ('device-bays', forms.DeviceBayTemplateImportForm), )) class DeviceTypeBulkEditView(BulkEditView): queryset = DeviceType.objects.prefetch_related('manufacturer').annotate( instance_count=Count('instances') ).order_by(*DeviceType._meta.ordering) filterset = filters.DeviceTypeFilterSet table = tables.DeviceTypeTable form = forms.DeviceTypeBulkEditForm class DeviceTypeBulkDeleteView(BulkDeleteView): queryset = DeviceType.objects.prefetch_related('manufacturer').annotate( instance_count=Count('instances') ).order_by(*DeviceType._meta.ordering) filterset = filters.DeviceTypeFilterSet table = tables.DeviceTypeTable # # Console port templates # class ConsolePortTemplateCreateView(ComponentCreateView): queryset = ConsolePortTemplate.objects.all() form = forms.ConsolePortTemplateCreateForm model_form = forms.ConsolePortTemplateForm template_name = 'dcim/device_component_add.html' class ConsolePortTemplateEditView(ObjectEditView): queryset = ConsolePortTemplate.objects.all() model_form = forms.ConsolePortTemplateForm class ConsolePortTemplateDeleteView(ObjectDeleteView): queryset = ConsolePortTemplate.objects.all() class ConsolePortTemplateBulkEditView(BulkEditView): queryset = ConsolePortTemplate.objects.all() table = tables.ConsolePortTemplateTable form = forms.ConsolePortTemplateBulkEditForm class ConsolePortTemplateBulkRenameView(BulkRenameView): queryset = ConsolePortTemplate.objects.all() class ConsolePortTemplateBulkDeleteView(BulkDeleteView): queryset = ConsolePortTemplate.objects.all() table = tables.ConsolePortTemplateTable # # Console server port templates # class ConsoleServerPortTemplateCreateView(ComponentCreateView): queryset = ConsoleServerPortTemplate.objects.all() form = forms.ConsoleServerPortTemplateCreateForm model_form = forms.ConsoleServerPortTemplateForm template_name = 'dcim/device_component_add.html' class ConsoleServerPortTemplateEditView(ObjectEditView): queryset = ConsoleServerPortTemplate.objects.all() model_form = forms.ConsoleServerPortTemplateForm class ConsoleServerPortTemplateDeleteView(ObjectDeleteView): queryset = ConsoleServerPortTemplate.objects.all() class ConsoleServerPortTemplateBulkEditView(BulkEditView): queryset = ConsoleServerPortTemplate.objects.all() table = tables.ConsoleServerPortTemplateTable form = forms.ConsoleServerPortTemplateBulkEditForm class ConsoleServerPortTemplateBulkRenameView(BulkRenameView): queryset = ConsoleServerPortTemplate.objects.all() class ConsoleServerPortTemplateBulkDeleteView(BulkDeleteView): queryset = ConsoleServerPortTemplate.objects.all() table = tables.ConsoleServerPortTemplateTable # # Power port templates # class PowerPortTemplateCreateView(ComponentCreateView): queryset = PowerPortTemplate.objects.all() form = forms.PowerPortTemplateCreateForm model_form = forms.PowerPortTemplateForm template_name = 'dcim/device_component_add.html' class PowerPortTemplateEditView(ObjectEditView): queryset = PowerPortTemplate.objects.all() model_form = forms.PowerPortTemplateForm class PowerPortTemplateDeleteView(ObjectDeleteView): queryset = PowerPortTemplate.objects.all() class PowerPortTemplateBulkEditView(BulkEditView): queryset = PowerPortTemplate.objects.all() table = tables.PowerPortTemplateTable form = forms.PowerPortTemplateBulkEditForm class PowerPortTemplateBulkRenameView(BulkRenameView): queryset = PowerPortTemplate.objects.all() class PowerPortTemplateBulkDeleteView(BulkDeleteView): queryset = PowerPortTemplate.objects.all() table = tables.PowerPortTemplateTable # # Power outlet templates # class PowerOutletTemplateCreateView(ComponentCreateView): queryset = PowerOutletTemplate.objects.all() form = forms.PowerOutletTemplateCreateForm model_form = forms.PowerOutletTemplateForm template_name = 'dcim/device_component_add.html' class PowerOutletTemplateEditView(ObjectEditView): queryset = PowerOutletTemplate.objects.all() model_form = forms.PowerOutletTemplateForm class PowerOutletTemplateDeleteView(ObjectDeleteView): queryset = PowerOutletTemplate.objects.all() class PowerOutletTemplateBulkEditView(BulkEditView): queryset = PowerOutletTemplate.objects.all() table = tables.PowerOutletTemplateTable form = forms.PowerOutletTemplateBulkEditForm class PowerOutletTemplateBulkRenameView(BulkRenameView): queryset = PowerOutletTemplate.objects.all() class PowerOutletTemplateBulkDeleteView(BulkDeleteView): queryset = PowerOutletTemplate.objects.all() table = tables.PowerOutletTemplateTable # # Interface templates # class InterfaceTemplateCreateView(ComponentCreateView): queryset = InterfaceTemplate.objects.all() form = forms.InterfaceTemplateCreateForm model_form = forms.InterfaceTemplateForm template_name = 'dcim/device_component_add.html' class InterfaceTemplateEditView(ObjectEditView): queryset = InterfaceTemplate.objects.all() model_form = forms.InterfaceTemplateForm class InterfaceTemplateDeleteView(ObjectDeleteView): queryset = InterfaceTemplate.objects.all() class InterfaceTemplateBulkEditView(BulkEditView): queryset = InterfaceTemplate.objects.all() table = tables.InterfaceTemplateTable form = forms.InterfaceTemplateBulkEditForm class InterfaceTemplateBulkRenameView(BulkRenameView): queryset = InterfaceTemplate.objects.all() class InterfaceTemplateBulkDeleteView(BulkDeleteView): queryset = InterfaceTemplate.objects.all() table = tables.InterfaceTemplateTable # # Front port templates # class FrontPortTemplateCreateView(ComponentCreateView): queryset = FrontPortTemplate.objects.all() form = forms.FrontPortTemplateCreateForm model_form = forms.FrontPortTemplateForm template_name = 'dcim/device_component_add.html' class FrontPortTemplateEditView(ObjectEditView): queryset = FrontPortTemplate.objects.all() model_form = forms.FrontPortTemplateForm class FrontPortTemplateDeleteView(ObjectDeleteView): queryset = FrontPortTemplate.objects.all() class FrontPortTemplateBulkEditView(BulkEditView): queryset = FrontPortTemplate.objects.all() table = tables.FrontPortTemplateTable form = forms.FrontPortTemplateBulkEditForm class FrontPortTemplateBulkRenameView(BulkRenameView): queryset = FrontPortTemplate.objects.all() class FrontPortTemplateBulkDeleteView(BulkDeleteView): queryset = FrontPortTemplate.objects.all() table = tables.FrontPortTemplateTable # # Rear port templates # class RearPortTemplateCreateView(ComponentCreateView): queryset = RearPortTemplate.objects.all() form = forms.RearPortTemplateCreateForm model_form = forms.RearPortTemplateForm template_name = 'dcim/device_component_add.html' class RearPortTemplateEditView(ObjectEditView): queryset = RearPortTemplate.objects.all() model_form = forms.RearPortTemplateForm class RearPortTemplateDeleteView(ObjectDeleteView): queryset = RearPortTemplate.objects.all() class RearPortTemplateBulkEditView(BulkEditView): queryset = RearPortTemplate.objects.all() table = tables.RearPortTemplateTable form = forms.RearPortTemplateBulkEditForm class RearPortTemplateBulkRenameView(BulkRenameView): queryset = RearPortTemplate.objects.all() class RearPortTemplateBulkDeleteView(BulkDeleteView): queryset = RearPortTemplate.objects.all() table = tables.RearPortTemplateTable # # Device bay templates # class DeviceBayTemplateCreateView(ComponentCreateView): queryset = DeviceBayTemplate.objects.all() form = forms.DeviceBayTemplateCreateForm model_form = forms.DeviceBayTemplateForm template_name = 'dcim/device_component_add.html' class DeviceBayTemplateEditView(ObjectEditView): queryset = DeviceBayTemplate.objects.all() model_form = forms.DeviceBayTemplateForm class DeviceBayTemplateDeleteView(ObjectDeleteView): queryset = DeviceBayTemplate.objects.all() class DeviceBayTemplateBulkEditView(BulkEditView): queryset = DeviceBayTemplate.objects.all() table = tables.DeviceBayTemplateTable form = forms.DeviceBayTemplateBulkEditForm class DeviceBayTemplateBulkRenameView(BulkRenameView): queryset = DeviceBayTemplate.objects.all() class DeviceBayTemplateBulkDeleteView(BulkDeleteView): queryset = DeviceBayTemplate.objects.all() table = tables.DeviceBayTemplateTable # # Device roles # class DeviceRoleListView(ObjectListView): queryset = DeviceRole.objects.annotate( device_count=get_subquery(Device, 'device_role'), vm_count=get_subquery(VirtualMachine, 'role') ) table = tables.DeviceRoleTable class DeviceRoleEditView(ObjectEditView): queryset = DeviceRole.objects.all() model_form = forms.DeviceRoleForm class DeviceRoleDeleteView(ObjectDeleteView): queryset = DeviceRole.objects.all() class DeviceRoleBulkImportView(BulkImportView): queryset = DeviceRole.objects.all() model_form = forms.DeviceRoleCSVForm table = tables.DeviceRoleTable class DeviceRoleBulkDeleteView(BulkDeleteView): queryset = DeviceRole.objects.all() table = tables.DeviceRoleTable # # Platforms # class PlatformListView(ObjectListView): queryset = Platform.objects.annotate( device_count=get_subquery(Device, 'platform'), vm_count=get_subquery(VirtualMachine, 'platform') ) table = tables.PlatformTable class PlatformEditView(ObjectEditView): queryset = Platform.objects.all() model_form = forms.PlatformForm class PlatformDeleteView(ObjectDeleteView): queryset = Platform.objects.all() class PlatformBulkImportView(BulkImportView): queryset = Platform.objects.all() model_form = forms.PlatformCSVForm table = tables.PlatformTable class PlatformBulkDeleteView(BulkDeleteView): queryset = Platform.objects.all() table = tables.PlatformTable # # Devices # class DeviceListView(ObjectListView): queryset = Device.objects.prefetch_related( 'device_type__manufacturer', 'device_role', 'tenant', 'site', 'rack', 'primary_ip4', 'primary_ip6' ) filterset = filters.DeviceFilterSet filterset_form = forms.DeviceFilterForm table = tables.DeviceTable template_name = 'dcim/device_list.html' class DeviceView(ObjectView): queryset = Device.objects.prefetch_related( 'site__region', 'rack__group', 'tenant__group', 'device_role', 'platform', 'primary_ip4', 'primary_ip6' ) def get(self, request, pk): device = get_object_or_404(self.queryset, pk=pk) # VirtualChassis members if device.virtual_chassis is not None: vc_members = Device.objects.restrict(request.user, 'view').filter( virtual_chassis=device.virtual_chassis ).order_by('vc_position') else: vc_members = [] # Console ports consoleports = ConsolePort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related( 'connected_endpoint__device', 'cable', ) # Console server ports consoleserverports = ConsoleServerPort.objects.restrict(request.user, 'view').filter( device=device ).prefetch_related( 'connected_endpoint__device', 'cable', ) # Power ports powerports = PowerPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related( '_connected_poweroutlet__device', 'cable', ) # Power outlets poweroutlets = PowerOutlet.objects.restrict(request.user, 'view').filter(device=device).prefetch_related( 'connected_endpoint__device', 'cable', 'power_port', ) # Interfaces interfaces = device.vc_interfaces.restrict(request.user, 'view').filter(device=device).prefetch_related( Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)), Prefetch('member_interfaces', queryset=Interface.objects.restrict(request.user)), 'lag', '_connected_interface__device', '_connected_circuittermination__circuit', 'cable', 'cable__termination_a', 'cable__termination_b', 'tags' ) # Front ports frontports = FrontPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related( 'rear_port', 'cable', ) # Rear ports rearports = RearPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related('cable') # Device bays devicebays = DeviceBay.objects.restrict(request.user, 'view').filter(device=device).prefetch_related( 'installed_device__device_type__manufacturer', ) # Services services = Service.objects.restrict(request.user, 'view').filter(device=device) # Secrets secrets = Secret.objects.restrict(request.user, 'view').filter(device=device) # Find up to ten devices in the same site with the same functional role for quick reference. related_devices = Device.objects.restrict(request.user, 'view').filter( site=device.site, device_role=device.device_role ).exclude( pk=device.pk ).prefetch_related( 'rack', 'device_type__manufacturer' )[:10] return render(request, 'dcim/device.html', { 'device': device, 'consoleports': consoleports, 'consoleserverports': consoleserverports, 'powerports': powerports, 'poweroutlets': poweroutlets, 'interfaces': interfaces, 'devicebays': devicebays, 'frontports': frontports, 'rearports': rearports, 'services': services, 'secrets': secrets, 'vc_members': vc_members, 'related_devices': related_devices, 'show_graphs': Graph.objects.filter(type__model='device').exists(), 'show_interface_graphs': Graph.objects.filter(type__model='interface').exists(), }) class DeviceInventoryView(ObjectView): queryset = Device.objects.all() def get(self, request, pk): device = get_object_or_404(self.queryset, pk=pk) inventory_items = InventoryItem.objects.restrict(request.user, 'view').filter( device=device, parent=None ).prefetch_related( 'manufacturer', 'child_items' ) return render(request, 'dcim/device_inventory.html', { 'device': device, 'inventory_items': inventory_items, 'active_tab': 'inventory', }) class DeviceStatusView(ObjectView): additional_permissions = ['dcim.napalm_read_device'] queryset = Device.objects.all() def get(self, request, pk): device = get_object_or_404(self.queryset, pk=pk) return render(request, 'dcim/device_status.html', { 'device': device, 'active_tab': 'status', }) class DeviceLLDPNeighborsView(ObjectView): additional_permissions = ['dcim.napalm_read_device'] queryset = Device.objects.all() def get(self, request, pk): device = get_object_or_404(self.queryset, pk=pk) interfaces = device.vc_interfaces.restrict(request.user, 'view').exclude( type__in=NONCONNECTABLE_IFACE_TYPES ).prefetch_related( '_connected_interface__device' ) return render(request, 'dcim/device_lldp_neighbors.html', { 'device': device, 'interfaces': interfaces, 'active_tab': 'lldp-neighbors', }) class DeviceConfigView(ObjectView): additional_permissions = ['dcim.napalm_read_device'] queryset = Device.objects.all() def get(self, request, pk): device = get_object_or_404(self.queryset, pk=pk) return render(request, 'dcim/device_config.html', { 'device': device, 'active_tab': 'config', }) class DeviceConfigContextView(ObjectConfigContextView): queryset = Device.objects.all() base_template = 'dcim/device.html' class DeviceEditView(ObjectEditView): queryset = Device.objects.all() model_form = forms.DeviceForm template_name = 'dcim/device_edit.html' class DeviceDeleteView(ObjectDeleteView): queryset = Device.objects.all() class DeviceBulkImportView(BulkImportView): queryset = Device.objects.all() model_form = forms.DeviceCSVForm table = tables.DeviceImportTable template_name = 'dcim/device_import.html' class ChildDeviceBulkImportView(BulkImportView): queryset = Device.objects.all() model_form = forms.ChildDeviceCSVForm table = tables.DeviceImportTable template_name = 'dcim/device_import_child.html' def _save_obj(self, obj_form, request): obj = obj_form.save() # Save the reverse relation to the parent device bay device_bay = obj.parent_bay device_bay.installed_device = obj device_bay.save() return obj class DeviceBulkEditView(BulkEditView): queryset = Device.objects.prefetch_related('tenant', 'site', 'rack', 'device_role', 'device_type__manufacturer') filterset = filters.DeviceFilterSet table = tables.DeviceTable form = forms.DeviceBulkEditForm class DeviceBulkDeleteView(BulkDeleteView): queryset = Device.objects.prefetch_related('tenant', 'site', 'rack', 'device_role', 'device_type__manufacturer') filterset = filters.DeviceFilterSet table = tables.DeviceTable # # Console ports # class ConsolePortListView(ObjectListView): queryset = ConsolePort.objects.prefetch_related('device', 'cable') filterset = filters.ConsolePortFilterSet filterset_form = forms.ConsolePortFilterForm table = tables.ConsolePortTable action_buttons = ('import', 'export') class ConsolePortView(ObjectView): queryset = ConsolePort.objects.all() class ConsolePortCreateView(ComponentCreateView): queryset = ConsolePort.objects.all() form = forms.ConsolePortCreateForm model_form = forms.ConsolePortForm template_name = 'dcim/device_component_add.html' class ConsolePortEditView(ObjectEditView): queryset = ConsolePort.objects.all() model_form = forms.ConsolePortForm class ConsolePortDeleteView(ObjectDeleteView): queryset = ConsolePort.objects.all() class ConsolePortBulkImportView(BulkImportView): queryset = ConsolePort.objects.all() model_form = forms.ConsolePortCSVForm table = tables.ConsolePortTable class ConsolePortBulkEditView(BulkEditView): queryset = ConsolePort.objects.all() filterset = filters.ConsolePortFilterSet table = tables.ConsolePortTable form = forms.ConsolePortBulkEditForm class ConsolePortBulkRenameView(BulkRenameView): queryset = ConsolePort.objects.all() class ConsolePortBulkDisconnectView(BulkDisconnectView): queryset = ConsolePort.objects.all() class ConsolePortBulkDeleteView(BulkDeleteView): queryset = ConsolePort.objects.all() filterset = filters.ConsolePortFilterSet table = tables.ConsolePortTable # # Console server ports # class ConsoleServerPortListView(ObjectListView): queryset = ConsoleServerPort.objects.prefetch_related('device', 'cable') filterset = filters.ConsoleServerPortFilterSet filterset_form = forms.ConsoleServerPortFilterForm table = tables.ConsoleServerPortTable action_buttons = ('import', 'export') class ConsoleServerPortView(ObjectView): queryset = ConsoleServerPort.objects.all() class ConsoleServerPortCreateView(ComponentCreateView): queryset = ConsoleServerPort.objects.all() form = forms.ConsoleServerPortCreateForm model_form = forms.ConsoleServerPortForm template_name = 'dcim/device_component_add.html' class ConsoleServerPortEditView(ObjectEditView): queryset = ConsoleServerPort.objects.all() model_form = forms.ConsoleServerPortForm class ConsoleServerPortDeleteView(ObjectDeleteView): queryset = ConsoleServerPort.objects.all() class ConsoleServerPortBulkImportView(BulkImportView): queryset = ConsoleServerPort.objects.all() model_form = forms.ConsoleServerPortCSVForm table = tables.ConsoleServerPortTable class ConsoleServerPortBulkEditView(BulkEditView): queryset = ConsoleServerPort.objects.all() filterset = filters.ConsoleServerPortFilterSet table = tables.ConsoleServerPortTable form = forms.ConsoleServerPortBulkEditForm class ConsoleServerPortBulkRenameView(BulkRenameView): queryset = ConsoleServerPort.objects.all() class ConsoleServerPortBulkDisconnectView(BulkDisconnectView): queryset = ConsoleServerPort.objects.all() class ConsoleServerPortBulkDeleteView(BulkDeleteView): queryset = ConsoleServerPort.objects.all() filterset = filters.ConsoleServerPortFilterSet table = tables.ConsoleServerPortTable # # Power ports # class PowerPortListView(ObjectListView): queryset = PowerPort.objects.prefetch_related('device', 'cable') filterset = filters.PowerPortFilterSet filterset_form = forms.PowerPortFilterForm table = tables.PowerPortTable action_buttons = ('import', 'export') class PowerPortView(ObjectView): queryset = PowerPort.objects.all() class PowerPortCreateView(ComponentCreateView): queryset = PowerPort.objects.all() form = forms.PowerPortCreateForm model_form = forms.PowerPortForm template_name = 'dcim/device_component_add.html' class PowerPortEditView(ObjectEditView): queryset = PowerPort.objects.all() model_form = forms.PowerPortForm class PowerPortDeleteView(ObjectDeleteView): queryset = PowerPort.objects.all() class PowerPortBulkImportView(BulkImportView): queryset = PowerPort.objects.all() model_form = forms.PowerPortCSVForm table = tables.PowerPortTable class PowerPortBulkEditView(BulkEditView): queryset = PowerPort.objects.all() filterset = filters.PowerPortFilterSet table = tables.PowerPortTable form = forms.PowerPortBulkEditForm class PowerPortBulkRenameView(BulkRenameView): queryset = PowerPort.objects.all() class PowerPortBulkDisconnectView(BulkDisconnectView): queryset = PowerPort.objects.all() class PowerPortBulkDeleteView(BulkDeleteView): queryset = PowerPort.objects.all() filterset = filters.PowerPortFilterSet table = tables.PowerPortTable # # Power outlets # class PowerOutletListView(ObjectListView): queryset = PowerOutlet.objects.prefetch_related('device', 'cable') filterset = filters.PowerOutletFilterSet filterset_form = forms.PowerOutletFilterForm table = tables.PowerOutletTable action_buttons = ('import', 'export') class PowerOutletView(ObjectView): queryset = PowerOutlet.objects.all() class PowerOutletCreateView(ComponentCreateView): queryset = PowerOutlet.objects.all() form = forms.PowerOutletCreateForm model_form = forms.PowerOutletForm template_name = 'dcim/device_component_add.html' class PowerOutletEditView(ObjectEditView): queryset = PowerOutlet.objects.all() model_form = forms.PowerOutletForm class PowerOutletDeleteView(ObjectDeleteView): queryset = PowerOutlet.objects.all() class PowerOutletBulkImportView(BulkImportView): queryset = PowerOutlet.objects.all() model_form = forms.PowerOutletCSVForm table = tables.PowerOutletTable class PowerOutletBulkEditView(BulkEditView): queryset = PowerOutlet.objects.all() filterset = filters.PowerOutletFilterSet table = tables.PowerOutletTable form = forms.PowerOutletBulkEditForm class PowerOutletBulkRenameView(BulkRenameView): queryset = PowerOutlet.objects.all() class PowerOutletBulkDisconnectView(BulkDisconnectView): queryset = PowerOutlet.objects.all() class PowerOutletBulkDeleteView(BulkDeleteView): queryset = PowerOutlet.objects.all() filterset = filters.PowerOutletFilterSet table = tables.PowerOutletTable # # Interfaces # class InterfaceListView(ObjectListView): queryset = Interface.objects.prefetch_related('device', 'cable') filterset = filters.InterfaceFilterSet filterset_form = forms.InterfaceFilterForm table = tables.InterfaceTable action_buttons = ('import', 'export') class InterfaceView(ObjectView): queryset = Interface.objects.all() def get(self, request, pk): interface = get_object_or_404(self.queryset, pk=pk) # Get assigned IP addresses ipaddress_table = InterfaceIPAddressTable( data=interface.ip_addresses.restrict(request.user, 'view').prefetch_related('vrf', 'tenant'), orderable=False ) # Get assigned VLANs and annotate whether each is tagged or untagged vlans = [] if interface.untagged_vlan is not None: vlans.append(interface.untagged_vlan) vlans[0].tagged = False for vlan in interface.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'): vlan.tagged = True vlans.append(vlan) vlan_table = InterfaceVLANTable( interface=interface, data=vlans, orderable=False ) return render(request, 'dcim/interface.html', { 'instance': interface, 'connected_interface': interface._connected_interface, 'connected_circuittermination': interface._connected_circuittermination, 'ipaddress_table': ipaddress_table, 'vlan_table': vlan_table, }) class InterfaceCreateView(ComponentCreateView): queryset = Interface.objects.all() form = forms.InterfaceCreateForm model_form = forms.InterfaceForm template_name = 'dcim/device_component_add.html' class InterfaceEditView(ObjectEditView): queryset = Interface.objects.all() model_form = forms.InterfaceForm template_name = 'dcim/interface_edit.html' class InterfaceDeleteView(ObjectDeleteView): queryset = Interface.objects.all() class InterfaceBulkImportView(BulkImportView): queryset = Interface.objects.all() model_form = forms.InterfaceCSVForm table = tables.InterfaceTable class InterfaceBulkEditView(BulkEditView): queryset = Interface.objects.all() filterset = filters.InterfaceFilterSet table = tables.InterfaceTable form = forms.InterfaceBulkEditForm class InterfaceBulkRenameView(BulkRenameView): queryset = Interface.objects.all() class InterfaceBulkDisconnectView(BulkDisconnectView): queryset = Interface.objects.all() class InterfaceBulkDeleteView(BulkDeleteView): queryset = Interface.objects.all() filterset = filters.InterfaceFilterSet table = tables.InterfaceTable # # Front ports # class FrontPortListView(ObjectListView): queryset = FrontPort.objects.prefetch_related('device', 'cable') filterset = filters.FrontPortFilterSet filterset_form = forms.FrontPortFilterForm table = tables.FrontPortTable action_buttons = ('import', 'export') class FrontPortView(ObjectView): queryset = FrontPort.objects.all() class FrontPortCreateView(ComponentCreateView): queryset = FrontPort.objects.all() form = forms.FrontPortCreateForm model_form = forms.FrontPortForm template_name = 'dcim/device_component_add.html' class FrontPortEditView(ObjectEditView): queryset = FrontPort.objects.all() model_form = forms.FrontPortForm class FrontPortDeleteView(ObjectDeleteView): queryset = FrontPort.objects.all() class FrontPortBulkImportView(BulkImportView): queryset = FrontPort.objects.all() model_form = forms.FrontPortCSVForm table = tables.FrontPortTable class FrontPortBulkEditView(BulkEditView): queryset = FrontPort.objects.all() filterset = filters.FrontPortFilterSet table = tables.FrontPortTable form = forms.FrontPortBulkEditForm class FrontPortBulkRenameView(BulkRenameView): queryset = FrontPort.objects.all() class FrontPortBulkDisconnectView(BulkDisconnectView): queryset = FrontPort.objects.all() class FrontPortBulkDeleteView(BulkDeleteView): queryset = FrontPort.objects.all() filterset = filters.FrontPortFilterSet table = tables.FrontPortTable # # Rear ports # class RearPortListView(ObjectListView): queryset = RearPort.objects.prefetch_related('device', 'cable') filterset = filters.RearPortFilterSet filterset_form = forms.RearPortFilterForm table = tables.RearPortTable action_buttons = ('import', 'export') class RearPortView(ObjectView): queryset = RearPort.objects.all() class RearPortCreateView(ComponentCreateView): queryset = RearPort.objects.all() form = forms.RearPortCreateForm model_form = forms.RearPortForm template_name = 'dcim/device_component_add.html' class RearPortEditView(ObjectEditView): queryset = RearPort.objects.all() model_form = forms.RearPortForm class RearPortDeleteView(ObjectDeleteView): queryset = RearPort.objects.all() class RearPortBulkImportView(BulkImportView): queryset = RearPort.objects.all() model_form = forms.RearPortCSVForm table = tables.RearPortTable class RearPortBulkEditView(BulkEditView): queryset = RearPort.objects.all() filterset = filters.RearPortFilterSet table = tables.RearPortTable form = forms.RearPortBulkEditForm class RearPortBulkRenameView(BulkRenameView): queryset = RearPort.objects.all() class RearPortBulkDisconnectView(BulkDisconnectView): queryset = RearPort.objects.all() class RearPortBulkDeleteView(BulkDeleteView): queryset = RearPort.objects.all() filterset = filters.RearPortFilterSet table = tables.RearPortTable # # Device bays # class DeviceBayListView(ObjectListView): queryset = DeviceBay.objects.prefetch_related('device', 'installed_device') filterset = filters.DeviceBayFilterSet filterset_form = forms.DeviceBayFilterForm table = tables.DeviceBayTable action_buttons = ('import', 'export') class DeviceBayView(ObjectView): queryset = DeviceBay.objects.all() class DeviceBayCreateView(ComponentCreateView): queryset = DeviceBay.objects.all() form = forms.DeviceBayCreateForm model_form = forms.DeviceBayForm template_name = 'dcim/device_component_add.html' class DeviceBayEditView(ObjectEditView): queryset = DeviceBay.objects.all() model_form = forms.DeviceBayForm class DeviceBayDeleteView(ObjectDeleteView): queryset = DeviceBay.objects.all() class DeviceBayPopulateView(ObjectEditView): queryset = DeviceBay.objects.all() def get(self, request, pk): device_bay = get_object_or_404(self.queryset, pk=pk) form = forms.PopulateDeviceBayForm(device_bay) return render(request, 'dcim/devicebay_populate.html', { 'device_bay': device_bay, 'form': form, 'return_url': reverse('dcim:device', kwargs={'pk': device_bay.device.pk}), }) def post(self, request, pk): device_bay = get_object_or_404(self.queryset, pk=pk) form = forms.PopulateDeviceBayForm(device_bay, request.POST) if form.is_valid(): device_bay.installed_device = form.cleaned_data['installed_device'] device_bay.save() messages.success(request, "Added {} to {}.".format(device_bay.installed_device, device_bay)) return redirect('dcim:device', pk=device_bay.device.pk) return render(request, 'dcim/devicebay_populate.html', { 'device_bay': device_bay, 'form': form, 'return_url': reverse('dcim:device', kwargs={'pk': device_bay.device.pk}), }) class DeviceBayDepopulateView(ObjectEditView): queryset = DeviceBay.objects.all() def get(self, request, pk): device_bay = get_object_or_404(self.queryset, pk=pk) form = ConfirmationForm() return render(request, 'dcim/devicebay_depopulate.html', { 'device_bay': device_bay, 'form': form, 'return_url': reverse('dcim:device', kwargs={'pk': device_bay.device.pk}), }) def post(self, request, pk): device_bay = get_object_or_404(self.queryset, pk=pk) form = ConfirmationForm(request.POST) if form.is_valid(): removed_device = device_bay.installed_device device_bay.installed_device = None device_bay.save() messages.success(request, "{} has been removed from {}.".format(removed_device, device_bay)) return redirect('dcim:device', pk=device_bay.device.pk) return render(request, 'dcim/devicebay_depopulate.html', { 'device_bay': device_bay, 'form': form, 'return_url': reverse('dcim:device', kwargs={'pk': device_bay.device.pk}), }) class DeviceBayBulkImportView(BulkImportView): queryset = DeviceBay.objects.all() model_form = forms.DeviceBayCSVForm table = tables.DeviceBayTable class DeviceBayBulkEditView(BulkEditView): queryset = DeviceBay.objects.all() filterset = filters.DeviceBayFilterSet table = tables.DeviceBayTable form = forms.DeviceBayBulkEditForm class DeviceBayBulkRenameView(BulkRenameView): queryset = DeviceBay.objects.all() class DeviceBayBulkDeleteView(BulkDeleteView): queryset = DeviceBay.objects.all() filterset = filters.DeviceBayFilterSet table = tables.DeviceBayTable # # Inventory items # class InventoryItemListView(ObjectListView): queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer') filterset = filters.InventoryItemFilterSet filterset_form = forms.InventoryItemFilterForm table = tables.InventoryItemTable action_buttons = ('import', 'export') class InventoryItemView(ObjectView): queryset = InventoryItem.objects.all() class InventoryItemEditView(ObjectEditView): queryset = InventoryItem.objects.all() model_form = forms.InventoryItemForm class InventoryItemCreateView(ComponentCreateView): queryset = InventoryItem.objects.all() form = forms.InventoryItemCreateForm model_form = forms.InventoryItemForm template_name = 'dcim/device_component_add.html' class InventoryItemDeleteView(ObjectDeleteView): queryset = InventoryItem.objects.all() class InventoryItemBulkImportView(BulkImportView): queryset = InventoryItem.objects.all() model_form = forms.InventoryItemCSVForm table = tables.InventoryItemTable class InventoryItemBulkEditView(BulkEditView): queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer') filterset = filters.InventoryItemFilterSet table = tables.InventoryItemTable form = forms.InventoryItemBulkEditForm class InventoryItemBulkRenameView(BulkRenameView): queryset = InventoryItem.objects.all() class InventoryItemBulkDeleteView(BulkDeleteView): queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer') table = tables.InventoryItemTable template_name = 'dcim/inventoryitem_bulk_delete.html' # # Bulk Device component creation # class DeviceBulkAddConsolePortView(BulkComponentCreateView): parent_model = Device parent_field = 'device' form = forms.ConsolePortBulkCreateForm queryset = ConsolePort.objects.all() model_form = forms.ConsolePortForm filterset = filters.DeviceFilterSet table = tables.DeviceTable default_return_url = 'dcim:device_list' class DeviceBulkAddConsoleServerPortView(BulkComponentCreateView): parent_model = Device parent_field = 'device' form = forms.ConsoleServerPortBulkCreateForm queryset = ConsoleServerPort.objects.all() model_form = forms.ConsoleServerPortForm filterset = filters.DeviceFilterSet table = tables.DeviceTable default_return_url = 'dcim:device_list' class DeviceBulkAddPowerPortView(BulkComponentCreateView): parent_model = Device parent_field = 'device' form = forms.PowerPortBulkCreateForm queryset = PowerPort.objects.all() model_form = forms.PowerPortForm filterset = filters.DeviceFilterSet table = tables.DeviceTable default_return_url = 'dcim:device_list' class DeviceBulkAddPowerOutletView(BulkComponentCreateView): parent_model = Device parent_field = 'device' form = forms.PowerOutletBulkCreateForm queryset = PowerOutlet.objects.all() model_form = forms.PowerOutletForm filterset = filters.DeviceFilterSet table = tables.DeviceTable default_return_url = 'dcim:device_list' class DeviceBulkAddInterfaceView(BulkComponentCreateView): parent_model = Device parent_field = 'device' form = forms.InterfaceBulkCreateForm queryset = Interface.objects.all() model_form = forms.InterfaceForm filterset = filters.DeviceFilterSet table = tables.DeviceTable default_return_url = 'dcim:device_list' # class DeviceBulkAddFrontPortView(BulkComponentCreateView): # parent_model = Device # parent_field = 'device' # form = forms.FrontPortBulkCreateForm # queryset = FrontPort.objects.all() # model_form = forms.FrontPortForm # filterset = filters.DeviceFilterSet # table = tables.DeviceTable # default_return_url = 'dcim:device_list' class DeviceBulkAddRearPortView(BulkComponentCreateView): parent_model = Device parent_field = 'device' form = forms.RearPortBulkCreateForm queryset = RearPort.objects.all() model_form = forms.RearPortForm filterset = filters.DeviceFilterSet table = tables.DeviceTable default_return_url = 'dcim:device_list' class DeviceBulkAddDeviceBayView(BulkComponentCreateView): parent_model = Device parent_field = 'device' form = forms.DeviceBayBulkCreateForm queryset = DeviceBay.objects.all() model_form = forms.DeviceBayForm filterset = filters.DeviceFilterSet table = tables.DeviceTable default_return_url = 'dcim:device_list' class DeviceBulkAddInventoryItemView(BulkComponentCreateView): parent_model = Device parent_field = 'device' form = forms.InventoryItemBulkCreateForm queryset = InventoryItem.objects.all() model_form = forms.InventoryItemForm filterset = filters.DeviceFilterSet table = tables.DeviceTable default_return_url = 'dcim:device_list' # # Cables # class CableListView(ObjectListView): queryset = Cable.objects.prefetch_related( 'termination_a', 'termination_b' ) filterset = filters.CableFilterSet filterset_form = forms.CableFilterForm table = tables.CableTable action_buttons = ('import', 'export') class CableView(ObjectView): queryset = Cable.objects.all() def get(self, request, pk): cable = get_object_or_404(self.queryset, pk=pk) return render(request, 'dcim/cable.html', { 'cable': cable, }) class CableTraceView(ObjectView): """ Trace a cable path beginning from the given termination. """ additional_permissions = ['dcim.view_cable'] def dispatch(self, request, *args, **kwargs): model = kwargs.pop('model') self.queryset = model.objects.all() return super().dispatch(request, *args, **kwargs) def get(self, request, pk): obj = get_object_or_404(self.queryset, pk=pk) path, split_ends, position_stack = obj.trace() total_length = sum( [entry[1]._abs_length for entry in path if entry[1] and entry[1]._abs_length] ) return render(request, 'dcim/cable_trace.html', { 'obj': obj, 'trace': path, 'split_ends': split_ends, 'position_stack': position_stack, 'total_length': total_length, }) class CableCreateView(ObjectEditView): queryset = Cable.objects.all() template_name = 'dcim/cable_connect.html' def dispatch(self, request, *args, **kwargs): # Set the model_form class based on the type of component being connected self.model_form = { 'console-port': forms.ConnectCableToConsolePortForm, 'console-server-port': forms.ConnectCableToConsoleServerPortForm, 'power-port': forms.ConnectCableToPowerPortForm, 'power-outlet': forms.ConnectCableToPowerOutletForm, 'interface': forms.ConnectCableToInterfaceForm, 'front-port': forms.ConnectCableToFrontPortForm, 'rear-port': forms.ConnectCableToRearPortForm, 'power-feed': forms.ConnectCableToPowerFeedForm, 'circuit-termination': forms.ConnectCableToCircuitTerminationForm, }[kwargs.get('termination_b_type')] return super().dispatch(request, *args, **kwargs) def alter_obj(self, obj, request, url_args, url_kwargs): termination_a_type = url_kwargs.get('termination_a_type') termination_a_id = url_kwargs.get('termination_a_id') termination_b_type_name = url_kwargs.get('termination_b_type') self.termination_b_type = ContentType.objects.get(model=termination_b_type_name.replace('-', '')) # Initialize Cable termination attributes obj.termination_a = termination_a_type.objects.get(pk=termination_a_id) obj.termination_b_type = self.termination_b_type return obj def get(self, request, *args, **kwargs): obj = self.alter_obj(self.get_object(kwargs), request, args, kwargs) # Parse initial data manually to avoid setting field values as lists initial_data = {k: request.GET[k] for k in request.GET} # Set initial site and rack based on side A termination (if not already set) if 'termination_b_site' not in initial_data: initial_data['termination_b_site'] = getattr(obj.termination_a.parent, 'site', None) if 'termination_b_rack' not in initial_data: initial_data['termination_b_rack'] = getattr(obj.termination_a.parent, 'rack', None) form = self.model_form(instance=obj, initial=initial_data) return render(request, self.template_name, { 'obj': obj, 'obj_type': Cable._meta.verbose_name, 'termination_b_type': self.termination_b_type.name, 'form': form, 'return_url': self.get_return_url(request, obj), }) class CableEditView(ObjectEditView): queryset = Cable.objects.all() model_form = forms.CableForm template_name = 'dcim/cable_edit.html' class CableDeleteView(ObjectDeleteView): queryset = Cable.objects.all() class CableBulkImportView(BulkImportView): queryset = Cable.objects.all() model_form = forms.CableCSVForm table = tables.CableTable class CableBulkEditView(BulkEditView): queryset = Cable.objects.prefetch_related('termination_a', 'termination_b') filterset = filters.CableFilterSet table = tables.CableTable form = forms.CableBulkEditForm class CableBulkDeleteView(BulkDeleteView): queryset = Cable.objects.prefetch_related('termination_a', 'termination_b') filterset = filters.CableFilterSet table = tables.CableTable # # Connections # class ConsoleConnectionsListView(ObjectListView): queryset = ConsolePort.objects.prefetch_related( 'device', 'connected_endpoint__device' ).filter( connected_endpoint__isnull=False ).order_by( 'cable', 'connected_endpoint__device__name', 'connected_endpoint__name' ) filterset = filters.ConsoleConnectionFilterSet filterset_form = forms.ConsoleConnectionFilterForm table = tables.ConsoleConnectionTable template_name = 'dcim/console_connections_list.html' def queryset_to_csv(self): csv_data = [ # Headers ','.join(['console_server', 'port', 'device', 'console_port', 'connection_status']) ] for obj in self.queryset: csv = csv_format([ obj.connected_endpoint.device.identifier if obj.connected_endpoint else None, obj.connected_endpoint.name if obj.connected_endpoint else None, obj.device.identifier, obj.name, obj.get_connection_status_display(), ]) csv_data.append(csv) return '\n'.join(csv_data) class PowerConnectionsListView(ObjectListView): queryset = PowerPort.objects.prefetch_related( 'device', '_connected_poweroutlet__device' ).filter( _connected_poweroutlet__isnull=False ).order_by( 'cable', '_connected_poweroutlet__device__name', '_connected_poweroutlet__name' ) filterset = filters.PowerConnectionFilterSet filterset_form = forms.PowerConnectionFilterForm table = tables.PowerConnectionTable template_name = 'dcim/power_connections_list.html' def queryset_to_csv(self): csv_data = [ # Headers ','.join(['pdu', 'outlet', 'device', 'power_port', 'connection_status']) ] for obj in self.queryset: csv = csv_format([ obj.connected_endpoint.device.identifier if obj.connected_endpoint else None, obj.connected_endpoint.name if obj.connected_endpoint else None, obj.device.identifier, obj.name, obj.get_connection_status_display(), ]) csv_data.append(csv) return '\n'.join(csv_data) class InterfaceConnectionsListView(ObjectListView): queryset = Interface.objects.prefetch_related( 'device', 'cable', '_connected_interface__device' ).filter( # Avoid duplicate connections by only selecting the lower PK in a connected pair _connected_interface__isnull=False, pk__lt=F('_connected_interface') ).order_by( 'device' ) filterset = filters.InterfaceConnectionFilterSet filterset_form = forms.InterfaceConnectionFilterForm table = tables.InterfaceConnectionTable template_name = 'dcim/interface_connections_list.html' def queryset_to_csv(self): csv_data = [ # Headers ','.join([ 'device_a', 'interface_a', 'device_b', 'interface_b', 'connection_status' ]) ] for obj in self.queryset: csv = csv_format([ obj.connected_endpoint.device.identifier if obj.connected_endpoint else None, obj.connected_endpoint.name if obj.connected_endpoint else None, obj.device.identifier, obj.name, obj.get_connection_status_display(), ]) csv_data.append(csv) return '\n'.join(csv_data) # # Virtual chassis # class VirtualChassisListView(ObjectListView): queryset = VirtualChassis.objects.prefetch_related('master').annotate( member_count=Count('members', distinct=True) ).order_by(*VirtualChassis._meta.ordering) table = tables.VirtualChassisTable filterset = filters.VirtualChassisFilterSet filterset_form = forms.VirtualChassisFilterForm class VirtualChassisView(ObjectView): queryset = VirtualChassis.objects.all() def get(self, request, pk): virtualchassis = get_object_or_404(self.queryset, pk=pk) members = Device.objects.restrict(request.user).filter(virtual_chassis=virtualchassis) return render(request, 'dcim/virtualchassis.html', { 'virtualchassis': virtualchassis, 'members': members, }) class VirtualChassisCreateView(ObjectEditView): queryset = VirtualChassis.objects.all() model_form = forms.VirtualChassisCreateForm template_name = 'dcim/virtualchassis_add.html' class VirtualChassisEditView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View): queryset = VirtualChassis.objects.all() def get_required_permission(self): return 'dcim.change_virtualchassis' def get(self, request, pk): virtual_chassis = get_object_or_404(self.queryset, pk=pk) VCMemberFormSet = modelformset_factory( model=Device, form=forms.DeviceVCMembershipForm, formset=forms.BaseVCMemberFormSet, extra=0 ) members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position') vc_form = forms.VirtualChassisForm(instance=virtual_chassis) vc_form.fields['master'].queryset = members_queryset formset = VCMemberFormSet(queryset=members_queryset) return render(request, 'dcim/virtualchassis_edit.html', { 'vc_form': vc_form, 'formset': formset, 'return_url': self.get_return_url(request, virtual_chassis), }) def post(self, request, pk): virtual_chassis = get_object_or_404(self.queryset, pk=pk) VCMemberFormSet = modelformset_factory( model=Device, form=forms.DeviceVCMembershipForm, formset=forms.BaseVCMemberFormSet, extra=0 ) members_queryset = virtual_chassis.members.prefetch_related('rack').order_by('vc_position') vc_form = forms.VirtualChassisForm(request.POST, instance=virtual_chassis) vc_form.fields['master'].queryset = members_queryset formset = VCMemberFormSet(request.POST, queryset=members_queryset) if vc_form.is_valid() and formset.is_valid(): with transaction.atomic(): # Save the VirtualChassis vc_form.save() # Nullify the vc_position of each member first to allow reordering without raising an IntegrityError on # duplicate positions. Then save each member instance. members = formset.save(commit=False) devices = Device.objects.filter(pk__in=[m.pk for m in members]) for device in devices: device.vc_position = None device.save() for member in members: member.save() return redirect(virtual_chassis.get_absolute_url()) return render(request, 'dcim/virtualchassis_edit.html', { 'vc_form': vc_form, 'formset': formset, 'return_url': self.get_return_url(request, virtual_chassis), }) class VirtualChassisDeleteView(ObjectDeleteView): queryset = VirtualChassis.objects.all() class VirtualChassisAddMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View): queryset = VirtualChassis.objects.all() def get_required_permission(self): return 'dcim.change_virtualchassis' def get(self, request, pk): virtual_chassis = get_object_or_404(self.queryset, pk=pk) initial_data = {k: request.GET[k] for k in request.GET} member_select_form = forms.VCMemberSelectForm(initial=initial_data) membership_form = forms.DeviceVCMembershipForm(initial=initial_data) return render(request, 'dcim/virtualchassis_add_member.html', { 'virtual_chassis': virtual_chassis, 'member_select_form': member_select_form, 'membership_form': membership_form, 'return_url': self.get_return_url(request, virtual_chassis), }) def post(self, request, pk): virtual_chassis = get_object_or_404(self.queryset, pk=pk) member_select_form = forms.VCMemberSelectForm(request.POST) if member_select_form.is_valid(): device = member_select_form.cleaned_data['device'] device.virtual_chassis = virtual_chassis data = {k: request.POST[k] for k in ['vc_position', 'vc_priority']} membership_form = forms.DeviceVCMembershipForm(data=data, validate_vc_position=True, instance=device) if membership_form.is_valid(): membership_form.save() msg = 'Added member {}'.format(device.get_absolute_url(), escape(device)) messages.success(request, mark_safe(msg)) if '_addanother' in request.POST: return redirect(request.get_full_path()) return redirect(self.get_return_url(request, device)) else: membership_form = forms.DeviceVCMembershipForm(data=request.POST) return render(request, 'dcim/virtualchassis_add_member.html', { 'virtual_chassis': virtual_chassis, 'member_select_form': member_select_form, 'membership_form': membership_form, 'return_url': self.get_return_url(request, virtual_chassis), }) class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURLMixin, View): queryset = Device.objects.all() def get_required_permission(self): return 'dcim.change_device' def get(self, request, pk): device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False) form = ConfirmationForm(initial=request.GET) return render(request, 'dcim/virtualchassis_remove_member.html', { 'device': device, 'form': form, 'return_url': self.get_return_url(request, device), }) def post(self, request, pk): device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False) form = ConfirmationForm(request.POST) # Protect master device from being removed virtual_chassis = VirtualChassis.objects.filter(master=device).first() if virtual_chassis is not None: msg = 'Unable to remove master device {} from the virtual chassis.'.format(escape(device)) messages.error(request, mark_safe(msg)) return redirect(device.get_absolute_url()) if form.is_valid(): devices = Device.objects.filter(pk=device.pk) for device in devices: device.virtual_chassis = None device.vc_position = None device.vc_priority = None device.save() msg = 'Removed {} from virtual chassis {}'.format(device, device.virtual_chassis) messages.success(request, msg) return redirect(self.get_return_url(request, device)) return render(request, 'dcim/virtualchassis_remove_member.html', { 'device': device, 'form': form, 'return_url': self.get_return_url(request, device), }) class VirtualChassisBulkImportView(BulkImportView): queryset = VirtualChassis.objects.all() model_form = forms.VirtualChassisCSVForm table = tables.VirtualChassisTable class VirtualChassisBulkEditView(BulkEditView): queryset = VirtualChassis.objects.all() filterset = filters.VirtualChassisFilterSet table = tables.VirtualChassisTable form = forms.VirtualChassisBulkEditForm class VirtualChassisBulkDeleteView(BulkDeleteView): queryset = VirtualChassis.objects.all() filterset = filters.VirtualChassisFilterSet table = tables.VirtualChassisTable # # Power panels # class PowerPanelListView(ObjectListView): queryset = PowerPanel.objects.prefetch_related( 'site', 'rack_group' ).annotate( powerfeed_count=Count('powerfeeds') ).order_by(*PowerPanel._meta.ordering) filterset = filters.PowerPanelFilterSet filterset_form = forms.PowerPanelFilterForm table = tables.PowerPanelTable class PowerPanelView(ObjectView): queryset = PowerPanel.objects.prefetch_related('site', 'rack_group') def get(self, request, pk): powerpanel = get_object_or_404(self.queryset, pk=pk) power_feeds = PowerFeed.objects.restrict(request.user).filter(power_panel=powerpanel).prefetch_related('rack') powerfeed_table = tables.PowerFeedTable( data=power_feeds, orderable=False ) powerfeed_table.exclude = ['power_panel'] return render(request, 'dcim/powerpanel.html', { 'powerpanel': powerpanel, 'powerfeed_table': powerfeed_table, }) class PowerPanelEditView(ObjectEditView): queryset = PowerPanel.objects.all() model_form = forms.PowerPanelForm class PowerPanelDeleteView(ObjectDeleteView): queryset = PowerPanel.objects.all() class PowerPanelBulkImportView(BulkImportView): queryset = PowerPanel.objects.all() model_form = forms.PowerPanelCSVForm table = tables.PowerPanelTable class PowerPanelBulkEditView(BulkEditView): queryset = PowerPanel.objects.prefetch_related('site', 'rack_group') filterset = filters.PowerPanelFilterSet table = tables.PowerPanelTable form = forms.PowerPanelBulkEditForm class PowerPanelBulkDeleteView(BulkDeleteView): queryset = PowerPanel.objects.prefetch_related( 'site', 'rack_group' ).annotate( rack_count=Count('powerfeeds') ).order_by(*PowerPanel._meta.ordering) filterset = filters.PowerPanelFilterSet table = tables.PowerPanelTable # # Power feeds # class PowerFeedListView(ObjectListView): queryset = PowerFeed.objects.prefetch_related( 'power_panel', 'rack' ) filterset = filters.PowerFeedFilterSet filterset_form = forms.PowerFeedFilterForm table = tables.PowerFeedTable class PowerFeedView(ObjectView): queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack') def get(self, request, pk): powerfeed = get_object_or_404(self.queryset, pk=pk) return render(request, 'dcim/powerfeed.html', { 'powerfeed': powerfeed, }) class PowerFeedEditView(ObjectEditView): queryset = PowerFeed.objects.all() model_form = forms.PowerFeedForm template_name = 'dcim/powerfeed_edit.html' class PowerFeedDeleteView(ObjectDeleteView): queryset = PowerFeed.objects.all() class PowerFeedBulkImportView(BulkImportView): queryset = PowerFeed.objects.all() model_form = forms.PowerFeedCSVForm table = tables.PowerFeedTable class PowerFeedBulkEditView(BulkEditView): queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack') filterset = filters.PowerFeedFilterSet table = tables.PowerFeedTable form = forms.PowerFeedBulkEditForm class PowerFeedBulkDeleteView(BulkDeleteView): queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack') filterset = filters.PowerFeedFilterSet table = tables.PowerFeedTable