Implement the base class of Fully Automated Firmware Test Sequence.

Many firmware tests require several reboot cycles and verify the resulted
system states. To do that, an Autotest test case should detailly handle
every action on each step. It makes the test case hard to read and many
duplicated code. The base class FAFTSequence is to solve this problem.

The actions of one reboot cycle is defined in a dict, namely FAFT_STEP.
There are four functions in the FAFT_STEP dict:
    state_checker: a function to check the current is valid or not,
        returning True if valid, otherwise, False to break the whole
        test sequence.
    userspace_action: a function to describe the action ran in userspace.
    reboot_action: a function to do reboot, default: software_reboot.
    firmware_action: a function to describe the action ran after reboot.

The default FAFT_STEP checks nothing in state_checker and does nothing in
userspace_action and firmware_action. Its reboot_action is simply a
software reboot. You can change the default FAFT_STEP by calling
self.register_faft_template(FAFT_STEP).

A FAFT test case consists of several FAFT_STEP's, namely FAFT_SEQUENCE.
FAFT_SEQUENCE is an array of FAFT_STEP's. Any missing fields on FAFT_STEP
fall back to default.

In the run_once(), it should register and run FAFT_SEQUENCE like:
    def run_once(self):
        self.register_faft_sequence(FAFT_SEQUENCE)
        self.run_faft_sequnce()

Note that in the last step, we only run state_checker. The
userspace_action, reboot_action, and firmware_action are not executed.

BUG=chromium-os:19710
TEST=Pending test to firmware_DevMode in a later CL.

Change-Id: I402ad8b43f2a30b0e3c56cc25c3191c3a5db6443
Reviewed-on: http://gerrit.chromium.org/gerrit/7158
Commit-Ready: Tom Wai-Hong Tam <waihong@chromium.org>
Reviewed-by: Tom Wai-Hong Tam <waihong@chromium.org>
Tested-by: Tom Wai-Hong Tam <waihong@chromium.org>
diff --git a/server/cros/servotest.py b/server/cros/servotest.py
index c3686ec..402092c 100755
--- a/server/cros/servotest.py
+++ b/server/cros/servotest.py
@@ -203,6 +203,20 @@
                 logging.info('Server: Relaunched remote %s.' % name)
 
 
+    def wait_for_client_offline(self, timeout=30):
+        """Wait for the client to come offline.
+
+        Args:
+          timeout: Time in seconds to wait the client to come offline.
+        """
+        # Wait for the client to come offline.
+        while timeout > 0 and self.ping_test(self._client.ip, timeout=1):
+            time.sleep(1)
+            timeout -= 1
+        assert timeout, 'Timed out waiting for client offline.'
+        logging.info('Server: Client machine is offline.')
+
+
     def cleanup(self):
         """Delete the Servo object, call remote cleanup, and kill ssh."""
         if self.servo: