cli: Make child lookup not specific to guest devices

<seclabel> is not singleton nowadays, so we need to extend our
infrastructure to handle non-device child properties. This is part of
that
This commit is contained in:
Cole Robinson
2015-09-04 16:07:01 -04:00
parent acfb988945
commit 4d87d2f27b
3 changed files with 62 additions and 47 deletions

View File

@@ -112,8 +112,8 @@ def get_domain_and_guest(conn, domstr):
# Change logic #
################
def _find_devices_to_edit(guest, action_name, editval, parserobj):
devlist = guest.get_devices(parserobj.devclass.virtual_device_type)
def _find_objects_to_edit(guest, action_name, editval, parserobj):
objlist = guest.list_children_for_class(parserobj.objclass)
idx = None
if editval is None:
@@ -123,26 +123,31 @@ def _find_devices_to_edit(guest, action_name, editval, parserobj):
idx = int(editval)
if idx is not None:
# Edit device by index
if idx == 0:
fail(_("Invalid --edit option '%s'") % editval)
if not devlist:
fail(_("No --%s devices found in the XML") %
if not objlist:
fail(_("No --%s objects found in the XML") %
parserobj.cli_arg_name)
if len(devlist) < abs(idx):
if len(objlist) < abs(idx):
fail(_("--edit %s requested but there's only %s "
"--%s devices in the XML") %
(idx, len(devlist), parserobj.cli_arg_name))
"--%s object in the XML") %
(idx, len(objlist), parserobj.cli_arg_name))
if idx > 0:
idx -= 1
inst = devlist[idx]
inst = objlist[idx]
elif editval == "all":
inst = devlist[:]
# Edit 'all' devices
inst = objlist[:]
else:
inst = parserobj.lookup_device_from_option_string(guest, editval)
# Lookup device by the passed prop string
inst = parserobj.lookup_child_from_option_string(guest, editval)
if not inst:
fail(_("No matching devices found for --%s %s") %
fail(_("No matching objects found for --%s %s") %
(action_name, editval))
return inst
@@ -182,8 +187,8 @@ def check_xmlopt_collision(options, parsermap):
def action_edit(guest, options, parsermap, parserobj):
if parserobj.devclass:
inst = _find_devices_to_edit(guest, "edit", options.edit, parserobj)
if parserobj.objclass:
inst = _find_objects_to_edit(guest, "edit", options.edit, parserobj)
else:
inst = guest
if options.edit and options.edit != '1' and options.edit != 'all':
@@ -196,18 +201,18 @@ def action_edit(guest, options, parsermap, parserobj):
def action_add_device(guest, options, parsermap, parserobj):
if not parserobj.devclass:
if not parserobj.objclass:
fail(_("Cannot use --add-device with --%s") % parserobj.cli_arg_name)
return cli.parse_option_strings(parsermap, options, guest, None)
def action_remove_device(guest, options, parsermap, parserobj):
ignore = parsermap
if not parserobj.devclass:
if not parserobj.objclass:
fail(_("Cannot use --remove-device with --%s") %
parserobj.cli_arg_name)
devs = _find_devices_to_edit(guest, "remove-device",
devs = _find_objects_to_edit(guest, "remove-device",
getattr(options, parserobj.option_variable_name)[-1], parserobj)
devs = util.listify(devs)
@@ -221,8 +226,8 @@ def action_build_xml(conn, options, parsermap, parserobj):
ret_inst = None
inst = None
if parserobj.devclass:
inst = parserobj.devclass(conn)
if parserobj.objclass:
inst = parserobj.objclass(conn)
elif parserobj.clear_attr:
ret_inst = getattr(guest, parserobj.clear_attr)
else:
@@ -440,7 +445,7 @@ def main(conn=None):
check_action_collision(options)
parserobj = check_xmlopt_collision(options, parsermap)
if options.update and not parserobj.devclass:
if options.update and not parserobj.objclass:
fail(_("Don't know how to --update for --%s") %
(parserobj.cli_arg_name))

View File

@@ -974,7 +974,7 @@ class VirtCLIParser(object):
from overriding _parse), so that we can show all options when the
user requests command line introspection like --disk=?
"""
devclass = None
objclass = None
def __init__(self, cli_arg_name):
"""
@@ -1053,8 +1053,8 @@ class VirtCLIParser(object):
ret = []
for optstr in optlist:
optinst = inst
if self.devclass and not inst:
optinst = self.devclass(guest.conn) # pylint: disable=not-callable
if self.objclass and not inst:
optinst = self.objclass(guest.conn) # pylint: disable=not-callable
try:
devs = self._parse_single_optstr(guest, optstr, optinst)
@@ -1082,15 +1082,15 @@ class VirtCLIParser(object):
return ret[0]
return ret
def lookup_device_from_option_string(self, guest, optstr):
def lookup_child_from_option_string(self, guest, optstr):
"""
Given a passed option string, search the guests' device list
for all devices which match the passed options.
Given a passed option string, search the guests' child list
for all objects which match the passed options.
"""
devlist = guest.get_devices(self.devclass.virtual_device_type)[:]
ret = []
objlist = guest.list_children_for_class(self.objclass)
for inst in devlist:
for inst in objlist:
try:
opts = VirtOptionString(optstr, self._params,
self.remove_first)
@@ -1582,7 +1582,7 @@ def _generate_new_volume_name(guest, poolobj, fmt):
class ParserDisk(VirtCLIParser):
def _init_params(self):
self.devclass = VirtualDisk
self.objclass = VirtualDisk
self.remove_first = "path"
def noset_cb(opts, inst, cliname, val):
@@ -1728,7 +1728,7 @@ class ParserDisk(VirtCLIParser):
class ParserNetwork(VirtCLIParser):
def _init_params(self):
self.devclass = VirtualNetworkInterface
self.objclass = VirtualNetworkInterface
self.remove_first = "type"
def set_mac_cb(opts, inst, cliname, val):
@@ -1797,7 +1797,7 @@ class ParserNetwork(VirtCLIParser):
class ParserGraphics(VirtCLIParser):
def _init_params(self):
self.devclass = VirtualGraphics
self.objclass = VirtualGraphics
self.remove_first = "type"
def set_keymap_cb(opts, inst, cliname, val):
@@ -1856,7 +1856,7 @@ class ParserGraphics(VirtCLIParser):
class ParserController(VirtCLIParser):
def _init_params(self):
self.devclass = VirtualController
self.objclass = VirtualController
self.remove_first = "type"
self.set_param("type", "type")
@@ -1885,7 +1885,7 @@ class ParserController(VirtCLIParser):
class ParserInput(VirtCLIParser):
def _init_params(self):
self.devclass = VirtualInputDevice
self.objclass = VirtualInputDevice
self.remove_first = "type"
self.set_param("type", "type")
@@ -1898,7 +1898,7 @@ class ParserInput(VirtCLIParser):
class ParserSmartcard(VirtCLIParser):
def _init_params(self):
self.devclass = VirtualSmartCardDevice
self.objclass = VirtualSmartCardDevice
self.remove_first = "mode"
self.check_none = True
@@ -1912,7 +1912,7 @@ class ParserSmartcard(VirtCLIParser):
class ParserRedir(VirtCLIParser):
def _init_params(self):
self.devclass = VirtualRedirDevice
self.objclass = VirtualRedirDevice
self.remove_first = "bus"
self.set_param("bus", "bus")
@@ -1938,7 +1938,7 @@ class ParserRedir(VirtCLIParser):
class ParserTPM(VirtCLIParser):
def _init_params(self):
self.devclass = VirtualTPMDevice
self.objclass = VirtualTPMDevice
self.remove_first = "type"
self.check_none = True
@@ -1958,7 +1958,7 @@ class ParserTPM(VirtCLIParser):
class ParserRNG(VirtCLIParser):
def _init_params(self):
self.devclass = VirtualRNGDevice
self.objclass = VirtualRNGDevice
self.remove_first = "type"
self.check_none = True
@@ -2028,7 +2028,7 @@ class ParserRNG(VirtCLIParser):
class ParserWatchdog(VirtCLIParser):
def _init_params(self):
self.devclass = VirtualWatchdog
self.objclass = VirtualWatchdog
self.remove_first = "model"
self.set_param("model", "model")
@@ -2041,7 +2041,7 @@ class ParserWatchdog(VirtCLIParser):
class ParserMemballoon(VirtCLIParser):
def _init_params(self):
self.devclass = VirtualMemballoon
self.objclass = VirtualMemballoon
self.remove_first = "model"
self.set_param("model", "model")
@@ -2053,7 +2053,7 @@ class ParserMemballoon(VirtCLIParser):
class ParserPanic(VirtCLIParser):
def _init_params(self):
self.devclass = VirtualPanicDevice
self.objclass = VirtualPanicDevice
self.remove_first = "iobase"
def set_iobase_cb(opts, inst, cliname, val):
@@ -2152,19 +2152,19 @@ class _ParserChar(VirtCLIParser):
class ParserSerial(_ParserChar):
devclass = VirtualSerialDevice
objclass = VirtualSerialDevice
class ParserParallel(_ParserChar):
devclass = VirtualParallelDevice
objclass = VirtualParallelDevice
class ParserChannel(_ParserChar):
devclass = VirtualChannelDevice
objclass = VirtualChannelDevice
class ParserConsole(_ParserChar):
devclass = VirtualConsoleDevice
objclass = VirtualConsoleDevice
########################
@@ -2173,7 +2173,7 @@ class ParserConsole(_ParserChar):
class ParserFilesystem(VirtCLIParser):
def _init_params(self):
self.devclass = VirtualFilesystem
self.objclass = VirtualFilesystem
self.remove_first = ["source", "target"]
self.set_param("type", "type")
@@ -2188,7 +2188,7 @@ class ParserFilesystem(VirtCLIParser):
class ParserVideo(VirtCLIParser):
def _init_params(self):
self.devclass = VirtualVideoDevice
self.objclass = VirtualVideoDevice
self.remove_first = "model"
self.set_param("model", "model", ignore_default=True)
@@ -2200,7 +2200,7 @@ class ParserVideo(VirtCLIParser):
class ParserSound(VirtCLIParser):
def _init_params(self):
self.devclass = VirtualAudio
self.objclass = VirtualAudio
self.remove_first = "model"
self.set_param("model", "model", ignore_default=True)
@@ -2218,7 +2218,7 @@ class ParserSound(VirtCLIParser):
class ParserHostdev(VirtCLIParser):
def _init_params(self):
self.devclass = VirtualHostDevice
self.objclass = VirtualHostDevice
self.remove_first = "name"
# If using the name_lookup_cb, this saves us repeatedly trying to

View File

@@ -984,6 +984,16 @@ class XMLBuilder(object):
_remove_xpath_node(self._xmlstate.xml_ctx, xpath, dofree=False)
self._set_child_xpaths()
def list_children_for_class(self, klass):
"""
Return a list of all XML child objects with the passed class
"""
ret = []
for prop in self._all_child_props().values():
ret += [obj for obj in util.listify(prop._get(self))
if obj.__class__ == klass]
return ret
#################################
# Private XML building routines #