| 1 | = Module E Design and Setup = |
| 2 | |
| 3 | == 1 Write Application for !OpenFlow Statistics Measurement == |
| 4 | |
| 5 | === 1.1 Get Flow Statistics you want to measure === |
| 6 | |
| 7 | Define stats_reply in /root/learning-switch-copy.rb as follows. This file is the Trema controller script which we ran in ModuleC. For the purpose of this tutorial, the modified script has been downloaded into switch1 prior to this tutorial. [[BR]] |
| 8 | You can find this file in the node called switch1. If you need help with logging in to your nodes, please refer [http://groups.geni.net/geni/wiki/HowTo/LoginToNodes How To Login]. [[BR]] |
| 9 | |
| 10 | |
| 11 | {{{ |
| 12 | #!ruby |
| 13 | def stats_reply (dpid, message) |
| 14 | puts "[flow stats_reply #{@my_switch}]---------------------------------" |
| 15 | byte_count = 0 |
| 16 | packet_count = 0 |
| 17 | flow_count = 0 |
| 18 | throughput = 0 |
| 19 | inst_throughput =0 |
| 20 | total_flow_count = message.stats.length |
| 21 | if(total_flow_count != 0) |
| 22 | message.stats.each do | flow_msg | |
| 23 | # WARNING: This only works for the EXACT case of two actions. If we add more than two actions the flow monitoring |
| 24 | # will break. |
| 25 | flow_ip = flow_msg.match |
| 26 | puts "This is the message #{flow_ip.nw_src}" |
| 27 | flow_count = flow_count + 1 |
| 28 | byte_count += flow_msg.byte_count |
| 29 | packet_count += flow_msg.packet_count |
| 30 | if flow_msg.duration_sec + flow_msg.duration_nsec/1000000000 != 0 |
| 31 | throughput += flow_msg.byte_count/(flow_msg.duration_sec + flow_msg.duration_nsec/1000000000) |
| 32 | file = File.open("/tmp/flowstats.out", "a") |
| 33 | file.puts "#{flow_ip.nw_src} #{flow_count.to_s} #{byte_count} #{packet_count} #{throughput} Bps" |
| 34 | file.close |
| 35 | end |
| 36 | end |
| 37 | end |
| 38 | @prev_byte_count = byte_count |
| 39 | end |
| 40 | |
| 41 | }}} |
| 42 | |
| 43 | === 1.2 Define Flow Statistics measurement points === |
| 44 | |
| 45 | We now proceed to write the application which collects the measurement points defined in the controller code above. [[BR]] |
| 46 | The complete script is already pre-loaded into the node switch1 at /usr/local/bin/learn_ofcollect.rb. The steps provided here are for your reference. |
| 47 | |
| 48 | {{{ |
| 49 | #!ruby |
| 50 | class MPThroughput < OML4R::MPBase |
| 51 | name :ofthroughput |
| 52 | param :srcaddr, :type => :string |
| 53 | param :numflows, :type => :int64 |
| 54 | param :numbytes, :type => :int64 |
| 55 | param :numpacket, :type => :int64 |
| 56 | param :throughput, :type => :int64 |
| 57 | param :units, :type => :string |
| 58 | end |
| 59 | }}} |
| 60 | |
| 61 | === 1.3 Parse Measurement Fields === |
| 62 | |
| 63 | {{{ |
| 64 | #!ruby |
| 65 | def processOutput(output) |
| 66 | column = output.split(" ") |
| 67 | puts "Each line process" |
| 68 | # Inject the measurements into OML |
| 69 | MPThroughput.inject("#{column[0]}", column[1], column[2], column[3], column[4], "#{column[5]}") |
| 70 | end |
| 71 | }}} |
| 72 | |
| 73 | === 1.4 Write application Script === |
| 74 | {{{ |
| 75 | #!ruby |
| 76 | !/usr/bin/env ruby |
| 77 | require 'rubygems' |
| 78 | require 'oml4r' |
| 79 | require 'file-tail' |
| 80 | APPNAME = "ofstats" |
| 81 | class MPThroughput < OML4R::MPBase |
| 82 | name :ofthroughput |
| 83 | param :srcaddr, :type => :string |
| 84 | param :numflows, :type => :int64 |
| 85 | param :numbytes, :type => :int64 |
| 86 | param :numpacket, :type => :int64 |
| 87 | param :throughput, :type => :int64 |
| 88 | param :units, :type => :string |
| 89 | end |
| 90 | class OFStatsWrapper |
| 91 | def initialize(args) |
| 92 | @addr = nil |
| 93 | @if_num = ' ' |
| 94 | @trema_port = ' ' |
| 95 | @trema_path = ' ' |
| 96 | @verbose = true |
| 97 | @numeric = ' ' |
| 98 | OML4R::init(args, :appName => "#{APPNAME}_wrapper", :domain => 'foo', :collect => 'file:-') { |argParser| |
| 99 | argParser.banner = "Reports OpenFlow stat measurements via OML\n\n" |
| 100 | argParser.on("-f" , "--file_path ADDRESS", "Path where output is saved"){ |address| @addr = address } |
| 101 | argParser.on("-i","--interface IFNUM","Interface number"){ |if_num| @if_num ="#{if_num.to_i()}" } |
| 102 | argParser.on("-q", "--no-quiet ","Don't show of stats output on console"){ @verbose = false } |
| 103 | argParser.on("-n", "--[no]-numeric ", "No attempt twill be made to look up symbolic names for host addresses"){ @numeric ='-n' } |
| 104 | } |
| 105 | unless @addr !=nil |
| 106 | raise "You did not specify path of file (-p option)" |
| 107 | end |
| 108 | end |
| 109 | class MyFile < File |
| 110 | include File::Tail |
| 111 | end |
| 112 | def start() |
| 113 | log = MyFile.new("#{@addr}") |
| 114 | log.interval = @if_num |
| 115 | log.backward(3) |
| 116 | puts "#{@if_num}" |
| 117 | log.tail { |line| print line |
| 118 | processOutput(line) |
| 119 | } |
| 120 | end |
| 121 | def processOutput(output) |
| 122 | column = output.split(" ") |
| 123 | puts "Each line process" |
| 124 | # Inject the measurements into OML |
| 125 | MPThroughput.inject("#{column[0]}", column[1], column[2], column[3], column[4], "#{column[5]}") |
| 126 | end |
| 127 | end |
| 128 | begin |
| 129 | app = OFStatsWrapper.new(ARGV) |
| 130 | app.start() |
| 131 | rescue SystemExit |
| 132 | rescue SignalException |
| 133 | puts "OFWrapper stopped." |
| 134 | rescue Exception => ex |
| 135 | puts "Error - When executing the wrapper application!" |
| 136 | puts "Error - Type: #{ex.class}" |
| 137 | puts "Error - Message: #{ex}\n\n" |
| 138 | # Uncomment the next line to get more info on errors |
| 139 | # puts "Trace - #{ex.backtrace.join("\n\t")}" |
| 140 | end |
| 141 | OML4R::close() |
| 142 | # Local Variables: |
| 143 | # mode:ruby |
| 144 | # End: |
| 145 | # vim: ft=ruby:sw=2 |
| 146 | }}} |
| 147 | |
| 148 | == 2. Write OEDL Script to run in !LabWiki == |
| 149 | |
| 150 | This script is used to orchestrate the OML application we just wrote. It is pre-loaded into your !LabWiki directory as GEC20_flowstatistics.oedl. [[BR]] |
| 151 | The steps provided here are for your reference. |
| 152 | |
| 153 | === 2.1 Define Resource name === |
| 154 | |
| 155 | Here you will identify the nodes you used for the tutorial as a combination of the <node-slicename> |
| 156 | |
| 157 | |
| 158 | {{{ |
| 159 | #!ruby |
| 160 | defProperty('source1', "switch1-rspecforMax", "ID of a resource") |
| 161 | defProperty('source2', "switch2-rspecforMax", "ID of a resource") |
| 162 | defProperty('source3', "switch1-rspecforMax", "ID of a resource") |
| 163 | defProperty('theSender1', "nodeb-rspecforMax", "ID of a resource") |
| 164 | defProperty('theSender2', "nodec-rspecforMax", "ID of a resource") |
| 165 | defProperty('theSender3', "noded-rspecforMax", "ID of a resource") |
| 166 | defProperty('theSender4', "nodec-rspecforMax", "ID of a resource") |
| 167 | defProperty('theSender5', "nodee-rspecforMax", "ID of a resource") |
| 168 | defProperty('theReceiver', "nodef-rspecforMax", "ID of a resource") |
| 169 | }}} |
| 170 | |
| 171 | === 2.2 Specify the location where Flow Statistics will be stored === |
| 172 | |
| 173 | {{{ |
| 174 | #!ruby |
| 175 | defProperty('pathfile', "/tmp/flowstats.out", "Path to file") |
| 176 | }}} |
| 177 | |
| 178 | === 2.3 Define the Application created in Step 1) in OEDL === |
| 179 | |
| 180 | |
| 181 | {{{ |
| 182 | #!ruby |
| 183 | defApplication('ofstats') do |app| |
| 184 | app.description = 'Simple Definition for the of-collect application' |
| 185 | # Define the path to the binary executable for this application |
| 186 | app.binary_path = '/usr/local/bin/learn_ofcollect.rb' |
| 187 | app.defProperty('target', 'Address to output file', '-f', {:type => :string}) |
| 188 | app.defProperty("interval","Interval",'-i', {:type => :string}) |
| 189 | app.defMeasurement('wrapper_ofthroughput') do |m| |
| 190 | m.defMetric(':srcaddr', :string) |
| 191 | m.defMetric('throughput',:int64) |
| 192 | end |
| 193 | end |
| 194 | }}} |
| 195 | |
| 196 | === 2.4 Create Node Group with Application === |
| 197 | |
| 198 | |
| 199 | {{{ |
| 200 | #!ruby |
| 201 | defGroup('Source3', property.source1) do |node| |
| 202 | node.addApplication("ofstats") do |app| |
| 203 | app.setProperty('target', property.pathfile) |
| 204 | app.setProperty('interval', property.intervalcol) |
| 205 | app.measure('wrapper_ofthroughput', :samples => 1) |
| 206 | end |
| 207 | end |
| 208 | }}} |
| 209 | |
| 210 | === 2.5 Define parameters for graphs === |
| 211 | |
| 212 | {{{ |
| 213 | #!ruby |
| 214 | defGraph 'Throughput' do |g| |
| 215 | g.ms('wrapper_ofthroughput').select(:oml_ts_client, :throughput, :srcaddr) |
| 216 | g.caption "Throughput of Flows" |
| 217 | g.type 'line_chart3' |
| 218 | g.mapping :x_axis => :oml_ts_client, :y_axis => :throughput, :group_by => :srcaddr |
| 219 | g.xaxis :legend => 'oml_ts' |
| 220 | g.yaxis :legend => 'Throughput', :ticks => {:format => 's'} |
| 221 | end |
| 222 | |
| 223 | }}} |
| 224 | |
| 225 | |
| 226 | |
| 227 | = [wiki:GEC21Agenda/LabWiki/ModuleE/Execute Next: Run Experiment] = |
| 228 | = [wiki:GEC21Agenda/LabWiki/ModuleE Module E Introduction] = |