=== modified file 'lava_dispatcher/device/nexus.py'
--- lava_dispatcher/device/nexus.py	2013-01-22 01:45:32 +0000
+++ lava_dispatcher/device/nexus.py	2013-02-04 13:31:34 +0000
@@ -39,6 +39,67 @@
     CriticalError
 )
 
+
+def _call(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)
+
+
+class FastBoot(object):
+
+    def __init__(self, device):
+        self.device = device
+
+    def __call__(self, args, ignore_failure=False, timeout=600):
+        command = self.device.config.fastboot_command + ' ' + args
+        command = "flock /var/lock/lava-fastboot.lck " + command
+        _call(command, ignore_failure, timeout)
+
+    def enter(self):
+        if self.on():
+            logging.debug("Device is on fastboot - no need to hard reset")
+            return
+        try:
+            # First we try a gentle reset
+            self.device._adb('reboot')
+        except subprocess.CalledProcessError:
+            # Now a more brute force attempt. In this case the device is
+            # probably hung.
+            if self.device.config.hard_reset_command:
+                logging.debug("Will hard reset the device")
+                logging_system(self.device.config.hard_reset_command)
+            else:
+                logging.critical(
+                    "Hard reset command not configured. "
+                    "Please reset the device manually."
+                )
+
+    def on(self):
+        try:
+            self('getvar all', timeout=2)
+            return True
+        except subprocess.CalledProcessError:
+            return False
+
+    def erase(self, partition):
+        self('erase %s' % partition)
+
+    def flash(self, partition, image):
+        self('flash %s %s' % (partition, image))
+
+    def boot(self, image):
+        # We need an extra bootloader reboot before actually booting the image
+        # to avoid the phone entering charging mode and getting stuck.
+        self('reboot')
+        # specifically after `fastboot reset`, we have to wait a little
+        sleep(10)
+        self('boot %s' % image)
+
+
 class NexusTarget(Target):
 
     def __init__(self, context, config):
@@ -52,6 +113,7 @@
 
         self._booted = False
         self._working_dir = None
+        self.fastboot = FastBoot(self)
 
     def deploy_android(self, boot, system, userdata):
 
@@ -59,10 +121,10 @@
         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.fastboot.enter()
+        self.fastboot.erase('boot')
+        self.fastboot.flash('system', system)
+        self.fastboot.flash('userdata', userdata)
 
         self.deployment_data = Target.android_deployment_data
         self.deployment_data['boot_image'] = boot
@@ -71,11 +133,12 @@
         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.fastboot.enter()
+        self.fastboot.boot(self.deployment_data['boot_image'])
+        self._adb('wait-for-device')
 
         self._booted = True
-        proc = self._adb('shell', spawn = 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)
@@ -98,7 +161,7 @@
         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)
+        self._adb('pull %s %s' % (target_dir, host_dir), ignore_failure=True)
 
         yield host_dir
 
@@ -109,46 +172,11 @@
         # 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
+            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',
@@ -156,23 +184,12 @@
         }
         return lookup[partition]
 
-    def _adb(self, args, ignore_failure = False, spawn = False, timeout = 600):
+    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)
+            return logging_spawn(cmd, timeout=60)
+        else:
+            _call(cmd, ignore_failure, timeout)
 
     def _get_image(self, url):
         sdir = self.working_dir

