stella 0.3.2

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.
Files changed (56) hide show
  1. data/README.txt +135 -0
  2. data/Rakefile +100 -0
  3. data/bin/stella +12 -0
  4. data/lib/stella.rb +58 -0
  5. data/lib/stella/adapter/ab.rb +303 -0
  6. data/lib/stella/adapter/base.rb +87 -0
  7. data/lib/stella/adapter/httperf.rb +296 -0
  8. data/lib/stella/adapter/siege.rb +321 -0
  9. data/lib/stella/cli.rb +291 -0
  10. data/lib/stella/cli/agents.rb +73 -0
  11. data/lib/stella/cli/base.rb +19 -0
  12. data/lib/stella/cli/language.rb +18 -0
  13. data/lib/stella/cli/localtest.rb +80 -0
  14. data/lib/stella/command/base.rb +111 -0
  15. data/lib/stella/command/localtest.rb +339 -0
  16. data/lib/stella/logger.rb +63 -0
  17. data/lib/stella/response.rb +82 -0
  18. data/lib/stella/storable.rb +116 -0
  19. data/lib/stella/support.rb +106 -0
  20. data/lib/stella/test/base.rb +34 -0
  21. data/lib/stella/test/definition.rb +79 -0
  22. data/lib/stella/test/run/summary.rb +50 -0
  23. data/lib/stella/test/summary.rb +82 -0
  24. data/lib/stella/text.rb +64 -0
  25. data/lib/stella/text/resource.rb +39 -0
  26. data/lib/utils/crypto-key.rb +84 -0
  27. data/lib/utils/escape.rb +302 -0
  28. data/lib/utils/fileutil.rb +59 -0
  29. data/lib/utils/httputil.rb +210 -0
  30. data/lib/utils/mathutil.rb +78 -0
  31. data/lib/utils/textgraph.rb +267 -0
  32. data/lib/utils/timerutil.rb +58 -0
  33. data/support/text/en.yaml +54 -0
  34. data/support/text/nl.yaml +1 -0
  35. data/support/useragents.txt +75 -0
  36. data/vendor/useragent/MIT-LICENSE +20 -0
  37. data/vendor/useragent/README +21 -0
  38. data/vendor/useragent/init.rb +1 -0
  39. data/vendor/useragent/lib/user_agent.rb +83 -0
  40. data/vendor/useragent/lib/user_agent/browsers.rb +24 -0
  41. data/vendor/useragent/lib/user_agent/browsers/all.rb +69 -0
  42. data/vendor/useragent/lib/user_agent/browsers/gecko.rb +43 -0
  43. data/vendor/useragent/lib/user_agent/browsers/internet_explorer.rb +40 -0
  44. data/vendor/useragent/lib/user_agent/browsers/opera.rb +49 -0
  45. data/vendor/useragent/lib/user_agent/browsers/webkit.rb +94 -0
  46. data/vendor/useragent/lib/user_agent/comparable.rb +25 -0
  47. data/vendor/useragent/lib/user_agent/operating_systems.rb +19 -0
  48. data/vendor/useragent/spec/browsers/gecko_user_agent_spec.rb +209 -0
  49. data/vendor/useragent/spec/browsers/internet_explorer_user_agent_spec.rb +99 -0
  50. data/vendor/useragent/spec/browsers/opera_user_agent_spec.rb +59 -0
  51. data/vendor/useragent/spec/browsers/other_user_agent_spec.rb +19 -0
  52. data/vendor/useragent/spec/browsers/webkit_user_agent_spec.rb +373 -0
  53. data/vendor/useragent/spec/spec_helper.rb +1 -0
  54. data/vendor/useragent/spec/user_agent_spec.rb +331 -0
  55. data/vendor/useragent/useragent.gemspec +12 -0
  56. metadata +139 -0
