Snap for 5434517 from b2010fc2ff94af33259503aa28daa6eb70ff4f2d to qt-release

Change-Id: I18b432b01913a7748ea39acb648319d60da7c5e7
diff --git a/client/site_tests/graphics_Sanity/control b/client/site_tests/graphics_Sanity/control
index ccb2b87..67ee566 100644
--- a/client/site_tests/graphics_Sanity/control
+++ b/client/site_tests/graphics_Sanity/control
@@ -8,7 +8,8 @@
 CRITERIA = """
 This test fails if application screen shots cannot capture the screen output.
 """
-ATTRIBUTES = ("suite:bvt-cq, suite:graphics, suite:graphics_per-day,"
+#TODO(pwang): crbug.com/948506, change suite:bvt-perbuild back to bvt-cq.
+ATTRIBUTES = ("suite:bvt-perbuild, suite:graphics, suite:graphics_per-day,"
               "suite:graphics_system")
 TIME='SHORT'
 TEST_CATEGORY = 'Functional'
diff --git a/client/site_tests/touch_ScrollDirection/touch_ScrollDirection.py b/client/site_tests/touch_ScrollDirection/touch_ScrollDirection.py
index d38b7d5..69708ed 100644
--- a/client/site_tests/touch_ScrollDirection/touch_ScrollDirection.py
+++ b/client/site_tests/touch_ScrollDirection/touch_ScrollDirection.py
@@ -90,6 +90,9 @@
             # Setup.
             self._set_autotest_ext(cr.autotest_ext)
             self._open_events_page(cr)
+            self._events.expand_page()
+            self._events.set_prevent_defaults(False)
+
             self._emulate_mouse()
             self._center_cursor()
 
diff --git a/server/cros/network/netperf_runner.py b/server/cros/network/netperf_runner.py
index 6b1158e..a63af25 100644
--- a/server/cros/network/netperf_runner.py
+++ b/server/cros/network/netperf_runner.py
@@ -525,7 +525,7 @@
     def _restart_netserv(self):
         logging.info('Starting netserver...')
         self._kill_netserv()
-        self._server_host.run('%s -p %d >/dev/null 2>&1' %
+        self._server_host.run('%s -p %d' %
                               (self._command_netserv, self.NETPERF_PORT))
         startup_time = time.time()
         self._client_proxy.firewall_open('tcp', self._server_proxy.wifi_ip)
diff --git a/site_utils/deployment/install.py b/site_utils/deployment/install.py
index fbba6d1..cece839 100644
--- a/site_utils/deployment/install.py
+++ b/site_utils/deployment/install.py
@@ -69,6 +69,7 @@
 from autotest_lib.client.common_lib import time_utils
 from autotest_lib.client.common_lib import utils
 from autotest_lib.client.common_lib.cros import retry
+from autotest_lib.server import afe_utils
 from autotest_lib.server import constants
 from autotest_lib.server import frontend
 from autotest_lib.server import hosts
@@ -505,14 +506,13 @@
     @param host       Host instance for the DUT being installed.
     @param arguments  Command line arguments with options.
     """
+    repair_image = _get_cros_repair_image_name(host)
+    logging.info('Using repair image %s', repair_image)
     if arguments.dry_run:
         return
     if arguments.stageusb:
         try:
-            preparedut.download_image_to_servo_usb(
-                    host,
-                    host.get_cros_repair_image_name(),
-            )
+            preparedut.download_image_to_servo_usb(host, repair_image)
         except Exception as e:
             logging.exception('Failed to stage image on USB: %s', e)
             raise Exception('USB staging failed')
@@ -520,7 +520,7 @@
         try:
             if arguments.using_servo:
                 logging.debug('Install FW using servo.')
-                preparedut.flash_firmware_using_servo(host)
+                preparedut.flash_firmware_using_servo(host, repair_image)
             else:
                 logging.debug('Install FW by chromeos-firmwareupdate.')
                 preparedut.install_firmware(host, arguments.force_firmware)
@@ -831,6 +831,18 @@
     return host_attributes
 
 
+def _get_cros_repair_image_name(host):
+    """Get the CrOS repair image name for given host.
+
+    @param host: hosts.CrosHost object. This object need not have an AFE
+                 reference.
+    """
+    info = host.host_info_store.get()
+    if not info.board:
+        raise InstallFailedError('Unknown board for given host')
+    return afe_utils.get_stable_cros_image_name(info.board)
+
+
 def install_duts(arguments):
     """Install a test image on DUTs, and deploy them.
 
diff --git a/site_utils/deployment/prepare/dut.py b/site_utils/deployment/prepare/dut.py
index 97af542..37dec6b 100644
--- a/site_utils/deployment/prepare/dut.py
+++ b/site_utils/deployment/prepare/dut.py
@@ -29,7 +29,7 @@
 
 @contextlib.contextmanager
 def create_host(hostname, board, model, servo_hostname, servo_port,
-                servo_serial=None, uart_logs_dir=None):
+                servo_serial=None, logs_dir=None):
     """Yield a server.hosts.CrosHost object to use for DUT preparation.
 
     This object contains just enough inventory data to be able to prepare the
@@ -43,7 +43,8 @@
     @param servo_hostname:  FQDN of the servo host controlling the DUT.
     @param servo_port:      Servo host port used for the controlling servo.
     @param servo_serial:    (Optional) Serial number of the controlling servo.
-    @param uart_logs_dir:   (Optional) Directory to save UART logs.
+    @param logs_dir:        (Optional) Directory to save logs obtained from the
+                            host.
 
     @yield a server.hosts.Host object.
     """
@@ -72,7 +73,7 @@
             **servo_host.get_servo_args_for_host(host))
     _prepare_servo(servohost)
     host.set_servo_host(servohost)
-    host.servo.uart_logs_dir = uart_logs_dir
+    host.servo.uart_logs_dir = logs_dir
     try:
         yield host
     finally:
@@ -88,14 +89,75 @@
     host.servo.image_to_servo_usb(host.stage_image_for_servo(build))
 
 
-def flash_firmware_using_servo(host):
+def install_test_image(host):
+    """Install the test image for the given build to DUT.
+
+    This function assumes that the required image is already downloaded onto the
+    USB key connected to the DUT via servo.
+
+    @param host   servers.host.Host object.
+    """
+    host.servo_install()
+
+
+def flash_firmware_using_servo(host, build):
     """Flash DUT firmware directly using servo.
 
     Rather than running `chromeos-firmwareupdate` on DUT, we can flash DUT
     firmware directly using servo (run command `flashrom`, etc. on servo). In
     this way, we don't require DUT to be in dev mode and with dev_boot_usb
     enabled."""
-    host.firmware_install(build=host.get_cros_repair_image_name())
+    host.firmware_install(build)
+
+
+def install_firmware(host, force):
+    """Install dev-signed firmware after removing write-protect.
+
+    At start, it's assumed that hardware write-protect is disabled,
+    the DUT is in dev mode, and the servo's USB stick already has a
+    test image installed.
+
+    The firmware is installed by powering on and typing ctrl+U on
+    the keyboard in order to boot the test image from USB.  Once
+    the DUT is booted, we run a series of commands to install the
+    read-only firmware from the test image.  Then we clear debug
+    mode, and shut down.
+
+    @param host   Host instance to use for servo and ssh operations.
+    @param force  Boolean value determining if firmware install is forced.
+    """
+    servo = host.servo
+    # First power on.  We sleep to allow the firmware plenty of time
+    # to display the dev-mode screen; some boards take their time to
+    # be ready for the ctrl+U after power on.
+    servo.get_power_state_controller().power_off()
+    servo.switch_usbkey('dut')
+    servo.get_power_state_controller().power_on()
+    time.sleep(10)
+    # Dev mode screen should be up now:  type ctrl+U and wait for
+    # boot from USB to finish.
+    servo.ctrl_u()
+    if not host.wait_up(timeout=host.USB_BOOT_TIMEOUT):
+        raise Exception('DUT failed to boot in dev mode for '
+                        'firmware update')
+    # Disable software-controlled write-protect for both FPROMs, and
+    # install the RO firmware.
+    for fprom in ['host', 'ec']:
+        host.run('flashrom -p %s --wp-disable' % fprom,
+                 ignore_status=True)
+
+    fw_update_log = '/mnt/stateful_partition/home/root/cros-fw-update.log'
+    pid = _start_firmware_update(host, force, fw_update_log)
+    _wait_firmware_update_process(host, pid)
+    _check_firmware_update_result(host, fw_update_log)
+
+    # Get us out of dev-mode and clear GBB flags.  GBB flags are
+    # non-zero because boot from USB was enabled.
+    host.run('/usr/share/vboot/bin/set_gbb_flags.sh 0',
+             ignore_status=True)
+    host.run('crossystem disable_dev_request=1',
+             ignore_status=True)
+    host.halt()
 
 
 def _start_firmware_update(host, force, result_file):
@@ -158,67 +220,6 @@
         raise Exception("chromeos-firmwareupdate failed!")
 
 
-def install_firmware(host, force):
-    """Install dev-signed firmware after removing write-protect.
-
-    At start, it's assumed that hardware write-protect is disabled,
-    the DUT is in dev mode, and the servo's USB stick already has a
-    test image installed.
-
-    The firmware is installed by powering on and typing ctrl+U on
-    the keyboard in order to boot the test image from USB.  Once
-    the DUT is booted, we run a series of commands to install the
-    read-only firmware from the test image.  Then we clear debug
-    mode, and shut down.
-
-    @param host   Host instance to use for servo and ssh operations.
-    @param force  Boolean value determining if firmware install is forced.
-    """
-    servo = host.servo
-    # First power on.  We sleep to allow the firmware plenty of time
-    # to display the dev-mode screen; some boards take their time to
-    # be ready for the ctrl+U after power on.
-    servo.get_power_state_controller().power_off()
-    servo.switch_usbkey('dut')
-    servo.get_power_state_controller().power_on()
-    time.sleep(10)
-    # Dev mode screen should be up now:  type ctrl+U and wait for
-    # boot from USB to finish.
-    servo.ctrl_u()
-    if not host.wait_up(timeout=host.USB_BOOT_TIMEOUT):
-        raise Exception('DUT failed to boot in dev mode for '
-                        'firmware update')
-    # Disable software-controlled write-protect for both FPROMs, and
-    # install the RO firmware.
-    for fprom in ['host', 'ec']:
-        host.run('flashrom -p %s --wp-disable' % fprom,
-                 ignore_status=True)
-
-    fw_update_log = '/mnt/stateful_partition/home/root/cros-fw-update.log'
-    pid = _start_firmware_update(host, force, fw_update_log)
-    _wait_firmware_update_process(host, pid)
-    _check_firmware_update_result(host, fw_update_log)
-
-    # Get us out of dev-mode and clear GBB flags.  GBB flags are
-    # non-zero because boot from USB was enabled.
-    host.run('/usr/share/vboot/bin/set_gbb_flags.sh 0',
-             ignore_status=True)
-    host.run('crossystem disable_dev_request=1',
-             ignore_status=True)
-    host.halt()
-
-
-def install_test_image(host):
-    """Install the test image for the given build to DUT.
-
-    This function assumes that the required image is already downloaded onto the
-    USB key connected to the DUT via servo.
-
-    @param host   servers.host.Host object.
-    """
-    host.servo_install()
-
-
 def _prepare_servo(servohost):
     """Prepare servo connected to host for installation steps.
 
diff --git a/site_utils/deployment/prepare/main.py b/site_utils/deployment/prepare/main.py
old mode 100644
new mode 100755
index c35eb58..9a40a39
--- a/site_utils/deployment/prepare/main.py
+++ b/site_utils/deployment/prepare/main.py
@@ -1,45 +1,55 @@
-#!/usr/bin/env python
+#!/usr/bin/python -u
 # Copyright 2019 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.
 
-"""Tool to (re)prepare a DUT for lab deployment.
-
-TODO(this docstring is a stub).
-"""
+"""Tool to (re)prepare a DUT for lab deployment."""
 
 from __future__ import absolute_import
 from __future__ import division
 from __future__ import print_function
 
 import argparse
+import errno
 import logging
+import logging.config
+import os
 
 import common
+from autotest_lib.server import afe_utils
+from autotest_lib.server.hosts import file_store
 from autotest_lib.site_utils.deployment.prepare import dut as preparedut
 
+
+class DutPreparationError(Exception):
+  """Generic error raised during DUT preparation."""
+
+
 def main():
-  """Tool to (re)prepare a DUT for lab deployment.
-
-  TODO(this docstring is a stub).
-  """
+  """Tool to (re)prepare a DUT for lab deployment."""
   opts = _parse_args()
-  # Setup tempfile to capture trash dropped by autotest?
-  # Setup logging to avoid dumping everything to stdout?
-  logging.basicConfig(level=logging.DEBUG)
+  _configure_logging('prepare_dut', os.path.join(opts.results_dir, _LOG_FILE))
 
-  with preparedut.create_host(
-      opts.hostname, opts.board, opts.model, opts.servo_hostname,
-      opts.servo_port, opts.servo_serial, opts.uart_logs_dir) as host:
+  info = _read_store(opts.host_info_file)
+  repair_image = _get_cros_repair_image_name(info.board)
+  logging.info('Using repair image %s, obtained from AFE', repair_image)
+  with _create_host(opts.hostname, info, opts.results_dir) as host:
+    if opts.dry_run:
+      logging.info('DRY RUN: Would have run actions %s', opts.actions)
+      return
 
     if 'stage-usb' in opts.actions:
-      preparedut.download_image_to_servo_usb(host, opts.build)
+      preparedut.download_image_to_servo_usb(host, repair_image)
     if 'install-firmware' in opts.actions:
       preparedut.install_firmware(host, opts.force_firmware)
     if 'install-test-image' in opts.actions:
       preparedut.install_test_image(host)
 
 
+_LOG_FILE = 'prepare_dut.log'
+_DUT_LOGS_DIR = 'dut_logs'
+
+
 def _parse_args():
   parser = argparse.ArgumentParser(
       description='Prepare / validate DUT for lab deployment.')
@@ -51,53 +61,132 @@
       help='DUT preparation actions to execute.',
   )
   parser.add_argument(
+      '--dry-run',
+      action='store_true',
+      default=False,
+      help='Run in dry-run mode. No changes will be made to the DUT.',
+  )
+  parser.add_argument(
+      '--results-dir',
+      required=True,
+      help='Directory to drop logs and output artifacts in.',
+  )
+
+  parser.add_argument(
       '--hostname',
       required=True,
       help='Hostname of the DUT to prepare.',
   )
-
   parser.add_argument(
-      '--board',
+      '--host-info-file',
       required=True,
-      help='Board label of the DUT to prepare.',
-  )
-  parser.add_argument(
-      '--model',
-      required=True,
-      help='Model label of the DUT to prepare.',
-  )
-  parser.add_argument(
-      '--build',
-      required=True,
-      help='Chrome OS image version to use for installation.',
+      help=('Full path to HostInfo file.'
+            ' DUT inventory information is read from the HostInfo file.'),
   )
 
   parser.add_argument(
-      '--servo-hostname',
-      required=True,
-      help='Hostname of the servo host connected to the DUT.',
-  )
-  parser.add_argument(
-      '--servo-port',
-      required=True,
-      help='Servo host port (to be) used for the controlling servo.',
-  )
-  parser.add_argument(
-      '--servo-serial',
-      help='Serial number of the controlling servo.',
-  )
-  parser.add_argument(
       '--force-firmware',
       action='store_true',
       help='Force firmware isntallation via chromeos-installfirmware.',
   )
-  parser.add_argument(
-      '--uart-logs-dir',
-      help='The directory to save UART logs.'
-  )
 
   return parser.parse_args()
 
 
+def _configure_logging(name, tee_file):
+    """Configure logging globally.
+
+    @param name: Name to prepend to log messages.
+                 This should be the name of the program.
+    @param tee_file: File to tee logs to, in addition to stderr.
+    """
+    logging.config.dictConfig({
+        'version': 1,
+        'formatters': {
+            'stderr': {
+                'format': ('{name}: '
+                           '%(asctime)s:%(levelname)s'
+                           ':%(module)s:%(funcName)s:%(lineno)d'
+                           ': %(message)s'
+                           .format(name=name)),
+            },
+            'tee_file': {
+                'format': ('%(asctime)s:%(levelname)s'
+                           ':%(module)s:%(funcName)s:%(lineno)d'
+                           ': %(message)s'),
+            },
+        },
+        'handlers': {
+            'stderr': {
+                'class': 'logging.StreamHandler',
+                'formatter': 'stderr',
+            },
+            'tee_file': {
+                'class': 'logging.FileHandler',
+                'formatter': 'tee_file',
+                'filename': tee_file,
+            },
+        },
+        'root': {
+            'level': 'DEBUG',
+            'handlers': ['stderr', 'tee_file'],
+        },
+        'disable_existing_loggers': False,
+    })
+
+
+def _read_store(path):
+  """Read a HostInfo from a file at path."""
+  store = file_store.FileStore(path)
+  return store.get()
+
+
+def _create_host(hostname, info, results_dir):
+  """Yield a hosts.CrosHost object with the given inventory information.
+
+  @param hostname: Hostname of the DUT.
+  @param info: A HostInfo with the inventory information to use.
+  @param results_dir: Path to directory for logs / output artifacts.
+  @yield server.hosts.CrosHost object.
+  """
+  if not info.board:
+    raise DutPreparationError('No board in DUT labels')
+  if not info.model:
+    raise DutPreparationError('No model in DUT labels')
+
+  servo_args = {}
+  if 'servo_host' not in info.attributes:
+    raise DutPreparationError('No servo_host in DUT attributes')
+  if 'servo_port' not in info.attributes:
+    raise DutPreparationError('No servo_port in DUT attributes')
+
+  dut_logs_dir = os.path.join(results_dir, _DUT_LOGS_DIR)
+  try:
+    os.makedirs(dut_logs_dir)
+  except OSError as e:
+    if e.errno != errno.EEXIST:
+      raise
+
+  return preparedut.create_host(
+      hostname,
+      info.board,
+      info.model,
+      info.attributes['servo_host'],
+      info.attributes['servo_port'],
+      info.attributes.get('servo_serial', ''),
+      dut_logs_dir,
+  )
+
+
+def _get_cros_repair_image_name(board):
+  """Get the CrOS repair image name for given host.
+
+  TODO(pprabhu): This is an evil function with dependence on the environment
+  (global_config information) and the AFE. Remove this dependence when stable
+  image mappings move off of the AFE.
+  """
+  return afe_utils.get_stable_cros_image_name(board)
+
+
 if __name__ == '__main__':
   main()
diff --git a/site_utils/run_suite.py b/site_utils/run_suite.py
index 35eeb39..35bbe5b 100755
--- a/site_utils/run_suite.py
+++ b/site_utils/run_suite.py
@@ -54,9 +54,19 @@
 
 import common
 from chromite.lib import buildbot_annotations as annotations
+from chromite.lib import gs
+from chromite.lib import osutils
 
 from django.core import exceptions as django_exceptions
 
+try:
+    from suite_scheduler import config_reader
+    from suite_scheduler import skylab
+except ImportError:
+    # For unittest
+    config_reader = None
+    skylab = None
+
 from autotest_lib.client.common_lib import control_data
 from autotest_lib.client.common_lib import error
 from autotest_lib.client.common_lib import global_config
@@ -90,6 +100,13 @@
 _DEFAULT_AUTOTEST_INSTANCE = CONFIG.get_config_value(
         'SERVER', 'hostname', type=str)
 _URL_PATTERN = CONFIG.get_config_value('CROS', 'log_url_pattern', type=str)
+_ENABLE_RUN_SUITE_TRAMPOLINE = CONFIG.get_config_value(
+        'CROS', 'enable_run_suite_trampoline', type=bool, default=False)
+
+_MIGRATION_CONFIG_FILE = 'migration_config.ini'
+_MIGRATION_CONFIG_BUCKET = 'suite-scheduler.google.com.a.appspot.com'
+_TRAMPOLINE_CONFIG = 'gs://%s/%s' % (_MIGRATION_CONFIG_BUCKET,
+                                     _MIGRATION_CONFIG_FILE)
 
 # Minimum RPC timeout setting for calls expected to take long time, e.g.,
 # create_suite_job. If default socket time (socket.getdefaulttimeout()) is
@@ -2037,6 +2054,40 @@
         sys.exit(run_suite_common.RETURN_CODES.INFRA_FAILURE)
 
 
+def _check_if_use_skylab(options):
+    """Detect whether to run suite in skylab."""
+    if not _ENABLE_RUN_SUITE_TRAMPOLINE:
+        logging.info('trampoline to skylab is not enabled.')
+        return False
+
+    task_info = 'suite:%s, board:%s, model:%s, pool:%s' % (
+            options.name, options.board, options.model, options.pool)
+    ctx = gs.GSContext()
+    with osutils.TempDir(prefix='trampoline_') as tempdir:
+        temp_file = os.path.join(tempdir, _MIGRATION_CONFIG_FILE)
+        ctx.Copy(_TRAMPOLINE_CONFIG, temp_file)
+        _migration_config = config_reader.MigrationConfig(
+                config_reader.ConfigReader(temp_file))
+
+        logging.info('Checking whether to run in skylab: Task(%s)', task_info)
+        if skylab.should_run_in_skylab(_migration_config,
+                                       options.board,
+                                       options.model,
+                                       options.name,
+                                       options.pool):
+            logging.info('Task (%s) Should run in skylab', task_info)
+            return True
+
+    logging.info('Task (%s) Should run in autotest', task_info)
+    return False
+
+
+def _run_with_skylab(options):
+    """Run suite inside skylab."""
+    # TODO(xixuan): Implement running suite in skylab.
+    return _RETURN_RESULTS['ok']
+
+
 def _run_with_autotest(options):
     """Run suite inside autotest."""
     if options.pre_check and not _should_run(options):
@@ -2079,7 +2130,10 @@
         result = run_suite_common.SuiteResult(
                 run_suite_common.RETURN_CODES.INVALID_OPTIONS)
     else:
-        result = _run_with_autotest(options)
+        if _check_if_use_skylab(options):
+            result = _run_with_skylab(options)
+        else:
+            result = _run_with_autotest(options)
 
     logging.info('Will return from run_suite with status: %s',
                   run_suite_common.RETURN_CODES.get_string(result.return_code))
diff --git a/venv/skylab_suite/suite_runner.py b/venv/skylab_suite/suite_runner.py
index 3cea765..808ccee 100644
--- a/venv/skylab_suite/suite_runner.py
+++ b/venv/skylab_suite/suite_runner.py
@@ -222,7 +222,7 @@
 # SWARMING_SERVER environment variable. See crbug.com/948774
 def _is_dev():
     """Detect whether skylab tool should be invoked with -dev flag."""
-     return 'chromium-swarm-dev' in os.environ['SWARMING_SERVER']
+    return 'chromium-swarm-dev' in os.environ['SWARMING_SERVER']
 
 def _compute_tags(build, suite_id):
     tags = [