=== modified file 'lava_dispatcher/actions/android_deploy.py'
@@ -20,6 +20,7 @@
# along with this program; if not, see <http://www.gnu.org/licenses>.
from lava_dispatcher.actions import BaseAction
+from lava_dispatcher.client.master import LavaMasterImageClient
class cmd_deploy_linaro_android_image(BaseAction):
@@ -38,4 +39,6 @@
}
def run(self, boot, system, data, pkg=None, use_cache=True, rootfstype='ext4'):
+ if not isinstance(self.client, LavaMasterImageClient):
+ raise RuntimeError("Invalid LavaClient for this action")
self.client.deploy_linaro_android(boot, system, data, pkg, use_cache, rootfstype)
=== modified file 'lava_dispatcher/actions/deploy.py'
@@ -18,6 +18,8 @@
# along with this program; if not, see <http://www.gnu.org/licenses>.
from lava_dispatcher.actions import BaseAction
+from lava_dispatcher.client.master import LavaMasterImageClient
+from lava_dispatcher.client.qemu import LavaQEMUClient
class cmd_deploy_linaro_image(BaseAction):
@@ -77,6 +79,10 @@
def run(self, hwpack=None, rootfs=None, image=None, kernel_matrix=None,
use_cache=True, rootfstype='ext3'):
+ if not isinstance(self.client, LavaMasterImageClient) and \
+ not isinstance(self.client, LavaQEMUClient):
+ raise RuntimeError("Invalid LavaClient for this action")
+
self.client.deploy_linaro(
hwpack=hwpack, rootfs=rootfs, image=image,
kernel_matrix=kernel_matrix, use_cache=use_cache,
=== added file 'lava_dispatcher/actions/fastmodel_deploy.py'
@@ -0,0 +1,41 @@
+# Copyright (C) 2012 Linaro Limited
+#
+# Author: Andy Doan <andy.doan@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>.
+
+from lava_dispatcher.actions import BaseAction
+from lava_dispatcher.client.fastmodel import LavaFastModelClient
+
+
+class cmd_deploy_fastmodel_image(BaseAction):
+
+ parameters_schema = {
+ 'type': 'object',
+ 'properties': {
+ 'image': {'type': 'string', 'optional': False},
+ 'axf': {'type': 'string', 'optional': False},
+ 'image_type': {
+ 'type': 'string', 'optional': True, 'default': 'ubuntu',
+ 'enum': ['android', 'ubuntu']},
+ },
+ 'additionalProperties': False,
+ }
+
+ def run(self, image, axf, image_type='ubuntu'):
+ if not isinstance(self.client, LavaFastModelClient):
+ raise RuntimeError("Invalid LavaClient for this action")
+ self.client.deploy_image(image, axf, image_type=='android')
=== modified file 'lava_dispatcher/client/base.py'
@@ -384,9 +384,6 @@
if match_id == 1:
raise OperationFailed
- def deploy_linaro(self, hwpack, rootfs, kernel_matrix=None, use_cache=True, rootfstype='ext3'):
- raise NotImplementedError(self.deploy_linaro)
-
def setup_proxy(self, prompt_str):
lava_proxy = self.context.lava_proxy
if lava_proxy:
@@ -430,9 +427,6 @@
# Android stuff
- def deploy_linaro_android(self, boot, system, data, pkg=None, use_cache=True, rootfstype='ext4'):
- raise NotImplementedError(self.deploy_linaro_android)
-
def boot_linaro_android_image(self):
"""Reboot the system to the test android image."""
self._boot_linaro_android_image()
=== added file 'lava_dispatcher/client/fastmodel.py'
@@ -0,0 +1,157 @@
+# Copyright (C) 2012 Linaro Limited
+#
+# Author: Andy Doan <andy.doan@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 atexit
+import contextlib
+import logging
+import os
+import pexpect
+import threading
+
+from lava_dispatcher.client.base import (
+ CommandRunner,
+ LavaClient,
+ )
+from lava_dispatcher.client.lmc_utils import (
+ image_partition_mounted,
+ get_partition_offset,
+ )
+from lava_dispatcher.downloader import (
+ download_image,
+ )
+from lava_dispatcher.utils import (
+ logging_spawn,
+ logging_system,
+ )
+
+
+class LavaFastModelClient(LavaClient):
+
+ PORT_PATTERN = 'terminal_0: Listening for serial connection on port (\d+)'
+ ANDROID_WALLPAPER = 'system/wallpaper_info.xml'
+ SYS_PARTITION = 2
+ DATA_PARTITION = 5
+
+ def __init__(self, context, config):
+ super(LavaFastModelClient, self).__init__(context, config)
+ self._sim_binary = config.get('simulator_binary', None)
+ lic_server = config.get('license_server', None)
+ if not self._sim_binary or not lic_server:
+ raise RuntimeError("The device type config for this device "
+ "requires settings for 'simulator_binary' and 'license_server'")
+
+ os.putenv('ARMLMD_LICENSE_FILE', lic_server)
+ self._sim_proc = None
+
+ def _customize_android(self):
+ with image_partition_mounted(self._sd_image, self.DATA_PARTITION) as d:
+ wallpaper = '%s/%s' % (d, self.ANDROID_WALLPAPER)
+ # delete the android active wallpaper as slows things down
+ logging_system('sudo rm -f %s' % wallpaper)
+
+ #make sure PS1 is what we expect it to be
+ with image_partition_mounted(self._sd_image, self.SYS_PARTITION) as d:
+ logging_system(
+ 'sudo sh -c \'echo "PS1=%s ">> %s/etc/mkshrc\'' % (self.tester_str, d))
+
+ def _customize_ubuntu(self):
+ with image_partition_mounted(self._sd_image, self.root_part) as mntdir:
+ logging_system('sudo echo linaro > %s/etc/hostname' % mntdir)
+
+ def deploy_image(self, image, axf, is_android=False):
+ self._axf = download_image(axf, self.context)
+ self._sd_image = download_image(image, self.context)
+
+ logging.debug("image file is: %s" % self._sd_image)
+ if is_android:
+ self._customize_android()
+ else:
+ self._customize_ubuntu()
+
+ def _close_sim_proc(self):
+ self._sim_proc.close(True)
+
+ def _close_serial_proc(self):
+ self.proc.close(True)
+
+ def _get_sim_cmd(self):
+ return ("%s -a coretile.cluster0.*=%s "
+ "-C motherboard.smsc_91c111.enabled=1 "
+ "-C motherboard.hostbridge.userNetworking=1 "
+ "-C motherboard.mmc.p_mmc_file=%s "
+ "-C coretile.cache_state_modelled=0 "
+ "-C coretile.cluster0.cpu0.semihosting-enable=1 ") % (
+ self._sim_binary, self._axf, self._sd_image)
+
+ def _boot_linaro_image(self):
+ if self.proc is not None:
+ self.proc.close()
+ if self._sim_proc is not None:
+ self._sim_proc.close()
+
+ sim_cmd = self._get_sim_cmd()
+
+ # the simulator proc only has stdout/stderr about the simulator
+ # we hook up into a telnet port which emulates a serial console
+ logging.info('launching fastmodel with command %r' % sim_cmd)
+ self._sim_proc = logging_spawn(
+ sim_cmd,
+ logfile=self.sio,
+ timeout=1200)
+ atexit.register(self._close_sim_proc)
+ self._sim_proc.expect(self.PORT_PATTERN, timeout=300)
+ self._serial_port = self._sim_proc.match.groups()[0]
+ logging.info('serial console port on: %s' % self._serial_port)
+
+ match = self._sim_proc.expect(["ERROR: License check failed!",
+ "Simulation is started"])
+ if match == 0:
+ raise RuntimeError("fast model license check failed")
+
+ _pexpect_drain(self._sim_proc).start()
+
+ logging.info('simulator is started connecting to serial port')
+ self.proc = logging_spawn(
+ 'telnet localhost %s' % self._serial_port,
+ logfile=self.sio,
+ timeout=90)
+ atexit.register(self._close_serial_proc)
+
+ def _boot_linaro_android_image(self):
+ ''' booting android or ubuntu style images don't differ for FastModel'''
+ self._boot_linaro_image()
+
+ def reliable_session(self):
+ return self.tester_session()
+
+class _pexpect_drain(threading.Thread):
+ ''' The simulator process can dump a lot of information to its console. If
+ don't actively read from it, the pipe will get full and the process will
+ be blocked. This allows us to keep the pipe empty so the process can run
+ '''
+ def __init__(self, proc):
+ threading.Thread.__init__(self)
+ self.proc = proc
+ self.daemon = True #allows thread to die when main main proc exits
+ def run(self):
+ # change simproc's stdout so it doesn't overlap the stdout from our
+ # serial console logging
+ self.proc.logfile = open('/dev/null', 'w')
+ self.proc.interact()
=== modified file 'lava_dispatcher/client/master.py'
@@ -33,8 +33,10 @@
import pexpect
import errno
+from lava_dispatcher.downloader import (
+ download_image,
+ )
from lava_dispatcher.utils import (
- download_image,
logging_spawn,
logging_system,
string_to_list,
=== modified file 'lava_dispatcher/client/qemu.py'
@@ -22,7 +22,6 @@
import logging
import os
import pexpect
-from tempfile import mkdtemp
from lava_dispatcher.client.base import (
CommandRunner,
@@ -32,8 +31,10 @@
generate_image,
image_partition_mounted,
)
+from lava_dispatcher.downloader import (
+ download_image,
+ )
from lava_dispatcher.utils import (
- download_image,
logging_spawn,
logging_system,
)
=== modified file 'lava_dispatcher/context.py'
@@ -22,6 +22,7 @@
import tempfile
from lava_dispatcher.config import get_device_config
+from lava_dispatcher.client.fastmodel import LavaFastModelClient
from lava_dispatcher.client.master import LavaMasterImageClient
from lava_dispatcher.client.qemu import LavaQEMUClient
from lava_dispatcher.test_data import LavaTestData
@@ -38,10 +39,12 @@
self._client = LavaMasterImageClient(self, device_config)
elif client_type == 'qemu':
self._client = LavaQEMUClient(self, device_config)
+ elif client_type == 'fastmodel':
+ self._client = LavaFastModelClient(self, device_config)
else:
raise RuntimeError(
- "this version of lava-dispatcher only supports master & qemu "
- "clients, not %r" % device_config.get('client_type'))
+ "this version of lava-dispatcher only supports master, qemu, "
+ "and fastmodel clients, not %r" % device_config.get('client_type'))
self.test_data = LavaTestData()
self.oob_file = oob_file
self._host_result_dir = None
=== added file 'lava_dispatcher/default-config/lava-dispatcher/device-types/fastmodel.conf'
@@ -0,0 +1,5 @@
+client_type=fastmodel
+simulator_binary = /opt/arm/RTSM_A15x14-A7x14_VE/bin/RTSM_VE_Cortex-A15x4-A7x4
+
+# The license server must also be specified. eg:
+#license_server = 8224@192.168.1.10
=== added file 'lava_dispatcher/downloader.py'
@@ -0,0 +1,139 @@
+# Copyright (C) 2012 Linaro Limited
+#
+# Author: Andy Doan <andy.doan@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 atexit
+import bz2
+import contextlib
+import logging
+import os
+import shutil
+import subprocess
+import urllib2
+import urlparse
+import zlib
+
+from tempfile import mkdtemp
+
+@contextlib.contextmanager
+def _scp_stream(url, proxy=None, cookies=None):
+ process = None
+ try:
+ process = subprocess.Popen(
+ ['ssh', url.netloc, 'cat', url.path],
+ shell=False,
+ stdout=subprocess.PIPE
+ )
+ yield process.stdout
+ finally:
+ if process:
+ process.kill()
+
+@contextlib.contextmanager
+def _http_stream(url, proxy=None, cookies=None):
+ resp = None
+ handlers = []
+ if proxy:
+ handlers = [urllib2.ProxyHandler({'http': '%s' % proxy})]
+ opener = urllib2.build_opener(*handlers)
+
+ if cookies:
+ opener.addheaders.append(('Cookie', cookies))
+
+ try:
+ url = urllib2.quote(url.geturl(), safe=":/")
+ resp = opener.open(url, timeout=30)
+ yield resp
+ finally:
+ if resp:
+ resp.close()
+
+@contextlib.contextmanager
+def _file_stream(url, proxy=None, cookies=None):
+ fd = None
+ try:
+ fd = open(url.path, 'rb')
+ yield fd
+ finally:
+ if fd:
+ fd.close()
+
+@contextlib.contextmanager
+def _decompressor_stream(url, imgdir):
+ fd = None
+ decompressor = None
+
+ fname,suffix = _url_to_fname_suffix(url, imgdir)
+
+ if suffix == 'gz':
+ decompressor = zlib.decompressobj(16+zlib.MAX_WBITS)
+ elif suffix == 'bz2':
+ decompressor = bz2.BZ2Decompressor()
+ else:
+ fname = '%s.%s' % (fname, suffix) #don't remove the file's real suffix
+
+ def write(buff):
+ if decompressor:
+ buff = decompressor.decompress(buff)
+ fd.write(buff)
+
+ try:
+ fd = open(fname, 'wb')
+ yield (write,fname)
+ finally:
+ if fd:
+ fd.close
+
+def _url_to_fname_suffix(url, path='/tmp'):
+ filename = os.path.basename(url.path)
+ parts = filename.split('.')
+ suffix = parts[-1]
+ filename = os.path.join(path, '.'.join(parts[:-1]))
+ return (filename, suffix)
+
+def download_image(url, context, imgdir=None, delete_on_exit=True):
+ '''downloads a image that's been compressed as .bz2 or .gz and
+ decompresses it on the file to the cache directory
+ '''
+ logging.info("Downloading image: %s" % url)
+ if not imgdir:
+ imgdir = mkdtemp(dir=context.lava_image_tmpdir)
+ if delete_on_exit:
+ atexit.register(shutil.rmtree, imgdir)
+
+ url = urlparse.urlparse(url)
+ stream = None
+ if url.scheme == 'scp':
+ reader = _scp_stream
+ elif url.scheme == 'http' or url.scheme == 'https':
+ reader = _http_stream
+ elif url.scheme == 'file':
+ reader = _file_stream
+ else:
+ raise Exception("Unsupported url protocol scheme: %s" % url.scheme)
+
+ with reader(url, context.lava_proxy, context.lava_cookies) as r:
+ with _decompressor_stream(url, imgdir) as (writer, fname):
+ bsize = 32768
+ buff = r.read(bsize)
+ while buff:
+ writer(buff)
+ buff = r.read(bsize)
+ return fname
+
=== modified file 'lava_dispatcher/utils.py'
@@ -23,11 +23,9 @@
import logging
import os
import shutil
-import subprocess
import urllib2
import urlparse
from shlex import shlex
-from tempfile import mkdtemp
import pexpect
@@ -56,28 +54,6 @@
raise RuntimeError("Could not retrieve %s" % url)
return filename
-def decompress(image_file):
- for suffix, command in [('.gz', 'gunzip'),
- ('.xz', 'unxz'),
- ('.bz2', 'bunzip2')]:
- if image_file.endswith(suffix):
- logging.info("Uncompressing %s with %s", image_file, command)
- uncompressed_name = image_file[:-len(suffix)]
- subprocess.check_call(
- [command, '-c', image_file], stdout=open(uncompressed_name, 'w'))
- return uncompressed_name
- return image_file
-
-def download_image(url, context, imgdir=None):
- ''' common download function to be used by clients. This will download
- and decompress the image using LMC_COOKIES and/or LMC_PROXY settings
- '''
- logging.info("Downloading image: %s" % url)
- if not imgdir:
- imgdir = mkdtemp(dir=context.lava_image_tmpdir)
- img = download(url, imgdir, context.lava_proxy, context.lava_cookies)
- return decompress(img)
-
def link_or_copy_file(src, dest):
try:
dir = os.path.dirname(dest)