solutious-stella 0.5.5
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.
- 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
|
+
|