This commit is contained in:
Olof hagsand 2019-07-25 13:40:01 +02:00
commit ccc95b2826
9 changed files with 66 additions and 27 deletions

View file

@ -15,7 +15,7 @@
* *
* Yang extensions support * Yang extensions support
* New plugin callback: ca_extension * New plugin callback: ca_extension
* Main backend example includes example code on how to implement a Yang extension in a plugin. * The main example explains how to implement a Yang extension in a backend plugin.
### API changes on existing features (you may need to change your code) ### API changes on existing features (you may need to change your code)
* RESTCONF PUT/POST erroneously returned 200 OK. Instead restconf now returns: * RESTCONF PUT/POST erroneously returned 200 OK. Instead restconf now returns:

View file

@ -497,6 +497,7 @@ restconf_main_extension_cb(clicon_handle h,
char *modname; char *modname;
yang_stmt *ymod; yang_stmt *ymod;
yang_stmt *yc; yang_stmt *yc;
yang_stmt *yn = NULL;
ymod = ys_module(yext); ymod = ys_module(yext);
modname = yang_argument_get(ymod); modname = yang_argument_get(ymod);
@ -504,9 +505,11 @@ restconf_main_extension_cb(clicon_handle h,
if (strcmp(modname, "ietf-restconf") != 0 || strcmp(extname, "yang-data") != 0) if (strcmp(modname, "ietf-restconf") != 0 || strcmp(extname, "yang-data") != 0)
goto ok; goto ok;
clicon_debug(1, "%s Enabled extension:%s:%s", __FUNCTION__, modname, extname); clicon_debug(1, "%s Enabled extension:%s:%s", __FUNCTION__, modname, extname);
if ((yc = ys_prune(ys, 0)) == NULL) if ((yc = yang_find(ys, 0, NULL)) == NULL)
goto ok;
if ((yn = ys_dup(yc)) == NULL)
goto done; goto done;
if (yn_insert(yang_parent_get(ys), yc) < 0) if (yn_insert(yang_parent_get(ys), yn) < 0)
goto done; goto done;
ok: ok:
retval = 0; retval = 0;

View file

@ -183,11 +183,11 @@ Restconf support is also supported, see (restc)[../../apps/restconf/README.md].
## RPC Operations ## RPC Operations
Clixon implements Yang RPC operations by an extension mechanism. The Clixon implements Yang RPC operations by a mechanism that enables you
extension mechanism enables you to add application-specific to add application-specific operations. It works by adding
operations. It works by adding user-defined callbacks for added user-defined callbacks for added netconf operations. It is possible to
netconf operations. It is possible to use the extension mechanism use the extension mechanism independent of the yang rpc construct, but
independent of the yang rpc construct, but it is recommended. The example includes an example: not recommended . The example includes an example:
Example using CLI: Example using CLI:
``` ```
@ -273,14 +273,14 @@ The example contains some stubs for authorization according to [RFC8341(NACM)](h
## Extensions ## Extensions
Clixon supports Yang extensions, but you need to write plugin code. Clixon supports Yang extensions by writing plugin callback code.
The example backend implements an "example:e4" Yang extension, as follows: The example backend implements an "example:e4" Yang extension, as follows:
``` ```
extension e4 { extension e4 {
description description
"The first child of the ex:e4 (unknown) statement is replaced with its first "The first child of the ex:e4 (unknown) statement is inserted into
child. This means that 'uses bar;' in the ex:e4 statement below is a valid the module as a regular data statement. This means that 'uses bar;'
data node"; in the ex:e4 statement below is a valid data node";
argument arg; argument arg;
} }
ex:e4 arg1{ ex:e4 arg1{
@ -295,8 +295,8 @@ The backend plugin code registers an extension callback in the init struct:
The callback then receives a callback on all "unknown" Yang statements The callback then receives a callback on all "unknown" Yang statements
during yang parsing. If the extension matches "example:e4", it applies during yang parsing. If the extension matches "example:e4", it applies
the extension. In the example, it replaces the "ex:e4" statements with the extension. In the example, it copies the child of the "ex:e4" statement and
its first child, making it a proper yang statement. inserts in as a proper yang statement in the example module.
## Systemd ## Systemd

View file

@ -45,9 +45,9 @@ module clixon-example {
/* yang extension implemented by the example backend code. */ /* yang extension implemented by the example backend code. */
extension e4 { extension e4 {
description description
"The first child of the ex:e4 (unknown) statement is replaced with its first "The first child of the ex:e4 (unknown) statement is inserted into
child. This means that 'uses bar;' in the ex:e4 statement below is a valid the module as a regular data statement. This means that 'uses bar;'
data node"; in the ex:e4 statement below is a valid data node";
argument arg; argument arg;
} }
grouping bar { grouping bar {

View file

@ -369,6 +369,7 @@ example_extension(clicon_handle h,
char *modname; char *modname;
yang_stmt *ymod; yang_stmt *ymod;
yang_stmt *yc; yang_stmt *yc;
yang_stmt *yn = NULL;
ymod = ys_module(yext); ymod = ys_module(yext);
modname = yang_argument_get(ymod); modname = yang_argument_get(ymod);
@ -376,9 +377,11 @@ example_extension(clicon_handle h,
if (strcmp(modname, "example") != 0 || strcmp(extname, "e4") != 0) if (strcmp(modname, "example") != 0 || strcmp(extname, "e4") != 0)
goto ok; goto ok;
clicon_debug(1, "%s Enabled extension:%s:%s", __FUNCTION__, modname, extname); clicon_debug(1, "%s Enabled extension:%s:%s", __FUNCTION__, modname, extname);
if ((yc = ys_prune(ys, 0)) == NULL) if ((yc = yang_find(ys, 0, NULL)) == NULL)
goto ok;
if ((yn = ys_dup(yc)) == NULL)
goto done; goto done;
if (yn_insert(yang_parent_get(ys), yc) < 0) if (yn_insert(yang_parent_get(ys), yn) < 0)
goto done; goto done;
ok: ok:
retval = 0; retval = 0;

View file

@ -665,6 +665,7 @@ xmldb_put(clicon_handle h,
int permit = 0; /* nacm permit all */ int permit = 0; /* nacm permit all */
char *format; char *format;
cvec *nsc = NULL; /* nacm namespace context */ cvec *nsc = NULL; /* nacm namespace context */
int firsttime = 0;
if (cbret == NULL){ if (cbret == NULL){
clicon_err(OE_XML, EINVAL, "cbret is NULL"); clicon_err(OE_XML, EINVAL, "cbret is NULL");
@ -686,6 +687,7 @@ xmldb_put(clicon_handle h,
} }
/* If there is no xml x0 tree (in cache), then read it from file */ /* If there is no xml x0 tree (in cache), then read it from file */
if (x0 == NULL){ if (x0 == NULL){
firsttime++; /* to avoid leakage on error, see fail from text_modify */
if (xmldb_readfile(h, db, yspec, &x0, NULL) < 0) if (xmldb_readfile(h, db, yspec, &x0, NULL) < 0)
goto done; goto done;
} }
@ -725,8 +727,14 @@ xmldb_put(clicon_handle h,
if ((ret = text_modify_top(h, x0, x1, yspec, op, username, xnacm, permit, cbret)) < 0) if ((ret = text_modify_top(h, x0, x1, yspec, op, username, xnacm, permit, cbret)) < 0)
goto done; goto done;
/* If xml return - ie netconf error xml tree, then stop and return OK */ /* If xml return - ie netconf error xml tree, then stop and return OK */
if (ret == 0) if (ret == 0){
/* If first time and quit here, x0 is not written back into cache and leaks */
if (firsttime && x0){
xml_free(x0);
x0 = NULL;
}
goto fail; goto fail;
}
/* Remove NONE nodes if all subs recursively are also NONE */ /* Remove NONE nodes if all subs recursively are also NONE */
if (xml_tree_prune_flagged_sub(x0, XML_FLAG_NONE, 0, NULL) <0) if (xml_tree_prune_flagged_sub(x0, XML_FLAG_NONE, 0, NULL) <0)

View file

@ -2589,6 +2589,10 @@ ys_schemanode_check(yang_stmt *ys,
yang_stmt *yp; yang_stmt *yp;
char *arg; char *arg;
enum rfc_6020 keyword; enum rfc_6020 keyword;
char **vec = NULL;
char *v;
int nvec;
int i;
yp = yang_parent_get(ys); yp = yang_parent_get(ys);
arg = yang_argument_get(ys); arg = yang_argument_get(ys);
@ -2609,10 +2613,6 @@ ys_schemanode_check(yang_stmt *ys,
} }
break; break;
case Y_UNIQUE:{ 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- /* Unique: Sec 7.8.3 It takes as an argument a string that contains a space-
separated list of schema node identifiers */ separated list of schema node identifiers */
if ((vec = clicon_strsep(arg, " \t\n", &nvec)) == NULL) if ((vec = clicon_strsep(arg, " \t\n", &nvec)) == NULL)
@ -2643,6 +2643,8 @@ ys_schemanode_check(yang_stmt *ys,
} }
retval = 0; retval = 0;
done: done:
if (vec)
free(vec);
return retval; return retval;
} }
@ -3395,7 +3397,7 @@ ys_parse(yang_stmt *ys,
* That is, siblings, etc, may not be there. Complete checks are made in * That is, siblings, etc, may not be there. Complete checks are made in
* ys_populate instead. * ys_populate instead.
* @param[in] ys yang statement * @param[in] ys yang statement
* @param[in] extra Yang extra for cornercases (unknown/extension). * @param[in] extra Yang extra for cornercases (unknown/extension). Is consumed
* *
* The cv:s created in parse-tree as follows: * The cv:s created in parse-tree as follows:
* fraction-digits : Create cv as uint8, check limits [1:8] (must be made in 1st pass) * fraction-digits : Create cv as uint8, check limits [1:8] (must be made in 1st pass)
@ -3499,6 +3501,8 @@ ys_parse_sub(yang_stmt *ys,
} }
retval = 0; retval = 0;
done: done:
if (extra)
free(extra);
return retval; return retval;
} }

View file

@ -231,6 +231,9 @@ yang_parse_exit(struct clicon_yang_yacc_arg *yy)
return 0; return 0;
} }
/*! Pop a yang parse context on stack
* @param[in] yy Yang yacc argument
*/
int int
ystack_pop(struct clicon_yang_yacc_arg *yy) ystack_pop(struct clicon_yang_yacc_arg *yy)
{ {
@ -245,6 +248,10 @@ ystack_pop(struct clicon_yang_yacc_arg *yy)
return 0; return 0;
} }
/*! Push a yang parse context on stack
* @param[in] yy Yang yacc argument
* @param[in] yn Yang node to push
*/
struct ys_stack * struct ys_stack *
ystack_push(struct clicon_yang_yacc_arg *yy, ystack_push(struct clicon_yang_yacc_arg *yy,
yang_stmt *yn) yang_stmt *yn)
@ -307,7 +314,13 @@ ysp_add(struct clicon_yang_yacc_arg *yy,
return NULL; return NULL;
} }
/*! combination of ysp_add and ysp_push for sub-modules */ /*! Add a yang statement to existing top-of-stack and then push it on stack
*
* @param[in] yy Yang yacc argument
* @param[in] keyword Yang keyword
* @param[in] argument Yang argument
* @param[in] extra Yang extra for cornercases (unknown/extension)
*/
static yang_stmt * static yang_stmt *
ysp_add_push(struct clicon_yang_yacc_arg *yy, ysp_add_push(struct clicon_yang_yacc_arg *yy,
enum rfc_6020 keyword, enum rfc_6020 keyword,

View file

@ -206,6 +206,8 @@ if [ $nr -lt 1 -o $nr -gt 2 ]; then
err 1 "$nr" err 1 "$nr"
fi fi
sleep 2
# 2c # 2c
new "2c) start sub 8s - replay from start -8s - expect 3-4 notifications" new "2c) start sub 8s - replay from start -8s - expect 3-4 notifications"
ret=$($clixon_util_stream -u http://localhost/streams/EXAMPLE -t 10 -s -8) ret=$($clixon_util_stream -u http://localhost/streams/EXAMPLE -t 10 -s -8)
@ -219,6 +221,8 @@ if [ $nr -lt 3 -o $nr -gt 4 ]; then
err 4 "$nr" err 4 "$nr"
fi fi
sleep 2
# 2d) start sub 8s - replay from start -8s to stop +4s - expect 3 notifications # 2d) start sub 8s - replay from start -8s to stop +4s - expect 3 notifications
new "2d) start sub 8s - replay from start -8s to stop +4s - expect 3 notifications" new "2d) start sub 8s - replay from start -8s to stop +4s - expect 3 notifications"
ret=$($clixon_util_stream -u http://localhost/streams/EXAMPLE -t 10 -s -30 -e +4) ret=$($clixon_util_stream -u http://localhost/streams/EXAMPLE -t 10 -s -30 -e +4)
@ -232,6 +236,8 @@ if [ $nr -lt 4 -o $nr -gt 10 ]; then
err 6 "$nr" err 6 "$nr"
fi fi
sleep 2
# 2e) start sub 8s - replay from -90s w retention 60s - expect 10 notifications # 2e) start sub 8s - replay from -90s w retention 60s - expect 10 notifications
new "2e) start sub 8s - replay from -90s w retention 60s - expect 10 notifications" new "2e) start sub 8s - replay from -90s w retention 60s - expect 10 notifications"
ret=$($clixon_util_stream -u http://localhost/streams/EXAMPLE -t 10 -s -90 -e +0) ret=$($clixon_util_stream -u http://localhost/streams/EXAMPLE -t 10 -s -90 -e +0)
@ -246,6 +252,8 @@ if [ $nr -lt 9 -o $nr -gt 14 ]; then
err 10 "$nr" err 10 "$nr"
fi fi
sleep 2
# Try parallell # Try parallell
# start background job # start background job
curl -s -X GET -H "Accept: text/event-stream" -H "Cache-Control: no-cache" -H "Connection: keep-alive" "http://localhost/streams/EXAMPLE" > /dev/null & curl -s -X GET -H "Accept: text/event-stream" -H "Cache-Control: no-cache" -H "Connection: keep-alive" "http://localhost/streams/EXAMPLE" > /dev/null &