| # Copyright 2018 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Helper functions for remotely executing and copying files over a SSH |
| connection.""" |
| |
| import logging |
| import os |
| import subprocess |
| import sys |
| |
| _SSH = ['ssh'] |
| _SCP = ['scp', '-C'] # Use gzip compression. |
| _SSH_LOGGER = logging.getLogger('ssh') |
| |
| COPY_TO_TARGET = 0 |
| COPY_FROM_TARGET = 1 |
| |
| |
| def _IsLinkLocalIPv6(hostname): |
| return hostname.startswith('fe80::') |
| |
| |
| def RunSsh(config_path, host, port, command, silent): |
| """Executes an SSH command on the remote host and blocks until completion. |
| |
| config_path: Full path to SSH configuration. |
| host: The hostname or IP address of the remote host. |
| port: The port to connect to. |
| command: A list of strings containing the command and its arguments. |
| silent: If true, suppresses all output from 'ssh'. |
| |
| Returns the exit code from the remote command.""" |
| |
| ssh_command = _SSH + ['-F', config_path, |
| host, |
| '-p', str(port)] + command |
| _SSH_LOGGER.debug('ssh exec: ' + ' '.join(ssh_command)) |
| if silent: |
| devnull = open(os.devnull, 'w') |
| return subprocess.call(ssh_command, stderr=devnull, stdout=devnull) |
| else: |
| return subprocess.call(ssh_command) |
| |
| |
| def RunPipedSsh(config_path, host, port, command = None, ssh_args = None, |
| **kwargs): |
| """Executes an SSH command on the remote host and returns a process object |
| with access to the command's stdio streams. Does not block. |
| |
| config_path: Full path to SSH configuration. |
| host: The hostname or IP address of the remote host. |
| port: The port to connect to. |
| command: A list of strings containing the command and its arguments. |
| ssh_args: Arguments that will be passed to SSH. |
| kwargs: A dictionary of parameters to be passed to subprocess.Popen(). |
| The parameters can be used to override stdin and stdout, for example. |
| |
| Returns a Popen object for the command.""" |
| |
| if not command: |
| command = [] |
| if not ssh_args: |
| ssh_args = [] |
| |
| ssh_command = _SSH + ['-F', config_path, |
| host, |
| '-p', str(port)] + ssh_args + ['--'] + command |
| _SSH_LOGGER.debug(' '.join(ssh_command)) |
| return subprocess.Popen(ssh_command, **kwargs) |
| |
| |
| def RunScp(config_path, host, port, sources, dest, direction, recursive=False): |
| """Copies a file to or from a remote host using SCP and blocks until |
| completion. |
| |
| config_path: Full path to SSH configuration. |
| host: The hostname or IP address of the remote host. |
| port: The port to connect to. |
| sources: Paths of the files to be copied. |
| dest: The path that |source| will be copied to. |
| direction: Indicates whether the file should be copied to |
| or from the remote side. |
| Valid values are COPY_TO_TARGET or COPY_FROM_TARGET. |
| recursive: If true, performs a recursive copy. |
| |
| Function will raise an assertion if a failure occurred.""" |
| |
| scp_command = _SCP[:] |
| if ':' in host: |
| scp_command.append('-6') |
| host = '[' + host + ']' |
| if _SSH_LOGGER.getEffectiveLevel() == logging.DEBUG: |
| scp_command.append('-v') |
| if recursive: |
| scp_command.append('-r') |
| |
| if direction == COPY_TO_TARGET: |
| dest = "%s:%s" % (host, dest) |
| else: |
| sources = ["%s:%s" % (host, source) for source in sources] |
| |
| scp_command += ['-F', config_path, '-P', str(port)] |
| scp_command += sources |
| scp_command += [dest] |
| |
| _SSH_LOGGER.debug(' '.join(scp_command)) |
| subprocess.check_call(scp_command, stdout=open(os.devnull, 'w')) |