Very large commit for upcoming 4.4 release

Major New features

* New and updated search functions using xpath, api-path and instance-id
  * New search functions using api-path and instance_id:
    * C search functions: `clixon_find_instance_id()` and `clixon_find_api_path()`
  * Binary search optimization in lists for indexed leafs in all three formats.
    * This improves search performance to O(logN) which is drastical improvements for large lists.
  * You can also register explicit indexes for making binary search (not only list keys)
  * For more info, see docs at [paths](https://clixon-docs.readthedocs.io/en/latest/paths.html) and
[search](https://clixon-docs.readthedocs.io/en/latest/xml.html#searching-in-xml)

API changes on existing features (you may need to change your code)
* On failed validation of leafrefs, error message changed from: `No such leaf` to `No leaf <name> matching path <path>`.
* CLI Error message (clicon_rpc_generate_error()) changed when backend returns netconf error to be more descriptive:
  * Original: `Config error: Validate failed. Edit and try again or discard changes: Invalid argument`
  * New (example): `Netconf error: application operation-failed Identityref validation failed, undefined not derived from acl-base . Validate failed. Edit and try again or discard changes"

Minor changes

* Test framework
  * Added `-- -S <file>` command-line to main example to be able to return any state to main example.
  * Added `test/cicd` test scripts for running on a set of other hosts
* C-code restructuring
  * clixon_yang.c partitioned and moved code into clixon_yang_parse_lib.c and clixon_yang_module.c and move back some code from clixon_yang_type.c.
    * partly to reduce size, but most important to limit code that accesses internal yang structures, only clixon_yang.c does this now.
This commit is contained in:
Olof hagsand 2020-02-02 15:52:30 +01:00
parent e8ae628d06
commit 19e21be0bc
132 changed files with 6241 additions and 2332 deletions

View file

@ -1,5 +1,46 @@
# Clixon Changelog
## 4.4.0 (Expected: February 2020)
### Major New features
* New and updated search functions using xpath, api-path and instance-id
* New search functions using api-path and instance_id:
* C search functions: `clixon_find_instance_id()` and `clixon_find_api_path()`
* Binary search optimization in lists for indexed leafs in all three formats.
* This improves search performance to O(logN) which is drastical improvements for large lists.
* You can also register explicit indexes for making binary search (not only list keys)
* For more info, see docs at [paths](https://clixon-docs.readthedocs.io/en/latest/paths.html) and
[search](https://clixon-docs.readthedocs.io/en/latest/xml.html#searching-in-xml)
### API changes on existing features (you may need to change your code)
* On failed validation of leafrefs, error message changed from: `No such leaf` to `No leaf <name> matching path <path>`.
* CLI Error message (clicon_rpc_generate_error()) changed when backend returns netconf error to be more descriptive:
* Original: `Config error: Validate failed. Edit and try again or discard changes: Invalid argument`
* New (example): `Netconf error: application operation-failed Identityref validation failed, undefined not derived from acl-base . Validate failed. Edit and try again or discard changes"
### Minor changes
* Test framework
* Added `-- -S <file>` command-line to main example to be able to return any state to main example.
* Added `test/cicd` test scripts for running on a set of other hosts
* C-code restructuring
* clixon_yang.c partitioned and moved code into clixon_yang_parse_lib.c and clixon_yang_module.c and move back some code from clixon_yang_type.c.
* partly to reduce size, but most important to limit code that accesses internal yang structures, only clixon_yang.c does this now.
### Corrected Bugs
## 4.3.1 (2 February 2020)
Patch release based on testing by Dave Cornejo, Netgate
### Corrected Bugs
* Compile option `VALIDATE_STATE_XML` introduced in `include/custom.h` to control whether code for state data validation is compiled or not.
* Fixed: Validation of user state data led to wrong validation, if state relied on config data, eg leafref/must/when etc.
* Fixed: No revision in yang module led to errors in validation of state data
* Fixed: Leafref validation did not cover case of when the "path" statement is declared within a typedef, only if it was declared in the data part directly under leaf.
* Fixed: Yang `must` xpath statements containing prefixes stopped working due to namespace context updates
## 4.3.0 (1 January 2020)
There were several issues with multiple namespaces with augmented yangs in 4.2 that have been fixed in 4.3. Some other highlights include: several issues with XPaths including "canonical namespace context" support, a reorganization of the YANG files shipped with the release, and a wildchar in the CLICON_MODE variable.
@ -12,6 +53,7 @@ There were several issues with multiple namespaces with augmented yangs in 4.2 t
* Optional yang files can be installed in a separate dir with `--with-opt-yang-installdir=DIR` (renamed from `with-std-yang-installdir`)
* C-API
* Changed `clicon_rpc_generate_error(msg, xerr)` to `clicon_rpc_generate_error(xerr, msg, arg)`
* If you pass NULL as arg it produces the same message as before.
* Added namespace-context parameter `nsc` to `xpath_first` and `xpath_vec`, (`xpath_vec_nsc` and
xpath_first_nsc` are removed).
* Added clicon_handle as parameter to all `clicon_connect_` functions to get better error message

View file

@ -1,4 +1,6 @@
Copyright 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
CLIXON is dual license.

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -252,6 +253,7 @@ client_get_streams(clicon_handle h,
* @param[in] h Clicon handle
* @param[in] xpath Xpath selection, not used but may be to filter early
* @param[in] nsc XML Namespace context for xpath
* @param[in] content config/state or both
* @param[in,out] xret Existing XML tree, merge x into this
* @retval -1 Error (fatal)
* @retval 0 Statedata callback failed (clicon_err called)
@ -261,17 +263,15 @@ static int
client_statedata(clicon_handle h,
char *xpath,
cvec *nsc,
netconf_content content,
cxobj **xret)
{
int retval = -1;
cxobj **xvec = NULL;
size_t xlen;
int i;
yang_stmt *yspec;
yang_stmt *ymod;
int ret;
char *namespace;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
@ -320,32 +320,9 @@ client_statedata(clicon_handle h,
goto done;
if (ret == 0)
goto fail;
/* Code complex to filter out anything that is outside of xpath
* Actually this is a safety catch, should really be done in plugins
* and modules_state functions.
*/
if (xpath_vec(*xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* If vectors are specified then mark the nodes found and
* then filter out everything else,
* otherwise return complete tree.
*/
if (xvec != NULL){
for (i=0; i<xlen; i++)
xml_flag_set(xvec[i], XML_FLAG_MARK);
}
/* Remove everything that is not marked */
if (!xml_flag(*xret, XML_FLAG_MARK))
if (xml_tree_prune_flagged_sub(*xret, XML_FLAG_MARK, 1, NULL) < 0)
goto done;
/* reset flag */
if (xml_apply(*xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
retval = 1; /* OK */
done:
clicon_debug(1, "%s %d", __FUNCTION__, retval);
if (xvec)
free(xvec);
return retval;
fail:
retval = 0;
@ -354,73 +331,35 @@ client_statedata(clicon_handle h,
/*! Retrieve all or part of a specified configuration.
*
* @param[in] h Clicon handle
* @param[in] xe Request: <rpc><xn></rpc>
* Function reused from both from_client_get() and from_client_get_config
* @param[in] yspec
* @param[in] db
* @param[in] xpath
* @param[in] username
* @param[in] content
* @param[in] depth
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
* @param[in] arg client-entry
* @param[in] regarg User argument given at rpc_callback_register()
* @retval 0 OK
* @retval -1 Error
* @see from_client_get
*/
static int
from_client_get_config(clicon_handle h,
cxobj *xe,
cbuf *cbret,
void *arg,
void *regarg)
client_config_only(clicon_handle h,
cvec *nsc,
yang_stmt *yspec,
char *db,
char *xpath,
char *username,
int32_t depth,
cbuf *cbret)
{
int retval = -1;
char *db;
cxobj *xfilter;
char *xpath = NULL;
cxobj *xret = NULL;
cbuf *cbx = NULL; /* Assist cbuf */
cxobj *xnacm = NULL;
cxobj **xvec = NULL;
size_t xlen;
int ret;
char *username;
cvec *nsc = NULL; /* Create a netconf namespace context from filter */
yang_stmt *yspec;
username = clicon_username_get(h);
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec9");
goto done;
}
if ((db = netconf_db_find(xe, "source")) == NULL){
clicon_err(OE_XML, 0, "db not found");
goto done;
}
if (xmldb_validate_db(db) < 0){
if ((cbx = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
cprintf(cbx, "No such database: %s", db);
if (netconf_invalid_value(cbret, "protocol", cbuf_get(cbx))< 0)
goto done;
goto ok;
}
if ((xfilter = xml_find(xe, "filter")) != NULL){
char *xpath0;
cvec *nsc1 = NULL;
if ((xpath0 = xml_find_value(xfilter, "select"))==NULL)
xpath0="/";
/* Create namespace context for xpath from <filter>
* The set of namespace declarations are those in scope on the
* <filter> element.
*/
else
if (xml_nsctx_node(xfilter, &nsc) < 0)
goto done;
if (xpath2canonical(xpath0, nsc, yspec, &xpath, &nsc1) < 0)
goto done;
if (nsc)
xml_nsctx_free(nsc);
nsc = nsc1;
}
/* Note xret can be pruned by nacm below (and change name),
* so zero-copy cant be used
* Also, must use external namespace context here due to <filter stmt
@ -446,25 +385,114 @@ from_client_get_config(clicon_handle h,
else{
if (xml_name_set(xret, "data") < 0)
goto done;
if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
if (clicon_xml2cbuf(cbret, xret, 0, 0, depth>0?depth+1:depth) < 0)
goto done;
}
cprintf(cbret, "</rpc-reply>");
ok:
retval = 0;
done:
if (xpath)
free(xpath);
if (xnacm)
xml_free(xnacm);
if (xvec)
free(xvec);
if (xnacm)
xml_free(xnacm);
if (xret)
xml_free(xret);
return retval;
}
/*! Retrieve all or part of a specified configuration.
*
* @param[in] h Clicon handle
* @param[in] xe Request: <rpc><xn></rpc>
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
* @param[in] arg client-entry
* @param[in] regarg User argument given at rpc_callback_register()
* @retval 0 OK
* @retval -1 Error
* @see from_client_get
*/
static int
from_client_get_config(clicon_handle h,
cxobj *xe,
cbuf *cbret,
void *arg,
void *regarg)
{
int retval = -1;
char *db;
cxobj *xfilter;
char *xpath = NULL;
cbuf *cbx = NULL; /* Assist cbuf */
int ret;
char *username;
cvec *nsc = NULL; /* Create a netconf namespace context from filter */
yang_stmt *yspec;
int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
char *attr;
char *xpath0;
cvec *nsc1 = NULL;
username = clicon_username_get(h);
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec9");
goto done;
}
if ((db = netconf_db_find(xe, "source")) == NULL){
clicon_err(OE_XML, 0, "db not found");
goto done;
}
if (xmldb_validate_db(db) < 0){
if ((cbx = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
cprintf(cbx, "No such database: %s", db);
if (netconf_invalid_value(cbret, "protocol", cbuf_get(cbx))< 0)
goto done;
goto ok;
}
if ((xfilter = xml_find(xe, "filter")) != NULL){
if ((xpath0 = xml_find_value(xfilter, "select"))==NULL)
xpath0="/";
/* Create namespace context for xpath from <filter>
* The set of namespace declarations are those in scope on the
* <filter> element.
*/
else
if (xml_nsctx_node(xfilter, &nsc) < 0)
goto done;
if (xpath2canonical(xpath0, nsc, yspec, &xpath, &nsc1) < 0)
goto done;
if (nsc)
xml_nsctx_free(nsc);
nsc = nsc1;
}
/* Clixon extensions: depth */
if ((attr = xml_find_value(xe, "depth")) != NULL){
char *reason = NULL;
if ((ret = parse_int32(attr, &depth, &reason)) < 0){
clicon_err(OE_XML, errno, "parse_int32");
goto done;
}
if (ret == 0){
if (netconf_bad_attribute(cbret, "application",
"<bad-attribute>depth</bad-attribute>", "Unrecognized value of depth attribute") < 0)
goto done;
goto ok;
}
}
if ((ret = client_config_only(h, nsc, yspec, db, xpath, username, -1, cbret)) < 0)
goto done;
ok:
retval = 0;
done:
if (xpath)
free(xpath);
if (nsc)
xml_nsctx_free(nsc);
if (cbx)
cbuf_free(cbx);
if (xret)
xml_free(xret);
return retval;
}
@ -493,7 +521,6 @@ from_client_edit_config(clicon_handle h,
cxobj *xc;
cxobj *x;
enum operation_type operation = OP_MERGE;
int non_config = 0;
yang_stmt *yspec;
cbuf *cbx = NULL; /* Assist cbuf */
@ -885,20 +912,26 @@ from_client_get(clicon_handle h,
void *arg,
void *regarg)
{
int retval = -1;
cxobj *xfilter;
char *xpath = NULL;
cxobj *xret = NULL;
int ret;
cxobj **xvec = NULL;
size_t xlen;
cxobj *xnacm = NULL;
char *username;
cvec *nsc = NULL; /* Create a netconf namespace context from filter */
char *attr;
int retval = -1;
cxobj *xfilter;
char *xpath = NULL;
cxobj *xret = NULL;
int ret;
cxobj **xvec = NULL;
size_t xlen;
cxobj *xnacm = NULL;
char *username;
cvec *nsc = NULL; /* Create a netconf namespace context from filter */
char *attr;
netconf_content content = CONTENT_ALL;
int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
yang_stmt *yspec;
int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
yang_stmt *yspec;
int i;
#ifdef VALIDATE_STATE_XML
cxobj *xerr = NULL;
cxobj *xr;
cxobj *xb;
#endif
username = clicon_username_get(h);
if ((yspec = clicon_dbspec_yang(h)) == NULL){
@ -923,10 +956,10 @@ from_client_get(clicon_handle h,
xml_nsctx_free(nsc);
nsc = nsc1;
}
/* Clixon extensions: depth and content */
/* Clixon extensions: content */
if ((attr = xml_find_value(xe, "content")) != NULL)
content = netconf_content_str2int(attr);
/* Clixon extensions: depth */
if ((attr = xml_find_value(xe, "depth")) != NULL){
char *reason = NULL;
if ((ret = parse_int32(attr, &depth, &reason)) < 0){
@ -940,29 +973,105 @@ from_client_get(clicon_handle h,
goto ok;
}
}
if (content != CONTENT_NONCONFIG){
/* Get config
* Note xret can be pruned by nacm below and change name and
* metrged with state data, so zero-copy cant be used
* Also, must use external namespace context here due to <filter stmt
*/
if (xmldb_get0(h, "running", nsc, xpath, 1, &xret, NULL) < 0) {
if (netconf_operation_failed(cbret, "application", "read registry")< 0)
goto done;
goto ok;
}
}
if (content != CONTENT_CONFIG){
/* Get state data from plugins as defined by plugin_statedata(), if any */
clicon_err_reset();
if ((ret = client_statedata(h, xpath, nsc, &xret)) < 0)
if (content == CONTENT_CONFIG){ /* config only, no state */
if (client_config_only(h, nsc, yspec, "running", xpath, username, depth, cbret) < 0)
goto done;
if (ret == 0){ /* Error from callback (error in xret) */
if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
goto done;
goto ok;
}
goto ok;
}
/* If not only-state, then read running config
* Note xret can be pruned by nacm below and change name and
* metrged with state data, so zero-copy cant be used
* Also, must use external namespace context here due to <filter stmt
*/
if (xmldb_get0(h, "running", nsc, NULL, 1, &xret, NULL) < 0) {
if (netconf_operation_failed(cbret, "application", "read registry")< 0)
goto done;
goto ok;
}
/* If not only config,
* get state data from plugins as defined by plugin_statedata(), if any
*/
clicon_err_reset();
if ((ret = client_statedata(h, xpath, nsc, content, &xret)) < 0)
goto done;
if (ret == 0){ /* Error from callback (error in xret) */
if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
goto done;
goto ok;
}
#ifdef VALIDATE_STATE_XML
/* Check XML by validating it. return internal error with error cause
* Primarily intended for user-supplied state-data.
* The whole config tree must be present in case the state data references config data
*/
if ((ret = xml_yang_validate_all_top(h, xret, &xerr)) < 0)
goto done;
if (ret > 0 && (ret = xml_yang_validate_add(h, xret, &xerr)) < 0)
goto done;
if (ret == 0){
#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, xret, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s FAIL: %s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
}
#endif
if ((xr = xpath_first(xerr, NULL, "//error-tag")) != NULL &&
(xb = xml_body_get(xr))){
if (xml_value_set(xb, "operation-failed") < 0)
goto done;
}
if ((xr = xpath_first(xerr, NULL, "//error-message")) != NULL &&
(xb = xml_body_get(xr))){
if (xml_value_append(xb, " Internal error, state callback returned invalid XML") < 0)
goto done;
}
if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0)
goto done;
goto ok;
}
#endif /* VALIDATE_STATE_XML */
if (content == CONTENT_NONCONFIG){ /* state only, all config should be removed now */
/* Keep state data only, remove everything that is not config. Note that state data
* may be a sub-part in a config tree, we need to traverse to find all
*/
if (xml_apply(xret, CX_ELMNT, xml_non_config_data, NULL) < 0)
goto done;
if (xml_tree_prune_flagged_sub(xret, XML_FLAG_MARK, 1, NULL) < 0)
goto done;
if (xml_apply(xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
}
/* Code complex to filter out anything that is outside of xpath
* Actually this is a safety catch, should really be done in plugins
* and modules_state functions.
*/
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* If vectors are specified then mark the nodes found and
* then filter out everything else,
* otherwise return complete tree.
*/
if (xvec != NULL){
for (i=0; i<xlen; i++)
xml_flag_set(xvec[i], XML_FLAG_MARK);
}
if (xvec){
free(xvec);
xvec = NULL;
}
/* Remove everything that is not marked */
if (!xml_flag(xret, XML_FLAG_MARK))
if (xml_tree_prune_flagged_sub(xret, XML_FLAG_MARK, 1, NULL) < 0)
goto done;
/* reset flag */
if (xml_apply(xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
/* Pre-NACM access step */
if ((ret = nacm_access_pre(h, username, NACM_DATA, &xnacm)) < 0)
goto done;
@ -988,6 +1097,10 @@ from_client_get(clicon_handle h,
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
#ifdef VALIDATE_STATE_XML
if (xerr)
xml_free(xerr);
#endif
if (xpath)
free(xpath);
if (xnacm)

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -125,6 +126,7 @@ backend_terminate(clicon_handle h)
rpc_callback_delete_all(h);
/* Delete all backend plugin upgrade callbacks */
upgrade_callback_delete_all(h);
xpath_optimize_exit();
if (pidfile)
unlink(pidfile);
@ -463,6 +465,7 @@ main(int argc,
/* Initiate CLICON handle */
if ((h = backend_handle_init()) == NULL)
return -1;
foreground = 0;
once = 0;
zap = 0;

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -111,7 +112,6 @@ clixon_plugin_statedata(clicon_handle h,
{
int retval = -1;
int ret;
cxobj *xerr = NULL;
cxobj *x = NULL;
clixon_plugin *cp = NULL;
plgstatedata_t *fn; /* Plugin statedata fn */
@ -124,46 +124,17 @@ clixon_plugin_statedata(clicon_handle h,
goto done;
if (fn(h, nsc, xpath, x) < 0)
goto fail; /* Dont quit here on user callbacks */
if (xml_apply(x, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
/* Check XML from state callback by validating it. return internal
* error with error cause
*/
if ((ret = xml_yang_validate_all_top(h, x, &xerr)) < 0)
goto done;
if (ret > 0 && (ret = xml_yang_validate_add(h, x, &xerr)) < 0)
goto done;
if (ret == 0){
cxobj *xe;
cxobj *xb;
if ((xe = xpath_first(xerr, NULL, "//error-tag")) != NULL &&
(xb = xml_body_get(xe))){
if (xml_value_set(xb, "operation-failed") < 0)
goto done;
}
if ((xe = xpath_first(xerr, NULL, "//error-message")) != NULL &&
(xb = xml_body_get(xe))){
if (xml_value_append(xb, " Internal error, state callback returned invalid XML") < 0)
goto done;
}
if (*xret){
xml_free(*xret);
*xret = NULL;
}
*xret = xerr;
xerr = NULL;
goto fail;
}
#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, x, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s MERGE: %s", __FUNCTION__, cbuf_get(ccc));
clicon_debug(1, "%s STATE: %s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
}
#endif
if (xml_apply(x, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
if ((ret = netconf_trymerge(x, yspec, xret)) < 0)
goto done;
if (ret == 0)
@ -172,15 +143,13 @@ clixon_plugin_statedata(clicon_handle h,
xml_free(x);
x = NULL;
}
}
} /* while plugin */
retval = 1;
done:
if (cberr)
cbuf_free(cberr);
if (x)
xml_free(x);
if (xerr)
xml_free(xerr);
return retval;
fail:
retval = 0;

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -174,6 +175,7 @@ cli_terminate(clicon_handle h)
cvec_free(nsctx);
if ((x = clicon_conf_xml(h)) != NULL)
xml_free(x);
xpath_optimize_exit();
cli_plugin_finish(h);
cli_history_save(h);
cli_handle_exit(h);

View file

@ -1,7 +1,8 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
# Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
# Copyright (C) 2017-2020 Olof Hagsand
#
# This file is part of CLIXON
#

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -336,6 +337,7 @@ netconf_terminate(clicon_handle h)
cvec_free(nsctx);
if ((x = clicon_conf_xml(h)) != NULL)
xml_free(x);
xpath_optimize_exit();
event_exit();
clicon_handle_exit(h);
clicon_log_exit();

View file

@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
# Copyright (C) 2009-2020 Olof Hagsand
#
# This file is part of CLIXON
#

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -611,6 +611,7 @@ restconf_terminate(clicon_handle h)
cvec_free(nsctx);
if ((x = clicon_conf_xml(h)) != NULL)
xml_free(x);
xpath_optimize_exit();
clicon_handle_exit(h);
clicon_log_exit();
return 0;

7
configure vendored
View file

@ -2172,9 +2172,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
: ${INSTALLFLAGS="-s"}
CLIXON_VERSION_MAJOR="4"
CLIXON_VERSION_MINOR="3"
CLIXON_VERSION_MINOR="4"
CLIXON_VERSION_PATCH="0"
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}.PRE\""
# Check CLIgen
if test "$prefix" = "NONE"; then
@ -4976,7 +4976,7 @@ _ACEOF
ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/main/Makefile example/hello/Makefile extras/rpm/Makefile docker/Makefile docker/main/Makefile docker/base/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/mandatory/Makefile yang/optional/Makefile doc/Makefile test/Makefile"
ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/main/Makefile example/hello/Makefile extras/rpm/Makefile docker/Makefile docker/main/Makefile docker/base/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/mandatory/Makefile yang/optional/Makefile doc/Makefile test/Makefile test/cicd/Makefile"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
@ -5696,6 +5696,7 @@ do
"yang/optional/Makefile") CONFIG_FILES="$CONFIG_FILES yang/optional/Makefile" ;;
"doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
"test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;;
"test/cicd/Makefile") CONFIG_FILES="$CONFIG_FILES test/cicd/Makefile" ;;
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
esac

View file

@ -1,7 +1,9 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
# Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
# Copyright (C) 2017-2019 Olof Hagsand
# Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
#
# This file is part of CLIXON
#
@ -43,9 +45,9 @@ AC_INIT(lib/clixon/clixon.h.in)
: ${INSTALLFLAGS="-s"}
CLIXON_VERSION_MAJOR="4"
CLIXON_VERSION_MINOR="3"
CLIXON_VERSION_MINOR="4"
CLIXON_VERSION_PATCH="0"
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}.PRE\""
# Check CLIgen
if test "$prefix" = "NONE"; then
@ -283,6 +285,7 @@ AC_OUTPUT(Makefile
yang/mandatory/Makefile
yang/optional/Makefile
doc/Makefile
test/Makefile
test/Makefile
test/cicd/Makefile
)

View file

@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
# Copyright (C) 2017-2020 Olof Hagsand
#
# This file is part of CLIXON
#

View file

@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
# Copyright (C) 2017-2020 Olof Hagsand
#
# This file is part of CLIXON
#

View file

@ -1,4 +1,35 @@
#!/bin/bash
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2017-2020 Olof Hagsand
#
# This file is part of CLIXON
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Alternatively, the contents of this file may be used under the terms of
# the GNU General Public License Version 3 or later (the "GPL"),
# in which case the provisions of the GPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of the GPL, and not to allow others to
# use your version of this file under the terms of Apache License version 2,
# indicate your decision by deleting the provisions above and replace them with
# the notice and other provisions required by the GPL. If you do not delete
# 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 *****
# Usage: ./startup.sh
# Debug: DBG=1 ./startup.sh
# See also cleanup.sh

View file

@ -1,4 +1,36 @@
#!/bin/sh
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2017-2020 Olof Hagsand
#
# This file is part of CLIXON
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Alternatively, the contents of this file may be used under the terms of
# the GNU General Public License Version 3 or later (the "GPL"),
# in which case the provisions of the GPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of the GPL, and not to allow others to
# use your version of this file under the terms of Apache License version 2,
# indicate your decision by deleting the provisions above and replace them with
# the notice and other provisions required by the GPL. If you do not delete
# 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 *****
# This script is copied into the container on build time and runs
# _inside_ the container at start in runtime. It gets environment variables
# from the start.sh script.
@ -53,7 +85,7 @@ EOF
# - test_order.sh XXX this is a bug need debugging
cat <<EOF > /usr/local/bin/test/site.sh
# Add your local site specific env variables (or tests) here.
SKIPLIST="test_yangmodels.sh test_openconfig.sh test_install.sh test_privileges.sh"
SKIPLIST="test_api.sh test_yangmodels.sh test_openconfig.sh test_install.sh test_privileges.sh"
#IETFRFC=
EOF

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -47,6 +48,7 @@
#include <signal.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
#include <sys/time.h>
/* clicon */
@ -71,6 +73,11 @@ static int _reset = 0;
*/
static int _state = 0;
/*! File where state XML is read from, if _state is true
* Primarily for testing
*/
static char *_state_file = NULL;
/*! Variable to control upgrade callbacks.
* If set, call test-case for upgrading ietf-interfaces, otherwise call
* auto-upgrade
@ -307,55 +314,67 @@ example_statedata(clicon_handle h,
cvec *nsc1 = NULL;
cvec *nsc2 = NULL;
yang_stmt *yspec = NULL;
int fd;
if (!_state)
goto ok;
yspec = clicon_dbspec_yang(h);
/* Example of statedata, in this case merging state data with
* state information. In this case adding dummy interface operation state
* to configured interfaces.
* Get config according to xpath */
if ((nsc1 = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-interfaces")) == NULL)
goto done;
if (xmldb_get0(h, "running", nsc1, "/interfaces/interface/name", 1, &xt, NULL) < 0)
goto done;
if (xpath_vec(xt, nsc1, "/interfaces/interface/name", &xvec, &xlen) < 0)
goto done;
if (xlen){
cprintf(cb, "<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">");
for (i=0; i<xlen; i++){
name = xml_body(xvec[i]);
cprintf(cb, "<interface xmlns:ex=\"urn:example:clixon\"><name>%s</name><type>ex:eth</type><oper-status>up</oper-status>", name);
cprintf(cb, "<ex:my-status><ex:int>42</ex:int><ex:str>foo</ex:str></ex:my-status>");
cprintf(cb, "</interface>");
/* If -S is set, then read state data from file, otherwise construct it programmatically */
if (_state_file){
if ((fd = open(_state_file, O_RDONLY)) < 0){
clicon_err(OE_UNIX, errno, "open(%s)", _state_file);
goto done;
}
cprintf(cb, "</interfaces>");
if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0)
if (xml_parse_file(fd, NULL, yspec, &xstate) < 0)
goto done;
}
/* State in test_yang.sh , test_restconf.sh and test_order.sh */
if (yang_find_module_by_namespace(yspec, "urn:example:clixon") != NULL){
if (xml_parse_string("<state xmlns=\"urn:example:clixon\">"
"<op>42</op>"
"<op>41</op>"
"<op>43</op>" /* should not be ordered */
"</state>", NULL, &xstate) < 0)
goto done; /* For the case when urn:example:clixon is not loaded */
}
/* Event state from RFC8040 Appendix B.3.1
* Note: (1) order is by-system so is different,
* (2) event-count is XOR on name, so is not 42 and 4
*/
if (yang_find_module_by_namespace(yspec, "urn:example:events") != NULL){
cbuf_reset(cb);
cprintf(cb, "<events xmlns=\"urn:example:events\">");
cprintf(cb, "<event><name>interface-down</name><event-count>90</event-count></event>");
cprintf(cb, "<event><name>interface-up</name><event-count>77</event-count></event>");
cprintf(cb, "</events>");
if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0)
goto done;
}
else {
/* Example of statedata, in this case merging state data with
* state information. In this case adding dummy interface operation state
* to configured interfaces.
* Get config according to xpath */
if ((nsc1 = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-interfaces")) == NULL)
goto done;
if (xmldb_get0(h, "running", nsc1, "/interfaces/interface/name", 1, &xt, NULL) < 0)
goto done;
if (xpath_vec(xt, nsc1, "/interfaces/interface/name", &xvec, &xlen) < 0)
goto done;
if (xlen){
cprintf(cb, "<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">");
for (i=0; i<xlen; i++){
name = xml_body(xvec[i]);
cprintf(cb, "<interface xmlns:ex=\"urn:example:clixon\"><name>%s</name><type>ex:eth</type><oper-status>up</oper-status>", name);
cprintf(cb, "<ex:my-status><ex:int>42</ex:int><ex:str>foo</ex:str></ex:my-status>");
cprintf(cb, "</interface>");
}
cprintf(cb, "</interfaces>");
if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0)
goto done;
}
/* State in test_yang.sh , test_restconf.sh and test_order.sh */
if (yang_find_module_by_namespace(yspec, "urn:example:clixon") != NULL){
if (xml_parse_string("<state xmlns=\"urn:example:clixon\">"
"<op>42</op>"
"<op>41</op>"
"<op>43</op>" /* should not be ordered */
"</state>", NULL, &xstate) < 0)
goto done; /* For the case when urn:example:clixon is not loaded */
}
/* Event state from RFC8040 Appendix B.3.1
* Note: (1) order is by-system so is different,
* (2) event-count is XOR on name, so is not 42 and 4
*/
if (yang_find_module_by_namespace(yspec, "urn:example:events") != NULL){
cbuf_reset(cb);
cprintf(cb, "<events xmlns=\"urn:example:events\">");
cprintf(cb, "<event><name>interface-down</name><event-count>90</event-count></event>");
cprintf(cb, "<event><name>interface-up</name><event-count>77</event-count></event>");
cprintf(cb, "</events>");
if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0)
goto done;
}
}
ok:
retval = 0;
done:
@ -709,7 +728,7 @@ clixon_plugin_init(clicon_handle h)
goto done;
opterr = 0;
optind = 1;
while ((c = getopt(argc, argv, "rsut:")) != -1)
while ((c = getopt(argc, argv, "rsS:ut")) != -1)
switch (c) {
case 'r':
_reset = 1;
@ -717,6 +736,9 @@ clixon_plugin_init(clicon_handle h)
case 's':
_state = 1;
break;
case 'S': /* state file */
_state_file = optarg;
break;
case 'u':
_upgrade = 1;
break;

View file

@ -1,4 +1,36 @@
# Common CLI syntax for both server and PMNode operatio mode
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
# Copyright (C) 2017-2020 Olof Hagsand
#
# This file is part of CLIXON
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Alternatively, the contents of this file may be used under the terms of
# the GNU General Public License Version 3 or later (the "GPL"),
# in which case the provisions of the GPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of the GPL, and not to allow others to
# use your version of this file under the terms of Apache License version 2,
# indicate your decision by deleting the provisions above and replace them with
# the notice and other provisions required by the GPL. If you do not delete
# 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 *****
# Clixon example specification
CLICON_MODE="example";
CLICON_PROMPT="%U@%H> ";
CLICON_PLUGIN="example_cli";
@ -37,8 +69,8 @@ show("Show a particular state of the system"){
xml("Show comparison in xml"), compare_dbs((int32)0);
text("Show comparison in text"), compare_dbs((int32)1);
}
state("Show configuration and state"), cli_show_config_state("running", "text", "/"){
xml("Show configuration and state as XML"), cli_show_config_state("candidate", "xml", "/");{
state("Show configuration and state"), cli_show_config_state("running", "text", "/");{
xml("Show configuration and state as XML"), cli_show_config_state("running", "xml", "/");{
@datamodel, cli_show_auto_state("running", "xml");
}
}

View file

@ -1,7 +1,7 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -63,4 +63,18 @@
* Identify xpaths that search for exactly a list key, eg: "y[k=3]" and then call
* binary search. This only works if "y" has proper yang binding and is sorted by system
*/
#undef XPATH_LIST_OPTIMIZE
#define XPATH_LIST_OPTIMIZE
/*! Add search indexes, so that binary search can be made for non-key list indexes
* This also applies if there are multiple keys and you want to search on only the second for
* example.
*/
#undef XML_EXTRA_INDEX
/*! Validate user state callback content
* Use may register state callbacks using ca_statedata callback
* When this option is set, the XML returned from the callback is validated after merging with the running
* db. If it fails, an internal error is returned to the originating user.
* If the option is not set, the XML returned by the user is not validated.
*/
#define VALIDATE_STATE_XML

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -73,6 +74,7 @@
#include <clixon/clixon_file.h>
#include <clixon/clixon_xml.h>
#include <clixon/clixon_xml_sort.h>
#include <clixon/clixon_yang_parse_lib.h>
#include <clixon/clixon_yang_module.h>
#include <clixon/clixon_stream.h>
#include <clixon/clixon_proto.h>
@ -82,7 +84,7 @@
#include <clixon/clixon_options.h>
#include <clixon/clixon_data.h>
#include <clixon/clixon_regex.h>
#include <clixon/clixon_api_path.h>
#include <clixon/clixon_path.h>
#include <clixon/clixon_xml_map.h>
#include <clixon/clixon_validate.h>
#include <clixon/clixon_datastore.h>

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -58,9 +59,10 @@
enum clicon_err{
/* 0 means error not set) */
OE_DB = 1, /* database registries */
OE_DAEMON, /* demons: pidfiles, etc */
OE_DAEMON, /* demons: pidfiles, etc */
OE_EVENTS, /* events, filedescriptors, timeouts */
OE_CFG, /* configuration */
OE_NETCONF, /* Netconf error */
OE_PROTO, /* config/client communication */
OE_REGEX, /* Regexp error */
OE_UNIX, /* unix/linux syscall error */

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -31,6 +32,8 @@
***** END LICENSE BLOCK *****
* "Instance-identifier" is a subset of XML Xpaths and defined in Yang, used in NACM for example.
* and defined in RF7950 Sections 9.13 and 14.
*
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
* BNF:
@ -44,8 +47,29 @@
* <identifier> := (<ALPHA> | "_") (<ALPHA> | <DIGIT> | "_" | "-" | ".")
*/
#ifndef _CLIXON_API_PATH_H_
#define _CLIXON_API_PATH_H_
#ifndef _CLIXON_PATH_H_
#define _CLIXON_PATH_H_
/*
* Types
*/
/* Internal path structure. Somewhat more general than api-path, much less than xpath
* about the same as yang instance-identifier
* Not that cp_cvk api-paths do not specifiy key-names, so cp_cvk is just a list of
* (NULL:value)*, which means that names must be added using api_path_check() based on
* yang.
* Other formats (eg xpath) have the names given in the format.
*/
typedef struct {
qelem_t cp_qelem; /* List header */
char *cp_prefix; /* Prefix or module name, should be resolved + id to cp_yang */
char *cp_id; /* Identifier */
cvec *cp_cvk; /* Key values: list of (name:value) pairs alt (NULL:value)
* Can also be single uint32, if so positional eg x/y[42]
* This seems kludgy but follows RFC 7950 Sec 9.13
*/
yang_stmt *cp_yang; /* Corresponding yang spec (after XML match - ie resolved) */
} clixon_path;
/*
* Prototypes
@ -60,5 +84,14 @@ int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop,
yang_class nodeclass, int strict,
cxobj **xpathp, yang_stmt **ypathp, cxobj **xerr);
int xml2api_path_1(cxobj *x, cbuf *cb);
#if defined(__GNUC__) && __GNUC__ >= 3
int clixon_xml_find_api_path(cxobj *xcur, yang_stmt *yt, cxobj ***vec, size_t *veclen, char *format,
...) __attribute__ ((format (printf, 5, 6)));;
int clixon_xml_find_instance_id(cxobj *xcur, yang_stmt *yt, cxobj ***vec, size_t *veclen, char *format,
...) __attribute__ ((format (printf, 5, 6)));;
#else
int clixon_xml_find_api_path(cxobj *xcur, yang_stmt *yt, cxobj ***vec, size_t *veclen, char *format,o ...);
int clixon_xml_find_instance_id(cxobj *xcur, yang_stmt *yt, cxobj ***vec, size_t *veclen, char *format, ...);
#endif
#endif /* _CLIXON_API_PATH_H_ */
#endif /* _CLIXON_PATH_H_ */

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -151,7 +152,6 @@ int xml_spec_set(cxobj *x, yang_stmt *spec);
cg_var *xml_cv(cxobj *x);
int xml_cv_set(cxobj *x, cg_var *cv);
cxobj *xml_find(cxobj *xn_parent, char *name);
int xml_addsub(cxobj *xp, cxobj *xc);
cxobj *xml_wrap_all(cxobj *xp, char *tag);
cxobj *xml_wrap(cxobj *xc, char *tag);
@ -193,6 +193,7 @@ cxobj *xml_dup(cxobj *x0);
int cxvec_dup(cxobj **vec0, size_t len0, cxobj ***vec1, size_t *len1);
int cxvec_append(cxobj *x, cxobj ***vec, size_t *len);
int cxvec_prepend(cxobj *x, cxobj ***vec, size_t *len);
int xml_apply(cxobj *xn, enum cxobj_type type, xml_applyfn_t fn, void *arg);
int xml_apply0(cxobj *xn, enum cxobj_type type, xml_applyfn_t fn, void *arg);
int xml_apply_ancestor(cxobj *xn, xml_applyfn_t fn, void *arg);

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -40,11 +41,13 @@
* Prototypes
*/
int xml_child_spec(cxobj *x, cxobj *xp, yang_stmt *yspec, yang_stmt **yp);
int xml_cmp(cxobj *x1, cxobj *x2, int enm);
int xml_cmp(cxobj *x1, cxobj *x2, int same, int skip1);
int xml_sort(cxobj *x0, void *arg);
int xml_insert(cxobj *xp, cxobj *xc, enum insert_type ins, char *key_val, cvec *nsckey);
int xml_sort_verify(cxobj *x, void *arg);
int match_base_child(cxobj *x0, cxobj *x1c, yang_stmt *yc, cxobj **x0cp);
int xml_binsearch(cxobj *xp, yang_stmt *yc, cvec *cvk, cxobj **xretp);
int clixon_xml_find_index(cxobj *xp, yang_stmt *yp, char *namespace, char *name,
cvec *cvk, cxobj ***xvec, size_t *xlen);
int clixon_xml_find_pos(cxobj *xp, yang_stmt *yc, uint32_t pos, cxobj ***xvec, size_t *xlen);
#endif /* _CLIXON_XML_SORT_H */

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -55,8 +56,9 @@ enum xp_objtype{
XT_STRING
};
/* Expression evaluation occurs with respect to a context. XSLT and XPointer specify how the context is
* determined for XPath expressions used in XSLT and XPointer respectively. The context consists of:
/* Expression evaluation occurs with respect to a context. XSLT and XPointer specify how the
* context is determined for XPath expressions used in XSLT and XPointer respectively. The
* context consists of:
* a node (the context node)
* a pair of non-zero positive integers (the context position and the context size)
* a set of variable bindings

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -39,7 +39,22 @@
#ifndef _CLIXON_YANG_H_
#define _CLIXON_YANG_H_
/*
* Clixon-specific cligen variable (cv) flags
* CLIgen flags defined are in the range 0x01 -0x0f
* An application can use any flags above that
* @see cv_flag
*/
#define V_UNSET 0x10 /* Used by XML code to denote a value is not default */
/*
* Yang flags used in
*/
#define YANG_FLAG_MARK 0x01 /* (Dynamic) marker for dynamic algorithms, eg expand */
#ifdef XML_EXTRA_INDEX
#define YANG_FLAG_INDEX 0x02 /* This yang node under list is (extra) index. --> you can access
* list elements using this index with binary search */
#endif
/*
* Types
@ -146,12 +161,20 @@ typedef int (yang_applyfn_t)(yang_stmt *ys, void *arg);
* Prototypes
*/
/* Access functions */
int yang_len_get(yang_stmt *ys);
yang_stmt *yang_child_i(yang_stmt *ys, int i);
yang_stmt *yang_parent_get(yang_stmt *ys);
enum rfc_6020 yang_keyword_get(yang_stmt *ys);
char *yang_argument_get(yang_stmt *ys);
int yang_argument_set(yang_stmt *ys, char *arg);
cg_var *yang_cv_get(yang_stmt *ys);
cvec *yang_cvec_get(yang_stmt *ys);
int yang_cvec_set(yang_stmt *ys, cvec *cvv);
uint16_t yang_flag_get(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);
/* Other functions */
yang_stmt *yspec_new(void);
@ -169,10 +192,6 @@ int ys_module_by_xml(yang_stmt *ysp, struct xml *xt, yang_stmt **ymodp);
yang_stmt *ys_module(yang_stmt *ys);
yang_stmt *ys_real_module(yang_stmt *ys);
yang_stmt *ys_spec(yang_stmt *ys);
yang_stmt *yang_find_module_by_prefix(yang_stmt *ys, char *prefix);
yang_stmt *yang_find_module_by_prefix_yspec(yang_stmt *yspec, char *prefix);
yang_stmt *yang_find_module_by_namespace(yang_stmt *yspec, char *namespace);
yang_stmt *yang_find_module_by_name(yang_stmt *yspec, char *name);
yang_stmt *yang_find(yang_stmt *yn, int keyword, const char *argument);
int yang_match(yang_stmt *yn, int keyword, char *argument);
yang_stmt *yang_find_datanode(yang_stmt *yn, char *argument);
@ -186,8 +205,7 @@ int yang_print(FILE *f, yang_stmt *yn);
int yang_print_cbuf(cbuf *cb, yang_stmt *yn, int marginal);
int if_feature(yang_stmt *yspec, char *module, char *feature);
int ys_populate(yang_stmt *ys, void *arg);
yang_stmt *yang_parse_file(int fd, const char *name, yang_stmt *ysp);
yang_stmt *yang_parse_filename(const char *filename, yang_stmt *ysp);
int ys_populate2(yang_stmt *ys, void *arg);
int yang_apply(yang_stmt *yn, enum rfc_6020 key, yang_applyfn_t fn,
void *arg);
int yang_datanode(yang_stmt *ys);
@ -196,17 +214,16 @@ int yang_abs_schema_nodeid(yang_stmt *yspec, yang_stmt *ys,
enum rfc_6020 keyword, yang_stmt **yres);
int yang_desc_schema_nodeid(yang_stmt *yn, char *schema_nodeid,
enum rfc_6020 keyword, yang_stmt **yres);
int ys_parse_date_arg(char *datearg, uint32_t *dateint);
cg_var *ys_parse(yang_stmt *ys, enum cv_type cvtype);
int ys_parse_sub(yang_stmt *ys, char *extra);
int yang_mandatory(yang_stmt *ys);
int yang_config(yang_stmt *ys);
int yang_spec_parse_module(clicon_handle h, const char *module,
const char *revision, yang_stmt *yspec);
int yang_spec_parse_file(clicon_handle h, char *filename, yang_stmt *yspec);
int yang_spec_load_dir(clicon_handle h, char *dir, yang_stmt *yspec);
int yang_features(clicon_handle h, yang_stmt *yt);
cvec *yang_arg2cvec(yang_stmt *ys, char *delimi);
int yang_key_match(yang_stmt *yn, char *name);
int yang_type_cache_regexp_set(yang_stmt *ytype, int rxmode, cvec *regexps);
int yang_type_cache_get(yang_stmt *ytype, yang_stmt **resolved, int *options,
cvec **cvv, cvec *patterns, int *rxmode, cvec *regexps, uint8_t *fraction);
int yang_type_cache_set(yang_stmt *ys, yang_stmt *resolved, int options, cvec *cvv,
cvec *patterns, uint8_t fraction);
#endif /* _CLIXON_YANG_H_ */

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -66,7 +66,10 @@ char *yang_modules_revision(clicon_handle h);
int yang_modules_state_get(clicon_handle h, yang_stmt *yspec, char *xpath,
cvec *nsc, int brief, cxobj **xret);
int clixon_module_upgrade(clicon_handle h, cxobj *xt, modstate_diff_t *msd, cbuf *cb);
yang_stmt *yang_find_module_by_prefix(yang_stmt *ys, char *prefix);
yang_stmt *yang_find_module_by_prefix_yspec(yang_stmt *yspec, char *prefix);
yang_stmt *yang_find_module_by_namespace(yang_stmt *yspec, char *namespace);
yang_stmt *yang_find_module_by_name(yang_stmt *yspec, char *name);
#endif /* _CLIXON_YANG_MODULE_H_ */

View file

@ -0,0 +1,64 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
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 *****
* CALLING ORDER OF YANG PARSE FILES
* =================================
* yang_spec_parse_module
* | |
* v v v
* yang_spec_parse_file-> yang_parse_post->yang_parse_recurse->yang_parse_module
* \ / v
* yang_spec_load_dir ------------------------------------> yang_parse_filename
* v
* yang_parse_file
* v
* yang_parse_str
*/
#ifndef _CLIXON_YANG_PARSE_LIB_H_
#define _CLIXON_YANG_PARSE_LIB_H_
/*
* Prototypes
*/
yang_stmt *yang_parse_file(int fd, const char *name, yang_stmt *ysp);
yang_stmt *yang_parse_filename(const char *filename, yang_stmt *ysp);
int yang_spec_parse_module(clicon_handle h, const char *module,
const char *revision, yang_stmt *yspec);
int yang_spec_parse_file(clicon_handle h, char *filename, yang_stmt *yspec);
int yang_spec_load_dir(clicon_handle h, char *dir, yang_stmt *yspec);
int ys_parse_date_arg(char *datearg, uint32_t *dateint);
cg_var *ys_parse(yang_stmt *ys, enum cv_type cvtype);
int ys_parse_sub(yang_stmt *ys, char *extra);
#endif /* _CLIXON_YANG_LIB_H_ */

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -54,8 +54,6 @@ typedef struct yang_type_cache yang_type_cache;
/*
* Prototypes
*/
int yang_type_cache_cp(yang_type_cache **ycnew, yang_type_cache *ycold);
int yang_type_cache_free(yang_type_cache *ycache);
int ys_resolve_type(yang_stmt *ys, void *arg);
int yang2cv_type(char *ytype, enum cv_type *cv_type);
char *cv2yang_type(enum cv_type cv_type);

View file

@ -1,7 +1,8 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
# Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
# Copyright (C) 2017-2020 Olof Hagsand
#
# This file is part of CLIXON
#
@ -68,10 +69,10 @@ INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib/clixon -I$(top_srcdir)/include -I$
SRC = clixon_sig.c clixon_uid.c clixon_log.c clixon_err.c clixon_event.c \
clixon_string.c clixon_regex.c clixon_handle.c clixon_file.c \
clixon_xml.c clixon_xml_sort.c clixon_xml_map.c \
clixon_json.c clixon_yang.c clixon_yang_type.c clixon_yang_module.c \
clixon_xml.c clixon_xml_sort.c clixon_xml_map.c clixon_json.c \
clixon_yang.c clixon_yang_type.c clixon_yang_module.c clixon_yang_parse_lib.c \
clixon_yang_cardinality.c clixon_xml_changelog.c clixon_xml_nsctx.c \
clixon_api_path.c clixon_validate.c \
clixon_path.c clixon_validate.c \
clixon_hash.c clixon_options.c clixon_data.c clixon_plugin.c \
clixon_proto.c clixon_proto_client.c \
clixon_xpath.c clixon_xpath_ctx.c clixon_xpath_eval.c clixon_xpath_optimize.c \
@ -82,7 +83,9 @@ SRC = clixon_sig.c clixon_uid.c clixon_log.c clixon_err.c clixon_event.c \
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \
lex.clixon_json_parse.o clixon_json_parse.tab.o \
lex.clixon_xpath_parse.o clixon_xpath_parse.tab.o
lex.clixon_xpath_parse.o clixon_xpath_parse.tab.o \
lex.clixon_api_path_parse.o clixon_api_path_parse.tab.o \
lex.clixon_instance_id_parse.o clixon_instance_id_parse.tab.o
# Generated src
GENSRC = build.c
@ -105,10 +108,14 @@ clean:
rm -f clixon_yang_parse.tab.[ch] clixon_yang_parse.[co]
rm -f clixon_json_parse.tab.[ch] clixon_json_parse.[co]
rm -f clixon_xpath_parse.tab.[ch] clixon_xpath_parse.[co]
rm -f clixon_api_path_parse.tab.[ch] clixon_api_path_parse.[co]
rm -f clixon_instance_id_parse.tab.[ch] clixon_instance_id_parse.[co]
rm -f lex.clixon_xml_parse.c
rm -f lex.clixon_yang_parse.c
rm -f lex.clixon_json_parse.c
rm -f lex.clixon_xpath_parse.c
rm -f lex.clixon_api_path_parse.c
rm -f lex.clixon_instance_id_parse.c
#############################################################################
# Implicit rules for lex and yacc.
@ -174,6 +181,32 @@ clixon_xpath_parse.tab.c: clixon_xpath_parse.tab.h
lex.clixon_xpath_parse.o : lex.clixon_xpath_parse.c clixon_xpath_parse.tab.h
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
# api-path parser
lex.clixon_api_path_parse.c : clixon_api_path_parse.l clixon_api_path_parse.tab.h
$(LEX) -Pclixon_api_path_parse clixon_api_path_parse.l # -d is debug
clixon_api_path_parse.tab.h: clixon_api_path_parse.y
$(YACC) -l -d -b clixon_api_path_parse -p clixon_api_path_parse clixon_api_path_parse.y # -t is debug
# extra rule to avoid parallell yaccs
clixon_api_path_parse.tab.c: clixon_api_path_parse.tab.h
lex.clixon_api_path_parse.o : lex.clixon_api_path_parse.c clixon_api_path_parse.tab.h
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
# instance-identifier parser
lex.clixon_instance_id_parse.c : clixon_instance_id_parse.l clixon_instance_id_parse.tab.h
$(LEX) -Pclixon_instance_id_parse clixon_instance_id_parse.l # -d is debug
clixon_instance_id_parse.tab.h: clixon_instance_id_parse.y
$(YACC) -l -d -b clixon_instance_id_parse -p clixon_instance_id_parse clixon_instance_id_parse.y # -t is debug
# extra rule to avoid parallell yaccs
clixon_instance_id_parse.tab.c: clixon_instance_id_parse.tab.h
lex.clixon_instance_id_parse.o : lex.clixon_instance_id_parse.c clixon_instance_id_parse.tab.h
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
distclean: clean
rm -f Makefile *~ .depend

View file

@ -0,0 +1,68 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
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 *****
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
*/
#ifndef _CLIXON_API_PATH_PARSE_H_
#define _CLIXON_API_PATH_PARSE_H_
/*
* Types
*/
struct clicon_api_path_yacc_arg{
const char *ay_name; /* Name of syntax (for error string) */
int ay_linenum; /* Number of \n in parsed buffer */
char *ay_parse_string; /* original (copy of) parse string */
void *ay_lexbuf; /* internal parse buffer from lex */
clixon_path *ay_top;
};
/*
* Variables
*/
extern char *clixon_api_path_parsetext;
/*
* Prototypes
*/
int api_path_scan_init(struct clicon_api_path_yacc_arg *);
int api_path_scan_exit(struct clicon_api_path_yacc_arg *);
int api_path_parse_init(struct clicon_api_path_yacc_arg *);
int api_path_parse_exit(struct clicon_api_path_yacc_arg *);
int clixon_api_path_parselex(void *);
int clixon_api_path_parseparse(void *);
void clixon_api_path_parseerror(void *, char*);
#endif /* _CLIXON_API_PATH_PARSE_H_ */

View file

@ -0,0 +1,150 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
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 *****
*
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.53.
* BNF:
* <api-path> := <root> ("/" (<api-identifier> | <list-instance>))*
* <root> := <string>
* <api-identifier> := [<module-name> ":"] <identifier>
* <module-name> := <identifier>
* <list-instance> := <api-identifier> "=" key-value *("," key-value)
* <key-value> := <string>
* <string> := <an unquoted string>
* <identifier> := (<ALPHA> | "_") (<ALPHA> | <DIGIT> | "_" | "-" | ".")
* @note 1. <root> is the RESTCONF root resource (Sec 3.3) omitted in all calls below, it is
* assumed to be stripped from api-path before calling these functions.
* @note 2. characters in a key value string are constrained, and some characters need to be
* percent-encoded,
* XXX For some reason, I cant use "return *string" in these rules, so I resort to symbols
* (eg slash)
*/
%{
#include "clixon_config.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include "clixon_api_path_parse.tab.h" /* generated */
#include <cligen/cligen.h>
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_log.h"
#include "clixon_string.h"
#include "clixon_xml.h"
#include "clixon_path.h"
#include "clixon_api_path_parse.h"
/* Redefine main lex function so that you can send arguments to it: _yy is added to arg list */
#define YY_DECL int clixon_api_path_parselex(void *_ay)
/* Dont use input function (use user-buffer) */
#define YY_NO_INPUT
/* typecast macro */
#define _AY ((struct clicon_api_path_yacc_arg *)_ay)
#define MAXBUF 4*4*64*1024
#undef clixon_api_path_parsewrap
int
clixon_api_path_parsewrap(void)
{
return 1;
}
%}
namestart [A-Z_a-z]
namechar [A-Z_a-z\-\.0-9]
identifier {namestart}{namechar}*
%x INIT
%s KEYV
%%
<INIT,KEYV>[ \t]
<INIT,KEYV>\n { _AY->ay_linenum++; }
<INIT,KEYV>\r
<INIT,KEYV><<EOF>> { return X_EOF; }
<INIT>\/ { return SLASH;}
<INIT>\= { BEGIN(KEYV); return EQUAL; }
<INIT>\: { return COLON; }
<INIT>{identifier} { clixon_api_path_parselval.string = strdup(yytext);
return IDENTIFIER; }
<<INIT>. { clixon_api_path_parseerror(_AY, "LEXICAL ERROR\n"); return -1; }
<KEYV>\, { return COMMA; }
<KEYV>\/ { BEGIN(INIT); return SLASH; }
<KEYV>[^:/?#\[\]@,]+ { clixon_api_path_parselval.string = strdup(yytext);
return STRING;}
<KEYV>. { clixon_api_path_parseerror(_AY, "LEXICAL ERROR\n"); return -1; }
%%
/*! Initialize scanner.
*/
int
api_path_scan_init(struct clicon_api_path_yacc_arg *ay)
{
BEGIN(INIT);
ay->ay_lexbuf = yy_scan_string(ay->ay_parse_string);
#if 1 /* XXX: just to use unput to avoid warning */
if (0)
yyunput(0, "");
#endif
return 0;
}
/*
* free buffers
* Even within Flex version 2.5 (this is assumed), freeing buffers is different.
*/
int
api_path_scan_exit(struct clicon_api_path_yacc_arg *ay)
{
yy_delete_buffer(ay->ay_lexbuf);
clixon_api_path_parselex_destroy(); /* modern */
return 0;
}

View file

@ -0,0 +1,298 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
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 *****
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
* BNF:
* <api-path> := <root> ("/" (<api-identifier> | <list-instance>))*
* <root> := <string> # See note 1 below
* <root> := <string>
* <api-identifier> := [<module-name> ":"] <identifier>
* <module-name> := <identifier>
* <list-instance> := <api-identifier> "=" key-value *("," key-value)
* <key-value> := <string>
* <string> := <an unquoted string>
* <identifier> := (<ALPHA> | "_") (<ALPHA> | <DIGIT> | "_" | "-" | ".")
* @note 1. <root> is the RESTCONF root resource (Sec 3.3) omitted in all calls below, it is
* assumed to be stripped from api-path before calling these functions.
* @note 2. characters in a key value string are constrained, and some characters need to be
* percent-encoded,
*/
%start start
/* Must be here to define YYSTYPE */
%union {
char *string;
void *stack; /* cv / cvec */
}
%token <string> IDENTIFIER
%token <string> STRING
%token <string> SLASH
%token <string> COLON
%token <string> COMMA
%token <string> EQUAL
%token <string> X_EOF
%type <stack> list
%type <stack> element
%type <stack> api_identifier
%type <string> module_name
%type <stack> list_instance
%type <stack> key_values
%type <stack> key_value
%lex-param {void *_ay} /* Add this argument to parse() and lex() function */
%parse-param {void *_ay}
%{
/* Here starts user C-code */
/* typecast macro */
#define _AY ((struct clicon_api_path_yacc_arg *)_ay)
#define _YYERROR(msg) {clicon_err(OE_XML, 0, "YYERROR %s '%s' %d", (msg), clixon_api_path_parsetext, _AY->ay_linenum); YYERROR;}
/* add _yy to error parameters */
#define YY_(msgid) msgid
#include "clixon_config.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <cligen/cligen.h>
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_queue.h"
#include "clixon_string.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_path.h"
#include "clixon_api_path_parse.h"
/*
also called from yacc generated code *
*/
void
clixon_api_path_parseerror(void *_ay,
char *s)
{
clicon_err(OE_XML, 0, "%s on line %d: %s at or before: '%s'",
_AY->ay_name,
_AY->ay_linenum ,
s,
clixon_api_path_parsetext);
return;
}
int
api_path_parse_init(struct clicon_api_path_yacc_arg *ay)
{
return 0;
}
int
api_path_parse_exit(struct clicon_api_path_yacc_arg *ay)
{
return 0;
}
/*! Append new path structure to clixon path list
*/
static clixon_path *
path_append(clixon_path *list,
clixon_path *new)
{
clicon_debug(1, "%s()", __FUNCTION__);
if (new == NULL)
return NULL;
ADDQ(new, list);
return list;
}
/*! Add keyvalue to existing clixon path
*/
static clixon_path *
path_add_keyvalue(clixon_path *cp,
cvec *cvk)
{
clicon_debug(1, "%s()", __FUNCTION__);
if (cp)
cp->cp_cvk = cvk;
return cp;
}
static clixon_path *
path_new(char *module_name,
char *id)
{
clixon_path *cp = NULL;
clicon_debug(1, "%s(%s,%s)", __FUNCTION__, module_name, id);
if ((cp = malloc(sizeof(*cp))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(cp, 0, sizeof(*cp));
if (module_name)
if ((cp->cp_prefix = strdup(module_name)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
if ((cp->cp_id = strdup(id)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
return cp;
done:
return NULL;
}
/*! Append a key-value cv to a cvec, create the cvec if not exist
* @param[in] cvv Either created cvv or NULL, in whihc case it is created
* @param[in] cv Is consumed by thius function (if appended)
* @retval NULL Error
* @retval cvv Cvec
*/
static cvec *
keyval_add(cvec *cvv,
cg_var *cv)
{
clicon_debug(1, "%s()", __FUNCTION__);
if (cv == NULL)
goto done;
if (cvv == NULL &&
(cvv = cvec_new(0)) == NULL) {
clicon_err(OE_UNIX, errno, "cvec_new");
goto done;
}
if (cvec_append_var(cvv, cv) == NULL){
clicon_err(OE_UNIX, errno, "cvec_append_var");
cvv = NULL;
goto done;
}
cv_free(cv);
done:
return cvv;
}
/*! Create a single key-value as cv and return it
*/
static cg_var *
keyval_set(char *name,
char *val)
{
cg_var *cv = NULL;
clicon_debug(1, "%s(%s=%s)", __FUNCTION__, name, val);
if ((cv = cv_new(CGV_STRING)) == NULL){
clicon_err(OE_UNIX, errno, "cv_new");
goto done;
}
if (name && cv_name_set(cv, name) == NULL){
clicon_err(OE_UNIX, errno, "cv_string_set");
cv = NULL;
goto done;
}
if (cv_string_set(cv, val) == NULL){
clicon_err(OE_UNIX, errno, "cv_string_set");
cv = NULL;
goto done;
}
done:
return cv;
}
%}
%%
/*
*/
start : list X_EOF {clicon_debug(2,"top");_AY->ay_top=$1; YYACCEPT; }
;
list : list SLASH element { if (($$ = path_append($1, $3)) == NULL) YYABORT;
clicon_debug(2,"list = list / element");}
| { $$ = NULL;
clicon_debug(2,"list = ");}
;
element : api_identifier { $$=$1;
clicon_debug(2,"element = api_identifier");}
| list_instance { $$=$1;
clicon_debug(2,"element = list_instance");}
;
api_identifier : module_name COLON IDENTIFIER { $$ = path_new($1, $3); free($1); free($3);
clicon_debug(2,"api_identifier = module_name : IDENTIFIER");}
| IDENTIFIER { $$ = path_new(NULL, $1); free($1);
clicon_debug(2,"api_identifier = IDENTIFIER");}
;
module_name : IDENTIFIER { $$ = $1;
clicon_debug(2,"module_name = IDENTIFIER");}
;
list_instance : api_identifier EQUAL key_values { $$ = path_add_keyvalue($1, $3);
clicon_debug(2,"list_instance->api_identifier = key_values");}
;
key_values : key_values COMMA key_value { if (($$ = keyval_add($1, $3)) == NULL) YYABORT;
clicon_debug(2,"key_values->key_values , key_value");}
| key_value { if (($$ = keyval_add(NULL, $1)) == NULL) YYABORT;
clicon_debug(2,"key_values->key_value");}
;
key_value : STRING { $$ = keyval_set(NULL, $1); free($1); clicon_debug(2,"keyvalue->STRING"); }
| { $$ = keyval_set(NULL, ""); clicon_debug(2,"keyvalue->"); }
;
%%

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -853,7 +853,6 @@ xmldb_put(clicon_handle h,
xml_name(x1));
goto done;
}
if ((de = clicon_db_elmnt_get(h, db)) != NULL){
if (clicon_datastore_cache(h) != DATASTORE_NOCACHE)
x0 = de->de_xml;

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -88,6 +89,7 @@ static struct errvec EV[] = {
{"Daemon error", OE_DAEMON},
{"Event error", OE_EVENTS},
{"Config error", OE_CFG},
{"Netconf error", OE_NETCONF},
{"Protocol error", OE_PROTO},
{"Regexp error", OE_REGEX},
{"UNIX error", OE_UNIX},

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -60,10 +60,11 @@
#include "clixon_string.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_yang_type.h"
#include "clixon_options.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_yang_type.h"
#include "clixon_yang_module.h"
#include "clixon_xml_sort.h"
#include "clixon_xml_map.h"
#include "clixon_xml_nsctx.h" /* namespace context */
@ -331,7 +332,7 @@ json2xml_decode_identityref(cxobj *x,
if (yang_find_prefix_by_namespace(y, namespace, &prefix2) == 0){
#ifndef IDENTITYREF_KLUDGE
/* Just get the prefix from the module's own namespace */
if (netconf_unknown_namespace_xml(xerr, "application",
if (xerr && netconf_unknown_namespace_xml(xerr, "application",
namespace,
"No local prefix corresponding to namespace") < 0)
goto done;
@ -365,7 +366,7 @@ json2xml_decode_identityref(cxobj *x,
goto done;
}
else{
if (netconf_unknown_namespace_xml(xerr, "application",
if (xerr && netconf_unknown_namespace_xml(xerr, "application",
prefix,
"No module corresponding to prefix") < 0)
goto done;
@ -1187,7 +1188,7 @@ json_parse(char *str,
*
* @code
* cxobj *cx = NULL;
* if (json_parse_str(str, &cx) < 0)
* if (json_parse_str(str, yspec, &cx, &xerr) < 0)
* err;
* xml_free(cx);
* @endcode

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -82,7 +82,7 @@ integer {digit}+
real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+)
exp ({integer}|{real})[eE][+-]{integer}
%x START
%x START§
%s STRING
%s ESCAPE

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -245,7 +245,7 @@ json_current_body(struct clicon_json_yacc_arg *jy,
*/
/* top: json -> value is also possible */
json : value J_EOF { clicon_debug(2,"json->object"); YYACCEPT; }
§json : value J_EOF { clicon_debug(2,"json->object"); YYACCEPT; }
;
value : J_TRUE { json_current_body(_JY, "true");}

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -498,7 +498,7 @@ nacm_data_read_xr(cxobj *xt,
* For NETCONF filtering purposes, the selection criteria are applied to the
* subset of nodes that the user is authorized to read, not the entire datastore.
* @note assume mode is internal or external, not disabled
* @node There is unclarity on what "a data node" means wrt a read operation.
* @note There is unclarity on what "a data node" means wrt a read operation.
* Suppose a tree is accessed. Is "the data node" just the top of the tree?
* (1) Or is it all nodes, recursively, in the data-tree?
* (2) Or is the datanode only the requested tree, NOT the whole datatree?

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -66,6 +67,7 @@
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_yang_module.h"
#include "clixon_yang_parse_lib.h"
#include "clixon_netconf_lib.h"
/*! Create Netconf in-use error XML tree according to RFC 6241 Appendix A

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -72,6 +73,7 @@
#include "clixon_data.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_yang_parse_lib.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_nsctx.h"
#include "clixon_validate.h"

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -31,17 +31,30 @@
***** END LICENSE BLOCK *****
* This file has code for several variants of paths in cxobj trees:
* - api-path as defined by RESTCONF
* - instance-identifier as defined by YANG
* - clixon-path is an internal format which both ^ use as internal representation
*
* "Instance-identifier" is a subset of XML Xpaths and defined in Yang, used in NACM for example.
* and defined in RF7950 Sections 9.13 and 14.
* To note: prefixes depend on the XML context in which the value occurs,
*
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
* BNF:
* <api-path> := <root> ("/" (<api-identifier> | <list-instance>))*
* <root> := <string>
* <root> := <string> # See note 1 below
* <api-identifier> := [<module-name> ":"] <identifier>
* <module-name> := <identifier>
* <list-instance> := <api-identifier> "=" key-value *("," key-value)
* <key-value> := <string>
* <string> := <an unquoted string>
* <identifier> := (<ALPHA> | "_") (<ALPHA> | <DIGIT> | "_" | "-" | ".")
*
* @note 1. <root> is the RESTCONF root resource (Sec 3.3) omitted in all calls below, it is
* assumed to be stripped from api-path before calling these functions.
* @note 2. characters in a key value string are constrained, and some characters need to be
* percent-encoded,
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
@ -75,9 +88,158 @@
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xml_sort.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_map.h"
#include "clixon_api_path.h"
#include "clixon_yang_module.h"
#include "clixon_path.h"
#include "clixon_api_path_parse.h"
#include "clixon_instance_id_parse.h"
/*! Given api-path, parse it, and return a clixon-path struct
*
* @param[in] api_path String with api-path syntax according to RESTCONF RFC8040
* @param[out] cplist Structured internal clixon-path
* @retval 0 OK
* @retval -1 Error
* @code
* clixon_path *cplist = NULL;
* if (api_path_parse(api_path, &cplist) < 0)
* err;
* if (api_path_resolve(cplist, yt) < 0)
* err;
* ...
* if (cplist)
* clixon_path_free(cplist);
* @endcode
* @see clixon_path_free
*/
static int
api_path_parse(char *api_path,
clixon_path **cplist)
{
int retval = -1;
struct clicon_api_path_yacc_arg ay = {0,};
clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path);
ay.ay_parse_string = api_path;
ay.ay_name = "api-path parser";
ay.ay_linenum = 1;
if (api_path_scan_init(&ay) < 0)
goto done;
if (api_path_parse_init(&ay) < 0)
goto done;
if (clixon_api_path_parseparse(&ay) != 0) { /* yacc returns 1 on error */
clicon_log(LOG_NOTICE, "API-PATH error: on line %d", ay.ay_linenum);
if (clicon_errno == 0)
clicon_err(OE_XML, 0, "API-PATH parser error with no error code (should not happen)");
goto done;
}
api_path_parse_exit(&ay);
api_path_scan_exit(&ay);
*cplist = ay.ay_top;
retval = 0;
done:
return retval;
}
/*! Given instance-id path, parse it, and return an clixon-path struct
*
* @param[in] api_path String with syntax according to YANG RFC7980
* @param[out] cplist Structured internal clixon-path
* @retval 0 OK
* @retval -1 Error
* @code
* clixon_path *cplist = NULL;
* if (instance_id_parse(api_path, &cplist) < 0)
* err;
* ...
* if (cplist)
* clixon_path_free(cplist);
* @endcode
* @see clixon_path_free
*/
static int
instance_id_parse(char *path,
clixon_path **cplist)
{
int retval = -1;
struct clicon_instance_id_yacc_arg iy = {0,};
clicon_debug(1, "%s path:%s", __FUNCTION__, path);
iy.iy_parse_string = path;
iy.iy_name = "instance-id parser";
iy.iy_linenum = 1;
if (instance_id_scan_init(&iy) < 0)
goto done;
if (instance_id_parse_init(&iy) < 0)
goto done;
if (clixon_instance_id_parseparse(&iy) != 0) { /* yacc returns 1 on error */
clicon_log(LOG_NOTICE, "Instance-id error: on line %d", iy.iy_linenum);
if (clicon_errno == 0)
clicon_err(OE_XML, 0, "Instance-id parser error with no error code (should not happen)");
goto done;
}
instance_id_parse_exit(&iy);
instance_id_scan_exit(&iy);
*cplist = iy.iy_top;
retval = 0;
done:
return retval;
}
static int
clixon_path_free(clixon_path *cplist)
{
clixon_path *cp;
while ((cp = cplist) != NULL){
DELQ(cp, cplist, clixon_path *);
if (cp->cp_prefix)
free(cp->cp_prefix);
if (cp->cp_id)
free(cp->cp_id);
if (cp->cp_cvk)
cvec_free(cp->cp_cvk);
free(cp);
}
return 0;
}
/*! Print path on instance-id/xpath form
*/
static int
clixon_path_print(FILE *f,
clixon_path *cplist)
{
clixon_path *cp;
cg_var *cv;
if ((cp = cplist) != NULL){
do {
fprintf(f, "/");
if (cp->cp_prefix)
fprintf(f, "%s:", cp->cp_prefix);
fprintf(f, "%s", cp->cp_id);
if (cp->cp_cvk){
fprintf(f, "=");
cv = NULL;
while ((cv = cvec_each(cp->cp_cvk, cv)) != NULL){
fprintf(f, "[");
/* If cvk has one integer argument, interpret as position, eg x/y[42] */
if (cvec_len(cp->cp_cvk) == 1 && (cv_type_get(cv) == CGV_UINT32))
fprintf(f, "%u", cv_uint32_get(cv));
else
fprintf(f, "%s=\"%s\"", cv_name_get(cv), cv_string_get(cv));
fprintf(f, "]");
}
}
cp = NEXTQ(clixon_path *, cp);
} while (cp && cp != cplist);
}
fprintf(f, "\n");
return 0;
}
/*! Given an XML node, return root node
* A root node is an ancestor xr of x with one or both of the following properties
@ -936,7 +1098,7 @@ api_path2xml(char *api_path,
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (*api_path!='/'){
if (*api_path != '/'){
cprintf(cberr, "Invalid api-path: %s (must start with '/')", api_path);
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
goto done;
@ -955,8 +1117,8 @@ api_path2xml(char *api_path,
}
nvec--; /* NULL-terminated */
if ((retval = api_path2xml_vec(vec+1, nvec,
xtop, yspec, nodeclass, strict,
xbotp, ybotp, xerr)) < 1)
xtop, yspec, nodeclass, strict,
xbotp, ybotp, xerr)) < 1)
goto done;
xml_yang_root(*xbotp, &xroot);
if (xmlns_assign(xroot) < 0)
@ -1062,3 +1224,409 @@ xml2api_path_1(cxobj *x,
done:
return retval;
}
/*! Resolve api-path module:names to yang statements
* @param[in] cplist Lisp of clixon-path
* @param[in] yt Yang statement of top symbol (can be yang-spec if top-level)
* @retval -1 Error
* @retval 0 Fail
* @retval 1 OK
* Reasons for fail (retval = 0) are: XXX
* - Modulename in api-path does not correspond to existing module
* - Modulename not defined for top-level id.
* - Corresponding yang node for id not found
* - Number of keys in key-value list does not match Yang list
* - Key-values only defined for list or leaf-list
* @see instance_id_resolve
*/
static int
api_path_resolve(clixon_path *cplist,
yang_stmt *yt)
{
int retval = -1;
clixon_path *cp;
yang_stmt *yc;
int i;
cg_var *cva;
cg_var *cvy;
if ((cp = cplist) != NULL){
do {
if (yang_keyword_get(yt) == Y_SPEC){
if (cp->cp_prefix == NULL){
clicon_err(OE_YANG, ENOENT, "Modulename not defined for top-level id.");
goto fail;
}
if ((yt = yang_find_module_by_name(yt, cp->cp_prefix)) == NULL){
clicon_err(OE_YANG, ENOENT, "Modulename in api-path does not correspond to existing module");
goto fail;
}
}
if ((yc = yang_find_datanode(yt, cp->cp_id)) == NULL){
clicon_err(OE_YANG, ENOENT, "Corresponding yang node for id not found");
goto fail;
}
cp->cp_yang = yc;
if (cp->cp_cvk){
/* Iterate over yang list keys and assign as names (or "." for leaf-list) in cvk */
if (yang_keyword_get(yc) == Y_LEAF_LIST){
cva = NULL;
while ((cva = cvec_each(cp->cp_cvk, cva)) != NULL) {
if (cv_name_get(cva) == NULL)
cv_name_set(cva, ".");
}
}
else if (yang_keyword_get(yc) == Y_LIST){
if (cvec_len(cp->cp_cvk) > cvec_len(yang_cvec_get(yc))){
clicon_err(OE_YANG, ENOENT, "Number of keys in key-value list does not match Yang list");
goto fail;
}
i = 0;
cva = NULL;
while ((cva = cvec_each(cp->cp_cvk, cva)) != NULL) {
if (cv_name_get(cva) == NULL){
cvy = cvec_i(yang_cvec_get(yc), i);
cv_name_set(cva, cv_string_get(cvy));
}
i++;
}
}
else{
clicon_err(OE_YANG, ENOENT, "key-values only defined for list or leaf-list");
goto fail;
}
}
yt = yc;
cp = NEXTQ(clixon_path *, cp);
} while (cp && cp != cplist);
}
retval = 1;
done:
return retval;
fail:
retval = 0;
goto done;
}
/*! Resolve instance-id prefix:names to yang statements
* @param[in] cplist Lisp of clixon-path
* @param[in] yt Yang statement of top symbol (can be yang-spec if top-level)
* @param[in] xt XML statement for prefix context
* @retval -1 Error
* @retval 0 Fail
* @retval 1 OK
* @note: The spec says: prefixes depend on the XML context in which the value occurs. However,
* canonical prefixes/namespaces are used based on loaded yang modules.
* This means that prefix=NULL is not allowed.
* Reasons for fail (retval = 0) are:
* - No prefix of identifier (keynames may omit prefix)
* - Prefix does not correspond to existing module.
* - Corresponding yang node for id not found
* - Number of keys in key-value list does not match Yang list
* - Key-values only defined for list or leaf-list
* @see api_path_resolve
*/
static int
instance_id_resolve(clixon_path *cplist,
yang_stmt *yt)
{
int retval = -1;
clixon_path *cp;
yang_stmt *yc;
int i;
cg_var *cva;
cg_var *cvy;
yang_stmt *yspec;
yspec = ys_spec(yt);
if ((cp = cplist) != NULL){
do {
if (cp->cp_prefix == NULL){
clicon_err(OE_YANG, ENOENT, "No prefix of identifier (keynames may omit prefix)");
goto fail;
}
if (yang_keyword_get(yt) == Y_SPEC){
if ((yt = yang_find_module_by_prefix_yspec(yspec, cp->cp_prefix)) == NULL){
clicon_err(OE_YANG, ENOENT, "Prefix does not correspond to existing module.");
goto fail;
}
}
if ((yc = yang_find_datanode(yt, cp->cp_id)) == NULL){
clicon_err(OE_YANG, ENOENT, "Corresponding yang node for id not found");
goto fail;
}
cp->cp_yang = yc;
if (cp->cp_cvk){
/* Iterate over yang list keys and assign as names in cvk */
if (yang_keyword_get(yc) == Y_LEAF_LIST)
;
else if (yang_keyword_get(yc) == Y_LIST){
#ifdef notused
if (cvec_len(cp->cp_cvk) > cvec_len(yang_cvec_get(yc))){
clicon_err(OE_YANG, ENOENT, "Number of keys in key-value list does not match Yang list ");
goto fail;
}
#endif
i = 0;
cva = NULL;
while ((cva = cvec_each(cp->cp_cvk, cva)) != NULL) {
if (cv_name_get(cva) == NULL){
cvy = cvec_i(yang_cvec_get(yc), i);
cv_name_set(cva, cv_string_get(cvy));
}
i++;
}
}
else{
clicon_err(OE_YANG, ENOENT, "key-values only defined for list or leaf-list");
goto fail;
}
}
yt = yc;
cp = NEXTQ(clixon_path *, cp);
} while (cp && cp != cplist);
}
retval = 1;
done:
return retval;
fail:
retval = 0;
goto done;
}
/*! Search XML tree using Clixon path struct
*
* @param[in] xt Top xml-tree where to search
* @param[in] yt Yang statement of top symbol (can be yang-spec if top-level)
* @param[in] cplist Lisp of clixon-path
* @param[out] xvec Vector of xml-trees. Vector must be free():d after use
* @param[out] xlen Returns length of vector in return value
* @retval -1 Error
* @retval 0 Fail fail: eg no yang
* @retval 1 OK with found xml nodes in xvec (if any)
*/
static int
clixon_path_search(cxobj *xt,
yang_stmt *yt,
clixon_path *cplist,
cxobj ***xvec0,
size_t *xlen0)
{
int retval = -1;
char *modns; /* Module namespace of api-path */
clixon_path *cp;
cxobj **xvecp = NULL; /* parent set */
size_t xlenp = 0;
cxobj **xvecc = NULL; /* child set */
size_t xlenc = 0;
yang_stmt *yc;
cxobj *xp;
int i;
cg_var *cv;
modns = NULL;
if ((cp = cplist) != NULL){
cxvec_append(xt, &xvecp, &xlenp); /* Initialize parent xml set */
do {
yc = cp->cp_yang;
if ((modns = yang_find_mynamespace(yc)) == NULL)
goto fail;
for (i=0; i<xlenp; i++){
xp = xvecp[i]; /* Iterate over parent set */
if (cp->cp_cvk && /* cornercase for instance-id [<pos>] special case */
(yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST) &&
cvec_len(cp->cp_cvk) == 1 && (cv = cvec_i(cp->cp_cvk,0)) &&
(cv_type_get(cv) == CGV_UINT32)){
if (clixon_xml_find_pos(xp, yc, cv_uint32_get(cv), &xvecc, &xlenc) < 0)
goto done;
}
else if (clixon_xml_find_index(xp, yang_parent_get(yc), modns, yang_argument_get(yc),
cp->cp_cvk, &xvecc, &xlenc) < 0)
goto done;
}
if (xvecp)
free(xvecp);
xvecp = xvecc; xlenp = xlenc;
xvecc = NULL, xlenc = 0;
cp = NEXTQ(clixon_path *, cp);
} while (cp && cp != cplist);
*xvec0 = xvecp; xvecp = NULL;
*xlen0 = xlenp;
}
retval = 1;
done:
if (xvecp)
free(xvecp);
if (xvecc)
free(xvecc);
return retval;
fail: /* eg no yang for api-path, no match */
*xvec0 = NULL;
*xlen0 = 0;
retval = 0;
goto done;
}
/*! Given RESTCONF api-path and an XML tree, return matching xml node vector using stdarg
*
* @param[in] xt Top xml-tree where to search
* @param[in] yt Yang statement of top symbol (can be yang-spec if top-level)
* @param[out] xvec Vector of xml-trees. Vector must be free():d after use
* @param[out] xlen Returns length of vector in return value
* @param[in] format Format string for api-path syntax
* @retval -1 Error
* @retval 0 Nomatch
* @retval 1 OK with found xml nodes in xvec (if any)
* Reasons for nomatch (retval = 0) are:
* - Modulename in api-path does not correspond to existing module
* - Modulename not defined for top-level id.
* - Number of keys in key-value list does not match Yang list
* @code
* cxobj **xvec = NULL;
* size_t xlen;
* if (clixon_xml_find_api_path(x, &xvec, &xlen, "/symbol/%s", "foo") < 0)
* goto err;
* for (i=0; i<xlen; i++){
* xn = xvec[i];
* ...
* }
* free(xvec);
* @endcode
* @see xpath_vec
*/
int
clixon_xml_find_api_path(cxobj *xt,
yang_stmt *yt,
cxobj ***xvec,
size_t *xlen,
char *format,
...)
{
int retval = -1;
va_list ap;
size_t len;
char *api_path = NULL;
clixon_path *cplist = NULL;
int ret;
va_start(ap, format);
len = vsnprintf(NULL, 0, format, ap);
va_end(ap);
/* allocate an api-path string exactly fitting the length */
if ((api_path = malloc(len+1)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
/* second round: actually compute api-path string content */
va_start(ap, format);
if (vsnprintf(api_path, len+1, format, ap) < 0){
clicon_err(OE_UNIX, errno, "vsnprintf");
va_end(ap);
goto done;
}
va_end(ap);
/* Parse api-path string to structured clixon-path data */
if (api_path_parse(api_path, &cplist) < 0)
goto done;
if (debug)
clixon_path_print(stderr, cplist);
/* Resolve module:name to yang-stmt, fail if not successful */
if ((ret = api_path_resolve(cplist, yt)) < 0)
goto done;
if (ret == 0)
goto fail;
retval = clixon_path_search(xt, yt, cplist, xvec, xlen);
done:
if (cplist)
clixon_path_free(cplist);
if (api_path)
free(api_path);
return retval;
fail:
retval = 0;
goto done;
}
/*! Given (instance-id) path and an XML tree, return matching xml node vector using stdarg
*
* Instance-identifier is a subset of XML Xpaths and defined in Yang, used in NACM for example.
* @param[in] xt Top xml-tree where to search
* @param[in] yt Yang statement of top symbol (can be yang-spec if top-level)
* @param[out] xvec Vector of xml-trees. Vector must be free():d after use
* @param[out] xlen Returns length of vector in return value
* @param[in] format Format string for api-path syntax
* @retval -1 Error
* @retval 0 Nomatch
* @retval 1 OK with found xml nodes in xvec (if any)
* Reasons for nomatch (retval = 0) are:
* - Modulename in api-path does not correspond to existing module
* - Modulename not defined for top-level id.
* - Number of keys in key-value list does not match Yang list
* @code
* cxobj **xvec = NULL;
* size_t xlen;
* if (clixon_xml_find_instance_id(x, &xvec, &xlen, "/symbol/%s", "foo") < 0)
* goto err;
* for (i=0; i<xlen; i++){
* xn = xvec[i];
* ...
* }
* free(xvec);
* @endcode
* @see xpath_vec for full XML Xpaths
* @see api_path_search for RESTCONF api-paths
* @see RFC7950 Sec 9.13
*/
int
clixon_xml_find_instance_id(cxobj *xt,
yang_stmt *yt,
cxobj ***xvec,
size_t *xlen,
char *format,
...)
{
int retval = -1;
va_list ap;
size_t len;
char *path = NULL;
clixon_path *cplist = NULL;
int ret;
va_start(ap, format);
len = vsnprintf(NULL, 0, format, ap);
va_end(ap);
/* allocate a path string exactly fitting the length */
if ((path = malloc(len+1)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
/* second round: actually compute api-path string content */
va_start(ap, format);
if (vsnprintf(path, len+1, format, ap) < 0){
clicon_err(OE_UNIX, errno, "vsnprintf");
va_end(ap);
goto done;
}
va_end(ap);
if (instance_id_parse(path, &cplist) < 0)
goto done;
if (debug)
clixon_path_print(stderr, cplist);
/* Resolve module:name to yang-stmt, fail if not successful */
if ((ret = instance_id_resolve(cplist, yt)) < 0)
goto done;
if (ret == 0)
goto fail;
if ((retval = clixon_path_search(xt, yt, cplist, xvec, xlen)) < 0)
goto done;
done:
if (cplist)
clixon_path_free(cplist);
if (path)
free(path);
return retval;
fail:
retval = 0;
goto done;
}

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -247,12 +248,10 @@ clicon_rpc_generate_error(cxobj *xerr,
}
if (netconf_err2cb(xerr, cb) < 0)
goto done;
if (arg){
cprintf(cb, "%s: \"%s\": ", msg, arg);
clicon_err(OE_CFG, EINVAL, "%s", cbuf_get(cb));
}
else /* XXX: should really be clicon_err but dont do it yet for backward compatability */
clicon_log(LOG_ERR, "%s: %s", msg, cbuf_get(cb));
cprintf(cb, ". %s", msg);
if (arg)
cprintf(cb, " \"%s\" ", arg);
clicon_err(OE_NETCONF, 0, "%s", cbuf_get(cb));
retval = 0;
done:
if (cb)

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -71,22 +72,14 @@
#include "clixon_xml_nsctx.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#if 0
#include "clixon_plugin.h"
#include "clixon_log.h"
#include "clixon_xml_sort.h"
#include "clixon_yang_internal.h" /* internal */
#endif
#include "clixon_yang_module.h"
#include "clixon_yang_type.h"
#include "clixon_xml_map.h"
#include "clixon_validate.h"
/*! Validate xml node of type leafref, ensure the value is one of that path's reference
* @param[in] xt XML leaf node of type leafref
* @param[in] ys Yang spec of leaf
* @param[in] ytype Yang type statement belonging to the XML node
* @param[out] xret Error XML tree. Free with xml_free after use
* @retval 1 Validation OK
@ -97,17 +90,19 @@
* in addition to the definition in Section 6.4.1:
* o If the "path" statement is defined within a typedef, the context
* node is the leaf or leaf-list node in the data tree that
* references the typedef.
* references the typedef. (ie ys)
* o Otherwise, the context node is the node in the data tree for which
* the "path" statement is defined.
* the "path" statement is defined. (ie yc)
*/
static int
validate_leafref(cxobj *xt,
yang_stmt *ys,
yang_stmt *ytype,
cxobj **xret)
{
int retval = -1;
yang_stmt *ypath;
yang_stmt *yp;
cxobj **xvec = NULL;
cxobj *x;
int i;
@ -115,6 +110,8 @@ validate_leafref(cxobj *xt,
char *leafrefbody;
char *leafbody;
cvec *nsc = NULL;
cbuf *cberr = NULL;
char *path;
if ((leafrefbody = xml_body(xt)) == NULL)
goto ok;
@ -123,10 +120,17 @@ validate_leafref(cxobj *xt,
goto done;
goto fail;
}
/* XXX see comment above regarding typeref or not */
if (xml_nsctx_yang(ytype, &nsc) < 0)
goto done;
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, yang_argument_get(ypath)) < 0)
/* See comment^: If path is defined in typedef or not */
if ((yp = yang_parent_get(ytype)) != NULL &&
yang_keyword_get(yp) == Y_TYPEDEF){
if (xml_nsctx_yang(ys, &nsc) < 0)
goto done;
}
else
if (xml_nsctx_yang(ytype, &nsc) < 0)
goto done;
path = yang_argument_get(ypath);
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, path) < 0)
goto done;
for (i = 0; i < xlen; i++) {
x = xvec[i];
@ -136,13 +140,20 @@ validate_leafref(cxobj *xt,
break;
}
if (i==xlen){
if (netconf_bad_element_xml(xret, "application", leafrefbody, "Leafref validation failed: No such leaf") < 0)
if ((cberr = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cberr, "Leafref validation failed: No leaf %s matching path %s", leafrefbody, path);
if (netconf_bad_element_xml(xret, "application", leafrefbody, cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
ok:
retval = 1;
done:
if (cberr)
cbuf_free(cberr);
if (nsc)
xml_nsctx_free(nsc);
if (xvec)
@ -153,7 +164,6 @@ validate_leafref(cxobj *xt,
goto done;
}
/*! Validate xml node of type identityref, ensure value is a defined identity
* Check if a given node has value derived from base identity. This is
* a run-time check necessary when validating eg netconf.
@ -963,7 +973,6 @@ xml_yang_validate_add(clicon_handle h,
goto fail;
}
}
if ((ys_cv_validate(h, cv, yt, &reason)) != 1){
if (netconf_bad_element_xml(xret, "application", yang_argument_get(yt), reason) < 0)
goto done;
@ -1064,6 +1073,7 @@ xml_yang_validate_all(clicon_handle h,
cxobj *x;
char *namespace = NULL;
cbuf *cb = NULL;
cvec *nsc = NULL;
/* if not given by argument (overide) use default link
and !Node has a config sub-statement and it is false */
@ -1102,7 +1112,7 @@ xml_yang_validate_all(clicon_handle h,
if (yang_type_get(ys, NULL, &yc, NULL, NULL, NULL, NULL, NULL) < 0)
goto done;
if (strcmp(yang_argument_get(yc), "leafref") == 0){
if ((ret = validate_leafref(xt, yc, xret)) < 0)
if ((ret = validate_leafref(xt, ys, yc, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -1124,7 +1134,9 @@ xml_yang_validate_all(clicon_handle h,
if (yang_keyword_get(yc) != Y_MUST)
continue;
xpath = yang_argument_get(yc); /* "must" has xpath argument */
if ((nr = xpath_vec_bool(xt, NULL, "%s", xpath)) < 0)
if (xml_nsctx_yang(yc, &nsc) < 0)
goto done;
if ((nr = xpath_vec_bool(xt, nsc, "%s", xpath)) < 0)
goto done;
if (!nr){
ye = yang_find(yc, Y_ERROR_MESSAGE, NULL);
@ -1133,6 +1145,10 @@ xml_yang_validate_all(clicon_handle h,
goto done;
goto fail;
}
if (nsc){
xml_nsctx_free(nsc);
nsc = NULL;
}
}
/* "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */
if ((yc = yang_find(ys, Y_WHEN, NULL)) != NULL){
@ -1167,6 +1183,8 @@ xml_yang_validate_all(clicon_handle h,
done:
if (cb)
cbuf_free(cb);
if (nsc)
xml_nsctx_free(nsc);
return retval;
fail:
retval = 0;

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -62,6 +63,7 @@
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_options.h" /* xml_spec_populate */
#include "clixon_yang_module.h"
#include "clixon_xml_map.h" /* xml_spec_populate */
#include "clixon_xml_sort.h"
#include "clixon_xml_parse.h"
@ -263,7 +265,7 @@ nscache_get_prefix(cxobj *x,
return 0;
}
/*! Dump whole namespacet context cache of one xml node
/*! Dump whole namespace context cache of one xml node
* @param[in] x XML node
* @retval nsc Whole namespace context of x
* @retval NULL Empty nsc
@ -581,7 +583,7 @@ xml_parent_set(cxobj *xn,
/*! Get xml node flags, used for internal algorithms
* @param[in] xn xml node
* @retval flag Flags value, see XML_FLAG_*
* @retval flag Flag value(s), see XML_FLAG_*
*/
uint16_t
xml_flag(cxobj *xn,
@ -592,7 +594,7 @@ xml_flag(cxobj *xn,
/*! Set xml node flags, used for internal algorithms
* @param[in] xn xml node
* @param[in] flag Flags value to set, see XML_FLAG_*
* @param[in] flag Flag values to set, see XML_FLAG_*
*/
int
xml_flag_set(cxobj *xn,
@ -604,7 +606,7 @@ xml_flag_set(cxobj *xn,
/*! Reset xml node flags, used for internal algorithms
* @param[in] xn xml node
* @param[in] flag Flags value to reset, see XML_FLAG_*
* @param[in] flag Flag value(s) to reset, see XML_FLAG_*
*/
int
xml_flag_reset(cxobj *xn,
@ -1048,8 +1050,12 @@ xml_cv_set(cxobj *x,
*
* @retval xmlobj if found.
* @retval NULL if no such node found.
* @see xml_find_type A more generic function
* @note Linear scalability and relies on strcmp
* There are several issues with this function:
* @note (1) Ignores prefix which means namespaces are ignored
* @note (2) Does not differentiate between element,attributes and body. You usually want elements.
* @note (3) Linear scalability and relies on strcmp, does not use search/key indexes
* @note (4) Only returns first match, eg a list/leaf-list may have several children with same name
* @see xml_find_type A more generic function fixes (1) and (2) above
*/
cxobj *
xml_find(cxobj *x_up,
@ -1494,9 +1500,10 @@ xml_find_type(cxobj *xt,
char *xprefix; /* xprefix */
while ((x = xml_child_each(xt, x, type)) != NULL) {
xprefix = xml_prefix(x);
if (prefix)
pmatch = xprefix?strcmp(prefix,xprefix)==0:0;
if (prefix){
xprefix = xml_prefix(x);
pmatch = xprefix ? strcmp(prefix,xprefix)==0 : 0;
}
else
pmatch = 1;
if (pmatch && strcmp(name, xml_name(x)) == 0)
@ -1980,7 +1987,9 @@ FSM(char *tag,
*
* @code
* cxobj *xt = NULL;
* xml_parse_file(0, "</config>", yspec, &xt);
* int fd;
* fd = open(filename, O_RDONLY);
* xml_parse_file(fd, "</config>", yspec, &xt);
* xml_free(xt);
* @endcode
* @see xml_parse_string
@ -2246,10 +2255,23 @@ cxvec_dup(cxobj **vec0,
return retval;
}
/*! Append a new xml tree to an existing xml vector
/*! Append a new xml tree to an existing xml vector last in the list
* @param[in] x XML tree (append this to vector)
* @param[in,out] vec XML tree vector
* @param[in,out] len Length of XML tree vector
* @retval 0 OK
* @retval -1 Error
* @code
* cxobj **xvec = NULL;
* size_t xlen = 0;
* cxobj *x;
*
* if (cxvec_append(x, &xvec, &xlen) < 0)
* err;
* if (xvec)
* free(xvec);
* @endcode
* @see cxvec_prepend
*/
int
cxvec_append(cxobj *x,
@ -2268,6 +2290,44 @@ cxvec_append(cxobj *x,
return retval;
}
/*! Prepend a new xml tree to an existing xml vector first in the list
* @param[in] x XML tree (append this to vector)
* @param[in,out] vec XML tree vector
* @param[in,out] len Length of XML tree vector
* @retval 0 OK
* @retval -1 Error
* @code
* cxobj **xvec = NULL;
* size_t xlen = 0;
* cxobj *x;
*
* if (cxvec_append(x, &xvec, &xlen) < 0)
* err;
* if (xvec)
* free(xvec);
* @endcode
* @see cxvec_prepend
*/
int
cxvec_prepend(cxobj *x,
cxobj ***vec,
size_t *len)
{
int retval = -1;
if ((*vec = realloc(*vec, sizeof(cxobj *) * (*len+1))) == NULL){
clicon_err(OE_XML, errno, "realloc");
goto done;
}
memmove(&(*vec)[1], &(*vec)[0], sizeof(cxobj *) * (*len));
(*vec)[0] = x;
(*len)++;
retval = 0;
done:
return retval;
}
/*! Apply a function call recursively on all xml node children recursively
* Recursively traverse all xml nodes in a parse-tree and apply fn(arg) for
* each object found. The function is called with the xml node and an
@ -2548,8 +2608,6 @@ xml_operation(char *opstr,
return 0;
}
/*! Map xml operation from enumeration to string
* @param[in] op enumeration operation, eg OP_MERGE,...
* @retval str String, eg "merge". Static string, no free necessary

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -66,6 +67,7 @@
#include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_yang_module.h"
#include "clixon_yang_parse_lib.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xml_map.h"

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -72,7 +73,6 @@
#include "clixon_err.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_sort.h"
#include "clixon_yang_internal.h" /* internal */
#include "clixon_yang_type.h"
#include "clixon_xml_map.h"
@ -296,7 +296,7 @@ xml2cvec(cxobj *xt,
name = xml_name(xc);
if ((ys = yang_find_datanode(yt, name)) == NULL){
clicon_debug(0, "%s: yang sanity problem: %s in xml but not present in yang under %s",
__FUNCTION__, name, yt->ys_argument);
__FUNCTION__, name, yang_argument_get(yt));
if ((body = xml_body(xc)) != NULL){
if ((cv = cv_new(CGV_STRING)) == NULL){
clicon_err(OE_PLUGIN, errno, "cv_new");
@ -476,7 +476,7 @@ xml_diff1(yang_stmt *ys,
continue;
}
/* Both x0c and x1c exists, check if they are equal. */
eq = xml_cmp(x0c, x1c, 0);
eq = xml_cmp(x0c, x1c, 0, 0);
if (eq < 0){
if (cxvec_append(x0c, x0vec, x0veclen) < 0)
goto done;
@ -502,7 +502,7 @@ xml_diff1(yang_stmt *ys,
if (cxvec_append(x1c, changed_x1, changedlen) < 0)
goto done;
}
else if (yc->ys_keyword == Y_LEAF){
else if (yang_keyword_get(yc) == Y_LEAF){
/* if x0c and x1c are leafs w bodies, then they are changed */
if ((b1 = xml_body(x0c)) == NULL) /* empty type */
break;
@ -761,7 +761,7 @@ xml_default(cxobj *xt,
int retval = -1;
yang_stmt *ys;
yang_stmt *y;
int i;
// int i; // XXX
cxobj *xc;
cxobj *xb;
char *str;
@ -775,15 +775,15 @@ xml_default(cxobj *xt,
goto done;
}
/* Check leaf defaults */
if (ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST ||
ys->ys_keyword == Y_INPUT){
for (i=0; i<ys->ys_len; i++){
y = ys->ys_stmt[i];
if (y->ys_keyword != Y_LEAF)
if (yang_keyword_get(ys) == Y_CONTAINER || yang_keyword_get(ys) == Y_LIST ||
yang_keyword_get(ys) == Y_INPUT){
y = NULL;
while ((y = yn_each(ys, y)) != NULL) {
if (yang_keyword_get(y) != Y_LEAF)
continue;
if (!cv_flag(yang_cv_get(y), V_UNSET)){ /* Default value exists */
if (!xml_find(xt, y->ys_argument)){
if ((xc = xml_new(y->ys_argument, NULL, y)) == NULL)
if (!xml_find(xt, yang_argument_get(y))){
if ((xc = xml_new(yang_argument_get(y), NULL, y)) == NULL)
goto done;
/* assign right prefix */
if ((namespace = yang_find_mynamespace(y)) != NULL){
@ -843,9 +843,9 @@ xml_sanity(cxobj *xt,
goto done;
}
name = xml_name(xt);
if (strstr(ys->ys_argument, name)==NULL){
if (strstr(yang_argument_get(ys), name)==NULL){
clicon_err(OE_XML, 0, "xml node name '%s' does not match yang spec arg '%s'",
name, ys->ys_argument);
name, yang_argument_get(ys));
goto done;
}
retval = 0;
@ -1126,7 +1126,7 @@ xmlns_assign(cxobj *x)
}
/* 1. Check which namespace x should have (via yang). This is correct namespace. */
if ((ns_correct = yang_find_mynamespace(y)) == NULL){
clicon_err(OE_YANG, ENOENT, "yang node %s does not have namespace", y->ys_argument);
clicon_err(OE_YANG, ENOENT, "yang node %s does not have namespace", yang_argument_get(y));
goto done;
}
/* 2. Check which namespace x has via its XML tree */
@ -1293,7 +1293,7 @@ xml_merge1(cxobj *x0, /* the target */
assert(y0);
x1name = xml_name(x1);
if (y0->ys_keyword == Y_LEAF_LIST || y0->ys_keyword == Y_LEAF){
if (yang_keyword_get(y0) == Y_LEAF_LIST || yang_keyword_get(y0) == Y_LEAF){
x1bstr = xml_body(x1);
if (x0==NULL){
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
@ -1479,7 +1479,7 @@ yang_enum_int_value(cxobj *node,
if (yang_type_resolve(ys, ys, ytype, &yrestype,
NULL, NULL, NULL, NULL, NULL) < 0)
goto done;
if (yrestype==NULL || strcmp(yrestype->ys_argument, "enumeration"))
if (yrestype==NULL || strcmp(yang_argument_get(yrestype), "enumeration"))
goto done;
if ((yenum = yang_find(yrestype, Y_ENUM, xml_body(node))) == NULL)
goto done;
@ -1487,7 +1487,7 @@ yang_enum_int_value(cxobj *node,
if ((yval = yang_find(yenum, Y_VALUE, NULL)) == NULL)
goto done;
/* reason is string containing why int could not be parsed */
if (parse_int32(yval->ys_argument, val, &reason) < 0)
if (parse_int32(yang_argument_get(yval), val, &reason) < 0)
goto done;
retval = 0;
done:

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -51,7 +52,7 @@ struct xml_parse_yacc_arg{
cxobj *ya_xparent; /* xml parent element*/
int ya_skipspace; /* If set, remove all non-terminal bodies (strip pretty-print) */
yang_stmt *ya_yspec; /* If set, top-level yang-spec */
int ya_lex_state; /* lex start condition (AMPERSAND) */
int ya_lex_state; /* lex return state */
};
extern char *clixon_xml_parsetext;

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -31,7 +32,7 @@
***** END LICENSE BLOCK *****
* XML search functions when used with YANG
* XML sort and search functions when used with YANG
*/
#ifdef HAVE_CONFIG_H
@ -65,6 +66,7 @@
#include "clixon_options.h"
#include "clixon_xml_map.h"
#include "clixon_yang_type.h"
#include "clixon_yang_module.h"
#include "clixon_xml_sort.h"
/*! Get xml body value as cligen variable
@ -193,10 +195,10 @@ xml_child_spec(cxobj *x,
* @param[in] x2 object 2
* @param[in] same If set, x1 and x2 are member of same parent & enumeration
* is used (see explanation below)
* @param[in] skip1 Key matching skipped for keys not in x1 (see explanation)
* @retval 0 If equal
* @retval <0 If x1 is less than x2
* @retval >0 If x1 is greater than x2
* @see xml_cmp1 Similar, but for one object
*
* There are distinct calls for this function:
* 1. For sorting in an existing list of XML children
@ -205,11 +207,19 @@ xml_child_spec(cxobj *x,
* if they have the same yang-spec, the existing order is used as tie-breaker.
* In other words, if order-by-system, or if the case (2) above, the existing
* order is ignored and the actual xml element contents is examined.
*
* Also, if pattern is set, list matching is not made so that x1 and x2 must have same keys.
* instead, x1 and x2 are considered equal if the keys that x1 have match. Keys that x2 but not
* x1 has are ignored.
* Example: x1: <x><k1>1</k1></x> and x2: <x><k1>1</k1><k2>2</k2></x> are considered equal.
* This is useful in searching for indexes in trees, where x1 is a search index and not a
* complete tree.
*
* @note empty value/NULL is smallest value
* @note some error cases return as -1 (qsort cant handle errors)
* @note some error cases return as -1 (qsort cant handle errors)
*
* NOTE: "comparing" x1 and x2 here judges equality from a structural (model)
* @note "comparing" x1 and x2 here judges equality from a structural (model)
* perspective, ie both have the same yang spec, if they are lists, they have the
* the same keys. NOT that the values are equal!
* In other words, if x is a leaf with the same yang spec, <x>1</x> and <x>2</x> are
@ -221,7 +231,8 @@ xml_child_spec(cxobj *x,
int
xml_cmp(cxobj *x1,
cxobj *x2,
int same)
int same,
int skip1)
{
yang_stmt *y1;
yang_stmt *y2;
@ -281,9 +292,13 @@ xml_cmp(cxobj *x1,
}
switch (yang_keyword_get(y1)){
case Y_LEAF_LIST: /* Match with name and value */
if ((b1 = xml_body(x1)) == NULL)
b1 = xml_body(x1);
b2 = xml_body(x2);
if (b1 == NULL && b2 == NULL)
;
else if (b1 == NULL)
equal = -1;
else if ((b2 = xml_body(x2)) == NULL)
else if (b2 == NULL)
equal = 1;
else{
if (xml_cv_cache(x1, &cv1) < 0) /* error case */
@ -301,33 +316,47 @@ xml_cmp(cxobj *x1,
}
break;
case Y_LIST: /* Match with key values
* Use Y_LIST cache (see struct yang_stmt)
*/
* Use Y_LIST cache (see struct yang_stmt) */
cvk = yang_cvec_get(y1); /* Use Y_LIST cache, see ys_populate_list() */
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi); /* operational data may have NULL keys*/
if ((x1b = xml_find(x1, keyname)) == NULL ||
xml_body(x1b) == NULL)
x1b = xml_find(x1, keyname);
/* match1: key matching skipped for keys not in x1 (see explanation) */
if (skip1 && x1b == NULL)
continue;
x2b = xml_find(x2, keyname);
if (x1b == NULL && x2b == NULL)
;
else if (x1b == NULL)
equal = -1;
else if ((x2b = xml_find(x2, keyname)) == NULL ||
xml_body(x2b) == NULL)
else if (x2b == NULL)
equal = 1;
else{
if (xml_cv_cache(x1b, &cv1) < 0) /* error case */
goto done;
if (xml_cv_cache(x2b, &cv2) < 0) /* error case */
goto done;
assert(cv1 && cv2);
if ((equal = cv_cmp(cv1, cv2)) != 0)
goto done;
b1 = xml_body(x1b);
b2 = xml_body(x2b);
if (b1 == NULL && b2 == NULL)
;
else if (b1 == NULL)
equal = -1;
else if (b2 == NULL)
equal = 1;
else{
if (xml_cv_cache(x1b, &cv1) < 0) /* error case */
goto done;
if (xml_cv_cache(x2b, &cv2) < 0) /* error case */
goto done;
assert(cv1 && cv2);
equal = cv_cmp(cv1, cv2);
}
}
}
equal = 0;
if (equal)
break;
} /* while cvi */
break;
default:
break;
}
} /* switch */
done:
clicon_debug(2, "%s %s %s %d nr: %d %d yi: %d %d", __FUNCTION__, xml_name(x1), xml_name(x2), equal, nr1, nr2, yi1, yi2);
return equal;
@ -340,7 +369,7 @@ static int
xml_cmp_qsort(const void* arg1,
const void* arg2)
{
return xml_cmp(*(struct xml**)arg1, *(struct xml**)arg2, 1);
return xml_cmp(*(struct xml**)arg1, *(struct xml**)arg2, 1, 0);
}
/*! Sort children of an XML node
@ -366,21 +395,28 @@ xml_sort(cxobj *x,
return 0;
}
/*! Special case search for ordered-by user where linear sort is used
/*! Special case search for ordered-by user or state data where linear sort is used
*
* @param[in] xp Parent XML node (go through its childre)
* @param[in] x1 XML node to match
* @param[in] yangi Yang order number (according to spec)
* @param[in] mid Where to start from (may be in middle of interval)
* @param[out] xretp XML return object, or NULL
* @retval 0 OK, see xretp
* @param[out] xvec Vector of matching XML return objects (can be empty)
* @param[out] xlen Length of xvec
* @retval 0 OK, see xvec (may be empty)
* @retval -1 Error
* XXX: only first match
*/
static int
xml_search_userorder(cxobj *xp,
cxobj *x1,
int yangi,
int mid,
cxobj **xretp)
xml_find_keys_notsorted(cxobj *xp,
cxobj *x1,
int yangi,
int mid,
int skip1,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
int i;
cxobj *xc;
yang_stmt *yc;
@ -388,113 +424,186 @@ xml_search_userorder(cxobj *xp,
for (i=mid+1; i<xml_child_nr(xp); i++){ /* First increment */
xc = xml_child_i(xp, i);
yc = xml_spec(xc);
if (yangi!=yang_order(yc))
if (yangi != yang_order(yc)) /* wrong yang */
break;
if (xml_cmp(xc, x1, 0) == 0){
*xretp = xc;
goto done;
if (xml_cmp(xc, x1, 0, skip1) == 0){
if (cxvec_append(xc, xvec, xlen) < 0)
goto done;
goto ok; /* found */
}
}
for (i=mid-1; i>=0; i--){ /* Then decrement */
xc = xml_child_i(xp, i);
yc = xml_spec(xc);
if (yangi!=yang_order(yc))
if (yangi != yang_order(yc)) /* wrong yang */
break;
if (xml_cmp(xc, x1, 0) == 0){
*xretp = xc;
goto done;
if (xml_cmp(xc, x1, 0, skip1) == 0){
if (cxvec_append(xc, xvec, xlen) < 0)
goto done;
goto ok; /* found */
}
}
*xretp = NULL;
ok:
retval = 0;
done:
return 0;
return retval;
}
/*! Find more equal objects in a vector up and down in the array of the present
* @param[in] xp Parent XML node (go through its childre)
* @param[in] x1 XML node to match
* @param[in] yangi Yang order number (according to spec)
* @param[in] mid Where to start from (may be in middle of interval)
* @param[out] xvec Vector of matching XML return objects (can be empty)
* @param[out] xlen Length of xvec
* @retval 0 OK, see xvec (may be empty)
* @retval -1 Error
*/
static int
more_equals(cxobj *xp,
cxobj *x1,
int yangi,
int mid,
int skip1,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
int i;
cxobj *xc;
yang_stmt *yc;
for (i=mid-1; i>=0; i--){ /* First decrement */
xc = xml_child_i(xp, i);
yc = xml_spec(xc);
if (yangi != yang_order(yc)) /* wrong yang */
break;
if (xml_cmp(x1, xc, 0, skip1) != 0)
break;
if (cxvec_prepend(xc, xvec, xlen) < 0)
goto done;
}
for (i=mid+1; i<xml_child_nr(xp); i++){ /* Then increment */
xc = xml_child_i(xp, i);
yc = xml_spec(xc);
if (yangi != yang_order(yc)) /* wrong yang */
break;
if (xml_cmp(x1, xc, 0, skip1) != 0)
break;
if (cxvec_append(xc, xvec, xlen) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Find XML child under xp matching x1 using binary search
* @param[in] xp Parent xml node.
* @param[in] x1 Find this object among xp:s children
* @param[in] userorder If x1 is ordered by user
* @param[in] sorted If x1 is ordered by user or statedata, the list is not sorted
* @param[in] yangi Yang order
* @param[in] low Lower bound of childvec search interval
* @param[in] upper Lower bound of childvec search interval
* @param[out] xretp XML return object, or NULL
* @retval 0 OK, see xretp
* @param[in] skip1 Key matching skipped for keys not in x1
* @param[out] xvec Vector of matching XML return objects (can be empty)
* @param[out] xlen Length of xvec
* @retval 0 OK, see xvec (may be empty)
* @retval -1 Error
*/
static int
xml_search1(cxobj *xp,
cxobj *x1,
int userorder,
int yangi,
int low,
int upper,
cxobj **xretp)
xml_find_keys1(cxobj *xp,
cxobj *x1,
int sorted,
int yangi,
int low,
int upper,
int skip1,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
int mid;
int cmp;
cxobj *xc;
yang_stmt *y;
if (upper < low)
goto notfound;
goto ok;
mid = (low + upper) / 2;
if (mid >= xml_child_nr(xp)) /* beyond range */
goto notfound;
goto ok;
xc = xml_child_i(xp, mid);
if ((y = xml_spec(xc)) == NULL)
goto notfound;
goto ok;
cmp = yangi-yang_order(y);
/* Here is right yang order == same yang? */
if (cmp == 0){
cmp = xml_cmp(x1, xc, 0);
if (cmp && userorder){ /* Ordered by user (if not equal) */
xml_search_userorder(xp, x1, yangi, mid, xretp);
cmp = xml_cmp(x1, xc, 0, skip1);
if (cmp && !sorted){ /* Ordered by user (if not equal) */
retval = xml_find_keys_notsorted(xp, x1, yangi, mid, skip1, xvec, xlen);
goto done;
}
}
if (cmp == 0)
*xretp = xc;
if (cmp == 0){
if (cxvec_append(xc, xvec, xlen) < 0)
goto done;
/* there may be more? */
if (more_equals(xp, x1, yangi, mid, skip1, xvec, xlen) < 0)
goto done;
}
else if (cmp < 0)
xml_search1(xp, x1, userorder, yangi, low, mid-1, xretp);
xml_find_keys1(xp, x1, sorted, yangi, low, mid-1, skip1, xvec, xlen);
else
xml_search1(xp, x1, userorder, yangi, mid+1, upper, xretp);
xml_find_keys1(xp, x1, sorted, yangi, mid+1, upper, skip1, xvec, xlen);
ok:
retval = 0;
done:
return 0;
notfound:
*xretp = NULL;
goto done;
return retval;
}
/*! Find XML child under xp matching x1 using binary search
/*! Find XML child under xp matching x1 using binary search for list/leaf-list keys
*
* Match is tried xp with x1 with either name only (container/leaf) or using keys (list/leaf-lists)
* Any non-key leafs or other structure of x1 is not matched.
*
* @param[in] xp Parent xml node.
* @param[in] x1 Find this object among xp:s children
* @param[in] yc Yang spec of x1
* @param[out] xretp XML return object, or NULL
* @retval 0 OK, see xretp
* @param[in] skip1 Key matching skipped for keys not in x1
* @param[out] xvec Vector of matching XML return objects (can be empty)
* @param[out] xlen Length of xvec
* @retval 0 OK, see xvec (may be empty)
* @retval -1 Error
* @see xml_find_index for a generic search function
*/
static int
xml_search(cxobj *xp,
cxobj *x1,
yang_stmt *yc,
cxobj **xretp)
xml_find_keys(cxobj *xp,
cxobj *x1,
yang_stmt *yc,
int skip1,
cxobj ***xvec,
size_t *xlen)
{
cxobj *xa;
int low = 0;
int upper = xml_child_nr(xp);
int userorder=0;
int yangi;
cxobj *xa;
int low = 0;
int upper = xml_child_nr(xp);
int sorted = 1;
int yangi;
/* Assume if there are any attributes, they are first in the list, mask
them by raising low to skip them */
for (low=0; low<upper; low++)
if ((xa = xml_child_i(xp, low)) == NULL || xml_type(xa)!=CX_ATTR)
if ((xa = xml_child_i(xp, low)) == NULL || xml_type(xa) != CX_ATTR)
break;
/* Find if non-config and if ordered-by-user */
if (yang_config(yc)==0)
userorder = 1;
sorted = 0;
else if (yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST)
userorder = (yang_find(yc, Y_ORDERED_BY, "user") != NULL);
sorted = (yang_find(yc, Y_ORDERED_BY, "user") == NULL);
yangi = yang_order(yc);
return xml_search1(xp, x1, userorder, yangi, low, upper, xretp);
return xml_find_keys1(xp, x1, sorted, yangi, low, upper, skip1, xvec, xlen);
}
/*! Insert xn in xp:s sorted child list (special case of ordered-by user)
@ -652,7 +761,7 @@ xml_insert2(cxobj *xp,
goto done;
}
else /* Ordered by system */
cmp = xml_cmp(xn, xc, 0);
cmp = xml_cmp(xn, xc, 0, 0);
}
else{ /* Not equal yang - compute diff */
cmp = yni - yang_order(yc);
@ -769,7 +878,7 @@ xml_sort_verify(cxobj *x0,
xml_enumerate_children(x0);
while ((x = xml_child_each(x0, x, -1)) != NULL) {
if (xprev != NULL){ /* Check xprev <= x */
if (xml_cmp(xprev, x, 1) > 0)
if (xml_cmp(xprev, x, 1, 0) > 0)
goto done;
}
xprev = x;
@ -779,13 +888,14 @@ xml_sort_verify(cxobj *x0,
return retval;
}
/*! Given child tree x1c, find matching child in base tree x0 and return as x0cp
/*! Given child tree x1c, find (first) matching child in base tree x0 and return as x0cp
* @param[in] x0 Base tree node
* @param[in] x1c Modification tree child
* @param[in] yc Yang spec of tree child
* @param[out] x0cp Matching base tree child (if any)
* @retval 0 OK
* @retval -1 Error
* XXX: only handles first match
*/
int
match_base_child(cxobj *x0,
@ -802,6 +912,8 @@ match_base_child(cxobj *x0,
yang_stmt *y0c;
yang_stmt *y0p;
yang_stmt *yp; /* yang parent */
cxobj **xvec = NULL;
size_t xlen = 0;
*x0cp = NULL; /* init return value */
/* Special case is if yc parent (yp) is choice/case
@ -816,6 +928,7 @@ match_base_child(cxobj *x0,
y0p == yp)
break; /* x0c will have a value */
}
*x0cp = x0c;
goto ok; /* What to do if not found? */
}
switch (yang_keyword_get(yc)){
@ -823,58 +936,146 @@ match_base_child(cxobj *x0,
case Y_LEAF: /* Equal regardless */
break;
case Y_LEAF_LIST: /* Match with name and value */
if (xml_body(x1c) == NULL){ /* Treat as empty string */
// assert(0);
if (xml_body(x1c) == NULL) /* Treat as empty string */
goto ok;
}
break;
case Y_LIST: /* Match with key values */
cvk = yang_cvec_get(yc); /* Use Y_LIST cache, see ys_populate_list() */
/* Count number of key indexes
* Then create two vectors one with names and one with values of x1c,
* ec: keyvec: [a,b,c] keyval: [1,2,3]
*/
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
// keyvec[i] = keyname;
if ((xb = xml_find(x1c, keyname)) == NULL){
if ((xb = xml_find(x1c, keyname)) == NULL)
goto ok;
}
}
default:
break;
}
/* Get match. */
xml_search(x0, x1c, yc, &x0c);
if (xml_find_keys(x0, x1c, yc, 0, &xvec, &xlen) < 0)
goto done;
if (xlen)
*x0cp = xvec[0];
ok:
*x0cp = x0c;
retval = 0;
done:
if (xvec)
free(xvec);
return retval;
}
/*! Experimental API for binary search
*
* Create a temporary search object: a list (xc) with a key (xk) and call the binary search.
* @param[in] xp Parent xml node.
* @param[in] yc Yang spec of list child
* @param[in] cvk List of keys and values as CLIgen vector
* @param[out] xret Found XML object, NULL if not founs
* @retval 0 OK, see xret
* @code
* cvec *cvk = NULL; vector of index keys
* ... Populate cvk with key/values eg a:5 b:6
* if (xml_binsearch(xp, yc, cvk, xp) < 0)
* err;
* @endcode
* @retval -1 Error
* Can extend to leaf-list?
/*! API for search in XML child list with non-indexed variables
*/
int
xml_binsearch(cxobj *xp,
yang_stmt *yc,
cvec *cvk,
cxobj **xretp)
static int
xml_find_noyang_cvk(char *ns0,
cxobj *xc,
cvec *cvk,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
cg_var *cvi;
char *ns;
cxobj *xcc;
char *keyname;
char *keyval;
char *body;
cvi = NULL;
/* Loop through index variables. xc should match all, on exit if cvi=NULL it macthes */
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_name_get(cvi);
keyval = cv_string_get(cvi);
if (keyname==NULL || strcmp(keyname, ".")==0){ /* Index variable on form .=<val> (self) */
body = xml_body(xc);
if ((body==NULL && (keyval==NULL || strlen(keyval) == 0)) || /* both null, break */
(body != NULL && strcmp(body, keyval) == 0)) /* Name match, break */
continue; /* match */
break; /* nomatch */
}
else{
/* Index variable on form <id>=<val>
* Loop through children of the matched x (to match keyname and value) */
xcc = NULL;
while ((xcc = xml_child_each(xc, xcc, CX_ELMNT)) != NULL) {
if (xml2ns(xcc, xml_prefix(xcc), &ns) < 0)
goto done;
if (strcmp(ns0, ns) != 0) /* Namespace does not match, skip */
continue;
if (strcmp(keyname, xml_name(xcc)) != 0) /* Name does not match, skip */
continue;
body = xml_body(xcc);
if (body==NULL && (keyval==NULL || strlen(keyval) == 0)) /* both null, break */
break;
if (body != NULL && strcmp(body, keyval) == 0) /* Name match, break */
break;
}
if (xcc == NULL)
break; /* No match found */
}
}
if (cvi == NULL) /* means we iterated through all indexes without breaks */
if (cxvec_append(xc, xvec, xlen) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! API for search in XML child list with no yang available
* Fallback if no yang available. Only linear search for matching name, and eventual index match
*/
static int
xml_find_noyang_name(cxobj *xp,
char *ns0,
char *name,
cvec *cvk,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
cxobj *xc;
char *ns;
if (name == NULL || ns0 == NULL){
clicon_err(OE_XML, EINVAL, "name and namespace required");
goto done;
}
/* Go through children linearly */
xc = NULL;
while ((xc = xml_child_each(xp, xc, CX_ELMNT)) != NULL) {
ns = NULL;
if (xml2ns(xc, xml_prefix(xc), &ns) < 0)
goto done;
if (ns == NULL)
continue;
if (strcmp(ns0, ns) != 0) /* Namespace does not match, skip */
continue;
if (strcmp(name, xml_name(xc)) != 0) /* Namespace does not match, skip */
continue;
if (cvk){ /* Check indexes */
if (xml_find_noyang_cvk(ns0, xc, cvk, xvec, xlen) < 0)
goto done;
}
else /* No index variables: just add node to found list */
if (cxvec_append(xc, xvec, xlen) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! API for search in XML child list with yang available
* @retval 1 OK
* @retval 0 Revert, try again with no-yang search
* @retval -1 Error
*/
static int
xml_find_index_yang(cxobj *xp,
yang_stmt *yc,
cvec *cvk,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
cxobj *xc = NULL;
@ -882,30 +1083,58 @@ xml_binsearch(cxobj *xp,
cg_var *cvi = NULL;
cbuf *cb = NULL;
yang_stmt *yk;
char *kname;
cvec *ycvk;
cg_var *ycv = NULL;
int i;
char *name;
if (yc == NULL || xml_spec(xp) == NULL){
clicon_err(OE_YANG, ENOENT, "yang spec not found");
goto done;
}
name = yang_argument_get(yc);
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cb, "<%s>", name);
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
cprintf(cb, "<%s>%s</%s>",
cv_name_get(cvi),
cv_string_get(cvi),
cv_name_get(cvi));
switch (yang_keyword_get(yc)){
case Y_LIST:
cprintf(cb, "<%s>", name);
ycvk = yang_cvec_get(yc); /* Check that those are proper index keys */
cvi = NULL;
i = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
if ((kname = cv_name_get(cvi)) == NULL){
clicon_err(OE_YANG, ENOENT, "missing yang key name in cvk");
goto done;
}
/* Parameter in cvk is not key or not in right key order, then we cannot call
* xml_find_keys and we need to revert to noyang
*/
if ((ycv = cvec_i(ycvk, i)) == NULL || strcmp(kname, cv_string_get(ycv))){
goto revert;
break;
}
cprintf(cb, "<%s>%s</%s>", kname, cv_string_get(cvi), kname);
i++;
}
cprintf(cb, "</%s>", name);
break;
case Y_LEAF_LIST:
if (cvec_len(cvk) != 1){
clicon_err(OE_YANG, ENOENT, "expected exactly one leaf-list key");
goto done;
}
cvi = cvec_i(cvk, 0);
cprintf(cb, "<%s>%s</%s>", name, cv_string_get(cvi), name);
break;
default:
cprintf(cb, "<%s/>", name);
break;
}
cprintf(cb, "</%s>", name);
clicon_debug(1, "%s XML:%s", __FUNCTION__, cbuf_get(cb));
if (xml_parse_string(cbuf_get(cb), yc, &xc) < 0)
goto done;
if (xml_rootchild(xc, 0, &xc) < 0)
goto done;
/* Populate created XML tree with yang specs */
if (xml_spec_set(xc, yc) < 0)
goto done;
xk = NULL;
@ -917,11 +1146,150 @@ xml_binsearch(cxobj *xp,
if (xml_spec_set(xk, yk) < 0)
goto done;
}
retval = xml_search(xp, xc, yc, xretp);
if (xml_find_keys(xp, xc, yc, 1, xvec, xlen) < 0)
goto done;
retval = 1; /* OK */
done:
if (cb)
cbuf_free(cb);
if (xc)
xml_free(xc);
return retval;
revert: /* means try name-only*/
retval = 0;
goto done;
}
/*! API for search in XML child list using indexes and binary search if applicable
*
* Generic search function as alternative to xml_find() and others that finds YANG associated
* children of an XML tree node using indexes and optimized lookup. Optimized lookup is used only
* if an associated YANG exists. The optimized lookup is based on binary trees but is used
* The function takes as input:
* 1) An XML tree (parent) node (cxobj). See note(1) below
* 2) A set of parameters to define wanted children by name or yang-spec using yc, name, ns, yp:
* a. yp and ns:name given: YANG spec of parent + ns:name derives yc. Not found: only ns:name
* b. name given: yp is derived from xp spec, then goto 2b above.
* 3) A vector of index expressions on the form <id>=<val> refining the child set:
* a. cvk is NULL, return all children matching according to (B)
* b. cvk contains <id>=<val> entries, return children with <id> leafs matching <val>.
* See "Optimized" section below.
* 4) A return vector of XML nodes matching name and index variables
*
* # Optimized
* The function makes the index matching <id>=<val> above optimized using binary search if:
* - yc is defined AND one of the following
* - if xp is leaf-list and "id" is "."
* - if xp is a yang list and "id" is a registered index key
* - if xp is a yang list and first "id" is first leaf key, second "id" is second leaf key, etc.
* - Otherwise search is made using linear search
*
* @param[in] xp Parent xml node.
* @param[in] yc Yang spec of list child (preferred) See rule (2) above
* @param[in] yp Yang spec of parent node or yang-spec/yang (Alternative if yc not given).
* @param[in] ns Namespace (needed only if name is derived from top-symbol)
* @param[in] name Name of child (not required if yc given)
* @param[in] cvk List of keys and values as CLIgen vector on the form k1=foo, k2=bar
* @param[out] xvec Array of found nodes
* @param[out] xlen Len of xvec
* @retval 0 OK, see xret
* @retval -1 Error
* @code
* cxobj **xvec = NULL;
* size_t xlen = 0;
* cvec *cvk = NULL; vector of index keys
* ... Populate cvk with key/values eg a:5 b:6
* if (clixon_xml_find_index(xp, yp, "a", ns, cvk, &xvec, &xlen) < 0)
* err;
* @endcode
* Discussion:
* (1) Rule 2 on how to get the child name election seems unecessary complex. First, it would be
* nice to just state name and parent 2c. But xp as top-objects may sometimes be dummies, for
* parsing, copy-buffers, or simply for XML nodes that do not have YANG.
* (2) Short form could be: "first" only returning first object to get rid of vectors.
* (3) Another short form could be: keyname,keyval instead of cvk for single keys.
*/
int
clixon_xml_find_index(cxobj *xp,
yang_stmt *yp,
char *namespace,
char *name,
cvec *cvk,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
int ret;
yang_stmt *yc = NULL;
if (name == NULL){
clicon_err(OE_YANG, ENOENT, "name");
goto done;
}
if (yp == NULL)
yp = xml_spec(xp);
if (yp){/* 2. YANG spec of parent + name derives yc. If not found use just name. */
if (yang_keyword_get(yp) == Y_SPEC)
yp = yang_find_module_by_namespace(yp, namespace);
if (yp)
yc = yang_find_datanode(yp, name);
}
if (yc){
if ((ret = xml_find_index_yang(xp, yc, cvk, xvec, xlen)) < 0)
goto done;
if (ret == 0)
if (xml_find_noyang_name(xp, namespace, name, cvk, xvec, xlen) < 0)
goto done;
}
else
if (xml_find_noyang_name(xp, namespace, name, cvk, xvec, xlen) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Find positional parameter in xml child list, eg x/y[42]. Note, not optimized
*
* Create a temporary search object: a list (xc) with a key (xk) and call the binary search.
* @param[in] xp Parent xml node.
* @param[in] yc Yang spec of list child
* @param[in] pos Position
* @param[out] xvec Array of found nodes
* @param[out] xlen Len of xvec
* @retval 0 OK, see xret
* @retval -1 Error
*/
int
clixon_xml_find_pos(cxobj *xp,
yang_stmt *yc,
uint32_t pos,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
cxobj *xc = NULL;
char *name;
uint32_t u;
if (yc == NULL){
clicon_err(OE_YANG, ENOENT, "yang spec not found");
goto done;
}
name = yang_argument_get(yc);
u = 0;
xc = NULL;
while ((xc = xml_child_each(xp, xc, CX_ELMNT)) != NULL) {
if (strcmp(name, xml_name(xc)))
continue;
if (pos == u++){ /* Found */
if (cxvec_append(xc, xvec, xlen) < 0)
goto done;
break;
}
}
retval = 0;
done:
return retval;
}

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -83,6 +83,7 @@
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_nsctx.h"
#include "clixon_yang_module.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xpath_parse.h"
@ -726,12 +727,12 @@ xpath_vec(cxobj *xcur,
va_start(ap, veclen);
len = vsnprintf(NULL, 0, xpformat, ap);
va_end(ap);
/* allocate a message string exactly fitting the message length */
/* allocate an xpath string exactly fitting the length */
if ((xpath = malloc(len+1)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
/* second round: compute write message from reason and args */
/* second round: actually compute xpath string content */
va_start(ap, veclen);
if (vsnprintf(xpath, len+1, xpformat, ap) < 0){
clicon_err(OE_UNIX, errno, "vsnprintf");

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -117,8 +117,6 @@ ctx_dup(xp_ctx *xc0)
* @param[in] xc XPATH evaluation context
* @param[in] ind Indentation margin
* @param[in] str Prefix string in printout
*/
int
ctx_print_cb(cbuf *cb,

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -339,25 +339,17 @@ xp_eval_step(xp_ctx *xc0,
else for (i=0; i<xc->xc_size; i++){
xv = xc->xc_nodeset[i];
x = NULL;
if ((ret = xpath_optimize_check(xs, xv, &x)) < 0)
if ((ret = xpath_optimize_check(xs, xv, &vec, &veclen)) < 0)
goto done;
switch (ret){
case 1: /* optimized */
if (x) /* keep only x */
if (cxvec_append(x, &vec, &veclen) < 0)
goto done;
break;
case 0: /* regular code */
if (ret == 0){ /* No optimization made */
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
/* xs->xs_c0 is nodetest */
if (nodetest == NULL || nodetest_eval(x, nodetest, nsc, localonly) == 1){
if (cxvec_append(x, &vec, &veclen) < 0)
goto done;
}
if (ret == 0){/* regular code, no optimization made */
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
/* xs->xs_c0 is nodetest */
if (nodetest == NULL || nodetest_eval(x, nodetest, nsc, localonly) == 1){
if (cxvec_append(x, &vec, &veclen) < 0)
goto done;
}
}
} /* switch */
}
}
}
ctx_nodeset_replace(xc, vec, veclen);
@ -932,7 +924,7 @@ xp_eval(xp_ctx *xc,
xp_ctx *xr2 = NULL;
int use_xr0 = 0; /* In 2nd child use transitively result of 1st child */
if (debug)
if (debug > 1)
ctx_print(stderr, xc, xpath_tree_int2str(xs->xs_type));
/* Pre-actions before check first child c0
*/
@ -1088,7 +1080,7 @@ xp_eval(xp_ctx *xc,
xr0 = NULL;
}
ok:
if (debug)
if (debug>1)
ctx_print(stderr, *xrp, xpath_tree_int2str(xs->xs_type));
retval = 0;
done:

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -68,7 +68,6 @@
#include "clixon_xpath.h"
#include "clixon_xpath_optimize.h"
#ifdef XPATH_LIST_OPTIMIZE
static xpath_tree *_xmtop = NULL; /* pattern match tree top */
static xpath_tree *_xm = NULL;
@ -77,7 +76,6 @@ static int _optimize_enable = 1;
static int _optimize_hits = 0;
#endif /* XPATH_LIST_OPTIMIZE */
/* XXX development in clixon_xpath_eval */
int
xpath_list_optimize_stats(int *hits)
@ -89,6 +87,9 @@ xpath_list_optimize_stats(int *hits)
return 0;
}
/*! Enable xpath optimize
* Cant replace this with optin since there is no handle in xpath functions,...
*/
int
xpath_list_optimize_set(int enable)
{
@ -213,7 +214,8 @@ loop_preds(xpath_tree *xt,
*
* @param[in] xt XPath tree
* @param[in] xv XML base node
* @param[out] xp XML found node (retval == 1)
* @param[out] xvec Array of found nodes
* @param[out] xlen Len of xvec
* @param[out] key
* @param[out] keyval
* @retval -1 Error
@ -225,7 +227,8 @@ loop_preds(xpath_tree *xt,
static int
xpath_list_optimize_fn(xpath_tree *xt,
cxobj *xv,
cxobj **xp)
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
xpath_tree *xm = NULL;
@ -262,9 +265,9 @@ xpath_list_optimize_fn(xpath_tree *xt,
if (veclen != 2)
goto ok;
name = vec[0]->xs_s1;
/* Extract variables XXX strdups */
/* Extract variables */
if ((yc = yang_find(yp, Y_LIST, name)) == NULL)
#ifdef NOTYET
#ifdef NOTYET /* leaf-list is not detected by xpath optimize detection */
if ((yc = yang_find(yp, Y_LEAF_LIST, name)) == NULL) /* XXX */
#endif
goto ok;
@ -286,14 +289,12 @@ xpath_list_optimize_fn(xpath_tree *xt,
i = 0;
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
if (strcmp(cv_name_get(cvi), cv_string_get(cvec_i(cvv,i)))){
fprintf(stderr, "%s key name mismatch %s vs %s\n",
__FUNCTION__, cv_name_get(cvi), cv_string_get(cvec_i(cvv,i)));
if (strcmp(cv_name_get(cvi), cv_string_get(cvec_i(cvv,i))))
goto ok;
}
i++;
}
if (xml_binsearch(xv, yc, cvk, xp) < 0)
/* Use 2a form since yc allready given to compute cvk */
if (clixon_xml_find_index(xv, yp, NULL, name, cvk, xvec, xlen) < 0)
goto done;
retval = 1; /* match */
done:
@ -316,8 +317,9 @@ xpath_list_optimize_fn(xpath_tree *xt,
*/
int
xpath_optimize_check(xpath_tree *xs,
cxobj *xv,
cxobj **xp)
cxobj *xv,
cxobj ***xvec,
size_t *xlen)
{
#ifdef XPATH_LIST_OPTIMIZE
@ -325,7 +327,7 @@ xpath_optimize_check(xpath_tree *xs,
if (!_optimize_enable)
return 0; /* use regular code */
if ((ret = xpath_list_optimize_fn(xs, xv, xp)) < 0)
if ((ret = xpath_list_optimize_fn(xs, xv, xvec, xlen)) < 0)
return -1;
if (ret == 1){
_optimize_hits++;

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -39,7 +39,7 @@
/*
* Types
*/
struct clicon_xpath_yacc_arg{ /* XXX: mostly unrelevant */
struct clicon_xpath_yacc_arg{
const char *xy_name; /* Name of syntax (for error string) */
int xy_linenum; /* Number of \n in parsed buffer */
char *xy_parse_string; /* original (copy of) parse string */
@ -55,11 +55,11 @@ extern char *clixon_xpath_parsetext;
/*
* Prototypes
*/
int xpath_scan_init(struct clicon_xpath_yacc_arg *jy);
int xpath_scan_exit(struct clicon_xpath_yacc_arg *jy);
int xpath_scan_init(struct clicon_xpath_yacc_arg *xy);
int xpath_scan_exit(struct clicon_xpath_yacc_arg *xy);
int xpath_parse_init(struct clicon_xpath_yacc_arg *jy);
int xpath_parse_exit(struct clicon_xpath_yacc_arg *jy);
int xpath_parse_init(struct clicon_xpath_yacc_arg *xy);
int xpath_parse_exit(struct clicon_xpath_yacc_arg *xy);
int clixon_xpath_parselex(void *);
int clixon_xpath_parseparse(void *);

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -77,8 +77,6 @@ clixon_xpath_parsewrap(void)
return 1;
}
%}
digit [0-9]

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -124,7 +124,7 @@
#include "clixon_xpath_parse.h"
extern int clixon_xpath_parseget_lineno (void);
extern int clixon_xpath_parseget_lineno (void); /*XXX obsolete ? */
/*
also called from yacc generated code *

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -63,7 +63,6 @@
#include "clixon_handle.h"
#include "clixon_err.h"
#include "clixon_yang.h"
#include "clixon_yang_internal.h" /* internal */
#include "clixon_yang_cardinality.h"
/*
@ -545,8 +544,8 @@ yang_cardinality(clicon_handle h,
/* 4) Recurse */
i = 0;
while (i<yt->ys_len){ /* Note, children may be removed cant use yn_each */
ys = yt->ys_stmt[i++];
while (i< yang_len_get(yt)){ /* Note, children may be removed cant use yn_each */
ys = yang_child_i(yt, i++);
if (yang_cardinality(h, ys, modname) < 0)
goto done;
}

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -31,24 +31,15 @@
***** END LICENSE BLOCK *****
* Yang functions
* @see https://tools.ietf.org/html/rfc6020 YANG 1.0
* @see https://tools.ietf.org/html/rfc7950 YANG 1.1
*/
*
* This file defines the internal YANG data structures used by the Clixon implementation
* This file SHOULD ONLY be included by clixon_yang.h.
* Accesses should be made via the API defined in clixon_yang.h
*/
#ifndef _CLIXON_YANG_INTERNAL_H_
#define _CLIXON_YANG_INTERNAL_H_
/*
* Clixon-specific cligen variable (cv) flags
* CLIgen flags defined are in the range 0x01 -0x0f
* An application can use any flags above that
* @see cv_flag
*/
#define V_UNSET 0x10 /* Used by XML code to denote a value is not default */
#define YANG_FLAG_MARK 0x01 /* Marker for dynamic algorithms, eg expand */
/*! Yang type cache. Yang type statements can cache all typedef info here
* @note unions not cached
*/
@ -76,7 +67,7 @@ struct yang_stmt{
enum rfc_6020 ys_keyword; /* See clicon_yang_parse.tab.h */
char *ys_argument; /* String / argument depending on keyword */
int ys_flags; /* Flags according to YANG_FLAG_* above */
uint16_t ys_flags; /* Flags according to YANG_FLAG_* */
yang_stmt *ys_mymodule; /* Shortcut to "my" module. Augmented
nodes can belong to other
modules than the ancestor module */
@ -116,3 +107,4 @@ struct yang_stmt{
#define yang_schemanode(y) (yang_datanode(y) || (y)->ys_keyword == Y_RPC || (y)->ys_keyword == Y_CHOICE || (y)->ys_keyword == Y_CASE || (y)->ys_keyword == Y_INPUT || (y)->ys_keyword == Y_OUTPUT || (y)->ys_keyword == Y_NOTIFICATION)
#endif /* _CLIXON_YANG_INTERNAL_H_ */

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -77,8 +77,8 @@
#include "clixon_plugin.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_map.h"
#include "clixon_yang_parse_lib.h"
#include "clixon_yang_module.h"
#include "clixon_yang_internal.h" /* internal */
modstate_diff_t *
modstate_diff_new(void)
@ -155,7 +155,7 @@ yang_modules_revision(clicon_handle h)
if ((ymod = yang_find(yspec, Y_MODULE, "ietf-yang-library")) != NULL ||
(ymod = yang_find(yspec, Y_SUBMODULE, "ietf-yang-library")) != NULL){
if ((yrev = yang_find(ymod, Y_REVISION, NULL)) != NULL){
revision = yrev->ys_argument;
revision = yang_argument_get(yrev);
}
}
return revision;
@ -189,25 +189,25 @@ yms_build(clicon_handle h,
goto done;
}
cprintf(cb,"<modules-state xmlns=\"%s\">", yns->ys_argument);
cprintf(cb,"<modules-state xmlns=\"%s\">", yang_argument_get(yns));
cprintf(cb,"<module-set-id>%s</module-set-id>", msid);
ymod = NULL;
while ((ymod = yn_each(yspec, ymod)) != NULL) {
if (ymod->ys_keyword != Y_MODULE &&
ymod->ys_keyword != Y_SUBMODULE)
if (yang_keyword_get(ymod) != Y_MODULE &&
yang_keyword_get(ymod) != Y_SUBMODULE)
continue;
cprintf(cb,"<module>");
cprintf(cb,"<name>%s</name>", ymod->ys_argument);
cprintf(cb,"<name>%s</name>", yang_argument_get(ymod));
if ((ys = yang_find(ymod, Y_REVISION, NULL)) != NULL)
cprintf(cb,"<revision>%s</revision>", ys->ys_argument);
cprintf(cb,"<revision>%s</revision>", yang_argument_get(ys));
else{
/* RFC7895 1 If no (such) revision statement exists, the module's or
submodule's revision is the zero-length string. */
cprintf(cb,"<revision></revision>");
}
if ((ys = yang_find(ymod, Y_NAMESPACE, NULL)) != NULL)
cprintf(cb,"<namespace>%s</namespace>", ys->ys_argument);
cprintf(cb,"<namespace>%s</namespace>", yang_argument_get(ys));
else
cprintf(cb,"<namespace></namespace>");
/* This follows order in rfc 7895: feature, conformance-type,
@ -215,10 +215,10 @@ yms_build(clicon_handle h,
if (!brief){
yc = NULL;
while ((yc = yn_each(ymod, yc)) != NULL) {
switch(yc->ys_keyword){
switch(yang_keyword_get(yc)){
case Y_FEATURE:
if (yc->ys_cv && cv_bool_get(yc->ys_cv))
cprintf(cb,"<feature>%s</feature>", yc->ys_argument);
if (yang_cv_get(yc) && cv_bool_get(yang_cv_get(yc)))
cprintf(cb,"<feature>%s</feature>", yang_argument_get(yc));
break;
default:
break;
@ -228,12 +228,12 @@ yms_build(clicon_handle h,
}
yc = NULL;
while ((yc = yn_each(ymod, yc)) != NULL) {
switch(yc->ys_keyword){
switch(yang_keyword_get(yc)){
case Y_SUBMODULE:
cprintf(cb,"<submodule>");
cprintf(cb,"<name>%s</name>", yc->ys_argument);
cprintf(cb,"<name>%s</name>", yang_argument_get(yc));
if ((ys = yang_find(yc, Y_REVISION, NULL)) != NULL)
cprintf(cb,"<revision>%s</revision>", ys->ys_argument);
cprintf(cb,"<revision>%s</revision>", yang_argument_get(ys));
else
cprintf(cb,"<revision></revision>");
cprintf(cb,"</submodule>");
@ -413,7 +413,7 @@ mod_ns_upgrade(clicon_handle h,
goto fail;
if ((yrev = yang_find(ymod, Y_REVISION, NULL)) == NULL)
goto fail;
if (ys_parse_date_arg(yrev->ys_argument, &to) < 0)
if (ys_parse_date_arg(yang_argument_get(yrev), &to) < 0)
goto done;
}
if ((ret = upgrade_callback_call(h, xt, ns, from, to, cbret)) < 0)
@ -482,3 +482,125 @@ clixon_module_upgrade(clicon_handle h,
retval = 0;
goto done;
}
/*! Given a yang statement and a prefix, return yang module to that relative prefix
* Note, not the other module but the proxy import statement only
* @param[in] ys A yang statement
* @param[in] prefix prefix
* @retval ymod Yang module statement if found
* @retval NULL not found
* @note Prefixes are relative to the module they are defined
* @see yang_find_module_by_name
* @see yang_find_module_by_namespace
*/
yang_stmt *
yang_find_module_by_prefix(yang_stmt *ys,
char *prefix)
{
yang_stmt *yimport;
yang_stmt *yprefix;
yang_stmt *my_ymod;
yang_stmt *ymod = NULL;
yang_stmt *yspec;
char *myprefix;
if ((yspec = ys_spec(ys)) == NULL){
clicon_err(OE_YANG, 0, "My yang spec not found");
goto done;
}
if ((my_ymod = ys_module(ys)) == NULL){
clicon_err(OE_YANG, 0, "My yang module not found");
goto done;
}
myprefix = yang_find_myprefix(ys);
if (myprefix && strcmp(myprefix, prefix) == 0){
ymod = my_ymod;
goto done;
}
yimport = NULL;
while ((yimport = yn_each(my_ymod, yimport)) != NULL) {
if (yang_keyword_get(yimport) != Y_IMPORT)
continue;
if ((yprefix = yang_find(yimport, Y_PREFIX, NULL)) != NULL &&
strcmp(yang_argument_get(yprefix), prefix) == 0){
break;
}
}
if (yimport){
if ((ymod = yang_find(yspec, Y_MODULE, yang_argument_get(yimport))) == NULL){
clicon_err(OE_YANG, 0, "No module or sub-module found with prefix %s",
prefix);
yimport = NULL;
goto done; /* unresolved */
}
}
done:
return ymod;
}
/* Get module from its own prefix
* This is really not a valid usecase, a kludge for the identityref derived
* list workaround (IDENTITYREF_KLUDGE)
* Actually, for canonical prefixes it is!
*/
yang_stmt *
yang_find_module_by_prefix_yspec(yang_stmt *yspec,
char *prefix)
{
yang_stmt *ymod = NULL;
yang_stmt *yprefix;
while ((ymod = yn_each(yspec, ymod)) != NULL)
if (yang_keyword_get(ymod) == Y_MODULE &&
(yprefix = yang_find(ymod, Y_PREFIX, NULL)) != NULL &&
strcmp(yang_argument_get(yprefix), prefix) == 0)
return ymod;
return NULL;
}
/*! Given a yang spec and a namespace, return yang module
*
* @param[in] yspec A yang specification
* @param[in] namespace namespace
* @retval ymod Yang module statement if found
* @retval NULL not found
* @see yang_find_module_by_name
* @see yang_find_module_by_prefix module-specific prefix
*/
yang_stmt *
yang_find_module_by_namespace(yang_stmt *yspec,
char *namespace)
{
yang_stmt *ymod = NULL;
if (namespace == NULL)
goto done;
while ((ymod = yn_each(yspec, ymod)) != NULL) {
if (yang_find(ymod, Y_NAMESPACE, namespace) != NULL)
break;
}
done:
return ymod;
}
/*! Given a yang spec and a module name, return yang module or submodule
*
* @param[in] yspec A yang specification
* @param[in] name Name of module
* @retval ymod Yang module statement if found
* @retval NULL not found
* @see yang_find_module_by_namespace
* @see yang_find_module_by_prefix module-specific prefix
*/
yang_stmt *
yang_find_module_by_name(yang_stmt *yspec,
char *name)
{
yang_stmt *ymod = NULL;
while ((ymod = yn_each(yspec, ymod)) != NULL)
if ((yang_keyword_get(ymod) == Y_MODULE || yang_keyword_get(ymod) == Y_SUBMODULE) &&
strcmp(yang_argument_get(ymod), name)==0)
return ymod;
return NULL;
}

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -51,7 +51,6 @@
%start file
%union {
int intval;
char *string;
}
@ -190,8 +189,8 @@
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_yang.h"
#include "clixon_yang_parse_lib.h"
#include "clixon_yang_parse.h"
#include "clixon_yang_internal.h" /* internal */
extern int clixon_yang_parseget_lineno (void);
@ -300,7 +299,7 @@ ysp_add(struct clicon_yang_yacc_arg *yy,
if ((ys = ys_new(keyword)) == NULL)
goto err;
/* NOTE: does not make a copy of string, ie argument is 'consumed' here */
ys->ys_argument = argument;
yang_argument_set(ys, argument);
if (yn_insert(yn, ys) < 0) /* Insert into hierarchy */
goto err;
if (ys_parse_sub(ys, extra) < 0) /* Check statement-specific syntax */

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -91,13 +91,12 @@
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_regex.h"
#include "clixon_yang.h"
#include "clixon_hash.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_plugin.h"
#include "clixon_options.h"
#include "clixon_yang.h"
#include "clixon_yang_internal.h" /* internal */
#include "clixon_yang_module.h"
#include "clixon_yang_type.h"
/*
@ -166,176 +165,6 @@ yang_builtin(char *type)
return 0;
}
/*! Set type cache for yang type
* @param[in] rxmode Kludge to know which regexp engine is used
* @see yang_type_cache_regexp_set where cache is extended w compiled regexps
*/
static int
yang_type_cache_set(yang_type_cache **ycache0,
yang_stmt *resolved,
int options,
cvec *cvv,
cvec *patterns,
uint8_t fraction)
{
int retval = -1;
yang_type_cache *ycache = *ycache0;
assert (ycache == NULL);
if ((ycache = (yang_type_cache *)malloc(sizeof(*ycache))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(ycache, 0, sizeof(*ycache));
*ycache0 = ycache;
ycache->yc_resolved = resolved;
ycache->yc_options = options;
if (cvv){
if ((ycache->yc_cvv = cvec_dup(cvv)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_dup");
goto done;
}
}
if (patterns && (ycache->yc_patterns = cvec_dup(patterns)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_dup");
goto done;
}
ycache->yc_fraction = fraction;
retval = 0;
done:
return retval;
}
/*! Extend yang type cache with compiled regexps
* Compiled Regexps are computed in validate code - after initial cache set
* @param[in] regexps
*/
static int
yang_type_cache_regexp_set(yang_stmt *ytype,
int rxmode,
cvec *regexps)
{
int retval = -1;
yang_type_cache *ycache;
assert(regexps);
assert(yang_keyword_get(ytype) == Y_TYPE);
assert((ycache = ytype->ys_typecache) != NULL);
assert(ycache->yc_regexps == NULL);
ycache->yc_rxmode = rxmode;
if ((ycache->yc_regexps = cvec_dup(regexps)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_dup");
goto done;
}
retval = 0;
done:
return retval;
}
/*! Get individual fields (direct/destructively) from yang type cache.
* @param[out] patterns Initialized cvec of regexp patterns strings
*/
static int
yang_type_cache_get(yang_type_cache *ycache,
yang_stmt **resolved,
int *options,
cvec **cvv,
cvec *patterns,
int *rxmode,
cvec *regexps,
uint8_t *fraction)
{
int retval = -1;
cg_var *cv = NULL;
if (resolved)
*resolved = ycache->yc_resolved;
if (options)
*options = ycache->yc_options;
if (cvv)
*cvv = ycache->yc_cvv;
if (patterns){
cv = NULL;
while ((cv = cvec_each(ycache->yc_patterns, cv)) != NULL)
cvec_append_var(patterns, cv);
}
if (regexps){
cv = NULL;
while ((cv = cvec_each(ycache->yc_regexps, cv)) != NULL)
cvec_append_var(regexps, cv);
}
if (rxmode)
*rxmode = ycache->yc_rxmode;
if (fraction)
*fraction = ycache->yc_fraction;
retval = 0;
// done:
return retval;
}
int
yang_type_cache_cp(yang_type_cache **ycnew,
yang_type_cache *ycold)
{
int retval = -1;
int options;
cvec *cvv;
cvec *patterns = NULL;
uint8_t fraction;
yang_stmt *resolved;
if ((patterns = cvec_new(0)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_new");
goto done;
}
/* Note, regexps are not copied since they are voids, if they were, they
* could not be freed in a simple way since copies are made at augment/group
*/
yang_type_cache_get(ycold, &resolved, &options, &cvv, patterns, NULL, NULL, &fraction);
if (yang_type_cache_set(ycnew, resolved, options, cvv, patterns, fraction) < 0)
goto done;
retval = 0;
done:
if (patterns)
cvec_free(patterns);
return retval;
}
int
yang_type_cache_free(yang_type_cache *ycache)
{
cg_var *cv;
void *p;
if (ycache->yc_cvv)
cvec_free(ycache->yc_cvv);
if (ycache->yc_patterns)
cvec_free(ycache->yc_patterns);
if (ycache->yc_regexps){
cv = NULL;
while ((cv = cvec_each(ycache->yc_regexps, cv)) != NULL){
/* need to store mode since clicon_handle is not available */
switch (ycache->yc_rxmode){
case REGEXP_POSIX:
cligen_regex_posix_free(cv_void_get(cv));
break;
case REGEXP_LIBXML2:
cligen_regex_libxml2_free(cv_void_get(cv));
break;
default:
break;
}
if ((p = cv_void_get(cv)) != NULL){
free(p);
cv_void_set(cv, NULL);
}
}
cvec_free(ycache->yc_regexps);
}
free(ycache);
return 0;
}
/*! Compile yang patterns in string form to regex compiled void* form
* and re-store into "patterns" cvec.
* This is done here instead of deep in resolve code (resolve_restrictions)
@ -415,7 +244,7 @@ ys_resolve_type(yang_stmt *ys,
/* Recursively resolve ys -> resolve with restrictions(options, etc)
* Note that the resolved type could be ys itself.
*/
if (yang_type_resolve(ys->ys_parent, ys->ys_parent,
if (yang_type_resolve(yang_parent_get(ys), yang_parent_get(ys),
ys, &resolved,
&options, &cvv, patterns, NULL, &fraction) < 0)
goto done;
@ -423,8 +252,7 @@ ys_resolve_type(yang_stmt *ys,
/* Cache the type resolving locally. Only place where this is done.
* Why not do it in yang_type_resolve? (compile regexps needs clicon_handle)
*/
if (yang_type_cache_set(&ys->ys_typecache,
resolved, options, cvv,
if (yang_type_cache_set(ys, resolved, options, cvv,
patterns, fraction) < 0)
goto done;
retval = 0;
@ -512,13 +340,15 @@ clicon_type2cv(char *origtype,
enum cv_type *cvtype)
{
int retval = -1;
yang_stmt *ym;
*cvtype = CGV_ERR;
ym = ys_module(ys);
if (restype != NULL){
yang2cv_type(restype, cvtype);
if (*cvtype == CGV_ERR){
clicon_err(OE_YANG, 0, "%s: \"%s\" type not translated",
ys_module(ys)->ys_argument, restype);
yang_argument_get(ym), restype);
goto done;
}
}
@ -530,7 +360,7 @@ clicon_type2cv(char *origtype,
yang2cv_type(origtype, cvtype);
if (*cvtype == CGV_ERR){
clicon_err(OE_YANG, 0, "%s:\"%s\": type not resolved",
ys_module(ys)->ys_argument, origtype);
yang_argument_get(ym), origtype);
goto done;
}
}
@ -832,9 +662,9 @@ cv_validate1(clicon_handle h,
yi = NULL;
if (str != NULL)
while ((yi = yn_each(yrestype, yi)) != NULL){
if (yi->ys_keyword != Y_ENUM)
if (yang_keyword_get(yi) != Y_ENUM)
continue;
if (strcmp(yi->ys_argument, str) == 0){
if (strcmp(yang_argument_get(yi), str) == 0){
found++;
break;
}
@ -860,9 +690,9 @@ cv_validate1(clicon_handle h,
found = 0;
yi = NULL;
while ((yi = yn_each(yrestype, yi)) != NULL){
if (yi->ys_keyword != Y_BIT)
if (yang_keyword_get(yi) != Y_BIT)
continue;
if (strcmp(yi->ys_argument, v) == 0){
if (strcmp(yang_argument_get(yi), v) == 0){
found++;
break;
}
@ -944,7 +774,7 @@ ys_cv_validate_union_one(clicon_handle h,
if (yang_type_resolve(ys, ys, yt, &yrt, &options, &cvv, patterns, regexps,
&fraction) < 0)
goto done;
restype = yrt?yrt->ys_argument:NULL;
restype = yrt?yang_argument_get(yrt):NULL;
if (restype && strcmp(restype, "union") == 0){ /* recursive union */
if ((retval = ys_cv_validate_union(h, ys, reason, yrt, type, val)) < 0)
goto done;
@ -957,6 +787,10 @@ ys_cv_validate_union_one(clicon_handle h,
clicon_err(OE_UNIX, errno, "cv_new");
goto done;
}
if (val == NULL){ /* Fail validation on NULL */
retval = 0;
goto done;
}
if ((retval = cv_parse1(val, cvt, reason)) < 0){
clicon_err(OE_UNIX, errno, "cv_parse");
goto done;
@ -988,7 +822,7 @@ ys_cv_validate_union_one(clicon_handle h,
return retval;
}
/*!
/*! Validate union
* @param[out] reason If given, and return value is 0, contains malloced string
* @retval -1 Error (fatal), with errno set to indicate error
* @retval 0 Validation not OK, malloced reason is returned. Free reason with free()
@ -1007,7 +841,7 @@ ys_cv_validate_union(clicon_handle h,
char *reason1 = NULL; /* saved reason */
while ((yt = yn_each(yrestype, yt)) != NULL){
if (yt->ys_keyword != Y_TYPE)
if (yang_keyword_get(yt) != Y_TYPE)
continue;
if ((retval = ys_cv_validate_union_one(h, ys, reason, yt, type, val)) < 0)
goto done;
@ -1069,11 +903,11 @@ ys_cv_validate(clicon_handle h,
if (reason)
*reason=NULL;
if (ys->ys_keyword != Y_LEAF && ys->ys_keyword != Y_LEAF_LIST){
if (yang_keyword_get(ys) != Y_LEAF && yang_keyword_get(ys) != Y_LEAF_LIST){
retval = 1;
goto done;
}
ycv = ys->ys_cv;
ycv = yang_cv_get(ys);
if ((regexps = cvec_new(0)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_new");
goto done;
@ -1087,7 +921,7 @@ ys_cv_validate(clicon_handle h,
patterns, regexps,
&fraction) < 0)
goto done;
restype = yrestype?yrestype->ys_argument:NULL;
restype = yrestype?yang_argument_get(yrestype):NULL;
if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0)
goto done;
@ -1104,7 +938,11 @@ ys_cv_validate(clicon_handle h,
/* Note restype can be NULL here for example with unresolved hardcoded uuid */
if (restype && strcmp(restype, "union") == 0){
assert(cvtype == CGV_REST);
val = cv_string_get(cv);
/* Instead of NULL, give an empty string to validate, this is to avoid cv_parse
* errors and may actually be the wrong thing to do.
*/
if ((val = cv_string_get(cv)) == NULL)
val = "";
if ((retval2 = ys_cv_validate_union(h, ys, reason, yrestype, origtype, val)) < 0)
goto done;
retval = retval2; /* invalid (0) with latest reason or valid 1 */
@ -1146,8 +984,8 @@ ys_cv_validate(clicon_handle h,
static inline int
ys_typedef(yang_stmt *ys)
{
return ys->ys_keyword == Y_MODULE || ys->ys_keyword == Y_SUBMODULE ||
ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST;
return yang_keyword_get(ys) == Y_MODULE || yang_keyword_get(ys) == Y_SUBMODULE ||
yang_keyword_get(ys) == Y_CONTAINER || yang_keyword_get(ys) == Y_LIST;
}
/* find next ys up which can contain a typedef */
@ -1157,9 +995,9 @@ ys_typedef_up(yang_stmt *ys)
yang_stmt *yn;
while (ys != NULL && !ys_typedef(ys)){
yn = ys->ys_parent;
yn = yang_parent_get(ys);
/* Some extra stuff to ensure ys is a stmt */
if (yn && yn->ys_keyword == Y_SPEC)
if (yn && yang_keyword_get(yn) == Y_SPEC)
yn = NULL;
ys = (yang_stmt*)yn;
}
@ -1222,8 +1060,8 @@ yang_find_identity(yang_stmt *ys,
if ((yid = yang_find(ys, Y_IDENTITY, id)) != NULL)
break;
/* Did not find a matching typedef there, proceed to next level */
yn = ys->ys_parent;
if (yn && yn->ys_keyword == Y_SPEC)
yn = yang_parent_get(ys);
if (yn && yang_keyword_get(yn) == Y_SPEC)
yn = NULL;
ys = (yang_stmt*)yn;
}
@ -1263,12 +1101,12 @@ yang_type_resolve_restrictions(yang_stmt *ytype,
if (options && cvv &&
(ys = yang_find(ytype, Y_RANGE, NULL)) != NULL){
*cvv = ys->ys_cvec;
*cvv = yang_cvec_get(ys);
*options |= YANG_OPTIONS_RANGE;
}
if (options && cvv &&
(ys = yang_find(ytype, Y_LENGTH, NULL)) != NULL){
*cvv = ys->ys_cvec;
*cvv = yang_cvec_get(ys);
*options |= YANG_OPTIONS_LENGTH;
}
/* Find all patterns */
@ -1281,7 +1119,7 @@ yang_type_resolve_restrictions(yang_stmt *ytype,
clicon_err(OE_UNIX, errno, "cvec_add");
goto done;
}
pattern = ys->ys_argument; /* clear text pattern */
pattern = yang_argument_get(ys); /* clear text pattern */
/* Check 1.1 invert pattern */
if (yang_find(ys, Y_MODIFIER, "invert-match") != NULL)
cv_flag_set(cv, V_INVERT);
@ -1290,7 +1128,7 @@ yang_type_resolve_restrictions(yang_stmt *ytype,
}
if (options && fraction &&
(ys = yang_find(ytype, Y_FRACTION_DIGITS, NULL)) != NULL){
*fraction = cv_uint8_get(ys->ys_cv);
*fraction = cv_uint8_get(yang_cv_get(ys));
*options |= YANG_OPTIONS_FRACTION_DIGITS;
}
retval = 0;
@ -1339,6 +1177,7 @@ yang_type_resolve(yang_stmt *yorig,
int retval = -1;
yang_stmt *yn;
yang_stmt *yrmod; /* module where resolved type is looked for */
int ret;
if (options)
*options = 0x0;
@ -1347,13 +1186,20 @@ yang_type_resolve(yang_stmt *yorig,
if (nodeid_split(yang_argument_get(ytype), &prefix, &type) < 0)
goto done;
/* Cache does not work for eg string length 32? */
#if 1
if ((ret = yang_type_cache_get(ytype, yrestype,
options, cvv, patterns, NULL, regexps, fraction)) < 0)
goto done;
if (ret == 1)
goto ok;
#else
if (ytype->ys_typecache != NULL){
if (yang_type_cache_get(ytype->ys_typecache, yrestype,
if (yang_type_cache_get(ytype, yrestype,
options, cvv, patterns, NULL, regexps, fraction) < 0)
goto done;
goto ok;
}
#endif
/* Check if type is basic type. If so, return that */
if ((prefix == NULL && yang_builtin(type))){
*yrestype = ytype;
@ -1366,7 +1212,7 @@ yang_type_resolve(yang_stmt *yorig,
if (prefix){ /* Go to top and find import that matches */
if ((yrmod = yang_find_module_by_prefix(ytype, prefix)) == NULL){
clicon_err(OE_DB, 0, "Type not resolved: \"%s:%s\" in module %s",
prefix, type, ys_module(yorig)->ys_argument);
prefix, type, yang_argument_get(ys_module(yorig)));
goto done;
}
if ((rytypedef = yang_find(yrmod, Y_TYPEDEF, type)) == NULL)
@ -1384,8 +1230,8 @@ yang_type_resolve(yang_stmt *yorig,
if ((rytypedef = yang_find(ys, Y_TYPEDEF, type)) != NULL)
break;
/* Did not find a matching typedef there, proceed to next level */
yn = ys->ys_parent;
if (yn && (yn->ys_keyword == Y_SPEC))
yn = yang_parent_get(ys);
if (yn && (yang_keyword_get(yn) == Y_SPEC))
yn = NULL;
ys = (yang_stmt*)yn;
}
@ -1488,8 +1334,6 @@ yang_type_get(yang_stmt *ys,
if (yang_type_resolve(ys, ys, ytype, yrestype,
options, cvv, patterns, regexps, fraction) < 0)
goto done;
clicon_debug(3, "%s: %s %s->%s", __FUNCTION__, ys->ys_argument, type,
*yrestype?(*yrestype)->ys_argument:"null");
retval = 0;
done:
if (type)
@ -1512,7 +1356,7 @@ yang_type2cv(yang_stmt *ys)
if (yang_type_get(ys, &origtype, &yrestype, NULL, NULL, NULL, NULL, NULL)
< 0)
goto done;
restype = yrestype?yrestype->ys_argument:NULL;
restype = yrestype?yang_argument_get(yrestype):NULL;
if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0) /* This handles non-resolved also */
goto done;
done:

View file

@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
# Copyright (C) 2009-2020 Olof Hagsand
#
# This file is part of CLIXON
#
@ -33,12 +33,6 @@
VPATH = @srcdir@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
CC = @CC@
CFLAGS = @CFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
SHELL = /bin/sh
.PHONY: all clean distclean depend install uninstall

View file

@ -17,6 +17,8 @@ In the CI process, the system is built and configured and then the
[clixon test container](../docker/system) is built and the tests in
this directory is executed.
There are also [manual cicd scripts here](cicd/README.md)
## Getting started
You need to build and install the clixon utility programs before running the tests as some of the tests rely on them:

74
test/cicd/Makefile.in Normal file
View file

@ -0,0 +1,74 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2020 Olof Hagsand
1#
# This file is part of CLIXON
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Alternatively, the contents of this file may be used under the terms of
# the GNU General Public License Version 3 or later (the "GPL"),
# in which case the provisions of the GPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of the GPL, and not to allow others to
# use your version of this file under the terms of Apache License version 2,
# indicate your decision by deleting the provisions above and replace them with
# the notice and other provisions required by the GPL. If you do not delete
# 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 *****
#
VPATH = @srcdir@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
SHELL = /bin/sh
.PHONY: all clean distclean depend install uninstall
HOSTS = vandal.hagsand.com # i86_32 ubuntu
HOSTS += clixon.dogwood.com # FreeBSD x86_64
#HOSTS += nuc1.hagsand.com # x86_64 ubuntu
#HOSTS += pi2.hagsand.com # arm Raspian
SCRIPTS = cligen-mk.sh
SCRIPTS += clixon-mk.sh
SCRIPTS += clixon-config.sh
.PHONY: all clean distclean depend install uninstall $(HOSTS)
all: $(HOSTS)
$(HOSTS):
for s in $(SCRIPTS); do \
(scp $$s $@:/tmp/ ; ssh $@ chmod 750 /tmp/$$s || exit 1) \
done;
./cicd.sh $@ 2>&1 | tee $@.log
clean:
rm -f *.log
distclean: clean
rm -f Makefile *~ .depend
depend:
install-include:
install:
uninstall:

14
test/cicd/README.md Normal file
View file

@ -0,0 +1,14 @@
CICD scripts
============
Manual scripts for running committed code on a set of hosts.
The script then uses a Makefile and logs in to each host, pulls from
git, configure, makes and runs through the tests. Make is used to get
concurrency - non-trivial with bash, eg with `make -j 10`
Note there are other cicd scripts than this, such as the the "travis" scrips.
The Makefile contains a configurable HOSTS variable, please edit.
Logs appear in : <hostname>.log.

39
test/cicd/cicd.sh Executable file
View file

@ -0,0 +1,39 @@
#!/usr/bin/env bash
# CI/CD script complementing trevor github
# Login in to a number of hosts and fo the following:
# 0. Create and transfer sub-scripts used in main script: cligen-mk.sh clixon-mk.sh clixon-config.sh
# 1. pull latest version
# 2. Run configure
# 3. Compile and install (assume mk.sh)
# 4. Run tests
# Assume:
# - subscripts SCRIPTS exists locally where this script is executed
# - A test/site.sh file is handmade on each host
# - some commands are passwordless using
# sudo visudo -f /etc/sudoers.d/clixonci
# <user> ALL = (root)NOPASSWD : ALL
# <user> ALL = (www-data)NOPASSWD : ALL
# <user> ALL = (clicon)NOPASSWD : /usr/local/sbin/clixon_backend
# Experiment in identifying all commands: /usr/bin/make,/usr/local/sbin/clixon_backend,/usr/bin/pkill,/usr/local/bin/clixon_util_socket,/usr/bin/tee,/bin/rm,/usr/bin/touch,/bin/chmod
#
# Typical run: ./cicd.sh 2>&1 | tee cilog
set -eux # x
if [ $# -ne 1 ]; then
echo "usage: $0 <host>"
exit -1
fi
h=$1
ssh -t $h "test -d src/cligen || (cd src;git clone https://github.com/olofhagsand/cligen.git)"
ssh -t $h "(cd src/cligen;git pull)"
ssh -t $h "(cd src/cligen;./configure)"
ssh -t $h "(cd src/cligen; /tmp/cligen-mk.sh)"
ssh -t $h "test -d src/clixon || (cd src;git clone https://github.com/clicon/clixon.git)"
ssh -t $h "(cd src/clixon;git pull)"
ssh -t $h "(cd src/clixon; /tmp/clixon-config.sh)"
ssh -t $h "(cd src/clixon; /tmp/clixon-mk.sh)"
ssh -t $h sudo ldconfig
ssh -t $h "(cd src/clixon/test; ./sum.sh)"

11
test/cicd/cligen-mk.sh Normal file
View file

@ -0,0 +1,11 @@
#!/bin/sh
# A top-level maker for cligen
set -eux
if [ $(uname) = "FreeBSD" ]; then
MAKE=$(which gmake)
else
MAKE=$(which make)
fi
$MAKE clean
$MAKE -j10
sudo $MAKE install

View file

@ -0,0 +1,8 @@
#!/bin/sh
# A top-level configurer for clixon
set -eux
if [ $(uname) = "FreeBSD" ]; then
./configure --with-cligen=/usr/local --with-wwwuser=www --enable-optyangs
else
./configure --enable-optyangs
fi

16
test/cicd/clixon-mk.sh Normal file
View file

@ -0,0 +1,16 @@
#!/bin/sh
# A top-level maker for clixon
set -eux
if [ $(uname) = "FreeBSD" ]; then
MAKE=$(which gmake)
else
MAKE=$(which make)
fi
$MAKE clean
$MAKE -j10
sudo $MAKE install
sudo $MAKE install-include
(cd example; $MAKE)
(cd util; $MAKE)
(cd example; sudo $MAKE install)
(cd util; sudo $MAKE install)

View file

@ -339,7 +339,7 @@ expecteq(){
# Evaluate and return
# like expecteq but partial match is OK
# Example: expecteq $(fn arg) 0 "my return"
# Example: expectpart $(fn arg) 0 "my return"
# - evaluated expression
# - expected command return value (0 if OK)
# - expected stdout outcome*
@ -364,15 +364,15 @@ expectpart(){
let i=0;
for exp in "$@"; do
if [ $i -gt 1 ]; then
# echo "exp:$exp"
match=`echo $ret | grep --null -o "$exp"` # XXX -EZo: -E cant handle {}
if [ -z "$match" ]; then
err "$exp" "$ret"
fi
fi
let i++;
done
# echo "echo \"$ret\" | grep --null -o \"$exp"\"
match=$(echo "$ret" | grep --null -o "$exp") # XXX -EZo: -E cant handle {}
r=$?
if [ $r != 0 ]; then
err "$exp" "$ret"
fi
fi
let i++;
done
# if [[ "$ret" != "$expect" ]]; then
# err "$expect" "$ret"
# fi

View file

@ -98,9 +98,9 @@ done
# Then actual run
testnr=0
for cmd in $cmds; do
for cmd1 in $cmds; do
if [ $testnr != 0 ]; then echo; fi
println "Mem test $cmd begin"
memonce $cmd
println "Mem test $cmd done"
println "Mem test $cmd1 begin"
memonce $cmd1
println "Mem test $cmd1 done"
done

265
test/test_api.sh Executable file
View file

@ -0,0 +1,265 @@
#!/bin/bash
# Advanced API XML test. Compile a backend plugin and start the backend, and then send an RPC to
# trigger that plugin
# The plugin looks in an XML tree using three different methods:
# 1. xml_each and xml_find
# 2. xpath_first
# 3. binary_search
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
# Which format to use as datastore format internally
: ${format:=xml}
APPNAME=example
cfg=$dir/conf_yang.xml
fyang=$dir/example-api.yang
cfile=$dir/example-api.c
pdir=$dir/plugin
sofile=$pdir/example-api.so
if [ ! -d $pdir ]; then
mkdir $pdir
fi
cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>/tmp/conf_yang.xml</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
<CLICON_BACKEND_DIR>$pdir</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_XMLDB_FORMAT>$format</CLICON_XMLDB_FORMAT>
</clixon-config>
EOF
cat <<EOF > $fyang
module example-api{
yang-version 1.1;
namespace "urn:example:api";
prefix ex;
container c {
leaf-list y0 {
ordered-by user;
type string;
}
leaf-list y1 {
ordered-by system;
type string;
}
list y2 {
ordered-by user;
key "k";
leaf k {
type int32;
}
leaf val {
type string;
}
}
list y3 {
ordered-by system;
key "k";
leaf k {
type int32;
}
leaf val {
type string;
}
}
}
rpc trigger {
description "trigger an action in the backend";
}
}
EOF
cat<<EOF > $cfile
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/syslog.h>
/* clicon */
#include <cligen/cligen.h>
/* Clicon library functions. */
#include <clixon/clixon.h>
/* These include signatures for plugin and transaction callbacks. */
#include <clixon/clixon_backend.h>
static int
trigger_rpc(clicon_handle h, /* Clicon handle */
cxobj *xe, /* Request: <rpc><xn></rpc> */
cbuf *cbret, /* Reply eg <rpc-reply>... */
void *arg, /* client_entry */
void *regarg) /* Argument given at register */
{
int retval = -1;
cxobj *xret = NULL;
cxobj *xc = NULL;
cxobj *x = NULL;
char *k;
char *val;
cvec *cvk = NULL;
cg_var *cv;
cxobj **xvec = NULL;
size_t xlen = 0;
if (xmldb_get(h, "running", NULL, "/c", &xret) < 0)
goto done;
clicon_debug(1, "%s xret:%s", __FUNCTION__, xml_name(xret));
xc = xpath_first(xret, NULL, "/c");
clicon_debug(1, "%s xc:%s", __FUNCTION__, xml_name(xc));
/* Method 1 loop */
x = NULL;
val = NULL;
while ((x = xml_child_each(xc, x, -1)) != NULL) {
if (strcmp(xml_name(x), "y3") != 0)
continue;
if ((k = xml_find_body(x, "k")) != NULL &&
strcmp(k, "5") == 0){
val = xml_find_body(x, "val");
break;
}
}
clicon_debug(1, "%s Method 1: val:%s", __FUNCTION__, val?val:"null");
/* Method 2 xpath */
val = NULL;
if ((x = xpath_first(xc, NULL, "y3[k=5]")) != NULL)
val = xml_find_body(x, "val");
clicon_debug(1, "%s Method 2: val:%s", __FUNCTION__, val?val:"null");
/* Method 3 binsearch */
val = NULL;
/* Add key/value vector */
if ((cvk = cvec_new(0)) == NULL){
clicon_err(OE_YANG, errno, "cvec_new");
goto done;
}
if ((cv = cvec_add(cvk, CGV_STRING)) == NULL)
goto done;
cv_name_set(cv, "k");
cv_string_set(cv, "5");
/* Use form 2c use spec of xc + name */
if (clixon_xml_find_index(xc, NULL, NULL, "y3", cvk, &xvec, &xlen) < 0)
goto done;
if (xlen)
val = xml_find_body(xvec[0], "val");
else
val = NULL;
clicon_debug(1, "%s Method 3: val:%s", __FUNCTION__, val?val:"null");
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (cvk)
cvec_free(cvk);
if (xret)
xml_free(xret);
if (xvec)
free(xvec);
return retval;
}
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
static clixon_plugin_api api = {
"order", /* name */ /*--- Common fields. ---*/
clixon_plugin_init, /* init */
};
/*! Backend plugin initialization
* @param[in] h Clixon handle
* @retval NULL Error with clicon_err set
* @retval api Pointer to API struct
*/
clixon_plugin_api *
clixon_plugin_init(clicon_handle h)
{
clicon_debug(1, "%s test-order", __FUNCTION__);
/* From example.yang (clicon) */
if (rpc_callback_register(h, trigger_rpc,
NULL,
"urn:example:api",
"trigger"/* Xml tag when callback is made */
) < 0)
return NULL;
return &api;
}
EOF
new "compile $cfile"
gcc -g -Wall -rdynamic -fPIC -shared $cfile -o $sofile
new "test params: -s running -f $cfg"
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then
err
fi
new "start backend"
start_backend -s running -f $cfg
fi
new "kill old restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
new "start restconf daemon"
start_restconf -f $cfg
new "waiting"
wait_backend
wait_restconf
XML='<c xmlns="urn:example:api"><y3><k>2</k></y3><y3><k>3</k></y3><y3><k>5</k><val>zorro</val></y3><y3><k>7</k></y3></c>'
# Add a set of entries using restconf
new "PUT a set of entries"
expectpart "$(curl -si -X PUT -H 'Content-Type: application/yang-data+xml' http://localhost/restconf/data/example-api:c -d "$XML")" 0 "HTTP/1.1 201 Created"
new "Check entries"
expectpart "$(curl -si -X GET http://localhost/restconf/data/example-api:c -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' "$XML"
new "Send a trigger"
expectpart "$(curl -si -X POST http://localhost/restconf/operations/example-api:trigger -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 204 No Content'
if [ $BE -eq 0 ]; then
exit # BE
fi
new "Kill backend"
# Check if premature kill
pid=`pgrep -u root -f clixon_backend`
if [ -z "$pid" ]; then
err "backend already dead"
fi
# kill backend
stop_backend -f $cfg
# unset conditional parameters
unset format
rm -rf $dir

260
test/test_api_path.sh Executable file
View file

@ -0,0 +1,260 @@
#!/usr/bin/env bash
# API-PATH tests
# Most tests are: generate lists, then access
# Tests include single and double indexes.
# string and int indexes
# Lists and leaf-lists
# Augmented yang where two lists are inside each other (depth)
# Multiple matches
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
: ${clixon_util_path:=clixon_util_path -a -D $DBG}
# Number of list/leaf-list entries
: ${nr:=100}
# XML file (alt provide it in stdin after xpath)
for (( i=1; i<7; i++ )); do
eval xml$i=$dir/xml$i.xml
done
ydir=$dir/yang
if [ ! -d $ydir ]; then
mkdir $ydir
fi
# XPATH binary search in ordered-by system lists
cat <<EOF > $ydir/moda.yang
module moda{
namespace "urn:example:a";
prefix a;
container x1{
description "list with single string key";
list y{
ordered-by system;
key k1;
leaf k1{
type string;
}
leaf z{
type string;
}
}
}
container x2{
description "list with single int key";
list y{
ordered-by system;
key k1;
leaf k1{
type uint32;
}
leaf z{
type string;
}
}
}
container x3{
description "list with double string keys";
list y{
ordered-by system;
key "k1 k2";
leaf k1{
type string;
}
leaf k2{
type string;
}
leaf z{
type string;
}
}
}
container x4{
description "leaf-list with int key";
leaf-list y{
type string;
}
}
list x5{
ordered-by system;
description "Direct under root";
key "k1";
leaf k1{
type string;
}
leaf z{
type string;
}
}
augment "/b:x6/b:yy" {
list y{
ordered-by system;
key "k1 k2";
leaf k1{
type string;
}
leaf k2{
type string;
}
leaf-list z{
type string;
}
}
}
}
EOF
# This is for augment usecase
cat <<EOF > $ydir/modb.yang
module modb{
namespace "urn:example:b";
prefix b;
container x6{
description "deep tree and augment";
list yy{
ordered-by system;
key "kk1 kk2";
leaf kk1{
type string;
}
leaf kk2{
type string;
}
leaf-list zz{
type string;
}
}
}
}
EOF
rnd=$(( ( RANDOM % $nr ) ))
# Single string key
new "generate list with $nr single string key to $xml1"
echo -n '<x1 xmlns="urn:example:a">' > $xml1
for (( i=0; i<$nr; i++ )); do
echo -n "<y><k1>a$i</k1><z>foo$i</z></y>" >> $xml1
done
echo -n '</x1>' >> $xml1
new "api-path single string key k1=a$rnd"
echo "$clixon_util_path -f $xml1 -y $ydir -p /moda:x1/y=a$rnd"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /moda:x1/y=a$rnd)" 0 "^0: <y><k1>a$rnd</k1><z>foo$rnd</z></y>$"
new "api-path single string key /x1"
echo "$clixon_util_path -f $xml1 -y $ydir -p /moda:x1"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /moda:x1)" 0 "0: <x1 xmlns=\"urn:example:a\"><y><k1>a0</k1><z>foo0</z></y><y><k1>a1</k1><z>foo1</z></y>" # Assume at least two elements
new "api-path single string key omit key"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /moda:x1/y)" 0 '^0: <y><k1>a0</k1><z>foo0</z></y>
1: <y><k1>a0</k1><z>foo0</z></y>'
new "api-path single string wrong module, notfound"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /modxxx:x1/y=a$rnd 2> /dev/null)" 255 '^$'
new "api-path single string no module, notfound"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /x1/y=a$rnd 2> /dev/null)" 255 '^$'
new "api-path single string wrong top-symbol, notfound"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /moda:xxx/y=a$rnd 2> /dev/null)" 255 '^$'
new "api-path single string wrong list-symbol, notfound"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /moda:x1/xxx=a$rnd 2> /dev/null)" 255 '^$'
new "api-path single string two keys, notfound"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /moda:x1/y=a$rnd,a$rnd 2> /dev/null)" 255 '^$'
new "api-path single string sub-element, notfound"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /moda:x1/y=a$rnd/xxx 2> /dev/null)" 255 '^$'
# Single int key
new "generate list with $nr single int key to $xml2"
echo -n '<x2 xmlns="urn:example:a">' > $xml2
for (( i=0; i<$nr; i++ )); do
echo -n "<y><k1>$i</k1><z>foo$i</z></y>" >> $xml2
done
echo -n '</x2>' >> $xml2
new "api-path single int key k1=$rnd"
echo "$clixon_util_path -f $xml2 -y $ydir -p /moda:x2/y=$rnd"
expectpart "$($clixon_util_path -f $xml2 -y $ydir -p /moda:x2/y=$rnd)" 0 "^0: <y><k1>$rnd</k1><z>foo$rnd</z></y>$"
# Double string key
new "generate list with $nr double string keys to $xml3 (two k2 entries per k1 key)"
echo -n '<x3 xmlns="urn:example:a">' > $xml3
for (( i=0; i<$nr; i++ )); do
echo -n "<y><k1>a$i</k1><k2>a$i</k2><z>foo$i</z></y>" >> $xml3
echo -n "<y><k1>a$i</k1><k2>b$i</k2><z>foob$i</z></y>" >> $xml3
done
# Add two rules with empty k2 string
echo -n "<y><k1>a0</k1><k2></k2><z>foo0</z></y>" >> $xml3
echo -n "<y><k1>a1</k1><k2></k2><z>foo1</z></y>" >> $xml3
echo -n '</x3>' >> $xml3
new "api-path double string key k1=a$rnd,b$rnd"
echo "$clixon_util_path -f $xml3 -y $ydir -p /moda:x3/y=a$rnd,b$rnd"
expectpart "$($clixon_util_path -f $xml3 -y $ydir -p /moda:x3/y=a$rnd,b$rnd)" 0 "0: <y><k1>a$rnd</k1><k2>b$rnd</k2><z>foob$rnd</z></y>"
new "api-path double string key k1=a$rnd, - empty k2 string"
echo "$clixon_util_path -f $xml3 -y $ydir -p /moda:x3/y=a1,"
expectpart "$($clixon_util_path -f $xml3 -y $ydir -p /moda:x3/y=a1,)" 0 "0: <y><k1>a1</k1><k2/><z>foo1</z></y>"
new "api-path double string key k1=a$rnd, - no k2 string - three matches"
echo "$clixon_util_path -f $xml3 -y $ydir -p /moda:x3/y=a1"
expecteq "$($clixon_util_path -f $xml3 -y $ydir -p /moda:x3/y=a1)" 0 "0: <y><k1>a1</k1><k2/><z>foo1</z></y>
1: <y><k1>a1</k1><k2>a1</k2><z>foo1</z></y>
2: <y><k1>a1</k1><k2>b1</k2><z>foob1</z></y>"
# Leaf-list
new "generate leaf-list int keys to $xml4"
echo -n '<x4 xmlns="urn:example:a">' > $xml4
for (( i=0; i<$nr; i++ )); do
echo -n "<y>a$i</y>" >> $xml4
done
echo -n '</x4>' >> $xml4
new "api-path leaf-list k1=a$rnd"
echo "$clixon_util_path -f $xml4 -y $ydir -p /moda:x4/y=a$rnd"
expectpart "$($clixon_util_path -f $xml4 -y $ydir -p /moda:x4/y=a$rnd)" 0 "^0: <y>a$rnd</y>$"
# Single string key direct under root
new "generate list with $nr single string key to $xml5"
echo -n '' > $xml5
for (( i=0; i<$nr; i++ )); do
echo -n "<x5 xmlns=\"urn:example:a\"><k1>a$i</k1><z>foo$i</z></x5>" >> $xml5
done
new "api-path direct under root single string key k1=a$rnd"
echo "$clixon_util_path -f $xml5 -y $ydir -p /moda:x5=a$rnd"
expectpart "$($clixon_util_path -f $xml5 -y $ydir -p /moda:x5=a$rnd)" 0 "^0: <x5 xmlns=\"urn:example:a\"><k1>a$rnd</k1><z>foo$rnd</z></x5>$"
# Depth and augment
# Deep augmented xml path
new "generate deep list with augment"
echo -n '<x6 xmlns="urn:example:b">' > $xml6
for (( i=0; i<$nr; i++ )); do
echo -n "<yy><kk1>b$i</kk1><kk2>b$i</kk2><zz>foo$i</zz>" >> $xml6
for (( j=0; j<3; j++ )); do
echo -n "<y xmlns=\"urn:example:a\"><k1>a$j</k1><k2>a$j</k2><z>foo$j</z></y>" >> $xml6
done
echo -n "</yy>" >> $xml6
done
echo -n '</x6>' >> $xml6
new "api-path double string key k1=b$rnd,b$rnd in modb"
echo "$clixon_util_path -f $xml6 -y $ydir -p /modb:x6/yy=b$rnd,b$rnd"
expectpart "$($clixon_util_path -f $xml6 -y $ydir -p /modb:x6/yy=b$rnd,b$rnd)" 0 "0: <yy><kk1>b$rnd</kk1><kk2>b$rnd</kk2><zz>foo$rnd</zz><y xmlns=\"urn:example:a\"><k1>a0</k1><k2>a0</k2><z>foo0</z></y><y xmlns=\"urn:example:a\"><k1>a1</k1><k2>a1</k2><z>foo1</z></y><y xmlns=\"urn:example:a\"><k1>a2</k1><k2>a2</k2><z>foo2</z></y></yy>"
new "api-path double string key k1=a$rnd,b$rnd in modb + augmented in moda"
echo "$clixon_util_path -f $xml6 -y $ydir -p /modb:x6/yy=b$rnd,b$rnd/moda:y=a1,a1"
expectpart "$($clixon_util_path -f $xml6 -y $ydir -p /modb:x6/yy=b$rnd,b$rnd/moda:y=a1,a1/z=foo1)" 0 "0: <z>foo1</z>"
# unset conditional parameters
unset nr
unset clixon_util_path
rm -rf $dir

View file

@ -36,7 +36,6 @@ cat <<EOF > $cfg
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
<CLICON_MODULE_LIBRARY_RFC7895>true</CLICON_MODULE_LIBRARY_RFC7895>
</clixon-config>

View file

@ -27,7 +27,6 @@ cat <<EOF > $cfg
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
</clixon-config>
EOF

View file

@ -73,7 +73,7 @@ new "cli configure using encoded chars name <&"
expectfn "$clixon_cli -1 -f $cfg set interfaces interface fddi&< type ianaift:ethernetCsmacd" 0 ""
new "cli failed validate"
expectfn "$clixon_cli -1 -f $cfg -l o validate" 255 "Validate failed. Edit and try again or discard changes: application missing-element Mandatory variable <bad-element>type</bad-element>"
expectfn "$clixon_cli -1 -f $cfg -l o validate" 255 "Netconf error: application missing-element Mandatory variable <bad-element>type</bad-element>. Validate failed. Edit and try again or discard changes"
new "cli configure ip addr"
expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 ipv4 address 1.2.3.4 prefix-length 24" 0 "^$"

View file

@ -27,7 +27,6 @@ cat <<EOF > $cfg
<CLICON_CLI_HIST_SIZE>10</CLICON_CLI_HIST_SIZE>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
</clixon-config>
EOF
@ -125,5 +124,5 @@ fi
# kill backend
stop_backend -f $cfg
unset nr
rm -rf $dir

View file

@ -46,7 +46,6 @@ cat <<EOF > $cfg
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_SOCK>$dir/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
</clixon-config>
EOF

View file

@ -161,7 +161,8 @@ diff $mydir/kalle_db $mydir/candidate_db
new "datastore lock"
expectfn "$clixon_util_datastore $conf lock 756" 0 ""
#leaf-list
# unset conditional parameters
unset clixon_util_datastore
rm -rf $mydir

View file

@ -39,7 +39,6 @@ cat <<EOF > $cfg
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
<CLICON_MODULE_LIBRARY_RFC7895>true</CLICON_MODULE_LIBRARY_RFC7895>
</clixon-config>

View file

@ -250,15 +250,15 @@ expectfn "$clixon_cli -1 -f $cfg -l o validate" 0 "^$"
new "CLI set wrong acl-type"
expectfn "$clixon_cli -1 -f $cfg -l o set acls acl x type undefined" 0 "^$"
new "cli validate"
expectfn "$clixon_cli -1 -f $cfg -l o validate" 255 "Identityref validation failed"
new "cli validate acl-type"
expectfn "$clixon_cli -1 -f $cfg -l o validate" 255 "Netconf error: application operation-failed Identityref validation failed, undefined not derived from acl-base . Validate failed. Edit and try again or discard changes"
# test empty identityref list
new "cli set empty"
expectfn "$clixon_cli -1 -f $cfg -l o set e undefined" 0 "^$"
new "cli validate"
expectfn "$clixon_cli -1 -f $cfg -l o validate" 255 "Identityref validation failed"
new "cli validate empty"
expectfn "$clixon_cli -1 -f $cfg -l o validate" 255 "Netconf error: application operation-failed Identityref validation failed, undefined not derived from acl-base . Validate failed. Edit and try again or discard changes"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"

View file

@ -211,3 +211,6 @@ new "adv list add leaf-list"
testrun "$x0" "<e>32</e>"
rm -rf $dir
# unset conditional parameters
unset clixon_util_insert

View file

@ -63,3 +63,6 @@ if [ -n "$l" ]; then
fi
rm -rf $dir
# unset conditional parameters
unset make

318
test/test_instance_id.sh Executable file
View file

@ -0,0 +1,318 @@
#!/usr/bin/env bash
# INSTANCE-ID tests
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
: ${clixon_util_path:=clixon_util_path -D $DBG}
# Number of list/leaf-list entries
: ${nr:=100}
# XML file (alt provide it in stdin after xpath)
for (( i=1; i<9; i++ )); do
eval xml$i=$dir/xml$i.xml
done
ydir=$dir/yang
if [ ! -d $ydir ]; then
mkdir $ydir
fi
# XPATH binary search in ordered-by system lists
cat <<EOF > $ydir/moda.yang
module moda{
namespace "urn:example:a";
prefix a;
container x1{
description "list with single string key";
list y{
ordered-by system;
key k1;
leaf k1{
type string;
}
leaf z{
type string;
}
}
}
container x2{
description "list with single int key";
list y{
ordered-by system;
key k1;
leaf k1{
type uint32;
}
leaf z{
type string;
}
}
}
container x3{
description "list with double string keys";
list y{
ordered-by system;
key "k1 k2";
leaf k1{
type string;
}
leaf k2{
type string;
}
leaf z{
type string;
}
}
}
container x4{
description "leaf-list with int key";
leaf-list y{
type string;
}
}
list x5{
ordered-by system;
description "Direct under root";
key "k1";
leaf k1{
type string;
}
leaf z{
type string;
}
}
augment "/b:x6/b:yy" {
list y{
ordered-by system;
key "k1 k2";
leaf k1{
type string;
}
leaf k2{
type string;
}
leaf-list z{
type string;
}
}
}
container x7{
description "Single list, ordered by user";
list y{
ordered-by user;
key k1;
leaf k1{
type string;
}
leaf z{
type string;
}
}
}
container x8{
description "Single list state data";
config false;
list y{
key k1;
leaf k1{
type string;
}
leaf z{
type string;
}
}
}
}
EOF
# This is for augment usecase
cat <<EOF > $ydir/modb.yang
module modb{
namespace "urn:example:b";
prefix b;
container x6{
description "deep tree and augment";
list yy{
ordered-by system;
key "kk1 kk2";
leaf kk1{
type string;
}
leaf kk2{
type string;
}
leaf-list zz{
type string;
}
}
}
}
EOF
rnd=$(( ( RANDOM % $nr ) ))
# Single string key
new "generate list with $nr single string key to $xml1"
echo -n '<x1 xmlns="urn:example:a">' > $xml1
for (( i=0; i<$nr; i++ )); do
echo -n "<y><k1>a$i</k1><z>foo$i</z></y>" >> $xml1
done
echo -n '</x1>' >> $xml1
new "instance-id single string key k1=a$rnd"
echo "$clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:k1=\"a$rnd\"]"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:k1=\"a$rnd\"])" 0 "^0: <y><k1>a$rnd</k1><z>foo$rnd</z></y>$"
new "instance-id single string key /x1"
echo "$clixon_util_path -f $xml1 -y $ydir -p /a:x1"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1)" 0 "0: <x1 xmlns=\"urn:example:a\"><y><k1>a0</k1><z>foo0</z></y><y><k1>a1</k1><z>foo1</z></y>" # Assume at least two elements
new "instance-id position specific position 5"
echo "$clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[5]"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[5])" 0 "0: <y><k1>a13</k1><z>foo13</z>" # sort alphanumerivc wrong 1,10,2
new "instance-id single string key omit key"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y)" 0 '^0: <y><k1>a0</k1><z>foo0</z></y>
1: <y><k1>a0</k1><z>foo0</z></y>'
# Fails and error handling
new "instance-id single string search non-index"
echo "$clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:z=\"foo$rnd\"]"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:z=\"foo$rnd\"] )" 0 "<y><k1>a$rnd</k1><z>foo$rnd</z></y>$"
new "instance-id single string search non-index (two variables, index first)"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:k1=\"a$rnd\"][a:z=\"foo$rnd\"] )" 0 "<y><k1>a$rnd</k1><z>foo$rnd</z></y>$"
new "instance-id single string search non-index (two variables, index last)"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:z=\"foo$rnd\"][a:k1=\"a$rnd\"] )" 0 "<y><k1>a$rnd</k1><z>foo$rnd</z></y>$"
new "instance-id single string wrong module, notfound"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /xxx:x1/a:y[a:k1=\"a$rnd\"] 2> /dev/null)" 255 '^$'
new "instance-id single string no module, notfound"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /x1/a:y[a:k1=\"a$rnd\"] 2> /dev/null)" 255 '^$'
new "instance-id single string no sub-prefixes, notfound"
echo "$clixon_util_path -f $xml1 -y $ydir -p /a:x1/y[k1=\"a$rnd\"]"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/y[k1=\"a$rnd\"] 2> /dev/null)" 255 '^$'
new "instance-id single string two keys, notfound"
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:k1=a$rnd][a:k2=a$rnd] 2> /dev/null)" 255 '^$'
# Single int key
new "generate list with $nr single int key to $xml2"
echo -n '<x2 xmlns="urn:example:a">' > $xml2
for (( i=0; i<$nr; i++ )); do
echo -n "<y><k1>$i</k1><z>foo$i</z></y>" >> $xml2
done
echo -n '</x2>' >> $xml2
new "instance-id single int key k1=$rnd"
echo "$clixon_util_path -f $xml2 -y $ydir -p /a:x2/a:y[a:k1=\"$rnd\"]"
expectpart "$($clixon_util_path -f $xml2 -y $ydir -p /a:x2/a:y[a:k1=\"$rnd\"])" 0 "^0: <y><k1>$rnd</k1><z>foo$rnd</z></y>$"
# Double string key
new "generate list with $nr double string keys to $xml3 (two k2 entries per k1 key)"
echo -n '<x3 xmlns="urn:example:a">' > $xml3
for (( i=0; i<$nr; i++ )); do
echo -n "<y><k1>a$i</k1><k2>a$i</k2><z>foo$i</z></y>" >> $xml3
echo -n "<y><k1>a$i</k1><k2>b$i</k2><z>foob$i</z></y>" >> $xml3
done
# Add two rules with empty k2 string
echo -n "<y><k1>a0</k1><k2></k2><z>foo0</z></y>" >> $xml3
echo -n "<y><k1>a1</k1><k2></k2><z>foo1</z></y>" >> $xml3
echo -n '</x3>' >> $xml3
new "instance-id double string key k1=a$rnd k2=b$rnd"
echo "$clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[k1=\"a$rnd\"][k2=\"b$rnd\"]"
expectpart "$($clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[k1=\"a$rnd\"][k2=\"b$rnd\"])" 0 "0: <y><k1>a$rnd</k1><k2>b$rnd</k2><z>foob$rnd</z></y>"
new "instance-id double string key k1=a$rnd, - empty k2 string"
echo "$clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[k1=\"a1\"][k2=\"\"]"
expectpart "$($clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[k1=\"a1\"][k2=\"\"])" 0 "0: <y><k1>a1</k1><k2/><z>foo1</z></y>"
new "instance-id double string key k1=a$rnd, - no k2 string - three matches"
echo "$clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[k1=\"a1\"]"
expecteq "$($clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[k1=\"a1\"])" 0 "0: <y><k1>a1</k1><k2/><z>foo1</z></y>
1: <y><k1>a1</k1><k2>a1</k2><z>foo1</z></y>
2: <y><k1>a1</k1><k2>b1</k2><z>foob1</z></y>"
new "instance-id double string specific position 5"
echo "$clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[5]"
expectpart "$($clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[5])" 0 "0: <y><k1>a1</k1><k2>b1</k2><z>foob1</z></y>" # sort alphanumerivc wrong 1,10,2
# Leaf-list
new "generate leaf-list int keys to $xml4"
echo -n '<x4 xmlns="urn:example:a">' > $xml4
for (( i=0; i<$nr; i++ )); do
echo -n "<y>a$i</y>" >> $xml4
done
echo -n '</x4>' >> $xml4
new "instance-id leaf-list k1=a$rnd"
echo "$clixon_util_path -f $xml4 -y $ydir -p /a:x4/a:y[.=\"a$rnd\"]"
expectpart "$($clixon_util_path -f $xml4 -y $ydir -p /a:x4/a:y[.=\"a$rnd\"])" 0 "^0: <y>a$rnd</y>$"
# Single string key direct under root
new "generate list with $nr single string key to $xml5"
echo -n '' > $xml5
for (( i=0; i<$nr; i++ )); do
echo -n "<x5 xmlns=\"urn:example:a\"><k1>a$i</k1><z>foo$i</z></x5>" >> $xml5
done
new "instance-id direct under root single string key k1=a$rnd"
echo ""
expectpart "$($clixon_util_path -f $xml5 -y $ydir -p /a:x5[k1=\"a$rnd\"])" 0 "^0: <x5 xmlns=\"urn:example:a\"><k1>a$rnd</k1><z>foo$rnd</z></x5>$"
# Depth and augment
# Deep augmented xml path
new "generate deep list with augment"
echo -n '<x6 xmlns="urn:example:b">' > $xml6
for (( i=0; i<$nr; i++ )); do
echo -n "<yy><kk1>b$i</kk1><kk2>b$i</kk2><zz>foo$i</zz>" >> $xml6
for (( j=0; j<3; j++ )); do
echo -n "<y xmlns=\"urn:example:a\"><k1>a$j</k1><k2>a$j</k2><z>foo$j</z></y>" >> $xml6
done
echo -n "</yy>" >> $xml6
done
echo -n '</x6>' >> $xml6
new "instance-id double string key b$rnd,b$rnd in mod b"
echo "$clixon_util_path -f $xml6 -y $ydir -p /b:x6/b:yy[kk1=\"b$rnd\"][kk2=\"b$rnd\"]"
expectpart "$($clixon_util_path -f $xml6 -y $ydir -p /b:x6/b:yy[kk1=\"b$rnd\"][kk2=\"b$rnd\"])" 0 "0: <yy><kk1>b$rnd</kk1><kk2>b$rnd</kk2><zz>foo$rnd</zz><y xmlns=\"urn:example:a\"><k1>a0</k1><k2>a0</k2><z>foo0</z></y><y xmlns=\"urn:example:a\"><k1>a1</k1><k2>a1</k2><z>foo1</z></y><y xmlns=\"urn:example:a\"><k1>a2</k1><k2>a2</k2><z>foo2</z></y></yy>"
new "instance-id double string key a$rnd,b$rnd in modb + augmented in moda"
expectpart "$($clixon_util_path -f $xml6 -y $ydir -p /b:x6/b:yy[kk1=\"b$rnd\"][kk2=\"b$rnd\"]/a:y[k1=\"a1\"][k2=\"a1\"]/a:z[.=\"foo1\"])" 0 "0: <z>foo1</z>"
# Single list ordered by user
new "generate list with $nr single string key to $xml7"
echo -n '<x7 xmlns="urn:example:a">' > $xml7
for (( i=0; i<$nr; i++ )); do
echo -n "<y><k1>a$i</k1><z>foo$i</z></y>" >> $xml7
done
echo -n '</x7>' >> $xml7
new "instance-id single string key k1=a$rnd ordered by user"
echo "$clixon_util_path -f $xml7 -y $ydir -p /a:x7/a:y[a:k1=\"a$rnd\"]"
expectpart "$($clixon_util_path -f $xml7 -y $ydir -p /a:x7/a:y[a:k1=\"a$rnd\"])" 0 "^0: <y><k1>a$rnd</k1><z>foo$rnd</z></y>$"
# Single list state data (non-config)
new "generate list with $nr single string key to $xml8"
echo -n '<x8 xmlns="urn:example:a">' > $xml8
for (( i=0; i<$nr; i++ )); do
echo -n "<y><k1>a$i</k1><z>foo$i</z></y>" >> $xml8
done
echo -n '</x8>' >> $xml8
new "instance-id single string key k1=a$rnd ordered by user"
expectpart "$($clixon_util_path -f $xml8 -y $ydir -p /a:x8/a:y[a:k1=\"a$rnd\"])" 0 "^0: <y><k1>a$rnd</k1><z>foo$rnd</z></y>$"
rm -rf $dir
unset nr
unset clixon_util_path # for other script reusing it

View file

@ -129,3 +129,7 @@ expecteofx "$clixon_util_json -j -y $fyang" 0 "$JSON" "$JSON"
fi
rm -rf $dir
# unset conditional parameters
unset clixon_util_json
unset clixon_util_xml

View file

@ -21,7 +21,6 @@ cat <<EOF > $cfg
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
</clixon-config>
EOF
@ -141,7 +140,7 @@ new "leafref add non-existing ref"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><default-address xmlns="urn:example:clixon"><absname>eth3</absname><address>10.0.4.6</address></default-address></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "leafref validate"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>eth3</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No such leaf</error-message></rpc-error></rpc-reply>]]>]]>$'
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>eth3</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No leaf eth3 matching path /if:interfaces/if:interface/if:name</error-message></rpc-error></rpc-reply>]]>]]>$'
#new "leafref wrong ref"
#expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><default-address xmlns="urn:example:clixon"><wrong>eth3</wrong><address>10.0.4.6</address></default-address></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
@ -171,7 +170,7 @@ new "leafref delete leaf"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><interface nc:operation="delete"><name>eth0</name></interface></interfaces></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>'
new "leafref validate (should fail)"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>eth0</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No such leaf</error-message></rpc-error></rpc-reply>]]>]]>$'
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>eth0</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No leaf eth0 matching path /if:interfaces/if:interface/if:name</error-message></rpc-error></rpc-reply>]]>]]>$'
new "leafref discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"

282
test/test_leafref_augment.sh Executable file
View file

@ -0,0 +1,282 @@
#!/usr/bin/env bash
# Yang leafref + augment + grouping taking from a more complex netgate errorcase
# A main yang spec: leafref
# and a secondary yang spec: augment
# module leafref has a primary construct (sender) and a leafref typedef
# module augment has an augment and a grouping from where it uses the leafref typedef
# Which means that you should first have xml such as:
# <sender>
# <name>x</name>
# </sender>
# and you can then track it via for example (extra levels for debugging):
# <sender>
# <name>y</name>
# <stub> # original
# <extra> # augment
# <track> # grouping
# <sender>
# <name>x</name> <----
# </sender>
# </track>
# </extra>
# </stub>
# </sender>
#
# There is also test for using prefixes or not, as well as swithcing prefix between the main module and
# it import statement.
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
APPNAME=example
cfg=$dir/conf_yang.xml
fyang1=$dir/leafref.yang
fyang2=$dir/augment.yang
cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_FILE>$fyang2</CLICON_YANG_MAIN_FILE>
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
</clixon-config>
EOF
# NOTE prefix "example" used in module different from "ex" used in mport of that module
cat <<EOF > $fyang1
module leafref{
yang-version 1.1;
namespace "urn:example:example";
prefix example;
typedef sender-ref {
description "For testing leafref across augment and grouping";
type leafref {
path "/ex:sender/ex:name";
}
}
typedef sender-ref-local {
description "For testing leafref local";
type leafref {
path "/example:sender/example:name";
}
}
list sender{
key name;
leaf name{
type string;
}
container stub{
description "Here is where augmentation is done";
}
leaf ref{
description "top-level ref (wrong prefix)";
type sender-ref;
}
leaf ref-local{
description "top-level ref (right prefix)";
type sender-ref-local;
}
}
}
EOF
cat <<EOF > $fyang2
module augment{
yang-version 1.1;
namespace "urn:example:augment";
prefix aug;
import leafref {
description "Note different from canonical (leafref module own prefix is 'example'";
prefix "ex";
}
grouping attributes {
container track{
description "replicates original structure but only references original";
list sender{
description "reference using path in typedef";
key name;
leaf name{
type ex:sender-ref;
}
}
list senderdata{
description "reference using path inline in data (not typedef)";
key name;
leaf name{
type leafref {
path "/ex:sender/ex:name";
}
}
}
}
}
augment "/ex:sender/ex:stub" {
description "Main leafref/sender stub.";
container extra{
presence "ensuring it is there";
uses attributes;
}
}
}
EOF
new "test params: -f $cfg"
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then
err
fi
new "start backend -s init -f $cfg"
start_backend -s init -f $cfg
new "waiting"
wait_backend
fi
# Test top-level, default prefix, wring leafref prefix and typedef path
XML=$(cat <<EOF
<sender xmlns="urn:example:example">
<name>x</name>
</sender>
<sender xmlns="urn:example:example">
<name>y</name>
<ref>x</ref>
</sender>
EOF
)
new "leafref augment+leafref config top-level"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></target><config>$XML</config></edit-config></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "leafref augment+leafref validate top-level wrong prefix"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>x</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No leaf x matching path /ex:sender/ex:name</error-message></rpc-error></rpc-reply>]]>]]>$"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
# Test top-level, default prefix, correct leafref and typedef path
XML=$(cat <<EOF
<sender xmlns="urn:example:example">
<name>x</name>
</sender>
<sender xmlns="urn:example:example">
<name>y</name>
<ref-local>x</ref-local>
</sender>
EOF
)
# Use augment + explicit prefixes, correct leafref and typedef path
XML=$(cat <<EOF
<sender xmlns="urn:example:example">
<name>x</name>
</sender>
<sender xmlns="urn:example:example">
<name>y</name>
<stub>
<aug:extra xmlns:aug="urn:example:augment">
<aug:track>
<aug:sender>
<aug:name>x</aug:name>
</aug:sender>
</aug:track>
</aug:extra>
</stub>
</sender>
EOF
)
new "leafref augment+leafref config"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></target><config>$XML</config></edit-config></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "leafref augment+leafref validate"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
# Use augment, default prefixes, wrong leafref and typedef path
XML=$(cat <<EOF
<sender xmlns="urn:example:example">
<name>x</name>
</sender>
<sender xmlns="urn:example:example">
<name>y</name>
<stub>
<extra xmlns="urn:example:augment">
<track>
<sender>
<name>xxx</name>
</sender>
</track>
</extra>
</stub>
</sender>
EOF
)
new "leafref augment+leafref config wrong ref"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></target><config>$XML</config></edit-config></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "leafref augment+leafref validate wrong ref"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>xxx</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No leaf xxx matching path /ex:sender/ex:name</error-message></rpc-error></rpc-reply>]]>]]>$"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
# Use augment, default prefixes, correct leafref and in-data path
XML=$(cat <<EOF
<sender xmlns="urn:example:example">
<name>x</name>
</sender>
<sender xmlns="urn:example:example">
<name>y</name>
<stub>
<extra xmlns="urn:example:augment">
<track>
<senderdata>
<name>x</name>
</senderdata>
</track>
</extra>
</stub>
</sender>
EOF
)
new "leafref augment+leafref config in-data"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></target><config>$XML</config></edit-config></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "leafref augment+leafref validate in-data"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
if [ $BE -eq 0 ]; then
exit # BE
fi
new "Kill backend"
# Check if premature kill
pid=$(pgrep -u root -f clixon_backend)
if [ -z "$pid" ]; then
err "backend already dead"
fi
# kill backend
stop_backend -f $cfg
rm -rf $dir

171
test/test_leafref_state.sh Executable file
View file

@ -0,0 +1,171 @@
#!/usr/bin/env bash
# Yang leafref + state tests
# The difficulty here is a "leafref" in state data that references config-data.
# Problem being that config-data from running needs to be mergedwith state data and filtered/cropped
# correctly
#
# The YANG has two parts, one config part (sender-config) and one state part (sender-state)
# The leafref in the sender-state part references a leaf in the sender-config part
# Netconf tests are made to get state, state+config, using content attribute config/nonconfig/all
# with different paths.
# Using the -sS <file> state capability of the main example, that is why CLICON_BACKEND_DIR is
# /usr/local/lib/$APPNAME/backend so that the main backend plugins is included.
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
APPNAME=example
cfg=$dir/conf_yang.xml
fstate=$dir/state.xml
fyang=$dir/leafref.yang
cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_MODULE_LIBRARY_RFC7895>false</CLICON_MODULE_LIBRARY_RFC7895>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
</clixon-config>
EOF
# NOTE prefix "example" used in module different from "ex" used in mport of that module
cat <<EOF > $fyang
module leafref{
yang-version 1.1;
namespace "urn:example:example";
prefix ex;
list sender-config{
description "Main config of senders";
key name;
leaf name{
type string;
}
}
list sender-state{
description "State referencing configured senders";
config false;
key ref;
leaf ref{
type leafref {
path "/ex:sender-config/ex:name";
}
}
}
}
EOF
cat <<EOF > $fstate
<sender-state xmlns="urn:example:example">
<ref>x</ref>
</sender-state>
EOF
new "test params: -f $cfg -- -sS $fstate"
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then
err
fi
new "start backend -s init -f $cfg -- -sS $fstate"
start_backend -s init -f $cfg -- -sS $fstate
new "waiting"
wait_backend
fi
# Test top-level, default prefix, wring leafref prefix and typedef path
XML=$(cat <<EOF
<sender-config xmlns="urn:example:example">
<name>x</name>
</sender-config>
EOF
)
new "leafref config sender x"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></target><config>$XML</config></edit-config></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "netconf commit"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
# Get path=/, state vs config
new "netconf get / config+state"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get content="all"><filter type="xpath" select="/"/></get></rpc>]]>]]>' '^<rpc-reply><data><sender-config xmlns="urn:example:example"><name>x</name></sender-config><sender-state xmlns="urn:example:example"><ref>x</ref></sender-state></data></rpc-reply>]]>]]>$'
new "netconf get / state-only"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get content="nonconfig"><filter type="xpath" select="/"/></get></rpc>]]>]]>' '^<rpc-reply><data><sender-state xmlns="urn:example:example"><ref>x</ref></sender-state></data></rpc-reply>]]>]]>$'
new "netconf get / config-only"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get content="config"><filter type="xpath" select="/"/></get></rpc>]]>]]>' '^<rpc-reply><data><sender-config xmlns="urn:example:example"><name>x</name></sender-config></data></rpc-reply>]]>]]>$'
# Get path=/sender-state, state vs config
new "netconf get /sender-state config+state"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get content="all"><filter type="xpath" select="/sender-state" xmlns="urn:example:example"/></get></rpc>]]>]]>' '^<rpc-reply><data><sender-state xmlns="urn:example:example"><ref>x</ref></sender-state></data></rpc-reply>]]>]]>$'
new "netconf get /sender-state state-only"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get content="nonconfig"><filter type="xpath" select="/sender-state" xmlns="urn:example:example"/></get></rpc>]]>]]>' '^<rpc-reply><data><sender-state xmlns="urn:example:example"><ref>x</ref></sender-state></data></rpc-reply>]]>]]>$'
new "netconf get /sender-state config-only"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get content="config"><filter type="xpath" select="/sender-state" xmlns="urn:example:example"/></get></rpc>]]>]]>' '^<rpc-reply><data/></rpc-reply>]]>]]>$'
# Get path=/sender-config, state vs config
new "netconf get /sender-config config+state"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get content="all"><filter type="xpath" select="/sender-config" xmlns="urn:example:example"/></get></rpc>]]>]]>' '^<rpc-reply><data><sender-config xmlns="urn:example:example"><name>x</name></sender-config></data></rpc-reply>]]>]]>$'
new "netconf get /sender-config state-only"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get content="nonconfig"><filter type="xpath" select="/sender-config" xmlns="urn:example:example"/></get></rpc>]]>]]>' '^<rpc-reply><data/></rpc-reply>]]>]]>$'
new "netconf get /sender-config config-only"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get content="config"><filter type="xpath" select="/sender-config" xmlns="urn:example:example"/></get></rpc>]]>]]>' '^<rpc-reply><data><sender-config xmlns="urn:example:example"><name>x</name></sender-config></data></rpc-reply>]]>]]>$'
# delete x, add y
XML=$(cat <<EOF
<sender-config xmlns="urn:example:example" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="delete">
<name>x</name>
</sender-config>
<sender-config xmlns="urn:example:example">
<name>y</name>
</sender-config>
EOF
)
# Negative tests, start with remove x and and add y instead
new "leafref config delete sender x add y"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></target><config>$XML</config></edit-config></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "netconf commit"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
# Leafref wrong
new "netconf get / config+state should fail"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get content="all"><filter type="xpath" select="/"/></get></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-info><bad-element>x</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No leaf x matching path /ex:sender-config/ex:name Internal error, state callback returned invalid XML</error-message></rpc-error></rpc-reply>]]>]]>$'
new "netconf get / state-only should fail"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get content="nonconfig"><filter type="xpath" select="/"/></get></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-info><bad-element>x</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No leaf x matching path /ex:sender-config/ex:name Internal error, state callback returned invalid XML</error-message></rpc-error></rpc-reply>]]>]]>$'
new "netconf get / config-only ok"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get content="config"><filter type="xpath" select="/"/></get></rpc>]]>]]>' '^<rpc-reply><data><sender-config xmlns="urn:example:example"><name>y</name></sender-config></data></rpc-reply>]]>]]>$'
if [ $BE -eq 0 ]; then
exit # BE
fi
new "Kill backend"
# Check if premature kill
pid=$(pgrep -u root -f clixon_backend)
if [ -z "$pid" ]; then
err "backend already dead"
fi
# kill backend
stop_backend -f $cfg
rm -rf $dir

View file

@ -20,7 +20,6 @@ cat <<EOF > $cfg
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
</clixon-config>
EOF

View file

@ -28,7 +28,6 @@ cat <<EOF > $cfg
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>

View file

@ -227,3 +227,6 @@ new "Credentials: mode=except, fam=UNIX user=admin sudo"
testrun except $USER IPv4 127.0.0.1 "$ERROR" ""
rm -rf $dir
# unset conditional parameters
unset clixon_util_socket

View file

@ -27,7 +27,6 @@ cat <<EOF > $cfg
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
@ -197,3 +196,6 @@ new "nacm enabled, exec default permit, write permit (expect fail)"
testrun true deny permit permit 2 0 2
rm -rf $dir
# unset conditional parameters
unset format

View file

@ -31,7 +31,6 @@ cat <<EOF > $cfg
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_MODULE_LIBRARY_RFC7895>false</CLICON_MODULE_LIBRARY_RFC7895>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
<CLICON_NACM_MODE>external</CLICON_NACM_MODE>

View file

@ -42,7 +42,6 @@ cat <<EOF > $cfg
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>

View file

@ -48,7 +48,6 @@ cat <<EOF > $cfg
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>

Some files were not shown because too many files have changed in this diff Show more