=== modified file 'lava_dispatcher/__init__.py'
--- lava_dispatcher/__init__.py	2011-10-20 20:40:41 +0000
+++ lava_dispatcher/__init__.py	2011-10-26 02:46:04 +0000
@@ -117,10 +117,6 @@
         self.config = dispatcher_config
         self.job_data = job_data
         device_config = get_device_config(target)
-        if device_config.get('client_type') != 'conmux':
-            raise RuntimeError(
-                "this version of lava-dispatcher only supports conmux "
-                "clients, not %r" % device_config.get('client_type'))
         if image_type == "android":
             self._client = LavaAndroidClient(self, device_config)
         else:

=== modified file 'lava_dispatcher/android_client.py'
--- lava_dispatcher/android_client.py	2011-10-25 02:01:41 +0000
+++ lava_dispatcher/android_client.py	2011-10-26 02:59:28 +0000
@@ -20,13 +20,11 @@
 import logging
 import os
 import pexpect
-import re
 import sys
+from tempfile import mkdtemp
 import time
 
-from tempfile import mkdtemp
-
-from lava_dispatcher.client import LavaClient, OperationFailed, NetworkError, GeneralError
+from lava_dispatcher.client import LavaClient, NetworkError, GeneralError
 from lava_dispatcher.utils import string_to_list
 
 
@@ -52,30 +50,9 @@
             pass
         return False
 
-    def in_test_shell(self):
-        """ Check that we are in a shell on the test image
-        """
-        self.proc.sendline("")
-        match_id = self.proc.expect([self.tester_str , pexpect.TIMEOUT])
-        if match_id == 1:
-            raise OperationFailed
-
     def boot_linaro_android_image(self):
-        """ Reboot the system to the test android image
-        """
-        self.soft_reboot()
-        try:
-            self.enter_uboot()
-        except:
-            logging.exception('enter_uboot failed')
-            self.hard_reboot()
-            self.enter_uboot()
-        bootloader_prompt = re.escape(self.device_option('bootloader_prompt'))
-        boot_cmds = string_to_list(self.config.get('boot_cmds_android'))
-        self.proc.sendline(boot_cmds[0])
-        for line in range(1, len(boot_cmds)):
-            self.proc.expect(bootloader_prompt)
-            self.proc.sendline(boot_cmds[line])
+        """Reboot the system to the test android image."""
+        self._boot(string_to_list(self.config.get('boot_cmds_android')))
         self.in_test_shell()
         self.proc.sendline("export PS1=\"root@linaro: \"")
 
@@ -121,7 +98,7 @@
 
     def android_adb_disconnect(self, dev_ip):
         cmd = "adb disconnect %s" % dev_ip
-        adb_proc = pexpect.run(cmd, timeout=300, logfile=sys.stdout)
+        pexpect.run(cmd, timeout=300, logfile=sys.stdout)
 
     def check_adb_status(self):
         device_ip = self.get_default_nic_ip()

=== modified file 'lava_dispatcher/client.py'
--- lava_dispatcher/client.py	2011-10-25 02:02:17 +0000
+++ lava_dispatcher/client.py	2011-10-26 03:24:05 +0000
@@ -19,7 +19,6 @@
 # with this program; if not, see <http://www.gnu.org/licenses>.
 
 import pexpect
-import re
 import sys
 import time
 from cStringIO import StringIO
@@ -27,6 +26,11 @@
 from utils import string_to_list
 import logging
 
+from lava_dispatcher.connection import (
+    LavaConmuxConnection,
+    )
+
+
 class LavaClient(object):
     """
     LavaClient manipulates the target board, bootup, reset, power off the board,
@@ -35,11 +39,13 @@
     def __init__(self, context, config):
         self.context = context
         self.config = config
-        cmd = "conmux-console %s" % self.hostname
         self.sio = SerialIO(sys.stdout)
-        self.proc = pexpect.spawn(cmd, timeout=3600, logfile=self.sio)
-        #serial can be slow, races do funny things if you don't increase delay
-        self.proc.delaybeforesend=1
+        if config.get('client_type') == 'conmux':
+            self.proc = LavaConmuxConnection(config, self.sio)
+        else:
+            raise RuntimeError(
+                "this version of lava-dispatcher only supports conmux "
+                "clients, not %r" % config.get('client_type'))
 
     def device_option(self, option_name):
         return self.config.get(option_name)
@@ -99,21 +105,21 @@
         Check that we are in a shell on the test image
         """
         self.proc.sendline("")
-        id = self.proc.expect([self.tester_str, pexpect.TIMEOUT])
-        if id == 1:
+        match_id = self.proc.expect([self.tester_str, pexpect.TIMEOUT])
+        if match_id == 1:
             raise OperationFailed
 
     def boot_master_image(self):
         """
         reboot the system, and check that we are in a master shell
         """
-        self.soft_reboot()
+        self.proc.soft_reboot()
         try:
             self.proc.expect("Starting kernel")
             self.in_master_shell(120)
         except:
             logging.exception("in_master_shell failed")
-            self.hard_reboot()
+            self.proc.hard_reboot()
             self.in_master_shell(300)
         self.proc.sendline('export PS1="$PS1 [rc=$(echo \$?)]: "')
         self.proc.expect(self.master_str)
