热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

OpenflowPlugin学习笔记1

ConfigurableOpenFlowProviderModule是OpenFlowPlugin中启动加载的入口,如下:@Overridepublicjava.lang.Auto

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 identifier = identifierFromDatapathId(datapathId);
    NodeRef nodeRef = new NodeRef(identifier);
    NodeId nodeId = nodeIdFromDatapathId(datapathId);
    ModelDrivenSwitchImpl ofSwitch = new ModelDrivenSwitchImpl(nodeId, identifier, context); // 创建交换机实例
    CompositeObjectRegistration 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>

而AbstractModelDrivenSwitch则实现ModelDrivenSwitch,如下:

public abstract class AbstractModelDrivenSwitch implements ModelDrivenSwitch

在AbstractModelDrivenSwitch中,注册了所有的rpc实现为AbstractModelDrivenSwitch,由于此类为抽象类,因此具体方法的实现将由实现类完成,即ModelDrivenSwitchImpl,如下:

 @Override
public CompositeObjectRegistration register(RpcProviderRegistry rpcProviderRegistry) {
    CompositeObjectRegistrationBuilder builder = CompositeObjectRegistration
            . builderFor(this);

    final RoutedRpcRegistration flowRegistration = rpcProviderRegistry.addRoutedRpcImplementation(SalFlowService.class, this);
    flowRegistration.registerPath(NodeContext.class, getIdentifier()); // 将rpc路由表中的数据进行更新,getIdentifier() 方法带入更新节点的path
    builder.add(flowRegistration);
    
    ...
}

上述rpc接口的实现均在ModelDrivenSwitchImpl中,以SalFlowService为例,如下:

@Override
public Future> addFlow(final AddFlowInput input) {
    LOG.debug("Calling the FlowMod RPC method on MessageDispatchService");
    // use primary connection
    SwitchConnectionDistinguisher COOKIE = null;

    OFRpcTask> task =
            OFRpcTaskFactory.createAddFlowTask(rpcTaskContext, input, COOKIE);
    ListenableFuture> 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> starterChain = new ArrayList<>(switchConnectionProviders.size());
for (SwitchConnectionProvider switchConnectionPrv : switchConnectionProviders) {
    switchConnectionPrv.setSwitchConnectionHandler(switchConnectionHandler);
    ListenableFuture 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() { // 队列容量为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(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 rolePushResult = broadcastPool.submit(task); // 该方法会调用RolePushTask中的call方法
        CheckedFuture rolePushResultChecked =
                RoleUtil.makeCheckedRuleRequestFxResult(rolePushResult);
        try {
            Boolean succeeded = rolePushResultChecked.checkedGet(TIMEOUT, TIMEOUT_UNIT);
            if (!MoreObjects.firstNonNull(succeeded, Boolean.FALSE)) {
                if (task.getRetryCounter() 

RolePushTask中的call方法将发送RoleRequest,如下:

generatiOnId= RoleUtil.getNextGenerationId(generationId);

// try to possess role on device
Future> roleReply = RoleUtil.sendRoleChangeRequest(session, role, generationId);
// flush election result with barrier
BarrierInput barrierInput = MessageFactory.createBarrier(
        session.getFeatures().getVersion(), session.getNextXid());
Future> 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,这是一个翻译器,所有到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 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> starterChain = new ArrayList<>(switchConnectionProviders.size());
    for (SwitchConnectionProvider switchConnectionPrv : switchConnectionProviders) {
        switchConnectionPrv.setSwitchConnectionHandler(switchConnectionHandler);
        ListenableFuture isOnlineFuture= switchConnectionPrv.startup();
        starterChain.add(isOnlineFuture);
    }

    Future> srvStarted = Futures.allAsList(starterChain);
}

推荐阅读
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • React项目中运用React技巧解决实际问题的总结
    本文总结了在React项目中如何运用React技巧解决一些实际问题,包括取消请求和页面卸载的关联,利用useEffect和AbortController等技术实现请求的取消。文章中的代码是简化后的例子,但思想是相通的。 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
author-avatar
成事r2003
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有