[libvirt] [PATCH 04/13] Add new domain device: "controller"

wolfgang.mauerer at siemens.com wolfgang.mauerer at siemens.com
Mon Nov 16 23:53:35 UTC 2009


From: Wolfgang Mauerer <wolfgang.mauerer at siemens.com>

This augments virDomainDevice with a <controller> element
that is used to represent disk controllers (e.g., scsi
controllers). The XML format is given by

<controller type="scsi" name="<my_id>">
   <address type="pci" domain="0xNUM" bus="0xNUM" slot="0xNUM"/>
</controller>

where type denotes the disk interface (scsi, ide,...), name
is an arbitrary string that identifies the controller for
disk hotadd/remove, and the <address> element specifies
the controller address on the PCI bus. Domain, bus and
slot are specified as hexadecimal numbers.

The address element can be omitted; in this case,
an address will be assigned automatically. Currently,
only hotplugging scsi controllers on a PCI bus
is supported, and this only for qemu guests

Routines for parsing this definition and the associated data
structures are included in this commit.

Signed-off-by: Wolfgang Mauerer <wolfgang.mauerer at siemens.com>
Signed-off-by: Jan Kiszka <jan.kiszka at siemens.com>
---
 docs/schemas/domain.rng      |  158 ++++++++++++++++++------
 src/conf/domain_conf.c       |  279 +++++++++++++++++++++++++++++++++++++++++-
 src/conf/domain_conf.h       |   48 +++++++-
 src/libvirt_private.syms     |    1 -
 src/qemu/qemu_driver.c       |    1 -
 src/qemu/qemu_monitor_text.c |   47 +++++++
 6 files changed, 488 insertions(+), 46 deletions(-)

diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng
index b75f17e..0d5fb47 100644
--- a/docs/schemas/domain.rng
+++ b/docs/schemas/domain.rng
@@ -393,49 +393,129 @@
   <define name="disk">
     <element name="disk">
       <optional>
-        <attribute name="device">
+	<attribute name="device">
+	  <choice>
+	    <value>floppy</value>
+	    <value>disk</value>
+	    <value>cdrom</value>
+	  </choice>
+	</attribute>
+      </optional>
+      <optional>
+	<element name="controller">
+	  <optional>
+	    <attribute name="bus">
+	      <ref name="unsignedInt"/>
+	    </attribute>
+	  </optional>
+	  <optional>
+	    <attribute name="unit">
+	      <ref name="unsignedInt"/>
+	    </attribute>
+	  </optional>
+	  <choice>
+	    <attribute name="name">
+	      <ref name="genericName"/>
+	    </attribute>
+	    <attribute name="pciaddr">
+	      <!-- Just for testing -->
+	      <ref name="deviceName"/>
+	    </attribute>
+	  </choice>
+	</element>
+      </optional>
+      <choice>
+	<group>
+	  <attribute name="type">
+	    <value>file</value>
+	  </attribute>
+	  <interleave>
+	    <optional>
+	      <element name="source">
+		<attribute name="file">
+		  <ref name="absFilePath"/>
+		</attribute>
+		<empty/>
+	      </element>
+	    </optional>
+	    <ref name="diskspec"/>
+	  </interleave>
+	</group>
+	<group>
+	  <attribute name="type">
+	    <value>block</value>
+	  </attribute>
+	  <interleave>
+	    <optional>
+	      <element name="source">
+		<attribute name="dev">
+		  <ref name="deviceName"/>
+		</attribute>
+		<empty/>
+	      </element>
+	    </optional>
+	    <ref name="diskspec"/>
+	  </interleave>
+	</group>
+	<ref name="diskspec"/>
+      </choice>
+    </element>
+  </define>
+  <define name="target">
+    <element name="target">
+      <attribute name="dev">
+        <ref name="deviceName"/>
+      </attribute>
+      <optional>
+        <attribute name="bus">
           <choice>
-            <value>floppy</value>
-            <value>disk</value>
-            <value>cdrom</value>
+            <value>ide</value>
+            <value>fdc</value>
+            <value>scsi</value>
+            <value>virtio</value>
+            <value>xen</value>
+            <value>usb</value>
+            <value>uml</value>
           </choice>
         </attribute>
       </optional>
