Repository: cobbler/cobbler

Issue:
Cobbler rejects InfiniBand MAC addresses
### Describe the bug

InfiniBand uses 128-bit MAC addresses like 80:00:02:08:fe:80:00:00:00:00:00:00:f4:52:14:03:00:96:79:31

### Steps to reproduce

1. Try to configure an infiniband network interface with MAC address

### Expected behavior

No error.

### Cobbler version

<!--- Paste output from `cobbler version` -->
````paste below
3.2.2
````

### Operating system

EL9

### Cobbler log

<!--- Paste (partial) output from `/var/log/cobbler/cobbler.log` -->
````paste below
2024-03-12T14:12:29 - INFO | Exception occurred: <class 'cobbler.cexceptions.CX'>
2024-03-12T14:12:29 - INFO | Exception value: 'Invalid mac address format (80:00:02:08:fe:80:00:00:00:00:00:00:f4:52:14:03:00:96:79:31)'
2024-03-12T14:12:29 - INFO | Exception Info:
  File "/usr/lib/python3.9/site-packages/cobbler/remote.py", line 3453, in _dispatch
    return method_handle(*params)

  File "/usr/lib/python3.9/site-packages/cobbler/remote.py", line 1643, in modify_system
    return self.modify_item("system", object_id, attribute, arg, token)

  File "/usr/lib/python3.9/site-packages/cobbler/remote.py", line 1606, in modify_item
    method(arg)

  File "/usr/lib/python3.9/site-packages/cobbler/items/system.py", line 753, in modify_interface
    self.set_mac_address(value, interface)

  File "/usr/lib/python3.9/site-packages/cobbler/items/system.py", line 384, in set_mac_address
    address = validate.mac_address(address)

  File "/usr/lib/python3.9/site-packages/cobbler/validate.py", line 111, in mac_address
    raise CX("Invalid mac address format (%s)" % mac)
````


### Additional information
There is a request for support in netaddr, but it hasn't gone anywhere: https://github.com/netaddr/netaddr/issues/88


Discussion:

Resolving patch:
diff --git a/cobbler/validate.py b/cobbler/validate.py
--- a/cobbler/validate.py
+++ b/cobbler/validate.py
@@ -31,6 +31,9 @@
     r"^[a-zA-Z\d-]{,63}(\.[a-zA-Z\d-]{,63})*$"
 )  # https://stackoverflow.com/a/2894918
 RE_SCRIPT_NAME = re.compile(r"[a-zA-Z0-9_\-.]+")
+RE_INFINIBAND_MAC = re.compile(
+    "^" + ":".join(["([0-9A-F]{1,2})"] * 20) + "$", re.IGNORECASE
+)
 
 # blacklist invalid values to the repo statement in autoinsts
 AUTOINSTALL_REPO_BLACKLIST = ["enabled", "gpgcheck", "gpgkey"]
@@ -85,7 +88,7 @@ def mac_address(mac: str, for_item: bool = True) -> str:
         if mac == "":
             return mac
 
-    if not netaddr.valid_mac(mac):  # type: ignore
+    if not netaddr.valid_mac(mac) and RE_INFINIBAND_MAC.match(mac) is None:  # type: ignore
         raise ValueError(f"Invalid mac address format ({mac})")
 
     return mac


Test patch:
diff --git a/tests/items/network_interface_test.py b/tests/items/network_interface_test.py
--- a/tests/items/network_interface_test.py
+++ b/tests/items/network_interface_test.py
@@ -234,6 +234,11 @@ def test_dns_name(
         ("AA:BB", "", pytest.raises(ValueError)),
         (0, "", pytest.raises(TypeError)),
         ("random", "AA:BB:CC:DD:EE:FF", does_not_raise()),
+        (
+            "80:00:0a:43:fe:80:00:00:00:00:00:00:0e:fa:aa:bb:cc:dd:ee:ff",
+            "80:00:0a:43:fe:80:00:00:00:00:00:00:0e:fa:aa:bb:cc:dd:ee:ff",
+            does_not_raise(),
+        ),
         ("AA:AA:AA:AA:AA:AA", "", pytest.raises(ValueError)),
     ],
 )