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 f21122d..6013fe3 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5073,6 +5073,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"), @@ -5207,6 +5210,7 @@ qemuDomainUpdateDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { virDomainDiskDefPtr orig, disk; + virDomainNetDefPtr net; int pos; switch (dev->type) { @@ -5245,6 +5249,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 b7fdfa0..02d1dda 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -743,6 +743,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; @@ -1031,6 +1055,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 009f1f6..f564f3c 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -64,6 +64,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 58b4d36..6398c7b 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) @@ -2976,6 +3009,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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list