From 3db5cb5f06cfb05718aa890bda3ef655bf319592 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Mon, 9 Sep 2013 17:14:16 -0400 Subject: [PATCH] Use XMLBuilder for Interface XML So we unify parsing and building the XML. Since we already do this for vmmDomain, take the opportunity to move the shared infrastructure into vmmLibvirtObject --- tests/interface-xml/test-bond-arp.xml | 29 +- tests/interface-xml/test-bond-mii.xml | 29 +- tests/interface-xml/test-bond.xml | 27 +- tests/interface-xml/test-bridge-ip.xml | 56 +- tests/interface-xml/test-bridge.xml | 32 +- .../test-ethernet-copy-proto.xml | 8 +- tests/interface-xml/test-ethernet-params.xml | 6 +- tests/interface-xml/test-ethernet.xml | 3 +- tests/interface-xml/test-vlan.xml | 6 +- tests/interface.py | 99 ++- tests/utils.py | 15 +- .../interface-test-bond-arp-out.xml | 20 + .../interface-test-bond-mii-out.xml | 20 + .../interface-test-bridge-ip-out.xml | 21 + .../xmlparse-xml/interface-test-vlan-out.xml | 5 + tests/xmlparse.py | 97 +++ virtManager/createinterface.py | 146 ++-- virtManager/domain.py | 191 ++--- virtManager/host.py | 4 +- virtManager/interface.py | 168 +--- virtManager/libvirtobject.py | 95 ++- virtManager/snapshots.py | 12 +- virtinst/__init__.py | 2 +- virtinst/interface.py | 773 ++++-------------- virtinst/xmlbuilder.py | 3 +- 25 files changed, 743 insertions(+), 1124 deletions(-) create mode 100644 tests/xmlparse-xml/interface-test-bond-arp-out.xml create mode 100644 tests/xmlparse-xml/interface-test-bond-mii-out.xml create mode 100644 tests/xmlparse-xml/interface-test-bridge-ip-out.xml create mode 100644 tests/xmlparse-xml/interface-test-vlan-out.xml diff --git a/tests/interface-xml/test-bond-arp.xml b/tests/interface-xml/test-bond-arp.xml index a782b07c9..ec5176dba 100644 --- a/tests/interface-xml/test-bond-arp.xml +++ b/tests/interface-xml/test-bond-arp.xml @@ -1,21 +1,20 @@ - - - - - - + - - - - + + + - - + + - - - + + + + + + + + diff --git a/tests/interface-xml/test-bond-mii.xml b/tests/interface-xml/test-bond-mii.xml index bb8eca0fa..b27f72a71 100644 --- a/tests/interface-xml/test-bond-mii.xml +++ b/tests/interface-xml/test-bond-mii.xml @@ -1,21 +1,20 @@ - - - - - - + - - - - + + + - - + + - - - + + + + + + + + diff --git a/tests/interface-xml/test-bond.xml b/tests/interface-xml/test-bond.xml index 62a829226..c314c9a2f 100644 --- a/tests/interface-xml/test-bond.xml +++ b/tests/interface-xml/test-bond.xml @@ -1,20 +1,19 @@ - - - - - - + - - - + + + - - + + - - - + + + + + + + diff --git a/tests/interface-xml/test-bridge-ip.xml b/tests/interface-xml/test-bridge-ip.xml index 7998fd736..503e08720 100644 --- a/tests/interface-xml/test-bridge-ip.xml +++ b/tests/interface-xml/test-bridge-ip.xml @@ -1,38 +1,38 @@ - - - - - - - - - - - - - - + - + - - - - - - + + + + + + - - - + + + - + - - - + + + + + + + + + + + + + + + + diff --git a/tests/interface-xml/test-bridge.xml b/tests/interface-xml/test-bridge.xml index 9295881db..a95a1b02e 100644 --- a/tests/interface-xml/test-bridge.xml +++ b/tests/interface-xml/test-bridge.xml @@ -1,25 +1,25 @@ - - - + + + - - - - - - + + + + + + - - - + + + - + - - - + + + diff --git a/tests/interface-xml/test-ethernet-copy-proto.xml b/tests/interface-xml/test-ethernet-copy-proto.xml index 30aa84459..5f570e765 100644 --- a/tests/interface-xml/test-ethernet-copy-proto.xml +++ b/tests/interface-xml/test-ethernet-copy-proto.xml @@ -1,5 +1,5 @@ - - - - + + + + diff --git a/tests/interface-xml/test-ethernet-params.xml b/tests/interface-xml/test-ethernet-params.xml index dbd2116a1..ea6abac10 100644 --- a/tests/interface-xml/test-ethernet-params.xml +++ b/tests/interface-xml/test-ethernet-params.xml @@ -1,4 +1,4 @@ - - - + + + diff --git a/tests/interface-xml/test-ethernet.xml b/tests/interface-xml/test-ethernet.xml index 1732fb3dd..04718372a 100644 --- a/tests/interface-xml/test-ethernet.xml +++ b/tests/interface-xml/test-ethernet.xml @@ -1,2 +1 @@ - - + diff --git a/tests/interface-xml/test-vlan.xml b/tests/interface-xml/test-vlan.xml index c9aa86fc2..25e97400d 100644 --- a/tests/interface-xml/test-vlan.xml +++ b/tests/interface-xml/test-vlan.xml @@ -1,5 +1,5 @@ - - - + + + diff --git a/tests/interface.py b/tests/interface.py index ed3320575..cdbeb6acb 100644 --- a/tests/interface.py +++ b/tests/interface.py @@ -18,48 +18,47 @@ import os import unittest import logging -from virtinst import Interface +from virtinst import Interface, InterfaceProtocol from tests import utils conn = utils.open_testdriver() datadir = "tests/interface-xml" -vlan_iface = conn.interfaceLookupByName("vlaneth1") -bond_iface = conn.interfaceLookupByName("bond-brbond") -eth_iface1 = conn.interfaceLookupByName("eth0") -eth_iface2 = conn.interfaceLookupByName("eth1") -eth_iface3 = conn.interfaceLookupByName("eth2") -br_iface = conn.interfaceLookupByName("brempty") + +def _m(_n): + xml = conn.interfaceLookupByName(_n).XMLDesc(0) + return Interface(conn, parsexml=xml) class TestInterfaces(unittest.TestCase): - - def setUp(self): - pass - def build_interface(self, interface_type, name): - iclass = Interface.Interface.interface_class_for_type(interface_type) - iobj = iclass(conn, name) + iobj = Interface(conn) + iobj.type = interface_type + iobj.name = name return iobj def set_general_params(self, iface_obj): iface_obj.mtu = 1501 iface_obj.macaddr = "AA:AA:AA:AA:AA:AA" - iface_obj.start_mode = Interface.Interface.INTERFACE_START_MODE_ONBOOT - iface_obj.protocols = [Interface.InterfaceProtocolIPv4()] + iface_obj.start_mode = Interface.INTERFACE_START_MODE_ONBOOT + proto = InterfaceProtocol(conn) + proto.family = InterfaceProtocol.INTERFACE_PROTOCOL_FAMILY_IPV4 + iface_obj.add_protocol(proto) def add_child_interfaces(self, iface_obj): - if iface_obj.object_type == Interface.Interface.INTERFACE_TYPE_BRIDGE: - iface_obj.interfaces.append(vlan_iface) - iface_obj.interfaces.append(bond_iface) - iface_obj.interfaces.append(eth_iface1) - elif iface_obj.object_type == Interface.Interface.INTERFACE_TYPE_BOND: - iface_obj.interfaces.append(eth_iface1) - iface_obj.interfaces.append(eth_iface2) - iface_obj.interfaces.append(eth_iface3) + if iface_obj.type == Interface.INTERFACE_TYPE_BRIDGE: + iface_obj.add_interface(_m("vlaneth1")) + iface_obj.add_interface(_m("bond-brbond")) + iface_obj.add_interface(_m("eth0")) + elif iface_obj.type == Interface.INTERFACE_TYPE_BOND: + iface_obj.add_interface(_m("eth0")) + iface_obj.add_interface(_m("eth1")) + iface_obj.add_interface(_m("eth2")) def define_xml(self, obj, compare=True): + obj.validate() + xml = obj.get_xml_config() logging.debug("Defining interface XML:\n%s", xml) @@ -77,7 +76,7 @@ class TestInterfaces(unittest.TestCase): # Bridge tests def testBridgeInterface(self): filename = "bridge" - obj = self.build_interface(Interface.Interface.INTERFACE_TYPE_BRIDGE, + obj = self.build_interface(Interface.INTERFACE_TYPE_BRIDGE, "test-%s" % filename) self.add_child_interfaces(obj) @@ -88,44 +87,39 @@ class TestInterfaces(unittest.TestCase): def testBridgeInterfaceIP(self): filename = "bridge-ip" - obj = self.build_interface(Interface.Interface.INTERFACE_TYPE_BRIDGE, + obj = self.build_interface(Interface.INTERFACE_TYPE_BRIDGE, "test-%s" % filename) self.add_child_interfaces(obj) # IPv4 proto - iface_ip1 = Interface.InterfaceProtocolIPAddress("129.63.1.2") - iface_ip2 = Interface.InterfaceProtocolIPAddress("255.255.255.0") - iface_proto1 = Interface.InterfaceProtocol.protocol_class_for_family( - Interface.InterfaceProtocol.INTERFACE_PROTOCOL_FAMILY_IPV4)() - iface_proto1.ips = [iface_ip1, iface_ip2] + iface_proto1 = InterfaceProtocol(conn) + iface_proto1.family = InterfaceProtocol.INTERFACE_PROTOCOL_FAMILY_IPV4 + iface_proto1.add_ip("129.63.1.2") + iface_proto1.add_ip("255.255.255.0") iface_proto1.gateway = "1.2.3.4" iface_proto1.dhcp = True iface_proto1.dhcp_peerdns = True # IPv6 proto - iface_ip3 = Interface.InterfaceProtocolIPAddress( - "fe99::215:58ff:fe6e:5", - prefix="32") - iface_ip4 = Interface.InterfaceProtocolIPAddress( - "fe80::215:58ff:fe6e:5", - prefix="64") - iface_proto2 = Interface.InterfaceProtocol.protocol_class_for_family( - Interface.InterfaceProtocol.INTERFACE_PROTOCOL_FAMILY_IPV6)() + iface_proto2 = InterfaceProtocol(conn) + iface_proto2.family = InterfaceProtocol.INTERFACE_PROTOCOL_FAMILY_IPV6 - iface_proto2.ips = [iface_ip3, iface_ip4] + iface_proto2.add_ip("fe99::215:58ff:fe6e:5", prefix="32") + iface_proto2.add_ip("fe80::215:58ff:fe6e:5", prefix="64") iface_proto2.gateway = "1.2.3.4" iface_proto2.dhcp = True iface_proto2.dhcp_peerdns = True iface_proto2.autoconf = True - obj.protocols = [iface_proto1, iface_proto2] + obj.add_protocol(iface_proto1) + obj.add_protocol(iface_proto2) self.define_xml(obj) # Bond tests def testBondInterface(self): filename = "bond" - obj = self.build_interface(Interface.Interface.INTERFACE_TYPE_BOND, + obj = self.build_interface(Interface.INTERFACE_TYPE_BOND, "test-%s" % filename) self.add_child_interfaces(obj) self.set_general_params(obj) @@ -134,12 +128,11 @@ class TestInterfaces(unittest.TestCase): def testBondInterfaceARP(self): filename = "bond-arp" - obj = self.build_interface(Interface.Interface.INTERFACE_TYPE_BOND, + obj = self.build_interface(Interface.INTERFACE_TYPE_BOND, "test-%s" % filename) self.add_child_interfaces(obj) self.set_general_params(obj) - obj.monitor_mode = "arpmon" obj.arp_interval = 100 obj.arp_target = "192.168.100.200" obj.arp_validate_mode = "backup" @@ -148,12 +141,11 @@ class TestInterfaces(unittest.TestCase): def testBondInterfaceMII(self): filename = "bond-mii" - obj = self.build_interface(Interface.Interface.INTERFACE_TYPE_BOND, + obj = self.build_interface(Interface.INTERFACE_TYPE_BOND, "test-%s" % filename) self.add_child_interfaces(obj) self.set_general_params(obj) - obj.monitor_mode = "miimon" obj.mii_frequency = "123" obj.mii_updelay = "12" obj.mii_downdelay = "34" @@ -164,34 +156,34 @@ class TestInterfaces(unittest.TestCase): # Ethernet tests def testEthernetInterface(self): filename = "ethernet" - obj = self.build_interface(Interface.Interface.INTERFACE_TYPE_ETHERNET, + obj = self.build_interface(Interface.INTERFACE_TYPE_ETHERNET, "test-%s" % filename) self.define_xml(obj) def testEthernetManyParam(self): filename = "ethernet-params" - obj = self.build_interface(Interface.Interface.INTERFACE_TYPE_ETHERNET, + obj = self.build_interface(Interface.INTERFACE_TYPE_ETHERNET, "test-%s" % filename) obj.mtu = 1234 obj.mac = "AA:BB:FF:FF:BB:AA" - obj.start_mode = Interface.Interface.INTERFACE_START_MODE_HOTPLUG + obj.start_mode = Interface.INTERFACE_START_MODE_HOTPLUG self.define_xml(obj) # VLAN tests def testVLANInterface(self): filename = "vlan" - obj = self.build_interface(Interface.Interface.INTERFACE_TYPE_VLAN, + obj = self.build_interface(Interface.INTERFACE_TYPE_VLAN, "test-%s" % filename) obj.tag = "123" - obj.parent_interface = eth_iface3 + obj.parent_interface = "eth2" self.define_xml(obj) def testVLANInterfaceBusted(self): - obj = self.build_interface(Interface.Interface.INTERFACE_TYPE_VLAN, + obj = self.build_interface(Interface.INTERFACE_TYPE_VLAN, "vlan1") try: @@ -205,13 +197,14 @@ class TestInterfaces(unittest.TestCase): # protocol_xml test def testEthernetProtocolInterface(self): filename = "ethernet-copy-proto" - obj = self.build_interface(Interface.Interface.INTERFACE_TYPE_ETHERNET, + obj = self.build_interface(Interface.INTERFACE_TYPE_ETHERNET, "test-%s" % filename) protoxml = (" \n" " \n" " \n") - obj.protocol_xml = protoxml + proto = InterfaceProtocol(conn, parsexml=protoxml) + obj.add_protocol(proto) self.define_xml(obj) diff --git a/tests/utils.py b/tests/utils.py index 5a00ac8da..2260e4c45 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -126,25 +126,26 @@ def sanitize_xml_for_define(xml): return xml -def test_create(testconn, xml): +def test_create(testconn, xml, define_func="defineXML"): xml = sanitize_xml_for_define(xml) try: - dom = testconn.defineXML(xml) + func = getattr(testconn, define_func) + obj = func(xml) except Exception, e: raise RuntimeError(str(e) + "\n" + xml) try: - dom.create() - dom.destroy() - dom.undefine() + obj.create() + obj.destroy() + obj.undefine() except: try: - dom.destroy() + obj.destroy() except: pass try: - dom.undefine() + obj.undefine() except: pass diff --git a/tests/xmlparse-xml/interface-test-bond-arp-out.xml b/tests/xmlparse-xml/interface-test-bond-arp-out.xml new file mode 100644 index 000000000..aee2b6bb5 --- /dev/null +++ b/tests/xmlparse-xml/interface-test-bond-arp-out.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/tests/xmlparse-xml/interface-test-bond-mii-out.xml b/tests/xmlparse-xml/interface-test-bond-mii-out.xml new file mode 100644 index 000000000..5b0a77e6a --- /dev/null +++ b/tests/xmlparse-xml/interface-test-bond-mii-out.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/tests/xmlparse-xml/interface-test-bridge-ip-out.xml b/tests/xmlparse-xml/interface-test-bridge-ip-out.xml new file mode 100644 index 000000000..d4cfba046 --- /dev/null +++ b/tests/xmlparse-xml/interface-test-bridge-ip-out.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/xmlparse-xml/interface-test-vlan-out.xml b/tests/xmlparse-xml/interface-test-vlan-out.xml new file mode 100644 index 000000000..7a873157f --- /dev/null +++ b/tests/xmlparse-xml/interface-test-vlan-out.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/xmlparse.py b/tests/xmlparse.py index 16de6779c..810582609 100644 --- a/tests/xmlparse.py +++ b/tests/xmlparse.py @@ -810,6 +810,103 @@ class XMLParseTest(unittest.TestCase): utils.diff_compare(snap.get_xml_config(), outfile) + + ################### + # Interface tests # + ################### + + def testInterfaceBridgeIP(self): + basename = "test-bridge-ip" + infile = "tests/interface-xml/%s.xml" % basename + outfile = "tests/xmlparse-xml/interface-%s-out.xml" % basename + iface = virtinst.Interface(conn, parsexml=file(infile).read()) + + self.assertEquals(len(iface.protocols), 2) + self.assertEquals(len(iface.interfaces), 3) + + check = self._make_checker(iface) + check("type", "bridge", "foo", "bridge") + check("name", "test-bridge-ip", "foo-new") + check("stp", None, True) + check("delay", None, 2) + + check = self._make_checker(iface.protocols[0]) + check("family", "ipv4", "foo", "ipv4") + check("dhcp_peerdns", True, False) + check("gateway", "1.2.3.4", "5.5.5.5") + self.assertEquals(iface.protocols[0].ips[1].address, "255.255.255.0") + + check = self._make_checker(iface.protocols[1]) + check("dhcp", True, False) + check("autoconf", True, False) + + check = self._make_checker(iface.protocols[1].ips[1]) + check("address", "fe80::215:58ff:fe6e:5", "foobar") + check("prefix", 64, 38) + + # Remove a child interface, verify it's data remains intact + child_iface = iface.interfaces[1] + iface.remove_interface(child_iface) + + check = self._make_checker(child_iface) + check("name", "bond-brbond") + self.assertEquals(len(child_iface.interfaces), 2) + + utils.diff_compare(iface.get_xml_config(), outfile) + utils.test_create(conn, iface.get_xml_config(), "interfaceDefineXML") + + def testInterfaceBondArp(self): + basename = "test-bond-arp" + infile = "tests/interface-xml/%s.xml" % basename + outfile = "tests/xmlparse-xml/interface-%s-out.xml" % basename + iface = virtinst.Interface(conn, parsexml=file(infile).read()) + + check = self._make_checker(iface) + check("start_mode", "onboot", "hotplug") + check("macaddr", "AA:AA:AA:AA:AA:AA", "AA:AA:AA:11:AA:AA") + check("mtu", 1501, 1234) + + check("bond_mode", None, "active-backup") + check("arp_interval", 100, 234) + check("arp_target", "192.168.100.200", "1.2.3.4") + check("arp_validate_mode", "backup", "active") + + utils.diff_compare(iface.get_xml_config(), outfile) + utils.test_create(conn, iface.get_xml_config(), "interfaceDefineXML") + + def testInterfaceBondMii(self): + basename = "test-bond-mii" + infile = "tests/interface-xml/%s.xml" % basename + outfile = "tests/xmlparse-xml/interface-%s-out.xml" % basename + iface = virtinst.Interface(conn, parsexml=file(infile).read()) + + check = self._make_checker(iface) + check("mii_frequency", 123, 111) + check("mii_downdelay", 34, 22) + check("mii_updelay", 12, 33) + check("mii_carrier_mode", "netif", "ioctl") + + utils.diff_compare(iface.get_xml_config(), outfile) + utils.test_create(conn, iface.get_xml_config(), "interfaceDefineXML") + + def testInterfaceVLAN(self): + basename = "test-vlan" + infile = "tests/interface-xml/%s.xml" % basename + outfile = "tests/xmlparse-xml/interface-%s-out.xml" % basename + iface = virtinst.Interface(conn, parsexml=file(infile).read()) + + check = self._make_checker(iface) + check("tag", 123, 456) + check("parent_interface", "eth2", "foonew") + + utils.diff_compare(iface.get_xml_config(), outfile) + utils.test_create(conn, iface.get_xml_config(), "interfaceDefineXML") + + + ############## + # Misc tests # + ############## + def testzzzzCheckProps(self): # pylint: disable=W0212 # Access to protected member, needed to unittest stuff diff --git a/virtManager/createinterface.py b/virtManager/createinterface.py index 40bb1fcb7..00f3202de 100644 --- a/virtManager/createinterface.py +++ b/virtManager/createinterface.py @@ -25,7 +25,7 @@ from gi.repository import Gdk import logging -from virtinst import Interface +from virtinst import Interface, InterfaceProtocol from virtManager import uihelpers from virtManager.baseclass import vmmGObjectUI @@ -188,13 +188,13 @@ class vmmCreateInterface(vmmGObjectUI): text = Gtk.CellRendererText() type_list.pack_start(text, True) type_list.add_attribute(text, 'text', 1) - type_model.append([Interface.Interface.INTERFACE_TYPE_BRIDGE, + type_model.append([Interface.INTERFACE_TYPE_BRIDGE, _("Bridge")]) - type_model.append([Interface.Interface.INTERFACE_TYPE_BOND, + type_model.append([Interface.INTERFACE_TYPE_BOND, _("Bond")]) - type_model.append([Interface.Interface.INTERFACE_TYPE_ETHERNET, + type_model.append([Interface.INTERFACE_TYPE_ETHERNET, _("Ethernet")]) - type_model.append([Interface.Interface.INTERFACE_TYPE_VLAN, + type_model.append([Interface.INTERFACE_TYPE_VLAN, _("VLAN")]) # Start mode @@ -251,7 +251,7 @@ class vmmCreateInterface(vmmGObjectUI): mode_list.pack_start(txt, True) mode_list.add_attribute(txt, "text", 0) mode_model.append([_("System default"), None]) - for m in Interface.InterfaceBond.INTERFACE_BOND_MODES: + for m in Interface.INTERFACE_BOND_MODES: mode_model.append([m, m]) mon_list = self.widget("bond-monitor-mode") @@ -261,7 +261,7 @@ class vmmCreateInterface(vmmGObjectUI): mon_list.pack_start(txt, True) mon_list.add_attribute(txt, "text", 0) mon_model.append([_("System default"), None]) - for m in Interface.InterfaceBond.INTERFACE_BOND_MONITOR_MODES: + for m in Interface.INTERFACE_BOND_MONITOR_MODES: mon_model.append([m, m]) validate_list = self.widget("arp-validate") @@ -270,7 +270,7 @@ class vmmCreateInterface(vmmGObjectUI): txt = Gtk.CellRendererText() validate_list.pack_start(txt, True) validate_list.add_attribute(txt, "text", 0) - for m in Interface.InterfaceBond.INTERFACE_BOND_MONITOR_MODE_ARP_VALIDATE_MODES: + for m in Interface.INTERFACE_BOND_MONITOR_MODE_ARP_VALIDATE_MODES: validate_model.append([m]) carrier_list = self.widget("mii-carrier") @@ -279,7 +279,7 @@ class vmmCreateInterface(vmmGObjectUI): txt = Gtk.CellRendererText() carrier_list.pack_start(txt, True) carrier_list.add_attribute(txt, "text", 0) - for m in Interface.InterfaceBond.INTERFACE_BOND_MONITOR_MODE_MII_CARRIER_TYPES: + for m in Interface.INTERFACE_BOND_MONITOR_MODE_MII_CARRIER_TYPES: carrier_model.append([m]) # IP config @@ -364,6 +364,8 @@ class vmmCreateInterface(vmmGObjectUI): self.widget("ipv6-mode").set_active(IP_NONE) self.widget("ipv6-autoconf").set_active(False) + self.widget("ipv6-gateway").set_text("") + self.widget("ipv6-address-list").get_model().clear() self.ipv6_address_selected() def populate_details_page(self): @@ -373,8 +375,8 @@ class vmmCreateInterface(vmmGObjectUI): self.widget("interface-name-entry").hide() self.widget("interface-name-label").hide() - if itype in [Interface.Interface.INTERFACE_TYPE_BRIDGE, - Interface.Interface.INTERFACE_TYPE_BOND]: + if itype in [Interface.INTERFACE_TYPE_BRIDGE, + Interface.INTERFACE_TYPE_BOND]: widget = "interface-name-entry" else: widget = "interface-name-label" @@ -385,9 +387,9 @@ class vmmCreateInterface(vmmGObjectUI): # Make sure interface type specific fields are shown type_dict = { - Interface.Interface.INTERFACE_TYPE_BRIDGE : "bridge", - Interface.Interface.INTERFACE_TYPE_BOND : "bond", - Interface.Interface.INTERFACE_TYPE_VLAN : "vlan", + Interface.INTERFACE_TYPE_BRIDGE : "bridge", + Interface.INTERFACE_TYPE_BOND : "bond", + Interface.INTERFACE_TYPE_VLAN : "vlan", } for key, value in type_dict.items(): @@ -395,10 +397,10 @@ class vmmCreateInterface(vmmGObjectUI): self.widget("%s-label" % value).set_visible(do_show) self.widget("%s-box" % value).set_visible(do_show) - if itype == Interface.Interface.INTERFACE_TYPE_BRIDGE: + if itype == Interface.INTERFACE_TYPE_BRIDGE: self.update_bridge_desc() - elif itype == Interface.Interface.INTERFACE_TYPE_BOND: + elif itype == Interface.INTERFACE_TYPE_BOND: self.update_bond_desc() # Populate device list @@ -417,9 +419,9 @@ class vmmCreateInterface(vmmGObjectUI): copy_model = copy_combo.get_model() # Only select 'copy from' option if using bridge/bond/vlan - enable_copy = (itype in [Interface.Interface.INTERFACE_TYPE_BRIDGE, - Interface.Interface.INTERFACE_TYPE_BOND, - Interface.Interface.INTERFACE_TYPE_VLAN]) + enable_copy = (itype in [Interface.INTERFACE_TYPE_BRIDGE, + Interface.INTERFACE_TYPE_BOND, + Interface.INTERFACE_TYPE_VLAN]) # Set defaults if required copy_model.clear() @@ -489,18 +491,18 @@ class vmmCreateInterface(vmmGObjectUI): model = iface_list.get_model() model.clear() - ifilter = [Interface.Interface.INTERFACE_TYPE_ETHERNET] + ifilter = [Interface.INTERFACE_TYPE_ETHERNET] msg = None - if itype == Interface.Interface.INTERFACE_TYPE_BRIDGE: - ifilter.append(Interface.Interface.INTERFACE_TYPE_VLAN) - ifilter.append(Interface.Interface.INTERFACE_TYPE_BOND) + if itype == Interface.INTERFACE_TYPE_BRIDGE: + ifilter.append(Interface.INTERFACE_TYPE_VLAN) + ifilter.append(Interface.INTERFACE_TYPE_BOND) msg = _("Choose interface(s) to bridge:") - elif itype == Interface.Interface.INTERFACE_TYPE_VLAN: + elif itype == Interface.INTERFACE_TYPE_VLAN: msg = _("Choose parent interface:") - elif itype == Interface.Interface.INTERFACE_TYPE_BOND: + elif itype == Interface.INTERFACE_TYPE_BOND: msg = _("Choose interfaces to bond:") - elif itype == Interface.Interface.INTERFACE_TYPE_ETHERNET: + elif itype == Interface.INTERFACE_TYPE_ETHERNET: msg = _("Choose an unconfigured interface:") self.widget("interface-list-text").set_text(msg) @@ -524,7 +526,7 @@ class vmmCreateInterface(vmmGObjectUI): if iface_type not in ifilter: continue - if itype == Interface.Interface.INTERFACE_TYPE_ETHERNET: + if itype == Interface.INTERFACE_TYPE_ETHERNET: if name in row_dict: del(row_dict[name]) @@ -557,22 +559,22 @@ class vmmCreateInterface(vmmGObjectUI): itype = self.get_config_interface_type() name = _("No interface selected") - if itype == Interface.Interface.INTERFACE_TYPE_BRIDGE: - name = Interface.Interface.find_free_name(self.conn.get_backend(), + if itype == Interface.INTERFACE_TYPE_BRIDGE: + name = Interface.find_free_name(self.conn.get_backend(), "br") - elif itype == Interface.Interface.INTERFACE_TYPE_BOND: - name = Interface.Interface.find_free_name(self.conn.get_backend(), + elif itype == Interface.INTERFACE_TYPE_BOND: + name = Interface.find_free_name(self.conn.get_backend(), "bond") else: ifaces = self.get_config_selected_interfaces() if len(ifaces) > 0: iface = ifaces[0][INTERFACE_ROW_NAME] - if itype == Interface.Interface.INTERFACE_TYPE_VLAN: + if itype == Interface.INTERFACE_TYPE_VLAN: tag = uihelpers.spin_get_helper(self.widget("vlan-tag")) name = "%s.%s" % (iface, int(tag)) - elif itype == Interface.Interface.INTERFACE_TYPE_ETHERNET: + elif itype == Interface.INTERFACE_TYPE_ETHERNET: name = iface return name @@ -642,8 +644,8 @@ class vmmCreateInterface(vmmGObjectUI): active = src.get_active() model = slave_list.get_model() - if itype in [Interface.Interface.INTERFACE_TYPE_ETHERNET, - Interface.Interface.INTERFACE_TYPE_VLAN]: + if itype in [Interface.INTERFACE_TYPE_ETHERNET, + Interface.INTERFACE_TYPE_VLAN]: # Deselect any selected rows for row in model: if row == model[index]: @@ -658,8 +660,8 @@ class vmmCreateInterface(vmmGObjectUI): def update_interface_name(self, ignore1=None, ignore2=None): itype = self.get_config_interface_type() - if itype not in [Interface.Interface.INTERFACE_TYPE_VLAN, - Interface.Interface.INTERFACE_TYPE_ETHERNET]: + if itype not in [Interface.INTERFACE_TYPE_VLAN, + Interface.INTERFACE_TYPE_ETHERNET]: # The rest have editable name fields, so don't overwrite return @@ -769,14 +771,13 @@ class vmmCreateInterface(vmmGObjectUI): def build_ip(addr_str): if not addr_str: - return None - + return None, None ret = addr_str.rsplit("/", 1) - ip = Interface.InterfaceProtocolIPAddress(ret[0]) + address = ret[0] + prefix = None if len(ret) > 1: - ip.prefix = ret[1] - - return ip + prefix = ret[1] + return address, prefix is_manual = self.widget("ip-do-manual").get_active() @@ -804,25 +805,28 @@ class vmmCreateInterface(vmmGObjectUI): else: # Build IPv4 Info if v4_mode != IP_NONE: - ipv4 = Interface.InterfaceProtocolIPv4() + ipv4 = InterfaceProtocol(self.conn.get_backend()) + ipv4.family = "ipv4" ipv4.dhcp = bool(v4_mode == IP_DHCP) if not ipv4.dhcp: - if v4_addr: - ipv4.ips.append(build_ip(v4_addr)) - + addr, prefix = build_ip(v4_addr) + if addr: + ipv4.add_ip(addr, prefix) if v4_gate: ipv4.gateway = v4_gate # Build IPv6 Info if v6_mode != IP_NONE: - ipv6 = Interface.InterfaceProtocolIPv6() + ipv6 = InterfaceProtocol(self.conn.get_backend()) + ipv6.family = "ipv6" ipv6.dhcp = bool(v6_mode == IP_DHCP) ipv6.autoconf = bool(v6_auto) if not ipv6.dhcp: if v6_gate: ipv6.gateway = v6_gate - if v6_addrlist: - ipv6.ips = [build_ip(i) for i in v6_addrlist] + addr, prefix = build_ip(v4_addr) + if addr: + ipv6.add_ip(addr, prefix) return [is_manual, copy_name, ipv4, ipv6, proto_xml] @@ -912,29 +916,34 @@ class vmmCreateInterface(vmmGObjectUI): name = self.get_config_interface_name() start = self.get_config_interface_startmode() ifaces = self.get_config_selected_interfaces() - iclass = Interface.Interface.interface_class_for_type(itype) if not name: return self.err.val_err(_("An interface name is required.")) - if (itype != Interface.Interface.INTERFACE_TYPE_BRIDGE and + if (itype != Interface.INTERFACE_TYPE_BRIDGE and len(ifaces) == 0): return self.err.val_err(_("An interface must be selected")) try: - iobj = iclass(self.conn.get_backend(), name) + iobj = Interface(self.conn.get_backend()) + iobj.type = itype + iobj.name = name iobj.start_mode = start check_conflict = False # Pull info from selected interfaces - if hasattr(iobj, "interfaces"): - iobj.interfaces = [x[INTERFACE_ROW_KEY] for x in ifaces] + if (itype == Interface.INTERFACE_TYPE_BRIDGE or + itype == Interface.INTERFACE_TYPE_BOND): + for row in ifaces: + child = Interface(self.conn.get_backend(), + parsexml=row[INTERFACE_ROW_KEY].XMLDesc(0)) + iobj.add_interface(child) check_conflict = True - elif hasattr(iobj, "parent_interface"): - iobj.parent_interface = ifaces[0][INTERFACE_ROW_KEY] + elif itype == Interface.INTERFACE_TYPE_VLAN: + iobj.parent_interface = ifaces[0][INTERFACE_ROW_NAME] - elif itype == Interface.Interface.INTERFACE_TYPE_ETHERNET: + elif itype == Interface.INTERFACE_TYPE_ETHERNET: iobj.macaddr = ifaces[0][INTERFACE_ROW_MAC] # Warn about defined interfaces @@ -963,28 +972,30 @@ class vmmCreateInterface(vmmGObjectUI): ipv6, proto_xml) = self.get_config_ip_info() if is_manual: - protos = [] if ipv4: - protos.append(ipv4) + iobj.add_protocol(ipv4) if ipv6: - protos.append(ipv6) - iobj.protocols = protos + iobj.add_protocol(ipv6) else: - iobj.protocol_xml = proto_xml + for proto in proto_xml: + iobj.add_protocol(InterfaceProtocol( + self.conn.get_backend(), + parsexml=proto.get_xml_config())) - if itype == Interface.Interface.INTERFACE_TYPE_BRIDGE: + if itype == Interface.INTERFACE_TYPE_BRIDGE: ret = self.validate_bridge(iobj, ifaces) - elif itype == Interface.Interface.INTERFACE_TYPE_BOND: + elif itype == Interface.INTERFACE_TYPE_BOND: ret = self.validate_bond(iobj, ifaces) - elif itype == Interface.Interface.INTERFACE_TYPE_VLAN: + elif itype == Interface.INTERFACE_TYPE_VLAN: ret = self.validate_vlan(iobj, ifaces) - elif itype == Interface.Interface.INTERFACE_TYPE_ETHERNET: + elif itype == Interface.INTERFACE_TYPE_ETHERNET: ret = self.validate_ethernet(iobj, ifaces) if not ret: return ret iobj.get_xml_config() + iobj.validate() self.interface = iobj except Exception, e: @@ -1030,7 +1041,6 @@ class vmmCreateInterface(vmmGObjectUI): mii_down = self.widget("mii-downdelay").get_value() iobj.bond_mode = mode - iobj.monitor_mode = mon if not mon: # No monitor params, just return diff --git a/virtManager/domain.py b/virtManager/domain.py index cf2825d55..ecbe8ed58 100644 --- a/virtManager/domain.py +++ b/virtManager/domain.py @@ -144,22 +144,19 @@ class vmmDomainSnapshot(vmmLibvirtObject): Class wrapping a virDomainSnapshot object """ def __init__(self, conn, backend): - vmmLibvirtObject.__init__(self, conn, backend, backend.getName()) + vmmLibvirtObject.__init__(self, conn, backend, backend.getName(), + parseclass=virtinst.DomainSnapshot) - self._xmlbackend = None self.refresh_xml() def get_name(self): - return self.xml.name + return self.xmlobj.name def _XMLDesc(self, flags): - rawxml = self._backend.getXMLDesc(flags=flags) - self._xmlbackend = virtinst.DomainSnapshot(self.conn.get_backend(), - rawxml) - return self._xmlbackend.get_xml_config() + return self._backend.getXMLDesc(flags=flags) - def _get_xml_backend(self): - return self._xmlbackend - xml = property(_get_xml_backend) + def _get_xmlobj_prop(self): + return self._get_xmlobj() + xmlobj = property(_get_xmlobj_prop) def is_current(self): return self._backend.isCurrent() @@ -181,7 +178,8 @@ class vmmDomain(vmmLibvirtObject): } def __init__(self, conn, backend, key): - vmmLibvirtObject.__init__(self, conn, backend, key) + vmmLibvirtObject.__init__(self, conn, backend, key, + parseclass=virtinst.Guest) self.uuid = key self.cloning = False @@ -202,9 +200,6 @@ class vmmDomain(vmmLibvirtObject): self._name = None self._snapshot_list = None - self._inactive_xml_flags = 0 - self._active_xml_flags = 0 - self.lastStatus = libvirt.VIR_DOMAIN_SHUTOFF self._getvcpus_supported = None @@ -213,9 +208,6 @@ class vmmDomain(vmmLibvirtObject): self.remote_console_supported = False self.snapshots_supported = False - self._guest = None - self._guest_to_define = None - self._enable_net_poll = False self._stats_net_supported = True self._stats_net_skip = [] @@ -259,8 +251,6 @@ class vmmDomain(vmmLibvirtObject): """ Initialization to do if backed by a libvirt virDomain """ - self._reparse_xml() - self.managedsave_supported = self.conn.get_dom_managedsave_supported( self._backend) @@ -290,7 +280,6 @@ class vmmDomain(vmmLibvirtObject): self.toggle_sample_disk_io)) self.connect("status-changed", self._update_start_vcpus) - self.connect("config-changed", self._reparse_xml) self.connect("pre-startup", self._prestartup_nodedev_check) def _prestartup_nodedev_check(self, src, ret): @@ -385,21 +374,11 @@ class vmmDomain(vmmLibvirtObject): def _invalidate_xml(self): vmmLibvirtObject._invalidate_xml(self) - self._guest_to_define = None self._name = None self._id = None - def _get_guest_to_define(self): - if not self._guest_to_define: - self._guest_to_define = self._get_guest(inactive=True) - return self._guest_to_define - - def _redefine_guest(self, cb): - guest = self._get_guest_to_define() - return cb(guest) - def _redefine_device(self, cb, origdev): - defguest = self._get_guest_to_define() + defguest = self._get_xmlobj_to_define() dev = find_device(defguest, origdev) if dev: return cb(dev) @@ -407,56 +386,15 @@ class vmmDomain(vmmLibvirtObject): # If we are removing multiple dev from an active VM, a double # attempt may result in a lookup failure. If device is present # in the active XML, assume all is good. - if find_device(self._get_guest(), origdev): + if find_device(self._get_xmlobj(), origdev): logging.debug("Device in active config but not inactive config.") return raise RuntimeError(_("Could not find specified device in the " "inactive VM configuration: %s") % repr(origdev)) - def redefine_cached(self): - if not self._guest_to_define: - logging.debug("No cached XML to define, skipping.") - return - - guest = self._get_guest_to_define() - xml = guest.get_xml_config() - self._redefine_xml(xml) - - def _get_domain_xml(self, inactive=False, refresh_if_nec=True): - return vmmLibvirtObject.get_xml(self, - inactive=inactive, - refresh_if_nec=refresh_if_nec) - - def get_xml(self, inactive=False, refresh_if_nec=True): - guest = self._get_guest(inactive=inactive, - refresh_if_nec=refresh_if_nec) - return guest.get_xml_config() - - def _get_guest(self, inactive=False, refresh_if_nec=True): - xml = self._get_domain_xml(inactive, refresh_if_nec) - - if inactive: - # If inactive XML requested, always return a fresh guest even - # the current Guest is inactive XML (like when the domain is - # stopped). Callers that request inactive are basically expecting - # a new copy. - return self._build_guest(xml) - - return self._guest - get_guest_for_virtinst_func = _get_guest - - def _build_guest(self, xml): - return virtinst.Guest(self.conn.get_backend(), - parsexml=xml) - - def _reparse_xml(self, ignore=None): - try: - self._guest = self._build_guest(self._get_domain_xml()) - except libvirt.libvirtError, e: - if uihelpers.exception_is_libvirt_error(e, "VIR_ERR_NO_DOMAIN"): - return - raise + def get_guest_for_virtinst_func(self, *args, **kwargs): + return self._get_xmlobj(*args, **kwargs) ############################## @@ -465,10 +403,10 @@ class vmmDomain(vmmLibvirtObject): # Rename def define_name(self, newname): - # Do this, so that _guest_to_define has original inactive XML + # Do this, so that _xmlobj_to_define has original inactive XML self._invalidate_xml() - guest = self._get_guest_to_define() + guest = self._get_xmlobj_to_define() if guest.name == newname: return @@ -494,7 +432,7 @@ class vmmDomain(vmmLibvirtObject): """ def change(guest): guest.add_device(devobj) - ret = self._redefine_guest(change) + ret = self._redefine(change) self.redefine_cached() return ret @@ -518,7 +456,7 @@ class vmmDomain(vmmLibvirtObject): guest.remove_device(editdev) return self._redefine_device(rmdev, devobj) - ret = self._redefine_guest(change) + ret = self._redefine(change) self.redefine_cached() return ret @@ -527,11 +465,11 @@ class vmmDomain(vmmLibvirtObject): def change(guest): guest.curvcpus = int(vcpus) guest.vcpus = int(maxvcpus) - return self._redefine_guest(change) + return self._redefine(change) def define_cpuset(self, cpuset): def change(guest): guest.cpuset = cpuset - return self._redefine_guest(change) + return self._redefine(change) def define_cpu_topology(self, sockets, cores, threads): def change(guest): @@ -539,7 +477,7 @@ class vmmDomain(vmmLibvirtObject): cpu.sockets = sockets cpu.cores = cores cpu.threads = threads - return self._redefine_guest(change) + return self._redefine(change) def define_cpu(self, model, vendor, from_host, featurelist): def change(guest): if from_host: @@ -574,14 +512,14 @@ class vmmDomain(vmmLibvirtObject): for fname, fpol in featurelist: set_feature(fname, fpol) - return self._redefine_guest(change) + return self._redefine(change) # Mem define methods def define_both_mem(self, memory, maxmem): def change(guest): guest.memory = int(memory) guest.maxmemory = int(maxmem) - return self._redefine_guest(change) + return self._redefine(change) # Security define methods def define_seclabel(self, model, t, label, relabel): @@ -601,53 +539,53 @@ class vmmDomain(vmmLibvirtObject): if label: seclabel.label = label - return self._redefine_guest(change) + return self._redefine(change) # Machine config define methods def define_acpi(self, newvalue): def change(guest): guest.features["acpi"] = newvalue - return self._redefine_guest(change) + return self._redefine(change) def define_apic(self, newvalue): def change(guest): guest.features["apic"] = newvalue - return self._redefine_guest(change) + return self._redefine(change) def define_clock(self, newvalue): def change(guest): guest.clock.offset = newvalue - return self._redefine_guest(change) + return self._redefine(change) def define_machtype(self, newvalue): def change(guest): guest.os.machine = newvalue - return self._redefine_guest(change) + return self._redefine(change) def define_description(self, newvalue): def change(guest): guest.description = newvalue or None - return self._redefine_guest(change) + return self._redefine(change) # Boot define methods def set_boot_device(self, boot_list): def change(guest): guest.os.bootorder = boot_list - return self._redefine_guest(change) + return self._redefine(change) def set_boot_menu(self, newval): def change(guest): guest.os.enable_bootmenu = bool(newval) - return self._redefine_guest(change) + return self._redefine(change) def set_boot_kernel(self, kernel, initrd, dtb, args): def change(guest): guest.os.kernel = kernel or None guest.os.initrd = initrd or None guest.os.dtb = dtb or None guest.os.kernel_args = args or None - return self._redefine_guest(change) + return self._redefine(change) def set_boot_init(self, init): def change(guest): guest.os.init = init - return self._redefine_guest(change) + return self._redefine(change) # Disk define methods def define_storage_media(self, devobj, newpath): @@ -782,7 +720,7 @@ class vmmDomain(vmmLibvirtObject): if not apply_spice_defaults: return - guest = self._get_guest_to_define() + guest = self._get_xmlobj_to_define() is_spice = (newval == virtinst.VirtualGraphics.TYPE_SPICE) if is_spice: @@ -852,7 +790,7 @@ class vmmDomain(vmmLibvirtObject): def change(editdev): ignore = editdev - guest = self._get_guest_to_define() + guest = self._get_xmlobj_to_define() ctrls = guest.get_devices("controller") ctrls = [x for x in ctrls if (x.type == virtinst.VirtualController.TYPE_USB)] @@ -1016,49 +954,49 @@ class vmmDomain(vmmLibvirtObject): ######################## def is_container(self): - return self._get_guest().os.is_container() + return self._get_xmlobj().os.is_container() def is_xenpv(self): - return self._get_guest().os.is_xenpv() + return self._get_xmlobj().os.is_xenpv() def is_hvm(self): - return self._get_guest().os.is_hvm() + return self._get_xmlobj().os.is_hvm() def get_uuid(self): return self.uuid def get_abi_type(self): - return self._get_guest().os.os_type + return self._get_xmlobj().os.os_type def get_hv_type(self): - return self._get_guest().type + return self._get_xmlobj().type def get_pretty_hv_type(self): return uihelpers.pretty_hv(self.get_abi_type(), self.get_hv_type()) def get_arch(self): - return self._get_guest().os.arch + return self._get_xmlobj().os.arch def get_init(self): - return self._get_guest().os.init + return self._get_xmlobj().os.init def get_emulator(self): - return self._get_guest().emulator + return self._get_xmlobj().emulator def get_acpi(self): - return self._get_guest().features["acpi"] + return self._get_xmlobj().features["acpi"] def get_apic(self): - return self._get_guest().features["apic"] + return self._get_xmlobj().features["apic"] def get_clock(self): - return self._get_guest().clock.offset + return self._get_xmlobj().clock.offset def get_machtype(self): - return self._get_guest().os.machine + return self._get_xmlobj().os.machine def get_description(self): # Always show the inactive , let's us fake hotplug # for a field that's strictly metadata - return self._get_guest(inactive=True).description + return self._get_xmlobj(inactive=True).description def get_memory(self): - return int(self._get_guest().memory) + return int(self._get_xmlobj().memory) def maximum_memory(self): - return int(self._get_guest().maxmemory) + return int(self._get_xmlobj().maxmemory) def vcpu_count(self): - return int(self._get_guest().vcpus) + return int(self._get_xmlobj().vcpus) def vcpu_max_count(self): - guest = self._get_guest() + guest = self._get_xmlobj() has_xml_max = (guest.curvcpus != guest.vcpus) if has_xml_max or not self.is_active(): return guest.vcpus @@ -1068,22 +1006,22 @@ class vmmDomain(vmmLibvirtObject): return int(self._startup_vcpus) def vcpu_pinning(self): - return self._get_guest().cpuset or "" + return self._get_xmlobj().cpuset or "" def get_cpu_config(self): - return self._get_guest().cpu + return self._get_xmlobj().cpu def get_boot_device(self): - return self._get_guest().os.bootorder + return self._get_xmlobj().os.bootorder def get_boot_menu(self): - guest = self._get_guest() + guest = self._get_xmlobj() return bool(guest.os.enable_bootmenu) def get_boot_kernel_info(self): - guest = self._get_guest() + guest = self._get_xmlobj() return (guest.os.kernel, guest.os.initrd, guest.os.dtb, guest.os.kernel_args) def get_seclabel(self): - seclabel = self._get_guest().seclabel + seclabel = self._get_xmlobj().seclabel model = seclabel.model t = seclabel.type or "dynamic" label = seclabel.label or "" @@ -1109,7 +1047,7 @@ class vmmDomain(vmmLibvirtObject): def _build_device_list(self, device_type, refresh_if_nec=True, inactive=False): - guest = self._get_guest(refresh_if_nec=refresh_if_nec, + guest = self._get_xmlobj(refresh_if_nec=refresh_if_nec, inactive=inactive) devs = guest.get_devices(device_type) @@ -1835,9 +1773,9 @@ class vmmDomainVirtinst(vmmDomain): def _XMLDesc(self, flags): raise RuntimeError("Shouldn't be called") - def get_xml(self, inactive=False, refresh_if_nec=True): - ignore = inactive - ignore = refresh_if_nec + def get_xml(self, *args, **kwargs): + ignore = args + ignore = kwargs xml = self._backend.get_install_xml(install=False) if not self._orig_xml: @@ -1845,10 +1783,13 @@ class vmmDomainVirtinst(vmmDomain): return xml # Internal XML implementations - def _get_guest(self, inactive=False, refresh_if_nec=True): + def _get_xmlobj(self, inactive=False, refresh_if_nec=True): # Make sure XML is up2date self.get_xml() return self._backend + def _reparse_xml(self, *args, **kwargs): + ignore = args + ignore = kwargs def _define(self, newxml): ignore = newxml @@ -1873,4 +1814,4 @@ class vmmDomainVirtinst(vmmDomain): def define_name(self, newname): def change(guest): guest.name = str(newname) - return self._redefine_guest(change) + return self._redefine(change) diff --git a/virtManager/host.py b/virtManager/host.py index a4d3c5898..3a48ff051 100644 --- a/virtManager/host.py +++ b/virtManager/host.py @@ -1212,8 +1212,8 @@ class vmmHost(vmmGObjectUI): self.widget("interface-start").set_sensitive(not active) show_child = (children or - itype in [Interface.Interface.INTERFACE_TYPE_BRIDGE, - Interface.Interface.INTERFACE_TYPE_BOND]) + itype in [Interface.INTERFACE_TYPE_BRIDGE, + Interface.INTERFACE_TYPE_BOND]) self.widget("interface-child-box").set_visible(show_child) self.populate_interface_children() diff --git a/virtManager/interface.py b/virtManager/interface.py index 5b9e6fcd3..e9b021175 100644 --- a/virtManager/interface.py +++ b/virtManager/interface.py @@ -19,24 +19,20 @@ # from virtinst import Interface -from virtinst import util from virtManager.libvirtobject import vmmLibvirtObject class vmmInterface(vmmLibvirtObject): def __init__(self, conn, backend, key): - vmmLibvirtObject.__init__(self, conn, backend, key) + vmmLibvirtObject.__init__(self, conn, backend, key, + parseclass=Interface) self._name = key self._active = True - self._xml = None - self._xml_flags = None - (self._inactive_xml_flags, - self._active_xml_flags) = self.conn.get_interface_flags( - self._backend) + self._active_xml_flags) = self.conn.get_interface_flags(self._backend) self._support_isactive = None @@ -49,17 +45,6 @@ class vmmInterface(vmmLibvirtObject): def _define(self, xml): return self.conn.define_interface(xml) - def xpath(self, *args, **kwargs): - # Must use this function for ALL XML parsing - ret = util.xpath(self.get_xml(), *args, **kwargs) - if ret: - return ret - if not self.is_active(): - return ret - - # The running config did not have the info requested - return util.xpath(self.get_xml(inactive=True), *args, **kwargs) - def set_active(self, state): if state == self._active: return @@ -89,7 +74,7 @@ class vmmInterface(vmmLibvirtObject): return self._name def get_mac(self): - return self.xpath("/interface/mac/@address") + return self._get_xmlobj().macaddr def _kick_conn(self): self.conn.schedule_priority_tick(polliface=True) @@ -113,12 +98,12 @@ class vmmInterface(vmmLibvirtObject): return typ == "bridge" def get_type(self): - return self.xpath("/interface/@type") + return self._get_xmlobj().type def get_pretty_type(self): itype = self.get_type() - if itype == Interface.Interface.INTERFACE_TYPE_VLAN: + if itype == Interface.INTERFACE_TYPE_VLAN: return "VLAN" elif itype: return str(itype).capitalize() @@ -126,124 +111,57 @@ class vmmInterface(vmmLibvirtObject): return "Interface" def get_startmode(self): - return self.xpath("/interface/start/@mode") or "none" + return self._get_xmlobj().start_mode or "none" def set_startmode(self, newmode): - def set_start_xml(doc, ctx): - node = ctx.xpathEval("/interface/start[1]") - node = (node and node[0] or None) - iface_node = ctx.xpathEval("/interface")[0] - - if not node: - node = iface_node.newChild(None, "start", None) - - node.setProp("mode", newmode) - - return doc.serialize() - - self._redefine(util.xml_parse_wrapper, set_start_xml) - + def change(obj): + obj.start_mode = newmode + self._redefine(change) + self.redefine_cached() def get_slaves(self): - typ = self.get_type() - xpath = "/interface/%s/interface/@name" % typ - - def node_func(ctx): - nodes = ctx.xpathEval(xpath) - names = [x.content for x in nodes] - ret = [] - - for name in names: - type_path = ("/interface/%s/interface[@name='%s']/@type" % - (typ, name)) - nodes = ctx.xpathEval(type_path) - - ret.append((name, nodes and nodes[0].content or "Unknown")) - - return ret - - ret = self.xpath(func=node_func) - - if not ret: - return [] - return ret + return [[obj.name, obj.type or "Unknown"] for obj in + self._get_xmlobj().interfaces] def get_slave_names(self): # Returns a list of names of all enslaved interfaces return [x[0] for x in self.get_slaves()] + def _get_ip(self, iptype): + obj = self._get_xmlobj() + found = None + for protocol in obj.protocols: + if protocol.family == iptype: + found = protocol + break + if not found: + return None, [] + + ret = [] + for ip in found.ips: + ipstr = ip.address + if not ipstr: + continue + if ip.prefix: + ipstr += "/%s" % ip.prefix + ret.append(ipstr) + return found, ret + def get_ipv4(self): - base_xpath = "/interface/protocol[@family='ipv4']" - if not self.xpath(base_xpath): + proto, ips = self._get_ip("ipv4") + if proto is None: return [] - dhcp = bool(self.xpath("count(%s/dhcp)" % base_xpath)) - addr = self.xpath(base_xpath + "/ip/@address") - if addr: - prefix = self.xpath(base_xpath + "/ip[@address='%s']/@prefix" % - addr) - if prefix: - addr += "/%s" % prefix - - return [dhcp, addr] + ipstr = None + if ips: + ipstr = ips[0] + return [proto.dhcp, ipstr] def get_ipv6(self): - base_xpath = "/interface/protocol[@family='ipv6']" - if not self.xpath(base_xpath): + proto, ips = self._get_ip("ipv6") + if proto is None: return [] - - dhcp = bool(self.xpath("count(%s/dhcp)" % base_xpath)) - autoconf = bool(self.xpath("count(%s/autoconf)" % base_xpath)) - - def addr_func(ctx): - nodes = ctx.xpathEval(base_xpath + "/ip") - nodes = nodes or [] - ret = [] - - for node in nodes: - addr = node.prop("address") - pref = node.prop("prefix") - - if not addr: - continue - - if pref: - addr += "/%s" % pref - ret.append(addr) - - return ret - - ret = self.xpath(func=addr_func) - - return [dhcp, autoconf, ret] + return [proto.dhcp, proto.autoconf, ips] def get_protocol_xml(self): - def protocol(ctx): - node = ctx.xpathEval("/interface/protocol") - node = node and node[0] or None - - ret = None - if node: - ret = node.serialize() - - return ret - - ret = self.xpath(func=protocol) - if ret: - ret = " %s\n" % ret - return ret - - def _redefine(self, xml_func, *args): - """ - Helper function for altering a redefining VM xml - - @param xml_func: Function to alter the running XML. Takes the - original XML as its first argument. - @param args: Extra arguments to pass to xml_func - """ - origxml = self._xml_to_redefine() - # Sanitize origxml to be similar to what we will get back - origxml = util.xml_parse_wrapper(origxml, lambda d, c: d.serialize()) - - newxml = xml_func(origxml, *args) - self._redefine_xml(newxml) + return self._get_xmlobj().protocols[:] diff --git a/virtManager/libvirtobject.py b/virtManager/libvirtobject.py index 8cdba6db2..4497c4df1 100644 --- a/virtManager/libvirtobject.py +++ b/virtManager/libvirtobject.py @@ -47,19 +47,25 @@ class vmmLibvirtObject(vmmGObject): "stopped": (GObject.SignalFlags.RUN_FIRST, None, []), } - def __init__(self, conn, backend, key): + def __init__(self, conn, backend, key, parseclass=None): vmmGObject.__init__(self) self._conn = conn self._backend = backend self._key = key + self._parseclass = parseclass self._xml = None self._is_xml_valid = False + self._xmlobj = None + self._xmlobj_to_define = None + # These should be set by the child classes if necessary self._inactive_xml_flags = 0 self._active_xml_flags = 0 + self.connect("config-changed", self._reparse_xml) + def _cleanup(self): pass @@ -91,27 +97,13 @@ class vmmLibvirtObject(vmmGObject): # Public XML API # ################## - def get_xml(self, inactive=False, refresh_if_nec=True): + def get_xml(self, *args, **kwargs): """ - Get domain xml. If cached xml is invalid, update. - - @param inactive: Return persistent XML, not the running config. - No effect if domain is not running. Use this flag - if the XML will be used for redefining a guest - @param refresh_if_nec: Check if XML is out of date, and if so, - refresh it (default behavior). Skipping a refresh is - useful to prevent updating xml in the tick loop when - it's not that important (disk/net stats) + See _get_raw_xml for parameter docs """ - if inactive: - return self._XMLDesc(self._inactive_xml_flags) - - if self._xml is None: - self.refresh_xml() - elif refresh_if_nec and not self._is_xml_valid: - self.refresh_xml() - - return self._xml + if self._parseclass: + return self._get_xmlobj(*args, **kwargs).get_xml_config() + return self._get_raw_xml(*args, **kwargs) def refresh_xml(self, forcesignal=False): # Force an xml update. Signal 'config-changed' if domain xml has @@ -133,15 +125,74 @@ class vmmLibvirtObject(vmmGObject): def _invalidate_xml(self): # Mark cached xml as invalid self._is_xml_valid = False + self._xmlobj_to_define = None ########################## # Internal API functions # ########################## + def _get_raw_xml(self, inactive=False, refresh_if_nec=True): + """ + Get object xml. If cached xml is invalid, update. + + @param inactive: Return persistent XML, not the running config. + No effect if domain is not running. Use this flag + if the XML will be used for redefining a guest + @param refresh_if_nec: Check if XML is out of date, and if so, + refresh it (default behavior). Skipping a refresh is + useful to prevent updating xml in the tick loop when + it's not that important (disk/net stats) + """ + if inactive: + return self._XMLDesc(self._inactive_xml_flags) + + if self._xml is None: + self.refresh_xml() + elif refresh_if_nec and not self._is_xml_valid: + self.refresh_xml() + + return self._xml + + def _get_xmlobj(self, inactive=False, refresh_if_nec=True): + xml = self._get_raw_xml(inactive, refresh_if_nec) + + if inactive: + # If inactive XML requested, always return a fresh object even + # the current object is inactive XML (like when the domain is + # stopped). Callers that request inactive are basically expecting + # a new copy. + return self._build_xmlobj(xml) + + if not self._xmlobj: + self._reparse_xml() + return self._xmlobj + def _xml_to_redefine(self): return _sanitize_xml(self.get_xml(inactive=True)) + def redefine_cached(self): + if not self._xmlobj_to_define: + logging.debug("No cached XML to define, skipping.") + return + + obj = self._get_xmlobj_to_define() + xml = obj.get_xml_config() + self._redefine_xml(xml) + + def _reparse_xml(self, ignore=None): + if not self._parseclass: + return + self._xmlobj = self._build_xmlobj(self._get_raw_xml()) + + def _build_xmlobj(self, xml): + return self._parseclass(self.conn.get_backend(), parsexml=xml) + + def _get_xmlobj_to_define(self): + if not self._xmlobj_to_define: + self._xmlobj_to_define = self._get_xmlobj(inactive=True) + return self._xmlobj_to_define + def _redefine_helper(self, origxml, newxml): origxml = _sanitize_xml(origxml) newxml = _sanitize_xml(newxml) @@ -164,3 +215,7 @@ class vmmLibvirtObject(vmmGObject): def _redefine_xml(self, newxml): origxml = self._xml_to_redefine() return self._redefine_helper(origxml, newxml) + + def _redefine(self, cb): + guest = self._get_xmlobj_to_define() + return cb(guest) diff --git a/virtManager/snapshots.py b/virtManager/snapshots.py index 61884c515..14b7f6f28 100644 --- a/virtManager/snapshots.py +++ b/virtManager/snapshots.py @@ -177,7 +177,7 @@ class vmmSnapshotPage(vmmGObjectUI): do_select = None for snap in snapshots: - desc = snap.xml.description + desc = snap.xmlobj.description if not uihelpers.can_set_row_none: desc = desc or "" @@ -202,12 +202,12 @@ class vmmSnapshotPage(vmmGObjectUI): self.widget("snapshot-notebook").set_current_page(0) name = snap and snap.get_name() or "" - desc = snap and snap.xml.description or "" - state = snap and snap.xml.state or "shutoff" + desc = snap and snap.xmlobj.description or "" + state = snap and snap.xmlobj.state or "shutoff" timestamp = "" if snap: timestamp = str(datetime.datetime.fromtimestamp( - snap.xml.creationTime)) + snap.xmlobj.creationTime)) current = "" if snap and snap.is_current(): @@ -253,8 +253,8 @@ class vmmSnapshotPage(vmmGObjectUI): desc_widget = self.widget("snapshot-description") desc = desc_widget.get_buffer().get_property("text") or "" - snap.xml.description = desc - newxml = snap.xml.get_xml_config() + snap.xmlobj.description = desc + newxml = snap.xmlobj.get_xml_config() self.vm.create_snapshot(newxml, redefine=True) snap.refresh_xml() self._set_snapshot_state(snap) diff --git a/virtinst/__init__.py b/virtinst/__init__.py index 603e2dcb4..9530aa4ab 100644 --- a/virtinst/__init__.py +++ b/virtinst/__init__.py @@ -32,7 +32,7 @@ from virtinst.seclabel import Seclabel import virtinst.storage as Storage import virtinst.nodedev as NodeDeviceParser import virtinst.capabilities as CapabilitiesParser -import virtinst.interface as Interface +from virtinst.interface import Interface, InterfaceProtocol from virtinst.device import VirtualDevice from virtinst.deviceinterface import VirtualNetworkInterface diff --git a/virtinst/interface.py b/virtinst/interface.py index 8f70d01e1..0caf17a85 100644 --- a/virtinst/interface.py +++ b/virtinst/interface.py @@ -20,14 +20,55 @@ Classes for building and installing libvirt interface xml """ -import libvirt - import logging +import libvirt + from virtinst import util +from virtinst.xmlbuilder import XMLBuilder, XMLChildProperty, XMLProperty -class Interface(object): +class _IPAddress(XMLBuilder): + _XML_PROP_ORDER = ["address", "prefix"] + _XML_ROOT_NAME = "ip" + + address = XMLProperty("./@address") + prefix = XMLProperty("./@prefix", is_int=True) + + +class InterfaceProtocol(XMLBuilder): + INTERFACE_PROTOCOL_FAMILY_IPV4 = "ipv4" + INTERFACE_PROTOCOL_FAMILY_IPV6 = "ipv6" + INTERFACE_PROTOCOL_FAMILIES = [INTERFACE_PROTOCOL_FAMILY_IPV4, + INTERFACE_PROTOCOL_FAMILY_IPV6] + + _XML_ROOT_NAME = "protocol" + _XML_PROP_ORDER = ["autoconf", "dhcp", "dhcp_peerdns", "ips", "gateway"] + + family = XMLProperty("./@family") + dhcp = XMLProperty("./dhcp", is_bool=True, doc=_("Whether to enable DHCP")) + dhcp_peerdns = XMLProperty("./dhcp/@peerdns", is_yesno=True) + gateway = XMLProperty("./route/@gateway", doc=_("Network gateway address")) + autoconf = XMLProperty("./autoconf", is_bool=True, + doc=_("Whether to enable IPv6 autoconfiguration")) + + + ##################### + # IP child handling # + ##################### + + def add_ip(self, addr, prefix=None): + ip = _IPAddress(self.conn) + ip.address = addr + ip.prefix = prefix + self._add_child(ip) + def remove_ip(self, ip): + self._remove_child(ip) + ip.clear() + ips = XMLChildProperty(_IPAddress) + + +class Interface(XMLBuilder): """ Base class for building any libvirt interface object. @@ -48,22 +89,20 @@ class Interface(object): INTERFACE_START_MODE_ONBOOT, INTERFACE_START_MODE_HOTPLUG] - @staticmethod - def interface_class_for_type(interface_type): - if interface_type not in Interface.INTERFACE_TYPES: - raise ValueError("Unknown interface type '%s'" % interface_type) + INTERFACE_BOND_MODES = ["active-backup", "balance-alb", "balance-rr", + "balance-tlb", "balance-xor", "broadcast", + "802.3ad"] + + INTERFACE_BOND_MONITOR_MODE_ARP = "arpmon" + INTERFACE_BOND_MONITOR_MODE_MII = "miimon" + INTERFACE_BOND_MONITOR_MODES = [INTERFACE_BOND_MONITOR_MODE_ARP, + INTERFACE_BOND_MONITOR_MODE_MII] + + INTERFACE_BOND_MONITOR_MODE_ARP_VALIDATE_MODES = ["active", "backup", + "all"] + + INTERFACE_BOND_MONITOR_MODE_MII_CARRIER_TYPES = ["netif", "ioctl"] - if interface_type == Interface.INTERFACE_TYPE_BRIDGE: - return InterfaceBridge - elif interface_type == Interface.INTERFACE_TYPE_BOND: - return InterfaceBond - elif interface_type == Interface.INTERFACE_TYPE_ETHERNET: - return InterfaceEthernet - elif interface_type == Interface.INTERFACE_TYPE_VLAN: - return InterfaceVLAN - else: - raise ValueError("No class for interface type '%s'" % - interface_type) @staticmethod def find_free_name(conn, prefix): @@ -73,92 +112,41 @@ class Interface(object): etc. """ return util.generate_name(prefix, conn.interfaceLookupByName, sep="", - force_num=True) + force_num=True) - def __init__(self, conn, object_type, name): - """ - Initialize object parameters - """ - if object_type not in self.INTERFACE_TYPES: - raise ValueError("Unknown interface object type: %s" % - object_type) + _XML_ROOT_NAME = "interface" + _XML_PROP_ORDER = ["type", "name", "start_mode", "macaddr", "mtu", + "stp", "delay", "bond_mode", "arp_interval", + "arp_target", "arp_validate_mode", "mii_frequency", + "mii_downdelay", "mii_updelay", "mii_carrier_mode", + "tag", "parent_interface", + "protocols", "interfaces"] - self._object_type = object_type - self._conn = conn - self._name = None - self._mtu = None - self._macaddr = None - self._start_mode = None - self._protocols = [] - self._protocol_xml = None + ################## + # Child handling # + ################## - self.name = name + def add_interface(self, obj): + self._add_child(obj) + def remove_interface(self, obj): + self._remove_child(obj) + # 'interfaces' property is added outside this class, since it needs + # to reference the completed Interface class - # Initialize all optional properties - self._perms = None + def add_protocol(self, obj): + self._add_child(obj) + def remove_protocol(self, obj): + self._remove_child(obj) + protocols = XMLChildProperty(InterfaceProtocol) - ## Properties - def _get_object_type(self): - return self._object_type - object_type = property(_get_object_type) + ###################### + # Validation helpers # + ###################### - def _get_conn(self): - return self._conn - conn = property(_get_conn) - - def _get_name(self): - return self._name - def _set_name(self, val): - util.validate_name(_("Interface name"), val) - - self._check_name_collision(val) - self._name = val - name = property(_get_name, _set_name, - doc=_("Name for the interface object.")) - - def _get_mtu(self): - return self._mtu - def _set_mtu(self, val): - self._mtu = val - mtu = property(_get_mtu, _set_mtu, - doc=_("Maximum transmit size in bytes")) - - def _get_macaddr(self): - return self._macaddr - def _set_macaddr(self, val): - util.validate_macaddr(val) - self._macaddr = val - macaddr = property(_get_macaddr, _set_macaddr, - doc=_("Interface MAC address")) - - def _get_start_mode(self): - return self._start_mode - def _set_start_mode(self, val): - if val not in self.INTERFACE_START_MODES: - raise ValueError(_("Unknown start mode '%s") % val) - self._start_mode = val - start_mode = property(_get_start_mode, _set_start_mode, - doc=_("When the interface will be auto-started.")) - - def _get_protocols(self): - return self._protocols - def _set_protocols(self, val): - self._protocols = val - protocols = property(_get_protocols, _set_protocols, - doc=_("Network protocol configuration")) - - def _get_protocol_xml_attr(self): - return self._protocol_xml - def _set_protocol_xml_attr(self, val): - self._protocol_xml = val - protocol_xml = property(_get_protocol_xml_attr, _set_protocol_xml_attr, - doc="String of XML to use in place of " - "generated protocol XML. This can be " - "parsed from an existing interface for " - "example.") - - def _check_name_collision(self, name): + def _validate_name(self, name): + if name == self.name: + return try: self.conn.interfaceLookupByName(name) except libvirt.libvirtError: @@ -167,52 +155,84 @@ class Interface(object): raise ValueError(_("Name '%s' already in use by another interface.") % name) - # XML Building - def _get_protocol_xml(self): - """ - Returns IP protocol XML - """ - if self.protocol_xml is not None: - return self.protocol_xml - xml = "" - for p in self.protocols: - xml += p.get_xml_config() - return xml - - def _get_interface_xml(self): - """ - Returns the bridge/bond/... specific xml blob - """ - raise NotImplementedError("Must be implemented in subclass") - - def get_xml_config(self): - """ - Construct the xml description of the interface object - - @returns: xml description - @rtype: C{str} - """ - xml = "" + def _validate_mac(self, val): + util.validate_macaddr(val) + return val - xml += "\n""" % (self.object_type, - self.name) + ################## + # General params # + ################## - if self.start_mode: - xml += " \n" % self.start_mode + type = XMLProperty("./@type") + mtu = XMLProperty("./mtu/@size", is_int=True, + doc=_("Maximum transmit size in bytes")) + start_mode = XMLProperty("./start/@mode", + doc=_("When the interface will be auto-started.")) - if self.macaddr: - xml += " \n" % self.macaddr + name = XMLProperty("./@name", validate_cb=_validate_name, + doc=_("Name for the interface object.")) - if self.mtu is not None: - xml += " \n" % str(self.mtu) + macaddr = XMLProperty("./mac/@address", validate_cb=_validate_mac, + doc=_("Interface MAC address")) - xml += self._get_protocol_xml() - xml += self._get_interface_xml() - xml += "\n" + ################# + # Bridge params # + ################# - return xml + stp = XMLProperty("./bridge/@stp", is_onoff=True, + doc=_("Whether STP is enabled on the bridge")) + delay = XMLProperty("./bridge/@delay", + doc=_("Delay in seconds before forwarding begins when " + "joining a network.")) + + ############### + # Bond params # + ############### + + bond_mode = XMLProperty("./bond/@mode", + doc=_("Mode of operation of the bonding device")) + + arp_interval = XMLProperty("./bond/arpmon/@interval", is_int=True, + doc=_("ARP monitoring interval in " + "milliseconds")) + arp_target = XMLProperty("./bond/arpmon/@target", + doc=_("IP target used in ARP monitoring packets")) + arp_validate_mode = XMLProperty("./bond/arpmon/@validate", + doc=_("ARP monitor validation mode")) + + mii_carrier_mode = XMLProperty("./bond/miimon/@carrier", + doc=_("MII monitoring method.")) + mii_frequency = XMLProperty("./bond/miimon/@freq", is_int=True, + doc=_("MII monitoring interval in " + "milliseconds")) + mii_updelay = XMLProperty("./bond/miimon/@updelay", is_int=True, + doc=_("Time in milliseconds to wait before " + "enabling a slave after link recovery ")) + mii_downdelay = XMLProperty("./bond/miimon/@downdelay", is_int=True, + doc=_("Time in milliseconds to wait before " + "disabling a slave after link failure")) + + + ############### + # VLAN params # + ############### + + tag = XMLProperty("./vlan/@tag", is_int=True, + doc=_("VLAN device tag number")) + parent_interface = XMLProperty("./vlan/interface/@name", + doc=_("Parent interface to create VLAN on")) + + + ################## + # Build routines # + ################## + + def validate(self): + if (self.type == self.INTERFACE_TYPE_VLAN and + (self.tag is None or self.parent_interface is None)): + raise ValueError(_("VLAN Tag and parent interface are required.")) def install(self, meter=None, create=True): """ @@ -245,484 +265,5 @@ class Interface(object): return iface - -class _InterfaceCompound(Interface): - """ - Class representing an interface which can have child interfaces - """ - - def __init__(self, conn, interface_type, name): - Interface.__init__(self, conn, interface_type, name) - self._interfaces = [] - - def _get_interfaces(self): - return self._interfaces - def _set_interfaces(self, val): - if val is not None: - if type(val) is not list: - raise ValueError("Interfaces must be a list or None") - - for i in val: - if type(i) is str: - # Assume this is a plain ethernet name - continue - - if not isinstance(i, libvirt.virInterface): - raise ValueError("List members must be virInterface " - "instances, not %s" % type(i)) - - self._interfaces = val - interfaces = property(_get_interfaces, _set_interfaces) - - def _indent_xml(self, xml, indent_size): - newxml = "" - for line in xml.split("\n"): - if line: - line = (" " * indent_size) + line + "\n" - newxml += line - - return newxml - - def _get_child_interface_xml(self): - xml = "" - for i in self.interfaces: - if type(i) is str: - iface_xml = " \n" % i - else: - iface_xml = self._indent_xml(i.XMLDesc(0), 4) - - xml += iface_xml - return xml - - def _get_interface_xml(self): - raise NotImplementedError("Must be implemented in subclass") - - -class InterfaceBridge(_InterfaceCompound): - """ - Class for building and installing libvirt interface bridge xml - """ - - def __init__(self, conn, name): - _InterfaceCompound.__init__(self, conn, - Interface.INTERFACE_TYPE_BRIDGE, name) - - self._stp = None - self._delay = None - - def _get_stp(self): - return self._stp - def _set_stp(self, val): - if type(val) is not bool: - raise ValueError("STP must be a bool value") - self._stp = val - stp = property(_get_stp, _set_stp, - doc=_("Whether STP is enabled on the bridge")) - - def _get_delay(self): - return self._delay - def _set_delay(self, val): - self._delay = val - delay = property(_get_delay, _set_delay, - doc=_("Delay in seconds before forwarding begins when " - "joining a network.")) - - def _get_interface_xml(self): - xml = "