Ngsdn-Tutorial-Ⅱ

2020-05-23  本文已影响0人  SmartSloth

练习2-Yang,OpenConfig和gNMI基础

说明:本教程翻译自Next-Gen SDN TutorialExercise 2 - Yang, OpenConfig, and gNMI basics,自用学习Docker-P4-Tutorial

这套练习旨在让您更多地了解YANG,OpenConfig和gNMI。

此练习分为三个部分:

  1. 了解YANG语言
  2. 了解YANG编码
  3. 了解启用YANG的传输协议(使用gNMI)

第1部分:了解YANG语言

我们将从一个简单的YANG模块:demo-port中的yang/demo-port.yang开始

看一下模型并尝试推导结构。每个叶节点的有效值是多少?

    leaf speed {
        type identityref {
            base demo-port:SPEED;
        }
        description "Configurable speed of a switch port";
    }

该模型是自包含的,因此阅读起来并不是很困难。但是,大多数YANG模型都是定义在许多文件上的,这使得想象出整体结构非常复杂。

为了简化此过程,我们可以使用一个名为pyang的工具来尝试可视化模型的结构。

首先输入yang-tools的Docker:

$ make yang-tools
bash-4.4#

接下来,在demo-port.yang模型上运行pyang

bash-4.4# pyang -f tree demo-port.yang

您应该看到demo-port模块的树表示形式。这符合您的期望吗?

pyang -f tree demo-port.yang

Extra credit:如果您快速完成此操作,则可以尝试将新的叶子节点添加到port-configport-state分组,然后重新运行pyang并查看新叶子的添加位置。


我们还可以pyang用来可视化更复杂的模型集,例如Stratum使用的OpenConfig模型集。

这些模型已经加载到目录中的yang-tools容器中/models

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

您应该看到中显示的模型的树结构less。您可以使用箭头键或j/k上下滚动。键入q退出。

在接口模型中,我们可以看到启用或禁用接口的路径:interfaces/interface[name]/config/enabled

读取接口上传的入数据包数量(in-pkts)的路径是什么?

Extra credit:如果您有时间,请查看/models目录中的模型或在Github上浏览它们。

尝试找到enabledin-pkts叶子节点的描述。

    // 接口上接收到的数据包总数,包括所有单播,多播,广播和错误数据包等。
    leaf in-pkts {
        type oc-yang:counter64;
        description
            "The total number of packets received on the interface,
            including all unicast, multicast, broadcast and bad packets
            etc.";
        reference
            "RFC 2819: Remote Network Monitoring Management Information
            Base";
    }
    // 该叶子包含接口的已配置期望状态。
    // 如RFC 2863中所述,在初始化ifEntry之后,
    // 实现IF-MIB的系统使用“运行中”数据存储区中该叶子的值来将
    // IF-MIB.ifAdminStatus设置为“up”或“down”。
    // “正在运行”的数据存储区中的“叶”反映在ifAdminStatus中,
    // 但是如果通过SNMP更改了“ifAdminStatus”,则此叶不受影响。
    leaf enabled {
        type boolean;
        default "true";
        description
            "This leaf contains the configured, desired state of the
            interface.
            Systems that implement the IF-MIB use the value of this
            leaf in the 'running' datastore to set
            IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry
            has been initialized, as described in RFC 2863.
            Changes in this leaf in the 'running' datastore are
            reflected in ifAdminStatus, but if ifAdminStatus is
            changed over SNMP, this leaf is not affected.";
        reference
            "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
    }

提示:查看openconfig-interfaces.yang文件。

第2部分:了解YANG编码

YANG数据编码没有标准,但是可以将遵循YANG模型将数据编码为XML、JSON或Protobuf(以及其他格式)。这些格式都有自己的编码规范。

首先,我们看一下YANG的第一个XML格式规范表示。要查看以XML编码的数据的空骨架,请运行:

bash-4.4# pyang -f sample-xml-skeleton demo-port.yang

该骨架应与我们在第1部分中看到的树表示形式匹配。


YANG-XML-Skeleton

我们还可以使用pyang基于YANG模型生成DSDL模式:

