• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# Copyright 2016 Google Inc.
4#
5# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7
8
9import default_flavor
10import os
11import posixpath
12import subprocess
13import ssh_devices
14
15
16"""Utils for running tests remotely over SSH."""
17
18
19class SSHFlavorUtils(default_flavor.DefaultFlavorUtils):
20  def __init__(self, *args, **kwargs):
21    super(SSHFlavorUtils, self).__init__(*args, **kwargs)
22    slave_info = ssh_devices.SLAVE_INFO.get(self._bot_info.slave_name,
23                                            ssh_devices.SLAVE_INFO['default'])
24    self._host = slave_info.ssh_host
25    self._port = slave_info.ssh_port
26    self._user = slave_info.ssh_user
27
28  @property
29  def host(self):
30    return self._host
31
32  @property
33  def port(self):
34    return self._port
35
36  @property
37  def user(self):
38    return self._user
39
40  def ssh(self, cmd, **kwargs):
41    """Run the given SSH command."""
42    ssh_cmd = ['ssh']
43    if self.port:
44      ssh_cmd.extend(['-p', self.port])
45    dest = self.host
46    if self.user:
47      dest = self.user + '@' + dest
48    ssh_cmd.append(dest)
49    ssh_cmd.extend(cmd)
50    return self._bot_info.run(ssh_cmd, **kwargs)
51
52  def step(self, *args, **kwargs):
53    """Run the given step over SSH."""
54    self.ssh(*args, **kwargs)
55
56  def device_path_join(self, *args):
57    """Like os.path.join(), but for paths on a remote machine."""
58    return posixpath.join(*args)
59
60  def device_path_exists(self, path):  # pragma: no cover
61    """Like os.path.exists(), but for paths on a remote device."""
62    try:
63      self.ssh(['test', '-e', path])
64      return True
65    except subprocess.CalledProcessError:
66      return False
67
68  def _remove_device_dir(self, path):
69    """Remove the directory on the device."""
70    self.ssh(['rm', '-rf', path])
71
72  def _create_device_dir(self, path):
73    """Create the directory on the device."""
74    self.ssh(['mkdir', '-p', path])
75
76  def create_clean_device_dir(self, path):
77    """Like shutil.rmtree() + os.makedirs(), but on a remote device."""
78    self._remove_device_dir(path)
79    self._create_device_dir(path)
80
81  def _make_scp_cmd(self, remote_path, recurse=True):
82    """Prepare an SCP command.
83
84    Returns a partial SCP command and an adjusted remote path.
85    """
86    cmd = ['scp']
87    if recurse:
88      cmd.append('-r')
89    if self.port:
90      cmd.extend(['-P', self.port])
91    adj_remote_path = self.host + ':' + remote_path
92    if self.user:
93      adj_remote_path = self.user + '@' + adj_remote_path
94    return cmd, adj_remote_path
95
96  def copy_directory_contents_to_device(self, host_dir, device_dir):
97    """Like shutil.copytree(), but for copying to a remote device."""
98    _, remote_path = self._make_scp_cmd(device_dir)
99    cmd = [os.path.join(self._bot_info.skia_dir, 'tools',
100                        'scp_dir_contents.sh'),
101           host_dir, remote_path]
102    self._bot_info.run(cmd)
103
104  def copy_directory_contents_to_host(self, device_dir, host_dir):
105    """Like shutil.copytree(), but for copying from a remote device."""
106    _, remote_path = self._make_scp_cmd(device_dir)
107    cmd = [os.path.join(self._bot_info.skia_dir, 'tools',
108                        'scp_dir_contents.sh'),
109           remote_path, host_dir]
110    self._bot_info.run(cmd)
111
112  def copy_file_to_device(self, host_path, device_path):
113    """Like shutil.copyfile, but for copying to a connected device."""
114    cmd, remote_path = self._make_scp_cmd(device_path, recurse=False)
115    cmd.extend([host_path, remote_path])
116    self._bot_info.run(cmd)
117
118  def read_file_on_device(self, path):
119    return self.ssh(['cat', path]).rstrip()
120
121  def remove_file_on_device(self, path):
122    """Delete the given file."""
123    return self.ssh(['rm', '-f', path])
124