solutious-stella 0.5.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +36 -0
- data/README.textile +162 -0
- data/Rakefile +88 -0
- data/bin/stella +12 -0
- data/bin/stella.bat +12 -0
- data/lib/daemonize.rb +56 -0
- data/lib/pcaplet.rb +180 -0
- data/lib/stella.rb +101 -0
- data/lib/stella/adapter/ab.rb +337 -0
- data/lib/stella/adapter/base.rb +106 -0
- data/lib/stella/adapter/httperf.rb +305 -0
- data/lib/stella/adapter/pcap_watcher.rb +221 -0
- data/lib/stella/adapter/proxy_watcher.rb +76 -0
- data/lib/stella/adapter/siege.rb +341 -0
- data/lib/stella/cli.rb +258 -0
- data/lib/stella/cli/agents.rb +73 -0
- data/lib/stella/cli/base.rb +55 -0
- data/lib/stella/cli/language.rb +18 -0
- data/lib/stella/cli/localtest.rb +78 -0
- data/lib/stella/cli/sysinfo.rb +16 -0
- data/lib/stella/cli/watch.rb +278 -0
- data/lib/stella/command/base.rb +40 -0
- data/lib/stella/command/localtest.rb +358 -0
- data/lib/stella/data/domain.rb +82 -0
- data/lib/stella/data/http.rb +131 -0
- data/lib/stella/logger.rb +84 -0
- data/lib/stella/response.rb +85 -0
- data/lib/stella/storable.rb +201 -0
- data/lib/stella/support.rb +276 -0
- data/lib/stella/sysinfo.rb +257 -0
- data/lib/stella/test/definition.rb +79 -0
- data/lib/stella/test/run/summary.rb +70 -0
- data/lib/stella/test/stats.rb +114 -0
- data/lib/stella/text.rb +64 -0
- data/lib/stella/text/resource.rb +38 -0
- data/lib/utils/crypto-key.rb +84 -0
- data/lib/utils/domainutil.rb +47 -0
- data/lib/utils/escape.rb +302 -0
- data/lib/utils/fileutil.rb +78 -0
- data/lib/utils/httputil.rb +266 -0
- data/lib/utils/mathutil.rb +15 -0
- data/lib/utils/stats.rb +88 -0
- data/lib/utils/textgraph.rb +267 -0
- data/lib/utils/timerutil.rb +58 -0
- data/lib/win32/Console.rb +970 -0
- data/lib/win32/Console/ANSI.rb +305 -0
- data/support/kvm.h +91 -0
- data/support/ruby-pcap-takuma-notes.txt +19 -0
- data/support/ruby-pcap-takuma-patch.txt +30 -0
- data/support/text/en.yaml +80 -0
- data/support/text/nl.yaml +7 -0
- data/support/useragents.txt +75 -0
- data/tests/01-util_test.rb +0 -0
- data/tests/02-stella-util_test.rb +42 -0
- data/tests/10-stella_test.rb +104 -0
- data/tests/11-stella-storable_test.rb +68 -0
- data/tests/60-stella-command_test.rb +248 -0
- data/tests/80-stella-cli_test.rb +45 -0
- data/tests/spec-helper.rb +31 -0
- metadata +165 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Stella
|
4
|
+
|
5
|
+
|
6
|
+
module Test
|
7
|
+
|
8
|
+
# Stella::Test::Definition
|
9
|
+
#
|
10
|
+
# This class defines the properties of load test. These are "generic" properties
|
11
|
+
# in that they don't relate to a specific tool.
|
12
|
+
class Definition
|
13
|
+
|
14
|
+
# Stella::Test::Definition::Rampup
|
15
|
+
#
|
16
|
+
# This class holds the values for a rampup: interval and ceiling.
|
17
|
+
class Rampup
|
18
|
+
attr_accessor :interval
|
19
|
+
attr_accessor :ceiling
|
20
|
+
def initialize(interval, ceiling)
|
21
|
+
@interval = interval
|
22
|
+
@ceiling = ceiling
|
23
|
+
end
|
24
|
+
def to_s
|
25
|
+
to_a.join(',')
|
26
|
+
end
|
27
|
+
def to_a
|
28
|
+
[interval,ceiling]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Number of virtual users to create or to begin with if rampup is specific.
|
33
|
+
attr_accessor :vusers
|
34
|
+
# The total number of requests per test
|
35
|
+
attr_accessor :requests
|
36
|
+
# The number of requests per virtual user
|
37
|
+
attr_accessor :request_ratio
|
38
|
+
|
39
|
+
# Number of times to repeat the test run. Integer.
|
40
|
+
attr_reader :repetitions
|
41
|
+
# The amount of time to pause between test runs
|
42
|
+
attr_accessor :sleep
|
43
|
+
# Warmup factor (0.1 - 1) for a single test run before the actual test.
|
44
|
+
# A warmup factor of 0.5 means run a test run at 50% strength.
|
45
|
+
attr_accessor :warmup
|
46
|
+
# Contains an interval and maximum threshold to increase virtual users.
|
47
|
+
# Rampup object, or Array like [R,M] where R is the interval and M is the maximum.
|
48
|
+
attr_reader :rampup
|
49
|
+
# An array of string appropriate for a User-Agent HTTP header
|
50
|
+
attr_accessor :agents
|
51
|
+
# A short reminder to yourself what you're testing
|
52
|
+
attr_accessor :message
|
53
|
+
|
54
|
+
def initialize
|
55
|
+
@repetitions = 3
|
56
|
+
end
|
57
|
+
|
58
|
+
def repetitions=(v)
|
59
|
+
return unless v && v > 0
|
60
|
+
@repetitions = v
|
61
|
+
end
|
62
|
+
|
63
|
+
def rampup=(v)
|
64
|
+
return unless v
|
65
|
+
|
66
|
+
if (v.is_a? Rampup)
|
67
|
+
@rampup = v
|
68
|
+
elsif (v.is_a?(Array) && v.size == 2)
|
69
|
+
@rampup = Rampup.new(v[0], v[1])
|
70
|
+
else
|
71
|
+
raise UnknownValue.new(v.to_s)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
module Stella::Test::Run
|
5
|
+
|
6
|
+
class Summary < Stella::Storable
|
7
|
+
attr_accessor :format
|
8
|
+
|
9
|
+
field :availability => Float
|
10
|
+
field :transactions => Integer
|
11
|
+
field :elapsed_time => Float
|
12
|
+
field :data_transferred => Float
|
13
|
+
field :headers_transferred => Float
|
14
|
+
field :response_time => Float
|
15
|
+
field :transaction_rate => Float
|
16
|
+
field :throughput => Float
|
17
|
+
field :vusers => Integer
|
18
|
+
field :successful => Integer
|
19
|
+
field :failed => Integer
|
20
|
+
field :note => String
|
21
|
+
field :raw => String
|
22
|
+
field :tool => String
|
23
|
+
field :version => String
|
24
|
+
|
25
|
+
def initialize(note="")
|
26
|
+
#init
|
27
|
+
@note = note
|
28
|
+
reset
|
29
|
+
end
|
30
|
+
|
31
|
+
def reset
|
32
|
+
@transactions = 0
|
33
|
+
@headers_transferred = 0
|
34
|
+
@elapsed_time = 0
|
35
|
+
@data_transferred = 0
|
36
|
+
@response_time = 0
|
37
|
+
@successful = 0
|
38
|
+
@failed = 0
|
39
|
+
@transaction_rate = 0
|
40
|
+
@vusers = 0
|
41
|
+
end
|
42
|
+
|
43
|
+
def availability
|
44
|
+
begin
|
45
|
+
(@transactions / @successful).to_f * 100
|
46
|
+
rescue => ex
|
47
|
+
return 0.0
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# We calculate the throughput because Apache Bench does not provide this
|
52
|
+
# value in the output.
|
53
|
+
def throughput
|
54
|
+
begin
|
55
|
+
return (@data_transferred / @elapsed_time).to_f
|
56
|
+
rescue => ex
|
57
|
+
return 0.0
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
def available?
|
64
|
+
@successful && @transactions && @elapsed_time && @vusers && @response_time
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Stella::Test
|
4
|
+
|
5
|
+
# Stella::Test::Stats
|
6
|
+
class Stats < Stella::Storable
|
7
|
+
|
8
|
+
field :elapsed_time_avg => Float
|
9
|
+
field :transaction_rate_avg => Float
|
10
|
+
field :vusers_avg => Float
|
11
|
+
field :response_time_avg => Float
|
12
|
+
|
13
|
+
field :elapsed_time_sdev => Float
|
14
|
+
field :transaction_rate_sdev => Float
|
15
|
+
field :vusers_sdev => Float
|
16
|
+
field :response_time_sdev => Float
|
17
|
+
|
18
|
+
|
19
|
+
field :transactions_total => Float
|
20
|
+
field :successful_total => Float
|
21
|
+
field :failed_total => Float
|
22
|
+
|
23
|
+
field :data_transferred_total => Float
|
24
|
+
field :headers_transferred_total => Float
|
25
|
+
|
26
|
+
|
27
|
+
field :elapsed_time_total => Float
|
28
|
+
field :availability => Float
|
29
|
+
field :throughput_avg => Float
|
30
|
+
field :throughput_sdev => Float
|
31
|
+
|
32
|
+
def availability
|
33
|
+
return 0 if @successful_total == 0
|
34
|
+
begin
|
35
|
+
(@transactions_total / @successful_total).to_f * 100
|
36
|
+
rescue => ex
|
37
|
+
0.0
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
attr_reader :runs
|
43
|
+
|
44
|
+
def initialize(msg="")
|
45
|
+
@message = msg
|
46
|
+
@runs = []
|
47
|
+
end
|
48
|
+
|
49
|
+
# Add a TestRun object to the list
|
50
|
+
def add_run(run)
|
51
|
+
raise "I got a #{run.class} but I wanted a Run::Summary" unless run.is_a?(Run::Summary)
|
52
|
+
@runs << run
|
53
|
+
calculate
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def calculate
|
58
|
+
# We simply keep a running tally of these
|
59
|
+
@transactions_total = 0
|
60
|
+
@headers_transferred_total = 0
|
61
|
+
@data_transferred_total = 0
|
62
|
+
@elapsed_time_total = 0
|
63
|
+
@successful_total = 0
|
64
|
+
@failed_total = 0
|
65
|
+
|
66
|
+
# We keep a list of the values for averaging and std dev
|
67
|
+
elapsed_times = ::Stats.new
|
68
|
+
transaction_rates = ::Stats.new
|
69
|
+
vusers_list = ::Stats.new
|
70
|
+
response_times = ::Stats.new
|
71
|
+
response_time = ::Stats.new
|
72
|
+
transaction_rate = ::Stats.new
|
73
|
+
throughput = ::Stats.new
|
74
|
+
vusers = ::Stats.new
|
75
|
+
|
76
|
+
# Each run is the summary of a single run (i.e. run01/SUMMARY.csv)
|
77
|
+
runs.each do |run|
|
78
|
+
# These are totaled
|
79
|
+
@transactions_total += run.transactions || 0
|
80
|
+
@headers_transferred_total += run.headers_transferred || 0
|
81
|
+
@data_transferred_total += run.data_transferred || 0
|
82
|
+
@successful_total += run.successful || 0
|
83
|
+
@failed_total += run.failed || 0
|
84
|
+
@elapsed_time_total += run.elapsed_time || 0
|
85
|
+
|
86
|
+
# These are used for standard deviation
|
87
|
+
elapsed_times.sample(run.elapsed_time)
|
88
|
+
transaction_rates.sample(run.transaction_rate)
|
89
|
+
vusers_list.sample(run.vusers)
|
90
|
+
response_times.sample(run.response_time)
|
91
|
+
throughput.sample(run.throughput)
|
92
|
+
response_time.sample(run.response_time)
|
93
|
+
transaction_rate.sample(run.transaction_rate)
|
94
|
+
vusers.sample(run.vusers)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Calculate Averages
|
98
|
+
@elapsed_time_avg = elapsed_times.mean
|
99
|
+
@throughput_avg = throughput.mean
|
100
|
+
@response_time_avg = response_time.mean
|
101
|
+
@transaction_rate_avg = transaction_rate.mean
|
102
|
+
@vusers_avg = vusers.mean
|
103
|
+
|
104
|
+
# Calculate Standard Deviations
|
105
|
+
@elapsed_time_sdev = elapsed_times.sd
|
106
|
+
@throughput_sdev= throughput.sd
|
107
|
+
@transaction_rate_sdev = transaction_rates.sd
|
108
|
+
@vusers_sdev = vusers_list.sd
|
109
|
+
@response_time_sdev = response_times.sd
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
data/lib/stella/text.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
|
2
|
+
module Stella
|
3
|
+
|
4
|
+
# Stella::Text
|
5
|
+
#
|
6
|
+
# This is the API for retrieving interface text in Stella. The intended use
|
7
|
+
# is to have a single instance of this class although there's nothing stopping
|
8
|
+
# you (or anyone else!) from having as many instances as you see fit.
|
9
|
+
# Currently only yaml files are supported.
|
10
|
+
class Text
|
11
|
+
require 'stella/text/resource'
|
12
|
+
|
13
|
+
DEFAULT_LANGUAGE = 'en'.freeze unless defined? LANGUAGE
|
14
|
+
MESSAGE_NOT_DEFINED = "The message %s is not defined"
|
15
|
+
RESOURCES_PATH = File.join(STELLA_HOME, "support", "text").freeze unless defined? RESOURCES_PATH
|
16
|
+
|
17
|
+
attr_reader :resource, :lang
|
18
|
+
|
19
|
+
def initialize(language=nil)
|
20
|
+
@lang = determine_language(language)
|
21
|
+
@resource = Resource.new(RESOURCES_PATH, @lang)
|
22
|
+
@available_languages = []
|
23
|
+
end
|
24
|
+
|
25
|
+
def determine_language(language)
|
26
|
+
return language if Text.supported_language?(language)
|
27
|
+
Stella::LOGGER.info("There's no translation for '#{language}' yet. Maybe you can help? stella@solutious.com") if language
|
28
|
+
language = (ENV['STELLA_LANG'] || ENV['LOCALE'] || '').split('_')[0]
|
29
|
+
Text.supported_language?(language) ? language : DEFAULT_LANGUAGE
|
30
|
+
end
|
31
|
+
|
32
|
+
def msg(txtsym, *vars)
|
33
|
+
return self.parse(MESSAGE_NOT_DEFINED, txtsym) unless @resource.message.has_key?(txtsym)
|
34
|
+
parse(@resource.message[txtsym], vars)
|
35
|
+
end
|
36
|
+
|
37
|
+
def err(txtsym, *vars)
|
38
|
+
return self.parse(MESSAGE_NOT_DEFINED, txtsym) unless @resource.error.has_key?(txtsym)
|
39
|
+
parse(@resource.error[txtsym], vars)
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse(text, vars)
|
43
|
+
sprintf(text, *vars)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.supported_language?(language)
|
47
|
+
return File.exists?(File.join(RESOURCES_PATH, "#{language}.yaml"))
|
48
|
+
end
|
49
|
+
|
50
|
+
def available_languages
|
51
|
+
#return @available_languages unless @available_languages.empty?
|
52
|
+
translations = Dir.glob(File.join(RESOURCES_PATH, "*.yaml"))
|
53
|
+
translations.each do |path|
|
54
|
+
trans = YAML.load_file(path)
|
55
|
+
next if !trans || trans.empty? || !trans[:info] || !trans[:info][:enabled]
|
56
|
+
@available_languages << trans[:info]
|
57
|
+
end
|
58
|
+
@available_languages
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Stella
|
4
|
+
|
5
|
+
class Text
|
6
|
+
class Resource
|
7
|
+
require 'yaml'
|
8
|
+
|
9
|
+
attr_reader :lang, :country, :encoding
|
10
|
+
|
11
|
+
def initialize(path, lang)
|
12
|
+
@path = path
|
13
|
+
@lang = lang
|
14
|
+
@messages = {}
|
15
|
+
load_resource
|
16
|
+
end
|
17
|
+
|
18
|
+
def path
|
19
|
+
File.join(@path, "#{@lang}.yaml")
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_resource
|
23
|
+
return @messages unless @messages.empty?
|
24
|
+
#Stella::LOGGER.debug("LOADING #{path}")
|
25
|
+
raise UnsupportedLanguage unless File.exists?(path)
|
26
|
+
@messages = YAML.load_file(path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def messages
|
30
|
+
@messages
|
31
|
+
end
|
32
|
+
alias :message :messages
|
33
|
+
alias :error :messages
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# based on: http://blog.leetsoft.com/2006/03/14/simple-encryption
|
2
|
+
|
3
|
+
|
4
|
+
require 'base64' # Added, to fix called to Base64
|
5
|
+
|
6
|
+
# IF NOT JRUBY
|
7
|
+
require 'openssl'
|
8
|
+
# ELSE
|
9
|
+
#module JRuby
|
10
|
+
# module OpenSSL
|
11
|
+
# GEM_ONLY = false unless defined?(GEM_ONLY)
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# if JRuby::OpenSSL::GEM_ONLY
|
16
|
+
# require 'jruby/openssl/gem'
|
17
|
+
# else
|
18
|
+
# module OpenSSL
|
19
|
+
# class OpenSSLError < StandardError; end
|
20
|
+
# # These require the gem
|
21
|
+
# %w[
|
22
|
+
# ASN1
|
23
|
+
# BN
|
24
|
+
# Cipher
|
25
|
+
# Config
|
26
|
+
# Netscape
|
27
|
+
# PKCS7
|
28
|
+
# PKey
|
29
|
+
# Random
|
30
|
+
# SSL
|
31
|
+
# X509
|
32
|
+
# ].each {|c| autoload c, "jruby/openssl/gem"}
|
33
|
+
# end
|
34
|
+
# require "jruby/openssl/builtin"
|
35
|
+
# end
|
36
|
+
#end
|
37
|
+
|
38
|
+
module Crypto
|
39
|
+
VERSION = 1.0
|
40
|
+
|
41
|
+
def self.create_keys(bits = 2048)
|
42
|
+
pk = OpenSSL::PKey::RSA.new(bits)
|
43
|
+
end
|
44
|
+
|
45
|
+
@@digest = OpenSSL::Digest::Digest.new("sha1")
|
46
|
+
def self.sign(secret, string)
|
47
|
+
sig = OpenSSL::HMAC.hexdigest(@@digest, secret, string).strip
|
48
|
+
#sig.gsub(/\+/, "%2b")
|
49
|
+
end
|
50
|
+
def self.aws_sign(secret, string)
|
51
|
+
Base64.encode64(self.sign(secret, string)).strip
|
52
|
+
end
|
53
|
+
|
54
|
+
class Key
|
55
|
+
attr_reader :data, :key
|
56
|
+
|
57
|
+
def initialize(data)
|
58
|
+
@data = data
|
59
|
+
@public = (data =~ /^-----BEGIN (RSA|DSA) PRIVATE KEY-----$/).nil?
|
60
|
+
@key = OpenSSL::PKey::RSA.new(@data)
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def self.from_file(filename)
|
65
|
+
self.new File.read( filename )
|
66
|
+
end
|
67
|
+
|
68
|
+
def encrypt(text)
|
69
|
+
Base64.encode64(@key.send("#{type}_encrypt", text))
|
70
|
+
end
|
71
|
+
|
72
|
+
def decrypt(text)
|
73
|
+
@key.send("#{type}_decrypt", Base64.decode64(text))
|
74
|
+
end
|
75
|
+
|
76
|
+
def private?() !@public; end # Added () and ;
|
77
|
+
|
78
|
+
def public?() @public; end # Added () and ;
|
79
|
+
|
80
|
+
def type
|
81
|
+
@public ? :public : :private
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|