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.
@@ -4,604 +4,630 @@
4
4
  # to specify the Mu parameters, 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_runscale < Command
9
-
10
- attr_accessor :api, :params, :hosts, :addr_indexes, :offset_indexes, :peak_concurrency, :peak_throughput
11
-
12
- # displays command-line help
13
- # * argv = command-line arguments
14
- def cmd_help argv
15
- help
16
- end
17
-
18
- # sets up, executes, and closes a Studio Scale test
19
- # * argv = command-line arguments , requires a scenario (-s) argument
20
- def cmd_run_file argv
21
- setup argv
22
-
23
- if not @hash['scenario']
24
- msg "scenario required"
25
- return help
26
- else
27
- if @hash['scenario'].include?(".msl")
28
- scenario = @hash['scenario']
29
- else # TODO: eventually, xml and mus file may be supported by scale api
30
- msg "only .msl files are currently supported"
31
- return help
32
- end
33
- end
34
-
35
- if not @hash['dir']
36
- @dir = ""
37
- path = scenario
38
- else
39
- @dir = @hash['dir']
40
- path = @dir + "/" + scenario
41
- end
42
-
43
- if !File.exists?(path)
44
- raise "*** Error: File #{path} does not exist"
45
- end
46
-
47
- @api = Scale.new(@@mu_ip, @@mu_admin_user, @@mu_admin_pass)
48
- @api.configure("pattern", @cmd_line_pattern)
49
- @params = {}
50
- @params["dir"] = @dir
51
- @params["msl"] = scenario
52
- @params["hosts"] = @cmd_line_hosts
53
- run(scenario)
54
- @api.release
55
- end
56
-
57
- # sets up, executes, and closes a Studio Scale test for each scenario (.msl file) found in the specified directory
58
- # * argv = command-line arguments, requires a directory (-d) argument
59
- # * optional -r argument for recursive directory search (default is a flat directory)
60
- def cmd_run_dir argv
61
- setup argv
62
-
63
- if not @hash['dir']
64
- return help
65
- else
66
- @dir = @hash['dir']
67
- end
68
-
69
- File.delete("app_id_status.json") if File.exists?("app_id_status.json")
70
- File.delete("app_id_stats.csv") if File.exists?("app_id_stats.csv")
71
-
72
- @api = Scale.new(@@mu_ip, @@mu_admin_user, @@mu_admin_pass)
73
- @api.configure("pattern", @cmd_line_pattern)
74
- @params = {}
75
- @params["dir"] = @dir
76
- @params["hosts"] = @cmd_line_hosts
77
- Dir.chdir(@params["dir"])
78
- File.delete("app_id_status.json") if File.exists?("app_id_status.json")
79
- if @hash['recursive'].nil?
80
- files = Dir.glob("*.msl")
81
- else
82
- files = Dir.glob("**/*.msl")
83
- end
84
- if !files.empty?
85
- files.sort.each do | f |
86
- run(f)
87
- output_csv(f)
88
- sleep 2
89
- end
90
- else
91
- msg "no msl files found in #{@dir}"
92
- end
93
- @api.release
94
- end
95
-
96
- private
97
-
98
- def setup argv
99
- parse_cli argv
100
- @params = {}
101
- @peak_concurrency = 0
102
- @peak_throughput = 0.0
103
-
104
- if @hash['test']
105
- @verify_only = true
106
- else
107
- @verify_only = false
108
- end
109
-
110
- if not @hash['testset']
111
- @testset = ""
112
- else
113
- @testset = @hash['testset']
114
- end
115
-
116
- if not @hash['pattern']
117
- @cmd_line_pattern = "{ \"iterations\": 1, \"intervals\": [ {\"iterations\":1, \"end\":100, \"start\":1, \"duration\":20 } ] }"
118
- else
119
- @cmd_line_pattern = @hash['pattern']
120
- end
121
-
122
- if not @hash['interfaces']
123
- @cmd_line_hosts = "b1,b2"
124
- else
125
- @cmd_line_hosts = @hash['interfaces']
126
- end
127
-
128
- if not @hash['delay']
129
- @delay = 0
130
- else
131
- @delay = @hash['delay'].to_i
132
- end
133
-
134
- if not @hash['no_verify']
135
- @no_verify = false
136
- else
137
- @no_verify = true
138
- end
139
-
140
- if not @hash['vector_address_pairing']
141
- @vector_address_pairing = false
142
- @limit_concurrency = false
143
- else
144
- @vector_address_pairing = true
145
- @limit_concurrency = true
146
- end
7
+ class Command
8
+ class Cmd_runscale < Command
147
9
 
148
- =begin
149
- if not @hash['hold_concurrency']
150
- @hold_concurrency = false
151
- else
152
- @hold_concurrency = true
153
- end
154
- =end
155
-
156
- end
157
-
158
- def run(scenario)
159
- # assume the scenario and testset files are in dir unless they contain '/'
160
- # in which case they are assumed to be absolute paths
161
- if scenario.include?("/")
162
- musl_file = scenario
163
- else
164
- musl_file = @params["dir"] + "/" + scenario
165
- end
166
- # msg musl_file, Logger::DEBUG
167
- @api.configure("musl", File.read(musl_file))
168
-
169
- unless @testset.empty?
170
- if @testset.include?("/")
171
- csv_file = @testset
172
- else
173
- csv_file = @params["dir"] + "/" + @testset
174
- end
175
- @api.configure("csv", File.read(csv_file))
176
- end
177
-
178
- File.delete("app_id_status.json") if File.exists?("app_id_status.json")
179
- File.delete("app_id_stats.csv") if File.exists?("app_id_stats.csv")
180
-
181
- configure_hosts
182
- @api.configure("delay", @delay)
183
- @api.configure("vectorAddressPairing", @vector_address_pairing)
184
- @api.configure("limitConcurrency", @limit_concurrency)
185
- # @api.configure("holdConcurrency", @hold_concurrency)
186
-
187
- if @no_verify == false # don't do verify if no_verify==true
188
- msg "verifying #{scenario} ..."
189
- response = @api.verify
190
- msg response, Logger::DEBUG
191
- # sleep 3
192
- v = parse_verify_response(response)
193
- msg "#{v}", Logger::DEBUG
194
- if v.nil?
195
- msg "error in verify"
196
- return
197
- end
198
- if @verify_only
199
- msg v
200
- return
201
- end
202
- end
203
-
204
- msg "starting #{scenario} ..."
205
- @api.start
206
- start_time = Time.now.to_i
207
- while true
208
- sleep 5
209
- status = @api.status
210
- if !status.nil?
211
- if !status["status"].nil?
212
- if status["status"]["running"] == false
213
- msg "running = #{status["status"]["running"]}", Logger::DEBUG
214
- r = parse_status(status)
215
- dump_status(status, musl_file)
216
- return
217
- else
218
- r = parse_status(status)
219
- end
220
- else # status['status'].nil? ... no bonafide status was returned
221
- time_now = Time.now.to_i
222
- if time_now - start_time > 20
223
- # puts "\nError: timing out after 20 seconds. Test had failed to start or verify"
224
- break
225
- end
226
- end
227
- end
228
- end
229
- ensure
230
- msg "stopping #{scenario} ..."
231
- end
232
-
233
- def cmd_running?
234
- if @api.nil?
235
- msg "false"
236
- return
237
- end
238
-
239
- status = @api.status
240
- if !status.nil?
241
- if !status["status"].nil?
242
- msg status["status"]["running"]
243
- end
244
- else
245
- msg "false"
246
- end
247
- end
248
-
249
- def configure_hosts
250
- @hosts = Array.new
251
- @addr_indexes = Array.new
252
- @offset_indexes = Array.new
253
- hosts = @params["hosts"]
254
- if !hosts.nil?
255
- p = hosts.split(",")
256
- p.each do | h |
257
- if h.include?("-") # b1-1000,b2-1 to indicate addr_count
258
- q = h.split("-")
259
- @hosts << q[0]
260
- if q[1].include?(":") # -1000:20 to indicate offset within range
261
- r = q[1].split(":")
262
- @addr_indexes << r[0]
263
- @offset_indexes << r[1]
264
- else
265
- @addr_indexes << q[1]
266
- @offset_indexes << 1
267
- end
268
- else # default to the 1st addr index
269
- @hosts << h
270
- @addr_indexes << 1
271
- @offset_indexes << 1
272
- end
273
- end
274
- else
275
- @hosts = ['b1','b2']
276
- @addr_indexes = [1,1]
277
- @offset_indexes = [1,1]
278
- end
279
-
280
- set_hosts_byname(@hosts, @addr_indexes, @offset_indexes)
281
- end
282
-
283
- def set_hosts_byname(hosts=@hosts, count=[1,1], offset=[1,1], v4=true)
284
- new_hosts = Array.new
285
- str = ""
286
- hosts.each_with_index do |n, i|
287
- if n.match(/^[ab][1-4]$/) or n.include?(".") # possible vlan
288
- if count[i] == 1 or count[i].nil?
289
- str = "#{n}/*"
290
- else
291
- str = "#{n}/*,#{count[i]},#{offset[i]}"
292
- end
293
- msg "using host #{str}", Logger::DEBUG
294
- else
295
- @net = Netconfig.new
296
- @net.setup(@hosts, @username, @password)
297
- if v4
298
- addr = @net.get("hosts/#{n}")['v4_addr']
299
- else
300
- addr = @net.get("hosts/#{n}")['v6_addr']
301
- end
302
- str = "#{addr}"
303
- msg "using host #{str}", Logger::DEBUG
304
- end
305
- new_hosts << str
306
- end
307
- set_hosts(new_hosts)
308
- end
309
-
310
- # expects full strings: e.g. b1/12.89.0.1 ...
311
- def set_hosts(hosts=["b1","b2"])
312
- host_params = {}
313
-
314
- # assign hosts to consecutive string keys, host_0, host_1, etc ...
315
- hosts.each_with_index do | h, i |
316
- host_params["host_#{i}"] = hosts[i]
317
- end
318
-
319
- # convert keys to symbols
320
- # host_params.each_key { |k| host_params[k.to_sym] = host_params[k] }
321
- new_host_params = {}
322
- host_params.each_key { |k| new_host_params[k.to_sym] = host_params[k] }
10
+ attr_accessor :api, :params, :hosts, :addr_indexes, :offset_indexes, :peak_concurrency, :peak_throughput
323
11
 
324
- =begin
325
- # add default host, set to host_1 if it exists, unless specified on the command-line
326
- if not @hash['default_host']
327
- new_host_params[:default_host] = new_host_params[:host_1] unless new_host_params[:host_1].nil?
328
- else
329
- new_host_params[:default_host] = @hash['default_host']
330
- end
331
- =end
332
-
333
- @api.configure("hosts", new_host_params)
334
-
335
- end
336
-
337
- def dump_status(status, msl)
338
- filename = "app_id_status.json"
339
- f = File.open(filename, "a")
340
- status["filename"] = msl
341
- str = JSON.pretty_generate(status)
342
- f.write(",") if !File.zero?(f) # if appending, we need to insert a comma
343
- f.write(str)
344
- f.close
345
- end
346
-
347
- def output_csv(msl_file)
348
- filename = "app_id_stats.csv"
349
- f = File.open(filename, "a")
350
- 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"
351
- File.open(filename, 'a') {|f| f.write(doc) }
352
- end
353
-
354
- def parse_verify_response(response)
355
- if response.nil? # || response.empty?
356
- msg "*** error = no response received from /verify ***\n\n"
357
- return nil
358
- end
359
- begin
360
- msg JSON.pretty_generate(response), Logger::DEBUG
361
- if !response["status"].nil?
362
- if response["status"]["error"] == true
363
- @error = response["status"]["error"]
364
- @reason = response["status"]["reason"]
365
- dump_status(response)
366
- msg "*** Error = #{@error}, reason = #{@reason} ***\n\n"
367
- return nil
368
- end
369
- end
370
- msg "*** verify: okay ***", Logger::DEBUG
371
- return "*** verify: okay ***"
372
- rescue => e
373
- msg e, Logger::DEBUG
374
- return nil
375
- end
376
- end
377
-
378
- def parse_status(status)
379
- return nil if status.nil?
380
- @reported_volume = 0
381
- if !status["status"]["error"].nil?
382
- if status["status"]["error"] == true
383
- @error = status["status"]["error"]
384
- @reason = status["status"]["reason"]
385
- msg "*** Error = #{@error}, reason = #{@reason} ***\n\n"
386
- return nil
387
- end
388
- end
389
-
390
- @stats_summary = status["status"]["statistics"]["summary"]
391
- @duration = @stats_summary["duration"]
392
- @instances = @stats_summary["instances"]
393
- @total_instances = @instances["total"]
394
- @executed = @instances["executed"]
395
- @timeouts = @instances["timeouts"]
396
- @errors = @instances["errors"]
397
- @asserts_failed = @stats_summary["asserts"]["failed"]
398
- @server = @stats_summary["server"]
399
- @server_tx_bytes = @server["tx"]["bytes"]
400
- @server_tx_msgs = @server["tx"]["msgs"]
401
- @server_rx_bytes = @server["rx"]["bytes"]
402
- @server_rx_msgs = @server["rx"]["msgs"]
403
- @client = @stats_summary["client"]
404
- @client_tx_bytes = @client["tx"]["bytes"]
405
- @client_tx_msgs = @client["tx"]["msgs"]
406
- @client_rx_bytes = @client["rx"]["bytes"]
407
- @client_rx_msgs = @client["rx"]["msgs"]
408
- @scenarios = status["status"]["statistics"]["scenarios"]
409
- @scenarios.each do | scenario |
410
- @reported_volume = @reported_volume + scenario["volume"]
411
- end
412
-
413
- if @reported_volume.to_i > @peak_concurrency
414
- @peak_concurrency = @reported_volume.to_i
415
- end
416
-
417
- bits1 = (@client_tx_bytes.to_i + @client_rx_bytes.to_i) * 8
418
- dur1 = @duration.to_f
419
- thruput = format_float(2, bits1.to_f / dur1)
420
-
421
- if thruput.to_f > @peak_throughput
422
- @peak_throughput = thruput.to_f
423
- end
424
-
425
- msg ""
426
- msg "duration: #{format_float(2, @duration)}"
427
- msg "concurrency: #{@reported_volume}"
428
- msg "tests/sec: #{format_float(2, @executed.to_f / @duration)}" if @duration.to_i > 0
429
- msg "bits/sec: #{thruput}" if @duration.to_i > 0
430
- msg "passed: #{@executed}"
431
- msg "errors: #{@errors}"
432
- msg "timeouts: #{@timeouts}"
433
- msg "client tx bytes/sec #{format_float(2, @client_tx_bytes.to_f / @duration)}" if @duration.to_i > 0
434
- msg "client tx msgs/sec #{format_float(2, @client_tx_msgs.to_f / @duration)}" if @duration.to_i > 0
435
- msg "client rx bytes/sec #{format_float(2, @client_rx_bytes.to_f / @duration)}" if @duration.to_i > 0
436
- msg "client rx msgs/sec #{format_float(2, @client_rx_msgs.to_f / @duration)}" if @duration.to_i > 0
437
- msg "server tx bytes/sec #{format_float(2, @server_tx_bytes.to_f / @duration)}" if @duration.to_i > 0
438
- msg "server tx msgs/sec #{format_float(2, @server_tx_msgs.to_f / @duration)}" if @duration.to_i > 0
439
- msg "server rx bytes/sec #{format_float(2, @server_rx_bytes.to_f / @duration)}" if @duration.to_i > 0
440
- msg "server rx msgs/sec #{format_float(2, @server_rx_msgs.to_f / @duration)}" if @duration.to_i > 0
441
- msg ""
442
- end
443
-
444
- def parse_cli argv
445
- @hash = Hash.new
446
- while not argv.empty?
447
- break if argv.first[0,1] != '-'
448
-
449
- k = argv.shift
450
-
451
- if [ '-a', '--vector_address_pairing' ].member? k
452
- @hash['vector_address_pairing'] = true
453
- next
454
- end
12
+ # displays command-line help
13
+ # * argv = command-line arguments
14
+ def cmd_help argv
15
+ help
16
+ end
455
17
 
456
- if [ '-c', '--csv' ].member? k
457
- @hash['testset'] = shift(k, argv)
458
- next
459
- end
18
+ # sets up, executes, and closes a Studio Scale test
19
+ # * argv = command-line arguments , requires a scenario (-s) argument
20
+ def cmd_run_file argv
21
+ puts "\n***WARNING***\nThe following commands will be deprecated in a future release:"
22
+ puts "cmd_runscale:run_file, cmd_runscale:run_dir, cmd_appid\n"
23
+ puts "Please use 'cmd_runscale:run_files'"
24
+ setup argv
25
+
26
+ if not @hash['scenario']
27
+ msg "scenario required", Logger::ERROR
28
+ return help
29
+ else
30
+ if @hash['scenario'].include?(".msl")
31
+ scenario = @hash['scenario']
32
+ else # TODO: eventually, xml and mus file may be supported by scale api
33
+ msg "only .msl files are currently supported", Logger::ERROR
34
+ return help
35
+ end
36
+ end
37
+
38
+ if not File.readable?(scenario)
39
+ msg "*** Error: can't read scenario file #{scenario}", Logger::ERROR
40
+ return
41
+ end
42
+
43
+ @api = Scale.new(@@mu_ip, @@mu_admin_user, @@mu_admin_pass)
44
+ @api.configure("pattern", @cmd_line_pattern)
45
+ @params = {}
46
+ @params["dir"] = @dir
47
+ @params["msl"] = scenario
48
+ @params["hosts"] = @cmd_line_hosts
49
+ run(scenario)
50
+ @api.release
51
+ end
460
52
 
461
- if [ '-d', '--dir' ].member? k
462
- @hash['dir'] = shift(k, argv)
463
- next
464
- end
53
+ # sets up, executes, and closes a Studio Scale test for each scenario (.msl file) found in the specified directory
54
+ # * argv = command-line arguments, requires a directory (-d) argument
55
+ # * optional -r argument for recursive directory search (default is a flat directory)
56
+ def cmd_run_dir argv
57
+ puts "\n***WARNING***\nThe following commands will be deprecated in a future release:"
58
+ puts "cmd_runscale:run_file, cmd_runscale:run_dir, cmd_appid\n"
59
+ puts "Please use 'cmd_runscale:run_files'"
60
+ setup argv
61
+
62
+ if not @hash['dir']
63
+ return help
64
+ else
65
+ @dir = @hash['dir']
66
+ end
67
+
68
+ msg "Clean up existing stats files: app_id_status.json, app_id_stats.csv", Logger::INFO
69
+ File.delete("app_id_status.json") if File.exists?("app_id_status.json")
70
+ File.delete("app_id_stats.csv") if File.exists?("app_id_stats.csv")
71
+
72
+ @api = Scale.new(@@mu_ip, @@mu_admin_user, @@mu_admin_pass)
73
+ @api.configure("pattern", @cmd_line_pattern)
74
+ @params = {}
75
+ @params["dir"] = @dir
76
+ @params["hosts"] = @cmd_line_hosts
77
+ recursive = (@hash['recursive'].nil?) ? "": "**"
78
+ files = Dir.glob(File.join(@dir,recursive,"*.msl"))
79
+ if !files.empty?
80
+ files.sort.each do | f |
81
+ run(f)
82
+ output_csv(f)
83
+ sleep 2
84
+ end
85
+ else
86
+ msg "no msl files found in #{@dir}", Logger::ERROR
87
+ end
88
+ @api.release
89
+ end
465
90
 
466
- if [ '-f', '--default_host' ].member? k
467
- @hash['default_host'] = shift(k, argv)
468
- next
469
- end
91
+ # sets up, executes, and closes a Studio Scale test for one scenario or each scenario (.msl file) found in the specified directory
92
+ # * argv = command-line arguments
93
+ # * optional -s scenario file
94
+ # * optional -d directory containing one or more scenario files
95
+ # * optional -r argument for recursive directory search (default is a flat directory)
96
+ def cmd_run_files argv
97
+ setup argv
98
+
99
+ msg "Clean up existing stats files: app_id_status.json, app_id_stats.csv", Logger::INFO
100
+ File.delete("app_id_status.json") if File.exists?("app_id_status.json")
101
+ File.delete("app_id_stats.csv") if File.exists?("app_id_stats.csv")
102
+
103
+ @api = Scale.new(@@mu_ip, @@mu_admin_user, @@mu_admin_pass)
104
+ @api.configure("pattern", @cmd_line_pattern)
105
+ @params = {}
106
+ @params["hosts"] = @cmd_line_hosts
107
+ if @hash["dir"].nil?
108
+ files = [@hash["scenario"]]
109
+ else
110
+ recursive = (@hash['recursive'].nil?) ? "": "**"
111
+ files = Dir.glob(File.join(@hash["dir"],recursive,"*.msl"))
112
+ end
113
+ if files.nil? or files.empty?
114
+ msg "no msl files found", Logger::ERROR
115
+ else
116
+ files.each do | scenario |
117
+ run scenario
118
+ if @verify_only
119
+ output_verify_results scenario
120
+ else
121
+ output_csv scenario
122
+ end
123
+ sleep 2
124
+ end
125
+ end
126
+ @api.release
127
+ end
470
128
 
471
- if [ '-i', '--interfaces' ].member? k
472
- @hash['interfaces'] = shift(k, argv)
473
- next
474
- end
129
+ private
130
+
131
+ def setup argv
132
+ parse_cli argv
133
+ @params = {}
134
+ @peak_concurrency = 0
135
+ @peak_throughput = 0.0
136
+
137
+ if @hash['test']
138
+ @verify_only = true
139
+ else
140
+ @verify_only = false
141
+ end
142
+
143
+ if not @hash['testset']
144
+ @testset = ""
145
+ else
146
+ @testset = @hash['testset']
147
+ end
148
+
149
+ if not @hash['pattern']
150
+ @cmd_line_pattern = "{ \"iterations\": 1, \"intervals\": [ {\"iterations\":1, \"end\":100, \"start\":1, \"duration\":20 } ] }"
151
+ else
152
+ @cmd_line_pattern = @hash['pattern']
153
+ end
154
+
155
+ if not @hash['interfaces']
156
+ @cmd_line_hosts = "b1,b2"
157
+ else
158
+ @cmd_line_hosts = @hash['interfaces']
159
+ end
160
+
161
+ if not @hash['delay']
162
+ @delay = 0
163
+ else
164
+ @delay = @hash['delay'].to_i
165
+ end
166
+
167
+ if not @hash['no_verify']
168
+ @no_verify = false
169
+ else
170
+ @no_verify = true
171
+ end
172
+
173
+ if not @hash['vector_address_pairing']
174
+ @vector_address_pairing = false
175
+ @limit_concurrency = false
176
+ else
177
+ @vector_address_pairing = true
178
+ @limit_concurrency = true
179
+ end
475
180
 
476
- if [ '-h', '--help' ].member? k
477
- help
478
- exit
479
- end
480
- =begin
481
- if [ '-hold', '--hold_concurrency' ].member? k
482
- @hash['hold_concurrency'] = true
483
- next
484
- end
485
- =end
486
- if [ '-l', '--delay' ].member? k
487
- @hash['delay'] = shift(k, argv)
488
- next
489
- end
181
+ end
490
182
 
491
- if [ '-m', '--mu_string' ].member? k
492
- mu_string = shift(k, argv)
493
- if mu_string =~ /(.+?):(.+?)@(.*)/
494
- @@mu_admin_user = $1
495
- @@mu_admin_pass = $2
496
- @@mu_ip = $3
497
- end
498
- next
499
- end
183
+ def run(scenario)
184
+ begin
185
+ @api.configure("musl", File.read(scenario))
186
+ rescue
187
+ raise "Failed to read in scenario #{scenario}"
188
+ end
189
+
190
+ unless @testset.empty?
191
+ @api.configure("csv", File.read(@testset))
192
+ end
193
+
194
+ configure_hosts
195
+ @api.configure("delay", @delay)
196
+ @api.configure("vectorAddressPairing", @vector_address_pairing)
197
+ @api.configure("limitConcurrency", @limit_concurrency)
198
+
199
+ if @no_verify == false # don't do verify if no_verify==true
200
+ msg "verifying #{scenario} ...", Logger::INFO
201
+ response = @api.verify
202
+ msg response, Logger::DEBUG
203
+ # sleep 3
204
+ v = parse_verify_response(response)
205
+ msg "#{v}", Logger::DEBUG
206
+ if v.nil? or v.include? "Error"
207
+ msg "error in verify", Logger::ERROR
208
+ return
209
+ end
210
+ if @verify_only
211
+ msg v, Logger::DEBUG
212
+ return v
213
+ end
214
+ end
215
+
216
+ msg "starting #{scenario} ...", Logger::INFO
217
+ @api.start
218
+ start_time = Time.now.to_i
219
+ while true
220
+ sleep 5
221
+ status = @api.status
222
+ if !status.nil?
223
+ if !status["status"].nil?
224
+ if status["status"]["running"] == false
225
+ msg "running = #{status["status"]["running"]}", Logger::DEBUG
226
+ r = parse_status(status)
227
+ dump_status(status, scenario)
228
+ return
229
+ else
230
+ r = parse_status(status)
231
+ end
232
+ else # status['status'].nil? ... no bonafide status was returned
233
+ time_now = Time.now.to_i
234
+ if time_now - start_time > 60
235
+ msg "Error: timing out after 60 seconds. Test had failed to start or verify", Logger::ERROR
236
+ break
237
+ end
238
+ end
239
+ end
240
+ end
241
+ ensure
242
+ msg "stopping #{scenario} ...", Logger::DEBUG
243
+ end
500
244
 
501
- if [ '-n', '--no_verify' ].member? k
502
- @hash['no_verify'] = true
503
- next
504
- end
245
+ def cmd_running?
246
+ if @api.nil?
247
+ msg "false"
248
+ return
249
+ end
250
+
251
+ status = @api.status
252
+ if !status.nil?
253
+ if !status["status"].nil?
254
+ msg status["status"]["running"]
255
+ end
256
+ else
257
+ msg "false"
258
+ end
259
+ end
505
260
 
506
- if [ '-o', '--output' ].member? k
507
- $stdout.reopen(shift(k, argv), "w")
508
- next
509
- end
261
+ def configure_hosts
262
+ @hosts = Array.new
263
+ @addr_indexes = Array.new
264
+ @offset_indexes = Array.new
265
+ hosts = @params["hosts"]
266
+ if !hosts.nil?
267
+ p = hosts.split(",")
268
+ p.each do | h |
269
+ if h.include?("-") # b1-1000,b2-1 to indicate addr_count
270
+ q = h.split("-")
271
+ @hosts << q[0]
272
+ if q[1].include?(":") # -1000:20 to indicate offset within range
273
+ r = q[1].split(":")
274
+ @addr_indexes << r[0]
275
+ @offset_indexes << r[1]
276
+ else
277
+ @addr_indexes << q[1]
278
+ @offset_indexes << 1
279
+ end
280
+ else # default to the 1st addr index
281
+ @hosts << h
282
+ @addr_indexes << 1
283
+ @offset_indexes << 1
284
+ end
285
+ end
286
+ else
287
+ @hosts = ['b1','b2']
288
+ @addr_indexes = [1,1]
289
+ @offset_indexes = [1,1]
290
+ end
291
+
292
+ set_hosts_byname(@hosts, @addr_indexes, @offset_indexes)
293
+ end
510
294
 
511
- if [ '-p', '--pattern' ].member? k
512
- patterns = Array.new
513
- pattern_string = shift(k, argv)
514
- pstrings = pattern_string.split(",")
515
- pstrings.each do | p |
516
- if p =~ /(.+?)-(.+?):(.*)/ # e.g. 1-10000:60
517
- start_vol = $1
518
- end_vol = $2
519
- duration = $3
520
- patterns << "{\"iterations\":1, \"end\":#{end_vol}, \"start\":#{start_vol}, \"duration\":#{duration} }"
521
- end
295
+ def set_hosts_byname(hosts=@hosts, count=[1,1], offset=[1,1], v4=true)
296
+ new_hosts = Array.new
297
+ str = ""
298
+ hosts.each_with_index do |n, i|
299
+ if n.match(/^[ab][1-4]$/) or n.include?(".") # possible vlan
300
+ if count[i] == 1 or count[i].nil?
301
+ str = "#{n}/*"
302
+ else
303
+ str = "#{n}/*,#{count[i]},#{offset[i]}"
304
+ end
305
+ msg "using host #{str}", Logger::DEBUG
306
+ else
307
+ @net = Netconfig.new
308
+ @net.setup(@hosts, @username, @password)
309
+ if v4
310
+ addr = @net.get("hosts/#{n}")['v4_addr']
311
+ else
312
+ addr = @net.get("hosts/#{n}")['v6_addr']
313
+ end
314
+ str = "#{addr}"
315
+ msg "using host #{str}", Logger::DEBUG
316
+ end
317
+ new_hosts << str
318
+ end
319
+ set_hosts(new_hosts)
522
320
  end
523
- ps = "{ \"iterations\": 1, \"intervals\": ["
524
- patterns.each do | p |
525
- ps = ps + p + ","
321
+
322
+ # expects full strings: e.g. b1/12.89.0.1 ...
323
+ def set_hosts(hosts=["b1","b2"])
324
+ host_params = {}
325
+
326
+ # assign hosts to consecutive string keys, host_0, host_1, etc ...
327
+ hosts.each_with_index do | h, i |
328
+ host_params["host_#{i}"] = hosts[i]
329
+ end
330
+
331
+ # convert keys to symbols
332
+ # host_params.each_key { |k| host_params[k.to_sym] = host_params[k] }
333
+ new_host_params = {}
334
+ host_params.each_key { |k| new_host_params[k.to_sym] = host_params[k] }
335
+
336
+ @api.configure("hosts", new_host_params)
337
+
526
338
  end
527
- ps = ps[0..ps.length-2] # remove final comma
528
- ps = ps + "] }"
529
- @hash['pattern'] = ps
530
- next
531
- end
532
339
 
533
- if [ '-r', '--recursive'].member? k
534
- @hash['recursive'] = true
535
- next
536
- end
340
+ def dump_status(status, msl)
341
+ filename = "app_id_status.json"
342
+ msg "Update status to: #{File.absolute_path(filename)}", Logger::INFO
343
+ f = File.open(filename, "a")
344
+ status["filename"] = msl
345
+ str = JSON.pretty_generate(status)
346
+ File.zero?(filename) or f.puts(",")
347
+ f.write(str)
348
+ f.close
349
+ end
537
350
 
351
+ def output_csv(msl_file)
352
+ filename = "app_id_stats.csv"
353
+ msg "Update stats to: #{File.absolute_path(filename)}", Logger::INFO
354
+ unless File.exists?(filename)
355
+ heading = "scenario,executed,errors,timeouts,client_tx_bytes,client_tx_msgs,client_rx_bytes,client_rx_msgs,server_tx_bytes,server_tx_msgs,server_rx_bytes,server_rx_msgs"
356
+ File.open(filename, 'a'){|f| f.puts(heading)}
357
+ end
358
+ @row = "#{msl_file},#{@executed},#{@errors},#{@timeouts},#{@client_tx_bytes},#{@client_tx_msgs},#{@client_rx_bytes},#{@client_rx_msgs},#{@server_tx_bytes},#{@server_tx_msgs},#{@server_rx_bytes},#{@server_rx_msgs}"
359
+ File.open(filename, 'a'){|f| f.puts(@row)}
360
+ end
538
361
 
539
- if [ '-s', '--scenario' ].member? k
540
- @hash['scenario'] = shift(k, argv)
541
- next
542
- end
362
+ def output_verify_results(msl_file)
363
+ filename = "app_id_stats.csv"
364
+ msg "Update verify results to: #{File.absolute_path(filename)}", Logger::DEBUG
365
+ unless File.exists?(filename)
366
+ msg "Writting verify results to: #{File.absolute_path(filename)}", Logger::INFO
367
+ heading = "scenario, status"
368
+ File.open(filename, 'a'){|f| f.puts(heading)}
369
+ end
370
+ File.open(filename, 'a'){|f| f.puts("#{msl_file},#{@verify_response}")}
371
+ end
543
372
 
544
- if [ '-t', '--test' ].member? k
545
- @hash['test'] = true
546
- next
547
- end
548
-
549
- if [ '-v', '--verbose' ].member? k
550
- $log.level = Logger::DEBUG
373
+ def parse_verify_response(response)
374
+ if response.nil? # || response.empty?
375
+ msg "*** error = no response received from /verify ***\n\n", Logger::ERROR
376
+ @verify_response = "Error = No response from verify"
377
+ return @verify_response
378
+ end
379
+ begin
380
+ msg JSON.pretty_generate(response), Logger::DEBUG
381
+ if !response["status"].nil?
382
+ if response["status"]["error"] == true
383
+ @error = response["status"]["error"]
384
+ @reason = response["status"]["reason"]
385
+ msg "*** Error = #{@error}, reason = #{@reason} ***\n\n", Logger::ERROR
386
+ @verify_response = "Error = #{@error}, reason = #{@reason}"
387
+ return @verify_response
388
+ end
389
+ end
390
+ msg "*** verify: okay ***", Logger::INFO
391
+ @verify_response = "okay"
392
+ return @verify_response
393
+ rescue => e
394
+ msg e, Logger::ERROR
395
+ raise
396
+ return nil
397
+ end
398
+ end
399
+
400
+ def parse_status(status)
401
+ return nil if status.nil?
402
+ @reported_volume = 0
403
+ if !status["status"]["error"].nil?
404
+ if status["status"]["error"] == true
405
+ @error = status["status"]["error"]
406
+ @reason = status["status"]["reason"]
407
+ msg "*** Error = #{@error}, reason = #{@reason} ***\n\n"
408
+ return nil
409
+ end
410
+ end
411
+
412
+ @stats_summary = status["status"]["statistics"]["summary"]
413
+ @duration = @stats_summary["duration"]
414
+ @instances = @stats_summary["instances"]
415
+ @total_instances = @instances["total"]
416
+ @executed = @instances["executed"]
417
+ @timeouts = @instances["timeouts"]
418
+ @errors = @instances["errors"]
419
+ @asserts_failed = @stats_summary["asserts"]["failed"]
420
+ @server = @stats_summary["server"]
421
+ @server_tx_bytes = @server["tx"]["bytes"]
422
+ @server_tx_msgs = @server["tx"]["msgs"]
423
+ @server_rx_bytes = @server["rx"]["bytes"]
424
+ @server_rx_msgs = @server["rx"]["msgs"]
425
+ @client = @stats_summary["client"]
426
+ @client_tx_bytes = @client["tx"]["bytes"]
427
+ @client_tx_msgs = @client["tx"]["msgs"]
428
+ @client_rx_bytes = @client["rx"]["bytes"]
429
+ @client_rx_msgs = @client["rx"]["msgs"]
430
+ @scenarios = status["status"]["statistics"]["scenarios"]
431
+ @scenarios.each do | scenario |
432
+ @reported_volume = @reported_volume + scenario["volume"]
433
+ end
434
+
435
+ if @reported_volume.to_i > @peak_concurrency
436
+ @peak_concurrency = @reported_volume.to_i
437
+ end
438
+
439
+ bits1 = (@client_tx_bytes.to_i + @client_rx_bytes.to_i) * 8
440
+ dur1 = @duration.to_f
441
+ thruput = format_float(2, bits1.to_f / dur1)
442
+
443
+ if thruput.to_f > @peak_throughput
444
+ @peak_throughput = thruput.to_f
445
+ end
446
+
447
+ msg ""
448
+ msg "duration: #{format_float(2, @duration)}"
449
+ msg "concurrency: #{@reported_volume}"
450
+ msg "tests/sec: #{format_float(2, @executed.to_f / @duration)}" if @duration.to_i > 0
451
+ msg "bits/sec: #{thruput}" if @duration.to_i > 0
452
+ msg "passed: #{@executed}"
453
+ msg "errors: #{@errors}"
454
+ msg "timeouts: #{@timeouts}"
455
+ msg "client tx bytes/sec #{format_float(2, @client_tx_bytes.to_f / @duration)}" if @duration.to_i > 0
456
+ msg "client tx msgs/sec #{format_float(2, @client_tx_msgs.to_f / @duration)}" if @duration.to_i > 0
457
+ msg "client rx bytes/sec #{format_float(2, @client_rx_bytes.to_f / @duration)}" if @duration.to_i > 0
458
+ msg "client rx msgs/sec #{format_float(2, @client_rx_msgs.to_f / @duration)}" if @duration.to_i > 0
459
+ msg "server tx bytes/sec #{format_float(2, @server_tx_bytes.to_f / @duration)}" if @duration.to_i > 0
460
+ msg "server tx msgs/sec #{format_float(2, @server_tx_msgs.to_f / @duration)}" if @duration.to_i > 0
461
+ msg "server rx bytes/sec #{format_float(2, @server_rx_bytes.to_f / @duration)}" if @duration.to_i > 0
462
+ msg "server rx msgs/sec #{format_float(2, @server_rx_msgs.to_f / @duration)}" if @duration.to_i > 0
463
+ msg ""
464
+ end
465
+
466
+ def parse_cli argv
467
+ @hash = Hash.new
468
+ while not argv.empty?
469
+ break if argv.first[0,1] != '-'
470
+
471
+ k = argv.shift
472
+
473
+ if [ '-a', '--vector_address_pairing' ].member? k
474
+ @hash['vector_address_pairing'] = true
475
+ next
476
+ end
477
+
478
+ if [ '-c', '--csv' ].member? k
479
+ @hash['testset'] = shift(k, argv)
480
+ next
481
+ end
482
+
483
+ if [ '-d', '--dir' ].member? k
484
+ @hash['dir'] = shift(k, argv)
485
+ next
486
+ end
487
+
488
+ if [ '-f', '--default_host' ].member? k
489
+ @hash['default_host'] = shift(k, argv)
490
+ next
491
+ end
492
+
493
+ if [ '-i', '--interfaces' ].member? k
494
+ @hash['interfaces'] = shift(k, argv)
495
+ next
496
+ end
497
+
498
+ if [ '-h', '--help' ].member? k
499
+ help
500
+ exit
501
+ end
502
+ =begin
503
+ if [ '-hold', '--hold_concurrency' ].member? k
504
+ @hash['hold_concurrency'] = true
551
505
  next
