mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-09 00:58:16 -06:00
12826 add RackType
This commit is contained in:
parent
52546608f6
commit
21e11cd78f
85
netbox/dcim/migrations/0188_racktype.py
Normal file
85
netbox/dcim/migrations/0188_racktype.py
Normal 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'),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user