def begin(self):
        command = sys.argv
        command[0] = os.path.basename(command[0])
        self.logger.info('input command: ' + ' '.join(command))
        self.logger.info('param: ' + str(self.param))
        self.logger.info('ptl version: ' + str(ptl.__version__))
        _m = 'platform: ' + ' '.join(platform.uname()).strip()
        self.logger.info(_m)
        self.logger.info('python version: ' + str(platform.python_version()))
        self.logger.info('user: '******'-' * 80)

        if self.lcov_data is not None:
            self.lcov_utils = LcovUtils(cov_bin=self.lcov_bin,
                                        html_bin=self.genhtml_bin,
                                        cov_out=self.lcov_out,
                                        data_dir=self.lcov_data,
                                        html_nosrc=self.lcov_nosrc,
                                        html_baseurl=self.lcov_baseurl)
            # Initialize coverage analysis
            self.lcov_utils.zero_coverage()
            # The following 'dance' is done due to some oddities on lcov's
            # part, according to this the lcov readme file at
            # http://ltp.sourceforge.net/coverage/lcov/readme.php that reads:
            #
            # Note that this step only works after the application has
            # been started and stopped at least once. Otherwise lcov will
            # abort with an error mentioning that there are no data/.gcda
            # files.
            self.lcov_utils.initialize_coverage(name='PTLTestCov')
            PBSInitServices().restart()
        self._cleanup()
    def begin(self):
        command = sys.argv
        command[0] = os.path.basename(command[0])
        self.logger.info('input command: ' + ' '.join(command))
        self.logger.info('param: ' + str(self.param))
        self.logger.info('ptl version: ' + str(ptl.__version__))
        _m = 'platform: ' + ' '.join(platform.uname()).strip()
        self.logger.info(_m)
        self.logger.info('python version: ' + str(platform.python_version()))
        self.logger.info('user: '******'-' * 80)

        if self.lcov_data is not None:
            self.lcov_utils = LcovUtils(cov_bin=self.lcov_bin,
                                        html_bin=self.genhtml_bin,
                                        cov_out=self.lcov_out,
                                        data_dir=self.lcov_data,
                                        html_nosrc=self.lcov_nosrc,
                                        html_baseurl=self.lcov_baseurl)
            # Initialize coverage analysis
            self.lcov_utils.zero_coverage()
            # The following 'dance' is done due to some oddities on lcov's
            # part, according to this the lcov readme file at
            # http://ltp.sourceforge.net/coverage/lcov/readme.php that reads:
            #
            # Note that this step only works after the application has
            # been started and stopped at least once. Otherwise lcov will
            # abort with an error mentioning that there are no data/.gcda
            # files.
            self.lcov_utils.initialize_coverage(name='PTLTestCov')
            PBSInitServices().restart()
        self._cleanup()
class PTLTestRunner(Plugin):
    """
    PTL Test Runner Plugin
    """
    name = 'PTLTestRunner'
    score = sys.maxint - 4
    logger = logging.getLogger(__name__)

    def __init__(self):
        Plugin.__init__(self)
        self.param = None
        self.lcov_bin = None
        self.lcov_data = None
        self.lcov_out = None
        self.lcov_utils = None
        self.lcov_nosrc = None
        self.lcov_baseurl = None
        self.genhtml_bin = None
        self.config = None
        self.result = None
        self.tc_failure_threshold = None
        self.cumulative_tc_failure_threshold = None
        self.__failed_tc_count = 0
        self.__tf_count = 0
        self.__failed_tc_count_msg = False

    def options(self, parser, env):
        """
        Register command line options
        """
        pass

    def set_data(self, paramfile, testparam, lcov_bin, lcov_data, lcov_out,
                 genhtml_bin, lcov_nosrc, lcov_baseurl, tc_failure_threshold,
                 cumulative_tc_failure_threshold):
        if paramfile is not None:
            _pf = open(paramfile, 'r')
            _params_from_file = _pf.readlines()
            _pf.close()
            _nparams = []
            for l in range(len(_params_from_file)):
                if _params_from_file[l].startswith('#'):
                    continue
                else:
                    _nparams.append(_params_from_file[l])
            _f = ','.join(map(lambda l: l.strip('\r\n'), _nparams))
            if testparam is not None:
                testparam += ',' + _f
            else:
                testparam = _f
        self.param = testparam
        self.lcov_bin = lcov_bin
        self.lcov_data = lcov_data
        self.lcov_out = lcov_out
        self.genhtml_bin = genhtml_bin
        self.lcov_nosrc = lcov_nosrc
        self.lcov_baseurl = lcov_baseurl
        self.tc_failure_threshold = tc_failure_threshold
        self.cumulative_tc_failure_threshold = cumulative_tc_failure_threshold

    def configure(self, options, config):
        """
        Configure the plugin and system, based on selected options
        """
        self.config = config
        self.enabled = True

    def prepareTestRunner(self, runner):
        """
        Prepare test runner
        """
        return PtlTestRunner(verbosity=3, config=self.config)

    def prepareTestResult(self, result):
        """
        Prepare test result
        """
        self.result = result

    def startContext(self, context):
        context.param = self.param
        context.start_time = datetime.datetime.now()
        if isclass(context) and issubclass(context, PBSTestSuite):
            self.result.logger.info(self.result.separator1)
            self.result.logger.info('suite name: ' + context.__name__)
            doc = context.__doc__
            if doc is not None:
                self.result.logger.info('suite docstring: \n' + doc + '\n')
            self.result.logger.info(self.result.separator1)
            self.__failed_tc_count = 0
            self.__failed_tc_count_msg = False

    def __get_timeout(self, test):
        try:
            method = getattr(test.test, getattr(test.test, '_testMethodName'))
            return getattr(method, TIMEOUT_KEY)
        except:
            if hasattr(test, 'test'):
                __conf = getattr(test.test, 'conf')
            else:
                __conf = getattr(test.context, 'conf')
            return __conf['default_testcase_timeout']

    def __set_test_end_data(self, test, err=None):
        if not hasattr(test, 'start_time'):
            test = test.context
        if err is not None:
            is_skip = issubclass(err[0], SkipTest)
            is_tctr = issubclass(err[0], TCThresholdReached)
            if not (is_skip or is_tctr):
                self.__failed_tc_count += 1
                self.__tf_count += 1
            try:
                test.err_in_string = self.result._exc_info_to_string(err, test)
            except:
                etype, value, tb = err
                test.err_in_string = ''.join(format_exception(
                    etype, value, tb))
        else:
            test.err_in_string = 'None'
        test.end_time = datetime.datetime.now()
        test.duration = test.end_time - test.start_time
        test.captured_logs = self.result.handler.get_logs()

    def startTest(self, test):
        """
        Start the test
        """
        if ((self.cumulative_tc_failure_threshold != 0)
                and (self.__tf_count >= self.cumulative_tc_failure_threshold)):
            _msg = 'Total testcases failure count exceeded cumulative'
            _msg += ' testcase failure threshold '
            _msg += '(%d)' % self.cumulative_tc_failure_threshold
            self.logger.error(_msg)
            raise KeyboardInterrupt
        if ((self.tc_failure_threshold != 0)
                and (self.__failed_tc_count >= self.tc_failure_threshold)):
            if self.__failed_tc_count_msg:
                raise TCThresholdReached
            _msg = 'Testcases failure for this testsuite count exceeded'
            _msg += ' testcase failure threshold '
            _msg += '(%d)' % self.tc_failure_threshold
            self.logger.error(_msg)
            self.__failed_tc_count_msg = True
            raise TCThresholdReached
        timeout = self.__get_timeout(test)

        def timeout_handler(signum, frame):
            raise TimeOut('Timed out after %s second' % timeout)

        old_handler = signal.signal(signal.SIGALRM, timeout_handler)
        setattr(test, 'old_sigalrm_handler', old_handler)
        signal.alarm(timeout)

    def stopTest(self, test):
        """
        Stop the test
        """
        old_sigalrm_handler = getattr(test, 'old_sigalrm_handler', None)
        if old_sigalrm_handler is not None:
            signal.signal(signal.SIGALRM, old_sigalrm_handler)
            signal.alarm(0)

    def addError(self, test, err):
        """
        Add error
        """
        if isclass(err[0]) and issubclass(err[0], TCThresholdReached):
            return True
        self.__set_test_end_data(test, err)

    def addFailure(self, test, err):
        """
        Add failure
        """
        self.__set_test_end_data(test, err)

    def addSuccess(self, test):
        """
        Add success
        """
        self.__set_test_end_data(test)

    def _cleanup(self):
        self.logger.info('Cleaning up temporary files')
        du = DshUtils()
        tmpdir = tempfile.gettempdir()
        ftd = []
        if tmpdir:
            files = du.listdir(path=tmpdir)
            bn = os.path.basename
            ftd.extend([f for f in files if bn(f).startswith('PtlPbs')])
            ftd.extend([f for f in files if bn(f).startswith('STDIN')])
            for f in ftd:
                du.rm(path=f,
                      sudo=True,
                      recursive=True,
                      force=True,
                      level=logging.DEBUG)
        os.chdir(tmpdir)
        tmppath = os.path.join(tmpdir, 'dejagnutemp%s' % os.getpid())
        if du.isdir(path=tmppath):
            du.rm(path=tmppath,
                  recursive=True,
                  sudo=True,
                  force=True,
                  level=logging.DEBUG)

    def begin(self):
        command = sys.argv
        command[0] = os.path.basename(command[0])
        self.logger.info('input command: ' + ' '.join(command))
        self.logger.info('param: ' + str(self.param))
        self.logger.info('ptl version: ' + str(ptl.__version__))
        _m = 'platform: ' + ' '.join(platform.uname()).strip()
        self.logger.info(_m)
        self.logger.info('python version: ' + str(platform.python_version()))
        self.logger.info('user: '******'-' * 80)

        if self.lcov_data is not None:
            self.lcov_utils = LcovUtils(cov_bin=self.lcov_bin,
                                        html_bin=self.genhtml_bin,
                                        cov_out=self.lcov_out,
                                        data_dir=self.lcov_data,
                                        html_nosrc=self.lcov_nosrc,
                                        html_baseurl=self.lcov_baseurl)
            # Initialize coverage analysis
            self.lcov_utils.zero_coverage()
            # The following 'dance' is done due to some oddities on lcov's
            # part, according to this the lcov readme file at
            # http://ltp.sourceforge.net/coverage/lcov/readme.php that reads:
            #
            # Note that this step only works after the application has
            # been started and stopped at least once. Otherwise lcov will
            # abort with an error mentioning that there are no data/.gcda
            # files.
            self.lcov_utils.initialize_coverage(name='PTLTestCov')
            PBSInitServices().restart()
        self._cleanup()

    def finalize(self, result):
        if self.lcov_data is not None:
            # See note above that briefly explains the 'dance' needed to get
            # reliable coverage data
            PBSInitServices().restart()
            self.lcov_utils.capture_coverage(name='PTLTestCov')
            exclude = [
                '"*work/gSOAP/*"', '"*/pbs/doc/*"', 'lex.yy.c',
                'pbs_ifl_wrap.c', 'usr/include/*', 'unsupported/*'
            ]
            self.lcov_utils.merge_coverage_traces(name='PTLTestCov',
                                                  exclude=exclude)
            self.lcov_utils.generate_html()
            self.lcov_utils.change_baseurl()
            self.logger.info('\n'.join(self.lcov_utils.summarize_coverage()))
        self._cleanup()
