Home | History | Annotate | Download | only in hosts
      1 # Copyright 2016 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import logging
      6 import os
      7 import re
      8 import sys
      9 
     10 import common
     11 from autotest_lib.client.common_lib import error
     12 from autotest_lib.server import utils
     13 from autotest_lib.server.hosts import adb_host
     14 from autotest_lib.utils import emulator_manager
     15 
     16 OS_TYPE_EMULATED_BRILLO = 'emulated_brillo'
     17 OS_TYPE_DEFAULT = OS_TYPE_EMULATED_BRILLO
     18 BOARD_DEFAULT = "brilloemulator_arm"
     19 EMULATED_BRILLO_ARTIFACT_FORMAT = (
     20     '%(build_target)s-target_files-%(build_id)s.zip')
     21 EMULATED_BRILLO_DTB_FORMAT = (
     22     '%(build_target)s-dtb-%(build_id)s.zip')
     23 
     24 
     25 class EmulatedADBHost(adb_host.ADBHost):
     26     """Run an emulator as an ADB device preserving the API and assumptions of
     27     ADBHost.
     28 
     29     Currently supported emulators:
     30     * Brillo
     31         * brilloemulator_arm
     32     """
     33 
     34     def _initialize(self, *args, **kwargs):
     35         """Intialize an emulator so that existing assumptions that the host is
     36         always ready ar satisfied.
     37 
     38         @param args: pass through to ADBHost
     39         @param kwargs: pass through to ADBHost
     40         """
     41         super(EmulatedADBHost, self)._initialize(*args, **kwargs)
     42 
     43         # Verify serial
     44         m = re.match('emulator-(\d{4})', self.adb_serial)
     45         if not m:
     46             raise ValueError('Emulator serial must be in the format '
     47                              'emulator-PORT.')
     48         self.port = int(m.group(1)) + 1
     49 
     50         # Determine directory for images (needs to be persistent)
     51         tmp_dir = self.teststation.get_tmp_dir()
     52         self.imagedir = os.path.join(os.path.dirname(tmp_dir), self.adb_serial)
     53         self.teststation.run('rm -rf %s' % tmp_dir)
     54         self.teststation.run('mkdir -p %s' % self.imagedir)
     55 
     56         # Boot the emulator, if not already booted, since ADBHost assumes the
     57         # device is always available
     58         self._emulator = emulator_manager.EmulatorManager(
     59                 self.imagedir, self.port, run=self.teststation.run)
     60         self._start_emulator_if_not_started()
     61 
     62 
     63     def _start_emulator_if_not_started(self):
     64         """Boot or reboot the emulator if necessary.
     65 
     66         If the emulator is not started boot the emulator. Otherwise leave it
     67         alone. Ensure emulator is running and ready before returning.
     68         """
     69         host_os = self.get_os_type()
     70         board = self.get_board()
     71 
     72         # Check that images exist in imagedir
     73         try:
     74             self.teststation.run('test -f %s' % os.path.join(self.imagedir,
     75                                                             'system.img'))
     76 
     77         # Use default images
     78         except error.GenericHostRunError:
     79             self.teststation.run('cp %s/* %s/' % (
     80                 os.path.join('/usr/local/emulator_images', host_os, board),
     81                 self.imagedir
     82             ))
     83 
     84         if not self._emulator.find():
     85             self._emulator.start()
     86         self.wait_up()
     87         self._reset_adbd_connection()
     88 
     89 
     90     def get_os_type(self):
     91         """Determine the OS type from afe_host object or use the default if
     92         no os label / no afe_host object.
     93 
     94         @return: os type as str
     95         """
     96         info = self.host_info_store.get()
     97         return info.os or OS_TYPE_DEFAULT
     98 
     99 
    100     def get_board(self):
    101         """Determine the board from afe_host object or use the default if
    102         no board label / no afe_host object.
    103 
    104         @return: board as str
    105         """
    106         info = self.host_info_store.get()
    107         return info.board or BOARD_DEFAULT
    108 
    109 
    110     @staticmethod
    111     def check_host(host, timeout=10):
    112         """No dynamic host checking. Must be explicit.
    113 
    114         @param host: ignored
    115         @param timeout: ignored
    116 
    117         @return: False
    118         """
    119         return False
    120 
    121 
    122     def stage_emulator_artifact(self, build_url):
    123         """Download required build artifact from the given build_url to a
    124         local directory in the machine running the emulator.
    125 
    126         @param build_url: The url to use for downloading Android artifacts.
    127                           pattern: http://$devserv/static/branch/target/build_id
    128 
    129         @return: Path to the directory contains image files.
    130         """
    131         build_info = self.get_build_info_from_build_url(build_url)
    132 
    133         zipped_artifact = EMULATED_BRILLO_ARTIFACT_FORMAT % build_info
    134         dtb_artifact = EMULATED_BRILLO_DTB_FORMAT % build_info
    135         image_dir = self.teststation.get_tmp_dir()
    136 
    137         try:
    138             self.download_file(build_url, zipped_artifact, image_dir,
    139                                unzip=True)
    140             self.download_file(build_url, dtb_artifact, image_dir,
    141                                unzip=True)
    142             return image_dir
    143         except:
    144             self.teststation.run('rm -rf %s' % image_dir)
    145             raise
    146 
    147 
    148     def setup_brillo_emulator(self, build_url, build_local_path=None):
    149         """Install the Brillo DUT.
    150 
    151         Following are the steps used here to provision an android device:
    152         1. If build_local_path is not set, download the target_files zip, e.g.,
    153         brilloemulator_arm-target_files-123456.zip, and unzip it.
    154         2. Move the necessary images to a new directory.
    155         3. Determine port for ADB from serial.
    156         4. Use EmulatorManager to start the emulator.
    157 
    158         @param build_url: The url to use for downloading Android artifacts.
    159                           pattern: http://$devserver:###/static/$build
    160         @param build_local_path: The path to a local folder that contains the
    161                                  image files needed to provision the device.
    162                                  Note that the folder is in the machine running
    163                                  adb command, rather than the drone.
    164 
    165         @raises AndroidInstallError if any error occurs.
    166         """
    167         # If the build is not staged in local server yet, clean up the temp
    168         # folder used to store image files after the provision is completed.
    169         delete_build_folder = bool(not build_local_path)
    170 
    171         try:
    172             # Download image files needed for provision to a local directory.
    173             if not build_local_path:
    174                 build_local_path = self.stage_emulator_artifact(build_url)
    175 
    176             # Create directory with required files
    177             self.teststation.run('rm -rf %s && mkdir %s' % (self.imagedir,
    178                                                             self.imagedir))
    179             self.teststation.run('mv %s %s' % (
    180                     os.path.join(build_local_path, 'IMAGES', 'system.img'),
    181                     os.path.join(self.imagedir, 'system.img')
    182             ))
    183             self.teststation.run('mv %s %s' % (
    184                     os.path.join(build_local_path, 'IMAGES', 'userdata.img'),
    185                     os.path.join(self.imagedir, 'userdata.img')
    186             ))
    187             self.teststation.run('mv %s %s' % (
    188                     os.path.join(build_local_path, 'BOOT', 'kernel'),
    189                     os.path.join(self.imagedir, 'kernel')
    190             ))
    191             self.teststation.run('mv %s/*.dtb %s' % (build_local_path,
    192                                                      self.imagedir))
    193 
    194             # Start the emulator
    195             self._emulator.force_stop()
    196             self._start_emulator_if_not_started()
    197 
    198         except Exception as e:
    199             logging.error('Install Brillo build failed with error: %s', e)
    200             # Re-raise the exception with type of AndroidInstallError.
    201             raise (adb_host.AndroidInstallError, sys.exc_info()[1],
    202                    sys.exc_info()[2])
    203         finally:
    204             if delete_build_folder:
    205                 self.teststation.run('rm -rf %s' % build_local_path)
    206                 logging.info('Successfully installed Android build staged at '
    207                              '%s.', build_url)
    208 
    209 
    210     def machine_install(self, build_url=None, build_local_path=None, wipe=True,
    211                         flash_all=False, os_type=None):
    212         """Install the DUT.
    213 
    214         @param build_url: The url to use for downloading Android artifacts.
    215                           pattern: http://$devserver:###/static/$build.
    216                           If build_url is set to None, the code may try
    217                           _parser.options.image to do the installation. If none
    218                           of them is set, machine_install will fail.
    219         @param build_local_path: The path to a local directory that contains the
    220                                  image files needed to provision the device.
    221         @param wipe: No-op
    222         @param flash_all: No-op
    223         @param os_type: OS to install (overrides label).
    224 
    225         @returns A tuple of (image_name, host_attributes). image_name is the
    226                  name of image installed, e.g.,
    227                  git_mnc-release/shamu-userdebug/1234
    228                  host_attributes is a dictionary of (attribute, value), which
    229                  can be saved to afe_host_attributes table in database. This
    230                  method returns a dictionary with a single entry of
    231                  `job_repo_url_[adb_serial]`: devserver_url, where devserver_url
    232                  is a url to the build staged on devserver.
    233         """
    234         os_type = os_type or self.get_os_type()
    235         if not build_url and self._parser.options.image:
    236             build_url, _ = self.stage_build_for_install(
    237                     self._parser.options.image, os_type=os_type)
    238         if os_type == OS_TYPE_EMULATED_BRILLO:
    239             self.setup_brillo_emulator(
    240                     build_url=build_url, build_local_path=build_local_path)
    241             self.ensure_adb_mode()
    242         else:
    243             raise error.InstallError(
    244                     'Installation of os type %s is not supported.' %
    245                     os_type)
    246         return (build_url.split('static/')[-1],
    247                 {self.job_repo_url_attribute: build_url})
    248 
    249 
    250     def repair(self):
    251         """No-op. No repair procedures for emulated devices.
    252         """
    253         pass
    254 
    255 
    256     def verify_software(self):
    257         """Verify commands are available on teststation.
    258 
    259         @return: Bool - teststation has necessary software installed.
    260         """
    261         adb = self.teststation.run('which adb')
    262         qemu = self.teststation.run('which qemu-system-arm')
    263         unzip = self.teststation.run('which unzip')
    264 
    265         return bool(adb and qemu and unzip)
    266 
    267 
    268     def fastboot_run(self, command, **kwargs):
    269         """No-op, emulators do not support fastboot.
    270 
    271         @param command: command to not execute
    272         @param kwargs: additional arguments to ignore
    273 
    274         @return: empty CmdResult object
    275         """
    276         return utils.CmdResult()
    277 
    278 
    279     def get_labels(self):
    280         """No-op, emulators do not have any detectable labels.
    281 
    282         @return: empty list
    283         """
    284         return []
    285 
    286 
    287     def get_platform(self):
    288         """@return: emulated_adb
    289         """
    290         return 'emulated_adb'
    291 
    292