mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-24 17:38:37 -06:00
commit
58e3d5ae09
@ -1032,6 +1032,13 @@ class Interface(models.Model):
|
|||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
|
||||||
|
if self.form_factor == IFACE_FF_VIRTUAL and self.is_connected:
|
||||||
|
raise ValidationError({'form_factor': "Virtual interfaces cannot be connected to another interface or "
|
||||||
|
"circuit. Disconnect the interface or choose a physical form "
|
||||||
|
"factor."})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_physical(self):
|
def is_physical(self):
|
||||||
return self.form_factor != IFACE_FF_VIRTUAL
|
return self.form_factor != IFACE_FF_VIRTUAL
|
||||||
|
@ -18,9 +18,10 @@ GRAPH_TYPE_CHOICES = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
EXPORTTEMPLATE_MODELS = [
|
EXPORTTEMPLATE_MODELS = [
|
||||||
'site', 'rack', 'device', 'consoleport', 'powerport', 'interfaceconnection',
|
'site', 'rack', 'device', 'consoleport', 'powerport', 'interfaceconnection', # DCIM
|
||||||
'aggregate', 'prefix', 'ipaddress', 'vlan',
|
'aggregate', 'prefix', 'ipaddress', 'vlan', # IPAM
|
||||||
'provider', 'circuit'
|
'provider', 'circuit', # Circuits
|
||||||
|
'tenant', # Tenants
|
||||||
]
|
]
|
||||||
|
|
||||||
ACTION_CREATE = 1
|
ACTION_CREATE = 1
|
||||||
|
@ -5,6 +5,7 @@ from django.core.exceptions import ValidationError
|
|||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models.expressions import RawSQL
|
||||||
|
|
||||||
from dcim.models import Interface
|
from dcim.models import Interface
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
@ -295,6 +296,20 @@ class Prefix(CreatedUpdatedModel):
|
|||||||
return STATUS_CHOICE_CLASSES[self.status]
|
return STATUS_CHOICE_CLASSES[self.status]
|
||||||
|
|
||||||
|
|
||||||
|
class IPAddressManager(models.Manager):
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
"""
|
||||||
|
By default, PostgreSQL will order INETs with shorter (larger) prefix lengths ahead of those with longer
|
||||||
|
(smaller) masks. This makes no sense when ordering IPs, which should be ordered solely by family and host
|
||||||
|
address. We can use HOST() to extract just the host portion of the address (ignoring its mask), but we must
|
||||||
|
then re-cast this value to INET() so that records will be ordered properly. We are essentially re-casting each
|
||||||
|
IP address as a /32 or /128.
|
||||||
|
"""
|
||||||
|
qs = super(IPAddressManager, self).get_queryset()
|
||||||
|
return qs.annotate(host=RawSQL('INET(HOST(ipam_ipaddress.address))', [])).order_by('family', 'host')
|
||||||
|
|
||||||
|
|
||||||
class IPAddress(CreatedUpdatedModel):
|
class IPAddress(CreatedUpdatedModel):
|
||||||
"""
|
"""
|
||||||
An IPAddress represents an individual IPv4 or IPv6 address and its mask. The mask length should match what is
|
An IPAddress represents an individual IPv4 or IPv6 address and its mask. The mask length should match what is
|
||||||
@ -317,6 +332,8 @@ class IPAddress(CreatedUpdatedModel):
|
|||||||
null=True, verbose_name='NAT IP (inside)')
|
null=True, verbose_name='NAT IP (inside)')
|
||||||
description = models.CharField(max_length=100, blank=True)
|
description = models.CharField(max_length=100, blank=True)
|
||||||
|
|
||||||
|
objects = IPAddressManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['family', 'address']
|
ordering = ['family', 'address']
|
||||||
verbose_name = 'IP address'
|
verbose_name = 'IP address'
|
||||||
|
@ -12,7 +12,7 @@ except ImportError:
|
|||||||
"the documentation.")
|
"the documentation.")
|
||||||
|
|
||||||
|
|
||||||
VERSION = '1.5.1'
|
VERSION = '1.5.2'
|
||||||
|
|
||||||
# Import local configuration
|
# Import local configuration
|
||||||
for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']:
|
for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']:
|
||||||
|
@ -8,9 +8,15 @@ $(document).ready(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update livesearch text when real field changes
|
// Update livesearch text when real field changes
|
||||||
search_field.val(real_field.children('option:selected').text());
|
if (real_field.val()) {
|
||||||
real_field.change(function() {
|
|
||||||
search_field.val(real_field.children('option:selected').text());
|
search_field.val(real_field.children('option:selected').text());
|
||||||
|
}
|
||||||
|
real_field.change(function() {
|
||||||
|
if (real_field.val()) {
|
||||||
|
search_field.val(real_field.children('option:selected').text());
|
||||||
|
} else {
|
||||||
|
search_field.val('');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
search_field.autocomplete({
|
search_field.autocomplete({
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
<span class="fa fa-plus" aria-hidden="true"></span>
|
<span class="fa fa-plus" aria-hidden="true"></span>
|
||||||
Add a circuit
|
Add a circuit
|
||||||
</a>
|
</a>
|
||||||
|
<a href="{% url 'circuits:circuit_import' %}" class="btn btn-info">
|
||||||
|
<span class="fa fa-download" aria-hidden="true"></span>
|
||||||
|
Import circuits
|
||||||
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'inc/export_button.html' with obj_type='circuits' %}
|
{% include 'inc/export_button.html' with obj_type='circuits' %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,6 +9,10 @@
|
|||||||
<span class="fa fa-plus" aria-hidden="true"></span>
|
<span class="fa fa-plus" aria-hidden="true"></span>
|
||||||
Add a provider
|
Add a provider
|
||||||
</a>
|
</a>
|
||||||
|
<a href="{% url 'circuits:provider_import' %}" class="btn btn-info">
|
||||||
|
<span class="fa fa-download" aria-hidden="true"></span>
|
||||||
|
Import providers
|
||||||
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'inc/export_button.html' with obj_type='providers' %}
|
{% include 'inc/export_button.html' with obj_type='providers' %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -56,6 +56,10 @@
|
|||||||
<a href="{% url 'dcim:interfaceconnection_delete' pk=iface.connection.pk %}?device={{ device.pk }}" class="btn btn-danger btn-xs" title="Delete connection">
|
<a href="{% url 'dcim:interfaceconnection_delete' pk=iface.connection.pk %}?device={{ device.pk }}" class="btn btn-danger btn-xs" title="Delete connection">
|
||||||
<i class="glyphicon glyphicon-remove" aria-hidden="true"></i>
|
<i class="glyphicon glyphicon-remove" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
|
{% elif iface.circuit and perms.circuits.change_circuit %}
|
||||||
|
<a href="{% url 'circuits:circuit_edit' pk=iface.circuit.pk %}" class="btn btn-danger btn-xs" title="Edit circuit">
|
||||||
|
<i class="glyphicon glyphicon-remove" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{% url 'dcim:interfaceconnection_add' pk=device.pk %}?interface={{ iface.pk }}" class="btn btn-success btn-xs" title="Connect">
|
<a href="{% url 'dcim:interfaceconnection_add' pk=device.pk %}?interface={{ iface.pk }}" class="btn btn-success btn-xs" title="Connect">
|
||||||
<i class="glyphicon glyphicon-plus" aria-hidden="true"></i>
|
<i class="glyphicon glyphicon-plus" aria-hidden="true"></i>
|
||||||
|
@ -11,6 +11,10 @@
|
|||||||
<span class="fa fa-plus" aria-hidden="true"></span>
|
<span class="fa fa-plus" aria-hidden="true"></span>
|
||||||
Add an aggregate
|
Add an aggregate
|
||||||
</a>
|
</a>
|
||||||
|
<a href="{% url 'ipam:aggregate_import' %}" class="btn btn-info">
|
||||||
|
<span class="fa fa-download" aria-hidden="true"></span>
|
||||||
|
Import aggregates
|
||||||
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'inc/export_button.html' with obj_type='aggregates' %}
|
{% include 'inc/export_button.html' with obj_type='aggregates' %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
<span class="fa fa-plus" aria-hidden="true"></span>
|
<span class="fa fa-plus" aria-hidden="true"></span>
|
||||||
Add a tenant
|
Add a tenant
|
||||||
</a>
|
</a>
|
||||||
|
<a href="{% url 'tenancy:tenant_import' %}" class="btn btn-info">
|
||||||
|
<span class="fa fa-download" aria-hidden="true"></span>
|
||||||
|
Import tenants
|
||||||
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'inc/export_button.html' with obj_type='tenants' %}
|
{% include 'inc/export_button.html' with obj_type='tenants' %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||||
from django.db.models import Count
|
from django.db.models import Count, Q
|
||||||
from django.shortcuts import get_object_or_404, render
|
from django.shortcuts import get_object_or_404, render
|
||||||
|
|
||||||
from circuits.models import Circuit
|
from circuits.models import Circuit
|
||||||
@ -59,8 +59,14 @@ def tenant(request, slug):
|
|||||||
'rack_count': Rack.objects.filter(tenant=tenant).count(),
|
'rack_count': Rack.objects.filter(tenant=tenant).count(),
|
||||||
'device_count': Device.objects.filter(tenant=tenant).count(),
|
'device_count': Device.objects.filter(tenant=tenant).count(),
|
||||||
'vrf_count': VRF.objects.filter(tenant=tenant).count(),
|
'vrf_count': VRF.objects.filter(tenant=tenant).count(),
|
||||||
'prefix_count': Prefix.objects.filter(tenant=tenant).count(),
|
'prefix_count': Prefix.objects.filter(
|
||||||
'ipaddress_count': IPAddress.objects.filter(tenant=tenant).count(),
|
Q(tenant=tenant) |
|
||||||
|
Q(tenant__isnull=True, vrf__tenant=tenant)
|
||||||
|
).count(),
|
||||||
|
'ipaddress_count': IPAddress.objects.filter(
|
||||||
|
Q(tenant=tenant) |
|
||||||
|
Q(tenant__isnull=True, vrf__tenant=tenant)
|
||||||
|
).count(),
|
||||||
'vlan_count': VLAN.objects.filter(tenant=tenant).count(),
|
'vlan_count': VLAN.objects.filter(tenant=tenant).count(),
|
||||||
'circuit_count': Circuit.objects.filter(tenant=tenant).count(),
|
'circuit_count': Circuit.objects.filter(tenant=tenant).count(),
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user