X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/56725653f1d73b3f4a2dfc91f499ed3708ba716d..refs/heads/stable-2.8-grnet-rebased:/lib/hypervisor/hv_kvm.py diff --git a/lib/hypervisor/hv_kvm.py b/lib/hypervisor/hv_kvm.py index 1fdd46e..de15a89 100644 --- a/lib/hypervisor/hv_kvm.py +++ b/lib/hypervisor/hv_kvm.py @@ -114,6 +114,8 @@ _RUNTIME_ENTRY = { 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 @@ -143,29 +145,36 @@ def _GenerateDeviceKVMId(dev_type, dev, idx=None): " 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): @@ -695,6 +704,7 @@ class KVMHypervisor(hv_base.BaseHypervisor): 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), @@ -771,7 +781,9 @@ class KVMHypervisor(hv_base.BaseHypervisor): 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, @@ -1007,6 +1019,8 @@ class KVMHypervisor(hv_base.BaseHypervisor): """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)) @@ -1293,12 +1307,14 @@ class KVMHypervisor(hv_base.BaseHypervisor): 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 @@ -1309,12 +1325,11 @@ class KVMHypervisor(hv_base.BaseHypervisor): @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 @@ -1322,7 +1337,7 @@ class KVMHypervisor(hv_base.BaseHypervisor): 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: @@ -1335,7 +1350,7 @@ class KVMHypervisor(hv_base.BaseHypervisor): 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 @@ -1435,7 +1450,27 @@ class KVMHypervisor(hv_base.BaseHypervisor): 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"]) @@ -1688,7 +1723,9 @@ class KVMHypervisor(hv_base.BaseHypervisor): 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", @@ -1716,10 +1753,6 @@ class KVMHypervisor(hv_base.BaseHypervisor): 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]: @@ -1735,15 +1768,14 @@ class KVMHypervisor(hv_base.BaseHypervisor): 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 @@ -1872,6 +1904,7 @@ class KVMHypervisor(hv_base.BaseHypervisor): devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST) bdev_opts = self._GenerateKVMBlockDevicesOptions(instance, + up_hvp, kvm_disks, kvmhelp, devlist) @@ -2101,11 +2134,7 @@ class KVMHypervisor(hv_base.BaseHypervisor): 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. @@ -2238,7 +2267,8 @@ class KVMHypervisor(hv_base.BaseHypervisor): 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] @@ -2465,6 +2495,7 @@ class KVMHypervisor(hv_base.BaseHypervisor): self._WriteKVMRuntime(instance.name, info) else: + self._UnconfigureInstanceNICs(instance.name, info) self.StopInstance(instance, force=True) def MigrateInstance(self, instance, target, live): @@ -2498,6 +2529,17 @@ class KVMHypervisor(hv_base.BaseHypervisor): 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)