[autotest] Handle config error in suite scheduler.
This change adds more error handling in parsing a task. If a task has
bad config values, log the error and ignore the task.
BUG=chromium:729330
TEST=unittest
Change-Id: Idfc068781211f4535dec5f2debbee671dc13ccc0
Reviewed-on: https://chromium-review.googlesource.com/531755
Commit-Ready: Dan Shi <dshi@google.com>
Tested-by: Dan Shi <dshi@google.com>
Reviewed-by: Xixuan Wu <xixuan@chromium.org>
diff --git a/site_utils/suite_scheduler/driver.py b/site_utils/suite_scheduler/driver.py
index 3dcc964..861bd7f 100644
--- a/site_utils/suite_scheduler/driver.py
+++ b/site_utils/suite_scheduler/driver.py
@@ -8,6 +8,7 @@
from multiprocessing import pool
import base_event, board_enumerator, build_event, deduping_scheduler
+import error
import task, timed_event
import common
@@ -90,7 +91,7 @@
for option in config.options(BOARD_WHITELIST_SECTION):
if option in board_lists:
- raise task.MalformedConfigEntry(
+ raise error.MalformedConfigEntry(
'Board list name must be unique.')
else:
board_lists[option] = config.getstring(
@@ -140,7 +141,7 @@
try:
keyword, new_task = task.Task.CreateFromConfigSection(
config, section, board_lists=board_lists)
- except task.MalformedConfigEntry as e:
+ except error.MalformedConfigEntry as e:
logging.warning('%s is malformed: %s', section, str(e))
continue
tasks.setdefault(keyword, []).append(new_task)
diff --git a/site_utils/suite_scheduler/error.py b/site_utils/suite_scheduler/error.py
new file mode 100644
index 0000000..e1c9ffa
--- /dev/null
+++ b/site_utils/suite_scheduler/error.py
@@ -0,0 +1,9 @@
+# Copyright 2017 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Errors being raised in suite scheduler."""
+
+class MalformedConfigEntry(Exception):
+ """Raised to indicate a failure to parse a Task out of a config."""
+ pass
\ No newline at end of file
diff --git a/site_utils/suite_scheduler/forgiving_config_parser.py b/site_utils/suite_scheduler/forgiving_config_parser.py
index 07827f7..126ab48 100644
--- a/site_utils/suite_scheduler/forgiving_config_parser.py
+++ b/site_utils/suite_scheduler/forgiving_config_parser.py
@@ -3,6 +3,7 @@
# found in the LICENSE file.
import ConfigParser
+import error
def forgive_config_error(func):
@@ -12,6 +13,8 @@
return func(*args, **kwargs)
except ConfigParser.Error:
return None
+ except ValueError as e:
+ raise error.MalformedConfigEntry(str(e))
return wrapper
diff --git a/site_utils/suite_scheduler/task.py b/site_utils/suite_scheduler/task.py
index 1478db2..d181523 100644
--- a/site_utils/suite_scheduler/task.py
+++ b/site_utils/suite_scheduler/task.py
@@ -10,6 +10,7 @@
import base_event
import deduping_scheduler
import driver
+import error
import manifest_versions
from distutils import version
from constants import Labels
@@ -36,11 +37,6 @@
# there is only one board specified in `boards`
TESTBED_DUT_COUNT_REGEX = '[^,]*-(\d+)'
-class MalformedConfigEntry(Exception):
- """Raised to indicate a failure to parse a Task out of a config."""
- pass
-
-
BARE_BRANCHES = ['factory', 'firmware']
@@ -124,7 +120,7 @@
tot_spec = tot_spec.lower()
match = re.match('(tot)[-]?(1$|2$)?', tot_spec)
if not match:
- raise MalformedConfigEntry(
+ raise error.MalformedConfigEntry(
"%s isn't a valid branch spec." % tot_spec)
tot_mstone = self.tot
num_back = match.groups()[1]
@@ -179,7 +175,7 @@
@raise MalformedConfigEntry if there's a problem parsing |section|.
"""
if not config.has_section(section):
- raise MalformedConfigEntry('unknown section %s' % section)
+ raise error.MalformedConfigEntry('unknown section %s' % section)
allowed = set(['suite', 'run_on', 'branch_specs', 'pool', 'num',
'boards', 'file_bugs', 'cros_build_spec',
@@ -193,7 +189,7 @@
# comparison against the allowed set.
section_headers = allowed.union(dict(config.items(section)).keys())
if allowed != section_headers:
- raise MalformedConfigEntry('unknown entries: %s' %
+ raise error.MalformedConfigEntry('unknown entries: %s' %
", ".join(map(str, section_headers.difference(allowed))))
keyword = config.getstring(section, 'run_on')
@@ -222,20 +218,20 @@
try:
num = config.getint(section, 'num')
except ValueError as e:
- raise MalformedConfigEntry("Ill-specified 'num': %r" % e)
+ raise error.MalformedConfigEntry("Ill-specified 'num': %r" % e)
if not keyword:
- raise MalformedConfigEntry('No event to |run_on|.')
+ raise error.MalformedConfigEntry('No event to |run_on|.')
if not suite:
- raise MalformedConfigEntry('No |suite|')
+ raise error.MalformedConfigEntry('No |suite|')
try:
hour = config.getint(section, 'hour')
except ValueError as e:
- raise MalformedConfigEntry("Ill-specified 'hour': %r" % e)
+ raise error.MalformedConfigEntry("Ill-specified 'hour': %r" % e)
if hour is not None and (hour < 0 or hour > 23):
- raise MalformedConfigEntry(
+ raise error.MalformedConfigEntry(
'`hour` must be an integer between 0 and 23.')
if hour is not None and keyword != 'nightly':
- raise MalformedConfigEntry(
+ raise error.MalformedConfigEntry(
'`hour` is the trigger time that can only apply to nightly '
'event.')
@@ -248,13 +244,13 @@
try:
day = config.getint(section, 'day')
except ValueError as e:
- raise MalformedConfigEntry("Ill-specified 'day': %r" % e)
+ raise error.MalformedConfigEntry("Ill-specified 'day': %r" % e)
if day is not None and (day < 0 or day > 6):
- raise MalformedConfigEntry(
+ 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 MalformedConfigEntry(
+ raise error.MalformedConfigEntry(
'`day` is the trigger of the day of a week, that can only '
'apply to weekly events.')
@@ -265,27 +261,28 @@
os_type = config.getstring(section, 'os_type') or OS_TYPE_CROS
if os_type not in OS_TYPES:
- raise MalformedConfigEntry('`os_type` must be one of %s' % 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 MalformedConfigEntry(
+ 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 MalformedConfigEntry(
+ 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 MalformedConfigEntry(
+ 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 MalformedConfigEntry(
+ raise error.MalformedConfigEntry(
'testbed_dut_count is only supported for Launch Control '
'builds testing with testbed.')
@@ -353,7 +350,8 @@
branch[branch.index('tot'):])
have_seen_numeric_constraint = True
continue
- raise MalformedConfigEntry("%s isn't a valid branch spec." % branch)
+ raise error.MalformedConfigEntry(
+ "%s isn't a valid branch spec.'" % branch)
def __init__(self, name, suite, branch_specs, pool=None, num=None,
@@ -483,11 +481,11 @@
cros_build_spec) and
not self.test_source in [Builds.FIRMWARE_RW, Builds.FIRMWARE_RO,
Builds.CROS]):
- raise MalformedConfigEntry(
+ 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 MalformedConfigEntry(
+ 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 '
@@ -496,12 +494,12 @@
'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 MalformedConfigEntry(
+ 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 MalformedConfigEntry(
+ raise error.MalformedConfigEntry(
'testbed_dut_count is only applicable to testbed to run '
'test with builds from Launch Control.')
diff --git a/site_utils/suite_scheduler/task_unittest.py b/site_utils/suite_scheduler/task_unittest.py
index 0ffa047..d4158e6 100644
--- a/site_utils/suite_scheduler/task_unittest.py
+++ b/site_utils/suite_scheduler/task_unittest.py
@@ -10,6 +10,7 @@
# driver must be imported first due to circular imports in base_event and task
import driver # pylint: disable-msg=W0611
+import error
import deduping_scheduler, forgiving_config_parser, task, build_event
@@ -189,7 +190,7 @@
def testCreateFromNoSuiteConfig(self):
"""Ensure we require a suite in Task config."""
self.config.remove_option(self._TASK_NAME, 'suite')
- self.assertRaises(task.MalformedConfigEntry,
+ self.assertRaises(error.MalformedConfigEntry,
task.Task.CreateFromConfigSection,
self.config,
self._TASK_NAME)
@@ -198,7 +199,7 @@
def testCreateFromNoKeywordConfig(self):
"""Ensure we require a run_on event in Task config."""
self.config.remove_option(self._TASK_NAME, 'run_on')
- self.assertRaises(task.MalformedConfigEntry,
+ self.assertRaises(error.MalformedConfigEntry,
task.Task.CreateFromConfigSection,
self.config,
self._TASK_NAME)
@@ -206,7 +207,7 @@
def testCreateFromNonexistentConfig(self):
"""Ensure we fail gracefully if we pass in a bad section name."""
- self.assertRaises(task.MalformedConfigEntry,
+ self.assertRaises(error.MalformedConfigEntry,
task.Task.CreateFromConfigSection,
self.config,
'not_a_thing')
@@ -216,7 +217,7 @@
"""Ensure testbed_dut_count specified in boards is only applicable for
testing Launch Control builds."""
self.config.set(self._TASK_NAME, 'boards', 'shamu-2')
- self.assertRaises(task.MalformedConfigEntry,
+ self.assertRaises(error.MalformedConfigEntry,
task.Task.CreateFromConfigSection,
self.config,
self._TASK_NAME)