普通文本  |  124行  |  3.64 KB

#!/usr/bin/env python
#
# Copyright 2016 Google Inc.
#
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.


import default_flavor
import os
import posixpath
import subprocess
import ssh_devices


"""Utils for running tests remotely over SSH."""


class SSHFlavorUtils(default_flavor.DefaultFlavorUtils):
  def __init__(self, *args, **kwargs):
    super(SSHFlavorUtils, self).__init__(*args, **kwargs)
    slave_info = ssh_devices.SLAVE_INFO.get(self._bot_info.slave_name,
                                            ssh_devices.SLAVE_INFO['default'])
    self._host = slave_info.ssh_host
    self._port = slave_info.ssh_port
    self._user = slave_info.ssh_user

  @property
  def host(self):
    return self._host

  @property
  def port(self):
    return self._port

  @property
  def user(self):
    return self._user

  def ssh(self, cmd, **kwargs):
    """Run the given SSH command."""
    ssh_cmd = ['ssh']
    if self.port:
      ssh_cmd.extend(['-p', self.port])
    dest = self.host
    if self.user:
      dest = self.user + '@' + dest
    ssh_cmd.append(dest)
    ssh_cmd.extend(cmd)
    return self._bot_info.run(ssh_cmd, **kwargs)

  def step(self, *args, **kwargs):
    """Run the given step over SSH."""
    self.ssh(*args, **kwargs)

  def device_path_join(self, *args):
    """Like os.path.join(), but for paths on a remote machine."""
    return posixpath.join(*args)

  def device_path_exists(self, path):  # pragma: no cover
    """Like os.path.exists(), but for paths on a remote device."""
    try:
      self.ssh(['test', '-e', path])
      return True
    except subprocess.CalledProcessError:
      return False

  def _remove_device_dir(self, path):
    """Remove the directory on the device."""
    self.ssh(['rm', '-rf', path])

  def _create_device_dir(self, path):
    """Create the directory on the device."""
    self.ssh(['mkdir', '-p', path])

  def create_clean_device_dir(self, path):
    """Like shutil.rmtree() + os.makedirs(), but on a remote device."""
    self._remove_device_dir(path)
    self._create_device_dir(path)

  def _make_scp_cmd(self, remote_path, recurse=True):
    """Prepare an SCP command.

    Returns a partial SCP command and an adjusted remote path.
    """
    cmd = ['scp']
    if recurse:
      cmd.append('-r')
    if self.port:
      cmd.extend(['-P', self.port])
    adj_remote_path = self.host + ':' + remote_path
    if self.user:
      adj_remote_path = self.user + '@' + adj_remote_path
    return cmd, adj_remote_path

  def copy_directory_contents_to_device(self, host_dir, device_dir):
    """Like shutil.copytree(), but for copying to a remote device."""
    _, remote_path = self._make_scp_cmd(device_dir)
    cmd = [os.path.join(self._bot_info.skia_dir, 'tools',
                        'scp_dir_contents.sh'),
           host_dir, remote_path]
    self._bot_info.run(cmd)

  def copy_directory_contents_to_host(self, device_dir, host_dir):
    """Like shutil.copytree(), but for copying from a remote device."""
    _, remote_path = self._make_scp_cmd(device_dir)
    cmd = [os.path.join(self._bot_info.skia_dir, 'tools',
                        'scp_dir_contents.sh'),
           remote_path, host_dir]
    self._bot_info.run(cmd)

  def copy_file_to_device(self, host_path, device_path):
    """Like shutil.copyfile, but for copying to a connected device."""
    cmd, remote_path = self._make_scp_cmd(device_path, recurse=False)
    cmd.extend([host_path, remote_path])
    self._bot_info.run(cmd)

  def read_file_on_device(self, path):
    return self.ssh(['cat', path]).rstrip()

  def remove_file_on_device(self, path):
    """Delete the given file."""
    return self.ssh(['rm', '-f', path])