[ovs-dev] [PATCH] vswitchd: adding plugin infrastructre

michael.zayats at hp.com michael.zayats at hp.com
Wed Jun 10 08:15:21 UTC 2015


Per this patch ovs-vswitchd searches for shared objects, with 
a certain set of exposed routines, in a specific configurable path. 
Once found, all compatible plugins are loaded and their routines are 
called at appropriate points of the daemon execution.

Currently, the main purpose is to allow integration of netdev/ofproto 
providers without having to modify the main codebase.
However, the design of the plugin core interface is intentionally
kept simple enough (init, run, wait, destroy) to accomodate future 
extensibility use cases.

All "make check" tests pass in both default and --enable-shared configuration.
"make distcheck" pass.
Additional test is added into the testsuite, validating plugin insertion 
and operation. Note that this test is skipped if OVS is not configured 
with --enable-shared.

Signed-off-by: Michael Zayats <michael.zayats at hp.com>
---
 .gitignore                 |   4 ++
 Makefile.am                |   5 ++-
 boot.sh                    |   2 +-
 configure.ac               |  12 +++++-
 lib/automake.mk            |   9 ++++-
 lib/dirs.c.in              |   9 +++++
 lib/dirs.h                 |   1 +
 lib/netdev.c               |   3 ++
 lib/plugins.c              | 218 ++++++++++++++++++++++++++++++++++++++++++++
 lib/plugins.h              |  59 +++++++++++++++++++++++++++++
 lib/plugins.man            |   5 +++
 manpages.mk                |   4 ++
 ofproto/ofproto.c          |   3 ++
 tests/automake.mk          |   7 ++++
 tests/libplugin.c          |  73 ++++++++++++++++++++++++++++++++++++
 tests/ofproto-macros.at    |   7 +++-
 tests/plugin.at            |  23 ++++++++++++
 tests/testsuite.at         |   1 +
 vswitchd/ovs-vswitchd.8.in |   1 +
 vswitchd/ovs-vswitchd.c    |  22 +++++++++--
 20 files changed, 460 insertions(+), 8 deletions(-)

diff --git a/.gitignore b/.gitignore
index c96ceed..2402e2a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,6 +55,9 @@
 /missing
 /missing-distfiles
 /package.m4
+/m4/ltargz.m4
+/m4/argz.m4
+/m4/ltdl.m4
 /stamp-h1
 /_build-gcc
 /_build-clang
@@ -66,4 +69,5 @@ _debian
 odp-netlink.h
 OvsDpInterface.h
 /.vagrant/
+/libltdl/
 testsuite.tmp.orig
diff --git a/Makefile.am b/Makefile.am
index 59a1466..1d2e6e2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,7 +7,7 @@
 
 AUTOMAKE_OPTIONS = foreign subdir-objects
 ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = datapath
+SUBDIRS = datapath libltdl
 
 AM_CPPFLAGS = $(SSL_CFLAGS)
 AM_LDFLAGS = $(SSL_LDFLAGS)
@@ -25,6 +25,8 @@ AM_CPPFLAGS += -I $(top_srcdir)/include
 AM_CPPFLAGS += -I $(top_builddir)/include
 AM_CPPFLAGS += -I $(top_srcdir)/lib
 AM_CPPFLAGS += -I $(top_builddir)/lib
+AM_CPPFLAGS += -I $(top_srcdir)/libltdl
+AM_CPPFLAGS += -I $(top_builddir)/libltdl
 
 AM_CPPFLAGS += $(SSL_INCLUDES)
 
@@ -172,6 +174,7 @@ SUFFIXES += .in
                 -e 's,[@]pkgdatadir[@],$(pkgdatadir),g' \
                 -e 's,[@]sysconfdir[@],$(sysconfdir),g' \
                 -e 's,[@]bindir[@],$(bindir),g' \
+                -e 's,[@]libdir[@],$(libdir),g' \
                 -e 's,[@]sbindir[@],$(sbindir),g' \
                 -e 's,[@]abs_top_srcdir[@],$(abs_top_srcdir),g' \
             > $@.tmp
diff --git a/boot.sh b/boot.sh
index 05dd359..33b5668 100755
--- a/boot.sh
+++ b/boot.sh
@@ -1,2 +1,2 @@
 #! /bin/sh
