stella 0.5.3 → 0.5.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.
Files changed (88) hide show
  1. data/{README.txt → README.textile} +63 -40
  2. data/Rakefile +7 -5
  3. data/bin/stella +1 -1
  4. data/bin/stella.bat +12 -0
  5. data/lib/pcaplet.rb +180 -0
  6. data/lib/stella/adapter/ab.rb +57 -33
  7. data/lib/stella/adapter/base.rb +11 -1
  8. data/lib/stella/adapter/httperf.rb +13 -10
  9. data/lib/stella/adapter/pcap_watcher.rb +221 -0
  10. data/lib/stella/adapter/proxy_watcher.rb +76 -0
  11. data/lib/stella/adapter/siege.rb +28 -11
  12. data/lib/stella/cli/agents.rb +2 -2
  13. data/lib/stella/cli/base.rb +37 -1
  14. data/lib/stella/cli/localtest.rb +1 -2
  15. data/lib/stella/cli/sysinfo.rb +17 -0
  16. data/lib/stella/cli/watch.rb +278 -0
  17. data/lib/stella/cli.rb +23 -11
  18. data/lib/stella/command/base.rb +1 -10
  19. data/lib/stella/command/localtest.rb +43 -23
  20. data/lib/stella/data/domain.rb +75 -0
  21. data/lib/stella/data/http.rb +124 -0
  22. data/lib/stella/logger.rb +16 -5
  23. data/lib/stella/storable.rb +4 -2
  24. data/lib/stella/support.rb +71 -0
  25. data/lib/stella/sysinfo.rb +247 -0
  26. data/lib/stella/test/base.rb +5 -1
  27. data/lib/stella/test/definition.rb +1 -1
  28. data/lib/stella/test/run/summary.rb +14 -4
  29. data/lib/stella/text/resource.rb +0 -1
  30. data/lib/stella.rb +28 -10
  31. data/lib/utils/domainutil.rb +47 -0
  32. data/lib/utils/fileutil.rb +22 -3
  33. data/lib/utils/httputil.rb +184 -128
  34. data/lib/utils/mathutil.rb +20 -7
  35. data/lib/win32/Console/ANSI.rb +305 -0
  36. data/lib/win32/Console.rb +970 -0
  37. data/spec/show-agents_spec.rb +0 -0
  38. data/support/kvm.h +91 -0
  39. data/support/ruby-pcap-takuma-notes.txt +19 -0
  40. data/support/ruby-pcap-takuma-patch.txt +30 -0
  41. data/support/text/en.yaml +26 -3
  42. data/vendor/frylock/README.textile +72 -0
  43. data/vendor/frylock/bin/example +170 -0
  44. data/vendor/frylock/frylock.gemspec +18 -0
  45. data/vendor/frylock/lib/frylock/exceptions.rb +24 -0
  46. data/vendor/frylock/lib/frylock.rb +232 -0
  47. data/vendor/frylock/test/command_test.rb +33 -0
  48. data/vendor/hitimes-0.4.0/HISTORY +28 -0
  49. data/vendor/hitimes-0.4.0/LICENSE.txt +19 -0
  50. data/vendor/hitimes-0.4.0/README +80 -0
  51. data/vendor/hitimes-0.4.0/Rakefile +63 -0
  52. data/vendor/hitimes-0.4.0/examples/benchmarks.rb +86 -0
  53. data/vendor/hitimes-0.4.0/examples/stats.rb +29 -0
  54. data/vendor/hitimes-0.4.0/ext/extconf.rb +15 -0
  55. data/vendor/hitimes-0.4.0/ext/hitimes_ext.c +21 -0
  56. data/vendor/hitimes-0.4.0/ext/hitimes_instant_clock_gettime.c +20 -0
  57. data/vendor/hitimes-0.4.0/ext/hitimes_instant_osx.c +16 -0
  58. data/vendor/hitimes-0.4.0/ext/hitimes_instant_windows.c +27 -0
  59. data/vendor/hitimes-0.4.0/ext/hitimes_interval.c +340 -0
  60. data/vendor/hitimes-0.4.0/ext/hitimes_interval.h +73 -0
  61. data/vendor/hitimes-0.4.0/ext/hitimes_stats.c +242 -0
  62. data/vendor/hitimes-0.4.0/ext/hitimes_stats.h +30 -0
  63. data/vendor/hitimes-0.4.0/ext/rbconfig-mingw.rb +178 -0
  64. data/vendor/hitimes-0.4.0/ext/rbconfig.rb +178 -0
  65. data/vendor/hitimes-0.4.0/gemspec.rb +54 -0
  66. data/vendor/hitimes-0.4.0/lib/hitimes/mutexed_stats.rb +23 -0
  67. data/vendor/hitimes-0.4.0/lib/hitimes/paths.rb +54 -0
  68. data/vendor/hitimes-0.4.0/lib/hitimes/stats.rb +29 -0
  69. data/vendor/hitimes-0.4.0/lib/hitimes/timer.rb +223 -0
  70. data/vendor/hitimes-0.4.0/lib/hitimes/version.rb +42 -0
  71. data/vendor/hitimes-0.4.0/lib/hitimes.rb +24 -0
  72. data/vendor/hitimes-0.4.0/spec/interval_spec.rb +115 -0
  73. data/vendor/hitimes-0.4.0/spec/mutex_stats_spec.rb +34 -0
  74. data/vendor/hitimes-0.4.0/spec/paths_spec.rb +14 -0
  75. data/vendor/hitimes-0.4.0/spec/spec_helper.rb +6 -0
  76. data/vendor/hitimes-0.4.0/spec/stats_spec.rb +72 -0
  77. data/vendor/hitimes-0.4.0/spec/timer_spec.rb +105 -0
  78. data/vendor/hitimes-0.4.0/spec/version_spec.rb +27 -0
  79. data/vendor/hitimes-0.4.0/tasks/announce.rake +39 -0
  80. data/vendor/hitimes-0.4.0/tasks/config.rb +107 -0
  81. data/vendor/hitimes-0.4.0/tasks/distribution.rake +53 -0
  82. data/vendor/hitimes-0.4.0/tasks/documentation.rake +33 -0
  83. data/vendor/hitimes-0.4.0/tasks/extension.rake +64 -0
  84. data/vendor/hitimes-0.4.0/tasks/rspec.rake +31 -0
  85. data/vendor/hitimes-0.4.0/tasks/rubyforge.rake +52 -0
  86. data/vendor/hitimes-0.4.0/tasks/utils.rb +80 -0
  87. data/vendor/useragent/lib/user_agent.rb +1 -1
  88. metadata +87 -8
@@ -0,0 +1,221 @@
1
+
2
+ require 'webrick'
3
+ require 'stringio'
4
+ require 'net/dns/packet'
5
+
6
+
7
+ module Stella
8
+ module Adapter
9
+ # Make sure Stella's lib directory is before the system defined ones.
10
+ # We are using a modified version of pcaplet.rb.
11
+ require 'pcap'
12
+ require 'observer'
13
+
14
+ # Stella::Adapter::PcapWatcher
15
+ #
16
+ # Record HTTP or DNS events with Pcap (TCP sniffer). This requires ruby-pcap and the C pcap
17
+ # library as well as root acceess (TCP packet sniffing requires root privileges). If you're
18
+ # running Ruby 1.9, JRuby, or Windows this will not be available on your system.
19
+ # To sniff traffic, you must be on either the machine sending the requests or the machine
20
+ # receiving the requests.
21
+ class PcapWatcher
22
+ include Observable
23
+
24
+ # Building Ruby::Pcap with Ruby 1.9.1
25
+ # RSTRING()->len ia now RSTRING_LEN(), ...
26
+ # see: http://gnufied.org/2007/12/21/mysql-c-bindings-for-ruby-19/#comment-3133
27
+ # see: http://www.rubyinside.com/ruby-1-9-1-preview-released-why-its-a-big-deal-1280.html#comment-37223
28
+ # TRAP_BEG and TRAP_END are also fucked. But the fix is not clear.
29
+ # Basically Ruby::PCap is not ready for 1.9
30
+ # See: http://d.hatena.ne.jp/takuma104/20080210/1202638583
31
+
32
+ # Network interface device ID. eri0, en0, lo0, etc... /sbin/ifconfig -a will tell you.
33
+ attr_accessor :device
34
+ # Buffer size, in bytes, to read from each packet (default: 1500)
35
+ attr_accessor :snaplen
36
+ # Port of the machine sending requests (default: 80)
37
+ attr_accessor :sport
38
+ # Port of the machine receiving requests (default: 80)
39
+ attr_accessor :dport
40
+ # dns or http
41
+ attr_accessor :service
42
+ # udp or tcp
43
+ attr_accessor :protocol
44
+ # Maximum number of packets to sniff
45
+ attr_accessor :maxpacks
46
+
47
+ attr_reader :pcaplet
48
+
49
+ def initialize(options={})
50
+ # The proper service name for dns is "domain"
51
+ @service = options[:service] || 'http'
52
+ @service = 'domain' if options[:service] == 'dns'
53
+
54
+ if @service == 'domain'
55
+ @protocol = 'udp'
56
+ else
57
+ @protocol = options[:protocol] || 'tcp'
58
+ end
59
+
60
+ @dport = options[:port] || Socket::getservbyname(@service)
61
+ @sport = options[:port] || @dport
62
+
63
+ @device = options[:device] || guess_device
64
+ @snaplen = options[:snaplen] || 1500 # 10KB
65
+ @maxpacks = options[:maxpacks] || 100_000
66
+
67
+ Stella::LOGGER.info("Watching interface #{@device} for #{@service} activity on #{@protocol} port #{@dport}")
68
+ end
69
+
70
+ def guess_device
71
+ # NOTE: This should be passed in as a value, not called from the global
72
+ case Stella::SYSINFO.implementation
73
+ when :osx
74
+ "en1" # Pcap.lookupdev returns en0
75
+ else
76
+ Pcap.lookupdev
77
+ end
78
+ end
79
+
80
+ def run
81
+
82
+ if (respond_to? "monitor_#{@service}")
83
+ self.send("monitor_#{@service}")
84
+ else
85
+ raise "Unknown service type (#{@service})"
86
+ end
87
+
88
+ end
89
+
90
+
91
+
92
+ # monitor_domain
93
+ #
94
+ # Use Ruby-Pcap to sniff packets off the network interface.
95
+ #
96
+ # DNS monitor based on: http://www.linuxjournal.com/article/9614
97
+ # Install http://rubyforge.org/projects/net-dns
98
+ #
99
+ # NOTE: Is there a better way to match up a request packet with a
100
+ # response packet?
101
+ # We keep connect a request with the response using the domain name.
102
+ # It's possible that two (or more) requests to be made for the same domain
103
+ # at the same time and the responses could be mixed up. This will affect
104
+ # the exact response time but probably not by much.
105
+ def monitor_domain
106
+
107
+ @pcaplet = Pcaplet.new(:device => @device, :count => @maxpacks)
108
+
109
+ req_filter = Pcap::Filter.new("#{@protocol} and dst port #{@dport}", @pcaplet.capture)
110
+ resp_filter = Pcap::Filter.new("#{@protocol} and src port #{@dport}", @pcaplet.capture)
111
+ @pcaplet.add_filter(req_filter | resp_filter)
112
+ @pcaplet.each_packet do |packet|
113
+ data = packet.udp_data
114
+ case packet
115
+ when req_filter
116
+ dobj = Stella::Data::DomainRequest.new(data)
117
+ dobj.time = packet.time
118
+ dobj.client_ip = packet.ip_src
119
+ dobj.server_ip = packet.ip_dst
120
+
121
+ changed
122
+ notify_observers(:domain_request, dobj)
123
+
124
+ when resp_filter
125
+ dobj = Stella::Data::DomainResponse.new(data)
126
+ dobj.time = packet.time
127
+ dobj.client_ip = packet.ip_dst
128
+ dobj.server_ip = packet.ip_src
129
+
130
+ changed
131
+ notify_observers(:domain_response, dobj)
132
+ end
133
+
134
+ end
135
+
136
+ rescue Interrupt
137
+ after
138
+ exit
139
+ end
140
+
141
+
142
+ def monitor_http
143
+
144
+ @pcaplet = Pcaplet.new(:device => @device, :count => @maxpacks)
145
+
146
+ begin
147
+ req_filter = Pcap::Filter.new("#{@protocol} and dst port #{@dport}", @pcaplet.capture)
148
+ resp_filter = Pcap::Filter.new("#{@protocol} and src port #{@sport}", @pcaplet.capture)
149
+ @pcaplet.add_filter(req_filter | resp_filter)
150
+ @pcaplet.each_packet do |packet|
151
+ data = packet.tcp_data
152
+ next if data.nil?
153
+
154
+
155
+ # NOTE: With HTTP 1.1 keep alive connections, multiple requests can be passed
156
+ # through single connection. This makes it difficult to match responses with
157
+ # requests.
158
+ # NOTE: We don't parse the body of POST and PUT requests because the data can
159
+ # be (and likely is), split across numerous packets. We also only grab 1500
160
+ # bytes from each packet.
161
+ # NOTE: The hostname is taken from the Host header. Requests made without
162
+ # this header (including HTTP 1.0) will contain the local hostname instead.
163
+ # TODO: resolve the hostname from the IP address.
164
+ # There are some helpful methods for doing some of this stuff:
165
+ # http://www.goto.info.waseda.ac.jp/~fukusima/ruby/pcap/doc/TCPPacket.html
166
+ case packet
167
+ when req_filter
168
+ next unless data and data =~ /^(GET|POST|HEAD|DELETE|PUT)\s+(.+?)\s+(HTTP.+?)$/
169
+ dobj = Stella::Data::HTTPRequest.new(data.gsub(/\r?\n/, $/)) # Use the system's line terminators
170
+ dobj.time = packet.time
171
+ dobj.client_ip = packet.ip_src
172
+ dobj.server_ip = packet.ip_dst
173
+
174
+ changed
175
+ notify_observers(:http_request, dobj)
176
+
177
+ when resp_filter
178
+ # NOTE: Some responses do not contain a body in the first packet.
179
+ # TODO: investigate further. Try: http://www.ruby-doc.org/core/classes/Enumerable.html
180
+ next unless data and data =~ /^(HTTP.+)$/
181
+ dobj = Stella::Data::HTTPResponse.new(data.gsub(/\r?\n/, $/))
182
+ dobj.time = packet.time
183
+ dobj.client_ip = packet.ip_dst
184
+ dobj.server_ip = packet.ip_src
185
+
186
+ changed
187
+ notify_observers(:http_response, dobj)
188
+
189
+ end
190
+
191
+ end
192
+ rescue Interrupt
193
+ after
194
+ exit
195
+ rescue => ex
196
+ Stella::LOGGER.error(ex)
197
+ end
198
+
199
+
200
+ end
201
+
202
+ def after
203
+ STDOUT.flush
204
+ stat = @pcaplet.capture.stats
205
+ if stat
206
+ Stella::LOGGER.info("#{$/}#{stat.recv} packets received by filter");
207
+ Stella::LOGGER.info("#{stat.drop} packets dropped by kernel", ''); # with an extra line
208
+ end
209
+ STDOUT.flush
210
+ @pcaplet.capture.close
211
+ delete_observers
212
+ rescue
213
+
214
+ # Ignore errors
215
+ end
216
+
217
+
218
+ end
219
+ end
220
+ end
221
+
@@ -0,0 +1,76 @@
1
+
2
+ require 'webrick/httpproxy'
3
+ require 'observer'
4
+
5
+
6
+ module Stella
7
+ module Adapter
8
+
9
+ # Stella::Adapter::ProxyWatcher
10
+ #
11
+ # Starts up an HTTP proxy using WEBrick to record HTTP events. This is used
12
+ # when PcapRecorder is not available.
13
+ class ProxyWatcher
14
+ include Observable
15
+
16
+ attr_accessor :port
17
+
18
+ def initialize(options={})
19
+ @port = options[:port]
20
+ end
21
+ require 'pp'
22
+ def run
23
+
24
+ @server = WEBrick::HTTPProxyServer.new(
25
+ :Port => @port || 3114,
26
+ :AccessLog => [],
27
+ :ProxyContentHandler => Proc.new do |req,res|
28
+
29
+ begin
30
+ res_obj = Stella::Data::HTTPResponse.new(res.to_s)
31
+ res_obj.time = Time.now
32
+
33
+ req_obj = Stella::Data::HTTPRequest.new(req.to_s)
34
+ req_obj.time = Time.now
35
+ req_obj.client_ip = '0.0.0.0'
36
+ req_obj.server_ip = '0.0.0.0'
37
+
38
+ req_obj.response = res_obj
39
+
40
+ changed
41
+ notify_observers(:http_request, req_obj)
42
+
43
+ rescue SystemExit => ex
44
+ after
45
+
46
+ rescue Exception => ex
47
+ # There are miscellaneous errors (mostly to do with
48
+ # incorrect content-length) that we don't care about.
49
+ Stella::LOGGER.error(ex.message)
50
+ end
51
+
52
+ end
53
+ )
54
+
55
+ # We need to trap this INT to kill WEBrick. Unlike with Pcap,
56
+ # rescuing Interrupt doesn't work.
57
+
58
+ trap('INT') do
59
+ after
60
+ end
61
+
62
+ @server.start
63
+ after
64
+
65
+ rescue => ex
66
+ after
67
+ end
68
+
69
+ def after
70
+ delete_observers
71
+ @server.shutdown
72
+ end
73
+
74
+ end
75
+ end
76
+ end
@@ -32,27 +32,37 @@ module Stella
32
32
  # -A, --user-agent="text" Sets User-Agent in request
