Skip to content

Commit

Permalink
Closes #450: Add 'outer_width' and 'outer_depth' fields to Rack
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremystretch committed Nov 2, 2018
1 parent 4ffe186 commit c60c550
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ v2.5.0 (FUTURE)

## Enhancements

* [#450](https://github.com/digitalocean/netbox/issues/450) - Added `outer_width` and `outer_depth` fields to rack model
* [#1444](https://github.com/digitalocean/netbox/issues/1444) - Added an `asset_tag` field for racks
* [#2000](https://github.com/digitalocean/netbox/issues/2000) - Dropped support for Python 2
* [#2104](https://github.com/digitalocean/netbox/issues/2104) - Added a `status` field for racks
Expand Down
7 changes: 4 additions & 3 deletions netbox/dcim/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,15 @@ class RackSerializer(TaggitSerializer, CustomFieldModelSerializer):
role = NestedRackRoleSerializer(required=False, allow_null=True)
type = ChoiceField(choices=RACK_TYPE_CHOICES, required=False, allow_null=True)
width = ChoiceField(choices=RACK_WIDTH_CHOICES, required=False)
outer_unit = ChoiceField(choices=RACK_DIMENSION_UNIT_CHOICES, required=False)
tags = TagListSerializerField(required=False)

class Meta:
model = Rack
fields = [
'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'status', 'role', 'serial',
'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'comments', 'tags', 'custom_fields', 'created',
'last_updated',
'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit',
'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
# Omit the UniqueTogetherValidator that would be automatically added to validate (group, facility_id). This
# prevents facility_id from being interpreted as a required field.
Expand Down Expand Up @@ -504,7 +505,7 @@ class CableSerializer(ValidatedModelSerializer):
termination_a = serializers.SerializerMethodField(read_only=True)
termination_b = serializers.SerializerMethodField(read_only=True)
status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, required=False)
length_unit = ChoiceField(choices=LENGTH_UNIT_CHOICES, required=False)
length_unit = ChoiceField(choices=CABLE_LENGTH_UNIT_CHOICES, required=False)

class Meta:
model = Cable
Expand Down
7 changes: 6 additions & 1 deletion netbox/dcim/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,11 +362,16 @@

LENGTH_UNIT_METER = 'm'
LENGTH_UNIT_CENTIMETER = 'cm'
LENGTH_UNIT_MILLIMETER = 'mm'
LENGTH_UNIT_FOOT = 'ft'
LENGTH_UNIT_INCH = 'in'
LENGTH_UNIT_CHOICES = (
CABLE_LENGTH_UNIT_CHOICES = (
(LENGTH_UNIT_METER, 'Meters'),
(LENGTH_UNIT_CENTIMETER, 'Centimeters'),
(LENGTH_UNIT_FOOT, 'Feet'),
(LENGTH_UNIT_INCH, 'Inches'),
)
RACK_DIMENSION_UNIT_CHOICES = (
(LENGTH_UNIT_MILLIMETER, 'Millimeters'),
(LENGTH_UNIT_INCH, 'Inches'),
)
5 changes: 4 additions & 1 deletion netbox/dcim/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,10 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):

class Meta:
model = Rack
fields = ['name', 'serial', 'asset_tag', 'type', 'width', 'u_height', 'desc_units']
fields = [
'name', 'serial', 'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth',
'outer_unit',
]

def search(self, queryset, name, value):
if not value.strip():
Expand Down
27 changes: 23 additions & 4 deletions netbox/dcim/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ class Meta:
model = Rack
fields = [
'site', 'group', 'name', 'facility_id', 'tenant_group', 'tenant', 'status', 'role', 'serial', 'asset_tag',
'type', 'width', 'u_height', 'desc_units', 'comments', 'tags',
'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'comments', 'tags',
]
help_texts = {
'site': "The site at which the rack exists",
Expand Down Expand Up @@ -368,6 +368,11 @@ class RackCSVForm(forms.ModelForm):
),
help_text='Rail-to-rail width (in inches)'
)
outer_unit = CSVChoiceField(
choices=RACK_DIMENSION_UNIT_CHOICES,
required=False,
help_text='Unit for outer dimensions'
)

class Meta:
model = Rack
Expand Down Expand Up @@ -458,12 +463,26 @@ class RackBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
widget=BulkEditNullBooleanSelect,
label='Descending units'
)
outer_width = forms.IntegerField(
required=False,
min_value=1
)
outer_depth = forms.IntegerField(
required=False,
min_value=1
)
outer_unit = forms.ChoiceField(
choices=add_blank_choice(RACK_DIMENSION_UNIT_CHOICES),
required=False
)
comments = CommentField(
widget=SmallTextarea
)

class Meta:
nullable_fields = ['group', 'tenant', 'role', 'serial', 'asset_tag', 'comments']
nullable_fields = [
'group', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_depth', 'outer_unit', 'comments',
]


class RackFilterForm(BootstrapMixin, CustomFieldFilterForm):
Expand Down Expand Up @@ -1864,7 +1883,7 @@ class CableCSVForm(forms.ModelForm):
help_text='Cable type'
)
length_unit = CSVChoiceField(
choices=LENGTH_UNIT_CHOICES,
choices=CABLE_LENGTH_UNIT_CHOICES,
required=False,
help_text='Length unit'
)
Expand Down Expand Up @@ -1962,7 +1981,7 @@ class CableBulkEditForm(BootstrapMixin, BulkEditForm):
required=False
)
length_unit = forms.ChoiceField(
choices=add_blank_choice(LENGTH_UNIT_CHOICES),
choices=add_blank_choice(CABLE_LENGTH_UNIT_CHOICES),
required=False,
initial=''
)
Expand Down
15 changes: 15 additions & 0 deletions netbox/dcim/migrations/0068_rack_new_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,19 @@ class Migration(migrations.Migration):
name='asset_tag',
field=utilities.fields.NullableCharField(blank=True, max_length=50, null=True, unique=True),
),
migrations.AddField(
model_name='rack',
name='outer_depth',
field=models.PositiveSmallIntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='rack',
name='outer_unit',
field=models.CharField(blank=True, max_length=2),
),
migrations.AddField(
model_name='rack',
name='outer_width',
field=models.PositiveSmallIntegerField(blank=True, null=True),
),
]
28 changes: 26 additions & 2 deletions netbox/dcim/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,19 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
verbose_name='Descending units',
help_text='Units are numbered top-to-bottom'
)
outer_width = models.PositiveSmallIntegerField(
blank=True,
null=True
)
outer_depth = models.PositiveSmallIntegerField(
blank=True,
null=True
)
outer_unit = models.CharField(
choices=RACK_DIMENSION_UNIT_CHOICES,
max_length=2,
blank=True
)
comments = models.TextField(
blank=True
)
Expand All @@ -527,7 +540,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel):

