aboutsummaryrefslogtreecommitdiffstats
path: root/src/vppinfra/file.h
diff options
context:
space:
mode:
authorRuslan Babayev <ruslan@babayev.com>2019-10-31 01:37:50 -0700
committerRuslan Babayev <ruslan@babayev.com>2020-05-01 17:06:52 -0700
commit7f286f720d6fe2115423212dda6af66dd810691d (patch)
treee95269c1c9db7bcf64cc0667be0fe0ce86314a2b /src/vppinfra/file.h
parent073d74d0ba9a6110ed73a38df668c47ceb3ca776 (diff)
api: fix include_guard when path contains a plus
The path to VPP source might contain a '+' when building it with Yocto/OpenEmbedded. Type: fix Signed-off-by: Ruslan Babayev <ruslan@babayev.com> Change-Id: I205ac0de7d8726724af0e30f5b199391e05dc615
Diffstat (limited to 'src/vppinfra/file.h')
0 files changed, 0 insertions, 0 deletions
> 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
#!/router/bin/python

import os
import sys
import subprocess
import misc_methods
import re
import signal
import time
from CProgressDisp import TimedProgressBar
from stateful_tests.tests_exceptions import TRexInUseError
import datetime

class CTRexScenario:
    modes            = set() # list of modes of this setup: loopback, virtual etc.
    server_logs      = False
    is_test_list     = False
    is_init          = False
    is_stl_init      = False
    trex_crashed     = False
    configuration    = None
    trex             = None
    stl_trex         = None
    stl_ports_map    = None
    stl_init_error   = None
    router           = None
    router_cfg       = None
    daemon_log_lines = 0
    setup_name       = None
    setup_dir        = None
    router_image     = None
    trex_version     = None
    scripts_path     = None
    benchmark        = None
    report_dir       = 'reports'
    # logger         = None
    test_types       = {'functional_tests': [], 'stateful_tests': [], 'stateless_tests': []}
    is_copied        = False
    GAManager        = None
    no_daemon        = False
    router_image     = None

class CTRexRunner:
    """This is an instance for generating a CTRexRunner"""

    def __init__ (self, config_dict, yaml):
        self.trex_config = config_dict#misc_methods.load_config_file(config_file)
        self.yaml = yaml


    def get_config (self):
        """ get_config() -> dict

        Returns the stored configuration of the TRex server of the CTRexRunner instance as a dictionary
        """
        return self.trex_config

    def set_yaml_file (self, yaml_path):
        """ update_yaml_file (self, yaml_path) -> None

        Defines the yaml file to be used by the TRex.
        """
        self.yaml = yaml_path


    def generate_run_cmd (self, multiplier, cores, duration, nc = True, export_path="/tmp/trex.txt", **kwargs):
        """ generate_run_cmd(self, multiplier, duration, export_path) -> str

        Generates a custom running command for the kick-off of the TRex traffic generator.
        Returns a command (string) to be issued on the trex server

        Parameters
        ----------
        multiplier : float
            Defines the TRex multiplier factor (platform dependant)
        duration : int
            Defines the duration of the test
        export_path : str
            a full system path to which the results of the trex-run will be logged.

        """
        fileName, fileExtension = os.path.splitext(self.yaml)
        if self.yaml == None:
            raise ValueError('TRex yaml file is not defined')
        elif fileExtension != '.yaml':
            raise TypeError('yaml path is not referencing a .yaml file')

        if 'results_file_path' in kwargs:
            export_path = kwargs['results_file_path']

        trex_cmd_str = './t-rex-64 -c %d -m %f -d %d -f %s '

        if nc:
            trex_cmd_str = trex_cmd_str + ' --nc '

        trex_cmd = trex_cmd_str % (cores,
            multiplier,
            duration,
            self.yaml)
            # self.trex_config['trex_latency'])

        for key, value in kwargs.items():
            tmp_key = key.replace('_','-')
            dash = ' -' if (len(key)==1) else ' --'
            if value == True:
                trex_cmd += (dash + tmp_key)
            else:
                trex_cmd += (dash + '{k} {val}'.format( k = tmp_key, val =  value ))

        print("\nTRex COMMAND: ", trex_cmd)

        cmd = 'sshpass.exp %s %s root "cd %s; %s > %s"' % (self.trex_config['trex_password'],
            self.trex_config['trex_name'],
            self.trex_config['trex_version_path'],
            trex_cmd,
            export_path)

        return cmd;

    def generate_fetch_cmd (self, result_file_full_path="/tmp/trex.txt"):
        """ generate_fetch_cmd(self, result_file_full_path) -> str

        Generates a custom command for which will enable to fetch the resutls of the TRex run.
        Returns a command (string) to be issued on the trex server.

        Example use: fetch_trex_results()                                   -   command that will fetch the content from the default log file- /tmp/trex.txt
                     fetch_trex_results("/tmp/trex_secondary_file.txt")     -   command that will fetch the content from a custom log file- /tmp/trex_secondary_file.txt
        """
        #dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
        script_running_dir = os.path.dirname(os.path.realpath(__file__))    # get the current script working directory so that the sshpass could be accessed.
        cmd = script_running_dir + '/sshpass.exp %s %s root "cat %s"' % (self.trex_config['trex_password'],
            self.trex_config['trex_name'],
            result_file_full_path);
        return cmd;



    def run (self, multiplier, cores, duration, **kwargs):
        """ run(self, multiplier, duration, results_file_path) -> CTRexResults

        Running the TRex server based on the config file.
        Returns a CTRexResults object containing the results of the run.

        Parameters
        ----------
        multiplier : float
            Defines the TRex multiplier factor (platform dependant)
        duration : int
            Defines the duration of the test
        results_file_path : str
            a full system path to which the results of the trex-run will be logged and fetched from.

        """
        tmp_path = None
        # print kwargs
        if 'export_path' in kwargs:
            tmp_path = kwargs['export_path']
            del kwargs['export_path']
            cmd = self.generate_run_cmd(multiplier, cores, duration, tmp_path, **kwargs)
        else:
            cmd = self.generate_run_cmd(multiplier, cores, duration, **kwargs)

#       print 'TRex complete command to be used:'
#       print cmd
        # print kwargs

        progress_thread = TimedProgressBar(duration)
        progress_thread.start()
        interrupted = False
        try:
            start_time = time.time()
            start = datetime.datetime.now()
            results = subprocess.call(cmd, shell = True, stdout = open(os.devnull, 'wb'))
            end_time = time.time()
            fin = datetime.datetime.now()
            # print "Time difference : ", fin-start
            runtime_deviation =  abs(( (end_time - start_time)/ (duration+15) ) - 1)
            print("runtime_deviation: %2.0f %%" % ( runtime_deviation*100.0))
            if (   runtime_deviation  > 0.6 )  :
                # If the run stopped immediately - classify as Trex in use or reachability issue
                interrupted = True
                if ((end_time - start_time) < 2):
                    raise TRexInUseError ('TRex run failed since TRex is used by another process, or due to reachability issues')
                else:
                    CTRexScenario.trex_crashed = True
            # results = subprocess.Popen(cmd, stdout = open(os.devnull, 'wb'),
            #            shell=True, preexec_fn=os.setsid)
        except KeyboardInterrupt:
            print("\nTRex test interrupted by user during traffic generation!!")
            results.killpg(results.pid, signal.SIGTERM)  # Send the kill signal to all the process groups
            interrupted = True
            raise RuntimeError
        finally:
            progress_thread.join(isPlannedStop = (not interrupted) )

        if results!=0:
            sys.stderr.write("TRex run failed. Please Contact trex-dev mailer for further details")
            sys.stderr.flush()
            return None
        elif interrupted:
            sys.stderr.write("TRex run failed due user-interruption.")
            sys.stderr.flush()
            return None
        else:

            if tmp_path:
                cmd = self.generate_fetch_cmd( tmp_path )#**kwargs)#results_file_path)
            else:
                cmd = self.generate_fetch_cmd()

            try:
                run_log = subprocess.check_output(cmd, shell = True)
                trex_result = CTRexResult(None, run_log)
                trex_result.load_file_lines()
                trex_result.parse()

                return trex_result

            except subprocess.CalledProcessError:
                sys.stderr.write("TRex result fetching failed. Please Contact trex-dev mailer for further details")
                sys.stderr.flush()
                return None

class CTRexResult():
    """This is an instance for generating a CTRexResult"""
    def __init__ (self, file, buffer = None):
        self.file = file
        self.buffer = buffer
        self.result = {}


    def load_file_lines (self):
        """ load_file_lines(self) -> None

        Loads into the self.lines the content of self.file
        """
        if self.buffer:
            self.lines = self.buffer.split("\n")
        else:
            f = open(self.file,'r')
            self.lines = f.readlines()
            f.close()


    def dump (self):
        """ dump(self) -> None

        Prints nicely the content of self.result dictionary into the screen
        """
        for key, value in self.result.items():
            print("{0:20} : \t{1}".format(key, float(value)))

    def update (self, key, val, _str):
        """ update (self, key, val, _str) -> None

        Updates the self.result[key] with a possibly new value representation of val
        Example: 15K might be updated into 15000.0

        Parameters
        ----------
        key :
            Key of the self.result dictionary of the TRexResult instance
        val : float
            Key of the self.result dictionary of the TRexResult instance
        _str : str
            a represntation of the BW (.

        """

        s = _str.strip()

        if s[0]=="G":
            val = val*1E9
        elif s[0]=="M":
            val = val*1E6
        elif s[0]=="K":
            val = val*1E3

        if key in self.result:
            if self.result[key] > 0:
                if (val/self.result[key] > 0.97 ):
                    self.result[key]= val
            else:
                self.result[key] = val
        else:
            self.result[key] = val



    def parse (self):
        """ parse(self) -> None

        Parse the content of the result file from the TRex test and upload the data into
        """
        stop_read = False
        d = {
            'total-tx'      : 0,
            'total-rx'      : 0,
            'total-pps'     : 0,
            'total-cps'     : 0,

            'expected-pps'  : 0,
            'expected-cps'  : 0,
            'expected-bps'  : 0,
            'active-flows'  : 0,
            'open-flows'    : 0
        }

        self.error = ""

        # Parse the output of the test, line by line (each line matches another RegEx and as such
        # different rules apply
        for line in self.lines:
            match = re.match(".*/var/run/.rte_config.*", line)
            if match:
                stop_read = True
                continue

            #Total-Tx        :     462.42 Mbps   Nat_time_out    :        0   ==> we try to parse the next decimal in this case Nat_time_out
