1# Lint as: python2, python3 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 6import json 7import logging 8import os 9import shutil 10 11from autotest_lib.client.common_lib import utils 12 13_DEFAULT_RUN = utils.run 14 15# Directory for preloaded DLCs that may be packaged with the OS. Preloaded 16# DLCs can be installed without needing to go through update_engine. 17_PRELOAD_DIR = '/mnt/stateful_partition/var_overlay/cache/dlc-images/' 18 19class DLCUtil(object): 20 """ 21 Wrapper around dlcservice_util for tests that require DLC management. 22 23 For dlc_util to work seamlessly in both client and server tests, we need 24 those tests to define the _run() function: 25 server side: host.run 26 client side: utils.run 27 28 """ 29 _DLCSERVICE_UTIL_CMD = "dlcservice_util" 30 _SAMPLE_DLC_ID = "sample-dlc" 31 32 def __init__(self, run_func=_DEFAULT_RUN): 33 """ 34 Initialize this class with _run() function. 35 36 @param run_func: The function to use to run commands on the client. 37 Defaults for use by client tests, but can be 38 overwritten to run remotely from a server test. 39 40 """ 41 self.set_run(run_func) 42 43 def set_run(self, run_func): 44 """Initializes the run function if it has been changed. 45 46 @param run_func: See __init__. 47 48 """ 49 self._run = run_func 50 51 52 def list(self): 53 """ 54 List DLCs that are installed on the DUT and additional information 55 about them such as name, version, root_mount, and size. The output is 56 a dictionary containing the result of dlcservice_util --list, which 57 looks like: 58 { 59 "sample-dlc": [ { 60 "fs-type": "squashfs", 61 "id": "sample-dlc", 62 "image_type": "dlc", 63 "manifest": "/opt/google/dlc/sample-dlc/package/imageloader.json", 64 "name": "Sample DLC", 65 "package": "package", 66 "preallocated_size": "4194304", 67 "root_mount": "/run/imageloader/sample-dlc/package", 68 "size": "53248", 69 "version": "1.0.0-r10" 70 } ] 71 } 72 73 @return Dictionary containing information about the installed DLCs, 74 whose keys are the DLC IDs. 75 76 """ 77 status = self._run([self._DLCSERVICE_UTIL_CMD, '--list']) 78 logging.info(status) 79 80 return json.loads(status.stdout) 81 82 83 def install(self, dlc_id, omaha_url, timeout=900): 84 """ 85 Install a DLC on the stateful partition. 86 87 @param dlc_id: The id of the DLC to install. 88 @param omaha_url: The Omaha URL to send the install request to. 89 90 """ 91 cmd = [self._DLCSERVICE_UTIL_CMD, '--install', '--id=%s' % dlc_id, 92 '--omaha_url=%s' % omaha_url] 93 self._run(cmd, timeout=timeout) 94 95 96 def uninstall(self, dlc_id, ignore_status=False): 97 """ 98 Uninstall a DLC. The DLC will remain on the DUT in an un-mounted state 99 and can be reinstalled without requiring an install request. To 100 completely remove a DLC, purge it instead. 101 102 @param dlc_id: The id of the DLC to uninstall. 103 @param ignore_status: Whether or not to ignore the return status when 104 running the uninstall command. 105 106 """ 107 cmd = [self._DLCSERVICE_UTIL_CMD, '--uninstall', '--id=%s' % dlc_id] 108 self._run(cmd, ignore_status=ignore_status) 109 110 111 def purge(self, dlc_id, ignore_status=False): 112 """ 113 Purge a DLC. This will completely remove the DLC from the device. 114 115 @param dlc_id: The id of the DLC to purge. 116 @param ignore_status: Whether or not to ignore the return status when 117 running the purge command. 118 119 """ 120 cmd = [self._DLCSERVICE_UTIL_CMD, '--purge', '--id=%s' % dlc_id] 121 self._run(cmd, ignore_status=ignore_status) 122 123 124 def remove_preloaded(self, dlc_id): 125 """ 126 Remove a DLC from the preload directory. DLCs in this directory can be 127 preloaded, meaning they can be installed without needing to download 128 and install them using update_engine. It is hard to differentiate 129 preloading a DLC and installing a DLC after a successful AU. After 130 AU, updated DLCs should be installed but not yet mounted. In both 131 cases, calling dlcservice_util --install should result in the DLC 132 being installed without going through Omaha/Nebraska. Clearing the 133 preload directory for a DLC allows us to verify it was updated, 134 by ruling out the possibility that it was preloaded instead. 135 136 @param dlc_id: Wipe preload directory for the DLC with this id. 137 138 """ 139 preload_dir = os.path.join(_PRELOAD_DIR, dlc_id) 140 if os.path.exists(preload_dir) and os.path.isdir(preload_dir): 141 shutil.rmtree(preload_dir) 142 143 144 def is_installed(self, dlc_id): 145 """ 146 Check if a DLC is installed. 147 148 @param dlc_id: The id of the DLC to check. 149 150 @return True if the DLC is installed, False if it's not. 151 152 """ 153 return dlc_id in self.list() 154