Merge "trex: Add support for burst traffic type"
[vswitchperf.git] / tools / pkt_gen / testcenter / testcenter.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 Code to integrate Spirent TestCenter with the vsperf test framework.
17
18 Provides a model for Spirent TestCenter as a test tool for implementing
19 various performance tests of a virtual switch.
20 """
21
22 import csv
23 import logging
24 import os
25 import subprocess
26
27 from conf import settings
28 from core.results.results_constants import ResultsConstants
29 from tools.pkt_gen import trafficgen
30
31
32 def get_stc_common_settings():
33     """
34     Return the common Settings
35     These settings would apply to almost all the tests.
36     """
37     args = ["--lab_server_addr",
38             settings.getValue("TRAFFICGEN_STC_LAB_SERVER_ADDR"),
39             "--license_server_addr",
40             settings.getValue("TRAFFICGEN_STC_LICENSE_SERVER_ADDR"),
41             "--east_chassis_addr",
42             settings.getValue("TRAFFICGEN_STC_EAST_CHASSIS_ADDR"),
43             "--east_slot_num",
44             settings.getValue("TRAFFICGEN_STC_EAST_SLOT_NUM"),
45             "--east_port_num",
46             settings.getValue("TRAFFICGEN_STC_EAST_PORT_NUM"),
47             "--west_chassis_addr",
48             settings.getValue("TRAFFICGEN_STC_WEST_CHASSIS_ADDR"),
49             "--west_slot_num",
50             settings.getValue("TRAFFICGEN_STC_WEST_SLOT_NUM"),
51             "--west_port_num",
52             settings.getValue("TRAFFICGEN_STC_WEST_PORT_NUM"),
53             "--test_session_name",
54             settings.getValue("TRAFFICGEN_STC_TEST_SESSION_NAME"),
55             "--results_dir",
56             settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
57             "--csv_results_file_prefix",
58             settings.getValue("TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX")]
59     return args
60
61
62 def get_rfc2544_common_settings():
63     """
64     Retrun Generic RFC 2544 settings.
65     These settings apply to all the 2544 tests
66     """
67     args = [settings.getValue("TRAFFICGEN_STC_PYTHON2_PATH"),
68             os.path.join(
69                 settings.getValue("TRAFFICGEN_STC_TESTCENTER_PATH"),
70                 settings.getValue(
71                     "TRAFFICGEN_STC_RFC2544_TPUT_TEST_FILE_NAME")),
72             "--metric",
73             settings.getValue("TRAFFICGEN_STC_RFC2544_METRIC"),
74             "--search_mode",
75             settings.getValue("TRAFFICGEN_STC_SEARCH_MODE"),
76             "--learning_mode",
77             settings.getValue("TRAFFICGEN_STC_LEARNING_MODE"),
78             "--rate_lower_limit_pct",
79             settings.getValue("TRAFFICGEN_STC_RATE_LOWER_LIMIT_PCT"),
80             "--rate_upper_limit_pct",
81             settings.getValue("TRAFFICGEN_STC_RATE_UPPER_LIMIT_PCT"),
82             "--rate_initial_pct",
83             settings.getValue("TRAFFICGEN_STC_RATE_INITIAL_PCT"),
84             "--rate_step_pct",
85             settings.getValue("TRAFFICGEN_STC_RATE_STEP_PCT"),
86             "--resolution_pct",
87             settings.getValue("TRAFFICGEN_STC_RESOLUTION_PCT"),
88             "--acceptable_frame_loss_pct",
89             settings.getValue("TRAFFICGEN_STC_ACCEPTABLE_FRAME_LOSS_PCT"),
90             "--east_intf_addr",
91             settings.getValue("TRAFFICGEN_STC_EAST_INTF_ADDR"),
92             "--east_intf_gateway_addr",
93             settings.getValue("TRAFFICGEN_STC_EAST_INTF_GATEWAY_ADDR"),
94             "--west_intf_addr",
95             settings.getValue("TRAFFICGEN_STC_WEST_INTF_ADDR"),
96             "--west_intf_gateway_addr",
97             settings.getValue("TRAFFICGEN_STC_WEST_INTF_GATEWAY_ADDR"),
98             "--trial_duration_sec",
99             settings.getValue("TRAFFICGEN_STC_TRIAL_DURATION_SEC"),
100             "--traffic_pattern",
101             settings.getValue("TRAFFICGEN_STC_TRAFFIC_PATTERN")]
102     return args
103
104
105 def get_rfc2544_custom_settings(framesize, custom_tr, tests):
106     """
107     Return RFC2544 Custom Settings
108     """
109     args = ["--frame_size_list",
110             str(framesize),
111             "--traffic_custom",
112             str(custom_tr),
113             "--num_trials",
114             str(tests)]
115     return args
116
117
118 def get_rfc2889_common_settings(framesize, tests, metric):
119     """
120     Return RFC2889 common Settings
121     """
122     new_metric = metric.replace('rfc2889_', '')
123     args = [settings.getValue("TRAFFICGEN_STC_PYTHON2_PATH"),
124             os.path.join(
125                 settings.getValue("TRAFFICGEN_STC_TESTCENTER_PATH"),
126                 settings.getValue(
127                     "TRAFFICGEN_STC_RFC2889_TEST_FILE_NAME")),
128             "--lab_server_addr",
129             settings.getValue("TRAFFICGEN_STC_LAB_SERVER_ADDR"),
130             "--license_server_addr",
131             settings.getValue("TRAFFICGEN_STC_LICENSE_SERVER_ADDR"),
132             "--location_list",
133             settings.getValue("TRAFFICGEN_STC_RFC2889_LOCATIONS"),
134             "--test_session_name",
135             settings.getValue("TRAFFICGEN_STC_TEST_SESSION_NAME"),
136             "--results_dir",
137             settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
138             "--csv_results_file_prefix",
139             settings.getValue("TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX"),
140             "--frame_size_list",
141             str(framesize),
142             "--metric",
143             str(new_metric),
144             "--num_trials",
145             str(tests)]
146
147     return args
148
149
150 def get_rfc2889_custom_settings():
151     """
152     Return RFC2889 Custom Settings
153     """
154     args = ["--min_learning_rate",
155             settings.getValue("TRAFFICGEN_STC_RFC2889_MIN_LR"),
156             "--max_learning_rate",
157             settings.getValue("TRAFFICGEN_STC_RFC2889_MAX_LR"),
158             "--min_num_addrs",
159             settings.getValue("TRAFFICGEN_STC_RFC2889_MIN_ADDRS"),
160             "--max_num_addrs",
161             settings.getValue("TRAFFICGEN_STC_RFC2889_MAX_ADDRS"),
162             "--ac_learning_rate",
163             settings.getValue("TRAFFICGEN_STC_RFC2889_AC_LR")]
164     return args
165
166
167 class TestCenter(trafficgen.ITrafficGenerator):
168     """
169     Spirent TestCenter
170     """
171     _logger = logging.getLogger(__name__)
172
173     def connect(self):
174         """
175         Do nothing.
176         """
177         return self
178
179     def disconnect(self):
180         """
181         Do nothing.
182         """
183         pass
184
185     def send_burst_traffic(self, traffic=None, duration=20):
186         """
187         Do nothing.
188         """
189         return None
190
191     def get_rfc2889_addr_learning_results(self, filename):
192         """
193         Reads the CSV file and return the results
194         """
195         result = {}
196         with open(filename, "r") as csvfile:
197             csvreader = csv.DictReader(csvfile)
198             for row in csvreader:
199                 self._logger.info("Row: %s", row)
200                 learn_rate = float(row["OptimalLearningRate"])
201                 result[ResultsConstants.OPTIMAL_LEARNING_RATE_FPS] = learn_rate
202         return result
203
204     def get_rfc2889_addr_caching_results(self, filename):
205         """
206         Reads the CSV file and return the results
207         """
208         result = {}
209         with open(filename, "r") as csvfile:
210             csvreader = csv.DictReader(csvfile)
211             for row in csvreader:
212                 self._logger.info("Row: %s", row)
213                 caching_cap = float(row["RxFrameCount"])
214                 learn_per = (100.0 - (float(row["PercentFrameLoss(%)"])))
215                 result[ResultsConstants.CACHING_CAPACITY_ADDRS] = caching_cap
216                 result[ResultsConstants.ADDR_LEARNED_PERCENT] = learn_per
217         return result
218
219     def get_rfc2889_forwarding_results(self, filename):
220         """
221         Reads the CSV file and return the results
222         """
223         result = {}
224         with open(filename, "r") as csvfile:
225             csvreader = csv.DictReader(csvfile)
226             for row in csvreader:
227                 self._logger.info("Row: %s", row)
228                 duration = int((float(row["TxSignatureFrameCount"])) /
229                                (float(row["OfferedLoad(fps)"])))
230                 tx_fps = (float(row["OfferedLoad(fps)"]))
231                 rx_fps = float((float(row["RxFrameCount"])) /
232                                float(duration))
233                 tx_mbps = ((tx_fps * float(row["FrameSize"])) /
234                            (1000000.0))
235                 rx_mbps = ((rx_fps * float(row["FrameSize"])) /
236                            (1000000.0))
237                 result[ResultsConstants.TX_RATE_FPS] = tx_fps
238                 result[ResultsConstants.THROUGHPUT_RX_FPS] = rx_fps
239                 result[ResultsConstants.TX_RATE_MBPS] = tx_mbps
240                 result[ResultsConstants.THROUGHPUT_RX_MBPS] = rx_mbps
241                 result[ResultsConstants.TX_RATE_PERCENT] = float(
242                     row["OfferedLoad(%)"])
243                 result[ResultsConstants.FRAME_LOSS_PERCENT] = float(
244                     row["PercentFrameLoss(%)"])
245                 result[ResultsConstants.FORWARDING_RATE_FPS] = float(
246                     row["ForwardingRate(fps)"])
247         return result
248
249     def send_rfc2889_forwarding(self, traffic=None, tests=1, _duration=20):
250         """
251         Send traffic per RFC2889 Forwarding test specifications.
252         """
253         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
254         if traffic and 'l2' in traffic:
255             if 'framesize' in traffic['l2']:
256                 framesize = traffic['l2']['framesize']
257         args = get_rfc2889_common_settings(framesize, tests,
258                                            traffic['traffic_type'])
259         if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
260             args.append("--verbose")
261             verbose = True
262             self._logger.debug("Arguments used to call test: %s", args)
263         subprocess.check_call(args)
264
265         filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
266                              settings.getValue(
267                                  "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
268                              ".csv")
269
270         if verbose:
271             self._logger.info("file: %s", filec)
272
273         return self.get_rfc2889_forwarding_results(filec)
274
275     def send_rfc2889_caching(self, traffic=None, tests=1, _duration=20):
276         """
277         Send as per RFC2889 Addr-Caching test specifications.
278         """
279         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
280         if traffic and 'l2' in traffic:
281             if 'framesize' in traffic['l2']:
282                 framesize = traffic['l2']['framesize']
283         common_args = get_rfc2889_common_settings(framesize, tests,
284                                                   traffic['traffic_type'])
285         custom_args = get_rfc2889_custom_settings()
286         args = common_args + custom_args
287
288         if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
289             args.append("--verbose")
290             verbose = True
291             self._logger.debug("Arguments used to call test: %s", args)
292         subprocess.check_call(args)
293
294         filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
295                              settings.getValue(
296                                  "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
297                              ".csv")
298
299         if verbose:
300             self._logger.info("file: %s", filec)
301
302         return self.get_rfc2889_addr_caching_results(filec)
303
304     def send_rfc2889_learning(self, traffic=None, tests=1, _duration=20):
305         """
306         Send traffic per RFC2889 Addr-Learning test specifications.
307         """
308         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
309         if traffic and 'l2' in traffic:
310             if 'framesize' in traffic['l2']:
311                 framesize = traffic['l2']['framesize']
312         common_args = get_rfc2889_common_settings(framesize, tests,
313                                                   traffic['traffic_type'])
314         custom_args = get_rfc2889_custom_settings()
315         args = common_args + custom_args
316
317         if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
318             args.append("--verbose")
319             verbose = True
320             self._logger.debug("Arguments used to call test: %s", args)
321         subprocess.check_call(args)
322
323         filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
324                              settings.getValue(
325                                  "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
326                              ".csv")
327
328         if verbose:
329             self._logger.info("file: %s", filec)
330
331         return self.get_rfc2889_addr_learning_results(filec)
332
333     def get_rfc2544_results(self, filename):
334         """
335         Reads the CSV file and return the results
336         """
337         result = {}
338         with open(filename, "r") as csvfile:
339             csvreader = csv.DictReader(csvfile)
340             for row in csvreader:
341                 self._logger.info("Row: %s", row)
342                 tx_fps = ((float(row["TxFrameCount"])) /
343                           (float(row["Duration(sec)"])))
344                 rx_fps = ((float(row["RxFrameCount"])) /
345                           (float(row["Duration(sec)"])))
346                 tx_mbps = ((float(row["TxFrameCount"]) *
347                             float(row["ConfiguredFrameSize"])) /
348                            (float(row["Duration(sec)"]) * 1000000.0))
349                 rx_mbps = ((float(row["RxFrameCount"]) *
350                             float(row["ConfiguredFrameSize"])) /
351                            (float(row["Duration(sec)"]) * 1000000.0))
352                 result[ResultsConstants.TX_RATE_FPS] = tx_fps
353                 result[ResultsConstants.THROUGHPUT_RX_FPS] = rx_fps
354                 result[ResultsConstants.TX_RATE_MBPS] = tx_mbps
355                 result[ResultsConstants.THROUGHPUT_RX_MBPS] = rx_mbps
356                 result[ResultsConstants.TX_RATE_PERCENT] = float(
357                     row["OfferedLoad(%)"])
358                 result[ResultsConstants.THROUGHPUT_RX_PERCENT] = float(
359                     row["Throughput(%)"])
360                 result[ResultsConstants.MIN_LATENCY_NS] = float(
361                     row["MinimumLatency(us)"]) * 1000
362                 result[ResultsConstants.MAX_LATENCY_NS] = float(
363                     row["MaximumLatency(us)"]) * 1000
364                 result[ResultsConstants.AVG_LATENCY_NS] = float(
365                     row["AverageLatency(us)"]) * 1000
366                 result[ResultsConstants.FRAME_LOSS_PERCENT] = float(
367                     row["PercentLoss"])
368         return result
369
370     def send_cont_traffic(self, traffic=None, duration=30):
371         """
372         Send Custom - Continuous Test traffic
373         Reuse RFC2544 throughput test specifications along with
374         'custom' configuration
375         """
376         verbose = False
377         custom = "cont"
378         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
379         if traffic and 'l2' in traffic:
380             if 'framesize' in traffic['l2']:
381                 framesize = traffic['l2']['framesize']
382
383         stc_common_args = get_stc_common_settings()
384         rfc2544_common_args = get_rfc2544_common_settings()
385         rfc2544_custom_args = get_rfc2544_custom_settings(framesize,
386                                                           custom, 1)
387         args = rfc2544_common_args + stc_common_args + rfc2544_custom_args
388
389         if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
390             args.append("--verbose")
391             verbose = True
392             self._logger.debug("Arguments used to call test: %s", args)
393         subprocess.check_call(args)
394
395         filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
396                              settings.getValue(
397                                  "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
398                              ".csv")
399
400         if verbose:
401             self._logger.info("file: %s", filec)
402
403         return self.get_rfc2544_results(filec)
404
405     def send_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
406                                 lossrate=0.0):
407         """
408         Send traffic per RFC2544 throughput test specifications.
409         """
410         verbose = False
411         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
412         if traffic and 'l2' in traffic:
413             if 'framesize' in traffic['l2']:
414                 framesize = traffic['l2']['framesize']
415
416         stc_common_args = get_stc_common_settings()
417         rfc2544_common_args = get_rfc2544_common_settings()
418         rfc2544_custom_args = get_rfc2544_custom_settings(framesize, '',
419                                                           tests)
420         args = rfc2544_common_args + stc_common_args + rfc2544_custom_args
421
422         if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
423             args.append("--verbose")
424             verbose = True
425             self._logger.debug("Arguments used to call test: %s", args)
426         subprocess.check_call(args)
427
428         filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
429                              settings.getValue(
430                                  "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
431                              ".csv")
432
433         if verbose:
434             self._logger.info("file: %s", filec)
435
436         return self.get_rfc2544_results(filec)
437
438     def send_rfc2544_back2back(self, traffic=None, tests=1, duration=20,
439                                lossrate=0.0):
440         """
441         Send traffic per RFC2544 BacktoBack test specifications.
442         """
443         verbose = False
444         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
445         if traffic and 'l2' in traffic:
446             if 'framesize' in traffic['l2']:
447                 framesize = traffic['l2']['framesize']
448
449         stc_common_args = get_stc_common_settings()
450         rfc2544_common_args = get_rfc2544_common_settings()
451         rfc2544_custom_args = get_rfc2544_custom_settings(framesize, '',
452                                                           tests)
453         args = rfc2544_common_args + stc_common_args + rfc2544_custom_args
454
455         if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
456             args.append("--verbose")
457             verbose = True
458             self._logger.info("Arguments used to call test: %s", args)
459         subprocess.check_call(args)
460
461         filecs = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
462                               settings.getValue(
463                                   "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
464                               ".csv")
465         if verbose:
466             self._logger.debug("file: %s", filecs)
467
468         return self.get_rfc2544_results(filecs)
469
470     def start_cont_traffic(self, traffic=None, duration=30):
471         raise NotImplementedError('TestCenter start_cont_traffic not implement.')
472
473     def stop_cont_traffic(self):
474         raise NotImplementedError('TestCenter stop_cont_traffic not implement.')
475
476     def start_rfc2544_back2back(self, traffic=None, tests=1, duration=20,
477                                 lossrate=0.0):
478         raise NotImplementedError('TestCenter start_rfc2544_back2back not implement.')
479
480     def wait_rfc2544_back2back(self):
481         raise NotImplementedError('TestCenter wait_rfc2544_back2back not implement.')
482
483     def start_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
484                                  lossrate=0.0):
485         raise NotImplementedError('TestCenter start_rfc2544_throughput not implement.')
486
487     def wait_rfc2544_throughput(self):
488         raise NotImplementedError('TestCenter wait_rfc2544_throughput not implement.')
489
490 if __name__ == '__main__':
491     TRAFFIC = {
492         'l3': {
493             'proto': 'tcp',
494             'srcip': '1.1.1.1',
495             'dstip': '90.90.90.90',
496         },
497     }
498     with TestCenter() as dev:
499         print(dev.send_rfc2544_throughput(traffic=TRAFFIC))
500         # pylint: disable=no-member
501         print(dev.send_rfc2544_backtoback(traffic=TRAFFIC))