-      <choice>
-        <group>
-          <attribute name="type">
-            <value>file</value>
-          </attribute>
-          <interleave>
-            <optional>
-              <element name="source">
-                <attribute name="file">
-                  <ref name="absFilePath"/>
-                </attribute>
-                <empty/>
-              </element>
-            </optional>
-            <ref name="diskspec"/>
-          </interleave>
-        </group>
-        <group>
-          <attribute name="type">
-            <value>block</value>
-          </attribute>
-          <interleave>
-            <optional>
-              <element name="source">
-                <attribute name="dev">
-                  <ref name="deviceName"/>
-                </attribute>
-                <empty/>
-              </element>
-            </optional>
-            <ref name="diskspec"/>
-          </interleave>
-        </group>
-        <ref name="diskspec"/>
-      </choice>
+    </element>
+  </define>
+
+  <define name="controller">
+    <element name="controller">
+      <interleave>
+	<attribute name="type">
+	  <choice>
+	    <!-- For now, only SCSI is supported -->
+	    <value>scsi</value>
+	  </choice>
+	</attribute>
+	<attribute name="id">
+	  <ref name="genericName"/>
+	</attribute>
+      </interleave>
+      <optional>
+	<element name="address">
+	  <attribute name="type">
+	    <ref name="adressType"/>
+	  </attribute>
+	  <optional>
+	    <attribute name="domain">
+	      <ref name="pciDomain"/>
+	    </attribute>
+	  </optional>
+	  <attribute name="bus">
+	    <ref name="pciBus"/>
+	  </attribute>
+	  <attribute name="slot">
+	    <ref name="pciSlot"/>
+	  </attribute>
+	  <attribute name="function">
+	    <ref name="pciFunc"/>
+	  </attribute>
+	</element>
+      </optional>
     </element>
   </define>
   <define name="target">
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index ecddb68..4e2b84e 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -80,6 +80,7 @@ VIR_ENUM_IMPL(virDomainLifecycle, VIR_DOMAIN_LIFECYCLE_LAST,
 
 VIR_ENUM_IMPL(virDomainDevice, VIR_DOMAIN_DEVICE_LAST,
               "disk",
+              "controller",
               "filesystem",
               "interface",
               "input",
@@ -88,6 +89,9 @@ VIR_ENUM_IMPL(virDomainDevice, VIR_DOMAIN_DEVICE_LAST,
               "hostdev",
               "watchdog")
 
+VIR_ENUM_IMPL(virDomainDeviceAddress, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST,
+              "pci")
+
 VIR_ENUM_IMPL(virDomainDisk, VIR_DOMAIN_DISK_TYPE_LAST,
               "block",
               "file")
@@ -342,6 +346,16 @@ void virDomainInputDefFree(virDomainInputDefPtr def)
     VIR_FREE(def);
 }
 
+void virDomainDeviceAddressFree(virDomainDeviceAddressPtr def)
+{
+    if (!def)
+        return;
+
+    VIR_FREE(def->data);
+
+    VIR_FREE(def);
+}
+
 void virDomainDiskDefFree(virDomainDiskDefPtr def)
 {
     if (!def)
@@ -357,6 +371,18 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def)
     VIR_FREE(def);
 }
 
