xmlbuilder: centralize adding child new child prop instances

Currently the domain CPU class has a child property like:

  siblings = XMLChildProperty(_CPUCellSibling)

If a user wants to add a new sibling, we add a convenience function:

    def add_sibling(self):
        obj = _CPUCellSibling(self.conn)
        self.add_child(obj)
        return obj

Rather than require every child property to define a similar matching
helper function, this adds infrastructure in xmlbuilder to do this
generically for every child property. Now callers can do

    obj = guest.cpu.siblings.add_new()
This commit is contained in:
Cole Robinson
2018-02-07 17:27:56 -05:00
parent 8819176ad4
commit dae8642bb1
17 changed files with 84 additions and 141 deletions

View File

@ -40,7 +40,8 @@ def createPool(conn, ptype, poolname=None, fmt=None, target_path=None,
pool_inst.type = ptype
if pool_inst.supports_property("hosts"):
pool_inst.add_host("some.random.hostname")
hostobj = pool_inst.hosts.add_new()
hostobj.name = "some.random.hostname"
if pool_inst.supports_property("source_path"):
pool_inst.source_path = source_path or "/some/source/path"
if pool_inst.supports_property("target_path"):

View File

@ -120,11 +120,11 @@ class XMLParseTest(unittest.TestCase):
check = self._make_checker(guest.clock)
check("offset", "utc", "localtime")
guest.clock.remove_timer(guest.clock.timers[0])
guest.clock.remove_child(guest.clock.timers[0])
check = self._make_checker(guest.clock.timers[0])
check("name", "pit", "rtc")
check("tickpolicy", "delay", "merge")
timer = guest.clock.add_timer()
timer = guest.clock.timers.add_new()
check = self._make_checker(timer)
check("name", None, "hpet")
check("present", None, False)
@ -180,7 +180,7 @@ class XMLParseTest(unittest.TestCase):
check("name", "x2apic")
check("policy", "force", "disable")
rmfeat = guest.cpu.features[3]
guest.cpu.remove_feature(rmfeat)
guest.cpu.remove_child(rmfeat)
self.assertEqual(rmfeat.get_xml_config(),
"""<feature name="foo" policy="bar"/>\n""")
guest.cpu.add_feature("addfeature")
@ -1196,7 +1196,9 @@ class XMLParseTest(unittest.TestCase):
check = self._make_checker(pool.hosts[2])
check("name", "ceph-mon-3.example.com")
check("port", 6789, 1000)
pool.add_host("frobber", "5555")
hostobj = pool.hosts.add_new()
hostobj.name = "frobber"
hostobj.port = "5555"
utils.diff_compare(pool.get_xml_config(), outfile)
utils.test_create(conn, pool.get_xml_config(), "storagePoolDefineXML")
@ -1287,7 +1289,7 @@ class XMLParseTest(unittest.TestCase):
check("family", "ipv6", "ipv6")
check("prefix", 64, 63)
r = net.add_route()
r = net.routes.add_new()
r.family = "ipv4"
r.address = "192.168.8.0"
r.prefix = "24"

View File

@ -756,7 +756,7 @@ class vmmCreateNetwork(vmmGObjectUI):
if net.forward.mode == "hostdev":
net.forward.managed = "yes"
pfobj = net.forward.add_pf()
pfobj = net.forward.pfs.add_new()
pfobj.dev = net.forward.dev
net.forward.dev = None
net.domain_name = None
@ -765,12 +765,12 @@ class vmmCreateNetwork(vmmGObjectUI):
if self.get_config_ipv4_enable():
ip = self.get_config_ip4()
ipobj = net.add_ip()
ipobj = net.ips.add_new()
ipobj.address = str(ip.network_address + 1)
ipobj.netmask = str(ip.netmask)
if self.get_config_dhcpv4_enable():
dhcpobj = ipobj.add_range()
dhcpobj = ipobj.ranges.add_new()
dhcpobj.start = str(
self.get_config_dhcpv4_start().network_address
)
@ -778,13 +778,13 @@ class vmmCreateNetwork(vmmGObjectUI):
if self.get_config_ipv6_enable():
ip = self.get_config_ip6()
ipobj = net.add_ip()
ipobj = net.ips.add_new()
ipobj.family = "ipv6"
ipobj.address = str(ip.network_address + 1)
ipobj.prefix = str(ip.prefixlen)
if self.get_config_dhcpv6_enable():
dhcpobj = ipobj.add_range()
dhcpobj = ipobj.ranges.add_new()
dhcpobj.start = str(
self.get_config_dhcpv6_start().network_address
)
@ -795,7 +795,7 @@ class vmmCreateNetwork(vmmGObjectUI):
netaddr = _make_ipaddr(self.get_config_routev4_network())
gwaddr = _make_ipaddr(self.get_config_routev4_gateway())
if netaddr and gwaddr:
route = net.add_route()
route = net.routes.add_new()
route.family = "ipv4"
route.address = netaddr.network_address
route.prefix = netaddr.prefixlen
@ -804,7 +804,7 @@ class vmmCreateNetwork(vmmGObjectUI):
netaddr = _make_ipaddr(self.get_config_routev6_network())
gwaddr = _make_ipaddr(self.get_config_routev6_gateway())
if netaddr and gwaddr:
route = net.add_route()
route = net.routes.add_new()
route.family = "ipv6"
route.address = netaddr.network_address
route.prefix = netaddr.prefixlen

View File

@ -514,7 +514,8 @@ class vmmCreatePool(vmmGObjectUI):
try:
self._pool.target_path = target
if host:
self._pool.add_host(host)
hostobj = self._pool.hosts.add_new()
hostobj.name = host
if source:
self._pool.source_path = source
if fmt:

View File

@ -1143,7 +1143,7 @@ class VirtCLIParser(object):
# end of the domain XML, which gives an ugly diff
clear_inst.clear(leave_stub="," in self.optstr)
def _make_find_inst_cb(self, cliarg, objpropname, objaddfn):
def _make_find_inst_cb(self, cliarg, objpropname):
"""
Create a callback used for find_inst_cb command line lookup.
@ -1152,9 +1152,6 @@ class VirtCLIParser(object):
:param objpropname: The property name on the virtinst object that
this parameter maps too. For the seclabel example, we want
disk.seclabels, so this value is 'seclabels'
:param objaddfn: The function name for adding a new instance of
this parameter to the virtinst object. For the seclabel example,
we want disk.add_seclabel(), so this value is "add_seclabels"
"""
def cb(inst, val, virtarg, can_edit):
ignore = val
@ -1165,7 +1162,7 @@ class VirtCLIParser(object):
if can_edit:
while len(getattr(inst, objpropname)) < (num + 1):
getattr(inst, objaddfn)()
getattr(inst, objpropname).add_new()
try:
return getattr(inst, objpropname)[num]
except IndexError:
@ -1465,8 +1462,7 @@ class ParserCPU(VirtCLIParser):
def cell_find_inst_cb(self, *args, **kwargs):
cliarg = "cell" # cell[0-9]*
objpropname = "cells" # cpu.cells
objaddfn = "add_cell" # cpu.add_cell
cb = self._make_find_inst_cb(cliarg, objpropname, objaddfn)
cb = self._make_find_inst_cb(cliarg, objpropname)
return cb(*args, **kwargs)
def sibling_find_inst_cb(self, inst, *args, **kwargs):
@ -1475,8 +1471,7 @@ class ParserCPU(VirtCLIParser):
cliarg = "sibling" # cell[0-9]*.distances.sibling[0-9]*
objpropname = "siblings" # cell.siblings
objaddfn = "add_sibling" # cell.add_sibling
cb = self._make_find_inst_cb(cliarg, objpropname, objaddfn)
cb = self._make_find_inst_cb(cliarg, objpropname)
return cb(inst, *args, **kwargs)
def set_model_cb(self, inst, val, virtarg):
@ -1508,8 +1503,8 @@ class ParserCPU(VirtCLIParser):
def set_l3_cache_cb(self, inst, val, virtarg, can_edit):
cpu = inst
if can_edit:
cpu.set_l3_cache_mode()
if can_edit and not cpu.cache:
cpu.cache.add_new()
try:
return cpu.cache[0]
except IndexError:
@ -1580,8 +1575,7 @@ class ParserCPUTune(VirtCLIParser):
def vcpu_find_inst_cb(self, *args, **kwargs):
cliarg = "vcpupin" # vcpupin[0-9]*
objpropname = "vcpus"
objaddfn = "add_vcpu"
cb = self._make_find_inst_cb(cliarg, objpropname, objaddfn)
cb = self._make_find_inst_cb(cliarg, objpropname)
return cb(*args, **kwargs)
_register_virt_parser(ParserCPUTune)
@ -1823,7 +1817,7 @@ class ParserClock(VirtCLIParser):
break
if not timerobj:
timerobj = inst.add_timer()
timerobj = inst.timers.add_new()
timerobj.name = tname
setattr(timerobj, attrname, val)
@ -1928,11 +1922,14 @@ class ParserQemuCLI(VirtCLIParser):
def args_cb(self, inst, val, virtarg):
for opt in shlex.split(val):
inst.add_arg(opt)
obj = inst.args.add_new()
obj.value = opt
def env_cb(self, inst, val, virtarg):
name, envval = val.split("=", 1)
inst.add_env(name, envval)
obj = inst.envs.add_new()
obj.name = name
obj.value = envval
def _parse(self, inst):
self.optdict.clear()
@ -2023,8 +2020,7 @@ class ParserDisk(VirtCLIParser):
def seclabel_find_inst_cb(self, *args, **kwargs):
cliarg = "seclabel" # seclabel[0-9]*
objpropname = "seclabels" # disk.seclabels
objaddfn = "add_seclabel" # disk.add_seclabel
cb = self._make_find_inst_cb(cliarg, objpropname, objaddfn)
cb = self._make_find_inst_cb(cliarg, objpropname)
return cb(*args, **kwargs)
def _parse(self, inst):
@ -2303,7 +2299,7 @@ class ParserGraphics(VirtCLIParser):
inst.set_listen_none()
elif val == "socket":
inst.remove_all_listens()
obj = inst.add_listen()
obj = inst.listens.add_new()
obj.type = "socket"
else:
inst.listen = val
@ -2311,8 +2307,7 @@ class ParserGraphics(VirtCLIParser):
def listens_find_inst_cb(self, *args, **kwargs):
cliarg = "listens" # listens[0-9]*
objpropname = "listens" # graphics.listens
objaddfn = "add_listen" # graphics.add_listen
cb = self._make_find_inst_cb(cliarg, objpropname, objaddfn)
cb = self._make_find_inst_cb(cliarg, objpropname)
return cb(*args, **kwargs)
def _parse(self, inst):

View File

@ -36,10 +36,3 @@ class Clock(XMLBuilder):
offset = XMLProperty("./@offset")
timers = XMLChildProperty(_ClockTimer)
def add_timer(self):
obj = _ClockTimer(self.conn)
self.add_child(obj)
return obj
def remove_timer(self, obj):
self.remove_child(obj)

View File

@ -43,11 +43,6 @@ class _CPUCell(XMLBuilder):
memory = XMLProperty("./@memory", is_int=True)
siblings = XMLChildProperty(_CPUCellSibling, relative_xpath="./distances")
def add_sibling(self):
obj = _CPUCellSibling(self.conn)
self.add_child(obj)
return obj
class CPUCache(XMLBuilder):
"""
@ -103,7 +98,7 @@ class CPU(XMLBuilder):
self.vendor = None
self.model_fallback = None
for f in self.features:
self.remove_feature(f)
self.remove_child(f)
self.mode = val
elif val == self.SPECIAL_MODE_HOST_COPY:
self.copy_host_cpu()
@ -121,26 +116,13 @@ class CPU(XMLBuilder):
self.special_mode_was_set = True
def add_feature(self, name, policy="require"):
feature = CPUFeature(self.conn)
feature = self.features.add_new()
feature.name = name
feature.policy = policy
self.add_child(feature)
def remove_feature(self, feature):
self.remove_child(feature)
features = XMLChildProperty(CPUFeature)
cells = XMLChildProperty(_CPUCell, relative_xpath="./numa")
def add_cell(self):
obj = _CPUCell(self.conn)
self.add_child(obj)
return obj
cache = XMLChildProperty(CPUCache)
def set_l3_cache_mode(self):
obj = CPUCache(self.conn)
self.add_child(obj)
return obj
def copy_host_cpu(self):
"""
@ -157,7 +139,7 @@ class CPU(XMLBuilder):
self.vendor = cpu.vendor
for feature in self.features:
self.remove_feature(feature)
self.remove_child(feature)
for feature in cpu.features:
self.add_feature(feature.name)

View File

@ -34,7 +34,3 @@ class CPUTune(XMLBuilder):
"""
_XML_ROOT_NAME = "cputune"
vcpus = XMLChildProperty(_VCPUPin)
def add_vcpu(self):
obj = _VCPUPin(self.conn)
self.add_child(obj)
return obj

View File

@ -600,15 +600,6 @@ class VirtualDisk(VirtualDevice):
auth_secret_type = XMLProperty("./auth/secret/@type")
auth_secret_uuid = XMLProperty("./auth/secret/@uuid")
def add_host(self, name, port):
obj = _Host(self.conn)
obj.name = name
obj.port = port
self.add_child(obj)
def remove_host(self, obj):
self.remove_child(obj)
hosts = XMLChildProperty(_Host, relative_xpath="./source")
source_name = XMLProperty("./source/@name")
@ -651,7 +642,9 @@ class VirtualDisk(VirtualDevice):
self.source_host_name = poolxml.hosts[0].name
self.source_host_port = poolxml.hosts[0].port
for host in poolxml.hosts:
self.add_host(host.name, host.port)
obj = self.hosts.add_new()
obj.name = host.name
obj.port = host.port
path = ""
if poolxml.source_name:
@ -792,10 +785,6 @@ class VirtualDisk(VirtualDevice):
iotune_wis = XMLProperty("./iotune/write_iops_sec", is_int=True)
seclabels = XMLChildProperty(_DiskSeclabel, relative_xpath="./source")
def add_seclabel(self):
obj = _DiskSeclabel(self.conn)
self.add_child(obj)
return obj
#################################

View File

@ -223,11 +223,6 @@ class VirtualGraphics(VirtualDevice):
for listen in self.listens:
self.remove_child(listen)
def add_listen(self):
obj = _GraphicsListen(self.conn)
self.add_child(obj)
return obj
def get_first_listen_type(self):
if len(self.listens) > 0:
return self.listens[0].type
@ -243,7 +238,7 @@ class VirtualGraphics(VirtualDevice):
if self.conn.check_support(
self.conn.SUPPORT_CONN_GRAPHICS_LISTEN_NONE):
obj = self.add_listen()
obj = self.listens.add_new()
obj.type = "none"
# Spice bits

View File

@ -844,15 +844,15 @@ class Guest(XMLBuilder):
#
# This is what has been recommended by the RH qemu guys :)
rtc = self.clock.add_timer()
rtc = self.clock.timers.add_new()
rtc.name = "rtc"
rtc.tickpolicy = "catchup"
pit = self.clock.add_timer()
pit = self.clock.timers.add_new()
pit.name = "pit"
pit.tickpolicy = "delay"
hpet = self.clock.add_timer()
hpet = self.clock.timers.add_new()
hpet.name = "hpet"
hpet.present = False
@ -861,7 +861,7 @@ class Guest(XMLBuilder):
if (self._os_object.is_windows() and self._hyperv_supported() and
(hv_clock or (self.stable_defaults() and hv_clock_rhel))):
hyperv = self.clock.add_timer()
hyperv = self.clock.timers.add_new()
hyperv.name = "hypervclock"
hyperv.present = True

View File

@ -67,10 +67,9 @@ class InterfaceProtocol(XMLBuilder):
#####################
def add_ip(self, addr, prefix=None):
ip = _IPAddress(self.conn)
ip = self.ips.add_new()
ip.address = addr
ip.prefix = prefix
self.add_child(ip)
def remove_ip(self, ip):
self.remove_child(ip)
ip.clear()

View File

@ -56,11 +56,6 @@ class _NetworkIP(XMLBuilder):
ranges = XMLChildProperty(_NetworkDHCPRange, relative_xpath="./dhcp")
hosts = XMLChildProperty(_NetworkDHCPHost, relative_xpath="./dhcp")
def add_range(self):
r = _NetworkDHCPRange(self.conn)
self.add_child(r)
return r
class _NetworkRoute(XMLBuilder):
_XML_ROOT_NAME = "route"
@ -95,11 +90,6 @@ class _NetworkForward(XMLBuilder):
pf = XMLChildProperty(_NetworkForwardPf)
vfs = XMLChildProperty(_NetworkForwardAddress)
def add_pf(self):
r = _NetworkForwardPf(self.conn)
self.add_child(r)
return r
def pretty_desc(self):
return Network.pretty_forward_desc(self.mode, self.dev)
@ -250,15 +240,6 @@ class Network(XMLBuilder):
routes = XMLChildProperty(_NetworkRoute)
bandwidth = XMLChildProperty(_NetworkBandwidth, is_single=True)
def add_ip(self):
ip = _NetworkIP(self.conn)
self.add_child(ip)
return ip
def add_route(self):
route = _NetworkRoute(self.conn)
self.add_child(route)
return route
##################
# build routines #

View File

@ -86,23 +86,19 @@ class OSXML(XMLBuilder):
self.remove_child(dev)
for d in newdevs:
dev = _BootDevice(self.conn)
dev = self._bootdevs.add_new()
dev.dev = d
self.add_child(dev)
_bootdevs = XMLChildProperty(_BootDevice)
bootorder = property(_get_bootorder, _set_bootorder)
initargs = XMLChildProperty(_InitArg)
def add_initarg(self, val):
obj = _InitArg(self.conn)
obj.val = val
self.add_child(obj)
def set_initargs_string(self, argstring):
import shlex
for obj in self.initargs:
self.remove_child(obj)
for val in shlex.split(argstring):
self.add_initarg(val)
obj = self.initargs.add_new()
obj.val = val
enable_bootmenu = XMLProperty("./bootmenu/@enable", is_yesno=True)
useserial = XMLProperty("./bios/@useserial", is_yesno=True)

View File

@ -186,8 +186,8 @@ class StoragePool(_StorageObject):
obj.type = pool_type
obj.source_path = parseobj.source_path
for h in parseobj.hosts:
parseobj.remove_host(h)
obj.add_host_obj(h)
parseobj.remove_child(h)
obj.add_child(h)
obj.source_name = parseobj.source_name
obj.format = parseobj.format
@ -421,15 +421,6 @@ class StoragePool(_StorageObject):
target_path = XMLProperty("./target/path",
default_cb=_get_default_target_path)
def add_host_obj(self, obj):
self.add_child(obj)
def add_host(self, name, port=None):
obj = _Host(self.conn)
obj.name = name
obj.port = port
self.add_child(obj)
def remove_host(self, obj):
self.remove_child(obj)
hosts = XMLChildProperty(_Host, relative_xpath="./source")

View File

@ -298,6 +298,34 @@ def _remove_xpath_node(ctx, xpath, dofree=True):
node.freeNode()
class _XMLChildList(list):
"""
Little wrapper for a list containing XMLChildProperty output.
This is just to insert a dynamically created add_new() function
which instantiates and appends a new child object
"""
def __init__(self, childclass, copylist, xmlbuilder):
list.__init__(self)
self._childclass = childclass
self._xmlbuilder = xmlbuilder
for i in copylist:
self.append(i)
def new(self):
"""
Instantiate a new child object and return it
"""
return self._childclass(self._xmlbuilder.conn)
def add_new(self):
"""
Instantiate a new child object, append it, and return it
"""
obj = self.new()
self._xmlbuilder.add_child(obj)
return obj
class XMLChildProperty(property):
"""
Property that points to a class used for parsing a subsection of
@ -305,6 +333,8 @@ class XMLChildProperty(property):
/domain/cpu/feature of the /domain/cpu class.
@child_classes: Single class or list of classes to parse state into
The list option is used by Guest._devices for parsing all
devices into a single list
@relative_xpath: Relative location where the class is rooted compared
to its _XML_ROOT_PATH. So interface xml can have nested
interfaces rooted at /interface/bridge/interface, so we pass
@ -344,7 +374,9 @@ class XMLChildProperty(property):
def _fget(self, xmlbuilder):
if self.is_single:
return self._get(xmlbuilder)
return self._get(xmlbuilder)[:]
return _XMLChildList(self.child_classes[0],
self._get(xmlbuilder),
xmlbuilder)
def clear(self, xmlbuilder):
if self.is_single:

View File

@ -40,14 +40,4 @@ class XMLNSQemu(XMLBuilder):
_XML_PROP_ORDER = ["args", "envs"]
args = XMLChildProperty(_XMLNSQemuArg)
def add_arg(self, value):
arg = _XMLNSQemuArg(conn=self.conn)
arg.value = value
self.add_child(arg)
envs = XMLChildProperty(_XMLNSQemuEnv)
def add_env(self, name, value):
env = _XMLNSQemuEnv(conn=self.conn)
env.name = name
env.value = value
self.add_child(env)