Merge branch 'master' of https://github.com/clicon/clixon
This commit is contained in:
commit
ccc95b2826
9 changed files with 66 additions and 27 deletions
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 &
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue