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

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 first.

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



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


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

_______________________________________________
netmod mailing list
[email protected]
https://www.ietf.org/mailman/listinfo/netmod

Reply via email to