這一節主要講下odl的netconf應用,其實odl比較多的是利用openflow對交換機下流表,實現真正的控制與轉發的分離,然而有時候並不能如願。很多實際真正使用的交換機不能實現真正的控制分離,如果能那麼設備商會少很多利潤,所以一個折衷對交換機的控制方案就是利用netconf對交換機進行控制,netconf可以看作一個通信協議,交換機一般在830端口起一個服務器,接受客戶端的請求調用,通信數據是xml格式的,客戶端可以是python腳本,如果要利用odl,就相當於將odl的南向plugin作爲客戶端,與交換機進行通信。
首先介紹一下netconf,netconf是一種基於XML的網絡配置管理協議 。用戶可以使用這套機制增加、修改、刪除網絡設備的配置,獲取網絡設備的配置和狀態信息。NETCONF面向連接,以遠程過程調用的方式實現操作和控制。採用XML作爲配置數據和協議消息內容的編碼方式;擴展性好,廠商可以定義自己的協議操作,以實現獨特功能,netconf在最底層是建立ssh連接,在最上層是利用數據交互來獲取配置,或者修改機器配置,或者完成nofication通知等操作。
netconf使用簡單的基於RPC(Remote Procedure Call)機制實現客戶端和服務器之間通信。客戶端可以是腳本或者網管上運行的一個應用程序。服務器是一個典型的網絡設備。NETCONF提供了一種通過運行網絡管理軟件的中心計算機(即網絡管理工作站)來遠程管理和監控設備的方法。
NETCONF協議將數據區分爲配置數據和狀態數據,並分別提供不同的操作進行數據的增刪改查。 配置數據(configuration data)是對網絡設備進行配置的數據,例如創建VLAN的數據。配置數據一般是可讀寫的。 狀態數據(state data)是反映設備狀態的數據,例如端口的up/down狀態,報文統計等。狀態數據一般是隻讀的。會存在多個數據庫,例如用於正在運行的,或者啓動時加載的。
客戶端與服務器調用rpc的時候存在一個交互過程,首先需要發hello message,確認能力
netconf調用操作層僅承載在僅<rpc>和<rpc-reply>消息上,<hello>和<notification>消息無操作層。 NETCONF協議規定了9種簡單的rpc操作,同時也支持用戶自定義rpc操作。有關自定義操作的內容放到內容層來講。 開放但規範的內容層是netconf協議的精髓所在。其開放體現在netconf協議本身沒有對內容層的數據結構做任何的限定。而其規範則體現在其內容層需要使用Yang語言對其數據進行建模。內容層未指定具體的模型結構,而是指定了一套建模語言–yang。也就是說使用yang定義的數據模型,均可以作爲netconf的內容層。所以擴展對netconf來說就是不斷的增加和修改yang文件而已。在上面將操作的時候提到netcon支持用戶自定義操作。也就是說我們不必糾結標準制定的9個操作類型是否夠用,完全可以根據實際的需求在yang文件中定義相應的rpc操作。
再說說odl裏面的netconf應用開發,第一步就是獲取交換機的yang文件,yang文件就等同於交換機內的樹形數據,在odl裏面是在掛載節點下面的datastore裏面保存,如圖交換機vlan的yang模型
container vlan {
description
"VLAN management.";
container vlans {
description
"VLAN list.";
list vlan {
key "vlanId";
max-elements "4094";
description
"VLAN information.";
leaf vlanId {
type vlanId {
range "1..4094";
}
description
"VLAN ID.";
}
leaf vlanName {
type string {
length "1..31";
}
must "not(../vlanType='carrier' or ../vlanType='fcoe')";
description
"VLAN name.";
ext:allowDelete "true";
}
leaf vlanDesc {
type string {
length "1..80";
}
must "not(../vlanType='carrier' or ../vlanType='fcoe')";
description
"VLAN description.";
ext:allowDelete "true";
}
leaf vlanType {
type vlanType;
must "((../vlanType='common' or ../vlanType='super' or ../vlanType='principal') ) or (../vlanType='common' and (../vlanType='common' or ../vlanType='super' or ../vlanType='principal') ) or (../vlanType='group' and (../vlanType='common' or ../vlanType='group') ) or (../vlanType='principal' and (../vlanType='common' or ../vlanType='principal') ) or (../vlanType='seperate' and (../vlanType='common' or ../vlanType='seperate') ) or (../vlanType='sub' and (../vlanType='common' or ../vlanType='sub') ) or (../vlanType='super' and (../vlanType='common' or ../vlanType='super') ) or not(../vlanType!='common' and ../vlanType!='super' and ../vlanType!='sub' and ../vlanType!='principal' and ../vlanType!='group' and ../vlanType!='seperate' or ../vlanType='common' or ../vlanType='group' or ../vlanType='principal' or ../vlanType='seperate' or ../vlanType='sub' or ../vlanType='super')";
default "common";
description
"VLAN type, such as common VLANs, super VLANs, and sub-VLANs. ";
}
利用yang文件通過yangtools生成數據節點的java文件,由此構造設備節點下的datasore數據,首先就是在連接設備情況下,拿到mountPoint:
final Optional<MountPoint> deviceNodeOptional=mountService.getMountPoint(NetconfIidFactory.netconfNodeIid(deviceId));
deviceId是connect device設置的device id,然後利用mountPoint 拿到datastore:
Preconditions.checkArgument(deviceNodeOptional.isPresent(),
"Unable to locate mountpoint: %s, not mounted yet or not configured", deviceId);
final MountPoint deviceNode = deviceNodeOptional.get();
// Get the DataBroker for the mounted node
final DataBroker deviceNodeBroker = deviceNode.getService(DataBroker.class).get();
接下來就是操作datastore裏面的數據,例如要獲取交換機中所有vlan的配置,就是讀datastore節點中的數據
final ReadOnlyTransaction deviceNodeReadTx = deviceNodeBroker.newReadOnlyTransaction();
InstanceIdentifier<Vlans> idn = InstanceIdentifier.create(
org.opendaylight.yang.gen.v1.http.www.huawei.com.netconf.vrp.huawei.vlan.rev181123.Vlan.class)
.child(Vlans.class);
Optional<Vlans> ldn;
try {
ldn=deviceNodeReadTx.read(LogicalDatastoreType.OPERATIONAL,idn).checkedGet();
} catch (ReadFailedException e) {
throw new IllegalStateException("Unexpected error reading data from " + deviceId, e);
}
反之,配置vlan數據是利用寫datastore來實現的:
final WriteTransaction deviceNodeWriteTx = deviceNodeBroker.newWriteOnlyTransaction();
try {
deviceNodeWriteTx.put(LogicalDatastoreType.CONFIGURATION, idn, data);
FluentFuture<? extends @NonNull CommitInfo> future = deviceNodeWriteTx.commit();
future.get(5000, TimeUnit.MILLISECONDS);
}catch (TimeoutException e){
return RpcResultBuilder.<ConfigVlanOutput>failed()
.withResult(new ConfigVlanOutputBuilder().setResult(e.getMessage())).buildFuture();
} catch (Exception e) {
return RpcResultBuilder.<ConfigVlanOutput>failed()
.withResult(new ConfigVlanOutputBuilder().setResult(e.getMessage())).buildFuture();
}
至於odl內部的實現原理,可以理解爲內部設置了datastore的監聽器,當數據變化時,發起對mount node的netconf通信,還有對odl netconf的解讀可以參考如下:
ODL Netconf MountPoint及其集羣模式實現
https://www.sdnlab.com/22981.html
ODL Netconf底層連接機制實現
https://www.sdnlab.com/22997.html
使用Docker容器構建ODL集羣
https://www.sdnlab.com/22554.html
ODL Netconf底層連接機制實現