class PTLTestRunner(Plugin):

    """
    PTL Test Runner Plugin
    """
    name = 'PTLTestRunner'
    score = sys.maxint - 4
    logger = logging.getLogger(__name__)

    def __init__(self):
        Plugin.__init__(self)
        self.param = None
        self.lcov_bin = None
        self.lcov_data = None
        self.lcov_out = None
        self.lcov_utils = None
        self.lcov_nosrc = None
        self.lcov_baseurl = None
        self.genhtml_bin = None
        self.config = None
        self.result = None
        self.tc_failure_threshold = None
        self.cumulative_tc_failure_threshold = None
        self.__failed_tc_count = 0
        self.__tf_count = 0
        self.__failed_tc_count_msg = False
        self._test_marker = 'test_'

    def options(self, parser, env):
        """
        Register command line options
        """
        pass

    def set_data(self, paramfile, testparam,
                 lcov_bin, lcov_data, lcov_out, genhtml_bin, lcov_nosrc,
                 lcov_baseurl, tc_failure_threshold,
                 cumulative_tc_failure_threshold):
        if paramfile is not None:
            _pf = open(paramfile, 'r')
            _params_from_file = _pf.readlines()
            _pf.close()
            _nparams = []
            for l in range(len(_params_from_file)):
                if _params_from_file[l].startswith('#'):
                    continue
                else:
                    _nparams.append(_params_from_file[l])
            _f = ','.join(map(lambda l: l.strip('\r\n'), _nparams))
            if testparam is not None:
                testparam += ',' + _f
            else:
                testparam = _f
        self.param = testparam
        self.lcov_bin = lcov_bin
        self.lcov_data = lcov_data
        self.lcov_out = lcov_out
        self.genhtml_bin = genhtml_bin
        self.lcov_nosrc = lcov_nosrc
        self.lcov_baseurl = lcov_baseurl
        self.tc_failure_threshold = tc_failure_threshold
        self.cumulative_tc_failure_threshold = cumulative_tc_failure_threshold

    def configure(self, options, config):
        """
        Configure the plugin and system, based on selected options
        """
        self.config = config
        self.enabled = True
        self.param_dict = self.__get_param_dictionary()

    def prepareTestRunner(self, runner):
        """
        Prepare test runner
        """
        return PtlTestRunner(verbosity=3, config=self.config)

    def prepareTestResult(self, result):
        """
        Prepare test result
        """
        self.result = result

    def startContext(self, context):
        context.param = self.param
        context.start_time = datetime.datetime.now()
        if isclass(context) and issubclass(context, PBSTestSuite):
            self.result.logger.info(self.result.separator1)
            self.result.logger.info('suite name: ' + context.__name__)
            doc = context.__doc__
            if doc is not None:
                self.result.logger.info('suite docstring: \n' + doc + '\n')
            self.result.logger.info(self.result.separator1)
            self.__failed_tc_count = 0
            self.__failed_tc_count_msg = False

    def __get_timeout(self, test):
        try:
            method = getattr(test.test, getattr(test.test, '_testMethodName'))
            return getattr(method, TIMEOUT_KEY)
        except AttributeError:
            testcase_timeout = MINIMUM_TESTCASE_TIMEOUT
            if hasattr(test, 'test'):
                if hasattr(test.test, 'conf'):
                    __conf = getattr(test.test, 'conf')
                    testcase_timeout = __conf['default_testcase_timeout']
            elif hasattr(test, 'context'):
                if hasattr(test.context, 'conf'):
                    __conf = getattr(test.context, 'conf')
                    testcase_timeout = __conf['default_testcase_timeout']
            return testcase_timeout

    def __set_test_end_data(self, test, err=None):
        if not hasattr(test, 'start_time'):
            test = test.context
        if err is not None:
            is_skip = issubclass(err[0], SkipTest)
            is_tctr = issubclass(err[0], TCThresholdReached)
            if not (is_skip or is_tctr):
                self.__failed_tc_count += 1
                self.__tf_count += 1
            try:
                test.err_in_string = self.result._exc_info_to_string(err,
                                                                     test)
            except:
                etype, value, tb = err
                test.err_in_string = ''.join(format_exception(etype, value,
                                                              tb))
        else:
            test.err_in_string = 'None'
        test.end_time = datetime.datetime.now()
        test.duration = test.end_time - test.start_time
        test.captured_logs = self.result.handler.get_logs()

    def __get_param_dictionary(self):
        """
        Method to convert data in param into dictionary of cluster
        information
        """
        tparam_contents = {}
        nomomlist = []
        shortname = (socket.gethostname()).split('.', 1)[0]
        for key in ['servers', 'moms', 'comms', 'clients']:
            tparam_contents[key] = []
        if self.param is not None:
            for h in self.param.split(','):
                if '=' in h:
                    k, v = h.split('=', 1)
                    if (k == 'server' or k == 'servers'):
                        tparam_contents['servers'].extend(v.split(':'))
                    elif (k == 'mom' or k == 'moms'):
                        tparam_contents['moms'].extend(v.split(':'))
                    elif k == 'comms':
                        tparam_contents['comms'] = v.split(':')
                    elif k == 'client':
                        tparam_contents['clients'] = v.split(':')
                    elif k == 'nomom':
                        nomomlist = v.split(':')
        for pkey in ['servers', 'moms', 'comms', 'clients']:
            if not tparam_contents[pkey]:
                tparam_contents[pkey] = set([shortname])
            else:
                tparam_contents[pkey] = set(tparam_contents[pkey])
        if nomomlist:
            tparam_contents['moms'] -= set(nomomlist)
        return tparam_contents

    @staticmethod
    def __are_requirements_matching(param_dic=None, test=None):
        """
        Validates test requirements against test cluster information
        returns True on match or False otherwise None

        :param param_dic: dictionary of cluster information from data passed
                          to param list
        :param_dic type: dic
        :param test: test object
        :test type: object

        :returns True or False or None
        """
        ts_requirements = {}
        tc_requirements = {}
        param_count = {}
        shortname = (socket.gethostname()).split('.', 1)[0]
        if test is None:
            return None
        test_name = getattr(test.test, '_testMethodName', None)
        if test_name is not None:
            method = getattr(test.test, test_name, None)
        if method is not None:
            tc_requirements = getattr(method, REQUIREMENTS_KEY, {})
            cls = method.im_class
            ts_requirements = getattr(cls, REQUIREMENTS_KEY, {})
        if not tc_requirements:
            if not ts_requirements:
                return None
        eff_tc_req = get_effective_reqs(ts_requirements, tc_requirements)
        setattr(test.test, 'requirements', eff_tc_req)
        for key in ['servers', 'moms', 'comms', 'clients']:
            param_count['num_' + key] = len(param_dic[key])
        for pk in param_count:
            if param_count[pk] < eff_tc_req[pk]:
                return False
        if set(param_dic['moms']) & set(param_dic['servers']):
            if eff_tc_req['no_mom_on_server']:
                return False
        else:
            if not eff_tc_req['no_mom_on_server']:
                return False
        if set(param_dic['comms']) & set(param_dic['servers']):
            if eff_tc_req['no_comm_on_server']:
                return False
        else:
            if not eff_tc_req['no_comm_on_server']:
                return False
        comm_mom_list = set(param_dic['moms']) & set(param_dic['comms'])
        if comm_mom_list and shortname in comm_mom_list:
            # Excluding the server hostname for flag 'no_comm_on_mom'
            comm_mom_list.remove(shortname)
        if comm_mom_list:
            if eff_tc_req['no_comm_on_mom']:
                return False
        else:
            if not eff_tc_req['no_comm_on_mom']:
                return False

    def startTest(self, test):
        """
        Start the test
        """
        if ((self.cumulative_tc_failure_threshold != 0) and
                (self.__tf_count >= self.cumulative_tc_failure_threshold)):
            _msg = 'Total testcases failure count exceeded cumulative'
            _msg += ' testcase failure threshold '
            _msg += '(%d)' % self.cumulative_tc_failure_threshold
            self.logger.error(_msg)
            raise KeyboardInterrupt
        if ((self.tc_failure_threshold != 0) and
                (self.__failed_tc_count >= self.tc_failure_threshold)):
            if self.__failed_tc_count_msg:
                raise TCThresholdReached
            _msg = 'Testcases failure for this testsuite count exceeded'
            _msg += ' testcase failure threshold '
            _msg += '(%d)' % self.tc_failure_threshold
            self.logger.error(_msg)
            self.__failed_tc_count_msg = True
            raise TCThresholdReached
        timeout = self.__get_timeout(test)
        rv = self.__are_requirements_matching(self.param_dict, test)
        if rv is False:
            # Below method call is needed in order to get the test case
            # details in the output and to have the skipped test count
            # included in total run count of the test run
            self.result.startTest(test)
            raise SkipTest('Test requirements are not matching')

        def timeout_handler(signum, frame):
            raise TimeOut('Timed out after %s second' % timeout)
        old_handler = signal.signal(signal.SIGALRM, timeout_handler)
        setattr(test, 'old_sigalrm_handler', old_handler)
        signal.alarm(timeout)

    def stopTest(self, test):
        """
        Stop the test
        """
        old_sigalrm_handler = getattr(test, 'old_sigalrm_handler', None)
        if old_sigalrm_handler is not None:
            signal.signal(signal.SIGALRM, old_sigalrm_handler)
            signal.alarm(0)

    def addError(self, test, err):
        """
        Add error
        """
        if isclass(err[0]) and issubclass(err[0], TCThresholdReached):
            return True
        self.__set_test_end_data(test, err)

    def addFailure(self, test, err):
        """
        Add failure
        """
        self.__set_test_end_data(test, err)

    def addSuccess(self, test):
        """
        Add success
        """
        self.__set_test_end_data(test)

    def _cleanup(self):
        self.logger.info('Cleaning up temporary files')
        du = DshUtils()
        root_dir = os.sep
        dirlist = set([os.path.join(root_dir, 'tmp'),
                       os.path.join(root_dir, 'var', 'tmp')])
        # get tmp dir from the environment
        for envname in 'TMPDIR', 'TEMP', 'TMP':
            dirname = os.getenv(envname)
            if dirname:
                dirlist.add(dirname)

        p = re.compile(r'^pbs\.\d+')
        for tmpdir in dirlist:
            # list the contents of each tmp dir and
            # get the file list to be deleted
            self.logger.info('Cleaning up ' + tmpdir + ' dir')
            ftd = []
            files = du.listdir(path=tmpdir)
            bn = os.path.basename
            ftd.extend([f for f in files if bn(f).startswith('PtlPbs')])
            ftd.extend([f for f in files if bn(f).startswith('STDIN')])
            ftd.extend([f for f in files if bn(f).startswith('pbsscrpt')])
            ftd.extend([f for f in files if bn(f).startswith('pbs.conf.')])
            ftd.extend([f for f in files if p.match(bn(f))])
            for f in ftd:
                du.rm(path=f, sudo=True, recursive=True, force=True,
                      level=logging.DEBUG)
        for f in du.tmpfilelist:
            du.rm(path=f, sudo=True, force=True, level=logging.DEBUG)
        del du.tmpfilelist[:]
        tmpdir = tempfile.gettempdir()
        os.chdir(tmpdir)
        tmppath = os.path.join(tmpdir, 'dejagnutemp%s' % os.getpid())
        if du.isdir(path=tmppath):
            du.rm(path=tmppath, recursive=True, sudo=True, force=True,
                  level=logging.DEBUG)

    def begin(self):
        command = sys.argv
        command[0] = os.path.basename(command[0])
        self.logger.info('input command: ' + ' '.join(command))
        self.logger.info('param: ' + str(self.param))
        self.logger.info('ptl version: ' + str(ptl.__version__))
        _m = 'platform: ' + ' '.join(platform.uname()).strip()
        self.logger.info(_m)
        self.logger.info('python version: ' + str(platform.python_version()))
        self.logger.info('user: '******'-' * 80)

        if self.lcov_data is not None:
            self.lcov_utils = LcovUtils(cov_bin=self.lcov_bin,
                                        html_bin=self.genhtml_bin,
                                        cov_out=self.lcov_out,
                                        data_dir=self.lcov_data,
                                        html_nosrc=self.lcov_nosrc,
                                        html_baseurl=self.lcov_baseurl)
            # Initialize coverage analysis
            self.lcov_utils.zero_coverage()
            # The following 'dance' is done due to some oddities on lcov's
            # part, according to this the lcov readme file at
            # http://ltp.sourceforge.net/coverage/lcov/readme.php that reads:
            #
            # Note that this step only works after the application has
            # been started and stopped at least once. Otherwise lcov will
            # abort with an error mentioning that there are no data/.gcda
            # files.
            self.lcov_utils.initialize_coverage(name='PTLTestCov')
            PBSInitServices().restart()
        self._cleanup()

    def finalize(self, result):
        if self.lcov_data is not None:
            # See note above that briefly explains the 'dance' needed to get
            # reliable coverage data
            PBSInitServices().restart()
            self.lcov_utils.capture_coverage(name='PTLTestCov')
            exclude = ['"*work/gSOAP/*"', '"*/pbs/doc/*"', 'lex.yy.c',
                       'pbs_ifl_wrap.c', 'usr/include/*', 'unsupported/*']
            self.lcov_utils.merge_coverage_traces(name='PTLTestCov',
                                                  exclude=exclude)
            self.lcov_utils.generate_html()
            self.lcov_utils.change_baseurl()
            self.logger.info('\n'.join(self.lcov_utils.summarize_coverage()))
        self._cleanup()
