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@xxxxxxxxxxxx | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/ -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list