Update naturalize_interface

This commit is contained in:
Pieter Lambrecht 2022-06-01 14:28:49 +02:00
parent 2e5a5f71ba
commit e1fd723d54
2 changed files with 46 additions and 38 deletions

View File

@ -57,7 +57,7 @@ class NaturalOrderingField(models.CharField):
Generate a naturalized value from the target field Generate a naturalized value from the target field
""" """
original_value = getattr(model_instance, self.target_field) original_value = getattr(model_instance, self.target_field)
naturalized_value = self.naturalize_function(original_value, max_length=self.max_length) naturalized_value = self.naturalize_function(original_value, max_length=self.max_length, model_instance=model_instance)
setattr(model_instance, self.attname, naturalized_value) setattr(model_instance, self.attname, naturalized_value)
return naturalized_value return naturalized_value

View File

@ -1,17 +1,7 @@
import re import re
INTERFACE_NAME_REGEX = r'(^(?P<type>[^\d\.:]+)?)' \
r'((?P<slot>\d+)/)?' \
r'((?P<subslot>\d+)/)?' \
r'((?P<position>\d+)/)?' \
r'((?P<subposition>\d+)/)?' \
r'((?P<id>\d+))?' \
r'(:(?P<channel>\d+))?' \
r'(\.(?P<vc>\d+))?' \
r'(?P<remainder>.*)$'
def naturalize(value, max_length, integer_places=8, model_instance=None):
def naturalize(value, max_length, integer_places=8):
""" """
Take an alphanumeric string and prepend all integers to `integer_places` places to ensure the strings Take an alphanumeric string and prepend all integers to `integer_places` places to ensure the strings
are ordered naturally. For example: are ordered naturally. For example:
@ -29,6 +19,7 @@ def naturalize(value, max_length, integer_places=8):
:param value: The value to be naturalized :param value: The value to be naturalized
:param max_length: The maximum length of the returned string. Characters beyond this length will be stripped. :param max_length: The maximum length of the returned string. Characters beyond this length will be stripped.
:param integer_places: The number of places to which each integer will be expanded. (Default: 8) :param integer_places: The number of places to which each integer will be expanded. (Default: 8)
:param model_instance: The netbox model instance. This is required to modify the sort order based on the model characteristics
""" """
if not value: if not value:
return value return value
@ -43,43 +34,60 @@ def naturalize(value, max_length, integer_places=8):
return ret[:max_length] return ret[:max_length]
def naturalize_interface(value, max_length): def naturalize_interface(value, max_length, integer_places=5, model_instance=None):
""" """
Similar in nature to naturalize(), but takes into account a particular naming format adapted from the old Similar in nature to naturalize(), but takes into account a particular naming format adapted from the old
InterfaceManager. InterfaceManager.
:param value: The value to be naturalized :param value: The value to be naturalized
:param max_length: The maximum length of the returned string. Characters beyond this length will be stripped. :param max_length: The maximum length of the returned string. Characters beyond this length will be stripped.
:param integer_places: The number of places to which each integer will be expanded. (Default: 5, minimum: 5)
:param model_instance: The netbox model instance. This is required to modify the sort order based on the model characteristics
""" """
output = ''
match = re.search(INTERFACE_NAME_REGEX, value)
if match is None:
return value
# First, we order by slot/position, padding each to four digits. If a field is not present, digit_separators = [':', '/', '-']
# set it to 9999 to ensure it is ordered last. subinterface_separators = ['.']
for part_name in ('slot', 'subslot', 'position', 'subposition'): interface_remainder_len = 10
part = match.group(part_name)
if part is not None: interface_type_weight_list = {
output += part.rjust(4, '0') r'^([fgstx]e|et|lt)-': '05', # Group juniper interfaces (https://www.juniper.net/documentation/us/en/software/junos/interfaces-fundamentals/topics/topic-map/router-interfaces-overview.html). Other Juniper interfaces will come after these and sorted alphabetically
r'^ethernet': '10', # Group Arista Interfaces Ethernet1, Ethernet2, Ethernet49/1, Ethernet50/1
r'[' + ''.join(digit_separators) + ']+': '15', # Group Anything with a digit_separator
r'eth': '20', # Group Anything with eth in the name
}
if integer_places < 5:
integer_places = 5
output = value
parts = re.split(r'(\d+)', value)
if parts:
# If the last part of the interface name is non-digit, then this will be added to the naturalized name for sorting purposes. Maxlength is 10 (space padded)
interface_remainder = ''.ljust(interface_remainder_len, ' ')
if re.match(r'[^\d]+', parts[-1]):
interface_remainder = naturalize(parts[-1], interface_remainder_len).ljust(interface_remainder_len, ' ')
parts.pop()
interface_type_weight = value[:2].upper() # the two character of the interface name will determin the sort order
if getattr(model_instance, 'mgmt_only', False):
interface_type_weight = "ZZ" # mgmt interfaces are put at the end of the table
else: else:
output += '9999' for regmatch, weigth in interface_type_weight_list.items(): # unless it matches a specific pattern, then the interfaces will be grouped
if re.search(regmatch, value, re.IGNORECASE): # first match is applied
interface_type_weight = weigth
break
# Append the type, if any. output = interface_type_weight.rjust(2, '9')
if match.group('type') is not None:
output += match.group('type')
# Append any remaining fields, left-padding to six digits each. for part in parts:
for part_name in ('id', 'channel', 'vc'): if part.isdigit():
part = match.group(part_name) output += part.rjust(integer_places, '0') # zero-pre-pad the port number. 'integer_places' digits must be at least 5, because subinterfaces can go to 65535.
if part is not None: elif part in subinterface_separators: # replace the subinterface separators with a 0
output += part.rjust(6, '0') output += '0' # this will group subinterfaces with the master interface, when there are interfaces with more /'s (ex: Eth1.1 and Eth1/1.5)
else: elif part in digit_separators: # standardize the digit separators to a 9.
output += '......' output += '9'
# Finally, naturalize any remaining text and append it output = output.ljust(max_length - interface_remainder_len, '.') + interface_remainder
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] return output[:max_length]