stella 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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