Change hotplug-related methods (i.e., HotAddNic() and HotAddDisk())
to use slot 'hvinfo' instead of slot 'pci'.

Change HasPCIDevice() to HasDevice(). This will parse the output of
the 'query-block' and 'query-pci' QMP commands and try to match the
given device ID.

Make _VerifyHotplugCommand() depend only on the device ID and not
on the whole device object.

Signed-off-by: Dimitris Aragiorgis <[email protected]>
---
 lib/hypervisor/hv_kvm/__init__.py |   14 +++----
 lib/hypervisor/hv_kvm/monitor.py  |   75 ++++++++++++++++++++++++++++++-------
 2 files changed, 67 insertions(+), 22 deletions(-)

diff --git a/lib/hypervisor/hv_kvm/__init__.py 
b/lib/hypervisor/hv_kvm/__init__.py
index 67f2845..d81ced4 100644
--- a/lib/hypervisor/hv_kvm/__init__.py
+++ b/lib/hypervisor/hv_kvm/__init__.py
@@ -283,8 +283,7 @@ def _GetExistingDeviceInfo(dev_type, device, runtime):
   @type runtime: tuple (cmd, nics, hvparams, disks)
   @param runtime: the runtime data to search for the device
   @raise errors.HotplugError: in case the requested device does not
-    exist (e.g. device has been added without --hotplug option) or
-    device info has not pci slot (e.g. old devices in the cluster)
+    exist (e.g. device has been added without --hotplug option)
 
   """
   index = _DEVICE_RUNTIME_INDEX[dev_type]
@@ -2041,18 +2040,17 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     return bus_slots
 
   @_with_qmp
-  def _VerifyHotplugCommand(self, _instance,
-                            device, kvm_devid, should_exist):
+  def _VerifyHotplugCommand(self, _instance, kvm_devid, should_exist):
     """Checks if a previous hotplug command has succeeded.
 
     Depending on the should_exist value, verifies that an entry identified by