csv_headers = [
'site', 'group_name', 'name', 'facility_id', 'tenant', 'status', 'role', 'type', 'serial', 'asset_tag', 'width',
'u_height', 'desc_units', 'comments',
'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'comments',
]

class Meta:
Expand All @@ -545,6 +558,14 @@ def get_absolute_url(self):

def clean(self):

# Validate outer dimensions and unit
if self.outer_width and not self.outer_unit:
raise ValidationError("Must specify a unit when setting an outer width")
if self.outer_depth and not self.outer_unit:
raise ValidationError("Must specify a unit when setting an outer depth")
if self.outer_unit and self.outer_width is None and self.outer_depth is None:
self.length_unit = ''

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()
Expand Down Expand Up @@ -591,6 +612,9 @@ def to_csv(self):
self.width,
self.u_height,
self.desc_units,
self.outer_width,
self.outer_depth,
self.outer_unit,
self.comments,
)

Expand Down Expand Up @@ -2410,7 +2434,7 @@ class Cable(ChangeLoggedModel):
null=True
)
length_unit = models.CharField(
choices=LENGTH_UNIT_CHOICES,
choices=CABLE_LENGTH_UNIT_CHOICES,
max_length=2,
blank=True
)
Expand Down
20 changes: 20 additions & 0 deletions netbox/templates/dcim/rack.html
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,26 @@ <h1>{% block title %}Rack {{ rack }}{% endblock %}</h1>
<td>Height</td>
<td>{{ rack.u_height }}U ({% if rack.desc_units %}descending{% else %}ascending{% endif %})</td>
</tr>
<tr>
<td>Outer Width</td>
<td>
{% if rack.outer_width %}
<span>{{ rack.outer_width }}{{ rack.outer_unit }}</span>
{% else %}
<span class="text-muted">&mdash;</span>
{% endif %}
</td>
</tr>
<tr>
<td>Outer Depth</td>
<td>
{% if rack.outer_depth %}
<span>{{ rack.outer_depth }}{{ rack.outer_unit }}</span>
{% else %}
<span class="text-muted">&mdash;</span>
{% endif %}
</td>
</tr>
</table>
</div>
{% include 'inc/custom_fields_panel.html' with obj=rack %}
Expand Down
12 changes: 12 additions & 0 deletions netbox/templates/dcim/rack_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@
{% render_field form.type %}
{% render_field form.width %}
{% render_field form.u_height %}
<div class="form-group">
<label class="col-md-3 control-label">Outer dimensions</label>
<div class="col-md-3">
{{ form.outer_width }}
</div>
<div class="col-md-3">
{{ form.outer_depth }}
</div>
<div class="col-md-2">
{{ form.outer_unit }}
</div>
</div>
{% render_field form.desc_units %}
</div>
</div>
Expand Down

0 comments on commit c60c550

Please sign in to comment.