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