-    the PCI slot and device ID is present or not.
+    device ID is present or not.
 
     @raise errors.HypervisorError: if result is not the expected one
 
     """
     for i in range(5):
-      found = self.qmp.HasPCIDevice(device, kvm_devid)
+      found = self.qmp.HasDevice(kvm_devid)
       logging.info("Verifying hotplug command (retry %s): %s", i, found)
       if found and should_exist:
         break
@@ -2097,7 +2095,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
       self.qmp.HotAddNic(device, kvm_devid, tapfds, vhostfds, features)
       utils.WriteFile(self._InstanceNICFile(instance.name, seq), data=tap)
 
-    self._VerifyHotplugCommand(instance, device, kvm_devid, True)
+    self._VerifyHotplugCommand(instance, kvm_devid, True)
     # update relevant entries in runtime file
     index = _DEVICE_RUNTIME_INDEX[dev_type]
     entry = _RUNTIME_ENTRY[dev_type](device, extra)
@@ -2124,7 +2122,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     elif dev_type == constants.HOTPLUG_TARGET_NIC:
       self.qmp.HotDelNic(kvm_devid)
       utils.RemoveFile(self._InstanceNICFile(instance.name, seq))
-    self._VerifyHotplugCommand(instance, kvm_device, kvm_devid, False)
+    self._VerifyHotplugCommand(instance, kvm_devid, False)
     index = _DEVICE_RUNTIME_INDEX[dev_type]
     runtime[index].remove(entry)
     self._SaveKVMRuntime(instance, runtime)
diff --git a/lib/hypervisor/hv_kvm/monitor.py b/lib/hypervisor/hv_kvm/monitor.py
index f2b7d02..1d68849 100644
--- a/lib/hypervisor/hv_kvm/monitor.py
+++ b/lib/hypervisor/hv_kvm/monitor.py
@@ -256,6 +256,11 @@ class QmpConnection(MonitorSocket):
   _QUERY_COMMANDS = "query-commands"
   _MESSAGE_END_TOKEN = "\r\n"
   _QEMU_PCI_SLOTS = 32 # The number of PCI slots QEMU exposes by default
+  # List of valid attributes for the device_add QMP command.
+  # Extra attributes found in device's hvinfo will be ignored.
+  _DEVICE_ATTRIBUTES = [
+    "driver", "id", "bus", "addr", "channel", "scsi-id", "lun"
+    ]
 
   def __init__(self, monitor_filename):
     super(QmpConnection, self).__init__(monitor_filename)
@@ -459,6 +464,15 @@ class QmpConnection(MonitorSocket):
 
       return response[self._RETURN_KEY]
 
+  def _filter_hvinfo(self, hvinfo):
+    """Filter non valid keys of the device's hvinfo (if any)."""
+    ret = {}
+    for k in self._DEVICE_ATTRIBUTES:
+      if k in hvinfo:
+        ret[k] = hvinfo[k]
+
+    return ret
+
   @_ensure_connection
   def HotAddNic(self, nic, devid, tapfds=None, vhostfds=None, features=None):
     """Hot-add a NIC
@@ -501,13 +515,12 @@ class QmpConnection(MonitorSocket):
     self.Execute("netdev_add", arguments)
 
     arguments = {
-      "driver": "virtio-net-pci",
-      "id": devid,
-      "bus": "pci.0",
-      "addr": hex(nic.pci),
       "netdev": devid,
       "mac": nic.mac,
     }
+    # Note that hvinfo that _GenerateDeviceHVInfo() creates
+    # sould include *only* the driver, id, bus, and addr keys
+    arguments.update(self._filter_hvinfo(nic.hvinfo))
     if enable_mq:
       arguments.update({
         "mq": "on",
@@ -559,12 +572,12 @@ class QmpConnection(MonitorSocket):
       self._RemoveFdset(fdset)
 
     arguments = {
-      "driver": "virtio-blk-pci",
-      "id": devid,
-      "bus": "pci.0",
-      "addr": hex(disk.pci),
       "drive": devid,
     }
+    # Note that hvinfo that _GenerateDeviceHVInfo() creates
+    # sould include *only* the driver, id, bus, and
+    # addr or channel, scsi-id, and lun keys
+    arguments.update(self._filter_hvinfo(disk.hvinfo))
     self.Execute("device_add", arguments)
 
   @_ensure_connection
@@ -589,17 +602,51 @@ class QmpConnection(MonitorSocket):
     devices = bus["devices"]
     return devices
 
+  def _HasPCIDevice(self, devid):
+    """Check if a specific device ID exists on the PCI bus.
+
+    """
+    for d in self._GetPCIDevices():
+      if d["qdev_id"] == devid:
+        return True
+
+    return False
+
+  def _GetBlockDevices(self):
+    """Get the block devices of a running instance.
+
+    The query-block QMP command returns a list of dictionaries
+    including information for each virtual disk. For example:
+
+    [{"device": "disk-049f140d", "inserted": {"file": ..., "image": ...}}]
+
+    @rtype: list of dicts
+    @return: Info about the virtual disks of the instance.
+
+    """
+    self._check_connection()
+    devices = self.Execute("query-block")
+    return devices
+
+  def _HasBlockDevice(self, devid):
+    """Check if a specific device ID exists among block devices.
+
+    """
+    for d in self._GetBlockDevices():
+      if d["device"] == devid:
+        return True
+
+    return False
+
   @_ensure_connection
-  def HasPCIDevice(self, device, devid):
+  def HasDevice(self, devid):
     """Check if a specific device exists or not on a running instance.
 
-    It will match the PCI slot of the device and the id currently
-    obtained by _GenerateDeviceKVMId().
+    It first checks the PCI devices and then the block devices.
 
     """
-    for d in self._GetPCIDevices():
-      if d["qdev_id"] == devid and d["slot"] == device.pci:
-        return True
+    if (self._HasPCIDevice(devid) or self._HasBlockDevice(devid)):
+      return True
 
     return False
 
-- 
1.7.10.4

Reply via email to