Merge "docs: add LTP to index.rst"
[vswitchperf.git] / tools / pkt_gen / testcenter / testcenter-rfc2544-rest.py
1 # Copyright 2016 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 '''
16 @author Spirent Communications
17
18 This test automates the RFC2544 tests using the Spirent
19 TestCenter REST APIs. This test supports Python 3.4
20
21 '''
22 import argparse
23 import logging
24 import os
25
26
27 logger = logging.getLogger(__name__)
28
29
30 def create_dir(path):
31     """Create the directory as specified in path """
32     if not os.path.exists(path):
33         try:
34             os.makedirs(path)
35         except OSError as e:
36             logger.error("Failed to create directory %s: %s", path, str(e))
37             raise
38
39
40 def write_query_results_to_csv(results_path, csv_results_file_prefix,
41                                query_results):
42     """ Write the results of the query to the CSV """
43     create_dir(results_path)
44     filec = os.path.join(results_path, csv_results_file_prefix + ".csv")
45     with open(filec, "wb") as f:
46         f.write(query_results["Columns"].replace(" ", ",") + "\n")
47         for row in (query_results["Output"].replace("} {", ",").
48                     replace("{", "").replace("}", "").split(",")):
49             f.write(row.replace(" ", ",") + "\n")
50
51
52 def positive_int(value):
53     """ Positive Integer type for Arguments """
54     ivalue = int(value)
55     if ivalue <= 0:
56         raise argparse.ArgumentTypeError(
57             "%s is an invalid positive int value" % value)
58     return ivalue
59
60
61 def percent_float(value):
62     """ Floating type for Arguments """
63     pvalue = float(value)
64     if pvalue < 0.0 or pvalue > 100.0:
65         raise argparse.ArgumentTypeError(
66             "%s not in range [0.0, 100.0]" % pvalue)
67     return pvalue
68
69
70 def main():
71     """ Read the arguments, Invoke Test and Return the results"""
72     parser = argparse.ArgumentParser()
73     # Required parameters
74     required_named = parser.add_argument_group("required named arguments")
75     required_named.add_argument("--lab_server_addr",
76                                 required=True,
77                                 help=("The IP address of the"
78                                       "Spirent Lab Server"),
79                                 dest="lab_server_addr")
80     required_named.add_argument("--license_server_addr",
81                                 required=True,
82                                 help=("The IP address of the Spirent"
83                                       "License Server"),
84                                 dest="license_server_addr")
85     required_named.add_argument("--east_chassis_addr",
86                                 required=True,
87                                 help=("The TestCenter chassis IP address to"
88                                       "use for the east test port"),
89                                 dest="east_chassis_addr")
90     required_named.add_argument("--east_slot_num",
91                                 type=positive_int,
92                                 required=True,
93                                 help=("The TestCenter slot number to"
94                                       "use for the east test port"),
95                                 dest="east_slot_num")
96     required_named.add_argument("--east_port_num",
97                                 type=positive_int,
98                                 required=True,
99                                 help=("The TestCenter port number to use"
100                                       "for the east test port"),
101                                 dest="east_port_num")
102     required_named.add_argument("--west_chassis_addr",
103                                 required=True,
104                                 help=("The TestCenter chassis IP address"
105                                       "to use for the west test port"),
106                                 dest="west_chassis_addr")
107     required_named.add_argument("--west_slot_num",
108                                 type=positive_int,
109                                 required=True,
110                                 help=("The TestCenter slot number to use"
111                                       "for the west test port"),
112                                 dest="west_slot_num")
113     required_named.add_argument("--west_port_num",
114                                 type=positive_int,
115                                 required=True,
116                                 help=("The TestCenter port number to"
117                                       "use for the west test port"),
118                                 dest="west_port_num")
119     # Optional parameters
120     optional_named = parser.add_argument_group("optional named arguments")
121     optional_named.add_argument("--metric",
122                                 required=False,
123                                 help=("One among - throughput, latency,\
124                                       backtoback and frameloss"),
125                                 choices=["throughput", "latency",
126                                          "backtoback", "frameloss"],
127                                 default="throughput",
128                                 dest="metric")
129     optional_named.add_argument("--test_session_name",
130                                 required=False,
131                                 default="RFC2544 East-West Throughput",
132                                 help=("The friendly name to identify"
133                                       "the Spirent Lab Server test session"),
134                                 dest="test_session_name")
135
136     optional_named.add_argument("--test_user_name",
137                                 required=False,
138                                 default="RFC2544 East-West User",
139                                 help=("The friendly name to identify the"
140                                       "Spirent Lab Server test user"),
141                                 dest="test_user_name")
142     optional_named.add_argument("--results_dir",
143                                 required=False,
144                                 default="./Results",
145                                 help="The directory to copy results to",
146                                 dest="results_dir")
147     optional_named.add_argument("--csv_results_file_prefix",
148                                 required=False,
149                                 default="Rfc2544Tput",
150                                 help="The prefix for the CSV results files",
151                                 dest="csv_results_file_prefix")
152     optional_named.add_argument("--num_trials",
153                                 type=positive_int,
154                                 required=False,
155                                 default=1,
156                                 help=("The number of trials to execute during"
157                                       "the test"),
158                                 dest="num_trials")
159     optional_named.add_argument("--trial_duration_sec",
160                                 type=positive_int,
161                                 required=False,
162                                 default=60,
163                                 help=("The duration of each trial executed"
164                                       "during the test"),
165                                 dest="trial_duration_sec")
166     optional_named.add_argument("--traffic_pattern",
167                                 required=False,
168                                 choices=["BACKBONE", "MESH", "PAIR"],
169                                 default="PAIR",
170                                 help="The traffic pattern between endpoints",
171                                 dest="traffic_pattern")
172     optional_named.add_argument("--traffic_custom",
173                                 required=False,
174                                 default=None,
175                                 help="The traffic pattern between endpoints",
176                                 dest="traffic_custom")
177     optional_named.add_argument("--search_mode",
178                                 required=False,
179                                 choices=["COMBO", "STEP", "BINARY"],
180                                 default="BINARY",
181                                 help=("The search mode used to find the"
182                                       "throughput rate"),
183                                 dest="search_mode")
184     optional_named.add_argument("--learning_mode",
185                                 required=False,
186                                 choices=["AUTO", "L2_LEARNING",
187                                          "L3_LEARNING", "NONE"],
188                                 default="AUTO",
189                                 help=("The learning mode used during the test,"
190                                       "default is 'NONE'"),
191                                 dest="learning_mode")
192     optional_named.add_argument("--rate_lower_limit_pct",
193                                 type=percent_float,
194                                 required=False,
195                                 default=1.0,
196                                 help=("The minimum percent line rate that"
197                                       "will be used during the test"),
198                                 dest="rate_lower_limit_pct")
199     optional_named.add_argument("--rate_upper_limit_pct",
200                                 type=percent_float,
201                                 required=False,
202                                 default=99.0,
203                                 help=("The maximum percent line rate that"
204                                       "will be used during the test"),
205                                 dest="rate_upper_limit_pct")
206     optional_named.add_argument("--rate_initial_pct",
207                                 type=percent_float,
208                                 required=False,
209                                 default=99.0,
210                                 help=("If Search Mode is BINARY, the percent"
211                                       "line rate that will be used at the"
212                                       "start of the test"),
213                                 dest="rate_initial_pct")
214     optional_named.add_argument("--rate_step_pct",
215                                 type=percent_float,
216                                 required=False,
217                                 default=10.0,
218                                 help=("If SearchMode is STEP, the percent"
219                                       "load increase per step"),
220                                 dest="rate_step_pct")
221     optional_named.add_argument("--resolution_pct",
222                                 type=percent_float,
223                                 required=False,
224                                 default=1.0,
225                                 help=("The minimum percentage of load"
226                                       "adjustment between iterations"),
227                                 dest="resolution_pct")
228     optional_named.add_argument("--frame_size_list",
229                                 type=lambda s: [int(item)
230                                                 for item in s.split(',')],
231                                 required=False,
232                                 default=[256],
233                                 help="A comma-delimited list of frame sizes",
234                                 dest="frame_size_list")
235     optional_named.add_argument("--acceptable_frame_loss_pct",
236                                 type=percent_float,
237                                 required=False,
238                                 default=0.0,
239                                 help=("The maximum acceptable frame loss"
240                                       "percent in any iteration"),
241                                 dest="acceptable_frame_loss_pct")
242     optional_named.add_argument("--east_intf_addr",
243                                 required=False,
244                                 default="192.85.1.3",
245                                 help=("The address to assign to the first"
246                                       "emulated device interface on the first"
247                                       "east port"),
248                                 dest="east_intf_addr")
249     optional_named.add_argument("--east_intf_gateway_addr",
250                                 required=False,
251                                 default="192.85.1.53",
252                                 help=("The gateway address to assign to the"
253                                       "first emulated device interface on the"
254                                       "first east port"),
255                                 dest="east_intf_gateway_addr")
256     optional_named.add_argument("--west_intf_addr",
257                                 required=False,
258                                 default="192.85.1.53",
259                                 help=("The address to assign to the first"
260                                       "emulated device interface on the"
261                                       "first west port"),
262                                 dest="west_intf_addr")
263     optional_named.add_argument("--west_intf_gateway_addr",
264                                 required=False,
265                                 default="192.85.1.53",
266                                 help=("The gateway address to assign to"
267                                       "the first emulated device interface"
268                                       "on the first west port"),
269                                 dest="west_intf_gateway_addr")
270     parser.add_argument("-v",
271                         "--verbose",
272                         required=False,
273                         default=True,
274                         help="More output during operation when present",
275                         action="store_true",
276                         dest="verbose")
277     args = parser.parse_args()
278
279     if args.verbose:
280         logger.debug("Creating results directory")
281     create_dir(args.results_dir)
282
283     session_name = args.test_session_name
284     user_name = args.test_user_name
285
286     try:
287         # Load Spirent REST Library
288         from stcrestclient import stchttp
289
290         stc = stchttp.StcHttp(args.lab_server_addr)
291         session_id = stc.new_session(user_name, session_name)
292         stc.join_session(session_id)
293     except RuntimeError as e:
294         logger.error(e)
295         raise
296
297     # Get STC system info.
298     tx_port_loc = "//%s/%s/%s" % (args.east_chassis_addr,
299                                   args.east_slot_num,
300                                   args.east_port_num)
301     rx_port_loc = "//%s/%s/%s" % (args.west_chassis_addr,
302                                   args.west_slot_num,
303                                   args.west_port_num)
304
305     # Retrieve and display the server information
306     if args.verbose:
307         logger.debug("SpirentTestCenter system version: %s",
308                      stc.get("system1", "version"))
309
310     try:
311         device_list = []
312         port_list = []
313         if args.verbose:
314             logger.debug("Bring up license server")
315         license_mgr = stc.get("system1", "children-licenseservermanager")
316         if args.verbose:
317             logger.debug("license_mgr = %s", license_mgr)
318         stc.create("LicenseServer", under=license_mgr, attributes={
319                    "server": args.license_server_addr})
320
321         # Create the root project object
322         if args.verbose:
323             logger.debug("Creating project ...")
324         project = stc.get("System1", "children-Project")
325
326         # Configure any custom traffic parameters
327         if args.traffic_custom == "cont":
328             if args.verbose:
329                 logger.debug("Configure Continuous Traffic")
330             stc.create("ContinuousTestConfig", under=project)
331
332         # Create ports
333         if args.verbose:
334             logger.debug("Creating ports ...")
335         east_chassis_port = stc.create('port', project)
336         if args.verbose:
337             logger.debug("Configuring TX port ...")
338         stc.config(east_chassis_port, {'location': tx_port_loc})
339         port_list.append(east_chassis_port)
340
341         west_chassis_port = stc.create('port', project)
342         if args.verbose:
343             logger.debug("Configuring RX port ...")
344         stc.config(west_chassis_port, {'location': rx_port_loc})
345         port_list.append(west_chassis_port)
346
347         # Create emulated genparam for east port
348         east_device_gen_params = stc.create("EmulatedDeviceGenParams",
349                                             under=project,
350                                             attributes={"Port":
351                                                         east_chassis_port})
352         # Create the DeviceGenEthIIIfParams object
353         stc.create("DeviceGenEthIIIfParams",
354                    under=east_device_gen_params)
355         # Configuring Ipv4 interfaces
356         stc.create("DeviceGenIpv4IfParams",
357                    under=east_device_gen_params,
358                    attributes={"Addr": args.east_intf_addr,
359                                "Gateway": args.east_intf_gateway_addr})
360         # Create Devices using the Device Wizard
361         device_gen_config = stc.perform("DeviceGenConfigExpand",
362                                         params={"DeleteExisting": "No",
363                                                 "GenParams":
364                                                 east_device_gen_params})
365         # Append to the device list
366         device_list.append(device_gen_config['ReturnList'])
367
368         # Create emulated genparam for west port
369         west_device_gen_params = stc.create("EmulatedDeviceGenParams",
370                                             under=project,
371                                             attributes={"Port":
372                                                         west_chassis_port})
373         # Create the DeviceGenEthIIIfParams object
374         stc.create("DeviceGenEthIIIfParams",
375                    under=west_device_gen_params)
376         # Configuring Ipv4 interfaces
377         stc.create("DeviceGenIpv4IfParams",
378                    under=west_device_gen_params,
379                    attributes={"Addr": args.west_intf_addr,
380                                "Gateway": args.west_intf_gateway_addr})
381         # Create Devices using the Device Wizard
382         device_gen_config = stc.perform("DeviceGenConfigExpand",
383                                         params={"DeleteExisting": "No",
384                                                 "GenParams":
385                                                 west_device_gen_params})
386         # Append to the device list
387         device_list.append(device_gen_config['ReturnList'])
388         if args.verbose:
389             logger.debug(device_list)
390
391         # Create the RFC 2544 'metric test
392         if args.metric == "throughput":
393             if args.verbose:
394                 logger.debug("Set up the RFC2544 throughput test...")
395             stc.perform("Rfc2544SetupThroughputTestCommand",
396                         params={"AcceptableFrameLoss":
397                                 args.acceptable_frame_loss_pct,
398                                 "Duration": args.trial_duration_sec,
399                                 "FrameSizeList": args.frame_size_list,
400                                 "LearningMode": args.learning_mode,
401                                 "NumOfTrials": args.num_trials,
402                                 "RateInitial": args.rate_initial_pct,
403                                 "RateLowerLimit": args.rate_lower_limit_pct,
404                                 "RateStep": args.rate_step_pct,
405                                 "RateUpperLimit": args.rate_upper_limit_pct,
406                                 "Resolution": args.resolution_pct,
407                                 "SearchMode": args.search_mode,
408                                 "TrafficPattern": args.traffic_pattern})
409         elif args.metric == "backtoback":
410             stc.perform("Rfc2544SetupBackToBackTestCommand",
411                         params={"AcceptableFrameLoss":
412                                 args.acceptable_frame_loss_pct,
413                                 "Duration": args.trial_duration_sec,
414                                 "FrameSizeList": args.frame_size_list,
415                                 "LearningMode": args.learning_mode,
416                                 "LatencyType": args.latency_type,
417                                 "NumOfTrials": args.num_trials,
418                                 "RateInitial": args.rate_initial_pct,
419                                 "RateLowerLimit": args.rate_lower_limit_pct,
420                                 "RateStep": args.rate_step_pct,
421                                 "RateUpperLimit": args.rate_upper_limit_pct,
422                                 "Resolution": args.resolution_pct,
423                                 "SearchMode": args.search_mode,
424                                 "TrafficPattern": args.traffic_pattern})
425         elif args.metric == "frameloss":
426             stc.perform("Rfc2544SetupFrameLossTestCommand",
427                         params={"AcceptableFrameLoss":
428                                 args.acceptable_frame_loss_pct,
429                                 "Duration": args.trial_duration_sec,
430                                 "FrameSizeList": args.frame_size_list,
431                                 "LearningMode": args.learning_mode,
432                                 "LatencyType": args.latency_type,
433                                 "NumOfTrials": args.num_trials,
434                                 "RateInitial": args.rate_initial_pct,
435                                 "RateLowerLimit": args.rate_lower_limit_pct,
436                                 "RateStep": args.rate_step_pct,
437                                 "RateUpperLimit": args.rate_upper_limit_pct,
438                                 "Resolution": args.resolution_pct,
439                                 "SearchMode": args.search_mode,
440                                 "TrafficPattern": args.traffic_pattern})
441         elif args.metric == "latency":
442             stc.perform("Rfc2544SetupLatencyTestCommand",
443                         params={"AcceptableFrameLoss":
444                                 args.acceptable_frame_loss_pct,
445                                 "Duration": args.trial_duration_sec,
446                                 "FrameSizeList": args.frame_size_list,
447                                 "LearningMode": args.learning_mode,
448                                 "LatencyType": args.latency_type,
449                                 "NumOfTrials": args.num_trials,
450                                 "RateInitial": args.rate_initial_pct,
451                                 "RateLowerLimit": args.rate_lower_limit_pct,
452                                 "RateStep": args.rate_step_pct,
453                                 "RateUpperLimit": args.rate_upper_limit_pct,
454                                 "Resolution": args.resolution_pct,
455                                 "SearchMode": args.search_mode,
456                                 "TrafficPattern": args.traffic_pattern})
457
458         # Save the configuration
459         stc.perform("SaveToTcc", params={"Filename": "2544.tcc"})
460         # Connect to the hardware...
461         stc.perform("AttachPorts", params={"portList": stc.get(
462             "system1.project", "children-port"), "autoConnect": "TRUE"})
463         # Apply configuration.
464         if args.verbose:
465             logger.debug("Apply configuration...")
466         stc.apply()
467
468         if args.verbose:
469             logger.debug("Starting the sequencer...")
470         stc.perform("SequencerStart")
471
472         # Wait for sequencer to finish
473         logger.info(
474             "Starting test... Please wait for the test to complete...")
475         stc.wait_until_complete()
476         logger.info("The test has completed... Saving results...")
477
478         # Determine what the results database filename is...
479         lab_server_resultsdb = stc.get(
480             "system1.project.TestResultSetting", "CurrentResultFileName")
481
482         if args.verbose:
483             logger.debug("The lab server results database is %s",
484                          lab_server_resultsdb)
485
486         stc.perform("CSSynchronizeFiles",
487                     params={"DefaultDownloadDir": args.results_dir})
488
489         resultsdb = args.results_dir + \
490             lab_server_resultsdb.split("/Results")[1]
491
492         logger.info(
493             "The local summary DB file has been saved to %s", resultsdb)
494
495         # The returns the "RFC2544ThroughputTestResultDetailedSummaryView"
496         # table view from the results database.
497         # There are other views available.
498
499         if args.metric == "throughput":
500             resultsdict = (
501                 stc.perform("QueryResult",
502                             params={
503                                 "DatabaseConnectionString":
504                                 resultsdb,
505                                 "ResultPath":
506                                 ("RFC2544ThroughputTestResultDetailed"
507                                  "SummaryView")}))
508
509         # The returns the "RFC2544BacktoBackTestResultDetailedSummaryView"
510         # table view from the results database.
511         # There are other views available.
512         elif args.metric == "backtoback":
513             resultsdict = (
514                 stc.perform("QueryResult",
515                             params={
516                                 "DatabaseConnectionString":
517                                 resultsdb,
518                                 "ResultPath":
519                                 ("RFC2544Back2BackTestResultDetailed"
520                                  "SummaryView")}))
521
522         # The returns the "RFC2544LatencyTestResultDetailedSummaryView"
523         # table view from the results database.
524         # There are other views available.
525         elif args.metric == "latency":
526             resultsdict = (
527                 stc.perform("QueryResult",
528                             params={
529                                 "DatabaseConnectionString":
530                                 resultsdb,
531                                 "ResultPath":
532                                 ("RFC2544LatencyTestResultDetailed"
533                                  "SummaryView")}))
534
535         # The returns the "RFC2544FrameLossTestResultDetailedSummaryView"
536         # table view from the results database.
537         # There are other views available.
538         elif args.metric == "frameloss":
539             resultsdict = (
540                 stc.perform("QueryResult",
541                             params={
542                                 "DatabaseConnectionString":
543                                 resultsdb,
544                                 "ResultPath":
545                                 ("RFC2544FrameLossTestResultDetailed"
546                                  "SummaryView")}))
547         if args.verbose:
548             logger.debug("resultsdict[\"Columns\"]: %s",
549                          resultsdict["Columns"])
550             logger.debug("resultsdict[\"Output\"]: %s", resultsdict["Output"])
551             logger.debug("Result paths: %s",
552                          stc.perform("GetTestResultSettingPaths"))
553
554             # Write results to csv
555             logger.debug("Writing CSV file to results directory %s",
556                          args.results_dir)
557         write_query_results_to_csv(
558             args.results_dir, args.csv_results_file_prefix, resultsdict)
559
560     except RuntimeError as e:
561         logger.error(e)
562
563     if args.verbose:
564         logger.debug("Destroy session on lab server")
565     stc.end_session()
566
567     logger.info("Test complete!")
568
569 if __name__ == "__main__":
570     main()