blob: 45f28e4c1c3d5835cf1c10690951a35df64b03a9 [file] [log] [blame]
Mike Frysinger2d27b8e2020-02-27 05:15:12 -05001#!/usr/bin/env python3
Achuith Bhandarkar78010f72019-01-03 17:21:52 -08002# -*- coding: utf-8 -*-
Chris Sosada9632e2013-03-04 12:28:06 -08003# Copyright (c) 2013 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
Mike Frysingerdb50f4f2019-02-24 19:52:59 -05007"""Integration test to test the basic functionality of dev-install.
Chris Sosada9632e2013-03-04 12:28:06 -08008
9This module contains a test that runs some sanity integration tests against
10a VM. First it starts a VM test image and turns it into a base image by wiping
11all of the stateful partition. Once done, runs dev_install to restore the
Mike Frysingerdb50f4f2019-02-24 19:52:59 -050012stateful partition.
Chris Sosada9632e2013-03-04 12:28:06 -080013"""
14
Achuith Bhandarkarb6bc33d2018-03-28 00:16:47 -070015from __future__ import print_function
16
17import argparse
Chris Sosada9632e2013-03-04 12:28:06 -080018import os
19import shutil
Chris Sosada9632e2013-03-04 12:28:06 -080020import sys
21import tempfile
22
23import constants
24sys.path.append(constants.SOURCE_ROOT)
25sys.path.append(constants.CROS_PLATFORM_ROOT)
26
Shao-Chuan Lee4a382fb2020-06-29 13:53:24 +090027# pylint: disable=C0413
Achuith Bhandarkar78010f72019-01-03 17:21:52 -080028from chromite.lib import constants as chromite_constants
Chris Sosada9632e2013-03-04 12:28:06 -080029from chromite.lib import cros_build_lib
Achuith Bhandarkarb6bc33d2018-03-28 00:16:47 -070030from chromite.lib import cros_logging as logging
Yu-Ju Hong29111982013-12-20 15:04:41 -080031from chromite.lib import dev_server_wrapper
Mike Frysinger266ff2f2019-09-17 15:38:45 -040032from chromite.lib import image_lib
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -080033from chromite.lib import osutils
Chris Sosa928085e2013-03-08 17:25:30 -080034from chromite.lib import remote_access
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -080035from chromite.lib import vm
36
Chris Sosada9632e2013-03-04 12:28:06 -080037from crostestutils.lib import test_helper
38
39
Chris Sosada9632e2013-03-04 12:28:06 -080040class TestError(Exception):
41 """Raised on any error during testing. It being raised is a test failure."""
42
43
44class DevModeTest(object):
45 """Wrapper for dev mode tests."""
Nicolas Norvezc5a20ae2018-03-02 14:49:54 -080046
47 # qemu hardcodes 10.0.2.2 as the host from the guest's point of view
48 # https://wiki.qemu.org/Documentation/Networking#User_Networking_.28SLIRP.29
49 # When the host's eth0 IP address is also 10.0.2.*, and the guest tries to
50 # access it, the requests will be handled by qemu and never be seen by the
51 # host. Instead, let the guest always connect to 10.0.2.2.
52 HOST_IP_ADDRESS = '10.0.2.2'
53
Chris Sosada9632e2013-03-04 12:28:06 -080054 def __init__(self, image_path, board, binhost):
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -080055 """Initializes DevModeTest.
56
Chris Sosada9632e2013-03-04 12:28:06 -080057 Args:
58 image_path: Filesystem path to the image to test.
59 board: Board of the image under test.
60 binhost: Binhost override. Binhost as defined here is where dev-install
Mike Frysingerdb50f4f2019-02-24 19:52:59 -050061 go to search for binary packages. By default this will
Chris Sosada9632e2013-03-04 12:28:06 -080062 be set to the devserver url of the host running this script.
63 If no override i.e. the default is ok, set to None.
64 """
65 self.image_path = image_path
66 self.board = board
67 self.binhost = binhost
Chris Sosada9632e2013-03-04 12:28:06 -080068 self.tmpdir = tempfile.mkdtemp('DevModeTest')
Chris Sosada9632e2013-03-04 12:28:06 -080069 self.working_image_path = None
70 self.devserver = None
Achuith Bhandarkar518c8e22018-06-08 19:42:04 +000071 self.device = None
Achuith Bhandarkar78010f72019-01-03 17:21:52 -080072 self.port = None
Chris Sosada9632e2013-03-04 12:28:06 -080073
74 def Cleanup(self):
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -080075 """Cleans up any state at the end of the test."""
Chris Sosada9632e2013-03-04 12:28:06 -080076 try:
Chris Sosada9632e2013-03-04 12:28:06 -080077 if self.devserver:
78 self.devserver.Stop()
Chris Sosada9632e2013-03-04 12:28:06 -080079 self.devserver = None
Achuith Bhandarkar518c8e22018-06-08 19:42:04 +000080 if self.device:
81 self.device.Cleanup()
82 self.device = None
Achuith Bhandarkar78010f72019-01-03 17:21:52 -080083 if self.port:
Mike Frysinger3f49c6d2019-12-12 14:15:10 -050084 cros_build_lib.run(['./cros_vm', '--stop', '--ssh-port=%d' % self.port],
85 cwd=chromite_constants.CHROMITE_BIN_DIR,
86 check=False)
Achuith Bhandarkar78010f72019-01-03 17:21:52 -080087 self.port = None
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -080088 osutils.RmDir(self.tmpdir, ignore_missing=True)
Chris Sosada9632e2013-03-04 12:28:06 -080089 self.tmpdir = None
90 except Exception:
Prathmesh Prabhueea4fed2017-10-27 10:25:03 -070091 logging.exception('Received error during cleanup')
Chris Sosada9632e2013-03-04 12:28:06 -080092
Chris Sosab8c2af52013-07-03 10:45:39 -070093 def _WipeDevInstall(self):
94 """Wipes the devinstall state."""
Achuith Bhandarkar85928c42019-04-18 17:02:27 -070095 logging.info('Wiping /usr/local/bin from the image.')
Mike Frysinger266ff2f2019-09-17 15:38:45 -040096 with image_lib.LoopbackPartitions(
97 self.working_image_path, destination=self.tmpdir) as image:
98 image.Mount(('STATE',), mount_opts=())
99 dev_image_path = os.path.join(self.tmpdir, 'dir-STATE', 'dev_image')
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -0800100 osutils.RmDir(dev_image_path, sudo=True)
Chris Sosada9632e2013-03-04 12:28:06 -0800101
Chris Sosada9632e2013-03-04 12:28:06 -0800102 def PrepareTest(self):
103 """Pre-test modification to the image and env to setup test."""
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -0800104 logging.info('Setting up the image %s for vm testing.', self.image_path)
105 vm_path = vm.CreateVMImage(image=self.image_path, board=self.board,
Yu-Ju Hongb933bfc2014-02-19 11:15:34 -0800106 updatable=False)
Chris Sosada9632e2013-03-04 12:28:06 -0800107
108 logging.info('Making copy of the vm image %s to manipulate.', vm_path)
David James45b55dd2013-04-24 09:16:40 -0700109 self.working_image_path = os.path.join(self.tmpdir,
110 os.path.basename(vm_path))
Chris Sosada9632e2013-03-04 12:28:06 -0800111 shutil.copyfile(vm_path, self.working_image_path)
112 logging.debug('Copy of vm image stored at %s.', self.working_image_path)
113
Chris Sosab8c2af52013-07-03 10:45:39 -0700114 self._WipeDevInstall()
Chris Sosada9632e2013-03-04 12:28:06 -0800115
Achuith Bhandarkar78010f72019-01-03 17:21:52 -0800116 self.port = remote_access.GetUnusedPort()
117 logging.info('Starting the vm on port %d.', self.port)
118 vm_cmd = ['./cros_vm', '--ssh-port=%d' % self.port,
Shao-Chuan Lee4a382fb2020-06-29 13:53:24 +0900119 '--board=%s' % self.board,
Achuith Bhandarkar78010f72019-01-03 17:21:52 -0800120 '--image-path=%s' % self.working_image_path, '--start']
Mike Frysinger3f49c6d2019-12-12 14:15:10 -0500121 cros_build_lib.run(vm_cmd, cwd=chromite_constants.CHROMITE_BIN_DIR)
Yu-Ju Honga1dfbf52014-01-31 10:23:03 -0800122
Achuith Bhandarkar518c8e22018-06-08 19:42:04 +0000123 self.device = remote_access.ChromiumOSDevice(
Achuith Bhandarkar78010f72019-01-03 17:21:52 -0800124 remote_access.LOCALHOST, port=self.port, base_dir=self.tmpdir)
Achuith Bhandarkar85928c42019-04-18 17:02:27 -0700125 if not self.device.MountRootfsReadWrite():
126 raise TestError('Failed to make rootfs writeable')
Achuith Bhandarkar518c8e22018-06-08 19:42:04 +0000127
Chris Sosada9632e2013-03-04 12:28:06 -0800128 if not self.binhost:
129 logging.info('Starting the devserver.')
Chris Sosaa404a382013-08-22 11:28:38 -0700130 self.devserver = dev_server_wrapper.DevServerWrapper()
131 self.devserver.Start()
Nicolas Norvezc5a20ae2018-03-02 14:49:54 -0800132 self.binhost = self.devserver.GetDevServerURL(
133 ip=self.HOST_IP_ADDRESS, port=self.devserver.port,
Chris Sosac9447962013-03-12 10:12:29 -0700134 sub_dir='static/pkgroot/%s/packages' % self.board)
Chris Sosada9632e2013-03-04 12:28:06 -0800135
136 logging.info('Using binhost %s', self.binhost)
137
138 def TestDevInstall(self):
139 """Tests that we can run dev-install and have python work afterwards."""
140 try:
141 logging.info('Running dev install in the vm.')
Mike Frysinger3f49c6d2019-12-12 14:15:10 -0500142 self.device.run(
Chris Sosada9632e2013-03-04 12:28:06 -0800143 ['bash', '-l', '-c',
Mike Frysinger9d4f9302019-09-11 21:51:40 -0400144 '"/usr/bin/dev_install --yes --binhost=%s"' % self.binhost])
Chris Sosada9632e2013-03-04 12:28:06 -0800145
Mike Frysingere3b0ea72019-09-12 01:26:46 -0400146 logging.info('Verifying that all python versions work on the image.')
Mike Frysinger339c5c92019-10-03 17:06:18 -0400147 # Symlinks can be tricky, and running python from one place might work
148 # while it fails from another. Test all the prefixes (and $PATH).
149 for prefix in ('/usr/bin', '/usr/local/bin', '/usr/local/usr/bin', ''):
150 for prog in ('python', 'python2', 'python3'):
Mike Frysinger3f49c6d2019-12-12 14:15:10 -0500151 self.device.run(['sudo', '-u', 'chronos', '--',
152 os.path.join(prefix, prog),
153 '-c', '"print(\'hello world\')"'])
Yu-Ju Hong5ed02452014-01-30 09:05:00 -0800154 except (cros_build_lib.RunCommandError,
155 remote_access.SSHConnectionError) as e:
Chris Sosada9632e2013-03-04 12:28:06 -0800156 self.devserver.PrintLog()
157 logging.error('dev-install test failed. See devserver log above for more '
158 'details.')
159 raise TestError('dev-install test failed with: %s' % str(e))
160
Achuith Bhandarkarb6bc33d2018-03-28 00:16:47 -0700161 def Run(self):
162 try:
163 self.PrepareTest()
164 self.TestDevInstall()
Achuith Bhandarkarb6bc33d2018-03-28 00:16:47 -0700165 logging.info('All tests passed.')
166 finally:
167 self.Cleanup()
168
Chris Sosada9632e2013-03-04 12:28:06 -0800169
170def main():
Achuith Bhandarkarb6bc33d2018-03-28 00:16:47 -0700171 parser = argparse.ArgumentParser(description=__doc__)
172 parser.add_argument('--binhost', metavar='URL',
173 help='binhost override. By default, starts up a devserver'
174 ' and uses it as the binhost.')
175 parser.add_argument('board', nargs=1, help='board to use.')
176 parser.add_argument('image_path', nargs=1, help='path to test|vm image.')
177 parser.add_argument('-v', '--verbose', default=False, action='store_true',
178 help='Print out added debugging information')
179 options = parser.parse_args()
Chris Sosada9632e2013-03-04 12:28:06 -0800180
181 test_helper.SetupCommonLoggingFormat(verbose=options.verbose)
Achuith Bhandarkarb6bc33d2018-03-28 00:16:47 -0700182 DevModeTest(os.path.realpath(options.image_path[0]), options.board[0],
183 options.binhost).Run()
Chris Sosada9632e2013-03-04 12:28:06 -0800184
185if __name__ == '__main__':
186 main()