Network Automation ramblings by Kristian Larsson
30 Oct 2017

YANG validation in the real world

Some time ago I wrote an [introductory post on how to validate data using YANG](/validating-data-with-YANG). A simple example as it were, it can be difficult to apply to the real world as there are some blanks to fill in. This time around we'll follow up and use the same tools to validate if the NETCONF / YANG interface of a Huawei router is sound and adheres to standards.

Evaluating whether NETCONF / YANG interfaces are RFC compliant is something I do on a rather frequent basis. Unfortunately I can't share all the YANG models or our configuration as both are secret or private in one way or another. I could perhaps have censored but that would likely have required much more time than I was willing to spend on this post.

The Huawei router is running a software build that is compiled for us (TeraStream) so you can't currently download or get your hands on this (unless you ask Huawei nicely to compile one for you too, I guess).

I got the YANG models separately from Huawei;

kll@lingloi:~/vrp-netconf/yang$ ls
huawei-aaa-action.yang
huawei-aaa-lam-action.yang
huawei-aaa-lam-type.yang
huawei-aaa-lam.yang
huawei-aaa-type.yang
huawei-aaa.yang
huawei-acl-action.yang
huawei-acl-type.yang
huawei-acl.yang
huawei-bfd-action.yang
huawei-bfd-type.yang
huawei-bfd.yang
huawei-bgp-action.yang
huawei-bgp-bgpcomm-action.yang
huawei-bgp-bgpcomm.yang
huawei-bgp-bgpmultiinstcomm.yang
huawei-bgp-type.yang
huawei-bgp.yang
huawei-dcn-action.yang
huawei-dcn-dscpremark.yang
huawei-dcn-type.yang
huawei-dcn.yang
huawei-devm-action.yang
huawei-devm-type.yang
huawei-devm.yang
huawei-dgmp-type.yang
huawei-dgmp.yang
huawei-dhcpv6-action.yang
huawei-dhcpv6-relay-action.yang
huawei-dhcpv6-relay-type.yang
huawei-dhcpv6-relay.yang
huawei-dhcpv6-server-action.yang
huawei-dhcpv6-server-type.yang
huawei-dhcpv6-server.yang
huawei-dhcpv6-type.yang
huawei-dhcpv6.yang
huawei-dns-action.yang
huawei-dns-type.yang
huawei-dns.yang
huawei-ethernet-action.yang
huawei-ethernet-stacking.yang
huawei-ethernet-type.yang
huawei-ethernet.yang
huawei-extension.yang
huawei-hwtacacs-action.yang
huawei-hwtacacs-type.yang
huawei-hwtacacs.yang
huawei-ifm-action.yang
huawei-ifmatm-type.yang
huawei-ifmatm.yang
huawei-ifmbundle-type.yang
huawei-ifmbundle.yang
huawei-ifmcpostrunk-type.yang
huawei-ifmcpostrunk.yang
huawei-ifm-flowalarm.yang
huawei-ifm-fr-type.yang
huawei-ifm-fr.yang
huawei-ifm-hdlc-type.yang
huawei-ifm-hdlc.yang
huawei-ifmima-type.yang
huawei-ifmima.yang
huawei-ifmlag-action.yang
huawei-ifmlag-type.yang
huawei-ifmlag.yang
huawei-ifmmp-type.yang
huawei-ifmmp.yang
huawei-ifmpostrunk-type.yang
huawei-ifmpostrunk.yang
huawei-ifm-pppbase-type.yang
huawei-ifm-pppbase.yang
huawei-ifmtrunk-action.yang
huawei-ifmtrunk-type.yang
huawei-ifmtrunk.yang
huawei-ifm-type.yang
huawei-ifm.yang
huawei-ipsec-action.yang
huawei-ipsec-ike-action.yang
huawei-ipsec-ike-type.yang
huawei-ipsec-ike.yang
huawei-ipsec-type.yang
huawei-ipsec.yang
huawei-isiscomm-action.yang
huawei-isiscomm-type.yang
huawei-isiscomm.yang
huawei-l2tpv3-action.yang
huawei-l2tpv3-type.yang
huawei-l2tpv3.yang
huawei-l3vpn-action.yang
huawei-l3vpn-l3vpncomm-type.yang
huawei-l3vpn-l3vpncomm.yang
huawei-l3vpn-mpls-type.yang
huawei-l3vpn-mpls.yang
huawei-l3vpn-netslice.yang
huawei-l3vpn-qos-action.yang
huawei-l3vpn-qos-type.yang
huawei-l3vpn-qos.yang
huawei-l3vpn-staticfrr.yang
huawei-l3vpn-tnl-type.yang
huawei-l3vpn-tnl.yang
huawei-l3vpn-type.yang
huawei-l3vpn.yang
huawei-mcastbase-type.yang
huawei-mcastbase.yang
huawei-nd-action.yang
huawei-nd-type.yang
huawei-nd.yang
huawei-netconf-authorization-type.yang
huawei-netconf-authorization.yang
huawei-netconf-notification-type.yang
huawei-netconf-notification.yang
huawei-netconf-type.yang
huawei-netconf.yang
huawei-ntp-type.yang
huawei-ntp.yang
huawei-pim-pimafspro-type.yang
huawei-pim-pimafspro.yang
huawei-pim-type.yang
huawei-pim.yang
huawei-pub-type.yang
huawei-qos-action.yang
huawei-qos-cbqos-type.yang
huawei-qos-cbqos.yang
huawei-qos-hqos-type.yang
huawei-qos-hqos.yang
huawei-qos-type.yang
huawei-qos-vllpipe.yang
huawei-qos-wred.yang
huawei-qos.yang
huawei-rm-action.yang
huawei-rm-l3vpn-labelstack.yang
huawei-rm-rmbase-type.yang
huawei-rm-rmbase.yang
huawei-rm.yang
huawei-rsa-type.yang
huawei-rsa.yang
huawei-rtp-action.yang
huawei-rtp-type.yang
huawei-rtp.yang
huawei-snmp-action.yang
huawei-snmp-type.yang
huawei-snmp.yang
huawei-sshc-action.yang
huawei-sshc-type.yang
huawei-sshc.yang
huawei-sshs-action.yang
huawei-sshs-type.yang
huawei-sshs.yang
huawei-staticrt-staticmrt-type.yang
huawei-staticrt-staticmrt.yang
huawei-staticrt-staticrtbase-type.yang
huawei-staticrt-staticrtbase.yang
huawei-staticrt.yang
huawei-syslog-action.yang
huawei-syslog-type.yang
huawei-syslog.yang
huawei-system-action.yang
huawei-system-type.yang
huawei-system.yang
huawei-timerange-type.yang
huawei-timerange.yang
huawei-tty-type.yang
huawei-tty.yang
huawei-vlan-action.yang
huawei-vlan-type.yang
huawei-vlan.yang
huawei-vty-action.yang
huawei-vty-type.yang
huawei-vty.yang
huawei-wdm-type.yang
huawei-wdm.yang
huawei-y1731-action.yang
huawei-y1731-dtools-action.yang
huawei-y1731-dtools-type.yang
huawei-y1731-dtools.yang
huawei-y1731-type.yang
huawei-y1731.yang
ietf-inet-types.yang
ietf-yang-types.yang