#           match = re.match("\W*(\w(\w|[-])+)\W*([:]|[=])\W*(\d+[.]\d+)\W*\w+\W+(\w+)\W*([:]|[=])\W*(\d+)(.*)", line);
#           if match:
#               key = misc_methods.mix_string(match.group(5))
#               val = float(match.group(7))
#               # continue to parse !! we try the second
#               self.result[key] = val #update latest

            # check if we need to stop reading
            match = re.match(".*latency daemon has stopped.*", line)
            if match:
                stop_read = True
                continue

            match = re.match("\W*(\w(\w|[-])+)\W*([:]|[=])\W*(\d+[.]\d+)(.*ps)\s+(\w+)\W*([:]|[=])\W*(\d+)", line)
            if match:
                key = misc_methods.mix_string(match.group(1))
                val = float(match.group(4))
                if key in d:
                   if stop_read == False:
                       self.update (key, val, match.group(5))
                else:
                    self.result[key] = val # update latest
                key2 = misc_methods.mix_string(match.group(6))
                val2 = int(match.group(8))
                self.result[key2] = val2 # always take latest


            match = re.match("\W*(\w(\w|[-])+)\W*([:]|[=])\W*(\d+[.]\d+)(.*)", line)
            if match:
               key = misc_methods.mix_string(match.group(1))
               val = float(match.group(4))
               if key in d:
                   if stop_read == False:
                       self.update (key, val, match.group(5))
               else:
                    self.result[key] = val # update latest
               continue

            match = re.match("\W*(\w(\w|[-])+)\W*([:]|[=])\W*(\d+)(.*)", line)
            if match:
                key = misc_methods.mix_string(match.group(1))
                val = float(match.group(4))
                self.result[key] = val #update latest
                continue

            match = re.match("\W*(\w(\w|[-])+)\W*([:]|[=])\W*(OK)(.*)", line)
            if match:
                key = misc_methods.mix_string(match.group(1))
                val = 0 # valid
                self.result[key] = val #update latest
                continue

            match = re.match("\W*(Cpu Utilization)\W*([:]|[=])\W*(\d+[.]\d+)  %(.*)", line)
            if match:
                key = misc_methods.mix_string(match.group(1))
                val = float(match.group(3))
                if key in self.result:
                    if (self.result[key] < val): # update only if larger than previous value
                        self.result[key] = val
                else:
                    self.result[key] = val
                continue

            match = re.match(".*(rx_check\s.*)\s+:\s+(\w+)", line)
            if match:
                key = misc_methods.mix_string(match.group(1))
                try:
                    val = int(match.group(2))
                except ValueError: # corresponds with rx_check validation case
                    val = match.group(2)
                finally:
                    self.result[key] = val
                continue


    def get_status (self, drop_expected = False):
        if (self.error != ""):
            print(self.error)
            return (self.STATUS_ERR_FATAL)

        d = self.result

        # test for latency
        latency_limit = 5000
        if ( d['maximum-latency'] > latency_limit ):
            self.reason="Abnormal latency measured (higher than %s" % latency_limit
            return self.STATUS_ERR_LATENCY

        # test for drops
        if drop_expected == False:
            if ( d['total-pkt-drop'] > 0 ):
                self.reason=" At least one packet dropped "
                return self.STATUS_ERR_DROP

        # test for rx/tx distance
        rcv_vs_tx = d['total-tx']/d['total-rx']
        if ( (rcv_vs_tx >1.2) or (rcv_vs_tx <0.9) ):
            self.reason="rx and tx should be close"
            return self.STATUS_ERR_RX_TX_DISTANCE

        # expected measurement
        expect_vs_measued=d['total-tx']/d['expected-bps']
        if ( (expect_vs_measued >1.1) or (expect_vs_measued < 0.9) ) :
            print(expect_vs_measued)
            print(d['total-tx'])
            print(d['expected-bps'])
            self.reason="measure is not as expected"
            return self.STATUS_ERR_BAD_EXPECTED_MEASUREMENT

        if ( d['latency-any-error'] !=0 ):
            self.reason=" latency-any-error has error"
            return self.STATUS_ERR_LATENCY_ANY_ERROR

        return self.STATUS_OK

        # return types
        STATUS_OK = 0
        STATUS_ERR_FATAL = 1
        STATUS_ERR_LATENCY = 2
        STATUS_ERR_DROP = 3
        STATUS_ERR_RX_TX_DISTANCE = 4
        STATUS_ERR_BAD_EXPECTED_MEASUREMENT = 5,
        STATUS_ERR_LATENCY_ANY_ERROR = 6

def test_TRex_result_parser():
    t=CTRexResult('trex.txt');
    t.load_file_lines()
    t.parse()
    print(t.result)




if __name__ == "__main__":
    #test_TRex_result_parser();
    pass