ruby-load 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 46e778e48feb5d53446b7387ace66c1824f50baa
4
+ data.tar.gz: 72141593544e44f8bbacb0609ff1194f5f5e2ad2
5
+ SHA512:
6
+ metadata.gz: 1528d3bf11ad1d68f6de25b19a1c5e95c85b8520c9f7ef2eac5f16a2103b2a6ecfac513452581e1591739e6ef1867bb43ba4bdc189931ef727ef2898651f851a
7
+ data.tar.gz: 4d990a03f757b1ae77305f192ab614ca01414689d24553ace4f4ad39af6a62f56d26b48d2f0416177e875e36c35d84966721cb64844fd7074a48d42f2220dc5b
@@ -0,0 +1,131 @@
1
+ require_relative "./load_node.rb"
2
+ require_relative "./http_report.rb"
3
+ require 'singleton'
4
+
5
+ class ClientCommandLine
6
+ attr_reader :run_id, :node_info_json
7
+
8
+ def require_directory(directory_name)
9
+ project_root = File.dirname(File.absolute_path(__FILE__))
10
+ parent_directory = project_root + "/#{directory_name}"
11
+ puts "Parent root: #{parent_directory}"
12
+ Dir.glob("#{parent_directory}/*") do |file|
13
+ require file
14
+ end
15
+ end
16
+
17
+ def launch(args)
18
+ require_directory("custom")
19
+ puts "Wasp!"
20
+ index = 0
21
+ main_command = args[0]
22
+ puts "Command: #{main_command}"
23
+ index = 1
24
+ if (main_command == "run")
25
+ node_count = 1
26
+ configuration_file_name = nil
27
+ server_address = nil
28
+ @target_server = nil
29
+ while (index < args.length)
30
+ arg = args[index]
31
+
32
+ if (arg == "-n")
33
+ index += 1
34
+ node_count = args[index].to_i
35
+ end
36
+
37
+ if (arg == "-s")
38
+ index += 1
39
+ server_address = args[index]
40
+ end
41
+
42
+ if (arg == "-t")
43
+ index += 1
44
+ @target_server = args[index]
45
+ end
46
+
47
+ if (arg == "-c")
48
+ index += 1
49
+ configuration_file_name = args[index]
50
+ end
51
+ index += 1
52
+ #puts "Looking at command line param: #{index}"
53
+ end
54
+ if (configuration_file_name == nil)
55
+ raise "Failed to pass a -c parameter for config file"
56
+ end
57
+
58
+ if (@target_server == nil)
59
+ raise "Must specify: -t 'target_server' on the command line"
60
+ end
61
+
62
+ uri = "http://#{server_address}/client"
63
+ puts "Wasp server: #{uri}"
64
+ create_run(node_count, configuration_file_name, server_address, @target_server)
65
+
66
+ launch_node_command = "\nTo Start Nodes:\n"
67
+ @node_info_json.each do |node_config|
68
+ code = node_config["code"]
69
+ launch_node_command += " ruby wasp.rb node -c #{code} -s #{server_address} -r #{@run_id}\n"
70
+ end
71
+
72
+ puts "
73
+ Run established.
74
+ Run ID: #{@run_id}
75
+ Waiting for #{node_count} nodes.
76
+ Run details page: http://#{server_address}/reports/run_details?run=#{@run_id}
77
+ #{launch_node_command}
78
+ "
79
+
80
+ elsif (main_command == "node")
81
+ @run_id = nil
82
+ load_node = process_node_command(args)
83
+ else
84
+ help_text =
85
+ "Usage:
86
+ wasp run -n /node_count/ -d /definition code/ {start with this process being the only node}
87
+ wasp node m {m is number of nodes, returns the run id for the node start up}
88
+ wasp node -r /run_id/ {starts one of the nodes for a run}
89
+ "
90
+ puts help_text
91
+ end
92
+ end
93
+
94
+ def create_run(node_count, configuration_file_name, wasp_server_address, target_server)
95
+ messenger = HttpReport.new(wasp_server_address)
96
+ @run_id = messenger.run_create({ node_count: node_count, config_file_name: configuration_file_name, target_server: target_server})
97
+ @node_info_json = messenger.run_node_info({run_id: @run_id})
98
+ end
99
+
100
+ def process_node_command(args)
101
+ while (index < args.length)
102
+ arg = args[index]
103
+ if (arg == "-r")
104
+ index += 1
105
+ @run_id = args[index].to_i
106
+ elsif (arg == "-s")
107
+ index += 1
108
+ server_address = args[index]
109
+ elsif (arg == "-c")
110
+ index += 1
111
+ node_code = args[index]
112
+ end
113
+ index += 1
114
+ end
115
+
116
+ if (@run_id == nil)
117
+ raise "Failed to pass the run_id with -r for the node to run against."
118
+ end
119
+ if (server_address == nil)
120
+ raise "Failed to pass the server address with -s for the node to run against."
121
+ end
122
+
123
+ load_node = launch_node(@run_id, server_address, node_code)
124
+ return load_node
125
+ end
126
+
127
+ def launch_node(run_id, wasp_server_address, node_code)
128
+ load_node = LoadNode.new(run_id, wasp_server_address, node_code)
129
+ end
130
+
131
+ end
@@ -0,0 +1,132 @@
1
+ require "json"
2
+ require "net/http"
3
+
4
+ class HttpReport
5
+
6
+ def initialize(server_address)
7
+ @server = server_address
8
+ end
9
+
10
+ def server_address
11
+ return "http://#{@server}/client"
12
+ end
13
+
14
+ def post(action, params)
15
+ address = server_address + action
16
+ puts "Posting to: #{address}, Params: #{params}"
17
+ uri = URI.parse(server_address + action)
18
+ begin
19
+ response = Net::HTTP.post_form(uri, params)
20
+ rescue Errno::ETIMEDOUT => error
21
+ raise "Communication with server error: #{error} Address: #{uri}"
22
+ end
23
+
24
+ if (response.code != "200")
25
+ puts "Action: #{action} Response: #{response.code} Response body: #{response.body}"
26
+ raise "Bad response from load http: #{response.code} Response: #{response.body}"
27
+ end
28
+ return response.body
29
+ end
30
+
31
+ def node_finished(params)
32
+ post "/node_finished", params
33
+ end
34
+
35
+ def report_result(run_id, node_id, test_code, wasp_id, run_at_millis, time, result)
36
+ response = nil
37
+ begin
38
+ millis = nil
39
+ if (time != nil)
40
+ millis = time * 1000
41
+ end
42
+ response = post "/timing", {"run_at_millis" => run_at_millis, "time" => "#{millis}", "run_id" => run_id, "node_id" => node_id,"test_code" => test_code, "wasp_id" => wasp_id.to_s, "result" => result}
43
+ rescue Exception => e
44
+ puts "Error sending results (#{result} in #{millis}) to server(#{server_address}): #{e.message}"
45
+ end
46
+ return response
47
+ end
48
+
49
+ def status_for_test(run_id, wasp_id)
50
+ # Checking for kill or finished run.
51
+ response = post("/status_for_test", {"run_id" => run_id, 'wasp_id' => wasp_id})
52
+ return JSON.parse(response)
53
+ end
54
+
55
+ def report_load_to_server(run_id, time, load_count)
56
+ response = post "/load", {"run_id" => run_id, "time" => time, "load" => load_count}
57
+ end
58
+
59
+ def node_register(params)
60
+ response = post("/node_register", params)
61
+ data = JSON.parse(response)
62
+ return data
63
+ end
64
+
65
+ def node_monitors(params)
66
+ response = post("/node_monitors", params)
67
+ data = JSON.parse(response)
68
+ return data
69
+ end
70
+
71
+
72
+ def node_start?(params)
73
+ response = post("/node_ask_to_start", params)
74
+ data = JSON.parse(response)
75
+ #puts "Node start response: #{data}"
76
+ return data["start"]
77
+ end
78
+
79
+ def node_schedule(params)
80
+ response = post("/node_schedule", params)
81
+ data = JSON.parse(response)
82
+ return data
83
+ end
84
+
85
+ def node_ready(params)
86
+ response = post("/node_ready", params)
87
+ return response
88
+ end
89
+
90
+ def run_create(params)
91
+ node_count = params[:node_count]
92
+ if (node_count == nil)
93
+ node_count = 1
94
+ params[:node_count] = node_count
95
+ end
96
+
97
+ if (params[:config_file_name] != nil)
98
+ file_name = params[:config_file_name]
99
+ config_from_file = ""
100
+ begin
101
+ File.open(file_name, "r") do |f|
102
+ config_from_file = f.read()
103
+ end
104
+ params[:config_file_name] = file_name
105
+ # params[:configuration] = config_from_file
106
+ configuration = eval(config_from_file)
107
+ server_list = configuration[:servers]
108
+ raise "Configuration lacks 'servers' list" unless (server_list != nil)
109
+ target_server_address = server_list[params[:target_server].to_sym]
110
+ raise "Configuration lacks entry in 'servers' list for ':#{params[:target_server]}'\n #{server_list}" unless target_server_address != nil
111
+ params[:configuration] = configuration.to_s
112
+ params[:config_code] = configuration[:code]
113
+ raise "'duration' not specified for run." unless configuration[:duration] != nil
114
+ params[:duration] = configuration[:duration]
115
+ params[:name] = configuration[:name]
116
+ rescue Exception => e
117
+ raise "Exception reading config from file(#{File::absolute_path(file_name)}): #{e.message}"
118
+ end
119
+ end
120
+
121
+ response = post("/run_create", params)
122
+ data = JSON.parse(response)
123
+ return data["run_id"].to_i
124
+ end
125
+
126
+ def run_node_info(params)
127
+ response = post("/run_node_info", params)
128
+ data = JSON.parse(response)
129
+ return data
130
+ end
131
+
132
+ end
@@ -0,0 +1,74 @@
1
+ require_relative "./load_ramp_up"
2
+ require_relative "./load_reporter"
3
+ require_relative "./test_factory"
4
+ require_relative "./http_report"
5
+
6
+
7
+ class LoadManager
8
+ attr_reader :run_id, :configuration
9
+
10
+ def initialize(configuration, target_code)
11
+ @configuration = configuration
12
+ end
13
+
14
+ def ramp_up
15
+ puts "Load Manager - ramp_up"
16
+ puts "Load Manager: #{Process.pid}"
17
+ pids = []
18
+
19
+ definition_code = configuration[:code]
20
+ @server_address = ENV["SERVER"] || "localhost:2233"
21
+ puts "Server address: #{@server_address}"
22
+ ENV[""]
23
+
24
+
25
+ if (ENV['SERVER'] == nil)
26
+ puts "No 'SERVER' env specified, creating local/minimal server to store results"
27
+ pids << fork {
28
+ @reporter = LoadReporter.new({server: @server_address})
29
+ while (@reporter.started == false)
30
+ puts "Cannot find Wasp server"
31
+ sleep 1
32
+ end
33
+ }
34
+ else
35
+ puts "Using existent 'SERVER': #{@server_address}"
36
+ end
37
+
38
+ @run_id = create_run({definition_code: definition_code})
39
+ ramp_up = LoadRampUp.new(@run_id, @target_code, @configuration)
40
+
41
+ pids << fork {
42
+ ramp_up.run
43
+ }
44
+
45
+ quit = false
46
+ while !quit
47
+ begin
48
+ system("stty raw -echo")
49
+ str = STDIN.getc
50
+ ensure
51
+ system("stty -raw echo")
52
+ end
53
+ input = str.chr
54
+ puts "You pressed: #{input} ##{str.to_i}"
55
+ if (input == "q")
56
+ quit = true
57
+ puts "=================================================================================="
58
+ puts "DONE WITH RAMP UP"
59
+ puts " PID's: #{pids}"
60
+ puts "=================================================================================="
61
+ end
62
+ end
63
+ kill_pids(pids)
64
+ end
65
+
66
+
67
+ def kill_pids(pids)
68
+ puts "Killing child processes"
69
+ pids.each do |pid|
70
+ Process.kill("INT", pid)
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,217 @@
1
+ require_relative "./http_report"
2
+ require_relative "./load_test"
3
+ require_relative "./monitors/cpu_monitor.rb"
4
+ require_relative "./monitors/ram_monitor.rb"
5
+ require_relative "./monitors/file_io_monitor.rb"
6
+ require_relative "./monitors/network_monitor.rb"
7
+ require_relative "./test_factory.rb"
8
+
9
+ @@load_node_instance = nil
10
+
11
+ class LoadNode
12
+ attr_reader :node_id, :schedule, :duration_millis
13
+ attr_accessor :target_server, :target_code
14
+
15
+ def self.instance
16
+ return @@load_node_instance
17
+ end
18
+
19
+ def initialize(run_id, server_address, node_code = nil)
20
+ if (@@load_node_instance != nil)
21
+ raise "LoadNode instance initialized a second time"
22
+ end
23
+ @target_code = target_code
24
+ @test_index = 1
25
+ @messenger = nil
26
+ @@load_node_instance = self
27
+ @factory = TestFactory.new
28
+ if (server_address == nil)
29
+ # Perf tests don't require server
30
+ else
31
+ begin
32
+ puts "Registering node"
33
+ @run_id = run_id
34
+ @child_pids = []
35
+ @messenger = HttpReport.new(server_address)
36
+ node_info = @messenger.node_register({run_id: @run_id, address: "#{Socket.gethostname}", code: node_code})
37
+ if (node_info['configuration'] == nil)
38
+ puts "Error starting node. Did run already complete?"
39
+ else
40
+ @configuration = eval(node_info['configuration'])
41
+ @node_id = node_info["id"].to_i
42
+ @node_code = node_info["code"]
43
+ @target_code = node_info["target_server"]
44
+ @target_server = @configuration[:servers][@target_code.to_sym]
45
+ puts "Node registered: #{@node_id} Code: #{@node_code} Target server: #{@target_server}"
46
+ @schedule = @messenger.node_schedule({node_id: @node_id})
47
+ @load_tests = create_node_tests(@node_id, @schedule)
48
+ @duration_millis = node_info["duration"]
49
+ @messenger.node_ready({node_id: @node_id})
50
+ wait_for_node_start_ok
51
+ launch_node_tests
52
+ wait_for_finish
53
+ @messenger.node_finished({node_id: @node_id})
54
+ end
55
+ rescue Interrupt => e
56
+ puts "NODE (#{@node_id}) - Interrupt signal received, quitting. [#{self.class}]"
57
+ rescue Exception => exc
58
+ puts "Exception in node: #{exc.message} \n" + exc.backtrace.join("\n")
59
+ ensure
60
+ kill_child_processes
61
+ end
62
+ #exit 0
63
+ end
64
+ end
65
+
66
+ def report_block_result(test_code, wasp_id, time_ellapsed, benchmark_time, result)
67
+ @messenger.report_result(@run_id, @node_id, test_code, wasp_id, time_ellapsed, benchmark_time, result)
68
+ end
69
+
70
+
71
+ def write_pid_to_file(pid)
72
+ open('node_pids_#{@node_id}.pid', 'a') { |f|
73
+ f.puts "#{pid}\n"
74
+ }
75
+ end
76
+
77
+ def launch_node_tests
78
+ @load_tests.each do |test|
79
+ pid = fork {
80
+ test.run
81
+ }
82
+ @child_pids << pid
83
+ write_pid_to_file(pid)
84
+ end
85
+ end
86
+
87
+
88
+ def wait_for_node_start_ok
89
+ ok_to_start = false
90
+ last_message_at = 0
91
+ while (!ok_to_start)
92
+ ok_to_start = @messenger.node_start?({node_id: @node_id})
93
+ sleep 0.2
94
+ if (Time.new.to_i - last_message_at > 5)
95
+ puts "Node waiting for other nodes to start (#{@node_code})"
96
+ last_message_at = Time.new.to_i
97
+ end
98
+ end
99
+ puts "All Nodes Launched. Node (#{@node_code}) can start now "
100
+ end
101
+
102
+
103
+ def wait_for_finish
104
+ @start_time = Time.now.to_i
105
+ quit = false
106
+ while !quit
107
+ sleep 10
108
+ ellapsed_time = Time.now.to_i - @start_time
109
+ if (ellapsed_time > (@duration_millis / 1000))
110
+ quit = true
111
+ puts "Node duration reached: #{@duration_millis} seconds. Node quitting"
112
+ else
113
+ #puts "Node duration (#{ellapsed_time} secs) NOT reached: #{@duration_millis/1000} seconds."
114
+ end
115
+ @messenger.node_schedule({node_id: @node_id})
116
+
117
+ status = @messenger.status_for_test(@run_id, nil)
118
+ if (status['run_status'] == "killed")
119
+ puts "XXXXX RUN HAS BEEN KILLED - KILLING NODE: #{@wasp_id} XXXXXXX"
120
+ quit = true
121
+ elsif (status['run_status'] == "finished")
122
+ puts "XXXXX RUN IS FINISHED - KILLING NODE: #{@wasp_id} XXXXXXX"
123
+ quit = true
124
+ end
125
+
126
+ end
127
+ puts "Node finished. ##{@node_code}"
128
+ end
129
+
130
+
131
+ def create_node_tests(node_id, node_schedule)
132
+ wasp_id = 1
133
+ load_tests = []
134
+
135
+ puts "Node Schedule:"
136
+ node_schedule.each do |test_schedule|
137
+ test_name = test_schedule["test_name"]
138
+ events = test_schedule["events"]
139
+ sched_text = "Test: #{test_name} Events: "
140
+ events.each do |event|
141
+ sched_text += "#{event['action'].capitalize} at #{event['time']} sec "
142
+ end
143
+ test_config = find_config_for_test(test_name)
144
+ test = @factory.create_test(test_name, @target_code, test_config)
145
+ test.index = @test_index
146
+ @test_index += 1
147
+
148
+ load_test = LoadTest.new(self, @run_id, node_id, wasp_id, test, events, @messenger)
149
+ load_tests << load_test
150
+ wasp_id += 1
151
+ end
152
+
153
+ node_monitors = @messenger.node_monitors( {node_id: @node_id} )
154
+ node_monitors.each do |monitor_config|
155
+ type = monitor_config["type"]
156
+ name = monitor_config["name"]
157
+ duration = monitor_config["duration"].to_i / 1000
158
+ if (name == nil)
159
+ name = "#{type.capitalize} Monitor"
160
+ end
161
+
162
+ if (type == "network")
163
+ puts "Config: #{monitor_config}"
164
+ address = monitor_config["address"]
165
+ monitor = NetworkMonitor.new(address)
166
+ elsif (type == "cpu")
167
+ monitor = CpuMonitor.new()
168
+ elsif (type == "file.io")
169
+ monitor = FileIoMonitor.new()
170
+ elsif (type == "ram")
171
+ monitor = RamMonitor.new()
172
+ else
173
+ raise "Do no know how to handle monitor type: #{type} Name: #{name}"
174
+ end
175
+
176
+ events = [{"time" => "0", "action" => "run"}, {"time" => "#{duration}", "action" => "pause"}]
177
+ load_test = LoadTest.new(self, @run_id, node_id, wasp_id, monitor, events, @messenger)
178
+ load_tests << load_test
179
+ wasp_id += 1
180
+ end
181
+
182
+ return load_tests
183
+ end
184
+
185
+ def find_config_for_test(test_name)
186
+ test_configs = @configuration[:tests]
187
+ test_configs.each do |test_config|
188
+ if (test_config[:test] == test_name)
189
+ return test_config
190
+ end
191
+ end
192
+ end
193
+
194
+
195
+ def kill_child_processes
196
+ # puts "=================================================================================="
197
+ # puts "Node Finished: #{@node_id}"
198
+ # puts " Killing child PID's: #{@child_pids}"
199
+ # puts "=================================================================================="
200
+
201
+ # trap("INT") do
202
+ # exit
203
+ # end
204
+ Process.kill('INT', -Process.getpgrp)
205
+ #
206
+ # @child_pids.each do |pid|
207
+ # begin
208
+ # puts "Killing: #{pid}"
209
+ # Process.kill("INT", pid)
210
+ # rescue Exception => e
211
+ # puts "Exception killing pid #{pid}. #{e.message}"
212
+ # end
213
+ # end
214
+ end
215
+
216
+
217
+ end
@@ -0,0 +1,159 @@
1
+ require_relative "./load_test"
2
+ require_relative "./http_report"
3
+ require_relative "./test_factory"
4
+
5
+
6
+ class LoadRampUp
7
+ attr_reader :tests
8
+
9
+
10
+ def initialize(run_id, target_code, configuration)
11
+ puts "Load Ramp Up created"
12
+ if (run_id == nil)
13
+ end
14
+ @pids = []
15
+ @run_id = run_id
16
+ @target_code = target_code
17
+ @factory = TestFactory.new
18
+ @tests = {}
19
+ @all_tests = []
20
+ @configuration = configuration
21
+ wasp_id = 0
22
+
23
+ @configuration[:tests].each do |test_config|
24
+ test_class = test_config[:test]
25
+
26
+ initial_count = test_config[:initial]
27
+ ramp_up_config = test_config[:ramp_up]
28
+ final_count = ramp_up_config[:final]
29
+ @tests[test_class] = []
30
+ while (@tests[test_class].length < final_count)
31
+ # (run_id, node_id, wasp_id, test, schedule_events, messenger)
32
+ test = @factory.create_test(test_class, @target_code, test_config)
33
+ load_test = LoadTest.new(self, run_id, nil, wasp_id, test, test_config, messenger)
34
+ wasp_id += 1
35
+ @tests[test_class] << load_test
36
+ @all_tests << load_test
37
+ end
38
+ assign_schedules_to_tests(test_config, @tests[test_class])
39
+ end
40
+ send_schedule_to_server(@configuration)
41
+ end
42
+
43
+
44
+ def send_schedule_to_server(configuration)
45
+ (0..duration).each do |time|
46
+ total_load = 0
47
+ @all_tests.each do |test|
48
+ action = test.schedule.current_action(time)
49
+ if (action == :run)
50
+ total_load += 1
51
+ end
52
+ end
53
+ report_load_to_server(@run_id, time, total_load)
54
+ end
55
+ end
56
+
57
+
58
+ def duration
59
+ last_time = 0
60
+ @all_tests.each do |test|
61
+ test_last_time = test.duration
62
+ if (test_last_time > last_time)
63
+ last_time = test_last_time
64
+ end
65
+ end
66
+ return last_time
67
+ end
68
+
69
+
70
+ def find_total_loads
71
+ load_at_times
72
+ time = 0
73
+ while (!done) do
74
+ count = 0
75
+ @all_tests.each do |test|
76
+ if (test.current_action(time) == :run)
77
+ count += 1
78
+ end
79
+ end
80
+ time += 1
81
+ end
82
+ end
83
+
84
+
85
+ def assign_schedules_to_tests(test_config, load_tests)
86
+ ramp_up = test_config[:ramp_up]
87
+ initial_count = test_config[:initial]
88
+
89
+ load_tests.each do |test|
90
+ test.schedule.add(0, :pause)
91
+ end
92
+
93
+ (0..initial_count - 1).each do |index|
94
+ test = load_tests[index]
95
+ test.schedule.add(0, :run)
96
+ end
97
+
98
+ rate = ramp_up[:rate]
99
+ current_time = 0
100
+ tests_started = initial_count
101
+ while (tests_started < ramp_up[:final]) do
102
+ current_time += rate
103
+ load_tests[tests_started].schedule.add(current_time, :run)
104
+ tests_started += 1
105
+ end
106
+
107
+ sustain_time = test_config[:sustain]
108
+ current_time += sustain_time
109
+
110
+ ramp_down = test_config[:ramp_down]
111
+ final_count = ramp_down[:final]
112
+ rate = ramp_down[:rate]
113
+ while (tests_started > final_count) do
114
+ load_tests[tests_started - 1].schedule.add(current_time, :pause)
115
+ tests_started -= 1
116
+ current_time += rate
117
+ end
118
+ end
119
+
120
+
121
+
122
+ def run
123
+ begin
124
+ puts "Running Load test"
125
+ @configuration[:tests].each do |test_config|
126
+ test_name = test_config[:name]
127
+ puts "Starting test: #{test_name}"
128
+ @tests[test_name].each do |load_test|
129
+ @pids << fork {
130
+ #puts "Launching test: #{test_name} : #{load_test.wasp_id}"
131
+ load_test.run
132
+ }
133
+ end
134
+ end
135
+ wait_for_kill_signal()
136
+ rescue Interrupt => e
137
+ puts "Interrupt signal received, quitting. [#{e.class.name}] #{e.message}"
138
+ ensure
139
+ kill_pids(@pids)
140
+ end
141
+ exit 0
142
+ end
143
+
144
+
145
+ def wait_for_kill_signal()
146
+ while (true) do
147
+ sleep 2000
148
+ end
149
+ end
150
+
151
+
152
+ def kill_pids(pids)
153
+ puts "Killing child processes"
154
+ pids.each do |pid|
155
+ Process.kill("INT", pid)
156
+ end
157
+ end
158
+
159
+ end
@@ -0,0 +1,48 @@
1
+ require 'webrick'
2
+
3
+ class LoadReporter
4
+ attr_reader :started
5
+ include WEBrick
6
+
7
+ def initialize(params)
8
+ puts "Initializing Load Reporter"
9
+ @started = false
10
+ @server_address = params[:server]
11
+ @current_load = 0
12
+ file_name = "data_#{Time.now.to_i}.txt"
13
+ @out_file = File.new("out.txt", "w")
14
+ port = params[:port] || 2233
15
+ puts "Starting server: http://#{Socket.gethostname}:#{port}"
16
+ server = HTTPServer.new(:Port=>2233,:DocumentRoot=>Dir::pwd )
17
+ trap("INT") {
18
+ puts "Server going down"
19
+ server.shutdown
20
+ }
21
+
22
+ server.mount_proc '/' do |request, response|
23
+ response.body = process_request(request)
24
+ end
25
+ server.start
26
+ @started = true
27
+ end
28
+
29
+
30
+ def process_request(request)
31
+ response = "
32
+ Current Load: #{@current_load}
33
+ Query String: #{request.query_string}
34
+ "
35
+
36
+ request.query.collect { | key, value |
37
+ #f.write("#{key}: #{value}\n")
38
+ if (key == "load")
39
+ @current_load = value.to_i
40
+ response += "Current load changed to: #{@current_load}"
41
+ end
42
+ response += "#{key}: #{value}\n"
43
+ }
44
+ puts "RESPONSE: #{response}"
45
+ return response
46
+ end
47
+
48
+ end
@@ -0,0 +1,196 @@
1
+ require "net/http"
2
+ require "uri"
3
+ require "benchmark"
4
+ require_relative "http_report"
5
+ require_relative "load_test_schedule"
6
+ require 'logger'
7
+ require 'fileutils'
8
+
9
+ class LoadTest
10
+ PASSED = "passed"
11
+ FAILED = "failed"
12
+
13
+ attr_reader :wasp_id, :start_time, :test
14
+ attr_accessor :schedule
15
+
16
+ def initialize(owner, run_id, node_id, wasp_id, test, schedule_events, messenger = nil)
17
+ @owner = owner
18
+ test.owner = self
19
+ @schedule = LoadTestSchedule.new
20
+ @schedule.load_json(schedule_events)
21
+ @messenger = messenger
22
+ @node_id = node_id
23
+ @wasp_id = wasp_id
24
+ @time_of_last_status_check = 0
25
+
26
+ @test = test
27
+ if (run_id == nil) || (run_id == 0)
28
+ raise "No run_id specified for load test"
29
+ end
30
+ test_code = self.class.name
31
+ @run_id = run_id
32
+ @name = "Test for #{test.test_code}"
33
+ end
34
+
35
+ def log
36
+ return @logger
37
+ end
38
+
39
+ def create_logger
40
+ FileUtils.mkdir_p('./log/load') unless File.directory?('./log/load')
41
+ FileUtils.mkdir_p("./log/load/run_#{@run_id}") unless File.directory?("./log/load/run_#{@run_id}")
42
+ log_file = "./log/load/run_#{@run_id}/#{@test.class.name}_#{@wasp_id}.log"
43
+ @logger = Logger.new(log_file)
44
+ @logger.level = Logger::DEBUG
45
+ @test.logger = @logger
46
+ end
47
+
48
+ def run
49
+ create_logger()
50
+ trap('INT') do
51
+ puts "Wasp asked to die: #{test_code}:#{@wasp_id} Pid: #{Process.pid}"
52
+ #log.info "Wasp asked to die: #{test_code}:#{@wasp_id} Pid: #{Process.pid}"
53
+ exit 0
54
+ end
55
+
56
+ begin
57
+ @start_time = Time.now
58
+ @exit = false
59
+ while (!@exit)
60
+ result = FAILED
61
+ run_if_time_right(time_ellapsed_millis)
62
+ end
63
+ puts "End of run for wasp: #{@wasp_id}. Duration: #{duration_millis}"
64
+ log.info "End of run. Duration: #{duration_millis}"
65
+ rescue Interrupt => e
66
+ log.warn "WASP KILL: #{test_code}:#{@wasp_id} Interrupt signal received, quitting. [#{e.class.name}] #{e.message}"
67
+ puts "WASP KILL: #{test_code}:#{@wasp_id} Interrupt signal received, quitting. [#{e.class.name}] #{e.message}"
68
+ @exit = true
69
+ rescue Exception => exc
70
+ log.warn "Exception in wasp: #{@wasp_id} . [#{exc.class.name}] #{exc.message}"
71
+ puts "Exception in wasp: #{@wasp_id} . [#{exc.class.name}] #{exc.message}"
72
+ ensure
73
+ log.info "Wasp process exiting: #{test_code}:#{@wasp_id} Pid: #{Process.pid}"
74
+ puts "Wasp process exiting: #{test_code}:#{@wasp_id} Pid: #{Process.pid}"
75
+ exit 0
76
+ end
77
+ end
78
+
79
+
80
+ def run_if_time_right(current_time_millis)
81
+ benchmark_time = 0
82
+ if (current_time_millis >= duration_millis)
83
+ @exit = true
84
+ puts "Time for LoadTest instance to die. Ellapsed: #{current_time_millis} Test duration: #{duration_millis}"
85
+ else
86
+ current_action = current_action_for_time(current_time_millis)
87
+ if (current_action == :run)
88
+ #puts "Time is right. Test being performed"
89
+ result = PASSED
90
+ begin
91
+ benchmark_time = Benchmark.realtime {
92
+ perform_test
93
+ }
94
+ log.info "Test completed normally in #{benchmark_time} seconds"
95
+ rescue SystemExit => se
96
+ log.warn "Caught system exit signal. Exiting..."
97
+ @exit = true
98
+ rescue Exception => e
99
+ result = FAILED
100
+ log.error("Test (#{@test.class}) produced exception: #{e.class} #{e.message}")
101
+ e.backtrace.each do |back|
102
+ log.error(back)
103
+ end
104
+ puts "Test (#{@test.class}) produced exception: #{e.class} #{e.message} #{e.backtrace}"
105
+ end
106
+
107
+ custom_timing = @test.timing
108
+ if (custom_timing != nil)
109
+ benchmark_time = custom_timing
110
+ end
111
+ @messenger.report_result(@run_id, @node_id, @test.test_code, @wasp_id, current_time_millis, benchmark_time, result)
112
+ else
113
+ #puts "Not running test. current_action = #{current_action}"
114
+ sleep 0.2
115
+ end
116
+
117
+ if (@exit == false)
118
+ time_since_last_status_check = current_time_millis - @time_of_last_status_check
119
+ if (time_since_last_status_check > 20*1000)
120
+ @time_of_last_status_check = current_time_millis
121
+ status = @messenger.status_for_test(@run_id, @wasp_id)
122
+ if (status['run_status'] == "killed")
123
+ log.warn "XXXXX RUN HAS BEEN KILLED - KILLING WASP: #{@wasp_id} XXXXXXX"
124
+ @exit = true
125
+ elsif (status['run_status'] == "finished")
126
+ log.warn "XXXXX RUN IS FINISHED - KILLING WASP: #{@wasp_id} XXXXXXX"
127
+ @exit = true
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+
135
+
136
+ def duration_millis
137
+ last_run_time = nil
138
+ @schedule.events.each do |event|
139
+ if (event[:action] == :run)
140
+ last_run_time = nil
141
+ else
142
+ last_run_time = event[:time]
143
+ end
144
+ end
145
+
146
+ if (last_run_time == nil)
147
+ raise "This test never finishes on it's own"
148
+ end
149
+ return (last_run_time * 1000)
150
+ end
151
+
152
+
153
+ def time_ellapsed_millis
154
+ time_ellapsed = (Time.now - @start_time) * 1000.0
155
+ return time_ellapsed
156
+ end
157
+
158
+ def report_block_result(test_code, wasp_id, ellapsed_millis, benchmark_time, result, target_code = "unknown")
159
+ @owner.report_block_result(test_code, wasp_id, ellapsed_millis, benchmark_time, result)
160
+ end
161
+
162
+ def current_action
163
+ return current_action_for_time(time_ellapsed_millis)
164
+ end
165
+
166
+ def current_action_for_time(millis)
167
+ current_action = schedule.current_action(millis / 1000)
168
+ return current_action
169
+ end
170
+
171
+ def test_code
172
+ return @test.test_code
173
+ end
174
+
175
+ def to_s
176
+ return "I am only one wasp of a swarm. ##{@wasp_id}"
177
+ end
178
+
179
+ def assert(value)
180
+ if (value == false)
181
+ raise "Assertion failed"
182
+ end
183
+ end
184
+
185
+ def perform_test
186
+ @test.owner = self
187
+ @test.set_up
188
+ @test.run
189
+ @test.pause_after_run
190
+ @test.tear_down
191
+ end
192
+
193
+ end
194
+
195
+
196
+
@@ -0,0 +1,79 @@
1
+ class LoadTestSchedule
2
+ attr_accessor :events
3
+
4
+ def initialize
5
+ @events = []
6
+ @previous_action = nil
7
+ end
8
+
9
+ def add(time, action)
10
+ if (action != @previous_action)
11
+ remove_action_at_time(time)
12
+ @events << {time: time, action: action}
13
+ else
14
+ warn "Attempt to add same action consecutively, ignoring the later one. Time: #{time} Action: #{action}"
15
+ end
16
+ @previous_action = action
17
+ end
18
+
19
+ def load_json(schedule_events)
20
+ schedule_events.each do |event|
21
+ time = event["time"].to_i
22
+ action = event["action"].to_sym
23
+ add(time, action)
24
+ end
25
+ end
26
+
27
+ def remove_action_at_time(time)
28
+ (0..@events.length - 1).each do |i|
29
+ if (@events[i][:time] == time)
30
+ @events.delete_at(i)
31
+ return
32
+ end
33
+ end
34
+ end
35
+
36
+
37
+ def current_action(time)
38
+ event = current_event(time)
39
+ if (event == nil)
40
+ return :pause
41
+ else
42
+ return event[:action]
43
+ end
44
+ end
45
+
46
+
47
+ def next_action(time)
48
+ event = next_event(time)
49
+ if (event == nil)
50
+ return nil
51
+ else
52
+ return event[:action]
53
+ end
54
+ end
55
+
56
+
57
+ def current_event(time)
58
+ current_event = nil
59
+ @events.each do |event|
60
+ if (event[:time] <= time.to_i)
61
+ current_event = event
62
+ end
63
+ end
64
+ return current_event
65
+ end
66
+
67
+
68
+ def next_event(time)
69
+ current_event = nil
70
+ @events.each do |event|
71
+ if (event[:time] > time.to_i)
72
+ return event
73
+ end
74
+ end
75
+ return @events.last
76
+ end
77
+
78
+
79
+ end
@@ -0,0 +1,2 @@
1
+ class Meter
2
+ end
@@ -0,0 +1,30 @@
1
+ require "uri"
2
+ require "benchmark"
3
+ require_relative "../timing_test"
4
+
5
+
6
+ class CpuMonitor < TimingTest
7
+
8
+ def initialize
9
+ @sleep_seconds = 4
10
+ end
11
+
12
+ def run
13
+ a = 5000
14
+ (0..1000).each do |i|
15
+ a /= 2.0
16
+ end
17
+ end
18
+
19
+ def pause_after_run
20
+ sleep @sleep_seconds
21
+ end
22
+
23
+
24
+ def test_code
25
+ return ("monitor.cpu")
26
+ end
27
+
28
+ end
29
+
30
+
@@ -0,0 +1,42 @@
1
+ require "uri"
2
+ require "benchmark"
3
+ require_relative "../timing_test"
4
+
5
+
6
+ class FileIoMonitor < TimingTest
7
+
8
+ def initialize
9
+ @sleep_seconds = 4
10
+ end
11
+
12
+ def run
13
+ file_name = "./log/file_io_bench_#{Time.now.to_i}.dat"
14
+ (1..200).each do |i|
15
+ File.open(file_name, 'w') { |file|
16
+ (1..1000).each do |i|
17
+ file.write("Test text #{i} aohoasihdf oiahsdf pohiasdfha oisdhf aiousdfgho iausgdf oaia aiusdhfia hdfihdsfi ahsdfi ahudf \n")
18
+ file.flush
19
+ end
20
+ }
21
+
22
+ File.readlines(file_name).each do |line|
23
+ if (!line.start_with? "Test text")
24
+ raise "Error reading back file"
25
+ end
26
+ end
27
+ File.delete(file_name)
28
+ end
29
+ end
30
+
31
+ def pause_after_run
32
+ sleep @sleep_seconds
33
+ end
34
+
35
+
36
+ def test_code
37
+ return ("monitor.file")
38
+ end
39
+
40
+ end
41
+
42
+
@@ -0,0 +1,48 @@
1
+ require_relative "../timing_test"
2
+ require 'net/ping'
3
+
4
+
5
+ class NetworkMonitor < TimingTest
6
+ attr_accessor :address
7
+
8
+ def initialize(address)
9
+ @sleep_seconds = 4
10
+ @address = address
11
+ end
12
+
13
+ def timing
14
+ return @timing
15
+ end
16
+
17
+ def run
18
+ output = ""
19
+ begin
20
+ timing = 0
21
+ pinger = Net::Ping::External.new
22
+ (1..10).each do
23
+ pinger.ping(@address)
24
+ timing += (pinger.duration * 1000.0)
25
+ end
26
+ puts "Ping timing for 10 pings: #{timing}"
27
+ @timing = timing
28
+ rescue Exception => e
29
+ @timing = nil
30
+ puts "Error pinging address: #{@address} => #{output}"
31
+ raise "Error pinging address: #{@address} => #{output}"
32
+ end
33
+ end
34
+
35
+ #64 bytes from localhost (127.0.0.1): icmp_req=4 ttl=64 time=0.015 ms
36
+ #64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.018 ms
37
+
38
+ def pause_after_run
39
+ sleep @sleep_seconds
40
+ end
41
+
42
+ def test_code
43
+ return ("monitor.network")
44
+ end
45
+
46
+ end
47
+
48
+
@@ -0,0 +1,25 @@
1
+ require_relative "../timing_test"
2
+
3
+
4
+ class RamMonitor < TimingTest
5
+
6
+ def initialize
7
+ @sleep_seconds = 4
8
+ end
9
+
10
+ def run
11
+
12
+ end
13
+
14
+ def pause_after_run
15
+ sleep @sleep_seconds
16
+ end
17
+
18
+
19
+ def test_code
20
+ return ("monitor.ram")
21
+ end
22
+
23
+ end
24
+
25
+
@@ -0,0 +1,20 @@
1
+ # Require all files in custom test directory
2
+ this_dir_name = File.dirname(File.absolute_path(__FILE__))
3
+ Dir.glob(this_dir_name + '/custom/*') {|file| require file}
4
+
5
+ class TestFactory
6
+
7
+ def initialize
8
+ end
9
+
10
+ def create_test(test_code, target_code, config)
11
+ test_class = test_code.split(".").first
12
+ test_object = Object::const_get(test_class).new
13
+ test_object.test_code = test_code
14
+ test_object.target_code = target_code
15
+ raise "No parameters for target: :#{target_code} Test: #{test_code}" unless (config[:params] != nil) && config[:params][target_code.to_sym]
16
+ test_object.params = config[:params][target_code.to_sym]
17
+ return test_object
18
+ end
19
+
20
+ end
@@ -0,0 +1,76 @@
1
+ require "uri"
2
+ require "benchmark"
3
+
4
+
5
+ class TimingTest
6
+ attr_accessor :params, :index, :owner, :test_code, :logger, :target_code
7
+
8
+ def initialize
9
+ @params = {}
10
+ @index = 0
11
+ @owner = nil
12
+ @test_code = nil
13
+ @target_code = nil
14
+ end
15
+
16
+ def set_up
17
+ end
18
+
19
+ def tear_down
20
+ end
21
+
22
+ def run
23
+ raise "Not Implemented"
24
+ end
25
+
26
+ def server_url
27
+ url = LoadNode.instance.target_server
28
+ if (url == nil)
29
+ raise "target_server not specified when starting main process"
30
+ end
31
+ return url
32
+ end
33
+
34
+ def ellaped_millis
35
+ @owner.time_ellapsed_millis
36
+ end
37
+
38
+ def log
39
+ return @logger
40
+ end
41
+
42
+ def time_block(code)
43
+ start_time = Time.now
44
+ begin
45
+ yield
46
+ end_time = Time.now
47
+ block_time = (end_time.to_f - start_time.to_f) * 1000
48
+ @owner.report_block_result(code, @index, @owner.time_ellapsed_millis, block_time, LoadTest::PASSED, target_code)
49
+ rescue Exception => e
50
+ log.error("Exception running timing block (#{code}) test: #{e.message}")
51
+ end_time = Time.now
52
+ block_time = (end_time.to_f - start_time.to_f) * 1000
53
+ @owner.report_block_result(code, @index, @owner.time_ellapsed_millis, block_time, LoadTest::FAILED, target_code)
54
+ raise e
55
+ end
56
+ end
57
+
58
+
59
+ def assert(value)
60
+ if (value == false)
61
+ puts "Assertion failed"
62
+ raise "Assertion failed"
63
+ end
64
+ end
65
+
66
+ def pause_after_run
67
+ # Default is no pause
68
+ end
69
+
70
+ def timing
71
+ # LoadTest times the run method and uses that timing unless the instance of TimingTest timing method returns a non-nil time.
72
+ return nil
73
+ end
74
+
75
+ end
76
+
data/lib/ruby_load.rb ADDED
@@ -0,0 +1 @@
1
+ Dir["#{File.dirname(__FILE__)}/load/**/*.rb"].each {|f| require f}
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-load
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.8
5
+ platform: ruby
6
+ authors:
7
+ - Mike Moore
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-29 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Ruby Load Tester
14
+ email: m.moore.denver@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/load/client_command_line.rb
20
+ - lib/load/http_report.rb
21
+ - lib/load/load_manager.rb
22
+ - lib/load/load_node.rb
23
+ - lib/load/load_ramp_up.rb
24
+ - lib/load/load_reporter.rb
25
+ - lib/load/load_test.rb
26
+ - lib/load/load_test_schedule.rb
27
+ - lib/load/meter/meter.rb
28
+ - lib/load/monitors/cpu_monitor.rb
29
+ - lib/load/monitors/file_io_monitor.rb
30
+ - lib/load/monitors/network_monitor.rb
31
+ - lib/load/monitors/ram_monitor.rb
32
+ - lib/load/test_factory.rb
33
+ - lib/load/timing_test.rb
34
+ - lib/ruby_load.rb
35
+ homepage: ''
36
+ licenses:
37
+ - MIT
38
+ metadata: {}
39
+ post_install_message:
40
+ rdoc_options: []
41
+ require_paths:
42
+ - lib
43
+ - lib/ruby_load
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubyforge_project:
56
+ rubygems_version: 2.0.14.1
57
+ signing_key:
58
+ specification_version: 4
59
+ summary: Ruby Load Tester
60
+ test_files: []