OpenFlow Plugin:PoC For Single Model Layer
Proposal 1 - Direct OpenFlowJava (OFJ) models use
Changes on bulk-o-matic enables to test using OFJ model to test performance improvements. Using those models gives should give us possibility to avoid translators and spare time and memory usage. This changes are related to the first changes proposal from this document.
Change of the service
In SalFlowService in OpenFlowPlugin (OFP) were added new RPC
rpc add-flow-direct { description "Adding flow to openflow device."; input { uses tr:transaction-metadata; leaf flow-ref { type types:flow-ref; } uses node-flow-direct; } output { uses tr:transaction-aware; } }
This RPC uses the openflow protocol specification flow.
Model comparison
OFP standard flow
AddFlowInput[ _flowRef=FlowRef [ _value=KeyedInstanceIdentifier { targetType=interface org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow, path= [ org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes, org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node [ key=NodeKey [ _id=Uri [ _value=openflow:3 ] ] ], org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode, org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table [ key=TableKey [ _id=1 ] ], org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow [ key=FlowKey [ _id=Uri [ _value=600 ] ] ] ] } ], _flowTable=FlowTableRef [ _value=KeyedInstanceIdentifier { targetType=interface org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table, path= [ org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes, org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node [ key=NodeKey [ _id=Uri [ _value=openflow:3 ] ] ], org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode, org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table [ key=TableKey [ _id=1 ] ] ] } ], _match=Match [ _ethernetMatch=EthernetMatch [ _ethernetType=EthernetType [ _type=EtherType [ _value=2048 ], augmentation= [] ], augmentation= [] ], _layer3Match=Ipv4Match [ _ipv4Source=Ipv4Prefix [ _value=0.0.0.100/32 ], augmentation= [] ], augmentation= [] ], _node=NodeRef [ _value=KeyedInstanceIdentifier { targetType=interface org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, path= [ org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes, org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node [ key=NodeKey [ _id=Uri [ _value=openflow:3 ] ] ] ] } ], _tableId=1, augmentation= [] ]
OFJ specification flow
AddFlowDirectInput [ _bufferId=4294967295, _command=OFPFCADD, _cookie=0, _cookieMask=0, _flags=FlowModFlags [ _oFPFFSENDFLOWREM=false, _oFPFFCHECKOVERLAP=false, _oFPFFRESETCOUNTS=false, _oFPFFNOPKTCOUNTS=false, _oFPFFNOBYTCOUNTS=false ], _flowRef=FlowRef [ _value=KeyedInstanceIdentifier { targetType=interface org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow, path= [ org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes, org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node [ key=NodeKey [ _id=Uri [ _value=openflow:1 ] ] ], org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode, org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table [ key=TableKey [ _id=1 ] ], org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow [ key=FlowKey [ _id=Uri [ _value=600 ] ] ] ] } ], _flowTable=FlowTableRef [ _value=KeyedInstanceIdentifier { targetType=interface org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table, path= [ org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes, org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node [ key=NodeKey [ _id=Uri [ _value=openflow:1 ] ] ], org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode, org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table [ key=TableKey [ _id=1 ] ] ] } ], _hardTimeout=0, _idleTimeout=0, _match=Match [ _matchEntry= [ MatchEntry [ _matchEntryValue=Ipv4SrcCase [ _ipv4Src=Ipv4Src [ _ipv4Address=Ipv4Address [ _value=0.0.0.100 ], augmentation= [] ], augmentation= [] ], _oxmClass=class org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.OpenflowBasicClass, _oxmMatchField=class org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.Ipv4Src, _hasMask=false, augmentation= [] ], MatchEntry [ _matchEntryValue=EthTypeCase [ _ethType=EthType [ _ethType=EtherType [ _value=2048 ], augmentation= [] ], augmentation= [] ], _oxmClass=class org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.OpenflowBasicClass, _oxmMatchField=class org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.EthType, _hasMask=false, augmentation= [] ] ], _type=class org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.OxmMatchType, augmentation= [] ], _node=NodeRef [ _value=KeyedInstanceIdentifier { targetType=interface org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, path= [ org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes, org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node [ key=NodeKey [ _id=Uri [ _value=openflow:1 ] ] ] ] } ], _outGroup=4294967295, _outPort=PortNumber [ _value=4294967295 ], _priority=32768, _tableId=TableId [ _value=1 ], _version=4, augmentation= [] ]
Proposal 2 - Convert OpenflowPlugin models directly to raw bytes
Adding new OFJ serializers for OFP models is another way to avoid translators and spare time and memory usage. This changes are related to the second changes proposal from this document. This proposal is easier to adapt for downstream projects, because it do not requires change of models, but it is harder to implement than first one, because making custom serializers and deserializers for OFP models will take a lot more time, than just switching models in Proposal 1.
Change of the service
In SalFlowService in OpenFlowPlugin (OFP) were added new RPC
rpc add-flow-raw { description "Adding flow to openflow device."; input { uses tr:transaction-metadata; leaf flow-ref { type types:flow-ref; } uses node-flow; } output { uses tr:transaction-aware; } }
This RPC is exactly same as add-flow rpc, but it serves as path delimiter (if this rpc is called, we will skip translations and send openflowplugin models directly to openflowjava, so it can use our custom serializers).
Test results
Results for adding 5000 flows in 20 runs for each flow path with more advanced flow what have flow flags, instructions with actions and match with ipv4 source and destination.
Path types:
NORMAL: Uses current path, translating openflowplugin models to openflowjava models and then to raw bytes
DIRECT: Uses openflowjava models directly, translating them to raw bytes
RAW: Uses openflowplugin models and translates them directly to raw bytes
Results:
NORMAL | DIRECT | RAW | |
---|---|---|---|
Runs | run # dur 1 2258 (warmup) 2 872 3 1163 4 610 5 561 6 590 7 643 8 395 9 591 10 294 11 731 12 630 13 666 14 549 15 575 16 643 17 634 18 537 19 572 20 658 | run # dur 1 417 (warmup) 2 764 3 541 4 556 5 618 6 647 7 520 8 622 9 549 10 626 11 518 12 523 13 559 14 592 15 585 16 531 17 576 18 428 19 519 20 925 | run # dur 1 615 (warmup) 2 614 3 598 4 683 5 546 6 622 7 591 8 639 9 525 10 592 11 650 12 616 13 487 14 245 15 336 16 552 17 501 18 546 19 615 20 388 |
Total | 11914 | 11199 | 10346 |
Average | 627 | 589 | 544 |
All numbers above are in milliseconds, and numbers in each run is duration between start of bulk-o-matic add flow job and successful flow add response from device for last flow in bulk-o-matic add flow job. So, for example when we are adding 5000 flows, we send RPC to bulk-o-matic, that will prepare these 5000 flows, and then sends each of them as RPC to SalFlowService. Last flow in this case will be flow with id 5500, because in bulk-o-matic, starting flow id is always 500. So, in this case, it will be duration between starting of sending these 5000 flows to SalFlowService and successful add flow response from device for flow with id 5500. We are getting all these informations from logs.
How to setup and run test
Testing script can be downloaded here and run on this patch from Gerrit (important, make sure you are on latest patchset).
./flowpathtest.sh -h
Usage: -h show this message -s skip adding flows, only process logs -t <flow_type> flow type used for performance tests, can be NORMAL/DIRECT/RAW (NORMAL) -p <log_path> path to karaf log file (distribution/karaf/target/assembly/data/log/karaf.log) -f <num_flows> total number of flows added per loop (1000) -r <num_runs> how many runs should test do, higher is better for accuracy (10) -c <address> address of controller (127.0.0.1:8181) -w <wait_time> how many seconds should test wait between each run (30)
Example usage:
./flowpathtest.sh -f 5000 -r 50 -w 10 -c "127.0.0.1:8181" -t "NORMAL" -p "distribution/karaf/target/assembly/data/log/karaf.log"
Command above will try to add 5000 flows via Bulk-o-matic 50 times each with NORMAL path (OpenflowPlugin models -> translators -> device) with delay of 10 seconds between each run to prevent interfering of runs between each other. Proper delay is very important, because if we do not set big enough delay, logs will be messed up and testing script will not be able to get time of last succesfully added flow to compute total duration.
This test requires to have INFO logging in FlowWriterDirectOFRpc and DEBUG logging in SalFlowServiceImpl and rest of logging on minimum, so your log4j configuration should look like this:
log4j.logger.ROOT=ERROR log4j.logger.org.opendaylight=ERROR log4j.logger.org.opendaylight.openflowplugin.impl.services.SalFlowServiceImpl=DEBUG log4j.logger.org.opendaylight.openflowplugin.applications.bulk.o.matic.FlowWriterDirectOFRpc=INFO