diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index 1f3ced50a..ef7a4be60 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -79,7 +79,7 @@ class RackSerializer(CustomFieldSerializer, serializers.ModelSerializer): class Meta: model = Rack fields = ['id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'role', 'type', 'width', - 'u_height', 'comments', 'custom_fields'] + 'u_height', 'desc_units', 'comments', 'custom_fields'] class RackNestedSerializer(RackSerializer): @@ -94,7 +94,7 @@ class RackDetailSerializer(RackSerializer): class Meta(RackSerializer.Meta): fields = ['id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'role', 'type', 'width', - 'u_height', 'comments', 'custom_fields', 'front_units', 'rear_units'] + 'u_height', 'desc_units', 'comments', 'custom_fields', 'front_units', 'rear_units'] def get_front_units(self, obj): units = obj.get_rack_units(face=RACK_FACE_FRONT) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 3c6ac7e43..7e0867396 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -142,7 +142,8 @@ class RackForm(BootstrapMixin, CustomFieldForm): class Meta: model = Rack - fields = ['site', 'group', 'name', 'facility_id', 'tenant', 'role', 'type', 'width', 'u_height', 'comments'] + fields = ['site', 'group', 'name', 'facility_id', 'tenant', 'role', 'type', 'width', 'u_height', 'desc_units', + 'comments'] help_texts = { 'site': "The site at which the rack exists", 'name': "Organizational rack name", @@ -178,7 +179,8 @@ class RackFromCSVForm(forms.ModelForm): class Meta: model = Rack - fields = ['site', 'group_name', 'name', 'facility_id', 'tenant', 'role', 'type', 'width', 'u_height'] + fields = ['site', 'group_name', 'name', 'facility_id', 'tenant', 'role', 'type', 'width', 'u_height', + 'desc_units'] def clean(self): @@ -368,7 +370,7 @@ class DeviceForm(BootstrapMixin, CustomFieldForm): attrs={'filter-for': 'position'} )) position = forms.TypedChoiceField(required=False, empty_value=None, - help_text="For multi-U devices, this is the lowest occupied rack unit.", + help_text="The lowest-numbered unit occupied by the device", widget=APISelect(api_url='/api/dcim/racks/{{rack}}/rack-units/?face={{face}}', disabled_indicator='device')) manufacturer = forms.ModelChoiceField(queryset=Manufacturer.objects.all(), diff --git a/netbox/dcim/migrations/0020_rack_desc_units.py b/netbox/dcim/migrations/0020_rack_desc_units.py new file mode 100644 index 000000000..d5a74706d --- /dev/null +++ b/netbox/dcim/migrations/0020_rack_desc_units.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10 on 2016-10-28 15:01 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0019_new_iface_form_factors'), + ] + + operations = [ + migrations.AddField( + model_name='rack', + name='desc_units', + field=models.BooleanField(default=False, help_text=b'Units are numbered top-to-bottom', verbose_name=b'Descending units'), + ), + ] diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index a7ef02396..b40770c35 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -375,6 +375,8 @@ class Rack(CreatedUpdatedModel, CustomFieldModel): help_text='Rail-to-rail width') u_height = models.PositiveSmallIntegerField(default=42, verbose_name='Height (U)', validators=[MinValueValidator(1), MaxValueValidator(100)]) + desc_units = models.BooleanField(default=False, verbose_name='Descending units', + help_text='Units are numbered top-to-bottom') comments = models.TextField(blank=True) custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id') @@ -422,7 +424,10 @@ class Rack(CreatedUpdatedModel, CustomFieldModel): @property def units(self): - return reversed(range(1, self.u_height + 1)) + if self.desc_units: + return range(1, self.u_height + 1) + else: + return reversed(range(1, self.u_height + 1)) @property def display_name(self): @@ -441,7 +446,7 @@ class Rack(CreatedUpdatedModel, CustomFieldModel): """ elevation = OrderedDict() - for u in reversed(range(1, self.u_height + 1)): + for u in self.units: elevation[u] = {'id': u, 'name': 'U{}'.format(u), 'face': face, 'device': None} # Add devices to rack units list @@ -815,7 +820,7 @@ class Device(CreatedUpdatedModel, CustomFieldModel): rack = models.ForeignKey('Rack', related_name='devices', on_delete=models.PROTECT) position = models.PositiveSmallIntegerField(blank=True, null=True, validators=[MinValueValidator(1)], verbose_name='Position (U)', - help_text='Number of the lowest U position occupied by the device') + help_text='The lowest-numbered unit occupied by the device') face = models.PositiveSmallIntegerField(blank=True, null=True, choices=RACK_FACE_CHOICES, verbose_name='Rack face') status = models.BooleanField(choices=STATUS_CHOICES, default=STATUS_ACTIVE, verbose_name='Status') primary_ip4 = models.OneToOneField('ipam.IPAddress', related_name='primary_ip4_for', on_delete=models.SET_NULL, diff --git a/netbox/dcim/tests/test_apis.py b/netbox/dcim/tests/test_apis.py index 5f52776c3..352c36899 100644 --- a/netbox/dcim/tests/test_apis.py +++ b/netbox/dcim/tests/test_apis.py @@ -49,6 +49,7 @@ class SiteTest(APITestCase): 'type', 'width', 'u_height', + 'desc_units', 'comments', 'custom_fields', ] @@ -129,6 +130,7 @@ class RackTest(APITestCase): 'type', 'width', 'u_height', + 'desc_units', 'comments', 'custom_fields', ] @@ -145,6 +147,7 @@ class RackTest(APITestCase): 'type', 'width', 'u_height', + 'desc_units', 'comments', 'custom_fields', 'front_units', diff --git a/netbox/templates/dcim/device_import.html b/netbox/templates/dcim/device_import.html index 5220e6c2d..a603ab4ef 100644 --- a/netbox/templates/dcim/device_import.html +++ b/netbox/templates/dcim/device_import.html @@ -78,7 +78,7 @@ Position (U) - Lowest rack unit occupied by the device (optional) + Lowest-numbered rack unit occupied by the device (optional) 21 diff --git a/netbox/templates/dcim/_rack_elevation.html b/netbox/templates/dcim/inc/_rack_elevation.html similarity index 100% rename from netbox/templates/dcim/_rack_elevation.html rename to netbox/templates/dcim/inc/_rack_elevation.html diff --git a/netbox/templates/dcim/rack.html b/netbox/templates/dcim/rack.html index 4c2aef15d..af457a21d 100644 --- a/netbox/templates/dcim/rack.html +++ b/netbox/templates/dcim/rack.html @@ -122,7 +122,7 @@ Height - {{ rack.u_height }}U + {{ rack.u_height }}U ({% if rack.desc_units %}descending{% else %}ascending{% endif %}) Devices @@ -189,13 +189,13 @@

Front

- {% include 'dcim/_rack_elevation.html' with primary_face=front_elevation secondary_face=rear_elevation face_id=0 %} + {% include 'dcim/inc/_rack_elevation.html' with primary_face=front_elevation secondary_face=rear_elevation face_id=0 %}

Rear

- {% include 'dcim/_rack_elevation.html' with primary_face=rear_elevation secondary_face=front_elevation face_id=1 %} + {% include 'dcim/inc/_rack_elevation.html' with primary_face=rear_elevation secondary_face=front_elevation face_id=1 %}
diff --git a/netbox/templates/dcim/rack_edit.html b/netbox/templates/dcim/rack_edit.html index c2066afcd..dd4f610c3 100644 --- a/netbox/templates/dcim/rack_edit.html +++ b/netbox/templates/dcim/rack_edit.html @@ -14,6 +14,7 @@ {% render_field form.type %} {% render_field form.width %} {% render_field form.u_height %} + {% render_field form.desc_units %} {% if form.custom_fields %} diff --git a/netbox/templates/dcim/rack_import.html b/netbox/templates/dcim/rack_import.html index c5775cffd..807bff8eb 100644 --- a/netbox/templates/dcim/rack_import.html +++ b/netbox/templates/dcim/rack_import.html @@ -73,10 +73,15 @@ Height in rack units 42 + + Descending units + Units are numbered top-to-bottom + False +

Example

-
DC-4,Cage 1400,R101,J12.100,Pied Piper,Compute,4-post cabinet,19,42
+
DC-4,Cage 1400,R101,J12.100,Pied Piper,Compute,4-post cabinet,19,42,False
{% endblock %}