Beispiel #5
0
class PTLTestRunner(Plugin):
    """
    PTL Test Runner Plugin
    """
    name = 'PTLTestRunner'
    score = sys.maxsize - 4
    logger = logging.getLogger(__name__)
    timeout = None

    def __init__(self):
        Plugin.__init__(self)
        self.param = None
        self.repeat_count = 1
        self.repeat_delay = 0
        self.use_cur_setup = False
        self.lcov_bin = None
        self.lcov_data = None
        self.lcov_out = None
        self.lcov_utils = None
        self.lcov_nosrc = None
        self.lcov_baseurl = None
        self.genhtml_bin = None
        self.config = None
        self.result = None
        self.tc_failure_threshold = None
        self.cumulative_tc_failure_threshold = None
        self.__failed_tc_count = 0
        self.__tf_count = 0
        self.__failed_tc_count_msg = False
        self._test_marker = 'test_'
        self.hardware_report_timer = None

    def options(self, parser, env):
        """
        Register command line options
        """
        pass

    def set_data(self, paramfile, testparam, repeat_count, repeat_delay,
                 lcov_bin, lcov_data, lcov_out, genhtml_bin, lcov_nosrc,
                 lcov_baseurl, tc_failure_threshold,
                 cumulative_tc_failure_threshold, use_cur_setup):
        if paramfile is not None:
            _pf = open(paramfile, 'r')
            _params_from_file = _pf.readlines()
            _pf.close()
            _nparams = []
            for l in range(len(_params_from_file)):
                if _params_from_file[l].startswith('#'):
                    continue
                else:
                    _nparams.append(_params_from_file[l])
            _f = ','.join([l.strip('\r\n') for l in _nparams])
            if testparam is not None:
                testparam += ',' + _f
            else:
                testparam = _f
        self.param = testparam
        self.repeat_count = repeat_count
        self.repeat_delay = repeat_delay
        self.use_cur_setup = use_cur_setup
        self.lcov_bin = lcov_bin
        self.lcov_data = lcov_data
        self.lcov_out = lcov_out
        self.genhtml_bin = genhtml_bin
        self.lcov_nosrc = lcov_nosrc
        self.lcov_baseurl = lcov_baseurl
        self.tc_failure_threshold = tc_failure_threshold
        self.cumulative_tc_failure_threshold = cumulative_tc_failure_threshold

    def configure(self, options, config):
        """
        Configure the plugin and system, based on selected options
        """
        self.config = config
        self.enabled = True
        self.param_dict = self.__get_param_dictionary()

    def prepareTestRunner(self, runner):
        """
        Prepare test runner
        """
        return PtlTextTestRunner(verbosity=3,
                                 config=self.config,
                                 repeat_count=self.repeat_count,
                                 repeat_delay=self.repeat_delay)

    def prepareTestResult(self, result):
        """
        Prepare test result
        """
        self.result = result

    def startContext(self, context):
        context.param = self.param
        context.use_cur_setup = self.use_cur_setup
        context.start_time = datetime.datetime.now()
        if isclass(context) and issubclass(context, unittest.TestCase):
            self.result.logger.info(self.result.separator1)
            self.result.logger.info('suite name: ' + context.__name__)
            doc = context.__doc__
            if doc is not None:
                self.result.logger.info('suite docstring: \n' + doc + '\n')
            self.result.logger.info(self.result.separator1)
            self.__failed_tc_count = 0
            self.__failed_tc_count_msg = False

    def __get_timeout(self, test):
        _test = None
        if hasattr(test, 'test'):
            _test = test.test
        elif hasattr(test, 'context'):
            _test = test.context
        if _test is None:
            return MINIMUM_TESTCASE_TIMEOUT
        dflt_timeout = int(
            getattr(_test, 'conf', {}).get('default-testcase-timeout',
                                           MINIMUM_TESTCASE_TIMEOUT))
        tc_timeout = int(
            getattr(
                getattr(_test, getattr(_test, '_testMethodName', ''), None),
                TIMEOUT_KEY, 0))
        return max([dflt_timeout, tc_timeout])

    def __set_test_end_data(self, test, err=None):
        if self.hardware_report_timer is not None:
            self.hardware_report_timer.cancel()
        if not hasattr(test, 'start_time'):
            test = test.context
        if err is not None:
            is_skip = issubclass(err[0], SkipTest)
            is_tctr = issubclass(err[0], TCThresholdReached)
            if not (is_skip or is_tctr):
                self.__failed_tc_count += 1
                self.__tf_count += 1
            try:
                test.err_in_string = self.result._exc_info_to_string(err, test)
            except BaseException:
                etype, value, tb = err
                test.err_in_string = ''.join(format_exception(
                    etype, value, tb))
        else:
            test.err_in_string = 'None'
        test.end_time = datetime.datetime.now()
        test.duration = test.end_time - test.start_time
        test.captured_logs = self.result.handler.get_logs()

    def __get_param_dictionary(self):
        """
        Method to convert data in param into dictionary of cluster
        information
        """
        tparam_contents = {}
        nomomlist = []
        shortname = (socket.gethostname()).split('.', 1)[0]
        for key in ['servers', 'moms', 'comms', 'clients', 'nomom']:
            tparam_contents[key] = []
        tparam_contents['no_mom_on_server'] = False
        tparam_contents['no_comm_on_server'] = False
        tparam_contents['no_comm_on_mom'] = False
        if self.param is not None:
            for h in self.param.split(','):
                if '=' in h:
                    k, v = h.split('=', 1)
                    hosts = [x.split('@')[0] for x in v.split(':')]
                    if (k == 'server' or k == 'servers'):
                        tparam_contents['servers'].extend(hosts)
                    elif (k == 'mom' or k == 'moms'):
                        tparam_contents['moms'].extend(hosts)
                    elif k == 'comms':
                        tparam_contents['comms'] = hosts
                    elif k == 'client':
                        tparam_contents['clients'] = hosts
                    elif k == 'nomom':
                        nomomlist = hosts
                    elif k == 'no_mom_on_server':
                        tparam_contents['no_mom_on_server'] = v
                    elif k == 'no_comm_on_mom':
                        tparam_contents['no_comm_on_mom'] = v
        for pkey in ['servers', 'moms', 'comms', 'clients']:
            if not tparam_contents[pkey]:
                tparam_contents[pkey] = set([shortname])
            else:
                tparam_contents[pkey] = set(tparam_contents[pkey])
        if nomomlist:
            tparam_contents['nomom'] = set(nomomlist)
        return tparam_contents

    @staticmethod
    def __are_requirements_matching(param_dic=None, test=None):
        """
        Validates test requirements against test cluster information
        returns True on match or error message otherwise None

        :param param_dic: dictionary of cluster information from data passed
                          to param list
        :param_dic type: dic
        :param test: test object
        :test type: object

        :returns True or error message or None
        """
        logger = logging.getLogger(__name__)
        ts_requirements = {}
        tc_requirements = {}
        param_count = {}
        _servers = set(param_dic['servers'])
        _moms = set(param_dic['moms'])
        _comms = set(param_dic['comms'])
        _nomom = set(param_dic['nomom'])
        _no_mom_on_server = param_dic['no_mom_on_server']
        _no_comm_on_mom = param_dic['no_comm_on_mom']
        _no_comm_on_server = param_dic['no_comm_on_server']
        shortname = (socket.gethostname()).split('.', 1)[0]
        if test is None:
            return None
        test_name = getattr(test.test, '_testMethodName', None)
        if test_name is not None:
            method = getattr(test.test, test_name, None)
        if method is not None:
            tc_requirements = getattr(method, REQUIREMENTS_KEY, {})
            cls = method.__self__.__class__
            ts_requirements = getattr(cls, REQUIREMENTS_KEY, {})
        if not tc_requirements:
            if not ts_requirements:
                return None
        eff_tc_req = get_effective_reqs(ts_requirements, tc_requirements)
        setattr(test.test, 'requirements', eff_tc_req)
        for key in ['servers', 'moms', 'comms', 'clients']:
            param_count['num_' + key] = len(param_dic[key])
        for pk in param_count:
            if param_count[pk] < eff_tc_req[pk]:
                _msg = 'available ' + pk + " ("
                _msg += str(param_count[pk]) + ") is less than required " + pk
                _msg += " (" + str(eff_tc_req[pk]) + ")"
                logger.error(_msg)
                return _msg

        if hasattr(test, 'test'):
            _test = test.test
        elif hasattr(test, 'context'):
            _test = test.context
        else:
            return None

        name = 'moms'
        if (hasattr(_test, name) and (getattr(_test, name, None) is not None)):
            for mc in getattr(_test, name).values():
                platform = mc.platform
                if platform not in ['linux', 'shasta', 'cray'
                                    ] and mc.hostname in _moms:
                    _moms.remove(mc.hostname)
        for hostname in _moms:
            si = SystemInfo()
            si.get_system_info(hostname)
            available_sys_ram = getattr(si, 'system_ram', None)
            if available_sys_ram is None:
                _msg = 'failed to get ram info on host: ' + hostname
                logger.error(_msg)
                return _msg
            elif eff_tc_req['min_mom_ram'] >= available_sys_ram:
                _msg = hostname + ': available ram (' + str(available_sys_ram)
                _msg += ') is less than the minimum required ram ('
                _msg += str(eff_tc_req['min_mom_ram'])
                _msg += ') for test execution'
                logger.error(_msg)
                return _msg
            available_sys_disk = getattr(si, 'system_disk', None)
            if available_sys_disk is None:
                _msg = 'failed to get disk info on host: ' + hostname
                logger.error(_msg)
                return _msg
            elif eff_tc_req['min_mom_disk'] >= available_sys_disk:
                _msg = hostname + ': available disk space ('
                _msg += str(available_sys_disk)
                _msg += ') is less than the minimum required disk space ('
                _msg += str(eff_tc_req['min_mom_disk'])
                _msg += ') for test execution'
                logger.error(_msg)
                return _msg
        for hostname in param_dic['servers']:
            si = SystemInfo()
            si.get_system_info(hostname)
            available_sys_ram = getattr(si, 'system_ram', None)
            if available_sys_ram is None:
                _msg = 'failed to get ram info on host: ' + hostname
                logger.error(_msg)
                return _msg
            elif eff_tc_req['min_server_ram'] >= available_sys_ram:
                _msg = hostname + ': available ram (' + str(available_sys_ram)
                _msg += ') is less than the minimum required ram ('
                _msg += str(eff_tc_req['min_server_ram'])
                _msg += ') for test execution'
                logger.error(_msg)
                return _msg
            available_sys_disk = getattr(si, 'system_disk', None)
            if available_sys_disk is None:
                _msg = 'failed to get disk info on host: ' + hostname
                logger.error(_msg)
                return _msg
            elif eff_tc_req['min_server_disk'] >= available_sys_disk:
                _msg = hostname + ': available disk space ('
                _msg += str(available_sys_disk)
                _msg += ') is less than the minimum required disk space ('
                _msg += str(eff_tc_req['min_server_disk'])
                _msg += ') for test execution'
                logger.error(_msg)
                return _msg
        if _moms & _servers:
            if eff_tc_req['no_mom_on_server'] or \
               (_nomom - _servers) or \
               _no_mom_on_server:
                _msg = 'no mom on server'
                logger.error(_msg)
                return _msg
        if _comms & _servers:
            if eff_tc_req['no_comm_on_server'] or _no_comm_on_server:
                return False
                _msg = 'no comm on server'
                logger.error(_msg)
                return _msg
        comm_mom_list = _moms & _comms
        if comm_mom_list and shortname in comm_mom_list:
            # Excluding the server hostname for flag 'no_comm_on_mom'
            comm_mom_list.remove(shortname)
        if comm_mom_list:
            if eff_tc_req['no_comm_on_mom']:
                _msg = 'no comm on mom'
                logger.error(_msg)
                return _msg
        else:
            if not eff_tc_req['no_comm_on_mom']:
                _msg = 'no comm on server'
                logger.error(_msg)
                return _msg

    def check_hardware_status_and_core_files(self, test):
        """
        function checks hardware status and core files
        every 5 minutes
        """
        du = DshUtils()
        systems = list(self.param_dict['servers'])
        systems.extend(self.param_dict['moms'])
        systems.extend(self.param_dict['comms'])
        systems = list(set(systems))

        if hasattr(test, 'test'):
            _test = test.test
        elif hasattr(test, 'context'):
            _test = test.context
        else:
            return None

        for name in ['servers', 'moms', 'comms', 'clients']:
            mlist = None
            if (hasattr(_test, name)
                    and (getattr(_test, name, None) is not None)):
                mlist = getattr(_test, name).values()
            if mlist:
                for mc in mlist:
                    platform = mc.platform
                    if ((platform not in ['linux', 'shasta', 'cray'])
                            and (mc.hostname in systems)):
                        systems.remove(mc.hostname)

        self.hardware_report_timer = Timer(
            300, self.check_hardware_status_and_core_files, args=(test, ))
        self.hardware_report_timer.start()

        for hostname in systems:
            hr = SystemInfo()
            hr.get_system_info(hostname)
            # monitors disk
            used_disk_percent = getattr(hr, 'system_disk_used_percent', None)
            if used_disk_percent is None:
                _msg = hostname
                _msg += ": unable to get disk info"
                self.hardware_report_timer.cancel()
                raise SkipTest(_msg)
            elif 70 <= used_disk_percent < 95:
                _msg = hostname + ": disk usage is at "
                _msg += str(used_disk_percent) + "%"
                _msg += ", disk cleanup is recommended."
                self.logger.warning(_msg)
            elif used_disk_percent >= 95:
                _msg = hostname + ":disk usage > 95%, skipping the test(s)"
                self.hardware_report_timer.cancel()
                raise SkipTest(_msg)
            # checks for core files
            pbs_conf = du.parse_pbs_config(hostname)
            mom_priv_path = os.path.join(pbs_conf["PBS_HOME"], "mom_priv")
            if du.isdir(hostname=hostname, path=mom_priv_path):
                mom_priv_files = du.listdir(hostname=hostname,
                                            path=mom_priv_path,
                                            sudo=True,
                                            fullpath=False)
                if fnmatch.filter(mom_priv_files, "core*"):
                    _msg = hostname + ": core files found in "
                    _msg += mom_priv_path
                    self.logger.warning(_msg)
            server_priv_path = os.path.join(pbs_conf["PBS_HOME"],
                                            "server_priv")
            if du.isdir(hostname=hostname, path=server_priv_path):
                server_priv_files = du.listdir(hostname=hostname,
                                               path=server_priv_path,
                                               sudo=True,
                                               fullpath=False)
                if fnmatch.filter(server_priv_files, "core*"):
                    _msg = hostname + ": core files found in "
                    _msg += server_priv_path
                    self.logger.warning(_msg)
            sched_priv_path = os.path.join(pbs_conf["PBS_HOME"], "sched_priv")
            if du.isdir(hostname=hostname, path=sched_priv_path):
                sched_priv_files = du.listdir(hostname=hostname,
                                              path=sched_priv_path,
                                              sudo=True,
                                              fullpath=False)
                if fnmatch.filter(sched_priv_files, "core*"):
                    _msg = hostname + ": core files found in "
                    _msg += sched_priv_path
                    self.logger.warning(_msg)
            for u in PBS_ALL_USERS:
                user_home_files = du.listdir(hostname=hostname,
                                             path=u.home,
                                             sudo=True,
                                             fullpath=False,
                                             runas=u.name)
                if user_home_files and fnmatch.filter(user_home_files,
                                                      "core*"):
                    _msg = hostname + ": user-" + str(u)
                    _msg += ": core files found in "
                    self.logger.warning(_msg + u.home)

    def startTest(self, test):
        """
        Start the test
        """
        if ((self.cumulative_tc_failure_threshold != 0)
                and (self.__tf_count >= self.cumulative_tc_failure_threshold)):
            _msg = 'Total testcases failure count exceeded cumulative'
            _msg += ' testcase failure threshold '
            _msg += '(%d)' % self.cumulative_tc_failure_threshold
            self.logger.error(_msg)
            raise KeyboardInterrupt
        if ((self.tc_failure_threshold != 0)
                and (self.__failed_tc_count >= self.tc_failure_threshold)):
            if self.__failed_tc_count_msg:
                raise TCThresholdReached
            _msg = 'Testcases failure for this testsuite count exceeded'
            _msg += ' testcase failure threshold '
            _msg += '(%d)' % self.tc_failure_threshold
            self.logger.error(_msg)
            self.__failed_tc_count_msg = True
            raise TCThresholdReached
        rv = None
        rv = self.__are_requirements_matching(self.param_dict, test)
        if rv is not None:
            # Below method call is needed in order to get the test case
            # details in the output and to have the skipped test count
            # included in total run count of the test run
            self.result.startTest(test)
            raise SkipTest(rv)
        # function report hardware status and core files
        self.check_hardware_status_and_core_files(test)

        def timeout_handler(signum, frame):
            raise TimeOut('Timed out after %s second' % timeout)

        if PTLTestRunner.timeout is None:
            timeout = self.__get_timeout(test)
            old_handler = signal.signal(signal.SIGALRM, timeout_handler)
            setattr(test, 'old_sigalrm_handler', old_handler)
            signal.alarm(timeout)

    def stopTest(self, test):
        """
        Stop the test
        """
        old_sigalrm_handler = getattr(test, 'old_sigalrm_handler', None)
        if old_sigalrm_handler is not None:
            signal.signal(signal.SIGALRM, old_sigalrm_handler)
            signal.alarm(0)

    def addError(self, test, err):
        """
        Add error
        """
        if isclass(err[0]) and issubclass(err[0], TCThresholdReached):
            return True
        self.__set_test_end_data(test, err)

    def addFailure(self, test, err):
        """
        Add failure
        """
        self.__set_test_end_data(test, err)

    def addSuccess(self, test):
        """
        Add success
        """
        self.__set_test_end_data(test)

    def _cleanup(self):
        self.logger.info('Cleaning up temporary files')
        du = DshUtils()
        root_dir = os.sep
        dirlist = set([
            os.path.join(root_dir, 'tmp'),
            os.path.join(root_dir, 'var', 'tmp')
        ])
        # get tmp dir from the environment
        for envname in 'TMPDIR', 'TEMP', 'TMP':
            dirname = os.getenv(envname)
            if dirname:
                dirlist.add(dirname)

        p = re.compile(r'^pbs\.\d+')
        for tmpdir in dirlist:
            # list the contents of each tmp dir and
            # get the file list to be deleted
            self.logger.info('Cleaning up ' + tmpdir + ' dir')
            ftd = []
            files = du.listdir(path=tmpdir)
            bn = os.path.basename
            ftd.extend([f for f in files if bn(f).startswith('PtlPbs')])
            ftd.extend([f for f in files if bn(f).startswith('STDIN')])
            ftd.extend([f for f in files if bn(f).startswith('pbsscrpt')])
            ftd.extend([f for f in files if bn(f).startswith('pbs.conf.')])
            ftd.extend([f for f in files if p.match(bn(f))])
            for f in ftd:
                du.rm(path=f,
                      sudo=True,
                      recursive=True,
                      force=True,
                      level=logging.DEBUG)
        for f in du.tmpfilelist:
            du.rm(path=f, sudo=True, force=True, level=logging.DEBUG)
        del du.tmpfilelist[:]
        tmpdir = tempfile.gettempdir()
        os.chdir(tmpdir)
        tmppath = os.path.join(tmpdir, 'dejagnutemp%s' % os.getpid())
        if du.isdir(path=tmppath):
            du.rm(path=tmppath,
                  recursive=True,
                  sudo=True,
                  force=True,
                  level=logging.DEBUG)

    def begin(self):
        command = sys.argv
        command[0] = os.path.basename(command[0])
        self.logger.info('input command: ' + ' '.join(command))
        self.logger.info('param: ' + str(self.param))
        self.logger.info('ptl version: ' + str(ptl.__version__))
        _m = 'platform: ' + ' '.join(platform.uname()).strip()
        self.logger.info(_m)
        self.logger.info('python version: ' + str(platform.python_version()))
        self.logger.info('user: '******'-' * 80)

        if self.lcov_data is not None:
            self.lcov_utils = LcovUtils(cov_bin=self.lcov_bin,
                                        html_bin=self.genhtml_bin,
                                        cov_out=self.lcov_out,
                                        data_dir=self.lcov_data,
                                        html_nosrc=self.lcov_nosrc,
                                        html_baseurl=self.lcov_baseurl)
            # Initialize coverage analysis
            self.lcov_utils.zero_coverage()
            # The following 'dance' is done due to some oddities on lcov's
            # part, according to this the lcov readme file at
            # http://ltp.sourceforge.net/coverage/lcov/readme.php that reads:
            #
            # Note that this step only works after the application has
            # been started and stopped at least once. Otherwise lcov will
            # abort with an error mentioning that there are no data/.gcda
            # files.
            self.lcov_utils.initialize_coverage(name='PTLTestCov')
            PBSInitServices().restart()
        self._cleanup()

    def finalize(self, result):
        if self.lcov_data is not None:
            # See note above that briefly explains the 'dance' needed to get
            # reliable coverage data
            PBSInitServices().restart()
            self.lcov_utils.capture_coverage(name='PTLTestCov')
            exclude = [
                '"*work/gSOAP/*"', '"*/pbs/doc/*"', 'lex.yy.c',
                'pbs_ifl_wrap.c', 'usr/include/*', 'unsupported/*'
            ]
            self.lcov_utils.merge_coverage_traces(name='PTLTestCov',
                                                  exclude=exclude)
            self.lcov_utils.generate_html()
            self.lcov_utils.change_baseurl()
            self.logger.info('\n'.join(self.lcov_utils.summarize_coverage()))
        self._cleanup()