+void virDomainControllerDefFree(virDomainControllerDefPtr def)
+{
+    if (!def)
+        return;
+
+    VIR_FREE(def->type);
+    VIR_FREE(def->name);
+    VIR_FREE(def->address);
+
+    VIR_FREE(def);
+}
+
 void virDomainFSDefFree(virDomainFSDefPtr def)
 {
     if (!def)
@@ -708,6 +734,152 @@ void virDomainRemoveInactive(virDomainObjListPtr doms,
     virHashRemoveEntry(doms->objs, uuidstr, virDomainObjListDeallocator);
 }
 
+/* Generate a string representation of a device address
+ * @param address Device address to stringify
+ */
+const char *virDomainFormatDeviceAddress(virDomainDeviceAddressPtr address) {
+    int ret;
+    char *tmp;
+
+    if (!address) {
+        return "(undefined)";
+    }
+
+    switch (address->type) {
+    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI:
+        ret = virAsprintf(&tmp, "%x:%x:%x", address->data.pci.domain,
+                          address->data.pci.bus, address->data.pci.slot);
+        if  (!ret)
+            return tmp;
+        break;
+    default:
+        return "(unknown)";
+    }
+
+    return "(error)";
+}
+
+/* Compare two device addresses. Returns true if addresses are
+ * identical and false if the they differ (or are of different types)
+ * @param a, @para b Pointers to the addresses
+ */
+int virDomainCompareDeviceAddresses(virDomainDeviceAddressPtr a,
+                                    virDomainDeviceAddressPtr b) {
+    if (!a || !b || a->type != b-> type) {
+        return 0;
+    }
+
+    switch (a->type) {
+    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI:
+        if (a->data.pci.domain == b->data.pci.domain &&
+            a->data.pci.bus    == b->data.pci.bus &&
+            a->data.pci.slot   == b->data.pci.slot) {
+            return 1;
+        }
+
+        return 0;
+    }
+
+    return 0;
+}
+
+/* Parse the XML definition for a device address
+ * @param node XML nodeset to parse for device address definition
+ */
+static virDomainDeviceAddressPtr
+virDomainDeviceAddressParseXML(virConnectPtr conn,
+                              xmlNodePtr node) {
+    virDomainDeviceAddressPtr def;
+    char *type = NULL;
+    char *domain, *slot, *bus, *function;
+
+    if (VIR_ALLOC(def) < 0) {
+        virReportOOMError(conn);
+        return NULL;
+    }
+
+     type = virXMLPropString(node, "type");
+
+     if (type) {
+         if ((def->type = virDomainDeviceAddressTypeFromString(type)) < 0) {
+             virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                  _("unknown address type '%s'"), type);
+             goto error;
+         }
+     } else {
+         virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              _("No type specified for device address"));
+         goto error;
+     }
+
+     switch (def->type) {
+     case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI:
+         domain   = virXMLPropString(node, "domain");
+         bus      = virXMLPropString(node, "bus");
+         slot     = virXMLPropString(node, "slot");
+         function = virXMLPropString(node, "function");
+
+         def->data.pci.domain = -1;
+         if (domain &&
+             virStrToLong_i(domain, NULL, 16, &def->data.pci.domain) < 0) {
+             virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s",
+                              _("Cannot parse <address> 'domain' attribute"));
+             goto error;
+         }
+
+         def->data.pci.bus = -1;
+         if (bus &&
+             virStrToLong_i(bus, NULL, 16, &def->data.pci.bus) < 0) {
+             virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s",
+                              _("Cannot parse <address> 'bus' attribute"));
+             goto error;
+         }
+
+         def->data.pci.slot = -1;
+         if (slot &&
+             virStrToLong_i(slot, NULL, 16, &def->data.pci.slot) < 0) {
+             virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s",
+                              _("Cannot parse <address> 'slot' attribute"));
+             goto error;
+         }
+
+         def->data.pci.function = -1;
+         if (function &&
+             virStrToLong_i(function, NULL, 16, &def->data.pci.function) < 0) {
+             virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s",
+                              _("Cannot parse <address> 'function' attribute"));
+             goto error;
+         }
+
+         if (def->data.pci.domain == -1 || def->data.pci.bus == -1
+             || def->data.pci.slot == -1) {
+             virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s",
+                              _("Unsufficient specification for PCI address"));
+             goto error;
+         }
+
+         break;
+     default:
+         /* Should not happen */
+         virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              _("Unknown device address type"));
+         goto error;
+     }
+
+cleanup:
+     VIR_FREE(domain);
+     VIR_FREE(bus);
+     VIR_FREE(slot);
+     VIR_FREE(function);
+
+     return def;
+
+error:
+     virDomainDeviceAddressFree(def);
+     def = NULL;
+     goto cleanup;
+}
+
 
 /* Parse the XML definition for a disk
  * @param node XML nodeset to parse for disk definition
@@ -725,9 +897,10 @@ virDomainDiskDefParseXML(virConnectPtr conn,
     char *source = NULL;
     char *target = NULL;
     char *controller = NULL;
+    char *controller_pci_addr = NULL;
     char *bus_id = NULL;
     char *unit_id = NULL;
-    char *controller_id = NULL;
+    char *controller_name = NULL;
     char *bus = NULL;
     char *unit = NULL;
     char *cachetag = NULL;
@@ -782,8 +955,9 @@ virDomainDiskDefParseXML(virConnectPtr conn,
                     memmove(target, target+6, strlen(target)-5);
             } else if ((controller == NULL) &&
                        (xmlStrEqual(cur->name, BAD_CAST "controller"))) {
-	        controller_id = virXMLPropString(cur, "id");
-	        bus_id = virXMLPropString(cur, "bus");
+                controller_name = virXMLPropString(cur, "name");
+                controller_pci_addr =  virXMLPropString(cur, "pci_addr");
+                bus_id = virXMLPropString(cur, "bus");
 	        unit_id = virXMLPropString(cur, "unit");
             } else if ((driverName == NULL) &&
                        (xmlStrEqual(cur->name, BAD_CAST "driver"))) {
@@ -886,7 +1060,7 @@ virDomainDiskDefParseXML(virConnectPtr conn,
     }
 
     if (controller) {
-        def->controller_id = controller_id;
+        def->controller_name = controller_name;
 
         def->bus_id = -1;
 	if (bus_id && virStrToLong_i(bus_id, NULL, 10, &def->bus_id) < 0) {
@@ -901,8 +1075,26 @@ virDomainDiskDefParseXML(virConnectPtr conn,
 			 _("Cannot parse <controller> 'unit' attribute"));
 	    goto error;
 	}
+
+	/* TODO: Should we re-use devaddr for this purpose,
+           or is an extra field justified? */
+	if (controller_pci_addr &&
+	    sscanf(controller_pci_addr, "%x:%x:%x",
+		   &def->controller_pci_addr.domain,
+		   &def->controller_pci_addr.bus,
+		   &def->controller_pci_addr.slot) < 3) {
+	    virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+		  _("Unable to parse <controller> 'pci_addr' parameter '%s'"),
+				 controller_pci_addr);
+	    goto error;
+	}
     }
 
