diff --git a/nova/tests/test_libvirt_vif.py b/nova/tests/test_libvirt_vif.py
new file mode 100644
index 000000000000..3f9b8e1686a0
--- /dev/null
+++ b/nova/tests/test_libvirt_vif.py
@@ -0,0 +1,159 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Nicira, Inc
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from xml.etree import ElementTree
+
+from nova import flags
+from nova import test
+from nova import utils
+from nova.virt import firewall
+from nova.virt import vif
+from nova.virt.libvirt import connection
+from nova.virt.libvirt.vif import LibvirtBridgeDriver, \
+ LibvirtOpenVswitchDriver, LibvirtOpenVswitchVirtualPortDriver
+
+FLAGS = flags.FLAGS
+
+
+class LibvirtVifTestCase(test.TestCase):
+
+ net = {
+ 'cidr': '101.168.1.0/24',
+ 'cidr_v6': '101:1db9::/64',
+ 'gateway_v6': '101:1db9::1',
+ 'netmask_v6': '64',
+ 'netmask': '255.255.255.0',
+ 'bridge': 'br0',
+ 'bridge_interface': 'eth0',
+ 'vlan': 99,
+ 'gateway': '101.168.1.1',
+ 'broadcast': '101.168.1.255',
+ 'dns1': '8.8.8.8'
+ }
+
+ mapping = {
+ 'mac': 'ca:fe:de:ad:be:ef',
+ 'gateway_v6': net['gateway_v6'],
+ 'ips': [{'ip': '101.168.1.9'}],
+ 'dhcp_server': '191.168.1.1',
+ 'vif_uuid': 'vif-xxx-yyy-zzz'
+ }
+
+ instance = {
+ 'uuid': 'instance-uuid'
+ }
+
+ def setUp(self):
+ super(LibvirtVifTestCase, self).setUp()
+ self.flags(allow_same_net_traffic=True)
+ self.executes = []
+
+ def fake_execute(*cmd, **kwargs):
+ self.executes.append(cmd)
+ return None, None
+
+ self.stubs.Set(utils, 'execute', fake_execute)
+ t = __import__('Cheetah.Template', globals(), locals(),
+ ['Template'], -1)
+ self.Template = t.Template
+ xml_file = open(FLAGS.libvirt_xml_template)
+ self.xml_template = xml_file.read()
+
+ def _create_xml_info(self, vif_type, nics):
+ return {
+ 'type': 'qemu',
+ 'name': 'fake-name',
+ 'uuid': 'fake-uuid',
+ 'memory_kb': 100 * 1024,
+ 'basepath': 'foobar',
+ 'vcpus': 4,
+ 'rescue': False,
+ 'disk_prefix': '/dev/sda',
+ 'driver_type': 'raw',
+ 'root_device_type': 'disk',
+ 'vif_type': vif_type,
+ 'nics': nics,
+ 'ebs_root': True,
+ 'ephemeral_device': False,
+ 'volumes': [],
+ 'use_virtio_for_bridges': False,
+ 'ephemerals': []}
+
+ def _get_instance_xml(self, driver, vif_type):
+ nic_dict = driver.plug(self.instance, self.net, self.mapping)
+ xml_info = self._create_xml_info(vif_type, [nic_dict])
+ xml = str(self.Template(self.xml_template, searchList=[xml_info]))
+ return xml
+
+ def test_bridge_driver(self):
+ d = LibvirtBridgeDriver()
+ xml = self._get_instance_xml(d, 'bridge')
+
+ doc = ElementTree.fromstring(xml)
+ ret = doc.findall('./devices/interface')
+ self.assertEqual(len(ret), 1)
+ node = ret[0]
+ self.assertEqual(node.get("type"), "bridge")
+ br_name = node.find("source").get("bridge")
+ self.assertEqual(br_name, self.net['bridge'])
+ mac = node.find("mac").get("address")
+ self.assertEqual(mac, self.mapping['mac'])
+
+ d.unplug(None, self.net, self.mapping)
+
+ def test_ovs_ethernet_driver(self):
+ d = LibvirtOpenVswitchDriver()
+ xml = self._get_instance_xml(d, 'ethernet')
+
+ doc = ElementTree.fromstring(xml)
+ ret = doc.findall('./devices/interface')
+ self.assertEqual(len(ret), 1)
+ node = ret[0]
+ self.assertEqual(node.get("type"), "ethernet")
+ dev_name = node.find("target").get("dev")
+ self.assertTrue(dev_name.startswith("tap"))
+ mac = node.find("mac").get("address")
+ self.assertEqual(mac, self.mapping['mac'])
+ script = node.find("script").get("path")
+ self.assertEquals(script, "")
+
+ d.unplug(None, self.net, self.mapping)
+
+ def test_ovs_virtualport_driver(self):
+ d = LibvirtOpenVswitchVirtualPortDriver()
+ xml = self._get_instance_xml(d, 'ovs_virtualport')
+
+ doc = ElementTree.fromstring(xml)
+ ret = doc.findall('./devices/interface')
+ self.assertEqual(len(ret), 1)
+ node = ret[0]
+ self.assertEqual(node.get("type"), "bridge")
+
+ br_name = node.find("source").get("bridge")
+ self.assertEqual(br_name, FLAGS.libvirt_ovs_bridge)
+ mac = node.find("mac").get("address")
+ self.assertEqual(mac, self.mapping['mac'])
+ vp = node.find("virtualport")
+ self.assertEqual(vp.get("type"), "openvswitch")
+ iface_id_found = False
+ for p_elem in vp.findall("parameters"):
+ iface_id = p_elem.get("interfaceid", None)
+ if iface_id:
+ self.assertEqual(iface_id, self.mapping['vif_uuid'])
+ iface_id_found = True
+
+ self.assertTrue(iface_id_found)
+ d.unplug(None, self.net, self.mapping)
diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template
index d41b09f7be38..9a216e14b856 100644
--- a/nova/virt/libvirt.xml.template
+++ b/nova/virt/libvirt.xml.template
@@ -130,6 +130,14 @@
+ #else if $vif_type == 'ovs_virtualport'
+
+
+
+
+
+
+
#else
diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py
index b60a9b930853..886eea77f9e6 100644
--- a/nova/virt/libvirt/vif.py
+++ b/nova/virt/libvirt/vif.py
@@ -103,7 +103,9 @@ class LibvirtBridgeDriver(VIFDriver):
class LibvirtOpenVswitchDriver(VIFDriver):
- """VIF driver for Open vSwitch."""
+ """VIF driver for Open vSwitch that uses type='ethernet'
+ libvirt XML. Used for libvirt versions that do not support
+ OVS virtual port XML (0.9.10 or earlier)."""
def get_dev_name(_self, iface_id):
return "tap" + iface_id[0:11]
@@ -132,6 +134,8 @@ class LibvirtOpenVswitchDriver(VIFDriver):
"external-ids:iface-status=active",
'--', 'set', 'Interface', dev,
"external-ids:attached-mac=%s" % mapping['mac'],
+ '--', 'set', 'Interface', dev,
+ "external-ids:vm-uuid=%s" % instance['uuid'],
run_as_root=True)
result = {
@@ -152,3 +156,19 @@ class LibvirtOpenVswitchDriver(VIFDriver):
LOG.warning(_("Failed while unplugging vif of instance '%s'"),
instance['name'])
raise
+
+
+class LibvirtOpenVswitchVirtualPortDriver(VIFDriver):
+ """VIF driver for Open vSwitch that uses integrated libvirt
+ OVS virtual port XML (introduced in libvirt 0.9.11)."""
+
+ def plug(self, instance, network, mapping):
+ """ Pass data required to create OVS virtual port element"""
+ return {
+ 'bridge_name': FLAGS.libvirt_ovs_bridge,
+ 'ovs_interfaceid': mapping['vif_uuid'],
+ 'mac_address': mapping['mac']}
+
+ def unplug(self, instance, network, mapping):
+ """No action needed. Libvirt takes care of cleanup"""
+ pass