mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Initial work on #91: Support for subdevices
This commit is contained in:
@ -4,7 +4,7 @@ from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.db import models
|
||||
from django.db.models import Q, ObjectDoesNotExist
|
||||
from django.db.models import Count, Q, ObjectDoesNotExist
|
||||
|
||||
from extras.rpc import RPC_CLIENTS
|
||||
from utilities.fields import NullableCharField
|
||||
@ -18,6 +18,14 @@ RACK_FACE_CHOICES = [
|
||||
[RACK_FACE_REAR, 'Rear'],
|
||||
]
|
||||
|
||||
SUBDEVICE_ROLE_PARENT = True
|
||||
SUBDEVICE_ROLE_CHILD = False
|
||||
SUBDEVICE_ROLE_CHOICES = (
|
||||
(None, 'None'),
|
||||
(SUBDEVICE_ROLE_PARENT, 'Parent'),
|
||||
(SUBDEVICE_ROLE_CHILD, 'Child'),
|
||||
)
|
||||
|
||||
COLOR_TEAL = 'teal'
|
||||
COLOR_GREEN = 'green'
|
||||
COLOR_BLUE = 'blue'
|
||||
@ -274,6 +282,7 @@ class Rack(CreatedUpdatedModel):
|
||||
# Add devices to rack units list
|
||||
if self.pk:
|
||||
for device in Device.objects.select_related('device_type__manufacturer', 'device_role')\
|
||||
.annotate(devicebay_count=Count('device_bays'))\
|
||||
.exclude(pk=exclude)\
|
||||
.filter(rack=self, position__gt=0)\
|
||||
.filter(Q(face=face) | Q(device_type__is_full_depth=True)):
|
||||
@ -380,6 +389,10 @@ class DeviceType(models.Model):
|
||||
help_text="This type of device has power outlets")
|
||||
is_network_device = models.BooleanField(default=True, verbose_name='Is a network device',
|
||||
help_text="This type of device has network interfaces")
|
||||
subdevice_role = models.NullBooleanField(default=None, verbose_name='Parent/child status',
|
||||
choices=SUBDEVICE_ROLE_CHOICES,
|
||||
help_text="Parent devices house child devices in device bays. Select "
|
||||
"\"None\" if this device type is neither a parent nor a child.")
|
||||
|
||||
class Meta:
|
||||
ordering = ['manufacturer', 'model']
|
||||
@ -389,11 +402,24 @@ class DeviceType(models.Model):
|
||||
]
|
||||
|
||||
def __unicode__(self):
|
||||
return "{0} {1}".format(self.manufacturer, self.model)
|
||||
return "{} {}".format(self.manufacturer, self.model)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('dcim:devicetype', args=[self.pk])
|
||||
|
||||
def clean(self):
|
||||
|
||||
if self.u_height and self.subdevice_role == SUBDEVICE_ROLE_CHILD:
|
||||
raise ValidationError("Child device types must be 0U.")
|
||||
|
||||
@property
|
||||
def is_parent_device(self):
|
||||
return bool(self.subdevice_role)
|
||||
|
||||
@property
|
||||
def is_child_device(self):
|
||||
return bool(self.subdevice_role is False)
|
||||
|
||||
|
||||
class ConsolePortTemplate(models.Model):
|
||||
"""
|
||||
@ -481,6 +507,21 @@ class InterfaceTemplate(models.Model):
|
||||
return self.name
|
||||
|
||||
|
||||
class DeviceBayTemplate(models.Model):
|
||||
"""
|
||||
A template for a DeviceBay to be created for a new parent Device.
|
||||
"""
|
||||
device_type = models.ForeignKey('DeviceType', related_name='device_bay_templates', on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=30)
|
||||
|
||||
class Meta:
|
||||
ordering = ['device_type', 'name']
|
||||
unique_together = ['device_type', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
#
|
||||
# Devices
|
||||
#
|
||||
@ -563,6 +604,10 @@ class Device(CreatedUpdatedModel):
|
||||
|
||||
def clean(self):
|
||||
|
||||
# Child devices cannot be assigned to a rack face/unit
|
||||
if self.device_type.is_child_device and (self.face is not None or self.position):
|
||||
raise ValidationError("Child device types cannot be assigned a rack face or position.")
|
||||
|
||||
# Validate position/face combination
|
||||
if self.position and self.face is None:
|
||||
raise ValidationError("Must specify rack face with rack position.")
|
||||
@ -610,6 +655,10 @@ class Device(CreatedUpdatedModel):
|
||||
[Interface(device=self, name=template.name, form_factor=template.form_factor,
|
||||
mgmt_only=template.mgmt_only) for template in self.device_type.interface_templates.all()]
|
||||
)
|
||||
DeviceBay.objects.bulk_create(
|
||||
[DeviceBay(device=self, name=template.name) for template in
|
||||
self.device_type.device_bay_templates.all()]
|
||||
)
|
||||
|
||||
def to_csv(self):
|
||||
return ','.join([
|
||||
@ -643,6 +692,12 @@ class Device(CreatedUpdatedModel):
|
||||
return self.name
|
||||
return '{{{}}}'.format(self.pk)
|
||||
|
||||
def get_children(self):
|
||||
"""
|
||||
Return the set of child Devices installed in DeviceBays within this Device.
|
||||
"""
|
||||
return Device.objects.filter(parent_bay__device=self.pk)
|
||||
|
||||
def get_rpc_client(self):
|
||||
"""
|
||||
Return the appropriate RPC (e.g. NETCONF, ssh, etc.) client for this device's platform, if one is defined.
|
||||
@ -860,6 +915,33 @@ class InterfaceConnection(models.Model):
|
||||
])
|
||||
|
||||
|
||||
class DeviceBay(models.Model):
|
||||
"""
|
||||
An empty space within a Device which can house a child device
|
||||
"""
|
||||
device = models.ForeignKey('Device', related_name='device_bays', on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=50, verbose_name='Name')
|
||||
installed_device = models.OneToOneField('Device', related_name='parent_bay', blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['device', 'name']
|
||||
unique_together = ['device', 'name']
|
||||
|
||||
def __unicode__(self):
|
||||
return '{} - {}'.format(self.device.name, self.name)
|
||||
|
||||
def clean(self):
|
||||
|
||||
# Validate that the parent Device can have DeviceBays
|
||||
if not self.device.device_type.is_parent_device:
|
||||
raise ValidationError("This type of device ({}) does not support device bays."
|
||||
.format(self.device.device_type))
|
||||
|
||||
# Cannot install a device into itself, obviously
|
||||
if self.device == self.installed_device:
|
||||
raise ValidationError("Cannot install a device into itself.")
|
||||
|
||||
|
||||
class Module(models.Model):
|
||||
"""
|
||||
A Module represents a piece of hardware within a Device, such as a line card or power supply. Modules are used only
|
||||
|
Reference in New Issue
Block a user