* Pushed tag to 4.0.1.PRE
* Restconf RFC 8040 increased feature compliance
* Cache-Control: no-cache added in HTTP responses (RFC Section 5.5)
* Restconf monitoring capabilities (RFC Section 9.1)
* Added support for Yang extensions
* New plugin callback: ca_extension
* Main backend example includes example code on how to implement a Yang extension in a plugin.
* JSON changes
* Non-pretty-print output removed all extra spaces.
* Example: `{"nacm-example:x": 42}` --> {"nacm-example:x":42}`
* Empty JSON container changed from `null` to `{}`.
* Empty list and leafs remain as `null`
* Removed unnecessary configure dependencies
* libnsl, libcrypt, libm, if_vlan,...
* pseudo-plugin added, to enable callbacks also for main programs. Useful for extensions
* Yang Unique statements with multiple schema identifiers did not work on some platforms due to memory error.
This commit is contained in:
parent
fe46a0e093
commit
e7b60619da
60 changed files with 1619 additions and 568 deletions
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -91,14 +91,17 @@ enum array_element_type{
|
|||
};
|
||||
|
||||
enum childtype{
|
||||
NULL_CHILD=0, /* eg <a/> no children */
|
||||
NULL_CHILD=0, /* eg <a/> no children. Translated to null if in
|
||||
* array or leaf terminal, and to {} if proper object, ie container.
|
||||
* anyxml/anydata?
|
||||
*/
|
||||
BODY_CHILD, /* eg one child which is a body, eg <a>1</a> */
|
||||
ANY_CHILD, /* eg <a><b/></a> or <a><b/><c/></a> */
|
||||
};
|
||||
|
||||
/*! x is element and has exactly one child which in turn has none
|
||||
* remove attributes from x
|
||||
* Clone from clixon_xml_map.c
|
||||
* @see tleaf in clixon_xml_map.c
|
||||
*/
|
||||
static enum childtype
|
||||
child_type(cxobj *x)
|
||||
|
|
@ -372,6 +375,7 @@ xml2json1_cbuf(cbuf *cb,
|
|||
modname0 = modname; /* modname0 is ancestor ns passed to child */
|
||||
}
|
||||
childt = child_type(x);
|
||||
|
||||
if (pretty==2)
|
||||
cprintf(cb, "#%s_array, %s_child ",
|
||||
arraytype2str(arraytype),
|
||||
|
|
@ -393,11 +397,17 @@ xml2json1_cbuf(cbuf *cb,
|
|||
cprintf(cb, "%*s\"", pretty?(level*JSON_INDENT):0, "");
|
||||
if (modname)
|
||||
cprintf(cb, "%s:", modname);
|
||||
cprintf(cb, "%s\": ", xml_name(x));
|
||||
cprintf(cb, "%s\":%s", xml_name(x), pretty?" ":"");
|
||||
}
|
||||
switch (childt){
|
||||
case NULL_CHILD:
|
||||
cprintf(cb, "null");
|
||||
/* If x is a container, use {} instead of null
|
||||
* That is, x is not a list or leaf
|
||||
*/
|
||||
if (ys && yang_keyword_get(ys) == Y_CONTAINER)
|
||||
cprintf(cb, "{}");
|
||||
else
|
||||
cprintf(cb, "null");
|
||||
break;
|
||||
case BODY_CHILD:
|
||||
break;
|
||||
|
|
@ -413,7 +423,7 @@ xml2json1_cbuf(cbuf *cb,
|
|||
cprintf(cb, "%*s\"", pretty?(level*JSON_INDENT):0, "");
|
||||
if (modname)
|
||||
cprintf(cb, "%s:", modname);
|
||||
cprintf(cb, "%s\": ", xml_name(x));
|
||||
cprintf(cb, "%s\":%s", xml_name(x), pretty?" ":"");
|
||||
level++;
|
||||
cprintf(cb, "[%s%*s",
|
||||
pretty?"\n":"",
|
||||
|
|
@ -443,7 +453,7 @@ xml2json1_cbuf(cbuf *cb,
|
|||
case BODY_CHILD:
|
||||
break;
|
||||
case ANY_CHILD:
|
||||
cprintf(cb, "{ %s", pretty?"\n":"");
|
||||
cprintf(cb, "{%s", pretty?"\n":"");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -169,14 +169,14 @@ clixon_plugin_find(clicon_handle h,
|
|||
}
|
||||
|
||||
/*! Load a dynamic plugin object and call its init-function
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] file Which plugin to load
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] file Which plugin to load
|
||||
* @param[in] function Which function symbol to load and call
|
||||
* @param[in] dlflags See man(3) dlopen
|
||||
* @param[in] dlflags See man(3) dlopen
|
||||
* @param[out] cpp Clixon plugin structure (if retval is 1)
|
||||
* @retval 1 OK
|
||||
* @retval 0 Failed load, log, skip and continue with other plugins
|
||||
* @retval -1 Error
|
||||
* @retval 1 OK
|
||||
* @retval 0 Failed load, log, skip and continue with other plugins
|
||||
* @retval -1 Error
|
||||
* @see clixon_plugins_load Load all plugins
|
||||
*/
|
||||
static int
|
||||
|
|
@ -236,8 +236,6 @@ plugin_load_one(clicon_handle h,
|
|||
if ((p=strrchr(name, '.')) != NULL)
|
||||
*p = '\0';
|
||||
/* Copy name to struct */
|
||||
memcpy(cp->cp_name, name, strlen(name)+1);
|
||||
|
||||
snprintf(cp->cp_name, sizeof(cp->cp_name), "%*s",
|
||||
(int)strlen(name), name);
|
||||
cp->cp_api = *api;
|
||||
|
|
@ -274,7 +272,7 @@ clixon_plugins_load(clicon_handle h,
|
|||
struct dirent *dp = NULL;
|
||||
int i;
|
||||
char filename[MAXPATHLEN];
|
||||
clixon_plugin *cp;
|
||||
clixon_plugin *cp = NULL;
|
||||
int ret;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
|
|
@ -305,6 +303,47 @@ done:
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Create a pseudo plugin so that a main function can register callbacks
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] name Plugin name
|
||||
* @param[out] cpp Clixon plugin structure (direct pointer)
|
||||
* @retval 0 OK, with cpp set
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
clixon_pseudo_plugin(clicon_handle h,
|
||||
char *name,
|
||||
clixon_plugin **cpp)
|
||||
{
|
||||
int retval = -1;
|
||||
clixon_plugin *cp = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
|
||||
/* Create a pseudo plugins */
|
||||
/* Note: sizeof clixon_plugin_api which is largest of clixon_plugin_api:s */
|
||||
if ((cp = (clixon_plugin *)malloc(sizeof(struct clixon_plugin))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(cp, 0, sizeof(struct clixon_plugin));
|
||||
snprintf(cp->cp_name, sizeof(cp->cp_name), "%*s", (int)strlen(name), name);
|
||||
|
||||
_clixon_nplugins++;
|
||||
if ((_clixon_plugins = realloc(_clixon_plugins, _clixon_nplugins*sizeof(clixon_plugin))) == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "realloc");
|
||||
goto done;
|
||||
}
|
||||
_clixon_plugins[_clixon_nplugins-1] = *cp;
|
||||
*cpp = &_clixon_plugins[_clixon_nplugins-1];
|
||||
|
||||
retval = 0;
|
||||
done:
|
||||
if (cp)
|
||||
free(cp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Call plugin_start in all plugins
|
||||
* @param[in] h Clicon handle
|
||||
* Call plugin start functions (if defined)
|
||||
|
|
@ -395,11 +434,45 @@ clixon_plugin_auth(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Callback for a yang extension (unknown) statement
|
||||
* Called at parsing of yang module containing a statement of an extension.
|
||||
* A plugin may identify the extension and perform actions
|
||||
* on the yang statement, such as transforming the yang.
|
||||
* A callback is made for every statement, which means that several calls per
|
||||
* extension can be made.
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] yext Yang node of extension
|
||||
* @param[in] ys Yang node of (unknown) statement belonging to extension
|
||||
* @retval 0 OK, all callbacks executed OK
|
||||
* @retval -1 Error in one callback
|
||||
*/
|
||||
int
|
||||
clixon_plugin_extension(clicon_handle h,
|
||||
yang_stmt *yext,
|
||||
yang_stmt *ys)
|
||||
{
|
||||
clixon_plugin *cp;
|
||||
int i;
|
||||
plgextension_t *extfn; /* Plugin extension fn */
|
||||
int retval = 1;
|
||||
|
||||
for (i = 0; i < _clixon_nplugins; i++) {
|
||||
cp = &_clixon_plugins[i];
|
||||
if ((extfn = cp->cp_api.ca_extension) == NULL)
|
||||
continue;
|
||||
if ((retval = extfn(h, yext, ys)) < 0) {
|
||||
clicon_debug(1, "plugin_extension() failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
* RPC callbacks for both client/frontend and backend plugins.
|
||||
* RPC callbacks are explicitly registered in the plugin_init() function
|
||||
* with a tag and a function
|
||||
* WHen the the tag is encountered, the callback is called.
|
||||
* When the the tag is encountered, the callback is called.
|
||||
* Primarily backend, but also netconf and restconf frontend plugins.
|
||||
* CLI frontend so far have direct callbacks, ie functions in the cligen
|
||||
* specification are directly dlsym:ed to the CLI plugin.
|
||||
|
|
@ -511,11 +584,11 @@ rpc_callback_call(clicon_handle h,
|
|||
|
||||
if (rpc_cb_list == NULL)
|
||||
return 0;
|
||||
name = xml_name(xe);
|
||||
prefix = xml_prefix(xe);
|
||||
xml2ns(xe, prefix, &namespace);
|
||||
rc = rpc_cb_list;
|
||||
do {
|
||||
name = xml_name(xe);
|
||||
prefix = xml_prefix(xe);
|
||||
xml2ns(xe, prefix, &namespace);
|
||||
if (strcmp(rc->rc_name, name) == 0 &&
|
||||
namespace && rc->rc_namespace &&
|
||||
strcmp(rc->rc_namespace, namespace) == 0){
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@
|
|||
* if ((vec = clicon_strsep("/home/user/src/clixon", "/", &nvec)) == NULL)
|
||||
* err;
|
||||
* for (i=0; i<nvec; i++){
|
||||
* v = vec[i++];
|
||||
* v = vec[i];
|
||||
* ...
|
||||
* }
|
||||
* free(vec);
|
||||
|
|
|
|||
|
|
@ -110,7 +110,9 @@ isxmlns(cxobj *x)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! x is element and has eactly one child which in turn has none */
|
||||
/*! x is element and has eactly one child which in turn has none
|
||||
* @see child_type in clixon_json.c
|
||||
*/
|
||||
static int
|
||||
tleaf(cxobj *x)
|
||||
{
|
||||
|
|
@ -2420,7 +2422,7 @@ api_path2xpath_cvv(cvec *api_path,
|
|||
nodeid = cv_name_get(cv);
|
||||
if (nodeid_split(nodeid, &prefix, &name) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s [%d] cvname:%s", __FUNCTION__, i, name);
|
||||
clicon_debug(1, "%s [%d] cvname: %s:%s", __FUNCTION__, i, prefix?prefix:"", name);
|
||||
if (i == offset){ /* top-node */
|
||||
if (prefix == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "'%s': Expected prefix:name", nodeid);
|
||||
|
|
|
|||
|
|
@ -289,8 +289,6 @@ ys_free1(yang_stmt *ys)
|
|||
{
|
||||
if (ys->ys_argument)
|
||||
free(ys->ys_argument);
|
||||
if (ys->ys_extra)
|
||||
free(ys->ys_extra);
|
||||
if (ys->ys_cv)
|
||||
cv_free(ys->ys_cv);
|
||||
if (ys->ys_cvec)
|
||||
|
|
@ -309,7 +307,7 @@ ys_free1(yang_stmt *ys)
|
|||
* @see ys_free Deallocate yang node
|
||||
* @note Do not call this in a loop of yang children (unless you know what you are doing)
|
||||
*/
|
||||
static yang_stmt *
|
||||
yang_stmt *
|
||||
ys_prune(yang_stmt *yp,
|
||||
int i)
|
||||
{
|
||||
|
|
@ -323,7 +321,7 @@ ys_prune(yang_stmt *yp,
|
|||
memmove(&yp->ys_stmt[i],
|
||||
&yp->ys_stmt[i+1],
|
||||
size);
|
||||
yc = yp->ys_stmt[yp->ys_len--] = NULL;;
|
||||
yp->ys_stmt[yp->ys_len--] = NULL;;
|
||||
done:
|
||||
return yc;
|
||||
}
|
||||
|
|
@ -411,11 +409,6 @@ ys_cp(yang_stmt *ynew,
|
|||
clicon_err(OE_YANG, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
if (yold->ys_extra)
|
||||
if ((ynew->ys_extra = strdup(yold->ys_extra)) == NULL){
|
||||
clicon_err(OE_YANG, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
if (yold->ys_cv)
|
||||
if ((ynew->ys_cv = cv_dup(yold->ys_cv)) == NULL){
|
||||
clicon_err(OE_YANG, errno, "cv_dup");
|
||||
|
|
@ -473,6 +466,8 @@ ys_dup(yang_stmt *old)
|
|||
*
|
||||
* @param[in] ys_parent Add child to this parent
|
||||
* @param[in] ys_child Add this child
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* Also add parent to child as up-pointer
|
||||
*/
|
||||
int
|
||||
|
|
@ -646,7 +641,7 @@ yang_find_datanode(yang_stmt *yn,
|
|||
goto match;
|
||||
}
|
||||
} /* Y_CHOICE */
|
||||
else
|
||||
else{
|
||||
if (yang_datanode(ys)){
|
||||
if (argument == NULL)
|
||||
ysmatch = ys;
|
||||
|
|
@ -656,6 +651,7 @@ yang_find_datanode(yang_stmt *yn,
|
|||
if (ysmatch)
|
||||
goto match;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Special case: if not match and yang node is module or submodule, extend
|
||||
* search to include submodules */
|
||||
|
|
@ -1794,44 +1790,46 @@ ys_populate_unique(clicon_handle h,
|
|||
/*! Populate unknown node with extension
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] ys The yang statement (unknown) to populate.
|
||||
* RFC 7950 Sec 7.19:
|
||||
* If no "argument" statement is present, the keyword expects no argument when
|
||||
* it is used.
|
||||
*/
|
||||
static int
|
||||
ys_populate_unknown(clicon_handle h,
|
||||
yang_stmt *ys)
|
||||
{
|
||||
int retval = -1;
|
||||
int cvret;
|
||||
char *reason = NULL;
|
||||
int retval = -1;
|
||||
yang_stmt *ymod;
|
||||
yang_stmt *yext; /* extension */
|
||||
char *prefix = NULL;
|
||||
char *id = NULL;
|
||||
char *extra;
|
||||
char *argument; /* This is the unknown optional argument */
|
||||
cg_var *cv;
|
||||
|
||||
if ((extra = ys->ys_extra) == NULL)
|
||||
goto ok;
|
||||
/* Find extension, if found, store it as unknown, if not,
|
||||
break for error */
|
||||
if (nodeid_split(yang_argument_get(ys), &prefix, &id) < 0)
|
||||
goto done;
|
||||
if ((ymod = yang_find_module_by_prefix(ys, prefix)) == NULL)
|
||||
goto ok; /* shouldnt happen */
|
||||
if (yang_find(ymod, Y_EXTENSION, id) == NULL){
|
||||
clicon_err(OE_YANG, errno, "Extension %s:%s not found", prefix, id);
|
||||
if ((ymod = yang_find_module_by_prefix(ys, prefix)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "Extension %s:%s, module not found", prefix, id);
|
||||
goto done;
|
||||
}
|
||||
if ((ys->ys_cv = cv_new(CGV_STRING)) == NULL){
|
||||
clicon_err(OE_YANG, errno, "cv_new");
|
||||
if ((yext = yang_find(ymod, Y_EXTENSION, id)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "Extension %s:%s not found", prefix, id);
|
||||
goto done;
|
||||
}
|
||||
if ((cvret = cv_parse1(extra, ys->ys_cv, &reason)) < 0){ /* error */
|
||||
clicon_err(OE_YANG, errno, "parsing cv");
|
||||
goto done;
|
||||
/* Optional argument (only if "argument") - save it in ys_cv */
|
||||
if ((cv = yang_cv_get(ys)) != NULL &&
|
||||
(argument = cv_string_get(cv)) != NULL){
|
||||
if (yang_find(yext, Y_ARGUMENT, NULL) == NULL &&
|
||||
argument != NULL){
|
||||
clicon_err(OE_YANG, 0, "No argument specified in extension %s, but argument %s present when used", yang_argument_get(ys), argument);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (cvret == 0){ /* parsing failed */
|
||||
clicon_err(OE_YANG, errno, "Parsing CV: %s", reason);
|
||||
/* Make extension callbacks that may alter yang structure */
|
||||
if (clixon_plugin_extension(h, yext, ys) < 0)
|
||||
goto done;
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (prefix)
|
||||
|
|
@ -2581,39 +2579,62 @@ yang_parse_recurse(clicon_handle h,
|
|||
return retval; /* top-level (sub)module */
|
||||
}
|
||||
|
||||
int
|
||||
static int
|
||||
ys_schemanode_check(yang_stmt *ys,
|
||||
void *arg)
|
||||
void *dummy)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yspec;
|
||||
yang_stmt *yres;
|
||||
yang_stmt *yp;
|
||||
int retval = -1;
|
||||
yang_stmt *yspec;
|
||||
yang_stmt *yres = NULL;
|
||||
yang_stmt *yp;
|
||||
char *arg;
|
||||
enum rfc_6020 keyword;
|
||||
|
||||
yp = ys->ys_parent;
|
||||
switch (ys->ys_keyword){
|
||||
yp = yang_parent_get(ys);
|
||||
arg = yang_argument_get(ys);
|
||||
keyword = yang_keyword_get(ys);
|
||||
switch (yang_keyword_get(ys)){
|
||||
case Y_AUGMENT:
|
||||
if (yp->ys_keyword == Y_MODULE || /* Not top-level */
|
||||
yp->ys_keyword == Y_SUBMODULE)
|
||||
if (yang_keyword_get(yp) == Y_MODULE || /* Not top-level */
|
||||
yang_keyword_get(yp) == Y_SUBMODULE)
|
||||
break;
|
||||
/* fallthru */
|
||||
case Y_REFINE:
|
||||
case Y_UNIQUE:
|
||||
if (yang_desc_schema_nodeid(yp, ys->ys_argument, -1, &yres) < 0)
|
||||
if (yang_desc_schema_nodeid(yp, arg, -1, &yres) < 0)
|
||||
goto done;
|
||||
if (yres == NULL){
|
||||
clicon_err(OE_YANG, 0, "schemanode sanity check of %s %s",
|
||||
yang_key2str(ys->ys_keyword),
|
||||
ys->ys_argument);
|
||||
yang_key2str(keyword), arg);
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
case Y_UNIQUE:{
|
||||
char **vec = NULL;
|
||||
char *v;
|
||||
int nvec;
|
||||
int i;
|
||||
/* Unique: Sec 7.8.3 It takes as an argument a string that contains a space-
|
||||
separated list of schema node identifiers */
|
||||
if ((vec = clicon_strsep(arg, " \t\n", &nvec)) == NULL)
|
||||
goto done;
|
||||
for (i=0; i<nvec; i++){
|
||||
v = vec[i];
|
||||
if (yang_desc_schema_nodeid(yp, v, -1, &yres) < 0)
|
||||
goto done;
|
||||
if (yres == NULL){
|
||||
clicon_err(OE_YANG, 0, "schemanode sanity check of %s %s",
|
||||
yang_key2str(yang_keyword_get(ys)), v);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Y_DEVIATION:
|
||||
yspec = ys_spec(ys);
|
||||
if (yang_abs_schema_nodeid(yspec, ys, ys->ys_argument, -1, &yres) < 0)
|
||||
if (yang_abs_schema_nodeid(yspec, ys, arg, -1, &yres) < 0)
|
||||
goto done;
|
||||
if (yres == NULL){
|
||||
clicon_err(OE_YANG, 0, "schemanode sanity check of %s", ys->ys_argument);
|
||||
clicon_err(OE_YANG, 0, "schemanode sanity check of %s", arg);
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
|
|
@ -3104,11 +3125,11 @@ yang_datanode(yang_stmt *ys)
|
|||
* @retval 0 OK
|
||||
*/
|
||||
static int
|
||||
schema_nodeid_vec(yang_stmt *yn,
|
||||
char **vec,
|
||||
int nvec,
|
||||
schema_nodeid_vec(yang_stmt *yn,
|
||||
char **vec,
|
||||
int nvec,
|
||||
enum rfc_6020 keyword,
|
||||
yang_stmt **yres)
|
||||
yang_stmt **yres)
|
||||
{
|
||||
int retval = -1;
|
||||
char *arg;
|
||||
|
|
@ -3205,6 +3226,7 @@ yang_abs_schema_nodeid(yang_stmt *yspec,
|
|||
char *prefix = NULL;
|
||||
yang_stmt *yprefix;
|
||||
|
||||
*yres = NULL;
|
||||
/* check absolute schema_nodeid */
|
||||
if (schema_nodeid[0] != '/'){
|
||||
clicon_err(OE_YANG, EINVAL, "absolute schema nodeid should start with /");
|
||||
|
|
@ -3265,15 +3287,16 @@ yang_abs_schema_nodeid(yang_stmt *yspec,
|
|||
* Used in yang: unique, refine, uses augment
|
||||
*/
|
||||
int
|
||||
yang_desc_schema_nodeid(yang_stmt *yn,
|
||||
char *schema_nodeid,
|
||||
yang_desc_schema_nodeid(yang_stmt *yn,
|
||||
char *schema_nodeid,
|
||||
enum rfc_6020 keyword,
|
||||
yang_stmt **yres)
|
||||
yang_stmt **yres)
|
||||
{
|
||||
int retval = -1;
|
||||
char **vec = NULL;
|
||||
int nvec;
|
||||
|
||||
*yres = NULL;
|
||||
if (strlen(schema_nodeid) == 0)
|
||||
goto done;
|
||||
/* check absolute schema_nodeid */
|
||||
|
|
@ -3454,12 +3477,23 @@ ys_parse_sub(yang_stmt *ys,
|
|||
goto done;
|
||||
}
|
||||
break;
|
||||
case Y_UNKNOWN: /* XXX This code assumes ymod already loaded
|
||||
but it may not be */
|
||||
case Y_UNKNOWN:{ /* save (optional) argument in ys_cv */
|
||||
if (extra == NULL)
|
||||
break;
|
||||
ys->ys_extra = extra;
|
||||
if ((ys->ys_cv = cv_new(CGV_STRING)) == NULL){
|
||||
clicon_err(OE_YANG, errno, "cv_new");
|
||||
goto done;
|
||||
}
|
||||
if ((ret = cv_parse1(extra, ys->ys_cv, &reason)) < 0){ /* error */
|
||||
clicon_err(OE_YANG, errno, "parsing cv");
|
||||
goto done;
|
||||
}
|
||||
if (ret == 0){ /* parsing failed */
|
||||
clicon_err(OE_YANG, errno, "Parsing CV: %s", reason);
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -3629,3 +3663,4 @@ yang_key_match(yang_stmt *yn,
|
|||
cvec_free(cvv);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,8 +82,6 @@ struct yang_stmt{
|
|||
yang_stmt *ys_mymodule; /* Shortcut to "my" module. Augmented
|
||||
nodes can belong to other
|
||||
modules than the ancestor module */
|
||||
|
||||
char *ys_extra; /* For unknown */
|
||||
cg_var *ys_cv; /* cligen variable. See ys_populate()
|
||||
Following stmts have cv:s:
|
||||
leaf: for default value
|
||||
|
|
@ -91,7 +89,7 @@ struct yang_stmt{
|
|||
config: boolean true or false
|
||||
mandatory: boolean true or false
|
||||
fraction-digits for fraction-digits
|
||||
unknown-stmt (argument)
|
||||
unknown-stmt (optional argument)
|
||||
*/
|
||||
cvec *ys_cvec; /* List of stmt-specific variables
|
||||
Y_RANGE: range_min, range_max
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ identifier [A-Za-z_][A-Za-z0-9_\-\.]*
|
|||
|
||||
%%
|
||||
/* Common tokens */
|
||||
<KEYWORD,BOOLEAN,INTEGER,STRARG,STRING,UNKNOWN>[ \t]
|
||||
<KEYWORD,BOOLEAN,INTEGER,STRARG,STRING>[ \t]
|
||||
<KEYWORD,STRING,UNKNOWN,COMMENT2><<EOF>> { return MY_EOF; }
|
||||
<KEYWORD,BOOLEAN,INTEGER,STRARG,STRING,COMMENT1,UNKNOWN>\n { _YY->yy_linenum++; }
|
||||
<KEYWORD,BOOLEAN,INTEGER,STRARG,STRING,COMMENT1,UNKNOWN>\r
|
||||
|
|
@ -199,7 +199,8 @@ identifier [A-Za-z_][A-Za-z0-9_\-\.]*
|
|||
<UNKNOWN>; { BEGIN(KEYWORD); return *yytext; }
|
||||
<UNKNOWN>\" { _YY->yy_lex_string_state =UNKNOWN; BEGIN(STRINGDQ); return *yytext; }
|
||||
<UNKNOWN>\{ { BEGIN(KEYWORD); return *yytext; }
|
||||
<UNKNOWN>. { clixon_yang_parselval.string = strdup(yytext);
|
||||
<UNKNOWN>[ \t]+ { return SEP; }
|
||||
<UNKNOWN>[^{";: \t]+ { clixon_yang_parselval.string = strdup(yytext);
|
||||
return CHARS; }
|
||||
|
||||
<BOOLEAN>true { clixon_yang_parselval.string = strdup(yytext);
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@
|
|||
|
||||
%token MY_EOF
|
||||
%token SQ /* Single quote: ' */
|
||||
%token SEP /* Separators (at least one) */
|
||||
%token <string> CHARS
|
||||
%token <string> IDENTIFIER
|
||||
%token <string> BOOL
|
||||
|
|
@ -1540,26 +1541,33 @@ deviate_substmt : type_stmt { clicon_debug(2,"deviate-substmt -> type-st
|
|||
;
|
||||
|
||||
|
||||
/* For extensions XXX: we just drop the data */
|
||||
unknown_stmt : ustring ':' ustring ';'
|
||||
/* Represents the usage of an extension
|
||||
unknown-statement = prefix ":" identifier [sep string] optsep
|
||||
(";" /
|
||||
"{" optsep
|
||||
*((yang-stmt / unknown-statement) optsep)
|
||||
"}") stmt
|
||||
*
|
||||
*/
|
||||
unknown_stmt : ustring ':' ustring optsep ';'
|
||||
{ char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt");
|
||||
if (ysp_add(_yy, Y_UNKNOWN, id, NULL) == NULL) _YYERROR("unknown_stmt");
|
||||
clicon_debug(2,"unknown-stmt -> ustring : ustring");
|
||||
}
|
||||
| ustring ':' ustring string ';'
|
||||
| ustring ':' ustring SEP string optsep ';'
|
||||
{ char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt");
|
||||
if (ysp_add(_yy, Y_UNKNOWN, id, $4) == NULL){ _YYERROR("unknwon_stmt"); }
|
||||
if (ysp_add(_yy, Y_UNKNOWN, id, $5) == NULL){ _YYERROR("unknwon_stmt"); }
|
||||
clicon_debug(2,"unknown-stmt -> ustring : ustring string");
|
||||
}
|
||||
| ustring ':' ustring
|
||||
| ustring ':' ustring optsep
|
||||
{ char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt");
|
||||
if (ysp_add_push(_yy, Y_UNKNOWN, id, NULL) == NULL) _YYERROR("unknown_stmt"); }
|
||||
'{' yang_stmts '}'
|
||||
{ if (ystack_pop(_yy) < 0) _YYERROR("unknown_stmt");
|
||||
clicon_debug(2,"unknown-stmt -> ustring : ustring { yang-stmts }"); }
|
||||
| ustring ':' ustring string
|
||||
| ustring ':' ustring SEP string optsep
|
||||
{ char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt");
|
||||
if (ysp_add_push(_yy, Y_UNKNOWN, id, $4) == NULL) _YYERROR("unknown_stmt"); }
|
||||
if (ysp_add_push(_yy, Y_UNKNOWN, id, $5) == NULL) _YYERROR("unknown_stmt"); }
|
||||
'{' yang_stmts '}'
|
||||
{ if (ystack_pop(_yy) < 0) _YYERROR("unknown_stmt");
|
||||
clicon_debug(2,"unknown-stmt -> ustring : ustring string { yang-stmts }"); }
|
||||
|
|
@ -1815,6 +1823,11 @@ node_identifier : IDENTIFIER
|
|||
identifier_ref : node_identifier { $$=$1;}
|
||||
;
|
||||
|
||||
optsep : SEP
|
||||
|
|
||||
;
|
||||
|
||||
|
||||
stmtend : ';'
|
||||
| '{' '}'
|
||||
| '{' unknown_stmt '}'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue