From 909323663eca94493a2eb64e8e08bcae58290be8 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 13 Feb 2020 21:41:00 -0500 Subject: [PATCH] Fixes #4166: Fix schema migrations to enforce maximum character length for naturalized fields --- docs/release-notes/version-2.7.md | 8 ++++++++ .../dcim/migrations/0093_device_component_ordering.py | 2 +- .../0094_device_component_template_ordering.py | 2 +- netbox/dcim/migrations/0095_primary_model_ordering.py | 2 +- netbox/dcim/migrations/0096_interface_ordering.py | 2 +- netbox/extras/management/commands/renaturalize.py | 2 +- netbox/utilities/ordering.py | 8 ++++---- netbox/utilities/tests/test_ordering.py | 10 ++++++++-- 8 files changed, 25 insertions(+), 11 deletions(-) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index 04b3972ca..75269b6ad 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -1,3 +1,11 @@ +# v2.7.6 (FUTURE) + +## Bug Fixes + +* [#4166](https://github.com/netbox-community/netbox/issues/4166) - Fix schema migrations to enforce maximum character length for naturalized fields + +--- + # v2.7.5 (2020-02-13) **Note:** This release includes several database schema migrations that calculate and store copies of names for certain objects to improve natural ordering performance (see [#3799](https://github.com/netbox-community/netbox/issues/3799)). These migrations may take a few minutes to run if you have a very large number of objects defined in NetBox. diff --git a/netbox/dcim/migrations/0093_device_component_ordering.py b/netbox/dcim/migrations/0093_device_component_ordering.py index 017241c8b..4e3c941a1 100644 --- a/netbox/dcim/migrations/0093_device_component_ordering.py +++ b/netbox/dcim/migrations/0093_device_component_ordering.py @@ -6,7 +6,7 @@ import utilities.ordering def _update_model_names(model): # Update each unique field value in bulk for name in model.objects.values_list('name', flat=True).order_by('name').distinct(): - model.objects.filter(name=name).update(_name=utilities.ordering.naturalize(name)) + model.objects.filter(name=name).update(_name=utilities.ordering.naturalize(name, max_length=100)) def naturalize_consoleports(apps, schema_editor): diff --git a/netbox/dcim/migrations/0094_device_component_template_ordering.py b/netbox/dcim/migrations/0094_device_component_template_ordering.py index fc39f76b2..24fe98e94 100644 --- a/netbox/dcim/migrations/0094_device_component_template_ordering.py +++ b/netbox/dcim/migrations/0094_device_component_template_ordering.py @@ -6,7 +6,7 @@ import utilities.ordering def _update_model_names(model): # Update each unique field value in bulk for name in model.objects.values_list('name', flat=True).order_by('name').distinct(): - model.objects.filter(name=name).update(_name=utilities.ordering.naturalize(name)) + model.objects.filter(name=name).update(_name=utilities.ordering.naturalize(name, max_length=100)) def naturalize_consoleporttemplates(apps, schema_editor): diff --git a/netbox/dcim/migrations/0095_primary_model_ordering.py b/netbox/dcim/migrations/0095_primary_model_ordering.py index 9cef0a581..3bc780161 100644 --- a/netbox/dcim/migrations/0095_primary_model_ordering.py +++ b/netbox/dcim/migrations/0095_primary_model_ordering.py @@ -6,7 +6,7 @@ import utilities.ordering def _update_model_names(model): # Update each unique field value in bulk for name in model.objects.values_list('name', flat=True).order_by('name').distinct(): - model.objects.filter(name=name).update(_name=utilities.ordering.naturalize(name)) + model.objects.filter(name=name).update(_name=utilities.ordering.naturalize(name, max_length=100)) def naturalize_sites(apps, schema_editor): diff --git a/netbox/dcim/migrations/0096_interface_ordering.py b/netbox/dcim/migrations/0096_interface_ordering.py index 284066462..f1622f504 100644 --- a/netbox/dcim/migrations/0096_interface_ordering.py +++ b/netbox/dcim/migrations/0096_interface_ordering.py @@ -6,7 +6,7 @@ import utilities.ordering def _update_model_names(model): # Update each unique field value in bulk for name in model.objects.values_list('name', flat=True).order_by('name').distinct(): - model.objects.filter(name=name).update(_name=utilities.ordering.naturalize_interface(name)) + model.objects.filter(name=name).update(_name=utilities.ordering.naturalize_interface(name, max_length=100)) def naturalize_interfacetemplates(apps, schema_editor): diff --git a/netbox/extras/management/commands/renaturalize.py b/netbox/extras/management/commands/renaturalize.py index 70f57c1ba..cfd037910 100644 --- a/netbox/extras/management/commands/renaturalize.py +++ b/netbox/extras/management/commands/renaturalize.py @@ -86,7 +86,7 @@ class Command(BaseCommand): # Find all unique values for the field queryset = model.objects.values_list(target_field, flat=True).order_by(target_field).distinct() for value in queryset: - naturalized_value = naturalize(value) + naturalized_value = naturalize(value, max_length=field.max_length) if options['verbosity'] >= 2: self.stdout.write(" {} -> {}".format(value, naturalized_value), ending='') diff --git a/netbox/utilities/ordering.py b/netbox/utilities/ordering.py index a560e776e..39e21733a 100644 --- a/netbox/utilities/ordering.py +++ b/netbox/utilities/ordering.py @@ -10,7 +10,7 @@ INTERFACE_NAME_REGEX = r'(^(?P[^\d\.:]+)?)' \ r'(.(?P\d+)$)?' -def naturalize(value, max_length=None, integer_places=8): +def naturalize(value, max_length, integer_places=8): """ Take an alphanumeric string and prepend all integers to `integer_places` places to ensure the strings are ordered naturally. For example: @@ -39,10 +39,10 @@ def naturalize(value, max_length=None, integer_places=8): output.append(segment) ret = ''.join(output) - return ret[:max_length] if max_length else ret + return ret[:max_length] -def naturalize_interface(value, max_length=None): +def naturalize_interface(value, max_length): """ Similar in nature to naturalize(), but takes into account a particular naming format adapted from the old InterfaceManager. @@ -77,4 +77,4 @@ def naturalize_interface(value, max_length=None): output.append('000000') ret = ''.join(output) - return ret[:max_length] if max_length else ret + return ret[:max_length] diff --git a/netbox/utilities/tests/test_ordering.py b/netbox/utilities/tests/test_ordering.py index a875c688c..958224c94 100644 --- a/netbox/utilities/tests/test_ordering.py +++ b/netbox/utilities/tests/test_ordering.py @@ -21,7 +21,10 @@ class NaturalizationTestCase(TestCase): ) for origin, naturalized in data: - self.assertEqual(naturalize(origin), naturalized) + self.assertEqual(naturalize(origin, max_length=50), naturalized) + + def test_naturalize_max_length(self): + self.assertEqual(naturalize('abc123def456', max_length=10), 'abc0000012') def test_naturalize_interface(self): @@ -40,4 +43,7 @@ class NaturalizationTestCase(TestCase): ) for origin, naturalized in data: - self.assertEqual(naturalize_interface(origin), naturalized) + self.assertEqual(naturalize_interface(origin, max_length=50), naturalized) + + def test_naturalize_interface_max_length(self): + self.assertEqual(naturalize_interface('Gi1/2/3', max_length=20), '0001000299999999Gi00')