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
ACK, but that part is not simple, I wonder how hard it would be to
have regression tests for the various scenarios,
Daniel
--
Daniel Veillard | libxml Gnome XML XSLT toolkit