[libvirt] [PATCH v3 4/5] link-state: qemu: Add net intf modification to virUpdateDeviceFlags

Peter Krempa pkrempa at redhat.com
Mon Sep 5 15:26:53 UTC 2011


This patch enables modifying network device configuration using the
virUpdateDeviceFlags API method. Matching of devices is accomplished
using MAC addresses.

While updating live configuration of a running domain, the user is
allowed only to change link state of the interface. Additional
modifications may be added later. For now the code checks for
unsupported changes and tereafer changes the link state, if applicable.

When updating persistent configuration of guest's network interface the
whole configuration (except for the MAC address) may be modified and
is stored for the next startup.

  src/qemu/qemu_driver.c   - Add dispatching of virUpdateDevice for
                             network devices update (live/config)
  src/qemu/qemu_hotplug.c  - add setting of initial link state on live
                             device addition
                           - add function to change network device
                             configuration. By now it supports only
                             changing of link state
  src/qemu/qemu_hotplug.h  - Headers to above functions
  src/qemu/qemu_process.c  - set link states before virtual machine
                             start. Qemu does not support setting of
                             this on the command line.
---
 src/qemu/qemu_driver.c  |   24 +++++++
 src/qemu/qemu_hotplug.c |  176 +++++++++++++++++++++++++++++++++++++++++++++++
 src/qemu/qemu_hotplug.h |    8 ++
 src/qemu/qemu_process.c |   47 ++++++++++++-
 4 files changed, 254 insertions(+), 1 deletions(-)

diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 7028d72..648afae 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -5474,6 +5474,9 @@ qemuDomainUpdateDeviceLive(virDomainObjPtr vm,
     case VIR_DOMAIN_DEVICE_GRAPHICS:
         ret = qemuDomainChangeGraphics(driver, vm, dev->data.graphics);
         break;
+    case VIR_DOMAIN_DEVICE_NET:
+        ret = qemuDomainChangeNet(driver, vm, dom, dev->data.net);
+        break;
     default:
         qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                         _("device type '%s' cannot be updated"),
@@ -5608,6 +5611,7 @@ qemuDomainUpdateDeviceConfig(virDomainDefPtr vmdef,
                              virDomainDeviceDefPtr dev)
 {
     virDomainDiskDefPtr orig, disk;
+    virDomainNetDefPtr net;
     int pos;

     switch (dev->type) {
@@ -5646,6 +5650,26 @@ qemuDomainUpdateDeviceConfig(virDomainDefPtr vmdef,
         }
         disk->src = NULL;
         break;
+
+    case VIR_DOMAIN_DEVICE_NET:
+        net = dev->data.net;
+        if ((pos = virDomainNetIndexByMac(vmdef, net->mac)) < 0) {
+            char macbuf[VIR_MAC_STRING_BUFLEN];
+            virFormatMacAddr(net->mac, macbuf);
+            qemuReportError(VIR_ERR_INVALID_ARG,
+                            _("mac %s doesn't exist"), macbuf);
+            return -1;
+        }
+
+        VIR_FREE(vmdef->nets[pos]);
+
+        vmdef->nets[pos] = net;
+        dev->data.net = NULL;
+
+        if (qemuDomainAssignPCIAddresses(vmdef) < 0)
+            return -1;
+        break;
+
     default:
         qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                          _("persistent update of device is not supported"));
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 6ae834c..ad57b2f 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -751,6 +751,30 @@ int qemuDomainAttachNetDevice(virConnectPtr conn,
     }
     qemuDomainObjExitMonitorWithDriver(driver, vm);

+    /* set link state */
+    if (net->linkstate == VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN) {
+        if (!net->info.alias) {
+            qemuReportError(VIR_ERR_OPERATION_FAILED,
+                            _("device alias not found: cannot set link state to down"));
+        } else {
+            qemuDomainObjEnterMonitorWithDriver(driver, vm);
+
+            if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_NETDEV)) {
+                if (qemuMonitorSetLink(priv->mon, net->info.alias, VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN) < 0) {
+                    qemuDomainObjExitMonitorWithDriver(driver, vm);
+                    virDomainAuditNet(vm, NULL, net, "attach", false);
+                    goto try_remove;
+                }
+            } else {
+                qemuReportError(VIR_ERR_OPERATION_FAILED,
+                                _("setting of link state not supported: Link is up"));
+            }
+
+            qemuDomainObjExitMonitorWithDriver(driver, vm);
+        }
+        /* link set to down */
+    }
+
     virDomainAuditNet(vm, NULL, net, "attach", true);

     ret = 0;
