cli: Replace add --check option (bz 1063471)

For fine grained enabling/disabling validation checks. Use this to
replace the heavy handed --force option.
This commit is contained in:
Cole Robinson 2015-04-11 19:25:46 -04:00
parent 0f3d86ac9e
commit 93f826a8b2
7 changed files with 128 additions and 51 deletions

View File

@ -139,6 +139,10 @@ Show the help message and exit
Show program's version number and exit
=item B<--check>
Enable or disable some validation checks. See L<virt-install(1)> for more details.
=item B<-q>
=item B<--quiet>

View File

@ -1476,6 +1476,10 @@ change host device configuration, or actually teach libvirt about the guest.
virt-install may still fetch install media, since this is required to
properly detect the OS to install.
=item B<--check>
Enable or disable some validation checks. Some examples are warning about using a disk that's already assigned to another VM (--check path_in_use=on|off), or warning about potentially running out of space during disk allocation (--check disk_size=on|off). Most checks are performed by default.
=item B<-q>
=item B<--quiet>

View File

@ -96,6 +96,8 @@ test_files = {
'AUTOMANAGEIMG' : "/some/new/pool/dir/new",
'EXISTIMG1' : "/dev/default-pool/testvol1.img",
'EXISTIMG2' : "/dev/default-pool/testvol2.img",
'EXISTIMG3' : exist_images[0],
'EXISTIMG4' : exist_images[1],
'EXISTUPPER' : "/dev/default-pool/UPPER",
'POOL' : "default-pool",
'VOL' : "testvol1.img",
@ -638,6 +640,7 @@ c.add_valid("--disk path=%(MANAGEDNEW1)s,format=raw,size=.0000001") # Managed f
c.add_valid("--disk path=%(MANAGEDNEW1)s,format=qcow2,size=.0000001") # Managed file using format qcow2
c.add_valid("--disk %(EXISTIMG1)s") # Not specifying path=
c.add_valid("--disk %(NEWIMG1)s,format=raw,size=.0000001") # Not specifying path= but creating storage
c.add_valid("--disk %(COLLIDE)s --check path_in_use=off") # Colliding storage with --check
c.add_valid("--disk %(COLLIDE)s --force") # Colliding storage with --force
c.add_valid("--disk %(SHARE)s,perms=sh") # Colliding shareable storage
c.add_valid("--disk path=%(EXISTIMG1)s,device=cdrom --disk path=%(EXISTIMG1)s,device=cdrom") # Two IDE cds
@ -645,7 +648,7 @@ c.add_valid("--disk %(EXISTIMG1)s,driver_name=qemu,driver_type=qcow2") # Driver
c.add_valid("--disk /dev/zero") # Referencing a local unmanaged /dev node
c.add_valid("--disk pool=default,size=.00001") # Building 'default' pool
c.add_valid("--disk %(AUTOMANAGEIMG)s,size=.1") # autocreate the pool
c.add_invalid("--disk %(NEWIMG1)s,sparse=true,size=100000000000 --force") # Don't warn about fully allocated file exceeding disk space
c.add_valid("--disk %(NEWIMG1)s,sparse=true,size=100000000 --check disk_size=off") # Don't warn about fully allocated file exceeding disk space
c.add_invalid("--file %(NEWIMG1)s --file-size 100000 --nonsparse") # Nonexisting file, size too big
c.add_invalid("--file %(NEWIMG1)s --file-size 100000") # Huge file, sparse, but no prompting
c.add_invalid("--file %(NEWIMG1)s") # Nonexisting file, no size
@ -828,7 +831,7 @@ c.add_invalid("--mac 22:22:33:12:34:AB") # Colliding macaddr
c = vinst.add_category("storage-back-compat", "--pxe --noautoconsole")
c.add_valid("--file %(EXISTIMG1)s --nonsparse --file-size 4") # Existing file, other opts
c.add_valid("--file %(EXISTIMG1)s") # Existing file, no opts
c.add_valid("--file %(EXISTIMG1)s --file virt-clone --file virt-clone") # Multiple existing files
c.add_valid("--file %(EXISTIMG1)s --file %(EXISTIMG1)s") # Multiple existing files
c.add_valid("--file %(NEWIMG1)s --file-size .00001 --nonsparse") # Nonexistent file
@ -936,20 +939,22 @@ c.add_compare("--remove-device --host-device 0x04b3:0x4485", "remove-hostdev-nam
vclon = App("virt-clone")
c = vclon.add_category("remote", "--connect %(REMOTEURI)s")
c.add_valid("-o test --auto-clone") # Auto flag, no storage
c.add_valid("--original-xml %(CLONE_STORAGE_XML)s --auto-clone") # Auto flag w/ managed storage,
c.add_invalid("--original-xml %(CLONE_DISK_XML)s --auto-clone") # Auto flag w/ storage,
c.add_valid("--original-xml %(CLONE_STORAGE_XML)s --auto-clone") # Auto flag w/ managed storage
c.add_invalid("--original-xml %(CLONE_DISK_XML)s --auto-clone") # Auto flag w/ local storage, which is invalid for remote connection
c = vclon.add_category("misc", "")
c.add_compare("--connect %(KVMURI)s -o test-for-clone --auto-clone --clone-running", "clone-auto1", compare_check=support.SUPPORT_CONN_LOADER_ROM)
c.add_compare("-o test-clone-simple --name newvm --auto-clone --clone-running", "clone-auto2", compare_check=support.SUPPORT_CONN_LOADER_ROM)
c.add_valid("-o test --auto-clone") # Auto flag, no storage
c.add_valid("--original-xml %(CLONE_DISK_XML)s --auto-clone") # Auto flag w/ storage,
c.add_valid("--original-xml %(CLONE_STORAGE_XML)s --auto-clone") # Auto flag w/ managed storage,
c.add_valid("--original-xml %(CLONE_STORAGE_XML)s --auto-clone") # Auto flag w/ managed storage
c.add_valid("--original-xml %(CLONE_DISK_XML)s --auto-clone") # Auto flag w/ local storage
c.add_valid("-o test-for-clone --auto-clone --clone-running") # Auto flag, actual VM, skip state check
c.add_valid("-o test-clone-simple -n newvm --preserve-data --file /dev/default-pool/default-vol --clone-running --force") # Preserve data shouldn't complain about existing volume
c.add_valid("-o test-clone-simple -n newvm --preserve-data --file %(EXISTIMG1)s --clone-running") # Preserve data shouldn't complain about existing volume
c.add_valid("-n clonetest --original-xml %(CLONE_DISK_XML)s --file %(EXISTIMG3)s --file %(EXISTIMG4)s --check path_exists=off") # Skip existing file check
c.add_invalid("--auto-clone") # Just the auto flag
c.add_invalid("-o test-for-clone --auto-clone")
c.add_invalid("-o test-clone-simple -n newvm --file %(EXISTIMG1)s --clone-running") # Should complain about overwriting existing file
c = vclon.add_category("general", "-n clonetest")

View File

@ -24,8 +24,6 @@ import argparse
import logging
import sys
import urlgrabber.progress as progress
import virtinst.cli as cli
from virtinst import Cloner
from virtinst.cli import fail, print_stdout, print_stderr
@ -163,8 +161,9 @@ def main(conn=None):
options.quiet = options.quiet or options.xmlonly
cli.setupLogging("virt-clone", options.debug, options.quiet)
cli.convert_old_force(options)
cli.parse_check(options.check)
cli.set_prompt(options.prompt)
cli.set_force(options.force)
if conn is None:
conn = cli.getConnection(options.connect)
@ -200,9 +199,7 @@ def main(conn=None):
if options.xmlonly:
print_stdout(design.clone_xml, do_force=True)
else:
# start cloning
meter = progress.TextMeter(fo=sys.stdout)
design.start_duplicate(meter)
design.start_duplicate(cli.get_meter())
print_stdout("")
print_stdout(_("Clone '%s' created successfully.") % design.clone_name)

View File

@ -24,7 +24,6 @@ import sys
import time
import libvirt
import urlgrabber.progress as progress
import virtinst
from virtinst import cli
@ -418,7 +417,7 @@ def validate_required_options(options, guest):
msg = ""
if not options.name:
msg += "\n" + cli.name_missing
msg += "\n" + _("--name is required")
if not options.memory:
msg += "\n" + _("--memory amount in MiB is required")
@ -739,9 +738,7 @@ def start_install(guest, continue_inst, options):
wait_on_install = True
wait_time = -1
meter = (options.quiet and
progress.BaseMeter() or
progress.TextMeter(fo=sys.stdout))
meter = cli.get_meter()
logging.debug("Guest.has_install_phase: %s",
guest.installer.has_install_phase())
@ -1048,7 +1045,8 @@ def main(conn=None):
check_cdrom_option_error(options)
cli.set_force(options.force)
cli.convert_old_force(options)
cli.parse_check(options.check)
cli.set_prompt(options.prompt)
parsermap = cli.build_parser_map(options)

View File

@ -24,7 +24,6 @@ import os
import sys
import libvirt
import urlgrabber.progress as progress
import virtinst
from virtinst import cli
@ -241,11 +240,7 @@ def setup_device(dev):
logging.debug("Doing setup for disk=%s", dev)
meter = ((cli.quiet or "VIRTINST_TEST_SUITE" in os.environ) and
progress.BaseMeter() or
progress.TextMeter(fo=sys.stdout))
dev.setup(meter)
dev.setup(cli.get_meter())
dev.virt_xml_setup = True

View File

@ -29,6 +29,7 @@ import sys
import traceback
import libvirt
from urlgrabber import progress
from virtcli import CLIConfig
@ -58,8 +59,38 @@ from .osxml import OSXML
from .storage import StoragePool, StorageVolume
force = False
quiet = False
##########################
# Global option handling #
##########################
class _GlobalState(object):
def __init__(self):
self.quiet = False
self.all_checks = None
self._validation_checks = {}
def set_validation_check(self, checkname, val):
self._validation_checks[checkname] = val
def get_validation_check(self, checkname):
if self.all_checks is not None:
return self.all_checks
# Default to True for all checks
return self._validation_checks.get(checkname, True)
_globalstate = None
def get_global_state():
return _globalstate
def _reset_global_state():
global _globalstate
_globalstate = _GlobalState()
####################
@ -136,8 +167,8 @@ def earlyLogging():
def setupLogging(appname, debug_stdout, do_quiet, cli_app=True):
global quiet
quiet = do_quiet
_reset_global_state()
get_global_state().quiet = do_quiet
vi_dir = None
logfile = None
@ -192,7 +223,7 @@ def setupLogging(appname, debug_stdout, do_quiet, cli_app=True):
elif not cli_app:
streamHandler = None
else:
if quiet:
if get_global_state().quiet:
level = logging.ERROR
else:
level = logging.WARN
@ -270,7 +301,7 @@ def fail(msg, do_exit=True):
def print_stdout(msg, do_force=False):
if do_force or not quiet:
if do_force or not get_global_state().quiet:
print msg
@ -303,27 +334,22 @@ def install_fail(guest):
sys.exit(1)
def set_force(val=True):
global force
force = val
def set_prompt(prompt):
# Set whether we allow prompts, or fail if a prompt pops up
if prompt:
logging.warning("--prompt mode is no longer supported.")
name_missing = _("--name is required")
def validate_disk(dev, warn_overwrite=False):
def _optional_fail(msg):
if force:
logging.debug("--force skipping error condition '%s'", msg)
logging.warn(msg)
else:
fail(msg + _(" (Use --force to override)"))
def _optional_fail(msg, checkname):
do_check = get_global_state().get_validation_check(checkname)
if do_check:
fail(msg + (_(" (Use --check %s=off or "
"--check all=off to override)") % checkname))
logging.debug("Skipping --check %s error condition '%s'",
checkname, msg)
logging.warn(msg)
def check_path_exists(dev):
"""
@ -331,10 +357,11 @@ def validate_disk(dev, warn_overwrite=False):
"""
if not warn_overwrite:
return
if VirtualDisk.path_definitely_exists(dev.conn, dev.path):
_optional_fail(
_("This will overwrite the existing path '%s'" % dev.path))
if not VirtualDisk.path_definitely_exists(dev.conn, dev.path):
return
_optional_fail(
_("This will overwrite the existing path '%s'" % dev.path),
"path_exists")
def check_inuse_conflict(dev):
"""
@ -345,7 +372,8 @@ def validate_disk(dev, warn_overwrite=False):
return
_optional_fail(_("Disk %s is already in use by other guests %s." %
(dev.path, names)))
(dev.path, names)),
"path_in_use")
def check_size_conflict(dev):
"""
@ -354,7 +382,7 @@ def validate_disk(dev, warn_overwrite=False):
isfatal, errmsg = dev.is_size_conflict()
# The isfatal case should have already caused us to fail
if not isfatal and errmsg:
_optional_fail(errmsg)
_optional_fail(errmsg, "disk_size")
def check_path_search(dev):
user, broken_paths = dev.check_path_search(dev.conn, dev.path)
@ -446,6 +474,12 @@ def get_console_cb(guest):
return _gfx_console
def get_meter():
if get_global_state().quiet or "VIRTINST_TEST_SUITE" in os.environ:
return progress.BaseMeter()
return progress.TextMeter(fo=sys.stdout)
###########################
# Common CLI option/group #
###########################
@ -509,6 +543,11 @@ def add_misc_options(grp, prompt=False, replace=False,
help=_("Run through install process, but do not "
"create devices or define the guest."))
if prompt:
grp.add_argument("--check",
help=_("Enable or disable validation checks. Example:\n"
"--check path_in_use=off\n"
"--check all=off"))
grp.add_argument("-q", "--quiet", action="store_true",
help=_("Suppress non-error output"))
grp.add_argument("-d", "--debug", action="store_true",
@ -1100,6 +1139,41 @@ class VirtCLIParser(object):
raise NotImplementedError()
###################
# --check parsing #
###################
def convert_old_force(options):
if options.force:
if not options.check:
options.check = "all=off"
del(options.force)
class ParseCLICheck(VirtCLIParser):
# This sets properties on the _GlobalState objects
def _init_params(self):
def _set_check(opts, inst, cliname, val):
ignore = opts
inst.set_validation_check(cliname, val)
self.set_param(None, "path_in_use",
is_onoff=True, setter_cb=_set_check)
self.set_param(None, "disk_size",
is_onoff=True, setter_cb=_set_check)
self.set_param(None, "path_exists",
is_onoff=True, setter_cb=_set_check)
self.set_param("all_checks", "all", is_onoff=True)
def parse_check(checkstr):
# Overwrite this for each parse,
parser = ParseCLICheck("check")
parser.parse(None, checkstr, get_global_state())
######################
# --metadata parsing #
######################