[autotest] Pipe check_hosts parameter down into dynamic_suite

The builders need to schedule a test suite and then wait for it to
complete.  They probably want to check to make sure the suite can be
scheduled, and error out if not.  That's great.  For suites that we
schedule once a day or asynchronously in some other way, we probably
don't want to bail just because the testbed has too many hosts that
need Repair or something.  We want to fire and forget.

To support these two modes of operation, pipe a 'check_hosts' paramter
all the way from the create_suite_job() RPC, down through control
files, all the way to dynamic_suite.reimage_and_run().  Ensure that we
tolerate check_hosts being left unset in reimage_and_run().  Also,
make 'atest suite create' able to toggle this value.

BUG=chromium-os:27526
TEST=dynamic_suite_unittest.py, site_rpc_interface_unittest.py
TEST=./server/autoserv test_suites/dev_harness with check_hosts set both ways.
TEST=install the patch on an autotest instance, and re-run a test suite against the new interface.  The suite should check for hosts and behave appropriately.

Change-Id: I10c3f42dbc37f26d7af3c40439ce212ebf74cfcd
Reviewed-on: https://gerrit.chromium.org/gerrit/17633
Tested-by: Chris Masone <cmasone@chromium.org>
Reviewed-by: Scott Zawalski <scottz@chromium.org>
Commit-Ready: Chris Masone <cmasone@chromium.org>
diff --git a/server/cros/dynamic_suite_unittest.py b/server/cros/dynamic_suite_unittest.py
index 311f678..d755c26 100755
--- a/server/cros/dynamic_suite_unittest.py
+++ b/server/cros/dynamic_suite_unittest.py
@@ -55,12 +55,13 @@
                        'num': 1,
                        'pool': 'pool',
                        'skip_reimage': True,
+                       'check_hosts': False,
                        'add_experimental': False}
 
 
     def testVetRequiredReimageAndRunArgs(self):
         """Should verify only that required args are present and correct."""
-        build, board, name, job, _, _, _, _ = \
+        build, board, name, job, _, _, _, _,_ = \
             dynamic_suite._vet_reimage_and_run_args(**self._DARGS)
         self.assertEquals(build, self._DARGS['build'])
         self.assertEquals(board, self._DARGS['board'])
@@ -102,10 +103,11 @@
 
     def testOverrideOptionalReimageAndRunArgs(self):
         """Should verify that optional args can be overridden."""
-        _, _, _, _, pool, num, skip, expr = \
+        _, _, _, _, pool, num, check, skip, expr = \
             dynamic_suite._vet_reimage_and_run_args(**self._DARGS)
         self.assertEquals(pool, self._DARGS['pool'])
         self.assertEquals(num, self._DARGS['num'])
+        self.assertEquals(check, self._DARGS['check_hosts'])
         self.assertEquals(skip, self._DARGS['skip_reimage'])
         self.assertEquals(expr, self._DARGS['add_experimental'])
 
@@ -114,12 +116,14 @@
         """Should verify that optional args get defaults."""
         del(self._DARGS['pool'])
         del(self._DARGS['skip_reimage'])
+        del(self._DARGS['check_hosts'])
         del(self._DARGS['add_experimental'])
         del(self._DARGS['num'])
-        _, _, _, _, pool, num, skip, expr = \
+        _, _, _, _, pool, num, check, skip, expr = \
             dynamic_suite._vet_reimage_and_run_args(**self._DARGS)
         self.assertEquals(pool, None)
         self.assertEquals(num, None)
+        self.assertEquals(check, True)
         self.assertEquals(skip, False)
         self.assertEquals(expr, True)
 
@@ -308,7 +312,7 @@
         self.afe.run('delete_label', id=label.id)
 
 
