mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-24 09:28:38 -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:
|
class Meta:
|
||||||
model = Rack
|
model = Rack
|
||||||
fields = ['id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'role', 'type', 'width',
|
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):
|
class RackNestedSerializer(RackSerializer):
|
||||||
@ -94,7 +94,7 @@ class RackDetailSerializer(RackSerializer):
|
|||||||
|
|
||||||
class Meta(RackSerializer.Meta):
|
class Meta(RackSerializer.Meta):
|
||||||
fields = ['id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'role', 'type', 'width',
|
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):
|
def get_front_units(self, obj):
|
||||||
units = obj.get_rack_units(face=RACK_FACE_FRONT)
|
units = obj.get_rack_units(face=RACK_FACE_FRONT)
|
||||||
|
@ -142,7 +142,8 @@ class RackForm(BootstrapMixin, CustomFieldForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Rack
|
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 = {
|
help_texts = {
|
||||||
'site': "The site at which the rack exists",
|
'site': "The site at which the rack exists",
|
||||||
'name': "Organizational rack name",
|
'name': "Organizational rack name",
|
||||||
@ -178,7 +179,8 @@ class RackFromCSVForm(forms.ModelForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Rack
|
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):
|
def clean(self):
|
||||||
|
|
||||||
@ -368,7 +370,7 @@ class DeviceForm(BootstrapMixin, CustomFieldForm):
|
|||||||
attrs={'filter-for': 'position'}
|
attrs={'filter-for': 'position'}
|
||||||
))
|
))
|
||||||
position = forms.TypedChoiceField(required=False, empty_value=None,
|
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}}',
|
widget=APISelect(api_url='/api/dcim/racks/{{rack}}/rack-units/?face={{face}}',
|
||||||
disabled_indicator='device'))
|
disabled_indicator='device'))
|
||||||
manufacturer = forms.ModelChoiceField(queryset=Manufacturer.objects.all(),
|
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')
|
help_text='Rail-to-rail width')
|
||||||
u_height = models.PositiveSmallIntegerField(default=42, verbose_name='Height (U)',
|
u_height = models.PositiveSmallIntegerField(default=42, verbose_name='Height (U)',
|
||||||
validators=[MinValueValidator(1), MaxValueValidator(100)])
|
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)
|
comments = models.TextField(blank=True)
|
||||||
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
|
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
|
||||||
|
|
||||||
@ -422,7 +424,10 @@ class Rack(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def units(self):
|
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
|
@property
|
||||||
def display_name(self):
|
def display_name(self):
|
||||||
@ -441,7 +446,7 @@ class Rack(CreatedUpdatedModel, CustomFieldModel):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
elevation = OrderedDict()
|
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}
|
elevation[u] = {'id': u, 'name': 'U{}'.format(u), 'face': face, 'device': None}
|
||||||
|
|
||||||
# Add devices to rack units list
|
# 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)
|
rack = models.ForeignKey('Rack', related_name='devices', on_delete=models.PROTECT)
|
||||||
position = models.PositiveSmallIntegerField(blank=True, null=True, validators=[MinValueValidator(1)],
|
position = models.PositiveSmallIntegerField(blank=True, null=True, validators=[MinValueValidator(1)],
|
||||||
verbose_name='Position (U)',
|
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')
|
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')
|
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,
|
primary_ip4 = models.OneToOneField('ipam.IPAddress', related_name='primary_ip4_for', on_delete=models.SET_NULL,
|
||||||
|
@ -49,6 +49,7 @@ class SiteTest(APITestCase):
|
|||||||
'type',
|
'type',
|
||||||
'width',
|
'width',
|
||||||
'u_height',
|
'u_height',
|
||||||
|
'desc_units',
|
||||||
'comments',
|
'comments',
|
||||||
'custom_fields',
|
'custom_fields',
|
||||||
]
|
]
|
||||||
@ -129,6 +130,7 @@ class RackTest(APITestCase):
|
|||||||
'type',
|
'type',
|
||||||
'width',
|
'width',
|
||||||
'u_height',
|
'u_height',
|
||||||
|
'desc_units',
|
||||||
'comments',
|
'comments',
|
||||||
'custom_fields',
|
'custom_fields',
|
||||||
]
|
]
|
||||||
@ -145,6 +147,7 @@ class RackTest(APITestCase):
|
|||||||
'type',
|
'type',
|
||||||
'width',
|
'width',
|
||||||
'u_height',
|
'u_height',
|
||||||
|
'desc_units',
|
||||||
'comments',
|
'comments',
|
||||||
'custom_fields',
|
'custom_fields',
|
||||||
'front_units',
|
'front_units',
|
||||||
|
@ -78,7 +78,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Position (U)</td>
|
<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>
|
<td>21</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -122,7 +122,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Height</td>
|
<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>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Devices</td>
|
<td>Devices</td>
|
||||||
@ -189,13 +189,13 @@
|
|||||||
<div class="rack_header">
|
<div class="rack_header">
|
||||||
<h4>Front</h4>
|
<h4>Front</h4>
|
||||||
</div>
|
</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>
|
||||||
<div class="col-md-6 col-sm-6 col-xs-12">
|
<div class="col-md-6 col-sm-6 col-xs-12">
|
||||||
<div class="rack_header">
|
<div class="rack_header">
|
||||||
<h4>Rear</h4>
|
<h4>Rear</h4>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
{% render_field form.type %}
|
{% render_field form.type %}
|
||||||
{% render_field form.width %}
|
{% render_field form.width %}
|
||||||
{% render_field form.u_height %}
|
{% render_field form.u_height %}
|
||||||
|
{% render_field form.desc_units %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if form.custom_fields %}
|
{% if form.custom_fields %}
|
||||||
|
@ -73,10 +73,15 @@
|
|||||||
<td>Height in rack units</td>
|
<td>Height in rack units</td>
|
||||||
<td>42</td>
|
<td>42</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Descending units</td>
|
||||||
|
<td>Units are numbered top-to-bottom</td>
|
||||||
|
<td>False</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<h4>Example</h4>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
Loading…
Reference in New Issue
Block a user