From ee946a00f5a8ae1042434a07b18a0f9bce2b2e7a Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 13 Jul 2018 12:44:59 +0200 Subject: [PATCH 1/5] Changed `plugin_init()` backend return semantics: If returns NULL, _without_ calling clicon_err(), the module is disabled. Also, example documentation corrected according to: https://github.com/clicon/clixon/issues/33 --- CHANGELOG.md | 3 ++- example/README.md | 2 +- example/example_backend_nacm.c | 7 +++++++ lib/clixon/clixon_plugin.h | 5 ++++- lib/src/clixon_plugin.c | 18 ++++++++++++------ lib/src/clixon_xml.c | 2 -- lib/src/clixon_xml_map.c | 32 ++++++++++++++++++++++++++------ 7 files changed, 52 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff1a1db6..48aecb65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. ### Minor changes: -* Dedicated xml,json,yang and xsl parser utility programs added +* Changed `plugin_init()` backend return semantics: If returns NULL, _without_ calling clicon_err(), the module is disabled. +* Dedicated standalone xml,json,yang and xsl parser utility test programs added under lib/src/. * CDATA xml support (patch by David Cornejo, Netgate) * Encode and decode (parsing) support * Validation of yang bits type space-separated list value diff --git a/example/README.md b/example/README.md index cafd796f..e373476b 100644 --- a/example/README.md +++ b/example/README.md @@ -23,7 +23,7 @@ routing example. It contains the following files: ``` Start backend: ``` - clixon_backend -f /usr/local/etc/example.xml -I + sudo clixon_backend -f /usr/local/etc/example.xml -s init ``` Edit cli: ``` diff --git a/example/example_backend_nacm.c b/example/example_backend_nacm.c index d6ed4600..f484362e 100644 --- a/example/example_backend_nacm.c +++ b/example/example_backend_nacm.c @@ -114,6 +114,13 @@ static clixon_plugin_api api = { clixon_plugin_api * clixon_plugin_init(clicon_handle h) { + char *nacm_mode; + clicon_debug(1, "%s backend nacm", __FUNCTION__); + nacm_mode = clicon_option_str(h, "CLICON_NACM_MODE"); + if (nacm_mode==NULL || strcmp(nacm_mode, "disabled") == 0){ + clicon_debug(1, "%s CLICON_NACM_MODE not enabled: example nacm module disabled", __FUNCTION__); + return NULL; + } return &api; } diff --git a/lib/clixon/clixon_plugin.h b/lib/clixon/clixon_plugin.h index cd340458..cd6038aa 100644 --- a/lib/clixon/clixon_plugin.h +++ b/lib/clixon/clixon_plugin.h @@ -41,7 +41,9 @@ /* * Constants */ -/* Hardcoded plugin symbol. Must exist in all plugins to kickstart */ +/* Hardcoded plugin symbol. Must exist in all plugins to kickstart + * @see clixon_plugin_init + */ #define CLIXON_PLUGIN_INIT "clixon_plugin_init" /* @@ -181,6 +183,7 @@ typedef struct clixon_plugin clixon_plugin; /*! Plugin initialization function. Must appear in all plugins * @param[in] h Clixon handle * @retval api Pointer to API struct + * @retval NULL Failure (if clixon_err() called), module disabled otherwise. * @see CLIXON_PLUGIN_INIT default symbol */ clixon_plugin_api *clixon_plugin_init(clicon_handle h); diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c index 3d0b80ae..2631938c 100644 --- a/lib/src/clixon_plugin.c +++ b/lib/src/clixon_plugin.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -204,12 +205,16 @@ plugin_load_one(clicon_handle h, clicon_err(OE_UNIX, 0, "dlsym: %s: %s", file, error); goto done; } + clicon_err_reset(); if ((api = initfn(h)) == NULL) { - clicon_err(OE_PLUGIN, errno, "Failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file); - if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ - clicon_err(OE_DB, 0, "Unknown error: %s: plugin_init does not make clicon_err call on error", - file); - goto err; + if (!clicon_errno){ /* if clicon_err() is not called then log and continue */ + clicon_log(LOG_WARNING, "Warning: failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file); + dlclose(handle); + } + else{ + clicon_err(OE_PLUGIN, errno, "Failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file); + goto err; + } } /* Note: sizeof clixon_plugin_api which is largest of clixon_plugin_api:s */ if ((cp = (clixon_plugin *)malloc(sizeof(struct clixon_plugin))) == NULL){ @@ -228,7 +233,8 @@ plugin_load_one(clicon_handle h, snprintf(cp->cp_name, sizeof(cp->cp_name), "%*s", (int)strlen(name), name); - cp->cp_api = *api; + if (api) + cp->cp_api = *api; clicon_debug(1, "%s", __FUNCTION__); done: return cp; diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index 1e80bf68..c2393782 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -1487,8 +1487,6 @@ int xml_copy_one(cxobj *x0, cxobj *x1) { - cg_var *cv1; - xml_type_set(x1, xml_type(x0)); if (xml_value(x0)){ /* malloced string */ if ((x1->x_value = strdup(x0->x_value)) == NULL){ diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 7e1b69e4..88aa0d6d 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -1715,6 +1715,7 @@ xml_merge1(cxobj *x0, cxobj *x1c; /* mod child */ char *x1bstr; /* mod body string */ yang_stmt *yc; /* yang child */ + cbuf *cbr = NULL; /* Reason buffer */ assert(x1 && xml_type(x1) == CX_ELMNT); assert(y0); @@ -1753,9 +1754,16 @@ xml_merge1(cxobj *x0, x1cname = xml_name(x1c); /* Get yang spec of the child */ if ((yc = yang_find_datanode(y0, x1cname)) == NULL){ - if (reason && (*reason = strdup("XML node has no corresponding yang specification (Invalid XML or wrong Yang spec?")) == NULL){ - clicon_err(OE_UNIX, errno, "strdup"); - goto done; + if (reason){ + if ((cbr = cbuf_new()) == NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } + cprintf(cbr, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?", xml_name(x1), x1cname); + if ((*reason = strdup(cbuf_get(cbr))) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } } break; } @@ -1772,6 +1780,8 @@ xml_merge1(cxobj *x0, ok: retval = 0; done: + if (cbr) + cbuf_free(cbr); return retval; } @@ -1797,6 +1807,7 @@ xml_merge(cxobj *x0, cxobj *x0c; /* base child */ cxobj *x1c; /* mod child */ yang_stmt *yc; + cbuf *cbr = NULL; /* Reason buffer */ /* Loop through children of the modification tree */ x1c = NULL; @@ -1804,9 +1815,16 @@ xml_merge(cxobj *x0, x1cname = xml_name(x1c); /* Get yang spec of the child */ if ((yc = yang_find_topnode(yspec, x1cname, YC_DATANODE)) == NULL){ - if (reason && (*reason = strdup("XML node has no corresponding yang specification (Invalid XML or wrong Yang spec?")) == NULL){ - clicon_err(OE_UNIX, errno, "strdup"); - goto done; + if (reason){ + if ((cbr = cbuf_new()) == NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } + cprintf(cbr, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?", xml_name(x1), x1cname); + if ((*reason = strdup(cbuf_get(cbr))) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } } break; } @@ -1820,6 +1838,8 @@ xml_merge(cxobj *x0, } retval = 0; /* OK */ done: + if (cbr) + cbuf_free(cbr); return retval; } From fd19640579c9903133e41f06f27e1a406e9f01a0 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 13 Jul 2018 13:13:11 +0200 Subject: [PATCH 2/5] Added systemd example files under example/systemd --- CHANGELOG.md | 1 + apps/restconf/README.md | 2 +- doc/FAQ.md | 9 +++++++-- example/README.md | 4 ++++ example/systemd/example.service | 13 +++++++++++++ example/systemd/example_restconf.service | 14 ++++++++++++++ 6 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 example/systemd/example.service create mode 100644 example/systemd/example_restconf.service diff --git a/CHANGELOG.md b/CHANGELOG.md index 48aecb65..5448e813 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. ### Minor changes: +* Added systemd example files under example/systemd * Changed `plugin_init()` backend return semantics: If returns NULL, _without_ calling clicon_err(), the module is disabled. * Dedicated standalone xml,json,yang and xsl parser utility test programs added under lib/src/. * CDATA xml support (patch by David Cornejo, Netgate) diff --git a/apps/restconf/README.md b/apps/restconf/README.md index d6c69b89..7c5cc03e 100644 --- a/apps/restconf/README.md +++ b/apps/restconf/README.md @@ -2,7 +2,7 @@ ### Installation using Nginx -Define nginx config file/etc/nginx/sites-available/default +Define nginx config file: /etc/nginx/sites-available/default ``` server { ... diff --git a/doc/FAQ.md b/doc/FAQ.md index 09459599..1884a246 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -24,7 +24,7 @@ Clixon is written in C. The plugins are written in C. The CLI specification uses cligen (http://cligen.se) ## How to best understand Clixon? -Run the Clixon example, in the example directory. +Run the Clixon example, in the [example](../example) directory. ## How do you build and install Clixon (and the example)? Clixon: @@ -178,6 +178,11 @@ You may also add a default method in the configuration file: ``` +## Can I use systemd with Clixon? + +Yes. Systemd example files are provide for the backend and the +restconf daemon as part of the [example](../example/systemd). + ## How can I add extra XML? There are two ways to add extra XML to running database after start. Note that this XML is not "committed" into running. @@ -345,7 +350,7 @@ To authenticate, the callback needs to return the value 1 and supply a username. See [../apps/example/example_restconf.c] example_restconf_credentials() for an example of HTTP basic auth. -## How do I write a CLI translator function +## How do I write a CLI translator function? The CLI can perform variable translation. This is useful if you want to prcess the input, such as hashing, encrypting or in other way diff --git a/example/README.md b/example/README.md index e373476b..3622612d 100644 --- a/example/README.md +++ b/example/README.md @@ -188,6 +188,10 @@ The example contains some stubs for authorization according to [RFC8341(NACM)](h * A NACM backend plugin reporting the mandatory NACM state variables. +## Systemd files + +Example systemd files for backend and restconf daemons are found under the systemd directory. Install them under /etc/systemd/system for example. + ## Run as docker container (Note not updated) diff --git a/example/systemd/example.service b/example/systemd/example.service new file mode 100644 index 00000000..0bc4130d --- /dev/null +++ b/example/systemd/example.service @@ -0,0 +1,13 @@ +[Unit] +Description=Starts and stops a clixon example service on this system +Wants=example_restconf.service + +[Service] +Type=forking +User=root +RestartSec=60 +Restart=on-failure +ExecStart=/usr/local/sbin/clixon_backend -s running -f /usr/local/etc/example.xml + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/example/systemd/example_restconf.service b/example/systemd/example_restconf.service new file mode 100644 index 00000000..d47ff885 --- /dev/null +++ b/example/systemd/example_restconf.service @@ -0,0 +1,14 @@ +[Unit] +Description=Starts and stops an example clixon restconf service on this system +Wants=example.service +After=example.service + +[Service] +Type=simple +User=www-data +WorkingDirectory=/www-data +Restart=on-failure +ExecStart=/www-data/clixon_restconf -f /usr/local/etc/example.xml + +[Install] +WantedBy=multi-user.target From 719ea9339817a3f2d3fe53ea9ab1d08d44031470 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 13 Jul 2018 16:31:39 +0200 Subject: [PATCH 3/5] Conformance to RFC-8040 operations where prefix was used instead of module name. * Proper specification for an operation is POST /restconf/operations/: HTTP/1.1 * See https://github.com/clicon/clixon/issues/31, https://github.com/clicon/clixon/pull/32 and https://github.com/clicon/clixon/issues/30 * Thanks David Cornejo and Dmitry Vakhrushev of Netgate for pointing this out --- CHANGELOG.md | 4 +++ apps/restconf/restconf_methods.c | 35 +++++++++++++++++------- lib/clixon/clixon_yang.h | 1 + lib/src/clixon_yang.c | 46 +++++++++++++++++++++++++++++--- test/test_restconf.sh | 26 +++++++++--------- 5 files changed, 87 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5448e813..0ec25fdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 3.7.0 (Upcoming) ### Major changes: +* Conformance for RFC-8040 operations where prefix was used instead of module name. + * Proper specification for an operation is POST /restconf/operations/: HTTP/1.1 + * See https://github.com/clicon/clixon/issues/31, https://github.com/clicon/clixon/pull/32 and https://github.com/clicon/clixon/issues/30 + * Thanks David Cornejo and Dmitry Vakhrushev of Netgate for pointing this out. * Support for YANG identity and identityref according to RFC 7950 Sec 7.18 and 9.10 * Previous support did no validation of values. * Validation of types and CLI expansion diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index 142105d0..62580cd6 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -925,8 +925,7 @@ api_operations_get(clicon_handle h, yang_spec *yspec; yang_stmt *ym; yang_stmt *yc; - yang_stmt *yprefix; - char *prefix; + char *modname; cbuf *cbx = NULL; cxobj *xt = NULL; @@ -937,15 +936,12 @@ api_operations_get(clicon_handle h, cprintf(cbx, ""); ym = NULL; while ((ym = yn_each((yang_node*)yspec, ym)) != NULL) { - if ((yprefix = yang_find((yang_node*)ym, Y_PREFIX, NULL)) != NULL) - prefix = yprefix->ys_argument; - else - continue; + modname = ym->ys_argument; yc = NULL; while ((yc = yn_each((yang_node*)ym, yc)) != NULL) { if (yc->ys_keyword != Y_RPC) continue; - cprintf(cbx, "<%s:%s />", prefix, yc->ys_argument); + cprintf(cbx, "<%s:%s />", modname, yc->ys_argument); } } cprintf(cbx, ""); @@ -1030,6 +1026,9 @@ api_operations_post(clicon_handle h, char *username; cbuf *cbret = NULL; int ret = 0; + char *prefix = NULL; + char *id = NULL; + yang_stmt *ys = NULL; clicon_debug(1, "%s json:\"%s\" path:\"%s\"", __FUNCTION__, data, path); if ((yspec = clicon_dbspec_yang(h)) == NULL){ @@ -1047,8 +1046,22 @@ api_operations_post(clicon_handle h, } clicon_debug(1, "%s oppath: %s", __FUNCTION__, oppath); - /* Find yang rpc statement, return yang rpc statement if found */ - if (yang_abs_schema_nodeid(yspec, oppath, Y_RPC, &yrpc) < 0){ + /* Find yang rpc statement, return yang rpc statement if found + * POST {+restconf}/operations/ + * + * The field identifies the module name and rpc identifier + * string for the desired operation. + */ + if (yang_nodeid_split(oppath+1, &prefix, &id) < 0) /* +1 skip / */ + goto done; + if ((ys = yang_find((yang_node*)yspec, Y_MODULE, prefix)) == NULL){ + if (netconf_operation_failed_xml(&xerr, "protocol", "yang module not found") < 0) + goto done; + if (api_return_err(h, r, xerr, pretty, use_xml) < 0) + goto done; + goto ok; + } + if ((yrpc = yang_find((yang_node*)ys, Y_RPC, id)) == NULL){ if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0) goto done; if (api_return_err(h, r, xerr, pretty, use_xml) < 0) @@ -1229,6 +1242,10 @@ api_operations_post(clicon_handle h, retval = 0; done: clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); + if (prefix) + free(prefix); + if (id) + free(id); if (xdata) xml_free(xdata); if (xtop) diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h index 5ea7e986..18fbee6b 100644 --- a/lib/clixon/clixon_yang.h +++ b/lib/clixon/clixon_yang.h @@ -246,6 +246,7 @@ yang_stmt *yn_each(yang_node *yn, yang_stmt *ys); char *yang_key2str(int keyword); char *yarg_prefix(yang_stmt *ys); char *yarg_id(yang_stmt *ys); +int yang_nodeid_split(char *nodeid, char **prefix, char **id); yang_stmt *ys_module(yang_stmt *ys); yang_spec *ys_spec(yang_stmt *ys); yang_stmt *yang_find_module_by_prefix(yang_stmt *ys, char *prefix); diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 8960fdca..d034798b 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -611,8 +611,8 @@ yang_find_schemanode(yang_node *yn, /*! Find first matching data node in all (sub)modules in a yang spec * * @param[in] ysp Yang specification - * @param[in] argument if NULL, match any(first) argument. XXX is that really a case? - * @param[in] schemanode If set look for schema nodes, otherwise only data nodes + * @param[in] argument Name of node. If NULL match first + * @param[in] class See yang_class for class of yang nodes * A yang specification has modules as children which in turn can have * syntax-nodes as children. This function goes through all the modules to * look for nodes. Note that if a child to a module is a choice, @@ -803,7 +803,7 @@ yarg_id(yang_stmt *ys) return id; } -/* Assume argument is id on the type: <[prefix:]id>, return 'prefix' +/*! Assume argument is id on the type: <[prefix:]id>, return 'prefix' * @param[in] ys A yang statement * @retval NULL No prefix * @retval prefix Malloced string that needs to be freed by caller. @@ -822,6 +822,46 @@ yarg_prefix(yang_stmt *ys) return prefix; } + +/*! Split yang node identifier into prefix and identifer. + * @param[in] node-id + * @param[out] prefix Malloced string. May be NULL. + * @param[out] id Malloced identifier. + * @retval 0 OK + * @retval -1 Error + * @note caller need to free id and prefix after use + */ +int +yang_nodeid_split(char *nodeid, + char **prefix, + char **id) +{ + int retval = -1; + char *str; + + if ((str = strchr(nodeid, ':')) == NULL){ + if ((*id = strdup(nodeid)) == NULL){ + clicon_err(OE_YANG, errno, "strdup"); + goto done; + } + } + else{ + if ((*prefix = strdup(nodeid)) == NULL){ + clicon_err(OE_YANG, errno, "strdup"); + goto done; + } + (*prefix)[str-nodeid] = '\0'; + str++; + if ((*id = strdup(str)) == NULL){ + clicon_err(OE_YANG, errno, "strdup"); + goto done; + } + } + retval = 0; + done: + return retval; +} + /*! Given a yang statement and a prefix, return yang module to that prefix * Note, not the other module but the proxy import statement only * @param[in] ys A yang statement diff --git a/test/test_restconf.sh b/test/test_restconf.sh index 5008ad80..f4856e81 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -92,7 +92,7 @@ new "kill old restconf daemon" sudo pkill -u www-data clixon_restconf new "start restconf daemon" -sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg -y $fyang # -D +sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg -y $fyang -D sleep 1 @@ -113,12 +113,12 @@ expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/r ' new2 "restconf get restconf/operations. RFC8040 3.3.2 (json)" -expecteq "$(curl -sG http://localhost/restconf/operations)" '{"operations": {"ex:empty": null,"ex:input": null,"ex:output": null,"ex:client-rpc": null,"rt:fib-route": null,"rt:route-count": null}} +expecteq "$(curl -sG http://localhost/restconf/operations)" '{"operations": {"example:empty": null,"example:input": null,"example:output": null,"example:client-rpc": null,"ietf-routing:fib-route": null,"ietf-routing:route-count": null}} ' new "restconf get restconf/operations. RFC8040 3.3.2 (xml)" ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations) -expect="" +expect="" match=`echo $ret | grep -EZo "$expect"` if [ -z "$match" ]; then err "$expect" "$ret" @@ -143,7 +143,7 @@ expectfn "curl -s -I http://localhost/restconf/data" 0 "HTTP/1.1 200 OK" #Content-Type: application/yang-data+json" new2 "restconf empty rpc" -expecteq "$(curl -s -X POST -d {\"input\":{\"name\":\"\"}} http://localhost/restconf/operations/ex:empty)" "" +expecteq "$(curl -s -X POST -d {\"input\":{\"name\":\"\"}} http://localhost/restconf/operations/example:empty)" "" new2 "restconf get empty config + state json" expecteq "$(curl -sSG http://localhost/restconf/data)" '{"data": {"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}} @@ -246,29 +246,29 @@ expecteq "$(curl -s -G http://localhost/restconf/data)" '{"data": {"interfaces": ' new2 "restconf rpc using POST json" -expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/rt:fib-route)" '{"output": {"route": {"address-family": "ipv4","next-hop": {"next-hop-list": "2.3.4.5"}}}} +expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"output": {"route": {"address-family": "ipv4","next-hop": {"next-hop-list": "2.3.4.5"}}}} ' # Cant get this to work due to quoting #new2 "restconf rpc using POST wrong JSON" -#expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":ipv4}}' http://localhost/restconf/operations/rt:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": " on line 1: syntax error at or before: i"}}}} ' +#expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":ipv4}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": " on line 1: syntax error at or before: i"}}}} ' new2 "restconf rpc using POST json w/o mandatory element" -expecteq "$(curl -s -X POST -d '{"input":{"wrongelement":"ipv4"}}' http://localhost/restconf/operations/rt:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Missing mandatory variable: routing-instance-name"}}}} ' +expecteq "$(curl -s -X POST -d '{"input":{"wrongelement":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Missing mandatory variable: routing-instance-name"}}}} ' new2 "restconf rpc non-existing rpc w/o namespace" expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/kalle)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang node not found"}}}} ' new2 "restconf rpc non-existing rpc" -expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/ex:kalle)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang node not found"}}}} ' +expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/example:kalle)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang node not found"}}}} ' new2 "restconf rpc missing name" expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Operation name expected"}}}} ' new2 "restconf rpc missing input" -expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/rt:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Missing mandatory variable: routing-instance-name"}}}} ' +expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Missing mandatory variable: routing-instance-name"}}}} ' new "restconf rpc using POST xml" -ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/rt:fib-route) +ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route) expect="ipv42.3.4.5" match=`echo $ret | grep -EZo "$expect"` if [ -z "$match" ]; then @@ -276,10 +276,10 @@ if [ -z "$match" ]; then fi new2 "restconf rpc using wrong prefix" -expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/wrong:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang node not found"}}}} ' +expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/wrong:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang module not found"}}}} ' new "restconf local client rpc using POST xml" -ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"request":"example"}}' http://localhost/restconf/operations/ex:client-rpc) +ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"request":"example"}}' http://localhost/restconf/operations/example:client-rpc) expect="ok" match=`echo $ret | grep -EZo "$expect"` if [ -z "$match" ]; then @@ -287,7 +287,7 @@ if [ -z "$match" ]; then fi # XXX cant get -H to work -#expecteq 'curl -s -X POST -H "Accept: application/yang-data+xml" -d {"input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/rt:fib-route' 'ipv42.3.4.5' +#expecteq 'curl -s -X POST -H "Accept: application/yang-data+xml" -d {"input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/ietf-routing:fib-route' 'ipv42.3.4.5' # Cant get shell macros to work, inline matching from lib.sh From d61f86c1e0b1cf07832645a3b78b22efae6e750f Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 13 Jul 2018 16:51:51 +0200 Subject: [PATCH 4/5] restconf www-data group membership clarification --- apps/restconf/README.md | 5 +++++ doc/FAQ.md | 17 ++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/apps/restconf/README.md b/apps/restconf/README.md index 7c5cc03e..06ec90bb 100644 --- a/apps/restconf/README.md +++ b/apps/restconf/README.md @@ -2,6 +2,11 @@ ### Installation using Nginx +Ensure www-data is member of the CLICON_SOCK_GROUP (default clicon). If not, add it: +``` + sudo usermod -a -G clicon www-data +``` + Define nginx config file: /etc/nginx/sites-available/default ``` server { diff --git a/doc/FAQ.md b/doc/FAQ.md index 1884a246..880e34dc 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -1,4 +1,4 @@ -i# Clixon FAQ +# Clixon FAQ ## What is Clixon? @@ -41,14 +41,25 @@ The example: sudo make install ``` -## Do I need to setup anything? +## Do I need to setup anything? (IMPORTANT) The config demon requires a valid group to create a server UNIX socket. Define a valid CLICON_SOCK_GROUP in the config file or via the -g option or create the group and add the user to it. The default group is 'clicon'. +Add yourself and www-data, if you intend to use restconf. + On linux: +``` sudo groupadd clicon - sudo usermod -a -G clicon user + sudo usermod -a -G clicon + sudo usermod -a -G clicon www-data +``` + +Verify: +``` +grep clicon /etc/group +clicon:x:1001:,www-data +``` ## What about reference documentation? Clixon uses Doxygen for reference documentation. From 0c5ef826d2bf32890c1410660f3a03e1f1f0ebc8 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 13 Jul 2018 16:58:19 +0200 Subject: [PATCH 5/5] clicon group in example clarification --- example/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/example/README.md b/example/README.md index 3622612d..be751154 100644 --- a/example/README.md +++ b/example/README.md @@ -16,7 +16,11 @@ routing example. It contains the following files: * example_netconf.c Netconf callback plugin * Makefile.in Example makefile where plugins are built and installed + ## Compile and run + +Before you start, see [preparation](../doc/FAQ.md#do-i-need-to-setup-anything-important). + ``` cd example make && sudo make install