mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-23 04:22:01 -06:00
Enable creating services from templates in the UI
This commit is contained in:
parent
97e7ef9a3f
commit
bb5ded2039
@ -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.")
|
||||
|
@ -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'),
|
||||
|
@ -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
|
||||
|
74
netbox/templates/ipam/service_create.html
Normal file
74
netbox/templates/ipam/service_create.html
Normal file
@ -0,0 +1,74 @@
|
||||
{% extends 'generic/object_edit.html' %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block form %}
|
||||
<div class="field-group my-5">
|
||||
<div class="row mb-2">
|
||||
<h5 class="offset-sm-3">Service</h5>
|
||||
</div>
|
||||
|
||||
{# Device/VM selection #}
|
||||
<div class="row mb-2">
|
||||
<div class="offset-sm-3">
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation" class="nav-item">
|
||||
<button role="tab" type="button" id="device_tab" data-bs-toggle="tab" aria-controls="device" data-bs-target="#device" class="nav-link {% if not form.initial.virtual_machine %}active{% endif %}">
|
||||
Device
|
||||
</button>
|
||||
</li>
|
||||
<li role="presentation" class="nav-item">
|
||||
<button role="tab" type="button" id="vm_tab" data-bs-toggle="tab" aria-controls="vm" data-bs-target="#vm" class="nav-link {% if form.initial.virtual_machine %}active{% endif %}">
|
||||
Virtual Machine
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-content p-0 border-0">
|
||||
<div class="tab-pane {% if not form.initial.virtual_machine %}active{% endif %}" id="device" role="tabpanel" aria-labeled-by="device_tab">
|
||||
{% render_field form.device %}
|
||||
</div>
|
||||
<div class="tab-pane {% if form.initial.virtual_machine %}active{% endif %}" id="vm" role="tabpanel" aria-labeled-by="vm_tab">
|
||||
{% render_field form.virtual_machine %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Template or custom #}
|
||||
<div class="row mb-2">
|
||||
<div class="offset-sm-3">
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li role="presentation" class="nav-item">
|
||||
<button role="tab" type="button" id="template_tab" data-bs-toggle="tab" data-bs-target="#template" class="nav-link active">
|
||||
From Template
|
||||
</button>
|
||||
</li>
|
||||
<li role="presentation" class="nav-item">
|
||||
<button role="tab" type="button" id="custom_tab" data-bs-toggle="tab" data-bs-target="#custom" class="nav-link">
|
||||
Custom
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-content p-0 border-0">
|
||||
<div class="tab-pane active" id="template" role="tabpanel" aria-labeled-by="template_tab">
|
||||
{% render_field form.service_template %}
|
||||
</div>
|
||||
<div class="tab-pane" id="custom" role="tabpanel" aria-labeled-by="custom_tab">
|
||||
{% render_field form.name %}
|
||||
{% render_field form.protocol %}
|
||||
{% render_field form.ports %}
|
||||
</div>
|
||||
</div>
|
||||
{% render_field form.ipaddresses %}
|
||||
{% render_field form.description %}
|
||||
{% render_field form.tags %}
|
||||
</div>
|
||||
|
||||
{% if form.custom_fields %}
|
||||
<div class="row mb-2">
|
||||
<h5 class="offset-sm-3">Custom Fields</h5>
|
||||
</div>
|
||||
{% render_custom_fields form %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
Loading…
Reference in New Issue
Block a user