mandatory:YANG:mandatory语法(mandatory true)和mandatory节点(mandatory node)

*专注NETCONF/YANG技术科普,接受任何问题,包括我答不上来的问题。欢迎光临!欢迎提问:链接
这篇帖子写了两天,第一天我家人共用电脑的时候不知道帮我关机没保存草稿。只好第二天重写。个人认为这篇应该算是干货满满,写到后面自己都有点累了,举例讲解都开始从简。有点虎头蛇尾。
*

一、前言

mandatory在英文中有“强制性、强制、必须”的含义。
YANG语法中有个mandatory属性;而YANG的术语里面有个mandatory节点(英文叫mandatory node)。本来YANG语法的mandatory属性就已经不容易理解了。这里再跟mandatory节点混在一起,今天先聊个重磅、易错的mandatory。

本文先讲解YANG语法的mandatory属性;再讲mandatory节点;最后引出 presence container。presence container本文不做详细讲解,下次有空讲。

二、mandatory属性

mandatory是YANG1.0语法的一个属性,有两个取值:false/true。默认是false,也就是说,yang节点里面如果没有mandatory这个属性,说明这个节点就是可选的(mandatory false等价于没有mandatory属性)。如果这个节点下有写mandatory true,则说明当数据库中父节点存在时,这个节点是必须存在的。
叶子节点leaf、choice节点、anydata、anyxml节点都可以设置mandatory属性。(——这里可以思考一下:为什么list、leaflist、container节点上不支持设置mandatory属性呢?)
以下使用leaf的mandatory属性来进行举例讲解。

2.1 【重点】mandatory true的含义

前面说到mandatory true表示:数据库中,当在父节点存在的时候,本节点必须存在。这里有一个传递的思想在里面了。
举例:
下面的模型中均为普通container(又称NP-container),在YANG模型中普通container是认为默认存在的。
leaf iftype是必须存在,其前提是container basic必须存在;container basic是普通container,默认存在,往上一层看container interface;container interface是普通container,则container interface也默认存在。
这就是说:如果此YANG模块一加载,空配的时候(数据库为空DB)的时候就要有/interface/basic/iftype这个leaf必须存在咱还不知道填啥。这不是自相矛盾吗?所以这个YANG模型是不合理的。

