Openflow Plugin学习笔记1
主入口
ConfigurableOpenFlowProviderModule是OpenFlowPlugin中启动加载的入口,如下:
@Override
public java.lang.AutoCloseable createInstance() {
pluginProvider = new OpenflowPluginProvider();
pluginProvider.setDataBroker(getDataBrokerDependency());
pluginProvider.setNotificationService(getNotificationServiceDependency());
pluginProvider.setRpcRegistry(getRpcRegistryDependency());
pluginProvider.setSwitchConnectionProviders(getOpenflowSwitchConnectionProviderDependency());
pluginProvider.setRole(getRole()); // 此时获得的role为缺省值NOCHANGE,该值位于AbstractConfigurableOpenFlowProviderModule中
pluginProvider.initialization();
return pluginProvider;
}
其中创建了一个OpenflowPluginProvider,即OpenflowPlugin功能实际提供者,其中调用的initialization方法将服务进行了初始化,如下:
public void initialization() {
messageCountProvider = new MessageSpyCounterImpl();
extensionConverterManager = new ExtensionConverterManagerImpl();
roleManager = new OFRoleManager(OFSessionUtil.getSessionManager());
LOG.debug("dependencies gathered..");
registrationManager = new SalRegistrationManager();
registrationManager.setDataService(dataBroker);
registrationManager.setPublishService(notificationService);
registrationManager.setRpcProviderRegistry(rpcRegistry);
registrationManager.init();
mdController = new MDController();
mdController.setSwitchConnectionProviders(switchConnectionProviders);
mdController.setMessageSpyCounter(messageCountProvider);
mdController.setExtensionConverterProvider(extensionConverterManager);
mdController.init();
mdController.start();
}
下行通道
其中可大体分成两部分,一部分是SalRegistrationManager,另一部分则是MDController。
SalRegistrationManager可以理解为管理了控制器到交换机的下行通道。
在SalRegistrationManager中init方法,注册了SessionListener,当控制器与交换机连接会话发生变化时,会触发onSessionAdded和onSessionRemoved方法,如下:
@Override
public void onSessionAdded(final SwitchSessionKeyOF sessionKey, final SessionContext context) {
GetFeaturesOutput features = context.getFeatures(); // 获取OFPT_FEATURES_REPLY
BigInteger datapathId = features.getDatapathId(); // 从OFPT_FEATURES_REPLY中获取dpid
InstanceIdentifier<Node> identifier = identifierFromDatapathId(datapathId);
NodeRef nodeRef = new NodeRef(identifier);
NodeId nodeId = nodeIdFromDatapathId(datapathId);
ModelDrivenSwitchImpl ofSwitch = new ModelDrivenSwitchImpl(nodeId, identifier, context); // 创建交换机实例
CompositeObjectRegistration<ModelDrivenSwitch> registration =
ofSwitch.register(rpcProviderRegistry); // 注册rpc调用
context.setProviderRegistration(registration);
LOG.debug("ModelDrivenSwitch for {} registered to MD-SAL.", datapathId);
NotificationQueueWrapper wrappedNotification = new NotificationQueueWrapper(
nodeAdded(ofSwitch, features, nodeRef),
context.getFeatures().getVersion());
context.getNotificationEnqueuer().enqueueNotification(wrappedNotification);
}
可以认为每一台交换机与控制器建立连接后,控制器都会为其创建一个ModelDrivenSwitchImpl实例,并为其注册相应的rpc,而ModelDrivenSwitchImpl则实现了多个rpc接口,继承如下:
public interface ModelDrivenSwitch
extends
SalGroupService,
SalFlowService,
SalMeterService, SalTableService, SalPortService, PacketProcessingService, NodeConfigService,
OpendaylightGroupStatisticsService, OpendaylightMeterStatisticsService, OpendaylightFlowStatisticsService,
OpendaylightPortStatisticsService, OpendaylightFlowTableStatisticsService, OpendaylightQueueStatisticsService,
Identifiable<InstanceIdentifier<Node>>
而AbstractModelDrivenSwitch则实现ModelDrivenSwitch,如下:
public abstract class AbstractModelDrivenSwitch implements ModelDrivenSwitch
在AbstractModelDrivenSwitch中,注册了所有的rpc实现为AbstractModelDrivenSwitch,由于此类为抽象类,因此具体方法的实现将由实现类完成,即ModelDrivenSwitchImpl,如下:
@Override
public CompositeObjectRegistration<ModelDrivenSwitch> register(RpcProviderRegistry rpcProviderRegistry) {
CompositeObjectRegistrationBuilder<ModelDrivenSwitch> builder = CompositeObjectRegistration
.<ModelDrivenSwitch> builderFor(this);
final RoutedRpcRegistration<SalFlowService> flowRegistration = rpcProviderRegistry.addRoutedRpcImplementation(SalFlowService.class, this);
flowRegistration.registerPath(NodeContext.class, getIdentifier()); // 将rpc路由表中的数据进行更新,getIdentifier() 方法带入更新节点的path
builder.add(flowRegistration);
...
}
上述rpc接口的实现均在ModelDrivenSwitchImpl中,以SalFlowService为例,如下:
@Override
public Future<RpcResult<AddFlowOutput>> addFlow(final AddFlowInput input) {
LOG.debug("Calling the FlowMod RPC method on MessageDispatchService");
// use primary connection
SwitchConnectionDistinguisher cookie = null;
OFRpcTask<AddFlowInput, RpcResult<UpdateFlowOutput>> task =
OFRpcTaskFactory.createAddFlowTask(rpcTaskContext, input, cookie);
ListenableFuture<RpcResult<UpdateFlowOutput>> result = task.submit();
return Futures.transform(result, OFRpcFutureResultTransformFactory.createForAddFlowOutput());
}
上行通道
MDController负责控制器与交换机的信令交互,即非流表、组表消息的交互,可以理解为控制器与交换机的上行通道管理。在init方法中,交换机与控制器消息处理实现被添加到映射中,如下:
OpenflowPortsUtil.init(); // 完成协议中端口定义的映射
...
// 每个translator对应了Openflow协议中一种消息,负责将Of消息转换为MD-SAL中的各个notification
addMessageTranslator(ErrorMessage.class, OF10, new ErrorV10Translator());
addMessageTranslator(ErrorMessage.class, OF13, new ErrorTranslator());
addMessageTranslator(FlowRemovedMessage.class, OF10, new FlowRemovedTranslator());
addMessageTranslator(FlowRemovedMessage.class, OF13, new FlowRemovedTranslator());
...
// 制定了每种notification的通用pulisher
addMessagePopListener(NodeErrorNotification.class, notificationPopListener);
addMessagePopListener(BadActionErrorNotification.class, notificationPopListener);
addMessagePopListener(BadInstructionErrorNotification.class, notificationPopListener);
addMessagePopListener(BadMatchErrorNotification.class, notificationPopListener);
在init后,调用start方法创建SwitchConnectionHandlerImpl负责与处理交换机连接,start方法会启动一系列SwitchConnectionHandler,这些SwitchConnectionHandler会依次处理连接,以找到一个合适的,如下:
List<ListenableFuture<Boolean>> starterChain = new ArrayList<>(switchConnectionProviders.size());
for (SwitchConnectionProvider switchConnectionPrv : switchConnectionProviders) {
switchConnectionPrv.setSwitchConnectionHandler(switchConnectionHandler);
ListenableFuture<Boolean> isOnlineFuture = switchConnectionPrv.startup();
starterChain.add(isOnlineFuture);
}
角色管理
除去SalRegistrationManager与MDController,OpenflowPluginProvider还创建了一个OFRoleManager实例,在OpenflowPluginProvider中与Role相关的方法如下:
/**
* @param role of instance
*/
public void setRole(OfpRole role) {
this.role = role;
}
/**
* @param newRole
*/
public void fireRoleChange(OfpRole newRole) {
if (!role.equals(newRole)) {
LOG.debug("my role was chaged from {} to {}", role, newRole);
role = newRole;
switch (role) {
case BECOMEMASTER:
//TODO: implement appropriate action
roleManager.manageRoleChange(role);
break;
case BECOMESLAVE:
//TODO: implement appropriate action
roleManager.manageRoleChange(role);
break;
case NOCHANGE:
//TODO: implement appropriate action
roleManager.manageRoleChange(role);
break;
default:
LOG.warn("role not supported: {}", role);
break;
}
}
}
从方法实现的角度看,似乎role在OpenflowPluginProvider中是一个集中控制的对象,并非与交换机节点绑定,即对于一定区域内的所有交换机而言,只能出现一个一个master,而不能出现某个交换机在某个控制器上为master的情况。从OFRoleManager提供的方法看似乎也验证了这一点,如下:
/**
* @param sessionManager
*/
public OFRoleManager(final SessionManager sessionManager) {
Preconditions.checkNotNull("Session manager can not be empty.", sessionManager);
this.sessionManager = sessionManager;
workQueue = new PriorityBlockingQueue<>(500, new Comparator<RolePushTask>() { // 队列容量为500
@Override
public int compare(final RolePushTask o1, final RolePushTask o2) {
return Integer.compare(o1.getPriority(), o2.getPriority()); // 按优先级排列的队列
}
});
ThreadPoolLoggingExecutor delegate = new ThreadPoolLoggingExecutor(
1, 1, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1), "ofRoleBroadcast");
broadcastPool = MoreExecutors.listeningDecorator(
delegate);
}
/**
* change role on each connected device
*
* @param role
*/
public void manageRoleChange(final OfpRole role) {
for (final SessionContext session : sessionManager.getAllSessions()) { // 遍历所有的连接会话
try {
workQueue.put(new RolePushTask(role, session));
} catch (InterruptedException e) {
LOG.warn("Processing of role request failed while enqueueing role task: {}", e.getMessage());
}
}
while (!workQueue.isEmpty()) {
RolePushTask task = workQueue.poll();
ListenableFuture<Boolean> rolePushResult = broadcastPool.submit(task); // 该方法会调用RolePushTask中的call方法
CheckedFuture<Boolean, RolePushException> rolePushResultChecked =
RoleUtil.makeCheckedRuleRequestFxResult(rolePushResult);
try {
Boolean succeeded = rolePushResultChecked.checkedGet(TIMEOUT, TIMEOUT_UNIT);
if (!MoreObjects.firstNonNull(succeeded, Boolean.FALSE)) {
if (task.getRetryCounter() < RETRY_LIMIT) {
workQueue.offer(task); // 修改失败role且失败次数小于重试次数的会话重新存入queue
}
}
} catch (RolePushException | TimeoutException e) {
LOG.warn("failed to process role request: {}", e);
}
}
}
RolePushTask中的call方法将发送RoleRequest,如下:
generationId = RoleUtil.getNextGenerationId(generationId);
// try to possess role on device
Future<RpcResult<RoleRequestOutput>> roleReply = RoleUtil.sendRoleChangeRequest(session, role, generationId);
// flush election result with barrier
BarrierInput barrierInput = MessageFactory.createBarrier(
session.getFeatures().getVersion(), session.getNextXid());
Future<RpcResult<BarrierOutput>> barrierResult = session.getPrimaryConductor().getConnectionAdapter().barrier(barrierInput);
改变Role的顶层调用在OpenflowPluginProvider的fireRoleChange方法中,如下,该方法只在ConfigurableOpenFlowProviderModule中reuseInstance被调用
public void fireRoleChange(OfpRole newRole) {
if (!role.equals(newRole)) {
LOG.debug("my role was chaged from {} to {}", role, newRole);
role = newRole;
switch (role) {
case BECOMEMASTER:
//TODO: implement appropriate action
roleManager.manageRoleChange(role);
break;
case BECOMESLAVE:
//TODO: implement appropriate action
roleManager.manageRoleChange(role);
break;
case NOCHANGE:
//TODO: implement appropriate action
roleManager.manageRoleChange(role);
break;
default:
LOG.warn("role not supported: {}", role);
break;
}
}
}
从代码看目前对于角色转换的功能是需要开发者加入的,并从外部调用实现交换机角色的切换,且更倾向于主备的实现方式。
Openflow消息转译
MDController中注册了各种Openflow协议消息的处理器,这些处理器均继承自IMDMessageTranslator<I, O>,这是一个翻译器,所有到MD-SAL或往MD-SAL的消息都由它处理,它只有一个方法,如下:
/**
* This method is called in order to translate message to MD-SAL or from MD-SAL.
*
* @param cookie
* auxiliary connection identifier
* @param sc
* The SessionContext which sent the OF message
* @param msg
* The OF message
*
* @return translated message
*/
O translate(SwitchConnectionDistinguisher cookie, SessionContext sc, I msg);
其中cookie一般不会被使用,translate方法可以参照以下步骤实现:
@Override
public List<DataObject> translate(SwitchConnectionDistinguisher cookie,
SessionContext sc, OfHeader msg) {
if(msg instanceof OF_MESSAGE.class) { // 判断消息是否属于要处理的消息类型
// 按消息格式填充各个字段
...
return list;
} else {
return Collections.emptyList(); // 处理出错则返回一个空的列表
}
}
MDController
方法start创建了SwitchConnectionHandlerImpl对象,此处似乎与上图不符,如下:
public void start() {
LOG.debug("starting ..");
LOG.debug("switchConnectionProvider: " + switchConnectionProviders);
// setup handler
SwitchConnectionHandlerImpl switchConnectionHandler = new SwitchConnectionHandlerImpl(); // 实际创建了一个queuekeeper
switchConnectionHandler.setMessageSpy(messageSpyCounter);
errorHandler = new ErrorHandlerSimpleImpl(); // 为捕获异常创建实例
switchConnectionHandler.setErrorHandler(errorHandler);
switchConnectionHandler.init(); // 经过调用,真正地注册了translator和popListener
List<ListenableFuture<Boolean>> starterChain = new ArrayList<>(switchConnectionProviders.size());
for (SwitchConnectionProvider switchConnectionPrv : switchConnectionProviders) {
switchConnectionPrv.setSwitchConnectionHandler(switchConnectionHandler);
ListenableFuture<Boolean> isOnlineFuture = switchConnectionPrv.startup();
starterChain.add(isOnlineFuture);
}
Future<List<Boolean>> srvStarted = Futures.allAsList(starterChain);
}
Openflow Plugin学习笔记1的更多相关文章
- Openflow Plugin学习笔记3
MDController.java 中的start方法,创建了SwitchConnectionHandlerImpl实例 SwitchConnectionHandlerImpl switchConne ...
- Openflow Plugin学习笔记2
OpenDaylight OpenFlow Plugin 过载保护 过载保护 OF Plugin中的过载保护按如下流程工作: ConnectionConductor将消息送入队列,是最靠近OFJava ...
- OpenFlow Switch学习笔记(五)——Group Table、Meter Table及Counters
本文主要详述OpenFlow Switch的另外两个主要组件——Group Table和Meter Table,它们在整个OpenFlow Swtich Processing中也起到了重要作用. 1. ...
- OpenFlow Switch学习笔记(四)——Matching
这次我们着重详述来自于网络中的数据包在OpenFlow Switch中与Flow Entries的具体匹配过程,以及当出现Table Miss时的处理方式,下面就将从这两方面说起. 1.Matchin ...
- OpenFlow Switch学习笔记(一)——基础概念
OpenFlow Switch v1.4.0规范是在2013年10月14号发布,规范涵盖了OpenFlow Switch各个组件的功能定义.Controller与Switch之间的通信协议Open F ...
- OpenFlow Switch学习笔记(七)——Matching Fields
Matching Fields in_port=port Matches OpenFlow port port dl_vlan=vlan Matches IEEE 802.1q Virtual LAN ...
- OpenFlow Switch学习笔记(六)——Instructions和Actions
本文主要重点讨论OpenFlow Switch规范的指令集,它们深刻影响着数据包在Switch中的处理行为,下面开始从以下几个部分谈起. 1.Instructions 每一个Flow Entry里都包 ...
- OpenFlow Switch学习笔记(三)——Flow Tables
这次我们主要讨论下OpenFlow Switch的核心组件之一——Flow Tables,以了解其内部的 matching 以及 action handling 机制.下文将会分为几个部分来逐步详述O ...
- OpenFlow Switch学习笔记(二)——OpenFlow Ports
OpenFlow Ports是OpenFlow Switch与剩余网络之间传递Packet的网络接口.OpenFlow Switches之间通过OpenFlow Ports彼此相互逻辑连接.一个Ope ...
随机推荐
- bl bl bl bl bl
package com.dh.activiti; import org.springframework.web.servlet.HandlerInterceptor; import org.sprin ...
- paoding rose controller包及文件名命名规则
1.包命名规则:xxx.xxx.controllers(否则扫描不到)
- sleep() 与 wait()的比较
1.这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类. sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用了b的sleep方法,实际上还 ...
- 超链接提示效果jQuery+CSS+html
我们知道浏览器自带了超链接提示, 只需要在超链接中加入 title 属性就可以了. <a href="#" title="吉大砍人案致1死1伤 受害者死前大喊他手里 ...
- 【刷题】BZOJ 4196 [Noi2015]软件包管理器
Description Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖( ...
- [APIO2014]回文串 manacher 后缀数组
题面:洛谷 题解: 还是这个性质:对于每个串而言,本质不同的回文串最多只有O(n)个. 所以我们先求出这O(n)个本质不同的回文串,然后对整个串求一次sa. 然后对于每个回文串,求出它的出现次数,更新 ...
- 【BZOJ5306】[HAOI2018]染色(NTT)
[BZOJ5306]染色(NTT) 题面 BZOJ 洛谷 题解 我们只需要考虑每一个\(W[i]\)的贡献就好了 令\(lim=min(M,\frac{N}{S})\) 那么,开始考虑每一个\(W[i ...
- Unity3D手游开发日记(5) - 适合移动平台的植被随风摆动
一直在思考怎么让场景更有生机,我觉得植被的随风摆动是必不可少的.CE3引擎的植被bending就做得特别棒.我也准备在手机上做一套. 先分析一下植被摆动常见的几种做法.其实不管哪种做法,核心就是让植被 ...
- python基础(4)
条件判断和循环 条件判断 计算机之所以能做很多自动化的任务,因为它可以自己做条件判断. 比如,输入用户年龄,根据年龄打印不同的内容,在Python程序中,用if语句实现: age = 20 if ag ...
- 【bzoj4559】成绩比较
Portal -->bzoj4559 补档计划 借这题补个档--拉格朗日插值 插值的话大概就是有一个\(n-1\)次多项式\(A(x)\),你只知道它在\(n\)处的点值,分别是\ ...