blob: 580f67cefee563d52f2982d06312f3a34b5e70bc [file] [log] [blame]
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +08001#!/usr/bin/python -Bu
2#
3# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Factory toolkit installer.
8
9The factory toolkit is a self-extracting shellball containing factory test
10related files and this installer. This installer is invoked when the toolkit
11is deployed and is responsible for installing files.
12"""
13
14
15import argparse
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080016from contextlib import contextmanager
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080017import os
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080018import sys
19
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080020import factory_common # pylint: disable=W0611
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080021from cros.factory.test import factory
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080022from cros.factory.tools.mount_partition import MountPartition
23from cros.factory.utils.process_utils import Spawn
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080024
25
26class FactoryToolkitInstaller():
27 """Factory toolkit installer.
28
29 Args:
30 src: Source path containing usr/ and var/.
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080031 dest: Installation destination path. Set this to the mount point of the
32 stateful partition if patching a test image.
33 no_enable: True to not install the tag file.
34 system_root: The path to the root of the file system. This must be left
35 as its default value except for unit testing.
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080036 """
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080037
38 def __init__(self, src, dest, no_enable, system_root='/'):
39 self._system_root = system_root
40 if dest == self._system_root:
41 self._usr_local_dest = os.path.join(dest, 'usr', 'local')
42 self._var_dest = os.path.join(dest, 'var')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080043 if os.getuid() != 0:
44 raise Exception('Must be root to install on live machine!')
45 if not os.path.exists('/etc/lsb-release'):
46 raise Exception('/etc/lsb-release is missing. '
47 'Are you running this in chroot?')
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080048 else:
49 self._usr_local_dest = os.path.join(dest, 'dev_image')
50 self._var_dest = os.path.join(dest, 'var_overlay')
51 if (not os.path.exists(self._usr_local_dest) or
52 not os.path.exists(self._var_dest)):
53 raise Exception(
54 'The destination path %s is not a stateful partition!' % dest)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080055
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080056 self._dest = dest
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080057 self._usr_local_src = os.path.join(src, 'usr', 'local')
58 self._var_src = os.path.join(src, 'var')
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080059 self._no_enable = no_enable
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +080060 self._tag_file = os.path.join(self._usr_local_dest, 'factory', 'enabled')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080061
62 if (not os.path.exists(self._usr_local_src) or
63 not os.path.exists(self._var_src)):
64 raise Exception(
65 'This installer must be run from within the factory toolkit!')
66
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080067 def WarningMessage(self, target_test_image=None):
68 if target_test_image:
69 ret = (
70 '\n'
71 '\n'
72 '*** You are about to patch factory toolkit into:\n'
73 '*** %s\n'
74 '***' % target_test_image)
75 else:
76 ret = (
77 '\n'
78 '\n'
79 '*** You are about to install factory toolkit to:\n'
80 '*** %s\n'
81 '***' % self._dest)
82 if self._dest == self._system_root:
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +080083 if self._no_enable:
84 ret += ('\n'
85 '*** Factory tests will be disabled after this process is done, but\n'
86 '*** you can enable them by creating factory enabled tag:\n'
87 '*** %s\n'
88 '***' % self._tag_file)
89 else:
90 ret += ('\n'
91 '*** After this process is done, your device will start factory\n'
92 '*** tests on the next reboot.\n'
93 '***\n'
94 '*** Factory tests can be disabled by deleting factory enabled tag:\n'
95 '*** %s\n'
96 '***' % self._tag_file)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080097 return ret
98
99 def _Rsync(self, src, dest):
100 print '*** %s -> %s' % (src, dest)
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800101 Spawn(['rsync', '-a', src + '/', dest],
102 sudo=True, log=True, check_output=True)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800103
104 def Install(self):
105 print '*** Installing factory toolkit...'
106 self._Rsync(self._usr_local_src, self._usr_local_dest)
107 self._Rsync(self._var_src, self._var_dest)
108
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800109 if self._no_enable:
110 print '*** Removing factory enabled tag...'
111 try:
112 os.unlink(self._tag_file)
113 except OSError:
114 pass
115 else:
116 print '*** Installing factory enabled tag...'
117 open(self._tag_file, 'w').close()
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800118
119 print '*** Installation completed.'
120
121
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800122@contextmanager
123def DummyContext(arg):
124 """A context manager that simply yields its argument."""
125 yield arg
126
127
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800128def PrintBuildInfo(src_root):
129 """Print build information."""
130 info_file = os.path.join(src_root, 'REPO_STATUS')
131 if not os.path.exists(info_file):
132 raise OSError('Build info file not found!')
133 with open(info_file, 'r') as f:
134 print f.read()
135
136
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800137def main():
138 parser = argparse.ArgumentParser(
139 description='Factory toolkit installer.')
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800140 parser.add_argument('dest', nargs='?', default='/',
141 help='A test image or the mount point of the stateful partition. '
142 "If omitted, install to live system, i.e. '/'.")
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800143 parser.add_argument('--no-enable', '-n', action='store_true',
144 help="Don't enable factory tests after installing")
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800145 parser.add_argument('--yes', '-y', action='store_true',
146 help="Don't ask for confirmation")
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800147 parser.add_argument('--build-info', action='store_true',
148 help="Print build information and exit")
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800149 args = parser.parse_args()
150
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800151 src_root = factory.FACTORY_PATH
152 for _ in xrange(3):
153 src_root = os.path.dirname(src_root)
154
155 if args.build_info:
156 PrintBuildInfo(src_root)
157 return
158
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800159 # Change to original working directory in case the user specifies
160 # a relative path.
161 # TODO: Use USER_PWD instead when makeself is upgraded
162 os.chdir(os.environ['OLDPWD'])
163
164 if not os.path.exists(args.dest):
165 parser.error('Destination %s does not exist!' % args.dest)
166
167 patch_test_image = os.path.isfile(args.dest)
168
169 with (MountPartition(args.dest, 1, rw=True) if patch_test_image
170 else DummyContext(args.dest)) as dest:
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800171 installer = FactoryToolkitInstaller(src_root, dest, args.no_enable)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800172
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800173 print installer.WarningMessage(args.dest if patch_test_image else None)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800174
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800175 if not args.yes:
176 answer = raw_input('*** Continue? [y/N] ')
177 if not answer or answer[0] not in 'yY':
178 sys.exit('Aborting.')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800179
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800180 installer.Install()
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800181
182if __name__ == '__main__':
183 main()