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.
@@ -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,5 @@
1
+ module Cloudscale
2
+ module Benchmark
3
+ VERSION = "0.0.2"
4
+ end
5
+ 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
+