xmlbuilder: Support clear()ing an object in place

This gives friendly XML output via virt-xml for clearxml=yes +
extra options: the XML block is editted in place, rather than removed
and with the newchanges appended
This commit is contained in:
Cole Robinson
2016-05-20 14:23:39 -04:00
parent 333103adbf
commit 55327c81b7
5 changed files with 48 additions and 36 deletions

View File

@@ -1,20 +1,12 @@
<feature policy="require" name="xtpr"/>
<feature policy="require" name="acpi"/> <feature policy="require" name="acpi"/>
</cpu> </cpu>
- <clock offset="utc"> <clock offset="utc">
- <timer name="rtc" tickpolicy="catchup"/> - <timer name="rtc" tickpolicy="catchup"/>
- <timer name="pit" tickpolicy="delay"/> - <timer name="pit" tickpolicy="delay"/>
- <timer name="hpet" present="no"/> - <timer name="hpet" present="no"/>
- </clock> </clock>
<on_poweroff>destroy</on_poweroff> <on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot> <on_reboot>restart</on_reboot>
<on_crash>restart</on_crash>
@@
</panic>
</devices>
<seclabel type="dynamic" model="selinux" relabel="yes"/>
+ <clock offset="utc"/>
</domain>
Domain 'test-for-virtxml' defined successfully. Domain 'test-for-virtxml' defined successfully.
Changes will take effect after the next domain shutdown. Changes will take effect after the next domain shutdown.

View File

@@ -17,16 +17,10 @@
- <feature policy="require" name="ds_cpl"/> - <feature policy="require" name="ds_cpl"/>
- <feature policy="require" name="xtpr"/> - <feature policy="require" name="xtpr"/>
- <feature policy="require" name="acpi"/> - <feature policy="require" name="acpi"/>
- </cpu> + <cpu mode="host-passthrough">
</cpu>
<clock offset="utc"> <clock offset="utc">
<timer name="rtc" tickpolicy="catchup"/> <timer name="rtc" tickpolicy="catchup"/>
<timer name="pit" tickpolicy="delay"/>
@@
</panic>
</devices>
<seclabel type="dynamic" model="selinux" relabel="yes"/>
+ <cpu mode="host-passthrough"/>
</domain>
Domain 'test-for-virtxml' defined successfully. Domain 'test-for-virtxml' defined successfully.
Changes will take effect after the next domain shutdown. Changes will take effect after the next domain shutdown.

View File

@@ -1028,17 +1028,27 @@ class VirtCLIParser(object):
def __init_global_params(self): def __init_global_params(self):
def set_clearxml_cb(opts, inst, cliname, val): def set_clearxml_cb(opts, inst, cliname, val):
ignore = opts = cliname ignore = opts
ignore = cliname
if not self.objclass and not self.clear_attr: if not self.objclass and not self.clear_attr:
raise RuntimeError("Don't know how to clearxml --%s" % raise RuntimeError("Don't know how to clearxml --%s" %
self.cli_arg_name) self.cli_arg_name)
if val is not True: if val is not True:
return return
clear_inst = inst
if self.clear_attr: if self.clear_attr:
getattr(inst, self.clear_attr).clear() clear_inst = getattr(inst, self.clear_attr)
else:
inst.clear() # If there's any opts remaining, leave the root stub element
# in place, so virt-xml updates are done in place.
#
# So --edit --cpu clearxml=yes should remove the entire <cpu>
# block. But --edit --cpu clearxml=yes,model=foo should leave
# a <cpu> stub in place, so that it gets model=foo in place,
# otherwise the newly created cpu block gets appened to the
# end of the domain XML, which gives an ugly diff
clear_inst.clear(leave_stub=bool(opts.opts))
self.set_param(None, "clearxml", self.set_param(None, "clearxml",
setter_cb=set_clearxml_cb, is_onoff=True) setter_cb=set_clearxml_cb, is_onoff=True)

View File

@@ -312,9 +312,9 @@ SUPPORT_CONN_MEM_STATS_PERIOD = _make(
function="virDomain.setMemoryStatsPeriod", function="virDomain.setMemoryStatsPeriod",
version="1.1.1", hv_version={"qemu": 0}) version="1.1.1", hv_version={"qemu": 0})
SUPPORT_CONN_SPICE_GL = _make(version="1.3.3", SUPPORT_CONN_SPICE_GL = _make(version="1.3.3",
hv_version={"qemu": "2.5.92", "test": 0}) hv_version={"qemu": "2.7.92", "test": 0})
SUPPORT_CONN_VIDEO_VIRTIO_ACCEL3D = _make(version="1.3.0", SUPPORT_CONN_VIDEO_VIRTIO_ACCEL3D = _make(version="1.3.0",
hv_version={"qemu": "2.5.0", "test": 0}) hv_version={"qemu": "2.7.0", "test": 0})
# This is for disk <driver name=qemu>. xen supports this, but it's # This is for disk <driver name=qemu>. xen supports this, but it's

View File

@@ -38,6 +38,10 @@ _trackprops = bool("VIRTINST_TEST_SUITE" in os.environ)
_allprops = [] _allprops = []
_seenprops = [] _seenprops = []
# Convenience global to prevent _remove_xpath_node from unlinking the
# top relavtive node in certain cases
_top_node = None
class _DocCleanupWrapper(object): class _DocCleanupWrapper(object):
def __init__(self, doc): def __init__(self, doc):
@@ -224,17 +228,17 @@ def _remove_xpath_node(ctx, xpath, dofree=True):
if not is_orig: if not is_orig:
continue continue
if node == root_node or node == _top_node:
# Don't unlink the root node, since it's spread out to all
# child objects and it ends up wreaking havoc.
break
# Look for preceding whitespace and remove it # Look for preceding whitespace and remove it
white = node.get_prev() white = node.get_prev()
if white and white.type == "text" and not white.content.count("<"): if white and white.type == "text" and not white.content.count("<"):
white.unlinkNode() white.unlinkNode()
white.freeNode() white.freeNode()
if node == root_node:
# Don't unlink the root node, since it's spread out to all
# child objects and it ends up wreaking havoc.
break
node.unlinkNode() node.unlinkNode()
if dofree: if dofree:
node.freeNode() node.freeNode()
@@ -836,17 +840,28 @@ class XMLBuilder(object):
finally: finally:
self._finish_get_xml(data) self._finish_get_xml(data)
def clear(self): def clear(self, leave_stub=False):
""" """
Wipe out all properties of the object Wipe out all properties of the object
:param leave_stub: if True, don't unlink the top stub node,
see virtinst/cli usage for an explanation
""" """
global _top_node
old_top_node = _top_node
try:
if leave_stub:
_top_node = _get_xpath_node(self._xmlstate.xml_ctx,
self.get_root_xpath())
props = self._all_xml_props().values() props = self._all_xml_props().values()
props += self._all_child_props().values() props += self._all_child_props().values()
for prop in props: for prop in props:
prop.clear(self) prop.clear(self)
finally:
_top_node = old_top_node
is_child = bool(re.match("^.*\[\d+\]$", self.get_root_xpath())) is_child = bool(re.match("^.*\[\d+\]$", self.get_root_xpath()))
if is_child: if is_child or leave_stub:
# User requested to clear an object that is the child of # User requested to clear an object that is the child of
# another object (xpath ends in [1] etc). We can't fully remove # another object (xpath ends in [1] etc). We can't fully remove
# the node in that case, since then the xmlbuilder object is # the node in that case, since then the xmlbuilder object is
@@ -855,6 +870,7 @@ class XMLBuilder(object):
node = _get_xpath_node(self._xmlstate.xml_ctx, node = _get_xpath_node(self._xmlstate.xml_ctx,
self.get_root_xpath()) self.get_root_xpath())
indent = 2 * self.get_root_xpath().count("/") indent = 2 * self.get_root_xpath().count("/")
if node:
node.setContent("\n" + (indent * " ")) node.setContent("\n" + (indent * " "))
else: else:
_remove_xpath_node(self._xmlstate.xml_ctx, _remove_xpath_node(self._xmlstate.xml_ctx,