12826 add RackType

This commit is contained in:
Arthur Hanson 2024-06-25 09:44:21 -07:00
parent 52546608f6
commit 21e11cd78f
2 changed files with 250 additions and 0 deletions

View File

@ -0,0 +1,85 @@
# Generated by Django 4.2.11 on 2024-06-25 16:43
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import taggit.managers
import utilities.fields
import utilities.json
import utilities.ordering
class Migration(migrations.Migration):
dependencies = [
('extras', '0117_customfield_uniqueness'),
('dcim', '0187_alter_device_vc_position'),
]
operations = [
migrations.CreateModel(
name='RackType',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
(
'custom_field_data',
models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
),
('description', models.CharField(blank=True, max_length=200)),
('comments', models.TextField(blank=True)),
('weight', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True)),
('weight_unit', models.CharField(blank=True, max_length=50)),
('_abs_weight', models.PositiveBigIntegerField(blank=True, null=True)),
('name', models.CharField(max_length=100)),
(
'_name',
utilities.fields.NaturalOrderingField(
'name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize
),
),
('type', models.CharField(blank=True, max_length=50)),
('width', models.PositiveSmallIntegerField(default=19)),
(
'u_height',
models.PositiveSmallIntegerField(
default=42,
validators=[
django.core.validators.MinValueValidator(1),
django.core.validators.MaxValueValidator(100),
],
),
),
(
'starting_unit',
models.PositiveSmallIntegerField(
default=1, validators=[django.core.validators.MinValueValidator(1)]
),
),
('desc_units', models.BooleanField(default=False)),
('outer_width', models.PositiveSmallIntegerField(blank=True, null=True)),
('outer_depth', models.PositiveSmallIntegerField(blank=True, null=True)),
('outer_unit', models.CharField(blank=True, max_length=50)),
('max_weight', models.PositiveIntegerField(blank=True, null=True)),
('_abs_max_weight', models.PositiveBigIntegerField(blank=True, null=True)),
('mounting_depth', models.PositiveSmallIntegerField(blank=True, null=True)),
(
'role',
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name='racktypes',
to='dcim.rackrole',
),
),
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
],
options={
'verbose_name': 'racktype',
'verbose_name_plural': 'racktypes',
'ordering': ('_name', 'pk'),
},
),
]

View File

@ -29,13 +29,178 @@ __all__ = (
'Rack', 'Rack',
'RackReservation', 'RackReservation',
'RackRole', 'RackRole',
'RackType',
) )
#
# Rack Types
#
class RackType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
"""
Devices are housed within Racks. Each rack has a defined height measured in rack units, and a front and rear face.
Each Rack is assigned to a Site and (optionally) a Location.
"""
name = models.CharField(
verbose_name=_('name'),
max_length=100
)
_name = NaturalOrderingField(
target_field='name',
max_length=100,
blank=True
)
role = models.ForeignKey(
to='dcim.RackRole',
on_delete=models.PROTECT,
related_name='racktypes',
blank=True,
null=True,
help_text=_('Functional role')
)
type = models.CharField(
choices=RackTypeChoices,
max_length=50,
blank=True,
verbose_name=_('type')
)
width = models.PositiveSmallIntegerField(
choices=RackWidthChoices,
default=RackWidthChoices.WIDTH_19IN,
verbose_name=_('width'),
help_text=_('Rail-to-rail width')
)
u_height = models.PositiveSmallIntegerField(
default=RACK_U_HEIGHT_DEFAULT,
verbose_name=_('height (U)'),
validators=[MinValueValidator(1), MaxValueValidator(RACK_U_HEIGHT_MAX)],
help_text=_('Height in rack units')
)
starting_unit = models.PositiveSmallIntegerField(
default=RACK_STARTING_UNIT_DEFAULT,
verbose_name=_('starting unit'),
validators=[MinValueValidator(1),],
help_text=_('Starting unit for rack')
)
desc_units = models.BooleanField(
default=False,
verbose_name=_('descending units'),
help_text=_('Units are numbered top-to-bottom')
)
outer_width = models.PositiveSmallIntegerField(
verbose_name=_('outer width'),
blank=True,
null=True,
help_text=_('Outer dimension of rack (width)')
)
outer_depth = models.PositiveSmallIntegerField(
verbose_name=_('outer depth'),
blank=True,
null=True,
help_text=_('Outer dimension of rack (depth)')
)
outer_unit = models.CharField(
verbose_name=_('outer unit'),
max_length=50,
choices=RackDimensionUnitChoices,
blank=True,
)
max_weight = models.PositiveIntegerField(
verbose_name=_('max weight'),
blank=True,
null=True,
help_text=_('Maximum load capacity for the rack')
)
# Stores the normalized max weight (in grams) for database ordering
_abs_max_weight = models.PositiveBigIntegerField(
blank=True,
null=True
)
mounting_depth = models.PositiveSmallIntegerField(
verbose_name=_('mounting depth'),
blank=True,
null=True,
help_text=(
_('Maximum depth of a mounted device, in millimeters. For four-post racks, this is the '
'distance between the front and rear rails.')
)
)
clone_fields = (
'role', 'type', 'width', 'u_height', 'desc_units', 'outer_width',
'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit',
)
prerequisite_models = (
'dcim.Site',
)
class Meta:
ordering = ('_name', 'pk') # (site, location, name) may be non-unique
verbose_name = _('racktype')
verbose_name_plural = _('racktypes')
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('dcim:racktype', args=[self.pk])
def clean(self):
super().clean()
# Validate outer dimensions and unit
if (self.outer_width is not None or self.outer_depth is not None) and not self.outer_unit:
raise ValidationError(_("Must specify a unit when setting an outer width/depth"))
# Validate max_weight and weight_unit
if self.max_weight and not self.weight_unit:
raise ValidationError(_("Must specify a unit when setting a maximum weight"))
def save(self, *args, **kwargs):
# Store the given max weight (if any) in grams for use in database ordering
if self.max_weight and self.weight_unit:
self._abs_max_weight = to_grams(self.max_weight, self.weight_unit)
else:
self._abs_max_weight = None
# Clear unit if outer width & depth are not set
if self.outer_width is None and self.outer_depth is None:
self.outer_unit = ''
super().save(*args, **kwargs)
@property
def units(self):
"""
Return a list of unit numbers, top to bottom.
"""
if self.desc_units:
return drange(decimal.Decimal(self.starting_unit), self.u_height + self.starting_unit, 0.5)
return drange(self.u_height + decimal.Decimal(0.5) + self.starting_unit - 1, 0.5 + self.starting_unit - 1, -0.5)
@cached_property
def total_weight(self):
total_weight = sum(
device.device_type._abs_weight
for device in self.devices.exclude(device_type___abs_weight__isnull=True).prefetch_related('device_type')
)
total_weight += sum(
module.module_type._abs_weight
for module in Module.objects.filter(device__rack=self)
.exclude(module_type___abs_weight__isnull=True)
.prefetch_related('module_type')
)
if self._abs_weight:
total_weight += self._abs_weight
return round(total_weight / 1000, 2)
# #
# Racks # Racks
# #
class RackRole(OrganizationalModel): class RackRole(OrganizationalModel):
""" """
Racks can be organized by functional role, similar to Devices. Racks can be organized by functional role, similar to Devices.