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.
@@ -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