* Experimental IPC API, clixon_client, to support a loose integration model

* Many systems using other tools employ such a model, and this API is an effort to make a usage of clixon easier
  * see https://clixon-docs.readthedocs.io/en/latest/overview.html#loose-integration
  * This is work-in-progress and is expected to change
This commit is contained in:
Olof hagsand 2021-01-12 14:08:35 +01:00
parent 2a392ca0e9
commit c269d094e4
5 changed files with 548 additions and 1 deletions

View file

@ -29,6 +29,8 @@
## 4.10.0 ## 4.10.0
Expected: February 2021 Expected: February 2021
### New features
### C/CLI-API changes on existing features ### C/CLI-API changes on existing features
Developers may need to change their code Developers may need to change their code
@ -51,6 +53,10 @@ Users may have to change how they access the system
### Minor changes ### Minor changes
* Experimental IPC API, `clixon_client`, to support a loose integration model
* Many systems using other tools employ such a model, and this API is an effort to make a usage of clixon easier
* see https://clixon-docs.readthedocs.io/en/latest/overview.html#loose-integration
* This is work-in-progress and is expected to change
* Use [https://github.com/clicon/libevhtp](https://github.com/clicon/libevhtp) instead of [https://github.com/criticalstack/libevhtp](https://github.com/criticalstack/libevhtp) as a source of the evhtp source * Use [https://github.com/clicon/libevhtp](https://github.com/clicon/libevhtp) instead of [https://github.com/criticalstack/libevhtp](https://github.com/criticalstack/libevhtp) as a source of the evhtp source
* Added callback to process-control RPC feature in clixon-lib.yang to manage processes * Added callback to process-control RPC feature in clixon-lib.yang to manage processes
* WHen an RPC comes in, be able to look at configuration * WHen an RPC comes in, be able to look at configuration

View file

@ -105,6 +105,7 @@ extern "C" {
#include <clixon/clixon_xml_changelog.h> #include <clixon/clixon_xml_changelog.h>
#include <clixon/clixon_xml_nsctx.h> #include <clixon/clixon_xml_nsctx.h>
#include <clixon/clixon_xml_vec.h> #include <clixon/clixon_xml_vec.h>
#include <clixon/clixon_client.h>
/* /*
* Global variables generated by Makefile * Global variables generated by Makefile

View file

@ -0,0 +1,70 @@
/*
*
***** 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.
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.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2, indicate
your decision by deleting the provisions above and replace them with the
notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
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
/*
* Prototypes
*/
#ifdef __cplusplus
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);
#ifdef __cplusplus
}
#endif
#endif /* _CLIXON_CLIENT_H */

View file

@ -83,7 +83,7 @@ SRC = clixon_sig.c clixon_uid.c clixon_log.c clixon_err.c clixon_event.c \
clixon_proto.c clixon_proto_client.c \ clixon_proto.c clixon_proto_client.c \
clixon_xpath.c clixon_xpath_ctx.c clixon_xpath_eval.c clixon_xpath_function.c clixon_xpath_optimize.c \ clixon_xpath.c clixon_xpath_ctx.c clixon_xpath_eval.c clixon_xpath_function.c clixon_xpath_optimize.c \
clixon_sha1.c clixon_datastore.c clixon_datastore_write.c clixon_datastore_read.c \ clixon_sha1.c clixon_datastore.c clixon_datastore_write.c clixon_datastore_read.c \
clixon_netconf_lib.c clixon_stream.c clixon_nacm.c clixon_netconf_lib.c clixon_stream.c clixon_nacm.c clixon_client.c
YACCOBJS = lex.clixon_xml_parse.o clixon_xml_parse.tab.o \ YACCOBJS = lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \ lex.clixon_yang_parse.o clixon_yang_parse.tab.o \

470
lib/src/clixon_client.c Normal file
View file

