hansel 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +0 -1
- data/VERSION +1 -1
- data/bin/hansel +2 -6
- data/hansel.gemspec +3 -8
- data/lib/arg_parser.rb +106 -63
- data/lib/config.rb +53 -20
- data/lib/csv_formatter.rb +35 -14
- data/lib/hansel.rb +164 -79
- data/lib/octave_formatter.rb +62 -46
- data/lib/yaml_formatter.rb +12 -7
- metadata +14 -18
- data/hansel.rb +0 -0
- data/lib/mutter.rb +0 -16
data/Rakefile
CHANGED
@@ -10,7 +10,6 @@ begin
|
|
10
10
|
gem.email = "paul.mylchreest@mac.com"
|
11
11
|
gem.homepage = "http://github.com/xlymian/hansel"
|
12
12
|
gem.authors = ["Paul Mylchreest"]
|
13
|
-
gem.add_dependency("mutter", ">= 0.5.2")
|
14
13
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
14
|
end
|
16
15
|
Jeweler::GemcutterTasks.new
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.4
|
data/bin/hansel
CHANGED
@@ -11,13 +11,9 @@ class File
|
|
11
11
|
end
|
12
12
|
|
13
13
|
require File.here '/../lib/arg_parser'
|
14
|
-
require File.here '/../lib/mutter'
|
15
14
|
require File.here '/../lib/config'
|
16
15
|
require File.here '/../lib/hansel'
|
17
16
|
|
18
|
-
|
19
|
-
include Mutter
|
20
|
-
|
21
|
-
hansel = Hansel.new(options)
|
17
|
+
options = Hansel::Config.new(ARGV).options
|
22
18
|
exit if options.exit
|
23
|
-
|
19
|
+
Hansel::Hansel.new(options).run
|
data/hansel.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{hansel}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.4"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Paul Mylchreest"]
|
12
|
-
s.date = %q{2010-04-
|
12
|
+
s.date = %q{2010-04-27}
|
13
13
|
s.default_executable = %q{hansel}
|
14
14
|
s.description = %q{Ruby driver for httperf - automated load and performance testing}
|
15
15
|
s.email = %q{paul.mylchreest@mac.com}
|
@@ -27,19 +27,17 @@ Gem::Specification.new do |s|
|
|
27
27
|
"VERSION",
|
28
28
|
"bin/hansel",
|
29
29
|
"hansel.gemspec",
|
30
|
-
"hansel.rb",
|
31
30
|
"lib/arg_parser.rb",
|
32
31
|
"lib/config.rb",
|
33
32
|
"lib/csv_formatter.rb",
|
34
33
|
"lib/hansel.rb",
|
35
|
-
"lib/mutter.rb",
|
36
34
|
"lib/octave_formatter.rb",
|
37
35
|
"lib/yaml_formatter.rb"
|
38
36
|
]
|
39
37
|
s.homepage = %q{http://github.com/xlymian/hansel}
|
40
38
|
s.rdoc_options = ["--charset=UTF-8"]
|
41
39
|
s.require_paths = ["lib"]
|
42
|
-
s.rubygems_version = %q{1.3.
|
40
|
+
s.rubygems_version = %q{1.3.6}
|
43
41
|
s.summary = %q{Ruby driver for httperf - automated load and performance testing}
|
44
42
|
|
45
43
|
if s.respond_to? :specification_version then
|
@@ -47,12 +45,9 @@ Gem::Specification.new do |s|
|
|
47
45
|
s.specification_version = 3
|
48
46
|
|
49
47
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
50
|
-
s.add_runtime_dependency(%q<mutter>, [">= 0.5.2"])
|
51
48
|
else
|
52
|
-
s.add_dependency(%q<mutter>, [">= 0.5.2"])
|
53
49
|
end
|
54
50
|
else
|
55
|
-
s.add_dependency(%q<mutter>, [">= 0.5.2"])
|
56
51
|
end
|
57
52
|
end
|
58
53
|
|
data/lib/arg_parser.rb
CHANGED
@@ -1,101 +1,144 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
#
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
options
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
1
|
+
module Hansel
|
2
|
+
#
|
3
|
+
# Parse the command configuration file options and command line arguments.
|
4
|
+
# Command line arguments override those from the configuration file
|
5
|
+
# See http://www.ruby-doc.org/stdlib/libdoc/optparse/rdoc/classes/OptionParser.html
|
6
|
+
#
|
7
|
+
class ArgParser
|
8
|
+
#
|
9
|
+
# Setup default values for the parsed options
|
10
|
+
#
|
11
|
+
def initialize args
|
12
|
+
@args = args
|
13
|
+
@options = OpenStruct.new(
|
14
|
+
:verbose => false,
|
15
|
+
:server => 'localhost',
|
16
|
+
:port => '80',
|
17
|
+
:uri => '/',
|
18
|
+
:num_conns => 1,
|
19
|
+
:rate => 1,
|
20
|
+
:cookie => nil,
|
21
|
+
:low_rate => 1,
|
22
|
+
:high_rate => 2,
|
23
|
+
:rate_step => 1,
|
24
|
+
:output_format => :yaml,
|
25
|
+
:output => nil,
|
26
|
+
:output_dir => File.join(ENV['HOME'], 'hansel_output'),
|
27
|
+
:exit => false
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def server options
|
32
|
+
options.on("-s", "--server=S",
|
30
33
|
"Specifies the IP hostname of the server.") do |opt|
|
31
|
-
options.server = opt
|
34
|
+
@options.server = opt
|
32
35
|
end
|
36
|
+
end
|
33
37
|
|
34
|
-
|
35
|
-
|
36
|
-
|
38
|
+
def port options
|
39
|
+
options.on("-p", "--port=N",
|
40
|
+
"This option specifies the port number N on which the web server is listening for HTTP requests.") do |opt|
|
41
|
+
@options.port = opt
|
37
42
|
end
|
43
|
+
end
|
38
44
|
|
39
|
-
|
45
|
+
def uri options
|
46
|
+
options.on("-u", "--uri=S",
|
40
47
|
"Specifies that URI S should be accessed on the server.") do |opt|
|
41
|
-
options.uri = opt
|
48
|
+
@options.uri = opt
|
42
49
|
end
|
50
|
+
end
|
43
51
|
|
44
|
-
|
52
|
+
def connections options
|
53
|
+
options.on("-n", "--num_conns=N",
|
45
54
|
"Specifies the total number of connections to create.") do |opt|
|
46
|
-
options.num_conns = opt
|
55
|
+
@options.num_conns = opt
|
47
56
|
end
|
57
|
+
end
|
48
58
|
|
49
|
-
|
59
|
+
def low_rate options
|
60
|
+
options.on("-l", "--low_rate=S",
|
50
61
|
"Specifies the starting fixed rate at which connections are created.") do |opt|
|
51
|
-
options.low_rate = opt
|
62
|
+
@options.low_rate = opt
|
52
63
|
end
|
64
|
+
end
|
53
65
|
|
54
|
-
|
66
|
+
def high_rate options
|
67
|
+
options.on("-l", "--high_rate=S",
|
55
68
|
"Specifies the ending fixed rate at which connections are created.") do |opt|
|
56
|
-
options.high_rate = opt
|
69
|
+
@options.high_rate = opt
|
57
70
|
end
|
71
|
+
end
|
58
72
|
|
59
|
-
|
73
|
+
def rate_step options
|
74
|
+
options.on("-l", "--rate_step=S",
|
60
75
|
"Specifies the fixed rate step at which connections are created.") do |opt|
|
61
|
-
options.rate_step = opt
|
76
|
+
@options.rate_step = opt
|
62
77
|
end
|
78
|
+
end
|
63
79
|
|
64
|
-
|
65
|
-
|
80
|
+
def cookie options
|
81
|
+
options.on("-c", "--cookie=C", "Specify a cookie.") do |opt|
|
82
|
+
@options.cookie = opt
|
66
83
|
end
|
84
|
+
end
|
67
85
|
|
68
|
-
|
69
|
-
|
86
|
+
def format options
|
87
|
+
options.on("-f", "--format=FILE [yaml|csv]", "Specify an output format.") do |opt|
|
88
|
+
@options.output_format = opt.to_sym
|
70
89
|
end
|
90
|
+
end
|
71
91
|
|
72
|
-
|
73
|
-
|
92
|
+
def output options
|
93
|
+
options.on("-o", "--output=FILE", "Specify an output file.") do |opt|
|
94
|
+
@options.output = opt
|
74
95
|
end
|
96
|
+
end
|
75
97
|
|
76
|
-
|
77
|
-
|
98
|
+
def output_dir options
|
99
|
+
options.on("-d", "--output_dir=PATH", "Specify an output directory.") do |opt|
|
100
|
+
@options.output_dir = opt
|
78
101
|
end
|
102
|
+
end
|
79
103
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
opts.on("-v", "--[no-]verbose", "Run verbosely") do |opt|
|
84
|
-
options.verbose = opt
|
104
|
+
def verbose options
|
105
|
+
options.on("-v", "--[no-]verbose", "Run verbosely") do |opt|
|
106
|
+
@options.verbose = opt
|
85
107
|
end
|
108
|
+
end
|
86
109
|
|
87
|
-
|
88
|
-
|
89
|
-
options
|
110
|
+
def help options
|
111
|
+
options.on_tail("-h", "--help", "Show this message") do
|
112
|
+
puts options
|
113
|
+
@options.exit = true
|
90
114
|
end
|
115
|
+
end
|
91
116
|
|
92
|
-
|
117
|
+
def version options
|
118
|
+
options.on_tail("--version", "Show version") do
|
93
119
|
puts "Hansel version #{IO.foreach('VERSION').first.strip}"
|
94
|
-
options.exit = true
|
120
|
+
@options.exit = true
|
95
121
|
end
|
96
122
|
end
|
97
123
|
|
98
|
-
|
99
|
-
|
124
|
+
def parse_options options
|
125
|
+
options.banner = "Usage: hansel [options]"
|
126
|
+
options.separator "Options:"
|
127
|
+
%w(server port uri connections low_rate high_rate rate_step cookie
|
128
|
+
format output output_dir verbose help version).map(&:to_sym).each do |method|
|
129
|
+
self.send method, options
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
#
|
134
|
+
# Uses OptionParser to return an OpenStruct object describing the options.
|
135
|
+
#
|
136
|
+
def parse
|
137
|
+
# The options specified on the command line will be collected in *options*.
|
138
|
+
# We set default values here.
|
139
|
+
OptionParser.new { |options| parse_options options}.parse!(@args)
|
140
|
+
@options
|
141
|
+
end
|
142
|
+
|
100
143
|
end
|
101
144
|
end
|
data/lib/config.rb
CHANGED
@@ -1,27 +1,60 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module Hansel
|
2
|
+
#
|
3
|
+
# Loads and parses the configuration file
|
4
|
+
#
|
5
|
+
class Config
|
6
|
+
def initialize(argv)
|
7
|
+
@argv = argv
|
8
|
+
@args = []
|
9
|
+
end
|
5
10
|
|
6
|
-
|
7
|
-
|
8
|
-
|
11
|
+
#
|
12
|
+
# The main configuration directory: defaults to ~/.hansel
|
13
|
+
# Creates the configuration directory if it doesn't exist.
|
14
|
+
#
|
15
|
+
def config_path
|
16
|
+
@config_path ||= File.join [ENV['HOME'], '.hansel']
|
17
|
+
FileUtils.mkdir_p @config_path unless File.exists? @config_path
|
18
|
+
@config_path
|
19
|
+
end
|
9
20
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
File.
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
21
|
+
#
|
22
|
+
# The options file located in the configuration directory
|
23
|
+
#
|
24
|
+
def options_path
|
25
|
+
@options_path ||= File.join config_path, 'options'
|
26
|
+
end
|
27
|
+
|
28
|
+
def read_lines file
|
29
|
+
file.read.split("\n").each do |line|
|
30
|
+
next if line =~ /#+/
|
31
|
+
@args += line.split(' ')
|
19
32
|
end
|
20
33
|
end
|
21
|
-
args
|
22
|
-
end
|
23
34
|
|
24
|
-
|
25
|
-
|
35
|
+
def read_file path
|
36
|
+
File.open path do |file|
|
37
|
+
read_lines file
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Reads the options file and returns an Array of String objects.
|
43
|
+
# Line starting with '#' are skipped.
|
44
|
+
#
|
45
|
+
def read_config_file
|
46
|
+
path = options_path
|
47
|
+
if File.exists? path
|
48
|
+
read_file path
|
49
|
+
end
|
50
|
+
@args
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Returns an OpenStruct object with the options
|
55
|
+
#
|
56
|
+
def options
|
57
|
+
@options ||= ArgParser.new(read_config_file + @argv).parse
|
58
|
+
end
|
26
59
|
end
|
27
60
|
end
|
data/lib/csv_formatter.rb
CHANGED
@@ -1,20 +1,41 @@
|
|
1
1
|
require 'csv'
|
2
|
+
module Hansel
|
3
|
+
#
|
4
|
+
# Output to csv format
|
5
|
+
#
|
6
|
+
class CsvFormatter
|
7
|
+
def initialize(data)
|
8
|
+
return if data.empty?
|
9
|
+
@data = data
|
10
|
+
@csv = ""
|
11
|
+
@info_keys = []
|
12
|
+
@data_keys = []
|
13
|
+
@data.keys.each do |key|
|
14
|
+
@info_keys << key if key.instance_of? Symbol
|
15
|
+
@data_keys << key if key.instance_of? Fixnum
|
16
|
+
end
|
17
|
+
@keys ||= @data[@data_keys.first].keys
|
18
|
+
@info = @info_keys.collect{|key| @data[key]}
|
19
|
+
line header
|
20
|
+
end
|
2
21
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
22
|
+
def line text
|
23
|
+
@csv << text
|
24
|
+
@csv << "\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
def header
|
28
|
+
@header ||= CSV.generate_line((@info_keys + @keys).map(&:to_s))
|
29
|
+
end
|
30
|
+
|
31
|
+
def format_line data_key
|
32
|
+
line CSV.generate_line(@info + @keys.collect{|key| @data[data_key][key]})
|
33
|
+
end
|
7
34
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
result << CSV.generate_line(keys.map(&:to_s))
|
13
|
-
result << "\n"
|
14
|
-
@data.each_pair do |key, value|
|
15
|
-
result << CSV.generate_line(keys.collect{|k| value[k]})
|
16
|
-
result << "\n"
|
35
|
+
def format
|
36
|
+
return result if @data.empty?
|
37
|
+
@data_keys.each { |data_key| format_line data_key }
|
38
|
+
@csv
|
17
39
|
end
|
18
|
-
result
|
19
40
|
end
|
20
41
|
end
|
data/lib/hansel.rb
CHANGED
@@ -1,93 +1,178 @@
|
|
1
1
|
require 'fileutils'
|
2
|
+
module Hansel
|
3
|
+
#
|
4
|
+
# Class wrapper over httperf.
|
5
|
+
#
|
6
|
+
class Hansel
|
7
|
+
def initialize(options = {})
|
8
|
+
@options = options
|
9
|
+
@results = {}
|
10
|
+
@current_rate = nil
|
11
|
+
end
|
2
12
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
@results = {}
|
7
|
-
end
|
13
|
+
def options
|
14
|
+
@options
|
15
|
+
end
|
8
16
|
|
9
|
-
|
10
|
-
|
11
|
-
|
17
|
+
def parse_replies line
|
18
|
+
@result[:replies] = $1 if line =~ /^Total: .*replies (\d+)/
|
19
|
+
end
|
12
20
|
|
13
|
-
|
14
|
-
|
15
|
-
|
21
|
+
def parse_connection_rate line
|
22
|
+
@result[:connection_rate] = $1 if line =~ /^Connection rate: (\d+\.\d)/
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse_request_rate line
|
26
|
+
@result[:request_rate] = $1 if line =~ /^Request rate: (\d+\.\d)/
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse_reply_time line
|
30
|
+
@result[:reply_time] = $1 if line =~ /^Reply time .* response (\d+\.\d)/
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse_net_io line
|
34
|
+
@result[:net_io] = $1 if line =~ /^Net I\/O: (\d+\.\d)/
|
35
|
+
end
|
16
36
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
37
|
+
def parse_errors line
|
38
|
+
@result[:errors] = $1 if line =~ /^Errors: total (\d+)/
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_status line
|
42
|
+
@result[:status] = $1 if line =~ /^Reply status: 1xx=\d+ 2xx=\d+ 3xx=\d+ 4xx=\d+ 5xx=(\d+)/
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse_reply_rate line
|
46
|
+
if line =~ /^Reply rate .*min (\d+\.\d) avg (\d+\.\d) max (\d+\.\d) stddev (\d+\.\d)/
|
47
|
+
@result[:reply_rate_min] = $1
|
48
|
+
@result[:reply_rate_avg] = $2
|
49
|
+
@result[:reply_rate_max] = $3
|
50
|
+
@result[:reply_rate_stddev] = $4
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_line line
|
55
|
+
%w(parse_replies parse_connection_rate parse_request_rate
|
56
|
+
parse_reply_time parse_net_io parse_errors parse_status
|
57
|
+
parse_reply_rate).map(&:to_sym).each do |method|
|
58
|
+
self.send method, line
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def parse pipe
|
63
|
+
@result = {:output => ""}
|
31
64
|
while line = pipe.gets
|
32
|
-
|
33
|
-
when /^Total: .*replies (\d+)/ then result[:replies] = $1
|
34
|
-
when /^Connection rate: (\d+\.\d)/ then result[:connection_rate] = $1
|
35
|
-
when /^Request rate: (\d+\.\d)/ then result[:request_rate] = $1
|
36
|
-
when /^Reply time .* response (\d+\.\d)/ then result[:reply_time] = $1
|
37
|
-
when /^Net I\/O: (\d+\.\d)/ then result[:net_io] = $1
|
38
|
-
when /^Errors: total (\d+)/ then result[:errors] = $1
|
39
|
-
when /^Reply rate .*min (\d+\.\d) avg (\d+\.\d) max (\d+\.\d) stddev (\d+\.\d)/ then
|
40
|
-
result[:reply_rate_min] = $1
|
41
|
-
result[:reply_rate_avg] = $2
|
42
|
-
result[:reply_rate_max] = $3
|
43
|
-
result[:reply_rate_stddev] = $4
|
44
|
-
when /^Reply status: 1xx=\d+ 2xx=\d+ 3xx=\d+ 4xx=\d+ 5xx=(\d+)/ then result[:status] = $1
|
45
|
-
end
|
65
|
+
parse_line line
|
46
66
|
end
|
47
67
|
end
|
48
|
-
result
|
49
|
-
end
|
50
68
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
69
|
+
def build_httperf_cmd
|
70
|
+
cookie = @options.cookie
|
71
|
+
httperf_cmd = [
|
72
|
+
"httperf --hog",
|
73
|
+
"--server=#{@options.server}",
|
74
|
+
"--port=#{@options.port}",
|
75
|
+
"--uri=#{@options.uri}",
|
76
|
+
"--num-conns=#{@options.num_conns}",
|
77
|
+
"--rate=#{@current_rate}",
|
78
|
+
cookie && "--add-header='Cookie: #{cookie}\\n'"
|
79
|
+
].compact.join ' '
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# Runs httperf with a given request rate. Parses the output and returns
|
84
|
+
# a hash with the results.
|
85
|
+
#
|
86
|
+
def httperf
|
87
|
+
httperf_cmd = build_httperf_cmd
|
88
|
+
IO.popen("#{httperf_cmd} 2>&1") do |pipe|
|
89
|
+
puts "\n#{httperf_cmd}"
|
90
|
+
parse pipe
|
70
91
|
end
|
92
|
+
@results[@current_rate] = @result
|
71
93
|
end
|
72
|
-
end
|
73
94
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
@results
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
95
|
+
def yaml_formatter
|
96
|
+
load File.here '/../lib/yaml_formatter.rb'
|
97
|
+
YamlFormatter.new(@results)
|
98
|
+
end
|
99
|
+
|
100
|
+
def csv_formatter
|
101
|
+
load File.here '/../lib/csv_formatter.rb'
|
102
|
+
CsvFormatter.new(@results)
|
103
|
+
end
|
104
|
+
|
105
|
+
def octave_formatter
|
106
|
+
load File.here '/../lib/octave_formatter.rb'
|
107
|
+
puts @output_file_name
|
108
|
+
OctaveFormatter.new(@results, {:output_file_name => @output_file_name})
|
109
|
+
end
|
110
|
+
|
111
|
+
def make_formatter format
|
112
|
+
case format
|
113
|
+
when :yaml
|
114
|
+
yaml_formatter
|
115
|
+
when :csv
|
116
|
+
csv_formatter
|
117
|
+
when :octave
|
118
|
+
octave_formatter
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def formatted_output
|
123
|
+
@output_file_name = [@options.server, @options.num_conns.to_s].join('.')
|
124
|
+
output_file = File.join @options.output_dir, @output_file_name
|
125
|
+
output_format = @options.output_format
|
126
|
+
type = { :yaml => 'yml', :csv => 'csv', :octave => 'm' }[output_format]
|
127
|
+
File.open([output_file, type].join('.'), "w+") do |file|
|
128
|
+
file.puts make_formatter(output_format).format
|
129
|
+
end
|
130
|
+
end
|
88
131
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
132
|
+
#
|
133
|
+
# Output the results based on the requested output format
|
134
|
+
#
|
135
|
+
def output
|
136
|
+
if @options.output
|
137
|
+
FileUtils.mkdir_p @options.output_dir
|
138
|
+
formatted_output
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def status text
|
143
|
+
puts text if @options.verbose
|
144
|
+
end
|
145
|
+
|
146
|
+
def one_run
|
147
|
+
@results[:server] = @options.server
|
148
|
+
@results[:port] = @options.port
|
149
|
+
@results[:uri] = @options.uri
|
150
|
+
@results[:num_conns] = @options.num_conns
|
151
|
+
httperf
|
152
|
+
end
|
153
|
+
|
154
|
+
def run_all
|
155
|
+
(@options.low_rate.to_i..@options.high_rate.to_i).step(@options.rate_step.to_i) do |rate|
|
156
|
+
status "running httperf at rate: #{rate}"
|
157
|
+
@current_rate = rate
|
158
|
+
one_run
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
#
|
163
|
+
# Run httperf from low_rate to high_rate, stepping by rate_step
|
164
|
+
#
|
165
|
+
def run
|
166
|
+
status "starting run..."
|
167
|
+
run_all
|
168
|
+
status "ending run..."
|
169
|
+
output
|
170
|
+
@results
|
171
|
+
end
|
172
|
+
|
173
|
+
trap("INT") {
|
174
|
+
puts "Terminating."
|
175
|
+
Process.exit
|
176
|
+
}
|
177
|
+
end
|
93
178
|
end
|
data/lib/octave_formatter.rb
CHANGED
@@ -1,53 +1,69 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
module Hansel
|
2
|
+
#
|
3
|
+
# Output to a Octave script.
|
4
|
+
#
|
5
|
+
class OctaveFormatter
|
6
|
+
def initialize(data, options = {})
|
7
|
+
@data = data
|
8
|
+
@options = options
|
9
|
+
@png_output = [(options[:output_file_name] || 'stats'), 'png'].join('.')
|
10
|
+
@rates = @data.keys.select{|key| key.instance_of? Fixnum}.map(&:to_i).sort
|
11
|
+
@max_rate = @rates.last
|
12
|
+
@results = OpenStruct.new(
|
13
|
+
:request_rate => [],
|
14
|
+
:connection_rate => [],
|
15
|
+
:reply_rate_avg => [],
|
16
|
+
:reply_rate_max => [],
|
17
|
+
:reply_time => [],
|
18
|
+
:reply_rate_stddev => [],
|
19
|
+
:errors => []
|
20
|
+
)
|
21
|
+
end
|
7
22
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
reply_time = rate.collect{|x| @data[x][:reply_time]}
|
15
|
-
reply_rate_stddev = rate.collect{|x| @data[x][:reply_rate_stddev]}
|
16
|
-
errors = rate.collect{|x| @data[x][:errors]}
|
23
|
+
def build_arrays rate, data
|
24
|
+
%w(request_rate connection_rate reply_rate_avg reply_rate_max
|
25
|
+
reply_time reply_rate_stddev errors).map(&:to_sym).each do |method|
|
26
|
+
(@results.send method) << data[method]
|
27
|
+
end
|
28
|
+
end
|
17
29
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
30
|
+
def format
|
31
|
+
# TODO: render an erb template instead.
|
32
|
+
@rates.each { |rate| build_arrays rate, @data[rate] }
|
33
|
+
result = <<-EOS
|
34
|
+
rate = [#{@rates.join(',')}];
|
35
|
+
request_rate = [#{@results.request_rate.join(',')}];
|
36
|
+
connection_rate = [#{@results.connection_rate.join(',')}];
|
37
|
+
reply_rate_avg = [#{@results.reply_rate_avg.join(',')}];
|
38
|
+
reply_rate_max = [#{@results.reply_rate_max.join(',')}];
|
39
|
+
reply_time = [#{@results.reply_time.join(',')}];
|
40
|
+
reply_rate_stddev = [#{@results.reply_rate_stddev.join(',')}];
|
41
|
+
errors = [#{@results.errors.join(',')}];
|
27
42
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
43
|
+
plot(rate, request_rate, '-k*');
|
44
|
+
hold on;
|
45
|
+
plot(rate, connection_rate, '-kd');
|
46
|
+
hold on;
|
47
|
+
plot(rate, reply_rate_max, '-kp');
|
48
|
+
hold on;
|
49
|
+
plot(rate, reply_rate_max, '-k+');
|
50
|
+
hold on;
|
51
|
+
plot(rate, reply_rate_stddev, '-kh');
|
52
|
+
hold on;
|
53
|
+
plot(rate, reply_time, '-g*');
|
54
|
+
hold on;
|
55
|
+
plot(rate, errors, '-r*');
|
41
56
|
|
42
|
-
|
57
|
+
grid on;
|
43
58
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
59
|
+
axis([0 #{@max_rate} 0 #{@max_rate}]);
|
60
|
+
title('Hansel report for #{@data[:server]}:#{@data[:port]}#{@data[:uri]} (#{@data[:num_conns]} connections per run)')
|
61
|
+
xlabel('Demanded Request Rate');
|
62
|
+
legend('Request Rate', 'Connection Rate', 'Avg. reply rate', 'Max. reply rate', 'Reply rate StdDev', 'Reply time', 'Errors');
|
63
|
+
print('#{@png_output}', '-dpng')
|
64
|
+
EOS
|
65
|
+
result = result.gsub ' ', ''
|
66
|
+
result
|
67
|
+
end
|
52
68
|
end
|
53
69
|
end
|
data/lib/yaml_formatter.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module Hansel
|
2
|
+
#
|
3
|
+
# Output to yaml format
|
4
|
+
#
|
5
|
+
class YamlFormatter
|
6
|
+
def initialize(data)
|
7
|
+
@data = data
|
8
|
+
end
|
5
9
|
|
6
|
-
|
7
|
-
|
10
|
+
def format
|
11
|
+
@data.to_yaml
|
12
|
+
end
|
8
13
|
end
|
9
|
-
end
|
14
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hansel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 4
|
9
|
+
version: 0.1.4
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Paul Mylchreest
|
@@ -9,19 +14,10 @@ autorequire:
|
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date: 2010-04-
|
17
|
+
date: 2010-04-27 00:00:00 -04:00
|
13
18
|
default_executable: hansel
|
14
|
-
dependencies:
|
15
|
-
|
16
|
-
name: mutter
|
17
|
-
type: :runtime
|
18
|
-
version_requirement:
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 0.5.2
|
24
|
-
version:
|
19
|
+
dependencies: []
|
20
|
+
|
25
21
|
description: Ruby driver for httperf - automated load and performance testing
|
26
22
|
email: paul.mylchreest@mac.com
|
27
23
|
executables:
|
@@ -40,12 +36,10 @@ files:
|
|
40
36
|
- VERSION
|
41
37
|
- bin/hansel
|
42
38
|
- hansel.gemspec
|
43
|
-
- hansel.rb
|
44
39
|
- lib/arg_parser.rb
|
45
40
|
- lib/config.rb
|
46
41
|
- lib/csv_formatter.rb
|
47
42
|
- lib/hansel.rb
|
48
|
-
- lib/mutter.rb
|
49
43
|
- lib/octave_formatter.rb
|
50
44
|
- lib/yaml_formatter.rb
|
51
45
|
has_rdoc: true
|
@@ -61,18 +55,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
61
55
|
requirements:
|
62
56
|
- - ">="
|
63
57
|
- !ruby/object:Gem::Version
|
58
|
+
segments:
|
59
|
+
- 0
|
64
60
|
version: "0"
|
65
|
-
version:
|
66
61
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
62
|
requirements:
|
68
63
|
- - ">="
|
69
64
|
- !ruby/object:Gem::Version
|
65
|
+
segments:
|
66
|
+
- 0
|
70
67
|
version: "0"
|
71
|
-
version:
|
72
68
|
requirements: []
|
73
69
|
|
74
70
|
rubyforge_project:
|
75
|
-
rubygems_version: 1.3.
|
71
|
+
rubygems_version: 1.3.6
|
76
72
|
signing_key:
|
77
73
|
specification_version: 3
|
78
74
|
summary: Ruby driver for httperf - automated load and performance testing
|
data/hansel.rb
DELETED
File without changes
|
data/lib/mutter.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
module Mutter
|
2
|
-
begin
|
3
|
-
require 'mutter'
|
4
|
-
rescue LoadError
|
5
|
-
STDERR.puts "mutter gem wasn't found, using default output."
|
6
|
-
end
|
7
|
-
|
8
|
-
def mutter
|
9
|
-
mutter ||= Mutter.new({
|
10
|
-
:underline => %w'( )', :yellow => %w'[ ]',
|
11
|
-
:bold => %w'< >'
|
12
|
-
}).clear(:default)
|
13
|
-
rescue
|
14
|
-
nil
|
15
|
-
end
|
16
|
-
end
|