sippy_cup 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
[](https://rubygems.org/gems/sippy_cup)
|
2
|
+
[](http://travis-ci.org/mojolingo/sippy_cup)
|
3
|
+
[](https://gemnasium.com/mojolingo/sippy_cup)
|
4
|
+
[](https://codeclimate.com/github/mojolingo/sippy_cup)
|
5
|
+
[](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
|