[libvirt] [PATCH v3 4/5] link-state: qemu: Add net intf modification to virUpdateDeviceFlags
Daniel Veillard
veillard at redhat.com
Tue Sep 6 08:30:43 UTC 2011
On Mon, Sep 05, 2011 at 05:26:53PM +0200, Peter Krempa wrote:
> 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.
thereafter I guess :-)
> 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 http://xmlsoft.org/
daniel at veillard.com | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library http://libvirt.org/
More information about the libvir-list
mailing list