1# -*- coding: utf-8 -*- 2# Copyright 2020 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""Module for updating the stateful partition on the device. 7 8Use this module to update the stateful partition given a stateful payload 9(e.g. stateful.tgz) on the device. This module untars/uncompresses the payload 10on the device into var_new and dev_image_new directories. Optinonally, you can 11ask this module to reset a stateful partition by preparing it to be clobbered on 12reboot. 13""" 14 15from __future__ import print_function 16 17import os 18import tempfile 19 20from autotest_lib.utils.frozen_chromite.lib import constants 21from autotest_lib.utils.frozen_chromite.lib import cros_build_lib 22from autotest_lib.utils.frozen_chromite.lib import cros_logging as logging 23from autotest_lib.utils.frozen_chromite.lib import osutils 24 25 26class Error(Exception): 27 """Base exception class of StatefulUpdater errors.""" 28 29 30class StatefulUpdater(object): 31 """The module for updating the stateful partition.""" 32 33 UPDATE_TYPE_STANDARD = 'standard' 34 UPDATE_TYPE_CLOBBER = 'clobber' 35 36 _VAR_DIR = 'var_new' 37 _DEV_IMAGE_DIR = 'dev_image_new' 38 _UPDATE_TYPE_FILE = '.update_available' 39 40 def __init__(self, device, stateful_dir=constants.STATEFUL_DIR): 41 """Initializes the module. 42 43 Args: 44 device: The ChromiumOsDevice to be updated. 45 stateful_dir: The stateful directory on the Chromium OS device. 46 """ 47 self._device = device 48 self._stateful_dir = stateful_dir 49 self._var_dir = os.path.join(self._stateful_dir, self._VAR_DIR) 50 self._dev_image_dir = os.path.join(self._stateful_dir, self._DEV_IMAGE_DIR) 51 self._update_type_file = os.path.join(self._stateful_dir, 52 self._UPDATE_TYPE_FILE) 53 54 def Update(self, payload_path_on_device, update_type=None): 55 """Updates the stateful partition given the update file. 56 57 Args: 58 payload_path_on_device: The path to the stateful update (stateful.tgz) 59 on the DUT. 60 update_type: The type of the stateful update to be marked. Accepted 61 values: 'standard' (default) and 'clobber'. 62 """ 63 if not self._device.IfPathExists(payload_path_on_device): 64 raise Error('Missing the file: %s' % payload_path_on_device) 65 66 try: 67 cmd = ['tar', '--ignore-command-error', '--overwrite', 68 '--directory', self._stateful_dir, '-xzf', payload_path_on_device] 69 self._device.run(cmd) 70 except cros_build_lib.RunCommandError as e: 71 raise Error('Failed to untar the stateful update with error %s' % e) 72 73 # Make sure target directories are generated on the device. 74 if (not self._device.IfPathExists(self._var_dir) or 75 not self._device.IfPathExists(self._dev_image_dir)): 76 raise Error('Missing var or dev_image in stateful payload.') 77 78 self._MarkUpdateType(update_type if update_type is not None 79 else self.UPDATE_TYPE_STANDARD) 80 81 def _MarkUpdateType(self, update_type): 82 """Marks the type of the update. 83 84 Args: 85 update_type: The type of the update to be marked. See Update() 86 """ 87 if update_type not in (self.UPDATE_TYPE_CLOBBER, self.UPDATE_TYPE_STANDARD): 88 raise Error('Invalid update type %s' % update_type) 89 90 with tempfile.NamedTemporaryFile() as f: 91 if update_type == self.UPDATE_TYPE_STANDARD: 92 logging.notice('Performing standard stateful update...') 93 elif update_type == self.UPDATE_TYPE_CLOBBER: 94 logging.notice('Restoring stateful to factory_install ' 95 'with dev_image...') 96 osutils.WriteFile(f.name, 'clobber') 97 98 try: 99 self._device.CopyToDevice(f.name, self._update_type_file, 'scp') 100 except cros_build_lib.RunCommandError as e: 101 raise Error('Failed to copy update type file to device with error %s' % 102 e) 103 104 def Reset(self): 105 """Resets the stateful partition.""" 106 logging.info('Resetting stateful update state.') 107 108 try: 109 self._device.run(['rm', '-rf', self._update_type_file, 110 self._var_dir, self._dev_image_dir]) 111 except cros_build_lib.RunCommandError as e: 112 logging.warning('(ignoring) Failed to delete stateful update paths with' 113 ' error: %s', e) 114