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