bash-4.4# pyang -f dsdl demo-port.yang | xmllint --format -

模式的第一部分描述了树结构,第二部分描述了叶节点的值约束。

Extra credit:尝试添加新的速度标识(例如SPEED_100G)或更改中的port-number值范围demo-port.yang,然后重新运行pyang -f dsdl。您是否看到您的更改反映在DSDL模式中?


接下来,我们将研究使用协议缓冲区(protobuf)编码数据。protobuf编码是一种比XML更紧凑的二进制编码,并且可以自动为数十种语言生成库。我们可以使用ygotproto_generator产生从我们的洋型号protobuf的消息。

bash-4.4# proto_generator -output_dir=/proto -package_name=tutorial demo-port.yang

proto_generator将生成两个文件:

使用less打开demo_port.proto

bash-4.4# less /proto/tutorial/demo_port/demo_port.proto

该文件包含与YANG模型中定义的结构匹配的top-level Ports消息。您可以看到,proto_generator它还向每个protobuf消息字段添加了一个自定义选项yext.schemapath,该字段显式映射到YANG叶路径。枚举(如tutorial.enums.DemoPortSPEED)不包含在此文件中,proto_generator将它们放在单独的enums.proto文件中:

使用less打开enums.proto

bash-4.4# less /proto/tutorial/enums/enums.proto

如果您完成刚才的Extra credit,您应该会看到10GB speed的枚举以及添加的任何其他速度。


我们还可以proto_generator用来为Stratum使用的OpenConfig模型构建protobuf消息:

bash-4.4# proto_generator \
    -generate_fakeroot \
    -output_dir=/proto \
    -package_name=openconfig \
    -exclude_modules=ietf-interfaces \
    -compress_paths \
    -base_import_path= \
    -path=ietf,openconfig,hercules \
    openconfig/interfaces/openconfig-interfaces.yang \
    openconfig/interfaces/openconfig-if-ip.yang \
    openconfig/lacp/openconfig-lacp.yang \
    openconfig/platform/openconfig-platform-linecard.yang \
    openconfig/platform/openconfig-platform-port.yang \
    openconfig/platform/openconfig-platform-transceiver.yang \
    openconfig/platform/openconfig-platform.yang \
    openconfig/system/openconfig-system.yang \
    openconfig/vlan/openconfig-vlan.yang \
    hercules/openconfig-hercules-interfaces.yang \
    hercules/openconfig-hercules-platform-chassis.yang \
    hercules/openconfig-hercules-platform-linecard.yang \
    hercules/openconfig-hercules-platform-node.yang \
    hercules/openconfig-hercules-platform-port.yang \
    hercules/openconfig-hercules-platform.yang \
    hercules/openconfig-hercules-qos.yang \
    hercules/openconfig-hercules.yang

您将在目录/proto/openconfig中找到openconfig.protoenums.proto

Extra credit:尝试查找用于启用端口的Protobuf消息字段或获取Protobuf消息中的入口数据包计数器。

提示:通过schemapath搜索可能会有所帮助。


ygot也可以用来生成符合YANG模型的Go结构,并能够验证数据的结构,类型和值。

Extra credit:如果您有多余的时间或对一起使用yang和Go感兴趣,请尝试为该demo-port模块生成Go代码。

bash-4.4# mkdir -p /goSrc
bash-4.4# generator -output_dir=/goSrc -package_name=tutorial demo-port.yang

看看中的Go文件/goSrc

现在,您可以退出容器(使用Ctrl-Dexit)。

第3部分:了解启用YANG的传输协议

有几种YANG模型不可知协议,可用于获取或设置符合模型的数据,例如NETCONF,RESTCONF和gNMI。

本部分重点介绍通过gNMI使用protobuf编码。

首先,请确保您的Mininet容器仍在运行。

$ make start
docker-compose up -d
mininet is up-to-date
onos is up-to-date

如果您看到以下输出,则表明Mininet没有运行:

Starting mininet ... done
Starting onos    ... done

您将需要回到练习1并安装转发规则,以在此练习之间h1a以及h1b之后的部分重新建立ping。

