Ejemplo n.º 1
0
    def CheckBranchSpecs(branch_specs):
        """Make sure entries in the list branch_specs are correctly formed.

        We accept any of BARE_BRANCHES in |branch_specs|, as
        well as _one_ string of the form '>=RXX' or '==RXX', where 'RXX' is a
        CrOS milestone number.

        @param branch_specs: an iterable of branch specifiers.
        @raise MalformedConfigEntry if there's a problem parsing |branch_specs|.
        """
        have_seen_numeric_constraint = False
        for branch in branch_specs:
            if branch in BARE_BRANCHES:
                continue
            if not have_seen_numeric_constraint:
                #TODO(beeps): Why was <= dropped on the floor?
                if branch.startswith('>=R') or branch.startswith('==R'):
                    have_seen_numeric_constraint = True
                elif 'tot' in branch:
                    TotMilestoneManager().ConvertTotSpec(
                        branch[branch.index('tot'):])
                    have_seen_numeric_constraint = True
                continue
            raise error.MalformedConfigEntry("%s isn't a valid branch spec.'" %
                                             branch)
Ejemplo n.º 2
0
    def ConvertTotSpec(self, tot_spec):
        """Converts a tot spec to the appropriate milestone.

        Assume tot is R40:
        tot   -> R40
        tot-1 -> R39
        tot-2 -> R38
        tot-(any other numbers) -> R40

        With the last option one assumes that a malformed configuration that has
        'tot' in it, wants at least tot.

        @param tot_spec: A string representing the tot spec.
        @raises MalformedConfigEntry: If the tot_spec doesn't match the
            expected format.
        """
        tot_spec = tot_spec.lower()
        match = re.match('(tot)[-]?(1$|2$)?', tot_spec)
        if not match:
            raise error.MalformedConfigEntry("%s isn't a valid branch spec." %
                                             tot_spec)
        tot_mstone = self.tot
        num_back = match.groups()[1]
        if num_back:
            tot_mstone_num = tot_mstone.lstrip('R')
            tot_mstone = tot_mstone.replace(
                tot_mstone_num, str(int(tot_mstone_num) - int(num_back)))
        return tot_mstone
Ejemplo n.º 3
0
 def wrapper(*args, **kwargs):
     try:
         return func(*args, **kwargs)
     except ConfigParser.Error:
         return None
     except ValueError as e:
         raise error.MalformedConfigEntry(str(e))
Ejemplo n.º 4
0
    def _ReadBoardWhitelist(self, config):
        """Read board whitelist from config and save as dict.

        @param config: an instance of ForgivingConfigParser.
        """
        board_lists = {}
        if BOARD_WHITELIST_SECTION not in config.sections():
            return board_lists

        for option in config.options(BOARD_WHITELIST_SECTION):
            if option in board_lists:
                raise error.MalformedConfigEntry(
                        'Board list name must be unique.')
            else:
                board_lists[option] = config.getstring(
                        BOARD_WHITELIST_SECTION, option)

        return board_lists
Ejemplo n.º 5
0
    def __init__(self,
                 name,
                 suite,
                 branch_specs,
                 pool=None,
                 num=None,
                 boards=None,
                 priority=None,
                 timeout=None,
                 file_bugs=False,
                 cros_build_spec=None,
                 firmware_rw_build_spec=None,
                 firmware_ro_build_spec=None,
                 test_source=None,
                 job_retry=False,
                 hour=None,
                 day=None,
                 os_type=OS_TYPE_CROS,
                 launch_control_branches=None,
                 launch_control_targets=None,
                 testbed_dut_count=None,
                 no_delay=False):
        """Constructor

        Given an iterable in |branch_specs|, pre-vetted using CheckBranchSpecs,
        we'll store them such that _FitsSpec() can be used to check whether a
        given branch 'fits' with the specifications passed in here.
        For example, given branch_specs = ['factory', '>=R18'], we'd set things
        up so that _FitsSpec() would return True for 'factory', or 'RXX'
        where XX is a number >= 18. Same check is done for branch_specs = [
        'factory', '==R18'], which limit the test to only one specific branch.

        Given branch_specs = ['factory', 'firmware'], _FitsSpec()
        would pass only those two specific strings.

        Example usage:
          t = Task('Name', 'suite', ['factory', '>=R18'])
          t._FitsSpec('factory')  # True
          t._FitsSpec('R19')  # True
          t._FitsSpec('R17')  # False
          t._FitsSpec('firmware')  # False
          t._FitsSpec('goober')  # False

          t = Task('Name', 'suite', ['factory', '==R18'])
          t._FitsSpec('R19')  # False, branch does not equal to 18
          t._FitsSpec('R18')  # True
          t._FitsSpec('R17')  # False

        cros_build_spec and firmware_rw_build_spec are set for tests require
        firmware update on the dut. Only one of them can be set.
        For example:
        branch_specs: ==tot
        firmware_rw_build_spec: firmware
        test_source: cros
        This will run test using latest build on firmware branch, and the latest
        ChromeOS build on ToT. The test source build is ChromeOS build.

        branch_specs: firmware
        cros_build_spec: ==tot-1
        test_source: firmware_rw
        This will run test using latest build on firmware branch, and the latest
        ChromeOS build on dev channel (ToT-1). The test source build is the
        firmware RW build.

        branch_specs: ==tot
        firmware_rw_build_spec: cros
        test_source: cros
        This will run test using latest ChromeOS and firmware RW build on ToT.
        ChromeOS build on ToT. The test source build is ChromeOS build.

        @param name: name of this task, e.g. 'NightlyPower'
        @param suite: the name of the suite to run, e.g. 'bvt'
        @param branch_specs: a pre-vetted iterable of branch specifiers,
                             e.g. ['>=R18', 'factory']
        @param pool: the pool of machines to use for scheduling purposes.
                     Default: None
        @param num: the number of devices across which to shard the test suite.
                    Type: integer or None
                    Default: None
        @param boards: A comma separated list of boards to run this task on.
                       Default: Run on all boards.
        @param priority: The string name of a priority from
                         client.common_lib.priorities.Priority.
        @param timeout: The max lifetime of the suite in hours.
        @param file_bugs: True if bug filing is desired for the suite created
                          for this task.
        @param cros_build_spec: Spec used to determine the ChromeOS build to
                                test with a firmware build, e.g., tot, R41 etc.
        @param firmware_rw_build_spec: Spec used to determine the firmware RW
                                       build test with a ChromeOS build.
        @param firmware_ro_build_spec: Spec used to determine the firmware RO
                                       build test with a ChromeOS build.
        @param test_source: The source of test code when firmware will be
                            updated in the test. The value can be `firmware_rw`,
                            `firmware_ro` or `cros`.
        @param job_retry: Set to True to enable job-level retry. Default is
                          False.
        @param hour: An integer specifying the hour that a nightly run should
                     be triggered, default is set to 21.
        @param day: An integer specifying the day of a week that a weekly run
                should be triggered, default is set to 5, which is Saturday.
        @param os_type: Type of OS, e.g., cros, brillo, android. Default is
                cros. The argument is required for android/brillo builds.
        @param launch_control_branches: Comma separated string of Launch Control
                branches. The argument is required and only applicable for
                android/brillo builds.
        @param launch_control_targets: Comma separated string of build targets
                for Launch Control builds. The argument is required and only
                applicable for android/brillo builds.
        @param testbed_dut_count: Number of duts to test when using a testbed.
        @param no_delay: Set to True to allow suite to be created without
                configuring delay_minutes. Default is False.
        """
        self._name = name
        self._suite = suite
        self._branch_specs = branch_specs
        self._pool = pool
        self._num = num
        self._priority = priority
        self._timeout = timeout
        self._file_bugs = file_bugs
        self._cros_build_spec = cros_build_spec
        self._firmware_rw_build_spec = firmware_rw_build_spec
        self._firmware_ro_build_spec = firmware_ro_build_spec
        self._test_source = test_source
        self._job_retry = job_retry
        self._hour = hour
        self._day = day
        self._os_type = os_type
        self._launch_control_branches = ([
            b.strip() for b in launch_control_branches.split(',')
        ] if launch_control_branches else [])
        self._launch_control_targets = ([
            t.strip() for t in launch_control_targets.split(',')
        ] if launch_control_targets else [])
        self._testbed_dut_count = testbed_dut_count
        self._no_delay = no_delay

        if ((self._firmware_rw_build_spec or self._firmware_ro_build_spec
             or cros_build_spec) and not self.test_source
                in [Builds.FIRMWARE_RW, Builds.FIRMWARE_RO, Builds.CROS]):
            raise error.MalformedConfigEntry(
                'You must specify the build for test source. It can only '
                'be `firmware_rw`, `firmware_ro` or `cros`.')
        if self._firmware_rw_build_spec and cros_build_spec:
            raise error.MalformedConfigEntry(
                'You cannot specify both firmware_rw_build_spec and '
                'cros_build_spec. firmware_rw_build_spec is used to specify'
                ' a firmware build when the suite requires firmware to be '
                'updated in the dut, its value can only be `firmware` or '
                '`cros`. cros_build_spec is used to specify a ChromeOS '
                'build when build_specs is set to firmware.')
        if (self._firmware_rw_build_spec
                and self._firmware_rw_build_spec not in ['firmware', 'cros']):
            raise error.MalformedConfigEntry(
                'firmware_rw_build_spec can only be empty, firmware or '
                'cros. It does not support other build type yet.')

        if os_type not in OS_TYPES_LAUNCH_CONTROL and self._testbed_dut_count:
            raise error.MalformedConfigEntry(
                'testbed_dut_count is only applicable to testbed to run '
                'test with builds from Launch Control.')

        self._bare_branches = []
        self._version_equal_constraint = False
        self._version_gte_constraint = False
        self._version_lte_constraint = False
        if not branch_specs:
            # Any milestone is OK.
            self._numeric_constraint = version.LooseVersion('0')
        else:
            self._numeric_constraint = None
            for spec in branch_specs:
                if 'tot' in spec.lower():
                    tot_str = spec[spec.index('tot'):]
                    spec = spec.replace(
                        tot_str,
                        TotMilestoneManager().ConvertTotSpec(tot_str))
                if spec.startswith('>='):
                    self._numeric_constraint = version.LooseVersion(
                        spec.lstrip('>=R'))
                    self._version_gte_constraint = True
                elif spec.startswith('<='):
                    self._numeric_constraint = version.LooseVersion(
                        spec.lstrip('<=R'))
                    self._version_lte_constraint = True
                elif spec.startswith('=='):
                    self._version_equal_constraint = True
                    self._numeric_constraint = version.LooseVersion(
                        spec.lstrip('==R'))
                else:
                    self._bare_branches.append(spec)

        # Since we expect __hash__() and other comparator methods to be used
        # frequently by set operations, and they use str() a lot, pre-compute
        # the string representation of this object.
        if num is None:
            numStr = '[Default num]'
        else:
            numStr = '%d' % num

        if boards is None:
            self._boards = set()
            boardsStr = '[All boards]'
        else:
            self._boards = set([x.strip() for x in boards.split(',')])
            boardsStr = boards

        time_str = ''
        if self._hour:
            time_str = ' Run at %d:00.' % self._hour
        elif self._day:
            time_str = ' Run on %s.' % _WEEKDAYS[self._day]
        if os_type == OS_TYPE_CROS:
            self._str = ('%s: %s on %s with pool %s, boards [%s], file_bugs = '
                         '%s across %s machines.%s' %
                         (self.__class__.__name__, suite, branch_specs, pool,
                          boardsStr, self._file_bugs, numStr, time_str))
        else:
            testbed_dut_count_str = '.'
            if self._testbed_dut_count:
                testbed_dut_count_str = (', each with %d duts.' %
                                         self._testbed_dut_count)
            self._str = (
                '%s: %s on branches %s and targets %s with pool %s, '
                'boards [%s], file_bugs = %s across %s machines%s%s' %
                (self.__class__.__name__, suite, launch_control_branches,
                 launch_control_targets, pool, boardsStr, self._file_bugs,
                 numStr, testbed_dut_count_str, time_str))
Ejemplo n.º 6
0
    def CreateFromConfigSection(config, section, board_lists={}):
        """Create a Task from a section of a config file.

        The section to parse should look like this:
        [TaskName]
        suite: suite_to_run  # Required
        run_on: event_on which to run  # Required
        hour: integer of the hour to run, only applies to nightly. # Optional
        branch_specs: factory,firmware,>=R12 or ==R12 # Optional
        pool: pool_of_devices  # Optional
        num: sharding_factor  # int, Optional
        boards: board1, board2  # comma seperated string, Optional
        # Settings for Launch Control builds only:
        os_type: brillo # Type of OS, e.g., cros, brillo, android. Default is
                 cros. Required for android/brillo builds.
        branches: git_mnc_release # comma separated string of Launch Control
                  branches. Required and only applicable for android/brillo
                  builds.
        targets: dragonboard-eng # comma separated string of build targets.
                 Required and only applicable for android/brillo builds.
        testbed_dut_count: Number of duts to test when using a testbed.

        By default, Tasks run on all release branches, not factory or firmware.

        @param config: a ForgivingConfigParser.
        @param section: the section to parse into a Task.
        @param board_lists: a dict including all board whitelist for tasks.
        @return keyword, Task object pair.  One or both will be None on error.
        @raise MalformedConfigEntry if there's a problem parsing |section|.
        """
        if not config.has_section(section):
            raise error.MalformedConfigEntry('unknown section %s' % section)

        allowed = set([
            'suite', 'run_on', 'branch_specs', 'pool', 'num', 'boards',
            'file_bugs', 'cros_build_spec', 'firmware_rw_build_spec',
            'firmware_ro_build_spec', 'test_source', 'job_retry', 'hour',
            'day', 'branches', 'targets', 'os_type', 'no_delay', 'owner',
            'priority', 'timeout'
        ])
        # The parameter of union() is the keys under the section in the config
        # The union merges this with the allowed set, so if any optional keys
        # are omitted, then they're filled in. If any extra keys are present,
        # then they will expand unioned set, causing it to fail the following
        # comparison against the allowed set.
        section_headers = allowed.union(dict(config.items(section)).keys())
        if allowed != section_headers:
            raise error.MalformedConfigEntry(
                'unknown entries: %s' %
                ", ".join(map(str, section_headers.difference(allowed))))

        keyword = config.getstring(section, 'run_on')
        hour = config.getstring(section, 'hour')
        suite = config.getstring(section, 'suite')
        branch_specs = config.getstring(section, 'branch_specs')
        pool = config.getstring(section, 'pool')
        boards = config.getstring(section, 'boards')
        file_bugs = config.getboolean(section, 'file_bugs')
        cros_build_spec = config.getstring(section, 'cros_build_spec')
        firmware_rw_build_spec = config.getstring(section,
                                                  'firmware_rw_build_spec')
        firmware_ro_build_spec = config.getstring(section,
                                                  'firmware_ro_build_spec')
        test_source = config.getstring(section, 'test_source')
        job_retry = config.getboolean(section, 'job_retry')
        no_delay = config.getboolean(section, 'no_delay')
        # In case strings empty use sane low priority defaults.
        priority = 0
        timeout = 24
        # Set priority/timeout based on the event type.
        for klass in driver.Driver.EVENT_CLASSES:
            if klass.KEYWORD == keyword:
                priority = klass.PRIORITY
                timeout = klass.TIMEOUT
                break
        # Set priority/timeout from config file explicitly if set.
        priority_string = config.getstring(section, 'priority')
        if priority_string:
            # Try to parse priority as int first. If failed, then use the
            # global string->priority mapping to lookup its value.
            try:
                try:
                    priority = int(priority_string)
                except ValueError:
                    priority = priorities.Priority.get_value(priority_string)
            except ValueError:
                raise error.MalformedConfigEntry(
                    "Priority string not "
                    "recognized as value (%s).", priority_string)
        timeout_value = config.getint(section, 'timeout')
        if timeout_value:
            timeout = timeout_value

        # Sanity Check for priority and timeout.
        if priority < 0 or priority > 100:
            raise error.MalformedConfigEntry('Priority(%d) should be inside '
                                             'the range 0-100.' % priority)
        if timeout <= 0:
            raise error.MalformedConfigEntry(
                'Timeout(%d) needs to be positive '
                'integer (hours).' % timeout)

        try:
            num = config.getint(section, 'num')
        except ValueError as e:
            raise error.MalformedConfigEntry("Ill-specified 'num': %r" % e)
        if not keyword:
            raise error.MalformedConfigEntry('No event to |run_on|.')
        if not suite:
            raise error.MalformedConfigEntry('No |suite|')
        try:
            hour = config.getint(section, 'hour')
        except ValueError as e:
            raise error.MalformedConfigEntry("Ill-specified 'hour': %r" % e)
        if hour is not None and (hour < 0 or hour > 23):
            raise error.MalformedConfigEntry(
                '`hour` must be an integer between 0 and 23.')
        if hour is not None and keyword != 'nightly':
            raise error.MalformedConfigEntry(
                '`hour` is the trigger time that can only apply to nightly '
                'event.')

        testbed_dut_count = None
        if boards:
            match = re.match(TESTBED_DUT_COUNT_REGEX, boards)
            if match:
                testbed_dut_count = int(match.group(1))

        try:
            day = config.getint(section, 'day')
        except ValueError as e:
            raise error.MalformedConfigEntry("Ill-specified 'day': %r" % e)
        if day is not None and (day < 0 or day > 6):
            raise error.MalformedConfigEntry(
                '`day` must be an integer between 0 and 6, where 0 is for '
                'Monday and 6 is for Sunday.')
        if day is not None and keyword != 'weekly':
            raise error.MalformedConfigEntry(
                '`day` is the trigger of the day of a week, that can only '
                'apply to weekly events.')

        specs = []
        if branch_specs:
            specs = re.split('\s*,\s*', branch_specs)
            Task.CheckBranchSpecs(specs)

        os_type = config.getstring(section, 'os_type') or OS_TYPE_CROS
        if os_type not in OS_TYPES:
            raise error.MalformedConfigEntry('`os_type` must be one of %s' %
                                             OS_TYPES)

        lc_branches = config.getstring(section, 'branches')
        lc_targets = config.getstring(section, 'targets')
        if os_type == OS_TYPE_CROS and (lc_branches or lc_targets):
            raise error.MalformedConfigEntry(
                '`branches` and `targets` are only supported for Launch '
                'Control builds, not ChromeOS builds.')
        if (os_type in OS_TYPES_LAUNCH_CONTROL
                and (not lc_branches or not lc_targets)):
            raise error.MalformedConfigEntry(
                '`branches` and `targets` must be specified for Launch '
                'Control builds.')
        if (os_type in OS_TYPES_LAUNCH_CONTROL and boards
                and not testbed_dut_count):
            raise error.MalformedConfigEntry(
                '`boards` for Launch Control builds are retrieved from '
                '`targets` setting, it should not be set for Launch '
                'Control builds.')
        if os_type == OS_TYPE_CROS and testbed_dut_count:
            raise error.MalformedConfigEntry(
                'testbed_dut_count is only supported for Launch Control '
                'builds testing with testbed.')

        # Extract boards from targets list.
        if os_type in OS_TYPES_LAUNCH_CONTROL:
            boards = ''
            for target in lc_targets.split(','):
                board_name, _ = server_utils.parse_launch_control_target(
                    target.strip())
                # Translate board name in build target to the actual board name.
                board_name = server_utils.ANDROID_TARGET_TO_BOARD_MAP.get(
                    board_name, board_name)
                boards += '%s,' % board_name
            boards = boards.strip(',')
        elif os_type == OS_TYPE_CROS:
            if board_lists:
                if boards not in board_lists:
                    logging.debug(
                        'The board_list name %s does not exist in '
                        'section board_lists in config.', boards)
                    # TODO(xixuan): Raise MalformedConfigEntry when a CrOS task
                    # specify a 'boards' which is not defined in board_lists.
                    # Currently exception won't be raised to make sure suite
                    # scheduler keeps running when developers are in the middle
                    # of migrating boards.
                else:
                    boards = board_lists[boards]

        return keyword, Task(section,
                             suite,
                             specs,
                             pool,
                             num,
                             boards,
                             priority,
                             timeout,
                             file_bugs=file_bugs if file_bugs else False,
                             cros_build_spec=cros_build_spec,
                             firmware_rw_build_spec=firmware_rw_build_spec,
                             firmware_ro_build_spec=firmware_ro_build_spec,
                             test_source=test_source,
                             job_retry=job_retry,
                             hour=hour,
                             day=day,
                             os_type=os_type,
                             launch_control_branches=lc_branches,
                             launch_control_targets=lc_targets,
                             testbed_dut_count=testbed_dut_count,
                             no_delay=no_delay)