• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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