-    def expect_attempt(self, success, ex=None):
+    def expect_attempt(self, success, ex=None, check_hosts=True):
         """Sets up |self.reimager| to expect an attempt() that returns |success|
 
         Also stubs out Reimger._clear_build_state(), should the caller wish
@@ -326,9 +330,10 @@
         self.reimager._schedule_reimage_job(self._BUILD,
                                             self._NUM,
                                             self._BOARD).AndReturn(canary)
-
-        self.mox.StubOutWithMock(self.reimager, '_count_usable_hosts')
-        self.reimager._count_usable_hosts(mox.IgnoreArg()).AndReturn(self._NUM)
+        if check_hosts:
+            self.mox.StubOutWithMock(self.reimager, '_count_usable_hosts')
+            self.reimager._count_usable_hosts(
+                mox.IgnoreArg()).AndReturn(self._NUM)
 
         if success is not None:
             self.mox.StubOutWithMock(self.reimager, '_report_results')
@@ -352,59 +357,76 @@
 
     def testSuccessfulReimage(self):
         """Should attempt a reimage and record success."""
-        canary = self.expect_attempt(True)
+        canary = self.expect_attempt(success=True)
 
         rjob = self.mox.CreateMock(base_job.base_job)
         rjob.record('START', mox.IgnoreArg(), mox.IgnoreArg())
         rjob.record('END GOOD', mox.IgnoreArg(), mox.IgnoreArg())
         self.reimager._clear_build_state(mox.StrContains(canary.hostname))
         self.mox.ReplayAll()
-        self.reimager.attempt(self._BUILD, self._BOARD, rjob.record)
+        self.reimager.attempt(self._BUILD, self._BOARD, rjob.record, True)
         self.reimager.clear_reimaged_host_state(self._BUILD)
 
 
     def testFailedReimage(self):
         """Should attempt a reimage and record failure."""
-        canary = self.expect_attempt(False)
+        canary = self.expect_attempt(success=False)
 
         rjob = self.mox.CreateMock(base_job.base_job)
         rjob.record('START', mox.IgnoreArg(), mox.IgnoreArg())
         rjob.record('END FAIL', mox.IgnoreArg(), mox.IgnoreArg())
         self.reimager._clear_build_state(mox.StrContains(canary.hostname))
         self.mox.ReplayAll()
-        self.reimager.attempt(self._BUILD, self._BOARD, rjob.record)
+        self.reimager.attempt(self._BUILD, self._BOARD, rjob.record, True)
         self.reimager.clear_reimaged_host_state(self._BUILD)
 
 
     def testReimageThatNeverHappened(self):
         """Should attempt a reimage and record that it didn't run."""
-        canary = self.expect_attempt(None)
+        canary = self.expect_attempt(success=None)
 
         rjob = self.mox.CreateMock(base_job.base_job)
         rjob.record('START', mox.IgnoreArg(), mox.IgnoreArg())
         rjob.record('FAIL', mox.IgnoreArg(), canary.name, mox.IgnoreArg())
         rjob.record('END FAIL', mox.IgnoreArg(), mox.IgnoreArg())
         self.mox.ReplayAll()
-        self.reimager.attempt(self._BUILD, self._BOARD, rjob.record)
+        self.reimager.attempt(self._BUILD, self._BOARD, rjob.record, True)
         self.reimager.clear_reimaged_host_state(self._BUILD)
 
 
     def testReimageThatRaised(self):
         """Should attempt a reimage that raises an exception and record that."""
         ex_message = 'Oh no!'
-        canary = self.expect_attempt(None, Exception(ex_message))
+        canary = self.expect_attempt(success=None, ex=Exception(ex_message))
 
         rjob = self.mox.CreateMock(base_job.base_job)
         rjob.record('START', mox.IgnoreArg(), mox.IgnoreArg())
         rjob.record('END ERROR', mox.IgnoreArg(), mox.IgnoreArg(), ex_message)
         self.mox.ReplayAll()
-        self.reimager.attempt(self._BUILD, self._BOARD, rjob.record)
+        self.reimager.attempt(self._BUILD, self._BOARD, rjob.record, True)
+        self.reimager.clear_reimaged_host_state(self._BUILD)
+
+
+    def testSuccessfulReimageThatCouldNotScheduleRightAway(self):
+        """
+        Should attempt a reimage, ignoring host availability and record success.
+        """
+        canary = self.expect_attempt(success=True, check_hosts=False)
+
+        rjob = self.mox.CreateMock(base_job.base_job)
+        rjob.record('START', mox.IgnoreArg(), mox.IgnoreArg())
+        rjob.record('END GOOD', mox.IgnoreArg(), mox.IgnoreArg())
+        self.reimager._clear_build_state(mox.StrContains(canary.hostname))
+        self.mox.ReplayAll()
+        self.reimager.attempt(self._BUILD, self._BOARD, rjob.record, False)
         self.reimager.clear_reimaged_host_state(self._BUILD)
 
 
     def testReimageThatCouldNotSchedule(self):
         """Should attempt a reimage that can't be scheduled."""
         canary = FakeJob()
+        self.mox.StubOutWithMock(self.reimager, '_ensure_version_label')
+        self.reimager._ensure_version_label(mox.StrContains(self._BUILD))
 
         self.mox.StubOutWithMock(self.reimager, '_count_usable_hosts')
         self.reimager._count_usable_hosts(mox.IgnoreArg()).AndReturn(0)
@@ -415,7 +437,7 @@
                     mox.StrContains('Too few hosts'))
         self.expect_label_cleanup(self._BUILD)
         self.mox.ReplayAll()
-        self.reimager.attempt(self._BUILD, self._BOARD, rjob.record)
+        self.reimager.attempt(self._BUILD, self._BOARD, rjob.record, True)
         self.reimager.clear_reimaged_host_state(self._BUILD)