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