Merge "Tools: Improve Stability."
[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             "--vsperf_results_dir",
103             settings.getValue("RESULTS_PATH")]
104     return args
105
106
107 def get_rfc2544_custom_settings(framesize, custom_tr, tests):
108     """
109     Return RFC2544 Custom Settings
110     """
111     args = ["--frame_size_list",
112             str(framesize),
113             "--traffic_custom",
114             str(custom_tr),
115             "--num_trials",
116             str(tests)]
117     return args
118
119
120 def get_rfc2889_common_settings(framesize, tests, metric):
121     """
122     Return RFC2889 common Settings
123     """
124     new_metric = metric.replace('rfc2889_', '')
125     args = [settings.getValue("TRAFFICGEN_STC_PYTHON2_PATH"),
126             os.path.join(
127                 settings.getValue("TRAFFICGEN_STC_TESTCENTER_PATH"),
128                 settings.getValue(
129                     "TRAFFICGEN_STC_RFC2889_TEST_FILE_NAME")),
130             "--lab_server_addr",
131             settings.getValue("TRAFFICGEN_STC_LAB_SERVER_ADDR"),
132             "--license_server_addr",
133             settings.getValue("TRAFFICGEN_STC_LICENSE_SERVER_ADDR"),
134             "--location_list",
135             settings.getValue("TRAFFICGEN_STC_RFC2889_LOCATIONS"),
136             "--test_session_name",
137             settings.getValue("TRAFFICGEN_STC_TEST_SESSION_NAME"),
138             "--results_dir",
139             settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
140             "--csv_results_file_prefix",
141             settings.getValue("TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX"),
142             "--frame_size_list",
143             str(framesize),
144             "--metric",
145             str(new_metric),
146             "--num_trials",
147             str(tests)]
148
149     return args
150
151
152 def get_rfc2889_custom_settings():
153     """
154     Return RFC2889 Custom Settings
155     """
156     args = ["--min_learning_rate",
157             settings.getValue("TRAFFICGEN_STC_RFC2889_MIN_LR"),
158             "--max_learning_rate",
159             settings.getValue("TRAFFICGEN_STC_RFC2889_MAX_LR"),
160             "--min_num_addrs",
161             settings.getValue("TRAFFICGEN_STC_RFC2889_MIN_ADDRS"),
162             "--max_num_addrs",
163             settings.getValue("TRAFFICGEN_STC_RFC2889_MAX_ADDRS"),
164             "--ac_learning_rate",
165             settings.getValue("TRAFFICGEN_STC_RFC2889_AC_LR")]
166     return args
167
168
169 class TestCenter(trafficgen.ITrafficGenerator):
170     """
171     Spirent TestCenter
172     """
173     _logger = logging.getLogger(__name__)
174     _liveresults_file = settings.getValue("TRAFFICGEN_STC_LIVERESULTS_FILE")
175
176     def connect(self):
177         """
178         Do nothing.
179         """
180         return self
181
182     def disconnect(self):
183         """
184         Do nothing.
185         """
186         pass
187
188     def send_burst_traffic(self, traffic=None, duration=20):
189         """
190         Do nothing.
191         """
192         return None
193
194     def get_rfc2889_addr_learning_results(self, filename):
195         """
196         Reads the CSV file and return the results
197         """
198         result = {}
199         with open(filename, "r") as csvfile:
200             csvreader = csv.DictReader(csvfile)
201             for row in csvreader:
202                 self._logger.info("Row: %s", row)
203                 learn_rate = float(row["OptimalLearningRate"])
204                 result[ResultsConstants.OPTIMAL_LEARNING_RATE_FPS] = learn_rate
205         return result
206
207     def get_rfc2889_addr_caching_results(self, filename):
208         """
209         Reads the CSV file and return the results
210         """
211         result = {}
212         with open(filename, "r") as csvfile:
213             csvreader = csv.DictReader(csvfile)
214             for row in csvreader:
215                 self._logger.info("Row: %s", row)
216                 caching_cap = float(row["RxFrameCount"])
217                 learn_per = (100.0 - (float(row["PercentFrameLoss(%)"])))
218                 result[ResultsConstants.CACHING_CAPACITY_ADDRS] = caching_cap
219                 result[ResultsConstants.ADDR_LEARNED_PERCENT] = learn_per
220         return result
221
222     def get_rfc2889_forwarding_results(self, filename):
223         """
224         Reads the CSV file and return the results
225         """
226         result = {}
227         with open(filename, "r") as csvfile:
228             csvreader = csv.DictReader(csvfile)
229             for row in csvreader:
230                 self._logger.info("Row: %s", row)
231                 duration = int((float(row["TxSignatureFrameCount"])) /
232                                (float(row["OfferedLoad(fps)"])))
233                 tx_fps = (float(row["OfferedLoad(fps)"]))
234                 rx_fps = float((float(row["RxFrameCount"])) /
235                                float(duration))
236                 tx_mbps = ((tx_fps * float(row["FrameSize"])) /
237                            (1000000.0))
238                 rx_mbps = ((rx_fps * float(row["FrameSize"])) /
239                            (1000000.0))
240                 result[ResultsConstants.TX_RATE_FPS] = tx_fps
241                 result[ResultsConstants.THROUGHPUT_RX_FPS] = rx_fps
242                 result[ResultsConstants.TX_RATE_MBPS] = tx_mbps
243                 result[ResultsConstants.THROUGHPUT_RX_MBPS] = rx_mbps
244                 result[ResultsConstants.TX_RATE_PERCENT] = float(
245                     row["OfferedLoad(%)"])
246                 result[ResultsConstants.FRAME_LOSS_PERCENT] = float(
247                     row["PercentFrameLoss(%)"])
248                 result[ResultsConstants.FORWARDING_RATE_FPS] = float(
249                     row["ForwardingRate(fps)"])
250         return result
251
252     def send_rfc2889_forwarding(self, traffic=None, tests=1, _duration=20):
253         """
254         Send traffic per RFC2889 Forwarding test specifications.
255         """
256         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
257         if traffic and 'l2' in traffic:
258             if 'framesize' in traffic['l2']:
259                 framesize = traffic['l2']['framesize']
260         args = get_rfc2889_common_settings(framesize, tests,
261                                            traffic['traffic_type'])
262         if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
263             args.append("--verbose")
264             verbose = True
265             self._logger.debug("Arguments used to call test: %s", args)
266         subprocess.check_call(args)
267
268         filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
269                              settings.getValue(
270                                  "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
271                              ".csv")
272
273         if verbose:
274             self._logger.info("file: %s", filec)
275
276         return self.get_rfc2889_forwarding_results(filec)
277
278     def send_rfc2889_caching(self, traffic=None, tests=1, _duration=20):
279         """
280         Send as per RFC2889 Addr-Caching test specifications.
281         """
282         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
283         if traffic and 'l2' in traffic:
284             if 'framesize' in traffic['l2']:
285                 framesize = traffic['l2']['framesize']
286         common_args = get_rfc2889_common_settings(framesize, tests,
287                                                   traffic['traffic_type'])
288         custom_args = get_rfc2889_custom_settings()
289         args = common_args + custom_args
290
291         if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
292             args.append("--verbose")
293             verbose = True
294             self._logger.debug("Arguments used to call test: %s", args)
295         subprocess.check_call(args)
296
297         filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
298                              settings.getValue(
299                                  "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
300                              ".csv")
301
302         if verbose:
303             self._logger.info("file: %s", filec)
304
305         return self.get_rfc2889_addr_caching_results(filec)
306
307     def send_rfc2889_learning(self, traffic=None, tests=1, _duration=20):
308         """
309         Send traffic per RFC2889 Addr-Learning test specifications.
310         """
311         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
312         if traffic and 'l2' in traffic:
313             if 'framesize' in traffic['l2']:
314                 framesize = traffic['l2']['framesize']
315         common_args = get_rfc2889_common_settings(framesize, tests,
316                                                   traffic['traffic_type'])
317         custom_args = get_rfc2889_custom_settings()
318         args = common_args + custom_args
319
320         if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
321             args.append("--verbose")
322             verbose = True
323             self._logger.debug("Arguments used to call test: %s", args)
324         subprocess.check_call(args)
325
326         filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
327                              settings.getValue(
328                                  "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
329                              ".csv")
330
331         if verbose:
332             self._logger.info("file: %s", filec)
333
334         return self.get_rfc2889_addr_learning_results(filec)
335
336     def get_rfc2544_results(self, filename, genome=None):
337         """
338         Reads the CSV file and return the results
339         """
340         result = {}
341         if not os.path.exists(filename):
342             return result
343         with open(filename, "r") as csvfile:
344             csvreader = csv.DictReader(csvfile)
345             for row in csvreader:
346                 self._logger.info("Row: %s", row)
347                 tx_fps = ((float(row["TxFrameCount"])) /
348                           (float(row["Duration(sec)"])))
349                 rx_fps = ((float(row["RxFrameCount"])) /
350                           (float(row["Duration(sec)"])))
351                 tx_mbps = ((float(row["TxFrameCount"]) *
352                             float(row["ConfiguredFrameSize"])) /
353                            (float(row["Duration(sec)"]) * 1000000.0))
354                 rx_mbps = ((float(row["RxFrameCount"]) *
355                             float(row["ConfiguredFrameSize"])) /
356                            (float(row["Duration(sec)"]) * 1000000.0))
357                 result[ResultsConstants.TX_RATE_FPS] = tx_fps
358                 result[ResultsConstants.THROUGHPUT_RX_FPS] = rx_fps
359                 result[ResultsConstants.TX_RATE_MBPS] = tx_mbps
360                 result[ResultsConstants.THROUGHPUT_RX_MBPS] = rx_mbps
361                 result[ResultsConstants.TX_RATE_PERCENT] = float(
362                     row["OfferedLoad(%)"])
363                 result[ResultsConstants.THROUGHPUT_RX_PERCENT] = float(
364                     row["Throughput(%)"])
365                 result[ResultsConstants.MIN_LATENCY_NS] = float(
366                     row["MinimumLatency(us)"]) * 1000
367                 result[ResultsConstants.MAX_LATENCY_NS] = float(
368                     row["MaximumLatency(us)"]) * 1000
369                 result[ResultsConstants.AVG_LATENCY_NS] = float(
370                     row["AverageLatency(us)"]) * 1000
371                 result[ResultsConstants.FRAME_LOSS_PERCENT] = float(
372                     row["PercentLoss"])
373                 if genome:
374                     result[ResultsConstants.IMIX_GENOME] = genome
375                     result[ResultsConstants.IMIX_AVG_FRAMESIZE] = float(
376                         row["AvgFrameSize"])
377         return result
378
379     def send_cont_traffic(self, traffic=None, duration=30):
380         """
381         Send Custom - Continuous Test traffic
382         Reuse RFC2544 throughput test specifications along with
383         'custom' configuration
384         """
385         verbose = False
386         custom = "cont"
387         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
388         if traffic and 'l2' in traffic:
389             if 'framesize' in traffic['l2']:
390                 framesize = traffic['l2']['framesize']
391
392         stc_common_args = get_stc_common_settings()
393         rfc2544_common_args = get_rfc2544_common_settings()
394         rfc2544_custom_args = get_rfc2544_custom_settings(framesize,
395                                                           custom, 1)
396         args = rfc2544_common_args + stc_common_args + rfc2544_custom_args
397
398         if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
399             args.append("--verbose")
400             verbose = True
401             self._logger.debug("Arguments used to call test: %s", args)
402         subprocess.check_call(args)
403
404         filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
405                              settings.getValue(
406                                  "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
407                              ".csv")
408
409         if verbose:
410             self._logger.info("file: %s", filec)
411
412         return self.get_rfc2544_results(filec)
413
414     def send_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
415                                 lossrate=0.0):
416         """
417         Send traffic per RFC2544 throughput test specifications.
418         """
419         verbose = False
420         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
421         if traffic and 'l2' in traffic:
422             if 'framesize' in traffic['l2']:
423                 framesize = traffic['l2']['framesize']
424
425         stc_common_args = get_stc_common_settings()
426         rfc2544_common_args = get_rfc2544_common_settings()
427         rfc2544_custom_args = get_rfc2544_custom_settings(framesize, '',
428                                                           tests)
429         args = rfc2544_common_args + stc_common_args + rfc2544_custom_args
430
431         if traffic and 'latency_histogram' in traffic:
432             if traffic['latency_histogram']['enabled']:
433                 if traffic['latency_histogram']['type'] == 'Default':
434                     args.append("--latency_histogram")
435
436         genome = ''
437         if traffic and 'imix' in traffic:
438             if traffic['imix']['enabled']:
439                 if traffic['imix']['type'] == 'genome':
440                     genome = traffic['imix']['genome']
441                     args.append('--imix')
442                     args.append(genome)
443
444         if settings.getValue("TRAFFICGEN_STC_LIVE_RESULTS") == "True":
445             args.append('--live_results')
446             args.append('--logfile')
447             args.append(self._liveresults_file)
448
449         if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
450             args.append("--verbose")
451             verbose = True
452             self._logger.debug("Arguments used to call test: %s", args)
453         subprocess.check_call(args)
454
455         filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
456                              settings.getValue(
457                                  "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
458                              ".csv")
459
460         if verbose:
461             self._logger.info("file: %s", filec)
462
463         return self.get_rfc2544_results(filec, genome)
464
465     def send_rfc2544_back2back(self, traffic=None, tests=1, duration=20,
466                                lossrate=0.0):
467         """
468         Send traffic per RFC2544 BacktoBack test specifications.
469         """
470         verbose = False
471         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
472         if traffic and 'l2' in traffic:
473             if 'framesize' in traffic['l2']:
474                 framesize = traffic['l2']['framesize']
475
476         stc_common_args = get_stc_common_settings()
477         rfc2544_common_args = get_rfc2544_common_settings()
478         rfc2544_custom_args = get_rfc2544_custom_settings(framesize, '',
479                                                           tests)
480         args = rfc2544_common_args + stc_common_args + rfc2544_custom_args
481
482         if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
483             args.append("--verbose")
484             verbose = True
485             self._logger.info("Arguments used to call test: %s", args)
486         subprocess.check_call(args)
487
488         filecs = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
489                               settings.getValue(
490                                   "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
491                               ".csv")
492         if verbose:
493             self._logger.debug("file: %s", filecs)
494
495         return self.get_rfc2544_results(filecs)
496
497     def start_cont_traffic(self, traffic=None, duration=30):
498         raise NotImplementedError('TestCenter start_cont_traffic not implement.')
499
500     def stop_cont_traffic(self):
501         raise NotImplementedError('TestCenter stop_cont_traffic not implement.')
502
503     def start_rfc2544_back2back(self, traffic=None, tests=1, duration=20,
504                                 lossrate=0.0):
505         raise NotImplementedError('TestCenter start_rfc2544_back2back not implement.')
506
507     def wait_rfc2544_back2back(self):
508         raise NotImplementedError('TestCenter wait_rfc2544_back2back not implement.')
509
510     def start_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
511                                  lossrate=0.0):
512         raise NotImplementedError('TestCenter start_rfc2544_throughput not implement.')
513
514     def wait_rfc2544_throughput(self):
515         raise NotImplementedError('TestCenter wait_rfc2544_throughput not implement.')
516
517 if __name__ == '__main__':
518     TRAFFIC = {
519         'l3': {
520             'proto': 'tcp',
521             'srcip': '1.1.1.1',
522             'dstip': '90.90.90.90',
523         },
524     }
525     with TestCenter() as dev:
526         print(dev.send_rfc2544_throughput(traffic=TRAFFIC))
527         # pylint: disable=no-member
528         print(dev.send_rfc2544_backtoback(traffic=TRAFFIC))