@@ -0,0 +1,87 @@
1
+
2
+
3
+ module Stella::Adapter
4
+ class CommandNotReady < RuntimeError
5
+ def initialize(name="")
6
+ super(Stella::TEXT.msg(:error_adapter_command_not_ready, name))
7
+ end
8
+ end
9
+
10
+ class Base
11
+
12
+
13
+ attr_accessor :arguments, :load_factor, :working_directory, :stats
14
+
15
+ def initialize(options={}, arguments=[])
16
+ self.arguments = arguments
17
+ self.options = options
18
+ end
19
+
20
+ def load_factor=(load_factor)
21
+ @load_factor = load_factor
22
+ end
23
+ def stdout_path
24
+ File.join(@working_directory, 'STDOUT.txt')
25
+ end
26
+
27
+ def stderr_path
28
+ File.join(@working_directory, 'STDERR.txt')
29
+ end
30
+
31
+ def summary_path(ext='yaml')
32
+ File.join(@working_directory, "SUMMARY.#{ext}")
33
+ end
34
+
35
+ # process_options
36
+ #
37
+ # This method must be overridden by the implementing class. This is intended
38
+ # for processing the command-specific command-line arguments
39
+ def process_options
40
+ raise Stella::TEXT.msg(:error_class_must_override, 'process_options')
41
+ end
42
+
43
+ def options=(options={})
44
+ options = options.marshal_dump if options.is_a? OpenStruct
45
+ unless options.nil? || options.empty?
46
+ options.each_pair do |name,value|
47
+ next if @private_variables.member?(name)
48
+ Stella::LOGGER.info(:error_class_unknown_argument, name) && next unless self.respond_to?("#{name.to_s}=")
49
+ instance_variable_set("@#{name.to_s}", value)
50
+ end
51
+ end
52
+ end
53
+
54
+ def arguments=(arguments=[])
55
+ @arguments = arguments unless arguments.nil?
56
+ end
57
+
58
+ def available?
59
+ (version.to_f > 0)
60
+ end
61
+
62
+ def name
63
+ @name
64
+ end
65
+
66
+ def rate
67
+ 1
68
+ end
69
+ def vuser_rate
70
+ "#{vusers}/#{rate}"
71
+ end
72
+
73
+ def command
74
+ raise Stella::TEXT.msg(:error_class_must_override, 'command')
75
+ end
76
+
77
+ def add_header
78
+ raise Stella::TEXT.msg(:error_class_must_override, 'add_header')
79
+ end
80
+
81
+ def user_agent=
82
+ raise Stella::TEXT.msg(:error_class_must_override, 'user_agent=')
83
+ end
84
+
85
+ end
86
+ end
87
+
@@ -0,0 +1,296 @@
1
+
2
+
3
+ module Stella
4
+ module Adapter
5
+
6
+ #Usage: httperf [-hdvV] [--add-header S] [--burst-length N] [--client N/N]
7
+ # [--close-with-reset] [--debug N] [--failure-status N]
8
+ # [--help] [--hog] [--http-version S] [--max-connections N]
9
+ # [--max-piped-calls N] [--method S] [--no-host-hdr]
10
+ # [--num-calls N] [--num-conns N] [--period [d|u|e]T1[,T2]]
11
+ # [--port N] [--print-reply [header|body]] [--print-request [header|body]]
12
+ # [--rate X] [--recv-buffer N] [--retry-on-failure] [--send-buffer N]
13
+ # [--server S] [--server-name S] [--session-cookies]
14
+ # [--ssl] [--ssl-ciphers L] [--ssl-no-reuse]
15
+ # [--think-timeout X] [--timeout X] [--uri S] [--verbose] [--version]
16
+ # [--wlog y|n,file] [--wsess N,N,X] [--wsesslog N,X,file]
17
+ # [--wset N,X]
18
+ #
19
+ class Httperf < Stella::Adapter::Base
20
+
21
+
22
+
23
+ attr_accessor :hog, :server, :uri, :num_conns, :num_calls, :rate, :timeout, :think_timeout, :port
24
+ attr_accessor :add_header, :burst_length, :client, :close_with_reset, :debug, :failure_status
25
+ attr_accessor :help, :http_version, :max_connections, :max_piped_calls, :method, :no_host_hdr
26
+ attr_accessor :period, :print_reply, :print_request, :recv_buffer, :retry_on_failure, :send_buffer
27
+ attr_accessor :server_name, :session_cookies, :ssl, :ssl_ciphers, :ssl_no_reuse, :verbose
28
+ attr_accessor :version, :wlog, :wsess, :wsesslog, :wset
29
+
30
+ def initialize(options={}, arguments=[])
31
+ super(options, arguments)
32
+ @name = 'httperf'
33
+
34
+ @private_variables = ['private_variables', 'name', 'arguments', 'load_factor', 'working_directory']
35
+ @load_factor = 1
36
+ end
37
+
38
+
39
+
40
+ # Before calling run
41
+ def before
42
+
43
+
44
+ end
45
+ def command
46
+ raise CommandNotReady.new(self.class.to_s) unless ready?
47
+
48
+ command = "#{@name} "
49
+
50
+ instance_variables.each do |name|
51
+ canon = name.tr('@', '') # instance_variables returns '@name'
52
+ next if @private_variables.member?(canon)
53
+
54
+ # It's important that we take the value from the getter method
55
+ # because it applies the load factor.
56
+ value = self.send(canon)
57
+ if (value.is_a? Array)
58
+ value.each { |el| command << "--#{canon.tr('_', '-')} #{EscapeUtil.shell_single_word(el.to_s)} " }
59
+ else
60
+ command << "--#{canon.tr('_', '-')} #{EscapeUtil.shell_single_word(value.to_s)} "
61
+ end
62
+
63
+ end
64
+
65
+ command << (@arguments.map { |uri| "'#{uri}'" }).join(' ') unless @arguments.empty?
66
+ command
67
+ end
68
+
69
+ # After calling run
70
+ def after
71
+
72
+
73
+ save_stats
74
+ end
75
+
76
+ #httperf --hog --server=queen --uri=/0k.html --num-conns=10000 --rate=0 --timeout=30 --think-timeout=0
77
+ def process_options(arguments)
78
+
79
+ options = OpenStruct.new
80
+ opts = OptionParser.new
81
+ opts.on('--hog') do @hog = true end
82
+ opts.on('--server=S', String) do |v| @server = v end
83
+ opts.on('--server-name=S', String) do |v| @server_name = v end
84
+ opts.on('--port=N', Integer) do |v| @port = v end
85
+ opts.on('--uri=S', String) do |v| @uri = v end
86
+ opts.on('--num-conns=N', Integer) do |v| @num_conns = v end
87
+ opts.on('--num-calls=N', Integer) do |v| @num_calls = v end
88
+ opts.on('--rate=N', Integer) do |v| @rate = v end
89
+ opts.on('--timeout=N', Integer) do |v| @timeout = v end
90
+ opts.on('--think-timeout=N', Integer) do |v| @think_timeout = v end
91
+
92
+ opts.on('-h', '--help') do |v| @help = true end
93
+ opts.on('-v', '--verbose') do |v| @verbose = true end
94
+ opts.on('-V', '--version') do |v| @version = true end
95
+ opts.on('--close-with-reset') do |v| @close_with_reset = true end
96
+ opts.on('--session-cookies') do |v| @session_cookies = true end
97
+ opts.on('--ssl') do |v| @ssl = true end
98
+ opts.on('--ssl-ciphers') do |v| @ssl_ciphers = true end
99
+ opts.on('--ssl-no-reuse') do |v| @ssl_no_reuse = true end
100
+ opts.on('--no-host-hdr') do |v| @no_host_hdr = true end
101
+ opts.on('--retry-on-failure') do |v| @retry_on_failure = true end
102
+
103
+ opts.on('--add-header=S', String) do |v| @add_header ||= []; @add_header << v; end
104
+ opts.on('--burst-length=N', Integer) do |v| @burst_length = v end
105
+ opts.on('--client=S', String) do |v| @client = v end
106
+ opts.on('-d N', '--debug=N', Integer) do |v| @debug ||= 0; @debug = v end
107
+ opts.on('--failure-status=N', Integer) do |v| @failure_status = v end
108
+
109
+ opts.on('--http-version=S', String) do |v| @http_version = v end
110
+
111
+ opts.on('--max-connections=N', Integer) do |v| @max_connections = v end
112
+ opts.on('--max-piped-calls=N', Integer) do |v| @max_piped_calls = v end
113
+ opts.on('--method=S', String) do |v| @method = v end
114
+
115
+ opts.on('--period=S', String) do |v| @period = v end # TODO: Requires parsing
116
+ opts.on('--print-reply=[S]', String) do |v| @print_reply = v end
117
+ opts.on('--print-request=[S]', String) do |v| @print_request = v end
118
+
119
+ opts.on('--recv-buffer=N', Integer) do |v| @recv_buffer = v end
120
+ opts.on('--send-buffer=N', Integer) do |v| @send_buffer = v end
121
+
122
+
123
+ opts.on('--wlog=S', String) do |v| @wlog = Stella::Util::expand_str(v) end
124
+ opts.on('--wsess=S', String) do |v| @wsess = Stella::Util::expand_str(v) end
125
+ opts.on('--wsesslog=S', String) do |v| @wsesslog = Stella::Util::expand_str(v) end
126
+ opts.on('--wset=S', String) do |v| @wset = Stella::Util::expand_str(v) end
127
+
128
+ # parse! removes the options it finds.
129
+ # It also fails when it finds unknown switches (i.e. -X)
130
+ # Which should leave only the remaining arguments (URIs in this case)
131
+ opts.parse!(arguments)
132
+
133
+
134
+ options
135
+ rescue OptionParser::InvalidOption => ex
136
+ # We want to replace this text so we grab just the name of the argument
137
+ badarg = ex.message.gsub('invalid option: ', '')
138
+ raise InvalidArgument.new(badarg)
139
+ end
140
+
141
+
142
+ def version
143
+ vsn = 0
144
+ text = ""
145
+ Open3.popen3("#{@name} --version") do |stdin, stdout, stderr|
146
+ text = stdout.readlines.join
147
+ text.scan(/httperf\-([\d\.]+)\s/) { |v| vsn = v[0] }
148
+ end
149
+ vsn
150
+ end
151
+
152
+ # loadtest
153
+ #
154
+ # True or false: is the call to siege a load test? If it's a call to help or version or
155
+ # to display the config this with return false. It's no reason for someone to make this
156
+ # call through Stella but it's here for goodness sake.
157
+ def loadtest?
158
+ @uri && !@uri.empty?
159
+ end
160
+ def ready?
161
+ @name && !instance_variables.empty?
162
+ end
163
+
164
+ def add_header(name=false, value=false)
165
+ # This is a hack since we have an instance variable called add_header.
166
+ # I figure this is the best of two evils because I'd rather keep the
167
+ # instance variable naming consistent.
168
+ return @add_header if !name && !value
169
+ @add_header ||= []
170
+ @add_header << "#{name}: #{value}"
171
+ end
172
+
173
+ def user_agent=(list=[])
174
+ return unless list && !list.empty?
175
+ list = list.to_ary
176
+ list.each do |agent|
177
+ add_header("User-Agent", agent)
178
+ end
179
+ end
180
+ def vusers
181
+ @rate
182
+ end
183
+ def vusers=(v)
184
+ 0
185
+ end
186
+ def requests
187
+ @num_conns # TODO: also check wsess and wlog params
188
+ end
189
+ def requests=(v)
190
+ 0
191
+ end
192
+ def vuser_requests
193
+ 0
194
+ end
195
+ def wsess
196
+ @wsess.join(',')
197
+ end
198
+
199
+ def wset
200
+ @wset.join(',')
201
+ end
202
+
203
+
204
+ def wsesslog
205
+ @wsesslog.join(',')
206
+ end
207
+ def wlog
208
+ @wlog.join(',')
209
+ end
210
+
211
+ #def concurrent
212
+ # (@concurrent * @load_factor).to_i
213
+ #end
214
+ #def concurrent_f
215
+ # (@concurrent * @load_factor).to_f
216
+ #end
217
+ #def reps
218
+ # @reps
219
+ #end
220
+
221
+
222
+
223
+ # Siege writes the summary to STDERR
224
+ def stats_file
225
+ File.new(stdout_path)
226
+ end
227
+
228
+ def rc_file
229
+ File.join(@working_directory, "siegerc")
230
+ end
231
+
232
+ def log_file
233
+ File.join(@working_directory, "siege.log")
234
+ end
235
+
236
+ def uris_file
237
+ File.join(@working_directory, File.basename(@file))
238
+ end
239
+
240
+ # httperf --hog --timeout=30 --client=0/1 --server=127.0.0.1 --port=5600 --uri=/ --send-buffer=4096 --recv-buffer=16384 --num-conns=5 --num-calls=1
241
+ # httperf: warning: open file limit > FD_SETSIZE; limiting max. # of open files to FD_SETSIZE
242
+ # Maximum connect burst length: 1
243
+ #
244
+ # Total: connections 5 requests 5 replies 5 test-duration 0.513 s
245
+ #
246
+ # Connection rate: 9.7 conn/s (102.7 ms/conn, <=1 concurrent connections)
247
+ # Connection time [ms]: min 102.1 avg 102.7 max 104.1 median 102.5 stddev 0.8
248
+ # Connection time [ms]: connect 0.2
249
+ # Connection length [replies/conn]: 1.000
250
+ #
251
+ # Request rate: 9.7 req/s (102.7 ms/req)
252
+ # Request size [B]: 62.0
253
+ #
254
+ # Reply rate [replies/s]: min 0.0 avg 0.0 max 0.0 stddev 0.0 (0 samples)
255
+ # Reply time [ms]: response 102.3 transfer 0.1
256
+ # Reply size [B]: header 136.0 content 96.0 footer 0.0 (total 232.0)
257
+ # Reply status: 1xx=0 2xx=5 3xx=0 4xx=0 5xx=0
258
+ #
259
+ # CPU time [s]: user 0.12 system 0.39 (user 22.5% system 75.3% total 97.8%)
260
+ # Net I/O: 2.8 KB/s (0.0*10^6 bps)
261
+ #
262
+ # Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
263
+ # Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0
264
+
265
+ def stats
266
+ return unless stats_file
267
+
268
+ raw = stats_file.readlines.join
269
+ stats = Stella::Test::Run::Summary.new
270
+
271
+ raw.scan(/Request rate: (\d+?\.\d+?) req.s .(\d+?\.\d+?) ms.req./) do |rate,time|
272
+ stats.transaction_rate = rate.to_f
273
+ stats.response_time = (time.to_f) / 1000
274
+ end
275
+
276
+ raw.scan(/connections (\d+?) requests (\d+?) replies (\d+?) test-duration (\d+\.\d+?) s/) do |conn,req,rep,time|
277
+ stats.elapsed_time = time.to_f
278
+ stats.successful = rep.to_i
279
+ stats.failed = conn.to_i - rep.to_i # maybe this should be from the Error line
280
+ stats.transactions = conn.to_i
281
+ end
282
+
283
+ raw.scan(/Reply size [B]: header (\d+\.\d+?) content (\d+\.\d+?) footer (\d+\.\d+?) .total (\d+\.\d+?)./) do |h,c,f,t|
284
+ stats.data_transferred = ((t.to_f || 0 ) / 1_048_576).to_f # TODO: convert from bytes to MB
285
+ end
286
+ stats.vusers = self.vusers
287
+
288
+
289
+ stats
290
+ end
291
+
292
+
293
+
294
+ end
295
+ end
296
+ end
@@ -0,0 +1,321 @@
1
+
2
+
3
+ module Stella
4
+ module Adapter
5
+
6
+ # SIEGE
7
+ # Usage: siege [options]
8
+ # siege [options] URL
9
+ # siege -g URL
10
+ # Options:
11
+ # -V, --version VERSION, prints version number to screen.
12
+ # -h, --help HELP, prints this section.
13
+ # -C, --config CONFIGURATION, show the current configuration.
14
+ # -v, --verbose VERBOSE, prints notification to screen.
15
+ # -g, --get GET, pull down headers from the server and display HTTP
16
+ # transaction. Great for web application debugging.
17
+ # -c, --concurrent=NUM CONCURRENT users, default is 10
18
+ # -u, --url="URL" Deprecated. Set URL as the last argument.
19
+ # -i, --internet INTERNET user simulation, hits the URLs randomly.
20
+ # -b, --benchmark BENCHMARK, signifies no delay for time testing.
21
+ # -t, --time=NUMm TIME based testing where "m" is the modifier S, M, or H
22
+ # no space between NUM and "m", ex: --time=1H, one hour test.
23
+ # -r, --reps=NUM REPS, number of times to run the test, default is 25
24
+ # -f, --file=FILE FILE, change the configuration file to file.
25
+ # -R, --rc=FILE RC, change the siegerc file to file. Overrides
26
+ # the SIEGERC environmental variable.
27
+ # -l, --log LOG, logs the transaction to PREFIX/var/siege.log
28
+ # -m, --mark="text" MARK, mark the log file with a string separator.
29
+ # -d, --delay=NUM Time DELAY, random delay between 1 and num designed
30
+ # to simulate human activity. Default value is 3
31
+ # -H, --header="text" Add a header to request (can be many)
32
+ # -A, --user-agent="text" Sets User-Agent in request
33
+ class Siege < Stella::Adapter::Base
34
+
35
+
36
+ attr_accessor :version, :help, :config, :verbose, :get, :log, :mark, :delay, :header, :user_agent
37
+ attr_accessor :reps, :concurrent, :rc, :file, :time, :benchmark, :internet
38
+
39
+ def initialize(options={}, arguments=[])
40
+ super(options, arguments)
41
+ @name = 'siege'
42
+ @reps = 1
43
+ @concurrent = 1
44
+ @rc = File.join(ENV['HOME'], '.siegerc')
45
+ @private_variables = ['private_variables', 'name', 'arguments', 'load_factor', 'working_directory', 'orig_logfile']
46
+ @load_factor = 1
47
+ end
48
+
49
+
50
+ def version
51
+ vsn = 0
52
+ text = ""
53
+ Open3.popen3("#{@name} --version") do |stdin, stdout, stderr|
54
+ text = stderr.readlines.join
55
+ text.scan(/SIEGE (\d+?\.\d+)/) { |v| vsn = v[0] }
56
+ end
57
+ vsn
58
+ end
59
+
60
+ # loadtest
61
+ #
62
+ # True or false: is the call to siege a load test? If it's a call to help or version or
63
+ # to display the config this with return false. It's no reason for someone to make this
64
+ # call through Stella but it's here for goodness sake.
65
+ def loadtest?
66
+ !@arguments.empty? # The argument is a URI
67
+ end
68
+
69
+ def ready?
70
+ @name && !instance_variables.empty?
71
+ end
72
+
73
+
74
+ # Before calling run
75
+ def before
76
+
77
+ # Keep a copy of the configuration file.
78
+ copy_siegerc
79
+
80
+ # Keep a copy of the URLs file.
81
+ copy_urls_file if @file
82
+
83
+ # TODO: Print message about neither --benchmark or --internet
84
+ end
85
+ def command
86
+ raise CommandNotReady.new(self.class.to_s) unless ready?
87
+
88
+ command = "#{@name} "
89
+
90
+ instance_variables.each do |name|
91
+ canon = name.tr('@', '') # instance_variables returns '@name'
92
+ next if @private_variables.member?(canon)
93
+
94
+ # It's important that we take the value from the getter method
95
+ # because it applies the load factor.
96
+ value = self.send(canon)
97
+ if (value.is_a? Array)
98
+ value.each { |el| command << "--#{canon.tr('_', '-')} #{EscapeUtil.shell_single_word(el.to_s)} " }
99
+ else
100
+ command << "--#{canon.tr('_', '-')} #{EscapeUtil.shell_single_word(value.to_s)} "
101
+ end
102
+
103
+ end
104
+
105
+ command << (@arguments.map { |uri| "'#{uri}'" }).join(' ') unless @arguments.empty?
106
+ command
107
+ end
108
+
109
+ # After calling run
110
+ def after
111
+
112
+ update_orig_logfile if @orig_logfile
113
+
114
+ save_stats
115
+ end
116
+
117
+
118
+ def process_options(arguments)
119
+ options = OpenStruct.new
120
+ opts = OptionParser.new
121
+ opts.on('-V', '--version') do |v| @version = v end
122
+ opts.on('-h', '--help') do |v| @help = v end
123
+ opts.on('-C', '--config') do |v| @config = v end
124
+ opts.on('-v', '--verbose') do |v| @verbose = v end
125
+ opts.on('-g', '--get') do |v| @get = v end
126
+ opts.on('-l', '--log') do |v| @log = v end
127
+ opts.on('-m S', '--mark=S', String) do |v| @mark = v end
128
+ opts.on('-d N', '--delay=N', Float) do |v| @delay = v end
129
+ opts.on('-H S', '--header=S', String) do |v| @header ||= []; @header << v end
130
+
131
+ opts.on('-r N', '--reps=N', Integer) do |v| @reps = v.to_i end
132
+ opts.on('-c N', '--concurrent=N', Integer) do |v| @concurrent = v.to_i end
133
+ opts.on('-R S', '--rc=S', String) do |v| @rc = v end
134
+ opts.on('-f S', '--file=S', String) do |v| @file = v end
135
+ opts.on('-t S', '--time=S', String) do |v| @time = v end
136
+ opts.on('-b', '--benchmark') do |v| @benchmark = true; end
137
+ opts.on('-i', '--internet') do |v| @internet = true; end
138
+ opts.on('-A S', '--user-agent=S', String) do |v| @user_agent ||= []; @user_agent << v end
139
+
140
+ raise "You cannot select both --internet and --benchmark" if options.internet && options.benchmark
141
+
142
+ # parse! removes the options it finds.
143
+ # It also fails when it finds unknown switches (i.e. -X)
144
+ # Which should leave only the remaining arguments (URIs in this case)
145
+ opts.parse!(arguments)
146
+ options
147
+ rescue OptionParser::InvalidOption => ex
148
+ # We want to replace this text so we grab just the name of the argument
149
+ badarg = ex.message.gsub('invalid option: ', '')
150
+ raise InvalidArgument.new(badarg)
151
+ end
152
+
153
+
154
+ def add_header(name, value)
155
+ @header ||= []
156
+ @header << "#{name}: #{value}"
157
+ end
158
+ def user_agent=(list=[])
159
+ return unless list && !list.empty?
160
+ list = list.to_ary
161
+ @user_agent ||= []
162
+ @user_agent << list
163
+ @user_agent.flatten
164
+ end
165
+
166
+ def vusers
167
+ concurrent || 0
168
+ end
169
+ def vusers=(v)
170
+ @concurrent = v
171
+ end
172
+ def requests
173
+ (@reps * concurrent_f).to_i
174
+ end
175
+ def requests=(v)
176
+ @reps = (v / concurrent_f).to_i
177
+ end
178
+ def vuser_requests
179
+ @reps
180
+ end
181
+
182
+ def concurrent
183
+ (@concurrent * @load_factor).to_i
184
+ end
185
+ def concurrent_f
186
+ (@concurrent * @load_factor).to_f
187
+ end
188
+ def reps
189
+ @reps
190
+ end
191
+
192
+
193
+ # Take the last line of the siege.log file and write it to the log file
194
+ # specified by the user. We don't this so running with Stella is
195
+ # identical to running it standalone
196
+ def update_orig_logfile
197
+
198
+ return unless (@orig_logfile)
199
+ log_str = FileUtil.read_file_to_array(log_file) || ''
200
+ return if log_str.empty?
201
+
202
+ if File.exists?(@orig_logfile)
203
+ FileUtil.append_file(@orig_logfile, log_str[-1], true)
204
+ else
205
+ FileUtil.write_file(@orig_logfile, log_str.join(''), true)
206
+ end
207
+
208
+ end
209
+
210
+ # We want to keep a copy of the configuration file and also
211
+ # modify it a little bit to make sure we get all the mad info from siege
212
+ def copy_siegerc
213
+
214
+ # Read in the siegerc file so we can manipulate it
215
+ siegerc_str = FileUtil.read_file(File.expand_path(@rc))
216
+
217
+ siegerc_vars = {
218
+ :verbose => [false, true], # We want to maximize the data output by siege
219
+ :logging => [false, true],
220
+ :csv => [false, true]
221
+ }
222
+
223
+ #if (@agent)
224
+ # siegerc_vars['user-agent'] = ['.*', 'dogs2']
225
+ #end
226
+
227
+ # We'll set the variables in the siegerc file
228
+ siegerc_vars.each_pair do |var,value|
229
+ siegerc_str.gsub!(/#{var}\s*=\s*#{value[0]}/, "#{var} = #{value[1]}") # make true
230
+ siegerc_str.gsub!(/^\#+\s*#{var}/, "#{var}") # remove comment
231
+ end
232
+
233
+ # Look for the enabled logile path
234
+ # We will use this later to update it from the last line in our copy
235
+ siegerc_str =~ /^\s*logfile\s*=\s*(.+?)$/
236
+ @orig_logfile = $1 || nil
237
+
238
+ # Replace all environment variables with literal values
239
+ @orig_logfile.gsub!(/\$\{#{$1}\}/, ENV[$1]) while (@orig_logfile =~ /\$\{(.+?)\}/ && ENV.has_key?($1))
240
+
241
+ @orig_logfile = File.expand_path(@orig_logfile) if @orig_logfile
242
+
243
+
244
+ siegerc_str.gsub!(/^\#*\s*logfile\s*=\s*.*?$/, "logfile = " + log_file)
245
+
246
+ FileUtil.write_file(rc_file, siegerc_str, true)
247
+ @rc = rc_file
248
+ end
249
+
250
+ # We want to keep a copy of the URLs file too
251
+ def copy_urls_file
252
+ if @file
253
+ File.copy(File.expand_path(@file), uris_file)
254
+ @file = uris_file
255
+ end
256
+ end
257
+
258
+ # Siege writes the summary to STDERR
259
+ def stats_file
260
+ File.new(stderr_path)
261
+ end
262
+
263
+ def rc_file
264
+ File.join(@working_directory, "siegerc")
265
+ end
266
+
267
+ def log_file
268
+ File.join(@working_directory, "siege.log")
269
+ end
270
+
271
+ def uris_file
272
+ File.join(@working_directory, File.basename(@file))
273
+ end
274
+
275
+ def stats
276
+ return unless stats_file
277
+ raw = {}
278
+ stats_file.each_line { |l|
279
+ l.chomp!
280
+ nvpair = l.split(':')
281
+ next unless nvpair && nvpair.size == 2
282
+ n = nvpair[0].strip.tr(' ', '_').downcase[/\w+/]
283
+ v = nvpair[1].strip[/[\.\d]+/]
284
+ raw[n.to_sym] = v.to_f
285
+ }
286
+
287
+ stats = Stella::Test::Run::Summary.new
288
+
289
+ # Transactions: 750 hits
290
+ # Availability: 100.00 %
291
+ # Elapsed time: 2.33 secs
292
+ # Data transferred: 0.07 MB
293
+ # Response time: 0.21 secs
294
+ # Transaction rate: 321.89 trans/sec
295
+ # Throughput: 0.03 MB/sec
296
+ # Concurrency: 67.49
297
+ # Successful transactions: 750
298
+ # Failed transactions: 0
299
+ # Longest transaction: 0.33
300
+ # Shortest transaction: 0.10
301
+
302
+ stats.vusers = raw[:concurrency]
303
+ stats.data_transferred = raw[:data_transferred]
304
+ stats.elapsed_time = raw[:elapsed_time]
305
+ stats.response_time = raw[:response_time]
306
+ stats.transactions = raw[:transactions].to_i
307
+ stats.transaction_rate = raw[:transaction_rate]
308
+ stats.failed = raw[:failed_transactions].to_i
309
+ stats.successful = raw[:successful_transactions].to_i
310
+
311
+ #stats.shortest_transaction = raw[:shortest_transaction]
312
+ #stats.longest_transaction = raw[:longest_transaction]
313
+
314
+ stats
315
+ end
316
+
317
+
318
+
319
+ end
320
+ end
321
+ end