From patchwork Wed Jan 23 17:55:16 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antonio Terceiro X-Patchwork-Id: 14249 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id A6DFC23E1A for ; Wed, 23 Jan 2013 17:56:10 +0000 (UTC) Received: from mail-vc0-f181.google.com (mail-vc0-f181.google.com [209.85.220.181]) by fiordland.canonical.com (Postfix) with ESMTP id 4206CA18113 for ; Wed, 23 Jan 2013 17:56:10 +0000 (UTC) Received: by mail-vc0-f181.google.com with SMTP id d16so5477083vcd.26 for ; Wed, 23 Jan 2013 09:56:09 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:x-forwarded-to:x-forwarded-for:delivered-to:x-received :received-spf:content-type:mime-version:x-launchpad-project :x-launchpad-branch:x-launchpad-message-rationale :x-launchpad-branch-revision-number:x-launchpad-notification-type:to :from:subject:message-id:date:reply-to:sender:errors-to:precedence :x-generated-by:x-launchpad-hash:x-gm-message-state; bh=+fGKm6GC1EF3ZXF3qLfjaejvtMjvFEHQUayh5ED8fio=; b=S3bRORNHmjKbrbNgB8P+3jo3jvnDWaNJ7vJEzz36k4u39YMsfx57DAHwPV5ISvoXLt T/6N4+xbMr8l4sWrVsDqXri57zzIu+T7Q9lCWvv6dvvW2vUpo5ianoGFeRu9ozzKXv1X vilMbZ3j/MNZrkSBLQVCG5+eVwbY9G73epQBAM0MSdHZud2wxZQKPGTVZ4mZx9ci1k+y 45rV2joMxw1SNUS7sDahVJe2AaVG9Ds/KJ2Wz9Mbzt8HUb5t2HL4Uu6SO57tnjORVXjj oT7u9ZvzxwZ3ETYE9xDSGKJ+TMwhLrs6zPDBeE+Oiy1EzqobJ0klKsSQUQt7tPtbG6Hj bAag== X-Received: by 10.220.218.197 with SMTP id hr5mr2357957vcb.8.1358963769707; Wed, 23 Jan 2013 09:56:09 -0800 (PST) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.58.145.101 with SMTP id st5csp23895veb; Wed, 23 Jan 2013 09:55:55 -0800 (PST) X-Received: by 10.194.235.6 with SMTP id ui6mr4299500wjc.12.1358963716996; Wed, 23 Jan 2013 09:55:16 -0800 (PST) Received: from indium.canonical.com (indium.canonical.com. [91.189.90.7]) by mx.google.com with ESMTPS id d8si6744340wie.19.2013.01.23.09.55.16 (version=TLSv1 cipher=RC4-SHA bits=128/128); Wed, 23 Jan 2013 09:55:16 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) client-ip=91.189.90.7; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) smtp.mail=bounces@canonical.com Received: from ackee.canonical.com ([91.189.89.26]) by indium.canonical.com with esmtp (Exim 4.71 #1 (Debian)) id 1Ty4Xc-0001Ef-3Z for ; Wed, 23 Jan 2013 17:55:16 +0000 Received: from ackee.canonical.com (localhost [127.0.0.1]) by ackee.canonical.com (Postfix) with ESMTP id 0C379E0241 for ; Wed, 23 Jan 2013 17:55:16 +0000 (UTC) MIME-Version: 1.0 X-Launchpad-Project: lava-dispatcher X-Launchpad-Branch: ~linaro-validation/lava-dispatcher/trunk X-Launchpad-Message-Rationale: Subscriber X-Launchpad-Branch-Revision-Number: 543 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [Branch ~linaro-validation/lava-dispatcher/trunk] Rev 543: Galaxy Nexus support. Message-Id: <20130123175516.24917.68358.launchpad@ackee.canonical.com> Date: Wed, 23 Jan 2013 17:55:16 -0000 Reply-To: noreply@launchpad.net Sender: bounces@canonical.com Errors-To: bounces@canonical.com Precedence: bulk X-Generated-By: Launchpad (canonical.com); Revision="16445"; Instance="launchpad-lazr.conf" X-Launchpad-Hash: 3e687a73c6950cbd99a71932cca49eff2b361457 X-Gm-Message-State: ALoCoQmAm4LaZgIEna1awguz/wuT1MpYRnYBYf53KNi+ID+KrC2JixhbhN+cHTiKzKSR9oNgNPBl Merge authors: Antonio Terceiro (terceiro) Related merge proposals: https://code.launchpad.net/~terceiro/lava-dispatcher/nexus/+merge/144204 proposed by: Antonio Terceiro (terceiro) review: Approve - Michael Hudson-Doyle (mwhudson) ------------------------------------------------------------ revno: 543 [merge] committer: Antonio Terceiro branch nick: trunk timestamp: Wed 2013-01-23 14:53:29 -0300 message: Galaxy Nexus support. This merge introduces support for the Galaxy Nexus, using fastboot for image flashing and adb for interaction with Android. added: lava_dispatcher/default-config/lava-dispatcher/device-types/nexus.conf lava_dispatcher/device/nexus.py modified: lava_dispatcher/config.py --- lp:lava-dispatcher https://code.launchpad.net/~linaro-validation/lava-dispatcher/trunk You are subscribed to branch lp:lava-dispatcher. To unsubscribe from this branch go to https://code.launchpad.net/~linaro-validation/lava-dispatcher/trunk/+edit-subscription === modified file 'lava_dispatcher/config.py' --- lava_dispatcher/config.py 2013-01-18 11:08:24 +0000 +++ lava_dispatcher/config.py 2013-01-21 13:11:47 +0000 @@ -92,6 +92,9 @@ arm_probe_config = schema.StringOption(default='/usr/local/etc/arm-probe-config') arm_probe_channels = schema.ListOption(default=['VDD_VCORE1']) + adb_command = schema.StringOption() + fastboot_command = schema.StringOption() + nexus_working_directory = schema.StringOption(default=None) class OptionDescriptor(object): def __init__(self, name): === added file 'lava_dispatcher/default-config/lava-dispatcher/device-types/nexus.conf' --- lava_dispatcher/default-config/lava-dispatcher/device-types/nexus.conf 1970-01-01 00:00:00 +0000 +++ lava_dispatcher/default-config/lava-dispatcher/device-types/nexus.conf 2013-01-22 01:58:09 +0000 @@ -0,0 +1,46 @@ +client_type = nexus + +# The ADB command line. +# +# In the case where there are multiple android devices plugged into a +# single host, this connection command must be overriden on each device to +# include the serial number of the device, e.g. +# +# serial_number = XXXXXXXXXXXXXXXX +# adb_command = adb -s %(serial_number)s +adb_command = adb + +# The fastboot command. +# +# The same as above: if you have more than one device, you will want to +# override this in your device config to add a serial number, e.g. +# +# serial_number = XXXXXXXXXXXXXXXX +# fastboot_command = fastboot -s %(serial_number)s +# +# Of course, in the case you override both adb_command *and* fastboot_command, +# you don't need to specify `serial_number` twice. +fastboot_command = fastboot + +# Working directory for temporary files. By default, the usual place for LAVA +# images will be used. +# +# This is useful when the lava dispatcher is controlling Nexus phones that are +# physically plugged to other machines by setting adb_command to something like +# "ssh adb" and fastboot_command to something like "ssh +# fastboot". adb and fastboot always operate on local files, so +# you need your local files to also be seen as local files on the host where +# adb/fastboot are executed. +# +# In this case, you should set nexus_work_directory to a shared directory +# between the machine running the dispatcher and the machine where the phone is +# plugged. This shared directory must have the same path in both machines. +# For example, you can have your /var/tmp/lava mounted at /var/tmp/lava at +# (or the other way around). +nexus_working_directory = + +connection_command = %(adb_command)s shell + +enable_network_after_boot_android = false +android_adb_over_usb = true +android_adb_over_tcp = false === added file 'lava_dispatcher/device/nexus.py' --- lava_dispatcher/device/nexus.py 1970-01-01 00:00:00 +0000 +++ lava_dispatcher/device/nexus.py 2013-01-22 01:45:32 +0000 @@ -0,0 +1,193 @@ +# Copyright (C) 2012 Linaro Limited +# +# Author: Antonio Terceiro +# +# This file is part of LAVA Dispatcher. +# +# LAVA Dispatcher is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# LAVA Dispatcher is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along +# with this program; if not, see . + +import subprocess +import pexpect +from time import sleep +import logging +import contextlib + +from lava_dispatcher.device.target import ( + Target +) +from lava_dispatcher.downloader import ( + download_image +) +from lava_dispatcher.utils import ( + logging_system, + logging_spawn, + mkdtemp +) +from lava_dispatcher.errors import ( + CriticalError +) + +class NexusTarget(Target): + + def __init__(self, context, config): + super(NexusTarget, self).__init__(context, config) + + if not config.hard_reset_command: + logging.warn( + "Setting the hard_reset_command config option " + "is highly recommended!" + ) + + self._booted = False + self._working_dir = None + + def deploy_android(self, boot, system, userdata): + + boot = self._get_image(boot) + system = self._get_image(system) + userdata = self._get_image(userdata) + + self._enter_fastboot() + self._fastboot('erase boot') + self._fastboot('flash system %s' % system) + self._fastboot('flash userdata %s' % userdata) + + self.deployment_data = Target.android_deployment_data + self.deployment_data['boot_image'] = boot + + def power_on(self): + if not self.deployment_data.get('boot_image', False): + raise CriticalError('Deploy action must be run first') + + self._enter_fastboot() + self._boot_test_image() + + self._booted = True + proc = self._adb('shell', spawn = True) + proc.sendline("") # required to put the adb shell in a reasonable state + proc.sendline("export PS1='%s'" % self.deployment_data['TESTER_PS1']) + self._runner = self._get_runner(proc) + + return proc + + def power_off(self, proc): + # We always leave the device on + pass + + @contextlib.contextmanager + def file_system(self, partition, directory): + + if not self._booted: + self.power_on() + + mount_point = self._get_partition_mount_point(partition) + + host_dir = '%s/mnt/%s' % (self.working_dir, directory) + target_dir = '%s/%s' % (mount_point, directory) + + subprocess.check_call(['mkdir', '-p', host_dir]) + self._adb('pull %s %s' % (target_dir, host_dir), ignore_failure = True) + + yield host_dir + + self._adb('push %s %s' % (host_dir, target_dir)) + + def get_device_version(self): + # this is tricky, because fastboot does not have a visible version + # number. For now let's use just the adb version number. + return subprocess.check_output( + "%s version | sed 's/.* version //'" % self.config.adb_command, + shell = True + ).strip() + + # start of private methods + + def _enter_fastboot(self): + if self._already_on_fastboot(): + logging.debug("Device is on fastboot - no need to hard reset") + return + try: + # First we try a gentle reset + self._adb('reboot') + except subprocess.CalledProcessError: + # Now a more brute force attempt. In this case the device is + # probably hung. + if self.config.hard_reset_command: + logging.debug("Will hard reset the device") + logging_system(self.config.hard_reset_command) + else: + logging.critical( + "Hard reset command not configured. " + "Please reset the device manually." + ) + + def _already_on_fastboot(self): + try: + self._fastboot('getvar all', timeout = 2) + return True + except subprocess.CalledProcessError: + return False + + def _boot_test_image(self): + # We need an extra bootloader reboot before actually booting the image + # to avoid the phone entering charging mode and getting stuck. + self._fastboot('reboot') + # specifically after `fastboot reset`, we have to wait a little + sleep(10) + self._fastboot('boot %s' % self.deployment_data['boot_image']) + self._adb('wait-for-device') + + def _get_partition_mount_point(self, partition): + lookup = { + self.config.data_part_android_org: '/data', + self.config.sys_part_android_org: '/system', + } + return lookup[partition] + + def _adb(self, args, ignore_failure = False, spawn = False, timeout = 600): + cmd = self.config.adb_command + ' ' + args + if spawn: + return logging_spawn(cmd, timeout = 60) + else: + self._call(cmd, ignore_failure, timeout) + + def _fastboot(self, args, ignore_failure = False, timeout = 600): + self._call(self.config.fastboot_command + ' ' + args, ignore_failure, timeout) + + def _call(self, cmd, ignore_failure, timeout): + cmd = 'timeout ' + str(timeout) + 's ' + cmd + logging.debug("Running on the host: %s" % cmd) + if ignore_failure: + subprocess.call(cmd, shell = True) + else: + subprocess.check_call(cmd, shell = True) + + def _get_image(self, url): + sdir = self.working_dir + image = download_image(url, self.context, sdir, decompress=False) + return image + + @property + def working_dir(self): + if (self.config.nexus_working_directory is None or + self.config.nexus_working_directory.strip() == ''): + return self.scratch_dir + + if self._working_dir is None: + self._working_dir = mkdtemp(self.config.nexus_working_directory) + return self._working_dir + + +target_class = NexusTarget