Clean up related objects for sites, tenants

This commit is contained in:
jeremystretch 2023-01-25 12:26:38 -05:00 committed by jeremystretch
parent 48d6d7279d
commit 0c9e7aa074
5 changed files with 99 additions and 294 deletions

View File

@ -335,19 +335,25 @@ class SiteView(generic.ObjectView):
queryset = Site.objects.prefetch_related('tenant__group')
def get_extra_context(self, request, instance):
stats = {
'location_count': Location.objects.restrict(request.user, 'view').filter(site=instance).count(),
'rack_count': Rack.objects.restrict(request.user, 'view').filter(site=instance).count(),
'device_count': Device.objects.restrict(request.user, 'view').filter(site=instance).count(),
'prefix_count': Prefix.objects.restrict(request.user, 'view').filter(site=instance).count(),
'vlangroup_count': VLANGroup.objects.restrict(request.user, 'view').filter(
related_models = [
# DCIM
Location.objects.restrict(request.user, 'view').filter(site=instance),
Rack.objects.restrict(request.user, 'view').filter(site=instance),
Device.objects.restrict(request.user, 'view').filter(site=instance),
# Virtualization
VirtualMachine.objects.restrict(request.user, 'view').filter(cluster__site=instance),
# IPAM
Prefix.objects.restrict(request.user, 'view').filter(site=instance),
ASN.objects.restrict(request.user, 'view').filter(sites=instance),
VLANGroup.objects.restrict(request.user, 'view').filter(
scope_type=ContentType.objects.get_for_model(Site),
scope_id=instance.pk
).count(),
'vlan_count': VLAN.objects.restrict(request.user, 'view').filter(site=instance).count(),
'circuit_count': Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).distinct().count(),
'vm_count': VirtualMachine.objects.restrict(request.user, 'view').filter(cluster__site=instance).count(),
}
),
VLAN.objects.restrict(request.user, 'view').filter(site=instance),
# Circuits
Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).distinct(),
]
locations = Location.objects.add_related_count(
Location.objects.all(),
Rack,
@ -369,15 +375,9 @@ class SiteView(generic.ObjectView):
parent_bay__isnull=True
).prefetch_related('device_type__manufacturer', 'parent_bay', 'device_role')
asns = ASN.objects.restrict(request.user, 'view').filter(sites=instance)
asn_count = asns.count()
stats.update({'asn_count': asn_count})
return {
'stats': stats,
'related_models': related_models,
'locations': locations,
'asns': asns,
'nonracked_devices': nonracked_devices.order_by('-pk')[:10],
'total_nonracked_devices_count': nonracked_devices.count(),
}

View File

@ -126,112 +126,7 @@
{% plugin_left_page object %}
</div>
<div class="col col-md-6">
<div class="card">
<h5 class="card-header">Related Objects</h5>
<div class="card-body">
<table class="table table-hover attr-table">
<tr>
<th scope="row">Locations</th>
<td class="text-end">
{% if stats.location_count %}
<a href="{% url 'dcim:location_list' %}?site_id={{ object.pk }}">{{ stats.location_count }}</a>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">Racks</th>
<td class="text-end">
{% if stats.rack_count %}
<div class="dropdown">
<button class="btn btn-sm btn-light dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
{{ stats.rack_count }}
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{% url 'dcim:rack_list' %}?site_id={{ object.pk }}">View Racks</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:rack_elevation_list' %}?site_id={{ object.pk }}">View Elevations</a></li>
</ul>
</div>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">Devices</th>
<td class="text-end">
{% if stats.device_count %}
<a href="{% url 'dcim:device_list' %}?site_id={{ object.pk }}">{{ stats.device_count }}</a>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">Virtual Machines</th>
<td class="text-end">
{% if stats.vm_count %}
<a href="{% url 'virtualization:virtualmachine_list' %}?site_id={{ object.pk }}">{{ stats.vm_count }}</a>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">Prefixes</th>
<td class="text-end">
{% if stats.prefix_count %}
<a href="{% url 'ipam:prefix_list' %}?site_id={{ object.pk }}">{{ stats.prefix_count }}</a>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">VLAN Groups</th>
<td class="text-end">
{% if stats.vlangroup_count %}
<a href="{% url 'ipam:vlangroup_list' %}?site={{ object.pk }}">{{ stats.vlangroup_count }}</a>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">VLANs</th>
<td class="text-end">
{% if stats.vlan_count %}
<a href="{% url 'ipam:vlan_list' %}?site_id={{ object.pk }}">{{ stats.vlan_count }}</a>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">ASNs</th>
<td class="text-end">
{% if stats.asn_count %}
<a href="{% url 'ipam:asn_list' %}?site_id={{ object.pk }}">{{ stats.asn_count }}</a>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">Circuits</th>
<td class="text-end">
{% if stats.circuit_count %}
<a href="{% url 'circuits:circuit_list' %}?site_id={{ object.pk }}">{{ stats.circuit_count }}</a>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
</table>
</div>
</div>
{% include 'dcim/inc/nonracked_devices.html' %}
{% include 'inc/panels/related_objects.html' with filter_name='site_id' %}
{% include 'inc/panels/contacts.html' %}
<div class="card">
<h5 class="card-header">Locations</h5>
@ -276,40 +171,13 @@
</div>
{% endif %}
</div>
<div class="card">
<h5 class="card-header">ASNs</h5>
<div class='card-body'>
{% if asns %}
<table class="table table-hover">
<tr>
<th>ASN</th>
<th>Description</th>
</tr>
{% for asn in asns %}
<tr>
<td>{{ asn|linkify }}</td>
<td>{{ asn.description|placeholder }}</td>
</tr>
{% endfor %}
</table>
{% else %}
<span class="text-muted">None</span>
{% endif %}
</div>
{% if perms.ipam.add_asn %}
<div class="card-footer text-end noprint">
<a href="{% url 'ipam:asn_add' %}?sites={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary btn-sm">
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add an ASN
</a>
</div>
{% endif %}
</div>
{% include 'inc/panels/image_attachments.html' %}
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col col-md-12">
{% include 'dcim/inc/nonracked_devices.html' %}
{% plugin_full_width_page object %}
</div>
</div>