-autoreconf --install --force
+LIBTOOLIZE_OPTIONS=--quiet autoreconf --install --force
diff --git a/configure.ac b/configure.ac
index 068674e..a326768 100644
--- a/configure.ac
+++ b/configure.ac
@@ -19,7 +19,7 @@ AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_AUX_DIR([build-aux])
 AC_CONFIG_HEADERS([config.h])
 AC_CONFIG_TESTDIR([tests])
-AM_INIT_AUTOMAKE([tar-pax])
+AM_INIT_AUTOMAKE([tar-pax subdir-objects])
 
 AC_PROG_CC_C99
 AM_PROG_CC_C_O
@@ -78,6 +78,16 @@ AC_SUBST([LT_REVISION])
 LT_AGE=libopenvswitch_lt_age
 AC_SUBST([LT_AGE])
 
+LT_CONFIG_LTDL_DIR([libltdl])
+LTDL_INIT
+
+AC_CHECK_HEADER([ltdl.h],
+    [AC_CHECK_LIB([ltdl], [lt_dladvise_init],
+        [LIBLTDL=-lltdl], [LIBLTDL=])],
+    [LIBLTDL=])
+
+AM_CONDITIONAL(ENABLE_SHARED, test "$enable_shared" = "yes")
+
 AC_SEARCH_LIBS([pow], [m])
 AC_SEARCH_LIBS([clock_gettime], [rt])
 AC_SEARCH_LIBS([timer_create], [rt])
diff --git a/lib/automake.mk b/lib/automake.mk
index 7a34c1a..b8a5ca1 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -13,6 +13,8 @@ if WIN32
 lib_libopenvswitch_la_LIBADD += ${PTHREAD_LIBS}
 endif
 
+lib_libopenvswitch_la_LIBADD += libltdl/libltdlc.la
+
 lib_libopenvswitch_la_LDFLAGS = \
         -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
         -Wl,--version-script=$(top_builddir)/lib/libopenvswitch.sym \
@@ -187,6 +189,8 @@ lib_libopenvswitch_la_SOURCES = \
 	lib/pcap-file.h \
 	lib/perf-counter.h \
 	lib/perf-counter.c \
+	lib/plugins.c \
+	lib/plugins.h \
 	lib/poll-loop.c \
 	lib/poll-loop.h \
 	lib/process.c \
@@ -277,7 +281,8 @@ lib_libopenvswitch_la_SOURCES = \
 	lib/lldp/lldpd.c \
 	lib/lldp/lldpd.h \
 	lib/lldp/lldpd-structs.c \
-	lib/lldp/lldpd-structs.h
+	lib/lldp/lldpd-structs.h \
+	libltdl/ltdl.h
 
 if WIN32
 lib_libopenvswitch_la_SOURCES += \
@@ -416,6 +421,7 @@ MAN_FRAGMENTS += \
 	lib/memory-unixctl.man \
 	lib/ofp-version.man \
 	lib/ovs.tmac \
+	lib/plugins.man \
 	lib/service.man \
 	lib/service-syn.man \
 	lib/ssl-bootstrap.man \
@@ -453,6 +459,7 @@ lib/dirs.c: lib/dirs.c.in Makefile
 		-e 's,[@]RUNDIR[@],"$(RUNDIR)",g' \
 		-e 's,[@]DBDIR[@],"$(DBDIR)",g' \
 		-e 's,[@]bindir[@],"$(bindir)",g' \
+		-e 's,[@]libdir[@],"$(libdir)",g' \
 		-e 's,[@]sysconfdir[@],"$(sysconfdir)",g' \
 		-e 's,[@]pkgdatadir[@],"$(pkgdatadir)",g') \
 	     > lib/dirs.c.tmp && \
diff --git a/lib/dirs.c.in b/lib/dirs.c.in
index 85c49ee..9b2d5b3 100644
--- a/lib/dirs.c.in
+++ b/lib/dirs.c.in
@@ -110,3 +110,12 @@ ovs_bindir(void)
 
     return get_dir(&d);
 }
+
+const char *
+ovs_pluginsdir(void)
+{
+    static const char *dbdir;
+
+    dbdir = xasprintf("%s/openvswitch/plugins", @libdir@);
+    return dbdir;
+}
diff --git a/lib/dirs.h b/lib/dirs.h
index 811a51f..742f844 100644
--- a/lib/dirs.h
+++ b/lib/dirs.h
@@ -23,5 +23,6 @@ const char *ovs_rundir(void);     /* /usr/local/var/run/openvswitch */
 const char *ovs_logdir(void);     /* /usr/local/var/log/openvswitch */
 const char *ovs_dbdir(void);      /* /usr/local/etc/openvswitch */
 const char *ovs_bindir(void);     /* /usr/local/bin */
