| # Copyright 2020 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import json |
| import logging |
| import os |
| import shutil |
| |
| from autotest_lib.client.common_lib import utils |
| |
| _DEFAULT_RUN = utils.run |
| |
| # Directory for preloaded DLCs that may be packaged with the OS. Preloaded |
| # DLCs can be installed without needing to go through update_engine. |
| _PRELOAD_DIR = '/mnt/stateful_partition/var_overlay/cache/dlc-images/' |
| |
| class DLCUtil(object): |
| """ |
| Wrapper around dlcservice_util for tests that require DLC management. |
| |
| For dlc_util to work seamlessly in both client and server tests, we need |
| those tests to define the _run() function: |
| server side: host.run |
| client side: utils.run |
| |
| """ |
| _DLCSERVICE_UTIL_CMD = "dlcservice_util" |
| _SAMPLE_DLC_ID = "sample-dlc" |
| |
| def __init__(self, run_func=_DEFAULT_RUN): |
| """ |
| Initialize this class with _run() function. |
| |
| @param run_func: The function to use to run commands on the client. |
| Defaults for use by client tests, but can be |
| overwritten to run remotely from a server test. |
| |
| """ |
| self._run = run_func |
| |
| |
| def list(self): |
| """ |
| List DLCs that are installed on the DUT and additional information |
| about them such as name, version, root_mount, and size. The output is |
| a dictionary containing the result of dlcservice_util --list, which |
| looks like: |
| { |
| "sample-dlc": [ { |
| "fs-type": "squashfs", |
| "id": "sample-dlc", |
| "image_type": "dlc", |
| "manifest": "/opt/google/dlc/sample-dlc/package/imageloader.json", |
| "name": "Sample DLC", |
| "package": "package", |
| "preallocated_size": "4194304", |
| "root_mount": "/run/imageloader/sample-dlc/package", |
| "size": "53248", |
| "version": "1.0.0-r10" |
| } ] |
| } |
| |
| @return Dictionary containing information about the installed DLCs, |
| whose keys are the DLC IDs. |
| |
| """ |
| status = self._run([self._DLCSERVICE_UTIL_CMD, '--list']) |
| logging.info(status) |
| |
| return json.loads(status.stdout) |
| |
| |
| def install(self, dlc_id, omaha_url, timeout=900): |
| """ |
| Install a DLC on the stateful partition. |
| |
| @param dlc_id: The id of the DLC to install. |
| @param omaha_url: The Omaha URL to send the install request to. |
| |
| """ |
| cmd = [self._DLCSERVICE_UTIL_CMD, '--install', '--id=%s' % dlc_id, |
| '--omaha_url=%s' % omaha_url] |
| self._run(cmd, timeout=timeout) |
| |
| |
| def uninstall(self, dlc_id, ignore_status=False): |
| """ |
| Uninstall a DLC. The DLC will remain on the DUT in an un-mounted state |
| and can be reinstalled without requiring an install request. To |
| completely remove a DLC, purge it instead. |
| |
| @param dlc_id: The id of the DLC to uninstall. |
| @param ignore_status: Whether or not to ignore the return status when |
| running the uninstall command. |
| |
| """ |
| cmd = [self._DLCSERVICE_UTIL_CMD, '--uninstall', '--id=%s' % dlc_id] |
| self._run(cmd, ignore_status=ignore_status) |
| |
| |
| def purge(self, dlc_id, ignore_status=False): |
| """ |
| Purge a DLC. This will completely remove the DLC from the device. |
| |
| @param dlc_id: The id of the DLC to purge. |
| @param ignore_status: Whether or not to ignore the return status when |
| running the purge command. |
| |
| """ |
| cmd = [self._DLCSERVICE_UTIL_CMD, '--purge', '--id=%s' % dlc_id] |
| self._run(cmd, ignore_status=ignore_status) |
| |
| |
| def remove_preloaded(self, dlc_id): |
| """ |
| Remove a DLC from the preload directory. DLCs in this directory can be |
| preloaded, meaning they can be installed without needing to download |
| and install them using update_engine. It is hard to differentiate |
| preloading a DLC and installing a DLC after a successful AU. After |
| AU, updated DLCs should be installed but not yet mounted. In both |
| cases, calling dlcservice_util --install should result in the DLC |
| being installed without going through Omaha/Nebraska. Clearing the |
| preload directory for a DLC allows us to verify it was updated, |
| by ruling out the possibility that it was preloaded instead. |
| |
| @param dlc_id: Wipe preload directory for the DLC with this id. |
| |
| """ |
| preload_dir = os.path.join(_PRELOAD_DIR, dlc_id) |
| if os.path.exists(preload_dir) and os.path.isdir(preload_dir): |
| shutil.rmtree(preload_dir) |
| |
| |
| def is_installed(self, dlc_id): |
| """ |
| Check if a DLC is installed. |
| |
| @param dlc_id: The id of the DLC to check. |
| |
| @return True if the DLC is installed, False if it's not. |
| |
| """ |
| return dlc_id in self.list().keys() |