Просмотр исходного кода

Fixes #4336: Ensure interfaces without a subinterface ID are ordered before subinterface zero

Jeremy Stretch 5 лет назад
Родитель
Сommit
131d2c97ca

+ 1 - 0
docs/release-notes/version-2.8.md

@@ -10,6 +10,7 @@
 
 * [#2994](https://github.com/netbox-community/netbox/issues/2994) - Prevent modifying termination points of existing cable to ensure end-to-end path integrity
 * [#3356](https://github.com/netbox-community/netbox/issues/3356) - Correct Swagger schema specification for the available prefixes/IPs API endpoints
+* [#4336](https://github.com/netbox-community/netbox/issues/4336) - Ensure interfaces without a subinterface ID are ordered before subinterface zero
 * [#4361](https://github.com/netbox-community/netbox/issues/4361) - Fix Type of `connection_state` in Swagger schema
 * [#4388](https://github.com/netbox-community/netbox/issues/4388) - Fix detection of connected endpoints when connecting rear ports
 * [#4489](https://github.com/netbox-community/netbox/issues/4489) - Fix display of parent/child role on device type view

+ 18 - 0
netbox/dcim/migrations/0105_interface_name_collation.py

@@ -0,0 +1,18 @@
+# Generated by Django 3.0.5 on 2020-04-21 20:13
+
+from django.db import migrations
+import utilities.query_functions
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0104_correct_infiniband_types'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='interface',
+            options={'ordering': ('device', utilities.query_functions.CollateAsChar('_name'))},
+        ),
+    ]

+ 2 - 1
netbox/dcim/models/device_components.py

@@ -16,6 +16,7 @@ from extras.models import ObjectChange, TaggedItem
 from extras.utils import extras_features
 from utilities.fields import NaturalOrderingField
 from utilities.ordering import naturalize_interface
+from utilities.query_functions import CollateAsChar
 from utilities.utils import serialize_object
 from virtualization.choices import VMInterfaceTypeChoices
 
@@ -676,7 +677,7 @@ class Interface(CableTermination, ComponentModel):
 
     class Meta:
         # TODO: ordering and unique_together should include virtual_machine
-        ordering = ('device', '_name')
+        ordering = ('device', CollateAsChar('_name'))
         unique_together = ('device', 'name')
 
     def __str__(self):

+ 6 - 0
netbox/dcim/tests/test_natural_ordering.py

@@ -23,28 +23,34 @@ class NaturalOrderingTestCase(TestCase):
 
         INTERFACES = [
             '0',
+            '0.0',
             '0.1',
             '0.2',
             '0.10',
             '0.100',
             '0:1',
+            '0:1.0',
             '0:1.1',
             '0:1.2',
             '0:1.10',
             '0:2',
+            '0:2.0',
             '0:2.1',
             '0:2.2',
             '0:2.10',
             '1',
+            '1.0',
             '1.1',
             '1.2',
             '1.10',
             '1.100',
             '1:1',
+            '1:1.0',
             '1:1.1',
             '1:1.2',
             '1:1.10',
             '1:2',
+            '1:2.0',
             '1:2.1',
             '1:2.2',
             '1:2.10',

+ 1 - 1
netbox/utilities/ordering.py

@@ -75,7 +75,7 @@ def naturalize_interface(value, max_length):
         if part is not None:
             output += part.rjust(6, '0')
         else:
-            output += '000000'
+            output += '......'
 
     # Finally, naturalize any remaining text and append it
     if match.group('remainder') is not None and len(output) < max_length:

+ 9 - 0
netbox/utilities/query_functions.py

@@ -0,0 +1,9 @@
+from django.db.models import F, Func
+
+
+class CollateAsChar(Func):
+    """
+    Disregard localization by collating a field as a plain character string. Helpful for ensuring predictable ordering.
+    """
+    function = 'C'
+    template = '(%(expressions)s) COLLATE "%(function)s"'

+ 19 - 16
netbox/utilities/tests/test_ordering.py

@@ -30,29 +30,32 @@ class NaturalizationTestCase(TestCase):
 
         # Original, naturalized
         data = (
+
             # IOS/JunOS-style
-            ('Gi', '9999999999999999Gi000000000000000000'),
-            ('Gi1', '9999999999999999Gi000001000000000000'),
-            ('Gi1.0', '9999999999999999Gi000001000000000000'),
-            ('Gi1.1', '9999999999999999Gi000001000000000001'),
-            ('Gi1:0', '9999999999999999Gi000001000000000000'),
+            ('Gi', '9999999999999999Gi..................'),
+            ('Gi1', '9999999999999999Gi000001............'),
+            ('Gi1.0', '9999999999999999Gi000001......000000'),
+            ('Gi1.1', '9999999999999999Gi000001......000001'),
+            ('Gi1:0', '9999999999999999Gi000001000000......'),
             ('Gi1:0.0', '9999999999999999Gi000001000000000000'),
             ('Gi1:0.1', '9999999999999999Gi000001000000000001'),
-            ('Gi1:1', '9999999999999999Gi000001000001000000'),
+            ('Gi1:1', '9999999999999999Gi000001000001......'),
             ('Gi1:1.0', '9999999999999999Gi000001000001000000'),
             ('Gi1:1.1', '9999999999999999Gi000001000001000001'),
-            ('Gi1/2', '0001999999999999Gi000002000000000000'),
-            ('Gi1/2/3', '0001000299999999Gi000003000000000000'),
-            ('Gi1/2/3/4', '0001000200039999Gi000004000000000000'),
-            ('Gi1/2/3/4/5', '0001000200030004Gi000005000000000000'),
-            ('Gi1/2/3/4/5:6', '0001000200030004Gi000005000006000000'),
+            ('Gi1/2', '0001999999999999Gi000002............'),
+            ('Gi1/2/3', '0001000299999999Gi000003............'),
+            ('Gi1/2/3/4', '0001000200039999Gi000004............'),
+            ('Gi1/2/3/4/5', '0001000200030004Gi000005............'),
+            ('Gi1/2/3/4/5:6', '0001000200030004Gi000005000006......'),
             ('Gi1/2/3/4/5:6.7', '0001000200030004Gi000005000006000007'),
+
             # Generic
-            ('Interface 1', '9999999999999999Interface 000001000000000000'),
-            ('Interface 1 (other)', '9999999999999999Interface 000001000000000000 (other)'),
-            ('Interface 99', '9999999999999999Interface 000099000000000000'),
-            ('PCIe1-p1', '9999999999999999PCIe000001000000000000-p00000001'),
-            ('PCIe1-p99', '9999999999999999PCIe000001000000000000-p00000099'),
+            ('Interface 1', '9999999999999999Interface 000001............'),
+            ('Interface 1 (other)', '9999999999999999Interface 000001............ (other)'),
+            ('Interface 99', '9999999999999999Interface 000099............'),
+            ('PCIe1-p1', '9999999999999999PCIe000001............-p00000001'),
+            ('PCIe1-p99', '9999999999999999PCIe000001............-p00000099'),
+
         )
 
         for origin, naturalized in data: