autotest: add board_lists for suite_scheduler
This CL adds board_lists for suite_scheduler, to let CrOS tasks re-use
board whitelist by specifying a pre-defined list in option 'boards:'.
BUG=chromium:717074
TEST=Run driver_unittest & task_unittest.
Run ./suite_scheduler.py --sanity.
Run ./suite_scheduler.py -d /usr/local/autotest/logs -f
xixuan_test_1.ini -r /tmp/_autotmp__suite_scheduler
Change-Id: I2c7811cf8041834e5d5779c373fea425577374fe
Reviewed-on: https://chromium-review.googlesource.com/494887
Commit-Ready: Xixuan Wu <xixuan@chromium.org>
Tested-by: Xixuan Wu <xixuan@chromium.org>
Reviewed-by: Xixuan Wu <xixuan@chromium.org>
diff --git a/site_utils/suite_scheduler/driver.py b/site_utils/suite_scheduler/driver.py
index e41e8e2..3dcc964 100644
--- a/site_utils/suite_scheduler/driver.py
+++ b/site_utils/suite_scheduler/driver.py
@@ -22,6 +22,9 @@
POOL_SIZE = 32
+BOARD_WHITELIST_SECTION = 'board_lists'
+PRE_SECTIONS = [BOARD_WHITELIST_SECTION]
+
class Driver(object):
"""Implements the main loop of the suite_scheduler.
@@ -76,6 +79,26 @@
self._events = self._CreateEventsWithTasks(config, mv)
+ 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 task.MalformedConfigEntry(
+ 'Board list name must be unique.')
+ else:
+ board_lists[option] = config.getstring(
+ BOARD_WHITELIST_SECTION, option)
+
+ return board_lists
+
+
def _CreateEventsWithTasks(self, config, mv):
"""Create task lists from config, and assign to newly-minted events.
@@ -109,12 +132,14 @@
@return dict of {event_keyword: [tasks]} mappings.
@raise MalformedConfigEntry on a task parsing error.
"""
+ board_lists = self._ReadBoardWhitelist(config)
tasks = {}
for section in config.sections():
- if not base_event.HonoredSection(section):
+ if (not base_event.HonoredSection(section) and
+ section not in PRE_SECTIONS):
try:
keyword, new_task = task.Task.CreateFromConfigSection(
- config, section)
+ config, section, board_lists=board_lists)
except task.MalformedConfigEntry as e:
logging.warning('%s is malformed: %s', section, str(e))
continue
diff --git a/site_utils/suite_scheduler/driver_unittest.py b/site_utils/suite_scheduler/driver_unittest.py
index 0173b74..9640597 100644
--- a/site_utils/suite_scheduler/driver_unittest.py
+++ b/site_utils/suite_scheduler/driver_unittest.py
@@ -65,15 +65,24 @@
return [mock_nightly, mock_weekly, mock_new_build]
+ def _ExpectBoardListConfig(self):
+ board_lists = 'sub_board1,sub_board2'
+ self.config.add_section(driver.BOARD_WHITELIST_SECTION)
+ self.config.set(driver.BOARD_WHITELIST_SECTION,
+ self._BOARDS[0], board_lists)
+
+
def _ExpectTaskConfig(self):
self.config.add_section(timed_event.Nightly.KEYWORD)
self.config.add_section(timed_event.Weekly.KEYWORD)
self.mox.StubOutWithMock(task.Task, 'CreateFromConfigSection')
task.Task.CreateFromConfigSection(
- self.config, timed_event.Nightly.KEYWORD).InAnyOrder().AndReturn(
+ self.config, timed_event.Nightly.KEYWORD,
+ board_lists=mox.IgnoreArg()).InAnyOrder().AndReturn(
(timed_event.Nightly.KEYWORD, self.nightly_bvt))
task.Task.CreateFromConfigSection(
- self.config, timed_event.Weekly.KEYWORD).InAnyOrder().AndReturn(
+ self.config, timed_event.Weekly.KEYWORD,
+ board_lists=mox.IgnoreArg()).InAnyOrder().AndReturn(
(timed_event.Weekly.KEYWORD, self.weekly_bvt))
@@ -125,6 +134,7 @@
def testTasksFromConfig(self):
"""Test that we can build a list of Tasks from a config."""
self._ExpectTaskConfig()
+ self._ExpectBoardListConfig()
self.mox.ReplayAll()
tasks = self.driver.TasksFromConfig(self.config)
self.assertTrue(self.nightly_bvt in tasks[timed_event.Nightly.KEYWORD])
@@ -135,6 +145,7 @@
"""Test that we can build a list of Tasks from a config twice."""
events = self._ExpectSetup()
self._ExpectTaskConfig()
+ self._ExpectBoardListConfig()
self.mox.ReplayAll()
self.driver.SetUpEventsAndTasks(self.config, self.mv)
@@ -153,7 +164,8 @@
self._ExpectSetup()
self.mox.StubOutWithMock(task.Task, 'CreateFromConfigSection')
task.Task.CreateFromConfigSection(
- self.config, timed_event.Nightly.KEYWORD).InAnyOrder().AndReturn(
+ self.config, timed_event.Nightly.KEYWORD,
+ board_lists=mox.IgnoreArg()).InAnyOrder().AndReturn(
(timed_event.Nightly.KEYWORD, self.nightly_bvt))
self.mox.ReplayAll()
self.driver.SetUpEventsAndTasks(self.config, self.mv)
@@ -167,6 +179,7 @@
def testHandleAllEventsOnce(self):
"""Test that all events being ready is handled correctly."""
events = self._ExpectSetup()
+ self._ExpectBoardListConfig()
self._ExpectEnumeration()
launch_control_build_called = False
for event in events:
@@ -182,6 +195,7 @@
def testHandleNightlyEventOnce(self):
"""Test that one ready event is handled correctly."""
events = self._ExpectSetup()
+ self._ExpectBoardListConfig()
self._ExpectEnumeration()
for event in events:
if event.keyword == timed_event.Nightly.KEYWORD:
@@ -198,6 +212,7 @@
def testForceOnceForBuild(self):
"""Test that one event being forced is handled correctly."""
events = self._ExpectSetup()
+ self._ExpectBoardListConfig()
board = 'board'
type = 'release'
diff --git a/site_utils/suite_scheduler/task.py b/site_utils/suite_scheduler/task.py
index 0036a1d..325809c 100644
--- a/site_utils/suite_scheduler/task.py
+++ b/site_utils/suite_scheduler/task.py
@@ -148,7 +148,7 @@
@staticmethod
- def CreateFromConfigSection(config, section):
+ def CreateFromConfigSection(config, section, board_lists={}):
"""Create a Task from a section of a config file.
The section to parse should look like this:
@@ -174,6 +174,7 @@
@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|.
"""
@@ -299,6 +300,20 @@
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,
diff --git a/site_utils/suite_scheduler/task_unittest.py b/site_utils/suite_scheduler/task_unittest.py
index 8dc802f..0ffa047 100644
--- a/site_utils/suite_scheduler/task_unittest.py
+++ b/site_utils/suite_scheduler/task_unittest.py
@@ -81,6 +81,28 @@
self.assertFalse(new_task._FitsSpec('12'))
+ def testCreateFromConfigCheckBoards(self):
+ """Ensure a CrOS Task can be built from a correct config boards."""
+ board_whitelist = 'board2,board3'
+ board_lists = {self._BOARD: board_whitelist}
+ keyword, new_task = task.Task.CreateFromConfigSection(
+ self.config, self._TASK_NAME, board_lists=board_lists)
+ self.assertEquals(keyword, self._EVENT_KEY)
+ self.assertEquals(new_task.boards,
+ set([x.strip() for x in board_whitelist.split(',')]))
+
+
+ def testCreateFromConfigCheckNonExistBoards(self):
+ """Ensure a CrOS Task can be built if board_list is not specified."""
+ board_whitelist = 'board2,board3'
+ board_lists = {'test-%s' % self._BOARD: board_whitelist}
+ keyword, new_task = task.Task.CreateFromConfigSection(
+ self.config, self._TASK_NAME, board_lists=board_lists)
+ self.assertEquals(keyword, self._EVENT_KEY)
+ self.assertEquals(new_task.boards,
+ set([x.strip() for x in self._BOARD.split(',')]))
+
+
def testCreateFromConfigEqualBranch(self):
"""Ensure a Task can be built from a correct config with support of
branch_specs: ==RXX."""