diff --git a/ironic/common/neutron.py b/ironic/common/neutron.py
index 2cd31f5f95..1486806496 100644
--- a/ironic/common/neutron.py
+++ b/ironic/common/neutron.py
@@ -10,6 +10,8 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
+import random
+
 from neutronclient.common import exceptions as neutron_exceptions
 from neutronclient.v2_0 import client as clientv20
 from oslo_log import log
@@ -38,6 +40,26 @@ PHYSNET_PARAM_NAME = 'provider:physical_network'
 SEGMENTS_PARAM_NAME = 'segments'
 """Name of the neutron network API segments parameter."""
 
+BASE_MAC = ['fa', '16', '3e', '00', '00', '00']
+
+
+def _get_random_mac(base_mac):
+    """Get a random MAC address string of the specified base format.
+
+    The first 3 octets will remain unchanged. If the 4th octet is not
+    00, it will also be used. The others will be randomly generated.
+
+    :param base_mac: Base MAC address represented by an array of 6 strings/int
+    :returns: The MAC address string.
+    """
+
+    mac = [int(base_mac[0], 16), int(base_mac[1], 16),
+           int(base_mac[2], 16), random.getrandbits(8),
+           random.getrandbits(8), random.getrandbits(8)]
+    if base_mac[3] != '00':
+        mac[3] = int(base_mac[3], 16)
+    return ':'.join(["%02x" % x for x in mac])
+
 
 def _get_neutron_session():
     global _NEUTRON_SESSION
@@ -102,11 +124,18 @@ def unbind_neutron_port(port_id, client=None, context=None):
     if not client:
         client = get_client(context=context)
 
-    body = {'port': {'binding:host_id': '',
-                     'binding:profile': {}}}
+    body_unbind = {'port': {'binding:host_id': '',
+                            'binding:profile': {}}}
+    body_reset_mac = {'port': {
+        'mac_address': _get_random_mac(BASE_MAC)}}
 
     try:
-        client.update_port(port_id, body)
+        client.update_port(port_id, body_unbind)
+        # NOTE(hjensas): We need to reset the mac address in a separate step.
+        #   Exception PortBound will be raised by neutron as it refuses to
+        #   update the mac address of a bound port if we attempt to unbind and
+        #   reset the mac in the same call.
+        client.update_port(port_id, body_reset_mac)
     # NOTE(vsaienko): Ignore if port was deleted before calling vif detach.
     except neutron_exceptions.PortNotFoundClient:
         LOG.info('Port %s was not found while unbinding.', port_id)
diff --git a/ironic/drivers/modules/network/common.py b/ironic/drivers/modules/network/common.py
index b60ce7b238..485434c3dd 100644
--- a/ironic/drivers/modules/network/common.py
+++ b/ironic/drivers/modules/network/common.py
@@ -255,6 +255,7 @@ def plug_port_to_tenant_network(task, port_like_obj, client=None):
         'port': {
             'binding:vnic_type': neutron.VNIC_BAREMETAL,
             'binding:host_id': node.uuid,
+            'mac_address': port_like_obj.address
         }
     }
     binding_profile = {'local_link_information': local_link_info}
