mu 5.7.2.7 → 5.7.8

Sign up to get free protection for your applications and to get access to all the features.
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