hansel 0.1.3 → 0.1.4
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/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
|