1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

393 lines
10 KiB
Python
Raw Normal View History

from django.core.exceptions import ValidationError
2016-03-01 11:23:03 -05:00
from django.db import models
from django.urls import reverse
2016-03-01 11:23:03 -05:00
from dcim.fields import ASNField
from dcim.models import CableTermination, PathEndpoint
from extras.models import ObjectChange
from extras.utils import extras_features
from netbox.models import BigIDModel, ChangeLoggedModel, OrganizationalModel, PrimaryModel
from utilities.querysets import RestrictedQuerySet
2019-11-07 11:10:46 -05:00
from .choices import *
__all__ = (
'Circuit',
'CircuitTermination',
'CircuitType',
2021-04-01 10:21:41 -04:00
'ProviderNetwork',
'Provider',
)
@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')
class Provider(PrimaryModel):
2016-03-01 11:23:03 -05:00
"""
2016-06-21 12:45:02 -04:00
Each Circuit belongs to a Provider. This is usually a telecommunications company or similar organization. This model
stores information pertinent to the user's relationship with the Provider.
2016-03-01 11:23:03 -05:00
"""
2018-03-30 13:57:26 -04:00
name = models.CharField(
max_length=100,
2018-03-30 13:57:26 -04:00
unique=True
)
slug = models.SlugField(
max_length=100,
2018-03-30 13:57:26 -04:00
unique=True
)
asn = ASNField(
blank=True,
null=True,
verbose_name='ASN',
help_text='32-bit autonomous system number'
2018-03-30 13:57:26 -04:00
)
account = models.CharField(
max_length=30,
blank=True,
verbose_name='Account number'
)
portal_url = models.URLField(
blank=True,
verbose_name='Portal URL'
2018-03-30 13:57:26 -04:00
)
noc_contact = models.TextField(
blank=True,
verbose_name='NOC contact'
)
admin_contact = models.TextField(
blank=True,
verbose_name='Admin contact'
)
comments = models.TextField(
blank=True
)
objects = RestrictedQuerySet.as_manager()
csv_headers = [
'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments',
]
clone_fields = [
'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact',
]
2016-03-01 11:23:03 -05:00
class Meta:
ordering = ['name']
def __str__(self):
2016-03-01 11:23:03 -05:00
return self.name
def get_absolute_url(self):
return reverse('circuits:provider', args=[self.pk])
2016-03-01 11:23:03 -05:00
def to_csv(self):
2018-02-02 11:34:31 -05:00
return (
self.name,
self.slug,
self.asn,
self.account,
self.portal_url,
self.noc_contact,
self.admin_contact,
self.comments,
2018-02-02 11:34:31 -05:00
)
2016-03-01 11:23:03 -05:00
2021-03-18 11:10:48 -04:00
#
2021-04-01 10:21:41 -04:00
# Provider networks
2021-03-18 11:10:48 -04:00
#
@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')
2021-04-01 10:21:41 -04:00
class ProviderNetwork(PrimaryModel):
"""
This represents a provider network which exists outside of NetBox, the details of which are unknown or
unimportant to the user.
"""
2021-03-18 11:10:48 -04:00
name = models.CharField(
max_length=100
)
provider = models.ForeignKey(
to='circuits.Provider',
on_delete=models.PROTECT,
2021-04-01 10:21:41 -04:00
related_name='networks'
2021-03-18 11:10:48 -04:00
)
description = models.CharField(
max_length=200,
blank=True
)
comments = models.TextField(
blank=True
)
csv_headers = [
'provider', 'name', 'description', 'comments',
]
objects = RestrictedQuerySet.as_manager()
class Meta:
ordering = ('provider', 'name')
constraints = (
models.UniqueConstraint(
fields=('provider', 'name'),
2021-04-01 10:21:41 -04:00
name='circuits_providernetwork_provider_name'
2021-03-18 11:10:48 -04:00
),
)
unique_together = ('provider', 'name')
def __str__(self):
return self.name
def get_absolute_url(self):
2021-04-01 10:21:41 -04:00
return reverse('circuits:providernetwork', args=[self.pk])
2021-03-18 11:10:48 -04:00
def to_csv(self):
return (
self.provider.name,
self.name,
self.description,
self.comments,
)
@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')
class CircuitType(OrganizationalModel):
2016-03-01 11:23:03 -05:00
"""
Circuits can be organized by their functional role. For example, a user might wish to define CircuitTypes named
2016-06-21 12:45:02 -04:00
"Long Haul," "Metro," or "Out-of-Band".
2016-03-01 11:23:03 -05:00
"""
2018-03-30 13:57:26 -04:00
name = models.CharField(
max_length=100,
2018-03-30 13:57:26 -04:00
unique=True
)
slug = models.SlugField(
max_length=100,
2018-03-30 13:57:26 -04:00
unique=True
)
description = models.CharField(
max_length=200,
blank=True,
)
2016-03-01 11:23:03 -05:00
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'slug', 'description']
2016-03-01 11:23:03 -05:00
class Meta:
ordering = ['name']
def __str__(self):
2016-03-01 11:23:03 -05:00
return self.name
2016-05-13 12:44:03 -04:00
def get_absolute_url(self):
return reverse('circuits:circuittype', args=[self.pk])
2016-05-13 12:44:03 -04:00
def to_csv(self):
return (
self.name,
self.slug,
self.description,
)
2016-03-01 11:23:03 -05:00
@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')
class Circuit(PrimaryModel):
2016-03-01 11:23:03 -05:00
"""
2016-06-21 12:45:02 -04:00
A communications circuit connects two points. Each Circuit belongs to a Provider; Providers may have multiple
circuits. Each circuit is also assigned a CircuitType and a Site. Circuit port speed and commit rate are measured
in Kbps.
2016-03-01 11:23:03 -05:00
"""
2018-03-30 13:57:26 -04:00
cid = models.CharField(
max_length=100,
2018-03-30 13:57:26 -04:00
verbose_name='Circuit ID'
)
provider = models.ForeignKey(
to='circuits.Provider',
on_delete=models.PROTECT,
related_name='circuits'
)
type = models.ForeignKey(
to='CircuitType',
on_delete=models.PROTECT,
related_name='circuits'
)
2019-11-07 11:10:46 -05:00
status = models.CharField(
max_length=50,
choices=CircuitStatusChoices,
default=CircuitStatusChoices.STATUS_ACTIVE
2018-03-30 13:57:26 -04:00
)
tenant = models.ForeignKey(
to='tenancy.Tenant',
on_delete=models.PROTECT,
related_name='circuits',
blank=True,
null=True
)
install_date = models.DateField(
blank=True,
null=True,
verbose_name='Date installed'
)
commit_rate = models.PositiveIntegerField(
blank=True,
null=True,
verbose_name='Commit rate (Kbps)')
description = models.CharField(
max_length=200,
2018-03-30 13:57:26 -04:00
blank=True
)
comments = models.TextField(
blank=True
)
2016-03-01 11:23:03 -05:00
2021-03-18 13:54:05 -04:00
# Cache associated CircuitTerminations
termination_a = models.ForeignKey(
to='circuits.CircuitTermination',
on_delete=models.SET_NULL,
related_name='+',
editable=False,
blank=True,
null=True
)
termination_z = models.ForeignKey(
to='circuits.CircuitTermination',
on_delete=models.SET_NULL,
related_name='+',
editable=False,
blank=True,
null=True
)
objects = RestrictedQuerySet.as_manager()
csv_headers = [
'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
]
clone_fields = [
'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description',
]
2016-03-01 11:23:03 -05:00
class Meta:
ordering = ['provider', 'cid']
unique_together = ['provider', 'cid']
def __str__(self):
return self.cid
2016-03-01 11:23:03 -05:00
def get_absolute_url(self):
return reverse('circuits:circuit', args=[self.pk])
def to_csv(self):
2018-02-02 11:34:31 -05:00
return (
self.cid,
self.provider.name,
self.type.name,
self.get_status_display(),
self.tenant.name if self.tenant else None,
2018-02-02 11:34:31 -05:00
self.install_date,
self.commit_rate,
self.description,
self.comments,
2018-02-02 11:34:31 -05:00
)
2016-06-20 15:58:18 -04:00
def get_status_class(self):
return CircuitStatusChoices.CSS_CLASSES.get(self.status)
@extras_features('webhooks')
class CircuitTermination(ChangeLoggedModel, CableTermination):
2018-03-30 13:57:26 -04:00
circuit = models.ForeignKey(
to='circuits.Circuit',
on_delete=models.CASCADE,
related_name='terminations'
)
term_side = models.CharField(
max_length=1,
choices=CircuitTerminationSideChoices,
2018-03-30 13:57:26 -04:00
verbose_name='Termination'
)
site = models.ForeignKey(
to='dcim.Site',
on_delete=models.PROTECT,
related_name='circuit_terminations',
blank=True,
null=True
)
2021-04-01 10:21:41 -04:00
provider_network = models.ForeignKey(
to=ProviderNetwork,
on_delete=models.PROTECT,
related_name='circuit_terminations',
blank=True,
null=True
2018-03-30 13:57:26 -04:00
)
port_speed = models.PositiveIntegerField(
verbose_name='Port speed (Kbps)',
blank=True,
null=True
)
upstream_speed = models.PositiveIntegerField(
2018-03-30 13:57:26 -04:00
blank=True,
null=True,
verbose_name='Upstream speed (Kbps)',
help_text='Upstream speed, if different from port speed'
)
2018-03-30 13:57:26 -04:00
xconnect_id = models.CharField(
max_length=50,
blank=True,
verbose_name='Cross-connect ID'
)
pp_info = models.CharField(
max_length=100,
blank=True,
verbose_name='Patch panel/port(s)'
)
description = models.CharField(
max_length=200,
blank=True
)
objects = RestrictedQuerySet.as_manager()
class Meta:
ordering = ['circuit', 'term_side']
unique_together = ['circuit', 'term_side']
def __str__(self):
return f'Termination {self.term_side}: {self.site or self.provider_network}'
2021-03-18 14:49:06 -04:00
def get_absolute_url(self):
if self.site:
return self.site.get_absolute_url()
2021-04-01 10:21:41 -04:00
return self.provider_network.get_absolute_url()
def clean(self):
super().clean()
2021-04-01 10:21:41 -04:00
# Must define either site *or* provider network
if self.site is None and self.provider_network is None:
raise ValidationError("A circuit termination must attach to either a site or a provider network.")
if self.site and self.provider_network:
raise ValidationError("A circuit termination cannot attach to both a site and a provider network.")
def to_objectchange(self, action):
# Annotate the parent Circuit
try:
circuit = self.circuit
except Circuit.DoesNotExist:
# Parent circuit has been deleted
circuit = None
return super().to_objectchange(action, related_object=circuit)
@property
def parent_object(self):
return self.circuit
def get_peer_termination(self):
peer_side = 'Z' if self.term_side == 'A' else 'A'
try:
return CircuitTermination.objects.prefetch_related('site').get(
circuit=self.circuit,
term_side=peer_side
)
except CircuitTermination.DoesNotExist:
return None