hansel 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Paul Mylchreest
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,22 @@
1
+ = hansel
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == References
16
+
17
+ "HTTP Perforance Testing with httperf":http://agiletesting.blogspot.com/2005/04/http-performance-testing-with-httperf.html
18
+ "Perforance vs Load Testing":http://agiletesting.blogspot.com/2005/04/more-on-performance-vs-load-testing.html
19
+
20
+ == Copyright
21
+
22
+ Copyright (c) 2010 Paul Mylchreest. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "hansel"
8
+ gem.summary = %Q{Ruby driver for httperf - automated load and performance testing}
9
+ gem.description = %Q{Ruby driver for httperf - automated load and performance testing}
10
+ gem.email = "paul.mylchreest@mac.com"
11
+ gem.homepage = "http://github.com/xlymian/hansel"
12
+ gem.authors = ["Paul Mylchreest"]
13
+ gem.add_dependency("mutter", ">= 0.5.2")
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/bin/hansel ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'optparse'
5
+ require 'ostruct'
6
+
7
+ class File
8
+ def self.here(string)
9
+ expand_path(dirname(__FILE__)) + string
10
+ end
11
+ end
12
+
13
+ require File.here '/../lib/arg_parser'
14
+ require File.here '/../lib/mutter'
15
+ require File.here '/../lib/config'
16
+ require File.here '/../lib/hansel'
17
+
18
+ include Config
19
+ include Mutter
20
+
21
+ hansel = Hansel.new(options)
22
+ puts hansel.run
data/hansel.gemspec ADDED
@@ -0,0 +1,58 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{hansel}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Paul Mylchreest"]
12
+ s.date = %q{2010-01-24}
13
+ s.default_executable = %q{hansel}
14
+ s.description = %q{Ruby driver for httperf - automated load and performance testing}
15
+ s.email = %q{paul.mylchreest@mac.com}
16
+ s.executables = ["hansel"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".gitignore",
24
+ "LICENSE",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "bin/hansel",
29
+ "hansel.gemspec",
30
+ "hansel.rb",
31
+ "lib/arg_parser.rb",
32
+ "lib/config.rb",
33
+ "lib/csv_formatter.rb",
34
+ "lib/hansel.rb",
35
+ "lib/mutter.rb",
36
+ "lib/octave_formatter.rb",
37
+ "lib/yaml_formatter.rb"
38
+ ]
39
+ s.homepage = %q{http://github.com/xlymian/hansel}
40
+ s.rdoc_options = ["--charset=UTF-8"]
41
+ s.require_paths = ["lib"]
42
+ s.rubygems_version = %q{1.3.5}
43
+ s.summary = %q{Ruby driver for httperf - automated load and performance testing}
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
50
+ s.add_runtime_dependency(%q<mutter>, [">= 0.5.2"])
51
+ else
52
+ s.add_dependency(%q<mutter>, [">= 0.5.2"])
53
+ end
54
+ else
55
+ s.add_dependency(%q<mutter>, [">= 0.5.2"])
56
+ end
57
+ end
58
+
data/hansel.rb ADDED
@@ -0,0 +1 @@
1
+
data/lib/arg_parser.rb ADDED
@@ -0,0 +1,100 @@
1
+ class ArgParser
2
+ # http://www.ruby-doc.org/stdlib/libdoc/optparse/rdoc/classes/OptionParser.html
3
+ # Return a structure describing the options.
4
+ def self.parse(args)
5
+ # The options specified on the command line will be collected in *options*.
6
+ # We set default values here.
7
+ options = OpenStruct.new
8
+ options.verbose = false
9
+ options.server = 'localhost'
10
+ options.port = '80'
11
+ options.uri = '/'
12
+ options.num_conns = 1
13
+ options.rate = 1
14
+ options.cookie = nil
15
+ options.low_rate = 1
16
+ options.high_rate = 2
17
+ options.rate_step = 1
18
+ options.output_format = :yaml
19
+ options.output = nil
20
+
21
+ opts = OptionParser.new do |opts|
22
+ opts.banner = "Usage: hansel [options]"
23
+
24
+ opts.separator ""
25
+ opts.separator "Specific options:"
26
+
27
+ # Mandatory argument.
28
+ opts.on("-s", "--server=S",
29
+ "Specifies the IP hostname of the server.") do |opt|
30
+ options.server = opt
31
+ end
32
+
33
+ opts.on("-p", "--port=N",
34
+ "This option specifies the port number N on which the web server is listening for HTTP requests.") do |opt|
35
+ options.port = opt
36
+ end
37
+
38
+ opts.on("-u", "--uri=S",
39
+ "Specifies that URI S should be accessed on the server.") do |opt|
40
+ options.uri = opt
41
+ end
42
+
43
+ opts.on("-n", "--num_conns=N",
44
+ "Specifies the total number of connections to create.") do |opt|
45
+ options.num_conns = opt.to_i
46
+ end
47
+
48
+ opts.on("-l", "--low_rate=S",
49
+ "Specifies the starting fixed rate at which connections are created.") do |opt|
50
+ options.low_rate = opt.to_i
51
+ end
52
+
53
+ opts.on("-l", "--high_rate=S",
54
+ "Specifies the ending fixed rate at which connections are created.") do |opt|
55
+ options.high_rate = opt.to_i
56
+ end
57
+
58
+ opts.on("-l", "--rate_step=S",
59
+ "Specifies the fixed rate step at which connections are created.") do |opt|
60
+ options.rate_step = opt.to_i
61
+ end
62
+
63
+ opts.on("-c", "--cookie=C", "Specify a cookie.") do |opt|
64
+ options.cookie = opt
65
+ end
66
+
67
+ opts.on("-f", "--format=FILE [yaml|csv]", "Specify an output format.") do |opt|
68
+ options.output_format = opt.to_sym
69
+ end
70
+
71
+ opts.on("-o", "--output=FILE", "Specify an output file.") do |opt|
72
+ options.output = opt
73
+ end
74
+
75
+ opts.separator ""
76
+ opts.separator "Common options:"
77
+
78
+ # Boolean switch.
79
+ opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
80
+ options.verbose = v
81
+ end
82
+
83
+ # No argument, shows at tail. This will print an options summary.
84
+ # Try it and see!
85
+ opts.on_tail("-h", "--help", "Show this message") do
86
+ puts opts
87
+ exit
88
+ end
89
+
90
+ # Another typical switch to print the version.
91
+ opts.on_tail("--version", "Show version") do
92
+ puts OptionParser::Version.join('.')
93
+ exit
94
+ end
95
+ end
96
+
97
+ opts.parse!(args)
98
+ options
99
+ end
100
+ end
data/lib/config.rb ADDED
@@ -0,0 +1,27 @@
1
+ module Config
2
+ def config_path
3
+ config_path ||= File.join [ENV['HOME'], '.hansel']
4
+ end
5
+
6
+ def options_path
7
+ options_path ||= File.join config_path, 'options'
8
+ end
9
+
10
+ def read_config_file
11
+ args = []
12
+ FileUtils.mkdir_p config_path unless File.exists? config_path
13
+ if File.exists? options_path
14
+ File.open options_path do |file|
15
+ file.read.split("\n").each do |line|
16
+ next if line =~ /#+/
17
+ args += line.split(' ')
18
+ end
19
+ end
20
+ end
21
+ args
22
+ end
23
+
24
+ def options
25
+ @opt ||= ArgParser.parse(read_config_file + ARGV)
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ require 'csv'
2
+
3
+ class CsvFormatter
4
+ def initialize(data)
5
+ @data = data
6
+ end
7
+
8
+ def format
9
+ result = ""
10
+ return result if @data.empty?
11
+ keys = @data[@data.keys.first].keys
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"
17
+ end
18
+ result
19
+ end
20
+ end
data/lib/hansel.rb ADDED
@@ -0,0 +1,83 @@
1
+ class Hansel
2
+ def initialize(options = {})
3
+ @options = options
4
+ @results = {}
5
+ end
6
+
7
+ def verbose?
8
+ @options.verbose
9
+ end
10
+
11
+ def benchmark(rate)
12
+ result = {:output => ""}
13
+ httperf_cmd = [
14
+ "httperf --hog",
15
+ "--server=#{@options.server}",
16
+ "--port=#{@options.port}",
17
+ "--uri=#{@options.uri}",
18
+ "--num-conns=#{@options.num_conns}",
19
+ "--rate=#{rate}",
20
+ @options.cookie && "--add-header='Cookie: #{@options.cookie}\\n'"
21
+ ].compact.join ' '
22
+
23
+ IO.popen("#{httperf_cmd} 2>&1") do |pipe|
24
+ puts "\n#{httperf_cmd}"
25
+ while line = pipe.gets
26
+ case line
27
+ when /^Total: .*replies (\d+)/ then result[:replies] = $1
28
+ when /^Connection rate: (\d+\.\d)/ then result[:connection_rate] = $1
29
+ when /^Request rate: (\d+\.\d)/ then result[:request_rate] = $1
30
+ when /^Reply time .* response (\d+\.\d)/ then result[:reply_time] = $1
31
+ when /^Net I\/O: (\d+\.\d)/ then result[:net_io] = $1
32
+ when /^Errors: total (\d+)/ then result[:errors] = $1
33
+ when /^Reply rate .*min (\d+\.\d) avg (\d+\.\d) max (\d+\.\d) stddev (\d+\.\d)/ then
34
+ result[:reply_rate_min] = $1
35
+ result[:reply_rate_avg] = $2
36
+ result[:reply_rate_max] = $3
37
+ result[:reply_rate_stddev] = $4
38
+ when /^Reply status: 1xx=\d+ 2xx=\d+ 3xx=\d+ 4xx=\d+ 5xx=(\d+)/ then result[:status] = $1
39
+ end
40
+ end
41
+ end
42
+ result
43
+ end
44
+
45
+ def output
46
+ if @options.output
47
+ File.open(@options.output, "w+") do |f|
48
+ formatter = case @options.output_format
49
+ when :yaml
50
+ load File.here '/../lib/yaml_formatter.rb'
51
+ YamlFormatter.new(@results)
52
+ when :csv
53
+ load File.here '/../lib/csv_formatter.rb'
54
+ CsvFormatter.new(@results)
55
+ when :octave
56
+ load File.here '/../lib/octave_formatter.rb'
57
+ OctaveFormatter.new(@results)
58
+ end
59
+ f.puts formatter.format
60
+ end
61
+ end
62
+ end
63
+
64
+ def run
65
+ puts "starting run..." if verbose?
66
+ (@options.low_rate..@options.high_rate).step(@options.rate_step) do |rate|
67
+ puts "benchmarking at rate: #{rate}" if verbose?
68
+ @results[:server] = @options.server
69
+ @results[:port] = @options.port
70
+ @results[:uri] = @options.uri
71
+ @results[:num_conns] = @options.num_conns
72
+ @results[rate] = benchmark(rate)
73
+ end
74
+ puts "ending run..." if verbose?
75
+ output
76
+ @results
77
+ end
78
+
79
+ trap("INT") {
80
+ puts "Terminating tests."
81
+ Process.exit
82
+ }
83
+ end
data/lib/mutter.rb ADDED
@@ -0,0 +1,16 @@
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
@@ -0,0 +1,47 @@
1
+ class OctaveFormatter
2
+ def initialize(data)
3
+ @data = data
4
+ end
5
+
6
+ def format
7
+ rate = @data.keys.reject{|x| x.instance_of? Symbol}.map(&:to_i).sort
8
+ request_rate = rate.collect{|x| @data[x][:request_rate]}
9
+ connection_rate = rate.collect{|x| @data[x][:connection_rate]}
10
+ reply_rate_avg = rate.collect{|x| @data[x][:reply_rate_avg]}
11
+ reply_rate_max = rate.collect{|x| @data[x][:reply_rate_max]}
12
+ reply_rate_stddev = rate.collect{|x| @data[x][:reply_rate_stddev]}
13
+ errors = rate.collect{|x| @data[x][:errors]}
14
+
15
+ result = <<-EOS
16
+ rate = [#{rate.join(',')}];
17
+ request_rate = [#{request_rate.join(',')}];
18
+ connection_rate = [#{connection_rate.join(',')}];
19
+ reply_rate_avg = [#{reply_rate_avg.join(',')}];
20
+ reply_rate_max = [#{reply_rate_max.join(',')}];
21
+ reply_rate_stddev = [#{reply_rate_stddev.join(',')}];
22
+ errors = [#{errors.join(',')}];
23
+
24
+ plot(rate, request_rate, '-k*');
25
+ hold on;
26
+ plot(rate, connection_rate, '-kd');
27
+ hold on;
28
+ plot(rate, reply_rate_max, '-kp');
29
+ hold on;
30
+ plot(rate, reply_rate_max, '-k+');
31
+ hold on;
32
+ plot(rate, reply_rate_stddev, '-kh');
33
+ hold on;
34
+ plot(rate, errors, '-r<');
35
+
36
+ grid on;
37
+
38
+ axis([0 #{rate.last} 0 #{rate.last}]);
39
+ title('Hansel report for #{@data[:server]}:#{@data[:port]}#{@data[:uri]} (#{@data[:num_conns]} connections per run)')
40
+ xlabel('Demanded Request Rate');
41
+ legend('Request Rate', 'Connection Rate', 'Avg. reply rate', 'Max. reply rate', 'Reply rate StdDev', 'Errors');
42
+ print('stats.png', '-dpng')
43
+ EOS
44
+ result = result.gsub ' ', ''
45
+ result
46
+ end
47
+ end
@@ -0,0 +1,9 @@
1
+ class YamlFormatter
2
+ def initialize(data)
3
+ @data = data
4
+ end
5
+
6
+ def format
7
+ @data.to_yaml
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hansel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Paul Mylchreest
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-24 00:00:00 -05:00
13
+ default_executable: hansel
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
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:
25
+ description: Ruby driver for httperf - automated load and performance testing
26
+ email: paul.mylchreest@mac.com
27
+ executables:
28
+ - hansel
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.rdoc
34
+ files:
35
+ - .document
36
+ - .gitignore
37
+ - LICENSE
38
+ - README.rdoc
39
+ - Rakefile
40
+ - VERSION
41
+ - bin/hansel
42
+ - hansel.gemspec
43
+ - hansel.rb
44
+ - lib/arg_parser.rb
45
+ - lib/config.rb
46
+ - lib/csv_formatter.rb
47
+ - lib/hansel.rb
48
+ - lib/mutter.rb
49
+ - lib/octave_formatter.rb
50
+ - lib/yaml_formatter.rb
51
+ has_rdoc: true
52
+ homepage: http://github.com/xlymian/hansel
53
+ licenses: []
54
+
55
+ post_install_message:
56
+ rdoc_options:
57
+ - --charset=UTF-8
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ version:
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.3.5
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Ruby driver for httperf - automated load and performance testing
79
+ test_files: []
80
+