2 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.
5 For the purpose of this guide we are going to render the following augmented fields from the OpenFlow model:
14 * from inventory node-connector:
20 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.
23 There are two ways to implement support for your specific topology rendering:
25 * add a module to your project that depends on the Topology Processing Framework
26 * add a module to the Topology Processing Framework itself
28 Regardless, a successful implementation must complete all of the following steps.
30 ===== Step1 - Target Model Creation
31 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.
33 .Rendering to the inventory-rendering model
34 image::topoprocessing/Inventory_Rendering_Use_case.png[width=500]
36 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.
38 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:
42 identity inventory-rendering-model {
43 description "inventory-rendering.yang";
48 After that you will be able to set inventory-rendering-model as output model in XML.
50 ===== Step2 - Module and Feature Creation
51 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.
53 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.
58 |pom.xml |add new module to topoprocessing
59 |features.xml |add feature to topoprocessing
60 |features/pom.xml |add dependencies needed by features
61 |topoprocessing-artifacts/pom.xml |add artifact
62 |topoprocessing-config/pom.xml |add configuration file
63 |81-topoprocessing-inventory-rendering-config.xml |configuration file for new module
64 |topoprocessing-inventory-rendering/pom.xml |main pom for new module
65 |TopoProcessingProviderIR.java |contains startup method which register new model adapter
66 |TopoProcessingProviderIRModule.java |generated class which contains createInstance method. You should call your startup method from here.
67 |TopoProcessingProviderIRModuleFactory.java |generated class. You will probably not need to edit this file
68 |log4j.xml |configuration file for logger
69 topoprocessing-inventory-rendering-provider-impl.yang|main yang module. Generated classes are generated according to this yang file
72 ===== Step3 - Module Adapters Creation
73 There are seven mandatory interfaces or abstract classes that needs to be implemented in each module. They are:
75 * TopoProcessingProvider - provides module registration
76 * ModelAdapter - provides model specific instances
77 * TopologyRequestListener - listens on changes in the configuration datastore
78 * TopologyRequestHandler - processes configuration datastore changes
79 * UnderlayTopologyListener - listens for changes in the specific model
80 * LinkTransaltor and NodeTranslator - used by OverlayItemTranslator to create NormalizedNodes from OverlayItems
82 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.
86 * 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.
87 * 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.
92 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:
94 * *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.
95 * *TopoProcessingProvider* - in `startup(TopoProcessingProvider topoProvider)` function provides ModelAdapter registration to TopoProcessingProviderImpl.
96 * *ModelAdapter* - provides creation of corresponding module specific classes.
100 This includes the creation of the classes responsible for input data processing. In this case, we had to create five classes implementing:
102 * *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.
103 * *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.
104 * *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.
105 * *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.
109 For purposes of topology rendering from inventory to network-topology, there are misused fields in UnderlayItem as follows:
111 * item - contains node from network-topology part of inventory
112 * leafItem - contains node from inventory
114 In case of implementing UnderlayTopologyListener or InventoryListener you have to carefully adjust UnderlayItem creation to these terms.
119 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.
121 The output part consists of two translators implementing the NodeTranslator and LinkTranslator interfaces.
123 *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.
125 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.
127 *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.
130 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:
132 `feature:install odl-restconf-noauth odl-topoprocessing-inventory-rendering odl-openflowplugin-southbound odl-openflowplugin-nsf-model`
134 Now you can send messages to REST from any REST client (e.g. Postman in Chrome). Messages have to have following headers:
139 |Content-Type:|application/xml
140 |Accept: |application/xml
145 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:
149 <topology xmlns="urn:TBD:params:xml:ns:yang:network-topology">
150 <topology-id>render:1</topology-id>
151 <correlations xmlns="urn:opendaylight:topology:correlation" >
152 <output-model>inventory-rendering-model</output-model>
154 <correlation-id>1</correlation-id>
155 <type>rendering-only</type>
156 <correlation-item>node</correlation-item>
158 <underlay-topology>und-topo:1</underlay-topology>
164 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.
166 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:
169 <topology xmlns="urn:TBD:params:xml:ns:yang:network-topology"
170 xmlns:it="urn:opendaylight:model:topology:inventory"
171 xmlns:i="urn:opendaylight:inventory">
172 <topology-id>und-topo:1</topology-id>
174 <node-id>openflow:1</node-id>
175 <it:inventory-node-ref>
176 /i:nodes/i:node[i:id="openflow:1"]
177 </it:inventory-node-ref>
180 <it:inventory-node-connector-ref>
181 /i:nodes/i:node[i:id="openflow:1"]/i:node-connector[i:id="openflow:1:1"]
182 </it:inventory-node-connector-ref>
187 And the last input will be inventory part of topology. To the URL http://localhost:8181/restconf/config/opendaylight-inventory:nodes we PUT:
191 xmlns="urn:opendaylight:inventory">
195 <id>openflow:1:1</id>
197 xmlns="urn:opendaylight:flow:inventory">1
200 xmlns="urn:opendaylight:flow:inventory">10000000
203 xmlns="urn:opendaylight:flow:inventory">s1-eth1
206 xmlns="urn:opendaylight:flow:inventory">
209 xmlns="urn:opendaylight:flow:inventory">copper ten-gb-fd
212 xmlns="urn:opendaylight:flow:inventory">
215 xmlns="urn:opendaylight:flow:inventory">
218 xmlns="urn:opendaylight:flow:inventory">0
221 xmlns="urn:opendaylight:flow:inventory">
222 </advertised-features>
224 xmlns="urn:opendaylight:flow:inventory">0E:DC:8C:63:EC:D1
227 xmlns="urn:opendaylight:flow:inventory">
228 <link-down>false</link-down>
229 <blocked>false</blocked>
232 <flow-capable-node-connector-statistics
233 xmlns="urn:opendaylight:port:statistics">
234 <receive-errors>0</receive-errors>
235 <receive-frame-error>0</receive-frame-error>
236 <receive-over-run-error>0</receive-over-run-error>
237 <receive-crc-error>0</receive-crc-error>
239 <transmitted>595</transmitted>
240 <received>378</received>
242 <receive-drops>0</receive-drops>
245 <nanosecond>410000000</nanosecond>
247 <transmit-errors>0</transmit-errors>
248 <collision-count>0</collision-count>
250 <transmitted>7</transmitted>
251 <received>5</received>
253 <transmit-drops>0</transmit-drops>
254 </flow-capable-node-connector-statistics>
257 <id>openflow:1:LOCAL</id>
259 xmlns="urn:opendaylight:flow:inventory">4294967294
262 xmlns="urn:opendaylight:flow:inventory">0
265 xmlns="urn:opendaylight:flow:inventory">s1
268 xmlns="urn:opendaylight:flow:inventory">
271 xmlns="urn:opendaylight:flow:inventory">
274 xmlns="urn:opendaylight:flow:inventory">
277 xmlns="urn:opendaylight:flow:inventory">
280 xmlns="urn:opendaylight:flow:inventory">0
283 xmlns="urn:opendaylight:flow:inventory">
284 </advertised-features>
286 xmlns="urn:opendaylight:flow:inventory">BA:63:87:0C:76:41
289 xmlns="urn:opendaylight:flow:inventory">
290 <link-down>false</link-down>
291 <blocked>false</blocked>
294 <flow-capable-node-connector-statistics
295 xmlns="urn:opendaylight:port:statistics">
296 <receive-errors>0</receive-errors>
297 <receive-frame-error>0</receive-frame-error>
298 <receive-over-run-error>0</receive-over-run-error>
299 <receive-crc-error>0</receive-crc-error>
301 <transmitted>576</transmitted>
302 <received>468</received>
304 <receive-drops>0</receive-drops>
307 <nanosecond>426000000</nanosecond>
309 <transmit-errors>0</transmit-errors>
310 <collision-count>0</collision-count>
312 <transmitted>6</transmitted>
313 <received>6</received>
315 <transmit-drops>0</transmit-drops>
316 </flow-capable-node-connector-statistics>
319 xmlns="urn:opendaylight:flow:inventory">None
322 xmlns="urn:opendaylight:flow:inventory">Nicira, Inc.
325 xmlns="urn:opendaylight:flow:inventory">Open vSwitch
328 xmlns="urn:opendaylight:flow:inventory">2.1.3
331 xmlns="urn:opendaylight:flow:inventory">None
334 xmlns="urn:opendaylight:flow:inventory">10.20.30.40
337 xmlns="urn:opendaylight:meter:statistics">
338 <max_bands>0</max_bands>
339 <max_color>0</max_color>
340 <max_meter>0</max_meter>
343 xmlns="urn:opendaylight:group:statistics">
344 <group-capabilities-supported
345 xmlns:x="urn:opendaylight:group:types">x:chaining
346 </group-capabilities-supported>
347 <group-capabilities-supported
348 xmlns:x="urn:opendaylight:group:types">x:select-weight
349 </group-capabilities-supported>
350 <group-capabilities-supported
351 xmlns:x="urn:opendaylight:group:types">x:select-liveness
352 </group-capabilities-supported>
353 <max-groups>4294967040</max-groups>
354 <actions>67082241</actions>
360 After this, the expected result from a GET request to http://127.0.0.1:8181/restconf/operational/network-topology:network-topology is:
364 xmlns="urn:TBD:params:xml:ns:yang:network-topology">
366 <topology-id>render:1</topology-id>
368 <node-id>openflow:1</node-id>
370 xmlns="urn:opendaylight:topology:inventory:rendering">
371 <ip-address>10.20.30.40</ip-address>
372 <serial-number>None</serial-number>
373 <manufacturer>Nicira, Inc.</manufacturer>
374 <description>None</description>
375 <hardware>Open vSwitch</hardware>
376 <software>2.1.3</software>
379 <tp-id>openflow:1:1</tp-id>
381 xmlns="urn:opendaylight:topology:inventory:rendering">
382 <hardware-address>0E:DC:8C:63:EC:D1</hardware-address>
383 <current-speed>10000000</current-speed>
384 <maximum-speed>0</maximum-speed>
389 <tp-id>openflow:1:LOCAL</tp-id>
391 xmlns="urn:opendaylight:topology:inventory:rendering">
392 <hardware-address>BA:63:87:0C:76:41</hardware-address>
393 <current-speed>0</current-speed>
394 <maximum-speed>0</maximum-speed>