Subject: [netmod] leafref to multi-key list: can only use current() in 2nd leafref

From: "Sterne, Jason (Nokia - CA/Ottawa)" <[email-masked]>
Date: Thu, 20 May 2021 16:19:54 +0000

Hi all,

In any examples I've seen where a YANG model contains a set of leafrefs to a multi-key list, or to a list key within a list, the "current()" xpath function is only in the 2nd leafref and never the first.

EXAMPLE A - OpenConfig  ACL model

      list acl-set {
        key "name type";

... snip ...

  grouping interface-ingress-acl-config {
    description
      "Configuration data for per-interface ingress ACLs";

    leaf set-name {
      type leafref {
        path "../../../../../../acl-sets/acl-set/config/name";
      }
      description
        "Reference to the ACL set name applied on ingress";
    }

    leaf type {
      type leafref {
        path "../../../../../../acl-sets/acl-set[name=current()/../set-name]" +
          "/config/type";
      }
      description
        "Reference to the ACL set type applied on ingress";
    }
  }

We've tried this type of 2-leaf leafref in some models, and yangLint complains (validating instance data) if we instead changed the first one to the following (this is just an illustrative example, we actually did a similar thing with our own models, not actually with this OpenCOnfig model):

    leaf set-name {
      type leafref {
        path "../../../../../../acl-sets/acl-set[type=current()/../type]/config/name";

      }
      description
        "Reference to the ACL set name applied on ingress";
    }

I'm not sure I understand why this wouldn't work. When all is said and done, both leafrefs would be satisfied if this is applied atomically. But maybe it is some sort of circular chicken-and-egg problem in resolving the value spaces ?

I suspect I'm missing some basic understanding here since:
- Other examples of 2-part leafrefs always only use current() in the 2nd leafref, and
- yangLint complains about this (with instance data)

Rgds,
Jason

---

From: Jan Lindblad <[email-masked]>
Date: Fri, 21 May 2021 09:56:31 +0200

Hi Jason,

> In any examples I've seen where a YANG model contains a set of leafrefs to a multi-key list, or to a list key within a list, the "current()" xpath function is only in the 2nd leafref and never the…

Yes. When you want to model a reference to a particular list instance somewhere, you need to use one leafref for each key in the path from the referrer to the referenced instance. But that is not enough. From the second leafref on, you also need to bind the keys together, so that the leafrefs identify a single instance.

Let me give you an example (shortened, stolen from Network Programmability with YANG, chapter 3). Let's say we have a list of books keyed by title and delivery format:

list book {
  key "title format";
  leaf title { type string; }
  leaf format { type bookformat; }
}

And the following three instances exist:

TITLE                                   FORMAT
--------------------------------------------------------------
The Hitchhiker’s Guide to the Galaxy    paperback
The Hitchhiker’s Guide to the Galaxy    mp3
The Neverending Story                   paperback

A naïve refererrer might use:

grouping book-reference {
  leaf title { 
    type leafref {
      path "/book/title";
    }
  }
  leaf format { 
    type leafref {
      path "/book/format";
    }
  }
}
  
This modeling is not very good because this allows the operator to fill in a reference to "The Neverending Story" + "mp3". Both leafref values are valid according to the YANG, they both exist in that list. They just don't identify any single instance.

This is why the leafrefs have to be bound together, so that they only allow values among the instances pointed out by the previous/other leafrefs. For example like this:

grouping book-reference {
  leaf title { 
    type leafref {
      path "/book/title";
    }
  }
  leaf format { 
    type leafref {
      path "/book[title=current()/../title]/format";
    }
  }
}

This makes "format" only able to take on values that exist for the books pointed out by title. In the case of "The Neverending Story", that would only be "paperback". For "The Hitchhiker’s Guide to the Galaxy", that would be "paperback" or "mp3". 

If you had a list with three keys, the binding would get a bit longer:

grouping basic-mylist-reference {
  leaf ref1 { 
    type leafref {
      path "../../mylist/key1";
    }
  }
  leaf ref2 { 
    type leafref {
      path "../../mylist[key1=current()/../ref1]/key2";
    }
  }
  leaf ref3 { 
    type leafref {
      path "../../mylist[key1=current()/../ref1][key2=current()/../ref2]/key3";
    }
  }
}

From a logical validity point of view, you could of course add those predicates (filters) to every leafref, like this:

grouping not-optimal-mylist-reference {
  leaf ref1 { 
    type leafref {
      path "../../mylist[key2=current()/../ref2][key3=current()/../ref3]/key1";
    }
  }
  leaf ref2 { 
    type leafref {
      path "../../mylist[key1=current()/../ref1][key3=current()/../ref3]/key2";
    }
  }
  leaf ref3 { 
    type leafref {
      path "../../mylist[key1=current()/../ref1][key2=current()/../ref2]/key3";
    }
  }
}

I would advice against this, however. You would gain some symmetry and verbosity, but on the bottom line it would cost you two things:
- Performace. If the YANG validator in question is implemented using an XPath evaluator, you have just doubled the validation work for this part of the model.
- Step by step guidance. If the validity of each of the values depend on all the others, it's hard for a system to convey to the operator what the valid values are for each ref leaf. Using the basic-mylist-reference above (or modern-mylist-reference below), systems could allow ref1 values to be tab-completed or presented in a drop down. Once filled in, the same could be done with ref2, and then ref3. Very convenient for the operator. That flow is lost with the non-optimal-mylist-reference.

A more modern way of binding the keys together is to use the YANG 1.1 XPath function deref(). It reduces the verbosity and sharpens the modelers intent a bit. Using deref, you would get this:

grouping modern-mylist-reference {
  leaf ref1 { 
    type leafref {
      path "../../mylist/key1";
    }
  }
  leaf ref2 { 
    type leafref {
      path "deref(../ref1)/../key2";
    }
  }
  leaf ref3 { 
    type leafref {
      path "deref(../ref2)/../key3";
    }
  }
}

Readable, efficient and with a good flow if you ask me.

You did not share the details around the issues with yanglint instance data validation, so it's hard to say what went wrong there.

Best Regards,
/jan

> … snip …
> _______________________________________________
> netmod mailing list
> [email-masked] <mailto:[email-masked]>
> https://www.ietf.org/mailman/listinfo/netmod <https://www.ietf.org/mailman/listinfo/netmod>

---

From: "Sterne, Jason (Nokia - CA/Ottawa)" <[email-masked]>
Date: Fri, 21 May 2021 20:57:37 +0000

Thanks for the excellent detailed response Jan.

I see what you are saying. I was familiar with the first part (the need for the "current()" in the predicates to bind things together), but unsure whether predicates in *all* the leafrefs was somehow invalid (I wasn't seeing that anywhere in other models).

We're taking a closer look back at our specific problem now that we know this is valid. It looks like it may be something else on our side (and yangLint was correct to complain).

Jason

From: Jan Lindblad <[email-masked]>
Sent: Friday, May 21, 2021 3:57 AM
To: Sterne, Jason (Nokia - CA/Ottawa) <[email-masked]>
Cc: [email-masked]
Subject: Re: [netmod] leafref to multi-key list: can only use current() in 2nd leafref

Hi Jason,

list book {
  key "title format";

TITLE                                   FORMAT

Best Regards,
/jan

      list acl-set {
        key "name type";

… snip …

    leaf set-name {
      type leafref {

    leaf type {
      type leafref {

    leaf set-name {
      type leafref {

      }
      description

Rgds,
Jason

_______________________________________________
netmod mailing list
[email-masked]<mailto:[email-masked]>
https://www.ietf.org/mailman/listinfo/netmod

---

From: Kent Watsen <[email-masked]>
Date: Mon, 24 May 2021 12:10:15 +0000

> Thanks for the excellent detailed response Jan.

+1