container interface { //模块根节点 container basic { leaf name { type string;}leaf iftype { type string; mandatory true;} }}

第二个例子:
leaf iftype是必须存在,其前提是list interface必须存在;interface是个列表,最少有0个接口,最多可以有N个接口,说明list interface中可以一条数据都没有,list interface可以不存在。
这就是说:如果此YANG模块一加载,空配的时候(数据库为空DB)的时候/interfaces/interface表项为空不存在,此时leaf iftype也就不存在了;如果此时创建了一条interface数据,则这条interface的数据中,必须有iftype的数据。所以这个YANG模型是合理的。

container interfaces { //模块根节点 list interface { // <-- 相比举例1的变化点 key "name"; leaf name { type string;}leaf iftype { type string; mandatory true;} }}

在第二个例子的基础上演变出第三个例子:
继续解释:
leaf iftype是必须存在,其前提是list interface必须存在;list interface有个属性“min-elements 1”表示其父节点存在时此list至少要有一条数据。list interface的父节点是container interfaces,是普通container,默认存在。
这就是说:如果此YANG模块一加载,空配的时候(数据库为空DB)的时候,/interfaces/interface就至少要有一条数据,这条数据中用接口名name为key,而且iftype这个leaf也必须存在。这跟空配的场景是矛盾的。所以这个YANG模型是不合理的。

container interfaces { //模块根节点 list interface { key "name"; min-elements 1; // <-- 比举例2中增加了此行 leaf name { type string;}leaf iftype { type string; mandatory true;} }}

mandatory true还有个容易出错的地方。mandatory true属性表示对最终数据库中的数据进行约束校验(validation),并不针对某个NETCONF报文做要求。
继续举例,进行NETCONF报文说明。

container interfaces { //模块根节点 list interface { key "name"; leaf name { type string;}leaf shutdown { // <-- 此节点为mandatory true type boolean; mandatory true;}leaf dscp { type uint8;} }}

网管先下发第一个报文创建接口;再下发第二个报文设置dscp。

<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> <edit-config> <target> <running/> </target> <interfaces xmlns="http://example.com/schema/1.2/config"> <interface xc:operation="create"> <name>Ethernet0/0</name> <shutdown>true</shutdown> </interface> </interfaces> </edit-config> </rpc> <rpc-reply message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> <ok/> </rpc-reply>

第二个报文

<rpc message-id="103" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> <edit-config> <target> <running/> </target> <interfaces xmlns="http://example.com/schema/1.2/config"> <interface xc:operation="merge"> <name>Ethernet0/0</name> <dscp>4</dscp> <!-- 注意到报文中并没有Ethernet0/0的shutdown标签 --> </interface> </interfaces> </edit-config> </rpc> <rpc-reply message-id="103" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> <ok/> </rpc-reply>

注意到报文中并没有Ethernet0/0的shutdown标签。之前创建Ethernet0/0接口时已经设置过leaf shutdown了,DB中已经存在leaf shutdown的数据,因此第二个报文是合法的,没有报错。

2.2 mandatory true和default不能同时存在

2.2.1 mandatory true和带default值的叶子节点不能同时存在

这点很好理解,default值是说父节点存在时,此节点在DB中就算不显式下发,也会用一个默认值来填充。而mandatory true是说,父节点存在时,次节点在DB中必须要存在有数据。显然,有了默认值后,mandatory true就变成冗余的了。因此这里在RFC中是“MUST NOT”,禁止同时存在。下面给个错误的例子。

container interfaces { //模块根节点 list interface { key "name"; leaf name { type string;}leaf shutdown { // <-- 此节点为mandatory true,又有默认值,因此是非法的 type boolean; mandatory true; default false; } }}

2.2.2 choice的mandatory true和default case不能同时存在

choice的mandatory true,意思是:choice下的几个case分支中,必须存在一个case分支。
理解这点后,这里的要求就很好理解。参考2.2.1,不再展开。

2.3 key节点的mandatory true属性可忽略

对于config true的list而言,key是必须存在的。key是区分一条数据的唯一标识,一条数据中的所有key都是必须存在的。所以key的含义 > mandatory true

2.4 when + mandatory的理解

下面的例子是基于vlan段设置dscp。
当vlan-begin存在时,vlan-end必须存在;如果vlan-begin不存在,那么vlan-end则不存在。因此语义检查时,when的优先级要高于mandatory优先级

container dscp { leaf vlan-begin { type uint16;}leaf vlan-end { when "../vlan-begin"; type uint16; mandatory true;} }}

2.5 augment下的mandatory属性注意事项

这里再引申出一个问题:augment下的mandatory需要非常慎重。如果augment下有个mandatory节点,则augment无论挂接到哪里,将给父节点带来额外的要求——父节点必须能阻断这个mandatory节点向上传递。这种设计是不合理的。所以PYANG等一些validation工具做了一些校验,比如要求augment下的mandatory true必须和when一起使用,或者augment下有presence container能阻断mandatory。
YANG设计时尽量避免augment下的mandatory,必须使用时一定要注意模型的逻辑性。而且有的时候,就算你的逻辑是合理的,三方YANG网管编译工具、validation工具校验不通过也是白搭。毕竟YANG设计出来就是给外界用的。

2.6 【重点】mandatory属性变更的兼容性

删除mandatory,或者mandatory从true变成false是兼容的,反之不兼容
这里很好理解:必须存在的节点是一个约束,约束删除了当时是兼容的;增加约束是不兼容的。
when + mandatory时,去掉when让mandatory直接生效也是不兼容的
在已存在的一张表中增加mandatory节点是不兼容的。尤其是在YANG模块的顶层container下增加mandatory节点,是不允许的。

2.7 【慎重使用】mandatory属性支持deviation定制

有了前面兼容性的知识,这里就容易理解了。
deviation虽然是数据模型的一种偏差定义,但是一般情况下网管或者我们的客户不希望看到这种偏差。——谁不想搞统一网管呢?openconfig还想统一所有设备厂商的行为呢。
YANG语法上允许对mandatory进行deviate偏差定制。但我个人建议尽量避免此用法,慎重使用

  • 如果将数据模型中的mandatory true,在deviation中删除,变成mandatory false。——因为网管不乐意为单个产品适配,万一网管不适配或者适配错了,这里网管下发就要出问题啦。
  • 如果数据模型中原来没有mandatory属性,想在deviation中增加mandatory true,是可以的。——但是万一网管不适配或者适配错了呢?

2.8 【慎重使用】refine grouping的时候,mandatory属性是可以refine的。

这个道理和2.7很类似,考虑到用refine grouping语法的人很少,这里就不展开了。

三、mandatory节点

前面有的时候有提到mandatory节点这个词。最后补充一下什么是mandatory节点。
不光是mandatory true的节点是mandatory节点,min-elements不为0的节点也为mandatory节点。而且mandatory节点具有向上(向父节点)的传递性,只要不是presence container、list、case,mandatory都可以向上传递。


所以,这就要求default case下不能存在mandatory节点。因为mandatory节点本来到case已经阻断了。结果遇到了default case,继续向上传递。

相关推荐

相关文章