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