blob: 8fbde66d610971baf2fb03d5e00ccaa7cec33b61 [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
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +080026INSTALLER_PATH = 'usr/local/factory/py/toolkit/installer.py'
27
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080028class FactoryToolkitInstaller():
29 """Factory toolkit installer.
30
31 Args:
32 src: Source path containing usr/ and var/.
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080033 dest: Installation destination path. Set this to the mount point of the
34 stateful partition if patching a test image.
35 no_enable: True to not install the tag file.
36 system_root: The path to the root of the file system. This must be left
37 as its default value except for unit testing.
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080038 """
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080039
40 def __init__(self, src, dest, no_enable, system_root='/'):
41 self._system_root = system_root
42 if dest == self._system_root:
43 self._usr_local_dest = os.path.join(dest, 'usr', 'local')
44 self._var_dest = os.path.join(dest, 'var')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080045 if os.getuid() != 0:
46 raise Exception('Must be root to install on live machine!')
47 if not os.path.exists('/etc/lsb-release'):
48 raise Exception('/etc/lsb-release is missing. '
49 'Are you running this in chroot?')
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080050 else:
51 self._usr_local_dest = os.path.join(dest, 'dev_image')
52 self._var_dest = os.path.join(dest, 'var_overlay')
53 if (not os.path.exists(self._usr_local_dest) or
54 not os.path.exists(self._var_dest)):
55 raise Exception(
56 'The destination path %s is not a stateful partition!' % dest)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080057
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080058 self._dest = dest
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080059 self._usr_local_src = os.path.join(src, 'usr', 'local')
60 self._var_src = os.path.join(src, 'var')
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080061 self._no_enable = no_enable
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +080062 self._tag_file = os.path.join(self._usr_local_dest, 'factory', 'enabled')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080063
64 if (not os.path.exists(self._usr_local_src) or
65 not os.path.exists(self._var_src)):
66 raise Exception(
67 'This installer must be run from within the factory toolkit!')
68
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080069 def WarningMessage(self, target_test_image=None):
70 if target_test_image:
71 ret = (
72 '\n'
73 '\n'
74 '*** You are about to patch factory toolkit into:\n'
75 '*** %s\n'
76 '***' % target_test_image)
77 else:
78 ret = (
79 '\n'
80 '\n'
81 '*** You are about to install factory toolkit to:\n'
82 '*** %s\n'
83 '***' % self._dest)
84 if self._dest == self._system_root:
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +080085 if self._no_enable:
86 ret += ('\n'
87 '*** Factory tests will be disabled after this process is done, but\n'
88 '*** you can enable them by creating factory enabled tag:\n'
89 '*** %s\n'
90 '***' % self._tag_file)
91 else:
92 ret += ('\n'
93 '*** After this process is done, your device will start factory\n'
94 '*** tests on the next reboot.\n'
95 '***\n'
96 '*** Factory tests can be disabled by deleting factory enabled tag:\n'
97 '*** %s\n'
98 '***' % self._tag_file)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080099 return ret
100
101 def _Rsync(self, src, dest):
102 print '*** %s -> %s' % (src, dest)
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800103 Spawn(['rsync', '-a', src + '/', dest],
104 sudo=True, log=True, check_output=True)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800105
106 def Install(self):
107 print '*** Installing factory toolkit...'
108 self._Rsync(self._usr_local_src, self._usr_local_dest)
109 self._Rsync(self._var_src, self._var_dest)
110
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800111 if self._no_enable:
112 print '*** Removing factory enabled tag...'
113 try:
114 os.unlink(self._tag_file)
115 except OSError:
116 pass
117 else:
118 print '*** Installing factory enabled tag...'
119 open(self._tag_file, 'w').close()
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800120
121 print '*** Installation completed.'
122
123
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800124@contextmanager
125def DummyContext(arg):
126 """A context manager that simply yields its argument."""
127 yield arg
128
129
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800130def PrintBuildInfo(src_root):
131 """Print build information."""
132 info_file = os.path.join(src_root, 'REPO_STATUS')
133 if not os.path.exists(info_file):
134 raise OSError('Build info file not found!')
135 with open(info_file, 'r') as f:
136 print f.read()
137
138
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800139def PackFactoryToolkit(src_root, output_path):
140 """Packs the files containing this script into a factory toolkit."""
141 with open(os.path.join(src_root, 'VERSION'), 'r') as f:
142 version = f.read().strip()
143 Spawn([os.path.join(src_root, 'makeself.sh'), '--bzip2', '--nox11',
144 src_root, output_path, version, INSTALLER_PATH],
145 check_call=True, log=True)
146 print ('\n'
147 ' Factory toolkit generated at %s.\n'
148 '\n'
149 ' To install factory toolkit on a live device running a test image,\n'
150 ' copy this to the device and execute it as root.\n'
151 '\n'
152 ' Alternatively, the factory toolkit can be used to patch a test\n'
153 ' image. For more information, run:\n'
154 ' %s -- --help\n'
155 '\n' % (output_path, output_path))
156
157
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800158def main():
159 parser = argparse.ArgumentParser(
160 description='Factory toolkit installer.')
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800161 parser.add_argument('dest', nargs='?', default='/',
162 help='A test image or the mount point of the stateful partition. '
163 "If omitted, install to live system, i.e. '/'.")
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800164 parser.add_argument('--no-enable', '-n', action='store_true',
165 help="Don't enable factory tests after installing")
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800166 parser.add_argument('--yes', '-y', action='store_true',
167 help="Don't ask for confirmation")
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800168 parser.add_argument('--build-info', action='store_true',
169 help="Print build information and exit")
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800170 parser.add_argument('--pack-into', metavar='NEW_TOOLKIT',
171 help="Pack the files into a new factory toolkit")
172 parser.add_argument('--repack', metavar='UNPACKED_TOOLKIT',
173 help="Repack from previously unpacked toolkit")
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800174 args = parser.parse_args()
175
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800176 src_root = factory.FACTORY_PATH
177 for _ in xrange(3):
178 src_root = os.path.dirname(src_root)
179
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800180 # --pack-into may be called directly so this must be done before changing
181 # working directory to OLDPWD.
182 if args.pack_into and args.repack is None:
183 PackFactoryToolkit(src_root, args.pack_into)
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800184 return
185
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800186 # Change to original working directory in case the user specifies
187 # a relative path.
188 # TODO: Use USER_PWD instead when makeself is upgraded
189 os.chdir(os.environ['OLDPWD'])
190
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800191 if args.repack:
192 if args.pack_into is None:
193 parser.error('Must specify --pack-into when using --repack.')
194 Spawn([os.path.join(args.repack, INSTALLER_PATH),
195 '--pack-into', args.pack_into], check_call=True, log=True)
196 return
197
198 if args.build_info:
199 PrintBuildInfo(src_root)
200 return
201
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800202 if not os.path.exists(args.dest):
203 parser.error('Destination %s does not exist!' % args.dest)
204
205 patch_test_image = os.path.isfile(args.dest)
206
207 with (MountPartition(args.dest, 1, rw=True) if patch_test_image
208 else DummyContext(args.dest)) as dest:
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800209 installer = FactoryToolkitInstaller(src_root, dest, args.no_enable)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800210
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800211 print installer.WarningMessage(args.dest if patch_test_image else None)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800212
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800213 if not args.yes:
214 answer = raw_input('*** Continue? [y/N] ')
215 if not answer or answer[0] not in 'yY':
216 sys.exit('Aborting.')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800217
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800218 installer.Install()
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800219
220if __name__ == '__main__':
221 main()