From 9fa5004a35c5e5ac4f2c06e49c5cfabf121b13a3 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 6 Mar 2020 16:33:43 -0500 Subject: [PATCH] Closes #4324: Add CSV import view for services --- docs/release-notes/version-2.7.md | 1 + netbox/ipam/forms.py | 31 ++++++++++++++++++++++++++++++ netbox/ipam/models.py | 2 +- netbox/ipam/tests/test_views.py | 10 +++++++--- netbox/ipam/urls.py | 1 + netbox/ipam/views.py | 7 +++++++ netbox/templates/inc/nav_menu.html | 5 +++++ 7 files changed, 53 insertions(+), 4 deletions(-) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index 8d95390bc..dc32f2a57 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -3,6 +3,7 @@ ## Enhancements * [#4323](https://github.com/netbox-community/netbox/issues/4323) - Add bulk edit view for power panels +* [#4324](https://github.com/netbox-community/netbox/issues/4324) - Add CSV import view for services --- diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index 8156ae4aa..f9c6fe515 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -1382,6 +1382,37 @@ class ServiceFilterForm(BootstrapMixin, CustomFieldFilterForm): tag = TagFilterField(model) +class ServiceCSVForm(CustomFieldModelCSVForm): + device = FlexibleModelChoiceField( + queryset=Device.objects.all(), + required=False, + to_field_name='name', + help_text='Name or ID of device', + error_messages={ + 'invalid_choice': 'Device not found.', + } + ) + virtual_machine = FlexibleModelChoiceField( + queryset=VirtualMachine.objects.all(), + required=False, + to_field_name='name', + help_text='Name or ID of virtual machine', + error_messages={ + 'invalid_choice': 'Virtual machine not found.', + } + ) + protocol = CSVChoiceField( + choices=ServiceProtocolChoices, + help_text='IP protocol' + ) + + class Meta: + model = Service + fields = Service.csv_headers + help_texts = { + } + + class ServiceBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=Service.objects.all(), diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py index b4ba92fb5..4cbcb4bf0 100644 --- a/netbox/ipam/models.py +++ b/netbox/ipam/models.py @@ -1027,7 +1027,7 @@ class Service(ChangeLoggedModel, CustomFieldModel): tags = TaggableManager(through=TaggedItem) - csv_headers = ['device', 'virtual_machine', 'name', 'protocol', 'description'] + csv_headers = ['device', 'virtual_machine', 'name', 'protocol', 'port', 'description'] class Meta: ordering = ('protocol', 'port', 'pk') # (protocol, port) may be non-unique diff --git a/netbox/ipam/tests/test_views.py b/netbox/ipam/tests/test_views.py index 66e649005..ba9db74f7 100644 --- a/netbox/ipam/tests/test_views.py +++ b/netbox/ipam/tests/test_views.py @@ -334,9 +334,6 @@ class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase): class ServiceTestCase(ViewTestCases.PrimaryObjectViewTestCase): model = Service - # Disable inapplicable tests - test_import_objects = None - # TODO: Resolve URL for Service creation test_create_object = None @@ -366,6 +363,13 @@ class ServiceTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'tags': 'Alpha,Bravo,Charlie', } + cls.csv_data = ( + "device,name,protocol,port,description", + "Device 1,Service 1,TCP,1,First service", + "Device 1,Service 2,TCP,2,Second service", + "Device 1,Service 3,UDP,3,Third service", + ) + cls.bulk_edit_data = { 'protocol': ServiceProtocolChoices.PROTOCOL_UDP, 'port': 888, diff --git a/netbox/ipam/urls.py b/netbox/ipam/urls.py index 604287f24..f1211473e 100644 --- a/netbox/ipam/urls.py +++ b/netbox/ipam/urls.py @@ -94,6 +94,7 @@ urlpatterns = [ # Services path('services/', views.ServiceListView.as_view(), name='service_list'), + 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'), path('services//', views.ServiceView.as_view(), name='service'), diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 053098f0b..8a3057faa 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -1015,6 +1015,13 @@ class ServiceCreateView(PermissionRequiredMixin, ObjectEditView): return service.parent.get_absolute_url() +class ServiceBulkImportView(PermissionRequiredMixin, BulkImportView): + permission_required = 'ipam.add_service' + model_form = forms.ServiceCSVForm + table = tables.ServiceTable + default_return_url = 'ipam:service_list' + + class ServiceEditView(ServiceCreateView): permission_required = 'ipam.change_service' diff --git a/netbox/templates/inc/nav_menu.html b/netbox/templates/inc/nav_menu.html index 900d783f6..355d0d8bb 100644 --- a/netbox/templates/inc/nav_menu.html +++ b/netbox/templates/inc/nav_menu.html @@ -338,6 +338,11 @@
  • + {% if perms.ipam.add_vservice %} +
    + +
    + {% endif %} Services