mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
401 lines
10 KiB
Python
401 lines
10 KiB
Python
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
|
|
)
|