From 6bea8cc5468a7e4244b9ba3fb65e633fdff08c0f Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 26 Oct 2018 10:28:25 -0400 Subject: [PATCH] Added length and length_unit fields to Cable --- netbox/dcim/constants.py | 11 +++++++ netbox/dcim/filters.py | 11 ++++++- netbox/dcim/forms.py | 2 +- netbox/dcim/migrations/0066_cables.py | 3 ++ netbox/dcim/models.py | 30 +++++++++++++++++-- netbox/dcim/tables.py | 14 ++++++++- netbox/templates/dcim/cable.html | 12 +++++++- .../templates/dcim/inc/cable_termination.html | 2 +- netbox/utilities/utils.py | 20 +++++++++++++ 9 files changed, 97 insertions(+), 8 deletions(-) diff --git a/netbox/dcim/constants.py b/netbox/dcim/constants.py index fed089486..b5ceb5e3d 100644 --- a/netbox/dcim/constants.py +++ b/netbox/dcim/constants.py @@ -317,3 +317,14 @@ COMPATIBLE_TERMINATION_TYPES = { 'frontport': ['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'), +) \ No newline at end of file diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index 71f136d15..f0ce0cfea 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -786,10 +786,19 @@ class VirtualChassisFilter(django_filters.FilterSet): class CableFilter(django_filters.FilterSet): + q = django_filters.CharFilter( + method='search', + label='Search', + ) class Meta: 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): diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 883d72498..a09ee7161 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -2211,7 +2211,7 @@ class CableForm(BootstrapMixin, forms.ModelForm): class Meta: model = Cable - fields = ('type', 'status', 'label', 'color') + fields = ('type', 'status', 'label', 'color', 'length', 'length_unit') class CableFilterForm(BootstrapMixin, forms.Form): diff --git a/netbox/dcim/migrations/0066_cables.py b/netbox/dcim/migrations/0066_cables.py index 773dc4d3a..e095c3dda 100644 --- a/netbox/dcim/migrations/0066_cables.py +++ b/netbox/dcim/migrations/0066_cables.py @@ -123,6 +123,9 @@ class Migration(migrations.Migration): ('status', models.BooleanField(default=True)), ('label', models.CharField(blank=True, max_length=100)), ('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_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')), ], diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 881a3b2d7..e3032a295 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -20,7 +20,7 @@ from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange from utilities.fields import ColorField, NullableCharField from utilities.managers import NaturalOrderByManager from utilities.models import ChangeLoggedModel -from utilities.utils import serialize_object +from utilities.utils import serialize_object, to_meters from .constants import * from .fields import ASNField, MACAddressField from .querysets import CableQuerySet, InterfaceQuerySet @@ -2366,6 +2366,22 @@ class Cable(ChangeLoggedModel): color = ColorField( 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() @@ -2383,6 +2399,14 @@ class Cable(ChangeLoggedModel): def get_absolute_url(self): 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): """ 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 # 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 next_cable is None: return None # 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) diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index 7a27ecc64..09c210f30 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -170,6 +170,10 @@ VIRTUALCHASSIS_ACTIONS = """ {% endif %} """ +CABLE_LENGTH = """ +{% if record.length %}{{ record.length }}{{ record.length_unit }}{% else %}—{% endif %} +""" + # # Regions @@ -626,6 +630,10 @@ class CableTable(BaseTable): args=[Accessor('pk')], verbose_name='PK' ) + label_ = tables.Column( + accessor=Accessor('label'), + verbose_name='Label' + ) device_a = tables.LinkColumn( viewname='dcim:device', accessor=Accessor('termination_a.device'), @@ -650,10 +658,14 @@ class CableTable(BaseTable): orderable=False, verbose_name='Component' ) + length = tables.TemplateColumn( + template_code=CABLE_LENGTH, + order_by='_abs_length' + ) class Meta(BaseTable.Meta): 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') # diff --git a/netbox/templates/dcim/cable.html b/netbox/templates/dcim/cable.html index 72bea3ebc..7f38b1ffe 100644 --- a/netbox/templates/dcim/cable.html +++ b/netbox/templates/dcim/cable.html @@ -48,7 +48,17 @@ Color - + {{ cable.color }}A + + + + Length + + {% if cable.length %} + {{ cable.length }} {{ cable.get_length_unit_display }} + {% else %} + N/A + {% endif %} diff --git a/netbox/templates/dcim/inc/cable_termination.html b/netbox/templates/dcim/inc/cable_termination.html index 8089c6636..cc2ec60f5 100644 --- a/netbox/templates/dcim/inc/cable_termination.html +++ b/netbox/templates/dcim/inc/cable_termination.html @@ -7,6 +7,6 @@ Component - {{ termination_a }} + {{ termination }} diff --git a/netbox/utilities/utils.py b/netbox/utilities/utils.py index be756275e..4aa1b5b3c 100644 --- a/netbox/utilities/utils.py +++ b/netbox/utilities/utils.py @@ -5,6 +5,8 @@ import six from django.core.serializers import serialize 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): """ @@ -107,3 +109,21 @@ def serialize_object(obj, extra=None): data.update(extra) 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))