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