+    VIR_DEBUG("Controller PCI addr for disk parsed as %x:%x:%x\n",
+              def->controller_pci_addr.domain,
+              def->controller_pci_addr.bus,
+              def->controller_pci_addr.slot);
+
     if (def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY &&
         def->bus != VIR_DOMAIN_DISK_BUS_FDC) {
         virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
@@ -969,6 +1161,59 @@ cleanup:
 }
 
 
+/* Parse the XML definition for a controller
+ * @param node XML nodeset to parse for controller definition
+ */
+static virDomainControllerDefPtr
+virDomainControllerDefParseXML(virConnectPtr conn,
+			       xmlNodePtr node,
+			       int flags ATTRIBUTE_UNUSED) {
+    virDomainControllerDefPtr def;
+    xmlNodePtr cur;
+    char *type = NULL;
+
+    if (VIR_ALLOC(def) < 0) {
+        virReportOOMError(conn);
+        return NULL;
+    }
+
+    def->address = NULL;
+    type = virXMLPropString(node, "type");
+    def->type = VIR_DOMAIN_DISK_BUS_SCSI;
+    if (type) {
+        if ((def->type = virDomainDiskBusTypeFromString(type)) < 0) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("unknown disk bus type '%s'"), type);
+            goto error;
+        }
+    }
+
+    def->name = virXMLPropString(node, "name");
+
+    cur = node->children;
+    while (cur != NULL) {
+        if (cur->type == XML_ELEMENT_NODE &&
+            def->address == NULL &&
+            xmlStrEqual(cur->name, BAD_CAST "address")) {
+
+            def->address = virDomainDeviceAddressParseXML(conn, cur);
+        }
+
+        cur = cur->next;
+    }
+
+
+cleanup:
+    VIR_FREE(type);
+
+    return def;
+
+ error:
+    virDomainControllerDefFree(def);
+    def = NULL;
+    goto cleanup;
+}
+
 /* Parse the XML definition for a disk
  * @param node XML nodeset to parse for disk definition
  */
@@ -2616,6 +2861,11 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virConnectPtr conn,
         dev->type = VIR_DOMAIN_DEVICE_DISK;
         if (!(dev->data.disk = virDomainDiskDefParseXML(conn, node, flags)))
             goto error;
