18b52a31eSHanoh Haim#!/router/bin/python-2.7.4
28b52a31eSHanoh Haimimport h_avc
38b52a31eSHanoh Haim
48b52a31eSHanoh Haim
58b52a31eSHanoh Haimimport ConfigParser
68b52a31eSHanoh Haimimport threading
78b52a31eSHanoh Haimimport time,signal
88b52a31eSHanoh Haimimport argparse
98b52a31eSHanoh Haimimport sys
108b52a31eSHanoh Haimimport os
1131597431SYaroslav Brustinovsys.path.append(os.path.join('trex_control_plane', 'stf', 'trex_stf_lib'))
1231597431SYaroslav Brustinovfrom trex_client import CTRexClient
138b52a31eSHanoh Haimimport subprocess
148b52a31eSHanoh Haimfrom time import sleep
158b52a31eSHanoh Haimimport signal
168b52a31eSHanoh Haimimport textwrap
178b52a31eSHanoh Haimimport getpass
188b52a31eSHanoh Haimimport random
198b52a31eSHanoh Haimimport datetime
208b52a31eSHanoh Haimfrom datetime import timedelta
218b52a31eSHanoh Haimimport traceback
228b52a31eSHanoh Haimimport math
238b52a31eSHanoh Haimimport re
248b52a31eSHanoh Haimimport termios
258b52a31eSHanoh Haimimport errno
268b52a31eSHanoh Haimimport smtplib
278b52a31eSHanoh Haimfrom email.MIMEMultipart import MIMEMultipart
288b52a31eSHanoh Haimfrom email.MIMEBase import MIMEBase
298b52a31eSHanoh Haimfrom email.MIMEText import MIMEText
308b52a31eSHanoh Haimfrom email.Utils import COMMASPACE, formatdate
318b52a31eSHanoh Haimfrom email import Encoders
328b52a31eSHanoh Haimfrom email.mime.image import MIMEImage
338b52a31eSHanoh Haim
348b52a31eSHanoh Haimfrom distutils.version import StrictVersion
358b52a31eSHanoh Haim
368b52a31eSHanoh Haimclass TrexRunException(Exception):
378b52a31eSHanoh Haim    def __init__ (self, reason, cmd = None, std_log = None, err_log = None):
388b52a31eSHanoh Haim        self.reason = reason
398b52a31eSHanoh Haim        self.std_log = std_log
408b52a31eSHanoh Haim        self.err_log = err_log
418b52a31eSHanoh Haim        # generate the error message
428b52a31eSHanoh Haim        self.message = "\nSummary of error:\n\n %s\n" % (reason)
438b52a31eSHanoh Haim
448b52a31eSHanoh Haim        if std_log:
458b52a31eSHanoh Haim            self.message += "\nConsole Log:\n\n %s\n" % (self.std_log)
468b52a31eSHanoh Haim
478b52a31eSHanoh Haim        if err_log:
488b52a31eSHanoh Haim            self.message += "\nStd Error Log:\n\n %s\n" % (self.err_log)
498b52a31eSHanoh Haim
508b52a31eSHanoh Haim    def __str__(self):
518b52a31eSHanoh Haim        return self.message
528b52a31eSHanoh Haim
538b52a31eSHanoh Haim
548b52a31eSHanoh Haim############################# utility functions start #################################
558b52a31eSHanoh Haim
568b52a31eSHanoh Haimdef verify_glibc_version ():
578b52a31eSHanoh Haim    x = subprocess.check_output("/usr/bin/ldd --version", shell=True)
5831597431SYaroslav Brustinov    m = re.match("^ldd \([^\)]+\) (.*)", x)
598b52a31eSHanoh Haim    if not m:
608b52a31eSHanoh Haim        raise Exception("Cannot determine LDD version")
618b52a31eSHanoh Haim    current_version = m.group(1)
628b52a31eSHanoh Haim
638b52a31eSHanoh Haim    if StrictVersion(current_version) < StrictVersion("2.5"):
648b52a31eSHanoh Haim        raise Exception("GNU ldd version required for graph plotting is at least 2.5, system is %s - please run simple 'find'" % current_version)
658b52a31eSHanoh Haim
668b52a31eSHanoh Haimdef get_median(numericValues):
678b52a31eSHanoh Haim    theValues = sorted(numericValues)
688b52a31eSHanoh Haim    if len(theValues) % 2 == 1:
698b52a31eSHanoh Haim        return theValues[(len(theValues)+1)/2-1]
708b52a31eSHanoh Haim    else:
718b52a31eSHanoh Haim        lower = theValues[len(theValues)/2-1]
728b52a31eSHanoh Haim        upper = theValues[len(theValues)/2]
738b52a31eSHanoh Haim    return (float(lower + upper)) / 2
748b52a31eSHanoh Haim
758b52a31eSHanoh Haimdef list_to_clusters(l, n):
768b52a31eSHanoh Haim    for i in xrange(0, len(l), n):
778b52a31eSHanoh Haim        yield l[i:i+n]
788b52a31eSHanoh Haim
798b52a31eSHanoh Haimdef cpu_histo_to_str (cpu_histo):
808b52a31eSHanoh Haim    s = "\nCPU Samplings:\n\n"
818b52a31eSHanoh Haim    period = 0
828b52a31eSHanoh Haim
838b52a31eSHanoh Haim    clusters = list(list_to_clusters(cpu_histo, 10))
848b52a31eSHanoh Haim
858b52a31eSHanoh Haim    for cluster in clusters:
868b52a31eSHanoh Haim        period += 10
878b52a31eSHanoh Haim        line = "%3s Seconds: [" % period
888b52a31eSHanoh Haim
898b52a31eSHanoh Haim        cluster += (10 - len(cluster)) * [None]
908b52a31eSHanoh Haim
918b52a31eSHanoh Haim        for x in cluster:
928b52a31eSHanoh Haim            if (x != None):
938b52a31eSHanoh Haim                line += "%5.1f%%, " % x
948b52a31eSHanoh Haim            else:
958b52a31eSHanoh Haim                line += "        "
968b52a31eSHanoh Haim
978b52a31eSHanoh Haim        line = line[:-2] # trim the comma and space
988b52a31eSHanoh Haim        line += " "      # return the space
998b52a31eSHanoh Haim
1008b52a31eSHanoh Haim        line += "]\n"
1018b52a31eSHanoh Haim
1028b52a31eSHanoh Haim        s += line
1038b52a31eSHanoh Haim
1048b52a31eSHanoh Haim    return s
1058b52a31eSHanoh Haim
1068b52a31eSHanoh Haim# Terminal Manager Class
1078b52a31eSHanoh Haimclass TermMng:
1088b52a31eSHanoh Haim    def __enter__(self):
1098b52a31eSHanoh Haim        self.fd = sys.stdin.fileno()
1108b52a31eSHanoh Haim        self.old = termios.tcgetattr(self.fd)
1118b52a31eSHanoh Haim
1128b52a31eSHanoh Haim        # copy new and remove echo
1138b52a31eSHanoh Haim        new = self.old[:]
1148b52a31eSHanoh Haim        new[3] &= ~termios.ECHO
1158b52a31eSHanoh Haim
1168b52a31eSHanoh Haim        self.tcsetattr_flags = termios.TCSAFLUSH
1178b52a31eSHanoh Haim        if hasattr(termios, 'TCSASOFT'):
1188b52a31eSHanoh Haim            self.tcsetattr_flags |= termios.TCSASOFT
1198b52a31eSHanoh Haim
1208b52a31eSHanoh Haim        termios.tcsetattr(self.fd, self.tcsetattr_flags, new)
1218b52a31eSHanoh Haim
1228b52a31eSHanoh Haim    def __exit__ (self ,type, value, traceback):
1238b52a31eSHanoh Haim        termios.tcsetattr(self.fd, self.tcsetattr_flags, self.old)
1248b52a31eSHanoh Haim
1258b52a31eSHanoh Haim############################# utility functions stop #################################
1268b52a31eSHanoh Haim
1278b52a31eSHanoh Haimdef send_mail(send_from, send_to, subject, html_text, txt_attachments=[], images=[], server="localhost"):
1288b52a31eSHanoh Haim    assert isinstance(send_to, list)
1298b52a31eSHanoh Haim    assert isinstance(txt_attachments, list)
1308b52a31eSHanoh Haim    assert isinstance(images, list)
1318b52a31eSHanoh Haim
1328b52a31eSHanoh Haim    # create a multi part message
1338b52a31eSHanoh Haim    msg = MIMEMultipart()
1348b52a31eSHanoh Haim    msg['From'] = send_from
1358b52a31eSHanoh Haim    msg['To'] = COMMASPACE.join(send_to)
1368b52a31eSHanoh Haim    msg['Date'] = formatdate(localtime=True)
1378b52a31eSHanoh Haim    msg['Subject'] = subject
1388b52a31eSHanoh Haim    msg['Cc'] = "imarom@cisco.com"
1398b52a31eSHanoh Haim
1408b52a31eSHanoh Haim    # add all images to the text as embbeded images
1418b52a31eSHanoh Haim    for image in images:
1428b52a31eSHanoh Haim        html_text += '<br><img src="cid:{0}"><br>'.format(image)
1438b52a31eSHanoh Haim        fp = open(image, 'rb')
1448b52a31eSHanoh Haim        image_object = MIMEImage(fp.read())
1458b52a31eSHanoh Haim        fp.close()
1468b52a31eSHanoh Haim        image_object.add_header('Content-ID', image)
1478b52a31eSHanoh Haim        msg.attach(image_object)
1488b52a31eSHanoh Haim
1498b52a31eSHanoh Haim    # attach the main report as embedded HTML
1508b52a31eSHanoh Haim    msg.attach( MIMEText(html_text, 'html') )
1518b52a31eSHanoh Haim
1528b52a31eSHanoh Haim    # attach regualr txt files
1538b52a31eSHanoh Haim    for f in txt_attachments:
1548b52a31eSHanoh Haim        part = MIMEBase('application', "octet-stream")
1558b52a31eSHanoh Haim        part.set_payload( open(f,"rb").read() )
1568b52a31eSHanoh Haim        Encoders.encode_base64(part)
1578b52a31eSHanoh Haim        part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
1588b52a31eSHanoh Haim        msg.attach(part)
1598b52a31eSHanoh Haim
1608b52a31eSHanoh Haim    smtp = smtplib.SMTP(server)
1618b52a31eSHanoh Haim    smtp.sendmail(send_from, send_to, msg.as_string())
1628b52a31eSHanoh Haim    smtp.close()
1638b52a31eSHanoh Haim
1648b52a31eSHanoh Haim# convert HTML to image - returning a image file as a string
1658b52a31eSHanoh Haimdef html2image (html_filename, image_filename):
1668b52a31eSHanoh Haim    cmd = "./phantom/phantomjs ./phantom/rasterize.js {0} {1}".format(html_filename, image_filename)
1678b52a31eSHanoh Haim    subprocess.call(cmd, shell=True)
1688b52a31eSHanoh Haim
1698b52a31eSHanoh Haim    assert os.path.exists(image_filename)
1708b52a31eSHanoh Haim
1718b52a31eSHanoh Haim    return (image_filename)
1728b52a31eSHanoh Haim
1738b52a31eSHanoh Haim# convert results of run to a string
1748b52a31eSHanoh Haimdef run_results_to_str (results, cond_type):
1758b52a31eSHanoh Haim    output = ""
1768b52a31eSHanoh Haim
1778b52a31eSHanoh Haim    output +=  "M:                  {0:<12.6f}\n".format(results['m'])
1788b52a31eSHanoh Haim    output +=  "BW:                 {0:<12,.2f} [Mbps]\n".format(results['tx'])
1798b52a31eSHanoh Haim    output +=  "PPS:                {0:<12,} [pkts]\n".format(int(results['total-pps']))
1808b52a31eSHanoh Haim    output +=  "CPU:                {0:.4f} %\n".format(results['cpu_util'])
1818b52a31eSHanoh Haim    output +=  "Maximum Latency:    {0:<12,} [usec]\n".format(int(results['maximum-latency']))
1828b52a31eSHanoh Haim    output +=  "Average Latency:    {0:<12,} [usec]\n".format(int(results['average-latency']))
1838b52a31eSHanoh Haim    output +=  "Pkt Drop:           {0:<12,} [pkts]\n".format(int(results['total-pkt-drop']))
1848b52a31eSHanoh Haim    output +=  "Condition:          {0:<12} ({1})\n".format("Passed" if check_condition(cond_type, results) else "Failed", cond_type_to_str(cond_type))
1858b52a31eSHanoh Haim
1868b52a31eSHanoh Haim    return (output)
1878b52a31eSHanoh Haim
1888b52a31eSHanoh Haim############################# classes #################################
1898b52a31eSHanoh Haimclass ErrorHandler(object):
1908b52a31eSHanoh Haim    def __init__ (self, exception, traceback):
1918b52a31eSHanoh Haim
1928b52a31eSHanoh Haim        if isinstance(exception, TrexRunException):
1938b52a31eSHanoh Haim            logger.log("\n*** Script Terminated Due To Trex Failure")
1948b52a31eSHanoh Haim            logger.log("\n********************** TRex Error - Report **************************\n")
1958b52a31eSHanoh Haim            logger.log(str(exception))
1968b52a31eSHanoh Haim            logger.flush()
1978b52a31eSHanoh Haim
1988b52a31eSHanoh Haim        elif isinstance(exception, IOError):
1998b52a31eSHanoh Haim            logger.log("\n*** Script Terminated Due To IO Error")
2008b52a31eSHanoh Haim            logger.log("\nEither Router address or the Trex config is bad or some file is missing - check traceback below")
2018b52a31eSHanoh Haim            logger.log("\n********************** IO Error - Report **************************\n")
2028b52a31eSHanoh Haim            logger.log(str(exception))
2038b52a31eSHanoh Haim            logger.log(str(traceback))
2048b52a31eSHanoh Haim            logger.flush()
2058b52a31eSHanoh Haim
2068b52a31eSHanoh Haim
2078b52a31eSHanoh Haim        else:
2088b52a31eSHanoh Haim            logger.log("\n*** Script Terminated Due To Fatal Error")
2098b52a31eSHanoh Haim            logger.log("\n********************** Internal Error - Report **************************\n")
2108b52a31eSHanoh Haim            logger.log(str(exception) + "\n")
2118b52a31eSHanoh Haim            logger.log(str(traceback))
2128b52a31eSHanoh Haim            logger.flush()
2138b52a31eSHanoh Haim
2148b52a31eSHanoh Haim
2158b52a31eSHanoh Haim        # call the handler
2168b52a31eSHanoh Haim        g_kill_cause = "error"
2178b52a31eSHanoh Haim        os.kill(os.getpid(), signal.SIGUSR1)
2188b52a31eSHanoh Haim
2198b52a31eSHanoh Haim
2208b52a31eSHanoh Haim# simple HTML table
2218b52a31eSHanoh Haimclass HTMLTable:
2228b52a31eSHanoh Haim    def __init__ (self):
2238b52a31eSHanoh Haim        self.table_rows = []
2248b52a31eSHanoh Haim
2258b52a31eSHanoh Haim    def add_row (self, param, value):
2268b52a31eSHanoh Haim        self.table_rows.append([param, value])
2278b52a31eSHanoh Haim
2288b52a31eSHanoh Haim    def generate_table(self):
2298b52a31eSHanoh Haim        txt = '<table class="myWideTable" style="width:50%">'
2308b52a31eSHanoh Haim        txt += "<tr><th>Parameter</th><th>Results</th></tr>"
2318b52a31eSHanoh Haim
2328b52a31eSHanoh Haim        for row in self.table_rows:
2338b52a31eSHanoh Haim            txt += "<tr><td>{0}</td><td>{1}</td></tr>".format(row[0], row[1])
2348b52a31eSHanoh Haim
2358b52a31eSHanoh Haim        txt += "</table>"
2368b52a31eSHanoh Haim
2378b52a31eSHanoh Haim        return txt
2388b52a31eSHanoh Haim
2398b52a31eSHanoh Haim# process results and dispatch it
2408b52a31eSHanoh Haimclass JobReporter:
2418b52a31eSHanoh Haim    def __init__ (self, job_summary):
2428b52a31eSHanoh Haim        self.job_summary = job_summary
2438b52a31eSHanoh Haim        pass
2448b52a31eSHanoh Haim
2458b52a31eSHanoh Haim    def __plot_results_to_str (self, plot_results):
2468b52a31eSHanoh Haim        output = "\nPlotted Points: \n\n"
2478b52a31eSHanoh Haim        for p in plot_results:
2488b52a31eSHanoh Haim            output += "BW  : {0:8.2f},  ".format(p['tx'])
2498b52a31eSHanoh Haim            output += "PPS : {0:8,}  ".format(int(p['total-pps']))
2508b52a31eSHanoh Haim            output += "CPU : {0:8.2f} %,  ".format(p['cpu_util'])
2518b52a31eSHanoh Haim            output += "Max Latency : {0:10,},  ".format(int(p['maximum-latency']))
2528b52a31eSHanoh Haim            output += "Avg Latency : {0:10,},  ".format(int(p['average-latency']))
2538b52a31eSHanoh Haim            output += "Pkt Drop    : {0:12,},  \n".format(int(p['total-pkt-drop']))
2548b52a31eSHanoh Haim
2558b52a31eSHanoh Haim        return (output + "\n")
2568b52a31eSHanoh Haim
2578b52a31eSHanoh Haim    def __summary_to_string (self):
2588b52a31eSHanoh Haim        output = ""
2598b52a31eSHanoh Haim
2608b52a31eSHanoh Haim        output += "\n-== Job Completed Successfully ==-\n\n"
2618b52a31eSHanoh Haim        output += "Job Report:\n\n"
2628b52a31eSHanoh Haim        output +=  "Job Name:          {0}\n".format(self.job_summary['job_name'])
2638b52a31eSHanoh Haim        output +=  "YAML file:         {0}\n".format(self.job_summary['yaml'])
2648b52a31eSHanoh Haim        output +=  "Job Type:          {0}\n".format(self.job_summary['job_type_str'])
2658b52a31eSHanoh Haim        output +=  "Condition:         {0}\n".format(self.job_summary['cond_name'])
2668b52a31eSHanoh Haim        output +=  "Job Dir:           {0}\n".format(self.job_summary['job_dir'])
2678b52a31eSHanoh Haim        output +=  "Job Log:           {0}\n".format(self.job_summary['log_filename'])
2688b52a31eSHanoh Haim        output +=  "Email Report:      {0}\n".format(self.job_summary['email'])
2698b52a31eSHanoh Haim        output +=  "Job Total Time:    {0}\n\n".format(self.job_summary['total_run_time'])
2708b52a31eSHanoh Haim
2718b52a31eSHanoh Haim        if (self.job_summary.get('find_results') != None):
2728b52a31eSHanoh Haim            find_results = self.job_summary['find_results']
2738b52a31eSHanoh Haim            output += ("Maximum BW Point Details:\n\n")
2748b52a31eSHanoh Haim            output += run_results_to_str(find_results, self.job_summary['cond_type'])
2758b52a31eSHanoh Haim
2768b52a31eSHanoh Haim        if (self.job_summary.get('plot_results') != None):
2778b52a31eSHanoh Haim            plot_results = self.job_summary['plot_results']
2788b52a31eSHanoh Haim            output += self.__plot_results_to_str(plot_results)
2798b52a31eSHanoh Haim
2808b52a31eSHanoh Haim        return output
2818b52a31eSHanoh Haim
2828b52a31eSHanoh Haim
2838b52a31eSHanoh Haim    # simple print to screen of the job summary
2848b52a31eSHanoh Haim    def print_summary (self):
2858b52a31eSHanoh Haim        summary = self.__summary_to_string()
2868b52a31eSHanoh Haim        logger.log(summary)
2878b52a31eSHanoh Haim
2888b52a31eSHanoh Haim    def __generate_graph_report (self, plot_results):
2898b52a31eSHanoh Haim        graph_data = str( [ [x['tx'], x['cpu_util']/100, x['maximum-latency'], x['average-latency']] for x in plot_results ] )
2908b52a31eSHanoh Haim        table_data = str( [ [x['tx'], x['total-pps'], x['cpu_util']/100, x['norm_cpu'], x['maximum-latency'], x['average-latency'], x['total-pkt-drop']] for x in plot_results ] )
2918b52a31eSHanoh Haim
2928b52a31eSHanoh Haim        with open ("graph_template.html", "r") as myfile:
2938b52a31eSHanoh Haim            data = myfile.read()
2948b52a31eSHanoh Haim            data = data.replace("!@#$template_fill_head!@#$", self.job_summary['yaml'])
2958b52a31eSHanoh Haim            data = data.replace("!@#$template_fill_graph!@#$", graph_data[1:(len(graph_data) - 1)])
2968b52a31eSHanoh Haim            data = data.replace("!@#$template_fill_table!@#$", table_data[1:(len(table_data) - 1)])
2978b52a31eSHanoh Haim
2988b52a31eSHanoh Haim        # generate HTML report
2998b52a31eSHanoh Haim        graph_filename = self.job_summary['graph_filename']
3008b52a31eSHanoh Haim        text_file = open(graph_filename, "w")
3018b52a31eSHanoh Haim        text_file.write(str(data))
3028b52a31eSHanoh Haim        text_file.close()
3038b52a31eSHanoh Haim
3048b52a31eSHanoh Haim        return graph_filename
3058b52a31eSHanoh Haim
3068b52a31eSHanoh Haim    def __generate_body_report (self):
3078b52a31eSHanoh Haim        job_setup_table = HTMLTable()
3088b52a31eSHanoh Haim
3098b52a31eSHanoh Haim        job_setup_table.add_row("User Name", self.job_summary['user'])
3108b52a31eSHanoh Haim        job_setup_table.add_row("Job Name", self.job_summary['job_name'])
3118b52a31eSHanoh Haim        job_setup_table.add_row("Job Type", self.job_summary['job_type_str'])
3128b52a31eSHanoh Haim        job_setup_table.add_row("Test Condition", self.job_summary['cond_name'])
3138b52a31eSHanoh Haim        job_setup_table.add_row("YAML File", self.job_summary['yaml'])
3148b52a31eSHanoh Haim        job_setup_table.add_row("Job Total Time", "{0}".format(self.job_summary['total_run_time']))
3158b52a31eSHanoh Haim
3168b52a31eSHanoh Haim        job_summary_table = HTMLTable()
3178b52a31eSHanoh Haim
3188b52a31eSHanoh Haim        find_results = self.job_summary['find_results']
3198b52a31eSHanoh Haim
3208b52a31eSHanoh Haim        if find_results != None:
3218b52a31eSHanoh Haim            job_summary_table.add_row("Maximum Bandwidth", "{0:,.2f} [Mbps]".format(find_results['tx']))
3228b52a31eSHanoh Haim            job_summary_table.add_row("Maximum PPS", "{0:,} [pkts]".format(int(find_results['total-pps'])))
3238b52a31eSHanoh Haim            job_summary_table.add_row("CPU Util.", "{0:.2f}%".format(find_results['cpu_util']))
3248b52a31eSHanoh Haim            job_summary_table.add_row("Maximum Latency", "{0:,} [usec]".format(int(find_results['maximum-latency'])))
3258b52a31eSHanoh Haim            job_summary_table.add_row("Average Latency", "{0:,} [usec]".format(int(find_results['average-latency'])))
3268b52a31eSHanoh Haim            job_summary_table.add_row("Total Pkt Drop", "{0:,}  [pkts]".format(int(find_results['total-pkt-drop'])))
3278b52a31eSHanoh Haim
3288b52a31eSHanoh Haim        with open ("report_template.html", "r") as myfile:
3298b52a31eSHanoh Haim            data = myfile.read()
3308b52a31eSHanoh Haim            data = data.replace("!@#$template_fill_job_setup_table!@#$", job_setup_table.generate_table())
3318b52a31eSHanoh Haim            data = data.replace("!@#$template_fill_job_summary_table!@#$", job_summary_table.generate_table())
3328b52a31eSHanoh Haim
3338b52a31eSHanoh Haim        return data
3348b52a31eSHanoh Haim
3358b52a31eSHanoh Haim    # create an email report and send to the user
3368b52a31eSHanoh Haim    def send_email_report (self):
3378b52a31eSHanoh Haim        images = []
3388b52a31eSHanoh Haim
3398b52a31eSHanoh Haim        logger.log("\nCreating E-Mail Report...\n")
3408b52a31eSHanoh Haim
3418b52a31eSHanoh Haim        # generate main report
3428b52a31eSHanoh Haim        report_str = self.__generate_body_report()
3438b52a31eSHanoh Haim
3448b52a31eSHanoh Haim        # generate graph report (if exists)
3458b52a31eSHanoh Haim        plot_results = self.job_summary['plot_results']
3468b52a31eSHanoh Haim        if plot_results:
3478b52a31eSHanoh Haim            logger.log("Generating Plot Results HTML ...\n")
3488b52a31eSHanoh Haim            graph_filename  = self.__generate_graph_report(plot_results)
3498b52a31eSHanoh Haim            logger.log("Converting HTML to image ...\n")
3508b52a31eSHanoh Haim            images.append(html2image(graph_filename, graph_filename + ".png"))
3518b52a31eSHanoh Haim
3528b52a31eSHanoh Haim        else:
3538b52a31eSHanoh Haim            graph_filename = None
3548b52a31eSHanoh Haim
3558b52a31eSHanoh Haim        # create email
3568b52a31eSHanoh Haim        from_addr = 'TrexReporter@cisco.com'
3578b52a31eSHanoh Haim        to_addr = []
3588b52a31eSHanoh Haim        to_addr.append(self.job_summary['email'])
3598b52a31eSHanoh Haim        to_addr.append('imarom@cisco.com')
3608b52a31eSHanoh Haim
3618b52a31eSHanoh Haim        attachments = []
3628b52a31eSHanoh Haim        attachments.append(self.job_summary['log_filename'])
3638b52a31eSHanoh Haim        logger.log("Attaching log {0}...".format(self.job_summary['log_filename']))
3648b52a31eSHanoh Haim
3658b52a31eSHanoh Haim        if graph_filename:
3668b52a31eSHanoh Haim            attachments.append(graph_filename)
3678b52a31eSHanoh Haim            logger.log("Attaching plotting report {0}...".format(graph_filename))
3688b52a31eSHanoh Haim
3698b52a31eSHanoh Haim        logger.flush()
3708b52a31eSHanoh Haim
3718b52a31eSHanoh Haim        send_mail(from_addr, to_addr, "TRex Performance Report", report_str, attachments, images)
3728b52a31eSHanoh Haim        logger.log("\nE-mail sent successfully to: " + self.job_summary['email'])
3738b52a31eSHanoh Haim
3748b52a31eSHanoh Haim# dummy logger in case logger creation failed
3758b52a31eSHanoh Haimclass DummyLogger(object):
3768b52a31eSHanoh Haim    def __init__(self):
3778b52a31eSHanoh Haim        pass
3788b52a31eSHanoh Haim
3798b52a31eSHanoh Haim    def log(self, text, force = False, newline = True):
3808b52a31eSHanoh Haim        text_out = (text + "\n") if newline else text
3818b52a31eSHanoh Haim        sys.stdout.write(text_out)
3828b52a31eSHanoh Haim
3838b52a31eSHanoh Haim    def console(self, text, force = False, newline = True):
3848b52a31eSHanoh Haim        self.log(text, force, newline)
3858b52a31eSHanoh Haim
3868b52a31eSHanoh Haim    def flush (self):
3878b52a31eSHanoh Haim        pass
3888b52a31eSHanoh Haim
3898b52a31eSHanoh Haim# logger object
3908b52a31eSHanoh Haimclass MyLogger(object):
3918b52a31eSHanoh Haim
3928b52a31eSHanoh Haim    def __init__(self, log_filename):
3938b52a31eSHanoh Haim        # Store the original stdout and stderr
3948b52a31eSHanoh Haim        sys.stdout.flush()
3958b52a31eSHanoh Haim        sys.stderr.flush()
3968b52a31eSHanoh Haim
3978b52a31eSHanoh Haim        self.stdout_fd = os.dup(sys.stdout.fileno())
3988b52a31eSHanoh Haim        self.devnull = os.open('/dev/null', os.O_WRONLY)
3998b52a31eSHanoh Haim        self.log_file = open(log_filename, 'w')
4008b52a31eSHanoh Haim        self.silenced = False
4018b52a31eSHanoh Haim        self.pending_log_file_prints = 0
4028b52a31eSHanoh Haim        self.active = True
4038b52a31eSHanoh Haim
4048b52a31eSHanoh Haim    def shutdown (self):
4058b52a31eSHanoh Haim        self.active = False
4068b52a31eSHanoh Haim
4078b52a31eSHanoh Haim    def reactive (self):
4088b52a31eSHanoh Haim        self.active = True
4098b52a31eSHanoh Haim
4108b52a31eSHanoh Haim    # silence all prints from stdout
4118b52a31eSHanoh Haim    def silence(self):
4128b52a31eSHanoh Haim        os.dup2(self.devnull, sys.stdout.fileno())
4138b52a31eSHanoh Haim        self.silenced = True
4148b52a31eSHanoh Haim
4158b52a31eSHanoh Haim    # restore stdout status
4168b52a31eSHanoh Haim    def restore(self):
4178b52a31eSHanoh Haim        sys.stdout.flush()
4188b52a31eSHanoh Haim        sys.stderr.flush()
4198b52a31eSHanoh Haim        # Restore normal stdout
4208b52a31eSHanoh Haim        os.dup2(self.stdout_fd, sys.stdout.fileno())
4218b52a31eSHanoh Haim        self.silenced = False
4228b52a31eSHanoh Haim
4238b52a31eSHanoh Haim    #print a message to the log (both stdout / log file)
4248b52a31eSHanoh Haim    def log(self, text, force = False, newline = True):
4258b52a31eSHanoh Haim        if not self.active:
4268b52a31eSHanoh Haim            return
4278b52a31eSHanoh Haim
4288b52a31eSHanoh Haim        self.log_file.write((text + "\n") if newline else text)
4298b52a31eSHanoh Haim        self.pending_log_file_prints += 1
4308b52a31eSHanoh Haim
4318b52a31eSHanoh Haim        if (self.pending_log_file_prints >= 10):
4328b52a31eSHanoh Haim             self.log_file.flush()
4338b52a31eSHanoh Haim             self.pending_log_file_prints = 0
4348b52a31eSHanoh Haim
4358b52a31eSHanoh Haim        self.console(text, force, newline)
4368b52a31eSHanoh Haim
4378b52a31eSHanoh Haim    # print a message to the console alone
4388b52a31eSHanoh Haim    def console(self, text, force = False, newline = True):
4398b52a31eSHanoh Haim        if not self.active:
4408b52a31eSHanoh Haim            return
4418b52a31eSHanoh Haim
4428b52a31eSHanoh Haim        _text = (text + "\n") if newline else text
4438b52a31eSHanoh Haim
4448b52a31eSHanoh Haim        # if we are silenced and not forced - go home
4458b52a31eSHanoh Haim        if self.silenced and not force:
4468b52a31eSHanoh Haim            return
4478b52a31eSHanoh Haim
4488b52a31eSHanoh Haim        if self.silenced:
4498b52a31eSHanoh Haim            os.write(self.stdout_fd, _text)
4508b52a31eSHanoh Haim        else:
4518b52a31eSHanoh Haim            sys.stdout.write(_text)
4528b52a31eSHanoh Haim
4538b52a31eSHanoh Haim        sys.stdout.flush()
4548b52a31eSHanoh Haim
4558b52a31eSHanoh Haim    # flush
4568b52a31eSHanoh Haim    def flush(self):
4578b52a31eSHanoh Haim        sys.stdout.flush()
4588b52a31eSHanoh Haim        self.log_file.flush()
4598b52a31eSHanoh Haim
4608b52a31eSHanoh Haim    def __del__(self):
4618b52a31eSHanoh Haim        os.close(self.devnull)
4628b52a31eSHanoh Haim        if self.log_file:
4638b52a31eSHanoh Haim            self.log_file.flush()
4648b52a31eSHanoh Haim            self.log_file.close()
4658b52a31eSHanoh Haim
4668b52a31eSHanoh Haim
4678b52a31eSHanoh Haim# simple progress bar
4688b52a31eSHanoh Haimclass ProgressBar(threading.Thread):
4698b52a31eSHanoh Haim    def __init__(self, time_sec, router):
4708b52a31eSHanoh Haim        super(ProgressBar, self).__init__()
4718b52a31eSHanoh Haim        self.active = True
4728b52a31eSHanoh Haim        self.time_sec = time_sec + 15
4738b52a31eSHanoh Haim        self.router = router
4748b52a31eSHanoh Haim
4758b52a31eSHanoh Haim    def run (self):
4768b52a31eSHanoh Haim        global g_stop
4778b52a31eSHanoh Haim
4788b52a31eSHanoh Haim        col = 40
4798b52a31eSHanoh Haim        delta_for_sec = float(col) / self.time_sec
4808b52a31eSHanoh Haim
4818b52a31eSHanoh Haim        accu = 0.0
4828b52a31eSHanoh Haim
4838b52a31eSHanoh Haim        for i in range(self.time_sec):
4848b52a31eSHanoh Haim            if (self.active == False):
4858b52a31eSHanoh Haim                # print 100% - done
4868b52a31eSHanoh Haim                bar = "\r[" + ('#' * col) + "] {0:.2f} %".format(100)
4878b52a31eSHanoh Haim                logger.console(bar, force = True, newline = False)
4888b52a31eSHanoh Haim                break
4898b52a31eSHanoh Haim
4908b52a31eSHanoh Haim            if (g_stop == True):
4918b52a31eSHanoh Haim                break
4928b52a31eSHanoh Haim
4938b52a31eSHanoh Haim            sleep(1)
4948b52a31eSHanoh Haim            accu += delta_for_sec
4958b52a31eSHanoh Haim            bar = "\r[" + ('#' * int(accu)) + (' ' * (col - int(accu))) + "] {0:.2f} %".format( (accu/col) * 100 )
4968b52a31eSHanoh Haim            bar += " / Router CPU: {0:.2f} %".format(self.router.get_last_cpu_util())
4978b52a31eSHanoh Haim            logger.console(bar, force = True, newline = False)
4988b52a31eSHanoh Haim
4998b52a31eSHanoh Haim        logger.console("\r\n", force = True, newline = False)
5008b52a31eSHanoh Haim        logger.flush()
5018b52a31eSHanoh Haim
5028b52a31eSHanoh Haim    def stop (self):
5038b52a31eSHanoh Haim        self.active = False
5048b52a31eSHanoh Haim        self.join()
5058b52a31eSHanoh Haim
5068b52a31eSHanoh Haim# global vars
5078b52a31eSHanoh Haim
5088b52a31eSHanoh Haimg_stop = False
5098b52a31eSHanoh Haimlogger = DummyLogger()
5108b52a31eSHanoh Haim
5118b52a31eSHanoh Haim# cleanup list is a list of callables to be run when cntrl+c is caught
5128b52a31eSHanoh Haimcleanup_list = []
5138b52a31eSHanoh Haim
5148b52a31eSHanoh Haim################ threads ########################
5158b52a31eSHanoh Haim
5168b52a31eSHanoh Haim# sampler
5178b52a31eSHanoh Haimclass Sample_Thread (threading.Thread):
5188b52a31eSHanoh Haim    def __init__(self, threadID, router):
5198b52a31eSHanoh Haim
5208b52a31eSHanoh Haim        threading.Thread.__init__(self)
5218b52a31eSHanoh Haim        self.threadID = threadID
5228b52a31eSHanoh Haim        self.router = router
5238b52a31eSHanoh Haim        self.stop = False
5248b52a31eSHanoh Haim
5258b52a31eSHanoh Haim    def run(self):
5268b52a31eSHanoh Haim        self.router.clear_sampling_stats()
5278b52a31eSHanoh Haim
5288b52a31eSHanoh Haim        try:
5298b52a31eSHanoh Haim            while (self.stop==False) and (g_stop==False):
5308b52a31eSHanoh Haim                self.router.sample_stats()
5318b52a31eSHanoh Haim                time.sleep(1);
5328b52a31eSHanoh Haim        except Exception as e:
5338b52a31eSHanoh Haim            ErrorHandler(e, traceback.format_exc())
5348b52a31eSHanoh Haim
5358b52a31eSHanoh Haim    def do_stop(self):
5368b52a31eSHanoh Haim        self.stop = True
5378b52a31eSHanoh Haim
5388b52a31eSHanoh Haim
5398b52a31eSHanoh Haimdef general_cleanup_on_error ():
5408b52a31eSHanoh Haim    global g_stop
5418b52a31eSHanoh Haim    global cleanup_list
5428b52a31eSHanoh Haim
5438b52a31eSHanoh Haim    # mark all the threads to finish
5448b52a31eSHanoh Haim    g_stop = True;
5458b52a31eSHanoh Haim
5468b52a31eSHanoh Haim    # shutdown and flush the logger
5478b52a31eSHanoh Haim    logger.shutdown()
5488b52a31eSHanoh Haim    if logger:
5498b52a31eSHanoh Haim        logger.flush()
5508b52a31eSHanoh Haim
5518b52a31eSHanoh Haim    # execute the registered callables
5528b52a31eSHanoh Haim    for c in cleanup_list:
5538b52a31eSHanoh Haim        c()
5548b52a31eSHanoh Haim
5558b52a31eSHanoh Haim    # dummy wait for threads to finish (TODO: make this more smart)
5568b52a31eSHanoh Haim    time.sleep(2)
5578b52a31eSHanoh Haim    exit(-1)
5588b52a31eSHanoh Haim
5598b52a31eSHanoh Haim# just a dummy for preventing chain calls
5608b52a31eSHanoh Haimdef signal_handler_dummy (sig_id, frame):
5618b52a31eSHanoh Haim    pass
5628b52a31eSHanoh Haim
5638b52a31eSHanoh Haimdef error_signal_handler (sig_id, frame):
5648b52a31eSHanoh Haim    # make sure no chain of calls
5658b52a31eSHanoh Haim    signal.signal(signal.SIGUSR1, signal_handler_dummy)
5668b52a31eSHanoh Haim    signal.signal(signal.SIGINT, signal_handler_dummy)
5678b52a31eSHanoh Haim
5688b52a31eSHanoh Haim    general_cleanup_on_error()
5698b52a31eSHanoh Haim
5708b52a31eSHanoh Haimdef int_signal_handler(sig_id, frame):
5718b52a31eSHanoh Haim    # make sure no chain of calls
5728b52a31eSHanoh Haim    signal.signal(signal.SIGINT, signal_handler_dummy)
5738b52a31eSHanoh Haim    signal.signal(signal.SIGUSR1, signal_handler_dummy)
5748b52a31eSHanoh Haim
5758b52a31eSHanoh Haim    logger.log("\n\nCaught Cntrl+C... Cleaning up!\n\n")
5768b52a31eSHanoh Haim
5778b52a31eSHanoh Haim    general_cleanup_on_error()
5788b52a31eSHanoh Haim
5798b52a31eSHanoh Haim
5808b52a31eSHanoh Haim# Trex with sampling
5818b52a31eSHanoh Haimclass CTRexWithRouter:
5828b52a31eSHanoh Haim    def __init__(self, trex, trex_params):
5838b52a31eSHanoh Haim        self.trex = trex;
5848b52a31eSHanoh Haim        self.trex_params = trex_params
5858b52a31eSHanoh Haim
5868b52a31eSHanoh Haim        if self.trex_params['router_type'] == "ASR":
5878b52a31eSHanoh Haim            self.router = h_avc.ASR1k(self.trex_params['router_interface'], self.trex_params['router_port'], self.trex_params['router_password'])
5888b52a31eSHanoh Haim        elif self.trex_params['router_type'] == "ISR":
5898b52a31eSHanoh Haim            self.router = h_avc.ISR(self.trex_params['router_interface'], self.trex_params['router_port'], self.trex_params['router_password'])
5908b52a31eSHanoh Haim        else:
5918b52a31eSHanoh Haim            raise Exception("unknown router type in config file")
5928b52a31eSHanoh Haim
5938b52a31eSHanoh Haim        self.router.connect()
5948b52a31eSHanoh Haim
5958b52a31eSHanoh Haim    def get_router (self):
5968b52a31eSHanoh Haim        return self.router
5978b52a31eSHanoh Haim
5988b52a31eSHanoh Haim    def run(self, m, duration):
5998b52a31eSHanoh Haim
6008b52a31eSHanoh Haim        self.sample_thread = Sample_Thread(1, self.router)
6018b52a31eSHanoh Haim        self.sample_thread.start();
6028b52a31eSHanoh Haim
6038b52a31eSHanoh Haim        # launch trex
6048b52a31eSHanoh Haim        try:
6058b52a31eSHanoh Haim#           trex_res = self.trex.run(m, duration);
6068b52a31eSHanoh Haim            self.trex.start_trex(c = self.trex_params['trex_cores'],
6078b52a31eSHanoh Haim                m = m,
6088b52a31eSHanoh Haim                d = duration,
6098b52a31eSHanoh Haim                f = self.trex_params['trex_yaml_file'],
6108b52a31eSHanoh Haim                nc = True,
6118b52a31eSHanoh Haim                l = self.trex_params['trex_latency'],
6128b52a31eSHanoh Haim                limit_ports = self.trex_params['trex_limit_ports'])
6138b52a31eSHanoh Haim            self.trex.sample_to_run_finish(20)      # collect trex-sample every 20 seconds.
6148b52a31eSHanoh Haim        except Exception:
6158b52a31eSHanoh Haim            self.sample_thread.do_stop()  # signal to stop
6168b52a31eSHanoh Haim            self.sample_thread.join()     # wait for it to realy stop
6178b52a31eSHanoh Haim            raise
6188b52a31eSHanoh Haim
6198b52a31eSHanoh Haim        self.sample_thread.do_stop()  # signal to stop
6208b52a31eSHanoh Haim        self.sample_thread.join()     # wait for it to realy stop
6218b52a31eSHanoh Haim
6228b52a31eSHanoh Haim        self.res = self.trex.get_result_obj()
6238b52a31eSHanoh Haim
6248b52a31eSHanoh Haim        results = {}
6258b52a31eSHanoh Haim        results['status'] = True
6268b52a31eSHanoh Haim        results['trex_results'] = self.res
6278b52a31eSHanoh Haim        results['avc_results']  = self.router.get_stats()
6288b52a31eSHanoh Haim
6298b52a31eSHanoh Haim        return (results)
6308b52a31eSHanoh Haim        #return(trex_res.get_status() == STATUS_OK);
6318b52a31eSHanoh Haim
6328b52a31eSHanoh Haim# sanity checks to see run really went well
6338b52a31eSHanoh Haimdef sanity_test_run (trex_r, avc_r):
6348b52a31eSHanoh Haim    pass
6358b52a31eSHanoh Haim    #if (sum(avc_r['cpu_histo']) == 0):
6368b52a31eSHanoh Haim        #raise h_trex.TrexRunException("CPU utilization from router is zero, check connectivity")
6378b52a31eSHanoh Haim
6388b52a31eSHanoh Haimdef _trex_run (job_summary, m, duration):
6398b52a31eSHanoh Haim
6408b52a31eSHanoh Haim    trex_thread = job_summary['trex_thread']
6418b52a31eSHanoh Haim
6428b52a31eSHanoh Haim    p = ProgressBar(duration, trex_thread.get_router())
6438b52a31eSHanoh Haim    p.start()
6448b52a31eSHanoh Haim
6458b52a31eSHanoh Haim    try:
6468b52a31eSHanoh Haim        results = trex_thread.run(m, duration)
6479adfbadaSYaroslav Brustinov    except Exception as e:
6488b52a31eSHanoh Haim        p.stop()
6498b52a31eSHanoh Haim        raise
6508b52a31eSHanoh Haim
6518b52a31eSHanoh Haim    p.stop()
6528b52a31eSHanoh Haim
6538b52a31eSHanoh Haim    if (results == None):
6548b52a31eSHanoh Haim        raise Exception("Failed to run Trex")
6558b52a31eSHanoh Haim
6568b52a31eSHanoh Haim    # fetch values
6578b52a31eSHanoh Haim    trex_r = results['trex_results']
6588b52a31eSHanoh Haim    avc_r  = results['avc_results']
6598b52a31eSHanoh Haim
6608b52a31eSHanoh Haim    sanity_test_run(trex_r, avc_r)
6618b52a31eSHanoh Haim
6628b52a31eSHanoh Haim    res_dict = {}
6638b52a31eSHanoh Haim
6648b52a31eSHanoh Haim    res_dict['m']  = m
6658b52a31eSHanoh Haim    total_tx_bps = trex_r.get_last_value("trex-global.data.m_tx_bps")
6668b52a31eSHanoh Haim    res_dict['tx'] = total_tx_bps / (1000 * 1000)  # EVENTUALLY CONTAINS IN MBPS (EXTRACTED IN BPS)
6678b52a31eSHanoh Haim
6688b52a31eSHanoh Haim    res_dict['cpu_util'] = avc_r['cpu_util']
6698b52a31eSHanoh Haim
6708b52a31eSHanoh Haim    if int(res_dict['cpu_util']) == 0:
6718b52a31eSHanoh Haim        res_dict['norm_cpu']=1;
6728b52a31eSHanoh Haim    else:
6738b52a31eSHanoh Haim        res_dict['norm_cpu'] = (res_dict['tx'] / res_dict['cpu_util']) * 100
6748b52a31eSHanoh Haim
6758b52a31eSHanoh Haim    res_dict['maximum-latency']  = max ( trex_r.get_max_latency().values() ) #trex_r.res['maximum-latency']
6768b52a31eSHanoh Haim    res_dict['average-latency']  = trex_r.get_avg_latency()['all'] #trex_r.res['average-latency']
6778b52a31eSHanoh Haim
6788b52a31eSHanoh Haim    logger.log(cpu_histo_to_str(avc_r['cpu_histo']))
6798b52a31eSHanoh Haim
6808b52a31eSHanoh Haim    res_dict['total-pkt-drop']  = trex_r.get_total_drops()
6818b52a31eSHanoh Haim    res_dict['expected-bps']    = trex_r.get_expected_tx_rate()['m_tx_expected_bps']
6828b52a31eSHanoh Haim    res_dict['total-pps']       = get_median( trex_r.get_value_list("trex-global.data.m_tx_pps") )#trex_r.res['total-pps']
6838b52a31eSHanoh Haim    res_dict['m_total_pkt']     = trex_r.get_last_value("trex-global.data.m_total_tx_pkts")
6848b52a31eSHanoh Haim
6858b52a31eSHanoh Haim    res_dict['latency_condition'] = job_summary['trex_params']['trex_latency_condition']
6868b52a31eSHanoh Haim
6878b52a31eSHanoh Haim    return res_dict
6888b52a31eSHanoh Haim
6898b52a31eSHanoh Haimdef trex_run (job_summary, m, duration):
6908b52a31eSHanoh Haim    res = _trex_run (job_summary, m, duration)
6918b52a31eSHanoh Haim    return res
6928b52a31eSHanoh Haim
6938b52a31eSHanoh Haim
6948b52a31eSHanoh Haimdef m_to_mbps (job_summary, m):
6958b52a31eSHanoh Haim    return (m * job_summary['base_m_unit'])
6968b52a31eSHanoh Haim
6978b52a31eSHanoh Haim# find the correct range of M
6988b52a31eSHanoh Haimdef find_m_range (job_summary):
6998b52a31eSHanoh Haim
7008b52a31eSHanoh Haim    trex = job_summary['trex']
7018b52a31eSHanoh Haim    trex_config = job_summary['trex_params']
7028b52a31eSHanoh Haim
7038b52a31eSHanoh Haim    # if not provided - guess the correct range of bandwidth
7048b52a31eSHanoh Haim    if not job_summary['m_range']:
7058b52a31eSHanoh Haim        m_range = [0.0, 0.0]
7068b52a31eSHanoh Haim        # 1 Mbps -> 1 Gbps
7078b52a31eSHanoh Haim        LOW_TX = 1.0 * 1000 * 1000
7088b52a31eSHanoh Haim        MAX_TX = 1.0 * 1000 * 1000 * 1000
7098b52a31eSHanoh Haim
7108b52a31eSHanoh Haim        # for 10g go to 10g
7118b52a31eSHanoh Haim        if trex_config['trex_machine_type'] == "10G":
7128b52a31eSHanoh Haim            MAX_TX *= 10
7138b52a31eSHanoh Haim
7148b52a31eSHanoh Haim        # dual injection can potentially reach X2 speed
7158b52a31eSHanoh Haim        if trex_config['trex_is_dual'] == True:
7168b52a31eSHanoh Haim            MAX_TX *= 2
7178b52a31eSHanoh Haim
7188b52a31eSHanoh Haim    else:
7198b52a31eSHanoh Haim        m_range = job_summary['m_range']
7208b52a31eSHanoh Haim        LOW_TX = m_range[0] * 1000 * 1000
7218b52a31eSHanoh Haim        MAX_TX = m_range[1] * 1000 * 1000
7228b52a31eSHanoh Haim
7238b52a31eSHanoh Haim
7248b52a31eSHanoh Haim    logger.log("\nSystem Settings - Min: {0:,} Mbps / Max: {1:,} Mbps".format(LOW_TX / (1000 * 1000), MAX_TX / (1000 * 1000)))
7258b52a31eSHanoh Haim    logger.log("\nTrying to get system minimum M and maximum M...")
7268b52a31eSHanoh Haim
7278b52a31eSHanoh Haim    res_dict = trex_run(job_summary, 1, 30)
7288b52a31eSHanoh Haim
7298b52a31eSHanoh Haim    # figure out low / high M
7308b52a31eSHanoh Haim    m_range[0] = (LOW_TX / res_dict['expected-bps']) * 1
7318b52a31eSHanoh Haim    m_range[1] = (MAX_TX / res_dict['expected-bps']) * 1
7328b52a31eSHanoh Haim
7338b52a31eSHanoh Haim
7348b52a31eSHanoh Haim    # return both the m_range and the base m unit for future calculation
7358b52a31eSHanoh Haim    results = {}
7368b52a31eSHanoh Haim    results['m_range'] = m_range
7378b52a31eSHanoh Haim    results['base_m_unit'] = res_dict['expected-bps'] /(1000 * 1000)
7388b52a31eSHanoh Haim
7398b52a31eSHanoh Haim    return (results)
7408b52a31eSHanoh Haim
7418b52a31eSHanoh Haim# calculate points between m_range[0] and m_range[1]
7428b52a31eSHanoh Haimdef calculate_plot_points (job_summary, m_range, plot_count):
7438b52a31eSHanoh Haim
7448b52a31eSHanoh Haim    cond_type = job_summary['cond_type']
7458b52a31eSHanoh Haim    delta_m = (m_range[1] - m_range[0]) / plot_count
7468b52a31eSHanoh Haim
7478b52a31eSHanoh Haim    m_current = m_range[0]
7488b52a31eSHanoh Haim    m_end = m_range[1]
7498b52a31eSHanoh Haim
7508b52a31eSHanoh Haim    logger.log("\nStarting Plot Graph Task ...\n")
7518b52a31eSHanoh Haim    logger.log("Plotting Range Is From: {0:.2f} [Mbps] To: {1:.2f} [Mbps] Over {2} Points".format(m_to_mbps(job_summary, m_range[0]),
7528b52a31eSHanoh Haim                                                                                                  m_to_mbps(job_summary, m_range[1]),
7538b52a31eSHanoh Haim                                                                                                  plot_count))
7548b52a31eSHanoh Haim    logger.log("Delta Between Points is {0:.2f} [Mbps]".format(m_to_mbps(job_summary, delta_m)))
7558b52a31eSHanoh Haim    plot_points = []
7568b52a31eSHanoh Haim
7578b52a31eSHanoh Haim    duration = 180
7588b52a31eSHanoh Haim
7598b52a31eSHanoh Haim    iter = 1
7608b52a31eSHanoh Haim
7618b52a31eSHanoh Haim    trex = job_summary['trex']
7628b52a31eSHanoh Haim    while (iter <= plot_count):
7638b52a31eSHanoh Haim        logger.log("\nPlotting Point [{0}/{1}]:\n".format(iter, plot_count))
7648b52a31eSHanoh Haim        logger.log("Estimated BW   ~= {0:,.2f} [Mbps]\n".format(m_to_mbps(job_summary, m_current)))
7658b52a31eSHanoh Haim        logger.log("M               = {0:.6f}".format(m_current))
7668b52a31eSHanoh Haim        logger.log("Duration        = {0} seconds\n".format(duration))
7678b52a31eSHanoh Haim
7688b52a31eSHanoh Haim        res_dict = trex_run(job_summary, m_current, duration)
7698b52a31eSHanoh Haim        print_trex_results(res_dict, cond_type)
7708b52a31eSHanoh Haim
7718b52a31eSHanoh Haim        plot_points.append(dict(res_dict))
7728b52a31eSHanoh Haim
7738b52a31eSHanoh Haim        m_current += delta_m
7748b52a31eSHanoh Haim        iter = iter + 1
7758b52a31eSHanoh Haim
7768b52a31eSHanoh Haim        # last point - make sure its the maximum point
7778b52a31eSHanoh Haim        if (iter == plot_count):
7788b52a31eSHanoh Haim            m_current = m_range[1]
7798b52a31eSHanoh Haim
7808b52a31eSHanoh Haim        #print "waiting for system to stabilize ..."
7818b52a31eSHanoh Haim        #time.sleep(30);
7828b52a31eSHanoh Haim
7838b52a31eSHanoh Haim    return plot_points
7848b52a31eSHanoh Haim
7858b52a31eSHanoh Haim
7868b52a31eSHanoh Haimdef cond_type_to_str (cond_type):
7878b52a31eSHanoh Haim    return "Max Latency" if cond_type=='latency' else "Pkt Drop"
7888b52a31eSHanoh Haim
7898b52a31eSHanoh Haim# success condition (latency or drop)
7908b52a31eSHanoh Haimdef check_condition (cond_type, res_dict):
7918b52a31eSHanoh Haim    if cond_type == 'latency':
7928b52a31eSHanoh Haim        if res_dict['maximum-latency'] < res_dict['latency_condition']:
7938b52a31eSHanoh Haim            return True
7948b52a31eSHanoh Haim        else:
7958b52a31eSHanoh Haim            return False
7968b52a31eSHanoh Haim
7978b52a31eSHanoh Haim    # drop condition is a bit more complex - it should create high latency in addition to 0.2% drop
7988b52a31eSHanoh Haim    elif cond_type == 'drop':
7998b52a31eSHanoh Haim        if (res_dict['maximum-latency'] > (res_dict['latency_condition']+2000) ) and (res_dict['total-pkt-drop'] > (0.002 * res_dict['m_total_pkt'])):
8008b52a31eSHanoh Haim            return False
8018b52a31eSHanoh Haim        else:
8028b52a31eSHanoh Haim            return True
8038b52a31eSHanoh Haim
8048b52a31eSHanoh Haim    assert(0)
8058b52a31eSHanoh Haim
8068b52a31eSHanoh Haimdef print_trex_results (res_dict, cond_type):
8078b52a31eSHanoh Haim    logger.log("\nRun Results:\n")
8088b52a31eSHanoh Haim    output = run_results_to_str(res_dict, cond_type)
8098b52a31eSHanoh Haim    logger.log(output)
8108b52a31eSHanoh Haim
8118b52a31eSHanoh Haim
8128b52a31eSHanoh Haim######################## describe a find job ########################
8138b52a31eSHanoh Haimclass FindJob:
8148b52a31eSHanoh Haim    # init a job object with min / max
8158b52a31eSHanoh Haim    def __init__ (self, min, max, job_summary):
8168b52a31eSHanoh Haim        self.min = float(min)
8178b52a31eSHanoh Haim        self.max = float(max)
8188b52a31eSHanoh Haim        self.job_summary = job_summary
8198b52a31eSHanoh Haim        self.cond_type = job_summary['cond_type']
8208b52a31eSHanoh Haim        self.success_points = []
8218b52a31eSHanoh Haim        self.iter_num = 1
8228b52a31eSHanoh Haim        self.found = False
8238b52a31eSHanoh Haim        self.iter_duration = 300
8248b52a31eSHanoh Haim
8258b52a31eSHanoh Haim    def _distance (self):
8268b52a31eSHanoh Haim        return ( (self.max - self.min) / min(self.max, self.min) )
8278b52a31eSHanoh Haim
8288b52a31eSHanoh Haim    def time_to_end (self):
8298b52a31eSHanoh Haim        time_in_sec = (self.iters_to_end() * self.iter_duration)
8308b52a31eSHanoh Haim        return timedelta(seconds = time_in_sec)
8318b52a31eSHanoh Haim
8328b52a31eSHanoh Haim    def iters_to_end (self):
8338b52a31eSHanoh Haim        # find 2% point
8348b52a31eSHanoh Haim        ma = self.max
8358b52a31eSHanoh Haim        mi = self.min
8368b52a31eSHanoh Haim        iter = 0
8378b52a31eSHanoh Haim
8388b52a31eSHanoh Haim        while True:
8398b52a31eSHanoh Haim            dist = (ma - mi) / min(ma , mi)
8408b52a31eSHanoh Haim            if dist < 0.02:
8418b52a31eSHanoh Haim                break
8428b52a31eSHanoh Haim            if random.choice(["up", "down"]) == "down":
8438b52a31eSHanoh Haim                ma = (ma + mi) / 2
8448b52a31eSHanoh Haim            else:
8458b52a31eSHanoh Haim                mi = (ma + mi) / 2
8468b52a31eSHanoh Haim
8478b52a31eSHanoh Haim            iter += 1
8488b52a31eSHanoh Haim
8498b52a31eSHanoh Haim        return (iter)
8508b52a31eSHanoh Haim
8518b52a31eSHanoh Haim    def _cur (self):
8528b52a31eSHanoh Haim        return ( (self.min + self.max) / 2 )
8538b52a31eSHanoh Haim
8548b52a31eSHanoh Haim    def _add_success_point (self, res_dict):
8558b52a31eSHanoh Haim        self.success_points.append(res_dict.copy())
8568b52a31eSHanoh Haim
8578b52a31eSHanoh Haim    def _is_found (self):
8588b52a31eSHanoh Haim        return (self.found)
8598b52a31eSHanoh Haim
8608b52a31eSHanoh Haim    def _next_iter_duration (self):
8618b52a31eSHanoh Haim        return (self.iter_duration)
8628b52a31eSHanoh Haim
8638b52a31eSHanoh Haim    # execute iteration
8648b52a31eSHanoh Haim    def _execute (self):
8658b52a31eSHanoh Haim        # reset the found var before running
8668b52a31eSHanoh Haim        self.found = False
8678b52a31eSHanoh Haim
8688b52a31eSHanoh Haim        # run and print results
8698b52a31eSHanoh Haim        res_dict = trex_run(self.job_summary, self._cur(), self.iter_duration)
8708b52a31eSHanoh Haim
8718b52a31eSHanoh Haim        self.iter_num += 1
8728b52a31eSHanoh Haim        cur = self._cur()
8738b52a31eSHanoh Haim
8748b52a31eSHanoh Haim        if (self._distance() < 0.02):
8758b52a31eSHanoh Haim            if (check_condition(self.cond_type, res_dict)):
8768b52a31eSHanoh Haim                # distance < 2% and success - we are done
8778b52a31eSHanoh Haim                self.found = True
8788b52a31eSHanoh Haim            else:
8798b52a31eSHanoh Haim                # lower to 90% of current and retry
8808b52a31eSHanoh Haim                self.min = cur * 0.9
8818b52a31eSHanoh Haim                self.max = cur
8828b52a31eSHanoh Haim        else:
8838b52a31eSHanoh Haim            # success
8848b52a31eSHanoh Haim            if (check_condition(self.cond_type, res_dict)):
8858b52a31eSHanoh Haim                self.min = cur
8868b52a31eSHanoh Haim            else:
8878b52a31eSHanoh Haim                self.max = cur
8888b52a31eSHanoh Haim
8898b52a31eSHanoh Haim        if (check_condition(self.cond_type, res_dict)):
8908b52a31eSHanoh Haim            self._add_success_point(res_dict)
8918b52a31eSHanoh Haim
8928b52a31eSHanoh Haim        return res_dict
8938b52a31eSHanoh Haim
8948b52a31eSHanoh Haim    # find the max M before
8958b52a31eSHanoh Haim    def find_max_m (self):
8968b52a31eSHanoh Haim
8978b52a31eSHanoh Haim        res_dict = {}
8988b52a31eSHanoh Haim        while not self._is_found():
8998b52a31eSHanoh Haim
9008b52a31eSHanoh Haim            logger.log("\n-> Starting Find Iteration #{0}\n".format(self.iter_num))
9018b52a31eSHanoh Haim            logger.log("Estimated BW         ~=  {0:,.2f} [Mbps]".format(m_to_mbps(self.job_summary, self._cur())))
9028b52a31eSHanoh Haim            logger.log("M                     =  {0:.6f}".format(self._cur()))
9038b52a31eSHanoh Haim            logger.log("Duration              =  {0} seconds".format(self._next_iter_duration()))
9048b52a31eSHanoh Haim            logger.log("Current BW Range      =  {0:,.2f} [Mbps] / {1:,.2f} [Mbps]".format(m_to_mbps(self.job_summary, self.min), m_to_mbps(self.job_summary, self.max)))
9058b52a31eSHanoh Haim            logger.log("Est. Iterations Left  =  {0} Iterations".format(self.iters_to_end()))
9068b52a31eSHanoh Haim            logger.log("Est. Time Left        =  {0}\n".format(self.time_to_end()))
9078b52a31eSHanoh Haim
9088b52a31eSHanoh Haim            res_dict = self._execute()
9098b52a31eSHanoh Haim
9108b52a31eSHanoh Haim            print_trex_results(res_dict, self.cond_type)
9118b52a31eSHanoh Haim
9128b52a31eSHanoh Haim        find_results = res_dict.copy()
9138b52a31eSHanoh Haim        find_results['max_m'] = self._cur()
9148b52a31eSHanoh Haim        return (find_results)
9158b52a31eSHanoh Haim
9168b52a31eSHanoh Haim######################## describe a plot job ########################
9178b52a31eSHanoh Haimclass PlotJob:
9188b52a31eSHanoh Haim    def __init__(self, findjob):
9198b52a31eSHanoh Haim        self.job_summary = findjob.job_summary
9208b52a31eSHanoh Haim
9218b52a31eSHanoh Haim        self.plot_points = list(findjob.success_points)
9228b52a31eSHanoh Haim        self.plot_points.sort(key = lambda item:item['tx'])
9238b52a31eSHanoh Haim
9248b52a31eSHanoh Haim    def plot (self, duration = 300):
9258b52a31eSHanoh Haim        return self.plot_points
9268b52a31eSHanoh Haim
9278b52a31eSHanoh Haim        # add points if needed
9288b52a31eSHanoh Haim        #iter = 0
9298b52a31eSHanoh Haim        #for point in self.success_points:
9308b52a31eSHanoh Haim            #iter += 1
9318b52a31eSHanoh Haim            #logger.log("\nPlotting Point [{0}/{1}]:\n".format(iter, self.plot_count))
9328b52a31eSHanoh Haim            #logger.log("Estimated BW   ~= {0:,.2f} [Mbps]\n".format(m_to_mbps(self.job_summary, point['m'])))
9338b52a31eSHanoh Haim            #logger.log("M               = {0:.6f}".format(point['m']))
9348b52a31eSHanoh Haim            #logger.log("Duration        = {0} seconds\n".format(duration))
9358b52a31eSHanoh Haim
9368b52a31eSHanoh Haim            #res_dict = trex_run(self.job_summary, point['m'], duration)
9378b52a31eSHanoh Haim            #print_trex_results(res_dict, self.job_summary['cond_type'])
9388b52a31eSHanoh Haim
9398b52a31eSHanoh Haim            #self.plot_points.append(dict(res_dict))
9408b52a31eSHanoh Haim
9418b52a31eSHanoh Haim        #self.plot_points = list(self.success_points)
9428b52a31eSHanoh Haim
9438b52a31eSHanoh Haim        #print self.plot_points
9448b52a31eSHanoh Haim        #self.plot_points.sort(key = lambda item:item['m'])
9458b52a31eSHanoh Haim        #print self.plot_points
9468b52a31eSHanoh Haim
9478b52a31eSHanoh Haim        #return self.plot_points
9488b52a31eSHanoh Haim
9498b52a31eSHanoh Haim
9508b52a31eSHanoh Haimdef generate_job_id ():
9518b52a31eSHanoh Haim    return (str(int(random.getrandbits(32))))
9528b52a31eSHanoh Haim
9538b52a31eSHanoh Haimdef print_header ():
9544c797902SDan Klein    logger.log("--== TRex Performance Tool v1.0 (2014) ==--")
9558b52a31eSHanoh Haim
9568b52a31eSHanoh Haim# print startup summary
9578b52a31eSHanoh Haimdef log_startup_summary (job_summary):
9588b52a31eSHanoh Haim
9598b52a31eSHanoh Haim    trex = job_summary['trex']
9608b52a31eSHanoh Haim    trex_config = job_summary['trex_params']
9618b52a31eSHanoh Haim
9628b52a31eSHanoh Haim    logger.log("\nWork Request Details:\n")
9638b52a31eSHanoh Haim    logger.log("Setup Details:\n")
964b18e8dc6SHanoh Haim    logger.log("TRex Config File:   {0}".format(job_summary['config_file']))
9658b52a31eSHanoh Haim    logger.log("Machine Name:        {0}".format(trex_config['trex_name']))
966b18e8dc6SHanoh Haim    logger.log("TRex Type:          {0}".format(trex_config['trex_machine_type']))
967b18e8dc6SHanoh Haim    logger.log("TRex Dual Int. Tx:  {0}".format(trex_config['trex_is_dual']))
9688b52a31eSHanoh Haim    logger.log("Router Interface:    {0}".format(trex_config['router_interface']))
9698b52a31eSHanoh Haim
9708b52a31eSHanoh Haim    logger.log("\nJob Details:\n")
9718b52a31eSHanoh Haim    logger.log("Job Name:            {0}".format(job_summary['job_name']))
9728b52a31eSHanoh Haim    logger.log("YAML file:           {0}".format(job_summary['yaml']))
9738b52a31eSHanoh Haim    logger.log("Job Type:            {0}".format(job_summary['job_type_str']))
9748b52a31eSHanoh Haim    logger.log("Condition Type:      {0}".format(job_summary['cond_name']))
9758b52a31eSHanoh Haim    logger.log("Job Log:             {0}".format(job_summary['log_filename']))
9768b52a31eSHanoh Haim    logger.log("Email Report:        {0}".format(job_summary['email']))
9778b52a31eSHanoh Haim
9788b52a31eSHanoh Haim#   logger.log("\nTrex Command Used:\n{0}".format(trex.build_cmd(1, 10)))
9798b52a31eSHanoh Haim
9808b52a31eSHanoh Haimdef load_trex_config_params (filename, yaml_file):
9818b52a31eSHanoh Haim    config = {}
9828b52a31eSHanoh Haim
9838b52a31eSHanoh Haim    parser = ConfigParser.ConfigParser()
9848b52a31eSHanoh Haim
9858b52a31eSHanoh Haim    try:
9868b52a31eSHanoh Haim        parser.read(filename)
9878b52a31eSHanoh Haim
9888b52a31eSHanoh Haim        config['trex_name'] = parser.get("trex", "machine_name")
9898b52a31eSHanoh Haim        config['trex_port'] = parser.get("trex", "machine_port")
9908b52a31eSHanoh Haim        config['trex_hisory_size'] = parser.getint("trex", "history_size")
9918b52a31eSHanoh Haim
9928b52a31eSHanoh Haim        config['trex_latency_condition'] = parser.getint("trex", "latency_condition")
9938b52a31eSHanoh Haim        config['trex_yaml_file'] = yaml_file
9948b52a31eSHanoh Haim
9958b52a31eSHanoh Haim        # support legacy data
9968b52a31eSHanoh Haim        config['trex_latency'] = parser.getint("trex", "latency")
9978b52a31eSHanoh Haim        config['trex_limit_ports'] = parser.getint("trex", "limit_ports")
9988b52a31eSHanoh Haim        config['trex_cores'] = parser.getint("trex", "cores")
9998b52a31eSHanoh Haim        config['trex_machine_type'] = parser.get("trex", "machine_type")
10008b52a31eSHanoh Haim        config['trex_is_dual'] = parser.getboolean("trex", "is_dual")
10018b52a31eSHanoh Haim
10028b52a31eSHanoh Haim        # optional Trex parameters
10038b52a31eSHanoh Haim        if parser.has_option("trex", "config_file"):
10048b52a31eSHanoh Haim            config['trex_config_file'] = parser.get("trex", "config_file")
10058b52a31eSHanoh Haim        else:
10068b52a31eSHanoh Haim            config['trex_config_file'] = None
10078b52a31eSHanoh Haim
10088b52a31eSHanoh Haim        if parser.has_option("trex", "misc_params"):
10098b52a31eSHanoh Haim            config['trex_misc_params'] = parser.get("trex", "misc_params")
10108b52a31eSHanoh Haim        else:
10118b52a31eSHanoh Haim            config['trex_misc_params'] = None
10128b52a31eSHanoh Haim
10138b52a31eSHanoh Haim        # router section
10148b52a31eSHanoh Haim
10158b52a31eSHanoh Haim        if parser.has_option("router", "port"):
10168b52a31eSHanoh Haim            config['router_port'] = parser.get("router", "port")
10178b52a31eSHanoh Haim        else:
10188b52a31eSHanoh Haim            # simple telnet port
10198b52a31eSHanoh Haim            config['router_port'] = 23
10208b52a31eSHanoh Haim
10218b52a31eSHanoh Haim        config['router_interface'] = parser.get("router", "interface")
10228b52a31eSHanoh Haim        config['router_password'] = parser.get("router", "password")
10238b52a31eSHanoh Haim        config['router_type'] = parser.get("router", "type")
10248b52a31eSHanoh Haim
10258b52a31eSHanoh Haim    except Exception as inst:
10268b52a31eSHanoh Haim        raise TrexRunException("\nBad configuration file: '{0}'\n\n{1}".format(filename, inst))
10278b52a31eSHanoh Haim
10288b52a31eSHanoh Haim    return config
10298b52a31eSHanoh Haim
10308b52a31eSHanoh Haimdef prepare_for_run (job_summary):
10318b52a31eSHanoh Haim    global logger
10328b52a31eSHanoh Haim
10338b52a31eSHanoh Haim    # generate unique id
10348b52a31eSHanoh Haim    job_summary['job_id'] = generate_job_id()
10358b52a31eSHanoh Haim    job_summary['job_dir'] = "trex_job_{0}".format(job_summary['job_id'])
10368b52a31eSHanoh Haim
10378b52a31eSHanoh Haim    job_summary['start_time'] = datetime.datetime.now()
10388b52a31eSHanoh Haim
10398b52a31eSHanoh Haim    if not job_summary['email']:
10408b52a31eSHanoh Haim        job_summary['user'] = getpass.getuser()
10418b52a31eSHanoh Haim        job_summary['email'] = "{0}@cisco.com".format(job_summary['user'])
10428b52a31eSHanoh Haim
10438b52a31eSHanoh Haim    # create dir for reports
10448b52a31eSHanoh Haim    try:
10458b52a31eSHanoh Haim        job_summary['job_dir'] = os.path.abspath( os.path.join(os.getcwd(), 'logs', job_summary['job_dir']) )
10469adfbadaSYaroslav Brustinov        print(job_summary['job_dir'])
10478b52a31eSHanoh Haim        os.makedirs( job_summary['job_dir'] )
10488b52a31eSHanoh Haim
10498b52a31eSHanoh Haim    except OSError as err:
10508b52a31eSHanoh Haim        if err.errno == errno.EACCES:
10518b52a31eSHanoh Haim            # fall back. try creating the dir name at /tmp path
10528b52a31eSHanoh Haim            job_summary['job_dir'] = os.path.join("/tmp/", "trex_job_{0}".format(job_summary['job_id']) )
10538b52a31eSHanoh Haim            os.makedirs(job_summary['job_dir'])
10548b52a31eSHanoh Haim
10558b52a31eSHanoh Haim    job_summary['log_filename'] = os.path.join(job_summary['job_dir'], "trex_log_{0}.txt".format(job_summary['job_id']))
10568b52a31eSHanoh Haim    job_summary['graph_filename'] = os.path.join(job_summary['job_dir'], "trex_graph_{0}.html".format(job_summary['job_id']))
10578b52a31eSHanoh Haim
10588b52a31eSHanoh Haim    # init logger
10598b52a31eSHanoh Haim    logger = MyLogger(job_summary['log_filename'])
10608b52a31eSHanoh Haim
10618b52a31eSHanoh Haim    # mark those as not populated yet
10628b52a31eSHanoh Haim    job_summary['find_results'] = None
10638b52a31eSHanoh Haim    job_summary['plot_results'] = None
10648b52a31eSHanoh Haim
10658b52a31eSHanoh Haim    # create trex client instance
10668b52a31eSHanoh Haim    trex_params = load_trex_config_params(job_summary['config_file'],job_summary['yaml'])
10678b52a31eSHanoh Haim    trex = CTRexClient(trex_host = trex_params['trex_name'],
10688b52a31eSHanoh Haim        max_history_size = trex_params['trex_hisory_size'],
10698b52a31eSHanoh Haim        trex_daemon_port = trex_params['trex_port'])
10708b52a31eSHanoh Haim
10718b52a31eSHanoh Haim    job_summary['trex'] = trex
10728b52a31eSHanoh Haim    job_summary['trex_params'] = trex_params
10738b52a31eSHanoh Haim
10748b52a31eSHanoh Haim    # create trex task thread
10758b52a31eSHanoh Haim    job_summary['trex_thread'] = CTRexWithRouter(trex, trex_params);
10768b52a31eSHanoh Haim
10778b52a31eSHanoh Haim    # in case of an error we need to call the remote cleanup
10788b52a31eSHanoh Haim    cleanup_list.append(trex.stop_trex)
10798b52a31eSHanoh Haim
10808b52a31eSHanoh Haim    # signal handler
10818b52a31eSHanoh Haim    signal.signal(signal.SIGINT, int_signal_handler)
10828b52a31eSHanoh Haim    signal.signal(signal.SIGUSR1, error_signal_handler)
10838b52a31eSHanoh Haim
10848b52a31eSHanoh Haim
10858b52a31eSHanoh Haimdef after_run (job_summary):
10868b52a31eSHanoh Haim
10878b52a31eSHanoh Haim    job_summary['total_run_time'] = datetime.datetime.now() - job_summary['start_time']
10888b52a31eSHanoh Haim    reporter = JobReporter(job_summary)
10898b52a31eSHanoh Haim    reporter.print_summary()
10908b52a31eSHanoh Haim    reporter.send_email_report()
10918b52a31eSHanoh Haim
10928b52a31eSHanoh Haimdef launch (job_summary):
10938b52a31eSHanoh Haim
10948b52a31eSHanoh Haim    prepare_for_run(job_summary)
10958b52a31eSHanoh Haim
10968b52a31eSHanoh Haim    print_header()
10978b52a31eSHanoh Haim
10988b52a31eSHanoh Haim    log_startup_summary(job_summary)
10998b52a31eSHanoh Haim
11008b52a31eSHanoh Haim    # find the correct M range if not provided
11018b52a31eSHanoh Haim    range_results = find_m_range(job_summary)
11028b52a31eSHanoh Haim
11038b52a31eSHanoh Haim    job_summary['base_m_unit'] = range_results['base_m_unit']
11048b52a31eSHanoh Haim
11058b52a31eSHanoh Haim    if job_summary['m_range']:
11068b52a31eSHanoh Haim        m_range = job_summary['m_range']
11078b52a31eSHanoh Haim    else:
11088b52a31eSHanoh Haim        m_range = range_results['m_range']
11098b52a31eSHanoh Haim
11108b52a31eSHanoh Haim    logger.log("\nJob Bandwidth Working Range:\n")
11118b52a31eSHanoh Haim    logger.log("Min M = {0:.6f} / {1:,.2f} [Mbps] \nMax M = {2:.6f} / {3:,.2f} [Mbps]".format(m_range[0], m_to_mbps(job_summary, m_range[0]), m_range[1], m_to_mbps(job_summary, m_range[1])))
11128b52a31eSHanoh Haim
11138b52a31eSHanoh Haim    # job time
11148b52a31eSHanoh Haim    findjob = FindJob(m_range[0], m_range[1], job_summary)
11158b52a31eSHanoh Haim    job_summary['find_results'] = findjob.find_max_m()
11168b52a31eSHanoh Haim
11178b52a31eSHanoh Haim    if job_summary['job_type'] == "all":
11188b52a31eSHanoh Haim        # plot points to graph
11198b52a31eSHanoh Haim        plotjob = PlotJob(findjob)
11208b52a31eSHanoh Haim        job_summary['plot_results'] = plotjob.plot()
11218b52a31eSHanoh Haim
11228b52a31eSHanoh Haim    after_run(job_summary)
11238b52a31eSHanoh Haim
11248b52a31eSHanoh Haim
11258b52a31eSHanoh Haim# populate the fields for run
11268b52a31eSHanoh Haimdef populate_fields (job_summary, args):
11278b52a31eSHanoh Haim    job_summary['config_file']  = args.config_file
11288b52a31eSHanoh Haim    job_summary['job_type']     = args.job
11298b52a31eSHanoh Haim    job_summary['cond_type']    = args.cond_type
11308b52a31eSHanoh Haim    job_summary['yaml']         = args.yaml
11318b52a31eSHanoh Haim
11328b52a31eSHanoh Haim    if args.n:
11338b52a31eSHanoh Haim        job_summary['job_name']     = args.n
11348b52a31eSHanoh Haim    else:
11358b52a31eSHanoh Haim        job_summary['job_name']     = "Nameless"
11368b52a31eSHanoh Haim
11378b52a31eSHanoh Haim    # did the user provided an email
11388b52a31eSHanoh Haim    if args.e:
11398b52a31eSHanoh Haim        job_summary['email']        = args.e
11408b52a31eSHanoh Haim    else:
11418b52a31eSHanoh Haim        job_summary['email']        = None
11428b52a31eSHanoh Haim
11438b52a31eSHanoh Haim    # did the user provide a range ?
11448b52a31eSHanoh Haim    if args.m:
11458b52a31eSHanoh Haim        job_summary['m_range'] = args.m
11468b52a31eSHanoh Haim    else:
11478b52a31eSHanoh Haim        job_summary['m_range'] = None
11488b52a31eSHanoh Haim
11498b52a31eSHanoh Haim    # some pretty shows
11508b52a31eSHanoh Haim    job_summary['cond_name'] = 'Drop Pkt' if (args.cond_type == 'drop') else 'High Latency'
11518b52a31eSHanoh Haim
11528b52a31eSHanoh Haim    if args.job == "find":
11538b52a31eSHanoh Haim        job_summary['job_type_str'] = "Find Max BW"
11548b52a31eSHanoh Haim    elif args.job == "plot":
11558b52a31eSHanoh Haim        job_summary['job_type_str'] = "Plot Graph"
11568b52a31eSHanoh Haim    else:
11578b52a31eSHanoh Haim        job_summary['job_type_str'] = "Find Max BW & Plot Graph"
11588b52a31eSHanoh Haim
11598b52a31eSHanoh Haim    if args.job != "find":
11608b52a31eSHanoh Haim        verify_glibc_version()
11618b52a31eSHanoh Haim
11628b52a31eSHanoh Haim
11638b52a31eSHanoh Haim
11648b52a31eSHanoh Haim# verify file exists for argparse
11658b52a31eSHanoh Haimdef is_valid_file (parser, err_msg, filename):
11668b52a31eSHanoh Haim    if not os.path.exists(filename):
11678b52a31eSHanoh Haim        parser.error("{0}: '{1}'".format(err_msg, filename))
11688b52a31eSHanoh Haim    else:
11698b52a31eSHanoh Haim        return (filename)  # return an open file handle
11708b52a31eSHanoh Haim
11718b52a31eSHanoh Haimdef entry ():
11728b52a31eSHanoh Haim
11738b52a31eSHanoh Haim    job_summary = {}
11748b52a31eSHanoh Haim
11758b52a31eSHanoh Haim    parser = argparse.ArgumentParser()
11768b52a31eSHanoh Haim
11778b52a31eSHanoh Haim    parser.add_argument("-n", help="Job Name",
11788b52a31eSHanoh Haim                        type = str)
11798b52a31eSHanoh Haim
11808b52a31eSHanoh Haim    parser.add_argument("-m", help="M Range [default: auto calcuation]",
11818b52a31eSHanoh Haim                        nargs = 2,
11828b52a31eSHanoh Haim                        type = float)
11838b52a31eSHanoh Haim
11848b52a31eSHanoh Haim    parser.add_argument("-e", help="E-Mail for report [default: whoami@cisco.com]",
11858b52a31eSHanoh Haim                        type = str)
11868b52a31eSHanoh Haim
11878b52a31eSHanoh Haim    parser.add_argument("-c", "--cfg", dest = "config_file", required = True,
11888b52a31eSHanoh Haim                        help = "Configuration File For Trex/Router Pair",
11898b52a31eSHanoh Haim                        type = lambda x: is_valid_file(parser, "config file does not exists",x))
11908b52a31eSHanoh Haim
11918b52a31eSHanoh Haim    parser.add_argument("job", help = "Job type",
11928b52a31eSHanoh Haim                        type = str,
11938b52a31eSHanoh Haim                        choices = ['find', 'plot', 'all'])
11948b52a31eSHanoh Haim
11958b52a31eSHanoh Haim    parser.add_argument("cond_type", help="type of failure condition",
11968b52a31eSHanoh Haim                        type = str,
11978b52a31eSHanoh Haim                        choices = ['latency','drop'])
11988b52a31eSHanoh Haim
11998b52a31eSHanoh Haim    parser.add_argument("-f", "--yaml", dest = "yaml", required = True,
12008b52a31eSHanoh Haim                        help="YAML file to use", type = str)
12018b52a31eSHanoh Haim
12028b52a31eSHanoh Haim    args = parser.parse_args()
12038b52a31eSHanoh Haim
12048b52a31eSHanoh Haim    with TermMng():
12058b52a31eSHanoh Haim        try:
12068b52a31eSHanoh Haim            populate_fields(job_summary, args)
12078b52a31eSHanoh Haim            launch(job_summary)
12088b52a31eSHanoh Haim
12098b52a31eSHanoh Haim        except Exception as e:
12108b52a31eSHanoh Haim            ErrorHandler(e, traceback.format_exc())
12118b52a31eSHanoh Haim
12128b52a31eSHanoh Haim    logger.log("\nReport bugs to imarom@cisco.com\n")
12138b52a31eSHanoh Haim    g_stop = True
12148b52a31eSHanoh Haim
12158b52a31eSHanoh Haimdef dummy_test ():
12168b52a31eSHanoh Haim    job_summary = {}
12178b52a31eSHanoh Haim    find_results = {}
12188b52a31eSHanoh Haim
12198b52a31eSHanoh Haim    job_summary['config_file'] = 'config/trex01-1g.cfg'
12208b52a31eSHanoh Haim    job_summary['yaml'] = 'dummy.yaml'
12218b52a31eSHanoh Haim    job_summary['email']        = 'imarom@cisco.com'
12228b52a31eSHanoh Haim    job_summary['job_name'] = 'test'
12238b52a31eSHanoh Haim    job_summary['job_type_str'] = 'test'
12248b52a31eSHanoh Haim
12258b52a31eSHanoh Haim    prepare_for_run(job_summary)
12268b52a31eSHanoh Haim
12278b52a31eSHanoh Haim    time.sleep(2)
12288b52a31eSHanoh Haim    job_summary['yaml'] = 'dummy.yaml'
12298b52a31eSHanoh Haim    job_summary['job_type']  = 'find'
12308b52a31eSHanoh Haim    job_summary['cond_name'] = 'Drop'
12318b52a31eSHanoh Haim    job_summary['cond_type'] = 'drop'
12328b52a31eSHanoh Haim    job_summary['job_id']= 94817231
12338b52a31eSHanoh Haim
12348b52a31eSHanoh Haim
12358b52a31eSHanoh Haim    find_results['tx'] = 210.23
12368b52a31eSHanoh Haim    find_results['m'] = 1.292812
12378b52a31eSHanoh Haim    find_results['total-pps'] = 1000
12388b52a31eSHanoh Haim    find_results['cpu_util'] = 74.0
12398b52a31eSHanoh Haim    find_results['maximum-latency'] = 4892
12408b52a31eSHanoh Haim    find_results['average-latency'] = 201
12418b52a31eSHanoh Haim    find_results['total-pkt-drop'] = 0
12428b52a31eSHanoh Haim
12438b52a31eSHanoh Haim
12448b52a31eSHanoh Haim    findjob = FindJob(1,1,job_summary)
12458b52a31eSHanoh Haim    plotjob = PlotJob(findjob)
12468b52a31eSHanoh Haim    job_summary['plot_results'] = plotjob.plot()
12478b52a31eSHanoh Haim
12488b52a31eSHanoh Haim    job_summary['find_results'] = find_results
12498b52a31eSHanoh Haim    job_summary['plot_results'] = [{'cpu_util': 2.0,'norm_cpu': 1.0,  'total-pps': 1000, 'expected-bps': 999980.0, 'average-latency': 85.0, 'tx': 0.00207*1000, 'total-pkt-drop': 0.0, 'maximum-latency': 221.0},
12508b52a31eSHanoh Haim                                   {'cpu_util': 8.0,'norm_cpu': 1.0,  'total-pps': 1000,'expected-bps': 48500000.0, 'average-latency': 87.0, 'tx': 0.05005*1000, 'total-pkt-drop': 0.0, 'maximum-latency': 279.0},
12518b52a31eSHanoh Haim                                   {'cpu_util': 14.0,'norm_cpu': 1.0, 'total-pps': 1000,'expected-bps': 95990000.0, 'average-latency': 92.0, 'tx': 0.09806*1000, 'total-pkt-drop': 0.0, 'maximum-latency': 273.0},
12528b52a31eSHanoh Haim                                   {'cpu_util': 20.0,'norm_cpu': 1.0, 'total-pps': 1000,'expected-bps': 143490000.0, 'average-latency': 95.0, 'tx': 0.14613*1000, 'total-pkt-drop': 0.0, 'maximum-latency': 271.0},
12538b52a31eSHanoh Haim                                   {'cpu_util': 25.0,'norm_cpu': 1.0, 'total-pps': 1000,'expected-bps': 190980000.0, 'average-latency': 97.0, 'tx': 0.1933*1000, 'total-pkt-drop': 0.0, 'maximum-latency': 302.0},
12548b52a31eSHanoh Haim                                   {'cpu_util': 31.0,'norm_cpu': 1.0, 'total-pps': 1000,'expected-bps': 238480000.0, 'average-latency': 98.0, 'tx': 0.24213*1000, 'total-pkt-drop': 1.0, 'maximum-latency': 292.0},
12558b52a31eSHanoh Haim                                   {'cpu_util': 37.0,'norm_cpu': 1.0, 'total-pps': 1000, 'expected-bps': 285970000.0, 'average-latency': 99.0, 'tx': 0.29011*1000, 'total-pkt-drop': 0.0, 'maximum-latency': 344.0},
12568b52a31eSHanoh Haim                                   {'cpu_util': 43.0,'norm_cpu': 1.0, 'total-pps': 1000, 'expected-bps': 333470000.0, 'average-latency': 100.0, 'tx': 0.3382*1000, 'total-pkt-drop': 0.0, 'maximum-latency': 351.0},
12578b52a31eSHanoh Haim                                   {'cpu_util': 48.0,'norm_cpu': 1.0, 'total-pps': 1000, 'expected-bps': 380970000.0, 'average-latency': 100.0, 'tx': 0.38595*1000, 'total-pkt-drop': 0.0, 'maximum-latency': 342.0},
12588b52a31eSHanoh Haim                                   {'cpu_util': 54.0,'norm_cpu': 1.0, 'total-pps': 1000, 'expected-bps': 428460000.0, 'average-latency': 19852.0, 'tx': 0.43438*1000, 'total-pkt-drop': 1826229.0, 'maximum-latency': 25344.0}]
12598b52a31eSHanoh Haim
12608b52a31eSHanoh Haim
12618b52a31eSHanoh Haim
12628b52a31eSHanoh Haim    after_run(job_summary)
12638b52a31eSHanoh Haim
12648b52a31eSHanoh Haimif __name__ == "__main__":
12658b52a31eSHanoh Haim    entry ()
12668b52a31eSHanoh Haim