mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 20:12:00 -06:00
Closes #191: Support for racks numbered top-to-bottom
This commit is contained in:
parent
f8f5d6876b
commit
2db50dd4a7
@ -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)
|
||||
|
@ -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(),
|
||||
|
20
netbox/dcim/migrations/0020_rack_desc_units.py
Normal file
20
netbox/dcim/migrations/0020_rack_desc_units.py
Normal file
@ -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'),
|
||||
),
|
||||
]
|
@ -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,
|
||||
|
@ -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',
|
||||
|
@ -78,7 +78,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Position (U)</td>
|
||||
<td>Lowest rack unit occupied by the device (optional)</td>
|
||||
<td>Lowest-numbered rack unit occupied by the device (optional)</td>
|
||||
<td>21</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -122,7 +122,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Height</td>
|
||||
<td>{{ rack.u_height }}U</td>
|
||||
<td>{{ rack.u_height }}U ({% if rack.desc_units %}descending{% else %}ascending{% endif %})</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Devices</td>
|
||||
@ -189,13 +189,13 @@
|
||||
<div class="rack_header">
|
||||
<h4>Front</h4>
|
||||
</div>
|
||||
{% 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 %}
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-6 col-xs-12">
|
||||
<div class="rack_header">
|
||||
<h4>Rear</h4>
|
||||
</div>
|
||||
{% 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 %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -14,6 +14,7 @@
|
||||
{% render_field form.type %}
|
||||
{% render_field form.width %}
|
||||
{% render_field form.u_height %}
|
||||
{% render_field form.desc_units %}
|
||||
</div>
|
||||
</div>
|
||||
{% if form.custom_fields %}
|
||||
|
@ -73,10 +73,15 @@
|
||||
<td>Height in rack units</td>
|
||||
<td>42</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Descending units</td>
|
||||
<td>Units are numbered top-to-bottom</td>
|
||||
<td>False</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h4>Example</h4>
|
||||
<pre>DC-4,Cage 1400,R101,J12.100,Pied Piper,Compute,4-post cabinet,19,42</pre>
|
||||
<pre>DC-4,Cage 1400,R101,J12.100,Pied Piper,Compute,4-post cabinet,19,42,False</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
Loading…
Reference in New Issue
Block a user