class SippyCup::Runner
Attributes
Public Class Methods
Create a runner from a scenario
@param [Scenario, XMLScenario] scenario The scenario to execute @param [Hash] opts Options to modify the runner @option opts [optional, true, false] :full_sipp_output Whether or not to copy SIPp's stdout/stderr to the parent process. Defaults to true. @option opts [optional, Logger] :logger A logger to use in place of the internal logger to STDOUT. @option opts [optional, String] :command The command to execute. This is mostly available for testing.
# File lib/sippy_cup/runner.rb, line 21 def initialize(scenario, opts = {}) @scenario = scenario @scenario_options = @scenario.scenario_options defaults = { full_sipp_output: true } @options = defaults.merge(opts) @command = @options[:command] @logger = @options[:logger] || Logger.new(STDOUT) end
Public Instance Methods
Runs the loaded scenario using SIPp
# File lib/sippy_cup/runner.rb, line 35 def run @input_files = @scenario.to_tmpfiles @logger.info "Preparing to run SIPp command: #{command}" execute_with_redirected_streams wait unless @options[:async] ensure cleanup_input_files unless @options[:async] end
Tries to stop SIPp by killing the target PID
@raises Errno::ESRCH when the PID does not correspond to a known process @raises Errno::EPERM when the process referenced by the PID cannot be killed
# File lib/sippy_cup/runner.rb, line 53 def stop Process.kill "KILL", @sipp_pid if @sipp_pid end
Waits for the runner to finish execution
@raises Errno::ENOENT when the SIPp executable cannot be found @raises SippyCup::ExitOnInternalCommand
when SIPp exits on an internal command. Calls may have been processed @raises SippyCup::NoCallsProcessed
when SIPp exit normally, but has processed no calls @raises SippyCup::FatalError
when SIPp encounters a fatal failure @raises SippyCup::FatalSocketBindingError
when SIPp fails to bind to the specified socket @raises SippyCup::SippGenericError
when SIPp encounters another type of error
@return Boolean true if execution succeeded without any failed calls, false otherwise
# File lib/sippy_cup/runner.rb, line 69 def wait exit_status = Process.wait2 @sipp_pid.to_i @err_rd.close if @err_rd @stdout_rd.close if @stdout_rd final_result = process_exit_status exit_status, @stderr_buffer if final_result @logger.info "Test completed successfully!" else @logger.info "Test completed successfully but some calls failed." end @logger.info "Statistics logged at #{File.expand_path @scenario_options[:stats_file]}" if @scenario_options[:stats_file] final_result ensure cleanup_input_files end
Private Instance Methods
# File lib/sippy_cup/runner.rb, line 205 def cleanup_input_files @input_files.values.compact.each do |value| value.close value.unlink end if @input_files end
# File lib/sippy_cup/runner.rb, line 88 def command @command ||= begin command = "sudo $(which sipp)" command_options.each_pair do |key, value| command << (value ? " -#{key} #{value}" : " -#{key}") end command << " #{@scenario_options[:destination]}" end end
# File lib/sippy_cup/runner.rb, line 98 def command_options options = { p: @scenario_options[:source_port] || '8836', sf: @input_files[:scenario].path, } max_concurrent = @scenario_options[:concurrent_max] || @scenario_options[:max_concurrent] options[:l] = max_concurrent if max_concurrent options[:m] = @scenario_options[:number_of_calls] if @scenario_options[:number_of_calls] options[:r] = @scenario_options[:calls_per_second] if @scenario_options[:calls_per_second] options[:s] = @scenario_options[:to].to_s.split('@').first if @scenario_options[:to] options[:i] = @scenario_options[:source] if @scenario_options[:source] options[:mp] = @scenario_options[:media_port] if @scenario_options[:media_port] if @scenario_options[:calls_per_second_max] options[:no_rate_quit] = nil options[:rate_max] = @scenario_options[:calls_per_second_max] options[:rate_increase] = @scenario_options[:calls_per_second_incr] || 1 options[:rate_interval] = @scenario_options[:calls_per_second_interval] if @scenario_options[:calls_per_second_interval] end if @scenario_options[:stats_file] options[:trace_stat] = nil options[:stf] = @scenario_options[:stats_file] options[:fd] = @scenario_options[:stats_interval] || 1 end if @scenario_options[:summary_report_file] options[:trace_screen] = nil options[:screen_file] = @scenario_options[:summary_report_file] end if @scenario_options[:errors_report_file] options[:trace_err] = nil options[:error_file] = @scenario_options[:errors_report_file] end if @scenario_options[:transport_mode] options[:t] = @scenario_options[:transport_mode] end if @scenario_options[:scenario_variables] options[:inf] = @scenario_options[:scenario_variables] end options.merge! @scenario_options[:options] if @scenario_options[:options] options end
# File lib/sippy_cup/runner.rb, line 149 def execute_with_redirected_streams @err_rd, err_wr = IO.pipe stdout_target = if @options[:full_sipp_output] @stdout_rd, stdout_wr = IO.pipe stdout_wr else '/dev/null' end @sipp_pid = spawn command, err: err_wr, out: stdout_target @stderr_buffer = String.new Thread.new do err_wr.close until @err_rd.eof? buffer = @err_rd.readpartial(1024).strip @stderr_buffer += buffer $stderr << buffer if @options[:full_sipp_output] end end if @stdout_rd @stdout_buffer = String.new Thread.new do stdout_wr.close until @stdout_rd.eof? buffer = @stdout_rd.readpartial(1024).strip @stdout_buffer += buffer $stdout << buffer end end end end
# File lib/sippy_cup/runner.rb, line 185 def process_exit_status(process_status, error_message = nil) exit_code = process_status[1].exitstatus case exit_code when 0 true when 1 false when 97 raise SippyCup::ExitOnInternalCommand, error_message when 99 raise SippyCup::NoCallsProcessed, error_message when 255 raise SippyCup::FatalError, error_message when 254 raise SippyCup::FatalSocketBindingError, error_message else raise SippyCup::SippGenericError, error_message end end