+    } else if (xmlStrEqual(node->name, BAD_CAST "controller")) {
+        dev->type = VIR_DOMAIN_DEVICE_CONTROLLER;
+        if (!(dev->data.controller =
+	      virDomainControllerDefParseXML(conn, node, flags)))
+            goto error;
     } else if (xmlStrEqual(node->name, BAD_CAST "filesystem")) {
         dev->type = VIR_DOMAIN_DEVICE_FS;
         if (!(dev->data.fs = virDomainFSDefParseXML(conn, node, flags)))
@@ -3033,6 +3283,27 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn,
     }
     VIR_FREE(nodes);
 
+    /* analysis of the disk controllers */
+    if ((n = virXPathNodeSet(conn, "./devices/controller", ctxt, &nodes)) < 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("cannot extract controller devices"));
+        goto error;
+    }
+    if (n && VIR_ALLOC_N(def->controllers, n) < 0)
+        goto no_memory;
+    for (i = 0 ; i < n ; i++) {
+        virDomainControllerDefPtr controller =
+	    virDomainControllerDefParseXML(conn, nodes[i], flags);
+        if (!controller)
+            goto error;
+
+        def->controllers[def->ncontrollers++] = controller;
+    }
+    /*    qsort(def->controllers, def->ncontrollers, sizeof(*def->controllers),
+          virDomainControllerQSort); */
+    VIR_FREE(nodes);
+
+
     /* analysis of the filesystems */
     if ((n = virXPathNodeSet(conn, "./devices/filesystem", ctxt, &nodes)) < 0) {
         virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index c034ae4..591143f 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -63,6 +63,30 @@ enum virDomainVirtType {
     VIR_DOMAIN_VIRT_LAST,
 };
 
+enum virDomainDeviceAddressType {
+    VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI,
+
+    VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST
+};
+
+typedef struct _virDomainDevicePCIAddress virDomainDevicePCIAddress;
+struct _virDomainDevicePCIAddress {
+    int domain;
+    int bus;
+    int slot;
+    int function;
+};
+
+typedef struct _virDomainDeviceAddress virDomainDeviceAddress;
+typedef virDomainDeviceAddress *virDomainDeviceAddressPtr;
+struct _virDomainDeviceAddress {
+    int type;
+    union {
+        virDomainDevicePCIAddress pci;
+    } data;
+};
+
+
 /* Two types of disk backends */
 enum virDomainDiskType {
     VIR_DOMAIN_DISK_TYPE_BLOCK,
@@ -112,7 +136,12 @@ struct _virDomainDiskDef {
     int unit_id;      /* Unit on the controller */
     char *src;
     char *dst;
-    char *controller_id;
+    char *controller_name;
+    struct {
+        unsigned domain;
+        unsigned bus;
+        unsigned slot;
+    } controller_pci_addr;
     char *driverName;
     char *driverType;
     char *serial;
@@ -127,6 +156,15 @@ struct _virDomainDiskDef {
     virStorageEncryptionPtr encryption;
 };
 
+/* Stores the virtual disk controller configuration */
+typedef struct _virDomainControllerDef virDomainControllerDef;
+typedef virDomainControllerDef *virDomainControllerDefPtr;
+struct _virDomainControllerDef {
+    int type;
+    char *name;
+    virDomainDeviceAddressPtr address;
+};
+
 static inline int
 virDiskHasValidPciAddr(virDomainDiskDefPtr def)
 {
@@ -483,6 +521,7 @@ enum virDomainDeviceType {
     VIR_DOMAIN_DEVICE_VIDEO,
     VIR_DOMAIN_DEVICE_HOSTDEV,
     VIR_DOMAIN_DEVICE_WATCHDOG,
+    VIR_DOMAIN_DEVICE_CONTROLLER,
 
     VIR_DOMAIN_DEVICE_LAST,
 };
@@ -493,6 +532,7 @@ struct _virDomainDeviceDef {
     int type;
     union {
         virDomainDiskDefPtr disk;
+        virDomainControllerDefPtr controller;
         virDomainFSDefPtr fs;
         virDomainNetDefPtr net;
         virDomainInputDefPtr input;
@@ -605,6 +645,9 @@ struct _virDomainDef {
     int ndisks;
     virDomainDiskDefPtr *disks;
 
+    int ncontrollers;
+    virDomainControllerDefPtr *controllers;
+
     int nfss;
     virDomainFSDefPtr *fss;
 
@@ -691,6 +734,7 @@ virDomainObjPtr virDomainFindByName(const virDomainObjListPtr doms,
 void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def);
 void virDomainInputDefFree(virDomainInputDefPtr def);
 void virDomainDiskDefFree(virDomainDiskDefPtr def);
+void virDomainControllerDefFree(virDomainControllerDefPtr def);
 void virDomainFSDefFree(virDomainFSDefPtr def);
 void virDomainNetDefFree(virDomainNetDefPtr def);
 void virDomainChrDefFree(virDomainChrDefPtr def);
@@ -699,6 +743,7 @@ void virDomainWatchdogDefFree(virDomainWatchdogDefPtr def);
 void virDomainVideoDefFree(virDomainVideoDefPtr def);
 void virDomainHostdevDefFree(virDomainHostdevDefPtr def);
 void virDomainDeviceDefFree(virDomainDeviceDefPtr def);
+void virDomainDeviceAddressFree(virDomainDeviceAddressPtr def);
 void virDomainDefFree(virDomainDefPtr vm);
 void virDomainObjFree(virDomainObjPtr vm);
 
@@ -832,6 +877,7 @@ VIR_ENUM_DECL(virDomainBoot)
 VIR_ENUM_DECL(virDomainFeature)
 VIR_ENUM_DECL(virDomainLifecycle)
 VIR_ENUM_DECL(virDomainDevice)
+VIR_ENUM_DECL(virDomainDeviceAddress)
 VIR_ENUM_DECL(virDomainDisk)
 VIR_ENUM_DECL(virDomainDiskDevice)
 VIR_ENUM_DECL(virDomainDiskBus)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index fb81ace..92cf86c 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -68,7 +68,6 @@ virCgroupGetCpuacctUsage;
 virCgroupGetFreezerState;
 virCgroupSetFreezerState;
 
-
 # datatypes.h
 virGetDomain;
 virGetInterface;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index f022f89..af7b59c 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -4356,7 +4356,6 @@ static int qemudDomainChangeEjectableMedia(virConnectPtr conn,
     return ret;
 }
 
-
 static int qemudDomainAttachPciDiskDevice(virConnectPtr conn,
                                           virDomainObjPtr vm,
                                           virDomainDeviceDefPtr dev)
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index 35cd330..f6f3e58 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -1481,6 +1481,53 @@ qemuMonitorParsePciAddReply(virDomainObjPtr vm,
     return 0;
 }
 
+static int
+qemudParseDriveAddReply(virDomainObjPtr vm,
+			const char *reply,
+			int *bus,
+			int *unit)
+{
+    char *s, *e;
+
+    DEBUG("%s: drive_add reply: %s", vm->def->name, reply);
+
+    /* If the command succeeds qemu prints:
+     * OK bus X, unit Y
+     */
+
+    if (!(s = strstr(reply, "OK ")))
+        return -1;
+
+    s += 3;
+
+    if (STRPREFIX(s, "bus ")) {
+        s += strlen("bus ");
+
+        if (virStrToLong_i(s, &e, 10, bus) == -1) {
+            VIR_WARN(_("Unable to parse bus '%s'\n"), s);
+            return -1;
+        }
+
+        if (!STRPREFIX(e, ", ")) {
+            VIR_WARN(_("Expected ', ' parsing drive_add reply '%s'\n"), s);
+            return -1;
+        }
+        s = e + 2;
+    }
+
+    if (!STRPREFIX(s, "unit ")) {
+        VIR_WARN(_("Expected 'unit ' parsing drive_add reply '%s'\n"), s);
+        return -1;
+    }
+    s += strlen("bus ");
+
+    if (virStrToLong_i(s, &e, 10, unit) == -1) {
+        VIR_WARN(_("Unable to parse unit number '%s'\n"), s);
+        return -1;
+    }
+
+    return 0;
+}
 
 int qemuMonitorAddPCIHostDevice(const virDomainObjPtr vm,
                                 unsigned hostDomain ATTRIBUTE_UNUSED,
-- 
1.6.4




More information about the libvir-list mailing list