diff --git a/netbox/extras/constants.py b/netbox/extras/constants.py index e1e12b78f..116e9a6f1 100644 --- a/netbox/extras/constants.py +++ b/netbox/extras/constants.py @@ -51,7 +51,7 @@ EXPORTTEMPLATE_MODELS = [ 'provider', 'circuit', # Circuits 'site', 'region', 'rack', 'rackgroup', 'manufacturer', 'devicetype', 'device', # DCIM 'consoleport', 'powerport', 'interfaceconnection', 'virtualchassis', # DCIM - 'aggregate', 'prefix', 'ipaddress', 'vlan', 'vrf', # IPAM + 'aggregate', 'prefix', 'ipaddress', 'vlan', 'vrf', 'service', # IPAM 'tenant', # Tenancy 'cluster', 'virtualmachine', # Virtualization ] diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py index 242acf184..103a895ff 100644 --- a/netbox/ipam/models.py +++ b/netbox/ipam/models.py @@ -880,6 +880,7 @@ class Service(ChangeLoggedModel, CustomFieldModel): tags = TaggableManager() serializer = 'ipam.api.serializers.ServiceSerializer' + csv_headers = ['device', 'virtual_machine', 'name', 'protocol', 'description'] class Meta: ordering = ['protocol', 'port'] @@ -901,3 +902,13 @@ class Service(ChangeLoggedModel, CustomFieldModel): raise ValidationError("A service cannot be associated with both a device and a virtual machine.") if not self.device and not self.virtual_machine: raise ValidationError("A service must be associated with either a device or a virtual machine.") + + def to_csv(self): + return ( + self.device.name if self.device else None, + self.virtual_machine.name if self.virtual_machine else None, + self.name, + self.get_protocol_display(), + self.port, + self.description, + ) diff --git a/netbox/templates/ipam/service_list.html b/netbox/templates/ipam/service_list.html index 5cfc3e12d..d2e67a000 100644 --- a/netbox/templates/ipam/service_list.html +++ b/netbox/templates/ipam/service_list.html @@ -1,6 +1,10 @@ {% extends '_base.html' %} +{% load buttons %} {% block content %} +