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 """IXIA traffic generator model.
16 Provides a model for the IXIA traffic generator. In addition, provides
17 a number of generic "helper" functions that are used to do the "heavy
20 This requires the following settings in your config file:
22 * TRAFFICGEN_IXIA_ROOT_DIR
24 * TRAFFICGEN_IXIA_HOST
25 IXIA chassis IP address
26 * TRAFFICGEN_IXIA_CARD
28 * TRAFFICGEN_IXIA_PORT1
30 * TRAFFICGEN_IXIA_PORT2
33 If any of these don't exist, the application will raise an exception
41 from collections import OrderedDict
42 from tools import systeminfo
43 from tools.pkt_gen import trafficgen
44 from conf import settings
45 from core.results.results_constants import ResultsConstants
47 _ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
48 _IXIA_ROOT_DIR = settings.getValue('TRAFFICGEN_IXIA_ROOT_DIR')
52 """Configure envionment for TCL.
55 os.environ['IXIA_HOME'] = _IXIA_ROOT_DIR
57 # USER MAY NEED TO CHANGE THESE IF USING OWN TCL LIBRARY
58 os.environ['TCL_HOME'] = _IXIA_ROOT_DIR
59 os.environ['TCLver'] = '8.5'
61 # USER NORMALLY DOES NOT CHANGE ANY LINES BELOW
62 os.environ['IxiaLibPath'] = os.path.expandvars('$IXIA_HOME/lib')
63 os.environ['IxiaBinPath'] = os.path.expandvars('$IXIA_HOME/bin')
65 os.environ['TCLLibPath'] = os.path.expandvars('$TCL_HOME/lib')
66 os.environ['TCLBinPath'] = os.path.expandvars('$TCL_HOME/bin')
68 os.environ['TCL_LIBRARY'] = os.path.expandvars('$TCLLibPath/tcl$TCLver')
69 os.environ['TK_LIBRARY'] = os.path.expandvars('$TCLLibPath/tk$TCLver')
71 os.environ['PATH'] = os.path.expandvars('$IxiaBinPath:.:$TCLBinPath:$PATH')
72 os.environ['TCLLIBPATH'] = os.path.expandvars('$IxiaLibPath')
73 os.environ['LD_LIBRARY_PATH'] = os.path.expandvars(
74 '$IxiaLibPath:$TCLLibPath:$LD_LIBRARY_PATH')
76 os.environ['IXIA_RESULTS_DIR'] = '/tmp/Ixia/Results'
77 os.environ['IXIA_LOGS_DIR'] = '/tmp/Ixia/Logs'
78 os.environ['IXIA_TCL_DIR'] = os.path.expandvars('$IxiaLibPath')
79 os.environ['IXIA_SAMPLES'] = os.path.expandvars('$IxiaLibPath/ixTcl1.0')
80 os.environ['IXIA_VERSION'] = systeminfo.get_version('Ixia')['version']
83 def _build_set_cmds(values, prefix='dict set'):
84 """Generate a list of 'dict set' args for Tcl.
86 Parse a dictionary and recursively build the arguments for the
87 'dict set' Tcl command, given that this is of the format:
89 dict set [name...] [key] [value]
91 For example, for a non-nested dict (i.e. a non-dict element):
93 dict set mydict mykey myvalue
95 For a nested dict (i.e. a dict element):
97 dict set mydict mysubdict mykey myvalue
99 :param values: Dictionary to yield values for
100 :param prefix: Prefix to append to output string. Generally the
101 already generated part of the command.
103 :yields: Output strings to be passed to a `Tcl` instance.
108 # Not allowing derived dictionary types for now
109 # pylint: disable=unidiomatic-typecheck
110 if type(value) == dict:
111 _prefix = ' '.join([prefix, key]).strip()
112 for subkey in _build_set_cmds(value, _prefix):
116 # tcl doesn't recognise the strings "True" or "False", only "1"
117 # or "0". Special case to convert them
118 if type(value) == bool:
119 value = str(int(value))
124 yield ' '.join([prefix, key, value]).strip()
126 yield ' '.join([key, value]).strip()
129 class Ixia(trafficgen.ITrafficGenerator):
130 """A wrapper around the IXIA traffic generator.
132 Runs different traffic generator tests through an Ixia traffic
133 generator chassis by generating TCL scripts from templates.
135 _script = os.path.join(settings.getValue('TRAFFICGEN_IXIA_3RD_PARTY'),
137 _tclsh = tkinter.Tcl()
138 _logger = logging.getLogger(__name__)
140 def run_tcl(self, cmd):
141 """Run a TCL script using the TCL interpreter found in ``tkinter``.
143 :param cmd: Command to execute
145 :returns: Output of command, where applicable.
147 self._logger.debug('%s%s', trafficgen.CMD_PREFIX, cmd)
149 output = self._tclsh.eval(cmd)
151 return output.split()
154 """Connect to Ixia chassis.
157 'lib_path': os.path.join(_IXIA_ROOT_DIR, 'lib', 'ixTcl1.0'),
158 'host': settings.getValue('TRAFFICGEN_IXIA_HOST'),
159 'card': settings.getValue('TRAFFICGEN_IXIA_CARD'),
160 'port1': settings.getValue('TRAFFICGEN_IXIA_PORT1'),
161 'port2': settings.getValue('TRAFFICGEN_IXIA_PORT2'),
164 self._logger.info('Connecting to IXIA...')
166 self._logger.debug('IXIA configuration configuration : %s', ixia_cfg)
170 for cmd in _build_set_cmds(ixia_cfg, prefix='set'):
173 output = self.run_tcl('source {%s}' % self._script)
175 self._logger.critical(
176 'An error occured when connecting to IXIA...')
177 raise RuntimeError('Ixia failed to initialise.')
179 self._logger.info('Connected to IXIA...')
183 def disconnect(self):
184 """Disconnect from Ixia chassis.
186 self._logger.info('Disconnecting from IXIA...')
188 self.run_tcl('cleanUp')
190 self._logger.info('Disconnected from IXIA...')
192 def _send_traffic(self, flow, traffic):
193 """Send regular traffic.
195 :param flow: Flow specification
196 :param traffic: Traffic specification
198 :returns: Results from IXIA
202 params['flow'] = flow
203 params['traffic'] = self.traffic_defaults.copy()
206 params['traffic'] = trafficgen.merge_spec(
207 params['traffic'], traffic)
209 for cmd in _build_set_cmds(params):
212 result = self.run_tcl('sendTraffic $flow $traffic')
216 def send_burst_traffic(self, traffic=None, numpkts=100, duration=20):
217 """See ITrafficGenerator for description
221 'duration': duration,
222 'type': 'stopStream',
223 'framerate': traffic['frame_rate'],
226 result = self._send_traffic(flow, traffic)
228 assert len(result) == 6 # fail-fast if underlying Tcl code changes
230 #TODO - implement Burst results setting via TrafficgenResults.
232 def send_cont_traffic(self, traffic=None, duration=30):
233 """See ITrafficGenerator for description
237 'duration': duration,
238 'type': 'contPacket',
239 'framerate': traffic['frame_rate'],
240 'multipleStreams': traffic['multistream'],
243 result = self._send_traffic(flow, traffic)
245 return Ixia._create_result(result)
247 def start_cont_traffic(self, traffic=None, duration=30):
248 """See ITrafficGenerator for description
250 return self.send_cont_traffic(traffic, 0)
252 def stop_cont_traffic(self):
253 """See ITrafficGenerator for description
255 return self.run_tcl('stopTraffic')
257 def send_rfc2544_throughput(self, traffic=None, tests=1, duration=20, lossrate=0.0):
258 """See ITrafficGenerator for description
264 'duration': duration,
265 'lossrate': lossrate,
266 'multipleStreams': traffic['multistream'],
268 params['traffic'] = self.traffic_defaults.copy()
271 params['traffic'] = trafficgen.merge_spec(
272 params['traffic'], traffic)
274 for cmd in _build_set_cmds(params):
277 # this will return a list with one result
278 result = self.run_tcl('rfcThroughputTest $config $traffic')
280 return Ixia._create_result(result)
283 def _create_result(result):
284 """Create result based on list returned from tcl script.
286 :param result: list representing output from tcl script.
288 :returns: dictionary strings representing results from
291 assert len(result) == 8 # fail-fast if underlying Tcl code changes
293 if float(result[0]) == 0:
296 loss_rate = (float(result[0]) - float(result[1])) / float(result[0]) * 100
297 result_dict = OrderedDict()
298 # drop the first 4 elements as we don't use/need them. In
299 # addition, IxExplorer does not support latency or % line rate
300 # metrics so we have to return dummy values for these metrics
301 result_dict[ResultsConstants.THROUGHPUT_RX_FPS] = result[4]
302 result_dict[ResultsConstants.TX_RATE_FPS] = result[5]
303 result_dict[ResultsConstants.THROUGHPUT_RX_MBPS] = str(round(int(result[6]) / 1000000, 3))
304 result_dict[ResultsConstants.TX_RATE_MBPS] = str(round(int(result[7]) / 1000000, 3))
305 result_dict[ResultsConstants.FRAME_LOSS_PERCENT] = loss_rate
306 result_dict[ResultsConstants.TX_RATE_PERCENT] = \
307 ResultsConstants.UNKNOWN_VALUE
308 result_dict[ResultsConstants.THROUGHPUT_RX_PERCENT] = \
309 ResultsConstants.UNKNOWN_VALUE
310 result_dict[ResultsConstants.MIN_LATENCY_NS] = \
311 ResultsConstants.UNKNOWN_VALUE
312 result_dict[ResultsConstants.MAX_LATENCY_NS] = \
313 ResultsConstants.UNKNOWN_VALUE
314 result_dict[ResultsConstants.AVG_LATENCY_NS] = \
315 ResultsConstants.UNKNOWN_VALUE
319 if __name__ == '__main__':
324 'dstip': '10.1.1.254',
329 print(dev.send_burst_traffic(traffic=TRAFFIC))
330 print(dev.send_cont_traffic(traffic=TRAFFIC))
331 print(dev.send_rfc2544_throughput(traffic=TRAFFIC))