class PTLTestRunner(Plugin):

    """
    PTL Test Runner Plugin
    """
    name = 'PTLTestRunner'
    score = sys.maxint - 4
    logger = logging.getLogger(__name__)

    def __init__(self):
        Plugin.__init__(self)
        self.param = None
        self.lcov_bin = None
        self.lcov_data = None
        self.lcov_out = None
        self.lcov_utils = None
        self.lcov_nosrc = None
        self.lcov_baseurl = None
        self.genhtml_bin = None
        self.config = None
        self.result = None
        self.tc_failure_threshold = None
        self.cumulative_tc_failure_threshold = None
        self.__failed_tc_count = 0
        self.__tf_count = 0
        self.__failed_tc_count_msg = False

    def options(self, parser, env):
        """
        Register command line options
        """
        pass

    def set_data(self, paramfile, testparam,
                 lcov_bin, lcov_data, lcov_out, genhtml_bin, lcov_nosrc,
                 lcov_baseurl, tc_failure_threshold,
                 cumulative_tc_failure_threshold):
        if paramfile is not None:
            _pf = open(paramfile, 'r')
            _params_from_file = _pf.readlines()
            _pf.close()
            _nparams = []
            for l in range(len(_params_from_file)):
                if _params_from_file[l].startswith('#'):
                    continue
                else:
                    _nparams.append(_params_from_file[l])
            _f = ','.join(map(lambda l: l.strip('\r\n'), _nparams))
            if testparam is not None:
                testparam += ',' + _f
            else:
                testparam = _f
        self.param = testparam
        self.lcov_bin = lcov_bin
        self.lcov_data = lcov_data
        self.lcov_out = lcov_out
        self.genhtml_bin = genhtml_bin
        self.lcov_nosrc = lcov_nosrc
        self.lcov_baseurl = lcov_baseurl
        self.tc_failure_threshold = tc_failure_threshold
        self.cumulative_tc_failure_threshold = cumulative_tc_failure_threshold

    def configure(self, options, config):
        """
        Configure the plugin and system, based on selected options
        """
        self.config = config
        self.enabled = True

    def prepareTestRunner(self, runner):
        """
        Prepare test runner
        """
        return PtlTestRunner(verbosity=3, config=self.config)

    def prepareTestResult(self, result):
        """
        Prepare test result
        """
        self.result = result

    def startContext(self, context):
        context.param = self.param
        context.start_time = datetime.datetime.now()
        if isclass(context) and issubclass(context, PBSTestSuite):
            self.result.logger.info(self.result.separator1)
            self.result.logger.info('suite name: ' + context.__name__)
            doc = context.__doc__
            if doc is not None:
                self.result.logger.info('suite docstring: \n' + doc + '\n')
            self.result.logger.info(self.result.separator1)
            self.__failed_tc_count = 0
            self.__failed_tc_count_msg = False

    def __get_timeout(self, test):
        try:
            method = getattr(test.test, getattr(test.test, '_testMethodName'))
            return getattr(method, TIMEOUT_KEY)
        except:
            if hasattr(test, 'test'):
                __conf = getattr(test.test, 'conf')
            else:
                __conf = getattr(test.context, 'conf')
            return __conf['default_testcase_timeout']

    def __set_test_end_data(self, test, err=None):
        if not hasattr(test, 'start_time'):
            test = test.context
        if err is not None:
            is_skip = issubclass(err[0], SkipTest)
            is_tctr = issubclass(err[0], TCThresholdReached)
            if not (is_skip or is_tctr):
                self.__failed_tc_count += 1
                self.__tf_count += 1
            try:
                test.err_in_string = self.result._exc_info_to_string(err,
                                                                     test)
            except:
                etype, value, tb = err
                test.err_in_string = ''.join(format_exception(etype, value,
                                                              tb))
        else:
            test.err_in_string = 'None'
        test.end_time = datetime.datetime.now()
        test.duration = test.end_time - test.start_time
        test.captured_logs = self.result.handler.get_logs()

    def startTest(self, test):
        """
        Start the test
        """
        if ((self.cumulative_tc_failure_threshold != 0) and
                (self.__tf_count >= self.cumulative_tc_failure_threshold)):
            _msg = 'Total testcases failure count exceeded cumulative'
            _msg += ' testcase failure threshold '
            _msg += '(%d)' % self.cumulative_tc_failure_threshold
            self.logger.error(_msg)
            raise KeyboardInterrupt
        if ((self.tc_failure_threshold != 0) and
                (self.__failed_tc_count >= self.tc_failure_threshold)):
            if self.__failed_tc_count_msg:
                raise TCThresholdReached
            _msg = 'Testcases failure for this testsuite count exceeded'
            _msg += ' testcase failure threshold '
            _msg += '(%d)' % self.tc_failure_threshold
            self.logger.error(_msg)
            self.__failed_tc_count_msg = True
            raise TCThresholdReached
        timeout = self.__get_timeout(test)

        def timeout_handler(signum, frame):
            raise TimeOut('Timed out after %s second' % timeout)
        old_handler = signal.signal(signal.SIGALRM, timeout_handler)
        setattr(test, 'old_sigalrm_handler', old_handler)
        signal.alarm(timeout)

    def stopTest(self, test):
        """
        Stop the test
        """
        old_sigalrm_handler = getattr(test, 'old_sigalrm_handler', None)
        if old_sigalrm_handler is not None:
            signal.signal(signal.SIGALRM, old_sigalrm_handler)
            signal.alarm(0)

    def addError(self, test, err):
        """
        Add error
        """
        if isclass(err[0]) and issubclass(err[0], TCThresholdReached):
            return True
        self.__set_test_end_data(test, err)

    def addFailure(self, test, err):
        """
        Add failure
        """
        self.__set_test_end_data(test, err)

    def addSuccess(self, test):
        """
        Add success
        """
        self.__set_test_end_data(test)

    def _cleanup(self):
        self.logger.info('Cleaning up temporary files')
        du = DshUtils()
        tmpdir = tempfile.gettempdir()
        ftd = []
        if tmpdir:
            files = du.listdir(path=tmpdir)
            bn = os.path.basename
            ftd.extend([f for f in files if bn(f).startswith('PtlPbs')])
            ftd.extend([f for f in files if bn(f).startswith('STDIN')])
            for f in ftd:
                du.rm(path=f, sudo=True, recursive=True, force=True,
                      level=logging.DEBUG)
        os.chdir(tmpdir)
        tmppath = os.path.join(tmpdir, 'dejagnutemp%s' % os.getpid())
        if du.isdir(path=tmppath):
            du.rm(path=tmppath, recursive=True, sudo=True, force=True,
                  level=logging.DEBUG)

    def begin(self):
        command = sys.argv
        command[0] = os.path.basename(command[0])
        self.logger.info('input command: ' + ' '.join(command))
        self.logger.info('param: ' + str(self.param))
        self.logger.info('ptl version: ' + str(ptl.__version__))
        _m = 'platform: ' + ' '.join(platform.uname()).strip()
        self.logger.info(_m)
        self.logger.info('python version: ' + str(platform.python_version()))
        self.logger.info('user: '******'-' * 80)

        if self.lcov_data is not None:
            self.lcov_utils = LcovUtils(cov_bin=self.lcov_bin,
                                        html_bin=self.genhtml_bin,
                                        cov_out=self.lcov_out,
                                        data_dir=self.lcov_data,
                                        html_nosrc=self.lcov_nosrc,
                                        html_baseurl=self.lcov_baseurl)
            # Initialize coverage analysis
            self.lcov_utils.zero_coverage()
            # The following 'dance' is done due to some oddities on lcov's
            # part, according to this the lcov readme file at
            # http://ltp.sourceforge.net/coverage/lcov/readme.php that reads:
            #
            # Note that this step only works after the application has
            # been started and stopped at least once. Otherwise lcov will
            # abort with an error mentioning that there are no data/.gcda
            # files.
            self.lcov_utils.initialize_coverage(name='PTLTestCov')
            PBSInitServices().restart()
        self._cleanup()

    def finalize(self, result):
        if self.lcov_data is not None:
            # See note above that briefly explains the 'dance' needed to get
            # reliable coverage data
            PBSInitServices().restart()
            self.lcov_utils.capture_coverage(name='PTLTestCov')
            exclude = ['"*work/gSOAP/*"', '"*/pbs/doc/*"', 'lex.yy.c',
                       'pbs_ifl_wrap.c', 'usr/include/*', 'unsupported/*']
            self.lcov_utils.merge_coverage_traces(name='PTLTestCov',
                                                  exclude=exclude)
            self.lcov_utils.generate_html()
            self.lcov_utils.change_baseurl()
            self.logger.info('\n'.join(self.lcov_utils.summarize_coverage()))
        self._cleanup()
Beispiel #7
0
class PTLTestRunner(Plugin):
    """
    PTL Test Runner Plugin
    """
    name = 'PTLTestRunner'
    score = sys.maxint - 4
    logger = logging.getLogger(__name__)

    def __init__(self):
        Plugin.__init__(self)
        self.param = None
        self.lcov_bin = None
        self.lcov_data = None
        self.lcov_out = None
        self.lcov_utils = None
        self.lcov_nosrc = None
        self.lcov_baseurl = None
        self.genhtml_bin = None
        self.config = None
        self.result = None
        self.tc_failure_threshold = None
        self.cumulative_tc_failure_threshold = None
        self.__failed_tc_count = 0
        self.__tf_count = 0
        self.__failed_tc_count_msg = False
        self._test_marker = 'test_'

    def options(self, parser, env):
        """
        Register command line options
        """
        pass

    def set_data(self, paramfile, testparam, lcov_bin, lcov_data, lcov_out,
                 genhtml_bin, lcov_nosrc, lcov_baseurl, tc_failure_threshold,
                 cumulative_tc_failure_threshold):
        if paramfile is not None:
            _pf = open(paramfile, 'r')
            _params_from_file = _pf.readlines()
            _pf.close()
            _nparams = []
            for l in range(len(_params_from_file)):
                if _params_from_file[l].startswith('#'):
                    continue
                else:
                    _nparams.append(_params_from_file[l])
            _f = ','.join(map(lambda l: l.strip('\r\n'), _nparams))
            if testparam is not None:
                testparam += ',' + _f
            else:
                testparam = _f
        self.param = testparam
        self.lcov_bin = lcov_bin
        self.lcov_data = lcov_data
        self.lcov_out = lcov_out
        self.genhtml_bin = genhtml_bin
        self.lcov_nosrc = lcov_nosrc
        self.lcov_baseurl = lcov_baseurl
        self.tc_failure_threshold = tc_failure_threshold
        self.cumulative_tc_failure_threshold = cumulative_tc_failure_threshold

    def configure(self, options, config):
        """
        Configure the plugin and system, based on selected options
        """
        self.config = config
        self.enabled = True
        self.param_dict = self.__get_param_dictionary()

    def prepareTestRunner(self, runner):
        """
        Prepare test runner
        """
        return PtlTestRunner(verbosity=3, config=self.config)

    def prepareTestResult(self, result):
        """
        Prepare test result
        """
        self.result = result

    def startContext(self, context):
        context.param = self.param
        context.start_time = datetime.datetime.now()
        if isclass(context) and issubclass(context, PBSTestSuite):
            self.result.logger.info(self.result.separator1)
            self.result.logger.info('suite name: ' + context.__name__)
            doc = context.__doc__
            if doc is not None:
                self.result.logger.info('suite docstring: \n' + doc + '\n')
            self.result.logger.info(self.result.separator1)
            self.__failed_tc_count = 0
            self.__failed_tc_count_msg = False

    def __get_timeout(self, test):
        try:
            method = getattr(test.test, getattr(test.test, '_testMethodName'))
            return getattr(method, TIMEOUT_KEY)
        except AttributeError:
            testcase_timeout = MINIMUM_TESTCASE_TIMEOUT
            if hasattr(test, 'test'):
                if hasattr(test.test, 'conf'):
                    __conf = getattr(test.test, 'conf')
                    testcase_timeout = __conf['default_testcase_timeout']
            elif hasattr(test, 'context'):
                if hasattr(test.context, 'conf'):
                    __conf = getattr(test.context, 'conf')
                    testcase_timeout = __conf['default_testcase_timeout']
            return testcase_timeout

    def __set_test_end_data(self, test, err=None):
        if not hasattr(test, 'start_time'):
            test = test.context
        if err is not None:
            is_skip = issubclass(err[0], SkipTest)
            is_tctr = issubclass(err[0], TCThresholdReached)
            if not (is_skip or is_tctr):
                self.__failed_tc_count += 1
                self.__tf_count += 1
            try:
                test.err_in_string = self.result._exc_info_to_string(err, test)
            except:
                etype, value, tb = err
                test.err_in_string = ''.join(format_exception(
                    etype, value, tb))
        else:
            test.err_in_string = 'None'
        test.end_time = datetime.datetime.now()
        test.duration = test.end_time - test.start_time
        test.captured_logs = self.result.handler.get_logs()

    def __get_param_dictionary(self):
        """
        Method to convert data in param into dictionary of cluster
        information
        """
        tparam_contents = {}
        nomomlist = []
        shortname = (socket.gethostname()).split('.', 1)[0]
        for key in ['servers', 'moms', 'comms', 'clients']:
            tparam_contents[key] = []
        if self.param is not None:
            for h in self.param.split(','):
                if '=' in h:
                    k, v = h.split('=', 1)
                    if (k == 'server' or k == 'servers'):
                        tparam_contents['servers'].extend(v.split(':'))
                    elif (k == 'mom' or k == 'moms'):
                        tparam_contents['moms'].extend(v.split(':'))
                    elif k == 'comms':
                        tparam_contents['comms'] = v.split(':')
                    elif k == 'client':
                        tparam_contents['clients'] = v.split(':')
                    elif k == 'nomom':
                        nomomlist = v.split(':')
        for pkey in ['servers', 'moms', 'comms', 'clients']:
            if not tparam_contents[pkey]:
                tparam_contents[pkey] = set([shortname])
            else:
                tparam_contents[pkey] = set(tparam_contents[pkey])
        if nomomlist:
            tparam_contents['moms'] -= set(nomomlist)
        return tparam_contents

    @staticmethod
    def __are_requirements_matching(param_dic=None, test=None):
        """
        Validates test requirements against test cluster information
        returns True on match or False otherwise None

        :param param_dic: dictionary of cluster information from data passed
                          to param list
        :param_dic type: dic
        :param test: test object
        :test type: object

        :returns True or False or None
        """
        ts_requirements = {}
        tc_requirements = {}
        param_count = {}
        shortname = (socket.gethostname()).split('.', 1)[0]
        if test is None:
            return None
        test_name = getattr(test.test, '_testMethodName', None)
        if test_name is not None:
            method = getattr(test.test, test_name, None)
        if method is not None:
            tc_requirements = getattr(method, REQUIREMENTS_KEY, {})
            cls = method.im_class
            ts_requirements = getattr(cls, REQUIREMENTS_KEY, {})
        if not tc_requirements:
            if not ts_requirements:
                return None
        eff_tc_req = get_effective_reqs(ts_requirements, tc_requirements)
        setattr(test.test, 'requirements', eff_tc_req)
        for key in ['servers', 'moms', 'comms', 'clients']:
            param_count['num_' + key] = len(param_dic[key])
        for pk in param_count:
            if param_count[pk] < eff_tc_req[pk]:
                return False
        if set(param_dic['moms']) & set(param_dic['servers']):
            if eff_tc_req['no_mom_on_server']:
                return False
        else:
            if not eff_tc_req['no_mom_on_server']:
                return False
        if set(param_dic['comms']) & set(param_dic['servers']):
            if eff_tc_req['no_comm_on_server']:
                return False
        else:
            if not eff_tc_req['no_comm_on_server']:
                return False
        comm_mom_list = set(param_dic['moms']) & set(param_dic['comms'])
        if comm_mom_list and shortname in comm_mom_list:
            # Excluding the server hostname for flag 'no_comm_on_mom'
            comm_mom_list.remove(shortname)
        if comm_mom_list:
            if eff_tc_req['no_comm_on_mom']:
                return False
        else:
            if not eff_tc_req['no_comm_on_mom']:
                return False

    def startTest(self, test):
        """
        Start the test
        """
        if ((self.cumulative_tc_failure_threshold != 0)
                and (self.__tf_count >= self.cumulative_tc_failure_threshold)):
            _msg = 'Total testcases failure count exceeded cumulative'
            _msg += ' testcase failure threshold '
            _msg += '(%d)' % self.cumulative_tc_failure_threshold
            self.logger.error(_msg)
            raise KeyboardInterrupt
        if ((self.tc_failure_threshold != 0)
                and (self.__failed_tc_count >= self.tc_failure_threshold)):
            if self.__failed_tc_count_msg:
                raise TCThresholdReached
            _msg = 'Testcases failure for this testsuite count exceeded'
            _msg += ' testcase failure threshold '
            _msg += '(%d)' % self.tc_failure_threshold
            self.logger.error(_msg)
            self.__failed_tc_count_msg = True
            raise TCThresholdReached
        timeout = self.__get_timeout(test)
        rv = self.__are_requirements_matching(self.param_dict, test)
        if rv is False:
            # Below method call is needed in order to get the test case
            # details in the output and to have the skipped test count
            # included in total run count of the test run
            self.result.startTest(test)
            raise SkipTest('Test requirements are not matching')

        def timeout_handler(signum, frame):
            raise TimeOut('Timed out after %s second' % timeout)

        old_handler = signal.signal(signal.SIGALRM, timeout_handler)
        setattr(test, 'old_sigalrm_handler', old_handler)
        signal.alarm(timeout)

    def stopTest(self, test):
        """
        Stop the test
        """
        old_sigalrm_handler = getattr(test, 'old_sigalrm_handler', None)
        if old_sigalrm_handler is not None:
            signal.signal(signal.SIGALRM, old_sigalrm_handler)
            signal.alarm(0)

    def addError(self, test, err):
        """
        Add error
        """
        if isclass(err[0]) and issubclass(err[0], TCThresholdReached):
            return True
        self.__set_test_end_data(test, err)

    def addFailure(self, test, err):
        """
        Add failure
        """
        self.__set_test_end_data(test, err)

    def addSuccess(self, test):
        """
        Add success
        """
        self.__set_test_end_data(test)

    def _cleanup(self):
        self.logger.info('Cleaning up temporary files')
        du = DshUtils()
        root_dir = os.sep
        dirlist = set([
            os.path.join(root_dir, 'tmp'),
            os.path.join(root_dir, 'var', 'tmp')
        ])
        # get tmp dir from the environment
        for envname in 'TMPDIR', 'TEMP', 'TMP':
            dirname = os.getenv(envname)
            if dirname:
                dirlist.add(dirname)

        p = re.compile(r'^pbs\.\d+')
        for tmpdir in dirlist:
            # list the contents of each tmp dir and
            # get the file list to be deleted
            self.logger.info('Cleaning up ' + tmpdir + ' dir')
            ftd = []
            files = du.listdir(path=tmpdir)
            bn = os.path.basename
            ftd.extend([f for f in files if bn(f).startswith('PtlPbs')])
            ftd.extend([f for f in files if bn(f).startswith('STDIN')])
            ftd.extend([f for f in files if bn(f).startswith('pbsscrpt')])
            ftd.extend([f for f in files if bn(f).startswith('pbs.conf.')])
            ftd.extend([f for f in files if p.match(bn(f))])
            for f in ftd:
                du.rm(path=f,
                      sudo=True,
                      recursive=True,
                      force=True,
                      level=logging.DEBUG)
        for f in du.tmpfilelist:
            du.rm(path=f, sudo=True, force=True, level=logging.DEBUG)
        del du.tmpfilelist[:]
        tmpdir = tempfile.gettempdir()
        os.chdir(tmpdir)
        tmppath = os.path.join(tmpdir, 'dejagnutemp%s' % os.getpid())
        if du.isdir(path=tmppath):
            du.rm(path=tmppath,
                  recursive=True,
                  sudo=True,
                  force=True,
                  level=logging.DEBUG)

    def begin(self):
        command = sys.argv
        command[0] = os.path.basename(command[0])
        self.logger.info('input command: ' + ' '.join(command))
        self.logger.info('param: ' + str(self.param))
        self.logger.info('ptl version: ' + str(ptl.__version__))
        _m = 'platform: ' + ' '.join(platform.uname()).strip()
        self.logger.info(_m)
        self.logger.info('python version: ' + str(platform.python_version()))
        self.logger.info('user: '******'-' * 80)

        if self.lcov_data is not None:
            self.lcov_utils = LcovUtils(cov_bin=self.lcov_bin,
                                        html_bin=self.genhtml_bin,
                                        cov_out=self.lcov_out,
                                        data_dir=self.lcov_data,
                                        html_nosrc=self.lcov_nosrc,
                                        html_baseurl=self.lcov_baseurl)
            # Initialize coverage analysis
            self.lcov_utils.zero_coverage()
            # The following 'dance' is done due to some oddities on lcov's
            # part, according to this the lcov readme file at
            # http://ltp.sourceforge.net/coverage/lcov/readme.php that reads:
            #
            # Note that this step only works after the application has
            # been started and stopped at least once. Otherwise lcov will
            # abort with an error mentioning that there are no data/.gcda
            # files.
            self.lcov_utils.initialize_coverage(name='PTLTestCov')
            PBSInitServices().restart()
        self._cleanup()

    def finalize(self, result):
        if self.lcov_data is not None:
            # See note above that briefly explains the 'dance' needed to get
            # reliable coverage data
            PBSInitServices().restart()
            self.lcov_utils.capture_coverage(name='PTLTestCov')
            exclude = [
                '"*work/gSOAP/*"', '"*/pbs/doc/*"', 'lex.yy.c',
                'pbs_ifl_wrap.c', 'usr/include/*', 'unsupported/*'
            ]
            self.lcov_utils.merge_coverage_traces(name='PTLTestCov',
                                                  exclude=exclude)
            self.lcov_utils.generate_html()
            self.lcov_utils.change_baseurl()
            self.logger.info('\n'.join(self.lcov_utils.summarize_coverage()))
        self._cleanup()