]> git.proxmox.com Git - mirror_frr.git/blobdiff - lib/northbound.c
zebra, lib: fix the ZEBRA_INTERFACE_VRF_UPDATE zapi message
[mirror_frr.git] / lib / northbound.c
index f8045b89aa9ef4ddd878807392dd1a50d21acaf7..a7f9c8620ef9e57c5fa1331bca35471e2023bc0a 100644 (file)
@@ -50,6 +50,12 @@ static void nb_transaction_free(struct nb_transaction *transaction);
 static int nb_transaction_process(enum nb_event event,
                                  struct nb_transaction *transaction);
 static void nb_transaction_apply_finish(struct nb_transaction *transaction);
+static int nb_oper_data_iter_node(const struct lys_node *snode,
+                                 const char *xpath, const void *list_entry,
+                                 const struct yang_list_keys *list_keys,
+                                 struct yang_translator *translator,
+                                 bool first, uint32_t flags,
+                                 nb_oper_data_cb cb, void *arg);
 
 static int nb_node_check_config_only(const struct lys_node *snode, void *arg)
 {
@@ -89,6 +95,13 @@ static int nb_node_new_cb(const struct lys_node *snode, void *arg)
                if (config_only)
                        SET_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY);
        }
+       if (CHECK_FLAG(snode->nodetype, LYS_LIST)) {
+               struct lys_node_list *slist;
+
+               slist = (struct lys_node_list *)snode;
+               if (slist->keys_size == 0)
+                       SET_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST);
+       }
 
        /*
         * Link the northbound node and the libyang schema node with one
@@ -706,6 +719,7 @@ static int nb_configuration_callback(const enum nb_event event,
        const struct lyd_node *dnode = change->cb.dnode;
        union nb_resource *resource;
        int ret = NB_ERR;
+       enum lib_log_refs ref;
 
        if (debug_northbound) {
                const char *value = "(none)";
@@ -738,12 +752,36 @@ static int nb_configuration_callback(const enum nb_event event,
                break;
        }
 
-       if (ret != NB_OK)
-               flog_warn(
-                       EC_LIB_NB_CB_CONFIG,
-                       "%s: error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]",
-                       __func__, nb_err_name(ret), nb_event_name(event),
-                       nb_operation_name(operation), xpath);
+       if (ret != NB_OK) {
+               switch (event) {
+               case NB_EV_VALIDATE:
+                       ref = EC_LIB_NB_CB_CONFIG_VALIDATE;
+                       break;
+               case NB_EV_PREPARE:
+                       ref = EC_LIB_NB_CB_CONFIG_PREPARE;
+                       break;
+               case NB_EV_ABORT:
+                       ref = EC_LIB_NB_CB_CONFIG_ABORT;
+                       break;
+               case NB_EV_APPLY:
+                       ref = EC_LIB_NB_CB_CONFIG_APPLY;
+                       break;
+               }
+               if (event == NB_EV_VALIDATE || event == NB_EV_PREPARE)
+                       flog_warn(
+                               ref,
+                               "%s: error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]",
+                               __func__, nb_err_name(ret),
+                               nb_event_name(event),
+                               nb_operation_name(operation), xpath);
+               else
+                       flog_err(
+                               ref,
+                               "%s: error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]",
+                               __func__, nb_err_name(ret),
+                               nb_event_name(event),
+                               nb_operation_name(operation), xpath);
+       }
 
        return ret;
 }
@@ -930,6 +968,344 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction)
        }
 }
 
+static int nb_oper_data_iter_children(const struct lys_node *snode,
+                                     const char *xpath, const void *list_entry,
+                                     const struct yang_list_keys *list_keys,
+                                     struct yang_translator *translator,
+                                     bool first, uint32_t flags,
+                                     nb_oper_data_cb cb, void *arg)
+{
+       struct lys_node *child;
+
+       LY_TREE_FOR (snode->child, child) {
+               int ret;
+
+               ret = nb_oper_data_iter_node(child, xpath, list_entry,
+                                            list_keys, translator, false,
+                                            flags, cb, arg);
+               if (ret != NB_OK)
+                       return ret;
+       }
+
+       return NB_OK;
+}
+
+static int nb_oper_data_iter_leaf(const struct nb_node *nb_node,
+                                 const char *xpath, const void *list_entry,
+                                 const struct yang_list_keys *list_keys,
+                                 struct yang_translator *translator,
+                                 uint32_t flags, nb_oper_data_cb cb, void *arg)
+{
+       struct yang_data *data;
+
+       if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
+               return NB_OK;
+
+       /* Ignore list keys. */
+       if (lys_is_key((struct lys_node_leaf *)nb_node->snode, NULL))
+               return NB_OK;
+
+       data = nb_node->cbs.get_elem(xpath, list_entry);
+       if (data == NULL)
+               /* Leaf of type "empty" is not present. */
+               return NB_OK;
+
+       return (*cb)(nb_node->snode, translator, data, arg);
+}
+
+static int nb_oper_data_iter_container(const struct nb_node *nb_node,
+                                      const char *xpath,
+                                      const void *list_entry,
+                                      const struct yang_list_keys *list_keys,
+                                      struct yang_translator *translator,
+                                      uint32_t flags, nb_oper_data_cb cb,
+                                      void *arg)
+{
+       if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
+               return NB_OK;
+
+       /* Presence containers. */
+       if (nb_node->cbs.get_elem) {
+               struct yang_data *data;
+               int ret;
+
+               data = nb_node->cbs.get_elem(xpath, list_entry);
+               if (data == NULL)
+                       /* Presence container is not present. */
+                       return NB_OK;
+
+               ret = (*cb)(nb_node->snode, translator, data, arg);
+               if (ret != NB_OK)
+                       return ret;
+       }
+
+       /* Iterate over the child nodes. */
+       return nb_oper_data_iter_children(nb_node->snode, xpath, list_entry,
+                                         list_keys, translator, false, flags,
+                                         cb, arg);
+}
+
+static int
+nb_oper_data_iter_leaflist(const struct nb_node *nb_node, const char *xpath,
+                          const void *parent_list_entry,
+                          const struct yang_list_keys *parent_list_keys,
+                          struct yang_translator *translator, uint32_t flags,
+                          nb_oper_data_cb cb, void *arg)
+{
+       const void *list_entry = NULL;
+
+       if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
+               return NB_OK;
+
+       do {
+               struct yang_data *data;
+               int ret;
+
+               list_entry =
+                       nb_node->cbs.get_next(parent_list_entry, list_entry);
+               if (!list_entry)
+                       /* End of the list. */
+                       break;
+
+               data = nb_node->cbs.get_elem(xpath, list_entry);
+               if (data == NULL)
+                       continue;
+
+               ret = (*cb)(nb_node->snode, translator, data, arg);
+               if (ret != NB_OK)
+                       return ret;
+       } while (list_entry);
+
+       return NB_OK;
+}
+
+static int nb_oper_data_iter_list(const struct nb_node *nb_node,
+                                 const char *xpath_list,
+                                 const void *parent_list_entry,
+                                 const struct yang_list_keys *parent_list_keys,
+                                 struct yang_translator *translator,
+                                 uint32_t flags, nb_oper_data_cb cb, void *arg)
+{
+       struct lys_node_list *slist = (struct lys_node_list *)nb_node->snode;
+       const void *list_entry = NULL;
+       uint32_t position = 1;
+
+       if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
+               return NB_OK;
+
+       /* Iterate over all list entries. */
+       do {
+               struct yang_list_keys list_keys;
+               char xpath[XPATH_MAXLEN * 2];
+               int ret;
+
+               /* Obtain list entry. */
+               list_entry =
+                       nb_node->cbs.get_next(parent_list_entry, list_entry);
+               if (!list_entry)
+                       /* End of the list. */
+                       break;
+
+               if (!CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST)) {
+                       /* Obtain the list entry keys. */
+                       if (nb_node->cbs.get_keys(list_entry, &list_keys)
+                           != NB_OK) {
+                               flog_warn(EC_LIB_NB_CB_STATE,
+                                         "%s: failed to get list keys",
+                                         __func__);
+                               return NB_ERR;
+                       }
+
+                       /* Build XPath of the list entry. */
+                       strlcpy(xpath, xpath_list, sizeof(xpath));
+                       for (unsigned int i = 0; i < list_keys.num; i++) {
+                               snprintf(xpath + strlen(xpath),
+                                        sizeof(xpath) - strlen(xpath),
+                                        "[%s='%s']", slist->keys[i]->name,
+                                        list_keys.key[i]);
+                       }
+               } else {
+                       /*
+                        * Keyless list - build XPath using a positional index.
+                        */
+                       snprintf(xpath, sizeof(xpath), "%s[%u]", xpath_list,
+                                position);
+                       position++;
+               }
+
+               /* Iterate over the child nodes. */
+               ret = nb_oper_data_iter_children(
+                       nb_node->snode, xpath, list_entry, &list_keys,
+                       translator, false, flags, cb, arg);
+               if (ret != NB_OK)
+                       return ret;
+       } while (list_entry);
+
+       return NB_OK;
+}
+
+static int nb_oper_data_iter_node(const struct lys_node *snode,
+                                 const char *xpath_parent,
+                                 const void *list_entry,
+                                 const struct yang_list_keys *list_keys,
+                                 struct yang_translator *translator,
+                                 bool first, uint32_t flags,
+                                 nb_oper_data_cb cb, void *arg)
+{
+       struct nb_node *nb_node;
+       char xpath[XPATH_MAXLEN];
+       int ret = NB_OK;
+
+       if (!first && CHECK_FLAG(flags, NB_OPER_DATA_ITER_NORECURSE)
+           && CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST))
+               return NB_OK;
+
+       /* Update XPath. */
+       strlcpy(xpath, xpath_parent, sizeof(xpath));
+       if (!first && snode->nodetype != LYS_USES)
+               snprintf(xpath + strlen(xpath), sizeof(xpath) - strlen(xpath),
+                        "/%s", snode->name);
+
+       nb_node = snode->priv;
+       switch (snode->nodetype) {
+       case LYS_CONTAINER:
+               ret = nb_oper_data_iter_container(nb_node, xpath, list_entry,
+                                                 list_keys, translator, flags,
+                                                 cb, arg);
+               break;
+       case LYS_LEAF:
+               ret = nb_oper_data_iter_leaf(nb_node, xpath, list_entry,
+                                            list_keys, translator, flags, cb,
+                                            arg);
+               break;
+       case LYS_LEAFLIST:
+               ret = nb_oper_data_iter_leaflist(nb_node, xpath, list_entry,
+                                                list_keys, translator, flags,
+                                                cb, arg);
+               break;
+       case LYS_LIST:
+               ret = nb_oper_data_iter_list(nb_node, xpath, list_entry,
+                                            list_keys, translator, flags, cb,
+                                            arg);
+               break;
+       case LYS_USES:
+               ret = nb_oper_data_iter_children(snode, xpath, list_entry,
+                                                list_keys, translator, false,
+                                                flags, cb, arg);
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
+                        uint32_t flags, nb_oper_data_cb cb, void *arg)
+{
+       struct nb_node *nb_node;
+       const void *list_entry = NULL;
+       struct yang_list_keys list_keys;
+       struct list *list_dnodes;
+       struct lyd_node *dnode, *dn;
+       struct listnode *ln;
+       int ret;
+
+       nb_node = nb_node_find(xpath);
+       if (!nb_node) {
+               flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
+                         "%s: unknown data path: %s", __func__, xpath);
+               return NB_ERR;
+       }
+
+       /* For now this function works only with containers and lists. */
+       if (!CHECK_FLAG(nb_node->snode->nodetype, LYS_CONTAINER | LYS_LIST)) {
+               flog_warn(
+                       EC_LIB_NB_OPERATIONAL_DATA,
+                       "%s: can't iterate over YANG leaf or leaf-list [xpath %s]",
+                       __func__, xpath);
+               return NB_ERR;
+       }
+
+       /*
+        * Create a data tree from the XPath so that we can parse the keys of
+        * all YANG lists (if any).
+        */
+       ly_errno = 0;
+       dnode = lyd_new_path(NULL, ly_native_ctx, xpath, NULL, 0,
+                            LYD_PATH_OPT_UPDATE);
+       if (!dnode && ly_errno) {
+               flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed",
+                         __func__);
+               return NB_ERR;
+       }
+       /*
+        * We can remove the following two lines once we depend on
+        * libyang-v0.16-r2, which has the LYD_PATH_OPT_NOPARENTRET flag for
+        * lyd_new_path().
+        */
+       dnode = yang_dnode_get(dnode, xpath);
+       assert(dnode);
+
+       /*
+        * Create a linked list to sort the data nodes starting from the root.
+        */
+       list_dnodes = list_new();
+       for (dn = dnode; dn; dn = dn->parent) {
+               if (dn->schema->nodetype != LYS_LIST || !dn->child)
+                       continue;
+               listnode_add_head(list_dnodes, dn);
+       }
+       /*
+        * Use the northbound callbacks to find list entry pointer corresponding
+        * to the given XPath.
+        */
+       for (ALL_LIST_ELEMENTS_RO(list_dnodes, ln, dn)) {
+               struct lyd_node *child;
+               struct nb_node *nn;
+               unsigned int n = 0;
+
+               /* Obtain the list entry keys. */
+               memset(&list_keys, 0, sizeof(list_keys));
+               LY_TREE_FOR (dn->child, child) {
+                       if (!lys_is_key((struct lys_node_leaf *)child->schema,
+                                       NULL))
+                               continue;
+                       strlcpy(list_keys.key[n],
+                               yang_dnode_get_string(child, NULL),
+                               sizeof(list_keys.key[n]));
+                       n++;
+               }
+               list_keys.num = n;
+               assert(list_keys.num
+                      == ((struct lys_node_list *)dn->schema)->keys_size);
+
+               /* Find the list entry pointer. */
+               nn = dn->schema->priv;
+               list_entry = nn->cbs.lookup_entry(list_entry, &list_keys);
+               if (list_entry == NULL) {
+                       list_delete(&list_dnodes);
+                       yang_dnode_free(dnode);
+                       return NB_ERR_NOT_FOUND;
+               }
+       }
+
+       /* If a list entry was given, iterate over that list entry only. */
+       if (dnode->schema->nodetype == LYS_LIST && dnode->child)
+               ret = nb_oper_data_iter_children(
+                       nb_node->snode, xpath, list_entry, &list_keys,
+                       translator, true, flags, cb, arg);
+       else
+               ret = nb_oper_data_iter_node(nb_node->snode, xpath, list_entry,
+                                            &list_keys, translator, true,
+                                            flags, cb, arg);
+
+       list_delete(&list_dnodes);
+       yang_dnode_free(dnode);
+
+       return ret;
+}
+
 bool nb_operation_is_valid(enum nb_operation operation,
                           const struct lys_node *snode)
 {
@@ -1038,6 +1414,7 @@ bool nb_operation_is_valid(enum nb_operation operation,
 
                switch (snode->nodetype) {
                case LYS_LEAF:
+               case LYS_LEAFLIST:
                        break;
                case LYS_CONTAINER:
                        scontainer = (struct lys_node_container *)snode;
@@ -1049,12 +1426,27 @@ bool nb_operation_is_valid(enum nb_operation operation,
                }
                return true;
        case NB_OP_GET_NEXT:
+               switch (snode->nodetype) {
+               case LYS_LIST:
+                       if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
+                               return false;
+                       break;
+               case LYS_LEAFLIST:
+                       if (CHECK_FLAG(snode->flags, LYS_CONFIG_W))
+                               return false;
+                       break;
+               default:
+                       return false;
+               }
+               return true;
        case NB_OP_GET_KEYS:
        case NB_OP_LOOKUP_ENTRY:
                switch (snode->nodetype) {
                case LYS_LIST:
                        if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
                                return false;
+                       if (CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST))
+                               return false;
                        break;
                default:
                        return false;
@@ -1194,7 +1586,8 @@ static void nb_load_callbacks(const struct frr_yang_module_info *module)
        }
 }
 
-void nb_init(const struct frr_yang_module_info *modules[], size_t nmodules)
+void nb_init(struct thread_master *tm,
+            const struct frr_yang_module_info *modules[], size_t nmodules)
 {
        unsigned int errors = 0;
 
@@ -1229,7 +1622,7 @@ void nb_init(const struct frr_yang_module_info *modules[], size_t nmodules)
        running_config = nb_config_new(NULL);
 
        /* Initialize the northbound CLI. */
-       nb_cli_init();
+       nb_cli_init(tm);
 }
 
 void nb_terminate(void)