Beispiel #1
0
    def testReimageAndSIGTERM(self):
        """Should reimage_and_run that causes a SIGTERM and fails cleanly."""
        def suicide(*_, **__):
            """Send SIGTERM to current process to exit.

            @param _: Ignored.
            @param __: Ignored.
            """
            os.kill(os.getpid(), signal.SIGTERM)

        # Mox doesn't play well with SIGTERM, but it does play well with
        # with exceptions, so here we're using an exception to simulate
        # execution being interrupted by a signal.
        class UnhandledSIGTERM(Exception):
            """Exception to be raised when SIGTERM is received."""
            pass

        def handler(signal_number, frame):
            """Handler for receiving a signal.

            @param signal_number: signal number.
            @param frame: stack frame object.
            """
            raise UnhandledSIGTERM()

        signal.signal(signal.SIGTERM, handler)
        spec = mock.MagicMock()
        spec.builds = self._BUILDS
        spec.test_source_build = Suite.get_test_source_build(self._BUILDS)
        spec.devserver.stage_artifacts.side_effect = suicide
        spec.run_prod_code = False

        self.assertRaises(UnhandledSIGTERM,
                          dynamic_suite._perform_reimage_and_run, spec, None,
                          None, None)
    def _init_test_source_build(self, test_source_build):
        """Initialize test_source_build attribute."""
        if test_source_build:
            test_source_build = self.devserver.translate(test_source_build)

        self.test_source_build = Suite.get_test_source_build(
                self.builds, test_source_build=test_source_build)
Beispiel #3
0
    def testReimageAndSIGTERM(self):
        """Should reimage_and_run that causes a SIGTERM and fails cleanly."""
        def suicide(*_, **__):
            """Send SIGTERM to current process to exit.

            @param _: Ignored.
            @param __: Ignored.
            """
            os.kill(os.getpid(), signal.SIGTERM)

        # Mox doesn't play well with SIGTERM, but it does play well with
        # with exceptions, so here we're using an exception to simulate
        # execution being interrupted by a signal.
        class UnhandledSIGTERM(Exception):
            """Exception to be raised when SIGTERM is received."""
            pass

        def handler(signal_number, frame):
            """Handler for receiving a signal.

            @param signal_number: signal number.
            @param frame: stack frame object.
            """
            raise UnhandledSIGTERM()

        signal.signal(signal.SIGTERM, handler)
        spec = self.mox.CreateMock(dynamic_suite.SuiteSpec)
        spec.builds = self._BUILDS
        spec.test_source_build = Suite.get_test_source_build(self._BUILDS)
        spec.devserver = self.mox.CreateMock(dev_server.ImageServer)
        spec.devserver.stage_artifacts(
            image=spec.builds[provision.CROS_VERSION_PREFIX],
            artifacts=['control_files',
                       'test_suites']).WithSideEffects(suicide)
        spec.run_prod_code = False

        self.mox.ReplayAll()

        self.assertRaises(UnhandledSIGTERM,
                          dynamic_suite._perform_reimage_and_run, spec, None,
                          None, None)
Beispiel #4
0
def create_suite_job(name='',
                     board='',
                     build='',
                     pool='',
                     control_file='',
                     check_hosts=True,
                     num=None,
                     file_bugs=False,
                     timeout=24,
                     timeout_mins=None,
                     priority=priorities.Priority.DEFAULT,
                     suite_args=None,
                     wait_for_results=True,
                     job_retry=False,
                     max_retries=None,
                     max_runtime_mins=None,
                     suite_min_duts=0,
                     offload_failures_only=False,
                     builds={},
                     test_source_build=None,
                     run_prod_code=False,
                     **kwargs):
    """
    Create a job to run a test suite on the given device with the given image.

    When the timeout specified in the control file is reached, the
    job is guaranteed to have completed and results will be available.

    @param name: The test name if control_file is supplied, otherwise the name
                 of the test suite to run, e.g. 'bvt'.
    @param board: the kind of device to run the tests on.
    @param build: unique name by which to refer to the image from now on.
    @param builds: the builds to install e.g.
                   {'cros-version:': 'x86-alex-release/R18-1655.0.0',
                    'fw-version:':  'x86-alex-firmware/R36-5771.50.0',
                    'fwro-version:':  'x86-alex-firmware/R36-5771.49.0'}
                   If builds is given a value, it overrides argument build.
    @param test_source_build: Build that contains the server-side test code.
    @param pool: Specify the pool of machines to use for scheduling
            purposes.
    @param check_hosts: require appropriate live hosts to exist in the lab.
    @param num: Specify the number of machines to schedule across (integer).
                Leave unspecified or use None to use default sharding factor.
    @param file_bugs: File a bug on each test failure in this suite.
    @param timeout: The max lifetime of this suite, in hours.
    @param timeout_mins: The max lifetime of this suite, in minutes. Takes
                         priority over timeout.
    @param priority: Integer denoting priority. Higher is more important.
    @param suite_args: Optional arguments which will be parsed by the suite
                       control file. Used by control.test_that_wrapper to
                       determine which tests to run.
    @param wait_for_results: Set to False to run the suite job without waiting
                             for test jobs to finish. Default is True.
    @param job_retry: Set to True to enable job-level retry. Default is False.
    @param max_retries: Integer, maximum job retries allowed at suite level.
                        None for no max.
    @param max_runtime_mins: Maximum amount of time a job can be running in
                             minutes.
    @param suite_min_duts: Integer. Scheduler will prioritize getting the
                           minimum number of machines for the suite when it is
                           competing with another suite that has a higher
                           priority but already got minimum machines it needs.
    @param offload_failures_only: Only enable gs_offloading for failed jobs.
    @param run_prod_code: If True, the suite will run the test code that
                          lives in prod aka the test code currently on the
                          lab servers. If False, the control files and test
                          code for this suite run will be retrieved from the
                          build artifacts.
    @param kwargs: extra keyword args. NOT USED.

    @raises ControlFileNotFound: if a unique suite control file doesn't exist.
    @raises NoControlFileList: if we can't list the control files at all.
    @raises StageControlFileFailure: If the dev server throws 500 while
                                     staging test_suites.
    @raises ControlFileEmpty: if the control file exists on the server, but
                              can't be read.

    @return: the job ID of the suite; -1 on error.
    """
    if type(num) is not int and num is not None:
        raise error.SuiteArgumentException('Ill specified num argument %r. '
                                           'Must be an integer or None.' % num)
    if num == 0:
        logging.warning("Can't run on 0 hosts; using default.")
        num = None

    # TODO(dshi): crbug.com/496782 Remove argument build and its reference after
    # R45 falls out of stable channel.
    if build and not builds:
        builds = {provision.CROS_VERSION_PREFIX: build}
    # TODO(dshi): crbug.com/497236 Remove this check after firmware ro provision
    # is supported in Autotest.
    if provision.FW_RO_VERSION_PREFIX in builds:
        raise error.SuiteArgumentException(
            'Updating RO firmware is not supported yet.')
    # Default test source build to CrOS build if it's not specified.
    test_source_build = Suite.get_test_source_build(
        builds, test_source_build=test_source_build)

    suite_name = canonicalize_suite_name(name)
    if run_prod_code:
        ds = dev_server.ImageServer.resolve(build)
        keyvals = {}
        getter = control_file_getter.FileSystemGetter([
            _CONFIG.get_config_value('SCHEDULER',
                                     'drone_installation_directory')
        ])
        control_file = getter.get_control_file_contents_by_name(suite_name)
    else:
        (ds, keyvals) = _stage_build_artifacts(test_source_build)
    keyvals[constants.SUITE_MIN_DUTS_KEY] = suite_min_duts

    if not control_file:
        # No control file was supplied so look it up from the build artifacts.
        suite_name = canonicalize_suite_name(name)
        control_file = _get_control_file_contents_by_name(
            test_source_build, ds, suite_name)
        # Do not change this naming convention without updating
        # site_utils.parse_job_name.
        name = '%s-%s' % (test_source_build, suite_name)

    timeout_mins = timeout_mins or timeout * 60
    max_runtime_mins = max_runtime_mins or timeout * 60

    if not board:
        board = utils.ParseBuildName(builds[provision.CROS_VERSION_PREFIX])[0]

    # TODO(dshi): crbug.com/496782 Remove argument build and its reference after
    # R45 falls out of stable channel.
    # Prepend build and board to the control file.
    inject_dict = {
        'board': board,
        'build': builds.get(provision.CROS_VERSION_PREFIX),
        'builds': builds,
        'check_hosts': check_hosts,
        'pool': pool,
        'num': num,
        'file_bugs': file_bugs,
        'timeout': timeout,
        'timeout_mins': timeout_mins,
        'devserver_url': ds.url(),
        'priority': priority,
        'suite_args': suite_args,
        'wait_for_results': wait_for_results,
        'job_retry': job_retry,
        'max_retries': max_retries,
        'max_runtime_mins': max_runtime_mins,
        'offload_failures_only': offload_failures_only,
        'test_source_build': test_source_build,
        'run_prod_code': run_prod_code
    }

    control_file = tools.inject_vars(inject_dict, control_file)

    return rpc_utils.create_job_common(name,
                                       priority=priority,
                                       timeout_mins=timeout_mins,
                                       max_runtime_mins=max_runtime_mins,
                                       control_type='Server',
                                       control_file=control_file,
                                       hostless=True,
                                       keyvals=keyvals)
Beispiel #5
0
    def __init__(self,
                 build=None,
                 builds=None,
                 board=None,
                 name=None,
                 job=None,
                 pool=None,
                 num=None,
                 check_hosts=True,
                 add_experimental=True,
                 file_bugs=False,
                 file_experimental_bugs=False,
                 max_runtime_mins=24 * 60,
                 timeout=24,
                 timeout_mins=None,
                 firmware_reimage=False,
                 suite_dependencies=[],
                 version_prefix=None,
                 bug_template={},
                 devserver_url=None,
                 priority=priorities.Priority.DEFAULT,
                 predicate=None,
                 wait_for_results=True,
                 job_retry=False,
                 max_retries=None,
                 offload_failures_only=False,
                 test_source_build=None,
                 run_prod_code=False,
                 **dargs):
        """
        Vets arguments for reimage_and_run() and populates self with supplied
        values.

        TODO(dshi): crbug.com/496782 once R45 falls off stable channel, we
        should remove option build, firmware_reimage and version_prefix, as they
        will be all merged into option builds.

        Currently required args:
        @param board: which kind of devices to reimage.
        @param name: a value of the SUITE control file variable to search for.
        @param job: an instance of client.common_lib.base_job representing the
                    currently running suite job.
        @param devserver_url: url to the selected devserver.

        Currently supported optional args:
        @param build: the build to install e.g.
                      x86-alex-release/R18-1655.0.0-a1-b1584.
        @param builds: the builds to install e.g.
                       {'cros-version:': 'x86-alex-release/R18-1655.0.0',
                        'fw-version:':  'x86-alex-firmware/R36-5771.50.0'}
        @param test_source_build: Build that contains the server-side test code,
                e.g., it can be the value of builds['cros-version:'] or
                builds['fw-version:']. Default is None, that is, use
                the server-side test code from builds['cros-version:']
        @param pool: specify the pool of machines to use for scheduling purposes
                     Default: None
        @param num: the maximum number of devices to reimage.
                    Default in global_config
        @param check_hosts: require appropriate hosts to be available now.
        @param add_experimental: schedule experimental tests as well, or not.
                                 Default: True
        @param file_bugs: File bugs when tests in this suite fail.
                          Default: False
        @param file_experimental_bugs: File bugs when experimental tests in
                                       this suite fail.
                                       Default: False
        @param max_runtime_mins: Max runtime in mins for each of the sub-jobs
                                 this suite will run.
        @param timeout: Max lifetime in hours for each of the sub-jobs that
                        this suite run.
        @param firmware_reimage: True if we should use FW_RW_VERSION_PREFIX as
                                 the version_prefix.
                                 False if we should use CROS_VERSION_PREFIX as
                                 the version_prefix.
                                 (This flag has now been deprecated in favor of
                                  version_prefix.)
        @param suite_dependencies: A list of strings of suite level
                                   dependencies, which act just like test
                                   dependencies and are appended to each test's
                                   set of dependencies at job creation time.
                                   A string of comma seperated labels is
                                   accepted for backwards compatibility.
        @param bug_template: A template dictionary specifying the default bug
                             filing options for failures in this suite.
        @param version_prefix: A version prefix from provision.py that the
                               tests should be scheduled with.
        @param priority: Integer priority level.  Higher is more important.
        @param predicate: Optional argument. If present, should be a function
                          mapping ControlData objects to True if they should be
                          included in suite. If argument is absent, suite
                          behavior will default to creating a suite of based
                          on the SUITE field of control files.
        @param wait_for_results: Set to False to run the suite job without
                                 waiting for test jobs to finish. Default is
                                 True.
        @param job_retry: Set to True to enable job-level retry. Default is
                          False.
        @param max_retries: Maximum retry limit at suite level.
                            Regardless how many times each individual test
                            has been retried, the total number of retries
                            happening in the suite can't exceed _max_retries.
                            Default to None, no max.
        @param offload_failures_only: Only enable gs_offloading for failed
                                      jobs.
        @param run_prod_code: If true, the suite will run the test code that
                              lives in prod aka the test code currently on the
                              lab servers.
        @param **dargs: these arguments will be ignored.  This allows us to
                        deprecate and remove arguments in ToT while not
                        breaking branch builds.
        """
        # TODO(dshi): crbug.com/496782 Following should be added to
        # required_keywords after R45 falls off stable channel:
        # 'builds': dict,
        # To allow the transition, build is removed from the list, but the code
        # will check either build or builds should exist.
        required_keywords = {
            'board': str,
            'name': str,
            'job': base_job.base_job,
            'devserver_url': str
        }
        for key, expected in required_keywords.iteritems():
            value = locals().get(key)
            if not value or not isinstance(value, expected):
                raise error.SuiteArgumentException(
                    'reimage_and_run() needs %s=<%r>' % (key, expected))
        self._verify_builds(build, builds)

        self.board = 'board:%s' % board
        self.devserver = dev_server.ImageServer(devserver_url)

        if builds:
            self.builds = builds
        else:
            # TODO(dshi): crbug.com/496782 This warning can be removed after R45
            # falls off stable channel.
            logging.warning(
                'reimage_and_run arguments firmware_reimage and '
                'version_prefix have been deprecated. Please use '
                'a dictionary builds to specify images, e.g., '
                '{\'cros-version:\':\'peppy-release/R38-5655.0.0\','
                ' \'fw-version:\':\'peppy-firmware/R36-5371.0.0\'}')

            if version_prefix:
                prefix = version_prefix
            else:
                prefix = (provision.FW_RW_VERSION_PREFIX if firmware_reimage
                          else provision.CROS_VERSION_PREFIX)
            self.builds = {prefix: build}

        if provision.CROS_VERSION_PREFIX in self.builds:
            translated_build = self.devserver.translate(
                self.builds[provision.CROS_VERSION_PREFIX])
            self.builds[provision.CROS_VERSION_PREFIX] = translated_build

        if test_source_build:
            test_source_build = self.devserver.translate(test_source_build)

        self.test_source_build = Suite.get_test_source_build(
            self.builds, test_source_build=test_source_build)

        self.name = name
        self.job = job
        if pool:
            self.pool = 'pool:%s' % pool
        else:
            self.pool = pool
        self.num = num
        self.check_hosts = check_hosts
        self.skip_reimage = skip_reimage
        self.add_experimental = add_experimental
        self.file_bugs = file_bugs
        self.file_experimental_bugs = file_experimental_bugs
        self.dependencies = {'': []}
        self.max_runtime_mins = max_runtime_mins
        self.timeout = timeout
        self.timeout_mins = timeout_mins or timeout * 60
        if isinstance(suite_dependencies, str):
            self.suite_dependencies = [
                dep.strip(' ') for dep in suite_dependencies.split(',')
            ]
        else:
            self.suite_dependencies = suite_dependencies
        self.bug_template = bug_template
        self.priority = priority
        self.predicate = predicate
        self.wait_for_results = wait_for_results
        self.job_retry = job_retry
        self.max_retries = max_retries
        self.offload_failures_only = offload_failures_only
        self.run_prod_code = run_prod_code