@ -0,0 +1,470 @@
/*
*
***** 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.
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.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2, indicate
your decision by deleting the provisions above and replace them with the
notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
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 */
#endif
#include <stdio.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
/* cligen */
#include <cligen/cligen.h>
#include "clixon_queue.h"
#include "clixon_string.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_log.h"
#include "clixon_err.h"
#include "clixon_yang.h"
#include "clixon_options.h"
#include "clixon_xml.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xml_io.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_netconf_lib.h"
#include "clixon_proto.h"
#include "clixon_proto_client.h"
#include "clixon_client.h"
/*! 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)
* @see clixon_client_close
*/
clicon_handle
clixon_client_init(const char *name,
FILE *estream,
const int debug,
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__);
/* 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);
/* Find, read and parse configfile */
if (clicon_options_main(h) < 0)
return NULL;
return h;
}
/*! Deallocate everything from client_init
* @see clixon_client_init
*/
int
clixon_client_terminate(clicon_handle h)
{
clicon_debug(1, "%s", __FUNCTION__);
clicon_handle_exit(h);
return 0;
}
/*! 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
*/
int
clixon_client_connect(clicon_handle h,
const struct sockaddr* srv,
int srv_sz)
{
int s = -1;
clicon_debug(1, "%s", __FUNCTION__);
if (clicon_rpc_connect(h, &s) < 0)
return -1;
return s;
}
/*! Connect client to clixon backend according to config and return a socket
* @param[in] s Socket, opened by connect call
* @see clixon_client_connect
*/
int
clixon_client_close(int s)
{
clicon_debug(1, "%s", __FUNCTION__);
close(s);
return 0;
}
int
clixon_client_subscribe(int sock,
int priority,
int nspace,
int *spoint,
const char *fmt, ...)
{
clicon_debug(1, "%s", __FUNCTION__);
return 0;
}
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;
}
/*! Internal function to construct a get-config and query a value from the backend
*
* @param[in] sock Stream socket
* @param[in] xpath XPath
* @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc)
* @param[out] val String value
*/
static int
clixon_client_get_xdata(int sock,
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;
const char *db = "running";
cvec *nsc = NULL;
if ((cb = cbuf_new()) == NULL)
goto done;
cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
cprintf(cb, " xmlns:%s=\"%s\"",
NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
cprintf(cb, "><get-config><source><%s/></source>", db);
if (xpath && strlen(xpath)){
cprintf(cb, "<%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)
goto done;
cprintf(cb, "/>");
}
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)
goto done;
if (clicon_rpc(sock, msg, &retdata) < 0)
goto done;
if (clixon_xml_parse_string(retdata, 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 */
clixon_netconf_error(xd, "Get config", NULL);
goto done; /* Not fatal */
}
else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL){
if ((xd = xml_new("data", NULL, CX_ELMNT)) == NULL)
goto done;
}
else{
if (xml_rm(xd) < 0)
goto done;
}
*xdata = xd;
retval = 0;
done:
if (xret)
xml_free(xret);
if (cb)
cbuf_free(cb);
if (msg)
free(msg);
return retval;
}
static int
clixon_client_get_val(int sock,
const char *namespace,
const char *xpath,
char **val)
{
int retval = -1;
cxobj *xdata = NULL;
cxobj *xobj;
if (val == NULL){
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)
goto done;
#else
if (clixon_client_get_xdata(sock, namespace, xpath, &xdata) < 0)
goto done;
#endif
if ((xobj = xpath_first(xdata, NULL, "%s", xpath)) == NULL){
clicon_err(OE_XML, ENOENT, "Expected local xpath result");
goto done;
}
*val = xml_body(xobj);
retval = 0;
done:
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,
int *rval,
const char *namespace,
const char *xpath)
{
int retval = -1;
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)
goto done;
if ((ret = parse_bool(val, &val0, &reason)) < 0){
clicon_err(OE_XML, errno, "parse_bool");
goto done;
}
if (ret == 0){
clicon_err(OE_XML, EINVAL, "%s", reason);
goto done;
}
*rval = (int)val0;
retval = 0;
done:
if (reason)
free(reason);
return retval;
}
/*! Get string using get-config
* @param[in] sock Stream socket
* @param[out] rval Return value string
* @param[in] n Length of string
* @param[in] xpath XPath
* @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc)
*/
int
clixon_client_get_str(int sock,
char *rval,
int n,
const char *namespace,
const char *xpath)
{
int retval = -1;
char *val = NULL;
clicon_debug(1, "%s", __FUNCTION__);
if (clixon_client_get_val(sock, namespace, xpath, &val) < 0)
goto done;
strncpy(rval, val, n-1);
rval[n-1]= '\0';
retval = 0;
done:
return retval;
}
int
clixon_client_get_u_int8(int sock,
uint8_t *rval,
const char *namespace,
const char *xpath)
{
int retval = -1;
char *val = NULL;
char *reason = NULL;
int ret;
clicon_debug(1, "%s", __FUNCTION__);
if (clixon_client_get_val(sock, namespace, xpath, &val) < 0)
goto done;
if ((ret = parse_uint8(val, rval, &reason)) < 0){
clicon_err(OE_XML, errno, "parse_bool");
goto done;
}
if (ret == 0){
clicon_err(OE_XML, EINVAL, "%s", reason);
goto done;
}
retval = 0;
done:
if (reason)
free(reason);
return retval;
}
int
clixon_client_get_u_int16(int sock,
uint16_t *rval,
const char *namespace,
const char *xpath)
{
int retval = -1;
char *val = NULL;
char *reason = NULL;
int ret;
clicon_debug(1, "%s", __FUNCTION__);
if (clixon_client_get_val(sock, namespace, xpath, &val) < 0)
goto done;
if ((ret = parse_uint16(val, rval, &reason)) < 0){
clicon_err(OE_XML, errno, "parse_bool");
goto done;
}
if (ret == 0){
clicon_err(OE_XML, EINVAL, "%s", reason);
goto done;
}
retval = 0;
done:
if (reason)
free(reason);
return retval;
}
int
clixon_client_get_u_int32(int sock,
uint32_t *rval,
const char *namespace,
const char *xpath)
{
int retval = -1;
char *val = NULL;
char *reason = NULL;
int ret;
clicon_debug(1, "%s", __FUNCTION__);
if (clixon_client_get_val(sock, namespace, xpath, &val) < 0)
goto done;
if ((ret = parse_uint32(val, rval, &reason)) < 0){
clicon_err(OE_XML, errno, "parse_bool");
goto done;
}
if (ret == 0){
clicon_err(OE_XML, EINVAL, "%s", reason);
goto done;
}
retval = 0;
done:
if (reason)
free(reason);
return retval;
}
int
clixon_client_get_u_int64(int sock,
uint64_t *rval,
const char *namespace,
const char *xpath)
{
int retval = -1;
char *val = NULL;
char *reason = NULL;
int ret;
clicon_debug(1, "%s", __FUNCTION__);
if (clixon_client_get_val(sock, namespace, xpath, &val) < 0)
goto done;
if ((ret = parse_uint64(val, rval, &reason)) < 0){
clicon_err(OE_XML, errno, "parse_bool");
goto done;
}
if (ret == 0){
clicon_err(OE_XML, EINVAL, "%s", reason);
goto done;
}
retval = 0;
done:
if (reason)
free(reason);
return retval;
}