From bb5ded203937bea67cca23511640587961aac769 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 13 Jan 2022 10:32:42 -0500 Subject: [PATCH] Enable creating services from templates in the UI --- netbox/ipam/forms/models.py | 34 +++++++++++ netbox/ipam/urls.py | 2 +- netbox/ipam/views.py | 6 ++ netbox/templates/ipam/service_create.html | 74 +++++++++++++++++++++++ 4 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 netbox/templates/ipam/service_create.html diff --git a/netbox/ipam/forms/models.py b/netbox/ipam/forms/models.py index 9aabd4d54..34c67773f 100644 --- a/netbox/ipam/forms/models.py +++ b/netbox/ipam/forms/models.py @@ -31,6 +31,7 @@ __all__ = ( 'RoleForm', 'RouteTargetForm', 'ServiceForm', + 'ServiceCreateForm', 'ServiceTemplateForm', 'VLANForm', 'VLANGroupForm', @@ -880,3 +881,36 @@ class ServiceForm(CustomFieldModelForm): 'protocol': StaticSelect(), 'ipaddresses': StaticSelectMultiple(), } + + +class ServiceCreateForm(ServiceForm): + service_template = DynamicModelChoiceField( + queryset=ServiceTemplate.objects.all(), + required=False + ) + + class Meta(ServiceForm.Meta): + fields = [ + 'device', 'virtual_machine', 'service_template', 'name', 'protocol', 'ports', 'ipaddresses', 'description', + 'tags', + ] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Fields which may be populated from a ServiceTemplate are not required + for field in ('name', 'protocol', 'ports'): + self.fields[field].required = False + del(self.fields[field].widget.attrs['required']) + + def clean(self): + if self.cleaned_data['service_template']: + # Create a new Service from the specified template + service_template = self.cleaned_data['service_template'] + self.cleaned_data['name'] = service_template.name + self.cleaned_data['protocol'] = service_template.protocol + self.cleaned_data['ports'] = service_template.ports + if not self.cleaned_data['description']: + self.cleaned_data['description'] = service_template.description + elif not all(self.cleaned_data[f] for f in ('name', 'protocol', 'ports')): + raise forms.ValidationError("Must specify name, protocol, and port(s) if not using a service template.") diff --git a/netbox/ipam/urls.py b/netbox/ipam/urls.py index fe8cfd150..0a4eddc6c 100644 --- a/netbox/ipam/urls.py +++ b/netbox/ipam/urls.py @@ -176,7 +176,7 @@ urlpatterns = [ # Services path('services/', views.ServiceListView.as_view(), name='service_list'), - path('services/add/', views.ServiceEditView.as_view(), name='service_add'), + path('services/add/', views.ServiceCreateView.as_view(), name='service_add'), path('services/import/', views.ServiceBulkImportView.as_view(), name='service_import'), path('services/edit/', views.ServiceBulkEditView.as_view(), name='service_bulk_edit'), path('services/delete/', views.ServiceBulkDeleteView.as_view(), name='service_bulk_delete'), diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index f5aa0a7d7..35d5cf502 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -1087,6 +1087,12 @@ class ServiceView(generic.ObjectView): queryset = Service.objects.prefetch_related('ipaddresses') +class ServiceCreateView(generic.ObjectEditView): + queryset = Service.objects.all() + model_form = forms.ServiceCreateForm + template_name = 'ipam/service_create.html' + + class ServiceEditView(generic.ObjectEditView): queryset = Service.objects.prefetch_related('ipaddresses') model_form = forms.ServiceForm diff --git a/netbox/templates/ipam/service_create.html b/netbox/templates/ipam/service_create.html new file mode 100644 index 000000000..022821bcf --- /dev/null +++ b/netbox/templates/ipam/service_create.html @@ -0,0 +1,74 @@ +{% extends 'generic/object_edit.html' %} +{% load form_helpers %} + +{% block form %} +
+
+
Service
+
+ + {# Device/VM selection #} +
+
+ +
+
+
+
+ {% render_field form.device %} +
+
+ {% render_field form.virtual_machine %} +
+
+ + {# Template or custom #} +
+
+ +
+
+
+
+ {% render_field form.service_template %} +
+
+ {% render_field form.name %} + {% render_field form.protocol %} + {% render_field form.ports %} +
+
+ {% render_field form.ipaddresses %} + {% render_field form.description %} + {% render_field form.tags %} +
+ + {% if form.custom_fields %} +
+
Custom Fields
+
+ {% render_custom_fields form %} + {% endif %} +{% endblock %}