==== Chapter Overview In the most recent OpenDaylight release, the opendaylight-inventory model is marked as deprecated. To facilitate migration from it to the network-topology model, there were requests to render (translate) data from inventory model (whether augmented or not) to another model for further processing. The Topology Processing Framework was extended to provide this functionality by implementing several rendering-specific classes. This chapter is a step-by-step guide on how to implement your own topology rendering using our inventory rendering as an example. ==== Use case For the purpose of this guide we are going to render the following augmented fields from the OpenFlow model: * from inventory node: ** manufacturer ** hardware ** software ** serial-number ** description ** ip-address * from inventory node-connector: ** name ** hardware-address ** current-speed ** maximum-speed We also want to preserve the node ID and termination-point ID from opendaylight-topology-inventory model, which is network-topology part of the inventory model. ==== Implementation There are two ways to implement support for your specific topology rendering: * add a module to your project that depends on the Topology Processing Framework * add a module to the Topology Processing Framework itself Regardless, a successful implementation must complete all of the following steps. ===== Step1 - Target Model Creation Because the network-topology node does not have fields to store all desired data, it is necessary to create new model to render this extra data in to. For this guide we created the inventory-rendering model. The picture below shows how data will be rendered and stored. .Rendering to the inventory-rendering model image::topoprocessing/Inventory_Rendering_Use_case.png[width=500] IMPORTANT: When implementing your version of the topology-rendering model in the Topology Processing Framework, the source file of the model (.yang) must be saved in /topoprocessing-api/src/main/yang folder so corresponding structures can be generated during build and can be accessed from every module through dependencies. When the target model is created you have to add an identifier through which you can set your new model as output model. To do that you have to add another identity item to topology-correlation.yang file. For our inventory-rendering model identity looks like this: [source,yang] ---- identity inventory-rendering-model { description "inventory-rendering.yang"; base model; } ---- After that you will be able to set inventory-rendering-model as output model in XML. ===== Step2 - Module and Feature Creation IMPORTANT: This and following steps are based on the <<_model_specific_approach,model specific approach>> in the Topology Processing Framework. We highly recommend that you familiarize yourself with this approach in advance. To create a base module and add it as a feature to Karaf in the Topology Processing Framework we made the changes in following https://git.opendaylight.org/gerrit/#/c/26223/[commit]. Changes in other projects will likely be similar. [options="header"] |====== |File |Changes |pom.xml |add new module to topoprocessing |features.xml |add feature to topoprocessing |features/pom.xml |add dependencies needed by features |topoprocessing-artifacts/pom.xml |add artifact |topoprocessing-config/pom.xml |add configuration file |81-topoprocessing-inventory-rendering-config.xml |configuration file for new module |topoprocessing-inventory-rendering/pom.xml |main pom for new module |TopoProcessingProviderIR.java |contains startup method which register new model adapter |TopoProcessingProviderIRModule.java |generated class which contains createInstance method. You should call your startup method from here. |TopoProcessingProviderIRModuleFactory.java |generated class. You will probably not need to edit this file |log4j.xml |configuration file for logger topoprocessing-inventory-rendering-provider-impl.yang|main yang module. Generated classes are generated according to this yang file |====== ===== Step3 - Module Adapters Creation There are seven mandatory interfaces or abstract classes that needs to be implemented in each module. They are: * TopoProcessingProvider - provides module registration * ModelAdapter - provides model specific instances * TopologyRequestListener - listens on changes in the configuration datastore * TopologyRequestHandler - processes configuration datastore changes * UnderlayTopologyListener - listens for changes in the specific model * LinkTransaltor and NodeTranslator - used by OverlayItemTranslator to create NormalizedNodes from OverlayItems The name convention we used was to add an abbreviation for the specific model to the beginning of implementing class name (e.g. the IRModelAdapter refers to class which implements ModelAdapter in module Inventory Rendering). In the case of the provider class, we put the abbreviation at the end. [IMPORTANT] ====== * In the next sections, we use the terms TopologyRequestListener, TopologyRequestHandler, etc. without a prepended or appended abbreviation because the steps apply regardless of which specific model you are targeting. * If you want to implement rendering from inventory to network-topology, you can just copy-paste our module and additional changes will be required only in the output part. ====== *Provider part* This part is the starting point of the whole module. It is responsible for creating and registering TopologyRequestListeners. It is necessary to create three classes which will import: * *TopoProcessingProviderModule* - is a generated class from topoprocessing-inventory-rendering-provider-impl.yang (created in previous step, file will appear after first build). Its method `createInstance()` is called at the feature start and must be modified to create an instance of TopoProcessingProvider and call its `startup(TopoProcessingProvider topoProvider)` function. * *TopoProcessingProvider* - in `startup(TopoProcessingProvider topoProvider)` function provides ModelAdapter registration to TopoProcessingProviderImpl. * *ModelAdapter* - provides creation of corresponding module specific classes. *Input part* This includes the creation of the classes responsible for input data processing. In this case, we had to create five classes implementing: * *TopologyRequestListener* and *TopologyRequestHandler* - when notified about a change in the configuration datastore, verify if the change contains a topology request (has correlations in it) and creates UnderlayTopologyListeners if needed. The implementation of these classes will differ according to the model in which are correlations saved (network-topology or i2rs). In the case of using network-topology, as the input model, you can use our classes IRTopologyRequestListener and IRTopologyRequestHandler. * *UnderlayTopologyListener* - registers underlay listeners according to input model. In our case (listening in the inventory model), we created listeners for the network-topology model and inventory model, and set the NotificationInterConnector as the first operator and set the IRRenderingOperator as the second operator (after NotificationInterConnector). Same as for TopologyRequestListener/Handler, if you are rendering from the inventory model, you can use our class IRUnderlayTopologyListener. * *InventoryListener* - a new implementation of this class is required only for inventory input model. This is because the InventoryListener from topoprocessing-impl requires pathIdentifier which is absent in the case of rendering. * *TopologyOperator* - replaces classic topoprocessing operator. While the classic operator provides specific operations on topology, the rendering operator just wraps each received UnderlayItem to OverlayItem and sends them to write. [IMPORTANT] ====== For purposes of topology rendering from inventory to network-topology, there are misused fields in UnderlayItem as follows: * item - contains node from network-topology part of inventory * leafItem - contains node from inventory In case of implementing UnderlayTopologyListener or InventoryListener you have to carefully adjust UnderlayItem creation to these terms. ====== *Output part* The output part of topology rendering is responsible for translating received overlay items to normalized nodes. In the case of inventory rendering, this is where node information from inventory are combined with node information from network-topology. This combined information is stored in our inventory-rendering model normalized node and passed to the writer. The output part consists of two translators implementing the NodeTranslator and LinkTranslator interfaces. *NodeTranslator implementation* - The NodeTranslator interface has one `translate(OverlayItemWrapper wrapper)` method. For our purposes, there is one important thing in wrapper - the list of OverlayItems which have one or more common UnderlayItems. Regardless of this list, in the case of rendering it will always contains only one OverlayItem. This item has list of UnderlayItems, but again in case of rendering there will be only one UnderlayItem item in this list. In NodeTranslator, the OverlayItem and corresponding UnderlayItem represent nodes from the translating model. The UnderlayItem has several attributes. How you will use these attributes in your rendering is up to you, as you create this item in your topology operator. For example, as mentioned above, in our inventory rendering example is an inventory node normalized node stored in the UnderlayItem leafNode attribute, and we also store node-id from network-topology model in UnderlayItem itemId attribute. You can now use these attributes to build a normalized node for your new model. How to read and create normalized nodes is out of scope of this document. *LinkTranslator implementation* - The LinkTranslator interface also has one `translate(OverlayItemWrapper wrapper)` method. In our inventory rendering this method returns `null`, because the inventory model doesn't have links. But if you also need links, this is the place where you should translate it into a normalized node for your model. In LinkTranslator, the OverlayItem and corresponding UnderlayItem represent links from the translating model. As in NodeTranslator, there will be only one OverlayItem and one UnderlayItem in the corresponding lists. ==== Testing If you want to test our implementation you must apply https://git.opendaylight.org/gerrit/#/c/26612[this patch]. It adds an OpenFlow Plugin dependency so we can use it in the Karaf distribution as a feature. After adding patch and building the whole framework, you can start Karaf. Next, you have to install necessary features. In our case it is: `feature:install odl-restconf-noauth odl-topoprocessing-inventory-rendering odl-openflowplugin-southbound odl-openflowplugin-nsf-model` Now you can send messages to REST from any REST client (e.g. Postman in Chrome). Messages have to have following headers: [options="header"] |===== |Header |Value |Content-Type:|application/xml |Accept: |application/xml |username: |admin |password: |admin |===== Firstly send topology request to http://localhost:8181/restconf/config/network-topology:network-topology/topology/render:1 with method PUT. Example of simple rendering request: [source, xml] ---- render:1 inventory-rendering-model 1 rendering-only node und-topo:1 ---- This request says that we want create topology with name render:1 and this topology should be stored in the inventory-rendering-model and it should be created from topology flow:1 by node rendering. Next we send the network-topology part of topology flow:1. So to the URL http://localhost:8181/restconf/config/network-topology:network-topology/topology/und-topo:1 we PUT: [source,xml] ---- und-topo:1 openflow:1 /i:nodes/i:node[i:id="openflow:1"] tp:1 /i:nodes/i:node[i:id="openflow:1"]/i:node-connector[i:id="openflow:1:1"] ---- And the last input will be inventory part of topology. To the URL http://localhost:8181/restconf/config/opendaylight-inventory:nodes we PUT: [source,xml] ---- openflow:1 openflow:1:1 1 10000000 s1-eth1 copper ten-gb-fd 0 0E:DC:8C:63:EC:D1 false false false 0 0 0 0 595 378 0 28 410000000 0 0 7 5 0 openflow:1:LOCAL 4294967294 0 s1 0 BA:63:87:0C:76:41 false false false 0 0 0 0 576 468 0 28 426000000 0 0 6 6 0 None Nicira, Inc. Open vSwitch 2.1.3 None 10.20.30.40 0 0 0 x:chaining x:select-weight x:select-liveness 4294967040 67082241 0 ---- After this, the expected result from a GET request to http://127.0.0.1:8181/restconf/operational/network-topology:network-topology is: [source,xml] ---- render:1 openflow:1 10.20.30.40 None Nicira, Inc. None Open vSwitch 2.1.3 openflow:1:1 0E:DC:8C:63:EC:D1 10000000 0 s1-eth1 openflow:1:LOCAL BA:63:87:0C:76:41 0 0 s1 ----