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

Closes #3092: Split DCIM models into separate files for easier management

This commit is contained in:
Jeremy Stretch
2020-01-10 14:22:22 -05:00
parent 9f627fd0d3
commit ca13045515
4 changed files with 1471 additions and 1368 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,400 @@
from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from dcim.choices import *
from dcim.constants import *
from dcim.managers import InterfaceManager
from extras.models import ObjectChange
from utilities.managers import NaturalOrderingManager
from utilities.utils import serialize_object
from .device_components import (
ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, PowerOutlet, PowerPort, RearPort,
)
__all__ = (
'ConsolePortTemplate',
'ConsoleServerPortTemplate',
'DeviceBayTemplate',
'FrontPortTemplate',
'InterfaceTemplate',
'PowerOutletTemplate',
'PowerPortTemplate',
'RearPortTemplate',
)
class ComponentTemplateModel(models.Model):
class Meta:
abstract = True
def instantiate(self, device):
"""
Instantiate a new component on the specified Device.
"""
raise NotImplementedError()
def to_objectchange(self, action):
return ObjectChange(
changed_object=self,
object_repr=str(self),
action=action,
related_object=self.device_type,
object_data=serialize_object(self)
)
class ConsolePortTemplate(ComponentTemplateModel):
"""
A template for a ConsolePort to be created for a new Device.
"""
device_type = models.ForeignKey(
to='dcim.DeviceType',
on_delete=models.CASCADE,
related_name='consoleport_templates'
)
name = models.CharField(
max_length=50
)
type = models.CharField(
max_length=50,
choices=ConsolePortTypeChoices,
blank=True
)
objects = NaturalOrderingManager()
class Meta:
ordering = ['device_type', 'name']
unique_together = ['device_type', 'name']
def __str__(self):
return self.name
def instantiate(self, device):
return ConsolePort(
device=device,
name=self.name,
type=self.type
)
class ConsoleServerPortTemplate(ComponentTemplateModel):
"""
A template for a ConsoleServerPort to be created for a new Device.
"""
device_type = models.ForeignKey(
to='dcim.DeviceType',
on_delete=models.CASCADE,
related_name='consoleserverport_templates'
)
name = models.CharField(
max_length=50
)
type = models.CharField(
max_length=50,
choices=ConsolePortTypeChoices,
blank=True
)
objects = NaturalOrderingManager()
class Meta:
ordering = ['device_type', 'name']
unique_together = ['device_type', 'name']
def __str__(self):
return self.name
def instantiate(self, device):
return ConsoleServerPort(
device=device,
name=self.name,
type=self.type
)
class PowerPortTemplate(ComponentTemplateModel):
"""
A template for a PowerPort to be created for a new Device.
"""
device_type = models.ForeignKey(
to='dcim.DeviceType',
on_delete=models.CASCADE,
related_name='powerport_templates'
)
name = models.CharField(
max_length=50
)
type = models.CharField(
max_length=50,
choices=PowerPortTypeChoices,
blank=True
)
maximum_draw = models.PositiveSmallIntegerField(
blank=True,
null=True,
validators=[MinValueValidator(1)],
help_text="Maximum power draw (watts)"
)
allocated_draw = models.PositiveSmallIntegerField(
blank=True,
null=True,
validators=[MinValueValidator(1)],
help_text="Allocated power draw (watts)"
)
objects = NaturalOrderingManager()
class Meta:
ordering = ['device_type', 'name']
unique_together = ['device_type', 'name']
def __str__(self):
return self.name
def instantiate(self, device):
return PowerPort(
device=device,
name=self.name,
maximum_draw=self.maximum_draw,
allocated_draw=self.allocated_draw
)
class PowerOutletTemplate(ComponentTemplateModel):
"""
A template for a PowerOutlet to be created for a new Device.
"""
device_type = models.ForeignKey(
to='dcim.DeviceType',
on_delete=models.CASCADE,
related_name='poweroutlet_templates'
)
name = models.CharField(
max_length=50
)
type = models.CharField(
max_length=50,
choices=PowerOutletTypeChoices,
blank=True
)
power_port = models.ForeignKey(
to='dcim.PowerPortTemplate',
on_delete=models.SET_NULL,
blank=True,
null=True,
related_name='poweroutlet_templates'
)
feed_leg = models.CharField(
max_length=50,
choices=PowerOutletFeedLegChoices,
blank=True,
help_text="Phase (for three-phase feeds)"
)
objects = NaturalOrderingManager()
class Meta:
ordering = ['device_type', 'name']
unique_together = ['device_type', 'name']
def __str__(self):
return self.name
def clean(self):
# Validate power port assignment
if self.power_port and self.power_port.device_type != self.device_type:
raise ValidationError(
"Parent power port ({}) must belong to the same device type".format(self.power_port)
)
def instantiate(self, device):
if self.power_port:
power_port = PowerPort.objects.get(device=device, name=self.power_port.name)
else:
power_port = None
return PowerOutlet(
device=device,
name=self.name,
power_port=power_port,
feed_leg=self.feed_leg
)
class InterfaceTemplate(ComponentTemplateModel):
"""
A template for a physical data interface on a new Device.
"""
device_type = models.ForeignKey(
to='dcim.DeviceType',
on_delete=models.CASCADE,
related_name='interface_templates'
)
name = models.CharField(
max_length=64
)
type = models.CharField(
max_length=50,
choices=InterfaceTypeChoices
)
mgmt_only = models.BooleanField(
default=False,
verbose_name='Management only'
)
objects = InterfaceManager()
class Meta:
ordering = ['device_type', 'name']
unique_together = ['device_type', 'name']
def __str__(self):
return self.name
def instantiate(self, device):
return Interface(
device=device,
name=self.name,
type=self.type,
mgmt_only=self.mgmt_only
)
class FrontPortTemplate(ComponentTemplateModel):
"""
Template for a pass-through port on the front of a new Device.
"""
device_type = models.ForeignKey(
to='dcim.DeviceType',
on_delete=models.CASCADE,
related_name='frontport_templates'
)
name = models.CharField(
max_length=64
)
type = models.CharField(
max_length=50,
choices=PortTypeChoices
)
rear_port = models.ForeignKey(
to='dcim.RearPortTemplate',
on_delete=models.CASCADE,
related_name='frontport_templates'
)
rear_port_position = models.PositiveSmallIntegerField(
default=1,
validators=[MinValueValidator(1), MaxValueValidator(64)]
)
objects = NaturalOrderingManager()
class Meta:
ordering = ['device_type', 'name']
unique_together = [
['device_type', 'name'],
['rear_port', 'rear_port_position'],
]
def __str__(self):
return self.name
def clean(self):
# Validate rear port assignment
if self.rear_port.device_type != self.device_type:
raise ValidationError(
"Rear port ({}) must belong to the same device type".format(self.rear_port)
)
# Validate rear port position assignment
if self.rear_port_position > self.rear_port.positions:
raise ValidationError(
"Invalid rear port position ({}); rear port {} has only {} positions".format(
self.rear_port_position, self.rear_port.name, self.rear_port.positions
)
)
def instantiate(self, device):
if self.rear_port:
rear_port = RearPort.objects.get(device=device, name=self.rear_port.name)
else:
rear_port = None
return FrontPort(
device=device,
name=self.name,
type=self.type,
rear_port=rear_port,
rear_port_position=self.rear_port_position
)
class RearPortTemplate(ComponentTemplateModel):
"""
Template for a pass-through port on the rear of a new Device.
"""
device_type = models.ForeignKey(
to='dcim.DeviceType',
on_delete=models.CASCADE,
related_name='rearport_templates'
)
name = models.CharField(
max_length=64
)
type = models.CharField(
max_length=50,
choices=PortTypeChoices
)
positions = models.PositiveSmallIntegerField(
default=1,
validators=[MinValueValidator(1), MaxValueValidator(64)]
)
objects = NaturalOrderingManager()
class Meta:
ordering = ['device_type', 'name']
unique_together = ['device_type', 'name']
def __str__(self):
return self.name
def instantiate(self, device):
return RearPort(
device=device,
name=self.name,
type=self.type,
positions=self.positions
)
class DeviceBayTemplate(ComponentTemplateModel):
"""
A template for a DeviceBay to be created for a new parent Device.
"""
device_type = models.ForeignKey(
to='dcim.DeviceType',
on_delete=models.CASCADE,
related_name='device_bay_templates'
)
name = models.CharField(
max_length=50
)
objects = NaturalOrderingManager()
class Meta:
ordering = ['device_type', 'name']
unique_together = ['device_type', 'name']
def __str__(self):
return self.name
def instantiate(self, device):
return DeviceBay(
device=device,
name=self.name
)

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,8 @@
from django.core.exceptions import ValidationError
from django.test import TestCase
from dcim.choices import *
from dcim.constants import CONNECTION_STATUS_CONNECTED, CONNECTION_STATUS_PLANNED
from dcim.models import *
from tenancy.models import Tenant
@ -498,7 +501,7 @@ class CablePathTestCase(TestCase):
self.assertEqual(interface1.connection_status, CONNECTION_STATUS_PLANNED)
# Switch third segment from planned to connected
cable3.status = CONNECTION_STATUS_CONNECTED
cable3.status = CableStatusChoices.STATUS_CONNECTED
cable3.save()
interface1 = Interface.objects.get(pk=self.interface1.pk)
self.assertEqual(interface1.connected_endpoint, self.interface2)