最近对onos进行了二次开发,主要实现了Juniper路由器的 driver开发。在Driver中利用NETCONF协议读取并修改路由器配置、生成NETCONF命令、并下发到路由器是Driver开发的重要部分。开发过程中,我们研读了onos里NETCONF相关的代码,了解设备是如何进行连接,session是如何创建,命令是如何发送的。在这里分享给大家。
一、前期准备
要进行设备的连接,有两个很重要的前提。一个是物理设备可以被onos控制器所识别。第二个是该设备对应的驱动已经在控制器中加载。只有这两个前提条件被满足,onos才能与设备进行连接,从而下发控制命令。
如果对这两个步骤还不是很了解的同学,可以参考以下连接:https://wiki.onosproject.org/display/ONOS/NETCONF。
对于驱动是如何加载的分析,我们将会在后续的文章中进行介绍。
二、设备连接的建立
进入正题,当控制器要对具体的设备进行控制时,第一步要做的就是与该设备进行连接。
首先进入的是org.onosproject.net.device.impl.DeviceManager的deviceConnected方法。
1、根据设备所属的BasicDeviceConfig获得设备的基本配置。
2、根据传进来的deviceDescription和获得的配置信息重新更新deviceDescription并且创建role(master、none、standby等)
3、调用applyRole(deviceId,role)将设备与role绑定。需要注意的是,当调用完这个方法之后就已经创建了onos与设备的连接,并将创建的session保存起来,方便以后调用(具体的实现请往下看)。
4、将device的信息存入store中。
走到这一步,onos就完成了与设备的连接,当需要对设备发送命令时,只要找到对应的session就可以进行发送了。
从上面的分析我们知道applyRole是一个很重要的方法。接下来我们就分析一下applyRole是如何完成上述的功能。
从上面的源码中我们看到,在applyRole函数中最关键的就是调用了DeviceProvider的roleChanged(deviceId,newRole)函数。因为DeviceProvider是一个接口,它具体实现的类是NetconfDeviceProvider,对应的函数是Rolechanged。
可以看出,在roleChanged函数中,根据role的类型进行不同的处理。特别注意的是,当role显示为master时,通过initiateConnection函数调用NetconfControllerImpl类的connectDevice(deviceId)方法,完成NetconfDeviceInfo和NetconfDevice 的创建。并完成与设备的连接。
下面是NetconfControllerImpl类中connectDevice(deviceId)函数的具体代码:
首先通过deviceId从deviceService中获得username、password等信息。然后根据得到的用户信息以及之前存储的设备信息生成NetconfDeviceInfo。再根据生成的NetconfDeviceInfo,最后通过createDevice函数调用deviceFactory的createNetconfDevice方法生成NetconfDevice,同时与该设备进行连接。
createNetconfDevice方法的具体实现在NetconfControllerImpl类中。在该方法中他调用了DefaultNetconfDevice的构造函数,创建了NetconfDevice。从下图构造函数代码中,我们可以看见,系统在创建NetconfDevice的同时,也创建了netconfSession,并将netconfSession作为Device的一个属性保存起来。当用户需要与某设备进行交互时,只需要找到对应的Device对象,就能获得与该设备进行交互的session。
【注意】以下就是onos与device进行连接的具体代码。
与DeviceFactory一样,SessionFactory的createNetconfSession函数,也是调用了NetconfSessionImpl的构造函数,进行netconfSession的创建。
与设备进行连接的操作是通过NetconfSessionImpl类startConnection函数完成的。
1、根据deviceInfo的ip和port的信息构建connection。
2、调用connect函数进行连接
3、根据deviceinfo中的username等信息,看用户是否被授权。
4、如果用户被授权则调用startSshSession
在startSshSession中,创建Stream线程为以后的交流提供服务。同时向设备发送hello的NETCONF命令,完成两个设备间所支持功能的同步,为以后使用NETCONF命令进行交流打下基础。
至此onos就完成了与设备的连接,并将创建的session保存在netconfController中。在下一节中我们将介绍如何使用创建的session进行命令的发送。
三、NETCONF命令的发送
当控制器与设备建立好连接以后,我们就可以通过控制器想设备发送命令了。
本文以onos中的device-configuration命令为例,讲解netconf命令是如何下发,同时控制器是如何获得路由器返回信息的。
3.1 NETCONF命令发送
当用户在onos的终端输入device-configuration命令以后,就进入了org.onosproject.cli.net.DeviceConfigGetterCommand中。
系统根据具体的配置文件,找到ConfigGetter接口的具体实习类——NetconfConfigGetter。
在上一节我们介绍过,在NetconfControllerImpl的createDevice函数中,系统将生成的Device放入到了DevicesMap中,方便日后使用。
所有当需要发送命令的时候,系统先从NetconfController里的DevicesMap中获得对应的Device,然后根据得到的Device找到对应的NetconfSession。
在获得session以后,根据用户传入的信息构建对应的NETCONF命令。并通过NetconfSessionImpl中的sendRequest函数将命令发送给设备。
在sendRequest中,先对传入的request格式进行检查,确保符合NETCONF命令的规范。
确保request符合规范以后,使用request函数将request发送给设备。同时返回CompletableFuture futureReply。futureReply主要是用来读取设备返回的信息。如果设备无法在规定的时间内返回信息,则系统会抛出异常。
下面让我们看一下onos是如何发送信息的。
进入到NetconfSessionImpl的request函数:
3.2 设备返回信息读取
当NetconfStreamThread对象中的run函数接受到设备返回的信息以后,会对信息进行解析生成事件,发送给监听器,并将信息传达给CompletableFuture。
此时在主线程的sendRequest函数中,通过调用CompletableFuture的get方法获得返回信息。
至此,NETCONF命令的下发和信息的返回过程就结束了。
不知道我有没有讲清楚,欢迎大家拍砖讨论。