diff --git a/ironic/drivers/modules/network/flat.py b/ironic/drivers/modules/network/flat.py
index e62fefda4d..1b31aa5dfb 100644
--- a/ironic/drivers/modules/network/flat.py
+++ b/ironic/drivers/modules/network/flat.py
@@ -53,12 +53,7 @@ class FlatNetwork(common.NeutronVIFPortIDMixin,
         """
         self.get_cleaning_network_uuid(task)
 
-    def add_provisioning_network(self, task):
-        """Add the provisioning network to a node.
-
-        :param task: A TaskManager instance.
-        :raises: NetworkError when failed to set binding:host_id
-        """
+    def _bind_flat_ports(self, task):
         LOG.debug("Binding flat network ports")
         client = neutron.get_client(context=task.context)
         for port_like_obj in task.ports + task.portgroups:
@@ -71,7 +66,8 @@ class FlatNetwork(common.NeutronVIFPortIDMixin,
             body = {
                 'port': {
                     'binding:host_id': task.node.uuid,
-                    'binding:vnic_type': neutron.VNIC_BAREMETAL
+                    'binding:vnic_type': neutron.VNIC_BAREMETAL,
+                    'mac_address': port_like_obj.address
                 }
             }
             try:
@@ -83,26 +79,52 @@ class FlatNetwork(common.NeutronVIFPortIDMixin,
                 LOG.exception(msg)
                 raise exception.NetworkError(msg)
 
+    def _unbind_flat_ports(self, task):
+        node = task.node
+        LOG.info('Unbinding instance ports from node %s', node.uuid)
+
+        ports = [p for p in task.ports if not p.portgroup_id]
+        portgroups = task.portgroups
+        for port_like_obj in ports + portgroups:
+            vif_port_id = (
+                port_like_obj.internal_info.get(common.TENANT_VIF_KEY) or
+                port_like_obj.extra.get('vif_port_id'))
+            if not vif_port_id:
+                continue
+            neutron.unbind_neutron_port(vif_port_id, context=task.context)
+
+    def add_provisioning_network(self, task):
+        """Add the provisioning network to a node.
+
+        :param task: A TaskManager instance.
+        :raises: NetworkError when failed to set binding:host_id
+        """
+        self._bind_flat_ports(task)
+
     def remove_provisioning_network(self, task):
         """Remove the provisioning network from a node.
 
         :param task: A TaskManager instance.
         """
-        pass
+        self._unbind_flat_ports(task)
 
     def configure_tenant_networks(self, task):
         """Configure tenant networks for a node.
 
         :param task: A TaskManager instance.
         """
-        pass
+        self._bind_flat_ports(task)
 
     def unconfigure_tenant_networks(self, task):
         """Unconfigure tenant networks for a node.
 
+        Unbind the port here/now to avoid the possibility of the ironic port
+        being bound to the tenant and cleaning networks at the same time.
+
         :param task: A TaskManager instance.
+        :raises: NetworkError
         """
-        pass
+        self._unbind_flat_ports(task)
 
     def add_cleaning_network(self, task):
         """Add the cleaning network to a node.
@@ -141,3 +163,28 @@ class FlatNetwork(common.NeutronVIFPortIDMixin,
                 del internal_info['cleaning_vif_port_id']
                 port.internal_info = internal_info
                 port.save()
+
+    def add_rescuing_network(self, task):
+        """Add the rescuing network to a node.
+
+        Flat network does not use the rescuing network.
+        Bind the port again since unconfigure_tenant_network() unbound it.
+
+        :param task: A TaskManager instance.
+        :returns: a dictionary in the form {port.uuid: neutron_port['id']}
+        :raises: NetworkError, InvalidParameterValue
+        """
+        LOG.info('Bind ports for rescuing node %s', task.node.uuid)
+        self._bind_flat_ports(task)
+
+    def remove_rescuing_network(self, task):
+        """Remove the rescuing network from a node.
+
+        Flat network does not use the rescuing network.
+        Unbind the port again since add_rescuing_network() bound it.
+
+        :param task: A TaskManager instance.
+        :raises: NetworkError
+        """
+        LOG.info('Unbind ports for rescuing node %s', task.node.uuid)
+        self._unbind_flat_ports(task)
diff --git a/ironic/tests/unit/common/test_neutron.py b/ironic/tests/unit/common/test_neutron.py
index e58cac646c..d871d5f219 100644
--- a/ironic/tests/unit/common/test_neutron.py
+++ b/ironic/tests/unit/common/test_neutron.py
@@ -10,6 +10,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import random
+
 from keystoneauth1 import loading as kaloading
 import mock
 from neutronclient.common import exceptions as neutron_client_exc
@@ -748,29 +750,42 @@ class TestUpdatePortAddress(base.TestCase):
 
 
 @mock.patch.object(neutron, 'get_client', autospec=True)
+@mock.patch.object(neutron, '_get_random_mac', autospec=True)
 class TestUnbindPort(base.TestCase):
 
     def setUp(self):
         super(TestUnbindPort, self).setUp()
         self.context = context.RequestContext()
 
-    def test_unbind_neutron_port_client_passed(self, mock_client):
+    def test_unbind_neutron_port_client_passed(self, mock_random_mac,
+                                               mock_client):
         port_id = 'fake-port-id'
-        body = {
+        address = 'fe:54:00:77:07:d9'
+        mock_random_mac.return_value = address
+        body_unbind = {
             'port': {
                 'binding:host_id': '',
                 'binding:profile': {}
             }
         }
+        body_reset_mac = {
+            'port': {
+                'mac_address': address
+            }
+        }
+        update_calls = [
+            mock.call(port_id, body_unbind),
+            mock.call(port_id, body_reset_mac)
+        ]
         neutron.unbind_neutron_port(port_id,
                                     mock_client(context=self.context),
                                     context=self.context)
         self.assertEqual(1, mock_client.call_count)
-        mock_client.return_value.update_port.assert_called_once_with(port_id,
-                                                                     body)
+        mock_client.return_value.update_port.assert_has_calls(update_calls)
 
     @mock.patch.object(neutron, 'LOG', autospec=True)
-    def test_unbind_neutron_port_failure(self, mock_log, mock_client):
+    def test_unbind_neutron_port_failure(self, mock_log, mock_random_mac,
+                                         mock_client):
         mock_client.return_value.update_port.side_effect = (
             neutron_client_exc.NeutronClientException())
         body = {
@@ -787,21 +802,32 @@ class TestUnbindPort(base.TestCase):
                                                                      body)
         mock_log.exception.assert_called_once()
 
-    def test_unbind_neutron_port(self, mock_client):
+    def test_unbind_neutron_port(self, mock_random_mac, mock_client):
         port_id = 'fake-port-id'
-        body = {
+        address = 'fe:54:00:77:07:d9'
+        mock_random_mac.return_value = address
+        body_unbind = {
             'port': {
                 'binding:host_id': '',
                 'binding:profile': {}
             }
         }
+        body_reset_mac = {
+            'port': {
+                'mac_address': address
+            }
+        }
+        update_calls = [
+            mock.call(port_id, body_unbind),
+            mock.call(port_id, body_reset_mac)
+        ]
         neutron.unbind_neutron_port(port_id, context=self.context)
         mock_client.assert_called_once_with(context=self.context)
-        mock_client.return_value.update_port.assert_called_once_with(port_id,
-                                                                     body)
+        mock_client.return_value.update_port.assert_has_calls(update_calls)
 
     @mock.patch.object(neutron, 'LOG', autospec=True)
-    def test_unbind_neutron_port_not_found(self, mock_log, mock_client):
+    def test_unbind_neutron_port_not_found(self, mock_log, mock_random_mac,
+                                           mock_client):
         port_id = 'fake-port-id'
         mock_client.return_value.update_port.side_effect = (
             neutron_client_exc.PortNotFoundClient())
@@ -1042,3 +1068,18 @@ class TestGetPhysnetsByPortUUID(base.TestCase):
                                         fields=self.PORT_FIELDS)
         mock_gn.assert_called_once_with(self.client, network_uuid,
                                         fields=self.NETWORK_FIELDS)
+
+
+class TestGetRandomMac(base.TestCase):
+
+    @mock.patch.object(random, 'getrandbits', return_value=0xa2)
+    def test_first_4_octets_unchanged(self, mock_rnd):
+        mac = neutron._get_random_mac(['aa', 'bb', '00', 'dd', 'ee', 'ff'])
+        self.assertEqual('aa:bb:00:dd:a2:a2', mac)
+        mock_rnd.assert_called_with(8)
+
+    @mock.patch.object(random, 'getrandbits', return_value=0xa2)
+    def test_first_4th_octet_generated(self, mock_rnd):
+        mac = neutron._get_random_mac(['aa', 'bb', 'cc', '00', 'ee', 'ff'])
+        self.assertEqual('aa:bb:cc:a2:a2:a2', mac)
+        mock_rnd.assert_called_with(8)
diff --git a/ironic/tests/unit/drivers/modules/network/test_flat.py b/ironic/tests/unit/drivers/modules/network/test_flat.py
index 31462c3fb9..7179f6590d 100644
--- a/ironic/tests/unit/drivers/modules/network/test_flat.py
+++ b/ironic/tests/unit/drivers/modules/network/test_flat.py
@@ -175,8 +175,7 @@ class TestFlatInterface(db_base.DbTestCase):
         self.assertNotIn('cleaning_vif_port_id', self.port.internal_info)
 
     @mock.patch.object(neutron, 'get_client')
-    def test_add_provisioning_network_set_binding_host_id(
-            self, client_mock):
+    def test__bind_flat_ports_set_binding_host_id(self, client_mock):
         upd_mock = mock.Mock()
         client_mock.return_value.update_port = upd_mock
         extra = {'vif_port_id': 'foo'}
@@ -184,14 +183,14 @@ class TestFlatInterface(db_base.DbTestCase):
                                address='52:54:00:cf:2d:33', extra=extra,
                                uuid=uuidutils.generate_uuid())
         exp_body = {'port': {'binding:host_id': self.node.uuid,
-                             'binding:vnic_type': neutron.VNIC_BAREMETAL}}
+                             'binding:vnic_type': neutron.VNIC_BAREMETAL,
+                             'mac_address': '52:54:00:cf:2d:33'}}
         with task_manager.acquire(self.context, self.node.id) as task:
-            self.interface.add_provisioning_network(task)
+            self.interface._bind_flat_ports(task)
         upd_mock.assert_called_once_with('foo', exp_body)
 
     @mock.patch.object(neutron, 'get_client')
-    def test_add_provisioning_network_set_binding_host_id_portgroup(
-            self, client_mock):
+    def test__bind_flat_ports_set_binding_host_id_portgroup(self, client_mock):
         upd_mock = mock.Mock()
         client_mock.return_value.update_port = upd_mock
         internal_info = {'tenant_vif_port_id': 'foo'}
@@ -201,17 +200,46 @@ class TestFlatInterface(db_base.DbTestCase):
         utils.create_test_port(
             self.context, node_id=self.node.id, address='52:54:00:cf:2d:33',
             extra={'vif_port_id': 'bar'}, uuid=uuidutils.generate_uuid())
-        exp_body = {'port': {'binding:host_id': self.node.uuid,
-                             'binding:vnic_type': neutron.VNIC_BAREMETAL}}
+        exp_body1 = {'port': {'binding:host_id': self.node.uuid,
+                              'binding:vnic_type': neutron.VNIC_BAREMETAL,
+                              'mac_address': '52:54:00:cf:2d:33'}}
+        exp_body2 = {'port': {'binding:host_id': self.node.uuid,
+                              'binding:vnic_type': neutron.VNIC_BAREMETAL,
+                              'mac_address': '52:54:00:cf:2d:31'}}
         with task_manager.acquire(self.context, self.node.id) as task:
-            self.interface.add_provisioning_network(task)
+            self.interface._bind_flat_ports(task)
         upd_mock.assert_has_calls([
-            mock.call('bar', exp_body), mock.call('foo', exp_body)
-        ])
+            mock.call('bar', exp_body1), mock.call('foo', exp_body2)])
+
+    @mock.patch.object(neutron, 'unbind_neutron_port')
+    def test__unbind_flat_ports(self, unbind_neutron_port_mock):
+        extra = {'vif_port_id': 'foo'}
+        utils.create_test_port(self.context, node_id=self.node.id,
+                               address='52:54:00:cf:2d:33', extra=extra,
+                               uuid=uuidutils.generate_uuid())
+        with task_manager.acquire(self.context, self.node.id) as task:
+            self.interface._unbind_flat_ports(task)
+        unbind_neutron_port_mock.assert_called_once_with('foo',
+                                                         context=self.context)
+
+    @mock.patch.object(neutron, 'unbind_neutron_port')
+    def test__unbind_flat_ports_portgroup(self, unbind_neutron_port_mock):
+        internal_info = {'tenant_vif_port_id': 'foo'}
+        utils.create_test_portgroup(self.context, node_id=self.node.id,
+                                    internal_info=internal_info,
+                                    uuid=uuidutils.generate_uuid())
+        extra = {'vif_port_id': 'bar'}
+        utils.create_test_port(self.context, node_id=self.node.id,
+                               address='52:54:00:cf:2d:33', extra=extra,
+                               uuid=uuidutils.generate_uuid())
+        with task_manager.acquire(self.context, self.node.id) as task:
+            self.interface._unbind_flat_ports(task)
+        unbind_neutron_port_mock.has_calls(
+            [mock.call('foo', context=self.context),
+             mock.call('bar', context=self.context)])
 
     @mock.patch.object(neutron, 'get_client')
