傳統人工 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
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>http://cisco.com/ns/yang/cisco-xr-ietf-netconf-monitoring-deviations?module=cisco-xr-ietf-netconf-monitoring-deviations&revision=2016-02-16</capability>
<capability>http://cisco.com/ns/yang/Cisco-IOS-XR-Ethernet-SPAN-cfg?module=Cisco-IOS-XR-Ethernet-SPAN-cfg&revision=2015-11-09</capability>
<capability>http://cisco.com/ns/yang/Cisco-IOS-XR-Ethernet-SPAN-datatypes?module=Cisco-IOS-XR-Ethernet-SPAN-datatypes&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&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 的內容:
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 來開發各種客戶端,去呼叫裝置。