1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4"""
5Dan Klein, Itay Marom
6Cisco Systems, Inc.
7
8Copyright (c) 2015-2015 Cisco Systems, Inc.
9Licensed under the Apache License, Version 2.0 (the "License");
10you may not use this file except in compliance with the License.
11You may obtain a copy of the License at
12    http://www.apache.org/licenses/LICENSE-2.0
13Unless required by applicable law or agreed to in writing, software
14distributed under the License is distributed on an "AS IS" BASIS,
15WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16See the License for the specific language governing permissions and
17limitations under the License.
18"""
19from __future__ import print_function
20
21import subprocess
22import cmd
23import json
24import ast
25import argparse
26import random
27import readline
28import string
29import os
30import sys
31import tty, termios
32from threading import Lock
33import threading
34
35try:
36    import stl_path
37except:
38    from . import stl_path
39from trex_stl_lib.api import *
40
41from trex_stl_lib.utils.text_opts import *
42from trex_stl_lib.utils.common import user_input, get_current_user, set_window_always_on_top
43from trex_stl_lib.utils import parsing_opts
44from .trex_capture import CaptureManager
45
46try:
47    import trex_tui
48except:
49    from . import trex_tui
50
51from functools import wraps
52
53__version__ = "2.0"
54
55# console custom logger
56class ConsoleLogger(LoggerApi):
57    def __init__ (self):
58        self.prompt_redraw = None
59
60    def write (self, msg, newline = True):
61        if newline:
62            print(msg)
63        else:
64            print(msg, end=' ')
65
66    def flush (self):
67        sys.stdout.flush()
68
69    # override this for the prompt fix
70    def async_log (self, msg, level = LoggerApi.VERBOSE_REGULAR, newline = True):
71        self.log(msg, level, newline)
72        if ( (self.level >= LoggerApi.VERBOSE_REGULAR) and self.prompt_redraw ):
73            self.prompt_redraw()
74            self.flush()
75
76
77class TRexGeneralCmd(cmd.Cmd):
78    def __init__(self):
79        cmd.Cmd.__init__(self)
80        # configure history behaviour
81        self._history_file_dir = "/tmp/trex/console/"
82        self._history_file = self.get_history_file_full_path()
83        readline.set_history_length(100)
84        # load history, if any
85        self.load_console_history()
86
87
88    def get_console_identifier(self):
89        return self.__class__.__name__
90
91    def get_history_file_full_path(self):
92        return "{dir}{filename}.hist".format(dir=self._history_file_dir,
93                                             filename=self.get_console_identifier())
94
95    def load_console_history(self):
96        if os.path.exists(self._history_file):
97            readline.read_history_file(self._history_file)
98        return
99
100    def save_console_history(self):
101        if not os.path.exists(self._history_file_dir):
102            # make the directory available for every user
103            try:
104                original_umask = os.umask(0)
105                os.makedirs(self._history_file_dir, mode = 0o777)
106            finally:
107                os.umask(original_umask)
108
109
110        # os.mknod(self._history_file)
111        readline.write_history_file(self._history_file)
112        return
113
114    def print_history (self):
115
116        length = readline.get_current_history_length()
117
118        for i in range(1, length + 1):
119            cmd = readline.get_history_item(i)
120            print("{:<5}   {:}".format(i, cmd))
121
122    def get_history_item (self, index):
123        length = readline.get_current_history_length()
124        if index > length:
125            print(format_text("please select an index between {0} and {1}".format(0, length)))
126            return None
127
128        return readline.get_history_item(index)
129
130
131    def emptyline(self):
132        """Called when an empty line is entered in response to the prompt.
133
134        This overriding is such that when empty line is passed, **nothing happens**.
135        """
136        return
137
138    def completenames(self, text, *ignored):
139        """
140        This overriding is such that a space is added to name completion.
141        """
142        dotext = 'do_'+text
143        return [a[3:]+' ' for a in self.get_names() if a.startswith(dotext)]
144
145
146#
147# main console object
148class TRexConsole(TRexGeneralCmd):
149    """Trex Console"""
150
151    def __init__(self, stateless_client, verbose = False):
152
153        # cmd lock is used to make sure background job
154        # of the console is not done while the user excutes commands
155        self.cmd_lock = Lock()
156
157        self.stateless_client = stateless_client
158
159        TRexGeneralCmd.__init__(self)
160
161        self.tui = trex_tui.TrexTUI(stateless_client)
162        self.terminal = None
163
164        self.verbose = verbose
165
166        self.intro  = "\n-=TRex Console v{ver}=-\n".format(ver=__version__)
167        self.intro += "\nType 'help' or '?' for supported actions\n"
168
169        self.cap_mngr = CaptureManager(stateless_client, self.cmd_lock)
170
171        self.postcmd(False, "")
172
173
174
175    ################### internal section ########################
176
177    def prompt_redraw (self):
178        self.postcmd(False, "")
179        sys.stdout.write("\n" + self.prompt + readline.get_line_buffer())
180        sys.stdout.flush()
181
182
183    def verify_connected(f):
184        @wraps(f)
185        def wrap(*args):
186            inst = args[0]
187            func_name = f.__name__
188            if func_name.startswith("do_"):
189                func_name = func_name[3:]
190
191            if not inst.stateless_client.is_connected():
192                print(format_text("\n'{0}' cannot be executed on offline mode\n".format(func_name), 'bold'))
193                return
194
195            ret = f(*args)
196            return ret
197
198        return wrap
199
200
201    def get_console_identifier(self):
202        return "{context}_{server}".format(context=get_current_user(),
203                                           server=self.stateless_client.get_connection_info()['server'])
204
205    def register_main_console_methods(self):
206        main_names = set(self.trex_console.get_names()).difference(set(dir(self.__class__)))
207        for name in main_names:
208            for prefix in 'do_', 'help_', 'complete_':
209                if name.startswith(prefix):
210                    self.__dict__[name] = getattr(self.trex_console, name)
211
212    def precmd(self, line):
213        # before doing anything, save history snapshot of the console
214        # this is done before executing the command in case of ungraceful application exit
215        self.save_console_history()
216
217        lines = line.split(';')
218        try:
219            self.cmd_lock.acquire()
220            for line in lines:
221                stop = self.onecmd(line)
222                stop = self.postcmd(stop, line)
223                if stop:
224                    return "quit"
225
226            return ""
227
228        except STLError as e:
229            print(e)
230            return ''
231
232        finally:
233            self.cmd_lock.release()
234
235
236
237    def postcmd(self, stop, line):
238        self.prompt = self.stateless_client.generate_prompt(prefix = 'trex')
239        return stop
240
241
242    def default(self, line):
243        print("'{0}' is an unrecognized command. type 'help' or '?' for a list\n".format(line))
244
245    @staticmethod
246    def tree_autocomplete(text):
247        dir = os.path.dirname(text)
248        if dir:
249            path = dir
250        else:
251            path = "."
252
253
254        start_string = os.path.basename(text)
255
256        targets = []
257
258        for x in os.listdir(path):
259            if x.startswith(start_string):
260                y = os.path.join(path, x)
261                if os.path.isfile(y):
262                    targets.append(x + ' ')
263                elif os.path.isdir(y):
264                    targets.append(x + '/')
265
266        return targets
267
268
269    ####################### shell commands #######################
270    @verify_connected
271    def do_ping (self, line):
272        '''Ping the server\n'''
273        self.stateless_client.ping_line(line)
274
275
276    @verify_connected
277    def do_shutdown (self, line):
278        '''Sends the server a shutdown request\n'''
279        self.stateless_client.shutdown_line(line)
280
281    # set verbose on / off
282    def do_verbose(self, line):
283        '''Shows or set verbose mode\n'''
284        if line == "":
285            print("\nverbose is " + ("on\n" if self.verbose else "off\n"))
286
287        elif line == "on":
288            self.verbose = True
289            self.stateless_client.set_verbose("high")
290            print(format_text("\nverbose set to on\n", 'green', 'bold'))
291
292        elif line == "off":
293            self.verbose = False
294            self.stateless_client.set_verbose("normal")
295            print(format_text("\nverbose set to off\n", 'green', 'bold'))
296
297        else:
298            print(format_text("\nplease specify 'on' or 'off'\n", 'bold'))
299
300    # show history
301    def help_history (self):
302        self.do_history("-h")
303
304    def do_shell (self, line):
305        self.do_history(line)
306
307    @verify_connected
308    def do_push (self, line):
309        '''Push a local PCAP file\n'''
310        self.stateless_client.push_line(line)
311
312    def help_push (self):
313        self.do_push("-h")
314
315    def do_debug (self, line):
316        '''Launches IPython for interactively debugging'''
317        self.stateless_client.debug_line(line)
318
319    def help_debug (self):
320        self.do_debug('-h')
321
322    @verify_connected
323    def do_portattr (self, line):
324        '''Change/show port(s) attributes\n'''
325        self.stateless_client.set_port_attr_line(line)
326
327    def help_portattr (self):
328        self.do_portattr("-h")
329
330    @verify_connected
331    def do_l2 (self, line):
332        '''Configures a port in L2 mode'''
333        self.stateless_client.set_l2_mode_line(line)
334
335    def help_l2 (self):
336        self.do_l2("-h")
337
338    @verify_connected
339    def do_l3 (self, line):
340        '''Configures a port in L3 mode'''
341        self.stateless_client.set_l3_mode_line(line)
342
343    def help_l3 (self):
344        self.do_l3("-h")
345
346
347    @verify_connected
348    def do_capture (self, line):
349        '''Manage PCAP captures'''
350        self.cap_mngr.parse_line(line)
351
352    def help_capture (self):
353        self.do_capture("-h")
354
355    @verify_connected
356    def do_resolve (self, line):
357        '''Resolve ARP for ports'''
358        self.stateless_client.resolve_line(line)
359
360    @verify_connected
361    def do_scan6(self, line):
362        '''Search for IPv6 neighbors'''
363        self.stateless_client.scan6_line(line)
364
365    def help_resolve (self):
366        self.do_resolve("-h")
367
368    do_arp = do_resolve
369    help_arp = help_resolve
370
371    @verify_connected
372    def do_map (self, line):
373        '''Maps ports topology\n'''
374        ports = self.stateless_client.get_acquired_ports()
375        if not ports:
376            print("No ports acquired\n")
377            return
378
379
380        try:
381            with self.stateless_client.logger.supress():
382                table = stl_map_ports(self.stateless_client, ports = ports)
383        except STLError as e:
384            print(format_text(e.brief() + "\n", 'bold'))
385            return
386
387
388        print(format_text('\nAcquired ports topology:\n', 'bold', 'underline'))
389
390        # bi-dir ports
391        print(format_text('Bi-directional ports:\n','underline'))
392        for port_a, port_b in table['bi']:
393            print("port {0} <--> port {1}".format(port_a, port_b))
394
395        print("")
396
397        # unknown ports
398        print(format_text('Mapping unknown:\n','underline'))
399        for port in table['unknown']:
400            print("port {0}".format(port))
401        print("")
402
403
404
405
406    def do_history (self, line):
407        '''Manage the command history\n'''
408
409        item = parsing_opts.ArgumentPack(['item'],
410                                         {"nargs": '?',
411                                          'metavar': 'item',
412                                          'type': parsing_opts.check_negative,
413                                          'help': "an history item index",
414                                          'default': 0})
415
416        parser = parsing_opts.gen_parser(self.stateless_client,
417                                         "history",
418                                         self.do_history.__doc__,
419                                         item)
420
421        opts = parser.parse_args(line.split())
422        if opts is None:
423            return
424
425        if opts.item == 0:
426            self.print_history()
427        else:
428            cmd = self.get_history_item(opts.item)
429            if cmd == None:
430                return
431
432            print("Executing '{0}'".format(cmd))
433
434            return self.onecmd(cmd)
435
436
437
438    ############### connect
439    def do_connect (self, line):
440        '''Connects to the server and acquire ports\n'''
441
442        self.stateless_client.connect_line(line)
443
444    def help_connect (self):
445        self.do_connect("-h")
446
447    def do_disconnect (self, line):
448        '''Disconnect from the server\n'''
449
450        # stop any monitors before disconnecting
451        self.cap_mngr.stop()
452        self.stateless_client.disconnect_line(line)
453
454
455    @verify_connected
456    def do_acquire (self, line):
457        '''Acquire ports\n'''
458
459        self.stateless_client.acquire_line(line)
460
461
462    @verify_connected
463    def do_release (self, line):
464        '''Release ports\n'''
465        self.stateless_client.release_line(line)
466
467    @verify_connected
468    def do_reacquire (self, line):
469        '''reacquire all the ports under your logged user name'''
470        self.stateless_client.reacquire_line(line)
471
472    def help_acquire (self):
473        self.do_acquire("-h")
474
475    def help_release (self):
476        self.do_release("-h")
477
478    def help_reacquire (self):
479        self.do_reacquire("-h")
480
481    ############### start
482
483    def complete_start(self, text, line, begidx, endidx):
484        s = line.split()
485        l = len(s)
486
487        file_flags = parsing_opts.get_flags(parsing_opts.FILE_PATH)
488
489        if (l > 1) and (s[l - 1] in file_flags):
490            return TRexConsole.tree_autocomplete("")
491
492        if (l > 2) and (s[l - 2] in file_flags):
493            return TRexConsole.tree_autocomplete(s[l - 1])
494
495    complete_push = complete_start
496
497    @verify_connected
498    def do_start(self, line):
499        '''Start selected traffic in specified port(s) on TRex\n'''
500
501        self.stateless_client.start_line(line)
502
503
504
505    def help_start(self):
506        self.do_start("-h")
507
508    ############# stop
509    @verify_connected
510    def do_stop(self, line):
511        '''stops port(s) transmitting traffic\n'''
512
513        self.stateless_client.stop_line(line)
514
515    def help_stop(self):
516        self.do_stop("-h")
517
518    ############# update
519    @verify_connected
520    def do_update(self, line):
521        '''update speed of port(s) currently transmitting traffic\n'''
522
523        self.stateless_client.update_line(line)
524
525    def help_update (self):
526        self.do_update("-h")
527
528    ############# pause
529    @verify_connected
530    def do_pause(self, line):
531        '''pause port(s) transmitting traffic\n'''
532
533        self.stateless_client.pause_line(line)
534
535    ############# resume
536    @verify_connected
537    def do_resume(self, line):
538        '''resume port(s) transmitting traffic\n'''
539
540        self.stateless_client.resume_line(line)
541
542
543
544    ########## reset
545    @verify_connected
546    def do_reset (self, line):
547        '''force stop all ports\n'''
548        self.stateless_client.reset_line(line)
549
550
551    ######### validate
552    @verify_connected
553    def do_validate (self, line):
554        '''validates port(s) stream configuration\n'''
555
556        self.stateless_client.validate_line(line)
557
558
559    @verify_connected
560    def do_stats(self, line):
561        '''Fetch statistics from TRex server by port\n'''
562        self.stateless_client.show_stats_line(line)
563
564
565    def help_stats(self):
566        self.do_stats("-h")
567
568    @verify_connected
569    def do_streams(self, line):
570        '''Fetch statistics from TRex server by port\n'''
571        self.stateless_client.show_streams_line(line)
572
573
574    def help_streams(self):
575        self.do_streams("-h")
576
577    @verify_connected
578    def do_clear(self, line):
579        '''Clear cached local statistics\n'''
580        self.stateless_client.clear_stats_line(line)
581
582    @verify_connected
583    def do_service (self, line):
584        '''Sets port(s) service mode state'''
585        self.stateless_client.service_line(line)
586
587    def help_service (self, line):
588        self.do_service("-h")
589
590    @verify_connected
591    def do_pkt (self, line):
592        '''Sends a scapy notation packet'''
593        self.stateless_client.pkt_line(line)
594
595    def help_pkt (self, line):
596        self.do_pkt("-h")
597
598    def help_clear(self):
599        self.do_clear("-h")
600
601
602    def help_events (self):
603        self.do_events("-h")
604
605    def do_events (self, line):
606        '''shows events recieved from server\n'''
607        self.stateless_client.get_events_line(line)
608
609
610    def complete_profile(self, text, line, begidx, endidx):
611        return self.complete_start(text,line, begidx, endidx)
612
613    def do_profile (self, line):
614        '''shows information about a profile'''
615        self.stateless_client.show_profile_line(line)
616
617    # tui
618    @verify_connected
619    def do_tui (self, line):
620        '''Shows a graphical console\n'''
621        parser = parsing_opts.gen_parser(self.stateless_client,
622                                         "tui",
623                                         self.do_tui.__doc__,
624                                         parsing_opts.XTERM,
625                                         parsing_opts.LOCKED)
626
627        opts = parser.parse_args(line.split())
628
629        if not opts:
630            return opts
631        if opts.xterm:
632            if not os.path.exists('/usr/bin/xterm'):
633                print(format_text("XTERM does not exists on this machine", 'bold'))
634                return
635
636            info = self.stateless_client.get_connection_info()
637
638            exe = './trex-console --top -t -q -s {0} -p {1} --async_port {2}'.format(info['server'], info['sync_port'], info['async_port'])
639            cmd = ['/usr/bin/xterm', '-geometry', '{0}x{1}'.format(self.tui.MIN_COLS, self.tui.MIN_ROWS), '-sl', '0', '-title', 'trex_tui', '-e', exe]
640
641            # detach child
642            self.terminal = subprocess.Popen(cmd, preexec_fn = os.setpgrp)
643
644            return
645
646
647        try:
648            with self.stateless_client.logger.supress():
649                self.tui.show(self.stateless_client, self.save_console_history, locked = opts.locked)
650
651        except self.tui.ScreenSizeException as e:
652            print(format_text(str(e) + "\n", 'bold'))
653
654
655    def help_tui (self):
656        do_tui("-h")
657
658
659    # quit function
660    def do_quit(self, line):
661        '''Exit the client\n'''
662        return True
663
664
665    def do_help (self, line):
666         '''Shows This Help Screen\n'''
667         if line:
668             try:
669                 func = getattr(self, 'help_' + line)
670             except AttributeError:
671                 try:
672                     doc = getattr(self, 'do_' + line).__doc__
673                     if doc:
674                         self.stdout.write("%s\n"%str(doc))
675                         return
676                 except AttributeError:
677                     pass
678                 self.stdout.write("%s\n"%str(self.nohelp % (line,)))
679                 return
680             func()
681             return
682
683         print("\nSupported Console Commands:")
684         print("----------------------------\n")
685
686         cmds =  [x[3:] for x in self.get_names() if x.startswith("do_")]
687         hidden = ['EOF', 'q', 'exit', 'h', 'shell']
688         for cmd in cmds:
689             if cmd in hidden:
690                 continue
691
692             try:
693                 doc = getattr(self, 'do_' + cmd).__doc__
694                 if doc:
695                     help = str(doc)
696                 else:
697                     help = "*** Undocumented Function ***\n"
698             except AttributeError:
699                 help = "*** Undocumented Function ***\n"
700
701             l=help.splitlines()
702             print("{:<30} {:<30}".format(cmd + " - ",l[0] ))
703
704
705    # a custorm cmdloop wrapper
706    def start(self):
707        try:
708            while True:
709                try:
710                    self.cmdloop()
711                    break
712                except KeyboardInterrupt as e:
713                    if not readline.get_line_buffer():
714                        raise KeyboardInterrupt
715                    else:
716                        print("")
717                        self.intro = None
718                        continue
719
720        finally:
721            # capture manager is not presistent - kill it before going out
722            self.cap_mngr.stop()
723
724        if self.terminal:
725            self.terminal.kill()
726
727    # aliases
728    do_exit = do_EOF = do_q = do_quit
729    do_h = do_history
730
731
732# run a script of commands
733def run_script_file (self, filename, stateless_client):
734
735    self.logger.log(format_text("\nRunning script file '{0}'...".format(filename), 'bold'))
736
737    with open(filename) as f:
738        script_lines = f.readlines()
739
740    cmd_table = {}
741
742    # register all the commands
743    cmd_table['start'] = stateless_client.start_line
744    cmd_table['stop']  = stateless_client.stop_line
745    cmd_table['reset'] = stateless_client.reset_line
746
747    for index, line in enumerate(script_lines, start = 1):
748        line = line.strip()
749        if line == "":
750            continue
751        if line.startswith("#"):
752            continue
753
754        sp = line.split(' ', 1)
755        cmd = sp[0]
756        if len(sp) == 2:
757            args = sp[1]
758        else:
759            args = ""
760
761        stateless_client.logger.log(format_text("Executing line {0} : '{1}'\n".format(index, line)))
762
763        if not cmd in cmd_table:
764            print("\n*** Error at line {0} : '{1}'\n".format(index, line))
765            stateless_client.logger.log(format_text("unknown command '{0}'\n".format(cmd), 'bold'))
766            return False
767
768        cmd_table[cmd](args)
769
770    stateless_client.logger.log(format_text("\n[Done]", 'bold'))
771
772    return True
773
774
775#
776def is_valid_file(filename):
777    if not os.path.isfile(filename):
778        raise argparse.ArgumentTypeError("The file '%s' does not exist" % filename)
779
780    return filename
781
782
783
784def setParserOptions():
785    parser = argparse.ArgumentParser(prog="trex_console.py")
786
787    parser.add_argument("-s", "--server", help = "TRex Server [default is localhost]",
788                        default = "localhost",
789                        type = str)
790
791    parser.add_argument("-p", "--port", help = "TRex Server Port  [default is 4501]\n",
792                        default = 4501,
793                        type = int)
794
795    parser.add_argument("--async_port", help = "TRex ASync Publisher Port [default is 4500]\n",
796                        default = 4500,
797                        dest='pub',
798                        type = int)
799
800    parser.add_argument("-u", "--user", help = "User Name  [default is currently logged in user]\n",
801                        default = get_current_user(),
802                        type = str)
803
804    parser.add_argument("-v", "--verbose", dest="verbose",
805                        action="store_true", help="Switch ON verbose option. Default is: OFF.",
806                        default = False)
807
808
809    group = parser.add_mutually_exclusive_group()
810
811    group.add_argument("-a", "--acquire", dest="acquire",
812                       nargs = '+',
813                       type = int,
814                       help="Acquire ports on connect. default is all available ports",
815                       default = None)
816
817    group.add_argument("-r", "--readonly", dest="readonly",
818                       action="store_true",
819                       help="Starts console in a read only mode",
820                       default = False)
821
822
823    parser.add_argument("-f", "--force", dest="force",
824                        action="store_true",
825                        help="Force acquire the requested ports",
826                        default = False)
827
828    parser.add_argument("--batch", dest="batch",
829                        nargs = 1,
830                        type = is_valid_file,
831                        help = "Run the console in a batch mode with file",
832                        default = None)
833
834    parser.add_argument("-t", "--tui", dest="tui",
835                        action="store_true", help="Starts with TUI mode",
836                        default = False)
837
838    parser.add_argument("-x", "--xtui", dest="xtui",
839                        action="store_true", help="Starts with XTERM TUI mode",
840                        default = False)
841
842    parser.add_argument("--top", dest="top",
843                        action="store_true", help="Set the window as always on top",
844                        default = False)
845
846    parser.add_argument("-q", "--quiet", dest="quiet",
847                        action="store_true", help="Starts with all outputs suppressed",
848                        default = False)
849
850    return parser
851
852# a simple info printed on log on
853def show_intro (logger, c):
854    x   = c.get_server_system_info()
855    ver = c.get_server_version().get('version', 'N/A')
856
857    # find out which NICs the server has
858    port_types = {}
859    for port in x['ports']:
860        if 'supp_speeds' in port and port['supp_speeds']:
861            speed = max(port['supp_speeds']) // 1000
862        else:
863            speed = c.ports[port['index']].get_speed_gbps()
864        key = (speed, port.get('description', port['driver']))
865        if key not in port_types:
866            port_types[key] = 0
867        port_types[key] += 1
868
869    port_line = ''
870    for k, v in port_types.items():
871        port_line += "{0} x {1}Gbps @ {2}\t".format(v, k[0], k[1])
872
873    logger.log(format_text("\nServer Info:\n", 'underline'))
874    logger.log("Server version:   {:>}".format(format_text(ver, 'bold')))
875    logger.log("Server CPU:       {:>}".format(format_text("{:>} x {:>}".format(x.get('dp_core_count'), x.get('core_type')), 'bold')))
876    logger.log("Ports count:      {:>}".format(format_text(port_line, 'bold')))
877
878
879def main():
880    parser = setParserOptions()
881    options = parser.parse_args()
882
883    if options.xtui:
884        options.tui = True
885
886    # always on top
887    if options.top:
888        set_window_always_on_top('trex_tui')
889
890
891    # Stateless client connection
892    if options.quiet:
893        verbose_level = LoggerApi.VERBOSE_QUIET
894    elif options.verbose:
895        verbose_level = LoggerApi.VERBOSE_HIGH
896    else:
897        verbose_level = LoggerApi.VERBOSE_REGULAR
898
899    # Stateless client connection
900    logger = ConsoleLogger()
901    stateless_client = STLClient(username = options.user,
902                                 server = options.server,
903                                 sync_port = options.port,
904                                 async_port = options.pub,
905                                 verbose_level = verbose_level,
906                                 logger = logger)
907
908    # TUI or no acquire will give us READ ONLY mode
909    try:
910        stateless_client.connect()
911    except STLError as e:
912        logger.log("Log:\n" + format_text(e.brief() + "\n", 'bold'))
913        return
914
915    if not options.tui and not options.readonly:
916        try:
917            # acquire all ports
918            stateless_client.acquire(options.acquire, force = options.force)
919        except STLError as e:
920            logger.log("Log:\n" + format_text(e.brief() + "\n", 'bold'))
921
922            logger.log("\n*** Failed to acquire all required ports ***\n")
923            return
924
925    if options.readonly:
926        logger.log(format_text("\nRead only mode - only few commands will be available", 'bold'))
927
928    # console
929    try:
930        show_intro(logger, stateless_client)
931
932        # a script mode
933        if options.batch:
934            cont = run_script_file(options.batch[0], stateless_client)
935            if not cont:
936                return
937
938        console = TRexConsole(stateless_client, options.verbose)
939        logger.prompt_redraw = console.prompt_redraw
940
941        # TUI
942        if options.tui:
943            console.do_tui("-x" if options.xtui else "-l")
944
945        else:
946            console.start()
947
948    except KeyboardInterrupt as e:
949        print("\n\n*** Caught Ctrl + C... Exiting...\n\n")
950
951    finally:
952        with stateless_client.logger.supress():
953            stateless_client.disconnect(stop_traffic = False)
954
955
956if __name__ == '__main__':
957    main()
958
959