Merge "Tools: Improve Stability."
[vswitchperf.git] / tools / pkt_gen / testcenter / testcenter-rfc2544-rest.py
1 # Copyright 2016-2017 Spirent Communications.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #   http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 #
15 # Invalid name of file, must be used '_' instead '-'
16 # pylint: disable=invalid-name
17 '''
18 @author Spirent Communications
19
20 This test automates the RFC2544 tests using the Spirent
21 TestCenter REST APIs. This test supports Python 3.4
22
23 '''
24 import argparse
25 import collections
26 import logging
27 import os
28 import sqlite3
29 import time
30
31 _LOGGER = logging.getLogger(__name__)
32
33 GENOME_PKTSIZE_ENCODING = {"a": 64, "b": 128, "c": 256, "d": 512,
34                            "e": 1024, "f": 1280, "g": 1518, "h": 2112}
35
36
37 def genome2weights(sequence):
38     """ Convert genome sequence to packetsize weights"""
39     weights = collections.defaultdict(int)
40     for char in GENOME_PKTSIZE_ENCODING:
41         charcount = sequence.count(char)
42         if charcount:
43             weights[GENOME_PKTSIZE_ENCODING[char]] = charcount
44     return weights
45
46
47 def create_dir(path):
48     """Create the directory as specified in path """
49     if not os.path.exists(path):
50         try:
51             os.makedirs(path)
52         except OSError as ex:
53             _LOGGER.error("Failed to create directory %s: %s", path, str(ex))
54             raise
55
56
57 def write_histogram_to_csv(results_path, csv_results_file_prefix,
58                            counts, ranges):
59     """ Write the results of the query to the CSV """
60     filec = os.path.join(results_path, csv_results_file_prefix + ".csv")
61     with open(filec, "wb") as result_file:
62         for key in counts:
63             result_file.write(str(key) + "\n")
64             result_file.write(str(ranges) + "\n")
65             result_file.write(str(counts[key]) + "\n")
66
67
68 def write_query_results_to_csv(results_path, csv_results_file_prefix,
69                                query_results):
70     """ Write the results of the query to the CSV """
71     create_dir(results_path)
72     filec = os.path.join(results_path, csv_results_file_prefix + ".csv")
73     with open(filec, "wb") as result_file:
74         result_file.write(query_results["Columns"].replace(" ", ",") + "\n")
75         for row in (query_results["Output"].replace("} {", ",").
76                     replace("{", "").replace("}", "").split(",")):
77             result_file.write(row.replace(" ", ",") + "\n")
78
79
80 def write_headers(results_path, file_name, rx_tx):
81     """ Write headers for the live-results files """
82     filec = os.path.join(results_path, file_name + rx_tx)
83     with open(filec, "a") as result_file:
84         if 'rx' in rx_tx:
85             result_file.write('Time,RxPrt,DrpFrCnt,SeqRnLen,AvgLat,' +
86                               'DrpFrRate,FrCnt,FrRate,MaxLat,MinLat,' +
87                               'OctCnt,OctRate\n')
88         else:
89             result_file.write('Time,StrId,BlkId,FrCnt,FrRate,ERxFrCnt,' +
90                               'OctCnt,OctRate,bitCnt,bitRate\n')
91
92
93 def write_rx_live_results_to_file(results_path, file_name, results):
94     """ Write live results from the rx-ports"""
95     filec = os.path.join(results_path, file_name + ".rx")
96     with open(filec, "a") as result_file:
97         result_file.write('{0},{3},{1},{2},{4},{5},{6},{7},{8},{9},{10},{11}\n'
98                           .format(time.time(), results['DroppedFrameCount'],
99                                   results['SeqRunLength'], results['RxPort'],
100                                   results['AvgLatency'],
101                                   results['DroppedFrameRate'],
102                                   results['FrameCount'], results['FrameRate'],
103                                   results['MaxLatency'], results['MinLatency'],
104                                   results['OctetCount'], results['OctetRate']))
105
106
107 def write_tx_live_results_to_file(results_path, file_name, results):
108     """ Write live results from the tx-ports"""
109     filec = os.path.join(results_path, file_name + ".tx")
110     with open(filec, "a") as result_file:
111         result_file.write('{0},{1},{9},{2},{3},{4},{5},{6},{7},{8}\n'
112                           .format(time.time(), results['StreamId'],
113                                   results['FrameCount'], results['FrameRate'],
114                                   results['ExpectedRxFrameCount'],
115                                   results['OctetCount'], results['OctetRate'],
116                                   results['BitCount'], results['BitRate'],
117                                   results['BlockId']))
118
119
120 def positive_int(value):
121     """ Positive Integer type for Arguments """
122     ivalue = int(value)
123     if ivalue <= 0:
124         raise argparse.ArgumentTypeError(
125             "%s is an invalid positive int value" % value)
126     return ivalue
127
128
129 def percent_float(value):
130     """ Floating type for Arguments """
131     pvalue = float(value)
132     if pvalue < 0.0 or pvalue > 100.0:
133         raise argparse.ArgumentTypeError(
134             "%s not in range [0.0, 100.0]" % pvalue)
135     return pvalue
136
137
138 # pylint: disable=too-many-branches, too-many-statements, too-many-locals
139 def main():
140     """ Read the arguments, Invoke Test and Return the results"""
141     parser = argparse.ArgumentParser()
142     # Required parameters
143     required_named = parser.add_argument_group("required named arguments")
144     required_named.add_argument("--lab_server_addr",
145                                 required=True,
146                                 help=("The IP address of the"
147                                       "Spirent Lab Server"),
148                                 dest="lab_server_addr")
149     required_named.add_argument("--license_server_addr",
150                                 required=True,
151                                 help=("The IP address of the Spirent"
152                                       "License Server"),
153                                 dest="license_server_addr")
154     required_named.add_argument("--east_chassis_addr",
155                                 required=True,
156                                 help=("The TestCenter chassis IP address to"
157                                       "use for the east test port"),
158                                 dest="east_chassis_addr")
159     required_named.add_argument("--east_slot_num",
160                                 type=positive_int,
161                                 required=True,
162                                 help=("The TestCenter slot number to"
163                                       "use for the east test port"),
164                                 dest="east_slot_num")
165     required_named.add_argument("--east_port_num",
166                                 type=positive_int,
167                                 required=True,
168                                 help=("The TestCenter port number to use"
169                                       "for the east test port"),
170                                 dest="east_port_num")
171     required_named.add_argument("--west_chassis_addr",
172                                 required=True,
173                                 help=("The TestCenter chassis IP address"
174                                       "to use for the west test port"),
175                                 dest="west_chassis_addr")
176     required_named.add_argument("--west_slot_num",
177                                 type=positive_int,
178                                 required=True,
179                                 help=("The TestCenter slot number to use"
180                                       "for the west test port"),
181                                 dest="west_slot_num")
182     required_named.add_argument("--west_port_num",
183                                 type=positive_int,
184                                 required=True,
185                                 help=("The TestCenter port number to"
186                                       "use for the west test port"),
187                                 dest="west_port_num")
188     # Optional parameters
189     optional_named = parser.add_argument_group("optional named arguments")
190     optional_named.add_argument("--metric",
191                                 required=False,
192                                 help=("One among - throughput, latency,\
193                                       backtoback and frameloss"),
194                                 choices=["throughput", "latency",
195                                          "backtoback", "frameloss"],
196                                 default="throughput",
197                                 dest="metric")
198     optional_named.add_argument("--test_session_name",
199                                 required=False,
200                                 default="RFC2544 East-West Throughput",
201                                 help=("The friendly name to identify"
202                                       "the Spirent Lab Server test session"),
203                                 dest="test_session_name")
204
205     optional_named.add_argument("--test_user_name",
206                                 required=False,
207                                 default="RFC2544 East-West User",
208                                 help=("The friendly name to identify the"
209                                       "Spirent Lab Server test user"),
210                                 dest="test_user_name")
211     optional_named.add_argument("--results_dir",
212                                 required=False,
213                                 default="./Results",
214                                 help="The directory to copy results to",
215                                 dest="results_dir")
216     optional_named.add_argument("--vsperf_results_dir",
217                                 required=False,
218                                 default="./Results",
219                                 help="The directory to copy results to",
220                                 dest="vsperf_results_dir")
221     optional_named.add_argument("--csv_results_file_prefix",
222                                 required=False,
223                                 default="Rfc2544Tput",
224                                 help="The prefix for the CSV results files",
225                                 dest="csv_results_file_prefix")
226     optional_named.add_argument("--num_trials",
227                                 type=positive_int,
228                                 required=False,
229                                 default=1,
230                                 help=("The number of trials to execute during"
231                                       "the test"),
232                                 dest="num_trials")
233     optional_named.add_argument("--trial_duration_sec",
234                                 type=positive_int,
235                                 required=False,
236                                 default=60,
237                                 help=("The duration of each trial executed"
238                                       "during the test"),
239                                 dest="trial_duration_sec")
240     optional_named.add_argument("--traffic_pattern",
241                                 required=False,
242                                 choices=["BACKBONE", "MESH", "PAIR"],
243                                 default="PAIR",
244                                 help="The traffic pattern between endpoints",
245                                 dest="traffic_pattern")
246     optional_named.add_argument("--traffic_custom",
247                                 required=False,
248                                 default=None,
249                                 help="The traffic pattern between endpoints",
250                                 dest="traffic_custom")
251     optional_named.add_argument("--search_mode",
252                                 required=False,
253                                 choices=["COMBO", "STEP", "BINARY"],
254                                 default="BINARY",
255                                 help=("The search mode used to find the"
256                                       "throughput rate"),
257                                 dest="search_mode")
258     optional_named.add_argument("--learning_mode",
259                                 required=False,
260                                 choices=["AUTO", "L2_LEARNING",
261                                          "L3_LEARNING", "NONE"],
262                                 default="AUTO",
263                                 help=("The learning mode used during the test,"
264                                       "default is 'NONE'"),
265                                 dest="learning_mode")
266     optional_named.add_argument("--rate_lower_limit_pct",
267                                 type=percent_float,
268                                 required=False,
269                                 default=1.0,
270                                 help=("The minimum percent line rate that"
271                                       "will be used during the test"),
272                                 dest="rate_lower_limit_pct")
273     optional_named.add_argument("--rate_upper_limit_pct",
274                                 type=percent_float,
275                                 required=False,
276                                 default=99.0,
277                                 help=("The maximum percent line rate that"
278                                       "will be used during the test"),
279                                 dest="rate_upper_limit_pct")
280     optional_named.add_argument("--rate_initial_pct",
281                                 type=percent_float,
282                                 required=False,
283                                 default=99.0,
284                                 help=("If Search Mode is BINARY, the percent"
285                                       "line rate that will be used at the"
286                                       "start of the test"),
287                                 dest="rate_initial_pct")
288     optional_named.add_argument("--rate_step_pct",
289                                 type=percent_float,
290                                 required=False,
291                                 default=10.0,
292                                 help=("If SearchMode is STEP, the percent"
293                                       "load increase per step"),
294                                 dest="rate_step_pct")
295     optional_named.add_argument("--resolution_pct",
296                                 type=percent_float,
297                                 required=False,
298                                 default=1.0,
299                                 help=("The minimum percentage of load"
300                                       "adjustment between iterations"),
301                                 dest="resolution_pct")
302     optional_named.add_argument("--frame_size_list",
303                                 type=lambda s: [int(item)
304                                                 for item in s.split(',')],
305                                 required=False,
306                                 default=[256],
307                                 help="A comma-delimited list of frame sizes",
308                                 dest="frame_size_list")
309     optional_named.add_argument("--acceptable_frame_loss_pct",
310                                 type=percent_float,
311                                 required=False,
312                                 default=0.0,
313                                 help=("The maximum acceptable frame loss"
314                                       "percent in any iteration"),
315                                 dest="acceptable_frame_loss_pct")
316     optional_named.add_argument("--east_intf_addr",
317                                 required=False,
318                                 default="192.85.1.3",
319                                 help=("The address to assign to the first"
320                                       "emulated device interface on the first"
321                                       "east port"),
322                                 dest="east_intf_addr")
323     optional_named.add_argument("--east_intf_gateway_addr",
324                                 required=False,
325                                 default="192.85.1.53",
326                                 help=("The gateway address to assign to the"
327                                       "first emulated device interface on the"
328                                       "first east port"),
329                                 dest="east_intf_gateway_addr")
330     optional_named.add_argument("--west_intf_addr",
331                                 required=False,
332                                 default="192.85.1.53",
333                                 help=("The address to assign to the first"
334                                       "emulated device interface on the"
335                                       "first west port"),
336                                 dest="west_intf_addr")
337     optional_named.add_argument("--west_intf_gateway_addr",
338                                 required=False,
339                                 default="192.85.1.53",
340                                 help=("The gateway address to assign to"
341                                       "the first emulated device interface"
342                                       "on the first west port"),
343                                 dest="west_intf_gateway_addr")
344     optional_named.add_argument("--latency_histogram",
345                                 required=False,
346                                 action="store_true",
347                                 help="latency histogram is required in output?",
348                                 dest="latency_histogram")
349     optional_named.add_argument("--imix",
350                                 required=False,
351                                 default="",
352                                 help=("IMIX specification as genome"
353                                       "Encoding - RFC 6985"),
354                                 dest="imix")
355     optional_named.add_argument("--live_results",
356                                 required=False,
357                                 action="store_true",
358                                 help="Live Results required?",
359                                 dest="live_results")
360     optional_named.add_argument("--logfile",
361                                 required=False,
362                                 default="./traffic_gen.log",
363                                 help="Log file to log live results",
364                                 dest="logfile")
365     parser.add_argument("-v",
366                         "--verbose",
367                         required=False,
368                         default=True,
369                         help="More output during operation when present",
370                         action="store_true",
371                         dest="verbose")
372     args = parser.parse_args()
373
374     if args.verbose:
375         _LOGGER.debug("Creating results directory")
376     create_dir(args.results_dir)
377
378     session_name = args.test_session_name
379     user_name = args.test_user_name
380     # pylint: disable=import-error
381     try:
382         # Load Spirent REST Library
383         from stcrestclient import stchttp
384
385         stc = stchttp.StcHttp(args.lab_server_addr)
386         session_id = stc.new_session(user_name, session_name)
387         stc.join_session(session_id)
388     except RuntimeError as err:
389         _LOGGER.error(err)
390         raise
391
392     # Get STC system info.
393     tx_port_loc = "//%s/%s/%s" % (args.east_chassis_addr,
394                                   args.east_slot_num,
395                                   args.east_port_num)
396     rx_port_loc = "//%s/%s/%s" % (args.west_chassis_addr,
397                                   args.west_slot_num,
398                                   args.west_port_num)
399
400     # Retrieve and display the server information
401     if args.verbose:
402         _LOGGER.debug("SpirentTestCenter system version: %s",
403                       stc.get("system1", "version"))
404
405     # pylint: disable=too-many-nested-blocks
406     try:
407         device_list = []
408         port_list = []
409         if args.verbose:
410             _LOGGER.debug("Bring up license server")
411         license_mgr = stc.get("system1", "children-licenseservermanager")
412         if args.verbose:
413             _LOGGER.debug("license_mgr = %s", license_mgr)
414         stc.create("LicenseServer", under=license_mgr, attributes={
415             "server": args.license_server_addr})
416
417         # Create the root project object
418         if args.verbose:
419             _LOGGER.debug("Creating project ...")
420         project = stc.get("System1", "children-Project")
421
422         # Configure the Result view
423         resultopts = stc.get('project1', 'children-resultoptions')
424         stc.config(resultopts, {'ResultViewMode': 'BASIC'})
425
426         # Configure any custom traffic parameters
427         if args.traffic_custom == "cont":
428             if args.verbose:
429                 _LOGGER.debug("Configure Continuous Traffic")
430             stc.create("ContinuousTestConfig", under=project)
431
432         # Create ports
433         if args.verbose:
434             _LOGGER.debug("Creating ports ...")
435         east_chassis_port = stc.create('port', project)
436         if args.verbose:
437             _LOGGER.debug("Configuring TX port ...")
438         stc.config(east_chassis_port, {'location': tx_port_loc})
439         port_list.append(east_chassis_port)
440
441         west_chassis_port = stc.create('port', project)
442         if args.verbose:
443             _LOGGER.debug("Configuring RX port ...")
444         stc.config(west_chassis_port, {'location': rx_port_loc})
445         port_list.append(west_chassis_port)
446
447         # Create emulated genparam for east port
448         east_device_gen_params = stc.create("EmulatedDeviceGenParams",
449                                             under=project,
450                                             attributes={"Port":
451                                                         east_chassis_port})
452         # Create the DeviceGenEthIIIfParams object
453         stc.create("DeviceGenEthIIIfParams",
454                    under=east_device_gen_params,
455                    attributes={'UseDefaultPhyMac': True})
456
457         # Configuring Ipv4 interfaces
458         stc.create("DeviceGenIpv4IfParams",
459                    under=east_device_gen_params,
460                    attributes={"Addr": args.east_intf_addr,
461                                "Gateway": args.east_intf_gateway_addr})
462         # Create Devices using the Device Wizard
463         device_gen_config = stc.perform("DeviceGenConfigExpand",
464                                         params={"DeleteExisting": "No",
465                                                 "GenParams":
466                                                 east_device_gen_params})
467         # Append to the device list
468         device_list.append(device_gen_config['ReturnList'])
469
470         # Create emulated genparam for west port
471         west_device_gen_params = stc.create("EmulatedDeviceGenParams",
472                                             under=project,
473                                             attributes={"Port":
474                                                         west_chassis_port})
475         # Create the DeviceGenEthIIIfParams object
476         stc.create("DeviceGenEthIIIfParams",
477                    under=west_device_gen_params,
478                    attributes={'UseDefaultPhyMac': True})
479
480         # Configuring Ipv4 interfaces
481         stc.create("DeviceGenIpv4IfParams",
482                    under=west_device_gen_params,
483                    attributes={"Addr": args.west_intf_addr,
484                                "Gateway": args.west_intf_gateway_addr})
485         # Create Devices using the Device Wizard
486         device_gen_config = stc.perform("DeviceGenConfigExpand",
487                                         params={"DeleteExisting": "No",
488                                                 "GenParams":
489                                                 west_device_gen_params})
490         # Append to the device list
491         device_list.append(device_gen_config['ReturnList'])
492         if args.verbose:
493             _LOGGER.debug(device_list)
494
495         # Configure Histogram
496         if args.latency_histogram:
497             # Generic Configuration
498             histResOptions = stc.get("project1", 'children-ResultOptions')
499             stc.config(histResOptions, {'ResultViewMode': 'HISTOGRAM'})
500             # East Port Configuration
501             histAnaEast = stc.get(east_chassis_port, 'children-Analyzer')
502             histAnaEastConfig = stc.get(histAnaEast, 'children-AnalyzerConfig')
503             stc.config(histAnaEastConfig, {'HistogramMode': 'LATENCY'})
504             eLatHist = stc.get(histAnaEastConfig, 'children-LatencyHistogram')
505             stc.config(eLatHist, {'ConfigMode': 'CONFIG_LIMIT_MODE',
506                                   'BucketSizeUnit': 'ten_nanoseconds',
507                                   'Active': 'TRUE',
508                                   'DistributionMode': 'CENTERED_MODE'})
509             # West Port Configuration
510             histAnaWest = stc.get(west_chassis_port, 'children-Analyzer')
511             histAnaWestConfig = stc.get(histAnaWest, 'children-AnalyzerConfig')
512             stc.config(histAnaWestConfig, {'HistogramMode': 'LATENCY'})
513             wLatHist = stc.get(histAnaWestConfig, 'children-LatencyHistogram')
514             stc.config(wLatHist, {'ConfigMode': 'CONFIG_LIMIT_MODE',
515                                   'BucketSizeUnit': 'ten_nanoseconds',
516                                   'Active': 'TRUE',
517                                   'DistributionMode': 'CENTERED_MODE'})
518             gBucketSizeList = stc.get(wLatHist, 'BucketSizeList')
519             # gLimitSizeList  = stc.get(wLatHist, 'LimitList')
520
521         # IMIX configuration
522         fld = None
523         if args.imix:
524             args.frame_size_list = []
525             weights = genome2weights(args.imix)
526             fld = stc.create('FrameLengthDistribution', under=project)
527             def_slots = stc.get(fld, "children-framelengthdistributionslot")
528             stc.perform("Delete", params={"ConfigList": def_slots})
529             for fsize in weights:
530                 stc.create('framelengthdistributionslot', under=fld,
531                            attributes={'FixedFrameLength': fsize,
532                                        'Weight': weights[fsize]})
533
534         # Create the RFC 2544 'metric test
535         if args.metric == "throughput":
536             if args.verbose:
537                 _LOGGER.debug("Set up the RFC2544 throughput test...")
538             stc.perform("Rfc2544SetupThroughputTestCommand",
539                         params={"AcceptableFrameLoss":
540                                 args.acceptable_frame_loss_pct,
541                                 "Duration": args.trial_duration_sec,
542                                 "FrameSizeList": args.frame_size_list,
543                                 "LearningMode": args.learning_mode,
544                                 "NumOfTrials": args.num_trials,
545                                 "RateInitial": args.rate_initial_pct,
546                                 "RateLowerLimit": args.rate_lower_limit_pct,
547                                 "RateStep": args.rate_step_pct,
548                                 "RateUpperLimit": args.rate_upper_limit_pct,
549                                 "Resolution": args.resolution_pct,
550                                 "SearchMode": args.search_mode,
551                                 "TrafficPattern": args.traffic_pattern,
552                                 "FrameSizeDistributionList": fld})
553         elif args.metric == "backtoback":
554             stc.perform("Rfc2544SetupBackToBackTestCommand",
555                         params={"AcceptableFrameLoss":
556                                 args.acceptable_frame_loss_pct,
557                                 "Duration": args.trial_duration_sec,
558                                 "FrameSizeList": args.frame_size_list,
559                                 "LearningMode": args.learning_mode,
560                                 "LatencyType": args.latency_type,
561                                 "NumOfTrials": args.num_trials,
562                                 "RateInitial": args.rate_initial_pct,
563                                 "RateLowerLimit": args.rate_lower_limit_pct,
564                                 "RateStep": args.rate_step_pct,
565                                 "RateUpperLimit": args.rate_upper_limit_pct,
566                                 "Resolution": args.resolution_pct,
567                                 "SearchMode": args.search_mode,
568                                 "TrafficPattern": args.traffic_pattern})
569         elif args.metric == "frameloss":
570             stc.perform("Rfc2544SetupFrameLossTestCommand",
571                         params={"AcceptableFrameLoss":
572                                 args.acceptable_frame_loss_pct,
573                                 "Duration": args.trial_duration_sec,
574                                 "FrameSizeList": args.frame_size_list,
575                                 "LearningMode": args.learning_mode,
576                                 "LatencyType": args.latency_type,
577                                 "NumOfTrials": args.num_trials,
578                                 "RateInitial": args.rate_initial_pct,
579                                 "RateLowerLimit": args.rate_lower_limit_pct,
580                                 "RateStep": args.rate_step_pct,
581                                 "RateUpperLimit": args.rate_upper_limit_pct,
582                                 "Resolution": args.resolution_pct,
583                                 "SearchMode": args.search_mode,
584                                 "TrafficPattern": args.traffic_pattern})
585         elif args.metric == "latency":
586             stc.perform("Rfc2544SetupLatencyTestCommand",
587                         params={"AcceptableFrameLoss":
588                                 args.acceptable_frame_loss_pct,
589                                 "Duration": args.trial_duration_sec,
590                                 "FrameSizeList": args.frame_size_list,
591                                 "LearningMode": args.learning_mode,
592                                 "LatencyType": args.latency_type,
593                                 "NumOfTrials": args.num_trials,
594                                 "RateInitial": args.rate_initial_pct,
595                                 "RateLowerLimit": args.rate_lower_limit_pct,
596                                 "RateStep": args.rate_step_pct,
597                                 "RateUpperLimit": args.rate_upper_limit_pct,
598                                 "Resolution": args.resolution_pct,
599                                 "SearchMode": args.search_mode,
600                                 "TrafficPattern": args.traffic_pattern})
601
602         # Save the configuration
603         stc.perform("SaveToTcc", params={"Filename": "2544.tcc"})
604         # Connect to the hardware...
605         stc.perform("AttachPorts", params={"portList": stc.get(
606             "system1.project", "children-port"), "autoConnect": "TRUE"})
607         # Apply configuration.
608         if args.verbose:
609             _LOGGER.debug("Apply configuration...")
610         stc.apply()
611
612         # Register for the results
613         hResDataRx = stc.create('ResultDataSet', under='project1')
614         strmBlockList = stc.get('project1', 'children-streamblock')
615         stc.create('ResultQuery', under=hResDataRx, attributes={
616             'ResultRootList': strmBlockList,
617             'ConfigClassId': 'StreamBlock',
618             'ResultClassId': 'RxStreamSummaryResults',
619             'PropertyIdArray': "RxStreamSummaryResults.RxPort \
620                                 RxStreamSummaryResults.AvgLatency \
621                                 RxStreamSummaryResults.BitCount \
622                                 RxStreamSummaryResults.BitRate \
623                                 RxStreamSummaryResults.DroppedFrameCount\
624                                 RxStreamSummaryResults.DroppedFrameRate \
625                                 RxStreamSummaryResults.FrameCount \
626                                 RxStreamSummaryResults.FrameRate \
627                                 RxStreamSummaryResults.MaxLatency \
628                                 RxStreamSummaryResults.MinLatency \
629                                 RxStreamSummaryResults.OctetCount \
630                                 RxStreamSummaryResults.OctetRate \
631                                 RxStreamSummaryResults.SeqRunLength"})
632         hResDataTx = stc.create('ResultDataSet', under='project1')
633         strmBlockList = stc.get('project1', 'children-streamblock')
634         stc.create('ResultQuery', under=hResDataTx, attributes={
635             'ResultRootList': strmBlockList,
636             'ConfigClassId': 'StreamBlock',
637             'ResultClassId': 'TxStreamResults',
638             'PropertyIdArray': "TxStreamResults.BlockId \
639                                 TxStreamResults.BitCount \
640                                 TxStreamResults.BitRate \
641                                 TxStreamResults.FrameCount \
642                                 TxStreamResults.FrameRate \
643                                 TxStreamResults.OctetCount \
644                                 TxStreamResults.OctetRate"})
645         stc.perform('ResultDataSetSubscribe', params={'ResultDataSet': hResDataRx})
646         stc.perform('ResultDataSetSubscribe', params={'ResultDataSet': hResDataTx})
647         time.sleep(3)
648         stc.perform('RefreshResultView', params={'ResultDataSet': hResDataTx})
649         hndListRx = stc.get(hResDataRx, 'ResultHandleList')
650         hndListTx = stc.get(hResDataTx, 'ResultHandleList')
651
652         if args.verbose:
653             _LOGGER.debug("Starting the sequencer...")
654         stc.perform("SequencerStart")
655
656         sequencer = stc.get("system1", "children-sequencer")
657         state = stc.get(sequencer, 'State')
658
659         # If Live-results are required, we don't wait for the test to complete
660         if args.live_results:
661             write_headers(args.vsperf_results_dir, args.logfile, '.rx')
662             write_headers(args.vsperf_results_dir, args.logfile, '.tx')
663             while state != 'IDLE':
664                 state = stc.get(sequencer, 'State')
665                 hndListTx = stc.get(hResDataTx, 'ResultHandleList')
666                 if hndListTx:
667                     handles = hndListTx.split(' ')
668                     for handle in handles:
669                         tx_values = stc.get(handle)
670                         write_tx_live_results_to_file(args.vsperf_results_dir,
671                                                       args.logfile,
672                                                       tx_values)
673                 if hndListRx:
674                     handles = hndListRx.split(' ')
675                     for handle in handles:
676                         rx_values = stc.get(handle)
677                         write_rx_live_results_to_file(args.vsperf_results_dir,
678                                                       args.logfile,
679                                                       rx_values)
680                 time.sleep(1)
681         # Live results not needed, so just wait!
682         else:
683             # Wait for sequencer to finish
684             _LOGGER.info(
685                 "Starting test... Please wait for the test to complete...")
686             stc.wait_until_complete()
687
688         _LOGGER.info("The test has completed... Saving results...")
689
690         # Determine what the results database filename is...
691         lab_server_resultsdb = stc.get(
692             "system1.project.TestResultSetting", "CurrentResultFileName")
693
694         if not lab_server_resultsdb or 'Results' not in lab_server_resultsdb:
695             _LOGGER.info("Failed to find results.")
696             stc.end_session()
697             return
698
699         if args.verbose:
700             _LOGGER.debug("The lab server results database is %s",
701                           lab_server_resultsdb)
702
703         # Create Latency Histogram CSV file()
704         if args.latency_histogram:
705             hist_dict_counts = {}
706             for file_url in stc.files():
707                 if '-FrameSize-' in file_url:
708                     stc.download(file_url)
709                     filename = file_url.split('/')[-1]
710                     if os.path.exists(os.getcwd() + '/' + filename):
711                         conn = sqlite3.connect(os.getcwd() + '/' + filename)
712                         # cursor = conn.execute(
713                         #    'select * from RxEotStreamResults')
714                         # names = [desc[0] for desc in cursor.description]
715                         counts = conn.execute("SELECT \
716                                               HistBin1Count, HistBin2Count,\
717                                               HistBin3Count, HistBin4Count,\
718                                               HistBin5Count, HistBin6Count,\
719                                               HistBin7Count, HistBin8Count,\
720                                               HistBin9Count, HistBin10Count,\
721                                               HistBin11Count, HistBin12Count,\
722                                               HistBin13Count, HistBin14Count, \
723                                               HistBin15Count, HistBin16Count \
724                                               from RxEotStreamResults")
725                         strs = filename.split('-')
726                         key = strs[strs.index('FrameSize')+1]
727                         if key in hist_dict_counts:
728                             hist_dict_counts[key] = [a+b for a, b in
729                                                      zip(counts.fetchone(),
730                                                          hist_dict_counts[key])]
731                         else:
732                             hist_dict_counts[key] = counts.fetchone()
733                         conn.close()
734
735             write_histogram_to_csv(args.vsperf_results_dir, 'Histogram',
736                                    hist_dict_counts,
737                                    gBucketSizeList)
738
739         stc.perform("CSSynchronizeFiles",
740                     params={"DefaultDownloadDir": args.results_dir})
741
742         resultsdb = args.results_dir + \
743             lab_server_resultsdb.split("/Results")[1]
744
745         if not os.path.exists(resultsdb):
746             resultsdb = lab_server_resultsdb
747             _LOGGER.info("Failed to create the local summary DB File, using"
748                          " the remote DB file instead.")
749         else:
750             _LOGGER.info(
751                 "The local summary DB file has been saved to %s", resultsdb)
752
753         # The returns the "RFC2544ThroughputTestResultDetailedSummaryView"
754         # table view from the results database.
755         # There are other views available.
756
757         if args.metric == "throughput":
758             resultsdict = (
759                 stc.perform("QueryResult",
760                             params={
761                                 "DatabaseConnectionString":
762                                 resultsdb,
763                                 "ResultPath":
764                                 ("RFC2544ThroughputTestResultDetailed"
765                                  "SummaryView")}))
766
767         # The returns the "RFC2544BacktoBackTestResultDetailedSummaryView"
768         # table view from the results database.
769         # There are other views available.
770         elif args.metric == "backtoback":
771             resultsdict = (
772                 stc.perform("QueryResult",
773                             params={
774                                 "DatabaseConnectionString":
775                                 resultsdb,
776                                 "ResultPath":
777                                 ("RFC2544Back2BackTestResultDetailed"
778                                  "SummaryView")}))
779
780         # The returns the "RFC2544LatencyTestResultDetailedSummaryView"
781         # table view from the results database.
782         # There are other views available.
783         elif args.metric == "latency":
784             resultsdict = (
785                 stc.perform("QueryResult",
786                             params={
787                                 "DatabaseConnectionString":
788                                 resultsdb,
789                                 "ResultPath":
790                                 ("RFC2544LatencyTestResultDetailed"
791                                  "SummaryView")}))
792
793         # The returns the "RFC2544FrameLossTestResultDetailedSummaryView"
794         # table view from the results database.
795         # There are other views available.
796         elif args.metric == "frameloss":
797             resultsdict = (
798                 stc.perform("QueryResult",
799                             params={
800                                 "DatabaseConnectionString":
801                                 resultsdb,
802                                 "ResultPath":
803                                 ("RFC2544FrameLossTestResultDetailed"
804                                  "SummaryView")}))
805         if args.verbose:
806             _LOGGER.debug("resultsdict[\"Columns\"]: %s",
807                           resultsdict["Columns"])
808             _LOGGER.debug("resultsdict[\"Output\"]: %s", resultsdict["Output"])
809             _LOGGER.debug("Result paths: %s",
810                           stc.perform("GetTestResultSettingPaths"))
811
812             # Write results to csv
813             _LOGGER.debug("Writing CSV file to results directory %s",
814                           args.results_dir)
815         write_query_results_to_csv(
816             args.results_dir, args.csv_results_file_prefix, resultsdict)
817
818     except RuntimeError as e:
819         stc.end_session()
820         _LOGGER.error(e)
821
822     if args.verbose:
823         _LOGGER.debug("Destroy session on lab server")
824     stc.end_session()
825
826     _LOGGER.info("Test complete!")
827
828 if __name__ == "__main__":
829     main()