+const char *ovs_pluginsdir(void); /* /usr/local/lib/openvswitch/plugins */
 
 #endif /* dirs.h */
diff --git a/lib/netdev.c b/lib/netdev.c
index 03a7549..0c44071 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -37,6 +37,7 @@
 #include "odp-netlink.h"
 #include "openflow/openflow.h"
 #include "packets.h"
+#include "plugins.h"
 #include "poll-loop.h"
 #include "seq.h"
 #include "shash.h"
@@ -155,6 +156,8 @@ netdev_initialize(void)
 #endif
         netdev_dpdk_register();
 
+        plugins_call(PL_NETDEV_REGISTER);
+
         ovsthread_once_done(&once);
     }
 }
diff --git a/lib/plugins.c b/lib/plugins.c
index e69de29..e4dde28 100644
--- a/lib/plugins.c
+++ b/lib/plugins.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Hewlett-Packard Company Confidential (C)
+ * Copyright 2015 Hewlett-Packard Development Company, L.P.
+ */
+
+#include <config.h>
+#include "plugins.h"
+
+#include <errno.h>
+#include <ltdl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "coverage.h"
+#include "dynamic-string.h"
+#include "lib/dirs.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(plugins);
+
+#define PLAPI(api, name) name,
+static char *plugin_api_names[] = {
+    PLUGINS_API
+};
+#undef PLAPI
+
+/* All plugin APIs are of this form.
+ * It allows to keep things as simple as possible.
+ * Might be extended in the future if need arises */
+typedef void (plugin_func)(void);
+
+struct plugin_class {
+    plugin_func *api_callbacks[PL_API_MAX];
+};
+
+/* libltdl supports multiple plugin interfaces.
+ * We only support one for now. */
+static lt_dlinterface_id interface_id = NULL;
+
+/* This function is called once per each plugin library being loaded */
+static int plugins_open_plugin(const char *filename, void *data);
+
+/* ovs-vswitchd.c calls plugins_init() in the same area where it calls
+ * bridge_init() etc. Since this location is pretty early, plugins
+ * should not use it to perform tasks like netdev/ofproto provider
+ * registrations. 'path' is determined during the configuration process
+ * of OVS */
+void
+plugins_init(const char *path)
+{
+    char *plugins_path;
+    lt_dladvise advise;
+
+    /* User might run the process with '--plugins-path=none' */
+    if (path && !strcmp(path, "none")) {
+        return;
+    }
+
+    /* Various initializations of libltdl library */
+    plugins_path = path ? xstrdup(path) : xstrdup(ovs_pluginsdir());
+    if (!plugins_path) {
+        VLOG_DBG("Failed to allocate plugins path");
+        goto err_path;
+    }
+
+    if (lt_dlinit() ||
+        lt_dlsetsearchpath(plugins_path) || lt_dladvise_init(&advise)) {
+        VLOG_DBG("ltdl initializations: %s", lt_dlerror());
+        goto err_init;
+    }
+
+    interface_id = lt_dlinterface_register("ovs-plugin", NULL);
+    if (!interface_id) {
+        VLOG_DBG("lt_dlinterface_register: %s", lt_dlerror());
+        goto err_interface_register;
+    }
+
+    /* plugins_open_plugin is called for every plugin that is
+     * identified in the search path*/
+    if (lt_dladvise_global(&advise) || lt_dladvise_ext(&advise) ||
+        lt_dlforeachfile(lt_dlgetsearchpath(), &plugins_open_plugin,
+                         &advise)) {
+        VLOG_DBG("ltdl setting advise: %s", lt_dlerror());
+        goto err_set_advise;
+    }
+
+    return;
+
+err_set_advise:
+    /* interface_id is the key indicator of libltdl being
+     * properly initialized. Rest of the routines validate
+     * it before making any libltdl call */
+    lt_dlinterface_free(interface_id);
+    interface_id = NULL;
+
+err_interface_register:
+    if (lt_dladvise_destroy(&advise)) {
+        VLOG_DBG("destroying ltdl advise%s", lt_dlerror());
+    }
+    lt_dlexit();
+
+err_init:
+    free(plugins_path);
+
+err_path:
+    VLOG_ERR("Failed initializing plugin infrastructure");
+}
+
+/* This function is being called by libltdl for every
+ * plugin which is detected in the plugins directory */
+static int
+plugins_open_plugin(const char *filename, void *data)
+{
+    struct plugin_class *plcl;
+    lt_dlhandle handle;
+    int api;
+
+    /* load the plugin */
+    handle = lt_dlopenadvise(filename, *(lt_dladvise *)data);
+    if (!handle) {
+        VLOG_ERR("Failed loading %s: %s", filename, lt_dlerror());
+        return 0;
+    }
+
+    plcl = (struct plugin_class *)malloc(sizeof(struct plugin_class));
+    if (!plcl) {
+        VLOG_ERR("Couldn't allocate plugin class");
+        goto err_plugin_class;
+    }
+
+    /* Load all the available plugin APIs */
+    for (api = 0; api < PL_API_MAX; api++) {
+        plcl->api_callbacks[api] = lt_dlsym(handle, plugin_api_names[api]);
+    }
+
+    /* 'init' and 'destroy' are mandatory, the rest are optional */
+    if (!plcl->api_callbacks[PL_INIT] || !plcl->api_callbacks[PL_DESTROY]) {
+        VLOG_ERR("Mandatory APIs are missing in %s", filename);
+        goto err_dlsym;
+    }
+
+    /* libltdl stores the handle for us, no need to
+     * preserve it anywhere else */
+    if (lt_dlcaller_set_data(interface_id, handle, plcl)) {
+        VLOG_ERR("plugin %s initialized twice? must be a bug", filename);
+        goto err_set_data;
+    }
+
+    /* Let the plugin initialize itself. */
+    plcl->api_callbacks[PL_INIT]();
+
+    VLOG_INFO("Loaded plugin library %s", filename);
+    return 0;
+
+err_set_data:
+err_dlsym:
+    free(plcl);
+
+err_plugin_class:
+    if (lt_dlclose(handle)) {
+        VLOG_ERR("Couldn't dlclose %s", filename);
+    }
+
+    return 0;
+}
+
+/* This function is to be called by hook points.
+ * PL_INIT and PL_DESTROY should not be called outside of this file.
+ * plugins_init and plugins_destroy should be called instead. */
+void
+plugins_call(enum plugin_api api)
+{
+    lt_dlhandle iter_handle = 0;
+    struct plugin_class *plcl;
+
+    /* Validate that plugins infra is properly initialized */
+    if (interface_id) {
+        /* Run the required API in all plugins that have it implemented */
+        while ((iter_handle = lt_dlhandle_iterate(interface_id,
+                                                  iter_handle))) {
+            plcl = (struct plugin_class *)lt_dlcaller_get_data(interface_id,
+                                                               iter_handle);
+            if (plcl && plcl->api_callbacks[api]) {
+                plcl->api_callbacks[api]();
+            }
+        }
+    }
+}
+
+/* Deinitializes all the plugins and libltdl itself */
+void
+plugins_destroy(void)
+{
+    if (interface_id) {
+        /* Give plugin a chance to properly deinitialize itself */
+        plugins_call(PL_DESTROY);
+
+        /* Fully deinitialize libltdl */
+        lt_dlinterface_free(interface_id);
+        lt_dlexit();
+    }
+}
diff --git a/lib/plugins.h b/lib/plugins.h
index e69de29..8f0bbe9 100644
--- a/lib/plugins.h
+++ b/lib/plugins.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2008, 2009, 2011 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Hewlett-Packard Company Confidential (C)
+ * Copyright 2015 Hewlett-Packard Development Company, L.P.
+ */
+
+/* This is an implementation of plugin interface for OVS.
+ * Currently, it's mainly geared towards adding netdev and
+ * ofproto providers to ovs-vswitchd without touching ovs-vswitchd sources.
+ * However, the interface is generic enough to serve for additional purposes. */
+
+#ifndef PLUGINS_H
+#define PLUGINS_H 1
+
+/* Extend this list if additional APIs are defined for plugins.
+ * PL_ parameter is used by the hook points of the APIs.
+ * String names are the names of the APIs that have to be provided
+ * by the plugins. */
+#define PLUGINS_API \
+    PLAPI(PL_INIT, "init") \
+    PLAPI(PL_RUN, "run") \
+    PLAPI(PL_WAIT, "wait") \
+    PLAPI(PL_NETDEV_REGISTER, "netdev_register") \
+    PLAPI(PL_OFPROTO_REGISTER, "ofproto_register") \
+    PLAPI(PL_DESTROY, "destroy")
+
+#define PLAPI(api, name) api,
+enum plugin_api {
+    PLUGINS_API
+    PL_API_MAX
+};
+#undef PLAPI
+
+/* Initialization routine for plugins infrastructure
+ * and initialization of all the available plugins */
+void plugins_init(const char *path);
+
+/* A routine that allows hook points to call certain API 
+ * in all loaded plugins */
+void plugins_call(enum plugin_api);
+
+/* Destructor for plugins infrastructure and all
+ * loaded plugins */
+void plugins_destroy(void);
+
+#endif /* plugins.h */
diff --git a/lib/plugins.man b/lib/plugins.man
index e69de29..248ce50 100644
--- a/lib/plugins.man
+++ b/lib/plugins.man
@@ -0,0 +1,5 @@
+.IP "\fB\-\-plugins-path=\fIpath\fR"
+Sets the path where plugins will be searched. If \fB\-\-plugins-path\fR is
+not used at all, the default path is \fB at libdir@/openvswitch/plugins\fR.
+.IP
+Specifying \fBnone\fR for \fIpath\fR disables the plugins feature.
diff --git a/manpages.mk b/manpages.mk
index 4a76cf1..72fadb4 100644
--- a/manpages.mk
+++ b/manpages.mk
@@ -245,10 +245,12 @@ vswitchd/ovs-vswitchd.8: \
 	lib/daemon.man \
 	lib/dpctl.man \
 	lib/memory-unixctl.man \
