QEMU commits 48f364dd and da2cf4e8 included in version 2.2 and later state the following:
* HMP's drive_del is not supported any more on a drive added via QMP's blockdev-add * Stay away from immature blockdev-add unless you want to help with development. To this end, currently we cannot hot-remove a disk that was previously hot-added. Here, we work around this constrain by using HMP's drive_add instead of blockdev-add. This must be done via a callback in HotAddDisk() wrapper method, due to the fact that if a QMP connection terminates before a drive keeps a reference to the fd passed via the add-fd QMP command, then the fd gets closed and cannot be used later. So we open a QMP connection, pass the fd, then invoke the drive_add HMP command referencing the corresponding fdset, then remove the fdset, then add the device and then terminate the QMP connection. Signed-off-by: Dimitris Aragiorgis <[email protected]> --- lib/hypervisor/hv_kvm/__init__.py | 15 ++++++++++++++- lib/hypervisor/hv_kvm/monitor.py | 36 +++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/lib/hypervisor/hv_kvm/__init__.py b/lib/hypervisor/hv_kvm/__init__.py index ffd530b..7b0bb2d 100644 --- a/lib/hypervisor/hv_kvm/__init__.py +++ b/lib/hypervisor/hv_kvm/__init__.py @@ -2074,7 +2074,20 @@ class KVMHypervisor(hv_base.BaseHypervisor): runtime = self._LoadKVMRuntime(instance) if dev_type == constants.HOTPLUG_TARGET_DISK: uri = _GetDriveURI(device, extra[0], extra[1]) - self.qmp.HotAddDisk(device, kvm_devid, uri) + + def drive_add_fn(filename): + """Helper function that uses HMP to hot-add a drive.""" + cmd = "drive_add dummy file=%s,if=none,id=%s,format=raw" % \ + (filename, kvm_devid) + self._CallMonitorCommand(instance.name, cmd) + + # This must be done indirectly due to the fact that we pass the drive's + # file descriptor via QMP first, then we add the corresponding drive that + # refers to this fd. Note that if the QMP connection terminates before + # a drive which keeps a reference to the fd passed via the add-fd QMP + # command has been created, then the fd gets closed and cannot be used + # later (e.g., via an drive_add HMP command). + self.qmp.HotAddDisk(device, kvm_devid, uri, drive_add_fn) elif dev_type == constants.HOTPLUG_TARGET_NIC: kvmpath = instance.hvparams[constants.HV_KVM_PATH] kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP) diff --git a/lib/hypervisor/hv_kvm/monitor.py b/lib/hypervisor/hv_kvm/monitor.py index 1d68849..74317f6 100644 --- a/lib/hypervisor/hv_kvm/monitor.py +++ b/lib/hypervisor/hv_kvm/monitor.py @@ -537,12 +537,13 @@ class QmpConnection(MonitorSocket): self.Execute("netdev_del", {"id": devid}) @_ensure_connection - def HotAddDisk(self, disk, devid, uri): + def HotAddDisk(self, disk, devid, uri, drive_add_fn=None): """Hot-add a disk Try opening the device to obtain a fd and pass it with SCM_RIGHTS. This will be omitted in case of userspace access mode (open will fail). - Then use blockdev-add and then device_add. + Then use blockdev-add QMP command or drive_add_fn() callback if any. + The add the guest device. """ if os.path.exists(uri): @@ -556,17 +557,30 @@ class QmpConnection(MonitorSocket): filename = uri fdset = None - arguments = { - "options": { - "driver": "raw", - "id": devid, - "file": { - "driver": "file", - "filename": filename, + # FIXME: Use blockdev-add/blockdev-del when properly implemented in QEMU. + # This is an ugly hack to work around QEMU commits 48f364dd and da2cf4e8: + # * HMP's drive_del is not supported any more on a drive added + # via QMP's blockdev-add + # * Stay away from immature blockdev-add unless you want to help + # with development. + # Using drive_add here must be done via a callback due to the fact that if + # a QMP connection terminates before a drive keeps a reference to the fd + # passed via the add-fd QMP command, then the fd gets closed and + # cannot be used later. + if drive_add_fn: + drive_add_fn(filename) + else: + arguments = { + "options": { + "driver": "raw", + "id": devid, + "file": { + "driver": "file", + "filename": filename, + } } } - } - self.Execute("blockdev-add", arguments) + self.Execute("blockdev-add", arguments) if fdset is not None: self._RemoveFdset(fdset) -- 1.7.10.4
