Skip to content

Commit

Permalink
12175 rack with starting unit > 1 (#12778)
Browse files Browse the repository at this point in the history
* 12175 add rack starting unit

* 12175 rack starting unit to svg

* verify devices can still fit if change rack starting_unit

* 12175 fix migration

* 12175 fix typo and test

* 12175 fix test

* 12175 fix max height calc display

* Misc cleanup & fixes

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
  • Loading branch information
arthanson and jeremystretch authored Jun 22, 2023
1 parent 518fd8c commit eff4a37
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 17 deletions.
2 changes: 2 additions & 0 deletions netbox/dcim/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
RACK_ELEVATION_DEFAULT_LEGEND_WIDTH = 30
RACK_ELEVATION_DEFAULT_MARGIN_WIDTH = 15

RACK_STARTING_UNIT_DEFAULT = 1


#
# RearPorts
Expand Down
4 changes: 2 additions & 2 deletions netbox/dcim/forms/model_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ class Meta:
model = Rack
fields = [
'site', 'location', 'name', 'facility_id', 'tenant_group', 'tenant', 'status', 'role', 'serial',
'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit',
'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'description', 'comments', 'tags',
'asset_tag', 'type', 'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'outer_depth',
'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'description', 'comments', 'tags',
]


Expand Down
17 changes: 17 additions & 0 deletions netbox/dcim/migrations/0174_rack_starting_unit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.1.9 on 2023-05-31 15:47

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
('dcim', '0174_device_latitude_device_longitude'),
]

operations = [
migrations.AddField(
model_name='rack',
name='starting_unit',
field=models.PositiveSmallIntegerField(default=1),
),
]
35 changes: 22 additions & 13 deletions netbox/dcim/models/racks.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ class Rack(PrimaryModel, WeightMixin):
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',
help_text=_('Starting unit for rack')
)
desc_units = models.BooleanField(
default=False,
verbose_name='Descending units',
Expand Down Expand Up @@ -228,20 +233,24 @@ def clean(self):
raise ValidationError("Must specify a unit when setting a maximum weight")

if self.pk:
# Validate that Rack is tall enough to house the installed Devices
top_device = Device.objects.filter(
rack=self
).exclude(
position__isnull=True
).order_by('-position').first()
if top_device:
min_height = top_device.position + top_device.device_type.u_height - 1
mounted_devices = Device.objects.filter(rack=self).exclude(position__isnull=True).order_by('position')

# Validate that Rack is tall enough to house the highest mounted Device
if top_device := mounted_devices.last():
min_height = top_device.position + top_device.device_type.u_height - self.starting_unit
if self.u_height < min_height:
raise ValidationError({
'u_height': "Rack must be at least {}U tall to house currently installed devices.".format(
min_height
)
'u_height': f"Rack must be at least {min_height}U tall to house currently installed devices."
})

# Validate that the Rack's starting unit is less than or equal to the position of the lowest mounted Device
if last_device := mounted_devices.first():
if self.starting_unit > last_device.position:
raise ValidationError({
'starting_unit': f"Rack unit numbering must begin at {last_device.position} or less to house "
f"currently installed devices."
})

# Validate that Rack was assigned a Location of its same site, if applicable
if self.location:
if self.location.site != self.site:
Expand Down Expand Up @@ -269,8 +278,8 @@ def units(self):
Return a list of unit numbers, top to bottom.
"""
if self.desc_units:
return drange(decimal.Decimal(1.0), self.u_height + 1, 0.5)
return drange(self.u_height + decimal.Decimal(0.5), 0.5, -0.5)
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)

def get_status_color(self):
return RackStatusChoices.colors.get(self.status)
Expand Down
6 changes: 4 additions & 2 deletions netbox/dcim/svg/racks.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,9 @@ def _get_device_coords(self, position, height):
x = self.legend_width + RACK_ELEVATION_BORDER_WIDTH
y = RACK_ELEVATION_BORDER_WIDTH
if self.rack.desc_units:
y += int((position - 1) * self.unit_height)
y += int((position - self.rack.starting_unit) * self.unit_height)
else:
y += int((self.rack.u_height - position + 1) * self.unit_height) - int(height * self.unit_height)
y += int((self.rack.u_height - position + self.rack.starting_unit) * self.unit_height) - int(height * self.unit_height)

return x, y

Expand Down Expand Up @@ -237,6 +237,7 @@ def draw_legend(self):
start_y = ru * self.unit_height + RACK_ELEVATION_BORDER_WIDTH
position_coordinates = (self.legend_width / 2, start_y + self.unit_height / 2 + RACK_ELEVATION_BORDER_WIDTH)
unit = ru + 1 if self.rack.desc_units else self.rack.u_height - ru
unit = unit + self.rack.starting_unit - 1
self.drawing.add(
Text(str(unit), position_coordinates, class_='unit')
)
Expand Down Expand Up @@ -278,6 +279,7 @@ def draw_background(self, face):

for ru in range(0, self.rack.u_height):
unit = ru + 1 if self.rack.desc_units else self.rack.u_height - ru
unit = unit + self.rack.starting_unit - 1
y_offset = RACK_ELEVATION_BORDER_WIDTH + ru * self.unit_height
text_coords = (
x_offset + self.unit_width / 2,
Expand Down
1 change: 1 addition & 0 deletions netbox/dcim/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ def setUpTestData(cls):
'outer_width': 500,
'outer_depth': 500,
'outer_unit': RackDimensionUnitChoices.UNIT_MILLIMETER,
'starting_unit': 1,
'weight': 100,
'max_weight': 2000,
'weight_unit': WeightUnitChoices.UNIT_POUND,
Expand Down
6 changes: 6 additions & 0 deletions netbox/templates/dcim/rack.html
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ <h5 class="card-header">Dimensions</h5>
<th scope="row">Height</th>
<td>{{ object.u_height }}U ({% if object.desc_units %}descending{% else %}ascending{% endif %})</td>
</tr>
<tr>
<th scope="row">Starting Unit</th>
<td>
{{ object.starting_unit }}
</td>
</tr>
<tr>
<th scope="row">Outer Width</th>
<td>
Expand Down
1 change: 1 addition & 0 deletions netbox/templates/dcim/rack_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ <h5 class="offset-sm-3">Dimensions</h5>
</div>
{% render_field form.mounting_depth %}
{% render_field form.desc_units %}
{% render_field form.starting_unit %}
</div>

{% if form.custom_fields %}
Expand Down

0 comments on commit eff4a37

Please sign in to comment.