solutious-stella 0.5.5 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +39 -2
- data/LICENSE.txt +19 -0
- data/README.rdoc +85 -0
- data/Rakefile +54 -59
- data/bin/example_test.rb +82 -0
- data/bin/example_webapp.rb +63 -0
- data/lib/{stella/logger.rb → logger.rb} +6 -11
- data/lib/stella.rb +76 -58
- data/lib/stella/clients.rb +161 -0
- data/lib/stella/command/base.rb +4 -24
- data/lib/stella/command/form.rb +36 -0
- data/lib/stella/command/get.rb +44 -0
- data/lib/stella/common.rb +53 -0
- data/lib/stella/crypto.rb +88 -0
- data/lib/stella/data/domain.rb +2 -2
- data/lib/stella/data/http.rb +164 -36
- data/lib/stella/environment.rb +66 -0
- data/lib/stella/functest.rb +105 -0
- data/lib/stella/loadtest.rb +186 -0
- data/lib/{utils → stella}/stats.rb +16 -20
- data/lib/stella/testplan.rb +237 -0
- data/lib/stella/testrunner.rb +64 -0
- data/lib/storable.rb +280 -0
- data/lib/threadify.rb +171 -0
- data/lib/timeunits.rb +65 -0
- data/lib/util/httputil.rb +266 -0
- data/stella.gemspec +69 -0
- data/tryouts/drb/drb_test.rb +65 -0
- data/tryouts/drb/open4.rb +19 -0
- data/tryouts/drb/slave.rb +27 -0
- data/tryouts/oo_tryout.rb +30 -0
- metadata +39 -107
- data/README.textile +0 -162
- data/bin/stella +0 -12
- data/bin/stella.bat +0 -12
- data/lib/daemonize.rb +0 -56
- data/lib/pcaplet.rb +0 -180
- data/lib/stella/adapter/ab.rb +0 -337
- data/lib/stella/adapter/base.rb +0 -106
- data/lib/stella/adapter/httperf.rb +0 -305
- data/lib/stella/adapter/pcap_watcher.rb +0 -221
- data/lib/stella/adapter/proxy_watcher.rb +0 -76
- data/lib/stella/adapter/siege.rb +0 -341
- data/lib/stella/cli.rb +0 -258
- data/lib/stella/cli/agents.rb +0 -73
- data/lib/stella/cli/base.rb +0 -55
- data/lib/stella/cli/language.rb +0 -18
- data/lib/stella/cli/localtest.rb +0 -78
- data/lib/stella/cli/sysinfo.rb +0 -16
- data/lib/stella/cli/watch.rb +0 -278
- data/lib/stella/command/localtest.rb +0 -358
- data/lib/stella/response.rb +0 -85
- data/lib/stella/storable.rb +0 -201
- data/lib/stella/support.rb +0 -276
- data/lib/stella/sysinfo.rb +0 -257
- data/lib/stella/test/definition.rb +0 -79
- data/lib/stella/test/run/summary.rb +0 -70
- data/lib/stella/test/stats.rb +0 -114
- data/lib/stella/text.rb +0 -64
- data/lib/stella/text/resource.rb +0 -38
- data/lib/utils/crypto-key.rb +0 -84
- data/lib/utils/domainutil.rb +0 -47
- data/lib/utils/escape.rb +0 -302
- data/lib/utils/fileutil.rb +0 -78
- data/lib/utils/httputil.rb +0 -266
- data/lib/utils/mathutil.rb +0 -15
- data/lib/utils/textgraph.rb +0 -267
- data/lib/utils/timerutil.rb +0 -58
- data/lib/win32/Console.rb +0 -970
- data/lib/win32/Console/ANSI.rb +0 -305
- data/support/kvm.h +0 -91
- data/support/ruby-pcap-takuma-notes.txt +0 -19
- data/support/ruby-pcap-takuma-patch.txt +0 -30
- data/support/text/en.yaml +0 -80
- data/support/text/nl.yaml +0 -7
- data/support/useragents.txt +0 -75
- data/tests/01-util_test.rb +0 -0
- data/tests/02-stella-util_test.rb +0 -42
- data/tests/10-stella_test.rb +0 -104
- data/tests/11-stella-storable_test.rb +0 -68
- data/tests/60-stella-command_test.rb +0 -248
- data/tests/80-stella-cli_test.rb +0 -45
- data/tests/spec-helper.rb +0 -31
data/bin/stella.bat
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
@echo off
|
2
|
-
|
3
|
-
rem Check for funkiness when called from another batch script.
|
4
|
-
rem We want FULL_PATH to contain the full path to the stella bin directory.
|
5
|
-
IF EXIST "%~dp0stella.bat" (set FULL_PATH=%~dp0) ELSE (set FULL_PATH=%~dp$PATH:0)
|
6
|
-
|
7
|
-
rem Check for JRuby, otherwise use Ruby.
|
8
|
-
rem We want EXECUTABLE to contain either "jruby" or "ruby"
|
9
|
-
IF EXIST "%JRUBY_HOME%" (set EXECUTABLE=jruby) ELSE (set EXECUTABLE=ruby)
|
10
|
-
|
11
|
-
rem Call the Ruby script, passing it all the arguments.
|
12
|
-
@%EXECUTABLE% %FULL_PATH%stella %*
|
data/lib/daemonize.rb
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
module Daemonize
|
2
|
-
VERSION = "0.1.2"
|
3
|
-
|
4
|
-
# Try to fork if at all possible retrying every 5 sec if the
|
5
|
-
# maximum process limit for the system has been reached
|
6
|
-
def safefork
|
7
|
-
tryagain = true
|
8
|
-
|
9
|
-
while tryagain
|
10
|
-
tryagain = false
|
11
|
-
begin
|
12
|
-
if pid = fork
|
13
|
-
return pid
|
14
|
-
end
|
15
|
-
rescue Errno::EWOULDBLOCK
|
16
|
-
sleep 5
|
17
|
-
tryagain = true
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# This method causes the current running process to become a daemon
|
23
|
-
# If closefd is true, all existing file descriptors are closed
|
24
|
-
def daemonize(oldmode=0, closefd=false)
|
25
|
-
srand # Split rand streams between spawning and daemonized process
|
26
|
-
safefork and exit # Fork and exit from the parent
|
27
|
-
|
28
|
-
# Detach from the controlling terminal
|
29
|
-
unless sess_id = Process.setsid
|
30
|
-
raise 'Cannot detach from controlled terminal'
|
31
|
-
end
|
32
|
-
|
33
|
-
# Prevent the possibility of acquiring a controlling terminal
|
34
|
-
if oldmode.zero?
|
35
|
-
trap 'SIGHUP', 'IGNORE'
|
36
|
-
exit if pid = safefork
|
37
|
-
end
|
38
|
-
|
39
|
-
Dir.chdir "/" # Release old working directory
|
40
|
-
File.umask 0000 # Insure sensible umask
|
41
|
-
|
42
|
-
if closefd
|
43
|
-
# Make sure all file descriptors are closed
|
44
|
-
ObjectSpace.each_object(IO) do |io|
|
45
|
-
unless [STDIN, STDOUT, STDERR].include?(io)
|
46
|
-
io.close rescue nil
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
STDIN.reopen "/dev/null" # Free file descriptors and
|
52
|
-
STDOUT.reopen "/dev/null", "a" # point them somewhere sensible
|
53
|
-
STDERR.reopen STDOUT # STDOUT/STDERR should go to a logfile
|
54
|
-
return oldmode ? sess_id : 0 # Return value is mostly irrelevant
|
55
|
-
end
|
56
|
-
end
|
data/lib/pcaplet.rb
DELETED
@@ -1,180 +0,0 @@
|
|
1
|
-
require 'pcap'
|
2
|
-
|
3
|
-
|
4
|
-
# Pcaplet
|
5
|
-
#
|
6
|
-
# Adapted from Ruby portion of Ruby-Pcap:
|
7
|
-
# http://www.goto.info.waseda.ac.jp/~fukusima/ruby/pcap-e.html
|
8
|
-
# The lib/pcaplet.rb and lib/pcap_misc.rb files are in merge into this file
|
9
|
-
# and cleaned up. The Ruby-Pcap C extension was modified to apply several fixes
|
10
|
-
# so it would compile on OS X and to remove several warning messages. With
|
11
|
-
# help from: http://d.hatena.ne.jp/takuma104/20080210/1202638583
|
12
|
-
# We specifically removed the dependency on ARGV and OptParse. It was messy
|
13
|
-
# and required ARGV to be specifically modified before requiring this package.
|
14
|
-
# Manual: http://www.goto.info.waseda.ac.jp/~fukusima/ruby/pcap/doc/index.html
|
15
|
-
class Pcaplet
|
16
|
-
|
17
|
-
attr_accessor :debug, :verbose
|
18
|
-
# do not convert address to name
|
19
|
-
attr_accessor :convert
|
20
|
-
attr_accessor :device, :rfile, :count, :snaplen, :filter
|
21
|
-
|
22
|
-
def initialize(args = {})
|
23
|
-
|
24
|
-
@debug = args[:debug] || false
|
25
|
-
@verbose = args[:verbose] || false
|
26
|
-
@device = args[:device] || guess_device
|
27
|
-
@rfile = args[:rfile]
|
28
|
-
@convert = args[:convert] || false
|
29
|
-
@count = args[:count].to_i || 1000
|
30
|
-
@snaplen = args[:snaplen].to_i > 0 ? args[:snaplen] : 1500
|
31
|
-
@filter = args[:filter] || ''
|
32
|
-
|
33
|
-
Pcap.convert = @convert
|
34
|
-
|
35
|
-
# check option consistency
|
36
|
-
usage(1) if @device && @rfile
|
37
|
-
if !@device and !@rfile
|
38
|
-
@device = Pcap.lookupdev
|
39
|
-
end
|
40
|
-
|
41
|
-
# open
|
42
|
-
begin
|
43
|
-
if @device
|
44
|
-
@capture = Pcap::Capture.open_live(@device, @snaplen)
|
45
|
-
elsif @rfile
|
46
|
-
if @rfile !~ /\.gz$/
|
47
|
-
@capture = Capture.open_offline(@rfile)
|
48
|
-
else
|
49
|
-
$stdin = IO.popen("gzip -dc < #@rfile", 'r')
|
50
|
-
@capture = Capture.open_offline('-')
|
51
|
-
end
|
52
|
-
end
|
53
|
-
@capture.setfilter(@filter) if @filter
|
54
|
-
rescue Pcap::PcapError, ArgumentError
|
55
|
-
$stdout.flush
|
56
|
-
$stderr.puts $!
|
57
|
-
exit(1)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
attr('capture')
|
62
|
-
|
63
|
-
|
64
|
-
def add_filter(f)
|
65
|
-
if @filter == nil || @filter =~ /^\s*$/ # if empty
|
66
|
-
#puts "filter is nil or empty"
|
67
|
-
@filter = f
|
68
|
-
else
|
69
|
-
#puts "filter is #{@filter}"
|
70
|
-
f = f.source if f.is_a? Filter
|
71
|
-
@filter = "( #{@filter} ) and ( #{f} )"
|
72
|
-
end
|
73
|
-
#puts "filter being set #{@filter}"
|
74
|
-
@capture.setfilter(@filter)
|
75
|
-
end
|
76
|
-
|
77
|
-
def each_packet(&block)
|
78
|
-
|
79
|
-
begin
|
80
|
-
duplicated = (RUBY_PLATFORM =~ /linux/ && @device == "lo")
|
81
|
-
unless duplicated
|
82
|
-
@capture.loop(@count, &block)
|
83
|
-
else
|
84
|
-
flip = true
|
85
|
-
@capture.loop(@count) do |pkt|
|
86
|
-
flip = (! flip)
|
87
|
-
next if flip
|
88
|
-
block.call pkt
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
alias each each_packet
|
95
|
-
|
96
|
-
def close
|
97
|
-
@capture.close
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
module Pcap
|
104
|
-
class Packet
|
105
|
-
def to_s
|
106
|
-
'Some packet'
|
107
|
-
end
|
108
|
-
|
109
|
-
def inspect
|
110
|
-
"#<#{type}: #{self}>"
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
class IPPacket
|
115
|
-
def to_s
|
116
|
-
"#{ip_src} > #{ip_dst}"
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
class TCPPacket
|
121
|
-
def tcp_data_len
|
122
|
-
ip_len - 4 * (ip_hlen + tcp_hlen)
|
123
|
-
end
|
124
|
-
|
125
|
-
# The 3-way handshake that initiates a request
|
126
|
-
# ....S. --->
|
127
|
-
# .A..S. <---
|
128
|
-
# .A.... --->
|
129
|
-
#
|
130
|
-
# The teardown procedure at the end of the request
|
131
|
-
# .A...F --->
|
132
|
-
# .A.... <---
|
133
|
-
# .A...F --->
|
134
|
-
# .A.... <...
|
135
|
-
def tcp_flags_s
|
136
|
-
return \
|
137
|
-
(tcp_urg? ? 'U' : '.') + # Urgent
|
138
|
-
(tcp_ack? ? 'A' : '.') + # ACKnowledgement: Successful transfer
|
139
|
-
(tcp_psh? ? 'P' : '.') + # Push
|
140
|
-
(tcp_rst? ? 'R' : '.') + # Reset
|
141
|
-
(tcp_syn? ? 'S' : '.') + # SYNchronization: this is the first segment in a new transaction
|
142
|
-
(tcp_fin? ? 'F' : '.') # FINal: final transaction
|
143
|
-
end
|
144
|
-
|
145
|
-
def to_s
|
146
|
-
"#{src}:#{sport} > #{dst}:#{dport} #{tcp_flags_s}"
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
class UDPPacket
|
151
|
-
def to_s
|
152
|
-
"#{src}:#{sport} > #{dst}:#{dport} len #{udp_len} sum #{udp_sum}"
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
class ICMPPacket
|
157
|
-
def to_s
|
158
|
-
"#{src} > #{dst}: icmp: #{icmp_typestr}"
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
#
|
163
|
-
# Backword compatibility
|
164
|
-
#
|
165
|
-
IpPacket = IPPacket unless defined? IpPacket
|
166
|
-
IpAddress = IPAddress unless defined? IpAddress
|
167
|
-
TcpPacket = TCPPacket unless defined? TcpPacket
|
168
|
-
UdpPacket = UDPPacket unless defined? UdpPacket
|
169
|
-
|
170
|
-
end
|
171
|
-
|
172
|
-
class Time
|
173
|
-
# tcpdump style format
|
174
|
-
def tcpdump
|
175
|
-
sprintf "%0.2d:%0.2d:%0.2d.%0.6d", hour, min, sec, tv_usec
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
|
180
|
-
|
data/lib/stella/adapter/ab.rb
DELETED
@@ -1,337 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'fileutils'
|
3
|
-
|
4
|
-
module Stella
|
5
|
-
module Adapter
|
6
|
-
|
7
|
-
#Usage: ab [options] [http[s]://]hostname[:port]/path
|
8
|
-
#Options are:
|
9
|
-
# -n requests Number of requests to perform
|
10
|
-
# -c concurrency Number of multiple requests to make
|
11
|
-
# -t timelimit Seconds to max. wait for responses
|
12
|
-
# -b windowsize Size of TCP send/receive buffer, in bytes
|
13
|
-
# -p postfile File containing data to POST. Remember also to set -T
|
14
|
-
# -T content-type Content-type header for POSTing, eg.
|
15
|
-
# 'application/x-www-form-urlencoded'
|
16
|
-
# Default is 'text/plain'
|
17
|
-
# -v verbosity How much troubleshooting info to print
|
18
|
-
# -w Print out results in HTML tables
|
19
|
-
# -i Use HEAD instead of GET
|
20
|
-
# -x attributes String to insert as table attributes
|
21
|
-
# -y attributes String to insert as tr attributes
|
22
|
-
# -z attributes String to insert as td or th attributes
|
23
|
-
# -C attribute Add cookie, eg. 'Apache=1234. (repeatable)
|
24
|
-
# -H attribute Add Arbitrary header line, eg. 'Accept-Encoding: gzip'
|
25
|
-
# Inserted after all normal header lines. (repeatable)
|
26
|
-
# -A attribute Add Basic WWW Authentication, the attributes
|
27
|
-
# are a colon separated username and password.
|
28
|
-
# -P attribute Add Basic Proxy Authentication, the attributes
|
29
|
-
# are a colon separated username and password.
|
30
|
-
# -X proxy:port Proxyserver and port number to use
|
31
|
-
# -V Print version number and exit
|
32
|
-
# -k Use HTTP KeepAlive feature
|
33
|
-
# -d Do not show percentiles served table.
|
34
|
-
# -S Do not show confidence estimators and warnings.
|
35
|
-
# -g filename Output collected data to gnuplot format file.
|
36
|
-
# -e filename Output CSV file with percentages served
|
37
|
-
# -r Don't exit on socket receive errors.
|
38
|
-
# -h Display usage information (this message)
|
39
|
-
# -Z ciphersuite Specify SSL/TLS cipher suite (See openssl ciphers)
|
40
|
-
# -f protocol Specify SSL/TLS protocol (SSL2, SSL3, TLS1, or ALL)
|
41
|
-
class ApacheBench < Stella::Adapter::Base
|
42
|
-
|
43
|
-
attr_writer :n, :c
|
44
|
-
attr_accessor :t, :b, :p, :T, :v, :w, :i, :x, :z, :y
|
45
|
-
attr_accessor :C, :H, :A, :P, :X, :V, :k, :d, :S, :e, :g, :r, :h, :Z, :f
|
46
|
-
|
47
|
-
def initialize(options={}, arguments=[])
|
48
|
-
@private_variables = ['private_variables', 'name', 'arguments', 'load_factor', 'working_directory']
|
49
|
-
@c = 1
|
50
|
-
@n = 1
|
51
|
-
@name = 'ab'
|
52
|
-
@load_factor = 1
|
53
|
-
|
54
|
-
super(options, arguments)
|
55
|
-
|
56
|
-
end
|
57
|
-
|
58
|
-
def error
|
59
|
-
(File.exists? stderr_path) ? FileUtil.read_file(stderr_path) : "Unknown error"
|
60
|
-
end
|
61
|
-
|
62
|
-
def version
|
63
|
-
vsn = 0
|
64
|
-
Stella::Util.capture_output("#{@name} -V") do |stdout, stderr|
|
65
|
-
stdout.join.scan(/Version (\d+?\.\d+)/) { |v| vsn = v[0] }
|
66
|
-
end
|
67
|
-
vsn
|
68
|
-
end
|
69
|
-
|
70
|
-
def percentiles_file
|
71
|
-
@working_directory + "/ab-percentiles.log"
|
72
|
-
end
|
73
|
-
|
74
|
-
def requests_file
|
75
|
-
@working_directory + "/ab-requests.log"
|
76
|
-
end
|
77
|
-
|
78
|
-
def before
|
79
|
-
@e = percentiles_file if @e.nil?
|
80
|
-
@g = requests_file if @g.nil?
|
81
|
-
end
|
82
|
-
|
83
|
-
def command
|
84
|
-
raise CommandNotReady.new(self.class.to_s) unless ready?
|
85
|
-
|
86
|
-
command = "#{@name} "
|
87
|
-
|
88
|
-
instance_variables.each do |name|
|
89
|
-
canon = name.to_s.tr('@', '') # instance_variables returns '@name'
|
90
|
-
next if @private_variables.member?(canon)
|
91
|
-
|
92
|
-
# It's important that we take the value from the getter method
|
93
|
-
# because it applies the load factor.
|
94
|
-
value = self.send(canon)
|
95
|
-
if (value.is_a? Array)
|
96
|
-
value.each { |el| command << "-#{canon} #{EscapeUtil.shell_single_word(value.to_s)} " }
|
97
|
-
else
|
98
|
-
command << "-#{canon} #{EscapeUtil.shell_single_word(value.to_s)} "
|
99
|
-
end
|
100
|
-
|
101
|
-
end
|
102
|
-
|
103
|
-
command << (@arguments.map { |uri| "#{uri}" }).join(' ') unless @arguments.empty?
|
104
|
-
|
105
|
-
command
|
106
|
-
end
|
107
|
-
# loadtest
|
108
|
-
#
|
109
|
-
# True or false: is the call to ab a load test? If it's a call to help or version or
|
110
|
-
# to display the config this with return false. It's no reason for someone to make this
|
111
|
-
# call through Stella but it's here for goodness sake.
|
112
|
-
def loadtest?
|
113
|
-
!@arguments.empty? # The argument is a URI
|
114
|
-
end
|
115
|
-
def ready?
|
116
|
-
(!self.loadtest?) || (@name && !instance_variables.empty? && !@arguments.empty?)
|
117
|
-
end
|
118
|
-
|
119
|
-
|
120
|
-
def process_arguments(arguments)
|
121
|
-
opts = OptionParser.new
|
122
|
-
|
123
|
-
# TODO: there's no need to use an OpenStruct here. It's confusing b/c we can
|
124
|
-
# use the instance var methods here instead of in Base::options=.
|
125
|
-
|
126
|
-
# TODO: Print a note for w that we don't parse the HTML results
|
127
|
-
%w{v w i V k d S r h}.each do |n|
|
128
|
-
opts.on("-#{n}") do |v| instance_variable_set("@#{n}", true) end
|
129
|
-
end
|
130
|
-
|
131
|
-
%w{e g p T x y z P Z f A}.each do |n|
|
132
|
-
opts.on("-#{n} S", String) do |v| instance_variable_set("@#{n}", v) end
|
133
|
-
end
|
134
|
-
|
135
|
-
%w{c n t b}.each do |n|
|
136
|
-
opts.on("-#{n} S", Integer) do |v| instance_variable_set("@#{n}", v) end
|
137
|
-
end
|
138
|
-
|
139
|
-
opts.on('-H S', String) do |v| @H ||= []; @H << v; end
|
140
|
-
opts.on('-C S', String) do |v| @C ||= []; @C << v; end
|
141
|
-
|
142
|
-
opts.on('-b') do |v|
|
143
|
-
Stella.warn("-b is not an ab option. I'll pretend it's not there.")
|
144
|
-
end
|
145
|
-
|
146
|
-
opts.on('-r N',Integer) do |v|
|
147
|
-
Stella.error("-r is not an ab parameter. You probably want -n.")
|
148
|
-
exit 1
|
149
|
-
end
|
150
|
-
|
151
|
-
# NOTE: parse! removes the options it finds in @arguments. It will leave
|
152
|
-
# all unnamed arguments and throw a fit about unknown ones.
|
153
|
-
opts.parse!(arguments)
|
154
|
-
|
155
|
-
if arguments.empty?
|
156
|
-
Stella.error("You need to provide a URI")
|
157
|
-
exit 1
|
158
|
-
elsif arguments.size > 1
|
159
|
-
Stella.warn("ab can handle only one URI. The others will be ignored.")
|
160
|
-
arguments = arguments.first
|
161
|
-
else
|
162
|
-
# Let's make sure the URI has a path (at least a trailing slash). Otherwise
|
163
|
-
# ab gives a cryptic error.
|
164
|
-
begin
|
165
|
-
uri = URI.parse(arguments.first)
|
166
|
-
if !uri || uri.path.empty?
|
167
|
-
Stella.error("ab requires a trailing slash for #{uri.to_s}")
|
168
|
-
exit 1
|
169
|
-
end
|
170
|
-
rescue => ex
|
171
|
-
Stella.error("Bad URI: #{arguments.first}")
|
172
|
-
exit 1
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
self.arguments = arguments
|
177
|
-
|
178
|
-
rescue OptionParser::InvalidOption => ex
|
179
|
-
# We want to replace this text so we grab just the name of the argument
|
180
|
-
badarg = ex.message.gsub('invalid option: ', '')
|
181
|
-
raise InvalidArgument.new(badarg)
|
182
|
-
end
|
183
|
-
|
184
|
-
def after
|
185
|
-
# We want to maintain copies of all test output, even when the user has
|
186
|
-
# supplied other path names so we'll copy the files from the testrun directory
|
187
|
-
# to the location specified by the user.
|
188
|
-
# NOTE: For tests with more than one test run, the specified files will be
|
189
|
-
# overwritten after each run. Should we force append the run number?
|
190
|
-
[[@e, 'percentiles'], [@g, 'requests']].each do |tuple|
|
191
|
-
if File.expand_path(File.dirname(tuple[0])) != File.expand_path(@working_directory)
|
192
|
-
from = tuple[0]
|
193
|
-
to = @working_directory + "/ab-#{tuple[1]}.log"
|
194
|
-
next unless File.exists?(from)
|
195
|
-
FileUtils.cp(from, to)
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
|
200
|
-
end
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
def add_header(name, value)
|
206
|
-
@H ||= []
|
207
|
-
@H << "#{name}: #{value}"
|
208
|
-
end
|
209
|
-
|
210
|
-
def user_agent=(list=[])
|
211
|
-
return unless list && !list.empty?
|
212
|
-
list = list.to_ary
|
213
|
-
list.each do |agent|
|
214
|
-
add_header("User-Agent", agent)
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
def vusers
|
219
|
-
c || 0
|
220
|
-
end
|
221
|
-
def vusers=(v)
|
222
|
-
ratio = vuser_requests
|
223
|
-
@c = v
|
224
|
-
@n = ratio * @c
|
225
|
-
end
|
226
|
-
def requests
|
227
|
-
n || 0
|
228
|
-
end
|
229
|
-
def requests=(v)
|
230
|
-
@n = v
|
231
|
-
end
|
232
|
-
def vuser_requests
|
233
|
-
ratio = 1
|
234
|
-
# The request ratio tells us how many requests will be
|
235
|
-
# generated per vuser. It helps us later when we need to
|
236
|
-
# warmp up and ramp up.
|
237
|
-
if @n > 0 && @c > 0
|
238
|
-
ratio = (@n.to_f / @c.to_f).to_f
|
239
|
-
# If concurrency isn't set, we'll assume the total number of requests
|
240
|
-
# is intended to be per request
|
241
|
-
elsif @n > 0
|
242
|
-
ratio = @n
|
243
|
-
end
|
244
|
-
ratio
|
245
|
-
end
|
246
|
-
def c
|
247
|
-
(@c * @load_factor).to_i
|
248
|
-
end
|
249
|
-
def n
|
250
|
-
(@n * @load_factor).to_i
|
251
|
-
end
|
252
|
-
|
253
|
-
def hosts
|
254
|
-
hosts = @arguments || []
|
255
|
-
#hosts << get_hosts_from_file
|
256
|
-
hosts = hosts.map{ |h| tmp = URI.parse(h.strip); "#{tmp.host}:#{tmp.port}" }
|
257
|
-
hosts
|
258
|
-
end
|
259
|
-
|
260
|
-
def paths
|
261
|
-
paths = @arguments || []
|
262
|
-
#hosts << get_hosts_from_file
|
263
|
-
paths = paths.map{ |h| tmp = URI.parse(h.strip); "#{tmp.path}?#{tmp.query}" }
|
264
|
-
paths
|
265
|
-
end
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
# Apache bench writes the summary to STDOUT
|
270
|
-
def summary_file
|
271
|
-
File.new(stdout_path) if File.exists?(stdout_path)
|
272
|
-
end
|
273
|
-
|
274
|
-
def summary
|
275
|
-
return unless summary_file
|
276
|
-
raw = {}
|
277
|
-
summary_file.each_line { |l|
|
278
|
-
l.chomp!
|
279
|
-
nvpair = l.split(':')
|
280
|
-
next unless nvpair && nvpair.size == 2
|
281
|
-
n = nvpair[0].strip.tr(' ', '_').downcase[/\w+/]
|
282
|
-
v = nvpair[1].strip[/[\.\d]+/]
|
283
|
-
|
284
|
-
# Apache Bench outputs two fields with the name "Time per request".
|
285
|
-
# We want only the first one so we don't overwrite values.
|
286
|
-
raw[n.to_sym] = v.to_f unless raw.has_key? n.to_sym
|
287
|
-
}
|
288
|
-
|
289
|
-
# Document Path: /
|
290
|
-
# Document Length: 96 bytes
|
291
|
-
#
|
292
|
-
# Concurrency Level: 75
|
293
|
-
# Time taken for tests: 2.001 seconds
|
294
|
-
# Complete requests: 750
|
295
|
-
# Failed requests: 0
|
296
|
-
# Write errors: 0
|
297
|
-
# Total transferred: 174000 bytes
|
298
|
-
# HTML transferred: 72000 bytes
|
299
|
-
# Requests per second: 374.74 [#/sec] (mean)
|
300
|
-
# Time per request: 200.138 [ms] (mean)
|
301
|
-
# Time per request: 2.669 [ms] (mean, across all concurrent requests)
|
302
|
-
# Transfer rate: 84.90 [Kbytes/sec] received
|
303
|
-
|
304
|
-
stats = Stella::Test::Run::Summary.new
|
305
|
-
|
306
|
-
if !raw.empty? && raw.has_key?(:time_taken_for_tests)
|
307
|
-
|
308
|
-
stats.elapsed_time = raw[:time_taken_for_tests]
|
309
|
-
|
310
|
-
# We want this in MB, Apache Bench gives Bytes.
|
311
|
-
stats.data_transferred = ((raw[:html_transferred] || 0) / 1_048_576)
|
312
|
-
|
313
|
-
# total_transferred is header data + response data (html_transfered)
|
314
|
-
stats.headers_transferred = ((raw[:total_transferred] || 0) / 1_048_576) - stats.data_transferred
|
315
|
-
|
316
|
-
# Apache Bench returns ms
|
317
|
-
stats.response_time = (raw[:time_per_request] || 0) / 1000
|
318
|
-
stats.transaction_rate = raw[:requests_per_second]
|
319
|
-
|
320
|
-
stats.vusers = raw[:concurrency_level].to_i
|
321
|
-
stats.successful = raw[:complete_requests].to_i
|
322
|
-
stats.failed = raw[:failed_requests].to_i
|
323
|
-
|
324
|
-
stats.transactions = stats.successful + stats.failed
|
325
|
-
|
326
|
-
#stats.raw = raw if @global_options.debug
|
327
|
-
end
|
328
|
-
|
329
|
-
stats
|
330
|
-
end
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
end
|
335
|
-
|
336
|
-
end
|
337
|
-
end
|