Repository: netbox-community/netbox
Pull request: Add /api/virtualization/virtual-machines/{id}/render-config/ endpoint
<!--
    Thank you for your interest in contributing to NetBox! Please note that
    our contribution policy requires that a feature request or bug report be
    approved and assigned prior to opening a pull request. This helps avoid
    waste time and effort on a proposed change that we might not be able to
    accept.

    IF YOUR PULL REQUEST DOES NOT REFERENCE AN ISSUE WHICH HAS BEEN ASSIGNED
    TO YOU, IT WILL BE CLOSED AUTOMATICALLY.

    Please specify your assigned issue number on the line below.
-->
### Fixes: #13808

<!--
    Add /api/virtualization/virtual-machines/{id}/render-config/ endpoint
-->


Problem statement:
Add render-config API endpoint for virtual machine
### NetBox version

v3.6.2-dev

### Feature type

New functionality

### Proposed functionality

Add config template rendering API endpoint for virtual machine.
Config template rendering feature was added in the interface, but there's no corresponding API endpoint: /api/virtualization/virtual-machines/{id}/render-config/


### Use case

Allow config template rendering for virtual machines using NetBox API.


### Database changes

_No response_

### External dependencies

_No response_

Hints from issue discussion:
Can I take  this issue on me?
@LuPo do you think you'd be able to submit a PR for it by early next week? We're due to release the first v3.7 beta by the end of November, so we'll need to have all milestone issues completed very soon.
Yes, I will submit a PR for review today.
@LuPo awesome, thank you for stepping up!

Commit message: Add /api/virtualization/virtual-machines/{id}/render-config/ endpoint

Patch to review:
diff --git a/netbox/virtualization/api/views.py b/netbox/virtualization/api/views.py
index 5b9cf411733..d785c091759 100644
--- a/netbox/virtualization/api/views.py
+++ b/netbox/virtualization/api/views.py
@@ -1,7 +1,12 @@
+from rest_framework.decorators import action
+from rest_framework.renderers import JSONRenderer
+from rest_framework.response import Response
 from rest_framework.routers import APIRootView
+from rest_framework.status import HTTP_400_BAD_REQUEST
 
 from dcim.models import Device
-from extras.api.mixins import ConfigContextQuerySetMixin
+from extras.api.mixins import ConfigContextQuerySetMixin, ConfigTemplateRenderMixin
+from netbox.api.renderers import TextRenderer
 from netbox.api.viewsets import NetBoxModelViewSet
 from utilities.utils import count_related
 from virtualization import filtersets
@@ -52,9 +57,9 @@ class ClusterViewSet(NetBoxModelViewSet):
 # Virtual machines
 #
 
-class VirtualMachineViewSet(ConfigContextQuerySetMixin, NetBoxModelViewSet):
+class VirtualMachineViewSet(ConfigContextQuerySetMixin, ConfigTemplateRenderMixin, NetBoxModelViewSet):
     queryset = VirtualMachine.objects.prefetch_related(
-        'site', 'cluster', 'device', 'role', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'tags'
+        'site', 'cluster', 'device', 'role', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'config_template', 'tags'
     )
     filterset_class = filtersets.VirtualMachineFilterSet
 
@@ -78,6 +83,23 @@ def get_serializer_class(self):
 
         return serializers.VirtualMachineWithConfigContextSerializer
 
+    @action(detail=True, methods=['post'], url_path='render-config', renderer_classes=[JSONRenderer, TextRenderer])
+    def render_config(self, request, pk):
+        """
+        Resolve and render the preferred ConfigTemplate for this Device.
+        """
+        instance = self.get_object()
+        configtemplate = instance.get_config_template()
+        if not configtemplate:
+            return Response({'error': 'No config template found for this virtual machine.'}, status=HTTP_400_BAD_REQUEST)
+
+        # Compile context data
+        context_data = instance.get_config_context()
+        context_data.update(request.data)
+        context_data.update({'virtualmachine': instance})
+
+        return self.render_configtemplate(request, configtemplate, context_data)
+
 
 class VMInterfaceViewSet(NetBoxModelViewSet):
     queryset = VMInterface.objects.prefetch_related(


Code review comments:
diff_hunk: @@ -78,6 +83,23 @@ def get_serializer_class(self):
 
         return serializers.VirtualMachineWithConfigContextSerializer
 
+    @action(detail=True, methods=['post'], url_path='render-config', renderer_classes=[JSONRenderer, TextRenderer])
+    def render_config(self, request, pk):
+        """
+        Resolve and render the preferred ConfigTemplate for this Device.
path: netbox/virtualization/api/views.py
text: @user1:
Docstring should mention "..this Virtual Machine" instead of "...this Device"