Implemented built-in CSV export for IPAM objects

This commit is contained in:
Jeremy Stretch 2016-06-16 12:08:50 -04:00
parent 4bb9363e81
commit 66600ef984
8 changed files with 80 additions and 66 deletions

View File

@ -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]

View 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 %}

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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: