From 316c42a645d41edd3b29f029fe8e44452a8f867d Mon Sep 17 00:00:00 2001 From: Nico Felbinger Date: Tue, 16 Apr 2024 16:25:21 +0200 Subject: [PATCH] Add documents for virtual machines --- README.md | 1 + netbox_documents/__init__.py | 2 + netbox_documents/api/serializers.py | 32 ++++- netbox_documents/api/urls.py | 1 + netbox_documents/api/views.py | 9 +- netbox_documents/filtersets.py | 16 ++- netbox_documents/forms.py | 36 +++++- .../migrations/0006_vmdocument.py | 40 ++++++ netbox_documents/models.py | 119 ++++++++++++++++++ netbox_documents/navigation.py | 15 +++ netbox_documents/search.py | 12 +- netbox_documents/tables.py | 28 ++++- netbox_documents/template_content.py | 29 ++++- .../netbox_documents/vmdocument.html | 56 +++++++++ .../netbox_documents/vmdocument_edit.html | 49 ++++++++ .../netbox_documents/vmdocument_include.html | 49 ++++++++ netbox_documents/urls.py | 10 ++ netbox_documents/utils.py | 2 + netbox_documents/views.py | 22 +++- 19 files changed, 515 insertions(+), 13 deletions(-) create mode 100644 netbox_documents/migrations/0006_vmdocument.py create mode 100644 netbox_documents/templates/netbox_documents/vmdocument.html create mode 100644 netbox_documents/templates/netbox_documents/vmdocument_edit.html create mode 100644 netbox_documents/templates/netbox_documents/vmdocument_include.html diff --git a/README.md b/README.md index 2e86464..72435cc 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ A plugin designed to faciliate the storage of site, circuit, device type and dev - Device Types - Sites - Locations + - Virtual Machines * Upload documents to your NetBox media/ folder or other Django supported storage method e.g. S3 * Supports a wide array of common file types (bmp, gif, jpeg, jpg, png, pdf, txt, doc, docx, xls, xlsx, xlsm) diff --git a/netbox_documents/__init__.py b/netbox_documents/__init__.py index abf037d..2197059 100644 --- a/netbox_documents/__init__.py +++ b/netbox_documents/__init__.py @@ -15,12 +15,14 @@ class NetboxDocuments(PluginConfig): "enable_circuit_documents": True, "enable_device_documents": True, "enable_device_type_documents": True, + "enable_vm_documents": True, "enable_navigation_menu": True, "site_documents_location": "left", "location_documents_location": "left", "circuit_documents_location": "left", "device_documents_location": "left", "device_type_documents_location": "left", + "vm_documents_location": "left", } config = NetboxDocuments diff --git a/netbox_documents/api/serializers.py b/netbox_documents/api/serializers.py index 8c2a846..c1a9a09 100644 --- a/netbox_documents/api/serializers.py +++ b/netbox_documents/api/serializers.py @@ -1,9 +1,10 @@ from rest_framework import serializers from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer -from ..models import SiteDocument, LocationDocument, DeviceDocument, DeviceTypeDocument, CircuitDocument +from ..models import SiteDocument, LocationDocument, DeviceDocument, DeviceTypeDocument, CircuitDocument, VMDocument from dcim.api.nested_serializers import NestedSiteSerializer, NestedLocationSerializer, NestedDeviceSerializer, NestedDeviceTypeSerializer from circuits.api.nested_serializers import NestedCircuitSerializer +from virtualization.api.nested_serializers import NestedVirtualMachineSerializer from .fields import UploadableBase64FileField # Site Document Serializer @@ -141,3 +142,32 @@ class Meta: fields = ( 'id', 'url', 'display', 'name', 'document', 'external_url', 'document_type', 'filename', ) + +# VM Document Serializer +class VMDocumentSerializer(NetBoxModelSerializer): + + url = serializers.HyperlinkedIdentityField( + view_name='plugins-api:netbox_documents-api:vmdocument-detail' + ) + + vm = NestedVirtualMachineSerializer() + document = UploadableBase64FileField(required=False) + + class Meta: + model = VMDocument + fields = ( + 'id', 'url', 'display', 'name', 'document', 'external_url', 'document_type', 'filename', 'vm', 'comments', 'tags', 'custom_fields', 'created', + 'last_updated', + ) + +class NestedVMDocumentSerializer(WritableNestedSerializer): + + url = serializers.HyperlinkedIdentityField( + view_name='plugins-api:netbox_documents-api:vmdocument-detail' + ) + + class Meta: + model = VMDocument + fields = ( + 'id', 'url', 'display', 'name', 'document', 'external_url', 'document_type', 'filename', + ) diff --git a/netbox_documents/api/urls.py b/netbox_documents/api/urls.py index c65b5a4..d2c739c 100644 --- a/netbox_documents/api/urls.py +++ b/netbox_documents/api/urls.py @@ -9,5 +9,6 @@ router.register('device-documents', views.DeviceDocumentViewSet) router.register('device-type-documents', views.DeviceTypeDocumentViewSet) router.register('circuit-documents', views.CircuitDocumentViewSet) +router.register('vm-documents', views.VMDocumentViewSet) urlpatterns = router.urls \ No newline at end of file diff --git a/netbox_documents/api/views.py b/netbox_documents/api/views.py index 5a9e003..f6d7ceb 100644 --- a/netbox_documents/api/views.py +++ b/netbox_documents/api/views.py @@ -1,7 +1,7 @@ from netbox.api.viewsets import NetBoxModelViewSet from .. import models, filtersets -from .serializers import SiteDocumentSerializer, LocationDocumentSerializer, DeviceDocumentSerializer, DeviceTypeDocumentSerializer, CircuitDocumentSerializer +from .serializers import SiteDocumentSerializer, LocationDocumentSerializer, DeviceDocumentSerializer, DeviceTypeDocumentSerializer, CircuitDocumentSerializer, VMDocumentSerializer class SiteDocumentViewSet(NetBoxModelViewSet): queryset = models.SiteDocument.objects.prefetch_related('tags') @@ -26,4 +26,9 @@ class DeviceTypeDocumentViewSet(NetBoxModelViewSet): class CircuitDocumentViewSet(NetBoxModelViewSet): queryset = models.CircuitDocument.objects.prefetch_related('tags') serializer_class = CircuitDocumentSerializer - filterset_class = filtersets.CircuitDocumentFilterSet \ No newline at end of file + filterset_class = filtersets.CircuitDocumentFilterSet + +class VMDocumentViewSet(NetBoxModelViewSet): + queryset = models.VMDocument.objects.prefetch_related('tags') + serializer_class = VMDocumentSerializer + filterset_class = filtersets.VMDocumentFilterSet diff --git a/netbox_documents/filtersets.py b/netbox_documents/filtersets.py index b08fbe1..fe6097d 100644 --- a/netbox_documents/filtersets.py +++ b/netbox_documents/filtersets.py @@ -1,5 +1,5 @@ from netbox.filtersets import NetBoxModelFilterSet -from .models import SiteDocument, LocationDocument, DeviceDocument, DeviceTypeDocument, CircuitDocument +from .models import SiteDocument, LocationDocument, DeviceDocument, DeviceTypeDocument, CircuitDocument, VMDocument from django.db.models import Q class SiteDocumentFilterSet(NetBoxModelFilterSet): @@ -72,3 +72,17 @@ def search(self, queryset, name, value): Q(name__icontains=value) | Q(document__icontains=value) ) + +class VMDocumentFilterSet(NetBoxModelFilterSet): + + class Meta: + model = VMDocument + fields = ('id', 'name', 'document_type', 'vm') + + def search(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(name__icontains=value) | + Q(document__icontains=value) + ) \ No newline at end of file diff --git a/netbox_documents/forms.py b/netbox_documents/forms.py index 384d999..e46ce97 100644 --- a/netbox_documents/forms.py +++ b/netbox_documents/forms.py @@ -1,9 +1,10 @@ from django import forms from netbox.forms import NetBoxModelForm, NetBoxModelFilterSetForm -from dcim.models import Site, Location, Device, DeviceType +from dcim.models import Site, Location, Device, DeviceType +from virtualization.models import VirtualMachine from circuits.models import Circuit from utilities.forms.fields import TagFilterField, CommentField, DynamicModelChoiceField -from .models import SiteDocument, LocationDocument, DeviceDocument, DeviceTypeDocument, CircuitDocument, CircuitDocTypeChoices, SiteDocTypeChoices, LocationDocTypeChoices, DeviceDocTypeChoices, DeviceTypeDocTypeChoices +from .models import SiteDocument, LocationDocument, DeviceDocument, DeviceTypeDocument, CircuitDocument, CircuitDocTypeChoices, SiteDocTypeChoices, LocationDocTypeChoices, DeviceDocTypeChoices, DeviceTypeDocTypeChoices, VMDocument, VMDocTypeChoices #### Site Document Form & Filter Form @@ -179,3 +180,34 @@ class CircuitDocumentFilterForm(NetBoxModelFilterSetForm): ) tag = TagFilterField(model) + +#### VM Document Form & Filter Form +class VMDocumentForm(NetBoxModelForm): + comments = CommentField() + + vm = DynamicModelChoiceField( + queryset=VirtualMachine.objects.all() + ) + + class Meta: + model = VMDocument + fields = ('name', 'document', 'external_url', 'document_type', 'vm', 'comments', 'tags') + +class VMDocumentFilterForm(NetBoxModelFilterSetForm): + model = VMDocument + + name = forms.CharField( + required=False + ) + + vm = forms.ModelMultipleChoiceField( + queryset=VirtualMachine.objects.all(), + required=False + ) + + document_type = forms.MultipleChoiceField( + choices=VMDocTypeChoices, + required=False + ) + + tag = TagFilterField(model) diff --git a/netbox_documents/migrations/0006_vmdocument.py b/netbox_documents/migrations/0006_vmdocument.py new file mode 100644 index 0000000..d4ad518 --- /dev/null +++ b/netbox_documents/migrations/0006_vmdocument.py @@ -0,0 +1,40 @@ +# Generated by Django 4.2.9 on 2024-04-16 12:19 + +from django.db import migrations, models +import django.db.models.deletion +import netbox_documents.utils +import taggit.managers +import utilities.json + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0106_bookmark_user_cascade_deletion'), + ('virtualization', '0038_virtualdisk'), + ('netbox_documents', '0005_alter_circuitdocument_external_url_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='VMDocument', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), + ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)), + ('name', models.CharField(blank=True, max_length=100)), + ('document', models.FileField(blank=True, upload_to=netbox_documents.utils.file_upload)), + ('external_url', models.URLField(blank=True, max_length=255)), + ('document_type', models.CharField(max_length=30)), + ('comments', models.TextField(blank=True)), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ('vm', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='virtualization.virtualmachine')), + ], + options={ + 'verbose_name': 'VM Document', + 'verbose_name_plural': 'VM Documents', + 'ordering': ('name',), + }, + ), + ] diff --git a/netbox_documents/models.py b/netbox_documents/models.py index 3f37c7a..fa50c29 100644 --- a/netbox_documents/models.py +++ b/netbox_documents/models.py @@ -71,6 +71,19 @@ class CircuitDocTypeChoices(ChoiceSet): ('other', 'Other', 'gray'), ] +class VMDocTypeChoices(ChoiceSet): + + key = 'DocTypeChoices.virtualmachine' + + CHOICES = [ + ('diagram', 'Network Diagram', 'green'), + ('manual', 'Manual', 'pink'), + ('purchaseorder', 'Purchase Order', 'orange'), + ('quote', 'Quote', 'indigo'), + ('supportcontract', 'Support Contract', 'blue'), + ('other', 'Other', 'gray'), + ] + class SiteDocument(NetBoxModel): name = models.CharField( max_length=100, @@ -618,3 +631,109 @@ def delete(self, *args, **kwargs): else: # Straight delete of external URL super().delete(*args, **kwargs) + + +class VMDocument(NetBoxModel): + name = models.CharField( + max_length=100, + blank=True, + help_text='(Optional) Specify a name to display for this document. If no name is specified, the filename will be used.' + ) + + document = models.FileField( + upload_to=file_upload, + blank=True + ) + + external_url = models.URLField( + blank=True, + max_length=255 + ) + + document_type = models.CharField( + max_length=30, + choices=VMDocTypeChoices + ) + + vm = models.ForeignKey( + to='virtualization.VirtualMachine', + on_delete=models.CASCADE, + related_name='documents' + ) + + comments = models.TextField( + blank=True + ) + + class Meta: + ordering = ('name',) + verbose_name_plural = "VM Documents" + verbose_name = "VM Document" + + def get_document_type_color(self): + return VMDocTypeChoices.colors.get(self.document_type) + + @property + def size(self): + """ + Wrapper around `document.size` to suppress an OSError in case the file is inaccessible. Also opportunistically + catch other exceptions that we know other storage back-ends to throw. + """ + expected_exceptions = [OSError] + + try: + from botocore.exceptions import ClientError + expected_exceptions.append(ClientError) + except ImportError: + pass + + try: + return self.document.size + except: + return None + + @property + def filename(self): + if self.external_url: + return self.external_url + filename = self.document.name.rsplit('/', 1)[-1] + return filename.split('_', 1)[1] + + def __str__(self): + if self.name: + return self.name + + if self.external_url: + return self.external_url + + filename = self.document.name.rsplit('/', 1)[-1] + return filename.split('_', 1)[1] + + def get_absolute_url(self): + return reverse('plugins:netbox_documents:vmdocument', args=[self.pk]) + + def clean(self): + super().clean() + + # Must have an uploaded document or an external URL. cannot have both + if not self.document and self.external_url == '': + raise ValidationError("A document must contain an uploaded file or an external URL.") + if self.document and self.external_url: + raise ValidationError("A document cannot contain both an uploaded file and an external URL.") + + def delete(self, *args, **kwargs): + + # Check if its a document or a URL + if self.external_url == '': + + _name = self.document.name + + # Delete file from disk + super().delete(*args, **kwargs) + self.document.delete(save=False) + + # Restore the name of the document as it's re-used in the notifications later + self.document.name = _name + else: + # Straight delete of external URL + super().delete(*args, **kwargs) diff --git a/netbox_documents/navigation.py b/netbox_documents/navigation.py index 8bfde1e..8933960 100644 --- a/netbox_documents/navigation.py +++ b/netbox_documents/navigation.py @@ -94,6 +94,21 @@ icon_class='mdi mdi-file-document-multiple' ) + # Add a menu item for VM Documents if enabled + if plugin_settings.get('enable_vm_documents'): + menuitem.append( + PluginMenuItem( + link='plugins:netbox_documents:vmdocument_list', + link_text='VM Documents', + buttons=[PluginMenuButton( + link='plugins:netbox_documents:vmdocument_add', + title='Add', + icon_class='mdi mdi-plus-thick', + color=ButtonColorChoices.GREEN + )] + ) + ) + else: # Fall back to pre 3.4 navigation option diff --git a/netbox_documents/search.py b/netbox_documents/search.py index 52ececd..60c1b32 100644 --- a/netbox_documents/search.py +++ b/netbox_documents/search.py @@ -1,5 +1,5 @@ from netbox.search import SearchIndex -from .models import SiteDocument, LocationDocument, DeviceDocument, DeviceTypeDocument, CircuitDocument +from .models import SiteDocument, LocationDocument, DeviceDocument, DeviceTypeDocument, CircuitDocument, VMDocument from django.conf import settings # If we run NB 3.4+ register search indexes @@ -44,5 +44,13 @@ class DeviceDocumentIndex(SearchIndex): ("comments", 5000), ) + class VMDocumentIndex(SearchIndex): + model = VMDocument + fields = ( + ("name", 100), + ("document", 500), + ("comments", 5000), + ) + # Register indexes - indexes = [SiteDocumentIndex, LocationDocumentIndex, CircuitDocumentIndex, DeviceTypeDocumentIndex, DeviceDocumentIndex] \ No newline at end of file + indexes = [SiteDocumentIndex, LocationDocumentIndex, CircuitDocumentIndex, DeviceTypeDocumentIndex, DeviceDocumentIndex, VMDocumentIndex] \ No newline at end of file diff --git a/netbox_documents/tables.py b/netbox_documents/tables.py index 066f03d..1d771ee 100644 --- a/netbox_documents/tables.py +++ b/netbox_documents/tables.py @@ -1,7 +1,7 @@ import django_tables2 as tables from netbox.tables import NetBoxTable, columns -from .models import SiteDocument, LocationDocument, DeviceDocument, DeviceTypeDocument, CircuitDocument +from .models import SiteDocument, LocationDocument, DeviceDocument, DeviceTypeDocument, CircuitDocument, VMDocument SITE_DOCUMENT_LINK = """ {% if record.size %} @@ -43,6 +43,14 @@ {% endif %} """ +VM_DOCUMENT_LINK = """ +{% if record.size %} + {% firstof record.name record.filename %} (View Document) +{% else %} + {% firstof record.name record.filename %} (View External Document) +{% endif %} +""" + class SiteDocumentTable(NetBoxTable): name = tables.TemplateColumn(template_code=SITE_DOCUMENT_LINK) document_type = columns.ChoiceFieldColumn() @@ -125,4 +133,20 @@ class CircuitDocumentTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = CircuitDocument fields = ('pk', 'id', 'name', 'document_type', 'size', 'filename', 'circuit', 'comments', 'actions', 'created', 'last_updated', 'tags') - default_columns = ('name', 'document_type', 'circuit', 'tags') \ No newline at end of file + default_columns = ('name', 'document_type', 'circuit', 'tags') + +class VMDocumentTable(NetBoxTable): + name = tables.TemplateColumn(template_code=VM_DOCUMENT_LINK) + document_type = columns.ChoiceFieldColumn() + vm = tables.Column( + linkify=True + ) + + tags = columns.TagColumn( + url_name='dcim:sitegroup_list' + ) + + class Meta(NetBoxTable.Meta): + model = VMDocument + fields = ('pk', 'id', 'name', 'document_type', 'size', 'filename', 'vm', 'comments', 'actions', 'created', 'last_updated', 'tags') + default_columns = ('name', 'document_type', 'vm', 'tags') \ No newline at end of file diff --git a/netbox_documents/template_content.py b/netbox_documents/template_content.py index dce5ad4..8c9bd98 100644 --- a/netbox_documents/template_content.py +++ b/netbox_documents/template_content.py @@ -1,6 +1,6 @@ from extras.plugins import PluginTemplateExtension from django.conf import settings -from .models import SiteDocument, LocationDocument, DeviceDocument, DeviceTypeDocument, CircuitDocument +from .models import SiteDocument, LocationDocument, DeviceDocument, DeviceTypeDocument, CircuitDocument, VMDocument plugin_settings = settings.PLUGINS_CONFIG.get('netbox_documents', {}) @@ -132,4 +132,29 @@ def right_page(self): else: return "" -template_extensions = [SiteDocumentList, LocationDocumentList, DeviceDocumentList, DeviceTypeDocumentList, CircuitDocumentList] \ No newline at end of file +class VMDocumentList(PluginTemplateExtension): + model = 'virtualization.virtualmachine' + + def left_page(self): + + if plugin_settings.get('enable_vm_documents') and plugin_settings.get('vm_documents_location') == 'left': + + return self.render('netbox_documents/vmdocument_include.html', extra_context={ + 'vm_documents': VMDocument.objects.filter(vm=self.context['object']), + }) + + else: + return "" + + def right_page(self): + + if plugin_settings.get('enable_vm_documents') and plugin_settings.get('vm_documents_location') == 'right': + + return self.render('netbox_documents/vmdocument_include.html', extra_context={ + 'vm_documents': VMDocument.objects.filter(vm=self.context['object']), + }) + + else: + return "" + +template_extensions = [SiteDocumentList, LocationDocumentList, DeviceDocumentList, DeviceTypeDocumentList, CircuitDocumentList, VMDocumentList] diff --git a/netbox_documents/templates/netbox_documents/vmdocument.html b/netbox_documents/templates/netbox_documents/vmdocument.html new file mode 100644 index 0000000..5795ec5 --- /dev/null +++ b/netbox_documents/templates/netbox_documents/vmdocument.html @@ -0,0 +1,56 @@ +{% extends 'generic/object.html' %} +{% load helpers %} +{% load plugins %} + +{% block content %} +
+
+
+
VM Document
+
+ + + + + + + + + + + + + + {% if object.external_url %} + + + + + {% else %} + + + + + + + + + {% endif %} +
Name{{ object.name|placeholder }}
VM{{ object.vm }}
Document Type{% badge object.get_document_type_display bg_color=object.get_document_type_color %}
External URL{{ object.external_url }}
Filename{{ object.filename }}
Size{{ object.size|filesizeformat }}
+
+
+ {% include 'inc/panels/custom_fields.html' %} + {% plugin_left_page object %} +
+
+ {% include 'inc/panels/tags.html' %} + {% include 'inc/panels/comments.html' %} + {% plugin_right_page object %} +
+
+
+
+ {% plugin_full_width_page object %} +
+
+{% endblock %} \ No newline at end of file diff --git a/netbox_documents/templates/netbox_documents/vmdocument_edit.html b/netbox_documents/templates/netbox_documents/vmdocument_edit.html new file mode 100644 index 0000000..55abccd --- /dev/null +++ b/netbox_documents/templates/netbox_documents/vmdocument_edit.html @@ -0,0 +1,49 @@ +{% extends 'generic/object_edit.html' %} +{% load static %} +{% load form_helpers %} +{% load helpers %} + +{% block form %} +
+ {% render_field form.name %} + +
+
+
+ +
+
+
+
+ {% render_field form.document %} +
+
+ {% render_field form.external_url %} +
+
+ + {% render_field form.document_type %} + {% render_field form.vm %} + +
+
+
Comments
+
+ {% render_field form.comments %} +
+ + {% render_field form.tags %} +
+
+{% endblock %} diff --git a/netbox_documents/templates/netbox_documents/vmdocument_include.html b/netbox_documents/templates/netbox_documents/vmdocument_include.html new file mode 100644 index 0000000..4306a40 --- /dev/null +++ b/netbox_documents/templates/netbox_documents/vmdocument_include.html @@ -0,0 +1,49 @@ +{% load helpers %} + +
+
+ Documents +
+
+{% if vm_documents %} + + + + + + + + {% for document in vm_documents %} + + + + + + + {% endfor %} +
NameSizeType
+ {% firstof document.name document.filename document.external_url %} + {% if document.size %}{{ document.size|filesizeformat }}{% else %}{% endif %}{% badge document.get_document_type_display bg_color=document.get_document_type_color %} + + + + + + + + + +
+ {% else %} +
+ None +
+ {% endif %} +
+ +
\ No newline at end of file diff --git a/netbox_documents/urls.py b/netbox_documents/urls.py index 38d7cb8..182dd2f 100644 --- a/netbox_documents/urls.py +++ b/netbox_documents/urls.py @@ -54,6 +54,16 @@ path('circuit-document//changelog/', ObjectChangeLogView.as_view(), name='circuitdocument_changelog', kwargs={ 'model': models.CircuitDocument }), + + # VMDocument + path('vm-document/', views.VMDocumentListView.as_view(), name='vmdocument_list'), + path('vm-document/add/', views.VMDocumentEditView.as_view(), name='vmdocument_add'), + path('vm-document//', views.VMDocumentView.as_view(), name='vmdocument'), + path('vm-document//edit/', views.VMDocumentEditView.as_view(), name='vmdocument_edit'), + path('vm-document//delete/', views.VMDocumentDeleteView.as_view(), name='vmdocument_delete'), + path('vm-document//changelog/', ObjectChangeLogView.as_view(), name='vmdocument_changelog', kwargs={ + 'model': models.VMDocument + }), ) \ No newline at end of file diff --git a/netbox_documents/utils.py b/netbox_documents/utils.py index ec34046..5e9051d 100644 --- a/netbox_documents/utils.py +++ b/netbox_documents/utils.py @@ -18,6 +18,8 @@ def file_upload(instance, filename): path_prepend = instance.device_type.id if hasattr(instance, 'circuit'): path_prepend = instance.circuit.id + if hasattr(instance, 'vm'): + path_prepend = instance.vm.id # Rename the file to the provided name, if any. Attempt to preserve the file extension. extension = filename.rsplit('.')[-1].lower() diff --git a/netbox_documents/views.py b/netbox_documents/views.py index ad6645b..fdb5672 100644 --- a/netbox_documents/views.py +++ b/netbox_documents/views.py @@ -98,4 +98,24 @@ class CircuitDocumentEditView(generic.ObjectEditView): template_name = 'netbox_documents/circuitdocument_edit.html' class CircuitDocumentDeleteView(generic.ObjectDeleteView): - queryset = models.CircuitDocument.objects.all() \ No newline at end of file + queryset = models.CircuitDocument.objects.all() + + +### VMDocument +class VMDocumentView(generic.ObjectView): + queryset = models.VMDocument.objects.all() + +class VMDocumentListView(generic.ObjectListView): + queryset = models.VMDocument.objects.all() + table = tables.VMDocumentTable + filterset = filtersets.VMDocumentFilterSet + filterset_form = forms.VMDocumentFilterForm + +class VMDocumentEditView(generic.ObjectEditView): + queryset = models.VMDocument.objects.all() + form = forms.VMDocumentForm + + template_name = 'netbox_documents/vmdocument_edit.html' + +class VMDocumentDeleteView(generic.ObjectDeleteView): + queryset = models.VMDocument.objects.all() \ No newline at end of file