From f6bddbe4161bfb6ff02a807d5455c018c98d607d Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Wed, 7 Mar 2012 08:39:07 -0500 Subject: Introduce a set of classes for storing libvirt guest configuration With the current Cheetah template based approach to XML generation, there is never any canonical representation of the guest configuration. When generating XML, a hash is filled in with parameters to be used to generate XML, an even some pre-built XML snippets. When reading XML, typically XPath is used, but sometimes manual DOM traversal is used. This change introduces a set of classes for explicitly representing the guest configuration. They are intended to allow the guest config to be read/written using normal programatic APIs, avoiding the need for any code to know about XML. This first impl though, only supports writing of XML. The code to parse an existing XML doc is not yet written. The class hierarchy is as follows LibvirtConfigObject | + LibvirtConfigGuest + LibvirtConfigGuestDevice | +- LibvirtConfigGuestDisk +- LibvirtConfigGuestFilesys +- LibvirtConfigGuestInterface +- LibvirtConfigGuestInput +- LibvirtConfigGuestGraphics +- LibvirtConfigGuestChar | +- LibvirtConfigGuestSerial +- LibvirtConfigGuestConsole The base LibvirtConfigObject class provides some generic boilerplate code. Subclasses need to override the "format_dom" method for generating XML DOMS, and "parse_dom" for reading XML DOMs. The conversion from DOM <-> XML String is handled by the base class. The DOMs are based on the lxml.etree.Element class. * nova/tests/test_libvirt_config.py: Test cases for XML formatting of all config object classes * nova/virt/libvirt/config.py: Config object classes for libvirt guest XML schema blueprint libvirt-xml-config-apis Change-Id: I0474b6640b47ad0e5bb74503a5ff99a8a41bcdc4 Signed-off-by: Daniel P. Berrange --- nova/tests/test_libvirt_config.py | 363 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 363 insertions(+) create mode 100644 nova/tests/test_libvirt_config.py (limited to 'nova/tests') diff --git a/nova/tests/test_libvirt_config.py b/nova/tests/test_libvirt_config.py new file mode 100644 index 000000000..186fb5a18 --- /dev/null +++ b/nova/tests/test_libvirt_config.py @@ -0,0 +1,363 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright (C) 2012 Red Hat, 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 lxml import etree +from lxml import objectify + +from nova import test + +from nova.virt.libvirt import config + + +class LibvirtConfigBaseTest(test.TestCase): + def assertXmlEqual(self, expectedXmlstr, actualXmlstr): + expected = etree.tostring(objectify.fromstring(expectedXmlstr)) + actual = etree.tostring(objectify.fromstring(actualXmlstr)) + self.assertEqual(expected, actual) + + +class LibvirtConfigTest(LibvirtConfigBaseTest): + + def test_config_plain(self): + obj = config.LibvirtConfigObject(root_name="demo") + xml = obj.to_xml() + + self.assertXmlEqual(xml, "") + + def test_config_ns(self): + obj = config.LibvirtConfigObject(root_name="demo", ns_prefix="foo", + ns_uri="http://example.com/foo") + xml = obj.to_xml() + + self.assertXmlEqual(xml, """ + """) + + def test_config_text(self): + obj = config.LibvirtConfigObject(root_name="demo") + root = obj.format_dom() + root.append(obj._text_node("foo", "bar")) + + xml = etree.tostring(root) + self.assertXmlEqual(xml, "bar") + + +class LibvirtConfigGuestDiskTest(LibvirtConfigBaseTest): + + def test_config_file(self): + obj = config.LibvirtConfigGuestDisk() + obj.source_type = "file" + obj.source_path = "/tmp/hello" + obj.target_dev = "/dev/hda" + obj.target_bus = "ide" + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + + + """) + + def test_config_block(self): + obj = config.LibvirtConfigGuestDisk() + obj.source_type = "block" + obj.source_path = "/tmp/hello" + obj.source_device = "cdrom" + obj.driver_name = "qemu" + obj.target_dev = "/dev/hdc" + obj.target_bus = "ide" + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + + + + """) + + def test_config_network(self): + obj = config.LibvirtConfigGuestDisk() + obj.source_type = "network" + obj.source_protocol = "iscsi" + obj.source_host = "foo.bar.com" + obj.driver_name = "qemu" + obj.driver_format = "qcow2" + obj.target_dev = "/dev/hda" + obj.target_bus = "ide" + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + + + + """) + + +class LibvirtConfigGuestFilesysTest(LibvirtConfigBaseTest): + + def test_config_mount(self): + obj = config.LibvirtConfigGuestFilesys() + obj.source_type = "mount" + obj.source_dir = "/tmp/hello" + obj.target_dir = "/mnt" + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + + + """) + + +class LibvirtConfigGuestInputTest(LibvirtConfigBaseTest): + + def test_config_tablet(self): + obj = config.LibvirtConfigGuestInput() + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + """) + + +class LibvirtConfigGuestGraphicsTest(LibvirtConfigBaseTest): + + def test_config_graphics(self): + obj = config.LibvirtConfigGuestGraphics() + obj.type = "vnc" + obj.autoport = True + obj.keymap = "en_US" + obj.listen = "127.0.0.1" + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + """) + + +class LibvirtConfigGuestSerialTest(LibvirtConfigBaseTest): + + def test_config_file(self): + obj = config.LibvirtConfigGuestSerial() + obj.type = "file" + obj.source_path = "/tmp/vm.log" + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + + """) + + +class LibvirtConfigGuestSerialTest(LibvirtConfigBaseTest): + def test_config_pty(self): + obj = config.LibvirtConfigGuestConsole() + obj.type = "pty" + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + """) + + +class LibvirtConfigGuestInterfaceTest(LibvirtConfigBaseTest): + def test_config_ethernet(self): + obj = config.LibvirtConfigGuestInterface() + obj.net_type = "ethernet" + obj.mac_addr = "DE:AD:BE:EF:CA:FE" + obj.model = "virtio" + obj.target_dev = "vnet0" + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + + + + """) + + def test_config_bridge(self): + obj = config.LibvirtConfigGuestInterface() + obj.net_type = "bridge" + obj.source_dev = "br0" + obj.mac_addr = "DE:AD:BE:EF:CA:FE" + obj.model = "virtio" + obj.filtername = "clean-traffic" + obj.filterparams.append({"key": "IP", "value": "192.168.122.1"}) + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + + + + + + + """) + + def test_config_bridge_ovs(self): + obj = config.LibvirtConfigGuestInterface() + obj.net_type = "bridge" + obj.source_dev = "br0" + obj.mac_addr = "DE:AD:BE:EF:CA:FE" + obj.model = "virtio" + obj.vporttype = "openvswitch" + obj.vportparams.append({"key": "instanceid", "value": "foobar"}) + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + + + + + + + """) + + def test_config_8021Qbh(self): + obj = config.LibvirtConfigGuestInterface() + obj.net_type = "direct" + obj.mac_addr = "DE:AD:BE:EF:CA:FE" + obj.model = "virtio" + obj.source_dev = "eth0" + obj.vporttype = "802.1Qbh" + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + + + + + """) + + +class LibvirtConfigGuestTest(LibvirtConfigBaseTest): + + def test_config_lxc(self): + obj = config.LibvirtConfigGuest() + obj.virt_type = "lxc" + obj.memory = 1024 * 1024 * 100 + obj.vcpus = 2 + obj.name = "demo" + obj.uuid = "b38a3f43-4be2-4046-897f-b67c2f5e0147" + obj.os_type = "exe" + obj.os_init_path = "/sbin/init" + + fs = config.LibvirtConfigGuestFilesys() + fs.source_dir = "/root/lxc" + fs.target_dir = "/" + + obj.add_device(fs) + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + b38a3f43-4be2-4046-897f-b67c2f5e0147 + demo + 104857600 + 2 + + exe + /sbin/init + + + + + + + + """) + + def test_config_xen(self): + obj = config.LibvirtConfigGuest() + obj.virt_type = "xen" + obj.memory = 1024 * 1024 * 100 + obj.vcpus = 2 + obj.name = "demo" + obj.uuid = "b38a3f43-4be2-4046-897f-b67c2f5e0147" + obj.os_type = "linux" + obj.os_kernel = "/tmp/vmlinuz" + obj.os_initrd = "/tmp/ramdisk" + obj.os_root = "root=xvda" + obj.os_cmdline = "console=xvc0" + + disk = config.LibvirtConfigGuestDisk() + disk.source_type = "file" + disk.source_path = "/tmp/img" + disk.target_dev = "/dev/xvda" + disk.target_bus = "xen" + + obj.add_device(disk) + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + b38a3f43-4be2-4046-897f-b67c2f5e0147 + demo + 104857600 + 2 + + linux + /tmp/vmlinuz + /tmp/ramdisk + console=xvc0 + root=xvda + + + + + + + + """) + + def test_config_kvm(self): + obj = config.LibvirtConfigGuest() + obj.virt_type = "kvm" + obj.memory = 1024 * 1024 * 100 + obj.vcpus = 2 + obj.name = "demo" + obj.uuid = "b38a3f43-4be2-4046-897f-b67c2f5e0147" + obj.os_type = "linux" + obj.os_boot_dev = "hd" + + disk = config.LibvirtConfigGuestDisk() + disk.source_type = "file" + disk.source_path = "/tmp/img" + disk.target_dev = "/dev/vda" + disk.target_bus = "virtio" + + obj.add_device(disk) + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + b38a3f43-4be2-4046-897f-b67c2f5e0147 + demo + 104857600 + 2 + + linux + + + + + + + + + """) -- cgit