First complete pass over the developer guide
[docs.git] / manuals / developer-guide / src / main / asciidoc / openflowjava / odl-openflowjava-protocol-dev.adoc
1 == OpenFlow Protocol Library Developer Guide
2
3 === Introduction
4 OpenFlow Protocol Library is component in OpenDaylight, that mediates communication
5 between OpenDaylight controller and hardware devices supporting OpenFlow protocol.
6 Primary goal is to provide user (or upper layers of OpenDaylight) communication
7 channel, that can be used for managing network hardware devices.
8
9 === Features Overview
10 There are three features inside openflowjava:
11
12 * *odl-openflowjava-protocol* provides all openflowjava bundles, that are needed
13 for communication with openflow devices. It ensures message translation and
14 handles network connections. It also provides openflow protocol specific
15 model.
16 * *odl-openflowjava-all* currently contains only odl-openflowjava-protocol feature.
17 * *odl-openflowjava-stats* provides mechanism for message counting and reporting.
18 Can be used for performance analysis.
19
20 === odl-openflowjava-protocol Architecture
21 Basic bundles contained in this feature are openflow-protocol-api,
22 openflow-protocol-impl, openflow-protocol-spi and util.
23
24 * *openflow-protocol-api* - contains openflow model, constants and keys used for
25 (de)serializer registration.
26 * *openflow-protocol-impl* - contains message factories, that translate binary
27 messages into DataObjects and vice versa. Bundle also contains network connection
28 handlers - servers, netty pipeline handlers, ...
29 * *openflow-protocol-spi* - entry point for openflowjava configuration,
30 startup and close. Basically starts implementation.
31 * *util* - utility classes for binary-Java conversions and to ease experimenter
32 key creation
33
34 === odl-openflowjava-stats Feature
35 Runs over odl-openflowjava-protocol. It counts various message types / events
36 and reports counts in specified time periods. Statistics collection can be
37 configured in openflowjava-config/src/main/resources/45-openflowjava-stats.xml
38
39 === Key APIs and Interfaces
40 Basic API / SPI classes are ConnectionAdapter (Rpc/notifications) and
41 SwitchConnectionProcider (configure, start, shutdown)
42
43 //=== API Reference Documentation
44 //Provide links to JavaDoc, REST API documentation, etc.  [TBD]
45
46 === Installation ===
47 Pull the code and import project into your IDE.
48 ----
49 git clone ssh://<username>@git.opendaylight.org:29418/openflowjava.git
50 ----
51 === Configuration ===
52 Current implementation allows to configure:
53
54 * listening port (mandatory)
55 * transfer protocol (mandatory)
56 * switch idle timeout (mandatory)
57 * TLS configuration (optional)
58 * thread count (optional)
59
60 You can find exemplary Openflow Protocol Library instance configuration below:
61 ----
62 <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
63   <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
64     <!-- default OF-switch-connection-provider (port 6633) -->
65     <module>
66       <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:openflow:switch:connection:provider:impl">prefix:openflow-switch-connection-provider-impl</type>
67       <name>openflow-switch-connection-provider-default-impl</name>
68       <port>6633</port>
69 <!--  Possible transport-protocol options: TCP, TLS, UDP -->
70       <transport-protocol>TCP</transport-protocol>
71       <switch-idle-timeout>15000</switch-idle-timeout>
72 <!--       Exemplary TLS configuration:
73             - uncomment the <tls> tag
74             - copy exemplary-switch-privkey.pem, exemplary-switch-cert.pem and exemplary-cacert.pem
75               files into your virtual machine
76             - set VM encryption options to use copied keys
77             - start communication
78            Please visit OpenflowPlugin or Openflow Protocol Library#Documentation wiki pages
79            for detailed information regarding TLS -->
80 <!--       <tls>
81              <keystore>/exemplary-ctlKeystore</keystore>
82              <keystore-type>JKS</keystore-type>
83              <keystore-path-type>CLASSPATH</keystore-path-type>
84              <keystore-password>opendaylight</keystore-password>
85              <truststore>/exemplary-ctlTrustStore</truststore>
86              <truststore-type>JKS</truststore-type>
87              <truststore-path-type>CLASSPATH</truststore-path-type>
88              <truststore-password>opendaylight</truststore-password>
89              <certificate-password>opendaylight</certificate-password>
90            </tls> -->
91 <!--       Exemplary thread model configuration. Uncomment <threads> tag below to adjust default thread model -->
92 <!--       <threads>
93              <boss-threads>2</boss-threads>
94              <worker-threads>8</worker-threads>
95            </threads> -->
96     </module>
97 ----
98 ----
99     <!-- default OF-switch-connection-provider (port 6653) -->
100     <module>
101       <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:openflow:switch:connection:provider:impl">prefix:openflow-switch-connection-provider-impl</type>
102       <name>openflow-switch-connection-provider-legacy-impl</name>
103       <port>6653</port>
104 <!--  Possible transport-protocol options: TCP, TLS, UDP -->
105       <transport-protocol>TCP</transport-protocol>
106       <switch-idle-timeout>15000</switch-idle-timeout>
107 <!--       Exemplary TLS configuration:
108             - uncomment the <tls> tag
109             - copy exemplary-switch-privkey.pem, exemplary-switch-cert.pem and exemplary-cacert.pem
110               files into your virtual machine
111             - set VM encryption options to use copied keys
112             - start communication
113            Please visit OpenflowPlugin or Openflow Protocol Library#Documentation wiki pages
114            for detailed information regarding TLS -->
115 <!--       <tls>
116              <keystore>/exemplary-ctlKeystore</keystore>
117              <keystore-type>JKS</keystore-type>
118              <keystore-path-type>CLASSPATH</keystore-path-type>
119              <keystore-password>opendaylight</keystore-password>
120              <truststore>/exemplary-ctlTrustStore</truststore>
121              <truststore-type>JKS</truststore-type>
122              <truststore-path-type>CLASSPATH</truststore-path-type>
123              <truststore-password>opendaylight</truststore-password>
124              <certificate-password>opendaylight</certificate-password>
125            </tls> -->
126 <!--       Exemplary thread model configuration. Uncomment <threads> tag below to adjust default thread model -->
127 <!--       <threads>
128              <boss-threads>2</boss-threads>
129              <worker-threads>8</worker-threads>
130            </threads> -->
131     </module>
132 ----
133 ----
134     <module>
135       <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:openflow:common:config:impl">prefix:openflow-provider-impl</type>
136       <name>openflow-provider-impl</name>
137       <openflow-switch-connection-provider>
138         <type xmlns:ofSwitch="urn:opendaylight:params:xml:ns:yang:openflow:switch:connection:provider">ofSwitch:openflow-switch-connection-provider</type>
139         <name>openflow-switch-connection-provider-default</name>
140       </openflow-switch-connection-provider>
141       <openflow-switch-connection-provider>
142         <type xmlns:ofSwitch="urn:opendaylight:params:xml:ns:yang:openflow:switch:connection:provider">ofSwitch:openflow-switch-connection-provider</type>
143         <name>openflow-switch-connection-provider-legacy</name>
144       </openflow-switch-connection-provider>
145       <binding-aware-broker>
146         <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
147         <name>binding-osgi-broker</name>
148       </binding-aware-broker>
149     </module>
150   </modules>
151 ----
152 Possible transport-protocol options:
153
154 * TCP
155 * TLS
156 * UDP
157
158 Switch-idle timeout specifies time needed to detect idle state of switch. When
159 no message is received from switch within this time, upper layers are notified
160 on switch idleness.
161 To be able to use this exemplary TLS configuration:
162
163 * uncomment the +<tls>+ tag
164 * copy _exemplary-switch-privkey.pem_, _exemplary-switch-cert.pem_ and
165 _exemplary-cacert.pem_ files into your virtual machine
166 * set VM encryption options to use copied keys (please visit TLS support wiki page
167 for detailed information regarding TLS)
168 * start communication
169
170 Thread model configuration specifies how many threads are desired to perform
171 Netty's I/O operations.
172
173 * boss-threads specifies the number of threads that register incoming connections
174 * worker-threads specifies the number of threads performing read / write
175 (+ serialization / deserialization) operations.
176
177
178 === Architecture
179
180 ==== Public API +(openflow-protocol-api)+
181 Set of interfaces and builders for immutable data transfer objects representing
182 Openflow Protocol structures.
183
184 Transfer objects and service APIs are infered from several YANG models
185 using code generator to reduce verbosity of definition and repeatibility of code.
186
187 The following YANG modules are defined:
188
189 * openflow-types - defines common Openflow specific types
190 * openflow-instruction - defines base Openflow instructions
191 * openflow-action - defines base Openflow actions
192 * openflow-augments - defines object augmentations
193 * openflow-extensible-match - defines Openflow OXM match
194 * openflow-protocol - defines Openflow Protocol messages
195 * system-notifications - defines system notification objects
196 * openflow-configuration - defines structures used in ConfigSubsystem
197
198 This modules also reuse types from following YANG modules:
199
200 * ietf-inet-types - IP adresses, IP prefixes, IP-protocol related types
201 * ietf-yang-types - Mac Address, etc.
202
203 The use of predefined types is to make APIs contracts more safe, better readable
204 and documented (e.g using MacAddress instead of byte array...)
205
206 ==== TCP Channel pipeline +(openflow-protocol-impl)+
207
208 Creates channel processing pipeline based on configuration and support.
209
210 .TCP Channel pipeline
211 imageopenflowjava/500px-TCPChannelPipeline.png[width=500]
212
213 .Switch Connection Provider
214 Implementation of connection point for other projects. Library exposes its
215 functionality through this class.
216 Library can be configured, started and shutdowned here. There are also methods
217 for custom (de)serializer registration.
218
219 .Tcp Connection Initializer
220 In order to initialize TCP connection to a device (switch), OF Plugin calls method
221 +initiateConnection()+ in +SwitchConnectionProvider+. This method in turn initializes
222 (Bootstrap) server side channel towards the device.
223
224 .TCP Handler
225 Represents single server that is handling incoming connections over TCP / TLS protocol.
226 TCP Handler creates a single instance of TCP Channel Initializer that will initialize
227 channels. After that it binds to configured InetAddress and port. When a new
228 device connects, TCP Handler registers its channel and passes control to
229 TCP Channel Initializer.
230
231 .TCP Channel Initializer
232 This class is used for channel initialization / rejection and passing arguments.
233 After a new channel has been registered it calls Switch Connection Handler's
234 (OF Plugin) accept method to decide if the library should keep the newly registered
235 channel or if the channel should be closed. If the channel has been accepted,
236 TCP Channel Initializer creates the whole pipeline with needed handlers and also
237 with ConnectionAdapter instance. After the channel pipeline is ready, Switch
238 Connection Handler is notified with +onConnectionReady+ notification.
239 OpenFlow Plugin can now start sending messages downstream.
240
241 .Idle Handler
242 If there are no messages received for more than time specified, this handler
243 triggers idle state notification.
244 The switch idle timeout is received as a parameter from ConnectionConfiguration
245 settings. Idle State Handler is inactive while there are messages received within
246 the switch idle timeout. If there are no messages received for more than timeout
247 specified, handler creates SwitchIdleEvent message and sends it upstream.
248
249 .TLS Handler
250 It encrypts and decrypts messages over TLS protocol.
251 Engaging TLS Handler into pipeline is matter of configuration (+<tls>+ tag).
252 TLS communication is either unsupported or required. TLS Handler is represented
253 as a Netty's SslHandler.
254
255 .OF Frame Decoder
256 Parses input stream into correct length message frames for further processing.
257 Framing is based on Openflow header length. If received message is shorter than
258 minimal length of OpenFlow message (8 bytes), OF Frame Decoder waits for more data.
259 After receiving at least 8 bytes the decoder checks length in OpenFlow header.
260 If there are still some bytes missing, the decoder waits for them. Else the OF
261 Frame Decoder sends correct length message to next handler in the channel pipeline.
262
263 .OF Version Detector
264 Detects version of used OpenFlow Protocol and discards unsupported version messages.
265 If the detected version is supported, OF Version Detector creates
266 +VersionMessageWrapper+ object containing the detected version and byte message
267 and sends this object upstream.
268
269 .OF Decoder
270 Chooses correct deserilization factory (based on message type) and deserializes
271 messages into generated DTOs (Data Transfer Object).
272 OF Decoder receives +VersionMessageWrapper+ object and passes it to
273 +DeserializationFactory+ which will return translated DTO. +DeserializationFactory+
274 creates +MessageCodeKey+ object with version and type of received message and
275 Class of object that will be the received message deserialized into. This object
276 is used as key when searching for appropriate decoder in +DecoderTable+.
277 +DecoderTable+ is basically a map storing decoders. Found decoder translates
278 received message into DTO. If there was no decoder found, null is returned. After
279 returning translated DTO back to OF Decoder, the decoder checks if it is null or not.
280 When the DTO is null, the decoder logs this state and throws an Exception. Else it
281 passes the DTO further upstream. Finally, the OF Decoder releases ByteBuf containing
282 received and decoded byte message.
283
284 .OF Encoder
285 Chooses correct serialization factory (based on type of DTO) and serializes DTOs
286 into byte messages.
287 OF Encoder does the opposite than the OF Decoder using the same principle.
288 OF Encoder receives DTO, passes it for translation and if the result is not null,
289 it sends translated DTO downstream as a ByteBuf. Searching for appropriate encoder
290 is done via MessageTypeKey, based on version and class of received DTO.
291
292 .Delegating Inbound Handler
293 Delegates received DTOs to Connection Adapter.
294 It also reacts on channelInactive and channelUnregistered events. Upon one of
295 these events is triggered, DelegatingInboundHandler creates DisconnectEvent message
296 and sends it upstream, notifying upper layers about switch disconnection.
297
298 .Channel Outbound Queue
299 Message flushing handler.
300 Stores outgoing messages (DTOs) and flushes them. Flush is performed based on time
301 expired and on the number of messages enqueued.
302
303 .Connection Adapter
304 Provides a facade on top of pipeline, which hides netty.io specifics. Provides a
305 set of methods to register for incoming messages and to send messages to particular
306 channel / session.
307 ConnectionAdapterImpl basically implements three interfaces (unified in one
308 superinterface ConnectionFacade):
309
310 * ConnectionAdapter
311 * MessageConsumer
312 * OpenflowProtocolService
313
314
315 *ConnectionAdapter* interface has methods for setting up listeners (message,
316 system and connection ready listener), method to check if all listeners are set,
317 checking if the channel is alive and disconnect method. Disconnect method clears
318 responseCache and disables consuming of new messages.
319
320 *MessageConsumer* interface holds only one method: +consume()+. +Consume()+ method
321 is called from DelegatingInboundHandler. This method processes received DTO's based
322 on their type. There are three types of received objects:
323
324 * System notifications - invoke system notifications in OpenFlow Plugin
325 (systemListener set). In case of +DisconnectEvent+ message, the Connection Adapter
326 clears response cache and disables consume() method processing,
327 * OpenFlow asynchronous messages (from switch) - invoke corresponding notifications
328 in OpenFlow Plugin,
329 * OpenFlow symmetric messages (replies to requests) - create +RpcResponseKey+
330 with XID and DTO's class set. This +RpcResponseKey+ is then used to find
331 corresponding future object in responseCache. Future object is set with success
332 flag, received message and errors (if any occurred). In case no corresponding
333 future was found in responseCache, Connection Adapter logs warning and discards
334 the message. Connection Adapter also logs warning when an unknown DTO is received.
335
336 *OpenflowProtocolService* interface contains all rpc-methods for sending messages
337 from upper layers (OpenFlow Plugin) downstream and responding. Request messages
338 return Future filled with expected reply message, otherwise the expected Future
339 is of type Void.
340
341 *NOTE:*
342 MultipartRequest message is the only exception. Basically it is request - reply
343 Message type, but it wouldn't be able to process more following MultipartReply
344 messages if this was implemented as rpc (only one Future). This is why MultipartReply
345 is implemented as notification. OpenFlow Plugin takes care of correct message
346 processing.
347
348
349 ==== UDP Channel pipeline (openflow-protocol-impl)
350 Creates UDP channel processing pipeline based on configuration and support.
351 *Switch Connection Provider*, *Channel Outbound Queue* and *Connection Adapter*
352 fulfill the same role as in case of TCP connection / channel pipeline (please
353 see above).
354
355 .UDP Channel pipeline
356 image::openflowjava/500px-UdpChannelPipeline.png[width=500]
357
358 .UDP Handler
359
360 Represents single server that is handling incoming connections over UDP (DTLS)
361 protocol.
362 UDP Handler creates a single instance of UDP Channel Initializer that will
363 initialize channels. After that it binds to configured InetAddress and port.
364 When a new device connects, UDP Handler registers its channel and passes control
365 to UDP Channel Initializer.
366
367 .UDP Channel Initializer
368 This class is used for channel initialization and passing arguments.
369 After a new channel has been registered (for UDP there is always only one channel)
370 UDP Channel Initializer creates whole pipeline with needed handlers.
371
372 .DTLS Handler
373 Haven't been implemented yet. Will take care of secure DTLS connections.
374
375 .OF Datagram Packet Handler
376 Combines functionality of OF Frame Decoder and OF Version Detector. Extracts
377 messages from received datagram packets and checks if message version is supported.
378 If there is a message received from yet unknown sender, OF Datagram Packet Handler
379 creates Connection Adapter for this sender and stores it under sender's address in
380 +UdpConnectionMap+. This map is also used for sending the messages and for correct
381 Connection Adapter lookup - to delegate messages from one channel to multiple sessions.
382
383 .OF Datagram Packet Decoder
384 Chooses correct deserilization factory (based on message type) and deserializes
385 messages into generated DTOs.
386 OF Decoder receives +VersionMessageUdpWrapper+ object and passes it to
387 +DeserializationFactory+ which will return translated DTO. +DeserializationFactory+
388 creates +MessageCodeKey+ object with version and type of received message and
389 Class of object that will be the received message deserialized into. This object
390 is used as key when searching for appropriate decoder in +DecoderTable+.
391 +DecoderTable+ is basically a map storing decoders. Found decoder translates
392 received message into DTO (DataTransferObject). If there was no decoder found,
393 null is returned. After returning translated DTO back to OF Datagram Packet Decoder,
394 the decoder checks if it is null or not. When the DTO is null, the decoder logs
395 this state. Else it looks up appropriate Connection Adapter in +UdpConnectionMap+
396 and passes the DTO to found Connection Adapter. Finally, the OF Decoder releases
397 +ByteBuf+ containing received and decoded byte message.
398
399 .OF Datagram Packet Encoder
400 Chooses correct serialization factory (based on type of DTO) and serializes DTOs
401 into byte messages.
402 OF Datagram Packet Encoder does the opposite than the OF Datagram Packet Decoder
403 using the same principle. OF Encoder receives DTO, passes it for translation and
404 if the result is not null, it sends translated DTO downstream as a datagram packet.
405 Searching for appropriate encoder is done via MessageTypeKey, based on version
406 and class of received DTO.
407
408 ==== SPI (openflow-protocol-spi)
409 Defines interface for library's connection point for other projects. Library
410 exposes its functionality through this interface.
411
412 ==== Integration test (openflow-protocol-it)
413 Testing communication with simple client.
414
415 ==== Simple client(simple-client)
416 Lightweight switch simulator - programmable with desired scenarios.
417
418 ==== Utility (util)
419 Contains utility classes, mainly for work with ByteBuf.
420
421
422 === Library's lifecycle
423
424 Steps (after the library's bundle is started):
425
426 * [1] Library is configured by ConfigSubsystem (adress, ports, encryption, ...)
427 * [2] Plugin injects its SwitchConnectionHandler into the Library
428 * [3] Plugin starts the Library
429 * [4] Library creates configured protocol handler (e.g. TCP Handler)
430 * [5] Protocol Handler creates Channel Initializer
431 * [6] Channel Initializer asks plugin whether to accept incoming connection on
432 each new switch connection
433 * [7] Plugin responds:
434     - true - continue building pipeline
435     - false - reject connection / disconnect channel
436 * [8] Library notifies Plugin with onSwitchConnected(ConnectionAdapter)
437 notification, passing reference to ConnectionAdapter, that will handle the connection
438 * [9] Plugin registers its system and message listeners
439 * [10] FireConnectionReadyNotification() is triggered, announcing that pipeline
440 handlers needed for communication have been created and Plugin can start
441 communication
442 * [11] Plugin shutdowns the Library when desired
443
444 .Library lifecycle
445 image::openflowjava/Library_lifecycle.png[width=500]
446
447
448 === Statistics collection
449
450 ==== Introduction
451 Statistics collection collects message statistics.
452 Current collected statistics (+DS+ - downstream, +US+ - upstream):
453
454 * +DS_ENTERED_OFJAVA+ - all messages that entered openflowjava (picked up from
455 openflowplugin)
456 * +DS_ENCODE_SUCCESS+ - successfully encoded messages
457 * +DS_ENCODE_FAIL+ - messages that failed during encoding (serialization) process
458 * +DS_FLOW_MODS_ENTERED+ - all flow-mod messages that entered openflowjava
459 * +DS_FLOW_MODS_SENT+ - all flow-mod messages that were successfully sent
460 * +US_RECEIVED_IN_OFJAVA+ - messages received from switch
461 * +US_DECODE_SUCCESS+ - successfully decoded messages
462 * +US_DECODE_FAIL+ - messages that failed during decoding (deserialization) process
463 * +US_MESSAGE_PASS+ - messages handed over to openflowplugin
464
465 ==== Karaf
466 In orded to start statistics, it is needed to feature:install odl-openflowjava-stats.
467 To see the logs one should use log:set DEBUG org.opendaylight.openflowjava.statistics
468 and than probably log:display (you can log:list to see if the logging has been set).
469 To adjust collection settings it is enough to modify 45-openflowjava-stats.xml.
470
471 ==== JConsole
472 JConsole provides two commands for the statistics collection:
473
474 * printing current statistics
475 * resetting statistic counters
476
477 After attaching JConsole to correct process, one only needs to go into MBeans
478 +tab -> org.opendaylight.controller -> RuntimeBean -> statistics-collection-service-impl
479 -> statistics-collection-service-impl -> Operations+  to be able to use this commands.
480
481 === TLS Support
482 NOTE: see OpenFlow Plugin Developper Guide
483
484 === Extensibility
485
486 ==== Introduction
487
488 Entry point for the extensibility is +SwitchConnectionProvider+.
489 +SwitchConnectionProvider+ contains methods for (de)serializer registration.
490 To register deserializer it is needed to use .register*Deserializer(key, impl).
491 To register serializer one must use .register*Serializer(key, impl). Registration
492 can occur either during configuration or at runtime.
493
494 *NOTE*: In case when experimenter message is received and no (de)serializer was
495 registered, the library will throw +IllegalArgumentException+.
496
497 ==== Basic Principle
498 In order to use extensions it is needed to augment existing model and register new (de)serializers.
499
500 Augmenting the model:
501 1. Create new augmentation
502
503 Register (de)serializers:
504 1. Create your (de)serializer
505 2. Let it implement +OFDeserializer<>+ / +OFSerializer<>+
506 - in case the structure you are (de)serializing needs to be used in Multipart
507 TableFeatures messages, let it implement +HeaderDeserializer<>+ / +HeaderSerializer+
508 3. Implement prescribed methods
509 4. Register your deserializer under appropriate key (in our case
510 +ExperimenterActionDeserializerKey+)
511 5. Register your serializer under appropriate key (in our case
512 +ExperimenterActionSerializerKey+)
513 6. Done, test your implementation
514
515 *NOTE*: If you don't know what key should be used with your (de)serializer
516 implementation, please visit <<registration_keys, Registration keys>> page.
517
518 ==== Example
519 Let's say we have vendor / experimenter action represented by this structure:
520 ----
521 struct foo_action {
522     uint16_t type;
523     uint16_t length;
524     uint32_t experimenter;
525     uint16_t first;
526     uint16_t second;
527     uint8_t  pad[4];
528 }
529 ----
530 First, we have to augment existing model. We create new module, which imports
531 "+openflow-types.yang+" (don't forget to update your +pom.xml+ with api dependency).
532 Now we create foo action identity:
533 ----
534 import openflow-types {prefix oft;}
535 identity foo {
536     description "Foo action description";
537     base oft:action-base;
538 }
539 ----
540
541 This will be used as type in our structure. Now we must augment existing action
542 structure, so that we will have the desired fields first and second. In order to
543 create new augmentation, our module has to import "+openflow-action.yang+". The
544 augment should look like this:
545 ----
546 import openflow-action {prefix ofaction;}
547 augment "/ofaction:actions-container/ofaction:action" {
548     ext:augment-identifier "foo-action";
549         leaf first {
550             type uint16;
551         }
552         leaf second {
553             type uint16;
554         }
555     }
556 ----
557 We are finished with model changes. Run mvn clean compile to generate sources.
558 After generation is done, we need to implement our (de)serializer.
559
560 Deserializer:
561 ----
562 public class FooActionDeserializer extends OFDeserializer<Action> {
563    @Override
564    public Action deserialize(ByteBuf input) {
565        ActionBuilder builder = new ActionBuilder();
566        input.skipBytes(SIZE_OF_SHORT_IN_BYTES); *// we know the type of action*
567        builder.setType(Foo.class);
568        input.skipBytes(SIZE_OF_SHORT_IN_BYTES); *// we don't need length*
569        *// now create experimenterIdAugmentation - so that openflowplugin can
570        differentiate correct vendor codec*
571        ExperimenterIdActionBuilder expIdBuilder = new ExperimenterIdActionBuilder();
572        expIdBuilder.setExperimenter(new ExperimenterId(input.readUnsignedInt()));
573        builder.addAugmentation(ExperimenterIdAction.class, expIdBuilder.build());
574        FooActionBuilder fooBuilder = new FooActionBuilder();
575        fooBuilder.setFirst(input.readUnsignedShort());
576        fooBuilder.setSecond(input.readUnsignedShort());
577        builder.addAugmentation(FooAction.class, fooBuilder.build());
578        input.skipBytes(4); *// padding*
579        return builder.build();
580    }
581 }
582 ----
583 Serializer:
584 ----
585 public class FooActionSerializer extends OFSerializer<Action> {
586    @Override
587    public void serialize(Action action, ByteBuf outBuffer) {
588        outBuffer.writeShort(FOO_CODE);
589        outBuffer.writeShort(16);
590        *// we don't have to check for ExperimenterIdAction augmentation - our
591        serializer*
592        *// was called based on the vendor / experimenter ID, so we simply write
593        it to buffer*
594        outBuffer.writeInt(VENDOR / EXPERIMENTER ID);
595        FooAction foo = action.getAugmentation(FooAction.class);
596        outBuffer.writeShort(foo.getFirst());
597        outBuffer.writeShort(foo.getSecond());
598        outBuffer.writeZero(4); //write padding
599    }
600 }
601 ----
602 Register both deserializer and serializer:
603 +SwitchConnectionProvider.registerDeserializer(new
604 ExperimenterActionDeserializerKey(0x04, VENDOR / EXPERIMENTER ID),
605 new FooActionDeserializer());+
606 +SwitchConnectionProvider.registerSerializer(new
607 ExperimenterActionSerializerKey(0x04, VENDOR / EXPERIMENTER ID),
608 new FooActionSerializer());+
609
610 We are ready to test our implementation.
611
612 *NOTE:* Vendor / Experimenter structures define only vendor / experimenter ID as
613 common distinguisher (besides action type). Vendor / Experimenter ID is unique
614 for all vendor messages - that's why vendor is able to register only one class
615 under ExperimenterAction(De)SerializerKey. And that's why vendor has to switch
616 / choose between his subclasses / subtypes on his own.
617
618 ==== Detailed walkthrough: Deserialization extensibility
619
620 .External interface & class description
621 *OFGeneralDeserializer:*
622
623 * +OFDeserializer<E extends DataObject>+
624 ** _deserialize(ByteBuf)_ - deserializes given ByteBuf
625 * +HeaderDeserializer<E extends DataObject>+
626 ** _deserializeHeaders(ByteBuf)_ - deserializes only E headers (used in Multipart
627 TableFeatures messages)
628
629 *DeserializerRegistryInjector*
630
631 * +injectDeserializerRegistry(DeserializerRegistry)+ - injects deserializer
632 registry into deserializer. Useful when custom deserializer needs access to
633 other deserializers.
634
635 *NOTE:* DeserializerRegistryInjector is not OFGeneralDeserializer descendand.
636 It is a standalone interface.
637
638 *MessageCodeKey and its descendants*
639 These keys are used as for deserializer lookup in DeserializerRegistry.
640 MessageCodeKey should is used in general, while its descendants are used in more
641 special cases. For Example ActionDeserializerKey is used for Action deserializer
642 lookup and (de)registration. Vendor is provided with special keys, which contain
643 only the most necessary fields. These keys usually start with "Experimenter"
644 prefix (MatchEntryDeserializerKey is an exception).
645
646 MessageCodeKey has these fields:
647
648 * short version - Openflow wire version number
649 * int value - value read from byte message
650 * Class<?> clazz - class of object being creating
651
652 .Scenario walkthrough
653 * [1] The scenario starts in a custom bundle which wants to extend library's
654 functionality. The custom bundle creates deserializers which implement exposed
655 +OFDeserializer+ / +HeaderDeserializer+ interfaces (wrapped under
656 +OFGeneralDeserializer+ unifying super interface).
657 * [2] Created deserializers are paired with corresponding ExperimenterKeys,
658 which are used for deserializer lookup.
659 If you don't know what key should be used with your (de)serializer implementation,
660 please visit <<registration_keys, Registration keys>> page.
661 * [3] Paired deserializers are passed to the OF Library
662 via *SwitchConnectionProvider*._registerCustomDeserializer(key, impl)_.
663 Library registers the deserializer.
664 ** While registering, Library checks if the deserializer is an instance of
665 *DeserializerRegistryInjector* interface. If yes, the DeserializerRegistry
666 (which stores all deserializer references) is injected into the deserializer.
667
668 This is particularly useful when the deserializer needs access to other
669 deserializers. For example +IntructionsDeserializer+ needs access to
670 +ActionsDeserializer+ in order to be able to process
671 OFPIT_WRITE_ACTIONS/OFPIT_APPLY_ACTIONS instructions.
672
673 .Deserialization scenario walkthrough
674 image::openflowjava/800px-Extensibility.png[width=500]
675
676 ==== Detailed walkthrough: Serialization extensibility
677 .External interface & class description
678
679 *OFGeneralSerializer:*
680
681 * OFSerializer<E extends DataObject>
682 ** _serialize(E,ByteBuf)_ - serializes E into given ByteBuf
683 * +HeaderSerializer<E extends DataObject>+
684 ** _serializeHeaders(E,ByteBuf)_ - serializes E headers (used in Multipart
685 TableFeatures messages)
686
687 *SerializerRegistryInjector*
688 * +injectSerializerRegistry(SerializerRegistry)+ - injects serializer registry
689 into serializer. Useful when custom serializer needs access to other serializers.
690
691 *NOTE:* SerializerRegistryInjector is not OFGeneralSerializer descendand.
692
693 *MessageTypeKey and its descendants*
694 These keys are used as for serializer lookup in SerializerRegistry.
695 MessageTypeKey should is used in general, while its descendants are used in more
696 special cases. For Example ActionSerializerKey is used for Action serializer
697 lookup and (de)registration. Vendor is provided with special keys, which contain
698 only the most necessary fields. These keys usually start with "Experimenter"
699 prefix (MatchEntrySerializerKey is an exception).
700
701 MessageTypeKey has these fields:
702
703 * _short version_ - Openflow wire version number
704 * _Class<E> msgType_ - DTO class
705
706 Scenario walkthrough
707
708 * [1] Serialization extensbility principles are similar to the deserialization
709 principles. The scenario starts in a custom bundle. The custom bundle creates
710 serializers which implement exposed OFSerializer / HeaderSerializer interfaces
711 (wrapped under OFGeneralSerializer unifying super interface).
712 * [2] Created serializers are paired with their ExperimenterKeys, which are used
713 for serializer lookup.
714 If you don't know what key should be used with your serializer implementation,
715 please visit <<registration_keys, Registration keys>> page.
716 * [3] Paired serializers are passed to the OF Library via
717 *SwitchConnectionProvider*._registerCustomSerializer(key, impl)_. Library
718 registers the serializer.
719 * While registering, Library checks if the serializer is an instance of
720 *SerializerRegistryInjector* interface. If yes, the SerializerRegistry (which
721 stores all serializer references) is injected into the serializer.
722
723 This is particularly useful when the serializer needs access to other deserializers.
724 For example IntructionsSerializer needs access to ActionsSerializer in order to
725 be able to process OFPIT_WRITE_ACTIONS/OFPIT_APPLY_ACTIONS instructions.
726
727 .Serialization scenario walkthrough
728 image::openflowjava/800px-Extensibility2.png[width=500]
729
730 ==== Internal description
731
732 *SwitchConnectionProvider*
733 +SwitchConnectionProvider+ constructs and initializes both deserializer and
734 serializer registries with default (de)serializers. It also injects the
735 +DeserializerRegistry+ into the +DeserializationFactory+, the +SerializerRegistry+
736 into the +SerializationFactory+.
737 When call to register custom (de)serializer is made, +SwitchConnectionProvider+
738 calls register method on appropriate registry.
739
740 *DeserializerRegistry / SerializerRegistry*
741 Both registries contain init() method to initialize default (de)serializers.
742 Registration checks if key or (de)serializer implementation are not +null+. If at
743 least one of the is +null+, +NullPointerException+ is thrown. Else the
744 (de)serializer implementation is checked if it is +(De)SerializerRegistryInjector+
745 instance. If it is an instance of this interface, the registry is injected into
746 this (de)serializer implementation.
747
748 +GetSerializer(key)+ or +GetDeserializer(key)+ performs registry lookup. Because
749 there are two separate interfaces that might be put into the registry, the
750 registry uses their unifying super interface. Get(De)Serializer(key) method casts
751 the super interface to desired type. There is also a null check for the
752 (de)serializer received from the registry. If the deserializer wasn't found,
753 +NullPointerException+ with key description is thrown. 
754
755
756 [[registration_keys]]
757 ==== Registration keys
758
759 .Deserialization
760
761 *Possible openflow extensions and their keys*
762
763 There are three vendor specific extensions in Openflow v1.0 and eight in
764 Openflow v1.3. These extensions are registered under registration keys,
765 that are shown in table below: 
766
767 .*Deserialization*
768 [options="header",cols="20%,10%,40%,30%"]
769 |========================================================================================================================================================
770 |Extension type            |OpenFlow|Registration key                                                                 |Utility class
771 |Vendor message            |1.0     |ExperimenterIdDeserializerKey(1, experimenterId, ExperimenterMessage.class)      |ExperimenterDeserializerKeyFactory
772 |Action                    |1.0     |ExperimenterActionDeserializerKey(1, experimenter ID)                            |.
773 |Stats message             |1.0     |ExperimenterMultipartReplyMessageDeserializerKey(1, experimenter ID)             |ExperimenterDeserializerKeyFactory
774 |Experimenter message      |1.3     |ExperimenterIdDeserializerKey(4, experimenterId, ExperimenterMessage.class)      |ExperimenterDeserializerKeyFactory
775 |Match entry               |1.3     |MatchEntryDeserializerKey(4, (number) ${oxm_Class}, (number) ${oxm_Field});      |.
776 |                          |        |key.setExperimenterId(experimenter ID);                                          |.
777 |Action                    |1.3     |ExperimenterActionDeserializerKey(4, experimenter ID)                            |.
778 |Instruction               |1.3     |ExperimenterInstructionDeserializerKey(4, experimenter ID)                       |.
779 |Multipart                 |1.3     |ExperimenterIdDeserializerKey(4, experimenterId, MultipartReplyMessage.class)    |ExperimenterDeserializerKeyFactory
780 |Multipart - Table features|1.3     |ExperimenterIdDeserializerKey(4, experimenterId, TableFeatureProperties.class)   |ExperimenterDeserializerKeyFactory
781 |Error                     |1.3     |ExperimenterIdDeserializerKey(4, experimenterId, ErrorMessage.class)             |ExperimenterDeserializerKeyFactory
782 |Queue property            |1.3     |ExperimenterIdDeserializerKey(4, experimenterId, QueueProperty.class)            |ExperimenterDeserializerKeyFactory
783 |Meter band type           |1.3     |ExperimenterIdDeserializerKey(4, experimenterId, MeterBandExperimenterCase.class)|ExperimenterDeserializerKeyFactory
784 |========================================================================================================================================================
785
786 .Serialization
787
788 *Possible openflow extensions and their keys*
789
790 There are three vendor specific extensions in Openflow v1.0 and seven Openflow
791 v1.3. These extensions are registered under registration keys, that are shown in
792 table below:
793
794
795 .*Serialization*
796 [options="header",cols="20%,10%,40%,30%"]
797 |=============================================================================================================================================================
798 |Extension type            |OpenFlow|Registration key                                                                        |Utility class
799 |Vendor message            |1.0     |ExperimenterIdSerializerKey<>(1, experimenterId, ExperimenterInput.class)               |ExperimenterSerializerKeyFactory
800 |Action                    |1.0     |ExperimenterActionSerializerKey(1, experimenterId, sub-type)                            |.
801 |Stats message             |1.0     |ExperimenterMultipartRequestSerializerKey(1, experimenter ID)                           |ExperimenterSerializerKeyFactory
802 |Experimenter message      |1.3     |ExperimenterIdSerializerKey<>(4, experimenterId, ExperimenterInput.class)               |ExperimenterSerializerKeyFactory
803 |Match entry               |1.3     |MatchEntrySerializerKey<>(4, (class) ${oxm_Class}, (class) ${oxm_Field});               |.
804 |                          |        |key.setExperimenterId(experimenter ID)                                                  |.
805 |Action                    |1.3     |ExperimenterActionSerializerKey(4, experimenterId, sub-type)                            |.
806 |Instruction               |1.3     |ExperimenterInstructionSerializerKey(4, experimenter ID)                                |.
807 |Multipart                 |1.3     |ExperimenterIdSerializerKey<>(4, experimenterId, MultipartRequestExperimenterCase.class)|ExperimenterSerializerKeyFactory
808 |Multipart - Table features|1.3     |ExperimenterIdSerializerKey<>(4, experimenterId, TableFeatureProperties.class)          |ExperimenterSerializerKeyFactory
809 |Meter band type           |1.3     |ExperimenterIdSerializerKey<>(4, experimenterId, MeterBandExperimenterCase.class)       |ExperimenterSerializerKeyFactory 
810 |=============================================================================================================================================================