YANG:YANG的数据类型

本文详细介绍了NETCONF/YANG中的内置数据类型,包括整数、实数、字符串、布尔、空、枚举、联合、leafref和identityref等,并探讨了如何创建自定义数据类型。同时,讨论了leaf节点的type类型的deviate定制,以及派生数据类型的使用。文章还提到了在实际应用中遇到的问题和注意事项,如字符串解析和NETCONF工具的兼容性问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

专注NETCONF/YANG技术科普,接受任何问题,包括我答不上来的问题。欢迎光临!欢迎提问:链接


这个主题来源于优快云上的一封私信,建议我写一写如何创建自定义数据类型。
本来我以为YANG的数据类型没啥好写的,后来重新读了读RFC7950,觉得leafref、identify还是值得写一写的。于是有了这篇。
另外最近看公众号大V的篇幅,文字数量集中在2K左右。我的博客是否文字太长了呀?写这玩意儿,要么不动笔,一动笔就收不住。

一、内置数据类型

众所周知,YANG语法的发明更多参考的C编程语言。下面就列举内置数据类型语法如下:

分类名称描述
整数内置类型int88位有符号整数
整数内置类型int1616位有符号整数
整数内置类型int3232位有符号整数
整数内置类型int6464位有符号整数
整数内置类型uint88位无符号整数
整数内置类型uint1616位无符号整数
整数内置类型uint3232位无符号整数
整数内置类型uint6464位无符号整数
实数内置类型decimal6464位有符号十进制实数
字符串内置类型string一个字符串
二进制内置类型binary任何二进制数据
布尔内置类型boolean“true” 或 “false”
空(empty)内置类型empty一个叶子,没有任何值,只有存在不存在
位(bits)内置类型bits一组比特(bits)或标志(flags)
枚举内置类型enumeration枚举的一组字符串之一
identityref内置类型identityref对抽象身份的引用
leafref内置类型leafref对叶子实例的引用
联合内置类型union成员类型的选择

我把上表中的数据类型分成几类:

  • 第一类是基础数据类型,包括整数内置类型、实数内置类型、字符串内置类型、二进制内置类型、布尔内置类型、空内置类型、bits内置类型、枚举内置类型;
  • 第二类是联合内置类型;
  • 第三类是leafref内置类型;
  • 第四类是identityref内置类型。
    前两类的内置类型比较简单,参考github的公有YANG,或者RFC7950,语法较为简单。第三类和第四类稍微复杂一些。
    这里基于实际应用,只讲一些我工作中遇到的一些好用的内置类型用法。

1.1 整数内置类型

包括:int8、int16、int32、int64、uint8、uint16、uint32、uint64,有符号+无符号各四种数据类型。含义参考C语言,不展开描述。
所有的整数类型支持多种表达方式:支持十六进制(0x+大小写字母均可)、支持八进制(0开头+八进制字母)、正号的“+”可以省略。
——有人可能觉得这段是废话,但是后面如果遇到IETF IPV6-address数据类型(string的扩展类型)的时候,就会思考配置下发FF01:0000:0000:0000:0000:0000:0000:0001和查询到的FF01::0001是否等价认为是一个值。在整数数据类型这里,很明确,是等价的。

整数类型有个子语句range,用于限制多组整数范围段。多组范围段用“|”隔开。“min”表示此限制类型可接受的最小值;“max”表示此限制类型可接受的最大值。

     typedef my-base-int32-type {
       type int32 {
         range "min..0| 10..40 | 101..max";
       }
     }

1.2 实数内置类型

decimal64表示实数的子集。它是由一个64位有符号整数乘以10的负幂而获得的一组数字,即,可表示为“i x 10^-n”,其中i是64位整数,n是介于1和18之间的整数。
这里的“n”就是decimal64的子语句fraction-digit。即:小数点后的位数。
下表列出了每个分数位数的最小值和最大值:
在这里插入图片描述

     typedef my-decimal {
       type decimal64 {
         fraction-digits 2; //小数点后保留两位
         range "1 .. 3.14 | 10 | 20..max";
       }
     }

实数内置类型也分标准形式和非标准形式,只要数值含义一致即可。但是禁止使用前导零和尾随零。比如:上面的my-decimal类型的3.10和3.1是等价的。

1.3 字符串内置类型

字符串内置类型表示YANG中可读的字符串。 合法字符是Unicode和ISO/IEC 10646 [ISO.10646]字符,包括制表符,回车符和换行符,但不包括其他C0控制字符,代理块和非字符。即:一般的可见字符均在YANG支持的范围内。如果要查详细的YANG字符串语法,在RFC7950或者RFC6020中搜索“yang-string”正式定义。
虽然YANG的字符串内置类型支持的字符很多,但是实际应用中,网管能解析的string类型字符却并不那么标准。我在NSO和NETCONF browser中发现以空格结尾的字符串(如:“abc “)会被网管忽略掉结尾的空格。NETCONF browser甚至对于用空格开头的字符串(如:” abc”)有时也会无法识别。我在其他一些通用NETCONF工具上也看到了这个问题。应该是在netconf解析报文截取数值时有些公共插件会自动忽略尖括号中的空格,导致的此问题。如果有网管同学,可以看看自己用的NETCONF解析器中是否存在此问题。
字符串用length子语句限制字符串长度(不含’\0’);用pattern进行正则表达式限制。

     typedef my-base-str-type {
       type string {
         length "1..255";
       }
     }

注意到我们可以用typedef定义一个string类型,然后在leaf上设置type 再次进行length限制。leaf上type的length限制只能缩小范围,不能扩大范围。

     typedef my-base-str-type {
       type string {
         length "1..255";
       }
     }
     leaf my-parameter {
       type my-base-str-type {
         // legal length refinement
         length "11 | 42..max"; // 11 | 42..255
         // illegal length refinement
         // length "1..999"; // 999比255大
       }
     }

1.4 布尔内置类型

一个布尔值的词汇表示是一个值为“true”或“false”的字符串,必须是小写。
注意一个布尔类型的leaf是有三种状态的:
1、leaf没有值;此时如果有默认值则按默认值处理;没有默认值则节点无值;
2、leaf有值,等于true;
3、leaf有值,等于false;
所以当配置一些开关量的时候,尤其关注leaf的三个状态。

如果leaf只有“有 / 无”两种状态怎么办呢?下面引入空(empty)内置类型

1.5 空(empty)内置类型

空内置类型比叫特殊。空内置类型代表一个没有任何值的叶子; 它通过它的存在或缺席传达信息。(类比于presence container)。
空(empty)内置类型不能有默认值。

leaf enable-qos {
  type empty;
}
// 如果叶子存在,NETCONF编码示例如下:
// <enable-qos/>

1.6 枚举内置类型

枚举内置类型表示一组用“字符串名称”来代表“整数值”的类型。枚举中所有分配的字符串名称都必须是唯一的。与其对应的整数值(“value”语句)是可选的,用于将整数值与枚举的指定名称相关联。整数值必须在-2147483648到2147483647之间,并且在枚举类型中必须是唯一的。
因为value语句是可选的,如果未指定值,则将自动指定一个值。如果“enum”子状态是第一个定义的子状态,则赋值为零(0);否则,分配的值比当前最高枚举值大一个(即,在父“type”语句中当前“enum”子语句之前的隐式或显式最高枚举值)。

     typedef my-base-enumeration-type {
       type enumeration {
         enum white {
           value 1;
         }
         enum yellow {
           value 2;
         }
         enum red; //分配的value值为3
       }
     }

1.7 联合内置类型

联合内置类型类似C语言的union。比较好理解。先给出样例。

     type union {
       type int32;
       type enumeration {
         enum "unbounded";
       }
     }

唯一说明的一点YANG的数据建模是要生成数据库表项(或者class类)的,**uion在生成数据库表项或者类的时候就没有那么友好,需要依赖解析器的具体实现。**因此在设计union类型的typedef时,需要注意union下的子类型要尽量为相同的基类型,以方便解析器以及网管识别。
比如:IETF中定义的typedef ip-address是ipv4-address和ipv6-address的联合体。

// ietf-inet-types.yang
  typedef ip-address {
    type union {
      type inet:ipv4-address;
      type inet:ipv6-address;
    }
    description
     "The ip-address type represents an IP address and is IP
      version neutral.  The format of the textual representation
      implies the IP version.  This type supports scoped addresses
      by allowing zone identifiers in the address format.";
    reference
     "RFC 4007: IPv6 Scoped Address Architecture";
  }

1.8 leafref内置类型

leafref内置类型被限制只能使用schema tree中的某个叶子或叶子列表节点的实例空间。
“path”子语句用于标识schema tree中引用的叶节点或叶节点列表节点。 引用节点的值空间是被引用节点的值空间。
“require instance”子语句为“true”,则表示引用的实例必须存在才能使数据有效(禁止悬空引用)。值为false,则表示引用的实例可能存在于有效数据中(允许悬空引用)。默认是禁止悬空引用的。——require instance是YANG1.1新引入的属性,用于明确引用关系是强约束还是悬空引用。
还是看例子:

     list interface {
       key "name";
       leaf name {
         type string;
       }
       leaf admin-status {
         type admin-status;
       }
       list address {
         key "ip";
         leaf ip {
           type yang:ip-address;
         }
       }
     }
// 上面对应的XML代码段示例:
//      <interface>
//        <name>eth0</name>
//      </interface>
//      <interface>
//        <name>lo</name>
//      </interface>     

// 另有一个模型leafref引用现有接口:
     leaf mgmt-interface1 {
       type leafref {
         path "../interface/name";
       }
     }
// mgmt-interface1的值空间的XML代码段示例:
//      <mgmt-interface1>eth0</mgmt-interface1>
//      或者 <mgmt-interface1>lo</mgmt-interface1>

// 还有一个模型leafref引用现有接口,但是是弱引用关系,支持悬空引用:
     leaf mgmt-interface2 {
       type leafref {
         path "../interface/name";
         require-instance false;
       }
     }     
// mgmt-interface2的值空间除了“interface/name”之外,还可以是接口名称的其他字符串:
//      <mgmt-interface1>eth0</mgmt-interface1>
//      或者 <mgmt-interface1>lo</mgmt-interface1>
//      或者 <mgmt-interface1>eth1234</mgmt-interface1>

前面说到,require instance是YANG1.1新引入的属性;在YANG1.0的时候无此语法,默认是禁止悬空引用。业界目前还是YANG1.0为主流,因此业务模型是否使用“允许悬空引用”建议慎重考虑。业务配置是否“防呆”在目前业界也分成了两派,一派是悬空派以juniper为代表;一派是防呆派以思科为代表。现在思科貌似也在转,也在纠结转不转。感觉这个方向的判断,我个人觉得后面2-3年内可能还是“防呆”的天下。——不太看好主流产品从设备厂商到运营商的断代决心。

1.9 identityref内置类型

identityref的用法是:首先要设置一个identity的基类型(有点像C++的基类)。再用这个基类型的identity生成派生的identity(像C++的派生类)。派生的identity以来基类型的identity。使用这一系列identity的leaf,type类型为identityref内置类型。
identityref的典型应用场景是:ietf-alarm.yang。

// ietf-alarms.yang
  identity alarm-type-id {}
  typedef alarm-type-id {
    type identityref {
      base alarm-type-id;
    }
    description
      "Identifies an alarm type.  The description of the alarm type
       id MUST indicate whether or not the alarm type is abstract.
       An abstract alarm type is used as a base for other alarm type
       ids and will not be used as a value for an alarm or be present
       in the alarm inventory.";
  }
  
//在xxx.yang中定义派生类型
module xxx{
  import ietf-alarms {
    prefix al;
  }
  identity equipment-alarm {
    base "al:alarm-type-id";
  }
  identity communication-alarm {
    base "al:alarm-type-id";
  }
  identity fan-alarm {
    base "equipment-alarm";
  }
  identity temperature-alarm {
    base "equipment-alarm";
  }
  container xxx {
    leaf alarm-type {
      type identityref {
        base "al:alarm-type-id";
      }
    }
    list active-alarm {
      when "../alarm-type = 'communication-alarm'";
      ...
    }
    ...
  }
}

注意:上面的xxx.yang作为一个上层应用,利用identity可以很容易地进行插件化裁剪。xxx.yang仅单向依赖ietf-alarms.yang(作为系统基础设施模型)。通过identity可以扩展上层业务模块。如果不支持xxx.yang,只需要netconf报文的capabilities里面不宣称xxx.yang的能力即可。
这里identity的用法要跟枚举的用法区分开。枚举的各个enum之间是平等的,且解耦复杂(deviation中可以重新定义一个新的枚举type类型,但是难以管控枚举范围只能缩小不能扩大);只有identity有继承关系存在,通过文件加载的方式能很容易地进行定制裁剪。

二、派生数据类型

派生数据类型使用typedef对内置数据类型进行一种复杂定义,而且在使用type属性的地方运行重定义(但是重定义的范围只能缩小不能扩大)。

	typedef my-base-int32-type {
	  type int32 {
	    range "1..4 | 10..20";
	  }
	}
	
	typedef my-type1 {
	  type my-base-int32-type {
	    // legal range restriction
	    range "11..max"; // 11..20
	    // illegal range restriction
	    // range "11..100";
	  }
	}
     
     typedef my-base-str-type {
       type string {
         length "1..255";
       }
     }
     leaf my-parameter {
       type my-base-str-type {
         // legal length refinement
         length "11 | 42..max"; // 11 | 42..255
         // illegal length refinement
         // length "1..999"; // 999比255大
       }
     }

三、leaf节点type类型的deviate定制

这章很简短,但是重要。
deviate定制时会遇到与上一章类似的问题。deviate定制的有效数据集范围建议不能超过原始type定义的原始数据范围。这里没有RFC撑腰,但是网管建模一般只会拿公共yang文件建模,而不是叠加deviation的模型去建模。
这种问题往往出现在枚举类型中。

  typedef trust-direction-type {
    type enumeration {
      enum "inbound" ;
      enum "outbound";
    }
  }
  augment "/ifm:ifm/ifm:interfaces/ifm:interface" {
      container trusts {
        list trust {
          ...
          leaf direction {
            type trust-direction-type;
          }
          ...
        }
      }
  }
  
//以下为deviation文件中内容
  typedef trust-direction-type-deviations {
    type enumeration {
      enum "in-outbound" {  //在原始公共yang文件中没有in-outbound枚举项,这里会给网管解析造成困难
        value 0;
        description 
          "Inbound and outbound.";
      }
    }
  }
  deviation "/ifm:ifm/ifm:interfaces/ifm:interface/qos:trusts/qos:trust/qos:direction" {
    deviate replace {
      type trust-direction-type-deviations;
    }
  }  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值