| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- from django.apps import apps
- from django.core.exceptions import ValidationError
- from django.db import models
- from django.urls import reverse
- from mptt.models import MPTTModel, TreeForeignKey
- from dcim.choices import LinkStatusChoices
- from dcim.constants import WIRELESS_IFACE_TYPES
- from netbox.models import NestedGroupModel, NetBoxModel
- from .choices import *
- from .constants import *
- __all__ = (
- 'WirelessLAN',
- 'WirelessLANGroup',
- 'WirelessLink',
- )
- class WirelessAuthenticationBase(models.Model):
- """
- Abstract model for attaching attributes related to wireless authentication.
- """
- auth_type = models.CharField(
- max_length=50,
- choices=WirelessAuthTypeChoices,
- blank=True,
- verbose_name="Auth Type",
- )
- auth_cipher = models.CharField(
- max_length=50,
- choices=WirelessAuthCipherChoices,
- blank=True
- )
- auth_psk = models.CharField(
- max_length=PSK_MAX_LENGTH,
- blank=True,
- verbose_name='Pre-shared key'
- )
- class Meta:
- abstract = True
- class WirelessLANGroup(NestedGroupModel):
- """
- A nested grouping of WirelessLANs
- """
- name = models.CharField(
- max_length=100,
- unique=True
- )
- slug = models.SlugField(
- max_length=100,
- unique=True
- )
- parent = TreeForeignKey(
- to='self',
- on_delete=models.CASCADE,
- related_name='children',
- blank=True,
- null=True,
- db_index=True
- )
- description = models.CharField(
- max_length=200,
- blank=True
- )
- class Meta:
- ordering = ('name', 'pk')
- unique_together = (
- ('parent', 'name')
- )
- verbose_name = 'Wireless LAN Group'
- def __str__(self):
- return self.name
- def get_absolute_url(self):
- return reverse('wireless:wirelesslangroup', args=[self.pk])
- class WirelessLAN(WirelessAuthenticationBase, NetBoxModel):
- """
- A wireless network formed among an arbitrary number of access point and clients.
- """
- ssid = models.CharField(
- max_length=SSID_MAX_LENGTH,
- verbose_name='SSID'
- )
- group = models.ForeignKey(
- to='wireless.WirelessLANGroup',
- on_delete=models.SET_NULL,
- related_name='wireless_lans',
- blank=True,
- null=True
- )
- vlan = models.ForeignKey(
- to='ipam.VLAN',
- on_delete=models.PROTECT,
- blank=True,
- null=True,
- verbose_name='VLAN'
- )
- tenant = models.ForeignKey(
- to='tenancy.Tenant',
- on_delete=models.PROTECT,
- related_name='wireless_lans',
- blank=True,
- null=True
- )
- description = models.CharField(
- max_length=200,
- blank=True
- )
- clone_fields = ('ssid', 'group', 'tenant', 'description')
- class Meta:
- ordering = ('ssid', 'pk')
- verbose_name = 'Wireless LAN'
- def __str__(self):
- return self.ssid
- def get_absolute_url(self):
- return reverse('wireless:wirelesslan', args=[self.pk])
- class WirelessLink(WirelessAuthenticationBase, NetBoxModel):
- """
- A point-to-point connection between two wireless Interfaces.
- """
- interface_a = models.ForeignKey(
- to='dcim.Interface',
- limit_choices_to={'type__in': WIRELESS_IFACE_TYPES},
- on_delete=models.PROTECT,
- related_name='+',
- verbose_name="Interface A",
- )
- interface_b = models.ForeignKey(
- to='dcim.Interface',
- limit_choices_to={'type__in': WIRELESS_IFACE_TYPES},
- on_delete=models.PROTECT,
- related_name='+',
- verbose_name="Interface B",
- )
- ssid = models.CharField(
- max_length=SSID_MAX_LENGTH,
- blank=True,
- verbose_name='SSID'
- )
- status = models.CharField(
- max_length=50,
- choices=LinkStatusChoices,
- default=LinkStatusChoices.STATUS_CONNECTED
- )
- tenant = models.ForeignKey(
- to='tenancy.Tenant',
- on_delete=models.PROTECT,
- related_name='wireless_links',
- blank=True,
- null=True
- )
- description = models.CharField(
- max_length=200,
- blank=True
- )
- # Cache the associated device for the A and B interfaces. This enables filtering of WirelessLinks by their
- # associated Devices.
- _interface_a_device = models.ForeignKey(
- to='dcim.Device',
- on_delete=models.CASCADE,
- related_name='+',
- blank=True,
- null=True
- )
- _interface_b_device = models.ForeignKey(
- to='dcim.Device',
- on_delete=models.CASCADE,
- related_name='+',
- blank=True,
- null=True
- )
- clone_fields = ('ssid', 'status')
- class Meta:
- ordering = ['pk']
- unique_together = ('interface_a', 'interface_b')
- def __str__(self):
- return f'#{self.pk}'
- @classmethod
- def get_prerequisite_models(cls):
- return [apps.get_model('dcim.Interface'), ]
- def get_absolute_url(self):
- return reverse('wireless:wirelesslink', args=[self.pk])
- def get_status_color(self):
- return LinkStatusChoices.colors.get(self.status)
- def clean(self):
- # Validate interface types
- if self.interface_a.type not in WIRELESS_IFACE_TYPES:
- raise ValidationError({
- 'interface_a': f"{self.interface_a.get_type_display()} is not a wireless interface."
- })
- if self.interface_b.type not in WIRELESS_IFACE_TYPES:
- raise ValidationError({
- 'interface_a': f"{self.interface_b.get_type_display()} is not a wireless interface."
- })
- def save(self, *args, **kwargs):
- # Store the parent Device for the A and B interfaces
- self._interface_a_device = self.interface_a.device
- self._interface_b_device = self.interface_b.device
- super().save(*args, **kwargs)
|