blob: a37c62eba9b511eaa84690c3f7eb1319ebb8501e [file] [log] [blame]
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001# 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
5import logging
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +08006import os
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08007import re
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +08008import tempfile
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08009import time
10import xmlrpclib
11
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +080012from autotest_lib.client.bin import utils
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080013from autotest_lib.client.common_lib import error
Tom Wai-Hong Tam22b77302011-11-03 13:03:48 +080014from autotest_lib.server.cros.servo_test import ServoTest
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080015
16
17class FAFTSequence(ServoTest):
18 """
19 The base class of Fully Automated Firmware Test Sequence.
20
21 Many firmware tests require several reboot cycles and verify the resulted
22 system states. To do that, an Autotest test case should detailly handle
23 every action on each step. It makes the test case hard to read and many
24 duplicated code. The base class FAFTSequence is to solve this problem.
25
26 The actions of one reboot cycle is defined in a dict, namely FAFT_STEP.
27 There are four functions in the FAFT_STEP dict:
28 state_checker: a function to check the current is valid or not,
29 returning True if valid, otherwise, False to break the whole
30 test sequence.
31 userspace_action: a function to describe the action ran in userspace.
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +080032 reboot_action: a function to do reboot, default: sync_and_hw_reboot.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080033 firmware_action: a function to describe the action ran after reboot.
34
Tom Wai-Hong Tam7c17ff22011-10-26 09:44:09 +080035 And configurations:
36 install_deps_after_boot: if True, install the Autotest dependency after
37 boot; otherwise, do nothing. It is for the cases of recovery mode
38 test. The test boots a USB/SD image instead of an internal image.
39 The previous installed Autotest dependency on the internal image
40 is lost. So need to install it again.
41
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080042 The default FAFT_STEP checks nothing in state_checker and does nothing in
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +080043 userspace_action and firmware_action. Its reboot_action is a hardware
44 reboot. You can change the default FAFT_STEP by calling
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080045 self.register_faft_template(FAFT_STEP).
46
47 A FAFT test case consists of several FAFT_STEP's, namely FAFT_SEQUENCE.
48 FAFT_SEQUENCE is an array of FAFT_STEP's. Any missing fields on FAFT_STEP
49 fall back to default.
50
51 In the run_once(), it should register and run FAFT_SEQUENCE like:
52 def run_once(self):
53 self.register_faft_sequence(FAFT_SEQUENCE)
54 self.run_faft_sequnce()
55
56 Note that in the last step, we only run state_checker. The
57 userspace_action, reboot_action, and firmware_action are not executed.
58
59 Attributes:
60 _faft_template: The default FAFT_STEP of each step. The actions would
61 be over-written if the registered FAFT_SEQUENCE is valid.
62 _faft_sequence: The registered FAFT_SEQUENCE.
63 """
64 version = 1
65
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +080066
67 # Mapping of partition number of kernel and rootfs.
68 KERNEL_MAP = {'a':'2', 'b':'4', '2':'2', '4':'4', '3':'2', '5':'4'}
69 ROOTFS_MAP = {'a':'3', 'b':'5', '2':'3', '4':'5', '3':'3', '5':'5'}
70 OTHER_KERNEL_MAP = {'a':'4', 'b':'2', '2':'4', '4':'2', '3':'4', '5':'2'}
71 OTHER_ROOTFS_MAP = {'a':'5', 'b':'3', '2':'5', '4':'3', '3':'5', '5':'3'}
72
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +080073 # Delay timing
74 FIRMWARE_SCREEN_DELAY = 10
75 TEXT_SCREEN_DELAY = 20
Tom Wai-Hong Tam9ca742a2011-12-05 15:48:57 +080076 USB_PLUG_DELAY = 10
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +080077 SYNC_DELAY = 5
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +080078
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +080079 _faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080080 _faft_sequence = ()
81
82
83 def setup(self):
84 """Autotest setup function."""
85 super(FAFTSequence, self).setup()
86 if not self._remote_infos['faft']['used']:
87 raise error.TestError('The use_faft flag should be enabled.')
88 self.register_faft_template({
89 'state_checker': (None),
90 'userspace_action': (None),
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +080091 'reboot_action': (self.sync_and_hw_reboot),
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080092 'firmware_action': (None)
93 })
94
95
96 def cleanup(self):
97 """Autotest cleanup function."""
98 self._faft_sequence = ()
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +080099 self._faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800100 super(FAFTSequence, self).cleanup()
101
102
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800103 def assert_test_image_in_usb_disk(self):
104 """Assert an USB disk plugged-in on servo and a test image inside.
105
106 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800107 error.TestError: if USB disk not detected or not a test image.
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800108 """
109 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
Jon Salzc88e5b62011-11-30 14:38:54 +0800110 usb_dev = self.servo.probe_host_usb_dev()
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800111 if not usb_dev:
112 raise error.TestError(
113 'An USB disk should be plugged in the servo board.')
114
115 tmp_dir = tempfile.mkdtemp()
Tom Wai-Hong Tamb0e80852011-12-07 16:15:06 +0800116 utils.system('sudo mount -r -t ext2 %s3 %s' % (usb_dev, tmp_dir))
Tom Wai-Hong Tame77459e2011-11-03 17:19:46 +0800117 code = utils.system(
118 'grep -qE "(Test Build|testimage-channel)" %s/etc/lsb-release' %
119 tmp_dir, ignore_status=True)
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800120 utils.system('sudo umount %s' % tmp_dir)
121 os.removedirs(tmp_dir)
122 if code != 0:
123 raise error.TestError(
124 'The image in the USB disk should be a test image.')
125
126
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800127 def _parse_crossystem_output(self, lines):
128 """Parse the crossystem output into a dict.
129
130 Args:
131 lines: The list of crossystem output strings.
132
133 Returns:
134 A dict which contains the crossystem keys/values.
135
136 Raises:
137 error.TestError: If wrong format in crossystem output.
138
139 >>> seq = FAFTSequence()
140 >>> seq._parse_crossystem_output([ \
141 "arch = x86 # Platform architecture", \
142 "cros_debug = 1 # OS should allow debug", \
143 ])
144 {'cros_debug': '1', 'arch': 'x86'}
145 >>> seq._parse_crossystem_output([ \
146 "arch=x86", \
147 ])
148 Traceback (most recent call last):
149 ...
150 TestError: Failed to parse crossystem output: arch=x86
151 >>> seq._parse_crossystem_output([ \
152 "arch = x86 # Platform architecture", \
153 "arch = arm # Platform architecture", \
154 ])
155 Traceback (most recent call last):
156 ...
157 TestError: Duplicated crossystem key: arch
158 """
159 pattern = "^([^ =]*) *= *(.*[^ ]) *# [^#]*$"
160 parsed_list = {}
161 for line in lines:
162 matched = re.match(pattern, line.strip())
163 if not matched:
164 raise error.TestError("Failed to parse crossystem output: %s"
165 % line)
166 (name, value) = (matched.group(1), matched.group(2))
167 if name in parsed_list:
168 raise error.TestError("Duplicated crossystem key: %s" % name)
169 parsed_list[name] = value
170 return parsed_list
171
172
173 def crossystem_checker(self, expected_dict):
174 """Check the crossystem values matched.
175
176 Given an expect_dict which describes the expected crossystem values,
177 this function check the current crossystem values are matched or not.
178
179 Args:
180 expected_dict: A dict which contains the expected values.
181
182 Returns:
183 True if the crossystem value matched; otherwise, False.
184 """
185 lines = self.faft_client.run_shell_command_get_output('crossystem')
186 got_dict = self._parse_crossystem_output(lines)
187 for key in expected_dict:
188 if key not in got_dict:
189 logging.info('Expected key "%s" not in crossystem result' % key)
190 return False
191 if isinstance(expected_dict[key], str):
192 if got_dict[key] != expected_dict[key]:
193 logging.info("Expected '%s' value '%s' but got '%s'" %
194 (key, expected_dict[key], got_dict[key]))
195 return False
196 elif isinstance(expected_dict[key], tuple):
197 # Expected value is a tuple of possible actual values.
198 if got_dict[key] not in expected_dict[key]:
199 logging.info("Expected '%s' values %s but got '%s'" %
200 (key, str(expected_dict[key]), got_dict[key]))
201 return False
202 else:
203 logging.info("The expected_dict is neither a str nor a dict.")
204 return False
205 return True
206
207
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800208 def root_part_checker(self, expected_part):
209 """Check the partition number of the root device matched.
210
211 Args:
212 expected_part: A string containing the number of the expected root
213 partition.
214
215 Returns:
216 True if the currect root partition number matched; otherwise, False.
217 """
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800218 part = self.faft_client.get_root_part()[-1]
219 if self.ROOTFS_MAP[expected_part] != part:
220 logging.info("Expected root part %s but got %s" %
221 (self.ROOTFS_MAP[expected_part], part))
222 return False
223 return True
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800224
225
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800226 def _join_part(self, dev, part):
227 """Return a concatenated string of device and partition number.
228
229 Args:
230 dev: A string of device, e.g.'/dev/sda'.
231 part: A string of partition number, e.g.'3'.
232
233 Returns:
234 A concatenated string of device and partition number, e.g.'/dev/sda3'.
235
236 >>> seq = FAFTSequence()
237 >>> seq._join_part('/dev/sda', '3')
238 '/dev/sda3'
239 >>> seq._join_part('/dev/mmcblk0', '2')
240 '/dev/mmcblk0p2'
241 """
242 if 'mmcblk' in dev:
243 return dev + 'p' + part
244 else:
245 return dev + part
246
247
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800248 def copy_kernel_and_rootfs(self, from_part, to_part):
249 """Copy kernel and rootfs from from_part to to_part.
250
251 Args:
252 from_part: A string of partition number to be copied from.
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800253 to_part: A string of partition number to be copied to.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800254 """
255 root_dev = self.faft_client.get_root_dev()
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800256 logging.info('Copying kernel from %s to %s. Please wait...' %
257 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800258 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800259 (self._join_part(root_dev, self.KERNEL_MAP[from_part]),
260 self._join_part(root_dev, self.KERNEL_MAP[to_part])))
261 logging.info('Copying rootfs from %s to %s. Please wait...' %
262 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800263 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800264 (self._join_part(root_dev, self.ROOTFS_MAP[from_part]),
265 self._join_part(root_dev, self.ROOTFS_MAP[to_part])))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800266
267
268 def ensure_kernel_boot(self, part):
269 """Ensure the request kernel boot.
270
271 If not, it duplicates the current kernel to the requested kernel
272 and sets the requested higher priority to ensure it boot.
273
274 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800275 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800276 """
277 if not self.root_part_checker(part):
278 self.copy_kernel_and_rootfs(from_part=self.OTHER_KERNEL_MAP[part],
279 to_part=part)
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800280 self.run_faft_step({
281 'userspace_action': (self.reset_and_prioritize_kernel, part),
282 })
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800283
284
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800285 def wait_fw_screen_and_ctrl_d(self):
286 """Wait for firmware warning screen and press Ctrl-D."""
287 time.sleep(self.FIRMWARE_SCREEN_DELAY)
288 self.servo.ctrl_d()
289
290
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800291 def wait_fw_screen_and_plug_usb(self):
292 """Wait for firmware warning screen and then unplug and plug the USB."""
293 time.sleep(self.FIRMWARE_SCREEN_DELAY)
294 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
295 time.sleep(self.USB_PLUG_DELAY)
296 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
297
298
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800299 def setup_tried_fwb(self, tried_fwb):
300 """Setup for fw B tried state.
301
302 It makes sure the system in the requested fw B tried state. If not, it
303 tries to do so.
304
305 Args:
306 tried_fwb: True if requested in tried_fwb=1; False if tried_fwb=0.
307 """
308 if tried_fwb:
309 if not self.crossystem_checker({'tried_fwb': '1'}):
310 logging.info(
311 'Firmware is not booted with tried_fwb. Reboot into it.')
312 self.run_faft_step({
313 'userspace_action': self.faft_client.set_try_fw_b,
314 })
315 else:
316 if not self.crossystem_checker({'tried_fwb': '0'}):
317 logging.info(
318 'Firmware is booted with tried_fwb. Reboot to clear.')
319 self.run_faft_step({})
320
321
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800322 def setup_dev_mode(self, dev_mode):
323 """Setup for development mode.
324
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800325 It makes sure the system in the requested normal/dev mode. If not, it
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800326 tries to do so.
327
328 Args:
329 dev_mode: True if requested in dev mode; False if normal mode.
330 """
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800331 # Change the default firmware_action for dev mode passing the fw screen.
332 self.register_faft_template({
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800333 'firmware_action': (self.wait_fw_screen_and_ctrl_d if dev_mode
334 else None),
335 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800336 if dev_mode:
337 if not self.crossystem_checker({'devsw_cur': '1'}):
338 logging.info('Dev switch is not on. Now switch it on.')
339 self.servo.enable_development_mode()
340 if not self.crossystem_checker({'devsw_boot': '1',
341 'mainfw_type': 'developer'}):
342 logging.info('System is not in dev mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800343 self.run_faft_step({
344 'userspace_action': (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800345 'chromeos-firmwareupdate --mode todev && reboot'),
346 'reboot_action': None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800347 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800348 else:
349 if not self.crossystem_checker({'devsw_cur': '0'}):
350 logging.info('Dev switch is not off. Now switch it off.')
351 self.servo.disable_development_mode()
352 if not self.crossystem_checker({'devsw_boot': '0',
353 'mainfw_type': 'normal'}):
354 logging.info('System is not in normal mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800355 self.run_faft_step({
356 'userspace_action': (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800357 'chromeos-firmwareupdate --mode tonormal && reboot'),
358 'reboot_action': None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800359 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800360
361
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800362 def setup_kernel(self, part):
363 """Setup for kernel test.
364
365 It makes sure both kernel A and B bootable and the current boot is
366 the requested kernel part.
367
368 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800369 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800370 """
371 self.ensure_kernel_boot(part)
372 self.copy_kernel_and_rootfs(from_part=part,
373 to_part=self.OTHER_KERNEL_MAP[part])
374 self.reset_and_prioritize_kernel(part)
375
376
377 def reset_and_prioritize_kernel(self, part):
378 """Make the requested partition highest priority.
379
380 This function also reset kerenl A and B to bootable.
381
382 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800383 part: A string of partition number to be prioritized.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800384 """
385 root_dev = self.faft_client.get_root_dev()
386 # Reset kernel A and B to bootable.
387 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
388 (self.KERNEL_MAP['a'], root_dev))
389 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
390 (self.KERNEL_MAP['b'], root_dev))
391 # Set kernel part highest priority.
392 self.faft_client.run_shell_command('cgpt prioritize -i%s %s' %
393 (self.KERNEL_MAP[part], root_dev))
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800394 # Safer to sync and wait until the cgpt status written to the disk.
395 self.faft_client.run_shell_command('sync')
396 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800397
398
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +0800399 def sync_and_hw_reboot(self):
400 """Request the client sync and do a warm reboot.
401
402 This is the default reboot action on FAFT.
403 """
404 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800405 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +0800406 self.servo.warm_reset()
407
408
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800409 def _str_action(self, action):
410 """Convert the action function into a readable string.
411
412 The simple str() doesn't work on remote objects since we disable
413 allow_dotted_names flag when we launch the SimpleXMLRPCServer.
414 So this function handles the exception in this case.
415
416 Args:
417 action: A function.
418
419 Returns:
420 A readable string.
421 """
422 try:
423 return str(action)
424 except xmlrpclib.Fault:
425 return '<remote method>'
426
427
428 def _call_action(self, action_tuple):
429 """Call the action function with/without arguments.
430
431 Args:
432 action_tuple: A function, or a tuple which consisted of a function
433 and its arguments (if any).
434
435 Returns:
436 The result value of the action function.
437 """
438 if isinstance(action_tuple, tuple):
439 action = action_tuple[0]
440 args = action_tuple[1:]
441 if callable(action):
442 logging.info('calling %s with parameter %s' % (
443 self._str_action(action), str(action_tuple[1])))
444 return action(*args)
445 else:
446 logging.info('action is not callable!')
447 else:
448 action = action_tuple
449 if action is not None:
450 if callable(action):
451 logging.info('calling %s' % self._str_action(action))
452 return action()
453 else:
454 logging.info('action is not callable!')
455
456 return None
457
458
459 def register_faft_template(self, template):
460 """Register FAFT template, the default FAFT_STEP of each step.
461
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800462 Any missing field falls back to the original faft_template.
463
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800464 Args:
465 template: A FAFT_STEP dict.
466 """
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800467 self._faft_template.update(template)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800468
469
470 def register_faft_sequence(self, sequence):
471 """Register FAFT sequence.
472
473 Args:
474 sequence: A FAFT_SEQUENCE array which consisted of FAFT_STEP dicts.
475 """
476 self._faft_sequence = sequence
477
478
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +0800479 def run_faft_step(self, step, no_reboot=False):
480 """Run a single FAFT step.
481
482 Any missing field falls back to faft_template. An empty step means
483 running the default faft_template.
484
485 Args:
486 step: A FAFT_STEP dict.
487 no_reboot: True to prevent running reboot_action and firmware_action.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800488
489 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800490 error.TestFail: An error when the test failed.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800491 """
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +0800492 test = {}
493 test.update(self._faft_template)
494 test.update(step)
495
496 if test['state_checker']:
497 if not self._call_action(test['state_checker']):
498 raise error.TestFail('State checker failed!')
499
500 self._call_action(test['userspace_action'])
501
502 # Don't run reboot_action and firmware_action if no_reboot is True.
503 if not no_reboot:
504 self._call_action(test['reboot_action'])
505 self.wait_for_client_offline()
506 self._call_action(test['firmware_action'])
507
508 if 'install_deps_after_boot' in test:
509 self.wait_for_client(
510 install_deps=test['install_deps_after_boot'])
511 else:
512 self.wait_for_client()
513
514
515 def run_faft_sequence(self):
516 """Run FAFT sequence which was previously registered."""
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800517 sequence = self._faft_sequence
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +0800518 for step in sequence:
519 # Don't reboot in the last step.
520 self.run_faft_step(step, no_reboot=(step is sequence[-1]))