diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index c929149d..fb6721c9 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -381,7 +381,7 @@ main(int argc, char **argv) argc -= optind; argv += optind; - /* Parse db spec file */ + /* Parse yang database spec file */ if (yang_spec_main(h, stdout, 0) < 0) goto done; diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c index 14b97ec5..39716908 100644 --- a/apps/netconf/netconf_rpc.c +++ b/apps/netconf/netconf_rpc.c @@ -61,7 +61,7 @@ */ -/* get-config help function +/*! get-config help function * xfilter is a filter expression starting with * only supported * needs refactoring: move the lower part (after xfilter) up to get-config or @@ -88,7 +88,7 @@ ]]>]]> * filter xpath + select all: ]]>]]> - * filter subnet + config: + * filter subtree + config: ]]>]]> * filter xpath + select: ]]>]]> @@ -158,7 +158,7 @@ netconf_filter_xmldb(clicon_handle h, "protocol", "error", NULL, - "select"); + "select"); goto done; } if (xmldb_get(h, source, selector, 0, &xdb, NULL, NULL) < 0){ diff --git a/apps/restconf/README b/apps/restconf/README index 66fd1a38..10c4b1c2 100644 --- a/apps/restconf/README +++ b/apps/restconf/README @@ -1,4 +1,5 @@ Work-in-progress restconf server +================================ See draft-ietf-netconf-restconf-13.txt @@ -26,7 +27,19 @@ server { # pass the REST API to FastCGI server location /restconf { root /usr/share/nginx/html/restconf; - fastcgi_pass unix:/www-data/fastcgi_api.sock; + fastcgi_pass unix:/www-data/clicon_restconf.sock; include fastcgi_params; } -} \ No newline at end of file +} + +Debugging +--------- +Start the fastcgi programs with debug flag: +sudo su -c "/www-data/clixon_restconf -D" -s /bin/sh www-data + +Look at syslog: +tail -f /var/log/syslog | grep clixon_restconf + +Send command: +curl -G http://127.0.0.1/restconf/data/foo + diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index c2e5b63b..ae485a62 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -25,19 +25,11 @@ * sudo apt-get install libfcgi-dev * gcc -o fastcgi fastcgi.c -lfcgi + + * sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.conf " -s /bin/sh www-data + * This is the interface: - * api/cli - * api/netconf - * api/login - * api/logout - * api/signup - * api/settings nyi - * api/callhome - * api/metric_rules - * api/metrics - * api/metric_spec * api/data/profile=/metric= PUT data:enable= - * api/user * api/test */ @@ -70,837 +62,113 @@ resource ([RFC6415]) */ #define RESTCONF_API_ROOT "/restconf/" -/*======================================================================= - * API code - *=======================================================================*/ -/*! Send a CLI command via GET, POST or PUT - * POST or PUT: - * URI: /api/cli/configure - * data: show version - * GET: - * URI: /api/cli/configure/show/version - * Yes, the GET syntax is ugly but it can be nice to have in eg a browser. - * @param[in] r Fastcgi request handle - * @param[in] pvec Vector of path (DOCUMENT_URI) - * @param[in] pn Length of path - * @param[in] qvec Vector of query string (QUERY_STRING) - * @param[in] qn Length of qvec - * @param[in] data Stream input data +/*! Generic REST GET method + * @param[in] r Fastcgi request handle + * @param[in] pcvec Vector of path ie DOCUMENT_URI element + * @param[in] pi Offset, where to start pcvec + * @param[in] dvec Stream input data + * @param[in] qvec Vector of query string (QUERY_STRING) + * @code + * curl -G http://localhost/api/data/profile/name=default/metric/rtt + * @endcode + * XXX: cant find a way to use Accept request field to choose Content-Type + * I would like to support both xml and json. + * Request may contain + * Accept: application/yang.data+json,application/yang.data+xml + * Response contains one of: + * Content-Type: application/yang.data+xml + * Content-Type: application/yang.data+json */ static int -api_cli(FCGX_Request *r, - char **pvec, - int pn, - cvec *qvec, - char *data) -{ - int retval = -1; - char *cmd; - char *mode; - char *request; - - FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n\r\n"); - mode = pvec[0]; - request = FCGX_GetParam("REQUEST_METHOD", r->envp); - if (strcmp(request, "GET")==0){ - pvec++; pn--; - if ((cmd = clicon_strjoin(pn, pvec, " ", __FUNCTION__)) == NULL) - goto done; - } - else - if (strcmp(request, "PUT")==0 || strcmp(request, "POST")==0) - cmd = data; - else - goto done; - - if (cli_cmd(r, mode, cmd) < 0) - goto done; - retval = 0; - done: - unchunk_group (__FUNCTION__); - return retval; -} - -/*! - * @param[in] r Fastcgi request handle - * @param[in] pvec Vector of path (DOCUMENT_URI) - * @param[in] pn Length of path - * @param[in] qvec Vector of query string (QUERY_STRING) - * @param[in] data Stream input data - */ -static int -api_netconf(FCGX_Request *r, - char **pvec, - int pn, - cvec *qvec, - char *data) -{ - int retval = -1; - char *request; - cbuf *cb = NULL; - - FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n\r\n"); - request = FCGX_GetParam("REQUEST_METHOD", r->envp); - if (strcmp(request, "PUT")!=0 && strcmp(request, "POST")!=0) - goto done; - if (netconf_cmd(r, data) < 0) - goto done; - retval = 0; - done: - if (cb) - cbuf_free(cb); - return retval; -} - -/*! Register new user - * @param[in] r Fastcgi request handle - * @param[in] pvec Vector of path (DOCUMENT_URI) - * @param[in] pn Length of path - * @param[in] qvec Vector of query string (QUERY_STRING) - * @param[in] dvec Stream input data - * In: user, passwd [fullname, organization, sender-template] - * Create: configuration user/passwd, influxdb database, user - */ -static int -api_signup(FCGX_Request *r, - char **pvec, - int pn, - cvec *qvec, - cvec *dvec) -{ - int retval = -1; - char *request; - char *server_addr; - char *root; - char *name; - char *dashboard_db_name; - char *passw; - char *fullname; - char *organization; - char *profile; - cbuf *resbuf = NULL; - cbuf *cb = NULL; - char *id; - cxobj *cx = NULL; - cxobj *x; - - clicon_debug(1, "%s", __FUNCTION__); - request = FCGX_GetParam("REQUEST_METHOD", r->envp); - server_addr = FCGX_GetParam("SERVER_ADDR", r->envp); - root = FCGX_GetParam("DOCUMENT_ROOT", r->envp); - if (strcmp(request, "POST")!=0) - goto done; - FCGX_SetExitStatus(201, r->out); - if ((name = cvec_find_str(dvec, "email")) == NULL) - goto done; - clicon_debug(1, "%s name=%s", __FUNCTION__, name); - if ((passw = cvec_find_str(dvec, "passw")) == NULL) - goto done; - fullname = cvec_find_str(dvec, "fullname"); - organization = cvec_find_str(dvec, "organization"); - profile = cvec_find_str(dvec, "profile"); - clicon_debug(1, "%s passw=%s", __FUNCTION__, passw); - if (strlen(name)==0){ - errorfn(r, root, "No Name given"); - goto done; - } - if (strlen(passw)==0){ - errorfn(r, root, "No Password given"); - goto done; - } - if ((resbuf = cbuf_new()) == NULL) - goto done; - /* Create user in gridye/clicon */ - if (cli_rpc(resbuf, "configure", "user %s password %s", name, passw) < 0) - goto done; - if (fullname && strlen(fullname)) - if (cli_rpc(resbuf, "configure", "user %s fullname %s", name, fullname) < 0) - goto done; - if (organization && strlen(organization)) - if (cli_rpc(resbuf, "configure", "user %s organization %s", name, organization) < 0) - goto done; - if (profile && strlen(profile)) - if (cli_rpc(resbuf, "configure", "user %s profile %s", name, profile) < 0) - goto done; - /* Create influxdb data database and user - (XXX create same user/passwd as server, may want to keep them separate) - */ - if (create_database(server_addr, name, WWW_USER, WWW_PASSWD) < 0) - goto done; - if (create_db_user(server_addr, name, name, passw, WWW_USER, WWW_PASSWD) < 0) - goto done; - /* */ - if ((cb = cbuf_new()) == NULL) - goto done; - cprintf(cb, "%s_db", name); - dashboard_db_name = cbuf_get(cb); - /* Create influxdb dashboard database and user */ - if (create_database(server_addr, dashboard_db_name, WWW_USER, WWW_PASSWD) < 0) - goto done; - if (create_db_user(server_addr, dashboard_db_name, - name, passw, WWW_USER, WWW_PASSWD) < 0) - goto done; - /* Create influxdb entry in gridye/clicon */ - if (cli_rpc(resbuf, "configure", "user %s resultdb url http://%s:8086/db/%s/series", - name, server_addr, name) < 0) - goto done; - if (cli_rpc(resbuf, "configure", "user %s resultdb minute true", name) < 0) - goto done; - if (cli_rpc(resbuf, "configure", "user %s resultdb username %s", name, name) < 0) - goto done; - if (cli_rpc(resbuf, "configure", "user %s resultdb password %s", name, passw) < 0) - goto done; - - if (cli_rpc(resbuf, "configure", "commit") < 0) - goto done; - /* Get database entry for user from name to get id */ - if (get_db_entry("user", "name", name, &cx) < 0) - goto done; - if ((x = xpath_first(cx, "//user/id")) == NULL) - goto done; - id = xml_body(x); - FCGX_FPrintF(r->out, "Content-Type: text/html\r\n"); - FCGX_FPrintF(r->out, "Location: /www/grideye.html\r\n"); - FCGX_FPrintF(r->out, "Set-Cookie: %s=%s; path=/; HttpOnly\r\n", - USER_COOKIE, id); - FCGX_FPrintF(r->out, "\r\n"); - - retval = 0; - done: - clicon_debug(1, "%s end %d", __FUNCTION__, retval); - if (resbuf) - cbuf_free(resbuf); - if (cb) - cbuf_free(cb); - return retval; -} - -static int -api_settings(FCGX_Request *r, - char **pvec, - int pn, - cvec *qvec, - cvec *dvec) -{ - /* NYI */ - return 0; -} - -/*! User pressed login submit button -> check user/passwd and set connect session cookie - * @param[in] r Fastcgi request handle - * @param[in] pvec Vector of path (DOCUMENT_URI) - * @param[in] pn Length of path - * @param[in] qvec Vector of query string (QUERY_STRING) - * @param[in] dvec Stream input data - */ -static int -api_login(FCGX_Request *r, - char **pvec, - int pn, - cvec *qvec, - cvec *dvec) -{ - int retval = -1; - char *request; - char *root; - char *name; - char *passw; - cxobj *cx = NULL; - cxobj *x; - char *id; - - clicon_debug(1, "%s", __FUNCTION__); - request = FCGX_GetParam("REQUEST_METHOD", r->envp); - root = FCGX_GetParam("DOCUMENT_ROOT", r->envp); - if (strcmp(request, "POST")!=0 && strcmp(request, "PUT")!=0) - goto done; - FCGX_SetExitStatus(201, r->out); - - if ((name = cvec_find_str(dvec, "email")) == NULL) - goto done; - clicon_debug(1, "%s name=%s", __FUNCTION__, name); - if ((passw = cvec_find_str(dvec, "passw")) == NULL) - goto done; - clicon_debug(1, "%s passw=%s", __FUNCTION__, passw); - if (strlen(name)==0){ - errorfn(r, root, "No Name given"); - goto done; - } - if (strlen(passw)==0){ - errorfn(r, root, "No Password given"); - goto done; - } - /* Get database entry for user from name */ - if (get_db_entry("user", "name", name, &cx) < 0) - goto done; - if (check_credentials(passw, cx) == 0){ - clicon_debug(1, "%s wrong password or user", __FUNCTION__); - errorfn(r, root, "Wrong password or user"); - goto done; - } - clicon_debug(1, "%s login credentials ok", __FUNCTION__); - if ((x = xpath_first(cx, "//user/id")) == NULL) - goto done; - id = xml_body(x); - /* Set connected-user cookie */ - FCGX_FPrintF(r->out, "Content-Type: text/html\r\n"); - FCGX_FPrintF(r->out, "Location: /www/grideye.html\r\n"); - FCGX_FPrintF(r->out, "Set-Cookie: %s=%s; path=/; HttpOnly\r\n", - USER_COOKIE, id); - FCGX_FPrintF(r->out, "\r\n"); - - retval = 0; - done: - if (cx) - xml_free(cx); - return retval; -} - -/*! - * @param[in] r Fastcgi request handle - * @param[in] pvec Vector of path (DOCUMENT_URI) - * @param[in] pn Length of path - * @param[in] qvec Vector of query string (QUERY_STRING) - * @param[in] dvec Stream input data - */ -static int -api_logout(FCGX_Request *r, - char **pvec, - int pn, - cvec *qvec, - cvec *dvec) -{ - int retval = -1; - - clicon_debug(1, "%s", __FUNCTION__); - /* Set connected-user cookie */ - FCGX_FPrintF(r->out, "Content-Type: text/html\r\n"); - FCGX_FPrintF(r->out, "Location: /www/grideye.html\r\n"); - FCGX_FPrintF(r->out, "Set-Cookie: %s=; path=/; HttpOnly; Expires=Sun, 06 Nov 1994 08:49:37 GMT\r\n", - USER_COOKIE); - FCGX_FPrintF(r->out, "\r\n"); - - retval = 0; - - return retval; -} - -/*! Get remote-addr, get name as param, get project from config. - * @param[in] r Fastcgi request handle - * @param[in] pvec Vector of path (DOCUMENT_URI) - * @param[in] pn Length of path - * @param[in] qvec Vector of query string (QUERY_STRING) - * @param[in] dvec Stream input data - * in: id, name, agent_address - * create: sender and start it - */ -static int -api_callhome(FCGX_Request *r, - char **pvec, - int pn, - cvec *qvec, - cvec *dvec) -{ - int retval = -1; - cxobj *cx = NULL; - cxobj *cs = NULL; - cxobj *x; - cxobj *y; - char *agent_addr; - char *agent_name; - char *request; - char *id; - char *template = DEFAULT_TEMPLATE; /* sender template */ - cbuf *resbuf = NULL; - - clicon_debug(1, "%s", __FUNCTION__); - request = FCGX_GetParam("REQUEST_METHOD", r->envp); - if (strcmp(request, "POST")!=0 && strcmp(request, "PUT")!=0) - goto done; - agent_addr = FCGX_GetParam("REMOTE_ADDR", r->envp); - clicon_debug(1, "%s agent_addr:%s", __FUNCTION__, agent_addr); - FCGX_SetExitStatus(201, r->out); - FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n"); - FCGX_FPrintF(r->out, "\r\n"); - /* Get user id=& agent name= from POST data stream (required) */ - if ((id = cvec_find_str(dvec, "id")) == NULL) - goto done; - clicon_debug(1, "%s id:%s", __FUNCTION__, id); - if ((agent_name = cvec_find_str(dvec, "name")) == NULL) - goto done; - clicon_debug(1, "%s agent_name:%s", __FUNCTION__, agent_name); - /* Get user configuration from cookie (=id) */ - if (get_db_entry("user", "id", id, &cx) < 0) - goto done; - if (check_credentials(NULL, cx) == 0) - goto done; - if ((x = xpath_first(cx, "//user/template")) != NULL) - template = xml_body(x); - clicon_debug(1, "%s: template=%s", __FUNCTION__, template); - - /* Check if sender = agent_name exists, if not create it using template, addr, name */ - if (get_db_entry("sender", "name", agent_name, &cs) < 0) - goto done; - if ((x = xpath_first(cx, "//sender/name")) == NULL){ - clicon_debug(1, "%s: create sender %s", __FUNCTION__, agent_name); - if (cli_rpc(resbuf, "configure", "sender %s template %s", agent_name, template) < 0) - goto done; - if (cli_rpc(resbuf, "configure", "sender %s ipv4_daddr %s", agent_name, agent_addr) < 0) - goto done; - if (cli_rpc(resbuf, "configure", "sender %s userid %s", agent_name, id) < 0) - goto done; - if (cli_rpc(resbuf, "configure", "sender %s start true", agent_name) < 0) - goto done; - if (cli_rpc(resbuf, "configure", "commit") < 0) - goto done; - } - else { - /* Check if userid exists */ - if ((y = xpath_first(x, "//sender/userid")) == NULL) - goto done; - /* Check if userid matches our id */ - if (strcmp(id, xml_body(y))) - goto done; - /* Check if started */ - if ((y = xpath_first(x, "//sender/start")) == NULL) - goto done; - if (strcmp("true", xml_body(y))) - goto done; - /* Check if IP address changed */ - if ((y = xpath_first(x, "//sender/ipv4_daddr")) == NULL) - goto done; - if (strcmp(agent_addr, xml_body(y))) - goto done; - } - /* We should be hunky dory */ - - FCGX_FPrintF(r->out, "template = '%s'\n", template?template:"(null)"); - FCGX_FPrintF(r->out, "agent_addr = '%s'\n", agent_addr); - FCGX_FPrintF(r->out, "agent_name = '%s'\n", agent_name); - - retval = 0; - done: - if (cx) - xml_free(cx); - if (cs) - xml_free(cs); - if (resbuf) - cbuf_free(resbuf); - return retval; -} - -/*! Get list of metric rules in a profile - * GET api/metric_rules/[/] - * @param[in] r Fastcgi request handle - * @param[in] pvec Vector of path (DOCUMENT_URI) - * @param[in] pn Length of path - * @param[in] qvec Vector of query string (QUERY_STRING) - * @param[in] dvec Stream input data - * @retval [ - {metric:"rtt", - perc: 1, - warningval:"john", - errorval: "au", - op: "eq"} - ]; - * XXX a fifth element in this record type defined in java script contains sender/host list - XXX Assume if anything in query_string, it is ?enable=true - */ -static int -api_metric_rules(FCGX_Request *r, - char **pvec, - int pn, - cvec *qvec, - cvec *dvec) -{ - int retval = -1; - char *sender; - char *metric0 = NULL; - cbuf *resbuf = NULL; - cxobj *ax = NULL; - cxobj *x; - char *xmlstr; - cxobj **xv = NULL; - size_t xlen; - int i; - int j; - char *metric; - cxobj *percentile; - cxobj *warningval; - cxobj *errorval; - cxobj *enableval; - cxobj *op; - - clicon_debug(1, "%s", __FUNCTION__); - if (pn < 1) - goto done; - sender = pvec[0]; - if (pn < 2) - metric0 = pvec[1]; - if ((resbuf = cbuf_new()) == NULL) - goto done; - if (netconf_rpc(resbuf, - "]]>]]>", sender) < 0){ - clicon_debug(1, "%s error", __FUNCTION__); - goto done; - } - - FCGX_SetExitStatus(201, r->out); - FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n"); - FCGX_FPrintF(r->out, "\r\n"); - xmlstr = cbuf_get(resbuf); - if (clicon_xml_parse_string(&xmlstr, &ax) < 0){ - clicon_debug(1, "err:%d reason:%s", clicon_errno, clicon_err_reason); - goto done; - } - if (cvec_len(qvec)){ - /* XXX Assume qvec == enable=true*/ - if (xpath_vec(ax, "//profile/metric/*[enable=true]", &xv, &xlen) < 0) - goto done; - } - else - if (xpath_vec(ax, "//profile/metric/*", &xv, &xlen) < 0) - goto done; - FCGX_FPrintF(r->out, "["); - j = 0; - for (i=0; iout, ","); - FCGX_FPrintF(r->out, "{\"metric\":\"%s\",\"perc\":\"%s\",\"warningval\":\"%s\",\"errorval\":\"%s\",\"op\":\"%s\", \"enable\":\"%s\"}", - metric, - percentile?xml_body(percentile):"95", - warningval?xml_body(warningval):"0", - errorval?xml_body(errorval):"0", - xml_body(op), - enableval?xml_body(enableval):"false" - ); - } - } - FCGX_FPrintF(r->out, "]"); - FCGX_FPrintF(r->out, "\r\n"); - - retval = 0; - done: - if (ax) - xml_free(ax); - if (xv) - free(xv); - if (resbuf) - cbuf_free(resbuf); - return retval; -} - - -/*! Get list of metrics from yang spec - * @param[in] r Fastcgi request handle - * @param[in] pvec Vector of path (DOCUMENT_URI) - * @param[in] pn Length of path - * @param[in] qvec Vector of query string (QUERY_STRING) - * @param[in] dvec Stream input data - - * GET api/metrics - * @retval ["rtt", "tior", ...]; - */ -static int -api_metrics(FCGX_Request *r, - char **pvec, - int pn, - cvec *qvec, - cvec *dvec) -{ - int retval = -1; - cbuf *resbuf = NULL; - char *xmlstr; - cxobj *mx = NULL; - cxobj *dx; - cxobj *x = NULL; - int i; - - if ((resbuf = cbuf_new()) == NULL) - goto done; - if (netconf_rpc(resbuf, - "]]>]]>") < 0){ - clicon_debug(1, "%s error", __FUNCTION__); - goto done; - } - xmlstr = cbuf_get(resbuf); - if (clicon_xml_parse_string(&xmlstr, &mx) < 0){ - clicon_debug(1, "err:%d reason:%s", clicon_errno, clicon_err_reason); - goto done; - } - FCGX_SetExitStatus(201, r->out); - FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n"); - FCGX_FPrintF(r->out, "\r\n"); - // FCGX_FPrintF(r->out, "{ \"metrics\":["); - - FCGX_FPrintF(r->out, "["); - if ((dx = xpath_first(mx, "//data")) != NULL){ - x = NULL; - i = 0; - while ((x = xml_child_each(dx, x, -1)) != NULL) { - if (i++) - FCGX_FPrintF(r->out, ", "); - FCGX_FPrintF(r->out, "\"%s\"", xml_name(x)); - } - } - FCGX_FPrintF(r->out, "]\r\n"); - - retval = 0; - done: - if (mx) - xml_free(mx); - if (resbuf) - cbuf_free(resbuf); - return retval; -} - -/*! - * @note XXX hardcoded that 'nordunet' is template and not included - */ -static int -api_agents(FCGX_Request *r, - char **pvec, - int pn, - cvec *qvec, - cvec *dvec) -{ - int retval = -1; - cxobj *ax = NULL; - cxobj **xvec = NULL; - cxobj *x = NULL; - int i; - int j; - size_t xlen; - - if (get_db_entry("sender", NULL, NULL, &ax) < 0) - goto done; - if (xpath_vec(ax, "//sender", &xvec, &xlen) < 0) - goto done; - - FCGX_SetExitStatus(201, r->out); - FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n"); - FCGX_FPrintF(r->out, "\r\n"); - // FCGX_FPrintF(r->out, "{ \"agents\":["); - FCGX_FPrintF(r->out, "["); - x = NULL; - j = 0; - for (i=0; i< xlen; i++){ - x = xvec[i]; - /* XXX: 'nordunet' hardcoded */ - if (strcmp("nordunet", xml_find_body(x, "name")) == 0) - continue; - if (j++) - FCGX_FPrintF(r->out, ", "); - - FCGX_FPrintF(r->out, "\"%s\"", xml_find_body(x, "name")); - } - FCGX_FPrintF(r->out, "]\r\n"); - - retval = 0; - done: - if (xvec) - free(xvec); - if (ax) - xml_free(ax); - return retval; -} - - -/*! Get metric specification from yang - * @param[in] r Fastcgi request handle - * @param[in] pvec Vector of path (DOCUMENT_URI) - * @param[in] pn Length of path - * @param[in] qvec Vector of query string (QUERY_STRING) - * @param[in] dvec Stream input data - * - * GET api/metric_spec?name=rtt - * @retval [ { - * "rtt": { - * "description": "Round-trip latency", - * "type": "uint32", - * "units": "µs" - * } - * }] - */ -static int -api_metric_spec(FCGX_Request *r, - char **pvec, - int pn, - cvec *qvec, - cvec *dvec) -{ - int retval = -1; - cbuf *resbuf = NULL; - char *xmlstr; - cxobj *mx = NULL; - cxobj *dx; - cxobj *sx; - char *metric; - cxobj *x = NULL; - int i; - int j; - - clicon_debug(1, "%s", __FUNCTION__); - if ((resbuf = cbuf_new()) == NULL) - goto done; - if ((metric = cvec_find_str(qvec, "name")) == NULL){ - if (netconf_rpc(resbuf, - "]]>]]>") < 0){ - clicon_debug(1, "%s error", __FUNCTION__); - goto done; - } - } - else { - if (netconf_rpc(resbuf, - "%s]]>]]>", - metric) < 0){ - clicon_debug(1, "%s error", __FUNCTION__); - goto done; - } - } - xmlstr = cbuf_get(resbuf); - clicon_debug(1, "%s xmlstr: %s", __FUNCTION__, xmlstr); - if (clicon_xml_parse_string(&xmlstr, &mx) < 0){ - clicon_debug(1, "err:%d reason:%s", clicon_errno, clicon_err_reason); - goto done; - } - FCGX_SetExitStatus(201, r->out); - FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n"); - FCGX_FPrintF(r->out, "\r\n"); - // FCGX_FPrintF(r->out, "{ \"%s\":[", ); - FCGX_FPrintF(r->out, "[{"); - /* - -{ - "rtt":{ - "description":"Round-trip latency", - "type":"uint32", - "units":"µs" - }} */ - if ((dx = xpath_first(mx, "//data")) != NULL){ - j = 0; - x = NULL; - while ((x = xml_child_each(dx, x, -1)) != NULL) { - if (j++) - FCGX_FPrintF(r->out, ", "); - FCGX_FPrintF(r->out, "\"%s\":{", xml_name(x)); - sx = NULL; - i = 0; - while ((sx = xml_child_each(x, sx, -1)) != NULL) { - if (i++) - FCGX_FPrintF(r->out, ", "); - FCGX_FPrintF(r->out, "\"%s\":\"%s\"", - xml_name(sx), xml_body(sx)); - } - FCGX_FPrintF(r->out, "}"); - } - } - FCGX_FPrintF(r->out, "}]\r\n"); - - retval = 0; - done: - if (mx) - xml_free(mx); - if (resbuf) - cbuf_free(resbuf); - return retval; -} - -/*! Generic REST GET method - * @param[in] r Fastcgi request handle - * @param[in] pcvec Vector of path ie DOCUMENT_URI element - * @param[in] pi Offset, where to start pcvec - * @param[in] dvec Stream input data - * @param[in] qvec Vector of query string (QUERY_STRING) - * Example: - * curl -G http://localhost/api/data/profile/name=default/metric/rtt - */ -static int -api_data_get(FCGX_Request *r, - cvec *pcvec, - int pi, - cvec *qvec) +api_data_get(clicon_handle h, + FCGX_Request *r, + cvec *pcvec, + int pi, + cvec *qvec) { int retval = -1; cg_var *cv; char *val; int i; - cbuf *res = NULL; cbuf *path = NULL; cbuf *path1 = NULL; - char *xmlstr; cxobj *xt = NULL; cxobj *xg = NULL; cbuf *cbx = NULL; - + cxobj **vec = NULL; + size_t veclen; + clicon_debug(1, "%s", __FUNCTION__); if ((path = cbuf_new()) == NULL) - goto done; + goto done; if ((path1 = cbuf_new()) == NULL) /* without [] qualifiers */ - goto done; + goto done; cv = NULL; cprintf(path1, "/"); /* translate eg a/b=c -> a/[b=c] */ for (i=pi; i 0){ - if ((val = cv2str_dup(cv)) == NULL) - goto done; - cprintf(path, "[%s=%s]", cv_name_get(cv), val); - free(val); - } - else{ - cprintf(path, "%s%s", (i==pi?"":"/"), cv_name_get(cv)); - cprintf(path1, "/%s", cv_name_get(cv)); - } + cv = cvec_i(pcvec, i); + if (cv2str(cv, NULL, 0) > 0){ + if ((val = cv2str_dup(cv)) == NULL) + goto done; + cprintf(path, "[%s=%s]", cv_name_get(cv), val); + free(val); + } + else{ + cprintf(path, "%s%s", (i==pi?"":"/"), cv_name_get(cv)); + cprintf(path1, "/%s", cv_name_get(cv)); + } } - clicon_debug(1, "path:%s", cbuf_get(path)); - if ((res = cbuf_new()) == NULL) - goto done; - if (netconf_rpc(res, - "]]>]]>", - cbuf_get(path)) < 0){ - clicon_debug(1, "%s error", __FUNCTION__); + clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path)); + clicon_debug(1, "%s path1:%s", __FUNCTION__, cbuf_get(path1)); + /* See netconf_rpc.c: 163 netconf_filter_xmldb() */ + + if (xmldb_get(h, "running", cbuf_get(path), 0, &xt, NULL, NULL) < 0) goto done; + { + cbuf *cb; + cb = cbuf_new(); + if (clicon_xml2cbuf(cb, xt, 0, 1) < 0) + goto done; + clicon_debug(1, "%s xt: %s", __FUNCTION__, cbuf_get(cb)); } - FCGX_SetExitStatus(201, r->out); + FCGX_SetExitStatus(200, r->out); /* OK */ + FCGX_FPrintF(r->out, "Content-Type: application/yang.data+xml\r\n"); FCGX_FPrintF(r->out, "\r\n"); - xmlstr = cbuf_get(res); - if (clicon_xml_parse_string(&xmlstr, &xt) < 0){ - clicon_debug(1, "err:%d reason:%s", clicon_errno, clicon_err_reason); - goto done; - } - if ((xg = xpath_first(xt, cbuf_get(path1))) != NULL){ - if ((cbx = cbuf_new()) == NULL) - goto done; - if (clicon_xml2cbuf(cbx, xg, 0, 0) < 0) - goto done; - FCGX_FPrintF(r->out, "%s\r\n", cbuf_get(cbx)); + /* Iterate over result */ + if (xpath_vec(xt, cbuf_get(path1), &vec, &veclen) < 0) + goto done; + if ((cbx = cbuf_new()) == NULL) + goto done; + cprintf(cbx, "[\n"); + for (i=0; iout, "%s\r\n", cbuf_get(cbx)); retval = 0; done: + if (vec) + free(vec); if (cbx) - cbuf_free(cbx); + cbuf_free(cbx); if (xt) - xml_free(xt); - if (res) - cbuf_free(res); + xml_free(xt); if (path) - cbuf_free(path); + cbuf_free(path); if (path1) - cbuf_free(path1); + cbuf_free(path1); return retval; } @@ -912,7 +180,8 @@ api_data_get(FCGX_Request *r, * curl -X PUT -d enable=true http://localhost/api/data/profile=default/metric=rtt */ static int -api_data_put(FCGX_Request *r, +api_data_put(clicon_handle h, + FCGX_Request *r, cvec *pcvec, int pi, cvec *qvec, @@ -975,7 +244,8 @@ api_data_put(FCGX_Request *r, * @note cant do leafs */ static int -api_data_delete(FCGX_Request *r, +api_data_delete(clicon_handle h, + FCGX_Request *r, cvec *pcvec, int pi, cvec *qvec) @@ -1036,7 +306,8 @@ api_data_delete(FCGX_Request *r, * XXX But really this module should be a restconf module to clixon */ static int -api_data(FCGX_Request *r, +api_data(clicon_handle h, + FCGX_Request *r, cvec *pcvec, int pi, cvec *qvec, @@ -1048,147 +319,23 @@ api_data(FCGX_Request *r, clicon_debug(1, "%s", __FUNCTION__); request_method = FCGX_GetParam("REQUEST_METHOD", r->envp); if (strcmp(request_method, "GET")==0) - retval = api_data_get(r, pcvec, pi, qvec); + retval = api_data_get(h, r, pcvec, pi, qvec); else if (strcmp(request_method, "PUT")==0) - retval = api_data_put(r, pcvec, pi, qvec, dvec); + retval = api_data_put(h, r, pcvec, pi, qvec, dvec); else if (strcmp(request_method, "DELETE")==0) - retval = api_data_delete(r, pcvec, pi, qvec); + retval = api_data_delete(h, r, pcvec, pi, qvec); else retval = notfound(r); return retval; } -/*! Get user (from cookie) - * @param[in] r Fastcgi request handle - * @param[in] pvec Vector of path (DOCUMENT_URI) - * @param[in] pn Length of path - * @param[in] qvec Vector of query string (QUERY_STRING) - * @param[in] dvec Stream input data - - * @retval { - name:"kalle", - template: "asender", - profile: "default", - dbuser:"john", - dbpasswd: "au", - agents: ["a1", "a2",..] - } - */ -static int -api_user(FCGX_Request *r, - char **pvec, - int pn, - cvec *qvec, - cvec *dvec) -{ - int retval = -1; - char *cookie; - char *cval = NULL; - cxobj *ux = NULL; - cxobj *sx = NULL; - cxobj *x = NULL; - char *user; - char *id; - char *dbuser; - char *dbpasswd; - char *plot_ival; - char *plot_dur; - char *profile = NULL; - cxobj *profx = NULL; - char *template = DEFAULT_TEMPLATE; - cxobj **xv = NULL; - size_t xlen; - int i; - - clicon_debug(1, "%s", __FUNCTION__); - if ((cookie = FCGX_GetParam("HTTP_COOKIE", r->envp)) == NULL){ - FCGX_SetExitStatus(403, r->out); - goto done; - } - if (get_user_cookie(cookie, USER_COOKIE, &cval) <0) - goto done; - if (cval == NULL){ /* Cookie not found, same as no cookie string found */ - FCGX_SetExitStatus(403, r->out); - goto done; - } - if (get_db_entry("user", "id", cval, &ux) < 0) - goto done; - if (check_credentials(NULL, ux) == 0){ - FCGX_SetExitStatus(403, r->out); - goto done; - } - if ((x = xpath_first(ux, "//user/name")) == NULL) - goto done; - user = xml_body(x); - if ((x = xpath_first(ux, "//user/template")) != NULL) - template = xml_body(x); - if ((x = xpath_first(ux, "//user/profile")) != NULL) - profile = xml_body(x); - - - if ((x = xpath_first(ux, "//user/id")) == NULL) - goto done; - id = xml_body(x); - if ((x = xpath_first(ux, "//user/resultdb/username")) == NULL) - goto done; - dbuser = xml_body(x); - if ((x = xpath_first(ux, "//user/resultdb/password")) == NULL) - goto done; - dbpasswd = xml_body(x); - /* Get profile including plot settings and metric rules */ - if (get_db_entry("profile", "name", profile, &profx) < 0) - goto done; - - if ((x = xpath_first(profx, "//profile/interval")) == NULL) - goto done; - plot_ival = xml_body(x); - if ((x = xpath_first(profx, "//profile/duration")) == NULL) - goto done; - plot_dur = xml_body(x); - /* Get all senders with matching id */ - if (get_db_entry("sender", "userid", cval, &sx) < 0) - goto done; - FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n"); - FCGX_FPrintF(r->out, "\r\n"); - FCGX_SetExitStatus(201, r->out); - FCGX_FPrintF(r->out, "{\"name\": \"%s\"," - "\"id\":\"%s\"," - "\"template\":\"%s\"," - "\"profile\":\"%s\"," - "\"dbuser\":\"%s\"," - "\"dbpasswd\":\"%s\"," - "\"plot_ival\":\"%s\"," - "\"plot_dur\":\"%s\"," - "\"agents\":[", - user, id, template, profile, dbuser, dbpasswd, - plot_ival, plot_dur); - if (xpath_vec(sx, "//sender", &xv, &xlen) < 0) - goto done; - for (i=0; iout, ","); - FCGX_FPrintF(r->out, "\"%s\"", xml_body(x)); - } - FCGX_FPrintF(r->out, "]}"); - retval = 0; - done: - clicon_debug(1, "%s end %d", __FUNCTION__, retval); - if (xv) - free(xv); - if (ux) - xml_free(ux); - if (sx) - xml_free(sx); - return retval; -} /*! Process a FastCGI request * @param[in] r Fastcgi request handle */ static int -request_process(FCGX_Request *r) +request_process(clicon_handle h, + FCGX_Request *r) { int retval = -1; char *path; @@ -1222,44 +369,8 @@ request_process(FCGX_Request *r) method = pvec[2]; retval = 0; test(r, 1); - if (strcmp(method, "cli") == 0) - retval = api_cli(r, pvec+3, pn-3, - qvec, data); - else if (strcmp(method, "netconf") == 0) - retval = api_netconf(r, pvec+3, pn-3, - qvec, data); - else if (strcmp(method, "login") == 0) - retval = api_login(r, pvec+3, pn-3, - qvec, dvec); - else if (strcmp(method, "logout") == 0) - retval = api_logout(r, pvec+3, pn-3, - qvec, dvec); - else if (strcmp(method, "signup") == 0) - retval = api_signup(r, pvec+3, pn-3, - qvec, dvec); - else if (strcmp(method, "settings") == 0) - retval = api_settings(r, pvec+3, pn-3, - qvec, dvec); - else if (strcmp(method, "callhome") == 0) - retval = api_callhome(r, pvec+3, pn-3, - qvec, dvec); - else if (strcmp(method, "metric_rules") == 0) - retval = api_metric_rules(r, pvec+3, pn-3, - qvec, dvec); - else if (strcmp(method, "metrics") == 0) - retval = api_metrics(r, pvec+3, pn-3, - qvec, dvec); - else if (strcmp(method, "agents") == 0) - retval = api_agents(r, pvec+3, pn-3, - qvec, dvec); - else if (strcmp(method, "metric_spec") == 0) - retval = api_metric_spec(r, pvec+3, pn-3, - qvec, dvec); - else if (strcmp(method, "data") == 0) /* restconf, skip /api/data */ - retval = api_data(r, pcvec, 2, qvec, dvec); - else if (strcmp(method, "user") == 0) - retval = api_user(r, pvec+3, pn-3, - qvec, dvec); + if (strcmp(method, "data") == 0) /* restconf, skip /api/data */ + retval = api_data(h, r, pcvec, 2, qvec, dvec); else if (strcmp(method, "test") == 0) retval = test(r, 0); else @@ -1343,20 +454,24 @@ main(int argc, /* Find and read configfile */ if (clicon_options_main(h) < 0) goto done; - clicon_debug(1, "%s", argv[0]); + + /* Parse yang database spec file */ + if (yang_spec_main(h, stdout, 0) < 0) + goto done; if ((sockpath = clicon_option_str(h, "CLICON_RESTCONF_PATH")) == NULL){ clicon_err(OE_CFG, errno, "No CLICON_RESTCONF_PATH in clixon configure file"); goto done; } - if ((sock = FCGX_OpenSocket(sockpath, 10)) < 0){ - clicon_err(OE_CFG, errno, "FCGX_OpenSocket"); - goto done; - } if (FCGX_Init() != 0){ clicon_err(OE_CFG, errno, "FCGX_Init"); goto done; } + if ((sock = FCGX_OpenSocket(sockpath, 10)) < 0){ + clicon_err(OE_CFG, errno, "FCGX_OpenSocket"); + goto done; + } + if (FCGX_InitRequest(r, sock, 0) != 0){ clicon_err(OE_CFG, errno, "FCGX_InitRequest"); goto done; @@ -1370,7 +485,7 @@ main(int argc, if ((path = FCGX_GetParam("DOCUMENT_URI", r->envp)) != NULL){ if (strncmp(path, RESTCONF_API_ROOT, strlen(RESTCONF_API_ROOT)) == 0 || strncmp(path, RESTCONF_API_ROOT, strlen(RESTCONF_API_ROOT)-1) == 0) - request_process(r); + request_process(h, r); else{ clicon_debug(1, "top-level not found"); notfound(r); diff --git a/clixon.conf.cpp.cpp b/clixon.conf.cpp.cpp index dfb24e57..2ab30581 100644 --- a/clixon.conf.cpp.cpp +++ b/clixon.conf.cpp.cpp @@ -120,6 +120,6 @@ CLICON_XMLDB_DIR localstatedir/APPNAME # FastCGI unix socket. Should be specified in webserver # Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock; -CLICON_RESTCONF_PATH "/www-data/clicon_restconf.sock" +CLICON_RESTCONF_PATH /www-data/fastcgi_restconf.sock diff --git a/lib/src/clixon_xml_db.c b/lib/src/clixon_xml_db.c index 404efabe..a16a5aad 100644 --- a/lib/src/clixon_xml_db.c +++ b/lib/src/clixon_xml_db.c @@ -957,7 +957,10 @@ xmldb_get_local(clicon_handle h, clicon_err(OE_XML, 0, "dbname NULL"); goto done; } - yspec = clicon_dbspec_yang(h); + if ((yspec = clicon_dbspec_yang(h)) == NULL){ + clicon_err(OE_YANG, ENOENT, "No yang spec"); + goto done; + } if (vector){ if (xmldb_get_vec(dbname, xpath, yspec, xtop, xvec, xlen) < 0) goto done; @@ -989,7 +992,7 @@ xmldb_get_local(clicon_handle h, * cxobj *xt; * cxobj **xvec; * size_t xlen; - * yang_spec *yspec = clicon_dbspec_yang(h); + * * if (xmldb_get("running", "/interfaces/interface[name="eth"]", * 1, &xt, &xvec, &xlen) < 0) * err; @@ -1011,11 +1014,13 @@ xmldb_get(clicon_handle h, cxobj ***xvec, size_t *xlen) { - assert(xpath); + int retval = -1; + if (clicon_xmldb_rpc(h)) - return xmldb_get_rpc(h, db, xpath, vector, xtop, xvec, xlen); + retval = xmldb_get_rpc(h, db, xpath, vector, xtop, xvec, xlen); else - return xmldb_get_local(h, db, xpath, vector, xtop, xvec, xlen); + retval = xmldb_get_local(h, db, xpath, vector, xtop, xvec, xlen); + return retval; } /*! Get value of the "operation" attribute and change op if given