2013-03-18 05:06:52 +08:00
|
|
|
#!/usr/bin/python -tt
|
|
|
|
#
|
2013-12-15 07:28:27 +08:00
|
|
|
# Copyright 2005-2013 Red Hat, Inc.
|
2013-03-18 05:06:52 +08:00
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
# MA 02110-1301 USA.
|
|
|
|
|
2014-01-19 06:01:43 +08:00
|
|
|
import argparse
|
|
|
|
import logging
|
2013-03-18 05:06:52 +08:00
|
|
|
import os
|
2014-01-19 06:01:43 +08:00
|
|
|
import re
|
2013-03-18 05:06:52 +08:00
|
|
|
import sys
|
|
|
|
import time
|
|
|
|
|
2013-08-10 03:00:16 +08:00
|
|
|
import libvirt
|
2013-03-18 05:06:52 +08:00
|
|
|
import urlgrabber.progress as progress
|
|
|
|
|
|
|
|
import virtinst
|
|
|
|
import virtinst.cli as 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|..."
|
|
|
|
install_missing = (_("An install method must be specified\n(%(methods)s)") %
|
2013-04-14 02:34:52 +08:00
|
|
|
{"methods" : install_methods})
|
2013-03-18 05:06:52 +08:00
|
|
|
disk_missing = _("--disk storage must be specified (override with --nodisks)")
|
|
|
|
|
2013-04-14 02:34:52 +08:00
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
def install_specified(location, cdpath, pxe, import_install):
|
|
|
|
return bool(pxe or cdpath or location or import_install)
|
|
|
|
|
2013-04-14 02:34:52 +08:00
|
|
|
|
2014-01-21 07:04:23 +08:00
|
|
|
def cdrom_specified(guest, disk=None):
|
2013-04-12 21:39:34 +08:00
|
|
|
disks = guest.get_devices("disk")
|
|
|
|
|
|
|
|
for disk in disks:
|
2013-03-18 05:06:52 +08:00
|
|
|
if disk.device == virtinst.VirtualDisk.DEVICE_CDROM:
|
|
|
|
return True
|
|
|
|
|
|
|
|
# Probably haven't set up disks yet
|
2014-01-21 07:04:23 +08:00
|
|
|
if not disks and disk:
|
|
|
|
for opts in disk:
|
2013-03-18 05:06:52 +08:00
|
|
|
if opts.count("device=cdrom"):
|
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
2013-04-14 02:34:52 +08:00
|
|
|
|
2013-09-28 08:16:35 +08:00
|
|
|
def storage_specified(disks, nodisks, filesystems):
|
|
|
|
return bool(disks or nodisks or filesystems)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-04-14 02:34:52 +08:00
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
def supports_pxe(guest):
|
|
|
|
"""
|
|
|
|
Return False if we are pretty sure the config doesn't support PXE
|
|
|
|
"""
|
|
|
|
for nic in guest.get_devices("interface"):
|
|
|
|
if nic.type == nic.TYPE_USER:
|
|
|
|
continue
|
|
|
|
if nic.type != nic.TYPE_VIRTUAL:
|
|
|
|
return True
|
|
|
|
|
|
|
|
try:
|
2013-09-24 22:00:01 +08:00
|
|
|
netobj = nic.conn.networkLookupByName(nic.source)
|
2013-09-23 05:04:22 +08:00
|
|
|
xmlobj = virtinst.Network(nic.conn, parsexml=netobj.XMLDesc(0))
|
|
|
|
if xmlobj.can_pxe():
|
2013-03-18 05:06:52 +08:00
|
|
|
return True
|
|
|
|
except:
|
|
|
|
logging.debug("Error checking if PXE supported", exc_info=True)
|
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
2013-04-22 00:28:14 +08:00
|
|
|
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
|
|
|
|
|
2013-04-05 00:04:07 +08:00
|
|
|
if not options.cdrom:
|
|
|
|
return
|
|
|
|
|
2013-04-22 00:28:14 +08:00
|
|
|
# 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.
|
2013-04-05 00:04:07 +08:00
|
|
|
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")
|
|
|
|
|
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
##############################
|
|
|
|
# Device validation wrappers #
|
|
|
|
##############################
|
|
|
|
|
2013-09-28 08:16:35 +08:00
|
|
|
def convert_old_sound(options):
|
2014-01-21 07:04:23 +08:00
|
|
|
if options.soundhw:
|
2013-09-28 08:16:35 +08:00
|
|
|
return
|
|
|
|
if not options.old_sound_bool:
|
|
|
|
return
|
2014-01-21 07:04:23 +08:00
|
|
|
options.soundhw = "default"
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
|
2014-01-22 04:36:34 +08:00
|
|
|
def convert_old_init(options):
|
|
|
|
if not options.init:
|
|
|
|
return
|
|
|
|
if not options.boot:
|
|
|
|
options.boot = ""
|
|
|
|
options.boot += ",init=%s" % options.init
|
|
|
|
logging.debug("Converted old --init to --boot %s", options.boot)
|
|
|
|
|
|
|
|
|
2013-09-28 08:16:35 +08:00
|
|
|
def get_disks(guest, disks, nodisks, need_storage):
|
|
|
|
if nodisks:
|
|
|
|
return
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-09-28 08:16:35 +08:00
|
|
|
if not disks and need_storage and cli.is_prompt():
|
|
|
|
disks = [None]
|
|
|
|
|
2014-01-21 07:04:23 +08:00
|
|
|
for disk in disks:
|
2013-09-28 08:16:35 +08:00
|
|
|
try:
|
2014-01-21 07:04:23 +08:00
|
|
|
if disk is None:
|
2013-09-28 08:16:35 +08:00
|
|
|
dev = None
|
|
|
|
sparse = True
|
|
|
|
size = None
|
|
|
|
path = None
|
|
|
|
else:
|
|
|
|
# We skip validation here, since we may have converted
|
|
|
|
# --file-size to --disk size=8 which doesn't validate on
|
|
|
|
# its own.
|
2014-01-21 07:04:23 +08:00
|
|
|
dev = cli.parse_disk(guest, disk,
|
2014-01-21 21:48:22 +08:00
|
|
|
virtinst.VirtualDisk(guest.conn),
|
|
|
|
validate=False)
|
2014-01-20 02:56:06 +08:00
|
|
|
size = dev.cli_size
|
2013-09-28 08:16:35 +08:00
|
|
|
path = dev.path
|
|
|
|
sparse = dev.get_sparse()
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-09-28 08:16:35 +08:00
|
|
|
d = cli.disk_prompt(guest.conn, path, size, sparse, origdev=dev)
|
|
|
|
d.validate()
|
|
|
|
except ValueError, e:
|
|
|
|
fail(_("Error with storage parameters: %s" % str(e)))
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-09-28 08:16:35 +08:00
|
|
|
guest.add_device(d)
|
2013-04-14 02:34:52 +08:00
|
|
|
|
2013-09-28 08:16:35 +08:00
|
|
|
|
|
|
|
def convert_old_disks(options):
|
2014-01-21 07:04:23 +08:00
|
|
|
if options.disk or options.nodisks:
|
2013-09-28 08:16:35 +08:00
|
|
|
return
|
|
|
|
|
|
|
|
paths = virtinst.util.listify(options.file_paths)
|
|
|
|
sizes = virtinst.util.listify(options.disksize)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
def padlist(l, padsize):
|
2013-09-28 08:16:35 +08:00
|
|
|
l = virtinst.util.listify(l)
|
2013-03-18 05:06:52 +08:00
|
|
|
l.extend((padsize - len(l)) * [None])
|
|
|
|
return l
|
|
|
|
|
2013-09-28 08:16:35 +08:00
|
|
|
disklist = padlist(paths, max(0, len(sizes)))
|
|
|
|
sizelist = padlist(sizes, len(disklist))
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-09-28 08:16:35 +08:00
|
|
|
opts = []
|
2013-03-18 05:06:52 +08:00
|
|
|
for idx in range(len(disklist)):
|
2013-09-28 08:16:35 +08:00
|
|
|
optstr = ""
|
|
|
|
if disklist[idx]:
|
|
|
|
optstr += "path=%s" % disklist[idx]
|
|
|
|
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)
|
|
|
|
|
2014-01-21 07:04:23 +08:00
|
|
|
options.disk = opts
|
2013-09-28 08:16:35 +08:00
|
|
|
options.file_paths = []
|
|
|
|
options.disksize = None
|
|
|
|
options.sparse = True
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-04-14 02:34:52 +08:00
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
########################
|
|
|
|
# Virt type validation #
|
|
|
|
########################
|
|
|
|
|
|
|
|
def prompt_virt(caps, arch, req_virt_type, req_accel):
|
|
|
|
supports_hvm = False
|
|
|
|
supports_pv = False
|
|
|
|
supports_accel = False
|
|
|
|
for guest in caps.guests:
|
|
|
|
if guest.os_type == "hvm":
|
|
|
|
supports_hvm = True
|
|
|
|
|
|
|
|
elif guest.os_type == "xen":
|
|
|
|
if (len(guest.domains) and
|
|
|
|
guest.domains[0].hypervisor_type == "kvm"):
|
|
|
|
# Don't prompt user for PV w/ xenner
|
|
|
|
continue
|
|
|
|
supports_pv = True
|
|
|
|
|
|
|
|
if not arch:
|
2014-01-19 02:23:30 +08:00
|
|
|
arch = caps.host.cpu.arch
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
if not req_virt_type:
|
|
|
|
if supports_hvm and supports_pv:
|
|
|
|
prompt_txt = _("Would you like a fully virtualized guest "
|
|
|
|
"(yes or no)? This will allow you to run "
|
|
|
|
"unmodified operating systems.")
|
|
|
|
|
2013-09-28 22:06:52 +08:00
|
|
|
if cli.prompt_for_yes_no(prompt_txt, ""):
|
2013-03-18 05:06:52 +08:00
|
|
|
req_virt_type = "hvm"
|
|
|
|
else:
|
|
|
|
req_virt_type = "xen"
|
|
|
|
|
|
|
|
elif supports_hvm:
|
|
|
|
req_virt_type = "hvm"
|
|
|
|
|
|
|
|
elif supports_pv:
|
|
|
|
req_virt_type = "xen"
|
|
|
|
|
|
|
|
# See if that domain supports acceleration
|
|
|
|
accel_type = ""
|
|
|
|
for guest in caps.guests:
|
|
|
|
if guest.os_type == req_virt_type and guest.arch == arch:
|
|
|
|
for dom in guest.domains:
|
|
|
|
if dom.is_accelerated():
|
|
|
|
supports_accel = True
|
|
|
|
accel_type = dom.hypervisor_type.upper()
|
|
|
|
|
|
|
|
if supports_accel and not req_accel:
|
|
|
|
prompt_txt = (_("Would you like to use %s acceleration? "
|
|
|
|
"(yes or no)") % accel_type)
|
|
|
|
|
2013-09-28 22:06:52 +08:00
|
|
|
req_accel = cli.prompt_for_yes_no(prompt_txt, "")
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
return (req_virt_type, req_accel)
|
|
|
|
|
2013-04-14 02:34:52 +08:00
|
|
|
|
2013-07-17 19:53:47 +08:00
|
|
|
def get_guest(conn, options):
|
2013-03-18 05:06:52 +08:00
|
|
|
# Set up all virt/hypervisor parameters
|
2013-04-12 04:32:00 +08:00
|
|
|
if sum([bool(f) for f in [options.fullvirt,
|
|
|
|
options.paravirt,
|
|
|
|
options.container]]) > 1:
|
2013-03-18 05:06:52 +08:00
|
|
|
fail(_("Can't do more than one of --hvm, --paravirt, or --container"))
|
|
|
|
|
|
|
|
req_accel = True
|
|
|
|
req_hv_type = options.hv_type and options.hv_type.lower() or None
|
|
|
|
if options.fullvirt:
|
|
|
|
req_virt_type = "hvm"
|
|
|
|
elif options.paravirt:
|
|
|
|
req_virt_type = "xen"
|
|
|
|
elif options.container:
|
|
|
|
req_virt_type = "exe"
|
|
|
|
else:
|
|
|
|
# This should force capabilities to give us the most sensible default
|
|
|
|
req_virt_type = None
|
|
|
|
|
|
|
|
if cli.is_prompt():
|
|
|
|
# User requested prompting but passed no virt type flag, ask for
|
|
|
|
# needed info
|
2013-07-07 02:12:13 +08:00
|
|
|
req_virt_type, req_accel = prompt_virt(conn.caps, options.arch,
|
2013-03-18 05:06:52 +08:00
|
|
|
req_virt_type, req_accel)
|
|
|
|
|
|
|
|
logging.debug("Requesting virt method '%s', hv type '%s'.",
|
|
|
|
(req_virt_type and req_virt_type or _("default")),
|
|
|
|
(req_hv_type and req_hv_type or _("default")))
|
|
|
|
|
|
|
|
arch = options.arch
|
|
|
|
if re.match("i.86", arch or ""):
|
|
|
|
arch = "i686"
|
|
|
|
|
|
|
|
try:
|
|
|
|
(capsguest,
|
2013-07-07 02:12:13 +08:00
|
|
|
capsdomain) = conn.caps.guest_lookup(
|
2013-03-18 05:06:52 +08:00
|
|
|
os_type=req_virt_type,
|
|
|
|
arch=arch,
|
2013-04-12 20:26:21 +08:00
|
|
|
typ=req_hv_type,
|
2013-03-18 05:06:52 +08:00
|
|
|
accelerated=req_accel,
|
|
|
|
machine=options.machine)
|
2013-07-17 19:53:47 +08:00
|
|
|
guest = conn.caps.build_virtinst_guest(conn, capsguest, capsdomain)
|
|
|
|
guest.os.machine = options.machine
|
2013-03-18 05:06:52 +08:00
|
|
|
except Exception, e:
|
|
|
|
fail(e)
|
|
|
|
|
|
|
|
if (not req_virt_type and
|
|
|
|
not req_hv_type and
|
|
|
|
req_accel and
|
2013-07-06 08:36:28 +08:00
|
|
|
conn.is_qemu() and
|
2013-03-18 05:06:52 +08:00
|
|
|
capsguest.arch in ["i686", "x86_64"] and
|
|
|
|
not capsdomain.is_accelerated()):
|
|
|
|
logging.warn("KVM acceleration not available, using '%s'",
|
|
|
|
capsdomain.hypervisor_type)
|
|
|
|
|
2013-07-17 19:53:47 +08:00
|
|
|
return guest
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
|
|
|
|
##################################
|
|
|
|
# Install media setup/validation #
|
|
|
|
##################################
|
|
|
|
|
|
|
|
def get_install_media(guest, location, cdpath, need_install):
|
|
|
|
manual_cdrom = cdrom_specified(guest)
|
|
|
|
cdinstall = bool(not location and (cdpath or cdrom_specified(guest)))
|
|
|
|
|
|
|
|
if not (location or cdpath or manual_cdrom or need_install):
|
|
|
|
return
|
|
|
|
|
|
|
|
try:
|
|
|
|
if not location and not cdinstall and cli.is_prompt():
|
|
|
|
media_prompt(guest)
|
|
|
|
else:
|
|
|
|
validate_install_media(guest, location, cdpath, cdinstall)
|
|
|
|
except ValueError, e:
|
|
|
|
fail(_("Error validating install location: %s" % str(e)))
|
|
|
|
|
2013-04-14 02:34:52 +08:00
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
def media_prompt(guest):
|
2013-07-17 19:53:47 +08:00
|
|
|
if guest.os.is_hvm():
|
2013-03-18 05:06:52 +08:00
|
|
|
prompt_txt = _("What is the install CD-ROM/ISO or URL?")
|
|
|
|
else:
|
|
|
|
prompt_txt = _("What is the install URL?")
|
|
|
|
|
|
|
|
while 1:
|
|
|
|
location = None
|
|
|
|
cdpath = None
|
|
|
|
media = cli.prompt_for_input("", prompt_txt, None)
|
|
|
|
|
|
|
|
if not len(media):
|
|
|
|
continue
|
|
|
|
|
2013-07-17 19:53:47 +08:00
|
|
|
if not guest.os.is_hvm() or media.count(":/"):
|
2013-03-18 05:06:52 +08:00
|
|
|
location = media
|
|
|
|
else:
|
|
|
|
cdpath = media
|
|
|
|
|
|
|
|
try:
|
|
|
|
validate_install_media(guest, location, cdpath)
|
|
|
|
except Exception, e:
|
|
|
|
logging.error(str(e))
|
|
|
|
continue
|
|
|
|
break
|
|
|
|
|
2013-04-14 02:34:52 +08:00
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
def validate_install_media(guest, location, cdpath, cdinstall=False):
|
|
|
|
if cdinstall or cdpath:
|
|
|
|
guest.installer.cdrom = True
|
|
|
|
if location or cdpath:
|
|
|
|
guest.installer.location = (location or cdpath)
|
2013-09-27 01:04:28 +08:00
|
|
|
guest.installer.check_location(guest)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
|
|
|
|
#############################
|
|
|
|
# General option validation #
|
|
|
|
#############################
|
|
|
|
|
|
|
|
def validate_required_options(options, guest):
|
|
|
|
# Required config. Don't error right away if nothing is specified,
|
|
|
|
# aggregate the errors to help first time users get it right
|
|
|
|
msg = ""
|
|
|
|
need_storage = False
|
|
|
|
need_install = False
|
|
|
|
|
|
|
|
if not options.name:
|
|
|
|
msg += "\n" + cli.name_missing
|
|
|
|
|
|
|
|
if not options.memory:
|
2014-01-25 07:56:59 +08:00
|
|
|
msg += "\n" + cli.memory_missing
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-07-17 19:53:47 +08:00
|
|
|
if (not guest.os.is_container() and
|
2014-01-21 07:04:23 +08:00
|
|
|
not storage_specified(options.disk,
|
2013-09-28 08:16:35 +08:00
|
|
|
options.nodisks,
|
2014-01-21 07:04:23 +08:00
|
|
|
options.filesystem)):
|
2013-03-18 05:06:52 +08:00
|
|
|
msg += "\n" + disk_missing
|
|
|
|
need_storage = True
|
|
|
|
|
2013-07-17 19:53:47 +08:00
|
|
|
if (not guest.os.is_container() and
|
2013-03-18 05:06:52 +08:00
|
|
|
(not install_specified(options.location, options.cdrom,
|
|
|
|
options.pxe, options.import_install)) and
|
2014-01-21 07:04:23 +08:00
|
|
|
(not cdrom_specified(guest, options.disk))):
|
2013-03-18 05:06:52 +08:00
|
|
|
msg += "\n" + install_missing
|
|
|
|
need_install = True
|
|
|
|
|
|
|
|
if msg and not cli.is_prompt():
|
|
|
|
fail(msg)
|
|
|
|
|
|
|
|
return need_storage, need_install
|
|
|
|
|
2013-04-14 02:34:52 +08:00
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
def check_option_collisions(options, guest):
|
|
|
|
# Disk collisions
|
|
|
|
if options.nodisks and (options.file_paths or
|
2014-01-21 07:04:23 +08:00
|
|
|
options.disk or
|
2013-03-18 05:06:52 +08:00
|
|
|
options.disksize):
|
|
|
|
fail(_("Cannot specify storage and use --nodisks"))
|
|
|
|
|
2013-04-14 02:34:52 +08:00
|
|
|
if ((options.file_paths or options.disksize or not options.sparse) and
|
2014-01-21 07:04:23 +08:00
|
|
|
options.disk):
|
2013-03-18 05:06:52 +08:00
|
|
|
fail(_("Cannot mix --file, --nonsparse, or --file-size with --disk "
|
|
|
|
"options. Use --disk PATH[,size=SIZE][,sparse=yes|no]"))
|
|
|
|
|
|
|
|
# Network collisions
|
|
|
|
if options.nonetworks:
|
|
|
|
if options.mac:
|
|
|
|
fail(_("Cannot use --mac with --nonetworks"))
|
|
|
|
if options.bridge:
|
|
|
|
fail(_("Cannot use --bridge with --nonetworks"))
|
|
|
|
if options.network:
|
|
|
|
fail(_("Cannot use --network with --nonetworks"))
|
|
|
|
return
|
|
|
|
|
|
|
|
# Install collisions
|
2013-04-12 04:32:00 +08:00
|
|
|
if sum([bool(l) for l in [options.pxe, options.location,
|
|
|
|
options.cdrom, options.import_install]]) > 1:
|
2013-03-18 05:06:52 +08:00
|
|
|
fail(_("Only one install method can be used (%(methods)s)") %
|
|
|
|
{"methods" : install_methods})
|
|
|
|
|
2013-07-17 19:53:47 +08:00
|
|
|
if (guest.os.is_container() and
|
2013-03-18 05:06:52 +08:00
|
|
|
install_specified(options.location, options.cdrom,
|
|
|
|
options.pxe, options.import_install)):
|
|
|
|
fail(_("Install methods (%s) cannot be specified for "
|
|
|
|
"container guests") % install_methods)
|
|
|
|
|
2013-07-17 19:53:47 +08:00
|
|
|
if guest.os.is_xenpv():
|
2013-03-18 05:06:52 +08:00
|
|
|
if options.pxe:
|
|
|
|
fail(_("Network PXE boot is not supported for paravirtualized "
|
|
|
|
"guests"))
|
|
|
|
if options.cdrom or options.livecd:
|
|
|
|
fail(_("Paravirtualized guests cannot install off cdrom media."))
|
|
|
|
|
|
|
|
if (options.location and
|
2013-07-06 08:36:28 +08:00
|
|
|
guest.conn.is_remote() and not
|
2013-10-06 22:08:04 +08:00
|
|
|
guest.conn.support_remote_url_install()):
|
2013-03-18 05:06:52 +08:00
|
|
|
fail(_("Libvirt version does not support remote --location installs"))
|
|
|
|
|
2014-01-21 07:04:23 +08:00
|
|
|
if not options.location and options.extra_args:
|
2013-03-18 05:06:52 +08:00
|
|
|
fail(_("--extra-args only work if specified with --location."))
|
2014-01-21 07:04:23 +08:00
|
|
|
if not options.location and options.initrd_inject:
|
2013-03-18 05:06:52 +08:00
|
|
|
fail(_("--initrd-inject only works if specified with --location."))
|
|
|
|
|
|
|
|
|
|
|
|
##########################
|
|
|
|
# Guest building helpers #
|
|
|
|
##########################
|
|
|
|
|
2013-07-17 19:53:47 +08:00
|
|
|
def build_installer(options, conn, virt_type):
|
2013-03-18 05:06:52 +08:00
|
|
|
# Build the Installer instance
|
|
|
|
if options.livecd:
|
|
|
|
instclass = virtinst.LiveCDInstaller
|
|
|
|
elif options.pxe:
|
|
|
|
if options.nonetworks:
|
|
|
|
fail(_("Can't use --pxe with --nonetworks"))
|
|
|
|
|
|
|
|
instclass = virtinst.PXEInstaller
|
|
|
|
elif options.cdrom or options.location:
|
|
|
|
instclass = virtinst.DistroInstaller
|
2014-01-21 07:04:23 +08:00
|
|
|
elif options.import_install or options.boot:
|
2013-03-18 05:06:52 +08:00
|
|
|
if options.import_install and options.nodisks:
|
|
|
|
fail(_("A disk device must be specified with --import."))
|
|
|
|
options.import_install = True
|
|
|
|
instclass = virtinst.ImportInstaller
|
|
|
|
elif virt_type == "exe":
|
|
|
|
instclass = virtinst.ContainerInstaller
|
|
|
|
else:
|
|
|
|
instclass = virtinst.DistroInstaller
|
|
|
|
|
|
|
|
# Only set installer params here that impact the hw config, not
|
|
|
|
# anything to do with install media
|
2013-07-17 04:47:08 +08:00
|
|
|
installer = instclass(conn)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
return installer
|
|
|
|
|
2013-04-14 02:34:52 +08:00
|
|
|
|
2014-01-22 22:06:35 +08:00
|
|
|
def build_guest_instance(conn, options, parsermap):
|
2013-07-17 19:53:47 +08:00
|
|
|
guest = get_guest(conn, options)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-07-17 19:53:47 +08:00
|
|
|
logging.debug("Received virt method '%s'", guest.type)
|
|
|
|
logging.debug("Hypervisor name is '%s'", guest.os.os_type)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-07-17 19:53:47 +08:00
|
|
|
guest.installer = build_installer(options, conn, guest.os.os_type)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2014-01-25 07:56:59 +08:00
|
|
|
cli.convert_old_memory(options)
|
2013-09-28 08:16:35 +08:00
|
|
|
convert_old_sound(options)
|
|
|
|
cli.convert_old_networks(guest, options, not options.nonetworks and 1 or 0)
|
|
|
|
cli.convert_old_graphics(guest, options)
|
|
|
|
convert_old_disks(options)
|
2013-09-28 21:36:11 +08:00
|
|
|
cli.convert_old_features(options)
|
2014-01-22 04:24:46 +08:00
|
|
|
cli.convert_old_cpuset(options)
|
2014-01-22 04:36:34 +08:00
|
|
|
convert_old_init(options)
|
2013-09-28 08:16:35 +08:00
|
|
|
|
2014-01-21 07:15:08 +08:00
|
|
|
# non-xml install options
|
2014-01-25 08:34:38 +08:00
|
|
|
guest.installer.extraargs = options.extra_args
|
2014-01-21 07:04:23 +08:00
|
|
|
guest.installer.initrd_injections = options.initrd_inject
|
2013-10-06 20:53:05 +08:00
|
|
|
cli.set_os_variant(guest, options.distro_type, options.distro_variant)
|
2014-01-21 07:15:08 +08:00
|
|
|
guest.autostart = options.autostart
|
2013-10-06 20:53:05 +08:00
|
|
|
|
2014-01-22 04:24:46 +08:00
|
|
|
cli.get_name(guest, options.name)
|
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
# Guest configuration
|
2014-01-21 07:15:08 +08:00
|
|
|
if options.uuid:
|
|
|
|
guest.uuid = options.uuid
|
2013-03-18 05:06:52 +08:00
|
|
|
guest.description = options.description
|
|
|
|
|
2014-01-22 22:06:35 +08:00
|
|
|
# We don't want to auto-parse --disk, but we wanted it for earlier
|
|
|
|
# parameter introspection
|
|
|
|
del(parsermap["disk"])
|
2014-01-22 03:28:47 +08:00
|
|
|
cli.parse_option_strings(parsermap, options, guest, None)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-10-06 20:30:33 +08:00
|
|
|
guest.add_default_input_device()
|
|
|
|
guest.add_default_console_device()
|
|
|
|
guest.add_default_video_device()
|
|
|
|
guest.add_default_usb_controller()
|
2013-10-06 21:19:59 +08:00
|
|
|
guest.add_default_channels()
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2014-01-25 07:56:59 +08:00
|
|
|
if cli.is_prompt():
|
|
|
|
cli.get_memory(guest, guest.memory and (guest.memory / 1024) or None)
|
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
# Do this after setting up all optional parameters, so we report error
|
|
|
|
# about those first.
|
|
|
|
need_storage, need_install = validate_required_options(options, guest)
|
|
|
|
|
2014-01-21 07:04:23 +08:00
|
|
|
get_disks(guest, options.disk, options.nodisks, need_storage)
|
2013-03-18 05:06:52 +08:00
|
|
|
get_install_media(guest, options.location, options.cdrom, need_install)
|
|
|
|
|
|
|
|
# Various little validations about option collisions. Need to do
|
|
|
|
# this after setting guest.installer at least
|
|
|
|
check_option_collisions(options, guest)
|
|
|
|
|
|
|
|
# Warnings
|
|
|
|
if options.pxe and not supports_pxe(guest):
|
|
|
|
logging.warn(_("The guest's network configuration does not support "
|
|
|
|
"PXE"))
|
|
|
|
|
|
|
|
return guest
|
|
|
|
|
|
|
|
|
|
|
|
###########################
|
|
|
|
# Install process helpers #
|
|
|
|
###########################
|
|
|
|
|
|
|
|
def _run_console(args):
|
|
|
|
logging.debug("Running: %s", " ".join(args))
|
|
|
|
child = os.fork()
|
|
|
|
if child:
|
|
|
|
return child
|
|
|
|
|
|
|
|
os.execvp(args[0], args)
|
2013-04-14 02:34:52 +08:00
|
|
|
os._exit(1) # pylint: disable=W0212
|
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
def vnc_console(dom, uri):
|
|
|
|
args = ["/usr/bin/virt-viewer",
|
|
|
|
"--connect", uri,
|
|
|
|
"--wait", str(dom.ID())]
|
|
|
|
|
|
|
|
if not os.path.exists(args[0]):
|
|
|
|
logging.warn(_("Unable to connect to graphical console: "
|
|
|
|
"virt-viewer not installed. Please install "
|
|
|
|
"the 'virt-viewer' package."))
|
|
|
|
return None
|
|
|
|
|
|
|
|
return _run_console(args)
|
|
|
|
|
2013-04-14 02:34:52 +08:00
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
def txt_console(dom, uri):
|
|
|
|
args = ["/usr/bin/virsh",
|
|
|
|
"--connect", uri,
|
|
|
|
"console", str(dom.ID())]
|
|
|
|
|
|
|
|
return _run_console(args)
|
|
|
|
|
2013-04-14 02:34:52 +08:00
|
|
|
|
2013-08-10 03:00:16 +08:00
|
|
|
def domain_is_crashed(domain):
|
|
|
|
"""
|
|
|
|
Return True if the created domain object is in a crashed state
|
|
|
|
"""
|
|
|
|
if not domain:
|
|
|
|
return False
|
|
|
|
|
|
|
|
dominfo = domain.info()
|
|
|
|
state = dominfo[0]
|
|
|
|
|
|
|
|
return state == libvirt.VIR_DOMAIN_CRASHED
|
|
|
|
|
|
|
|
|
|
|
|
def domain_is_shutdown(domain):
|
|
|
|
"""
|
|
|
|
Return True if the created domain object is shutdown
|
|
|
|
"""
|
|
|
|
if not domain:
|
|
|
|
return False
|
|
|
|
|
|
|
|
dominfo = domain.info()
|
|
|
|
|
|
|
|
state = dominfo[0]
|
|
|
|
cpu_time = dominfo[4]
|
|
|
|
|
|
|
|
if state == libvirt.VIR_DOMAIN_SHUTOFF:
|
|
|
|
return True
|
|
|
|
|
|
|
|
# If 'wait' was specified, the dom object we have was looked up
|
|
|
|
# before initially shutting down, which seems to bogus up the
|
|
|
|
# info data (all 0's). So, if it is bogus, assume the domain is
|
|
|
|
# shutdown. We will catch the error later.
|
|
|
|
return state == libvirt.VIR_DOMAIN_NOSTATE and cpu_time == 0
|
|
|
|
|
|
|
|
|
|
|
|
def connect_console(domain, consolecb, wait):
|
|
|
|
"""
|
|
|
|
Launched the passed console callback for the already defined
|
|
|
|
domain. If domain isn't running, return an error.
|
|
|
|
"""
|
|
|
|
child = None
|
|
|
|
if consolecb:
|
|
|
|
child = consolecb(domain)
|
|
|
|
|
|
|
|
if not child or not wait:
|
|
|
|
return
|
|
|
|
|
|
|
|
# If we connected the console, wait for it to finish
|
|
|
|
try:
|
|
|
|
os.waitpid(child, 0)
|
2013-09-20 01:12:05 +08:00
|
|
|
except OSError, e:
|
|
|
|
logging.debug("waitpid: %s: %s", e.errno, e.message)
|
2013-08-10 03:00:16 +08:00
|
|
|
|
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
def start_install(guest, continue_inst, options):
|
|
|
|
def show_console(dom):
|
2013-10-03 08:20:52 +08:00
|
|
|
xmlobj = virtinst.Guest(guest.conn, parsexml=dom.XMLDesc(0))
|
|
|
|
gdev = xmlobj.get_devices("graphics")
|
2013-04-12 21:02:12 +08:00
|
|
|
if not gdev:
|
2013-03-18 05:06:52 +08:00
|
|
|
logging.debug("Connecting to text console")
|
2013-07-14 04:29:43 +08:00
|
|
|
return txt_console(dom, guest.conn.getURI())
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-04-12 21:02:12 +08:00
|
|
|
gtype = gdev[0].type
|
|
|
|
if gtype in [virtinst.VirtualGraphics.TYPE_VNC,
|
|
|
|
virtinst.VirtualGraphics.TYPE_SPICE]:
|
|
|
|
logging.debug("Launching virt-viewer for graphics type '%s'",
|
|
|
|
gtype)
|
2013-07-14 04:29:43 +08:00
|
|
|
return vnc_console(dom, guest.conn.getURI())
|
2013-04-12 21:02:12 +08:00
|
|
|
else:
|
|
|
|
logging.debug("No viewer to launch for graphics type '%s'",
|
|
|
|
gtype)
|
2013-08-10 03:00:16 +08:00
|
|
|
return None
|
2013-04-12 21:02:12 +08:00
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
# There are two main cases we care about:
|
|
|
|
#
|
|
|
|
# Scripts: these should specify --wait always, maintaining the
|
|
|
|
# semantics of virt-install exit implying the domain has finished
|
|
|
|
# installing.
|
|
|
|
#
|
|
|
|
# Interactive: If this is a continue_inst domain, we default to
|
|
|
|
# waiting. Otherwise, we can exit before the domain has finished
|
|
|
|
# installing. Passing --wait will give the above semantics.
|
|
|
|
#
|
|
|
|
wait_on_install = continue_inst
|
|
|
|
wait_time = -1
|
2013-04-14 02:34:52 +08:00
|
|
|
if options.wait is not None:
|
2013-03-18 05:06:52 +08:00
|
|
|
wait_on_install = True
|
|
|
|
wait_time = options.wait * 60
|
|
|
|
|
|
|
|
# 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
|
|
|
|
|
|
|
|
# --wait 0 implies --noautoconsole
|
|
|
|
options.autoconsole = (wait_time != 0) and options.autoconsole or False
|
|
|
|
|
|
|
|
conscb = options.autoconsole and show_console or None
|
|
|
|
meter = (options.quiet and
|
|
|
|
progress.BaseMeter() or
|
|
|
|
progress.TextMeter(fo=sys.stdout))
|
|
|
|
logging.debug("Guest.has_install_phase: %s",
|
|
|
|
guest.installer.has_install_phase())
|
|
|
|
|
|
|
|
# we've got everything -- try to start the install
|
|
|
|
print_stdout(_("\nStarting install..."))
|
|
|
|
|
|
|
|
try:
|
|
|
|
start_time = time.time()
|
|
|
|
|
|
|
|
# Do first install phase
|
2013-08-09 23:25:05 +08:00
|
|
|
dom = guest.start_install(meter=meter, noboot=options.noreboot)
|
2013-08-10 03:00:16 +08:00
|
|
|
connect_console(dom, conscb, wait_on_console)
|
2013-03-18 05:06:52 +08:00
|
|
|
dom = check_domain(guest, dom, conscb,
|
|
|
|
wait_on_install, wait_time, start_time)
|
|
|
|
|
|
|
|
if continue_inst:
|
2013-08-09 23:25:05 +08:00
|
|
|
dom = guest.continue_install(meter=meter)
|
2013-08-10 03:00:16 +08:00
|
|
|
connect_console(dom, conscb, wait_on_console)
|
2013-03-18 05:06:52 +08:00
|
|
|
dom = check_domain(guest, dom, conscb,
|
|
|
|
wait_on_install, wait_time, start_time)
|
|
|
|
|
|
|
|
if options.noreboot or not guest.installer.has_install_phase():
|
|
|
|
print_stdout(
|
|
|
|
_("Domain creation completed. You can restart your domain by "
|
|
|
|
"running:\n %s") % cli.virsh_start_cmd(guest))
|
|
|
|
else:
|
|
|
|
print_stdout(
|
|
|
|
_("Guest installation complete... restarting guest."))
|
|
|
|
dom.create()
|
2013-08-10 03:00:16 +08:00
|
|
|
connect_console(dom, conscb, wait=True)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
logging.debug("", exc_info=True)
|
|
|
|
print_stderr(_("Domain install interrupted."))
|
|
|
|
raise
|
|
|
|
except RuntimeError, e:
|
|
|
|
fail(e)
|
|
|
|
except Exception, e:
|
|
|
|
fail(e, do_exit=False)
|
|
|
|
cli.install_fail(guest)
|
|
|
|
|
2013-04-14 02:34:52 +08:00
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
def check_domain(guest, dom, conscb, wait_for_install, wait_time, start_time):
|
|
|
|
"""
|
|
|
|
Make sure domain ends up in expected state, and wait if for install
|
|
|
|
to complete if requested
|
|
|
|
"""
|
|
|
|
wait_forever = (wait_time < 0)
|
|
|
|
|
|
|
|
# Wait a bit so info is accurate
|
|
|
|
def check_domain_state():
|
|
|
|
dominfo = dom.info()
|
|
|
|
state = dominfo[0]
|
|
|
|
|
2013-08-10 03:00:16 +08:00
|
|
|
if domain_is_crashed(guest.domain):
|
2013-03-18 05:06:52 +08:00
|
|
|
fail(_("Domain has crashed."))
|
|
|
|
|
2013-08-10 03:00:16 +08:00
|
|
|
if domain_is_shutdown(guest.domain):
|
2013-03-18 05:06:52 +08:00
|
|
|
return dom, state
|
|
|
|
|
|
|
|
return None, state
|
|
|
|
|
|
|
|
do_sleep = bool(conscb)
|
|
|
|
try:
|
|
|
|
ret, state = check_domain_state()
|
|
|
|
if ret:
|
|
|
|
return ret
|
|
|
|
except Exception, e:
|
|
|
|
# Sometimes we see errors from libvirt here due to races
|
|
|
|
logging.exception(e)
|
|
|
|
do_sleep = True
|
|
|
|
|
|
|
|
if do_sleep:
|
|
|
|
# Sleep a bit and try again to be sure the HV has caught up
|
|
|
|
time.sleep(2)
|
|
|
|
|
|
|
|
ret, state = check_domain_state()
|
|
|
|
if ret:
|
|
|
|
return ret
|
|
|
|
|
|
|
|
# Domain seems to be running
|
|
|
|
logging.debug("Domain state after install: %s", state)
|
|
|
|
|
|
|
|
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 guest.installer.has_install_phase():
|
|
|
|
return dom
|
|
|
|
|
|
|
|
print_stdout(
|
|
|
|
_("Domain installation still in progress. You can reconnect"
|
|
|
|
" to \nthe console to complete the installation process."))
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
timestr = (not wait_forever and
|
|
|
|
_("%d minutes ") % (int(wait_time) / 60) or "")
|
|
|
|
print_stdout(
|
2013-09-21 00:10:34 +08:00
|
|
|
_("Domain installation still in progress. Waiting "
|
|
|
|
"%(time_string)s for installation to complete.") %
|
|
|
|
{"time_string": timestr})
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
# Wait loop
|
|
|
|
while True:
|
2013-08-10 03:00:16 +08:00
|
|
|
if domain_is_shutdown(guest.domain):
|
2013-03-18 05:06:52 +08:00
|
|
|
print_stdout(_("Domain has shutdown. Continuing."))
|
|
|
|
try:
|
|
|
|
# Lookup a new domain object incase current
|
|
|
|
# one returned bogus data (see comment in
|
2013-08-10 03:00:16 +08:00
|
|
|
# domain_is_shutdown)
|
2013-03-18 05:06:52 +08:00
|
|
|
dom = guest.conn.lookupByName(guest.name)
|
|
|
|
except Exception, e:
|
|
|
|
raise RuntimeError(_("Could not lookup domain after "
|
|
|
|
"install: %s" % str(e)))
|
|
|
|
break
|
|
|
|
|
|
|
|
time_elapsed = (time.time() - start_time)
|
|
|
|
if not wait_forever and time_elapsed >= wait_time:
|
|
|
|
print_stdout(
|
|
|
|
_("Installation has exceeded specified time limit. "
|
|
|
|
"Exiting application."))
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
time.sleep(2)
|
|
|
|
|
|
|
|
return dom
|
|
|
|
|
|
|
|
|
|
|
|
########################
|
|
|
|
# XML printing helpers #
|
|
|
|
########################
|
|
|
|
|
|
|
|
def xml_to_print(guest, continue_inst, xmlonly, xmlstep, dry):
|
|
|
|
start_xml, final_xml = guest.start_install(dry=dry, return_xml=True)
|
|
|
|
second_xml = None
|
|
|
|
if not start_xml:
|
|
|
|
start_xml = final_xml
|
|
|
|
final_xml = None
|
|
|
|
|
|
|
|
if continue_inst:
|
|
|
|
second_xml, final_xml = guest.continue_install(dry=dry,
|
|
|
|
return_xml=True)
|
|
|
|
|
|
|
|
if dry and not (xmlonly or xmlstep):
|
|
|
|
print_stdout(_("Dry run completed successfully"))
|
|
|
|
return
|
|
|
|
|
|
|
|
# --print-xml
|
|
|
|
if xmlonly and not xmlstep:
|
|
|
|
if second_xml or final_xml:
|
|
|
|
fail(_("--print-xml can only be used with guests that do not have "
|
|
|
|
"an installation phase (--import, --boot, etc.). To see all"
|
|
|
|
" generated XML, please use --print-step all."))
|
|
|
|
return start_xml
|
|
|
|
|
|
|
|
# --print-step
|
|
|
|
if xmlstep == "1":
|
|
|
|
return start_xml
|
|
|
|
if xmlstep == "2":
|
|
|
|
if not (second_xml or final_xml):
|
|
|
|
fail(_("Requested installation does not have XML step 2"))
|
|
|
|
return second_xml or final_xml
|
|
|
|
if xmlstep == "3":
|
|
|
|
if not second_xml:
|
|
|
|
fail(_("Requested installation does not have XML step 3"))
|
|
|
|
return final_xml
|
|
|
|
|
|
|
|
# "all" case
|
|
|
|
xml = start_xml
|
|
|
|
if second_xml:
|
|
|
|
xml += second_xml
|
|
|
|
if final_xml:
|
|
|
|
xml += final_xml
|
|
|
|
return xml
|
|
|
|
|
|
|
|
|
|
|
|
#######################
|
|
|
|
# CLI option handling #
|
|
|
|
#######################
|
|
|
|
|
|
|
|
def parse_args():
|
2013-07-01 03:03:53 +08:00
|
|
|
parser = cli.setupParser(
|
2014-01-19 06:01:43 +08:00
|
|
|
"%(prog)s --name NAME --ram RAM STORAGE INSTALL [options]",
|
2014-01-22 22:06:35 +08:00
|
|
|
_("Create a new virtual machine from specified install media."),
|
|
|
|
introspection_epilog=True)
|
2013-03-18 05:06:52 +08:00
|
|
|
cli.add_connect_option(parser)
|
|
|
|
|
2014-01-19 06:01:43 +08:00
|
|
|
geng = parser.add_argument_group(_("General Options"))
|
2014-01-21 07:04:23 +08:00
|
|
|
geng.add_argument("-n", "--name",
|
2013-03-18 05:06:52 +08:00
|
|
|
help=_("Name of the guest instance"))
|
2014-01-25 07:56:59 +08:00
|
|
|
geng.add_argument("-u", "--uuid", help=argparse.SUPPRESS)
|
|
|
|
cli.add_memory_option(geng, backcompat=True)
|
2013-03-18 05:06:52 +08:00
|
|
|
cli.vcpu_cli_options(geng)
|
2014-01-21 07:04:23 +08:00
|
|
|
geng.add_argument("--description",
|
2013-03-18 05:06:52 +08:00
|
|
|
help=_("Human readable description of the VM to store in "
|
|
|
|
"the generated XML."))
|
2014-01-22 05:26:35 +08:00
|
|
|
cli.add_guest_xml_options(geng)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2014-01-19 06:01:43 +08:00
|
|
|
insg = parser.add_argument_group(_("Installation Method Options"))
|
|
|
|
insg.add_argument("-c", dest="cdrom_short", help=argparse.SUPPRESS)
|
2014-01-21 07:04:23 +08:00
|
|
|
insg.add_argument("--cdrom", help=_("CD-ROM installation media"))
|
|
|
|
insg.add_argument("-l", "--location",
|
2013-03-18 05:06:52 +08:00
|
|
|
help=_("Installation source (eg, nfs:host:/path, "
|
|
|
|
"http://host/path, ftp://host/path)"))
|
2014-01-21 07:04:23 +08:00
|
|
|
insg.add_argument("--pxe", action="store_true",
|
2013-03-18 05:06:52 +08:00
|
|
|
help=_("Boot from the network using the PXE protocol"))
|
2014-01-19 06:01:43 +08:00
|
|
|
insg.add_argument("--import", action="store_true", dest="import_install",
|
2013-03-18 05:06:52 +08:00
|
|
|
help=_("Build guest around an existing disk image"))
|
2014-01-21 07:04:23 +08:00
|
|
|
insg.add_argument("--livecd", action="store_true",
|
2013-03-18 05:06:52 +08:00
|
|
|
help=_("Treat the CD-ROM media as a Live CD"))
|
2014-01-21 07:04:23 +08:00
|
|
|
insg.add_argument("-x", "--extra-args",
|
2013-03-18 05:06:52 +08:00
|
|
|
help=_("Additional arguments to pass to the install kernel "
|
|
|
|
"booted from --location"))
|
2014-01-21 07:04:23 +08:00
|
|
|
insg.add_argument("--initrd-inject", action="append",
|
2013-03-18 05:06:52 +08:00
|
|
|
help=_("Add given file to root of initrd from --location"))
|
2013-08-11 06:17:37 +08:00
|
|
|
cli.add_distro_options(insg)
|
2014-01-22 05:26:35 +08:00
|
|
|
cli.add_boot_option(insg)
|
2014-01-22 04:36:34 +08:00
|
|
|
insg.add_argument("--init", help=argparse.SUPPRESS)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2014-01-19 06:01:43 +08:00
|
|
|
stog = parser.add_argument_group(_("Storage Configuration"))
|
2014-01-22 05:26:35 +08:00
|
|
|
cli.add_disk_option(stog)
|
2014-01-19 06:01:43 +08:00
|
|
|
stog.add_argument("--nodisks", action="store_true",
|
2013-03-18 05:06:52 +08:00
|
|
|
help=_("Don't set up any disks for the guest."))
|
|
|
|
cli.add_fs_option(stog)
|
|
|
|
|
|
|
|
# Deprecated storage options
|
2014-01-19 06:01:43 +08:00
|
|
|
stog.add_argument("-f", "--file", dest="file_paths", action="append",
|
|
|
|
help=argparse.SUPPRESS)
|
|
|
|
stog.add_argument("-s", "--file-size", type=float,
|
2013-03-18 05:06:52 +08:00
|
|
|
action="append", dest="disksize",
|
2014-01-19 06:01:43 +08:00
|
|
|
help=argparse.SUPPRESS)
|
|
|
|
stog.add_argument("--nonsparse", action="store_false",
|
2013-03-18 05:06:52 +08:00
|
|
|
default=True, dest="sparse",
|
2014-01-19 06:01:43 +08:00
|
|
|
help=argparse.SUPPRESS)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
netg = cli.network_option_group(parser)
|
|
|
|
|
2014-01-19 06:01:43 +08:00
|
|
|
netg.add_argument("--nonetworks", action="store_true",
|
2013-03-18 05:06:52 +08:00
|
|
|
help=_("Don't create network interfaces for the guest."))
|
|
|
|
|
|
|
|
vncg = cli.graphics_option_group(parser)
|
2014-01-19 06:01:43 +08:00
|
|
|
vncg.add_argument("--noautoconsole", action="store_false",
|
2013-03-18 05:06:52 +08:00
|
|
|
dest="autoconsole", default=True,
|
|
|
|
help=_("Don't automatically try to connect to the guest "
|
|
|
|
"console"))
|
|
|
|
|
2014-01-19 06:01:43 +08:00
|
|
|
devg = parser.add_argument_group(_("Device Options"))
|
2013-03-18 05:06:52 +08:00
|
|
|
cli.add_device_options(devg)
|
|
|
|
|
|
|
|
# Deprecated
|
2014-01-19 06:01:43 +08:00
|
|
|
devg.add_argument("--sound", action="store_true", dest="old_sound_bool",
|
|
|
|
default=False, help=argparse.SUPPRESS)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2014-01-19 06:01:43 +08:00
|
|
|
virg = parser.add_argument_group(_("Virtualization Platform Options"))
|
|
|
|
virg.add_argument("-v", "--hvm", action="store_true", dest="fullvirt",
|
2013-03-18 05:06:52 +08:00
|
|
|
help=_("This guest should be a fully virtualized guest"))
|
2014-01-21 07:04:23 +08:00
|
|
|
virg.add_argument("-p", "--paravirt", action="store_true",
|
2013-03-18 05:06:52 +08:00
|
|
|
help=_("This guest should be a paravirtualized guest"))
|
2014-01-19 06:01:43 +08:00
|
|
|
virg.add_argument("--container", action="store_true", default=False,
|
2013-03-18 05:06:52 +08:00
|
|
|
help=_("This guest should be a container guest"))
|
2014-01-19 06:01:43 +08:00
|
|
|
virg.add_argument("--virt-type", dest="hv_type",
|
2013-03-18 05:06:52 +08:00
|
|
|
default="",
|
|
|
|
help=_("Hypervisor name to use (kvm, qemu, xen, ...)"))
|
2014-01-19 06:01:43 +08:00
|
|
|
virg.add_argument("--accelerate", action="store_true", default=False,
|
2014-01-21 07:04:23 +08:00
|
|
|
help=argparse.SUPPRESS)
|
|
|
|
virg.add_argument("--arch",
|
2013-03-18 05:06:52 +08:00
|
|
|
help=_("The CPU architecture to simulate"))
|
2014-01-21 07:04:23 +08:00
|
|
|
virg.add_argument("--machine",
|
2013-03-18 05:06:52 +08:00
|
|
|
help=_("The machine type to emulate"))
|
2013-09-28 21:36:11 +08:00
|
|
|
cli.add_old_feature_options(virg)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2014-01-19 06:01:43 +08:00
|
|
|
misc = parser.add_argument_group(_("Miscellaneous Options"))
|
|
|
|
misc.add_argument("--autostart", action="store_true", dest="autostart",
|
2013-03-18 05:06:52 +08:00
|
|
|
default=False,
|
|
|
|
help=_("Have domain autostart on host boot up."))
|
2014-01-19 06:01:43 +08:00
|
|
|
misc.add_argument("--wait", type=int, dest="wait",
|
2013-09-28 23:27:26 +08:00
|
|
|
help=_("Minutes to wait for install to complete."))
|
|
|
|
|
|
|
|
cli.add_misc_options(misc, prompt=True, printxml=True, printstep=True,
|
|
|
|
noreboot=True, dryrun=True)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2014-01-19 06:01:43 +08:00
|
|
|
return parser.parse_args()
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
|
|
|
|
###################
|
|
|
|
# main() handling #
|
|
|
|
###################
|
|
|
|
|
|
|
|
def main(conn=None):
|
|
|
|
cli.earlyLogging()
|
2014-01-19 06:01:43 +08:00
|
|
|
options = parse_args()
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
# Default setup options
|
|
|
|
options.quiet = options.xmlstep or options.xmlonly or options.quiet
|
|
|
|
|
|
|
|
cli.setupLogging("virt-install", options.debug, options.quiet)
|
|
|
|
|
2013-04-22 00:28:14 +08:00
|
|
|
check_cdrom_option_error(options)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
if options.distro_variant == "list":
|
|
|
|
logging.debug("OS list requested")
|
2013-08-12 02:52:30 +08:00
|
|
|
for t in virtinst.osdict.list_os(list_types=True):
|
|
|
|
for v in virtinst.osdict.list_os(typename=t.name):
|
|
|
|
print "%-20s : %s" % (v.name, v.label)
|
2013-03-18 05:06:52 +08:00
|
|
|
return 0
|
|
|
|
|
|
|
|
cli.set_force(options.force)
|
|
|
|
cli.set_prompt(options.prompt)
|
|
|
|
|
2014-01-22 22:06:35 +08:00
|
|
|
parsermap = cli.build_parser_map(options)
|
|
|
|
if cli.check_option_introspection(options, parsermap):
|
|
|
|
return 0
|
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
if conn is None:
|
|
|
|
conn = cli.getConnection(options.connect)
|
|
|
|
|
|
|
|
if options.xmlstep not in [None, "1", "2", "3", "all"]:
|
|
|
|
fail(_("--print-step must be 1, 2, 3, or all"))
|
|
|
|
|
2014-01-22 22:06:35 +08:00
|
|
|
guest = build_guest_instance(conn, options, parsermap)
|
2013-03-18 05:06:52 +08:00
|
|
|
continue_inst = guest.get_continue_inst()
|
|
|
|
|
|
|
|
if options.xmlstep or options.xmlonly or options.dry:
|
|
|
|
xml = xml_to_print(guest, continue_inst,
|
|
|
|
options.xmlonly, options.xmlstep, options.dry)
|
|
|
|
if xml:
|
|
|
|
print_stdout(xml, do_force=True)
|
|
|
|
else:
|
|
|
|
start_install(guest, continue_inst, options)
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
try:
|
|
|
|
sys.exit(main())
|
|
|
|
except SystemExit, sys_e:
|
|
|
|
sys.exit(sys_e.code)
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
logging.debug("", exc_info=True)
|
|
|
|
print_stderr(_("Installation aborted at user request"))
|
|
|
|
except Exception, main_e:
|
|
|
|
fail(main_e)
|