+	lib/plugins.man \
 	lib/service.man \
 	lib/ssl-bootstrap.man \
 	lib/ssl.man \
 	lib/unixctl.man \
+	lib/plugins.man \
 	lib/vlog-unixctl.man \
 	lib/vlog.man \
 	ofproto/ofproto-dpif-unixctl.man \
@@ -262,10 +264,12 @@ lib/coverage-unixctl.man:
 lib/daemon.man:
 lib/dpctl.man:
 lib/memory-unixctl.man:
+lib/plugins.man:
 lib/service.man:
 lib/ssl-bootstrap.man:
 lib/ssl.man:
 lib/unixctl.man:
+lib/plugins.man:
 lib/vlog-unixctl.man:
 lib/vlog.man:
 ofproto/ofproto-dpif-unixctl.man:
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 029ff37..2d4a88a 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -48,6 +48,7 @@
 #include "packets.h"
 #include "pinsched.h"
 #include "pktbuf.h"
+#include "plugins.h"
 #include "poll-loop.h"
 #include "random.h"
 #include "seq.h"
@@ -348,6 +349,8 @@ ofproto_init(const struct shash *iface_hints)
 
     ofproto_class_register(&ofproto_dpif_class);
 
+    plugins_call(PL_OFPROTO_REGISTER);
+
     /* Make a local copy, since we don't own 'iface_hints' elements. */
     SHASH_FOR_EACH(node, iface_hints) {
         const struct iface_hint *orig_hint = node->data;
diff --git a/tests/automake.mk b/tests/automake.mk
index e63fb19..e73ab6e 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -77,6 +77,7 @@ TESTSUITE_AT = \
 	tests/ovs-vsctl.at \
 	tests/ovs-monitor-ipsec.at \
 	tests/ovs-xapi-sync.at \
+	tests/plugin.at \
 	tests/stp.at \
 	tests/rstp.at \
 	tests/interface-reconfigure.at \
@@ -293,6 +294,12 @@ tests_ovstest_SOURCES = \
 	tests/test-vconn.c \
 	tests/test-aa.c
 
+if ENABLE_SHARED
+lib_LTLIBRARIES += tests/libplugin.la
+tests_libplugin_la_SOURCES = tests/libplugin.c
+tests_libplugin_la_LDFLAGS = -module
+endif
+
 if !WIN32
 tests_ovstest_SOURCES += \
 	tests/test-unix-socket.c
diff --git a/tests/libplugin.c b/tests/libplugin.c
index e69de29..a1422af 100644
--- a/tests/libplugin.c
+++ b/tests/libplugin.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2008, 2009, 2011 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Hewlett-Packard Company Confidential (C)
+ * Copyright 2015 Hewlett-Packard Development Company, L.P.
+ */
+
+/* A simple plugin to test that ovs-vswitchd can load the plugin, 
+ * call its functions and plugin is able to make calls back into 
+ * ovs-vswitchd */
+
+#include <config.h>
+#include "openvswitch/vlog.h"
+
+/* The structure of the identifier is modulename_LTX_apiname */
+#define init libplugin_LTX_init
+#define run libplugin_LTX_run
+#define wait libplugin_LTX_wait
+#define destroy libplugin_LTX_destroy
+#define netdev_register libplugin_LTX_netdev_register
+#define ofproto_register libplugin_LTX_ofproto_register
+
+void init(void);
+void run(void);
+void wait(void);
+void destroy(void);
+void netdev_register(void);
+void ofproto_register(void);
+
+VLOG_DEFINE_THIS_MODULE(libplugin);
+
+void
+init(void){
+  VLOG_INFO("init");
+}
+
+void
+run(void){
+  VLOG_INFO("run");
+}
+
+void
+wait(void){
+  VLOG_INFO("wait");
+}
+
+void
+destroy(void){
+  VLOG_INFO("destroy");
+}
+
+void
+netdev_register(void){
+  VLOG_INFO("netdev_register");
+}
+
+void
+ofproto_register(void){
+  VLOG_INFO("ofproto_register");
+}
+
diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at
index a69719b..6259823 100644
--- a/tests/ofproto-macros.at
+++ b/tests/ofproto-macros.at
@@ -75,7 +75,11 @@ m4_define([_OVS_VSWITCHD_START],
 /vswitchd|INFO|ovs-vswitchd (Open vSwitch)/d
 /reconnect|INFO|/d
 /ofproto|INFO|using datapath ID/d
-/ofproto|INFO|datapath ID changed to fedcba9876543210/d']])
+/ofproto|INFO|datapath ID changed to fedcba9876543210/d
+/plugins|INFO|/d
+/libplugin|INFO|/d
+/libplugin/b
+/plugins|ERR|Failed loading/d']])
 ])
 
 # OVS_VSWITCHD_START([vsctl-args], [vsctl-output], [=override])
