随着互联网的发展,网络日趋复杂且规模庞大。网络的稳定性/可预测的故障恢复时间如何保障?大量的新设备和服务如何安全快速地融入老的系统?不同的厂商接口如何统一管理?这些问题需要大量的优秀工程师去处理,但另一个事实是,相当一部分的网络故障是由人为操作不当引起的。通过自动化网络管理去减轻工程师的运维负担是大势所趋。在 2002 年 IAB(Internet Architecture Board )就网络管理所面临的问题作了讨论(RFC3535),确定了四个解决问题的关键方向,支持事物/可回滚/实现成本低/配置可存储和恢复。随即 NETCONF 工作小组成立,基于这几个方向制定了 NETCONF 标准(RFC6241). NETCONF 协议包含四层,数据层/操作层/调用层/传输层, 但是缺少一种定义数据模型的方式,2008 年 NETMOD 工作组成立,2009 年 NETMOD 发布了 YANG(RFC7950), 一种为 NETCONF 定义数据模型的标准语言。
urn:ietf:params:netconf:capability:{name}:1.x
NETCONF 是 C/S 架构的模式,Client 和 Server 通过 RPC 以 xml 形式交换数据。Client 端作为网络管理服务的一部分,可以是脚本,也可以是应用等。Server 端一般是网络设备。
NETCONF 包含了四个层次,数据层/操作层/RPC/传输层, 对于普通用户,只需关注数据层和操作层即可。
设备模型数据,YANG 数据建模语言正是用来描述这些模型数据的。
操作层定义了一系列用于网络设备管理的操作。
定义了一种独立于协议之上( transport-protocol-independent)的数据组织和传输方式。
定义了数据的传输方式, NETCONF 并没有指定协议实现者在 C/S 之间使用什么传输协议,只是规定了一些 传输协议必须满足的要求,比如面向连接/支持 kee-alive 等,具体的参见RFC6241.
我们使用 juniper 的 vsrx 来做 NETCONF 的使用示例。
cli
3.3 进入配置模式
configure
3.4 设置密码
set system root-authentication plain-text-password
3.5 设置远程登录管理用户
set system login user remote class super-user authentication plain-text-password
3.6 设置网络
set interfaces fxp0 unit 0 family inet address 172.16.240.189/24
3.7 设置 netconf ssh port
set system services netconf ssh port 830
3.8 提交配置
commit check
commit
client 列表
连接完成后,server 端会返回自己的 capabilities
package main
import (
"fmt"
"golang.org/x/crypto/ssh"
"log"
"github.com/Juniper/go-netconf/netconf"
)
func main() {
sshConfig := &ssh.ClientConfig{
User: "root",
Auth: []ssh.AuthMethod{ssh.Password("r00ttest")},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
s, err := netconf.DialSSH("172.16.240.189", sshConfig)
if err != nil {
log.Fatal(err)
}
defer s.Close()
fmt.Println(s.ServerCapabilities)
fmt.Println(s.SessionID)
}
你会看到 running 的所有配置
package main
import (
"fmt"
"golang.org/x/crypto/ssh"
"log"
"github.com/Juniper/go-netconf/netconf"
)
func main() {
sshConfig := &ssh.ClientConfig{
User: "root",
Auth: []ssh.AuthMethod{ssh.Password("r00ttest")},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
s, err := netconf.DialSSH("172.16.240.189", sshConfig)
if err != nil {
log.Fatal(err)
}
defer s.Close()
fmt.Println(s.ServerCapabilities)
fmt.Println(s.SessionID)
// Sends raw XML
reply, err := s.Exec(netconf.RawMethod("<get-config><source><running/></source></get-config>"))
if err != nil {
panic(err)
}
fmt.Printf("Reply: %+v", reply)
}
包括错误回滚
package main
import (
"fmt"
"golang.org/x/crypto/ssh"
"log"
"github.com/Juniper/go-netconf/netconf"
)
func doRpc(s *netconf.Session, xml string) {
reply, err := s.Exec(netconf.RawMethod(xml))
if err != nil {
panic(err)
}
fmt.Printf("Reply: %+v", reply)
}
func main() {
sshConfig := &ssh.ClientConfig{
User: "root",
Auth: []ssh.AuthMethod{ssh.Password("r00ttest")},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
s, err := netconf.DialSSH("172.16.240.189", sshConfig)
if err != nil {
log.Fatal(err)
}
defer s.Close()
fmt.Println(s.ServerCapabilities)
fmt.Println(s.SessionID)
//xml := "<rpc>"
xml := ""
xml += "<edit-config>"
xml += " <target><candidate/></target>"
xml += " <error-option>stop-on-error</error-option>"
xml += " <config>"
xml += " <configuration>"
xml += " <interface>"
xml += " <name>fxp0</name>"
xml += " <unit>"
xml += " <name>0</name>"
xml += " <family>"
xml += " <inet>"
xml += " <address>172.16.240.189/24</address>"
xml += " </inet>"
xml += " </family>"
xml += " </unit>"
xml += " </interface>"
xml += " </configuration>"
xml += " </config>"
xml += "</edit-config>"
//xml += "</rpc>
// Sends raw XML
doRpc(s, xml)
xml = "<rpc><commit></commit></rpc>"
doRpc(s, xml)
}
当有多个设备需要修改配置时,为了保证操作的原子性,我们可以先锁定所有设备的配置,修改完成后,利用 confirmed-commit 特性,check 完之后再确认提交。
YANG 是为 NETCONF 而生的数据建模语言, 应用在 NETCONF 的 content 层和 operation 层。YANG 用层级的方式描述 NETCONF 的配置和状态数据,RPC 及通知,覆盖了 NETCONF C/S 的整个生命周期。YANG 模型可以转换成 JSON 和 XML。
YANG 定义了一系列的模型描述关键字。
YANG 定义了一系列的基础类型,同时可以用 typedef 描述自定义的类型, 一些通用的扩充类型可以参见RFC6021
# Sample configuration file for ISC dhcpd
default-lease-time 600;
max-lease-time 7200;
subnet 10.254.239.0 netmask 255.255.255.224 {
range dynamic-bootp 10.254.239.10 10.254.239.20;
option routers rtr-239-0-1.example.org, rtr-239-0-2.example.org;
max-lease-time 1200;
}
shared-network 224-29 {
subnet 10.17.224.0 netmask 255.255.255.0 {
range 10.17.224.10 10.17.224.250;
option routers rtr-224.example.org;
}
subnet 10.0.29.0 netmask 255.255.255.0 {
range 10.0.29.10 10.0.29.230;
option routers rtr-29.example.org;
}
}
module dhcp { // dhcp module
namespace "songtianyi:yang:dhcp";
prefix dhcp;
import ietf-yang-types { // 导入自定义类型
prefix yang;
}
import ietf-inet-types {
prefix inet;
}
organization "TIANYUAN CLOUD TECH";
description "Partial data model for DHCP, based on the config of the ISC DHCP reference implementation.";
container dhcp { // 根结点
description "configuration and operational parameters for a DHCP server.";
leaf max-lease-time { // 描述最大租赁期
type uint32;
units seconds; // 定义单位
default 7200; // 最大租赁期默认值
}
leaf default-lease-time {
type uint32;
units seconds;
must 'current() <= ../max-lease-time' { // 约束, default-lease-time 必须小于等于 max-lease-time
error-message "The default-lease-time must less or equal max-lease-time";
}
default 600; // 如果默认值大于 max-lease-time 也会报错
}
uses subnet-list; // 引用 grouping 描述
container shared-networks {
list shared-network {
key name;
leaf name {
type string;
}
uses subnet-list;
}
}
}
grouping subnet-list {
description "A reusable list of subnets";
list subnet {
key net;
leaf net {
type inet:ip-prefix;
}
container range {
presence "enables dynamic address assignment"; // presence 表明,range 作为子节点存在,即使 range 没有定义子节点
leaf dynamic-bootp {
type empty;
description "Allows BOOTP clients to get addresses in this range";
}
leaf low {
type inet:ip-address;
mandatory true;
}
leaf high {
type inet:ip-address;
mandatory true;
}
}
container dhcp-options {
description "Options in the DHCP protocol";
leaf-list router {
type inet:ip-address;
ordered-by user;
}
leaf domain-name {
type inet:domain-name;
}
leaf max-lease-time {
type uint32;
units seconds;
default 1200;
}
}
}
}
}
前边分别介绍了 NETCONF 和 YANG, 那么它们是如何结合起来的呢?
<dhcp xmlns="http://yang-central.org/ns/example/dhcp" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
<default-lease-time nc:operation="merge">10800</default-lease-time>
</dhcp>
在 netconf client 发送 xml 给 netconf server 的时候将 YANG 模型加入 xml 即可,server 会根据模型来检验 xml 里的配置数据,因此 default-lease-time 的 10800 配置会返回 rpc-error。需要注意的是,这些特性依赖于 netconf server 的具体实现,因此需要通过 server 端返回的 capbilities 来检查是否支持该特性。
在网络设备管理方面起步早且做的好的,应当是 tail -F systems, 该公司的 NSO(Network Service Orchestrator)产品已被 cisco 收购。
参见视频