* YANG schema mount RFC 8528, state data

This commit is contained in:
Olof Hagsand 2023-01-27 12:38:56 +01:00
parent 51ebbdf12f
commit a8e13047fc
21 changed files with 572 additions and 144 deletions

View file

@ -43,11 +43,14 @@ Expected: beginning of 2023
### New features ### New features
* YANG schema mount RFC 8528 (work in progress) * YANG schema mount RFC 8528
* Experimental
* Restrictions: * Restrictions:
* only schema-ref=inline, not shared-schema * Only schema-ref=inline, not shared-schema
* Only presence containers can be mount-points
* New plugin callback: `ca_yang_mount`
* Standards: RFC 8528 * Standards: RFC 8528
* Enable `YANG_SCHEMA_MOUNT` * To enable: define `YANG_SCHEMA_MOUNT`
* Netconf monitoring RFC 6022 , part 2 * Netconf monitoring RFC 6022 , part 2
* Datastores and sessions * Datastores and sessions
* Added clixon-specific transport identities: cli, snmp, netconf, restconf * Added clixon-specific transport identities: cli, snmp, netconf, restconf

View file

@ -16,7 +16,6 @@ General Public License Version 2; you choose, see [LICENSE.md](LICENSE.md).
Clixon has a master branch continuously tested with CI, but releases are made ca every second month. Latest 6.0.0 release is from November 2022. Next is planned for January 2023. See [CHANGELOG.md](CHANGELOG.md) release history. Clixon has a master branch continuously tested with CI, but releases are made ca every second month. Latest 6.0.0 release is from November 2022. Next is planned for January 2023. See [CHANGELOG.md](CHANGELOG.md) release history.
Clixon interaction is best done posting issues, pull requests, or joining the Clixon interaction is best done posting issues, pull requests, or joining the
Matrix channels: https://matrix.to/#/#clixondev:matrix.org for general info and discussion, and Matrix clixon forum https://matrix.to/#/#clixonforum:matrix.org.
https://matrix.to/#/#clixontech:matrix.org for technical details and issues.
Clixon is sponsored by [Rubicon Communications LLC(Netgate)](https://www.netgate.com/) and [Akamai Technologies, Inc.](https://www.akamai.com). Clixon is sponsored by [Rubicon Communications LLC(Netgate)](https://www.netgate.com/) and [Akamai Technologies, Inc.](https://www.akamai.com).

View file

@ -298,7 +298,7 @@ get_statedata(clicon_handle h,
goto fail; goto fail;
} }
#ifdef YANG_SCHEMA_MOUNT #ifdef YANG_SCHEMA_MOUNT
if ((ret = schema_mounts_state_get(h, yspec, xpath, nsc, xret, &xerr)) < 0) if ((ret = yang_schema_mount_statedata(h, yspec, xpath, nsc, xret, &xerr)) < 0)
goto done; goto done;
if (ret == 0){ if (ret == 0){
if (clixon_netconf_internal_error(xerr, " . Internal error, schema_mounts_state_get returned invalid XML", NULL) < 0) if (clixon_netconf_internal_error(xerr, " . Internal error, schema_mounts_state_get returned invalid XML", NULL) < 0)

View file

@ -880,6 +880,50 @@ example_upgrade(clicon_handle h,
return retval; return retval;
} }
/*! Example YANG schema mount
*
* Given an XML mount-point xt, return XML yang-lib modules-set
* @param[in] h Clixon handle
* @param[in] xt XML mount-point in XML tree
* @param[out] yanglib XML yang-lib module-set tree
* @retval 0 OK
* @retval -1 Error
* XXX hardcoded to clixon-example@2022-11-01.yang regardless of xt
* @see RFC 8528
*/
int
main_yang_mount(clicon_handle h,
cxobj *xt,
cxobj **yanglib)
{
int retval = -1;
cbuf *cb = NULL;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cb, "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">");
cprintf(cb, "<module-set>");
cprintf(cb, "<name>mount</name>");
cprintf(cb, "<module>");
cprintf(cb, "<name>clixon-example</name>");
cprintf(cb, "<revision>2022-11-01</revision>");
cprintf(cb, "<namespace>urn:example:urn</namespace>");
cprintf(cb, "</module>");
cprintf(cb, "</module-set>");
cprintf(cb, "</yang-library>");
if (clixon_xml_parse_string(cbuf_get(cb), YB_NONE, NULL, yanglib, NULL) < 0)
goto done;
if (xml_rootchild(*yanglib, 0, yanglib) < 0)
goto done;
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}
/*! Testcase module-specific upgrade function moving interfaces-state to interfaces /*! Testcase module-specific upgrade function moving interfaces-state to interfaces
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] xn XML tree to be updated * @param[in] xn XML tree to be updated
@ -1302,6 +1346,7 @@ static clixon_plugin_api api = {
.ca_trans_end=main_end, /* trans end */ .ca_trans_end=main_end, /* trans end */
.ca_trans_abort=main_abort, /* trans abort */ .ca_trans_abort=main_abort, /* trans abort */
.ca_datastore_upgrade=example_upgrade, /* general-purpose upgrade. */ .ca_datastore_upgrade=example_upgrade, /* general-purpose upgrade. */
.ca_yang_mount=main_yang_mount /* RFC 8528 schema mount */
}; };
/*! Backend plugin initialization /*! Backend plugin initialization

View file

@ -192,8 +192,8 @@
*/ */
#undef NETCONF_DEFAULT_RETRIEVAL_REPORT_ALL #undef NETCONF_DEFAULT_RETRIEVAL_REPORT_ALL
/*! Development option for RFC 8528 YANG schema mount /*! RFC 8528 YANG schema mount
* Work-in-progress * Experimental
* See also test/test_yang_schema_mount.sh * See also test/test_yang_schema_mount.sh
*/ */
#undef YANG_SCHEMA_MOUNT #undef YANG_SCHEMA_MOUNT

View file

@ -269,6 +269,26 @@ typedef char *(cli_prompthook_t)(clicon_handle, char *mode);
*/ */
typedef int (datastore_upgrade_t)(clicon_handle h, const char *db, cxobj *xt, modstate_diff_t *msd); typedef int (datastore_upgrade_t)(clicon_handle h, const char *db, cxobj *xt, modstate_diff_t *msd);
/*! YANG schema mount
*
* Given an XML mount-point xt, return XML yang-lib modules-set
* Return yanglib as XML tree on the RFC8525 form:
* <yang-library>
* <module-set>
* <module>...</module>
* ...
* </module-set>
* </yang-library>
* No need to YANG bind.
* @param[in] h Clixon handle
* @param[in] xt XML mount-point in XML tree
* @param[out] yanglib XML yang-lib module-set tree. Freed by caller.
* @retval 0 OK
* @retval -1 Error
* @see RFC 8528 (schema-mount) and RFC 8525 (yang-lib)
*/
typedef int (yang_mount_t)(clicon_handle h, cxobj *xt, cxobj **yanglib);
/*! Startup status for use in startup-callback /*! Startup status for use in startup-callback
* Note that for STARTUP_ERR and STARTUP_INVALID, running runs in failsafe mode * Note that for STARTUP_ERR and STARTUP_INVALID, running runs in failsafe mode
* and startup contains the erroneous or invalid database. * and startup contains the erroneous or invalid database.
@ -322,6 +342,7 @@ struct clixon_plugin_api{
trans_cb_t *cb_trans_end; /* Transaction completed */ trans_cb_t *cb_trans_end; /* Transaction completed */
trans_cb_t *cb_trans_abort; /* Transaction aborted */ trans_cb_t *cb_trans_abort; /* Transaction aborted */
datastore_upgrade_t *cb_datastore_upgrade; /* General-purpose datastore upgrade */ datastore_upgrade_t *cb_datastore_upgrade; /* General-purpose datastore upgrade */
yang_mount_t *cb_yang_mount; /* RFC 8528 schema mount */
} cau_backend; } cau_backend;
} u; } u;
}; };
@ -344,6 +365,7 @@ struct clixon_plugin_api{
#define ca_trans_end u.cau_backend.cb_trans_end #define ca_trans_end u.cau_backend.cb_trans_end
#define ca_trans_abort u.cau_backend.cb_trans_abort #define ca_trans_abort u.cau_backend.cb_trans_abort
#define ca_datastore_upgrade u.cau_backend.cb_datastore_upgrade #define ca_datastore_upgrade u.cau_backend.cb_datastore_upgrade
#define ca_yang_mount u.cau_backend.cb_yang_mount
/* /*
* Macros * Macros
@ -419,6 +441,9 @@ int clixon_plugin_extension_all(clicon_handle h, yang_stmt *yext, yang_stmt *ys)
int clixon_plugin_datastore_upgrade_one(clixon_plugin_t *cp, clicon_handle h, const char *db, cxobj *xt, modstate_diff_t *msd); int clixon_plugin_datastore_upgrade_one(clixon_plugin_t *cp, clicon_handle h, const char *db, cxobj *xt, modstate_diff_t *msd);
int clixon_plugin_datastore_upgrade_all(clicon_handle h, const char *db, cxobj *xt, modstate_diff_t *msd); int clixon_plugin_datastore_upgrade_all(clicon_handle h, const char *db, cxobj *xt, modstate_diff_t *msd);
int clixon_plugin_yang_mount_one(clixon_plugin_t *cp, clicon_handle h, cxobj *xt, cxobj **yanglib);
int clixon_plugin_yang_mount_all(clicon_handle h, cxobj *xt, cxobj **yanglib);
/* rpc callback API */ /* rpc callback API */
int rpc_callback_register(clicon_handle h, clicon_rpc_cb cb, void *arg, const char *ns, const char *name); int rpc_callback_register(clicon_handle h, clicon_rpc_cb cb, void *arg, const char *ns, const char *name);
int rpc_callback_call(clicon_handle h, cxobj *xe, void *arg, int *nrp, cbuf *cbret); int rpc_callback_call(clicon_handle h, cxobj *xe, void *arg, int *nrp, cbuf *cbret);

View file

@ -214,6 +214,7 @@ cg_var *yang_cv_get(yang_stmt *ys);
int yang_cv_set(yang_stmt *ys, cg_var *cv); int yang_cv_set(yang_stmt *ys, cg_var *cv);
cvec *yang_cvec_get(yang_stmt *ys); cvec *yang_cvec_get(yang_stmt *ys);
int yang_cvec_set(yang_stmt *ys, cvec *cvv); int yang_cvec_set(yang_stmt *ys, cvec *cvv);
cg_var *yang_cvec_add(yang_stmt *ys, enum cv_type type, char *name);
uint16_t yang_flag_get(yang_stmt *ys, uint16_t flag); uint16_t yang_flag_get(yang_stmt *ys, uint16_t flag);
int yang_flag_set(yang_stmt *ys, uint16_t flag); int yang_flag_set(yang_stmt *ys, uint16_t flag);
int yang_flag_reset(yang_stmt *ys, uint16_t flag); int yang_flag_reset(yang_stmt *ys, uint16_t flag);

View file

@ -78,5 +78,6 @@ yang_stmt *yang_find_module_by_name_revision(yang_stmt *yspec, const char *name,
yang_stmt *yang_find_module_by_name(yang_stmt *yspec, char *name); yang_stmt *yang_find_module_by_name(yang_stmt *yspec, char *name);
int yang_metadata_annotation_check(cxobj *x, yang_stmt *ymod, int *ismeta); int yang_metadata_annotation_check(cxobj *x, yang_stmt *ymod, int *ismeta);
int yang_metadata_init(clicon_handle h); int yang_metadata_init(clicon_handle h);
int yang_lib2yspec(clicon_handle h, cxobj *yanglib,yang_stmt *yspec);
#endif /* _CLIXON_YANG_MODULE_H_ */ #endif /* _CLIXON_YANG_MODULE_H_ */

View file

@ -52,6 +52,7 @@
* Prototypes * Prototypes
*/ */
yang_stmt *yang_parse_file(FILE *fp, const char *name, yang_stmt *ysp); yang_stmt *yang_parse_file(FILE *fp, const char *name, yang_stmt *ysp);
int yang_file_find_match(clicon_handle h, const char *module, const char *revision, cbuf *fbuf);
yang_stmt *yang_parse_filename(const char *filename, yang_stmt *ysp); yang_stmt *yang_parse_filename(const char *filename, yang_stmt *ysp);
int yang_parse_post(clicon_handle h, yang_stmt *yspec, int modmin); int yang_parse_post(clicon_handle h, yang_stmt *yspec, int modmin);
int yang_spec_parse_module(clicon_handle h, const char *module, int yang_spec_parse_module(clicon_handle h, const char *module,

View file

@ -44,12 +44,26 @@
*/ */
#define YANG_SCHEMA_MOUNT_NAMESPACE "urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount" #define YANG_SCHEMA_MOUNT_NAMESPACE "urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount"
/* Limitations/deviations from RFC 8528 */
/*! Only support YANG presende containers as mount-points
* This is a limitation of othe current implementation
*/
#define YANG_SCHEMA_MOUNT_ONLY_PRESENCE_CONTAINERS
/*! Force add ietf-yang-library@2019-01-04 on all mount-points
* This is a limitation of othe current implementation
*/
#define YANG_SCHEMA_MOUNT_YANG_LIB_FORCE
/* /*
* Prototypes * Prototypes
*/ */
int yang_schema_mount_point(yang_stmt *y); int yang_schema_mount_point(yang_stmt *y);
int xml_yang_mount_get(cxobj *x, yang_stmt **yspec);
int schema_mounts_state_get(clicon_handle h, yang_stmt *yspec, char *xpath, cvec *nsc, cxobj **xret, cxobj **xerr); int xml_yang_mount_set(cxobj *x, yang_stmt *yspec);
int yang_schema_unknown(clicon_handle h, yang_stmt *yext, yang_stmt *ys); int xml_yang_mount_freeall(cvec *cvv);
int yang_schema_mount_statedata(clicon_handle h, yang_stmt *yspec, char *xpath, cvec *nsc, cxobj **xret, cxobj **xerr);
int yang_schema_yanglib_parse_mount(clicon_handle h, cxobj *xt);
int yang_schema_get_child(clicon_handle h, cxobj *x1, cxobj *x1c, yang_stmt **yc);
#endif /* _CLIXON_YANG_SCHEMA_MOUNT_H_ */ #endif /* _CLIXON_YANG_SCHEMA_MOUNT_H_ */

View file

@ -75,6 +75,7 @@
#include "clixon_netconf_lib.h" #include "clixon_netconf_lib.h"
#include "clixon_yang_type.h" #include "clixon_yang_type.h"
#include "clixon_yang_module.h" #include "clixon_yang_module.h"
#include "clixon_yang_schema_mount.h"
#include "clixon_xml_nsctx.h" #include "clixon_xml_nsctx.h"
#include "clixon_xml_io.h" #include "clixon_xml_io.h"
#include "clixon_xml_default.h" #include "clixon_xml_default.h"
@ -854,7 +855,10 @@ text_modify(clicon_handle h,
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) { while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
x1cname = xml_name(x1c); x1cname = xml_name(x1c);
/* Get yang spec of the child by child matching */ /* Get yang spec of the child by child matching */
yc = yang_find_datanode(y0, x1cname); if ((yc = yang_find_datanode(y0, x1cname)) == NULL){
#ifdef YANG_SCHEMA_MOUNT
yc = xml_spec(x1c);
#endif
if (yc == NULL){ if (yc == NULL){
if (clicon_option_bool(h, "CLICON_YANG_UNKNOWN_ANYDATA") == 1){ if (clicon_option_bool(h, "CLICON_YANG_UNKNOWN_ANYDATA") == 1){
/* Add dummy Y_ANYDATA yang stmt, see ysp_add */ /* Add dummy Y_ANYDATA yang stmt, see ysp_add */
@ -871,6 +875,7 @@ text_modify(clicon_handle h,
goto fail; goto fail;
} }
} }
}
/* There is a cornercase (eg augment) of multi-namespace trees where /* There is a cornercase (eg augment) of multi-namespace trees where
* the yang child has a different namespace. * the yang child has a different namespace.
* As an alternative, return in populate where this is detected first time. * As an alternative, return in populate where this is detected first time.
@ -898,8 +903,11 @@ text_modify(clicon_handle h,
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) { while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
x0c = x0vec[i++]; x0c = x0vec[i++];
x1cname = xml_name(x1c); x1cname = xml_name(x1c);
if ((yc = yang_find_datanode(y0, x1cname)) == NULL){
yc = yang_find_datanode(y0, x1cname); #ifdef YANG_SCHEMA_MOUNT
yc = xml_spec(x1c);
#endif
}
if ((ret = text_modify(h, x0c, x0, x0t, x1c, x1t, if ((ret = text_modify(h, x0c, x0, x0t, x1c, x1t,
yc, op, yc, op,
username, xnacm, permit, cbret)) < 0) username, xnacm, permit, cbret)) < 0)

View file

@ -547,9 +547,9 @@ plugin_context_get(void)
* @param[in,out] wh Either: NULL for init, will be assigned, OR previous handle (will be freed) * @param[in,out] wh Either: NULL for init, will be assigned, OR previous handle (will be freed)
* @param[in] name Name of plugin for logging. Can be other name, context dependent * @param[in] name Name of plugin for logging. Can be other name, context dependent
* @param[in] fn Typically name of callback, or caller function * @param[in] fn Typically name of callback, or caller function
* @retval -1 Error
* @retval 0 Fail, log on syslog using LOG_WARNING
* @retval 1 OK * @retval 1 OK
* @retval 0 Fail, log on syslog using LOG_WARNING
* @retval -1 Error
* @note Only logs error, does not generate error * @note Only logs error, does not generate error
* @note name and fn are context dependent, since the env of callback calls are very different * @note name and fn are context dependent, since the env of callback calls are very different
* @see plugin_context_get * @see plugin_context_get
@ -786,9 +786,9 @@ clixon_plugin_exit_all(clicon_handle h)
* @param[out] authp NULL: Credentials failed, no user set (401 returned). * @param[out] authp NULL: Credentials failed, no user set (401 returned).
* String: Credentials OK, the associated user, must be mallloc:ed * String: Credentials OK, the associated user, must be mallloc:ed
* Parameter signtificant only if retval is 1/OK * Parameter signtificant only if retval is 1/OK
* @retval -1 Fatal error
* @retval 0 Ignore, undecided, not handled, same as no callback
* @retval 1 OK, see authp parameter on result. * @retval 1 OK, see authp parameter on result.
* @retval 0 Ignore, undecided, not handled, same as no callback
* @retval -1 Fatal error
* @note If authenticated either a callback was called and clicon_username_set() * @note If authenticated either a callback was called and clicon_username_set()
* Or no callback was found. * Or no callback was found.
*/ */
@ -833,9 +833,9 @@ clixon_plugin_auth_one(clixon_plugin_t *cp,
* @param[out] authp NULL: Credentials failed, no user set (401 returned). * @param[out] authp NULL: Credentials failed, no user set (401 returned).
* String: Credentials OK, the associated user, must be mallloc:ed * String: Credentials OK, the associated user, must be mallloc:ed
* Parameter signtificant only if retval is 1/OK * Parameter signtificant only if retval is 1/OK
* @retval -1 Fatal error
* @retval 0 Ignore, undecided, not handled, same as no callback
* @retval 1 OK, see authp parameter for result. * @retval 1 OK, see authp parameter for result.
* @retval 0 Ignore, undecided, not handled, same as no callback
* @retval -1 Fatal error
* @note If authp returns string, it should be malloced * @note If authp returns string, it should be malloced
*/ */
int int
@ -981,8 +981,8 @@ clixon_plugin_datastore_upgrade_one(clixon_plugin_t *cp,
* @param[in] db Name of datastore, eg "running", "startup" or "tmp" * @param[in] db Name of datastore, eg "running", "startup" or "tmp"
* @param[in] xt XML tree. Upgrade this "in place" * @param[in] xt XML tree. Upgrade this "in place"
* @param[in] msd Module-state diff, info on datastore module-state * @param[in] msd Module-state diff, info on datastore module-state
* @retval -1 Error
* @retval 0 OK * @retval 0 OK
* @retval -1 Error
* Upgrade datastore on load before or as an alternative to module-specific upgrading mechanism * Upgrade datastore on load before or as an alternative to module-specific upgrading mechanism
*/ */
int int
@ -1003,6 +1003,72 @@ clixon_plugin_datastore_upgrade_all(clicon_handle h,
return retval; return retval;
} }
/*! Call plugin YANG schema mount
*
* (No need to be yang bound?
* @param[in] cp Plugin handle
* @param[in] h Clixon handle
* @param[in] xt XML mount-point in XML tree
* @param[out] yanglib XML yang-lib module-set tree
* @retval 0 OK
* @retval -1 Error
*/
int
clixon_plugin_yang_mount_one(clixon_plugin_t *cp,
clicon_handle h,
cxobj *xt,
cxobj **yanglib)
{
int retval = -1;
yang_mount_t *fn;
void *wh = NULL;
if ((fn = cp->cp_api.ca_yang_mount) != NULL){
wh = NULL;
if (plugin_context_check(h, &wh, cp->cp_name, __FUNCTION__) < 0)
goto done;
if (fn(h, xt, yanglib) < 0) {
if (clicon_errno < 0)
clicon_log(LOG_WARNING, "%s: Internal error: Yang mount callback in plugin: %s returned -1 but did not make a clicon_err call",
__FUNCTION__, cp->cp_name);
goto done;
}
if (plugin_context_check(h, &wh, cp->cp_name, __FUNCTION__) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Call plugin YANG schema mount in all plugins, break at first return
*
* @param[in] h Clixon handle
* @param[in] xt XML mount-point in XML tree
* @param[out] yanglib XML yang-lib module-set tree (freed by caller)
* @retval 0 OK
* @retval -1 Error
*/
int
clixon_plugin_yang_mount_all(clicon_handle h,
cxobj *xt,
cxobj **yanglib)
{
int retval = -1;
clixon_plugin_t *cp = NULL;
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if (clixon_plugin_yang_mount_one(cp, h, xt, yanglib) < 0)
goto done;
if (*yanglib)
break;
}
retval = 0;
done:
return retval;
}
/*-------------------------------------------------------------------- /*--------------------------------------------------------------------
* RPC callbacks for both client/frontend and backend plugins. * RPC callbacks for both client/frontend and backend plugins.
*/ */
@ -1166,7 +1232,7 @@ rpc_callback_call(clicon_handle h,
*nrp = nr; *nrp = nr;
retval = 1; /* 0: none found, >0 nr of handlers called */ retval = 1; /* 0: none found, >0 nr of handlers called */
done: done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); clicon_debug(CLIXON_DBG_DETAIL, "%s retval:%d", __FUNCTION__, retval);
return retval; return retval;
fail: fail:
retval = 0; retval = 0;
@ -1373,9 +1439,9 @@ upgrade_callback_delete_all(clicon_handle h)
* @param[in] from From revision on the form YYYYMMDD (if DEL or CHANGE) * @param[in] from From revision on the form YYYYMMDD (if DEL or CHANGE)
* @param[in] to To revision on the form YYYYMMDD (if ADD or CHANGE) * @param[in] to To revision on the form YYYYMMDD (if ADD or CHANGE)
* @param[out] cbret Return XML (as string in CLIgen buffer), on invalid * @param[out] cbret Return XML (as string in CLIgen buffer), on invalid
* @retval -1 Error
* @retval 0 Invalid - cbret contains reason as netconf
* @retval 1 OK * @retval 1 OK
* @retval 0 Invalid - cbret contains reason as netconf
* @retval -1 Error
* @see upgrade_callback_reg_fn which registers the callbacks * @see upgrade_callback_reg_fn which registers the callbacks
*/ */
int int

View file

@ -433,6 +433,7 @@ clicon_msg_rcv(int s,
clicon_debug(CLIXON_DBG_MSG, "Recv: %s", (*msg)->op_body); clicon_debug(CLIXON_DBG_MSG, "Recv: %s", (*msg)->op_body);
retval = 0; retval = 0;
done: done:
clicon_debug(CLIXON_DBG_DETAIL, "%s retval:%d", __FUNCTION__, retval);
if (0) if (0)
set_signal(SIGINT, oldhandler, NULL); set_signal(SIGINT, oldhandler, NULL);
return retval; return retval;
@ -643,6 +644,7 @@ clicon_rpc(int sock,
struct clicon_msg *reply = NULL; struct clicon_msg *reply = NULL;
char *data = NULL; char *data = NULL;
clicon_debug(CLIXON_DBG_DETAIL, "%s", __FUNCTION__);
if (clicon_msg_send(sock, msg) < 0) if (clicon_msg_send(sock, msg) < 0)
goto done; goto done;
if (clicon_msg_rcv(sock, &reply, eof) < 0) if (clicon_msg_rcv(sock, &reply, eof) < 0)
@ -658,6 +660,7 @@ clicon_rpc(int sock,
ok: ok:
retval = 0; retval = 0;
done: done:
clicon_debug(CLIXON_DBG_DETAIL, "%s retval:%d", __FUNCTION__, retval);
if (reply) if (reply)
free(reply); free(reply);
return retval; return retval;

View file

@ -198,6 +198,7 @@ clicon_rpc_msg(clicon_handle h,
int s = -1; int s = -1;
int eof = 0; int eof = 0;
clicon_debug(CLIXON_DBG_DETAIL, "%s", __FUNCTION__);
#ifdef RPC_USERNAME_ASSERT #ifdef RPC_USERNAME_ASSERT
assert(strstr(msg->op_body, "username")!=NULL); /* XXX */ assert(strstr(msg->op_body, "username")!=NULL); /* XXX */
#endif #endif
@ -245,6 +246,7 @@ clicon_rpc_msg(clicon_handle h,
} }
retval = 0; retval = 0;
done: done:
clicon_debug(CLIXON_DBG_DETAIL, "%s %d", __FUNCTION__, retval);
if (retdata) if (retdata)
free(retdata); free(retdata);
if (xret) if (xret)
@ -961,6 +963,7 @@ clicon_rpc_get(clicon_handle h,
yang_stmt *yspec; yang_stmt *yspec;
cvec *nscd = NULL; cvec *nscd = NULL;
clicon_debug(CLIXON_DBG_DETAIL, "%s", __FUNCTION__);
if (session_id_check(h, &session_id) < 0) if (session_id_check(h, &session_id) < 0)
goto done; goto done;
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){
@ -1044,6 +1047,7 @@ clicon_rpc_get(clicon_handle h,
} }
retval = 0; retval = 0;
done: done:
clicon_debug(CLIXON_DBG_DETAIL, "%s %d", __FUNCTION__, retval);
if (nscd) if (nscd)
cvec_free(nscd); cvec_free(nscd);
if (cb) if (cb)

View file

@ -69,6 +69,8 @@
#include "clixon_options.h" #include "clixon_options.h"
#include "clixon_data.h" #include "clixon_data.h"
#include "clixon_yang_module.h" #include "clixon_yang_module.h"
#include "clixon_yang_schema_mount.h"
#include "clixon_plugin.h"
#include "clixon_xml_nsctx.h" #include "clixon_xml_nsctx.h"
#include "clixon_xpath_ctx.h" #include "clixon_xpath_ctx.h"
#include "clixon_xpath.h" #include "clixon_xpath.h"
@ -451,7 +453,34 @@ xml_bind_yang0_opt(clicon_handle h,
goto ok; goto ok;
strip_body_objects(xt); strip_body_objects(xt);
ybc = YB_PARENT; ybc = YB_PARENT;
#ifdef YANG_SCHEMA_MOUNT // Maybe in populate?
yspec1 = NULL;
if ((ret = xml_yang_mount_get(xt, &yspec1)) < 0)
goto done;
if (ret == 0)
yspec1 = yspec; yspec1 = yspec;
else{
if (yspec1)
ybc = YB_MODULE;
else if (h == NULL)
goto ok; /* treat as anydata */
else{
if ((ret = yang_schema_yanglib_parse_mount(h, xt)) < 0)
goto done;
if (ret == 0)
goto ok;
/* Try again */
if ((ret = xml_yang_mount_get(xt, &yspec1)) < 0)
goto done;
if (yspec1)
ybc = YB_MODULE;
else
goto ok;
}
}
#else
yspec1 = yspec;
#endif
xc = NULL; /* Apply on children */ xc = NULL; /* Apply on children */
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) { while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
/* It is xml2ns in populate_self_parent that needs improvement */ /* It is xml2ns in populate_self_parent that needs improvement */

View file

@ -307,11 +307,10 @@ yang_cvec_set(yang_stmt *ys,
* @retval cv The new cligen variable * @retval cv The new cligen variable
* @retval NULL Error * @retval NULL Error
*/ */
static cg_var * cg_var *
yang_cvec_add(yang_stmt *ys, yang_cvec_add(yang_stmt *ys,
enum cv_type type, enum cv_type type,
char *name) char *name)
{ {
cg_var *cv; cg_var *cv;
cvec *cvv; cvec *cvv;
@ -657,18 +656,27 @@ ys_free1(yang_stmt *ys,
cg_var *cv; cg_var *cv;
rpc_callback_t *rc; rpc_callback_t *rc;
if (ys->ys_argument){
free(ys->ys_argument);
ys->ys_argument = NULL;
}
if ((cv = yang_cv_get(ys)) != NULL){ if ((cv = yang_cv_get(ys)) != NULL){
yang_cv_set(ys, NULL); /* only frees on replace */ yang_cv_set(ys, NULL); /* only frees on replace */
cv_free(cv); cv_free(cv);
} }
if (ys->ys_cvec){ if (ys->ys_cvec){
#ifdef YANG_SCHEMA_MOUNT
/* Schema mount uses cvec in unknown to keep track of all yspecs
* Freed here once.
*/
if (yang_keyword_get(ys) == Y_UNKNOWN &&
strcmp(yang_argument_get(ys), "yangmnt:mount-point")==0){
xml_yang_mount_freeall(ys->ys_cvec);
}
#endif
cvec_free(ys->ys_cvec); cvec_free(ys->ys_cvec);
ys->ys_cvec = NULL; ys->ys_cvec = NULL;
} }
if (ys->ys_argument){
free(ys->ys_argument);
ys->ys_argument = NULL;
}
if (ys->ys_typecache){ if (ys->ys_typecache){
yang_type_cache_free(ys->ys_typecache); yang_type_cache_free(ys->ys_typecache);
ys->ys_typecache = NULL; ys->ys_typecache = NULL;
@ -1131,16 +1139,7 @@ yang_find_datanode(yang_stmt *yn,
yang_stmt *yspec; yang_stmt *yspec;
yang_stmt *ysmatch = NULL; yang_stmt *ysmatch = NULL;
char *name; char *name;
#ifdef YANG_SCHEMA_MOUNT
int ret;
/* Sanity-check mount-point extension */
if ((ret = yang_schema_mount_point(yn)) < 0)
goto done;
if (ret == 1){
; // NYI
}
#endif
ys = NULL; ys = NULL;
while ((ys = yn_each(yn, ys)) != NULL){ while ((ys = yn_each(yn, ys)) != NULL){
if (yang_keyword_get(ys) == Y_CHOICE){ /* Look for its children */ if (yang_keyword_get(ys) == Y_CHOICE){ /* Look for its children */
@ -1904,7 +1903,7 @@ yang_spec_print(FILE *f,
fprintf(f, "%s", yang_key2str(ym->ys_keyword)); fprintf(f, "%s", yang_key2str(ym->ys_keyword));
fprintf(f, " %s", ym->ys_argument); fprintf(f, " %s", ym->ys_argument);
if ((yrev = yang_find(ym, Y_REVISION, NULL)) != NULL){ if ((yrev = yang_find(ym, Y_REVISION, NULL)) != NULL){
fprintf(f, "@%u", cv_uint32_get(yang_cv_get(yrev))); fprintf(f, "@%s", yang_argument_get(yrev));
} }
fprintf(f, ".yang"); fprintf(f, ".yang");
fprintf(f, "\n"); fprintf(f, "\n");
@ -2754,10 +2753,6 @@ ys_populate_unknown(clicon_handle h,
clicon_debug(1, "plugin_extension() failed"); clicon_debug(1, "plugin_extension() failed");
return -1; return -1;
} }
#endif
#ifdef YANG_SCHEMA_MOUNT
if (yang_schema_unknown(h, yext, ys) < 0)
goto done;
#endif #endif
/* Make extension callbacks that may alter yang structure /* Make extension callbacks that may alter yang structure
* Note: this may be a "layering" violation: assuming plugins are loaded * Note: this may be a "layering" violation: assuming plugins are loaded

View file

@ -96,6 +96,7 @@ struct yang_stmt{
types as <module>:<id> list types as <module>:<id> list
Y_UNIQUE: vector of descendant schema node ids Y_UNIQUE: vector of descendant schema node ids
Y_EXTENSION: vector of instantiated UNKNOWNSo Y_EXTENSION: vector of instantiated UNKNOWNSo
Y_UNKNOWN: app-dep: yang-mount-points
*/ */
yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */ yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */
char *ys_when_xpath; /* Special conditional for a "when"-associated augment/uses xpath */ char *ys_when_xpath; /* Special conditional for a "when"-associated augment/uses xpath */

View file

@ -210,8 +210,10 @@ yang_modules_state_build(clicon_handle h,
yang_stmt *ysub; yang_stmt *ysub;
char *name; char *name;
if ((ylib = yang_find(yspec, Y_MODULE, module)) == NULL && /* In case of several mountpoints, this is always the top-level */
(ylib = yang_find(yspec, Y_SUBMODULE, module)) == NULL){ if ((ylib = yang_find(yspec, Y_MODULE, module)) == NULL
/* && (ylib = yang_find(yspec0, Y_SUBMODULE, module)) == NULL */
){
clicon_err(OE_YANG, 0, "%s not found", module); clicon_err(OE_YANG, 0, "%s not found", module);
goto done; goto done;
} }
@ -219,7 +221,6 @@ yang_modules_state_build(clicon_handle h,
clicon_err(OE_YANG, 0, "%s yang namespace not found", module); clicon_err(OE_YANG, 0, "%s yang namespace not found", module);
goto done; goto done;
} }
if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895")){ if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895")){
cprintf(cb,"<modules-state xmlns=\"%s\">", yang_argument_get(yns)); cprintf(cb,"<modules-state xmlns=\"%s\">", yang_argument_get(yns));
cprintf(cb,"<module-set-id>%s</module-set-id>", msid); cprintf(cb,"<module-set-id>%s</module-set-id>", msid);
@ -824,3 +825,55 @@ yang_metadata_init(clicon_handle h)
done: done:
return retval; return retval;
} }
/*! Given a yang-lib module-set XML tree, parse all modules into an yspec
*
* This function is used where a yang-lib module-set is available to populate an
* XML mount-point.
* @param[in] h Clicon handle
* @param[in] xylib yang-lib XML tree on the form <yang-lib>...
* @param[in] yspec Will be populated with YANGs, is consumed
* @retval 1 OK
* @retval 0 Parse error
* @retval -1 Error
* @see xml_schema_add_mount_points
*/
int
yang_lib2yspec(clicon_handle h,
cxobj *yanglib,
yang_stmt *yspec)
{
int retval = -1;
cxobj *xi;
char *name;
char *revision;
cvec *nsc = NULL;
cxobj **vec = NULL;
size_t veclen;
int i;
if (xpath_vec(yanglib, nsc, "module-set/module", &vec, &veclen) < 0)
goto done;
for (i=0; i<veclen; i++){
xi = vec[i];
if ((name = xml_find_body(xi, "name")) == NULL)
continue;
if ((revision = xml_find_body(xi, "revision")) == NULL)
continue;
if (yang_spec_parse_module(h, name, revision, yspec) < 0)
goto fail;
}
#ifdef YANG_SCHEMA_MOUNT_YANG_LIB_FORCE
/* XXX: Ensure yang-lib is always there otherwise get state dont work for mountpoint */
if (yang_spec_parse_module(h, "ietf-yang-library", "2019-01-04", yspec) < 0)
goto fail;
#endif
retval = 1;
done:
if (vec)
free(vec);
return retval;
fail:
retval = 0;
goto done;
}

View file

@ -1028,7 +1028,10 @@ value_stmt : K_VALUE integer_value_str stmtend
; ;
/* Grouping */ /* Grouping */
grouping_stmt : K_GROUPING identifier_str grouping_stmt : K_GROUPING identifier_str ';'
{ if (ysp_add(_yy, Y_GROUPING, $2, NULL) == NULL) _YYERROR("grouping_stmt");
_PARSE_DEBUG("grouping-stmt -> GROUPING id-arg-str ;"); }
| K_GROUPING identifier_str
{ if (ysp_add_push(_yy, Y_GROUPING, $2, NULL) == NULL) _YYERROR("grouping_stmt"); } { if (ysp_add_push(_yy, Y_GROUPING, $2, NULL) == NULL) _YYERROR("grouping_stmt"); }
'{' grouping_substmts '}' '{' grouping_substmts '}'
{ if (ystack_pop(_yy) < 0) _YYERROR("grouping_stmt"); { if (ystack_pop(_yy) < 0) _YYERROR("grouping_stmt");

View file

@ -32,6 +32,19 @@
***** END LICENSE BLOCK ***** ***** END LICENSE BLOCK *****
* RFC 8525 Yang schema mount support * RFC 8525 Yang schema mount support
*
* Structure of mount-points in XML:
* YANG mount extentsion -->* YANG unknown mount stmt -->* XML mount-points
* |
* cvec mapping xpath->yspec mountpoint
*
* The calls into this code are:
* 1. yang_schema_mount_point() Check that a yang nod eis mount-point
* 2. xml_yang_mount_get(): from xml_bind_yang and xmldb_put
* 3. xml_yang_mount_freeall(): from ys_free1 when deallocatin YANG trees
* 4. yang_schema_mount_statedata(): from get_common/get_statedata to retrieve system state
* 5. yang_schema_yanglib_parse_mount(): from xml_bind_yang to parse and mount
* 6. yang_schema_get_child(): from xmldb_put/text_modify when adding new XML nodes
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -43,6 +56,8 @@
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
#include <string.h> #include <string.h>
#include <assert.h>
#include <sys/param.h>
/* cligen */ /* cligen */
#include <cligen/cligen.h> #include <cligen/cligen.h>
@ -50,18 +65,25 @@
/* clixon */ /* clixon */
#include "clixon_queue.h" #include "clixon_queue.h"
#include "clixon_hash.h" #include "clixon_hash.h"
#include "clixon_string.h"
#include "clixon_handle.h" #include "clixon_handle.h"
#include "clixon_err.h" #include "clixon_err.h"
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_yang.h" #include "clixon_yang.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_xml_io.h" #include "clixon_xml_io.h"
#include "clixon_xml_map.h"
#include "clixon_data.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_yang_module.h" #include "clixon_yang_module.h"
#include "clixon_yang_parse_lib.h"
#include "clixon_plugin.h"
#include "clixon_xml_bind.h"
#include "clixon_netconf_lib.h" #include "clixon_netconf_lib.h"
#include "clixon_yang_schema_mount.h" #include "clixon_yang_schema_mount.h"
/*! Check if y is a RFC 8525 YANG schema mount /*! Check if YANG node is a RFC 8525 YANG schema mount
* *
* Check if: * Check if:
* - y is CONTAINER or LIST, AND * - y is CONTAINER or LIST, AND
@ -87,7 +109,14 @@ yang_schema_mount_point(yang_stmt *y)
goto done; goto done;
} }
keyw = yang_keyword_get(y); keyw = yang_keyword_get(y);
if (keyw != Y_CONTAINER && keyw != Y_LIST) if (keyw != Y_CONTAINER
#ifndef YANG_SCHEMA_MOUNT_ONLY_PRESENCE_CONTAINERS
&& keyw != Y_LIST
#endif
#if 0 /* See this in some standard YANGs but RFC 8528 does not allow it */
&& keyw != Y_ANYDATA
#endif
)
goto fail; goto fail;
if (yang_extension_value(y, "mount-point", YANG_SCHEMA_MOUNT_NAMESPACE, &exist, &value) < 0) if (yang_extension_value(y, "mount-point", YANG_SCHEMA_MOUNT_NAMESPACE, &exist, &value) < 0)
goto done; goto done;
@ -103,6 +132,113 @@ yang_schema_mount_point(yang_stmt *y)
goto done; goto done;
} }
/*! Get yangspec mount-point
*
* @param[in] x XML moint-point node
* @param[out] yspec YANG stmt spec
* @retval 1 x is a mount-point: yspec may be set
* @retval 0 x is not a mount point
* @retval -1 Error
*/
int
xml_yang_mount_get(cxobj *x,
yang_stmt **yspec)
{
int retval = -1;
cvec *cvv = NULL;
cg_var *cv;
yang_stmt *y;
yang_stmt *yu;
char *xpath = NULL; // XXX free it
int ret;
if ((y = xml_spec(x)) == NULL)
goto fail;
if ((ret = yang_schema_mount_point(y)) < 0)
goto done;
if (ret == 0)
goto fail;
// XXX
if ((yu = yang_find(y, Y_UNKNOWN, "yangmnt:mount-point")) == NULL)
goto ok;
if (xml2xpath(x, NULL, &xpath) < 0)
goto done;
if ((cvv = yang_cvec_get(yu)) == NULL)
goto ok;
if ((cv = cvec_find(cvv, xpath)) == NULL)
goto ok;
if (yspec)
*yspec = cv_void_get(cv);
ok:
retval = 1;
done:
if (xpath)
free(xpath);
return retval;
fail:
retval = 0;
goto done;
}
/*! Set yangspec mount-point
*
* Stored in a separate structure (not in XML config tree)
* @param[in] x XML moint-point node
* @param[in] yspec Yangspec for this mount-point (consumed)
* @retval 0 OK
* @retval -1 Error
*/
int
xml_yang_mount_set(cxobj *x,
yang_stmt *yspec)
{
cg_var *cv;
yang_stmt *y;
yang_stmt *yu;
yang_stmt *yspec0;
char *xpath = NULL;
cvec *cvv;
if ((y = xml_spec(x)) == NULL ||
(yu = yang_find(y, Y_UNKNOWN, "yangmnt:mount-point")) == NULL){
goto done;
}
if (xml2xpath(x, NULL, &xpath) < 0)
goto done;
if ((cvv = yang_cvec_get(yu)) != NULL &&
(cv = cvec_find(cvv, xpath)) != NULL &&
(yspec0 = cv_void_get(cv)) != NULL){
assert(0);
ys_free(yspec0);
cv_void_set(cv, NULL);
}
else if ((cv = yang_cvec_add(yu, CGV_VOID, xpath)) == NULL)
return -1;
cv_void_set(cv, yspec);
done:
if (xpath)
free(xpath);
return 0;
}
/*! Free all yspec yang-mounts
* @aparm[in] cvv Cligen-variable vector containing xpath -> yspec mapping
*/
int
xml_yang_mount_freeall(cvec *cvv)
{
cg_var *cv = NULL;
yang_stmt *ys;
cv = NULL;
while ((cv = cvec_each(cvv, cv)) != NULL){
if ((ys = cv_void_get(cv)) != NULL)
ys_free(ys);
}
return 0;
}
/*! Find schema mounts - callback function for xml_apply /*! Find schema mounts - callback function for xml_apply
* *
* @param[in] x XML node * @param[in] x XML node
@ -142,7 +278,6 @@ find_schema_mounts(cxobj *x,
* Brute force: traverse whole XML, match all x that have ymount as yspec * Brute force: traverse whole XML, match all x that have ymount as yspec
* Add yang-library state for all x * Add yang-library state for all x
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] yspec Yang spec
* @param[in] xpath XML Xpath * @param[in] xpath XML Xpath
* @param[in] nsc XML Namespace context for xpath * @param[in] nsc XML Namespace context for xpath
* @param[in,out] xret Existing XML tree, merge x into this * @param[in,out] xret Existing XML tree, merge x into this
@ -159,8 +294,7 @@ find_schema_mounts(cxobj *x,
* Alt: see snmp_yang2xml to get instances instead of brute force traverse of whole tree * Alt: see snmp_yang2xml to get instances instead of brute force traverse of whole tree
*/ */
static int static int
schema_mounts_yang_library(clicon_handle h, yang_schema_mount_statedata_yanglib(clicon_handle h,
yang_stmt *yspec,
char *xpath, char *xpath,
cvec *nsc, cvec *nsc,
cxobj **xret, cxobj **xret,
@ -170,9 +304,10 @@ schema_mounts_yang_library(clicon_handle h,
cvec *cvv = NULL; cvec *cvv = NULL;
cg_var *cv; cg_var *cv;
cxobj *xmp; /* xml mount-point */ cxobj *xmp; /* xml mount-point */
cxobj *xylib = NULL; /* xml yang-lib */ cxobj *yanglib = NULL; /* xml yang-lib */
cbuf *cb = NULL; cbuf *cb = NULL;
char *msid = "mount-point"; /* modules-set-id dummy */ yang_stmt *yspec;
int ret;
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, 0, "clicon buffer"); clicon_err(OE_UNIX, 0, "clicon buffer");
@ -187,25 +322,21 @@ schema_mounts_yang_library(clicon_handle h,
cv = NULL; cv = NULL;
while ((cv = cvec_each(cvv, cv)) != NULL) { while ((cv = cvec_each(cvv, cv)) != NULL) {
xmp = cv_void_get(cv); xmp = cv_void_get(cv);
/* addsub here */ yanglib = NULL;
cbuf_reset(cb); /* User callback */
// XXX change yspec to mount-point if (clixon_plugin_yang_mount_all(h, xmp, &yanglib) < 0)
/* Build a cb string: <modules-state>... */
if (yang_modules_state_build(h, yspec, msid, 0, cb) < 0)
goto done; goto done;
/* Parse cb, x is on the form: <top><modules-state>... if (yanglib == NULL)
* Note, list is not sorted since it is state (should not be) continue;
*/ yspec = clicon_dbspec_yang(h);
if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, &xylib, NULL) < 0){ // if ((ret = xml_bind_yang(h, yanglib, YB_NONE, yspec, &xerr)) < 0)
if (xret && netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0) if ((ret = xml_bind_yang0(h, yanglib, YB_MODULE, yspec, xerr)) < 0)
goto done; goto done;
if (ret == 0)
goto fail; goto fail;
} if (xml_addsub(xmp, yanglib) < 0)
if (xml_rootchild(xylib, 0, &xylib) < 0)
goto done; goto done;
if (xml_addsub(xmp, xylib) < 0) yanglib = NULL;
goto done;
xylib = NULL;
} }
retval = 1; retval = 1;
done: done:
@ -219,7 +350,7 @@ schema_mounts_yang_library(clicon_handle h,
goto done; goto done;
} }
/*! Get modules state according to RFC 8528 /*! Get schema mount-point state according to RFC 8528
* *
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] yspec Yang spec * @param[in] yspec Yang spec
@ -233,7 +364,7 @@ schema_mounts_yang_library(clicon_handle h,
* @note Only "inline" specification of mounted schema supported, not "shared schema" * @note Only "inline" specification of mounted schema supported, not "shared schema"
*/ */
int int
schema_mounts_state_get(clicon_handle h, yang_schema_mount_statedata(clicon_handle h,
yang_stmt *yspec, yang_stmt *yspec,
char *xpath, char *xpath,
cvec *nsc, cvec *nsc,
@ -291,7 +422,7 @@ schema_mounts_state_get(clicon_handle h,
goto fail; goto fail;
} }
/* Find mount-points and return yang-library state */ /* Find mount-points and return yang-library state */
if (0 && schema_mounts_yang_library(h, yspec, xpath, nsc, xret, xerr) < 0) if (yang_schema_mount_statedata_yanglib(h, xpath, nsc, xret, xerr) < 0)
goto done; goto done;
ok: ok:
retval = 1; retval = 1;
@ -306,41 +437,85 @@ schema_mounts_state_get(clicon_handle h,
goto done; goto done;
} }
/*! Callback for yang schema mount-point extension /*! Get yanglib, parse it and mount it
*
* @param[in] h Clixon handle
* @param[in] yext Yang node of extension
* @param[in] ys Yang node of (unknown) statement belonging to extension
* @retval 0 OK
* @retval -1 Error
// XXX his may not even be necessary
*/ */
int int
yang_schema_unknown(clicon_handle h, yang_schema_yanglib_parse_mount(clicon_handle h,
yang_stmt *yext, cxobj *xt)
yang_stmt *ys)
{ {
int retval = -1; int retval = -1;
char *extname; cxobj *yanglib = NULL;
char *modname; yang_stmt *yspec = NULL;
yang_stmt *ymod; int ret;
cg_var *cv;
char *label;
ymod = ys_module(yext); if (clixon_plugin_yang_mount_all(h, xt, &yanglib) < 0)
modname = yang_argument_get(ymod); goto done;
extname = yang_argument_get(yext); if (yanglib == NULL)
if (strcmp(modname, "ietf-yang-schema-mount") != 0 || strcmp(extname, "mount-point") != 0) goto anydata;
goto ok; /* Parse it and set mount-point */
if ((cv = yang_cv_get(ys)) == NULL){ if ((yspec = yspec_new()) == NULL)
clicon_err(OE_YANG, 0, "mount-point extension must have label"); goto done;
if ((ret = yang_lib2yspec(h, yanglib, yspec)) < 0)
goto done;
if (ret == 0)
goto anydata;
#ifdef YANG_SCHEMA_MOUNT_YANG_LIB_FORCE
/* XXX: Ensure yang-lib is always there otherwise get state dont work for mountpoint */
if (yang_spec_parse_module(h, "ietf-yang-library", "2019-01-04", yspec) < 0)
goto done;
#endif
if (xml_yang_mount_set(xt, yspec) < 0)
goto done;
retval = 1;
done:
if (yspec)
ys_free(yspec);
if (yanglib)
xml_free(yanglib);
return retval;
anydata: // Treat as anydata
retval = 0;
goto done; goto done;
} }
label = cv_string_get(cv);
clicon_debug(1, "%s Enabled extension:%s:%s label:%s", __FUNCTION__, modname, extname, label); /*! Check if XML nod is mount-point and return matching YANG child
// XXX his may not even be necessary * @param[in] h Clicon handle
ok: * @param[in] x1 XML node
retval = 0; * @param[in] x1c A child of x1
* @param[out] yc YANG child
* @retval 1 OK, yc contains child
* @retval 0 No such child
* @retval -1 Error
* XXX maybe not needed
*/
int
yang_schema_get_child(clicon_handle h,
cxobj *x1,
cxobj *x1c,
yang_stmt **yc)
{
int retval = -1;
yang_stmt *yspec1;
yang_stmt *ymod1 = NULL;
char *x1cname;
int ret;
x1cname = xml_name(x1c);
if ((ret = xml_yang_mount_get(x1, &yspec1)) < 0)
goto done;
if (ret == 1 && yspec1 != NULL){
if (ys_module_by_xml(yspec1, x1c, &ymod1) <0)
goto done;
if (ymod1 != NULL)
*yc = yang_find_datanode(ymod1, x1cname);
else{ /* It is in fact a mountpoint, there is a yang mount, but it is not found */
goto fail;
}
}
retval = 1;
done: done:
return retval; return retval;
fail:
retval = 0;
goto done;
} }

View file

@ -11,7 +11,6 @@ if true; then # enable YANG_SCHEMA_MOUNT
if [ -z "${CLIXON_YANG_PATCH}" -a "$s" = $0 ]; then exit 0; else return 0; fi if [ -z "${CLIXON_YANG_PATCH}" -a "$s" = $0 ]; then exit 0; else return 0; fi
fi fi
APPNAME=example APPNAME=example
cfg=$dir/conf_mount.xml cfg=$dir/conf_mount.xml
@ -33,6 +32,7 @@ cat <<EOF > $cfg
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR> <CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_NETCONF_MONITORING>true</CLICON_NETCONF_MONITORING> <CLICON_NETCONF_MONITORING>true</CLICON_NETCONF_MONITORING>
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML> <CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
<CLICON_STREAM_DISCOVERY_RFC5277>true</CLICON_STREAM_DISCOVERY_RFC5277>
</clixon-config> </clixon-config>
EOF EOF
@ -45,17 +45,20 @@ module clixon-example{
prefix yangmnt; prefix yangmnt;
} }
container top{ container top{
list root{ list mylist{
key name; key name;
leaf name{ leaf name{
type string; type string;
} }
container root{
presence "Otherwise root is not visible";
yangmnt:mount-point "myroot"{ yangmnt:mount-point "myroot"{
description "Root for other yang models"; description "Root for other yang models";
} }
} }
} }
} }
}
EOF EOF
new "test params: -f $cfg" new "test params: -f $cfg"
@ -73,19 +76,19 @@ fi
new "wait backend" new "wait backend"
wait_backend wait_backend
new "Add two mountpoints: x and y"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><top xmlns=\"urn:example:clixon\"><root><name>x</name></root><root><name>y</name></root></top></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Add two mountpoints: x and y"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><top xmlns=\"urn:example:clixon\"><mylist><name>x</name><root/></mylist><mylist><name>y</name><root/></mylist></top></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "netconf commit" new "netconf commit"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><commit/></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><commit/></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Retrieve schema-mounts with <get> Operation" new "Retrieve schema-mounts with <get> Operation"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"subtree\"><schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\"></schema-mounts></filter></get></rpc>" "<rpc-reply $DEFAULTNS><data><schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\"><mount-point><module>clixon-example</module><label>myroot</label><config>true</config><inline/></mount-point></schema-mounts></data></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"subtree\"><schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\"></schema-mounts></filter></get></rpc>" "<rpc-reply $DEFAULTNS><data><schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\"><mount-point><module>clixon-example</module><label>myroot</label><config>true</config><inline/></mount-point></schema-mounts></data></rpc-reply>"
if true; then
new "get yang-lib at mountpoint" new "get yang-lib at mountpoint"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"subtree\"><top xmlns=\"urn:example:clixon\"><root></root></top>></filter></get></rpc>" "<rpc-reply $DEFAULTNS><data><top xmlns=\"urn:example:clixon\"><root><name>x</name></root><root><name>y</name></root></top></data></rpc-reply>" # XXX maybe too many yangs here, difficult to maintain
fi expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"subtree\"><top xmlns=\"urn:example:clixon\"><mylist/></top>></filter></get></rpc>" "<rpc-reply $DEFAULTNS><data><top xmlns=\"urn:example:clixon\"><mylist><name>x</name><root>
<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"><module-set><name>default</name><module><name>clixon-autocli</name><revision>2022-02-11</revision><namespace>http://clicon.org/autocli</namespace></module><module><name>clixon-example</name><revision>2022-11-01</revision><namespace>urn:example:clixon</namespace></module><module><name>ietf-datastores</name><revision>2018-02-14</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace></module><module><name>ietf-inet-types</name><revision>2021-02-22</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace></module><module><name>ietf-yang-library</name><revision>2019-01-04</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace></module><module><name>ietf-yang-types</name><revision>2013-07-15</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace></module></module-set><content-id>mount-point</content-id></yang-library></root></mylist><mylist><name>y</name><root><yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"><module-set><name>default</name><module><name>clixon-autocli</name><revision>2022-02-11</revision><namespace>http://clicon.org/autocli</namespace></module><module><name>clixon-example</name><revision>2022-11-01</revision><namespace>urn:example:clixon</namespace></module><module><name>ietf-datastores</name><revision>2018-02-14</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace></module><module><name>ietf-inet-types</name><revision>2021-02-22</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace></module><module><name>ietf-yang-library</name><revision>2019-01-04</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace></module><module><name>ietf-yang-types</name><revision>2013-07-15</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace></module></module-set><content-id>mount-point</content-id></yang-library></root></mylist></top></data></rpc-reply>"
if [ $BE -ne 0 ]; then if [ $BE -ne 0 ]; then
new "Kill backend" new "Kill backend"
@ -98,7 +101,6 @@ if [ $BE -ne 0 ]; then
stop_backend -f $cfg stop_backend -f $cfg
fi fi
rm -rf $dir rm -rf $dir
new "endtest" new "endtest"