1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
# 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.
import logging
import os
import subprocess
import threading
from common import SubprocessCallWithTimeout
_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 _EscapeIfIPv6Address(address):
if ':' in address:
return '[' + address + ']'
else:
return address
class CommandRunner(object):
"""Helper class used to execute commands on a remote host over SSH."""
def __init__(self, config_path, host, port):
"""Creates a CommandRunner that connects to the specified |host| and |port|
using the ssh config at the specified |config_path|.
config_path: Full path to SSH configuration.
host: The hostname or IP address of the remote host.
port: The port to connect to."""
self._config_path = config_path
self._host = host
self._port = port
def _GetSshCommandLinePrefix(self):
cmd_prefix = _SSH + ['-F', self._config_path, self._host]
if self._port:
cmd_prefix += ['-p', str(self._port)]
return cmd_prefix
def RunCommand(self, command, silent, timeout_secs=None):
"""Executes an SSH command on the remote host and blocks until completion.
command: A list of strings containing the command and its arguments.
silent: If true, suppresses all output from 'ssh'.
timeout_secs: If set, limits the amount of time that |command| may run.
Commands which exceed the timeout are killed.
Returns the exit code from the remote command."""
ssh_command = self._GetSshCommandLinePrefix() + command
logging.warning(ssh_command)
_SSH_LOGGER.debug('ssh exec: ' + ' '.join(ssh_command))
retval, _, _ = SubprocessCallWithTimeout(ssh_command, silent, timeout_secs)
return retval
def RunCommandPiped(self, command, stdout, stderr, 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.
command: A list of strings containing the command and its arguments.
stdout: subprocess stdout. Must not be None.
stderr: subprocess stderr. Must not be None.
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 stdout or not stderr:
raise Exception('Stdout/stderr must be specified explicitly')
if not ssh_args:
ssh_args = []
ssh_command = self._GetSshCommandLinePrefix() + ssh_args + ['--'] + command
logging.warning(ssh_command)
_SSH_LOGGER.debug(' '.join(ssh_command))
return subprocess.Popen(ssh_command, stdout=stdout, stderr=stderr, **kwargs)
def RunScp(self, sources, dest, direction, recursive=False):
"""Copies a file to or from a remote host using SCP and blocks until
completion.
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 _SSH_LOGGER.getEffectiveLevel() == logging.DEBUG:
scp_command.append('-v')
if recursive:
scp_command.append('-r')
host = _EscapeIfIPv6Address(self._host)
if direction == COPY_TO_TARGET:
dest = "%s:%s" % (host, dest)
else:
sources = ["%s:%s" % (host, source) for source in sources]
scp_command += ['-F', self._config_path]
if self._port:
scp_command += ['-P', str(self._port)]
scp_command += sources
scp_command += [dest]
_SSH_LOGGER.debug(' '.join(scp_command))
try:
scp_output = subprocess.check_output(scp_command,
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as error:
_SSH_LOGGER.info(error.output)
raise
|