mu 5.7.2.7 → 5.7.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.
data/lib/mu.rb CHANGED
@@ -13,6 +13,7 @@ class Mu
13
13
  @version = "5.7.2"
14
14
  Version = @version.freeze
15
15
  $log = Logger.new(STDOUT)
16
+ $log.level = Logger::INFO
16
17
  $cookie = nil # http_helper
17
18
 
18
19
  extend Mu::Helper
@@ -4,520 +4,528 @@
4
4
  # the interfaces to use, and the pattern in which to run
5
5
  require 'mu/api/scale'
6
6
  class Mu
7
- class Command
8
- class Cmd_appid < Command
9
-
10
- attr_accessor :api, :params, :hosts, :addr_indexes, :hash
11
-
12
- # displays command-line help
13
- def cmd_help argv
14
- help
15
- end
16
-
17
- # returns a boolean indicating whether the scale test is running or not
18
- # * argv = command-line arguments
19
- def cmd_running? argv
20
- if @api.nil?
21
- msg "false"
22
- return
23
- end
24
-
25
- status = @api.status
26
- if !status.nil?
27
- if !status["status"].nil?
28
- msg status["status"]["running"]
29
- end
30
- else
31
- msg "false"
32
- end
33
- end
34
-
35
- # runs a single Studio Scale test
36
- # * argv = command-line arguments, requires a scenario (-s) argument
37
- def cmd_run_file argv
38
- setup argv
39
-
40
- if not @hash['scenario']
41
- raise "*** Error: scenario required, using -s option"
42
- else
43
- scenario = @hash['scenario']
44
- end
45
-
46
- if !File.exists?(scenario)
47
- raise "*** Error: Scenario file #{scenario} was not found"
48
- end
49
-
50
- File.delete("app_id_status.json") if File.exists?("app_id_status.json")
51
- File.delete("app_id_stats.csv") if File.exists?("app_id_stats.csv")
52
-
53
- @api = Scale.new(@@mu_ip, @@mu_admin_user, @@mu_admin_pass)
54
- @api.configure("pattern", @cmd_line_pattern)
55
- @params = {}
56
- @params["msl"] = scenario
57
- @params["hosts"] = @cmd_line_hosts
58
- run(scenario)
59
- @api.release
60
- end
61
-
62
- # runs through a directory of msl files and executes a Studio Scale test for each one
63
- # * argv = command-line arguments, require a directory (-d) argument
64
- # * optional -r argument for recursive directory search (default is a flat directory)
65
- def cmd_run_dir argv
66
- setup argv
67
-
68
- if not @hash['dir']
69
- raise "*** Error: directory required, using -d option"
70
- else
71
- dir = @hash['dir']
72
- end
73
-
74
- File.delete("app_id_status.json") if File.exists?("app_id_status.json")
75
- File.delete("app_id_stats.csv") if File.exists?("app_id_stats.csv")
76
-
77
- @api = Scale.new(@@mu_ip, @@mu_admin_user, @@mu_admin_pass)
78
- @api.configure("pattern", @cmd_line_pattern)
79
- @params = {}
80
- @params["dir"] = dir
81
- @params["hosts"] = @cmd_line_hosts
82
- Dir.chdir(@params["dir"])
83
- File.delete("app_id_status.json") if File.exists?("app_id_status.json")
84
- if @hash['recursive'].nil?
85
- files = Dir.glob("*.msl")
86
- else
87
- files = Dir.glob("**/*.msl")
88
- end
89
- if !files.empty?
90
- files.sort.each do | f |
91
- run(f)
92
- output_csv(f)
93
- sleep 2
94
- end
95
- else
96
- msg "no msl files found in #{dir}"
97
- end
98
- @api.release
99
- end
100
-
101
- private
102
-
103
- def setup argv
104
- parse_cli argv
105
- @params = {}
106
- @peak_throughput = 0.0
107
-
108
- if @hash['test']
109
- @verify_only = true
110
- else
111
- @verify_only = false
112
- end
113
-
114
- if not @hash['pattern']
115
- @cmd_line_pattern = "{ \"iterations\": 1, \"intervals\": [ {\"iterations\":1, \"end\":100, \"start\":1, \"duration\":20 } ] }"
116
- else
117
- @cmd_line_pattern = @hash['pattern']
118
- end
119
-
120
- if not @hash['interfaces']
121
- @cmd_line_hosts = "b1,b2"
122
- else
123
- @cmd_line_hosts = @hash['interfaces']
124
- end
125
-
126
- if not @hash['testset']
127
- @testset = ""
128
- else
129
- @testset = @hash['testset']
130
- end
131
-
132
- if not @hash['delay']
133
- @delay = 0
134
- else
135
- @delay = @hash['delay'].to_i
136
- end
137
-
138
- if not @hash['no_verify']
139
- @no_verify = false
140
- else
141
- @no_verify = true
142
- end
143
-
144
- end
145
-
146
- def run(msl)
147
- if !File.exists?(msl)
148
- return "file not found: #{msl}"
149
- end
150
-
151
- @api.configure("musl", File.read(msl))
152
-
153
- unless @testset.empty?
154
- if @testset.include?("/") # assume it is the full path in this case
155
- csv_file = @testset
156
- else
157
- csv_file = @params["dir"] + "/" + @testset
158
- end
159
- @api.configure("csv", File.read(csv_file))
160
- end
161
-
162
- set_global_hosts
163
- all_hosts = get_all_hosts_from_musl(msl)
164
- @hosts_config = map_all_hosts_to_json(all_hosts)
165
- @api.configure("hosts", @hosts_config)
166
- @api.configure("delay", @delay)
167
- if @no_verify == false # don't do verify if no_verify==true
168
- msg "verifying #{msl} ..."
169
- response = @api.verify
170
- # sleep 3
171
- v = parse_verify_response(response)
172
- if v.nil?
173
- msg "error in verify"
174
- return
175
- end
176
- if @verify_only
177
- msg v
178
- return
179
- end
180
- end
181
- msg "starting #{msl} ..."
182
- @api.start
183
- start_time = Time.now.to_i
184
- while true
185
- sleep 5
186
- status = @api.status
187
- if !status.nil?
188
- if !status["status"].nil?
189
- if status["status"]["running"] == false
190
- msg "running = #{status["status"]["running"]}", Logger::DEBUG
191
- r = parse_status(status)
192
- dump_status(status, msl)
193
- return
194
- else
195
- r = parse_status(status)
196
- end
197
- else # status['status'].nil? ... no bonafide status was returned
198
- time_now = Time.now.to_i
199
- if time_now - start_time > 20
200
- # puts "\nError: timing out after 20 seconds. Test had failed to start or verify"
201
- break
202
- end
203
- end
204
- end
205
- end
206
- ensure
207
- msg "stopping #{msl} ..."
208
- end
209
-
210
- def set_global_hosts
211
- @hosts = Array.new
212
- @addr_indexes = Array.new
213
- hosts = @params["hosts"]
214
- if !hosts.nil?
215
- p = hosts.split(",")
216
- p.each do | h |
217
- if h.include?("-") # b1-1000,b2-1 to indicate addr_count
218
- q = h.split("-")
219
- @hosts << q[0]
220
- @addr_indexes << q[1]
221
- else # default to the 1st addr index
222
- @hosts << h
223
- @addr_indexes << 1
224
- end
225
- end
226
- else
227
- @hosts = ['b1','b2']
228
- @addr_indexes = [1,1]
229
- end
230
- end
231
-
232
- def dump_status(status, msl)
233
- filename = "app_id_status.json"
234
- f = File.open(filename, "a")
235
- status["filename"] = msl
236
- str = JSON.pretty_generate(status)
237
- f.write(",") if !File.zero?(f) # if appending, we need to insert a comma
238
- f.write(str)
239
- f.close
240
- end
241
-
242
- def output_csv(msl_file)
243
- filename = "app_id_stats.csv"
244
- f = File.open(filename, "a")
245
- doc = "#{msl_file},#{@executed},#{@errors.to_i},#{@timeouts.to_i},#{@client_tx_bytes},#{@client_tx_msgs},#{@client_rx_bytes},#{@client_rx_msgs},#{@server_tx_bytes},#{@server_tx_msgs},#{@server_rx_bytes},#{@server_rx_msgs}\n"
246
- File.open(filename, 'a') {|f| f.write(doc) }
247
- end
248
-
249
- # finds all the hosts in the musl file
250
- def get_all_hosts_from_musl(msl)
251
- f = IO.read(msl)
252
- hosts = f.scan(/host_\d+/)
253
- hosts.uniq!
254
- return hosts
255
- end
256
-
257
- # maps host_0 to the client interface
258
- # maps all other hosts to the server interface
259
- def map_all_hosts_to_json(hosts=[])
260
- new_hosts = Array.new
261
- hosts.each_with_index do | h, i |
262
- if i == 0
263
- new_hosts << @hosts[0] + "/*,#{@addr_indexes[0]}"
264
- else
265
- new_hosts << @hosts[1] + "/*,#{@addr_indexes[1]}"
266
- end
267
- end
268
-
269
- hosts_config = {}
270
-
271
- # assign hosts to consecutive string keys, host_0, host_1, etc ...
272
- new_hosts.each_with_index do | h, i |
273
- hosts_config["host_#{i}"] = h # new_hosts[i]
274
- end
275
-
276
- # convert keys to symbols
277
- new_hosts_config = {}
278
- hosts_config.each_key { |k| new_hosts_config[k.to_sym] = hosts_config[k] }
279
-
280
- return new_hosts_config
281
- end
282
-
283
- def parse_verify_response(response)
284
- if response.nil? # || response.empty?
285
- msg "*** error = no response received from /verify ***\n\n"
286
- return nil
287
- end
288
- begin
289
- msg JSON.pretty_generate(response), Logger::DEBUG
290
- if !response["status"].nil?
291
- if response["status"]["error"] == true
292
- @error = response["status"]["error"]
293
- @reason = response["status"]["reason"]
294
- dump_status(response)
295
- msg "*** Error = #{@error}, reason = #{@reason} ***\n\n"
296
- return nil
297
- end
298
- end
299
- msg "*** verify: okay ***", Logger::DEBUG
300
- return "*** verify: okay ***"
301
- rescue
302
- # could nbe json parse error
303
- return nil
304
- end
305
- end
306
-
307
- def parse_status(status)
308
- return nil if status.nil?
309
- msg JSON.pretty_generate(status), Logger::DEBUG
310
- @reported_volume = 0
311
- if !status["status"]["error"].nil?
312
- if status["status"]["error"] == true
313
- @error = status["status"]["error"]
314
- @reason = status["status"]["reason"]
315
- # puts "*** Error = #{@error}, reason = #{@reason} ***\n\n"
316
- return nil
317
- end
318
- end
319
-
320
- @stats_summary = status["status"]["statistics"]["summary"]
321
- @duration = @stats_summary["duration"]
322
- @instances = @stats_summary["instances"]
323
- @total_instances = @instances["total"]
324
- @executed = @instances["executed"]
325
- @timeouts = @instances["timeouts"]
326
- @errors = @instances["errors"]
327
- @asserts_failed = @stats_summary["asserts"]["failed"]
328
- @server = @stats_summary["server"]
329
- @server_tx_bytes = @server["tx"]["bytes"]
330
- @server_tx_msgs = @server["tx"]["msgs"]
331
- @server_rx_bytes = @server["rx"]["bytes"]
332
- @server_rx_msgs = @server["rx"]["msgs"]
333
- @client = @stats_summary["client"]
334
- @client_tx_bytes = @client["tx"]["bytes"]
335
- @client_tx_msgs = @client["tx"]["msgs"]
336
- @client_rx_bytes = @client["rx"]["bytes"]
337
- @client_rx_msgs = @client["rx"]["msgs"]
338
- @scenarios = status["status"]["statistics"]["scenarios"]
339
- @scenarios.each do | scenario |
340
- @reported_volume = @reported_volume + scenario["volume"]
341
- end
342
-
343
- bits1 = (@client_tx_bytes.to_i + @client_rx_bytes.to_i) * 8
344
- dur1 = @duration.to_f
345
- thruput = format_float(2, bits1.to_f / dur1)
346
-
347
- if thruput.to_f > @peak_throughput
348
- @peak_throughput = thruput.to_f
349
- end
350
-
351
- msg ""
352
- msg "duration: #{format_float(2, @duration)}"
353
- msg "concurrency: #{@reported_volume}"
354
- msg "tests/sec: #{format_float(2, @executed.to_f / @duration)}" if @duration.to_i > 0
355
- msg "bits/sec: #{thruput}" if @duration.to_i > 0
356
- msg "passed: #{@executed}"
357
- msg "errors: #{@errors}"
358
- msg "timeouts: #{@timeouts}"
359
- msg "client tx bytes/sec #{format_float(2, @client_tx_bytes.to_f / @duration)}" if @duration.to_i > 0
360
- msg "client tx msgs/sec #{format_float(2, @client_tx_msgs.to_f / @duration)}" if @duration.to_i > 0
361
- msg "client rx bytes/sec #{format_float(2, @client_rx_bytes.to_f / @duration)}" if @duration.to_i > 0
362
- msg "client rx msgs/sec #{format_float(2, @client_rx_msgs.to_f / @duration)}" if @duration.to_i > 0
363
- msg "server tx bytes/sec #{format_float(2, @server_tx_bytes.to_f / @duration)}" if @duration.to_i > 0
364
- msg "server tx msgs/sec #{format_float(2, @server_tx_msgs.to_f / @duration)}" if @duration.to_i > 0
365
- msg "server rx bytes/sec #{format_float(2, @server_rx_bytes.to_f / @duration)}" if @duration.to_i > 0
366
- msg "server rx msgs/sec #{format_float(2, @server_rx_msgs.to_f / @duration)}" if @duration.to_i > 0
367
- msg ""
368
- end
369
-
370
- def parse_cli argv
371
- @hash = Hash.new
372
- while not argv.empty?
373
- break if argv.first[0,1] != '-'
374
-
375
- k = argv.shift
376
-
377
- if [ '-c', '--csv' ].member? k
378
- @hash['testset'] = shift(k, argv)
379
- next
380
- end
381
-
382
- if [ '-d', '--dir' ].member? k
383
- @hash['dir'] = shift(k, argv)
384
- next
385
- end
386
-
387
- if [ '-i', '--interfaces' ].member? k
388
- @hash['interfaces'] = shift(k, argv)
389
- next
390
- end
391
-
392
- if [ '-h', '--help' ].member? k
393
- help
394
- exit
395
- end
396
-
397
- if [ '-l', '--delay' ].member? k
398
- @hash['delay'] = shift(k, argv)
399
- next
400
- end
401
-
402
- if [ '-m', '--mu_string' ].member? k
403
- mu_string = shift(k, argv)
404
- if mu_string =~ /(.+?):(.+?)@(.*)/
405
- @@mu_admin_user = $1
406
- @@mu_admin_pass = $2
407
- @@mu_ip = $3
408
- end
409
- next
410
- end
411
-
412
- if [ '-n', '--no_verify' ].member? k
413
- @hash['no_verify'] = true
414
- next
415
- end
416
-
417
- if [ '-o', '--output'].member? k
418
- $stdout.reopen(shift(k, argv), "w")
419
- next
420
- end
421
-
422
- if [ '-p', '--pattern' ].member? k
423
- patterns = Array.new
424
- pattern_string = shift(k, argv)
425
- pstrings = pattern_string.split(",")
426
- pstrings.each do | p |
427
- if p =~ /(.+?)-(.+?):(.*)/ # e.g. 1-10000:60
428
- start_vol = $1
429
- end_vol = $2
430
- duration = $3
431
- patterns << "{\"iterations\":1, \"end\":#{end_vol}, \"start\":#{start_vol}, \"duration\":#{duration} }"
432
- end
7
+ class Command
8
+ class Cmd_appid < Command
9
+
10
+ attr_accessor :api, :params, :hosts, :addr_indexes, :hash
11
+
12
+ # displays command-line help
13
+ def cmd_help argv
14
+ help
433
15
  end
434
- ps = "{ \"iterations\": 1, \"intervals\": ["
435
- patterns.each do | p |
436
- ps = ps + p + ","
16
+
17
+ # returns a boolean indicating whether the scale test is running or not
18
+ # * argv = command-line arguments
19
+ def cmd_running? argv
20
+ if @api.nil?
21
+ msg "false"
22
+ return
23
+ end
24
+
25
+ status = @api.status
26
+ if !status.nil?
27
+ if !status["status"].nil?
28
+ msg status["status"]["running"]
29
+ end
30
+ else
31
+ msg "false"
32
+ end
437
33
  end
438
- ps = ps[0..ps.length-2] # remove final comma
439
- ps = ps + "] }"
440
- @hash['pattern'] = ps
441
- next
442
- end
443
-
444
- if [ '-r', '--recursive'].member? k
445
- @hash['recursive'] = true
446
- next
447
- end
448
-
449
- if [ '-s', '--scenario' ].member? k
450
- @hash['scenario'] = shift(k, argv)
451
- next
452
- end
453
-
454
- if [ '-t', '--test' ].member? k
455
- @hash['test'] = true
456
- next
457
- end
458
-
459
- if [ '-v', '--verbose' ].member? k
460
- $log.level = Logger::DEBUG
461
- next
462
- end
463
-
464
- raise ArgumentError, "Unknown option #{k}"
465
- end
466
-
467
- hash
468
- end
469
-
470
- def help
471
- helps = [
472
- { :short => '-c', :long => '--csv', :value => '<string>', :help => 'name of the csv testset to run' },
473
- { :short => '-d', :long => '--dir', :value => '<string>', :help => 'directory containing msl files, required for run_dir' },
474
- { :short => '-h', :long => '--help', :value => '', :help => 'help on command line options' },
475
- { :short => '-i', :long => '--interfaces', :value => '<string>', :help => 'comma-separated list of interfaces, e.g. b1,b2 or b1-1000:0,b2 for ip range and offset' },
476
- { :short => '-l', :long => '--delay', :value => '<string>', :help => 'intra-scenario delay value' },
477
- { :short => '-m', :long => '--mu_string', :value => '<string>', :help => 'user, password, mu_ip in the form of admin:admin@10.9.8.7' },
478
- { :short => '-n', :long => '--no_verify', :value => '', :help => 'do not do verify before start' },
479
- { :short => '-o', :long => '--output', :value => '<string>', :help => 'output logging to this file' },
480
- { :short => '-p', :long => '--pattern', :value => '<string>', :help => 'pattern in the form of comma-separated concurrency_start-end:duration strings, e.g. 1-10000:60,10000-1:30. Duration is in seconds' },
481
- { :short => '-r', :long => '--recursive', :value => '', :help => 'for run_dir, recurse through sub-directories' },
482
- { :short => '-s', :long => '--scenario', :value => '<string>', :help => 'msl file, required for run_msl' },
483
- { :short => '-t', :long => '--test', :value => '', :help => 'do verify only' },
484
- { :short => '-v', :long => '--verbose', :value => '', :help => 'set Logger::DEBUG level' }
485
- ]
486
-
487
- cmds = [
488
- "mu cmd_appid:help",
489
- "mu cmd_appid:run_file -s <file>",
490
- "mu cmd_appid:run_dir -d <dir> [-r]",
491
- "mu cmd_appid:running?"
492
- ]
493
-
494
- max_long_size = helps.inject(0) { |memo, obj| [ obj[:long].size, memo ].max }
495
- max_value_size = helps.inject(0) { |memo, obj| [ obj[:value].size, memo ].max }
496
- puts
497
- puts "Usage: mu cmd_appid:<command> <options>"
498
- puts
499
- helps.each do |h|
500
- puts "%-*s %*s %-*s %s" % [max_long_size, h[:long], 2, h[:short], max_value_size, h[:value], h[:help]]
501
- end
502
- puts
503
- puts "Available Commands"
504
- puts
505
- cmds.each do | c |
506
- puts c
34
+
35
+ # runs a single Studio Scale test
36
+ # * argv = command-line arguments, requires a scenario (-s) argument
37
+ def cmd_run_file argv
38
+ puts "\n***WARNING***\nThe following commands will be deprecated in a future release:"
39
+ puts "cmd_runscale:run_file, cmd_runscale:run_dir, cmd_appid\n"
40
+ puts "Please use 'cmd_runscale:run_files'"
41
+ setup argv
42
+
43
+ if not @hash['scenario']
44
+ raise "*** Error: scenario required, using -s option"
45
+ else
46
+ scenario = @hash['scenario']
47
+ end
48
+
49
+ if !File.exists?(scenario)
50
+ raise "*** Error: Scenario file #{scenario} was not found"
51
+ end
52
+
53
+ File.delete("app_id_status.json") if File.exists?("app_id_status.json")
54
+ File.delete("app_id_stats.csv") if File.exists?("app_id_stats.csv")
55
+
56
+ @api = Scale.new(@@mu_ip, @@mu_admin_user, @@mu_admin_pass)
57
+ @api.configure("pattern", @cmd_line_pattern)
58
+ @params = {}
59
+ @params["msl"] = scenario
60
+ @params["hosts"] = @cmd_line_hosts
61
+ run(scenario)
62
+ @api.release
63
+ end
64
+
65
+ # runs through a directory of msl files and executes a Studio Scale test for each one
66
+ # * argv = command-line arguments, require a directory (-d) argument
67
+ # * optional -r argument for recursive directory search (default is a flat directory)
68
+ def cmd_run_dir argv
69
+ puts "\n***WARNING***\nThe following commands will be deprecated in a future release:"
70
+ puts "cmd_runscale:run_file, cmd_runscale:run_dir, cmd_appid\n"
71
+ puts "Please use 'cmd_runscale:run_files'"
72
+ setup argv
73
+
74
+ if not @hash['dir']
75
+ raise "*** Error: directory required, using -d option"
76
+ else
77
+ dir = @hash['dir']
78
+ end
79
+
80
+ File.delete("app_id_status.json") if File.exists?("app_id_status.json")
81
+ File.delete("app_id_stats.csv") if File.exists?("app_id_stats.csv")
82
+
83
+ @api = Scale.new(@@mu_ip, @@mu_admin_user, @@mu_admin_pass)
84
+ @api.configure("pattern", @cmd_line_pattern)
85
+ @params = {}
86
+ @params["dir"] = dir
87
+ @params["hosts"] = @cmd_line_hosts
88
+ Dir.chdir(@params["dir"])
89
+ File.delete("app_id_status.json") if File.exists?("app_id_status.json")
90
+ if @hash['recursive'].nil?
91
+ files = Dir.glob("*.msl")
92
+ else
93
+ files = Dir.glob("**/*.msl")
94
+ end
95
+ if !files.empty?
96
+ files.sort.each do | f |
97
+ run(f)
98
+ output_csv(f)
99
+ sleep 2
100
+ end
101
+ else
102
+ msg "no msl files found in #{dir}"
103
+ end
104
+ @api.release
105
+ end
106
+
107
+ private
108
+
109
+ def setup argv
110
+ parse_cli argv
111
+ @params = {}
112
+ @peak_throughput = 0.0
113
+
114
+ if @hash['test']
115
+ @verify_only = true
116
+ else
117
+ @verify_only = false
118
+ end
119
+
120
+ if not @hash['pattern']
121
+ @cmd_line_pattern = "{ \"iterations\": 1, \"intervals\": [ {\"iterations\":1, \"end\":100, \"start\":1, \"duration\":20 } ] }"
122
+ else
123
+ @cmd_line_pattern = @hash['pattern']
124
+ end
125
+
126
+ if not @hash['interfaces']
127
+ @cmd_line_hosts = "b1,b2"
128
+ else
129
+ @cmd_line_hosts = @hash['interfaces']
130
+ end
131
+
132
+ if not @hash['testset']
133
+ @testset = ""
134
+ else
135
+ @testset = @hash['testset']
136
+ end
137
+
138
+ if not @hash['delay']
139
+ @delay = 0
140
+ else
141
+ @delay = @hash['delay'].to_i
142
+ end
143
+
144
+ if not @hash['no_verify']
145
+ @no_verify = false
146
+ else
147
+ @no_verify = true
148
+ end
149
+
150
+ end
151
+
152
+ def run(msl)
153
+ if !File.exists?(msl)
154
+ return "file not found: #{msl}"
155
+ end
156
+
157
+ @api.configure("musl", File.read(msl))
158
+
159
+ unless @testset.empty?
160
+ if @testset.include?("/") # assume it is the full path in this case
161
+ csv_file = @testset
162
+ else
163
+ csv_file = @params["dir"] + "/" + @testset
164
+ end
165
+ @api.configure("csv", File.read(csv_file))
166
+ end
167
+
168
+ set_global_hosts
169
+ all_hosts = get_all_hosts_from_musl(msl)
170
+ @hosts_config = map_all_hosts_to_json(all_hosts)
171
+ @api.configure("hosts", @hosts_config)
172
+ @api.configure("delay", @delay)
173
+ if @no_verify == false # don't do verify if no_verify==true
174
+ msg "verifying #{msl} ..."
175
+ response = @api.verify
176
+ # sleep 3
177
+ v = parse_verify_response(response)
178
+ if v.nil?
179
+ msg "error in verify"
180
+ return
181
+ end
182
+ if @verify_only
183
+ msg v
184
+ return
185
+ end
186
+ end
187
+ msg "starting #{msl} ..."
188
+ @api.start
189
+ start_time = Time.now.to_i
190
+ while true
191
+ sleep 5
192
+ status = @api.status
193
+ if !status.nil?
194
+ if !status["status"].nil?
195
+ if status["status"]["running"] == false
196
+ msg "running = #{status["status"]["running"]}", Logger::DEBUG
197
+ r = parse_status(status)
198
+ dump_status(status, msl)
199
+ return
200
+ else
201
+ r = parse_status(status)
202
+ end
203
+ else # status['status'].nil? ... no bonafide status was returned
204
+ time_now = Time.now.to_i
205
+ if time_now - start_time > 20
206
+ # puts "\nError: timing out after 20 seconds. Test had failed to start or verify"
207
+ break
208
+ end
209
+ end
210
+ end
211
+ end
212
+ ensure
213
+ msg "stopping #{msl} ..."
214
+ end
215
+
216
+ def set_global_hosts
217
+ @hosts = Array.new
218
+ @addr_indexes = Array.new
219
+ hosts = @params["hosts"]
220
+ if !hosts.nil?
221
+ p = hosts.split(",")
222
+ p.each do | h |
223
+ if h.include?("-") # b1-1000,b2-1 to indicate addr_count
224
+ q = h.split("-")
225
+ @hosts << q[0]
226
+ @addr_indexes << q[1]
227
+ else # default to the 1st addr index
228
+ @hosts << h
229
+ @addr_indexes << 1
230
+ end
231
+ end
232
+ else
233
+ @hosts = ['b1','b2']
234
+ @addr_indexes = [1,1]
235
+ end
236
+ end
237
+
238
+ def dump_status(status, msl)
239
+ filename = "app_id_status.json"
240
+ f = File.open(filename, "a")
241
+ status["filename"] = msl
242
+ str = JSON.pretty_generate(status)
243
+ f.write(",") if !File.zero?(f) # if appending, we need to insert a comma
244
+ f.write(str)
245
+ f.close
246
+ end
247
+
248
+ def output_csv(msl_file)
249
+ filename = "app_id_stats.csv"
250
+ doc = "#{msl_file},#{@executed},#{@errors.to_i},#{@timeouts.to_i},#{@client_tx_bytes},#{@client_tx_msgs},#{@client_rx_bytes},#{@client_rx_msgs},#{@server_tx_bytes},#{@server_tx_msgs},#{@server_rx_bytes},#{@server_rx_msgs}\n"
251
+ File.open(filename, File::RDWR|File::TRUNC|File::CREAT) {|f| f.write(doc) }
252
+ end
253
+
254
+ # finds all the hosts in the musl file
255
+ def get_all_hosts_from_musl(msl)
256
+ f = IO.read(msl)
257
+ hosts = f.scan(/host_\d+/)
258
+ hosts.uniq!
259
+ return hosts
260
+ end
261
+
262
+ # maps host_0 to the client interface
263
+ # maps all other hosts to the server interface
264
+ def map_all_hosts_to_json(hosts=[])
265
+ new_hosts = Array.new
266
+ hosts.each_with_index do | h, i |
267
+ if i == 0
268
+ new_hosts << @hosts[0] + "/*,#{@addr_indexes[0]}"
269
+ else
270
+ new_hosts << @hosts[1] + "/*,#{@addr_indexes[1]}"
271
+ end
272
+ end
273
+
274
+ hosts_config = {}
275
+
276
+ # assign hosts to consecutive string keys, host_0, host_1, etc ...
277
+ new_hosts.each_with_index do | h, i |
278
+ hosts_config["host_#{i}"] = h # new_hosts[i]
279
+ end
280
+
281
+ # convert keys to symbols
282
+ new_hosts_config = {}
283
+ hosts_config.each_key { |k| new_hosts_config[k.to_sym] = hosts_config[k] }
284
+
285
+ return new_hosts_config
286
+ end
287
+
288
+ def parse_verify_response(response)
289
+ if response.nil? # || response.empty?
290
+ msg "*** error = no response received from /verify ***\n\n"
291
+ return nil
292
+ end
293
+ begin
294
+ msg JSON.pretty_generate(response), Logger::DEBUG
295
+ if !response["status"].nil?
296
+ if response["status"]["error"] == true
297
+ @error = response["status"]["error"]
298
+ @reason = response["status"]["reason"]
299
+ dump_status(response)
300
+ msg "*** Error = #{@error}, reason = #{@reason} ***\n\n"
301
+ return nil
302
+ end
303
+ end
304
+ msg "*** verify: okay ***", Logger::DEBUG
305
+ return "*** verify: okay ***"
306
+ rescue
307
+ # could nbe json parse error
308
+ return nil
309
+ end
310
+ end
311
+
312
+ def parse_status(status)
313
+ return nil if status.nil?
314
+ msg JSON.pretty_generate(status), Logger::DEBUG
315
+ @reported_volume = 0
316
+ if !status["status"]["error"].nil?
317
+ if status["status"]["error"] == true
318
+ @error = status["status"]["error"]
319
+ @reason = status["status"]["reason"]
320
+ # puts "*** Error = #{@error}, reason = #{@reason} ***\n\n"
321
+ return nil
322
+ end
323
+ end
324
+
325
+ @stats_summary = status["status"]["statistics"]["summary"]
326
+ @duration = @stats_summary["duration"]
327
+ @instances = @stats_summary["instances"]
328
+ @total_instances = @instances["total"]
329
+ @executed = @instances["executed"]
330
+ @timeouts = @instances["timeouts"]
331
+ @errors = @instances["errors"]
332
+ @asserts_failed = @stats_summary["asserts"]["failed"]
333
+ @server = @stats_summary["server"]
334
+ @server_tx_bytes = @server["tx"]["bytes"]
335
+ @server_tx_msgs = @server["tx"]["msgs"]
336
+ @server_rx_bytes = @server["rx"]["bytes"]
337
+ @server_rx_msgs = @server["rx"]["msgs"]
338
+ @client = @stats_summary["client"]
339
+ @client_tx_bytes = @client["tx"]["bytes"]
340
+ @client_tx_msgs = @client["tx"]["msgs"]
341
+ @client_rx_bytes = @client["rx"]["bytes"]
342
+ @client_rx_msgs = @client["rx"]["msgs"]
343
+ @scenarios = status["status"]["statistics"]["scenarios"]
344
+ @scenarios.each do | scenario |
345
+ @reported_volume = @reported_volume + scenario["volume"]
346
+ end
347
+
348
+ bits1 = (@client_tx_bytes.to_i + @client_rx_bytes.to_i) * 8
349
+ dur1 = @duration.to_f
350
+ thruput = format_float(2, bits1.to_f / dur1)
351
+
352
+ if thruput.to_f > @peak_throughput
353
+ @peak_throughput = thruput.to_f
354
+ end
355
+
356
+ msg ""
357
+ msg "duration: #{format_float(2, @duration)}"
358
+ msg "concurrency: #{@reported_volume}"
359
+ msg "tests/sec: #{format_float(2, @executed.to_f / @duration)}" if @duration.to_i > 0
360
+ msg "bits/sec: #{thruput}" if @duration.to_i > 0
361
+ msg "passed: #{@executed}"
362
+ msg "errors: #{@errors}"
363
+ msg "timeouts: #{@timeouts}"
364
+ msg "client tx bytes/sec #{format_float(2, @client_tx_bytes.to_f / @duration)}" if @duration.to_i > 0
365
+ msg "client tx msgs/sec #{format_float(2, @client_tx_msgs.to_f / @duration)}" if @duration.to_i > 0
366
+ msg "client rx bytes/sec #{format_float(2, @client_rx_bytes.to_f / @duration)}" if @duration.to_i > 0
367
+ msg "client rx msgs/sec #{format_float(2, @client_rx_msgs.to_f / @duration)}" if @duration.to_i > 0
368
+ msg "server tx bytes/sec #{format_float(2, @server_tx_bytes.to_f / @duration)}" if @duration.to_i > 0
369
+ msg "server tx msgs/sec #{format_float(2, @server_tx_msgs.to_f / @duration)}" if @duration.to_i > 0
370
+ msg "server rx bytes/sec #{format_float(2, @server_rx_bytes.to_f / @duration)}" if @duration.to_i > 0
371
+ msg "server rx msgs/sec #{format_float(2, @server_rx_msgs.to_f / @duration)}" if @duration.to_i > 0
372
+ msg ""
373
+ end
374
+
375
+ def parse_cli argv
376
+ @hash = Hash.new
377
+ while not argv.empty?
378
+ break if argv.first[0,1] != '-'
379
+
380
+ k = argv.shift
381
+
382
+ if [ '-c', '--csv' ].member? k
383
+ @hash['testset'] = shift(k, argv)
384
+ next
385
+ end
386
+
387
+ if [ '-d', '--dir' ].member? k
388
+ @hash['dir'] = shift(k, argv)
389
+ next
390
+ end
391
+
392
+ if [ '-i', '--interfaces' ].member? k
393
+ @hash['interfaces'] = shift(k, argv)
394
+ next
395
+ end
396
+
397
+ if [ '-h', '--help' ].member? k
398
+ help
399
+ exit
400
+ end
401
+
402
+ if [ '-l', '--delay' ].member? k
403
+ @hash['delay'] = shift(k, argv)
404
+ next
405
+ end
406
+
407
+ if [ '-m', '--mu_string' ].member? k
408
+ mu_string = shift(k, argv)
409
+ if mu_string =~ /(.+?):(.+?)@(.*)/
410
+ @@mu_admin_user = $1
411
+ @@mu_admin_pass = $2
412
+ @@mu_ip = $3
413
+ end
414
+ next
415
+ end
416
+
417
+ if [ '-n', '--no_verify' ].member? k
418
+ @hash['no_verify'] = true
419
+ next
420
+ end
421
+
422
+ if [ '-o', '--output'].member? k
423
+ $stdout.reopen(shift(k, argv), "w")
424
+ next
425
+ end
426
+
427
+ if [ '-p', '--pattern' ].member? k
428
+ patterns = Array.new
429
+ pattern_string = shift(k, argv)
430
+ pstrings = pattern_string.split(",")
431
+ pstrings.each do | p |
432
+ if p =~ /(.+?)-(.+?):(.*)/ # e.g. 1-10000:60
433
+ start_vol = $1
434
+ end_vol = $2
435
+ duration = $3
436
+ patterns << "{\"iterations\":1, \"end\":#{end_vol}, \"start\":#{start_vol}, \"duration\":#{duration} }"
437
+ end
438
+ end
439
+ ps = "{ \"iterations\": 1, \"intervals\": ["
440
+ patterns.each do | p |
441
+ ps = ps + p + ","
442
+ end
443
+ ps = ps[0..ps.length-2] # remove final comma
444
+ ps = ps + "] }"
445
+ @hash['pattern'] = ps
446
+ next
447
+ end
448
+
449
+ if [ '-r', '--recursive'].member? k
450
+ @hash['recursive'] = true
451
+ next
452
+ end
453
+
454
+ if [ '-s', '--scenario' ].member? k
455
+ @hash['scenario'] = shift(k, argv)
456
+ next
457
+ end
458
+
459
+ if [ '-t', '--test' ].member? k
460
+ @hash['test'] = true
461
+ next
462
+ end
463
+
464
+ if [ '-v', '--verbose' ].member? k
465
+ $log.level = Logger::DEBUG
466
+ next
467
+ end
468
+
469
+ raise ArgumentError, "Unknown option #{k}"
470
+ end
471
+
472
+ hash
473
+ end
474
+
475
+ def help
476
+ helps = [
477
+ { :short => '-c', :long => '--csv', :value => '<string>', :help => 'name of the csv testset to run' },
478
+ { :short => '-d', :long => '--dir', :value => '<string>', :help => 'directory containing msl files, required for run_dir' },
479
+ { :short => '-h', :long => '--help', :value => '', :help => 'help on command line options' },
480
+ { :short => '-i', :long => '--interfaces', :value => '<string>', :help => 'comma-separated list of interfaces, e.g. b1,b2 or b1-1000:0,b2 for ip range and offset' },
481
+ { :short => '-l', :long => '--delay', :value => '<string>', :help => 'intra-scenario delay value' },
482
+ { :short => '-m', :long => '--mu_string', :value => '<string>', :help => 'user, password, mu_ip in the form of admin:admin@10.9.8.7' },
483
+ { :short => '-n', :long => '--no_verify', :value => '', :help => 'do not do verify before start' },
484
+ { :short => '-o', :long => '--output', :value => '<string>', :help => 'output logging to this file' },
485
+ { :short => '-p', :long => '--pattern', :value => '<string>', :help => 'pattern in the form of comma-separated concurrency_start-end:duration strings, e.g. 1-10000:60,10000-1:30. Duration is in seconds' },
486
+ { :short => '-r', :long => '--recursive', :value => '', :help => 'for run_dir, recurse through sub-directories' },
487
+ { :short => '-s', :long => '--scenario', :value => '<string>', :help => 'msl file, required for run_msl' },
488
+ { :short => '-t', :long => '--test', :value => '', :help => 'do verify only' },
489
+ { :short => '-v', :long => '--verbose', :value => '', :help => 'set Logger::DEBUG level' }
490
+ ]
491
+
492
+ cmds = [
493
+ "mu cmd_appid:help",
494
+ "mu cmd_appid:run_file -s <file>",
495
+ "mu cmd_appid:run_dir -d <dir> [-r]",
496
+ "mu cmd_appid:running?"
497
+ ]
498
+
499
+ max_long_size = helps.inject(0) { |memo, obj| [ obj[:long].size, memo ].max }
500
+ max_value_size = helps.inject(0) { |memo, obj| [ obj[:value].size, memo ].max }
501
+ puts "\n***WARNING***\nThe following commands will be deprecated in a future release:"
502
+ puts "cmd_runscale:run_file, cmd_runscale:run_dir, cmd_appid\n"
503
+ puts "Please use 'cmd_runscale:run_files'"
504
+ puts
505
+ puts "Usage: mu cmd_appid:<command> <options>"
506
+ puts
507
+ helps.each do |h|
508
+ puts "%-*s %*s %-*s %s" % [max_long_size, h[:long], 2, h[:short], max_value_size, h[:value], h[:help]]
509
+ end
510
+ puts
511
+ puts "Available Commands"
512
+ puts
513
+ cmds.each do | c |
514
+ puts c
515
+ end
516
+ puts
517
+ puts "Outputs"
518
+ puts
519
+ puts "app_id_stats.csv"
520
+ puts "scenario_name , passed , errors , timeouts,"
521
+ puts "client tx bytes/sec , client tx msgs/sec , client rx bytes/sec , client rx msgs/src,"
522
+ puts "server tx bytes/sec , server tx msgs/sec , server rx bytes/sec , server rx msgs/src"
523
+ puts
524
+ puts "app_id_status.json"
525
+ puts "contains the last status json object returned from polling, per scenario"
526
+ end
527
+
507
528
  end
508
- puts
509
- puts "Outputs"
510
- puts
511
- puts "app_id_stats.csv"
512
- puts "scenario_name , passed , errors , timeouts,"
513
- puts "client tx bytes/sec , client tx msgs/sec , client rx bytes/sec , client rx msgs/src,"
514
- puts "server tx bytes/sec , server tx msgs/sec , server rx bytes/sec , server rx msgs/src"
515
- puts
516
- puts "app_id_status.json"
517
- puts "contains the last status json object returned from polling, per scenario"
518
- end
519
-
520
- end
521
- end # Command
529
+ end # Command
522
530
  end # Mu
523
531