From 65c809b1c3262eadb1f54a28c1e1ee19eb5ef11f Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Wed, 24 Apr 2019 15:13:19 +0200 Subject: [PATCH 1/5] Removed external direct access to the yang_stmt struct. --- CHANGELOG.md | 1 + lib/clixon/clixon_xml_map.h | 3 ++ lib/clixon/clixon_yang.h | 75 +------------------------------ lib/clixon/clixon_yang_type.h | 3 +- lib/src/clixon_datastore_read.c | 4 +- lib/src/clixon_datastore_write.c | 11 ++--- lib/src/clixon_json.c | 8 ++-- lib/src/clixon_nacm.c | 2 +- lib/src/clixon_xml_changelog.c | 2 +- lib/src/clixon_xml_map.c | 29 ++++++------ lib/src/clixon_xml_sort.c | 21 +++++---- lib/src/clixon_yang.c | 1 + lib/src/clixon_yang_cardinality.c | 16 +++---- lib/src/clixon_yang_internal.h | 26 +++++++++++ lib/src/clixon_yang_module.c | 1 + lib/src/clixon_yang_parse.y | 1 + lib/src/clixon_yang_type.c | 1 + 17 files changed, 85 insertions(+), 120 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7445019b..652a597d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ * Change all y->ys_argument to yang_argument_get(y) * Change all y->ys_cv to yang_cv_get(y) * Change all y->ys_cvec to yang_cvec_get(y) + * Removed external direct access to the yang_stmt struct. * xmldb_get() removed unnecessary config option: * Change all calls to dbget from: `xmldb_get(h, db, xpath, 0|1, &xret, msd)` to `xmldb_get(h, db, xpath, &xret, msd)` diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index c1398cd2..7d6bc9f6 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -38,6 +38,9 @@ #ifndef _CLIXON_XML_MAP_H_ #define _CLIXON_XML_MAP_H_ +/* declared in clixon_yang_internal */ +typedef enum yang_class yang_class; + /* * Prototypes */ diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h index f9472bba..44919010 100644 --- a/lib/clixon/clixon_yang.h +++ b/lib/clixon/clixon_yang.h @@ -40,16 +40,10 @@ #define _CLIXON_YANG_H_ -/* - * Actually cligen variable stuff XXX - */ -#define V_UNIQUE 0x01 /* Variable flag */ -#define V_UNSET 0x08 /* Variable is unset, ie no default */ /* * Types */ -struct xml; /*! YANG keywords from RFC6020. * See also keywords generated by yacc/bison in clicon_yang_parse.tab.h, but they start with K_ * instead of Y_ @@ -142,85 +136,20 @@ enum yang_class{ }; typedef enum yang_class yang_class; -#define YANG_FLAG_MARK 0x01 /* Marker for dynamic algorithms, eg expand */ +struct xml; /* Yang data node * See RFC7950 Sec 3: * o data node: A node in the schema tree that can be instantiated in a * data tree. One of container, leaf, leaf-list, list, anydata, and * anyxml. + * XXX move to function */ #define yang_datanode(y) ((y)->ys_keyword == Y_CONTAINER || (y)->ys_keyword == Y_LEAF || (y)->ys_keyword == Y_LIST || (y)->ys_keyword == Y_LEAF_LIST || (y)->ys_keyword == Y_ANYXML || (y)->ys_keyword == Y_ANYDATA) -/* Yang data definition statement - * See RFC 7950 Sec 3: - * o data definition statement: A statement that defines new data - * nodes. One of "container", "leaf", "leaf-list", "list", "choice", - * "case", "augment", "uses", "anydata", and "anyxml". - */ -#define yang_datadefinition(y) (yang_datanode(y) || (y)->ys_keyword == Y_CHOICE || (y)->ys_keyword == Y_CASE || (y)->ys_keyword == Y_AUGMENT || (y)->ys_keyword == Y_USES) - -/* Yang schema node . - * See RFC 7950 Sec 3: - * o schema node: A node in the schema tree. One of action, container, - * leaf, leaf-list, list, choice, case, rpc, input, output, - * notification, anydata, and anyxml. - */ -#define yang_schemanode(y) (yang_datanode(y) || (y)->ys_keyword == Y_RPC || (y)->ys_keyword == Y_CHOICE || (y)->ys_keyword == Y_CASE || (y)->ys_keyword == Y_INPUT || (y)->ys_keyword == Y_OUTPUT || (y)->ys_keyword == Y_NOTIFICATION) - typedef struct yang_stmt yang_stmt; /* Defined in clixon_yang_internal */ -/*! Yang type cache. Yang type statements can cache all typedef info here - * @note unions not cached -*/ -struct yang_type_cache{ - int yc_options; /* See YANG_OPTIONS_* that determines pattern/ - fraction fields. */ - cvec *yc_cvv; /* Range and length restriction. (if YANG_OPTION_ - LENGTH|RANGE. Can be a vector if multiple - ranges*/ - char *yc_pattern; /* regex (posix) (if YANG_OPTIONS_PATTERN) */ - uint8_t yc_fraction; /* Fraction digits for decimal64 (if - YANG_OPTIONS_FRACTION_DIGITS */ - yang_stmt *yc_resolved; /* Resolved type object, can be NULL - note direct ptr */ -}; -typedef struct yang_type_cache yang_type_cache; - -/*! yang statement - */ - -struct yang_stmt{ - int ys_len; /* Number of children */ - struct yang_stmt **ys_stmt; /* Vector of children statement pointers */ - struct yang_stmt *ys_parent; /* Backpointer to parent: yang-stmt or yang-spec */ - enum rfc_6020 ys_keyword; /* See clicon_yang_parse.tab.h */ - - char *ys_argument; /* String / argument depending on keyword */ - int ys_flags; /* Flags according to YANG_FLAG_* above */ - yang_stmt *ys_module; /* Shortcut to "my" module. Augmented - nodes can belong to other - modules than the ancestor module */ - - char *ys_extra; /* For unknown */ - cg_var *ys_cv; /* cligen variable. See ys_populate() - Following stmts have cv:s: - leaf: for default value - leaf-list, - config: boolean true or false - mandatory: boolean true or false - fraction-digits for fraction-digits - unknown-stmt (argument) - */ - cvec *ys_cvec; /* List of stmt-specific variables - Y_RANGE: range_min, range_max - Y_LIST: vector of keys - Y_TYPE & identity: store all derived types - */ - yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */ - int _ys_vector_i; /* internal use: yn_each */ -}; - typedef int (yang_applyfn_t)(yang_stmt *ys, void *arg); /* diff --git a/lib/clixon/clixon_yang_type.h b/lib/clixon/clixon_yang_type.h index 2e859e47..86597f1b 100644 --- a/lib/clixon/clixon_yang_type.h +++ b/lib/clixon/clixon_yang_type.h @@ -49,7 +49,8 @@ /* * Types */ - +/* declared in clixon_yang_internal */ +typedef struct yang_type_cache yang_type_cache; /* * Prototypes diff --git a/lib/src/clixon_datastore_read.c b/lib/src/clixon_datastore_read.c index 80d3e3c6..d222f544 100644 --- a/lib/src/clixon_datastore_read.c +++ b/lib/src/clixon_datastore_read.c @@ -70,11 +70,11 @@ #include "clixon_data.h" #include "clixon_xpath_ctx.h" #include "clixon_xpath.h" -#include "clixon_xml_map.h" #include "clixon_json.h" #include "clixon_nacm.h" #include "clixon_netconf_lib.h" #include "clixon_yang_module.h" +#include "clixon_xml_map.h" #include "clixon_datastore.h" #include "clixon_datastore_read.h" @@ -184,7 +184,7 @@ xml_copy_marked(cxobj *x0, } /* (3) Special case: key nodes in lists are copied if any * node in list is marked */ - if (mark && yt && yt->ys_keyword == Y_LIST){ + if (mark && yt && yang_keyword_get(yt) == Y_LIST){ /* XXX: I think yang_key_match is suboptimal here */ if ((iskey = yang_key_match(yt, name)) < 0) goto done; diff --git a/lib/src/clixon_datastore_write.c b/lib/src/clixon_datastore_write.c index b1727945..13fa6440 100644 --- a/lib/src/clixon_datastore_write.c +++ b/lib/src/clixon_datastore_write.c @@ -71,11 +71,11 @@ #include "clixon_data.h" #include "clixon_xpath_ctx.h" #include "clixon_xpath.h" -#include "clixon_xml_map.h" #include "clixon_json.h" #include "clixon_nacm.h" #include "clixon_netconf_lib.h" #include "clixon_yang_module.h" +#include "clixon_xml_map.h" #include "clixon_datastore.h" #include "clixon_datastore_write.h" @@ -136,7 +136,7 @@ text_modify(clicon_handle h, if (xml_operation(opstr, &op) < 0) goto done; x1name = xml_name(x1); - if (y0->ys_keyword == Y_LEAF_LIST || y0->ys_keyword == Y_LEAF){ + if (yang_keyword_get(y0) == Y_LEAF_LIST || yang_keyword_get(y0) == Y_LEAF){ x1bstr = xml_body(x1); switch(op){ case OP_CREATE: @@ -174,7 +174,7 @@ text_modify(clicon_handle h, #if 0 /* If it is key I dont want to mark it */ - if ((iamkey=yang_key_match(y0->ys_parent, x1name)) < 0) + if ((iamkey=yang_key_match(yang_parent_get(y0), x1name)) < 0) goto done; if (!iamkey && op==OP_NONE) #else @@ -255,7 +255,8 @@ text_modify(clicon_handle h, can be modified in its entirety only. Any "operation" attributes present on subelements of an anyxml node are ignored by the NETCONF server.*/ - if (y0->ys_keyword == Y_ANYXML || y0->ys_keyword == Y_ANYDATA){ + if (yang_keyword_get(y0) == Y_ANYXML || + yang_keyword_get(y0) == Y_ANYDATA){ if (op == OP_NONE) break; if (op==OP_MERGE && !permit && xnacm){ @@ -546,7 +547,7 @@ xml_container_presence(cxobj *x, goto done; } /* Mark node that is: container, have no children, dont have presence */ - if (y->ys_keyword == Y_CONTAINER && + if (yang_keyword_get(y) == Y_CONTAINER && xml_child_nr_notype(x, CX_ATTR)==0 && yang_find(y, Y_PRESENCE, NULL) == NULL) xml_flag_set(x, XML_FLAG_MARK); /* Mark, remove later */ diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c index 428831e4..530b342c 100644 --- a/lib/src/clixon_json.c +++ b/lib/src/clixon_json.c @@ -208,7 +208,7 @@ array_eval(cxobj *xprev, array = LAST_ARRAY; else if (eqnext) array = FIRST_ARRAY; - else if (ys && ys->ys_keyword == Y_LIST) + else if (ys && yang_keyword_get(ys) == Y_LIST) array = SINGLE_ARRAY; else array = NO_ARRAY; @@ -338,7 +338,7 @@ xml2json1_cbuf(cbuf *cb, /* Find module name associated with namspace URI */ if (namespace && yspec && (ymod = yang_find_module_by_namespace(yspec, namespace)) != NULL){ - modname = ymod->ys_argument; + modname = yang_argument_get(ymod); } childt = child_type(x); if (pretty==2) @@ -427,8 +427,8 @@ xml2json1_cbuf(cbuf *cb, * This is code for writing 42 as "a":42 and not "a":"42" */ if (childt == BODY_CHILD && ys!=NULL && - (ys->ys_keyword == Y_LEAF || ys->ys_keyword == Y_LEAF_LIST)) - switch (cv_type_get(ys->ys_cv)){ + (yang_keyword_get(ys) == Y_LEAF || yang_keyword_get(ys) == Y_LEAF_LIST)) + switch (cv_type_get(yang_cv_get(ys))){ case CGV_INT8: case CGV_INT16: case CGV_INT32: diff --git a/lib/src/clixon_nacm.c b/lib/src/clixon_nacm.c index 6f6f014b..2c57da27 100644 --- a/lib/src/clixon_nacm.c +++ b/lib/src/clixon_nacm.c @@ -330,7 +330,7 @@ nacm_rule_datanode(cxobj *xt, if ((ys = xml_spec(xr)) == NULL) goto nomatch; ymod = ys_module(ys); - module = ymod->ys_argument; + module = yang_argument_get(ymod); if (strcmp(module, module_rule) != 0) goto nomatch; } diff --git a/lib/src/clixon_xml_changelog.c b/lib/src/clixon_xml_changelog.c index fb96f8b1..1a0ffb32 100644 --- a/lib/src/clixon_xml_changelog.c +++ b/lib/src/clixon_xml_changelog.c @@ -66,8 +66,8 @@ #include "clixon_xml.h" #include "clixon_options.h" #include "clixon_data.h" -#include "clixon_xml_map.h" #include "clixon_yang_module.h" +#include "clixon_xml_map.h" #include "clixon_xml_changelog.h" #include "clixon_xpath_ctx.h" #include "clixon_xpath.h" diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index d49257f3..98c649f2 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -80,7 +80,6 @@ #include "clixon_handle.h" #include "clixon_string.h" #include "clixon_yang.h" -#include "clixon_yang_type.h" #include "clixon_xml.h" #include "clixon_options.h" #include "clixon_plugin.h" @@ -90,6 +89,8 @@ #include "clixon_err.h" #include "clixon_netconf_lib.h" #include "clixon_xml_sort.h" +#include "clixon_yang_internal.h" /* internal */ +#include "clixon_yang_type.h" #include "clixon_xml_map.h" /*! x is element and has eactly one child which in turn has none */ @@ -186,7 +187,7 @@ xml2cli(FILE *f, goto ok; if ((ys = xml_spec(x)) == NULL) goto ok; - if (ys->ys_keyword == Y_LEAF || ys->ys_keyword == Y_LEAF_LIST){ + if (yang_keyword_get(ys) == Y_LEAF || yang_keyword_get(ys) == Y_LEAF_LIST){ if (prepend0) fprintf(f, "%s", prepend0); if (gt == GT_ALL || gt == GT_VARS) @@ -209,7 +210,7 @@ xml2cli(FILE *f, cprintf(cbpre, "%s", prepend0); cprintf(cbpre, "%s ", xml_name(x)); - if (ys->ys_keyword == Y_LIST){ + if (yang_keyword_get(ys) == Y_LIST){ /* If list then first loop through keys */ xe = NULL; while ((xe = xml_child_each(x, xe, -1)) != NULL){ @@ -225,7 +226,7 @@ xml2cli(FILE *f, /* Then loop through all other (non-keys) */ xe = NULL; while ((xe = xml_child_each(x, xe, -1)) != NULL){ - if (ys->ys_keyword == Y_LIST){ + if (yang_keyword_get(ys) == Y_LIST){ if ((match = yang_key_match(ys, xml_name(xe))) < 0) goto done; if (match){ @@ -269,11 +270,11 @@ validate_leafref(cxobj *xt, if ((leafrefbody = xml_body(xt)) == NULL) goto ok; if ((ypath = yang_find(ytype, Y_PATH, NULL)) == NULL){ - if (netconf_missing_element(cbret, "application", ytype->ys_argument, "Leafref requires path statement") < 0) + if (netconf_missing_element(cbret, "application", yang_argument_get(ytype), "Leafref requires path statement") < 0) goto done; goto fail; } - if (xpath_vec(xt, "%s", &xvec, &xlen, ypath->ys_argument) < 0) + if (xpath_vec(xt, "%s", &xvec, &xlen, yang_argument_get(ypath)) < 0) goto done; for (i = 0; i < xlen; i++) { x = xvec[i]; @@ -349,23 +350,23 @@ validate_identityref(cxobj *xt, } /* This is the type's base reference */ if ((ybaseref = yang_find(ytype, Y_BASE, NULL)) == NULL){ - if (netconf_missing_element(cbret, "application", ytype->ys_argument, "Identityref validation failed, no base") < 0) + if (netconf_missing_element(cbret, "application", yang_argument_get(ytype), "Identityref validation failed, no base") < 0) goto done; goto fail; } /* This is the actual base identity */ - if ((ybaseid = yang_find_identity(ybaseref, ybaseref->ys_argument)) == NULL){ - if (netconf_missing_element(cbret, "application", ybaseref->ys_argument, "Identityref validation failed, no base identity") < 0) + if ((ybaseid = yang_find_identity(ybaseref, yang_argument_get(ybaseref))) == NULL){ + if (netconf_missing_element(cbret, "application", yang_argument_get(ybaseref), "Identityref validation failed, no base identity") < 0) goto done; goto fail; } /* Here check if node is in the derived node list of the base identity * The derived node list is a cvec computed XXX */ - if (cvec_find(ybaseid->ys_cvec, node) == NULL){ + if (cvec_find(yang_cvec_get(ybaseid), node) == NULL){ cbuf_reset(cb); cprintf(cb, "Identityref validation failed, %s not derived from %s", - node, ybaseid->ys_argument); + node, yang_argument_get(ybaseid)); if (netconf_operation_failed(cbret, "application", cbuf_get(cb)) < 0) goto done; goto fail; @@ -398,11 +399,11 @@ xml_yang_root(cxobj *x, while ((xp = xml_parent(x)) != NULL){ if ((y = xml_spec(x)) != NULL && - (yp = (yang_stmt*)y->ys_parent) != NULL) + (yp = yang_parent_get(y)) != NULL) /* Actually, maybe only the Y_MODULE clause is relevant */ if (yp==NULL || - yp->ys_keyword == Y_MODULE || - yp->ys_keyword == Y_SUBMODULE) + yang_keyword_get(yp) == Y_MODULE || + yang_keyword_get(yp) == Y_SUBMODULE) break; /* x is the root */ x = xp; } diff --git a/lib/src/clixon_xml_sort.c b/lib/src/clixon_xml_sort.c index c5cc305b..5c0dd389 100644 --- a/lib/src/clixon_xml_sort.c +++ b/lib/src/clixon_xml_sort.c @@ -60,10 +60,10 @@ #include "clixon_hash.h" #include "clixon_handle.h" #include "clixon_yang.h" -#include "clixon_yang_type.h" #include "clixon_xml.h" #include "clixon_options.h" #include "clixon_xml_map.h" +#include "clixon_yang_type.h" #include "clixon_xml_sort.h" /*! Get xml body value as cligen variable @@ -96,10 +96,10 @@ xml_cv_cache(cxobj *x, goto ok; if (yang_type_get(y, NULL, &yrestype, &options, NULL, NULL, &fraction) < 0) goto done; - yang2cv_type(yrestype->ys_argument, &cvtype); + yang2cv_type(yang_argument_get(yrestype), &cvtype); if (cvtype==CGV_ERR){ clicon_err(OE_YANG, errno, "yang->cligen type %s mapping failed", - yrestype->ys_argument); + yang_argument_get(yrestype)); goto done; } if ((cv = cv_new(cvtype)) == NULL){ @@ -157,11 +157,10 @@ xml_child_spec(cxobj *x, char *name; name = xml_name(x); - if (xp && (yparent = xml_spec(xp)) != NULL){ /* First case: parent already has an associated yang statement, * then find matching child of that */ - if (yparent->ys_keyword == Y_RPC){ + if (yang_keyword_get(yparent) == Y_RPC){ if ((yi = yang_find(yparent, Y_INPUT, NULL)) != NULL) y = yang_find_datanode(yi, name); } @@ -179,7 +178,7 @@ xml_child_spec(cxobj *x, else y = NULL; /* kludge rpc -> input */ - if (y && y->ys_keyword == Y_RPC && yang_find(y, Y_INPUT, NULL)) + if (y && yang_keyword_get(y) == Y_RPC && yang_find(y, Y_INPUT, NULL)) y = yang_find(y, Y_INPUT, NULL); *yresult = y; retval = 0; @@ -258,7 +257,7 @@ xml_cmp(cxobj *x1, goto done; /* Ordered by user or state data : maintain existing order */ } e=4; - switch (y1->ys_keyword){ + switch (yang_keyword_get(y1)){ case Y_LEAF_LIST: /* Match with name and value */ if ((b1 = xml_body(x1)) == NULL) equal = -1; @@ -275,7 +274,7 @@ xml_cmp(cxobj *x1, case Y_LIST: /* Match with key values * Use Y_LIST cache (see struct yang_stmt) */ - cvk = y1->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */ + cvk = yang_cvec_get(y1); /* Use Y_LIST cache, see ys_populate_list() */ cvi = NULL; while ((cvi = cvec_each(cvk, cvi)) != NULL) { keyname = cv_string_get(cvi); /* operational data may have NULL keys*/ @@ -682,7 +681,7 @@ match_base_child(cxobj *x0, } goto ok; /* What to do if not found? */ } - switch (yc->ys_keyword){ + switch (yang_keyword_get(yc)){ case Y_CONTAINER: /* Equal regardless */ case Y_LEAF: /* Equal regardless */ break; @@ -702,7 +701,7 @@ match_base_child(cxobj *x0, goto done; break; case Y_LIST: /* Match with key values */ - cvk = yc->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */ + cvk = yang_cvec_get(yc); /* Use Y_LIST cache, see ys_populate_list() */ /* Count number of key indexes * Then create two vectors one with names and one with values of x1c, * ec: keyvec: [a,b,c] keyval: [1,2,3] @@ -741,7 +740,7 @@ match_base_child(cxobj *x0, } /* Get match. */ yorder = yang_order(yc); - x0c = xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval, keycvec); + x0c = xml_search(x0, xml_name(x1c), yorder, yang_keyword_get(yc), keynr, keyvec, keyval, keycvec); ok: *x0cp = x0c; retval = 0; diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 6056130b..b6706087 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -89,6 +89,7 @@ #include "clixon_options.h" #include "clixon_yang_parse.h" #include "clixon_yang_cardinality.h" +#include "clixon_yang_internal.h" /* internal */ #include "clixon_yang_type.h" /* Size of json read buffer when reading from file*/ diff --git a/lib/src/clixon_yang_cardinality.c b/lib/src/clixon_yang_cardinality.c index 5fbe7787..06fdf5bf 100644 --- a/lib/src/clixon_yang_cardinality.c +++ b/lib/src/clixon_yang_cardinality.c @@ -64,6 +64,7 @@ #include "clixon_handle.h" #include "clixon_err.h" #include "clixon_yang.h" +#include "clixon_yang_internal.h" /* internal */ #include "clixon_yang_cardinality.h" /* @@ -499,15 +500,14 @@ yang_cardinality(clicon_handle h, const struct ycard *ycplist; /* ycard parent table*/ const struct ycard *yc; - pk = yt->ys_keyword; + pk = yang_keyword_get(yt); /* 0) Find parent sub-parts of cardinality vector */ if ((ycplist = ycard_find(pk, 0, yclist, 0)) == NULL) goto ok; /* skip */ /* 1) For all children, if neither in 0..n, 0..1, 1 or 1..n ->ERROR */ - i = 0; - while (iys_len){ - ys = yt->ys_stmt[i++]; - ck = ys->ys_keyword; + ys = NULL; + while ((ys = yn_each(yt, ys)) != NULL) { + ck = yang_keyword_get(ys); if (ck == Y_UNKNOWN) /* special case */ continue; /* Find entry in yang cardinality table from parent/child keyword pair */ @@ -515,9 +515,9 @@ yang_cardinality(clicon_handle h, clicon_err(OE_YANG, 0, "%s: \"%s\"(%s) is child of \"%s\"(%s), but should not be", modname, yang_key2str(ck), - ys->ys_argument, + yang_argument_get(ys), yang_key2str(pk), - yt->ys_argument); + yang_argument_get(yt)); goto done; } } @@ -546,7 +546,7 @@ yang_cardinality(clicon_handle h, /* 4) Recurse */ i = 0; - while (iys_len){ /* Note, children may be removed */ + while (iys_len){ /* Note, children may be removed cant use yn_each */ ys = yt->ys_stmt[i++]; if (yang_cardinality(h, ys, modname) < 0) goto done; diff --git a/lib/src/clixon_yang_internal.h b/lib/src/clixon_yang_internal.h index 2e3e7fb5..e554e6ef 100644 --- a/lib/src/clixon_yang_internal.h +++ b/lib/src/clixon_yang_internal.h @@ -39,6 +39,16 @@ #ifndef _CLIXON_YANG_INTERNAL_H_ #define _CLIXON_YANG_INTERNAL_H_ + +/* + * Actually cligen variable stuff XXX + */ +#define V_UNIQUE 0x01 /* Variable flag */ +#define V_UNSET 0x08 /* Variable is unset, ie no default */ + + +#define YANG_FLAG_MARK 0x01 /* Marker for dynamic algorithms, eg expand */ + /*! Yang type cache. Yang type statements can cache all typedef info here * @note unions not cached */ @@ -88,4 +98,20 @@ struct yang_stmt{ int _ys_vector_i; /* internal use: yn_each */ }; +/* Yang data definition statement + * See RFC 7950 Sec 3: + * o data definition statement: A statement that defines new data + * nodes. One of "container", "leaf", "leaf-list", "list", "choice", + * "case", "augment", "uses", "anydata", and "anyxml". + */ +#define yang_datadefinition(y) (yang_datanode(y) || (y)->ys_keyword == Y_CHOICE || (y)->ys_keyword == Y_CASE || (y)->ys_keyword == Y_AUGMENT || (y)->ys_keyword == Y_USES) + +/* Yang schema node . + * See RFC 7950 Sec 3: + * o schema node: A node in the schema tree. One of action, container, + * leaf, leaf-list, list, choice, case, rpc, input, output, + * notification, anydata, and anyxml. + */ +#define yang_schemanode(y) (yang_datanode(y) || (y)->ys_keyword == Y_RPC || (y)->ys_keyword == Y_CHOICE || (y)->ys_keyword == Y_CASE || (y)->ys_keyword == Y_INPUT || (y)->ys_keyword == Y_OUTPUT || (y)->ys_keyword == Y_NOTIFICATION) + #endif /* _CLIXON_YANG_INTERNAL_H_ */ diff --git a/lib/src/clixon_yang_module.c b/lib/src/clixon_yang_module.c index 4b6c0937..3bfea1cb 100644 --- a/lib/src/clixon_yang_module.c +++ b/lib/src/clixon_yang_module.c @@ -76,6 +76,7 @@ #include "clixon_plugin.h" #include "clixon_netconf_lib.h" #include "clixon_yang_module.h" +#include "clixon_yang_internal.h" /* internal */ modstate_diff_t * modstate_diff_new(void) diff --git a/lib/src/clixon_yang_parse.y b/lib/src/clixon_yang_parse.y index 779b3195..b90b6ec5 100644 --- a/lib/src/clixon_yang_parse.y +++ b/lib/src/clixon_yang_parse.y @@ -190,6 +190,7 @@ #include "clixon_log.h" #include "clixon_yang.h" #include "clixon_yang_parse.h" +#include "clixon_yang_internal.h" /* internal */ extern int clixon_yang_parseget_lineno (void); diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c index 413d4e8e..34664487 100644 --- a/lib/src/clixon_yang_type.c +++ b/lib/src/clixon_yang_type.c @@ -67,6 +67,7 @@ #include "clixon_plugin.h" #include "clixon_options.h" #include "clixon_yang.h" +#include "clixon_yang_internal.h" /* internal */ #include "clixon_yang_type.h" /* From 2f997b169a6de6eee968e6cd795f308ce536f552 Mon Sep 17 00:00:00 2001 From: Vladimir Ratnikov Date: Thu, 25 Apr 2019 06:20:15 -0400 Subject: [PATCH 2/5] Add yang_stmt NULL check in yang_order to prevent SEGFAULT --- lib/src/clixon_yang.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 25722f11..4f58c9fd 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -850,6 +850,10 @@ yang_order(yang_stmt *y) * if so, the real parent (from an xml point of view) is the parents * parent. */ + if (y == NULL){ + return -1; + } + yp = y->ys_parent; while (yp->ys_keyword == Y_CASE || yp->ys_keyword == Y_CHOICE) yp = yp->ys_parent; From e4239496a872ec0ef0a38fb558df1de088f17ec3 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Thu, 25 Apr 2019 13:46:23 +0200 Subject: [PATCH 3/5] Yang access function. Mem leak in USE_XML_INSERT --- example/main/example_backend.c | 2 +- lib/clixon/clixon_yang.h | 12 ++---------- lib/src/clixon_datastore_write.c | 9 ++++++++- lib/src/clixon_xml.c | 4 ++-- lib/src/clixon_yang.c | 20 ++++++++++++++++++++ test/test_perf.sh | 2 +- test/test_startup.sh | 19 +++++++++++-------- 7 files changed, 45 insertions(+), 23 deletions(-) diff --git a/example/main/example_backend.c b/example/main/example_backend.c index b2fbc532..e4df5dd8 100644 --- a/example/main/example_backend.c +++ b/example/main/example_backend.c @@ -203,7 +203,7 @@ example_copy_extra(clicon_handle h, /* Clicon handle */ { int retval = -1; - fprintf(stderr, "%s\n", __FUNCTION__); + // fprintf(stderr, "%s\n", __FUNCTION__); retval = 0; // done: return retval; diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h index 944e9a8f..8f5c775a 100644 --- a/lib/clixon/clixon_yang.h +++ b/lib/clixon/clixon_yang.h @@ -138,18 +138,9 @@ typedef enum yang_class yang_class; struct xml; -/* Yang data node - * See RFC7950 Sec 3: - * o data node: A node in the schema tree that can be instantiated in a - * data tree. One of container, leaf, leaf-list, list, anydata, and - * anyxml. - * XXX move to function - */ -#define yang_datanode(y) ((y)->ys_keyword == Y_CONTAINER || (y)->ys_keyword == Y_LEAF || (y)->ys_keyword == Y_LIST || (y)->ys_keyword == Y_LEAF_LIST || (y)->ys_keyword == Y_ANYXML || (y)->ys_keyword == Y_ANYDATA) - - typedef struct yang_stmt yang_stmt; /* Defined in clixon_yang_internal */ + typedef int (yang_applyfn_t)(yang_stmt *ys, void *arg); /* @@ -197,6 +188,7 @@ int ys_populate(yang_stmt *ys, void *arg); yang_stmt *yang_parse_file(int fd, const char *name, yang_stmt *ysp); int yang_apply(yang_stmt *yn, enum rfc_6020 key, yang_applyfn_t fn, void *arg); +int yang_datanode(yang_stmt *ys); int yang_abs_schema_nodeid(yang_stmt *yspec, yang_stmt *ys, char *schema_nodeid, enum rfc_6020 keyword, yang_stmt **yres); diff --git a/lib/src/clixon_datastore_write.c b/lib/src/clixon_datastore_write.c index 63d2958f..d8d3df12 100644 --- a/lib/src/clixon_datastore_write.c +++ b/lib/src/clixon_datastore_write.c @@ -297,7 +297,9 @@ text_modify(clicon_handle h, } #ifdef USE_XML_INSERT /* Add new xml node but without parent - insert when node fully - copied (see changed conditional below) */ + * copied (see changed conditional below) + * Note x0 may dangle cases if exit before changed conditional + */ if ((x0 = xml_new(x1name, NULL, (yang_stmt*)y0)) == NULL) goto done; #else @@ -400,6 +402,11 @@ text_modify(clicon_handle h, #endif retval = 1; done: +#ifdef USE_XML_INSERT + /* Remove dangling added objects */ + if (changed && x0 && xml_parent(x0)==NULL) + xml_purge(x0); +#endif if (x0vec) free(x0vec); return retval; diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index 1735df14..b65aef9a 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -934,9 +934,9 @@ xml_wrap(cxobj *xc, * @retval 0 OK * @retval -1 * @note you cannot remove xchild in the loop (unless yoy keep track of xprev) - * + * @note Linear complexity - use xml_child_rm if possible * @see xml_free Free, dont remove from parent - * @see xml_child_rm Only remove dont free + * @see xml_child_rm Remove if child order is known (does not free) * Differs from xml_free it is removed from parent. */ int diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 25722f11..0d7be1b6 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -2867,6 +2867,26 @@ yang_apply(yang_stmt *yn, return retval; } +/*! Check if a node is a yang "data node" + * @param[in] ys Yang statement node + * @retval 0 Yang node is NOT a data node + * @retval !=0 Yang node IS a data noed + * @see RFC7950 Sec 3: + * o data node: A node in the schema tree that can be instantiated in a + * data tree. One of container, leaf, leaf-list, list, anydata, and + * anyxml. + */ +int +yang_datanode(yang_stmt *ys) +{ + return (yang_keyword_get(ys) == Y_CONTAINER || + yang_keyword_get(ys) == Y_LEAF || + yang_keyword_get(ys) == Y_LIST || + yang_keyword_get(ys) == Y_LEAF_LIST || + yang_keyword_get(ys) == Y_ANYXML || + yang_keyword_get(ys) == Y_ANYDATA); +} + /*! All the work for schema_nodeid functions both absolute and descendant * Ignore prefixes, see _abs * @param[in] yn Yang node. Find next yang stmt and return that if match. diff --git a/test/test_perf.sh b/test/test_perf.sh index c3df0aa3..3990a939 100755 --- a/test/test_perf.sh +++ b/test/test_perf.sh @@ -5,7 +5,7 @@ s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi # Number of list/leaf-list entries in file -: ${perfnr:=20000} +: ${perfnr:=10000} # Number of requests made get/put : ${perfreq:=100} diff --git a/test/test_startup.sh b/test/test_startup.sh index ac4be92f..8ec21156 100755 --- a/test/test_startup.sh +++ b/test/test_startup.sh @@ -169,16 +169,19 @@ testrun startup "$runvar" "$startvar" "$extravar" ' Date: Thu, 25 Apr 2019 13:50:02 +0200 Subject: [PATCH 4/5] yang access unctions --- lib/src/clixon_yang.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 554b4547..7a816bdd 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -845,17 +845,15 @@ yang_order(yang_stmt *y) int i; int j=0; int tot = 0; - + + if (y == NULL) + return -1; /* Some special handling if yp is choice (or case) and maybe union? * if so, the real parent (from an xml point of view) is the parents * parent. */ - if (y == NULL){ - return -1; - } - - yp = y->ys_parent; - while (yp->ys_keyword == Y_CASE || yp->ys_keyword == Y_CHOICE) + yp = yang_parent_get(y); + while (yang_keyword_get(yp) == Y_CASE || yang_keyword_get(yp) == Y_CHOICE) yp = yp->ys_parent; /* XML nodes with yang specs that are children of modules are special - @@ -864,8 +862,8 @@ yang_order(yang_stmt *y) * Example: * The order of x and y cannot be compared within a single yang module since they belong to different */ - if (yp->ys_keyword == Y_MODULE || yp->ys_keyword == Y_SUBMODULE){ - ypp = yp->ys_parent; /* yang spec */ + if (yang_keyword_get(yp) == Y_MODULE || yang_keyword_get(yp) == Y_SUBMODULE){ + ypp = yang_parent_get(yp); /* yang spec */ for (i=0; iys_len; i++){ /* iterate through other modules */ ym = ypp->ys_stmt[i]; if (yp == ym) From 6bf2a74e2453621485143c05d846823a00d0e961 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 26 Apr 2019 12:12:55 +0200 Subject: [PATCH 5/5] * Restconf with startup feature will now copy all edit changes to startup db (as it should according to RFC 8040) * See [Restconf does not handle startup datastore according to the RFC](https://github.com/clicon/clixon/issues/74) * Netconf Startup feature is no longer hardcoded, you need to explicitly enable it (See RFC 6241, Section 8.7) * Enable in config file with: `ietf-netconf:startup`, or use `*:*` --- CHANGELOG.md | 6 +- README.md | 4 +- apps/backend/backend_main.c | 7 ++ apps/cli/cli_main.c | 4 + apps/restconf/restconf_main.c | 10 +++ apps/restconf/restconf_methods.c | 55 ++++++++++++++ doc/FAQ.md | 11 +++ example/main/example.xml | 2 +- lib/clixon/clixon_data.h | 12 --- lib/clixon/clixon_yang.h | 1 + lib/src/clixon_data.c | 98 ------------------------ lib/src/clixon_netconf_lib.c | 6 -- lib/src/clixon_yang.c | 36 ++++++++- test/test_copy_config.sh | 1 + test/test_feature.sh | 2 +- test/test_nacm_default.sh | 1 + test/test_nacm_protocol.sh | 1 + test/test_netconf.sh | 1 + test/test_restconf.sh | 4 +- test/test_restconf_startup.sh | 126 +++++++++++++++++++++++++++++++ test/test_rpc.sh | 2 +- test/test_startup.sh | 4 +- test/test_upgrade.sh | 1 + test/test_upgrade_auto.sh | 1 + test/test_upgrade_interfaces.sh | 1 + test/test_upgrade_repair.sh | 1 + 26 files changed, 270 insertions(+), 128 deletions(-) create mode 100755 test/test_restconf_startup.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aaa51a1..4c95cb23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,9 @@ ### API changes on existing features (you may need to change your code) +* Restconf with startup feature will now copy all edit changes to startup db (as it should according to RFC 8040) +* Netconf Startup feature is no longer hardcoded, you need to explicitly enable it (See RFC 6241, Section 8.7) + * Enable in config file with: `ietf-netconf:startup`, or use `*:*` * The directory `docker/system` has been moved to `docker/main`, to reflect that it runs the main example. * xmldb_get() removed "config" parameter: * Change all calls to dbget from: `xmldb_get(h, db, xpath, 0|1, &xret, msd)` to `xmldb_get(h, db, xpath, &xret, msd)` @@ -111,7 +114,7 @@ ### Minor changes -* A new "hello world" example is added +* A new minimal "hello world" example has been added * Experimental customized error output strings, see [lib/clixon/clixon_err_string.h] * Empty leaf values, eg are now checked at validation. * Empty values were skipped in validation. @@ -138,6 +141,7 @@ * Added libgen.h for baseline() ### Corrected Bugs +* [Restconf does not handle startup datastore according to the RFC](https://github.com/clicon/clixon/issues/74) * Failure in startup with -m startup or running left running_db cleared. * Running-db should not be changed on failure. Unless failure-db defined. Or if SEGV, etc. In those cases, tmp_db should include the original running-db. * Backend plugin returning NULL was still installed - is now logged and skipped. diff --git a/README.md b/README.md index b26a32d2..5d195282 100644 --- a/README.md +++ b/README.md @@ -162,10 +162,12 @@ Clixon implements the following NETCONF proposals or standards: The following RFC6241 capabilities/features are hardcoded in Clixon: - :candidate (RFC6241 8.3) - :validate (RFC6241 8.6) -- :startup (RFC6241 8.7) - :xpath (RFC6241 8.9) - :notification: (RFC5277) +The following features are optional and can be enabled by setting CLICON_FEATURE: +- :startup (RFC6241 8.7) + Clixon does not support the following netconf features: - :url capability diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index 658447e8..e132435e 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -616,6 +616,13 @@ main(int argc, clicon_log(LOG_ERR, "Startup mode undefined. Specify option CLICON_STARTUP_MODE or specify -s option to clicon_backend."); goto done; } + /* Check that netconf :startup is enabled */ + if (startup_mode == SM_STARTUP && + !if_feature(yspec, "ietf-netconf", "startup")){ + clicon_log(LOG_ERR, "Startup mode selected but Netconf :startup feature is not enabled. Enable with option: ietf-netconf:startup"); + goto done; + } + /* Init running db if it is not there */ if (xmldb_exists(h, "running") != 1) diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index 7b4c5205..cc0ab648 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -485,6 +485,10 @@ main(int argc, char **argv) if (yang_modules_init(h) < 0) goto done; + /* Add netconf yang spec, used as internal protocol */ + if (netconf_module_load(h) < 0) + goto done; + /* Create tree generated from dataspec. If no other trees exists, this is * the only one. * The following code creates the tree @datamodel diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index 6d8e8082..92822e80 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -668,6 +668,11 @@ main(int argc, /* Load yang module library, RFC7895 */ if (yang_modules_init(h) < 0) goto done; + + /* Add netconf yang spec, used as internal protocol */ + if (netconf_module_load(h) < 0) + goto done; + /* Add system modules */ if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") && yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec)< 0) @@ -675,6 +680,11 @@ main(int argc, if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") && yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0) goto done; + + /* Dump configuration options on debug */ + if (debug) + clicon_option_dump(h, debug); + /* Call start function in all plugins before we go interactive */ if (clixon_plugin_start(h) < 0) diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index c9277e91..d9df89cd 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -588,6 +588,25 @@ api_data_post(clicon_handle h, goto done; goto ok; } + if (if_feature(yspec, "ietf-netconf", "startup")){ + /* RFC8040 Sec 1.4: + * If the NETCONF server supports :startup, the RESTCONF server MUST + * automatically update the non-volatile startup configuration + * datastore, after the "running" datastore has been altered as a + * consequence of a RESTCONF edit operation. + */ + cbuf_reset(cbx); + cprintf(cbx, "", NACM_RECOVERY_USER); + cprintf(cbx, ""); + if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0) + goto done; + /* If copy-config failed, log and ignore (already committed) */ + if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){ + + clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__); + } + } + FCGX_SetExitStatus(201, r->out); /* Created */ FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n"); FCGX_FPrintF(r->out, "\r\n"); @@ -914,6 +933,24 @@ api_data_put(clicon_handle h, goto done; goto ok; } + if (if_feature(yspec, "ietf-netconf", "startup")){ + /* RFC8040 Sec 1.4: + * If the NETCONF server supports :startup, the RESTCONF server MUST + * automatically update the non-volatile startup configuration + * datastore, after the "running" datastore has been altered as a + * consequence of a RESTCONF edit operation. + */ + cbuf_reset(cbx); + cprintf(cbx, "", NACM_RECOVERY_USER); + cprintf(cbx, ""); + if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0) + goto done; + /* If copy-config failed, log and ignore (already committed) */ + if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){ + + clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__); + } + } FCGX_SetExitStatus(201, r->out); /* Created */ FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n"); FCGX_FPrintF(r->out, "\r\n"); @@ -1071,6 +1108,24 @@ api_data_delete(clicon_handle h, goto done; goto ok; } + if (if_feature(yspec, "ietf-netconf", "startup")){ + /* RFC8040 Sec 1.4: + * If the NETCONF server supports :startup, the RESTCONF server MUST + * automatically update the non-volatile startup configuration + * datastore, after the "running" datastore has been altered as a + * consequence of a RESTCONF edit operation. + */ + cbuf_reset(cbx); + cprintf(cbx, "", NACM_RECOVERY_USER); + cprintf(cbx, ""); + if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0) + goto done; + /* If copy-config failed, log and ignore (already committed) */ + if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){ + + clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__); + } + } FCGX_SetExitStatus(201, r->out); FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n"); FCGX_FPrintF(r->out, "\r\n"); diff --git a/doc/FAQ.md b/doc/FAQ.md index a5c81437..24aaf048 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -298,6 +298,17 @@ The example below shows enabling a specific feature; enabling all features in mo Features can be probed by using RFC 7895 Yang module library which provides information on all modules and which features are enabled. +Clixon have three hardcoded features: +- :candidate (RFC6241 8.3) +- :validate (RFC6241 8.6) +- :xpath (RFC6241 8.9) + +You can select the startup feature by including it in the config file: +``` + ietf-netconf:startup +``` +(or just `ietf-netconf:*`). + ## Can I run Clixon as a container? Yes, Clixon has two examples on how to build docker containers. A [base](../docker/base) image and a complete [example system](../docker/system). diff --git a/example/main/example.xml b/example/main/example.xml index a7c13214..85c751bf 100644 --- a/example/main/example.xml +++ b/example/main/example.xml @@ -1,6 +1,6 @@ /usr/local/etc/example.xml - *:* + ietf-netconf:startup /usr/local/share/clixon clixon-example example diff --git a/lib/clixon/clixon_data.h b/lib/clixon/clixon_data.h index 6f4872b6..54537008 100644 --- a/lib/clixon/clixon_data.h +++ b/lib/clixon/clixon_data.h @@ -70,18 +70,6 @@ int clicon_config_yang_set(clicon_handle h, yang_stmt *ys); cxobj *clicon_conf_xml(clicon_handle h); int clicon_conf_xml_set(clicon_handle h, cxobj *x); -#ifdef XXX -plghndl_t clicon_xmldb_plugin_get(clicon_handle h); -int clicon_xmldb_plugin_set(clicon_handle h, plghndl_t handle); - -void *clicon_xmldb_api_get(clicon_handle h); -int clicon_xmldb_api_set(clicon_handle h, void *xa_api); - - -void *clicon_xmldb_handle_get(clicon_handle h); -int clicon_xmldb_handle_set(clicon_handle h, void *xh); -#endif - db_elmnt *clicon_db_elmnt_get(clicon_handle h, const char *db); int clicon_db_elmnt_set(clicon_handle h, const char *db, db_elmnt *xc); diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h index 8f5c775a..76e49980 100644 --- a/lib/clixon/clixon_yang.h +++ b/lib/clixon/clixon_yang.h @@ -184,6 +184,7 @@ yang_stmt *yang_choice(yang_stmt *y); int yang_order(yang_stmt *y); int yang_print(FILE *f, yang_stmt *yn); int yang_print_cbuf(cbuf *cb, yang_stmt *yn, int marginal); +int if_feature(yang_stmt *yspec, char *module, char *feature); int ys_populate(yang_stmt *ys, void *arg); yang_stmt *yang_parse_file(int fd, const char *name, yang_stmt *ysp); int yang_apply(yang_stmt *yn, enum rfc_6020 key, yang_applyfn_t fn, diff --git a/lib/src/clixon_data.c b/lib/src/clixon_data.c index 98521bff..56871268 100644 --- a/lib/src/clixon_data.c +++ b/lib/src/clixon_data.c @@ -214,104 +214,6 @@ clicon_conf_xml_set(clicon_handle h, return 0; } -#ifdef XXX -/*! Get xmldb datastore plugin handle, as used by dlopen/dlsym/dlclose */ -plghndl_t -clicon_xmldb_plugin_get(clicon_handle h) -{ - clicon_hash_t *cdat = clicon_data(h); - size_t len; - void *p; - - if ((p = hash_value(cdat, "xmldb_plugin", &len)) != NULL) - return *(plghndl_t*)p; - return NULL; -} - -/*! Set xmldb datastore plugin handle, as used by dlopen/dlsym/dlclose */ -int -clicon_xmldb_plugin_set(clicon_handle h, - plghndl_t handle) -{ - clicon_hash_t *cdat = clicon_data(h); - - if (hash_add(cdat, "xmldb_plugin", &handle, sizeof(void*)) == NULL) - return -1; - return 0; -} - - -/*! Get XMLDB API struct pointer - * @param[in] h Clicon handle - * @retval xa XMLDB API struct - * @note xa is really of type struct xmldb_api* - */ -void * -clicon_xmldb_api_get(clicon_handle h) -{ - clicon_hash_t *cdat = clicon_data(h); - size_t len; - void *xa; - - if ((xa = hash_value(cdat, "xmldb_api", &len)) != NULL) - return *(void**)xa; - return NULL; -} - -/*! Set or reset XMLDB API struct pointer - * @param[in] h Clicon handle - * @param[in] xa XMLDB API struct - * @note xa is really of type struct xmldb_api* - */ -int -clicon_xmldb_api_set(clicon_handle h, - void *xa) -{ - clicon_hash_t *cdat = clicon_data(h); - - /* It is the pointer to xa_api that should be copied by hash, - so we send a ptr to the ptr to indicate what to copy. - */ - if (hash_add(cdat, "xmldb_api", &xa, sizeof(void*)) == NULL) - return -1; - return 0; -} - - -/*! Get XMLDB storage handle - * @param[in] h Clicon handle - * @retval xh XMLDB storage handle. If not connected return NULL - */ -void * -clicon_xmldb_handle_get(clicon_handle h) -{ - clicon_hash_t *cdat = clicon_data(h); - size_t len; - void *xh; - - if ((xh = hash_value(cdat, "xmldb_handle", &len)) != NULL) - return *(void**)xh; - return NULL; -} - -/*! Set or reset XMLDB storage handle - * @param[in] h Clicon handle - * @param[in] xh XMLDB storage handle. If NULL reset it - * @note Just keep note of it, dont allocate it or so. - */ -int -clicon_xmldb_handle_set(clicon_handle h, - void *xh) -{ - clicon_hash_t *cdat = clicon_data(h); - - if (hash_add(cdat, "xmldb_handle", &xh, sizeof(void*)) == NULL) - return -1; - return 0; -} -#endif /* XXX */ - - /*! Get authorized user name * @param[in] h Clicon handle * @retval xh XMLDB storage handle. If not connected return NULL diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c index 87763b85..45956aa9 100644 --- a/lib/src/clixon_netconf_lib.c +++ b/lib/src/clixon_netconf_lib.c @@ -1030,14 +1030,8 @@ netconf_module_load(clicon_handle h) goto done; if (xml_parse_string("ietf-netconf:validate", yspec, &xc) < 0) goto done; - if (xml_parse_string("ietf-netconf:startup", yspec, &xc) < 0) - goto done; if (xml_parse_string("ietf-netconf:xpath", yspec, &xc) < 0) goto done; -#ifdef NYI - if (xml_parse_string("ietf-netconf:confirmed-commit", yspec, &xc) < 0) - goto done; -#endif /* Load yang spec */ if (yang_spec_parse_module(h, "ietf-netconf", NULL, yspec)< 0) goto done; diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 7a816bdd..525fc51b 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -1591,6 +1591,33 @@ ys_populate_identity(yang_stmt *ys, return retval; } +/*! Return 1 if feature is enabled, 0 if not using the populated yang tree + * + * @param[in] yspec yang specification + * @param[in] module Name of module + * @param[in] feature Name of feature + * @retval 0 Not found or not set + * @retval 1 Found and set + * XXX: should the in-param be h, ymod, or yspec? + */ +int +if_feature(yang_stmt *yspec, + char *module, + char *feature) +{ + yang_stmt *ym; /* module */ + yang_stmt *yf; /* feature */ + cg_var *cv; + + if ((ym = yang_find_module_by_name(yspec, module)) == NULL) + return 0; + if ((yf = yang_find(ym, Y_FEATURE, feature)) == NULL) + return 0; + if ((cv = yang_cv_get(yf)) == NULL) + return 0; + return cv_bool_get(cv); +} + /*! Populate yang feature statement - set cv to 1 if enabled * * @param[in] ys Feature yang statement to populate. @@ -1608,6 +1635,8 @@ ys_populate_feature(clicon_handle h, char *module; char *feature; cxobj *xc; + char *m; + char *f; /* get clicon config file in xml form */ if ((x = clicon_conf_xml(h)) == NULL) @@ -1620,11 +1649,12 @@ ys_populate_feature(clicon_handle h, feature = ys->ys_argument; xc = NULL; while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL && found == 0) { - char *m = NULL; - char *f = NULL; + m = NULL; + f = NULL; if (strcmp(xml_name(xc), "CLICON_FEATURE") != 0) continue; - /* get m and f from configuration feature rules */ + /* CLICON_FEATURE is on the form :. + * Split on colon to get module(m) and feature(f) respectively */ if (nodeid_split(xml_body(xc), &m, &f) < 0) goto done; if (m && f && diff --git a/test/test_copy_config.sh b/test/test_copy_config.sh index ee1d60d1..75e3eea9 100755 --- a/test/test_copy_config.sh +++ b/test/test_copy_config.sh @@ -32,6 +32,7 @@ cfg=$dir/conf_yang.xml cat < $cfg $cfg + ietf-netconf:startup 42 /usr/local/share/clixon $IETFRFC diff --git a/test/test_feature.sh b/test/test_feature.sh index 463b0555..d441b3e7 100755 --- a/test/test_feature.sh +++ b/test/test_feature.sh @@ -158,7 +158,7 @@ fi # Note order of features in ietf-netconf yang is alphabetically: candidate, startup, validate, xpath new "netconf module ietf-netconf" -expect="ietf-netconf2011-06-01urn:ietf:params:xml:ns:netconf:base:1.0candidatevalidatestartupxpathimplement" +expect="ietf-netconf2011-06-01urn:ietf:params:xml:ns:netconf:base:1.0candidatevalidatexpathimplement" match=`echo "$ret" | grep -GZo "$expect"` if [ -z "$match" ]; then err "$expect" "$ret" diff --git a/test/test_nacm_default.sh b/test/test_nacm_default.sh index b0dd3e60..cc457c60 100755 --- a/test/test_nacm_default.sh +++ b/test/test_nacm_default.sh @@ -13,6 +13,7 @@ fyang=$dir/nacm-example.yang cat < $cfg $cfg + ietf-netconf:startup /usr/local/share/clixon $IETFRFC $fyang diff --git a/test/test_nacm_protocol.sh b/test/test_nacm_protocol.sh index a6af2124..3e6089ce 100755 --- a/test/test_nacm_protocol.sh +++ b/test/test_nacm_protocol.sh @@ -37,6 +37,7 @@ fyang=$dir/nacm-example.yang cat < $cfg $cfg + ietf-netconf:startup /usr/local/share/clixon $IETFRFC $fyang diff --git a/test/test_netconf.sh b/test/test_netconf.sh index 9738345d..3b369875 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -14,6 +14,7 @@ tmp=$dir/tmp.x cat < $cfg $cfg + ietf-netconf:startup 42 /usr/local/share/clixon $IETFRFC diff --git a/test/test_restconf.sh b/test/test_restconf.sh index 1d71e78b..be4f73b0 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -73,12 +73,12 @@ expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/r # Should be alphabetically ordered new "restconf get restconf/operations. RFC8040 3.3.2 (json)" -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}} +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,"ietf-netconf:get-config": null,"ietf-netconf:edit-config": null,"ietf-netconf:copy-config": null,"ietf-netconf:delete-config": null,"ietf-netconf:lock": null,"ietf-netconf:unlock": null,"ietf-netconf:get": null,"ietf-netconf:close-session": null,"ietf-netconf:kill-session": null,"ietf-netconf:commit": null,"ietf-netconf:discard-changes": null,"ietf-netconf:validate": null,"clixon-rfc5277:create-subscription": 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" diff --git a/test/test_restconf_startup.sh b/test/test_restconf_startup.sh new file mode 100755 index 00000000..36037af8 --- /dev/null +++ b/test/test_restconf_startup.sh @@ -0,0 +1,126 @@ +#!/bin/bash +# Test restconf :startup +# RFC 8040 Sec 1.4 says: +# the NETCONF server supports :startup, the RESTCONF server MUST +# automatically update the non-volatile startup configuration +# datastore, after the "running" datastore has been altered as a +# consequence of a RESTCONF edit operation. +# Magic line must be first in script (see README.md) +s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi + +APPNAME=example + +cfg=$dir/conf.xml +fyang=$dir/example.yang + +cat < $fyang +module example{ + yang-version 1.1; + namespace "urn:example:clixon"; + prefix ip; + container x { + list y { + key "a"; + leaf a { + type string; + } + leaf b { + type string; + } + } + } +} +EOF + +# Use yang in example + +cat < $cfg + + $cfg + /usr/local/share/clixon + /usr/local/lib/$APPNAME/backend + example_backend.so$ + /usr/local/lib/$APPNAME/restconf + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + $dir + +EOF + +testrun(){ + option=$1 + + new "test params: -f $cfg -y $fyang $option" + if [ $BE -ne 0 ]; then + new "kill old backend" + sudo clixon_backend -zf $cfg + if [ $? -ne 0 ]; then + err + fi + new "start backend -s init -f $cfg -y $fyang $option" + start_backend -s init -f $cfg -y $fyang $option + fi + + new "kill old restconf daemon" + sudo pkill -u www-data clixon_restconf + + new "start restconf daemon" + start_restconf -f $cfg -y $fyang $option + + new "waiting" + sleep $RCWAIT + + new "restconf put 42" + expecteq "$(curl -s -X PUT http://localhost/restconf/data/example:x/y=42 -d '{"example:y":{"a":"42","b":"42"}}')" 0 "" + + new "restconf put 99" + expecteq "$(curl -s -X PUT http://localhost/restconf/data/example:x/y=99 -d '{"example:y":{"a":"99","b":"99"}}')" 0 "" + + new "restconf post 123" + expecteq "$(curl -s -X POST http://localhost/restconf/data/example:x -d '{"example:y":{"a":"123","b":"123"}}')" 0 "" + + new "restconf delete 42" + expecteq "$(curl -s -X DELETE http://localhost/restconf/data/example:x/y=42)" 0 "" + + new "Kill restconf daemon" + stop_restconf + + if [ $BE -ne 0 ]; then + new "Kill backend" + # Check if premature kill + pid=`pgrep -u root -f clixon_backend` + if [ -z "$pid" ]; then + err "backend already dead" + fi + # kill backend + stop_backend -f $cfg + fi +} + +# clear startup +sudo rm -f $dir/startup_db; + +new "Run with startup option, check running is copied" +testrun "-o CLICON_FEATURE=ietf-netconf:startup" + +new "Check running and startup exists and are same" +if [ ! -f $dir/startup_db ]; then + err "startup should exist but does not" +fi +echo "diff $dir/startup_db $dir/running_db" +d=$(sudo diff $dir/startup_db $dir/running_db) +if [ -n "$d" ]; then + err "running and startup should be equal" "$d" +fi + +# clear startup +sudo rm -f $dir/startup_db; + +new "Run without startup option, check running is copied" +testrun "" + +new "Check startup should not exist" +if [ -f $dir/startup_db ]; then + err "startup should not exist" +fi +rm -rf $dir diff --git a/test/test_rpc.sh b/test/test_rpc.sh index 3c2a44ee..d78ec54e 100755 --- a/test/test_rpc.sh +++ b/test/test_rpc.sh @@ -127,7 +127,7 @@ new "restconf wrong method" 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)" 0 '{"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": "application","error-tag": "missing-element","error-info": {"bad-element": "target"},"error-severity": "error","error-message": "Mandatory variable"}}} ' new "netconf kill-session missing session-id mandatory" expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' '^applicationmissing-elementsession-iderrorMandatory variable]]>]]>$' diff --git a/test/test_startup.sh b/test/test_startup.sh index 8ec21156..cd36277c 100755 --- a/test/test_startup.sh +++ b/test/test_startup.sh @@ -8,7 +8,7 @@ # - startup db starts with a "start" interface # There is also an "invalid" XML and a "broken" XML # There are two steps, first run through everything OK -# Then try with invalid and borken XML and ensure the backend quits and all is untouched +# Then try with invalid and broken XML and ensure the backend quits and all is untouched # Magic line must be first in script (see README.md) s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi @@ -20,6 +20,7 @@ cfg=$dir/conf_startup.xml cat < $cfg $cfg + ietf-netconf:startup /usr/local/share/clixon $IETFRFC clixon-example @@ -65,7 +66,6 @@ testrun(){ edb=$4 # extradb at start exprun=$5 # expected running_db after startup - sudo rm -f $dir/*_db echo "$rdb" > $dir/running_db echo "$sdb" > $dir/startup_db diff --git a/test/test_upgrade.sh b/test/test_upgrade.sh index d33dbaf1..03b59f7b 100755 --- a/test/test_upgrade.sh +++ b/test/test_upgrade.sh @@ -94,6 +94,7 @@ EOF cat < $cfg $cfg + ietf-netconf:startup /usr/local/share/clixon $dir /usr/local/var/$APPNAME/$APPNAME.sock diff --git a/test/test_upgrade_auto.sh b/test/test_upgrade_auto.sh index 9827848f..7991864c 100755 --- a/test/test_upgrade_auto.sh +++ b/test/test_upgrade_auto.sh @@ -173,6 +173,7 @@ XML='dont change merename me $cfg $cfg + ietf-netconf:startup /usr/local/share/clixon $dir /usr/local/var/$APPNAME/$APPNAME.sock diff --git a/test/test_upgrade_interfaces.sh b/test/test_upgrade_interfaces.sh index 2ec3b230..c72e7b2c 100755 --- a/test/test_upgrade_interfaces.sh +++ b/test/test_upgrade_interfaces.sh @@ -232,6 +232,7 @@ EOF cat < $cfg $cfg + ietf-netconf:startup /usr/local/share/clixon interfaces:if-mib $dir diff --git a/test/test_upgrade_repair.sh b/test/test_upgrade_repair.sh index c6cc4dda..e752d1d5 100755 --- a/test/test_upgrade_repair.sh +++ b/test/test_upgrade_repair.sh @@ -57,6 +57,7 @@ EOF cat < $cfg $cfg + ietf-netconf:startup /usr/local/share/clixon $dir /usr/local/var/$APPNAME/$APPNAME.sock