[libvirt] Using Libvirt to change the bridge a virtual network card of a running vm is connected to

Daniel P. Berrange berrange at redhat.com
Thu Jan 26 11:10:18 UTC 2012


On Wed, Jan 25, 2012 at 04:09:22PM +0100, Hendrik Schwartke wrote:
> I wrote a patch to change the mapping between a virtual bridge
> interface and the host bridge while the host is up. It's based on
> commit 6fba577e505611e6c25c68e322942eab7754de7e. The host and the
> interface definition I used for testing are also attached.
> I would be glad if the patch could be added to the repo.

Great !

> diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
> index 4b60839..f791795 100644
> --- a/src/qemu/qemu_hotplug.c
> +++ b/src/qemu/qemu_hotplug.c
> @@ -40,6 +40,7 @@
>  #include "qemu_cgroup.h"
>  #include "locking/domain_lock.h"
>  #include "network/bridge_driver.h"
> +#include "util/virnetdevbridge.h"
> 
>  #define VIR_FROM_THIS VIR_FROM_QEMU
> 
> @@ -1163,6 +1164,28 @@ static virDomainNetDefPtr
> qemuDomainFindNet(virDomainObjPtr vm,
>      return NULL;
>  }
> 
> +int qemuDomainChangeNetBridge(virDomainNetDefPtr olddev,
> +                              virDomainNetDefPtr newdev)
> +{
> +    const char *oldbridge=olddev->data.bridge.brname;
> +    const char *newbridge=newdev->data.bridge.brname;
> +    VIR_DEBUG("Change bridge for interface %s: %s -> %s",
> olddev->ifname, oldbridge, newbridge);

> +    if(virNetDevBridgeRemovePort(oldbridge, olddev->ifname)<0) {
> +        qemuReportError(VIR_ERR_OPERATION_FAILED,
> +                        _("unable to remove port from bridge"));

Good idea to include the bridge device name too

> +        return -1;
> +    }
> +    if(virNetDevBridgeAddPort(newbridge, newdev->ifname)<0) {
> +        qemuReportError(VIR_ERR_OPERATION_FAILED,
> +                        _("unable to add port to bridge"));
> +        return -1;
> +    }

Hmm, we'd end up with no NIC backend command at all here.
So we'd probably want to move the VIR_FREE() line before
here, so if we fail half-way through, we still update
the XML. To avoid stupid mistakes we also probably want
to check for existance of the target bridge before unplugging
fromthe source

> +    VIR_FREE(olddev->data.bridge.brname);
> +    olddev->data.bridge.brname=strdup(newbridge);

We could just steal newbridge from the newdev and
avoid the OOM potential

> +    return 0;
> +}

Various whitespace fixes

 - For assignment, we prefer 'foo = bar' instead of 'foo=bar'
 - For conditionals  'if (' instead of 'if('
 - For tests  "foo < 0" instead of "foo<0"


So all in all I think I'd write this as 

  int qemuDomainChangeNetBridge(virDomainNetDefPtr olddev,
                                virDomainNetDefPtr newdev)
  {
    const char *oldbridge = olddev->data.bridge.brname;
    const char *newbridge = newdev->data.bridge.brname;

    VIR_DEBUG("Change bridge for interface %s: %s -> %s",
              olddev->ifname, oldbridge, newbridge);

    if (!virNetDevExists(newbridge)) {
        qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
                        _("unable to remove port from bridge"));
        return -1;
    }

    if (oldbridge &&
        virNetDevBridgeRemovePort(oldbridge, olddev->ifname) < 0) {
        qemuReportError(VIR_ERR_OPERATION_FAILED,
                        _("unable to remove port from bridge %s"), oldbridge);
        return -1;
    }
    VIR_FREE(olddev->data.bridge.brname);
    if (virNetDevBridgeAddPort(newbridge, newdev->ifname) < 0) {
        qemuReportError(VIR_ERR_OPERATION_FAILED,
                        _("unable to add port to bridge"), newbridge);
        return -1;
    }
    olddev->data.bridge.brname = newdev->data.bridge.brname;
    newdev->data.bridge.brname = NULL;
    return 0;
  }

