From 3d911532756a37a9b3f501a207d8afe0c56184cd Mon Sep 17 00:00:00 2001 From: Ryan Breaker Date: Tue, 24 Oct 2017 00:09:38 -0500 Subject: [PATCH 01/10] Add alphabetic variants to interface expansions --- netbox/utilities/forms.py | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index f63c0c11d..e888cb6ae 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -39,6 +39,7 @@ COLOR_CHOICES = ( ('111111', 'Black'), ) NUMERIC_EXPANSION_PATTERN = '\[((?:\d+[?:,-])+\d+)\]' +ALPHABETIC_EXPANSION_PATTERN = '\[((?:[a-z]+[?:,-])+[a-z]+)\]' IP4_EXPANSION_PATTERN = '\[((?:[0-9]{1,3}[?:,-])+[0-9]{1,3})\]' IP6_EXPANSION_PATTERN = '\[((?:[0-9a-f]{1,4}[?:,-])+[0-9a-f]{1,4})\]' @@ -77,6 +78,37 @@ def expand_numeric_pattern(string): yield "{}{}{}".format(lead, i, remnant) +def parse_alphabetic_range(string): + """ + Expand an alphabetic range (continuous or not) into a list. + 'a-d,f' => ['a', 'b', 'c', 'd', 'f'] + """ + values = [] + for dash_range in string.split(','): + try: + begin, end = dash_range.split('-') + except ValueError: + begin, end = dash_range, dash_range + nums = list(range(ord(begin), ord(end)+1)) + for n in nums: + values.append(chr(n)) + return values + + +def expand_alphabetic_pattern(string): + """ + Expand an alphabetic pattern into a list of strings. + """ + lead, pattern, remnant = re.split(ALPHABETIC_EXPANSION_PATTERN, string, maxsplit=1) + parsed_range = parse_alphabetic_range(pattern) + for i in parsed_range: + if re.search(ALPHABETIC_EXPANSION_PATTERN, remnant): + for string in expand_alphabetic_pattern(remnant): + yield "{}{}{}".format(lead, i, string) + else: + yield "{}{}{}".format(lead, i, remnant) + + def expand_ipaddress_pattern(string, family): """ Expand an IP address pattern into a list of strings. Examples: @@ -307,9 +339,12 @@ class ExpandableNameField(forms.CharField): 'Example: ge-0/0/[0-23,25,30]' def to_python(self, value): + values = [] if re.search(NUMERIC_EXPANSION_PATTERN, value): - return list(expand_numeric_pattern(value)) - return [value] + values += expand_numeric_pattern(value) + if re.search(ALPHABETIC_EXPANSION_PATTERN, value): + values += expand_alphabetic_pattern(value) + return values class ExpandableIPAddressField(forms.CharField): From c107f35118778aadd1cfdaf514425343d66611ef Mon Sep 17 00:00:00 2001 From: Ryan Breaker Date: Tue, 24 Oct 2017 17:55:00 -0500 Subject: [PATCH 02/10] Merge letters and numbers into one function --- netbox/utilities/forms.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index e888cb6ae..d1f19c23b 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -39,7 +39,7 @@ COLOR_CHOICES = ( ('111111', 'Black'), ) NUMERIC_EXPANSION_PATTERN = '\[((?:\d+[?:,-])+\d+)\]' -ALPHABETIC_EXPANSION_PATTERN = '\[((?:[a-z]+[?:,-])+[a-z]+)\]' +ALPHANUMERIC_EXPANSION_PATTERN = '\[((?:\w+[?:,-])+\w+)\]' IP4_EXPANSION_PATTERN = '\[((?:[0-9]{1,3}[?:,-])+[0-9]{1,3})\]' IP6_EXPANSION_PATTERN = '\[((?:[0-9a-f]{1,4}[?:,-])+[0-9a-f]{1,4})\]' @@ -78,9 +78,9 @@ def expand_numeric_pattern(string): yield "{}{}{}".format(lead, i, remnant) -def parse_alphabetic_range(string): +def parse_alphanumeric_range(string): """ - Expand an alphabetic range (continuous or not) into a list. + Expand an alphanumeric range (continuous or not) into a list. 'a-d,f' => ['a', 'b', 'c', 'd', 'f'] """ values = [] @@ -95,15 +95,15 @@ def parse_alphabetic_range(string): return values -def expand_alphabetic_pattern(string): +def expand_alphanumeric_pattern(string): """ Expand an alphabetic pattern into a list of strings. """ - lead, pattern, remnant = re.split(ALPHABETIC_EXPANSION_PATTERN, string, maxsplit=1) - parsed_range = parse_alphabetic_range(pattern) + lead, pattern, remnant = re.split(ALPHANUMERIC_EXPANSION_PATTERN, string, maxsplit=1) + parsed_range = parse_alphanumeric_range(pattern) for i in parsed_range: - if re.search(ALPHABETIC_EXPANSION_PATTERN, remnant): - for string in expand_alphabetic_pattern(remnant): + if re.search(ALPHANUMERIC_EXPANSION_PATTERN, remnant): + for string in expand_alphanumeric_pattern(remnant): yield "{}{}{}".format(lead, i, string) else: yield "{}{}{}".format(lead, i, remnant) @@ -339,12 +339,9 @@ class ExpandableNameField(forms.CharField): 'Example: ge-0/0/[0-23,25,30]' def to_python(self, value): - values = [] - if re.search(NUMERIC_EXPANSION_PATTERN, value): - values += expand_numeric_pattern(value) - if re.search(ALPHABETIC_EXPANSION_PATTERN, value): - values += expand_alphabetic_pattern(value) - return values + if re.search(ALPHANUMERIC_EXPANSION_PATTERN, value): + return list(expand_alphanumeric_pattern(value)) + return [value] class ExpandableIPAddressField(forms.CharField): From b295849f536e06eba6a40d6288f373d3839ce51e Mon Sep 17 00:00:00 2001 From: Ryan Breaker Date: Tue, 24 Oct 2017 19:30:43 -0500 Subject: [PATCH 03/10] Prevent mismatch of types in ranges --- netbox/utilities/forms.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index d1f19c23b..964524f09 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -39,7 +39,7 @@ COLOR_CHOICES = ( ('111111', 'Black'), ) NUMERIC_EXPANSION_PATTERN = '\[((?:\d+[?:,-])+\d+)\]' -ALPHANUMERIC_EXPANSION_PATTERN = '\[((?:\w+[?:,-])+\w+)\]' +ALPHANUMERIC_EXPANSION_PATTERN = '\[((?:[a-zA-Z0-9]+[?:,-])+[a-zA-Z0-9]+)\]' IP4_EXPANSION_PATTERN = '\[((?:[0-9]{1,3}[?:,-])+[0-9]{1,3})\]' IP6_EXPANSION_PATTERN = '\[((?:[0-9a-f]{1,4}[?:,-])+[0-9a-f]{1,4})\]' @@ -87,6 +87,8 @@ def parse_alphanumeric_range(string): for dash_range in string.split(','): try: begin, end = dash_range.split('-') + if (str.isalpha(begin) and str.isdigit(end)) or (str.isdigit(begin) and str.isalpha(end)): + continue # Skip if it's invalid, just like any other bad pattern except ValueError: begin, end = dash_range, dash_range nums = list(range(ord(begin), ord(end)+1)) From 3df7e283e38d9fee7ca5a450599e5b6fca9d284e Mon Sep 17 00:00:00 2001 From: Ryan Breaker Date: Tue, 24 Oct 2017 19:46:12 -0500 Subject: [PATCH 04/10] Prevent mismatch of cases in ranges --- netbox/utilities/forms.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index 964524f09..b0e741080 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -87,8 +87,11 @@ def parse_alphanumeric_range(string): for dash_range in string.split(','): try: begin, end = dash_range.split('-') + # Skip if incompatible types or mixed case, just like any other bad pattern if (str.isalpha(begin) and str.isdigit(end)) or (str.isdigit(begin) and str.isalpha(end)): - continue # Skip if it's invalid, just like any other bad pattern + continue + if not (str.isupper(begin + end) or str.islower(begin + end)): + continue except ValueError: begin, end = dash_range, dash_range nums = list(range(ord(begin), ord(end)+1)) From 33a99441a48dad31e3115798e03e49b503cdb279 Mon Sep 17 00:00:00 2001 From: Ryan Breaker Date: Tue, 24 Oct 2017 19:55:50 -0500 Subject: [PATCH 05/10] Update help text for ExpandableNameField --- netbox/utilities/forms.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index b0e741080..ce9ecd324 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -340,8 +340,11 @@ class ExpandableNameField(forms.CharField): def __init__(self, *args, **kwargs): super(ExpandableNameField, self).__init__(*args, **kwargs) if not self.help_text: - self.help_text = 'Numeric ranges are supported for bulk creation.
'\ - 'Example: ge-0/0/[0-23,25,30]' + self.help_text = 'Alphanumeric ranges are supported for bulk creation.
' \ + 'Mixed cases and types in ranges are not supported.
' \ + 'Examples:
  • ge-0/0/[0-23,25,30]
  • ' \ + '
  • e[0-3][a-d,f]
  • ' \ + '
  • e[0-3,a-d,f]
' def to_python(self, value): if re.search(ALPHANUMERIC_EXPANSION_PATTERN, value): From 1a6ee237f6ce71eb52c8804d31b3f15c63f153b3 Mon Sep 17 00:00:00 2001 From: Ryan Breaker Date: Tue, 24 Oct 2017 19:59:37 -0500 Subject: [PATCH 06/10] Update help text for ExpandableNameField (again) --- netbox/utilities/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index ce9ecd324..9edc37a51 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -341,7 +341,7 @@ class ExpandableNameField(forms.CharField): super(ExpandableNameField, self).__init__(*args, **kwargs) if not self.help_text: self.help_text = 'Alphanumeric ranges are supported for bulk creation.
' \ - 'Mixed cases and types in ranges are not supported.
' \ + 'Mixed cases and types within a single range are not supported.
' \ 'Examples:
  • ge-0/0/[0-23,25,30]
  • ' \ '
  • e[0-3][a-d,f]
  • ' \ '
  • e[0-3,a-d,f]
' From 53f58d4496668a54484346e5efb2e0ae6fa894ac Mon Sep 17 00:00:00 2001 From: Ryan Breaker Date: Tue, 24 Oct 2017 20:03:10 -0500 Subject: [PATCH 07/10] Update comment --- netbox/utilities/forms.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index 9edc37a51..09585a8ee 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -81,7 +81,8 @@ def expand_numeric_pattern(string): def parse_alphanumeric_range(string): """ Expand an alphanumeric range (continuous or not) into a list. - 'a-d,f' => ['a', 'b', 'c', 'd', 'f'] + 'a-d,f' => [a, b, c, d, f] + '0-3,a-d' => [0, 1, 2, 3, a, b, c, d] """ values = [] for dash_range in string.split(','): From 3d023126bad51a0d058fb82c22d6f4fb73b65d6a Mon Sep 17 00:00:00 2001 From: Ryan Breaker Date: Tue, 24 Oct 2017 20:22:15 -0500 Subject: [PATCH 08/10] Refactor pattern check --- netbox/utilities/forms.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index 09585a8ee..c65d56980 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -89,9 +89,10 @@ def parse_alphanumeric_range(string): try: begin, end = dash_range.split('-') # Skip if incompatible types or mixed case, just like any other bad pattern - if (str.isalpha(begin) and str.isdigit(end)) or (str.isdigit(begin) and str.isalpha(end)): + vals = begin + end + if not (vals.isdigit() or vals.isalpha()): continue - if not (str.isupper(begin + end) or str.islower(begin + end)): + if vals.isalpha() and not (vals.isupper() or vals.islower()): continue except ValueError: begin, end = dash_range, dash_range From e57b8aa26f8389f6107d017d4c8e5aea9e2663fa Mon Sep 17 00:00:00 2001 From: Ryan Breaker Date: Tue, 24 Oct 2017 20:43:02 -0500 Subject: [PATCH 09/10] E226 fix --- netbox/utilities/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index c65d56980..1a285707e 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -96,7 +96,7 @@ def parse_alphanumeric_range(string): continue except ValueError: begin, end = dash_range, dash_range - nums = list(range(ord(begin), ord(end)+1)) + nums = list(range(ord(begin), ord(end) + 1)) for n in nums: values.append(chr(n)) return values From 57973f62c5b31ac235141de0f9c18a5aeaf8ed1a Mon Sep 17 00:00:00 2001 From: Ryan Breaker Date: Tue, 31 Oct 2017 22:03:57 -0500 Subject: [PATCH 10/10] Fix bug with numbers >10 --- netbox/utilities/forms.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index 1a285707e..417e164fb 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -88,17 +88,18 @@ def parse_alphanumeric_range(string): for dash_range in string.split(','): try: begin, end = dash_range.split('-') - # Skip if incompatible types or mixed case, just like any other bad pattern vals = begin + end - if not (vals.isdigit() or vals.isalpha()): - continue - if vals.isalpha() and not (vals.isupper() or vals.islower()): - continue + # Break out of loop if there's an invalid pattern to return an error + if (not (vals.isdigit() or vals.isalpha())) or (vals.isalpha() and not (vals.isupper() or vals.islower())): + return [] except ValueError: begin, end = dash_range, dash_range - nums = list(range(ord(begin), ord(end) + 1)) - for n in nums: - values.append(chr(n)) + if begin.isdigit() and end.isdigit(): + for n in list(range(int(begin), int(end) + 1)): + values.append(n) + else: + for n in list(range(ord(begin), ord(end) + 1)): + values.append(chr(n)) return values