New clixon_client API for external access
This commit is contained in:
parent
94c6bd9c49
commit
3fe218da2e
4 changed files with 324 additions and 206 deletions
|
|
@ -31,6 +31,8 @@ Expected: February 2021
|
|||
|
||||
### New features
|
||||
|
||||
* New clixon_client API for external access
|
||||
|
||||
### C/CLI-API changes on existing features
|
||||
|
||||
Developers may need to change their code
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC(Netgate)
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
|
@ -31,11 +30,25 @@
|
|||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _CLIXON_CLIENT_H
|
||||
#define _CLIXON_CLIENT_H
|
||||
|
||||
/*
|
||||
* Types
|
||||
*/
|
||||
typedef void *clixon_handle;
|
||||
typedef void *clixon_client_handle;
|
||||
|
||||
/* Connection type as parameter to connect */
|
||||
typedef enum {
|
||||
CLIXON_CLIENT_IPC, /* Internal IPC API, only experimental use */
|
||||
CLIXON_CLIENT_NETCONF, /* External Netconf */
|
||||
CLIXON_CLIENT_SSH /* External Netconf over SSH */
|
||||
} clixon_client_type;
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
|
|
@ -44,24 +57,16 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
int clixon_client_connect(void *h, const struct sockaddr* srv, int srv_sz);
|
||||
int clixon_client_close(int sock);
|
||||
int clixon_client_session_start(void *h, const char *db);
|
||||
int clixon_client_session_end(void *h);
|
||||
int clixon_client_subscribe(int sock, int priority, int nspace,
|
||||
int *spoint, const char *fmt, ...);
|
||||
int clixon_client_subscribe_done(int sock);
|
||||
int clixon_client_read_subscription_socket(int sock, int sub_points[], int *resultlen);
|
||||
int clixon_client_num_instances(int sock, const char *xnamespace, const char *xpath);
|
||||
int clixon_client_get_bool(int sock, int *rval, const char *xnamespace, const char *xpath);
|
||||
int clixon_client_get_str(int sock, char *rval, int n, const char *xnamespace, const char *xpath);
|
||||
int clixon_client_get_u_int8(int sock, uint8_t *rval, const char *xnamespace, const char *xpath);
|
||||
int clixon_client_get_u_int16(int sock, uint16_t *rval, const char *xnamespace, const char *xpath);
|
||||
int clixon_client_get_u_int32(int sock, uint32_t *rval, const char *xnamespace, const char *xpath);
|
||||
int clixon_client_get_u_int64(int sock, uint64_t *rval, const char *xnamespace, const char *xpath);
|
||||
|
||||
void *clixon_client_init(const char *name, FILE *estream, const int debug, const char *config_file);
|
||||
int clixon_client_terminate(void *h);
|
||||
clixon_handle clixon_client_init(const char *config_file);
|
||||
int clixon_client_terminate(clixon_handle h);
|
||||
clixon_client_handle clixon_client_connect(clixon_handle h, clixon_client_type socktype);
|
||||
int clixon_client_disconnect(clixon_client_handle ch);
|
||||
int clixon_client_get_bool(clixon_client_handle ch, int *rval, const char *xnamespace, const char *xpath);
|
||||
int clixon_client_get_str(clixon_client_handle ch, char *rval, int n, const char *xnamespace, const char *xpath);
|
||||
int clixon_client_get_uint8(clixon_client_handle ch, uint8_t *rval, const char *xnamespace, const char *xpath);
|
||||
int clixon_client_get_uint16(clixon_client_handle ch, uint16_t *rval, const char *xnamespace, const char *xpath);
|
||||
int clixon_client_get_uint32(clixon_client_handle ch, uint32_t *rval, const char *xnamespace, const char *xpath);
|
||||
int clixon_client_get_uint64(clixon_client_handle ch, uint64_t *rval, const char *xnamespace, const char *xpath);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC(Netgate)
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
|
@ -31,6 +30,7 @@
|
|||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
|
|
@ -41,6 +41,8 @@
|
|||
#include <syslog.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
/* cligen */
|
||||
|
|
@ -54,6 +56,7 @@
|
|||
#include "clixon_err.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_proc.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xml_io.h"
|
||||
|
|
@ -64,29 +67,61 @@
|
|||
#include "clixon_proto_client.h"
|
||||
#include "clixon_client.h"
|
||||
|
||||
/*
|
||||
* Constants
|
||||
*/
|
||||
/* Netconf binary default, override with environment variable: CLIXON_NETCONF_BIN
|
||||
* Could try to get path from install/makefile data
|
||||
*/
|
||||
#define CLIXON_NETCONF_BIN "/usr/local/bin/clixon_netconf"
|
||||
|
||||
#define CLIXON_CLIENT_MAGIC 0x54fe649a
|
||||
|
||||
#define chandle(ch) (assert(clixon_client_handle_check(ch)==0),(struct clixon_client_handle *)(ch))
|
||||
|
||||
/*! Internal structure of clixon client handle.
|
||||
*/
|
||||
struct clixon_client_handle{
|
||||
uint32_t cch_magic; /* magic number */
|
||||
clixon_client_type cch_type; /* Clixon socket type */
|
||||
int cch_fdin; /* Input file descriptor */
|
||||
int cch_fdout; /* Output file descriptor, Only applies for NETCONF/SSH */
|
||||
int cch_pid; /* Sub-process-id Only applies for NETCONF/SSH */
|
||||
};
|
||||
|
||||
/*! Check struct magic number for sanity checks
|
||||
* @param[in] h Clicon client handle
|
||||
* @retval 0 Sanity check OK
|
||||
* @retval -1 Sanity check failed
|
||||
*/
|
||||
static int
|
||||
clixon_client_handle_check(clixon_client_handle ch)
|
||||
{
|
||||
/* Dont use handle macro to avoid recursion */
|
||||
struct clixon_client_handle *cch = (struct clixon_client_handle *)(ch);
|
||||
|
||||
return cch->cch_magic == CLIXON_CLIENT_MAGIC ? 0 : -1;
|
||||
}
|
||||
|
||||
/*! Initialize Clixon client API
|
||||
* @param[in] name Name of client (NYI)
|
||||
* @param[in] estream Error/debug file (NULL: syslog)
|
||||
* @param[in] debug Clixon debug flag
|
||||
* @param[in] config_file Clixon configuration file (eg for CLICON_SOCK)
|
||||
* @param[in] config_file Clixon configuration file, or NULL for default
|
||||
* @see clixon_client_close
|
||||
*/
|
||||
clicon_handle
|
||||
clixon_client_init(const char *name,
|
||||
FILE *estream,
|
||||
const int debug,
|
||||
const char *config_file)
|
||||
clixon_handle
|
||||
clixon_client_init(const char *config_file)
|
||||
{
|
||||
clicon_handle h;
|
||||
|
||||
clicon_log_init("clixon", LOG_DEBUG, CLICON_LOG_STDERR);
|
||||
clicon_debug_init(debug, estream);
|
||||
fprintf(stderr, "fprintf %s\n", __FUNCTION__);
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
/* Initiate CLICON handle. CLIgen is also initialized */
|
||||
if ((h = clicon_handle_init()) == NULL)
|
||||
return NULL;
|
||||
/* Set clixon config file - reuse the one in the main installation */
|
||||
clicon_option_str_set(h, "CLICON_CONFIGFILE", (char*)config_file);
|
||||
clicon_option_str_set(h, "CLICON_CONFIGFILE",
|
||||
config_file?(char*)config_file:CLIXON_DEFAULT_CONFIG);
|
||||
/* Find, read and parse configfile */
|
||||
if (clicon_options_main(h) < 0)
|
||||
return NULL;
|
||||
|
|
@ -106,59 +141,144 @@ clixon_client_terminate(clicon_handle h)
|
|||
|
||||
/*! Connect client to clixon backend according to config and return a socket
|
||||
* @param[in] h Clixon handle
|
||||
* @retval s Socket, need to be closed by caller
|
||||
* @see clixon_client_close Close the socket returned here
|
||||
* @param[out] fdin Open netconf output file descr
|
||||
* @param[out] fdout Open netconf output file descr. If socket same as fdin
|
||||
* @retval cs Clixon session handler
|
||||
* @retval NULL Error
|
||||
* @see clixon_client_disconnect Close the socket returned here
|
||||
*/
|
||||
int
|
||||
clixon_client_handle
|
||||
clixon_client_connect(clicon_handle h,
|
||||
const struct sockaddr* srv,
|
||||
int srv_sz)
|
||||
clixon_client_type socktype)
|
||||
{
|
||||
int s = -1;
|
||||
struct clixon_client_handle *cch = NULL;
|
||||
char **argv = NULL;
|
||||
int nr;
|
||||
int i;
|
||||
char *netconf_bin = NULL;
|
||||
struct stat st = {0,};
|
||||
size_t sz = sizeof(struct clixon_client_handle);
|
||||
char dbgstr[8];
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (clicon_rpc_connect(h, &s) < 0)
|
||||
return -1;
|
||||
return s;
|
||||
if ((cch = malloc(sz)) == NULL){
|
||||
clicon_err(OE_NETCONF, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(cch, 0, sz);
|
||||
cch->cch_magic = CLIXON_CLIENT_MAGIC;
|
||||
cch->cch_type = socktype;
|
||||
switch (socktype){
|
||||
case CLIXON_CLIENT_IPC:
|
||||
if (clicon_rpc_connect(h, &cch->cch_fdin) < 0)
|
||||
goto err;
|
||||
cch->cch_fdout = cch->cch_fdin;
|
||||
break;
|
||||
case CLIXON_CLIENT_NETCONF:
|
||||
nr = 7;
|
||||
if (clicon_debug_get() != 0)
|
||||
nr += 2;
|
||||
if ((argv = calloc(nr, sizeof(char *))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "calloc");
|
||||
goto err;
|
||||
}
|
||||
i = 0;
|
||||
if ((netconf_bin = getenv("CLIXON_NETCONF_BIN")) == NULL)
|
||||
netconf_bin = CLIXON_NETCONF_BIN;
|
||||
if (stat(netconf_bin, &st) < 0){
|
||||
clicon_err(OE_NETCONF, 0, "netconf binary %s: %s. Set with CLIXON_NETCONF_BIN=",
|
||||
netconf_bin, strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
argv[i++] = netconf_bin;
|
||||
argv[i++] = "-q";
|
||||
argv[i++] = "-f";
|
||||
argv[i++] = clicon_option_str(h, "CLICON_CONFIGFILE");
|
||||
argv[i++] = "-l"; /* log to syslog */
|
||||
argv[i++] = "s";
|
||||
if (clicon_debug_get() != 0){
|
||||
argv[i++] = "-D";
|
||||
snprintf(dbgstr, sizeof(dbgstr)-1, "%d", clicon_debug_get());
|
||||
argv[i++] = dbgstr;
|
||||
}
|
||||
argv[i++] = NULL;
|
||||
assert(i==nr);
|
||||
if (clixon_proc_socket(argv, &cch->cch_pid, &cch->cch_fdin, &cch->cch_fdout) < 0){
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case CLIXON_CLIENT_SSH:
|
||||
break;
|
||||
} /* switch */
|
||||
done:
|
||||
return cch;
|
||||
err:
|
||||
free(cch);
|
||||
cch = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Connect client to clixon backend according to config and return a socket
|
||||
* @param[in] s Socket, opened by connect call
|
||||
* @see clixon_client_connect
|
||||
* @param[in] cch Clixon client session handle
|
||||
* @see clixon_client_connect where the handle is created
|
||||
* The handle is deallocated
|
||||
*/
|
||||
int
|
||||
clixon_client_close(int s)
|
||||
clixon_client_disconnect(clixon_client_handle ch)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clixon_client_handle *cch = chandle(ch);
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
close(s);
|
||||
return 0;
|
||||
if (cch == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "Expected cch handle");
|
||||
goto done;
|
||||
}
|
||||
switch(cch->cch_type){
|
||||
case CLIXON_CLIENT_IPC:
|
||||
close(cch->cch_fdin);
|
||||
break;
|
||||
case CLIXON_CLIENT_NETCONF:
|
||||
if (clixon_proc_socket_close(cch->cch_pid,
|
||||
cch->cch_fdin,
|
||||
cch->cch_fdout) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case CLIXON_CLIENT_SSH:
|
||||
break;
|
||||
}
|
||||
free(cch);
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
clixon_client_subscribe(int sock,
|
||||
int priority,
|
||||
int nspace,
|
||||
int *spoint,
|
||||
const char *fmt, ...)
|
||||
static int
|
||||
clixon_xml_bottom(cxobj *xtop,
|
||||
cxobj **xbotp)
|
||||
{
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
int retval = -1;
|
||||
cxobj *x;
|
||||
cxobj *xp;
|
||||
cxobj *xc = NULL;
|
||||
|
||||
int
|
||||
clixon_client_subscribe_done(int sock)
|
||||
{
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
clixon_client_read_subscription_socket(int sock,
|
||||
int sub_points[],
|
||||
int *resultlen)
|
||||
{
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
xp = xtop;
|
||||
while (xp != NULL){
|
||||
/* Find child, if many, one which is not a key, if any */
|
||||
xc = NULL;
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xp, x, CX_ELMNT)) != NULL)
|
||||
xc = x;
|
||||
/* xc is last XXX */
|
||||
if (xc == NULL)
|
||||
break;
|
||||
xp = xc;
|
||||
}
|
||||
retval = 0;
|
||||
*xbotp = xp;
|
||||
retval = 0;
|
||||
// done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Internal function to construct a get-config and query a value from the backend
|
||||
|
|
@ -169,44 +289,47 @@ clixon_client_read_subscription_socket(int sock,
|
|||
* @param[out] val String value
|
||||
*/
|
||||
static int
|
||||
clixon_client_get_xdata(int sock,
|
||||
clixon_client_get_xdata(int fdin,
|
||||
int fdout,
|
||||
const char *namespace,
|
||||
const char *xpath,
|
||||
cxobj **xdata)
|
||||
{
|
||||
int retval = -1;
|
||||
char *retdata = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xd;
|
||||
cbuf *cb = NULL;
|
||||
struct clicon_msg *msg = NULL;
|
||||
cbuf *msg = NULL;
|
||||
cbuf *msgret = NULL;
|
||||
const char *db = "running";
|
||||
cvec *nsc = NULL;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
if ((msg = cbuf_new()) == NULL){
|
||||
clicon_err(OE_PLUGIN, errno, "cbuf_new");
|
||||
goto done;
|
||||
cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
|
||||
cprintf(cb, " xmlns:%s=\"%s\"",
|
||||
}
|
||||
if ((msgret = cbuf_new()) == NULL){
|
||||
clicon_err(OE_PLUGIN, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(msg, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
|
||||
cprintf(msg, " xmlns:%s=\"%s\"",
|
||||
NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
|
||||
cprintf(cb, "><get-config><source><%s/></source>", db);
|
||||
cprintf(msg, "><get-config><source><%s/></source>", db);
|
||||
if (xpath && strlen(xpath)){
|
||||
cprintf(cb, "<%s:filter %s:type=\"xpath\" xmlns=\"%s\" %s:select=\"%s\"",
|
||||
cprintf(msg, "<%s:filter %s:type=\"xpath\" xmlns=\"%s\" %s:select=\"%s\"",
|
||||
NETCONF_BASE_PREFIX,
|
||||
NETCONF_BASE_PREFIX,
|
||||
namespace,
|
||||
NETCONF_BASE_PREFIX,
|
||||
xpath);
|
||||
if (xml_nsctx_cbuf(cb, nsc) < 0)
|
||||
if (xml_nsctx_cbuf(msg, nsc) < 0)
|
||||
goto done;
|
||||
cprintf(cb, "/>");
|
||||
cprintf(msg, "/>");
|
||||
}
|
||||
cprintf(cb, "</get-config></rpc>");
|
||||
clicon_debug(1, "%s xml:%s", __FUNCTION__, cbuf_get(cb));
|
||||
if ((msg = clicon_msg_encode(0, "%s", cbuf_get(cb))) == NULL)
|
||||
cprintf(msg, "</get-config></rpc>");
|
||||
if (clicon_rpc1(fdin, fdout, msg, msgret) < 0)
|
||||
goto done;
|
||||
if (clicon_rpc(sock, msg, &retdata) < 0)
|
||||
goto done;
|
||||
if (clixon_xml_parse_string(retdata, YB_NONE, NULL, &xret, NULL) < 0)
|
||||
if (clixon_xml_parse_string(cbuf_get(msgret), YB_NONE, NULL, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xd = xpath_first(xret, NULL, "/rpc-reply/rpc-error")) != NULL){
|
||||
xd = xml_parent(xd); /* point to rpc-reply */
|
||||
|
|
@ -224,17 +347,19 @@ clixon_client_get_xdata(int sock,
|
|||
*xdata = xd;
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (msg)
|
||||
free(msg);
|
||||
cbuf_free(msg);
|
||||
if (msgret)
|
||||
cbuf_free(msgret);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
clixon_client_get_val(int sock,
|
||||
clixon_client_get_val(int fdin,
|
||||
int fdout,
|
||||
const char *namespace,
|
||||
const char *xpath,
|
||||
char **val)
|
||||
|
|
@ -247,15 +372,12 @@ clixon_client_get_val(int sock,
|
|||
clicon_err(OE_XML, EINVAL, "Expected val");
|
||||
goto done;
|
||||
}
|
||||
#if 1 /* XXX: Problem is xpath with indexes used in original need to change to [0] in the 2nd access */
|
||||
if (clixon_client_get_xdata(sock, namespace, "/", &xdata) < 0)
|
||||
if (clixon_client_get_xdata(fdin, fdout, namespace, xpath, &xdata) < 0)
|
||||
goto done;
|
||||
#else
|
||||
if (clixon_client_get_xdata(sock, namespace, xpath, &xdata) < 0)
|
||||
if (clixon_xml_bottom(xdata, &xobj) < 0)
|
||||
goto done;
|
||||
#endif
|
||||
if ((xobj = xpath_first(xdata, NULL, "%s", xpath)) == NULL){
|
||||
clicon_err(OE_XML, ENOENT, "Expected local xpath result");
|
||||
if (xobj == NULL){
|
||||
clicon_err(OE_XML, EFAULT, "No bottom xml found");
|
||||
goto done;
|
||||
}
|
||||
*val = xml_body(xobj);
|
||||
|
|
@ -264,51 +386,23 @@ clixon_client_get_val(int sock,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Get number of list entries
|
||||
* @param[in] sock Stream socket
|
||||
* @note there is no way using netconf to get this number: instead the whole list is retrieved.
|
||||
*/
|
||||
int
|
||||
clixon_client_num_instances(int sock,
|
||||
const char *namespace,
|
||||
const char *xpath)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xdata = NULL;
|
||||
cxobj **vec = NULL;
|
||||
size_t veclen;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
#if 1 /* XXX: Problem is xpath with indexes used in original need to change to [0] in the 2nd access */
|
||||
if (clixon_client_get_xdata(sock, namespace, "/", &xdata) < 0)
|
||||
goto done;
|
||||
#else
|
||||
if (clixon_client_get_xdata(sock, namespace, xpath, &xdata) < 0)
|
||||
goto done;
|
||||
#endif
|
||||
if (xpath_vec(xdata, NULL, "%s", &vec, &veclen, xpath) < 0)
|
||||
goto done;
|
||||
retval = veclen;
|
||||
done:
|
||||
if (vec)
|
||||
free(vec);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
clixon_client_get_bool(int sock,
|
||||
clixon_client_get_bool(clixon_client_handle ch,
|
||||
int *rval,
|
||||
const char *namespace,
|
||||
const char *xpath)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clixon_client_handle *cch = chandle(ch);
|
||||
char *val = NULL;
|
||||
char *reason = NULL;
|
||||
int ret;
|
||||
uint8_t val0=0;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (clixon_client_get_val(sock, namespace, xpath, &val) < 0)
|
||||
if (clixon_client_get_val(cch->cch_fdin,
|
||||
cch->cch_fdout,
|
||||
namespace, xpath, &val) < 0)
|
||||
goto done;
|
||||
if ((ret = parse_bool(val, &val0, &reason)) < 0){
|
||||
clicon_err(OE_XML, errno, "parse_bool");
|
||||
|
|
@ -334,17 +428,20 @@ clixon_client_get_bool(int sock,
|
|||
* @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc)
|
||||
*/
|
||||
int
|
||||
clixon_client_get_str(int sock,
|
||||
clixon_client_get_str(clixon_client_handle ch,
|
||||
char *rval,
|
||||
int n,
|
||||
const char *namespace,
|
||||
const char *xpath)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clixon_client_handle *cch = chandle(ch);
|
||||
char *val = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (clixon_client_get_val(sock, namespace, xpath, &val) < 0)
|
||||
if (clixon_client_get_val(cch->cch_fdin,
|
||||
cch->cch_fdout,
|
||||
namespace, xpath, &val) < 0)
|
||||
goto done;
|
||||
strncpy(rval, val, n-1);
|
||||
rval[n-1]= '\0';
|
||||
|
|
@ -354,18 +451,21 @@ clixon_client_get_str(int sock,
|
|||
}
|
||||
|
||||
int
|
||||
clixon_client_get_u_int8(int sock,
|
||||
clixon_client_get_uint8(clixon_client_handle ch,
|
||||
uint8_t *rval,
|
||||
const char *namespace,
|
||||
const char *xpath)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clixon_client_handle *cch = chandle(ch);
|
||||
char *val = NULL;
|
||||
char *reason = NULL;
|
||||
int ret;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (clixon_client_get_val(sock, namespace, xpath, &val) < 0)
|
||||
if (clixon_client_get_val(cch->cch_fdin,
|
||||
cch->cch_fdout,
|
||||
namespace, xpath, &val) < 0)
|
||||
goto done;
|
||||
if ((ret = parse_uint8(val, rval, &reason)) < 0){
|
||||
clicon_err(OE_XML, errno, "parse_bool");
|
||||
|
|
@ -383,18 +483,21 @@ clixon_client_get_u_int8(int sock,
|
|||
}
|
||||
|
||||
int
|
||||
clixon_client_get_u_int16(int sock,
|
||||
clixon_client_get_uint16(clixon_client_handle ch,
|
||||
uint16_t *rval,
|
||||
const char *namespace,
|
||||
const char *xpath)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clixon_client_handle *cch = chandle(ch);
|
||||
char *val = NULL;
|
||||
char *reason = NULL;
|
||||
int ret;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (clixon_client_get_val(sock, namespace, xpath, &val) < 0)
|
||||
if (clixon_client_get_val(cch->cch_fdin,
|
||||
cch->cch_fdout,
|
||||
namespace, xpath, &val) < 0)
|
||||
goto done;
|
||||
if ((ret = parse_uint16(val, rval, &reason)) < 0){
|
||||
clicon_err(OE_XML, errno, "parse_bool");
|
||||
|
|
@ -412,18 +515,21 @@ clixon_client_get_u_int16(int sock,
|
|||
}
|
||||
|
||||
int
|
||||
clixon_client_get_u_int32(int sock,
|
||||
clixon_client_get_uint32(clixon_client_handle ch,
|
||||
uint32_t *rval,
|
||||
const char *namespace,
|
||||
const char *xpath)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clixon_client_handle *cch = chandle(ch);
|
||||
char *val = NULL;
|
||||
char *reason = NULL;
|
||||
int ret;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (clixon_client_get_val(sock, namespace, xpath, &val) < 0)
|
||||
if (clixon_client_get_val(cch->cch_fdin,
|
||||
cch->cch_fdout,
|
||||
namespace, xpath, &val) < 0)
|
||||
goto done;
|
||||
if ((ret = parse_uint32(val, rval, &reason)) < 0){
|
||||
clicon_err(OE_XML, errno, "parse_bool");
|
||||
|
|
@ -441,18 +547,21 @@ clixon_client_get_u_int32(int sock,
|
|||
}
|
||||
|
||||
int
|
||||
clixon_client_get_u_int64(int sock,
|
||||
clixon_client_get_uint64(clixon_client_handle ch,
|
||||
uint64_t *rval,
|
||||
const char *namespace,
|
||||
const char *xpath)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clixon_client_handle *cch = chandle(ch);
|
||||
char *val = NULL;
|
||||
char *reason = NULL;
|
||||
int ret;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (clixon_client_get_val(sock, namespace, xpath, &val) < 0)
|
||||
if (clixon_client_get_val(cch->cch_fdin,
|
||||
cch->cch_fdout,
|
||||
namespace, xpath, &val) < 0)
|
||||
goto done;
|
||||
if ((ret = parse_uint64(val, rval, &reason)) < 0){
|
||||
clicon_err(OE_XML, errno, "parse_bool");
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ cfg=$dir/conf_yang.xml
|
|||
fyang=$dir/example-client.yang
|
||||
cfile=$dir/example-client.c
|
||||
pdir=$dir/plugin
|
||||
app=$pdir/example-api
|
||||
app=$dir/clixon-app
|
||||
|
||||
if [ ! -d $pdir ]; then
|
||||
mkdir $pdir
|
||||
|
|
@ -67,43 +67,45 @@ EOF
|
|||
cat<<EOF > $cfile
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h> /* sockaddr_in */
|
||||
#include <arpa/inet.h> /* inet_addr */
|
||||
#include <clixon/clixon_client.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define CLIXONCONF "$cfg"
|
||||
#include <clixon/clixon_log.h>
|
||||
#include <clixon/clixon_client.h>
|
||||
|
||||
int
|
||||
main(int argc,
|
||||
char **argv)
|
||||
{
|
||||
int s;
|
||||
void *h = NULL; /* clixon handle */
|
||||
clixon_handle h = NULL; /* clixon handle */
|
||||
clixon_client_handle ch = NULL; /* clixon client handle */
|
||||
|
||||
if ((h = clixon_client_init("server", stderr, 0, CLIXONCONF)) == NULL)
|
||||
// clicon_log_init("client", LOG_DEBUG, CLICON_LOG_STDERR); // debug
|
||||
// clicon_debug_init(1, NULL); // debug
|
||||
|
||||
/* Provide a clixon config-file, get a clixon handle */
|
||||
if ((h = clixon_client_init("$cfg")) == NULL)
|
||||
return -1;
|
||||
if ((s = clixon_client_connect(h)) < 0){
|
||||
/* Make a conenction over netconf or ssh/netconf */
|
||||
if ((ch = clixon_client_connect(h, CLIXON_CLIENT_NETCONF)) == NULL)
|
||||
return -1;
|
||||
}
|
||||
/* Here are read functions depending on an example YANG
|
||||
* (Need an example YANG and XML input to confd)
|
||||
*/
|
||||
{
|
||||
uint32_t u = 0;
|
||||
if (clixon_client_get_uint32(s, &u, "urn:example:clixon-client", "/table/parameter[name='a']/value") < 0)
|
||||
if (clixon_client_get_uint32(ch, &u, "urn:example:clixon-client", "/table/parameter[name='a']/value") < 0)
|
||||
return -1;
|
||||
printf("%u\n", u); /* for test output */
|
||||
}
|
||||
clixon_client_close(s);
|
||||
clixon_client_disconnect(ch);
|
||||
clixon_client_terminate(h);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
new "compile $cfile -> $app"
|
||||
echo "$CC -g -Wall -I/usr/local/include $cfile -o $app -lclixon"
|
||||
expectpart "$($CC -g -Wall -I/usr/local/include $cfile -o $app -lclixon)" 0 ""
|
||||
exit
|
||||
|
||||
new "test params: -s init -f $cfg"
|
||||
|
||||
if [ $BE -ne 0 ]; then
|
||||
|
|
@ -130,17 +132,17 @@ if [ $RC -ne 0 ]; then
|
|||
wait_restconf
|
||||
fi
|
||||
|
||||
XML='<c xmlns="urn:example:api"><y3><k>2</k></y3><y3><k>3</k></y3><y3><k>5</k><val>zorro</val></y3><y3><k>7</k></y3></c>'
|
||||
XML='<table xmlns="urn:example:clixon-client"><parameter><name>a</name><value>42</value></parameter></table>'
|
||||
|
||||
# Add a set of entries using restconf
|
||||
new "PUT a set of entries"
|
||||
expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-api:c -d "$XML")" 0 "HTTP/1.1 201 Created"
|
||||
new "POST the XML"
|
||||
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data -d "$XML")" 0 "HTTP/1.1 201 Created"
|
||||
|
||||
new "Check entries"
|
||||
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-api:c -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' "$XML"
|
||||
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-client:table -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' "$XML"
|
||||
|
||||
new "Send a trigger"
|
||||
expectpart "$(curl $CURLOPTS -X POST $RCPROTO://localhost/restconf/operations/example-api:trigger -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 204 No Content'
|
||||
new "Run $app"
|
||||
expectpart "$($app)" 0 '^42$'
|
||||
|
||||
if [ $RC -ne 0 ]; then
|
||||
new "Kill restconf daemon"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue