From e8734c37e3e57d80b6c574a8ad0bc8e5554f2f92 Mon Sep 17 00:00:00 2001 From: Wen Liang Date: Wed, 17 Mar 2021 11:48:41 -0400 Subject: [PATCH] Support specifying mac address without quotation mark - In yaml, all the string value consists of a series of one-digit or two-digit numbers delimited by colons and all numbers but the first are between 0 and 59, it would be interpreted as a sexagesimal number, and would be converted automatically to the equivalent number of seconds. - The committed patch will guarantee to remediate the yaml's peculiarity and convert back into the mac address specified in the original yaml file. And in case that user specifies arbitrary integer(e.g. `mac:1234`), we introduce `force_len`(e.g. `MAC_ADDR_OCTETS = [6, 20]`) to raise error for invalid integer value. Signed-off-by: Wen Liang --- .../network_lsr/argument_validator.py | 19 +++++++++- module_utils/network_lsr/utils.py | 20 ++++++++++- tests/unit/test_network_connections.py | 35 +++++++++++++++++++ 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/module_utils/network_lsr/argument_validator.py b/module_utils/network_lsr/argument_validator.py index 92e07b8c..e05859d0 100644 --- a/module_utils/network_lsr/argument_validator.py +++ b/module_utils/network_lsr/argument_validator.py @@ -14,6 +14,9 @@ from ansible.module_utils.network_lsr.utils import Util # noqa:E501 UINT32_MAX = 0xFFFFFFFF +# a MAC address for type ethernet requires 6 octets +# a MAC address for type infiniband requires 20 octets +MAC_ADDR_OCTETS = [6, 20] class ArgUtil: @@ -416,11 +419,25 @@ def _validate_impl(self, value, name): class ArgValidatorMac(ArgValidatorStr): - def __init__(self, name, force_len=None, required=False, default_value=None): + def __init__( + self, name, force_len=MAC_ADDR_OCTETS, required=False, default_value=None + ): ArgValidatorStr.__init__(self, name, required, default_value, None) self.force_len = force_len def _validate_impl(self, value, name): + if type(value) == int: + try: + value = Util.num_to_mac(value, self.force_len) + except MyError: + raise ValidationError( + name, "value '%s' is not a valid MAC address" % (value) + ) + if not isinstance(value, Util.STRING_TYPE): + raise ValidationError( + name, + "must be a string and value should be quoted, but is '%s', " % (value), + ) v = ArgValidatorStr._validate_impl(self, value, name) try: addr = Util.mac_aton(v, self.force_len) diff --git a/module_utils/network_lsr/utils.py b/module_utils/network_lsr/utils.py index 7f672ce5..ab0778d7 100644 --- a/module_utils/network_lsr/utils.py +++ b/module_utils/network_lsr/utils.py @@ -255,7 +255,7 @@ def mac_aton(mac_str, force_len=None): if i == 1: raise MyError("not a valid MAC address: '%s'" % (mac_str)) if force_len is not None: - if force_len != len(b): + if not any(a for a in force_len if a == len(b)): raise MyError( "not a valid MAC address of length %s: '%s'" % (force_len, mac_str) ) @@ -272,6 +272,24 @@ def mac_ntoa(mac): def mac_norm(mac_str, force_len=None): return Util.mac_ntoa(Util.mac_aton(mac_str, force_len)) + @staticmethod + def num_to_mac(num, force_len=None): + mac = [] + s_num = num + while num: + num, mod = divmod(num, 60) + if mod < 10: + mac.insert(0, "0" + str(mod)) + else: + mac.insert(0, str(mod)) + mac_addr = ":".join(mac) + if force_len is not None: + if not any(a for a in force_len if a == len(mac)): + raise MyError( + "not a valid number '%d' for MAC address: '%s'" % (s_num, mac_addr) + ) + return mac_addr + @staticmethod def boolean(arg): if arg is None or isinstance(arg, bool): diff --git a/tests/unit/test_network_connections.py b/tests/unit/test_network_connections.py index a5002923..6a33055b 100644 --- a/tests/unit/test_network_connections.py +++ b/tests/unit/test_network_connections.py @@ -3361,6 +3361,41 @@ def test_set_deprecated_slave_type(self): self.assertTrue("port_type" in connection) self.assertTrue("slave_type" not in connection) + def test_mac_address_argvalidator(self): + """ + Test that argvalidator for validating mac address is correctly defined. + """ + validator = network_lsr.argument_validator.ArgValidatorMac("mac") + self.assertEqual(validator.validate(41135085296), "52:54:00:12:34:56") + self.assertEqual(validator.validate("00:00:5e:00:53:5d"), "00:00:5e:00:53:5d") + self.assertEqual(validator.validate("00:00:00:00:00:00"), "00:00:00:00:00:00") + self.assertEqual(validator.validate("ff:ff:ff:ff:ff:ff"), "ff:ff:ff:ff:ff:ff") + self.assertEqual( + validator.validate( + "80:00:00:6d:fe:80:00:00:00:00:00:00:00:02:55:00:70:33:cf:01" + ), + "80:00:00:6d:fe:80:00:00:00:00:00:00:00:02:55:00:70:33:cf:01", + ) + self.assertEqual( + validator.validate( + "01:02:03:04:05:06:07:08:09:0A:01:02:03:04:05:06:07:08:09:11" + ), + "01:02:03:04:05:06:07:08:09:0a:01:02:03:04:05:06:07:08:09:11", + ) + self.assertValidationError(validator, 1234) + self.assertValidationError(validator, "aa:bb") + self.assertValidationError(validator, sys.maxsize) + self.assertValidationError(validator, 0) + self.assertValidationError( + validator, "80:00:00:6d:fe:80:00:00:00:00:00:00:00:02:55:00:70:33:cf:" + ) + self.assertValidationError( + validator, "80:00:00:6d:fe:80:00:00:00:00:00:00:00:02:55:00:70:33:cf:xx" + ) + self.assertValidationError( + validator, "80:00:00:6d:fe:80:00:00:00:00:00:00:00:02:55:000:70:33:cf:01" + ) + @my_test_skipIf(nmutil is None, "no support for NM (libnm via pygobject)") class TestNM(unittest.TestCase):