mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-01-10 01:18:03 +03:00
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
This commit is contained in:
parent
cb06f7eb3d
commit
3db5cb5f06
@ -1,21 +1,20 @@
|
||||
<interface type='bond' name='test-bond-arp'>
|
||||
<start mode='onboot'/>
|
||||
<mac address='AA:AA:AA:AA:AA:AA'/>
|
||||
<mtu size='1501'/>
|
||||
<protocol family='ipv4'>
|
||||
</protocol>
|
||||
<interface type="bond" name="test-bond-arp">
|
||||
<bond>
|
||||
<arpmon interval='100' target='192.168.100.200' validate='backup'/>
|
||||
<interface type='ethernet' name='eth0'>
|
||||
<start mode='none'/>
|
||||
<mac address='00:FF:EE:FF:EE:FF'/>
|
||||
<interface type="ethernet" name="eth0">
|
||||
<start mode="none"/>
|
||||
<mac address="00:FF:EE:FF:EE:FF"/>
|
||||
</interface>
|
||||
<interface type='ethernet' name='eth1'>
|
||||
<mac address='00:FF:EE:FF:EE:AB'/>
|
||||
<interface type="ethernet" name="eth1">
|
||||
<mac address="00:FF:EE:FF:EE:AB"/>
|
||||
</interface>
|
||||
<interface type='ethernet' name='eth2'>
|
||||
<start mode='hotplug'/>
|
||||
<mac address='00:FF:EE:FF:EE:CD'/>
|
||||
<interface type="ethernet" name="eth2">
|
||||
<start mode="hotplug"/>
|
||||
<mac address="00:FF:EE:FF:EE:CD"/>
|
||||
</interface>
|
||||
<arpmon interval="100" target="192.168.100.200" validate="backup"/>
|
||||
</bond>
|
||||
<start mode="onboot"/>
|
||||
<mac address="AA:AA:AA:AA:AA:AA"/>
|
||||
<mtu size="1501"/>
|
||||
<protocol family="ipv4"/>
|
||||
</interface>
|
||||
|
@ -1,21 +1,20 @@
|
||||
<interface type='bond' name='test-bond-mii'>
|
||||
<start mode='onboot'/>
|
||||
<mac address='AA:AA:AA:AA:AA:AA'/>
|
||||
<mtu size='1501'/>
|
||||
<protocol family='ipv4'>
|
||||
</protocol>
|
||||
<interface type="bond" name="test-bond-mii">
|
||||
<bond>
|
||||
<miimon freq='123' downdelay='34' updelay='12' carrier='netif'/>
|
||||
<interface type='ethernet' name='eth0'>
|
||||
<start mode='none'/>
|
||||
<mac address='00:FF:EE:FF:EE:FF'/>
|
||||
<interface type="ethernet" name="eth0">
|
||||
<start mode="none"/>
|
||||
<mac address="00:FF:EE:FF:EE:FF"/>
|
||||
</interface>
|
||||
<interface type='ethernet' name='eth1'>
|
||||
<mac address='00:FF:EE:FF:EE:AB'/>
|
||||
<interface type="ethernet" name="eth1">
|
||||
<mac address="00:FF:EE:FF:EE:AB"/>
|
||||
</interface>
|
||||
<interface type='ethernet' name='eth2'>
|
||||
<start mode='hotplug'/>
|
||||
<mac address='00:FF:EE:FF:EE:CD'/>
|
||||
<interface type="ethernet" name="eth2">
|
||||
<start mode="hotplug"/>
|
||||
<mac address="00:FF:EE:FF:EE:CD"/>
|
||||
</interface>
|
||||
<miimon freq="123" downdelay="34" updelay="12" carrier="netif"/>
|
||||
</bond>
|
||||
<start mode="onboot"/>
|
||||
<mac address="AA:AA:AA:AA:AA:AA"/>
|
||||
<mtu size="1501"/>
|
||||
<protocol family="ipv4"/>
|
||||
</interface>
|
||||
|
@ -1,20 +1,19 @@
|
||||
<interface type='bond' name='test-bond'>
|
||||
<start mode='onboot'/>
|
||||
<mac address='AA:AA:AA:AA:AA:AA'/>
|
||||
<mtu size='1501'/>
|
||||
<protocol family='ipv4'>
|
||||
</protocol>
|
||||
<interface type="bond" name="test-bond">
|
||||
<bond>
|
||||
<interface type='ethernet' name='eth0'>
|
||||
<start mode='none'/>
|
||||
<mac address='00:FF:EE:FF:EE:FF'/>
|
||||
<interface type="ethernet" name="eth0">
|
||||
<start mode="none"/>
|
||||
<mac address="00:FF:EE:FF:EE:FF"/>
|
||||
</interface>
|
||||
<interface type='ethernet' name='eth1'>
|
||||
<mac address='00:FF:EE:FF:EE:AB'/>
|
||||
<interface type="ethernet" name="eth1">
|
||||
<mac address="00:FF:EE:FF:EE:AB"/>
|
||||
</interface>
|
||||
<interface type='ethernet' name='eth2'>
|
||||
<start mode='hotplug'/>
|
||||
<mac address='00:FF:EE:FF:EE:CD'/>
|
||||
<interface type="ethernet" name="eth2">
|
||||
<start mode="hotplug"/>
|
||||
<mac address="00:FF:EE:FF:EE:CD"/>
|
||||
</interface>
|
||||
</bond>
|
||||
<start mode="onboot"/>
|
||||
<mac address="AA:AA:AA:AA:AA:AA"/>
|
||||
<mtu size="1501"/>
|
||||
<protocol family="ipv4"/>
|
||||
</interface>
|
||||
|
@ -1,38 +1,38 @@
|
||||
<interface type='bridge' name='test-bridge-ip'>
|
||||
<protocol family='ipv4'>
|
||||
<dhcp peerdns='yes'/>
|
||||
<ip address='129.63.1.2'/>
|
||||
<ip address='255.255.255.0'/>
|
||||
<route gateway='1.2.3.4'/>
|
||||
</protocol>
|
||||
<protocol family='ipv6'>
|
||||
<autoconf/>
|
||||
<dhcp peerdns='yes'/>
|
||||
<ip address='fe99::215:58ff:fe6e:5' prefix='32'/>
|
||||
<ip address='fe80::215:58ff:fe6e:5' prefix='64'/>
|
||||
<route gateway='1.2.3.4'/>
|
||||
</protocol>
|
||||
<interface type="bridge" name="test-bridge-ip">
|
||||
<bridge>
|
||||
<interface type='ethernet' name='vlaneth1'>
|
||||
<interface type="ethernet" name="vlaneth1">
|
||||
</interface>
|
||||
<interface type='bond' name='bond-brbond'>
|
||||
<start mode='none'/>
|
||||
<protocol family='ipv6'>
|
||||
<ip address='3ffe:ffff:0:5::1' prefix='128'/>
|
||||
<ip address='3ffe:ffff:0:5::3' prefix='128'/>
|
||||
<ip address='3ffe:ffff:0:5::5' prefix='128'/>
|
||||
<interface type="bond" name="bond-brbond">
|
||||
<start mode="none"/>
|
||||
<protocol family="ipv6">
|
||||
<ip address="3ffe:ffff:0:5::1" prefix="128"/>
|
||||
<ip address="3ffe:ffff:0:5::3" prefix="128"/>
|
||||
<ip address="3ffe:ffff:0:5::5" prefix="128"/>
|
||||
</protocol>
|
||||
<bond mode='active-backup'>
|
||||
<miimon freq='100' updelay='10' carrier='ioctl'/>
|
||||
<interface type='ethernet' name='brbond-eth0'>
|
||||
<bond mode="active-backup">
|
||||
<miimon freq="100" updelay="10" carrier="ioctl"/>
|
||||
<interface type="ethernet" name="brbond-eth0">
|
||||
</interface>
|
||||
<interface type='ethernet' name='brbond-eth1'>
|
||||
<interface type="ethernet" name="brbond-eth1">
|
||||
</interface>
|
||||
</bond>
|
||||
</interface>
|
||||
<interface type='ethernet' name='eth0'>
|
||||
<start mode='none'/>
|
||||
<mac address='00:FF:EE:FF:EE:FF'/>
|
||||
<interface type="ethernet" name="eth0">
|
||||
<start mode="none"/>
|
||||
<mac address="00:FF:EE:FF:EE:FF"/>
|
||||
</interface>
|
||||
</bridge>
|
||||
<protocol family="ipv4">
|
||||
<dhcp peerdns="yes"/>
|
||||
<ip address="129.63.1.2"/>
|
||||
<ip address="255.255.255.0"/>
|
||||
<route gateway="1.2.3.4"/>
|
||||
</protocol>
|
||||
<protocol family="ipv6">
|
||||
<autoconf/>
|
||||
<dhcp peerdns="yes"/>
|
||||
<ip address="fe99::215:58ff:fe6e:5" prefix="32"/>
|
||||
<ip address="fe80::215:58ff:fe6e:5" prefix="64"/>
|
||||
<route gateway="1.2.3.4"/>
|
||||
</protocol>
|
||||
</interface>
|
||||
|
@ -1,25 +1,25 @@
|
||||
<interface type='bridge' name='test-bridge'>
|
||||
<bridge stp='off' delay='7'>
|
||||
<interface type='ethernet' name='vlaneth1'>
|
||||
<interface type="bridge" name="test-bridge">
|
||||
<bridge stp="off" delay="7">
|
||||
<interface type="ethernet" name="vlaneth1">
|
||||
</interface>
|
||||
<interface type='bond' name='bond-brbond'>
|
||||
<start mode='none'/>
|
||||
<protocol family='ipv6'>
|
||||
<ip address='3ffe:ffff:0:5::1' prefix='128'/>
|
||||
<ip address='3ffe:ffff:0:5::3' prefix='128'/>
|
||||
<ip address='3ffe:ffff:0:5::5' prefix='128'/>
|
||||
<interface type="bond" name="bond-brbond">
|
||||
<start mode="none"/>
|
||||
<protocol family="ipv6">
|
||||
<ip address="3ffe:ffff:0:5::1" prefix="128"/>
|
||||
<ip address="3ffe:ffff:0:5::3" prefix="128"/>
|
||||
<ip address="3ffe:ffff:0:5::5" prefix="128"/>
|
||||
</protocol>
|
||||
<bond mode='active-backup'>
|
||||
<miimon freq='100' updelay='10' carrier='ioctl'/>
|
||||
<interface type='ethernet' name='brbond-eth0'>
|
||||
<bond mode="active-backup">
|
||||
<miimon freq="100" updelay="10" carrier="ioctl"/>
|
||||
<interface type="ethernet" name="brbond-eth0">
|
||||
</interface>
|
||||
<interface type='ethernet' name='brbond-eth1'>
|
||||
<interface type="ethernet" name="brbond-eth1">
|
||||
</interface>
|
||||
</bond>
|
||||
</interface>
|
||||
<interface type='ethernet' name='eth0'>
|
||||
<start mode='none'/>
|
||||
<mac address='00:FF:EE:FF:EE:FF'/>
|
||||
<interface type="ethernet" name="eth0">
|
||||
<start mode="none"/>
|
||||
<mac address="00:FF:EE:FF:EE:FF"/>
|
||||
</interface>
|
||||
</bridge>
|
||||
</interface>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<interface type='ethernet' name='test-ethernet-copy-proto'>
|
||||
<protocol family='ipv6'>
|
||||
<dhcp/>
|
||||
</protocol>
|
||||
<interface type="ethernet" name="test-ethernet-copy-proto">
|
||||
<protocol family="ipv6">
|
||||
<dhcp/>
|
||||
</protocol>
|
||||
</interface>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<interface type='ethernet' name='test-ethernet-params'>
|
||||
<start mode='hotplug'/>
|
||||
<mtu size='1234'/>
|
||||
<interface type="ethernet" name="test-ethernet-params">
|
||||
<start mode="hotplug"/>
|
||||
<mtu size="1234"/>
|
||||
</interface>
|
||||
|
@ -1,2 +1 @@
|
||||
<interface type='ethernet' name='test-ethernet'>
|
||||
</interface>
|
||||
<interface type="ethernet" name="test-ethernet"/>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<interface type='vlan' name='test-vlan'>
|
||||
<vlan tag='123'>
|
||||
<interface name='eth2'/>
|
||||
<interface type="vlan" name="test-vlan">
|
||||
<vlan tag="123">
|
||||
<interface name="eth2"/>
|
||||
</vlan>
|
||||
</interface>
|
||||
|
@ -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 = (" <protocol family='ipv6'>\n"
|
||||
" <dhcp/>\n"
|
||||
" </protocol>\n")
|
||||
obj.protocol_xml = protoxml
|
||||
proto = InterfaceProtocol(conn, parsexml=protoxml)
|
||||
obj.add_protocol(proto)
|
||||
|
||||
self.define_xml(obj)
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
20
tests/xmlparse-xml/interface-test-bond-arp-out.xml
Normal file
20
tests/xmlparse-xml/interface-test-bond-arp-out.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<interface type="bond" name="test-bond-arp">
|
||||
<bond mode="active-backup">
|
||||
<interface type="ethernet" name="eth0">
|
||||
<start mode="none"/>
|
||||
<mac address="00:FF:EE:FF:EE:FF"/>
|
||||
</interface>
|
||||
<interface type="ethernet" name="eth1">
|
||||
<mac address="00:FF:EE:FF:EE:AB"/>
|
||||
</interface>
|
||||
<interface type="ethernet" name="eth2">
|
||||
<start mode="hotplug"/>
|
||||
<mac address="00:FF:EE:FF:EE:CD"/>
|
||||
</interface>
|
||||
<arpmon interval="234" target="1.2.3.4" validate="active"/>
|
||||
</bond>
|
||||
<start mode="hotplug"/>
|
||||
<mac address="AA:AA:AA:11:AA:AA"/>
|
||||
<mtu size="1234"/>
|
||||
<protocol family="ipv4"/>
|
||||
</interface>
|
20
tests/xmlparse-xml/interface-test-bond-mii-out.xml
Normal file
20
tests/xmlparse-xml/interface-test-bond-mii-out.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<interface type="bond" name="test-bond-mii">
|
||||
<bond>
|
||||
<interface type="ethernet" name="eth0">
|
||||
<start mode="none"/>
|
||||
<mac address="00:FF:EE:FF:EE:FF"/>
|
||||
</interface>
|
||||
<interface type="ethernet" name="eth1">
|
||||
<mac address="00:FF:EE:FF:EE:AB"/>
|
||||
</interface>
|
||||
<interface type="ethernet" name="eth2">
|
||||
<start mode="hotplug"/>
|
||||
<mac address="00:FF:EE:FF:EE:CD"/>
|
||||
</interface>
|
||||
<miimon freq="111" downdelay="22" updelay="33" carrier="ioctl"/>
|
||||
</bond>
|
||||
<start mode="onboot"/>
|
||||
<mac address="AA:AA:AA:AA:AA:AA"/>
|
||||
<mtu size="1501"/>
|
||||
<protocol family="ipv4"/>
|
||||
</interface>
|
21
tests/xmlparse-xml/interface-test-bridge-ip-out.xml
Normal file
21
tests/xmlparse-xml/interface-test-bridge-ip-out.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<interface type="bridge" name="foo-new">
|
||||
<bridge stp="on" delay="2">
|
||||
<interface type="ethernet" name="vlaneth1">
|
||||
</interface>
|
||||
<interface type="ethernet" name="eth0">
|
||||
<start mode="none"/>
|
||||
<mac address="00:FF:EE:FF:EE:FF"/>
|
||||
</interface>
|
||||
</bridge>
|
||||
<protocol family="ipv4">
|
||||
<dhcp peerdns="no"/>
|
||||
<ip address="129.63.1.2"/>
|
||||
<ip address="255.255.255.0"/>
|
||||
<route gateway="5.5.5.5"/>
|
||||
</protocol>
|
||||
<protocol family="ipv6">
|
||||
<ip address="fe99::215:58ff:fe6e:5" prefix="32"/>
|
||||
<ip address="foobar" prefix="38"/>
|
||||
<route gateway="1.2.3.4"/>
|
||||
</protocol>
|
||||
</interface>
|
5
tests/xmlparse-xml/interface-test-vlan-out.xml
Normal file
5
tests/xmlparse-xml/interface-test-vlan-out.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<interface type="vlan" name="test-vlan">
|
||||
<vlan tag="456">
|
||||
<interface name="foonew"/>
|
||||
</vlan>
|
||||
</interface>
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 <description>, 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)
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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[:]
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 += "<interface type='%s' name='%s'>\n""" % (self.object_type,
|
||||
self.name)
|
||||
##################
|
||||
# General params #
|
||||
##################
|
||||
|
||||
if self.start_mode:
|
||||
xml += " <start mode='%s'/>\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 += " <mac address='%s'/>\n" % self.macaddr
|
||||
name = XMLProperty("./@name", validate_cb=_validate_name,
|
||||
doc=_("Name for the interface object."))
|
||||
|
||||
if self.mtu is not None:
|
||||
xml += " <mtu size='%s'/>\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 += "</interface>\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 = " <interface name='%s' type='ethernet'/>\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 = " <bridge"
|
||||
if self.stp is not None:
|
||||
xml += " stp='%s'" % (self.stp and "on" or "off")
|
||||
if self.delay is not None:
|
||||
xml += " delay='%s'" % str(self.delay)
|
||||
xml += ">\n"
|
||||
|
||||
xml += self._get_child_interface_xml()
|
||||
xml += " </bridge>\n"
|
||||
return xml
|
||||
|
||||
|
||||
class InterfaceBond(_InterfaceCompound):
|
||||
"""
|
||||
Class for building and installing libvirt interface bond xml
|
||||
"""
|
||||
|
||||
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"]
|
||||
|
||||
def __init__(self, conn, name):
|
||||
_InterfaceCompound.__init__(self, conn,
|
||||
Interface.INTERFACE_TYPE_BOND, name)
|
||||
|
||||
self._bond_mode = None
|
||||
self._monitor_mode = None
|
||||
|
||||
# ARP values
|
||||
self._arp_interval = None
|
||||
self._arp_target = None
|
||||
self._arp_validate_mode = None
|
||||
|
||||
# MII values
|
||||
self._mii_frequency = None
|
||||
self._mii_updelay = None
|
||||
self._mii_downdelay = None
|
||||
self._mii_carrier_mode = None
|
||||
|
||||
def _get_bond_mode(self):
|
||||
return self._bond_mode
|
||||
def _set_bond_mode(self, val):
|
||||
if val is None:
|
||||
self._bond_mode = None
|
||||
return
|
||||
|
||||
self._bond_mode = val
|
||||
bond_mode = property(_get_bond_mode, _set_bond_mode,
|
||||
doc=_("Mode of operation of the bonding device"))
|
||||
|
||||
def _get_monitor_mode(self):
|
||||
return self._monitor_mode
|
||||
def _set_monitor_mode(self, val):
|
||||
if val is None:
|
||||
self._monitor_mode = None
|
||||
return
|
||||
|
||||
self._monitor_mode = val
|
||||
monitor_mode = property(_get_monitor_mode, _set_monitor_mode,
|
||||
doc=_("Availability monitoring mode for the bond "
|
||||
"device"))
|
||||
|
||||
# ARP props
|
||||
def _get_arp_interval(self):
|
||||
return self._arp_interval
|
||||
def _set_arp_interval(self, val):
|
||||
self._arp_interval = val
|
||||
arp_interval = property(_get_arp_interval, _set_arp_interval,
|
||||
doc=_("ARP monitoring interval in milliseconds"))
|
||||
|
||||
def _get_arp_target(self):
|
||||
return self._arp_target
|
||||
def _set_arp_target(self, val):
|
||||
self._arp_target = val
|
||||
arp_target = property(_get_arp_target, _set_arp_target,
|
||||
doc=_("IP target used in ARP monitoring packets"))
|
||||
|
||||
def _get_arp_validate_mode(self):
|
||||
return self._arp_validate_mode
|
||||
def _set_arp_validate_mode(self, val):
|
||||
self._arp_validate_mode = val
|
||||
arp_validate_mode = property(_get_arp_validate_mode,
|
||||
_set_arp_validate_mode,
|
||||
doc=_("ARP monitor validation mode"))
|
||||
|
||||
# MII props
|
||||
def _get_mii_carrier_mode(self):
|
||||
return self._mii_carrier_mode
|
||||
def _set_mii_carrier_mode(self, val):
|
||||
self._mii_carrier_mode = val
|
||||
mii_carrier_mode = property(_get_mii_carrier_mode, _set_mii_carrier_mode,
|
||||
doc=_("MII monitoring method."))
|
||||
|
||||
def _get_mii_frequency(self):
|
||||
return self._mii_frequency
|
||||
def _set_mii_frequency(self, val):
|
||||
self._mii_frequency = val
|
||||
mii_frequency = property(_get_mii_frequency, _set_mii_frequency,
|
||||
doc=_("MII monitoring interval in milliseconds"))
|
||||
|
||||
def _get_mii_updelay(self):
|
||||
return self._mii_updelay
|
||||
def _set_mii_updelay(self, val):
|
||||
self._mii_updelay = val
|
||||
mii_updelay = property(_get_mii_updelay, _set_mii_updelay,
|
||||
doc=_("Time in milliseconds to wait before "
|
||||
"enabling a slave after link recovery "))
|
||||
|
||||
def _get_mii_downdelay(self):
|
||||
return self._mii_downdelay
|
||||
def _set_mii_downdelay(self, val):
|
||||
self._mii_downdelay = val
|
||||
mii_downdelay = property(_get_mii_downdelay, _set_mii_downdelay,
|
||||
doc=_("Time in milliseconds to wait before "
|
||||
"disabling a slave after link failure"))
|
||||
|
||||
|
||||
|
||||
# XML Building methods
|
||||
def _get_monitor_xml(self):
|
||||
mode_xml = ""
|
||||
if self.monitor_mode == self.INTERFACE_BOND_MONITOR_MODE_ARP:
|
||||
mode_xml = " <arpmon"
|
||||
|
||||
if self.arp_interval is not None:
|
||||
mode_xml += " interval='%s'" % str(self.arp_interval)
|
||||
if self.arp_target is not None:
|
||||
mode_xml += " target='%s'" % str(self.arp_target)
|
||||
if self.arp_validate_mode is not None:
|
||||
mode_xml += " validate='%s'" % str(self.arp_validate_mode)
|
||||
|
||||
mode_xml += "/>\n"
|
||||
|
||||
elif self.monitor_mode == self.INTERFACE_BOND_MONITOR_MODE_MII:
|
||||
mode_xml = " <miimon"
|
||||
|
||||
if self.mii_frequency is not None:
|
||||
mode_xml += " freq='%s'" % str(self.mii_frequency)
|
||||
if self.mii_downdelay is not None:
|
||||
mode_xml += " downdelay='%s'" % str(self.mii_downdelay)
|
||||
if self.mii_updelay is not None:
|
||||
mode_xml += " updelay='%s'" % str(self.mii_updelay)
|
||||
if self.mii_carrier_mode is not None:
|
||||
mode_xml += " carrier='%s'" % str(self.mii_carrier_mode)
|
||||
|
||||
mode_xml += "/>\n"
|
||||
|
||||
return mode_xml
|
||||
|
||||
def _get_interface_xml(self):
|
||||
xml = ""
|
||||
|
||||
xml += " <bond"
|
||||
if self.bond_mode:
|
||||
xml += " mode='%s'" % self.bond_mode
|
||||
xml += ">\n"
|
||||
|
||||
xml += self._get_monitor_xml()
|
||||
xml += self._get_child_interface_xml()
|
||||
|
||||
xml += " </bond>\n"
|
||||
return xml
|
||||
|
||||
|
||||
class InterfaceEthernet(Interface):
|
||||
"""
|
||||
Class for building and installing libvirt interface ethernet xml
|
||||
"""
|
||||
|
||||
def __init__(self, conn, name):
|
||||
Interface.__init__(self, conn,
|
||||
Interface.INTERFACE_TYPE_ETHERNET, name)
|
||||
|
||||
def _get_interface_xml(self):
|
||||
# No ethernet specific XML
|
||||
return ""
|
||||
|
||||
|
||||
class InterfaceVLAN(Interface):
|
||||
"""
|
||||
Class for building and installing libvirt interface vlan xml
|
||||
"""
|
||||
|
||||
def __init__(self, conn, name):
|
||||
Interface.__init__(self, conn,
|
||||
Interface.INTERFACE_TYPE_VLAN, name)
|
||||
|
||||
self._tag = None
|
||||
self._parent_interface = None
|
||||
|
||||
def _get_tag(self):
|
||||
return self._tag
|
||||
def _set_tag(self, val):
|
||||
self._tag = val
|
||||
tag = property(_get_tag, _set_tag,
|
||||
doc=_("VLAN device tag number"))
|
||||
|
||||
def _get_parent_interface(self):
|
||||
return self._parent_interface
|
||||
def _set_parent_interface(self, val):
|
||||
if (type(val) is not str and
|
||||
not isinstance(val, libvirt.virInterface)):
|
||||
raise ValueError("VLAN parent interface must be a virInterface "
|
||||
"instance or string, not '%s'" % val)
|
||||
self._parent_interface = val
|
||||
parent_interface = property(_get_parent_interface,
|
||||
_set_parent_interface,
|
||||
doc=_("Parent interface to create VLAN on"))
|
||||
|
||||
def _get_interface_xml(self):
|
||||
if self.tag is None or self.parent_interface is None:
|
||||
raise ValueError(_("Tag and parent interface are required."))
|
||||
|
||||
if type(self.parent_interface) is str:
|
||||
name = self.parent_interface
|
||||
else:
|
||||
name = self.parent_interface.name()
|
||||
|
||||
xml = " <vlan tag='%s'>\n" % self.tag
|
||||
xml += " <interface name='%s'/>\n" % name
|
||||
xml += " </vlan>\n"
|
||||
|
||||
return xml
|
||||
|
||||
|
||||
class InterfaceProtocol(object):
|
||||
|
||||
INTERFACE_PROTOCOL_FAMILY_IPV4 = "ipv4"
|
||||
INTERFACE_PROTOCOL_FAMILY_IPV6 = "ipv6"
|
||||
INTERFACE_PROTOCOL_FAMILIES = [INTERFACE_PROTOCOL_FAMILY_IPV4,
|
||||
INTERFACE_PROTOCOL_FAMILY_IPV6]
|
||||
|
||||
@staticmethod
|
||||
def protocol_class_for_family(family):
|
||||
if family not in InterfaceProtocol.INTERFACE_PROTOCOL_FAMILIES:
|
||||
raise ValueError("Unknown interface protocol family '%s'" %
|
||||
family)
|
||||
|
||||
if family == InterfaceProtocol.INTERFACE_PROTOCOL_FAMILY_IPV4:
|
||||
return InterfaceProtocolIPv4
|
||||
elif family == InterfaceProtocol.INTERFACE_PROTOCOL_FAMILY_IPV6:
|
||||
return InterfaceProtocolIPv6
|
||||
|
||||
def __init__(self, family):
|
||||
if family not in InterfaceProtocol.INTERFACE_PROTOCOL_FAMILIES:
|
||||
raise ValueError("Unknown interface protocol family '%s'" %
|
||||
family)
|
||||
|
||||
self._family = family
|
||||
|
||||
def _get_family(self):
|
||||
return self._family
|
||||
family = property(_get_family)
|
||||
|
||||
def _get_protocol_xml(self):
|
||||
raise NotImplementedError("Must be implemented in subclass")
|
||||
|
||||
def get_xml_config(self):
|
||||
xml = ""
|
||||
xml += " <protocol family='%s'>\n" % self.family
|
||||
xml += self._get_protocol_xml()
|
||||
xml += " </protocol>\n"
|
||||
|
||||
return xml
|
||||
|
||||
|
||||
class InterfaceProtocolIP(InterfaceProtocol):
|
||||
|
||||
def __init__(self, family):
|
||||
InterfaceProtocol.__init__(self, family)
|
||||
|
||||
self._autoconf = False
|
||||
|
||||
self._dhcp = False
|
||||
self._dhcp_peerdns = None
|
||||
|
||||
self._ips = []
|
||||
|
||||
self._gateway = None
|
||||
|
||||
|
||||
def _get_dhcp(self):
|
||||
return self._dhcp
|
||||
def _set_dhcp(self, val):
|
||||
self._dhcp = val
|
||||
dhcp = property(_get_dhcp, _set_dhcp,
|
||||
doc=_("Whether to enable DHCP"))
|
||||
|
||||
def _get_dhcp_peerdns(self):
|
||||
return self._dhcp_peerdns
|
||||
def _set_dhcp_peerdns(self, val):
|
||||
self._dhcp_peerdns = val
|
||||
dhcp_peerdns = property(_get_dhcp_peerdns, _set_dhcp_peerdns)
|
||||
|
||||
def _get_gateway(self):
|
||||
return self._gateway
|
||||
def _set_gateway(self, val):
|
||||
self._gateway = val
|
||||
gateway = property(_get_gateway, _set_gateway,
|
||||
doc=_("Network gateway address"))
|
||||
|
||||
def _get_ips(self):
|
||||
return self._ips
|
||||
def _set_ips(self, val):
|
||||
self._ips = val
|
||||
ips = property(_get_ips, _set_ips,
|
||||
doc=_("Static IP addresses"))
|
||||
|
||||
def _get_protocol_xml(self):
|
||||
raise NotImplementedError("Must be implemented in subclass")
|
||||
|
||||
def _get_ip_xml(self):
|
||||
xml = ""
|
||||
|
||||
if self.dhcp:
|
||||
xml += " <dhcp"
|
||||
if self.dhcp_peerdns is not None:
|
||||
xml += " peerdns='%s'" % (bool(self.dhcp_peerdns) and "yes"
|
||||
or "no")
|
||||
xml += "/>\n"
|
||||
|
||||
for ip in self.ips:
|
||||
xml += ip.get_xml_config()
|
||||
|
||||
if self.gateway:
|
||||
xml += " <route gateway='%s'/>\n" % self.gateway
|
||||
|
||||
return xml
|
||||
|
||||
|
||||
class InterfaceProtocolIPv4(InterfaceProtocolIP):
|
||||
def __init__(self):
|
||||
InterfaceProtocolIP.__init__(self, self.INTERFACE_PROTOCOL_FAMILY_IPV4)
|
||||
|
||||
def _get_protocol_xml(self):
|
||||
return self._get_ip_xml()
|
||||
|
||||
|
||||
class InterfaceProtocolIPv6(InterfaceProtocolIP):
|
||||
def __init__(self):
|
||||
InterfaceProtocolIP.__init__(self, self.INTERFACE_PROTOCOL_FAMILY_IPV6)
|
||||
|
||||
self._autoconf = False
|
||||
|
||||
def _get_autoconf(self):
|
||||
return self._autoconf
|
||||
def _set_autoconf(self, val):
|
||||
self._autoconf = bool(val)
|
||||
autoconf = property(_get_autoconf, _set_autoconf,
|
||||
doc=_("Whether to enable IPv6 autoconfiguration"))
|
||||
|
||||
def _get_protocol_xml(self):
|
||||
xml = ""
|
||||
|
||||
if self.autoconf:
|
||||
xml += " <autoconf/>\n"
|
||||
|
||||
xml += self._get_ip_xml()
|
||||
return xml
|
||||
|
||||
|
||||
class InterfaceProtocolIPAddress(object):
|
||||
def __init__(self, address, prefix=None):
|
||||
self._address = address
|
||||
self._prefix = prefix
|
||||
|
||||
def _get_prefix(self):
|
||||
return self._prefix
|
||||
def _set_prefix(self, val):
|
||||
self._prefix = val
|
||||
prefix = property(_get_prefix, _set_prefix,
|
||||
doc=_("IPv6 address prefix"))
|
||||
|
||||
def _get_address(self):
|
||||
return self._address
|
||||
def _set_address(self, val):
|
||||
self._address = val
|
||||
address = property(_get_address, _set_address,
|
||||
doc=_("IP address"))
|
||||
|
||||
def get_xml_config(self):
|
||||
xml = " <ip address='%s'" % self.address
|
||||
|
||||
if self.prefix is not None:
|
||||
xml += " prefix='%s'" % self.prefix
|
||||
|
||||
xml += "/>\n"
|
||||
return xml
|
||||
Interface.interfaces = XMLChildProperty(Interface,
|
||||
relative_xpath="./%(type)s")
|
||||
|
@ -955,7 +955,8 @@ class XMLBuilder(object):
|
||||
|
||||
if not obj._xmlstate.is_build:
|
||||
use_xpath = obj.get_root_xpath().rsplit("/", 1)[0]
|
||||
newnode = libxml2.parseDoc(xml).children
|
||||
indent = 2 * obj.get_root_xpath().count("/")
|
||||
newnode = libxml2.parseDoc(_indent(xml, indent)).children
|
||||
_build_xpath_node(self._xmlstate.xml_ctx, use_xpath, newnode)
|
||||
obj._xmlstate._parse(None, self._xmlstate.xml_node)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user