Merge "trex_learning: Add learning packet option to T-Rex testing"
[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, numpkts=100, 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     # pylint: disable=unused-argument
250     def send_rfc2889_forwarding(self, traffic=None, tests=1, duration=20):
251         """
252         Send traffic per RFC2889 Forwarding test specifications.
253         """
254         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
255         if traffic and 'l2' in traffic:
256             if 'framesize' in traffic['l2']:
257                 framesize = traffic['l2']['framesize']
258         args = get_rfc2889_common_settings(framesize, tests,
259                                            traffic['traffic_type'])
260         if settings.getValue("TRAFFICGEN_STC_VERBOSE") is "True":
261             args.append("--verbose")
262             verbose = True
263             self._logger.debug("Arguments used to call test: %s", args)
264         subprocess.check_call(args)
265
266         filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
267                              settings.getValue(
268                                  "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
269                              ".csv")
270
271         if verbose:
272             self._logger.info("file: %s", filec)
273
274         return self.get_rfc2889_forwarding_results(filec)
275
276     def send_rfc2889_caching(self, traffic=None, tests=1, duration=20):
277         """
278         Send as per RFC2889 Addr-Caching test specifications.
279         """
280         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
281         if traffic and 'l2' in traffic:
282             if 'framesize' in traffic['l2']:
283                 framesize = traffic['l2']['framesize']
284         common_args = get_rfc2889_common_settings(framesize, tests,
285                                                   traffic['traffic_type'])
286         custom_args = get_rfc2889_custom_settings()
287         args = common_args + custom_args
288
289         if settings.getValue("TRAFFICGEN_STC_VERBOSE") is "True":
290             args.append("--verbose")
291             verbose = True
292             self._logger.debug("Arguments used to call test: %s", args)
293         subprocess.check_call(args)
294
295         filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
296                              settings.getValue(
297                                  "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
298                              ".csv")
299
300         if verbose:
301             self._logger.info("file: %s", filec)
302
303         return self.get_rfc2889_addr_caching_results(filec)
304
305     def send_rfc2889_learning(self, traffic=None, tests=1, duration=20):
306         """
307         Send traffic per RFC2889 Addr-Learning test specifications.
308         """
309         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
310         if traffic and 'l2' in traffic:
311             if 'framesize' in traffic['l2']:
312                 framesize = traffic['l2']['framesize']
313         common_args = get_rfc2889_common_settings(framesize, tests,
314                                                   traffic['traffic_type'])
315         custom_args = get_rfc2889_custom_settings()
316         args = common_args + custom_args
317
318         if settings.getValue("TRAFFICGEN_STC_VERBOSE") is "True":
319             args.append("--verbose")
320             verbose = True
321             self._logger.debug("Arguments used to call test: %s", args)
322         subprocess.check_call(args)
323
324         filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
325                              settings.getValue(
326                                  "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
327                              ".csv")
328
329         if verbose:
330             self._logger.info("file: %s", filec)
331
332         return self.get_rfc2889_addr_learning_results(filec)
333
334     def get_rfc2544_results(self, filename):
335         """
336         Reads the CSV file and return the results
337         """
338         result = {}
339         with open(filename, "r") as csvfile:
340             csvreader = csv.DictReader(csvfile)
341             for row in csvreader:
342                 self._logger.info("Row: %s", row)
343                 tx_fps = ((float(row["TxFrameCount"])) /
344                           (float(row["Duration(sec)"])))
345                 rx_fps = ((float(row["RxFrameCount"])) /
346                           (float(row["Duration(sec)"])))
347                 tx_mbps = ((float(row["TxFrameCount"]) *
348                             float(row["ConfiguredFrameSize"])) /
349                            (float(row["Duration(sec)"]) * 1000000.0))
350                 rx_mbps = ((float(row["RxFrameCount"]) *
351                             float(row["ConfiguredFrameSize"])) /
352                            (float(row["Duration(sec)"]) * 1000000.0))
353                 result[ResultsConstants.TX_RATE_FPS] = tx_fps
354                 result[ResultsConstants.THROUGHPUT_RX_FPS] = rx_fps
355                 result[ResultsConstants.TX_RATE_MBPS] = tx_mbps
356                 result[ResultsConstants.THROUGHPUT_RX_MBPS] = rx_mbps
357                 result[ResultsConstants.TX_RATE_PERCENT] = float(
358                     row["OfferedLoad(%)"])
359                 result[ResultsConstants.THROUGHPUT_RX_PERCENT] = float(
360                     row["Throughput(%)"])
361                 result[ResultsConstants.MIN_LATENCY_NS] = float(
362                     row["MinimumLatency(us)"]) * 1000
363                 result[ResultsConstants.MAX_LATENCY_NS] = float(
364                     row["MaximumLatency(us)"]) * 1000
365                 result[ResultsConstants.AVG_LATENCY_NS] = float(
366                     row["AverageLatency(us)"]) * 1000
367                 result[ResultsConstants.FRAME_LOSS_PERCENT] = float(
368                     row["PercentLoss"])
369         return result
370
371     def send_cont_traffic(self, traffic=None, duration=30):
372         """
373         Send Custom - Continuous Test traffic
374         Reuse RFC2544 throughput test specifications along with
375         'custom' configuration
376         """
377         verbose = False
378         custom = "cont"
379         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
380         if traffic and 'l2' in traffic:
381             if 'framesize' in traffic['l2']:
382                 framesize = traffic['l2']['framesize']
383
384         stc_common_args = get_stc_common_settings()
385         rfc2544_common_args = get_rfc2544_common_settings()
386         rfc2544_custom_args = get_rfc2544_custom_settings(framesize,
387                                                           custom, 1)
388         args = rfc2544_common_args + stc_common_args + rfc2544_custom_args
389
390         if settings.getValue("TRAFFICGEN_STC_VERBOSE") is "True":
391             args.append("--verbose")
392             verbose = True
393             self._logger.debug("Arguments used to call test: %s", args)
394         subprocess.check_call(args)
395
396         filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
397                              settings.getValue(
398                                  "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
399                              ".csv")
400
401         if verbose:
402             self._logger.info("file: %s", filec)
403
404         return self.get_rfc2544_results(filec)
405
406     def send_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
407                                 lossrate=0.0):
408         """
409         Send traffic per RFC2544 throughput test specifications.
410         """
411         verbose = False
412         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
413         if traffic and 'l2' in traffic:
414             if 'framesize' in traffic['l2']:
415                 framesize = traffic['l2']['framesize']
416
417         stc_common_args = get_stc_common_settings()
418         rfc2544_common_args = get_rfc2544_common_settings()
419         rfc2544_custom_args = get_rfc2544_custom_settings(framesize, '',
420                                                           tests)
421         args = rfc2544_common_args + stc_common_args + rfc2544_custom_args
422
423         if settings.getValue("TRAFFICGEN_STC_VERBOSE") is "True":
424             args.append("--verbose")
425             verbose = True
426             self._logger.debug("Arguments used to call test: %s", args)
427         subprocess.check_call(args)
428
429         filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
430                              settings.getValue(
431                                  "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
432                              ".csv")
433
434         if verbose:
435             self._logger.info("file: %s", filec)
436
437         return self.get_rfc2544_results(filec)
438
439     def send_rfc2544_back2back(self, traffic=None, tests=1, duration=20,
440                                lossrate=0.0):
441         """
442         Send traffic per RFC2544 BacktoBack test specifications.
443         """
444         verbose = False
445         framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
446         if traffic and 'l2' in traffic:
447             if 'framesize' in traffic['l2']:
448                 framesize = traffic['l2']['framesize']
449
450         stc_common_args = get_stc_common_settings()
451         rfc2544_common_args = get_rfc2544_common_settings()
452         rfc2544_custom_args = get_rfc2544_custom_settings(framesize, '',
453                                                           tests)
454         args = rfc2544_common_args + stc_common_args + rfc2544_custom_args
455
456         if settings.getValue("TRAFFICGEN_STC_VERBOSE") is "True":
457             args.append("--verbose")
458             verbose = True
459             self._logger.info("Arguments used to call test: %s", args)
460         subprocess.check_call(args)
461
462         filecs = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
463                               settings.getValue(
464                                   "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
465                               ".csv")
466         if verbose:
467             self._logger.debug("file: %s", filecs)
468
469         return self.get_rfc2544_results(filecs)
470
471     def start_cont_traffic(self, traffic=None, duration=30):
472         raise NotImplementedError('TestCenter start_cont_traffic not implement.')
473
474     def stop_cont_traffic(self):
475         raise NotImplementedError('TestCenter stop_cont_traffic not implement.')
476
477     def start_rfc2544_back2back(self, traffic=None, tests=1, duration=20,
478                                 lossrate=0.0):
479         raise NotImplementedError('TestCenter start_rfc2544_back2back not implement.')
480
481     def wait_rfc2544_back2back(self):
482         raise NotImplementedError('TestCenter wait_rfc2544_back2back not implement.')
483
484     def start_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
485                                  lossrate=0.0):
486         raise NotImplementedError('TestCenter start_rfc2544_throughput not implement.')
487
488     def wait_rfc2544_throughput(self):
489         raise NotImplementedError('TestCenter wait_rfc2544_throughput not implement.')
490
491 if __name__ == '__main__':
492     TRAFFIC = {
493         'l3': {
494             'proto': 'tcp',
495             'srcip': '1.1.1.1',
496             'dstip': '90.90.90.90',
497         },
498     }
499     with TestCenter() as dev:
500         print(dev.send_rfc2544_throughput(traffic=TRAFFIC))
501         print(dev.send_rfc2544_backtoback(traffic=TRAFFIC))