virt-install: add --destroy-on-exit

This makes the console window behave like a raw qemu command line:
when the user closes it, the VM is hard powered off.
This commit is contained in:
Cole Robinson 2018-10-11 14:11:37 -04:00
parent 96ac896e1a
commit 42a96cfd59
5 changed files with 43 additions and 9 deletions

View File

@ -1664,6 +1664,13 @@ after either of these events. Note that the VM's disks will not be
deleted. See:
L<https://wiki.libvirt.org/page/VM_lifecycle#Transient_guest_domains_vs_Persistent_guest_domains>
=item B<--destroy-on-exit>
When the VM console window is exited, destroy (force poweroff) the VM.
If you combine this with --transient, this makes the virt-install command
work similar to qemu, where the VM is shutdown when the console window
is closed by the user.
=item B<--print-xml> [STEP]
Print the generated XML of the guest, instead of defining it. By default this WILL do storage creation (can be disabled with --dry-run). This option implies --quiet.

View File

@ -824,6 +824,8 @@ c.add_valid("--file %(NEWIMG1)s --file-size .00001 --nonsparse") # Nonexistent
c = vinst.add_category("console-tests", "--pxe --nodisks")
c.add_valid("", grep="testsuite console command: ['virt-viewer'") # mock default graphics+virt-viewer usage
c.add_valid("--destroy-on-exit", grep="Restarting guest.\n") # destroy-on-exit
c.add_valid("--transient --destroy-on-exit", grep="Domain creation completed.") # destroy-on-exit + transient
c.add_valid("--graphics vnc --noreboot", grep="testsuite console command: ['virt-viewer'") # mock virt-viewer waiting, with noreboot magic
c.add_invalid("--noautoconsole --wait 1", grep="Installation has exceeded specified time limit") # --wait 1 is converted to 1 second if we are in the test suite, so this should actually touch the wait machinery. however in this case it exits with failure
c.add_valid("--nographics --transient", grep="testsuite console command: ['virsh'") # --transient handling

View File

@ -113,7 +113,7 @@ def main(conn=None):
elif not options.dry:
print_stdout(_("Creating guest '%s'.") % guest.name)
domain = installer.start_install(guest)
cli.connect_console(guest, domain, conscb, True)
cli.connect_console(guest, domain, conscb, True, False)
except Exception:
converter.cleanup()
raise

View File

@ -6,6 +6,7 @@
# See the COPYING file in the top-level directory.
import argparse
import atexit
import logging
import sys
import time
@ -600,7 +601,12 @@ def start_install(guest, installer, options):
doboot=not options.noreboot,
transient=options.transient,
autostart=options.autostart)
cli.connect_console(guest, domain, conscb, wait_on_console)
if options.destroy_on_exit:
atexit.register(_destroy_on_exit, domain)
cli.connect_console(guest, domain, conscb, wait_on_console,
options.destroy_on_exit)
check_domain(installer, domain, conscb, options.transient,
wait_on_install, wait_time, start_time)
@ -613,7 +619,8 @@ def start_install(guest, installer, options):
else:
print_stdout(_("Restarting guest."))
domain.create()
cli.connect_console(guest, domain, conscb, True)
cli.connect_console(guest, domain, conscb, True,
options.destroy_on_exit)
except KeyboardInterrupt:
logging.debug("", exc_info=True)
@ -656,7 +663,8 @@ def check_domain(installer, domain, conscb, transient,
# just closed the console and the VM is still running. In the
# the former case, libvirt may not have caught up yet with the
# VM having exited, so wait a bit and check again
time.sleep(2)
if not cli.in_testsuite():
time.sleep(2)
if check_domain_inactive():
return
@ -852,6 +860,9 @@ def parse_args():
help=_("Have domain autostart on host boot up."))
misc.add_argument("--transient", action="store_true", default=False,
help=_("Create a transient domain."))
misc.add_argument("--destroy-on-exit", action="store_true", default=False,
help=_("Force power off the domain when the console "
"viewer is closed."))
misc.add_argument("--wait", type=int,
help=_("Minutes to wait for install to complete."))
@ -865,6 +876,18 @@ def parse_args():
# main() handling #
###################
# Catchall for destroying the VM on ex. ctrl-c
def _destroy_on_exit(domain):
try:
isactive = bool(domain and domain.isActive())
if isactive:
domain.destroy()
except libvirt.libvirtError as e:
if e.get_error_code() != libvirt.VIR_ERR_NO_DOMAIN:
logging.debug("Error invoking atexit destroy_on_exit",
exc_info=True)
def set_test_stub_options(options):
# Set some basic options that will let virt-install succeed. Helps
# save boiler plate typing when testing new command line additions

View File

@ -358,13 +358,11 @@ def validate_disk(dev, warn_overwrite=False):
def _run_console(domain, args):
ignore = domain
logging.debug("Running: %s", " ".join(args))
if in_testsuite():
print_stdout("testsuite console command: %s" % args)
# Add this destroy() in here to trigger more virt-install code
# for the test suite
domain.destroy()
return None
args = ["/bin/true"]
child = os.fork()
if child:
@ -398,7 +396,7 @@ def _txt_console(guest, domain):
return _run_console(domain, args)
def connect_console(guest, domain, consolecb, wait):
def connect_console(guest, domain, consolecb, wait, destroy_on_exit):
"""
Launched the passed console callback for the already defined
domain. If domain isn't running, return an error.
@ -416,6 +414,10 @@ def connect_console(guest, domain, consolecb, wait):
except OSError as e:
logging.debug("waitpid error: %s", e)
if destroy_on_exit and domain.isActive():
logging.debug("console exited and destroy_on_exit passed, destroying")
domain.destroy()
def get_console_cb(guest):
gdevs = guest.devices.graphics