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.";