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:
Olof hagsand 2023-01-14 11:25:39 +01:00
parent 7558d40faa
commit 3916fa919c
36 changed files with 883 additions and 402 deletions

View file

@ -44,8 +44,8 @@ Expected: beginning of 2023
### New features ### New features
* Netconf monitoring, part 2 * Netconf monitoring, part 2
* Datastores * Datastores and sessions
* Remaining: sessions and statistics state * Remaining: statistics state
* Standards * Standards
* RFC 6022 "YANG Module for NETCONF Monitoring" * RFC 6022 "YANG Module for NETCONF Monitoring"
* See [Feature Request: Support RFC 6022 (NETCONF Monitoring)](https://github.com/clicon/clixon/issues/370) * See [Feature Request: Support RFC 6022 (NETCONF Monitoring)](https://github.com/clicon/clixon/issues/370)
@ -54,6 +54,9 @@ Expected: beginning of 2023
Users may have to change how they access the system Users may have to change how they access the system
* clixon-lib,yang
* Moved all extended internal NETCONF attributes to the clicon-lib namespace
* The internal attributes are documented in https://clixon-docs.readthedocs.io/en/latest/netconf.html
* With-defaults default retrieval mode has changed from `REPORT-ALL` to `EXPLICIT` * With-defaults default retrieval mode has changed from `REPORT-ALL` to `EXPLICIT`
* This means that all get operations without `with-defaults` parameter do no longer * This means that all get operations without `with-defaults` parameter do no longer
return implicit default values, only explicitly set values. return implicit default values, only explicitly set values.

View file

@ -78,7 +78,7 @@
*/ */
static struct client_entry * static struct client_entry *
ce_find_byid(struct client_entry *ce_list, ce_find_byid(struct client_entry *ce_list,
uint32_t id) uint32_t id)
{ {
struct client_entry *ce; struct client_entry *ce;
@ -154,6 +154,81 @@ release_all_dbs(clicon_handle h,
return retval; 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 /*! Remove client entry state
* Close down everything wrt clients (eg sockets, subscriptions) * Close down everything wrt clients (eg sockets, subscriptions)
* Finally actually remove client struct in handle * 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 */ xmldb_modified_set(h, target, 1); /* mark as dirty */
/* Clixon extension: autocommit */ /* Clixon extension: autocommit */
if ((attr = xml_find_value(xn, "autocommit")) != NULL && if ((attr = xml_find_value(xn, "autocommit")) != NULL &&
strcmp(attr,"true")==0) strcmp(attr,"true") == 0)
autocommit = 1; autocommit = 1;
/* If autocommit option is set or requested by client */ /* If autocommit option is set or requested by client */
if (clicon_autocommit(h) || autocommit) { 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); cprintf(cbret, "<rpc-reply xmlns=\"%s\"><ok", NETCONF_BASE_NAMESPACE);
if (clicon_data_get(h, "objectexisted", &val) == 0) 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>"); cprintf(cbret, "/></rpc-reply>");
ok: ok:
retval = 0; retval = 0;
@ -1375,6 +1452,11 @@ from_client_process_control(clicon_handle h,
} }
/*! Clixon hello to check liveness /*! 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 0 OK
* @retval -1 Error * @retval -1 Error
*/ */
@ -1386,11 +1468,24 @@ from_client_hello(clicon_handle h,
{ {
int retval = -1; int retval = -1;
uint32_t id; uint32_t id;
char *val;
if (clicon_session_id_get(h, &id) < 0){ if (clicon_session_id_get(h, &id) < 0){
clicon_err(OE_NETCONF, ENOENT, "session_id not set"); clicon_err(OE_NETCONF, ENOENT, "session_id not set");
goto done; 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++; id++;
clicon_session_id_set(h, id); clicon_session_id_set(h, id);
cprintf(cbret, "<hello xmlns=\"%s\"><session-id>%u</session-id></hello>", 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){ if (strcmp(rpcname, "rpc") == 0){
; /* continue below */ ce->ce_in_rpcs++; /* Track all RPCs */
} }
else if (strcmp(rpcname, "hello") == 0){ else if (strcmp(rpcname, "hello") == 0){
if ((ret = from_client_hello(h, x, ce, cbret)) <0) if ((ret = from_client_hello(h, x, ce, cbret)) <0)
@ -1508,9 +1603,12 @@ from_client_msg(clicon_handle h,
else{ else{
if (netconf_unknown_element(cbret, "protocol", rpcname, "Unrecognized netconf operation")< 0) if (netconf_unknown_element(cbret, "protocol", rpcname, "Unrecognized netconf operation")< 0)
goto done; 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; goto reply;
} }
ce->ce_id = id; ce->ce_id = id;
/* As a side-effect, this expands xt with default values according to "report-all" /* 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 * This may not be correct, the RFC does not mention expanding default values for
* input RPC * input RPC
@ -1520,6 +1618,8 @@ from_client_msg(clicon_handle h,
if (ret == 0){ if (ret == 0){
if (clixon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0) if (clixon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0)
goto done; goto done;
ce->ce_in_bad_rpcs++;
ce->ce_in_rpcs--; /* Track all RPCs */
goto reply; goto reply;
} }
xe = NULL; xe = NULL;
@ -1531,6 +1631,7 @@ from_client_msg(clicon_handle h,
if ((ye = xml_spec(xe)) == NULL){ if ((ye = xml_spec(xe)) == NULL){
if (netconf_operation_not_supported(cbret, "protocol", rpc) < 0) if (netconf_operation_not_supported(cbret, "protocol", rpc) < 0)
goto done; goto done;
ce->ce_out_rpc_errors++;
goto reply; goto reply;
} }
if ((ymod = ys_module(ye)) == NULL){ if ((ymod = ys_module(ye)) == NULL){
@ -1557,26 +1658,34 @@ from_client_msg(clicon_handle h,
creds = clicon_nacm_credentials(h); creds = clicon_nacm_credentials(h);
if ((ret = verify_nacm_user(h, creds, ce->ce_username, username, cbret)) < 0) if ((ret = verify_nacm_user(h, creds, ce->ce_username, username, cbret)) < 0)
goto done; goto done;
if (ret == 0) /* credentials fail */ if (ret == 0){ /* credentials fail */
ce->ce_out_rpc_errors++;
goto reply; goto reply;
}
/* NACM rpc operation exec validation */ /* NACM rpc operation exec validation */
if ((ret = nacm_rpc(rpc, module, username, xnacm, cbret)) < 0) if ((ret = nacm_rpc(rpc, module, username, xnacm, cbret)) < 0)
goto done; 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; goto reply;
}
} }
clicon_err_reset(); clicon_err_reset();
if ((ret = rpc_callback_call(h, xe, ce, &nr, cbret)) < 0){ if ((ret = rpc_callback_call(h, xe, ce, &nr, cbret)) < 0){
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0) if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
goto done; goto done;
clicon_log(LOG_NOTICE, "%s Error in rpc_callback_call:%s", __FUNCTION__, xml_name(xe)); 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 */ goto reply; /* Dont quit here on user callbacks */
} }
if (ret == 0) if (ret == 0){
ce->ce_out_rpc_errors++;
goto reply; goto reply;
}
if (nr == 0){ /* not handled by callback */ if (nr == 0){ /* not handled by callback */
if (netconf_operation_not_supported(cbret, "application", "RPC operation not supported")< 0) if (netconf_operation_not_supported(cbret, "application", "RPC operation not supported")< 0)
goto done; goto done;
ce->ce_out_rpc_errors++;
goto reply; goto reply;
} }
if (xnacm){ if (xnacm){

View file

@ -41,24 +41,33 @@
* Types * Types
*/ */
/* /*
* Client entry. * Backend client entry.
* Keep state about every connected client. * Keep state about every connected client.
* References from RFC 6022, ietf-netconf-monitoring.yang sessions container
*/ */
struct client_entry{ struct client_entry{
struct client_entry *ce_next; /* The clients linked list */ struct client_entry *ce_next; /* The clients linked list */
struct sockaddr ce_addr; /* The clients (UNIX domain) address */ struct sockaddr ce_addr; /* The clients (UNIX domain) address */
int ce_s; /* stream socket to client */ int ce_s; /* stream socket to client */
int ce_nr; /* Client number (for dbg/tracing) */ 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 */ uint32_t ce_id; /* Session id, accessor functions: clicon_session_id_get/set */
char *ce_username;/* Translated from peer user cred */ char *ce_username;/* Translated from peer user cred */
clicon_handle ce_handle; /* clicon config handle (all clients have same?) */ 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 * 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 backend_client_rm(clicon_handle h, struct client_entry *ce);
int from_client(int fd, void *arg); int from_client(int fd, void *arg);
int backend_rpc_init(clicon_handle h); int backend_rpc_init(clicon_handle h);

View file

@ -181,10 +181,22 @@ client_get_streams(clicon_handle h,
* @param[in] xpath XPath selection, may be used to filter early * @param[in] xpath XPath selection, may be used to filter early
* @param[in] nsc XML Namespace context for xpath * @param[in] nsc XML Namespace context for xpath
* @param[in] wdef With-defaults parameter, see RFC 6243 * @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 -1 Error (fatal)
* @retval 0 Statedata callback failed (clicon_err called) * @retval 0 Statedata callback failed (error in xret)
* @retval 1 OK * @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 static int
get_client_statedata(clicon_handle h, get_client_statedata(clicon_handle h,
@ -196,9 +208,11 @@ get_client_statedata(clicon_handle h,
int retval = -1; int retval = -1;
yang_stmt *yspec; yang_stmt *yspec;
yang_stmt *ymod; yang_stmt *ymod;
cxobj *x1 = NULL;
int ret; int ret;
char *namespace; char *namespace;
cbuf *cb = NULL; cbuf *cb = NULL;
cxobj *xerr = NULL;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if ((yspec = clicon_dbspec_yang(h)) == NULL){ if ((yspec = clicon_dbspec_yang(h)) == NULL){
@ -236,7 +250,6 @@ get_client_statedata(clicon_handle h,
goto done; goto done;
} }
cbuf_reset(cb); cbuf_reset(cb);
/* XXX This code does not filter state data with xpath */
cprintf(cb, "<restconf-state xmlns=\"%s\"/>", namespace); cprintf(cb, "<restconf-state xmlns=\"%s\"/>", namespace);
if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, NULL) < 0) if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, NULL) < 0)
goto done; goto done;
@ -254,7 +267,32 @@ get_client_statedata(clicon_handle h,
goto fail; goto fail;
} }
if (clicon_option_bool(h, "CLICON_NETCONF_MONITORING")){ 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; goto done;
if (ret == 0) if (ret == 0)
goto fail; goto fail;
@ -320,6 +358,10 @@ get_client_statedata(clicon_handle h,
retval = 1; /* OK */ retval = 1; /* OK */
done: done:
clicon_debug(1, "%s %d", __FUNCTION__, retval); clicon_debug(1, "%s %d", __FUNCTION__, retval);
if (xerr)
xml_free(xerr);
if (x1)
xml_free(x1);
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
return retval; return retval;
@ -734,19 +776,13 @@ get_list_pagination(clicon_handle h,
cbuf *cba = NULL; cbuf *cba = NULL;
/* Add remaining attribute */ /* Add remaining attribute */
if ((xa = xml_new("remaining", x1, CX_ATTR)) == NULL)
goto done;
if ((cba = cbuf_new()) == NULL){ if ((cba = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new"); clicon_err(OE_UNIX, errno, "cbuf_new");
goto done; goto done;
} }
cprintf(cba, "%u", remaining); cprintf(cba, "%u", remaining);
if (xml_value_set(xa, cbuf_get(cba)) < 0) if (xml_add_attr(x1, "remaining", cbuf_get(cba), "cp", "http://clicon.org/clixon-netconf-list-pagination") < 0)
goto done; 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 (cba) if (cba)
cbuf_free(cba); cbuf_free(cba);
} }

View file

@ -249,7 +249,6 @@ backend_accept_client(int fd,
} }
if ((ce = backend_client_add(h, &from)) == NULL) if ((ce = backend_client_add(h, &from)) == NULL)
goto done; goto done;
ce->ce_handle = h;
/* /*
* Get credentials of connected peer - only for unix socket * Get credentials of connected peer - only for unix socket

View file

@ -145,6 +145,8 @@ backend_client_add(clicon_handle h,
ce->ce_nr = bh->bh_ce_nr++; /* Session-id ? */ ce->ce_nr = bh->bh_ce_nr++; /* Session-id ? */
memcpy(&ce->ce_addr, addr, sizeof(*addr)); memcpy(&ce->ce_addr, addr, sizeof(*addr));
ce->ce_next = bh->bh_ce_list; ce->ce_next = bh->bh_ce_list;
ce->ce_handle = h;
gettimeofday(&ce->ce_time, NULL);
bh->bh_ce_list = ce; bh->bh_ce_list = ce;
return ce; return ce;
} }
@ -180,6 +182,10 @@ backend_client_delete(clicon_handle h,
*ce_prev = c->ce_next; *ce_prev = c->ce_next;
if (ce->ce_username) if (ce->ce_username)
free(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); free(ce);
break; break;
} }
@ -203,8 +209,9 @@ backend_client_print(clicon_handle h,
fprintf(f, "Client: %d\n", ce->ce_nr); fprintf(f, "Client: %d\n", ce->ce_nr);
fprintf(f, " Session: %d\n", ce->ce_id); fprintf(f, " Session: %d\n", ce->ce_id);
fprintf(f, " Socket: %d\n", ce->ce_s); fprintf(f, " Socket: %d\n", ce->ce_s);
fprintf(f, " Msgs in: %d\n", ce->ce_stat_in); fprintf(f, " RPCs in: %u\n", ce->ce_in_rpcs);
fprintf(f, " Msgs out: %d\n", ce->ce_stat_out); 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); fprintf(f, " Username: %s\n", ce->ce_username);
} }
return 0; return 0;

View file

@ -294,7 +294,6 @@ cli_dbxml(clicon_handle h,
cxobj *xbot = NULL; /* xpath, NULL if datastore */ cxobj *xbot = NULL; /* xpath, NULL if datastore */
yang_stmt *y = NULL; /* yang spec of xpath */ yang_stmt *y = NULL; /* yang spec of xpath */
cxobj *xtop = NULL; /* xpath root */ cxobj *xtop = NULL; /* xpath root */
cxobj *xa; /* attribute */
cxobj *xerr = NULL; cxobj *xerr = NULL;
int ret; int ret;
cg_var *cv; cg_var *cv;
@ -338,11 +337,7 @@ cli_dbxml(clicon_handle h,
goto done; goto done;
} }
} }
if ((xa = xml_new("operation", xbot, CX_ATTR)) == NULL) if (xml_add_attr(xbot, "operation", xml_operation2str(op), NETCONF_BASE_PREFIX, NULL) < 0)
goto done;
if (xml_prefix_set(xa, NETCONF_BASE_PREFIX) < 0)
goto done;
if (xml_value_set(xa, xml_operation2str(op)) < 0)
goto done; goto done;
/* Add body last in case of leaf */ /* Add body last in case of leaf */
if (cvec_len(cvv) > 1 && if (cvec_len(cvv) > 1 &&

View file

@ -818,6 +818,11 @@ main(int argc,
if (evalresult < 0) if (evalresult < 0)
goto done; 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 */ /* Go into event-loop unless -1 command-line */
if (!once){ if (!once){

View file

@ -110,6 +110,15 @@ netconf_add_request_attr(cxobj *xrpc,
/* If attribute already exists, dont copy it */ /* If attribute already exists, dont copy it */
if (xml_find_type(xrep, NULL, xml_name(xa), CX_ATTR) != NULL) if (xml_find_type(xrep, NULL, xml_name(xa), CX_ATTR) != NULL)
continue; /* Skip already present (dont overwrite) */ 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) if ((xa2 = xml_dup(xa)) ==NULL)
goto done; goto done;
if (xml_addsub(xrep, xa2) < 0) if (xml_addsub(xrep, xa2) < 0)
@ -192,11 +201,11 @@ netconf_rpc_message(clicon_handle h,
yang_stmt *yspec, yang_stmt *yspec,
int *eof) int *eof)
{ {
int retval = -1; int retval = -1;
cxobj *xret = NULL; /* Return (out) */ cxobj *xret = NULL; /* Return (out) */
int ret; int ret;
cbuf *cbret = NULL; cbuf *cbret = NULL;
cxobj *xc; cxobj *xc;
netconf_framing_type framing; netconf_framing_type framing;
framing = clicon_option_int(h, "netconf-framing"); framing = clicon_option_int(h, "netconf-framing");
@ -240,9 +249,9 @@ netconf_rpc_message(clicon_handle h,
goto done; goto done;
goto ok; goto ok;
} }
if (netconf_rpc_dispatch(h, xrpc, &xret, eof) < 0){ if (netconf_rpc_dispatch(h, xrpc, &xret, eof) < 0)
goto done; goto done;
}
/* Is there a return message in xret? */ /* Is there a return message in xret? */
if (xret == NULL){ if (xret == NULL){
if (netconf_operation_failed_xml(&xret, "rpc", "Internal error: no xml return")< 0) 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 * used by the client, even though new TCP sessions are created for
* each message sent to the backend. * 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; goto done;
clicon_session_id_set(h, id); clicon_session_id_set(h, id);

View file

@ -704,9 +704,7 @@ netconf_rpc_dispatch(clicon_handle h,
* It may even be wrong if something else is done with the incoming message? * It may even be wrong if something else is done with the incoming message?
*/ */
if ((username = clicon_username_get(h)) != NULL){ if ((username = clicon_username_get(h)) != NULL){
if ((xa = xml_new("username", xn, CX_ATTR)) == NULL) if (xml_add_attr(xn, "username", username, CLIXON_LIB_PREFIX, CLIXON_LIB_NS) < 0)
goto done;
if (xml_value_set(xa, username) < 0)
goto done; goto done;
} }
/* Many of these calls are now calling generic clicon_rpc_netconf_xml /* Many of these calls are now calling generic clicon_rpc_netconf_xml

View file

@ -405,7 +405,6 @@ restconf_insert_attributes(cxobj *xdata,
cvec *qvec) cvec *qvec)
{ {
int retval = -1; int retval = -1;
cxobj *xa;
char *instr; char *instr;
char *pstr; char *pstr;
yang_stmt *y; yang_stmt *y;
@ -422,12 +421,7 @@ restconf_insert_attributes(cxobj *xdata,
/* First add xmlns:yang attribute */ /* First add xmlns:yang attribute */
if (xmlns_set(xdata, "yang", YANG_XML_NAMESPACE) < 0) if (xmlns_set(xdata, "yang", YANG_XML_NAMESPACE) < 0)
goto done; goto done;
/* Then add insert attribute */ if (xml_add_attr(xdata, "insert", instr, "yang", NULL) < 0)
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)
goto done; goto done;
} }
if ((pstr = cvec_find_str(qvec, "point")) != NULL){ if ((pstr = cvec_find_str(qvec, "point")) != NULL){
@ -439,11 +433,7 @@ restconf_insert_attributes(cxobj *xdata,
attrname="key"; attrname="key";
else else
attrname="value"; 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) if ((ret = api_path2xpath(pstr, ys_spec(y), &xpath, &nsc, NULL)) < 0)
goto done; goto done;
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){
@ -471,7 +461,7 @@ restconf_insert_attributes(cxobj *xdata,
p++; p++;
cprintf(cb, "%s", 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; goto done;
} }
/* Add prefix/namespaces used in attributes */ /* Add prefix/namespaces used in attributes */

View file

@ -163,7 +163,7 @@ restconf_main_config(clicon_handle h,
else { else {
/* Loop to wait for backend starting, try again if not done */ /* Loop to wait for backend starting, try again if not done */
while (1){ while (1){
if (clicon_hello_req(h, &id) < 0){ if (clicon_hello_req(h, "cl:restconf", NULL, &id) < 0){
if (errno == ENOENT){ if (errno == ENOENT){
fprintf(stderr, "waiting"); fprintf(stderr, "waiting");
sleep(1); sleep(1);

View file

@ -634,7 +634,7 @@ restconf_clixon_backend(clicon_handle h,
/* Loop to wait for backend starting, try again if not done */ /* Loop to wait for backend starting, try again if not done */
while (1){ while (1){
if (clicon_hello_req(h, &id) < 0){ if (clicon_hello_req(h, "cl:restconf", NULL, &id) < 0){
if (errno == ENOENT){ if (errno == ENOENT){
fprintf(stderr, "waiting"); fprintf(stderr, "waiting");
sleep(1); sleep(1);

View file

@ -384,24 +384,12 @@ api_data_write(clicon_handle h,
/* Add operation create as attribute. If that fails with Conflict, then /* Add operation create as attribute. If that fails with Conflict, then
* try "replace" (see comment in function header) * 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; 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; 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 /* Top-of tree, no api-path
* Replace xparent with x, ie bottom of api-path with data * Replace xparent with x, ie bottom of api-path with data
*/ */
@ -512,16 +500,20 @@ api_data_write(clicon_handle h,
} /* api-path != NULL */ } /* api-path != NULL */
/* For internal XML protocol: add username attribute for access control /* For internal XML protocol: add username attribute for access control
*/ */
username = clicon_username_get(h);
/* Create text buffer for transfer to backend */ /* Create text buffer for transfer to backend */
if ((cbx = cbuf_new()) == NULL) if ((cbx = cbuf_new()) == NULL)
goto done; goto done;
cprintf(cbx, "<rpc xmlns=\"%s\" username=\"%s\" xmlns:%s=\"%s\" %s>", cprintf(cbx, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
NETCONF_BASE_NAMESPACE, cprintf(cbx, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
username?username:"", 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_PREFIX,
NETCONF_BASE_NAMESPACE, /* bind nc to netconf namespace */ NETCONF_BASE_NAMESPACE); /* bind nc to netconf namespace */
NETCONF_MESSAGE_ID_ATTR); cprintf(cbx, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cbx, ">");
cprintf(cbx, "<edit-config"); cprintf(cbx, "<edit-config");
/* RFC8040 Sec 1.4: /* RFC8040 Sec 1.4:
* If this is a "data" request and the NETCONF server supports :startup, * 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 ((IETF_DS_NONE == ds) &&
if_feature(yspec, "ietf-netconf", "startup") && if_feature(yspec, "ietf-netconf", "startup") &&
!clicon_option_bool(h, "CLICON_RESTCONF_STARTUP_DONTUPDATE")){ !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, "><target><candidate /></target>");
cprintf(cbx, "<default-operation>none</default-operation>"); cprintf(cbx, "<default-operation>none</default-operation>");
if (clixon_xml2cbuf(cbx, xtop, 0, 0, -1, 0) < 0) if (clixon_xml2cbuf(cbx, xtop, 0, 0, -1, 0) < 0)
@ -759,14 +753,17 @@ api_data_delete(clicon_handle h,
goto done; goto done;
/* For internal XML protocol: add username attribute for access control /* For internal XML protocol: add username attribute for access control
*/ */
username = clicon_username_get(h); cprintf(cbx, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
cprintf(cbx, "<rpc xmlns=\"%s\" username=\"%s\" xmlns:%s=\"%s\" %s>", cprintf(cbx, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
NETCONF_BASE_NAMESPACE, if ((username = clicon_username_get(h)) != NULL){
username?username:"", 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_PREFIX,
NETCONF_BASE_NAMESPACE, NETCONF_BASE_NAMESPACE);
NETCONF_MESSAGE_ID_ATTR); /* bind nc to netconf namespace */ cprintf(cbx, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cbx, ">");
cprintf(cbx, "<edit-config"); cprintf(cbx, "<edit-config");
/* RFC8040 Sec 1.4: /* RFC8040 Sec 1.4:
* If this is a "data" request and the NETCONF server supports :startup, * 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 ((IETF_DS_NONE == ds) &&
if_feature(yspec, "ietf-netconf", "startup") && if_feature(yspec, "ietf-netconf", "startup") &&
!clicon_option_bool(h, "CLICON_RESTCONF_STARTUP_DONTUPDATE")){ !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, "><target><candidate /></target>");
cprintf(cbx, "<default-operation>none</default-operation>"); cprintf(cbx, "<default-operation>none</default-operation>");
if (clixon_xml2cbuf(cbx, xtop, 0, 0, -1, 0) < 0) if (clixon_xml2cbuf(cbx, xtop, 0, 0, -1, 0) < 0)

View file

@ -337,14 +337,15 @@ api_data_post(clicon_handle h,
} }
/* For internal XML protocol: add username attribute for access control /* For internal XML protocol: add username attribute for access control
*/ */
username = clicon_username_get(h); cprintf(cbx, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
cprintf(cbx, "<rpc xmlns=\"%s\" username=\"%s\" xmlns:%s=\"%s\" %s>", if ((username = clicon_username_get(h)) != NULL){
NETCONF_BASE_NAMESPACE, cprintf(cbx, " %s:username=\"%s\" xmlns:%s=\"%s\"",
username?username:"", NETCONF_BASE_PREFIX,
NETCONF_BASE_PREFIX, username?username:"",
NETCONF_BASE_NAMESPACE, NETCONF_BASE_PREFIX,
NETCONF_MESSAGE_ID_ATTR); /* bind nc to netconf namespace */ NETCONF_BASE_NAMESPACE);
}
cprintf(cbx, " %s>", NETCONF_MESSAGE_ID_ATTR); /* bind nc to netconf namespace */
cprintf(cbx, "<edit-config"); cprintf(cbx, "<edit-config");
/* RFC8040 Sec 1.4: /* RFC8040 Sec 1.4:
* If this is a "data" request and the NETCONF server supports :startup, * 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 ((IETF_DS_NONE == ds) &&
if_feature(yspec, "ietf-netconf", "startup") && if_feature(yspec, "ietf-netconf", "startup") &&
!clicon_option_bool(h, "CLICON_RESTCONF_STARTUP_DONTUPDATE")){ !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, "><target><candidate /></target>");
cprintf(cbx, "<default-operation>none</default-operation>"); cprintf(cbx, "<default-operation>none</default-operation>");
if (clixon_xml2cbuf(cbx, xtop, 0, 0, -1, 0) < 0) if (clixon_xml2cbuf(cbx, xtop, 0, 0, -1, 0) < 0)

32
apps/snmp/README.md Normal file
View 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)

View file

@ -525,7 +525,7 @@ main(int argc,
* used by the client, even though new TCP sessions are created for * used by the client, even though new TCP sessions are created for
* each message sent to the backend. * 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; goto done;
clicon_session_id_set(h, id); clicon_session_id_set(h, id);

View file

@ -41,6 +41,6 @@
/* /*
* Prototypes * Prototypes
*/ */
int netconf_monitoring_state_get(clicon_handle h, yang_stmt *yspec, char *xpath, cvec *nsc, int brief, cxobj **xret); int netconf_monitoring_state_get(clicon_handle h, yang_stmt *yspec, char *xpath, cvec *nsc, cxobj **xret, cxobj **xerr);
#endif /* _CLIXON_NETCONF_MONITORING_H_ */ #endif /* _CLIXON_NETCONF_MONITORING_H_ */

View file

@ -51,7 +51,9 @@
* @see clixon-lib.yang * @see clixon-lib.yang
*/ */
#define CLIXON_CONF_NS "http://clicon.org/config" #define CLIXON_CONF_NS "http://clicon.org/config"
#define CLIXON_CONF_PREFIX "cc"
#define CLIXON_LIB_NS "http://clicon.org/lib" #define CLIXON_LIB_NS "http://clicon.org/lib"
#define CLIXON_LIB_PREFIX "cl"
#define CLIXON_AUTOCLI_NS "http://clicon.org/autocli" #define CLIXON_AUTOCLI_NS "http://clicon.org/autocli"
#define CLIXON_RESTCONF_NS "http://clicon.org/restconf" #define CLIXON_RESTCONF_NS "http://clicon.org/restconf"

View file

@ -68,7 +68,7 @@ int clicon_rpc_discard_changes(clicon_handle h);
int clicon_rpc_create_subscription(clicon_handle h, char *stream, char *filter, int *s); int clicon_rpc_create_subscription(clicon_handle h, char *stream, char *filter, int *s);
int clicon_rpc_debug(clicon_handle h, int level); int clicon_rpc_debug(clicon_handle h, int level);
int clicon_rpc_restconf_debug(clicon_handle h, int level); int clicon_rpc_restconf_debug(clicon_handle h, int level);
int clicon_hello_req(clicon_handle h, uint32_t *id); int clicon_hello_req(clicon_handle h, char *transport, char *source_host, uint32_t *id);
int clicon_rpc_restart_plugin(clicon_handle h, char *plugin); int clicon_rpc_restart_plugin(clicon_handle h, char *plugin);
#endif /* _CLIXON_PROTO_CLIENT_H_ */ #endif /* _CLIXON_PROTO_CLIENT_H_ */

View file

@ -279,7 +279,9 @@ cxobj *xml_root(cxobj *xn);
int xml_operation(char *opstr, enum operation_type *op); int xml_operation(char *opstr, enum operation_type *op);
char *xml_operation2str(enum operation_type op); char *xml_operation2str(enum operation_type op);
int xml_attr_insert2val(char *instr, enum insert_type *ins); int xml_attr_insert2val(char *instr, enum insert_type *ins);
int xml_add_attr(cxobj *xn, char *name, char *value, char *prefix, char *ns);
int clicon_log_xml(int level, cxobj *x, const char *format, ...) __attribute__ ((format (printf, 3, 4))); int clicon_log_xml(int level, cxobj *x, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
#ifdef XML_EXPLICIT_INDEX #ifdef XML_EXPLICIT_INDEX
int xml_search_index_p(cxobj *x); int xml_search_index_p(cxobj *x);

View file

@ -168,7 +168,6 @@ check_body_namespace(cxobj *x0,
char *prefix = NULL; char *prefix = NULL;
char *ns0 = NULL; char *ns0 = NULL;
char *ns1 = NULL; char *ns1 = NULL;
cxobj *xa;
cxobj *x; cxobj *x;
int isroot; int isroot;
cbuf *cberr = NULL; cbuf *cberr = NULL;
@ -210,17 +209,12 @@ bad-attribue?
goto done; goto done;
/* Create xmlns attribute to x0 XXX same code ^*/ /* Create xmlns attribute to x0 XXX same code ^*/
if (prefix){ if (prefix){
if ((xa = xml_new(prefix, x, CX_ATTR)) == NULL) if (xml_add_attr(x, prefix, ns0, "xmlns", NULL) < 0)
goto done;
if (xml_prefix_set(xa, "xmlns") < 0)
goto done; goto done;
} }
else{ else
if ((xa = xml_new("xmlns", x, CX_ATTR)) == NULL) if (xml_add_attr(x, "xmlns", ns0, NULL, NULL) < 0)
goto done; goto done;
}
if (xml_value_set(xa, ns0) < 0)
goto done;
xml_sort(x); /* Ensure attr is first / XXX xml_insert? */ xml_sort(x); /* Ensure attr is first / XXX xml_insert? */
} }
#if 0 #if 0
@ -237,11 +231,7 @@ bad-attribue?
; ;
} }
else{ /* Add it according to the kludge,... */ else{ /* Add it according to the kludge,... */
if ((xa = xml_new(prefix, x0, CX_ATTR)) == NULL) if (xml_add_attr(x0, prefix, ns0, "xmlns", NULL) < 0)
goto done;
if (xml_prefix_set(xa, "xmlns") < 0)
goto done;
if (xml_value_set(xa, ns0) < 0)
goto done; goto done;
} }
} }

View file

@ -293,7 +293,6 @@ json2xml_decode_identityref(cxobj *x,
char *ns; char *ns;
char *body; char *body;
cxobj *xb; cxobj *xb;
cxobj *xa;
char *prefix = NULL; char *prefix = NULL;
char *id = NULL; char *id = NULL;
yang_stmt *ymod; yang_stmt *ymod;
@ -333,12 +332,9 @@ json2xml_decode_identityref(cxobj *x,
if (prefix2 == NULL) if (prefix2 == NULL)
prefix2 = yang_find_myprefix(ymod); prefix2 = yang_find_myprefix(ymod);
/* Add "xmlns:prefix2=namespace" */ /* Add "xmlns:prefix2=namespace" */
if ((xa = xml_new(prefix2, x, CX_ATTR)) == NULL) if (xml_add_attr(x, prefix2, ns, "xmlns", NULL) < 0)
goto done;
if (xml_prefix_set(xa, "xmlns") < 0)
goto done;
if (xml_value_set(xa, ns) < 0)
goto done; goto done;
} }
/* Here prefix2 is valid and can be NULL /* Here prefix2 is valid and can be NULL
Change body prefix to prefix2:id */ Change body prefix to prefix2:id */

View file

@ -161,7 +161,6 @@ netconf_invalid_value_xml(cxobj **xret,
int retval =-1; int retval =-1;
cxobj *xerr = NULL; cxobj *xerr = NULL;
char *encstr = NULL; char *encstr = NULL;
cxobj *xa;
if (xret == NULL){ if (xret == NULL){
clicon_err(OE_NETCONF, EINVAL, "xret is NULL"); clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
@ -173,9 +172,7 @@ netconf_invalid_value_xml(cxobj **xret,
} }
else if (xml_name_set(*xret, "rpc-reply") < 0) else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done; goto done;
if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL) if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
goto done; goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done; goto done;
@ -282,7 +279,6 @@ netconf_missing_attribute_xml(cxobj **xret,
int retval = -1; int retval = -1;
cxobj *xerr = NULL; cxobj *xerr = NULL;
char *encstr = NULL; char *encstr = NULL;
cxobj *xa;
if (xret == NULL){ if (xret == NULL){
clicon_err(OE_NETCONF, EINVAL, "xret is NULL"); clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
@ -294,9 +290,7 @@ netconf_missing_attribute_xml(cxobj **xret,
} }
else if (xml_name_set(*xret, "rpc-reply") < 0) else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done; goto done;
if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL) if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
goto done; goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done; goto done;
@ -394,7 +388,6 @@ netconf_bad_attribute_xml(cxobj **xret,
int retval = -1; int retval = -1;
cxobj *xerr = NULL; cxobj *xerr = NULL;
char *encstr = NULL; char *encstr = NULL;
cxobj *xa;
if (xret == NULL){ if (xret == NULL){
clicon_err(OE_NETCONF, EINVAL, "xret is NULL"); clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
@ -406,9 +399,7 @@ netconf_bad_attribute_xml(cxobj **xret,
} }
else if (xml_name_set(*xret, "rpc-reply") < 0) else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done; goto done;
if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL) if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
goto done; goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done; goto done;
@ -491,7 +482,6 @@ netconf_common_xml(cxobj **xret,
int retval =-1; int retval =-1;
cxobj *xerr; cxobj *xerr;
char *encstr = NULL; char *encstr = NULL;
cxobj *xa;
if (xret == NULL){ if (xret == NULL){
clicon_err(OE_NETCONF, EINVAL, "xret is NULL"); clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
@ -500,9 +490,7 @@ netconf_common_xml(cxobj **xret,
if (*xret == NULL){ if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL) if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
goto done; goto done;
if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL) if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
goto done; goto done;
} }
else if (xml_name_set(*xret, "rpc-reply") < 0) else if (xml_name_set(*xret, "rpc-reply") < 0)
@ -751,7 +739,6 @@ netconf_access_denied_xml(cxobj **xret,
int retval =-1; int retval =-1;
cxobj *xerr; cxobj *xerr;
char *encstr = NULL; char *encstr = NULL;
cxobj *xa;
if (xret == NULL){ if (xret == NULL){
clicon_err(OE_NETCONF, EINVAL, "xret is NULL"); clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
@ -763,9 +750,7 @@ netconf_access_denied_xml(cxobj **xret,
} }
else if (xml_name_set(*xret, "rpc-reply") < 0) else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done; goto done;
if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL) if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
goto done; goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done; goto done;
@ -987,7 +972,6 @@ netconf_data_missing_xml(cxobj **xret,
int retval = -1; int retval = -1;
char *encstr = NULL; char *encstr = NULL;
cxobj *xerr; cxobj *xerr;
cxobj *xa;
if (xret == NULL){ if (xret == NULL){
clicon_err(OE_NETCONF, EINVAL, "xret is NULL"); clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
@ -999,9 +983,7 @@ netconf_data_missing_xml(cxobj **xret,
} }
else if (xml_name_set(*xret, "rpc-reply") < 0) else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done; goto done;
if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL) if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
goto done; goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done; goto done;
@ -1045,7 +1027,6 @@ netconf_missing_choice_xml(cxobj **xret,
int retval = -1; int retval = -1;
char *encstr = NULL; char *encstr = NULL;
cxobj *xerr; cxobj *xerr;
cxobj *xa;
char *path = NULL; char *path = NULL;
char *encpath = NULL; char *encpath = NULL;
@ -1059,9 +1040,7 @@ netconf_missing_choice_xml(cxobj **xret,
} }
else if (xml_name_set(*xret, "rpc-reply") < 0) else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done; goto done;
if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL) if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
goto done; goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done; goto done;
@ -1122,7 +1101,6 @@ netconf_operation_not_supported_xml(cxobj **xret,
int retval =-1; int retval =-1;
cxobj *xerr; cxobj *xerr;
char *encstr = NULL; char *encstr = NULL;
cxobj *xa;
if (xret == NULL){ if (xret == NULL){
clicon_err(OE_NETCONF, EINVAL, "xret is NULL"); clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
@ -1134,9 +1112,7 @@ netconf_operation_not_supported_xml(cxobj **xret,
} }
else if (xml_name_set(*xret, "rpc-reply") < 0) else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done; goto done;
if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL) if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
goto done; goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done; goto done;
@ -1237,7 +1213,6 @@ netconf_operation_failed_xml(cxobj **xret,
int retval =-1; int retval =-1;
cxobj *xerr; cxobj *xerr;
char *encstr = NULL; char *encstr = NULL;
cxobj *xa;
if (xret == NULL){ if (xret == NULL){
clicon_err(OE_NETCONF, EINVAL, "xret is NULL"); clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
@ -1249,9 +1224,7 @@ netconf_operation_failed_xml(cxobj **xret,
} }
else if (xml_name_set(*xret, "rpc-reply") < 0) else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done; goto done;
if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL) if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
goto done; goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done; goto done;
@ -1325,7 +1298,6 @@ netconf_malformed_message_xml(cxobj **xret,
int retval =-1; int retval =-1;
cxobj *xerr; cxobj *xerr;
char *encstr = NULL; char *encstr = NULL;
cxobj *xa;
if (xret == NULL){ if (xret == NULL){
clicon_err(OE_NETCONF, EINVAL, "xret is NULL"); clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
@ -1337,9 +1309,7 @@ netconf_malformed_message_xml(cxobj **xret,
} }
else if (xml_name_set(*xret, "rpc-reply") < 0) else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done; goto done;
if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL) if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
goto done; goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done; goto done;
@ -1408,7 +1378,6 @@ netconf_data_not_unique_xml(cxobj **xret,
cg_var *cvi = NULL; cg_var *cvi = NULL;
cxobj *xerr; cxobj *xerr;
cxobj *xinfo; cxobj *xinfo;
cxobj *xa;
char *path = NULL; char *path = NULL;
char *encpath = NULL; char *encpath = NULL;
@ -1422,9 +1391,7 @@ netconf_data_not_unique_xml(cxobj **xret,
} }
else if (xml_name_set(*xret, "rpc-reply") < 0) else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done; goto done;
if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL) if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
goto done; goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done; goto done;
@ -1482,7 +1449,6 @@ netconf_minmax_elements_xml(cxobj **xret,
cxobj *xerr; cxobj *xerr;
char *path = NULL; char *path = NULL;
char *encpath = NULL; char *encpath = NULL;
cxobj *xa;
if (xret == NULL){ if (xret == NULL){
clicon_err(OE_NETCONF, EINVAL, "xret is NULL"); clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
@ -1494,9 +1460,7 @@ netconf_minmax_elements_xml(cxobj **xret,
} }
else if (xml_name_set(*xret, "rpc-reply") < 0) else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done; goto done;
if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL) if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
goto done; goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done; goto done;
@ -1528,9 +1492,9 @@ netconf_minmax_elements_xml(cxobj **xret,
* @param[in] x XML tree * @param[in] x XML tree
* @param[in] yspec Yang spec * @param[in] yspec Yang spec
* @param[in,out] xret Existing XML tree, merge x into this * @param[in,out] xret Existing XML tree, merge x into this
* @retval -1 Error (fatal)
* @retval 0 Statedata callback failed
* @retval 1 OK * @retval 1 OK
* @retval 0 Statedata callback failed, error in xret?
* @retval -1 Error (fatal)
*/ */
int int
netconf_trymerge(cxobj *x, netconf_trymerge(cxobj *x,
@ -1995,9 +1959,9 @@ clixon_netconf_internal_error(cxobj *xerr,
* @param[in] defaultstr If given, default string which is accepted and sets value to 0 * @param[in] defaultstr If given, default string which is accepted and sets value to 0
* @param[in,out] cbret Output buffer for internal RPC message if invalid * @param[in,out] cbret Output buffer for internal RPC message if invalid
* @param[out] value Value if valid * @param[out] value Value if valid
* @retval -1 Error
* @retval 0 Invalid, cbret set
* @retval 1 OK * @retval 1 OK
* @retval 0 Invalid, cbret set
* @retval -1 Error
* @code * @code
* char *name = "a"; * char *name = "a";
* char *valstr = "322"; * char *valstr = "322";
@ -2238,10 +2202,10 @@ netconf_output_encap(netconf_framing_type framing,
* @param[in] ch New input character * @param[in] ch New input character
* @param[in,out] state State machine state * @param[in,out] state State machine state
* @param[in,out] size Remaining expecting chunk bytes. * @param[in,out] size Remaining expecting chunk bytes.
* @retval -1 Error
* @retval 0 Framing char, not data
* @retval 1 Chunk-data
* @retval 2 End-of-frame * @retval 2 End-of-frame
* @retval 1 Chunk-data
* @retval 0 Framing char, not data
* @retval -1 Error
* Example: * Example:
C: \n#4\n C: \n#4\n
C: <rpc C: <rpc
@ -2352,3 +2316,4 @@ netconf_input_chunked_framing(char ch,
retval = -1; /* Error */ retval = -1; /* Error */
goto done; goto done;
} }

View file

@ -78,6 +78,7 @@ per_datastore(clicon_handle h,
cprintf(cb, "<datastore><name>%s</name>", db); cprintf(cb, "<datastore><name>%s</name>", db);
if ((sid = xmldb_islocked(h, db)) > 0){ if ((sid = xmldb_islocked(h, db)) > 0){
cprintf(cb, "<locks>"); cprintf(cb, "<locks>");
cprintf(cb, "<global-lock>");
cprintf(cb, "<locked-by-session>%u</locked-by-session>", sid); cprintf(cb, "<locked-by-session>%u</locked-by-session>", sid);
xmldb_lock_timestamp(h, db, &tv); xmldb_lock_timestamp(h, db, &tv);
if (time2str(tv, timestr, sizeof(timestr)) < 0){ if (time2str(tv, timestr, sizeof(timestr)) < 0){
@ -85,6 +86,7 @@ per_datastore(clicon_handle h,
goto done; goto done;
} }
cprintf(cb, "<locked-time>%s</locked-time>", timestr); cprintf(cb, "<locked-time>%s</locked-time>", timestr);
cprintf(cb, "</global-lock>");
cprintf(cb, "</locks>"); cprintf(cb, "</locks>");
} }
cprintf(cb, "</datastore>"); cprintf(cb, "</datastore>");
@ -175,28 +177,6 @@ netconf_monitoring_schemas(clicon_handle h,
return retval; return retval;
} }
/*! Get netconf monitoring sessions state
*
* @param[in] h Clicon handle
* @param[in] yspec Yang spec
* @param[in,out] cb CLIgen buffer
* @retval -1 Error (fatal)
* @retval 0 OK
* @see RFC 6022 Section 2.1.4
* XXX: NYI
*/
static int
netconf_monitoring_sessions(clicon_handle h,
yang_stmt *yspec,
cbuf *cb)
{
int retval = -1;
retval = 0;
//done:
return retval;
}
/*! Get netconf monitoring statistics state /*! Get netconf monitoring statistics state
* *
* @param[in] h Clicon handle * @param[in] h Clicon handle
@ -227,11 +207,12 @@ netconf_monitoring_statistics(clicon_handle h,
* @param[in] yspec Yang spec * @param[in] yspec Yang spec
* @param[in] xpath XML Xpath * @param[in] xpath XML Xpath
* @param[in] nsc XML Namespace context for xpath * @param[in] nsc XML Namespace context for xpath
* @param[in] brief Just name, revision and uri (no cache)
* @param[in,out] xret Existing XML tree, merge x into this * @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 -1 Error (fatal)
* @retval 0 Statedata callback failed * @retval 0 Statedata callback failed, error in xret
* @retval 1 OK * @retval 1 OK
* @see backend_monitoring_state_get
* @see RFC 6022 * @see RFC 6022
*/ */
int int
@ -239,11 +220,12 @@ netconf_monitoring_state_get(clicon_handle h,
yang_stmt *yspec, yang_stmt *yspec,
char *xpath, char *xpath,
cvec *nsc, cvec *nsc,
int brief, cxobj **xret,
cxobj **xret) cxobj **xerr)
{ {
int retval = -1; int retval = -1;
cbuf *cb = NULL; cbuf *cb = NULL;
int ret;
if ((cb = cbuf_new()) ==NULL){ if ((cb = cbuf_new()) ==NULL){
clicon_err(OE_XML, errno, "cbuf_new"); clicon_err(OE_XML, errno, "cbuf_new");
@ -256,20 +238,22 @@ netconf_monitoring_state_get(clicon_handle h,
goto done; goto done;
if (netconf_monitoring_schemas(h, yspec, cb) < 0) if (netconf_monitoring_schemas(h, yspec, cb) < 0)
goto done; goto done;
if (netconf_monitoring_sessions(h, yspec, cb) < 0) /* sessions is backend-specific */
goto done;
if (netconf_monitoring_statistics(h, yspec, cb) < 0) if (netconf_monitoring_statistics(h, yspec, cb) < 0)
goto done; goto done;
cprintf(cb, "</netconf-state>"); cprintf(cb, "</netconf-state>");
if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, NULL) < 0) if ((ret = clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, xerr)) < 0)
goto done; goto done;
if (ret == 0){
goto fail;
}
retval = 1; retval = 1;
done: done:
clicon_debug(1, "%s %d", __FUNCTION__, retval); clicon_debug(1, "%s %d", __FUNCTION__, retval);
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
return retval; return retval;
// fail: fail:
// retval = 0; retval = 0;
// goto done; goto done;
} }

View file

@ -88,6 +88,11 @@
#define TIMEOUT_XML_FMT "<confirm-timeout>%u</confirm-timeout>" #define TIMEOUT_XML_FMT "<confirm-timeout>%u</confirm-timeout>"
/*! Connect to internal netconf socket /*! Connect to internal netconf socket
*
* @param[in] h Clixon handle
* @param[out] sockp Socket
* @retval 0 OK
* @retval -1 Error
*/ */
int int
clicon_rpc_connect(clicon_handle h, clicon_rpc_connect(clicon_handle h,
@ -133,10 +138,13 @@ clicon_rpc_connect(clicon_handle h,
} }
/*! Connect to backend or use cached socket and send RPC /*! Connect to backend or use cached socket and send RPC
* @param[in] h Clixonhandle *
* @param[in] h Clixon handle
* @param[in] msg Encoded message * @param[in] msg Encoded message
* @param[out] xret Returned data as netconf xml tree. * @param[out] xret Returned data as netconf xml tree.
* @param[out] eof Set if eof encountered * @param[out] eof Set if eof encountered
* @retval 0 OK
* @retval -1 Error
*/ */
static int static int
clicon_rpc_msg_once(clicon_handle h, clicon_rpc_msg_once(clicon_handle h,
@ -168,9 +176,12 @@ clicon_rpc_msg_once(clicon_handle h,
} }
/*! Send internal netconf rpc from client to backend /*! Send internal netconf rpc from client to backend
*
* @param[in] h CLICON handle * @param[in] h CLICON handle
* @param[in] msg Encoded message. Deallocate with free * @param[in] msg Encoded message. Deallocate with free
* @param[out] xret0 Return value from backend as xml tree. Free w xml_free * @param[out] xret0 Return value from backend as xml tree. Free w xml_free
* @retval 0 OK
* @retval -1 Error
* @note xret is populated with yangspec according to standard handle yangspec * @note xret is populated with yangspec according to standard handle yangspec
* @note side-effect, a socket created here is cached * @note side-effect, a socket created here is cached
* @see clicon_rpc_msg_persistent * @see clicon_rpc_msg_persistent
@ -242,11 +253,14 @@ clicon_rpc_msg(clicon_handle h,
} }
/*! Send internal netconf rpc from client to backend and return a persistent socket /*! Send internal netconf rpc from client to backend and return a persistent socket
*
* @param[in] h CLICON handle * @param[in] h CLICON handle
* @param[in] msg Encoded message. Deallocate with free * @param[in] msg Encoded message. Deallocate with free
* @param[out] xret0 Return value from backend as xml tree. Free w xml_free * @param[out] xret0 Return value from backend as xml tree. Free w xml_free
* @param[out] sock0 If pointer exists, do not close socket to backend on success * @param[out] sock0 If pointer exists, do not close socket to backend on success
* and return it here. For keeping a notify socket open * and return it here. For keeping a notify socket open
* @retval 0 OK
* @retval -1 Error
* @note xret is populated with yangspec according to standard handle yangspec * @note xret is populated with yangspec according to standard handle yangspec
*/ */
int int
@ -311,6 +325,7 @@ clicon_rpc_msg_persistent(clicon_handle h,
} }
/*! Check if there is a valid (cached) session-id. If not, send a hello request to backend /*! Check if there is a valid (cached) session-id. If not, send a hello request to backend
*
* Session-ids survive TCP sessions that are created for each message sent to the backend. * Session-ids survive TCP sessions that are created for each message sent to the backend.
* Clients use two approaches, either: * Clients use two approaches, either:
* (1) Once at the beginning of the session. Netconf and restconf does this * (1) Once at the beginning of the session. Netconf and restconf does this
@ -329,7 +344,7 @@ session_id_check(clicon_handle h,
uint32_t id; uint32_t id;
if (clicon_session_id_get(h, &id) < 0){ /* Not set yet */ if (clicon_session_id_get(h, &id) < 0){ /* Not set yet */
if (clicon_hello_req(h, &id) < 0) if (clicon_hello_req(h, NULL, NULL, &id) < 0)
goto done; goto done;
clicon_session_id_set(h, id); clicon_session_id_set(h, id);
} }
@ -345,6 +360,8 @@ session_id_check(clicon_handle h,
* @param[in] xmlstr XML netconf tree as string * @param[in] xmlstr XML netconf tree as string
* @param[out] xret Return XML netconf tree, error or OK (need to be freed) * @param[out] xret Return XML netconf tree, error or OK (need to be freed)
* @param[out] sp Socket pointer for notification, otherwise NULL * @param[out] sp Socket pointer for notification, otherwise NULL
* @retval 0 OK
* @retval -1 Error
* @code * @code
* cxobj *xret = NULL; * cxobj *xret = NULL;
* int s = -1; * int s = -1;
@ -383,6 +400,7 @@ clicon_rpc_netconf(clicon_handle h,
} }
/*! Generic xml netconf clicon rpc /*! Generic xml netconf clicon rpc
*
* Want to go over to use netconf directly between client and server,... * Want to go over to use netconf directly between client and server,...
* @param[in] h clicon handle * @param[in] h clicon handle
* @param[in] xml XML netconf tree * @param[in] xml XML netconf tree
@ -397,7 +415,6 @@ clicon_rpc_netconf(clicon_handle h,
* err; * err;
* xml_free(xret); * xml_free(xret);
* @endcode * @endcode
* @see clicon_rpc_netconf xml as string instead of tree * @see clicon_rpc_netconf xml as string instead of tree
*/ */
int int
@ -456,6 +473,7 @@ clicon_rpc_netconf_xml(clicon_handle h,
} }
/*! Get database configuration /*! Get database configuration
*
* Same as clicon_proto_change just with a cvec instead of lvec * Same as clicon_proto_change just with a cvec instead of lvec
* @param[in] h CLICON handle * @param[in] h CLICON handle
* @param[in] username If NULL, use default * @param[in] username If NULL, use default
@ -490,7 +508,7 @@ clicon_rpc_netconf_xml(clicon_handle h,
*/ */
int int
clicon_rpc_get_config(clicon_handle h, clicon_rpc_get_config(clicon_handle h,
char *username, char *username, // XXX: why is this only rpc call with username parameter?
char *db, char *db,
char *xpath, char *xpath,
cvec *nsc, cvec *nsc,
@ -510,16 +528,20 @@ clicon_rpc_get_config(clicon_handle h,
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
if ((cb = cbuf_new()) == NULL) if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done; goto done;
}
cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE); cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
if (username == NULL) if (username == NULL)
username = clicon_username_get(h); username = clicon_username_get(h);
if (username != NULL) if (username != NULL){
cprintf(cb, " username=\"%s\"", username); cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
}
cprintf(cb, " xmlns:%s=\"%s\"", cprintf(cb, " xmlns:%s=\"%s\"",
NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE); NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, "><get-config><source><%s/></source>", db); cprintf(cb, "><get-config><source><%s/></source>", db);
if (xpath && strlen(xpath)){ if (xpath && strlen(xpath)){
cprintf(cb, "<%s:filter %s:type=\"xpath\" %s:select=\"%s\"", cprintf(cb, "<%s:filter %s:type=\"xpath\" %s:select=\"%s\"",
@ -591,6 +613,7 @@ clicon_rpc_get_config(clicon_handle h,
} }
/*! Send database entries as XML to backend daemon /*! Send database entries as XML to backend daemon
*
* @param[in] h CLICON handle * @param[in] h CLICON handle
* @param[in] db Name of database * @param[in] db Name of database
* @param[in] op Operation on database item: OP_MERGE, OP_REPLACE * @param[in] op Operation on database item: OP_MERGE, OP_REPLACE
@ -620,13 +643,17 @@ clicon_rpc_edit_config(clicon_handle h,
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
if ((cb = cbuf_new()) == NULL) if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done; goto done;
}
cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE); cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
cprintf(cb, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE); cprintf(cb, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
if ((username = clicon_username_get(h)) != NULL) if ((username = clicon_username_get(h)) != NULL){
cprintf(cb, " username=\"%s\"", username); cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
}
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, "><edit-config><target><%s/></target>", db); cprintf(cb, "><edit-config><target><%s/></target>", db);
cprintf(cb, "<default-operation>%s</default-operation>", cprintf(cb, "<default-operation>%s</default-operation>",
xml_operation2str(op)); xml_operation2str(op));
@ -653,6 +680,7 @@ clicon_rpc_edit_config(clicon_handle h,
} }
/*! Send a request to backend to copy a file from one location to another /*! Send a request to backend to copy a file from one location to another
*
* Note this assumes the backend can access these files and (usually) assumes * Note this assumes the backend can access these files and (usually) assumes
* clients and servers have the access to the same filesystem. * clients and servers have the access to the same filesystem.
* @param[in] h CLICON handle * @param[in] h CLICON handle
@ -676,17 +704,25 @@ clicon_rpc_copy_config(clicon_handle h,
cxobj *xerr; cxobj *xerr;
char *username; char *username;
uint32_t session_id; uint32_t session_id;
cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
username = clicon_username_get(h); if ((cb = cbuf_new()) == NULL){
if ((msg = clicon_msg_encode(session_id, clicon_err(OE_XML, errno, "cbuf_new");
"<rpc xmlns=\"%s\" username=\"%s\" %s>" goto done;
"<copy-config><source><%s/></source><target><%s/></target></copy-config></rpc>", }
NETCONF_BASE_NAMESPACE, cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
username?username:"", cprintf(cb, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
NETCONF_MESSAGE_ID_ATTR, if ((username = clicon_username_get(h)) != NULL){
db1, db2)) == NULL) cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
}
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, ">");
cprintf(cb, "<copy-config><source><%s/></source><target><%s/></target></copy-config></rpc>",
db1, db2);
if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done; goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0) if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done; goto done;
@ -696,6 +732,8 @@ clicon_rpc_copy_config(clicon_handle h,
} }
retval = 0; retval = 0;
done: done:
if (cb)
cbuf_free(cb);
if (xret) if (xret)
xml_free(xret); xml_free(xret);
if (msg) if (msg)
@ -704,6 +742,7 @@ clicon_rpc_copy_config(clicon_handle h,
} }
/*! Send a request to backend to delete a config database /*! Send a request to backend to delete a config database
*
* @param[in] h CLICON handle * @param[in] h CLICON handle
* @param[in] db database, eg "running" * @param[in] db database, eg "running"
* @retval 0 OK * @retval 0 OK
@ -723,17 +762,25 @@ clicon_rpc_delete_config(clicon_handle h,
cxobj *xerr; cxobj *xerr;
char *username; char *username;
uint32_t session_id; uint32_t session_id;
cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
username = clicon_username_get(h); if ((cb = cbuf_new()) == NULL){
if ((msg = clicon_msg_encode(session_id, clicon_err(OE_XML, errno, "cbuf_new");
"<rpc xmlns=\"%s\" username=\"%s\" %s>" goto done;
"<edit-config><target><%s/></target><default-operation>none</default-operation><config operation=\"delete\"/></edit-config></rpc>", }
NETCONF_BASE_NAMESPACE, cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
username?username:"", cprintf(cb, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
NETCONF_MESSAGE_ID_ATTR, if ((username = clicon_username_get(h)) != NULL){
db)) == NULL) cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
}
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, ">");
cprintf(cb, "<edit-config><target><%s/></target><default-operation>none</default-operation><config operation=\"delete\"/></edit-config>", db);
cprintf(cb, "</rpc>");
if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done; goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0) if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done; goto done;
@ -743,6 +790,8 @@ clicon_rpc_delete_config(clicon_handle h,
} }
retval = 0; retval = 0;
done: done:
if (cb)
cbuf_free(cb);
if (xret) if (xret)
xml_free(xret); xml_free(xret);
if (msg) if (msg)
@ -766,17 +815,25 @@ clicon_rpc_lock(clicon_handle h,
cxobj *xerr; cxobj *xerr;
char *username; char *username;
uint32_t session_id; uint32_t session_id;
cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
username = clicon_username_get(h); if ((cb = cbuf_new()) == NULL){
if ((msg = clicon_msg_encode(session_id, clicon_err(OE_XML, errno, "cbuf_new");
"<rpc xmlns=\"%s\" username=\"%s\" %s>" goto done;
"<lock><target><%s/></target></lock></rpc>", }
NETCONF_BASE_NAMESPACE, cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
username?username:"", cprintf(cb, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
NETCONF_MESSAGE_ID_ATTR, if ((username = clicon_username_get(h)) != NULL){
db)) == NULL) cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
}
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, ">");
cprintf(cb, "<lock><target><%s/></target></lock>", db);
cprintf(cb, "</rpc>");
if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done; goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0) if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done; goto done;
@ -786,6 +843,8 @@ clicon_rpc_lock(clicon_handle h,
} }
retval = 0; retval = 0;
done: done:
if (cb)
cbuf_free(cb);
if (xret) if (xret)
xml_free(xret); xml_free(xret);
if (msg) if (msg)
@ -809,17 +868,25 @@ clicon_rpc_unlock(clicon_handle h,
cxobj *xerr; cxobj *xerr;
char *username; char *username;
uint32_t session_id; uint32_t session_id;
cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
username = clicon_username_get(h); if ((cb = cbuf_new()) == NULL){
if ((msg = clicon_msg_encode(session_id, clicon_err(OE_XML, errno, "cbuf_new");
"<rpc xmlns=\"%s\" username=\"%s\" %s>" goto done;
"<unlock><target><%s/></target></unlock></rpc>", }
NETCONF_BASE_NAMESPACE, cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
username?username:"", cprintf(cb, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
NETCONF_MESSAGE_ID_ATTR, if ((username = clicon_username_get(h)) != NULL){
db)) == NULL) cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
}
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, ">");
cprintf(cb, "<unlock><target><%s/></target></unlock>", db);
cprintf(cb, "</rpc>");
if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done; goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0) if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done; goto done;
@ -829,6 +896,8 @@ clicon_rpc_unlock(clicon_handle h,
} }
retval = 0; retval = 0;
done: done:
if (cb)
cbuf_free(cb);
if (xret) if (xret)
xml_free(xret); xml_free(xret);
if (msg) if (msg)
@ -846,8 +915,8 @@ clicon_rpc_unlock(clicon_handle h,
* @param[in] defaults Value of the with-defaults mode, rfc6243, or NULL * @param[in] defaults Value of the with-defaults mode, rfc6243, or NULL
* @param[out] xt XML tree. Free with xml_free. * @param[out] xt XML tree. Free with xml_free.
* Either <config> or <rpc-error>. * Either <config> or <rpc-error>.
* @retval 0 OK * @retval 0 OK
* @retval -1 Error, fatal or xml * @retval -1 Error, fatal or xml
* @note if xpath is set but namespace is NULL, the default, netconf base * @note if xpath is set but namespace is NULL, the default, netconf base
* namespace will be used which is most probably wrong. * namespace will be used which is most probably wrong.
* @code * @code
@ -894,21 +963,30 @@ clicon_rpc_get(clicon_handle h,
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
if ((cb = cbuf_new()) == NULL) if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done; goto done;
cprintf(cb, "<rpc xmlns=\"%s\" ", NETCONF_BASE_NAMESPACE); }
if ((username = clicon_username_get(h)) != NULL) cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
cprintf(cb, " username=\"%s\"", username); cprintf(cb, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
cprintf(cb, " xmlns:%s=\"%s\"", if ((username = clicon_username_get(h)) != NULL){
NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE); cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
}
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, "><get"); cprintf(cb, "><get");
/* Clixon extension, content=all,config, or nonconfig */ /* Clixon extension, content=all,config, or nonconfig */
if ((int)content != -1) if ((int)content != -1)
cprintf(cb, " content=\"%s\"", netconf_content_int2str(content)); cprintf(cb, " %s:content=\"%s\" xmlns:%s=\"%s\"",
CLIXON_LIB_PREFIX,
netconf_content_int2str(content),
CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
/* Clixon extension, depth=<level> */ /* Clixon extension, depth=<level> */
if (depth != -1) if (depth != -1)
cprintf(cb, " depth=\"%d\"", depth); cprintf(cb, " %s:depth=\"%d\" xmlns:%s=\"%s\"",
CLIXON_LIB_PREFIX,
depth,
CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
cprintf(cb, ">"); /* get */ cprintf(cb, ">"); /* get */
/* If xpath, add a filter */ /* If xpath, add a filter */
if (xpath && strlen(xpath)) { if (xpath && strlen(xpath)) {
@ -980,6 +1058,7 @@ clicon_rpc_get(clicon_handle h,
} }
/*! Get database configuration and state data collection /*! Get database configuration and state data collection
*
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] xpath To identify a list/leaf-list * @param[in] xpath To identify a list/leaf-list
* @param[in] namespace Namespace associated w xpath * @param[in] namespace Namespace associated w xpath
@ -1033,21 +1112,31 @@ clicon_rpc_get_pageable_list(clicon_handle h,
} }
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
if ((cb = cbuf_new()) == NULL) if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done; goto done;
}
cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE); cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
if ((username = clicon_username_get(h)) != NULL) if ((username = clicon_username_get(h)) != NULL){
cprintf(cb, " username=\"%s\"", username); cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
}
cprintf(cb, " xmlns:%s=\"%s\"", cprintf(cb, " xmlns:%s=\"%s\"",
NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE); NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, "><get "); cprintf(cb, "><get ");
/* Clixon extension, content=all,config, or nonconfig */ /* Clixon extension, content=all,config, or nonconfig */
if ((int)content != -1) if ((int)content != -1)
cprintf(cb, " content=\"%s\"", netconf_content_int2str(content)); cprintf(cb, " %s:content=\"%s\" xmlns:%s=\"%s\"",
CLIXON_LIB_PREFIX,
netconf_content_int2str(content),
CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
/* Clixon extension, depth=<level> */ /* Clixon extension, depth=<level> */
if (depth != -1) if (depth != -1)
cprintf(cb, " depth=\"%d\"", depth); cprintf(cb, " %s:depth=\"%d\" xmlns:%s=\"%s\"",
CLIXON_LIB_PREFIX,
depth,
CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
/* declare lp prefix in get, so sub-elements dont need to */ /* declare lp prefix in get, so sub-elements dont need to */
cprintf(cb, ">"); /* get */ cprintf(cb, ">"); /* get */
/* If xpath, add a filter */ /* If xpath, add a filter */
@ -1152,14 +1241,25 @@ clicon_rpc_close_session(clicon_handle h)
char *username; char *username;
uint32_t session_id; uint32_t session_id;
int s; int s;
cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
username = clicon_username_get(h); if ((cb = cbuf_new()) == NULL){
if ((msg = clicon_msg_encode(session_id, clicon_err(OE_XML, errno, "cbuf_new");
"<rpc xmlns=\"%s\" username=\"%s\" %s><close-session/></rpc>", goto done;
NETCONF_BASE_NAMESPACE, username?username:"", }
NETCONF_MESSAGE_ID_ATTR)) == NULL) cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
cprintf(cb, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
if ((username = clicon_username_get(h)) != NULL){
cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
}
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, ">");
cprintf(cb, "<close-session/>");
cprintf(cb, "</rpc>");
if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done; goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0) if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done; goto done;
@ -1173,6 +1273,8 @@ clicon_rpc_close_session(clicon_handle h)
} }
retval = 0; retval = 0;
done: done:
if (cb)
cbuf_free(cb);
if (xret) if (xret)
xml_free(xret); xml_free(xret);
if (msg) if (msg)
@ -1181,10 +1283,11 @@ clicon_rpc_close_session(clicon_handle h)
} }
/*! Kill other user sessions /*! Kill other user sessions
*
* @param[in] h CLICON handle * @param[in] h CLICON handle
* @param[in] session_id Session id of other user session * @param[in] session_id Session id of other user session
* @retval 0 OK * @retval 0 OK
* @retval -1 Error and logged to syslog * @retval -1 Error and logged to syslog
*/ */
int int
clicon_rpc_kill_session(clicon_handle h, clicon_rpc_kill_session(clicon_handle h,
@ -1196,16 +1299,25 @@ clicon_rpc_kill_session(clicon_handle h,
cxobj *xerr; cxobj *xerr;
char *username; char *username;
uint32_t my_session_id; /* Not the one to kill */ uint32_t my_session_id; /* Not the one to kill */
cbuf *cb = NULL;
if (session_id_check(h, &my_session_id) < 0) if (session_id_check(h, &my_session_id) < 0)
goto done; goto done;
username = clicon_username_get(h); if ((cb = cbuf_new()) == NULL){
if ((msg = clicon_msg_encode(my_session_id, clicon_err(OE_XML, errno, "cbuf_new");
"<rpc xmlns=\"%s\" username=\"%s\" %s><kill-session><session-id>%u</session-id></kill-session></rpc>", goto done;
NETCONF_BASE_NAMESPACE, }
username?username:"", cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
NETCONF_MESSAGE_ID_ATTR, cprintf(cb, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
session_id)) == NULL) if ((username = clicon_username_get(h)) != NULL){
cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
}
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, ">");
cprintf(cb, "<kill-session><session-id>%u</session-id></kill-session>", session_id);
cprintf(cb, "</rpc>");
if ((msg = clicon_msg_encode(my_session_id, "%s", cbuf_get(cb))) == NULL)
goto done; goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0) if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done; goto done;
@ -1215,6 +1327,8 @@ clicon_rpc_kill_session(clicon_handle h,
} }
retval = 0; retval = 0;
done: done:
if (cb)
cbuf_free(cb);
if (xret) if (xret)
xml_free(xret); xml_free(xret);
if (msg) if (msg)
@ -1223,6 +1337,7 @@ clicon_rpc_kill_session(clicon_handle h,
} }
/*! Send validate request to backend daemon /*! Send validate request to backend daemon
*
* @param[in] h CLICON handle * @param[in] h CLICON handle
* @param[in] db Name of database * @param[in] db Name of database
* @retval 1 OK * @retval 1 OK
@ -1240,16 +1355,25 @@ clicon_rpc_validate(clicon_handle h,
cxobj *xerr; cxobj *xerr;
char *username; char *username;
uint32_t session_id; uint32_t session_id;
cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
username = clicon_username_get(h); if ((cb = cbuf_new()) == NULL){
if ((msg = clicon_msg_encode(session_id, clicon_err(OE_XML, errno, "cbuf_new");
"<rpc xmlns=\"%s\" username=\"%s\" %s><validate><source><%s/></source></validate></rpc>", goto done;
NETCONF_BASE_NAMESPACE, }
username?username:"", cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
NETCONF_MESSAGE_ID_ATTR, cprintf(cb, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
db)) == NULL) if ((username = clicon_username_get(h)) != NULL){
cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
}
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, ">");
cprintf(cb, "<validate><source><%s/></source></validate>", db);
cprintf(cb, "</rpc>");
if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done; goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0) if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done; goto done;
@ -1260,6 +1384,8 @@ clicon_rpc_validate(clicon_handle h,
} }
retval = 1; retval = 1;
done: done:
if (cb)
cbuf_free(cb);
if (msg) if (msg)
free(msg); free(msg);
if (xret) if (xret)
@ -1268,6 +1394,7 @@ clicon_rpc_validate(clicon_handle h,
} }
/*! Commit changes send a commit request to backend daemon /*! Commit changes send a commit request to backend daemon
*
* @param[in] h CLICON handle * @param[in] h CLICON handle
* @param[in] confirmed If set, send commit/confirmed * @param[in] confirmed If set, send commit/confirmed
* @param[in] cancel If set, send cancel-commit * @param[in] cancel If set, send cancel-commit
@ -1297,6 +1424,7 @@ clicon_rpc_commit(clicon_handle h,
char *persist_id_xml = NULL; char *persist_id_xml = NULL;
char *persist_xml = NULL; char *persist_xml = NULL;
char *timeout_xml = NULL; char *timeout_xml = NULL;
cbuf *cb = NULL;
if (persist_id) { if (persist_id) {
if ((persist_id_xml = malloc(strlen(persist_id) + strlen(PERSIST_ID_XML_FMT) + 1)) == NULL) { if ((persist_id_xml = malloc(strlen(persist_id) + strlen(PERSIST_ID_XML_FMT) + 1)) == NULL) {
@ -1325,32 +1453,34 @@ clicon_rpc_commit(clicon_handle h,
} }
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
username = clicon_username_get(h);
if (cancel) { if ((cb = cbuf_new()) == NULL){
msg = clicon_msg_encode(session_id, clicon_err(OE_XML, errno, "cbuf_new");
"<rpc xmlns=\"%s\" username=\"%s\" %s><cancel-commit>%s</cancel-commit></rpc>", goto done;
NETCONF_BASE_NAMESPACE,
username ? username : "",
NETCONF_MESSAGE_ID_ATTR,
persist_id ? persist_id_xml : "");
} else if (confirmed) {
msg = clicon_msg_encode(session_id,
"<rpc xmlns=\"%s\" username=\"%s\" %s><commit><confirmed/>%s%s%s</commit></rpc>",
NETCONF_BASE_NAMESPACE,
username ? username : "",
NETCONF_MESSAGE_ID_ATTR,
timeout ? timeout_xml : "",
persist_id ? persist_id_xml : "",
persist ? persist_xml : "");
} else {
msg = clicon_msg_encode(session_id,
"<rpc xmlns=\"%s\" username=\"%s\" %s><commit>%s</commit></rpc>",
NETCONF_BASE_NAMESPACE,
username ? username : "",
NETCONF_MESSAGE_ID_ATTR,
persist ? persist_xml : "");
} }
if (msg == NULL) cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
cprintf(cb, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
if ((username = clicon_username_get(h)) != NULL){
cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
}
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, ">");
if (cancel) {
cprintf(cb, "<cancel-commit>%s</cancel-commit>",
persist_id ? persist_id_xml : "");
}
else if (confirmed) {
cprintf(cb, "<commit><confirmed/>%s%s%s</commit>",
timeout ? timeout_xml : "",
persist_id ? persist_id_xml : "",
persist ? persist_xml : "");
} else {
cprintf(cb, "<commit>%s</commit>",
persist ? persist_xml : "");
}
cprintf(cb, "</rpc>");
if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done; goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0) if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done; goto done;
@ -1361,6 +1491,8 @@ clicon_rpc_commit(clicon_handle h,
} }
retval = 1; retval = 1;
done: done:
if (cb)
cbuf_free(cb);
if (xret) if (xret)
xml_free(xret); xml_free(xret);
if (msg) if (msg)
@ -1375,6 +1507,7 @@ clicon_rpc_commit(clicon_handle h,
} }
/*! Discard all changes in candidate / revert to running /*! Discard all changes in candidate / revert to running
*
* @param[in] h CLICON handle * @param[in] h CLICON handle
* @retval 0 OK * @retval 0 OK
* @retval -1 Error and logged to syslog * @retval -1 Error and logged to syslog
@ -1388,15 +1521,25 @@ clicon_rpc_discard_changes(clicon_handle h)
cxobj *xerr; cxobj *xerr;
char *username; char *username;
uint32_t session_id; uint32_t session_id;
cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
username = clicon_username_get(h); if ((cb = cbuf_new()) == NULL){
if ((msg = clicon_msg_encode(session_id, clicon_err(OE_XML, errno, "cbuf_new");
"<rpc xmlns=\"%s\" username=\"%s\" %s><discard-changes/></rpc>", goto done;
NETCONF_BASE_NAMESPACE, }
username?username:"", cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
NETCONF_MESSAGE_ID_ATTR)) == NULL) cprintf(cb, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
if ((username = clicon_username_get(h)) != NULL){
cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
}
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, ">");
cprintf(cb, "<discard-changes/>");
cprintf(cb, "</rpc>");
if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done; goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0) if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done; goto done;
@ -1406,6 +1549,8 @@ clicon_rpc_discard_changes(clicon_handle h)
} }
retval = 0; retval = 0;
done: done:
if (cb)
cbuf_free(cb);
if (xret) if (xret)
xml_free(xret); xml_free(xret);
if (msg) if (msg)
@ -1414,13 +1559,13 @@ clicon_rpc_discard_changes(clicon_handle h)
} }
/*! Create a new notification subscription /*! Create a new notification subscription
*
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param{in] stream name of notificatio/log stream (CLICON is predefined) * @param{in] stream name of notificatio/log stream (CLICON is predefined)
* @param{in] filter message filter, eg xpath for xml notifications * @param{in] filter message filter, eg xpath for xml notifications
* @param[out] s0 socket returned where notification mesages will appear * @param[out] s0 socket returned where notification mesages will appear
* @retval 0 OK * @retval 0 OK
* @retval -1 Error and logged to syslog * @retval -1 Error and logged to syslog
* @note When using netconf create-subsrciption,status and format is not supported * @note When using netconf create-subsrciption,status and format is not supported
*/ */
int int
@ -1435,20 +1580,31 @@ clicon_rpc_create_subscription(clicon_handle h,
cxobj *xerr; cxobj *xerr;
char *username; char *username;
uint32_t session_id; uint32_t session_id;
cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
username = clicon_username_get(h); if ((cb = cbuf_new()) == NULL){
if ((msg = clicon_msg_encode(session_id, clicon_err(OE_XML, errno, "cbuf_new");
"<rpc xmlns=\"%s\" username=\"%s\" %s><create-subscription xmlns=\"%s\">" goto done;
"<stream>%s</stream>" }
"<filter type=\"xpath\" select=\"%s\" />" cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
"</create-subscription></rpc>", cprintf(cb, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
NETCONF_BASE_NAMESPACE, if ((username = clicon_username_get(h)) != NULL){
username?username:"", cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
NETCONF_MESSAGE_ID_ATTR, cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
EVENT_RFC5277_NAMESPACE, }
stream?stream:"", filter?filter:"")) == NULL) cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, ">");
cprintf(cb, "<create-subscription xmlns=\"%s\">"
"<stream>%s</stream>"
"<filter type=\"xpath\" select=\"%s\" />"
"</create-subscription>",
EVENT_RFC5277_NAMESPACE,
stream?stream:"",
filter?filter:"");
cprintf(cb, "</rpc>");
if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done; goto done;
if (clicon_rpc_msg_persistent(h, msg, &xret, s0) < 0) if (clicon_rpc_msg_persistent(h, msg, &xret, s0) < 0)
goto done; goto done;
@ -1458,6 +1614,8 @@ clicon_rpc_create_subscription(clicon_handle h,
} }
retval = 0; retval = 0;
done: done:
if (cb)
cbuf_free(cb);
if (xret) if (xret)
xml_free(xret); xml_free(xret);
if (msg) if (msg)
@ -1466,6 +1624,7 @@ clicon_rpc_create_subscription(clicon_handle h,
} }
/*! Send a debug request to backend server /*! Send a debug request to backend server
*
* @param[in] h CLICON handle * @param[in] h CLICON handle
* @param[in] level Debug level * @param[in] level Debug level
* @retval 0 OK * @retval 0 OK
@ -1481,17 +1640,26 @@ clicon_rpc_debug(clicon_handle h,
cxobj *xerr; cxobj *xerr;
char *username; char *username;
uint32_t session_id; uint32_t session_id;
cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
username = clicon_username_get(h); if ((cb = cbuf_new()) == NULL){
if ((msg = clicon_msg_encode(session_id, clicon_err(OE_XML, errno, "cbuf_new");
"<rpc xmlns=\"%s\" username=\"%s\" %s><debug xmlns=\"%s\"><level>%d</level></debug></rpc>", goto done;
NETCONF_BASE_NAMESPACE, }
username?username:"", cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
NETCONF_MESSAGE_ID_ATTR, cprintf(cb, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
CLIXON_LIB_NS, if ((username = clicon_username_get(h)) != NULL){
level)) == NULL) cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
}
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, ">");
cprintf(cb, "<debug xmlns=\"%s\"><level>%d</level></debug>", CLIXON_LIB_NS, level);
cprintf(cb, "</rpc>");
if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done; goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0) if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done; goto done;
@ -1505,6 +1673,8 @@ clicon_rpc_debug(clicon_handle h,
} }
retval = 0; retval = 0;
done: done:
if (cb)
cbuf_free(cb);
if (msg) if (msg)
free(msg); free(msg);
if (xret) if (xret)
@ -1532,17 +1702,29 @@ clicon_rpc_restconf_debug(clicon_handle h,
cxobj *xerr; cxobj *xerr;
char *username; char *username;
uint32_t session_id; uint32_t session_id;
cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
username = clicon_username_get(h); if ((cb = cbuf_new()) == NULL){
if ((msg = clicon_msg_encode(session_id, clicon_err(OE_XML, errno, "cbuf_new");
"<rpc xmlns=\"%s\" username=\"%s\" %s><edit-config><target><candidate/></target><config><restconf xmlns=\"%s\"><debug>%d</debug></restconf></config></edit-config></rpc>", goto done;
NETCONF_BASE_NAMESPACE, }
username?username:"", cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
NETCONF_MESSAGE_ID_ATTR, cprintf(cb, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
CLIXON_RESTCONF_NS, if ((username = clicon_username_get(h)) != NULL){
level)) == NULL) cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
}
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, ">");
cprintf(cb, "<edit-config><target><candidate/></target><config>");
cprintf(cb, "<restconf xmlns=\"%s\"><debug>%d</debug></restconf>",
CLIXON_RESTCONF_NS,
level);
cprintf(cb, "</config></edit-config>");
cprintf(cb, "</rpc>");
if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done; goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0) if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done; goto done;
@ -1557,6 +1739,8 @@ clicon_rpc_restconf_debug(clicon_handle h,
if ((retval = clicon_rpc_commit(h, 0, 0, 0, NULL, NULL)) < 1) if ((retval = clicon_rpc_commit(h, 0, 0, 0, NULL, NULL)) < 1)
goto done; goto done;
done: done:
if (cb)
cbuf_free(cb);
if (msg) if (msg)
free(msg); free(msg);
if (xret) if (xret)
@ -1564,17 +1748,25 @@ clicon_rpc_restconf_debug(clicon_handle h,
return retval; return retval;
} }
/*! Send a hello request to the backend server /*! Send a hello request to the backend server on INTERNAL netconf connection
* @param[in] h CLICON handle *
* @param[in] level Debug level * @param[in] h Clixon handle
* @retval 0 OK * @param[in] transport RFC 6022 transport.
* @retval -1 Error and logged to syslog * @param[in] source_host RFC 6022 source-host
* @param[out] id Session id returned by backend
* @retval 0 OK
* @retval -1 Error and logged to syslog
* @note this is internal netconf to backend, not northbound to user client * @note this is internal netconf to backend, not northbound to user client
* @note this deviates from RFC6241 slightly in that it waits for a reply, the RFC does not * @note this deviates from RFC6241 slightly in that it waits for a reply, the RFC does not
* stipulate that. * stipulate that.
* @note transport is an identity defined in RFC6022 with added values in clixon-lib.yang for clixon,
* and should in those cases be prefixed with the localname "cl:",
* Example: cl:cli, cl:restconf, cl:netconf
*/ */
int int
clicon_hello_req(clicon_handle h, clicon_hello_req(clicon_handle h,
char *transport,
char *source_host,
uint32_t *id) uint32_t *id)
{ {
int retval = -1; int retval = -1;
@ -1585,12 +1777,39 @@ clicon_hello_req(clicon_handle h,
char *username; char *username;
char *b; char *b;
int ret; int ret;
cbuf *cb = NULL;
int clixon_lib = 0;
username = clicon_username_get(h); if ((cb = cbuf_new()) == NULL){
if ((msg = clicon_msg_encode(0, "<hello username=\"%s\" xmlns=\"%s\"><capabilities><capability>%s</capability></capabilities></hello>", clicon_err(OE_XML, errno, "cbuf_new");
username?username:"", goto done;
NETCONF_BASE_NAMESPACE, }
NETCONF_BASE_CAPABILITY_1_1)) == NULL) cprintf(cb, "<hello xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
if ((username = clicon_username_get(h)) != NULL){
cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
clixon_lib++;
}
/* RFC 6022 session parameters transport and source-host */
if (transport == NULL)
clicon_data_get(h, "session-transport", &transport);
if (transport){
cprintf(cb, " %s:transport=\"%s\"", CLIXON_LIB_PREFIX, transport);
clixon_lib++;
}
if (source_host == NULL)
clicon_data_get(h, "session-source-host", &source_host);
if (source_host){
cprintf(cb, " %s:source-host=\"%s\"", CLIXON_LIB_PREFIX, source_host);
clixon_lib++;
}
if (clixon_lib)
cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
cprintf(cb, ">");
cprintf(cb, "<capabilities><capability>%s</capability></capabilities>",
NETCONF_BASE_CAPABILITY_1_1);
cprintf(cb, "</hello>");
if ((msg = clicon_msg_encode(0, "%s", cbuf_get(cb))) == NULL)
goto done; goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0) if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done; goto done;
@ -1609,6 +1828,8 @@ clicon_hello_req(clicon_handle h,
} }
retval = 0; retval = 0;
done: done:
if (cb)
cbuf_free(cb);
if (msg) if (msg)
free(msg); free(msg);
if (xret) if (xret)
@ -1617,6 +1838,7 @@ clicon_hello_req(clicon_handle h,
} }
/*! Send a restart plugin request to backend server /*! Send a restart plugin request to backend server
*
* @param[in] h CLICON handle * @param[in] h CLICON handle
* @param[in] level Debug level * @param[in] level Debug level
* @retval 0 OK * @retval 0 OK
@ -1632,17 +1854,26 @@ clicon_rpc_restart_plugin(clicon_handle h,
cxobj *xerr; cxobj *xerr;
char *username; char *username;
uint32_t session_id; uint32_t session_id;
cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
username = clicon_username_get(h); if ((cb = cbuf_new()) == NULL){
if ((msg = clicon_msg_encode(session_id, clicon_err(OE_XML, errno, "cbuf_new");
"<rpc xmlns=\"%s\" username=\"%s\" %s><restart-plugin xmlns=\"%s\"><plugin>%s</plugin></restart-plugin></rpc>", goto done;
NETCONF_BASE_NAMESPACE, }
username?username:"", cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
NETCONF_MESSAGE_ID_ATTR, cprintf(cb, " xmlns:%s=\"%s\"", NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
CLIXON_LIB_NS, if ((username = clicon_username_get(h)) != NULL){
plugin)) == NULL) cprintf(cb, " %s:username=\"%s\"", CLIXON_LIB_PREFIX, username);
cprintf(cb, " xmlns:%s=\"%s\"", CLIXON_LIB_PREFIX, CLIXON_LIB_NS);
}
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR); /* XXX: use incrementing sequence */
cprintf(cb, ">");
cprintf(cb, "<restart-plugin xmlns=\"%s\"><plugin>%s</plugin></restart-plugin>",
CLIXON_LIB_NS, plugin);
cprintf(cb, "</rpc>");
if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done; goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0) if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done; goto done;
@ -1656,6 +1887,8 @@ clicon_rpc_restart_plugin(clicon_handle h,
} }
retval = 0; retval = 0;
done: done:
if (cb)
cbuf_free(cb);
if (msg) if (msg)
free(msg); free(msg);
if (xret) if (xret)

View file

@ -2332,6 +2332,42 @@ xml_attr_insert2val(char *instr,
return 0; return 0;
} }
/*! Add attribute and value to an xml element as well as namespace mapping
*
* @param[in] xn XML element
* @param[in] name Attribute name
* @param[in] value Attribute value
* @param[in] prefix Attribute prefix, or NULL
* @param[in] namespace Attribute prefix, if NULL do not add namespace mapping
*/
int
xml_add_attr(cxobj *xn,
char *name,
char *value,
char *prefix,
char *namespace)
{
int retval = -1;
cxobj *xa;
char *ns = NULL;
if ((xa = xml_new(name, xn, CX_ATTR)) == NULL)
goto done;
if (prefix && xml_prefix_set(xa, prefix) < 0) /* clixon lib */
goto done;
if (xml_value_set(xa, value) < 0)
goto done;
if (namespace){
if (xml2ns(xn, prefix, &ns) < 0)
goto done;
if (ns == NULL && xmlns_set(xn, prefix, namespace) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Specialization of clicon_debug with xml tree /*! Specialization of clicon_debug with xml tree
* @param[in] level log level, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG. * @param[in] level log level, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG.
* @param[in] x XML tree that is logged without prettyprint * @param[in] x XML tree that is logged without prettyprint

View file

@ -611,15 +611,10 @@ xml_add_default_tag(cxobj *x,
uint16_t flags) uint16_t flags)
{ {
int retval = -1; int retval = -1;
cxobj *xattr;
if (xml_flag(x, flags)) { if (xml_flag(x, flags)) {
/* set default attribute */ /* set default attribute */
if ((xattr = xml_new("default", x, CX_ATTR)) == NULL) if (xml_add_attr(x, "default", "true", IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX, NULL) < 0)
goto done;
if (xml_value_set(xattr, "true") < 0)
goto done;
if (xml_prefix_set(xattr, IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX) < 0)
goto done; goto done;
} }
retval = 0; retval = 0;

View file

@ -648,7 +648,6 @@ xml_namespace_change(cxobj *x,
/* Add prefix to x, if any */ /* Add prefix to x, if any */
if (prefix && xml_prefix_set(x, prefix) < 0) if (prefix && xml_prefix_set(x, prefix) < 0)
goto done; goto done;
} }
ok: ok:
retval = 0; retval = 0;

View file

@ -569,6 +569,7 @@ xml2ns_recurse(cxobj *xt)
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
* @see xml2ns * @see xml2ns
* @see xml_add_attr generic method for adding an attribute
*/ */
int int
xmlns_set(cxobj *x, xmlns_set(cxobj *x,

View file

@ -82,6 +82,12 @@
#include "clixon_xml_map.h" #include "clixon_xml_map.h"
#include "clixon_yang_parse_lib.h" #include "clixon_yang_parse_lib.h"
/*! Create modstate structure
*
* @retval md modstate struct
* @retval NULL Error
* @see modstate_diff_free
*/
modstate_diff_t * modstate_diff_t *
modstate_diff_new(void) modstate_diff_new(void)
{ {
@ -95,6 +101,12 @@ modstate_diff_new(void)
return md; return md;
} }
/*! Free modstate structure
*
* @param[in] md Modstate struct
* @retval 0 OK
* @see modstate_diff_new
*/
int int
modstate_diff_free(modstate_diff_t *md) modstate_diff_free(modstate_diff_t *md)
{ {
@ -111,7 +123,9 @@ modstate_diff_free(modstate_diff_t *md)
/*! Init the Yang module library /*! Init the Yang module library
* *
* Load RFC7895 yang spec, module-set-id, etc. * Load RFC7895 yang spec, module-set-id, etc.
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @retval 0 OK
* @retval -1 Error
* @see netconf_module_load * @see netconf_module_load
*/ */
int int
@ -167,6 +181,13 @@ yang_modules_revision(clicon_handle h)
/*! Actually build the yang modules state XML tree according to RFC8525 /*! Actually build the yang modules state XML tree according to RFC8525
* *
* @param[in] h Clixon handle
* @param[in] yspec
* @param[in] msid
* @param[in] brief
* @param[out] cb
* @retval 0 OK
* @retval -1 Error
* This assumes CLICON_YANG_LIBRARY is enabled * This assumes CLICON_YANG_LIBRARY is enabled
* If also CLICON_MODULE_LIBRARY_RFC7895 is set, module-state is built according to RFC7895 instead * If also CLICON_MODULE_LIBRARY_RFC7895 is set, module-state is built according to RFC7895 instead
* @see RFC8525 * @see RFC8525
@ -277,9 +298,9 @@ yms_build(clicon_handle h,
* @param[in] nsc XML Namespace context for xpath * @param[in] nsc XML Namespace context for xpath
* @param[in] brief Just name, revision and uri (no cache) * @param[in] brief Just name, revision and uri (no cache)
* @param[in,out] xret Existing XML tree, merge x into this * @param[in,out] xret Existing XML tree, merge x into this
* @retval -1 Error (fatal)
* @retval 0 Statedata callback failed
* @retval 1 OK * @retval 1 OK
* @retval 0 Statedata callback failed
* @retval -1 Error (fatal)
* @notes NYI: schema, deviation * @notes NYI: schema, deviation
x +--ro modules-state x +--ro modules-state
x +--ro module-set-id string x +--ro module-set-id string

View file

@ -73,8 +73,8 @@ DATASTORE_TOP="config"
# clixon yang revisions occuring in tests (see eg yang/clixon/Makefile.in) # clixon yang revisions occuring in tests (see eg yang/clixon/Makefile.in)
CLIXON_AUTOCLI_REV="2022-02-11" CLIXON_AUTOCLI_REV="2022-02-11"
CLIXON_LIB_REV="2021-12-05" CLIXON_LIB_REV="2022-12-01"
CLIXON_CONFIG_REV="2022-03-21" CLIXON_CONFIG_REV="2022-12-01"
CLIXON_RESTCONF_REV="2022-08-01" CLIXON_RESTCONF_REV="2022-08-01"
CLIXON_EXAMPLE_REV="2022-11-01" CLIXON_EXAMPLE_REV="2022-11-01"

View file

@ -68,16 +68,13 @@ new "wait backend"
wait_backend wait_backend
new "Retrieving all state via <get> operation" new "Retrieving all state via <get> operation"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get/></rpc>" "<rpc-reply $DEFAULTNS><data><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><capabilities><capability>urn:ietf:params:netconf:base:1.0</capability><capability>urn:ietf:params:netconf:base:1.1</capability>.*<capability>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring</capability>.*</capabilities><datastores><datastore><name>candidate</name></datastore><datastore><name>running</name></datastore></datastores><schemas>.*</schemas></netconf-state></data></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get/></rpc>" "<rpc-reply $DEFAULTNS><data><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><capabilities><capability>urn:ietf:params:netconf:base:1.0</capability><capability>urn:ietf:params:netconf:base:1.1</capability>.*<capability>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring</capability>.*</capabilities><datastores><datastore><name>candidate</name></datastore><datastore><name>running</name></datastore></datastores><schemas>.*</schemas><sessions>.*</sessions></netconf-state></data></rpc-reply>"
# send multiple frames # send multiple frames
rpc=$(chunked_framing "<rpc $DEFAULTNS><lock><target><candidate/></target></lock></rpc>") rpc=$(chunked_framing "<rpc $DEFAULTNS><lock><target><candidate/></target></lock></rpc>")
rpc="${rpc} rpc="${rpc}
$(chunked_framing "<rpc $DEFAULTNS><get><filter type=\"subtree\"><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><datastores><datastore><name>candidate</name></datastore></datastores></netconf-state></filter></get></rpc>")" $(chunked_framing "<rpc $DEFAULTNS><get><filter type=\"subtree\"><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><datastores><datastore><name>candidate</name></datastore></datastores></netconf-state></filter></get></rpc>")"
reply=$(chunked_framing "<rpc-reply $DEFAULTNS><ok/>/rpc-reply")
reply=${reply}$(chunked_framing "<rpc-reply $DEFAULTNS><data><netconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"><datastores><datastore><name>candidate</name><locks><locked-by-session>3</locked-by-session><locked-time>2022-12-23T13:18:57.112204Z</locked-time></locks></datastore></datastores></netconf-state></data></rpc-reply")
new "Get databases with lock" new "Get databases with lock"
ret=$($clixon_netconf -q -f $cfg <<EOF ret=$($clixon_netconf -q -f $cfg <<EOF
$DEFAULTHELLO$rpc $DEFAULTHELLO$rpc
@ -87,7 +84,7 @@ r=$?
if [ $r -ne 0 ]; then if [ $r -ne 0 ]; then
err "0" "$r" err "0" "$r"
fi fi
for expect in "<rpc-reply $DEFAULTNS><ok/></rpc-reply>" "<rpc-reply $DEFAULTNS><data><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><datastores><datastore><name>candidate</name><locks><locked-by-session>[0-9]+</locked-by-session><locked-time>202.*Z</locked-time></locks></datastore></datastores></netconf-state></data></rpc-reply"; do for expect in "<rpc-reply $DEFAULTNS><ok/></rpc-reply>" "<rpc-reply $DEFAULTNS><data><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><datastores><datastore><name>candidate</name><locks><global-lock><locked-by-session>[0-9]+</locked-by-session><locked-time>202.*Z</locked-time></global-lock></locks></datastore></datastores></netconf-state></data></rpc-reply"; do
new "expect:$expect" new "expect:$expect"
match=`echo $ret | grep --null -Eo "$expect"` match=`echo $ret | grep --null -Eo "$expect"`
if [ -z "$match" ]; then if [ -z "$match" ]; then
@ -100,6 +97,10 @@ done
new "Retrieving Schema List via <get> Operation" new "Retrieving Schema List via <get> Operation"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"subtree\"><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas/></netconf-state></filter></get></rpc>" "<rpc-reply $DEFAULTNS><data><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas><schema><identifier>clixon-example</identifier><version>2022-01-01</version><format>yang</format><namespace>urn:example:clixon</namespace><location>NETCONF</location></schema>.*<schema><identifier>clixon-sub</identifier><version>2022-01-01</version><format>yang</format><namespace>urn:example:clixon</namespace><location>NETCONF</location></schema><schema>.*</schemas></netconf-state></data></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"subtree\"><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas/></netconf-state></filter></get></rpc>" "<rpc-reply $DEFAULTNS><data><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas><schema><identifier>clixon-example</identifier><version>2022-01-01</version><format>yang</format><namespace>urn:example:clixon</namespace><location>NETCONF</location></schema>.*<schema><identifier>clixon-sub</identifier><version>2022-01-01</version><format>yang</format><namespace>urn:example:clixon</namespace><location>NETCONF</location></schema><schema>.*</schemas></netconf-state></data></rpc-reply>"
# Session
new "Retrieve Session"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"subtree\"><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><sessions/></netconf-state></filter></get></rpc>" "<rpc-reply $DEFAULTNS><data><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><sessions><session><session-id>[1-9][0-9]*</session-id><transport xmlns:cl=\"http://clicon.org/lib\">cl:netconf</transport><username>.*</username><login-time>.*</login-time><in-rpcs>[0-9][0-9]*</in-rpcs><in-bad-rpcs>[0-9][0-9]*</in-bad-rpcs><out-rpc-errors>[0-9][0-9]*</out-rpc-errors><out-notifications>[0-9][0-9]*</out-notifications></session>.*</sessions></netconf-state></data></rpc-reply>"
# 4.2. Retrieving Schema Instances # 4.2. Retrieving Schema Instances
# From 2b. bar, version 2008-06-1 in YANG format, via get-schema # From 2b. bar, version 2008-06-1 in YANG format, via get-schema
new "Retrieving clixon-example schema instance using id, version, format" new "Retrieving clixon-example schema instance using id, version, format"

View file

@ -43,7 +43,7 @@ YANG_INSTALLDIR = @YANG_INSTALLDIR@
# Note: mirror these to test/config.sh.in # Note: mirror these to test/config.sh.in
YANGSPECS = clixon-config@2022-12-01.yang # 6.1 YANGSPECS = clixon-config@2022-12-01.yang # 6.1
YANGSPECS += clixon-lib@2021-12-05.yang # 5.5 YANGSPECS += clixon-lib@2022-12-01.yang # 6.1
YANGSPECS += clixon-rfc5277@2008-07-01.yang YANGSPECS += clixon-rfc5277@2008-07-01.yang
YANGSPECS += clixon-xml-changelog@2019-03-21.yang YANGSPECS += clixon-xml-changelog@2019-03-21.yang
YANGSPECS += clixon-restconf@2022-08-01.yang # 5.9 YANGSPECS += clixon-restconf@2022-08-01.yang # 5.9

View file

@ -15,6 +15,19 @@ module clixon-lib {
description description
"Clixon Netconf extensions for communication between clients and backend. "Clixon Netconf extensions for communication between clients and backend.
Clixon extends NETCONF for internal use with some internal attributes. These
are not visible for external usage bit belongs to the namespace of this YANG.
The internal attributes are:
- content (also RESTCONF)
- depth (also RESTCONF)
- username
- autocommit
- copystartup
- transport (see RFC6022)
- source-host (see RFC6022)
- objectcreate
- objectexisted
***** BEGIN LICENSE BLOCK ***** ***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC(Netgate) Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC(Netgate)

View file

@ -6,6 +6,9 @@ module clixon-lib {
import ietf-yang-types { import ietf-yang-types {
prefix yang; prefix yang;
} }
import ietf-netconf-monitoring {
prefix ncm;
}
organization organization
"Clicon / Clixon"; "Clicon / Clixon";
@ -13,9 +16,7 @@ module clixon-lib {
"Olof Hagsand <olof@hagsand.se>"; "Olof Hagsand <olof@hagsand.se>";
description description
"Clixon Netconf extensions for communication between clients and backend. "***** BEGIN LICENSE BLOCK *****
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC(Netgate) Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC(Netgate)
@ -42,8 +43,37 @@ module clixon-lib {
the provisions above, a recipient may use your version of this file under 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. the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****"; ***** END LICENSE BLOCK *****
Clixon Netconf extensions for communication between clients and backend.
This scheme adds:
- Added values of RFC6022 transport identityref
- RPCs for debug, stats and process-control
- Informal description of attributes
Additionally, Clixon extends NETCONF for internal use with some internal attributes. These
are not visible for external usage bit belongs to the namespace of this YANG.
The internal attributes are:
- content (also RESTCONF)
- depth (also RESTCONF)
- username
- autocommit
- copystartup
- transport (see RFC6022)
- source-host (see RFC6022)
- objectcreate
- objectexisted
";
revision 2022-12-01 {
description
"Added values of RFC6022 transport identityref
Added description of internal netconf attributes";
}
revision 2021-12-05 {
description
"Obsoleted: extension autocli-op";
}
revision 2021-11-11 { revision 2021-11-11 {
description description
"Changed: RPC stats extended with YANG stats"; "Changed: RPC stats extended with YANG stats";
@ -101,6 +131,23 @@ module clixon-lib {
description description
"Common operations that can be performed on a service"; "Common operations that can be performed on a service";
} }
identity snmp {
description
"SNMP";
base ncm:transport;
}
identity netconf {
description
"Just NETCONF without specitic underlying transport,
Clixon uses stdio for its netconf client and therefore does not know whether it is
invoked in a script, by a NETCONF/SSH subsystem, etc";
base ncm:transport;
}
identity restconf {
description
"RESTCONF either as HTTP/1 or /2, TLS or not, reverese proxy (eg fcgi/nginx) or native";
base ncm:transport;
}
extension autocli-op { extension autocli-op {
description description
"Takes an argument an operation defing how to modify the clispec at "Takes an argument an operation defing how to modify the clispec at
@ -109,8 +156,10 @@ module clixon-lib {
Operations is expected to be extended, but the following operations are defined: Operations is expected to be extended, but the following operations are defined:
- hide This command is active but not shown by ? or TAB (meaning, it hides the auto-completion of commands) - hide This command is active but not shown by ? or TAB (meaning, it hides the auto-completion of commands)
- hide-database This command hides the database - hide-database This command hides the database
- hide-database-auto-completion This command hides the database and the auto completion (meaning, this command acts as both commands above)"; - hide-database-auto-completion This command hides the database and the auto completion (meaning, this command acts as both commands above)
Obsolete: use clixon-autocli:hide and clixon-autocli:hide-show instead";
argument cliop; argument cliop;
status obsolete;
} }
rpc debug { rpc debug {
description "Set debug level of backend."; description "Set debug level of backend.";