diff --git a/netbox/dcim/tests/test_api.py b/netbox/dcim/tests/test_api.py index 61928e562..f2e9406bf 100644 --- a/netbox/dcim/tests/test_api.py +++ b/netbox/dcim/tests/test_api.py @@ -1633,6 +1633,32 @@ class DeviceTest(APIViewTestCases.APIViewTestCase): response = self.client.post(url, {}, format='json', HTTP_AUTHORIZATION=token_header) self.assertHttpStatus(response, status.HTTP_200_OK) + def test_render_config_with_config_template_id(self): + default_template = ConfigTemplate.objects.create( + name='Default Template', + template_code='Default config for {{ device.name }}' + ) + override_template = ConfigTemplate.objects.create( + name='Override Template', + template_code='Override config for {{ device.name }}' + ) + + device = Device.objects.first() + device.config_template = default_template + device.save() + + self.add_permissions('dcim.render_config_device', 'dcim.view_device') + url = reverse('dcim-api:device-render-config', kwargs={'pk': device.pk}) + + # Render with override template + response = self.client.post(url, {'config_template_id': override_template.pk}, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertEqual(response.data['content'], f'Override config for {device.name}') + + # Render with invalid config_template_id + response = self.client.post(url, {'config_template_id': 999999}, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + class ModuleTest(APIViewTestCases.APIViewTestCase): model = Module diff --git a/netbox/dcim/tests/test_views.py b/netbox/dcim/tests/test_views.py index a67cea127..56fb15fc4 100644 --- a/netbox/dcim/tests/test_views.py +++ b/netbox/dcim/tests/test_views.py @@ -2362,6 +2362,32 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase): self.remove_permissions('dcim.view_device') self.assertHttpStatus(self.client.get(url), 403) + def test_device_renderconfig_with_config_template_id(self): + default_template = ConfigTemplate.objects.create( + name='Default Template', + template_code='Default config for {{ device.name }}' + ) + override_template = ConfigTemplate.objects.create( + name='Override Template', + template_code='Override config for {{ device.name }}' + ) + device = Device.objects.first() + device.config_template = default_template + device.save() + + self.add_permissions('dcim.view_device', 'dcim.render_config_device') + url = reverse('dcim:device_render-config', kwargs={'pk': device.pk}) + + # Render with override config_template_id + response = self.client.get(url, {'config_template_id': override_template.pk}) + self.assertHttpStatus(response, 200) + self.assertIn(b'Override config for', response.content) + + # Render with invalid config_template_id still returns 200 with error message + response = self.client.get(url, {'config_template_id': 999999}) + self.assertHttpStatus(response, 200) + self.assertIn(b'Error rendering template', response.content) + def test_device_role_display_colored(self): parent_role = DeviceRole.objects.create(name='Parent Role', slug='parent-role', color='111111') child_role = DeviceRole.objects.create(name='Child Role', slug='child-role', parent=parent_role, color='aa00bb') diff --git a/netbox/extras/api/mixins.py b/netbox/extras/api/mixins.py index a98218b78..506d192bd 100644 --- a/netbox/extras/api/mixins.py +++ b/netbox/extras/api/mixins.py @@ -7,6 +7,7 @@ from rest_framework.status import HTTP_400_BAD_REQUEST from netbox.api.authentication import TokenWritePermission from netbox.api.renderers import TextRenderer +from extras.models import ConfigTemplate from .serializers import ConfigTemplateSerializer __all__ = ( @@ -85,15 +86,25 @@ class RenderConfigMixin(ConfigTemplateRenderMixin): instance = self.get_object() object_type = instance._meta.model_name - configtemplate = instance.get_config_template() - if not configtemplate: - return Response({ - 'error': f'No config template found for this {object_type}.' - }, status=HTTP_400_BAD_REQUEST) + + # Check for an optional config_template_id override in the request data + if config_template_id := request.data.get('config_template_id'): + try: + configtemplate = ConfigTemplate.objects.get(pk=config_template_id) + except ConfigTemplate.DoesNotExist: + return Response({ + 'error': f'Config template with ID {config_template_id} not found.' + }, status=HTTP_400_BAD_REQUEST) + else: + configtemplate = instance.get_config_template() + if not configtemplate: + return Response({ + 'error': f'No config template found for this {object_type}.' + }, status=HTTP_400_BAD_REQUEST) # Compile context data context_data = instance.get_config_context() - context_data.update(request.data) + context_data.update({k: v for k, v in request.data.items() if k != 'config_template_id'}) context_data.update({object_type: instance}) return self.render_configtemplate(request, configtemplate, context_data) diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 99e8d976a..8df93e50f 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -1268,10 +1268,20 @@ class ObjectRenderConfigView(generic.ObjectView): context_data = instance.get_config_context() context_data.update(self.get_extra_context_data(request, instance)) + # Check for an optional config_template_id override in the query params + config_template = None + error_message = '' + if config_template_id := request.GET.get('config_template_id'): + try: + config_template = ConfigTemplate.objects.get(pk=config_template_id) + except (ConfigTemplate.DoesNotExist, ValueError): + error_message = _("Config template with ID {id} not found.").format(id=config_template_id) + else: + config_template = instance.get_config_template() + # Render the config template rendered_config = None - error_message = '' - if config_template := instance.get_config_template(): + if config_template and not error_message: try: rendered_config = config_template.render(context=context_data) except TemplateError as e: diff --git a/netbox/templates/extras/object_render_config.html b/netbox/templates/extras/object_render_config.html index aec25cd64..e7bd2fd9b 100644 --- a/netbox/templates/extras/object_render_config.html +++ b/netbox/templates/extras/object_render_config.html @@ -49,13 +49,18 @@
{{ rendered_config }}