552
506
  end
507
+ =end
508
+ if [ '-l', '--delay' ].member? k
509
+ @hash['delay'] = shift(k, argv)
510
+ next
511
+ end
512
+
513
+ if [ '-m', '--mu_string' ].member? k
514
+ mu_string = shift(k, argv)
515
+ if mu_string =~ /(.+?):(.+?)@(.*)/
516
+ @@mu_admin_user = $1
517
+ @@mu_admin_pass = $2
518
+ @@mu_ip = $3
519
+ end
520
+ next
521
+ end
522
+
523
+ if [ '-n', '--no_verify' ].member? k
524
+ @hash['no_verify'] = true
525
+ next
526
+ end
527
+
528
+ if [ '-o', '--output' ].member? k
529
+ $stdout.reopen(shift(k, argv), "w")
530
+ next
531
+ end
532
+
533
+ if [ '-p', '--pattern' ].member? k
534
+ patterns = Array.new
535
+ pattern_string = shift(k, argv)
536
+ pstrings = pattern_string.split(",")
537
+ pstrings.each do | p |
538
+ if p =~ /(.+?)-(.+?):(.*)/ # e.g. 1-10000:60
539
+ start_vol = $1
540
+ end_vol = $2
541
+ duration = $3
542
+ patterns << "{\"iterations\":1, \"end\":#{end_vol}, \"start\":#{start_vol}, \"duration\":#{duration} }"
543
+ end
544
+ end
545
+ ps = "{ \"iterations\": 1, \"intervals\": ["
546
+ patterns.each do | p |
547
+ ps = ps + p + ","
548
+ end
549
+ ps = ps[0..ps.length-2] # remove final comma
550
+ ps = ps + "] }"
551
+ @hash['pattern'] = ps
552
+ next
553
+ end
554
+
555
+ if [ '-r', '--recursive'].member? k
556
+ @hash['recursive'] = true
557
+ next
558
+ end
559
+
560
+
561
+ if [ '-s', '--scenario' ].member? k
562
+ @hash['scenario'] = shift(k, argv)
563
+ next
564
+ end
565
+
566
+ if [ '-t', '--test' ].member? k
567
+ @hash['test'] = true
568
+ next
569
+ end
570
+
571
+ if [ '-v', '--verbose' ].member? k
572
+ $log.level = Logger::DEBUG
573
+ next
574
+ end
575
+
576
+ raise ArgumentError, "Unknown option #{k}"
577
+ end
578
+
579
+ @hash
580
+ end
553
581
 