Quite a few models!

Before this point we've had numerous issues with the NETCONF server but most of them seem to have been resolved so that we can get the config using NETCONFs get-config RPC. We use netconf-console to do this but you can use whatever NETCONF client you might have handy, like ncclient (which is kinda a personal favourite of mine).

kll@lingloi:~/vrp-netconf$ netconf-console -u test -p secr3tpassw0rd --proto ssh --port 830 --host my-vrp-lab-router --get-config > config-from-netconf
kll@lingloi:~/vrp-netconf$ wc -l config-from-netconf
205780 config-from-netconf

Gulp. That's a lot. The config on the router isn't very large at all so something seems off.

Just reading the file I find 141523 lines from the huawei-fib model. It starts with:

  <fib xmlns="http://www.huawei.com/netconf/vrp/huawei-fib">
    <uniAfs>
      <uniAf>
        <fibRoutes>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
        </fibRoutes>
        <fibStatisticss>
          <fibStatistics>
          </fibStatistics>
        </fibStatisticss>
      </uniAf>
      <uniAf>
        <fibRoutes>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
        </fibRoutes>
        <fibStatisticss>
          <fibStatistics>
          </fibStatistics>
        </fibStatisticss>
      </uniAf>
      <uniAf>
        <fibRoutes>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
          <fibRoute>
          </fibRoute>
...

and continues like that. This is clearly some bug. We are seeing a long list of entries but there is no data populated in each entry. We don't have 141k routes configured on this router (more like 1 static) and so I suspect that I'm getting back operational data, despite only asking for config data with get-config. This has happened with Huawei before so I find it entirely possible it is happening again.

If we ignore that though we can see if we can validate the rest of the data using the same principles as in the previous post. Using yang2dsdl, that is:

kll@lingloi:~/vrp-netconf/yang$ yang2dsdl -v ../config-from-netconf *.yang
huawei-pub-type.yang:75: warning: the escape sequence "\." is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:75: warning: the escape sequence "\." is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:146: warning: the escape sequence "\d" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:146: warning: the escape sequence "\d" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:271: warning: the escape sequence "\s" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:271: warning: the escape sequence "\s" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:434: warning: the escape sequence "\s" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:434: warning: the escape sequence "\-" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:434: warning: the escape sequence "\." is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:434: warning: the escape sequence "\(" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:434: warning: the escape sequence "\)" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:434: warning: the escape sequence "\s" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:434: warning: the escape sequence "\-" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:434: warning: the escape sequence "\." is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:434: warning: the escape sequence "\(" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:434: warning: the escape sequence "\)" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:449: warning: the escape sequence "\d" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:449: warning: the escape sequence "\s" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:449: warning: the escape sequence "\d" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:449: warning: the escape sequence "\s" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:464: warning: the escape sequence "\d" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:464: warning: the escape sequence "\d" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:472: warning: the escape sequence "\d" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
huawei-pub-type.yang:472: warning: the escape sequence "\d" is unsafe in double quoted strings - pass the flag --lax-quote-checks to avoid this warning
Cannot translate submodules
kll@lingloi:~/vrp-netconf/yang$

Okay, a bunch of warnings and then an error at the end. I don't like seeing warnings (sometimes they later lead to errors) so let's start with those. Line 75 of huawei-pub-type.yang is the pattern line:

typedef ipv4Address {
  type string {
    length "0..255";
    pattern "((([1-9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]))";
  }
  description
    "An IPV4 address in dotted decimal notation";
}

\. is used in the middle to mean a literal .. This is fine but as the warning messages tells us, doing escapes in a double quoted string is not safe. Simply changing the pattern to use single quotes removes the warning and stays true to the intent of the pattern.

With that fixed we are left with the error about submodules which is simply because we are telling yang2dsdl to validate an instance data document using a submodule. That's simply wrong and not valid. The correct thing to do is to validate using the module which naturally includes the submodule, thus we need to filter our submodules. All submodules include the statement belongs-to to point out which module they belong to.

This grep will thus yield all the submodules in the directory (-l displays files with matches but not the matching line itself):

kll@lingloi:~/vrp-netconf/yang$ grep -l belongs-to *.yang

What we are looking for is all modules that are NOT submodules, thus we list everything and then do a inverse grep on that, like this:

kll@lingloi:~/vrp-netconf/yang$ ls *.yang | grep -vf <(grep -l belongs-to *.yang)

grep -f takes a file as input for things to grep after and so we use a bash trick using <() to let the output of a sub-shell look like a file to the current command. The -v is to invert the match. This yields the list of files we want, now we give it to yang2dsdl by using a sub-shell for expansion:

kll@lingloi:~/vrp-netconf/yang$ yang2dsdl -v ../config-from-netconf $(ls *.yang | grep -vf <(grep -l belongs-to *))
/usr/bin/yang2dsdl: 243: /usr/bin/yang2dsdl: xsltproc: not found
== Generating RELAX NG schema './-data.rng'
/usr/bin/yang2dsdl: 76: /usr/bin/yang2dsdl: xsltproc: not found
kll@lingloi:~/vrp-netconf/yang$ xsltproc
The program 'xsltproc' is currently not installed. You can install it by typing:
sudo apt install xsltproc
kll@lingloi:~/vrp-netconf/yang$ sudo apt install xsltproc

Whops! I'm normally validating YANG etc on a computer in our lab but I'm now using the same computer which I'm writing this post on and I'm apparently missing some tools. I'll include it since you are likely to run into the same problem. Just install xsltproc and try again

kll@lingloi:~/vrp-netconf/yang$ yang2dsdl -v ../config-from-netconf $(ls *.yang | grep -vf <(grep -l belongs-to *))
warning: failed to load external entity "/usr/local/share/yang/xslt/basename.xsl"
cannot parse /usr/local/share/yang/xslt/basename.xsl
== Generating RELAX NG schema './-data.rng'
warning: failed to load external entity "schema-dir"
cannot parse schema-dir

My yang2dsdl is looking in /usr/local/share instad of /usr/share. Dunno why. Don't think I saw problem this on my other computer. Anyway, I just copied those files:

kll@lingloi:~/vrp-netconf/yang$ sudo cp -a /usr/share/yang /usr/local/share/yang

And run again:

kll@lingloi:~/vrp-netconf/yang$ yang2dsdl -v ../config-from-netconf $(ls *.yang | grep -vf <(grep -l belongs-to *))
== Generating RELAX NG schema './huawei-aaa_huawei-acl_huawei-bfd_huawei-bgp_huawei-dcn_huawei-devm_huawei-dgmp_huawei-dhcpv6_huawei-dns_huawei-ethernet_huawei-extension_huawei-hwtacacs_huawei-ifmatm_huawei-ifmbundle_huawei-ifmcpostrunk_huawei-ifmima_huawei-ifmlag_huawei-ifmmp_huawei-ifmpostrunk_huawei-ifmtrunk_huawei-ifm_huawei-ipsec_huawei-isiscomm_huawei-l2tpv3_huawei-l3vpn_huawei-mcastbase_huawei-nd_huawei-netconf_huawei-ntp_huawei-pim_huawei-pub-type_huawei-qos_huawei-rm_huawei-rsa_huawei-rtp_huawei-snmp_huawei-sshc_huawei-sshs_huawei-staticrt_huawei-syslog_huawei-system_huawei-timerange_huawei-tty_huawei-vlan_huawei-vty_huawei-wdm_huawei-y1731_ietf-inet-types_ietf-yang-types-data.rng'
I/O error : Filename too long
I/O error : Filename too long
kll@lingloi:~/vrp-netconf/yang$

Blargh. Okay, too many modules which yields too long of a name since yang2dsdl per default concatenates the names of all the modules in its intermediate output (it writes a single large schema file that is then used for validation). We can specify a basename to use with -b:

kll@lingloi:~/vrp-netconf/yang$ yang2dsdl -b hejohoj -v ../config-from-netconf $(ls *.yang | grep -vf <(grep -l belongs-to *))                                                                                                                                                                                                                                                                                                                                                                
== Generating RELAX NG schema './hejohoj-data.rng'
Done.

== Generating Schematron schema './hejohoj-data.sch'
Done.

== Generating DSRL schema './hejohoj-data.dsrl'
Done.

== Validating grammar and datatypes ...
/usr/bin/yang2dsdl: 103: /usr/bin/yang2dsdl: xmllint: not found

Okay, install xmllint too!

kll@lingloi:~/vrp-netconf/yang$ xmllint
The program 'xmllint' is currently not installed. You can install it by typing:
sudo apt install libxml2-utils
kll@lingloi:~/vrp-netconf/yang$ sudo apt-get install -qy libxml2-utils
Reading package lists...
Building dependency tree...
...

And now!

kll@lingloi:~/vrp-netconf/yang$ yang2dsdl -b hejohoj -v ../config-from-netconf $(ls *.yang | grep -vf <(grep -l belongs-to *))
== Generating RELAX NG schema './hejohoj-data.rng'
Done.

== Generating Schematron schema './hejohoj-data.sch'
Done.

== Generating DSRL schema './hejohoj-data.dsrl'
Done.

== Validating grammar and datatypes ...
../config-from-netconf:2: element rpc-reply: Relax-NG validity error : Expecting element data, got rpc-reply
../config-from-netconf fails to validate