接下来,我们将使用gNMI客户端CLI从Mininet网络中的Stratum交换机leaf1读取所有配置:

$ util/gnmi-cli --grpc-addr localhost:50001 get /

输出的第一部分显示了CLI发出的请求:

REQUEST
path {
}
type: CONFIG
encoding: PROTO

请求的路径是空路径(这意味着配置树的根),数据类型只是配置树,请求的响应编码为protobuf。

输出的第二部分显示了Stratum的响应:

RESPONSE
notification {
  update {
    path {
    }
    val {
      any_val {
        type_url: "type.googleapis.com/openconfig.Device"
        value: \252\221\231\304\001\... TRUNCATED
      }
    }
  }
}

您可以看到Stratum提供了type的响应openconfig.Device,这是在中定义的顶级消息openconfig.proto。响应是基于protobuf消息的数据的二进制编码。

该值不是很容易让人理解,但是我们可以使用实用程序来转换答复,该实用程序可以在protobuf消息的二进制表示形式和文本表示形式之间进行转换。

我们可以重新运行该命令,但这一次将输出通过转换器实用程序传递less给管道(然后通过管道输出使滚动更容易):

$ util/gnmi-cli --grpc-addr localhost:50001 get / | util/oc-pb-decoder | less

响应的内容应该更容易阅读。向下滚动到第一个interface。接口启用了吗?端口的速度是多少?

interface:leaf1-eth1

Extra credit:您能找到in-pkts吗?如果没有,您为什么认为它们不见了?


gNMI的好处之一是它的“无模式”编码,它允许客户端或设备仅更新需要更新的路径。这对于订阅特别有用。

首先,让我们通过请求leaf1h1a之间的配置端口来尝试无模式表示:

$ util/gnmi-cli --grpc-addr localhost:50001 get \
    /interfaces/interface[name=leaf1-eth3]/config

您应该看到此响应在config- enabledhealth-indicator下包含2个叶子:

RESPONSE
notification {
  update {
    path {
      elem {
        name: "interfaces"
      }
      elem {
        name: "interface"
        key {
          key: "name"
          value: "leaf1-eth3"
        }
      }
      elem {
        name: "config"
      }
      elem {
        name: "enabled"
      }
    }
    val {
      bool_val: true
    }
  }
}
notification {
  update {
    path {
      elem {
        name: "interfaces"
      }
      elem {
        name: "interface"
        key {
          key: "name"
          value: "leaf1-eth3"
        }
      }
      elem {
        name: "config"
      }
      elem {
        name: "health-indicator"
      }
    }
    val {
      string_val: "GOOD"
    }
  }
}

无模式的表示形式为每个包含路径的叶子提供并update了叶子的值。您可以确认该接口已启用(设置为true)。

接下来,我们将为leaf1连接到h1a(端口3)的接口订阅入口单播数据包计数器:

$ util/gnmi-cli --grpc-addr localhost:50001 \
    --interval 1000 sub-sample \
    /interfaces/interface[name=leaf1-eth3]/state/counters/in-unicast-pkts

输出的第一部分显示了CLI发出的请求:

REQUEST
subscribe {
  subscription {
    path {
      elem {
        name: "interfaces"
      }
      elem {
        name: "interface"
        key {
          key: "name"
          value: "leaf1-eth3"
        }
      }
      elem {
        name: "state"
      }
      elem {
        name: "counters"
      }
      elem {
        name: "in-unicast-pkts"
      }
    }
    mode: SAMPLE
    sample_interval: 1000
  }
  updates_only: true
}

我们具有订阅路径,订阅的类型(采样)和采样率(每1000ms或1s)。

输出的第二部分是响应流:

RESPONSE
update {
  timestamp: 1567895852136043891
  update {
    path {
      elem {
        name: "interfaces"
      }
      elem {
        name: "interface"
        key {
          key: "name"
          value: "leaf1-eth3"
        }
      }
      elem {
        name: "state"
      }
      elem {
        name: "counters"
      }
      elem {
        name: "in-unicast-pkts"
      }
    }
    val {
      uint_val: 1592
    }
  }
}

每个响应都有一个时间戳、路径和新值。因为我们正在采样,所以您应该看到每秒打印一次新的更新。保持运行状态,这时候我们也产生一些流量。

在另一个窗口中,打开Mininet CLI并开始ping:

$ make mn-cli
*** Attaching to Mininet CLI...
*** To detach press Ctrl-D (Mininet will keep running)
mininet> h1a ping h1b

在第一个窗口中,uint_val当ping仍在运行时,您应该看到每秒增加1。(如果不完全是1,那么可能会有其他流量,例如NDP消息导致流量增加。)

您可以使用Ctrl-C停止gNMI订阅。


最后,我们将使用gNMI的变更订阅功能监视链接事件。

为第一个交换机的第一个端口的操作状态启动订阅:

$ util/gnmi-cli --grpc-addr localhost:50001 sub-onchange \
    /interfaces/interface[name=leaf1-eth3]/state/oper-status

您应该立即看到一个响应,指示端口1为UP

RESPONSE
update {
  timestamp: 1567896668419430407
  update {
    path {
      elem {
        name: "interfaces"
      }
      elem {
        name: "interface"
        key {
          key: "name"
          value: "leaf1-eth3"
        }
      }
      elem {
        name: "state"
      }
      elem {
        name: "oper-status"
      }
    }
    val {
      string_val: "UP"
    }
  }
}

在运行Mininet CLI的Shell中,让我们断开leaf1连接到h1a的接口:

mininet> sh ifconfig leaf1-eth3 down

您应该在gNMI CLI窗口中看到一个响应,显示leaf1连接h1a的接口是DOWN

RESPONSE
update {
  timestamp: 1567896891549363399
  update {
    path {
      elem {
        name: "interfaces"
      }
      elem {
        name: "interface"
        key {
          key: "name"
          value: "leaf1-eth3"
        }
      }
      elem {
        name: "state"
      }
      elem {
        name: "oper-status"
      }
    }
    val {
      string_val: "DOWN"
    }
  }
}

我们可以使用以下Mininet命令恢复界面:

mininet> sh ifconfig leaf1-eth3 up

您应该在gNMI CLI窗口中看到另一个响应,指示该接口为UP


Extra credit:我们还可以使用gNMI禁用或启用接口。

保留您的gNMI订阅以运行操作状态更改。

在Mininet CLI中,在两个主机之间启动ping操作。

mininet> h1a ping h1b

您应该看到在Mininet CLI中显示了答复。

在第三个窗口中,我们将使用gNMI CLI将enabled叶子的配置值从true更改为false

$ util/gnmi-cli --grpc-addr localhost:50001 set \
    /interfaces/interface[name=leaf1-eth3]/config/enabled \
    --bool-val false

在gNMI设置窗口中,您应该看到指示enabled叶子的新值的请求:

REQUEST
update {
  path {
    elem {
      name: "interfaces"
    }
    elem {
      name: "interface"
      key {
        key: "name"
        value: "leaf1-eth3"
      }
    }
    elem {
      name: "config"
    }
    elem {
      name: "enabled"
    }
  }
  val {
    bool_val: false
  }
}

在gNMI订阅窗口中,您应该看到一个新的响应,指示的运行状态leaf1-eth3DOWN

RESPONSE
update {
  timestamp: 1567896891549363399
  update {
    path {
      elem {
        name: "interfaces"
      }
      elem {
        name: "interface"
        key {
          key: "name"
          value: "leaf1-eth3"
        }
      }
      elem {
        name: "state"
      }
      elem {
        name: "oper-status"
      }
    }
    val {
      string_val: "DOWN"
    }
  }
}

在Mininet CLI窗口中,您应该观察到ping命令已停止工作。

接下来,我们可以重新启用端口:

$ util/gnmi-cli --grpc-addr localhost:50001 set \
    /interfaces/interface[name=leaf1-eth3]/config/enabled \
    --bool-val true

您应该在gNMI订阅窗口中看到另一个更新,指示该接口是UP,并且ping应该在Mininet CLI窗口中恢复。

恭喜你!

您已经完成了第二个练习!

上一篇下一篇

猜你喜欢

热点阅读