mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-19 09:53:34 -06:00
Implemented built-in CSV export for IPAM objects
This commit is contained in:
parent
4bb9363e81
commit
66600ef984
@ -55,6 +55,13 @@ class VRF(models.Model):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('ipam:vrf', args=[self.pk])
|
return reverse('ipam:vrf', args=[self.pk])
|
||||||
|
|
||||||
|
def to_csv(self):
|
||||||
|
return ','.join([
|
||||||
|
self.name,
|
||||||
|
self.rd,
|
||||||
|
self.description,
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
class RIR(models.Model):
|
class RIR(models.Model):
|
||||||
"""
|
"""
|
||||||
@ -115,6 +122,14 @@ class Aggregate(models.Model):
|
|||||||
self.family = self.prefix.version
|
self.family = self.prefix.version
|
||||||
super(Aggregate, self).save(*args, **kwargs)
|
super(Aggregate, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
def to_csv(self):
|
||||||
|
return ','.join([
|
||||||
|
str(self.prefix),
|
||||||
|
self.rir.name,
|
||||||
|
self.date_added.isoformat() if self.date_added else '',
|
||||||
|
self.description,
|
||||||
|
])
|
||||||
|
|
||||||
def get_utilization(self):
|
def get_utilization(self):
|
||||||
"""
|
"""
|
||||||
Determine the utilization rate of the aggregate prefix and return it as a percentage.
|
Determine the utilization rate of the aggregate prefix and return it as a percentage.
|
||||||
@ -221,6 +236,16 @@ class Prefix(models.Model):
|
|||||||
self.family = self.prefix.version
|
self.family = self.prefix.version
|
||||||
super(Prefix, self).save(*args, **kwargs)
|
super(Prefix, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
def to_csv(self):
|
||||||
|
return ','.join([
|
||||||
|
str(self.prefix),
|
||||||
|
self.vrf.rd if self.vrf else '',
|
||||||
|
self.site.name,
|
||||||
|
self.get_status_display(),
|
||||||
|
self.role.name if self.role else '',
|
||||||
|
self.description,
|
||||||
|
])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def new_subnet(self):
|
def new_subnet(self):
|
||||||
if self.family == 4:
|
if self.family == 4:
|
||||||
@ -267,6 +292,16 @@ class IPAddress(models.Model):
|
|||||||
self.family = self.address.version
|
self.family = self.address.version
|
||||||
super(IPAddress, self).save(*args, **kwargs)
|
super(IPAddress, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
def to_csv(self):
|
||||||
|
return ','.join([
|
||||||
|
str(self.address),
|
||||||
|
self.vrf.rd if self.vrf else '',
|
||||||
|
self.device.name if self.device else '',
|
||||||
|
self.interface.name if self.interface else '',
|
||||||
|
'True' if getattr(self, 'primary_for', False) else '',
|
||||||
|
self.description,
|
||||||
|
])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device(self):
|
def device(self):
|
||||||
if self.interface:
|
if self.interface:
|
||||||
@ -298,5 +333,14 @@ class VLAN(models.Model):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('ipam:vlan', args=[self.pk])
|
return reverse('ipam:vlan', args=[self.pk])
|
||||||
|
|
||||||
|
def to_csv(self):
|
||||||
|
return ','.join([
|
||||||
|
self.site.name,
|
||||||
|
str(self.vid),
|
||||||
|
self.name,
|
||||||
|
self.get_status_display(),
|
||||||
|
self.role.name if self.role else '',
|
||||||
|
])
|
||||||
|
|
||||||
def get_status_class(self):
|
def get_status_class(self):
|
||||||
return STATUS_CHOICE_CLASSES[self.status]
|
return STATUS_CHOICE_CLASSES[self.status]
|
||||||
|
20
netbox/templates/inc/export_button.html
Normal file
20
netbox/templates/inc/export_button.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{% if export_templates %}
|
||||||
|
<div class="btn-group">
|
||||||
|
<button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<span class="glyphicon glyphicon-export" aria-hidden="true"></span>
|
||||||
|
Export {{ obj_type }} <span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a href="?{% if request.GET %}{{ request.GET.urlencode }}&{% endif %}export">CSV (default)</a></li>
|
||||||
|
<li class="divider"></li>
|
||||||
|
{% for et in export_templates %}
|
||||||
|
<li><a href="?{% if request.GET %}{{ request.GET.urlencode }}&{% endif %}export={{ et.name }}">{{ et.name }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<a href="?{% if request.GET %}{{ request.GET.urlencode }}&{% endif %}export" class="btn btn-success">
|
||||||
|
<span class="glyphicon glyphicon-export" aria-hidden="true"></span>
|
||||||
|
Export {{ obj_type }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
@ -12,19 +12,7 @@
|
|||||||
Add an aggregate
|
Add an aggregate
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if export_templates %}
|
{% include 'inc/export_button.html' with obj_type='aggregates' %}
|
||||||
<div class="btn-group">
|
|
||||||
<button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
||||||
<span class="glyphicon glyphicon-export" aria-hidden="true"></span>
|
|
||||||
Export aggregates <span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
{% for et in export_templates %}
|
|
||||||
<li><a href="{% url 'ipam:aggregate_list' %}?{% if request.GET %}{{ request.GET.urlencode }}&{% endif %}export={{ et.name }}">{{ et.name }}</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
<h1>Aggregates</h1>
|
<h1>Aggregates</h1>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -16,19 +16,7 @@
|
|||||||
Import IPs
|
Import IPs
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if export_templates %}
|
{% include 'inc/export_button.html' with obj_type='IPs' %}
|
||||||
<div class="btn-group">
|
|
||||||
<button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
||||||
<span class="glyphicon glyphicon-export" aria-hidden="true"></span>
|
|
||||||
Export IP addresses <span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
{% for et in export_templates %}
|
|
||||||
<li><a href="{% url 'ipam:ipaddress_list' %}?{% if request.GET %}{{ request.GET.urlencode }}&{% endif %}export={{ et.name }}">{{ et.name }}</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
<h1>IP Addresses</h1>
|
<h1>IP Addresses</h1>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -16,19 +16,7 @@
|
|||||||
Import prefixes
|
Import prefixes
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if export_templates %}
|
{% include 'inc/export_button.html' with obj_type='prefixes' %}
|
||||||
<div class="btn-group">
|
|
||||||
<button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
||||||
<span class="glyphicon glyphicon-export" aria-hidden="true"></span>
|
|
||||||
Export prefixes <span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
{% for et in export_templates %}
|
|
||||||
<li><a href="{% url 'ipam:prefix_list' %}?{% if request.GET %}{{ request.GET.urlencode }}&{% endif %}export={{ et.name }}">{{ et.name }}</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
<h1>Prefixes</h1>
|
<h1>Prefixes</h1>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -16,19 +16,7 @@
|
|||||||
Import VLANs
|
Import VLANs
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if export_templates %}
|
{% include 'inc/export_button.html' with obj_type='VLANs' %}
|
||||||
<div class="btn-group">
|
|
||||||
<button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
||||||
<span class="glyphicon glyphicon-export" aria-hidden="true"></span>
|
|
||||||
Export VLANs <span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
{% for et in export_templates %}
|
|
||||||
<li><a href="{% url 'ipam:vlan_list' %}?{% if request.GET %}{{ request.GET.urlencode }}&{% endif %}export={{ et.name }}">{{ et.name }}</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
<h1>VLANs</h1>
|
<h1>VLANs</h1>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -16,19 +16,7 @@
|
|||||||
Import VRFs
|
Import VRFs
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if export_templates %}
|
{% include 'inc/export_button.html' with obj_type='VRFs' %}
|
||||||
<div class="btn-group">
|
|
||||||
<button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
||||||
<span class="glyphicon glyphicon-export" aria-hidden="true"></span>
|
|
||||||
Export VRFs <span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
{% for et in export_templates %}
|
|
||||||
<li><a href="{% url 'ipam:vrf_list' %}?{% if request.GET %}{{ request.GET.urlencode }}&{% endif %}export={{ et.name }}">{{ et.name }}</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
<h1>VRFs</h1>
|
<h1>VRFs</h1>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -6,7 +6,7 @@ from django.contrib.contenttypes.models import ContentType
|
|||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.db import transaction, IntegrityError
|
from django.db import transaction, IntegrityError
|
||||||
from django.db.models import ProtectedError
|
from django.db.models import ProtectedError
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.template import TemplateSyntaxError
|
from django.template import TemplateSyntaxError
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
@ -47,6 +47,16 @@ class ObjectListView(View):
|
|||||||
except TemplateSyntaxError:
|
except TemplateSyntaxError:
|
||||||
messages.error(request, "There was an error rendering the selected export template ({})."
|
messages.error(request, "There was an error rendering the selected export template ({})."
|
||||||
.format(et.name))
|
.format(et.name))
|
||||||
|
# Fall back to built-in CSV export
|
||||||
|
elif 'export' in request.GET and hasattr(model, 'to_csv'):
|
||||||
|
output = '\n'.join([obj.to_csv() for obj in self.queryset])
|
||||||
|
response = HttpResponse(
|
||||||
|
output,
|
||||||
|
content_type='text/csv'
|
||||||
|
)
|
||||||
|
response['Content-Disposition'] = 'attachment; filename="netbox_{}.csv"'\
|
||||||
|
.format(self.queryset.model._meta.verbose_name_plural)
|
||||||
|
return response
|
||||||
|
|
||||||
# Attempt to redirect automatically if the search query returns a single result
|
# Attempt to redirect automatically if the search query returns a single result
|
||||||
if self.redirect_on_single_result and self.queryset.count() == 1 and request.GET:
|
if self.redirect_on_single_result and self.queryset.count() == 1 and request.GET:
|
||||||
|
Loading…
Reference in New Issue
Block a user