CESNET / libyang

YANG data modeling language library
BSD 3-Clause "New" or "Revised" License
367 stars 292 forks source link

How to validate a node based on the xpath? #2117

Open amitnagraj opened 12 months ago

amitnagraj commented 12 months ago

Hi,

I'm using Libyang version 2.1.4

The example data tree i'm working on:

module: examples
  +--rw cont
     +--rw l?       string
     +--rw config
        +--rw list1* [key1]
           +--rw key1    string
           +--rw item1?   string
           +--rw item2?   string

I'm trying to validate a node in list regardless of the list instance. For examples, /examples:cont/config/list1/item1 must return true only for the item1 nodes in all the list1 instances.

I tried lyd_eval_xpath() but it is returning true for all the nodes in the hierarchy if the data is present in the same.

Is there any other API to validate the same?

jktjkt commented 12 months ago

What does it mean that you're trying to "validate a node" in the first place? A YANG model defines some rules which, together, define cases where a snapshot of some YANG data are "valid", or not. This validation always works on a module (or even datastore) level, though; it's perfectly OK to have, e.g., must constraints which are defined in a subtree and check/access some completely different subtree -- or even a different module.

I don't understand how this definition of "validity" maps to what you're describing. Are you looking for some XPath mapping by any chance? What's the use case, exactly, and what code have you tried so far?

amitnagraj commented 12 months ago

In simple, I want to check the presence of a leaf node regardless of list instances in a specific data tree that is defined by me.

Code:

void print_info_r(struct lyd_node *root) {

        struct lyd_node *child, *next;
        ly_bool match;

        printf("Node Name : %s\n", root->schema->name);

        lyd_eval_xpath(root, "/examples:cont/config/list1/item1", &match);
        if(match)
                printf("Data Matched Successfully!!!!\n\n");

        LY_LIST_FOR_SAFE(lyd_child(root), next, child) {
                        print_info_r(child);
        }
}

int main() {
        struct lyd_node *data = NULL, *root = NULL;
        const struct ly_ctx *ly_ctx;
        sr_conn_ctx_t *connection = NULL;
        sr_session_ctx_t *session = NULL;
        int rc;
        const char *err;

    /* turn logging on */
    //sr_log_stderr(SR_LL_DBG);

    /* connect to sysrepo */
    rc = sr_connect(0, &connection);
    if (rc != SR_ERR_OK) {
        goto cleanup;
    }

    /* start session in operational datastore */
    rc = sr_session_start(connection, SR_DS_RUNNING, &session);
    if (rc != SR_ERR_OK) {
        goto cleanup;
    }

        ly_ctx = sr_acquire_context(sr_session_get_connection(session));

        rc = lyd_parse_data_path(ly_ctx, "data.json", LYD_JSON, LYD_PARSE_SUBTREE, LYD_VALIDATE_PRESENT, &data);
        if(rc != LY_SUCCESS) {
        err = ly_errmsg(ly_ctx);
        printf("ERROR : %s\n", err);
        goto cleanup;
    }

        sr_release_context(sr_session_get_connection(session));

        LY_LIST_FOR(data, root) {
                print_info_r(root);
        }
cleanup:
        if(data)
                lyd_free_all(data);
        sr_disconnect(connection);
        return rc ? EXIT_FAILURE : EXIT_SUCCESS;
}

data.json

  "examples:cont": {
    "config": {
      "list1": [
        {
          "key1": "inst1",
          "item1": "val1",
          "item2": "val2"
        },
        {
          "key1": "inst2",
          "item1": "val3",
          "item2": "val4"
        }
      ]
    }
  }

Output i m getting:

Node Name : cont
Data Matched Successfully!!!!

Node Name : config
Data Matched Successfully!!!!

Node Name : list1
Data Matched Successfully!!!!

Node Name : key1
Data Matched Successfully!!!!

Node Name : item1
Data Matched Successfully!!!!

Node Name : item2
Data Matched Successfully!!!!

Node Name : list1
Data Matched Successfully!!!!

Node Name : key1
Data Matched Successfully!!!!

Node Name : item1
Data Matched Successfully!!!!

Node Name : item2
Data Matched Successfully!!!!

Output I'm expecting:

Node Name : cont
Node Name : config
Node Name : list1
Node Name : key1
Node Name : item1
Data Matched Successfully!!!!

Node Name : item2
Node Name : list1
Node Name : key1
Node Name : item1
Data Matched Successfully!!!!

Node Name : item2

Thanks,

jktjkt commented 12 months ago

Ah, so this is actually unrelated to validation. You want to check, in your application code, whether there's any instance of a particular leaf within a list. I'll let others answer if there's some efficient way, but you can surely do that by iterating the whole subtree below the /examples:cont/config/list1 and checking for a child node with that name. If there's some helper for this, that helper would also need to perform this iteration, so I guess it won't be any more efficient, but Michal will know better for sure.

Also, the code releases the ly_ctx that is obtained from sysrepo before you've finished working with the libyang data. That's a bug; you can only release the context after you've stopped working with the lyd_node that were obtained through that context. It's probably "masked" in the current code because you're keeping the sysrepo session alive, but it's something to fix anyway.

michalvasko commented 12 months ago

In your code you are using the same XPath for every lyd_eval_xpath() call so no wonder the result is always the same. I am still not certain what exactly you are trying to achieve but I would say the code is way too complicated. Note that if using an absolute XPath (starting with /), the context node does not matter as long as it is from the same data tree. You can use a relative XPath to limit the evaluation to the descendants of the context node. But, I think you can avoid any loops if you just write an XPath that will select the exact nodes you want to find but I cannot help you with that if I do not know what those nodes are supposed to be.