blob: 3af3c51a6a4e731cbacdf6cc49fe214554bbb594 [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) Yang296871a2014-01-13 12:05:18 +0800128def main():
129 parser = argparse.ArgumentParser(
130 description='Factory toolkit installer.')
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800131 parser.add_argument('dest', nargs='?', default='/',
132 help='A test image or the mount point of the stateful partition. '
133 "If omitted, install to live system, i.e. '/'.")
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800134 parser.add_argument('--no-enable', '-n', action='store_true',
135 help="Don't enable factory tests after installing")
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800136 parser.add_argument('--yes', '-y', action='store_true',
137 help="Don't ask for confirmation")
138 args = parser.parse_args()
139
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800140 # Change to original working directory in case the user specifies
141 # a relative path.
142 # TODO: Use USER_PWD instead when makeself is upgraded
143 os.chdir(os.environ['OLDPWD'])
144
145 if not os.path.exists(args.dest):
146 parser.error('Destination %s does not exist!' % args.dest)
147
148 patch_test_image = os.path.isfile(args.dest)
149
150 with (MountPartition(args.dest, 1, rw=True) if patch_test_image
151 else DummyContext(args.dest)) as dest:
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800152 src_root = factory.FACTORY_PATH
153 for _ in xrange(3):
154 src_root = os.path.dirname(src_root)
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800155 installer = FactoryToolkitInstaller(src_root, dest, args.no_enable)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800156
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800157 print installer.WarningMessage(args.dest if patch_test_image else None)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800158
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800159 if not args.yes:
160 answer = raw_input('*** Continue? [y/N] ')
161 if not answer or answer[0] not in 'yY':
162 sys.exit('Aborting.')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800163
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800164 installer.Install()
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800165
166if __name__ == '__main__':
167 main()