Merge changes from topics 'YARDSTICK-1354', 'YARDSTICK-1348', 'YARDSTICK-1359', ...
[yardstick.git] / yardstick / network_services / libs / ixia_libs / ixnet / ixnet_api.py
1 # Copyright (c) 2016-2017 Intel Corporation
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 import ipaddress
16 import logging
17
18 import IxNetwork
19
20 from yardstick.common import exceptions
21 from yardstick.common import utils
22 from yardstick.network_services.traffic_profile import base as tp_base
23
24
25 log = logging.getLogger(__name__)
26
27 IP_VERSION_4 = 4
28 IP_VERSION_6 = 6
29
30 PROTO_ETHERNET = 'ethernet'
31 PROTO_IPV4 = 'ipv4'
32 PROTO_IPV6 = 'ipv6'
33 PROTO_UDP = 'udp'
34 PROTO_TCP = 'tcp'
35 PROTO_VLAN = 'vlan'
36
37 SINGLE_VALUE = "singleValue"
38
39 S_VLAN = 0
40 C_VLAN = 1
41
42 ETHER_TYPE_802_1ad = '0x88a8'
43
44 IP_VERSION_4_MASK = 24
45 IP_VERSION_6_MASK = 64
46
47 TRAFFIC_STATUS_STARTED = 'started'
48 TRAFFIC_STATUS_STOPPED = 'stopped'
49
50 SUPPORTED_PROTO = [PROTO_UDP]
51
52
53 class Vlan(object):
54     def __init__(self,
55                  vlan_id, vlan_id_step=None, vlan_id_direction='increment',
56                  prio=None, prio_step=None, prio_direction='increment',
57                  tp_id=None):
58         self.vlan_id = vlan_id
59         self.vlan_id_step = vlan_id_step
60         self.vlan_id_direction = vlan_id_direction
61         self.prio = prio
62         self.prio_step = prio_step
63         self.prio_direction = prio_direction
64         self.tp_id = tp_id
65
66
67 # NOTE(ralonsoh): this pragma will be removed in the last patch of this series
68 class IxNextgen(object):  # pragma: no cover
69
70     PORT_STATS_NAME_MAP = {
71         "stat_name": 'Stat Name',
72         "Frames_Tx": 'Frames Tx.',
73         "Valid_Frames_Rx": 'Valid Frames Rx.',
74         "Frames_Tx_Rate": 'Frames Tx. Rate',
75         "Valid_Frames_Rx_Rate": 'Valid Frames Rx. Rate',
76         "Tx_Rate_Kbps": 'Tx. Rate (Kbps)',
77         "Rx_Rate_Kbps": 'Rx. Rate (Kbps)',
78         "Tx_Rate_Mbps": 'Tx. Rate (Mbps)',
79         "Rx_Rate_Mbps": 'Rx. Rate (Mbps)',
80     }
81
82     LATENCY_NAME_MAP = {
83         "Store-Forward_Avg_latency_ns": 'Store-Forward Avg Latency (ns)',
84         "Store-Forward_Min_latency_ns": 'Store-Forward Min Latency (ns)',
85         "Store-Forward_Max_latency_ns": 'Store-Forward Max Latency (ns)',
86     }
87
88     @staticmethod
89     def get_config(tg_cfg):
90         card = []
91         port = []
92         external_interface = tg_cfg["vdu"][0]["external-interface"]
93         for intf in external_interface:
94             card_port0 = intf["virtual-interface"]["vpci"]
95             card0, port0 = card_port0.split(':')[:2]
96             card.append(card0)
97             port.append(port0)
98
99         cfg = {
100             'machine': tg_cfg["mgmt-interface"]["ip"],
101             'port': tg_cfg["mgmt-interface"]["tg-config"]["tcl_port"],
102             'chassis': tg_cfg["mgmt-interface"]["tg-config"]["ixchassis"],
103             'cards': card,
104             'ports': port,
105             'output_dir': tg_cfg["mgmt-interface"]["tg-config"]["dut_result_dir"],
106             'version': tg_cfg["mgmt-interface"]["tg-config"]["version"],
107             'bidir': True,
108         }
109
110         return cfg
111
112     def __init__(self):  # pragma: no cover
113         self._ixnet = None
114         self._cfg = None
115         self._params = None
116         self._bidir = None
117
118     @property
119     def ixnet(self):  # pragma: no cover
120         if self._ixnet:
121             return self._ixnet
122         raise exceptions.IxNetworkClientNotConnected()
123
124     def _get_config_element_by_flow_group_name(self, flow_group_name):
125         """Get a config element using the flow group name
126
127         Each named flow group contains one config element (by configuration).
128         According to the documentation, "configElements" is a list and "each
129         item in this list is aligned to the sequential order of your endpoint
130         list".
131
132         :param flow_group_name: (str) flow group name; this parameter is
133                                 always a number (converted to string) starting
134                                 from "1".
135         :return: (str) config element reference ID or None.
136         """
137         traffic_item = self.ixnet.getList(self.ixnet.getRoot() + '/traffic',
138                                           'trafficItem')[0]
139         flow_groups = self.ixnet.getList(traffic_item, 'endpointSet')
140         for flow_group in flow_groups:
141             if (str(self.ixnet.getAttribute(flow_group, '-name')) ==
142                     flow_group_name):
143                 return traffic_item + '/configElement:' + flow_group_name
144
145     def _get_stack_item(self, flow_group_name, protocol_name):
146         """Return the stack item given the flow group name and the proto name
147
148         :param flow_group_name: (str) flow group name
149         :param protocol_name: (str) protocol name, referred to PROTO_*
150                               constants
151         :return: list of stack item descriptors
152         """
153         celement = self._get_config_element_by_flow_group_name(flow_group_name)
154         if not celement:
155             raise exceptions.IxNetworkFlowNotPresent(
156                 flow_group=flow_group_name)
157         stack_items = self.ixnet.getList(celement, 'stack')
158         return [s_i for s_i in stack_items if protocol_name in s_i]
159
160     def _get_field_in_stack_item(self, stack_item, field_name):
161         """Return the field in a stack item given the name
162
163         :param stack_item: (str) stack item descriptor
164         :param field_name: (str) field name
165         :return: (str) field descriptor
166         """
167         fields = self.ixnet.getList(stack_item, 'field')
168         for field in (field for field in fields if field_name in field):
169             return field
170         raise exceptions.IxNetworkFieldNotPresentInStackItem(
171             field_name=field_name, stack_item=stack_item)
172
173     def _get_traffic_state(self):
174         """Get traffic state"""
175         return self.ixnet.getAttribute(self.ixnet.getRoot() + 'traffic',
176                                        '-state')
177
178     def is_traffic_running(self):
179         """Returns true if traffic state == TRAFFIC_STATUS_STARTED"""
180         return self._get_traffic_state() == TRAFFIC_STATUS_STARTED
181
182     def is_traffic_stopped(self):
183         """Returns true if traffic state == TRAFFIC_STATUS_STOPPED"""
184         return self._get_traffic_state() == TRAFFIC_STATUS_STOPPED
185
186     @staticmethod
187     def _parse_framesize(framesize):
188         """Parse "framesize" config param. to return a list of weighted pairs
189
190         :param framesize: dictionary of frame sizes and weights
191         :return: list of paired frame sizes and weights
192         """
193         weighted_range_pairs = []
194         for size, weight in ((s, w) for (s, w) in framesize.items()
195                              if int(w) != 0):
196             size = int(size.upper().replace('B', ''))
197             weighted_range_pairs.append([size, size, int(weight)])
198         return weighted_range_pairs
199
200     def iter_over_get_lists(self, x1, x2, y2, offset=0):
201         for x in self.ixnet.getList(x1, x2):
202             y_list = self.ixnet.getList(x, y2)
203             for i, y in enumerate(y_list, offset):
204                 yield x, y, i
205
206     def connect(self, tg_cfg):
207         self._cfg = self.get_config(tg_cfg)
208         self._ixnet = IxNetwork.IxNet()
209
210         machine = self._cfg['machine']
211         port = str(self._cfg['port'])
212         version = str(self._cfg['version'])
213         return self.ixnet.connect(machine, '-port', port,
214                                   '-version', version)
215
216     def clear_config(self):
217         """Wipe out any possible configuration present in the client"""
218         self.ixnet.execute('newConfig')
219
220     def assign_ports(self):
221         """Create and assign vports for each physical port defined in config
222
223         This configuration is present in the IXIA profile file. E.g.:
224             name: trafficgen_1
225             role: IxNet
226             interfaces:
227                 xe0:
228                     vpci: "2:15" # Card:port
229                     driver: "none"
230                     dpdk_port_num: 0
231                     local_ip: "152.16.100.20"
232                     netmask: "255.255.0.0"
233                     local_mac: "00:98:10:64:14:00"
234                 xe1:
235                     ...
236         """
237         chassis_ip = self._cfg['chassis']
238         ports = [(chassis_ip, card, port) for card, port in
239                  zip(self._cfg['cards'], self._cfg['ports'])]
240
241         log.info('Create and assign vports: %s', ports)
242
243         vports = []
244         for _ in ports:
245             vports.append(self.ixnet.add(self.ixnet.getRoot(), 'vport'))
246             self.ixnet.commit()
247
248         self.ixnet.execute('assignPorts', ports, [], vports, True)
249         self.ixnet.commit()
250
251         for vport in vports:
252             if self.ixnet.getAttribute(vport, '-state') != 'up':
253                 log.warning('Port %s is down', vport)
254
255     def _create_traffic_item(self):
256         """Create the traffic item to hold the flow groups
257
258         The traffic item tracking by "Traffic Item" is enabled to retrieve the
259         latency statistics.
260         """
261         log.info('Create the traffic item "RFC2544"')
262         traffic_item = self.ixnet.add(self.ixnet.getRoot() + '/traffic',
263                                       'trafficItem')
264         self.ixnet.setMultiAttribute(traffic_item, '-name', 'RFC2544',
265                                      '-trafficType', 'raw')
266         self.ixnet.commit()
267
268         traffic_item_id = self.ixnet.remapIds(traffic_item)[0]
269         self.ixnet.setAttribute(traffic_item_id + '/tracking',
270                                 '-trackBy', 'trafficGroupId0')
271         self.ixnet.commit()
272
273     def _create_flow_groups(self):
274         """Create the flow groups between the assigned ports"""
275         traffic_item_id = self.ixnet.getList(self.ixnet.getRoot() + 'traffic',
276                                              'trafficItem')[0]
277         log.info('Create the flow groups')
278         vports = self.ixnet.getList(self.ixnet.getRoot(), 'vport')
279         uplink_ports = vports[::2]
280         downlink_ports = vports[1::2]
281         index = 0
282         for up, down in zip(uplink_ports, downlink_ports):
283             log.info('FGs: %s <--> %s', up, down)
284             endpoint_set_1 = self.ixnet.add(traffic_item_id, 'endpointSet')
285             endpoint_set_2 = self.ixnet.add(traffic_item_id, 'endpointSet')
286             self.ixnet.setMultiAttribute(
287                 endpoint_set_1, '-name', str(index + 1),
288                 '-sources', [up + '/protocols'],
289                 '-destinations', [down + '/protocols'])
290             self.ixnet.setMultiAttribute(
291                 endpoint_set_2, '-name', str(index + 2),
292                 '-sources', [down + '/protocols'],
293                 '-destinations', [up + '/protocols'])
294             self.ixnet.commit()
295             index += 2
296
297     def _append_procotol_to_stack(self, protocol_name, previous_element):
298         """Append a new element in the packet definition stack"""
299         protocol = (self.ixnet.getRoot() +
300                     '/traffic/protocolTemplate:"{}"'.format(protocol_name))
301         self.ixnet.execute('append', previous_element, protocol)
302
303     def _setup_config_elements(self):
304         """Setup the config elements
305
306         The traffic item is configured to allow individual configurations per
307         config element. The default frame configuration is applied:
308             Ethernet II: added by default
309             IPv4: element to add
310             UDP: element to add
311             Payload: added by default
312             Ethernet II (Trailer): added by default
313         :return:
314         """
315         traffic_item_id = self.ixnet.getList(self.ixnet.getRoot() + 'traffic',
316                                              'trafficItem')[0]
317         log.info('Split the frame rate distribution per config element')
318         config_elements = self.ixnet.getList(traffic_item_id, 'configElement')
319         for config_element in config_elements:
320             self.ixnet.setAttribute(config_element + '/frameRateDistribution',
321                                     '-portDistribution', 'splitRateEvenly')
322             self.ixnet.setAttribute(config_element + '/frameRateDistribution',
323                                     '-streamDistribution', 'splitRateEvenly')
324             self.ixnet.commit()
325             self._append_procotol_to_stack(
326                 PROTO_UDP, config_element + '/stack:"ethernet-1"')
327             self._append_procotol_to_stack(
328                 PROTO_IPV4, config_element + '/stack:"ethernet-1"')
329
330     def create_traffic_model(self):
331         """Create a traffic item and the needed flow groups
332
333         Each flow group inside the traffic item (only one is present)
334         represents the traffic between two ports:
335                         (uplink)    (downlink)
336             FlowGroup1: port1    -> port2
337             FlowGroup2: port1    <- port2
338             FlowGroup3: port3    -> port4
339             FlowGroup4: port3    <- port4
340         """
341         self._create_traffic_item()
342         self._create_flow_groups()
343         self._setup_config_elements()
344
345     def _update_frame_mac(self, ethernet_descriptor, field, mac_address):
346         """Set the MAC address in a config element stack Ethernet field
347
348         :param ethernet_descriptor: (str) ethernet descriptor, e.g.:
349             /traffic/trafficItem:1/configElement:1/stack:"ethernet-1"
350         :param field: (str) field name, e.g.: destinationAddress
351         :param mac_address: (str) MAC address
352         """
353         field_descriptor = self._get_field_in_stack_item(ethernet_descriptor,
354                                                          field)
355         self.ixnet.setMultiAttribute(field_descriptor,
356                                      '-singleValue', mac_address,
357                                      '-fieldValue', mac_address,
358                                      '-valueType', 'singleValue')
359         self.ixnet.commit()
360
361     def update_frame(self, traffic, duration):
362         """Update the L2 frame
363
364         This function updates the L2 frame options:
365         - Traffic type: "continuous", "fixedDuration".
366         - Duration: in case of traffic_type="fixedDuration", amount of seconds
367                     to inject traffic.
368         - Rate: in frames per seconds or percentage.
369         - Type of rate: "framesPerSecond" or "percentLineRate" ("bitsPerSecond"
370                          no used)
371         - Frame size: custom IMIX [1] definition; a list of packet size in
372                       bytes and the weight. E.g.:
373                       [[64, 64, 10], [128, 128, 15], [512, 512, 5]]
374
375         [1] https://en.wikipedia.org/wiki/Internet_Mix
376
377         :param traffic: list of traffic elements; each traffic element contains
378                         the injection parameter for each flow group.
379         :param duration: (int) injection time in seconds.
380         """
381         for traffic_param in traffic.values():
382             fg_id = str(traffic_param['id'])
383             config_element = self._get_config_element_by_flow_group_name(fg_id)
384             if not config_element:
385                 raise exceptions.IxNetworkFlowNotPresent(flow_group=fg_id)
386
387             type = traffic_param.get('traffic_type', 'fixedDuration')
388             rate = traffic_param['rate']
389             rate_unit = (
390                 'framesPerSecond' if traffic_param['rate_unit'] ==
391                 tp_base.TrafficProfileConfig.RATE_FPS else 'percentLineRate')
392             weighted_range_pairs = self._parse_framesize(
393                 traffic_param['outer_l2']['framesize'])
394             srcmac = str(traffic_param['outer_l2'].get('srcmac', '00:00:00:00:00:01'))
395             dstmac = str(traffic_param['outer_l2'].get('dstmac', '00:00:00:00:00:02'))
396
397             if traffic_param['outer_l2']['QinQ']:
398                 s_vlan = traffic_param['outer_l2']['QinQ']['S-VLAN']
399                 c_vlan = traffic_param['outer_l2']['QinQ']['C-VLAN']
400
401                 field_descriptor = self._get_field_in_stack_item(
402                     self._get_stack_item(fg_id, PROTO_ETHERNET)[0],
403                     'etherType')
404
405                 self.ixnet.setMultiAttribute(field_descriptor,
406                                              '-auto', 'false',
407                                              '-singleValue', ETHER_TYPE_802_1ad,
408                                              '-fieldValue', ETHER_TYPE_802_1ad,
409                                              '-valueType', SINGLE_VALUE)
410
411                 self._append_procotol_to_stack(
412                     PROTO_VLAN, config_element + '/stack:"ethernet-1"')
413                 self._append_procotol_to_stack(
414                     PROTO_VLAN, config_element + '/stack:"ethernet-1"')
415
416                 self._update_vlan_tag(fg_id, s_vlan, S_VLAN)
417                 self._update_vlan_tag(fg_id, c_vlan, C_VLAN)
418
419             self.ixnet.setMultiAttribute(
420                 config_element + '/transmissionControl',
421                 '-type', type, '-duration', duration)
422             self.ixnet.setMultiAttribute(
423                 config_element + '/frameRate',
424                 '-rate', rate, '-type', rate_unit)
425             self.ixnet.setMultiAttribute(
426                 config_element + '/frameSize',
427                 '-type', 'weightedPairs',
428                 '-weightedRangePairs', weighted_range_pairs)
429             self.ixnet.commit()
430
431             self._update_frame_mac(
432                 self._get_stack_item(fg_id, PROTO_ETHERNET)[0],
433                 'destinationAddress', dstmac)
434             self._update_frame_mac(
435                 self._get_stack_item(fg_id, PROTO_ETHERNET)[0],
436                 'sourceAddress', srcmac)
437
438     def _update_vlan_tag(self, fg_id, params, vlan=0):
439         field_to_param_map = {
440             'vlanUserPriority': 'priority',
441             'cfi': 'cfi',
442             'vlanID': 'id'
443         }
444         for field, param in field_to_param_map.items():
445             value = params.get(param)
446             if value:
447                 field_descriptor = self._get_field_in_stack_item(
448                     self._get_stack_item(fg_id, PROTO_VLAN)[vlan],
449                     field)
450
451                 self.ixnet.setMultiAttribute(field_descriptor,
452                                              '-auto', 'false',
453                                              '-singleValue', value,
454                                              '-fieldValue', value,
455                                              '-valueType', SINGLE_VALUE)
456
457         self.ixnet.commit()
458
459     def _update_ipv4_address(self, ip_descriptor, field, ip_address, seed,
460                              mask, count):
461         """Set the IPv4 address in a config element stack IP field
462
463         :param ip_descriptor: (str) IP descriptor, e.g.:
464             /traffic/trafficItem:1/configElement:1/stack:"ipv4-2"
465         :param field: (str) field name, e.g.: scrIp, dstIp
466         :param ip_address: (str) IP address
467         :param seed: (int) seed length
468         :param mask: (int) IP address mask length
469         :param count: (int) number of random IPs to generate
470         """
471         field_descriptor = self._get_field_in_stack_item(ip_descriptor,
472                                                          field)
473         random_mask = str(ipaddress.IPv4Address(
474             2**(ipaddress.IPV4LENGTH - mask) - 1).compressed)
475         self.ixnet.setMultiAttribute(field_descriptor,
476                                      '-seed', seed,
477                                      '-fixedBits', ip_address,
478                                      '-randomMask', random_mask,
479                                      '-valueType', 'random',
480                                      '-countValue', count)
481         self.ixnet.commit()
482
483     def update_ip_packet(self, traffic):
484         """Update the IP packet
485
486         NOTE: Only IPv4 is currently supported.
487         :param traffic: list of traffic elements; each traffic element contains
488                         the injection parameter for each flow group.
489         """
490         # NOTE(ralonsoh): L4 configuration is not set.
491         for traffic_param in traffic.values():
492             fg_id = str(traffic_param['id'])
493             if not self._get_config_element_by_flow_group_name(fg_id):
494                 raise exceptions.IxNetworkFlowNotPresent(flow_group=fg_id)
495
496             count = traffic_param['outer_l3']['count']
497             srcip = str(traffic_param['outer_l3']['srcip'])
498             dstip = str(traffic_param['outer_l3']['dstip'])
499             srcseed = traffic_param['outer_l3']['srcseed']
500             dstseed = traffic_param['outer_l3']['dstseed']
501             srcmask = traffic_param['outer_l3']['srcmask'] or IP_VERSION_4_MASK
502             dstmask = traffic_param['outer_l3']['dstmask'] or IP_VERSION_4_MASK
503
504             self._update_ipv4_address(
505                 self._get_stack_item(fg_id, PROTO_IPV4)[0],
506                 'srcIp', srcip, srcseed, srcmask, count)
507             self._update_ipv4_address(
508                 self._get_stack_item(fg_id, PROTO_IPV4)[0],
509                 'dstIp', dstip, dstseed, dstmask, count)
510
511     def update_l4(self, traffic):
512         """Update the L4 headers
513
514         NOTE: Only UDP is currently supported
515         :param traffic: list of traffic elements; each traffic element contains
516                         the injection parameter for each flow group
517         """
518         for traffic_param in traffic.values():
519             fg_id = str(traffic_param['id'])
520             if not self._get_config_element_by_flow_group_name(fg_id):
521                 raise exceptions.IxNetworkFlowNotPresent(flow_group=fg_id)
522
523             proto = traffic_param['outer_l3']['proto']
524             if proto not in SUPPORTED_PROTO:
525                 raise exceptions.IXIAUnsupportedProtocol(protocol=proto)
526
527             count = traffic_param['outer_l4']['count']
528             seed = traffic_param['outer_l4']['seed']
529
530             srcport = traffic_param['outer_l4']['srcport']
531             srcmask = traffic_param['outer_l4']['srcportmask']
532
533             dstport = traffic_param['outer_l4']['dstport']
534             dstmask = traffic_param['outer_l4']['dstportmask']
535
536             if proto in SUPPORTED_PROTO:
537                 self._update_udp_port(self._get_stack_item(fg_id, proto)[0],
538                                       'srcPort', srcport, seed, srcmask, count)
539
540                 self._update_udp_port(self._get_stack_item(fg_id, proto)[0],
541                                       'dstPort', dstport, seed, dstmask, count)
542
543     def _update_udp_port(self, descriptor, field, value,
544                          seed=1, mask=0, count=1):
545         """Set the UDP port in a config element stack UDP field
546
547         :param udp_descriptor: (str) UDP descriptor, e.g.:
548             /traffic/trafficItem:1/configElement:1/stack:"udp-3"
549         :param field: (str) field name, e.g.: scrPort, dstPort
550         :param value: (int) UDP port fixed bits
551         :param seed: (int) seed length
552         :param mask: (int) UDP port mask
553         :param count: (int) number of random ports to generate
554         """
555         field_descriptor = self._get_field_in_stack_item(descriptor, field)
556
557         if mask == 0:
558             seed = count = 1
559
560         self.ixnet.setMultiAttribute(field_descriptor,
561                                      '-auto', 'false',
562                                      '-seed', seed,
563                                      '-fixedBits', value,
564                                      '-randomMask', mask,
565                                      '-valueType', 'random',
566                                      '-countValue', count)
567
568         self.ixnet.commit()
569
570     def _build_stats_map(self, view_obj, name_map):
571         return {data_yardstick: self.ixnet.execute(
572             'getColumnValues', view_obj, data_ixia)
573             for data_yardstick, data_ixia in name_map.items()}
574
575     def get_statistics(self):
576         """Retrieve port and flow statistics
577
578         "Port Statistics" parameters are stored in self.PORT_STATS_NAME_MAP.
579         "Flow Statistics" parameters are stored in self.LATENCY_NAME_MAP.
580
581         :return: dictionary with the statistics; the keys of this dictionary
582                  are PORT_STATS_NAME_MAP and LATENCY_NAME_MAP keys.
583         """
584         port_statistics = '::ixNet::OBJ-/statistics/view:"Port Statistics"'
585         flow_statistics = '::ixNet::OBJ-/statistics/view:"Flow Statistics"'
586         stats = self._build_stats_map(port_statistics,
587                                       self.PORT_STATS_NAME_MAP)
588         stats.update(self._build_stats_map(flow_statistics,
589                                           self.LATENCY_NAME_MAP))
590         return stats
591
592     def start_traffic(self):
593         """Start the traffic injection in the traffic item
594
595         By configuration, there is only one traffic item. This function returns
596         when the traffic state is TRAFFIC_STATUS_STARTED.
597         """
598         traffic_items = self.ixnet.getList('/traffic', 'trafficItem')
599         if self.is_traffic_running():
600             self.ixnet.execute('stop', '/traffic')
601             # pylint: disable=unnecessary-lambda
602             utils.wait_until_true(lambda: self.is_traffic_stopped())
603
604         self.ixnet.execute('generate', traffic_items)
605         self.ixnet.execute('apply', '/traffic')
606         self.ixnet.execute('start', '/traffic')
607         # pylint: disable=unnecessary-lambda
608         utils.wait_until_true(lambda: self.is_traffic_running())
609
610     def add_topology(self, name, vports):
611         log.debug("add_topology: name='%s' ports='%s'", name, vports)
612         obj = self.ixnet.add(self.ixnet.getRoot(), 'topology')
613         self.ixnet.setMultiAttribute(obj, '-name', name, '-vports', vports)
614         self.ixnet.commit()
615         return obj
616
617     def add_device_group(self, topology, name, multiplier):
618         log.debug("add_device_group: tpl='%s', name='%s', multiplier='%s'",
619                   topology, name, multiplier)
620
621         obj = self.ixnet.add(topology, 'deviceGroup')
622         self.ixnet.setMultiAttribute(obj, '-name', name, '-multiplier',
623                                      multiplier)
624         self.ixnet.commit()
625         return obj
626
627     def add_ethernet(self, dev_group, name):
628         log.debug(
629             "add_ethernet: device_group='%s' name='%s'", dev_group, name)
630         obj = self.ixnet.add(dev_group, 'ethernet')
631         self.ixnet.setMultiAttribute(obj, '-name', name)
632         self.ixnet.commit()
633         return obj
634
635     def _create_vlans(self, ethernet, count):
636         self.ixnet.setMultiAttribute(ethernet, '-useVlans', 'true')
637         self.ixnet.setMultiAttribute(ethernet, '-vlanCount', count)
638         self.ixnet.commit()
639
640     def _configure_vlans(self, ethernet, vlans):
641         vlans_obj = self.ixnet.getList(ethernet, 'vlan')
642         for i, vlan_obj in enumerate(vlans_obj):
643             if vlans[i].vlan_id_step is not None:
644                 vlan_id_obj = self.ixnet.getAttribute(vlan_obj, '-vlanId')
645                 self.ixnet.setMultiAttribute(vlan_id_obj, '-clearOverlays',
646                                              'true', '-pattern', 'counter')
647                 vlan_id_counter = self.ixnet.add(vlan_id_obj, 'counter')
648                 self.ixnet.setMultiAttribute(vlan_id_counter, '-start',
649                                              vlans[i].vlan_id, '-step',
650                                              vlans[i].vlan_id_step,
651                                              '-direction',
652                                              vlans[i].vlan_id_direction)
653             else:
654                 vlan_id_obj = self.ixnet.getAttribute(vlan_obj, '-vlanId')
655                 self.ixnet.setMultiAttribute(vlan_id_obj + '/singleValue',
656                                              '-value', vlans[i].vlan_id)
657
658             if vlans[i].prio_step is not None:
659                 prio_obj = self.ixnet.getAttribute(vlan_obj, '-priority')
660                 self.ixnet.setMultiAttribute(prio_obj, '-clearOverlays', 'true',
661                                              '-pattern', 'counter')
662                 prio_counter = self.ixnet.add(prio_obj, 'counter')
663                 self.ixnet.setMultiAttribute(prio_counter,
664                                         '-start', vlans[i].prio,
665                                         '-step', vlans[i].prio_step,
666                                         '-direction', vlans[i].prio_direction)
667             elif vlans[i].prio is not None:
668                 prio_obj = self.ixnet.getAttribute(vlan_obj, '-priority')
669                 self.ixnet.setMultiAttribute(prio_obj + '/singleValue',
670                                              '-value', vlans[i].prio)
671
672             if vlans[i].tp_id is not None:
673                 tp_id_obj = self.ixnet.getAttribute(vlan_obj, '-tpid')
674                 self.ixnet.setMultiAttribute(tp_id_obj + '/singleValue',
675                                              '-value', vlans[i].tp_id)
676
677         self.ixnet.commit()
678
679     def add_vlans(self, ethernet, vlans):
680         log.debug("add_vlans: ethernet='%s'", ethernet)
681
682         if vlans is None or len(vlans) == 0:
683             raise RuntimeError(
684                 "Invalid 'vlans' argument. Expected list of Vlan instances.")
685
686         self._create_vlans(ethernet, len(vlans))
687         self._configure_vlans(ethernet, vlans)
688
689     def add_ipv4(self, ethernet, name='',
690                  addr=None, addr_step=None, addr_direction='increment',
691                  prefix=None, prefix_step=None, prefix_direction='increment',
692                  gateway=None, gw_step=None, gw_direction='increment'):
693         log.debug("add_ipv4: ethernet='%s' name='%s'", ethernet, name)
694         obj = self.ixnet.add(ethernet, 'ipv4')
695         if name != '':
696             self.ixnet.setAttribute(obj, '-name', name)
697             self.ixnet.commit()
698
699         if addr_step is not None:
700             # handle counter pattern
701             _address = self.ixnet.getAttribute(obj, '-address')
702             self.ixnet.setMultiAttribute(_address, '-clearOverlays', 'true',
703                                          '-pattern', 'counter')
704
705             address_counter = self.ixnet.add(_address, 'counter')
706             self.ixnet.setMultiAttribute(address_counter,
707                                          '-start', addr,
708                                          '-step', addr_step,
709                                          '-direction', addr_direction)
710         elif addr is not None:
711             # handle single value
712             _address = self.ixnet.getAttribute(obj, '-address')
713             self.ixnet.setMultiAttribute(_address + '/singleValue', '-value',
714                                          addr)
715
716         if prefix_step is not None:
717             # handle counter pattern
718             _prefix = self.ixnet.getAttribute(obj, '-prefix')
719             self.ixnet.setMultiAttribute(_prefix, '-clearOverlays', 'true',
720                                          '-pattern', 'counter')
721             prefix_counter = self.ixnet.add(_prefix, 'counter')
722             self.ixnet.setMultiAttribute(prefix_counter,
723                                          '-start', prefix,
724                                          '-step', prefix_step,
725                                          '-direction', prefix_direction)
726         elif prefix is not None:
727             # handle single value
728             _prefix = self.ixnet.getAttribute(obj, '-prefix')
729             self.ixnet.setMultiAttribute(_prefix + '/singleValue', '-value',
730                                          prefix)
731
732         if gw_step is not None:
733             # handle counter pattern
734             _gateway = self.ixnet.getAttribute(obj, '-gatewayIp')
735             self.ixnet.setMultiAttribute(_gateway, '-clearOverlays', 'true',
736                                          '-pattern', 'counter')
737
738             gateway_counter = self.ixnet.add(_gateway, 'counter')
739             self.ixnet.setMultiAttribute(gateway_counter,
740                                          '-start', gateway,
741                                          '-step', gw_step,
742                                          '-direction', gw_direction)
743         elif gateway is not None:
744             # handle single value
745             _gateway = self.ixnet.getAttribute(obj, '-gatewayIp')
746             self.ixnet.setMultiAttribute(_gateway + '/singleValue', '-value',
747                                          gateway)
748
749         self.ixnet.commit()
750         return obj
751
752     def add_pppox_client(self, xproto, auth, user, pwd):
753         log.debug(
754             "add_pppox_client: xproto='%s', auth='%s', user='%s', pwd='%s'",
755             xproto, auth, user, pwd)
756         obj = self.ixnet.add(xproto, 'pppoxclient')
757         self.ixnet.commit()
758
759         if auth == 'pap':
760             auth_type = self.ixnet.getAttribute(obj, '-authType')
761             self.ixnet.setMultiAttribute(auth_type + '/singleValue', '-value',
762                                          auth)
763             pap_user = self.ixnet.getAttribute(obj, '-papUser')
764             self.ixnet.setMultiAttribute(pap_user + '/singleValue', '-value',
765                                          user)
766             pap_pwd = self.ixnet.getAttribute(obj, '-papPassword')
767             self.ixnet.setMultiAttribute(pap_pwd + '/singleValue', '-value',
768                                          pwd)
769         else:
770             raise NotImplementedError()
771
772         self.ixnet.commit()
773         return obj