stresser 0.3.2 → 0.4.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/HISTORY.markdown ADDED
@@ -0,0 +1,3 @@
1
+ ## 0.4
2
+ - added 'started at' column to csv export
3
+ - refactored options pasing in stresser bin to match stresser-grapher and stresser-loggen
data/README.markdown CHANGED
@@ -100,6 +100,10 @@ as often as you like:
100
100
 
101
101
  The `{{n}}` will be replaced with the numbers 0-99.
102
102
 
103
+ ## Tests
104
+ Run `rake spec` to run them tests. Currently, only httperf's output of a session based
105
+ replay log is parsed, but I will add more.
106
+
103
107
  ## Thanks
104
108
 
105
109
  Stresser is based on igvita's autoperf driver for httperf.
data/Rakefile CHANGED
@@ -47,9 +47,17 @@ task :default => :test
47
47
  require 'rake/rdoctask'
48
48
  Rake::RDocTask.new do |rdoc|
49
49
  version = File.exist?('VERSION') ? File.read('VERSION') : ""
50
-
51
50
  rdoc.rdoc_dir = 'rdoc'
52
51
  rdoc.title = "stresser #{version}"
53
52
  rdoc.rdoc_files.include('README*')
54
53
  rdoc.rdoc_files.include('lib/**/*.rb')
55
54
  end
55
+
56
+
57
+ require 'spec/rake/spectask'
58
+
59
+ desc "Run all examples"
60
+ Spec::Rake::SpecTask.new('spec') do |t|
61
+ t.spec_files = FileList['spec/**/*.rb']
62
+ end
63
+
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.2
1
+ 0.4.0
data/bin/stresser CHANGED
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
  $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+ require 'rubygems'
3
4
  require 'mp_perf'
4
5
 
6
+
5
7
  trap("INT") {
6
8
  puts "Terminating tests."
7
9
  Process.exit
data/bin/stresser-grapher CHANGED
@@ -23,7 +23,7 @@ EOS
23
23
  :type => String
24
24
  end.merge(:csv_file => ARGV.last)
25
25
 
26
- Trollop::die :output_dir, "must be writeable" unless opts[:output_dir] and File.stat(opts[:output_dir]).writable?
26
+ Trollop::die :output_dir, "must be writeable" unless opts[:output_dir] and File.stat(opts[:output_dir]).writable?
27
27
  Trollop::die :output_dir, "must be a directory" unless opts[:output_dir] and File.stat(opts[:output_dir]).directory?
28
28
 
29
29
  Grapher.generate_reports(opts)
Binary file
data/lib/httperf.rb CHANGED
@@ -2,11 +2,12 @@ module Httperf
2
2
  extend self
3
3
 
4
4
  def run(conf)
5
- res = Hash.new ""
5
+ res = Hash["started at", Time.now]
6
+
6
7
  httperf_opt = conf.keys.grep(/httperf/).collect {|k| "--#{k.gsub(/httperf_/, '')}=#{conf[k]}"}.join(" ")
7
8
  httperf_cmd = "httperf --hog --server=#{conf['host']} --port=#{conf['port']} #{httperf_opt}"
8
9
  IO.popen("#{httperf_cmd} 2>&1") do |pipe|
9
- res = parse_output(pipe)
10
+ res.merge! parse_output(pipe)
10
11
 
11
12
  # Now calculate the amount of stati per second
12
13
  (1..5).each do |i|
data/lib/mp_perf.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'optparse'
2
2
  require 'ruport'
3
3
  require 'httperf'
4
+ require 'trollop'
5
+
4
6
 
5
7
  #
6
8
  # Takes command line options and attempts to make a benchmark.
@@ -8,29 +10,33 @@ require 'httperf'
8
10
  class MPPerf
9
11
 
10
12
  def initialize(opts = {})
11
- @conf = {}
12
- OptionParser.new do |opts|
13
- opts.banner = "Usage: stresser -c your.conf -o output.csv"
14
- opts.on("-o", "--output FILE", String, "This file will be overwritten with a detailed report of the stresstest") do |v|
15
- @output_file = v
16
- end
17
- opts.on( "-c", "--config FILE", String, "Your stresser configuration file with stresstest options and parameters directly passed to httperf") do |v|
18
- @conf = parse_config(v)
19
- end
20
- end.parse!
13
+ parse_options
14
+ parse_config
15
+ run_suite
16
+ display_hints
17
+ end
21
18
 
22
- run()
19
+ #
20
+ # Parse command line options
21
+ #
22
+ def parse_options
23
+ @conf = Trollop::options do
24
+ banner = MPPerf.options_banner
25
+ opt :output_file, "The name of the csv file to write the results to. Warning: overwrites that file!",
26
+ :type => String
27
+ opt :config_file, "The name of the .conf file defining your testsuite. See http://github.com/moviepilot/stresser",
28
+ :type => String
29
+ end
23
30
 
24
- puts "~"*80
25
- puts "Great, now create a graph with"
26
- puts " stresser-grapher -o #{File.expand_path(File.dirname(@output_file))} #{@output_file}"
27
- puts ""
31
+ Trollop::die :output_file, "must be a writeable file" unless @conf[:config_file]
32
+ Trollop::die :config_file, "must be a readable file" unless @conf[:config_file]
28
33
  end
29
34
 
30
35
  #
31
36
  # Taken from http://github.com/igrigorik/autoperf
32
37
  #
33
- def parse_config(config_file)
38
+ def parse_config
39
+ config_file = @conf[:config_file]
34
40
  raise Errno::EACCES, "#{config_file} is not readable" unless File.readable?(config_file)
35
41
 
36
42
  conf = {}
@@ -56,7 +62,7 @@ class MPPerf
56
62
  end
57
63
  }
