trex_server.py revision dc905a7d
1#!/usr/bin/python
2
3
4import os
5import stat
6import sys
7import time
8import outer_packages
9import zmq
10import yaml
11from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer
12import jsonrpclib
13from jsonrpclib import Fault
14import binascii
15import socket
16import errno
17import signal
18from common.trex_status_e import TRexStatus
19from common.trex_exceptions import *
20import subprocess
21from random import randrange
22import logging
23import threading
24import CCustomLogger
25from trex_launch_thread import AsynchronousTRexSession
26from zmq_monitor_thread import ZmqMonitorSession
27from argparse import ArgumentParser, RawTextHelpFormatter
28import json
29import re
30import shlex
31import tempfile
32
33try:
34    from .singleton_daemon import register_socket, run_command
35except:
36    from singleton_daemon import register_socket, run_command
37
38
39# setup the logger
40CCustomLogger.setup_custom_logger('TRexServer')
41logger = logging.getLogger('TRexServer')
42
43class CTRexServer(object):
44    """This class defines the server side of the RESTfull interaction with TRex"""
45    DEFAULT_TREX_PATH = '/auto/proj-pcube-b/apps/PL-b/tools/bp_sim2/v1.55/' #'/auto/proj-pcube-b/apps/PL-b/tools/nightly/trex_latest'
46    TREX_START_CMD    = './t-rex-64'
47    DEFAULT_FILE_PATH = '/tmp/trex_files/'
48
49    def __init__(self, trex_path, trex_files_path, trex_host='0.0.0.0', trex_daemon_port=8090, trex_zmq_port=4500, trex_nice=-19):
50        """
51        Parameters
52        ----------
53        trex_host : str
54            a string of the TRex ip address or hostname.
55            default value: machine hostname as fetched from socket.gethostname()
56        trex_daemon_port : int
57            the port number on which the trex-daemon server can be reached
58            default value: 8090
59        trex_zmq_port : int
60            the port number on which trex's zmq module will interact with daemon server
61            default value: 4500
62        nice: int
63            priority of the TRex process
64
65        Instantiate a TRex client object, and connecting it to listening daemon-server
66        """
67        self.TREX_PATH          = os.path.abspath(os.path.dirname(trex_path+'/'))
68        self.trex_files_path    = os.path.abspath(os.path.dirname(trex_files_path+'/'))
69        self.__check_trex_path_validity()
70        self.__check_files_path_validity()
71        self.trex               = CTRex()
72        self.trex_version       = None
73        self.trex_host          = trex_host
74        self.trex_daemon_port   = trex_daemon_port
75        self.trex_zmq_port      = trex_zmq_port
76        self.trex_server_path   = "http://{hostname}:{port}".format( hostname = trex_host, port = trex_daemon_port )
77        self.start_lock         = threading.Lock()
78        self.__reservation      = None
79        self.zmq_monitor        = ZmqMonitorSession(self.trex, self.trex_zmq_port)    # intiate single ZMQ monitor thread for server usage
80        self.trex_nice          = int(trex_nice)
81        if self.trex_nice < -20 or self.trex_nice > 19:
82            err = "Parameter 'nice' should be integer in range [-20, 19]"
83            print(err)
84            logger.error(err)
85            raise Exception(err)
86
87    def add(self, x, y):
88        logger.info("Processing add function. Parameters are: {0}, {1} ".format( x, y ))
89        return x + y
90        # return Fault(-10, "")
91
92    def push_file (self, filename, bin_data):
93        logger.info("Processing push_file() command.")
94        try:
95            filepath = os.path.join(self.trex_files_path, os.path.basename(filename))
96            with open(filepath, 'wb') as f:
97                f.write(binascii.a2b_base64(bin_data))
98            logger.info("push_file() command finished. File is saved as %s" % filepath)
99            return True
100        except IOError as inst:
101            logger.error("push_file method failed. " + str(inst))
102            return False
103
104    def connectivity_check (self):
105        logger.info("Processing connectivity_check function.")
106        return True
107
108    def start(self):
109        """This method fires up the daemon server based on initialized parameters of the class"""
110        # initialize the server instance with given resources
111        register_socket('trex_daemon_server')
112        try:
113            print("Firing up TRex REST daemon @ port {trex_port} ...\n".format( trex_port = self.trex_daemon_port ))
114            logger.info("Firing up TRex REST daemon @ port {trex_port} ...".format( trex_port = self.trex_daemon_port ))
115            logger.info("current working dir is: {0}".format(self.TREX_PATH) )
116            logger.info("current files dir is  : {0}".format(self.trex_files_path) )
117            logger.debug("Starting TRex server. Registering methods to process.")
118            logger.info(self.get_trex_version(base64 = False))
119            self.server = SimpleJSONRPCServer( (self.trex_host, self.trex_daemon_port) )
120        except socket.error as e:
121            if e.errno == errno.EADDRINUSE:
122                logger.error("TRex server requested address already in use. Aborting server launching.")
123                print("TRex server requested address already in use. Aborting server launching.")
124                raise socket.error(errno.EADDRINUSE, "TRex daemon requested address already in use. "
125                                                     "Server launch aborted. Please make sure no other process is "
126                                                     "using the desired server properties.")
127            elif isinstance(e, socket.gaierror) and e.errno == -3:
128                # handling Temporary failure in name resolution exception
129                raise socket.gaierror(-3, "Temporary failure in name resolution.\n"
130                                          "Make sure provided hostname has DNS resolving.")
131            else:
132                raise
133
134        # set further functionality and peripherals to server instance
135        self.server.register_function(self.add)
136        self.server.register_function(self.cancel_reservation)
137        self.server.register_function(self.connectivity_check)
138        self.server.register_function(self.connectivity_check, 'check_connectivity') # alias
139        self.server.register_function(self.force_trex_kill)
140        self.server.register_function(self.get_file)
141        self.server.register_function(self.get_files_list)
142        self.server.register_function(self.get_files_path)
143        self.server.register_function(self.get_latest_dump)
144        self.server.register_function(self.get_running_info)
145        self.server.register_function(self.get_running_status)
146        self.server.register_function(self.get_trex_cmds)
147        self.server.register_function(self.get_trex_config)
148        self.server.register_function(self.get_trex_daemon_log)
149        self.server.register_function(self.get_trex_log)
150        self.server.register_function(self.get_trex_version)
151        self.server.register_function(self.is_reserved)
152        self.server.register_function(self.is_running)
153        self.server.register_function(self.kill_all_trexes)
154        self.server.register_function(self.push_file)
155        self.server.register_function(self.reserve_trex)
156        self.server.register_function(self.start_trex)
157        self.server.register_function(self.stop_trex)
158        self.server.register_function(self.wait_until_kickoff_finish)
159        signal.signal(signal.SIGTSTP, self.stop_handler)
160        signal.signal(signal.SIGTERM, self.stop_handler)
161        try:
162            self.zmq_monitor.start()
163            self.server.serve_forever()
164        except KeyboardInterrupt:
165            logger.info("Daemon shutdown request detected." )
166        finally:
167            self.zmq_monitor.join()            # close ZMQ monitor thread resources
168            self.server.shutdown()
169            #self.server.server_close()
170
171
172    # get files from Trex server and return their content (mainly for logs)
173    @staticmethod
174    def _pull_file(filepath):
175        try:
176            with open(filepath, 'rb') as f:
177                file_content = f.read()
178                return binascii.b2a_base64(file_content).decode(errors='replace')
179        except Exception as e:
180            err_str = "Can't get requested file %s: %s" % (filepath, e)
181            logger.error(err_str)
182            return Fault(-33, err_str)
183
184    # returns True if given path is under TRex package or under /tmp/trex_files
185    def _check_path_under_TRex_or_temp(self, path):
186        if not os.path.relpath(path, self.trex_files_path).startswith(os.pardir):
187            return True
188        if not os.path.relpath(path, self.TREX_PATH).startswith(os.pardir):
189            return True
190        return False
191
192    # gets the file content encoded base64 either from /tmp/trex_files or TRex server dir
193    def get_file(self, filepath):
194        try:
195            logger.info("Processing get_file() command.")
196            if not self._check_path_under_TRex_or_temp(filepath):
197                raise Exception('Given path should be under current TRex package or /tmp/trex_files')
198            return self._pull_file(filepath)
199        except Exception as e:
200            err_str = "Can't get requested file %s: %s" % (filepath, e)
201            logger.error(err_str)
202            return Fault(-33, err_str)
203
204    # get tuple (dirs, files) with directories and files lists from given path (limited under TRex package or /tmp/trex_files)
205    def get_files_list(self, path):
206        try:
207            logger.info("Processing get_files_list() command, given path: %s" % path)
208            if not self._check_path_under_TRex_or_temp(path):
209                raise Exception('Given path should be under current TRex package or /tmp/trex_files')
210            return os.walk(path).next()[1:3]
211        except Exception as e:
212            err_str = "Error processing get_files_list(): %s" % e
213            logger.error(err_str)
214            return Fault(-33, err_str)
215
216    # get Trex log /tmp/trex.txt
217    def get_trex_log(self):
218        logger.info("Processing get_trex_log() command.")
219        return self._pull_file('/tmp/trex.txt')
220
221    # get /etc/trex_cfg.yaml
222    def get_trex_config(self):
223        logger.info("Processing get_trex_config() command.")
224        return self._pull_file('/etc/trex_cfg.yaml')
225
226    # get daemon log /var/log/trex/trex_daemon_server.log
227    def get_trex_daemon_log (self):
228        logger.info("Processing get_trex_daemon_log() command.")
229        return self._pull_file('/var/log/trex/trex_daemon_server.log')
230
231    # get Trex version from ./t-rex-64 --help (last lines starting with "Version : ...")
232    def get_trex_version (self, base64 = True):
233        try:
234            logger.info("Processing get_trex_version() command.")
235            if not self.trex_version:
236                ret_code, stdout, stderr = run_command('./t-rex-64 --help', cwd = self.TREX_PATH)
237                search_result = re.search('\n\s*(Version\s*:.+)', stdout, re.DOTALL)
238                if not search_result:
239                    raise Exception('Could not determine version from ./t-rex-64 --help')
240                self.trex_version = binascii.b2a_base64(search_result.group(1).encode(errors='replace'))
241            if base64:
242                return self.trex_version.decode(errors='replace')
243            else:
244                return binascii.a2b_base64(self.trex_version).decode(errors='replace')
245        except Exception as e:
246            err_str = "Can't get trex version, error: %s" % e
247            logger.error(err_str)
248            return Fault(-33, err_str)
249
250    def stop_handler (self, *args, **kwargs):
251        logger.info("Daemon STOP request detected.")
252        if self.is_running():
253            # in case TRex process is currently running, stop it before terminating server process
254            self.stop_trex(self.trex.get_seq())
255        sys.exit(0)
256
257    def assert_zmq_ok(self):
258        if self.trex.zmq_error:
259            raise Exception('ZMQ thread got error: %s' % self.trex.zmq_error)
260        if not self.zmq_monitor.is_alive():
261            if self.trex.get_status() != TRexStatus.Idle:
262                self.force_trex_kill()
263            raise Exception('ZMQ thread is dead.')
264
265    def is_running (self):
266        run_status = self.trex.get_status()
267        logger.info("Processing is_running() command. Running status is: {stat}".format(stat = run_status) )
268        if run_status==TRexStatus.Running:
269            return True
270        else:
271            return False
272
273    def is_reserved (self):
274        logger.info("Processing is_reserved() command.")
275        return bool(self.__reservation)
276
277    def get_running_status (self):
278        run_status = self.trex.get_status()
279        logger.info("Processing get_running_status() command. Running status is: {stat}".format(stat = run_status) )
280        return { 'state' : run_status.value, 'verbose' : self.trex.get_verbose_status() }
281
282    def get_files_path (self):
283        logger.info("Processing get_files_path() command." )
284        return self.trex_files_path
285
286    def reserve_trex (self, user):
287        if user == "":
288            logger.info("TRex reservation cannot apply to empty string user. Request denied.")
289            return Fault(-33, "TRex reservation cannot apply to empty string user. Request denied.")
290
291        with self.start_lock:
292            logger.info("Processing reserve_trex() command.")
293            if self.is_reserved():
294                if user == self.__reservation['user']:
295                    # return True is the same user is asking and already has the resrvation
296                    logger.info("the same user is asking and already has the resrvation. Re-reserving TRex.")
297                    return True
298
299                logger.info("TRex is already reserved to another user ({res_user}), cannot reserve to another user.".format( res_user = self.__reservation['user'] ))
300                return Fault(-33, "TRex is already reserved to another user ({res_user}). Please make sure TRex is free before reserving it.".format(
301                    res_user = self.__reservation['user']) )  # raise at client TRexInUseError
302            elif self.trex.get_status() != TRexStatus.Idle:
303                logger.info("TRex is currently running, cannot reserve TRex unless in Idle state.")
304                return Fault(-13, 'TRex is currently running, cannot reserve TRex unless in Idle state. Please try again when TRex run finished.')  # raise at client TRexInUseError
305            else:
306                logger.info("TRex is now reserved for user ({res_user}).".format( res_user = user ))
307                self.__reservation = {'user' : user, 'since' : time.ctime()}
308                logger.debug("Reservation details: "+ str(self.__reservation))
309                return True
310
311    def cancel_reservation (self, user):
312        with self.start_lock:
313            logger.info("Processing cancel_reservation() command.")
314            if self.is_reserved():
315                if self.__reservation['user'] == user:
316                    logger.info("TRex reservation to {res_user} has been canceled successfully.".format(res_user = self.__reservation['user']))
317                    self.__reservation = None
318                    return True
319                else:
320                    logger.warning("TRex is reserved to different user than the provided one. Reservation wasn't canceled.")
321                    return Fault(-33, "Cancel reservation request is available to the user that holds the reservation. Request denied")  # raise at client TRexRequestDenied
322
323            else:
324                logger.info("TRex is not reserved to anyone. No need to cancel anything")
325                assert(self.__reservation is None)
326                return False
327
328    def start_trex(self, trex_cmd_options, user, block_to_success = True, timeout = 40, stateless = False, debug_image = False, trex_args = ''):
329        self.assert_zmq_ok()
330        with self.start_lock:
331            logger.info("Processing start_trex() command.")
332            if self.is_reserved():
333                # check if this is not the user to which TRex is reserved
334                if self.__reservation['user'] != user:
335                    logger.info("TRex is reserved to another user ({res_user}). Only that user is allowed to initiate new runs.".format(res_user = self.__reservation['user']))
336                    return Fault(-33, "TRex is reserved to another user ({res_user}). Only that user is allowed to initiate new runs.".format(res_user = self.__reservation['user']))  # raise at client TRexRequestDenied
337            elif self.trex.get_status() != TRexStatus.Idle:
338                err = 'TRex is already taken, cannot create another run until done.'
339                logger.info(err)
340                return Fault(-13, err) # raise at client TRexInUseError
341
342            try:
343                server_cmd_data = self.generate_run_cmd(stateless = stateless, debug_image = debug_image, trex_args = trex_args, **trex_cmd_options)
344                self.zmq_monitor.first_dump = True
345                self.trex.start_trex(self.TREX_PATH, server_cmd_data)
346                logger.info("TRex session has been successfully initiated.")
347                if block_to_success:
348                    # delay server response until TRex is at 'Running' state.
349                    start_time = time.time()
350                    trex_state = None
351                    while (time.time() - start_time) < timeout :
352                        trex_state = self.trex.get_status()
353                        if trex_state != TRexStatus.Starting:
354                            break
355                        else:
356                            time.sleep(0.5)
357                            self.assert_zmq_ok()
358
359                    # check for TRex run started normally
360                    if trex_state == TRexStatus.Starting:   # reached timeout
361                        logger.warning("TimeoutError: TRex initiation outcome could not be obtained, since TRex stays at Starting state beyond defined timeout.")
362                        return Fault(-12, 'TimeoutError: TRex initiation outcome could not be obtained, since TRex stays at Starting state beyond defined timeout.') # raise at client TRexWarning
363                    elif trex_state == TRexStatus.Idle:
364                        return Fault(-11, self.trex.get_verbose_status())   # raise at client TRexError
365
366                # reach here only if TRex is at 'Running' state
367                self.trex.gen_seq()
368                return self.trex.get_seq()          # return unique seq number to client
369
370            except TypeError as e:
371                logger.error("TRex command generation failed, probably because either -f (traffic generation .yaml file) and -c (num of cores) was not specified correctly.\nReceived params: {params}".format( params = trex_cmd_options) )
372                raise TypeError('TRex -f (traffic generation .yaml file) and -c (num of cores) must be specified. %s' % e)
373
374
375    def stop_trex(self, seq):
376        logger.info("Processing stop_trex() command.")
377        if self.trex.get_seq()== seq:
378            logger.debug("Abort request legit since seq# match")
379            return self.trex.stop_trex()
380        else:
381            if self.trex.get_status() != TRexStatus.Idle:
382                logger.warning("Abort request is only allowed to process initiated the run. Request denied.")
383
384                return Fault(-33, 'Abort request is only allowed to process initiated the run. Request denied.')  # raise at client TRexRequestDenied
385            else:
386                return False
387
388    def force_trex_kill (self):
389        logger.info("Processing force_trex_kill() command. --> Killing TRex session indiscriminately.")
390        return self.trex.stop_trex()
391
392    # returns list of tuples (pid, command line) of running TRex(es)
393    def get_trex_cmds(self):
394        logger.info('Processing get_trex_cmds() command.')
395        ret_code, stdout, stderr = run_command('ps -u root --format pid,comm,cmd')
396        if ret_code:
397            raise Exception('Failed to determine running processes, stderr: %s' % stderr)
398        trex_cmds_list = []
399        for line in stdout.splitlines():
400            pid, proc_name, full_cmd = line.strip().split(' ', 2)
401            pid = pid.strip()
402            full_cmd = full_cmd.strip()
403            if proc_name.find('t-rex-64') >= 0:
404                trex_cmds_list.append((pid, full_cmd))
405        return trex_cmds_list
406
407
408    # Silently tries to kill TRexes with given signal.
409    # Responsibility of client to verify with get_trex_cmds.
410    def kill_all_trexes(self, signal_name):
411        logger.info('Processing kill_all_trexes() command.')
412        trex_cmds_list = self.get_trex_cmds()
413        for pid, cmd in trex_cmds_list:
414            logger.info('Killing with signal %s process %s %s' % (signal_name, pid, cmd))
415            os.kill(int(pid), signal_name)
416
417
418    def wait_until_kickoff_finish (self, timeout = 40):
419        # block until TRex exits Starting state
420        logger.info("Processing wait_until_kickoff_finish() command.")
421        trex_state = None
422        start_time = time.time()
423        while (time.time() - start_time) < timeout :
424            self.assert_zmq_ok()
425            trex_state = self.trex.get_status()
426            if trex_state != TRexStatus.Starting:
427                return
428            sleep(0.1)
429        return Fault(-12, 'TimeoutError: TRex initiation outcome could not be obtained, since TRex stays at Starting state beyond defined timeout.') # raise at client TRexWarning
430
431    def get_running_info (self):
432        self.assert_zmq_ok()
433        logger.info("Processing get_running_info() command.")
434        return self.trex.get_running_info()
435
436    def get_latest_dump(self):
437        logger.info("Processing get_latest_dump() command.")
438        return self.trex.get_latest_dump()
439
440    def generate_run_cmd (self, iom = 0, export_path="/tmp/trex.txt", stateless = False, debug_image = False, trex_args = '', **kwargs):
441        """ generate_run_cmd(self, iom, export_path, kwargs) -> str
442
443        Generates a custom running command for the kick-off of the TRex traffic generator.
444        Returns a tuple of command (string) and export path (string) to be issued on the trex server
445
446        Parameters
447        ----------
448        iom: int
449            0 = don't print stats screen to log, 1 = print stats (can generate huge logs)
450        stateless: boolean
451            True = run as stateless, False = require -f and -d arguments
452        kwargs: dictionary
453            Dictionary of parameters for trex. For example: (c=1, nc=True, l_pkt_mode=3).
454            Notice that when sending command line parameters that has -, you need to replace it with _.
455            for example, to have on command line "--l-pkt-mode 3", you need to send l_pkt_mode=3
456        export_path : str
457            Full system path to which the results of the trex-run will be logged.
458
459        """
460        if 'results_file_path' in kwargs:
461            export_path = kwargs['results_file_path']
462            del kwargs['results_file_path']
463        if stateless:
464            kwargs['i'] = True
465
466        # adding additional options to the command
467        trex_cmd_options = ''
468        for key, value in kwargs.items():
469            tmp_key = key.replace('_','-').lstrip('-')
470            dash = ' -' if (len(key)==1) else ' --'
471            if value is True:
472                trex_cmd_options += (dash + tmp_key)
473            elif value is False:
474                continue
475            else:
476                trex_cmd_options += (dash + '{k} {val}'.format( k = tmp_key, val =  value ))
477        if trex_args:
478            trex_cmd_options += ' %s' % trex_args
479
480        self._check_zmq_port(trex_cmd_options)
481
482        if not stateless:
483            if 'f' not in kwargs:
484                raise Exception('Argument -f should be specified in stateful command')
485            if 'd' not in kwargs:
486                raise Exception('Argument -d should be specified in stateful command')
487
488        cmd = "{nice}{run_command}{debug_image} --iom {io} {cmd_options} --no-key".format( # -- iom 0 disables the periodic log to the screen (not needed)
489            nice = '' if self.trex_nice == 0 else 'nice -n %s ' % self.trex_nice,
490            run_command = self.TREX_START_CMD,
491            debug_image = '-debug' if debug_image else '',
492            cmd_options = trex_cmd_options,
493            io          = iom)
494
495        logger.info("TREX FULL COMMAND: {command}".format(command = cmd) )
496
497        return (cmd, export_path, kwargs.get('d', 0))
498
499
500    def _check_zmq_port(self, trex_cmd_options):
501        zmq_cfg_port = 4500 # default
502        parser = ArgumentParser()
503        parser.add_argument('--cfg', default = '/etc/trex_cfg.yaml')
504        args, _ = parser.parse_known_args(shlex.split(trex_cmd_options))
505        if not os.path.exists(args.cfg):
506            raise Exception('Platform config file "%s" does not exist!' % args.cfg)
507        with open(args.cfg) as f:
508            trex_cfg = yaml.safe_load(f.read())
509        if type(trex_cfg) is not list:
510            raise Exception('Platform config file "%s" content should be array.' % args.cfg)
511        if not len(trex_cfg):
512            raise Exception('Platform config file "%s" content should be array with one element.' % args.cfg)
513        trex_cfg = trex_cfg[0]
514        if 'enable_zmq_pub' in trex_cfg and trex_cfg['enable_zmq_pub'] == False:
515            raise Exception('TRex daemon expects ZMQ publisher to be enabled. Please change "enable_zmq_pub" to true.')
516        if 'zmq_pub_port' in trex_cfg:
517            zmq_cfg_port = trex_cfg['zmq_pub_port']
518        if zmq_cfg_port != self.trex_zmq_port:
519            raise Exception('ZMQ port does not match: platform config file is configured to: %s, daemon server to: %s' % (zmq_cfg_port, self.trex_zmq_port))
520
521
522    def __check_trex_path_validity(self):
523        # check for executable existance
524        if not os.path.exists(self.TREX_PATH+'/t-rex-64'):
525            print("The provided TRex path do not contain an executable TRex file.\nPlease check the path and retry.")
526            logger.error("The provided TRex path do not contain an executable TRex file")
527            exit(-1)
528        # check for executable permissions
529        st = os.stat(self.TREX_PATH+'/t-rex-64')
530        if not bool(st.st_mode & (stat.S_IXUSR ) ):
531            print("The provided TRex path do not contain an TRex file with execution privileges.\nPlease check the files permissions and retry.")
532            logger.error("The provided TRex path do not contain an TRex file with execution privileges")
533            exit(-1)
534        else:
535            return
536
537    def __check_files_path_validity(self):
538        # first, check for path existance. otherwise, try creating it with appropriate credentials
539        if not os.path.exists(self.trex_files_path):
540            try:
541                os.makedirs(self.trex_files_path, 0o660)
542                return
543            except os.error as inst:
544                print("The provided files path does not exist and cannot be created with needed access credentials using root user.\nPlease check the path's permissions and retry.")
545                logger.error("The provided files path does not exist and cannot be created with needed access credentials using root user.")
546                exit(-1)
547        elif os.access(self.trex_files_path, os.W_OK):
548            return
549        else:
550            print("The provided files path has insufficient access credentials for root user.\nPlease check the path's permissions and retry.")
551            logger.error("The provided files path has insufficient access credentials for root user")
552            exit(-1)
553
554class CTRex(object):
555    def __init__(self):
556        self.status         = TRexStatus.Idle
557        self.verbose_status = 'TRex is Idle'
558        self.errcode        = None
559        self.session        = None
560        self.zmq_monitor    = None
561        self.__zmq_dump     = {}
562        self.zmq_dump_lock  = threading.Lock()
563        self.zmq_error      = None
564        self.seq            = None
565        self.expect_trex    = threading.Event()
566
567    def get_status(self):
568        return self.status
569
570    def set_status(self, new_status):
571        self.status = new_status
572
573    def get_verbose_status(self):
574        return self.verbose_status
575
576    def set_verbose_status(self, new_status):
577        self.verbose_status = new_status
578
579    def gen_seq (self):
580        self.seq = randrange(1,1000)
581
582    def get_seq (self):
583        return self.seq
584
585    def get_latest_dump(self):
586        with self.zmq_dump_lock:
587            return json.dumps(self.__zmq_dump)
588
589    def update_zmq_dump_key(self, key, val):
590        with self.zmq_dump_lock:
591            self.__zmq_dump[key] = val
592
593    def clear_zmq_dump(self):
594        with self.zmq_dump_lock:
595            self.__zmq_dump = {}
596
597    def get_running_info (self):
598        if self.status == TRexStatus.Running:
599            return self.get_latest_dump()
600        else:
601            logger.info("TRex isn't running. Running information isn't available.")
602            if self.status == TRexStatus.Idle:
603                if self.errcode is not None:    # some error occured
604                    logger.info("TRex is in Idle state, with errors. returning fault")
605                    return Fault(self.errcode, self.verbose_status)               # raise at client relevant exception, depending on the reason the error occured
606                else:
607                    logger.info("TRex is in Idle state, no errors. returning {}")
608                    return u'{}'
609
610            return Fault(-12, self.verbose_status)                                # raise at client TRexWarning, indicating TRex is back to Idle state or still in Starting state
611
612    def stop_trex(self):
613        if self.status == TRexStatus.Idle:
614            # TRex isn't running, nothing to abort
615            logger.info("TRex isn't running. No need to stop anything.")
616            if self.errcode is not None:    # some error occurred, notify client despite TRex already stopped
617                    return Fault(self.errcode, self.verbose_status)               # raise at client relevant exception, depending on the reason the error occured
618            return False
619        else:
620            # handle stopping TRex's run
621            self.session.join()
622            logger.info("TRex session has been successfully aborted.")
623            return True
624
625    def start_trex(self, trex_launch_path, trex_cmd):
626        self.set_status(TRexStatus.Starting)
627        logger.info("TRex running state changed to 'Starting'.")
628        self.set_verbose_status('TRex is starting (data is not available yet)')
629
630        self.errcode    = None
631        self.session    = AsynchronousTRexSession(self, trex_launch_path, trex_cmd)
632        self.session.start()
633        self.expect_trex.set()
634#       self.zmq_monitor= ZmqMonitorSession(self, zmq_port)
635#       self.zmq_monitor.start()
636
637
638
639def generate_trex_parser ():
640    default_path        = os.path.abspath(os.path.join(outer_packages.CURRENT_PATH, os.pardir, os.pardir, os.pardir))
641    default_files_path  = os.path.abspath(CTRexServer.DEFAULT_FILE_PATH)
642
643    parser = ArgumentParser(description = 'Run server application for TRex traffic generator',
644        formatter_class = RawTextHelpFormatter,
645        usage = """
646trex_daemon_server [options]
647""" )
648
649    parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0')
650    parser.add_argument("-p", "--daemon-port", type=int, default = 8090, metavar="PORT", dest="daemon_port",
651        help="Select port on which the daemon runs.\nDefault port is 8090.", action="store")
652    parser.add_argument("-z", "--zmq-port", dest="zmq_port", type=int,
653        action="store", help="Select port on which the ZMQ module listens to TRex.\nDefault port is 4500.", metavar="PORT",
654        default = 4500)
655    parser.add_argument("-t", "--trex-path", dest="trex_path",
656        action="store", help="Specify the compiled TRex directory from which TRex would run.\nDefault path is: {def_path}.".format( def_path = default_path ),
657        metavar="PATH", default = default_path )
658    parser.add_argument("-f", "--files-path", dest="files_path",
659        action="store", help="Specify a path to directory on which pushed files will be saved at.\nDefault path is: {def_path}.".format( def_path = default_files_path ),
660        metavar="PATH", default = default_files_path )
661    parser.add_argument("--trex-host", dest="trex_host",
662        action="store", help="Specify a hostname to be registered as the TRex server.\n"
663                             "Default is to bind all IPs using '0.0.0.0'.",
664        metavar="HOST", default = '0.0.0.0')
665    parser.add_argument('-n', '--nice', dest='nice', action="store", default = -19, type = int,
666        help="Determine the priority TRex process [-20, 19] (lower = higher priority)\nDefault is -19.")
667    return parser
668
669trex_parser = generate_trex_parser()
670
671def do_main_program ():
672
673    args = trex_parser.parse_args()
674    server = CTRexServer(trex_path = args.trex_path,  trex_files_path = args.files_path,
675                         trex_host = args.trex_host, trex_daemon_port = args.daemon_port,
676                         trex_zmq_port = args.zmq_port, trex_nice = args.nice)
677    server.start()
678
679
680if __name__ == "__main__":
681    do_main_program()
682
683