restconf plugins
This commit is contained in:
parent
b82a8bae26
commit
64f197cb00
7 changed files with 247 additions and 34 deletions
|
|
@ -53,8 +53,7 @@
|
|||
#include "netconf_lib.h"
|
||||
#include "netconf_plugin.h"
|
||||
|
||||
/*
|
||||
* Unload a plugin
|
||||
/*! Unload a plugin
|
||||
*/
|
||||
static int
|
||||
plugin_unload(clicon_handle h, void *handle)
|
||||
|
|
@ -112,9 +111,7 @@ static int nplugins = 0;
|
|||
static plghndl_t *plugins = NULL;
|
||||
static netconf_reg_t *deps = NULL;
|
||||
|
||||
/*
|
||||
* netconf_plugin_load
|
||||
* Load allplugins you can find in CLICON_NETCONF_DIR
|
||||
/*! Load all plugins you can find in CLICON_NETCONF_DIR
|
||||
*/
|
||||
int
|
||||
netconf_plugin_load(clicon_handle h)
|
||||
|
|
@ -160,6 +157,7 @@ quit:
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Unload all netconf plugins */
|
||||
int
|
||||
netconf_plugin_unload(clicon_handle h)
|
||||
{
|
||||
|
|
@ -180,8 +178,7 @@ netconf_plugin_unload(clicon_handle h)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call plugin_start in all plugins
|
||||
/*! Call plugin_start in all plugins
|
||||
*/
|
||||
int
|
||||
netconf_plugin_start(clicon_handle h, int argc, char **argv)
|
||||
|
|
@ -203,8 +200,7 @@ netconf_plugin_start(clicon_handle h, int argc, char **argv)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* netconf_register_callback
|
||||
/*! Register netconf callback
|
||||
* Called from plugin to register a callback for a specific netconf XML tag.
|
||||
*/
|
||||
int
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <time.h>
|
||||
#include <fcgi_stdio.h>
|
||||
#include <signal.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <curl/curl.h>
|
||||
|
|
@ -230,3 +231,143 @@ readdata(FCGX_Request *r)
|
|||
return cb;
|
||||
}
|
||||
|
||||
static int nplugins = 0;
|
||||
static plghndl_t *plugins = NULL;
|
||||
|
||||
/*! Load a dynamic plugin object and call it's init-function
|
||||
* Note 'file' may be destructively modified
|
||||
*/
|
||||
static plghndl_t
|
||||
plugin_load (clicon_handle h,
|
||||
char *file,
|
||||
int dlflags,
|
||||
const char *cnklbl)
|
||||
{
|
||||
char *error;
|
||||
void *handle = NULL;
|
||||
plginit_t *initfn;
|
||||
|
||||
dlerror(); /* Clear any existing error */
|
||||
if ((handle = dlopen (file, dlflags)) == NULL) {
|
||||
error = (char*)dlerror();
|
||||
clicon_err(OE_PLUGIN, errno, "dlopen: %s\n", error ? error : "Unknown error");
|
||||
goto quit;
|
||||
}
|
||||
/* call plugin_init() if defined */
|
||||
if ((initfn = dlsym(handle, PLUGIN_INIT)) != NULL) {
|
||||
if (initfn(h) != 0) {
|
||||
clicon_err(OE_PLUGIN, errno, "Failed to initiate %s\n", strrchr(file,'/')?strchr(file, '/'):file);
|
||||
goto quit;
|
||||
}
|
||||
}
|
||||
quit:
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
/*! Load all plugins you can find in CLICON_RESTCONF_DIR
|
||||
*/
|
||||
int
|
||||
restconf_plugin_load(clicon_handle h)
|
||||
{
|
||||
int retval = -1;
|
||||
char *dir;
|
||||
int ndp;
|
||||
struct dirent *dp;
|
||||
int i;
|
||||
char *filename;
|
||||
plghndl_t *handle;
|
||||
|
||||
if ((dir = clicon_restconf_dir(h)) == NULL){
|
||||
clicon_err(OE_PLUGIN, 0, "clicon_restconf_dir not defined");
|
||||
goto quit;
|
||||
}
|
||||
/* Get plugin objects names from plugin directory */
|
||||
if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG, __FUNCTION__))<0)
|
||||
goto quit;
|
||||
|
||||
/* Load all plugins */
|
||||
for (i = 0; i < ndp; i++) {
|
||||
filename = chunk_sprintf(__FUNCTION__, "%s/%s", dir, dp[i].d_name);
|
||||
clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...",
|
||||
(int)strlen(filename), filename);
|
||||
if (filename == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "chunk");
|
||||
goto quit;
|
||||
}
|
||||
if ((handle = plugin_load (h, filename, RTLD_NOW, __FUNCTION__)) == NULL)
|
||||
goto quit;
|
||||
if ((plugins = rechunk(plugins, (nplugins+1) * sizeof (*plugins), NULL)) == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "chunk");
|
||||
goto quit;
|
||||
}
|
||||
plugins[nplugins++] = handle;
|
||||
unchunk (filename);
|
||||
}
|
||||
retval = 0;
|
||||
quit:
|
||||
unchunk_group(__FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Unload a plugin
|
||||
*/
|
||||
static int
|
||||
plugin_unload(clicon_handle h, void *handle)
|
||||
{
|
||||
int retval = 0;
|
||||
char *error;
|
||||
plgexit_t *exitfn;
|
||||
|
||||
/* Call exit function is it exists */
|
||||
exitfn = dlsym(handle, PLUGIN_EXIT);
|
||||
if (dlerror() == NULL)
|
||||
exitfn(h);
|
||||
|
||||
dlerror(); /* Clear any existing error */
|
||||
if (dlclose(handle) != 0) {
|
||||
error = (char*)dlerror();
|
||||
clicon_err(OE_PLUGIN, errno, "dlclose: %s\n", error ? error : "Unknown error");
|
||||
/* Just report */
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Unload all restconf plugins */
|
||||
int
|
||||
restconf_plugin_unload(clicon_handle h)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nplugins; i++)
|
||||
plugin_unload(h, plugins[i]);
|
||||
if (plugins)
|
||||
unchunk(plugins);
|
||||
nplugins = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Call plugin_start in all plugins
|
||||
*/
|
||||
int
|
||||
restconf_plugin_start(clicon_handle h,
|
||||
int argc,
|
||||
char **argv)
|
||||
{
|
||||
int i;
|
||||
plgstart_t *startfn;
|
||||
|
||||
for (i = 0; i < nplugins; i++) {
|
||||
/* Call exit function is it exists */
|
||||
if ((startfn = dlsym(plugins[i], PLUGIN_START)) == NULL)
|
||||
break;
|
||||
optind = 0;
|
||||
if (startfn(h, argc, argv) < 0) {
|
||||
clicon_debug(1, "plugin_start() failed\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,4 +30,10 @@ int str2cvec(char *string, char delim1, char delim2, cvec **cvp);
|
|||
int test(FCGX_Request *r, int dbg);
|
||||
cbuf *readdata(FCGX_Request *r);
|
||||
|
||||
|
||||
int restconf_plugin_load(clicon_handle h);
|
||||
int restconf_plugin_start(clicon_handle h, int argc, char **argv);
|
||||
int restconf_plugin_unload(clicon_handle h);
|
||||
|
||||
|
||||
#endif /* _RESTCONF_LIB_H_ */
|
||||
|
|
|
|||
|
|
@ -56,12 +56,42 @@
|
|||
#include "restconf_lib.h"
|
||||
|
||||
/* Command line options to be passed to getopt(3) */
|
||||
#define RESTCONF_OPTS "hDf:"
|
||||
#define RESTCONF_OPTS "hDf:p:"
|
||||
|
||||
/* Should be discovered via "/.well-known/host-meta"
|
||||
resource ([RFC6415]) */
|
||||
#define RESTCONF_API_ROOT "/restconf/"
|
||||
|
||||
/*! REST OPTIONS method
|
||||
* According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] head Set if HEAD request instead of GET
|
||||
* @code
|
||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||
* @endcode
|
||||
*/
|
||||
static int
|
||||
api_data_options(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
int head)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
FCGX_FPrintF(r->out, "GET, HEAD, OPTIONS, PUT, POST, DELETE\r\n");
|
||||
retval = 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Generic REST GET method
|
||||
* According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] h Clixon handle
|
||||
|
|
@ -172,19 +202,17 @@ api_data_get(clicon_handle h,
|
|||
cprintf(path1, "/%s", name);
|
||||
}
|
||||
}
|
||||
clicon_debug(1, "path:%s", cbuf_get(path));
|
||||
clicon_debug(1, "path1:%s", cbuf_get(path1));
|
||||
if (xmldb_get(h, "running", cbuf_get(path), 0, &xt, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/yang.data+xml\r\n");
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/yang.data+json\r\n");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
if (xml2json_cbuf(cbx, xt, 1, 0) < 0)
|
||||
goto done;
|
||||
FCGX_FPrintF(r->out, "%s\r\n", cbuf_get(cbx));
|
||||
FCGX_FPrintF(r->out, "%s", cbuf_get(cbx));
|
||||
FCGX_FPrintF(r->out, "hej\r\n\r\n");
|
||||
retval = 0;
|
||||
done:
|
||||
if (vec)
|
||||
|
|
@ -249,17 +277,23 @@ api_data_delete(clicon_handle h,
|
|||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] dvec Stream input data
|
||||
* @param[in] post POST instead of PUT
|
||||
* Example:
|
||||
curl -X PUT -d {\"enabled\":\"false\"} http://127.0.0.1/restconf/data/interfaces/interface=eth1
|
||||
*
|
||||
PUT:
|
||||
if the PUT request creates a new resource,
|
||||
a "201 Created" status-line is returned. If an existing resource is
|
||||
modified, a "204 No Content" status-line is returned.
|
||||
|
||||
* Problem: we have URI that defines a path (eg "interface/name=eth1") and data
|
||||
* which defines a tree from that point.
|
||||
* But, xmldb api can only do either
|
||||
* - xmldb_put() with a complete xml-tree, or
|
||||
* - xmldb_put_xkey for path and key value
|
||||
* What we need is path and sub-xml tree.
|
||||
* Alt1: parse URI to XML and and call xmldb_put()
|
||||
* Alt2: Extend xmldb API with path + xml-tree.
|
||||
POST:
|
||||
If the POST method succeeds, a "201 Created" status-line is returned
|
||||
and there is no response message-body. A "Location" header
|
||||
identifying the child resource that was created MUST be present in
|
||||
the response in this case.
|
||||
|
||||
If the data resource already exists, then the POST request MUST fail
|
||||
and a "409 Conflict" status-line MUST be returned.
|
||||
*/
|
||||
static int
|
||||
api_data_put(clicon_handle h,
|
||||
|
|
@ -268,7 +302,8 @@ api_data_put(clicon_handle h,
|
|||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data)
|
||||
char *data,
|
||||
int post)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
|
|
@ -304,7 +339,7 @@ api_data_put(clicon_handle h,
|
|||
if (clicon_rpc_commit(h, "candidate", "running",
|
||||
0, 0) < 0)
|
||||
goto done;
|
||||
FCGX_SetExitStatus(201, r->out);
|
||||
FCGX_SetExitStatus(201, r->out); /* Created */
|
||||
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
done:
|
||||
|
|
@ -339,10 +374,14 @@ api_data(clicon_handle h,
|
|||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
request_method = FCGX_GetParam("REQUEST_METHOD", r->envp);
|
||||
if (strcmp(request_method, "GET")==0)
|
||||
if (strcmp(request_method, "OPTIONS")==0)
|
||||
retval = api_data_options(h, r, pcvec, pi, qvec, 0);
|
||||
else if (strcmp(request_method, "GET")==0)
|
||||
retval = api_data_get(h, r, pcvec, pi, qvec);
|
||||
else if (strcmp(request_method, "PUT")==0)
|
||||
retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data);
|
||||
retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, 0);
|
||||
else if (strcmp(request_method, "POST")==0)
|
||||
retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, 1);
|
||||
else if (strcmp(request_method, "DELETE")==0)
|
||||
retval = api_data_delete(h, r, api_path, pi);
|
||||
else
|
||||
|
|
@ -389,6 +428,7 @@ request_process(clicon_handle h,
|
|||
method = pvec[2];
|
||||
retval = 0;
|
||||
test(r, 1);
|
||||
/* XXX Credentials */
|
||||
if (strcmp(method, "data") == 0) /* restconf, skip /api/data */
|
||||
retval = api_data(h, r, path, pcvec, 2, qvec, data);
|
||||
else if (strcmp(method, "test") == 0)
|
||||
|
|
@ -405,6 +445,7 @@ request_process(clicon_handle h,
|
|||
if (cb)
|
||||
cbuf_free(cb);
|
||||
unchunk_group(__FUNCTION__);
|
||||
clicon_debug(1, "%s end", __FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -417,13 +458,18 @@ usage(clicon_handle h,
|
|||
char *argv0)
|
||||
|
||||
{
|
||||
char *restconfdir = clicon_restconf_dir(h);
|
||||
|
||||
fprintf(stderr, "usage:%s [options]\n"
|
||||
"where options are\n"
|
||||
"\t-h \t\tHelp\n"
|
||||
"\t-D \t\tDebug. Log to syslog\n"
|
||||
"\t-f <file>\tConfiguration file (mandatory)\n",
|
||||
argv0
|
||||
"\t-f <file>\tConfiguration file (mandatory)\n"
|
||||
"\t-d <dir>\tSpecify restconf plugin directory dir (default: %s)\n",
|
||||
argv0,
|
||||
restconfdir
|
||||
);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/*! Main routine for grideye fastcgi API
|
||||
|
|
@ -460,6 +506,11 @@ main(int argc,
|
|||
usage(h, argv[0]);
|
||||
clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg);
|
||||
break;
|
||||
case 'd': /* Plugin directory */
|
||||
if (!strlen(optarg))
|
||||
usage(h, argv[0]);
|
||||
clicon_option_str_set(h, "CLICON_RESTCONF_DIR", optarg);
|
||||
break;
|
||||
default:
|
||||
usage(h, argv[0]);
|
||||
break;
|
||||
|
|
@ -474,6 +525,10 @@ main(int argc,
|
|||
if (clicon_options_main(h) < 0)
|
||||
goto done;
|
||||
|
||||
/* Initialize plugins group */
|
||||
if (restconf_plugin_load(h) < 0)
|
||||
return -1;
|
||||
|
||||
/* Parse yang database spec file */
|
||||
if (yang_spec_main(h, NULL, 0) < 0)
|
||||
goto done;
|
||||
|
|
@ -516,5 +571,6 @@ main(int argc,
|
|||
}
|
||||
retval = 0;
|
||||
done:
|
||||
restconf_plugin_unload(h);
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,9 @@ CLICON_BACKEND_DIR libdir/APPNAME/backend
|
|||
# Location of netconf (frontend) .so plugins
|
||||
CLICON_NETCONF_DIR libdir/APPNAME/netconf
|
||||
|
||||
# Location of restconf (frontend) .so plugins
|
||||
CLICON_RESTCONF_DIR libdir/APPNAME/restconf
|
||||
|
||||
# Location of cli frontend .so plugins
|
||||
CLICON_CLI_DIR libdir/APPNAME/cli
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ char *clicon_backend_dir(clicon_handle h);
|
|||
char *clicon_cli_dir(clicon_handle h);
|
||||
char *clicon_clispec_dir(clicon_handle h);
|
||||
char *clicon_netconf_dir(clicon_handle h);
|
||||
char *clicon_restconf_dir(clicon_handle h);
|
||||
char *clicon_archive_dir(clicon_handle h);
|
||||
char *clicon_startup_config(clicon_handle h);
|
||||
int clicon_sock_family(clicon_handle h);
|
||||
|
|
|
|||
|
|
@ -216,6 +216,10 @@ clicon_option_sanity(clicon_hash_t *copt)
|
|||
clicon_err(OE_UNIX, 0, "CLICON_NETCONF_DIR not defined in config file");
|
||||
goto done;
|
||||
}
|
||||
if (!hash_lookup(copt, "CLICON_RESTCONF_DIR")){
|
||||
clicon_err(OE_UNIX, 0, "CLICON_RESTCONF_DIR not defined in config file");
|
||||
goto done;
|
||||
}
|
||||
if (!hash_lookup(copt, "CLICON_YANG_DIR")){
|
||||
clicon_err(OE_UNIX, 0, "CLICON_YANG_DIR not defined in config file");
|
||||
goto done;
|
||||
|
|
@ -432,6 +436,12 @@ clicon_netconf_dir(clicon_handle h)
|
|||
return clicon_option_str(h, "CLICON_NETCONF_DIR");
|
||||
}
|
||||
|
||||
char *
|
||||
clicon_restconf_dir(clicon_handle h)
|
||||
{
|
||||
return clicon_option_str(h, "CLICON_RESTCONF_DIR");
|
||||
}
|
||||
|
||||
char *
|
||||
clicon_archive_dir(clicon_handle h)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue