在本章节,主要是了解YANG是什么,以及基于YANG下发配置的工作原理:
1.什么是YANG
在介绍之前,为了给大家一个最直观的感受,我们打开yang工具,它被打包成一个容器了,可以轻松的使用,可以看到,docker run了一个yang工具容器:
make yang-tools
当我们打开一个范例demo-port.yang和它的yml格式文件,我们可以看见它的一个格式:
pyang -f tree demo-port.yang
- 里面的对象有的可读可写,有的只读
- 加了一个*的表示这个对象是个序列
- 在yang的树叶上可以看到,每一个叶子成员(leaf)都是最基本的单元,他们有被定义一些值,比如number或者boolean这些
- 文件中,我们可以看到一个模型,就是一颗以树组织的结构,里面描述了每个节点是什么(description),每个节点的属性是什么以及这个属性基于什么属性(description,base)
- 也包括了一些节点的其他信息(namespace,description)
- 对于序列节点(list)它的每个成员的属性是什么(key),通过基本的属性(type)以及对属性的定义(typedef )可以得到任何属性,一些节点可能还有一些可重用的属性(grouping),定义时只需要使用(uses)即可
- 在yang中,一个模型可以被理解为多个成员(container)的集成,每个成员包括了自身的一些属性等等
module demo-port {
// YANG Boilerplate
yang-version "1";
namespace "https://opennetworking.org/yang/demo";
prefix "demo-port";
description "Demo model for managing ports";
revision "2019-09-10" {
description "Initial version";
reference "1.0.0";
}
// Identities and Typedefs
identity SPEED {
description "base type for port speeds";
}
identity SPEED_10GB {
base SPEED;
description "10 Gbps port speed";
}
typedef port-number {
type uint16 {
range 1..32;
}
description "New type for port number that ensure the number is between 1 and 32, inclusive";
}
// Reusable groupings for port config and state
grouping port-config {
description "Set of configurable attributes / leaves";
leaf speed {
type identityref {
base demo-port:SPEED;
}
description "Configurable speed of a switch port";
}
}
grouping port-state {
description "Set of read-only state";
leaf status {
type boolean;
description "Number";
}
}
// Top-level model definition
container ports {
description "The root container for port configuration and state";
list port {
key "port-number";
description "List of ports on a switch";
leaf port-number {
type port-number;
description "Port number (maps to the front panel port of a switch); also the key for the port list";
}
// each individual will have the elements defined in the grouping
container config {
description "Configuration data for a port";
uses port-config; // reference to grouping above
}
container state {
config false; // makes child nodes read-only
description "Read-only state for a port";
uses port-state; // reference to grouping above
}
}
}
}
让我来做个总结,YANG模型以一种树形结构描述了一个类的全面的性质,但是你一定很好奇,它只是描述了一个东西它应该有哪些属性,定义了这个属性是什么类型,比如,我们用一个YANG去描述一个网络设备,要对这个设备进行操作需要知晓设备的各个属性全貌,以及哪些是只读的(state),哪些是可以改的(config),但是,要对这个设备进行具体的操作,我们需要有值啊,所以,我们要对YANG定义出来的模型,为数据附上这些值。
bash-4.4# pyang -f tree \ -p ietf \ -p openconfig \ -p hercules \ openconfig/interfaces/openconfig-interfaces.yang \ openconfig/interfaces/openconfig-if-ethernet.yang \ openconfig/platform/* \ openconfig/qos/* \ openconfig/system/openconfig-system.yang \ hercules/openconfig-hercules-*.yang | less
小插曲:在这里,我们可以看看openconfig-interfaces.yang的长相,openconfig-interfaces.yang是网络中对接口配置的一个模型描述,很长很长,可以看到里面有一些参数,比如in_pkts进数据包和enable使能。
2.基于YANG的数据编码
刚刚我们知道,以面向对象的角度比喻:YANG它只是定义了一个类的格式,没有给他赋值,为了给这个类具体的值,我们需要实例化它,它被实例化出来的对象,它可以以XML的形式存在,也可以以JSON的形式存在,这无所谓,只要它的数据遵循类的规范即可。
pyang -f sample-xml-skeleton demo-port.yang
把YANG转换成xml的格式会是这样的(但是还是没有赋值):
转成DSDL格式会是这样的:
pyang -f dsdl demo-port.yang | xmllint --format -
接下来,我们将研究使用协议缓冲区(protobuf)对数据进行编码,protobuf编码是一种比XML更紧凑的二进制编码,并且可以为几十种语言自动生成库。我们可以使用ygot的proto_generator从我们的YANG模型中生成protobuf消息
proto_generator -output_dir=/proto -package_name=tutorial demo-port.yang
生成了两文件,一个用来紧凑的描述demo_port,一个用来描述属性
/proto/tutorial/demo_port/demo_port.proto
/proto/tutorial/enums/enums.proto
less /proto/tutorial/demo_port/demo_port.proto
less /proto/tutorial/enums/enums.proto
3.基于YANG的gNMI下发配置
在第二节中,我们知道了基于YANG的数据编码格式,在本小节中,我们讨论如何通过基于YANG的配置,操作数据平面。
首先make start打开我们的mininet,然后我们需要让两个主机ping通,不知道如何ping通的,可以回到上一个博客查看。
现在,我们来到gNMI client CLI,通过它实现对数据平面的配置,首先看看它的数据以及组织形式,这里的get只是总览一下数据平面中leaf1的信息。
util/gnmi-cli --grpc-addr localhost:50001 get /
上面的命令没有对数据编码进行修正,通过以下指令进行解码
util/gnmi-cli --grpc-addr localhost:50001 get / | util/oc-pb-decoder | less
在这里,我们看到了request中,我们要看的数据的类型是可config的,用的解码是PROTO
现在来看看leaf1的eth3的接口的信息
util/gnmi-cli --grpc-addr localhost:50001 get \
/interfaces/interface[name=leaf1-eth3]/config
请求部分,可以看到客户端是如何请求的,请求的数据组织格式:
有3个信息,使能:true,健康监测:GOOD,loopback模式:false(它不是一个环回接口,它是一个实体接口,要转发到别的主机上的)
接下来,我们订阅一些数据,我们用到如下指令:
util/gnmi-cli --grpc-addr localhost:50001 \
--interval 1000 sub-sample \
/interfaces/interface[name=leaf1-eth3]/state/counters/in-unicast-pkts
- util/gnmi-cli:gNMI的命令行
- --grpc-addr localhost:50001 grpc的服务器也就是在leaf1的BMV2
- --interval 1000 sub-sample 1000毫秒一次收集信息
- /interfaces/interface[name=leaf1-eth3]/state/counters/in-unicast-pkts 记录接口leaf1-eth3它的状态栏中关于计数器的部分,这个计数器专门指的是in-unicast-pkts,顾名思义,进来leaf1-eth3的单播数据包
现在,每秒都在更新一次数据,因为我们订阅了它: