diff --git a/CHANGELOG.md b/CHANGELOG.md index 202c250f..3b99fea8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,11 +21,11 @@ * A user can register upgrade callbacks per module/revision when in-compatible XML is encountered (`update_callback_register`). * A "failsafe" mode allowing a user to repair the startup on errors or failed validation. * Major rewrite of `backend_main.c` and a new module `backend_startup.c` -* New yang changelog experimental feature for automatic upgrade - * Yang module clixon-yang-changelog@2019-03-21.yang based on draft-wang-netmod-module-revision-management-01 +* New xml changelog experimental feature for automatic upgrade + * Yang module clixon-xml-changelog@2019-03-21.yang based on draft-wang-netmod-module-revision-management-01 * Two config options control: - * CLICON_YANG_CHANGELOG enables the yang changelog feature - * CLICON_YANG_CHANGELOG_FILE where the changelog resides + * CLICON_XML_CHANGELOG enables the yang changelog feature + * CLICON_XML_CHANGELOG_FILE where the changelog resides * Datastore files contain RFC7895 module-state information * Added modules-state diff parameter to xmldb_get datastore function * Set config option `CLICON_XMLDB_MODSTATE` to true diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index 581b3153..24c80079 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -251,11 +251,13 @@ validate_common(clicon_handle h, /*! Validate a candidate db and comnpare to running XXX Experimental * Get both source and dest datastore, validate target, compute diffs * and call application callback validations. - * @param[in] h Clicon handle - * @param[in] candidate The candidate database. The wanted backend state - * @retval -1 Error - or validation failed (but cbret not set) - * @retval 0 Validation failed (with cbret set) - * @retval 1 Validation OK + * @param[in] h Clicon handle + * @param[in] db The startup database. The wanted backend state + * @param[out] xtr Transformed XML + * @param[out] cbret CLIgen buffer w error stmt if retval = 0 + * @retval -1 Error - or validation failed (but cbret not set) + * @retval 0 Validation failed (with cbret set) + * @retval 1 Validation OK * @note Need to differentiate between error and validation fail * 1. Parse startup XML (or JSON) * 2. If syntax failure, call startup-cb(ERROR), copy failsafe db to @@ -268,12 +270,14 @@ validate_common(clicon_handle h, int startup_validate(clicon_handle h, char *db, + cxobj **xtr, cbuf *cbret) { int retval = -1; yang_spec *yspec; int ret; modstate_diff_t *msd = NULL; + cxobj *xt = NULL; transaction_data_t *td = NULL; /* Handcraft a transition with only target and add trees */ @@ -287,14 +291,23 @@ startup_validate(clicon_handle h, if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE")) if ((msd = modstate_diff_new()) == NULL) goto done; - if (xmldb_get(h, db, "/", 1, &td->td_target, msd) < 0) + if (xmldb_get(h, db, "/", 1, &xt, msd) < 0) goto done; - if ((ret = clixon_module_upgrade(h, td->td_target, msd, cbret)) < 0) + if ((ret = clixon_module_upgrade(h, xt, msd, cbret)) < 0) goto done; if (ret == 0) goto fail; - + if ((yspec = clicon_dbspec_yang(h)) == NULL){ + clicon_err(OE_YANG, 0, "Yang spec not set"); + goto done; + } + /* After upgrading, XML tree needs to be sorted and yang spec populated */ + if (xml_apply0(xt, CX_ELMNT, xml_spec_populate, yspec) < 0) + goto done; + if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0) + goto done; /* Handcraft transition with with only add tree */ + td->td_target = xt; if (cxvec_append(td->td_target, &td->td_avec, &td->td_alen) < 0) goto done; @@ -302,10 +315,6 @@ startup_validate(clicon_handle h, if (plugin_transaction_begin(h, td) < 0) goto done; - if ((yspec = clicon_dbspec_yang(h)) == NULL){ - clicon_err(OE_YANG, 0, "Yang spec not set"); - goto done; - } /* 5. Make generic validation on all new or changed data. Note this is only call that uses 3-values */ if ((ret = generic_validate(yspec, td, cbret)) < 0) @@ -320,6 +329,112 @@ startup_validate(clicon_handle h, /* 7. Call plugin transaction complete callbacks */ if (plugin_transaction_complete(h, td) < 0) goto done; + + if (xtr){ + *xtr = td->td_target; + td->td_target = NULL; + } + retval = 1; + done: + if (td) + transaction_free(td); + if (msd) + modstate_diff_free(msd); + return retval; + fail: /* cbret should be set */ + if (cbuf_len(cbret)==0){ + clicon_err(OE_CFG, EINVAL, "Validation fail but cbret not set"); + goto done; + } + retval = 0; + goto done; +} + +int +startup_commit(clicon_handle h, + char *db, + cbuf *cbret) +{ + int retval = -1; + yang_spec *yspec; + int ret; + modstate_diff_t *msd = NULL; + cxobj *xt = NULL; + transaction_data_t *td = NULL; + + /* Handcraft a transition with only target and add trees */ + if ((td = transaction_new()) == NULL) + goto done; + /* 2. Parse xml trees + * This is the state we are going to + * Note: xmsdiff contains non-matching modules + * Only if CLICON_XMLDB_MODSTATE is enabled + */ + if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE")) + if ((msd = modstate_diff_new()) == NULL) + goto done; + if (xmldb_get(h, db, "/", 1, &xt, msd) < 0) + goto done; + if (msd && (ret = clixon_module_upgrade(h, xt, msd, cbret)) < 0) + goto done; + if (ret == 0) + goto fail; + if ((yspec = clicon_dbspec_yang(h)) == NULL){ + clicon_err(OE_YANG, 0, "Yang spec not set"); + goto done; + } + /* After upgrading, XML tree needs to be sorted and yang spec populated */ + if (xml_apply0(xt, CX_ELMNT, xml_spec_populate, yspec) < 0) + goto done; + if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0) + goto done; + /* Handcraft transition with with only add tree */ + td->td_target = xt; + if (cxvec_append(td->td_target, &td->td_avec, &td->td_alen) < 0) + goto done; + + /* 4. Call plugin transaction start callbacks */ + if (plugin_transaction_begin(h, td) < 0) + goto done; + + /* 5. Make generic validation on all new or changed data. + Note this is only call that uses 3-values */ + if ((ret = generic_validate(yspec, td, cbret)) < 0) + goto done; + if (ret == 0) + goto fail; /* STARTUP_INVALID */ + + /* 6. Call plugin transaction validate callbacks */ + if (plugin_transaction_validate(h, td) < 0) + goto done; + + /* 7. Call plugin transaction complete callbacks */ + if (plugin_transaction_complete(h, td) < 0) + goto done; + + /* 8. Call plugin transaction commit callbacks */ + if (plugin_transaction_commit(h, td) < 0) + goto done; + /* 9, write (potentially modified) tree to running + * XXX note here startup is copied to candidate, which may confuse everything + */ + if ((ret = xmldb_put(h, "running", OP_REPLACE, td->td_target, + clicon_username_get(h), cbret)) < 0) + goto done; + if (ret == 0) + goto fail; + /* 10. Call plugin transaction end callbacks */ + plugin_transaction_end(h, td); + + /* 11. Copy running back to candidate in case end functions updated running + * XXX: room for improvement: candidate and running may be equal. + * Copy only diffs? + */ + if (xmldb_copy(h, "running", "candidate") < 0){ + /* ignore errors or signal major setback ? */ + clicon_log(LOG_NOTICE, "Error in rollback, trying to continue"); + goto done; + } retval = 1; done: if (td) diff --git a/apps/backend/backend_commit.h b/apps/backend/backend_commit.h index 0d6418b5..adcdc0bc 100644 --- a/apps/backend/backend_commit.h +++ b/apps/backend/backend_commit.h @@ -40,7 +40,8 @@ /* * Prototypes */ -int startup_validate(clicon_handle h, char *db, cbuf *cbret); +int startup_validate(clicon_handle h, char *db, cxobj **xtr, cbuf *cbret); +int startup_commit(clicon_handle h, char *db, cbuf *cbret); int candidate_commit(clicon_handle h, char *db, cbuf *cbret); int from_client_commit(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg, void *regarg); diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index 978aaf2c..3dde30a6 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -98,7 +98,7 @@ backend_terminate(clicon_handle h) close(ss); if ((x = clicon_module_state_get(h)) != NULL) xml_free(x); - if ((x = clicon_yang_changelog_get(h)) != NULL) + if ((x = clicon_xml_changelog_get(h)) != NULL) xml_free(x); if ((yspec = clicon_dbspec_yang(h)) != NULL) yspec_free(yspec); @@ -643,8 +643,8 @@ main(int argc, goto done; /* Must be after netconf_module_load, but before startup code */ - if (clicon_option_bool(h, "CLICON_YANG_CHANGELOG")) - if (clixon_yang_changelog_init(h) < 0) + if (clicon_option_bool(h, "CLICON_XML_CHANGELOG")) + if (clixon_xml_changelog_init(h) < 0) goto done; /* Save modules state of the backend (server). Compare with startup XML */ diff --git a/apps/backend/backend_startup.c b/apps/backend/backend_startup.c index 11780244..5fc9932a 100644 --- a/apps/backend/backend_startup.c +++ b/apps/backend/backend_startup.c @@ -161,15 +161,10 @@ startup_mode_startup(clicon_handle h, if (xmldb_create(h, db) < 0) /* diff */ return -1; } - if ((ret = startup_validate(h, db, cbret)) < 0) + if ((ret = startup_commit(h, db, cbret)) < 0) goto done; if (ret == 0) goto fail; - /* Commit startup */ - if (candidate_commit(h, db, cbret) < 1) /* diff */ - goto fail; - if (ret == 0) /* shouldnt happen (we already validate) */ - goto fail; retval = 1; done: return retval; @@ -241,6 +236,7 @@ startup_extraxml(clicon_handle h, int retval = -1; char *db = "tmp"; int ret; + cxobj *xt = NULL; /* Potentially upgraded XML */ /* Clear tmp db */ if (startup_db_reset(h, db) < 0) @@ -256,11 +252,17 @@ startup_extraxml(clicon_handle h, if (ret == 0) goto fail; } - /* Validate tmp (unless empty?) */ - if ((ret = startup_validate(h, db, cbret)) < 0) + /* Validate the tmp db and return possibly upgraded xml in xt + */ + if ((ret = startup_validate(h, db, &xt, cbret)) < 0) goto done; if (ret == 0) goto fail; + /* Write (potentially modified) xml tree xt back to tmp + */ + if ((ret = xmldb_put(h, "tmp", OP_REPLACE, xt, + clicon_username_get(h), cbret)) < 0) + goto done; /* Merge tmp into running (no commit) */ if ((ret = db_merge(h, db, "running", cbret)) < 0) goto fail; diff --git a/example/example_backend.c b/example/example_backend.c index ec32e65c..7e037bb7 100644 --- a/example/example_backend.c +++ b/example/example_backend.c @@ -246,7 +246,6 @@ example_statedata(clicon_handle h, return retval; } -#ifdef keep_as_example /*! Registered Upgrade callback function * @param[in] h Clicon handle * @param[in] xn XML tree to be updated @@ -259,22 +258,44 @@ example_statedata(clicon_handle h, * @retval 1 OK * @retval 0 Invalid * @retval -1 Error + * @see clicon_upgrade_cb */ static int upgrade_all(clicon_handle h, - cxobj *xn, - char *modname, - char *modns, + cxobj *xt, + char *ns, uint32_t from, uint32_t to, void *arg, cbuf *cbret) { - fprintf(stderr, "%s XML:%s mod:%s %s from:%d to:%d\n", __FUNCTION__, xml_name(xn), - modname, modns, from, to); - return 1; + int retval = -1; + yang_spec *yspec; + yang_stmt *ym; + cxobj **vec = NULL; + cxobj *xc; + size_t vlen; + int i; + + /* Get Yang module for this namespace. Note it may not exist (if obsolete) */ + yspec = clicon_dbspec_yang(h); + if ((ym = yang_find_module_by_namespace(yspec, ns)) == NULL) + goto ok; /* shouldnt happen */ + clicon_debug(1, "%s module %s", __FUNCTION__, ym?ym->ys_argument:"none"); + /* Get all XML nodes with that namespace */ + if (xml_namespace_vec(h, xt, ns, &vec, &vlen) < 0) + goto done; + for (i=0; i #include #include -#include +#include /* * Global variables generated by Makefile diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h index 629d6d80..28e36551 100644 --- a/lib/clixon/clixon_options.h +++ b/lib/clixon/clixon_options.h @@ -210,8 +210,8 @@ int clicon_socket_set(clicon_handle h, int s); cxobj *clicon_module_state_get(clicon_handle h); int clicon_module_state_set(clicon_handle h, cxobj *xms); -/*! Set and get module revision changelog */ -cxobj *clicon_yang_changelog_get(clicon_handle h); -int clicon_yang_changelog_set(clicon_handle h, cxobj *xchlog); +/*! Set and get yang/xml module revision changelog */ +cxobj *clicon_xml_changelog_get(clicon_handle h); +int clicon_xml_changelog_set(clicon_handle h, cxobj *xchlog); #endif /* _CLIXON_OPTIONS_H_ */ diff --git a/lib/clixon/clixon_plugin.h b/lib/clixon/clixon_plugin.h index ed8b3d39..37ee3cd3 100644 --- a/lib/clixon/clixon_plugin.h +++ b/lib/clixon/clixon_plugin.h @@ -70,8 +70,7 @@ typedef int (*clicon_rpc_cb)( /*! Registered Upgrade callback function * @param[in] h Clicon handle * @param[in] xn XML tree to be updated - * @param[in] modname Name of module - * @param[in] modns Namespace of module (for info) + * @param[in] namespace Namespace of module * @param[in] from From revision on the form YYYYMMDD * @param[in] to To revision on the form YYYYMMDD (0 not in system) * @param[in] arg User argument given at rpc_callback_register() @@ -83,8 +82,7 @@ typedef int (*clicon_rpc_cb)( typedef int (*clicon_upgrade_cb)( clicon_handle h, cxobj *xn, - char *modname, - char *modns, + char *namespace, uint32_t from, uint32_t to, void *arg, @@ -246,8 +244,8 @@ int rpc_callback_delete_all(void); int rpc_callback_call(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg); /* upgrade callback API */ -int upgrade_callback_register(clicon_handle h, clicon_upgrade_cb cb, void *arg, char *name, char *namespace, uint32_t from, uint32_t to); +int upgrade_callback_register(clicon_handle h, clicon_upgrade_cb cb, char *namespace, uint32_t from, uint32_t to, void *arg); int upgrade_callback_delete_all(void); -int upgrade_callback_call(clicon_handle h, cxobj *xt, char *modname, char *modns, uint32_t from, uint32_t to, cbuf *cbret); +int upgrade_callback_call(clicon_handle h, cxobj *xt, char *namespace, uint32_t from, uint32_t to, cbuf *cbret); #endif /* _CLIXON_PLUGIN_H_ */ diff --git a/lib/clixon/clixon_yang_changelog.h b/lib/clixon/clixon_xml_changelog.h similarity index 80% rename from lib/clixon/clixon_yang_changelog.h rename to lib/clixon/clixon_xml_changelog.h index d8f5a65d..587478df 100644 --- a/lib/clixon/clixon_yang_changelog.h +++ b/lib/clixon/clixon_xml_changelog.h @@ -34,13 +34,14 @@ * YANG module revision change management. * See draft-wang-netmod-module-revision-management-01 */ -#ifndef _CLIXON_YANG_CHANGELOG_H -#define _CLIXON_YANG_CHANGELOG_H +#ifndef _CLIXON_XML_CHANGELOG_H +#define _CLIXON_XML_CHANGELOG_H /* * Prototypes */ -int yang_changelog_upgrade(clicon_handle h, cxobj *xn, char *modname, char *modns, uint32_t from, uint32_t to, void *arg, cbuf *cbret); -int clixon_yang_changelog_init(clicon_handle h); +int xml_changelog_upgrade(clicon_handle h, cxobj *xn, char *namespace, uint32_t from, uint32_t to, void *arg, cbuf *cbret); +int clixon_xml_changelog_init(clicon_handle h); +int xml_namespace_vec(clicon_handle h, cxobj *xt, char *namespace, cxobj ***vec, size_t *veclen); -#endif /* _CLIXON_YANG_CHANGELOG_H */ +#endif /* _CLIXON_XML_CHANGELOG_H */ diff --git a/lib/clixon/clixon_yang_module.h b/lib/clixon/clixon_yang_module.h index 5466ecd7..160706b5 100644 --- a/lib/clixon/clixon_yang_module.h +++ b/lib/clixon/clixon_yang_module.h @@ -51,8 +51,8 @@ * This is in state of flux so it needss to be conatained and easily changed. */ typedef struct { - cxobj *md_del; /* yang mdoule state deletes */ - cxobj *md_mod; /* yang mdoule state modifications */ + cxobj *md_del; /* yang module state deletes */ + cxobj *md_mod; /* yang module state modifications */ } modstate_diff_t; /* diff --git a/lib/src/Makefile.in b/lib/src/Makefile.in index c80ec2cb..8e76ac2c 100644 --- a/lib/src/Makefile.in +++ b/lib/src/Makefile.in @@ -70,7 +70,7 @@ SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \ clixon_string.c clixon_handle.c \ clixon_xml.c clixon_xml_sort.c clixon_xml_map.c clixon_file.c \ clixon_json.c clixon_yang.c clixon_yang_type.c clixon_yang_module.c \ - clixon_yang_cardinality.c clixon_yang_changelog.c \ + clixon_yang_cardinality.c clixon_xml_changelog.c \ clixon_hash.c clixon_options.c clixon_plugin.c \ clixon_proto.c clixon_proto_client.c \ clixon_xpath.c clixon_xpath_ctx.c clixon_sha1.c \ diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c index 6dc0f754..fdc87c89 100644 --- a/lib/src/clixon_netconf_lib.c +++ b/lib/src/clixon_netconf_lib.c @@ -1043,8 +1043,8 @@ netconf_module_load(clicon_handle h) if (yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0) goto done; /* YANG module revision change management */ - if (clicon_option_bool(h, "CLICON_YANG_CHANGELOG")) - if (yang_spec_parse_module(h, "clixon-yang-changelog", NULL, yspec)< 0) + if (clicon_option_bool(h, "CLICON_XML_CHANGELOG")) + if (yang_spec_parse_module(h, "clixon-xml-changelog", NULL, yspec)< 0) goto done; retval = 0; done: diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index 2bebe421..16ff2750 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -1019,17 +1019,17 @@ clicon_module_state_set(clicon_handle h, * @see draft-wang-netmod-module-revision-management-01 */ cxobj * -clicon_yang_changelog_get(clicon_handle h) +clicon_xml_changelog_get(clicon_handle h) { clicon_hash_t *cdat = clicon_data(h); void *p; - if ((p = hash_value(cdat, "yang-changelog", NULL)) != NULL) + if ((p = hash_value(cdat, "xml-changelog", NULL)) != NULL) return *(cxobj **)p; return NULL; } -/*! Set yang module changelog +/*! Set xml module changelog * @param[in] h Clicon handle * @param[in] s Module revision changelog XML tree * @retval 0 OK @@ -1037,12 +1037,12 @@ clicon_yang_changelog_get(clicon_handle h) * @see draft-wang-netmod-module-revision-management-01 */ int -clicon_yang_changelog_set(clicon_handle h, +clicon_xml_changelog_set(clicon_handle h, cxobj *xchlog) { clicon_hash_t *cdat = clicon_data(h); - if (hash_add(cdat, "yang_changelog", &xchlog, sizeof(xchlog))==NULL) + if (hash_add(cdat, "xml-changelog", &xchlog, sizeof(xchlog))==NULL) return -1; return 0; } diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c index e1de0541..4a29e06f 100644 --- a/lib/src/clixon_plugin.c +++ b/lib/src/clixon_plugin.c @@ -539,13 +539,13 @@ rpc_callback_call(clicon_handle h, * revision. */ typedef struct { - qelem_t uc_qelem; /* List header */ + qelem_t uc_qelem; /* List header */ clicon_upgrade_cb uc_callback; /* RPC Callback */ - void *uc_arg; /* Application specific argument to cb */ - char *uc_name; /* Module name */ - char *uc_namespace; /* Module namespace ??? */ - uint32_t uc_from; /* Module revision (from) or 0 in YYYYMMDD format */ - uint32_t uc_to; /* Module revision (to) in YYYYMMDD format */ + void *uc_arg; /* Application specific argument to cb */ + char *uc_namespace; /* Module namespace */ + uint32_t uc_rev; /* Module revision (to) in YYYYMMDD format or 0 */ + uint32_t uc_from; /* Module revision (from) or 0 in YYYYMMDD format */ + } upgrade_callback_t; /* List of rpc callback entries XXX hang on handle */ @@ -556,10 +556,9 @@ static upgrade_callback_t *upgrade_cb_list = NULL; * @param[in] h clicon handle * @param[in] cb Callback called * @param[in] arg Domain-specific argument to send to callback - * @param[in] name Module name (if NULL all modules) - * @param[in] namespace Module namespace (NOTE not relevant) + * @param[in] namespace Module namespace (if NULL all modules) + * @param[in] rev To module revision (0 means module obsoleted) * @param[in] from From module revision (0 from any revision) - * @param[in] to To module revision (0 means module obsoleted) * @retval 0 OK * @retval -1 Error * @see upgrade_callback_call which makes the actual callback @@ -567,11 +566,10 @@ static upgrade_callback_t *upgrade_cb_list = NULL; int upgrade_callback_register(clicon_handle h, clicon_upgrade_cb cb, - void *arg, - char *name, char *namespace, + uint32_t revision, uint32_t from, - uint32_t to) + void *arg) { upgrade_callback_t *uc; @@ -582,18 +580,14 @@ upgrade_callback_register(clicon_handle h, memset(uc, 0, sizeof(*uc)); uc->uc_callback = cb; uc->uc_arg = arg; - if (name) - uc->uc_name = strdup(name); if (namespace) uc->uc_namespace = strdup(namespace); + uc->uc_rev = revision; uc->uc_from = from; - uc->uc_to = to; ADDQ(uc, upgrade_cb_list); return 0; done: if (uc){ - if (uc->uc_name) - free(uc->uc_name); if (uc->uc_namespace) free(uc->uc_namespace); free(uc); @@ -610,8 +604,6 @@ upgrade_callback_delete_all(void) while((uc = upgrade_cb_list) != NULL) { DELQ(uc, upgrade_cb_list, upgrade_callback_t *); - if (uc->uc_name) - free(uc->uc_name); if (uc->uc_namespace) free(uc->uc_namespace); free(uc); @@ -622,7 +614,7 @@ upgrade_callback_delete_all(void) /*! Search Upgrade callbacks and invoke if module match * * @param[in] h clicon handle - * @param[in] xt XML tree to be updated + * @param[in] xt Top-level XML tree to be updated (includes other ns as well) * @param[in] modname Name of module * @param[in] modns Namespace of module (for info) * @param[in] from From revision on the form YYYYMMDD @@ -636,8 +628,7 @@ upgrade_callback_delete_all(void) int upgrade_callback_call(clicon_handle h, cxobj *xt, - char *modname, - char *modns, + char *namespace, uint32_t from, uint32_t to, cbuf *cbret) @@ -659,11 +650,11 @@ upgrade_callback_call(clicon_handle h, * - Registered from revision >= from AND * - Registered to revision <= to (which includes case both 0) */ - if (uc->uc_name == NULL || strcmp(uc->uc_name, modname)==0) + if (uc->uc_namespace == NULL || strcmp(uc->uc_namespace, namespace)==0) if ((uc->uc_from == 0) || - (uc->uc_from >= from && uc->uc_to <= to)){ - if ((ret = uc->uc_callback(h, xt, modname, modns, from, to, uc->uc_arg, cbret)) < 0){ - clicon_debug(1, "%s Error in: %s", __FUNCTION__, uc->uc_name); + (uc->uc_from >= from && uc->uc_rev <= to)){ + if ((ret = uc->uc_callback(h, xt, namespace, from, to, uc->uc_arg, cbret)) < 0){ + clicon_debug(1, "%s Error in: %s", __FUNCTION__, uc->uc_namespace); goto done; } if (ret == 0) diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index 7b346887..9cb9f489 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -560,7 +560,8 @@ xml_child_nr_type(cxobj *xn, /*! Get a specific child * @param[in] xn xml node * @param[in] i the number of the child, eg order in children vector - * @retval child in XML tree, or NULL if no such child, or empty child + * @retval xml The child xml node + * @retval NULL if no such child, or empty child */ cxobj * xml_child_i(cxobj *xn, @@ -633,6 +634,8 @@ xml_child_each(cxobj *xparent, int i; cxobj *xn = NULL; + if (xparent == NULL) + return NULL; for (i=xprev?xprev->_x_vector_i+1:0; ix_childvec_len; i++){ xn = xparent->x_childvec[i]; if (xn == NULL) @@ -1612,8 +1615,6 @@ _xml_parse(const char *str, goto done; if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0) goto done; - if (xml_apply0(xt, -1, xml_sort_verify, NULL) < 0) - goto done; } retval = 0; done: diff --git a/lib/src/clixon_xml_changelog.c b/lib/src/clixon_xml_changelog.c new file mode 100644 index 00000000..bc4336f8 --- /dev/null +++ b/lib/src/clixon_xml_changelog.c @@ -0,0 +1,367 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + + * YANG module revision change management. + * See draft-wang-netmod-module-revision-management-01 + */ + +#ifdef HAVE_CONFIG_H +#include "clixon_config.h" /* generated by config & autoconf */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* cligen */ +#include + +/* clixon */ +#include "clixon_queue.h" +#include "clixon_hash.h" +#include "clixon_string.h" +#include "clixon_err.h" +#include "clixon_handle.h" +#include "clixon_yang.h" +#include "clixon_log.h" +#include "clixon_xml.h" +#include "clixon_options.h" +#include "clixon_xml_map.h" +#include "clixon_yang_module.h" +#include "clixon_xml_changelog.h" +#include "clixon_xpath_ctx.h" +#include "clixon_xpath.h" + +/*! Perform a changelog operation + * @param[in] h Clicon handle + * @param[in] xi Changelog item + * @param[in] xn XML to upgrade + * @note XXX error handling! + * @note XXX xn --> xt xpath may not match +*/ +static int +changelog_op(clicon_handle h, + cxobj *xt, + cxobj *xi) + +{ + int retval = -1; + char *op; + char *xptarget; /* xpath to target-node */ + char *xplocation; /* xpath to location-node (move) */ + char *ftransform; /* transform string format (modify, create) */ + cxobj *xtrg; /* xml target node */ + cxobj *xloc; /* xml location node */ + cxobj *xnew = NULL; + cxobj *x; + + if ((op = xml_find_body(xi, "change-operation")) == NULL) + goto ok; + if ((xptarget = xml_find_body(xi, "target-node")) == NULL) + goto ok; + /* target node (if any) */ + if ((xtrg = xpath_first(xt, "%s", xptarget)) == NULL) + goto fail; + // fprintf(stderr, "%s %s %s\n", __FUNCTION__, op, xml_name(xt)); + xplocation = xml_find_body(xi, "location-node"); + ftransform = xml_find_body(xi, "transform"); + if (strcmp(op, "insert") == 0){ + /* create a new node by parsing fttransform string and insert it at + target */ + if (ftransform == NULL) + goto fail; + if (xml_parse_va(&xtrg, NULL, "%s", ftransform) < 0) + goto done; + } + else if (strcmp(op, "delete") == 0){ + /* delete target */ + if (xml_purge(xtrg) < 0) + goto done; + } + else if (strcmp(op, "move") == 0){ + /* Move target node to location */ + if ((xloc = xpath_first(xt, "%s", xplocation)) == NULL) + goto fail; + if (xml_addsub(xloc, xtrg) < 0) + goto done; + } + else if (strcmp(op, "replace") == 0){ + /* create a new node by parsing fttransform string and insert it at + target */ + if (ftransform == NULL) + goto fail; + /* replace: remove all children of target */ + while ((x = xml_child_i(xtrg, 0)) != NULL) + if (xml_purge(x) < 0) + goto done; + /* Parse the new node */ + if (xml_parse_va(&xnew, NULL, "%s", ftransform) < 0) + goto done; + if (xml_rootchild(xnew, 0, &xnew) < 0) + goto done; + /* Copy old to new */ + if (xml_copy(xnew, xtrg) < 0) + goto done; + if (xml_purge(xnew) < 0) + goto done; + } + ok: + retval = 1; + done: + return retval; + fail: + retval = 0; + goto done; +} + +/*! Iterate through one changelog item + * @param[in] h Clicon handle + * @param[in] xt Changelog list + * @param[in] xn XML to upgrade + */ +static int +changelog_iterate(clicon_handle h, + cxobj *xt, + cxobj *xch) + +{ + int retval = -1; + cxobj **vec = NULL; + size_t veclen; + int ret; + int i; + + if (xpath_vec(xch, "change-log", &vec, &veclen) < 0) + goto done; + /* Iterate through changelog items */ + for (i=0; i..., f) || to, , NULL] + */ +int +xml_namespace_vec(clicon_handle h, + cxobj *xt, + char *namespace, + cxobj ***vecp, + size_t *veclenp) +{ + int retval = -1; + cxobj **xvec = NULL; + size_t xlen; + cxobj *xc; + char *ns; + int i; + + /* Allocate upper bound on length (ie could be too large) + a NULL element + * (event though we use veclen) + */ + xlen = xml_child_nr_type(xt, CX_ELMNT)+1; + if ((xvec = calloc(xlen, sizeof(cxobj*))) == NULL){ + clicon_err(OE_UNIX, errno, "calloc"); + goto done; + } + /* Iterate and find xml nodes with assoctaed namespace */ + xc = NULL; + i = 0; + while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) { + if (xml2ns(xc, NULL, &ns) < 0) /* Get namespace of XML */ + goto done; + if (strcmp(namespace, ns)) + continue; /* no match */ + xvec[i++] = xc; + } + *vecp = xvec; + *veclenp = i; + retval = 0; + done: + return retval; +} diff --git a/lib/src/clixon_yang_changelog.c b/lib/src/clixon_yang_changelog.c deleted file mode 100644 index 63062e30..00000000 --- a/lib/src/clixon_yang_changelog.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * - ***** BEGIN LICENSE BLOCK ***** - - Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren - - This file is part of CLIXON. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - Alternatively, the contents of this file may be used under the terms of - the GNU General Public License Version 3 or later (the "GPL"), - in which case the provisions of the GPL are applicable instead - of those above. If you wish to allow use of your version of this file only - under the terms of the GPL, and not to allow others to - use your version of this file under the terms of Apache License version 2, - indicate your decision by deleting the provisions above and replace them with - the notice and other provisions required by the GPL. If you do not delete - the provisions above, a recipient may use your version of this file under - the terms of any one of the Apache License version 2 or the GPL. - - ***** END LICENSE BLOCK ***** - - * YANG module revision change management. - * See draft-wang-netmod-module-revision-management-01 - */ - -#ifdef HAVE_CONFIG_H -#include "clixon_config.h" /* generated by config & autoconf */ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* cligen */ -#include - -/* clixon */ -#include "clixon_queue.h" -#include "clixon_hash.h" -#include "clixon_string.h" -#include "clixon_err.h" -#include "clixon_handle.h" -#include "clixon_yang.h" -#include "clixon_log.h" -#include "clixon_xml.h" -#include "clixon_options.h" -#include "clixon_xml_map.h" -#include "clixon_yang_module.h" -#include "clixon_yang_changelog.h" -#include "clixon_xpath_ctx.h" -#include "clixon_xpath.h" - -#if 0 -/*! Make a specific change - 0001 - create - - - /a:system/a:y; - - -*/ -static int -upgrade_op(cxobj *x) -{ - int retval = -1; - - xml_print(stderr, x); - retval = 0; - // done: - return retval; -} - -static int -upgrade_deleted(clicon_handle h, - char *name, - cxobj *xs) -{ - int retval = -1; - - fprintf(stderr, "%s \"%s\" belongs to a removed module\n", __FUNCTION__, name); - retval = 0; - // done: - return retval; -} - -/*! - * @param[in] xs Module state - */ -static int -upgrade_modified(clicon_handle h, - char *name, - char *namespace, - cxobj *xs, - cxobj *xch) -{ - int retval = -1; - char *mname; - yang_spec *yspec = NULL; - yang_stmt *ymod; - yang_stmt *yrev; - char *mrev; - cxobj **vec = NULL; - size_t veclen; - int i; - - fprintf(stderr, "%s: \"%s\" belongs to an upgraded module\n", __FUNCTION__, name); - yspec = clicon_dbspec_yang(h); - - /* We need module-name of XML since changelog uses that (change in changelog?)*/ - mname = xml_find_body(xs, "name"); - - /* Look up system module (alt send it via argument) */ - if ((ymod = yang_find_module_by_name(yspec, mname)) == NULL) - goto done; - if ((yrev = yang_find((yang_node*)ymod, Y_REVISION, NULL)) == NULL) - goto done; - mrev = yrev->ys_argument; - /* Look up in changelog */ - - if (xpath_vec(xch, "module[name=\"%s\" and revision=\"%s\"]/revision-change-log", - &vec, &veclen, mname, mrev) < 0) - goto done; - /* Iterate through changelog */ - for (i=0; i..., md_del) - free(md->md_del); + xml_free(md->md_del); if (md->md_mod) - free(md->md_mod); + xml_free(md->md_mod); free(md); return 0; } @@ -375,12 +375,72 @@ yang_modules_state_get(clicon_handle h, return retval; } +/*! For single module state with namespace, get revisions and send upgrade callbacks + * @param[in] h Clicon handle + * @param[in] xt Top-level XML tree to be updated (includes other ns as well) + * @param[in] xs XML module state (for one yang module) + * @param[in] xvec Help vector where to store XML child nodes (??) + * @param[in] xlen Length of xvec + * @param[in] ns0 Namespace of module state we are looking for + * @param[in] deleted If set, dont look for system yang module and "to" rev + * @param[out] cbret Netconf error message if invalid + * @retval 1 OK + * @retval 0 Validation failed (cbret set) + * @retval -1 Error + */ +static int +mod_ns_upgrade(clicon_handle h, + cxobj *xt, + cxobj *xs, + char *ns, + int deleted, + cbuf *cbret) +{ + int retval = -1; + char *b; /* string body */ + yang_stmt *ymod; + yang_stmt *yrev; + uint32_t from = 0; + uint32_t to = 0; + int ret; + yang_spec *yspec; + + /* Make upgrade callback for this XML, specifying the module + * namespace, from and to revision. + */ + if ((b = xml_find_body(xs, "revision")) != NULL) /* Module revision */ + if (ys_parse_date_arg(b, &from) < 0) + goto done; + if (deleted) + to = 0; + else { /* Look up system module (alt send it via argument) */ + yspec = clicon_dbspec_yang(h); + if ((ymod = yang_find_module_by_namespace(yspec, ns)) == NULL) + goto fail; + if ((yrev = yang_find((yang_node*)ymod, Y_REVISION, NULL)) == NULL) + goto fail; + if (ys_parse_date_arg(yrev->ys_argument, &to) < 0) + goto done; + } + if ((ret = upgrade_callback_call(h, xt, ns, from, to, cbret)) < 0) + goto done; + if (ret == 0) /* XXX ignore and continue? */ + goto fail; + retval = 1; + done: + return retval; + fail: + retval = 0; + goto done; +} + /*! Upgrade XML * @param[in] h Clicon handle * @param[in] xt XML tree (to upgrade) * @param[in] msd Modules-state differences of xt + * @param[out] cbret Netconf error message if invalid * @retval 1 OK - * @retval 0 Validation failed + * @retval 0 Validation failed (cbret set) * @retval -1 Error */ int @@ -390,67 +450,37 @@ clixon_module_upgrade(clicon_handle h, cbuf *cbret) { int retval = -1; - cxobj *xc; /* XML child of data */ - char *namespace; - cxobj *xs; /* XML module state */ - char *xname; /* XML top-level symbol name */ - int state; /* 0: no changes, 1: deleted, 2: modified */ - char *modname; - yang_spec *yspec; - yang_stmt *ymod; - yang_stmt *yrev; - char *rev; - uint32_t from; - uint32_t to; + char *ns; /* Namespace */ + cxobj *xs; /* XML module state */ int ret; - /* Iterate through db XML top-level - get namespace info */ - xc = NULL; - while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) { - xname = xml_name(xc); /* xml top-symbol name */ - if (xml2ns(xc, NULL, &namespace) < 0) /* Get namespace of XML */ - goto done; - if (namespace == NULL){ - clicon_log(LOG_DEBUG, "XML %s lacks namespace", xname); - goto fail; - } - /* Look up module-state via namespace of XML */ - state = 0; /* XML matches system modules */ - if (msd){ - if ((xs = xpath_first(msd->md_del, "module[namespace=\"%s\"]", namespace)) != NULL) - state = 1; /* XML belongs to a removed module */ - else if ((xs = xpath_first(msd->md_mod, "module[namespace=\"%s\"]", namespace)) != NULL) - state = 2; /* XML belongs to an outdated module */ - } - /* Pick up more data from data store module-state */ - from = to = 0; - modname = NULL; - if (state && xs && msd){ /* sanity: XXX what about no msd?? */ - modname = xml_find_body(xs, "name"); /* Module name */ - if ((rev = xml_find_body(xs, "revision")) != NULL) /* Module revision */ - if (ys_parse_date_arg(rev, &from) < 0) - goto done; - if (state > 1){ - yspec = clicon_dbspec_yang(h); - /* Look up system module (alt send it via argument) */ - if ((ymod = yang_find_module_by_name(yspec, modname)) == NULL) - goto fail; - if ((yrev = yang_find((yang_node*)ymod, Y_REVISION, NULL)) == NULL) - goto fail; - if (ys_parse_date_arg(yrev->ys_argument, &to) < 0) - goto done; - - } - } - /* Make upgrade callback for this XML, specifying the module name, - * namespace, from and to revision. - * XXX: namespace may be known but not module!! - */ - if ((ret = upgrade_callback_call(h, xc, modname, namespace, from, to, NULL)) < 0) + if (msd == NULL) + goto ok; + /* Iterate through xml modified module state */ + xs = NULL; + while ((xs = xml_child_each(msd->md_mod, xs, CX_ELMNT)) != NULL) { + /* Extract namespace */ + if ((ns = xml_find_body(xs, "namespace")) == NULL) + goto done; + /* Extract revisions and make callbacks */ + if ((ret = mod_ns_upgrade(h, xt, xs, ns, 0, cbret)) < 0) goto done; if (ret == 0) goto fail; } + /* Iterate through xml deleted module state */ + xs = NULL; + while ((xs = xml_child_each(msd->md_del, xs, CX_ELMNT)) != NULL) { + /* Extract namespace */ + if ((ns = xml_find_body(xs, "namespace")) == NULL) + continue; + /* Extract revisions and make callbacks (now w deleted=1) */ + if ((ret = mod_ns_upgrade(h, xt, xs, ns, 1, cbret)) < 0) + goto done; + if (ret == 0) + goto fail; + } + ok: retval = 1; done: return retval; diff --git a/test/test_upgrade_changelog.sh b/test/test_upgrade_changelog.sh index e753afcf..a463c691 100755 --- a/test/test_upgrade_changelog.sh +++ b/test/test_upgrade_changelog.sh @@ -3,7 +3,7 @@ # Ways of changes (operation-type) are: # create, delete, move, modify # In this example, example-a has the following changes: -# - Create y, delete x, modify host-name, move z +# - Create y, delete x, replace host-name, move z # example-b is completely obsoleted # Magic line must be first in script (see README.md) @@ -16,84 +16,103 @@ changelog=$dir/changelog.xml # Module revision changelog changelog2=$dir/changelog2.xml # From draft appendix exa01y=$dir/example-a@2017-12-01.yang exa20y=$dir/example-a@2017-12-20.yang +exb20y=$dir/example-b@2017-12-20.yang # draft-wang-netmod-module-revision-management-01 # 3.2.1 and 4.1 example-a revision 2017-12-01 cat < $exa01y - module example-a{ - yang-version 1.1; - namespace "urn:example:a"; - prefix "a"; +module example-a{ + yang-version 1.1; + namespace "urn:example:a"; + prefix "a"; - organization "foo."; - contact "fo@example.com"; - description - "foo."; + organization "foo."; + contact "fo@example.com"; + description + "foo."; - revision 2017-12-01 { - description "Initial revision."; - } + revision 2017-12-01 { + description "Initial revision."; + } - container system { - leaf a { - type string; - description "no change"; - } - leaf x { - type string; - description "delete"; - } - leaf host-name { - type uint32; - description "modify type"; - } - leaf z { - description "move to alt"; - type string; - } - } - container alt { - } - } + container system { + leaf a { + type string; + description "no change"; + } + leaf x { + type string; + description "delete"; + } + leaf host-name { + type uint32; + description "modify type"; + } + leaf z { + description "move to alt"; + type string; + } + } + container alt { + } +} EOF # 3.2.1 and 4.1 example-a revision 2017-12-20 cat < $exa20y - module example-a{ - yang-version 1.1; - namespace "urn:example:a"; - prefix "a"; +module example-a { + yang-version 1.1; + namespace "urn:example:a"; + prefix "a"; - organization "foo."; - contact "fo@example.com"; - description - "foo."; + organization "foo."; + contact "fo@example.com"; + description + "foo."; - revision 2017-12-20 { - description "Create y, delete x, modify host-name, move z"; - } - revision 2017-12-01 { - description "Initial revision."; - } - container system { - leaf a { - type string; - description "no change"; - } - leaf host-name { - type string; - description "modify type"; - } - leaf y { - type string; - description "create"; - } - } - container alt { - leaf z { - description "move to alt"; - type string; - } - } + revision 2017-12-20 { + description "Create y, delete x, replace host-name, move z"; + } + revision 2017-12-01 { + description "Initial revision."; + } + container system { + leaf a { + type string; + description "no change"; + } + leaf host-name { + type string; + description "replace"; + } + leaf y { + type string; + description "create"; + } + } + container alt { + leaf z { + description "move to alt"; + type string; + } + } +} +EOF + +# 3.2.1 and 4.1 example-a revision 2017-12-20 +cat < $exb20y +module example-b { + yang-version 1.1; + namespace "urn:example:b"; + prefix "b"; + + organization "foo."; + contact "fo@example.com"; + description + "foo."; + + revision 2017-12-20 { + description "Remove all"; + } } EOF @@ -129,12 +148,18 @@ cat < $dir/startup_db remove me move me + + Obsolete EOF +# Wanted new XML +XML='dont change mei am modifiedcreatedmove me' + + # Create configuration cat < $cfg @@ -147,8 +172,8 @@ cat < $cfg $dir /usr/local/lib/xmldb/text.so true - true - $changelog + true + $changelog /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/cli $APPNAME @@ -157,52 +182,44 @@ EOF # Changelog of example-a: cat < $changelog - + - example-b - 2017-12-01 - + urn:example:b + 2017-12-01 + 2017-12-20 + + 0001 + delete + /b:system-b + - example-a + urn:example:a + 2017-12-01 2017-12-20 - true - + 0001 - create - - - /a:system/a:y; - - - - + insert + /a:system + <y>created</y> + + 0002 delete - - - /a:system/a:x; - - - - + /a:system/a:x + + 0003 - modify - - - /a:system/a:host-name; - - - - + replace + /a:system/a:host-name + <host-name>i am modified</host-name> + + 0004 move - - - /a:system/a:z; - - - + /a:system/a:z + /a:alt + EOF @@ -236,7 +253,7 @@ sleep $RCWAIT new "Check failsafe (work in progress)" new "Check running db content" -expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' '^Failsafe]]>]]>$' +expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' "^$XML]]>]]>$" new "Kill restconf daemon" stop_restconf diff --git a/yang/clixon/Makefile.in b/yang/clixon/Makefile.in index d176e1cc..bd541876 100644 --- a/yang/clixon/Makefile.in +++ b/yang/clixon/Makefile.in @@ -43,7 +43,7 @@ CLIXON_DATADIR = @CLIXON_DATADIR@ YANGSPECS = clixon-config@2019-03-05.yang YANGSPECS += clixon-lib@2019-01-02.yang YANGSPECS += clixon-rfc5277@2008-07-01.yang -YANGSPECS += clixon-yang-changelog@2019-03-21.yang +YANGSPECS += clixon-xml-changelog@2019-03-21.yang APPNAME = clixon # subdir ehere these files are installed diff --git a/yang/clixon/clixon-config@2019-03-05.yang b/yang/clixon/clixon-config@2019-03-05.yang index bbf62324..7987e8a2 100644 --- a/yang/clixon/clixon-config@2019-03-05.yang +++ b/yang/clixon/clixon-config@2019-03-05.yang @@ -392,6 +392,18 @@ module clixon-config { info. When loaded at startup, a check is made if the system yang modules match"; } + leaf CLICON_XML_CHANGELOG { + type boolean; + default false; + description "If true enable automatic upgrade using yang clixon + changelog."; + } + leaf CLICON_XML_CHANGELOG_FILE { + type string; + description "Name of file with module revision changelog. + If CLICON_XML_CHANGELOG is true, Clixon + reads the module changelog from this file."; + } leaf CLICON_USE_STARTUP_CONFIG { type int32; default 0; @@ -428,18 +440,6 @@ module clixon-config { data. If enabled, module info will appear when doing netconf get or restconf GET"; } - leaf CLICON_YANG_CHANGELOG { - type boolean; - default false; - description "If true enable automatic upgrade using yang clixon - changelog."; - } - leaf CLICON_YANG_CHANGELOG_FILE { - type string; - description "Name of file with module revision changelog. - If CLICON_YANG_CHANGELOG is true, Clixon - reads the module changelog from this file."; - } leaf CLICON_MODULE_SET_ID { type string; default "0"; diff --git a/yang/clixon/clixon-xml-changelog@2019-03-21.yang b/yang/clixon/clixon-xml-changelog@2019-03-21.yang new file mode 100644 index 00000000..b5130db6 --- /dev/null +++ b/yang/clixon/clixon-xml-changelog@2019-03-21.yang @@ -0,0 +1,134 @@ +module clixon-xml-changelog { + yang-version 1.1; + namespace "http://clicon.org/xml-changelog"; + prefix ml; + + import ietf-yang-library { + prefix yanglib; + } + import ietf-yang-types { + prefix yang; + } + + organization "Clixon"; + contact + "Olof Hagsand "; + description + "This is experimental XML changelog module with several influences: + 1) draft-wang-netmod-module-revision-management-01, by: + Qin Wu + Zitao Wang + 2) XProc https://www.w3.org/TR/xproc/#xpath-context"; + + revision 2019-03-21 { + description + "Initial Clixon derived version"; + } + + typedef operation_type { + description + "From: https://en.wikipedia.org/wiki/XML_pipeline: + Rename - renames elements or attributes without modifying the content + Replace - replaces elements or attributes + Insert - adds a new data element to the output stream at a specified point + Delete - removes an element or attribute (also known as pruning the input tree) + Wrap - wraps elements with additional elements + Reorder - changes the order of elements + More inspiration in XProc: https://www.w3.org/TR/xproc/#ex2"; + type enumeration{ + enum rename { + description "Rename the target node (NYI)"; + } + enum replace { + description "Replace the target data node + modification is given by the leaf transform which + is a string with %s where the original value + is inserted"; + } + enum insert { + description "Create new data nodes and insert under an existing node"; + } + enum delete { + description "Delete the target node"; + } + enum move { + description "Move the target node(Added)"; + } + enum wrap { + description "Wraps elements with additional elements(NYI)"; + } + enum reorder { + description "Changes the order of elements (NYI)"; + } + } + } + container yang-modules { + config false; + list module { + key "namespace revision"; + leaf namespace { + type string; + description + "The YANG namespace identifying a module or submodule. + XML needs to be identified by namespace, translation to + module name may not always be possible."; + } + leaf revision { + type yanglib:revision-identifier; + description + "The YANG module or submodule revision date. + This is the actual date of the changlelog items. + Note however if the terminate flag is set, this is a virtual + revision just in place to terminate the XML, such as removing or + moving items,."; + } + leaf revfrom { + type yanglib:revision-identifier; + description + "Optional revision from date. This changelog is effective in the + range [from,to]. If from is not given the changelog is open-ended. + Several changelogs may be applied if the upgrade spans multiple + ranges: [from0,to0],..[fromN,toN]"; + } + list change-log { + description + "List for module revision change log"; + key "index"; + leaf index { + type uint32; + description + "Index for module change log"; + } + leaf change-operation { + type operation_type; + mandatory true; + description + "This leaf indicate the change operation, such as create, move, delete, modify, etc."; + } + leaf target-node { + type yang:xpath1.0; + mandatory true; + description + "Identifies the target data node for update. + for move, modify or delete the target-node points to + the data node of the old version. + For create, it is the parent where it should be + inserted."; + } + leaf location-node { + description + "If op is move, this denotes the destination"; + type yang:xpath1.0; + } + leaf transform { + description + "If op is modify or create, this denotes how to + transform the XML encoding. + Special value %s for the original value."; + type string; + } + + } + } + } +} diff --git a/yang/clixon/clixon-yang-changelog@2019-03-21.yang b/yang/clixon/clixon-yang-changelog@2019-03-21.yang deleted file mode 100644 index 01e5ecc3..00000000 --- a/yang/clixon/clixon-yang-changelog@2019-03-21.yang +++ /dev/null @@ -1,326 +0,0 @@ -module clixon-yang-changelog { - yang-version 1.1; - namespace "http://clicon.org/yang-changelog"; - prefix ml; - - import ietf-yang-library { - prefix yanglib; - } - import ietf-yang-types { - prefix yang; - } - - organization "Clixon"; - contact - "Olof Hagsand "; - description - "This is experimentalYANG changelog derived from: - draft-wang-netmod-module-revision-management-01 - with the following contacts and references: - WG Web: - - WG List: - - Author: Qin Wu - - Zitao Wang - "; - reference "draft-wang-netmod-module-revision-management-01"; - revision 2019-03-21 { - description - "Initial Clixon derived version"; - } - - identity operation-type { - description - "Abstract base identity for the operation type "; - } - - identity create { - base operation-type; - description - "Denotes create new data nodes"; - } - - identity delete { - base operation-type; - description - "Denotes delete the target node"; - } - - identity move { - base operation-type; - description - "Denote move the target node."; - } - - identity modify { - base operation-type; - description - "Denote modify the target data node."; - } - - identity statement-type { - description - "Base identity for statement type"; - } - - identity feature-statement { - base statement-type; - description - "feature statement, if this type be chose, it means that the - feature or if-feature statement been modified"; - } - identity identity-statement { - base statement-type; - description - "identity statement, if this type be chose, it means that the - identity statement been modified, for example, add new identity, etc."; - } - - identity grouping-statement { - base statement-type; - description - "grouping statement, if this type be chose, it means that the grouping - statement been modified."; - } - - identity typedef-statement { - base statement-type; - description - "typedef statement, if this type be chose, it means that the typedef - statement been modified."; - } - - identity augment-statement { - base statement-type; - description - "augment statement, if this type be chose, it means that the augment - statement been modified."; - } - - identity rpc-statement { - base statement-type; - description - "rpc statement, if this type be chose, it means that the rpc - statement been modified."; - } - - identity notification-statement { - base statement-type; - description - "notification statement, if this type be chose, it means that the notification - statement been modified."; - } - - extension purpose { - argument name; - description - "The purpose can be used to mark the data nodes change purpose. - The name argument can be specified in the following recommended mode - - bug-fix, which can help user to understand the data nodes' changes present bug fix, - - new-function, which can help user to understand the data nodes' changes present new function, - - nmda-conform, which can help user to understand the data nodes' changes conform to NMDA, - - and note that the user can argument the purpose name according to their sepcific requirements."; - } - - grouping data-definition { - container data-definition { - leaf target-node { - type yang:xpath1.0; - mandatory true; - description - "Identifies the target data node for update. - Notice that, if the update-type equal to move or delete, - this target-node must point to the data node of old version. - \t - For example, suppose the target node is a YANG leaf named a, - and the previous version is: - \t - container foo { - leaf a { type string; } - leaf b { type int32; } - } - \t - the new version is: - container foo { - leaf b {type int32;} - } - \t - Therefore, the targe-node should be /foo/a."; - } - leaf location-point { - type yang:xpath1.0; - description - "Identifies the location point where the updates happened."; - } - leaf where { - when "derived-from-or-self(../../change-operation, 'move')" { - description - "This leaf only applies for 'move' - updates."; - } - type enumeration { - enum "before" { - description - "Insert or move a data node before the data resource - identified by the 'point' parameter."; - } - enum "after" { - description - "Insert or move a data node after the data resource - identified by the 'point' parameter."; - } - enum "first" { - description - "Insert or move a data node so it becomes ordered - as the first entry."; - } - enum "last" { - description - "Insert or move a data node so it becomes ordered - as the last entry."; - } - } - default "last"; - description - "Identifies where a data resource will be inserted - or moved."; - } - anydata data-definition { - when "derived-from-or-self(../../change-operation, 'modify')" { - description - "This nodes only be present when - the 'change-operation' equal to 'modify'."; - } - description - "This nodes used for present the definitions before updated. - And this nodes only be present when - the 'change-operation' equal to 'modify'."; - } - description - "Container for data statement"; - } - description - "Grouping for data definition"; - } - - grouping other-statement { - container other-statement { - leaf statement-name { - type identityref { - base statement-type; - } - description - "Statement name, for example, identity, feature, typedef, etc."; - } - anydata statement-definition { - description - "This nodes used for present new the definitions."; - } - list substatements { - key "statement-name"; - leaf statement-name { - type identityref { - base statement-type; - } - description - "Statement name, for example, identity, feature, typedef, etc."; - } - anydata substatement-definition { - description - "This nodes used for present new the definitions."; - } - description - "List for substatements updates"; - } - description - "Container for header statement updates"; - } - description - "Grouping for header statement"; - } - - grouping change-log { - list revision-change-log { - key "index"; - leaf index { - type uint32; - description - "Index for module change log"; - } - leaf change-operation { - type identityref { - base operation-type; - } - mandatory true; - description - "This leaf indicate the change operation, such as create, move, delete, modify, etc."; - } - choice yang-statements { - description - "Choice for various YANG statements that have been impacted."; - case data-definition-statement { - uses data-definition; - } - case other-statement { - uses other-statement; - } - } - description - "List for module revision change log"; - } - description - "Grouping for module revision change log"; - } - - container yang-modules { - config false; - list module { - key "name revision"; - leaf name { - type yang:yang-identifier; - description - "The YANG module or submodule name."; - } - leaf revision { - type yanglib:revision-identifier; - description - "The YANG module or submodule revision date. If no revision - statement is present in the YANG module or submodule, this - leaf is not instantiated."; - } - leaf backward-compatible { - type boolean; - description - "Indicates whether it is a backward compatible version. - If this parameter is set to true, it means that this version is - a backwards compatible version"; - } - uses change-log; - description - "List for module updated log"; - } - description - "This container present the modules updated log."; - } - augment "/yanglib:yang-library/yanglib:module-set/yanglib:module" { - description - "Augment the yang library with backward compatibility indication."; - leaf backward-compatible { - type boolean; - description - "backward compatibility indication."; - } - } - augment "/yanglib:yang-library/yanglib:module-set/yanglib:module/yanglib:submodule" { - description - "Augment the yang library with backward compatibility indication."; - leaf backward-compatible { - type boolean; - description - "backward compatibility indication."; - } - } -}