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