-    def test_add_provisioning_network_binding_host_id_raise(
-            self, client_mock):
+    def test__bind_flat_ports_set_binding_host_id_raise(self, client_mock):
         client_mock.return_value.update_port.side_effect = \
             (neutron_exceptions.ConnectionFailed())
         extra = {'vif_port_id': 'foo'}
@@ -220,5 +248,28 @@ class TestFlatInterface(db_base.DbTestCase):
                                uuid=uuidutils.generate_uuid())
         with task_manager.acquire(self.context, self.node.id) as task:
             self.assertRaises(exception.NetworkError,
-                              self.interface.add_provisioning_network,
-                              task)
+                              self.interface._bind_flat_ports, task)
+
+    @mock.patch.object(flat_interface.FlatNetwork, '_bind_flat_ports')
+    def test_add_rescuing_network(self, bind_mock):
+        with task_manager.acquire(self.context, self.node.id) as task:
+            self.interface.add_rescuing_network(task)
+            bind_mock.assert_called_once_with(task)
+
+    @mock.patch.object(flat_interface.FlatNetwork, '_unbind_flat_ports')
+    def test_remove_rescuing_network(self, unbind_mock):
+        with task_manager.acquire(self.context, self.node.id) as task:
+            self.interface.remove_rescuing_network(task)
+            unbind_mock.assert_called_once_with(task)
+
+    @mock.patch.object(flat_interface.FlatNetwork, '_bind_flat_ports')
+    def test_add_provisioning_network(self, bind_mock):
+        with task_manager.acquire(self.context, self.node.id) as task:
+            self.interface.add_provisioning_network(task)
+            bind_mock.assert_called_once_with(task)
+
+    @mock.patch.object(flat_interface.FlatNetwork, '_unbind_flat_ports')
+    def test_remove_provisioning_network(self, unbind_mock):
+        with task_manager.acquire(self.context, self.node.id) as task:
+            self.interface.remove_provisioning_network(task)
+            unbind_mock.assert_called_once_with(task)
diff --git a/ironic/tests/unit/drivers/modules/network/test_neutron.py b/ironic/tests/unit/drivers/modules/network/test_neutron.py
index 49e3d01a20..39c7fd9d80 100644
--- a/ironic/tests/unit/drivers/modules/network/test_neutron.py
+++ b/ironic/tests/unit/drivers/modules/network/test_neutron.py
@@ -580,12 +580,10 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
                 'binding:vnic_type': 'baremetal',
                 'binding:host_id': self.node.uuid,
                 'binding:profile': {'local_link_information':
-                                    [self.port.local_link_connection]}
+                                    [self.port.local_link_connection]},
+                'mac_address': '52:54:00:cf:2d:32'
             }
         }
-        utils.create_test_port(self.context, node_id=self.node.id,
-                               address='52:54:00:cf:2d:33', extra={},
-                               uuid=uuidutils.generate_uuid())
         upd_mock = mock.Mock()
         client_mock.return_value.update_port = upd_mock
         with task_manager.acquire(self.context, self.node.id) as task:
@@ -645,10 +643,12 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
         port1_body['port']['binding:profile'] = {
             'local_link_information': [self.port.local_link_connection]
         }
+        port1_body['port']['mac_address'] = '52:54:00:cf:2d:32'
         port2_body = copy.deepcopy(expected_body)
         port2_body['port']['binding:profile'] = {
             'local_link_information': [second_port.local_link_connection]
         }
+        port2_body['port']['mac_address'] = '52:54:00:cf:2d:33'
         if is_client_id:
             port1_body['port']['extra_dhcp_opts'] = (
                 [{'opt_name': '61', 'opt_value': client_ids[0]}])
@@ -725,12 +725,14 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
         call1_body['port']['binding:profile'] = {
             'local_link_information': [self.port.local_link_connection],
         }
+        call1_body['port']['mac_address'] = '52:54:00:cf:2d:32'
         call2_body = copy.deepcopy(expected_body)
         call2_body['port']['binding:profile'] = {
             'local_link_information': [port1.local_link_connection,
                                        port2.local_link_connection],
             'local_group_information': local_group_info
         }
