diff --git a/CHANGELOG.md b/CHANGELOG.md index 6aa7cd04..812e6241 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -87,6 +87,9 @@ * `commit` - NACM is applied to candidate and running operations only (3.2.8) * Client-side RPC:s are _not_ supported. * Recovery user "_nacm_recovery" added. +* Change GIT branch handling to a single working master branch + * Develop branched abandoned + * Travis CI supported, see [https://travis-ci.org/clicon/clixon] ### API changes on existing features (you may need to change your code) * Added `username` argument to `xmldb_put()` datastore function for NACM data-node write checks @@ -119,15 +122,16 @@ * For backward compatibility, define CLICON_CLI_MODEL_TREENAME_PATCH in clixon_custom.h ### Minor changes -* Change GIT branch handling to a single working master branch - * Develop branched abandoned - * Travis CI supported, see [https://travis-ci.org/clicon/clixon] * XML parser conformance to W3 spec * Names lexically correct (NCName) * Syntactically Correct handling of '=" command-line option to all programs: backend, cli, netconf, restconf. diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index 0cbafccd..b3f18000 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -352,7 +352,7 @@ main(int argc, char **argv) break; /* see above */ case 'F': /* read commands from file */ if (freopen(optarg, "r", stdin) == NULL){ - cli_output(stderr, "freopen: %s\n", strerror(errno)); + fprintf(stderr, "freopen: %s\n", strerror(errno)); return -1; } break; @@ -531,6 +531,7 @@ main(int argc, char **argv) *(argv-1) = tmp; cligen_line_scrolling_set(cli_cligen(h), clicon_option_int(h,"CLICON_CLI_LINESCROLLING")); + cligen_utf8_set(cli_cligen(h), clicon_option_int(h,"CLICON_CLI_UTF8")); /* Launch interfactive event loop, unless -1 */ if (restarg != NULL && strlen(restarg)){ char *mode = cli_syntax_mode(h); diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index 4f17f948..74cfec38 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -455,15 +455,13 @@ int cli_handler_err(FILE *f) { if (clicon_errno){ - cli_output(f, "%s: %s", - clicon_strerror(clicon_errno), - clicon_err_reason); + fprintf(f, "%s: %s", clicon_strerror(clicon_errno), clicon_err_reason); if (clicon_suberrno) - cli_output(f, ": %s", strerror(clicon_suberrno)); - cli_output(f, "\n"); + fprintf(f, ": %s", strerror(clicon_suberrno)); + fprintf(f, "\n"); } else - cli_output(f, "CLI command error\n"); + fprintf(f, "CLI command error\n"); return 0; } @@ -482,7 +480,6 @@ clicon_eval(clicon_handle h, { int retval = 0; - cli_output_reset(); if (!cligen_exiting(cli_cligen(h))) { clicon_err_reset(); if ((retval = cligen_eval(cli_cligen(h), match_obj, cvv)) < 0) { @@ -546,7 +543,7 @@ clicon_parse(clicon_handle h, } else { if ((smode = syntax_mode_find(stx, modename, 0)) == NULL) { - cli_output(f, "Can't find syntax mode '%s'\n", modename); + fprintf(f, "Can't find syntax mode '%s'\n", modename); goto done; } } @@ -576,13 +573,12 @@ clicon_parse(clicon_handle h, switch (retval) { case CG_EOF: /* eof */ case CG_ERROR: - cli_output(f, "CLI parse error: %s\n", cmd); + fprintf(f, "CLI parse error: %s\n", cmd); break; case CG_NOMATCH: /* no match */ /* clicon_err(OE_CFG, 0, "CLI syntax error: \"%s\": %s", cmd, cli_nomatch(h));*/ - cli_output(f, "CLI syntax error: \"%s\": %s\n", - cmd, cli_nomatch(h)); + fprintf(f, "CLI syntax error: \"%s\": %s\n", cmd, cli_nomatch(h)); break; case CG_MATCH: if (strcmp(modename, *modenamep)){ /* Command in different mode */ @@ -596,7 +592,7 @@ clicon_parse(clicon_handle h, *evalres = r; break; default: - cli_output(f, "CLI syntax error: \"%s\" is ambiguous\n", cmd); + fprintf(f, "CLI syntax error: \"%s\" is ambiguous\n", cmd); break; } /* switch retval */ } diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 0fa9fa56..c5449638 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -193,7 +193,7 @@ expand_dbvar(void *h, if (xml_merge(xt, xtop, yspec, &reason) < 0) /* Merge xtop into xt */ goto done; if (reason){ - cli_output(stderr, "%s\n", reason); + fprintf(stderr, "%s\n", reason); goto done; } if ((xcur = xpath_first(xt, "%s", xpath)) == NULL){ @@ -602,9 +602,11 @@ done: return retval; } -int cli_show_version(clicon_handle h, cvec *vars, cvec *argv) +int cli_show_version(clicon_handle h, + cvec *vars, + cvec *argv) { - cli_output(stdout, "%s\n", CLIXON_VERSION_STRING); + fprintf(stdout, "%s\n", CLIXON_VERSION_STRING); return 0; } diff --git a/apps/cli/clixon_cli_api.h b/apps/cli/clixon_cli_api.h index d2926029..0c95a093 100644 --- a/apps/cli/clixon_cli_api.h +++ b/apps/cli/clixon_cli_api.h @@ -67,7 +67,6 @@ int cli_notification_register(clicon_handle h, char *stream, enum format_enum fo char *filter, int status, int (*fn)(int, void*), void *arg); -#define cli_output cligen_output /* cli_common.c: CLIgen new vector callbacks */ diff --git a/configure b/configure index 33f99392..092aa607 100755 --- a/configure +++ b/configure @@ -4433,7 +4433,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 extras/rpm/Makefile docker/Makefile docker/system/Makefile docker/cluster/Makefile datastore/Makefile datastore/text/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/standard/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 extras/rpm/Makefile docker/Makefile docker/system/Makefile datastore/Makefile datastore/text/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/standard/Makefile doc/Makefile test/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -5143,7 +5143,6 @@ do "extras/rpm/Makefile") CONFIG_FILES="$CONFIG_FILES extras/rpm/Makefile" ;; "docker/Makefile") CONFIG_FILES="$CONFIG_FILES docker/Makefile" ;; "docker/system/Makefile") CONFIG_FILES="$CONFIG_FILES docker/system/Makefile" ;; - "docker/cluster/Makefile") CONFIG_FILES="$CONFIG_FILES docker/cluster/Makefile" ;; "datastore/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/Makefile" ;; "datastore/text/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/text/Makefile" ;; "util/Makefile") CONFIG_FILES="$CONFIG_FILES util/Makefile" ;; diff --git a/doc/FAQ.md b/doc/FAQ.md index f15ee42c..de8e7a74 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -65,6 +65,7 @@ On linux: sudo usermod -a -G clicon sudo usermod -a -G clicon www-data ``` +(you may have to restart shell) Verify: ``` diff --git a/example/README.md b/example/README.md index 919fd363..9a0f6b24 100644 --- a/example/README.md +++ b/example/README.md @@ -121,11 +121,22 @@ Setup a web/reverse-proxy server. For example, using nginx, install, and edit config file: /etc/nginx/sites-available/default: ``` server { - ... - location /restconf { - fastcgi_pass unix:/www-data/fastcgi_restconf.sock; - include fastcgi_params; - } + ... + location / { + root /usr/share/nginx/html/restconf; + fastcgi_pass unix:/www-data/fastcgi_restconf.sock; + include fastcgi_params; + } + location /restconf { + fastcgi_pass unix:/www-data/fastcgi_restconf.sock; + include fastcgi_params; + } + location /streams { + fastcgi_pass unix:/www-data/fastcgi_restconf.sock; + include fastcgi_params; + proxy_http_version 1.1; + proxy_set_header Connection ""; + } } ``` Start nginx daemon diff --git a/example/example_cli.c b/example/example_cli.c index 10e122ee..56d3b82d 100644 --- a/example/example_cli.c +++ b/example/example_cli.c @@ -63,8 +63,8 @@ mycallback(clicon_handle h, cvec *cvv, cvec *argv) /* Access cligen callback variables */ myvar = cvec_find(cvv, "var"); /* get a cligen variable from vector */ - cli_output(stderr, "%s: %d\n", __FUNCTION__, cv_int32_get(myvar)); /* get int value */ - cli_output(stderr, "arg = %s\n", cv_string_get(cvec_i(argv,0))); /* get string value */ + fprintf(stderr, "%s: %d\n", __FUNCTION__, cv_int32_get(myvar)); /* get int value */ + fprintf(stderr, "arg = %s\n", cv_string_get(cvec_i(argv,0))); /* get string value */ /* Show eth0 interfaces config using XPATH */ if (clicon_rpc_get_config(h, "running", diff --git a/test/Makefile.in b/test/Makefile.in index acb0d930..23465f93 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -44,6 +44,7 @@ SHELL = /bin/sh all: ./test_xml.sh + ./test_json.sh # ./all.sh clean: diff --git a/test/lib.sh b/test/lib.sh index 65fd29a4..663c517b 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -12,6 +12,12 @@ #set -e +# Probe nginx +#sudo systemctl status nginx.service > /dev/null +#if [ $? -ne 0 ]; then +# sudo systemctl start nginx.service +#fi + # Site file, an example of this file in README.md if [ -x ./site.sh ]; then . ./site.sh @@ -26,6 +32,11 @@ testname= # If set, enable debugging (of backend) : ${DBG:=0} +# If reset, do NOT run tests with external yang models. +# This involves downloading +# https://github.com/openconfig/public and https://github.com/YangModels/yang +: ${MODELS:=1} + # Parse yangmodels from https://github.com/YangModels/yang # Recommended: checkout yangmodels elsewhere in the tree and set the env # to that diff --git a/test/test_openconfig.sh b/test/test_openconfig.sh index b5e03e83..5b35ca25 100755 --- a/test/test_openconfig.sh +++ b/test/test_openconfig.sh @@ -9,9 +9,12 @@ APPNAME=example # include err() and new() functions and creates $dir -. ./site.sh . ./lib.sh +if [ $MODELS -eq 0 ]; then + exit +fi + cfg=$dir/conf_yang.xml fyang=$dir/test.yang diff --git a/test/test_restconf.sh b/test/test_restconf.sh index 14c3dbc8..e607598b 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -58,22 +58,22 @@ sleep $RCWAIT new "restconf tests" new "restconf root discovery. RFC 8040 3.1 (xml+xrd)" -expecteq "$(curl -s -X GET http://localhost/.well-known/host-meta)" " +expecteq "$(curl -s -X GET http://localhost/.well-known/host-meta)" 0 " " new "restconf get restconf resource. RFC 8040 3.3 (json)" -expecteq "$(curl -sG http://localhost/restconf)" '{"restconf": {"data": null,"operations": null,"yang-library-version": "2016-06-21"}} +expecteq "$(curl -sG http://localhost/restconf)" 0 '{"restconf": {"data": null,"operations": null,"yang-library-version": "2016-06-21"}} ' new "restconf get restconf resource. RFC 8040 3.3 (xml)" # Get XML instead of JSON? -expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/restconf)" '2016-06-21 +expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/restconf)" 0 '2016-06-21 ' # Should be alphabetically ordered new "restconf get restconf/operations. RFC8040 3.3.2 (json)" -expecteq "$(curl -sG http://localhost/restconf/operations)" '{"operations": {"clixon-example:client-rpc": null,"clixon-example:empty": null,"clixon-example:optional": null,"clixon-example:example": null,"clixon-lib:debug": null} +expecteq "$(curl -sG http://localhost/restconf/operations)" 0 '{"operations": {"clixon-example:client-rpc": null,"clixon-example:empty": null,"clixon-example:optional": null,"clixon-example:example": null,"clixon-lib:debug": null} ' new "restconf get restconf/operations. RFC8040 3.3.2 (xml)" @@ -85,7 +85,7 @@ if [ -z "$match" ]; then fi new "restconf get restconf/yang-library-version. RFC8040 3.3.3" -expecteq "$(curl -sG http://localhost/restconf/yang-library-version)" '{"yang-library-version": "2016-06-21"}' +expecteq "$(curl -sG http://localhost/restconf/yang-library-version)" 0 '{"yang-library-version": "2016-06-21"}' new "restconf get restconf/yang-library-version. RFC8040 3.3.3 (xml)" ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/yang-library-version) @@ -96,7 +96,7 @@ if [ -z "$match" ]; then fi new "restconf schema resource, RFC 8040 sec 3.7 according to RFC 7895 (explicit resource)" -expecteq "$(curl -s -H 'Accept: application/yang-data+json' -G http://localhost/restconf/data/ietf-yang-library:modules-state/module=ietf-interfaces,2018-02-20)" '{"ietf-yang-library:module": [{"name": "ietf-interfaces","revision": "2018-02-20","namespace": "urn:ietf:params:xml:ns:yang:ietf-interfaces","conformance-type": "implement"}]} +expecteq "$(curl -s -H 'Accept: application/yang-data+json' -G http://localhost/restconf/data/ietf-yang-library:modules-state/module=ietf-interfaces,2018-02-20)" 0 '{"ietf-yang-library:module": [{"name": "ietf-interfaces","revision": "2018-02-20","namespace": "urn:ietf:params:xml:ns:yang:ietf-interfaces","conformance-type": "implement"}]} ' new "restconf options. RFC 8040 4.1" @@ -107,21 +107,21 @@ expectfn "curl -s -I http://localhost/restconf/data" 0 "HTTP/1.1 200 OK" #Content-Type: application/yang-data+json" new "restconf empty rpc" -expecteq "$(curl -s -X POST -d {\"clixon-example:input\":null} http://localhost/restconf/operations/clixon-example:empty)" "" +expecteq "$(curl -s -X POST -d {\"clixon-example:input\":null} http://localhost/restconf/operations/clixon-example:empty)" 0 "" new "restconf empty rpc with extra args (should fail)" -expecteq "$(curl -s -X POST -d {\"clixon-example:input\":{\"extra\":null}} http://localhost/restconf/operations/clixon-example:empty)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "extra"},"error-severity": "error"}}} ' +expecteq "$(curl -s -X POST -d {\"clixon-example:input\":{\"extra\":null}} http://localhost/restconf/operations/clixon-example:empty)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "extra"},"error-severity": "error"}}} ' new "restconf get empty config + state json" -expecteq "$(curl -sSG http://localhost/restconf/data/clixon-example:state)" '{"clixon-example:state": {"op": ["42","41","43"]}} +expecteq "$(curl -sSG http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state": {"op": ["42","41","43"]}} ' new "restconf get empty config + state json + module" -expecteq "$(curl -sSG http://localhost/restconf/data/clixon-example:state)" '{"clixon-example:state": {"op": ["42","41","43"]}} +expecteq "$(curl -sSG http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state": {"op": ["42","41","43"]}} ' new "restconf get empty config + state json with wrong module name" -expecteq "$(curl -sSG http://localhost/restconf/data/badmodule:state)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "No such yang module: badmodule"}}} ' +expecteq "$(curl -sSG http://localhost/restconf/data/badmodule:state)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "No such yang module: badmodule"}}} ' new "restconf get empty config + state xml" ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/clixon-example:state) @@ -132,7 +132,7 @@ if [ -z "$match" ]; then fi new "restconf get data/ json" -expecteq "$(curl -s -G http://localhost/restconf/data/clixon-example:state/op=42)" '{"clixon-example:op": ["42","41","43"]} +expecteq "$(curl -s -G http://localhost/restconf/data/clixon-example:state/op=42)" 0 '{"clixon-example:op": ["42","41","43"]} ' new "restconf get state operation eth0 xml" @@ -145,7 +145,7 @@ if [ -z "$match" ]; then fi new "restconf get state operation eth0 type json" -expecteq "$(curl -s -G http://localhost/restconf/data/clixon-example:state/op=42)" '{"clixon-example:op": ["42","41","43"]} +expecteq "$(curl -s -G http://localhost/restconf/data/clixon-example:state/op=42)" 0 '{"clixon-example:op": ["42","41","43"]} ' new "restconf get state operation eth0 type xml" @@ -158,7 +158,7 @@ if [ -z "$match" ]; then fi new "restconf GET datastore" -expecteq "$(curl -s -X GET http://localhost/restconf/data/clixon-example:state)" '{"clixon-example:state": {"op": ["42","41","43"]}} +expecteq "$(curl -s -X GET http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state": {"op": ["42","41","43"]}} ' # Exact match @@ -169,13 +169,13 @@ new "restconf Re-add subtree which should give error" expectfn 'curl -s -X POST -d {"ietf-interfaces:interfaces":{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}} http://localhost/restconf/data' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}' # XXX Cant get this to work -#expecteq "$(curl -s -X POST -d {\"interfaces\":{\"interface\":{\"name\":\"eth/0/0\",\"type\":\"ex:eth\",\"enabled\":true}}} http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}' +#expecteq "$(curl -s -X POST -d {\"interfaces\":{\"interface\":{\"name\":\"eth/0/0\",\"type\":\"ex:eth\",\"enabled\":true}}} http://localhost/restconf/data)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}' new "restconf Check interfaces eth/0/0 added" expectfn "curl -s -G http://localhost/restconf/data" 0 '"ietf-interfaces:interfaces": {"interface": \[{"name": "eth/0/0","type": "ex:eth","enabled": true}\]}' new "restconf delete interfaces" -expecteq $(curl -s -X DELETE http://localhost/restconf/data/ietf-interfaces:interfaces) "" +expecteq "$(curl -s -X DELETE http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 "" new "restconf Check empty config" expectfn "curl -sG http://localhost/restconf/data/clixon-example:state" 0 "$state @@ -186,63 +186,63 @@ expectfn "curl -sG http://localhost/restconf/data/clixon-example:state" 0 "$stat new "restconf Add interfaces subtree eth/0/0 using POST" expectfn 'curl -s -X POST -d {"ietf-interfaces:interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}} http://localhost/restconf/data/ietf-interfaces:interfaces' 0 "" # XXX cant get this to work -#expecteq "$(curl -s -X POST -d '{"interface":{"name":"eth/0/0","type\":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces)" "" +#expecteq "$(curl -s -X POST -d '{"interface":{"name":"eth/0/0","type\":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces)" 0 "" new "restconf Check eth/0/0 added config" -expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" '{"ietf-interfaces:interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]}} +expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]}} ' new "restconf Check eth/0/0 added state" -expecteq "$(curl -s -G http://localhost/restconf/data/clixon-example:state)" '{"clixon-example:state": {"op": ["42","41","43"]}} +expecteq "$(curl -s -G http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state": {"op": ["42","41","43"]}} ' new "restconf Re-post eth/0/0 which should generate error" -expecteq "$(curl -s -X POST -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} ' +expecteq "$(curl -s -X POST -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} ' new "Add leaf description using POST" -expecteq "$(curl -s -X POST -d '{"ietf-interfaces:description":"The-first-interface"}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" "" +expecteq "$(curl -s -X POST -d '{"ietf-interfaces:description":"The-first-interface"}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "" new "Add nothing using POST" expectfn 'curl -s -X POST http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0' 0 '"ietf-restconf:errors" : {"error": {"error-type": "rpc","error-tag": "malformed-message","error-severity": "error","error-message": " on line 1: syntax error at or before:' new "restconf Check description added" -expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" '{"ietf-interfaces:interfaces": {"interface": [{"name": "eth/0/0","description": "The-first-interface","type": "ex:eth","enabled": true}]}} +expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces": {"interface": [{"name": "eth/0/0","description": "The-first-interface","type": "ex:eth","enabled": true}]}} ' new "restconf delete eth/0/0" -expecteq "$(curl -s -X DELETE http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" "" +expecteq "$(curl -s -X DELETE http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "" new "Check deleted eth/0/0" expectfn 'curl -s -G http://localhost/restconf/data' 0 $state new "restconf Re-Delete eth/0/0 using none should generate error" -expecteq "$(curl -s -X DELETE http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-missing","error-severity": "error","error-message": "Data does not exist; cannot delete resource"}}} ' +expecteq "$(curl -s -X DELETE http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-missing","error-severity": "error","error-message": "Data does not exist; cannot delete resource"}}} ' new "restconf Add subtree eth/0/0 using PUT" -expecteq "$(curl -s -X PUT -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" "" +expecteq "$(curl -s -X PUT -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "" new "restconf get subtree" -expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" '{"ietf-interfaces:interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]}} +expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]}} ' new "restconf rpc using POST json" -expecteq "$(curl -s -X POST -d '{"clixon-example:input":{"x":42}}' http://localhost/restconf/operations/clixon-example:example)" '{"clixon-example:output": {"x": "42","y": "42"}} +expecteq "$(curl -s -X POST -d '{"clixon-example:input":{"x":42}}' http://localhost/restconf/operations/clixon-example:example)" 0 '{"clixon-example:output": {"x": "42","y": "42"}} ' new "restconf rpc using POST json wrong" -expecteq "$(curl -s -X POST -d '{"clixon-example:input":{"wrongelement":"ipv4"}}' http://localhost/restconf/operations/clixon-example:example)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "wrongelement"},"error-severity": "error"}}} ' +expecteq "$(curl -s -X POST -d '{"clixon-example:input":{"wrongelement":"ipv4"}}' http://localhost/restconf/operations/clixon-example:example)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "wrongelement"},"error-severity": "error"}}} ' new "restconf rpc non-existing rpc without namespace" -expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/kalle)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "kalle"},"error-severity": "error","error-message": "RPC not defined"}}} ' +expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/kalle)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "kalle"},"error-severity": "error","error-message": "RPC not defined"}}} ' new "restconf rpc non-existing rpc" -expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/clixon-example:kalle)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "kalle"},"error-severity": "error","error-message": "RPC not defined"}}} ' +expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/clixon-example:kalle)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "kalle"},"error-severity": "error","error-message": "RPC not defined"}}} ' new "restconf rpc missing name" -expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "Operation name expected"}}} ' +expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "Operation name expected"}}} ' new "restconf rpc missing input" -expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/clixon-example:example)" '{"ietf-restconf:errors" : {"error": {"error-type": "rpc","error-tag": "malformed-message","error-severity": "error","error-message": "restconf RPC does not have input statement"}}} ' +expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/clixon-example:example)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "rpc","error-tag": "malformed-message","error-severity": "error","error-message": "restconf RPC does not have input statement"}}} ' new "restconf rpc using POST xml" ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"clixon-example:input":{"x":42}}' http://localhost/restconf/operations/clixon-example:example) @@ -253,7 +253,7 @@ if [ -z "$match" ]; then fi new "restconf rpc using wrong prefix" -expecteq "$(curl -s -X POST -d '{"wrong:input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/wrong:example)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang module not found"}}} ' +expecteq "$(curl -s -X POST -d '{"wrong:input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/wrong:example)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang module not found"}}} ' new "restconf local client rpc using POST xml" ret=$(curl -s -i -X POST -H "Accept: application/yang-data+xml" -d '{"clixon-example:input":{"x":"example"}}' http://localhost/restconf/operations/clixon-example:client-rpc) diff --git a/test/test_restconf2.sh b/test/test_restconf2.sh index 59d78abb..fb9396f0 100755 --- a/test/test_restconf2.sh +++ b/test/test_restconf2.sh @@ -117,10 +117,10 @@ expectfn 'curl -s -X POST -d {"example:interface":{"name":"TEST","type":"eth0"}} # XXX should it be example:interface? new "restconf POST again" -expecteq "$(curl -s -X POST -d '{"interface":{"name":"TEST","type":"eth0"}}' http://localhost/restconf/data/example:cont1)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} ' +expecteq "$(curl -s -X POST -d '{"interface":{"name":"TEST","type":"eth0"}}' http://localhost/restconf/data/example:cont1)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} ' new "restconf POST from top" -expecteq "$(curl -s -X POST -d '{"example:cont1":{"interface":{"name":"TEST","type":"eth0"}}}' http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} ' +expecteq "$(curl -s -X POST -d '{"example:cont1":{"interface":{"name":"TEST","type":"eth0"}}}' http://localhost/restconf/data)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} ' new "restconf DELETE" expectfn 'curl -s -X DELETE http://localhost/restconf/data/example:cont1' 0 "" diff --git a/test/test_rpc.sh b/test/test_rpc.sh index 9598ff91..e1e24337 100755 --- a/test/test_rpc.sh +++ b/test/test_rpc.sh @@ -69,28 +69,28 @@ new "netconf empty rpc" expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' '^]]>]]>$' new "restconf example rpc json/json default - no http media headers" -expecteq "$(curl -s -X POST -d '{"clixon-example:input":{"x":0}}' http://localhost/restconf/operations/clixon-example:example)" '{"clixon-example:output": {"x": "0","y": "42"}} +expecteq "$(curl -s -X POST -d '{"clixon-example:input":{"x":0}}' http://localhost/restconf/operations/clixon-example:example)" 0 '{"clixon-example:output": {"x": "0","y": "42"}} ' new "restconf example rpc json/json change y default" -expecteq "$(curl -s -X POST -d '{"clixon-example:input":{"x":"0","y":"99"}}' http://localhost/restconf/operations/clixon-example:example)" '{"clixon-example:output": {"x": "0","y": "99"}} +expecteq "$(curl -s -X POST -d '{"clixon-example:input":{"x":"0","y":"99"}}' http://localhost/restconf/operations/clixon-example:example)" 0 '{"clixon-example:output": {"x": "0","y": "99"}} ' new "restconf example rpc json/json" # XXX example:input example:output -expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+json' -H 'Content-Type: application/yang-data+json' -d '{"clixon-example:input":{"x":"0"}}' http://localhost/restconf/operations/clixon-example:example)" '{"clixon-example:output": {"x": "0","y": "42"}} +expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+json' -H 'Content-Type: application/yang-data+json' -d '{"clixon-example:input":{"x":"0"}}' http://localhost/restconf/operations/clixon-example:example)" 0 '{"clixon-example:output": {"x": "0","y": "42"}} ' new "restconf example rpc xml/json" -expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -H 'Content-Type: application/yang-data+json' -d '0' http://localhost/restconf/operations/clixon-example:example)" '{"clixon-example:output": {"x": "0","y": "42"}} +expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -H 'Content-Type: application/yang-data+json' -d '0' http://localhost/restconf/operations/clixon-example:example)" 0 '{"clixon-example:output": {"x": "0","y": "42"}} ' new "restconf example rpc json/xml" -expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+json' -H 'Accept: application/yang-data+xml' -d '{"clixon-example:input":{"x":"0"}}' http://localhost/restconf/operations/clixon-example:example)" '042 +expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+json' -H 'Accept: application/yang-data+xml' -d '{"clixon-example:input":{"x":"0"}}' http://localhost/restconf/operations/clixon-example:example)" 0 '042 ' new "restconf example rpc xml/xml" -expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '0' http://localhost/restconf/operations/clixon-example:example)" '042 +expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '0' http://localhost/restconf/operations/clixon-example:example)" 0 '042 ' new "netconf example rpc" @@ -107,7 +107,7 @@ if [ -z "$match" ]; then fi new "restconf empty rpc with input x" -expecteq "$(curl -s -X POST -d '{"clixon-example:input":{"x":0}}' http://localhost/restconf/operations/clixon-example:empty)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "x"},"error-severity": "error"}}} ' +expecteq "$(curl -s -X POST -d '{"clixon-example:input":{"x":0}}' http://localhost/restconf/operations/clixon-example:empty)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "x"},"error-severity": "error"}}} ' # cornercase: optional has yang input/output sections but test without body new "restconf optional rpc with null input and output" @@ -119,16 +119,16 @@ if [ -z "$match" ]; then fi new "restconf omit mandatory" -expecteq "$(curl -s -X POST -d '{"clixon-example:input":null}' http://localhost/restconf/operations/clixon-example:example)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "x"},"error-severity": "error","error-message": "Mandatory variable"}}} ' +expecteq "$(curl -s -X POST -d '{"clixon-example:input":null}' http://localhost/restconf/operations/clixon-example:example)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "x"},"error-severity": "error","error-message": "Mandatory variable"}}} ' new "restconf add extra" -expecteq "$(curl -s -X POST -d '{"clixon-example:input":{"x":"0","extra":"0"}}' http://localhost/restconf/operations/clixon-example:example)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "extra"},"error-severity": "error"}}} ' +expecteq "$(curl -s -X POST -d '{"clixon-example:input":{"x":"0","extra":"0"}}' http://localhost/restconf/operations/clixon-example:example)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "extra"},"error-severity": "error"}}} ' new "restconf wrong method" -expecteq "$(curl -s -X POST -d '{"clixon-example:input":{"x":"0"}}' http://localhost/restconf/operations/clixon-example:wrong)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "wrong"},"error-severity": "error","error-message": "RPC not defined"}}} ' +expecteq "$(curl -s -X POST -d '{"clixon-example:input":{"x":"0"}}' http://localhost/restconf/operations/clixon-example:wrong)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "wrong"},"error-severity": "error","error-message": "RPC not defined"}}} ' new "restconf example missing input" -expecteq "$(curl -s -X POST -d '{"clixon-example:input":null}' http://localhost/restconf/operations/ietf-netconf:edit-config)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang module not found"}}} ' +expecteq "$(curl -s -X POST -d '{"clixon-example:input":null}' http://localhost/restconf/operations/ietf-netconf:edit-config)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang module not found"}}} ' new "netconf kill-session missing session-id mandatory" expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' '^applicationmissing-elementsession-iderrorMandatory variable]]>]]>$' diff --git a/test/test_yang_namespace.sh b/test/test_yang_namespace.sh index ae1f7cba..0a58d82a 100755 --- a/test/test_yang_namespace.sh +++ b/test/test_yang_namespace.sh @@ -99,23 +99,23 @@ new "netconf discard-changes" expecteof "$clixon_netconf -qf $cfg -y $fyang1" 0 "]]>]]>" "^]]>]]>$" new "restconf set x in example1" -expecteq "$(curl -s -X POST -d '{"example1:x":42}' http://localhost/restconf/data)" '' +expecteq "$(curl -s -X POST -d '{"example1:x":42}' http://localhost/restconf/data)" 0 '' new "restconf get config example1" -expecteq "$(curl -s -X GET http://localhost/restconf/data/example1:x)" '{"example1:x": 42} +expecteq "$(curl -s -X GET http://localhost/restconf/data/example1:x)" 0 '{"example1:x": 42} ' new "restconf set x in example2" -expecteq "$(curl -s -X POST -d '{"example2:x":{"y":99}}' http://localhost/restconf/data)" '' +expecteq "$(curl -s -X POST -d '{"example2:x":{"y":99}}' http://localhost/restconf/data)" 0 '' # XXX GET ../example1:x is translated to select=/x which gets both example1&2 #new "restconf get config example1" -#expecteq "$(curl -s -X GET http://localhost/restconf/data/example1:x)" '{"example1:x": 42} +#expecteq "$(curl -s -X GET http://localhost/restconf/data/example1:x)" 0 '{"example1:x": 42} # ' # XXX GET ../example2:x is translated to select=/x which gets both example1&2 #new "restconf get config example2" -#expecteq "$(curl -s -X GET http://localhost/restconf/data/example2:x)" '{"example2:x": {"y":42}} +#expecteq "$(curl -s -X GET http://localhost/restconf/data/example2:x)" 0 '{"example2:x": {"y":42}} # ' new "restconf get config example1 and example2" diff --git a/test/test_yangmodels.sh b/test/test_yangmodels.sh index 02c30ba8..b2b93df1 100755 --- a/test/test_yangmodels.sh +++ b/test/test_yangmodels.sh @@ -20,9 +20,12 @@ APPNAME=example # include err() and new() functions and creates $dir -. ./site.sh . ./lib.sh +if [ $MODELS -eq 0 ]; then + exit +fi + cfg=$dir/conf_yang.xml fyang=$dir/test.yang diff --git a/yang/clixon/Makefile.in b/yang/clixon/Makefile.in index 1f9c99a2..1c498b1a 100644 --- a/yang/clixon/Makefile.in +++ b/yang/clixon/Makefile.in @@ -40,7 +40,7 @@ datarootdir = @datarootdir@ CLIXON_DATADIR = @CLIXON_DATADIR@ -YANGSPECS = clixon-config@2018-10-21.yang +YANGSPECS = clixon-config@2019-02-06.yang YANGSPECS += clixon-lib@2019-01-02.yang YANGSPECS += clixon-rfc5277@2008-07-01.yang diff --git a/yang/clixon/clixon-config@2018-10-21.yang b/yang/clixon/clixon-config@2019-02-06.yang similarity index 97% rename from yang/clixon/clixon-config@2018-10-21.yang rename to yang/clixon/clixon-config@2019-02-06.yang index dfa2f5e1..4cb5bed0 100644 --- a/yang/clixon/clixon-config@2018-10-21.yang +++ b/yang/clixon/clixon-config@2019-02-06.yang @@ -39,6 +39,10 @@ module clixon-config { ***** END LICENSE BLOCK *****"; + revision 2019-02-06 { + description + "Released in Clixon 3.9"; + } revision 2018-10-21 { description "Released in Clixon 3.8"; @@ -275,6 +279,14 @@ module clixon-config { Set to 1 if you want CLI to scroll sideways when approaching right margin"; } + leaf CLICON_CLI_UTF8 { + type int8; + default 0; + description + "Set to 1 to enable CLIgen UTF-8 experimental mode. + Note that this feature is EXPERIMENTAL and may not properly handle + scrolling, control characters, etc"; + } leaf CLICON_SOCK_FAMILY { type string; default "UNIX";