Vic (Chun-Ju) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 1 | #!/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 | |
| 9 | The factory toolkit is a self-extracting shellball containing factory test |
| 10 | related files and this installer. This installer is invoked when the toolkit |
| 11 | is deployed and is responsible for installing files. |
| 12 | """ |
| 13 | |
| 14 | |
| 15 | import argparse |
Vic (Chun-Ju) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 16 | from contextlib import contextmanager |
Vic (Chun-Ju) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 17 | import os |
Vic (Chun-Ju) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 18 | import sys |
| 19 | |
Vic (Chun-Ju) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 20 | import factory_common # pylint: disable=W0611 |
Vic (Chun-Ju) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 21 | from cros.factory.test import factory |
Vic (Chun-Ju) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 22 | from cros.factory.tools.mount_partition import MountPartition |
| 23 | from cros.factory.utils.process_utils import Spawn |
Vic (Chun-Ju) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 24 | |
| 25 | |
| 26 | class FactoryToolkitInstaller(): |
| 27 | """Factory toolkit installer. |
| 28 | |
| 29 | Args: |
| 30 | src: Source path containing usr/ and var/. |
Vic (Chun-Ju) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 31 | 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) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 36 | """ |
Vic (Chun-Ju) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 37 | |
| 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) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 43 | 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) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 48 | 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) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 55 | |
Vic (Chun-Ju) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 56 | self._dest = dest |
Vic (Chun-Ju) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 57 | self._usr_local_src = os.path.join(src, 'usr', 'local') |
| 58 | self._var_src = os.path.join(src, 'var') |
Vic (Chun-Ju) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 59 | self._no_enable = no_enable |
Vic (Chun-Ju) Yang | 7cc3e67 | 2014-01-20 14:06:39 +0800 | [diff] [blame] | 60 | self._tag_file = os.path.join(self._usr_local_dest, 'factory', 'enabled') |
Vic (Chun-Ju) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 61 | |
| 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) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 67 | 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) Yang | 7cc3e67 | 2014-01-20 14:06:39 +0800 | [diff] [blame] | 83 | 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) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 97 | return ret |
| 98 | |
| 99 | def _Rsync(self, src, dest): |
| 100 | print '*** %s -> %s' % (src, dest) |
Vic (Chun-Ju) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 101 | Spawn(['rsync', '-a', src + '/', dest], |
| 102 | sudo=True, log=True, check_output=True) |
Vic (Chun-Ju) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 103 | |
| 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) Yang | 7cc3e67 | 2014-01-20 14:06:39 +0800 | [diff] [blame] | 109 | 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) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 118 | |
| 119 | print '*** Installation completed.' |
| 120 | |
| 121 | |
Vic (Chun-Ju) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 122 | @contextmanager |
| 123 | def DummyContext(arg): |
| 124 | """A context manager that simply yields its argument.""" |
| 125 | yield arg |
| 126 | |
| 127 | |
Vic (Chun-Ju) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 128 | def main(): |
| 129 | parser = argparse.ArgumentParser( |
| 130 | description='Factory toolkit installer.') |
Vic (Chun-Ju) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 131 | 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) Yang | 7cc3e67 | 2014-01-20 14:06:39 +0800 | [diff] [blame] | 134 | parser.add_argument('--no-enable', '-n', action='store_true', |
| 135 | help="Don't enable factory tests after installing") |
Vic (Chun-Ju) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 136 | parser.add_argument('--yes', '-y', action='store_true', |
| 137 | help="Don't ask for confirmation") |
| 138 | args = parser.parse_args() |
| 139 | |
Vic (Chun-Ju) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 140 | # 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) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 152 | src_root = factory.FACTORY_PATH |
| 153 | for _ in xrange(3): |
| 154 | src_root = os.path.dirname(src_root) |
Vic (Chun-Ju) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 155 | installer = FactoryToolkitInstaller(src_root, dest, args.no_enable) |
Vic (Chun-Ju) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 156 | |
Vic (Chun-Ju) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 157 | print installer.WarningMessage(args.dest if patch_test_image else None) |
Vic (Chun-Ju) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 158 | |
Vic (Chun-Ju) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 159 | 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) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 163 | |
Vic (Chun-Ju) Yang | 469592b | 2014-02-18 19:15:41 +0800 | [diff] [blame^] | 164 | installer.Install() |
Vic (Chun-Ju) Yang | 296871a | 2014-01-13 12:05:18 +0800 | [diff] [blame] | 165 | |
| 166 | if __name__ == '__main__': |
| 167 | main() |