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 +7 -0
- data/lib/load/client_command_line.rb +131 -0
- data/lib/load/http_report.rb +132 -0
- data/lib/load/load_manager.rb +74 -0
- data/lib/load/load_node.rb +217 -0
- data/lib/load/load_ramp_up.rb +159 -0
- data/lib/load/load_reporter.rb +48 -0
- data/lib/load/load_test.rb +196 -0
- data/lib/load/load_test_schedule.rb +79 -0
- data/lib/load/meter/meter.rb +2 -0
- data/lib/load/monitors/cpu_monitor.rb +30 -0
- data/lib/load/monitors/file_io_monitor.rb +42 -0
- data/lib/load/monitors/network_monitor.rb +48 -0
- data/lib/load/monitors/ram_monitor.rb +25 -0
- data/lib/load/test_factory.rb +20 -0
- data/lib/load/timing_test.rb +76 -0
- data/lib/ruby_load.rb +1 -0
- metadata +60 -0
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,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: []
|