]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
Add support for NIC hotplug using netdev_add in QEMU
authorDaniel P. Berrange <berrange@redhat.com>
Thu, 15 Apr 2010 13:52:03 +0000 (14:52 +0100)
committerDaniel P. Berrange <berrange@redhat.com>
Fri, 14 May 2010 13:16:36 +0000 (09:16 -0400)
QEMU is gaining a new monitor command netdev_add for hotplugging
NICs using the netdev backend code. We already support this on
the command this, though it is disabled. This adds support for
hotplug too, also to remain disabled until 0.13 QEMU is released

* src/qemu/qemu_driver.c: Support netdev hotplug for NICs
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
  src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
  src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
  support for netdev_add and netdev_remove commands

src/qemu/qemu_driver.c
src/qemu/qemu_monitor.c
src/qemu/qemu_monitor.h
src/qemu/qemu_monitor_json.c
src/qemu/qemu_monitor_json.h
src/qemu/qemu_monitor_text.c
src/qemu/qemu_monitor_text.h

index bb1079e640780ee2e7017b73f4394193ebccddeb..76487195169ae18bd668289d33e3eb984aab8255 100644 (file)
@@ -7194,12 +7194,17 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn,
         qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &net->info) < 0)
         goto cleanup;
 
-    vlan = qemuDomainNetVLAN(net);
+    if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
+        (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
+        vlan = -1;
+    } else {
+        vlan = qemuDomainNetVLAN(net);
 
-    if (vlan < 0) {
-        qemuReportError(VIR_ERR_NO_SUPPORT, "%s",
-                        _("Unable to attach network devices without vlan"));
-        goto cleanup;
+        if (vlan < 0) {
+            qemuReportError(VIR_ERR_NO_SUPPORT, "%s",
+                            _("Unable to attach network devices without vlan"));
+            goto cleanup;
+        }
     }
 
     if (tapfd != -1) {
@@ -7227,9 +7232,17 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn,
     }
 
     qemuDomainObjEnterMonitorWithDriver(driver, vm);
-    if (qemuMonitorAddHostNetwork(priv->mon, netstr) < 0) {
-        qemuDomainObjExitMonitorWithDriver(driver, vm);
-        goto try_tapfd_close;
+    if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
+        (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
+        if (qemuMonitorAddNetdev(priv->mon, netstr) < 0) {
+            qemuDomainObjExitMonitorWithDriver(driver, vm);
+            goto try_tapfd_close;
+        }
+    } else {
+        if (qemuMonitorAddHostNetwork(priv->mon, netstr) < 0) {
+            qemuDomainObjExitMonitorWithDriver(driver, vm);
+            goto try_tapfd_close;
+        }
     }
     qemuDomainObjExitMonitorWithDriver(driver, vm);
 
@@ -7286,7 +7299,20 @@ cleanup:
 
 try_remove:
     if (vlan < 0) {
-        VIR_WARN0(_("Unable to remove network backend"));
+        if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
+            (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
+            char *netdev_name;
+            if (virAsprintf(&netdev_name, "host%s", net->info.alias) < 0)
+                goto no_memory;
+            qemuDomainObjEnterMonitorWithDriver(driver, vm);
+            if (qemuMonitorRemoveNetdev(priv->mon, netdev_name) < 0)
+                VIR_WARN(_("Failed to remove network backend for netdev %s"),
+                         netdev_name);
+            qemuDomainObjExitMonitorWithDriver(driver, vm);
+            VIR_FREE(netdev_name);
+        } else {
+            VIR_WARN0(_("Unable to remove network backend"));
+        }
     } else {
         char *hostnet_name;
         if (virAsprintf(&hostnet_name, "host%s", net->info.alias) < 0)
@@ -8137,9 +8163,17 @@ qemudDomainDetachNetDevice(struct qemud_driver *driver,
         }
     }
 
-    if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0) {
-        qemuDomainObjExitMonitorWithDriver(driver, vm);
-        goto cleanup;
+    if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
+        (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
+        if (qemuMonitorRemoveNetdev(priv->mon, hostnet_name) < 0) {
+            qemuDomainObjExitMonitorWithDriver(driver, vm);
+            goto cleanup;
+        }
+    } else {
+        if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0) {
+            qemuDomainObjExitMonitorWithDriver(driver, vm);
+            goto cleanup;
+        }
     }
     qemuDomainObjExitMonitorWithDriver(driver, vm);
 
index 7517e3999add874c4cbbf66c9a49dec89f53cfda..2ce3d568eac579605c27807ea0276aa54f147083 100644 (file)
@@ -1425,6 +1425,37 @@ int qemuMonitorRemoveHostNetwork(qemuMonitorPtr mon,
     return ret;
 }
 
+
+int qemuMonitorAddNetdev(qemuMonitorPtr mon,
+                         const char *netdevstr)
+{
+    int ret;
+    DEBUG("mon=%p, fd=%d netdevstr=%s",
+          mon, mon->fd, netdevstr);
+
+    if (mon->json)
+        ret = qemuMonitorJSONAddNetdev(mon, netdevstr);
+    else
+        ret = qemuMonitorTextAddNetdev(mon, netdevstr);
+    return ret;
+}
+
+
+int qemuMonitorRemoveNetdev(qemuMonitorPtr mon,
+                            const char *alias)
+{
+    int ret;
+    DEBUG("mon=%p, fd=%d alias=%s",
+          mon, mon->fd, alias);
+
+    if (mon->json)
+        ret = qemuMonitorJSONRemoveNetdev(mon, alias);
+    else
+        ret = qemuMonitorTextRemoveNetdev(mon, alias);
+    return ret;
+}
+
+
 int qemuMonitorGetPtyPaths(qemuMonitorPtr mon,
                            virHashTablePtr paths)
 {
index 9760b4c7eae6657b50a9097fbd54a2a9b8bae258..ecbf655fd17ee1e6312cfd3e6ef0e77de91d6940 100644 (file)
@@ -327,6 +327,12 @@ int qemuMonitorRemoveHostNetwork(qemuMonitorPtr mon,
                                  int vlan,
                                  const char *netname);
 
+int qemuMonitorAddNetdev(qemuMonitorPtr mon,
+                         const char *netdevstr);
+
+int qemuMonitorRemoveNetdev(qemuMonitorPtr mon,
+                            const char *alias);
+
 int qemuMonitorGetPtyPaths(qemuMonitorPtr mon,
                            virHashTablePtr paths);
 
index 4e8fbfca6b3409c571084798e529873c2b83dbba..3c0a82365f31d396e2f13aa56741f9b0e62441be 100644 (file)
@@ -453,6 +453,65 @@ error:
 }
 
 
+static void
+qemuFreeKeywords(int nkeywords, char **keywords, char **values)
+{
+    int i;
+    for (i = 0 ; i < nkeywords ; i++) {
+        VIR_FREE(keywords[i]);
+        VIR_FREE(values[i]);
+    }
+    VIR_FREE(keywords);
+    VIR_FREE(values);
+}
+
+static virJSONValuePtr
+qemuMonitorJSONKeywordStringToJSON(const char *str, const char *firstkeyword)
+{
+    virJSONValuePtr ret = NULL;
+    char **keywords = NULL;
+    char **values = NULL;
+    int nkeywords = 0;
+    int i;
+
+    if (!(ret = virJSONValueNewObject()))
+        goto no_memory;
+
+    nkeywords = qemuParseKeywords(str, &keywords, &values, 1);
+
+    if (nkeywords < 0)
+        goto error;
+
+    for (i = 0 ; i < nkeywords ; i++) {
+        if (values[i] == NULL) {
+            if (i != 0) {
+                qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                                _("unexpected empty keyword in %s"), str);
+                goto error;
+            } else {
+                /* This 3rd arg isn't a typo - the way the parser works is
+                 * that the value ended up in the keyword field */
+                if (virJSONValueObjectAppendString(ret, firstkeyword, keywords[i]) < 0)
+                    goto no_memory;
+            }
+        } else {
+            if (virJSONValueObjectAppendString(ret, keywords[i], values[i]) < 0)
+                goto no_memory;
+        }
+    }
+
+    qemuFreeKeywords(nkeywords, keywords, values);
+    return ret;
+
+no_memory:
+    virReportOOMError();
+error:
+    qemuFreeKeywords(nkeywords, keywords, values);
+    virJSONValueFree(ret);
+    return NULL;
+}
+
+
 static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED)
 {
     qemuMonitorEmitShutdown(mon);
@@ -1754,6 +1813,63 @@ int qemuMonitorJSONRemoveHostNetwork(qemuMonitorPtr mon,
 }
 
 
+int qemuMonitorJSONAddNetdev(qemuMonitorPtr mon,
+                             const char *netdevstr)
+{
+    int ret = -1;
+    virJSONValuePtr cmd = NULL;
+    virJSONValuePtr reply = NULL;
+    virJSONValuePtr args = NULL;
+
+    cmd = qemuMonitorJSONMakeCommand("netdev_add", NULL);
+    if (!cmd)
+        return -1;
+
+    args = qemuMonitorJSONKeywordStringToJSON(netdevstr, "type");
+    if (!args)
+        goto cleanup;
+
+    if (virJSONValueObjectAppend(cmd, "arguments", args) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+    args = NULL; /* obj owns reference to args now */
+
+    ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+    if (ret == 0)
+        ret = qemuMonitorJSONCheckError(cmd, reply);
+
+cleanup:
+    virJSONValueFree(args);
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon,
+                                const char *alias)
+{
+    int ret;
+    virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("netdev_del",
+                                                     "s:id", alias,
+                                                     NULL);
+    virJSONValuePtr reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+    if (ret == 0)
+        ret = qemuMonitorJSONCheckError(cmd, reply);
+
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
+
 /*
  * Example return data
  *
@@ -1964,65 +2080,6 @@ int qemuMonitorJSONDelDevice(qemuMonitorPtr mon,
 }
 
 
-static void
-qemuFreeKeywords(int nkeywords, char **keywords, char **values)
-{
-    int i;
-    for (i = 0 ; i < nkeywords ; i++) {
-        VIR_FREE(keywords[i]);
-        VIR_FREE(values[i]);
-    }
-    VIR_FREE(keywords);
-    VIR_FREE(values);
-}
-
-static virJSONValuePtr
-qemuMonitorJSONKeywordStringToJSON(const char *str, const char *firstkeyword)
-{
-    virJSONValuePtr ret = NULL;
-    char **keywords = NULL;
-    char **values = NULL;
-    int nkeywords = 0;
-    int i;
-
-    if (!(ret = virJSONValueNewObject()))
-        goto no_memory;
-
-    nkeywords = qemuParseKeywords(str, &keywords, &values, 1);
-
-    if (nkeywords < 0)
-        goto error;
-
-    for (i = 0 ; i < nkeywords ; i++) {
-        if (values[i] == NULL) {
-            if (i != 0) {
-                qemuReportError(VIR_ERR_INTERNAL_ERROR,
-                                _("unexpected empty keyword in %s"), str);
-                goto error;
-            } else {
-                /* This 3rd arg isn't a typo - the way the parser works is
-                 * that the value ended up in the keyword field */
-                if (virJSONValueObjectAppendString(ret, firstkeyword, keywords[i]) < 0)
-                    goto no_memory;
-            }
-        } else {
-            if (virJSONValueObjectAppendString(ret, keywords[i], values[i]) < 0)
-                goto no_memory;
-        }
-    }
-
-    qemuFreeKeywords(nkeywords, keywords, values);
-    return ret;
-
-no_memory:
-    virReportOOMError();
-error:
-    qemuFreeKeywords(nkeywords, keywords, values);
-    virJSONValueFree(ret);
-    return NULL;
-}
-
-
 int qemuMonitorJSONAddDevice(qemuMonitorPtr mon,
                              const char *devicestr)
 {
index 07fa587d36b0b5cb23b0b100749a3638fea93cb8..9948fd8612cb277b8830e6cf52a704cc68e80936 100644 (file)
@@ -155,6 +155,12 @@ int qemuMonitorJSONRemoveHostNetwork(qemuMonitorPtr mon,
                                      int vlan,
                                      const char *netname);
 
+int qemuMonitorJSONAddNetdev(qemuMonitorPtr mon,
+                             const char *netdevstr);
+
+int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon,
+                                const char *alias);
+
 int qemuMonitorJSONGetPtyPaths(qemuMonitorPtr mon,
                                virHashTablePtr paths);
 
