天天看点

数据模型与网络自动化

传统人工 CLI 配置网络的模式,已经不在适用当代的网络,面临着兼容性,容错率低,效率低下等等问题,详细的内容可阅读这篇传统CLI面临的挑战

在这样的大背景下,各种网管协议应运而生。但这时就产生一个问题,以怎样的格式和内容去传递配置?

YANG 就是为了解决该问题而出现的,在解释 YANG 前,我们先来回忆下,传统 CLI 是如何下发配置的?

常常是由网络工程师通过 console/Telnet/SSH 等方式登录上设备,然后直接对设备进行配置,这时自然不用考虑怎样传参的问题,只要懂得设备的命令,直接上去敲就可以。

而 CLI 这样的配置,是一种无结构化的数据。

!
interface Bundle-Ether2
!
interface Bundle-Ether780
 description e***-vpws-test
!
interface MgmtEth0/RSP0/CPU0/0
 vrf mgmt
 ipv4 address 10.124.3.85 255.255.255.0
!
interface MgmtEth0/RSP0/CPU0/1
 shutdown
!
interface TenGigE0/0/1/0
 shutdown
!
interface TenGigE0/0/1/1
 shutdown
!
interface TenGigE0/0/2/0
 shutdown
!
           

这样无结构的数据对我们人来说是非常友好的,容易理解和阅读。

但由于换成了 NETCONF 这样的网管协议管理设备,这样的数据发挥不出任何优势,甚至无法被机器识别。因为使用 NETCONF 目的就是为了使用自动化,可编程化的方案代替人工,从而满足当下网络的各种业务场景。

此时配置结构化的数据就成了必然。

于是各个厂商开始对配置进行结构化的约定,在 NETOCNF 中,配置使用 XML 表示,进行参数的下发。

比如对接口的 MTU 进行更改:

<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
       <edit-config>
         <target>
           <running/>
         </target>
         <config>
           <top xmlns="http://example.com/schema/1.2/config">
             <interface>
               <name>Ethernet0/0</name>
               <mtu>1500</mtu>
             </interface>
           </top>
         </config>
       </edit-config>
</rpc>
           

但此时又有一个问题,就是下发数据的格式和参数是由谁指定呢,比如这里 interface 命名的定义,类型的定义,层级的定义是如何确定的呢?

我怎么知道在下发接口配置时,就下发这样的配置内容呢?

答案在于各个厂商定义了一套约束标准,也可以理解成配置的模板。这些模板对数据进行约束,判断是否是合法的数据。这也就是数据模型的由来。

拿 NETCONF 举例,NETCONF 采用 C/S 的架构,Client 端在生成配置内容时会参考定义好的数据模型。Server 在接收时,同样会用这些数据模型进行校验。

具体些,比如针对下发 JSON 格式的配置,通过 JSON Scheme 定义的 JSD 对数据进行约束。

数据模型与网络自动化

如左图中是具体的数据,右图中是对左侧数据具体进行约束的数据模型。比如这里限定了,productName 的类型是字符串,当前对象共有三个属性等。当传入其他不符合 scheme 的数据时,会进行的报错。

如果对 JSON-Scheme 感兴趣,可以去官网仔细了解下。

对于思科设备来说,这种由 JSON-Scheme 定义的文件叫 JSD,用于约束传入的数据类型,需要注意的是不同的厂商定义的 JSD 内容和格式是不一样的,甚至同一厂商定义的不同类型的 JSD 也不一样。

针对下发的 XML 格式的配置也采用相同的方法,各个厂商通过 XML Scheme 定义 XSD 对数据进行约束。

数据模型与网络自动化

左面是 xml 格式的数据,右边是 XSD 文件对其进行约束,规定了属性的类型。同样不同厂商编写的 XSD 约束也是不一样的。

虽然说通过编写 XSD 和 JSD 的方式解决了如何约束配置数据的问题。

但由于不同厂商编写的数据模型没有统一的规范,导致各种各样的 JSD/XSD 出现,学习成本也很高。而且造成很严重的兼容性问题。比如 Cisco 的 JSD 或者 XSD 一定和 HUAWEI 不一样。甚至 Cisco 本身每个团队开发出的 JSD/XSD 的内容也不一样。

为了解决这个问题,由 IETF 主导,开发出了 YANG - Yet Another Next Generation. 被现在各个协议广泛使用。

YANG

YANG 的定义

YANG 是一种数据模型语言,用于在 NETCONF 等网络协议中,将想要操作的配置或状态数据进行模型化,用层次化的表现形式,对数据进行表述。对于 YANG 模型来说,每个都有唯一标识命名空间 URI,用于区分。

简单来说,通过 YANG,对下发配置的进行约束,如下的公式很好的描述了 YANG:

data(下发的数据)+ YANG = 下发给设备的配置

数据模型与网络自动化

上图中很好的表示了 YANG 起到的作用,YANG 本身并不是数据,而更像是一种配置模板,起到约束数据的作用。

那么 YANG Model 一般是由谁定义的呢?

YANG Model 的定义,主要有两个角色:

  • 标准化的 YANG Model ,由 IETF,OpenConfig 等机构进行规划定义。这类的 YANG 主要是考虑到多尝试的兼容性问题,而推出的统一的 YANG Module。所有厂商都需要支持。
  • 各个厂商实现自定义私有的 YANG。这类 YANG Model 主要是为了厂商实现某些私有或特有功能 YANG. 比如 Cisco 中有许多私有的协议,如 EIGRP,BGP 的某些功能只有思科设备上有。

YANG 的结构

YANG Module 以层次化树形结构被组织起来,每个模块可以引入外部其他的模块,包含其子模块的数据。

简单来说,就是可以将模块作为参数,引入其他的模块进行使用。

YANG 定义了很多的内置类型,并提供了自定义类型的机制,类似于 C 中的

typedef

.

在 YANG 中定义了四种类型,用于将数据模型化:

Leaf Nodes:

一个节点用于表示数字或字符串等简单的数据。但只能表示一个值,不能拥有子节点。

YANG 表示:

leaf host-name {
           type string;
           description "Hostname for this system";
       }
           

xml 表示:

<host-name>my.example.com</host-name>
           

json 表示:

{
    "host-name": "my.example.com"
}
           

Leaf-List Nodes:

表示由 leaf node 构成的列表。

YANG 表示:

leaf-list domain-search {
         type string;
         description "List of domain names to search";
     }
           

xml 表示:

<domain-search>high.example.com</domain-search>
<domain-search>low.example.com</domain-search>
<domain-search>everywhere.example.com</domain-search>
           

json 表示:

[
{"domain-search": "high.example.com"},
{"domain-search": "low.example.com"},
{"domain-search": "everywhere.example.com"},
]
           

Container Nodes:

类似于编程语言中的 MAP 形式,将多个 node 组装到一起。一个 container node 可以包含多个任意类型的 node 节点,如 leafs,lists,leaf-lists,及本身 container 的类型。

YANG 表示:

container system {
         container login {
             leaf message {
                 type string;
                 description
                     "Message given at start of login session";
             }
         }
     }
           

xml 表示:

<system>
       <login>
         <message>Good morning</message>
       </login>
     </system>
           

json 表示:

{"system":{"login": {"message": "Good morning"}}}
           

List Nodes

由一个或多个 key leaf 和多个任意类型的子节点组成,类型包括,leafs,

lists, containers 等。

其中 key leaf 用于表示当前 list 的唯一性。

YANG 表示:

list user {
         key "name";
         leaf name {
             type string;
         }
         leaf full-name {
             type string;
         }
         leaf class {
             type string;
         }
     }
           

这里的 name 作为唯一的标识符。

xml 表示:

<user>
       <name>glocks</name>
       <full-name>Goldie Locks</full-name>
       <class>intruder</class>
     </user>
     <user>
       <name>snowey</name>
       <full-name>Snow White</full-name>
       <class>free-loader</class>
     </user>
     <user>
       <name>rzell</name>
       <full-name>Rapun Zell</full-name>
       <class>tower</class>
     </user>
           

List Nodes 和 Leaf-List Nodes 的区别就是,Leaf-List Nodes 仅能包含类型是 Leaf Nodes 的节点,而 List Nodes 可以包含任意类型。

YANG 的其他特性

对于 YANG 来说,本身支持很多特性:

  • 配置状态数据,对于定义那些不能配置的配置信息。
  • 内置大量的基础类型,binary,bits,boolean 等等。
  • 派生类型,自定义去定义如 binary 等类型。
  • 可重用组,引用通过 grouping 陈述定义的组,用于解耦和封装。
  • 支持 choices,类似于枚举。
  • 使用

    augment

    对 model 进行约束
  • 提供 RPC 调用
  • 提供通知定义

更多的功能,可以参考 YANG - RFC 文档

PYANG - 更好的浏览 YANG Model

在了解 YANG 语言,提供的强大功能后。一般在项目中,会使用由 Python 开发的

Pyang

工具来浏览 YANG 模型.

简单提一下安装方法,目前 Pyang 支持 Python2,Python3.

可以通过 docker 打包 Python 镜像后,作为运行环境:

[root@localhost pyang-env]# ls
Dockerfile  requirements.txt  yang_modules

[root@localhost pyang-env]# cat Dockerfile
FROM python:3.8.5

ENV MY_PROXY_URL="http://xxx:80"
ENV HTTP_PROXY=$MY_PROXY_URL \
    HTTPS_PROXY=$MY_PROXY_URL \
    FTP_PROXY=$MY_PROXY_URL \
    http_proxy=$MY_PROXY_URL \
    https_proxy=$MY_PROXY_URL \
    ftp_proxy=$MY_PROXY_URL

WORKDIR /src

COPY ./requirements.txt /

RUN pip install --no-cache-dir  pyang

ENV MY_PROXY_URL=
ENV HTTP_PROXY=$MY_PROXY_URL \
    HTTPS_PROXY=$MY_PROXY_URL \
    FTP_PROXY=$MY_PROXY_URL \
    http_proxy=$MY_PROXY_URL \
    https_proxy=$MY_PROXY_URL \
    ftp_proxy=$MY_PROXY_URL
           

使用

docker run -v /home/xx/pyang-env/yang_modules:/src -it --name pyang-env pyang-image /bin/bash

启动运行环境。

接着去 YANG 的 Github 中,下载由 IETF 或各个厂商开发后的 YANG Module 。这里以 IOS-XE 版本为 633 的 YANG Module 为例。

可以看到有很多类似的 YANG 文件:

数据模型与网络自动化

这时就可以通过 Pyang 工具,来浏览内容:

root@a8af90280cf1:/src/633# pyang -f tree ietf-interfaces.yang
module: ietf-interfaces
  +--rw interfaces
  |  +--rw interface* [name]
  |     +--rw name                        string
  |     +--rw description?                string
  |     +--rw type                        identityref
  |     +--rw enabled?                    boolean
  |     +--rw link-up-down-trap-enable?   enumeration {if-mib}?
  +--ro interfaces-state
     +--ro interface* [name]
        +--ro name               string
        +--ro type               identityref
        +--ro admin-status       enumeration {if-mib}?
        +--ro oper-status        enumeration
        +--ro last-change?       yang:date-and-time
        +--ro if-index           int32 {if-mib}?
        +--ro phys-address?      yang:phys-address
        +--ro higher-layer-if*   interface-state-ref
        +--ro lower-layer-if*    interface-state-ref
        +--ro speed?             yang:gauge64
        +--ro statistics
           +--ro discontinuity-time    yang:date-and-time
           +--ro in-octets?            yang:counter64
           +--ro in-unicast-pkts?      yang:counter64
           +--ro in-broadcast-pkts?    yang:counter64
           +--ro in-multicast-pkts?    yang:counter64
           +--ro in-discards?          yang:counter32
           +--ro in-errors?            yang:counter32
           +--ro in-unknown-protos?    yang:counter32
           +--ro out-octets?           yang:counter64
           +--ro out-unicast-pkts?     yang:counter64
           +--ro out-broadcast-pkts?   yang:counter64
           +--ro out-multicast-pkts?   yang:counter64
           +--ro out-discards?         yang:counter32
           +--ro out-errors?           yang:counter32
           

还可以转换成 js 浏览:

pyang -f jstree ietf-interfaces.yang >> ietf-interfaces.html
           
数据模型与网络自动化

更多命令可以查看帮助文档。

这时,对于网络工程师来说,可以将其从学习各厂商不同的配置命令转化到学习 Yang Module 中,更加聚焦功能,而不用在花费时间去学习相同的功能不同的命令。

YANG-Suite 介绍

YANG-Explorer 是一个用于浏览 YANG Model 的 WEB 服务,并提供生成 NETCONF RPC payload 等实用的功能。但由于其使用 Flash 编写,但在 2020 12 月后,Flash 已经被禁用,导致该工具无法使用。有兴趣的同学,可以安装老版本带 flash 的浏览器测试学习。

安装可以采用 docker:

YANG

docker pull robertcsapo/yang-explorer

docker run -it --rm -p 8088:8088 robertcsapo/yang-explorer
           

最新 CISCO 很开源了一个产品为 YANG-Suite,基于 YANG-Explorer 拓展了许多功能,并可以使用 docker-compose 启动,兼容性更好,预计未来会主流接受。

比如下面使用其生成 NETCONF RPC Payload

数据模型与网络自动化

YANG 与 NETCONF

YANG 在早期是专为 NETCONF 而开发的一种语言,后来才被普及到各个语言中,关于 NETCONF 的介绍,可以参考这篇。

下面主要涉及具体的操作,使用的设备是 ASR9000(IOS-XR 6.3.3)版本。

由于 NETCONF 本身采用 C/S 架构,需要在设备端打开:

netconf agent ssh
           

在客户端方面,可以使用

ssh

进行测试:

NETCONF 会首先使用 Hello 建立链接,报告所拥有的能力。

[root@localhost ~]# ssh cisco@ip -p 830 -s netconf

Password:
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
 <capabilities>
  <capability>urn:ietf:params:netconf:base:1.1</capability>
  <capability>urn:ietf:params:netconf:capability:candidate:1.0</capability>
  <capability>urn:ietf:params:netconf:capability:rollback-on-error:1.0</capability>
  <capability>urn:ietf:params:netconf:capability:validate:1.1</capability>
  <capability>urn:ietf:params:netconf:capability:confirmed-commit:1.1</capability>
  <capability>urn:ietf:params:netconf:capability:notification:1.0</capability>
  <capability>urn:ietf:params:netconf:capability:interleave:1.0</capability>
  <capability>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&amp;revision=2010-10-04&amp;deviations=cisco-xr-ietf-netconf-monitoring-deviations</capability>
  <capability>http://cisco.com/ns/yang/cisco-xr-ietf-netconf-monitoring-deviations?module=cisco-xr-ietf-netconf-monitoring-deviations&amp;revision=2016-02-16</capability>
  <capability>http://cisco.com/ns/yang/Cisco-IOS-XR-Ethernet-SPAN-cfg?module=Cisco-IOS-XR-Ethernet-SPAN-cfg&amp;revision=2015-11-09</capability>
  <capability>http://cisco.com/ns/yang/Cisco-IOS-XR-Ethernet-SPAN-datatypes?module=Cisco-IOS-XR-Ethernet-SPAN-datatypes&amp;revision=2015-11-09</capability>
  <capability>http://cisco.com/ns/yang/Cisco-IOS-X
           

每个 capability 都可包含四部分内容:

  • Model URI
  • Module Name ,Revision Date
  • Protocol Features
  • Deviations - 修改自那个 Module
<capability>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&amp;revision=2010-10-04&amp;deviations=cisco-xr-ietf-netconf-monitoring-deviations</capability>
 
 Model URI = urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring
 Module Name = module=ietf-netconf-monitoring
 Revision = 2010-10-04
 Protocol Features = NULL
 Deviations = cisco-xr-ietf-netconf-monitoring-deviations
           

使用

ncclient

操作 NETCONF 设备, 使用参见 ncclient-github

获取 running 配置:

from ncclient import manager

host = "10.1.1.22"
username = "cisco"
password = "cisco"
device_params = {"name": "iosxr"}

with manager.connect(host=host, port=830, username=user, hostkey_verify=False, password=password) as m:
    c = m.get_config(source='running').data_xml
    with open("%s.xml" % host, 'w') as f:
        f.write(c)
           

上面演示了 NETCONF 中

get-config

操作,其余配置或过滤的功能,可参考文档。

需要注意一点的是,在配置时,payload 的生成一般通过上面介绍的 YANG-Suite 工具。

YANG 与 RESTCONF

RESTCONF 和 NETCONF 很像,简单来说,就是将 HTTP 融入了 NETCONF 中,采用 REST 风格替代 SSH 和 RPC 的交互方式。

更详细的内容,可参看这一篇。RESTCONF,下面主要集中在具体操作。

先来看下 RESTCONF URL 的内容:

https://<ADDRESS>/<ROOT>/<DATASTORE>/[YANGMODULE:]CONTAINER/<LEAF>[?<OPTIONS>]
           
  • ADDRESS:表示 RESTCONF 代理的 IP
  • ROOT:表示 restconf 请求的入口点
  • DATASTORE:被查询的数据库
  • [YANGMODULE:]CONTAINER - 使用基础的模块名称
  • : 在 Container 内的独立 node
  • ?:返回结果的过滤参数

这里以 NSO - 思科的产品为例,直接操作设备也同理,比如 IOS-XE 的设备。

首先查询是否具有 RESTCONF 功能:

http://xx:8080/.well-known/host-meta

# Response 包含具有 RESTCONF 内容 
<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>
    <Link rel='restconf' href='/restconf'/>
</XRD>

# 里面的内容表示 ROOT 的内容,作为 RESTCONF 的入口点。
           

查看 NETCONF 支持的能力:

http://xx:8080/restconf/data/netconf-state/
           

查看 RESTCONF 支持的能力:

http://xx:8080/restconf/data/restconf-state/
           

NOTE:

需要在 Headers 中,指定发送和接受数据的格式:

Content-type:

  • application/yang-data+json
  • application/yang-data+xml
Accept:
  • application/yang-data+json
  • application/yang-data+xml

查询 NSO 纳管的设备 - GET Method:

http://10.124.207.154:8080/restconf/data/tailf-ncs:devices/device=ASR9K/name/
           

修改接口描述 - Patch Method:

http://10.124.207.154:8080/restconf/data/tailf-ncs:devices/device=ASR9K/config/tailf-ned-cisco-ios-xr:interface/TenGigE/

{
    "tailf-ned-cisco-ios-xr:TenGigE": 
        {
            "id": "0/0/1/0",
            "description": "restconf-test"
        }
}
           

总结

YANG 的本质是一种对数据进行结构化描述的语言,本身不是数据,而是起到约束数据的作用。

至于为什么需要 YANG,原因在于传统 CLI 的方式,不在适合当代网络的要求。而且结构化,统一的数据更容易被机器所处理。

现在 YANG 被广泛使用,特别是可编程化的特点,像让自动化,动态编排服务,甚至网络自我调节与优化都成为了可能。

下图中很好的描述了 YANG 所发挥的作用,在设备上通过 YANG 的定义,提供如 RESTCONF,NETCONF 的接口,让其通过 HTTP 或 RPC 管理设备。

在控制器中,根据定义的 YANG Module 来开发各种客户端,去调用设备。

参考

RFC6020 - YANG

RFC6020 - YANG1.1

YANG-Study

YANG Models

yang-explorer

RESTCONF-URL

继续阅读