blob: 05ee996db6418d560fcd3bfba4e5ca9b1e0bf59d [file] [log] [blame]
# 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 os
import remote_cmd
import subprocess
import sys
import tempfile
import time
_SHUTDOWN_CMD = ['dm', 'poweroff']
_ATTACH_MAX_RETRIES = 10
_ATTACH_RETRY_INTERVAL = 1
class Target(object):
"""Abstract base class representing a Fuchsia deployment target."""
def __init__(self, output_dir, target_cpu, verbose):
self._target_cpu = target_cpu
self._output_dir = output_dir
self._started = False
self._dry_run = False
self._vlogger = sys.stdout if verbose else open(os.devnull, 'w')
def Start(self):
"""Handles the instantiation and connection process for the Fuchsia
target instance."""
pass
def IsStarted(self):
"""Returns true if the Fuchsia target instance is ready to accept
commands."""
return self._started
def RunCommandPiped(self, command):
"""Starts a remote command and immediately returns a Popen object for the
command. The caller may interact with the streams, inspect the status code,
wait on command termination, etc.
command: A list of strings representing the command and arguments.
Returns: a Popen object.
Note: method does not block."""
self._AssertStarted()
host, port = self._GetEndpoint()
return remote_cmd.RunPipedSsh(self._GetSshConfigPath(), host, port, command)
def RunCommand(self, command, silent=False):
"""Executes a remote command and waits for it to finish executing.
Returns the exit code of the command."""
self._AssertStarted()
host, port = self._GetEndpoint()
return remote_cmd.RunSsh(self._GetSshConfigPath(), host, port, command,
silent)
def CopyTo(self, source, dest):
"""Copies a file from the local filesystem to the target filesystem.
source: The path of the file being copied.
dest: The path on the remote filesystem which will be copied to."""
self._AssertStarted()
host, port = self._GetEndpoint()
command = remote_cmd.RunScp(self._GetSshConfigPath(), host, port,
source, dest, remote_cmd.COPY_TO_TARGET)
def CopyFrom(self, source, dest):
"""Copies a file from the target filesystem to the local filesystem.
source: The path of the file being copied.
dest: The path on the local filesystem which will be copied to."""
self._AssertStarted()
host, port = self._GetEndpoint()
return remote_cmd.RunScp(self._GetSshConfigPath(), host, port,
source, dest, remote_cmd.COPY_FROM_TARGET)
def Shutdown(self):
self.RunCommand(_SHUTDOWN_CMD)
self._started = False
def _GetEndpoint(self):
"""Returns a (host, port) tuple for the SSH connection to the target."""
raise NotImplementedError
def _GetTargetSdkArch(self):
"""Returns the Fuchsia SDK architecture name for the target CPU."""
if self._target_cpu == 'arm64':
return 'aarch64'
elif self._target_cpu == 'x64':
return 'x86_64'
raise Exception('Unknown target_cpu:' + self._target_cpu)
def _AssertStarted(self):
assert self.IsStarted()
def _Attach(self):
self._vlogger.write('Trying to connect over SSH...')
self._vlogger.flush()
for _ in xrange(_ATTACH_MAX_RETRIES):
host, port = self._GetEndpoint()
if remote_cmd.RunSsh(self._ssh_config_path, host, port, ['echo'],
True) == 0:
self._vlogger.write(' connected!\n')
self._vlogger.flush()
self._started = True
return
self._vlogger.write('.')
self._vlogger.flush()
time.sleep(_ATTACH_RETRY_INTERVAL)
sys.stderr.write(' timeout limit reached.\n')
raise Exception('Couldn\'t connect to QEMU using SSH.')
def _GetSshConfigPath(self, path):
raise NotImplementedError