+        call2_body['port']['mac_address'] = 'ff:54:00:cf:2d:32'
         with task_manager.acquire(self.context, self.node.id) as task:
             # Override task.portgroups here, to have ability to check
             # that mocked get_local_group_information was called with
diff --git a/ironic/tests/unit/drivers/modules/test_agent.py b/ironic/tests/unit/drivers/modules/test_agent.py
index fba4807c4c..16c653c433 100644
--- a/ironic/tests/unit/drivers/modules/test_agent.py
+++ b/ironic/tests/unit/drivers/modules/test_agent.py
@@ -178,6 +178,7 @@ class TestAgentDeploy(db_base.DbTestCase):
             'driver_info': DRIVER_INFO,
             'driver_internal_info': DRIVER_INTERNAL_INFO,
             'storage_interface': 'noop',
+            'network_interface': 'noop'
         }
         self.node = object_utils.create_test_node(self.context, **n)
         self.ports = [
@@ -351,6 +352,9 @@ class TestAgentDeploy(db_base.DbTestCase):
                        storage_detach_volumes_mock):
         object_utils.create_test_volume_target(
             self.context, node_id=self.node.id)
+        node = self.node
+        node.network_interface = 'flat'
+        node.save()
         with task_manager.acquire(
                 self.context, self.node['uuid'], shared=False) as task:
             driver_return = self.driver.tear_down(task)
@@ -386,6 +390,9 @@ class TestAgentDeploy(db_base.DbTestCase):
             build_instance_info_mock, build_options_mock,
             pxe_prepare_ramdisk_mock, storage_driver_info_mock,
             storage_attach_volumes_mock):
+        node = self.node
+        node.network_interface = 'flat'
+        node.save()
         with task_manager.acquire(
                 self.context, self.node['uuid'], shared=False) as task:
             task.node.provision_state = states.DEPLOYING
@@ -601,6 +608,9 @@ class TestAgentDeploy(db_base.DbTestCase):
             build_options_mock, pxe_prepare_ramdisk_mock,
             validate_net_mock, add_provisioning_net_mock):
         self.config(group='agent', manage_agent_boot=False)
+        node = self.node
+        node.network_interface = 'flat'
+        node.save()
         with task_manager.acquire(
                 self.context, self.node['uuid'], shared=False) as task:
             task.node.provision_state = states.DEPLOYING
@@ -700,6 +710,9 @@ class TestAgentDeploy(db_base.DbTestCase):
             add_provisioning_net_mock, storage_driver_info_mock,
             storage_attach_volumes_mock, should_write_image_mock):
         should_write_image_mock.return_value = False
+        node = self.node
+        node.network_interface = 'flat'
+        node.save()
         with task_manager.acquire(
                 self.context, self.node['uuid'], shared=False) as task:
             task.node.provision_state = states.DEPLOYING
@@ -750,6 +763,9 @@ class TestAgentDeploy(db_base.DbTestCase):
                                       validate_net_mock,
                                       add_provisioning_net_mock):
         mock_write.return_value = False
+        node = self.node
+        node.network_interface = 'flat'
+        node.save()
         with task_manager.acquire(
                 self.context, self.node['uuid'], shared=False) as task:
             task.node.provision_state = states.DEPLOYING
diff --git a/ironic/tests/unit/drivers/modules/test_agent_base_vendor.py b/ironic/tests/unit/drivers/modules/test_agent_base_vendor.py
index c99c921875..cc071bb4cd 100644
--- a/ironic/tests/unit/drivers/modules/test_agent_base_vendor.py
+++ b/ironic/tests/unit/drivers/modules/test_agent_base_vendor.py
@@ -68,6 +68,7 @@ class AgentDeployMixinBaseTest(db_base.DbTestCase):
             'instance_info': INSTANCE_INFO,
             'driver_info': DRIVER_INFO,
             'driver_internal_info': DRIVER_INTERNAL_INFO,
+            'network_interface': 'noop'
         }
         self.node = object_utils.create_test_node(self.context, **n)
 
@@ -497,9 +498,9 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
                        spec=types.FunctionType)
     @mock.patch.object(agent_client.AgentClient, 'power_off',
                        spec=types.FunctionType)
