===========================================================================================
新的理解汇总本文:
- Rpc:提供服务的远程调用接口
- Notification:提供通知,可以发出通知和接收通知。
- DataStore:提供数据存储,读取,Transaction等功能。
- Request Routing:提供请求路由功能,把外部请求传送到正确的Plugin和Node instance处理。(请求路由)
- Node instance:Yang结构树上的节点实例
- Restconf Subsystem:自动定义和创建Restconf API的Plugin
- Config subsystem:提供统一的配置文件管理功能的Plugin
Rpc可以理解北向调用模型的相关服务接口;
Yang模型中有:container, list, leaf数据类型
===========================================================================================
概述
OpenDaylight的服务抽象层(SAL)早期的编程模型是API驱动(AD-SAL),北向API一一对应南向协议,使得AD-SAL开发的北向应用程序只能依赖唯一的南向协议,不具备通用性。后来编程模型改为模型驱动,即MD-SAL,本人主要接触的也是MD-SAL,并没有基于AD-SAL做过开发。MD-SAL通过Yang Model定义模型,北向应用程序与模型交互,模型与网络交互。
关于MD-SAL
- MD-SAL模型驱动的服务抽象层,为Apps和Plugins开发提供同意支持
- 提供Request Routing和用来实现抽象服务和相应API的基础框架
- 抽象服务和API都由各个Plugin通过Yang Model定义
- Yang Tools Plugin根据Yang Model的定义自动生成API,生成相应Java代码
- 开发者通过实现自动生成的Service Interface来实现具体的API功能和服务内容
- Plugin通过MD-SAL和生成的API(Rpc,Notification),DataStore去利用其他Plugin的服务和数据
- 所有功能模块的信息交互,数据存储调用都通过MD-SAL完成
名词解释
- Rpc:提供服务的远程调用接口
- Notification:提供通知,可以发出通知和接收通知。
- DataStore:提供数据存储,读取,Transaction等功能。
- Request Routing:提供请求路由功能,把外部请求传送到正确的Plugin和Node instance处理。(请求路由)
- Node instance:Yang结构树上的节点实例
- Restconf Subsystem:自动定义和创建Restconf API的Plugin
- Config subsystem:提供统一的配置文件管理功能的Plugin
MD-SAL Plugin
Plugin与MD-SAL的关联方式可分成两种:
1.BA(Binding-Aware) 使用Yang Model定义模型,并自动生成的Java Bindings的Plugin
2.BI(Binding-Independent) 与BA不同,不依赖于Java Bindings
Yang Model
Yang Model可以定义三种类型:Rpc,Notification,Data。
Rpc可以理解北向调用模型的相关服务接口;
Notification为通知,通常用于发送某种事件而广播通知,或订阅通知收到通知后的操作;
Data为数据,Yang Model中可以定义数据类型并存储数据,data分两种:Config和Operational。
Config为配置信息,北向应用向南向网络下发的配置数据信息,Operational为运行时信息,通常是ODL记录的南向的网络状态信息。
Yang Model详细语法规则,参照https://tools.ietf.org/html/rfc6020
Rpc
Rpc definitions in YANG
Toaster例子中,在toaster-api目录下的Yang文件中定义了三个rpc:make-toast, cancel-toast, restock-toaster。对应实现了三个功能:烤面包,取消烧烤,重新填装。烤面包操作需输入两个参数:熟的程度和面包类型。取消烧烤没有输入输出。重新填装需输入重新填装的数量。
Rpc registration
在api/目录下写完Yang Model后编译,自动生成Java Code, Rpc对应ToasterService
在toaster-impl/中需实现ToasterService接口
1 public class ToasterImpl implements ..., ToasterService, ...{}
然后在注册Rpc
1 @Override 2 public void onSessionInitiated(ProviderContext session){ 3 //Register the RPC Service 4 rpcReg = session.addRpcImplementation(ToasterService.class, this); 5 ... 6 }
Rpc implementation
最后实现Rpc的功能,e.g. make-toast
Notification
在Yang Model中定义
在SAL中注册
发送通知
1 if(outOfBread()) { 2 notificationService.publish(new ToasterOutOfBreadBuilder().build()); 3 }
编译api/,YangModel中定义的notification会自动生成Listener接口ToasterListener,订阅收到通知后的操作需实现该接口,如下是自动生成的ToasterListener代码。
DataStore
Yang Data Tree
MD-SAL中通过DataStore存储数据,与DataStore的交互通过DataBroker实现,DataStore中存储的数据发生变化会触发onDataChange()事件,在onDataChange()中响应DataStore的变化。数据在DataStore中以树形结构存储。 Yang模型中有:container, list, leaf数据类型。那么树形结构可能如:
使用DataBroker对DataStore进行数据读写,监听数据仓库变化事件。
- 注册监听器dataBroker.registerDataChangeListener(LogicalDataStoreType, InstanceIdentifier, dataChangeListener, DataChangeScope)
- LogicalDataStoreType:Configuration or Operational即DataStore中存储的数据类型,是Configuration或者Operational
- DataChangeScope:BASE(change in node only), ONE(change in node or direct children), SUBTREE(change in node or direct children or nested children)监听变化范围,BASE只监听当前节点变化事件,ONE监听当前节点和其左右儿子节点变化事件,SUBTREE监听当前节点和左右子树的所有变化事件。
- InstanceIdentifier是监听节点的身份标识。
- Implement the onDataChange(final AsyncDataChangeEvent, DataObject> change)实现onDataChange函数。
- AsyncDataChangeEvent has 4 APIs: getCreatedData, getUpdatedData, getOriginalData, getRemovedPaths.其作用是getCreatedData获取第一次创建DataStore并存入的数据。getUpdatedData获得更新后的数据,getOriginalData获得更新前的数据,getRemovedPaths获取删除以后的数据仓库。
- 事务有三种,读,写,读写:ReadTransaction/ReadWriteTransaction/WriteTransaction
监听DataStore变化
为监听节点,创建身份标识Instance Identifier:
public static final InstanceIdentifier<Toaster> TOASTER_IID = InstanceIdentifier.builder(Toaster.class).build();
使用DataBroker注册监听器
dcReg = dataService.registerDataChangeListener( LogicalDatastoreType.CONFIGURATION, TOASTER_IID, this, DataChangeScope.SUBTREE)
监听数据变化
@Override public void onDataChange(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change){ DataObject dataObject = change.getUpdatedSubtree(); if( dataObject instanceof Toaster) { Toaster toaster = (Toaster) dataObject; LOG.info("onDataChanged - new Toaster config: {}", toaster); } else { LOG.warn("onDataChange - not instance of Toaster {}", dataObject); } }
从DataStore读取数据
@Override public void onDataChange(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change){ DataObject dataObject = change.getUpdatedSubtree(); if( dataObject instanceof Toaster) { Toaster toaster = (Toaster) dataObject; LOG.info("onDataChanged - new Toaster config: {}", toaster); } else { LOG.warn("onDataChange - not instance of Toaster {}", dataObject); } }
l
向DataStore写入数据
1.Data:使用Yang模型自动生成的builder和setter方法创建数据。
2.Create:使用dataBroker创建writeTransaction
3.Put:将数据写入Transaction
4.Submit:提交Transaction
下面是同步方式写入
Toaster toaster = new ToasterBuilder().setToasterManufacturer( TOASTER_MANUFACTURER ).setToasterModelNumber(TOASTER_MODEL_NUMBER).setToasterStatus( ToasterStatus.Up).build();
WriteTransaction tx = dataService.newWriteOnlyTransaction();
tx.put(LogicalDatastoreType.OPERATIONAL, TOASTER_IID, toaster);
tx.submit();
下面是异步方式写入
总结
ODL wiki中Toaster是很好的入门OpenDayLight Plugin开发的例子,实现一遍Toaster,对刚刚入门的ODL学习者理解ODL非常受益,以上是我学习Toaster的一些总结,希望能给读者带来帮助。