1 # Copyright (c) 2016-2017 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.
19 from yardstick.common import exceptions
20 from yardstick.common import utils
23 log = logging.getLogger(__name__)
28 PROTO_ETHERNET = 'ethernet'
35 IP_VERSION_4_MASK = '0.0.0.255'
36 IP_VERSION_6_MASK = '0:0:0:0:0:0:0:ff'
38 TRAFFIC_STATUS_STARTED = 'started'
39 TRAFFIC_STATUS_STOPPED = 'stopped'
42 # NOTE(ralonsoh): this pragma will be removed in the last patch of this series
43 class IxNextgen(object): # pragma: no cover
45 PORT_STATS_NAME_MAP = {
46 "stat_name": 'Stat Name',
47 "Frames_Tx": 'Frames Tx.',
48 "Valid_Frames_Rx": 'Valid Frames Rx.',
49 "Frames_Tx_Rate": 'Frames Tx. Rate',
50 "Valid_Frames_Rx_Rate": 'Valid Frames Rx. Rate',
51 "Tx_Rate_Kbps": 'Tx. Rate (Kbps)',
52 "Rx_Rate_Kbps": 'Rx. Rate (Kbps)',
53 "Tx_Rate_Mbps": 'Tx. Rate (Mbps)',
54 "Rx_Rate_Mbps": 'Rx. Rate (Mbps)',
58 "Store-Forward_Avg_latency_ns": 'Store-Forward Avg Latency (ns)',
59 "Store-Forward_Min_latency_ns": 'Store-Forward Min Latency (ns)',
60 "Store-Forward_Max_latency_ns": 'Store-Forward Max Latency (ns)',
64 def get_config(tg_cfg):
67 external_interface = tg_cfg["vdu"][0]["external-interface"]
68 for intf in external_interface:
69 card_port0 = intf["virtual-interface"]["vpci"]
70 card0, port0 = card_port0.split(':')[:2]
75 'machine': tg_cfg["mgmt-interface"]["ip"],
76 'port': tg_cfg["mgmt-interface"]["tg-config"]["tcl_port"],
77 'chassis': tg_cfg["mgmt-interface"]["tg-config"]["ixchassis"],
80 'output_dir': tg_cfg["mgmt-interface"]["tg-config"]["dut_result_dir"],
81 'version': tg_cfg["mgmt-interface"]["tg-config"]["version"],
87 def __init__(self): # pragma: no cover
94 def ixnet(self): # pragma: no cover
97 raise exceptions.IxNetworkClientNotConnected()
99 def _get_config_element_by_flow_group_name(self, flow_group_name):
100 """Get a config element using the flow group name
102 Each named flow group contains one config element (by configuration).
103 According to the documentation, "configElements" is a list and "each
104 item in this list is aligned to the sequential order of your endpoint
107 :param flow_group_name: (str) flow group name; this parameter is
108 always a number (converted to string) starting
110 :return: (str) config element reference ID or None.
112 traffic_item = self.ixnet.getList(self.ixnet.getRoot() + '/traffic',
114 flow_groups = self.ixnet.getList(traffic_item, 'endpointSet')
115 for flow_group in flow_groups:
116 if (str(self.ixnet.getAttribute(flow_group, '-name')) ==
118 return traffic_item + '/configElement:' + flow_group_name
120 def _get_stack_item(self, flow_group_name, protocol_name):
121 """Return the stack item given the flow group name and the proto name
123 :param flow_group_name: (str) flow group name
124 :param protocol_name: (str) protocol name, referred to PROTO_*
126 :return: list of stack item descriptors
128 celement = self._get_config_element_by_flow_group_name(flow_group_name)
130 raise exceptions.IxNetworkFlowNotPresent(
131 flow_group=flow_group_name)
132 stack_items = self.ixnet.getList(celement, 'stack')
133 return [s_i for s_i in stack_items if protocol_name in s_i]
135 def _get_field_in_stack_item(self, stack_item, field_name):
136 """Return the field in a stack item given the name
138 :param stack_item: (str) stack item descriptor
139 :param field_name: (str) field name
140 :return: (str) field descriptor
142 fields = self.ixnet.getList(stack_item, 'field')
143 for field in (field for field in fields if field_name in field):
145 raise exceptions.IxNetworkFieldNotPresentInStackItem(
146 field_name=field_name, stack_item=stack_item)
148 def _get_traffic_state(self):
149 """Get traffic state"""
150 return self.ixnet.getAttribute(self.ixnet.getRoot() + 'traffic',
153 def is_traffic_running(self):
154 """Returns true if traffic state == TRAFFIC_STATUS_STARTED"""
155 return self._get_traffic_state() == TRAFFIC_STATUS_STARTED
157 def is_traffic_stopped(self):
158 """Returns true if traffic state == TRAFFIC_STATUS_STOPPED"""
159 return self._get_traffic_state() == TRAFFIC_STATUS_STOPPED
162 def _parse_framesize(framesize):
163 """Parse "framesize" config param. to return a list of weighted pairs
165 :param framesize: dictionary of frame sizes and weights
166 :return: list of paired frame sizes and weights
168 weighted_range_pairs = []
169 for size, weight in framesize.items():
170 weighted_range_pairs.append(int(size.upper().replace('B', '')))
171 weighted_range_pairs.append(int(weight))
172 return weighted_range_pairs
174 def iter_over_get_lists(self, x1, x2, y2, offset=0):
175 for x in self.ixnet.getList(x1, x2):
176 y_list = self.ixnet.getList(x, y2)
177 for i, y in enumerate(y_list, offset):
180 def connect(self, tg_cfg):
181 self._cfg = self.get_config(tg_cfg)
182 self._ixnet = IxNetwork.IxNet()
184 machine = self._cfg['machine']
185 port = str(self._cfg['port'])
186 version = str(self._cfg['version'])
187 return self.ixnet.connect(machine, '-port', port,
190 def clear_config(self):
191 """Wipe out any possible configuration present in the client"""
192 self.ixnet.execute('newConfig')
194 def assign_ports(self):
195 """Create and assign vports for each physical port defined in config
197 This configuration is present in the IXIA profile file. E.g.:
202 vpci: "2:15" # Card:port
205 local_ip: "152.16.100.20"
206 netmask: "255.255.0.0"
207 local_mac: "00:98:10:64:14:00"
211 chassis_ip = self._cfg['chassis']
212 ports = [(chassis_ip, card, port) for card, port in
213 zip(self._cfg['cards'], self._cfg['ports'])]
215 log.info('Create and assign vports: %s', ports)
217 vport = self.ixnet.add(self.ixnet.getRoot(), 'vport')
219 self.ixnet.execute('assignPorts', [port], [], [vport], True)
221 if self.ixnet.getAttribute(vport, '-state') != 'up':
222 log.warning('Port %s is down', vport)
224 def _create_traffic_item(self):
225 """Create the traffic item to hold the flow groups
227 The traffic item tracking by "Traffic Item" is enabled to retrieve the
230 log.info('Create the traffic item "RFC2544"')
231 traffic_item = self.ixnet.add(self.ixnet.getRoot() + '/traffic',
233 self.ixnet.setMultiAttribute(traffic_item, '-name', 'RFC2544',
234 '-trafficType', 'raw')
237 traffic_item_id = self.ixnet.remapIds(traffic_item)[0]
238 self.ixnet.setAttribute(traffic_item_id + '/tracking',
239 '-trackBy', 'trafficGroupId0')
242 def _create_flow_groups(self):
243 """Create the flow groups between the assigned ports"""
244 traffic_item_id = self.ixnet.getList(self.ixnet.getRoot() + 'traffic',
246 log.info('Create the flow groups')
247 vports = self.ixnet.getList(self.ixnet.getRoot(), 'vport')
248 uplink_ports = vports[::2]
249 downlink_ports = vports[1::2]
251 for up, down in zip(uplink_ports, downlink_ports):
252 log.info('FGs: %s <--> %s', up, down)
253 endpoint_set_1 = self.ixnet.add(traffic_item_id, 'endpointSet')
254 endpoint_set_2 = self.ixnet.add(traffic_item_id, 'endpointSet')
255 self.ixnet.setMultiAttribute(
256 endpoint_set_1, '-name', str(index + 1),
257 '-sources', [up + '/protocols'],
258 '-destinations', [down + '/protocols'])
259 self.ixnet.setMultiAttribute(
260 endpoint_set_2, '-name', str(index + 2),
261 '-sources', [down + '/protocols'],
262 '-destinations', [up + '/protocols'])
266 def _append_procotol_to_stack(self, protocol_name, previous_element):
267 """Append a new element in the packet definition stack"""
268 protocol = (self.ixnet.getRoot() +
269 '/traffic/protocolTemplate:"{}"'.format(protocol_name))
270 self.ixnet.execute('append', previous_element, protocol)
272 def _setup_config_elements(self):
273 """Setup the config elements
275 The traffic item is configured to allow individual configurations per
276 config element. The default frame configuration is applied:
277 Ethernet II: added by default
280 Payload: added by default
281 Ethernet II (Trailer): added by default
284 traffic_item_id = self.ixnet.getList(self.ixnet.getRoot() + 'traffic',
286 log.info('Split the frame rate distribution per config element')
287 config_elements = self.ixnet.getList(traffic_item_id, 'configElement')
288 for config_element in config_elements:
289 self.ixnet.setAttribute(config_element + '/frameRateDistribution',
290 '-portDistribution', 'splitRateEvenly')
291 self.ixnet.setAttribute(config_element + '/frameRateDistribution',
292 '-streamDistribution', 'splitRateEvenly')
294 self._append_procotol_to_stack(
295 PROTO_UDP, config_element + '/stack:"ethernet-1"')
296 self._append_procotol_to_stack(
297 PROTO_IPV4, config_element + '/stack:"ethernet-1"')
299 def create_traffic_model(self):
300 """Create a traffic item and the needed flow groups
302 Each flow group inside the traffic item (only one is present)
303 represents the traffic between two ports:
305 FlowGroup1: port1 -> port2
306 FlowGroup2: port1 <- port2
307 FlowGroup3: port3 -> port4
308 FlowGroup4: port3 <- port4
310 self._create_traffic_item()
311 self._create_flow_groups()
312 self._setup_config_elements()
314 def _update_frame_mac(self, ethernet_descriptor, field, mac_address):
315 """Set the MAC address in a config element stack Ethernet field
317 :param ethernet_descriptor: (str) ethernet descriptor, e.g.:
318 /traffic/trafficItem:1/configElement:1/stack:"ethernet-1"
319 :param field: (str) field name, e.g.: destinationAddress
320 :param mac_address: (str) MAC address
322 field_descriptor = self._get_field_in_stack_item(ethernet_descriptor,
324 self.ixnet.setMultiAttribute(field_descriptor,
325 '-singleValue', mac_address,
326 '-fieldValue', mac_address,
327 '-valueType', 'singleValue')
330 def update_frame(self, traffic):
331 """Update the L2 frame
333 This function updates the L2 frame options:
334 - Traffic type: "continuous", "fixedDuration".
335 - Duration: in case of traffic_type="fixedDuration", amount of seconds
337 - Rate: in frames per seconds or percentage.
338 - Type of rate: "framesPerSecond" ("bitsPerSecond" and
339 "percentLineRate" no used)
340 - Frame size: custom IMIX [1] definition; a list of packet size in
341 bytes and the weight. E.g.:
342 [64, 10, 128, 15, 512, 5]
344 [1] https://en.wikipedia.org/wiki/Internet_Mix
346 :param traffic: list of traffic elements; each traffic element contains
347 the injection parameter for each flow group.
349 for traffic_param in traffic.values():
350 fg_id = str(traffic_param['id'])
351 config_element = self._get_config_element_by_flow_group_name(fg_id)
352 if not config_element:
353 raise exceptions.IxNetworkFlowNotPresent(flow_group=fg_id)
355 type = traffic_param.get('traffic_type', 'fixedDuration')
356 duration = traffic_param.get('duration', 30)
357 rate = traffic_param['iload']
358 weighted_range_pairs = self._parse_framesize(
359 traffic_param['outer_l2']['framesize'])
360 srcmac = str(traffic_param.get('srcmac', '00:00:00:00:00:01'))
361 dstmac = str(traffic_param.get('dstmac', '00:00:00:00:00:02'))
362 # NOTE(ralonsoh): add QinQ tagging when
363 # traffic_param['outer_l2']['QinQ'] exists.
364 # s_vlan = traffic_param['outer_l2']['QinQ']['S-VLAN']
365 # c_vlan = traffic_param['outer_l2']['QinQ']['C-VLAN']
367 self.ixnet.setMultiAttribute(
368 config_element + '/transmissionControl',
369 '-type', type, '-duration', duration)
370 self.ixnet.setMultiAttribute(
371 config_element + '/frameRate',
372 '-rate', rate, '-type', 'framesPerSecond')
373 self.ixnet.setMultiAttribute(
374 config_element + '/frameSize',
375 '-type', 'weightedPairs',
376 '-weightedRangePairs', weighted_range_pairs)
379 self._update_frame_mac(
380 self._get_stack_item(fg_id, PROTO_ETHERNET)[0],
381 'destinationAddress', dstmac)
382 self._update_frame_mac(
383 self._get_stack_item(fg_id, PROTO_ETHERNET)[0],
384 'sourceAddress', srcmac)
386 def _update_ipv4_address(self, ip_descriptor, field, ip_address, seed,
388 """Set the IPv4 address in a config element stack IP field
390 :param ip_descriptor: (str) IP descriptor, e.g.:
391 /traffic/trafficItem:1/configElement:1/stack:"ipv4-2"
392 :param field: (str) field name, e.g.: scrIp, dstIp
393 :param ip_address: (str) IP address
394 :param seed: (int) seed length
395 :param mask: (str) IP address mask
396 :param count: (int) number of random IPs to generate
398 field_descriptor = self._get_field_in_stack_item(ip_descriptor,
400 self.ixnet.setMultiAttribute(field_descriptor,
402 '-fixedBits', ip_address,
404 '-valueType', 'random',
405 '-countValue', count)
408 def update_ip_packet(self, traffic):
409 """Update the IP packet
411 NOTE: Only IPv4 is currently supported.
412 :param traffic: list of traffic elements; each traffic element contains
413 the injection parameter for each flow group.
415 # NOTE(ralonsoh): L4 configuration is not set.
416 for traffic_param in traffic.values():
417 fg_id = str(traffic_param['id'])
418 if not self._get_config_element_by_flow_group_name(fg_id):
419 raise exceptions.IxNetworkFlowNotPresent(flow_group=fg_id)
421 count = traffic_param['outer_l3']['count']
422 srcip4 = str(traffic_param['outer_l3']['srcip4'])
423 dstip4 = str(traffic_param['outer_l3']['dstip4'])
425 self._update_ipv4_address(
426 self._get_stack_item(fg_id, PROTO_IPV4)[0],
427 'srcIp', srcip4, 1, IP_VERSION_4_MASK, count)
428 self._update_ipv4_address(
429 self._get_stack_item(fg_id, PROTO_IPV4)[0],
430 'dstIp', dstip4, 1, IP_VERSION_4_MASK, count)
432 def _build_stats_map(self, view_obj, name_map):
433 return {data_yardstick: self.ixnet.execute(
434 'getColumnValues', view_obj, data_ixia)
435 for data_yardstick, data_ixia in name_map.items()}
437 def get_statistics(self):
438 """Retrieve port and flow statistics
440 "Port Statistics" parameters are stored in self.PORT_STATS_NAME_MAP.
441 "Flow Statistics" parameters are stored in self.LATENCY_NAME_MAP.
443 :return: dictionary with the statistics; the keys of this dictionary
444 are PORT_STATS_NAME_MAP and LATENCY_NAME_MAP keys.
446 port_statistics = '::ixNet::OBJ-/statistics/view:"Port Statistics"'
447 flow_statistics = '::ixNet::OBJ-/statistics/view:"Flow Statistics"'
448 stats = self._build_stats_map(port_statistics,
449 self.PORT_STATS_NAME_MAP)
450 stats.update(self._build_stats_map(flow_statistics,
451 self.LATENCY_NAME_MAP))
454 def start_traffic(self):
455 """Start the traffic injection in the traffic item
457 By configuration, there is only one traffic item. This function returns
458 when the traffic state is TRAFFIC_STATUS_STARTED.
460 traffic_items = self.ixnet.getList('/traffic', 'trafficItem')
461 if self.is_traffic_running():
462 self.ixnet.execute('stop', '/traffic')
463 # pylint: disable=unnecessary-lambda
464 utils.wait_until_true(lambda: self.is_traffic_stopped())
466 self.ixnet.execute('generate', traffic_items)
467 self.ixnet.execute('apply', '/traffic')
468 self.ixnet.execute('start', '/traffic')
469 # pylint: disable=unnecessary-lambda
470 utils.wait_until_true(lambda: self.is_traffic_running())