1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Added length and length_unit fields to Cable

This commit is contained in:
Jeremy Stretch
2018-10-26 10:28:25 -04:00
parent 2d90fc608e
commit 6bea8cc546
9 changed files with 97 additions and 8 deletions

View File

@ -317,3 +317,14 @@ COMPATIBLE_TERMINATION_TYPES = {
'frontport': ['consoleport', 'consoleserverport', 'interface', 'frontport', 'rearport'], 'frontport': ['consoleport', 'consoleserverport', 'interface', 'frontport', 'rearport'],
'rearport': ['consoleport', 'consoleserverport', 'interface', 'frontport', 'rearport'], 'rearport': ['consoleport', 'consoleserverport', 'interface', 'frontport', 'rearport'],
} }
LENGTH_UNIT_METER = 'm'
LENGTH_UNIT_CENTIMETER = 'cm'
LENGTH_UNIT_FOOT = 'ft'
LENGTH_UNIT_INCH = 'in'
LENGTH_UNIT_CHOICES = (
(LENGTH_UNIT_METER, 'Meters'),
(LENGTH_UNIT_CENTIMETER, 'Centimeters'),
(LENGTH_UNIT_FOOT, 'Feet'),
(LENGTH_UNIT_INCH, 'Inches'),
)

View File

@ -786,10 +786,19 @@ class VirtualChassisFilter(django_filters.FilterSet):
class CableFilter(django_filters.FilterSet): class CableFilter(django_filters.FilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
)
class Meta: class Meta:
model = Cable model = Cable
fields = ['type', 'status', 'color'] fields = ['type', 'status', 'color', 'length', 'length_unit']
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(label__icontains=value)
class ConsoleConnectionFilter(django_filters.FilterSet): class ConsoleConnectionFilter(django_filters.FilterSet):

View File

@ -2211,7 +2211,7 @@ class CableForm(BootstrapMixin, forms.ModelForm):
class Meta: class Meta:
model = Cable model = Cable
fields = ('type', 'status', 'label', 'color') fields = ('type', 'status', 'label', 'color', 'length', 'length_unit')
class CableFilterForm(BootstrapMixin, forms.Form): class CableFilterForm(BootstrapMixin, forms.Form):

View File

@ -123,6 +123,9 @@ class Migration(migrations.Migration):
('status', models.BooleanField(default=True)), ('status', models.BooleanField(default=True)),
('label', models.CharField(blank=True, max_length=100)), ('label', models.CharField(blank=True, max_length=100)),
('color', utilities.fields.ColorField(blank=True, max_length=6)), ('color', utilities.fields.ColorField(blank=True, max_length=6)),
('length', models.PositiveSmallIntegerField(blank=True, null=True)),
('length_unit', models.CharField(blank=True, max_length=2)),
('_abs_length', models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True)),
('termination_a_type', models.ForeignKey(limit_choices_to={'model__in': ['consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport']}, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType')), ('termination_a_type', models.ForeignKey(limit_choices_to={'model__in': ['consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport']}, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType')),
('termination_b_type', models.ForeignKey(limit_choices_to={'model__in': ['consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport']}, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType')), ('termination_b_type', models.ForeignKey(limit_choices_to={'model__in': ['consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport']}, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType')),
], ],

View File

