cloudscale-benchmark 0.0.2
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/LICENSE.txt +22 -0
- data/README.md +33 -0
- data/bin/prepare_benchmark.sh +193 -0
- data/bin/run_benchmark.sh +4 -0
- data/lib/cloudscale/benchmark/agent/init.rb +74 -0
- data/lib/cloudscale/benchmark/model/agent/benchmark_agent_instance.rb +32 -0
- data/lib/cloudscale/benchmark/model/benchmark/benchmark_run.rb +67 -0
- data/lib/cloudscale/benchmark/model/benchmark/benchmark_run_result.rb +32 -0
- data/lib/cloudscale/benchmark/model/benchmark/benchmark_run_result_value.rb +28 -0
- data/lib/cloudscale/benchmark/version.rb +5 -0
- data/lib/cloudscale/benchmark.rb +98 -0
- data/lib/cloudscale/constants/agent_instance_store.rb +42 -0
- data/lib/cloudscale/constants/agent_store.rb +25 -0
- data/lib/cloudscale/plugins/phoronix/phoronix_bulk_execution.rb +259 -0
- data/lib/cloudscale/plugins/plugin.rb +38 -0
- data/lib/cloudscale/plugins/preops/plugin_preop.rb +48 -0
- data/lib/cloudscale/plugins/preops/preop.rb +46 -0
- data/lib/cloudscale/rest/rest_client.rb +200 -0
- data/lib/cloudscale/rest/rest_client_helper.rb +65 -0
- data/lib/cloudscale/store/agent/agent.yml +2 -0
- data/lib/cloudscale/store/agent/agent_instance.yml +6 -0
- data/lib/cloudscale/store/plugin/development +1 -0
- data/lib/cloudscale/store/plugin/numberOfRuns +1 -0
- data/lib/cloudscale/store/plugin/op_timeout +1 -0
- data/lib/cloudscale/store/rest/settings.yml +4 -0
- data/lib/cloudscale/store/settings/phoronix.yml +9 -0
- metadata +229 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
require "hashable"
|
2
|
+
|
3
|
+
##
|
4
|
+
#
|
5
|
+
# @author Johannes Hiemer, cloudscale.
|
6
|
+
#
|
7
|
+
##
|
8
|
+
module Cloudscale
|
9
|
+
module Benchmark
|
10
|
+
|
11
|
+
class BenchmarkRunResultValue
|
12
|
+
include Hashable
|
13
|
+
|
14
|
+
attr_accessor :name, :description, :arguments, :value, :rawString, :scale, :timestamp
|
15
|
+
|
16
|
+
def initialize()
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_h
|
20
|
+
hash = {}
|
21
|
+
instance_variables.each {|var| hash[var.to_s.delete("@")] = instance_variable_get(var) }
|
22
|
+
return hash
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/benchmark/version"
|
2
|
+
require "http"
|
3
|
+
require "optparse"
|
4
|
+
require "singleton"
|
5
|
+
require "yaml"
|
6
|
+
require "json"
|
7
|
+
require "#{File.dirname(__FILE__)}/benchmark/agent/init"
|
8
|
+
require "#{File.dirname(__FILE__)}/plugins/plugin"
|
9
|
+
require "#{File.dirname(__FILE__)}/plugins/preops/plugin_preop"
|
10
|
+
Dir["#{File.dirname(__FILE__)}/plugins/*/*.rb"].each { | f | require f }
|
11
|
+
|
12
|
+
##
|
13
|
+
#
|
14
|
+
# @author Johannes Hiemer, cloudscale.
|
15
|
+
#
|
16
|
+
##
|
17
|
+
module Cloudscale
|
18
|
+
module Benchmark
|
19
|
+
options = {}
|
20
|
+
mandatory = Array.new
|
21
|
+
|
22
|
+
agent_instance = Cloudscale::Benchmark::Init.new
|
23
|
+
agent_instance_id = agent_instance.agent_instance_id()
|
24
|
+
|
25
|
+
Plugins::PluginPreop.instance.init
|
26
|
+
|
27
|
+
parser = OptionParser.new do | parser |
|
28
|
+
|
29
|
+
parser.banner = "Usage: benchmark COMMAND [OPTIONS]"
|
30
|
+
parser.separator ""
|
31
|
+
parser.separator "Commands"
|
32
|
+
parser.separator " start: Start cloudscale benchmark agent"
|
33
|
+
parser.separator " stop: Stop cloudscale benchmark agent"
|
34
|
+
parser.separator ""
|
35
|
+
parser.separator "Options"
|
36
|
+
|
37
|
+
parser.on("-v", "--version", "Show version information about this program and quit.") do
|
38
|
+
puts "Cloudscale Benchmarking " + VERSION
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
|
42
|
+
parser.separator "\n"
|
43
|
+
parser.separator "Plugin specific Settings"
|
44
|
+
|
45
|
+
Plugins::PluginPreop.instance.options.each do | key, value |
|
46
|
+
if (value[:required])
|
47
|
+
mandatory.push(key)
|
48
|
+
end
|
49
|
+
parser.on(value[:argument][1,2] + " n", value[:argument] + "=n", value[:description]) do | value |
|
50
|
+
Plugins::PluginPreop.instance.set_option_value(key, value)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
begin
|
56
|
+
parser.parse!
|
57
|
+
missing = mandatory.select{ | param |
|
58
|
+
Plugins::PluginPreop.instance.get_option_value(param).nil?
|
59
|
+
}
|
60
|
+
if not missing.empty?
|
61
|
+
puts "\n"
|
62
|
+
puts "Error during execution: "
|
63
|
+
puts " Missing options: #{missing.join(', ')}"
|
64
|
+
puts "\n"
|
65
|
+
puts parser
|
66
|
+
exit
|
67
|
+
else
|
68
|
+
Plugins::PluginPreop.instance.save_options
|
69
|
+
end
|
70
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
|
71
|
+
puts $!.to_s
|
72
|
+
puts parser
|
73
|
+
exit
|
74
|
+
end
|
75
|
+
|
76
|
+
case ARGV[0]
|
77
|
+
when "start"
|
78
|
+
plugins = Set.new
|
79
|
+
|
80
|
+
Plugins::Plugin.plugins.each do | plugin_class |
|
81
|
+
plugin = plugin_class.new
|
82
|
+
plugins << plugin
|
83
|
+
end
|
84
|
+
|
85
|
+
plugins.each do | plugin |
|
86
|
+
if plugin.respond_to?('run')
|
87
|
+
plugin.run(agent_instance_id)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
when "stop"
|
92
|
+
puts "call stop on options #{options.inspect}"
|
93
|
+
else
|
94
|
+
puts parser
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "yaml/store"
|
2
|
+
|
3
|
+
##
|
4
|
+
#
|
5
|
+
# @author Johannes Hiemer, cloudscale.
|
6
|
+
#
|
7
|
+
##
|
8
|
+
module Cloudscale
|
9
|
+
module Constants
|
10
|
+
class AgentInstance
|
11
|
+
|
12
|
+
@@AGENT_INSTANCE_STORE_PATH = "#{File.dirname(__FILE__)}/../store/agent/agent_instance.yml"
|
13
|
+
|
14
|
+
def self.create(hash)
|
15
|
+
settings = YAML::Store.new(@@AGENT_INSTANCE_STORE_PATH)
|
16
|
+
|
17
|
+
settings.transaction {
|
18
|
+
hash.each { | key, value |
|
19
|
+
settings[key] = value
|
20
|
+
}
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.load
|
25
|
+
begin
|
26
|
+
YAML.load(File.read(@@AGENT_INSTANCE_STORE_PATH))
|
27
|
+
rescue
|
28
|
+
return nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def self.remove
|
34
|
+
file = File.read(@@AGENT_INSTANCE_STORE_PATH)
|
35
|
+
if (file)
|
36
|
+
File.delete(@@AGENT_INSTANCE_STORE_PATH)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "yaml/store"
|
2
|
+
|
3
|
+
##
|
4
|
+
#
|
5
|
+
# @author Johannes Hiemer, cloudscale.
|
6
|
+
#
|
7
|
+
##
|
8
|
+
module Cloudscale
|
9
|
+
|
10
|
+
module Constants
|
11
|
+
class Agent
|
12
|
+
|
13
|
+
@@AGENT_STORE = "#{File.dirname(__FILE__)}/../store/agent/agent.yml"
|
14
|
+
|
15
|
+
def self.load
|
16
|
+
begin
|
17
|
+
YAML.load(File.read(@@AGENT_STORE))
|
18
|
+
rescue
|
19
|
+
return nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
require "popen4"
|
2
|
+
require "#{File.dirname(__FILE__)}/../../benchmark/model/benchmark/benchmark_run"
|
3
|
+
require "#{File.dirname(__FILE__)}/../../benchmark/model/benchmark/benchmark_run_result"
|
4
|
+
require "#{File.dirname(__FILE__)}/../../benchmark/model/benchmark/benchmark_run_result_value"
|
5
|
+
require "#{File.dirname(__FILE__)}/../../plugins/preops/plugin_preop"
|
6
|
+
require "#{File.dirname(__FILE__)}/../../plugins/plugin"
|
7
|
+
require "#{File.dirname(__FILE__)}/../../rest/rest_client"
|
8
|
+
require "xmlsimple"
|
9
|
+
require "socket"
|
10
|
+
|
11
|
+
|
12
|
+
##
|
13
|
+
#
|
14
|
+
# @author Johannes Hiemer, cloudscale.
|
15
|
+
# @author Joerg Gottschlich, cloudscale.
|
16
|
+
#
|
17
|
+
##
|
18
|
+
module Cloudscale
|
19
|
+
module Plugins
|
20
|
+
class PhoronixBulkExecutor < Plugins::Plugin
|
21
|
+
attr_reader :none, :homeDir, :fileSeparator, :development, :numberOfRuns, :properties, :nanoSeconds
|
22
|
+
attr_accessor :benchmarkRun, :benchmarkRunResult, :benchmarkTest, :environment
|
23
|
+
|
24
|
+
def log
|
25
|
+
@log = Logger.new(STDOUT)
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
super
|
30
|
+
@options = Plugins::PluginPreop.instance.options
|
31
|
+
@settings = load_hash()
|
32
|
+
@homeDir = Dir.home
|
33
|
+
@fileSeparator = File::Separator
|
34
|
+
@nanoSeconds = 1000000000
|
35
|
+
@environment = []
|
36
|
+
# select first non-loopback ip address
|
37
|
+
@ip_address = Socket.ip_address_list.find { |ai| ai.ipv4? && !ai.ipv4_loopback? }.ip_address
|
38
|
+
@hostname = Socket.gethostname
|
39
|
+
end
|
40
|
+
|
41
|
+
def load_hash()
|
42
|
+
begin
|
43
|
+
YAML.load(File.read("#{File.dirname(__FILE__)}/../../store/settings/phoronix.yml"))
|
44
|
+
rescue
|
45
|
+
return nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def resultDir
|
50
|
+
@homeDir + @fileSeparator + ".phoronix-test-suite" + @fileSeparator + "test-results"
|
51
|
+
end
|
52
|
+
|
53
|
+
def lockFile
|
54
|
+
@homeDir + @fileSeparator + ".phoronix-test-suite" + @fileSeparator + "run_lock"
|
55
|
+
end
|
56
|
+
|
57
|
+
def compileParams(benchmarkCommand, testLabel)
|
58
|
+
benchmarkCommand + " " + testLabel
|
59
|
+
end
|
60
|
+
|
61
|
+
def testLabel(benchmarkTest)
|
62
|
+
"pts/" + benchmarkTest["identifier"] + "-" + benchmarkTest["version"]
|
63
|
+
end
|
64
|
+
|
65
|
+
def isRunning
|
66
|
+
File.exists?(lockFile)
|
67
|
+
end
|
68
|
+
|
69
|
+
def run(agentInstanceId)
|
70
|
+
|
71
|
+
begin # main loop
|
72
|
+
|
73
|
+
if (isRunning())
|
74
|
+
log.info("There are currently test cases running on this machine. Don't run two instances at a time.")
|
75
|
+
abort
|
76
|
+
else
|
77
|
+
puts "Querying server for scheduled benchmark tests..."
|
78
|
+
benchmarkRuns = client.searchAny('benchmarkRuns', 'findByBenchmarkInstanceAgentInstanceIdAndStatus',
|
79
|
+
{ :agentInstanceId => agentInstanceId, :status => 'scheduled' })
|
80
|
+
|
81
|
+
benchmarkRuns = benchmarkRuns["content"]; # TODO: better put to rest client helper
|
82
|
+
|
83
|
+
# if we have benchmarks => run
|
84
|
+
if (benchmarkRuns.any?)
|
85
|
+
puts "Received " + benchmarkRuns.length.to_s + " scheduled tests, starting execution..."
|
86
|
+
benchmarkRuns.each do |benchmarkRun|
|
87
|
+
reset()
|
88
|
+
@benchmarkRun = Benchmark::BenchmarkRun.new(benchmarkRun)
|
89
|
+
executeBenchmark()
|
90
|
+
end
|
91
|
+
else
|
92
|
+
puts Time.now.utc.to_s + ": There are currently no tests scheduled for this agent. Running execution again in two minutes."
|
93
|
+
sleep 120
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end until false
|
97
|
+
end
|
98
|
+
|
99
|
+
def executeBenchmark()
|
100
|
+
@benchmarkTest = @benchmarkRun.benchmarkTest
|
101
|
+
@environment.push(@settings["env"]["numberOfRunsVar"] + "=" + @benchmarkTest["numberOfRuns"].to_s)
|
102
|
+
@benchmarkRunResult = Benchmark::BenchmarkRunResult.new(@benchmarkRun)
|
103
|
+
|
104
|
+
|
105
|
+
puts "Executing BenchmarkTest " + @benchmarkTest["name"];
|
106
|
+
|
107
|
+
# install benchmark test
|
108
|
+
# update status on server
|
109
|
+
@benchmarkRun.running!("Installing BenchmarkTest " + @benchmarkTest["name"]);
|
110
|
+
post_status_update()
|
111
|
+
batchInstall()
|
112
|
+
|
113
|
+
# execute benchmark test
|
114
|
+
# update status on server
|
115
|
+
@benchmarkRun.running!("Running BenchmarkTest " + @benchmarkTest["name"]);
|
116
|
+
post_status_update()
|
117
|
+
batchRun()
|
118
|
+
|
119
|
+
# Post results
|
120
|
+
# Replace object with url
|
121
|
+
@benchmarkRunResult.benchmarkRun = RestClientWrapper.load_entity_ref(@benchmarkRunResult.benchmarkRun.to_h)["href"]
|
122
|
+
|
123
|
+
# post result entity
|
124
|
+
client.post("benchmarkRunResults", @benchmarkRunResult)
|
125
|
+
|
126
|
+
puts "Results posted to server."
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
def batchInstall
|
131
|
+
installCommand = compileParams("batch-install", testLabel(@benchmarkTest))
|
132
|
+
status = POpen4.popen4("sh") do | stdout, stderr, stdin, pid |
|
133
|
+
stdin.puts @settings["executable"] + " " + installCommand
|
134
|
+
stdin.close
|
135
|
+
|
136
|
+
puts "pid : #{ pid }"
|
137
|
+
puts "stdout : #{ stdout.read.strip }"
|
138
|
+
puts "stderr : #{ stderr.read.strip }"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def batchRun
|
143
|
+
installCommand = compileParams("batch-run", testLabel(@benchmarkTest))
|
144
|
+
startStamp = Time.now
|
145
|
+
puts "Start time: "+startStamp.utc.to_s
|
146
|
+
if (!@options["development"])
|
147
|
+
status = POpen4.popen4("sh") do | stdout, stderr, stdin, pid |
|
148
|
+
stdin.puts @settings["executable"] + " " + installCommand
|
149
|
+
stdin.close
|
150
|
+
|
151
|
+
puts "pid : #{ pid }"
|
152
|
+
puts "stdout : #{ stdout.read.strip }"
|
153
|
+
puts "stderr : #{ stderr.read.strip }"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
stopStamp = Time.now
|
157
|
+
puts "End time: "+stopStamp.utc.to_s
|
158
|
+
executionTime = (stopStamp - startStamp) # / @nanoSeconds;
|
159
|
+
|
160
|
+
puts "Benchmark " + testLabel(@benchmarkTest) + " executed in " + executionTime.to_s + " seconds"
|
161
|
+
|
162
|
+
|
163
|
+
# storing benchmarking execution gross timer
|
164
|
+
resultValueTimer = Benchmark::BenchmarkRunResultValue.new()
|
165
|
+
resultValueTimer.name = "ExecRuntime"
|
166
|
+
resultValueTimer.description = "Benchmark runtime measured by execution handler"
|
167
|
+
resultValueTimer.value = executionTime.to_s
|
168
|
+
resultValueTimer.scale = "Seconds"
|
169
|
+
# add timer value to result
|
170
|
+
@benchmarkRunResult.pushResultValue(resultValueTimer)
|
171
|
+
|
172
|
+
# get results here
|
173
|
+
puts "Collecting results from xml"
|
174
|
+
# walk result files (usually only one)
|
175
|
+
files = resultFiles()
|
176
|
+
files.each do |f|
|
177
|
+
puts "Processing #{f}..."
|
178
|
+
|
179
|
+
# read result file contents
|
180
|
+
resultxml = File.read(f)
|
181
|
+
if (resultxml == nil)
|
182
|
+
@benchmarkRun.failed!("ERROR, no xml results found")
|
183
|
+
post_status_update()
|
184
|
+
return
|
185
|
+
end
|
186
|
+
|
187
|
+
# convert xml to result objects
|
188
|
+
results = compileResults(resultxml)
|
189
|
+
if (! results.empty?)
|
190
|
+
@benchmarkRun.succeed!("OK, finished successfully.")
|
191
|
+
else
|
192
|
+
@benchmarkRun.failed!("ERROR, no results")
|
193
|
+
end
|
194
|
+
post_status_update()
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
end # batchRun
|
199
|
+
|
200
|
+
|
201
|
+
def compileResults(xml)
|
202
|
+
|
203
|
+
@benchmarkRunResult.rawBenchmarkOutput = xml
|
204
|
+
@benchmarkRunResult.ip_address = @ip_address
|
205
|
+
@benchmarkRunResult.hostname = @hostname
|
206
|
+
|
207
|
+
# parse result xml and convert to object
|
208
|
+
results = XmlSimple.xml_in(xml)
|
209
|
+
|
210
|
+
# loop over results and create
|
211
|
+
results['Result'].each do |res|
|
212
|
+
|
213
|
+
res['Data'].each do |entry|
|
214
|
+
|
215
|
+
# create BenchmarkRunResultValue - copy values from result XML
|
216
|
+
value = Benchmark::BenchmarkRunResultValue.new()
|
217
|
+
value.name = results['System'][0]['Identifier'][0]
|
218
|
+
value.timestamp = results['System'][0]['Timestamp']
|
219
|
+
value.description = res['Description'][0] || results["Generated"][0]["TestClient"][0] + "\n" + results["Generated"][0]["Description"][0]
|
220
|
+
value.arguments = res['Arguments'][0].empty? ? "" : res['Arguments'][0]
|
221
|
+
value.scale = res['Scale'][0]
|
222
|
+
value.value = entry['Entry'][0]['Value'][0]
|
223
|
+
value.rawString = entry['Entry'][0]['RawString'][0]
|
224
|
+
|
225
|
+
# add benchmark to result instance var
|
226
|
+
@benchmarkRunResult.pushResultValue(value)
|
227
|
+
|
228
|
+
value
|
229
|
+
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
def resultFiles
|
237
|
+
puts "Loading files from " + resultDir + @fileSeparator + "**/test-1.xml"
|
238
|
+
Dir[resultDir() + @fileSeparator + "**/test-1.xml"] ## TODO CHECK if this yields one file only and the right one, too :)
|
239
|
+
end
|
240
|
+
|
241
|
+
def reset()
|
242
|
+
@benchmarkRun = nil
|
243
|
+
@benchmarkRunResult = nil
|
244
|
+
if (!@options["development"] && File.exists?(resultDir))
|
245
|
+
FileUtils.rm_rf(resultDir)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def post_status_update
|
250
|
+
# post benchmarkRun status update
|
251
|
+
client.patch("benchmarkRuns", @benchmarkRun.id, @benchmarkRun.get_status_hash())
|
252
|
+
# TODO: error handling
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|