blob: 34bc99f5e1d0e86bdebed16ab12b3deefae4237c [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.
"""Implements commands for running and interacting with Fuchsia on devices."""
import boot_data
import logging
import os
import subprocess
import target
import time
import uuid
from common import SDK_ROOT, EnsurePathExists
CONNECT_RETRY_COUNT = 20
CONNECT_RETRY_WAIT_SECS = 1
class DeviceTarget(target.Target):
def __init__(self, output_dir, target_cpu, host=None, port=None,
ssh_config=None):
"""output_dir: The directory which will contain the files that are
generated to support the deployment.
target_cpu: The CPU architecture of the deployment target. Can be
"x64" or "arm64".
host: The address of the deployment target device.
port: The port of the SSH service on the deployment target device.
ssh_config: The path to SSH configuration data."""
super(DeviceTarget, self).__init__(output_dir, target_cpu)
self._port = 22
self._auto = not host or not ssh_config
self._new_instance = True
if self._auto:
self._ssh_config_path = EnsurePathExists(
boot_data.GetSSHConfigPath(output_dir))
else:
self._ssh_config_path = os.path.expanduser(ssh_config)
self._host = host
if port:
self._port = port
self._new_instance = False
def __Discover(self, node_name):
"""Returns the IP address and port of a Fuchsia instance discovered on
the local area network."""
netaddr_path = os.path.join(SDK_ROOT, 'tools', 'netaddr')
command = [netaddr_path, '--fuchsia', '--nowait', node_name]
logging.debug(' '.join(command))
proc = subprocess.Popen(command,
stdout=subprocess.PIPE,
stderr=open(os.devnull, 'w'))
proc.wait()
if proc.returncode == 0:
return proc.stdout.readlines()[0].strip()
return None
def Start(self):
if self._auto:
logging.debug('Starting automatic device deployment.')
node_name = boot_data.GetNodeName(self._output_dir)
self._host = self.__Discover(node_name)
if self._host and self._WaitUntilReady(retries=0):
logging.info('Connected to an already booted device.')
self._new_instance = False
return
logging.info('Netbooting Fuchsia. ' +
'Please ensure that your device is in bootloader mode.')
bootserver_path = os.path.join(SDK_ROOT, 'tools', 'bootserver')
bootserver_command = [
bootserver_path,
'-1',
'--efi',
EnsurePathExists(boot_data.GetTargetFile(self._GetTargetSdkArch(),
'local.esp.blk')),
'--fvm',
EnsurePathExists(boot_data.GetTargetFile(self._GetTargetSdkArch(),
'fvm.sparse.blk')),
'--fvm',
EnsurePathExists(
boot_data.ConfigureDataFVM(self._output_dir,
boot_data.FVM_TYPE_SPARSE)),
EnsurePathExists(boot_data.GetTargetFile(self._GetTargetSdkArch(),
'zircon.bin')),
EnsurePathExists(boot_data.GetTargetFile(self._GetTargetSdkArch(),
'bootdata-blob.bin')),
'--'] + boot_data.GetKernelArgs(self._output_dir)
logging.debug(' '.join(bootserver_command))
subprocess.check_call(bootserver_command)
logging.debug('Waiting for device to join network.')
for _ in xrange(CONNECT_RETRY_COUNT):
self._host = self.__Discover(node_name)
if self._host:
break
time.sleep(CONNECT_RETRY_WAIT_SECS)
if not self._host:
raise Exception('Couldn\'t connect to device.')
logging.debug('host=%s, port=%d' % (self._host, self._port))
self._WaitUntilReady();
def IsNewInstance(self):
return self._new_instance
def _GetEndpoint(self):
return (self._host, self._port)
def _GetSshConfigPath(self):
return self._ssh_config_path