58
64
 
59
- return conf
65
+ @conf.merge! conf
60
66
  end
61
67
 
62
68
  #
@@ -71,7 +77,7 @@ class MPPerf
71
77
  return res
72
78
  end
73
79
 
74
- def run
80
+ def run_suite
75
81
  results = {}
76
82
  report = nil
77
83
  (@conf['low_rate']..@conf['high_rate']).step(@conf['rate_step']) do |rate|
@@ -94,9 +100,23 @@ class MPPerf
94
100
  end
95
101
 
96
102
  # Write csv
97
- File.new(@output_file, "w").puts report.to_csv unless report.nil?
103
+ File.new(@conf[:output_file], "w").puts report.to_csv unless report.nil?
104
+ end
105
+
106
+ def display_hints
107
+ puts "~"*80
108
+ puts "Great, now create a graph with"
109
+ puts " stresser-grapher -o #{File.expand_path(File.dirname(@conf[:output_file]))} #{@conf[:output_file]}"
110
+ puts ""
98
111
  end
99
- end
100
-
101
112
 
113
+ def self.options_banner
114
+ <<BANNER
115
+ Runs a stresstest defined in a config file and write the results to a csv file.
102
116
 
117
+ Usage:
118
+ stresser -c my_config_file -o results.csv
119
+ where options are:
120
+ BANNER
121
+ end
122
+ end
data/sample.conf CHANGED
@@ -1,9 +1,9 @@
1
1
  # MPPerf Configuration File
2
2
 
3
3
  # The host, URI (relative to the document root) and port to test.
4
- host = www.intern.moviepilot.de
4
+ host = localhost
5
5
  uri = /
6
- port = 12001
6
+ port = 80
7
7
 
8
8
  # The 'rate' is the number of number of connections to open per second.
9
9
  # A series of tests will be conducted, starting at low rate,
@@ -4,126 +4,137 @@ describe Httperf do
4
4
 
5
5
  describe "parsing httperf output" do
6
6
 
7
- before(:all) do
8
- @pipe = File.open("spec/httperf_output.txt")
9
- @〉 = Httperf.parse_output(@pipe)
7
+ before(:each) do
8
+ @pipe = File.open("spec/httperf_session_based_output.txt")
9
+ Time.stub!(:now).and_return "Tue Nov 30 15:49:08 0100 2010"
10
+
11
+ #
12
+ # The friendly snail is greeting you!
13
+ #
14
+ IO.should_receive(:popen).and_yield @pipe
15
+ @⚋♉ = Httperf.run({})
10
16
  end
11
17
 
12
18
  it "should parse the 'Total' line correctly" do
13
- @〉['conns'].should == 500
14
- @〉['requests'].should == 600
15
- @〉['replies'].should == 300
16
- @〉['duration'].should == 50.354
19
+ @⚋♉['conns'].should == 500
20
+ @⚋♉['requests'].should == 600
21
+ @⚋♉['replies'].should == 300
22
+ @⚋♉['duration'].should == 50.354
17
23
  end
18
24
 
19
25
  it "should parse the 'Connection rate' line correctly" do
20
- @〉['conn/s'].should == 9.9
21
- @〉['ms/connection'].should == 100.7
22
- @〉['concurrent connections max'].should == 8
26
+ @⚋♉['conn/s'].should == 9.9
27
+ @⚋♉['ms/connection'].should == 100.7
28
+ @⚋♉['concurrent connections max'].should == 8
23
29
  end
24
30
 
25
31
  it "should parse the 'Connection time' line correctly" do
26
- @〉['conn time min'].should == 449.7
27
- @〉['conn time avg'].should == 465.1
28
- @〉['conn time max'].should == 2856.6
29
- @〉['conn time median'].should == 451.5
30
- @〉['conn time stddev'].should == 132.1
32
+ @⚋♉['conn time min'].should == 449.7
33
+ @⚋♉['conn time avg'].should == 465.1
34
+ @⚋♉['conn time max'].should == 2856.6
35
+ @⚋♉['conn time median'].should == 451.5
36
+ @⚋♉['conn time stddev'].should == 132.1
31
37
  end
32
38
 
33
39
  it "should parse the second 'Connection time' line correctly" do
34
- @〉['conn time connect'].should == 74.1
40
+ @⚋♉['conn time connect'].should == 74.1
35
41
  end
36
42
 
37
43
  it "should parse the 'Connection length' line correctly" do
38
- @〉['conn length replies/conn'].should == 1.0
44
+ @⚋♉['conn length replies/conn'].should == 1.0
39
45
  end
40
46
 
41
47
  it "should parse the 'Request rate' line correctly" do
42
- @〉['req/s'].should == 9.9
43
- @〉['ms/req'].should == 100.7
48
+ @⚋♉['req/s'].should == 9.9
49
+ @⚋♉['ms/req'].should == 100.7
44
50
  end
45
51
 
46
52
  it "should parse the 'Request size' line correctly" do
47
- @〉['request size'].should == 65.0
53
+ @⚋♉['request size'].should == 65.0
48
54
  end
49
55
 
50
56
  it "should parse the 'Reply rate' line correctly" do
51
- @〉['replies/s min'].should == 9.2
52
- @〉['replies/s avg'].should == 9.9
53
- @〉['replies/s max'].should == 10.0
54
- @〉['replies/s stddev'].should == 0.3
57
+ @⚋♉['replies/s min'].should == 9.2
58
+ @⚋♉['replies/s avg'].should == 9.9
59
+ @⚋♉['replies/s max'].should == 10.0
60
+ @⚋♉['replies/s stddev'].should == 0.3
55
61
  end
56
62
 
57
63
  it "should parse the 'Reply time' line correctly" do
58
- @〉['reply time response'].should == 88.1
59
- @〉['reply time transfer'].should == 302.9
64
+ @⚋♉['reply time response'].should == 88.1
65
+ @⚋♉['reply time transfer'].should == 302.9
60
66
  end
61
67
 
62
68
  it "should parse the 'Reply size' line correctly" do
63
- @〉['reply size header'].should == 274.0
64
- @〉['reply size content'].should == 54744.0
65
- @〉['reply size footer'].should == 2.0
66
- @〉['reply size total'].should == 55020.0
69
+ @⚋♉['reply size header'].should == 274.0
70
+ @⚋♉['reply size content'].should == 54744.0
71
+ @⚋♉['reply size footer'].should == 2.0
72
+ @⚋♉['reply size total'].should == 55020.0
67
73
  end
68
74
 
69
75
  it "should parse the 'Reply status' line correctly" do
70
- @〉['status 1xx'].should == 1
71
- @〉['status 2xx'].should == 500
72
- @〉['status 3xx'].should == 3
73
- @〉['status 4xx'].should == 4
74
- @〉['status 5xx'].should == 5
76
+ @⚋♉['status 1xx'].should == 1
77
+ @⚋♉['status 2xx'].should == 500
78
+ @⚋♉['status 3xx'].should == 3
79
+ @⚋♉['status 4xx'].should == 4
80
+ @⚋♉['status 5xx'].should == 5
75
81
  end
76
82
 
77
83
  it "should parse the 'CPU time' line correctly" do
78
- @〉['cpu time user'].should == 15.65
79
- @〉['cpu time system'].should == 34.65
80
- @〉['cpu time user %'].should == 31.1
81
- @〉['cpu time system %'].should == 68.8
82
- @〉['cpu time total %'].should == 99.9
84
+ @⚋♉['cpu time user'].should == 15.65
85
+ @⚋♉['cpu time system'].should == 34.65
86
+ @⚋♉['cpu time user %'].should == 31.1
87
+ @⚋♉['cpu time system %'].should == 68.8
88
+ @⚋♉['cpu time total %'].should == 99.9
83
89
  end
84
90
 
85
91
  it "should parse the 'Net I/O' line correctly" do
86
- @〉['net i/o (KB/s)'].should == 534.1
92
+ @⚋♉['net i/o (KB/s)'].should == 534.1
87
93
  end
88
94
 
89
95
  it "should parse the first 'Errors' line correctly" do
90
- @〉['errors total'].should == 1234
91
- @〉['errors client-timo'].should == 2345
92
- @〉['errors socket-timo'].should == 3456
93
- @〉['errors connrefused'].should == 4567
94
- @〉['errors connreset'].should == 5678
96
+ @⚋♉['errors total'].should == 1234
97
+ @⚋♉['errors client-timo'].should == 2345
98
+ @⚋♉['errors socket-timo'].should == 3456
99
+ @⚋♉['errors connrefused'].should == 4567
100
+ @⚋♉['errors connreset'].should == 5678
95
101
  end
96
102
 
97
103
  it "should parse the second 'Errors' line correctly" do
98
- @〉['errors fd-unavail'].should == 1
99
- @〉['errors addrunavail'].should == 2
100
- @〉['errors ftab-full'].should == 3
101
- @〉['errors other'].should == 4
104
+ @⚋♉['errors fd-unavail'].should == 1
105
+ @⚋♉['errors addrunavail'].should == 2
106
+ @⚋♉['errors ftab-full'].should == 3
107
+ @⚋♉['errors other'].should == 4
102
108
  end
103
109
 
104
110
  it "should parse the 'Session rate' line correctly" do
105
- @〉['session rate min'].should == 35.80
106
- @〉['session rate avg'].should == 37.04
107
- @〉['session rate max'].should == 38.20
108
- @〉['session rate stddev'].should == 0.98
109
- @〉['session rate quota'].should == "1000/1000"
111
+ @⚋♉['session rate min'].should == 35.80
112
+ @⚋♉['session rate avg'].should == 37.04
113
+ @⚋♉['session rate max'].should == 38.20
114
+ @⚋♉['session rate stddev'].should == 0.98
115
+ @⚋♉['session rate quota'].should == "1000/1000"
110
116
  end
111
117
 
112
118
  it "should parse the 'Session' line correctly" do
113
- @〉['session avg conns/sess'].should == 2.00
119
+ @⚋♉['session avg conns/sess'].should == 2.00
114
120
  end
115
121
 
116
122
  it "should parse the 'Session lifetime' line correctly" do
117
- @〉['session lifetime [s]'].should == 0.3
123
+ @⚋♉['session lifetime [s]'].should == 0.3
118
124
  end
119
125
 
120
126
  it "should parse the 'Session failtime' line correctly" do
121
- @〉['session failtime [s]'].should == 0.0
127
+ @⚋♉['session failtime [s]'].should == 0.0
122
128
  end
123
129
 
124
130
  it "should parse the 'Session length histogram' correctly" do
125
- @〉['session length histogram'].should == "0 0 1000"
131
+ @⚋♉['session length histogram'].should == "0 0 1000"
126
132
  end
133
+
134
+ it "should add a started at timestamp for each rate" do
135
+ @⚋♉['started at'].should == "Tue Nov 30 15:49:08 0100 2010"
136
+ end
137
+
127
138
  end
128
139
  end
129
140
 
data/stresser.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{stresser}
8
- s.version = "0.3.2"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jannis Hermanns"]
12
- s.date = %q{2010-11-16}
12
+ s.date = %q{2010-11-30}
13
13
  s.description = %q{Wrapper around httperf for stresstesting your app. Runs httperf multiple times with different concurrency levels and generates an executive summary™ in .csv"}
14
14
  s.email = %q{jannis@moviepilot.com}
15
15
  s.executables = ["stresser", "stresser-grapher", "stresser-loggen"]
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.files = [
21
21
  ".document",
22
22
  ".gitignore",
23
+ "HISTORY.markdown",
23
24
  "LICENSE",
24
25
  "README.markdown",
25
26
  "Rakefile",
@@ -27,13 +28,13 @@ Gem::Specification.new do |s|
27
28
  "bin/stresser",
28
29
  "bin/stresser-grapher",
29
30
  "bin/stresser-loggen",
30
- "build/stresser-0.3.2.gem",
31
+ "build/stresser-0.4.0.gem",
31
32
  "lib/grapher.rb",
32
33
  "lib/httperf.rb",
33
34
  "lib/mp_perf.rb",
34
35
  "lib/reports.yml",
35
36
  "sample.conf",
36
- "spec/httperf_output.txt",
37
+ "spec/httperf_session_based_output.txt",
37
38
  "spec/lib/httperf_spec.rb",
38
39
  "spec/lib/mp_perf_spec.rb",
39
40
  "spec/spec.opts",
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stresser
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 3
9
- - 2
10
- version: 0.3.2
8
+ - 4
9
+ - 0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jannis Hermanns
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-16 00:00:00 +01:00
18
+ date: 2010-11-30 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -88,6 +88,7 @@ extra_rdoc_files:
88
88
  files:
89
89
  - .document
90
90
  - .gitignore
91
+ - HISTORY.markdown
91
92
  - LICENSE
92
93
  - README.markdown
93
94
  - Rakefile
@@ -95,13 +96,13 @@ files:
95
96
  - bin/stresser
96
97
  - bin/stresser-grapher
97
98
  - bin/stresser-loggen
98
- - build/stresser-0.3.2.gem
99
+ - build/stresser-0.4.0.gem
99
100
  - lib/grapher.rb
100
101
  - lib/httperf.rb
101
102
  - lib/mp_perf.rb
102
103
  - lib/reports.yml
103
104
  - sample.conf
104
- - spec/httperf_output.txt
105
+ - spec/httperf_session_based_output.txt
105
106
  - spec/lib/httperf_spec.rb
106
107
  - spec/lib/mp_perf_spec.rb
107
108
  - spec/spec.opts
Binary file