@@ -106,6 +110,7 @@ check_logs () {
 /timeval.*disk: [[0-9]]* reads, [[0-9]]* writes/d
 /timeval.*context switches: [[0-9]]* voluntary, [[0-9]]* involuntary/d
 /ovs_rcu.*blocked [[0-9]]* ms waiting for .* to quiesce/d
+/plugins|ERR|Failed loading/d
 /|WARN|/p
 /|ERR|/p
 /|EMER|/p" ovs-vswitchd.log ovsdb-server.log
diff --git a/tests/plugin.at b/tests/plugin.at
index e69de29..770f251 100644
--- a/tests/plugin.at
+++ b/tests/plugin.at
@@ -0,0 +1,23 @@
+AT_BANNER([plugins])
+
+AT_SETUP([plugins - loading])
+
+AT_SKIP_IF([! test -e ../../.libs/libplugin.so])
+
+_OVS_VSWITCHD_START([--plugins-path=../../.libs])
+
+OVS_VSWITCHD_STOP
+
+verify_plugin_logs () {
+    sed -n "/$1/q
+\$p" ovs-vswitchd.log
+}
+
+AT_CHECK([verify_plugin_logs "libplugin|INFO|init"])
+AT_CHECK([verify_plugin_logs "plugins|INFO|Loaded plugin library ..\/..\/.libs\/libplugin"])
+AT_CHECK([verify_plugin_logs "libplugin|INFO|netdev_register"])
+AT_CHECK([verify_plugin_logs "libplugin|INFO|ofproto_register"])
+AT_CHECK([verify_plugin_logs "libplugin|INFO|run"])
+AT_CHECK([verify_plugin_logs "libplugin|INFO|wait"])
+
+AT_CLEANUP
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 40cb863..e310808 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -67,3 +67,4 @@ m4_include([tests/rstp.at])
 m4_include([tests/vlog.at])
 m4_include([tests/vtep-ctl.at])
 m4_include([tests/auto-attach.at])
+m4_include([tests/plugin.at])
diff --git a/vswitchd/ovs-vswitchd.8.in b/vswitchd/ovs-vswitchd.8.in
index 49c2a40..7a34338 100644
--- a/vswitchd/ovs-vswitchd.8.in
+++ b/vswitchd/ovs-vswitchd.8.in
@@ -102,6 +102,7 @@ configuration.
 .so lib/vlog.man
 .SS "Other Options"
 .so lib/unixctl.man
+.so lib/plugins.man
 .so lib/common.man
 .
 .SH "RUNTIME MANAGEMENT COMMANDS"
diff --git a/vswitchd/ovs-vswitchd.c b/vswitchd/ovs-vswitchd.c
index a1b33da..be1712a 100644
--- a/vswitchd/ovs-vswitchd.c
+++ b/vswitchd/ovs-vswitchd.c
@@ -37,6 +37,7 @@
 #include "netdev.h"
 #include "openflow/openflow.h"
 #include "ovsdb-idl.h"
+#include "plugins.h"
 #include "poll-loop.h"
 #include "simap.h"
 #include "stream-ssl.h"
@@ -58,7 +59,8 @@ static bool want_mlockall;
 
 static unixctl_cb_func ovs_vswitchd_exit;
 
-static char *parse_options(int argc, char *argv[], char **unixctl_path);
+static char *parse_options(int argc, char *argv[], char **unixctl_path,
+                           char **plugins_path);
 OVS_NO_RETURN static void usage(void);
 
 int
@@ -66,6 +68,7 @@ main(int argc, char *argv[])
 {
     char *unixctl_path = NULL;
     struct unixctl_server *unixctl;
+    char *plugins_path = NULL;
     char *remote;
     bool exiting;
     int retval;
@@ -77,12 +80,15 @@ main(int argc, char *argv[])
 
     ovs_cmdl_proctitle_init(argc, argv);
     service_start(&argc, &argv);
-    remote = parse_options(argc, argv, &unixctl_path);
+    remote = parse_options(argc, argv, &unixctl_path, &plugins_path);
+
     fatal_ignore_sigpipe();
     ovsrec_init();
 
     daemonize_start();
 
+    plugins_init(plugins_path);
+
     if (want_mlockall) {
 #ifdef HAVE_MLOCKALL
         if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
@@ -116,11 +122,13 @@ main(int argc, char *argv[])
         bridge_run();
         unixctl_server_run(unixctl);
         netdev_run();
+        plugins_call(PL_RUN);
 
         memory_wait();
         bridge_wait();
         unixctl_server_wait(unixctl);
         netdev_wait();
+        plugins_call(PL_WAIT);
         if (exiting) {
             poll_immediate_wake();
         }
@@ -131,18 +139,20 @@ main(int argc, char *argv[])
     }
     bridge_exit();
     unixctl_server_destroy(unixctl);
+    plugins_destroy();
     service_stop();
 
     return 0;
 }
 
 static char *
-parse_options(int argc, char *argv[], char **unixctl_pathp)
+parse_options(int argc, char *argv[], char **unixctl_pathp, char **plugins_pathp)
 {
     enum {
         OPT_PEER_CA_CERT = UCHAR_MAX + 1,
         OPT_MLOCKALL,
         OPT_UNIXCTL,
+        OPT_PLUGINS,
         VLOG_OPTION_ENUMS,
         OPT_BOOTSTRAP_CA_CERT,
         OPT_ENABLE_DUMMY,
@@ -155,6 +165,7 @@ parse_options(int argc, char *argv[], char **unixctl_pathp)
         {"version",     no_argument, NULL, 'V'},
         {"mlockall",    no_argument, NULL, OPT_MLOCKALL},
         {"unixctl",     required_argument, NULL, OPT_UNIXCTL},
+        {"plugins-path",     required_argument, NULL, OPT_PLUGINS},
         DAEMON_LONG_OPTIONS,
         VLOG_LONG_OPTIONS,
         STREAM_SSL_LONG_OPTIONS,
@@ -191,6 +202,10 @@ parse_options(int argc, char *argv[], char **unixctl_pathp)
             *unixctl_pathp = optarg;
             break;
 
+        case OPT_PLUGINS:
+            *plugins_pathp = optarg;
+            break;
+
         VLOG_OPTION_HANDLERS
         DAEMON_OPTION_HANDLERS
         STREAM_SSL_OPTION_HANDLERS
@@ -257,6 +272,7 @@ usage(void)
            "                            for use with userspace vHost.\n");
     printf("\nOther options:\n"
            "  --unixctl=SOCKET          override default control socket name\n"
+           "  --plugins-path=path       override default path to plugins directory\n"
            "  -h, --help                display this help message\n"
            "  -V, --version             display version information\n");
     exit(EXIT_SUCCESS);



More information about the dev mailing list