View File

@ -0,0 +1,21 @@
{% load helpers %}
<div class="card">
<h5 class="card-header">Related Objects</h5>
<ul class="list-group list-group-flush">
{% for qs in related_models %}
{% with viewname=qs.model|viewname:"list" %}
<a href="{% url viewname %}?{{ filter_name }}={{ object.pk }}" class="list-group-item list-group-item-action d-flex justify-content-between">
{{ qs.model|meta:"verbose_name_plural"|bettertitle }}
{% with count=qs.count %}
{% if count %}
<span class="badge bg-primary rounded-pill">{{ count }}</span>
{% else %}
<span class="badge bg-light rounded-pill">&mdash;</span>
{% endif %}
{% endwith %}
</a>
{% endwith %}
{% endfor %}
</ul>
</div>

View File

@ -10,127 +10,37 @@
{% endblock breadcrumbs %}
{% block content %}
<div class="row">
<div class="col col-md-7">
<div class="card">
<h5 class="card-header">
Tenant
</h5>
<div class="card-body">
<table class="table table-hover attr-table">
<tr>
<td>Group</td>
<td>{{ object.group|linkify|placeholder }}</td>
</tr>
<tr>
<td>Description</td>
<td>{{ object.description|placeholder }}</td>
</tr>
</table>
</div>
<div class="row">
<div class="col col-md-7">
<div class="card">
<h5 class="card-header">Tenant</h5>
<div class="card-body">
<table class="table table-hover attr-table">
<tr>
<td>Group</td>
<td>{{ object.group|linkify|placeholder }}</td>
</tr>
<tr>
<td>Description</td>
<td>{{ object.description|placeholder }}</td>
</tr>
</table>
</div>
{% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/tags.html' %}
{% include 'inc/panels/comments.html' %}
{% include 'inc/panels/contacts.html' %}
{% plugin_left_page object %}
</div>
<div class="col col-md-5">
<div class="card">
<h5 class="card-header">
Stats
</h5>
<div class="row card-body">
<div class="col col-md-4 text-center">
<h2><a href="{% url 'dcim:site_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.site_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.site_count }}</a></h2>
<p>Sites</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'dcim:rack_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.rack_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.rack_count }}</a></h2>
<p>Racks</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'dcim:rackreservation_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.rackreservation_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.rackreservation_count }}</a></h2>
<p>Rack reservations</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'dcim:location_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.location_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.location_count }}</a></h2>
<p>Locations</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'dcim:device_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.device_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.device_count }}</a></h2>
<p>Devices</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'dcim:virtualdevicecontext_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.vdc_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.vdc_count }}</a></h2>
<p>Virtual Device Contexts</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'dcim:cable_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.cable_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.cable_count }}</a></h2>
<p>Cables</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'ipam:vrf_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.vrf_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.vrf_count }}</a></h2>
<p>VRFs</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'ipam:aggregate_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.aggregate_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.aggregate_count }}</a></h2>
<p>Aggregates</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'ipam:asn_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.asn_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.asn_count }}</a></h2>
<p>ASNs</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'ipam:prefix_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.prefix_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.prefix_count }}</a></h2>
<p>Prefixes</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'ipam:iprange_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.iprange_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.iprange_count }}</a></h2>
<p>IP Ranges</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'ipam:ipaddress_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.ipaddress_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.ipaddress_count }}</a></h2>
<p>IP addresses</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'ipam:vlan_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.vlan_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.vlan_count }}</a></h2>
<p>VLANs</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'ipam:l2vpn_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.l2vpn_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.l2vpn_count }}</a></h2>
<p>L2VPNs</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'circuits:circuit_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.circuit_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.circuit_count }}</a></h2>
<p>Circuits</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'virtualization:virtualmachine_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.virtualmachine_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.virtualmachine_count }}</a></h2>
<p>Virtual machines</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'virtualization:cluster_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.cluster_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.cluster_count }}</a></h2>
<p>Clusters</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'wireless:wirelesslan_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.wirelesslan_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.wirelesslan_count }}</a></h2>
<p>Wireless LANs</p>
</div>
<div class="col col-md-4 text-center">
<h2><a href="{% url 'wireless:wirelesslink_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.wirelesslink_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.wirelesslink_count }}</a></h2>
<p>Wireless Links</p>
</div>
</div>
</div>
{% plugin_right_page object %}
</div>
{% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/tags.html' %}
{% include 'inc/panels/comments.html' %}
{% include 'inc/panels/contacts.html' %}
{% plugin_left_page object %}
</div>
</div>
<div class="row">
<div class="col col-md-5">
{% include 'inc/panels/related_objects.html' with filter_name='tenant_id' %}
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col col-md-12">
{% plugin_full_width_page object %}
{% plugin_full_width_page object %}
</div>
</div>
</div>
{% endblock %}

View File

@ -1,5 +1,6 @@
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext as _
from circuits.models import Circuit
from dcim.models import Cable, Device, Location, Rack, RackReservation, Site, VirtualDeviceContext
@ -92,31 +93,36 @@ class TenantView(generic.ObjectView):
queryset = Tenant.objects.all()
def get_extra_context(self, request, instance):
stats = {
'site_count': Site.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'rack_count': Rack.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'rackreservation_count': RackReservation.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'location_count': Location.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'device_count': Device.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'vdc_count': VirtualDeviceContext.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'vrf_count': VRF.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'aggregate_count': Aggregate.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'prefix_count': Prefix.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'iprange_count': IPRange.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'ipaddress_count': IPAddress.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'vlan_count': VLAN.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'l2vpn_count': L2VPN.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'circuit_count': Circuit.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'virtualmachine_count': VirtualMachine.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'cluster_count': Cluster.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'cable_count': Cable.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'asn_count': ASN.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'wirelesslan_count': WirelessLAN.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
'wirelesslink_count': WirelessLink.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
}
related_models = [
# DCIM
Site.objects.restrict(request.user, 'view').filter(tenant=instance),
Rack.objects.restrict(request.user, 'view').filter(tenant=instance),
RackReservation.objects.restrict(request.user, 'view').filter(tenant=instance),
Location.objects.restrict(request.user, 'view').filter(tenant=instance),
Device.objects.restrict(request.user, 'view').filter(tenant=instance),
VirtualDeviceContext.objects.restrict(request.user, 'view').filter(tenant=instance),
Cable.objects.restrict(request.user, 'view').filter(tenant=instance),
# IPAM
VRF.objects.restrict(request.user, 'view').filter(tenant=instance),
Aggregate.objects.restrict(request.user, 'view').filter(tenant=instance),
Prefix.objects.restrict(request.user, 'view').filter(tenant=instance),
IPRange.objects.restrict(request.user, 'view').filter(tenant=instance),
IPAddress.objects.restrict(request.user, 'view').filter(tenant=instance),
ASN.objects.restrict(request.user, 'view').filter(tenant=instance),
VLAN.objects.restrict(request.user, 'view').filter(tenant=instance),
L2VPN.objects.restrict(request.user, 'view').filter(tenant=instance),
# Circuits
Circuit.objects.restrict(request.user, 'view').filter(tenant=instance),
# Virtualization
VirtualMachine.objects.restrict(request.user, 'view').filter(tenant=instance),
Cluster.objects.restrict(request.user, 'view').filter(tenant=instance),
# Wireless
WirelessLAN.objects.restrict(request.user, 'view').filter(tenant=instance),
WirelessLink.objects.restrict(request.user, 'view').filter(tenant=instance),
]
return {
'stats': stats,
'related_models': related_models,
}