stella 0.5.3 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- data/{README.txt → README.textile} +63 -40
- data/Rakefile +7 -5
- data/bin/stella +1 -1
- data/bin/stella.bat +12 -0
- data/lib/pcaplet.rb +180 -0
- data/lib/stella/adapter/ab.rb +57 -33
- data/lib/stella/adapter/base.rb +11 -1
- data/lib/stella/adapter/httperf.rb +13 -10
- data/lib/stella/adapter/pcap_watcher.rb +221 -0
- data/lib/stella/adapter/proxy_watcher.rb +76 -0
- data/lib/stella/adapter/siege.rb +28 -11
- data/lib/stella/cli/agents.rb +2 -2
- data/lib/stella/cli/base.rb +37 -1
- data/lib/stella/cli/localtest.rb +1 -2
- data/lib/stella/cli/sysinfo.rb +17 -0
- data/lib/stella/cli/watch.rb +278 -0
- data/lib/stella/cli.rb +23 -11
- data/lib/stella/command/base.rb +1 -10
- data/lib/stella/command/localtest.rb +43 -23
- data/lib/stella/data/domain.rb +75 -0
- data/lib/stella/data/http.rb +124 -0
- data/lib/stella/logger.rb +16 -5
- data/lib/stella/storable.rb +4 -2
- data/lib/stella/support.rb +71 -0
- data/lib/stella/sysinfo.rb +247 -0
- data/lib/stella/test/base.rb +5 -1
- data/lib/stella/test/definition.rb +1 -1
- data/lib/stella/test/run/summary.rb +14 -4
- data/lib/stella/text/resource.rb +0 -1
- data/lib/stella.rb +28 -10
- data/lib/utils/domainutil.rb +47 -0
- data/lib/utils/fileutil.rb +22 -3
- data/lib/utils/httputil.rb +184 -128
- data/lib/utils/mathutil.rb +20 -7
- data/lib/win32/Console/ANSI.rb +305 -0
- data/lib/win32/Console.rb +970 -0
- data/spec/show-agents_spec.rb +0 -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 +26 -3
- data/vendor/frylock/README.textile +72 -0
- data/vendor/frylock/bin/example +170 -0
- data/vendor/frylock/frylock.gemspec +18 -0
- data/vendor/frylock/lib/frylock/exceptions.rb +24 -0
- data/vendor/frylock/lib/frylock.rb +232 -0
- data/vendor/frylock/test/command_test.rb +33 -0
- data/vendor/hitimes-0.4.0/HISTORY +28 -0
- data/vendor/hitimes-0.4.0/LICENSE.txt +19 -0
- data/vendor/hitimes-0.4.0/README +80 -0
- data/vendor/hitimes-0.4.0/Rakefile +63 -0
- data/vendor/hitimes-0.4.0/examples/benchmarks.rb +86 -0
- data/vendor/hitimes-0.4.0/examples/stats.rb +29 -0
- data/vendor/hitimes-0.4.0/ext/extconf.rb +15 -0
- data/vendor/hitimes-0.4.0/ext/hitimes_ext.c +21 -0
- data/vendor/hitimes-0.4.0/ext/hitimes_instant_clock_gettime.c +20 -0
- data/vendor/hitimes-0.4.0/ext/hitimes_instant_osx.c +16 -0
- data/vendor/hitimes-0.4.0/ext/hitimes_instant_windows.c +27 -0
- data/vendor/hitimes-0.4.0/ext/hitimes_interval.c +340 -0
- data/vendor/hitimes-0.4.0/ext/hitimes_interval.h +73 -0
- data/vendor/hitimes-0.4.0/ext/hitimes_stats.c +242 -0
- data/vendor/hitimes-0.4.0/ext/hitimes_stats.h +30 -0
- data/vendor/hitimes-0.4.0/ext/rbconfig-mingw.rb +178 -0
- data/vendor/hitimes-0.4.0/ext/rbconfig.rb +178 -0
- data/vendor/hitimes-0.4.0/gemspec.rb +54 -0
- data/vendor/hitimes-0.4.0/lib/hitimes/mutexed_stats.rb +23 -0
- data/vendor/hitimes-0.4.0/lib/hitimes/paths.rb +54 -0
- data/vendor/hitimes-0.4.0/lib/hitimes/stats.rb +29 -0
- data/vendor/hitimes-0.4.0/lib/hitimes/timer.rb +223 -0
- data/vendor/hitimes-0.4.0/lib/hitimes/version.rb +42 -0
- data/vendor/hitimes-0.4.0/lib/hitimes.rb +24 -0
- data/vendor/hitimes-0.4.0/spec/interval_spec.rb +115 -0
- data/vendor/hitimes-0.4.0/spec/mutex_stats_spec.rb +34 -0
- data/vendor/hitimes-0.4.0/spec/paths_spec.rb +14 -0
- data/vendor/hitimes-0.4.0/spec/spec_helper.rb +6 -0
- data/vendor/hitimes-0.4.0/spec/stats_spec.rb +72 -0
- data/vendor/hitimes-0.4.0/spec/timer_spec.rb +105 -0
- data/vendor/hitimes-0.4.0/spec/version_spec.rb +27 -0
- data/vendor/hitimes-0.4.0/tasks/announce.rake +39 -0
- data/vendor/hitimes-0.4.0/tasks/config.rb +107 -0
- data/vendor/hitimes-0.4.0/tasks/distribution.rake +53 -0
- data/vendor/hitimes-0.4.0/tasks/documentation.rake +33 -0
- data/vendor/hitimes-0.4.0/tasks/extension.rake +64 -0
- data/vendor/hitimes-0.4.0/tasks/rspec.rake +31 -0
- data/vendor/hitimes-0.4.0/tasks/rubyforge.rake +52 -0
- data/vendor/hitimes-0.4.0/tasks/utils.rb +80 -0
- data/vendor/useragent/lib/user_agent.rb +1 -1
- metadata +87 -8
@@ -0,0 +1,278 @@
|
|
1
|
+
|
2
|
+
# TODO: Record cookies.
|
3
|
+
# TODO: Investigate packetfu: http://code.google.com/p/packetfu/
|
4
|
+
# TODO: Investigate Winpcap (http://www.winpcap.org/) and libpcap on Windows
|
5
|
+
|
6
|
+
module Stella
|
7
|
+
class CLI
|
8
|
+
class Watch < Stella::CLI::Base
|
9
|
+
|
10
|
+
|
11
|
+
def run
|
12
|
+
@options = process_arguments(@arguments)
|
13
|
+
|
14
|
+
if can_pcap?(@options[:usepcap])
|
15
|
+
require 'stella/adapter/pcap_watcher'
|
16
|
+
@watcher = Stella::Adapter::PcapWatcher.new(@options)
|
17
|
+
|
18
|
+
else
|
19
|
+
require 'stella/adapter/proxy_watcher'
|
20
|
+
@watcher = Stella::Adapter::ProxyWatcher.new(@options)
|
21
|
+
|
22
|
+
# if can_pcap? returned false, but pcap was requested then we'll
|
23
|
+
# call check_pcap to raise the reason why it didn't load.
|
24
|
+
if @options[:usepcap]
|
25
|
+
check_pcap
|
26
|
+
exit 0
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# We use an observer model so the watcher class will notify us
|
31
|
+
# when they have new data. They call the update method below.
|
32
|
+
@watcher.add_observer(self)
|
33
|
+
|
34
|
+
if @options[:record]
|
35
|
+
|
36
|
+
@record_filepath = generate_record_filepath
|
37
|
+
Stella::LOGGER.info("Writing to #{@record_filepath}")
|
38
|
+
|
39
|
+
if File.exists?(@record_filepath)
|
40
|
+
Stella::LOGGER.error("#{@record_filepath} exists")
|
41
|
+
if @stella_options.force
|
42
|
+
Stella::LOGGER.error("But I'll overwrite it and continue because you forced me too!")
|
43
|
+
else
|
44
|
+
exit 1
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
Stella::LOGGER.info("Filter: #{@options[:filter]}") if @options[:filter]
|
51
|
+
Stella::LOGGER.info("Domain: #{@options[:domain]}") if @options[:domain]
|
52
|
+
|
53
|
+
# Turn wildcards into regular expressions
|
54
|
+
@options[:filter].gsub!('*', '.*') if @options[:filter]
|
55
|
+
@options[:domain].gsub!('*', '.*') if @options[:domain]
|
56
|
+
|
57
|
+
# Used to calculated user think times for session output
|
58
|
+
@think_time = 0
|
59
|
+
|
60
|
+
@watcher.run
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
# update
|
67
|
+
#
|
68
|
+
# This method is called from the watcher class when data is updated.
|
69
|
+
# +service+ is one of: domain, http_request, http_response
|
70
|
+
# +data+ is a string of TCP packet data. The format depends on the value of +service+.
|
71
|
+
def update(service, data_object)
|
72
|
+
|
73
|
+
begin
|
74
|
+
if @options[:record] && !@file_created_already
|
75
|
+
|
76
|
+
if File.exists?(@record_filepath)
|
77
|
+
if @stella_options.force
|
78
|
+
@record_file = FileUtil.create_file(@record_filepath, 'w', '.', :force)
|
79
|
+
else
|
80
|
+
exit 1
|
81
|
+
end
|
82
|
+
else
|
83
|
+
@record_file = FileUtil.create_file(@record_filepath, 'w', '.')
|
84
|
+
end
|
85
|
+
|
86
|
+
raise StellaError.new("Cannot open: #{@record_filepath}") unless @record_file
|
87
|
+
|
88
|
+
@file_created_already = true
|
89
|
+
end
|
90
|
+
rescue => ex
|
91
|
+
raise StellaError.new("Error creating file: #{ex.message}")
|
92
|
+
end
|
93
|
+
|
94
|
+
# TODO: combine requests and responses
|
95
|
+
# Disabled until we have a way to combine request and response objects (otherwise
|
96
|
+
# the requests are filters out but the responses are not).
|
97
|
+
#return if @options[:filter] && !(data_object.raw_data.to_s =~ /#{@options[:filter]}/i)
|
98
|
+
#return if @options[:domain] && !(data_object.uri.to_s =~ /(www.)?#{@options[:domain]}/i)
|
99
|
+
|
100
|
+
if @stella_options.format && data_object.respond_to?("to_#{@stella_options.format}")
|
101
|
+
Stella::LOGGER.info(data_object.send("to_#{@stella_options.format}"))
|
102
|
+
|
103
|
+
if data_object.has_response?
|
104
|
+
Stella::LOGGER.info(data_object.response.send("to_#{@stella_options.format}"))
|
105
|
+
end
|
106
|
+
|
107
|
+
else
|
108
|
+
if @stella_options.verbose > 1
|
109
|
+
Stella::LOGGER.info(data_object.inspect, '')
|
110
|
+
|
111
|
+
if data_object.has_response?
|
112
|
+
Stella::LOGGER.info(data_object.response.inspect, '', '')
|
113
|
+
end
|
114
|
+
|
115
|
+
elsif @stella_options.verbose > 0
|
116
|
+
Stella::LOGGER.info(data_object.to_s)
|
117
|
+
Stella::LOGGER.info(data_object.body) if data_object.has_body?
|
118
|
+
|
119
|
+
if data_object.has_response?
|
120
|
+
Stella::LOGGER.info(data_object.response.to_s)
|
121
|
+
Stella::LOGGER.info(data_object.response.body) if data_object.response.has_body?
|
122
|
+
end
|
123
|
+
|
124
|
+
else
|
125
|
+
Stella::LOGGER.info(data_object.to_s)
|
126
|
+
Stella::LOGGER.info(data_object.response.to_s) if data_object.has_response?
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
rescue Exception => ex
|
132
|
+
Stella::LOGGER.error(ex)
|
133
|
+
#exit 1
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
# can_pcap?
|
139
|
+
#
|
140
|
+
# Returns true is pcap is available
|
141
|
+
def can_pcap?(usepcap=false)
|
142
|
+
return false unless usepcap
|
143
|
+
begin
|
144
|
+
check_pcap
|
145
|
+
rescue
|
146
|
+
return false
|
147
|
+
end
|
148
|
+
return true
|
149
|
+
end
|
150
|
+
|
151
|
+
# check_pcap
|
152
|
+
#
|
153
|
+
# The Pcap gauntlet. A number of conditions must be met to run the Pcap recorder:
|
154
|
+
# - The watch command must be called with -p
|
155
|
+
# - The OS must be a form of Unix
|
156
|
+
# - Cannot be running via JRuby or Java
|
157
|
+
# - The user must be root (or running through sudo)
|
158
|
+
# - The Ruby-Pcap library must be installed.
|
159
|
+
def check_pcap(usepcap=false)
|
160
|
+
raise MissingDependency.new('pcap', :error_sysinfo_notunix) unless Stella::SYSINFO.os === :unix # pcap not available on windows or java
|
161
|
+
raise MissingDependency.new('pcap', :error_sysinfo_notroot) unless ENV['USER'] === 'root' # Must run as root or sudo
|
162
|
+
begin
|
163
|
+
require 'pcap'
|
164
|
+
rescue Exception, LoadError => ex
|
165
|
+
raise MissingDependency.new('pcap', :error_watch_norubypcap)
|
166
|
+
end
|
167
|
+
false
|
168
|
+
end
|
169
|
+
|
170
|
+
def generate_record_filepath
|
171
|
+
filepath = nil
|
172
|
+
|
173
|
+
if (@options[:record].is_a? String)
|
174
|
+
filepath = File.expand_path(@options[:record])
|
175
|
+
else
|
176
|
+
now = DateTime.now
|
177
|
+
daystr = "#{now.year}-#{now.mon.to_s.rjust(2,'0')}-#{now.mday.to_s.rjust(2,'0')}"
|
178
|
+
dirpath = File.join(@working_directory, 'stories', daystr)
|
179
|
+
|
180
|
+
FileUtil.create_dir(dirpath, ".")
|
181
|
+
filepath = File.join(dirpath, 'story')
|
182
|
+
testnum = 1.to_s.rjust(2,'0')
|
183
|
+
testnum.succ! while(File.exists? "#{filepath}-#{testnum}.txt")
|
184
|
+
filepath = "#{filepath}-#{testnum}.txt"
|
185
|
+
end
|
186
|
+
|
187
|
+
return filepath
|
188
|
+
end
|
189
|
+
|
190
|
+
def after
|
191
|
+
# Close Pcap / Shutdown Proxy
|
192
|
+
@watcher.after
|
193
|
+
|
194
|
+
# We don't need to close or delete a file that wasn't created
|
195
|
+
return unless @record_file
|
196
|
+
|
197
|
+
# And we don't want to delete a file that we're overwriting but may
|
198
|
+
# not have actually written anything to yet. IOW, original file will
|
199
|
+
# remain intact if we haven't written anything to it yet.
|
200
|
+
@record_file.close if @forced_overwrite
|
201
|
+
|
202
|
+
# Delete an empty file, otherwise close it
|
203
|
+
@record_file.stat.size == 0 ? File.unlink(@record_file.path) : @record_file.close
|
204
|
+
end
|
205
|
+
|
206
|
+
def process_arguments(arguments, display=false)
|
207
|
+
opts = OptionParser.new
|
208
|
+
|
209
|
+
opts.banner = "Usage: #{File.basename($0)} [global options] watch [command options] [http|dns]"
|
210
|
+
opts.on("#{$/}Example: #{File.basename($0)} -v watch -C http#{$/}")
|
211
|
+
opts.on('-h', '--help', "Display this message") do
|
212
|
+
Stella::LOGGER.info opts
|
213
|
+
exit 0
|
214
|
+
end
|
215
|
+
|
216
|
+
opts.on("#{$/}Operating mode")
|
217
|
+
opts.on('-P', '--useproxy', "Use an HTTP proxy to filter requests (default)") do |v| v end
|
218
|
+
opts.on('-C', '--usepcap', "Use Pcap to filter TCP packets") do |v| v end
|
219
|
+
#opts.on('-F', '--usepacketfu', "Use Packetfu to filter TCP packets") do |v| v end
|
220
|
+
|
221
|
+
opts.on("#{$/}Pcap-specific options")
|
222
|
+
opts.on('-i=S', '--interface=S', String, "Network device. eri0, en1, etc. (with --usepcap only)") do |v| v end
|
223
|
+
opts.on('-m=N', '--maxpacks=N', Integer, "Maximum number of packets to sniff (with --usepcap only)") do |v| v end
|
224
|
+
opts.on('-R=S', '--protocol=S', String, "Communication protocol to sniff. udp or tcp (with --usepcap only)") do |v| v end
|
225
|
+
|
226
|
+
opts.on("#{$/}Common options")
|
227
|
+
opts.on('-p=N', '--port=N', Integer, "With --useproxy this is the Proxy port. With --usecap this is the TCP port to filter. ") do |v| v end
|
228
|
+
#opts.on('-f=S', '--filter=S', String, "Filter out requests which do not contain this string") do |v| v end
|
229
|
+
#opts.on('-d=S', '--domain=S', String, "Only display requests to the given domain") do |v| v end
|
230
|
+
#opts.on('-r=S', '--record=S', String, "Record requests to file with an optional filename") do |v| v || true end
|
231
|
+
#opts.on('-F=S', '--format=S', "Format of recorded file. One of: simple (for Siege), session (for Httperf)") do |v| v end
|
232
|
+
|
233
|
+
options = opts.getopts(@arguments)
|
234
|
+
options = options.keys.inject({}) do |hash, key|
|
235
|
+
hash[key.to_sym] = options[key]
|
236
|
+
hash
|
237
|
+
end
|
238
|
+
|
239
|
+
# "interface" is more clear on the command line but we use "device" internally
|
240
|
+
options[:device] = options.delete(:interface) if options[:interface]
|
241
|
+
options[:service] = arguments.shift unless arguments.empty?
|
242
|
+
|
243
|
+
options
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
|
248
|
+
@@commands['watch'] = Stella::CLI::Watch
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
|
253
|
+
__END__
|
254
|
+
|
255
|
+
# pageload?
|
256
|
+
#
|
257
|
+
# Used while writing the session log file. Returns true when we
|
258
|
+
# suspect a new page has loaded. Otherwise the resource is considered
|
259
|
+
# to be a dependency.
|
260
|
+
def pageload?(now, think_time, host, referer, content_type)
|
261
|
+
time_difference = (now.to_i - @think_time.to_i)
|
262
|
+
time_passed = (@think_time == 0 || time_difference > 4)
|
263
|
+
non_html = (content_type !~ /text\/html/i) if content_type
|
264
|
+
#puts "POOO: #{content_type} #{referer}"
|
265
|
+
|
266
|
+
case [time_passed, non_html]
|
267
|
+
when [true,false]
|
268
|
+
return true
|
269
|
+
when [true,true]
|
270
|
+
return false
|
271
|
+
when [true,nil]
|
272
|
+
return true
|
273
|
+
when [false,false]
|
274
|
+
return false
|
275
|
+
else
|
276
|
+
return false
|
277
|
+
end
|
278
|
+
end
|
data/lib/stella/cli.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
|
2
|
+
# http://www.ruby-doc.org/stdlib/libdoc/observer/rdoc/index.html
|
3
|
+
|
2
4
|
require 'optparse'
|
3
5
|
require 'ostruct'
|
4
6
|
|
@@ -10,8 +12,11 @@ module Stella
|
|
10
12
|
#
|
11
13
|
# This Config class manages the content of ENV['HOME]/.stella. The functionality
|
12
14
|
# is currently disabled so stella is stateless.
|
15
|
+
# RUBY_PLATFORM = 'java' in jruby and i386-mswin32 for windows
|
13
16
|
class Config < Storable
|
14
|
-
|
17
|
+
USER_HOME = ENV['USERPROFILE'] || ENV['HOME']
|
18
|
+
STELLA_DIR = '.stella'
|
19
|
+
DEFAULT_HOME = File.join(USER_HOME, STELLA_DIR).freeze unless defined? DEFAULT_HOME
|
15
20
|
DEFAULT_DATA_HOME = File.join(Dir.getwd, 'stella').freeze unless defined? DEFAULT_DATA_HOME
|
16
21
|
|
17
22
|
attr_accessor :conf_path, :data_path
|
@@ -69,6 +74,8 @@ module Stella
|
|
69
74
|
@options.data_path = @config.working_directory
|
70
75
|
@options.agents = []
|
71
76
|
|
77
|
+
@stella_arguments = []
|
78
|
+
@command_arguments = []
|
72
79
|
end
|
73
80
|
|
74
81
|
def commands
|
@@ -76,6 +83,7 @@ module Stella
|
|
76
83
|
end
|
77
84
|
|
78
85
|
def run
|
86
|
+
|
79
87
|
process_arguments
|
80
88
|
process_options
|
81
89
|
|
@@ -89,14 +97,12 @@ module Stella
|
|
89
97
|
command = @@commands[@command_name].new(@command_name)
|
90
98
|
|
91
99
|
# Give the command object access to the config and runtime options
|
92
|
-
#command.global_config = @config
|
93
100
|
command.stella_options = @options
|
94
101
|
command.arguments = @command_arguments
|
95
102
|
command.working_directory = @options.data_path
|
96
103
|
|
97
104
|
command.run
|
98
105
|
|
99
|
-
|
100
106
|
rescue => ex
|
101
107
|
Stella::LOGGER.error(ex)
|
102
108
|
end
|
@@ -122,9 +128,9 @@ module Stella
|
|
122
128
|
break
|
123
129
|
end
|
124
130
|
end
|
125
|
-
|
126
|
-
@
|
127
|
-
@
|
131
|
+
|
132
|
+
@stella_arguments = [] unless @stella_arguments
|
133
|
+
@command_arguments = [] unless @command_arguments
|
128
134
|
|
129
135
|
# If there's no command we'll assume all the options are for Stella
|
130
136
|
unless @command_name
|
@@ -143,7 +149,7 @@ module Stella
|
|
143
149
|
|
144
150
|
opts = OptionParser.new
|
145
151
|
opts.banner = Stella::TEXT.msg(:option_help_usage)
|
146
|
-
opts.on Stella::TEXT.msg(:option_help_preamble, @@commands.keys.join(', '))
|
152
|
+
opts.on Stella::TEXT.msg(:option_help_preamble, @@commands.keys.sort.join(', '))
|
147
153
|
|
148
154
|
opts.on(Stella::TEXT.msg(:option_help_options_title))
|
149
155
|
opts.on('-V', '--version', Stella::TEXT.msg(:option_help_version)) do
|
@@ -151,8 +157,10 @@ module Stella
|
|
151
157
|
exit 0
|
152
158
|
end
|
153
159
|
opts.on('-h', '--help', Stella::TEXT.msg(:option_help_help)) { puts opts; exit 0 }
|
160
|
+
opts.on('-F', '--force', Stella::TEXT.msg(:option_help_force)) { |v| @options.force = true }
|
154
161
|
|
155
162
|
opts.on('-v', '--verbose', Stella::TEXT.msg(:option_help_verbose)) do
|
163
|
+
|
156
164
|
@options.verbose ||= 0
|
157
165
|
@options.verbose += 1
|
158
166
|
end
|
@@ -189,8 +197,8 @@ module Stella
|
|
189
197
|
@options.rampup = amount
|
190
198
|
end
|
191
199
|
|
192
|
-
opts.on('-x', '--
|
193
|
-
@options.
|
200
|
+
opts.on('-x', '--repetitions=N', Integer, Stella::TEXT.msg(:option_help_testreps)) do |v|
|
201
|
+
@options.repetitions = MathUtil.enforce_limit(v,1,99)
|
194
202
|
end
|
195
203
|
|
196
204
|
opts.on('-w [N]', '--warmup', Float, Stella::TEXT.msg(:option_help_warmup, 0.1)) do |v|
|
@@ -282,10 +290,14 @@ end
|
|
282
290
|
|
283
291
|
# Autoload CLI classes. These classes add themselves to the class variable @@commands.
|
284
292
|
begin
|
293
|
+
previous_path = ""
|
285
294
|
cli_classes = Dir.glob(File.join(STELLA_HOME, 'lib', 'stella', 'cli', "*.rb"))
|
286
295
|
cli_classes.each do |path|
|
296
|
+
previous_path = path
|
287
297
|
require path
|
288
298
|
end
|
289
299
|
rescue LoadError => ex
|
290
|
-
Stella::LOGGER.info("Error loading #{
|
291
|
-
end
|
300
|
+
Stella::LOGGER.info("Error loading #{previous_path}: #{ex.message}")
|
301
|
+
end
|
302
|
+
|
303
|
+
|
data/lib/stella/command/base.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
|
3
3
|
module Stella::Command
|
4
4
|
class Base
|
5
5
|
|
@@ -98,14 +98,5 @@ module Stella::Command
|
|
98
98
|
end
|
99
99
|
|
100
100
|
|
101
|
-
#
|
102
|
-
# Generates a string of random alphanumeric characters
|
103
|
-
# These are used as IDs throughout the system
|
104
|
-
def strand( len )
|
105
|
-
chars = ("a".."z").to_a + ("0".."9").to_a
|
106
|
-
newpass = ""
|
107
|
-
1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
|
108
|
-
return newpass
|
109
|
-
end
|
110
101
|
end
|
111
102
|
end
|
@@ -2,9 +2,7 @@
|
|
2
2
|
|
3
3
|
module Stella
|
4
4
|
class LocalTest < Stella::Command::Base
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
|
8
6
|
# A container for the test parameters
|
9
7
|
attr_accessor :testdef
|
10
8
|
# The load tool adapter
|
@@ -33,7 +31,8 @@ module Stella
|
|
33
31
|
@testdef = testdef if testdef
|
34
32
|
@adapter = adapter if adapter
|
35
33
|
|
36
|
-
|
34
|
+
# Disabled until we resolve JRuby on OSX issue (won't load openssl)
|
35
|
+
#@guid = Crypto.sign(rand.to_s, rand.to_s)
|
37
36
|
|
38
37
|
@test_runpaths = []
|
39
38
|
@all_runpaths = []
|
@@ -64,9 +63,9 @@ module Stella
|
|
64
63
|
|
65
64
|
raise UnavailableAdapter.new(@adapter.name) unless @adapter.available?
|
66
65
|
|
66
|
+
# If the adapter isn't being called for a loadtest, we don't have anything to do.
|
67
67
|
unless @adapter.loadtest?
|
68
68
|
system(@adapter.command)
|
69
|
-
|
70
69
|
return
|
71
70
|
end
|
72
71
|
|
@@ -157,6 +156,11 @@ module Stella
|
|
157
156
|
save_summary(File.join(test_path, "SUMMARY-RAMPUP.#{@format}"), test_stats)
|
158
157
|
print_summary(test_stats) if (@testdef.repetitions > 1)
|
159
158
|
end
|
159
|
+
rescue Interrupt
|
160
|
+
puts "HIHIHI2222"
|
161
|
+
exit
|
162
|
+
rescue AdapterError => ex
|
163
|
+
Stella::LOGGER.error(ex.message)
|
160
164
|
end
|
161
165
|
|
162
166
|
def latest_test_symlink_path
|
@@ -181,11 +185,12 @@ module Stella
|
|
181
185
|
# Make sure the test storage directory is created along with the
|
182
186
|
# latest symlink
|
183
187
|
FileUtil.create_dir(test_path)
|
184
|
-
File.unlink(latest_test_symlink_path) if File.exists?(latest_test_symlink_path)
|
185
|
-
File.symlink(File.expand_path(test_path), latest_test_symlink_path)
|
188
|
+
#File.unlink(latest_test_symlink_path) if File.exists?(latest_test_symlink_path)
|
189
|
+
#File.symlink(File.expand_path(test_path), latest_test_symlink_path)
|
186
190
|
|
187
191
|
# Write the test ID to the storage directory
|
188
|
-
|
192
|
+
# NOTE: Disabled until we resolve the issue with JRuby on OSX (won't load openssl)
|
193
|
+
#FileUtil.write_file(test_path + "/ID.txt", @guid, true)
|
189
194
|
|
190
195
|
# And the test run message
|
191
196
|
FileUtil.write_file(test_path + "/MESSAGE.txt", @testdef.message, true) if @testdef.message
|
@@ -241,25 +246,37 @@ module Stella
|
|
241
246
|
FileUtil.write_file(runpath + "/COMMAND.txt", @adapter.command, true)
|
242
247
|
|
243
248
|
# Execute the command, send STDOUT and STDERR to separate files.
|
244
|
-
command = "#{@adapter.command} 1>
|
249
|
+
command = "#{@adapter.command} 1> \"#{@adapter.stdout_path}\" 2> \"#{@adapter.stderr_path}\""
|
245
250
|
Stella::LOGGER.info(" COMMAND: #{command}") if @verbose >= 2
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
251
|
+
|
252
|
+
begin
|
253
|
+
# Call the load tool
|
254
|
+
# $? contains the error status
|
255
|
+
succeeded = system(command)
|
256
|
+
|
257
|
+
# TODO: Catch interrupts for system calls. Currently it will simply and and continue with the next command
|
258
|
+
# i.e. these don't work:
|
259
|
+
rescue Interrupt
|
260
|
+
exit
|
261
|
+
rescue SystemExit
|
262
|
+
exit
|
263
|
+
end
|
264
|
+
|
265
|
+
unless succeeded
|
266
|
+
Stella::LOGGER.info('', '') # We used print so we need a new line for the error message.
|
267
|
+
raise AdapterError.new(@adapter.name, @adapter.error)
|
268
|
+
end
|
269
|
+
|
252
270
|
stats = @adapter.stats
|
253
271
|
|
254
272
|
save_summary(@adapter.summary_path(@format), stats)
|
255
273
|
|
256
274
|
|
257
|
-
|
258
|
-
Stella::LOGGER.info_print(sprintf("%3.0f%% %9.2f/s %8.3fs ", stats.availability, stats.transaction_rate, stats.response_time))
|
259
|
-
Stella::LOGGER.info_print(sprintf("%8.3fMB/s %8.3fMB %8.3fs ", stats.throughput, stats.data_transferred, stats.elapsed_time))
|
275
|
+
if !@quiet && stats && stats.available?
|
276
|
+
Stella::LOGGER.info_print(sprintf("%3.0f%% %9.2f/s %8.3fs ", stats.availability || 0, stats.transaction_rate || 0, stats.response_time || 0))
|
277
|
+
Stella::LOGGER.info_print(sprintf("%8.3fMB/s %8.3fMB %8.3fs ", stats.throughput || 0, stats.data_transferred || 0, stats.elapsed_time || 0))
|
260
278
|
# NOTE: We don't print a line terminator here
|
261
279
|
end
|
262
|
-
|
263
280
|
end
|
264
281
|
end
|
265
282
|
|
@@ -274,10 +291,10 @@ module Stella
|
|
274
291
|
def print_summary(stats)
|
275
292
|
Stella::LOGGER.info(' ' << "-"*67) unless @quiet
|
276
293
|
|
277
|
-
Stella::LOGGER.info_printf("%8s: %10d@%-6d%
|
278
|
-
Stella::LOGGER.info_printf("%8.3fs %8.3fMB/s %8.3fMB %8.3fs", stats.response_time_avg, stats.throughput_avg, stats.data_transferred_total, stats.elapsed_time_total)
|
294
|
+
Stella::LOGGER.info_printf("%8s: %10d@%-6d %3.0f%% %9.2f/s ", "Total", stats.transactions_total || 0, stats.vusers_avg || 0, stats.availability || 0, stats.transaction_rate_avg || 0)
|
295
|
+
Stella::LOGGER.info_printf("%8.3fs %8.3fMB/s %8.3fMB %8.3fs", stats.response_time_avg || 0, stats.throughput_avg || 0, stats.data_transferred_total || 0, stats.elapsed_time_total || 0)
|
279
296
|
Stella::LOGGER.info('') # New line
|
280
|
-
Stella::LOGGER.info_printf("%8s: %22s %9.2f/s %8.3fs %8.3fMB/s %10s %8.3fs", "Std Dev", '', stats.transaction_rate_sdev, stats.response_time_sdev, stats.throughput_sdev, '', stats.elapsed_time_sdev)
|
297
|
+
Stella::LOGGER.info_printf("%8s: %22s %9.2f/s %8.3fs %8.3fMB/s %10s %8.3fs", "Std Dev", '', stats.transaction_rate_sdev || 0, stats.response_time_sdev || 0, stats.throughput_sdev || 0, '', stats.elapsed_time_sdev || 0)
|
281
298
|
Stella::LOGGER.info('') # New line
|
282
299
|
Stella::LOGGER.info('') unless @quiet # Extra new line
|
283
300
|
end
|
@@ -303,16 +320,19 @@ module Stella
|
|
303
320
|
# filepath:: the complete path for the file (string)
|
304
321
|
# stats:: Any object that extends Stella::Test::Base object
|
305
322
|
def save_summary(filepath, stats)
|
306
|
-
FileUtil.write_file(filepath, stats.dump(@format, :with_titles), true)
|
323
|
+
FileUtil.write_file(filepath, stats.dump(@format, :with_titles), true) if stats
|
307
324
|
end
|
308
325
|
|
309
326
|
# Load SUMMARY file for each run and create a summary with
|
310
327
|
# totals, averages, and standard deviations.
|
311
328
|
def process_test_stats(paths)
|
329
|
+
return unless paths && !paths.empty?
|
312
330
|
test_stats = Stella::Test::Summary.new(@message)
|
331
|
+
return unless test_stats
|
313
332
|
all_stats_obj = []
|
314
333
|
paths.each do |path|
|
315
334
|
file_contents = FileUtil.read_file_to_array("#{path}/SUMMARY.#{@format}")
|
335
|
+
next if !file_contents || file_contents.empty?
|
316
336
|
stats = Stella::Test::Run::Summary.undump(@format, file_contents)
|
317
337
|
stats_obj = Stella::Test::Run::Summary.from_hash(stats)
|
318
338
|
test_stats.add_run(stats_obj)
|
@@ -0,0 +1,75 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Stella::Data
|
4
|
+
class DomainRequest < Stella::Storable
|
5
|
+
attr_accessor :time, :client_ip, :server_ip, :dns_data, :domain_name, :header
|
6
|
+
attr_reader :raw_data
|
7
|
+
|
8
|
+
def initialize(raw_data)
|
9
|
+
@raw_data = raw_data
|
10
|
+
@dns_data, @domain_name, @header = DomainUtil::parse_domain_request(@raw_data)
|
11
|
+
end
|
12
|
+
|
13
|
+
def has_request?
|
14
|
+
false
|
15
|
+
end
|
16
|
+
def has_response?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
def has_body?
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
def field_names
|
24
|
+
[ :time, :client_ip, :server_ip, :domain_name, :header ]
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"%s: %s -> %s (%s)" % [@time, @client_ip, @server_ip, @domain_name]
|
29
|
+
end
|
30
|
+
|
31
|
+
def inspect
|
32
|
+
str = "#{$/};; REQUEST #{@time.to_s}"
|
33
|
+
str << "#{$/};; %s %s> %s" % [@client_ip, '-'*30, @server_ip]
|
34
|
+
str << "#{$/};;#{$/}"
|
35
|
+
str << @dns_data.inspect
|
36
|
+
str
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
class DomainResponse < Stella::Storable
|
42
|
+
attr_accessor :time, :client_ip, :server_ip, :dns_data, :domain_name, :header, :addresses, :cnames
|
43
|
+
attr_reader :raw_data
|
44
|
+
|
45
|
+
def initialize(raw_data)
|
46
|
+
@raw_data = raw_data
|
47
|
+
@dns_data, @domain_name, @header, @addresses, @cnames = DomainUtil::parse_domain_response(@raw_data)
|
48
|
+
end
|
49
|
+
|
50
|
+
def has_request?
|
51
|
+
false
|
52
|
+
end
|
53
|
+
def has_response?
|
54
|
+
false
|
55
|
+
end
|
56
|
+
def has_body?
|
57
|
+
false
|
58
|
+
end
|
59
|
+
|
60
|
+
def field_names
|
61
|
+
[ :time, :client_ip, :server_ip, :dns_data, :domain_name, :header, :addresses, :cnames ]
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
"%s: %s <- %s (%s) %s" % [@time, @client_ip, @server_ip, @domain_name, (@addresses || []).join(',')]
|
66
|
+
end
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
str = "#{$/};; RESPONSE #{@time.strftime(NICE_TIME_FORMAT)}"
|
70
|
+
str << "#{$/};; %s <%s %s" % [@client_ip, '-'*30, @server_ip]
|
71
|
+
str << "#{$/};;#{$/}"
|
72
|
+
str << @dns_data.inspect
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|