1 # Copyright 2015-2016 Intel Corporation.
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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 """IxNetwork traffic generator model.
16 Provides a model for an IxNetwork machine and appropriate applications.
18 This requires the following settings in your config file:
20 * TRAFFICGEN_IXNET_LIB_PATH
21 IxNetwork libraries path
22 * TRAFFICGEN_IXNET_PORT
23 IxNetwork host port number
24 * TRAFFICGEN_IXNET_USER
25 IxNetwork host user name
26 * TRAFFICGEN_IXNET_TESTER_RESULT_DIR
27 The result directory on the IxNetwork computer
28 * TRAFFICGEN_IXNET_DUT_RESULT_DIR
29 The result directory on DUT. This needs to map to the same directory
32 The following settings are also required. These can likely be shared
33 with an 'Ixia' traffic generator instance:
35 * TRAFFICGEN_IXIA_HOST
36 IXIA chassis IP address
37 * TRAFFICGEN_IXIA_CARD
39 * TRAFFICGEN_IXIA_PORT1
41 * TRAFFICGEN_IXIA_PORT2
44 If any of these don't exist, the application will raise an exception
47 Additional Configuration:
48 -------------------------
50 You will also need to configure the IxNetwork machine to start the IXIA
51 IxNetworkTclServer. This can be started like so:
53 1. Connect to the IxNetwork machine using RDP
60 IxNetwork 7.21.893.14 GA ->
63 Pin a shortcut to this application to the taskbar.
64 3. Before running it right click the pinned shortcut and go to
65 "Properties". Here change the port number to your own port number.
66 This will be the same value as "TRAFFICGEN_IXNET_PORT" above.
67 4. You will find this on the shortcut tab under the heading "Target"
68 5. Finally run it. If you see the following error check that you
69 followed the above steps exactly:
71 ERROR: couldn't open socket : connection refused
76 This method of automation is quite error prone as the IxNetwork API
77 does not give any feedback as to the status of tests. As such, it can
78 be expected that the user have access to the IxNetwork machine should
79 this trafficgen need to be debugged.
88 from collections import OrderedDict
89 from tools.pkt_gen import trafficgen
90 from conf import settings
91 from conf import merge_spec
92 from core.results.results_constants import ResultsConstants
94 _ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
96 _RESULT_RE = r'(?:\{kString,result\},\{kString,)(\w+)(?:\})'
97 _RESULTPATH_RE = r'(?:\{kString,resultPath\},\{kString,)([\\\w\.\-\:]+)(?:\})'
100 def _build_set_cmds(values, prefix='dict set'):
101 """Generate a list of 'dict set' args for Tcl.
103 Parse a dictionary and recursively build the arguments for the
104 'dict set' Tcl command, given that this is of the format:
106 dict set [name...] [key] [value]
108 For example, for a non-nested dict (i.e. a non-dict element):
110 dict set mydict mykey myvalue
112 For a nested dict (i.e. a dict element):
114 dict set mydict mysubdict mykey myvalue
116 :param values: Dictionary to yield values for
117 :param prefix: Prefix to append to output string. Generally the
118 already generated part of the command.
120 :yields: Output strings to be passed to a `Tcl` instance.
125 # Not allowing derived dictionary types for now
126 # pylint: disable=unidiomatic-typecheck
127 if type(value) == dict:
128 _prefix = ' '.join([prefix, key]).strip()
129 for subkey in _build_set_cmds(value, _prefix):
133 # pylint: disable=unidiomatic-typecheck
134 # tcl doesn't recognise the strings "True" or "False", only "1"
135 # or "0". Special case to convert them
136 if type(value) == bool:
137 value = str(int(value))
142 yield ' '.join([prefix, key, value]).strip()
144 yield ' '.join([key, value]).strip()
147 class IxNet(trafficgen.ITrafficGenerator):
148 """A wrapper around IXIA IxNetwork applications.
150 Runs different traffic generator tests through an Ixia traffic
151 generator chassis by generating TCL scripts from templates.
153 Currently only the RFC2544 tests are implemented.
157 """Initialize IXNET members
160 self._script = os.path.join(settings.getValue('TRAFFICGEN_IXIA_3RD_PARTY'),
161 settings.getValue('TRAFFICGEN_IXNET_TCL_SCRIPT'))
162 self._tclsh = tkinter.Tcl()
164 self._logger = logging.getLogger(__name__)
168 def run_tcl(self, cmd):
169 """Run a TCL script using the TCL interpreter found in ``tkinter``.
171 :param cmd: Command to execute
173 :returns: Output of command, where applicable.
175 self._logger.debug('%s%s', trafficgen.CMD_PREFIX, cmd)
177 output = self._tclsh.eval(cmd)
179 return output.split()
182 """Configure system for IxNetwork.
185 'lib_path': settings.getValue('TRAFFICGEN_IXNET_LIB_PATH'),
186 # IxNetwork machine configuration
187 'machine': settings.getValue('TRAFFICGEN_IXNET_MACHINE'),
188 'port': settings.getValue('TRAFFICGEN_IXNET_PORT'),
189 'user': settings.getValue('TRAFFICGEN_IXNET_USER'),
190 # IXIA chassis configuration
191 'chassis': settings.getValue('TRAFFICGEN_IXIA_HOST'),
192 'card': settings.getValue('TRAFFICGEN_IXIA_CARD'),
193 'port1': settings.getValue('TRAFFICGEN_IXIA_PORT1'),
194 'port2': settings.getValue('TRAFFICGEN_IXIA_PORT2'),
196 settings.getValue('TRAFFICGEN_IXNET_TESTER_RESULT_DIR'),
199 self._logger.debug('IXIA configuration configuration : %s', self._cfg)
203 def disconnect(self):
204 """Disconnect from Ixia chassis.
208 def send_cont_traffic(self, traffic=None, duration=30):
209 """See ITrafficGenerator for description
211 self.start_cont_traffic(traffic, duration)
213 return self.stop_cont_traffic()
215 def start_cont_traffic(self, traffic=None, duration=30):
216 """Start transmission.
218 self._bidir = traffic['bidir']
221 self._params['config'] = {
222 'binary': False, # don't do binary search and send one stream
223 'duration': duration,
224 'framerate': traffic['frame_rate'],
225 'multipleStreams': traffic['multistream'],
226 'streamType': traffic['stream_type'],
227 'rfc2544TestType': 'throughput',
229 self._params['traffic'] = self.traffic_defaults.copy()
232 self._params['traffic'] = merge_spec(
233 self._params['traffic'], traffic)
234 self._cfg['bidir'] = self._bidir
236 for cmd in _build_set_cmds(self._cfg, prefix='set'):
239 for cmd in _build_set_cmds(self._params):
242 output = self.run_tcl('source {%s}' % self._script)
244 self._logger.critical(
245 'An error occured when connecting to IxNetwork machine...')
246 raise RuntimeError('Ixia failed to initialise.')
248 self.run_tcl('startRfc2544Test $config $traffic')
250 self._logger.critical(
251 'Failed to start continuous traffic test')
252 raise RuntimeError('Continuous traffic test failed to start.')
254 def stop_cont_traffic(self):
255 """See ITrafficGenerator for description
257 return self._wait_result()
259 def send_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
261 """See ITrafficGenerator for description
263 self.start_rfc2544_throughput(traffic, tests, duration, lossrate)
265 return self.wait_rfc2544_throughput()
267 def start_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
269 """Start transmission.
271 self._bidir = traffic['bidir']
274 self._params['config'] = {
277 'duration': duration,
278 'lossrate': lossrate,
279 'multipleStreams': traffic['multistream'],
280 'streamType': traffic['stream_type'],
281 'rfc2544TestType': 'throughput',
283 self._params['traffic'] = self.traffic_defaults.copy()
286 self._params['traffic'] = merge_spec(
287 self._params['traffic'], traffic)
288 self._cfg['bidir'] = self._bidir
290 for cmd in _build_set_cmds(self._cfg, prefix='set'):
293 for cmd in _build_set_cmds(self._params):
296 output = self.run_tcl('source {%s}' % self._script)
298 self._logger.critical(
299 'An error occured when connecting to IxNetwork machine...')
300 raise RuntimeError('Ixia failed to initialise.')
302 self.run_tcl('startRfc2544Test $config $traffic')
304 self._logger.critical(
305 'Failed to start RFC2544 test')
306 raise RuntimeError('RFC2544 test failed to start.')
308 def wait_rfc2544_throughput(self):
309 """See ITrafficGenerator for description
311 return self._wait_result()
313 def _wait_result(self):
316 def parse_result_string(results):
317 """Get path to results file from output
319 Check for related errors
321 :param results: Text stream from test.
323 :returns: Path to results file.
325 result_status = re.search(_RESULT_RE, results)
326 result_path = re.search(_RESULTPATH_RE, results)
328 if not result_status or not result_path:
329 self._logger.critical(
330 'Could not parse results from IxNetwork machine...')
331 raise ValueError('Failed to parse output.')
333 if result_status.group(1) != 'pass':
334 self._logger.critical(
335 'An error occured when running tests...')
336 raise RuntimeError('Ixia failed to initialise.')
338 # transform path into someting useful
340 path = result_path.group(1).replace('\\', '/')
341 path = os.path.join(path, 'AggregateResults.csv')
343 settings.getValue('TRAFFICGEN_IXNET_TESTER_RESULT_DIR'),
344 settings.getValue('TRAFFICGEN_IXNET_DUT_RESULT_DIR'))
347 def parse_ixnet_rfc_results(path):
348 """Parse CSV output of IxNet RFC2544 test run.
350 :param path: Input file path
352 results = OrderedDict()
354 with open(path, 'r') as in_file:
355 reader = csv.reader(in_file, delimiter=',')
358 #Replace null entries added by Ixia with 0s.
359 row = [entry if len(entry) > 0 else '0' for entry in row]
361 # tx_fps and tx_mps cannot be reliably calculated
362 # as the DUT may be modifying the frame size
366 if bool(results.get(ResultsConstants.THROUGHPUT_RX_FPS)) \
368 prev_percent_rx = 0.0
371 float(results.get(ResultsConstants.THROUGHPUT_RX_FPS))
372 if float(row[5]) >= prev_percent_rx:
373 results[ResultsConstants.TX_RATE_FPS] = tx_fps
374 results[ResultsConstants.THROUGHPUT_RX_FPS] = row[5]
375 results[ResultsConstants.TX_RATE_MBPS] = tx_mbps
376 results[ResultsConstants.THROUGHPUT_RX_MBPS] = row[6]
377 results[ResultsConstants.TX_RATE_PERCENT] = row[3]
378 results[ResultsConstants.THROUGHPUT_RX_PERCENT] = row[4]
379 results[ResultsConstants.FRAME_LOSS_PERCENT] = row[10]
380 results[ResultsConstants.MIN_LATENCY_NS] = row[11]
381 results[ResultsConstants.MAX_LATENCY_NS] = row[12]
382 results[ResultsConstants.AVG_LATENCY_NS] = row[13]
385 output = self.run_tcl('waitForRfc2544Test')
387 # the run_tcl function will return a list with one element. We extract
388 # that one element (a string representation of an IXIA-specific Tcl
389 # datatype), parse it to find the path of the results file then parse
391 return parse_ixnet_rfc_results(parse_result_string(output[0]))
393 def send_rfc2544_back2back(self, traffic=None, tests=1, duration=2,
395 """See ITrafficGenerator for description
397 # NOTE 2 seconds is the recommended duration for a back 2 back
398 # test in RFC2544. 50 trials is the recommended number from the
400 self.start_rfc2544_back2back(traffic, tests, duration, lossrate)
402 return self.wait_rfc2544_back2back()
404 def start_rfc2544_back2back(self, traffic=None, tests=1, duration=2,
406 """Start transmission.
408 self._bidir = traffic['bidir']
411 self._params['config'] = {
414 'duration': duration,
415 'lossrate': lossrate,
416 'multipleStreams': traffic['multistream'],
417 'streamType': traffic['stream_type'],
418 'rfc2544TestType': 'back2back',
420 self._params['traffic'] = self.traffic_defaults.copy()
423 self._params['traffic'] = merge_spec(
424 self._params['traffic'], traffic)
425 self._cfg['bidir'] = self._bidir
427 for cmd in _build_set_cmds(self._cfg, prefix='set'):
430 for cmd in _build_set_cmds(self._params):
433 output = self.run_tcl('source {%s}' % self._script)
435 self._logger.critical(
436 'An error occured when connecting to IxNetwork machine...')
437 raise RuntimeError('Ixia failed to initialise.')
439 self.run_tcl('startRfc2544Test $config $traffic')
441 self._logger.critical(
442 'Failed to start RFC2544 test')
443 raise RuntimeError('RFC2544 test failed to start.')
445 def wait_rfc2544_back2back(self):
448 def parse_result_string(results):
449 """Get path to results file from output
451 Check for related errors
453 :param results: Text stream from test.
455 :returns: Path to results file.
457 result_status = re.search(_RESULT_RE, results)
458 result_path = re.search(_RESULTPATH_RE, results)
460 if not result_status or not result_path:
461 self._logger.critical(
462 'Could not parse results from IxNetwork machine...')
463 raise ValueError('Failed to parse output.')
465 if result_status.group(1) != 'pass':
466 self._logger.critical(
467 'An error occured when running tests...')
468 raise RuntimeError('Ixia failed to initialise.')
470 # transform path into something useful
472 path = result_path.group(1).replace('\\', '/')
473 path = os.path.join(path, 'iteration.csv')
475 settings.getValue('TRAFFICGEN_IXNET_TESTER_RESULT_DIR'),
476 settings.getValue('TRAFFICGEN_IXNET_DUT_RESULT_DIR'))
480 def parse_ixnet_rfc_results(path):
481 """Parse CSV output of IxNet RFC2544 Back2Back test run.
483 :param path: Input file path
485 :returns: Best parsed result from CSV file.
487 results = OrderedDict()
488 results[ResultsConstants.B2B_FRAMES] = 0
489 results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = 100
491 with open(path, 'r') as in_file:
492 reader = csv.reader(in_file, delimiter=',')
495 # if back2back count higher than previously found, store it
496 # Note: row[N] here refers to the Nth column of a row
497 if float(row[14]) <= self._params['config']['lossrate']:
499 int(results[ResultsConstants.B2B_FRAMES]):
500 results[ResultsConstants.B2B_FRAMES] = int(row[12])
501 results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = float(row[14])
505 output = self.run_tcl('waitForRfc2544Test')
507 # the run_tcl function will return a list with one element. We extract
508 # that one element (a string representation of an IXIA-specific Tcl
509 # datatype), parse it to find the path of the results file then parse
512 return parse_ixnet_rfc_results(parse_result_string(output[0]))
515 if __name__ == '__main__':
520 'dstip': '10.1.1.254',
525 print(dev.send_cont_traffic())
526 print(dev.send_rfc2544_throughput())