OLD | NEW |
---|---|
(Empty) | |
1 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 """Module that contains the interface for au_test_harness workers. | |
6 | |
7 An au test harnss worker is a class that contains the logic for performing | |
8 and validating updates on a target. This should be subclassed to handle | |
9 various types of target. Types of targets include VM's, real devices, etc. | |
10 """ | |
11 | |
12 import os | |
13 import sys | |
14 | |
15 import cros_build_lib as cros_lib | |
16 | |
17 import dev_server_wrapper | |
18 import update_exception | |
19 | |
20 | |
21 class AUWorker(object): | |
22 """Interface for a worker that updates and verifies images.""" | |
23 | |
24 update_cache = None | |
25 | |
26 # --- INTERFACE --- | |
27 | |
28 def __init__(self, options): | |
29 """Processes options for the specific-type of worker.""" | |
30 self.board = options.board | |
31 self.private_key = options.private_key | |
32 self.use_delta_updates = options.delta | |
33 self.verbose = options.verbose | |
34 self.vm_image_path = None | |
35 if options.quick_test: | |
36 self.verify_suite = 'build_RootFilesystemSize' | |
37 else: | |
38 self.verify_suite = 'suite_Smoke' | |
39 | |
40 # Set these up as they are used often. | |
41 self.crosutils = os.path.join(os.path.dirname(__file__), '..', '..') | |
42 self.crosutilsbin = os.path.join(os.path.dirname(__file__), '..') | |
43 | |
44 def UpdateImage(self, image_path, src_image_path='', stateful_change='old', | |
45 proxy_port=None, private_key_path=None): | |
46 """Implementation of an actual update. | |
47 | |
48 See PerformUpdate for description of args. Subclasses must override this | |
49 method with the correct update procedure for the class. | |
50 """ | |
51 pass | |
52 | |
53 def UpdateUsingPayload(self, update_path, stateful_change='old', | |
54 proxy_port=None): | |
55 """Updates target with the pre-generated update stored in update_path. | |
56 | |
57 Subclasses must override this method with the correct update procedure for | |
58 the class. | |
59 | |
60 Args: | |
61 update_path: Path to the image to update with. This directory should | |
62 contain both update.gz, and stateful.image.gz | |
63 proxy_port: Port to have the client connect to. For use with | |
64 CrosTestProxy. | |
65 """ | |
66 pass | |
67 | |
68 def VerifyImage(self, unittest, percent_required_to_pass=100): | |
69 """Verifies the image with tests. | |
70 | |
71 Verifies that the test images passes the percent required. Subclasses must | |
72 override this method with the correct update procedure for the class. | |
73 | |
74 Args: | |
75 unittest: pointer to a unittest to fail if we cannot verify the image. | |
76 percent_required_to_pass: percentage required to pass. This should be | |
77 fall between 0-100. | |
78 | |
79 Returns: | |
80 Returns the percent that passed. | |
81 """ | |
82 pass | |
83 | |
84 # --- INTERFACE TO AU_TEST --- | |
85 | |
86 def PerformUpdate(self, image_path, src_image_path='', stateful_change='old', | |
87 proxy_port=None, private_key_path=None): | |
88 """Performs an update using _UpdateImage and reports any error. | |
89 | |
90 Subclasses should not override this method but override _UpdateImage | |
91 instead. | |
92 | |
93 Args: | |
94 image_path: Path to the image to update with. This image must be a test | |
95 image. | |
96 src_image_path: Optional. If set, perform a delta update using the | |
97 image specified by the path as the source image. | |
98 stateful_change: How to modify the stateful partition. Values are: | |
99 'old': Don't modify stateful partition. Just update normally. | |
100 'clean': Uses clobber-state to wipe the stateful partition with the | |
101 exception of code needed for ssh. | |
102 proxy_port: Port to have the client connect to. For use with | |
103 CrosTestProxy. | |
104 private_key_path: Path to a private key to use with update payload. | |
105 Raises an update_exception.UpdateException if _UpdateImage returns an error. | |
106 """ | |
107 try: | |
108 if not self.use_delta_updates: src_image_path = '' | |
109 if private_key_path: | |
110 key_to_use = private_key_path | |
111 else: | |
112 key_to_use = self.private_key | |
113 | |
114 self.UpdateImage(image_path, src_image_path, stateful_change, | |
115 proxy_port, key_to_use) | |
116 except update_exception.UpdateException as err: | |
117 # If the update fails, print it out | |
118 Warning(err.stdout) | |
119 raise | |
120 | |
121 @classmethod | |
dgarrett
2011/03/03 02:17:21
Why classmethod here?
sosa
2011/03/03 03:11:41
Sets a static variable. I'm using class variables
| |
122 def SetUpdateCache(cls, update_cache): | |
123 """Sets the global update cache for getting paths to devserver payloads.""" | |
124 cls.update_cache = update_cache | |
125 | |
126 # --- METHODS FOR SUB CLASS USE --- | |
127 | |
128 def PrepareRealBase(self, image_path): | |
129 """Prepares a remote device for worker test by updating it to the image.""" | |
130 self.UpdateImage(image_path) | |
131 | |
132 def PrepareVMBase(self, image_path): | |
133 """Prepares a VM image for worker test by creating the VM file from the img. | |
134 """ | |
135 # VM Constants. | |
136 FULL_VDISK_SIZE = 6072 | |
137 FULL_STATEFULFS_SIZE = 3074 | |
138 # Needed for VM delta updates. We need to use the qemu image rather | |
139 # than the base image on a first update. By tracking the first_update | |
140 # we can set src_image to the qemu form of the base image when | |
141 # performing generating the delta payload. | |
142 self._first_update = True | |
143 self.vm_image_path = '%s/chromiumos_qemu_image.bin' % os.path.dirname( | |
144 image_path) | |
145 if not os.path.exists(self.vm_image_path): | |
146 cros_lib.Info('Creating %s' % self.vm_image_path) | |
147 cros_lib.RunCommand(['./image_to_vm.sh', | |
148 '--full', | |
149 '--from=%s' % cros_lib.ReinterpretPathForChroot( | |
150 os.path.dirname(image_path)), | |
151 '--vdisk_size=%s' % FULL_VDISK_SIZE, | |
152 '--statefulfs_size=%s' % FULL_STATEFULFS_SIZE, | |
153 '--board=%s' % self.board, | |
154 '--test_image' | |
155 ], enter_chroot=True, cwd=self.crosutils) | |
156 | |
157 cros_lib.Info('Using %s as base' % self.vm_image_path) | |
158 assert os.path.exists(self.vm_image_path) | |
159 | |
160 def GetStatefulChangeFlag(self, stateful_change): | |
161 """Returns the flag to pass to image_to_vm for the stateful change.""" | |
162 stateful_change_flag = '' | |
163 if stateful_change: | |
164 stateful_change_flag = '--stateful_update_flag=%s' % stateful_change | |
165 | |
166 return stateful_change_flag | |
167 | |
168 def AppendUpdateFlags(self, cmd, image_path, src_image_path, proxy_port, | |
169 private_key_path): | |
170 """Appends common args to an update cmd defined by an array. | |
171 | |
172 Modifies cmd in places by appending appropriate items given args. | |
173 """ | |
174 if proxy_port: cmd.append('--proxy_port=%s' % proxy_port) | |
175 | |
176 # Get pregenerated update if we have one. | |
177 update_id = dev_server_wrapper.GenerateUpdateId(image_path, src_image_path, | |
178 private_key_path) | |
179 cache_path = self.update_cache[update_id] | |
180 if cache_path: | |
181 update_url = dev_server_wrapper.DevServerWrapper.GetDevServerURL( | |
182 proxy_port, cache_path) | |
183 cmd.append('--update_url=%s' % update_url) | |
184 else: | |
185 cmd.append('--image=%s' % image_path) | |
186 if src_image_path: cmd.append('--src_image=%s' % src_image_path) | |
187 | |
188 def RunUpdateCmd(self, cmd): | |
189 """Runs the given update cmd given verbose options. | |
190 | |
191 Raises an update_exception.UpdateException if the update fails. | |
192 """ | |
193 if self.verbose: | |
194 try: | |
195 cros_lib.RunCommand(cmd) | |
196 except Exception, e: | |
dgarrett
2011/03/03 02:17:21
This syntax is morphing. What you are doing works,
sosa
2011/03/03 03:11:41
Done.
| |
197 Warning(str(e)) | |
198 raise update_exception.UpdateException(1, str(e)) | |
199 else: | |
200 (code, stdout, stderr) = cros_lib.RunCommandCaptureOutput(cmd) | |
201 if code != 0: | |
202 Warning(stdout) | |
203 raise update_exception.UpdateException(code, stdout) | |
204 | |
205 def AssertEnoughTestsPassed(self, unittest, output, percent_required_to_pass): | |
206 """Helper function that asserts a sufficient number of tests passed. | |
207 | |
208 Args: | |
209 output: stdout from a test run. | |
210 percent_required_to_pass: percentage required to pass. This should be | |
211 fall between 0-100. | |
212 Returns: | |
213 percent that passed. | |
214 """ | |
215 cros_lib.Info('Output from VerifyImage():') | |
216 print >> sys.stderr, output | |
217 sys.stderr.flush() | |
218 percent_passed = self._ParseGenerateTestReportOutput(output) | |
219 cros_lib.Info('Percent passed: %d vs. Percent required: %d' % ( | |
220 percent_passed, percent_required_to_pass)) | |
221 unittest.assertTrue(percent_passed >= percent_required_to_pass) | |
222 return percent_passed | |
223 | |
224 # --- PRIVATE HELPER FUNCTIONS --- | |
225 | |
226 def _ParseGenerateTestReportOutput(self, output): | |
227 """Returns the percentage of tests that passed based on output.""" | |
228 percent_passed = 0 | |
229 lines = output.split('\n') | |
230 | |
231 for line in lines: | |
232 if line.startswith("Total PASS:"): | |
233 # FORMAT: ^TOTAL PASS: num_passed/num_total (percent%)$ | |
234 percent_passed = line.split()[3].strip('()%') | |
235 cros_lib.Info('Percent of tests passed %s' % percent_passed) | |
236 break | |
237 | |
238 return int(percent_passed) | |
OLD | NEW |