數據模型與網路自動化
傳統人工 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="//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="//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&revision=2010-10-04&deviations=cisco-xr-ietf-netconf-monitoring-deviations</capability>
<capability>//cisco.com/ns/yang/cisco-xr-ietf-netconf-monitoring-deviations?module=cisco-xr-ietf-netconf-monitoring-deviations&revision=2016-02-16</capability>
<capability>//cisco.com/ns/yang/Cisco-IOS-XR-Ethernet-SPAN-cfg?module=Cisco-IOS-XR-Ethernet-SPAN-cfg&revision=2015-11-09</capability>
<capability>//cisco.com/ns/yang/Cisco-IOS-XR-Ethernet-SPAN-datatypes?module=Cisco-IOS-XR-Ethernet-SPAN-datatypes&revision=2015-11-09</capability>
<capability>//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&revision=2010-10-04&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 的內容:
//<ADDRESS>/<ROOT>/<DATASTORE>/[YANGMODULE:]CONTAINER/<LEAF>[?<OPTIONS>]
- ADDRESS:表示 RESTCONF 代理的 IP
- ROOT:表示 restconf 請求的入口點
- DATASTORE:被查詢的資料庫
- [YANGMODULE:]CONTAINER – 使用基礎的模組名稱
: 在 Container 內的獨立 node - ?
:返回結果的過濾參數
這裡以 NSO – 思科的產品為例,直接操作設備也同理,比如 IOS-XE 的設備。
首先查詢是否具有 RESTCONF 功能:
//xx:8080/.well-known/host-meta
# Response 包含具有 RESTCONF 內容
<XRD xmlns='//docs.oasis-open.org/ns/xri/xrd-1.0'>
<Link rel='restconf' href='/restconf'/>
</XRD>
# 裡面的內容表示 ROOT 的內容,作為 RESTCONF 的入口點。
查看 NETCONF 支援的能力:
//xx:8080/restconf/data/netconf-state/
查看 RESTCONF 支援的能力:
//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:
//10.124.207.154:8080/restconf/data/tailf-ncs:devices/device=ASR9K/name/
修改介面描述 – Patch Method:
//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 來開發各種客戶端,去調用設備。