blob: 3ca3ea4aef7b9d8455f1320072d0fab31ab0ca9c [file] [log] [blame]
Chris Sosada9632e2013-03-04 12:28:06 -08001#!/usr/bin/python
2#
3# 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
7"""Integration test to test the basic functionality of dev-install and gmerge.
8
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
12stateful partition and then runs gmerge.
13"""
14
15import logging
16import optparse
17import os
18import shutil
19import socket
20import sys
21import tempfile
Chris Sosa068c1e92013-03-17 22:54:20 -070022import time
Chris Sosada9632e2013-03-04 12:28:06 -080023
24import constants
25sys.path.append(constants.SOURCE_ROOT)
26sys.path.append(constants.CROS_PLATFORM_ROOT)
27
28from chromite.lib import cros_build_lib
Chris Sosa928085e2013-03-08 17:25:30 -080029from chromite.lib import remote_access
Chris Sosada9632e2013-03-04 12:28:06 -080030from crostestutils.lib import dev_server_wrapper
31from crostestutils.lib import mount_helper
32from crostestutils.lib import test_helper
33
34
35_LOCALHOST = 'localhost'
36_PRIVATE_KEY = os.path.join(constants.CROSUTILS_DIR, 'mod_for_test_scripts',
37 'ssh_keys', 'testing_rsa')
38
39class TestError(Exception):
40 """Raised on any error during testing. It being raised is a test failure."""
41
42
43class DevModeTest(object):
44 """Wrapper for dev mode tests."""
45 def __init__(self, image_path, board, binhost):
46 """
47 Args:
48 image_path: Filesystem path to the image to test.
49 board: Board of the image under test.
50 binhost: Binhost override. Binhost as defined here is where dev-install
51 or gmerge go to search for binary packages. By default this will
52 be set to the devserver url of the host running this script.
53 If no override i.e. the default is ok, set to None.
54 """
55 self.image_path = image_path
56 self.board = board
57 self.binhost = binhost
58
59 self.tmpdir = tempfile.mkdtemp('DevModeTest')
Chris Sosada9632e2013-03-04 12:28:06 -080060 self.tmpkvmpid = os.path.join(self.tmpdir, 'kvm_pid')
61
62 self.working_image_path = None
63 self.devserver = None
Chris Sosa928085e2013-03-08 17:25:30 -080064 self.remote_access = None
Chris Sosada9632e2013-03-04 12:28:06 -080065 self.port = None
66
67 def Cleanup(self):
68 """Clean up any state at the end of the test."""
69 try:
70 if self.working_image_path:
71 os.remove(self.working_image_path)
72
73 if self.devserver:
74 self.devserver.Stop()
75
76 self.devserver = None
77
78 cmd = ['%s/bin/cros_stop_vm' % constants.CROSUTILS_DIR,
79 '--kvm_pid', self.tmpkvmpid]
80 cros_build_lib.RunCommand(cmd, debug_level=logging.DEBUG)
81
82 if self.tmpdir:
83 shutil.rmtree(self.tmpdir, ignore_errors=True)
84
85 self.tmpdir = None
86 except Exception:
87 logging.warning('Received error during cleanup', exc_info=True)
88
89 def _SetupSSH(self):
90 """Sets up the necessary items for running ssh."""
91 self.port = self._FindUnusedPort()
Chris Sosa928085e2013-03-08 17:25:30 -080092 self.remote_access = remote_access.RemoteAccess(
93 _LOCALHOST, self.tmpdir, self.port,
94 debug_level=logging.DEBUG, interactive=False)
Chris Sosada9632e2013-03-04 12:28:06 -080095
96 def _WipeStatefulPartition(self):
97 """Deletes everything from the working image path's stateful partition."""
98 r_mount_point = os.path.join(self.tmpdir, 'm')
99 s_mount_point = os.path.join(self.tmpdir, 's')
100 mount_helper.MountImage(self.working_image_path,
101 r_mount_point, s_mount_point, read_only=False,
102 safe=True)
103 # Run in shell mode to interpret '*' as a glob.
104 cros_build_lib.SudoRunCommand('rm -rf %s/*' % s_mount_point, shell=True,
105 debug_level=logging.DEBUG)
106 mount_helper.UnmountImage(r_mount_point, s_mount_point)
107
108 def _FindUnusedPort(self):
109 """Returns a currently unused port."""
110 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
111 s.bind((_LOCALHOST, 0))
112 port = s.getsockname()[1]
113 s.close()
114 return port
115
116 def PrepareTest(self):
117 """Pre-test modification to the image and env to setup test."""
118 logging.info('Setting up the image %s for vm testing.',
119 self.image_path)
120 self._SetupSSH()
121 vm_path = test_helper.CreateVMImage(self.image_path, self.board,
122 full=False)
123
124 logging.info('Making copy of the vm image %s to manipulate.', vm_path)
125 self.working_image_path = vm_path + '.' + str(self.port)
126 shutil.copyfile(vm_path, self.working_image_path)
127 logging.debug('Copy of vm image stored at %s.', self.working_image_path)
128
129 logging.info('Wiping the stateful partition to prepare test.')
130 self._WipeStatefulPartition()
131
132 logging.info('Starting the vm on port %d.', self.port)
133 cmd = ['%s/bin/cros_start_vm' % constants.CROSUTILS_DIR,
134 '--ssh_port', str(self.port),
135 '--image_path', self.working_image_path,
136 '--no_graphics',
137 '--kvm_pid', self.tmpkvmpid]
138 cros_build_lib.RunCommand(cmd, debug_level=logging.DEBUG)
139
Chris Sosa068c1e92013-03-17 22:54:20 -0700140 # TODO(sosa): Super hack, fix with retry_ssh-like functionality.
141 # crbug.com/209719
142 time.sleep(30)
143
Chris Sosada9632e2013-03-04 12:28:06 -0800144 if not self.binhost:
145 logging.info('Starting the devserver.')
146 self.devserver = dev_server_wrapper.DevServerWrapper(self.tmpdir)
147 self.devserver.start()
148 self.devserver.WaitUntilStarted()
149 self.binhost = dev_server_wrapper.DevServerWrapper.GetDevServerURL(
Chris Sosac9447962013-03-12 10:12:29 -0700150 sub_dir='static/pkgroot/%s/packages' % self.board)
Chris Sosada9632e2013-03-04 12:28:06 -0800151
152 logging.info('Using binhost %s', self.binhost)
153
154 def TestDevInstall(self):
155 """Tests that we can run dev-install and have python work afterwards."""
156 try:
157 logging.info('Running dev install in the vm.')
Chris Sosa928085e2013-03-08 17:25:30 -0800158 self.remote_access.RemoteSh(
Chris Sosada9632e2013-03-04 12:28:06 -0800159 ['bash', '-l', '-c',
160 '"/usr/bin/dev_install --yes --binhost %s"' % self.binhost])
161
162 logging.info('Verifying that python works on the image.')
Chris Sosa928085e2013-03-08 17:25:30 -0800163 self.remote_access.RemoteSh(
Chris Sosada9632e2013-03-04 12:28:06 -0800164 ['sudo', '-u', 'chronos', '--',
165 'python', '-c', '"print \'hello world\'"'])
166 except cros_build_lib.RunCommandError as e:
167 self.devserver.PrintLog()
168 logging.error('dev-install test failed. See devserver log above for more '
169 'details.')
170 raise TestError('dev-install test failed with: %s' % str(e))
171
Chris Sosada9632e2013-03-04 12:28:06 -0800172 def TestGmerge(self):
173 """Evaluates whether the test passed or failed."""
Chris Sosa928085e2013-03-08 17:25:30 -0800174 logging.info('Testing that gmerge works on the image after dev install.')
Chris Sosada9632e2013-03-04 12:28:06 -0800175 try:
Chris Sosac9447962013-03-12 10:12:29 -0700176 self.remote_access.RemoteSh(
177 ['gmerge', 'gmerge', '--accept_stable', '--usepkg',
178 '--devserver_url', self.devserver.GetDevServerURL(),
179 '--board', self.board])
Chris Sosada9632e2013-03-04 12:28:06 -0800180 except cros_build_lib.RunCommandError as e:
181 logging.error('gmerge test failed. See log for details')
182 raise TestError('gmerge test failed with: %s' % str(e))
183
184
185def main():
186 usage = ('%s <board> <path_to_[test|vm]_image>. '
187 'See --help for more options' % os.path.basename(sys.argv[0]))
188 parser = optparse.OptionParser(usage)
189 parser.add_option('--binhost', metavar='URL',
190 help='binhost override. By default, starts up a devserver '
191 'and uses it as the binhost.')
192 parser.add_option('-v', '--verbose', default=False, action='store_true',
193 help='Print out added debugging information')
194
195 (options, args) = parser.parse_args()
196
197 if len(args) != 2:
198 parser.print_usage()
199 parser.error('Need board and path to test image.')
200
201 board = args[0]
202 image_path = os.path.realpath(args[1])
203
204 test_helper.SetupCommonLoggingFormat(verbose=options.verbose)
205
206 test = DevModeTest(image_path, board, options.binhost)
207 try:
208 test.PrepareTest()
209 test.TestDevInstall()
Chris Sosa928085e2013-03-08 17:25:30 -0800210 test.TestGmerge()
Chris Sosada9632e2013-03-04 12:28:06 -0800211 logging.info('All tests passed.')
212 finally:
213 test.Cleanup()
214
215
216if __name__ == '__main__':
217 main()