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:
parent
e8ae628d06
commit
19e21be0bc
132 changed files with 6241 additions and 2332 deletions
42
CHANGELOG.md
42
CHANGELOG.md
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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
7
configure
vendored
|
|
@ -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
|
||||
|
|
|
|||
11
configure.ac
11
configure.ac
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
64
lib/clixon/clixon_yang_parse_lib.h
Normal file
64
lib/clixon/clixon_yang_parse_lib.h
Normal 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_ */
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
68
lib/src/clixon_api_path_parse.h
Normal file
68
lib/src/clixon_api_path_parse.h
Normal 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_ */
|
||||
150
lib/src/clixon_api_path_parse.l
Normal file
150
lib/src/clixon_api_path_parse.l
Normal 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;
|
||||
}
|
||||
|
||||
298
lib/src/clixon_api_path_parse.y
Normal file
298
lib/src/clixon_api_path_parse.y
Normal 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->"); }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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");}
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
|
|
|
|||
|
|
@ -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 *);
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
1360
lib/src/clixon_yang_parse_lib.c
Normal file
1360
lib/src/clixon_yang_parse_lib.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
74
test/cicd/Makefile.in
Normal 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
14
test/cicd/README.md
Normal 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
39
test/cicd/cicd.sh
Executable 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
11
test/cicd/cligen-mk.sh
Normal 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
|
||||
8
test/cicd/clixon-config.sh
Normal file
8
test/cicd/clixon-config.sh
Normal 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
16
test/cicd/clixon-mk.sh
Normal 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)
|
||||
20
test/lib.sh
20
test/lib.sh
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
265
test/test_api.sh
Executable 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
260
test/test_api_path.sh
Executable 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
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 "^$"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>]]>]]>$"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
318
test/test_instance_id.sh
Executable 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
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
282
test/test_leafref_augment.sh
Executable 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
171
test/test_leafref_state.sh
Executable 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue