資料模型與網路自動化

以終為始發表於2021-03-04

傳統人工 CLI 配置網路的模式,已經不在適用當代的網路,面臨著相容性,容錯率低,效率低下等等問題,詳細的內容可閱讀這篇傳統CLI面臨的挑戰

在這樣的大背景下,各種網管協議應運而生。但這時就產生一個問題,以怎樣的格式和內容去傳遞配置?

YANG 就是為了解決該問題而出現的,在解釋 YANG 前,我們先來回憶下,傳統 CLI 是如何下發配置的?

常常是由網路工程師通過 console/Telnet/SSH 等方式登入上裝置,然後直接對裝置進行配置,這時自然不用考慮怎樣傳參的問題,只要懂得裝置的命令,直接上去敲就可以。

而 CLI 這樣的配置,是一種無結構化的資料。

!
interface Bundle-Ether2
!
interface Bundle-Ether780
 description evpn-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

Screenshot showing the selected YangModel, various parameters such as the selected value, mode, datastore, and RPC window with the RPC textbox displaying the RPC

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 來開發各種客戶端,去呼叫裝置。

Cisco Connect Toronto 2018 model-driven programmability for cisco i…

參考

RFC6020 - YANG

RFC6020 - YANG1.1

YANG-Study

YANG Models

yang-explorer

RESTCONF-URL

相關文章