Merge "trex_learning: Add learning packet option to T-Rex testing"
[vswitchperf.git] / tools / pkt_gen / testcenter / testcenter-rfc2544-throughput.py
1 # Copyright 2015 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 Comunications
17
18 This test automates the RFC2544 throughput test using the Spirent
19 TestCenter command sequencer. This test supports Python 2.7.
20
21 '''
22 from __future__ import print_function
23 import argparse
24 import os
25 from conf import settings
26
27
28 def create_dir(path):
29     if not os.path.exists(path):
30         try:
31             os.makedirs(path)
32         except Exception, e:
33             print("Failed to create directory %s: %s" % (path, str(e)))
34             raise
35
36
37 def write_query_results_to_csv(results_path, csv_results_file_prefix, query_results):
38     create_dir(results_path)
39     file = os.path.join(results_path, csv_results_file_prefix + ".csv")
40     with open(file, "wb") as f:
41         f.write(query_results["Columns"].replace(" ", ",") + "\n")
42         for row in query_results["Output"].replace("} {", ",").replace("{", "").replace("}", "").split(","):
43             f.write(row.replace(" ", ",") + "\n")
44
45
46 def destroy_session(stc, lab_server_addr, test_session_name):
47     stc.perform("CSServerConnectCommand",
48                 Host=lab_server_addr)
49
50     cs_server = stc.get("system1", "children-csserver")
51
52     cs_test_sessions = stc.get(cs_server, "children-cstestsession").split()
53
54     for session in cs_test_sessions:
55         name = stc.get(session, "Name")
56         if test_session_name in name:
57             stc.perform("CSDestroyTestSession",
58                         testSession=session)
59             break
60
61
62 def create_session(stc, lab_server_addr, test_session_name):
63     destroy_session(stc, lab_server_addr, test_session_name)
64     stc.perform("CSTestSessionConnect",
65                 Host=lab_server_addr,
66                 CreateNewTestSession="TRUE",
67                 TestSessionName=test_session_name)
68
69
70 def positive_int(value):
71     ivalue = int(value)
72     if ivalue <= 0:
73         raise argparse.ArgumentTypeError("%s is an invalid positive int value" % value)
74     return ivalue
75
76
77 def percent_float(value):
78     pvalue = float(value)
79     if pvalue < 0.0 or pvalue > 100.0:
80         raise argparse.ArgumentTypeError("%s not in range [0.0, 100.0]" % pvalue)
81     return pvalue
82
83
84 def main():
85     parser = argparse.ArgumentParser()
86     # Required parameters
87     requirednamed = parser.add_argument_group("required named arguments")
88     requirednamed.add_argument("--lab_server_addr",
89                                required=True,
90                                help="The IP address of the Spirent Lab Server",
91                                dest="lab_server_addr")
92     requirednamed.add_argument("--license_server_addr",
93                                required=True,
94                                help="The IP address of the Spirent License Server",
95                                dest="license_server_addr")
96     requirednamed.add_argument("--east_chassis_addr",
97                                required=True,
98                                help="The TestCenter chassis IP address to use for the east test port",
99                                dest="east_chassis_addr")
100     requirednamed.add_argument("--east_slot_num",
101                                type=positive_int,
102                                required=True,
103                                help="The TestCenter slot number to use for the east test port",
104                                dest="east_slot_num")
105     requirednamed.add_argument("--east_port_num",
106                                type=positive_int,
107                                required=True,
108                                help="The TestCenter port number to use for the east test port",
109                                dest="east_port_num")
110     requirednamed.add_argument("--west_chassis_addr",
111                                required=True,
112                                help="The TestCenter chassis IP address to use for the west test port",
113                                dest="west_chassis_addr")
114     requirednamed.add_argument("--west_slot_num",
115                                type=positive_int,
116                                required=True,
117                                help="The TestCenter slot number to use for the west test port",
118                                dest="west_slot_num")
119     requirednamed.add_argument("--west_port_num",
120                                type=positive_int,
121                                required=True,
122                                help="The TestCenter port number to use for the west test port",
123                                dest="west_port_num")
124     # Optional parameters
125     optionalnamed = parser.add_argument_group("optional named arguments")
126     optionalnamed.add_argument("--test_session_name",
127                                required=False,
128                                default="RFC2544 East-West Throughput",
129                                help="The friendly name to identify the Spirent Lab Server test session",
130                                dest="test_session_name")
131     optionalnamed.add_argument("--results_dir",
132                                required=False,
133                                default=settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
134                                help="The directory to copy results to",
135                                dest="results_dir")
136     optionalnamed.add_argument("--csv_results_file_prefix",
137                                required=False,
138                                default="Rfc2544Tput",
139                                help="The prefix for the CSV results files",
140                                dest="csv_results_file_prefix")
141     optionalnamed.add_argument("--num_trials",
142                                type=positive_int,
143                                required=False,
144                                default=1,
145                                help="The number of trials to execute during the test",
146                                dest="num_trials")
147     optionalnamed.add_argument("--trial_duration_sec",
148                                type=positive_int,
149                                required=False,
150                                default=60,
151                                help="The duration of each trial executed during the test",
152                                dest="trial_duration_sec")
153     optionalnamed.add_argument("--traffic_pattern",
154                                required=False,
155                                choices=["BACKBONE", "MESH", "PAIR"],
156                                default="PAIR",
157                                help="The traffic pattern between endpoints",
158                                dest="traffic_pattern")
159     optionalnamed.add_argument("--search_mode",
160                                required=False,
161                                choices=["COMBO", "STEP", "BINARY"],
162                                default="BINARY",
163                                help="The search mode used to find the throughput rate",
164                                dest="search_mode")
165     optionalnamed.add_argument("--learning_mode",
166                                required=False,
167                                choices=["AUTO", "L2_LEARNING", "L3_LEARNING", "NONE"],
168                                default="AUTO",
169                                help="The learning mode used during the test, default = 'NONE'",
170                                dest="learning_mode")
171     optionalnamed.add_argument("--rate_lower_limit_pct",
172                                type=percent_float,
173                                required=False,
174                                default=1.0,
175                                help="The minimum percent line rate that will be used during the test",
176                                dest="rate_lower_limit_pct")
177     optionalnamed.add_argument("--rate_upper_limit_pct",
178                                type=percent_float,
179                                required=False,
180                                default=99.0,
181                                help="The maximum percent line rate that will be used during the test",
182                                dest="rate_upper_limit_pct")
183     optionalnamed.add_argument("--rate_initial_pct",
184                                type=percent_float,
185                                required=False,
186                                default=99.0,
187                                help="If Search Mode is BINARY, the percent line rate that will be used at the start of the test",
188                                dest="rate_initial_pct")
189     optionalnamed.add_argument("--rate_step_pct",
190                                type=percent_float,
191                                required=False,
192                                default=10.0,
193                                help="If SearchMode is STEP, the percent load increase per step",
194                                dest="rate_step_pct")
195     optionalnamed.add_argument("--resolution_pct",
196                                type=percent_float,
197                                required=False,
198                                default=1.0,
199                                help="The minimum percentage of load adjustment between iterations",
200                                dest="resolution_pct")
201     optionalnamed.add_argument("--frame_size_list",
202                                type=lambda s: [int(item) for item in s.split(',')],
203                                required=False,
204                                default=[256],
205                                help="A comma-delimited list of frame sizes",
206                                dest="frame_size_list")
207     optionalnamed.add_argument("--acceptable_frame_loss_pct",
208                                type=percent_float,
209                                required=False,
210                                default=0.0,
211                                help="The maximum acceptable frame loss percent in any iteration",
212                                dest="acceptable_frame_loss_pct")
213     optionalnamed.add_argument("--east_intf_addr",
214                                required=False,
215                                default="192.85.1.3",
216                                help="The address to assign to the first emulated device interface on the first east port",
217                                dest="east_intf_addr")
218     optionalnamed.add_argument("--east_intf_gateway_addr",
219                                required=False,
220                                default="192.85.1.53",
221                                help="The gateway address to assign to the first emulated device interface on the first east port",
222                                dest="east_intf_gateway_addr")
223     optionalnamed.add_argument("--west_intf_addr",
224                                required=False,
225                                default="192.85.1.53",
226                                help="The address to assign to the first emulated device interface on the first west port",
227                                dest="west_intf_addr")
228     optionalnamed.add_argument("--west_intf_gateway_addr",
229                                required=False,
230                                default="192.85.1.53",
231                                help="The gateway address to assign to the first emulated device interface on the first west port",
232                                dest="west_intf_gateway_addr")
233     parser.add_argument("-v",
234                         "--verbose",
235                         required=False,
236                         default=False,
237                         help="More output during operation when present",
238                         action="store_true",
239                         dest="verbose")
240     args = parser.parse_args()
241
242     if args.verbose:
243         print("Make sure results directory exists")
244     create_dir(args.results_dir)
245
246     # Load Spirent Test Center library
247     from StcPython import StcPython
248     stc = StcPython()
249
250     tx_port_loc = "//%s/%s/%s" % (args.east_chassis_addr,
251                                   args.east_slot_num,
252                                   args.east_port_num)
253     rx_port_loc = "//%s/%s/%s" % (args.west_chassis_addr,
254                                   args.west_slot_num,
255                                   args.west_port_num)
256
257     # This line will write the TestCenter commands to a log file
258     stc.config("automationoptions", logto="test-op", loglevel="DEBUG")
259
260     # Retrieve and display the current API version.
261     if args.verbose:
262         print ("SpirentTestCenter system version: %s" % stc.get("system1", "version"))
263
264     create_session(stc, args.lab_server_addr, args.test_session_name)
265
266     try:
267         device_list = []
268
269         if args.verbose:
270             print("Bring up license server")
271         license_mgr = stc.get("system1", "children-licenseservermanager")
272         if args.verbose:
273             print("license_mgr = %s" % license_mgr)
274         stc.create("LicenseServer", under=license_mgr, server=args.license_server_addr)
275
276         # Create the root project object
277         if args.verbose:
278             print ("Creating project ...")
279         project = stc.get("System1", "children-Project")
280
281         # Create ports
282         if args.verbose:
283             print ("Creating ports ...")
284         east_chassis_port = stc.create("port",
285                                        under=project,
286                                        location=tx_port_loc,
287                                        useDefaultHost="False")
288         west_chassis_port = stc.create("port",
289                                        under=project,
290                                        location=rx_port_loc,
291                                        useDefaultHost="False")
292
293         if args.verbose:
294             print ("Creating devices...")
295         # Create emulated genparam for east port
296         east_device_gen_params = stc.create("EmulatedDeviceGenParams",
297                                             under=project,
298                                             Port=east_chassis_port)
299         # Create the DeviceGenEthIIIfParams object
300         stc.create("DeviceGenEthIIIfParams",
301                    under=east_device_gen_params)
302         # Configuring Ipv4 interfaces
303         stc.create("DeviceGenIpv4IfParams",
304                    under=east_device_gen_params,
305                    Addr=args.east_intf_addr,
306                    Gateway=args.east_intf_gateway_addr)
307         # Create Devices using the Device Wizard
308         device_gen_config = stc.perform("DeviceGenConfigExpand",
309                                         DeleteExisting="No",
310                                         GenParams=east_device_gen_params)
311         # Append to the device list
312         device_list.append(device_gen_config['ReturnList'])
313
314         # Create emulated genparam for west port
315         west_device_gen_params = stc.create("EmulatedDeviceGenParams",
316                                             under=project,
317                                             Port=west_chassis_port)
318         # Create the DeviceGenEthIIIfParams object
319         stc.create("DeviceGenEthIIIfParams",
320                    under=west_device_gen_params)
321         # Configuring Ipv4 interfaces
322         stc.create("DeviceGenIpv4IfParams",
323                    under=west_device_gen_params,
324                    Addr=args.west_intf_addr,
325                    Gateway=args.west_intf_gateway_addr)
326         # Create Devices using the Device Wizard
327         device_gen_config = stc.perform("DeviceGenConfigExpand",
328                                         DeleteExisting="No",
329                                         GenParams=west_device_gen_params)
330         # Append to the device list
331         device_list.append(device_gen_config['ReturnList'])
332
333         if args.verbose:
334             print(device_list)
335
336         # Create the RFC 2544 throughput test
337         if args.verbose:
338             print ("Set up the RFC2544 throughput test...")
339         stc.perform("Rfc2544SetupThroughputTestCommand",
340                     AcceptableFrameLoss=args.acceptable_frame_loss_pct,
341                     Duration=args.trial_duration_sec,
342                     FrameSizeList=args.frame_size_list,
343                     LearningMode=args.learning_mode,
344                     NumOfTrials=args.num_trials,
345                     RateInitial=args.rate_initial_pct,
346                     RateLowerLimit=args.rate_lower_limit_pct,
347                     RateStep=args.rate_step_pct,
348                     RateUpperLimit=args.rate_upper_limit_pct,
349                     Resolution=args.resolution_pct,
350                     SearchMode=args.search_mode,
351                     TrafficPattern=args.traffic_pattern)
352
353         # Save the configuration
354         stc.perform("SaveToTcc", Filename="2544.tcc")
355
356         # Connect to the hardware...
357         stc.perform("AttachPorts", portList=stc.get("system1.project", "children-port"), autoConnect="TRUE")
358
359         # Apply configuration.
360         if args.verbose:
361             print("Apply configuration...")
362         stc.apply
363
364         if args.verbose:
365             print("Starting the sequencer...")
366         stc.perform("SequencerStart")
367
368         # Wait for sequencer to finish
369         print("Starting test... Please wait for the test to complete...")
370         stc.waitUntilComplete()
371         print("The test has completed... Saving results...")
372
373         # Determine what the results database filename is...
374         lab_server_resultsdb = stc.get("system1.project.TestResultSetting", "CurrentResultFileName")
375
376         if args.verbose:
377             print("The lab server results database is %s" % lab_server_resultsdb)
378
379         stc.perform("CSSynchronizeFiles",
380                     DefaultDownloadDir=args.results_dir)
381
382         resultsdb = args.results_dir + lab_server_resultsdb.split("/Results")[1]
383
384         print("The local summary DB file has been saved to %s" % resultsdb)
385
386         # The returns the "RFC2544ThroughputTestResultDetailedSummaryView" table view from the results database.
387         # There are other views available.
388         resultsdict = stc.perform("QueryResult",
389                                   DatabaseConnectionString=resultsdb,
390                                   ResultPath="RFC2544ThroughputTestResultDetailedSummaryView")
391         if args.verbose:
392             print("resultsdict[\"Columns\"]: %s" % resultsdict["Columns"])
393             print("resultsdict[\"Output\"]: %s" % resultsdict["Output"])
394
395         if args.verbose:
396             print("Result paths: %s" % stc.perform("GetTestResultSettingPaths"))
397
398         # Write results to csv
399         if args.verbose:
400             print("Writing CSV file to results directory %s" % args.results_dir)
401         write_query_results_to_csv(args.results_dir, args.csv_results_file_prefix, resultsdict)
402
403     except Exception, e:
404         print("Exception: %s" % str(e))
405         # destroy_session(stc, args.lab_server_addr, args.test_session_name)
406         raise
407
408     if args.verbose:
409         print("Disconnect from lab server")
410     stc.perform("CSTestSessionDisconnect")
411
412     if args.verbose:
413         print("Destroy session on lab server")
414     # destroy_session(stc, args.lab_server_addr, args.test_session_name)
415     print("Test complete!")
416
417
418 if __name__ == "__main__":
419     main()