ruby-load 0.4.8

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.
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: []