Add options to install factory server using toolkit

Umpire is the unified factory server that distributes inside factory
toolkit. This change adds an option to setup factory server when running
install_factory_toolkit.run.

BUG=None
TEST=manual
  prepare a newly installed debian VM
  insatll dependencies:
    apt-get install python-yaml python-netifaces python-pexpect \
    python-numpy python-twisted python-twisted-web
  install factory toolkit:
    ./install_factory_toolkit.run -- --init-umpire-board=exampleboard
  optional arg:
    --exe-path=path/to/another/install_factory_toolkit.run

Change-Id: I31e9a78542d4f8fd506d9587550f333ce193f496
Reviewed-on: https://chromium-review.googlesource.com/213761
Commit-Queue: Rong Chang <rongchang@chromium.org>
Tested-by: Rong Chang <rongchang@chromium.org>
Reviewed-by: Dean Liao <deanliao@chromium.org>
diff --git a/py/toolkit/installer.py b/py/toolkit/installer.py
index e9eb632..3c5c39f 100755
--- a/py/toolkit/installer.py
+++ b/py/toolkit/installer.py
@@ -23,10 +23,13 @@
 from cros.factory.test import utils
 from cros.factory.tools import install_symlinks
 from cros.factory.tools.mount_partition import MountPartition
+from cros.factory.utils import file_utils
 from cros.factory.utils.process_utils import Spawn
 
 
 INSTALLER_PATH = 'usr/local/factory/py/toolkit/installer.py'
+MAKESELF_SHELL = '/bin/sh'
+TOOLKIT_NAME = 'install_factory_toolkit.run'
 
 # Short and sweet help header for the executable generated by makeself.
 HELP_HEADER = """
@@ -276,6 +279,41 @@
       '\n' % (output_path, output_path))
 
 
+def InitUmpire(exe_path, src_root, target_board):
+  """Inits Umpire server environment."""
+  if exe_path is None:
+    parent_cmdline = open('/proc/%s/cmdline' % os.getppid(),
+                          'r').read().rstrip('\0').split('\0')
+
+    if parent_cmdline > 1 and parent_cmdline[0] == MAKESELF_SHELL:
+      # Get parent script name from parent process.
+      exe_path = parent_cmdline[1]
+    else:
+      # Set to default.
+      exe_path = TOOLKIT_NAME
+
+  if not exe_path.startswith('/'):
+    exe_path = os.path.join(os.environ.get('OLDPWD'), exe_path)
+
+  with file_utils.TempDirectory() as nano_bundle:
+    bundle_toolkit_dir = os.path.join(nano_bundle, 'factory_toolkit')
+    os.mkdir(bundle_toolkit_dir)
+    os.symlink(exe_path, os.path.join(bundle_toolkit_dir,
+                                      os.path.basename(exe_path)))
+    umpire_bin = os.path.join(src_root, 'usr', 'local', 'factory', 'bin',
+                                  'umpire')
+    Spawn([umpire_bin, 'init', '--board', target_board, nano_bundle],
+          check_call=True, log=True)
+    print ('\n'
+        '  Umpire initialized successfully. Upstart service is running:\n'
+        '    umpire BOARD=%(board)s.\n'
+        '  For more information, please check umpire command line:\n'
+        '\n'
+        '    umpire-%(board)s --help  (if your id is in umpire group)\n'
+        '    or sudo umpire-%(board)s --help\n'
+        '\n' % {'board': target_board})
+
+
 def main():
   import logging
   logging.basicConfig(level=logging.INFO)
@@ -320,6 +358,12 @@
       help="Run goofy in device mode on startup")
   parser.add_argument('--no-enable-device', dest='enable_device',
       action='store_false', help=argparse.SUPPRESS)
+  parser.add_argument('--init-umpire-board', dest='umpire_board',
+      nargs='?', default=None,
+      help="Locally install Umpire server for specific board")
+  parser.add_argument('--exe-path', dest='exe_path',
+      nargs='?', default=None,
+      help="Current self-extracting archive pathname")
   parser.set_defaults(enable_device=True)
 
   args = parser.parse_args()
@@ -328,6 +372,12 @@
   for _ in xrange(3):
     src_root = os.path.dirname(src_root)
 
+  # --init-umpire-board creates a nano bundle, then calls umpire command
+  # line utility to install the server code and upstart configurations.
+  if args.umpire_board:
+    InitUmpire(args.exe_path, src_root, args.umpire_board)
+    return
+
   # --pack-into may be called directly so this must be done before changing
   # working directory to OLDPWD.
   if args.pack_into and args.repack is None: