#!/usr/bin/env ruby $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib') require 'drb/drb' require 'gli' require 'phut' require 'rake' require 'trema' ENV['GLI_DEBUG'] = 'true' # OpenFlow controller framework. module Trema # trema command. module App extend GLI::App desc 'Displays the current runtime version' program_desc 'Trema command-line tool' version Trema::VERSION desc 'Be verbose' switch [:v, :verbose], negatable: false desc 'Runs a trema application' arg_name 'controller' command :run do |c| c.desc 'Runs as a daemon' c.switch [:d, :daemonize], negatable: false c.desc 'Specifies emulated network configuration' c.flag [:c, :conf] c.desc 'Use OpenFlow1.3' c.switch :openflow13, default_value: false c.desc 'Overrides the default openflow channel port' c.flag [:p, :port] c.desc 'Location to put pid files' c.flag [:P, :pid_dir], default_value: Trema::DEFAULT_PID_DIR c.desc 'Location to put log files' c.flag [:L, :log_dir], default_value: Trema::DEFAULT_LOG_DIR c.desc 'Location to put socket files' c.flag [:S, :socket_dir], default_value: Trema::DEFAULT_SOCKET_DIR c.action do |global_options, options, args| Phut.pid_dir = options[:pid_dir] Phut.log_dir = options[:log_dir] Phut.socket_dir = options[:socket_dir] require 'pio/open_flow13' if options[:openflow13] require 'trema/switch' Trema::Command.new.run(args, global_options.merge(options)) end end desc 'Print all flow entries' arg_name 'switches...' command :dump_flows do |c| c.desc 'Location to put socket files' c.flag [:S, :socket_dir], default_value: Phut.socket_dir c.action do |_global_options, options, args| args.each do |each| puts Trema.fetch(each, options.fetch(:socket_dir)).dump_flows end end end desc 'Sends UDP packets to destination host' command :send_packets do |c| c.desc 'host that sends packets' c.flag [:s, :source] c.desc 'host that receives packets' c.flag [:d, :dest] c.desc 'number of packets to send' c.flag [:n, :npackets], default_value: 1 c.desc 'Location to put socket files' c.flag [:S, :socket_dir], default_value: Trema::DEFAULT_SOCKET_DIR c.action do |_global_options, options, _args| fail '--source option is mandatory' if options[:source].nil? fail '--dest option is mandatory' if options[:dest].nil? dest = Trema.fetch(options.fetch(:dest), options.fetch(:socket_dir)) Phut::VhostDaemon. process(options.fetch(:source), options.fetch(:socket_dir)). send_packets(dest, options.fetch(:npackets).to_i) end end desc 'Shows stats of packets' arg_name 'host' command :show_stats do |c| c.desc 'Location to find socket files' c.flag [:S, :socket_dir], default_value: Trema::DEFAULT_SOCKET_DIR c.action do |_global_options, options, args| help_now!('host is required') if args.empty? stats = Phut::VhostDaemon.process(args[0], options[:socket_dir]).stats dests = stats[:tx].map { |each| each.ip_destination_address.to_s }.uniq txstats = dests.map do |each| all = stats[:tx].select { |pkt| pkt.ip_destination_address == each } "#{all.first.ip_source_address} -> #{each} = " \ "#{all.size} packet#{all.size > 1 ? 's' : ''}" end unless txstats.empty? puts 'Packets sent:' txstats.each { |each| puts " #{each}" } end sources = stats[:rx].map { |each| each.ip_source_address.to_s }.uniq rxstats = sources.map do |each| all = stats[:rx].select { |pkt| pkt.ip_source_address == each } "#{each} -> #{all.first.ip_destination_address} = " \ "#{all.size} packet#{all.size > 1 ? 's' : ''}" end unless rxstats.empty? puts 'Packets received:' rxstats.each { |each| puts " #{each}" } end end end desc "Brings a switch's specified port up" command :port_up do |c| c.desc 'switch name' c.flag [:s, :switch] c.desc 'port' c.flag [:p, :port] c.desc 'Location to put socket files' c.flag [:S, :socket_dir], default_value: Trema::DEFAULT_SOCKET_DIR c.action do |_global_options, options, _args| fail '--switch option is mandatory' if options[:switch].nil? fail '--port option is mandatory' if options[:port].nil? Trema.trema_processes(options[:socket_dir]).each do |trema| begin trema.port_up(options[:switch], options[:port].to_i) rescue next end end end end desc "Brings a switch's specified port down" command :port_down do |c| c.desc 'switch name' c.flag [:s, :switch] c.desc 'port' c.flag [:p, :port] c.desc 'Location to put socket files' c.flag [:S, :socket_dir], default_value: Trema::DEFAULT_SOCKET_DIR c.action do |_global_options, options, _args| fail '--switch option is mandatory' if options[:switch].nil? fail '--port option is mandatory' if options[:port].nil? Trema.trema_processes(options[:socket_dir]).each do |trema| begin trema.port_down(options[:switch], options[:port].to_i) rescue next end end end end desc 'Stops a vswitch or a vhost' arg_name 'name' command :stop do |c| c.desc 'Location to find socket files' c.flag [:S, :socket_dir], default_value: Trema::DEFAULT_SOCKET_DIR c.action do |_global_options, options, args| help_now! if args.size != 1 Trema.fetch(args[0], options[:socket_dir]).stop end end desc 'Deletes a virtual link' arg_name 'endpoint1 endpoint2' command :delete_link do |c| c.desc 'Location to find socket files' c.flag [:S, :socket_dir], default_value: Trema::DEFAULT_SOCKET_DIR c.action do |_global_options, options, args| help_now! if args.size != 2 Trema.fetch(args, options[:socket_dir]).stop end end desc 'Starts the stopped vswitch or vhost again' arg_name 'name' command :start do |c| c.desc 'Location to find socket files' c.flag [:S, :socket_dir], default_value: Trema::DEFAULT_SOCKET_DIR c.action do |_global_options, options, args| help_now! if args.size != 1 Trema.fetch(args[0], options[:socket_dir]).run end end desc 'Terminates all trema processes' arg_name 'controller_name' command :killall do |c| c.desc 'Kill all known trema processes' c.switch :all, default_value: false, negatable: false c.desc 'Location to find socket files' c.flag [:S, :socket_dir], default_value: Trema::DEFAULT_SOCKET_DIR c.action do |_global_options, options, args| if options[:all] Trema.trema_processes(options[:socket_dir]).each do |each| begin each.killall rescue DRb::DRbConnError true # OK (trema process exitted). end end else help_now! if args.size != 1 begin Trema.trema_process(args[0], options[:socket_dir]).killall rescue DRb::DRbConnError true # OK (trema process exitted). end end end end default_command :help exit run(ARGV) end end