@@ -1082,6 +1106,158 @@ error:
     return -1;
 }

+static virDomainNetDefPtr qemuDomainFindNet(virDomainObjPtr vm,
+                                            virDomainNetDefPtr dev)
+{
+    int i;
+
+    for (i = 0; i < vm->def->nnets; i++) {
+        if (memcmp(vm->def->nets[i]->mac, dev->mac, VIR_MAC_BUFLEN) == 0)
+            return vm->def->nets[i];
+    }
+
+    return NULL;
+}
+
+int qemuDomainChangeNetLinkState(struct qemud_driver *driver,
+                                 virDomainObjPtr vm,
+                                 virDomainNetDefPtr dev,
+                                 int linkstate)
+{
+    int ret = -1;
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+
+    VIR_DEBUG("dev: %s, state: %d", dev->info.alias, linkstate);
+
+    if (!dev->info.alias) {
+        qemuReportError(VIR_ERR_OPERATION_FAILED,
+                        _("can't change link state: device alias not found"));
+        return -1;
+    }
+
+    qemuDomainObjEnterMonitorWithDriver(driver, vm);
+
+    ret = qemuMonitorSetLink(priv->mon, dev->info.alias, linkstate);
+    if (ret < 0)
+        goto cleanup;
+
+    /* modify the device configuration */
+    dev->linkstate = linkstate;
+
+cleanup:
+    qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+    return ret;
+}
+
+int qemuDomainChangeNet(struct qemud_driver *driver,
+                        virDomainObjPtr vm,
+                        virDomainPtr dom ATTRIBUTE_UNUSED,
+                        virDomainNetDefPtr dev)
+
+{
+    virDomainNetDefPtr olddev = qemuDomainFindNet(vm, dev);
+    int ret = 0;
+
+    if (!olddev) {
+        qemuReportError(VIR_ERR_NO_SUPPORT,
+                        _("cannot find existing network device to modify"));
+        return -1;
+    }
+
+    if (olddev->type != dev->type) {
+        qemuReportError(VIR_ERR_NO_SUPPORT,
+                        _("cannot change network interface type"));
+        return -1;
+    }
+
+    switch (olddev->type) {
+    case VIR_DOMAIN_NET_TYPE_USER:
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_ETHERNET:
+        if (STRNEQ_NULLABLE(olddev->data.ethernet.dev, dev->data.ethernet.dev) ||
+            STRNEQ_NULLABLE(olddev->data.ethernet.script, dev->data.ethernet.script) ||
+            STRNEQ_NULLABLE(olddev->data.ethernet.ipaddr, dev->data.ethernet.ipaddr)) {
+            qemuReportError(VIR_ERR_NO_SUPPORT,
+                            _("cannot modify ethernet network device configuration"));
+            return -1;
+        }
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_SERVER:
+    case VIR_DOMAIN_NET_TYPE_CLIENT:
+    case VIR_DOMAIN_NET_TYPE_MCAST:
+        if (STRNEQ_NULLABLE(olddev->data.socket.address, dev->data.socket.address) ||
+            olddev->data.socket.port != dev->data.socket.port) {
+            qemuReportError(VIR_ERR_NO_SUPPORT,
+                            _("cannot modify network socket device configuration"));
+            return -1;
+        }
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_NETWORK:
+        if (STRNEQ_NULLABLE(olddev->data.network.name, dev->data.network.name) ||
+            STRNEQ_NULLABLE(olddev->data.network.portgroup, dev->data.network.portgroup) ||
+            !virVirtualPortProfileEqual(olddev->data.network.virtPortProfile, dev->data.network.virtPortProfile)) {
+            qemuReportError(VIR_ERR_NO_SUPPORT,
+                            _("cannot modify network device configuration"));
+            return -1;
+        }
+
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_INTERNAL:
+        if (STRNEQ_NULLABLE(olddev->data.internal.name, dev->data.internal.name)) {
+            qemuReportError(VIR_ERR_NO_SUPPORT,
+                            _("cannot modify internal network device configuration"));
+            return -1;
+        }
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_DIRECT:
+        if (STRNEQ_NULLABLE(olddev->data.direct.linkdev, dev->data.direct.linkdev) ||
+            olddev->data.direct.mode != dev->data.direct.mode ||
+            !virVirtualPortProfileEqual(olddev->data.direct.virtPortProfile, dev->data.direct.virtPortProfile)) {
+            qemuReportError(VIR_ERR_NO_SUPPORT,
+                            _("cannot modify direct network device configuration"));
+            return -1;
+        }
+        break;
+
+    default:
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                        _("unable to change config on '%s' network type"),
+                        virDomainNetTypeToString(dev->type));
+        break;
+
+    }
+
+    /* all other unmodifiable parameters */
+    if (STRNEQ_NULLABLE(olddev->model, dev->model) ||
+        STRNEQ_NULLABLE(olddev->filter, dev->filter)) {
+        qemuReportError(VIR_ERR_NO_SUPPORT,
+                        _("cannot modify network device configuration"));
+        return -1;
+    }
+
+    /* check if device name has been set, if no, retain the autogenerated one */
+    if (dev->ifname &&
+        STRNEQ_NULLABLE(olddev->ifname, dev->ifname)) {
+        qemuReportError(VIR_ERR_NO_SUPPORT,
+                        _("cannot modify network device configuration"));
+        return -1;
+    }
+
+    if (olddev->linkstate != dev->linkstate) {
+        if ((ret = qemuDomainChangeNetLinkState(driver, vm, olddev, dev->linkstate)) < 0)
+            return ret;
+    }
+
+    return ret;
+}
+
+

 static virDomainGraphicsDefPtr qemuDomainFindGraphics(virDomainObjPtr vm,
                                                       virDomainGraphicsDefPtr dev)
diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h
index ea1cca0..65d1d30 100644
--- a/src/qemu/qemu_hotplug.h
+++ b/src/qemu/qemu_hotplug.h
@@ -67,6 +67,14 @@ int qemuDomainChangeGraphicsPasswords(struct qemud_driver *driver,
                                       int type,
                                       virDomainGraphicsAuthDefPtr auth,
                                       const char *defaultPasswd);
+int qemuDomainChangeNet(struct qemud_driver *driver,
+                        virDomainObjPtr vm,
+                        virDomainPtr dom,
+                        virDomainNetDefPtr dev);
+int qemuDomainChangeNetLinkState(struct qemud_driver *driver,
+                                 virDomainObjPtr vm,
+                                 virDomainNetDefPtr dev,
+                                 int linkstate);
 int qemuDomainDetachPciDiskDevice(struct qemud_driver *driver,
                                   virDomainObjPtr vm,
                                   virDomainDeviceDefPtr dev);
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index c22974f..357bab4 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -1443,7 +1443,40 @@ qemuProcessInitCpuAffinity(virDomainObjPtr vm)
     return 0;
 }

-/* Set CPU affinites for vcpus if vcpupin xml provided. */
+/* set link states to down on interfaces at qemu start */
+static int
+qemuProcessSetLinkStates(virDomainObjPtr vm)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+    virDomainDefPtr def = vm->def;
+    int i;
+    int ret = 0;
+
+    for (i = 0; i < def->nnets; i++) {
+        if (def->nets[i]->linkstate == VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN) {
+            VIR_DEBUG("Setting link state: %s", def->nets[i]->info.alias);
+
+            if (!qemuCapsGet(priv->qemuCaps, QEMU_CAPS_NETDEV)) {
+                qemuReportError(VIR_ERR_NO_SUPPORT,
+                                _("Setting of link state is not supported by this qemu"));
+                return -1;
+            }
+
+            ret = qemuMonitorSetLink(priv->mon,
+                                     def->nets[i]->info.alias,
+                                     VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN);
+            if (ret != 0) {
+                qemuReportError(VIR_ERR_OPERATION_FAILED,
+                               _("Couldn't set link state on interface: %s"), def->nets[i]->info.alias);
+                break;
+            }
+        }
+    }
+
+    return ret;
+}
+
+/* Set CPU affinities for vcpus if vcpupin xml provided. */
 static int
 qemuProcessSetVcpuAffinites(virConnectPtr conn,
                             virDomainObjPtr vm)
@@ -2981,6 +3014,18 @@ int qemuProcessStart(virConnectPtr conn,
             goto cleanup;
     }

+    /* set default link states */
+    /* qemu doesn't support setting this on the command line, so
+     * enter the monitor */
+    VIR_DEBUG("Setting network link states");
+    qemuDomainObjEnterMonitorWithDriver(driver, vm);
+    if (qemuProcessSetLinkStates(vm) < 0) {
+        qemuDomainObjExitMonitorWithDriver(driver, vm);
+        goto cleanup;
+    }
+
+    qemuDomainObjExitMonitorWithDriver(driver, vm);
+
     /* Technically, qemuProcessStart can be called from inside
      * QEMU_ASYNC_JOB_MIGRATION_IN, but we are okay treating this like
      * a sync job since no other job can call into the domain until
-- 
1.7.3.4




More information about the libvir-list mailing list