SDN学习之Opendaylight浅析(五)
这一节主要讲下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底层连接机制实现