@ -20,7 +20,7 @@ from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange
from utilities.fields import ColorField, NullableCharField from utilities.fields import ColorField, NullableCharField
from utilities.managers import NaturalOrderByManager from utilities.managers import NaturalOrderByManager
from utilities.models import ChangeLoggedModel from utilities.models import ChangeLoggedModel
from utilities.utils import serialize_object from utilities.utils import serialize_object, to_meters
from .constants import * from .constants import *
from .fields import ASNField, MACAddressField from .fields import ASNField, MACAddressField
from .querysets import CableQuerySet, InterfaceQuerySet from .querysets import CableQuerySet, InterfaceQuerySet
@ -2366,6 +2366,22 @@ class Cable(ChangeLoggedModel):
color = ColorField( color = ColorField(
blank=True blank=True
) )
length = models.PositiveSmallIntegerField(
blank=True,
null=True
)
length_unit = models.CharField(
choices=LENGTH_UNIT_CHOICES,
max_length=2,
blank=True
)
# Stores the normalized length (in meters) for database ordering
_abs_length = models.DecimalField(
max_digits=10,
decimal_places=4,
blank=True,
null=True
)
objects = CableQuerySet.as_manager() objects = CableQuerySet.as_manager()
@ -2383,6 +2399,14 @@ class Cable(ChangeLoggedModel):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('dcim:cable', args=[self.pk]) return reverse('dcim:cable', args=[self.pk])
def save(self, *args, **kwargs):
# Store the given length (if any) in meters for use in database ordering
if self.length and self.length_unit:
self._abs_length = to_meters(self.length, self.length_unit)
super(Cable, self).save(*args, **kwargs)
def get_path_endpoints(self): def get_path_endpoints(self):
""" """
Traverse both ends of a cable path and return its connected endpoints. Note that one or both endpoints may be Traverse both ends of a cable path and return its connected endpoints. Note that one or both endpoints may be
@ -2412,13 +2436,13 @@ class Cable(ChangeLoggedModel):
return termination return termination
# Find the cable (if any) attached to the peer port # Find the cable (if any) attached to the peer port
next_cable, far_end = peer_port.get_connection() next_cable = peer_port.get_connected_cable()
# If no cable exists, return None # If no cable exists, return None
if next_cable is None: if next_cable is None:
return None return None
# Return the far side termination of the cable # Return the far side termination of the cable
return trace_cable(far_end, position) return trace_cable(next_cable.far_end, position)
return trace_cable(self.termination_a), trace_cable(self.termination_b) return trace_cable(self.termination_a), trace_cable(self.termination_b)

View File

@ -170,6 +170,10 @@ VIRTUALCHASSIS_ACTIONS = """
{% endif %} {% endif %}
""" """
CABLE_LENGTH = """
{% if record.length %}{{ record.length }}{{ record.length_unit }}{% else %}—{% endif %}
"""
# #
# Regions # Regions
@ -626,6 +630,10 @@ class CableTable(BaseTable):
args=[Accessor('pk')], args=[Accessor('pk')],
verbose_name='PK' verbose_name='PK'
) )
label_ = tables.Column(
accessor=Accessor('label'),
verbose_name='Label'
)
device_a = tables.LinkColumn( device_a = tables.LinkColumn(
viewname='dcim:device', viewname='dcim:device',
accessor=Accessor('termination_a.device'), accessor=Accessor('termination_a.device'),
@ -650,10 +658,14 @@ class CableTable(BaseTable):
orderable=False, orderable=False,
verbose_name='Component' verbose_name='Component'
) )
length = tables.TemplateColumn(
template_code=CABLE_LENGTH,
order_by='_abs_length'
)
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Cable model = Cable
fields = ('pk', 'device_a', 'termination_a', 'device_b', 'termination_b', 'status', 'type', 'color') fields = ('pk', 'label_', 'device_a', 'termination_a', 'device_b', 'termination_b', 'status', 'type', 'color')
# #

View File

@ -48,7 +48,17 @@
<tr> <tr>
<td>Color</td> <td>Color</td>
<td> <td>
<label class="label" style="background-color: #{{ cable.color }}">{{ cable.color }}</label> <span style="background-color: #{{ cable.color }}">{{ cable.color }}A</span>
</td>
</tr>
<tr>
<td>Length</td>
<td>
{% if cable.length %}
{{ cable.length }} {{ cable.get_length_unit_display }}
{% else %}
<span class="text-muted">N/A</span>
{% endif %}
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -7,6 +7,6 @@
</tr> </tr>
<tr> <tr>
<td>Component</td> <td>Component</td>
<td>{{ termination_a }}</td> <td>{{ termination }}</td>
</tr> </tr>
</table> </table>

View File

@ -5,6 +5,8 @@ import six
from django.core.serializers import serialize from django.core.serializers import serialize
from django.http import HttpResponse from django.http import HttpResponse
from dcim.constants import LENGTH_UNIT_CENTIMETER, LENGTH_UNIT_FOOT, LENGTH_UNIT_INCH, LENGTH_UNIT_METER
def csv_format(data): def csv_format(data):
""" """
@ -107,3 +109,21 @@ def serialize_object(obj, extra=None):
data.update(extra) data.update(extra)
return data return data
def to_meters(length, unit):
"""
Convert the given length to meters.
"""
length = int(length)
if length < 0:
raise ValueError("Length must be a positive integer")
if unit == LENGTH_UNIT_METER:
return length
if unit == LENGTH_UNIT_CENTIMETER:
return length / 100
if unit == LENGTH_UNIT_FOOT:
return length * 0.3048
if unit == LENGTH_UNIT_INCH:
return length * 0.3048 * 12
raise ValueError("Unknown unit {}. Must be 'm', 'cm', 'ft', or 'in'.".format(unit))