[libvirt] [PATCH 0/5] udev based backend for virInterface

The goal is to provide a read-only virInterface provider when netcf is unavailable. With this patchset it makes virt-manager happy enough that it does not attempt to do anything with HAL to figure out the host's network devices. All patches but 4/5 are really ready for prime time. 4/5 can get merged but it is somewhat incomplete. With regards to bond and devices in a bridge. The patches are all at different versions depending on how many times they've hit the list. Doug Goldstein (5): interface: add udev based backend for virInterface interface: fix netcf based backend naming interface: always build all available backends interface: add virInterfaceGetXMLDesc() in udev interface: add udevIfaceIsActive() to udev backend configure.ac | 18 +- po/POTFILES.in | 1 + src/Makefile.am | 14 +- src/interface/interface_backend_netcf.c | 4 +- src/interface/interface_backend_udev.c | 671 +++++++++++++++++++++++++++++++ src/interface/interface_driver.c | 37 ++ src/interface/interface_driver.h | 3 + tools/virsh.c | 2 + 8 files changed, 734 insertions(+), 16 deletions(-) create mode 100644 src/interface/interface_backend_udev.c create mode 100644 src/interface/interface_driver.c -- 1.7.8.6

Add a read-only udev based backend for virInterface. Useful for distros that do not have netcf support yet. Multiple libvirt based utilities use a HAL based fallback when virInterface is not available which is less than ideal. This implements: * virConnectNumOfInterfaces() * virConnectListInterfaces() * virConnectNumOfDefinedInterfaces() * virConnectListDefinedInterfaces() * virConnectListAllInterfaces() * virConnectInterfaceLookupByName() * virConnectInterfaceLookupByMACString() --- Change from v5: * squash the virConnectListAllInterfaces() patch in * rename status enum to virUdevStatus * remove checks that don't print errors as the value is checked elsewhere * provide helpers for NumOfInterfaces/NumOfDefinedInterfaces to reduce code * provide helpers for ListInterfaces/ListDefinedInterfaces to reduce code Change from v4: * Fix up udev reference counting Change from v3: * fix make syntax-check issues * check strdup() and handle that case * simplify configure check Change from v2: * rebase from master Change from v1: * rebase from master configure.ac | 18 +- po/POTFILES.in | 1 + src/Makefile.am | 12 +- src/interface/interface_backend_udev.c | 511 ++++++++++++++++++++++++++++++++ tools/virsh.c | 2 + 5 files changed, 531 insertions(+), 13 deletions(-) create mode 100644 src/interface/interface_backend_udev.c diff --git a/configure.ac b/configure.ac index 6d50985..92f85df 100644 --- a/configure.ac +++ b/configure.ac @@ -2801,18 +2801,12 @@ if test "$with_libvirtd" = "no" ; then with_interface=no fi -dnl The interface driver depends on the netcf library -if test "$with_interface:$with_netcf" = "check:yes" ; then - with_interface=yes -fi - -if test "$with_interface:$with_netcf" = "check:no" ; then - with_interface=no -fi - -if test "$with_interface:$with_netcf" = "yes:no" ; then - AC_MSG_ERROR([Requested the Interface driver without netcf support]) -fi +dnl The interface driver depends on the netcf library or udev library +case $with_interface:$with_netcf:$with_udev in + check:*yes*) with_interface=yes ;; + check:no:no) with_interface=no ;; + yes:no:no) AC_MSG_ERROR([Requested the Interface driver without netcf or udev support]) ;; +esac if test "$with_interface" = "yes" ; then AC_DEFINE_UNQUOTED([WITH_INTERFACE], [1], diff --git a/po/POTFILES.in b/po/POTFILES.in index 74d8dcc..2538225 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -41,6 +41,7 @@ src/hyperv/hyperv_driver.c src/hyperv/hyperv_util.c src/hyperv/hyperv_wmi.c src/interface/interface_backend_netcf.c +src/interface/interface_backend_udev.c src/internal.h src/libvirt.c src/libvirt-qemu.c diff --git a/src/Makefile.am b/src/Makefile.am index ae3d491..98448c1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -558,11 +558,16 @@ INTERFACE_DRIVER_SOURCES = if WITH_INTERFACE INTERFACE_DRIVER_SOURCES += \ interface/interface_driver.h -endif if WITH_NETCF INTERFACE_DRIVER_SOURCES += \ interface/interface_backend_netcf.c +else +if HAVE_UDEV +INTERFACE_DRIVER_SOURCES += \ + interface/interface_backend_udev.c +endif +endif endif SECRET_DRIVER_SOURCES = \ @@ -1045,6 +1050,11 @@ libvirt_driver_interface_la_LIBADD = if WITH_NETCF libvirt_driver_interface_la_CFLAGS += $(NETCF_CFLAGS) libvirt_driver_interface_la_LIBADD += $(NETCF_LIBS) +else +if HAVE_UDEV +libvirt_driver_interface_la_CFLAGS += $(UDEV_CFLAGS) +libvirt_driver_interface_la_LIBADD += $(UDEV_LIBS) +endif endif if WITH_DRIVER_MODULES libvirt_driver_interface_la_LIBADD += ../gnulib/lib/libgnu.la diff --git a/src/interface/interface_backend_udev.c b/src/interface/interface_backend_udev.c new file mode 100644 index 0000000..bb161ab --- /dev/null +++ b/src/interface/interface_backend_udev.c @@ -0,0 +1,511 @@ +/* + * interface_backend_udev.c: udev backend for virInterface + * + * Copyright (C) 2012 Doug Goldstein <cardoe@cardoe.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; If not, see + * <http://www.gnu.org/licenses/>. + */ +#include <config.h> + +#include <libudev.h> + +#include "virterror_internal.h" +#include "datatypes.h" +#include "interface_driver.h" +#include "interface_conf.h" +#include "memory.h" + +#define VIR_FROM_THIS VIR_FROM_INTERFACE + +struct udev_iface_driver { + struct udev *udev; +}; + +typedef enum { + VIR_UDEV_IFACE_ACTIVE, + VIR_UDEV_IFACE_INACTIVE, + VIR_UDEV_IFACE_ALL +} virUdevStatus ; + +static const char * +virUdevStatusString(virUdevStatus status) +{ + switch (status) { + case VIR_UDEV_IFACE_ACTIVE: + return "active"; + case VIR_UDEV_IFACE_INACTIVE: + return "inactive"; + case VIR_UDEV_IFACE_ALL: + return "all"; + } + + return ""; +} + +static struct udev_enumerate * ATTRIBUTE_NONNULL(1) +udevIfaceGetDevices(struct udev *udev, virUdevStatus status) +{ + struct udev_enumerate *enumerate; + + /* Create a new enumeration to create a list */ + enumerate = udev_enumerate_new(udev); + + if (!enumerate) + return NULL; + + /* Enumerate all network subsystem devices */ + udev_enumerate_add_match_subsystem(enumerate, "net"); + + /* Ignore devices that are part of a bridge */ + udev_enumerate_add_nomatch_sysattr(enumerate, "brport/state", NULL); + + /* State of the device */ + switch (status) { + case VIR_UDEV_IFACE_ACTIVE: + udev_enumerate_add_match_sysattr(enumerate, "operstate", "up"); + break; + + case VIR_UDEV_IFACE_INACTIVE: + udev_enumerate_add_match_sysattr(enumerate, "operstate", "down"); + break; + + case VIR_UDEV_IFACE_ALL: + break; + } + + /* We don't want to see the TUN devices that QEMU creates for other guests + * running on this machine. By saying nomatch NULL, we just are getting + * devices without the tun_flags sysattr. + */ + udev_enumerate_add_nomatch_sysattr(enumerate, "tun_flags", NULL); + + return enumerate; +} + +static virDrvOpenStatus +udevIfaceOpenInterface(virConnectPtr conn, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + unsigned int flags) +{ + struct udev_iface_driver *driverState = NULL; + + virCheckFlags(0, VIR_DRV_OPEN_ERROR); + + if (VIR_ALLOC(driverState) < 0) { + virReportOOMError(); + goto err; + } + + driverState->udev = udev_new(); + if (!driverState->udev) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create udev context")); + goto err; + } + + conn->interfacePrivateData = driverState; + + return VIR_DRV_OPEN_SUCCESS; + +err: + VIR_FREE(driverState); + + return VIR_DRV_OPEN_ERROR; +} + +static int +udevIfaceCloseInterface(virConnectPtr conn) +{ + struct udev_iface_driver *driverState; + + if (conn->interfacePrivateData != NULL) { + driverState = conn->interfacePrivateData; + + udev_unref(driverState->udev); + + VIR_FREE(driverState); + } + + conn->interfacePrivateData = NULL; + return 0; +} + +static int +udevIfaceNumOfInterfacesByStatus(virConnectPtr conn, virUdevStatus status) +{ + struct udev_iface_driver *driverState = conn->interfacePrivateData; + struct udev *udev = udev_ref(driverState->udev); + struct udev_enumerate *enumerate = NULL; + struct udev_list_entry *devices; + struct udev_list_entry *dev_entry; + int count = 0; + + enumerate = udevIfaceGetDevices(udev, status); + + if (!enumerate) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to get number of %s interfaces on host"), + virUdevStatusString(status)); + count = -1; + goto err; + } + + /* Do the scan to load up the enumeration */ + udev_enumerate_scan_devices(enumerate); + + /* Get a list we can walk */ + devices = udev_enumerate_get_list_entry(enumerate); + + /* For each item so we can count */ + udev_list_entry_foreach(dev_entry, devices) { + count++; + } + +err: + if (enumerate) + udev_enumerate_unref(enumerate); + udev_unref(udev); + + return count; +} + +static int +udevIfaceListInterfacesByStatus(virConnectPtr conn, + char **const names, + int names_len, + virUdevStatus status) +{ + struct udev_iface_driver *driverState = conn->interfacePrivateData; + struct udev *udev = udev_ref(driverState->udev); + struct udev_enumerate *enumerate = NULL; + struct udev_list_entry *devices; + struct udev_list_entry *dev_entry; + int count = 0; + + enumerate = udevIfaceGetDevices(udev, status); + + if (!enumerate) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to get list of %s interfaces on host"), + virUdevStatusString(status)); + goto err; + } + + /* Do the scan to load up the enumeration */ + udev_enumerate_scan_devices(enumerate); + + /* Get a list we can walk */ + devices = udev_enumerate_get_list_entry(enumerate); + + /* For each item so we can count */ + udev_list_entry_foreach(dev_entry, devices) { + struct udev_device *dev; + const char *path; + + /* Ensure we won't exceed the size of our array */ + if (count > names_len) + break; + + path = udev_list_entry_get_name(dev_entry); + dev = udev_device_new_from_syspath(udev, path); + names[count] = strdup(udev_device_get_sysname(dev)); + udev_device_unref(dev); + + /* If strdup() failed, we are out of memory */ + if (!names[count]) { + virReportOOMError(); + goto err; + } + + count++; + } + + udev_enumerate_unref(enumerate); + udev_unref(udev); + + return count; + +err: + if (enumerate) + udev_enumerate_unref(enumerate); + udev_unref(udev); + + for (names_len = 0; names_len < count; names_len++) + VIR_FREE(names[names_len]); + + return -1; +} + +static int +udevIfaceNumOfInterfaces(virConnectPtr conn) +{ + return udevIfaceNumOfInterfacesByStatus(conn, VIR_UDEV_IFACE_ACTIVE); +} + +static int +udevIfaceListInterfaces(virConnectPtr conn, + char **const names, + int names_len) +{ + return udevIfaceListInterfacesByStatus(conn, names, names_len, + VIR_UDEV_IFACE_ACTIVE); +} + +static int +udevIfaceNumOfDefinedInterfaces(virConnectPtr conn) +{ + return udevIfaceNumOfInterfacesByStatus(conn, VIR_UDEV_IFACE_INACTIVE); +} + +static int +udevIfaceListDefinedInterfaces(virConnectPtr conn, + char **const names, + int names_len) +{ + return udevIfaceListInterfacesByStatus(conn, names, names_len, + VIR_UDEV_IFACE_INACTIVE); +} + +static int +udevIfaceListAllInterfaces(virConnectPtr conn, + virInterfacePtr **ifaces, + unsigned int flags) +{ + struct udev_iface_driver *driverState = conn->interfacePrivateData; + struct udev *udev; + struct udev_enumerate *enumerate = NULL; + struct udev_list_entry *devices; + struct udev_list_entry *dev_entry; + virInterfacePtr *ifaces_list; + virInterfacePtr iface_obj; + int tmp_count; + int count = 0; + int status = 0; + int ret; + + virCheckFlags(VIR_CONNECT_LIST_INTERFACES_ACTIVE | + VIR_CONNECT_LIST_INTERFACES_INACTIVE, -1); + + /* Grab a udev reference */ + udev = udev_ref(driverState->udev); + + /* List all interfaces incase we support more filter flags in the future */ + enumerate = udevIfaceGetDevices(udev, VIR_UDEV_IFACE_ALL); + + if (!enumerate) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to get list of %s interfaces on host"), + virUdevStatusString(status)); + ret = -1; + goto cleanup; + } + + /* Do the scan to load up the enumeration */ + udev_enumerate_scan_devices(enumerate); + + /* Get a list we can walk */ + devices = udev_enumerate_get_list_entry(enumerate); + + /* For each item so we can count */ + udev_list_entry_foreach(dev_entry, devices) { + count++; + } + + /* If we've got nothing, exit out */ + if (count == 0) { + ret = 0; + goto cleanup; + } + + /* If we're asked for the ifaces then alloc up memory */ + if (ifaces) { + if (VIR_ALLOC_N(ifaces_list, count + 1) < 0) { + virReportOOMError(); + ret = -1; + goto cleanup; + } + } + + /* Get a list we can walk */ + devices = udev_enumerate_get_list_entry(enumerate); + + /* reset our iterator */ + count = 0; + + /* Walk through each device */ + udev_list_entry_foreach(dev_entry, devices) { + struct udev_device *dev; + const char *path; + const char *name; + const char *macaddr; + int add_to_list; + + path = udev_list_entry_get_name(dev_entry); + dev = udev_device_new_from_syspath(udev, path); + name = udev_device_get_sysname(dev); + macaddr = udev_device_get_sysattr_value(dev, "address"); + status = STREQ(udev_device_get_sysattr_value(dev, "operstate"), "up"); + udev_device_unref(dev); + + /* Filter the results */ + if (status && (flags & VIR_CONNECT_LIST_INTERFACES_ACTIVE)) + add_to_list = 1; + else if (!status && (flags & VIR_CONNECT_LIST_INTERFACES_INACTIVE)) + add_to_list = 1; + + /* If we matched a filter, then add it */ + if (add_to_list) { + if (ifaces) { + iface_obj = virGetInterface(conn, name, macaddr); + ifaces_list[count] = iface_obj; + } + count++; + } + } + + /* Drop our refcounts */ + udev_enumerate_unref(enumerate); + udev_unref(udev); + + /* Trim the array to its final size */ + if (ifaces) { + ignore_value(VIR_REALLOC_N(ifaces_list, count + 1)); + *ifaces = ifaces_list; + } + + return count; + +cleanup: + if (enumerate) + udev_enumerate_unref(enumerate); + udev_unref(udev); + + if (ifaces) { + for (tmp_count = 0; tmp_count < count; tmp_count++) + virInterfaceFree(ifaces_list[tmp_count]); + } + + VIR_FREE(ifaces_list); + + return ret; + +} + +static virInterfacePtr +udevIfaceLookupByName(virConnectPtr conn, const char *name) +{ + struct udev_iface_driver *driverState = conn->interfacePrivateData; + struct udev *udev = udev_ref(driverState->udev); + struct udev_device *dev; + const char *macaddr; + virInterfacePtr ret = NULL; + + /* get a device reference based on the device name */ + dev = udev_device_new_from_subsystem_sysname(udev, "net", name); + if (!dev) { + virReportError(VIR_ERR_NO_INTERFACE, + _("couldn't find interface named '%s'"), + name); + goto err; + } + + macaddr = udev_device_get_sysattr_value(dev, "address"); + ret = virGetInterface(conn, name, macaddr); + udev_device_unref(dev); + +err: + udev_unref(udev); + + return ret; +} + +static virInterfacePtr +udevIfaceLookupByMACString(virConnectPtr conn, const char *macstr) +{ + struct udev_iface_driver *driverState = conn->interfacePrivateData; + struct udev *udev = udev_ref(driverState->udev); + struct udev_enumerate *enumerate = NULL; + struct udev_list_entry *dev_entry; + struct udev_device *dev; + const char *name; + virInterfacePtr ret = NULL; + + enumerate = udevIfaceGetDevices(udev, VIR_UDEV_IFACE_ALL); + + if (!enumerate) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to lookup interface with MAC address '%s'"), + macstr); + goto err; + } + + /* Match on MAC */ + udev_enumerate_add_match_sysattr(enumerate, "address", macstr); + + /* Do the scan to load up the enumeration */ + udev_enumerate_scan_devices(enumerate); + + /* Get a list we can walk */ + dev_entry = udev_enumerate_get_list_entry(enumerate); + + /* Check that we got something back */ + if (!dev_entry) { + virReportError(VIR_ERR_NO_INTERFACE, + _("couldn't find interface with MAC address '%s'"), + macstr); + goto err; + } + + /* Check that we didn't get multiple items back */ + if (udev_list_entry_get_next(dev_entry)) { + virReportError(VIR_ERR_MULTIPLE_INTERFACES, + _("the MAC address '%s' matches multiple interfaces"), + macstr); + goto err; + } + + dev = udev_device_new_from_syspath(udev, udev_list_entry_get_name(dev_entry)); + name = udev_device_get_sysname(dev); + ret = virGetInterface(conn, name, macstr); + udev_device_unref(dev); + +err: + if (enumerate) + udev_enumerate_unref(enumerate); + udev_unref(udev); + + return ret; +} + +static virInterfaceDriver udevIfaceDriver = { + "udev", + .open = udevIfaceOpenInterface, /* 0.10.3 */ + .close = udevIfaceCloseInterface, /* 0.10.3 */ + .numOfInterfaces = udevIfaceNumOfInterfaces, /* 0.10.3 */ + .listInterfaces = udevIfaceListInterfaces, /* 0.10.3 */ + .numOfDefinedInterfaces = udevIfaceNumOfDefinedInterfaces, /* 0.10.3 */ + .listDefinedInterfaces = udevIfaceListDefinedInterfaces, /* 0.10.3 */ + .listAllInterfaces = udevIfaceListAllInterfaces, /* 0.10.3 */ + .interfaceLookupByName = udevIfaceLookupByName, /* 0.10.3 */ + .interfaceLookupByMACString = udevIfaceLookupByMACString, /* 0.10.3 */ +}; + +int +interfaceRegister(void) { + if (virRegisterInterfaceDriver(&udevIfaceDriver) < 0) + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to register udev interface driver")); + return 0; +} diff --git a/tools/virsh.c b/tools/virsh.c index b32ee8b..f0ec625 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2708,6 +2708,8 @@ vshShowVersion(vshControl *ctl ATTRIBUTE_UNUSED) vshPrint(ctl, " Interface"); # if defined(WITH_NETCF) vshPrint(ctl, " netcf"); +# elif defined(HAVE_UDEV) + vshPrint(ctl, " udev"); # endif #endif #ifdef WITH_NWFILTER -- 1.7.8.6

On 10/06/2012 01:20 PM, Doug Goldstein wrote:
Add a read-only udev based backend for virInterface. Useful for distros that do not have netcf support yet. Multiple libvirt based utilities use a HAL based fallback when virInterface is not available which is less than ideal. This implements: * virConnectNumOfInterfaces() * virConnectListInterfaces() * virConnectNumOfDefinedInterfaces() * virConnectListDefinedInterfaces() * virConnectListAllInterfaces() * virConnectInterfaceLookupByName() * virConnectInterfaceLookupByMACString() --- Change from v5: * squash the virConnectListAllInterfaces() patch in * rename status enum to virUdevStatus * remove checks that don't print errors as the value is checked elsewhere * provide helpers for NumOfInterfaces/NumOfDefinedInterfaces to reduce code * provide helpers for ListInterfaces/ListDefinedInterfaces to reduce code
Looks good; the remaining things I asked in v5 you have saved for later in the series. ACK and pushed, with a couple of tweaks.
+ /* List all interfaces incase we support more filter flags in the future */
s/incase/in case/ Also, I squashed in a line from patch 3/5. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

All other backends for virInterface or other HVs implementations of virInterface list their own names for the name instead of the generic 'Interface' value. This does the same for the netcf based backend. --- src/interface/interface_backend_netcf.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/src/interface/interface_backend_netcf.c b/src/interface/interface_backend_netcf.c index fdc28ea..b857ac7 100644 --- a/src/interface/interface_backend_netcf.c +++ b/src/interface/interface_backend_netcf.c @@ -782,7 +782,7 @@ static int interfaceChangeRollback(virConnectPtr conn, unsigned int flags) #endif /* HAVE_NETCF_TRANSACTIONS */ static virInterfaceDriver interfaceDriver = { - "Interface", + "netcf", .open = interfaceOpenInterface, /* 0.7.0 */ .close = interfaceCloseInterface, /* 0.7.0 */ .numOfInterfaces = interfaceNumOfInterfaces, /* 0.7.0 */ -- 1.7.8.6

On 10/06/2012 01:20 PM, Doug Goldstein wrote:
All other backends for virInterface or other HVs implementations of virInterface list their own names for the name instead of the generic 'Interface' value. This does the same for the netcf based backend. --- src/interface/interface_backend_netcf.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
ACK. However, before pushing, I squashed in one more fix (based on what I saw you do in 3/5): @@ -806,6 +806,10 @@ static virInterfaceDriver interfaceDriver = { }; int interfaceRegister(void) { - virRegisterInterfaceDriver(&interfaceDriver); + if (virRegisterInterfaceDriver(&interfaceDriver) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to register netcf interface driver")); + return -1; + } return 0; } -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Always build all available backends to avoid bit-rot. At run time we select the correct backend and load it by attempting netcf first and then udev. --- src/Makefile.am | 6 ++-- src/interface/interface_backend_netcf.c | 2 +- src/interface/interface_backend_udev.c | 6 +++- src/interface/interface_driver.c | 37 +++++++++++++++++++++++++++++++ src/interface/interface_driver.h | 3 ++ 5 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 src/interface/interface_driver.c diff --git a/src/Makefile.am b/src/Makefile.am index 98448c1..34bc75c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -557,18 +557,18 @@ INTERFACE_DRIVER_SOURCES = if WITH_INTERFACE INTERFACE_DRIVER_SOURCES += \ - interface/interface_driver.h + interface/interface_driver.h \ + interface/interface_driver.c if WITH_NETCF INTERFACE_DRIVER_SOURCES += \ interface/interface_backend_netcf.c -else +endif if HAVE_UDEV INTERFACE_DRIVER_SOURCES += \ interface/interface_backend_udev.c endif endif -endif SECRET_DRIVER_SOURCES = \ secret/secret_driver.h secret/secret_driver.c diff --git a/src/interface/interface_backend_netcf.c b/src/interface/interface_backend_netcf.c index b857ac7..140fa9f 100644 --- a/src/interface/interface_backend_netcf.c +++ b/src/interface/interface_backend_netcf.c @@ -805,7 +805,7 @@ static virInterfaceDriver interfaceDriver = { #endif /* HAVE_NETCF_TRANSACTIONS */ }; -int interfaceRegister(void) { +int netcfIfaceRegister(void) { virRegisterInterfaceDriver(&interfaceDriver); return 0; } diff --git a/src/interface/interface_backend_udev.c b/src/interface/interface_backend_udev.c index bb161ab..b001e6e 100644 --- a/src/interface/interface_backend_udev.c +++ b/src/interface/interface_backend_udev.c @@ -503,9 +503,11 @@ static virInterfaceDriver udevIfaceDriver = { }; int -interfaceRegister(void) { - if (virRegisterInterfaceDriver(&udevIfaceDriver) < 0) +udevIfaceRegister(void) { + if (virRegisterInterfaceDriver(&udevIfaceDriver) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("failed to register udev interface driver")); + return -1; + } return 0; } diff --git a/src/interface/interface_driver.c b/src/interface/interface_driver.c new file mode 100644 index 0000000..fbf861e --- /dev/null +++ b/src/interface/interface_driver.c @@ -0,0 +1,37 @@ +/* + * interface_driver.c: loads the appropriate backend + * + * Copyright (C) 2012 Doug Goldstein <cardoe@cardoe.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; If not, see + * <http://www.gnu.org/licenses/>. + */ +#include <config.h> + +#include "interface_driver.h" + +int +interfaceRegister(void) { +#ifdef WITH_NETCF + /* Attempt to load the netcf based backend first */ + if (netcfIfaceRegister() == 0) + return 0; +#endif /* WITH_NETCF */ +#if HAVE_UDEV + /* If there's no netcf or it failed to load, register the udev backend */ + if (udevIfaceRegister() == 0) + return 0; +#endif /* HAVE_UDEV */ + return -1; +} diff --git a/src/interface/interface_driver.h b/src/interface/interface_driver.h index 67b6218..80ada5c 100644 --- a/src/interface/interface_driver.h +++ b/src/interface/interface_driver.h @@ -26,4 +26,7 @@ int interfaceRegister(void); +int netcfIfaceRegister(void); +int udevIfaceRegister(void); + #endif /* __VIR_INTERFACE__DRIVER_H */ -- 1.7.8.6

On 10/06/2012 01:20 PM, Doug Goldstein wrote:
Always build all available backends to avoid bit-rot. At run time we select the correct backend and load it by attempting netcf first and then udev. --- src/Makefile.am | 6 ++-- src/interface/interface_backend_netcf.c | 2 +- src/interface/interface_backend_udev.c | 6 +++- src/interface/interface_driver.c | 37 +++++++++++++++++++++++++++++++ src/interface/interface_driver.h | 3 ++ 5 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 src/interface/interface_driver.c
diff --git a/src/Makefile.am b/src/Makefile.am
+++ b/src/interface/interface_backend_udev.c @@ -503,9 +503,11 @@ static virInterfaceDriver udevIfaceDriver = { };
int -interfaceRegister(void) { - if (virRegisterInterfaceDriver(&udevIfaceDriver) < 0) +udevIfaceRegister(void) { + if (virRegisterInterfaceDriver(&udevIfaceDriver) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("failed to register udev interface driver")); + return -1;
Oops, this line should be squashed into 1/5.
+int +interfaceRegister(void) { +#ifdef WITH_NETCF + /* Attempt to load the netcf based backend first */ + if (netcfIfaceRegister() == 0) + return 0; +#endif /* WITH_NETCF */ +#if HAVE_UDEV + /* If there's no netcf or it failed to load, register the udev backend */ + if (udevIfaceRegister() == 0) + return 0; +#endif /* HAVE_UDEV */ + return -1;
If neither backend is available, this fails without a good error message. Then again, if neither backend is available, we don't compile this file. So no real loss.
+} diff --git a/src/interface/interface_driver.h b/src/interface/interface_driver.h index 67b6218..80ada5c 100644 --- a/src/interface/interface_driver.h +++ b/src/interface/interface_driver.h @@ -26,4 +26,7 @@
int interfaceRegister(void);
+int netcfIfaceRegister(void); +int udevIfaceRegister(void);
I guess it doesn't hurt to unconditionally declare these, even though they are only conditionally compiled. ACK and pushed. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Added support for retrieving the XML defining a specific interface via the udev based backend to virInterface. Implement the following APIs for the udev based backend: * virInterfaceGetXMLDesc() Note: Does not support bond devices. --- *** * NOTE: I'm aware this is incomplete (e.g. no bond support) and we aren't * showing devices in a bridge. This patch can be considered only a RFC * or it can be applied and I'll work on further support as I figure * out how to implement it. Waiting for feedback from LKML and from * the udev ML on some questions or patches. *** change since v1: * support vlans * cleanups for simplifications src/interface/interface_backend_udev.c | 128 ++++++++++++++++++++++++++++++++ 1 files changed, 128 insertions(+), 0 deletions(-) diff --git a/src/interface/interface_backend_udev.c b/src/interface/interface_backend_udev.c index b001e6e..2f37bed 100644 --- a/src/interface/interface_backend_udev.c +++ b/src/interface/interface_backend_udev.c @@ -489,6 +489,133 @@ err: return ret; } +static char * +udevIfaceGetXMLDesc(virInterfacePtr ifinfo, + unsigned int flags) +{ + struct udev_iface_driver *driverState = ifinfo->conn->interfacePrivateData; + struct udev *udev = udev_ref(driverState->udev); + struct udev_device *dev; + virInterfaceDef ifacedef; + unsigned int mtu; + const char *mtu_str; + char *vlan_parent_dev = NULL; + char *xmlstr = NULL; + + virCheckFlags(VIR_INTERFACE_XML_INACTIVE, NULL); + + /* Lookup the device we've been asked about */ + dev = udev_device_new_from_subsystem_sysname(udev, "net", + ifinfo->name); + if (!dev) { + virReportError(VIR_ERR_NO_INTERFACE, + _("couldn't find interface named '%s'"), + ifinfo->name); + goto cleanup; + } + + /* Zero it all out */ + memset(&ifacedef, 0, sizeof(ifacedef)); + + /* Common pieces */ + ifacedef.name = ifinfo->name; + ifacedef.mac = ifinfo->mac; + ifacedef.startmode = VIR_INTERFACE_START_UNSPECIFIED; + + /* MTU */ + mtu_str = udev_device_get_sysattr_value(dev, "mtu"); + if (virStrToLong_ui(mtu_str, NULL, 10, &mtu) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not parse MTU value '%s'"), mtu_str); + goto cleanup; + } + ifacedef.mtu = mtu; + + /* Number of IP protocols this interface has assigned */ + /* XXX: Do we want a netlink query or a call out to ip or leave it? */ + ifacedef.nprotos = 0; + ifacedef.protos = NULL; + + /* Check if its a VLAN since we can have a VLAN of any of the + * other devices */ + vlan_parent_dev = strrchr(ifinfo->name, '.'); + if (vlan_parent_dev) { + /* Found the VLAN dot */ + char *vid; + + vlan_parent_dev = strdup(ifinfo->name); + if (!vlan_parent_dev) { + virReportOOMError(); + goto cleanup; + } + + /* Find the DEVICE.VID separator again */ + vid = strrchr(vlan_parent_dev, '.'); + if (!vid) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to find the VID for the VLAN device '%s'"), + ifinfo->name); + goto cleanup; + } + + /* Replace the dot with a NULL so we can have the device and VID */ + vid[0] = '\0'; + vid++; + + /* Set our type to VLAN */ + ifacedef.type = VIR_INTERFACE_TYPE_VLAN; + + /* Set the VLAN specifics */ + ifacedef.data.vlan.tag = vid; + ifacedef.data.vlan.devname = vlan_parent_dev; + } else if (STREQ_NULLABLE(udev_device_get_devtype(dev), "bridge")) { + /* Check if its a bridge device */ + const char *stp_str; + int stp; + + /* Set our type to Bridge */ + ifacedef.type = VIR_INTERFACE_TYPE_BRIDGE; + + /* Bridge specifics */ + ifacedef.data.bridge.delay = + strdup(udev_device_get_sysattr_value(dev, "bridge/forward_delay")); + if (!ifacedef.data.bridge.delay) { + virReportOOMError(); + goto cleanup; + } + + stp_str = udev_device_get_sysattr_value(dev, "bridge/stp_state"); + if (virStrToLong_i(stp_str, NULL, 10, &stp) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not parse STP state '%s'"), stp_str); + goto cleanup; + } + ifacedef.data.bridge.stp = stp; + + /* Members of the bridge */ + /* XXX: Waiting to hear back from the udev/systemd ML how to + * successfully query this. + */ + ifacedef.data.bridge.nbItf = 0; + ifacedef.data.bridge.itf = NULL; + } else { + /* Set our type to ethernet */ + ifacedef.type = VIR_INTERFACE_TYPE_ETHERNET; + } + + /* Convert our interface to XML */ + xmlstr = virInterfaceDefFormat(&ifacedef); + +cleanup: + if (ifacedef.type == VIR_INTERFACE_TYPE_BRIDGE) + VIR_FREE(ifacedef.data.bridge.delay); + + VIR_FREE(vlan_parent_dev); + + udev_unref(udev); + return xmlstr; +} + static virInterfaceDriver udevIfaceDriver = { "udev", .open = udevIfaceOpenInterface, /* 0.10.3 */ @@ -500,6 +627,7 @@ static virInterfaceDriver udevIfaceDriver = { .listAllInterfaces = udevIfaceListAllInterfaces, /* 0.10.3 */ .interfaceLookupByName = udevIfaceLookupByName, /* 0.10.3 */ .interfaceLookupByMACString = udevIfaceLookupByMACString, /* 0.10.3 */ + .interfaceGetXMLDesc = udevIfaceGetXMLDesc, /* 0.10.3 */ }; int -- 1.7.8.6

On 10/06/2012 01:20 PM, Doug Goldstein wrote:
Added support for retrieving the XML defining a specific interface via the udev based backend to virInterface. Implement the following APIs for the udev based backend: * virInterfaceGetXMLDesc()
Note: Does not support bond devices. --- *** * NOTE: I'm aware this is incomplete (e.g. no bond support) and we aren't * showing devices in a bridge. This patch can be considered only a RFC * or it can be applied and I'll work on further support as I figure * out how to implement it. Waiting for feedback from LKML and from * the udev ML on some questions or patches. ***
I'd rather let Laine also chime in on this one before it gets pushed (or decide that we need to wait for a v3), but my cursory review didn't find any major problems. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 10/09/2012 12:22 PM, Eric Blake wrote:
On 10/06/2012 01:20 PM, Doug Goldstein wrote:
Added support for retrieving the XML defining a specific interface via the udev based backend to virInterface. Implement the following APIs for the udev based backend: * virInterfaceGetXMLDesc()
Note: Does not support bond devices. --- *** * NOTE: I'm aware this is incomplete (e.g. no bond support) and we aren't * showing devices in a bridge. This patch can be considered only a RFC * or it can be applied and I'll work on further support as I figure * out how to implement it. Waiting for feedback from LKML and from * the udev ML on some questions or patches. *** I'd rather let Laine also chime in on this one before it gets pushed (or decide that we need to wait for a v3), but my cursory review didn't find any major problems.
I just now saw this comment (my libvirt mail has been a bit out of control lately). My opinion of this backend is that there are two dangers here that we need to avoid: 1) (I began encountering this one while answering some of Doug's questions on IRC) For much of the functionality required for an interface driver backend on a Linux distro, the exact code that you would end up putting into your own back end has already been implemented in netcf, so you could end up duplicating a lot of code (and bugs), with the associated headaches. 2) After a certain point, the amount of time/energy spent re-implementing the same functionality in a different driver becomes greater than the amount of time it would have taken to just bite the bullet and do a netcf port. (On the other hand, it's a useful exercise in working out the kinks in a generalized interface driver interface) Actually it occurred to me just a couple nights ago that this effort might have been spent making a port of netcf that used udev to get the device list, rather than an alternative interface driver for libvirt; this would probably take about the same amount of effort, and the result would be a partial-featured "universal Linux" port for netcf. Where am I going with all of this? Well, I think the best implementation of this driver is the simplest one that provides enough information to satisfy your particular needs, and as I understand it that is to allow virt-manager to get a list of network devices and their types, to be used for a drop-down list of guest network connection options. Once you go beyond that, you're duplicating code (e.g., to get the ip addresses of an interface, you either have some ugly hack that calls the "ip" utility, or you write pretty much the same hunk of libnl calls that are already in netcf). At some point, I think it's best to give in and just make a netcf port for your distro (then you'll also gain things like "virsh bridge eth0 br0"). (BTW, something that I just thought about now - does this driver differentiate at all between transient and permanent bridge/vlan devices? Historically netcf has only listed devices that are permanently in the systems configuration, but not anything that is temporary (e.g. the virbrX bridges created for libvirt's virtual networks).)

Add support to check if a specific interface is active by supporting the following API function in the udev based virInterface backend: * virConnectInterfaceIsActive() --- src/interface/interface_backend_udev.c | 30 ++++++++++++++++++++++++++++++ 1 files changed, 30 insertions(+), 0 deletions(-) diff --git a/src/interface/interface_backend_udev.c b/src/interface/interface_backend_udev.c index 2f37bed..82c8681 100644 --- a/src/interface/interface_backend_udev.c +++ b/src/interface/interface_backend_udev.c @@ -616,6 +616,35 @@ cleanup: return xmlstr; } +static int +udevIfaceIsActive(virInterfacePtr ifinfo) +{ + struct udev_iface_driver *driverState = ifinfo->conn->interfacePrivateData; + struct udev *udev = udev_ref(driverState->udev); + struct udev_device *dev; + int status; + + dev = udev_device_new_from_subsystem_sysname(udev, "net", + ifinfo->name); + if (!dev) { + virReportError(VIR_ERR_NO_INTERFACE, + _("couldn't find interface named '%s'"), + ifinfo->name); + status = -1; + goto cleanup; + } + + /* Check if its active or not */ + status = STREQ(udev_device_get_sysattr_value(dev, "operstate"), "up"); + + udev_device_unref(dev); + +cleanup: + udev_unref(udev); + + return status; +} + static virInterfaceDriver udevIfaceDriver = { "udev", .open = udevIfaceOpenInterface, /* 0.10.3 */ @@ -628,6 +657,7 @@ static virInterfaceDriver udevIfaceDriver = { .interfaceLookupByName = udevIfaceLookupByName, /* 0.10.3 */ .interfaceLookupByMACString = udevIfaceLookupByMACString, /* 0.10.3 */ .interfaceGetXMLDesc = udevIfaceGetXMLDesc, /* 0.10.3 */ + .interfaceIsActive = udevIfaceIsActive, /* 0.10.3 */ }; int -- 1.7.8.6

On 10/06/2012 01:20 PM, Doug Goldstein wrote:
Add support to check if a specific interface is active by supporting the following API function in the udev based virInterface backend: * virConnectInterfaceIsActive() --- src/interface/interface_backend_udev.c | 30 ++++++++++++++++++++++++++++++ 1 files changed, 30 insertions(+), 0 deletions(-)
+ + /* Check if its active or not */
s/its/it's/ ACK and pushed. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org
participants (3)
-
Doug Goldstein
-
Eric Blake
-
Laine Stump