1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00
netbox-community-netbox/netbox/virtualization/views.py

667 lines
21 KiB
Python
Raw Normal View History

import traceback
from django.contrib import messages
from django.db import transaction
from django.db.models import Prefetch, Sum
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.translation import gettext as _
from jinja2.exceptions import TemplateError
from dcim.filtersets import DeviceFilterSet
2020-06-22 13:10:56 -04:00
from dcim.models import Device
from dcim.tables import DeviceTable
from extras.views import ObjectConfigContextView
from ipam.models import IPAddress
from ipam.tables import InterfaceVLANTable
from netbox.constants import DEFAULT_ACTION_PERMISSIONS
2020-11-11 16:07:38 -05:00
from netbox.views import generic
from tenancy.views import ObjectContactsView
from utilities.query import count_related
from utilities.query_functions import CollateAsChar
from utilities.views import ViewTab, register_model_view
2021-04-29 16:38:56 -04:00
from . import filtersets, forms, tables
from .models import *
#
# Cluster types
#
2020-11-11 16:07:38 -05:00
class ClusterTypeListView(generic.ObjectListView):
queryset = ClusterType.objects.annotate(
cluster_count=count_related(Cluster, 'type')
)
filterset = filtersets.ClusterTypeFilterSet
filterset_form = forms.ClusterTypeFilterForm
table = tables.ClusterTypeTable
@register_model_view(ClusterType)
class ClusterTypeView(generic.ObjectView):
queryset = ClusterType.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(Cluster.objects.restrict(request.user, 'view').filter(type=instance), 'type_id'),
)
return {
'related_models': related_models,
}
@register_model_view(ClusterType, 'edit')
2020-11-11 16:07:38 -05:00
class ClusterTypeEditView(generic.ObjectEditView):
queryset = ClusterType.objects.all()
form = forms.ClusterTypeForm
@register_model_view(ClusterType, 'delete')
2020-11-11 16:07:38 -05:00
class ClusterTypeDeleteView(generic.ObjectDeleteView):
queryset = ClusterType.objects.all()
2020-11-11 16:07:38 -05:00
class ClusterTypeBulkImportView(generic.BulkImportView):
2020-05-21 11:58:27 -04:00
queryset = ClusterType.objects.all()
model_form = forms.ClusterTypeImportForm
class ClusterTypeBulkEditView(generic.BulkEditView):
queryset = ClusterType.objects.annotate(
cluster_count=count_related(Cluster, 'type')
)
2021-04-29 16:38:56 -04:00
filterset = filtersets.ClusterTypeFilterSet
table = tables.ClusterTypeTable
form = forms.ClusterTypeBulkEditForm
2020-11-11 16:07:38 -05:00
class ClusterTypeBulkDeleteView(generic.BulkDeleteView):
queryset = ClusterType.objects.annotate(
cluster_count=count_related(Cluster, 'type')
)
filterset = filtersets.ClusterTypeFilterSet
table = tables.ClusterTypeTable
#
# Cluster groups
#
2020-11-11 16:07:38 -05:00
class ClusterGroupListView(generic.ObjectListView):
queryset = ClusterGroup.objects.annotate(
cluster_count=count_related(Cluster, 'group')
)
filterset = filtersets.ClusterGroupFilterSet
filterset_form = forms.ClusterGroupFilterForm
table = tables.ClusterGroupTable
@register_model_view(ClusterGroup)
class ClusterGroupView(generic.ObjectView):
queryset = ClusterGroup.objects.all()
def get_extra_context(self, request, instance):
related_models = (
(Cluster.objects.restrict(request.user, 'view').filter(group=instance), 'group_id'),
)
return {
'related_models': related_models,
}
@register_model_view(ClusterGroup, 'edit')
2020-11-11 16:07:38 -05:00
class ClusterGroupEditView(generic.ObjectEditView):
queryset = ClusterGroup.objects.all()
form = forms.ClusterGroupForm
@register_model_view(ClusterGroup, 'delete')
2020-11-11 16:07:38 -05:00
class ClusterGroupDeleteView(generic.ObjectDeleteView):
queryset = ClusterGroup.objects.all()
2020-11-11 16:07:38 -05:00
class ClusterGroupBulkImportView(generic.BulkImportView):
queryset = ClusterGroup.objects.annotate(
cluster_count=count_related(Cluster, 'group')
)
model_form = forms.ClusterGroupImportForm
class ClusterGroupBulkEditView(generic.BulkEditView):
queryset = ClusterGroup.objects.annotate(
cluster_count=count_related(Cluster, 'group')
)
2021-04-29 16:38:56 -04:00
filterset = filtersets.ClusterGroupFilterSet
table = tables.ClusterGroupTable
form = forms.ClusterGroupBulkEditForm
2020-11-11 16:07:38 -05:00
class ClusterGroupBulkDeleteView(generic.BulkDeleteView):
queryset = ClusterGroup.objects.annotate(
cluster_count=count_related(Cluster, 'group')
)
filterset = filtersets.ClusterGroupFilterSet
table = tables.ClusterGroupTable
@register_model_view(ClusterGroup, 'contacts')
class ClusterGroupContactsView(ObjectContactsView):
queryset = ClusterGroup.objects.all()
#
# Clusters
#
2020-11-11 16:07:38 -05:00
class ClusterListView(generic.ObjectListView):
2019-04-11 17:27:38 -04:00
permission_required = 'virtualization.view_cluster'
queryset = Cluster.objects.annotate(
device_count=count_related(Device, 'cluster'),
vm_count=count_related(VirtualMachine, 'cluster')
)
table = tables.ClusterTable
2021-04-29 16:38:56 -04:00
filterset = filtersets.ClusterFilterSet
filterset_form = forms.ClusterFilterForm
@register_model_view(Cluster)
2020-11-11 16:07:38 -05:00
class ClusterView(generic.ObjectView):
queryset = Cluster.objects.all()
def get_extra_context(self, request, instance):
return instance.virtual_machines.aggregate(vcpus_sum=Sum('vcpus'), memory_sum=Sum('memory'), disk_sum=Sum('disk'))
@register_model_view(Cluster, 'virtualmachines', path='virtual-machines')
class ClusterVirtualMachinesView(generic.ObjectChildrenView):
queryset = Cluster.objects.all()
child_model = VirtualMachine
table = tables.VirtualMachineTable
filterset = filtersets.VirtualMachineFilterSet
template_name = 'generic/object_children.html'
tab = ViewTab(
label=_('Virtual Machines'),
badge=lambda obj: obj.virtual_machines.count(),
permission='virtualization.view_virtualmachine',
weight=500
)
def get_children(self, request, parent):
return VirtualMachine.objects.restrict(request.user, 'view').filter(cluster=parent)
@register_model_view(Cluster, 'devices')
class ClusterDevicesView(generic.ObjectChildrenView):
queryset = Cluster.objects.all()
child_model = Device
table = DeviceTable
filterset = DeviceFilterSet
template_name = 'virtualization/cluster/devices.html'
actions = {
'add': {'add'},
'import': {'add'},
'export': {'view'},
'bulk_edit': {'change'},
'bulk_remove_devices': {'change'},
}
tab = ViewTab(
label=_('Devices'),
badge=lambda obj: obj.devices.count(),
permission='virtualization.view_virtualmachine',
weight=600
)
def get_children(self, request, parent):
return Device.objects.restrict(request.user, 'view').filter(cluster=parent)
@register_model_view(Cluster, 'edit')
2020-11-11 16:07:38 -05:00
class ClusterEditView(generic.ObjectEditView):
queryset = Cluster.objects.all()
form = forms.ClusterForm
@register_model_view(Cluster, 'delete')
2020-11-11 16:07:38 -05:00
class ClusterDeleteView(generic.ObjectDeleteView):
queryset = Cluster.objects.all()
2020-11-11 16:07:38 -05:00
class ClusterBulkImportView(generic.BulkImportView):
2020-05-21 11:58:27 -04:00
queryset = Cluster.objects.all()
model_form = forms.ClusterImportForm
2020-11-11 16:07:38 -05:00
class ClusterBulkEditView(generic.BulkEditView):
queryset = Cluster.objects.all()
2021-04-29 16:38:56 -04:00
filterset = filtersets.ClusterFilterSet
2017-09-11 16:14:05 -04:00
table = tables.ClusterTable
form = forms.ClusterBulkEditForm
2020-11-11 16:07:38 -05:00
class ClusterBulkDeleteView(generic.BulkDeleteView):
queryset = Cluster.objects.all()
2021-04-29 16:38:56 -04:00
filterset = filtersets.ClusterFilterSet
table = tables.ClusterTable
@register_model_view(Cluster, 'add_devices', path='devices/add')
2020-11-11 16:07:38 -05:00
class ClusterAddDevicesView(generic.ObjectEditView):
queryset = Cluster.objects.all()
form = forms.ClusterAddDevicesForm
template_name = 'virtualization/cluster_add_devices.html'
def get(self, request, pk):
cluster = get_object_or_404(self.queryset, pk=pk)
form = self.form(cluster, initial=request.GET)
return render(request, self.template_name, {
'cluster': cluster,
'form': form,
'return_url': reverse('virtualization:cluster', kwargs={'pk': pk}),
})
def post(self, request, pk):
cluster = get_object_or_404(self.queryset, pk=pk)
form = self.form(cluster, request.POST)
if form.is_valid():
device_pks = form.cleaned_data['devices']
with transaction.atomic():
# Assign the selected Devices to the Cluster
for device in Device.objects.filter(pk__in=device_pks):
device.cluster = cluster
device.save()
messages.success(request, "Added {} devices to cluster {}".format(
len(device_pks), cluster
))
return redirect(cluster.get_absolute_url())
return render(request, self.template_name, {
'cluster': cluster,
'form': form,
'return_url': cluster.get_absolute_url(),
})
@register_model_view(Cluster, 'remove_devices', path='devices/remove')
2020-11-11 16:07:38 -05:00
class ClusterRemoveDevicesView(generic.ObjectEditView):
queryset = Cluster.objects.all()
form = forms.ClusterRemoveDevicesForm
2022-02-08 11:49:18 -05:00
template_name = 'generic/bulk_remove.html'
def post(self, request, pk):
cluster = get_object_or_404(self.queryset, pk=pk)
if '_confirm' in request.POST:
form = self.form(request.POST)
if form.is_valid():
device_pks = form.cleaned_data['pk']
with transaction.atomic():
# Remove the selected Devices from the Cluster
for device in Device.objects.filter(pk__in=device_pks):
device.cluster = None
device.save()
messages.success(request, "Removed {} devices from cluster {}".format(
len(device_pks), cluster
))
return redirect(cluster.get_absolute_url())
else:
form = self.form(initial={'pk': request.POST.getlist('pk')})
selected_objects = Device.objects.filter(pk__in=form.initial['pk'])
device_table = DeviceTable(list(selected_objects), orderable=False)
return render(request, self.template_name, {
'form': form,
'parent_obj': cluster,
'table': device_table,
'obj_type_plural': 'devices',
'return_url': cluster.get_absolute_url(),
})
@register_model_view(Cluster, 'contacts')
class ClusterContactsView(ObjectContactsView):
queryset = Cluster.objects.all()
#
# Virtual machines
#
2020-11-11 16:07:38 -05:00
class VirtualMachineListView(generic.ObjectListView):
queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
2021-04-29 16:38:56 -04:00
filterset = filtersets.VirtualMachineFilterSet
filterset_form = forms.VirtualMachineFilterForm
table = tables.VirtualMachineTable
template_name = 'virtualization/virtualmachine_list.html'
@register_model_view(VirtualMachine)
2020-11-11 16:07:38 -05:00
class VirtualMachineView(generic.ObjectView):
queryset = VirtualMachine.objects.all()
@register_model_view(VirtualMachine, 'interfaces')
class VirtualMachineInterfacesView(generic.ObjectChildrenView):
queryset = VirtualMachine.objects.all()
child_model = VMInterface
table = tables.VirtualMachineVMInterfaceTable
filterset = filtersets.VMInterfaceFilterSet
template_name = 'virtualization/virtualmachine/interfaces.html'
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
tab = ViewTab(
label=_('Interfaces'),
badge=lambda obj: obj.interface_count,
permission='virtualization.view_vminterface',
weight=500
)
def get_children(self, request, parent):
return parent.interfaces.restrict(request.user, 'view').prefetch_related(
Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
'tags',
)
@register_model_view(VirtualMachine, 'disks')
class VirtualMachineVirtualDisksView(generic.ObjectChildrenView):
queryset = VirtualMachine.objects.all()
child_model = VirtualDisk
table = tables.VirtualMachineVirtualDiskTable
filterset = filtersets.VirtualDiskFilterSet
template_name = 'virtualization/virtualmachine/virtual_disks.html'
tab = ViewTab(
label=_('Virtual Disks'),
badge=lambda obj: obj.virtual_disk_count,
permission='virtualization.view_virtualdisk',
weight=500
)
actions = {
**DEFAULT_ACTION_PERMISSIONS,
'bulk_rename': {'change'},
}
def get_children(self, request, parent):
return parent.virtualdisks.restrict(request.user, 'view').prefetch_related('tags')
@register_model_view(VirtualMachine, 'configcontext', path='config-context')
class VirtualMachineConfigContextView(ObjectConfigContextView):
2020-10-23 01:18:04 -04:00
queryset = VirtualMachine.objects.annotate_config_context_data()
base_template = 'virtualization/virtualmachine.html'
tab = ViewTab(
label=_('Config Context'),
weight=2000
)
2018-06-27 16:02:34 -04:00
@register_model_view(VirtualMachine, 'render-config')
class VirtualMachineRenderConfigView(generic.ObjectView):
queryset = VirtualMachine.objects.all()
template_name = 'virtualization/virtualmachine/render_config.html'
tab = ViewTab(
label=_('Render Config'),
weight=2100
)
def get(self, request, **kwargs):
instance = self.get_object(**kwargs)
context = self.get_extra_context(request, instance)
# If a direct export has been requested, return the rendered template content as a
# downloadable file.
if request.GET.get('export'):
response = HttpResponse(context['rendered_config'], content_type='text')
filename = f"{instance.name or 'config'}.txt"
response['Content-Disposition'] = f'attachment; filename="{filename}"'
return response
return render(request, self.get_template_name(), {
'object': instance,
'tab': self.tab,
**context,
})
def get_extra_context(self, request, instance):
# Compile context data
context_data = instance.get_config_context()
context_data.update({'virtualmachine': instance})
# Render the config template
rendered_config = None
if config_template := instance.get_config_template():
try:
rendered_config = config_template.render(context=context_data)
except TemplateError as e:
messages.error(request, f"An error occurred while rendering the template: {e}")
rendered_config = traceback.format_exc()
return {
'config_template': config_template,
'context_data': context_data,
'rendered_config': rendered_config,
}
@register_model_view(VirtualMachine, 'edit')
2020-11-11 16:07:38 -05:00
class VirtualMachineEditView(generic.ObjectEditView):
queryset = VirtualMachine.objects.all()
form = forms.VirtualMachineForm
@register_model_view(VirtualMachine, 'delete')
2020-11-11 16:07:38 -05:00
class VirtualMachineDeleteView(generic.ObjectDeleteView):
queryset = VirtualMachine.objects.all()
2020-11-11 16:07:38 -05:00
class VirtualMachineBulkImportView(generic.BulkImportView):
2020-05-21 11:58:27 -04:00
queryset = VirtualMachine.objects.all()
model_form = forms.VirtualMachineImportForm
2020-11-11 16:07:38 -05:00
class VirtualMachineBulkEditView(generic.BulkEditView):
queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
2021-04-29 16:38:56 -04:00
filterset = filtersets.VirtualMachineFilterSet
table = tables.VirtualMachineTable
form = forms.VirtualMachineBulkEditForm
2020-11-11 16:07:38 -05:00
class VirtualMachineBulkDeleteView(generic.BulkDeleteView):
queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
2021-04-29 16:38:56 -04:00
filterset = filtersets.VirtualMachineFilterSet
2017-09-11 16:14:05 -04:00
table = tables.VirtualMachineTable
@register_model_view(VirtualMachine, 'contacts')
class VirtualMachineContactsView(ObjectContactsView):
queryset = VirtualMachine.objects.all()
#
# VM interfaces
#
2020-11-11 16:07:38 -05:00
class VMInterfaceListView(generic.ObjectListView):
queryset = VMInterface.objects.all()
2021-04-29 16:38:56 -04:00
filterset = filtersets.VMInterfaceFilterSet
2020-06-23 16:39:43 -04:00
filterset_form = forms.VMInterfaceFilterForm
table = tables.VMInterfaceTable
2020-06-22 13:10:56 -04:00
@register_model_view(VMInterface)
2020-11-11 16:07:38 -05:00
class VMInterfaceView(generic.ObjectView):
2020-06-23 13:16:21 -04:00
queryset = VMInterface.objects.all()
2020-06-22 13:10:56 -04:00
def get_extra_context(self, request, instance):
# Get child interfaces
child_interfaces = VMInterface.objects.restrict(request.user, 'view').filter(parent=instance)
child_interfaces_tables = tables.VMInterfaceTable(
child_interfaces,
exclude=('virtual_machine',),
orderable=False
)
# Get assigned VLANs and annotate whether each is tagged or untagged
vlans = []
if instance.untagged_vlan is not None:
vlans.append(instance.untagged_vlan)
vlans[0].tagged = False
for vlan in instance.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'):
vlan.tagged = True
vlans.append(vlan)
vlan_table = InterfaceVLANTable(
interface=instance,
data=vlans,
orderable=False
)
return {
'child_interfaces_table': child_interfaces_tables,
'vlan_table': vlan_table,
}
2020-06-22 13:10:56 -04:00
2020-11-11 16:07:38 -05:00
class VMInterfaceCreateView(generic.ComponentCreateView):
2020-06-23 13:16:21 -04:00
queryset = VMInterface.objects.all()
2020-06-23 16:39:43 -04:00
form = forms.VMInterfaceCreateForm
model_form = forms.VMInterfaceForm
2017-08-18 14:37:19 -04:00
@register_model_view(VMInterface, 'edit')
2020-11-11 16:07:38 -05:00
class VMInterfaceEditView(generic.ObjectEditView):
2020-06-23 13:16:21 -04:00
queryset = VMInterface.objects.all()
form = forms.VMInterfaceForm
2017-08-18 14:37:19 -04:00
@register_model_view(VMInterface, 'delete')
2020-11-11 16:07:38 -05:00
class VMInterfaceDeleteView(generic.ObjectDeleteView):
2020-06-23 13:16:21 -04:00
queryset = VMInterface.objects.all()
2017-08-18 14:37:19 -04:00
2020-11-11 16:07:38 -05:00
class VMInterfaceBulkImportView(generic.BulkImportView):
2020-06-23 17:01:57 -04:00
queryset = VMInterface.objects.all()
model_form = forms.VMInterfaceImportForm
2020-06-23 17:01:57 -04:00
2020-11-11 16:07:38 -05:00
class VMInterfaceBulkEditView(generic.BulkEditView):
2020-06-23 13:16:21 -04:00
queryset = VMInterface.objects.all()
filterset = filtersets.VMInterfaceFilterSet
2020-06-23 16:39:43 -04:00
table = tables.VMInterfaceTable
form = forms.VMInterfaceBulkEditForm
2017-08-18 14:37:19 -04:00
2020-11-11 16:07:38 -05:00
class VMInterfaceBulkRenameView(generic.BulkRenameView):
queryset = VMInterface.objects.all()
form = forms.VMInterfaceBulkRenameForm
2020-11-11 16:07:38 -05:00
class VMInterfaceBulkDeleteView(generic.BulkDeleteView):
# Ensure child interfaces are deleted prior to their parents
queryset = VMInterface.objects.order_by('virtual_machine', 'parent', CollateAsChar('_name'))
filterset = filtersets.VMInterfaceFilterSet
2020-06-23 16:39:43 -04:00
table = tables.VMInterfaceTable
#
# Virtual disks
#
class VirtualDiskListView(generic.ObjectListView):
queryset = VirtualDisk.objects.all()
filterset = filtersets.VirtualDiskFilterSet
filterset_form = forms.VirtualDiskFilterForm
table = tables.VirtualDiskTable
@register_model_view(VirtualDisk)
class VirtualDiskView(generic.ObjectView):
queryset = VirtualDisk.objects.all()
class VirtualDiskCreateView(generic.ComponentCreateView):
queryset = VirtualDisk.objects.all()
form = forms.VirtualDiskCreateForm
model_form = forms.VirtualDiskForm
@register_model_view(VirtualDisk, 'edit')
class VirtualDiskEditView(generic.ObjectEditView):
queryset = VirtualDisk.objects.all()
form = forms.VirtualDiskForm
@register_model_view(VirtualDisk, 'delete')
class VirtualDiskDeleteView(generic.ObjectDeleteView):
queryset = VirtualDisk.objects.all()
class VirtualDiskBulkImportView(generic.BulkImportView):
queryset = VirtualDisk.objects.all()
model_form = forms.VirtualDiskImportForm
class VirtualDiskBulkEditView(generic.BulkEditView):
queryset = VirtualDisk.objects.all()
filterset = filtersets.VirtualDiskFilterSet
table = tables.VirtualDiskTable
form = forms.VirtualDiskBulkEditForm
class VirtualDiskBulkRenameView(generic.BulkRenameView):
queryset = VirtualDisk.objects.all()
form = forms.VirtualDiskBulkRenameForm
class VirtualDiskBulkDeleteView(generic.BulkDeleteView):
queryset = VirtualDisk.objects.all()
filterset = filtersets.VirtualDiskFilterSet
table = tables.VirtualDiskTable
#
# Bulk Device component creation
#
2020-11-11 16:07:38 -05:00
class VirtualMachineBulkAddInterfaceView(generic.BulkComponentCreateView):
parent_model = VirtualMachine
parent_field = 'virtual_machine'
2020-06-23 16:39:43 -04:00
form = forms.VMInterfaceBulkCreateForm
2020-06-23 13:16:21 -04:00
queryset = VMInterface.objects.all()
2020-06-23 16:39:43 -04:00
model_form = forms.VMInterfaceForm
2021-04-29 16:38:56 -04:00
filterset = filtersets.VirtualMachineFilterSet
table = tables.VirtualMachineTable
default_return_url = 'virtualization:virtualmachine_list'
def get_required_permission(self):
return f'virtualization.add_vminterface'
class VirtualMachineBulkAddVirtualDiskView(generic.BulkComponentCreateView):
parent_model = VirtualMachine
parent_field = 'virtual_machine'
form = forms.VirtualDiskBulkCreateForm
queryset = VirtualDisk.objects.all()
model_form = forms.VirtualDiskForm
filterset = filtersets.VirtualMachineFilterSet
table = tables.VirtualMachineTable
default_return_url = 'virtualization:virtualmachine_list'
def get_required_permission(self):
return f'virtualization.add_virtualdisk'