> +
>  int qemuDomainChangeNetLinkState(struct qemud_driver *driver,
>                                   virDomainObjPtr vm,
>                                   virDomainNetDefPtr dev,
> @@ -1293,6 +1316,12 @@ int qemuDomainChangeNet(struct qemud_driver *driver,
>          return -1;
>      }
> 
> +    if(olddev->type==VIR_DOMAIN_NET_TYPE_BRIDGE
> + && dev->type==VIR_DOMAIN_NET_TYPE_BRIDGE
> + && STRNEQ(olddev->data.bridge.brname, dev->data.bridge.brname)) {
> +        qemuDomainChangeNetBridge(olddev, dev);
> +    }

And use STRNEQ_NULLABLE here to avoid risk of NULL brname field,
and if qemuDomainChangeNetBridge() returns -1, we need to return
to the caller here.

> +
>      if (olddev->linkstate != dev->linkstate) {
>          if ((ret = qemuDomainChangeNetLinkState(driver, vm, olddev,
> dev->linkstate)) < 0)
>              return ret;
> diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h
> index 0310361..1e1f75c 100644
> --- a/src/qemu/qemu_hotplug.h
> +++ b/src/qemu/qemu_hotplug.h
> @@ -80,6 +80,8 @@ int qemuDomainChangeNetLinkState(struct
> qemud_driver *driver,
>                                   virDomainObjPtr vm,
>                                   virDomainNetDefPtr dev,
>                                   int linkstate);
> +int qemuDomainChangeNetBridge(virDomainNetDefPtr olddev,
> +                              virDomainNetDefPtr newdev);
>  int qemuDomainDetachPciDiskDevice(struct qemud_driver *driver,
>                                    virDomainObjPtr vm,
>                                    virDomainDeviceDefPtr dev);
> 
> 
> Host definition:
> <domain type='kvm'>
> <name>test</name>
> <memory>32768</memory>
> <currentMemory>32768</currentMemory>
> <vcpu>1</vcpu>
> <os>
> <type arch='x86_64' machine='pc-0.12'>hvm</type>
> <boot dev='cdrom'/>
> <bootmenu enable='no'/>
> </os>
> <features>
> <acpi/>
> <apic/>
> <pae/>
> </features>
> <clock offset='utc'/>
> <on_poweroff>destroy</on_poweroff>
> <on_reboot>restart</on_reboot>
> <on_crash>restart</on_crash>
> <devices>
> <emulator>/usr/bin/kvm</emulator>
> <controller type='ide' index='0'>
> <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
> </controller>
> <interface type='bridge'>
> <mac address='52:54:00:ab:cd:02'/>
> <source bridge='br0'/>
> <target dev='testif'/>
> <link state='up'/>
> <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
> </interface>
> <serial type='pty'>
> <target port='0'/>
> </serial>
> <console type='pty'>
> <target type='serial' port='0'/>
> </console>
> <input type='tablet' bus='usb'/>
> <input type='mouse' bus='ps2'/>
> <graphics type='vnc' port='-1' autoport='yes'/>
> <video>
> <model type='cirrus' vram='9216' heads='1'/>
> <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
> </video>
> <memballoon model='virtio'>
> <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
> </memballoon>
> </devices>
> </domain>
> 
> 
> Interface definition:
> <interface type="bridge">
> <mac address="52:54:00:ab:cd:02"/>
> <source bridge="br1"/>
> <target dev="testif"/>
> <link state="up"/>
> <alias name="net0"/>
> <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x0"/>
> </interface>
> 
> 
> 
> Best regards
> Hendrik Schwartke
> 
> --
> libvir-list mailing list
> libvir-list at redhat.com
> https://www.redhat.com/mailman/listinfo/libvir-list

-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|




More information about the libvir-list mailing list