Skip to content

Question: should implicit nodes alone make a module "present" for LYD_VALIDATE_PRESENT? #2611

Open

Description

@lamestllama

libyang version: devel at fab861b (project version 6.4.19). The same output is produced by 6.4.13 (f7e0cc1).

libyang 2 and later deliberately create non-presence containers as part of the accessible tree, so this is asked as a question about the intended use of LYD_VALIDATE_PRESENT, not reported as a bug.

Setup

p.yang, a module with a default inside a non-presence container and a mandatory leaf:

module p {
  yang-version 1.1;
  namespace "urn:example:p";
  prefix p;

  container np {
    leaf t {
      type uint8;
      default 7;
    }
  }
  leaf m {
    type string;
    mandatory true;
  }
}

q.yang, which refers to that default:

module q {
  yang-version 1.1;
  namespace "urn:example:q";
  prefix q;

  import p {
    prefix p;
  }

  leaf ref {
    type leafref {
      path "/p:np/p:t";
    }
  }
}

data.xml contains data of q only:

<ref xmlns="urn:example:q">7</ref>

Observations

With LYD_VALIDATE_PRESENT, the defaults of p are not created, so the reference fails:

$ yanglint -e p.yang q.yang data.xml
libyang err : Invalid leafref value "7" - no target instance "/p:np/p:t" with the same value. (/q:ref)
YANGLINT[E]: Failed to parse input data file "data.xml".

Creating the implicit nodes of all modules first makes the reference resolvable, but the implicit container /p:np then makes p count as present, and its mandatory leaf is required:

#include <stdio.h>
#include <libyang/libyang.h>

static struct lyd_node *
parse_with_defaults(struct ly_ctx *ctx)
{
    struct lyd_node *tree = NULL;

    lyd_parse_data_path(ctx, "data.xml", LYD_XML, LYD_PARSE_ONLY, 0, &tree);
    lyd_new_implicit_all(&tree, ctx, LYD_IMPLICIT_NO_STATE, NULL);
    return tree;
}

int main(void)
{
    struct ly_ctx *ctx;
    struct lyd_node *tree;
    LY_ERR rc;

    ly_ctx_new(ly_yang_module_dir(), 0, &ctx);
    ly_ctx_set_searchdir(ctx, ".");
    ly_ctx_load_module(ctx, "q", NULL, NULL);

    tree = parse_with_defaults(ctx);
    rc = lyd_validate_all(&tree, ctx, LYD_VALIDATE_NO_STATE | LYD_VALIDATE_PRESENT, NULL);
    printf("lyd_validate_all(LYD_VALIDATE_PRESENT): %d\n", rc);
    lyd_free_all(tree);

    tree = parse_with_defaults(ctx);
    rc = lyd_validate_module(&tree, ly_ctx_get_module_implemented(ctx, "q"), LYD_VALIDATE_NO_STATE, NULL);
    printf("lyd_validate_module(q): %d\n", rc);
    lyd_free_all(tree);

    ly_ctx_destroy(ctx);
    return 0;
}
$ cc present.c -lyang -o present && ./present
libyang[0]: Mandatory node "m" instance does not exist. (schemadata path: /p:m)
lyd_validate_all(LYD_VALIDATE_PRESENT): 7
lyd_validate_module(q): 0

lyd_data_next_module() counts a module as present when it owns any top-level node, including a node with LYD_DEFAULT set.

Questions

  1. Is it intended that a module whose only top-level data are implicit nodes (a non-presence container or a default created by lyd_new_implicit_all()) counts as present for LYD_VALIDATE_PRESENT? The flag is documented as "Validate only modules whose data actually exist", and yanglint -e as "Validate only with the schema modules whose data actually exist in the provided input data files".
  2. If it is intended, is calling lyd_validate_module() once for each module that owns written top-level data the recommended way to let references see the defaults of other modules (RFC 7950, section 6.4.1: "In the accessible tree, all leafs and leaf-lists with default values in use exist") without enforcing the mandatory nodes of modules that have no data?

Why it matters

A configuration of one module that refers to another module's default currently has to choose between a failed reference (LYD_VALIDATE_PRESENT alone) and constraints of an unrelated module (lyd_new_implicit_all() first).

Related

Activity

  1. michalvasko commented on Nov 5, 2026

    @michalvasko
    Member
    1. Yes, it is intended. The idea of using LYD_VALIDATE_PRESENT is to work with a subset of modules and if you have even implicit data of a module, it means you have worked with it in some way.
    2. Your use-case is unsupported (validating some haphazard subset of restrictions) so if what you suggested works for you, you are fine to use it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    is:questionIssue is actually a question.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions