static struct list *sysrepo_threads;
static sr_session_ctx_t *session;
static sr_conn_ctx_t *connection;
+static struct nb_transaction *transaction;
static int frr_sr_read_cb(struct thread *thread);
static int frr_sr_write_cb(struct thread *thread);
/* Convert FRR YANG data value to sysrepo YANG data value. */
static int yang_data_frr2sr(struct yang_data *frr_data, sr_val_t *sr_data)
{
+ struct nb_node *nb_node;
const struct lys_node *snode;
struct lys_node_container *scontainer;
struct lys_node_leaf *sleaf;
sr_val_set_xpath(sr_data, frr_data->xpath);
- snode = frr_data->snode;
+ nb_node = nb_node_find(frr_data->xpath);
+ if (!nb_node) {
+ flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
+ "%s: unknown data path: %s", __func__,
+ frr_data->xpath);
+ return -1;
+ }
+
+ snode = nb_node->snode;
switch (snode->nodetype) {
case LYS_CONTAINER:
scontainer = (struct lys_node_container *)snode;
return NB_OK;
}
-/* Callback for changes in the running configuration. */
-static int frr_sr_config_change_cb(sr_session_ctx_t *session,
- const char *module_name,
- sr_notif_event_t sr_ev, void *private_ctx)
+static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session,
+ const char *module_name,
+ bool startup_config)
{
sr_change_iter_t *it;
int ret;
char xpath[XPATH_MAXLEN];
struct nb_config *candidate;
- /*
- * Ignore SR_EV_ABORT and SR_EV_APPLY. We'll leverage the northbound
- * layer itself to abort or apply the configuration changes when a
- * transaction is created.
- */
- if (sr_ev != SR_EV_ENABLED && sr_ev != SR_EV_VERIFY)
- return SR_ERR_OK;
-
snprintf(xpath, sizeof(xpath), "/%s:*", module_name);
ret = sr_get_changes_iter(session, xpath, &it);
if (ret != SR_ERR_OK) {
return SR_ERR_INTERNAL;
}
- /* Commit changes. */
- ret = nb_candidate_commit(candidate, NB_CLIENT_SYSREPO, true, NULL,
- NULL);
- nb_config_free(candidate);
+ transaction = NULL;
+ if (startup_config) {
+ /*
+ * sysrepod sends the entire startup configuration using a
+ * single event (SR_EV_ENABLED). This means we need to perform
+ * the full two-phase commit protocol in one go here.
+ */
+ ret = nb_candidate_commit(candidate, NB_CLIENT_SYSREPO, true,
+ NULL, NULL);
+ } else {
+ /*
+ * Validate the configuration changes and allocate all resources
+ * required to apply them.
+ */
+ ret = nb_candidate_commit_prepare(candidate, NB_CLIENT_SYSREPO,
+ NULL, &transaction);
+ }
/* Map northbound return code to sysrepo return code. */
switch (ret) {
case NB_OK:
+ return SR_ERR_OK;
case NB_ERR_NO_CHANGES:
+ nb_config_free(candidate);
return SR_ERR_OK;
case NB_ERR_LOCKED:
return SR_ERR_LOCKED;
}
}
-static void frr_sr_state_get_elem(struct list *elements,
- struct nb_node *nb_node,
- const void *list_entry, const char *xpath)
+static int frr_sr_config_change_cb_apply(sr_session_ctx_t *session,
+ const char *module_name)
{
- struct yang_data *data;
+ /* Apply the transaction. */
+ if (transaction) {
+ struct nb_config *candidate = transaction->config;
- data = nb_node->cbs.get_elem(xpath, list_entry);
- if (data)
- listnode_add(elements, data);
+ nb_candidate_commit_apply(transaction, true, NULL);
+ nb_config_free(candidate);
+ }
+
+ return SR_ERR_OK;
}
-static void frr_sr_state_cb_container(struct list *elements, const char *xpath,
- const struct lys_node *snode)
+static int frr_sr_config_change_cb_abort(sr_session_ctx_t *session,
+ const char *module_name)
{
- struct lys_node *child;
+ /* Abort the transaction. */
+ if (transaction) {
+ struct nb_config *candidate = transaction->config;
- LY_TREE_FOR (snode->child, child) {
- struct nb_node *nb_node = child->priv;
- char xpath_child[XPATH_MAXLEN];
-
- if (!nb_operation_is_valid(NB_OP_GET_ELEM, child))
- continue;
-
- snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath,
- child->name);
-
- frr_sr_state_get_elem(elements, nb_node, NULL, xpath_child);
+ nb_candidate_commit_abort(transaction);
+ nb_config_free(candidate);
}
+
+ return SR_ERR_OK;
}
-static void frr_sr_state_cb_list_entry(struct list *elements,
- const char *xpath_list,
- const void *list_entry,
- struct lys_node *child)
+/* Callback for changes in the running configuration. */
+static int frr_sr_config_change_cb(sr_session_ctx_t *session,
+ const char *module_name,
+ sr_notif_event_t sr_ev, void *private_ctx)
{
- struct nb_node *nb_node = child->priv;
- struct lys_node_leaf *sleaf;
- char xpath_child[XPATH_MAXLEN];
-
- /* Sysrepo doesn't want to know about list keys. */
- switch (child->nodetype) {
- case LYS_LEAF:
- sleaf = (struct lys_node_leaf *)child;
- if (lys_is_key(sleaf, NULL))
- return;
- break;
- case LYS_LEAFLIST:
- break;
+ switch (sr_ev) {
+ case SR_EV_ENABLED:
+ return frr_sr_config_change_cb_verify(session, module_name,
+ true);
+ case SR_EV_VERIFY:
+ return frr_sr_config_change_cb_verify(session, module_name,
+ false);
+ case SR_EV_APPLY:
+ return frr_sr_config_change_cb_apply(session, module_name);
+ case SR_EV_ABORT:
+ return frr_sr_config_change_cb_abort(session, module_name);
default:
- return;
+ flog_err(EC_LIB_LIBSYSREPO, "%s: unknown sysrepo event: %u",
+ __func__, sr_ev);
+ return SR_ERR_INTERNAL;
}
-
- if (!nb_operation_is_valid(NB_OP_GET_ELEM, child))
- return;
-
- snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath_list,
- child->name);
-
- frr_sr_state_get_elem(elements, nb_node, list_entry, xpath_child);
}
-static void frr_sr_state_cb_list(struct list *elements, const char *xpath,
- const struct lys_node *snode)
+static int frr_sr_state_data_iter_cb(const struct lys_node *snode,
+ struct yang_translator *translator,
+ struct yang_data *data, void *arg)
{
- struct nb_node *nb_node = snode->priv;
- struct lys_node_list *slist = (struct lys_node_list *)snode;
- const void *next;
-
- for (next = nb_node->cbs.get_next(xpath, NULL); next;
- next = nb_node->cbs.get_next(xpath, next)) {
- struct yang_list_keys keys;
- const void *list_entry;
- char xpath_list[XPATH_MAXLEN];
- struct lys_node *child;
-
- /* Get the list keys. */
- if (nb_node->cbs.get_keys(next, &keys) != NB_OK) {
- flog_warn(EC_LIB_NB_CB_STATE,
- "%s: failed to get list keys", __func__);
- continue;
- }
+ struct list *elements = arg;
- /* Get list item. */
- list_entry = nb_node->cbs.lookup_entry(&keys);
- if (!list_entry) {
- flog_warn(EC_LIB_NB_CB_STATE,
- "%s: failed to lookup list entry", __func__);
- continue;
- }
-
- /* Append list keys to the XPath. */
- strlcpy(xpath_list, xpath, sizeof(xpath_list));
- for (unsigned int i = 0; i < keys.num; i++) {
- snprintf(xpath_list + strlen(xpath_list),
- sizeof(xpath_list) - strlen(xpath_list),
- "[%s='%s']", slist->keys[i]->name,
- keys.key[i].value);
- }
+ listnode_add(elements, data);
- /* Loop through list entries. */
- LY_TREE_FOR (snode->child, child) {
- frr_sr_state_cb_list_entry(elements, xpath_list,
- list_entry, child);
- }
- }
+ return NB_OK;
}
/* Callback for state retrieval. */
{
struct list *elements;
struct yang_data *data;
- const struct lys_node *snode;
struct listnode *node;
sr_val_t *v;
int ret, count, i = 0;
- /* Find schema node. */
- snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 0);
-
elements = yang_data_list_new();
-
- switch (snode->nodetype) {
- case LYS_CONTAINER:
- frr_sr_state_cb_container(elements, xpath, snode);
- break;
- case LYS_LIST:
- frr_sr_state_cb_list(elements, xpath, snode);
- break;
- default:
- break;
+ if (nb_oper_data_iterate(xpath, NULL, NB_OPER_DATA_ITER_NORECURSE,
+ frr_sr_state_data_iter_cb, elements)
+ != NB_OK) {
+ flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
+ "%s: failed to obtain operational data [xpath %s]",
+ __func__, xpath);
+ goto exit;
}
+
if (list_isempty(elements))
goto exit;
sr_strerror(ret));
}
-static void frr_sr_subscribe_state(const struct lys_node *snode, void *arg1,
- void *arg2)
+static int frr_sr_subscribe_state(const struct lys_node *snode, void *arg)
{
- struct yang_module *module = arg1;
+ struct yang_module *module = arg;
struct nb_node *nb_node;
int ret;
- if (!(snode->flags & LYS_CONFIG_R))
- return;
+ if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
+ return YANG_ITER_CONTINUE;
/* We only need to subscribe to the root of the state subtrees. */
- if (snode->parent && (snode->parent->flags & LYS_CONFIG_R))
- return;
+ if (snode->parent && CHECK_FLAG(snode->parent->flags, LYS_CONFIG_R))
+ return YANG_ITER_CONTINUE;
nb_node = snode->priv;
if (debug_northbound)
if (ret != SR_ERR_OK)
flog_err(EC_LIB_LIBSYSREPO, "sr_dp_get_items_subscribe(): %s",
sr_strerror(ret));
+
+ return YANG_ITER_CONTINUE;
}
-static void frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg1,
- void *arg2)
+static int frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg)
{
- struct yang_module *module = arg1;
+ struct yang_module *module = arg;
struct nb_node *nb_node;
int ret;
if (snode->nodetype != LYS_RPC)
- return;
+ return YANG_ITER_CONTINUE;
nb_node = snode->priv;
if (debug_northbound)
if (ret != SR_ERR_OK)
flog_err(EC_LIB_LIBSYSREPO, "sr_rpc_subscribe(): %s",
sr_strerror(ret));
+
+ return YANG_ITER_CONTINUE;
}
-static void frr_sr_subscribe_action(const struct lys_node *snode, void *arg1,
- void *arg2)
+static int frr_sr_subscribe_action(const struct lys_node *snode, void *arg)
{
- struct yang_module *module = arg1;
+ struct yang_module *module = arg;
struct nb_node *nb_node;
int ret;
if (snode->nodetype != LYS_ACTION)
- return;
+ return YANG_ITER_CONTINUE;
nb_node = snode->priv;
if (debug_northbound)
if (ret != SR_ERR_OK)
flog_err(EC_LIB_LIBSYSREPO, "sr_action_subscribe(): %s",
sr_strerror(ret));
+
+ return YANG_ITER_CONTINUE;
}
/* FRR's Sysrepo initialization. */
/* Perform subscriptions. */
RB_FOREACH (module, yang_modules, &yang_modules) {
frr_sr_subscribe_config(module);
- yang_module_snodes_iterate(module->info, frr_sr_subscribe_state,
- 0, module, NULL);
- yang_module_snodes_iterate(module->info, frr_sr_subscribe_rpc,
- 0, module, NULL);
- yang_module_snodes_iterate(
- module->info, frr_sr_subscribe_action, 0, module, NULL);
+ yang_snodes_iterate_module(module->info, frr_sr_subscribe_state,
+ 0, module);
+ yang_snodes_iterate_module(module->info, frr_sr_subscribe_rpc,
+ 0, module);
+ yang_snodes_iterate_module(module->info,
+ frr_sr_subscribe_action, 0, module);
}
hook_register(nb_notification_send, frr_sr_notification_send);