Not quite :/ yang2dsdl defaults to assuming it's a "data" file we want to validate but this is the response from a get-config query and so the "data" element is wrapped inside a rpc-reply. We can inform yang2dsdl with -t that it is a get-config-reply:

kll@lingloi:~/vrp-netconf/yang$ yang2dsdl -t get-config-reply -b hejohoj -v ../config-from-netconf $(ls *.yang | grep -vf <(grep -l belongs-to *))                                                                                                                                                                                                                                                                                                                                                      
== Generating RELAX NG schema './hejohoj-get-config-reply.rng'
Done.

== Generating Schematron schema './hejohoj-get-config-reply.sch'
Done.

== Generating DSRL schema './hejohoj-get-config-reply.dsrl'
Done.

== Validating grammar and datatypes ...
../config-from-netconf:1976: element mac: Relax-NG validity error : Element data has extra content: mac
../config-from-netconf fails to validate
kll@lingloi:~/vrp-netconf/yang$

Now we are getting somewhere. We found a data inconsistency on line 1976 which tells us that at least 1975 lines actuallt passed validation! What's on line 1976?

<mac xmlns="http://www.huawei.com/netconf/vrp/huawei-mac">
  <globalAttribute>
    <macAgingTime>300</macAgingTime>
    <macAgeTimeEnable>enable</macAgeTimeEnable>
    <macSynchronize>
      <synenable>false</synenable>
    </macSynchronize>
  </globalAttribute>
  <macUsages>
    <macUsage>
      <slot>0</slot>
      <macThreshold>90</macThreshold>
    </macUsage>
    <macUsage>
      <slot>1</slot>
      <macThreshold>90</macThreshold>
    </macUsage>
    <macUsage>
      <slot>3</slot>
      <macThreshold>90</macThreshold>
    </macUsage>
  </macUsages>
</mac>

Looking at our YANG modules, there is none that define a namespace of huawei-mac:

kll@lingloi:~/vrp-netconf/yang$ grep huawei-mac *
kll@lingloi:~/vrp-netconf/yang$

this isn't wrong per se as NETCONF allows the return of data that we don't have YANG model for and thus can't validate. The NETCONF client should just ignore this data. This might seem strange and first but this is actually how upgrades are handled, i.e. if a client is using an older YANG model or simply doesn't understand all YANG models supported by the device, that is ok, since we just ignore the data. However, the yang2dsdl tool is a little more strict and complains about it. For the sake of progress, I'll remove that part and continue. After this, I actually found eight other namespaces that I did not have YANG models for and thus just removed the corresponding instance data for the sake of progressing with my testing.

Next error we run into is related to the qos config:

kll@lingloi:~/vrp-netconf/yang$ yang2dsdl -t get-config-reply -b hejohoj -v ../config-from-netconf $(ls *.yang | grep -vf <(grep -l belongs-to *))
== Generating RELAX NG schema './hejohoj-get-config-reply.rng'
Done.

== Generating Schematron schema './hejohoj-get-config-reply.sch'
Done.

== Generating DSRL schema './hejohoj-get-config-reply.dsrl'
Done.

== Validating grammar and datatypes ...
Relax-NG validity error : Extra element qos in interleave
../config-from-netconf:5156: element qos: Relax-NG validity error : Element data failed to validate content
../config-from-netconf fails to validate