554
- raise ArgumentError, "Unknown option #{k}"
555
- end
556
-
557
- @hash
558
- end
559
-
560
- def help
561
- helps = [
562
- { :short => '-a', :long => '--vector_address_pairing', :value => '', :help => 'sets vectorAddressPairing to true (limits and maps concurrency to the rows in the testset)' },
563
- { :short => '-c', :long => '--csv', :value => '<string>', :help => 'name of the csv testset to run' },
564
- { :short => '-d', :long => '--dir', :value => '<string>', :help => 'directory containing the scenario file' },
565
- { :short => '-f', :long => '--default_host', :value => '<string>', :help => 'default_host setting' },
566
- { :short => '-h', :long => '--help', :value => '', :help => 'help on command line options' },
567
- # { :short => '-hold', :long => '--hold_concurrency', :value => '', :help => 'sets holdConcurrency to tru' },
568
- { :short => '-i', :long => '--interfaces', :value => '<string>', :help => 'comma-separated list of interfaces, e.g. b1,b2 or b1-1000,b2 for ip range' },
569
- { :short => '-l', :long => '--delay', :value => '<string>', :help => 'intra-scenario delay value' },
570
- { :short => '-m', :long => '--mu_string', :value => '<string>', :help => 'user, password, mu_ip in the form of admin:admin@10.9.8.7' },
571
- { :short => '-n', :long => '--no_verify', :value => '', :help => 'do not do verify before start' },
572
- { :short => '-o', :long => '--output', :value => '<string>', :help => 'output logging to this file' },
573
- { :short => '-p', :long => '--pattern', :value => '<string>', :help => 'pattern in the form of comma-separated concurrency_start-end:duration patterns, e.g. 1-100:60,100-100:60,100-1:60' },
574
- { :short => '-r', :long => '--recursive', :value => '', :help => 'for run_dir, recurse through sub-directories' },
575
- { :short => '-s', :long => '--scenario', :value => '<string>', :help => 'scenario file to run' },
576
- { :short => '-t', :long => '--test', :value => '', :help => 'do verify only' },
577
- { :short => '-v', :long => '--verbose', :value => '', :help => 'set Logger::DEBUG level' }
578
- ]
579
-
580
- cmds = [
581
- "mu cmd_runscale:help",
582
- "mu cmd_runscale:run_file -s <scenario> -i <hosts, e.g. a1,dell-9> -p <pattern, e.g. 1-1000:30>",
583
- "mu cmd_runscale:run_dir -d <scenario_directory>",
584
- "mu cmd_runscale:running?"
585
- ]
586
-
587
- max_long_size = helps.inject(0) { |memo, obj| [ obj[:long].size, memo ].max }
588
- max_value_size = helps.inject(0) { |memo, obj| [ obj[:value].size, memo ].max }
589
- puts
590
- puts "Usage: mu cmd_runscale:<command> <options>"
591
- puts
592
- helps.each do |h|
593
- puts "%-*s %*s %-*s %s" % [max_long_size, h[:long], 2, h[:short], max_value_size, h[:value], h[:help]]
594
- end
595
- puts
596
- puts "Available Commands"
597
- puts
598
- cmds.each do | c |
599
- puts c
600
- end
601
- puts
602
- end
582
+ def help
583
+ helps = [
584
+ { :short => '-a', :long => '--vector_address_pairing', :value => '', :help => 'sets vectorAddressPairing to true (limits and maps concurrency to the rows in the testset)' },
585
+ { :short => '-c', :long => '--csv', :value => '<string>', :help => 'name of the csv testset to run' },
586
+ { :short => '-d', :long => '--dir', :value => '<string>', :help => 'directory containing the scenario file' },
587
+ { :short => '-f', :long => '--default_host', :value => '<string>', :help => 'default_host setting' },
588
+ { :short => '-h', :long => '--help', :value => '', :help => 'help on command line options' },
589
+ # { :short => '-hold', :long => '--hold_concurrency', :value => '', :help => 'sets holdConcurrency to tru' },
590
+ { :short => '-i', :long => '--interfaces', :value => '<string>', :help => 'comma-separated list of interfaces, e.g. b1,b2 or b1-1000,b2 for ip range' },
591
+ { :short => '-l', :long => '--delay', :value => '<string>', :help => 'intra-scenario delay value' },
592
+ { :short => '-m', :long => '--mu_string', :value => '<string>', :help => 'user, password, mu_ip in the form of admin:admin@10.9.8.7' },
593
+ { :short => '-n', :long => '--no_verify', :value => '', :help => 'do not do verify before start' },
594
+ { :short => '-o', :long => '--output', :value => '<string>', :help => 'output logging to this file' },
595
+ { :short => '-p', :long => '--pattern', :value => '<string>', :help => 'pattern in the form of comma-separated concurrency_start-end:duration patterns, e.g. 1-100:60,100-100:60,100-1:60' },
596
+ { :short => '-r', :long => '--recursive', :value => '', :help => 'for run_dir, recurse through sub-directories' },
597
+ { :short => '-s', :long => '--scenario', :value => '<string>', :help => 'scenario file to run' },
598
+ { :short => '-t', :long => '--test', :value => '', :help => 'do verify only' },
599
+ { :short => '-v', :long => '--verbose', :value => '', :help => 'set Logger::DEBUG level' }
600
+ ]
601
+
602
+ cmds = [
603
+ "mu cmd_runscale:help",
604
+ "mu cmd_runscale:run_files -s <scenario>|-d <scenario_directory> --recursive -i <hosts, e.g. a1,dell-9> -p <pattern, e.g. 1-1000:30>",
605
+ "mu cmd_runscale:run_file -s <scenario> -i <hosts, e.g. a1,dell-9> -p <pattern, e.g. 1-1000:30>",
606
+ "mu cmd_runscale:run_dir -d <scenario_directory>",
607
+ "mu cmd_runscale:running?"
608
+ ]
609
+
610
+ max_long_size = helps.inject(0) { |memo, obj| [ obj[:long].size, memo ].max }
611
+ max_value_size = helps.inject(0) { |memo, obj| [ obj[:value].size, memo ].max }
612
+ puts
613
+ puts "Usage: mu cmd_runscale:<command> <options>"
614
+ puts "\n***WARNING***\nThe following commands will be deprecated in a future release:"
615
+ puts "cmd_runscale:run_file, cmd_runscale:run_dir, cmd_appid\n"
616
+ puts "Please use 'cmd_runscale:run_files'"
617
+ puts
618
+ helps.each do |h|
619
+ puts "%-*s %*s %-*s %s" % [max_long_size, h[:long], 2, h[:short], max_value_size, h[:value], h[:help]]
620
+ end
621
+ puts
622
+ puts "Available Commands"
623
+ puts
624
+ cmds.each do | c |
625
+ puts c
626
+ end
627
+ puts
628
+ end
603
629
 
604
- end
605
- end # Command
630
+ end
631
+ end # Command
606
632
  end # Mu
607
633