solutious-stella 0.5.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +36 -0
- data/README.textile +162 -0
- data/Rakefile +88 -0
- data/bin/stella +12 -0
- data/bin/stella.bat +12 -0
- data/lib/daemonize.rb +56 -0
- data/lib/pcaplet.rb +180 -0
- data/lib/stella.rb +101 -0
- data/lib/stella/adapter/ab.rb +337 -0
- data/lib/stella/adapter/base.rb +106 -0
- data/lib/stella/adapter/httperf.rb +305 -0
- data/lib/stella/adapter/pcap_watcher.rb +221 -0
- data/lib/stella/adapter/proxy_watcher.rb +76 -0
- data/lib/stella/adapter/siege.rb +341 -0
- data/lib/stella/cli.rb +258 -0
- data/lib/stella/cli/agents.rb +73 -0
- data/lib/stella/cli/base.rb +55 -0
- data/lib/stella/cli/language.rb +18 -0
- data/lib/stella/cli/localtest.rb +78 -0
- data/lib/stella/cli/sysinfo.rb +16 -0
- data/lib/stella/cli/watch.rb +278 -0
- data/lib/stella/command/base.rb +40 -0
- data/lib/stella/command/localtest.rb +358 -0
- data/lib/stella/data/domain.rb +82 -0
- data/lib/stella/data/http.rb +131 -0
- data/lib/stella/logger.rb +84 -0
- data/lib/stella/response.rb +85 -0
- data/lib/stella/storable.rb +201 -0
- data/lib/stella/support.rb +276 -0
- data/lib/stella/sysinfo.rb +257 -0
- data/lib/stella/test/definition.rb +79 -0
- data/lib/stella/test/run/summary.rb +70 -0
- data/lib/stella/test/stats.rb +114 -0
- data/lib/stella/text.rb +64 -0
- data/lib/stella/text/resource.rb +38 -0
- data/lib/utils/crypto-key.rb +84 -0
- data/lib/utils/domainutil.rb +47 -0
- data/lib/utils/escape.rb +302 -0
- data/lib/utils/fileutil.rb +78 -0
- data/lib/utils/httputil.rb +266 -0
- data/lib/utils/mathutil.rb +15 -0
- data/lib/utils/stats.rb +88 -0
- data/lib/utils/textgraph.rb +267 -0
- data/lib/utils/timerutil.rb +58 -0
- data/lib/win32/Console.rb +970 -0
- data/lib/win32/Console/ANSI.rb +305 -0
- data/support/kvm.h +91 -0
- data/support/ruby-pcap-takuma-notes.txt +19 -0
- data/support/ruby-pcap-takuma-patch.txt +30 -0
- data/support/text/en.yaml +80 -0
- data/support/text/nl.yaml +7 -0
- data/support/useragents.txt +75 -0
- data/tests/01-util_test.rb +0 -0
- data/tests/02-stella-util_test.rb +42 -0
- data/tests/10-stella_test.rb +104 -0
- data/tests/11-stella-storable_test.rb +68 -0
- data/tests/60-stella-command_test.rb +248 -0
- data/tests/80-stella-cli_test.rb +45 -0
- data/tests/spec-helper.rb +31 -0
- metadata +165 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
|
2
|
+
require 'webrick/httpproxy'
|
3
|
+
require 'observer'
|
4
|
+
|
5
|
+
|
6
|
+
module Stella
|
7
|
+
module Adapter
|
8
|
+
|
9
|
+
# Stella::Adapter::ProxyWatcher
|
10
|
+
#
|
11
|
+
# Starts up an HTTP proxy using WEBrick to record HTTP events. This is used
|
12
|
+
# when PcapRecorder is not available.
|
13
|
+
class ProxyWatcher
|
14
|
+
include Observable
|
15
|
+
|
16
|
+
attr_accessor :port
|
17
|
+
|
18
|
+
def initialize(options={})
|
19
|
+
@port = options[:port]
|
20
|
+
end
|
21
|
+
require 'pp'
|
22
|
+
def run
|
23
|
+
|
24
|
+
@server = WEBrick::HTTPProxyServer.new(
|
25
|
+
:Port => @port || 3114,
|
26
|
+
:AccessLog => [],
|
27
|
+
:ProxyContentHandler => Proc.new do |req,res|
|
28
|
+
|
29
|
+
begin
|
30
|
+
res_obj = Stella::Data::HTTPResponse.new(res.to_s)
|
31
|
+
res_obj.time = Time.now
|
32
|
+
|
33
|
+
req_obj = Stella::Data::HTTPRequest.new(req.to_s)
|
34
|
+
req_obj.time = Time.now
|
35
|
+
req_obj.client_ip = '0.0.0.0'
|
36
|
+
req_obj.server_ip = '0.0.0.0'
|
37
|
+
|
38
|
+
req_obj.response = res_obj
|
39
|
+
|
40
|
+
changed
|
41
|
+
notify_observers(:http_request, req_obj)
|
42
|
+
|
43
|
+
rescue SystemExit => ex
|
44
|
+
after
|
45
|
+
|
46
|
+
rescue Exception => ex
|
47
|
+
# There are miscellaneous errors (mostly to do with
|
48
|
+
# incorrect content-length) that we don't care about.
|
49
|
+
Stella::LOGGER.error(ex.message)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
)
|
54
|
+
|
55
|
+
# We need to trap this INT to kill WEBrick. Unlike with Pcap,
|
56
|
+
# rescuing Interrupt doesn't work.
|
57
|
+
|
58
|
+
trap('INT') do
|
59
|
+
after
|
60
|
+
end
|
61
|
+
|
62
|
+
@server.start
|
63
|
+
after
|
64
|
+
|
65
|
+
rescue => ex
|
66
|
+
after
|
67
|
+
end
|
68
|
+
|
69
|
+
def after
|
70
|
+
delete_observers
|
71
|
+
@server.shutdown
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,341 @@
|
|
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
|
+
attr_writer :reps, :concurrent, :version
|
36
|
+
attr_reader :user_agent
|
37
|
+
attr_accessor :help, :config, :verbose, :get, :log, :mark, :delay, :header
|
38
|
+
attr_accessor :rc, :file, :time, :benchmark, :internet
|
39
|
+
|
40
|
+
def initialize(options={}, arguments=[])
|
41
|
+
|
42
|
+
@name = 'siege'
|
43
|
+
@reps = 1
|
44
|
+
@concurrent = 1
|
45
|
+
@private_variables = ['private_variables', 'name', 'arguments', 'load_factor', 'working_directory', 'orig_logfile']
|
46
|
+
@load_factor = 1
|
47
|
+
|
48
|
+
@rc = File.join(ENV['HOME'] || ENV['USERPROFILE'], '.siegerc')
|
49
|
+
|
50
|
+
super(options, arguments)
|
51
|
+
|
52
|
+
# Siege won't run unless there's a siegerc file. If the default one doesn't exist
|
53
|
+
# we need to call siege.config to create it. This should only happen once.
|
54
|
+
# We use capture_output here so STDOUT and STDERR don't print to the screen.
|
55
|
+
Stella::Util.capture_output("#{@name}.config") do 'nothing' end unless File.exists? @rc
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
def error
|
61
|
+
(File.exists? stderr_path) ? FileUtil.read_file(stderr_path) : "Unknown error"
|
62
|
+
end
|
63
|
+
|
64
|
+
def version
|
65
|
+
vsn = 0
|
66
|
+
Stella::Util.capture_output("#{@name} --version") do |stdout, stderr|
|
67
|
+
stderr.join.scan(/SIEGE (\d+?\.\d+)/) { |v| vsn = v[0] }
|
68
|
+
end
|
69
|
+
vsn
|
70
|
+
end
|
71
|
+
|
72
|
+
# loadtest
|
73
|
+
#
|
74
|
+
# True or false: is the call to siege a load test? If it's a call to help or version or
|
75
|
+
# to display the config this with return false. It's no reason for someone to make this
|
76
|
+
# call through Stella but it's here for goodness sake.
|
77
|
+
def loadtest?
|
78
|
+
!@arguments.empty? # The argument is a URI
|
79
|
+
end
|
80
|
+
|
81
|
+
def ready?
|
82
|
+
@name && !instance_variables.empty?
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
# Before calling run
|
87
|
+
def before
|
88
|
+
|
89
|
+
# Keep a copy of the configuration file.
|
90
|
+
copy_siegerc
|
91
|
+
|
92
|
+
# Keep a copy of the URLs file.
|
93
|
+
copy_urls_file if @file
|
94
|
+
|
95
|
+
# TODO: Print message about neither --benchmark or --internet
|
96
|
+
end
|
97
|
+
def command
|
98
|
+
raise CommandNotReady.new(self.class.to_s) unless ready?
|
99
|
+
|
100
|
+
command = "#{@name} "
|
101
|
+
|
102
|
+
instance_variables.each do |name|
|
103
|
+
canon = name.tr('@', '') # instance_variables returns '@name'
|
104
|
+
next if @private_variables.member?(canon)
|
105
|
+
|
106
|
+
# It's important that we take the value from the getter method
|
107
|
+
# because it applies the load factor.
|
108
|
+
value = self.send(canon)
|
109
|
+
if (value.is_a? Array)
|
110
|
+
value.each { |el| command << "--#{canon.tr('_', '-')} #{EscapeUtil.shell_single_word(el.to_s)} " }
|
111
|
+
else
|
112
|
+
command << "--#{canon.tr('_', '-')} #{EscapeUtil.shell_single_word(value.to_s)} "
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
command << (@arguments.map { |uri| "'#{uri}'" }).join(' ') unless @arguments.empty?
|
118
|
+
command
|
119
|
+
end
|
120
|
+
|
121
|
+
# After calling run
|
122
|
+
def after
|
123
|
+
|
124
|
+
update_orig_logfile if @orig_logfile
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
def process_arguments(arguments)
|
130
|
+
opts = OptionParser.new
|
131
|
+
opts.on('-V', '--version') do |v| @version = v end
|
132
|
+
opts.on('-h', '--help') do |v| @help = v end
|
133
|
+
opts.on('-C', '--config') do |v| @config = v end
|
134
|
+
opts.on('-v', '--verbose') do |v| @verbose = v end
|
135
|
+
opts.on('-g', '--get') do |v| @get = v end
|
136
|
+
opts.on('-l', '--log') do |v| @log = v end
|
137
|
+
opts.on('-m S', '--mark=S', String) do |v| @mark = v end
|
138
|
+
opts.on('-d N', '--delay=N', Float) do |v| @delay = v end
|
139
|
+
opts.on('-H S', '--header=S', String) do |v| @header ||= []; @header << v end
|
140
|
+
|
141
|
+
opts.on('-r N', '--reps=N', Integer) do |v| @reps = v.to_i end
|
142
|
+
opts.on('-c N', '--concurrent=N', Integer) do |v| @concurrent = v.to_i end
|
143
|
+
opts.on('-R S', '--rc=S', String) do |v| @rc = v end
|
144
|
+
opts.on('-f S', '--file=S', String) do |v| @file = v end
|
145
|
+
opts.on('-t S', '--time=S', String) do |v| @time = v end
|
146
|
+
opts.on('-b', '--benchmark') do |v| @benchmark = true; end
|
147
|
+
opts.on('-i', '--internet') do |v| @internet = true; end
|
148
|
+
opts.on('-A S', '--user-agent=S', String) do |v| @user_agent ||= []; @user_agent << v end
|
149
|
+
|
150
|
+
|
151
|
+
opts.on('-n N',Integer) do |v|
|
152
|
+
Stella::LOGGER.error("-n is not a Siege parameter. You probably want -r.")
|
153
|
+
exit 1
|
154
|
+
end
|
155
|
+
|
156
|
+
# parse! removes the options it finds.
|
157
|
+
# It also fails when it finds unknown switches (i.e. -X)
|
158
|
+
# Which should leave only the remaining arguments (URIs in this case)
|
159
|
+
opts.parse!(arguments)
|
160
|
+
|
161
|
+
unless @benchmark
|
162
|
+
Stella::LOGGER.warn('--benchmark (or -b) is not selected. Siege will include "think time" for all requests.')
|
163
|
+
end
|
164
|
+
|
165
|
+
self.arguments = arguments
|
166
|
+
|
167
|
+
rescue OptionParser::InvalidOption => ex
|
168
|
+
# We want to replace this text so we grab just the name of the argument
|
169
|
+
badarg = ex.message.gsub('invalid option: ', '')
|
170
|
+
raise InvalidArgument.new(badarg)
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
def add_header(name, value)
|
175
|
+
@header ||= []
|
176
|
+
@header << "#{name}: #{value}"
|
177
|
+
end
|
178
|
+
def user_agent=(list=[])
|
179
|
+
return unless list && !list.empty?
|
180
|
+
list = list.to_ary
|
181
|
+
@user_agent ||= []
|
182
|
+
@user_agent << list
|
183
|
+
@user_agent.flatten
|
184
|
+
end
|
185
|
+
|
186
|
+
def vusers
|
187
|
+
concurrent || 0
|
188
|
+
end
|
189
|
+
def vusers=(v)
|
190
|
+
@concurrent = v
|
191
|
+
end
|
192
|
+
def requests
|
193
|
+
(@reps * concurrent_f).to_i
|
194
|
+
end
|
195
|
+
def requests=(v)
|
196
|
+
@reps = (v / concurrent_f).to_i
|
197
|
+
end
|
198
|
+
def vuser_requests
|
199
|
+
@reps
|
200
|
+
end
|
201
|
+
|
202
|
+
def concurrent
|
203
|
+
(@concurrent * @load_factor).to_i
|
204
|
+
end
|
205
|
+
def concurrent_f
|
206
|
+
(@concurrent * @load_factor).to_f
|
207
|
+
end
|
208
|
+
def reps
|
209
|
+
@reps
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
# Take the last line of the siege.log file and write it to the log file
|
214
|
+
# specified by the user. We don't this so running with Stella is
|
215
|
+
# identical to running it standalone
|
216
|
+
def update_orig_logfile
|
217
|
+
|
218
|
+
return unless (@orig_logfile)
|
219
|
+
log_str = FileUtil.read_file_to_array(log_file) || ''
|
220
|
+
return if log_str.empty?
|
221
|
+
|
222
|
+
if File.exists?(@orig_logfile)
|
223
|
+
FileUtil.append_file(@orig_logfile, log_str[-1], true)
|
224
|
+
else
|
225
|
+
FileUtil.write_file(@orig_logfile, log_str.join(''), true)
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
|
230
|
+
# We want to keep a copy of the configuration file and also
|
231
|
+
# modify it a little bit to make sure we get all the mad info from siege
|
232
|
+
def copy_siegerc
|
233
|
+
|
234
|
+
# Read in the siegerc file so we can manipulate it
|
235
|
+
siegerc_str = FileUtil.read_file(File.expand_path(@rc))
|
236
|
+
|
237
|
+
siegerc_vars = {
|
238
|
+
:verbose => [false, true], # We want to maximize the data output by siege
|
239
|
+
:logging => [false, true],
|
240
|
+
:csv => [false, true]
|
241
|
+
}
|
242
|
+
|
243
|
+
#if (@agent)
|
244
|
+
# siegerc_vars['user-agent'] = ['.*', 'dogs2']
|
245
|
+
#end
|
246
|
+
|
247
|
+
# We'll set the variables in the siegerc file
|
248
|
+
siegerc_vars.each_pair do |var,value|
|
249
|
+
siegerc_str.gsub!(/#{var}\s*=\s*#{value[0]}/, "#{var} = #{value[1]}") # make true
|
250
|
+
siegerc_str.gsub!(/^\#+\s*#{var}/, "#{var}") # remove comment
|
251
|
+
end
|
252
|
+
|
253
|
+
# Look for the enabled logile path
|
254
|
+
# We will use this later to update it from the last line in our copy
|
255
|
+
siegerc_str =~ /^\s*logfile\s*=\s*(.+?)$/
|
256
|
+
@orig_logfile = $1 || nil
|
257
|
+
|
258
|
+
# Replace all environment variables with literal values
|
259
|
+
@orig_logfile.gsub!(/\$\{#{$1}\}/, ENV[$1]) while (@orig_logfile =~ /\$\{(.+?)\}/ && ENV.has_key?($1))
|
260
|
+
|
261
|
+
@orig_logfile = File.expand_path(@orig_logfile) if @orig_logfile
|
262
|
+
|
263
|
+
|
264
|
+
siegerc_str.gsub!(/^\#*\s*logfile\s*=\s*.*?$/, "logfile = " + log_file)
|
265
|
+
|
266
|
+
FileUtil.write_file(rc_file, siegerc_str, true)
|
267
|
+
@rc = rc_file
|
268
|
+
end
|
269
|
+
|
270
|
+
# We want to keep a copy of the URLs file too
|
271
|
+
def copy_urls_file
|
272
|
+
if @file
|
273
|
+
File.copy(File.expand_path(@file), uris_file)
|
274
|
+
@file = uris_file
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# Siege writes the summary to STDERR
|
279
|
+
def summary_file
|
280
|
+
File.new(stderr_path) if File.exists?(stderr_path)
|
281
|
+
end
|
282
|
+
|
283
|
+
def rc_file
|
284
|
+
File.join(@working_directory, "siegerc")
|
285
|
+
end
|
286
|
+
|
287
|
+
def log_file
|
288
|
+
File.join(@working_directory, "siege.log")
|
289
|
+
end
|
290
|
+
|
291
|
+
def uris_file
|
292
|
+
File.join(@working_directory, File.basename(@file))
|
293
|
+
end
|
294
|
+
|
295
|
+
def summary
|
296
|
+
return unless summary_file
|
297
|
+
raw = {}
|
298
|
+
summary_file.each_line { |l|
|
299
|
+
l.chomp!
|
300
|
+
nvpair = l.split(':')
|
301
|
+
next unless nvpair && nvpair.size == 2
|
302
|
+
n = nvpair[0].strip.tr(' ', '_').downcase[/\w+/]
|
303
|
+
v = nvpair[1].strip[/[\.\d]+/]
|
304
|
+
raw[n.to_sym] = v.to_f
|
305
|
+
}
|
306
|
+
|
307
|
+
stats = Stella::Test::Run::Summary.new
|
308
|
+
|
309
|
+
# Transactions: 750 hits
|
310
|
+
# Availability: 100.00 %
|
311
|
+
# Elapsed time: 2.33 secs
|
312
|
+
# Data transferred: 0.07 MB
|
313
|
+
# Response time: 0.21 secs
|
314
|
+
# Transaction rate: 321.89 trans/sec
|
315
|
+
# Throughput: 0.03 MB/sec
|
316
|
+
# Concurrency: 67.49
|
317
|
+
# Successful transactions: 750
|
318
|
+
# Failed transactions: 0
|
319
|
+
# Longest transaction: 0.33
|
320
|
+
# Shortest transaction: 0.10
|
321
|
+
|
322
|
+
stats.vusers = raw[:concurrency]
|
323
|
+
stats.data_transferred = raw[:data_transferred]
|
324
|
+
stats.elapsed_time = raw[:elapsed_time]
|
325
|
+
stats.response_time = raw[:response_time]
|
326
|
+
stats.transactions = raw[:transactions].to_i
|
327
|
+
stats.transaction_rate = raw[:transaction_rate]
|
328
|
+
stats.failed = raw[:failed_transactions].to_i
|
329
|
+
stats.successful = raw[:successful_transactions].to_i
|
330
|
+
|
331
|
+
#stats.shortest_transaction = raw[:shortest_transaction]
|
332
|
+
#stats.longest_transaction = raw[:longest_transaction]
|
333
|
+
|
334
|
+
stats
|
335
|
+
end
|
336
|
+
|
337
|
+
|
338
|
+
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
data/lib/stella/cli.rb
ADDED
@@ -0,0 +1,258 @@
|
|
1
|
+
|
2
|
+
# http://www.ruby-doc.org/stdlib/libdoc/observer/rdoc/index.html
|
3
|
+
|
4
|
+
|
5
|
+
require 'stella/cli/base'
|
6
|
+
|
7
|
+
module Stella
|
8
|
+
|
9
|
+
|
10
|
+
# Stella::CLI
|
11
|
+
#
|
12
|
+
# The is the front-end class for the command-line implementation. The stella script
|
13
|
+
# creates an instance of this class which is the glue between the command-line
|
14
|
+
# and the Stella command classes.
|
15
|
+
# Note: All Stella::CLI classes are autoloaded and they add themselves to @@commands.
|
16
|
+
class CLI
|
17
|
+
|
18
|
+
# Auto populated with 'command' => Stella::CLI::[class] by each cli class on 'require'.
|
19
|
+
@@commands = {}
|
20
|
+
|
21
|
+
attr_reader :command_name
|
22
|
+
attr_reader :options
|
23
|
+
attr_reader :logger
|
24
|
+
attr_reader :stella_arguments
|
25
|
+
attr_reader :command_arguments
|
26
|
+
|
27
|
+
|
28
|
+
def initialize(arguments=[], stdin=nil)
|
29
|
+
@arguments = arguments
|
30
|
+
@stdin = stdin
|
31
|
+
|
32
|
+
@options = OpenStruct.new
|
33
|
+
@options.verbose = 0
|
34
|
+
@options.data_path = 'stella'
|
35
|
+
@options.agents = []
|
36
|
+
|
37
|
+
@stella_arguments = []
|
38
|
+
@command_arguments = []
|
39
|
+
|
40
|
+
process_arguments
|
41
|
+
process_options
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
def commands
|
46
|
+
@@commands
|
47
|
+
end
|
48
|
+
|
49
|
+
def run
|
50
|
+
|
51
|
+
unless (@command_name)
|
52
|
+
process_options(:display)
|
53
|
+
exit 0
|
54
|
+
end
|
55
|
+
|
56
|
+
# Pull the requested command object out of the list
|
57
|
+
# and tell it what shortname that was used to call it.
|
58
|
+
command = @@commands[@command_name].new(@command_name)
|
59
|
+
|
60
|
+
# Give the command object access to the runtime options and arguments
|
61
|
+
command.stella_options = @options
|
62
|
+
command.arguments = @command_arguments
|
63
|
+
command.working_directory = @options.data_path
|
64
|
+
|
65
|
+
command.run
|
66
|
+
|
67
|
+
rescue => ex
|
68
|
+
Stella::LOGGER.error(ex)
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
protected
|
73
|
+
|
74
|
+
|
75
|
+
# process_arguments
|
76
|
+
#
|
77
|
+
# Split the arguments into stella args and command args
|
78
|
+
# i.e. stella -H push -f (-H is a stella arg, -f is a command arg)
|
79
|
+
# True if required arguments were provided
|
80
|
+
def process_arguments
|
81
|
+
|
82
|
+
@command_name = nil
|
83
|
+
@arguments.each do |arg|
|
84
|
+
if (@@commands.has_key? arg)
|
85
|
+
@command_name = arg
|
86
|
+
index = @arguments.index(@command_name)
|
87
|
+
@command_arguments = @arguments[index + 1..@arguments.size]
|
88
|
+
@stella_arguments = @arguments[0..index - 1] if index > 0
|
89
|
+
break
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
@stella_arguments = [] unless @stella_arguments
|
94
|
+
@command_arguments = [] unless @command_arguments
|
95
|
+
|
96
|
+
# If there's no command we'll assume all the options are for Stella
|
97
|
+
unless @command_name
|
98
|
+
@stella_arguments = @arguments
|
99
|
+
@arguments = []
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
# process_options
|
105
|
+
#
|
106
|
+
# Handle the command-line options for stella. Note: The command specific
|
107
|
+
# options are handled by the command/*.rb classes
|
108
|
+
# display:: When true, it'll print out the options and not parse the arguments
|
109
|
+
def process_options(display=false)
|
110
|
+
|
111
|
+
opts = OptionParser.new
|
112
|
+
opts.banner = Stella::TEXT.msg(:option_help_usage)
|
113
|
+
opts.on Stella::TEXT.msg(:option_help_preamble, @@commands.keys.sort.join(', '))
|
114
|
+
|
115
|
+
opts.on(Stella::TEXT.msg(:option_help_options_title))
|
116
|
+
opts.on('-V', '--version', Stella::TEXT.msg(:option_help_version)) do
|
117
|
+
output_version
|
118
|
+
exit 0
|
119
|
+
end
|
120
|
+
opts.on('-h', '--help', Stella::TEXT.msg(:option_help_help)) { puts opts; exit 0 }
|
121
|
+
opts.on('-F', '--force', Stella::TEXT.msg(:option_help_force)) { |v| @options.force = true }
|
122
|
+
|
123
|
+
opts.on('-v', '--verbose', Stella::TEXT.msg(:option_help_verbose)) do
|
124
|
+
|
125
|
+
@options.verbose ||= 0
|
126
|
+
@options.verbose += 1
|
127
|
+
end
|
128
|
+
opts.on('-q', '--quiet', Stella::TEXT.msg(:option_help_quiet)) do
|
129
|
+
@options.quiet = true
|
130
|
+
end
|
131
|
+
|
132
|
+
# Overhead is interesting for development and auditing but we're not
|
133
|
+
# currently tracking this. It needed to be re-implemented from scratch
|
134
|
+
# so we'll redo this soon. It's also useful for comparing Ruby/JRuby/IronRuby
|
135
|
+
#opts.on('--overhead', String, Stella::TEXT.msg(:option_help_overhead)) do
|
136
|
+
# @options.showoverhead = true
|
137
|
+
#end
|
138
|
+
|
139
|
+
opts.on('-O', '--stdout', Stella::TEXT.msg(:option_help_stdout)) do
|
140
|
+
@options.stdout = true
|
141
|
+
end
|
142
|
+
opts.on('-E', '--stderr', Stella::TEXT.msg(:option_help_stderr)) do
|
143
|
+
@options.stdout = true
|
144
|
+
end
|
145
|
+
|
146
|
+
opts.on('-m', '--message=M', String, Stella::TEXT.msg(:option_help_message)) do |v|
|
147
|
+
@options.message = v.to_s
|
148
|
+
end
|
149
|
+
opts.on('-s', '--sleep=N', Float, Stella::TEXT.msg(:option_help_sleep)) do |v|
|
150
|
+
@options.sleep = v.to_f
|
151
|
+
end
|
152
|
+
|
153
|
+
# Ramp up, establish default and enforce limits
|
154
|
+
opts.on('-r [R,U]', '--rampup', String, Stella::TEXT.msg(:option_help_rampup)) do |v|
|
155
|
+
amount = (v) ? Stella::Util::expand_str(v) : [10,100]
|
156
|
+
amount[0] = MathUtil.enforce_limit(amount[0].to_i, 1, 100)
|
157
|
+
amount[1] = MathUtil.enforce_limit((amount[1]) ? amount[1].to_i : 0, (amount[0]*2), 1000)
|
158
|
+
@options.rampup = amount
|
159
|
+
end
|
160
|
+
|
161
|
+
opts.on('-x', '--repetitions=N', Integer, Stella::TEXT.msg(:option_help_testreps)) do |v|
|
162
|
+
@options.repetitions = MathUtil.enforce_limit(v,1,99)
|
163
|
+
end
|
164
|
+
|
165
|
+
opts.on('-w [N]', '--warmup', Float, Stella::TEXT.msg(:option_help_warmup, 0.1)) do |v|
|
166
|
+
@options.warmup = MathUtil.enforce_limit(((v) ? v : 0), 0.1, 1)
|
167
|
+
end
|
168
|
+
|
169
|
+
opts.on('-a', '--agent=[S]', String, Stella::TEXT.msg(:option_help_agent)) do |v|
|
170
|
+
@options.agents ||= []
|
171
|
+
agent_ary = Stella::Util::expand_str(v || 'random')
|
172
|
+
@options.agents.push(agent_ary)
|
173
|
+
end
|
174
|
+
|
175
|
+
opts.on('-d', '--datapath=S', String, Stella::TEXT.msg(:option_help_datapath, ".#{File::SEPARATOR}stella")) do |v|
|
176
|
+
@options.data_path = v.to_s
|
177
|
+
end
|
178
|
+
opts.on('-f', '--format=S', String, Stella::TEXT.msg(:option_help_format)) do |v|
|
179
|
+
@options.format = v.to_s
|
180
|
+
end
|
181
|
+
|
182
|
+
# This is used for printing the help from other parts of this class
|
183
|
+
if display
|
184
|
+
Stella::LOGGER.info opts
|
185
|
+
return
|
186
|
+
end
|
187
|
+
|
188
|
+
# This applies the configuration above to the arguments provided.
|
189
|
+
# It also removes the discovered options from @stella_arguments
|
190
|
+
# leaving only the unnamed arguments.
|
191
|
+
opts.parse!(@stella_arguments)
|
192
|
+
|
193
|
+
# Quiet supercedes verbose
|
194
|
+
@options.verbose = 0 if @options.quiet
|
195
|
+
|
196
|
+
|
197
|
+
# This outputs when debugging is enabled.
|
198
|
+
dump_inputs
|
199
|
+
|
200
|
+
|
201
|
+
rescue OptionParser::InvalidOption => ex
|
202
|
+
# We want to replace this text so we grab just the name of the argument
|
203
|
+
badarg = ex.message.gsub('invalid option: ', '')
|
204
|
+
raise InvalidArgument.new(badarg)
|
205
|
+
end
|
206
|
+
|
207
|
+
#
|
208
|
+
# Process data sent to STDIN (a pipe for example).
|
209
|
+
# We assume each line is a URI and add it to @arguments.
|
210
|
+
def process_standard_input
|
211
|
+
return if @stdin.tty? # We only want piped data
|
212
|
+
|
213
|
+
while !@stdin.eof? do
|
214
|
+
line = @stdin.readline
|
215
|
+
line.chomp!
|
216
|
+
@arguments << line
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
def output_version
|
222
|
+
Stella::LOGGER.info(:cli_print_version, Stella::VERSION.to_s)
|
223
|
+
end
|
224
|
+
|
225
|
+
def dump_inputs
|
226
|
+
|
227
|
+
#ENV.each_pair do |n,v|
|
228
|
+
# Stella::LOGGER.debug("ENV[#{n}]=#{v}")
|
229
|
+
#end
|
230
|
+
|
231
|
+
Stella::LOGGER.debug("Commands (#{@command_name}): #{@@commands.keys.join(',')}")
|
232
|
+
|
233
|
+
|
234
|
+
Stella::LOGGER.debug("Options: ")
|
235
|
+
@options.marshal_dump.each_pair do |n,v|
|
236
|
+
v = [v] unless v.is_a? Array
|
237
|
+
Stella::LOGGER.debug(" #{n} = #{v.join(',')}")
|
238
|
+
end
|
239
|
+
|
240
|
+
Stella::LOGGER.debug("Stella Arguments: #{@stella_arguments.join(',')}")
|
241
|
+
Stella::LOGGER.debug("Command Arguments: #{@command_arguments.join(',')}" )
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Autoload CLI classes. These classes add themselves to the class variable @@commands.
|
247
|
+
begin
|
248
|
+
previous_path = ""
|
249
|
+
cli_classes = Dir.glob(File.join(STELLA_HOME, 'lib', 'stella', 'cli', "*.rb"))
|
250
|
+
cli_classes.each do |path|
|
251
|
+
previous_path = path
|
252
|
+
require path
|
253
|
+
end
|
254
|
+
rescue LoadError => ex
|
255
|
+
Stella::LOGGER.info("Error loading #{previous_path}: #{ex.message}")
|
256
|
+
end
|
257
|
+
|
258
|
+
|