diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9cf72fcf..9345b7f5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -44,8 +44,8 @@ Expected: beginning of 2023
### New features
* Netconf monitoring, part 2
- * Datastores
- * Remaining: sessions and statistics state
+ * Datastores and sessions
+ * Remaining: statistics state
* Standards
* RFC 6022 "YANG Module for NETCONF Monitoring"
* 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
+* 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`
* This means that all get operations without `with-defaults` parameter do no longer
return implicit default values, only explicitly set values.
diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c
index c9262f44..456127f5 100644
--- a/apps/backend/backend_client.c
+++ b/apps/backend/backend_client.c
@@ -78,7 +78,7 @@
*/
static struct client_entry *
ce_find_byid(struct client_entry *ce_list,
- uint32_t id)
+ uint32_t id)
{
struct client_entry *ce;
@@ -154,6 +154,81 @@ release_all_dbs(clicon_handle h,
return retval;
}
+/*! Get backend-specific client netconf monitoring state
+ *
+ * Backend-specific netconf monitoring state is:
+ * sessions
+ * @param[in] h Clicon handle
+ * @param[in] yspec Yang spec
+ * @param[in] xpath XML Xpath
+ * @param[in] nsc XML Namespace context for xpath
+ * @param[in,out] xret Existing XML tree, merge x into this
+ * @param[out] xerr XML error tree, if retval = 0
+ * @retval -1 Error (fatal)
+ * @retval 0 Statedata callback failed, error in xerr
+ * @retval 1 OK
+ * @see RFC 6022
+ */
+int
+backend_monitoring_state_get(clicon_handle h,
+ yang_stmt *yspec,
+ char *xpath,
+ cvec *nsc,
+ cxobj **xret,
+ cxobj **xerr)
+{
+ int retval = -1;
+ cbuf *cb = NULL;
+ struct client_entry *ce;
+ char timestr[28];
+ int ret;
+
+ if ((cb = cbuf_new()) ==NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "", NETCONF_MONITORING_NAMESPACE);
+ cprintf(cb, "");
+ for (ce = backend_client_list(h); ce; ce = ce->ce_next){
+ cprintf(cb, "");
+ cprintf(cb, "%u", ce->ce_id);
+ if (ce->ce_transport)
+ cprintf(cb, "%s",
+ CLIXON_LIB_PREFIX, CLIXON_LIB_NS,
+ ce->ce_transport);
+ cprintf(cb, "%s", ce->ce_username);
+ if (ce->ce_source_host)
+ cprintf(cb, "%s", 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, "%s", timestr);
+ }
+ cprintf(cb, "%u", ce->ce_in_rpcs);
+ cprintf(cb, "%u", ce->ce_in_bad_rpcs);
+ cprintf(cb, "%u", ce->ce_out_rpc_errors);
+ cprintf(cb, "%u", 0);
+ cprintf(cb, "");
+ }
+ cprintf(cb, "");
+ cprintf(cb, "");
+ if ((ret = clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, xerr)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
+ retval = 1;
+ done:
+ clicon_debug(1, "%s %d", __FUNCTION__, retval);
+ if (cb)
+ cbuf_free(cb);
+ return retval;
+ fail:
+ retval = 0;
+ goto done;
+}
+
/*! Remove client entry state
* Close down everything wrt clients (eg sockets, subscriptions)
* Finally actually remove client struct in handle
@@ -451,7 +526,7 @@ from_client_edit_config(clicon_handle h,
xmldb_modified_set(h, target, 1); /* mark as dirty */
/* Clixon extension: autocommit */
if ((attr = xml_find_value(xn, "autocommit")) != NULL &&
- strcmp(attr,"true")==0)
+ strcmp(attr,"true") == 0)
autocommit = 1;
/* If autocommit option is set or requested by client */
if (clicon_autocommit(h) || autocommit) {
@@ -513,7 +588,9 @@ from_client_edit_config(clicon_handle h,
}
cprintf(cbret, "");
ok:
retval = 0;
@@ -1375,6 +1452,11 @@ from_client_process_control(clicon_handle h,
}
/*! Clixon hello to check liveness
+ *
+ * @param[in] h Clixon handle
+ * @param[in] x Incoming XML of hello request
+ * @param[in] ce Client entry (from)
+ * @param[out] cbret Hello reply
* @retval 0 OK
* @retval -1 Error
*/
@@ -1386,11 +1468,24 @@ from_client_hello(clicon_handle h,
{
int retval = -1;
uint32_t id;
+ char *val;
if (clicon_session_id_get(h, &id) < 0){
clicon_err(OE_NETCONF, ENOENT, "session_id not set");
goto done;
}
+ if ((val = xml_find_type_value(x, "cl", "transport", CX_ATTR)) != NULL){
+ if ((ce->ce_transport = strdup(val)) == NULL){
+ clicon_err(OE_UNIX, errno, "strdup");
+ goto done;
+ }
+ }
+ if ((val = xml_find_type_value(x, "cl", "source-host", CX_ATTR)) != NULL){
+ if ((ce->ce_source_host = strdup(val)) == NULL){
+ clicon_err(OE_UNIX, errno, "strdup");
+ goto done;
+ }
+ }
id++;
clicon_session_id_set(h, id);
cprintf(cbret, "%u",
@@ -1498,7 +1593,7 @@ from_client_msg(clicon_handle h,
}
if (strcmp(rpcname, "rpc") == 0){
- ; /* continue below */
+ ce->ce_in_rpcs++; /* Track all RPCs */
}
else if (strcmp(rpcname, "hello") == 0){
if ((ret = from_client_hello(h, x, ce, cbret)) <0)
@@ -1508,9 +1603,12 @@ from_client_msg(clicon_handle h,
else{
if (netconf_unknown_element(cbret, "protocol", rpcname, "Unrecognized netconf operation")< 0)
goto done;
+ ce->ce_in_bad_rpcs++;
+ ce->ce_out_rpc_errors++; /* Number of messages sent that contained an */
goto reply;
}
ce->ce_id = id;
+
/* As a side-effect, this expands xt with default values according to "report-all"
* This may not be correct, the RFC does not mention expanding default values for
* input RPC
@@ -1520,6 +1618,8 @@ from_client_msg(clicon_handle h,
if (ret == 0){
if (clixon_xml2cbuf(cbret, xret, 0, 0, -1, 0) < 0)
goto done;
+ ce->ce_in_bad_rpcs++;
+ ce->ce_in_rpcs--; /* Track all RPCs */
goto reply;
}
xe = NULL;
@@ -1531,6 +1631,7 @@ from_client_msg(clicon_handle h,
if ((ye = xml_spec(xe)) == NULL){
if (netconf_operation_not_supported(cbret, "protocol", rpc) < 0)
goto done;
+ ce->ce_out_rpc_errors++;
goto reply;
}
if ((ymod = ys_module(ye)) == NULL){
@@ -1557,26 +1658,34 @@ from_client_msg(clicon_handle h,
creds = clicon_nacm_credentials(h);
if ((ret = verify_nacm_user(h, creds, ce->ce_username, username, cbret)) < 0)
goto done;
- if (ret == 0) /* credentials fail */
+ if (ret == 0){ /* credentials fail */
+ ce->ce_out_rpc_errors++;
goto reply;
+ }
/* NACM rpc operation exec validation */
if ((ret = nacm_rpc(rpc, module, username, xnacm, cbret)) < 0)
goto done;
- if (ret == 0) /* Not permitted and cbret set */
+ if (ret == 0){ /* Not permitted and cbret set */
+ ce->ce_out_rpc_errors++;
goto reply;
+ }
}
clicon_err_reset();
if ((ret = rpc_callback_call(h, xe, ce, &nr, cbret)) < 0){
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
goto done;
clicon_log(LOG_NOTICE, "%s Error in rpc_callback_call:%s", __FUNCTION__, xml_name(xe));
+ ce->ce_out_rpc_errors++;
goto reply; /* Dont quit here on user callbacks */
}
- if (ret == 0)
+ if (ret == 0){
+ ce->ce_out_rpc_errors++;
goto reply;
+ }
if (nr == 0){ /* not handled by callback */
if (netconf_operation_not_supported(cbret, "application", "RPC operation not supported")< 0)
goto done;
+ ce->ce_out_rpc_errors++;
goto reply;
}
if (xnacm){
diff --git a/apps/backend/backend_client.h b/apps/backend/backend_client.h
index 18251e69..c9f2be9d 100644
--- a/apps/backend/backend_client.h
+++ b/apps/backend/backend_client.h
@@ -41,24 +41,33 @@
* Types
*/
/*
- * Client entry.
+ * Backend client entry.
* Keep state about every connected client.
+ * References from RFC 6022, ietf-netconf-monitoring.yang sessions container
*/
struct client_entry{
struct client_entry *ce_next; /* The clients linked list */
struct sockaddr ce_addr; /* The clients (UNIX domain) address */
int ce_s; /* stream socket to client */
int ce_nr; /* Client number (for dbg/tracing) */
- int ce_stat_in; /* Nr of received msgs from client */
- int ce_stat_out;/* Nr of sent msgs to client */
uint32_t ce_id; /* Session id, accessor functions: clicon_session_id_get/set */
char *ce_username;/* Translated from peer user cred */
clicon_handle ce_handle; /* clicon config handle (all clients have same?) */
+ char *ce_transport; /* Identifies the transport for each session.
+ Clixon-lib.yang extends these values by prefixing with
+ "cl:", where cl is ensured to be declared ie by
+ netconf-monitoring state */
+ char *ce_source_host; /* Host identifier of the NETCONF client */
+ struct timeval ce_time; /* Time at the server at which the session was established. */
+ uint32_t ce_in_rpcs ; /* Number of correct messages received. */
+ uint32_t ce_in_bad_rpcs; /* Not correct messages */
+ uint32_t ce_out_rpc_errors; /* messages*/
};
/*
* Prototypes
*/
+int backend_monitoring_state_get(clicon_handle h, yang_stmt *yspec, char *xpath, cvec *nsc, cxobj **xret, cxobj **xerr);
int backend_client_rm(clicon_handle h, struct client_entry *ce);
int from_client(int fd, void *arg);
int backend_rpc_init(clicon_handle h);
diff --git a/apps/backend/backend_get.c b/apps/backend/backend_get.c
index 4efbf7f3..b11d548b 100644
--- a/apps/backend/backend_get.c
+++ b/apps/backend/backend_get.c
@@ -181,10 +181,22 @@ client_get_streams(clicon_handle h,
* @param[in] xpath XPath selection, may be used to filter early
* @param[in] nsc XML Namespace context for xpath
* @param[in] wdef With-defaults parameter, see RFC 6243
- * @param[in,out] xret Existing XML tree, merge x into this
+ * @param[in,out] xret Existing XML tree, merge x into this, or rpc-error
* @retval -1 Error (fatal)
- * @retval 0 Statedata callback failed (clicon_err called)
+ * @retval 0 Statedata callback failed (error in xret)
* @retval 1 OK
+ * @note This code in general does not look at xpath, needs to be filtered in retrospect
+ * @note Awkward error handling. Even if most of this is during development phase, except for plugin
+ * state callbacks.
+ * Present behavior:
+ * - Present behavior: should be returned in xret with retval 0(error) or 1(ok)
+ * - On error, previous content of xret is not freed
+ * - xret is in turn translated to cbuf in calling function
+ * Instead, I think there should be a second out argument **xerr with the error message, see code
+ * for CLICON_NETCONF_MONITORING which is transformed in calling function(?) to an internal error
+ * message. But this needs to be explored in all sub-functions
+ *
+ *
*/
static int
get_client_statedata(clicon_handle h,
@@ -196,9 +208,11 @@ get_client_statedata(clicon_handle h,
int retval = -1;
yang_stmt *yspec;
yang_stmt *ymod;
+ cxobj *x1 = NULL;
int ret;
char *namespace;
cbuf *cb = NULL;
+ cxobj *xerr = NULL;
clicon_debug(1, "%s", __FUNCTION__);
if ((yspec = clicon_dbspec_yang(h)) == NULL){
@@ -236,7 +250,6 @@ get_client_statedata(clicon_handle h,
goto done;
}
cbuf_reset(cb);
- /* XXX This code does not filter state data with xpath */
cprintf(cb, "", namespace);
if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, NULL) < 0)
goto done;
@@ -254,7 +267,32 @@ get_client_statedata(clicon_handle h,
goto fail;
}
if (clicon_option_bool(h, "CLICON_NETCONF_MONITORING")){
- if ((ret = netconf_monitoring_state_get(h, yspec, xpath, nsc, 0, xret)) < 0)
+ if ((ret = netconf_monitoring_state_get(h, yspec, xpath, nsc, xret, &xerr)) < 0)
+ goto done;
+ if (ret == 0){
+ if (clixon_netconf_internal_error(xerr, " . Internal error, netconf_monitoring_state returned invalid XML", NULL) < 0)
+ goto done;
+ if (*xret)
+ xml_free(*xret);
+ *xret = xerr;
+ xerr = NULL;
+ goto fail;
+ }
+ /* Some state, client state, is avaliable in backend only, not in lib
+ * Needs merge since same subtree as previous lib state
+ */
+ if ((ret = backend_monitoring_state_get(h, yspec, xpath, nsc, &x1, &xerr)) < 0)
+ goto done;
+ if (ret == 0){
+ if (clixon_netconf_internal_error(xerr, " . Internal error, baenckend_monitoring_state_get returned invalid XML", NULL) < 0)
+ goto done;
+ if (*xret)
+ xml_free(*xret);
+ *xret = xerr;
+ xerr = NULL;
+ goto fail;
+ }
+ if ((ret = netconf_trymerge(x1, yspec, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
@@ -320,6 +358,10 @@ get_client_statedata(clicon_handle h,
retval = 1; /* OK */
done:
clicon_debug(1, "%s %d", __FUNCTION__, retval);
+ if (xerr)
+ xml_free(xerr);
+ if (x1)
+ xml_free(x1);
if (cb)
cbuf_free(cb);
return retval;
@@ -734,19 +776,13 @@ get_list_pagination(clicon_handle h,
cbuf *cba = NULL;
/* Add remaining attribute */
- if ((xa = xml_new("remaining", x1, CX_ATTR)) == NULL)
- goto done;
if ((cba = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cba, "%u", remaining);
- if (xml_value_set(xa, cbuf_get(cba)) < 0)
- goto done;
- if (xml_prefix_set(xa, "cp") < 0)
- goto done;
- if (xmlns_set(x1, "cp", "http://clicon.org/clixon-netconf-list-pagination") < 0)
- goto done;
+ if (xml_add_attr(x1, "remaining", cbuf_get(cba), "cp", "http://clicon.org/clixon-netconf-list-pagination") < 0)
+ goto done;
if (cba)
cbuf_free(cba);
}
diff --git a/apps/backend/backend_socket.c b/apps/backend/backend_socket.c
index fa2ee9c1..a564b789 100644
--- a/apps/backend/backend_socket.c
+++ b/apps/backend/backend_socket.c
@@ -249,7 +249,6 @@ backend_accept_client(int fd,
}
if ((ce = backend_client_add(h, &from)) == NULL)
goto done;
- ce->ce_handle = h;
/*
* Get credentials of connected peer - only for unix socket
diff --git a/apps/backend/clixon_backend_handle.c b/apps/backend/clixon_backend_handle.c
index 4dd89fc3..2d738684 100644
--- a/apps/backend/clixon_backend_handle.c
+++ b/apps/backend/clixon_backend_handle.c
@@ -145,6 +145,8 @@ backend_client_add(clicon_handle h,
ce->ce_nr = bh->bh_ce_nr++; /* Session-id ? */
memcpy(&ce->ce_addr, addr, sizeof(*addr));
ce->ce_next = bh->bh_ce_list;
+ ce->ce_handle = h;
+ gettimeofday(&ce->ce_time, NULL);
bh->bh_ce_list = ce;
return ce;
}
@@ -180,6 +182,10 @@ backend_client_delete(clicon_handle h,
*ce_prev = c->ce_next;
if (ce->ce_username)
free(ce->ce_username);
+ if (ce->ce_transport)
+ free(ce->ce_transport);
+ if (ce->ce_source_host)
+ free(ce->ce_source_host);
free(ce);
break;
}
@@ -203,8 +209,9 @@ backend_client_print(clicon_handle h,
fprintf(f, "Client: %d\n", ce->ce_nr);
fprintf(f, " Session: %d\n", ce->ce_id);
fprintf(f, " Socket: %d\n", ce->ce_s);
- fprintf(f, " Msgs in: %d\n", ce->ce_stat_in);
- fprintf(f, " Msgs out: %d\n", ce->ce_stat_out);
+ fprintf(f, " RPCs in: %u\n", ce->ce_in_rpcs);
+ fprintf(f, " Bad RPCs in: %u\n", ce->ce_in_bad_rpcs);
+ fprintf(f, " Err RPCs out: %u\n", ce->ce_out_rpc_errors);
fprintf(f, " Username: %s\n", ce->ce_username);
}
return 0;
diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c
index 8e5ef002..1b4b781a 100644
--- a/apps/cli/cli_common.c
+++ b/apps/cli/cli_common.c
@@ -294,7 +294,6 @@ cli_dbxml(clicon_handle h,
cxobj *xbot = NULL; /* xpath, NULL if datastore */
yang_stmt *y = NULL; /* yang spec of xpath */
cxobj *xtop = NULL; /* xpath root */
- cxobj *xa; /* attribute */
cxobj *xerr = NULL;
int ret;
cg_var *cv;
@@ -338,11 +337,7 @@ cli_dbxml(clicon_handle h,
goto done;
}
}
- if ((xa = xml_new("operation", xbot, CX_ATTR)) == NULL)
- goto done;
- if (xml_prefix_set(xa, NETCONF_BASE_PREFIX) < 0)
- goto done;
- if (xml_value_set(xa, xml_operation2str(op)) < 0)
+ if (xml_add_attr(xbot, "operation", xml_operation2str(op), NETCONF_BASE_PREFIX, NULL) < 0)
goto done;
/* Add body last in case of leaf */
if (cvec_len(cvv) > 1 &&
diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c
index e53e3cc9..a7289197 100644
--- a/apps/cli/cli_main.c
+++ b/apps/cli/cli_main.c
@@ -818,7 +818,12 @@ main(int argc,
if (evalresult < 0)
goto done;
}
-
+ /* Set RFC6022 session parameters that will be sent in first hello,
+ * @see clicon_hello_req
+ */
+ clicon_data_set(h, "session-transport", "cl:cli");
+ clicon_data_set(h, "session-source-host", "localhost");
+
/* Go into event-loop unless -1 command-line */
if (!once){
retval = cli_interactive(h);
diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c
index f18bfd3d..f7abd04e 100644
--- a/apps/netconf/netconf_main.c
+++ b/apps/netconf/netconf_main.c
@@ -110,6 +110,15 @@ netconf_add_request_attr(cxobj *xrpc,
/* If attribute already exists, dont copy it */
if (xml_find_type(xrep, NULL, xml_name(xa), CX_ATTR) != NULL)
continue; /* Skip already present (dont overwrite) */
+ /* Filter all clixon-lib attributes and namespace declaration
+ * to acvoid leaking internal attributes to external NETCONF
+ * note this is only done on top-level.
+ */
+ if (xml_prefix(xa) && strcmp(xml_prefix(xa), CLIXON_LIB_PREFIX) == 0)
+ continue;
+ if (xml_prefix(xa) && strcmp(xml_prefix(xa), "xmlns") == 0 &&
+ strcmp(xml_name(xa), CLIXON_LIB_PREFIX) == 0)
+ continue;
if ((xa2 = xml_dup(xa)) ==NULL)
goto done;
if (xml_addsub(xrep, xa2) < 0)
@@ -192,11 +201,11 @@ netconf_rpc_message(clicon_handle h,
yang_stmt *yspec,
int *eof)
{
- int retval = -1;
- cxobj *xret = NULL; /* Return (out) */
- int ret;
- cbuf *cbret = NULL;
- cxobj *xc;
+ int retval = -1;
+ cxobj *xret = NULL; /* Return (out) */
+ int ret;
+ cbuf *cbret = NULL;
+ cxobj *xc;
netconf_framing_type framing;
framing = clicon_option_int(h, "netconf-framing");
@@ -240,9 +249,9 @@ netconf_rpc_message(clicon_handle h,
goto done;
goto ok;
}
- if (netconf_rpc_dispatch(h, xrpc, &xret, eof) < 0){
+ if (netconf_rpc_dispatch(h, xrpc, &xret, eof) < 0)
goto done;
- }
+
/* Is there a return message in xret? */
if (xret == NULL){
if (netconf_operation_failed_xml(&xret, "rpc", "Internal error: no xml return")< 0)
@@ -988,7 +997,7 @@ main(int argc,
* used by the client, even though new TCP sessions are created for
* each message sent to the backend.
*/
- if (clicon_hello_req(h, &id) < 0)
+ if (clicon_hello_req(h, "cl:netconf", NULL, &id) < 0)
goto done;
clicon_session_id_set(h, id);
diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c
index 5e5a721c..db111b82 100644
--- a/apps/netconf/netconf_rpc.c
+++ b/apps/netconf/netconf_rpc.c
@@ -704,9 +704,7 @@ netconf_rpc_dispatch(clicon_handle h,
* It may even be wrong if something else is done with the incoming message?
*/
if ((username = clicon_username_get(h)) != NULL){
- if ((xa = xml_new("username", xn, CX_ATTR)) == NULL)
- goto done;
- if (xml_value_set(xa, username) < 0)
+ if (xml_add_attr(xn, "username", username, CLIXON_LIB_PREFIX, CLIXON_LIB_NS) < 0)
goto done;
}
/* Many of these calls are now calling generic clicon_rpc_netconf_xml
diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c
index 63c005b8..065eb62f 100644
--- a/apps/restconf/restconf_lib.c
+++ b/apps/restconf/restconf_lib.c
@@ -405,7 +405,6 @@ restconf_insert_attributes(cxobj *xdata,
cvec *qvec)
{
int retval = -1;
- cxobj *xa;
char *instr;
char *pstr;
yang_stmt *y;
@@ -422,12 +421,7 @@ restconf_insert_attributes(cxobj *xdata,
/* First add xmlns:yang attribute */
if (xmlns_set(xdata, "yang", YANG_XML_NAMESPACE) < 0)
goto done;
- /* Then add insert attribute */
- if ((xa = xml_new("insert", xdata, CX_ATTR)) == NULL)
- goto done;
- if (xml_prefix_set(xa, "yang") < 0)
- goto done;
- if (xml_value_set(xa, instr) < 0)
+ if (xml_add_attr(xdata, "insert", instr, "yang", NULL) < 0)
goto done;
}
if ((pstr = cvec_find_str(qvec, "point")) != NULL){
@@ -439,11 +433,7 @@ restconf_insert_attributes(cxobj *xdata,
attrname="key";
else
attrname="value";
- /* Then add value/key attribute */
- if ((xa = xml_new(attrname, xdata, CX_ATTR)) == NULL)
- goto done;
- if (xml_prefix_set(xa, "yang") < 0)
- goto done;
+
if ((ret = api_path2xpath(pstr, ys_spec(y), &xpath, &nsc, NULL)) < 0)
goto done;
if ((cb = cbuf_new()) == NULL){
@@ -471,7 +461,7 @@ restconf_insert_attributes(cxobj *xdata,
p++;
cprintf(cb, "%s", p);
}
- if (xml_value_set(xa, cbuf_get(cb)) < 0)
+ if (xml_add_attr(xdata, attrname, cbuf_get(cb), "yang", NULL) < 0)
goto done;
}
/* Add prefix/namespaces used in attributes */
diff --git a/apps/restconf/restconf_main_fcgi.c b/apps/restconf/restconf_main_fcgi.c
index 36e2c880..48251057 100644
--- a/apps/restconf/restconf_main_fcgi.c
+++ b/apps/restconf/restconf_main_fcgi.c
@@ -163,7 +163,7 @@ restconf_main_config(clicon_handle h,
else {
/* Loop to wait for backend starting, try again if not done */
while (1){
- if (clicon_hello_req(h, &id) < 0){
+ if (clicon_hello_req(h, "cl:restconf", NULL, &id) < 0){
if (errno == ENOENT){
fprintf(stderr, "waiting");
sleep(1);
diff --git a/apps/restconf/restconf_main_native.c b/apps/restconf/restconf_main_native.c
index f607a156..f6bd11b0 100644
--- a/apps/restconf/restconf_main_native.c
+++ b/apps/restconf/restconf_main_native.c
@@ -634,7 +634,7 @@ restconf_clixon_backend(clicon_handle h,
/* Loop to wait for backend starting, try again if not done */
while (1){
- if (clicon_hello_req(h, &id) < 0){
+ if (clicon_hello_req(h, "cl:restconf", NULL, &id) < 0){
if (errno == ENOENT){
fprintf(stderr, "waiting");
sleep(1);
diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c
index 9c1fbebc..571587b4 100644
--- a/apps/restconf/restconf_methods.c
+++ b/apps/restconf/restconf_methods.c
@@ -384,24 +384,12 @@ api_data_write(clicon_handle h,
/* Add operation create as attribute. If that fails with Conflict, then
* try "replace" (see comment in function header)
*/
- if ((xa = xml_new("operation", xdata, CX_ATTR)) == NULL)
+ if (xml_add_attr(xdata, "operation", xml_operation2str(op), NETCONF_BASE_PREFIX, NULL) < 0)
goto done;
- if (xml_prefix_set(xa, NETCONF_BASE_PREFIX) < 0)
+ if (xml_add_attr(xdata, "objectcreate",
+ plain_patch?"false":"true",
+ CLIXON_LIB_PREFIX, CLIXON_LIB_NS) < 0)
goto done;
- if (xml_value_set(xa, xml_operation2str(op)) < 0) /* XXX here is where op is used */
- goto done;
- if ((xa = xml_new("objectcreate", xdata, CX_ATTR)) == NULL)
- goto done;
- if (plain_patch){
- /* RFC 8040 4.6. PATCH:
- * If the target resource instance does not exist, the server MUST NOT create it.
- */
- if (xml_value_set(xa, "false") < 0)
- goto done;
- }
- else
- if (xml_value_set(xa, "true") < 0)
- goto done;
/* Top-of tree, no api-path
* Replace xparent with x, ie bottom of api-path with data
*/
@@ -512,16 +500,20 @@ api_data_write(clicon_handle h,
} /* api-path != NULL */
/* For internal XML protocol: add username attribute for access control
*/
- username = clicon_username_get(h);
/* Create text buffer for transfer to backend */
if ((cbx = cbuf_new()) == NULL)
goto done;
- cprintf(cbx, "",
- NETCONF_BASE_NAMESPACE,
- username?username:"",
+ cprintf(cbx, "");
cprintf(cbx, "");
cprintf(cbx, "none");
if (clixon_xml2cbuf(cbx, xtop, 0, 0, -1, 0) < 0)
@@ -759,14 +753,17 @@ api_data_delete(clicon_handle h,
goto done;
/* For internal XML protocol: add username attribute for access control
*/
- username = clicon_username_get(h);
- cprintf(cbx, "",
- NETCONF_BASE_NAMESPACE,
- username?username:"",
+ cprintf(cbx, "");
cprintf(cbx, "");
cprintf(cbx, "none");
if (clixon_xml2cbuf(cbx, xtop, 0, 0, -1, 0) < 0)
diff --git a/apps/restconf/restconf_methods_post.c b/apps/restconf/restconf_methods_post.c
index b0926351..08a0b017 100644
--- a/apps/restconf/restconf_methods_post.c
+++ b/apps/restconf/restconf_methods_post.c
@@ -337,14 +337,15 @@ api_data_post(clicon_handle h,
}
/* For internal XML protocol: add username attribute for access control
*/
- username = clicon_username_get(h);
- cprintf(cbx, "",
- NETCONF_BASE_NAMESPACE,
- username?username:"",
- NETCONF_BASE_PREFIX,
- NETCONF_BASE_NAMESPACE,
- NETCONF_MESSAGE_ID_ATTR); /* bind nc to netconf namespace */
-
+ cprintf(cbx, "", NETCONF_MESSAGE_ID_ATTR); /* bind nc to netconf namespace */
cprintf(cbx, "");
cprintf(cbx, "none");
if (clixon_xml2cbuf(cbx, xtop, 0, 0, -1, 0) < 0)
diff --git a/apps/snmp/README.md b/apps/snmp/README.md
new file mode 100644
index 00000000..5cf44140
--- /dev/null
+++ b/apps/snmp/README.md
@@ -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)
diff --git a/apps/snmp/snmp_main.c b/apps/snmp/snmp_main.c
index b826bd48..ac3bc04a 100644
--- a/apps/snmp/snmp_main.c
+++ b/apps/snmp/snmp_main.c
@@ -525,7 +525,7 @@ main(int argc,
* used by the client, even though new TCP sessions are created for
* each message sent to the backend.
*/
- if (clicon_hello_req(h, &id) < 0)
+ if (clicon_hello_req(h, "cl:snmp", "localhost", &id) < 0)
goto done;
clicon_session_id_set(h, id);
diff --git a/lib/clixon/clixon_netconf_monitoring.h b/lib/clixon/clixon_netconf_monitoring.h
index 0e6cdd13..a32b232d 100644
--- a/lib/clixon/clixon_netconf_monitoring.h
+++ b/lib/clixon/clixon_netconf_monitoring.h
@@ -41,6 +41,6 @@
/*
* 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_ */
diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h
index 991da5aa..529e782d 100644
--- a/lib/clixon/clixon_options.h
+++ b/lib/clixon/clixon_options.h
@@ -51,7 +51,9 @@
* @see clixon-lib.yang
*/
#define CLIXON_CONF_NS "http://clicon.org/config"
+#define CLIXON_CONF_PREFIX "cc"
#define CLIXON_LIB_NS "http://clicon.org/lib"
+#define CLIXON_LIB_PREFIX "cl"
#define CLIXON_AUTOCLI_NS "http://clicon.org/autocli"
#define CLIXON_RESTCONF_NS "http://clicon.org/restconf"
diff --git a/lib/clixon/clixon_proto_client.h b/lib/clixon/clixon_proto_client.h
index d2fd7f93..82608827 100644
--- a/lib/clixon/clixon_proto_client.h
+++ b/lib/clixon/clixon_proto_client.h
@@ -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_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);
#endif /* _CLIXON_PROTO_CLIENT_H_ */
diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h
index 78d90ca9..819ddad7 100644
--- a/lib/clixon/clixon_xml.h
+++ b/lib/clixon/clixon_xml.h
@@ -279,7 +279,9 @@ cxobj *xml_root(cxobj *xn);
int xml_operation(char *opstr, enum operation_type *op);
char *xml_operation2str(enum operation_type op);
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)));
+
#ifdef XML_EXPLICIT_INDEX
int xml_search_index_p(cxobj *x);
diff --git a/lib/src/clixon_datastore_write.c b/lib/src/clixon_datastore_write.c
index 24f57255..a3ce6e93 100644
--- a/lib/src/clixon_datastore_write.c
+++ b/lib/src/clixon_datastore_write.c
@@ -168,7 +168,6 @@ check_body_namespace(cxobj *x0,
char *prefix = NULL;
char *ns0 = NULL;
char *ns1 = NULL;
- cxobj *xa;
cxobj *x;
int isroot;
cbuf *cberr = NULL;
@@ -210,17 +209,12 @@ bad-attribue?
goto done;
/* Create xmlns attribute to x0 XXX same code ^*/
if (prefix){
- if ((xa = xml_new(prefix, x, CX_ATTR)) == NULL)
- goto done;
- if (xml_prefix_set(xa, "xmlns") < 0)
+ if (xml_add_attr(x, prefix, ns0, "xmlns", NULL) < 0)
goto done;
}
- else{
- if ((xa = xml_new("xmlns", x, CX_ATTR)) == NULL)
+ else
+ if (xml_add_attr(x, "xmlns", ns0, NULL, NULL) < 0)
goto done;
- }
- if (xml_value_set(xa, ns0) < 0)
- goto done;
xml_sort(x); /* Ensure attr is first / XXX xml_insert? */
}
#if 0
@@ -237,11 +231,7 @@ bad-attribue?
;
}
else{ /* Add it according to the kludge,... */
- if ((xa = xml_new(prefix, x0, CX_ATTR)) == NULL)
- goto done;
- if (xml_prefix_set(xa, "xmlns") < 0)
- goto done;
- if (xml_value_set(xa, ns0) < 0)
+ if (xml_add_attr(x0, prefix, ns0, "xmlns", NULL) < 0)
goto done;
}
}
diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c
index 4b774b75..707031b6 100644
--- a/lib/src/clixon_json.c
+++ b/lib/src/clixon_json.c
@@ -293,7 +293,6 @@ json2xml_decode_identityref(cxobj *x,
char *ns;
char *body;
cxobj *xb;
- cxobj *xa;
char *prefix = NULL;
char *id = NULL;
yang_stmt *ymod;
@@ -333,12 +332,9 @@ json2xml_decode_identityref(cxobj *x,
if (prefix2 == NULL)
prefix2 = yang_find_myprefix(ymod);
/* Add "xmlns:prefix2=namespace" */
- if ((xa = xml_new(prefix2, x, CX_ATTR)) == NULL)
- goto done;
- if (xml_prefix_set(xa, "xmlns") < 0)
- goto done;
- if (xml_value_set(xa, ns) < 0)
+ if (xml_add_attr(x, prefix2, ns, "xmlns", NULL) < 0)
goto done;
+
}
/* Here prefix2 is valid and can be NULL
Change body prefix to prefix2:id */
diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c
index 51fd6b73..9fadd0fa 100644
--- a/lib/src/clixon_netconf_lib.c
+++ b/lib/src/clixon_netconf_lib.c
@@ -161,7 +161,6 @@ netconf_invalid_value_xml(cxobj **xret,
int retval =-1;
cxobj *xerr = NULL;
char *encstr = NULL;
- cxobj *xa;
if (xret == 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)
goto done;
- if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL)
- goto done;
- if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
+ if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@@ -282,7 +279,6 @@ netconf_missing_attribute_xml(cxobj **xret,
int retval = -1;
cxobj *xerr = NULL;
char *encstr = NULL;
- cxobj *xa;
if (xret == 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)
goto done;
- if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL)
- goto done;
- if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
+ if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@@ -394,7 +388,6 @@ netconf_bad_attribute_xml(cxobj **xret,
int retval = -1;
cxobj *xerr = NULL;
char *encstr = NULL;
- cxobj *xa;
if (xret == 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)
goto done;
- if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL)
- goto done;
- if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
+ if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@@ -491,7 +482,6 @@ netconf_common_xml(cxobj **xret,
int retval =-1;
cxobj *xerr;
char *encstr = NULL;
- cxobj *xa;
if (xret == NULL){
clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
@@ -500,9 +490,7 @@ netconf_common_xml(cxobj **xret,
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
goto done;
- if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL)
- goto done;
- if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
+ if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
@@ -751,7 +739,6 @@ netconf_access_denied_xml(cxobj **xret,
int retval =-1;
cxobj *xerr;
char *encstr = NULL;
- cxobj *xa;
if (xret == 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)
goto done;
- if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL)
- goto done;
- if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
+ if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@@ -987,7 +972,6 @@ netconf_data_missing_xml(cxobj **xret,
int retval = -1;
char *encstr = NULL;
cxobj *xerr;
- cxobj *xa;
if (xret == 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)
goto done;
- if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL)
- goto done;
- if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
+ if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@@ -1045,7 +1027,6 @@ netconf_missing_choice_xml(cxobj **xret,
int retval = -1;
char *encstr = NULL;
cxobj *xerr;
- cxobj *xa;
char *path = NULL;
char *encpath = NULL;
@@ -1059,9 +1040,7 @@ netconf_missing_choice_xml(cxobj **xret,
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
- if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL)
- goto done;
- if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
+ if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@@ -1122,7 +1101,6 @@ netconf_operation_not_supported_xml(cxobj **xret,
int retval =-1;
cxobj *xerr;
char *encstr = NULL;
- cxobj *xa;
if (xret == 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)
goto done;
- if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL)
- goto done;
- if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
+ if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@@ -1237,7 +1213,6 @@ netconf_operation_failed_xml(cxobj **xret,
int retval =-1;
cxobj *xerr;
char *encstr = NULL;
- cxobj *xa;
if (xret == 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)
goto done;
- if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL)
- goto done;
- if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
+ if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@@ -1325,8 +1298,7 @@ netconf_malformed_message_xml(cxobj **xret,
int retval =-1;
cxobj *xerr;
char *encstr = NULL;
- cxobj *xa;
-
+
if (xret == NULL){
clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
goto done;
@@ -1337,9 +1309,7 @@ netconf_malformed_message_xml(cxobj **xret,
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
- if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL)
- goto done;
- if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
+ if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@@ -1408,7 +1378,6 @@ netconf_data_not_unique_xml(cxobj **xret,
cg_var *cvi = NULL;
cxobj *xerr;
cxobj *xinfo;
- cxobj *xa;
char *path = NULL;
char *encpath = NULL;
@@ -1422,9 +1391,7 @@ netconf_data_not_unique_xml(cxobj **xret,
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
- if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL)
- goto done;
- if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
+ if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@@ -1482,7 +1449,6 @@ netconf_minmax_elements_xml(cxobj **xret,
cxobj *xerr;
char *path = NULL;
char *encpath = NULL;
- cxobj *xa;
if (xret == 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)
goto done;
- if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL)
- goto done;
- if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
+ if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) < 0)
goto done;
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
@@ -1528,9 +1492,9 @@ netconf_minmax_elements_xml(cxobj **xret,
* @param[in] x XML tree
* @param[in] yspec Yang spec
* @param[in,out] xret Existing XML tree, merge x into this
- * @retval -1 Error (fatal)
- * @retval 0 Statedata callback failed
* @retval 1 OK
+ * @retval 0 Statedata callback failed, error in xret?
+ * @retval -1 Error (fatal)
*/
int
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,out] cbret Output buffer for internal RPC message if invalid
* @param[out] value Value if valid
- * @retval -1 Error
- * @retval 0 Invalid, cbret set
* @retval 1 OK
+ * @retval 0 Invalid, cbret set
+ * @retval -1 Error
* @code
* char *name = "a";
* char *valstr = "322";
@@ -2238,10 +2202,10 @@ netconf_output_encap(netconf_framing_type framing,
* @param[in] ch New input character
* @param[in,out] state State machine state
* @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 1 Chunk-data
+ * @retval 0 Framing char, not data
+ * @retval -1 Error
* Example:
C: \n#4\n
C: %s", db);
if ((sid = xmldb_islocked(h, db)) > 0){
cprintf(cb, "");
+ cprintf(cb, "");
cprintf(cb, "%u", sid);
xmldb_lock_timestamp(h, db, &tv);
if (time2str(tv, timestr, sizeof(timestr)) < 0){
@@ -85,6 +86,7 @@ per_datastore(clicon_handle h,
goto done;
}
cprintf(cb, "%s", timestr);
+ cprintf(cb, "");
cprintf(cb, "");
}
cprintf(cb, "");
@@ -175,28 +177,6 @@ netconf_monitoring_schemas(clicon_handle h,
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
*
* @param[in] h Clicon handle
@@ -227,11 +207,12 @@ netconf_monitoring_statistics(clicon_handle h,
* @param[in] yspec Yang spec
* @param[in] xpath XML 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[out] xerr XML error tree, if retval = 0
* @retval -1 Error (fatal)
- * @retval 0 Statedata callback failed
+ * @retval 0 Statedata callback failed, error in xret
* @retval 1 OK
+ * @see backend_monitoring_state_get
* @see RFC 6022
*/
int
@@ -239,11 +220,12 @@ netconf_monitoring_state_get(clicon_handle h,
yang_stmt *yspec,
char *xpath,
cvec *nsc,
- int brief,
- cxobj **xret)
+ cxobj **xret,
+ cxobj **xerr)
{
- int retval = -1;
- cbuf *cb = NULL;
+ int retval = -1;
+ cbuf *cb = NULL;
+ int ret;
if ((cb = cbuf_new()) ==NULL){
clicon_err(OE_XML, errno, "cbuf_new");
@@ -256,20 +238,22 @@ netconf_monitoring_state_get(clicon_handle h,
goto done;
if (netconf_monitoring_schemas(h, yspec, cb) < 0)
goto done;
- if (netconf_monitoring_sessions(h, yspec, cb) < 0)
- goto done;
+ /* sessions is backend-specific */
if (netconf_monitoring_statistics(h, yspec, cb) < 0)
goto done;
cprintf(cb, "");
- 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;
+ 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;
+ fail:
+ retval = 0;
+ goto done;
}
diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c
index b14b5462..1045807a 100644
--- a/lib/src/clixon_proto_client.c
+++ b/lib/src/clixon_proto_client.c
@@ -88,6 +88,11 @@
#define TIMEOUT_XML_FMT "%u"
/*! Connect to internal netconf socket
+ *
+ * @param[in] h Clixon handle
+ * @param[out] sockp Socket
+ * @retval 0 OK
+ * @retval -1 Error
*/
int
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
- * @param[in] h Clixonhandle
+ *
+ * @param[in] h Clixon handle
* @param[in] msg Encoded message
* @param[out] xret Returned data as netconf xml tree.
* @param[out] eof Set if eof encountered
+ * @retval 0 OK
+ * @retval -1 Error
*/
static int
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
+ *
* @param[in] h CLICON handle
* @param[in] msg Encoded message. Deallocate with 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 side-effect, a socket created here is cached
* @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
+ *
* @param[in] h CLICON handle
* @param[in] msg Encoded message. Deallocate with 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
* 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
*/
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
+ *
* Session-ids survive TCP sessions that are created for each message sent to the backend.
* Clients use two approaches, either:
* (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;
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;
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[out] xret Return XML netconf tree, error or OK (need to be freed)
* @param[out] sp Socket pointer for notification, otherwise NULL
+ * @retval 0 OK
+ * @retval -1 Error
* @code
* cxobj *xret = NULL;
* int s = -1;
@@ -383,6 +400,7 @@ clicon_rpc_netconf(clicon_handle h,
}
/*! Generic xml netconf clicon rpc
+ *
* Want to go over to use netconf directly between client and server,...
* @param[in] h clicon handle
* @param[in] xml XML netconf tree
@@ -397,7 +415,6 @@ clicon_rpc_netconf(clicon_handle h,
* err;
* xml_free(xret);
* @endcode
-
* @see clicon_rpc_netconf xml as string instead of tree
*/
int
@@ -456,6 +473,7 @@ clicon_rpc_netconf_xml(clicon_handle h,
}
/*! Get database configuration
+ *
* Same as clicon_proto_change just with a cvec instead of lvec
* @param[in] h CLICON handle
* @param[in] username If NULL, use default
@@ -490,7 +508,7 @@ clicon_rpc_netconf_xml(clicon_handle h,
*/
int
clicon_rpc_get_config(clicon_handle h,
- char *username,
+ char *username, // XXX: why is this only rpc call with username parameter?
char *db,
char *xpath,
cvec *nsc,
@@ -510,16 +528,20 @@ clicon_rpc_get_config(clicon_handle h,
if (session_id_check(h, &session_id) < 0)
goto done;
- if ((cb = cbuf_new()) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
goto done;
+ }
cprintf(cb, "<%s/>", db);
if (xpath && strlen(xpath)){
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
+ *
* @param[in] h CLICON handle
* @param[in] db Name of database
* @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)
goto done;
- if ((cb = cbuf_new()) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
goto done;
+ }
cprintf(cb, "<%s/>", db);
cprintf(cb, "%s",
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
+ *
* Note this assumes the backend can access these files and (usually) assumes
* clients and servers have the access to the same filesystem.
* @param[in] h CLICON handle
@@ -676,17 +704,25 @@ clicon_rpc_copy_config(clicon_handle h,
cxobj *xerr;
char *username;
uint32_t session_id;
-
+ cbuf *cb = NULL;
+
if (session_id_check(h, &session_id) < 0)
goto done;
- username = clicon_username_get(h);
- if ((msg = clicon_msg_encode(session_id,
- ""
- "<%s/><%s/>",
- NETCONF_BASE_NAMESPACE,
- username?username:"",
- NETCONF_MESSAGE_ID_ATTR,
- db1, db2)) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "");
+ cprintf(cb, "<%s/><%s/>",
+ db1, db2);
+ if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done;
@@ -696,6 +732,8 @@ clicon_rpc_copy_config(clicon_handle h,
}
retval = 0;
done:
+ if (cb)
+ cbuf_free(cb);
if (xret)
xml_free(xret);
if (msg)
@@ -704,6 +742,7 @@ clicon_rpc_copy_config(clicon_handle h,
}
/*! Send a request to backend to delete a config database
+ *
* @param[in] h CLICON handle
* @param[in] db database, eg "running"
* @retval 0 OK
@@ -723,17 +762,25 @@ clicon_rpc_delete_config(clicon_handle h,
cxobj *xerr;
char *username;
uint32_t session_id;
+ cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0)
goto done;
- username = clicon_username_get(h);
- if ((msg = clicon_msg_encode(session_id,
- ""
- "<%s/>none",
- NETCONF_BASE_NAMESPACE,
- username?username:"",
- NETCONF_MESSAGE_ID_ATTR,
- db)) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "");
+ cprintf(cb, "<%s/>none", db);
+ cprintf(cb, "");
+ if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done;
@@ -743,6 +790,8 @@ clicon_rpc_delete_config(clicon_handle h,
}
retval = 0;
done:
+ if (cb)
+ cbuf_free(cb);
if (xret)
xml_free(xret);
if (msg)
@@ -766,17 +815,25 @@ clicon_rpc_lock(clicon_handle h,
cxobj *xerr;
char *username;
uint32_t session_id;
+ cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0)
goto done;
- username = clicon_username_get(h);
- if ((msg = clicon_msg_encode(session_id,
- ""
- "<%s/>",
- NETCONF_BASE_NAMESPACE,
- username?username:"",
- NETCONF_MESSAGE_ID_ATTR,
- db)) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "");
+ cprintf(cb, "<%s/>", db);
+ cprintf(cb, "");
+ if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done;
@@ -786,6 +843,8 @@ clicon_rpc_lock(clicon_handle h,
}
retval = 0;
done:
+ if (cb)
+ cbuf_free(cb);
if (xret)
xml_free(xret);
if (msg)
@@ -809,17 +868,25 @@ clicon_rpc_unlock(clicon_handle h,
cxobj *xerr;
char *username;
uint32_t session_id;
+ cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0)
goto done;
- username = clicon_username_get(h);
- if ((msg = clicon_msg_encode(session_id,
- ""
- "<%s/>",
- NETCONF_BASE_NAMESPACE,
- username?username:"",
- NETCONF_MESSAGE_ID_ATTR,
- db)) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "");
+ cprintf(cb, "<%s/>", db);
+ cprintf(cb, "");
+ if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done;
@@ -829,6 +896,8 @@ clicon_rpc_unlock(clicon_handle h,
}
retval = 0;
done:
+ if (cb)
+ cbuf_free(cb);
if (xret)
xml_free(xret);
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[out] xt XML tree. Free with xml_free.
* Either or .
- * @retval 0 OK
- * @retval -1 Error, fatal or xml
+ * @retval 0 OK
+ * @retval -1 Error, fatal or xml
* @note if xpath is set but namespace is NULL, the default, netconf base
* namespace will be used which is most probably wrong.
* @code
@@ -894,21 +963,30 @@ clicon_rpc_get(clicon_handle h,
if (session_id_check(h, &session_id) < 0)
goto done;
- if ((cb = cbuf_new()) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
goto done;
- cprintf(cb, " */
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 */
/* If xpath, add a filter */
if (xpath && strlen(xpath)) {
@@ -980,6 +1058,7 @@ clicon_rpc_get(clicon_handle h,
}
/*! Get database configuration and state data collection
+ *
* @param[in] h Clicon handle
* @param[in] xpath To identify a list/leaf-list
* @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)
goto done;
- if ((cb = cbuf_new()) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
goto done;
+ }
cprintf(cb, " */
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 */
cprintf(cb, ">"); /* get */
/* If xpath, add a filter */
@@ -1152,14 +1241,25 @@ clicon_rpc_close_session(clicon_handle h)
char *username;
uint32_t session_id;
int s;
-
+ cbuf *cb = NULL;
+
if (session_id_check(h, &session_id) < 0)
goto done;
- username = clicon_username_get(h);
- if ((msg = clicon_msg_encode(session_id,
- "",
- NETCONF_BASE_NAMESPACE, username?username:"",
- NETCONF_MESSAGE_ID_ATTR)) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "");
+ cprintf(cb, "");
+ cprintf(cb, "");
+ if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done;
@@ -1173,6 +1273,8 @@ clicon_rpc_close_session(clicon_handle h)
}
retval = 0;
done:
+ if (cb)
+ cbuf_free(cb);
if (xret)
xml_free(xret);
if (msg)
@@ -1181,10 +1283,11 @@ clicon_rpc_close_session(clicon_handle h)
}
/*! Kill other user sessions
+ *
* @param[in] h CLICON handle
* @param[in] session_id Session id of other user session
- * @retval 0 OK
- * @retval -1 Error and logged to syslog
+ * @retval 0 OK
+ * @retval -1 Error and logged to syslog
*/
int
clicon_rpc_kill_session(clicon_handle h,
@@ -1196,16 +1299,25 @@ clicon_rpc_kill_session(clicon_handle h,
cxobj *xerr;
char *username;
uint32_t my_session_id; /* Not the one to kill */
+ cbuf *cb = NULL;
if (session_id_check(h, &my_session_id) < 0)
goto done;
- username = clicon_username_get(h);
- if ((msg = clicon_msg_encode(my_session_id,
- "%u",
- NETCONF_BASE_NAMESPACE,
- username?username:"",
- NETCONF_MESSAGE_ID_ATTR,
- session_id)) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "");
+ cprintf(cb, "%u", session_id);
+ cprintf(cb, "");
+ if ((msg = clicon_msg_encode(my_session_id, "%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done;
@@ -1215,6 +1327,8 @@ clicon_rpc_kill_session(clicon_handle h,
}
retval = 0;
done:
+ if (cb)
+ cbuf_free(cb);
if (xret)
xml_free(xret);
if (msg)
@@ -1223,6 +1337,7 @@ clicon_rpc_kill_session(clicon_handle h,
}
/*! Send validate request to backend daemon
+ *
* @param[in] h CLICON handle
* @param[in] db Name of database
* @retval 1 OK
@@ -1240,16 +1355,25 @@ clicon_rpc_validate(clicon_handle h,
cxobj *xerr;
char *username;
uint32_t session_id;
+ cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0)
goto done;
- username = clicon_username_get(h);
- if ((msg = clicon_msg_encode(session_id,
- "<%s/>",
- NETCONF_BASE_NAMESPACE,
- username?username:"",
- NETCONF_MESSAGE_ID_ATTR,
- db)) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "");
+ cprintf(cb, "<%s/>", db);
+ cprintf(cb, "");
+ if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done;
@@ -1260,6 +1384,8 @@ clicon_rpc_validate(clicon_handle h,
}
retval = 1;
done:
+ if (cb)
+ cbuf_free(cb);
if (msg)
free(msg);
if (xret)
@@ -1268,6 +1394,7 @@ clicon_rpc_validate(clicon_handle h,
}
/*! Commit changes send a commit request to backend daemon
+ *
* @param[in] h CLICON handle
* @param[in] confirmed If set, send commit/confirmed
* @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_xml = NULL;
char *timeout_xml = NULL;
+ cbuf *cb = NULL;
if (persist_id) {
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)
goto done;
- username = clicon_username_get(h);
- if (cancel) {
- msg = clicon_msg_encode(session_id,
- "%s",
- NETCONF_BASE_NAMESPACE,
- username ? username : "",
- NETCONF_MESSAGE_ID_ATTR,
- persist_id ? persist_id_xml : "");
- } else if (confirmed) {
- msg = clicon_msg_encode(session_id,
- "%s%s%s",
- 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,
- "%s",
- NETCONF_BASE_NAMESPACE,
- username ? username : "",
- NETCONF_MESSAGE_ID_ATTR,
- persist ? persist_xml : "");
+
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
}
- if (msg == NULL)
+ cprintf(cb, "");
+ if (cancel) {
+ cprintf(cb, "%s",
+ persist_id ? persist_id_xml : "");
+ }
+ else if (confirmed) {
+ cprintf(cb, "%s%s%s",
+ timeout ? timeout_xml : "",
+ persist_id ? persist_id_xml : "",
+ persist ? persist_xml : "");
+ } else {
+ cprintf(cb, "%s",
+ persist ? persist_xml : "");
+ }
+ cprintf(cb, "");
+ if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done;
@@ -1361,6 +1491,8 @@ clicon_rpc_commit(clicon_handle h,
}
retval = 1;
done:
+ if (cb)
+ cbuf_free(cb);
if (xret)
xml_free(xret);
if (msg)
@@ -1375,6 +1507,7 @@ clicon_rpc_commit(clicon_handle h,
}
/*! Discard all changes in candidate / revert to running
+ *
* @param[in] h CLICON handle
* @retval 0 OK
* @retval -1 Error and logged to syslog
@@ -1388,15 +1521,25 @@ clicon_rpc_discard_changes(clicon_handle h)
cxobj *xerr;
char *username;
uint32_t session_id;
+ cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0)
goto done;
- username = clicon_username_get(h);
- if ((msg = clicon_msg_encode(session_id,
- "",
- NETCONF_BASE_NAMESPACE,
- username?username:"",
- NETCONF_MESSAGE_ID_ATTR)) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "");
+ cprintf(cb, "");
+ cprintf(cb, "");
+ if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done;
@@ -1406,6 +1549,8 @@ clicon_rpc_discard_changes(clicon_handle h)
}
retval = 0;
done:
+ if (cb)
+ cbuf_free(cb);
if (xret)
xml_free(xret);
if (msg)
@@ -1414,13 +1559,13 @@ clicon_rpc_discard_changes(clicon_handle h)
}
/*! Create a new notification subscription
+ *
* @param[in] h Clicon handle
* @param{in] stream name of notificatio/log stream (CLICON is predefined)
* @param{in] filter message filter, eg xpath for xml notifications
* @param[out] s0 socket returned where notification mesages will appear
* @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
*/
int
@@ -1435,20 +1580,31 @@ clicon_rpc_create_subscription(clicon_handle h,
cxobj *xerr;
char *username;
uint32_t session_id;
+ cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0)
goto done;
- username = clicon_username_get(h);
- if ((msg = clicon_msg_encode(session_id,
- ""
- "%s"
- ""
- "",
- NETCONF_BASE_NAMESPACE,
- username?username:"",
- NETCONF_MESSAGE_ID_ATTR,
- EVENT_RFC5277_NAMESPACE,
- stream?stream:"", filter?filter:"")) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "");
+ cprintf(cb, ""
+ "%s"
+ ""
+ "",
+ EVENT_RFC5277_NAMESPACE,
+ stream?stream:"",
+ filter?filter:"");
+ cprintf(cb, "");
+ if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg_persistent(h, msg, &xret, s0) < 0)
goto done;
@@ -1458,6 +1614,8 @@ clicon_rpc_create_subscription(clicon_handle h,
}
retval = 0;
done:
+ if (cb)
+ cbuf_free(cb);
if (xret)
xml_free(xret);
if (msg)
@@ -1466,6 +1624,7 @@ clicon_rpc_create_subscription(clicon_handle h,
}
/*! Send a debug request to backend server
+ *
* @param[in] h CLICON handle
* @param[in] level Debug level
* @retval 0 OK
@@ -1481,17 +1640,26 @@ clicon_rpc_debug(clicon_handle h,
cxobj *xerr;
char *username;
uint32_t session_id;
-
+ cbuf *cb = NULL;
+
if (session_id_check(h, &session_id) < 0)
goto done;
- username = clicon_username_get(h);
- if ((msg = clicon_msg_encode(session_id,
- "%d",
- NETCONF_BASE_NAMESPACE,
- username?username:"",
- NETCONF_MESSAGE_ID_ATTR,
- CLIXON_LIB_NS,
- level)) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "");
+ cprintf(cb, "%d", CLIXON_LIB_NS, level);
+ cprintf(cb, "");
+
+ if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done;
@@ -1505,6 +1673,8 @@ clicon_rpc_debug(clicon_handle h,
}
retval = 0;
done:
+ if (cb)
+ cbuf_free(cb);
if (msg)
free(msg);
if (xret)
@@ -1532,17 +1702,29 @@ clicon_rpc_restconf_debug(clicon_handle h,
cxobj *xerr;
char *username;
uint32_t session_id;
+ cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0)
goto done;
- username = clicon_username_get(h);
- if ((msg = clicon_msg_encode(session_id,
- "%d",
- NETCONF_BASE_NAMESPACE,
- username?username:"",
- NETCONF_MESSAGE_ID_ATTR,
- CLIXON_RESTCONF_NS,
- level)) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "");
+ cprintf(cb, "");
+ cprintf(cb, "%d",
+ CLIXON_RESTCONF_NS,
+ level);
+ cprintf(cb, "");
+ cprintf(cb, "");
+ if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0)
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)
goto done;
done:
+ if (cb)
+ cbuf_free(cb);
if (msg)
free(msg);
if (xret)
@@ -1564,17 +1748,25 @@ clicon_rpc_restconf_debug(clicon_handle h,
return retval;
}
-/*! Send a hello request to the backend server
- * @param[in] h CLICON handle
- * @param[in] level Debug level
- * @retval 0 OK
- * @retval -1 Error and logged to syslog
+/*! Send a hello request to the backend server on INTERNAL netconf connection
+ *
+ * @param[in] h Clixon handle
+ * @param[in] transport RFC 6022 transport.
+ * @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 deviates from RFC6241 slightly in that it waits for a reply, the RFC does not
* 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
clicon_hello_req(clicon_handle h,
+ char *transport,
+ char *source_host,
uint32_t *id)
{
int retval = -1;
@@ -1585,12 +1777,39 @@ clicon_hello_req(clicon_handle h,
char *username;
char *b;
int ret;
+ cbuf *cb = NULL;
+ int clixon_lib = 0;
- username = clicon_username_get(h);
- if ((msg = clicon_msg_encode(0, "%s",
- username?username:"",
- NETCONF_BASE_NAMESPACE,
- NETCONF_BASE_CAPABILITY_1_1)) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "");
+ cprintf(cb, "%s",
+ NETCONF_BASE_CAPABILITY_1_1);
+ cprintf(cb, "");
+
+ if ((msg = clicon_msg_encode(0, "%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done;
@@ -1609,6 +1828,8 @@ clicon_hello_req(clicon_handle h,
}
retval = 0;
done:
+ if (cb)
+ cbuf_free(cb);
if (msg)
free(msg);
if (xret)
@@ -1617,6 +1838,7 @@ clicon_hello_req(clicon_handle h,
}
/*! Send a restart plugin request to backend server
+ *
* @param[in] h CLICON handle
* @param[in] level Debug level
* @retval 0 OK
@@ -1632,17 +1854,26 @@ clicon_rpc_restart_plugin(clicon_handle h,
cxobj *xerr;
char *username;
uint32_t session_id;
+ cbuf *cb = NULL;
if (session_id_check(h, &session_id) < 0)
goto done;
- username = clicon_username_get(h);
- if ((msg = clicon_msg_encode(session_id,
- "%s",
- NETCONF_BASE_NAMESPACE,
- username?username:"",
- NETCONF_MESSAGE_ID_ATTR,
- CLIXON_LIB_NS,
- plugin)) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb, "");
+ cprintf(cb, "%s",
+ CLIXON_LIB_NS, plugin);
+ cprintf(cb, "");
+ if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done;
@@ -1656,6 +1887,8 @@ clicon_rpc_restart_plugin(clicon_handle h,
}
retval = 0;
done:
+ if (cb)
+ cbuf_free(cb);
if (msg)
free(msg);
if (xret)
diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c
index dd74bba2..13de292f 100644
--- a/lib/src/clixon_xml.c
+++ b/lib/src/clixon_xml.c
@@ -2332,6 +2332,42 @@ xml_attr_insert2val(char *instr,
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
* @param[in] level log level, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG.
* @param[in] x XML tree that is logged without prettyprint
diff --git a/lib/src/clixon_xml_default.c b/lib/src/clixon_xml_default.c
index c9574de2..089d9ed3 100644
--- a/lib/src/clixon_xml_default.c
+++ b/lib/src/clixon_xml_default.c
@@ -611,15 +611,10 @@ xml_add_default_tag(cxobj *x,
uint16_t flags)
{
int retval = -1;
- cxobj *xattr;
if (xml_flag(x, flags)) {
/* set default attribute */
- if ((xattr = xml_new("default", x, CX_ATTR)) == NULL)
- goto done;
- if (xml_value_set(xattr, "true") < 0)
- goto done;
- if (xml_prefix_set(xattr, IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX) < 0)
+ if (xml_add_attr(x, "default", "true", IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX, NULL) < 0)
goto done;
}
retval = 0;
diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c
index ea49bb86..bc45ec87 100644
--- a/lib/src/clixon_xml_map.c
+++ b/lib/src/clixon_xml_map.c
@@ -648,7 +648,6 @@ xml_namespace_change(cxobj *x,
/* Add prefix to x, if any */
if (prefix && xml_prefix_set(x, prefix) < 0)
goto done;
-
}
ok:
retval = 0;
diff --git a/lib/src/clixon_xml_nsctx.c b/lib/src/clixon_xml_nsctx.c
index 50afa622..5040f7ab 100644
--- a/lib/src/clixon_xml_nsctx.c
+++ b/lib/src/clixon_xml_nsctx.c
@@ -569,6 +569,7 @@ xml2ns_recurse(cxobj *xt)
* @retval 0 OK
* @retval -1 Error
* @see xml2ns
+ * @see xml_add_attr generic method for adding an attribute
*/
int
xmlns_set(cxobj *x,
diff --git a/lib/src/clixon_yang_module.c b/lib/src/clixon_yang_module.c
index 8d4867b9..be0bfbd5 100644
--- a/lib/src/clixon_yang_module.c
+++ b/lib/src/clixon_yang_module.c
@@ -82,6 +82,12 @@
#include "clixon_xml_map.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_new(void)
{
@@ -95,6 +101,12 @@ modstate_diff_new(void)
return md;
}
+/*! Free modstate structure
+ *
+ * @param[in] md Modstate struct
+ * @retval 0 OK
+ * @see modstate_diff_new
+ */
int
modstate_diff_free(modstate_diff_t *md)
{
@@ -111,7 +123,9 @@ modstate_diff_free(modstate_diff_t *md)
/*! Init the Yang module library
*
* 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
*/
int
@@ -167,6 +181,13 @@ yang_modules_revision(clicon_handle h)
/*! 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
* If also CLICON_MODULE_LIBRARY_RFC7895 is set, module-state is built according to RFC7895 instead
* @see RFC8525
@@ -277,9 +298,9 @@ yms_build(clicon_handle h,
* @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
- * @retval -1 Error (fatal)
- * @retval 0 Statedata callback failed
* @retval 1 OK
+ * @retval 0 Statedata callback failed
+ * @retval -1 Error (fatal)
* @notes NYI: schema, deviation
x +--ro modules-state
x +--ro module-set-id string
diff --git a/test/config.sh.in b/test/config.sh.in
index d6ff7fef..8b5e0913 100755
--- a/test/config.sh.in
+++ b/test/config.sh.in
@@ -73,8 +73,8 @@ DATASTORE_TOP="config"
# clixon yang revisions occuring in tests (see eg yang/clixon/Makefile.in)
CLIXON_AUTOCLI_REV="2022-02-11"
-CLIXON_LIB_REV="2021-12-05"
-CLIXON_CONFIG_REV="2022-03-21"
+CLIXON_LIB_REV="2022-12-01"
+CLIXON_CONFIG_REV="2022-12-01"
CLIXON_RESTCONF_REV="2022-08-01"
CLIXON_EXAMPLE_REV="2022-11-01"
diff --git a/test/test_netconf_monitoring.sh b/test/test_netconf_monitoring.sh
index fa1c3afa..81d394de 100755
--- a/test/test_netconf_monitoring.sh
+++ b/test/test_netconf_monitoring.sh
@@ -68,16 +68,13 @@ new "wait backend"
wait_backend
new "Retrieving all state via operation"
-expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "urn:ietf:params:netconf:base:1.0urn:ietf:params:netconf:base:1.1.*urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring.*candidaterunning.*"
+expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "urn:ietf:params:netconf:base:1.0urn:ietf:params:netconf:base:1.1.*urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring.*candidaterunning.*.*"
# send multiple frames
rpc=$(chunked_framing "")
rpc="${rpc}
$(chunked_framing "candidate")"
-reply=$(chunked_framing "/rpc-reply")
-reply=${reply}$(chunked_framing "candidate32022-12-23T13:18:57.112204Z" "candidate[0-9]+202.*Z" "candidate[0-9]+202.*Z Operation"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "clixon-example2022-01-01yangurn:example:clixonNETCONF.*clixon-sub2022-01-01yangurn:example:clixonNETCONF.*"
+# Session
+new "Retrieve Session"
+expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "[1-9][0-9]*cl:netconf.*.*[0-9][0-9]*[0-9][0-9]*[0-9][0-9]*[0-9][0-9]*.*"
+
# 4.2. Retrieving Schema Instances
# From 2b. bar, version 2008-06-1 in YANG format, via get-schema
new "Retrieving clixon-example schema instance using id, version, format"
diff --git a/yang/clixon/Makefile.in b/yang/clixon/Makefile.in
index e903a2ee..a709524f 100644
--- a/yang/clixon/Makefile.in
+++ b/yang/clixon/Makefile.in
@@ -43,7 +43,7 @@ YANG_INSTALLDIR = @YANG_INSTALLDIR@
# Note: mirror these to test/config.sh.in
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-xml-changelog@2019-03-21.yang
YANGSPECS += clixon-restconf@2022-08-01.yang # 5.9
diff --git a/yang/clixon/clixon-lib@2021-12-05.yang b/yang/clixon/clixon-lib@2021-12-05.yang
index e099faf8..67d702d6 100644
--- a/yang/clixon/clixon-lib@2021-12-05.yang
+++ b/yang/clixon/clixon-lib@2021-12-05.yang
@@ -14,6 +14,19 @@ module clixon-lib {
description
"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 *****
Copyright (C) 2009-2019 Olof Hagsand
diff --git a/yang/clixon/clixon-lib@2021-11-11.yang b/yang/clixon/clixon-lib@2022-12-01.yang
similarity index 84%
rename from yang/clixon/clixon-lib@2021-11-11.yang
rename to yang/clixon/clixon-lib@2022-12-01.yang
index 9e4e8e6c..d17368b1 100644
--- a/yang/clixon/clixon-lib@2021-11-11.yang
+++ b/yang/clixon/clixon-lib@2022-12-01.yang
@@ -6,6 +6,9 @@ module clixon-lib {
import ietf-yang-types {
prefix yang;
}
+ import ietf-netconf-monitoring {
+ prefix ncm;
+ }
organization
"Clicon / Clixon";
@@ -13,9 +16,7 @@ module clixon-lib {
"Olof Hagsand ";
description
- "Clixon Netconf extensions for communication between clients and backend.
-
- ***** BEGIN LICENSE BLOCK *****
+ "***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
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 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 {
description
"Changed: RPC stats extended with YANG stats";
@@ -101,6 +131,23 @@ module clixon-lib {
description
"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 {
description
"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:
- 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-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;
+ status obsolete;
}
rpc debug {
description "Set debug level of backend.";