trex_stl_client.py revision e4c8e44b
1#!/router/bin/python
2
3# for API usage the path name must be full
4from .trex_stl_exceptions import *
5from .trex_stl_streams import *
6
7from .trex_stl_jsonrpc_client import JsonRpcClient, BatchMessage
8from . import trex_stl_stats
9
10from .trex_stl_port import Port
11from .trex_stl_types import *
12from .trex_stl_async_client import CTRexAsyncClient
13
14from .utils import parsing_opts, text_tables, common
15from .utils.common import *
16from .utils.text_opts import *
17from functools import wraps
18
19from collections import namedtuple
20from yaml import YAMLError
21import time
22import datetime
23import re
24import random
25import json
26import traceback
27import os.path
28
29
30############################     logger     #############################
31############################                #############################
32############################                #############################
33
34# logger API for the client
35class LoggerApi(object):
36    # verbose levels
37    VERBOSE_QUIET         = 0
38    VERBOSE_REGULAR_SYNC  = 1
39    VERBOSE_REGULAR       = 2
40    VERBOSE_HIGH          = 3
41
42    def __init__(self):
43        self.level = LoggerApi.VERBOSE_REGULAR
44
45    # implemented by specific logger
46    def write(self, msg, newline = True):
47        raise Exception("Implement this")
48
49    # implemented by specific logger
50    def flush(self):
51        raise Exception("Implement this")
52
53    def set_verbose (self, level):
54        if not level in range(self.VERBOSE_QUIET, self.VERBOSE_HIGH + 1):
55            raise ValueError("Bad value provided for logger")
56
57        self.level = level
58
59    def get_verbose (self):
60        return self.level
61
62
63    def check_verbose (self, level):
64        return (self.level >= level)
65
66
67    # simple log message with verbose
68    def log (self, msg, level = VERBOSE_REGULAR_SYNC, newline = True):
69        if not self.check_verbose(level):
70            return
71
72        self.write(msg, newline)
73
74    # logging that comes from async event
75    def async_log (self, msg, level = VERBOSE_REGULAR, newline = True):
76        self.log(msg, level, newline)
77
78
79    def pre_cmd (self, desc):
80        self.log(format_text('\n{:<60}'.format(desc), 'bold'), newline = False)
81        self.flush()
82
83    def post_cmd (self, rc):
84        if rc:
85            self.log(format_text("[SUCCESS]\n", 'green', 'bold'))
86        else:
87            self.log(format_text("[FAILED]\n", 'red', 'bold'))
88
89
90    def log_cmd (self, desc):
91        self.pre_cmd(desc)
92        self.post_cmd(True)
93
94
95    # supress object getter
96    def supress (self, level = VERBOSE_QUIET):
97        class Supress(object):
98            def __init__ (self, logger, level):
99                self.logger = logger
100                self.level = level
101
102            def __enter__ (self):
103                self.saved_level = self.logger.get_verbose()
104                if self.level < self.saved_level:
105                    self.logger.set_verbose(self.level)
106
107            def __exit__ (self, type, value, traceback):
108                self.logger.set_verbose(self.saved_level)
109
110        return Supress(self, level)
111
112
113
114# default logger - to stdout
115class DefaultLogger(LoggerApi):
116
117    def __init__ (self):
118        super(DefaultLogger, self).__init__()
119
120    def write (self, msg, newline = True):
121        if newline:
122            print(msg)
123        else:
124            print (msg),
125
126    def flush (self):
127        sys.stdout.flush()
128
129
130############################     async event hander     #############################
131############################                            #############################
132############################                            #############################
133
134# an event
135class Event(object):
136
137    def __init__ (self, origin, ev_type, msg):
138        self.origin = origin
139        self.ev_type = ev_type
140        self.msg = msg
141
142        self.ts = datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S')
143
144    def __str__ (self):
145
146        prefix = "[{:^}][{:^}]".format(self.origin, self.ev_type)
147
148        return "{:<10} - {:18} - {:}".format(self.ts, prefix, format_text(self.msg, 'bold'))
149
150
151# handles different async events given to the client
152class EventsHandler(object):
153
154
155    def __init__ (self, client):
156        self.client = client
157        self.logger = self.client.logger
158
159        self.events = []
160
161    # public functions
162
163    def get_events (self, ev_type_filter = None):
164        if ev_type_filter:
165            return [ev for ev in self.events if ev.ev_type in listify(ev_type_filter)]
166        else:
167            return [ev for ev in self.events]
168
169
170    def clear_events (self):
171        self.events = []
172
173
174    def log_warning (self, msg, show = True):
175        self.__add_event_log('local', 'warning', msg, show)
176
177
178    # events called internally
179
180    def on_async_dead (self):
181        if self.client.connected:
182            msg = 'Lost connection to server'
183            self.client.connected = False
184            self.__add_event_log('local', 'info', msg, True)
185
186
187    def on_async_alive (self):
188        pass
189
190
191
192    def on_async_rx_stats_event (self, data, baseline):
193        self.client.flow_stats.update(data, baseline)
194
195    def on_async_latency_stats_event (self, data, baseline):
196        self.client.latency_stats.update(data, baseline)
197
198    # handles an async stats update from the subscriber
199    def on_async_stats_update(self, dump_data, baseline):
200        global_stats = {}
201        port_stats = {}
202
203        # filter the values per port and general
204        for key, value in dump_data.items():
205            # match a pattern of ports
206            m = re.search('(.*)\-(\d+)', key)
207            if m:
208                port_id = int(m.group(2))
209                field_name = m.group(1)
210                if port_id in self.client.ports:
211                    if not port_id in port_stats:
212                        port_stats[port_id] = {}
213                    port_stats[port_id][field_name] = value
214                else:
215                    continue
216            else:
217                # no port match - general stats
218                global_stats[key] = value
219
220        # update the general object with the snapshot
221        self.client.global_stats.update(global_stats, baseline)
222
223        # update all ports
224        for port_id, data in port_stats.items():
225            self.client.ports[port_id].port_stats.update(data, baseline)
226
227
228
229    # dispatcher for server async events (port started, port stopped and etc.)
230    def on_async_event (self, event_type, data):
231        # DP stopped
232        show_event = False
233
234        # port started
235        if (event_type == 0):
236            port_id = int(data['port_id'])
237            ev = "Port {0} has started".format(port_id)
238            self.__async_event_port_started(port_id)
239
240        # port stopped
241        elif (event_type == 1):
242            port_id = int(data['port_id'])
243            ev = "Port {0} has stopped".format(port_id)
244
245            # call the handler
246            self.__async_event_port_stopped(port_id)
247
248
249        # port paused
250        elif (event_type == 2):
251            port_id = int(data['port_id'])
252            ev = "Port {0} has paused".format(port_id)
253
254            # call the handler
255            self.__async_event_port_paused(port_id)
256
257        # port resumed
258        elif (event_type == 3):
259            port_id = int(data['port_id'])
260            ev = "Port {0} has resumed".format(port_id)
261
262            # call the handler
263            self.__async_event_port_resumed(port_id)
264
265        # port finished traffic
266        elif (event_type == 4):
267            port_id = int(data['port_id'])
268            ev = "Port {0} job done".format(port_id)
269
270            # call the handler
271            self.__async_event_port_job_done(port_id)
272            show_event = True
273
274        # port was acquired - maybe stolen...
275        elif (event_type == 5):
276            session_id = data['session_id']
277
278            port_id = int(data['port_id'])
279            who     = data['who']
280            force   = data['force']
281
282            # if we hold the port and it was not taken by this session - show it
283            if port_id in self.client.get_acquired_ports() and session_id != self.client.session_id:
284                show_event = True
285
286            # format the thief/us...
287            if session_id == self.client.session_id:
288                user = 'you'
289            elif who == self.client.username:
290                user = 'another session of you'
291            else:
292                user = "'{0}'".format(who)
293
294            if force:
295                ev = "Port {0} was forcely taken by {1}".format(port_id, user)
296            else:
297                ev = "Port {0} was taken by {1}".format(port_id, user)
298
299            # call the handler in case its not this session
300            if session_id != self.client.session_id:
301                self.__async_event_port_acquired(port_id, who)
302
303
304        # port was released
305        elif (event_type == 6):
306            port_id     = int(data['port_id'])
307            who         = data['who']
308            session_id  = data['session_id']
309
310            if session_id == self.client.session_id:
311                user = 'you'
312            elif who == self.client.username:
313                user = 'another session of you'
314            else:
315                user = "'{0}'".format(who)
316
317            ev = "Port {0} was released by {1}".format(port_id, user)
318
319            # call the handler in case its not this session
320            if session_id != self.client.session_id:
321                self.__async_event_port_released(port_id)
322
323        elif (event_type == 7):
324            port_id = int(data['port_id'])
325            ev = "port {0} job failed".format(port_id)
326            show_event = True
327
328        # port attr changed
329        elif (event_type == 8):
330
331            port_id = int(data['port_id'])
332
333            diff = self.__async_event_port_attr_changed(port_id, data['attr'])
334            if not diff:
335                return
336
337
338            ev = "port {0} attributes changed".format(port_id)
339            for key, (old_val, new_val) in diff.items():
340                ev += '\n  {key}: {old} -> {new}'.format(
341                        key = key,
342                        old = old_val.lower() if type(old_val) is str else old_val,
343                        new = new_val.lower() if type(new_val) is str else new_val)
344
345            show_event = True
346
347
348
349        # server stopped
350        elif (event_type == 100):
351            ev = "Server has stopped"
352            # to avoid any new messages on async
353            self.client.async_client.set_as_zombie()
354            self.__async_event_server_stopped()
355            show_event = True
356
357
358        else:
359            # unknown event - ignore
360            return
361
362
363        self.__add_event_log('server', 'info', ev, show_event)
364
365
366    # private functions
367
368    # on rare cases events may come on a non existent prot
369    # (server was re-run with different config)
370    def __async_event_port_job_done (self, port_id):
371        if port_id in self.client.ports:
372            self.client.ports[port_id].async_event_port_job_done()
373
374    def __async_event_port_stopped (self, port_id):
375        if port_id in self.client.ports:
376            self.client.ports[port_id].async_event_port_stopped()
377
378
379    def __async_event_port_started (self, port_id):
380        if port_id in self.client.ports:
381            self.client.ports[port_id].async_event_port_started()
382
383    def __async_event_port_paused (self, port_id):
384        if port_id in self.client.ports:
385            self.client.ports[port_id].async_event_port_paused()
386
387
388    def __async_event_port_resumed (self, port_id):
389        if port_id in self.client.ports:
390            self.client.ports[port_id].async_event_port_resumed()
391
392    def __async_event_port_acquired (self, port_id, who):
393        if port_id in self.client.ports:
394            self.client.ports[port_id].async_event_acquired(who)
395
396    def __async_event_port_released (self, port_id):
397        if port_id in self.client.ports:
398            self.client.ports[port_id].async_event_released()
399
400    def __async_event_server_stopped (self):
401        self.client.connected = False
402
403    def __async_event_port_attr_changed (self, port_id, attr):
404        if port_id in self.client.ports:
405            return self.client.ports[port_id].async_event_port_attr_changed(attr)
406
407    # add event to log
408    def __add_event_log (self, origin, ev_type, msg, show = False):
409
410        event = Event(origin, ev_type, msg)
411        self.events.append(event)
412        if show:
413            self.logger.async_log("\n\n{0}".format(str(event)))
414
415
416
417
418
419############################     RPC layer     #############################
420############################                   #############################
421############################                   #############################
422
423class CCommLink(object):
424    """Describes the connectivity of the stateless client method"""
425    def __init__(self, server="localhost", port=5050, virtual=False, client = None):
426        self.virtual = virtual
427        self.server = server
428        self.port = port
429        self.rpc_link = JsonRpcClient(self.server, self.port, client)
430
431    @property
432    def is_connected(self):
433        if not self.virtual:
434            return self.rpc_link.connected
435        else:
436            return True
437
438    def get_server (self):
439        return self.server
440
441    def get_port (self):
442        return self.port
443
444    def connect(self):
445        if not self.virtual:
446            return self.rpc_link.connect()
447
448    def disconnect(self):
449        if not self.virtual:
450            return self.rpc_link.disconnect()
451
452    def transmit(self, method_name, params = None, api_class = 'core'):
453        if self.virtual:
454            self._prompt_virtual_tx_msg()
455            _, msg = self.rpc_link.create_jsonrpc_v2(method_name, params, api_class)
456            print(msg)
457            return
458        else:
459            return self.rpc_link.invoke_rpc_method(method_name, params, api_class)
460
461    def transmit_batch(self, batch_list):
462        if self.virtual:
463            self._prompt_virtual_tx_msg()
464            print([msg
465                   for _, msg in [self.rpc_link.create_jsonrpc_v2(command.method, command.params, command.api_class)
466                                  for command in batch_list]])
467        else:
468            batch = self.rpc_link.create_batch()
469            for command in batch_list:
470                batch.add(command.method, command.params, command.api_class)
471            # invoke the batch
472            return batch.invoke()
473
474    def _prompt_virtual_tx_msg(self):
475        print("Transmitting virtually over tcp://{server}:{port}".format(server=self.server,
476                                                                         port=self.port))
477
478
479
480############################     client     #############################
481############################                #############################
482############################                #############################
483
484class STLClient(object):
485    """TRex Stateless client object - gives operations per TRex/user"""
486
487    # different modes for attaching traffic to ports
488    CORE_MASK_SPLIT = 1
489    CORE_MASK_PIN   = 2
490
491    def __init__(self,
492                 username = common.get_current_user(),
493                 server = "localhost",
494                 sync_port = 4501,
495                 async_port = 4500,
496                 verbose_level = LoggerApi.VERBOSE_QUIET,
497                 logger = None,
498                 virtual = False):
499        """
500        Configure the connection settings
501
502        :parameters:
503             username : string
504                the user name, for example imarom
505
506              server  : string
507                the server name or ip
508
509              sync_port : int
510                the RPC port
511
512              async_port : int
513                the ASYNC port
514
515        .. code-block:: python
516
517            # Example
518
519            # connect to local TRex server
520            c = STLClient()
521
522            # connect to remote server trex-remote-server
523            c = STLClient(server = "trex-remote-server" )
524
525            c = STLClient(server = "10.0.0.10" )
526
527            # verbose mode
528            c = STLClient(server = "10.0.0.10", verbose_level = LoggerApi.VERBOSE_HIGH )
529
530            # change user name
531            c = STLClient(username = "root",server = "10.0.0.10", verbose_level = LoggerApi.VERBOSE_HIGH )
532
533            c.connect()
534
535            c.disconnect()
536
537        """
538
539        self.username   = username
540
541        # init objects
542        self.ports = {}
543        self.server_version = {}
544        self.system_info = {}
545        self.session_id = random.getrandbits(32)
546        self.connected = False
547
548        # API classes
549        self.api_vers = [ {'type': 'core', 'major': 2, 'minor': 3 } ]
550        self.api_h = {'core': None}
551
552        # logger
553        self.logger = DefaultLogger() if not logger else logger
554
555        # initial verbose
556        self.logger.set_verbose(verbose_level)
557
558        # low level RPC layer
559        self.comm_link = CCommLink(server,
560                                   sync_port,
561                                   virtual,
562                                   self)
563
564        # async event handler manager
565        self.event_handler = EventsHandler(self)
566
567        # async subscriber level
568        self.async_client = CTRexAsyncClient(server,
569                                             async_port,
570                                             self)
571
572
573
574
575        # stats
576        self.connection_info = {"username":   username,
577                                "server":     server,
578                                "sync_port":  sync_port,
579                                "async_port": async_port,
580                                "virtual":    virtual}
581
582
583        self.global_stats = trex_stl_stats.CGlobalStats(self.connection_info,
584                                                        self.server_version,
585                                                        self.ports,
586                                                        self.event_handler)
587
588        self.flow_stats = trex_stl_stats.CRxStats(self.ports)
589
590        self.latency_stats = trex_stl_stats.CLatencyStats(self.ports)
591
592        self.util_stats = trex_stl_stats.CUtilStats(self)
593
594        self.xstats = trex_stl_stats.CXStats(self)
595
596        self.stats_generator = trex_stl_stats.CTRexInfoGenerator(self.global_stats,
597                                                                 self.ports,
598                                                                 self.flow_stats,
599                                                                 self.latency_stats,
600                                                                 self.util_stats,
601                                                                 self.xstats,
602                                                                 self.async_client.monitor)
603
604
605
606
607    ############# private functions - used by the class itself ###########
608
609    # some preprocessing for port argument
610    def __ports (self, port_id_list):
611
612        # none means all
613        if port_id_list == None:
614            return range(0, self.get_port_count())
615
616        # always list
617        if isinstance(port_id_list, int):
618            port_id_list = [port_id_list]
619
620        if not isinstance(port_id_list, list):
621             raise ValueError("Bad port id list: {0}".format(port_id_list))
622
623        for port_id in port_id_list:
624            if not isinstance(port_id, int) or (port_id < 0) or (port_id > self.get_port_count()):
625                raise ValueError("Bad port id {0}".format(port_id))
626
627        return port_id_list
628
629
630    # sync ports
631    def __sync_ports (self, port_id_list = None, force = False):
632        port_id_list = self.__ports(port_id_list)
633
634        rc = RC()
635
636        for port_id in port_id_list:
637            rc.add(self.ports[port_id].sync())
638
639        return rc
640
641    # acquire ports, if port_list is none - get all
642    def __acquire (self, port_id_list = None, force = False, sync_streams = True):
643        port_id_list = self.__ports(port_id_list)
644
645        rc = RC()
646
647        for port_id in port_id_list:
648            rc.add(self.ports[port_id].acquire(force, sync_streams))
649
650        return rc
651
652    # release ports
653    def __release (self, port_id_list = None):
654        port_id_list = self.__ports(port_id_list)
655
656        rc = RC()
657
658        for port_id in port_id_list:
659            rc.add(self.ports[port_id].release())
660
661        return rc
662
663
664    def __add_streams(self, stream_list, port_id_list = None):
665
666        port_id_list = self.__ports(port_id_list)
667
668        rc = RC()
669
670        for port_id in port_id_list:
671            rc.add(self.ports[port_id].add_streams(stream_list))
672
673        return rc
674
675
676
677    def __remove_streams(self, stream_id_list, port_id_list = None):
678
679        port_id_list = self.__ports(port_id_list)
680
681        rc = RC()
682
683        for port_id in port_id_list:
684            rc.add(self.ports[port_id].remove_streams(stream_id_list))
685
686        return rc
687
688
689
690    def __remove_all_streams(self, port_id_list = None):
691        port_id_list = self.__ports(port_id_list)
692
693        rc = RC()
694
695        for port_id in port_id_list:
696            rc.add(self.ports[port_id].remove_all_streams())
697
698        return rc
699
700
701    def __get_stream(self, stream_id, port_id, get_pkt = False):
702
703        return self.ports[port_id].get_stream(stream_id)
704
705
706    def __get_all_streams(self, port_id, get_pkt = False):
707
708        return self.ports[port_id].get_all_streams()
709
710
711    def __get_stream_id_list(self, port_id):
712
713        return self.ports[port_id].get_stream_id_list()
714
715
716    def __start (self,
717                 multiplier,
718                 duration,
719                 port_id_list,
720                 force,
721                 core_mask):
722
723        port_id_list = self.__ports(port_id_list)
724
725        rc = RC()
726
727
728        for port_id in port_id_list:
729            rc.add(self.ports[port_id].start(multiplier,
730                                             duration,
731                                             force,
732                                             core_mask[port_id]))
733
734        return rc
735
736
737    def __resume (self, port_id_list = None, force = False):
738
739        port_id_list = self.__ports(port_id_list)
740        rc = RC()
741
742        for port_id in port_id_list:
743            rc.add(self.ports[port_id].resume())
744
745        return rc
746
747    def __pause (self, port_id_list = None, force = False):
748
749        port_id_list = self.__ports(port_id_list)
750        rc = RC()
751
752        for port_id in port_id_list:
753            rc.add(self.ports[port_id].pause())
754
755        return rc
756
757
758    def __stop (self, port_id_list = None, force = False):
759
760        port_id_list = self.__ports(port_id_list)
761        rc = RC()
762
763        for port_id in port_id_list:
764            rc.add(self.ports[port_id].stop(force))
765
766        return rc
767
768
769    def __update (self, mult, port_id_list = None, force = False):
770
771        port_id_list = self.__ports(port_id_list)
772        rc = RC()
773
774        for port_id in port_id_list:
775            rc.add(self.ports[port_id].update(mult, force))
776
777        return rc
778
779
780    def __push_remote (self, pcap_filename, port_id_list, ipg_usec, speedup, count, duration, is_dual, min_ipg_usec):
781
782        port_id_list = self.__ports(port_id_list)
783        rc = RC()
784
785        for port_id in port_id_list:
786
787            # for dual, provide the slave handler as well
788            slave_handler = self.ports[port_id ^ 0x1].handler if is_dual else ""
789
790            rc.add(self.ports[port_id].push_remote(pcap_filename,
791                                                   ipg_usec,
792                                                   speedup,
793                                                   count,
794                                                   duration,
795                                                   is_dual,
796                                                   slave_handler,
797                                                   min_ipg_usec))
798
799        return rc
800
801
802    def __validate (self, port_id_list = None):
803        port_id_list = self.__ports(port_id_list)
804
805        rc = RC()
806
807        for port_id in port_id_list:
808            rc.add(self.ports[port_id].validate())
809
810        return rc
811
812
813    def __resolve (self, port_id_list = None, retries = 0):
814        port_id_list = self.__ports(port_id_list)
815
816        rc = RC()
817
818        for port_id in port_id_list:
819            rc.add(self.ports[port_id].arp_resolve(retries))
820
821        return rc
822
823
824    def __set_port_attr (self, port_id_list = None, attr_dict = None):
825
826        port_id_list = self.__ports(port_id_list)
827        rc = RC()
828
829        for port_id, port_attr_dict in zip(port_id_list, attr_dict):
830            rc.add(self.ports[port_id].set_attr(**port_attr_dict))
831
832        return rc
833
834
835    def __set_rx_sniffer (self, port_id_list, base_filename, limit):
836        port_id_list = self.__ports(port_id_list)
837        rc = RC()
838
839        for port_id in port_id_list:
840            head, tail = os.path.splitext(base_filename)
841            filename = "{0}-{1}{2}".format(head, port_id, tail)
842            rc.add(self.ports[port_id].set_rx_sniffer(filename, limit))
843
844        return rc
845
846
847    def __remove_rx_sniffer (self, port_id_list):
848        port_id_list = self.__ports(port_id_list)
849        rc = RC()
850
851        for port_id in port_id_list:
852            rc.add(self.ports[port_id].remove_rx_sniffer())
853
854        return rc
855
856    def __set_rx_queue (self, port_id_list, size):
857        port_id_list = self.__ports(port_id_list)
858        rc = RC()
859
860        for port_id in port_id_list:
861            rc.add(self.ports[port_id].set_rx_queue(size))
862
863        return rc
864
865    def __remove_rx_queue (self, port_id_list):
866        port_id_list = self.__ports(port_id_list)
867        rc = RC()
868
869        for port_id in port_id_list:
870            rc.add(self.ports[port_id].remove_rx_queue())
871
872        return rc
873
874    def __get_rx_queue_pkts (self, port_id_list):
875        port_id_list = self.__ports(port_id_list)
876        rc = RC()
877
878        for port_id in port_id_list:
879            rc.add(self.ports[port_id].get_rx_queue_pkts())
880
881        return rc
882
883    def __set_service_mode (self, port_id_list, enabled):
884        port_id_list = self.__ports(port_id_list)
885        rc = RC()
886
887        for port_id in port_id_list:
888            rc.add(self.ports[port_id].set_service_mode(enabled))
889
890        return rc
891
892
893    # connect to server
894    def __connect(self):
895
896        # first disconnect if already connected
897        if self.is_connected():
898            self.__disconnect()
899
900        # clear this flag
901        self.connected = False
902
903        # connect sync channel
904        self.logger.pre_cmd("Connecting to RPC server on {0}:{1}".format(self.connection_info['server'], self.connection_info['sync_port']))
905        rc = self.comm_link.connect()
906        self.logger.post_cmd(rc)
907
908        if not rc:
909            return rc
910
911
912        # API sync
913        rc = self._transmit("api_sync", params = {'api_vers': self.api_vers}, api_class = None)
914        if not rc:
915            return rc
916
917        # decode
918        for api in rc.data()['api_vers']:
919            self.api_h[ api['type'] ] = api['api_h']
920
921
922        # version
923        rc = self._transmit("get_version")
924        if not rc:
925            return rc
926
927        self.server_version = rc.data()
928        self.global_stats.server_version = rc.data()
929
930        # cache system info
931        rc = self._transmit("get_system_info")
932        if not rc:
933            return rc
934
935        self.system_info = rc.data()
936        self.global_stats.system_info = rc.data()
937
938        # cache supported commands
939        rc = self._transmit("get_supported_cmds")
940        if not rc:
941            return rc
942
943        self.supported_cmds = sorted(rc.data())
944
945        # create ports
946        for port_id in range(self.system_info["port_count"]):
947            info = self.system_info['ports'][port_id]
948
949            self.ports[port_id] = Port(port_id,
950                                       self.username,
951                                       self.comm_link,
952                                       self.session_id,
953                                       info)
954
955
956        # sync the ports
957        rc = self.__sync_ports()
958        if not rc:
959            return rc
960
961
962        # connect async channel
963        self.logger.pre_cmd("Connecting to publisher server on {0}:{1}".format(self.connection_info['server'], self.connection_info['async_port']))
964        rc = self.async_client.connect()
965        self.logger.post_cmd(rc)
966
967        if not rc:
968            return rc
969
970        self.connected = True
971
972        return RC_OK()
973
974
975    # disconenct from server
976    def __disconnect(self, release_ports = True):
977        # release any previous acquired ports
978        if self.is_connected() and release_ports:
979            self.__release(self.get_acquired_ports())
980
981        self.comm_link.disconnect()
982        self.async_client.disconnect()
983
984        self.connected = False
985
986        return RC_OK()
987
988
989    # clear stats
990    def __clear_stats(self, port_id_list, clear_global, clear_flow_stats, clear_latency_stats, clear_xstats):
991
992        # we must be sync with the server
993        self.async_client.barrier()
994
995        for port_id in port_id_list:
996            self.ports[port_id].clear_stats()
997
998        if clear_global:
999            self.global_stats.clear_stats()
1000
1001        if clear_flow_stats:
1002            self.flow_stats.clear_stats()
1003
1004        if clear_latency_stats:
1005            self.latency_stats.clear_stats()
1006
1007        if clear_xstats:
1008            self.xstats.clear_stats()
1009
1010        self.logger.log_cmd("Clearing stats on port(s) {0}:".format(port_id_list))
1011
1012        return RC
1013
1014
1015    # get stats
1016    def __get_stats (self, port_id_list):
1017        stats = {}
1018
1019        stats['global'] = self.global_stats.get_stats()
1020
1021        total = {}
1022        for port_id in port_id_list:
1023            port_stats = self.ports[port_id].get_stats()
1024            stats[port_id] = port_stats
1025
1026            for k, v in port_stats.items():
1027                if not k in total:
1028                    total[k] = v
1029                else:
1030                    total[k] += v
1031
1032        stats['total'] = total
1033
1034        stats['flow_stats'] = self.flow_stats.get_stats()
1035        stats['latency'] = self.latency_stats.get_stats()
1036
1037        return stats
1038
1039
1040    def __decode_core_mask (self, ports, core_mask):
1041
1042        # predefined modes
1043        if isinstance(core_mask, int):
1044            if core_mask not in [self.CORE_MASK_PIN, self.CORE_MASK_SPLIT]:
1045                raise STLError("'core_mask' can be either CORE_MASK_PIN, CORE_MASK_SPLIT or a list of masks")
1046
1047            decoded_mask = {}
1048            for port in ports:
1049                # a pin mode was requested and we have
1050                # the second port from the group in the start list
1051                if (core_mask == self.CORE_MASK_PIN) and ( (port ^ 0x1) in ports ):
1052                    decoded_mask[port] = 0x55555555 if( port % 2) == 0 else 0xAAAAAAAA
1053                else:
1054                    decoded_mask[port] = None
1055
1056            return decoded_mask
1057
1058        # list of masks
1059        elif isinstance(core_mask, list):
1060            if len(ports) != len(core_mask):
1061                raise STLError("'core_mask' list must be the same length as 'ports' list")
1062
1063            decoded_mask = {}
1064            for i, port in enumerate(ports):
1065                decoded_mask[port] = core_mask[i]
1066
1067            return decoded_mask
1068
1069
1070
1071    ############ functions used by other classes but not users ##############
1072
1073    def _validate_port_list (self, port_id_list):
1074        # listfiy single int
1075        if isinstance(port_id_list, int):
1076            port_id_list = [port_id_list]
1077
1078        # should be a list
1079        if not isinstance(port_id_list, list):
1080            raise STLTypeError('port_id_list', type(port_id_list), list)
1081
1082        if not port_id_list:
1083            raise STLError('No ports provided')
1084
1085        valid_ports = self.get_all_ports()
1086        for port_id in port_id_list:
1087            if not port_id in valid_ports:
1088                raise STLError("Port ID '{0}' is not a valid port ID - valid values: {1}".format(port_id, valid_ports))
1089
1090        return list_remove_dup(port_id_list)
1091
1092
1093    # transmit request on the RPC link
1094    def _transmit(self, method_name, params = None, api_class = 'core'):
1095        return self.comm_link.transmit(method_name, params, api_class)
1096
1097    # transmit batch request on the RPC link
1098    def _transmit_batch(self, batch_list):
1099        return self.comm_link.transmit_batch(batch_list)
1100
1101    # stats
1102    def _get_formatted_stats(self, port_id_list, stats_mask = trex_stl_stats.COMPACT):
1103
1104        stats_opts = common.list_intersect(trex_stl_stats.ALL_STATS_OPTS, stats_mask)
1105
1106        stats_obj = OrderedDict()
1107        for stats_type in stats_opts:
1108            stats_obj.update(self.stats_generator.generate_single_statistic(port_id_list, stats_type))
1109
1110        return stats_obj
1111
1112    def _get_streams(self, port_id_list, streams_mask=set()):
1113
1114        streams_obj = self.stats_generator.generate_streams_info(port_id_list, streams_mask)
1115
1116        return streams_obj
1117
1118
1119    def _invalidate_stats (self, port_id_list):
1120        for port_id in port_id_list:
1121            self.ports[port_id].invalidate_stats()
1122
1123        self.global_stats.invalidate()
1124        self.flow_stats.invalidate()
1125
1126        return RC_OK()
1127
1128
1129    # remove all RX filters in a safe manner
1130    def _remove_rx_filters (self, ports, rx_delay_ms):
1131
1132        # get the enabled RX ports
1133        rx_ports = [port_id for port_id in ports if self.ports[port_id].has_rx_enabled()]
1134
1135        if not rx_ports:
1136            return RC_OK()
1137
1138        # block while any RX configured port has not yet have it's delay expired
1139        while any([not self.ports[port_id].has_rx_delay_expired(rx_delay_ms) for port_id in rx_ports]):
1140            time.sleep(0.01)
1141
1142        # remove RX filters
1143        rc = RC()
1144        for port_id in rx_ports:
1145            rc.add(self.ports[port_id].remove_rx_filters())
1146
1147        return rc
1148
1149
1150    #################################
1151    # ------ private methods ------ #
1152    @staticmethod
1153    def __get_mask_keys(ok_values={True}, **kwargs):
1154        masked_keys = set()
1155        for key, val in kwargs.items():
1156            if val in ok_values:
1157                masked_keys.add(key)
1158        return masked_keys
1159
1160    @staticmethod
1161    def __filter_namespace_args(namespace, ok_values):
1162        return {k: v for k, v in namespace.__dict__.items() if k in ok_values}
1163
1164
1165    # API decorator - double wrap because of argument
1166    def __api_check(connected = True):
1167
1168        def wrap (f):
1169            @wraps(f)
1170            def wrap2(*args, **kwargs):
1171                client = args[0]
1172
1173                func_name = f.__name__
1174
1175                # check connection
1176                if connected and not client.is_connected():
1177                    raise STLStateError(func_name, 'disconnected')
1178
1179                try:
1180                    ret = f(*args, **kwargs)
1181                except KeyboardInterrupt as e:
1182                    raise STLError("Interrupted by a keyboard signal (probably ctrl + c)")
1183
1184                return ret
1185            return wrap2
1186
1187        return wrap
1188
1189
1190
1191    ############################     API     #############################
1192    ############################             #############################
1193    ############################             #############################
1194    def __enter__ (self):
1195        self.connect()
1196        self.acquire(force = True)
1197        self.reset()
1198        return self
1199
1200    def __exit__ (self, type, value, traceback):
1201        if self.get_active_ports():
1202            self.stop(self.get_active_ports())
1203        self.disconnect()
1204
1205    ############################   Getters   #############################
1206    ############################             #############################
1207    ############################             #############################
1208
1209
1210    # return verbose level of the logger
1211    def get_verbose (self):
1212        """
1213        Get the verbose mode
1214
1215        :parameters:
1216          none
1217
1218        :return:
1219            Get the verbose mode as Bool
1220
1221        :raises:
1222          None
1223
1224        """
1225        return self.logger.get_verbose()
1226
1227    # is the client on read only mode ?
1228    def is_all_ports_acquired (self):
1229        """
1230         is_all_ports_acquired
1231
1232        :parameters:
1233          None
1234
1235        :return:
1236            Returns True if all ports are acquired
1237
1238        :raises:
1239          None
1240
1241        """
1242
1243        return (self.get_all_ports() == self.get_acquired_ports())
1244
1245
1246    # is the client connected ?
1247    def is_connected (self):
1248        """
1249
1250        :parameters:
1251          None
1252
1253        :return:
1254            is_connected
1255
1256        :raises:
1257          None
1258
1259        """
1260
1261        return self.connected and self.comm_link.is_connected
1262
1263
1264    # get connection info
1265    def get_connection_info (self):
1266        """
1267
1268        :parameters:
1269          None
1270
1271        :return:
1272            Connection dict
1273
1274        :raises:
1275          None
1276
1277        """
1278
1279        return self.connection_info
1280
1281
1282    # get supported commands by the server
1283    def get_server_supported_cmds(self):
1284        """
1285
1286        :parameters:
1287          None
1288
1289        :return:
1290            Connection dict
1291
1292        :raises:
1293          None
1294
1295        """
1296
1297        return self.supported_cmds
1298
1299    # get server version
1300    def get_server_version(self):
1301        """
1302
1303        :parameters:
1304          None
1305
1306        :return:
1307            Connection dict
1308
1309        :raises:
1310          None
1311
1312        """
1313
1314        return self.server_version
1315
1316    # get server system info
1317    def get_server_system_info(self):
1318        """
1319
1320        :parameters:
1321          None
1322
1323        :return:
1324            Connection dict
1325
1326        :raises:
1327          None
1328
1329        """
1330
1331        return self.system_info
1332
1333    # get port count
1334    def get_port_count(self):
1335        """
1336
1337        :parameters:
1338          None
1339
1340        :return:
1341            Connection dict
1342
1343        :raises:
1344          None
1345
1346        """
1347
1348        return len(self.ports)
1349
1350
1351    # returns the port object
1352    def get_port (self, port_id):
1353        port = self.ports.get(port_id, None)
1354        if (port != None):
1355            return port
1356        else:
1357            raise STLArgumentError('port id', port_id, valid_values = self.get_all_ports())
1358
1359
1360    # get all ports as IDs
1361    def get_all_ports (self):
1362        """
1363
1364        :parameters:
1365          None
1366
1367        :return:
1368            Connection dict
1369
1370        :raises:
1371          None
1372
1373        """
1374
1375        return list(self.ports)
1376
1377    # get all acquired ports
1378    def get_acquired_ports(self):
1379        return [port_id
1380                for port_id, port_obj in self.ports.items()
1381                if port_obj.is_acquired()]
1382
1383    # get all active ports (TX or pause)
1384    def get_active_ports(self, owned = True):
1385        if owned:
1386            return [port_id
1387                    for port_id, port_obj in self.ports.items()
1388                    if port_obj.is_active() and port_obj.is_acquired()]
1389        else:
1390            return [port_id
1391                    for port_id, port_obj in self.ports.items()
1392                    if port_obj.is_active()]
1393
1394
1395    def get_resolvable_ports (self):
1396         return [port_id
1397                for port_id, port_obj in self.ports.items()
1398                if port_obj.is_acquired() and port_obj.get_dst_addr()['ipv4'] is not None]
1399
1400
1401    def get_service_enabled_ports(self):
1402        return [port_id
1403                for port_id, port_obj in self.ports.items()
1404                if port_obj.is_acquired() and port_obj.is_service_mode_on()]
1405
1406
1407    # get paused ports
1408    def get_paused_ports (self, owned = True):
1409        if owned:
1410            return [port_id
1411                    for port_id, port_obj in self.ports.items()
1412                    if port_obj.is_paused() and port_obj.is_acquired()]
1413        else:
1414            return [port_id
1415                    for port_id, port_obj in self.ports.items()
1416                    if port_obj.is_paused()]
1417
1418
1419    # get all TX ports
1420    def get_transmitting_ports (self, owned = True):
1421        if owned:
1422            return [port_id
1423                    for port_id, port_obj in self.ports.items()
1424                    if port_obj.is_transmitting() and port_obj.is_acquired()]
1425        else:
1426            return [port_id
1427                    for port_id, port_obj in self.ports.items()
1428                    if port_obj.is_transmitting()]
1429
1430
1431    # get stats
1432    @__api_check(True)
1433    def get_stats (self, ports = None, sync_now = True):
1434        """
1435        Return dictionary containing statistics information gathered from the server.
1436
1437        :parameters:
1438
1439          ports - List of ports to retreive stats on.
1440                  If None, assume the request is for all acquired ports.
1441
1442          sync_now - Boolean - If true, create a call to the server to get latest stats, and wait for result to arrive. Otherwise, return last stats saved in client cache.
1443                            Downside of putting True is a slight delay (few 10th msecs) in getting the result. For practical uses, value should be True.
1444        :return:
1445            Statistics dictionary of dictionaries with the following format:
1446
1447            ===============================  ===============
1448            key                               Meaning
1449            ===============================  ===============
1450            :ref:`numbers (0,1,..<total>`    Statistcs per port number
1451            :ref:`total <total>`             Sum of port statistics
1452            :ref:`flow_stats <flow_stats>`   Per flow statistics
1453            :ref:`global <global>`           Global statistics
1454            :ref:`latency <latency>`         Per flow statistics regarding flow latency
1455            ===============================  ===============
1456
1457            Below is description of each of the inner dictionaries.
1458
1459            .. _total:
1460
1461            **total** and per port statistics contain dictionary with following format.
1462
1463            Most of the bytes counters (unless specified otherwise) are in L2 layer, including the Ethernet FCS. e.g. minimum packet size is 64 bytes
1464
1465            ===============================  ===============
1466            key                               Meaning
1467            ===============================  ===============
1468            ibytes                           Number of input bytes
1469            ierrors                          Number of input errors
1470            ipackets                         Number of input packets
1471            obytes                           Number of output bytes
1472            oerrors                          Number of output errors
1473            opackets                         Number of output packets
1474            rx_bps                           Receive bytes per second rate (L2 layer)
1475            rx_pps                           Receive packet per second rate
1476            tx_bps                           Transmit bytes per second rate (L2 layer)
1477            tx_pps                           Transmit packet per second rate
1478            ===============================  ===============
1479
1480            .. _flow_stats:
1481
1482            **flow_stats** contains :ref:`global dictionary <flow_stats_global>`, and dictionaries per packet group id (pg id). See structures below.
1483
1484            **per pg_id flow stat** dictionaries have following structure:
1485
1486            =================   ===============
1487            key                 Meaning
1488            =================   ===============
1489            rx_bps              Received bytes per second rate
1490            rx_bps_l1           Received bytes per second rate, including layer one
1491            rx_bytes            Total number of received bytes
1492            rx_pkts             Total number of received packets
1493            rx_pps              Received packets per second
1494            tx_bps              Transmit bytes per second rate
1495            tx_bps_l1           Transmit bytes per second rate, including layer one
1496            tx_bytes            Total number of sent bytes
1497            tx_pkts             Total number of sent packets
1498            tx_pps              Transmit packets per second rate
1499            =================   ===============
1500
1501            .. _flow_stats_global:
1502
1503            **global flow stats** dictionary has the following structure:
1504
1505            =================   ===============
1506            key                 Meaning
1507            =================   ===============
1508            rx_err              Number of flow statistics packets received that we could not associate to any pg_id. This can happen if latency on the used setup is large. See :ref:`wait_on_traffic <wait_on_traffic>` rx_delay_ms parameter for details.
1509            tx_err              Number of flow statistics packets transmitted that we could not associate to any pg_id. This is never expected. If you see this different than 0, please report.
1510            =================   ===============
1511
1512            .. _global:
1513
1514            **global**
1515
1516            =================   ===============
1517            key                 Meaning
1518            =================   ===============
1519            bw_per_core         Estimated byte rate Trex can support per core. This is calculated by extrapolation of current rate and load on transmitting cores.
1520            cpu_util            Estimate of the average utilization percentage of the transimitting cores
1521            queue_full          Total number of packets transmitted while the NIC TX queue was full. The packets will be transmitted, eventually, but will create high CPU%due to polling the queue.  This usually indicates that the rate we trying to transmit is too high for this port.
1522            rx_cpu_util         Estimate of the utilization percentage of the core handling RX traffic. Too high value of this CPU utilization could cause drop of latency streams.
1523            rx_drop_bps         Received bytes per second drop rate
1524            rx_bps              Received bytes per second rate
1525            rx_pps              Received packets per second rate
1526            tx_bps              Transmit bytes per second rate
1527            tx_pps              Transmit packets per second rate
1528            =================   ===============
1529
1530            .. _latency:
1531
1532            **latency** contains :ref:`global dictionary <lat_stats_global>`, and dictionaries per packet group id (pg id). Each one with the following structure.
1533
1534            **per pg_id latency stat** dictionaries have following structure:
1535
1536            ===========================          ===============
1537            key                                  Meaning
1538            ===========================          ===============
1539            :ref:`err_cntrs<err-cntrs>`          Counters describing errors that occured with this pg id
1540            :ref:`latency<lat_inner>`            Information regarding packet latency
1541            ===========================          ===============
1542
1543            Following are the inner dictionaries of latency
1544
1545            .. _err-cntrs:
1546
1547            **err-cntrs**
1548
1549            =================   ===============
1550            key                 Meaning (see better explanation below the table)
1551            =================   ===============
1552            dropped             How many packets were dropped (estimation)
1553            dup                 How many packets were duplicated.
1554            out_of_order        How many packets we received out of order.
1555            seq_too_high        How many events of packet with sequence number too high we saw.
1556            seq_too_low         How many events of packet with sequence number too low we saw.
1557            =================   ===============
1558
1559            For calculating packet error events, we add sequence number to each packet's payload. We decide what went wrong only according to sequence number
1560            of last packet received and that of the previous packet. 'seq_too_low' and 'seq_too_high' count events we see. 'dup', 'out_of_order' and 'dropped'
1561            are heuristics we apply to try and understand what happened. They will be accurate in common error scenarios.
1562            We describe few scenarios below to help understand this.
1563
1564            Scenario 1: Received packet with seq num 10, and another one with seq num 10. We increment 'dup' and 'seq_too_low' by 1.
1565
1566            Scenario 2: Received pacekt with seq num 10 and then packet with seq num 15. We assume 4 packets were dropped, and increment 'dropped' by 4, and 'seq_too_high' by 1.
1567            We expect next packet to arrive with sequence number 16.
1568
1569            Scenario 2 continue: Received packet with seq num 11. We increment 'seq_too_low' by 1. We increment 'out_of_order' by 1. We *decrement* 'dropped' by 1.
1570            (We assume here that one of the packets we considered as dropped before, actually arrived out of order).
1571
1572
1573            .. _lat_inner:
1574
1575            **latency**
1576
1577            =================   ===============
1578            key                 Meaning
1579            =================   ===============
1580            average             Average latency over the stream lifetime (usec).Low pass filter is applied to the last window average.It is computed each sampling period by following formula: <average> = <prev average>/2 + <last sampling period average>/2
1581            histogram           Dictionary describing logarithmic distribution histogram of packet latencies. Keys in the dictionary represent range of latencies (in usec). Values are the total number of packets received in this latency range. For example, an entry {100:13} would mean that we saw 13 packets with latency in the range between 100 and 200 usec.
1582            jitter              Jitter of latency samples, computed as described in :rfc:`3550#appendix-A.8`
1583            last_max            Maximum latency measured between last two data reads from server (0.5 sec window).
1584            total_max           Maximum latency measured over the stream lifetime (in usec).
1585            total_min           Minimum latency measured over the stream lifetime (in usec).
1586            =================   ===============
1587
1588            .. _lat_stats_global:
1589
1590            **global latency stats** dictionary has the following structure:
1591
1592            =================   ===============
1593            key                 Meaning
1594            =================   ===============
1595            old_flow            Number of latency statistics packets received that we could not associate to any pg_id. This can happen if latency on the used setup is large. See :ref:`wait_on_traffic <wait_on_traffic>` rx_delay_ms parameter for details.
1596            bad_hdr             Number of latency packets received with bad latency data. This can happen becuase of garbage packets in the network, or if the DUT causes packet corruption.
1597            =================   ===============
1598
1599        :raises:
1600          None
1601
1602        """
1603        # by default use all acquired ports
1604        ports = ports if ports is not None else self.get_acquired_ports()
1605        ports = self._validate_port_list(ports)
1606
1607        # check async barrier
1608        if not type(sync_now) is bool:
1609            raise STLArgumentError('sync_now', sync_now)
1610
1611
1612        # if the user requested a barrier - use it
1613        if sync_now:
1614            rc = self.async_client.barrier()
1615            if not rc:
1616                raise STLError(rc)
1617
1618        return self.__get_stats(ports)
1619
1620
1621    def get_events (self, ev_type_filter = None):
1622        """
1623        returns all the logged events
1624
1625        :parameters:
1626          ev_type_filter - 'info', 'warning' or a list of those
1627                           default: no filter
1628
1629        :return:
1630            logged events
1631
1632        :raises:
1633          None
1634
1635        """
1636        return self.event_handler.get_events(ev_type_filter)
1637
1638
1639    def get_warnings (self):
1640        """
1641        returns all the warnings logged events
1642
1643        :parameters:
1644          None
1645
1646        :return:
1647            warning logged events
1648
1649        :raises:
1650          None
1651
1652        """
1653        return self.get_events(ev_type_filter = 'warning')
1654
1655
1656    def get_info (self):
1657        """
1658        returns all the info logged events
1659
1660        :parameters:
1661          None
1662
1663        :return:
1664            warning logged events
1665
1666        :raises:
1667          None
1668
1669        """
1670        return self.get_events(ev_type_filter = 'info')
1671
1672
1673    # get port(s) info as a list of dicts
1674    @__api_check(True)
1675    def get_port_info (self, ports = None):
1676
1677        ports = ports if ports is not None else self.get_all_ports()
1678        ports = self._validate_port_list(ports)
1679
1680        return [self.ports[port_id].get_formatted_info() for port_id in ports]
1681
1682
1683    ############################   Commands   #############################
1684    ############################              #############################
1685    ############################              #############################
1686
1687
1688    def set_verbose (self, level):
1689        """
1690            Sets verbose level
1691
1692            :parameters:
1693                level : str
1694                    "high"
1695                    "low"
1696                    "normal"
1697
1698            :raises:
1699                None
1700
1701        """
1702        modes = {'low' : LoggerApi.VERBOSE_QUIET, 'normal': LoggerApi.VERBOSE_REGULAR, 'high': LoggerApi.VERBOSE_HIGH}
1703
1704        if not level in modes.keys():
1705            raise STLArgumentError('level', level)
1706
1707        self.logger.set_verbose(modes[level])
1708
1709
1710    @__api_check(False)
1711    def connect (self):
1712        """
1713
1714            Connects to the TRex server
1715
1716            :parameters:
1717                None
1718
1719            :raises:
1720                + :exc:`STLError`
1721
1722        """
1723
1724        rc = self.__connect()
1725        if not rc:
1726            raise STLError(rc)
1727
1728
1729    @__api_check(False)
1730    def disconnect (self, stop_traffic = True, release_ports = True):
1731        """
1732            Disconnects from the server
1733
1734            :parameters:
1735                stop_traffic : bool
1736                    Attempts to stop traffic before disconnecting.
1737                release_ports : bool
1738                    Attempts to release all the acquired ports.
1739
1740        """
1741
1742        # try to stop ports but do nothing if not possible
1743        if stop_traffic:
1744            try:
1745                self.stop()
1746            except STLError:
1747                pass
1748
1749
1750        self.logger.pre_cmd("Disconnecting from server at '{0}':'{1}'".format(self.connection_info['server'],
1751                                                                              self.connection_info['sync_port']))
1752        rc = self.__disconnect(release_ports)
1753        self.logger.post_cmd(rc)
1754
1755
1756
1757    @__api_check(True)
1758    def acquire (self, ports = None, force = False, sync_streams = True):
1759        """
1760            Acquires ports for executing commands
1761
1762            :parameters:
1763                ports : list
1764                    Ports on which to execute the command
1765
1766                force : bool
1767                    Force acquire the ports.
1768
1769                sync_streams: bool
1770                    sync with the server about the configured streams
1771
1772            :raises:
1773                + :exc:`STLError`
1774
1775        """
1776
1777        # by default use all ports
1778        ports = ports if ports is not None else self.get_all_ports()
1779        ports = self._validate_port_list(ports)
1780
1781        if force:
1782            self.logger.pre_cmd("Force acquiring ports {0}:".format(ports))
1783        else:
1784            self.logger.pre_cmd("Acquiring ports {0}:".format(ports))
1785
1786        rc = self.__acquire(ports, force, sync_streams)
1787
1788        self.logger.post_cmd(rc)
1789
1790        if not rc:
1791            # cleanup
1792            self.__release(ports)
1793            raise STLError(rc)
1794
1795        for port_id in ports:
1796            if not self.ports[port_id].is_resolved():
1797                self.logger.log(format_text('*** Warning - Port {0} destination is unresolved ***'.format(port_id), 'bold'))
1798
1799    @__api_check(True)
1800    def release (self, ports = None):
1801        """
1802            Release ports
1803
1804            :parameters:
1805                ports : list
1806                    Ports on which to execute the command
1807
1808            :raises:
1809                + :exc:`STLError`
1810
1811        """
1812
1813        ports = ports if ports is not None else self.get_acquired_ports()
1814        ports = self._validate_port_list(ports)
1815
1816        self.logger.pre_cmd("Releasing ports {0}:".format(ports))
1817        rc = self.__release(ports)
1818        self.logger.post_cmd(rc)
1819
1820        if not rc:
1821            raise STLError(rc)
1822
1823
1824
1825    @__api_check(True)
1826    def ping_rpc_server(self):
1827        """
1828            Pings the RPC server
1829
1830            :parameters:
1831                 None
1832
1833            :raises:
1834                + :exc:`STLError`
1835
1836        """
1837
1838        self.logger.pre_cmd("Pinging the server on '{0}' port '{1}': ".format(self.connection_info['server'],
1839                                                                              self.connection_info['sync_port']))
1840        rc = self._transmit("ping", api_class = None)
1841
1842        self.logger.post_cmd(rc)
1843
1844        if not rc:
1845            raise STLError(rc)
1846
1847
1848    @__api_check(True)
1849    def set_l2_mode (self, port, dst_mac):
1850        """
1851            Sets the port mode to L2
1852
1853            :parameters:
1854                 port      - the port to set the source address
1855                 dst_mac   - destination MAC
1856            :raises:
1857                + :exc:`STLError`
1858        """
1859
1860        validate_type('port', port, int)
1861        if port not in self.get_all_ports():
1862            raise STLError("port {0} is not a valid port id".format(port))
1863
1864        if not is_valid_mac(dst_mac):
1865            raise STLError("dest_mac is not a valid MAC address: '{0}'".format(dst_mac))
1866
1867        self.logger.pre_cmd("Setting port {0} in L2 mode: ".format(port))
1868        rc = self.ports[port].set_l2_mode(dst_mac)
1869        self.logger.post_cmd(rc)
1870
1871        if not rc:
1872            raise STLError(rc)
1873
1874
1875    @__api_check(True)
1876    def set_l3_mode (self, port, src_ipv4, dst_ipv4):
1877        """
1878            Sets the port mode to L3
1879
1880            :parameters:
1881                 port      - the port to set the source address
1882                 src_ipv4  - IPv4 source address for the port
1883                 dst_ipv4  - IPv4 destination address
1884            :raises:
1885                + :exc:`STLError`
1886        """
1887
1888        validate_type('port', port, int)
1889        if port not in self.get_all_ports():
1890            raise STLError("port {0} is not a valid port id".format(port))
1891
1892        if not is_valid_ipv4(src_ipv4):
1893            raise STLError("src_ipv4 is not a valid IPv4 address: '{0}'".format(src_ipv4))
1894
1895        if not is_valid_ipv4(dst_ipv4):
1896            raise STLError("dst_ipv4 is not a valid IPv4 address: '{0}'".format(dst_ipv4))
1897
1898        self.logger.pre_cmd("Setting port {0} in L3 mode: ".format(port))
1899        rc = self.ports[port].set_l3_mode(src_ipv4, dst_ipv4)
1900        self.logger.post_cmd(rc)
1901
1902        if not rc:
1903            raise STLError(rc)
1904
1905        # try to resolve
1906        with self.logger.supress(level = LoggerApi.VERBOSE_REGULAR_SYNC):
1907            self.logger.pre_cmd("ARP resolving address '{0}': ".format(dst_ipv4))
1908            rc = self.ports[port].arp_resolve(0)
1909            self.logger.post_cmd(rc)
1910            if not rc:
1911                raise STLError(rc)
1912
1913
1914    @__api_check(True)
1915    def ping_ip (self, src_port, dst_ipv4, pkt_size = 64, count = 5):
1916        """
1917            Pings an IP address through a port
1918
1919            :parameters:
1920                 src_port - on which port_id to send the ICMP PING request
1921                 dst_ipv4 - which IP to ping
1922                 pkt_size - packet size to use
1923                 count    - how many times to ping
1924            :raises:
1925                + :exc:`STLError`
1926
1927        """
1928        # validate src port
1929        validate_type('src_port', src_port, int)
1930        if src_port not in self.get_all_ports():
1931            raise STLError("src port is not a valid port id")
1932
1933        if not is_valid_ipv4(dst_ipv4):
1934            raise STLError("dst_ipv4 is not a valid IPv4 address: '{0}'".format(dst_ipv4))
1935
1936        if (pkt_size < 64) or (pkt_size > 9216):
1937            raise STLError("pkt_size should be a value between 64 and 9216: '{0}'".format(pkt_size))
1938
1939        validate_type('count', count, int)
1940
1941        self.logger.pre_cmd("Pinging {0} from port {1} with {2} bytes of data:".format(dst_ipv4,
1942                                                                                       src_port,
1943                                                                                       pkt_size))
1944
1945        # no async messages
1946        with self.logger.supress(level = LoggerApi.VERBOSE_REGULAR_SYNC):
1947            self.logger.log('')
1948            for i in range(count):
1949                rc = self.ports[src_port].ping(ping_ipv4 = dst_ipv4, pkt_size = pkt_size)
1950                if not rc:
1951                    raise STLError(rc)
1952
1953                self.logger.log(rc.data())
1954
1955                if i != (count - 1):
1956                    time.sleep(1)
1957
1958
1959
1960    @__api_check(True)
1961    def server_shutdown (self, force = False):
1962        """
1963            Sends the server a request for total shutdown
1964
1965            :parameters:
1966                force - shutdown server even if some ports are owned by another
1967                        user
1968
1969            :raises:
1970                + :exc:`STLError`
1971
1972        """
1973
1974        self.logger.pre_cmd("Sending shutdown request for the server")
1975
1976        rc = self._transmit("shutdown", params = {'force': force, 'user': self.username})
1977
1978        self.logger.post_cmd(rc)
1979
1980        if not rc:
1981            raise STLError(rc)
1982
1983
1984    @__api_check(True)
1985    def get_active_pgids(self):
1986        """
1987            Get active group IDs
1988
1989            :parameters:
1990                None
1991
1992
1993            :raises:
1994                + :exc:`STLError`
1995
1996        """
1997
1998        self.logger.pre_cmd( "Getting active packet group ids")
1999
2000        rc = self._transmit("get_active_pgids")
2001
2002        self.logger.post_cmd(rc)
2003
2004        if not rc:
2005            raise STLError(rc)
2006
2007        return rc.data()
2008
2009    @__api_check(True)
2010    def get_util_stats(self):
2011        """
2012            Get utilization stats:
2013            History of TRex CPU utilization per thread (list of lists)
2014            MBUFs memory consumption per CPU socket.
2015
2016            :parameters:
2017                None
2018
2019            :raises:
2020                + :exc:`STLError`
2021
2022        """
2023        self.logger.pre_cmd('Getting Utilization stats')
2024        return self.util_stats.get_stats()
2025
2026    @__api_check(True)
2027    def get_xstats(self, port_id):
2028        print(port_id)
2029        """
2030            Get extended stats of port: all the counters as dict.
2031
2032            :parameters:
2033                port_id: int
2034
2035            :returns:
2036                Dict with names of counters as keys and values of uint64. Actual keys may vary per NIC.
2037
2038            :raises:
2039                + :exc:`STLError`
2040
2041        """
2042        self.logger.pre_cmd('Getting xstats')
2043        return self.xstats.get_stats(port_id)
2044
2045
2046    @__api_check(True)
2047    def reset(self, ports = None, restart = False):
2048        """
2049            Force acquire ports, stop the traffic, remove all streams and clear stats
2050
2051            :parameters:
2052                ports : list
2053                   Ports on which to execute the command
2054
2055                restart: bool
2056                   Restart the NICs (link down / up)
2057
2058            :raises:
2059                + :exc:`STLError`
2060
2061        """
2062
2063
2064        ports = ports if ports is not None else self.get_all_ports()
2065        ports = self._validate_port_list(ports)
2066
2067
2068        if restart:
2069            self.logger.pre_cmd("Hard resetting ports {0}:".format(ports))
2070        else:
2071            self.logger.pre_cmd("Resetting ports {0}:".format(ports))
2072
2073
2074        try:
2075            with self.logger.supress():
2076            # force take the port and ignore any streams on it
2077                self.acquire(ports, force = True, sync_streams = False)
2078                self.stop(ports, rx_delay_ms = 0)
2079                self.remove_all_streams(ports)
2080                self.clear_stats(ports)
2081                self.set_port_attr(ports,
2082                                   promiscuous = False,
2083                                   link_up = True if restart else None)
2084                self.set_service_mode(ports, False)
2085                self.remove_rx_sniffer(ports)
2086                self.remove_rx_queue(ports)
2087
2088        except STLError as e:
2089            self.logger.post_cmd(False)
2090            raise e
2091
2092
2093        self.logger.post_cmd(RC_OK())
2094
2095
2096    @__api_check(True)
2097    def remove_all_streams (self, ports = None):
2098        """
2099            remove all streams from port(s)
2100
2101            :parameters:
2102                ports : list
2103                    Ports on which to execute the command
2104
2105
2106            :raises:
2107                + :exc:`STLError`
2108
2109        """
2110
2111
2112        ports = ports if ports is not None else self.get_acquired_ports()
2113        ports = self._validate_port_list(ports)
2114
2115        self.logger.pre_cmd("Removing all streams from port(s) {0}:".format(ports))
2116        rc = self.__remove_all_streams(ports)
2117        self.logger.post_cmd(rc)
2118
2119        if not rc:
2120            raise STLError(rc)
2121
2122
2123    @__api_check(True)
2124    def add_streams (self, streams, ports = None):
2125        """
2126            Add a list of streams to port(s)
2127
2128            :parameters:
2129                ports : list
2130                    Ports on which to execute the command
2131                streams: list
2132                    Streams to attach (or profile)
2133
2134            :returns:
2135                List of stream IDs in order of the stream list
2136
2137            :raises:
2138                + :exc:`STLError`
2139
2140        """
2141
2142
2143        ports = ports if ports is not None else self.get_acquired_ports()
2144        ports = self._validate_port_list(ports)
2145
2146        if isinstance(streams, STLProfile):
2147            streams = streams.get_streams()
2148
2149        # transform single stream
2150        if not isinstance(streams, list):
2151            streams = [streams]
2152
2153        # check streams
2154        if not all([isinstance(stream, STLStream) for stream in streams]):
2155            raise STLArgumentError('streams', streams)
2156
2157        self.logger.pre_cmd("Attaching {0} streams to port(s) {1}:".format(len(streams), ports))
2158        rc = self.__add_streams(streams, ports)
2159        self.logger.post_cmd(rc)
2160
2161        if not rc:
2162            raise STLError(rc)
2163
2164        # return the stream IDs
2165        return rc.data()
2166
2167    @__api_check(True)
2168    def add_profile(self, filename, ports = None, **kwargs):
2169        """ |  Add streams from profile by its type. Supported types are:
2170            |  .py
2171            |  .yaml
2172            |  .pcap file that converted to profile automatically
2173
2174            :parameters:
2175                filename : string
2176                    filename (with path) of the profile
2177                ports : list
2178                    list of ports to add the profile (default: all acquired)
2179                kwargs : dict
2180                    forward those key-value pairs to the profile (tunables)
2181
2182            :returns:
2183                List of stream IDs in order of the stream list
2184
2185            :raises:
2186                + :exc:`STLError`
2187
2188        """
2189
2190        validate_type('filename', filename, basestring)
2191        profile = STLProfile.load(filename, **kwargs)
2192        return self.add_streams(profile.get_streams(), ports)
2193
2194
2195    @__api_check(True)
2196    def remove_streams (self, stream_id_list, ports = None):
2197        """
2198            Remove a list of streams from ports
2199
2200            :parameters:
2201                ports : list
2202                    Ports on which to execute the command
2203                stream_id_list: list
2204                    Stream id list to remove
2205
2206
2207            :raises:
2208                + :exc:`STLError`
2209
2210        """
2211
2212
2213        ports = ports if ports is not None else self.get_acquired_ports()
2214        ports = self._validate_port_list(ports)
2215
2216        # transform single stream
2217        if not isinstance(stream_id_list, list):
2218            stream_id_list = [stream_id_list]
2219
2220        # check streams
2221        for stream_id in stream_id_list:
2222            validate_type('stream_id', stream_id, int)
2223
2224        # remove streams
2225        self.logger.pre_cmd("Removing {0} streams from port(s) {1}:".format(len(stream_id_list), ports))
2226        rc = self.__remove_streams(stream_id_list, ports)
2227        self.logger.post_cmd(rc)
2228
2229        if not rc:
2230            raise STLError(rc)
2231
2232
2233    # common checks for start API
2234    def __pre_start_check (self, ports, force):
2235
2236        # verify link status
2237        ports_link_down = [port_id for port_id in ports if not self.ports[port_id].is_up()]
2238        if ports_link_down and not force:
2239            raise STLError("Port(s) %s - link DOWN - check the connection or specify 'force'" % ports_link_down)
2240
2241        # verify ports are stopped or force stop them
2242        active_ports = [port_id for port_id in ports if self.ports[port_id].is_active()]
2243        if active_ports and not force:
2244            raise STLError("Port(s) {0} are active - please stop them or specify 'force'".format(active_ports))
2245
2246        # warn if ports are not resolved
2247        unresolved_ports = [port_id for port_id in ports if not self.ports[port_id].is_resolved()]
2248        if unresolved_ports and not force:
2249            raise STLError("Port(s) {0} have unresolved destination addresses - please resolve them or specify 'force'".format(unresolved_ports))
2250
2251        if self.get_service_enabled_ports() and not force:
2252            raise STLError("Port(s) {0} are under service mode - please disable service mode or specify 'force'".format(self.get_service_enabled_ports()))
2253
2254
2255    @__api_check(True)
2256    def start (self,
2257               ports = None,
2258               mult = "1",
2259               force = False,
2260               duration = -1,
2261               total = False,
2262               core_mask = CORE_MASK_SPLIT):
2263        """
2264            Start traffic on port(s)
2265
2266            :parameters:
2267                ports : list
2268                    Ports on which to execute the command
2269
2270                mult : str
2271                    Multiplier in a form of pps, bps, or line util in %
2272                    Examples: "5kpps", "10gbps", "85%", "32mbps"
2273
2274                force : bool
2275                    If the ports are not in stopped mode or do not have sufficient bandwidth for the traffic, determines whether to stop the current traffic and force start.
2276                    True: Force start
2277                    False: Do not force start
2278
2279                duration : int
2280                    Limit the run time (seconds)
2281                    -1 = unlimited
2282
2283                total : bool
2284                    Determines whether to divide the configured bandwidth among the ports, or to duplicate the bandwidth for each port.
2285                    True: Divide bandwidth among the ports
2286                    False: Duplicate
2287
2288                core_mask: CORE_MASK_SPLIT, CORE_MASK_PIN or a list of masks (one per port)
2289                    Determine the allocation of cores per port
2290                    In CORE_MASK_SPLIT all the traffic will be divided equally between all the cores
2291                    associated with each port
2292                    In CORE_MASK_PIN, for each dual ports (a group that shares the same cores)
2293                    the cores will be divided half pinned for each port
2294
2295            :raises:
2296                + :exc:`STLError`
2297
2298        """
2299
2300        ports = ports if ports is not None else self.get_acquired_ports()
2301        ports = self._validate_port_list(ports)
2302
2303        validate_type('mult', mult, basestring)
2304        validate_type('force', force, bool)
2305        validate_type('duration', duration, (int, float))
2306        validate_type('total', total, bool)
2307        validate_type('core_mask', core_mask, (int, list))
2308
2309
2310        # some sanity checks before attempting start
2311        self.__pre_start_check(ports, force)
2312
2313        #########################
2314        # decode core mask argument
2315        decoded_mask = self.__decode_core_mask(ports, core_mask)
2316        #######################
2317
2318        # verify multiplier
2319        mult_obj = parsing_opts.decode_multiplier(mult,
2320                                                  allow_update = False,
2321                                                  divide_count = len(ports) if total else 1)
2322        if not mult_obj:
2323            raise STLArgumentError('mult', mult)
2324
2325
2326        # stop active ports if needed
2327        active_ports = list(set(self.get_active_ports()).intersection(ports))
2328        if active_ports and force:
2329            self.stop(active_ports)
2330
2331
2332        # start traffic
2333        self.logger.pre_cmd("Starting traffic on port(s) {0}:".format(ports))
2334        rc = self.__start(mult_obj, duration, ports, force, decoded_mask)
2335        self.logger.post_cmd(rc)
2336
2337        if not rc:
2338            raise STLError(rc)
2339
2340
2341    @__api_check(True)
2342    def stop (self, ports = None, rx_delay_ms = 10):
2343        """
2344            Stop port(s)
2345
2346            :parameters:
2347                ports : list
2348                    Ports on which to execute the command
2349
2350                rx_delay_ms : int
2351                    time to wait until RX filters are removed
2352                    this value should reflect the time it takes
2353                    packets which were transmitted to arrive
2354                    to the destination.
2355                    after this time the RX filters will be removed
2356
2357            :raises:
2358                + :exc:`STLError`
2359
2360        """
2361
2362        if ports is None:
2363            ports = self.get_active_ports()
2364            if not ports:
2365                return
2366
2367        ports = self._validate_port_list(ports)
2368
2369        self.logger.pre_cmd("Stopping traffic on port(s) {0}:".format(ports))
2370        rc = self.__stop(ports)
2371        self.logger.post_cmd(rc)
2372
2373        if not rc:
2374            raise STLError(rc)
2375
2376        # remove any RX filters
2377        rc = self._remove_rx_filters(ports, rx_delay_ms = rx_delay_ms)
2378        if not rc:
2379            raise STLError(rc)
2380
2381
2382    @__api_check(True)
2383    def update (self, ports = None, mult = "1", total = False, force = False):
2384        """
2385            Update traffic on port(s)
2386
2387            :parameters:
2388                ports : list
2389                    Ports on which to execute the command
2390
2391                mult : str
2392                    Multiplier in a form of pps, bps, or line util in %
2393                    Can also specify +/-
2394                    Examples: "5kpps+", "10gbps-", "85%", "32mbps", "20%+"
2395
2396                force : bool
2397                    If the ports are not in stopped mode or do not have sufficient bandwidth for the traffic, determines whether to stop the current traffic and force start.
2398                    True: Force start
2399                    False: Do not force start
2400
2401                total : bool
2402                    Determines whether to divide the configured bandwidth among the ports, or to duplicate the bandwidth for each port.
2403                    True: Divide bandwidth among the ports
2404                    False: Duplicate
2405
2406
2407            :raises:
2408                + :exc:`STLError`
2409
2410        """
2411
2412
2413        ports = ports if ports is not None else self.get_active_ports()
2414        ports = self._validate_port_list(ports)
2415
2416        validate_type('mult', mult, basestring)
2417        validate_type('force', force, bool)
2418        validate_type('total', total, bool)
2419
2420        # verify multiplier
2421        mult_obj = parsing_opts.decode_multiplier(mult,
2422                                                  allow_update = True,
2423                                                  divide_count = len(ports) if total else 1)
2424        if not mult_obj:
2425            raise STLArgumentError('mult', mult)
2426
2427
2428        # call low level functions
2429        self.logger.pre_cmd("Updating traffic on port(s) {0}:".format(ports))
2430        rc = self.__update(mult_obj, ports, force)
2431        self.logger.post_cmd(rc)
2432
2433        if not rc:
2434            raise STLError(rc)
2435
2436
2437
2438    @__api_check(True)
2439    def pause (self, ports = None):
2440        """
2441            Pause traffic on port(s). Works only for ports that are active, and only if all streams are in Continuous mode.
2442
2443            :parameters:
2444                ports : list
2445                    Ports on which to execute the command
2446
2447            :raises:
2448                + :exc:`STLError`
2449
2450        """
2451
2452
2453        ports = ports if ports is not None else self.get_transmitting_ports()
2454        ports = self._validate_port_list(ports)
2455
2456        self.logger.pre_cmd("Pausing traffic on port(s) {0}:".format(ports))
2457        rc = self.__pause(ports)
2458        self.logger.post_cmd(rc)
2459
2460        if not rc:
2461            raise STLError(rc)
2462
2463    @__api_check(True)
2464    def resume (self, ports = None):
2465        """
2466            Resume traffic on port(s)
2467
2468            :parameters:
2469                ports : list
2470                    Ports on which to execute the command
2471
2472            :raises:
2473                + :exc:`STLError`
2474
2475        """
2476
2477
2478        ports = ports if ports is not None else self.get_paused_ports()
2479        ports = self._validate_port_list(ports)
2480
2481
2482        self.logger.pre_cmd("Resume traffic on port(s) {0}:".format(ports))
2483        rc = self.__resume(ports)
2484        self.logger.post_cmd(rc)
2485
2486        if not rc:
2487            raise STLError(rc)
2488
2489
2490    @__api_check(True)
2491    def push_remote (self,
2492                     pcap_filename,
2493                     ports = None,
2494                     ipg_usec = None,
2495                     speedup = 1.0,
2496                     count = 1,
2497                     duration = -1,
2498                     is_dual = False,
2499                     min_ipg_usec = None):
2500        """
2501            Push a remote server-reachable PCAP file
2502            the path must be fullpath accessible to the server
2503
2504            :parameters:
2505                pcap_filename : str
2506                    PCAP file name in full path and accessible to the server
2507
2508                ports : list
2509                    Ports on which to execute the command
2510
2511                ipg_usec : float
2512                    Inter-packet gap in microseconds
2513
2514                speedup : float
2515                    A factor to adjust IPG. effectively IPG = IPG / speedup
2516
2517                count: int
2518                    How many times to transmit the cap
2519
2520                duration: float
2521                    Limit runtime by duration in seconds
2522
2523                is_dual: bool
2524                    Inject from both directions.
2525                    requires ERF file with meta data for direction.
2526                    also requires that all the ports will be in master mode
2527                    with their adjacent ports as slaves
2528
2529                min_ipg_usec : float
2530                    Minimum inter-packet gap in microseconds to guard from too small ipg.
2531
2532            :raises:
2533                + :exc:`STLError`
2534
2535        """
2536        ports = ports if ports is not None else self.get_acquired_ports()
2537        ports = self._validate_port_list(ports)
2538
2539        validate_type('pcap_filename', pcap_filename, basestring)
2540        validate_type('ipg_usec', ipg_usec, (float, int, type(None)))
2541        validate_type('speedup',  speedup, (float, int))
2542        validate_type('count',  count, int)
2543        validate_type('duration', duration, (float, int))
2544        validate_type('is_dual', is_dual, bool)
2545        validate_type('min_ipg_usec', min_ipg_usec, (float, int, type(None)))
2546
2547        # for dual mode check that all are masters
2548        if is_dual:
2549            if not pcap_filename.endswith('erf'):
2550                raise STLError("dual mode: only ERF format is supported for dual mode")
2551
2552            for port in ports:
2553                master = port
2554                slave = port ^ 0x1
2555
2556                if slave in ports:
2557                    raise STLError("dual mode: cannot provide adjacent ports ({0}, {1}) in a batch".format(master, slave))
2558
2559                if not slave in self.get_acquired_ports():
2560                    raise STLError("dual mode: adjacent port {0} must be owned during dual mode".format(slave))
2561
2562
2563        self.logger.pre_cmd("Pushing remote PCAP on port(s) {0}:".format(ports))
2564        rc = self.__push_remote(pcap_filename, ports, ipg_usec, speedup, count, duration, is_dual, min_ipg_usec)
2565        self.logger.post_cmd(rc)
2566
2567        if not rc:
2568            raise STLError(rc)
2569
2570
2571    @__api_check(True)
2572    def push_pcap (self,
2573                   pcap_filename,
2574                   ports = None,
2575                   ipg_usec = None,
2576                   speedup = 1.0,
2577                   count = 1,
2578                   duration = -1,
2579                   force = False,
2580                   vm = None,
2581                   packet_hook = None,
2582                   is_dual = False,
2583                   min_ipg_usec = None):
2584        """
2585            Push a local PCAP to the server
2586            This is equivalent to loading a PCAP file to a profile
2587            and attaching the profile to port(s)
2588
2589            file size is limited to 1MB
2590
2591            :parameters:
2592                pcap_filename : str
2593                    PCAP filename (accessible locally)
2594
2595                ports : list
2596                    Ports on which to execute the command
2597
2598                ipg_usec : float
2599                    Inter-packet gap in microseconds
2600
2601                speedup : float
2602                    A factor to adjust IPG. effectively IPG = IPG / speedup
2603
2604                count: int
2605                    How many times to transmit the cap
2606
2607                duration: float
2608                    Limit runtime by duration in seconds
2609
2610                force: bool
2611                    Ignore file size limit - push any file size to the server
2612
2613                vm: list of VM instructions
2614                    VM instructions to apply for every packet
2615
2616                packet_hook : Callable or function
2617                    Will be applied to every packet
2618
2619                is_dual: bool
2620                    Inject from both directions.
2621                    requires ERF file with meta data for direction.
2622                    also requires that all the ports will be in master mode
2623                    with their adjacent ports as slaves
2624
2625                min_ipg_usec : float
2626                    Minimum inter-packet gap in microseconds to guard from too small ipg.
2627
2628            :raises:
2629                + :exc:`STLError`
2630
2631        """
2632        ports = ports if ports is not None else self.get_acquired_ports()
2633        ports = self._validate_port_list(ports)
2634
2635        validate_type('pcap_filename', pcap_filename, basestring)
2636        validate_type('ipg_usec', ipg_usec, (float, int, type(None)))
2637        validate_type('speedup',  speedup, (float, int))
2638        validate_type('count',  count, int)
2639        validate_type('duration', duration, (float, int))
2640        validate_type('vm', vm, (list, type(None)))
2641        validate_type('is_dual', is_dual, bool)
2642        validate_type('min_ipg_usec', min_ipg_usec, (float, int, type(None)))
2643
2644
2645        # no support for > 1MB PCAP - use push remote
2646        if not force and os.path.getsize(pcap_filename) > (1024 * 1024):
2647            raise STLError("PCAP size of {:} is too big for local push - consider using remote push or provide 'force'".format(format_num(os.path.getsize(pcap_filename), suffix = 'B')))
2648
2649        if is_dual:
2650            for port in ports:
2651                master = port
2652                slave = port ^ 0x1
2653
2654                if slave in ports:
2655                    raise STLError("dual mode: please specify only one of adjacent ports ({0}, {1}) in a batch".format(master, slave))
2656
2657                if not slave in self.get_acquired_ports():
2658                    raise STLError("dual mode: adjacent port {0} must be owned during dual mode".format(slave))
2659
2660        # regular push
2661        if not is_dual:
2662
2663            # create the profile from the PCAP
2664            try:
2665                self.logger.pre_cmd("Converting '{0}' to streams:".format(pcap_filename))
2666                profile = STLProfile.load_pcap(pcap_filename,
2667                                               ipg_usec,
2668                                               speedup,
2669                                               count,
2670                                               vm = vm,
2671                                               packet_hook = packet_hook,
2672                                               min_ipg_usec = min_ipg_usec)
2673                self.logger.post_cmd(RC_OK)
2674            except STLError as e:
2675                self.logger.post_cmd(RC_ERR(e))
2676                raise
2677
2678
2679            self.remove_all_streams(ports = ports)
2680            id_list = self.add_streams(profile.get_streams(), ports)
2681
2682            return self.start(ports = ports, duration = duration)
2683
2684        else:
2685
2686            # create a dual profile
2687            split_mode = 'MAC'
2688
2689            try:
2690                self.logger.pre_cmd("Analyzing '{0}' for dual ports based on {1}:".format(pcap_filename, split_mode))
2691                profile_a, profile_b = STLProfile.load_pcap(pcap_filename,
2692                                                            ipg_usec,
2693                                                            speedup,
2694                                                            count,
2695                                                            vm = vm,
2696                                                            packet_hook = packet_hook,
2697                                                            split_mode = split_mode,
2698                                                            min_ipg_usec = min_ipg_usec)
2699
2700                self.logger.post_cmd(RC_OK())
2701
2702            except STLError as e:
2703                self.logger.post_cmd(RC_ERR(e))
2704                raise
2705
2706            all_ports = ports + [p ^ 0x1 for p in ports if profile_b]
2707
2708            self.remove_all_streams(ports = all_ports)
2709
2710            for port in ports:
2711                master = port
2712                slave = port ^ 0x1
2713
2714                self.add_streams(profile_a.get_streams(), master)
2715                if profile_b:
2716                    self.add_streams(profile_b.get_streams(), slave)
2717
2718            return self.start(ports = all_ports, duration = duration)
2719
2720
2721
2722
2723
2724    @__api_check(True)
2725    def validate (self, ports = None, mult = "1", duration = -1, total = False):
2726        """
2727            Validate port(s) configuration
2728
2729            :parameters:
2730                ports : list
2731                    Ports on which to execute the command
2732
2733             mult : str
2734                    Multiplier in a form of pps, bps, or line util in %
2735                    Examples: "5kpps", "10gbps", "85%", "32mbps"
2736
2737            duration : int
2738                    Limit the run time (seconds)
2739                    -1 = unlimited
2740
2741            total : bool
2742                    Determines whether to divide the configured bandwidth among the ports, or to duplicate the bandwidth for each port.
2743                    True: Divide bandwidth among the ports
2744                    False: Duplicate
2745
2746            :raises:
2747                + :exc:`STLError`
2748
2749        """
2750
2751
2752        ports = ports if ports is not None else self.get_acquired_ports()
2753        ports = self._validate_port_list(ports)
2754
2755        validate_type('mult', mult, basestring)
2756        validate_type('duration', duration, (int, float))
2757        validate_type('total', total, bool)
2758
2759
2760        # verify multiplier
2761        mult_obj = parsing_opts.decode_multiplier(mult,
2762                                                  allow_update = True,
2763                                                  divide_count = len(ports) if total else 1)
2764        if not mult_obj:
2765            raise STLArgumentError('mult', mult)
2766
2767        self.logger.pre_cmd("Validating streams on port(s) {0}:".format(ports))
2768        rc = self.__validate(ports)
2769        self.logger.post_cmd(rc)
2770
2771        if not rc:
2772            raise STLError(rc)
2773
2774        for port in ports:
2775            self.ports[port].print_profile(mult_obj, duration)
2776
2777
2778    @__api_check(False)
2779    def clear_stats (self, ports = None, clear_global = True, clear_flow_stats = True, clear_latency_stats = True, clear_xstats = True):
2780        """
2781            Clear stats on port(s)
2782
2783            :parameters:
2784                ports : list
2785                    Ports on which to execute the command
2786
2787                clear_global : bool
2788                    Clear the global stats
2789
2790                clear_flow_stats : bool
2791                    Clear the flow stats
2792
2793                clear_latency_stats : bool
2794                    Clear the latency stats
2795
2796                clear_xstats : bool
2797                    Clear the extended stats
2798
2799            :raises:
2800                + :exc:`STLError`
2801
2802        """
2803
2804        ports = ports if ports is not None else self.get_all_ports()
2805        ports = self._validate_port_list(ports)
2806
2807        # verify clear global
2808        if not type(clear_global) is bool:
2809            raise STLArgumentError('clear_global', clear_global)
2810
2811        rc = self.__clear_stats(ports, clear_global, clear_flow_stats, clear_latency_stats, clear_xstats)
2812        if not rc:
2813            raise STLError(rc)
2814
2815
2816
2817    @__api_check(True)
2818    def is_traffic_active (self, ports = None):
2819        """
2820            Return if specified port(s) have traffic
2821
2822            :parameters:
2823                ports : list
2824                    Ports on which to execute the command
2825
2826
2827            :raises:
2828                + :exc:`STLTimeoutError` - in case timeout has expired
2829                + :exe:'STLError'
2830
2831        """
2832
2833        ports = ports if ports is not None else self.get_acquired_ports()
2834        ports = self._validate_port_list(ports)
2835
2836        return set(self.get_active_ports()).intersection(ports)
2837
2838
2839
2840    @__api_check(True)
2841    def wait_on_traffic (self, ports = None, timeout = None, rx_delay_ms = 10):
2842        """
2843            .. _wait_on_traffic:
2844
2845            Block until traffic on specified port(s) has ended
2846
2847            :parameters:
2848                ports : list
2849                    Ports on which to execute the command
2850
2851                timeout : int
2852                    timeout in seconds
2853                    default will be blocking
2854
2855                rx_delay_ms : int
2856                    Time to wait (in milliseconds) after last packet was sent, until RX filters used for
2857                    measuring flow statistics and latency are removed.
2858                    This value should reflect the time it takes packets which were transmitted to arrive
2859                    to the destination.
2860                    After this time, RX filters will be removed, and packets arriving for per flow statistics feature and latency flows will be counted as errors.
2861
2862            :raises:
2863                + :exc:`STLTimeoutError` - in case timeout has expired
2864                + :exe:'STLError'
2865
2866        """
2867
2868        ports = ports if ports is not None else self.get_acquired_ports()
2869        ports = self._validate_port_list(ports)
2870
2871
2872        timer = PassiveTimer(timeout)
2873
2874        # wait while any of the required ports are active
2875        while set(self.get_active_ports()).intersection(ports):
2876
2877            # make sure ASYNC thread is still alive - otherwise we will be stuck forever
2878            if not self.async_client.is_active():
2879                raise STLError("subscriber thread is dead")
2880
2881            time.sleep(0.01)
2882            if timer.has_expired():
2883                raise STLTimeoutError(timeout)
2884
2885        # remove any RX filters
2886        rc = self._remove_rx_filters(ports, rx_delay_ms = rx_delay_ms)
2887        if not rc:
2888            raise STLError(rc)
2889
2890
2891    @__api_check(True)
2892    def set_port_attr (self,
2893                       ports = None,
2894                       promiscuous = None,
2895                       link_up = None,
2896                       led_on = None,
2897                       flow_ctrl = None,
2898                       resolve = True):
2899        """
2900            Set port attributes
2901
2902            :parameters:
2903                promiscuous      - True or False
2904                link_up          - True or False
2905                led_on           - True or False
2906                flow_ctrl        - 0: disable all, 1: enable tx side, 2: enable rx side, 3: full enable
2907                resolve          - if true, in case a destination address is configured as IPv4 try to resolve it
2908            :raises:
2909                + :exe:'STLError'
2910
2911        """
2912
2913        ports = ports if ports is not None else self.get_acquired_ports()
2914        ports = self._validate_port_list(ports)
2915
2916        # check arguments
2917        validate_type('promiscuous', promiscuous, (bool, type(None)))
2918        validate_type('link_up', link_up, (bool, type(None)))
2919        validate_type('led_on', led_on, (bool, type(None)))
2920        validate_type('flow_ctrl', flow_ctrl, (int, type(None)))
2921
2922        # common attributes for all ports
2923        cmn_attr_dict = {}
2924
2925        cmn_attr_dict['promiscuous']     = promiscuous
2926        cmn_attr_dict['link_status']     = link_up
2927        cmn_attr_dict['led_status']      = led_on
2928        cmn_attr_dict['flow_ctrl_mode']  = flow_ctrl
2929
2930        # each port starts with a set of the common attributes
2931        attr_dict = [dict(cmn_attr_dict) for _ in ports]
2932
2933        self.logger.pre_cmd("Applying attributes on port(s) {0}:".format(ports))
2934        rc = self.__set_port_attr(ports, attr_dict)
2935        self.logger.post_cmd(rc)
2936
2937        if not rc:
2938            raise STLError(rc)
2939
2940
2941
2942
2943    @__api_check(True)
2944    def set_service_mode (self, ports = None, enabled = True):
2945        """
2946            Set service mode for port(s)
2947            In service mode ports will respond to ARP, PING and etc.
2948
2949            :parameters:
2950                ports          - for which ports to configure service mode on/off
2951                enabled        - True for activating service mode, False for disabling
2952            :raises:
2953                + :exe:'STLError'
2954
2955        """
2956        # by default take all acquired ports
2957        ports = ports if ports is not None else self.get_acquired_ports()
2958        ports = self._validate_port_list(ports)
2959
2960        if enabled:
2961            self.logger.pre_cmd('Enabling service mode on port(s) {0}:'.format(ports))
2962        else:
2963            self.logger.pre_cmd('Disabling service mode on port(s) {0}:'.format(ports))
2964
2965        rc = self.__set_service_mode(ports, enabled)
2966        self.logger.post_cmd(rc)
2967
2968        if not rc:
2969            raise STLError(rc)
2970
2971
2972    @__api_check(True)
2973    def resolve (self, ports = None, retries = 0, verbose = True):
2974        """
2975            Resolves ports (ARP resolution)
2976
2977            :parameters:
2978                ports          - for which ports to apply a unique sniffer (each port gets a unique file)
2979                retires        - how many times to retry on each port (intervals of 100 milliseconds)
2980                verbose        - log for each request the response
2981            :raises:
2982                + :exe:'STLError'
2983
2984        """
2985        # by default - resolve all the ports that are configured with IPv4 dest
2986        ports = ports if ports is not None else self.get_resolvable_ports()
2987        ports = self._validate_port_list(ports)
2988
2989        self.logger.pre_cmd('Resolving destination on port(s) {0}:'.format(ports))
2990        with self.logger.supress():
2991            rc = self.__resolve(ports, retries)
2992
2993        self.logger.post_cmd(rc)
2994
2995        if not rc:
2996            raise STLError(rc)
2997
2998        # print the ARP transaction
2999        if verbose:
3000            self.logger.log(rc)
3001            self.logger.log('')
3002
3003
3004
3005    @__api_check(True)
3006    def set_rx_sniffer (self, ports = None, base_filename = 'rx.pcap', limit = 1000):
3007        """
3008            Sets a RX sniffer for port(s) written to a PCAP file
3009
3010            :parameters:
3011                ports          - for which ports to apply a unique sniffer (each port gets a unique file)
3012                base_filename  - filename will be appended with '-<port_number>', e.g. rx.pcap --> rx-0.pcap, rx-1.pcap etc.
3013                limit          - limit how many packets will be written
3014            :raises:
3015                + :exe:'STLError'
3016
3017        """
3018        ports = ports if ports is not None else self.get_acquired_ports()
3019        ports = self._validate_port_list(ports)
3020
3021        # check arguments
3022        validate_type('base_filename', base_filename, basestring)
3023        validate_type('limit', limit, (int))
3024        if limit <= 0:
3025            raise STLError("'limit' must be a positive value")
3026
3027        self.logger.pre_cmd("Setting RX sniffers on port(s) {0}:".format(ports))
3028        rc = self.__set_rx_sniffer(ports, base_filename, limit)
3029        self.logger.post_cmd(rc)
3030
3031
3032        if not rc:
3033            raise STLError(rc)
3034
3035
3036
3037    @__api_check(True)
3038    def remove_rx_sniffer (self, ports = None):
3039        """
3040            Removes RX sniffer from port(s)
3041
3042            :raises:
3043                + :exe:'STLError'
3044
3045        """
3046        ports = ports if ports is not None else self.get_acquired_ports()
3047        ports = self._validate_port_list(ports)
3048
3049        self.logger.pre_cmd("Removing RX sniffers on port(s) {0}:".format(ports))
3050        rc = self.__remove_rx_sniffer(ports)
3051        self.logger.post_cmd(rc)
3052
3053        if not rc:
3054            raise STLError(rc)
3055
3056
3057    @__api_check(True)
3058    def set_rx_queue (self, ports = None, size = 1000):
3059        """
3060            Sets RX queue for port(s)
3061            The queue is cyclic and will hold last 'size' packets
3062
3063            :parameters:
3064                ports          - for which ports to apply a queue
3065                size           - size of the queue
3066            :raises:
3067                + :exe:'STLError'
3068
3069        """
3070        ports = ports if ports is not None else self.get_acquired_ports()
3071        ports = self._validate_port_list(ports)
3072
3073        # check arguments
3074        validate_type('size', size, (int))
3075        if size <= 0:
3076            raise STLError("'size' must be a positive value")
3077
3078        self.logger.pre_cmd("Setting RX queue on port(s) {0}:".format(ports))
3079        rc = self.__set_rx_queue(ports, size)
3080        self.logger.post_cmd(rc)
3081
3082
3083        if not rc:
3084            raise STLError(rc)
3085
3086
3087
3088    @__api_check(True)
3089    def remove_rx_queue (self, ports = None):
3090        """
3091            Removes RX queue from port(s)
3092
3093            :parameters:
3094                ports          - for which ports to remove the RX queue
3095            :raises:
3096                + :exe:'STLError'
3097
3098        """
3099        ports = ports if ports is not None else self.get_acquired_ports()
3100        ports = self._validate_port_list(ports)
3101
3102        self.logger.pre_cmd("Removing RX queue on port(s) {0}:".format(ports))
3103        rc = self.__remove_rx_queue(ports)
3104        self.logger.post_cmd(rc)
3105
3106        if not rc:
3107            raise STLError(rc)
3108
3109
3110
3111    @__api_check(True)
3112    def get_rx_queue_pkts (self, ports = None):
3113        """
3114            Returns any packets queued on the RX side by the server
3115            return value is a dictonary per port
3116
3117            :parameters:
3118                ports          - for which ports to fetch
3119        """
3120
3121        ports = ports if ports is not None else self.get_acquired_ports()
3122        ports = self._validate_port_list(ports)
3123
3124        rc = self.__get_rx_queue_pkts(ports)
3125        if not rc:
3126            raise STLError(rc)
3127
3128        # decode the data back to the user
3129        result = {}
3130        for port, r in zip(ports, rc.data()):
3131            result[port] = r
3132
3133        return result
3134
3135
3136    def clear_events (self):
3137        """
3138            Clear all events
3139
3140            :parameters:
3141                None
3142
3143            :raises:
3144                None
3145
3146        """
3147        self.event_handler.clear_events()
3148
3149
3150    ############################   Line       #############################
3151    ############################   Commands   #############################
3152    ############################              #############################
3153
3154    # console decorator
3155    def __console(f):
3156        @wraps(f)
3157        def wrap(*args):
3158            client = args[0]
3159
3160            time1 = time.time()
3161
3162            try:
3163                rc = f(*args)
3164            except STLError as e:
3165                client.logger.log("\nAction has failed with the following error:\n" + format_text(e.brief() + "\n", 'bold'))
3166                return RC_ERR(e.brief())
3167
3168            # if got true - print time
3169            if rc:
3170                delta = time.time() - time1
3171                client.logger.log(format_time(delta) + "\n")
3172
3173            return rc
3174
3175        return wrap
3176
3177    @__console
3178    def ping_line (self, line):
3179        '''pings the server / specific IP'''
3180
3181        # no parameters - so ping server
3182        if not line:
3183            self.ping_rpc_server()
3184            return True
3185
3186        parser = parsing_opts.gen_parser(self,
3187                                         "ping",
3188                                         self.ping_line.__doc__,
3189                                         parsing_opts.SINGLE_PORT,
3190                                         parsing_opts.PING_IPV4,
3191                                         parsing_opts.PKT_SIZE,
3192                                         parsing_opts.PING_COUNT)
3193
3194        opts = parser.parse_args(line.split())
3195        if not opts:
3196            return opts
3197
3198        # IP ping
3199        # source ports maps to ports as a single port
3200        self.ping_ip(opts.ports[0], opts.ping_ipv4, opts.pkt_size, opts.count)
3201
3202
3203    @__console
3204    def shutdown_line (self, line):
3205        '''shutdown the server'''
3206        parser = parsing_opts.gen_parser(self,
3207                                         "shutdown",
3208                                         self.shutdown_line.__doc__,
3209                                         parsing_opts.FORCE)
3210
3211        opts = parser.parse_args(line.split())
3212        if not opts:
3213            return opts
3214
3215        self.server_shutdown(force = opts.force)
3216        return RC_OK()
3217
3218    @__console
3219    def connect_line (self, line):
3220        '''Connects to the TRex server and acquire ports'''
3221        parser = parsing_opts.gen_parser(self,
3222                                         "connect",
3223                                         self.connect_line.__doc__,
3224                                         parsing_opts.PORT_LIST_WITH_ALL,
3225                                         parsing_opts.FORCE)
3226
3227        opts = parser.parse_args(line.split(), default_ports = self.get_all_ports())
3228        if not opts:
3229            return opts
3230
3231        self.connect()
3232        self.acquire(ports = opts.ports, force = opts.force)
3233
3234        return RC_OK()
3235
3236
3237    @__console
3238    def acquire_line (self, line):
3239        '''Acquire ports\n'''
3240
3241        # define a parser
3242        parser = parsing_opts.gen_parser(self,
3243                                         "acquire",
3244                                         self.acquire_line.__doc__,
3245                                         parsing_opts.PORT_LIST_WITH_ALL,
3246                                         parsing_opts.FORCE)
3247
3248        opts = parser.parse_args(line.split(), default_ports = self.get_all_ports())
3249        if not opts:
3250            return opts
3251
3252        # filter out all the already owned ports
3253        ports = list_difference(opts.ports, self.get_acquired_ports())
3254        if not ports:
3255            msg = "acquire - all of port(s) {0} are already acquired".format(opts.ports)
3256            self.logger.log(format_text(msg, 'bold'))
3257            return RC_ERR(msg)
3258
3259        self.acquire(ports = ports, force = opts.force)
3260
3261        return RC_OK()
3262
3263
3264    #
3265    @__console
3266    def release_line (self, line):
3267        '''Release ports\n'''
3268
3269        parser = parsing_opts.gen_parser(self,
3270                                         "release",
3271                                         self.release_line.__doc__,
3272                                         parsing_opts.PORT_LIST_WITH_ALL)
3273
3274        opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports())
3275        if not opts:
3276            return opts
3277
3278        ports = list_intersect(opts.ports, self.get_acquired_ports())
3279        if not ports:
3280            if not opts.ports:
3281                msg = "release - no acquired ports"
3282                self.logger.log(format_text(msg, 'bold'))
3283                return RC_ERR(msg)
3284            else:
3285                msg = "release - none of port(s) {0} are acquired".format(opts.ports)
3286                self.logger.log(format_text(msg, 'bold'))
3287                return RC_ERR(msg)
3288
3289
3290        self.release(ports = ports)
3291
3292        return RC_OK()
3293
3294
3295    @__console
3296    def reacquire_line (self, line):
3297        '''reacquire all the ports under your username which are not acquired by your session'''
3298
3299        parser = parsing_opts.gen_parser(self,
3300                                         "reacquire",
3301                                         self.reacquire_line.__doc__)
3302
3303        opts = parser.parse_args(line.split())
3304        if not opts:
3305            return opts
3306
3307        # find all the on-owned ports under your name
3308        my_unowned_ports = list_difference([k for k, v in self.ports.items() if v.get_owner() == self.username], self.get_acquired_ports())
3309        if not my_unowned_ports:
3310            msg = "reacquire - no unowned ports under '{0}'".format(self.username)
3311            self.logger.log(msg)
3312            return RC_ERR(msg)
3313
3314        self.acquire(ports = my_unowned_ports, force = True)
3315        return RC_OK()
3316
3317
3318    @__console
3319    def disconnect_line (self, line):
3320        self.disconnect()
3321
3322
3323    @__console
3324    def reset_line (self, line):
3325        '''Reset ports - if no ports are provided all acquired ports will be reset'''
3326
3327        parser = parsing_opts.gen_parser(self,
3328                                         "reset",
3329                                         self.reset_line.__doc__,
3330                                         parsing_opts.PORT_LIST_WITH_ALL,
3331                                         parsing_opts.PORT_RESTART)
3332
3333        opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True)
3334        if not opts:
3335            return opts
3336
3337        self.reset(ports = opts.ports, restart = opts.restart)
3338
3339        return RC_OK()
3340
3341
3342
3343    @__console
3344    def start_line (self, line):
3345        '''Start selected traffic on specified ports on TRex\n'''
3346        # define a parser
3347        parser = parsing_opts.gen_parser(self,
3348                                         "start",
3349                                         self.start_line.__doc__,
3350                                         parsing_opts.PORT_LIST_WITH_ALL,
3351                                         parsing_opts.TOTAL,
3352                                         parsing_opts.FORCE,
3353                                         parsing_opts.FILE_PATH,
3354                                         parsing_opts.DURATION,
3355                                         parsing_opts.TUNABLES,
3356                                         parsing_opts.MULTIPLIER_STRICT,
3357                                         parsing_opts.DRY_RUN,
3358                                         parsing_opts.CORE_MASK_GROUP)
3359
3360        opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True)
3361        if not opts:
3362            return opts
3363
3364        # core mask
3365        if opts.core_mask is not None:
3366            core_mask =  opts.core_mask
3367        else:
3368            core_mask = self.CORE_MASK_PIN if opts.pin_cores else self.CORE_MASK_SPLIT
3369
3370        # just for sanity - will be checked on the API as well
3371        self.__decode_core_mask(opts.ports, core_mask)
3372
3373        # for better use experience - check this first
3374        try:
3375            self.__pre_start_check(opts.ports, opts.force)
3376        except STLError as e:
3377            msg = e.brief()
3378            self.logger.log(format_text(msg, 'bold'))
3379            return RC_ERR(msg)
3380
3381
3382        # stop ports if needed
3383        active_ports = list_intersect(self.get_active_ports(), opts.ports)
3384        if active_ports and opts.force:
3385            self.stop(active_ports)
3386
3387
3388        # process tunables
3389        if type(opts.tunables) is dict:
3390            tunables = opts.tunables
3391        else:
3392            tunables = {}
3393
3394
3395        # remove all streams
3396        self.remove_all_streams(opts.ports)
3397
3398        # pack the profile
3399        try:
3400            for port in opts.ports:
3401
3402                profile = STLProfile.load(opts.file[0],
3403                                          direction = tunables.get('direction', port % 2),
3404                                          port_id = port,
3405                                          **tunables)
3406
3407                self.add_streams(profile.get_streams(), ports = port)
3408
3409        except STLError as e:
3410            error = 'Unknown error.'
3411            for line in e.brief().split('\n'):
3412                if line:
3413                    error = line
3414            msg = format_text("\nError loading profile '{0}'".format(opts.file[0]), 'bold')
3415            self.logger.log(msg + '\n')
3416            self.logger.log(e.brief() + "\n")
3417            return RC_ERR("%s: %s" % (msg, error))
3418
3419
3420        if opts.dry:
3421            self.validate(opts.ports, opts.mult, opts.duration, opts.total)
3422        else:
3423
3424            self.start(opts.ports,
3425                       opts.mult,
3426                       opts.force,
3427                       opts.duration,
3428                       opts.total,
3429                       core_mask)
3430
3431        return RC_OK()
3432
3433
3434
3435    @__console
3436    def stop_line (self, line):
3437        '''Stop active traffic on specified ports on TRex\n'''
3438        parser = parsing_opts.gen_parser(self,
3439                                         "stop",
3440                                         self.stop_line.__doc__,
3441                                         parsing_opts.PORT_LIST_WITH_ALL)
3442
3443        opts = parser.parse_args(line.split(), default_ports = self.get_active_ports(), verify_acquired = True)
3444        if not opts:
3445            return opts
3446
3447
3448        # find the relevant ports
3449        ports = list_intersect(opts.ports, self.get_active_ports())
3450        if not ports:
3451            if not opts.ports:
3452                msg = 'stop - no active ports'
3453            else:
3454                msg = 'stop - no active traffic on ports {0}'.format(opts.ports)
3455
3456            self.logger.log(msg)
3457            return RC_ERR(msg)
3458
3459        # call API
3460        self.stop(ports)
3461
3462        return RC_OK()
3463
3464
3465    @__console
3466    def update_line (self, line):
3467        '''Update port(s) speed currently active\n'''
3468        parser = parsing_opts.gen_parser(self,
3469                                         "update",
3470                                         self.update_line.__doc__,
3471                                         parsing_opts.PORT_LIST_WITH_ALL,
3472                                         parsing_opts.MULTIPLIER,
3473                                         parsing_opts.TOTAL,
3474                                         parsing_opts.FORCE)
3475
3476        opts = parser.parse_args(line.split(), default_ports = self.get_active_ports(), verify_acquired = True)
3477        if not opts:
3478            return opts
3479
3480
3481        # find the relevant ports
3482        ports = list_intersect(opts.ports, self.get_active_ports())
3483        if not ports:
3484            if not opts.ports:
3485                msg = 'update - no active ports'
3486            else:
3487                msg = 'update - no active traffic on ports {0}'.format(opts.ports)
3488
3489            self.logger.log(msg)
3490            return RC_ERR(msg)
3491
3492        self.update(ports, opts.mult, opts.total, opts.force)
3493
3494        return RC_OK()
3495
3496
3497    @__console
3498    def pause_line (self, line):
3499        '''Pause active traffic on specified ports on TRex\n'''
3500        parser = parsing_opts.gen_parser(self,
3501                                         "pause",
3502                                         self.pause_line.__doc__,
3503                                         parsing_opts.PORT_LIST_WITH_ALL)
3504
3505        opts = parser.parse_args(line.split(), default_ports = self.get_transmitting_ports(), verify_acquired = True)
3506        if not opts:
3507            return opts
3508
3509        # check for already paused case
3510        if opts.ports and is_sub_list(opts.ports, self.get_paused_ports()):
3511            msg = 'pause - all of port(s) {0} are already paused'.format(opts.ports)
3512            self.logger.log(msg)
3513            return RC_ERR(msg)
3514
3515        # find the relevant ports
3516        ports = list_intersect(opts.ports, self.get_transmitting_ports())
3517        if not ports:
3518            if not opts.ports:
3519                msg = 'pause - no transmitting ports'
3520            else:
3521                msg = 'pause - none of ports {0} are transmitting'.format(opts.ports)
3522
3523            self.logger.log(msg)
3524            return RC_ERR(msg)
3525
3526        self.pause(ports)
3527
3528        return RC_OK()
3529
3530
3531    @__console
3532    def resume_line (self, line):
3533        '''Resume active traffic on specified ports on TRex\n'''
3534        parser = parsing_opts.gen_parser(self,
3535                                         "resume",
3536                                         self.resume_line.__doc__,
3537                                         parsing_opts.PORT_LIST_WITH_ALL)
3538
3539        opts = parser.parse_args(line.split(), default_ports = self.get_paused_ports(), verify_acquired = True)
3540        if not opts:
3541            return opts
3542
3543        # find the relevant ports
3544        ports = list_intersect(opts.ports, self.get_paused_ports())
3545        if not ports:
3546            if not opts.ports:
3547                msg = 'resume - no paused ports'
3548            else:
3549                msg = 'resume - none of ports {0} are paused'.format(opts.ports)
3550
3551            self.logger.log(msg)
3552            return RC_ERR(msg)
3553
3554
3555        self.resume(ports)
3556
3557        # true means print time
3558        return RC_OK()
3559
3560
3561    @__console
3562    def clear_stats_line (self, line):
3563        '''Clear cached local statistics\n'''
3564        # define a parser
3565        parser = parsing_opts.gen_parser(self,
3566                                         "clear",
3567                                         self.clear_stats_line.__doc__,
3568                                         parsing_opts.PORT_LIST_WITH_ALL)
3569
3570        opts = parser.parse_args(line.split())
3571
3572        if not opts:
3573            return opts
3574
3575        self.clear_stats(opts.ports)
3576
3577        return RC_OK()
3578
3579
3580    @__console
3581    def show_stats_line (self, line):
3582        '''Get statistics from TRex server by port\n'''
3583        # define a parser
3584        parser = parsing_opts.gen_parser(self,
3585                                         "stats",
3586                                         self.show_stats_line.__doc__,
3587                                         parsing_opts.PORT_LIST_WITH_ALL,
3588                                         parsing_opts.STATS_MASK)
3589
3590        opts = parser.parse_args(line.split())
3591
3592        if not opts:
3593            return opts
3594
3595        # determine stats mask
3596        mask = self.__get_mask_keys(**self.__filter_namespace_args(opts, trex_stl_stats.ALL_STATS_OPTS))
3597        if not mask:
3598            # set to show all stats if no filter was given
3599            mask = trex_stl_stats.COMPACT
3600
3601        stats_opts = common.list_intersect(trex_stl_stats.ALL_STATS_OPTS, mask)
3602
3603        stats = self._get_formatted_stats(opts.ports, mask)
3604
3605
3606        # print stats to screen
3607        for stat_type, stat_data in stats.items():
3608            text_tables.print_table_with_header(stat_data.text_table, stat_type)
3609
3610
3611    @__console
3612    def show_streams_line(self, line):
3613        '''Get stream statistics from TRex server by port\n'''
3614        # define a parser
3615        parser = parsing_opts.gen_parser(self,
3616                                         "streams",
3617                                         self.show_streams_line.__doc__,
3618                                         parsing_opts.PORT_LIST_WITH_ALL,
3619                                         parsing_opts.STREAMS_MASK)
3620
3621        opts = parser.parse_args(line.split())
3622
3623        if not opts:
3624            return opts
3625
3626        streams = self._get_streams(opts.ports, set(opts.streams))
3627        if not streams:
3628            self.logger.log(format_text("No streams found with desired filter.\n", "bold", "magenta"))
3629
3630        else:
3631            # print stats to screen
3632            for stream_hdr, port_streams_data in streams.items():
3633                text_tables.print_table_with_header(port_streams_data.text_table,
3634                                                    header= stream_hdr.split(":")[0] + ":",
3635                                                    untouched_header= stream_hdr.split(":")[1])
3636
3637
3638
3639
3640    @__console
3641    def validate_line (self, line):
3642        '''Validates port(s) stream configuration\n'''
3643
3644        parser = parsing_opts.gen_parser(self,
3645                                         "validate",
3646                                         self.validate_line.__doc__,
3647                                         parsing_opts.PORT_LIST_WITH_ALL)
3648
3649        opts = parser.parse_args(line.split())
3650        if not opts:
3651            return opts
3652
3653        self.validate(opts.ports)
3654
3655
3656
3657
3658    @__console
3659    def push_line (self, line):
3660        '''Push a pcap file '''
3661        args = [self,
3662                "push",
3663                self.push_line.__doc__,
3664                parsing_opts.REMOTE_FILE,
3665                parsing_opts.PORT_LIST_WITH_ALL,
3666                parsing_opts.COUNT,
3667                parsing_opts.DURATION,
3668                parsing_opts.IPG,
3669                parsing_opts.MIN_IPG,
3670                parsing_opts.SPEEDUP,
3671                parsing_opts.FORCE,
3672                parsing_opts.DUAL]
3673
3674        parser = parsing_opts.gen_parser(*(args + [parsing_opts.FILE_PATH_NO_CHECK]))
3675        opts = parser.parse_args(line.split(), verify_acquired = True)
3676
3677        if not opts:
3678            return opts
3679
3680        if not opts.remote:
3681            parser = parsing_opts.gen_parser(*(args + [parsing_opts.FILE_PATH]))
3682            opts = parser.parse_args(line.split(), verify_acquired = True)
3683
3684        if not opts:
3685            return opts
3686
3687        active_ports = list(set(self.get_active_ports()).intersection(opts.ports))
3688
3689        if active_ports:
3690            if not opts.force:
3691                msg = "Port(s) {0} are active - please stop them or add '--force'\n".format(active_ports)
3692                self.logger.log(format_text(msg, 'bold'))
3693                return RC_ERR(msg)
3694            else:
3695                self.stop(active_ports)
3696
3697
3698        if opts.remote:
3699            self.push_remote(opts.file[0],
3700                             ports     = opts.ports,
3701                             ipg_usec  = opts.ipg_usec,
3702                             min_ipg_usec   = opts.min_ipg_usec,
3703                             speedup   = opts.speedup,
3704                             count     = opts.count,
3705                             duration  = opts.duration,
3706                             is_dual   = opts.dual)
3707
3708        else:
3709            self.push_pcap(opts.file[0],
3710                           ports     = opts.ports,
3711                           ipg_usec  = opts.ipg_usec,
3712                           min_ipg_usec   = opts.min_ipg_usec,
3713                           speedup   = opts.speedup,
3714                           count     = opts.count,
3715                           duration  = opts.duration,
3716                           force     = opts.force,
3717                           is_dual   = opts.dual)
3718
3719
3720
3721        return RC_OK()
3722
3723
3724
3725    @__console
3726    def set_port_attr_line (self, line):
3727        '''Sets port attributes '''
3728
3729        parser = parsing_opts.gen_parser(self,
3730                                         "portattr",
3731                                         self.set_port_attr_line.__doc__,
3732                                         parsing_opts.PORT_LIST_WITH_ALL,
3733                                         parsing_opts.PROMISCUOUS,
3734                                         parsing_opts.LINK_STATUS,
3735                                         parsing_opts.LED_STATUS,
3736                                         parsing_opts.FLOW_CTRL,
3737                                         parsing_opts.SUPPORTED,
3738                                         )
3739
3740        opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True)
3741        if not opts:
3742            return opts
3743
3744        opts.prom            = parsing_opts.ON_OFF_DICT.get(opts.prom)
3745        opts.link            = parsing_opts.UP_DOWN_DICT.get(opts.link)
3746        opts.led             = parsing_opts.ON_OFF_DICT.get(opts.led)
3747        opts.flow_ctrl       = parsing_opts.FLOW_CTRL_DICT.get(opts.flow_ctrl)
3748
3749        # if no attributes - fall back to printing the status
3750        if not list(filter(lambda x:x is not None, [opts.prom, opts.link, opts.led, opts.flow_ctrl, opts.supp])):
3751            self.show_stats_line("--ps --port {0}".format(' '.join(str(port) for port in opts.ports)))
3752            return
3753
3754        if opts.supp:
3755            info = self.ports[0].get_formatted_info() # assume for now all ports are same
3756            print('')
3757            print('Supported attributes for current NICs:')
3758            print('  Promiscuous:   yes')
3759            print('  Link status:   %s' % info['link_change_supported'])
3760            print('  LED status:    %s' % info['led_change_supported'])
3761            print('  Flow control:  %s' % info['fc_supported'])
3762            print('')
3763        else:
3764             self.set_port_attr(opts.ports,
3765                                opts.prom,
3766                                opts.link,
3767                                opts.led,
3768                                opts.flow_ctrl)
3769
3770
3771
3772
3773    @__console
3774    def set_rx_sniffer_line (self, line):
3775        '''Sets a port sniffer on RX channel in form of a PCAP file'''
3776
3777        parser = parsing_opts.gen_parser(self,
3778                                         "set_rx_sniffer",
3779                                         self.set_rx_sniffer_line.__doc__,
3780                                         parsing_opts.PORT_LIST_WITH_ALL,
3781                                         parsing_opts.OUTPUT_FILENAME,
3782                                         parsing_opts.LIMIT)
3783
3784        opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True)
3785        if not opts:
3786            return opts
3787
3788        self.set_rx_sniffer(opts.ports, opts.output_filename, opts.limit)
3789
3790        return RC_OK()
3791
3792
3793    @__console
3794    def resolve_line (self, line):
3795        '''Performs a port ARP resolution'''
3796
3797        parser = parsing_opts.gen_parser(self,
3798                                         "resolve",
3799                                         self.resolve_line.__doc__,
3800                                         parsing_opts.PORT_LIST_WITH_ALL,
3801                                         parsing_opts.RETRIES)
3802
3803        opts = parser.parse_args(line.split(), default_ports = self.get_resolvable_ports(), verify_acquired = True)
3804        if not opts:
3805            return opts
3806
3807        ports = list_intersect(opts.ports, self.get_resolvable_ports())
3808        if not ports:
3809            if not opts.ports:
3810                msg = 'resolve - no ports with IPv4 destination'
3811            else:
3812                msg = 'pause - none of ports {0} are configured with IPv4 destination'.format(opts.ports)
3813
3814            self.logger.log(msg)
3815            return RC_ERR(msg)
3816
3817        self.resolve(ports = ports, retries = opts.retries)
3818
3819        return RC_OK()
3820
3821
3822    @__console
3823    def set_l2_mode_line (self, line):
3824        '''Configures a port in L2 mode'''
3825
3826        parser = parsing_opts.gen_parser(self,
3827                                         "port",
3828                                         self.set_l2_mode_line.__doc__,
3829                                         parsing_opts.SINGLE_PORT,
3830                                         parsing_opts.DST_MAC,
3831                                         )
3832
3833        opts = parser.parse_args(line.split())
3834        if not opts:
3835            return opts
3836
3837
3838        # source ports maps to ports as a single port
3839        self.set_l2_mode(opts.ports[0], dst_mac = opts.dst_mac)
3840
3841        return RC_OK()
3842
3843
3844    @__console
3845    def set_l3_mode_line (self, line):
3846        '''Configures a port in L3 mode'''
3847
3848        parser = parsing_opts.gen_parser(self,
3849                                         "port",
3850                                         self.set_l3_mode_line.__doc__,
3851                                         parsing_opts.SINGLE_PORT,
3852                                         parsing_opts.SRC_IPV4,
3853                                         parsing_opts.DST_IPV4,
3854                                         )
3855
3856        opts = parser.parse_args(line.split())
3857        if not opts:
3858            return opts
3859
3860
3861        # source ports maps to ports as a single port
3862        self.set_l3_mode(opts.ports[0], src_ipv4 = opts.src_ipv4, dst_ipv4 = opts.dst_ipv4)
3863
3864        return RC_OK()
3865
3866
3867    @__console
3868    def show_profile_line (self, line):
3869        '''Shows profile information'''
3870
3871        parser = parsing_opts.gen_parser(self,
3872                                         "profile",
3873                                         self.show_profile_line.__doc__,
3874                                         parsing_opts.FILE_PATH)
3875
3876        opts = parser.parse_args(line.split())
3877        if not opts:
3878            return opts
3879
3880        info = STLProfile.get_info(opts.file[0])
3881
3882        self.logger.log(format_text('\nProfile Information:\n', 'bold'))
3883
3884        # general info
3885        self.logger.log(format_text('\nGeneral Information:', 'underline'))
3886        self.logger.log('Filename:         {:^12}'.format(opts.file[0]))
3887        self.logger.log('Stream count:     {:^12}'.format(info['stream_count']))
3888
3889        # specific info
3890        profile_type = info['type']
3891        self.logger.log(format_text('\nSpecific Information:', 'underline'))
3892
3893        if profile_type == 'python':
3894            self.logger.log('Type:             {:^12}'.format('Python Module'))
3895            self.logger.log('Tunables:         {:^12}'.format(str(['{0} = {1}'.format(k ,v) for k, v in info['tunables'].items()])))
3896
3897        elif profile_type == 'yaml':
3898            self.logger.log('Type:             {:^12}'.format('YAML'))
3899
3900        elif profile_type == 'pcap':
3901            self.logger.log('Type:             {:^12}'.format('PCAP file'))
3902
3903        self.logger.log("")
3904
3905
3906    @__console
3907    def get_events_line (self, line):
3908        '''shows events recieved from server\n'''
3909
3910        x = [parsing_opts.ArgumentPack(['-c','--clear'],
3911                                      {'action' : "store_true",
3912                                       'default': False,
3913                                       'help': "clear the events log"}),
3914
3915             parsing_opts.ArgumentPack(['-i','--info'],
3916                                      {'action' : "store_true",
3917                                       'default': False,
3918                                       'help': "show info events"}),
3919
3920             parsing_opts.ArgumentPack(['-w','--warn'],
3921                                      {'action' : "store_true",
3922                                       'default': False,
3923                                       'help': "show warning events"}),
3924
3925             ]
3926
3927
3928        parser = parsing_opts.gen_parser(self,
3929                                         "events",
3930                                         self.get_events_line.__doc__,
3931                                         *x)
3932
3933        opts = parser.parse_args(line.split())
3934        if not opts:
3935            return opts
3936
3937
3938        ev_type_filter = []
3939
3940        if opts.info:
3941            ev_type_filter.append('info')
3942
3943        if opts.warn:
3944            ev_type_filter.append('warning')
3945
3946        if not ev_type_filter:
3947            ev_type_filter = None
3948
3949        events = self.get_events(ev_type_filter)
3950        for ev in events:
3951            self.logger.log(ev)
3952
3953        if opts.clear:
3954            self.clear_events()
3955
3956    def generate_prompt (self, prefix = 'trex'):
3957        if not self.is_connected():
3958            return "{0}(offline)>".format(prefix)
3959
3960        elif not self.get_acquired_ports():
3961            return "{0}(read-only)>".format(prefix)
3962
3963        elif self.is_all_ports_acquired():
3964
3965            p = prefix
3966
3967            if self.get_service_enabled_ports():
3968                if self.get_service_enabled_ports() == self.get_acquired_ports():
3969                    p += '(service)'
3970                else:
3971                    p += '(service: {0})'.format(', '.join(map(str, self.get_service_enabled_ports())))
3972
3973            return "{0}>".format(p)
3974
3975        else:
3976            return "{0} (ports: {1})>".format(prefix, ', '.join(map(str, self.get_acquired_ports())))
3977
3978
3979
3980    @__console
3981    def service_line (self, line):
3982        '''Configures port for service mode.
3983           In service mode ports will reply to ARP, PING
3984           and etc.
3985        '''
3986
3987        parser = parsing_opts.gen_parser(self,
3988                                         "service",
3989                                         self.service_line.__doc__,
3990                                         parsing_opts.PORT_LIST_WITH_ALL,
3991                                         parsing_opts.SERVICE_OFF)
3992
3993        opts = parser.parse_args(line.split())
3994        if not opts:
3995            return opts
3996
3997        self.set_service_mode(ports = opts.ports, enabled = opts.enabled)
3998
3999