33
33
  class Siege < Stella::Adapter::Base
34
34
 
35
-
36
- attr_accessor :version, :help, :config, :verbose, :get, :log, :mark, :delay, :header, :user_agent
37
- attr_accessor :reps, :concurrent, :rc, :file, :time, :benchmark, :internet
35
+ attr_writer :reps, :concurrent, :version
36
+ attr_reader :user_agent
37
+ attr_accessor :help, :config, :verbose, :get, :log, :mark, :delay, :header
38
+ attr_accessor :rc, :file, :time, :benchmark, :internet
38
39
 
39
40
  def initialize(options={}, arguments=[])
40
41
  super(options, arguments)
41
42
  @name = 'siege'
42
43
  @reps = 1
43
44
  @concurrent = 1
44
- @rc = File.join(ENV['HOME'], '.siegerc')
45
45
  @private_variables = ['private_variables', 'name', 'arguments', 'load_factor', 'working_directory', 'orig_logfile']
46
46
  @load_factor = 1
47
+
48
+ @rc = File.join(ENV['HOME'] || ENV['USERPROFILE'], '.siegerc')
49
+
50
+ # Siege won't run unless there's a siegerc file. If the default one doesn't exist
51
+ # we need to call siege.config to create it. This should only happen once.
52
+ # We use capture_output here so STDOUT and STDERR don't print to the screen.
53
+ Stella::Util.capture_output("#{@name}.config") do 'nothing' end unless File.exists? @rc
47
54
  end
48
55
 
49
56
 
57
+
58
+ def error
59
+ (File.exists? stderr_path) ? FileUtil.read_file(stderr_path) : "Unknown error"
60
+ end
61
+
50
62
  def version
51
63
  vsn = 0
52
- text = ""
53
- Open3.popen3("#{@name} --version") do |stdin, stdout, stderr|
54
- text = stderr.readlines.join
55
- text.scan(/SIEGE (\d+?\.\d+)/) { |v| vsn = v[0] }
64
+ Stella::Util.capture_output("#{@name} --version") do |stdout, stderr|
65
+ stderr.join.scan(/SIEGE (\d+?\.\d+)/) { |v| vsn = v[0] }
56
66
  end
57
67
  vsn
58
68
  end
@@ -137,8 +147,15 @@ module Stella
137
147
  opts.on('-i', '--internet') do |v| @internet = true; end
138
148
  opts.on('-A S', '--user-agent=S', String) do |v| @user_agent ||= []; @user_agent << v end
139
149
 
140
- raise "You cannot select both --internet and --benchmark" if options.internet && options.benchmark
141
-
150
+ unless options.benchmark
151
+ Stella::LOGGER.warn('--benchmark (or -b) is not selected. Siege will include "think-time" for all requests.')
152
+ end
153
+
154
+ opts.on('-n N',Integer) do |v|
155
+ Stella::LOGGER.error("-n is not a Siege parameter. You probably want -r.")
156
+ exit 1
157
+ end
158
+
142
159
  # parse! removes the options it finds.
