CSIT-66 Ixia Network driver
[csit.git] / resources / tools / ixnet / ixia-stateless.py
1 #!/usr/bin/python
2
3 # Copyright (c) 2016 Cisco and/or its affiliates.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 """This script uses IxNetwork Python API to control IxNetwork.
17
18 Requirements:
19 - IxNetwork v7.51
20  - ixnetwork.py library v7.51.1014.17 in /opt/ixnet-7.51
21
22 """
23
24 import argparse
25 import sys
26 import time
27
28 sys.path.insert(0, "/opt/ixnet-7.51/")
29 from IxNetwork import IxNet
30
31
32 class IxDriverError(Exception):
33     """Ixia-driver error handling."""
34
35
36 def add_ports(ixnet, count):
37     """Add ports to configuration.
38
39     :param ixnet: Instance of IxNet class.
40     :param count: Number of ports to add.
41     :type ixnet: IxNet
42     :type count: int
43     :return: List of ports added to configuration.
44     :rtype: list
45     """
46
47     if ixnet is None:
48         raise ValueError('No Ixnet instance')
49     if count <= 0 or count % 2 != 0:
50         raise ValueError('Port count must be even and higher then zero')
51
52     for _ in range(count):
53         ixnet.add(ixnet.getRoot(), 'vport')
54
55     ixnet.commit()
56
57     return ixnet.getList(ixnet.getRoot(), 'vport')
58
59
60 def configure_endpoints(ixnet, topologies, args):
61     """Configure endpoints with topologies.
62
63     :param ixnet: Instance of IxNet class.
64     :param topologies: List of topologies.
65     :param args: Arguments from command line.
66     :type ixnet: IxNet
67     :type topologies: list
68     :type args: ArgumentParser
69     :return: Traffic items
70     :rtype: list
71     """
72
73     if ixnet is None:
74         raise ValueError('No Ixnet instance')
75     if topologies is None:
76         raise ValueError('No topologies created')
77
78     traffic_items = []
79
80     if args.traffic_type == 'ethernetVlan':
81         path = '/deviceGroup:1/ethernet:1'
82     elif args.traffic_type == 'ipv4':
83         path = '/deviceGroup:1/ethernet:1/ipv4:1'
84     elif args.traffic_type == 'ipv6':
85         path = '/deviceGroup:1/ethernet:1/ipv6:1'
86     else:
87         raise ValueError('Traffic type not supported')
88
89     if args.pairing == 'full':
90         pairing = zip(topologies, topologies[1:])[::2]
91     elif args.pairing == 'half':
92         step = len(args.if_port)/2
93         pairing = zip(topologies, topologies[step:])[::1]
94     else:
95         raise ValueError('Pairing type not supported')
96
97     # Pairing endpoints and assigning them into traffic items
98     for topo1, topo2 in pairing:
99         src = [topo1 + path]
100         dst = [topo2 + path]
101         topo1_name = ixnet.getAttribute(topo1, '-name')
102         topo2_name = ixnet.getAttribute(topo2, '-name')
103         traffic_item = configure_traffic_item(ixnet, topo1_name, src, dst,
104                                               args.frame_size, args.rate,
105                                               args.rate_units,
106                                               args.traffic_type)
107         traffic_items.append(traffic_item)
108         traffic_item = configure_traffic_item(ixnet, topo2_name, dst, src,
109                                               args.frame_size, args.rate,
110                                               args.rate_units,
111                                               args.traffic_type)
112         traffic_items.append(traffic_item)
113
114     return traffic_items
115
116
117 def configure_traffic_item(ixnet, name, src_ep, dst_ep, frame_size,
118                            rate, rate_units, traffic_type):
119     """Create and configure traffic item.
120
121     :param ixnet: Instance of IxNet class.
122     :param name: Name of traffic profile.
123     :param src_ep: Source topology endpoint.
124     :param dst_ep: Destination topology endpoint.
125     :param frame_size: L2 frame size.
126     :param rate: Rate.
127     :param rate_units: Rate type (pps, perc, bps).
128     :param traffic_type: Traffic type (ipv4, ipv6, ethernetVlan).
129     :type ixnet: IxNet
130     :type name: str
131     :type src_ep: str
132     :type dst_ep: str
133     :type frame_size: int
134     :type rate: str
135     :type rate_units: str
136     :type traffic_type: str
137     :return: Created traffic item string.
138     :rtype: str
139     """
140
141     if ixnet is None:
142         raise ValueError('No Ixnet instance')
143     if traffic_type == 'ipv4':
144         if frame_size < 64:
145             raise ValueError('Min. frame size for traffic type is 64B')
146     elif traffic_type == 'ipv6':
147         if frame_size < 78:
148             raise ValueError('Min. frame size for traffic type is 78B')
149     elif traffic_type == 'ethernetVlan':
150         if frame_size < 64:
151             raise ValueError('Min. frame size for traffic type is 64B')
152     else:
153         raise ValueError('Traffic type not supported')
154
155     ixnet.add(ixnet.getRoot() + '/traffic', 'trafficItem')
156     ixnet.commit()
157     traffic_item = ixnet.getList(ixnet.getRoot() + '/traffic',
158                                  'trafficItem')[-1]
159     ixnet.setMultiAttribute(traffic_item,
160                             '-name', name,
161                             '-trafficType', traffic_type,
162                             '-allowSelfDestined', False,
163                             '-trafficItemType', 'l2L3',
164                             '-mergeDestinations', True,
165                             '-egressEnabled', False,
166                             '-srcDestMesh', 'oneToOne',
167                             '-enabled', True,
168                             '-routeMesh', 'oneToOne',
169                             '-transmitMode', 'interleaved',
170                             '-biDirectional', False,
171                             '-hostsPerNetwork', 1)
172     ixnet.setAttribute(traffic_item, '-trafficType', traffic_type)
173     ixnet.add(traffic_item, 'endpointSet',
174               '-sources', src_ep,
175               '-destinations', dst_ep,
176               '-name', 'endpointSet1')
177     ixnet.setMultiAttribute(traffic_item + '/configElement:1/frameSize',
178                             '-type', 'fixed',
179                             '-fixedSize', frame_size)
180     ixnet.commit()
181
182     # Use 4 Byte Signature
183     ixnet.setAttribute(ixnet.getRoot() + '/traffic', '-enableMinFrameSize',
184                        True)
185     ixnet.commit()
186
187     if rate_units == 'pps':
188         ixnet.setMultiAttribute(traffic_item + '/configElement:1/frameRate',
189                                 '-type', 'framesPerSecond',
190                                 '-rate', rate)
191     elif rate_units == '%':
192         ixnet.setMultiAttribute(traffic_item + '/configElement:1/frameRate',
193                                 '-type', 'percentLineRate',
194                                 '-rate', rate)
195     elif rate_units == 'bps':
196         ixnet.setMultiAttribute(traffic_item + '/configElement:1/frameRate',
197                                 '-type', 'bitsPerSecond',
198                                 '-rate', rate)
199     else:
200         raise ValueError('Rate type not supported')
201
202     ixnet.setMultiAttribute(traffic_item + '/configElement:1/transmissionControl',
203                             '-type', 'continuous')
204     ixnet.setMultiAttribute(traffic_item + "/tracking",
205                             '-trackBy', ['trackingenabled0'])
206     ixnet.commit()
207
208     # Get Full packet stack
209     flow_group = ixnet.remapIds(traffic_item)[0]
210     highlevel_stream = ixnet.getList(flow_group, 'highLevelStream')[0]
211     traffic_stacks = ixnet.getList(highlevel_stream, 'stack')
212
213     return traffic_item
214
215
216 def configure_protocols(ixnet, vports, args):
217     """Create and configure topologies and protocols.
218
219     :param ixnet: Instance of IxNet class.
220     :param vports: List of ports added to configuration.
221     :param args: Arguments from command line.
222     :type ixnet: IxNet
223     :type vports: list
224     :type args: ArgumentParser
225     :return: Topologies list
226     :rtype: list
227     """
228
229     if ixnet is None:
230         raise ValueError('No Ixnet instance')
231     if vports is None:
232         raise ValueError('Vports is Null')
233
234     topologies = None
235
236     # Add topologies
237     for _ in vports:
238         ixnet.add(ixnet.getRoot(), 'topology')
239     ixnet.commit()
240
241     # Get all topologies
242     topologies = ixnet.getList(ixnet.getRoot(), 'topology')
243
244     device_groups = []
245     # Pair topology with vport and add device group for each
246     for topology, vport in zip(topologies, vports):
247         # Add ports to topologies
248         ixnet.setAttribute(topology, '-vports', vport)
249         # Add device groups to topologies
250         ixnet.add(topology, 'deviceGroup')
251         ixnet.commit()
252         device_groups.append(ixnet.getList(topology, 'deviceGroup')[0])
253
254     ethernets = []
255     # Add ethernet protocol stacks to device groups
256     for device_group, cnt in zip(device_groups, args.port_src_ip_cnt):
257         ixnet.setAttribute(device_group, '-multiplier', cnt)
258         ixnet.add(device_group, 'ethernet')
259         ixnet.commit()
260         ethernets.append(ixnet.getList(device_group, 'ethernet')[0])
261
262     for i, ethernet in enumerate(ethernets):
263         mac = ixnet.getAttribute(ethernet, '-mac')
264         ixnet.setAttribute(mac + '/singleValue', \
265             '-value', args.port_src_mac[i])
266
267     if args.traffic_type == 'ipv4':
268         ipv4s = []
269         # Add ipv4 protocol stacks to device groups
270         for ethernet in ethernets:
271             ixnet.add(ethernet, 'ipv4')
272             ixnet.commit()
273             ipv4s.append(ixnet.getList(ethernet, 'ipv4')[0])
274
275         configure_ipv4_protocol(ixnet, ipv4s, args)
276     elif args.traffic_type == 'ipv6':
277         ipv6s = []
278         # Add ipv6 protocol stacks to device groups
279         for ethernet in ethernets:
280             ixnet.add(ethernet, 'ipv6')
281             ixnet.commit()
282             ipv6s.append(ixnet.getList(ethernet, 'ipv6')[0])
283
284         configure_ipv6_protocol(ixnet, ipv6s, args)
285     elif args.traffic_type == 'ethernetVlan':
286         # TODO: fix this profile
287         ipv4s = []
288         # Add ipv4 protocol stacks to device groups
289         for ethernet in ethernets:
290             ixnet.add(ethernet, 'ipv4')
291             ixnet.commit()
292             ipv4s.append(ixnet.getList(ethernet, 'ipv4')[0])
293
294         configure_ethernetVlan_protocol(ixnet, ipv4s, args)
295     else:
296         raise ValueError('Traffic type not supported')
297
298     return topologies
299
300
301 def configure_ethernetVlan_protocol(ixnet, ipv4s, args):
302     """Configure ethernet protocol.
303
304     :param ixnet: Instance of IxNet class.
305     :param ipv4s: List of ipv4 protocols.
306     :param args: Args from command line.
307     :type ixnet: IxNetwork
308     :type ipv4s: list
309     :type args: ArgumentParser
310     :return: nothing
311     """
312
313     if ixnet is None:
314         raise ValueError('No Ixnet instance')
315
316     for i, ipv4 in enumerate(ipv4s):
317         address = ixnet.getAttribute(ipv4, '-address')
318         gatewayIp = ixnet.getAttribute(ipv4, '-gatewayIp')
319         resolveGateway = ixnet.getAttribute(ipv4, '-resolveGateway')
320         gatewayMac = ixnet.getAttribute(ipv4, '-manualGatewayMac')
321
322         ixnet.setMultiAttribute(address + '/counter', \
323             '-direction', 'increment', \
324             '-start', args.port_src_ip[i], \
325             '-step', '0.0.0.1')
326         ixnet.setMultiAttribute(resolveGateway + '/singleValue', \
327             '-value', 'true')
328
329     ixnet.commit()
330
331
332 def configure_ipv4_protocol(ixnet, ipv4s, args):
333     """Configure ipv4 protocol.
334
335     :param ixnet: Instance of IxNet class.
336     :param ipv4s: List of ipv4 protocols.
337     :param args: Args from command line.
338     :type ixnet: IxNetwork
339     :type ipv4s: list
340     :type args: ArgumentParser
341     :return: nothing
342     """
343
344     if ixnet is None:
345         raise ValueError('No Ixnet instance')
346
347     for i, ipv4 in enumerate(ipv4s):
348         address = ixnet.getAttribute(ipv4, '-address')
349         gatewayIp = ixnet.getAttribute(ipv4, '-gatewayIp')
350         resolveGateway = ixnet.getAttribute(ipv4, '-resolveGateway')
351         gatewayMac = ixnet.getAttribute(ipv4, '-manualGatewayMac')
352
353         ixnet.setMultiAttribute(address + '/counter', \
354             '-direction', 'increment', \
355             '-start', args.port_src_ip[i], \
356             '-step', '0.0.0.1')
357         ixnet.setMultiAttribute(gatewayIp + '/singleValue', \
358             '-value', args.port_gw_ip[i])
359         ixnet.setMultiAttribute(resolveGateway + '/singleValue', \
360             '-value', 'false')
361         ixnet.setMultiAttribute(gatewayMac + '/singleValue', \
362             '-value', args.port_gw_mac[i])
363
364     ixnet.commit()
365
366
367 def configure_ipv6_protocol(ixnet, ipv6s, args):
368     """Configure ipv6 protocol.
369
370     :param ixnet: Instance of IxNet class.
371     :param ipv6s: List of ipv6 protocols.
372     :param args: Args from command line.
373     :type ixnet: IxNetwork
374     :type ipv6s: list
375     :type args: ArgumentParser
376     :return: nothing
377     """
378
379     if ixnet is None:
380         raise ValueError('No Ixnet instance')
381
382     for i, ipv6 in enumerate(ipv6s):
383         address = ixnet.getAttribute(ipv6, '-address')
384         gatewayIp = ixnet.getAttribute(ipv6, '-gatewayIp')
385         resolveGateway = ixnet.getAttribute(ipv6, '-resolveGateway')
386         gatewayMac = ixnet.getAttribute(ipv6, '-manualGatewayMac')
387
388         ixnet.setMultiAttribute(address + '/counter', \
389             '-direction', 'increment', \
390             '-start', args.port_src_ip[i], \
391             '-step', '::1')
392         ixnet.setMultiAttribute(gatewayIp + '/singleValue', \
393             '-value', args.port_gw_ip[i])
394         ixnet.setMultiAttribute(resolveGateway + '/singleValue', \
395             '-value', 'false')
396         ixnet.setMultiAttribute(gatewayMac + '/singleValue', \
397             '-value', args.port_gw_mac[i])
398
399     ixnet.commit()
400
401
402 def assign_physical_interfaces(ixnet, ixinterfaces):
403     """Assign physical interfaces on chassis.
404
405     :param ixnet: Instance of IxNet class.
406     :param ixinterfaces: List of physical interfaces (chassis/card/port).
407     :type ixnet: IxNet
408     :type ixinterfaces: list
409     :return: nothing
410     """
411
412     vports = ixnet.getList(ixnet.getRoot(), 'vport')
413     assign_ifs = ixnet.execute('assignPorts', ixinterfaces, [],
414                                ixnet.getList("/", "vport"), True)
415     if assign_ifs != vports:
416         raise IxDriverError("Assigning ports failed: {}".format(assign_ifs))
417
418
419 def apply_traffic(ixnet, traffic_items):
420     """Generate, apply all traffic items.
421
422     :param ixnet: Instance of IxNet class.
423     :param traffic_items: List of traffic items.
424     :type ixnet: IxNet
425     :type traffic_items: list
426     :return: nothing
427     """
428
429     if ixnet is None:
430         raise ValueError('No Ixnet instance')
431     if traffic_items is None:
432         raise ValueError('No traffic items')
433
434     for traffic_item in traffic_items:
435         ixnet.execute('generate', traffic_item)
436
437     ixnet.execute('apply', ixnet.getRoot() + '/traffic')
438
439
440 def start_traffic(ixnet):
441     """Start traffic on all traffic items.
442
443     :param ixnet: Instance of IxNet class.
444     :type ixnet: IxNet
445     :return: nothing
446     """
447
448     if ixnet is None:
449         raise ValueError('No Ixnet instance')
450
451     ixnet.execute('start', ixnet.getRoot() + '/traffic')
452
453
454 def stop_traffic(ixnet):
455     """Stop traffic on all traffic items.
456
457     :param ixnet: Instance of IxNet class.
458     :type ixnet: IxNet
459     :return: nothing
460     """
461
462     if ixnet is None:
463         raise ValueError('No Ixnet instance')
464
465     ixnet.execute('stop', ixnet.getRoot() + '/traffic')
466
467
468 def process_statistics(ixnet):
469     """Process the statistics.
470
471     :param ixnet: Instance of IxNet class.
472     :type ixnet: IxNet
473     :return: nothing
474     """
475
476     if ixnet is None:
477         raise ValueError('No Ixnet instance')
478
479     viewName = "Traffic Item Statistics"
480     views = ixnet.getList('/statistics', 'view')
481     viewObj = ''
482     editedViewName = '::ixNet::OBJ-/statistics/view:\"' + viewName + '\"'
483     for view in views:
484         if editedViewName == view:
485             viewObj = view
486             break
487
488     txFrames = ixnet.execute('getColumnValues', viewObj, 'Tx Frames')
489     rxFrames = ixnet.execute('getColumnValues', viewObj, 'Rx Frames')
490     loss = ixnet.execute('getColumnValues', viewObj, 'Loss %')
491     delta = ixnet.execute('getColumnValues', viewObj, 'Frames Delta')
492     SFavg = ixnet.execute('getColumnValues', viewObj,
493                           'Store-Forward Avg Latency (ns)')
494     SFmin = ixnet.execute('getColumnValues', viewObj,
495                           'Store-Forward Min Latency (ns)')
496     SFmax = ixnet.execute('getColumnValues', viewObj,
497                           'Store-Forward Max Latency (ns)')
498
499     print txFrames, rxFrames, loss, delta, SFavg, SFmin, SFmax
500
501
502 def print_error(msg):
503     """Print error message on stderr.
504
505     :param msg: Error message to print.
506     :type msg: string
507     :return: nothing
508     """
509
510     sys.stderr.write(msg+'\n')
511
512
513 def parse_args():
514     """Parse arguments from cmd line.add_ports
515
516     :return: Parsed arguments.
517     :rtype ArgumentParser
518     """
519
520     parser = argparse.ArgumentParser()
521     parser.add_argument("-i", "--ixia_server", required=True,
522                         help="IxNetwork TCL server")
523     parser.add_argument("-p", "--ixia_port", required=True, type=int,
524                         help="IxNetwork TCL port")
525     parser.add_argument("-d", "--duration", required=True, type=int,
526                         help="Duration of traffic run in seconds")
527     parser.add_argument("-s", "--frame_size", required=True, type=int,
528                         help="Size of a Frame without padding and IPG")
529     parser.add_argument("-r", "--rate", required=True,
530                         help="Traffic rate with")
531     parser.add_argument("-u", "--rate_units", required=True,
532                         choices=['pps', 'perc', 'bps'],
533                         help="Traffic rate units")
534     parser.add_argument("-t", "--traffic_type", required=True,
535                         choices=['ipv4', 'ipv6', 'ethernetVlan'],
536                         help="Traffic type")
537     parser.add_argument("--async", action="store_true",
538                         default=False,
539                         help="Non-blocking call of the script")
540     parser.add_argument("-w", "--warmup_time", type=int,
541                         default=0,
542                         help="Traffic warmup time in seconds, 0 = disable")
543     parser.add_argument("-x", "--pairing", choices=['full', 'half'],
544                         default='full',
545                         help="Pairing of ports e.g. (1,2)(3,4) or (1,3)(2,4)")
546
547     parser.add_argument('--if_chassis', nargs='+', default=[],
548                         required=True,
549                         help='Add Ixia chassis to a list')
550     parser.add_argument('--if_card', nargs='+', default=[],
551                         required=True, type=int,
552                         help='Add Ixia card to a list')
553     parser.add_argument('--if_port', nargs='+', default=[],
554                         required=True, type=int,
555                         help='Add Ixia port to a list')
556     parser.add_argument('--port_src_mac', nargs='+', default=[],
557                         required=True,
558                         help='Add port source mac address to a list')
559     parser.add_argument('--port_src_mac_cnt', nargs='+', default=[],
560                         type=int,
561                         help='Add port source mac address count to a list')
562     parser.add_argument('--port_src_ip', nargs='+', default=[],
563                         required=True,
564                         help='Add port source IP address to a list')
565     parser.add_argument('--port_src_ip_cnt', nargs='+', default=[],
566                         required=True, type=int,
567                         help='Add port source IP address count to a list')
568     parser.add_argument('--port_gw_ip', nargs='+', default=[],
569                         help='Add port gateway IP address to a list')
570     parser.add_argument('--port_gw_mac', nargs='+', default=[],
571                         help='Add port gateway mac address to a list')
572
573     return parser.parse_args()
574
575
576 def main():
577     """Main function."""
578
579     # Parse comand line arguments
580     args = parse_args()
581
582     ix_interfaces = []
583     for chassis, card, port in zip(args.if_chassis,
584                                    args.if_card,
585                                    args.if_port):
586         ix_interfaces.append((chassis, card, port))
587
588     try:
589         # Create IXIA instance
590         ixnet = IxNet()
591
592         # Debuging mode
593         ixnet.setDebug(True)
594
595         # Conntect to IxServer
596         ixnet.connect(args.ixia_server, '-port', args.ixia_port, '-version',
597                       '7.40')
598
599         # Create blank configuration
600         ixnet.execute('newConfig')
601
602         # Add interfaces to config
603         vports = add_ports(ixnet, len(ix_interfaces))
604
605         # Configure protocols
606         topologies = configure_protocols(ixnet, vports, args)
607
608         # Create traffic items
609         traffic_items = configure_endpoints(ixnet, topologies, args)
610
611         # Assign physical interfaces on chassis
612         assign_physical_interfaces(ixnet, ix_interfaces)
613
614         # Generate and apply traffic
615         apply_traffic(ixnet, traffic_items)
616
617         if args.warmup_time > 0:
618             # Start traffic
619             start_traffic(ixnet)
620             # Wait for warmup time
621             time.sleep(args.warmup_time)
622             # Stop traffic
623             stop_traffic(ixnet)
624             # Wait for incomming packets
625             time.sleep(10)
626
627         # Start traffic
628         start_traffic(ixnet)
629
630         if not args.async:
631             # Wait for duration time
632             time.sleep(args.duration)
633             # Stop traffic
634             stop_traffic(ixnet)
635             # Wait for incomming packets
636             time.sleep(10)
637
638             process_statistics(ixnet)
639     except Exception as ex_error:
640         print_error(str(ex_error))
641         sys.exit(1)
642
643     finally:
644         ixnet.disconnect()
645
646
647 if __name__ == "__main__":
648     sys.exit(main())