= Module E Design and Setup = == 1 Write Application for !OpenFlow Statistics Measurement == === 1.1 Get Flow Statistics you want to measure === Define stats_reply in /root/learning-switch-copy.rb as follows {{{ def stats_reply (dpid, message) puts "[flow stats_reply #{@my_switch}]---------------------------------" byte_count = 0 packet_count = 0 flow_count = 0 throughput = 0 inst_throughput =0 total_flow_count = message.stats.length if(total_flow_count != 0) message.stats.each do | flow_msg | # WARNING: This only works for the EXACT case of two actions. If we add more than two actions the flow monitoring # will break. flow_ip = flow_msg.match puts "This is the message #{flow_ip.nw_src}" flow_count = flow_count + 1 byte_count += flow_msg.byte_count packet_count += flow_msg.packet_count if flow_msg.duration_sec + flow_msg.duration_nsec/1000000000 != 0 throughput += flow_msg.byte_count/(flow_msg.duration_sec + flow_msg.duration_nsec/1000000000) file = File.open("/tmp/flowstats.out", "a") file.puts "#{flow_ip.nw_src} #{flow_count.to_s} #{byte_count} #{packet_count} #{throughput} Bps" file.close end end end @prev_byte_count = byte_count end }}} === 1.2 Define Flow Statistics measurement points === {{{ class MPThroughput < OML4R::MPBase name :ofthroughput param :srcaddr, :type => :string param :numflows, :type => :int64 param :numbytes, :type => :int64 param :numpacket, :type => :int64 param :throughput, :type => :int64 param :units, :type => :string end }}} === 1.3 Parse Measurement Fields === {{{ def processOutput(output) column = output.split(" ") puts "Each line process" # Inject the measurements into OML MPThroughput.inject("#{column[0]}", column[1], column[2], column[3], column[4], "#{column[5]}") end }}} === 1.4 Write application Script === {{{ !/usr/bin/env ruby require 'rubygems' require 'oml4r' require 'file-tail' APPNAME = "ofstats" class MPThroughput < OML4R::MPBase name :ofthroughput param :srcaddr, :type => :string param :numflows, :type => :int64 param :numbytes, :type => :int64 param :numpacket, :type => :int64 param :throughput, :type => :int64 param :units, :type => :string end class OFStatsWrapper def initialize(args) @addr = nil @if_num = ' ' @trema_port = ' ' @trema_path = ' ' @verbose = true @numeric = ' ' OML4R::init(args, :appName => "#{APPNAME}_wrapper", :domain => 'foo', :collect => 'file:-') { |argParser| argParser.banner = "Reports OpenFlow stat measurements via OML\n\n" argParser.on("-f" , "--file_path ADDRESS", "Path where output is saved"){ |address| @addr = address } argParser.on("-i","--interface IFNUM","Interface number"){ |if_num| @if_num ="#{if_num.to_i()}" } argParser.on("-q", "--no-quiet ","Don't show of stats output on console"){ @verbose = false } argParser.on("-n", "--[no]-numeric ", "No attempt twill be made to look up symbolic names for host addresses"){ @numeric ='-n' } } unless @addr !=nil raise "You did not specify path of file (-p option)" end end class MyFile < File include File::Tail end def start() log = MyFile.new("#{@addr}") log.interval = @if_num log.backward(3) puts "#{@if_num}" log.tail { |line| print line processOutput(line) } end def processOutput(output) column = output.split(" ") puts "Each line process" # Inject the measurements into OML MPThroughput.inject("#{column[0]}", column[1], column[2], column[3], column[4], "#{column[5]}") end end begin app = OFStatsWrapper.new(ARGV) app.start() rescue SystemExit rescue SignalException puts "OFWrapper stopped." rescue Exception => ex puts "Error - When executing the wrapper application!" puts "Error - Type: #{ex.class}" puts "Error - Message: #{ex}\n\n" # Uncomment the next line to get more info on errors # puts "Trace - #{ex.backtrace.join("\n\t")}" end OML4R::close() # Local Variables: # mode:ruby # End: # vim: ft=ruby:sw=2 }}} == 2. Write OEDL Script to run in LabWiki == === 2.1 Define Resource name === Here you will identify the nodes you used for the tutorial as a combination of the {{{ defProperty('source1', "switch1-rspecforMax", "ID of a resource") defProperty('source2', "switch2-rspecforMax", "ID of a resource") defProperty('source3', "switch1-rspecforMax", "ID of a resource") defProperty('theSender1', "nodeb-rspecforMax", "ID of a resource") defProperty('theSender2', "nodec-rspecforMax", "ID of a resource") defProperty('theSender3', "noded-rspecforMax", "ID of a resource") defProperty('theSender4', "nodec-rspecforMax", "ID of a resource") defProperty('theSender5', "nodee-rspecforMax", "ID of a resource") defProperty('theReceiver', "nodef-rspecforMax", "ID of a resource") }}} === 2.2 Specify the location where Flow Statistics will be stored === {{{ defProperty('pathfile', "/tmp/flowstats.out", "Path to file") }}} === 2.3 Define the Application created in Step 1) in OEDL === {{{ defApplication('ofstats') do |app| app.description = 'Simple Definition for the of-collect application' # Define the path to the binary executable for this application app.binary_path = '/usr/local/bin/learn_ofcollect.rb' app.defProperty('target', 'Address to output file', '-f', {:type => :string}) app.defProperty("interval","Interval",'-i', {:type => :string}) app.defMeasurement('wrapper_ofthroughput') do |m| m.defMetric(':srcaddr', :string) m.defMetric('throughput',:int64) end end }}} === 2.4 Create Node Group with Application === {{{ defGroup('Source3', property.source1) do |node| node.addApplication("ofstats") do |app| app.setProperty('target', property.pathfile) app.setProperty('interval', property.intervalcol) app.measure('wrapper_ofthroughput', :samples => 1) end end }}} === 2.5 Define parameters for graphs === {{{ defGraph 'Throughput' do |g| g.ms('wrapper_ofthroughput').select(:oml_ts_client, :throughput, :srcaddr) g.caption "Throughput of Flows" g.type 'line_chart3' g.mapping :x_axis => :oml_ts_client, :y_axis => :throughput, :group_by => :srcaddr g.xaxis :legend => 'oml_ts' g.yaxis :legend => 'Throughput', :ticks => {:format => 's'} end }}} === 2.5 The complete OEDL script is given Below === OEDL script {{{ defProperty('source1', "switch1-rspecforMax", "ID of a resource") defProperty('source2', "switch2-rspecforMax", "ID of a resource") defProperty('source3', "switch1-rspecforMax", "ID of a resource") defProperty('theSender1', "nodeb-rspecforMax", "ID of a resource") defProperty('theSender2', "nodec-rspecforMax", "ID of a resource") defProperty('theSender3', "noded-rspecforMax", "ID of a resource") defProperty('theSender4', "nodec-rspecforMax", "ID of a resource") defProperty('theSender5', "nodee-rspecforMax", "ID of a resource") defProperty('theReceiver', "nodef-rspecforMax", "ID of a resource") defProperty('intervalcol',"1", "Interval to Tail") defProperty('pathfile', "/tmp/flowstats.out", "Path to file") defApplication('ofstats') do |app| app.description = 'Simple Definition for the of-collect application' # Define the path to the binary executable for this application app.binary_path = '/usr/local/bin/learn_ofcollect.rb' app.defProperty('target', 'Address to output file', '-f', {:type => :string}) app.defProperty("interval","Interval",'-i', {:type => :string}) app.defMeasurement('wrapper_ofthroughput') do |m| m.defMetric(':srcaddr', :string) m.defMetric('throughput',:int64) end end defApplication('trema') do |app| app.description = 'This app runs trema from command line' app.binary_path = '/usr/bin/trema run /root/learning-switch.rb' end defApplication('iperfserv') do |app| app.description = "manually run Iperf server" app.binary_path = "/usr/bin/iperf -s" end defApplication('iperfclient') do |app| app.description = "manually run Iperf client" app.binary_path = "/usr/bin/iperf -c 192.168.1.15 -t 800 -P 2 -i 2" end defApplication('clean_iperf') do |app| app.description = 'Some commands to ensure that we start with a clean slate' app.binary_path = 'killall -s9 iperf' app.quiet = true end defGroup('Source2', property.source1,property.source2) do |node| node.addApplication("trema") end defGroup('Source3', property.source1) do |node| node.addApplication("ofstats") do |app| app.setProperty('target', property.pathfile) app.setProperty('interval', property.intervalcol) app.measure('wrapper_ofthroughput', :samples => 1) end end defGroup('Sender1', property.theSender1) do |node| node.addApplication("iperfclient") do |app| end end defGroup('Sender2', property.theSender2) do |node| node.addApplication("iperfclient") do |app| end end defGroup('Sender3', property.theSender3) do |node| node.addApplication("iperfclient") do |app| end end defGroup('Sender4', property.theSender4) do |node| node.addApplication("iperfclient") do |app| end end defGroup('Sender5', property.theSender5) do |node| node.addApplication("iperfclient") do |app| end end defGroup('Receiver', property.theReceiver) do |node| node.addApplication("iperfserv") do |app| end end onEvent(:ALL_UP_AND_INSTALLED) do |event| info "Starting the collect" info "Starting the collect" after 2 do group('Source2').startApplications group('Receiver').startApplications end after 10 do group('Sender1').startApplications end after 20 do group('Sender2').startApplications group('Source3').startApplications end after 40 do group('Sender3').startApplications end after 60 do group('Sender4').startApplications end after 80 do group('Sender5').startApplications end after 120 do info "Stopping the collect" allGroups.stopApplications Experiment.done end end defGraph 'Throughput' do |g| g.ms('wrapper_ofthroughput').select(:oml_ts_client, :throughput, :srcaddr) g.caption "Throughput of Flows" g.type 'line_chart3' g.mapping :x_axis => :oml_ts_client, :y_axis => :throughput, :group_by => :srcaddr g.xaxis :legend => 'oml_ts' g.yaxis :legend => 'Throughput', :ticks => {:format => 's'} end }}} [wiki:GEC20Agenda/LabWiki LabWiki Tutorial]