主入口

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的更多相关文章

  1. Openflow Plugin学习笔记3

    MDController.java 中的start方法,创建了SwitchConnectionHandlerImpl实例 SwitchConnectionHandlerImpl switchConne ...

  2. Openflow Plugin学习笔记2

    OpenDaylight OpenFlow Plugin 过载保护 过载保护 OF Plugin中的过载保护按如下流程工作: ConnectionConductor将消息送入队列,是最靠近OFJava ...

  3. OpenFlow Switch学习笔记(五)——Group Table、Meter Table及Counters

    本文主要详述OpenFlow Switch的另外两个主要组件——Group Table和Meter Table,它们在整个OpenFlow Swtich Processing中也起到了重要作用. 1. ...

  4. OpenFlow Switch学习笔记(四)——Matching

    这次我们着重详述来自于网络中的数据包在OpenFlow Switch中与Flow Entries的具体匹配过程,以及当出现Table Miss时的处理方式,下面就将从这两方面说起. 1.Matchin ...

  5. OpenFlow Switch学习笔记(一)——基础概念

    OpenFlow Switch v1.4.0规范是在2013年10月14号发布,规范涵盖了OpenFlow Switch各个组件的功能定义.Controller与Switch之间的通信协议Open F ...

  6. OpenFlow Switch学习笔记(七)——Matching Fields

    Matching Fields in_port=port Matches OpenFlow port port dl_vlan=vlan Matches IEEE 802.1q Virtual LAN ...

  7. OpenFlow Switch学习笔记(六)——Instructions和Actions

    本文主要重点讨论OpenFlow Switch规范的指令集,它们深刻影响着数据包在Switch中的处理行为,下面开始从以下几个部分谈起. 1.Instructions 每一个Flow Entry里都包 ...

  8. OpenFlow Switch学习笔记(三)——Flow Tables

    这次我们主要讨论下OpenFlow Switch的核心组件之一——Flow Tables,以了解其内部的 matching 以及 action handling 机制.下文将会分为几个部分来逐步详述O ...

  9. OpenFlow Switch学习笔记(二)——OpenFlow Ports

    OpenFlow Ports是OpenFlow Switch与剩余网络之间传递Packet的网络接口.OpenFlow Switches之间通过OpenFlow Ports彼此相互逻辑连接.一个Ope ...

随机推荐

  1. Java web 强制301跳转

    response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); response.setHeader("Location" ...

  2. Mybatis 映射关系

    相比 Hibernate,Mybatis 的映射关系就显得简单了很多. 未完待续....

  3. RPC和WebService的区别

    最近分析的这个系统,逻辑架构中有一层是RPC interface.之前对RPC不熟悉,就上网搜索了一下资料,在此总结一下: RPC是Remote Procedure Calling,远程过程调用的缩写 ...

  4. Css实现拖动效果

    效果如下,可以拖动滑块,数字显示的是离左侧距离:

  5. bzoj1007-水平可见直线

    题目 在平面直角坐标系上以\(y=kx+b\)的形式给出\(n (n\le 50000)\)条直线,求从无限高的地方能看到多少条直线. 分析 举几个例子发现我们要求的直线组成一个下凸的形状.所以我们只 ...

  6. BZOJ 3626 LCA(离线+树链剖分)

    首先注意到这样一个事实. 树上两个点(u,v)的LCA的深度,可以转化为先将u到根路径点权都加1,然后求v到根路径上的总点权值. 并且该题支持离线.那么我们可以把一个区间询问拆成两个前缀和形式的询问. ...

  7. MSSQL数据库分页存储过程

    create procedure [dbo].[p_splitpage] ), , , output, output as set nocount on declare @p1 int ,,@rowc ...

  8. 洛谷 P4139 上帝与集合的正确用法

    题目描述 根据一些书上的记载,上帝的一次失败的创世经历是这样的: 第一天, 上帝创造了一个世界的基本元素,称做“元”. 第二天, 上帝创造了一个新的元素,称作“α”.“α”被定义为“元”构成的集合.容 ...

  9. (转)linux下压缩和归档相关命令tar,zip,gzip,bzip2

    压缩包也有两种形式,一种是tar.gz包(.tgz包也是这种),一种是tar.bz2包. tar.gz包的解压方法:tar zxvf [PackageName].tar.gz tar.bz2包的解压方 ...

  10. selenium测试 - open Firefox

    环境:Python2.7+selenium3+Firefox47   问题1: 在打开火狐浏览器时报错:‘geckodriver‘ executable needs to be in PATH fro ...