@@ -122,19 +128,7 @@
         """
         Reboot the system to the test image
         """
-        self.soft_reboot()
-        try:
-            self.enter_uboot()
-        except:
-            logging.exception("enter_uboot failed")
-            self.hard_reboot()
-            self.enter_uboot()
-        boot_cmds = self.boot_cmds
-        self.proc.sendline(boot_cmds[0])
-        bootloader_prompt = re.escape(self.device_option('bootloader_prompt'))
-        for line in range(1, len(boot_cmds)):
-            self.proc.expect(bootloader_prompt, timeout=300)
-            self.proc.sendline(boot_cmds[line])
+        self.proc._boot(self.boot_cmds)
         self.in_test_shell()
         # set PS1 to include return value of last command
         # Details: system PS1 is set in /etc/bash.bashrc and user PS1 is set in
@@ -143,28 +137,6 @@
         self.proc.sendline('export PS1="$PS1 [rc=$(echo \$?)]: "')
         self.proc.expect(self.tester_str)
 
-    def enter_uboot(self):
-        self.proc.expect("Hit any key to stop autoboot")
-        self.proc.sendline("")
-
-    def soft_reboot(self):
-        self.proc.sendline("reboot")
-        # set soft reboot timeout 120s, or do a hard reset
-        id = self.proc.expect(['Will now restart', pexpect.TIMEOUT],
-            timeout=120)
-        if id != 0:
-            self.hard_reboot()
-
-    def hard_reboot(self):
-        self.proc.send("~$")
-        self.proc.sendline("hardreset")
-        # XXX Workaround for snowball
-        if self.device_type == "snowball_sd":
-            time.sleep(10)
-            self.in_master_shell(300)
-            # Intentionally avoid self.soft_reboot() to prevent looping
-            self.proc.sendline("reboot")
-
     def run_shell_command(self, cmd, response=None, timeout=-1):
         self.empty_pexpect_buffer()
         # return return-code if captured, else return None

=== added file 'lava_dispatcher/connection.py'
--- lava_dispatcher/connection.py	1970-01-01 00:00:00 +0000
+++ lava_dispatcher/connection.py	2011-10-26 03:24:05 +0000
@@ -0,0 +1,110 @@
+# Copyright (C) 2011 Linaro Limited
+#
+# Author: Michael Hudson-Doyle <michael.hudson@linaro.org>
+#
+# 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 <http://www.gnu.org/licenses>.
+
+import logging
+import re
+import time
+
+import pexpect
+
+
+class LavaConnection(object):
+
+    def __init__(self, device_config, sio):
+        self.device_config = device_config
+        self.proc = self._make_connection(sio)
+
+    def _make_connection(self, sio):
+        raise NotImplementedError(self._make_connection)
+
+    def device_option(self, option_name):
+        return self.device_config.get(option_name)
+
+    def device_option_int(self, option_name):
+        return self.device_config.getint(option_name)
+
+
+    # pexpect-like interface.
+
+    def sendline(self, *args, **kw):
+        return self.proc.sendline(*args, **kw)
+
+    def expect(self, *args, **kw):
+        return self.proc.expect(*args, **kw)
+
+    def sendcontrol(self, *args, **kw):
+        return self.proc.sendcontrol(*args, **kw)
+
+    @property
+    def match(self):
+        return self.proc.match
+
+
+    # Extra bits.
+
+    def _enter_uboot(self):
+        self.proc.expect("Hit any key to stop autoboot")
+        self.proc.sendline("")
+
+    def soft_reboot(self):
+        self.proc.sendline("reboot")
+        # set soft reboot timeout 120s, or do a hard reset
+        id = self.proc.expect(
+            ['Will now restart', pexpect.TIMEOUT], timeout=120)
+        if id != 0:
+            self.hard_reboot()
+
+    def hard_reboot(self):
+        raise NotImplementedError(self.hard_reboot)
+
+
+class LavaConmuxConnection(LavaConnection):
+
+    def _make_connection(self, sio):
+        cmd = "conmux-console %s" % self.device_option("hostname")
+        proc = pexpect.spawn(cmd, timeout=3600, logfile=sio)
+        #serial can be slow, races do funny things if you don't increase delay
+        proc.delaybeforesend=1
+        return proc
+
+    def hard_reboot(self):
+        self.proc.send("~$")
+        self.proc.sendline("hardreset")
+        # XXX Workaround for snowball
+        if self.device_option('device_type') == "snowball_sd":
+            time.sleep(10)
+            self.in_master_shell(300)
+            # Intentionally avoid self.soft_reboot() to prevent looping
+            self.proc.sendline("reboot")
+            self.enter_uboot()
+
+    def _boot(self, boot_cmds):
+        self.soft_reboot()
+        try:
+            self._enter_uboot()
+        except:
+            logging.exception("_enter_uboot failed")
+            self.hard_reboot()
+            self._enter_uboot()
+        self.proc.sendline(boot_cmds[0])
+        bootloader_prompt = re.escape(self.device_option('bootloader_prompt'))
+        for line in range(1, len(boot_cmds)):
+            self.proc.expect(bootloader_prompt, timeout=300)
+            self.proc.sendline(boot_cmds[line])

