virt-manager/virt-install

1019 lines
33 KiB
Python
Executable File

#!/usr/bin/env python3
#
# Copyright 2005-2014 Red Hat, Inc.
#
# This work is licensed under the GNU GPLv2 or later.
# See the COPYING file in the top-level directory.
import argparse
import atexit
import logging
import sys
import time
import libvirt
import virtinst
from virtinst import cli
from virtinst.cli import fail, print_stdout, print_stderr
##############################
# Validation utility helpers #
##############################
install_methods = "--location URL, --cdrom CD/ISO, --pxe, --import, --boot hd|cdrom|..."
def all_install_options(options):
return [options.pxe, options.cdrom, options.location,
options.import_install]
def install_specified(options):
return any([bool(o) for o in all_install_options(options)])
def supports_pxe(guest):
"""
Return False if we are pretty sure the config doesn't support PXE
"""
for nic in guest.devices.interface:
if nic.type == nic.TYPE_USER:
continue
if nic.type != nic.TYPE_VIRTUAL:
return True
try:
netobj = nic.conn.networkLookupByName(nic.source)
xmlobj = virtinst.Network(nic.conn, parsexml=netobj.XMLDesc(0))
if xmlobj.can_pxe():
return True
except Exception:
logging.debug("Error checking if PXE supported", exc_info=True)
return True
return False
def check_cdrom_option_error(options):
if options.cdrom_short and options.cdrom:
fail("Cannot specify both -c and --cdrom")
if options.cdrom_short:
if "://" in options.cdrom_short:
fail("-c specified with what looks like a URI. Did you mean "
"to use --connect? If not, use --cdrom instead")
options.cdrom = options.cdrom_short
if not options.cdrom:
return
# Catch a strangely common error of users passing -vcpus=2 instead of
# --vcpus=2. The single dash happens to map to enough shortened options
# that things can fail weirdly if --paravirt is also specified.
for vcpu in [o for o in sys.argv if o.startswith("-vcpu")]:
if options.cdrom == vcpu[3:]:
fail("You specified -vcpus, you want --vcpus")
#################################
# Back compat option conversion #
#################################
def convert_old_printxml(options):
if options.xmlstep:
options.xmlonly = options.xmlstep
del(options.xmlstep)
def convert_old_sound(options):
if not options.sound:
return
for idx in range(len(options.sound)):
if options.sound[idx] is None:
options.sound[idx] = "default"
def convert_old_init(options):
if not options.init:
return
if not options.boot:
options.boot = [""]
options.boot[-1] += ",init=%s" % options.init
logging.debug("Converted old --init to --boot %s", options.boot[-1])
def _do_convert_old_disks(options):
paths = virtinst.xmlutil.listify(options.file_paths)
sizes = virtinst.xmlutil.listify(options.disksize)
def padlist(l, padsize):
l = virtinst.xmlutil.listify(l)
l.extend((padsize - len(l)) * [None])
return l
disklist = padlist(paths, max(0, len(sizes)))
sizelist = padlist(sizes, len(disklist))
opts = []
for idx, path in enumerate(disklist):
optstr = ""
if path:
optstr += "path=%s" % path
if sizelist[idx]:
if optstr:
optstr += ","
optstr += "size=%s" % sizelist[idx]
if options.sparse is False:
if optstr:
optstr += ","
optstr += "sparse=no"
logging.debug("Converted to new style: --disk %s", optstr)
opts.append(optstr)
options.disk = opts
def convert_old_disks(options):
if options.nodisks and (options.file_paths or
options.disk or
options.disksize):
fail(_("Cannot specify storage and use --nodisks"))
if ((options.file_paths or options.disksize or not options.sparse) and
options.disk):
fail(_("Cannot mix --file, --nonsparse, or --file-size with --disk "
"options. Use --disk PATH[,size=SIZE][,sparse=yes|no]"))
if not options.disk:
if options.nodisks:
options.disk = ["none"]
else:
_do_convert_old_disks(options)
del(options.file_paths)
del(options.disksize)
del(options.sparse)
del(options.nodisks)
logging.debug("Distilled --disk options: %s", options.disk)
def convert_old_os_options(options):
# Default to distro autodetection
distkey = "auto"
if options.os_variant:
distkey = options.os_variant
elif options.old_os_type:
distkey = options.old_os_type
options.os_variant = cli.parse_os_variant(distkey)
del(options.old_os_type)
def convert_old_memory(options):
if options.memory:
return
if not options.oldmemory:
return
options.memory = str(options.oldmemory)
def convert_old_cpuset(options):
if not options.cpuset:
return
if not options.vcpus:
options.vcpus = [""]
options.vcpus[-1] += ",cpuset=%s" % options.cpuset
logging.debug("Generated compat cpuset: --vcpus %s", options.vcpus[-1])
def convert_old_networks(options):
if options.nonetworks:
options.network = ["none"]
macs = virtinst.xmlutil.listify(options.mac)
networks = virtinst.xmlutil.listify(options.network)
bridges = virtinst.xmlutil.listify(options.bridge)
if bridges and networks:
fail(_("Cannot mix both --bridge and --network arguments"))
if bridges:
# Convert old --bridges to --networks
networks = ["bridge:" + b for b in bridges]
def padlist(l, padsize):
l = virtinst.xmlutil.listify(l)
l.extend((padsize - len(l)) * [None])
return l
# If a plain mac is specified, have it imply a default network
networks = padlist(networks, max(len(macs), 1))
macs = padlist(macs, len(networks))
for idx, ignore in enumerate(networks):
if networks[idx] is None:
networks[idx] = "default"
if macs[idx]:
networks[idx] += ",mac=%s" % macs[idx]
# Handle old format of bridge:foo instead of bridge=foo
for prefix in ["network", "bridge"]:
if networks[idx].startswith(prefix + ":"):
networks[idx] = networks[idx].replace(prefix + ":",
prefix + "=")
del(options.mac)
del(options.bridge)
del(options.nonetworks)
options.network = networks
logging.debug("Distilled --network options: %s", options.network)
def convert_old_graphics(options):
vnc = options.vnc
vncport = options.vncport
vnclisten = options.vnclisten
nographics = options.nographics
sdl = options.sdl
keymap = options.keymap
graphics = options.graphics
if graphics and (vnc or sdl or keymap or vncport or vnclisten):
fail(_("Cannot mix --graphics and old style graphical options"))
optnum = sum([bool(g) for g in [vnc, nographics, sdl, graphics]])
if optnum > 1:
raise ValueError(_("Can't specify more than one of VNC, SDL, "
"--graphics or --nographics"))
if options.graphics:
return
if optnum == 0:
return
# Build a --graphics command line from old style opts
optstr = ((vnc and "vnc") or
(sdl and "sdl") or
(nographics and ("none")))
if vnclisten:
optstr += ",listen=%s" % vnclisten
if vncport:
optstr += ",port=%s" % vncport
if keymap:
optstr += ",keymap=%s" % keymap
logging.debug("--graphics compat generated: %s", optstr)
options.graphics = [optstr]
def convert_old_features(options):
if options.features:
return
opts = ""
if options.noacpi:
opts += "acpi=off"
if options.noapic:
if opts:
opts += ","
opts += "apic=off"
if opts:
options.features = [opts]
##################################
# Install media setup/validation #
##################################
def do_test_media_detection(conn, options):
url = options.test_media_detection
guest = virtinst.Guest(conn)
if options.arch:
guest.os.arch = options.arch
if options.os_type:
guest.os.os_type = options.os_type
guest.set_capabilities_defaults()
installer = virtinst.Installer(conn, location=url)
print_stdout(installer.detect_distro(guest), do_force=True)
#############################
# General option validation #
#############################
def storage_specified(options, guest):
if guest.os.is_container():
return True
return options.disk or options.filesystem
def memory_specified(guest):
return guest.memory or guest.currentMemory or guest.cpu.cells
def validate_required_options(options, guest, installer):
# Required config. Don't error right away if nothing is specified,
# aggregate the errors to help first time users get it right
msg = ""
if not guest.name:
msg += "\n" + _("--name is required")
if not memory_specified(guest):
msg += "\n" + _("--memory amount in MiB is required")
if not storage_specified(options, guest):
msg += "\n" + (
_("--disk storage must be specified (override with --disk none)"))
if not installer:
msg += "\n" + (
_("An install method must be specified\n(%(methods)s)") %
{"methods": install_methods})
if msg:
fail(msg)
_cdrom_location_man_page = _("See the man page for examples of "
"using --location with CDROM media")
def check_option_collisions(options, guest, installer):
if options.noreboot and options.transient:
fail(_("--noreboot and --transient can not be specified together"))
# Install collisions
if sum([bool(l) for l in all_install_options(options)]) > 1:
fail(_("Only one install method can be used (%(methods)s)") %
{"methods": install_methods})
if guest.os.is_container() and install_specified(options):
fail(_("Install methods (%s) cannot be specified for "
"container guests") % install_methods)
cdrom_err = ""
if installer.cdrom:
cdrom_err = " " + _cdrom_location_man_page
if not options.location and options.extra_args:
fail(_("--extra-args only work if specified with --location.") +
cdrom_err)
if not options.location and options.initrd_inject:
fail(_("--initrd-inject only works if specified with --location.") +
cdrom_err)
if options.unattended:
if options.initrd_inject or options.extra_args:
fail(_("--unattended does not support --initrd-inject nor --extra-args."))
def _show_nographics_warnings(options, guest, installer):
if guest.devices.graphics:
return
if not options.autoconsole:
return
if installer.cdrom:
logging.warning(_("CDROM media does not print to the text console "
"by default, so you likely will not see text install output. "
"You might want to use --location.") + " " +
_cdrom_location_man_page)
return
if not options.location:
return
# Trying --location --nographics with console connect. Warn if
# they likely won't see any output.
if not guest.devices.console:
logging.warning(_("No --console device added, you likely will not "
"see text install output from the guest."))
return
serial_arg = "console=ttyS0"
serial_arm_arg = "console=ttyAMA0"
hvc_arg = "console=hvc0"
console_type = serial_arg
if guest.os.is_arm():
console_type = serial_arm_arg
if guest.devices.console[0].target_type in ["virtio", "xen"]:
console_type = hvc_arg
if guest.os.is_ppc64() or guest.os.is_arm_machvirt() or guest.os.is_s390x():
# Later arm/ppc/s390x kernels figure out console= automatically, so
# don't warn about it.
return
for args in (options.extra_args or []):
if console_type in (args or ""):
return
logging.warning(_("Did not find '%(console_string)s' in --extra-args, "
"which is likely required to see text install output from the "
"guest."), {"console_string": console_type})
def show_warnings(options, guest, installer):
if options.pxe and not supports_pxe(guest):
logging.warning(_("The guest's network configuration does not support "
"PXE"))
# Limit it to hvm x86 guests which presently our defaults
# only really matter for
if (guest.osinfo.name == "generic" and
not options.os_variant.is_none and
not options.os_variant.name == "generic" and
guest.os.is_x86() and guest.os.is_hvm()):
logging.warning(_("No operating system detected, VM performance may "
"suffer. Specify an OS with --os-variant for optimal results."))
_show_nographics_warnings(options, guest, installer)
##########################
# Guest building helpers #
##########################
def build_installer(options, guest):
cdrom = None
location = None
location_kernel = None
location_initrd = None
install_bootdev = None
has_installer = True
if options.unattended:
if options.os_variant.is_none or options.os_variant.is_auto:
fail(_("--unattended requires an explicit --os-variant"))
if not guest.osinfo.is_windows():
if options.cdrom:
options.location = options.cdrom
options.cdrom = None
options.os_variant.install = "location"
INSTALL_VALUES = ["location"]
if options.os_variant.install not in INSTALL_VALUES + [None]:
fail(_("Unknown --os-variant install value '%s'. Must be one of: %s") %
(options.os_variant.install, ", ".join(INSTALL_VALUES)))
if options.os_variant.install == "location":
if not options.location:
location = guest.osinfo.get_location(guest.os.arch)
logging.debug(
"Generated default libosinfo '--location %s'", location)
options.location = location
if options.location:
(location,
location_kernel,
location_initrd) = cli.parse_location(options.location)
elif options.cdrom:
cdrom = options.cdrom
elif options.pxe:
install_bootdev = "network"
elif (guest.os.is_container() or
options.import_install or
options.xmlonly or
options.boot):
pass
else:
has_installer = False
if not has_installer:
# This triggers an error in validate_required_options
return None
installer = virtinst.Installer(guest.conn,
cdrom=cdrom,
location=location,
location_kernel=location_kernel,
location_initrd=location_initrd,
install_bootdev=install_bootdev)
if cdrom and options.livecd:
installer.livecd = True
if options.unattended:
unattended_data = cli.parse_unattended(options.unattended)
options.unattended = None
installer.set_unattended_data(unattended_data)
else:
if options.extra_args:
installer.extra_args = options.extra_args
if options.initrd_inject:
installer.set_initrd_injections(options.initrd_inject)
if options.autostart:
installer.autostart = True
distro = None
try:
# This also validates the install location
autodistro = installer.detect_distro(guest)
if options.os_variant.is_auto:
distro = autodistro
except ValueError as e:
fail(_("Error validating install location: %s") % str(e))
if distro:
guest.set_os_name(distro)
return installer
def set_resources_from_osinfo(options, guest):
if guest.os.is_container():
return
res = guest.osinfo.get_recommended_resources()
storage = res.get_recommended_storage(guest.os.arch)
ram = res.get_recommended_ram(guest.os.arch)
ncpus = res.get_recommended_ncpus(guest.os.arch)
if ram and not memory_specified(guest):
guest.currentMemory = ram // 1024
if ncpus:
# We need to do this upfront, so we don't incorrectly set guest.vcpus
guest.sync_vcpus_topology()
if not guest.vcpus:
guest.vcpus = ncpus
if storage and not storage_specified(options, guest):
diskstr = 'size=%d' % (storage // (1024 ** 3))
logging.debug("Generated default libosinfo '--disk %s'", diskstr)
options.disk = [diskstr]
cli.ParserDisk(diskstr, guest=guest).parse(None)
def build_guest_instance(conn, options):
guest = virtinst.Guest(conn)
if options.name:
guest.name = options.name
if options.uuid:
guest.uuid = options.uuid
if options.description:
guest.description = options.description
if options.os_type:
guest.os.os_type = options.os_type
if options.virt_type:
guest.type = options.virt_type
if options.arch:
guest.os.arch = options.arch
if options.machine:
guest.os.machine = options.machine
# If explicit os-variant requested, set it early since it will
# provide more defaults in the future
options.os_variant.set_os_name(guest)
cli.parse_option_strings(options, guest, None)
# Call set_capabilities_defaults explicitly here rather than depend
# on set_defaults calling it. Installer setup needs filled in values.
# However we want to do it after parse_option_strings to ensure
# we are operating on any arch/os/type values passed in with --boot
guest.set_capabilities_defaults()
installer = build_installer(options, guest)
set_resources_from_osinfo(options, guest)
if installer:
installer.set_install_defaults(guest)
for path in installer.get_search_paths(guest):
cli.check_path_search(guest.conn, path)
# cli specific disk validation
for disk in guest.devices.disk:
cli.validate_disk(disk)
validate_required_options(options, guest, installer)
check_option_collisions(options, guest, installer)
show_warnings(options, guest, installer)
return guest, installer
###########################
# Install process helpers #
###########################
def start_install(guest, installer, options):
if options.wait is not None:
wait_on_install = True
wait_time = options.wait * 60
else:
wait_on_install = False
wait_time = -1
# If --wait specified, we don't want the default behavior of waiting
# for virt-viewer to exit, since then we can't exit the app when time
# expires
wait_on_console = not wait_on_install
if wait_time == 0:
# --wait 0 implies --noautoconsole
autoconsole = False
else:
autoconsole = options.autoconsole
conscb = None
if autoconsole:
conscb = cli.get_console_cb(guest)
if not conscb:
# If there isn't any console to actually connect up,
# default to --wait -1 to get similarish behavior
autoconsole = False
if options.wait is None:
logging.warning(_("No console to launch for the guest, "
"defaulting to --wait -1"))
wait_on_install = True
wait_time = -1
meter = cli.get_meter()
logging.debug("Guest.has_install_phase: %s",
installer.has_install_phase())
# we've got everything -- try to start the install
print_stdout(_("\nStarting install..."))
domain = None
try:
start_time = time.time()
domain = installer.start_install(guest, meter=meter,
doboot=not options.noreboot,
transient=options.transient)
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)
print_stdout(_("Domain creation completed."))
if not options.transient and not domain.isActive():
if options.noreboot or not installer.has_install_phase():
print_stdout( # pragma: no cover
_("You can restart your domain by running:\n %s") %
cli.virsh_start_cmd(guest))
else:
print_stdout(_("Restarting guest."))
domain.create()
cli.connect_console(guest, domain, conscb, True,
options.destroy_on_exit)
except KeyboardInterrupt: # pragma: no cover
logging.debug("", exc_info=True)
print_stderr(_("Domain install interrupted."))
raise
except Exception as e:
fail(e, do_exit=False)
if domain is None:
installer.cleanup_created_disks(guest, meter)
cli.install_fail(guest)
if cli.in_testsuite() and options.destroy_on_exit:
# Helps with unit testing
_destroy_on_exit(domain)
def check_domain(installer, domain, conscb, transient,
wait_for_install, wait_time, start_time):
"""
Make sure domain ends up in expected state, and wait if for install
to complete if requested
"""
def check_domain_inactive():
try:
dominfo = domain.info()
state = dominfo[0]
logging.debug("Domain state after install: %s", state)
if state == libvirt.VIR_DOMAIN_CRASHED:
fail(_("Domain has crashed.")) # pragma: no cover
return not domain.isActive()
except libvirt.libvirtError as e:
if transient and e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN:
logging.debug("transient VM shutdown and disappeared.")
return True
raise # pragma: no cover
if check_domain_inactive():
return
if bool(conscb):
# We are trying to detect if the VM shutdown, or the user
# 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
if not cli.in_testsuite():
time.sleep(2) # pragma: no cover
if check_domain_inactive():
return # pragma: no cover
# If we reach here, the VM still appears to be running.
if not wait_for_install or wait_time == 0:
# User either:
# used --noautoconsole
# used --wait 0
# killed console and guest is still running
if not installer.has_install_phase():
return
print_stdout(
_("Domain installation still in progress. You can reconnect"
" to \nthe console to complete the installation process."))
sys.exit(0)
wait_forever = (wait_time < 0)
timestr = (not wait_forever and
_(" %d minutes") % (int(wait_time) / 60) or "")
print_stdout(
_("Domain installation still in progress. Waiting"
"%(time_string)s for installation to complete.") %
{"time_string": timestr})
# Wait loop
while True:
if not domain.isActive(): # pragma: no cover
print_stdout(_("Domain has shutdown. Continuing."))
break
if not cli.in_testsuite(): # pragma: no cover
time.sleep(1)
time_elapsed = (time.time() - start_time)
if not cli.in_testsuite(): # pragma: no cover
if wait_forever:
continue
if time_elapsed < wait_time:
continue
print_stdout(
_("Installation has exceeded specified time limit. "
"Exiting application."))
sys.exit(1)
########################
# XML printing helpers #
########################
def xml_to_print(guest, installer, xmlonly, dry):
start_xml, final_xml = installer.start_install(
guest, dry=dry, return_xml=True)
if not start_xml:
start_xml = final_xml
final_xml = None
if dry and not xmlonly:
print_stdout(_("Dry run completed successfully"))
return
if xmlonly not in [False, "1", "2", "all"]:
fail(_("Unknown XML step request '%s', must be 1, 2, or all") %
xmlonly)
if xmlonly == "1":
return start_xml
if xmlonly == "2":
if not final_xml:
fail(_("Requested installation does not have XML step 2"))
return final_xml
# "all" case
xml = start_xml
if final_xml:
xml += final_xml
return xml
#######################
# CLI option handling #
#######################
def parse_args():
parser = cli.setupParser(
"%(prog)s --name NAME --memory MB STORAGE INSTALL [options]",
_("Create a new virtual machine from specified install media."),
introspection_epilog=True)
cli.add_connect_option(parser)
geng = parser.add_argument_group(_("General Options"))
geng.add_argument("-n", "--name",
help=_("Name of the guest instance"))
cli.add_memory_option(geng, backcompat=True)
cli.vcpu_cli_options(geng)
cli.add_metadata_option(geng)
geng.add_argument("-u", "--uuid", help=argparse.SUPPRESS)
geng.add_argument("--description", help=argparse.SUPPRESS)
insg = parser.add_argument_group(_("Installation Method Options"))
insg.add_argument("-c", dest="cdrom_short", help=argparse.SUPPRESS)
insg.add_argument("--cdrom", help=_("CD-ROM installation media"))
insg.add_argument("-l", "--location",
help=_("Distro install URL, eg. https://host/path. See man "
"page for specific distro examples."))
insg.add_argument("--pxe", action="store_true",
help=_("Boot from the network using the PXE protocol"))
insg.add_argument("--import", action="store_true", dest="import_install",
help=_("Build guest around an existing disk image"))
insg.add_argument("--livecd", action="store_true",
help=_("Treat the CD-ROM media as a Live CD"))
insg.add_argument("-x", "--extra-args", action="append",
help=_("Additional arguments to pass to the install kernel "
"booted from --location"))
insg.add_argument("--initrd-inject", action="append",
help=_("Add given file to root of initrd from --location"))
insg.add_argument("--unattended",
help=_("Perform a unattended installation"))
# Takes a URL and just prints to stdout the detected distro name
insg.add_argument("--test-media-detection", help=argparse.SUPPRESS)
# Helper for cli testing, fills in standard stub options
insg.add_argument("--test-stub-command", action="store_true",
help=argparse.SUPPRESS)
cli.add_boot_options(insg)
insg.add_argument("--init", help=argparse.SUPPRESS)
osg = cli.add_os_variant_option(parser, virtinstall=True)
osg.add_argument("--os-type", dest="old_os_type", help=argparse.SUPPRESS)
devg = parser.add_argument_group(_("Device Options"))
cli.add_disk_option(devg)
cli.add_net_option(devg)
cli.add_gfx_option(devg)
cli.add_device_options(devg, sound_back_compat=True)
# Deprecated device options
devg.add_argument("-f", "--file", dest="file_paths", action="append",
help=argparse.SUPPRESS)
devg.add_argument("-s", "--file-size", type=float,
action="append", dest="disksize",
help=argparse.SUPPRESS)
devg.add_argument("--nonsparse", action="store_false",
default=True, dest="sparse",
help=argparse.SUPPRESS)
devg.add_argument("--nodisks", action="store_true", help=argparse.SUPPRESS)
devg.add_argument("--nonetworks", action="store_true",
help=argparse.SUPPRESS)
devg.add_argument("-b", "--bridge", action="append",
help=argparse.SUPPRESS)
devg.add_argument("-m", "--mac", action="append", help=argparse.SUPPRESS)
devg.add_argument("--vnc", action="store_true", help=argparse.SUPPRESS)
devg.add_argument("--vncport", type=int, help=argparse.SUPPRESS)
devg.add_argument("--vnclisten", help=argparse.SUPPRESS)
devg.add_argument("-k", "--keymap", help=argparse.SUPPRESS)
devg.add_argument("--sdl", action="store_true", help=argparse.SUPPRESS)
devg.add_argument("--nographics", action="store_true",
help=argparse.SUPPRESS)
gxmlg = parser.add_argument_group(_("Guest Configuration Options"))
cli.add_guest_xml_options(gxmlg)
virg = parser.add_argument_group(_("Virtualization Platform Options"))
ostypeg = virg.add_mutually_exclusive_group()
ostypeg.add_argument("-v", "--hvm",
action="store_const", const="hvm", dest="os_type",
help=_("This guest should be a fully virtualized guest"))
ostypeg.add_argument("-p", "--paravirt",
action="store_const", const="xen", dest="os_type",
help=_("This guest should be a paravirtualized guest"))
ostypeg.add_argument("--container",
action="store_const", const="exe", dest="os_type",
help=_("This guest should be a container guest"))
virg.add_argument("--virt-type",
help=_("Hypervisor name to use (kvm, qemu, xen, ...)"))
virg.add_argument("--arch", help=_("The CPU architecture to simulate"))
virg.add_argument("--machine", help=_("The machine type to emulate"))
virg.add_argument("--accelerate", action="store_true",
help=argparse.SUPPRESS)
virg.add_argument("--noapic", action="store_true",
default=False, help=argparse.SUPPRESS)
virg.add_argument("--noacpi", action="store_true",
default=False, help=argparse.SUPPRESS)
misc = parser.add_argument_group(_("Miscellaneous Options"))
misc.add_argument("--autostart", action="store_true", default=False,
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."))
cli.add_misc_options(misc, prompt=True, printxml=True, printstep=True,
noreboot=True, dryrun=True, noautoconsole=True)
cli.autocomplete(parser)
return parser.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: # pragma: no cover
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): # pragma: no cover
# Set some basic options that will let virt-install succeed. Helps
# save boiler plate typing when testing new command line additions
if not options.test_stub_command:
return
if not options.connect:
options.connect = "test:///default"
if not options.name:
options.name = "test-stub-command"
if not options.memory:
options.memory = "256"
if not options.disk:
options.disk = "none"
if not install_specified(options):
options.import_install = True
if not options.graphics:
options.graphics = "none"
if not options.os_variant:
options.os_variant = "fedora27"
def main(conn=None):
cli.earlyLogging()
options = parse_args()
# Default setup options
convert_old_printxml(options)
options.quiet = (options.xmlonly or
options.test_media_detection or options.quiet)
cli.setupLogging("virt-install", options.debug, options.quiet)
if cli.check_option_introspection(options):
return 0
check_cdrom_option_error(options)
cli.convert_old_force(options)
cli.parse_check(options.check)
cli.set_prompt(options.prompt)
convert_old_memory(options)
convert_old_sound(options)
convert_old_networks(options)
convert_old_graphics(options)
convert_old_disks(options)
convert_old_features(options)
convert_old_cpuset(options)
convert_old_init(options)
set_test_stub_options(options)
convert_old_os_options(options)
conn = cli.getConnection(options.connect, conn=conn)
if options.test_media_detection:
do_test_media_detection(conn, options)
return 0
guest, installer = build_guest_instance(conn, options)
if options.xmlonly or options.dry:
xml = xml_to_print(guest, installer, options.xmlonly, options.dry)
if xml:
print_stdout(xml, do_force=True)
else:
start_install(guest, installer, options)
return 0
if __name__ == "__main__": # pragma: no cover
try:
sys.exit(main())
except SystemExit as sys_e:
sys.exit(sys_e.code)
except KeyboardInterrupt:
logging.debug("", exc_info=True)
print_stderr(_("Installation aborted at user request"))
except Exception as main_e:
fail(main_e)