The validation is achieved by parsing the YANG models and producing a RelaxNG schema which in turn is used to validate the data. This means that at validation we no longer have an understanding of YANG, which I presume (I don't know that much about RelaxNG) leads to a loss of data. Unfortunately this results in an overly sparse error message.

Fortunately I have some experience in reading YANG models and after reading through a bit of the huawei-qos.yang model and its sub-modules I find that instances of non-presence containers containing mandatory leaf nodes. This is quite the anti-pattern of YANG module writing and this isn't the first time I've seen it.

There are two flavours of containers in YANG; presence and non-presence containers. Non-presence containers are the default and these containers do not themselves carry any explicit meaning and are used merely to organise data by providing structure. By adding the presence keyword under a container we can turn it into a presence container which means the existance of the container itself carries meaning.

Let's take a short example:

container foo {
  leaf bar {
    type string;
    mandatory true;
  }
}

With this model, the foo container, which is a non-presence container, MUST exist and there MUST be a bar leaf in it, since mandatory is set. I've seen this pattern in a lot of cases where the intention is to make the leaf bar mandatory but only when the container foo is present as a consequence of enabling the "foo" feature. To achieve that, we can use a presence container, like so:

container foo {
  presence "Enables feature foo";
  leaf bar {
    type string;
    mandatory true;
  }
}

Now the presence of the whole container foo is optional but if it exists then the bar leaf MUST be set (again, due to the mandatory statement).

Huawei's YANG models contain a bunch of places where they have rather deep nesting of containers and finally we find a leaf with mandatory true. When all of this data is missing in the instance data the validation fails with that extremely sparse message. I modified the YANG modules and added in presence statements on a couple of containers to make them optional, again to be able to make progress with my evaluation.

This type of problem is in fact so widespread that I had to find a better way of finding problematic instances. By looking at the output of pyang -f tree and then filtering this I could quickly find mandatory leaf nodes under containers. I started by removing all read-only data. I do this with vi and :g/+--ro /d. Second I can remove all leaves that are optional with :g/+--rw [A-Za-z0-9]\+?/d. We are now down to containers, lists and mandatory leaves. Here's an exceprt from the routing policy model:

module: huawei-rtp
    +--rw rtp
       +--rw asPathFilters
       |  +--rw asPathFilter* [index]
       |     +--rw index                string
       |     +--rw asPathFilterNodes
       |        +--rw asPathFilterNode* [nodeSequence]
       |           +--rw nodeSequence    uint32
       |           +--rw matchMode       rtpMatchMode
       |           +--rw regular         string

We can see how asPathFilters is a container that holds a single list, "index" is the key of that list and nex to it we find another container and in it a second list which wholds the members of the filter. That inner list is keyed on nodeSequence, which is fine and the matchMode and regular seems fine too, I guess the regular is the actual value and it's called "regular" because it's a regular expression. This structure seems fine.

However, if we move on down we get to the route policies themselves:

+--rw routePolicys
|  +--rw routePolicy* [name]
|     +--rw name                string
|     +--rw routePolicyNodes
|        +--rw routePolicyNode* [nodeSequence]
|           +--rw nodeSequence      uint32
|           +--rw matchMode         rtpMatchMode
|           +--rw matchCondition
|           |  +--rw matchCosts
|           |  |  +--rw matchCost
|           |  |     +--rw costValue    uint32
|           |  +--rw matchInterfaces
|           |  |  +--rw matchInterface* [ifName]
|           |  |     +--rw ifName    pub-type:ifName
|           |  +--rw matchRouteTypes
|           |  |  +--rw matchRouteType* [routeType]
|           |  |     +--rw routeType    rtpMchRtType
|           |  +--rw matchTags
|           |  |  +--rw matchTag
|           |  |     +--rw tagValue    uint32
|           |  +--rw matchMplsLabels
|           |  |  +--rw matchMplsLabel
|           |  |     +--rw mplsLabel    boolean
...

the model continues for another 150 lines just for the routePlicy list but I won't list it all here. We can see how there are a bunch of mandatory leaves here and they are tucked into two containers, like matchCosts/matchCost is a container in a container and inside we have the leaf costValue which is mandatory. The way the model is written it means pretty much all potential ways of matching things in the policy are mandatory. That can't be right!

As far as I've understood, Huawei generates their models from an internal representation so while I've found a whole bunch of instances with the same type of error, it doesn't actually mean fixing it is very hard. All they need to do is patch the logic that outputs the YANG model and all faulty occurences can be fixed in one swift go.

I found a couple of other instances of bugs but won't bore you with all the details as they are conceptually the same. I've brought it all up with Huawei who are committed to resolving them and improve the quality of their NETCONF / YANG interface.

I would like to thank Huawei for providing us with early access software and working with us on improving their NETCONF / YANG support as well as for the opportunity to publish this post and show how some of these things work.

Reach out to me on Twitter (see footer) if you have questions!

Tags: YANG

Other posts