diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index b9016e92..334a9e42 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -180,14 +180,12 @@ cli_signal_flush(clicon_handle h) cli_signal_block (h); } -/*! Transform data - * Add next-last cvv (resolved variable) as body to xml bottom for leaf and - * leaf-list. - * There may be some translation necessary. +/*! Create body and add last CLI variable vector as value + * Create and add an XML body as child of XML node xbot. Set its value to the last + * CLI variable vector element. */ static int dbxml_body(cxobj *xbot, - yang_stmt *ybot, cvec *cvv) { int retval = -1; @@ -214,12 +212,14 @@ dbxml_body(cxobj *xbot, } /*! Modify xml datastore from a callback using xml key format strings - * @param[in] h Clicon handle - * @param[in] cvv Vector of cli string and instantiated variables - * @param[in] argv Vector. First element xml key format string, eg "/aaa/%s" - * @param[in] op Operation to perform on database + * @param[in] h Clicon handle + * @param[in] cvv Vector of cli string and instantiated variables + * @param[in] argv Vector. First element xml key format string, eg "/aaa/%s" + * @param[in] op Operation to perform on database + * @param[in] nsctx Namespace context for last value added * Cvv will contain first the complete cli string, and then a set of optional * instantiated variables. + * If the last node is a leaf, the last cvv element is added as a value. This value * Example: * cvv[0] = "set interfaces interface eth0 type bgp" * cvv[1] = "eth0" @@ -227,12 +227,16 @@ dbxml_body(cxobj *xbot, * argv[0] = "/interfaces/interface/%s/type" * op: OP_MERGE * @see cli_callback_generate where arg is generated + * @note The last value may require namespace binding present in nsctx. Note that the nsctx + * cannot normally be supplied by the clispec functions, such as cli_set, but need to be + * generated by afunction such as clixon_instance_id_bind() or other programmatically. */ -static int +int cli_dbxml(clicon_handle h, cvec *cvv, cvec *argv, - enum operation_type op) + enum operation_type op, + cvec *nsctx) { int retval = -1; char *api_path_fmt; /* xml key format */ @@ -246,6 +250,7 @@ cli_dbxml(clicon_handle h, cxobj *xa; /* attribute */ cxobj *xerr = NULL; int ret; + cg_var *cv; if (cvec_len(argv) != 1){ clicon_err(OE_PLUGIN, 0, "Requires one element to be xml key format string"); @@ -284,10 +289,20 @@ cli_dbxml(clicon_handle h, goto done; if (xml_value_set(xa, xml_operation2str(op)) < 0) goto done; - if (yang_keyword_get(y) != Y_LIST && yang_keyword_get(y) != Y_LEAF_LIST){ - if (cvec_len(cvv) > 1 && - dbxml_body(xbot, y, cvv) < 0) + /* should it not be list & container?? */ + if (yang_keyword_get(y) != Y_LIST && + yang_keyword_get(y) != Y_LEAF_LIST && + cvec_len(cvv) > 1){ + if (dbxml_body(xbot, cvv) < 0) goto done; + /* Loop over namespace context and add them for the value */ + cv = NULL; + while ((cv = cvec_each(nsctx, cv)) != NULL){ + char *ns = cv_string_get(cv); + char *pf = cv_name_get(cv); + if (ns && pf && xmlns_set(xbot, pf, ns) < 0) + goto done; + } } if ((cb = cbuf_new()) == NULL){ clicon_err(OE_XML, errno, "cbuf_new"); @@ -326,7 +341,7 @@ cli_set(clicon_handle h, { int retval = -1; - if (cli_dbxml(h, cvv, argv, OP_REPLACE) < 0) + if (cli_dbxml(h, cvv, argv, OP_REPLACE, NULL) < 0) goto done; retval = 0; done: @@ -345,7 +360,7 @@ cli_merge(clicon_handle h, { int retval = -1; - if (cli_dbxml(h, cvv, argv, OP_MERGE) < 0) + if (cli_dbxml(h, cvv, argv, OP_MERGE, NULL) < 0) goto done; retval = 0; done: @@ -364,7 +379,7 @@ cli_create(clicon_handle h, { int retval = -1; - if (cli_dbxml(h, cvv, argv, OP_CREATE) < 0) + if (cli_dbxml(h, cvv, argv, OP_CREATE, NULL) < 0) goto done; retval = 0; done: @@ -383,7 +398,7 @@ cli_remove(clicon_handle h, { int retval = -1; - if (cli_dbxml(h, cvv, argv, OP_REMOVE) < 0) + if (cli_dbxml(h, cvv, argv, OP_REMOVE, NULL) < 0) goto done; retval = 0; done: @@ -402,7 +417,7 @@ cli_del(clicon_handle h, { int retval = -1; - if (cli_dbxml(h, cvv, argv, OP_REMOVE) < 0) + if (cli_dbxml(h, cvv, argv, OP_REMOVE, NULL) < 0) goto done; retval = 0; done: diff --git a/apps/cli/clixon_cli_api.h b/apps/cli/clixon_cli_api.h index c7c31040..3acfe90f 100644 --- a/apps/cli/clixon_cli_api.h +++ b/apps/cli/clixon_cli_api.h @@ -69,6 +69,8 @@ int cli_notification_register(clicon_handle h, char *stream, enum format_enum fo /* cli_common.c: CLIgen new vector callbacks */ +int cli_dbxml(clicon_handle h, cvec *vars, cvec *argv, cvec *nsctx); + int cli_set(clicon_handle h, cvec *vars, cvec *argv); int cli_merge(clicon_handle h, cvec *vars, cvec *argv); diff --git a/lib/clixon/clixon_path.h b/lib/clixon/clixon_path.h index 08b717fc..ecb8f347 100644 --- a/lib/clixon/clixon_path.h +++ b/lib/clixon/clixon_path.h @@ -89,9 +89,11 @@ int clixon_xml_find_api_path(cxobj *xt, yang_stmt *yt, cxobj ***xvec, int *xlen, ...) __attribute__ ((format (printf, 5, 6)));; int clixon_xml_find_instance_id(cxobj *xt, yang_stmt *yt, cxobj ***xvec, int *xlen, const char *format, ...) __attribute__ ((format (printf, 5, 6)));; +int clixon_instance_id_bind(yang_stmt *yt, cvec *nsctx, const char *format, ...) __attribute__ ((format (printf, 3, 4))); #else int clixon_xml_find_api_path(cxobj *xt, yang_stmt *yt, cxobj ***xvec, int *xlen, const char *format, ...); int clixon_xml_find_instance_id(cxobj *xt, yang_stmt *yt, cxobj ***xvec, int *xlen, const char *format, ...); +int clixon_instance_id_bind(yang_stmt *yt, cvec *nsctx, const char *format, ...); #endif #endif /* _CLIXON_PATH_H_ */ diff --git a/lib/src/clixon_path.c b/lib/src/clixon_path.c index 4492fa28..672230f1 100644 --- a/lib/src/clixon_path.c +++ b/lib/src/clixon_path.c @@ -1683,3 +1683,94 @@ clixon_xml_find_instance_id(cxobj *xt, goto done; } +/*! Given (instance-id) path and YANG, parse path, resolve YANG and return namespace binding + * + * Instance-identifier is a subset of XML XPaths and defined in Yang, used in NACM for + * example. + * @param[in] yt Yang statement of top symbol (can be yang-spec if top-level) + * @param[out] nsctx Namespace context (should be created on entry) + * @param[in] format Format string for api-path syntax + * @retval -1 Error + * @retval 0 Non-fatal failure, yang bind failures, etc, + * @retval 1 OK with found xml nodes in xvec (if any) + * Reasons for nomatch (retval = 0) are: + * - Modulename in api-path does not correspond to existing module + * - Modulename not defined for top-level id. + * - Number of keys in key-value list does not match Yang list + * @code + * cvec *nsctx = NULL; + * + * nsctx = cvec_new(0); + * if (clixon_instance_id_bind(yspec, nsctx, "/symbol/%s", "foo") < 0) + * goto err; + * ... + * cvec_free(nsctx); + * @endcode + * @note canonical namespace contexts are used, see xpath2canonical + * @see clixon_xml_find_instance_id for finding XML nodes using instance-id:s + * @see RFC7950 Sec 9.13 + */ +int +clixon_instance_id_bind(yang_stmt *yt, + cvec *nsctx, + const char *format, + ...) +{ + int retval = -1; + va_list ap; + size_t len; + char *path = NULL; + clixon_path *cplist = NULL; + clixon_path *cp; + int ret; + char *namespace; + + va_start(ap, format); + len = vsnprintf(NULL, 0, format, ap); + va_end(ap); + /* allocate a path string exactly fitting the length */ + if ((path = malloc(len+1)) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + /* second round: actually compute api-path string content */ + va_start(ap, format); + if (vsnprintf(path, len+1, format, ap) < 0){ + clicon_err(OE_UNIX, errno, "vsnprintf"); + va_end(ap); + goto done; + } + va_end(ap); + if (instance_id_parse(path, &cplist) < 0) + goto done; + if (clicon_debug_get()) + clixon_path_print(stderr, cplist); + /* Resolve module:name to pointer to yang-stmt, fail if not successful */ + if ((ret = instance_id_resolve(cplist, yt)) < 0) + goto done; + if (ret == 0) + goto fail; + /* Loop through prefixes used */ + if ((cp = cplist) != NULL){ + do { + if (cp->cp_prefix && + cp->cp_yang && + (namespace = yang_find_mynamespace(cp->cp_yang)) != NULL){ + if (xml_nsctx_get(nsctx, cp->cp_prefix) == NULL) + if (xml_nsctx_add(nsctx, cp->cp_prefix, namespace) < 0) + goto done; + } + cp = NEXTQ(clixon_path *, cp); + } while (cp && cp != cplist); + } + retval = 1; + done: + if (cplist) + clixon_path_free(cplist); + if (path) + free(path); + return retval; + fail: + retval = 0; + goto done; +}