[autotest] Vm Manager for lab testing environment.

This creates a vm manager capable of the following:
* Creating a VagrantFile based on a template
* Registering/unregistering boxes (disk images)
* Provisioning a cluster using the template and registered boxes

By using vagrant as an interface we can have multiple backends
like GCE, kwm/qemu, virtualbox; This cl only implements a VirtualBox
vm_manager.

BUG=chromium:433150
TEST=Provisioned vms.

Change-Id: Ied20560ae0fa21504d3ce44df15bb0bfc0060a45
Reviewed-on: https://chromium-review.googlesource.com/229699
Tested-by: Prashanth B <beeps@chromium.org>
Reviewed-by: Fang Deng <fdeng@chromium.org>
Commit-Queue: Prashanth B <beeps@chromium.org>
diff --git a/site_utils/lib/infra.py b/site_utils/lib/infra.py
index 68a4e1f..506e3bd 100644
--- a/site_utils/lib/infra.py
+++ b/site_utils/lib/infra.py
@@ -2,8 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import contextlib
 import getpass
 import subprocess
+import os
 
 import common
 from autotest_lib.server.hosts import ssh_host
@@ -13,20 +15,44 @@
 from autotest_lib.server.cros.dynamic_suite import frontend_wrappers
 
 
-def local_runner(cmd):
+@contextlib.contextmanager
+def chdir(dirname=None):
+    """A context manager to help change directories.
+
+    Will chdir into the provided dirname for the lifetime of the context and
+    return to cwd thereafter.
+
+    @param dirname: The dirname to chdir into.
+    """
+    curdir = os.getcwd()
+    try:
+        if dirname is not None:
+            os.chdir(dirname)
+        yield
+    finally:
+        os.chdir(curdir)
+
+
+def local_runner(cmd, stream_output=False):
     """
     Runs a command on the local system as the current user.
 
     @param cmd: The command to run.
+    @param stream_output: If True, streams the stdout of the process.
+
     @returns: The output of cmd.
     @raises CalledProcessError: If there was a non-0 return code.
     """
-    return subprocess.check_output(cmd, shell=True)
+    if not stream_output:
+        return subprocess.check_output(cmd, shell=True)
+    proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
+    while proc.poll() is None:
+        print proc.stdout.readline().rstrip('\n')
 
 
 _host_objects = {}
 
-def host_object_runner(host):
+def host_object_runner(host, **kwargs):
     """
     Returns a function that returns the output of running a command via a host
     object.
@@ -60,7 +86,7 @@
     return runner
 
 
-def become_runner(host):
+def become_runner(host, **kwargs):
     """
     Returns a function that return the output of running a command via shelling
     out to `become`.
@@ -82,7 +108,7 @@
     return runner
 
 
-def execute_command(host, cmd):
+def execute_command(host, cmd, **kwargs):
     """
     Executes a command on the host `host`.  This an optimization that if
     we're already chromeos-test, we can just ssh to the machine in question.
@@ -91,6 +117,7 @@
     @param host: The hostname to execute the command on.
     @param cmd: The command to run.  Special shell syntax (such as pipes)
                 is allowed.
+    @param kwargs: Key word arguments for the runner functions.
     @returns: The output of the command.
     """
     if utils.is_localhost(host):
@@ -100,7 +127,7 @@
     else:
         runner = become_runner(host)
 
-    return runner(cmd)
+    return runner(cmd, **kwargs)
 
 
 def _csv_to_list(s):