← Back to team overview

sts-sponsors team mailing list archive

[Merge] ~ack/maas:drop-mac-wrapper into maas:master

 

Alberto Donato has proposed merging ~ack/maas:drop-mac-wrapper into maas:master.

Commit message:
drop MAC wrapper object and related logic



Requested reviews:
  MAAS Maintainers (maas-maintainers)

For more details, see:
https://code.launchpad.net/~ack/maas/+git/maas/+merge/436442
-- 
Your team MAAS Committers is subscribed to branch maas:master.
diff --git a/src/maasserver/api/machines.py b/src/maasserver/api/machines.py
index 3f9b55e..7ba6143 100644
--- a/src/maasserver/api/machines.py
+++ b/src/maasserver/api/machines.py
@@ -62,7 +62,7 @@ from maasserver.exceptions import (
     NodeStateViolation,
     Unauthorized,
 )
-from maasserver.fields import validate_mac
+from maasserver.fields import mac_validator
 from maasserver.forms import (
     AdminMachineForm,
     get_machine_create_form,
@@ -1915,7 +1915,7 @@ class AnonMachinesHandler(AnonNodesHandler):
             macs_valid = True
             for mac_address in mac_addresses:
                 try:
-                    validate_mac(mac_address)
+                    mac_validator(mac_address)
                 except ValidationError:
                     macs_valid = False
                     break
diff --git a/src/maasserver/api/networks.py b/src/maasserver/api/networks.py
index bbe0eb8..8635109 100644
--- a/src/maasserver/api/networks.py
+++ b/src/maasserver/api/networks.py
@@ -137,7 +137,7 @@ class NetworkHandler(OperationsHandler):
             unique_interfaces_by_mac,
             key=lambda x: (
                 x.node_config.node.hostname.lower(),
-                x.mac_address.get_raw(),
+                x.mac_address,
             ),
         )
         return [
diff --git a/src/maasserver/api/tests/test_enlistment.py b/src/maasserver/api/tests/test_enlistment.py
index fe5f429..9384922 100644
--- a/src/maasserver/api/tests/test_enlistment.py
+++ b/src/maasserver/api/tests/test_enlistment.py
@@ -489,7 +489,7 @@ class TestAnonymousEnlistmentAPI(APITestCase.ForAnonymous):
                     random.choice(macs),
                     # A MAC address unknown to MAAS shouldn't effect finding
                     # the machine.
-                    factory.make_MAC(),
+                    factory.make_mac_address(),
                 ],
             },
         )
diff --git a/src/maasserver/api/tests/test_events.py b/src/maasserver/api/tests/test_events.py
index 64fb3a3..dfe5acd 100644
--- a/src/maasserver/api/tests/test_events.py
+++ b/src/maasserver/api/tests/test_events.py
@@ -815,7 +815,7 @@ class TestEventsURIs(APITestCase.ForUser):
             "domain": machine.domain.name,
             "hostname": machine.hostname,
             "id": machine.system_id,
-            "mac_address": interface.mac_address.raw,
+            "mac_address": interface.mac_address,
             "zone": machine.zone.name,
         }
         event = factory.make_Event(node=machine)
diff --git a/src/maasserver/api/tests/test_machine.py b/src/maasserver/api/tests/test_machine.py
index a2bd82c..0477be6 100644
--- a/src/maasserver/api/tests/test_machine.py
+++ b/src/maasserver/api/tests/test_machine.py
@@ -247,7 +247,7 @@ class TestMachineAPI(APITestCase.ForUser):
         self.assertEqual(http.client.OK, response.status_code)
         parsed_result = json_load_bytes(response.content)
         self.assertEqual(
-            machine.boot_interface.mac_address.get_raw(),
+            machine.boot_interface.mac_address,
             parsed_result["boot_interface"]["mac_address"],
         )
 
diff --git a/src/maasserver/api/tests/test_network.py b/src/maasserver/api/tests/test_network.py
index d81c4c2..843e360 100644
--- a/src/maasserver/api/tests/test_network.py
+++ b/src/maasserver/api/tests/test_network.py
@@ -202,10 +202,10 @@ class TestListConnectedMACs(APITestCase.ForUser):
             interfaces,
             key=lambda x: (
                 x.node_config.node.hostname.lower(),
-                x.mac_address.get_raw(),
+                x.mac_address,
             ),
         )
         self.assertEqual(
-            [nic.mac_address.get_raw() for nic in sorted_interfaces],
+            [nic.mac_address for nic in sorted_interfaces],
             self.extract_macs(self.request_connected_macs(subnet)),
         )
diff --git a/src/maasserver/djangosettings/settings.py b/src/maasserver/djangosettings/settings.py
index 9cf2d4d..1e7ccea 100644
--- a/src/maasserver/djangosettings/settings.py
+++ b/src/maasserver/djangosettings/settings.py
@@ -334,10 +334,6 @@ ALLOWED_HOSTS = ["*"]
 # Consider the request secure if the header is set
 SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
 
-# Extend Django's JSON serialization.  Without this, JSON serialization of
-# MAC addresses in model fields will break.
-SERIALIZATION_MODULES = {"maasjson": "maasserver.json"}
-
 # MAAS has no upload limit to allow for big image files.
 # (Django 1.10 introduced this limit with a default of 2.5MB.)
 DATA_UPLOAD_MAX_MEMORY_SIZE = None
diff --git a/src/maasserver/fields.py b/src/maasserver/fields.py
index 1034765..94d64cf 100644
--- a/src/maasserver/fields.py
+++ b/src/maasserver/fields.py
@@ -10,15 +10,12 @@ __all__ = [
     "HostListFormField",
     "IPListFormField",
     "IPv4CIDRField",
-    "MAC",
     "MACAddressField",
     "MACAddressFormField",
     "MODEL_NAME_VALIDATOR",
     "NodeChoiceField",
-    "register_mac_type",
     "VerboseRegexValidator",
     "VersionedTextFileField",
-    "validate_mac",
 ]
 
 import re
