xref: /aosp_15_r20/external/autotest/server/cros/minios/minios_util.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Lint as: python2, python3
2*9c5db199SXin Li# Copyright 2022 The Chromium OS Authors. All rights reserved.
3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
4*9c5db199SXin Li# found in the LICENSE file.
5*9c5db199SXin Li
6*9c5db199SXin Lifrom __future__ import absolute_import
7*9c5db199SXin Lifrom __future__ import division
8*9c5db199SXin Lifrom __future__ import print_function
9*9c5db199SXin Li
10*9c5db199SXin Liimport json
11*9c5db199SXin Liimport logging
12*9c5db199SXin Liimport os
13*9c5db199SXin Liimport requests
14*9c5db199SXin Liimport six
15*9c5db199SXin Li
16*9c5db199SXin Lifrom autotest_lib.client.common_lib import autotemp
17*9c5db199SXin Lifrom autotest_lib.client.common_lib import error
18*9c5db199SXin Lifrom autotest_lib.client.cros.update_engine import nebraska_wrapper
19*9c5db199SXin Li
20*9c5db199SXin Li
21*9c5db199SXin Liclass NebraskaService:
22*9c5db199SXin Li    """
23*9c5db199SXin Li    Remotely sets up nebraska on the DUT.
24*9c5db199SXin Li
25*9c5db199SXin Li    This service is different from
26*9c5db199SXin Li    `autotest_lib.client.cros.update_engine.nebraska_wrapper.NebraskaWrapper` in
27*9c5db199SXin Li    that it is used by server-only tests to remotely launch nebraska on the DUT.
28*9c5db199SXin Li
29*9c5db199SXin Li    """
30*9c5db199SXin Li
31*9c5db199SXin Li    def __init__(self, test, host, payload_url=None, **props_to_override):
32*9c5db199SXin Li        """
33*9c5db199SXin Li        Initializes the NebraskaService.
34*9c5db199SXin Li
35*9c5db199SXin Li        @param test: Instance of the test using the service.
36*9c5db199SXin Li        @param host: The DUT we will be running on.
37*9c5db199SXin Li        @param payload_url: The payload that will be returned in responses for
38*9c5db199SXin Li            update requests. This can be a single URL string or a list of URLs
39*9c5db199SXin Li            to return multiple payload URLs (such as a platform payload + DLC
40*9c5db199SXin Li            payloads) in the responses.
41*9c5db199SXin Li        @param props_to_override: Dictionary of key/values to use in responses
42*9c5db199SXin Li            instead of the default values in payload_url's properties file.
43*9c5db199SXin Li
44*9c5db199SXin Li        """
45*9c5db199SXin Li        self._host = host
46*9c5db199SXin Li        self._test = test
47*9c5db199SXin Li
48*9c5db199SXin Li        # _update_metadata_dir is the directory for storing the json metadata
49*9c5db199SXin Li        # files associated with the payloads.
50*9c5db199SXin Li        # _update_payloads_address is the address of the update server where
51*9c5db199SXin Li        # the payloads are staged.
52*9c5db199SXin Li        self._update_metadata_dir = None
53*9c5db199SXin Li        self._update_payloads_address = None
54*9c5db199SXin Li
55*9c5db199SXin Li        if payload_url:
56*9c5db199SXin Li            # Normalize payload_url to be a list.
57*9c5db199SXin Li            if not isinstance(payload_url, list):
58*9c5db199SXin Li                payload_url = [payload_url]
59*9c5db199SXin Li
60*9c5db199SXin Li            self._update_metadata_dir = self._host.get_tmp_dir()
61*9c5db199SXin Li            self._update_payloads_address = ''.join(
62*9c5db199SXin Li                    payload_url[0].rpartition('/')[0:2])
63*9c5db199SXin Li
64*9c5db199SXin Li            # Download the metadata files and save them in a tempdir for general
65*9c5db199SXin Li            # use.
66*9c5db199SXin Li            for url in payload_url:
67*9c5db199SXin Li                self.get_payload_properties_file(url,
68*9c5db199SXin Li                                                 self._update_metadata_dir,
69*9c5db199SXin Li                                                 **props_to_override)
70*9c5db199SXin Li
71*9c5db199SXin Li    def get_payload_properties_file(self, payload_url, target_dir, **kwargs):
72*9c5db199SXin Li        """
73*9c5db199SXin Li        Downloads the payload properties file into a directory on the DUT.
74*9c5db199SXin Li
75*9c5db199SXin Li        @param payload_url: The URL to the update payload file.
76*9c5db199SXin Li        @param target_dir: The directory on the DUT to download the file into.
77*9c5db199SXin Li        @param kwargs: A dictionary of key/values that needs to be overridden on
78*9c5db199SXin Li            the payload properties file.
79*9c5db199SXin Li
80*9c5db199SXin Li        """
81*9c5db199SXin Li        payload_props_url = payload_url + '.json'
82*9c5db199SXin Li        _, _, file_name = payload_props_url.rpartition('/')
83*9c5db199SXin Li        try:
84*9c5db199SXin Li            response = json.loads(requests.get(payload_props_url).text)
85*9c5db199SXin Li            # Override existing keys if any.
86*9c5db199SXin Li            for k, v in six.iteritems(kwargs):
87*9c5db199SXin Li                # Don't set default None values. We don't want to override good
88*9c5db199SXin Li                # values to None.
89*9c5db199SXin Li                if v is not None:
90*9c5db199SXin Li                    response[k] = v
91*9c5db199SXin Li            self._write_remote_file(os.path.join(target_dir, file_name),
92*9c5db199SXin Li                                    json.dumps(response))
93*9c5db199SXin Li
94*9c5db199SXin Li        except (requests.exceptions.RequestException, IOError,
95*9c5db199SXin Li                ValueError) as err:
96*9c5db199SXin Li            raise error.TestError(
97*9c5db199SXin Li                    'Failed to get update payload properties: %s with error: %s'
98*9c5db199SXin Li                    % (payload_props_url, err))
99*9c5db199SXin Li
100*9c5db199SXin Li    def start(self, **kwargs):
101*9c5db199SXin Li        """Launch nebraska on DUT."""
102*9c5db199SXin Li        # Generate nebraska configuration.
103*9c5db199SXin Li        self._write_remote_file(
104*9c5db199SXin Li                nebraska_wrapper.NEBRASKA_CONFIG,
105*9c5db199SXin Li                json.dumps(self._create_startup_config(**kwargs)),
106*9c5db199SXin Li        )
107*9c5db199SXin Li        logging.info('Start nebraska service')
108*9c5db199SXin Li        self._host.upstart_restart('nebraska')
109*9c5db199SXin Li        self._host.wait_for_service('nebraska')
110*9c5db199SXin Li
111*9c5db199SXin Li    def stop(self):
112*9c5db199SXin Li        """Stop Nebraska service."""
113*9c5db199SXin Li        logging.info('Stop nebraska service')
114*9c5db199SXin Li        self._host.upstart_stop('nebraska')
115*9c5db199SXin Li        self._host.run('rm', args=('-f', nebraska_wrapper.NEBRASKA_CONFIG))
116*9c5db199SXin Li
117*9c5db199SXin Li    def _create_startup_config(self, **kwargs):
118*9c5db199SXin Li        """
119*9c5db199SXin Li        Creates a nebraska startup config file. If this file is present, nebraska
120*9c5db199SXin Li        can be started by upstart.
121*9c5db199SXin Li
122*9c5db199SXin Li        @param kwargs: A dictionary of key/values for nebraska config options.
123*9c5db199SXin Li            See platform/dev/nebraska/nebraska.py for more info.
124*9c5db199SXin Li
125*9c5db199SXin Li        @return: A dictionary of nebraska config options.
126*9c5db199SXin Li
127*9c5db199SXin Li        """
128*9c5db199SXin Li        conf = {}
129*9c5db199SXin Li        if self._update_metadata_dir:
130*9c5db199SXin Li            conf['update_metadata'] = self._update_metadata_dir
131*9c5db199SXin Li        if self._update_payloads_address:
132*9c5db199SXin Li            conf['update_payloads_address'] = self._update_payloads_address
133*9c5db199SXin Li
134*9c5db199SXin Li        for k, v in six.iteritems(kwargs):
135*9c5db199SXin Li            conf[k] = v
136*9c5db199SXin Li        return conf
137*9c5db199SXin Li
138*9c5db199SXin Li    def _create_remote_dir(self, remote_dir, owner=None):
139*9c5db199SXin Li        """
140*9c5db199SXin Li        Create directory on DUT.
141*9c5db199SXin Li
142*9c5db199SXin Li        @param remote_dir: The directory to create.
143*9c5db199SXin Li        @param owner: Set owner of the remote directory.
144*9c5db199SXin Li
145*9c5db199SXin Li        """
146*9c5db199SXin Li        permission = '1777'
147*9c5db199SXin Li        if owner:
148*9c5db199SXin Li            permission = '1770'
149*9c5db199SXin Li        self._host.run(['mkdir', '-p', '-m', permission, remote_dir])
150*9c5db199SXin Li        if owner:
151*9c5db199SXin Li            self._host.run('chown', args=(owner, remote_dir))
152*9c5db199SXin Li
153*9c5db199SXin Li    def _write_remote_file(self,
154*9c5db199SXin Li                           filepath,
155*9c5db199SXin Li                           content,
156*9c5db199SXin Li                           permission=None,
157*9c5db199SXin Li                           owner=None):
158*9c5db199SXin Li        """
159*9c5db199SXin Li        Write content to filepath on DUT.
160*9c5db199SXin Li
161*9c5db199SXin Li        @param permission: set permission to 0xxx octal number of remote file.
162*9c5db199SXin Li        @param owner: set owner of remote file.
163*9c5db199SXin Li
164*9c5db199SXin Li        """
165*9c5db199SXin Li        tmpdir = autotemp.tempdir(unique_id='minios')
166*9c5db199SXin Li        tmp_path = os.path.join(tmpdir.name, os.path.basename(filepath))
167*9c5db199SXin Li        with open(tmp_path, 'w') as f:
168*9c5db199SXin Li            f.write(content)
169*9c5db199SXin Li        if permission is not None:
170*9c5db199SXin Li            os.chmod(tmp_path, permission)
171*9c5db199SXin Li        self._create_remote_dir(os.path.dirname(filepath), owner)
172*9c5db199SXin Li        self._host.send_file(tmp_path, filepath, delete_dest=True)
173*9c5db199SXin Li        if owner is not None:
174*9c5db199SXin Li            self._host.run('chown', args=(owner, filepath))
175*9c5db199SXin Li        tmpdir.clean()
176