RFC 8525:
- Change from RFC 7805: Remove revision if empty instead of sending empty revision RFC 6022 - Added cli identity to RFC6022 transport - Added source-host for natove restconf, bit no other sessions
This commit is contained in:
parent
21785a5d3e
commit
000cb866c2
17 changed files with 100 additions and 33 deletions
|
|
@ -45,11 +45,13 @@ Expected: beginning of 2023
|
||||||
|
|
||||||
* Netconf monitoring, part 2
|
* Netconf monitoring, part 2
|
||||||
* Datastores and sessions
|
* Datastores and sessions
|
||||||
* Remaining: statistics state
|
* Added clixon-specific transport identities: cli, snmp, netconf, restconf
|
||||||
|
* Added source-host fro native restonf, but no other transports
|
||||||
* Standards
|
* Standards
|
||||||
* RFC 6022 "YANG Module for NETCONF Monitoring"
|
* RFC 6022 "YANG Module for NETCONF Monitoring"
|
||||||
* See [Feature Request: Support RFC 6022 (NETCONF Monitoring)](https://github.com/clicon/clixon/issues/370)
|
* See [Feature Request: Support RFC 6022 (NETCONF Monitoring)](https://github.com/clicon/clixon/issues/370)
|
||||||
|
* Remaining: statistics state
|
||||||
|
|
||||||
### API changes on existing protocol/config features
|
### API changes on existing protocol/config features
|
||||||
|
|
||||||
Users may have to change how they access the system
|
Users may have to change how they access the system
|
||||||
|
|
@ -87,6 +89,8 @@ Developers may need to change their code
|
||||||
|
|
||||||
### Corrected Bugs
|
### Corrected Bugs
|
||||||
|
|
||||||
|
* Fixed: yang-library: Remove revision if empty instead of sending empty revision
|
||||||
|
* This was a change from RFC 7895 to RFC 8525
|
||||||
* Fixed: [locally scoped YANG typedef in grouping does not work #394](https://github.com/clicon/clixon/issues/394)
|
* Fixed: [locally scoped YANG typedef in grouping does not work #394](https://github.com/clicon/clixon/issues/394)
|
||||||
* Fixed: [leafref in new type no work in union type](https://github.com/clicon/clixon/issues/388)
|
* Fixed: [leafref in new type no work in union type](https://github.com/clicon/clixon/issues/388)
|
||||||
* Fixed: [must statement check int value failed](https://github.com/clicon/clixon/issues/386)
|
* Fixed: [must statement check int value failed](https://github.com/clicon/clixon/issues/386)
|
||||||
|
|
|
||||||
|
|
@ -192,10 +192,13 @@ backend_monitoring_state_get(clicon_handle h,
|
||||||
for (ce = backend_client_list(h); ce; ce = ce->ce_next){
|
for (ce = backend_client_list(h); ce; ce = ce->ce_next){
|
||||||
cprintf(cb, "<session>");
|
cprintf(cb, "<session>");
|
||||||
cprintf(cb, "<session-id>%u</session-id>", ce->ce_id);
|
cprintf(cb, "<session-id>%u</session-id>", ce->ce_id);
|
||||||
if (ce->ce_transport)
|
if (ce->ce_transport == NULL){
|
||||||
cprintf(cb, "<transport xmlns:%s=\"%s\">%s</transport>",
|
clicon_err(OE_XML, 0, "Mandatory element transport missing");
|
||||||
CLIXON_LIB_PREFIX, CLIXON_LIB_NS,
|
goto done;
|
||||||
ce->ce_transport);
|
}
|
||||||
|
cprintf(cb, "<transport xmlns:%s=\"%s\">%s</transport>",
|
||||||
|
CLIXON_LIB_PREFIX, CLIXON_LIB_NS,
|
||||||
|
ce->ce_transport);
|
||||||
cprintf(cb, "<username>%s</username>", ce->ce_username);
|
cprintf(cb, "<username>%s</username>", ce->ce_username);
|
||||||
if (ce->ce_source_host)
|
if (ce->ce_source_host)
|
||||||
cprintf(cb, "<source-host>%s</source-host>", ce->ce_source_host);
|
cprintf(cb, "<source-host>%s</source-host>", ce->ce_source_host);
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,8 @@ cli_terminate(clicon_handle h)
|
||||||
cvec *nsctx;
|
cvec *nsctx;
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
|
|
||||||
clicon_rpc_close_session(h);
|
if (clicon_data_get(h, "session-transport", NULL) == 0)
|
||||||
|
clicon_rpc_close_session(h);
|
||||||
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
||||||
ys_free(yspec);
|
ys_free(yspec);
|
||||||
if ((yspec = clicon_config_yang(h)) != NULL)
|
if ((yspec = clicon_config_yang(h)) != NULL)
|
||||||
|
|
@ -805,6 +806,12 @@ main(int argc,
|
||||||
goto done;
|
goto done;
|
||||||
/* Experimental utf8 mode */
|
/* Experimental utf8 mode */
|
||||||
cligen_utf8_set(cli_cligen(h), clicon_option_int(h,"CLICON_CLI_UTF8"));
|
cligen_utf8_set(cli_cligen(h), clicon_option_int(h,"CLICON_CLI_UTF8"));
|
||||||
|
|
||||||
|
/* Set RFC6022 session parameters that will be sent in first hello,
|
||||||
|
* @see clicon_hello_req
|
||||||
|
*/
|
||||||
|
clicon_data_set(h, "session-transport", "cl:cli");
|
||||||
|
|
||||||
/* Launch interfactive event loop, unless -1 */
|
/* Launch interfactive event loop, unless -1 */
|
||||||
if (restarg != NULL && strlen(restarg)){
|
if (restarg != NULL && strlen(restarg)){
|
||||||
char *mode = cli_syntax_mode(h);
|
char *mode = cli_syntax_mode(h);
|
||||||
|
|
@ -818,11 +825,6 @@ main(int argc,
|
||||||
if (evalresult < 0)
|
if (evalresult < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Set RFC6022 session parameters that will be sent in first hello,
|
|
||||||
* @see clicon_hello_req
|
|
||||||
*/
|
|
||||||
clicon_data_set(h, "session-transport", "cl:cli");
|
|
||||||
clicon_data_set(h, "session-source-host", "localhost");
|
|
||||||
|
|
||||||
/* Go into event-loop unless -1 command-line */
|
/* Go into event-loop unless -1 command-line */
|
||||||
if (!once){
|
if (!once){
|
||||||
|
|
|
||||||
|
|
@ -572,11 +572,17 @@ main(int argc,
|
||||||
clicon_err(OE_UNIX, errno, "chmod");
|
clicon_err(OE_UNIX, errno, "chmod");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Drop privileges if started as root to CLICON_RESTCONF_USER
|
/* Drop privileges if started as root to CLICON_RESTCONF_USER
|
||||||
* and use drop mode: CLICON_RESTCONF_PRIVILEGES
|
* and use drop mode: CLICON_RESTCONF_PRIVILEGES
|
||||||
*/
|
*/
|
||||||
if (restconf_drop_privileges(h) < 0)
|
if (restconf_drop_privileges(h) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
/* Set RFC6022 session parameters that will be sent in first hello,
|
||||||
|
* @see clicon_hello_req
|
||||||
|
*/
|
||||||
|
clicon_data_set(h, "session-transport", "cl:restconf");
|
||||||
|
|
||||||
if (FCGX_InitRequest(req, sock, 0) != 0){
|
if (FCGX_InitRequest(req, sock, 0) != 0){
|
||||||
clicon_err(OE_CFG, errno, "FCGX_InitRequest");
|
clicon_err(OE_CFG, errno, "FCGX_InitRequest");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -540,22 +540,43 @@ restconf_accept_client(int fd,
|
||||||
struct sockaddr from = {0,};
|
struct sockaddr from = {0,};
|
||||||
socklen_t len;
|
socklen_t len;
|
||||||
char *name = NULL;
|
char *name = NULL;
|
||||||
|
void *addr;
|
||||||
|
|
||||||
clicon_debug(1, "%s %d", __FUNCTION__, fd);
|
clicon_debug(1, "%s %d", __FUNCTION__, fd);
|
||||||
if ((rsock = (restconf_socket *)arg) == NULL){
|
if ((rsock = (restconf_socket *)arg) == NULL){
|
||||||
clicon_err(OE_YANG, EINVAL, "rsock is NULL");
|
clicon_err(OE_YANG, EINVAL, "rsock is NULL");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
clicon_debug(1, "%s type:%s addr:%s port:%hu", __FUNCTION__,
|
|
||||||
rsock->rs_addrtype,
|
|
||||||
rsock->rs_addrstr,
|
|
||||||
rsock->rs_port);
|
|
||||||
h = rsock->rs_h;
|
h = rsock->rs_h;
|
||||||
len = sizeof(from);
|
len = sizeof(from);
|
||||||
if ((s = accept(rsock->rs_ss, &from, &len)) < 0){
|
if ((s = accept(rsock->rs_ss, &from, &len)) < 0){
|
||||||
clicon_err(OE_UNIX, errno, "accept");
|
clicon_err(OE_UNIX, errno, "accept");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
switch (from.sa_family){
|
||||||
|
case AF_INET:{
|
||||||
|
struct sockaddr_in *in = (struct sockaddr_in *)&from;
|
||||||
|
addr = &(in->sin_addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AF_INET6:{
|
||||||
|
struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&from;
|
||||||
|
addr = &(in6->sin6_addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((rsock->rs_from_addr = calloc(INET6_ADDRSTRLEN, 1)) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "calloc");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (inet_ntop(from.sa_family, addr, rsock->rs_from_addr, INET6_ADDRSTRLEN) < 0)
|
||||||
|
goto done;
|
||||||
|
clicon_debug(1, "%s type:%s from:%s, dest:%s port:%hu", __FUNCTION__,
|
||||||
|
rsock->rs_addrtype,
|
||||||
|
rsock->rs_from_addr,
|
||||||
|
rsock->rs_addrstr,
|
||||||
|
rsock->rs_port);
|
||||||
|
clicon_data_set(h, "session-source-host", rsock->rs_from_addr);
|
||||||
/* Accept SSL */
|
/* Accept SSL */
|
||||||
if (restconf_ssl_accept_client(h, s, rsock, NULL) < 0)
|
if (restconf_ssl_accept_client(h, s, rsock, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -601,6 +622,8 @@ restconf_native_terminate(clicon_handle h)
|
||||||
free(rsock->rs_addrstr);
|
free(rsock->rs_addrstr);
|
||||||
if (rsock->rs_addrtype)
|
if (rsock->rs_addrtype)
|
||||||
free(rsock->rs_addrtype);
|
free(rsock->rs_addrtype);
|
||||||
|
if (rsock->rs_from_addr)
|
||||||
|
free(rsock->rs_from_addr);
|
||||||
free(rsock);
|
free(rsock);
|
||||||
}
|
}
|
||||||
if (rn->rn_ctx)
|
if (rn->rn_ctx)
|
||||||
|
|
@ -1285,6 +1308,11 @@ main(int argc,
|
||||||
if (restconf_drop_privileges(h) < 0)
|
if (restconf_drop_privileges(h) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
/* Set RFC6022 session parameters that will be sent in first hello,
|
||||||
|
* @see clicon_hello_req
|
||||||
|
*/
|
||||||
|
clicon_data_set(h, "session-transport", "cl:restconf");
|
||||||
|
|
||||||
/* Main event loop */
|
/* Main event loop */
|
||||||
if (clixon_event_loop(h) < 0)
|
if (clixon_event_loop(h) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,8 @@ typedef struct restconf_socket{
|
||||||
* Set in restconf_callhome_cb
|
* Set in restconf_callhome_cb
|
||||||
*/
|
*/
|
||||||
restconf_conn *rs_conns; /* List of transient connect sockets */
|
restconf_conn *rs_conns; /* List of transient connect sockets */
|
||||||
|
char *rs_from_addr; /* From IP address as seen by accept */
|
||||||
|
|
||||||
} restconf_socket;
|
} restconf_socket;
|
||||||
|
|
||||||
/* Restconf handle
|
/* Restconf handle
|
||||||
|
|
|
||||||
|
|
@ -525,7 +525,7 @@ main(int argc,
|
||||||
* used by the client, even though new TCP sessions are created for
|
* used by the client, even though new TCP sessions are created for
|
||||||
* each message sent to the backend.
|
* each message sent to the backend.
|
||||||
*/
|
*/
|
||||||
if (clicon_hello_req(h, "cl:snmp", "localhost", &id) < 0)
|
if (clicon_hello_req(h, "cl:snmp", NULL, &id) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
clicon_session_id_set(h, id);
|
clicon_session_id_set(h, id);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -239,8 +239,12 @@ yms_build(clicon_handle h,
|
||||||
cprintf(cb,"<revision>%s</revision>", yang_argument_get(ys));
|
cprintf(cb,"<revision>%s</revision>", yang_argument_get(ys));
|
||||||
else{
|
else{
|
||||||
/* RFC7895 1 If no (such) revision statement exists, the module's or
|
/* RFC7895 1 If no (such) revision statement exists, the module's or
|
||||||
submodule's revision is the zero-length string. */
|
submodule's revision is the zero-length string.
|
||||||
cprintf(cb,"<revision></revision>");
|
But in RFC8525 this has changed to:
|
||||||
|
If no revision statement is present in the YANG module or submodule, this
|
||||||
|
leaf is not instantiated.
|
||||||
|
cprintf(cb,"<revision></revision>");
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
if ((ys = yang_find(ymod, Y_NAMESPACE, NULL)) != NULL)
|
if ((ys = yang_find(ymod, Y_NAMESPACE, NULL)) != NULL)
|
||||||
cprintf(cb,"<namespace>%s</namespace>", yang_argument_get(ys));
|
cprintf(cb,"<namespace>%s</namespace>", yang_argument_get(ys));
|
||||||
|
|
@ -273,8 +277,6 @@ yms_build(clicon_handle h,
|
||||||
if ((ysub = yang_find(yspec, Y_SUBMODULE, name)) != NULL){
|
if ((ysub = yang_find(yspec, Y_SUBMODULE, name)) != NULL){
|
||||||
if ((ys = yang_find(ysub, Y_REVISION, NULL)) != NULL)
|
if ((ys = yang_find(ysub, Y_REVISION, NULL)) != NULL)
|
||||||
cprintf(cb,"<revision>%s</revision>", yang_argument_get(ys));
|
cprintf(cb,"<revision>%s</revision>", yang_argument_get(ys));
|
||||||
else
|
|
||||||
cprintf(cb,"<revision></revision>");
|
|
||||||
}
|
}
|
||||||
cprintf(cb,"</submodule>");
|
cprintf(cb,"</submodule>");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_YANG_LIBRARY>false</CLICON_YANG_LIBRARY>
|
<CLICON_YANG_LIBRARY>false</CLICON_YANG_LIBRARY>
|
||||||
<CLICON_STREAM_DISCOVERY_RFC8040>false</CLICON_STREAM_DISCOVERY_RFC8040>
|
<CLICON_STREAM_DISCOVERY_RFC8040>false</CLICON_STREAM_DISCOVERY_RFC8040>
|
||||||
<CLICON_NETCONF_MONITORING>false</CLICON_NETCONF_MONITORING>
|
<CLICON_NETCONF_MONITORING>false</CLICON_NETCONF_MONITORING>
|
||||||
|
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
|
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
@ -235,7 +236,7 @@ if [ -z "$match" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "netconf module A"
|
new "netconf module A"
|
||||||
expect="<module><name>example</name><revision/><namespace>urn:example:clixon</namespace><feature>A</feature><feature>A1</feature></module>"
|
expect="<module><name>example</name><namespace>urn:example:clixon</namespace><feature>A</feature><feature>A1</feature></module>"
|
||||||
match=`echo "$ret" | grep --null -Go "$expect"`
|
match=`echo "$ret" | grep --null -Go "$expect"`
|
||||||
if [ -z "$match" ]; then
|
if [ -z "$match" ]; then
|
||||||
err "$expect" "$ret"
|
err "$expect" "$ret"
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_SOCK>$dir/$APPNAME.sock</CLICON_SOCK>
|
<CLICON_SOCK>$dir/$APPNAME.sock</CLICON_SOCK>
|
||||||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
|
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_CLISPEC_DIR>/usr/local/lib/example/clispec</CLICON_CLISPEC_DIR>
|
<CLICON_CLISPEC_DIR>/usr/local/lib/example/clispec</CLICON_CLISPEC_DIR>
|
||||||
<CLICON_CLI_LINESCROLLING>0</CLICON_CLI_LINESCROLLING>
|
<CLICON_CLI_LINESCROLLING>0</CLICON_CLI_LINESCROLLING>
|
||||||
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
|
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
|
||||||
|
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
|
||||||
$RESTCONFIG
|
$RESTCONFIG
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
@ -121,20 +122,23 @@ if [ $RC -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "wait restconf"
|
new "wait restconf"
|
||||||
wait_restconf
|
swait_restconf
|
||||||
|
|
||||||
rpc="<rpc $DEFAULTNS><edit-config><target><candidate/></target><config>"
|
rpc="<rpc $DEFAULTNS><edit-config><target><candidate/></target><config>"
|
||||||
rpc+="<interfaces xmlns=\"urn:example:clixon\"><a><name>foo</name><b>"
|
rpc+="<interfaces xmlns=\"urn:example:clixon\"><a><name>foo</name><b>"
|
||||||
for (( i=0; i<$perfnr; i++ )); do
|
for (( i=0; i<$perfnr; i++ )); do
|
||||||
rpc+="<interface><name>e$i</name><type>ex:eth</type></interface>"
|
rpc+="<interface><name>e$i</name><type>eth</type></interface>"
|
||||||
done
|
done
|
||||||
rpc+="</b></a></interfaces>"
|
rpc+="</b></a></interfaces>"
|
||||||
rpc+="$(cat $fconfig)"
|
#rpc+="$(cat $fstate)"
|
||||||
rpc+="</config></edit-config></rpc>"
|
rpc+="</config></edit-config></rpc>"
|
||||||
echo foo
|
|
||||||
echo -n "$DEFAULTHELLO" > $fconfig
|
echo -n "$DEFAULTHELLO" > $fconfig
|
||||||
echo "$(chunked_framing "$rpc")" >> $fconfig
|
echo "$(chunked_framing "$rpc")" >> $fconfig
|
||||||
|
|
||||||
|
echo "fstate:$fstate"
|
||||||
|
echo "fconfig:$fconfig"
|
||||||
|
|
||||||
# Now take large config file and write it via netconf to candidate
|
# Now take large config file and write it via netconf to candidate
|
||||||
new "netconf write large config"
|
new "netconf write large config"
|
||||||
expecteof_file "time -p $clixon_netconf -qef $cfg" 0 "$fconfig" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>$" 2>&1 | awk '/real/ {print $2}'
|
expecteof_file "time -p $clixon_netconf -qef $cfg" 0 "$fconfig" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>$" 2>&1 | awk '/real/ {print $2}'
|
||||||
|
|
@ -150,7 +154,7 @@ new "netconf get test single req"
|
||||||
sel="/ex:interfaces/ex:a[ex:name='foo']/ex:b/ex:interface[ex:name='e1']"
|
sel="/ex:interfaces/ex:a[ex:name='foo']/ex:b/ex:interface[ex:name='e1']"
|
||||||
rpc=$(chunked_framing "<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"$sel\" xmlns:ex=\"urn:example:clixon\"/></get></rpc>")
|
rpc=$(chunked_framing "<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"$sel\" xmlns:ex=\"urn:example:clixon\"/></get></rpc>")
|
||||||
|
|
||||||
expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "$rpc" "" "<rpc-reply $DEFAULTNS><data><interfaces xmlns=\"urn:example:clixon\"><a><name>foo</name><b><interface><name>e1</name><type>ex:eth</type><status>up</status></interface></b></a></interfaces></data></rpc-reply>"
|
expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "$rpc" "" "<rpc-reply $DEFAULTNS><data><interfaces xmlns=\"urn:example:clixon\"><a><name>foo</name><b><interface><name>e1</name><type>eth</type><status>up</status></interface></b></a></interfaces></data></rpc-reply>"
|
||||||
|
|
||||||
new "netconf get $perfreq single reqs"
|
new "netconf get $perfreq single reqs"
|
||||||
{ time -p for (( i=0; i<$perfreq; i++ )); do
|
{ time -p for (( i=0; i<$perfreq; i++ )); do
|
||||||
|
|
@ -162,7 +166,7 @@ done | $clixon_netconf -qe1f $cfg > /dev/null; } 2>&1 | awk '/real/ {print $2}'
|
||||||
|
|
||||||
# RESTCONF get
|
# RESTCONF get
|
||||||
new "restconf get test single req"
|
new "restconf get test single req"
|
||||||
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:interfaces/a=foo/b/interface=e1)" 0 "HTTP/$HVER 200" '{"example:interface":\[{"name":"e1","type":"ex:eth","status":"up"}\]}'
|
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:interfaces/a=foo/b/interface=e1)" 0 "HTTP/$HVER 200" '{"example:interface":\[{"name":"e1","type":"eth","status":"up"}\]}'
|
||||||
|
|
||||||
new "restconf get $perfreq single reqs"
|
new "restconf get $perfreq single reqs"
|
||||||
#curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e67
|
#curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e67
|
||||||
|
|
@ -178,7 +182,7 @@ edit interfaces a foo b interface e1
|
||||||
show state xml
|
show state xml
|
||||||
EOF
|
EOF
|
||||||
new "cli get test single req"
|
new "cli get test single req"
|
||||||
expectpart "$($clixon_cli -F $fin -f $cfg)" 0 "<name>e1</name>" "<type>ex:eth</type>" "<status>up</status>$"
|
expectpart "$($clixon_cli -F $fin -f $cfg)" 0 "<name>e1</name>" "<type>eth</type>" "<status>up</status>$"
|
||||||
|
|
||||||
new "cli get $perfreq single reqs"
|
new "cli get $perfreq single reqs"
|
||||||
{ time -p for (( i=0; i<$perfreq; i++ )); do
|
{ time -p for (( i=0; i<$perfreq; i++ )); do
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ APPNAME=example
|
||||||
|
|
||||||
cfg=$dir/config.xml
|
cfg=$dir/config.xml
|
||||||
fyang=$dir/$APPNAME.yang
|
fyang=$dir/$APPNAME.yang
|
||||||
fconfig=$dir/large.xml
|
|
||||||
fstate=$dir/state.xml
|
fstate=$dir/state.xml
|
||||||
|
|
||||||
# Define default restconfig config: RESTCONFIG
|
# Define default restconfig config: RESTCONFIG
|
||||||
|
|
@ -49,6 +48,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_CLI_DIR>/usr/local/lib/example/cli</CLICON_CLI_DIR>
|
<CLICON_CLI_DIR>/usr/local/lib/example/cli</CLICON_CLI_DIR>
|
||||||
<CLICON_CLISPEC_DIR>/usr/local/lib/example/clispec</CLICON_CLISPEC_DIR>
|
<CLICON_CLISPEC_DIR>/usr/local/lib/example/clispec</CLICON_CLISPEC_DIR>
|
||||||
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
|
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
|
||||||
|
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
|
||||||
$RESTCONFIG
|
$RESTCONFIG
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_STREAM_DISCOVERY_RFC8040>true</CLICON_STREAM_DISCOVERY_RFC8040>
|
<CLICON_STREAM_DISCOVERY_RFC8040>true</CLICON_STREAM_DISCOVERY_RFC8040>
|
||||||
<CLICON_FEATURE>clixon-restconf:fcgi</CLICON_FEATURE>
|
<CLICON_FEATURE>clixon-restconf:fcgi</CLICON_FEATURE>
|
||||||
<CLICON_YANG_LIBRARY>true</CLICON_YANG_LIBRARY>
|
<CLICON_YANG_LIBRARY>true</CLICON_YANG_LIBRARY>
|
||||||
|
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
|
||||||
$RESTCONFIG
|
$RESTCONFIG
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
@ -106,7 +107,7 @@ expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPR
|
||||||
# This just catches the header and the jukebox module, the RFC has foo and bar which
|
# This just catches the header and the jukebox module, the RFC has foo and bar which
|
||||||
# seems wrong to recreate
|
# seems wrong to recreate
|
||||||
new "B.1.2. Retrieve the Server Module Information"
|
new "B.1.2. Retrieve the Server Module Information"
|
||||||
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/ietf-yang-library:yang-library)" 0 "HTTP/$HVER 200" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" "{\"ietf-yang-library:yang-library\":{\"module-set\":\[{\"name\":\"default\",\"module\":\[{\"name\":\"clixon-lib\",\"revision\":\"${CLIXON_LIB_REV}\",\"namespace\":\"http://clicon.org/lib\"}" '{"name":"example-events","revision":"","namespace":"urn:example:events"' '{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox"}' '{"name":"example-system","revision":"","namespace":"http://example.com/ns/example-system"}'
|
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/ietf-yang-library:yang-library)" 0 "HTTP/$HVER 200" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" "{\"ietf-yang-library:yang-library\":{\"module-set\":\[{\"name\":\"default\",\"module\":\[{\"name\":\"clixon-lib\",\"revision\":\"${CLIXON_LIB_REV}\",\"namespace\":\"http://clicon.org/lib\"}" '{"name":"example-events","namespace":"urn:example:events"' '{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox"}' '{"name":"example-system","namespace":"http://example.com/ns/example-system"}'
|
||||||
|
|
||||||
new "B.1.3. Retrieve the Server Capability Information"
|
new "B.1.3. Retrieve the Server Capability Information"
|
||||||
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/$HVER 200" "Content-Type: application/yang-data+xml" 'Cache-Control: no-cache' '<capabilities xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability><capability>urn:ietf:params:restconf:capability:depth</capability>
|
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/$HVER 200" "Content-Type: application/yang-data+xml" 'Cache-Control: no-cache' '<capabilities xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability><capability>urn:ietf:params:restconf:capability:depth</capability>
|
||||||
|
|
|
||||||
|
|
@ -422,6 +422,12 @@ expectpart "$($snmpwalk IF-MIB::ifTable)" 0 "IF-MIB::ifIndex.1 = INTEGER: 1" \
|
||||||
"IF-MIB::ifSpecific.1 = OID: SNMPv2-SMI::zeroDotZero" \
|
"IF-MIB::ifSpecific.1 = OID: SNMPv2-SMI::zeroDotZero" \
|
||||||
"IF-MIB::ifSpecific.2 = OID: iso.2.3"
|
"IF-MIB::ifSpecific.2 = OID: iso.2.3"
|
||||||
|
|
||||||
|
|
||||||
|
# There is an intricate error in the return of this test that has to with validation of state data
|
||||||
|
# clixon_snmp queries using xpath:
|
||||||
|
# if-mib:IF-MIB/if-mib:ifRcvAddressTable/if-mib:ifRcvAddressEntry[if-mib:ifIndex='1']
|
||||||
|
# But if-mib:ifIndex is a leafref that point to a value outside the XPath, which make the validation fail
|
||||||
|
# But if you set CLICON_VALIDATE_STATE_XML:true its ok
|
||||||
new "Test $OID24"
|
new "Test $OID24"
|
||||||
validate_oid $OID24 $OID24 "STRING" "11:bb:cc:dd:ee:ff"
|
validate_oid $OID24 $OID24 "STRING" "11:bb:cc:dd:ee:ff"
|
||||||
validate_oid $NAME24 $NAME24 "STRING" "11:bb:cc:dd:ee:ff" "IF-MIB::ifRcvAddressAddress.1.\"11:bb:cc:dd:ee:ff\" = STRING: 11:bb:cc:dd:ee:ff"
|
validate_oid $NAME24 $NAME24 "STRING" "11:bb:cc:dd:ee:ff" "IF-MIB::ifRcvAddressAddress.1.\"11:bb:cc:dd:ee:ff\" = STRING: 11:bb:cc:dd:ee:ff"
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_RESTCONF_DIR>/usr/local/lib/$APPNAME/restconf</CLICON_RESTCONF_DIR>
|
<CLICON_RESTCONF_DIR>/usr/local/lib/$APPNAME/restconf</CLICON_RESTCONF_DIR>
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
<CLICON_YANG_LIBRARY>true</CLICON_YANG_LIBRARY>
|
<CLICON_YANG_LIBRARY>true</CLICON_YANG_LIBRARY>
|
||||||
|
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
|
||||||
$RESTCONFIG
|
$RESTCONFIG
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
@ -263,10 +264,10 @@ new "restconf edit augment 2"
|
||||||
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/main:sub2 -d '{"main:aug2":"foo"}')" 0 "HTTP/$HVER 201"
|
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/main:sub2 -d '{"main:aug2":"foo"}')" 0 "HTTP/$HVER 201"
|
||||||
|
|
||||||
new "NETCONF get module state"
|
new "NETCONF get module state"
|
||||||
expecteof_netconf "$clixon_netconf -qf $cfg -D $DBG" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/yl:yang-library/yl:module-set[yl:name='default']/yl:module[yl:name='main']\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/></get></rpc>" "<rpc-reply $DEFAULTNS><data><yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"><module-set><name>default</name><module><name>main</name><revision>2021-03-08</revision><namespace>urn:example:clixon</namespace><submodule><name>sub1</name><revision/></submodule><feature>A</feature></module>" ""
|
expecteof_netconf "$clixon_netconf -qf $cfg -D $DBG" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/yl:yang-library/yl:module-set[yl:name='default']/yl:module[yl:name='main']\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/></get></rpc>" "<rpc-reply $DEFAULTNS><data><yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"><module-set><name>default</name><module><name>main</name><revision>2021-03-08</revision><namespace>urn:example:clixon</namespace><submodule><name>sub1</name></submodule><feature>A</feature></module>" ""
|
||||||
|
|
||||||
new "RESTCONF get module state"
|
new "RESTCONF get module state"
|
||||||
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data/ietf-yang-library:yang-library/module-set=default/module=main?config=nonconfig)" 0 "HTTP/$HVER 200" "<module xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"><name>main</name><revision>2021-03-08</revision><namespace>urn:example:clixon</namespace><submodule><name>sub1</name><revision/></submodule><feature>A</feature></module>"
|
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data/ietf-yang-library:yang-library/module-set=default/module=main?config=nonconfig)" 0 "HTTP/$HVER 200" "<module xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"><name>main</name><revision>2021-03-08</revision><namespace>urn:example:clixon</namespace><submodule><name>sub1</name></submodule><feature>A</feature></module>"
|
||||||
|
|
||||||
if [ $RC -ne 0 ]; then
|
if [ $RC -ne 0 ]; then
|
||||||
new "Kill restconf daemon"
|
new "Kill restconf daemon"
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,11 @@ module clixon-lib {
|
||||||
"RESTCONF either as HTTP/1 or /2, TLS or not, reverese proxy (eg fcgi/nginx) or native";
|
"RESTCONF either as HTTP/1 or /2, TLS or not, reverese proxy (eg fcgi/nginx) or native";
|
||||||
base ncm:transport;
|
base ncm:transport;
|
||||||
}
|
}
|
||||||
|
identity cli {
|
||||||
|
description
|
||||||
|
"A CLI session";
|
||||||
|
base ncm:transport;
|
||||||
|
}
|
||||||
extension autocli-op {
|
extension autocli-op {
|
||||||
description
|
description
|
||||||
"Takes an argument an operation defing how to modify the clispec at
|
"Takes an argument an operation defing how to modify the clispec at
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue