| 267 | |
| 268 | {{{ |
| 269 | #Example: Writing an OML Layer 2 Ping Application (aka pingPlus)# |
| 270 | |
| 271 | ##Prerequisites## |
| 272 | Setup your experiment as shown here with the pingPlus package [link](http://www.gpolab.bbn.com/experiment-support/gec17/pingPlus/pingPlus_v3.tar.gz): |
| 273 | http://groups.geni.net/geni/wiki/Tutorials/ICDCS2013/GettingStartedWithGENI_I/Procedure/DesignSetup |
| 274 | |
| 275 | ##Results## |
| 276 | |
| 277 | ![](_) |
| 278 | __Figure 1__. My layer 2 Ping Application |
| 279 | |
| 280 | |
| 281 | ##Write your Application## |
| 282 | |
| 283 | To write a omlified ping application, we can firstly parse output to get Measurement points: |
| 284 | |
| 285 | def process_output(row) |
| 286 | if not (parse= /RQ:'(?<pktsnt1>\d*)\+(?<pktsnt2>\d*)' to (?<host>[a-f0-9:]*)/.match(row)).nil? |
| 287 | puts "ReturnQual #{parse[:pktsnt1]}\n" |
| 288 | MPrt.inject(parse[:pktsnt1], parse[:pktsnt2],parse[:host]) |
| 289 | |
| 290 | elsif not (parse= /RL:(?<numofpkt1>\d*)\+(?<numofpkt2>\d*)=(?<totpktrec>\d*) from (?<dest_hw_addr>[a-f0-9:]*)/.match(row)).nil? |
| 291 | puts "ReturnLength\n" |
| 292 | p parse |
| 293 | p MPrl.inject(parse[:numofpkt1],parse[:numofpkt2],parse[:totpktrec],parse[:dest_hw_addr]) |
| 294 | |
| 295 | elsif not (parse = /RTT = (?<rtt>[0-9.]*)/.match(row)).nil? |
| 296 | puts "RoundTripTime #{parse[:rtt]}\n" |
| 297 | p parse |
| 298 | p MPrtt.inject(parse[:rtt]) |
| 299 | end |
| 300 | end |
| 301 | |
| 302 | Then write the whole application script: |
| 303 | |
| 304 | #!/usr/bin/ruby1.9.1 |
| 305 | require 'rubygems' |
| 306 | require 'oml4r' |
| 307 | |
| 308 | class MPrt < OML4R::MPBase |
| 309 | name :pingrt |
| 310 | param :pktsnt1, :type => :uint64 |
| 311 | param :pktsnt2, :type => :uint64 |
| 312 | param :host, :type => :string |
| 313 | end |
| 314 | |
| 315 | class MPrl < OML4R::MPBase |
| 316 | name :pingrl |
| 317 | param :totpktrec, :type => :uint64 |
| 318 | param :numofpkt1, :type => :uint64 |
| 319 | param :numofpkt2, :type => :uint64 |
| 320 | param :dest_hw_addr, :type => :string |
| 321 | end |
| 322 | |
| 323 | class MPrtt < OML4R::MPBase |
| 324 | name :pingrtt |
| 325 | param :rtt, :type => :double |
| 326 | end |
| 327 | |
| 328 | class Pingl2Wrapper |
| 329 | |
| 330 | def initialize(args) |
| 331 | @addr = nil |
| 332 | @if_num = '' |
| 333 | @eth = nil |
| 334 | @verbose = true |
| 335 | @numeric = '' |
| 336 | |
| 337 | leftover = OML4R::init(args, :appName => 'pingl2') do |argParser| |
| 338 | argParser.banner = "Runs layer 2 ping and reports measurements via OML\n\n" |
| 339 | argParser.on("-a" , "--dest_hw_addr ADDRESS", "Hardware address to ping (the -a switch is optional)"){ |address| @addr = address.to_s() } |
| 340 | argParser.on("-i","--interface IFNUM","Interface number"){ |if_num| @if_num ="#{if_num.to_i()}" } |
| 341 | argParser.on("-e", "--eth ETHTYPE","Ethernet Type") { |ethtype| @eth = ethtype.to_s() } |
| 342 | argParser.on("-c","--count NUMBER","Number of pings (default: infinite)"){ |count| @count = "#{count.to_i()}"} |
| 343 | argParser.on("-q", "--no-quiet ","Don't show layer 2 ping output on console"){ @verbose = false } |
| 344 | argParser.on("-n", "--[no]-numeric ", "No attempt twill be made to look up symbolic names for host addresses"){ @numeric ='-n' } |
| 345 | end |
| 346 | |
| 347 | if @addr.nil? |
| 348 | if leftover.length > 0 |
| 349 | @addr = leftover [0] |
| 350 | else |
| 351 | raise "You did not specify an address to ping!" |
| 352 | end |
| 353 | end |
| 354 | |
| 355 | end |
| 356 | |
| 357 | def process_output(row) |
| 358 | if not (parse= /RQ:'(?<pktsnt1>\d*)\+(?<pktsnt2>\d*)' to (?<host>[a-f0-9:]*)/.match(row)).nil? |
| 359 | puts "ReturnQual #{parse[:pktsnt1]}\n" |
| 360 | MPrt.inject(parse[:pktsnt1], parse[:pktsnt2],parse[:host]) |
| 361 | |
| 362 | elsif not (parse= /RL:(?<numofpkt1>\d*)\+(?<numofpkt2>\d*)=(?<totpktrec>\d*) from (?<dest_hw_addr>[a-f0-9:]*)/.match(row)).nil? |
| 363 | puts "ReturnLength\n" |
| 364 | p parse |
| 365 | p MPrl.inject(parse[:numofpkt1],parse[:numofpkt2],parse[:totpktrec],parse[:dest_hw_addr]) |
| 366 | |
| 367 | elsif not (parse = /RTT = (?<rtt>[0-9.]*)/.match(row)).nil? |
| 368 | puts "RoundTripTime #{parse[:rtt]}\n" |
| 369 | p parse |
| 370 | p MPrtt.inject(parse[:rtt]) |
| 371 | end |
| 372 | end |
| 373 | |
| 374 | def pingl2() |
| 375 | @pingio = IO.popen("/bin/pingPlus #{@addr} #{@eth} #{@if_num} #{@count}") |
| 376 | while true |
| 377 | row = @pingio.readline |
| 378 | puts row if @verbose |
| 379 | process_output(row) |
| 380 | end |
| 381 | end |
| 382 | |
| 383 | def start() |
| 384 | return if not @pingio.nil? |
| 385 | |
| 386 | # handle for OMF's exit command |
| 387 | a = Thread.new do |
| 388 | $stdin.each do |line| |
| 389 | if /^exit/ =~ line |
| 390 | Process.kill("INT",0) |
| 391 | end |
| 392 | end |
| 393 | end |
| 394 | |
| 395 | # Handle Ctrl+C and OMF's SIGTERM |
| 396 | Signal.trap("INT", stop) |
| 397 | Signal.trap("TERM", stop) |
| 398 | |
| 399 | begin |
| 400 | pingl2 |
| 401 | rescue EOFError |
| 402 | |
| 403 | end |
| 404 | end |
| 405 | |
| 406 | def stop() |
| 407 | return if @pingio.nil? |
| 408 | # Kill the ping process, which will result in EOFError from ping() |
| 409 | Process.kill("INT", @pingio.pid) |
| 410 | end |
| 411 | |
| 412 | end |
| 413 | begin |
| 414 | $stderr.puts "INFO\tpingl2 2.11.0\n" |
| 415 | app = Pingl2Wrapper.new(ARGV) |
| 416 | app.start() |
| 417 | sleep 1 |
| 418 | rescue Interrupt |
| 419 | rescue Exception => ex |
| 420 | $stderr.puts "Error\t#{ex}\n" |
| 421 | end |
| 422 | |
| 423 | |
| 424 | |
| 425 | # Local Variables: |
| 426 | # mode:ruby |
| 427 | # End: |
| 428 | # vim: ft=ruby:sw=2 |
| 429 | |
| 430 | ##Test your Application## |
| 431 | Finally write OEDL Script to test application: |
| 432 | |
| 433 | defProperty('source1', "client-testpingplus", "ID of a resource") |
| 434 | #defProperty('source2', "ig-utah-testBBN", "ID of a resource") |
| 435 | #defProperty('source3', "nodeC-createexoimage", "ID of a resource") |
| 436 | #defProperty('source4', "nodeD-createexoimage", "ID of a resource") |
| 437 | #defProperty('source5', "nodeE-createexoimage", "ID of a resource") |
| 438 | defProperty('graph', true, "Display graph or not") |
| 439 | |
| 440 | |
| 441 | defProperty('sinkaddr11', 'fe:16:3e:00:74:38', "Ping destination address") |
| 442 | defProperty('eth11','eth1',"Output Eth interface") |
| 443 | defProperty('sinkaddr12', 'fe:16:3e:00:74:38', "Ping destination address") |
| 444 | |
| 445 | #defProperty('sinkaddr11', '192.168.6.10', "Ping destination address") |
| 446 | #defProperty('sinkaddr12', '192.168.5.12', "Ping destination address") |
| 447 | |
| 448 | #defProperty('sinkaddr21', '192.168.4.11', "Ping destination address") |
| 449 | #defProperty('sinkaddr22', '192.168.2.12', "Ping destination address") |
| 450 | #defProperty('sinkaddr23', '192.168.1.13', "Ping destination address") |
| 451 | |
| 452 | #defProperty('sinkaddr31', '192.168.5.11', "Ping destination address") |
| 453 | #defProperty('sinkaddr32', '192.168.2.10', "Ping destination address") |
| 454 | #defProperty('sinkaddr33', '192.168.3.13', "Ping destination address") |
| 455 | #defProperty('sinkaddr34', '192.168.6.14', "Ping destination address") |
| 456 | |
| 457 | #defProperty('sinkaddr41', '192.168.1.10', "Ping destination address") |
| 458 | #defProperty('sinkaddr42', '192.168.3.12', "Ping destination address") |
| 459 | |
| 460 | #defProperty('sinkaddr51', '192.168.6.12', "Ping destination address") |
| 461 | |
| 462 | defApplication('ping') do |app| |
| 463 | app.description = 'Simple Definition for the pingl2 application' |
| 464 | # Define the path to the binary executable for this application |
| 465 | app.binary_path = '/usr/local/bin/pingl2' |
| 466 | # Define the configurable parameters for this application |
| 467 | # For example if target is set to foo.com and count is set to 2, then the |
| 468 | # application will be started with the command line: |
| 469 | # /usr/bin/ping-oml2 -a foo.com -c 2 |
| 470 | app.defProperty('target', 'Address to ping', '-a', {:type => :string}) |
| 471 | app.defProperty('count', 'Number of times to ping', '-c', {:type => :integer}) |
| 472 | app.defProperty('if_num', 'interface number', '-i', {:type => :integer}) |
| 473 | app.defProperty('eth', 'Ethernet Type', '-e', {:type => :string}) |
| 474 | # Define the OML2 measurement point that this application provides. |
| 475 | # Here we have only one measurement point (MP) named 'ping'. Each measurement |
| 476 | # sample from this MP will be composed of a 4-tuples (addr,ttl,rtt,rtt_unit) |
| 477 | app.defMeasurement('ping') do |m| |
| 478 | m.defMetric('hw_dest_addr',:string) |
| 479 | m.defMetric('rtt',:double) |
| 480 | end |
| 481 | end |
| 482 | defGroup('Source1', property.source1) do |node| |
| 483 | node.addApplication("ping") do |app| |
| 484 | app.setProperty('target', property.sinkaddr11) |
| 485 | app.setProperty('count', 30) |
| 486 | app.setProperty('if_num', 10002) |
| 487 | app.setProperty('eth',property.eth11) |
| 488 | app.measure('ping', :samples => 1) |
| 489 | end |
| 490 | end |
| 491 | |
| 492 | |
| 493 | #defGroup('Source2', property.source2) do |node| |
| 494 | # node.addApplication("ping") do |app| |
| 495 | # app.setProperty('target', property.sinkaddr11) |
| 496 | # app.setProperty('count', 30) |
| 497 | #app.setProperty('interval', 1) |
| 498 | # app.measure('ping', :samples => 1) |
| 499 | #end |
| 500 | #end |
| 501 | |
| 502 | |
| 503 | onEvent(:ALL_UP_AND_INSTALLED) do |event| |
| 504 | info "Starting the ping" |
| 505 | after 5 do |
| 506 | allGroups.startApplications |
| 507 | end |
| 508 | after 70 do |
| 509 | info "Stopping the ping" |
| 510 | allGroups.stopApplications |
| 511 | Experiment.done |
| 512 | end |
| 513 | end |
| 514 | |
| 515 | defGraph 'RTT' do |g| |
| 516 | g.ms('ping').select(:oml_seq, :hw_dest_addr, :rtt) |
| 517 | g.caption "RTT of received packets." |
| 518 | g.type 'line_chart3' |
| 519 | g.mapping :x_axis => :oml_seq, :y_axis => :rtt, :group_by => :hw_dest_addr |
| 520 | g.xaxis :legend => 'oml_seq' |
| 521 | g.yaxis :legend => 'rtt', :ticks => {:format => 's'} |
| 522 | end |
| 523 | |
| 524 | |
| 525 | |
| 526 | ##Troubleshooting## |
| 527 | To debug and test application manually on RC (resource controller): |
| 528 | |
| 529 | Manually run the command and store data on any location in the RC (~/testpingnow.out) |
| 530 | |
| 531 | env -i /usr/local/bin/pingl2 -a fe:16:3e:00:74:38 -c 5 -i 10002 -e eth1 --oml-id pingl2 --oml-domain tetspingl2 --oml-collect file:/home/dbhat/testpingnow.out |
| 532 | |
| 533 | Contents of ~/testpingnow.out: |
| 534 | |
| 535 | protocol: 4 |
| 536 | content: text |
| 537 | domain: tetspingl2 |
| 538 | start-time: 1387350654 |
| 539 | sender-id: pingl2 |
| 540 | app-name: pingl2 |
| 541 | schema: 0 _experiment_metadata subject:string key:string value:string |
| 542 | schema: 1 pingl2_pingrt pktsnt1:integer pktsnt2:integer host:string |
| 543 | schema: 2 pingl2_pingrl totpktrec:integer numofpkt1:integer numofpkt2:integer dest_hw_addr:string |
| 544 | schema: 3 pingl2_pingrtt rtt:double |
| 545 | |
| 546 | 0.008326062 1 1 8769 3486 fe:16:3e:0:74:38 |
| 547 | 0.008561018 2 1 8769 3486 12255 fe:16:3e:0:74:38 |
| 548 | 0.008747358 3 1 4.083984 |
| 549 | 0.008879672 1 2 219 3026 fe:16:3e:0:74:38 |
| 550 | 0.009011919 2 2 8769 3486 12255 fe:16:3e:0:74:38 |
| 551 | 0.009165199 3 2 1.743896 |
| 552 | |
| 553 | }}} |