index 31196002734c9c22b383b214239fd61cc2d085cb..ae5d4d2d26186ad610af9fa1a89aaf1ff449a771 100644 (file)
@@ -1820,6 +1820,64 @@ cleanup:
 }
 
 
+int qemuMonitorTextAddNetdev(qemuMonitorPtr mon,
+                             const char *netdevstr)
+{
+    char *cmd;
+    char *reply = NULL;
+    int ret = -1;
+
+    if (virAsprintf(&cmd, "netdev_add %s", netdevstr) < 0) {
+        virReportOOMError();
+        return -1;
+    }
+
+    if (qemuMonitorCommand(mon, cmd, &reply) < 0) {
+        qemuReportError(VIR_ERR_OPERATION_FAILED,
+                        _("failed to add netdev with '%s'"), cmd);
+        goto cleanup;
+    }
+
+    /* XXX error messages here ? */
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(cmd);
+    VIR_FREE(reply);
+    return ret;
+}
+
+
+int qemuMonitorTextRemoveNetdev(qemuMonitorPtr mon,
+                                const char *alias)
+{
+    char *cmd;
+    char *reply = NULL;
+    int ret = -1;
+
+    if (virAsprintf(&cmd, "netdev_del %s", alias) < 0) {
+        virReportOOMError();
+        return -1;
+    }
+
+    if (qemuMonitorCommand(mon, cmd, &reply) < 0) {
+        qemuReportError(VIR_ERR_OPERATION_FAILED,
+                        _("failed to remove netdev in qemu with '%s'"), cmd);
+        goto cleanup;
+    }
+
+    /* XXX error messages here ? */
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(cmd);
+    VIR_FREE(reply);
+    return ret;
+}
+
+
 /* Parse the output of "info chardev" and return a hash of pty paths.
  *
  * Output is:
index 7d1bc56cd0bdd20ad71534d5702ae7eebce9c99e..4fe40e22ca78d5109253c11776b7453852b0d1be 100644 (file)
@@ -154,6 +154,12 @@ int qemuMonitorTextRemoveHostNetwork(qemuMonitorPtr mon,
                                      int vlan,
                                      const char *netname);
 
+int qemuMonitorTextAddNetdev(qemuMonitorPtr mon,
+                             const char *netdevstr);
+
+int qemuMonitorTextRemoveNetdev(qemuMonitorPtr mon,
+                                const char *alias);
+
 int qemuMonitorTextGetPtyPaths(qemuMonitorPtr mon,
                                virHashTablePtr paths);