Netconf monitoring RFC 6022 Sessions (https://github.com/clicon/clixon/issues/370)
- statistics and transport/source-host parameters - extended internal NETCONF hello with transport and source-host attributes clixon-lib,yang - Moved all extended internal NETCONF attributes to the clicon-lib namespace C-API: - wrapped most attribute creation into new fn xml_add_attr()
This commit is contained in:
parent
7558d40faa
commit
3916fa919c
36 changed files with 883 additions and 402 deletions
|
|
@ -78,7 +78,7 @@
|
|||
*/
|
||||
static struct client_entry *
|
||||
ce_find_byid(struct client_entry *ce_list,
|
||||
uint32_t id)
|
||||
uint32_t id)
|
||||
{
|
||||
struct client_entry *ce;
|
||||
|
||||
|
|
@ -154,6 +154,81 @@ release_all_dbs(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Get backend-specific client netconf monitoring state
|
||||
*
|
||||
* Backend-specific netconf monitoring state is:
|
||||
* sessions
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[in] xpath XML Xpath
|
||||
* @param[in] nsc XML Namespace context for xpath
|
||||
* @param[in,out] xret Existing XML tree, merge x into this
|
||||
* @param[out] xerr XML error tree, if retval = 0
|
||||
* @retval -1 Error (fatal)
|
||||
* @retval 0 Statedata callback failed, error in xerr
|
||||
* @retval 1 OK
|
||||
* @see RFC 6022
|
||||
*/
|
||||
int
|
||||
backend_monitoring_state_get(clicon_handle h,
|
||||
yang_stmt *yspec,
|
||||
char *xpath,
|
||||
cvec *nsc,
|
||||
cxobj **xret,
|
||||
cxobj **xerr)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
struct client_entry *ce;
|
||||
char timestr[28];
|
||||
int ret;
|
||||
|
||||
if ((cb = cbuf_new()) ==NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "<netconf-state xmlns=\"%s\">", NETCONF_MONITORING_NAMESPACE);
|
||||
cprintf(cb, "<sessions>");
|
||||
for (ce = backend_client_list(h); ce; ce = ce->ce_next){
|
||||
cprintf(cb, "<session>");
|
||||
cprintf(cb, "<session-id>%u</session-id>", ce->ce_id);
|
||||
if (ce->ce_transport)
|
||||
cprintf(cb, "<transport xmlns:%s=\"%s\">%s</transport>",
|
||||
CLIXON_LIB_PREFIX, CLIXON_LIB_NS,
|
||||
ce->ce_transport);
|
||||
cprintf(cb, "<username>%s</username>", ce->ce_username);
|
||||
if (ce->ce_source_host)
|
||||
cprintf(cb, "<source-host>%s</source-host>", ce->ce_source_host);
|
||||
if (ce->ce_time.tv_sec != 0){
|
||||
if (time2str(ce->ce_time, timestr, sizeof(timestr)) < 0){
|
||||
clicon_err(OE_UNIX, errno, "time2str");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "<login-time>%s</login-time>", timestr);
|
||||
}
|
||||
cprintf(cb, "<in-rpcs>%u</in-rpcs>", ce->ce_in_rpcs);
|
||||
cprintf(cb, "<in-bad-rpcs>%u</in-bad-rpcs>", ce->ce_in_bad_rpcs);
|
||||
cprintf(cb, "<out-rpc-errors>%u</out-rpc-errors>", ce->ce_out_rpc_errors);
|
||||
cprintf(cb, "<out-notifications>%u</out-notifications>", 0);
|
||||
cprintf(cb, "</session>");
|
||||
}
|
||||
cprintf(cb, "</sessions>");
|
||||
cprintf(cb, "</netconf-state>");
|
||||
if ((ret = clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
retval = 1;
|
||||
done:
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Remove client entry state
|
||||
* Close down everything wrt clients (eg sockets, subscriptions)
|
||||
* Finally actually remove client struct in handle
|
||||
|
|
@ -451,7 +526,7 @@ from_client_edit_config(clicon_handle h,
|
|||
xmldb_modified_set(h, target, 1); /* mark as dirty */
|
||||
/* Clixon extension: autocommit */
|
||||
if ((attr = xml_find_value(xn, "autocommit")) != NULL &&
|
||||
strcmp(attr,"true")==0)
|
||||
strcmp(attr,"true") == 0)
|
||||
autocommit = 1;
|
||||
/* If autocommit option is set or requested by client */
|
||||
if (clicon_autocommit(h) || autocommit) {
|
||||
|
|
@ -513,7 +588,9 @@ from_client_edit_config(clicon_handle h,
|
|||
}
|
||||
cprintf(cbret, "<rpc-reply xmlns=\"%s\"><ok", NETCONF_BASE_NAMESPACE);
|
||||
if (clicon_data_get(h, "objectexisted", &val) == 0)
|
||||
cprintf(cbret, " objectexisted=\"%s\"", val);
|
||||
cprintf(cbret, " %s:objectexisted=\"%s\" xmlns:%s=\"%s\"",
|
||||
CLIXON_LIB_PREFIX, val,
|
||||
CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
|
||||
cprintf(cbret, "/></rpc-reply>");
|
||||
ok:
|
||||
retval = 0;
|
||||
|
|
@ -1375,6 +1452,11 @@ from_client_process_control(clicon_handle h,
|
|||
}
|
||||
|
||||
/*! Clixon hello to check liveness
|
||||
*
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] x Incoming XML of hello request
|
||||
* @param[in] ce Client entry (from)
|
||||
* @param[out] cbret Hello reply
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
|
|
@ -1386,11 +1468,24 @@ from_client_hello(clicon_handle h,
|
|||
{
|
||||
int retval = -1;
|
||||
uint32_t id;
|
||||
char *val;
|
||||
|
||||
if (clicon_session_id_get(h, &id) < 0){
|
||||
clicon_err(OE_NETCONF, ENOENT, "session_id not set");
|
||||
goto done;
|
||||
}
|
||||
if ((val = xml_find_type_value(x, "cl", "transport", CX_ATTR)) != NULL){
|
||||
if ((ce->ce_transport = strdup(val)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if ((val = xml_find_type_value(x, "cl", "source-host", CX_ATTR)) != NULL){
|
||||
if ((ce->ce_source_host = strdup(val)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
id++;
|
||||
clicon_session_id_set(h, id);
|
||||
cprintf(cbret, "<hello xmlns=\"%s\"><session-id>%u</session-id></hello>",
|
||||
|
|
@ -1498,7 +1593,7 @@ from_client_msg(clicon_handle h,
|
|||
}
|
||||
|
||||
if (strcmp(rpcname, "rpc") == 0){
|
||||
; /* continue below */
|
||||
ce->ce_in_rpcs++; /* Track all RPCs */
|
||||
}
|
||||
else if (strcmp(rpcname, "hello") == 0){
|
||||
if ((ret = from_client_hello(h, x, ce, cbret)) <0)
|
||||
|
|
@ -1508,9 +1603,12 @@ from_client_msg(clicon_handle h,
|
|||
else{
|
||||
if (netconf_unknown_element(cbret, "protocol", rpcname, "Unrecognized netconf operation")< 0)
|
||||
goto done;
|
||||
ce->ce_in_bad_rpcs++;
|
||||
ce->ce_out_rpc_errors++; /* Number of <rpc-reply> messages sent that contained an <rpc-error> */
|
||||
goto reply;
|
||||
}
|
||||
ce->ce_id = id;
|
||||
|
||||
/* As a side-effect, this expands xt with default values according to "report-all"
|
||||
* This may not be correct, the RFC does not mention expanding default values for
|
||||
* input RPC
|
||||
|
|
@ -1520,6 +1618,8 @@ from_client_msg(clicon_handle h,
|
|||
if (ret == 0){
|
||||
if (clixon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0)
|
||||
goto done;
|
||||
ce->ce_in_bad_rpcs++;
|
||||
ce->ce_in_rpcs--; /* Track all RPCs */
|
||||
goto reply;
|
||||
}
|
||||
xe = NULL;
|
||||
|
|
@ -1531,6 +1631,7 @@ from_client_msg(clicon_handle h,
|
|||
if ((ye = xml_spec(xe)) == NULL){
|
||||
if (netconf_operation_not_supported(cbret, "protocol", rpc) < 0)
|
||||
goto done;
|
||||
ce->ce_out_rpc_errors++;
|
||||
goto reply;
|
||||
}
|
||||
if ((ymod = ys_module(ye)) == NULL){
|
||||
|
|
@ -1557,26 +1658,34 @@ from_client_msg(clicon_handle h,
|
|||
creds = clicon_nacm_credentials(h);
|
||||
if ((ret = verify_nacm_user(h, creds, ce->ce_username, username, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0) /* credentials fail */
|
||||
if (ret == 0){ /* credentials fail */
|
||||
ce->ce_out_rpc_errors++;
|
||||
goto reply;
|
||||
}
|
||||
/* NACM rpc operation exec validation */
|
||||
if ((ret = nacm_rpc(rpc, module, username, xnacm, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0) /* Not permitted and cbret set */
|
||||
if (ret == 0){ /* Not permitted and cbret set */
|
||||
ce->ce_out_rpc_errors++;
|
||||
goto reply;
|
||||
}
|
||||
}
|
||||
clicon_err_reset();
|
||||
if ((ret = rpc_callback_call(h, xe, ce, &nr, cbret)) < 0){
|
||||
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
|
||||
goto done;
|
||||
clicon_log(LOG_NOTICE, "%s Error in rpc_callback_call:%s", __FUNCTION__, xml_name(xe));
|
||||
ce->ce_out_rpc_errors++;
|
||||
goto reply; /* Dont quit here on user callbacks */
|
||||
}
|
||||
if (ret == 0)
|
||||
if (ret == 0){
|
||||
ce->ce_out_rpc_errors++;
|
||||
goto reply;
|
||||
}
|
||||
if (nr == 0){ /* not handled by callback */
|
||||
if (netconf_operation_not_supported(cbret, "application", "RPC operation not supported")< 0)
|
||||
goto done;
|
||||
ce->ce_out_rpc_errors++;
|
||||
goto reply;
|
||||
}
|
||||
if (xnacm){
|
||||
|
|
|
|||
|
|
@ -41,24 +41,33 @@
|
|||
* Types
|
||||
*/
|
||||
/*
|
||||
* Client entry.
|
||||
* Backend client entry.
|
||||
* Keep state about every connected client.
|
||||
* References from RFC 6022, ietf-netconf-monitoring.yang sessions container
|
||||
*/
|
||||
struct client_entry{
|
||||
struct client_entry *ce_next; /* The clients linked list */
|
||||
struct sockaddr ce_addr; /* The clients (UNIX domain) address */
|
||||
int ce_s; /* stream socket to client */
|
||||
int ce_nr; /* Client number (for dbg/tracing) */
|
||||
int ce_stat_in; /* Nr of received msgs from client */
|
||||
int ce_stat_out;/* Nr of sent msgs to client */
|
||||
uint32_t ce_id; /* Session id, accessor functions: clicon_session_id_get/set */
|
||||
char *ce_username;/* Translated from peer user cred */
|
||||
clicon_handle ce_handle; /* clicon config handle (all clients have same?) */
|
||||
char *ce_transport; /* Identifies the transport for each session.
|
||||
Clixon-lib.yang extends these values by prefixing with
|
||||
"cl:", where cl is ensured to be declared ie by
|
||||
netconf-monitoring state */
|
||||
char *ce_source_host; /* Host identifier of the NETCONF client */
|
||||
struct timeval ce_time; /* Time at the server at which the session was established. */
|
||||
uint32_t ce_in_rpcs ; /* Number of correct <rpc> messages received. */
|
||||
uint32_t ce_in_bad_rpcs; /* Not correct <rpc> messages */
|
||||
uint32_t ce_out_rpc_errors; /* <rpc-error> messages*/
|
||||
};
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int backend_monitoring_state_get(clicon_handle h, yang_stmt *yspec, char *xpath, cvec *nsc, cxobj **xret, cxobj **xerr);
|
||||
int backend_client_rm(clicon_handle h, struct client_entry *ce);
|
||||
int from_client(int fd, void *arg);
|
||||
int backend_rpc_init(clicon_handle h);
|
||||
|
|
|
|||
|
|
@ -181,10 +181,22 @@ client_get_streams(clicon_handle h,
|
|||
* @param[in] xpath XPath selection, may be used to filter early
|
||||
* @param[in] nsc XML Namespace context for xpath
|
||||
* @param[in] wdef With-defaults parameter, see RFC 6243
|
||||
* @param[in,out] xret Existing XML tree, merge x into this
|
||||
* @param[in,out] xret Existing XML tree, merge x into this, or rpc-error
|
||||
* @retval -1 Error (fatal)
|
||||
* @retval 0 Statedata callback failed (clicon_err called)
|
||||
* @retval 0 Statedata callback failed (error in xret)
|
||||
* @retval 1 OK
|
||||
* @note This code in general does not look at xpath, needs to be filtered in retrospect
|
||||
* @note Awkward error handling. Even if most of this is during development phase, except for plugin
|
||||
* state callbacks.
|
||||
* Present behavior:
|
||||
* - Present behavior: should be returned in xret with retval 0(error) or 1(ok)
|
||||
* - On error, previous content of xret is not freed
|
||||
* - xret is in turn translated to cbuf in calling function
|
||||
* Instead, I think there should be a second out argument **xerr with the error message, see code
|
||||
* for CLICON_NETCONF_MONITORING which is transformed in calling function(?) to an internal error
|
||||
* message. But this needs to be explored in all sub-functions
|
||||
*
|
||||
*
|
||||
*/
|
||||
static int
|
||||
get_client_statedata(clicon_handle h,
|
||||
|
|
@ -196,9 +208,11 @@ get_client_statedata(clicon_handle h,
|
|||
int retval = -1;
|
||||
yang_stmt *yspec;
|
||||
yang_stmt *ymod;
|
||||
cxobj *x1 = NULL;
|
||||
int ret;
|
||||
char *namespace;
|
||||
cbuf *cb = NULL;
|
||||
cxobj *xerr = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
|
|
@ -236,7 +250,6 @@ get_client_statedata(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
cbuf_reset(cb);
|
||||
/* XXX This code does not filter state data with xpath */
|
||||
cprintf(cb, "<restconf-state xmlns=\"%s\"/>", namespace);
|
||||
if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, NULL) < 0)
|
||||
goto done;
|
||||
|
|
@ -254,7 +267,32 @@ get_client_statedata(clicon_handle h,
|
|||
goto fail;
|
||||
}
|
||||
if (clicon_option_bool(h, "CLICON_NETCONF_MONITORING")){
|
||||
if ((ret = netconf_monitoring_state_get(h, yspec, xpath, nsc, 0, xret)) < 0)
|
||||
if ((ret = netconf_monitoring_state_get(h, yspec, xpath, nsc, xret, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
if (clixon_netconf_internal_error(xerr, " . Internal error, netconf_monitoring_state returned invalid XML", NULL) < 0)
|
||||
goto done;
|
||||
if (*xret)
|
||||
xml_free(*xret);
|
||||
*xret = xerr;
|
||||
xerr = NULL;
|
||||
goto fail;
|
||||
}
|
||||
/* Some state, client state, is avaliable in backend only, not in lib
|
||||
* Needs merge since same subtree as previous lib state
|
||||
*/
|
||||
if ((ret = backend_monitoring_state_get(h, yspec, xpath, nsc, &x1, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
if (clixon_netconf_internal_error(xerr, " . Internal error, baenckend_monitoring_state_get returned invalid XML", NULL) < 0)
|
||||
goto done;
|
||||
if (*xret)
|
||||
xml_free(*xret);
|
||||
*xret = xerr;
|
||||
xerr = NULL;
|
||||
goto fail;
|
||||
}
|
||||
if ((ret = netconf_trymerge(x1, yspec, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -320,6 +358,10 @@ get_client_statedata(clicon_handle h,
|
|||
retval = 1; /* OK */
|
||||
done:
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
||||
if (xerr)
|
||||
xml_free(xerr);
|
||||
if (x1)
|
||||
xml_free(x1);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
|
|
@ -734,19 +776,13 @@ get_list_pagination(clicon_handle h,
|
|||
cbuf *cba = NULL;
|
||||
|
||||
/* Add remaining attribute */
|
||||
if ((xa = xml_new("remaining", x1, CX_ATTR)) == NULL)
|
||||
goto done;
|
||||
if ((cba = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cba, "%u", remaining);
|
||||
if (xml_value_set(xa, cbuf_get(cba)) < 0)
|
||||
goto done;
|
||||
if (xml_prefix_set(xa, "cp") < 0)
|
||||
goto done;
|
||||
if (xmlns_set(x1, "cp", "http://clicon.org/clixon-netconf-list-pagination") < 0)
|
||||
goto done;
|
||||
if (xml_add_attr(x1, "remaining", cbuf_get(cba), "cp", "http://clicon.org/clixon-netconf-list-pagination") < 0)
|
||||
goto done;
|
||||
if (cba)
|
||||
cbuf_free(cba);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,7 +249,6 @@ backend_accept_client(int fd,
|
|||
}
|
||||
if ((ce = backend_client_add(h, &from)) == NULL)
|
||||
goto done;
|
||||
ce->ce_handle = h;
|
||||
|
||||
/*
|
||||
* Get credentials of connected peer - only for unix socket
|
||||
|
|
|
|||
|
|
@ -145,6 +145,8 @@ backend_client_add(clicon_handle h,
|
|||
ce->ce_nr = bh->bh_ce_nr++; /* Session-id ? */
|
||||
memcpy(&ce->ce_addr, addr, sizeof(*addr));
|
||||
ce->ce_next = bh->bh_ce_list;
|
||||
ce->ce_handle = h;
|
||||
gettimeofday(&ce->ce_time, NULL);
|
||||
bh->bh_ce_list = ce;
|
||||
return ce;
|
||||
}
|
||||
|
|
@ -180,6 +182,10 @@ backend_client_delete(clicon_handle h,
|
|||
*ce_prev = c->ce_next;
|
||||
if (ce->ce_username)
|
||||
free(ce->ce_username);
|
||||
if (ce->ce_transport)
|
||||
free(ce->ce_transport);
|
||||
if (ce->ce_source_host)
|
||||
free(ce->ce_source_host);
|
||||
free(ce);
|
||||
break;
|
||||
}
|
||||
|
|
@ -203,8 +209,9 @@ backend_client_print(clicon_handle h,
|
|||
fprintf(f, "Client: %d\n", ce->ce_nr);
|
||||
fprintf(f, " Session: %d\n", ce->ce_id);
|
||||
fprintf(f, " Socket: %d\n", ce->ce_s);
|
||||
fprintf(f, " Msgs in: %d\n", ce->ce_stat_in);
|
||||
fprintf(f, " Msgs out: %d\n", ce->ce_stat_out);
|
||||
fprintf(f, " RPCs in: %u\n", ce->ce_in_rpcs);
|
||||
fprintf(f, " Bad RPCs in: %u\n", ce->ce_in_bad_rpcs);
|
||||
fprintf(f, " Err RPCs out: %u\n", ce->ce_out_rpc_errors);
|
||||
fprintf(f, " Username: %s\n", ce->ce_username);
|
||||
}
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -294,7 +294,6 @@ cli_dbxml(clicon_handle h,
|
|||
cxobj *xbot = NULL; /* xpath, NULL if datastore */
|
||||
yang_stmt *y = NULL; /* yang spec of xpath */
|
||||
cxobj *xtop = NULL; /* xpath root */
|
||||
cxobj *xa; /* attribute */
|
||||
cxobj *xerr = NULL;
|
||||
int ret;
|
||||
cg_var *cv;
|
||||
|
|
@ -338,11 +337,7 @@ cli_dbxml(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
}
|
||||
if ((xa = xml_new("operation", xbot, CX_ATTR)) == NULL)
|
||||
goto done;
|
||||
if (xml_prefix_set(xa, NETCONF_BASE_PREFIX) < 0)
|
||||
goto done;
|
||||
if (xml_value_set(xa, xml_operation2str(op)) < 0)
|
||||
if (xml_add_attr(xbot, "operation", xml_operation2str(op), NETCONF_BASE_PREFIX, NULL) < 0)
|
||||
goto done;
|
||||
/* Add body last in case of leaf */
|
||||
if (cvec_len(cvv) > 1 &&
|
||||
|
|
|
|||
|
|
@ -818,7 +818,12 @@ main(int argc,
|
|||
if (evalresult < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Set RFC6022 session parameters that will be sent in first hello,
|
||||
* @see clicon_hello_req
|
||||
*/
|
||||
clicon_data_set(h, "session-transport", "cl:cli");
|
||||
clicon_data_set(h, "session-source-host", "localhost");
|
||||
|
||||
/* Go into event-loop unless -1 command-line */
|
||||
if (!once){
|
||||
retval = cli_interactive(h);
|
||||
|
|
|
|||
|
|
@ -110,6 +110,15 @@ netconf_add_request_attr(cxobj *xrpc,
|
|||
/* If attribute already exists, dont copy it */
|
||||
if (xml_find_type(xrep, NULL, xml_name(xa), CX_ATTR) != NULL)
|
||||
continue; /* Skip already present (dont overwrite) */
|
||||
/* Filter all clixon-lib attributes and namespace declaration
|
||||
* to acvoid leaking internal attributes to external NETCONF
|
||||
* note this is only done on top-level.
|
||||
*/
|
||||
if (xml_prefix(xa) && strcmp(xml_prefix(xa), CLIXON_LIB_PREFIX) == 0)
|
||||
continue;
|
||||
if (xml_prefix(xa) && strcmp(xml_prefix(xa), "xmlns") == 0 &&
|
||||
strcmp(xml_name(xa), CLIXON_LIB_PREFIX) == 0)
|
||||
continue;
|
||||
if ((xa2 = xml_dup(xa)) ==NULL)
|
||||
goto done;
|
||||
if (xml_addsub(xrep, xa2) < 0)
|
||||
|
|
@ -192,11 +201,11 @@ netconf_rpc_message(clicon_handle h,
|
|||
yang_stmt *yspec,
|
||||
int *eof)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xret = NULL; /* Return (out) */
|
||||
int ret;
|
||||
cbuf *cbret = NULL;
|
||||
cxobj *xc;
|
||||
int retval = -1;
|
||||
cxobj *xret = NULL; /* Return (out) */
|
||||
int ret;
|
||||
cbuf *cbret = NULL;
|
||||
cxobj *xc;
|
||||
netconf_framing_type framing;
|
||||
|
||||
framing = clicon_option_int(h, "netconf-framing");
|
||||
|
|
@ -240,9 +249,9 @@ netconf_rpc_message(clicon_handle h,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if (netconf_rpc_dispatch(h, xrpc, &xret, eof) < 0){
|
||||
if (netconf_rpc_dispatch(h, xrpc, &xret, eof) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Is there a return message in xret? */
|
||||
if (xret == NULL){
|
||||
if (netconf_operation_failed_xml(&xret, "rpc", "Internal error: no xml return")< 0)
|
||||
|
|
@ -988,7 +997,7 @@ main(int argc,
|
|||
* used by the client, even though new TCP sessions are created for
|
||||
* each message sent to the backend.
|
||||
*/
|
||||
if (clicon_hello_req(h, &id) < 0)
|
||||
if (clicon_hello_req(h, "cl:netconf", NULL, &id) < 0)
|
||||
goto done;
|
||||
clicon_session_id_set(h, id);
|
||||
|
||||
|
|
|
|||
|
|
@ -704,9 +704,7 @@ netconf_rpc_dispatch(clicon_handle h,
|
|||
* It may even be wrong if something else is done with the incoming message?
|
||||
*/
|
||||
if ((username = clicon_username_get(h)) != NULL){
|
||||
if ((xa = xml_new("username", xn, CX_ATTR)) == NULL)
|
||||
goto done;
|
||||
if (xml_value_set(xa, username) < 0)
|
||||
if (xml_add_attr(xn, "username", username, CLIXON_LIB_PREFIX, CLIXON_LIB_NS) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Many of these calls are now calling generic clicon_rpc_netconf_xml
|
||||
|
|
|
|||
|
|
@ -405,7 +405,6 @@ restconf_insert_attributes(cxobj *xdata,
|
|||
cvec *qvec)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xa;
|
||||
char *instr;
|
||||
char *pstr;
|
||||
yang_stmt *y;
|
||||
|
|
@ -422,12 +421,7 @@ restconf_insert_attributes(cxobj *xdata,
|
|||
/* First add xmlns:yang attribute */
|
||||
if (xmlns_set(xdata, "yang", YANG_XML_NAMESPACE) < 0)
|
||||
goto done;
|
||||
/* Then add insert attribute */
|
||||
if ((xa = xml_new("insert", xdata, CX_ATTR)) == NULL)
|
||||
goto done;
|
||||
if (xml_prefix_set(xa, "yang") < 0)
|
||||
goto done;
|
||||
if (xml_value_set(xa, instr) < 0)
|
||||
if (xml_add_attr(xdata, "insert", instr, "yang", NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
if ((pstr = cvec_find_str(qvec, "point")) != NULL){
|
||||
|
|
@ -439,11 +433,7 @@ restconf_insert_attributes(cxobj *xdata,
|
|||
attrname="key";
|
||||
else
|
||||
attrname="value";
|
||||
/* Then add value/key attribute */
|
||||
if ((xa = xml_new(attrname, xdata, CX_ATTR)) == NULL)
|
||||
goto done;
|
||||
if (xml_prefix_set(xa, "yang") < 0)
|
||||
goto done;
|
||||
|
||||
if ((ret = api_path2xpath(pstr, ys_spec(y), &xpath, &nsc, NULL)) < 0)
|
||||
goto done;
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
|
|
@ -471,7 +461,7 @@ restconf_insert_attributes(cxobj *xdata,
|
|||
p++;
|
||||
cprintf(cb, "%s", p);
|
||||
}
|
||||
if (xml_value_set(xa, cbuf_get(cb)) < 0)
|
||||
if (xml_add_attr(xdata, attrname, cbuf_get(cb), "yang", NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Add prefix/namespaces used in attributes */
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ restconf_main_config(clicon_handle h,
|
|||
else {
|
||||
/* Loop to wait for backend starting, try again if not done */
|
||||
while (1){
|
||||
if (clicon_hello_req(h, &id) < 0){
|
||||
if (clicon_hello_req(h, "cl:restconf", NULL, &id) < 0){
|
||||
if (errno == ENOENT){
|
||||
fprintf(stderr, "waiting");
|
||||
sleep(1);
|
||||
|
|
|
|||
|
|
@ -634,7 +634,7 @@ restconf_clixon_backend(clicon_handle h,
|
|||
|
||||
/* Loop to wait for backend starting, try again if not done */
|
||||
while (1){
|
||||
if (clicon_hello_req(h, &id) < 0){
|
||||
if (clicon_hello_req(h, "cl:restconf", NULL, &id) < 0){
|
||||
if (errno == ENOENT){
|
||||
fprintf(stderr, "waiting");
|
||||
sleep(1);
|
||||
|
|
|
|||
|
|
@ -384,24 +384,12 @@ api_data_write(clicon_handle h,
|
|||
/* Add operation create as attribute. If that fails with Conflict, then
|
||||
* try "replace" (see comment in function header)
|
||||
*/
|
||||
if ((xa = xml_new("operation", xdata, CX_ATTR)) == NULL)
|
||||
if (xml_add_attr(xdata, "operation", xml_operation2str(op), NETCONF_BASE_PREFIX, NULL) < 0)
|
||||
goto done;
|
||||
if (xml_prefix_set(xa, NETCONF_BASE_PREFIX) < 0)
|
||||
if (xml_add_attr(xdata, "objectcreate",
|
||||
plain_patch?"false":"true",
|
||||
CLIXON_LIB_PREFIX, CLIXON_LIB_NS) < 0)
|
||||
goto done;
|
||||
if (xml_value_set(xa, xml_operation2str(op)) < 0) /* XXX here is where op is used */
|
||||
goto done;
|
||||
if ((xa = xml_new("objectcreate", xdata, CX_ATTR)) == NULL)
|
||||
goto done;
|
||||
if (plain_patch){
|
||||
/* RFC 8040 4.6. PATCH:
|
||||
* If the target resource instance does not exist, the server MUST NOT create it.
|
||||
*/
|
||||
if (xml_value_set(xa, "false") < 0)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
if (xml_value_set(xa, "true") < 0)
|
||||
goto done;
|
||||
/* Top-of tree, no api-path
|
||||
* Replace xparent with x, ie bottom of api-path with data
|
||||
*/
|
||||
|
|
@ -512,16 +500,20 @@ api_data_write(clicon_handle h,
|
|||
} /* api-path != NULL */
|
||||
/* For internal XML protocol: add username attribute for access control
|
||||
*/
|
||||
username = clicon_username_get(h);
|
||||
/* Create text buffer for transfer to backend */
|
||||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cbx, "<rpc xmlns=\"%s\" username=\"%s\" xmlns:%s=\"%s\" %s>",
|
||||
NETCONF_BASE_NAMESPACE,
|
||||
username?username:"",
|
||||
cprintf(cbx, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
|
||||
cprintf(cbx, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
|
||||
if ((username = clicon_username_get(h)) != NULL){
|
||||
cprintf(cbx, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
|
||||
cprintf(cbx, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
|
||||
}
|
||||
cprintf(cbx, " xmlns:%s=\"%s\"",
|
||||
NETCONF_BASE_PREFIX,
|
||||
NETCONF_BASE_NAMESPACE, /* bind nc to netconf namespace */
|
||||
NETCONF_MESSAGE_ID_ATTR);
|
||||
NETCONF_BASE_NAMESPACE); /* bind nc to netconf namespace */
|
||||
cprintf(cbx, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
|
||||
cprintf(cbx, ">");
|
||||
cprintf(cbx, "<edit-config");
|
||||
/* RFC8040 Sec 1.4:
|
||||
* If this is a "data" request and the NETCONF server supports :startup,
|
||||
|
|
@ -532,9 +524,11 @@ api_data_write(clicon_handle h,
|
|||
if ((IETF_DS_NONE == ds) &&
|
||||
if_feature(yspec, "ietf-netconf", "startup") &&
|
||||
!clicon_option_bool(h, "CLICON_RESTCONF_STARTUP_DONTUPDATE")){
|
||||
cprintf(cbx, " copystartup=\"true\"");
|
||||
cprintf(cbx, " %s:copystartup=\"true\"", CLIXON_LIB_PREFIX);
|
||||
cprintf(cbx, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
|
||||
}
|
||||
cprintf(cbx, " autocommit=\"true\"");
|
||||
cprintf(cbx, " %s:autocommit=\"true\" xmlns:%s=\"%s\"",
|
||||
CLIXON_LIB_PREFIX, CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
|
||||
cprintf(cbx, "><target><candidate /></target>");
|
||||
cprintf(cbx, "<default-operation>none</default-operation>");
|
||||
if (clixon_xml2cbuf(cbx, xtop, 0, 0, -1, 0) < 0)
|
||||
|
|
@ -759,14 +753,17 @@ api_data_delete(clicon_handle h,
|
|||
goto done;
|
||||
/* For internal XML protocol: add username attribute for access control
|
||||
*/
|
||||
username = clicon_username_get(h);
|
||||
cprintf(cbx, "<rpc xmlns=\"%s\" username=\"%s\" xmlns:%s=\"%s\" %s>",
|
||||
NETCONF_BASE_NAMESPACE,
|
||||
username?username:"",
|
||||
cprintf(cbx, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
|
||||
cprintf(cbx, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
|
||||
if ((username = clicon_username_get(h)) != NULL){
|
||||
cprintf(cbx, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
|
||||
cprintf(cbx, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
|
||||
}
|
||||
cprintf(cbx, " xmlns:%s=\"%s\"",
|
||||
NETCONF_BASE_PREFIX,
|
||||
NETCONF_BASE_NAMESPACE,
|
||||
NETCONF_MESSAGE_ID_ATTR); /* bind nc to netconf namespace */
|
||||
|
||||
NETCONF_BASE_NAMESPACE);
|
||||
cprintf(cbx, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
|
||||
cprintf(cbx, ">");
|
||||
cprintf(cbx, "<edit-config");
|
||||
/* RFC8040 Sec 1.4:
|
||||
* If this is a "data" request and the NETCONF server supports :startup,
|
||||
|
|
@ -777,9 +774,11 @@ api_data_delete(clicon_handle h,
|
|||
if ((IETF_DS_NONE == ds) &&
|
||||
if_feature(yspec, "ietf-netconf", "startup") &&
|
||||
!clicon_option_bool(h, "CLICON_RESTCONF_STARTUP_DONTUPDATE")){
|
||||
cprintf(cbx, " copystartup=\"true\"");
|
||||
cprintf(cbx, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
|
||||
cprintf(cbx, " %s:copystartup=\"true\"", CLIXON_LIB_PREFIX);
|
||||
}
|
||||
cprintf(cbx, " autocommit=\"true\"");
|
||||
cprintf(cbx, " %s:autocommit=\"true\" xmlns:%s=\"%s\"",
|
||||
CLIXON_LIB_PREFIX, CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
|
||||
cprintf(cbx, "><target><candidate /></target>");
|
||||
cprintf(cbx, "<default-operation>none</default-operation>");
|
||||
if (clixon_xml2cbuf(cbx, xtop, 0, 0, -1, 0) < 0)
|
||||
|
|
|
|||
|
|
@ -337,14 +337,15 @@ api_data_post(clicon_handle h,
|
|||
}
|
||||
/* For internal XML protocol: add username attribute for access control
|
||||
*/
|
||||
username = clicon_username_get(h);
|
||||
cprintf(cbx, "<rpc xmlns=\"%s\" username=\"%s\" xmlns:%s=\"%s\" %s>",
|
||||
NETCONF_BASE_NAMESPACE,
|
||||
username?username:"",
|
||||
NETCONF_BASE_PREFIX,
|
||||
NETCONF_BASE_NAMESPACE,
|
||||
NETCONF_MESSAGE_ID_ATTR); /* bind nc to netconf namespace */
|
||||
|
||||
cprintf(cbx, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
|
||||
if ((username = clicon_username_get(h)) != NULL){
|
||||
cprintf(cbx, " %s:username=\"%s\" xmlns:%s=\"%s\"",
|
||||
NETCONF_BASE_PREFIX,
|
||||
username?username:"",
|
||||
NETCONF_BASE_PREFIX,
|
||||
NETCONF_BASE_NAMESPACE);
|
||||
}
|
||||
cprintf(cbx, " %s>", NETCONF_MESSAGE_ID_ATTR); /* bind nc to netconf namespace */
|
||||
cprintf(cbx, "<edit-config");
|
||||
/* RFC8040 Sec 1.4:
|
||||
* If this is a "data" request and the NETCONF server supports :startup,
|
||||
|
|
@ -355,9 +356,11 @@ api_data_post(clicon_handle h,
|
|||
if ((IETF_DS_NONE == ds) &&
|
||||
if_feature(yspec, "ietf-netconf", "startup") &&
|
||||
!clicon_option_bool(h, "CLICON_RESTCONF_STARTUP_DONTUPDATE")){
|
||||
cprintf(cbx, " copystartup=\"true\"");
|
||||
cprintf(cbx, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
|
||||
cprintf(cbx, " %s:copystartup=\"true\"", CLIXON_LIB_PREFIX);
|
||||
}
|
||||
cprintf(cbx, " autocommit=\"true\"");
|
||||
cprintf(cbx, " %s:autocommit=\"true\" xmlns:%s=\"%s\"",
|
||||
CLIXON_LIB_PREFIX, CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
|
||||
cprintf(cbx, "><target><candidate /></target>");
|
||||
cprintf(cbx, "<default-operation>none</default-operation>");
|
||||
if (clixon_xml2cbuf(cbx, xtop, 0, 0, -1, 0) < 0)
|
||||
|
|
|
|||
32
apps/snmp/README.md
Normal file
32
apps/snmp/README.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
SNMP
|
||||
====
|
||||
|
||||
The SNMP frontend acts as an intermediate daemon between the Net-SNMP
|
||||
daemon (snmpd) and the Clixon backend. Clixon-snmp communicates over the AgentX
|
||||
protocol to snmpd typically via a UNIX socket, and over the internal IPC protocol to the Clixon
|
||||
backend.
|
||||
|
||||
Use Net-SNMP version 5.9 or later
|
||||
|
||||
To set up AgentX communication between ``clixon_snmp`` and ``snmpd`` a
|
||||
Unix or TCP socket is configured. This socket is also configured in
|
||||
Clixon (see below). An example `/etc/snmpd/snmpd.conf` is as follows::
|
||||
|
||||
master agentx
|
||||
agentaddress 127.0.0.1,[::1]
|
||||
rwcommunity public localhost
|
||||
agentXSocket unix:/var/run/snmp.sock
|
||||
agentxperms 777 777
|
||||
|
||||
It is necessary to ensure snmpd does `not` to load modules
|
||||
implemented by Clixon. For example, if Clixon implements the IF-MIB and
|
||||
system MIBs, snmpd should not load those modules. This can be done
|
||||
using the "-I" flag and prepending a "-" before each module::
|
||||
|
||||
-I -ifTable -I -system_mib -I -sysORTable
|
||||
|
||||
Net-snmp must be started via systemd or some other external mechanism before clixon_snmp is started.
|
||||
|
||||
To build the snmp support, netsnmp is enabled at configure time. Two configure options are added for SNMP:
|
||||
* ``--enable-netsnmp`` Enable SNMP support.
|
||||
* ``--with-mib-generated-yang-dir`` For tests: Directory of generated YANG specs (default: $prefix/share/mibyang)
|
||||
|
|
@ -525,7 +525,7 @@ main(int argc,
|
|||
* used by the client, even though new TCP sessions are created for
|
||||
* each message sent to the backend.
|
||||
*/
|
||||
if (clicon_hello_req(h, &id) < 0)
|
||||
if (clicon_hello_req(h, "cl:snmp", "localhost", &id) < 0)
|
||||
goto done;
|
||||
clicon_session_id_set(h, id);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue