From 65a8daf8ffc73ad4928308fe6a22dfc060817d08 Mon Sep 17 00:00:00 2001 From: Eric Fried Date: Tue, 10 Apr 2018 12:55:56 -0500 Subject: [PATCH] normalize_name helper Names of traits in placement are restricted to certain characters and lengths. Consumers (e.g. Nova virt driver implementations of update_provider_tree) may wish to generate such names dynamically based on inputs that don't necessarily know or follow those rules. Such consumers could use a convenient way to sanitize generated names such that they conform to what placement expects. This change set introduces a normalize_name method into the base os_traits namespace. It is functionally identical to ResourceClass.normalize_name: it converts and squashes any sequences of non-alphanumeric characters to an underscore, upcases, and prepends CUSTOM_. Change-Id: I0d83cb39abb417b337a3429d374e88942561d254 Related-Blueprint: update-provider-tree --- os_traits/__init__.py | 20 ++++++++++++++++++++ os_traits/tests/test_os_traits.py | 17 +++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/os_traits/__init__.py b/os_traits/__init__.py index 55ca9a5..b03c0fb 100644 --- a/os_traits/__init__.py +++ b/os_traits/__init__.py @@ -14,6 +14,7 @@ import importlib import pkgutil +import re import sys import pbr.version @@ -107,3 +108,22 @@ def is_custom(trait): :param trait: String name of the trait """ return trait.startswith(CUSTOM_NAMESPACE) + + +def normalize_name(name): + """Converts an input string to a legal* custom trait name. + + Legal custom trait names are prefixed with CUSTOM_ and contain only the + characters A-Z, 0-9, and _ (underscore). + + *Does not attempt to handle length restrictions. + + :param name: A string to be converted. + :return: A legal* custom trait name. + """ + if name is None: + return None + # Replace non-alphanumeric characters with underscores + norm_name = re.sub('[^0-9A-Za-z]+', '_', name) + # Bug #1762789: Do .upper after replacing non alphanumerics. + return CUSTOM_NAMESPACE + norm_name.upper() diff --git a/os_traits/tests/test_os_traits.py b/os_traits/tests/test_os_traits.py index 668df55..e428cf8 100644 --- a/os_traits/tests/test_os_traits.py +++ b/os_traits/tests/test_os_traits.py @@ -96,3 +96,20 @@ class TestSymbols(base.TestCase): match = valid_name.match(t) if not match: self.fail("Trait %s does not validate name regex." % t) + + def test_normalize_name(self): + values = [ + ("foo", "CUSTOM_FOO"), + ("VCPU", "CUSTOM_VCPU"), + ("CUSTOM_BOB", "CUSTOM_CUSTOM_BOB"), + ("CUSTM_BOB", "CUSTOM_CUSTM_BOB"), + (u"Fu\xdfball", u"CUSTOM_FU_BALL"), + ("abc-123", "CUSTOM_ABC_123"), + ("Hello, world! This is a test ^_^", + "CUSTOM_HELLO_WORLD_THIS_IS_A_TEST_"), + (" leading and trailing spaces ", + "CUSTOM__LEADING_AND_TRAILING_SPACES_"), + ] + for test_value, expected in values: + result = ot.normalize_name(test_value) + self.assertEqual(expected, result)