zoukankan      html  css  js  c++  java
  • DIDM源码分析

    DIDM源码分析

    版本来源:GitHub上Opendaylight DIDM项目

    参考资料来源:DIDM:Developer Guide

    概述

    DIDM是设备标识与驱动管理(Device Identification and Driver Management)的缩写,其设计初衷是解决ODL控制器中设备相关功能的统一处理。所谓设备相关功能,是指不同设备执行相同特性功能时所存在的限制以及表现出的性能。例如,在ODL控制器中,最常见的特性需求是配置VLAN和调整流表,即使是这样的特性需求,每个设备厂商的实现也是有所区别的。对于上述厂商实现的特性需求,通常称之为设备驱动,设备驱动是与设备相关功能绑定的,使用这种绑定的前提是识别设备类型,即了解设备是何厂商制造。

    架构

    DIDM框架提供了以下功能:

    • 发现(Discovery) - 判断设备是否属于控制器控制,以及是否可以建立与控制器的连接,发现功能提供两种机制:1)使用OpenFlow协议,2)非OpenFlow设备通过手动方式,如GUI或REST API
    • 识别(Identification) – 判断设备类型
    • 驱动注册(Driver Registration) – 采用routed RPCs方式注册设备驱动
    • 同步(Synchronization) – 设备信息、配置、链接搜集
    • 通用特性的数据模型(Data Models for Common Features) – 定义通用特性(例如,VLAN配置)的数据模型,一旦定义所述数据模型,则对通用特性的操作可以通过将数据写入模型的方式进行
    • 通用特性的RPC(RPCs for Common Features) – 通用特性的API采用RPC方式,驱动实现RPC以支持相关特性

    集群支持

    DIDM由于是MD-SAL应用,因此可进行集群配置

    处理流程

    用于手动发现的API将在下个版本中提供

    1. 设备与控制器建立连接
    2. 在config和operational存储中创建资产节点
    3. 完成operational树中的创建后,识别管理(Identification Manager)将收到数据变更通知
    4. 识别管理将按照(Openflow,SNMP,若均不是则标识为unknown)的顺序从设备获取信息
    5. 完成识别后,将在operational树中增加设备类型信息
    6. 当应用设备类型信息时,设备驱动将收到相应的通知
    7. 驱动负责收集同步数据
    8. 驱动注册routed RPC或数据变更通知

    基本过程如下图所示:

    ident mrg

    接口参考文档

    访问http://${CONTROLLER-IP}:8181/apidoc/explorer/index.html,其中的DIDM部分可查阅REST

    源码分析

    项目代码结构如图所示:

    project code structure

    整个代码结构可以按加载内容分为两部分,第一部分是DIDM框架代码(除vendor外的目录),第二部分是DIDM驱动范例(包含了独立的features)。DIDM框架按照features下features.xml中的描述,如下所示,主要包含didm-identification-api、didm-identification-impl、didm-drivers-api上述3个bundle,分别对应识别管理与驱动接口两个部分功能。

    <feature name='odl-didm-identification-api' version='${project.version}' description='OpenDaylight :: didm identification :: api'>
      <feature version='${ofplugin.version}'>odl-openflowplugin-nsf-model</feature>
    
      <bundle>mvn:org.opendaylight.didm/didm-identification-api/${project.version}</bundle>
    </feature>
    <feature name='odl-didm-identification' version='${project.version}' description='OpenDaylight :: didm identification'>
      <feature version='${ofplugin.version}'>odl-openflowplugin-nsf-services</feature>
      <feature version='${snmp.version}'>odl-snmp-plugin</feature>
      <feature version='${project.version}'>odl-didm-identification-api</feature>
    
      <bundle>mvn:org.opendaylight.didm/didm-identification-impl/${project.version}</bundle>
      <configfile finalname="${config.configfile.directory}/didm-identification.xml">mvn:org.opendaylight.didm/didm-identification-impl/${project.version}/xml/config</configfile>
    </feature>
    <feature name='odl-didm-drivers-api' version='${project.version}' description='OpenDaylight :: didm drivers :: api'>
      <feature version='${ofplugin.version}'>odl-openflowplugin-nsf-model</feature>
    
      <bundle>mvn:org.opendaylight.didm/didm-drivers-api/${project.version}</bundle>
    </feature>
    

    identification

    包含identification/api与identification/impl两个模块,其中api为identification的数据模型定义,如DeviceTyps的定义、inventory node的扩展等,采用yang实现;impl为identification的处理流程,包含处理流程章节所述的步骤3~5,采用Java及yang实现。

    在api中定义了DIDM使用的DeviceTypes基本数据类型,在didm-identification.yang中有如下定义:

    identity device-type-base {
        description "base identity for all device type identifiers";
    }
    
    identity unknown-device-type {
        description "Indicates the device type could not be identified.";
        base device-type-base;
    }
    
    augment "/inv:nodes/inv:node" {
        ext:augment-identifier "device-type";
        leaf device-type {
            type identityref {
                base device-type-base;
            }
        }
    }
    

    如上述定义,基本数据类型为device-type-base,用于处理被成功识别的设备;而unkonwn-device-type在此基础上继承得到,处理无法识别的设备,同时使用augment,拓展了inventory node,为其增加了一个叶节点device-type,其中存储设备类型,也即可以从inventory中获得关于设备类型的信息。
    在inventory node增加设备类型信息的同时,DIDM还设计了一个存储完整设备信息的datastore,可以通过外部配置设备信息,在didm-device-types.yang中进行了定义,如下所示:

    container device-types {
        list device-type-info {
            key "device-type";
    
            leaf device-type {
                type identityref {
                    base id:device-type-base;
                }
                description "identifier for a list entry, the device type name.";
            }
    
            leaf openflow-manufacturer {
                type string;
                description "The openflow manufacturer of the device.";
            }
    
            leaf-list openflow-hardware {
                type string;
                description "The openflow hardware of the device.";
            }
    
            leaf-list sysoid {
                type string;
                description "The SNMP sysObjectId that uniquely identifies the device";
            }
        }
    }
    

    此datastore中定义包含信息有device-type(设备类型),同时也是该datastore的键值;openflow-manufacturer(设备厂商);openflow-hardware(硬件信息,通常应当是硬件地址信息,即MAC),注意硬件信息以列表呈现,即设备可能包含多个硬件;sysoid(SNMP相关信息),同样以列表呈现。

    在impl中,DeviceIdentificationManageMoudle.java是didm-identification-impl bundle的入口文件,主要作用在于其中的createInstance方法,如下所示,将创建DeviceIdentificationManager实例,此实例将负责处理设备的识别流程。

    @Override
    public java.lang.AutoCloseable createInstance() {
        LOG.trace("Creating DeviceIdentificationManager instance");
        return new org.opendaylight.didm.identification.impl.DeviceIdentificationManager(getDataBrokerDependency(), getRpcRegistryDependency());
    }
    

    按照处理流程章节中流程图,DeviceIdentificationManager应当以inventory中数据变更事件驱动,因此在创建其实例过程中主要是注册数据变化监听,如下所示:

    dataChangeListenerRegistration = dataBroker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, NODE_IID, this, AsyncDataBroker.DataChangeScope.BASE);
    if (dataChangeListenerRegistration == null) {
        LOG.error("Failed to register onDataChanged Listener");
    }
    

    当有数据变化消息时,会触发onDataChanged方法的调用,由onDataChanged调用handleDataCreated方法,handleDataCreated方法为接受的每个变化数据,启动一个线程,该线程的任务是等待250ms,再次读取inventory中的FlowCapableNode数据(从其注释看,这么做的原因是需要的信息附着到node上的速度较慢,需要等待250ms),使用键值为更新数据中的node。数据获得成功后,进入设备识别流程,调用方法identifyDevice。

    方法identifiyDevice实现了流程图中描述的步骤3~4,包括

    • 监听inventory node变化
    • 填充inventory中的DeviceType

    首先,从datastore DeviceTypes中读取已经配置的设备信息,包括类型,厂商,硬件信息,对OpenFlow信息进行匹配,匹配源来自node中的FlowCapableNode,如下所示:

    List<DeviceTypeInfo> dtiInfoList = readDeviceTypeInfoFromMdsalDataStore();
    
    // 1) check for OF match
    FlowCapableNode flowCapableNode = node.getAugmentation(FlowCapableNode.class);
    checkOFMatch(path,node,flowCapableNode,dtiInfoList);
    

    其中的匹配流程调用checkOFMatch方法,具体执行过程是将厂商及硬件信息进行比较,如厂商信息相同,且硬件信息包含在配置的信息中,则认为设备类型属于,如下所示:

    private void checkOFMatch(final InstanceIdentifier<Node> path, Node node, FlowCapableNode flowCapableNode, List<DeviceTypeInfo> dtiInfoList ){
    	 if (flowCapableNode != null) {
             String hardware = flowCapableNode.getHardware();
             String manufacturer = flowCapableNode.getManufacturer();
             String serialNumber = flowCapableNode.getSerialNumber();
             String software = flowCapableNode.getSoftware();
    
             LOG.debug("Node '{}' is FlowCapable ("{}", "{}", "{}", "{}")",
                     node.getId().getValue(), hardware, manufacturer, serialNumber, software);
    
             // TODO: is there a more efficient way to do this?
             for(DeviceTypeInfo dti: dtiInfoList) {
                 // if the manufacturer matches and there is a h/w match
                 if (manufacturer != null && (manufacturer.equals(dti.getOpenflowManufacturer()))) {
                     List<String> hardwareValues = dti.getOpenflowHardware();
                     if(hardwareValues != null && hardwareValues.contains(hardware)) {
                             setDeviceType(dti.getDeviceType(), path);
                             return;
                     }
                 }
             }
         }
    
    }
    

    设备匹配后,则调用setDeviceType对inventory node中的DeviceType进行填充。如下所示:

    final InstanceIdentifier<DeviceType> deviceTypePath = path.augmentation(DeviceType.class);
    final WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
    
    tx.merge(LogicalDatastoreType.OPERATIONAL, deviceTypePath, new DeviceTypeBuilder().setDeviceType(deviceType).build());
    

    接下来,验证sysoid,调用SNMPSevice的RPC方法获取了设备的sysoid,如下所示:

    FetchSysOid fetchSysOid = new FetchSysOid(rpcProviderRegistry);
    String sysOid = fetchSysOid.fetch(ipStr);
    

    获取sysoid后与配置信息进行比较,最终也会对inventory node中的DeviceType进行填充,上述流程保证了OpenFlow设备与SNMP设备均可以正确地添加DeviceType。

    当OpenFlow与SNMP方式均匹配失败,则设备将被填充未知设备类型,如下所示:

    setDeviceType(UNKNOWN_DEVICE_TYPE, path);
    

    drivers

    openflow-feature.yang定义了RPC adjustFlow,由各厂商的驱动实现此RPC,如下所示:

    rpc adjust-flow {
    description "Adjust the provided flow, if necessary, based on the node's actual capabilities.
                 Depending on the node's capabilities, multiple flows may be returned.";
    
    input {
    
        uses "inv:node-context-ref";
    
        container flow {
           description "The flow to be adjusted.";
           uses flow:flow;
        }
    }
    
    output {
       list flow {
          description "The node-compatible flow(s) equivalent to the input flow.";
          uses flow:flow;
       }
    }
    }
    

    上述RPC的入参为node-context-ref与flow,出参为flow列表,编译后产生标准接口OpenflowFeatureService,如下所示,厂商驱动将负责实现该接口,可以在vendor目录下查看hp与mininet的范例实现。

    public interface OpenflowFeatureService
        extends
        RpcService
    {
        /**
         * Adjust the provided flow, if necessary, based on the node's actual capabilities.
         * Depending on the node's capabilities, multiple flows may be returned.
         *
         */
        Future<RpcResult<AdjustFlowOutput>> adjustFlow(AdjustFlowInput input);
    
    }
    

    vendor

    在此目录下,以HP3800和mininet为范例,展示了厂商定制化的driver如何实现,可以作为指导性的开发样本,下面以HP3800为例简单说明。
    HP3800创建OpenFlowDeviceDriver实现DIDM中定义的driver,如下所示:

    /**
     * The HP 3800 OF driver does the following:
     *
     * 1. listen for node added/removed in inventory (future: filtered by device type)
     * 2. when a HP 3800 node is added, register the routed RPCs (other driver types may register as DCLs for a feature such as vlan)
     * 3. when a HP 3800 node is removed, close the RPC registration (and/or DCLs for other driver types)
     */
    public class OpenFlowDeviceDriver implements OpenflowFeatureService, DataChangeListener, AutoCloseable {
    

    OpenFlowDeviceDriver实现了OpenflowFeatureService、DataChangeListener和AutoCloseable三个接口,因此OpenFlowDeviceDriver具备了三个功能:

    • 实现OpenflowFeatureService中的adjustFlow方法
    • 监听inventory数据变化
    • 能够自动释放申请的资源

    OpenFlowDeviceDriver实例在创建时,就注册监听inventory node中的DeviceType数据变化,该数据变化来自identification中介绍的设备识别流程,如下所示:

    private static final InstanceIdentifier<DeviceType> PATH = InstanceIdentifier.builder(Nodes.class).child(Node.class).augmentation(DeviceType.class).build();
        
        ...
        
    public OpenFlowDeviceDriver(DataBroker dataBroker, RpcProviderRegistry rpcRegistry) {
         this.rpcRegistry = Preconditions.checkNotNull(rpcRegistry);
    
         // register listener for Node, in future should be filtered by device type
         // subscribe to be notified when a device-type augmentation is applied to an inventory node
         dataChangeListenerRegistration = dataBroker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, PATH, this, AsyncDataBroker.DataChangeScope.BASE);
    }
    

    当监听数据变化时,OpenFlowDeviceDriver在onDataChanged方法中对其持有的RPC进行相应操作,首先,当DeviceType变化为添加时,过滤属于HP3800的变化,并按node节点注册routed RPC,如下所示:

    if(createdData != null) {
        for (Map.Entry<InstanceIdentifier<?>, DataObject> entry : createdData.entrySet()) {
            DeviceType deviceType = (DeviceType)entry.getValue();
            if(isHP3800DeviceType(deviceType.getDeviceType())) {
                registerRpcService(entry.getKey().firstIdentifierOf(Node.class));
            }
        }
    }
    

    当DeviceType变化为移除时,同样的操作,此时需要按node移除注册,如下所示:

    if((removedPaths != null) && !removedPaths.isEmpty()) {
        for (InstanceIdentifier<?> removedPath : removedPaths) {
            DeviceType deviceType = (DeviceType)change.getOriginalData().get(removedPath);
            if(isHP3800DeviceType(deviceType.getDeviceType())) {
                closeRpcRegistration(removedPath.firstIdentifierOf(Node.class));
            }
        }
    }
    

    提供的范例代码中未对DeviceType变化为更新进行处理

    在注册RPC后,HP3800提供的RPC服务正式可用,HP3800中的adjustFlow方法只能提供简单的实现,并无实际功能,如下所示:

    @Override
    public Future<RpcResult<AdjustFlowOutput>> adjustFlow(AdjustFlowInput input) {
        LOG.debug("HP 3800 adjustFlow");
    
        // TODO: should this be a deep copy?
        List<Flow> adjustedFlows = ImmutableList.of(new FlowBuilder(input.getFlow()).build());
    
        // TODO: finish this method, but for now just return the same flow that was receive
        AdjustFlowOutput output = new AdjustFlowOutputBuilder().setFlow(adjustedFlows).build();
        return Futures.immediateFuture(RpcResultBuilder.success(output).build());
    }
    

    state

    除去上述identificationdriversvendor外,DIDM项目下的state对inventory node进行了一进步扩展,在didm-state.yang定义了node状态,如下所示:

    augment "/inv:nodes/inv:node" {
            ext:augment-identifier "device-state";
            description "provides an augmentation on the ODL inventory node that provides
                    state information for a device.";
    
            container managed {
                description "state indicating if a given device is being actively managed
                            by the controller.";
                leaf managed {
                    type boolean;
                }
                uses change_info;
            }
            container synchronize {
                description "state indicating in which phase of synchronization the device
                            is currently";
                leaf state {
                    type enumeration {
                        enum unsynchronized {
                            description "No attempt has been made to synchronize device or for some reason,
                                                    such as loss of communications, the device is considered out of
                                                    synchronization";
                        }
                        enum synchronizing {
                            description "Device is currently being synchronized.";
                        }
                        enum synchronized {
                            description "Device has been successfully synchronized;";
                        }
                        enum error {
                            description "An error was encountered during last synchronization attempt.";
                        }
                    }
                }
                uses attempt_info;
            }
            container communication {
                description "state indicating if communications to the device are functional.";
                leaf state {
                    type enumeration {
                        enum up {
                            description "Communications are known to be functional.";
                        }
                        enum down {
                            description "Communications are known to be non-functional.";
                        }
                    }
                }
                uses attempt_info;
            }
        }
    

    其中定义了若干中状态,这些状态应当是DIDM完成设备识别及各厂商驱动完成注册后,填充至inventory node中,类似DeviceType的操作方式,目前GitHub上的版本未包含该部分代码将如何使用的部分,从描述性信息分析应当是由各厂商驱动负责处理,完成流程处理图中的步骤6,此部分内容应当能在ODL提供的发布版本中可以找到。

    结论

    DIDM实现功能比较简单,主要是通过对inventory node的拓展来实现附加信息的实现,同时注册routed RPC提供基于设备绑定的功能调用。

    存在的问题

    • 从目前获得源码结构看,尚有两点在源码中没有体现,分别是处理流程图中步骤1与步骤6
    • DeviceTypes信息的来源不清楚,个人认为配置是一种比较可能的数据来源,即每一种设备的元数据(制造商、硬件信息等)应当依赖外部导入
    • DIDM目前只实现了其描述举例中的调整流接口,即adjustFlow,而对VLAN配置没有提及
    • 对于adjustFlow的使用没有明确的说明,从现在给出的接口,不太清楚这样的流调整对上层应用究竟有何意义,是否上层应用直接调用adjustFlow接口就可以完成流表修改

    由于环境原因,为使编译通过DIDM代码,对项目pom进行了修改,使用自建镜像服务器

  • 相关阅读:
    火狐优化及遇到的问题
    拷贝工程,名字不改变问题
    Ajax基础实例
    Java中检测字符串的编码格式
    innerHTML和outerHTML有什么区别
    启动Tomcat出现“Bad version number in .class file (unable to load class XXX)”解决
    MyEclipse8.6安装SVN 教程 与遇到的问题
    彻底卸载JDK的-并只依赖配置环境安装JDK(不依赖注册表)-解决Error opening registry key'softwareJavasoftJava Runti问题
    数据库下的分页代码
    WSGI
  • 原文地址:https://www.cnblogs.com/hxfirefox/p/4652323.html
Copyright © 2011-2022 走看看