2013-03-18 05:06:52 +08:00
|
|
|
#
|
|
|
|
# Common code for all guests
|
|
|
|
#
|
2015-02-24 20:32:51 +08:00
|
|
|
# Copyright 2006-2009, 2013, 2014, 2015 Red Hat, Inc.
|
2013-03-18 05:06:52 +08:00
|
|
|
# Jeremy Katz <katzj@redhat.com>
|
|
|
|
#
|
|
|
|
# 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
|
2013-10-28 04:59:47 +08:00
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
2013-03-18 05:06:52 +08:00
|
|
|
#
|
|
|
|
# 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.
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
|
|
|
import urlgrabber.progress as progress
|
|
|
|
import libvirt
|
|
|
|
|
2013-10-03 06:06:52 +08:00
|
|
|
from virtcli import cliconfig
|
|
|
|
|
2014-09-13 03:59:22 +08:00
|
|
|
from . import osdict
|
|
|
|
from . import util
|
|
|
|
from . import support
|
|
|
|
from .clock import Clock
|
|
|
|
from .cpu import CPU
|
|
|
|
from .device import VirtualDevice
|
|
|
|
from .deviceaudio import VirtualAudio
|
|
|
|
from .devicechar import VirtualChannelDevice, VirtualConsoleDevice
|
|
|
|
from .devicecontroller import VirtualController
|
|
|
|
from .devicegraphics import VirtualGraphics
|
|
|
|
from .deviceinput import VirtualInputDevice
|
|
|
|
from .deviceredirdev import VirtualRedirDevice
|
|
|
|
from .devicevideo import VirtualVideoDevice
|
|
|
|
from .distroinstaller import DistroInstaller
|
|
|
|
from .domainblkiotune import DomainBlkiotune
|
|
|
|
from .domainfeatures import DomainFeatures
|
|
|
|
from .domainmemorybacking import DomainMemorybacking
|
|
|
|
from .domainmemorytune import DomainMemorytune
|
|
|
|
from .domainnumatune import DomainNumatune
|
|
|
|
from .domainresource import DomainResource
|
2015-02-19 04:16:48 +08:00
|
|
|
from .domcapabilities import DomainCapabilities
|
2014-09-13 03:59:22 +08:00
|
|
|
from .idmap import IdMap
|
|
|
|
from .osxml import OSXML
|
|
|
|
from .pm import PM
|
|
|
|
from .seclabel import Seclabel
|
|
|
|
from .xmlbuilder import XMLBuilder, XMLProperty, XMLChildProperty
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
|
2013-07-14 06:56:09 +08:00
|
|
|
class Guest(XMLBuilder):
|
2013-08-10 02:16:59 +08:00
|
|
|
@staticmethod
|
|
|
|
def check_vm_collision(conn, name, do_remove):
|
|
|
|
"""
|
|
|
|
Remove the existing VM with the same name if requested, or error
|
|
|
|
if there is a collision.
|
|
|
|
"""
|
|
|
|
vm = None
|
|
|
|
try:
|
|
|
|
vm = conn.lookupByName(name)
|
|
|
|
except libvirt.libvirtError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
if vm is None:
|
|
|
|
return
|
|
|
|
|
|
|
|
if not do_remove:
|
|
|
|
raise RuntimeError(_("Domain named %s already exists!") % name)
|
|
|
|
|
|
|
|
try:
|
|
|
|
logging.debug("Explicitly replacing guest '%s'", name)
|
|
|
|
if vm.ID() != -1:
|
|
|
|
logging.info("Destroying guest '%s'", name)
|
|
|
|
vm.destroy()
|
|
|
|
|
|
|
|
logging.info("Undefining guest '%s'", name)
|
|
|
|
vm.undefine()
|
|
|
|
except libvirt.libvirtError, e:
|
|
|
|
raise RuntimeError(_("Could not remove old vm '%s': %s") %
|
|
|
|
(str(e)))
|
|
|
|
|
|
|
|
@staticmethod
|
2014-01-18 07:44:26 +08:00
|
|
|
def validate_name(conn, name, check_collision, validate=True):
|
|
|
|
if validate:
|
|
|
|
util.validate_name(_("Guest"), name)
|
2013-08-10 02:16:59 +08:00
|
|
|
if not check_collision:
|
|
|
|
return
|
|
|
|
|
|
|
|
try:
|
|
|
|
conn.lookupByName(name)
|
|
|
|
except:
|
|
|
|
return
|
|
|
|
raise ValueError(_("Guest name '%s' is already in use.") % name)
|
|
|
|
|
2013-07-18 05:58:24 +08:00
|
|
|
|
2013-09-11 23:47:09 +08:00
|
|
|
_XML_ROOT_NAME = "domain"
|
2013-09-24 18:56:16 +08:00
|
|
|
_XML_PROP_ORDER = ["type", "name", "uuid", "title", "description",
|
2014-06-05 08:52:24 +08:00
|
|
|
"maxmemory", "memory", "blkiotune", "memtune", "memoryBacking",
|
|
|
|
"vcpus", "curvcpus", "numatune", "bootloader", "os", "idmap", "features",
|
|
|
|
"cpu", "clock", "on_poweroff", "on_reboot", "on_crash", "resource", "pm",
|
2014-03-23 00:25:28 +08:00
|
|
|
"emulator", "_devices", "seclabel"]
|
2013-07-18 05:58:24 +08:00
|
|
|
|
2013-09-11 23:47:09 +08:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
XMLBuilder.__init__(self, *args, **kwargs)
|
2013-08-09 23:04:01 +08:00
|
|
|
|
2013-07-18 05:58:24 +08:00
|
|
|
self.autostart = False
|
|
|
|
self.replace = False
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-10-03 06:06:52 +08:00
|
|
|
# Allow virt-manager to override the default graphics type
|
|
|
|
self.default_graphics_type = cliconfig.default_graphics
|
|
|
|
|
2013-10-06 21:41:37 +08:00
|
|
|
self.skip_default_console = False
|
|
|
|
self.skip_default_channel = False
|
2014-02-06 01:49:16 +08:00
|
|
|
self.skip_default_sound = False
|
2014-02-06 01:58:53 +08:00
|
|
|
self.skip_default_usbredir = False
|
2014-02-08 05:07:32 +08:00
|
|
|
self.skip_default_graphics = False
|
2014-02-06 02:51:53 +08:00
|
|
|
self.x86_cpu_default = self.cpu.SPECIAL_MODE_HOST_MODEL_ONLY
|
2013-10-06 21:41:37 +08:00
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
self._os_variant = None
|
2013-07-26 02:36:13 +08:00
|
|
|
self._random_uuid = None
|
2013-09-11 06:32:10 +08:00
|
|
|
self._install_devices = []
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
# The libvirt virDomain object we 'Create'
|
|
|
|
self.domain = None
|
|
|
|
|
2014-09-13 03:59:22 +08:00
|
|
|
self.installer = DistroInstaller(self.conn)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
|
|
|
|
######################
|
|
|
|
# Property accessors #
|
|
|
|
######################
|
|
|
|
|
2013-07-18 05:58:24 +08:00
|
|
|
def _validate_name(self, val):
|
|
|
|
if val == self.name:
|
|
|
|
return
|
2013-08-10 02:16:59 +08:00
|
|
|
self.validate_name(self.conn, val, check_collision=not self.replace)
|
2013-09-20 01:27:30 +08:00
|
|
|
name = XMLProperty("./name", validate_cb=_validate_name)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-07-18 05:58:24 +08:00
|
|
|
def _set_memory(self, val):
|
|
|
|
if val is None:
|
|
|
|
return None
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
if self.maxmemory is None or self.maxmemory < val:
|
|
|
|
self.maxmemory = val
|
|
|
|
return val
|
2013-09-20 01:27:30 +08:00
|
|
|
memory = XMLProperty("./currentMemory", is_int=True,
|
2013-07-18 05:58:24 +08:00
|
|
|
default_cb=lambda s: 1,
|
|
|
|
set_converter=_set_memory)
|
2013-09-20 01:27:30 +08:00
|
|
|
maxmemory = XMLProperty("./memory", is_int=True)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-07-18 05:58:24 +08:00
|
|
|
def _set_vcpus(self, val):
|
|
|
|
if val is None:
|
|
|
|
return None
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-07-18 05:58:24 +08:00
|
|
|
# Don't force set curvcpus unless already specified
|
|
|
|
if self.curvcpus is not None and self.curvcpus > val:
|
|
|
|
self.curvcpus = val
|
|
|
|
return val
|
2013-09-20 01:27:30 +08:00
|
|
|
vcpus = XMLProperty("./vcpu", is_int=True,
|
2013-07-18 05:58:24 +08:00
|
|
|
set_converter=_set_vcpus,
|
|
|
|
default_cb=lambda s: 1)
|
2013-09-20 01:27:30 +08:00
|
|
|
curvcpus = XMLProperty("./vcpu/@current", is_int=True)
|
2013-07-18 05:58:24 +08:00
|
|
|
|
|
|
|
def _validate_cpuset(self, val):
|
2013-03-18 05:06:52 +08:00
|
|
|
DomainNumatune.validate_cpuset(self.conn, val)
|
2013-09-20 01:27:30 +08:00
|
|
|
cpuset = XMLProperty("./vcpu/@cpuset",
|
2013-07-18 05:58:24 +08:00
|
|
|
validate_cb=_validate_cpuset)
|
|
|
|
|
2013-07-26 02:36:13 +08:00
|
|
|
def _get_default_uuid(self):
|
|
|
|
if self._random_uuid is None:
|
|
|
|
self._random_uuid = util.generate_uuid(self.conn)
|
|
|
|
return self._random_uuid
|
2013-09-20 01:27:30 +08:00
|
|
|
uuid = XMLProperty("./uuid",
|
2013-07-26 02:36:13 +08:00
|
|
|
validate_cb=lambda s, v: util.validate_uuid(v),
|
|
|
|
default_cb=_get_default_uuid)
|
|
|
|
|
2013-09-24 09:27:42 +08:00
|
|
|
id = XMLProperty("./@id", is_int=True)
|
2013-09-20 01:27:30 +08:00
|
|
|
type = XMLProperty("./@type", default_cb=lambda s: "xen")
|
|
|
|
bootloader = XMLProperty("./bootloader")
|
|
|
|
description = XMLProperty("./description")
|
2013-09-24 18:56:16 +08:00
|
|
|
title = XMLProperty("./title")
|
2013-09-20 01:27:30 +08:00
|
|
|
emulator = XMLProperty("./devices/emulator")
|
2013-07-18 05:58:24 +08:00
|
|
|
|
2013-09-20 01:27:30 +08:00
|
|
|
on_poweroff = XMLProperty("./on_poweroff",
|
2013-07-18 05:58:24 +08:00
|
|
|
default_cb=lambda s: "destroy")
|
2013-09-20 01:27:30 +08:00
|
|
|
on_reboot = XMLProperty("./on_reboot")
|
|
|
|
on_crash = XMLProperty("./on_crash")
|
2013-07-18 05:58:24 +08:00
|
|
|
|
2013-09-11 23:47:09 +08:00
|
|
|
os = XMLChildProperty(OSXML, is_single=True)
|
|
|
|
features = XMLChildProperty(DomainFeatures, is_single=True)
|
|
|
|
clock = XMLChildProperty(Clock, is_single=True)
|
|
|
|
seclabel = XMLChildProperty(Seclabel, is_single=True)
|
|
|
|
cpu = XMLChildProperty(CPU, is_single=True)
|
|
|
|
numatune = XMLChildProperty(DomainNumatune, is_single=True)
|
2014-02-03 05:12:29 +08:00
|
|
|
pm = XMLChildProperty(PM, is_single=True)
|
2014-02-06 22:41:00 +08:00
|
|
|
blkiotune = XMLChildProperty(DomainBlkiotune, is_single=True)
|
2014-02-25 10:02:31 +08:00
|
|
|
memtune = XMLChildProperty(DomainMemorytune, is_single=True)
|
2014-03-22 18:15:46 +08:00
|
|
|
memoryBacking = XMLChildProperty(DomainMemorybacking, is_single=True)
|
2014-02-09 23:25:25 +08:00
|
|
|
idmap = XMLChildProperty(IdMap, is_single=True)
|
2014-06-04 10:08:05 +08:00
|
|
|
resource = XMLChildProperty(DomainResource, is_single=True)
|
2013-09-11 23:47:09 +08:00
|
|
|
|
2013-07-18 05:58:24 +08:00
|
|
|
|
|
|
|
###############################
|
|
|
|
# Distro detection properties #
|
|
|
|
###############################
|
|
|
|
|
2013-08-11 06:48:43 +08:00
|
|
|
def _get_os_variant(self):
|
2013-03-18 05:06:52 +08:00
|
|
|
return self._os_variant
|
2013-08-11 06:48:43 +08:00
|
|
|
def _set_os_variant(self, val):
|
2014-09-07 23:57:04 +08:00
|
|
|
if val:
|
|
|
|
val = val.lower()
|
|
|
|
if osdict.lookup_os(val) is None:
|
|
|
|
raise ValueError(
|
|
|
|
_("Distro '%s' does not exist in our dictionary") % val)
|
|
|
|
|
2013-11-10 07:33:49 +08:00
|
|
|
logging.debug("Setting Guest.os_variant to '%s'", val)
|
2013-08-11 06:48:43 +08:00
|
|
|
self._os_variant = val
|
|
|
|
os_variant = property(_get_os_variant, _set_os_variant)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
|
|
|
|
########################################
|
|
|
|
# Device Add/Remove Public API methods #
|
|
|
|
########################################
|
|
|
|
|
2013-07-26 02:11:09 +08:00
|
|
|
def add_device(self, dev):
|
2013-03-18 05:06:52 +08:00
|
|
|
"""
|
|
|
|
Add the passed device to the guest's device list.
|
|
|
|
|
|
|
|
@param dev: VirtualDevice instance to attach to guest
|
|
|
|
"""
|
2013-09-10 07:45:19 +08:00
|
|
|
self._add_child(dev)
|
2013-07-24 20:46:55 +08:00
|
|
|
|
2013-09-11 06:32:10 +08:00
|
|
|
def remove_device(self, dev):
|
|
|
|
"""
|
|
|
|
Remove the passed device from the guest's device list
|
|
|
|
|
|
|
|
@param dev: VirtualDevice instance
|
|
|
|
"""
|
|
|
|
self._remove_child(dev)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
def get_devices(self, devtype):
|
|
|
|
"""
|
|
|
|
Return a list of devices of type 'devtype' that will installed on
|
|
|
|
the guest.
|
|
|
|
|
|
|
|
@param devtype: Device type to search for (one of
|
|
|
|
VirtualDevice.virtual_device_types)
|
|
|
|
"""
|
2013-09-11 06:32:10 +08:00
|
|
|
newlist = []
|
|
|
|
for i in self._devices:
|
|
|
|
if devtype == "all" or i.virtual_device_type == devtype:
|
|
|
|
newlist.append(i)
|
|
|
|
return newlist
|
|
|
|
_devices = XMLChildProperty(
|
|
|
|
[VirtualDevice.virtual_device_classes[_n]
|
2013-09-11 23:47:09 +08:00
|
|
|
for _n in VirtualDevice.virtual_device_types],
|
|
|
|
relative_xpath="./devices")
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
def get_all_devices(self):
|
|
|
|
"""
|
|
|
|
Return a list of all devices being installed with the guest
|
|
|
|
"""
|
|
|
|
retlist = []
|
|
|
|
for devtype in VirtualDevice.virtual_device_types:
|
|
|
|
retlist.extend(self.get_devices(devtype))
|
|
|
|
return retlist
|
|
|
|
|
|
|
|
|
|
|
|
############################
|
|
|
|
# Install Helper functions #
|
|
|
|
############################
|
|
|
|
|
|
|
|
def _prepare_install(self, meter, dry=False):
|
2013-07-25 02:37:07 +08:00
|
|
|
for dev in self._install_devices:
|
|
|
|
self.remove_device(dev)
|
2013-03-18 05:06:52 +08:00
|
|
|
self._install_devices = []
|
|
|
|
ignore = dry
|
|
|
|
|
|
|
|
# Fetch install media, prepare installer devices
|
2014-09-08 01:35:34 +08:00
|
|
|
self.installer.prepare(self, meter)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
# Initialize install device list
|
2013-07-17 04:47:08 +08:00
|
|
|
for dev in self.installer.install_devices:
|
2013-07-25 02:37:07 +08:00
|
|
|
self.add_device(dev)
|
2013-03-18 05:06:52 +08:00
|
|
|
self._install_devices.append(dev)
|
|
|
|
|
2013-07-18 05:58:24 +08:00
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
##############
|
|
|
|
# Public API #
|
|
|
|
##############
|
|
|
|
|
2013-07-18 05:58:24 +08:00
|
|
|
def _prepare_get_xml(self):
|
2013-07-18 01:06:01 +08:00
|
|
|
# We do a shallow copy of the device list here, and set the defaults.
|
|
|
|
# This way, default changes aren't persistent, and we don't need
|
|
|
|
# to worry about when to call set_defaults
|
2013-09-11 23:47:09 +08:00
|
|
|
#
|
|
|
|
# XXX: this is hacky, we should find a way to use xmlbuilder.copy(),
|
|
|
|
# but need to make sure it's not a massive performance hit
|
2013-09-11 06:32:10 +08:00
|
|
|
data = (self._devices[:], self.features, self.os)
|
2013-07-18 01:06:01 +08:00
|
|
|
try:
|
2013-09-11 06:32:10 +08:00
|
|
|
self._propstore["_devices"] = [dev.copy() for dev in self._devices]
|
2013-09-11 23:47:09 +08:00
|
|
|
self._propstore["features"] = self.features.copy()
|
|
|
|
self._propstore["os"] = self.os.copy()
|
2013-07-18 05:58:24 +08:00
|
|
|
except:
|
|
|
|
self._finish_get_xml(data)
|
|
|
|
raise
|
|
|
|
return data
|
|
|
|
|
|
|
|
def _finish_get_xml(self, data):
|
2013-09-11 23:47:09 +08:00
|
|
|
(self._propstore["_devices"],
|
|
|
|
self._propstore["features"],
|
|
|
|
self._propstore["os"]) = data
|
2013-07-18 01:06:01 +08:00
|
|
|
|
2013-07-24 23:32:30 +08:00
|
|
|
def get_install_xml(self, *args, **kwargs):
|
|
|
|
data = self._prepare_get_xml()
|
|
|
|
try:
|
|
|
|
return self._do_get_install_xml(*args, **kwargs)
|
|
|
|
finally:
|
|
|
|
self._finish_get_xml(data)
|
|
|
|
|
|
|
|
def _do_get_install_xml(self, install=True, disk_boot=False):
|
2013-03-18 05:06:52 +08:00
|
|
|
"""
|
|
|
|
Return the full Guest xml configuration.
|
|
|
|
|
|
|
|
@param install: Whether we want the 'OS install' configuration or
|
2014-09-08 01:42:56 +08:00
|
|
|
the 'post-install' configuration. (Some installs,
|
|
|
|
like an import or livecd may not have an 'install'
|
2013-03-18 05:06:52 +08:00
|
|
|
config.)
|
|
|
|
@type install: C{bool}
|
|
|
|
@param disk_boot: Whether we should boot off the harddisk, regardless
|
|
|
|
of our position in the install process (this is
|
|
|
|
used for 2 stage installs, where the second stage
|
|
|
|
boots off the disk. You probably don't need to touch
|
|
|
|
this.)
|
|
|
|
@type disk_boot: C{bool}
|
|
|
|
"""
|
2013-07-18 01:06:01 +08:00
|
|
|
osblob_install = install and not disk_boot
|
2013-03-18 05:06:52 +08:00
|
|
|
if osblob_install and not self.installer.has_install_phase():
|
|
|
|
return None
|
|
|
|
|
2013-07-18 05:58:24 +08:00
|
|
|
self.installer.alter_bootconfig(self, osblob_install, self.os)
|
|
|
|
self._set_transient_device_defaults(install)
|
|
|
|
|
|
|
|
action = install and "destroy" or "restart"
|
|
|
|
self.on_reboot = action
|
|
|
|
self.on_crash = action
|
|
|
|
|
2013-07-24 23:32:30 +08:00
|
|
|
self._set_defaults()
|
|
|
|
|
2013-07-18 05:58:24 +08:00
|
|
|
self.bootloader = None
|
2013-07-18 01:06:01 +08:00
|
|
|
if (not install and
|
|
|
|
self.os.is_xenpv() and
|
|
|
|
not self.os.kernel):
|
2013-07-18 05:58:24 +08:00
|
|
|
self.bootloader = "/usr/bin/pygrub"
|
|
|
|
self.os.clear()
|
|
|
|
|
2013-07-24 23:32:30 +08:00
|
|
|
return self.get_xml_config()
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
def get_continue_inst(self):
|
|
|
|
"""
|
|
|
|
Return True if this guest requires a call to 'continue_install',
|
|
|
|
which means the OS requires a 2 stage install (windows)
|
|
|
|
"""
|
|
|
|
# If we are doing an 'import' or 'liveCD' install, there is
|
|
|
|
# no true install process, so continue install has no meaning
|
|
|
|
if not self.installer.has_install_phase():
|
|
|
|
return False
|
|
|
|
|
2013-08-12 03:29:31 +08:00
|
|
|
return self._lookup_osdict_key("three_stage_install", False)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-07-26 02:36:13 +08:00
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
##########################
|
|
|
|
# Actual install methods #
|
|
|
|
##########################
|
|
|
|
|
2013-08-10 02:16:59 +08:00
|
|
|
def start_install(self, meter=None,
|
2013-08-09 23:25:05 +08:00
|
|
|
dry=False, return_xml=False, noboot=False):
|
2013-03-18 05:06:52 +08:00
|
|
|
"""
|
|
|
|
Begin the guest install (stage1).
|
|
|
|
@param return_xml: Don't create the guest, just return generated XML
|
|
|
|
"""
|
2013-07-14 07:43:52 +08:00
|
|
|
if self.domain is not None:
|
|
|
|
raise RuntimeError(_("Domain has already been started!"))
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-07-14 07:43:52 +08:00
|
|
|
is_initial = True
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
self._prepare_install(meter, dry)
|
|
|
|
try:
|
|
|
|
# Create devices if required (disk images, etc.)
|
|
|
|
if not dry:
|
2013-08-15 07:43:02 +08:00
|
|
|
for dev in self.get_all_devices():
|
|
|
|
dev.setup(meter)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
start_xml, final_xml = self._build_xml(is_initial)
|
|
|
|
if return_xml:
|
|
|
|
return (start_xml, final_xml)
|
|
|
|
if dry:
|
|
|
|
return
|
|
|
|
|
|
|
|
# Remove existing VM if requested
|
2013-08-10 02:16:59 +08:00
|
|
|
self.check_vm_collision(self.conn, self.name,
|
|
|
|
do_remove=self.replace)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-08-09 23:25:05 +08:00
|
|
|
self.domain = self._create_guest(meter,
|
2013-03-18 05:06:52 +08:00
|
|
|
start_xml, final_xml, is_initial,
|
|
|
|
noboot)
|
|
|
|
# Set domain autostart flag if requested
|
|
|
|
self._flag_autostart()
|
|
|
|
|
|
|
|
return self.domain
|
|
|
|
finally:
|
2013-08-15 07:43:02 +08:00
|
|
|
self.installer.cleanup()
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-08-09 23:25:05 +08:00
|
|
|
def continue_install(self, meter=None,
|
2013-03-18 05:06:52 +08:00
|
|
|
dry=False, return_xml=False):
|
|
|
|
"""
|
|
|
|
Continue with stage 2 of a guest install. Only required for
|
|
|
|
guests which have the 'continue' flag set (accessed via
|
|
|
|
get_continue_inst)
|
|
|
|
"""
|
|
|
|
is_initial = False
|
|
|
|
start_xml, final_xml = self._build_xml(is_initial)
|
|
|
|
if return_xml:
|
|
|
|
return (start_xml, final_xml)
|
|
|
|
if dry:
|
|
|
|
return
|
|
|
|
|
2013-08-09 23:25:05 +08:00
|
|
|
return self._create_guest(meter,
|
2013-03-18 05:06:52 +08:00
|
|
|
start_xml, final_xml, is_initial, False)
|
|
|
|
|
|
|
|
def _build_meter(self, meter, is_initial):
|
|
|
|
if is_initial:
|
|
|
|
meter_label = _("Creating domain...")
|
|
|
|
else:
|
|
|
|
meter_label = _("Starting domain...")
|
|
|
|
|
2013-04-14 02:34:52 +08:00
|
|
|
if meter is None:
|
2013-03-18 05:06:52 +08:00
|
|
|
meter = progress.BaseMeter()
|
|
|
|
meter.start(size=None, text=meter_label)
|
|
|
|
|
|
|
|
return meter
|
|
|
|
|
|
|
|
def _build_xml(self, is_initial):
|
|
|
|
log_label = is_initial and "install" or "continue"
|
|
|
|
disk_boot = not is_initial
|
|
|
|
|
2013-07-24 23:32:30 +08:00
|
|
|
start_xml = self.get_install_xml(install=True, disk_boot=disk_boot)
|
|
|
|
final_xml = self.get_install_xml(install=False)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
logging.debug("Generated %s XML: %s",
|
|
|
|
log_label,
|
|
|
|
(start_xml and ("\n" + start_xml) or "None required"))
|
|
|
|
logging.debug("Generated boot XML: \n%s", final_xml)
|
|
|
|
|
|
|
|
return start_xml, final_xml
|
|
|
|
|
2013-08-09 23:25:05 +08:00
|
|
|
def _create_guest(self, meter,
|
2013-03-18 05:06:52 +08:00
|
|
|
start_xml, final_xml, is_initial, noboot):
|
|
|
|
"""
|
2014-02-06 08:09:26 +08:00
|
|
|
Actually do the XML logging, guest defining/creating
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
@param is_initial: If running initial guest creation, else we
|
|
|
|
are continuing the install
|
|
|
|
@param noboot: Don't boot guest if no install phase
|
|
|
|
"""
|
|
|
|
meter = self._build_meter(meter, is_initial)
|
|
|
|
doboot = not noboot or self.installer.has_install_phase()
|
|
|
|
|
|
|
|
if is_initial and doboot:
|
|
|
|
dom = self.conn.createLinux(start_xml or final_xml, 0)
|
|
|
|
else:
|
|
|
|
dom = self.conn.defineXML(start_xml or final_xml)
|
|
|
|
if doboot:
|
|
|
|
dom.create()
|
|
|
|
|
|
|
|
self.domain = dom
|
|
|
|
meter.end(0)
|
|
|
|
|
|
|
|
self.domain = self.conn.defineXML(final_xml)
|
|
|
|
if is_initial:
|
|
|
|
try:
|
|
|
|
logging.debug("XML fetched from libvirt object:\n%s",
|
|
|
|
dom.XMLDesc(0))
|
|
|
|
except Exception, e:
|
|
|
|
logging.debug("Error fetching XML from libvirt object: %s", e)
|
|
|
|
|
2013-08-09 23:25:05 +08:00
|
|
|
return self.domain
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
|
|
|
|
def _flag_autostart(self):
|
|
|
|
"""
|
|
|
|
Set the autostart flag for self.domain if the user requested it
|
|
|
|
"""
|
|
|
|
if not self.autostart:
|
|
|
|
return
|
|
|
|
|
|
|
|
try:
|
|
|
|
self.domain.setAutostart(True)
|
|
|
|
except libvirt.libvirtError, e:
|
2013-07-06 23:20:28 +08:00
|
|
|
if util.is_error_nosupport(e):
|
2013-03-18 05:06:52 +08:00
|
|
|
logging.warn("Could not set autostart flag: libvirt "
|
|
|
|
"connection does not support autostart.")
|
|
|
|
else:
|
|
|
|
raise e
|
|
|
|
|
|
|
|
|
2013-08-18 02:21:30 +08:00
|
|
|
###################################
|
|
|
|
# Guest Dictionary Helper methods #
|
|
|
|
###################################
|
|
|
|
|
2014-09-07 00:58:59 +08:00
|
|
|
def _get_os_object(self):
|
|
|
|
ret = osdict.lookup_os(self.os_variant)
|
|
|
|
if ret is None:
|
|
|
|
ret = osdict.lookup_os("generic")
|
|
|
|
return ret
|
|
|
|
_os_object = property(_get_os_object)
|
|
|
|
|
|
|
|
|
2013-08-18 02:21:30 +08:00
|
|
|
def _lookup_osdict_key(self, key, default):
|
|
|
|
"""
|
|
|
|
Use self.os_variant to find key in OSTYPES
|
|
|
|
@returns: dict value, or None if os_type/variant wasn't set
|
|
|
|
"""
|
2013-08-18 05:53:17 +08:00
|
|
|
return osdict.lookup_osdict_key(self.os_variant, key, default)
|
|
|
|
|
2013-08-18 02:21:30 +08:00
|
|
|
|
2015-02-19 04:16:48 +08:00
|
|
|
###########################
|
|
|
|
# XML convenience helpers #
|
|
|
|
###########################
|
|
|
|
|
|
|
|
def set_uefi_default(self):
|
|
|
|
"""
|
|
|
|
Configure UEFI for the VM, but only if libvirt is advertising
|
|
|
|
a known UEFI binary path.
|
|
|
|
"""
|
|
|
|
domcaps = DomainCapabilities.build_from_guest(self)
|
|
|
|
|
|
|
|
if not domcaps.supports_uefi_xml():
|
|
|
|
raise RuntimeError(_("Libvirt version does not support UEFI."))
|
|
|
|
|
2015-02-23 00:02:55 +08:00
|
|
|
if not domcaps.arch_can_uefi():
|
2015-02-19 04:16:48 +08:00
|
|
|
raise RuntimeError(
|
|
|
|
_("Don't know how to setup UEFI for arch '%s'") %
|
|
|
|
self.os.arch)
|
|
|
|
|
2015-02-23 00:02:55 +08:00
|
|
|
path = domcaps.find_uefi_path_for_arch()
|
2015-02-19 04:16:48 +08:00
|
|
|
if not path:
|
|
|
|
raise RuntimeError(_("Did not find any UEFI binary path for "
|
|
|
|
"arch '%s'") % self.os.arch)
|
|
|
|
|
|
|
|
self.os.loader_ro = True
|
|
|
|
self.os.loader_type = "pflash"
|
|
|
|
self.os.loader = path
|
|
|
|
|
|
|
|
|
2013-03-18 05:06:52 +08:00
|
|
|
###################
|
|
|
|
# Device defaults #
|
|
|
|
###################
|
|
|
|
|
2015-03-24 04:43:39 +08:00
|
|
|
def stable_defaults(self):
|
|
|
|
return self.conn.stable_defaults(self.emulator)
|
|
|
|
|
2013-07-26 02:36:13 +08:00
|
|
|
def add_default_input_device(self):
|
|
|
|
if self.os.is_container():
|
|
|
|
return
|
2013-08-18 20:59:19 +08:00
|
|
|
if not self.os.is_x86():
|
|
|
|
return
|
2013-10-06 20:30:33 +08:00
|
|
|
if self.get_devices("input"):
|
|
|
|
return
|
2014-09-13 03:59:22 +08:00
|
|
|
self.add_device(VirtualInputDevice(self.conn))
|
2013-07-26 02:36:13 +08:00
|
|
|
|
2013-08-18 20:59:19 +08:00
|
|
|
def add_default_sound_device(self):
|
|
|
|
if not self.os.is_hvm():
|
|
|
|
return
|
|
|
|
if not self.os.is_x86():
|
|
|
|
return
|
2014-09-13 03:59:22 +08:00
|
|
|
self.add_device(VirtualAudio(self.conn))
|
2013-08-18 20:59:19 +08:00
|
|
|
|
2013-07-26 02:36:13 +08:00
|
|
|
def add_default_console_device(self):
|
2013-10-06 21:41:37 +08:00
|
|
|
if self.skip_default_console:
|
|
|
|
return
|
2013-07-26 02:36:13 +08:00
|
|
|
if self.os.is_xenpv():
|
|
|
|
return
|
2013-10-06 20:30:33 +08:00
|
|
|
if self.get_devices("console") or self.get_devices("serial"):
|
|
|
|
return
|
|
|
|
|
2014-09-13 03:59:22 +08:00
|
|
|
dev = VirtualConsoleDevice(self.conn)
|
2013-07-26 02:36:13 +08:00
|
|
|
dev.type = dev.TYPE_PTY
|
2013-10-06 20:53:05 +08:00
|
|
|
|
|
|
|
if (self.os.is_x86() and
|
|
|
|
self._lookup_osdict_key("virtioconsole", False) and
|
2013-10-06 22:08:04 +08:00
|
|
|
self.conn.check_support(
|
2013-10-06 20:53:05 +08:00
|
|
|
self.conn.SUPPORT_CONN_VIRTIO_CONSOLE)):
|
|
|
|
dev.target_type = "virtio"
|
|
|
|
|
2013-07-26 02:36:13 +08:00
|
|
|
self.add_device(dev)
|
|
|
|
|
2013-08-18 20:59:19 +08:00
|
|
|
def add_default_video_device(self):
|
|
|
|
if self.os.is_container():
|
|
|
|
return
|
2013-10-06 20:30:33 +08:00
|
|
|
if self.get_devices("video"):
|
|
|
|
return
|
|
|
|
if not self.get_devices("graphics"):
|
|
|
|
return
|
2014-09-13 03:59:22 +08:00
|
|
|
self.add_device(VirtualVideoDevice(self.conn))
|
2013-08-18 20:59:19 +08:00
|
|
|
|
2013-10-03 03:51:01 +08:00
|
|
|
def add_default_usb_controller(self):
|
|
|
|
if self.os.is_container():
|
|
|
|
return
|
|
|
|
if not self.os.is_x86():
|
|
|
|
return
|
2013-10-06 20:30:33 +08:00
|
|
|
if any([d.type == "usb" for d in self.get_devices("controller")]):
|
|
|
|
return
|
2013-10-06 22:08:04 +08:00
|
|
|
if not self.conn.check_support(
|
2013-10-03 03:51:01 +08:00
|
|
|
self.conn.SUPPORT_CONN_DEFAULT_USB2):
|
|
|
|
return
|
2014-09-13 03:59:22 +08:00
|
|
|
for dev in VirtualController.get_usb2_controllers(self.conn):
|
2013-10-03 03:51:01 +08:00
|
|
|
self.add_device(dev)
|
|
|
|
|
2013-10-06 21:19:59 +08:00
|
|
|
def add_default_channels(self):
|
2013-10-06 21:41:37 +08:00
|
|
|
if self.skip_default_channel:
|
|
|
|
return
|
2013-10-06 21:19:59 +08:00
|
|
|
if self.get_devices("channel"):
|
|
|
|
return
|
|
|
|
|
2014-01-13 03:58:43 +08:00
|
|
|
# Skip qemu-ga on ARM where virtio slots are currently limited
|
2013-10-06 21:19:59 +08:00
|
|
|
if (self.conn.is_qemu() and
|
2014-01-13 03:58:43 +08:00
|
|
|
not self.os.is_arm() and
|
2013-10-06 21:19:59 +08:00
|
|
|
self._lookup_osdict_key("qemu_ga", False) and
|
2013-10-06 22:08:04 +08:00
|
|
|
self.conn.check_support(self.conn.SUPPORT_CONN_AUTOSOCKET)):
|
2014-09-13 03:59:22 +08:00
|
|
|
dev = VirtualChannelDevice(self.conn)
|
2013-10-06 21:19:59 +08:00
|
|
|
dev.type = "unix"
|
|
|
|
dev.target_type = "virtio"
|
|
|
|
dev.target_name = dev.CHANNEL_NAME_QEMUGA
|
|
|
|
self.add_device(dev)
|
|
|
|
|
2014-02-08 05:07:32 +08:00
|
|
|
def add_default_graphics(self):
|
|
|
|
if self.skip_default_graphics:
|
|
|
|
return
|
|
|
|
if self.get_devices("graphics"):
|
|
|
|
return
|
|
|
|
if self.os.is_container():
|
|
|
|
return
|
2015-01-05 10:16:18 +08:00
|
|
|
if self.os.arch not in ["x86_64", "i686", "ppc64", "ppc64le", "ia64"]:
|
2014-02-08 05:07:32 +08:00
|
|
|
return
|
2014-09-13 03:59:22 +08:00
|
|
|
self.add_device(VirtualGraphics(self.conn))
|
2014-02-08 05:07:32 +08:00
|
|
|
|
|
|
|
def add_default_devices(self):
|
|
|
|
self.add_default_graphics()
|
|
|
|
self.add_default_video_device()
|
|
|
|
self.add_default_input_device()
|
|
|
|
self.add_default_console_device()
|
|
|
|
self.add_default_usb_controller()
|
|
|
|
self.add_default_channels()
|
|
|
|
|
2013-07-18 05:58:24 +08:00
|
|
|
def _set_transient_device_defaults(self, install):
|
|
|
|
def do_remove_media(d):
|
|
|
|
# Keep cdrom around, but with no media attached,
|
|
|
|
# But only if we are a distro that doesn't have a multi
|
|
|
|
# stage install (aka not Windows)
|
2013-08-18 02:21:30 +08:00
|
|
|
return (d.is_cdrom() and
|
2014-12-06 09:28:32 +08:00
|
|
|
getattr(d, "installer_media", False) and
|
2013-08-18 02:21:30 +08:00
|
|
|
not install and
|
2013-07-18 05:58:24 +08:00
|
|
|
not self.get_continue_inst())
|
|
|
|
|
|
|
|
def do_skip_disk(d):
|
|
|
|
# Skip transient labeled non-media disks
|
2014-12-06 09:28:32 +08:00
|
|
|
return (d.is_disk() and
|
|
|
|
getattr(d, "installer_media", False) and
|
|
|
|
not install)
|
2013-07-18 05:58:24 +08:00
|
|
|
|
2013-08-18 02:21:30 +08:00
|
|
|
for dev in self.get_devices("disk"):
|
2013-07-18 05:58:24 +08:00
|
|
|
if do_skip_disk(dev):
|
|
|
|
self.remove_device(dev)
|
2013-08-18 02:21:30 +08:00
|
|
|
elif do_remove_media(dev):
|
2013-07-18 05:58:24 +08:00
|
|
|
dev.path = None
|
|
|
|
|
2013-07-24 23:32:30 +08:00
|
|
|
def _set_defaults(self):
|
2013-07-18 05:58:24 +08:00
|
|
|
self._set_osxml_defaults()
|
2013-07-26 02:36:13 +08:00
|
|
|
self._set_clock_defaults()
|
2013-07-18 05:58:24 +08:00
|
|
|
self._set_emulator_defaults()
|
|
|
|
self._set_cpu_defaults()
|
2013-07-26 02:36:13 +08:00
|
|
|
self._set_feature_defaults()
|
2013-07-18 05:58:24 +08:00
|
|
|
|
2013-07-26 02:36:13 +08:00
|
|
|
for dev in self.get_all_devices():
|
2013-10-07 01:17:35 +08:00
|
|
|
dev.set_defaults(self)
|
2013-09-02 22:56:53 +08:00
|
|
|
self._check_address_multi()
|
2013-07-26 02:36:13 +08:00
|
|
|
self._set_disk_defaults()
|
2014-11-21 00:27:09 +08:00
|
|
|
self._add_implied_controllers()
|
2013-07-26 02:36:13 +08:00
|
|
|
self._set_net_defaults()
|
|
|
|
self._set_input_defaults()
|
2013-10-03 06:06:52 +08:00
|
|
|
self._set_graphics_defaults()
|
2013-07-26 02:36:13 +08:00
|
|
|
self._set_video_defaults()
|
2014-02-06 01:49:16 +08:00
|
|
|
self._set_sound_defaults()
|
2013-07-18 05:58:24 +08:00
|
|
|
|
2014-09-24 03:09:35 +08:00
|
|
|
def _is_full_os_container(self):
|
2014-03-10 22:25:14 +08:00
|
|
|
if not self.os.is_container():
|
|
|
|
return False
|
|
|
|
for fs in self.get_devices("filesystem"):
|
|
|
|
if fs.target == "/":
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2013-07-18 05:58:24 +08:00
|
|
|
def _set_osxml_defaults(self):
|
|
|
|
if self.os.is_container() and not self.os.init:
|
2014-09-24 03:09:35 +08:00
|
|
|
if self._is_full_os_container():
|
2014-03-10 22:25:14 +08:00
|
|
|
self.os.init = "/sbin/init"
|
2013-07-18 05:58:24 +08:00
|
|
|
self.os.init = self.os.init or "/bin/sh"
|
|
|
|
|
|
|
|
if not self.os.loader and self.os.is_hvm() and self.type == "xen":
|
|
|
|
self.os.loader = "/usr/lib/xen/boot/hvmloader"
|
|
|
|
if self.os.os_type == "xen" and self.type == "xen":
|
|
|
|
# Use older libvirt 'linux' value for back compat
|
|
|
|
self.os.os_type = "linux"
|
|
|
|
if self.os.kernel or self.os.init:
|
|
|
|
self.os.bootorder = []
|
|
|
|
|
2013-07-26 02:36:13 +08:00
|
|
|
def _set_clock_defaults(self):
|
|
|
|
if not self.os.is_hvm():
|
|
|
|
return
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-04-14 02:34:52 +08:00
|
|
|
if self.clock.offset is None:
|
2013-08-12 03:29:31 +08:00
|
|
|
self.clock.offset = self._lookup_osdict_key("clock", "utc")
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-10-06 05:02:26 +08:00
|
|
|
if self.clock.timers:
|
|
|
|
return
|
|
|
|
if not self.os.is_x86():
|
|
|
|
return
|
2013-10-06 22:08:04 +08:00
|
|
|
if not self.conn.check_support(
|
2013-10-06 05:02:26 +08:00
|
|
|
self.conn.SUPPORT_CONN_ADVANCED_CLOCK):
|
|
|
|
return
|
|
|
|
|
|
|
|
# Set clock policy that maps to qemu options:
|
|
|
|
# -no-hpet -no-kvm-pit-reinjection -rtc driftfix=slew
|
|
|
|
#
|
|
|
|
# hpet: Is unneeded and has a performance penalty
|
|
|
|
# pit: While it has no effect on windows, it doesn't hurt and
|
|
|
|
# is beneficial for linux
|
|
|
|
#
|
2014-07-07 06:46:16 +08:00
|
|
|
# If libvirt/qemu supports it and using a windows VM, also
|
|
|
|
# specify hypervclock.
|
|
|
|
#
|
2013-10-06 05:02:26 +08:00
|
|
|
# This is what has been recommended by the RH qemu guys :)
|
|
|
|
|
|
|
|
rtc = self.clock.add_timer()
|
|
|
|
rtc.name = "rtc"
|
|
|
|
rtc.tickpolicy = "catchup"
|
|
|
|
|
|
|
|
pit = self.clock.add_timer()
|
|
|
|
pit.name = "pit"
|
|
|
|
pit.tickpolicy = "delay"
|
|
|
|
|
|
|
|
hpet = self.clock.add_timer()
|
|
|
|
hpet.name = "hpet"
|
|
|
|
hpet.present = False
|
|
|
|
|
2014-07-07 06:46:16 +08:00
|
|
|
if (self._lookup_osdict_key("hyperv_features", False) and
|
2015-01-26 23:52:03 +08:00
|
|
|
self.conn.check_support(self.conn.SUPPORT_CONN_HYPERV_CLOCK) and
|
|
|
|
self._hv_supported()):
|
2014-07-07 06:46:16 +08:00
|
|
|
hyperv = self.clock.add_timer()
|
|
|
|
hyperv.name = "hypervclock"
|
|
|
|
hyperv.present = True
|
|
|
|
|
2013-07-26 02:36:13 +08:00
|
|
|
def _set_emulator_defaults(self):
|
|
|
|
if self.os.is_xenpv():
|
|
|
|
self.emulator = None
|
|
|
|
return
|
|
|
|
|
|
|
|
if self.emulator:
|
|
|
|
return
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-07-26 02:36:13 +08:00
|
|
|
if self.os.is_hvm() and self.type == "xen":
|
2014-01-19 02:23:30 +08:00
|
|
|
if self.conn.caps.host.cpu.arch == "x86_64":
|
2013-07-26 02:36:13 +08:00
|
|
|
self.emulator = "/usr/lib64/xen/bin/qemu-dm"
|
|
|
|
else:
|
|
|
|
self.emulator = "/usr/lib/xen/bin/qemu-dm"
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-07-26 02:36:13 +08:00
|
|
|
def _set_cpu_defaults(self):
|
|
|
|
self.cpu.set_topology_defaults(self.vcpus)
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2014-09-24 04:16:35 +08:00
|
|
|
if not self.conn.is_test() and not self.conn.is_qemu():
|
2014-02-06 02:51:53 +08:00
|
|
|
return
|
|
|
|
if self.cpu.get_xml_config().strip():
|
2014-09-24 04:16:35 +08:00
|
|
|
# User already configured CPU
|
2014-02-06 02:51:53 +08:00
|
|
|
return
|
|
|
|
|
2014-09-24 04:16:35 +08:00
|
|
|
if self.os.is_arm_machvirt() and self.type == "kvm":
|
|
|
|
# Should be host-passthrough, but the libvirt support is
|
|
|
|
# incomplete for arm cpu
|
|
|
|
self.cpu.model = "host"
|
|
|
|
|
|
|
|
elif self.os.is_arm64() and self.os.is_arm_machvirt():
|
|
|
|
# -M virt defaults to a 32bit CPU, even if using aarch64
|
|
|
|
self.cpu.model = "cortex-a57"
|
|
|
|
|
|
|
|
elif self.os.is_x86() and self.type == "kvm":
|
|
|
|
if self.os.arch != self.conn.caps.host.cpu.arch:
|
|
|
|
return
|
2014-09-24 04:19:54 +08:00
|
|
|
|
|
|
|
# We need this check to handle the user setting --cpu none on
|
|
|
|
# the CLI to override our defaults
|
2014-09-24 04:16:35 +08:00
|
|
|
if self.cpu.special_mode_was_set:
|
|
|
|
return
|
|
|
|
self.cpu.set_special_mode(self.x86_cpu_default)
|
2014-02-06 02:51:53 +08:00
|
|
|
|
2015-01-26 23:52:03 +08:00
|
|
|
def _hv_supported(self):
|
|
|
|
if (self.os.loader_type == "pflash" and
|
|
|
|
self.os_variant in ("win2k8r2", "win7")):
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2013-07-18 05:58:24 +08:00
|
|
|
def _set_feature_defaults(self):
|
|
|
|
if self.os.is_container():
|
|
|
|
self.features.acpi = None
|
|
|
|
self.features.apic = None
|
|
|
|
self.features.pae = None
|
2014-09-24 03:09:35 +08:00
|
|
|
if self._is_full_os_container():
|
2014-03-10 22:25:14 +08:00
|
|
|
self.features.privnet = True
|
2013-07-18 05:58:24 +08:00
|
|
|
return
|
|
|
|
|
|
|
|
if not self.os.is_hvm():
|
|
|
|
return
|
|
|
|
|
2013-08-18 05:53:17 +08:00
|
|
|
default = True
|
|
|
|
if (self._lookup_osdict_key("xen_disable_acpi", False) and
|
2014-02-11 05:14:39 +08:00
|
|
|
not self.conn.check_support(support.SUPPORT_CONN_CAN_ACPI)):
|
2013-08-18 05:53:17 +08:00
|
|
|
default = False
|
|
|
|
|
2013-09-28 03:08:44 +08:00
|
|
|
if self.features.acpi == "default":
|
|
|
|
self.features.acpi = self._lookup_osdict_key("acpi", default)
|
|
|
|
if self.features.apic == "default":
|
|
|
|
self.features.apic = self._lookup_osdict_key("apic", default)
|
|
|
|
if self.features.pae == "default":
|
|
|
|
self.features.pae = self.conn.caps.support_pae()
|
2013-07-18 05:58:24 +08:00
|
|
|
|
2014-07-07 06:46:16 +08:00
|
|
|
if (self._lookup_osdict_key("hyperv_features", False) and
|
2015-01-26 23:52:03 +08:00
|
|
|
self._hv_supported() and
|
2014-07-07 06:46:16 +08:00
|
|
|
self.conn.check_support(self.conn.SUPPORT_CONN_HYPERV_VAPIC)):
|
|
|
|
if self.features.hyperv_relaxed is None:
|
|
|
|
self.features.hyperv_relaxed = True
|
|
|
|
if self.features.hyperv_vapic is None:
|
|
|
|
self.features.hyperv_vapic = True
|
|
|
|
if self.features.hyperv_spinlocks is None:
|
|
|
|
self.features.hyperv_spinlocks = True
|
|
|
|
if self.features.hyperv_spinlocks_retries is None:
|
|
|
|
self.features.hyperv_spinlocks_retries = 8191
|
|
|
|
|
2013-07-26 02:36:13 +08:00
|
|
|
def _add_implied_controllers(self):
|
2014-11-21 00:27:09 +08:00
|
|
|
has_spapr_scsi = False
|
|
|
|
has_virtio_scsi = False
|
|
|
|
has_any_scsi = False
|
|
|
|
for dev in self.get_devices("controller"):
|
|
|
|
if dev.type == "scsi":
|
|
|
|
has_any_scsi = True
|
|
|
|
if dev.address.type == "spapr-vio":
|
|
|
|
has_spapr_scsi = True
|
|
|
|
if dev.model == "virtio":
|
|
|
|
has_virtio_scsi = True
|
|
|
|
|
|
|
|
# Add spapr-vio controller if needed
|
|
|
|
if not has_spapr_scsi:
|
|
|
|
for dev in self.get_devices("disk"):
|
|
|
|
if dev.address.type == "spapr-vio":
|
|
|
|
ctrl = VirtualController(self.conn)
|
|
|
|
ctrl.type = "scsi"
|
|
|
|
ctrl.address.set_addrstr("spapr-vio")
|
|
|
|
self.add_device(ctrl)
|
|
|
|
break
|
|
|
|
|
|
|
|
# Add virtio-scsi controller if needed
|
|
|
|
if (self.os.is_arm_machvirt() and
|
|
|
|
not has_any_scsi and
|
|
|
|
not has_virtio_scsi):
|
|
|
|
for dev in self.get_devices("disk"):
|
|
|
|
if dev.bus == "scsi":
|
|
|
|
ctrl = VirtualController(self.conn)
|
|
|
|
ctrl.type = "scsi"
|
|
|
|
ctrl.model = "virtio-scsi"
|
|
|
|
self.add_device(ctrl)
|
|
|
|
break
|
|
|
|
|
2013-07-18 05:58:24 +08:00
|
|
|
|
2013-09-02 22:56:53 +08:00
|
|
|
def _check_address_multi(self):
|
|
|
|
addresses = {}
|
2013-09-20 22:47:36 +08:00
|
|
|
for d in self.get_all_devices():
|
2013-09-02 22:56:53 +08:00
|
|
|
if d.address.type != d.address.ADDRESS_TYPE_PCI:
|
|
|
|
continue
|
|
|
|
|
|
|
|
addr = d.address
|
|
|
|
addrstr = "%d%d%d" % (d.address.domain,
|
|
|
|
d.address.bus,
|
|
|
|
d.address.slot)
|
|
|
|
|
|
|
|
if addrstr not in addresses:
|
|
|
|
addresses[addrstr] = {}
|
|
|
|
if addr.function in addresses[addrstr]:
|
|
|
|
raise ValueError(_("Duplicate address for devices %s and %s") %
|
|
|
|
(str(d), str(addresses[addrstr][addr.function])))
|
|
|
|
addresses[addrstr][addr.function] = d
|
|
|
|
|
|
|
|
for devs in addresses.values():
|
|
|
|
if len(devs) > 1 and 0 in devs:
|
|
|
|
devs[0].address.multifunction = True
|
|
|
|
|
2013-08-18 05:53:17 +08:00
|
|
|
def _can_virtio(self, key):
|
2013-08-18 05:59:17 +08:00
|
|
|
if not self.conn.is_qemu():
|
2013-08-18 05:53:17 +08:00
|
|
|
return False
|
2014-09-24 03:12:02 +08:00
|
|
|
|
|
|
|
if self.os.is_arm_machvirt():
|
|
|
|
# Only supports virtio
|
|
|
|
return True
|
|
|
|
|
2013-08-18 05:53:17 +08:00
|
|
|
if not self._lookup_osdict_key(key, False):
|
|
|
|
return False
|
2013-08-18 20:59:19 +08:00
|
|
|
|
|
|
|
if self.os.is_x86():
|
|
|
|
return True
|
2014-09-24 03:12:02 +08:00
|
|
|
|
2013-08-18 20:59:19 +08:00
|
|
|
if (self.os.is_arm_vexpress() and
|
|
|
|
self.os.dtb and
|
|
|
|
self._lookup_osdict_key("virtiommio", False) and
|
2013-10-06 22:08:04 +08:00
|
|
|
self.conn.check_support(support.SUPPORT_CONN_VIRTIO_MMIO)):
|
2013-08-18 20:59:19 +08:00
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
2013-08-18 05:53:17 +08:00
|
|
|
|
2013-07-26 02:36:13 +08:00
|
|
|
def _set_disk_defaults(self):
|
|
|
|
def set_disk_bus(d):
|
2013-08-18 02:21:30 +08:00
|
|
|
if d.is_floppy():
|
2013-07-26 02:36:13 +08:00
|
|
|
d.bus = "fdc"
|
|
|
|
return
|
|
|
|
if self.os.is_xenpv():
|
|
|
|
d.bus = "xen"
|
|
|
|
return
|
2013-08-18 02:21:30 +08:00
|
|
|
if not self.os.is_hvm():
|
|
|
|
d.bus = "ide"
|
|
|
|
return
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2014-11-21 04:39:53 +08:00
|
|
|
if self.os.is_arm_machvirt():
|
|
|
|
# We prefer virtio-scsi for machvirt, gets us hotplug
|
|
|
|
d.bus = "scsi"
|
|
|
|
elif self._can_virtio("virtiodisk") and d.is_disk():
|
2013-08-18 05:53:17 +08:00
|
|
|
d.bus = "virtio"
|
|
|
|
elif self.os.is_pseries():
|
2013-08-18 02:21:30 +08:00
|
|
|
d.bus = "scsi"
|
2013-08-18 20:59:19 +08:00
|
|
|
elif self.os.is_arm():
|
|
|
|
d.bus = "sd"
|
2013-08-18 02:21:30 +08:00
|
|
|
else:
|
|
|
|
d.bus = "ide"
|
2013-03-18 05:06:52 +08:00
|
|
|
|
|
|
|
used_targets = []
|
2013-07-24 20:46:55 +08:00
|
|
|
for disk in self.get_devices("disk"):
|
2013-03-18 05:06:52 +08:00
|
|
|
if not disk.bus:
|
2013-07-26 02:36:13 +08:00
|
|
|
set_disk_bus(disk)
|
|
|
|
|
|
|
|
# Generate disk targets
|
2014-01-26 10:08:11 +08:00
|
|
|
if disk.target and not getattr(disk, "cli_set_target", False):
|
2013-03-18 05:06:52 +08:00
|
|
|
used_targets.append(disk.target)
|
|
|
|
else:
|
|
|
|
used_targets.append(disk.generate_target(used_targets))
|
|
|
|
|
2013-07-26 02:36:13 +08:00
|
|
|
def _set_net_defaults(self):
|
2015-02-25 19:26:45 +08:00
|
|
|
net_model = None
|
|
|
|
variant = osdict.lookup_os(self.os_variant)
|
2013-07-26 02:36:13 +08:00
|
|
|
if not self.os.is_hvm():
|
|
|
|
net_model = None
|
2013-08-18 05:53:17 +08:00
|
|
|
elif self._can_virtio("virtionet"):
|
|
|
|
net_model = "virtio"
|
2015-02-25 19:26:45 +08:00
|
|
|
elif variant and not variant.virtionet:
|
2013-08-18 05:53:17 +08:00
|
|
|
net_model = self._lookup_osdict_key("netmodel", None)
|
2015-02-25 19:26:45 +08:00
|
|
|
if net_model:
|
|
|
|
for net in self.get_devices("interface"):
|
|
|
|
if not net.model:
|
|
|
|
net.model = net_model
|
2013-07-26 02:36:13 +08:00
|
|
|
|
|
|
|
def _set_input_defaults(self):
|
2013-08-12 03:29:31 +08:00
|
|
|
input_type = self._lookup_osdict_key("inputtype", "mouse")
|
|
|
|
input_bus = self._lookup_osdict_key("inputbus", "ps2")
|
2013-07-26 02:36:13 +08:00
|
|
|
if self.os.is_xenpv():
|
2014-09-13 03:59:22 +08:00
|
|
|
input_type = VirtualInputDevice.TYPE_MOUSE
|
|
|
|
input_bus = VirtualInputDevice.BUS_XEN
|
2013-07-26 02:36:13 +08:00
|
|
|
|
|
|
|
for inp in self.get_devices("input"):
|
|
|
|
if (inp.type == inp.TYPE_DEFAULT and
|
|
|
|
inp.bus == inp.BUS_DEFAULT):
|
|
|
|
inp.type = input_type
|
|
|
|
inp.bus = input_bus
|
|
|
|
|
|
|
|
def _set_sound_defaults(self):
|
2013-10-06 22:08:04 +08:00
|
|
|
if self.conn.check_support(
|
2013-10-03 04:41:23 +08:00
|
|
|
support.SUPPORT_CONN_SOUND_ICH6):
|
2013-08-12 03:29:31 +08:00
|
|
|
default = "ich6"
|
2013-10-06 22:08:04 +08:00
|
|
|
elif self.conn.check_support(
|
2013-10-03 04:41:23 +08:00
|
|
|
support.SUPPORT_CONN_SOUND_AC97):
|
2013-08-12 03:29:31 +08:00
|
|
|
default = "ac97"
|
|
|
|
else:
|
|
|
|
default = "es1370"
|
|
|
|
|
2013-07-24 20:46:55 +08:00
|
|
|
for sound in self.get_devices("sound"):
|
2013-03-18 05:06:52 +08:00
|
|
|
if sound.model == sound.MODEL_DEFAULT:
|
2013-08-12 03:29:31 +08:00
|
|
|
sound.model = default
|
2013-03-18 05:06:52 +08:00
|
|
|
|
2013-10-03 06:06:52 +08:00
|
|
|
def _set_graphics_defaults(self):
|
|
|
|
for gfx in self.get_devices("graphics"):
|
|
|
|
if gfx.type != "default":
|
|
|
|
continue
|
|
|
|
|
|
|
|
gtype = self.default_graphics_type
|
|
|
|
logging.debug("Using default_graphics=%s", gtype)
|
|
|
|
if (gtype == "spice" and not
|
2013-10-06 22:08:04 +08:00
|
|
|
self.conn.check_support(
|
2013-10-03 06:06:52 +08:00
|
|
|
self.conn.SUPPORT_CONN_GRAPHICS_SPICE)):
|
|
|
|
logging.debug("spice requested but HV doesn't support it. "
|
|
|
|
"Using vnc.")
|
|
|
|
gtype = "vnc"
|
|
|
|
gfx.type = gtype
|
|
|
|
|
|
|
|
def _add_spice_channels(self):
|
2013-10-06 21:41:37 +08:00
|
|
|
if self.skip_default_channel:
|
|
|
|
return
|
|
|
|
|
2014-02-06 01:49:16 +08:00
|
|
|
for chn in self.get_devices("channel"):
|
|
|
|
if chn.type == chn.TYPE_SPICEVMC:
|
|
|
|
return
|
|
|
|
|
|
|
|
if self.conn.check_support(self.conn.SUPPORT_CONN_CHAR_SPICEVMC):
|
2014-09-13 03:59:22 +08:00
|
|
|
agentdev = VirtualChannelDevice(self.conn)
|
2013-10-03 06:06:52 +08:00
|
|
|
agentdev.type = agentdev.TYPE_SPICEVMC
|
|
|
|
self.add_device(agentdev)
|
|
|
|
|
2014-02-06 01:49:16 +08:00
|
|
|
def _add_spice_sound(self):
|
|
|
|
if self.skip_default_sound:
|
|
|
|
return
|
|
|
|
if self.get_devices("sound"):
|
|
|
|
return
|
|
|
|
self.add_default_sound_device()
|
|
|
|
|
2014-02-06 01:58:53 +08:00
|
|
|
def _add_spice_usbredir(self):
|
|
|
|
if self.skip_default_usbredir:
|
|
|
|
return
|
|
|
|
if self.get_devices("redirdev"):
|
|
|
|
return
|
|
|
|
if not self.conn.check_support(self.conn.SUPPORT_CONN_USBREDIR):
|
|
|
|
return
|
|
|
|
|
2014-09-21 08:16:31 +08:00
|
|
|
# If we use 4 devices here, we fill up all the emulated USB2 slots,
|
|
|
|
# and directly assigned devices are forced to fall back to USB1
|
|
|
|
# https://bugzilla.redhat.com/show_bug.cgi?id=1135488
|
|
|
|
for ignore in range(2):
|
2014-09-13 03:59:22 +08:00
|
|
|
dev = VirtualRedirDevice(self.conn)
|
2014-02-06 01:58:53 +08:00
|
|
|
dev.bus = "usb"
|
|
|
|
dev.type = "spicevmc"
|
|
|
|
self.add_device(dev)
|
|
|
|
|
2014-09-07 00:58:59 +08:00
|
|
|
def has_spice(self):
|
|
|
|
for gfx in self.get_devices("graphics"):
|
|
|
|
if gfx.type == gfx.TYPE_SPICE:
|
|
|
|
return True
|
2013-10-03 06:06:52 +08:00
|
|
|
|
2014-09-07 00:58:59 +08:00
|
|
|
def _set_video_defaults(self):
|
|
|
|
if self.has_spice():
|
2013-10-03 06:06:52 +08:00
|
|
|
self._add_spice_channels()
|
2014-02-06 01:49:16 +08:00
|
|
|
self._add_spice_sound()
|
2014-02-06 01:58:53 +08:00
|
|
|
self._add_spice_usbredir()
|
2013-12-19 05:17:01 +08:00
|
|
|
|
2014-09-07 01:18:25 +08:00
|
|
|
video_model = self._os_object.get_videomodel(self)
|
2015-03-24 04:43:39 +08:00
|
|
|
if self.stable_defaults() and video_model == 'vmvga':
|
2015-02-25 02:00:38 +08:00
|
|
|
video_model = 'vga'
|
2013-07-24 20:46:55 +08:00
|
|
|
for video in self.get_devices("video"):
|
2013-07-15 23:07:40 +08:00
|
|
|
if video.model == video.MODEL_DEFAULT:
|
|
|
|
video.model = video_model
|