constants.HOTPLUG_TARGET_DISK: lambda d, e: (d, e)
}
+_MIGRATION_CAPS_DELIM = ":"
+
def _GenerateDeviceKVMId(dev_type, dev, idx=None):
"""Helper function to generate a unique device name used by KVM
" without UUID or PCI info")
-def _UpdatePCISlots(dev, pci_reservations):
- """Update pci configuration for a stopped instance
-
- If dev has a pci slot then reserve it, else find first available
- in pci_reservations bitarray. It acts on the same objects passed
- as params so there is no need to return anything.
+def _GetFreeSlot(slots, slot=None, reserve=False):
+ """Helper method to get first available slot in a bitarray
- @type dev: L{objects.Disk} or L{objects.NIC}
- @param dev: the device object for which we update its pci slot
- @type pci_reservations: bitarray
- @param pci_reservations: existing pci reservations for an instance
- @raise errors.HotplugError: in case an instance has all its slot occupied
+ @type slots: bitarray
+ @param slots: the bitarray to operate on
+ @type slot: integer
+ @param slot: if given we check whether the slot is free
+ @type reserve: boolean
+ @param reserve: whether to reserve the first available slot or not
+ @return: the idx of the (first) available slot
+ @raise errors.HotplugError: If all slots in a bitarray are occupied
+ or the given slot is not free.
"""
- if dev.pci:
- free = dev.pci
- else: # pylint: disable=E1103
- [free] = pci_reservations.search(_AVAILABLE_PCI_SLOT, 1)
- if not free:
- raise errors.HypervisorError("All PCI slots occupied")
- dev.pci = int(free)
+ if slot is not None:
+ assert slot < len(slots)
+ if slots[slot]:
+ raise errors.HypervisorError("Slots %d occupied" % slot)
+
+ else:
+ avail = slots.search(_AVAILABLE_PCI_SLOT, 1)
+ if not avail:
+ raise errors.HypervisorError("All slots occupied")
+
+ slot = int(avail[0])
- pci_reservations[free] = True
+ if reserve:
+ slots[slot] = True
+
+ return slot
def _GetExistingDeviceInfo(dev_type, device, runtime):
constants.HV_MIGRATION_BANDWIDTH: hv_base.REQ_NONNEGATIVE_INT_CHECK,
constants.HV_MIGRATION_DOWNTIME: hv_base.REQ_NONNEGATIVE_INT_CHECK,
constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
+ constants.HV_KVM_MIGRATION_CAPS: hv_base.NO_CHECK,
constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
constants.HV_DISK_CACHE:
hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
re.compile(r'^QEMU (\d+)\.(\d+)(\.(\d+))?.*monitor.*', re.M)
_INFO_VERSION_CMD = "info version"
- _DEFAULT_PCI_RESERVATIONS = "11110000000000000000000000000000"
+ # Slot 0 for Host bridge, Slot 1 for ISA bridge, Slot 2 for VGA controller
+ _DEFAULT_PCI_RESERVATIONS = "11100000000000000000000000000000"
+ _SOUNDHW_WITH_PCI_SLOT = ["ac97", "es1370", "hda"]
ANCILLARY_FILES = [
_KVM_NETWORK_SCRIPT,
"""Removes an instance's rutime sockets/files/dirs.
"""
+ # This takes info from NICDir and RuntimeFile
+ cls._UnconfigureInstanceNICs(instance_name)
utils.RemoveFile(pidfile)
utils.RemoveFile(cls._InstanceMonitor(instance_name))
utils.RemoveFile(cls._InstanceSerial(instance_name))
data.append(info)
return data
- def _GenerateKVMBlockDevicesOptions(self, instance, kvm_disks,
+ def _GenerateKVMBlockDevicesOptions(self, instance, up_hvp, kvm_disks,
kvmhelp, devlist):
"""Generate KVM options regarding instance's block devices.
@type instance: L{objects.Instance}
@param instance: the instance object
+ @type up_hvp: dict
+ @param up_hvp: the instance's runtime hypervisor parameters
@type kvm_disks: list of tuples
@param kvm_disks: list of tuples [(disk, link_name)..]
@type kvmhelp: string
@return: list of command line options eventually used by kvm executable
"""
- hvp = instance.hvparams
- kernel_path = hvp[constants.HV_KERNEL_PATH]
+ kernel_path = up_hvp[constants.HV_KERNEL_PATH]
if kernel_path:
boot_disk = False
else:
- boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
+ boot_disk = up_hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
# whether this is an older KVM version that uses the boot=on flag
# on devices
dev_opts = []
device_driver = None
- disk_type = hvp[constants.HV_DISK_TYPE]
+ disk_type = up_hvp[constants.HV_DISK_TYPE]
if disk_type == constants.HT_DISK_PARAVIRTUAL:
if_val = ",if=%s" % self._VIRTIO
try:
else:
if_val = ",if=%s" % disk_type
# Cache mode
- disk_cache = hvp[constants.HV_DISK_CACHE]
+ disk_cache = up_hvp[constants.HV_DISK_CACHE]
if instance.disk_template in constants.DTS_EXT_MIRROR:
if disk_cache != "none":
# TODO: make this a hard error, instead of a silent overwrite
kvm_cmd.extend(["-smp", ",".join(smp_list)])
kvm_cmd.extend(["-pidfile", pidfile])
- kvm_cmd.extend(["-balloon", "virtio"])
+
+ pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS)
+
+ # As requested by music lovers
+ if hvp[constants.HV_SOUNDHW]:
+ soundhw = hvp[constants.HV_SOUNDHW]
+ # For some reason only few sound devices require a PCI slot
+ # while the Audio controller *must* be in slot 3.
+ # That's why we bridge this option early in command line
+ if soundhw in self._SOUNDHW_WITH_PCI_SLOT:
+ _ = _GetFreeSlot(pci_reservations, reserve=True)
+ kvm_cmd.extend(["-soundhw", soundhw])
+
+ if hvp[constants.HV_DISK_TYPE] == constants.HT_DISK_SCSI:
+ # The SCSI controller requires another PCI slot.
+ _ = _GetFreeSlot(pci_reservations, reserve=True)
+
+ # Add id to ballon and place to the first available slot (3 or 4)
+ addr = _GetFreeSlot(pci_reservations, reserve=True)
+ pci_info = ",bus=pci.0,addr=%s" % hex(addr)
+ kvm_cmd.extend(["-balloon", "virtio,id=balloon%s" % pci_info])
kvm_cmd.extend(["-daemonize"])
if not instance.hvparams[constants.HV_ACPI]:
kvm_cmd.extend(["-no-acpi"])
else:
# Enable the spice agent communication channel between the host and the
# agent.
- kvm_cmd.extend(["-device", "virtio-serial-pci"])
+ addr = _GetFreeSlot(pci_reservations, reserve=True)
+ pci_info = ",bus=pci.0,addr=%s" % hex(addr)
+ kvm_cmd.extend(["-device", "virtio-serial-pci,id=spice%s" % pci_info])
kvm_cmd.extend([
"-device",
"virtserialport,chardev=spicechannel0,name=com.redhat.spice.0",
if hvp[constants.HV_CPU_TYPE]:
kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
- # As requested by music lovers
- if hvp[constants.HV_SOUNDHW]:
- kvm_cmd.extend(["-soundhw", hvp[constants.HV_SOUNDHW]])
-
# Pass a -vga option if requested, or if spice is used, for backwards
# compatibility.
if hvp[constants.HV_VGA]:
if hvp[constants.HV_KVM_EXTRA]:
kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
- pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS)
kvm_disks = []
for disk, link_name in block_devices:
- _UpdatePCISlots(disk, pci_reservations)
+ disk.pci = _GetFreeSlot(pci_reservations, disk.pci, True)
kvm_disks.append((disk, link_name))
kvm_nics = []
for nic in instance.nics:
- _UpdatePCISlots(nic, pci_reservations)
+ nic.pci = _GetFreeSlot(pci_reservations, nic.pci, True)
kvm_nics.append(nic)
hvparams = hvp
devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST)
bdev_opts = self._GenerateKVMBlockDevicesOptions(instance,
+ up_hvp,
kvm_disks,
kvmhelp,
devlist)
slot = int(match.group(1))
slots[slot] = True
- [free] = slots.search(_AVAILABLE_PCI_SLOT, 1) # pylint: disable=E1101
- if not free:
- raise errors.HypervisorError("All PCI slots occupied")
-
- dev.pci = int(free)
+ dev.pci = _GetFreeSlot(slots)
def VerifyHotplugSupport(self, instance, action, dev_type):
"""Verifies that hotplug is supported.
elif dev_type == constants.HOTPLUG_TARGET_NIC:
cmds = ["device_del %s" % kvm_devid]
cmds += ["netdev_del %s" % kvm_devid]
- utils.RemoveFile(self._InstanceNICFile(instance.name, seq))
+ self._UnconfigureNic(instance.name, kvm_device, False)
+ self._RemoveInstanceNICFiles(instance.name, seq, device)
self._CallHotplugCommands(instance.name, cmds)
self._VerifyHotplugCommand(instance.name, kvm_device, dev_type, False)
index = _DEVICE_RUNTIME_INDEX[dev_type]
self._WriteKVMRuntime(instance.name, info)
else:
+ self._UnconfigureInstanceNICs(instance.name, info)
self.StopInstance(instance, force=True)
def MigrateInstance(self, instance, target, live):
instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
self._CallMonitorCommand(instance_name, migrate_command)
+ # These commands are supported in latest qemu versions.
+ # Since _CallMonitorCommand does not catch monitor errors
+ # this does not raise an exception in case command is not supported
+ # TODO: either parse output of command or see if the command supported
+ # via info help (see hotplug)
+ migration_caps = instance.hvparams[constants.HV_KVM_MIGRATION_CAPS]
+ if migration_caps:
+ for c in migration_caps.split(_MIGRATION_CAPS_DELIM):
+ migrate_command = ("migrate_set_capability %s on" % c)
+ self._CallMonitorCommand(instance_name, migrate_command)
+
migrate_command = "migrate -d tcp:%s:%s" % (target, port)
self._CallMonitorCommand(instance_name, migrate_command)