diff --git a/nova/tests/unit/virt/libvirt/test_config.py b/nova/tests/unit/virt/libvirt/test_config.py
index ae3017261d..3bd460bd42 100644
--- a/nova/tests/unit/virt/libvirt/test_config.py
+++ b/nova/tests/unit/virt/libvirt/test_config.py
@@ -2103,6 +2103,7 @@ class LibvirtConfigGuestInterfaceTest(LibvirtConfigBaseTest):
def test_config_ethernet(self):
obj = config.LibvirtConfigGuestInterface()
obj.net_type = "ethernet"
+ obj.managed = "no"
obj.mac_addr = "DE:AD:BE:EF:CA:FE"
obj.model = "virtio"
obj.target_dev = "vnet0"
@@ -2120,7 +2121,7 @@ class LibvirtConfigGuestInterfaceTest(LibvirtConfigBaseTest):
-
+
@@ -2136,6 +2137,7 @@ class LibvirtConfigGuestInterfaceTest(LibvirtConfigBaseTest):
def test_config_ethernet_with_mtu(self):
obj = config.LibvirtConfigGuestInterface()
obj.net_type = "ethernet"
+ obj.managed = "no"
obj.mac_addr = "DE:AD:BE:EF:CA:FE"
obj.model = "virtio"
obj.target_dev = "vnet0"
@@ -2155,7 +2157,7 @@ class LibvirtConfigGuestInterfaceTest(LibvirtConfigBaseTest):
-
+
diff --git a/nova/tests/unit/virt/libvirt/test_vif.py b/nova/tests/unit/virt/libvirt/test_vif.py
index efd1058ca5..8ea2f3e093 100644
--- a/nova/tests/unit/virt/libvirt/test_vif.py
+++ b/nova/tests/unit/virt/libvirt/test_vif.py
@@ -540,14 +540,18 @@ class LibvirtVifTestCase(test.NoDBTestCase):
mac = node.find("mac").get("address")
self.assertEqual(mac, vif['address'])
- def _assertTypeEquals(self, node, type, attr, source, br_want):
+ def _assertTypeEquals(self, node, type, attr, source, br_want,
+ managed_want=None):
self.assertEqual(node.get("type"), type)
br_name = node.find(attr).get(source)
self.assertEqual(br_name, br_want)
+ if managed_want is not None:
+ managed = node.find(attr).get("managed")
+ self.assertEqual(managed, managed_want)
def _assertTypeAndMacEquals(self, node, type, attr, source, vif,
- br_want=None):
- self._assertTypeEquals(node, type, attr, source, br_want)
+ br_want=None, managed_want=None):
+ self._assertTypeEquals(node, type, attr, source, br_want, managed_want)
self._assertMacEquals(node, vif)
def _assertModel(self, xml, model_want=None, driver_want=None):
@@ -1113,7 +1117,7 @@ class LibvirtVifTestCase(test.NoDBTestCase):
xml = self._get_instance_xml(d, self.vif_tap)
node = self._get_node(xml)
self._assertTypeAndMacEquals(node, "ethernet", "target", "dev",
- self.vif_tap, br_want)
+ self.vif_tap, br_want, "no")
@mock.patch('nova.privsep.linux_net.device_exists', return_value=True)
@mock.patch('nova.privsep.linux_net.set_device_mtu')
diff --git a/nova/virt/libvirt/config.py b/nova/virt/libvirt/config.py
index 43bbdbceca..33a5bad4ef 100644
--- a/nova/virt/libvirt/config.py
+++ b/nova/virt/libvirt/config.py
@@ -1913,6 +1913,7 @@ class LibvirtConfigGuestInterface(LibvirtConfigGuestDevice):
self.device_addr = None
self.mtu = None
self.alias = None
+ self.managed = None
def __eq__(self, other):
if not isinstance(other, LibvirtConfigGuestInterface):
@@ -2019,7 +2020,11 @@ class LibvirtConfigGuestInterface(LibvirtConfigGuestDevice):
dev.append(vlan_elem)
if self.target_dev is not None:
- dev.append(etree.Element("target", dev=self.target_dev))
+ if self.managed is not None:
+ dev.append(etree.Element("target", dev=self.target_dev,
+ managed=self.managed))
+ else:
+ dev.append(etree.Element("target", dev=self.target_dev))
if self.vporttype is not None:
vport = etree.Element("virtualport", type=self.vporttype)
@@ -2105,6 +2110,7 @@ class LibvirtConfigGuestInterface(LibvirtConfigGuestDevice):
self.source_dev = c.get('bridge')
elif c.tag == 'target':
self.target_dev = c.get('dev')
+ self.managed = c.get('managed')
elif c.tag == 'script':
self.script = c.get('path')
elif c.tag == 'vlan':
diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py
index 275cb002af..cac808efff 100644
--- a/nova/virt/libvirt/vif.py
+++ b/nova/virt/libvirt/vif.py
@@ -435,6 +435,7 @@ class LibvirtGenericVIFDriver(object):
dev = self.get_vif_devname(vif)
designer.set_vif_host_backend_ethernet_config(conf, dev)
+ conf.managed = "no"
network = vif.get('network')
if network and network.get_meta('mtu'):
diff --git a/releasenotes/notes/bug-2033681-fix-vif-type-tap-with-new-libvirt-791e8b5f170e9c40.yaml b/releasenotes/notes/bug-2033681-fix-vif-type-tap-with-new-libvirt-791e8b5f170e9c40.yaml
new file mode 100644
index 0000000000..c254a2ce99
--- /dev/null
+++ b/releasenotes/notes/bug-2033681-fix-vif-type-tap-with-new-libvirt-791e8b5f170e9c40.yaml
@@ -0,0 +1,16 @@
+---
+fixes:
+ - |
+ Nova operation with VIF type TAP was broken by a libvirt change (in 9.5.0)
+ to treat TAP interface specifications as "managed" by default. This means
+ that libvirt expects to create the TAP interface itself, which is
+ incompatible with how Nova works, because Nova creates the TAP interface
+ before telling libvirt about it. In particular this broke OpenStack with
+ Calico as the network plugin, because Calico uses VIF type TAP.
+
+ This is now fixed, by Nova adding the `managed="no"` attribute to TAP
+ interface XML specifications.
+
+ See `bug 2033681`_ for more details.
+
+ .. _bug 2033681: https://bugs.launchpad.net/nova/+bug/2033681