@@ -39,7 +36,6 @@ from django.db.models import (
 from django.utils.deconstruct import deconstructible
 from django.utils.encoding import force_str
 from netaddr import AddrFormatError, IPAddress, IPNetwork
-import psycopg2.extensions
 
 from maasserver.models.versionedtextfile import VersionedTextFile
 from maasserver.utils.converters import parse_systemd_interval
@@ -84,13 +80,6 @@ class VerboseRegexValidator(RegexValidator):
 mac_validator = VerboseRegexValidator(regex=MAC_RE, message=MAC_ERROR_MSG)
 
 
-def validate_mac(value):
-    """Django validator for a MAC."""
-    if isinstance(value, MAC):
-        value = value.get_raw()
-    mac_validator(value)
-
-
 class StrippedCharField(forms.CharField):
     """A CharField that will strip surrounding whitespace before validation."""
 
@@ -144,145 +133,14 @@ class MACAddressField(Field):
 
     description = "MAC address"
 
-    default_validators = [validate_mac]
+    default_validators = [mac_validator]
 
     def db_type(self, *args, **kwargs):
         return "macaddr"
 
-    def to_python(self, value):
-        return MAC(value)
-
-    def from_db_value(self, value, expression, connection):
-        return MAC(value)
-
     def get_prep_value(self, value):
-        value = super().get_prep_value(value)
         # Convert empty string to None.
-        if not value:
-            return None
-        return value
-
-
-class MAC:
-    """A MAC address represented as a database value.
-
-    PostgreSQL supports MAC addresses as a native type. They show up
-    client-side as this class. It is essentially a wrapper for a string.
-
-    This NEVER represents a null or empty MAC address.
-    """
-
-    def __new__(cls, value):
-        """Return `None` if `value` is `None` or the empty string."""
-        if value is None:
-            return None
-        elif isinstance(value, (bytes, str)):
-            return None if len(value) == 0 else super().__new__(cls)
-        else:
-            return super().__new__(cls)
-
-    def __init__(self, value):
-        """Wrap a MAC address, or None, into a `MAC`.
-
-        :param value: A MAC address, in the form of a string or a `MAC`;
-            or None.
-        """
-        # The wrapped attribute is stored as self._wrapped, following
-        # ISQLQuote's example.
-        if isinstance(value, MAC):
-            self._wrapped = value._wrapped
-        elif isinstance(value, bytes):
-            self._wrapped = value.decode("ascii")
-        elif isinstance(value, str):
-            self._wrapped = value
-        else:
-            raise TypeError(f"expected MAC or string, got: {value!r}")
-
-    def __conform__(self, protocol):
-        """Tell psycopg2 that this type implements the adapter protocol."""
-        # The psychopg2 docs say to check that the protocol is ISQLQuote,
-        # but not what to do if it isn't.
-        assert protocol == psycopg2.extensions.ISQLQuote, (
-            "Unsupported psycopg2 adapter protocol: %s" % protocol
-        )
-        return self
-
-    def getquoted(self):
-        """Render this object in SQL.
-
-        This is part of psycopg2's adapter protocol.
-        """
-        return "'%s'::macaddr" % self._wrapped
-
-    def get_raw(self):
-        """Return the wrapped value."""
-        return self._wrapped
-
-    @property
-    def raw(self):
-        """The MAC address as a string."""
-        return self._wrapped
-
-    @classmethod
-    def parse(cls, value, cur):
-        """Turn a value as received from the database into a MAC."""
-        return cls(value)
-
-    def __repr__(self):
-        """Represent the MAC as a string."""
-        return "<MAC %s>" % self._wrapped
-
-    def __str__(self):
-        """Represent the MAC as a Unicode string."""
-        return self._wrapped
-
-    def __bytes__(self):
-        return self._wrapped.encode("ascii")
-
-    def __len__(self):
-        """Defer to len of the wrapped value."""
-        return len(self._wrapped)
-
-    def __eq__(self, other):
-        """Two `MAC`s are equal if they wrap the same value.
-
-        A MAC is is also equal to the value it wraps. This is non-commutative,
-        but it supports Django code that compares input values to various
-        kinds of "null" or "empty."
-        """
-        if isinstance(other, MAC):
-            return self._wrapped == other._wrapped
-        else:
-            return self._wrapped == other
-
-    def __ne__(self, other):
-        return not (self == other)
-
-    def __hash__(self):
-        return hash(self._wrapped)
-
-
-def register_mac_type(cursor):
-    """Register our `MAC` type with psycopg2 and Django."""
-
-    # This is standard, but not built-in, magic to register a type in
-    # psycopg2: execute a query that returns a field of the corresponding
-    # database type, then get its oid out of the cursor, use that to create
-    # a "typecaster" in psycopg (by calling new_type(), confusingly!), then
-    # register that type in psycopg.
-    cursor.execute("SELECT NULL::macaddr")
-    oid = cursor.description[0][1]
-    mac_caster = psycopg2.extensions.new_type((oid,), "macaddr", MAC.parse)
-    psycopg2.extensions.register_type(mac_caster)
-
-    # Now do the same for the type array-of-MACs.  The "typecaster" created
-    # for MAC is passed in; it gets used for parsing an individual element
-    # of an array's text representation as received from the database.
-    cursor.execute("SELECT '{}'::macaddr[]")
-    oid = cursor.description[0][1]
-    psycopg2.extensions.register_type(
-        psycopg2.extensions.new_array_type((oid,), "macaddr", mac_caster)
-    )
+        return super().get_prep_value(value) or None
 
 
 class XMLField(Field):
diff --git a/src/maasserver/forms/interface.py b/src/maasserver/forms/interface.py
index 6967f8e..46b6229 100644
--- a/src/maasserver/forms/interface.py
+++ b/src/maasserver/forms/interface.py
@@ -461,7 +461,7 @@ class ChildInterfaceForm(InterfaceForm):
         """
         if self.instance.id is not None:
             parent_macs = {
-                parent.mac_address.get_raw(): parent
+                parent.mac_address: parent
                 for parent in self.instance.parents.all()
             }
         else:
diff --git a/src/maasserver/json.py b/src/maasserver/json.py
deleted file mode 100644
index a560b37..0000000
--- a/src/maasserver/json.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright 2013-2016 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Extension of Django's JSON serializer to support MAAS custom data types.
-
-We register this as a replacement for Django's own JSON serialization by
-setting it in the SERIALIZATION_MODULES setting.
-"""
-
-
-import json
-
-import django.core.serializers.json
-
-from maasserver.fields import MAC
-
-
-class MAASJSONEncoder(django.core.serializers.json.DjangoJSONEncoder):
-    """MAAS-specific JSON encoder.
-
-    Compared to Django's encoder, it adds support for representing a
-    `MAC` in JSON.
-    """
-
-    def default(self, value):
-        if isinstance(value, MAC):
-            return value.get_raw()
-        else:
-            return super().default(value)
-
-
-class Serializer(django.core.serializers.json.Serializer):
-    """A copy of Django's serializer for JSON, but using our own encoder."""
-
-    def end_serialization(self):
-        json.dump(
-            self.objects, self.stream, cls=MAASJSONEncoder, **self.options
-        )
-
-
-# Keep using Django's deserializer.  Loading a MAC from JSON will produce a
-# string.
-Deserializer = django.core.serializers.json.Deserializer
diff --git a/src/maasserver/models/interface.py b/src/maasserver/models/interface.py
index 6c8368b..5b765e4 100644
--- a/src/maasserver/models/interface.py
+++ b/src/maasserver/models/interface.py
@@ -49,8 +49,8 @@ from maasserver.exceptions import (
     StaticIPAddressUnavailable,
 )
 from maasserver.fields import (
+    mac_validator,
     MACAddressField,
-    validate_mac,
     VerboseRegexValidator,
 )
 from maasserver.models.cleansave import CleanSave
@@ -979,7 +979,7 @@ class Interface(CleanSave, TimestampedModel):
         network = IPNetwork(net_cidr)
         if network.prefixlen != 64:
             return None
-        return EUI(self.mac_address.raw).ipv6(network.first)
+        return EUI(self.mac_address).ipv6(network.first)
 
     def remove_link_dhcp(self, subnet_family=None):
         """Removes the DHCP links if they have no subnet or if the linked
@@ -1490,7 +1490,7 @@ class Interface(CleanSave, TimestampedModel):
 
         # Verify that the MAC address is legal if it is not empty.
         if self.mac_address:
-            validate_mac(self.mac_address)
+            mac_validator(self.mac_address)
 
     def delete(self, remove_ip_address=True):
         # We set the _skip_ip_address_removal so the signal can use it to
diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py
index e9766d6..00202dd 100644
--- a/src/maasserver/models/node.py
+++ b/src/maasserver/models/node.py
@@ -112,7 +112,6 @@ from maasserver.exceptions import (
     StaticIPAddressExhaustion,
     StorageClearProblem,
 )
-from maasserver.fields import MAC
 from maasserver.models.blockdevice import BlockDevice
 from maasserver.models.bootresource import BootResource
 from maasserver.models.cacheset import CacheSet
@@ -2150,11 +2149,10 @@ class Node(CleanSave, TimestampedModel):
 
         if name is None:
             name = self.get_next_ifname()
-        mac = MAC(mac_address)
-        UnknownInterface.objects.filter(mac_address=mac).delete()
+        UnknownInterface.objects.filter(mac_address=mac_address).delete()
         numa_node = self.default_numanode if self.is_machine else None
         iface, created = PhysicalInterface.objects.get_or_create(
-            mac_address=mac,
+            mac_address=mac_address,
             defaults={
                 "node_config": self.current_config,
                 "name": name,
@@ -3159,8 +3157,7 @@ class Node(CleanSave, TimestampedModel):
                 boot_interface is not None
                 and boot_interface.mac_address is not None
             ):
-                mac = boot_interface.mac_address.get_raw()
-                power_params["mac_address"] = mac
+                power_params["mac_address"] = boot_interface.mac_address
 
         # boot_mode is something that tells the template whether this is
         # a PXE boot or a local HD boot.
diff --git a/src/maasserver/models/tests/test_interface.py b/src/maasserver/models/tests/test_interface.py
index b9c2e9b..82c70ce 100644
--- a/src/maasserver/models/tests/test_interface.py
+++ b/src/maasserver/models/tests/test_interface.py
@@ -1008,7 +1008,7 @@ class TestInterface(MAASServerTestCase):
         # twice: once as part of Interface.clean() resulting in the __all__
         # error, and once as part of field validation that happens after a
         # few queries are done, so we cannot easily get rid of
-        # validate_mac() in clean().
+        # mac_validator() in clean().
         self.assertThat(
             exception.message_dict,
             MatchesDict(
@@ -1035,7 +1035,7 @@ class TestInterface(MAASServerTestCase):
     def test_creates_interface(self):
         name = factory.make_name("name")
         node_config = factory.make_NodeConfig()
-        mac = factory.make_MAC()
+        mac = factory.make_mac_address()
         interface = factory.make_Interface(
             INTERFACE_TYPE.PHYSICAL,
             name=name,
@@ -1055,7 +1055,7 @@ class TestInterface(MAASServerTestCase):
     def test_allows_null_vlan(self):
         name = factory.make_name("name")
         node_config = factory.make_NodeConfig()
-        mac = factory.make_MAC()
+        mac = factory.make_mac_address()
         interface = factory.make_Interface(
             INTERFACE_TYPE.PHYSICAL,
             name=name,
@@ -1077,11 +1077,11 @@ class TestInterface(MAASServerTestCase):
     def test_string_representation_contains_essential_data(self):
         name = factory.make_name("name")
         node = factory.make_Node()
-        mac = factory.make_MAC()
+        mac = factory.make_mac_address()
         interface = factory.make_Interface(
             INTERFACE_TYPE.PHYSICAL, name=name, node=node, mac_address=mac
         )
-        self.assertIn(mac.get_raw(), str(interface))
+        self.assertIn(mac, str(interface))
         self.assertIn(name, str(interface))
 
     def test_deletes_related_children(self):
@@ -1802,7 +1802,7 @@ class TestPhysicalInterfaceTransactional(MAASTransactionServerTestCase):
             with transaction.atomic():
                 _create_physical(mac)
 
-        mac = factory.make_MAC()
+        mac = factory.make_mac_address()
         t = threading.Thread(target=create_physical, args=(mac,))
 
         with transaction.atomic():
@@ -2482,7 +2482,7 @@ class TestUpdateIpAddresses(MAASServerTestCase):
 
     def test_does_not_add_eui_64_address(self):
         # See also LP#1639090.
-        mac_address = factory.make_MAC()
+        mac_address = factory.make_mac_address()
         iface = factory.make_Interface(
             INTERFACE_TYPE.PHYSICAL, mac_address=mac_address
         )
@@ -2494,7 +2494,7 @@ class TestUpdateIpAddresses(MAASServerTestCase):
 
     def test_does_not_add_addresses_from_duplicate_subnet(self):
         # See also LP#1803188.
-        mac_address = factory.make_MAC()
+        mac_address = factory.make_mac_address()
         vlan = factory.make_VLAN()
         factory.make_Subnet(cidr="10.0.0.0/8", vlan=vlan)
         factory.make_Subnet(cidr="2001::/64", vlan=vlan)
diff --git a/src/maasserver/models/tests/test_node.py b/src/maasserver/models/tests/test_node.py
index 4cf6669..25fc5e1 100644
--- a/src/maasserver/models/tests/test_node.py
+++ b/src/maasserver/models/tests/test_node.py
@@ -771,8 +771,7 @@ class TestRegionControllerManagerGetOrCreateRunningController(
 
     def set_mac_address_to_match(self, node):
         raw_macs = [
-            nic.mac_address.raw
-            for nic in node.current_config.interface_set.all()
+            nic.mac_address for nic in node.current_config.interface_set.all()
         ]
         node_module.get_mac_addresses.return_value = [random.choice(raw_macs)]
 
diff --git a/src/maasserver/rpc/tests/test_nodes.py b/src/maasserver/rpc/tests/test_nodes.py
index dee97b7..80c49a3 100644
--- a/src/maasserver/rpc/tests/test_nodes.py
+++ b/src/maasserver/rpc/tests/test_nodes.py
@@ -339,7 +339,7 @@ class TestRequestNodeInfoByMACAddress(MAASServerTestCase):
     def test_request_node_info_by_mac_address_returns_node_for_mac(self):
         interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
         node, boot_purpose = request_node_info_by_mac_address(
-            interface.mac_address.get_raw()
+            interface.mac_address
         )
         self.assertEqual(node, interface.node_config.node)
 
diff --git a/src/maasserver/rpc/tests/test_rackcontrollers.py b/src/maasserver/rpc/tests/test_rackcontrollers.py
index 8e9c1a7..88b0591 100644
--- a/src/maasserver/rpc/tests/test_rackcontrollers.py
+++ b/src/maasserver/rpc/tests/test_rackcontrollers.py
@@ -112,11 +112,10 @@ class TestRegisterRackController(MAASServerTestCase):
     def test_finds_existing_node_by_mac(self):
         node = factory.make_Node()
         nic = factory.make_Interface(node=node)
-        mac = nic.mac_address.raw
         interfaces = {
             nic.name: {
                 "type": "physical",
-                "mac_address": mac,
+                "mac_address": nic.mac_address,
                 "parents": [],
                 "links": [],
                 "enabled": True,
diff --git a/src/maasserver/rpc/tests/test_regionservice_calls.py b/src/maasserver/rpc/tests/test_regionservice_calls.py
index ddda99c..dbc756a 100644
--- a/src/maasserver/rpc/tests/test_regionservice_calls.py
+++ b/src/maasserver/rpc/tests/test_regionservice_calls.py
@@ -958,7 +958,7 @@ class TestRegionProtocol_SendEventMACAddress(MAASTransactionServerTestCase):
                 Region(),
                 SendEventMACAddress,
                 {
-                    "mac_address": mac_address.get_raw(),
+                    "mac_address": mac_address,
                     "type_name": name,
                     "description": event_description,
                 },
@@ -985,7 +985,7 @@ class TestRegionProtocol_SendEventMACAddress(MAASTransactionServerTestCase):
         event_type = factory.make_name("type_name")
         yield deferToDatabase(self.create_event_type, event_type, "", 0)
         interface = yield deferToDatabase(self.make_interface)
-        mac_address = interface.mac_address.get_raw()
+        mac_address = interface.mac_address
 
         yield eventloop.start()
         try:
@@ -1289,7 +1289,7 @@ class TestRegionProtocol_RequestNodeInforByMACAddress(
         node_info_function.return_value = (node, purpose)
         interface = yield deferToDatabase(self.make_interface, node)
 
-        params = {"mac_address": interface.mac_address.get_raw()}
+        params = {"mac_address": interface.mac_address}
 
         response = yield call_responder(
             Region(), RequestNodeInfoByMACAddress, params
diff --git a/src/maasserver/start_up.py b/src/maasserver/start_up.py
index c572b1f..824eca2 100644
--- a/src/maasserver/start_up.py
+++ b/src/maasserver/start_up.py
@@ -6,7 +6,6 @@
 
 import logging
 
-from django.db import connection
 from django.db.utils import DatabaseError
 from twisted.internet.defer import inlineCallbacks
 
@@ -16,7 +15,6 @@ from maasserver.deprecations import (
     log_deprecations,
     sync_deprecation_notifications,
 )
-from maasserver.fields import register_mac_type
 from maasserver.models import (
     Config,
     ControllerInfo,
@@ -169,9 +167,6 @@ def start_up(master=False):
 @transactional
 def inner_start_up(master=False):
     """Startup jobs that must run serialized w.r.t. other starting servers."""
-    # Register our MAC data type with psycopg.
-    register_mac_type(connection.cursor())
-
     # All commissioning and testing scripts are stored in the database. For
     # a commissioning ScriptSet to be created Scripts must exist first. Call
     # this early, only on the master process, to ensure they exist and are
diff --git a/src/maasserver/testing/factory.py b/src/maasserver/testing/factory.py
index 65414ca..521f706 100644
--- a/src/maasserver/testing/factory.py
+++ b/src/maasserver/testing/factory.py
@@ -42,7 +42,7 @@ from maasserver.enum import (
     POWER_STATE,
     RDNS_MODE,
 )
-from maasserver.fields import LargeObjectFile, MAC
+from maasserver.fields import LargeObjectFile
 from maasserver.models import (
     BlockDevice,
     BootResource,
@@ -1058,10 +1058,6 @@ class Factory(maastesting.factory.Factory):
             **kwargs,
         )
 
-    def make_MAC(self):
-        """Generate a random MAC address, in the form of a MAC object."""
-        return MAC(self.make_mac_address())
-
     def make_Node_with_Interface_on_Subnet(
         self,
         interface_count=1,
@@ -1783,7 +1779,7 @@ class Factory(maastesting.factory.Factory):
             INTERFACE_TYPE.BRIDGE,
             INTERFACE_TYPE.UNKNOWN,
         ]:
-            mac_address = self.make_MAC()
+            mac_address = self.make_mac_address()
         if tags is None:
             tags = [self.make_name("tag") for _ in range(3)]
         link_speeds = [10, 100, 1000, 10000, 20000, 40000, 50000, 100000]
diff --git a/src/maasserver/testing/testcase.py b/src/maasserver/testing/testcase.py
index 4ea0dfb..143a288 100644
--- a/src/maasserver/testing/testcase.py
+++ b/src/maasserver/testing/testcase.py
@@ -25,7 +25,6 @@ from django.db import (
 )
 from django.db.utils import IntegrityError, OperationalError
 
-from maasserver.fields import register_mac_type
 from maasserver.models import signals
 from maasserver.testing.fixtures import (
     IntroCompletedFixture,
@@ -78,11 +77,6 @@ class MAASRegionTestCaseBase(PostCommitHooksTestMixin):
         """Set the current client."""
         self.__client = client
 
-    @classmethod
-    def setUpClass(cls):
-        super().setUpClass()
-        register_mac_type(connection.cursor())
-
     def setUp(self):
         reset_queries()  # Formerly this was handled by... Django?
         super().setUp()
diff --git a/src/maasserver/tests/test_fields.py b/src/maasserver/tests/test_fields.py
index 0769d8d..dee3717 100644
--- a/src/maasserver/tests/test_fields.py
+++ b/src/maasserver/tests/test_fields.py
@@ -1,16 +1,13 @@
 # Copyright 2012-2017 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
-import json
 from random import choice, randint
 import re
 
-from django.core import serializers
 from django.core.exceptions import ValidationError
 from django.db import connection, DatabaseError
 from django.db.models import BinaryField
 from psycopg2 import OperationalError
-from psycopg2.extensions import ISQLQuote
 from testtools import ExpectedException
 
 from maasserver.enum import INTERFACE_TYPE
@@ -20,15 +17,12 @@ from maasserver.fields import (
     IPListFormField,
     LargeObjectField,
     LargeObjectFile,
-    MAC,
     MODEL_NAME_VALIDATOR,
     NodeChoiceField,
-    register_mac_type,
     SubnetListFormField,
     SystemdIntervalField,
     URLOrPPAFormField,
     URLOrPPAValidator,
-    validate_mac,
     VerboseRegexField,
     VerboseRegexValidator,
     VersionedTextFileField,
@@ -50,134 +44,6 @@ from maastesting.matchers import MockCalledOnceWith
 from maastesting.testcase import MAASTestCase
 
 
-class TestMAC(MAASServerTestCase):
-    def test_conform_accepts_ISQLQuote(self):
-        mac = MAC(factory.make_mac_address())
-        self.assertEqual(mac, mac.__conform__(ISQLQuote))
-
-    def test_new_MAC_with_None_is_None(self):
-        self.assertIsNone(MAC(None))
-
-    def test_new_MAC_with_empty_unicode_string_is_None(self):
-        self.assertIsNone(MAC(""))
-
-    def test_new_MAC_with_empty_byte_string_is_None(self):
-        self.assertIsNone(MAC(b""))
-
-    def test_new_MAC_with_other_value_types_are_rejected(self):
-        self.assertRaises(TypeError, MAC, 1234)
-        self.assertRaises(TypeError, MAC, object())
-        self.assertRaises(TypeError, MAC, self)
-
-    def test_as_representation(self):
-        addr = factory.make_mac_address()
-        mac = MAC(addr)
-        self.assertEqual("<MAC " + addr + ">", repr(mac))
-
-    def test_as_unicode_string(self):
-        addr = factory.make_mac_address()
-        mac = MAC(addr)
-        self.assertEqual(addr, str(mac))
-
-    def test_as_byte_string(self):
-        addr = factory.make_mac_address()
-        mac = MAC(addr)
-        self.assertEqual(addr.encode("ascii"), bytes(mac))
-
-    def test_get_raw_returns_wrapped_address(self):
-        addr = factory.make_mac_address()
-        self.assertEqual(addr, MAC(addr).get_raw())
-
-    def test_get_raw_punches_through_double_wrapping(self):
-        addr = factory.make_mac_address()
-        self.assertEqual(addr, MAC(MAC(addr)).get_raw())
-
-    def test_raw_property_is_the_address(self):
-        addr = factory.make_mac_address()
-        self.assertEqual(addr, MAC(addr).raw)
-
-    def test_getquoted_returns_SQL_for_MAC(self):
-        addr = factory.make_mac_address()
-        self.assertEqual("'%s'::macaddr" % addr, MAC(addr).getquoted())
-
-    def test_getquoted_punches_through_double_wrapping(self):
-        addr = factory.make_mac_address()
-        self.assertEqual("'%s'::macaddr" % addr, MAC(MAC(addr)).getquoted())
-
-    def test_mac_is_len_able(self):
-        mac = factory.make_MAC()
-        self.assertEqual(len(mac), len(mac.raw))
-
-    def test_mac_equals_self(self):
-        mac = factory.make_MAC()
-        self.assertTrue(mac == mac)
-
-    def test_mac_equals_identical_mac(self):
-        addr = factory.make_mac_address()
-        self.assertTrue(MAC(addr) == MAC(addr))
-
-    def test_eq_punches_through_double_wrapping_on_self(self):
-        mac = factory.make_MAC()
-        self.assertTrue(MAC(mac) == mac)
-
-    def test_eq_punches_through_double_wrapping_on_other(self):
-        mac = factory.make_MAC()
-        self.assertTrue(mac == MAC(mac))
-
-    def test_eq_punches_through_double_double_wrappings(self):
-        mac = factory.make_MAC()
-        self.assertTrue(MAC(mac) == MAC(mac))
-
-    def test_mac_does_not_equal_other(self):
-        self.assertFalse(factory.make_MAC() == factory.make_MAC())
-
-    def test_mac_differs_from_other(self):
-        self.assertTrue(factory.make_MAC() != factory.make_MAC())
-
-    def test_mac_does_not_differ_from_self(self):
-        mac = factory.make_MAC()
-        self.assertFalse(mac != mac)
-
-    def test_mac_address_does_not_equal_none(self):
-        self.assertIsNotNone(factory.make_MAC())
-
-    def test_ne_punches_through_double_wrapping_on_self(self):
-        mac = factory.make_MAC()
-        self.assertFalse(MAC(mac) != mac)
-
-    def test_ne_punches_through_double_wrapping_on_other(self):
-        mac = factory.make_MAC()
-        self.assertFalse(mac != MAC(mac))
-
-    def test_ne_punches_through_double_double_wrapping(self):
-        mac = factory.make_MAC()
-        self.assertFalse(MAC(mac) != MAC(mac))
-
-    def test_different_macs_hash_differently(self):
-        mac1 = factory.make_MAC()
-        mac2 = factory.make_MAC()
-        self.assertCountEqual({mac1, mac2}, [mac1, mac2])
-
-    def test_identical_macs_hash_identically(self):
-        addr = factory.make_mac_address()
-        self.assertCountEqual(
-            {MAC(addr), MAC(addr), MAC(MAC(addr)), addr}, [addr]
-        )
-
-    def test_django_serializes_MAC_to_JSON(self):
-        interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL)
-        query = Interface.objects.filter(id=interface.id)
-        output = serializers.serialize("json", query)
-        self.assertIn(json.dumps(interface.mac_address.get_raw()), output)
-        self.assertIn('"%s"' % interface.mac_address.get_raw(), output)
-
-    def test_register_mac_type_is_idempotent(self):
-        register_mac_type(connection.cursor())
-        register_mac_type(connection.cursor())
-        # The test is that we get here without crashing.
-        pass
-
-
 class TestModelNameValidator(MAASServerTestCase):
     def test_valid_name(self):
         self.assertIsNone(MODEL_NAME_VALIDATOR("a-valid-name"))
@@ -235,49 +101,6 @@ class TestMACAddressField(MAASServerTestCase):
         loaded_mac = Interface.objects.get(id=interface.id)
         self.assertEqual("aa:bb:cc:dd:ee:ff", loaded_mac.mac_address)
 
-    def test_accepts_colon_separated_octets(self):
-        validate_mac("00:aa:22:cc:44:dd")
-        # No error.
-        pass
-
-    def test_accepts_dash_separated_octets(self):
-        validate_mac("00-aa-22-cc-44-dd")
-        # No error.
-        pass
-
-    def test_accepts_upper_and_lower_case(self):
-        validate_mac("AA:BB:CC:dd:ee:ff")
-        # No error.
-        pass
-
-    def test_accepts_cisco_format(self):
-        validate_mac("0000.0200.3fff")
-        # No error.
-        pass
-
-    def test_accepts_leading_and_trailing_whitespace(self):
-        validate_mac(" AA:BB:CC:DD:EE:FF ")
-        # No error.
-        pass
-
-    def test_rejects_short_mac(self):
-        self.assertRaises(ValidationError, validate_mac, "00:11:22:33:44")
-
-    def test_rejects_long_mac(self):
-        self.assertRaises(
-            ValidationError, validate_mac, "00:11:22:33:44:55:66"
-        )
-
-    def test_accepts_short_octet(self):
-        # Hit both parts of the regex.
-        validate_mac("00:1:22:33:44:55")
-        validate_mac("00:11:22:33:44:5")
-        # No error.
-        pass
-
-    def test_rejects_long_octet(self):
-        self.assertRaises(ValidationError, validate_mac, "00:11:222:33:44:55")
-
 
 class TestXMLField(MAASLegacyServerTestCase):
 
diff --git a/src/maasserver/tests/test_preseed_network.py b/src/maasserver/tests/test_preseed_network.py
index 91ffd76..a1d5a72 100644
--- a/src/maasserver/tests/test_preseed_network.py
+++ b/src/maasserver/tests/test_preseed_network.py
@@ -503,7 +503,7 @@ class TestDHCPNetworkLayout(MAASServerTestCase, AssertNetworkConfigMixin):
                 "config": [
                     {
                         "id": iface.name,
-                        "mac_address": iface.mac_address.raw,
+                        "mac_address": iface.mac_address,
                         "mtu": iface.get_effective_mtu(),
                         "name": iface.name,
                         "type": "physical",
diff --git a/src/maasserver/tests/test_start_up.py b/src/maasserver/tests/test_start_up.py
index 13e806f..95f85e0 100644
--- a/src/maasserver/tests/test_start_up.py
+++ b/src/maasserver/tests/test_start_up.py
@@ -77,8 +77,8 @@ class TestStartUp(MAASTransactionServerTestCase):
         def check_lock(_):
             raise locked if locks.startup.is_locked() else unlocked
 
-        self.patch(start_up, "register_mac_type").side_effect = check_lock
-        self.assertRaises(type(locked), start_up.inner_start_up, master=False)
+        self.patch(start_up, "load_builtin_scripts").side_effect = check_lock
+        self.assertRaises(type(locked), start_up.inner_start_up, master=True)
 
     def test_start_up_retries_with_wait_on_exception(self):
         inner_start_up = self.patch(start_up, "inner_start_up")
diff --git a/src/maasserver/triggers/tests/test_websocket_listener.py b/src/maasserver/triggers/tests/test_websocket_listener.py
index 1506d30..277fce2 100644
--- a/src/maasserver/triggers/tests/test_websocket_listener.py
+++ b/src/maasserver/triggers/tests/test_websocket_listener.py
@@ -1847,7 +1847,7 @@ class TestNodeInterfaceListener(
             yield deferToDatabase(
                 self.update_interface,
                 interface.id,
-                {"mac_address": factory.make_MAC()},
+                {"mac_address": factory.make_mac_address()},
             )
             yield dv.get(timeout=2)
             self.assertEqual(("update", node.system_id), dv.value)
@@ -1949,7 +1949,7 @@ class TestDeviceWithParentInterfaceListener(
             yield deferToDatabase(
                 self.update_interface,
                 interface.id,
-                {"mac_address": factory.make_MAC()},
+                {"mac_address": factory.make_mac_address()},
             )
             yield dv.get(timeout=2)
             self.assertEqual(("update", parent.system_id), dv.value)
diff --git a/src/maasserver/websockets/handlers/device.py b/src/maasserver/websockets/handlers/device.py
index 4d98b8c..4d3a696 100644
--- a/src/maasserver/websockets/handlers/device.py
+++ b/src/maasserver/websockets/handlers/device.py
@@ -35,7 +35,7 @@ def get_Interface_from_list(interfaces, mac):
     mac = EUI(mac)
     for interface in interfaces:
         ifmac = interface.mac_address
-        if ifmac is not None and EUI(ifmac.raw) == mac:
+        if ifmac is not None and EUI(ifmac) == mac:
             return interface
     else:
         return None
diff --git a/src/maasserver/websockets/handlers/tests/test_device.py b/src/maasserver/websockets/handlers/tests/test_device.py
index eb595d7..c792209 100644
--- a/src/maasserver/websockets/handlers/tests/test_device.py
+++ b/src/maasserver/websockets/handlers/tests/test_device.py
@@ -18,7 +18,6 @@ from maasserver.enum import (
     NODE_TYPE,
 )
 from maasserver.exceptions import NodeActionError
-from maasserver.fields import MAC
 from maasserver.forms import DeviceForm, DeviceWithMACsForm
 from maasserver.models import Interface, StaticIPAddress
 from maasserver.node_action import compile_node_actions
@@ -577,7 +576,7 @@ class TestDeviceHandler(MAASTransactionServerTestCase):
             created_device["ip_assignment"],
             Equals(DEVICE_IP_ASSIGNMENT_TYPE.STATIC),
         )
-        static_interface = Interface.objects.get(mac_address=MAC(mac))
+        static_interface = Interface.objects.get(mac_address=mac)
         observed_subnet = static_interface.ip_addresses.first().subnet
         self.expectThat(
             observed_subnet,
@@ -620,7 +619,7 @@ class TestDeviceHandler(MAASTransactionServerTestCase):
             Equals(DEVICE_IP_ASSIGNMENT_TYPE.STATIC),
         )
         self.expectThat(created_device["ip_address"], Equals(ip_address))
-        static_interface = Interface.objects.get(mac_address=MAC(mac))
+        static_interface = Interface.objects.get(mac_address=mac)
         observed_subnet = static_interface.ip_addresses.first().subnet
         self.expectThat(
             observed_subnet,
@@ -674,7 +673,7 @@ class TestDeviceHandler(MAASTransactionServerTestCase):
         self.expectThat(
             created_device["ip_address"], Equals(static_ip_address)
         )
-        static_interface = Interface.objects.get(mac_address=MAC(mac_static))
+        static_interface = Interface.objects.get(mac_address=mac_static)
         observed_subnet = static_interface.ip_addresses.first().subnet
         self.expectThat(
             observed_subnet,
@@ -824,7 +823,7 @@ class TestDeviceHandler(MAASTransactionServerTestCase):
             updated_device["ip_assignment"],
             Equals(DEVICE_IP_ASSIGNMENT_TYPE.STATIC),
         )
-        static_interface = Interface.objects.get(mac_address=MAC(mac))
+        static_interface = Interface.objects.get(mac_address=mac)
         observed_subnet = static_interface.ip_addresses.first().subnet
         self.expectThat(
             observed_subnet,
@@ -857,7 +856,7 @@ class TestDeviceHandler(MAASTransactionServerTestCase):
             Equals(DEVICE_IP_ASSIGNMENT_TYPE.STATIC),
         )
         self.expectThat(updated_device["ip_address"], Equals(ip_address))
-        static_interface = Interface.objects.get(mac_address=MAC(mac))
+        static_interface = Interface.objects.get(mac_address=mac)
         observed_subnet = static_interface.ip_addresses.first().subnet
         self.expectThat(
             observed_subnet,
diff --git a/src/metadataserver/builtin_scripts/tests/test_hooks.py b/src/metadataserver/builtin_scripts/tests/test_hooks.py
index 8ee17cc..182ed48 100644
--- a/src/metadataserver/builtin_scripts/tests/test_hooks.py
+++ b/src/metadataserver/builtin_scripts/tests/test_hooks.py
@@ -21,7 +21,6 @@ from maasserver.enum import (
     NODE_STATUS,
     PARTITION_TABLE_TYPE,
 )
-from maasserver.fields import MAC
 from maasserver.models import (
     NodeMetadata,
     NUMANode,
@@ -3892,9 +3891,9 @@ class TestUpdateNodeNetworkInformation(MAASServerTestCase):
     """
 
     EXPECTED_INTERFACES = {
-        "eth0": MAC("00:00:00:00:00:01"),
-        "eth1": MAC("00:00:00:00:00:02"),
-        "eth2": MAC("00:00:00:00:00:03"),
+        "eth0": "00:00:00:00:00:01",
+        "eth1": "00:00:00:00:00:02",
+        "eth2": "00:00:00:00:00:03",
     }
 
     def assert_expected_interfaces_and_macs_exist_for_node(
@@ -4160,7 +4159,7 @@ class TestUpdateNodeNetworkInformation(MAASServerTestCase):
             iface.mac_address
             for iface in node.current_config.interface_set.all()
         ]
-        self.assertNotIn(MAC("01:23:45:67:89:ab"), db_macaddresses)
+        self.assertNotIn("01:23:45:67:89:ab", db_macaddresses)
 
     def test_reassign_mac(self):
         """Test whether we can assign a MAC address previously connected to a
@@ -4169,7 +4168,7 @@ class TestUpdateNodeNetworkInformation(MAASServerTestCase):
 
         # Create a MAC address that we know IS in the test dataset.
         interface_to_be_reassigned = factory.make_Interface(node=node1)
-        interface_to_be_reassigned.mac_address = MAC("00:00:00:00:00:01")
+        interface_to_be_reassigned.mac_address = "00:00:00:00:00:01"
         interface_to_be_reassigned.save()
 
         node2 = factory.make_Node()
@@ -4223,27 +4222,27 @@ class TestUpdateNodeNetworkInformation(MAASServerTestCase):
         # Note: since this VLANInterface will be linked to the default VLAN
         # ("vid 0", which is actually invalid) the VLANInterface will
         # automatically get the name "vlan0".
-        ETH0_MAC = self.EXPECTED_INTERFACES["eth0"].get_raw()
-        ETH1_MAC = self.EXPECTED_INTERFACES["eth1"].get_raw()
+        eth0_mac = self.EXPECTED_INTERFACES["eth0"]
+        eth1_mac = self.EXPECTED_INTERFACES["eth1"]
         BOND_NAME = "bond0"
         node = factory.make_Node()
 
         eth0 = factory.make_Interface(
-            name="eth0", mac_address=ETH0_MAC, node=node
+            name="eth0", mac_address=eth0_mac, node=node
         )
         eth1 = factory.make_Interface(
-            name="eth1", mac_address=ETH1_MAC, node=node
+            name="eth1", mac_address=eth1_mac, node=node
         )
 
         factory.make_Interface(
             INTERFACE_TYPE.VLAN,
-            mac_address=ETH0_MAC,
+            mac_address=eth0_mac,
             parents=[eth0],
             node=node,
         )
         factory.make_Interface(
             INTERFACE_TYPE.BOND,
-            mac_address=ETH1_MAC,
+            mac_address=eth1_mac,
             parents=[eth1],
             node=node,
             name=BOND_NAME,
@@ -4255,12 +4254,12 @@ class TestUpdateNodeNetworkInformation(MAASServerTestCase):
         self.assert_expected_interfaces_and_macs_exist_for_node(node)
 
     def test_interface_name_changed(self):
-        ETH0_MAC = self.EXPECTED_INTERFACES["eth1"].get_raw()
+        eth0_mac = self.EXPECTED_INTERFACES["eth1"]
         node = factory.make_Node()
         factory.make_Interface(
             INTERFACE_TYPE.PHYSICAL,
             name="eth0",
-            mac_address=ETH0_MAC,
+            mac_address=eth0_mac,
             node=node,
         )
         update_node_network_information(
@@ -4272,10 +4271,10 @@ class TestUpdateNodeNetworkInformation(MAASServerTestCase):
 
     def test_mac_id_is_preserved(self):
         """Test whether MAC address entities are preserved and not recreated"""
-        ETH0_MAC = self.EXPECTED_INTERFACES["eth0"].get_raw()
+        eth0_mac = self.EXPECTED_INTERFACES["eth0"]
         node = factory.make_Node()
         iface_to_be_preserved = factory.make_Interface(
-            mac_address=ETH0_MAC, node=node
+            mac_address=eth0_mac, node=node
         )
 
         update_node_network_information(
@@ -4285,11 +4284,11 @@ class TestUpdateNodeNetworkInformation(MAASServerTestCase):
         self.assertIsNotNone(reload_object(iface_to_be_preserved))
 
     def test_legacy_model_upgrade_preserves_interfaces(self):
-        ETH0_MAC = self.EXPECTED_INTERFACES["eth0"].get_raw()
-        ETH1_MAC = self.EXPECTED_INTERFACES["eth1"].get_raw()
+        eth0_mac = self.EXPECTED_INTERFACES["eth0"]
+        eth1_mac = self.EXPECTED_INTERFACES["eth1"]
         node = factory.make_Node()
-        eth0 = factory.make_Interface(mac_address=ETH0_MAC, node=node)
-        eth1 = factory.make_Interface(mac_address=ETH1_MAC, node=node)
+        eth0 = factory.make_Interface(mac_address=eth0_mac, node=node)
+        eth1 = factory.make_Interface(mac_address=eth1_mac, node=node)
         update_node_network_information(
             node, make_lxd_output(), create_numa_nodes(node)
         )
@@ -4300,15 +4299,15 @@ class TestUpdateNodeNetworkInformation(MAASServerTestCase):
         self.assert_expected_interfaces_and_macs_exist_for_node(node)
 
     def test_legacy_model_with_extra_mac(self):
-        ETH0_MAC = self.EXPECTED_INTERFACES["eth0"].get_raw()
-        ETH1_MAC = self.EXPECTED_INTERFACES["eth1"].get_raw()
-        ETH2_MAC = self.EXPECTED_INTERFACES["eth2"].get_raw()
-        ETH3_MAC = "00:00:00:00:01:04"
+        eth0_mac = self.EXPECTED_INTERFACES["eth0"]
+        eth1_mac = self.EXPECTED_INTERFACES["eth1"]
+        eth2_mac = self.EXPECTED_INTERFACES["eth2"]
+        eth3_mac = "00:00:00:00:01:04"
         node = factory.make_Node()
-        eth0 = factory.make_Interface(mac_address=ETH0_MAC, node=node)
-        eth1 = factory.make_Interface(mac_address=ETH1_MAC, node=node)
-        eth2 = factory.make_Interface(mac_address=ETH2_MAC, node=node)
-        eth3 = factory.make_Interface(mac_address=ETH3_MAC, node=node)
+        eth0 = factory.make_Interface(mac_address=eth0_mac, node=node)
+        eth1 = factory.make_Interface(mac_address=eth1_mac, node=node)
+        eth2 = factory.make_Interface(mac_address=eth2_mac, node=node)
+        eth3 = factory.make_Interface(mac_address=eth3_mac, node=node)
 
         update_node_network_information(
             node, make_lxd_output(), create_numa_nodes(node)
@@ -4325,12 +4324,12 @@ class TestUpdateNodeNetworkInformation(MAASServerTestCase):
         self.assertIsNone(reload_object(eth3))
 
     def test_deletes_virtual_interfaces_with_unique_mac(self):
-        ETH0_MAC = self.EXPECTED_INTERFACES["eth0"].get_raw()
-        ETH1_MAC = self.EXPECTED_INTERFACES["eth1"].get_raw()
+        eth0_mac = self.EXPECTED_INTERFACES["eth0"]
+        eth1_mac = self.EXPECTED_INTERFACES["eth1"]
         BOND_MAC = "00:00:00:00:01:02"
         node = factory.make_Node()
-        eth0 = factory.make_Interface(mac_address=ETH0_MAC, node=node)
-        eth1 = factory.make_Interface(mac_address=ETH1_MAC, node=node)
+        eth0 = factory.make_Interface(mac_address=eth0_mac, node=node)
+        eth1 = factory.make_Interface(mac_address=eth1_mac, node=node)
         factory.make_Interface(INTERFACE_TYPE.VLAN, node=node, parents=[eth0])
         factory.make_Interface(
             INTERFACE_TYPE.BOND,
@@ -4715,7 +4714,7 @@ class TestUpdateBootInterface(MAASServerTestCase):
         )
 
     def test_boot_interface_bootif_bonded_interfaces(self):
-        mac_address = factory.make_MAC()
+        mac_address = factory.make_mac_address()
         parent = factory.make_Interface(
             INTERFACE_TYPE.PHYSICAL, node=self.node, mac_address=mac_address
         )

Follow ups