sippy_cup 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +9 -9
- data/.gitignore +3 -0
- data/.rspec +1 -0
- data/.travis.yml +15 -0
- data/CHANGELOG.md +20 -0
- data/Gemfile +2 -0
- data/Guardfile +4 -9
- data/README.markdown +28 -28
- data/Rakefile +2 -3
- data/bin/sippy_cup +16 -26
- data/lib/sippy_cup/media.rb +3 -1
- data/lib/sippy_cup/runner.rb +148 -50
- data/lib/sippy_cup/scenario.rb +436 -206
- data/lib/sippy_cup/tasks.rb +3 -3
- data/lib/sippy_cup/version.rb +1 -1
- data/lib/sippy_cup/xml_scenario.rb +57 -0
- data/lib/sippy_cup.rb +1 -0
- data/sippy_cup.gemspec +2 -1
- data/spec/fixtures/dtmf_2833_1.pcap +0 -0
- data/spec/fixtures/scenario.xml +73 -0
- data/spec/sippy_cup/fixtures/test.yml +16 -0
- data/spec/sippy_cup/runner_spec.rb +425 -71
- data/spec/sippy_cup/scenario_spec.rb +820 -71
- data/spec/sippy_cup/xml_scenario_spec.rb +103 -0
- data/spec/spec_helper.rb +5 -2
- metadata +30 -5
- data/tmp/rspec_guard_result +0 -1
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZTYzYWNiZmNmOTU4NjU5MzBhOTRhZGI4M2ZlZjg5YzBiOWEyZmIzNw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
7
|
-
|
6
|
+
NTU0NDIwOWUyZjc5MTRjMzkzOTQwZTNhZWZjNjhlNTMwZmM0N2NhYQ==
|
7
|
+
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZWE1NDM0Nzg5OTU0NDAzMDA0MmI3Mjk0YjY1MWFkOTMzZDQ4YmU0ZTI0ZDk0
|
10
|
+
N2ViNjlhYWVlOGQ4YTQxOGUxYTQ5NzE0ZjY4Y2ZlNjVlMjBhMTczZmMxZmFh
|
11
|
+
YzAxMzU4MTk4YzYyZWZmN2EyZDRlYjMyYmZlNGYxMGY1NzdlYzg=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MmRiYTBlMjc4MzUxMjAwZjdmZGJjYWY1MDNjNGM2MTU5ZTEyNjk2MWE2ZTgy
|
14
|
+
NWE1ZDM2YTQzNmYyZDg0NDUwNTk0NDEzZWZhYTg0OWIxNGY4NzIwMTQxNWM0
|
15
|
+
MDk0MTEyZDhhYzIzNjU1YjIwMDMyYjU4ODJlNTkxMDI5YjZmZDk=
|
data/.gitignore
CHANGED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 1.9.3
|
4
|
+
- 2.0.0
|
5
|
+
- jruby-19mode
|
6
|
+
- rbx-19mode
|
7
|
+
- ruby-head
|
8
|
+
matrix:
|
9
|
+
allow_failures:
|
10
|
+
- rvm: 2.0.0
|
11
|
+
- rvm: jruby-19mode
|
12
|
+
- rvm: rbx-19mode
|
13
|
+
- rvm: ruby-head
|
14
|
+
notifications:
|
15
|
+
irc: "irc.freenode.org#adhearsion"
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
# develop
|
2
|
+
|
3
|
+
# [0.3.0](https://github.com/bklang/sippy_cup/compare/v0.2.3...v0.3.0)
|
4
|
+
* Feature: A whole lot more documentation, test coverage and cleaner internals.
|
5
|
+
* Feature: Added a :transport_mode option that will add the -t switch to SIPp for setting TCP or other UDP modes.
|
6
|
+
* Feature: A YAML manifest may now reference a SIPp scenario (and media) on disk rather than providing steps.
|
7
|
+
* Feature: A media port may be specified as `:media_port` in the manifest or at runtime.
|
8
|
+
* Feature: API for validation of scenarios in manifests.
|
9
|
+
* Feature: Handle SIPp exit codes with clean exceptions.
|
10
|
+
* Feature: Allow passing arbitary headers in an INVITE
|
11
|
+
* Change: AVP is now AVPF in SDP.
|
12
|
+
* Change: Rake tasks for executing scenarios are removed.
|
13
|
+
* Change: Running and compiling scenarios are now separate concepts.
|
14
|
+
* `-c` on the CLI writes a YAML manifest to disk as SIPp XML and PCAP media. `-r` executes a YAML manifest and does not write to disk.
|
15
|
+
* XML scenarios may be referenced in a YAML manifest using the `scenario:` and `media:` keys, providing paths.
|
16
|
+
* `Runner` now takes a `Scenario` which it executes using SIPp via a temporary local-disk export. Most options passed to `Runner.new` are now properties of Scenario and can be specified in the YAML manifest. `Runner` no longer executes a scenario by path.
|
17
|
+
* Bugfix/Security: Don't symbolise untrusted data (YAML manifests).
|
18
|
+
* Bugfix: Allow the `sleep` step to take fractional seconds.
|
19
|
+
* Bugfix: Proxy full SIPp output to terminal by default.
|
20
|
+
|
1
21
|
# [0.2.3](https://github.com/bklang/sippy_cup/compare/v0.2.2...v0.2.3)
|
2
22
|
* Bugfix: Handle file extensions .yaml and .yml equally (#21)
|
3
23
|
* Bugfix: Fix missing Logger constant (#20)
|
data/Gemfile
CHANGED
data/Guardfile
CHANGED
@@ -1,10 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
watch(%r{^spec/.+_spec\.rb$})
|
6
|
-
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
7
|
-
watch('spec/spec_helper.rb') { "spec/" }
|
8
|
-
end
|
1
|
+
guard 'rspec', :cli => '--format documentation', :all_on_start => true, :all_after_pass => true do
|
2
|
+
watch(%r{^spec/.+_spec\.rb$})
|
3
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
4
|
+
watch('spec/spec_helper.rb') { "spec/" }
|
9
5
|
end
|
10
|
-
|
data/README.markdown
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/sippy_cup.png)](https://rubygems.org/gems/sippy_cup)
|
2
|
+
[![Build Status](https://secure.travis-ci.org/mojolingo/sippy_cup.png?branch=master)](http://travis-ci.org/mojolingo/sippy_cup)
|
3
|
+
[![Dependency Status](https://gemnasium.com/mojolingo/sippy_cup.png?travis)](https://gemnasium.com/mojolingo/sippy_cup)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/mojolingo/sippy_cup.png)](https://codeclimate.com/github/mojolingo/sippy_cup)
|
5
|
+
[![Coverage Status](https://coveralls.io/repos/mojolingo/sippy_cup/badge.png?branch=master)](https://coveralls.io/r/mojolingo/sippy_cup)
|
6
|
+
|
1
7
|
# Sippy Cup
|
2
8
|
|
3
9
|
## Overview
|
@@ -87,14 +93,16 @@ steps:
|
|
87
93
|
|
88
94
|
Both `source` and `destination` above may be optionally supplied with a port number, eg. `192.0.2.200:5061`
|
89
95
|
|
90
|
-
Next,
|
96
|
+
Next, execute the scenario:
|
91
97
|
|
92
98
|
```Shell
|
93
|
-
$ sippy_cup -
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
99
|
+
$ sippy_cup -r my_test_scenario.yml
|
100
|
+
I, [2013-09-30T14:48:08.388106 #9883] INFO -- : Preparing to run SIPp command: sudo sipp -i 192.0.2.15 -p 8836 -sf /var/folders/n4/dpzsp6_95tb3c4sp12xj5wdr0000gn/T/scenario20130930-9883-1crejcw -l 10 -m 20 -r 5 -s 1 192.0.2.200
|
101
|
+
Password:
|
102
|
+
|
103
|
+
...snip...
|
104
|
+
|
105
|
+
I, [2013-09-30T14:48:16.728712 #9883] INFO -- : Test completed successfully.
|
98
106
|
```
|
99
107
|
|
100
108
|
### Example embedding SIPp in another Ruby process
|
@@ -121,20 +129,13 @@ end
|
|
121
129
|
scenario.compile!
|
122
130
|
```
|
123
131
|
|
124
|
-
The above code can
|
125
|
-
```Ruby
|
126
|
-
require 'sippy_cup/tasks'
|
127
|
-
```
|
128
|
-
|
129
|
-
Then running the rake task `rake sippy_cup:compile[sippy_cup.rb]`
|
130
|
-
|
131
|
-
And finally running `rake sippy_cup:run[sippy_cup.yml]` to execute the scenario.
|
132
|
+
The above code can be executed as a standalone Ruby script and the resulting scenario file run with SIPp.
|
132
133
|
|
133
134
|
## Customize Your Scenarios
|
134
135
|
|
135
136
|
### Available Scenario Steps
|
136
137
|
|
137
|
-
Each command below can take [SIPp attributes](http://sipp.sourceforge.net/doc/reference.html) as optional arguments.
|
138
|
+
Each command below can take [SIPp attributes](http://sipp.sourceforge.net/doc/reference.html) as optional arguments. For a full list of available steps, see the [API documentation](http://rubydoc.info/gems/sippy_cup/SippyCup/Scenario).
|
138
139
|
|
139
140
|
* `sleep <seconds>` Wait a specified number of seconds
|
140
141
|
* `invite` Send a SIP INVITE to the specified target
|
@@ -155,28 +156,27 @@ Each command below can take [SIPp attributes](http://sipp.sourceforge.net/doc/re
|
|
155
156
|
|
156
157
|
Don't want your scenario to end up in the same directory as your script? Need the filename to be different than the scenario name? No problem!
|
157
158
|
|
158
|
-
For the `sippy_cup`
|
159
|
+
For the `sippy_cup` manifest, use `filename`:
|
159
160
|
|
160
161
|
```YAML
|
161
162
|
---
|
162
|
-
|
163
|
+
filename: /path/to/somewhere
|
163
164
|
```
|
164
165
|
|
165
166
|
Or, in Ruby:
|
166
167
|
|
167
168
|
```Ruby
|
168
|
-
|
169
|
-
s = SippyCup::Scenario.new 'SippyCup', my_opts do
|
169
|
+
s = SippyCup::Scenario.new 'SippyCup', source: '192.168.5.5:10001', destination: '10.10.0.3:19995', filename: '/path/to/somewhere' do
|
170
170
|
# scenario definitions here...
|
171
171
|
end
|
172
|
+
s.compile!
|
172
173
|
```
|
173
174
|
|
174
|
-
This will create the files `somewhere.xml
|
175
|
+
This will create the files `somewhere.xml` and `somewhere.pcap` in the `/path/to/` directory.
|
175
176
|
|
176
177
|
### Customizing the Test Run
|
177
178
|
|
178
|
-
|
179
|
-
Each parameter has an impact on the test, and may either be changed once the YAML file is generated or specified in the options hash for `SippyCup::Scenario.new`. In addition to the default parameters, some additional parameters can be set:
|
179
|
+
Each parameter has an impact on the test, and may either be changed once the XML file is generated or specified in the options hash for `SippyCup::Scenario.new`. In addition to the default parameters, some additional parameters can be set:
|
180
180
|
<dl>
|
181
181
|
<dt>stats_file</dt>
|
182
182
|
<dd>Path to a file where call statistics will be stored in a CSV format, defaults to not storing stats</dd>
|
@@ -188,10 +188,10 @@ Each parameter has an impact on the test, and may either be changed once the YAM
|
|
188
188
|
<dd>SIP username to use. Defaults to "1" (as in 1@127.0.0.1)</dd>
|
189
189
|
|
190
190
|
<dt>full_sipp_output</dt>
|
191
|
-
<dd>By default, SippyCup will
|
191
|
+
<dd>By default, SippyCup will show SIPp's command line output while running a scenario. Set this parameter to `false` to hide full command line output</dd>
|
192
192
|
|
193
|
-
<dt>
|
194
|
-
<dd>By default, SIPp assigns
|
193
|
+
<dt>media_port</dt>
|
194
|
+
<dd>By default, SIPp assigns RTP ports dynamically. However, if there is a need for a static RTP port (say, for data collection purposes), it can be done by supplying a port number here.</dd>
|
195
195
|
|
196
196
|
<dt>scenario_variables</dt>
|
197
197
|
<dd>If you're using sippy_cup to run a SIPp XML file, there may be CSV fields in the scenario ([field0], [field1], etc.). Specify a path to a CSV file containing the required information using this option. (File is semicolon delimeted, information can be found [here](http://sipp.sourceforge.net/doc/reference.html#inffile).)</dd>
|
@@ -202,14 +202,14 @@ Each parameter has an impact on the test, and may either be changed once the YAM
|
|
202
202
|
With Sippy Cup, you can add additional attributes to each step of the scenario:
|
203
203
|
|
204
204
|
```Ruby
|
205
|
-
#This limits the amount of time the server has to reply to an invite (3 seconds)
|
205
|
+
# This limits the amount of time the server has to reply to an invite (3 seconds)
|
206
206
|
s.receive_answer timeout: 3000
|
207
207
|
|
208
|
-
#You can override the default 'optional' parameters
|
208
|
+
# You can override the default 'optional' parameters
|
209
209
|
s.receive_ringing optional: false
|
210
210
|
s.receive_answer optional: true
|
211
211
|
|
212
|
-
#Let's combine multiple attributes...
|
212
|
+
# Let's combine multiple attributes...
|
213
213
|
s.receive_answer timeout: 3000, crlf: true
|
214
214
|
```
|
215
215
|
|
data/Rakefile
CHANGED
data/bin/sippy_cup
CHANGED
@@ -6,9 +6,8 @@ require 'getoptlong'
|
|
6
6
|
def usage
|
7
7
|
puts "#{$0} [OPTS] </path/to/sippy_cup_definition.yml>"
|
8
8
|
puts
|
9
|
-
puts "--compile, -c Compile the given scenario
|
10
|
-
puts "--run, -r Run the scenario
|
11
|
-
puts " compiled SippyCup file"
|
9
|
+
puts "--compile, -c Compile the given scenario manifest to XML and PCAP"
|
10
|
+
puts "--run, -r Run the scenario"
|
12
11
|
puts "--help, -h Print this usage information"
|
13
12
|
puts "--version, -V Print SippyCup version"
|
14
13
|
end
|
@@ -20,16 +19,14 @@ opts = GetoptLong.new(
|
|
20
19
|
['--version', '-V', GetoptLong::NO_ARGUMENT]
|
21
20
|
)
|
22
21
|
|
23
|
-
|
24
|
-
|
22
|
+
compile = false
|
23
|
+
run = false
|
25
24
|
opts.each do |opt, arg|
|
26
25
|
case opt
|
27
26
|
when '--compile'
|
28
|
-
|
29
|
-
@definition_file = arg
|
27
|
+
compile = true
|
30
28
|
when '--run'
|
31
|
-
|
32
|
-
@compiled_file = arg
|
29
|
+
run = true
|
33
30
|
when '--help'
|
34
31
|
usage
|
35
32
|
exit 0
|
@@ -41,38 +38,31 @@ opts.each do |opt, arg|
|
|
41
38
|
end
|
42
39
|
|
43
40
|
unless ARGV.count == 1
|
44
|
-
puts "ERROR: Must supply the SippyCup
|
41
|
+
puts "ERROR: Must supply the SippyCup manifest file"
|
45
42
|
puts
|
46
43
|
usage
|
47
44
|
exit 1
|
48
45
|
end
|
49
46
|
|
50
|
-
|
47
|
+
manifest_path = ARGV.shift
|
51
48
|
|
52
|
-
unless File.readable?
|
53
|
-
puts "ERROR: Unable to read
|
49
|
+
unless File.readable? manifest_path
|
50
|
+
puts "ERROR: Unable to read manifest file"
|
54
51
|
puts
|
55
52
|
usage
|
56
53
|
exit 1
|
57
54
|
end
|
58
55
|
|
59
|
-
unless
|
56
|
+
unless compile || run
|
60
57
|
puts "No action (compile or run) specified. Exiting."
|
61
58
|
usage
|
62
59
|
exit 1
|
63
60
|
end
|
64
61
|
|
65
|
-
|
62
|
+
scenario = SippyCup::Scenario.from_manifest File.read(manifest_path), input_filename: manifest_path
|
63
|
+
scenario.compile! if compile
|
66
64
|
|
67
|
-
|
68
|
-
|
65
|
+
if run
|
66
|
+
runner = SippyCup::Runner.new scenario
|
67
|
+
runner.run
|
69
68
|
end
|
70
|
-
unless @definition.has_key? :name
|
71
|
-
@definition[:name] = @definition[:scenario]
|
72
|
-
end
|
73
|
-
|
74
|
-
|
75
|
-
runner = SippyCup::Runner.new @definition
|
76
|
-
runner.compile if @compile
|
77
|
-
runner.run if @run
|
78
|
-
|
data/lib/sippy_cup/media.rb
CHANGED
@@ -37,7 +37,7 @@ module SippyCup
|
|
37
37
|
|
38
38
|
@sequence.each do |input|
|
39
39
|
action, value = get_step input
|
40
|
-
|
40
|
+
|
41
41
|
case action
|
42
42
|
when 'silence'
|
43
43
|
# value is the duration in milliseconds
|
@@ -85,7 +85,9 @@ module SippyCup
|
|
85
85
|
end
|
86
86
|
@pcap_file
|
87
87
|
end
|
88
|
+
|
88
89
|
private
|
90
|
+
|
89
91
|
def get_step(input)
|
90
92
|
action, value = input.split ':'
|
91
93
|
raise "Invalid Sequence: #{input}" unless VALID_STEPS.include? action
|
data/lib/sippy_cup/runner.rb
CHANGED
@@ -1,75 +1,173 @@
|
|
1
|
-
require 'yaml'
|
2
1
|
require 'logger'
|
3
|
-
require 'active_support/core_ext/hash'
|
4
2
|
|
3
|
+
#
|
4
|
+
# Service object to oversee the execution of a Scenario
|
5
|
+
#
|
5
6
|
module SippyCup
|
6
7
|
class Runner
|
7
8
|
attr_accessor :sipp_pid
|
8
|
-
attr_accessor :logger
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
#
|
11
|
+
# Create a runner from a scenario
|
12
|
+
#
|
13
|
+
# @param [Scenario, XMLScenario] scenario The scenario to execute
|
14
|
+
# @param [Hash] opts Options to modify the runner
|
15
|
+
# @option opts [optional, true, false] :full_sipp_output Whether or not to copy SIPp's stdout/stderr to the parent process. Defaults to true.
|
16
|
+
# @option opts [optional, Logger] :logger A logger to use in place of the internal logger to STDOUT.
|
17
|
+
# @option opts [optional, String] :command The command to execute. This is mostly available for testing.
|
18
|
+
#
|
19
|
+
def initialize(scenario, opts = {})
|
20
|
+
@scenario = scenario
|
21
|
+
@scenario_options = @scenario.scenario_options
|
22
|
+
|
23
|
+
defaults = { full_sipp_output: true }
|
24
|
+
@options = defaults.merge(opts)
|
25
|
+
|
26
|
+
@command = @options[:command]
|
12
27
|
@logger = @options[:logger] || Logger.new(STDOUT)
|
13
28
|
end
|
14
29
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
30
|
+
# Runs the loaded scenario using SIPp
|
31
|
+
#
|
32
|
+
# @raises Errno::ENOENT when the SIPp executable cannot be found
|
33
|
+
# @raises SippyCup::ExitOnInternalCommand when SIPp exits on an internal command. Calls may have been processed
|
34
|
+
# @raises SippyCup::NoCallsProcessed when SIPp exit normally, but has processed no calls
|
35
|
+
# @raises SippyCup::FatalError when SIPp encounters a fatal failure
|
36
|
+
# @raises SippyCup::FatalSocketBindingError when SIPp fails to bind to the specified socket
|
37
|
+
# @raises SippyCup::SippGenericError when SIPp encounters another type of error
|
38
|
+
#
|
39
|
+
# @return Boolean true if execution succeeded without any failed calls, false otherwise
|
40
|
+
#
|
41
|
+
def run
|
42
|
+
@input_files = @scenario.to_tmpfiles
|
43
|
+
|
44
|
+
@logger.info "Preparing to run SIPp command: #{command}"
|
45
|
+
|
46
|
+
exit_status, stderr_buffer = execute_with_redirected_streams
|
47
|
+
|
48
|
+
final_result = process_exit_status exit_status, stderr_buffer
|
49
|
+
|
50
|
+
if final_result
|
51
|
+
@logger.info "Test completed successfully!"
|
52
|
+
else
|
53
|
+
@logger.info "Test completed successfully but some calls failed."
|
54
|
+
end
|
55
|
+
@logger.info "Statistics logged at #{File.expand_path @scenario_options[:stats_file]}" if @scenario_options[:stats_file]
|
56
|
+
|
57
|
+
final_result
|
58
|
+
ensure
|
59
|
+
cleanup_input_files
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Tries to stop SIPp by killing the target PID
|
64
|
+
#
|
65
|
+
# @raises Errno::ESRCH when the PID does not correspond to a known process
|
66
|
+
# @raises Errno::EPERM when the process referenced by the PID cannot be killed
|
67
|
+
#
|
68
|
+
def stop
|
69
|
+
Process.kill "KILL", @sipp_pid if @sipp_pid
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def command
|
75
|
+
@command ||= begin
|
76
|
+
command = "sudo sipp"
|
77
|
+
command_options.each_pair do |key, value|
|
78
|
+
command << (value ? " -#{key} #{value}" : " -#{key}")
|
29
79
|
end
|
80
|
+
command << " #{@scenario_options[:destination]}"
|
30
81
|
end
|
31
|
-
scenario.compile!
|
32
82
|
end
|
33
83
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
84
|
+
def command_options
|
85
|
+
options = {
|
86
|
+
i: @scenario_options[:source],
|
87
|
+
p: @scenario_options[:source_port] || '8836',
|
88
|
+
sf: @input_files[:scenario].path,
|
89
|
+
l: @scenario_options[:max_concurrent],
|
90
|
+
m: @scenario_options[:number_of_calls],
|
91
|
+
r: @scenario_options[:calls_per_second],
|
92
|
+
s: @scenario_options[:from_user] || '1'
|
93
|
+
}
|
94
|
+
|
95
|
+
options[:mp] = @scenario_options[:media_port] if @scenario_options[:media_port]
|
96
|
+
|
97
|
+
if @scenario_options[:stats_file]
|
98
|
+
options[:trace_stat] = nil
|
99
|
+
options[:stf] = @scenario_options[:stats_file]
|
100
|
+
options[:fd] = @scenario_options[:stats_interval] || 1
|
37
101
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
stats_interval = @options[:stats_interval] || 1
|
46
|
-
command << " -trace_stat -stf #{@options[:stats_file]} -fd #{stats_interval}"
|
102
|
+
|
103
|
+
if @scenario_options[:transport_mode]
|
104
|
+
options[:t] = @scenario_options[:transport_mode]
|
105
|
+
end
|
106
|
+
|
107
|
+
if @scenario_options[:scenario_variables]
|
108
|
+
options[:inf] = @scenario_options[:scenario_variables]
|
47
109
|
end
|
48
|
-
|
49
|
-
|
50
|
-
command << " > /dev/null 2>&1" unless @options[:full_sipp_output]
|
51
|
-
command
|
110
|
+
|
111
|
+
options
|
52
112
|
end
|
53
113
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
114
|
+
def execute_with_redirected_streams
|
115
|
+
rd, wr = IO.pipe
|
116
|
+
stdout_target = @options[:full_sipp_output] ? $stdout : '/dev/null'
|
117
|
+
|
118
|
+
@sipp_pid = spawn command, err: wr, out: stdout_target
|
57
119
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
120
|
+
stderr_buffer = String.new
|
121
|
+
|
122
|
+
Thread.new do
|
123
|
+
wr.close
|
124
|
+
until rd.eof?
|
125
|
+
buffer = rd.readpartial(1024).strip
|
126
|
+
stderr_buffer += buffer
|
127
|
+
$stderr << buffer if @options[:full_sipp_output]
|
128
|
+
end
|
63
129
|
end
|
64
130
|
|
65
|
-
|
66
|
-
|
131
|
+
exit_status = Process.wait2 @sipp_pid.to_i
|
132
|
+
|
133
|
+
rd.close
|
134
|
+
|
135
|
+
[exit_status, stderr_buffer]
|
67
136
|
end
|
68
137
|
|
69
|
-
def
|
70
|
-
|
71
|
-
|
72
|
-
|
138
|
+
def process_exit_status(process_status, error_message = nil)
|
139
|
+
exit_code = process_status[1].exitstatus
|
140
|
+
case exit_code
|
141
|
+
when 0
|
142
|
+
true
|
143
|
+
when 1
|
144
|
+
false
|
145
|
+
when 97
|
146
|
+
raise SippyCup::ExitOnInternalCommand, error_message
|
147
|
+
when 99
|
148
|
+
raise SippyCup::NoCallsProcessed, error_message
|
149
|
+
when 255
|
150
|
+
raise SippyCup::FatalError, error_message
|
151
|
+
when 254
|
152
|
+
raise SippyCup::FatalSocketBindingError, error_message
|
153
|
+
else
|
154
|
+
raise SippyCup::SippGenericError, error_message
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def cleanup_input_files
|
159
|
+
@input_files.each_pair do |key, value|
|
160
|
+
value.close
|
161
|
+
value.unlink
|
162
|
+
end
|
73
163
|
end
|
74
164
|
end
|
165
|
+
|
166
|
+
# The corresponding SIPp error code is listed after the exception
|
167
|
+
class Error < StandardError; end
|
168
|
+
class ExitOnInternalCommand < Error; end # 97
|
169
|
+
class NoCallsProcessed < Error; end # 99
|
170
|
+
class FatalError < Error; end # -1
|
171
|
+
class FatalSocketBindingError < Error; end # -2
|
172
|
+
class SippGenericError < Error; end # 255 and undocumented errors
|
75
173
|
end
|