Added sysinfo support to log pre and post iteration (currently it logs
the contents of /proc/schedstat). Added client.common_lib.test hooks to
call pre and post iteration. Used the hooks in client.bin.test.runtest()
for client tests. Refactored server.test and added wrappers for the
pre/post iteration logging and used them on the new pre/post iteration
hooks.

For each iteration there will be a
resultsdir/sysinfo/iteration.N/schedstat.after and schedstat.before.

If there are tests that override execute() and implement their own 
iteration logic they need to be changed over to override run_once() 
instead and take advantage of the default execute() that supports the 
pre/post iteration hooks and other things.

Signed-off-by: Mihai Rusu <[email protected]>



git-svn-id: http://test.kernel.org/svn/autotest/trunk@2929 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/server/test.py b/server/test.py
index d1eecf9..00e81a7 100755
--- a/server/test.py
+++ b/server/test.py
@@ -12,7 +12,7 @@
     pass
 
 
-_sysinfo_before_script = """\
+_sysinfo_before_test_script = """\
 import pickle
 from autotest_lib.client.bin import test
 mytest = test.test(job, '', %r)
@@ -22,7 +22,7 @@
 job.record('GOOD', '', 'sysinfo.before')
 """
 
-_sysinfo_after_script = """\
+_sysinfo_after_test_script = """\
 import pickle
 from autotest_lib.client.bin import test
 mytest = test.test(job, '', %r)
@@ -34,6 +34,23 @@
 job.record('GOOD', '', 'sysinfo.after')
 """
 
+# this script is ran after _sysinfo_before_test_script and before
+# _sysinfo_after_test_script which means the pickle file exists
+# already and should be dumped with updated state for
+# _sysinfo_after_test_script to pick it up later
+_sysinfo_iteration_script = """\
+import pickle
+from autotest_lib.client.bin import test
+mytest = test.test(job, '', %r)
+sysinfo_pickle = os.path.join(mytest.outputdir, 'sysinfo.pickle')
+if os.path.exists(sysinfo_pickle):
+    job.sysinfo = pickle.load(open(sysinfo_pickle))
+    job.sysinfo.__init__(job.resultdir)
+job.sysinfo.%s(mytest, iteration=%d)
+pickle.dump(job.sysinfo, open(sysinfo_pickle, 'w'))
+job.record('GOOD', '', 'sysinfo.iteration.%s')
+"""
+
 
 class _sysinfo_logger(object):
     def __init__(self, job):
@@ -53,38 +70,28 @@
         return host, at
 
 
-    @log.log_and_ignore_errors("pre-test server sysinfo error:")
-    def before_hook(self, mytest):
-        host, at = self._install()
-        outputdir = host.get_tmp_dir()
-
-        # run the pre-test sysinfo script
-        at.run(_sysinfo_before_script % outputdir,
-               results_dir=self.job.resultdir)
-
-        # pull back the sysinfo pickle
+    def _pull_pickle(self, host, outputdir):
+        """Pulls from the client the pickle file with the saved sysinfo state.
+        """
         fd, path = tempfile.mkstemp(dir=self.job.tmpdir)
         os.close(fd)
         host.get_file(os.path.join(outputdir, "sysinfo.pickle"), path)
         self.pickle = path
 
 
-    @log.log_and_ignore_errors("post-test server sysinfo error:")
-    def after_hook(self, mytest):
-        host, at = self._install()
-        outputdir = host.get_tmp_dir()
-
-        # push the sysinfo pickle out to the remote machine
+    def _push_pickle(self, host, outputdir):
+        """Pushes the server saved sysinfo pickle file to the client.
+        """
         if self.pickle:
             host.send_file(self.pickle,
                            os.path.join(outputdir, "sysinfo.pickle"))
             os.remove(self.pickle)
             self.pickle = None
 
-        # run the post-test sysinfo script
-        at.run(_sysinfo_after_script % outputdir,
-               results_dir=self.job.resultdir)
 
+    def _pull_sysinfo_keyval(self, host, outputdir, mytest):
+        """Pulls sysinfo and keyval data from the client.
+        """
         # pull the sysinfo data back on to the server
         host.get_file(os.path.join(outputdir, "sysinfo"), mytest.outputdir)
 
@@ -97,10 +104,73 @@
         mytest.write_test_keyval(keyval)
 
 
+    @log.log_and_ignore_errors("pre-test server sysinfo error:")
+    def before_hook(self, mytest):
+        host, at = self._install()
+        outputdir = host.get_tmp_dir()
+
+        # run the pre-test sysinfo script
+        at.run(_sysinfo_before_test_script % outputdir,
+               results_dir=self.job.resultdir)
+
+        self._pull_pickle(host, outputdir)
+
+
+    @log.log_and_ignore_errors("pre-test iteration server sysinfo error:")
+    def before_iteration_hook(self, mytest):
+        host, at = self._install()
+        outputdir = host.get_tmp_dir()
+
+        # this function is called after before_hook() se we have sysinfo state
+        # to push to the server
+        self._push_pickle(host, outputdir);
+        # run the pre-test iteration sysinfo script
+        at.run(_sysinfo_iteration_script %
+               (outputdir, 'log_before_each_iteration', mytest.iteration,
+                'before'),
+               results_dir=self.job.resultdir)
+
+        # get the new sysinfo state from the client
+        self._pull_pickle(host, outputdir)
+
+
+    @log.log_and_ignore_errors("post-test iteration server sysinfo error:")
+    def after_iteration_hook(self, mytest):
+        host, at = self._install()
+        outputdir = host.get_tmp_dir()
+
+        # push latest sysinfo state to the client
+        self._push_pickle(host, outputdir);
+        # run the post-test iteration sysinfo script
+        at.run(_sysinfo_iteration_script %
+               (outputdir, 'log_after_each_iteration', mytest.iteration,
+                'after'),
+               results_dir=self.job.resultdir)
+
+        # get the new sysinfo state from the client
+        self._pull_pickle(host, outputdir)
+        self._pull_sysinfo_keyval(host, outputdir, mytest)
+
+
+    @log.log_and_ignore_errors("post-test server sysinfo error:")
+    def after_hook(self, mytest):
+        host, at = self._install()
+        outputdir = host.get_tmp_dir()
+
+        self._push_pickle(host, outputdir);
+        # run the post-test sysinfo script
+        at.run(_sysinfo_after_test_script % outputdir,
+               results_dir=self.job.resultdir)
+
+        self._pull_sysinfo_keyval(host, outputdir, mytest)
+
+
 def runtest(job, url, tag, args, dargs):
     if not dargs.pop('disable_sysinfo', False):
         logger = _sysinfo_logger(job)
-        logging_args = [logger.before_hook, logger.after_hook]
+        logging_args = [logger.before_hook, logger.after_hook,
+                        logger.before_iteration_hook,
+                        logger.after_iteration_hook]
     else:
         logging_args = [None, None]
     common_test.runtest(job, url, tag, args, dargs, locals(), globals(),