solutious-stella 0.5.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +36 -0
- data/README.textile +162 -0
- data/Rakefile +88 -0
- data/bin/stella +12 -0
- data/bin/stella.bat +12 -0
- data/lib/daemonize.rb +56 -0
- data/lib/pcaplet.rb +180 -0
- data/lib/stella.rb +101 -0
- data/lib/stella/adapter/ab.rb +337 -0
- data/lib/stella/adapter/base.rb +106 -0
- data/lib/stella/adapter/httperf.rb +305 -0
- data/lib/stella/adapter/pcap_watcher.rb +221 -0
- data/lib/stella/adapter/proxy_watcher.rb +76 -0
- data/lib/stella/adapter/siege.rb +341 -0
- data/lib/stella/cli.rb +258 -0
- data/lib/stella/cli/agents.rb +73 -0
- data/lib/stella/cli/base.rb +55 -0
- data/lib/stella/cli/language.rb +18 -0
- data/lib/stella/cli/localtest.rb +78 -0
- data/lib/stella/cli/sysinfo.rb +16 -0
- data/lib/stella/cli/watch.rb +278 -0
- data/lib/stella/command/base.rb +40 -0
- data/lib/stella/command/localtest.rb +358 -0
- data/lib/stella/data/domain.rb +82 -0
- data/lib/stella/data/http.rb +131 -0
- data/lib/stella/logger.rb +84 -0
- data/lib/stella/response.rb +85 -0
- data/lib/stella/storable.rb +201 -0
- data/lib/stella/support.rb +276 -0
- data/lib/stella/sysinfo.rb +257 -0
- data/lib/stella/test/definition.rb +79 -0
- data/lib/stella/test/run/summary.rb +70 -0
- data/lib/stella/test/stats.rb +114 -0
- data/lib/stella/text.rb +64 -0
- data/lib/stella/text/resource.rb +38 -0
- data/lib/utils/crypto-key.rb +84 -0
- data/lib/utils/domainutil.rb +47 -0
- data/lib/utils/escape.rb +302 -0
- data/lib/utils/fileutil.rb +78 -0
- data/lib/utils/httputil.rb +266 -0
- data/lib/utils/mathutil.rb +15 -0
- data/lib/utils/stats.rb +88 -0
- data/lib/utils/textgraph.rb +267 -0
- data/lib/utils/timerutil.rb +58 -0
- data/lib/win32/Console.rb +970 -0
- data/lib/win32/Console/ANSI.rb +305 -0
- data/support/kvm.h +91 -0
- data/support/ruby-pcap-takuma-notes.txt +19 -0
- data/support/ruby-pcap-takuma-patch.txt +30 -0
- data/support/text/en.yaml +80 -0
- data/support/text/nl.yaml +7 -0
- data/support/useragents.txt +75 -0
- data/tests/01-util_test.rb +0 -0
- data/tests/02-stella-util_test.rb +42 -0
- data/tests/10-stella_test.rb +104 -0
- data/tests/11-stella-storable_test.rb +68 -0
- data/tests/60-stella-command_test.rb +248 -0
- data/tests/80-stella-cli_test.rb +45 -0
- data/tests/spec-helper.rb +31 -0
- metadata +165 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
module Stella
|
5
|
+
class CLI
|
6
|
+
|
7
|
+
class Agents < Stella::CLI::Base
|
8
|
+
|
9
|
+
attr_accessor :full
|
10
|
+
attr_accessor :list
|
11
|
+
attr_accessor :search
|
12
|
+
attr_accessor :help
|
13
|
+
|
14
|
+
def initialize(adapter)
|
15
|
+
super(adapter)
|
16
|
+
@full = false
|
17
|
+
@list = false
|
18
|
+
@help = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
process_options
|
23
|
+
|
24
|
+
if @help
|
25
|
+
process_options(:display)
|
26
|
+
return
|
27
|
+
end
|
28
|
+
|
29
|
+
# The LocalTest command is the keeper of the user agents
|
30
|
+
localtest = Stella::LocalTest.new
|
31
|
+
|
32
|
+
agents = []
|
33
|
+
all_agents = localtest.available_agents
|
34
|
+
all_agents.each_pair do |key,value|
|
35
|
+
if (@full)
|
36
|
+
value.each do |full_value|
|
37
|
+
agent = full_value.join(' ')
|
38
|
+
agents << agent if (!@search || agent.to_s.match(/#{search}/i))
|
39
|
+
end
|
40
|
+
else
|
41
|
+
agents << key.to_s if (!@search || key.to_s.match(/#{search}/i))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
msg = (@list) ? agents.uniq.sort.join("\n") : Stella::TEXT.msg(:agents_count_message, agents.uniq.size)
|
46
|
+
puts msg
|
47
|
+
end
|
48
|
+
|
49
|
+
def process_options(display=false)
|
50
|
+
|
51
|
+
opts = OptionParser.new
|
52
|
+
opts.banner = Stella::TEXT.msg(:option_help_usage)
|
53
|
+
opts.on('-h', '--help', Stella::TEXT.msg(:option_help_help)) { @help = true }
|
54
|
+
opts.on('-f', '--full', Stella::TEXT.msg(:agents_option_full)) { @full = true }
|
55
|
+
opts.on('-l', '--list', Stella::TEXT.msg(:agents_option_list)) { @list = true }
|
56
|
+
# TODO: display agents based on shortnames. This is important to maintain continuity with the stella option.
|
57
|
+
#opts.on('-a', '--agent', Stella::TEXT.msg(:agents_option_list)) { @list = true }
|
58
|
+
opts.on('-s', '--search=S', String, Stella::TEXT.msg(:agents_option_search)) { |v| @search = v }
|
59
|
+
|
60
|
+
opts.parse!(@arguments)
|
61
|
+
|
62
|
+
if display
|
63
|
+
Stella::LOGGER.info opts
|
64
|
+
return
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
@@commands['agents'] = Stella::CLI::Agents
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Stella
|
2
|
+
class CLI
|
3
|
+
# Stella::CLI::Base
|
4
|
+
#
|
5
|
+
# A base case for the command line interface classes. All Stella::CLI
|
6
|
+
# classes should be based on this class. Otherwise great destruction could occur.
|
7
|
+
class Base
|
8
|
+
attr_reader :adapter
|
9
|
+
attr_accessor :stella_options
|
10
|
+
attr_accessor :arguments
|
11
|
+
attr_accessor :working_directory
|
12
|
+
|
13
|
+
def initialize(adapter)
|
14
|
+
@adapter_name = adapter
|
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
|
38
|
+
end
|
39
|
+
end
|
40
|
+
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
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
module Stella
|
5
|
+
class CLI
|
6
|
+
class Language < Stella::CLI::Base
|
7
|
+
|
8
|
+
|
9
|
+
def run
|
10
|
+
languages = Stella::TEXT.available_languages
|
11
|
+
puts Stella::TEXT.msg(:text_available_languages, languages.map { |l| "#{l[:language]} " })
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
@@commands['lang'] = Stella::CLI::Language
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Stella
|
4
|
+
class CLI
|
5
|
+
# Stella::CLI::LocalTest
|
6
|
+
#
|
7
|
+
# A wrapper that takes the command line input and makes it appropriate for
|
8
|
+
# calling an instance of Stella::LocalTest. Then it calls that instance!
|
9
|
+
class LocalTest < Stella::CLI::Base
|
10
|
+
|
11
|
+
attr_reader :testdef
|
12
|
+
|
13
|
+
def initialize(adapter)
|
14
|
+
super(adapter)
|
15
|
+
@testdef = Stella::Test::Definition.new
|
16
|
+
|
17
|
+
if (adapter == 'ab')
|
18
|
+
@adapter = Stella::Adapter::ApacheBench.new
|
19
|
+
elsif (adapter == 'siege')
|
20
|
+
@adapter = Stella::Adapter::Siege.new
|
21
|
+
elsif (adapter == 'httperf')
|
22
|
+
@adapter = Stella::Adapter::Httperf.new
|
23
|
+
else
|
24
|
+
raise UnknownValue.new(adapter)
|
25
|
+
end
|
26
|
+
|
27
|
+
@driver = Stella::LocalTest.new
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def run
|
32
|
+
process_stella_options
|
33
|
+
|
34
|
+
@adapter.process_arguments(@arguments)
|
35
|
+
|
36
|
+
@adapter.arguments = @arguments
|
37
|
+
|
38
|
+
@testdef.vusers = @adapter.vusers
|
39
|
+
@testdef.requests = @adapter.requests
|
40
|
+
|
41
|
+
@driver.adapter = @adapter
|
42
|
+
@driver.testdef = @testdef
|
43
|
+
|
44
|
+
@driver.working_directory = @working_directory
|
45
|
+
|
46
|
+
@driver.run
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
# process_stella_options
|
52
|
+
#
|
53
|
+
# Populates @testdef with values from @stella_options
|
54
|
+
def process_stella_options
|
55
|
+
@testdef.repetitions = @stella_options.repetitions
|
56
|
+
@testdef.sleep = @stella_options.sleep
|
57
|
+
@testdef.warmup = @stella_options.warmup
|
58
|
+
@testdef.rampup = @stella_options.rampup
|
59
|
+
@testdef.agents = @stella_options.agents
|
60
|
+
@testdef.message = @stella_options.message
|
61
|
+
|
62
|
+
|
63
|
+
@driver.quiet = @stella_options.quiet
|
64
|
+
@driver.verbose = @stella_options.verbose
|
65
|
+
@driver.format = @stella_options.format || 'yaml'
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
@@commands['ab'] = Stella::CLI::LocalTest
|
75
|
+
@@commands['siege'] = Stella::CLI::LocalTest
|
76
|
+
@@commands['httperf'] = Stella::CLI::LocalTest
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,278 @@
|
|
1
|
+
|
2
|
+
# TODO: Record cookies.
|
3
|
+
# TODO: Investigate packetfu: http://code.google.com/p/packetfu/
|
4
|
+
# TODO: Investigate Winpcap (http://www.winpcap.org/) and libpcap on Windows
|
5
|
+
|
6
|
+
module Stella
|
7
|
+
class CLI
|
8
|
+
class Watch < Stella::CLI::Base
|
9
|
+
|
10
|
+
|
11
|
+
def run
|
12
|
+
@options = process_arguments(@arguments)
|
13
|
+
|
14
|
+
if can_pcap?(@options[:usepcap])
|
15
|
+
require 'stella/adapter/pcap_watcher'
|
16
|
+
@watcher = Stella::Adapter::PcapWatcher.new(@options)
|
17
|
+
|
18
|
+
else
|
19
|
+
require 'stella/adapter/proxy_watcher'
|
20
|
+
@watcher = Stella::Adapter::ProxyWatcher.new(@options)
|
21
|
+
|
22
|
+
# if can_pcap? returned false, but pcap was requested then we'll
|
23
|
+
# call check_pcap to raise the reason why it didn't load.
|
24
|
+
if @options[:usepcap]
|
25
|
+
check_pcap
|
26
|
+
exit 0
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# We use an observer model so the watcher class will notify us
|
31
|
+
# when they have new data. They call the update method below.
|
32
|
+
@watcher.add_observer(self)
|
33
|
+
|
34
|
+
if @options[:record]
|
35
|
+
|
36
|
+
@record_filepath = generate_record_filepath
|
37
|
+
Stella::LOGGER.info("Writing to #{@record_filepath}")
|
38
|
+
|
39
|
+
if File.exists?(@record_filepath)
|
40
|
+
Stella::LOGGER.error("#{@record_filepath} exists")
|
41
|
+
if @stella_options.force
|
42
|
+
Stella::LOGGER.error("But I'll overwrite it and continue because you forced me too!")
|
43
|
+
else
|
44
|
+
exit 1
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
Stella::LOGGER.info("Filter: #{@options[:filter]}") if @options[:filter]
|
51
|
+
Stella::LOGGER.info("Domain: #{@options[:domain]}") if @options[:domain]
|
52
|
+
|
53
|
+
# Turn wildcards into regular expressions
|
54
|
+
@options[:filter].gsub!('*', '.*') if @options[:filter]
|
55
|
+
@options[:domain].gsub!('*', '.*') if @options[:domain]
|
56
|
+
|
57
|
+
# Used to calculated user think times for session output
|
58
|
+
@think_time = 0
|
59
|
+
|
60
|
+
@watcher.run
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
# update
|
67
|
+
#
|
68
|
+
# This method is called from the watcher class when data is updated.
|
69
|
+
# +service+ is one of: domain, http_request, http_response
|
70
|
+
# +data+ is a string of TCP packet data. The format depends on the value of +service+.
|
71
|
+
def update(service, data_object)
|
72
|
+
|
73
|
+
begin
|
74
|
+
if @options[:record] && !@file_created_already
|
75
|
+
|
76
|
+
if File.exists?(@record_filepath)
|
77
|
+
if @stella_options.force
|
78
|
+
@record_file = FileUtil.create_file(@record_filepath, 'w', '.', :force)
|
79
|
+
else
|
80
|
+
exit 1
|
81
|
+
end
|
82
|
+
else
|
83
|
+
@record_file = FileUtil.create_file(@record_filepath, 'w', '.')
|
84
|
+
end
|
85
|
+
|
86
|
+
raise StellaError.new("Cannot open: #{@record_filepath}") unless @record_file
|
87
|
+
|
88
|
+
@file_created_already = true
|
89
|
+
end
|
90
|
+
rescue => ex
|
91
|
+
raise StellaError.new("Error creating file: #{ex.message}")
|
92
|
+
end
|
93
|
+
|
94
|
+
# TODO: combine requests and responses
|
95
|
+
# Disabled until we have a way to combine request and response objects (otherwise
|
96
|
+
# the requests are filters out but the responses are not).
|
97
|
+
#return if @options[:filter] && !(data_object.raw_data.to_s =~ /#{@options[:filter]}/i)
|
98
|
+
#return if @options[:domain] && !(data_object.uri.to_s =~ /(www.)?#{@options[:domain]}/i)
|
99
|
+
|
100
|
+
if @stella_options.format && data_object.respond_to?("to_#{@stella_options.format}")
|
101
|
+
Stella::LOGGER.info(data_object.send("to_#{@stella_options.format}"))
|
102
|
+
|
103
|
+
if data_object.has_response?
|
104
|
+
Stella::LOGGER.info(data_object.response.send("to_#{@stella_options.format}"))
|
105
|
+
end
|
106
|
+
|
107
|
+
else
|
108
|
+
if @stella_options.verbose > 1
|
109
|
+
Stella::LOGGER.info(data_object.inspect, '')
|
110
|
+
|
111
|
+
if data_object.has_response?
|
112
|
+
Stella::LOGGER.info(data_object.response.inspect, '', '')
|
113
|
+
end
|
114
|
+
|
115
|
+
elsif @stella_options.verbose > 0
|
116
|
+
Stella::LOGGER.info(data_object.to_s)
|
117
|
+
Stella::LOGGER.info(data_object.body) if data_object.has_body?
|
118
|
+
|
119
|
+
if data_object.has_response?
|
120
|
+
Stella::LOGGER.info(data_object.response.to_s)
|
121
|
+
Stella::LOGGER.info(data_object.response.body) if data_object.response.has_body?
|
122
|
+
end
|
123
|
+
|
124
|
+
else
|
125
|
+
Stella::LOGGER.info(data_object.to_s)
|
126
|
+
Stella::LOGGER.info(data_object.response.to_s) if data_object.has_response?
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
rescue Exception => ex
|
132
|
+
Stella::LOGGER.error(ex)
|
133
|
+
#exit 1
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
# can_pcap?
|
139
|
+
#
|
140
|
+
# Returns true is pcap is available
|
141
|
+
def can_pcap?(usepcap=false)
|
142
|
+
return false unless usepcap
|
143
|
+
begin
|
144
|
+
check_pcap
|
145
|
+
rescue
|
146
|
+
return false
|
147
|
+
end
|
148
|
+
return true
|
149
|
+
end
|
150
|
+
|
151
|
+
# check_pcap
|
152
|
+
#
|
153
|
+
# The Pcap gauntlet. A number of conditions must be met to run the Pcap recorder:
|
154
|
+
# - The watch command must be called with -p
|
155
|
+
# - The OS must be a form of Unix
|
156
|
+
# - Cannot be running via JRuby or Java
|
157
|
+
# - The user must be root (or running through sudo)
|
158
|
+
# - The Ruby-Pcap library must be installed.
|
159
|
+
def check_pcap(usepcap=false)
|
160
|
+
raise MissingDependency.new('pcap', :error_sysinfo_notunix) unless Stella::SYSINFO.os === :unix # pcap not available on windows or java
|
161
|
+
raise MissingDependency.new('pcap', :error_sysinfo_notroot) unless ENV['USER'] === 'root' # Must run as root or sudo
|
162
|
+
begin
|
163
|
+
require 'pcap'
|
164
|
+
rescue Exception, LoadError => ex
|
165
|
+
raise MissingDependency.new('pcap', :error_watch_norubypcap)
|
166
|
+
end
|
167
|
+
false
|
168
|
+
end
|
169
|
+
|
170
|
+
def generate_record_filepath
|
171
|
+
filepath = nil
|
172
|
+
|
173
|
+
if (@options[:record].is_a? String)
|
174
|
+
filepath = File.expand_path(@options[:record])
|
175
|
+
else
|
176
|
+
now = DateTime.now
|
177
|
+
daystr = "#{now.year}-#{now.mon.to_s.rjust(2,'0')}-#{now.mday.to_s.rjust(2,'0')}"
|
178
|
+
dirpath = File.join(@working_directory, 'stories', daystr)
|
179
|
+
|
180
|
+
FileUtil.create_dir(dirpath, ".")
|
181
|
+
filepath = File.join(dirpath, 'story')
|
182
|
+
testnum = 1.to_s.rjust(2,'0')
|
183
|
+
testnum.succ! while(File.exists? "#{filepath}-#{testnum}.txt")
|
184
|
+
filepath = "#{filepath}-#{testnum}.txt"
|
185
|
+
end
|
186
|
+
|
187
|
+
return filepath
|
188
|
+
end
|
189
|
+
|
190
|
+
def after
|
191
|
+
# Close Pcap / Shutdown Proxy
|
192
|
+
@watcher.after
|
193
|
+
|
194
|
+
# We don't need to close or delete a file that wasn't created
|
195
|
+
return unless @record_file
|
196
|
+
|
197
|
+
# And we don't want to delete a file that we're overwriting but may
|
198
|
+
# not have actually written anything to yet. IOW, original file will
|
199
|
+
# remain intact if we haven't written anything to it yet.
|
200
|
+
@record_file.close if @forced_overwrite
|
201
|
+
|
202
|
+
# Delete an empty file, otherwise close it
|
203
|
+
@record_file.stat.size == 0 ? File.unlink(@record_file.path) : @record_file.close
|
204
|
+
end
|
205
|
+
|
206
|
+
def process_arguments(arguments, display=false)
|
207
|
+
opts = OptionParser.new
|
208
|
+
|
209
|
+
opts.banner = "Usage: #{File.basename($0)} [global options] watch [command options] [http|dns]"
|
210
|
+
opts.on("#{$/}Example: #{File.basename($0)} -v watch -C http#{$/}")
|
211
|
+
opts.on('-h', '--help', "Display this message") do
|
212
|
+
Stella::LOGGER.info opts
|
213
|
+
exit 0
|
214
|
+
end
|
215
|
+
|
216
|
+
opts.on("#{$/}Operating mode")
|
217
|
+
opts.on('-P', '--useproxy', "Use an HTTP proxy to filter requests (default)") do |v| v end
|
218
|
+
opts.on('-C', '--usepcap', "Use Pcap to filter TCP packets") do |v| v end
|
219
|
+
#opts.on('-F', '--usepacketfu', "Use Packetfu to filter TCP packets") do |v| v end
|
220
|
+
|
221
|
+
opts.on("#{$/}Pcap-specific options")
|
222
|
+
opts.on('-i=S', '--interface=S', String, "Network device. eri0, en1, etc. (with --usepcap only)") do |v| v end
|
223
|
+
opts.on('-m=N', '--maxpacks=N', Integer, "Maximum number of packets to sniff (with --usepcap only)") do |v| v end
|
224
|
+
opts.on('-R=S', '--protocol=S', String, "Communication protocol to sniff. udp or tcp (with --usepcap only)") do |v| v end
|
225
|
+
|
226
|
+
opts.on("#{$/}Common options")
|
227
|
+
opts.on('-p=N', '--port=N', Integer, "With --useproxy this is the Proxy port. With --usecap this is the TCP port to filter. ") do |v| v end
|
228
|
+
#opts.on('-f=S', '--filter=S', String, "Filter out requests which do not contain this string") do |v| v end
|
229
|
+
#opts.on('-d=S', '--domain=S', String, "Only display requests to the given domain") do |v| v end
|
230
|
+
#opts.on('-r=S', '--record=S', String, "Record requests to file with an optional filename") do |v| v || true end
|
231
|
+
#opts.on('-F=S', '--format=S', "Format of recorded file. One of: simple (for Siege), session (for Httperf)") do |v| v end
|
232
|
+
|
233
|
+
options = opts.getopts(@arguments)
|
234
|
+
options = options.keys.inject({}) do |hash, key|
|
235
|
+
hash[key.to_sym] = options[key]
|
236
|
+
hash
|
237
|
+
end
|
238
|
+
|
239
|
+
# "interface" is more clear on the command line but we use "device" internally
|
240
|
+
options[:device] = options.delete(:interface) if options[:interface]
|
241
|
+
options[:service] = arguments.shift unless arguments.empty?
|
242
|
+
|
243
|
+
options
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
|
248
|
+
@@commands['watch'] = Stella::CLI::Watch
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
|
253
|
+
__END__
|
254
|
+
|
255
|
+
# pageload?
|
256
|
+
#
|
257
|
+
# Used while writing the session log file. Returns true when we
|
258
|
+
# suspect a new page has loaded. Otherwise the resource is considered
|
259
|
+
# to be a dependency.
|
260
|
+
def pageload?(now, think_time, host, referer, content_type)
|
261
|
+
time_difference = (now.to_i - @think_time.to_i)
|
262
|
+
time_passed = (@think_time == 0 || time_difference > 4)
|
263
|
+
non_html = (content_type !~ /text\/html/i) if content_type
|
264
|
+
#puts "POOO: #{content_type} #{referer}"
|
265
|
+
|
266
|
+
case [time_passed, non_html]
|
267
|
+
when [true,false]
|
268
|
+
return true
|
269
|
+
when [true,true]
|
270
|
+
return false
|
271
|
+
when [true,nil]
|
272
|
+
return true
|
273
|
+
when [false,false]
|
274
|
+
return false
|
275
|
+
else
|
276
|
+
return false
|
277
|
+
end
|
278
|
+
end
|