diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 0978747d1..6efdb63f0 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -4,17 +4,15 @@ from django.core.paginator import EmptyPage, PageNotAnInteger from django.db import transaction from django.db.models import Prefetch from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, modelformset_factory -from django.http import HttpResponse 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.utils.translation import gettext_lazy as _ from django.views.generic import View -from jinja2.exceptions import TemplateError from circuits.models import Circuit, CircuitTermination -from extras.views import ObjectConfigContextView +from extras.views import ObjectConfigContextView, ObjectRenderConfigView from ipam.models import ASN, IPAddress, Prefix, VLANGroup from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable from netbox.constants import DEFAULT_ACTION_PERMISSIONS @@ -2253,54 +2251,14 @@ class DeviceConfigContextView(ObjectConfigContextView): @register_model_view(Device, 'render-config') -class DeviceRenderConfigView(generic.ObjectView): +class DeviceRenderConfigView(ObjectRenderConfigView): queryset = Device.objects.all() - template_name = 'dcim/device/render_config.html' + base_template = 'dcim/device/base.html' tab = ViewTab( label=_('Render Config'), - weight=2100 + 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'): - content = context['rendered_config'] or context['error_message'] - response = HttpResponse(content, 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({'device': instance}) - - # Render the config template - rendered_config = None - error_message = None - if config_template := instance.get_config_template(): - try: - rendered_config = config_template.render(context=context_data) - except TemplateError as e: - error_message = _("An error occurred while rendering the template: {error}").format(error=e) - - return { - 'config_template': config_template, - 'context_data': context_data, - 'rendered_config': rendered_config, - 'error_message': error_message, - } - @register_model_view(Device, 'virtual-machines') class DeviceVirtualMachinesView(generic.ObjectChildrenView): diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 3672e5336..86e7f214a 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -10,6 +10,7 @@ from django.utils import timezone from django.utils.module_loading import import_string from django.utils.translation import gettext as _ from django.views.generic import View +from jinja2.exceptions import TemplateError from core.choices import ManagedFileRootPathChoices from core.forms import ManagedFileForm @@ -885,6 +886,61 @@ class ConfigTemplateBulkSyncDataView(generic.BulkSyncDataView): queryset = ConfigTemplate.objects.all() +class ObjectRenderConfigView(generic.ObjectView): + base_template = None + template_name = 'extras/object_render_config.html' + + 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'): + content = context['rendered_config'] or context['error_message'] + response = HttpResponse(content, 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_data(self, request, instance): + return { + f'{instance._meta.model_name}': instance, + } + + def get_extra_context(self, request, instance): + # Compile context data + context_data = instance.get_config_context() + context_data.update(self.get_extra_context_data(request, instance)) + + # Render the config template + rendered_config = None + error_message = None + if config_template := instance.get_config_template(): + try: + rendered_config = config_template.render(context=context_data) + except TemplateError as e: + error_message = _("An error occurred while rendering the template: {error}").format(error=e) + + return { + 'base_template': self.base_template, + 'config_template': config_template, + 'context_data': context_data, + 'rendered_config': rendered_config, + 'error_message': error_message, + } + + # # Image attachments # diff --git a/netbox/templates/dcim/device/render_config.html b/netbox/templates/extras/object_render_config.html similarity index 95% rename from netbox/templates/dcim/device/render_config.html rename to netbox/templates/extras/object_render_config.html index ab2f1c531..b28146ff4 100644 --- a/netbox/templates/dcim/device/render_config.html +++ b/netbox/templates/extras/object_render_config.html @@ -1,4 +1,5 @@ -{% extends 'dcim/device/base.html' %} +{% extends base_template %} +{% load helpers %} {% load static %} {% load i18n %} @@ -67,7 +68,7 @@ {% endif %} {% else %}
- {% trans "No configuration template has been assigned for this device." %} + {% trans "No configuration template has been assigned." %}
{% endif %} diff --git a/netbox/templates/virtualization/virtualmachine/render_config.html b/netbox/templates/virtualization/virtualmachine/render_config.html deleted file mode 100644 index fa6f1723b..000000000 --- a/netbox/templates/virtualization/virtualmachine/render_config.html +++ /dev/null @@ -1,75 +0,0 @@ -{% extends 'virtualization/virtualmachine/base.html' %} -{% load static %} -{% load i18n %} - -{% block title %}{{ object }} - {% trans "Config" %}{% endblock %} - -{% block content %} -
-
-
-

{% trans "Config Template" %}

- - - - - - - - - - - - - -
{% trans "Config Template" %}{{ config_template|linkify|placeholder }}
{% trans "Data Source" %}{{ config_template.data_file.source|linkify|placeholder }}
{% trans "Data File" %}{{ config_template.data_file|linkify|placeholder }}
-
-
-
-
-
-
-
-

- -

-
-
-
{{ context_data|pprint }}
-
-
-
-
-
-
-
-
-
-
- {% if config_template %} - {% if rendered_config %} -
-

- {% trans "Rendered Config" %} - - {% trans "Download" %} - -

-
{{ rendered_config }}
-
- {% else %} -
-

{% trans "Error rendering template" %}

- {% trans error_message %} -
- {% endif %} - {% else %} -
- {% trans "No configuration template has been assigned for this virtual machine." %} -
- {% endif %} -
-
-{% endblock %} diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index 7682d0fc8..343d346e4 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -1,17 +1,15 @@ 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_lazy as _ -from jinja2.exceptions import TemplateError from dcim.filtersets import DeviceFilterSet from dcim.forms import DeviceFilterForm from dcim.models import Device from dcim.tables import DeviceTable -from extras.views import ObjectConfigContextView +from extras.views import ObjectConfigContextView, ObjectRenderConfigView from ipam.models import IPAddress from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable from netbox.constants import DEFAULT_ACTION_PERMISSIONS @@ -427,54 +425,14 @@ class VirtualMachineConfigContextView(ObjectConfigContextView): @register_model_view(VirtualMachine, 'render-config') -class VirtualMachineRenderConfigView(generic.ObjectView): +class VirtualMachineRenderConfigView(ObjectRenderConfigView): queryset = VirtualMachine.objects.all() - template_name = 'virtualization/virtualmachine/render_config.html' + base_template = 'virtualization/virtualmachine/base.html' tab = ViewTab( label=_('Render Config'), - weight=2100 + 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'): - content = context['rendered_config'] or context['error_message'] - response = HttpResponse(content, 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 - error_message = None - if config_template := instance.get_config_template(): - try: - rendered_config = config_template.render(context=context_data) - except TemplateError as e: - error_message = _("An error occurred while rendering the template: {error}").format(error=e) - - return { - 'config_template': config_template, - 'context_data': context_data, - 'rendered_config': rendered_config, - 'error_message': error_message, - } - @register_model_view(VirtualMachine, 'add', detail=False) @register_model_view(VirtualMachine, 'edit')