146bbe560Simarom#!/usr/bin/env python
2823b8294SYaroslav Brustinov
3823b8294SYaroslav Brustinov__copyright__ = "Copyright 2014"
4823b8294SYaroslav Brustinov
5823b8294SYaroslav Brustinov"""
6823b8294SYaroslav BrustinovName:
7823b8294SYaroslav Brustinov     trex_unit_test.py
8823b8294SYaroslav Brustinov
9823b8294SYaroslav Brustinov
10823b8294SYaroslav BrustinovDescription:
11823b8294SYaroslav Brustinov
12d279c8c1SYaroslav Brustinov    This script creates the functionality to test the performance of the TRex traffic generator
13d279c8c1SYaroslav Brustinov    The tested scenario is a TRex TG directly connected to a Cisco router.
14823b8294SYaroslav Brustinov
15823b8294SYaroslav Brustinov::
16823b8294SYaroslav Brustinov
17823b8294SYaroslav Brustinov    Topology:
18823b8294SYaroslav Brustinov
19823b8294SYaroslav Brustinov       -------                         --------
20823b8294SYaroslav Brustinov      |       | Tx---1gig/10gig----Rx |        |
21d279c8c1SYaroslav Brustinov      | TRex  |                       | router |
22823b8294SYaroslav Brustinov      |       | Rx---1gig/10gig----Tx |        |
23823b8294SYaroslav Brustinov       -------                         --------
24823b8294SYaroslav Brustinov
25823b8294SYaroslav Brustinov"""
26823b8294SYaroslav Brustinov
27823b8294SYaroslav Brustinovimport os
28823b8294SYaroslav Brustinovimport sys
29823b8294SYaroslav Brustinovimport outer_packages
30420216e5SHanoh Haimimport datetime
31823b8294SYaroslav Brustinovimport nose
32823b8294SYaroslav Brustinovfrom nose.plugins import Plugin
33c376394cSYaroslav Brustinovfrom nose.plugins.xunit import escape_cdata
34a76479bcSYaroslav Brustinovfrom nose.selector import Selector
3523fabbe8SHanoh Haimfrom nose.exc import SkipTest
3623fabbe8SHanoh Haimfrom nose.pyversion import force_unicode, format_exception
37823b8294SYaroslav Brustinovimport CustomLogger
38823b8294SYaroslav Brustinovimport misc_methods
39823b8294SYaroslav Brustinovfrom rednose import RedNose
40823b8294SYaroslav Brustinovimport termstyle
41afefddfaSYaroslav Brustinovfrom trex import CTRexScenario
42b91c216dSYaroslav Brustinovfrom trex_stf_lib.trex_client import *
43b91c216dSYaroslav Brustinovfrom trex_stf_lib.trex_exceptions import *
44afefddfaSYaroslav Brustinovfrom trex_stl_lib.api import *
45a29339a3Simaromfrom trex_stl_lib.utils.GAObjClass import GAmanager_Regression
46420216e5SHanoh Haimimport trex_elk
47823b8294SYaroslav Brustinovimport trex
48823b8294SYaroslav Brustinovimport socket
49823b8294SYaroslav Brustinovfrom pprint import pprint
50afefddfaSYaroslav Brustinovimport time
5189b608aeSYaroslav Brustinovfrom distutils.dir_util import mkpath
5223fabbe8SHanoh Haimimport re
5323fabbe8SHanoh Haimfrom io import StringIO
5423fabbe8SHanoh Haim
5523fabbe8SHanoh Haim
5623fabbe8SHanoh Haim
5723fabbe8SHanoh HaimTEST_ID = re.compile(r'^(.*?)(\(.*\))$')
5823fabbe8SHanoh Haim
5923fabbe8SHanoh Haimdef id_split(idval):
6023fabbe8SHanoh Haim    m = TEST_ID.match(idval)
6123fabbe8SHanoh Haim    if m:
6223fabbe8SHanoh Haim        name, fargs = m.groups()
6323fabbe8SHanoh Haim        head, tail = name.rsplit(".", 1)
6423fabbe8SHanoh Haim        return [head, tail+fargs]
6523fabbe8SHanoh Haim    else:
6623fabbe8SHanoh Haim        return idval.rsplit(".", 1)
67823b8294SYaroslav Brustinov
68a76479bcSYaroslav Brustinov# nose overrides
69a76479bcSYaroslav Brustinov
70a76479bcSYaroslav Brustinov# option to select wanted test by name without file, class etc.
71a76479bcSYaroslav Brustinovdef new_Selector_wantMethod(self, method, orig_Selector_wantMethod = Selector.wantMethod):
72a76479bcSYaroslav Brustinov    result = orig_Selector_wantMethod(self, method)
7303a49691SYaroslav Brustinov    return result and (not CTRexScenario.test or list(filter(lambda t: t in getattr(method, '__name__', ''), CTRexScenario.test.split(','))))
74a76479bcSYaroslav Brustinov
75a76479bcSYaroslav BrustinovSelector.wantMethod = new_Selector_wantMethod
76a76479bcSYaroslav Brustinov
77a76479bcSYaroslav Brustinovdef new_Selector_wantFunction(self, function, orig_Selector_wantFunction = Selector.wantFunction):
78a76479bcSYaroslav Brustinov    result = orig_Selector_wantFunction(self, function)
7903a49691SYaroslav Brustinov    return result and (not CTRexScenario.test or list(filter(lambda t: t in getattr(function, '__name__', ''), CTRexScenario.test.split(','))))
80a76479bcSYaroslav Brustinov
81a76479bcSYaroslav BrustinovSelector.wantFunction = new_Selector_wantFunction
82a76479bcSYaroslav Brustinov
83e5ec923eSYaroslav Brustinov# override nose's strange representation of setUpClass errors
84e5ec923eSYaroslav Brustinovdef __suite_repr__(self):
851bc9c49fSYaroslav Brustinov    if hasattr(self.context, '__module__'): # inside class, setUpClass etc.
861bc9c49fSYaroslav Brustinov        class_repr = nose.suite._strclass(self.context)
871bc9c49fSYaroslav Brustinov    else:                                   # outside of class, setUpModule etc.
881bc9c49fSYaroslav Brustinov        class_repr = nose.suite._strclass(self.__class__)
891bc9c49fSYaroslav Brustinov    return '%s.%s' % (class_repr, getattr(self.context, '__name__', self.context))
90e5ec923eSYaroslav Brustinov
91e5ec923eSYaroslav Brustinovnose.suite.ContextSuite.__repr__ = __suite_repr__
92e5ec923eSYaroslav Brustinovnose.suite.ContextSuite.__str__  = __suite_repr__
93e5ec923eSYaroslav Brustinov
94a76479bcSYaroslav Brustinov# /nose overrides
95a76479bcSYaroslav Brustinov
9682b3ac03SYaroslav Brustinovdef fatal(txt):
9782b3ac03SYaroslav Brustinov    print(txt)
9882b3ac03SYaroslav Brustinov    sys.exit(1)
9982b3ac03SYaroslav Brustinov
100823b8294SYaroslav Brustinovdef check_trex_path(trex_path):
101823b8294SYaroslav Brustinov    if os.path.isfile('%s/trex_daemon_server' % trex_path):
102823b8294SYaroslav Brustinov        return os.path.abspath(trex_path)
103823b8294SYaroslav Brustinov
104823b8294SYaroslav Brustinovdef check_setup_path(setup_path):
105823b8294SYaroslav Brustinov    if os.path.isfile('%s/config.yaml' % setup_path):
106823b8294SYaroslav Brustinov        return os.path.abspath(setup_path)
107823b8294SYaroslav Brustinov
108823b8294SYaroslav Brustinov
109823b8294SYaroslav Brustinovdef get_trex_path():
110823b8294SYaroslav Brustinov    latest_build_path = check_trex_path(os.getenv('TREX_UNDER_TEST'))    # TREX_UNDER_TEST is env var pointing to <trex-core>/scripts
111823b8294SYaroslav Brustinov    if not latest_build_path:
112823b8294SYaroslav Brustinov        latest_build_path = check_trex_path(os.path.join(os.pardir, os.pardir))
113823b8294SYaroslav Brustinov    if not latest_build_path:
11482b3ac03SYaroslav Brustinov        fatal('Could not determine trex_under_test folder, try setting env.var. TREX_UNDER_TEST')
115823b8294SYaroslav Brustinov    return latest_build_path
116823b8294SYaroslav Brustinov
1173f747bcfSYaroslav Brustinov
1183f747bcfSYaroslav Brustinovdef address_to_ip(address):
119a76479bcSYaroslav Brustinov    for i in range(5):
1203f747bcfSYaroslav Brustinov        try:
1213f747bcfSYaroslav Brustinov            return socket.gethostbyname(address)
1223f747bcfSYaroslav Brustinov        except:
1233f747bcfSYaroslav Brustinov            continue
1243f747bcfSYaroslav Brustinov    return socket.gethostbyname(address)
1253f747bcfSYaroslav Brustinov
126823b8294SYaroslav Brustinov
12723fabbe8SHanoh Haimclass TRexTee(object):
12823fabbe8SHanoh Haim    def __init__(self, encoding, *args):
12923fabbe8SHanoh Haim        self._encoding = encoding
13023fabbe8SHanoh Haim        self._streams = args
13123fabbe8SHanoh Haim
13223fabbe8SHanoh Haim    def write(self, data):
13323fabbe8SHanoh Haim        data = force_unicode(data, self._encoding)
13423fabbe8SHanoh Haim        for s in self._streams:
13523fabbe8SHanoh Haim            s.write(data)
13623fabbe8SHanoh Haim
13723fabbe8SHanoh Haim    def writelines(self, lines):
13823fabbe8SHanoh Haim        for line in lines:
13923fabbe8SHanoh Haim            self.write(line)
14023fabbe8SHanoh Haim
14123fabbe8SHanoh Haim    def flush(self):
14223fabbe8SHanoh Haim        for s in self._streams:
14323fabbe8SHanoh Haim            s.flush()
14423fabbe8SHanoh Haim
14523fabbe8SHanoh Haim    def isatty(self):
14623fabbe8SHanoh Haim        return False
14723fabbe8SHanoh Haim
14823fabbe8SHanoh Haim
149823b8294SYaroslav Brustinovclass CTRexTestConfiguringPlugin(Plugin):
15023fabbe8SHanoh Haim    encoding = 'UTF-8'
15123fabbe8SHanoh Haim
15223fabbe8SHanoh Haim    def __init__(self):
15323fabbe8SHanoh Haim        super(CTRexTestConfiguringPlugin, self).__init__()
15423fabbe8SHanoh Haim        self._capture_stack = []
15523fabbe8SHanoh Haim        self._currentStdout = None
15623fabbe8SHanoh Haim        self._currentStderr = None
15723fabbe8SHanoh Haim
15823fabbe8SHanoh Haim    def _timeTaken(self):
15923fabbe8SHanoh Haim        if hasattr(self, '_timer'):
16023fabbe8SHanoh Haim            taken = time.time() - self._timer
16123fabbe8SHanoh Haim        else:
16223fabbe8SHanoh Haim            # test died before it ran (probably error in setup())
16323fabbe8SHanoh Haim            # or success/failure added before test started probably
16423fabbe8SHanoh Haim            # due to custom TestResult munging
16523fabbe8SHanoh Haim            taken = 0.0
16623fabbe8SHanoh Haim        return taken
16723fabbe8SHanoh Haim
16823fabbe8SHanoh Haim    def _startCapture(self):
16923fabbe8SHanoh Haim        self._capture_stack.append((sys.stdout, sys.stderr))
17023fabbe8SHanoh Haim        self._currentStdout = StringIO()
17123fabbe8SHanoh Haim        self._currentStderr = StringIO()
17223fabbe8SHanoh Haim        sys.stdout = TRexTee(self.encoding, self._currentStdout, sys.stdout)
17323fabbe8SHanoh Haim        sys.stderr = TRexTee(self.encoding, self._currentStderr, sys.stderr)
17423fabbe8SHanoh Haim
17523fabbe8SHanoh Haim    def startContext(self, context):
17623fabbe8SHanoh Haim        self._startCapture()
17723fabbe8SHanoh Haim
17823fabbe8SHanoh Haim    def stopContext(self, context):
17923fabbe8SHanoh Haim        self._endCapture()
18023fabbe8SHanoh Haim
18123fabbe8SHanoh Haim    def beforeTest(self, test):
18223fabbe8SHanoh Haim        self._timer = time.time()
18323fabbe8SHanoh Haim        self._startCapture()
18423fabbe8SHanoh Haim
18523fabbe8SHanoh Haim    def _endCapture(self):
18623fabbe8SHanoh Haim        if self._capture_stack:
18723fabbe8SHanoh Haim            sys.stdout, sys.stderr = self._capture_stack.pop()
18823fabbe8SHanoh Haim
18923fabbe8SHanoh Haim    def afterTest(self, test):
19023fabbe8SHanoh Haim        self._endCapture()
19123fabbe8SHanoh Haim        self._currentStdout = None
19223fabbe8SHanoh Haim        self._currentStderr = None
19323fabbe8SHanoh Haim
19423fabbe8SHanoh Haim    def _getCapturedStdout(self):
19523fabbe8SHanoh Haim        if self._currentStdout:
19623fabbe8SHanoh Haim            value = self._currentStdout.getvalue()
19723fabbe8SHanoh Haim            if value:
19823fabbe8SHanoh Haim                return '<system-out><![CDATA[%s]]></system-out>' % escape_cdata(
19923fabbe8SHanoh Haim                        value)
20023fabbe8SHanoh Haim        return ''
20123fabbe8SHanoh Haim
20223fabbe8SHanoh Haim    def _getCapturedStderr(self):
20323fabbe8SHanoh Haim        if self._currentStderr:
20423fabbe8SHanoh Haim            value = self._currentStderr.getvalue()
20523fabbe8SHanoh Haim            if value:
20623fabbe8SHanoh Haim                return '<system-err><![CDATA[%s]]></system-err>' % escape_cdata(
20723fabbe8SHanoh Haim                        value)
20823fabbe8SHanoh Haim        return ''
20923fabbe8SHanoh Haim
21023fabbe8SHanoh Haim    def addError(self, test, err, capt=None):
21123fabbe8SHanoh Haim        elk = CTRexScenario.elk
21223fabbe8SHanoh Haim        if elk:
213f22fba6eSHanoh Haim            taken = self._timeTaken()
214f22fba6eSHanoh Haim            id = test.id()
215f22fba6eSHanoh Haim            err_msg=self._getCapturedStdout()+self._getCapturedStderr();
216f22fba6eSHanoh Haim            name=id_split(id)[-1]
217f22fba6eSHanoh Haim
21823fabbe8SHanoh Haim            elk_obj = trex.copy_elk_info ()
21923fabbe8SHanoh Haim            elk_obj['test']={
22023fabbe8SHanoh Haim                       "name"   : name,
221f22fba6eSHanoh Haim                       "name_key"   : name,
222f22fba6eSHanoh Haim                       "name_full"  : id,
223f22fba6eSHanoh Haim                       "type"  : self.get_operation_mode (),
224f22fba6eSHanoh Haim                       "duration_sec"  : taken,
225f22fba6eSHanoh Haim                       "result" :  "ERROR",
226f22fba6eSHanoh Haim                       "stdout" : err_msg,
22723fabbe8SHanoh Haim            };
22823fabbe8SHanoh Haim            #pprint(elk_obj['test']);
22923fabbe8SHanoh Haim            elk.reg.push_data(elk_obj)
23023fabbe8SHanoh Haim
23123fabbe8SHanoh Haim
23223fabbe8SHanoh Haim
23323fabbe8SHanoh Haim    def addFailure(self, test, err, capt=None, tb_info=None):
23423fabbe8SHanoh Haim        elk = CTRexScenario.elk
23523fabbe8SHanoh Haim        if elk:
236f22fba6eSHanoh Haim            taken = self._timeTaken()
237f22fba6eSHanoh Haim            tb = format_exception(err, self.encoding)
238f22fba6eSHanoh Haim            id = test.id()
239f22fba6eSHanoh Haim            err_msg=self._getCapturedStdout()+self._getCapturedStderr();
240f22fba6eSHanoh Haim            name=id_split(id)[-1]
241f22fba6eSHanoh Haim
24223fabbe8SHanoh Haim            elk_obj = trex.copy_elk_info ()
24323fabbe8SHanoh Haim            elk_obj['test']={
24423fabbe8SHanoh Haim                       "name"   : name,
245f22fba6eSHanoh Haim                       "name_key"   : name,
246f22fba6eSHanoh Haim                       "name_full"  : id,
24723fabbe8SHanoh Haim                        "type"  : self.get_operation_mode (),
24823fabbe8SHanoh Haim                        "duration_sec"  : taken,
24923fabbe8SHanoh Haim                        "result" :  "FAILURE",
25023fabbe8SHanoh Haim                        "stdout" : err_msg,
25123fabbe8SHanoh Haim            };
25223fabbe8SHanoh Haim            #pprint(elk_obj['test']);
25323fabbe8SHanoh Haim            elk.reg.push_data(elk_obj)
25423fabbe8SHanoh Haim
25523fabbe8SHanoh Haim
25623fabbe8SHanoh Haim
25723fabbe8SHanoh Haim    def addSuccess(self, test, capt=None):
25823fabbe8SHanoh Haim        elk = CTRexScenario.elk
25923fabbe8SHanoh Haim        if elk:
260f22fba6eSHanoh Haim            taken = self._timeTaken()
261f22fba6eSHanoh Haim            id = test.id()
262f22fba6eSHanoh Haim            name=id_split(id)[-1]
26323fabbe8SHanoh Haim            elk_obj = trex.copy_elk_info ()
26423fabbe8SHanoh Haim            elk_obj['test']={
26523fabbe8SHanoh Haim                       "name"   : name,
266f22fba6eSHanoh Haim                       "name_key"   : name,
267f22fba6eSHanoh Haim                       "name_full"  : id,
268f22fba6eSHanoh Haim                       "type"  : self.get_operation_mode (),
269f22fba6eSHanoh Haim                       "duration_sec"  : taken,
270f22fba6eSHanoh Haim                       "result" :  "PASS",
271f22fba6eSHanoh Haim                       "stdout" : "",
27223fabbe8SHanoh Haim            };
27323fabbe8SHanoh Haim            #pprint(elk_obj['test']);
27423fabbe8SHanoh Haim            elk.reg.push_data(elk_obj)
27523fabbe8SHanoh Haim
27623fabbe8SHanoh Haim
27723fabbe8SHanoh Haim
27823fabbe8SHanoh Haim    def get_operation_mode (self):
27923fabbe8SHanoh Haim        if self.stateful:
28023fabbe8SHanoh Haim            return('stateful');
28123fabbe8SHanoh Haim        return('stateless');
28223fabbe8SHanoh Haim
28323fabbe8SHanoh Haim
28423fabbe8SHanoh Haim
28523fabbe8SHanoh Haim
28623fabbe8SHanoh Haim##### option/configure
28723fabbe8SHanoh Haim
288823b8294SYaroslav Brustinov    def options(self, parser, env = os.environ):
289823b8294SYaroslav Brustinov        super(CTRexTestConfiguringPlugin, self).options(parser, env)
290823b8294SYaroslav Brustinov        parser.add_option('--cfg', '--trex-scenario-config', action='store',
291823b8294SYaroslav Brustinov                            dest='config_path',
292823b8294SYaroslav Brustinov                            help='Specify path to folder with config.yaml and benchmark.yaml')
293823b8294SYaroslav Brustinov        parser.add_option('--skip-clean', '--skip_clean', action='store_true',
294823b8294SYaroslav Brustinov                            dest='skip_clean_config',
295823b8294SYaroslav Brustinov                            help='Skip the clean configuration replace on the platform.')
296823b8294SYaroslav Brustinov        parser.add_option('--load-image', '--load_image', action='store_true', default = False,
297823b8294SYaroslav Brustinov                            dest='load_image',
298823b8294SYaroslav Brustinov                            help='Install image specified in config file on router.')
299823b8294SYaroslav Brustinov        parser.add_option('--log-path', '--log_path', action='store',
300823b8294SYaroslav Brustinov                            dest='log_path',
301823b8294SYaroslav Brustinov                            help='Specify path for the tests` log to be saved at. Once applied, logs capturing by nose will be disabled.') # Default is CURRENT/WORKING/PATH/trex_log/trex_log.log')
302cc53c3d2SYaroslav Brustinov        parser.add_option('--json-verbose', '--json_verbose', action="store_true", default = False,
303cc53c3d2SYaroslav Brustinov                            dest="json_verbose",
304cc53c3d2SYaroslav Brustinov                            help="Print JSON-RPC commands.")
305cc53c3d2SYaroslav Brustinov        parser.add_option('--telnet-verbose', '--telnet_verbose', action="store_true", default = False,
306cc53c3d2SYaroslav Brustinov                            dest="telnet_verbose",
307cc53c3d2SYaroslav Brustinov                            help="Print telnet commands and responces.")
308823b8294SYaroslav Brustinov        parser.add_option('--server-logs', '--server_logs', action="store_true", default = False,
309afefddfaSYaroslav Brustinov                            dest="server_logs",
310823b8294SYaroslav Brustinov                            help="Print server side (TRex and trex_daemon) logs per test.")
3113f747bcfSYaroslav Brustinov        parser.add_option('--kill-running', '--kill_running', action="store_true", default = False,
312afefddfaSYaroslav Brustinov                            dest="kill_running",
313823b8294SYaroslav Brustinov                            help="Kills running TRex process on remote server (useful for regression).")
314afefddfaSYaroslav Brustinov        parser.add_option('--func', '--functional', action="store_true", default = False,
315afefddfaSYaroslav Brustinov                            dest="functional",
316afefddfaSYaroslav Brustinov                            help="Run functional tests.")
317afefddfaSYaroslav Brustinov        parser.add_option('--stl', '--stateless', action="store_true", default = False,
318afefddfaSYaroslav Brustinov                            dest="stateless",
319afefddfaSYaroslav Brustinov                            help="Run stateless tests.")
320afefddfaSYaroslav Brustinov        parser.add_option('--stf', '--stateful', action="store_true", default = False,
321afefddfaSYaroslav Brustinov                            dest="stateful",
322afefddfaSYaroslav Brustinov                            help="Run stateful tests.")
3232ef9113cSYaroslav Brustinov        parser.add_option('--pkg', type = str,
3242ef9113cSYaroslav Brustinov                            help="Run with given TRex package. Make sure the path available at server machine. Implies --restart-daemon.")
3252ef9113cSYaroslav Brustinov        parser.add_option('--restart-daemon', action="store_true", default = False,
3262ef9113cSYaroslav Brustinov                            help="Flag that specifies to restart daemon. Implied by --pkg.")
327fff0f8efSYaroslav Brustinov        parser.add_option('--collect', action="store_true", default = False,
328fff0f8efSYaroslav Brustinov                            help="Alias to --collect-only.")
329fff0f8efSYaroslav Brustinov        parser.add_option('--warmup', action="store_true", default = False,
330fff0f8efSYaroslav Brustinov                            help="Warm up the system for stateful: run 30 seconds 9k imix test without check of results.")
33182a3c6f2SYaroslav Brustinov        parser.add_option('--test-client-package', '--test_client_package', action="store_true", default = False,
33282a3c6f2SYaroslav Brustinov                            help="Includes tests of client package.")
333e831a539SYaroslav Brustinov        parser.add_option('--long', action="store_true", default = False,
334e831a539SYaroslav Brustinov                            help="Flag of long tests (stability).")
33589b608aeSYaroslav Brustinov        parser.add_option('--ga', action="store_true", default = False,
33689b608aeSYaroslav Brustinov                            help="Flag to send benchmarks to GA.")
337fad99b60SYaroslav Brustinov        parser.add_option('--no-daemon', action="store_true", default = False,
338fad99b60SYaroslav Brustinov                            dest="no_daemon",
339fad99b60SYaroslav Brustinov                            help="Flag that specifies to use running stl server, no need daemons.")
340e058f6e0SYaroslav Brustinov        parser.add_option('--debug-image', action="store_true", default = False,
341e058f6e0SYaroslav Brustinov                            help="Flag that specifies to use t-rex-64-debug as TRex executable.")
3422ef9113cSYaroslav Brustinov        parser.add_option('--trex-args', default = '',
343ddbed433SYaroslav Brustinov                            help="Additional TRex arguments (--no-watchdog etc.).")
3442ef9113cSYaroslav Brustinov        parser.add_option('-t', '--test', type = str,
345d3c7c398SYaroslav Brustinov                            help = 'Test name to run (without file, class etc.). Can choose several names splitted by comma.')
346d3c7c398SYaroslav Brustinov        parser.add_option('--no-dut-config', action = 'store_true',
347d3c7c398SYaroslav Brustinov                            help = 'Skip the config of DUT to save time. Implies --skip-clean.')
348fad99b60SYaroslav Brustinov
349823b8294SYaroslav Brustinov
350823b8294SYaroslav Brustinov    def configure(self, options, conf):
351fad99b60SYaroslav Brustinov        self.collect_only   = options.collect_only
352cc53c3d2SYaroslav Brustinov        self.functional     = options.functional
353cc53c3d2SYaroslav Brustinov        self.stateless      = options.stateless
354cc53c3d2SYaroslav Brustinov        self.stateful       = options.stateful
355cc53c3d2SYaroslav Brustinov        self.pkg            = options.pkg
3562ef9113cSYaroslav Brustinov        self.restart_daemon = options.restart_daemon
357cc53c3d2SYaroslav Brustinov        self.json_verbose   = options.json_verbose
358cc53c3d2SYaroslav Brustinov        self.telnet_verbose = options.telnet_verbose
359fad99b60SYaroslav Brustinov        self.no_daemon      = options.no_daemon
360a76479bcSYaroslav Brustinov        CTRexScenario.test  = options.test
3612ef9113cSYaroslav Brustinov        if self.no_daemon and (self.pkg or self.restart_daemon):
36282b3ac03SYaroslav Brustinov            fatal('You have specified both --no-daemon and either --pkg or --restart-daemon at same time.')
36382b3ac03SYaroslav Brustinov        if self.no_daemon and self.stateful :
36482b3ac03SYaroslav Brustinov            fatal("Can't run stateful without daemon.")
365fad99b60SYaroslav Brustinov        if self.collect_only or self.functional:
3667fbeb2f7SYaroslav Brustinov            return
367823b8294SYaroslav Brustinov        if CTRexScenario.setup_dir and options.config_path:
36882b3ac03SYaroslav Brustinov            fatal('Please either define --cfg or use env. variable SETUP_DIR, not both.')
369f5817145SYaroslav Brustinov        if not options.config_path and CTRexScenario.setup_dir:
370a29339a3Simarom            options.config_path = CTRexScenario.setup_dir
371f5817145SYaroslav Brustinov        if not options.config_path:
37282b3ac03SYaroslav Brustinov            fatal('Please specify path to config.yaml using --cfg parameter or env. variable SETUP_DIR')
373f5817145SYaroslav Brustinov        options.config_path = options.config_path.rstrip('/')
374f5817145SYaroslav Brustinov        CTRexScenario.setup_name = os.path.basename(options.config_path)
375240dbb67SYaroslav Brustinov        self.configuration = misc_methods.load_complete_config_file(os.path.join(options.config_path, 'config.yaml'))
376240dbb67SYaroslav Brustinov        self.configuration.trex['trex_name'] = address_to_ip(self.configuration.trex['trex_name']) # translate hostname to ip
377240dbb67SYaroslav Brustinov        self.benchmark     = misc_methods.load_benchmark_config_file(os.path.join(options.config_path, 'benchmark.yaml'))
378240dbb67SYaroslav Brustinov        self.enabled       = True
379823b8294SYaroslav Brustinov        self.modes         = self.configuration.trex.get('modes', [])
380823b8294SYaroslav Brustinov        self.kill_running  = options.kill_running
381823b8294SYaroslav Brustinov        self.load_image    = options.load_image
382823b8294SYaroslav Brustinov        self.clean_config  = False if options.skip_clean_config else True
383d3c7c398SYaroslav Brustinov        self.no_dut_config = options.no_dut_config
384d3c7c398SYaroslav Brustinov        if self.no_dut_config:
385d3c7c398SYaroslav Brustinov            self.clean_config = False
386823b8294SYaroslav Brustinov        self.server_logs   = options.server_logs
387823b8294SYaroslav Brustinov        if options.log_path:
388823b8294SYaroslav Brustinov            self.loggerPath = options.log_path
389823b8294SYaroslav Brustinov        # initialize CTRexScenario global testing class, to be used by all tests
390823b8294SYaroslav Brustinov        CTRexScenario.configuration = self.configuration
391a76479bcSYaroslav Brustinov        CTRexScenario.no_daemon     = options.no_daemon
392823b8294SYaroslav Brustinov        CTRexScenario.benchmark     = self.benchmark
393823b8294SYaroslav Brustinov        CTRexScenario.modes         = set(self.modes)
394823b8294SYaroslav Brustinov        CTRexScenario.server_logs   = self.server_logs
395e058f6e0SYaroslav Brustinov        CTRexScenario.debug_image   = options.debug_image
3960d42a434SYaroslav Brustinov        CTRexScenario.json_verbose  = self.json_verbose
397dbff547fSYaroslav Brustinov        additional_args             = self.configuration.trex.get('trex_add_args', '')
398fad99b60SYaroslav Brustinov        if not self.no_daemon:
399e058f6e0SYaroslav Brustinov            CTRexScenario.trex      = CTRexClient(trex_host   = self.configuration.trex['trex_name'],
400e058f6e0SYaroslav Brustinov                                                  verbose     = self.json_verbose,
401ddbed433SYaroslav Brustinov                                                  debug_image = options.debug_image,
402dbff547fSYaroslav Brustinov                                                  trex_args   = options.trex_args + ' ' + additional_args)
40382b3ac03SYaroslav Brustinov
4042ef9113cSYaroslav Brustinov        if self.pkg or self.restart_daemon:
4058e230286SYaroslav Brustinov            if not CTRexScenario.trex.check_master_connectivity():
40682b3ac03SYaroslav Brustinov                fatal('Could not connect to master daemon')
40789b608aeSYaroslav Brustinov        if options.ga and CTRexScenario.setup_name:
408ce9cf273SYaroslav Brustinov            CTRexScenario.GAManager  = GAmanager_Regression(
409ce9cf273SYaroslav Brustinov                    GoogleID         = CTRexScenario.global_cfg['google']['id'],
410ce9cf273SYaroslav Brustinov                    AnalyticsUserID  = CTRexScenario.setup_name,
411ce9cf273SYaroslav Brustinov                    QueueSize        = CTRexScenario.global_cfg['google']['queue_size'],
412ce9cf273SYaroslav Brustinov                    Timeout          = CTRexScenario.global_cfg['google']['timeout'],  # seconds
413ce9cf273SYaroslav Brustinov                    UserPermission   = 1,
414ce9cf273SYaroslav Brustinov                    BlockingMode     = CTRexScenario.global_cfg['google']['blocking'],
415ce9cf273SYaroslav Brustinov                    appName          = 'TRex',
416ce9cf273SYaroslav Brustinov                    appVer           = CTRexScenario.trex_version)
417ce9cf273SYaroslav Brustinov
418ce9cf273SYaroslav Brustinov            CTRexScenario.elk = trex_elk.TRexEs(
419ce9cf273SYaroslav Brustinov                    CTRexScenario.global_cfg['elk']['server'],
420ce9cf273SYaroslav Brustinov                    CTRexScenario.global_cfg['elk']['port']);
421420216e5SHanoh Haim            self.set_cont_elk_info ()
422420216e5SHanoh Haim
423420216e5SHanoh Haim    def set_cont_elk_info (self):
424420216e5SHanoh Haim        elk_info={}
4258df11b2eSHanoh Haim        timestamp = datetime.datetime.now() - datetime.timedelta(hours=2); # need to update this
426420216e5SHanoh Haim        info = {};
427420216e5SHanoh Haim
428420216e5SHanoh Haim
429420216e5SHanoh Haim        img={}
430420216e5SHanoh Haim        img['sha'] = "v2.14"                #TBD
431420216e5SHanoh Haim        img['build_time'] = timestamp.strftime("%Y-%m-%d %H:%M:%S")
432842be705SHanoh Haim        img['version'] = "v2.14"           #TBD need to fix
433420216e5SHanoh Haim        img['formal'] = False
434420216e5SHanoh Haim
4358c3c9822SYaroslav Brustinov        setup = {}
4368c3c9822SYaroslav Brustinov
4378c3c9822SYaroslav Brustinov        setup['distro'] = 'None'            #TBD 'Ubunto14.03'
4388c3c9822SYaroslav Brustinov        setup['kernel'] = 'None'           #TBD '2.6.12'
4398c3c9822SYaroslav Brustinov        setup['baremetal'] = True          #TBD
4408c3c9822SYaroslav Brustinov        setup['hypervisor'] = 'None'       #TBD
4418c3c9822SYaroslav Brustinov        setup['name'] = CTRexScenario.setup_name
4428c3c9822SYaroslav Brustinov
4438c3c9822SYaroslav Brustinov        setup['cpu-sockets'] = 0           #TBD  2
4448c3c9822SYaroslav Brustinov        setup['cores'] = 0                 #TBD 16
4458c3c9822SYaroslav Brustinov        setup['cpu-speed'] = -1            #TBD 3.5
4468c3c9822SYaroslav Brustinov
4478c3c9822SYaroslav Brustinov        setup['dut'] = 'None'             #TBD 'loopback'
4488c3c9822SYaroslav Brustinov        setup['drv-name'] = 'None'         #TBD 'mlx5'
4498c3c9822SYaroslav Brustinov        setup['nic-ports'] = 0             #TBD 2
4508c3c9822SYaroslav Brustinov        setup['total-nic-ports'] = 0       #TBD 2
451420216e5SHanoh Haim        setup['nic-speed'] = "None"      #"40GbE" TBD
452420216e5SHanoh Haim
453420216e5SHanoh Haim
454420216e5SHanoh Haim
455420216e5SHanoh Haim        info['image'] = img
456420216e5SHanoh Haim        info['setup'] = setup
457420216e5SHanoh Haim
458420216e5SHanoh Haim        elk_info['info'] =info;
459420216e5SHanoh Haim
4608c3c9822SYaroslav Brustinov        elk_info['timestamp'] = timestamp.strftime("%Y-%m-%d %H:%M:%S")  # need to update it
461e562c918SYaroslav Brustinov        elk_info['build_id'] = os.environ.get('BUILD_NUM')
4628c3c9822SYaroslav Brustinov        elk_info['scenario'] = os.environ.get('SCENARIO')
463420216e5SHanoh Haim
464420216e5SHanoh Haim        CTRexScenario.elk_info = elk_info
465420216e5SHanoh Haim
466240dbb67SYaroslav Brustinov
467240dbb67SYaroslav Brustinov    def begin (self):
468138686f3SYaroslav Brustinov        client = CTRexScenario.trex
4692ef9113cSYaroslav Brustinov        if self.pkg and not CTRexScenario.pkg_updated:
470138686f3SYaroslav Brustinov            if client.master_daemon.is_trex_daemon_running() and client.get_trex_cmds() and not self.kill_running:
47182b3ac03SYaroslav Brustinov                fatal("Can't update TRex, it's running. Consider adding --kill-running flag.")
47289b608aeSYaroslav Brustinov            print('Updating TRex to %s' % self.pkg)
473138686f3SYaroslav Brustinov            if not client.master_daemon.update_trex(self.pkg):
47482b3ac03SYaroslav Brustinov                fatal('Failed to update TRex.')
47589b608aeSYaroslav Brustinov            else:
4762ef9113cSYaroslav Brustinov                print('Updated.')
4772ef9113cSYaroslav Brustinov            CTRexScenario.pkg_updated = True
478afefddfaSYaroslav Brustinov        if self.functional or self.collect_only:
479afefddfaSYaroslav Brustinov            return
4802ef9113cSYaroslav Brustinov        if self.pkg or self.restart_daemon:
481fad99b60SYaroslav Brustinov            print('Restarting TRex daemon server')
482138686f3SYaroslav Brustinov            res = client.restart_trex_daemon()
483fad99b60SYaroslav Brustinov            if not res:
48482b3ac03SYaroslav Brustinov                fatal('Could not restart TRex daemon server')
485a78ab130SYaroslav Brustinov            print('Restarted.')
486afefddfaSYaroslav Brustinov
487db0cc224SYaroslav Brustinov        if not self.no_daemon:
488f2aeea52SYaroslav Brustinov            if self.kill_running:
489138686f3SYaroslav Brustinov                client.kill_all_trexes()
490db0cc224SYaroslav Brustinov            elif client.get_trex_cmds():
491db0cc224SYaroslav Brustinov                fatal('TRex is already running. Use --kill-running flag to kill it.')
49282b3ac03SYaroslav Brustinov            try:
49382b3ac03SYaroslav Brustinov                client.check_server_connectivity()
49482b3ac03SYaroslav Brustinov            except Exception as e:
49582b3ac03SYaroslav Brustinov                fatal(e)
49682b3ac03SYaroslav Brustinov
497fad99b60SYaroslav Brustinov
498823b8294SYaroslav Brustinov        if 'loopback' not in self.modes:
499afefddfaSYaroslav Brustinov            CTRexScenario.router_cfg = dict(config_dict      = self.configuration.router,
500afefddfaSYaroslav Brustinov                                            forceImageReload = self.load_image,
501cc53c3d2SYaroslav Brustinov                                            silent_mode      = not self.telnet_verbose,
502afefddfaSYaroslav Brustinov                                            forceCleanConfig = self.clean_config,
503d3c7c398SYaroslav Brustinov                                            no_dut_config    = self.no_dut_config,
504afefddfaSYaroslav Brustinov                                            tftp_config_dict = self.configuration.tftp)
505823b8294SYaroslav Brustinov        try:
506823b8294SYaroslav Brustinov            CustomLogger.setup_custom_logger('TRexLogger', self.loggerPath)
507823b8294SYaroslav Brustinov        except AttributeError:
508823b8294SYaroslav Brustinov            CustomLogger.setup_custom_logger('TRexLogger')
509afefddfaSYaroslav Brustinov
510823b8294SYaroslav Brustinov    def finalize(self, result):
51123fabbe8SHanoh Haim        while self._capture_stack:
51223fabbe8SHanoh Haim            self._endCapture()
51323fabbe8SHanoh Haim
51419ef256bSYaroslav Brustinov        if self.functional or self.collect_only:
5150f49e42fSYaroslav Brustinov            return
516ac332c5cSYaroslav Brustinov        #CTRexScenario.is_init = False
517f4b44c0dSYaroslav Brustinov        if self.stateful:
518f4b44c0dSYaroslav Brustinov            CTRexScenario.trex = None
519f4b44c0dSYaroslav Brustinov        if self.stateless:
5201454985eSYaroslav Brustinov            if self.no_daemon:
5211454985eSYaroslav Brustinov                if CTRexScenario.stl_trex and CTRexScenario.stl_trex.is_connected():
5221454985eSYaroslav Brustinov                    CTRexScenario.stl_trex.disconnect()
5231454985eSYaroslav Brustinov            else:
524fad99b60SYaroslav Brustinov                CTRexScenario.trex.force_kill(False)
52589b608aeSYaroslav Brustinov            CTRexScenario.stl_trex = None
526823b8294SYaroslav Brustinov
527823b8294SYaroslav Brustinov
528823b8294SYaroslav Brustinovdef save_setup_info():
529823b8294SYaroslav Brustinov    try:
530823b8294SYaroslav Brustinov        if CTRexScenario.setup_name and CTRexScenario.trex_version:
531823b8294SYaroslav Brustinov            setup_info = ''
532823b8294SYaroslav Brustinov            for key, value in CTRexScenario.trex_version.items():
533823b8294SYaroslav Brustinov                setup_info += '{0:8}: {1}\n'.format(key, value)
534823b8294SYaroslav Brustinov            cfg = CTRexScenario.configuration
535823b8294SYaroslav Brustinov            setup_info += 'Server: %s, Modes: %s' % (cfg.trex.get('trex_name'), cfg.trex.get('modes'))
536823b8294SYaroslav Brustinov            if cfg.router:
537823b8294SYaroslav Brustinov                setup_info += '\nRouter: Model: %s, Image: %s' % (cfg.router.get('model'), CTRexScenario.router_image)
538e058f6e0SYaroslav Brustinov            if CTRexScenario.debug_image:
539e058f6e0SYaroslav Brustinov                setup_info += '\nDebug image: %s' % CTRexScenario.debug_image
540e058f6e0SYaroslav Brustinov
541823b8294SYaroslav Brustinov            with open('%s/report_%s.info' % (CTRexScenario.report_dir, CTRexScenario.setup_name), 'w') as f:
542823b8294SYaroslav Brustinov                f.write(setup_info)
543823b8294SYaroslav Brustinov    except Exception as err:
54489a2be82Simarom        print('Error saving setup info: %s ' % err)
545823b8294SYaroslav Brustinov
546823b8294SYaroslav Brustinov
547823b8294SYaroslav Brustinovif __name__ == "__main__":
548afefddfaSYaroslav Brustinov
549823b8294SYaroslav Brustinov    # setting defaults. By default we run all the test suite
550823b8294SYaroslav Brustinov    specific_tests              = False
551823b8294SYaroslav Brustinov    CTRexScenario.report_dir    = 'reports'
552afefddfaSYaroslav Brustinov    need_to_copy                = False
5532dfd6cf6SYaroslav Brustinov    setup_dir                   = os.getenv('SETUP_DIR', '').rstrip('/')
554823b8294SYaroslav Brustinov    CTRexScenario.setup_dir     = check_setup_path(setup_dir)
555afefddfaSYaroslav Brustinov    CTRexScenario.scripts_path  = get_trex_path()
556823b8294SYaroslav Brustinov    if not CTRexScenario.setup_dir:
5572dfd6cf6SYaroslav Brustinov        CTRexScenario.setup_dir = check_setup_path(os.path.join('setups', setup_dir))
558afefddfaSYaroslav Brustinov
559f5817145SYaroslav Brustinov
5602f75f197SYaroslav Brustinov    nose_argv = ['', '-s', '-v', '--exe', '--rednose', '--detailed-errors']
56182a3c6f2SYaroslav Brustinov    test_client_package = False
56282a3c6f2SYaroslav Brustinov    if '--test-client-package' in sys.argv:
56382a3c6f2SYaroslav Brustinov        test_client_package = True
56482a3c6f2SYaroslav Brustinov
565fff0f8efSYaroslav Brustinov    if '--collect' in sys.argv:
566fff0f8efSYaroslav Brustinov        sys.argv.append('--collect-only')
56719ef256bSYaroslav Brustinov    if '--collect-only' in sys.argv: # this is a user trying simply to view the available tests. no need xunit.
568afefddfaSYaroslav Brustinov        CTRexScenario.is_test_list   = True
569afefddfaSYaroslav Brustinov        xml_arg                      = ''
57019ef256bSYaroslav Brustinov    else:
571afefddfaSYaroslav Brustinov        xml_name                     = 'unit_test.xml'
572afefddfaSYaroslav Brustinov        if CTRexScenario.setup_dir:
573afefddfaSYaroslav Brustinov            CTRexScenario.setup_name = os.path.basename(CTRexScenario.setup_dir)
574afefddfaSYaroslav Brustinov            xml_name = 'report_%s.xml' % CTRexScenario.setup_name
575afefddfaSYaroslav Brustinov        xml_arg= '--xunit-file=%s/%s' % (CTRexScenario.report_dir, xml_name)
57689b608aeSYaroslav Brustinov        mkpath(CTRexScenario.report_dir)
57719ef256bSYaroslav Brustinov
578afefddfaSYaroslav Brustinov    sys_args = sys.argv[:]
57919ef256bSYaroslav Brustinov    for i, arg in enumerate(sys.argv):
580823b8294SYaroslav Brustinov        if 'log-path' in arg:
581afefddfaSYaroslav Brustinov            nose_argv += ['--nologcapture']
58219ef256bSYaroslav Brustinov        else:
583afefddfaSYaroslav Brustinov            for tests_type in CTRexScenario.test_types.keys():
584afefddfaSYaroslav Brustinov                if tests_type in arg:
585afefddfaSYaroslav Brustinov                    specific_tests = True
586afefddfaSYaroslav Brustinov                    CTRexScenario.test_types[tests_type].append(arg[arg.find(tests_type):])
587afefddfaSYaroslav Brustinov                    sys_args.remove(arg)
588823b8294SYaroslav Brustinov
589afefddfaSYaroslav Brustinov    if not specific_tests:
590afefddfaSYaroslav Brustinov        for key in ('--func', '--functional'):
591afefddfaSYaroslav Brustinov            if key in sys_args:
592afefddfaSYaroslav Brustinov                CTRexScenario.test_types['functional_tests'].append('functional_tests')
593afefddfaSYaroslav Brustinov                sys_args.remove(key)
594afefddfaSYaroslav Brustinov        for key in ('--stf', '--stateful'):
595afefddfaSYaroslav Brustinov            if key in sys_args:
596afefddfaSYaroslav Brustinov                CTRexScenario.test_types['stateful_tests'].append('stateful_tests')
597afefddfaSYaroslav Brustinov                sys_args.remove(key)
598afefddfaSYaroslav Brustinov        for key in ('--stl', '--stateless'):
599afefddfaSYaroslav Brustinov            if key in sys_args:
600afefddfaSYaroslav Brustinov                CTRexScenario.test_types['stateless_tests'].append('stateless_tests')
601afefddfaSYaroslav Brustinov                sys_args.remove(key)
602afefddfaSYaroslav Brustinov        # Run all of the tests or just the selected ones
603afefddfaSYaroslav Brustinov        if not sum([len(x) for x in CTRexScenario.test_types.values()]):
604afefddfaSYaroslav Brustinov            for key in CTRexScenario.test_types.keys():
605afefddfaSYaroslav Brustinov                CTRexScenario.test_types[key].append(key)
606afefddfaSYaroslav Brustinov
607afefddfaSYaroslav Brustinov    nose_argv += sys_args
608afefddfaSYaroslav Brustinov
609a76479bcSYaroslav Brustinov    addplugins = [RedNose(), CTRexTestConfiguringPlugin()]
610afefddfaSYaroslav Brustinov    result = True
611823b8294SYaroslav Brustinov    try:
612afefddfaSYaroslav Brustinov        if len(CTRexScenario.test_types['functional_tests']):
613afefddfaSYaroslav Brustinov            additional_args = ['--func'] + CTRexScenario.test_types['functional_tests']
614afefddfaSYaroslav Brustinov            if xml_arg:
615afefddfaSYaroslav Brustinov                additional_args += ['--with-xunit', xml_arg.replace('.xml', '_functional.xml')]
616a76479bcSYaroslav Brustinov            result = nose.run(argv = nose_argv + additional_args, addplugins = addplugins)
6178c3c9822SYaroslav Brustinov        if len(CTRexScenario.test_types['stateless_tests']):
6188c3c9822SYaroslav Brustinov            additional_args = ['--stl', 'stateless_tests/stl_general_test.py:STLBasic_Test.test_connectivity'] + CTRexScenario.test_types['stateless_tests']
6198c3c9822SYaroslav Brustinov            if not test_client_package:
6208c3c9822SYaroslav Brustinov                additional_args.extend(['-a', '!client_package'])
6218c3c9822SYaroslav Brustinov            if xml_arg:
6228c3c9822SYaroslav Brustinov                additional_args += ['--with-xunit', xml_arg.replace('.xml', '_stateless.xml')]
6238c3c9822SYaroslav Brustinov            result = nose.run(argv = nose_argv + additional_args, addplugins = addplugins) and result
624afefddfaSYaroslav Brustinov        if len(CTRexScenario.test_types['stateful_tests']):
625fff0f8efSYaroslav Brustinov            additional_args = ['--stf']
626fff0f8efSYaroslav Brustinov            if '--warmup' in sys.argv:
627fff0f8efSYaroslav Brustinov                additional_args.append('stateful_tests/trex_imix_test.py:CTRexIMIX_Test.test_warm_up')
628fff0f8efSYaroslav Brustinov            additional_args += CTRexScenario.test_types['stateful_tests']
62982a3c6f2SYaroslav Brustinov            if not test_client_package:
63082a3c6f2SYaroslav Brustinov                additional_args.extend(['-a', '!client_package'])
631afefddfaSYaroslav Brustinov            if xml_arg:
632afefddfaSYaroslav Brustinov                additional_args += ['--with-xunit', xml_arg.replace('.xml', '_stateful.xml')]
633a76479bcSYaroslav Brustinov            result = nose.run(argv = nose_argv + additional_args, addplugins = addplugins) and result
634fff0f8efSYaroslav Brustinov    #except Exception as e:
635fff0f8efSYaroslav Brustinov    #    result = False
636fff0f8efSYaroslav Brustinov    #    print(e)
637823b8294SYaroslav Brustinov    finally:
638afefddfaSYaroslav Brustinov        save_setup_info()
639afefddfaSYaroslav Brustinov
640b91c216dSYaroslav Brustinov    if not CTRexScenario.is_test_list:
641b91c216dSYaroslav Brustinov        if result == True:
642b91c216dSYaroslav Brustinov            print(termstyle.green("""
643afefddfaSYaroslav Brustinov                 ..::''''::..
644afefddfaSYaroslav Brustinov               .;''        ``;.
645afefddfaSYaroslav Brustinov              ::    ::  ::    ::
646afefddfaSYaroslav Brustinov             ::     ::  ::     ::
647afefddfaSYaroslav Brustinov             ::     ::  ::     ::
648afefddfaSYaroslav Brustinov             :: .:' ::  :: `:. ::
649afefddfaSYaroslav Brustinov             ::  :          :  ::
650afefddfaSYaroslav Brustinov              :: `:.      .:' ::
651afefddfaSYaroslav Brustinov               `;..``::::''..;'
652afefddfaSYaroslav Brustinov                 ``::,,,,::''
653afefddfaSYaroslav Brustinov
654afefddfaSYaroslav Brustinov               ___  ___   __________
655afefddfaSYaroslav Brustinov              / _ \/ _ | / __/ __/ /
656afefddfaSYaroslav Brustinov             / ___/ __ |_\ \_\ \/_/
657afefddfaSYaroslav Brustinov            /_/  /_/ |_/___/___(_)
658afefddfaSYaroslav Brustinov
65989a2be82Simarom        """))
660b91c216dSYaroslav Brustinov            sys.exit(0)
661b91c216dSYaroslav Brustinov        else:
662b91c216dSYaroslav Brustinov            print(termstyle.red("""
663b91c216dSYaroslav Brustinov           /\_/\
664b91c216dSYaroslav Brustinov          ( o.o )
665b91c216dSYaroslav Brustinov           > ^ <
666b91c216dSYaroslav Brustinov
667b91c216dSYaroslav BrustinovThis cat is sad, test failed.
668b91c216dSYaroslav Brustinov        """))
669b91c216dSYaroslav Brustinov            sys.exit(-1)
670b91c216dSYaroslav Brustinov
671b91c216dSYaroslav Brustinov
672afefddfaSYaroslav Brustinov
673afefddfaSYaroslav Brustinov
674afefddfaSYaroslav Brustinov
675afefddfaSYaroslav Brustinov
676afefddfaSYaroslav Brustinov
677823b8294SYaroslav Brustinov
678823b8294SYaroslav Brustinov
679823b8294SYaroslav Brustinov
680823b8294SYaroslav Brustinov
681