[libvirt] [PATCH 2/2] netcf backend for virInterface*.

Laine Stump laine at laine.org
Tue Jul 7 06:40:26 UTC 2009


---
 include/libvirt/virterror.h |    1 +
 qemud/Makefile.am           |    4 +
 qemud/qemud.c               |    6 +
 src/Makefile.am             |   16 ++-
 src/interface_driver.c      |  499 +++++++++++++++++++++++++++++++++++++++++++
 src/interface_driver.h      |   29 +++
 src/libvirt.c               |    4 -
 src/virterror.c             |    6 +
 8 files changed, 560 insertions(+), 5 deletions(-)
 create mode 100644 src/interface_driver.c
 create mode 100644 src/interface_driver.h

diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index 1092896..f587fbf 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -163,6 +163,7 @@ typedef enum {
     VIR_WAR_NO_INTERFACE, /* failed to start interface driver */
     VIR_ERR_NO_INTERFACE, /* interface driver not running */
     VIR_ERR_INVALID_INTERFACE, /* invalid interface object */
+    VIR_ERR_MULTIPLE_INTERFACES, /* more than one matching interface found */
 } virErrorNumber;
 
 /**
diff --git a/qemud/Makefile.am b/qemud/Makefile.am
index 403846a..9f982ba 100644
--- a/qemud/Makefile.am
+++ b/qemud/Makefile.am
@@ -134,6 +134,10 @@ if WITH_NETWORK
     libvirtd_LDADD += ../src/libvirt_driver_network.la
 endif
 
+if WITH_NETCF
+    libvirtd_LDADD += ../src/libvirt_driver_interface.la
+endif
+
 if WITH_NODE_DEVICES
     libvirtd_LDADD += ../src/libvirt_driver_nodedev.la
 endif
diff --git a/qemud/qemud.c b/qemud/qemud.c
index da20aa9..e43f7e3 100644
--- a/qemud/qemud.c
+++ b/qemud/qemud.c
@@ -81,6 +81,9 @@
 #ifdef WITH_NETWORK
 #include "network_driver.h"
 #endif
+#ifdef WITH_NETCF
+#include "interface_driver.h"
+#endif
 #ifdef WITH_STORAGE_DIR
 #include "storage_driver.h"
 #endif
@@ -822,6 +825,9 @@ static struct qemud_server *qemudInitialize(int sigread) {
 #ifdef WITH_NETWORK
     networkRegister();
 #endif
+#ifdef WITH_NETCF
+    interfaceRegister();
+#endif
 #ifdef WITH_STORAGE_DIR
     storageRegister();
 #endif
diff --git a/src/Makefile.am b/src/Makefile.am
index 0c284c0..e94006e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -156,6 +156,9 @@ ONE_DRIVER_SOURCES =					\
 NETWORK_DRIVER_SOURCES =					\
 		network_driver.h network_driver.c
 
+INTERFACE_DRIVER_SOURCES =					\
+		interface_driver.h interface_driver.c
+
 # Storage backend specific impls
 STORAGE_DRIVER_SOURCES =					\
 		storage_driver.h storage_driver.c		\
@@ -386,8 +389,18 @@ libvirt_driver_network_la_SOURCES = $(NETWORK_DRIVER_SOURCES)
 endif
 
 if WITH_NETCF
-libvirt_driver_interface_la_CFLAGS = $(NETCF_CFLAGS)
 libvirt_driver_interface_la_LDFLAGS = $(NETCF_LIBS)
+libvirt_driver_interface_la_CFLAGS = $(NETCF_CFLAGS)
+if WITH_DRIVER_MODULES
+mod_LTLIBRARIES += libvirt_driver_interface.la
+else
+noinst_LTLIBRARIES += libvirt_driver_interface.la
+libvirt_la_LIBADD += libvirt_driver_interface.la
+endif
+if WITH_DRIVER_MODULES
+libvirt_driver_interface_la_LDFLAGS += -module -avoid-version
+endif
+libvirt_driver_interface_la_SOURCES = $(INTERFACE_DRIVER_SOURCES)
 endif
 
 # Needed to keep automake quiet about conditionals
@@ -472,6 +485,7 @@ EXTRA_DIST +=							\
 		$(OPENVZ_DRIVER_SOURCES)			\
 		$(VBOX_DRIVER_SOURCES)				\
 		$(NETWORK_DRIVER_SOURCES)			\
+		$(INTERFACE_DRIVER_SOURCES)			\
 		$(STORAGE_DRIVER_SOURCES)			\
 		$(STORAGE_DRIVER_FS_SOURCES)			\
 		$(STORAGE_DRIVER_LVM_SOURCES)			\
diff --git a/src/interface_driver.c b/src/interface_driver.c
new file mode 100644
index 0000000..6fd0b76
--- /dev/null
+++ b/src/interface_driver.c
@@ -0,0 +1,499 @@
+/*
+ * interface_driver.c: backend driver methods to handle physical
+ *                     interface configuration using the netcf library.
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Laine Stump <laine at redhat.com>
+ */
+
+#include <config.h>
+
+#include <netcf.h>
+
+#include "virterror_internal.h"
+#include "datatypes.h"
+#include "interface_driver.h"
+#include "memory.h"
+
+#define VIR_FROM_THIS VIR_FROM_INTERFACE
+
+#define interfaceReportError(conn, dom, net, code, fmt...)            \
+    virReportErrorHelper(conn, VIR_FROM_THIS, code, __FILE__,         \
+                           __FUNCTION__, __LINE__, fmt)
+
+/* Main driver state */
+struct interface_driver
+{
+    virMutex lock;
+    struct netcf *netcf;
+};
+
+
+static void interfaceDriverLock(struct interface_driver *driver)
+{
+    virMutexLock(&driver->lock);
+}
+
+static void interfaceDriverUnlock(struct interface_driver *driver)
+{
+    virMutexUnlock(&driver->lock);
+}
+
+static int netcf_to_vir_err(int netcf_errcode)
+{
+    switch (netcf_errcode)
+    {
+        case NETCF_NOERROR:
+            /* no error, everything ok */
+            return VIR_ERR_OK;
+        case NETCF_EINTERNAL:
+            /* internal error, aka bug */
+            return VIR_ERR_INTERNAL_ERROR;
+        case NETCF_EOTHER:
+            /* other error, copout for being more specific */
+            return VIR_ERR_INTERNAL_ERROR;
+        case NETCF_ENOMEM:
+            /* allocation failed */
+            return VIR_ERR_NO_MEMORY;
+        case NETCF_EXMLPARSER:
+            /* XML parser choked */
+            return VIR_ERR_XML_ERROR;
+        case NETCF_EXMLINVALID:
+            /* XML invalid in some form */
+            return VIR_ERR_XML_ERROR;
+        case NETCF_ENOENT:
+            /* Required entry in a tree is missing */
+            return VIR_ERR_INTERNAL_ERROR;
+        case NETCF_EEXEC:
+            /* external program execution failed or returned non-0 */
+            return VIR_ERR_INTERNAL_ERROR;
+        default:
+            return VIR_ERR_INTERNAL_ERROR;
+    }
+}
+
+static struct netcf_if *interfaceDriverGetNetcfIF(struct netcf *ncf, virInterfacePtr ifinfo)
+{
+    /* 1) caller already has lock,
+     * 2) caller cleans up iface on return
+     */
+    struct netcf_if *iface = ncf_lookup_by_name(ncf, ifinfo->name);
+    if (!iface) {
+        const char *errmsg, *details;
+        int errcode = ncf_error(ncf, &errmsg, &details);
+        if (errcode != NETCF_NOERROR) {
+            interfaceReportError(ifinfo->conn, NULL, ifinfo, netcf_to_vir_err(errcode),
+                                 "couldn't find interface named '%s' (netcf: %s - %s)",
+                                ifinfo->name, errmsg, details ? details : "");
+        } else {
+            interfaceReportError(ifinfo->conn, NULL, ifinfo, VIR_ERR_NO_INTERFACE,
+                                 "couldn't find interface named '%s'", ifinfo->name);
+        }
+    }
+    return iface;
+}
+
+static virDrvOpenStatus interfaceOpenInterface(virConnectPtr conn,
+                                               virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+                                               int flags ATTRIBUTE_UNUSED)
+{
+    struct interface_driver *driverState;
+
+    if (VIR_ALLOC(driverState) < 0)
+    {
+        virReportOOMError(conn);
+        goto alloc_error;
+    }
+
+    /* initialize non-0 stuff in driverState */
+    if (virMutexInit(&driverState->lock) < 0)
+    {
+        /* what error to report? */
+        goto mutex_error;
+    }
+
+    /* open netcf */
+    if (ncf_init(&driverState->netcf, NULL) != 0)
+    {
+        /* what error to report? */
+        goto netcf_error;
+    }
+
+    conn->interfacePrivateData = driverState;
+    return 0;
+
+netcf_error:
+    if (driverState->netcf)
+    {
+        ncf_close(driverState->netcf);
+    }
+    virMutexDestroy (&driverState->lock);
+mutex_error:
+    VIR_FREE(driverState);
+alloc_error:
+    return -1;
+}
+
+static int interfaceCloseInterface(virConnectPtr conn)
+{
+
+    if (conn->interfacePrivateData != NULL)
+    {
+        struct interface_driver *driver = conn->interfacePrivateData;
+
+        /* close netcf instance */
+        ncf_close(driver->netcf);
+        /* destroy lock */
+        virMutexDestroy(&driver->lock);
+        /* free driver state */
+        VIR_FREE(driver);
+    }
+    conn->interfacePrivateData = NULL;
+    return 0;
+}
+
+static int interfaceNumOfInterfaces(virConnectPtr conn)
+{
+    int count;
+    struct interface_driver *driver = conn->interfacePrivateData;
+
+    interfaceDriverLock(driver);
+    count = ncf_num_of_interfaces(driver->netcf, NETCF_IFACE_ACTIVE);
+    if (count < 0) {
+        const char *errmsg, *details;
+        int errcode = ncf_error(driver->netcf, &errmsg, &details);
+        interfaceReportError(conn, NULL, NULL, netcf_to_vir_err(errcode),
+                             "%s (netcf: %s - %s)",
+                             _("failed to get number of interfaces on host"),
+                            errmsg, details ? details : "");
+    }
+
+    interfaceDriverUnlock(driver);
+    return count;
+}
+
+static int interfaceListInterfaces(virConnectPtr conn, char **const names, int nnames)
+{
+    struct interface_driver *driver = conn->interfacePrivateData;
+    int count;
+
+    interfaceDriverLock(driver);
+
+    count = ncf_list_interfaces(driver->netcf, nnames, names, NETCF_IFACE_ACTIVE);
+    if (count < 0) {
+        const char *errmsg, *details;
+        int errcode = ncf_error(driver->netcf, &errmsg, &details);
+        interfaceReportError(conn, NULL, NULL, netcf_to_vir_err(errcode),
+                             "%s (netcf: %s - %s)",
+                             _("failed to list host interfaces"),
+                            errmsg, details ? details : "");
+    }
+
+    interfaceDriverUnlock(driver);
+    return count;
+
+}
+
+static int interfaceNumOfDefinedInterfaces(virConnectPtr conn)
+{
+    int count;
+    struct interface_driver *driver = conn->interfacePrivateData;
+
+    interfaceDriverLock(driver);
+    count = ncf_num_of_interfaces(driver->netcf, NETCF_IFACE_INACTIVE);
+    if (count < 0) {
+        const char *errmsg, *details;
+        int errcode = ncf_error(driver->netcf, &errmsg, &details);
+        interfaceReportError(conn, NULL, NULL, netcf_to_vir_err(errcode),
+                             "%s (netcf: %s - %s)",
+                             _("failed to get number of defined interfaces on host"),
+                            errmsg, details ? details : "");
+    }
+
+    interfaceDriverUnlock(driver);
+    return count;
+}
+
+static int interfaceListDefinedInterfaces(virConnectPtr conn, char **const names, int nnames)
+{
+    struct interface_driver *driver = conn->interfacePrivateData;
+    int count;
+
+    interfaceDriverLock(driver);
+
+    count = ncf_list_interfaces(driver->netcf, nnames, names, NETCF_IFACE_INACTIVE);
+    if (count < 0) {
+        const char *errmsg, *details;
+        int errcode = ncf_error(driver->netcf, &errmsg, &details);
+        interfaceReportError(conn, NULL, NULL, netcf_to_vir_err(errcode),
+                             "%s (netcf: %s - %s)",
+                             _("failed to list host defined interfaces"),
+                            errmsg, details ? details : "");
+    }
+
+    interfaceDriverUnlock(driver);
+    return count;
+
+}
+
+static virInterfacePtr interfaceLookupByName(virConnectPtr conn,
+                                             const char *name)
+{
+    struct interface_driver *driver = conn->interfacePrivateData;
+    struct netcf_if *iface;
+    virInterfacePtr ret = NULL;
+
+    interfaceDriverLock(driver);
+    iface = ncf_lookup_by_name(driver->netcf, name);
+    if (!iface) {
+        const char *errmsg, *details;
+        int errcode = ncf_error(driver->netcf, &errmsg, &details);
+        if (errcode != NETCF_NOERROR) {
+            interfaceReportError(conn, NULL, NULL, netcf_to_vir_err(errcode),
+                                 "couldn't find interface named '%s' (netcf: %s - %s)",
+                                 name, errmsg, details ? details : "");
+        } else {
+            interfaceReportError(conn, NULL, NULL, VIR_ERR_NO_INTERFACE,
+                                 "couldn't find interface named '%s'", name);
+        }
+        goto cleanup;
+    }
+
+    ret = virGetInterface(conn, ncf_if_name(iface), ncf_if_mac_string(iface));
+
+cleanup:
+    ncf_if_free(iface);
+    interfaceDriverUnlock(driver);
+    return ret;
+}
+
+static virInterfacePtr interfaceLookupByMACString(virConnectPtr conn,
+                                                  const char *macstr)
+{
+    struct interface_driver *driver = conn->interfacePrivateData;
+    struct netcf_if *iface;
+    int niface;
+    virInterfacePtr ret = NULL;
+
+    interfaceDriverLock(driver);
+    niface = ncf_lookup_by_mac_string(driver->netcf, macstr, 1, &iface);
+
+    if (niface < 0) {
+        const char *errmsg, *details;
+        int errcode = ncf_error(driver->netcf, &errmsg, &details);
+        interfaceReportError(conn, NULL, NULL, netcf_to_vir_err(errcode),
+                             "couldn't find interface with MAC address '%s' (netcf: %s - %s)",
+                             macstr, errmsg, details ? details : "");
+        goto cleanup;
+    }
+    if (niface == 0) {
+        interfaceReportError(conn, NULL, NULL, VIR_ERR_NO_INTERFACE,
+                             "couldn't find interface with MAC address '%s'",
+                             macstr);
+        goto cleanup;
+    }
+    if (niface > 1) {
+        interfaceReportError(conn, NULL, NULL, VIR_ERR_MULTIPLE_INTERFACES,
+                             "%s", _("multiple interfaces with matching MAC address"));
+        goto cleanup;
+    }
+
+    ret = virGetInterface(conn, ncf_if_name(iface), ncf_if_mac_string(iface));
+
+cleanup:
+    ncf_if_free(iface);
+    interfaceDriverUnlock(driver);
+    return ret;
+}
+
+static char *interfaceGetXMLDesc(virInterfacePtr ifinfo,
+                                 unsigned int flags ATTRIBUTE_UNUSED)
+{
+    struct interface_driver *driver = ifinfo->conn->interfacePrivateData;
+    struct netcf_if *iface = NULL;
+    char *ret = NULL;
+
+    interfaceDriverLock(driver);
+
+    iface = interfaceDriverGetNetcfIF(driver->netcf, ifinfo);
+    if (!iface) {
+        /* helper already reported error */
+        goto cleanup;
+    }
+
+    ret = ncf_if_xml_desc(iface);
+    if (!ret) {
+        const char *errmsg, *details;
+        int errcode = ncf_error(driver->netcf, &errmsg, &details);
+        interfaceReportError(ifinfo->conn, NULL, ifinfo, netcf_to_vir_err(errcode),
+                             "could not get interface XML description (netcf: %s - %s)",
+                            errmsg, details ? details : "");
+        goto cleanup;
+    }
+
+cleanup:
+    ncf_if_free(iface);
+    interfaceDriverUnlock(driver);
+    return ret;
+}
+
+static virInterfacePtr interfaceDefineXML(virConnectPtr conn,
+                                          const char *xml,
+                                          unsigned int flags ATTRIBUTE_UNUSED)
+{
+    struct interface_driver *driver = conn->interfacePrivateData;
+    struct netcf_if *iface = NULL;
+    virInterfacePtr ret = NULL;
+
+    interfaceDriverLock(driver);
+
+    /*
+     * This is where we will want to validate the XML, and possibly
+     * transform it before sending it on.
+     */
+
+    iface = ncf_define(driver->netcf, xml);
+    if (!iface) {
+        const char *errmsg, *details;
+        int errcode = ncf_error(driver->netcf, &errmsg, &details);
+        interfaceReportError(conn, NULL, NULL, netcf_to_vir_err(errcode),
+                             "could not get interface XML description (netcf: %s - %s)",
+                            errmsg, details ? details : "");
+        goto cleanup;
+    }
+
+    ret = virGetInterface(conn, ncf_if_name(iface), ncf_if_mac_string(iface));
+
+cleanup:
+    ncf_if_free(iface);
+    interfaceDriverUnlock(driver);
+    return ret;
+}
+
+static int interfaceUndefine(virInterfacePtr ifinfo) {
+    struct interface_driver *driver = ifinfo->conn->interfacePrivateData;
+    struct netcf_if *iface = NULL;
+    int ret = -1;
+
+    interfaceDriverLock(driver);
+
+    iface = interfaceDriverGetNetcfIF(driver->netcf, ifinfo);
+    if (!iface) {
+        /* helper already reported error */
+        goto cleanup;
+    }
+
+    ret = ncf_if_undefine(iface);
+    if (ret < 0) {
+        const char *errmsg, *details;
+        int errcode = ncf_error(driver->netcf, &errmsg, &details);
+        interfaceReportError(ifinfo->conn, NULL, ifinfo, netcf_to_vir_err(errcode),
+                             "failed to undefine interface %s (netcf: %s - %s)",
+                             ifinfo->name, errmsg, details ? details : "");
+        goto cleanup;
+    }
+
+cleanup:
+    ncf_if_free(iface);
+    interfaceDriverUnlock(driver);
+    return ret;
+}
+
+static int interfaceCreate(virInterfacePtr ifinfo,
+                           unsigned int flags ATTRIBUTE_UNUSED)
+{
+    struct interface_driver *driver = ifinfo->conn->interfacePrivateData;
+    struct netcf_if *iface = NULL;
+    int ret = -1;
+
+    interfaceDriverLock(driver);
+
+    iface = interfaceDriverGetNetcfIF(driver->netcf, ifinfo);
+    if (!iface) {
+        /* helper already reported error */
+        goto cleanup;
+    }
+
+    ret = ncf_if_up(iface);
+    if (ret < 0) {
+        const char *errmsg, *details;
+        int errcode = ncf_error(driver->netcf, &errmsg, &details);
+        interfaceReportError(ifinfo->conn, NULL, ifinfo, netcf_to_vir_err(errcode),
+                             "failed to create (start) interface %s (netcf: %s - %s)",
+                             ifinfo->name, errmsg, details ? details : "");
+        goto cleanup;
+    }
+
+cleanup:
+    ncf_if_free(iface);
+    interfaceDriverUnlock(driver);
+    return ret;
+}
+
+static int interfaceDestroy(virInterfacePtr ifinfo,
+                            unsigned int flags ATTRIBUTE_UNUSED)
+{
+    struct interface_driver *driver = ifinfo->conn->interfacePrivateData;
+    struct netcf_if *iface = NULL;
+    int ret = -1;
+
+    interfaceDriverLock(driver);
+
+    iface = interfaceDriverGetNetcfIF(driver->netcf, ifinfo);
+    if (!iface) {
+        /* helper already reported error */
+        goto cleanup;
+    }
+
+    ret = ncf_if_down(iface);
+    if (ret < 0) {
+        const char *errmsg, *details;
+        int errcode = ncf_error(driver->netcf, &errmsg, &details);
+        interfaceReportError(ifinfo->conn, NULL, ifinfo, netcf_to_vir_err(errcode),
+                             "failed to destroy (stop) interface %s (netcf: %s - %s)",
+                             ifinfo->name, errmsg, details ? details : "");
+        goto cleanup;
+    }
+
+cleanup:
+    ncf_if_free(iface);
+    interfaceDriverUnlock(driver);
+    return ret;
+}
+
+static virInterfaceDriver interfaceDriver = {
+    "Interface",
+    interfaceOpenInterface,          /* open */
+    interfaceCloseInterface,         /* close */
+    interfaceNumOfInterfaces,        /* numOfInterfaces */
+    interfaceListInterfaces,         /* listInterfaces */
+    interfaceNumOfDefinedInterfaces, /* numOfInterfaces */
+    interfaceListDefinedInterfaces,  /* listInterfaces */
+    interfaceLookupByName,           /* interfaceLookupByName */
+    interfaceLookupByMACString,      /* interfaceLookupByMACSTring */
+    interfaceGetXMLDesc,             /* interfaceGetXMLDesc */
+    interfaceDefineXML,              /* interfaceDefineXML */
+    interfaceUndefine,               /* interfaceUndefine */
+    interfaceCreate,                 /* interfaceCreate */
+    interfaceDestroy,                /* interfaceDestroy */
+};
+
+int interfaceRegister(void) {
+    virRegisterInterfaceDriver(&interfaceDriver);
+    return 0;
+}
diff --git a/src/interface_driver.h b/src/interface_driver.h
new file mode 100644
index 0000000..b4ace5b
--- /dev/null
+++ b/src/interface_driver.h
@@ -0,0 +1,29 @@
+/*
+ * interface_driver.h: core driver methods for managing physical host interfaces
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author:  Laine Stump <laine at redhat.com>
+ */
+
+
+#ifndef __VIR_INTERFACE__DRIVER_H
+#define __VIR_INTERFACE__DRIVER_H
+
+int interfaceRegister(void);
+
+#endif /* __VIR_INTERFACE__DRIVER_H */
diff --git a/src/libvirt.c b/src/libvirt.c
index c1699f4..b85718c 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -1026,9 +1026,6 @@ do_open (const char *name,
         }
     }
 
-#if 0
-    /* TODO: reactivate once we have an interface driver */
-
     for (i = 0; i < virInterfaceDriverTabCount; i++) {
         res = virInterfaceDriverTab[i]->open (ret, auth, flags);
         DEBUG("interface driver %d %s returned %s",
@@ -1047,7 +1044,6 @@ do_open (const char *name,
             break;
         }
     }
-#endif
 
     /* Secondary driver for storage. Optional */
     for (i = 0; i < virStorageDriverTabCount; i++) {
diff --git a/src/virterror.c b/src/virterror.c
index d284fb8..7d0f2e9 100644
--- a/src/virterror.c
+++ b/src/virterror.c
@@ -1056,6 +1056,12 @@ virErrorMsg(virErrorNumber error, const char *info)
             else
                 errmsg = _("invalid interface pointer in %s");
             break;
+        case VIR_ERR_MULTIPLE_INTERFACES:
+            if (info == NULL)
+                errmsg = _("multiple matching interfaces found");
+            else
+                errmsg = _("multiple matching interfaces found: %s");
+            break;
     }
     return (errmsg);
 }
-- 
1.6.3.3.353.g4f2b1




More information about the libvir-list mailing list