143
160
  # It also fails when it finds unknown switches (i.e. -X)
144
161
  # Which should leave only the remaining arguments (URIs in this case)
@@ -257,7 +274,7 @@ module Stella
257
274
 
258
275
  # Siege writes the summary to STDERR
259
276
  def stats_file
260
- File.new(stderr_path)
277
+ File.new(stderr_path) if File.exists?(stderr_path)
261
278
  end
262
279
 
263
280
  def rc_file
@@ -42,8 +42,8 @@ module Stella
42
42
  end
43
43
  end
44
44
 
45
- puts (@list) ? agents.uniq.sort.join("\n") : Stella::TEXT.msg(:agents_count_message, agents.uniq.size)
46
-
45
+ msg = (@list) ? agents.uniq.sort.join("\n") : Stella::TEXT.msg(:agents_count_message, agents.uniq.size)
46
+ puts msg
47
47
  end
48
48
 
49
49
  def process_options(display=false)
@@ -13,7 +13,43 @@ module Stella
13
13
  def initialize(adapter)
14
14
  @adapter_name = adapter
15
15
  @options = OpenStruct.new
16
+
17
+ # There is a bug in Ruby 1.8.6 where a trapped SIGINT will hang.
18
+ # This workaround is from: http://redmine.ruby-lang.org/issues/show/362
19
+ # It works in Ruby 1.9 and JRuby as well.
20
+ # NEW WARNING: This puts the process into a new thread which somehow
21
+ # prevents Pcap from reporting on UDP/DNS packets (TCP/HTTP is unaffected).
22
+ # I left this here as an example of how not to it. Incidentally,
23
+ # "rescue Interrupt" seems to be working fine now.
24
+ # See also: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/220937
25
+ #@killer = Thread.new do
26
+ # puts "#{$/}Exiting...#{$/}"
27
+ # Thread.stop
28
+ # Thread.main.join(1)
29
+ # exit 0
30
+ #end
31
+ #
32
+ # Note that this is just a default. Any class that implements another
33
+ # Signal.trap will override this.
34
+ #Signal.trap('INT') do
35
+ # @killer.call
36
+ # exit 0
37
+ #end
16
38
  end
17
39
  end
18
40
  end
19
- end
41
+ end
42
+
43
+ __END__
44
+
45
+ TODO: Investigate frylock style definition
46
+ include Stella::CLI::Base
47
+
48
+ before do
49
+ # stuff that would go in initialize
50
+ end
51
+
52
+ command 'sysinfo' do
53
+ puts Stella::SYSINFO.to_yaml(:headers)
54
+ end
55
+
@@ -45,7 +45,6 @@ module Stella
45
45
  @driver.working_directory = @working_directory
46
46
 
47
47
  @driver.run
48
-
49
48
  end
50
49
 
51
50
 
@@ -54,7 +53,7 @@ module Stella
54
53
  #
55
54
  # Populates @testdef with values from @stella_options
56
55
  def process_stella_options
57
- @testdef.repetitions = @stella_options.testreps
56
+ @testdef.repetitions = @stella_options.repetitions
58
57
  @testdef.sleep = @stella_options.sleep
59
58
  @testdef.warmup = @stella_options.warmup
60
59
  @testdef.rampup = @stella_options.rampup
@@ -0,0 +1,17 @@
1
+
2
+ module Stella
3
+ class CLI
4
+ class SystemInfo < Stella::CLI::Base
5
+
6
+
7
+ def run
8
+ puts Stella::SYSINFO
9
+ #puts "HI"
10
+ end
11
+
12
+ end
13
+
14
+ @@commands['sysinfo'] = Stella::CLI::SystemInfo
15
+ end
16
+ end
17
+