-    @mock.patch('ironic.drivers.modules.network.flat.FlatNetwork.'
+    @mock.patch('ironic.drivers.modules.network.noop.NoopNetwork.'
                 'remove_provisioning_network', spec_set=True, autospec=True)
-    @mock.patch('ironic.drivers.modules.network.flat.FlatNetwork.'
+    @mock.patch('ironic.drivers.modules.network.noop.NoopNetwork.'
                 'configure_tenant_networks', spec_set=True, autospec=True)
     def test_reboot_and_finish_deploy_soft_poweroff_doesnt_complete(
             self, configure_tenant_net_mock, remove_provisioning_net_mock,
@@ -528,9 +529,9 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
     @mock.patch.object(manager_utils, 'node_power_action', autospec=True)
     @mock.patch.object(agent_client.AgentClient, 'power_off',
                        spec=types.FunctionType)
-    @mock.patch('ironic.drivers.modules.network.flat.FlatNetwork.'
+    @mock.patch('ironic.drivers.modules.network.noop.NoopNetwork.'
                 'remove_provisioning_network', spec_set=True, autospec=True)
-    @mock.patch('ironic.drivers.modules.network.flat.FlatNetwork.'
+    @mock.patch('ironic.drivers.modules.network.noop.NoopNetwork.'
                 'configure_tenant_networks', spec_set=True, autospec=True)
     def test_reboot_and_finish_deploy_soft_poweroff_fails(
             self, configure_tenant_net_mock, remove_provisioning_net_mock,
@@ -560,9 +561,9 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
                        spec=types.FunctionType)
     @mock.patch.object(agent_client.AgentClient, 'power_off',
                        spec=types.FunctionType)
-    @mock.patch('ironic.drivers.modules.network.flat.FlatNetwork.'
+    @mock.patch('ironic.drivers.modules.network.noop.NoopNetwork.'
                 'remove_provisioning_network', spec_set=True, autospec=True)
-    @mock.patch('ironic.drivers.modules.network.flat.FlatNetwork.'
+    @mock.patch('ironic.drivers.modules.network.noop.NoopNetwork.'
                 'configure_tenant_networks', spec_set=True, autospec=True)
     def test_reboot_and_finish_deploy_get_power_state_fails(
             self, configure_tenant_net_mock, remove_provisioning_net_mock,
@@ -655,9 +656,9 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
                        spec=types.FunctionType)
     @mock.patch.object(agent_client.AgentClient, 'power_off',
                        spec=types.FunctionType)
-    @mock.patch('ironic.drivers.modules.network.flat.FlatNetwork.'
+    @mock.patch('ironic.drivers.modules.network.noop.NoopNetwork.'
                 'remove_provisioning_network', spec_set=True, autospec=True)
-    @mock.patch('ironic.drivers.modules.network.flat.FlatNetwork.'
+    @mock.patch('ironic.drivers.modules.network.noop.NoopNetwork.'
                 'configure_tenant_networks', spec_set=True, autospec=True)
     def test_reboot_and_finish_deploy_power_on_fails(
             self, configure_tenant_net_mock, remove_provisioning_net_mock,
diff --git a/releasenotes/notes/ensure-unbind-flat-vifs-and-clear-macs-34eec149618e5964.yaml b/releasenotes/notes/ensure-unbind-flat-vifs-and-clear-macs-34eec149618e5964.yaml
new file mode 100644
index 0000000000..347b7d3c5c
--- /dev/null
+++ b/releasenotes/notes/ensure-unbind-flat-vifs-and-clear-macs-34eec149618e5964.yaml
@@ -0,0 +1,13 @@
+---
+fixes:
+  - |
+    Fixes an issue where Neutron ports would be left with a baremetal MAC
+    address associated after an instance is deleted from a baremetal host. This
+    caused problems with MAC address conflicts in follow up deployments to the
+    same baremetal host. `bug 2004428
+    <https://storyboard.openstack.org/#!/story/2004428>`_.
+  - |
+    Fixes an issue where a flat Neutron port would be left with a host ID
+    associated with it after an instance is deleted from a baremetal host. This
+    caused problems with reusing the same port for a new instance as it is
+    already bound to the old instance.