Fixes #4211: Include trailing text when naturalizing interface names

This commit is contained in:
Jeremy Stretch 2020-02-20 09:46:24 -05:00
parent b38eeaebc9
commit 322b328584
3 changed files with 34 additions and 14 deletions

View File

@ -1,5 +1,12 @@
# v2.7.7 (FUTURE)
**Note:** This release fixes a bug affecting the natural ordering of interfaces. If any interfaces appear unordered in
NetBox, run the following management command to recalculate their naturalized values after upgrading:
```
python3 manage.py renaturalize dcim.Interface
```
## Enhancements
* [#2511](https://github.com/netbox-community/netbox/issues/2511) - Compare object change to the previous change
@ -19,6 +26,7 @@
* [#4196](https://github.com/netbox-community/netbox/issues/4196) - Fix exception when viewing LLDP neighbors page
* [#4202](https://github.com/netbox-community/netbox/issues/4202) - Prevent reassignment to master device when bulk editing VC member interfaces
* [#4204](https://github.com/netbox-community/netbox/issues/4204) - Fix assignment of mask length when bulk editing prefixes
* [#4211](https://github.com/netbox-community/netbox/issues/4211) - Include trailing text when naturalizing interface names
---

View File

@ -7,7 +7,8 @@ INTERFACE_NAME_REGEX = r'(^(?P<type>[^\d\.:]+)?)' \
r'((?P<subposition>\d+)/)?' \
r'((?P<id>\d+))?' \
r'(:(?P<channel>\d+))?' \
r'(.(?P<vc>\d+)$)?'
r'(\.(?P<vc>\d+))?' \
r'(?P<remainder>.*)$'
def naturalize(value, max_length, integer_places=8):
@ -50,7 +51,7 @@ def naturalize_interface(value, max_length):
:param value: The value to be naturalized
:param max_length: The maximum length of the returned string. Characters beyond this length will be stripped.
"""
output = []
output = ''
match = re.search(INTERFACE_NAME_REGEX, value)
if match is None:
return value
@ -60,21 +61,25 @@ def naturalize_interface(value, max_length):
for part_name in ('slot', 'subslot', 'position', 'subposition'):
part = match.group(part_name)
if part is not None:
output.append(part.rjust(4, '0'))
output += part.rjust(4, '0')
else:
output.append('9999')
output += '9999'
# Append the type, if any.
if match.group('type') is not None:
output.append(match.group('type'))
output += match.group('type')
# Finally, append any remaining fields, left-padding to six digits each.
# Append any remaining fields, left-padding to six digits each.
for part_name in ('id', 'channel', 'vc'):
part = match.group(part_name)
if part is not None:
output.append(part.rjust(6, '0'))
output += part.rjust(6, '0')
else:
output.append('000000')
output += '000000'
ret = ''.join(output)
return ret[:max_length]
# Finally, naturalize any remaining text and append it
if match.group('remainder') is not None and len(output) < max_length:
remainder = naturalize(match.group('remainder'), max_length - len(output))
output += remainder
return output[:max_length]

View File

@ -9,8 +9,8 @@ class NaturalizationTestCase(TestCase):
"""
def test_naturalize(self):
# Original, naturalized
data = (
# Original, naturalized
('abc', 'abc'),
('123', '00000123'),
('abc123', 'abc00000123'),
@ -21,15 +21,16 @@ class NaturalizationTestCase(TestCase):
)
for origin, naturalized in data:
self.assertEqual(naturalize(origin, max_length=50), naturalized)
self.assertEqual(naturalize(origin, max_length=100), naturalized)
def test_naturalize_max_length(self):
self.assertEqual(naturalize('abc123def456', max_length=10), 'abc0000012')
def test_naturalize_interface(self):
# Original, naturalized
data = (
# Original, naturalized
# IOS/JunOS-style
('Gi', '9999999999999999Gi000000000000000000'),
('Gi1', '9999999999999999Gi000001000000000000'),
('Gi1/2', '0001999999999999Gi000002000000000000'),
@ -40,10 +41,16 @@ class NaturalizationTestCase(TestCase):
('Gi1/2/3/4/5:6.7', '0001000200030004Gi000005000006000007'),
('Gi1:2', '9999999999999999Gi000001000002000000'),
('Gi1:2.3', '9999999999999999Gi000001000002000003'),
# Generic
('Interface 1', '9999999999999999Interface 000001000000000000'),
('Interface 1 (other)', '9999999999999999Interface 000001000000000000 (other)'),
('Interface 99', '9999999999999999Interface 000099000000000000'),
('PCIe1-p1', '9999999999999999PCIe000001000000000000-p00000001'),
('PCIe1-p99', '9999999999999999PCIe000001000000000000-p00000099'),
)
for origin, naturalized in data:
self.assertEqual(naturalize_interface(origin, max_length=50), naturalized)
self.assertEqual(naturalize_interface(origin, max_length=100), naturalized)
def test_naturalize_interface_max_length(self):
self.assertEqual(naturalize_interface('Gi1/2/3', max_length=20), '0001000299999999Gi00')