diff --git a/.env b/.env index be21ee4..887869d 100644 --- a/.env +++ b/.env @@ -1,3 +1,3 @@ DC_POSTGRES_IMAGE=postgres:11.2-alpine -DC_SERVER_IMAGE=lavasoftware/lava-server:2019.04 -DC_DISPATCHER_IMAGE=lavasoftware/lava-dispatcher:2019.04 +DC_SERVER_IMAGE=lavasoftware/lava-server:2019.06 +DC_DISPATCHER_IMAGE=lavasoftware/lava-dispatcher:2019.06 diff --git a/Makefile b/Makefile index a98d42a..077b60e 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,74 @@ +# Host address on which LAVA is accessible. +LAVA_HOST = localhost +# LAVA user to create/use. +LAVA_USER = admin +# lavacli "identity" (cached credential name) for the above user, to submit jobs +# "dispatcher" is legacy name from the original instructions. +LAVA_IDENTITY = dispatcher + + all: + contrib/monitor-images.sh $(LAVA_IDENTITY) & + sudo contrib/udev-forward.py -i lava-dispatcher & docker-compose up +stop: + pkill -f monitor-images.sh + -sudo pkill udev-forward.py + docker-compose stop + clean: + pkill -f monitor-images.sh + -sudo pkill udev-forward.py docker-compose rm -vsf docker volume rm -f lava-server-pgdata lava-server-joboutput lava-server-devices lava-server-health-checks + +# Create various board configs for connected board(s). Supposed to be done +# before "install" target. +board-configs: + @echo "Note: you should have *all* of your boards connected to USB before running this." + @echo "Press Ctrl+C to break if not. Review results carefully afterwards." + @read dummy + -mv ser2net/ser2net.conf ser2net/ser2net.conf.old + touch ser2net/ser2net.conf + @echo + contrib/make-board-files.sh devices + +install: + sudo cp contrib/LAVA.rules /etc/udev/rules.d/ + sudo cp contrib/usb-passthrough /usr/local/bin/ + sudo udevadm control --reload + +lava-setup: lava-user lava-identity lava-boards + +lava-user: + @echo -n "Input LAVA admin user passwd: "; \ + read passwd; \ + test -n "$$passwd" && docker exec -it lava-server lava-server manage users add $(LAVA_USER) --superuser --staff --passwd $$passwd || true + @echo + @echo "Now login at http://$(LAVA_HOST)/accounts/login/?next=/api/tokens/ and create an auth token (long sequence of chars)" + -xdg-open http://$(LAVA_HOST)/accounts/login/?next=/api/tokens/ + +lava-identity: + @echo + @echo -n "Enter auth token: "; \ + read token; \ + test -n "$$token" && lavacli identities add --username $(LAVA_USER) --token $$token --uri http://$(LAVA_HOST)/RPC2 $(LAVA_IDENTITY) || true + lavacli -i dispatcher system version + +lava-boards: + -lavacli -i $(LAVA_IDENTITY) device-types add frdm-k64f + -lavacli -i $(LAVA_IDENTITY) devices add --type frdm-k64f --worker lava-dispatcher frdm-k64f-01 + lavacli -i $(LAVA_IDENTITY) devices dict set frdm-k64f-01 devices/frdm-k64f-01.jinja2 + -lavacli -i $(LAVA_IDENTITY) device-types add qemu + -lavacli -i $(LAVA_IDENTITY) devices add --type qemu --worker lava-dispatcher qemu-01 + lavacli -i $(LAVA_IDENTITY) devices dict set qemu-01 devices/qemu-01.jinja2 + + -lavacli -i $(LAVA_IDENTITY) device-types add musca_a + lavacli -i $(LAVA_IDENTITY) device-types template set musca_a device-types/musca_a.jinja2 + +testjob: + lavacli -i dispatcher jobs submit example/lava.job + +dispatcher-shell: + docker exec -it lava-dispatcher bash diff --git a/contrib/LAVA.rules b/contrib/LAVA.rules new file mode 100644 index 0000000..2481e99 --- /dev/null +++ b/contrib/LAVA.rules @@ -0,0 +1,10 @@ +# Example LAVA.rules for USB passthrough to container +# +# Assumes that usb-passthrough script is installed in +# /usr/local/bin/passthrough + +# Pass all usb devices (TTY, block, CDC) to lava-dispatcher container +# FRDM-K64F +ACTION=="add", ENV{ID_SERIAL_SHORT}=="MYSERIALNUM", RUN+="/usr/local/bin/usb-passthrough -a -d %E{ID_SERIAL_SHORT} -i lava-dispatcher" +# Pass TTY through to lava-ser2net container +ACTION=="add", SUBSYSTEM=="tty", ENV{ID_SERIAL_SHORT}=="MYSERIALNUM", RUN+="/usr/local/bin/usb-passthrough -a -d %E{ID_SERIAL_SHORT} -i lava-ser2net" diff --git a/contrib/board-setup-helper.py b/contrib/board-setup-helper.py new file mode 100755 index 0000000..49ca256 --- /dev/null +++ b/contrib/board-setup-helper.py @@ -0,0 +1,118 @@ +#!/usr/bin/python3 + +# Copyright 2019 Linaro Limited +# Author: Kumar Gala + +# This program 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. +# +# This program 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. + +import pyudev +import argparse +import os +import pprint + +def ser2net(dev, ser2net_file): + ser2net = {} + port_num = 5001 + + with open(ser2net_file, 'r') as fd: + for line in fd: + line = line.strip() + values = line.split(':') + port = int(values.pop(0)) + ser2net[port] = values + + if len(ser2net): + port_num = max(ser2net.keys()) + 1 + + new_device = True + + for k in ser2net: + if dev in ser2net[k][2]: + new_device = False + print("WARNING: device %s already exists in %s on port %d" % (dev, ser2net_file, k)) + port_num = k + + if new_device: + with open(ser2net_file, 'a') as fd: + fd.write("%d:telnet:0:%s:115200 8DATABITS NONE 1STOPBIT LOCAL max-connections=10\n" % (port_num, dev)) + + return port_num + +def get_device_numbers(serial_no, dev_type, board_file, ser2net_file): + result = None, None + context = pyudev.Context() + + devices = context.list_devices() + + board_jinja = open(board_file, 'w') + + board_jinja.write("{%% extends '%s.jinja2' %%}\n" % dev_type) + board_jinja.write("{%% set board_id = '%s' %%}\n" % serial_no) + + for device in devices: + serial = device.attributes.get("serial") + if serial is not None and serial_no in serial.decode("utf-8"): + child = device.children + for c in child: + if (c.subsystem == "block"): + for l in c.device_links: + if "by-id" in l: + board_jinja.write("{%% set usb_mass_device = '%s' %%}\n" % l) + + if (c.subsystem == "tty"): + for l in c.device_links: + if "by-id" in l: + port = ser2net(l, ser2net_file) + if port: + board_jinja.write("{%% set connection_command = 'telnet ser2net %d' %%}\n" % port) + break + + if dev_type == "frdm-k64f": + board_jinja.write("{% set resets_after_flash = false %}\n") + + +def main(): + parser = argparse.ArgumentParser(description='LAVA board helper', add_help=False) + + parser.add_argument("-d", "--device_serial_num", type=str, required=True, + help="Devices serial number") + + parser.add_argument("-t", "--device_type", type=str, required=True, + help="Devices type") + + parser.add_argument("-s", "--ser2net_conf", type=str, required=False, + default = "./ser2net/ser2net.conf", + help="ser2net configuration file") + + parser.add_argument("-u", dest='udev', default=False, action='store_true') + + parser.add_argument("-b", "--board_file", type=str, required=False, + default = "board.jinja2", + help="board jinja file") + + options = parser.parse_args() + + get_device_numbers(options.device_serial_num, options.device_type, + options.board_file, options.ser2net_conf) + + if (options.udev): + print('ACTION=="add", ENV{ID_SERIAL_SHORT}=="%s", RUN+="/usr/local/bin/usb-passthrough -a -d %%E{ID_SERIAL_SHORT} -i lava-dispatcher"' % options.device_serial_num) + print('ACTION=="add", SUBSYSTEM=="tty", ENV{ID_SERIAL_SHORT}=="%s", RUN+="/usr/local/bin/usb-passthrough -a -d %%E{ID_SERIAL_SHORT} -i lava-ser2net"' % options.device_serial_num) + + + +if __name__ == '__main__': + main() diff --git a/contrib/make-board-files.sh b/contrib/make-board-files.sh new file mode 100755 index 0000000..001e3bf --- /dev/null +++ b/contrib/make-board-files.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +if [ -z "$1" ]; then + echo "Usage: $0 " + exit 1 +fi + +for ser in $1/*.serial; do + echo "Processing: $ser" + devname=$(basename $ser .serial) + devtype=$(echo $devname | python -c "import sys; print(sys.stdin.readline().rsplit('-', 1)[0])") + contrib/board-setup-helper.py -d $(cat $ser) -t $devtype -b $1/$devname.jinja2 -u >contrib/LAVA.rules +done diff --git a/contrib/monitor-images.sh b/contrib/monitor-images.sh new file mode 100755 index 0000000..64d2ce7 --- /dev/null +++ b/contrib/monitor-images.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# Usage: ./monitor-images.sh +# +# This script monitors the 'test-images' directory for any file that is +# dropped into it. It assumes the directory layout is +# test-images/, and upon detecting a new file, uses the +# corresponding job template in job-templates/lava_.job +# to submit the job with the provided LAVA identity. +# +# '' in the template is automatically substituted with the +# actual path to the test image. + +contrib_dir=`dirname $0` +dir=test-images +template_dir=job-templates + +cd $contrib_dir/.. + +inotifywait -r -m "$dir" -e close_write --format '%w%f' | + while IFS=' ' read -r fname + do + IFS='/' + read -ra path <<< "$fname" + template="$template_dir"/lava_"${path[1]}".job + if [ ! -f "$template" ] + then + #if no template found for the device-type, do nothing + continue + fi + + echo "Using template for device type ${path[1]}:" + echo " $template" + + # Replace with actual path to test image + sed_cmd=s/\/"file:\/\/\/${fname//\//\\\/}"/g + + tmp_file=$(mktemp) + echo "Submitting job for $fname" + [ -f "$fname" ] && sed "$sed_cmd" "$template" > "$tmp_file" && lavacli -i $1 jobs submit "$tmp_file" && rm "$tmp_file" + done diff --git a/contrib/udev-forward.py b/contrib/udev-forward.py new file mode 100755 index 0000000..bac8142 --- /dev/null +++ b/contrib/udev-forward.py @@ -0,0 +1,271 @@ +#!/usr/bin/python3 + +# Copyright 2019 Linaro Limited +# Copyright (c) 2014 Taeyeon Mori (for MurmurHash2 code) + +# Author: Kumar Gala + +# This program 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. +# +# This program 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# +# The MurmurHash2 implementation is take from: +# https://github.com/Orochimarufan/cdev/blob/master/cdev/murmurhash2.py +# +# UDEV event forwarding to a container +# +# based on https://github.com/eiz/udevfw + +import os +import sys +import socket +import pyudev +import syslog +import threading +import docker +import queue +import array +from struct import * +from ctypes import CDLL, get_errno +import argparse + +NETLINK_KOBJECT_UEVENT = 15 +UDEV_MONITOR_UDEV = 2 +CLONE_NEWNET = 0x40000000 +UDEV_MONITOR_MAGIC = 0xFEEDCAFE + +containers = {} + +class containersClass: + def __init__(self): + self.thread = threading.Thread() + self.wq = queue.Queue() + +if array.array('L').itemsize == 4: + uint32_t = 'L' +elif array.array('I').itemsize == 4: + uint32_t = 'I' +else: + raise ImportError("Could not determine 4-byte array code!") + +def MurmurHash2(input, seed=0): + """ + Generate a 32-bit hash from a string using the MurmurHash2 algorithm + + takes a bytestring! + + Pure-python implementation. + """ + l = len(input) + + # m and r are mixing constants generated offline + # They're not really magic, they just happen to work well + m = 0x5bd1e995 + #r = 24 + + # Initialize the hash to a "random" value + h = seed ^ l + + # Mix 4 bytes at a time into the hash + x = l % 4 + o = l - x + + for k in array.array(uint32_t, input[:o]): + # Original Algorithm + #k *= m; + #k ^= k >> r; + #k *= m; + + #h *= m; + #h ^= k; + + # My Algorithm + k = (k * m) & 0xFFFFFFFF + h = (((k ^ (k >> 24)) * m) ^ (h * m)) & 0xFFFFFFFF + + # Explanation: We need to keep it 32-bit. There are a few rules: + # 1. Inputs to >> must be truncated, it never overflows + # 2. Inputs to * must be truncated, it may overflow + # 3. Inputs to ^ may be overflowed, it overflows if any input was overflowed + # 4. The end result must be truncated + # Therefore: + # b = k * m -> may overflow, we truncate it because b >> r cannot take overflowed data + # c = b ^ (b >> r) -> never overflows, as b is truncated and >> never does + # h = (c * m) ^ (h * m) -> both inputs to ^ may overflow, but since ^ can take it, we truncate once afterwards. + + # Handle the last few bytes of the input array + if x > 0: + if x > 2: + h ^= input[o+2] << 16 + if x > 1: + h ^= input[o+1] << 8 + h = ((h ^ input[o]) * m) & 0xFFFFFFFF + + # Do a few final mixes of the hash to ensure the last few + # bytes are well incorporated + + # Original: + #h ^= h >> 13; + #h *= m; + #h ^= h >> 15; + + h = ((h ^ (h >> 13)) * m) & 0xFFFFFFFF + return (h ^ (h >> 15)) + +def bloomHash(tag): + bits = 0 + hash = MurmurHash2(tag.encode()) + + bits = bits | 1 << (hash & 63) + bits = bits | 1 << ((hash >> 6) & 63) + bits = bits | 1 << ((hash >> 12) & 63) + bits = bits | 1 << ((hash >> 18) & 63) + + return bits + + +def buildHeader(proplen, subsys, devtype, taghash): + header_fmt = "8s8I" + header_size = calcsize(header_fmt) + subsys_hash = 0 + devtype_hash = 0 + + if subsys: + subsys_hash = socket.htonl(MurmurHash2(subsys.encode())) + + if devtype: + devtype_hash = socket.htonl(MurmurHash2(devtype.encode())) + + tag_low = socket.htonl(taghash & 0xffffffff) + tag_high = socket.htonl(taghash >> 32) + + return pack(header_fmt, b"libudev", socket.htonl(UDEV_MONITOR_MAGIC), + header_size, header_size, proplen, subsys_hash, devtype_hash, + tag_low, tag_high) + + +def BuildPacket(dev): + subsys = dev.subsystem + devtype = dev.device_type + + proplist = bytearray() + for p in dev.properties: + proppair = p + "=" + dev.properties[p] + proplist = proplist + proppair.encode() + bytes([0]) + + tag_hash = 0 + for t in dev.tags: + tag_hash = tag_hash | bloomHash(t) + + hdr = buildHeader(len(proplist), subsys, devtype, tag_hash) + + return hdr + proplist + +def errcheck(ret, func, args): + if ret == -1: + e = get_errno() + raise OSError(e, os.strerror(e)) + +def sendMsgThread(inst, netns_file): + nsfd = open(netns_file, "r") + libc = CDLL('libc.so.6', use_errno=True) + libc.setns.errcheck = errcheck + libc.setns(nsfd.fileno(), CLONE_NEWNET) + sendfd = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW|socket.SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT) + if options.debug: + print(sendfd) + + while True: + (work_type, pkt) = containers[inst].wq.get() + + if work_type == "PKT": + # Older kernels (like 4.15 on Ubuntu 18.04) return ECONNREFUSED + # to work around this we just ignore this specific error as the + # data still is send on the socket. + try: + sendfd.sendto(pkt, (0, UDEV_MONITOR_UDEV)) + except ConnectionRefusedError: + pass + + if work_type == "DOCKER": + nsfd.close() + break + +def udev_event_callback(dev): + if options.debug: + print('background event {0.action}: {0.device_path}'.format(dev)) + for i in containers: + if containers[i].thread.is_alive(): + containers[i].wq.put(("PKT", BuildPacket(dev))) + +def start_up_thread(name): + container = client.containers.get(name) + ns_filename = container.attrs['NetworkSettings']['SandboxKey'] + if options.debug: + print("DBG: Container[%s] netns file %s" % (name, ns_filename)) + containers[name] = containersClass() + containers[name].thread = threading.Thread(name=name, target=sendMsgThread, args=(name, ns_filename)) + containers[name].thread.start() + +def main(): + parser = argparse.ArgumentParser(description='USB device passthrough for docker containers', add_help=False) + + parser.add_argument("-i", "--instance", type=str, required=True, action='append', + help="Docker instance", dest="names") + + parser.add_argument("-d", "--debug", action="store_true", + help="Enable Debug Logging") + + global options + options = parser.parse_args() + + context = pyudev.Context() + if options.debug: + context.log_priority = syslog.LOG_DEBUG + monitor = pyudev.Monitor.from_netlink(context) + observer = pyudev.MonitorObserver(monitor, callback=udev_event_callback, name='monitor-observer') + + observer.start() + + global client + + client = docker.from_env() + + # If the container is running get the namespace file (SandboxKey) + # and startup the sendMsgThread + f = {'name': options.names, 'status': 'running'} + if client.containers.list(filters=f): + for name in options.names: + start_up_thread(name) + + # Watch for docker events to startup or shutdown a new sendMsgThread + f = {'type': 'container', 'event': ['start', 'stop'], 'container': options.names } + try: + for event in client.events(decode=True, filters=f): + name = event['Actor']['Attributes']['name'] + if options.debug: + print("DOCKER: %s for %s" % (event['Action'], name)) + if event['Action'] == 'start': + start_up_thread(name) + if event['Action'] == 'stop': + containers[name].wq.put(("DOCKER", event['Action'])) + containers[name].thread.join() + except KeyboardInterrupt: + for i in containers: + if containers[i].thread.is_alive(): + containers[i].wq.put(("DOCKER", 'stop')) + + +if __name__ == '__main__': + main() diff --git a/contrib/usb-passthrough b/contrib/usb-passthrough new file mode 100755 index 0000000..788ef60 --- /dev/null +++ b/contrib/usb-passthrough @@ -0,0 +1,122 @@ +#!/usr/bin/python3 + +# Copyright 2019 Linaro Limited +# Author: Dave Pigott +# Author: Kumar Gala + +# This program 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. +# +# This program 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# +# USB device passthrough for docker containers + +import argparse +import os +import sys +import stat +import docker + +# Example of what a udev rule looks like for using this script +# +# ACTION=="add", ENV{ID_SERIAL_SHORT}=="E00A1029", RUN+="/usr/local/bin/usb-passthrough -a -d %E{ID_SERIAL_SHORT} -i lava-dispatcher" + +def pass_device_into_container(instance, dev_type, major, minor, node, serial_no, subsys, links): + client = docker.from_env() + + container = client.containers.get(instance) + + allow_devices = open("/sys/fs/cgroup/devices/docker/%s/devices.allow" % container.id, "w") + allow_devices.write("%s %s:%s rwm\n" % (dev_type, major, minor)) + allow_devices.close() + create_dev_cmd = "sh -c '[ ! -e " + node + " ] " + \ + "&& mkdir -p $(dirname " + node + \ + ") && mknod " + node + " " + dev_type + \ + " " + str(major) + " " + str(minor) + "'" + container.exec_run(create_dev_cmd) + + for link in links.split(): + link_cmd = "sh -c 'mkdir -p $(dirname " + link + ");" + \ + "ln -f -s " + node + " " + link + "'" + container.exec_run(link_cmd) + + udev_file_name = "/etc/udev/rules.d/lava-%s-%s-%s" % (instance, serial_no, subsys) + if ('ID_USB_INTERFACE_NUM' in os.environ): + udev_file_name += "-if%s" % os.environ['ID_USB_INTERFACE_NUM'] + udev_file_name += ".rules" + + udev_rule = open(udev_file_name, "w") + + udev_rule.write('ACTION=="remove",ENV{DEVNAME}=="' + node) + udev_rule.write('",RUN+="' + sys.argv[0]) + udev_rule.write(' -d ' + serial_no) + udev_rule.write(' -i ' + instance) + udev_rule.write('"\n') + + udev_rule.close() + +def remove_device_from_container(instance, dev_type, major, minor, node, serial_no, subsys, links): + client = docker.from_env() + + container = client.containers.get(instance) + + allow_devices = open("/sys/fs/cgroup/devices/docker/%s/devices.deny" % container.id, "w") + allow_devices.write("%s %s:%s rwm\n" % (dev_type, major, minor)) + allow_devices.close() + create_dev_cmd = "rm " + node + container.exec_run(create_dev_cmd) + + for link in links.split(): + link_cmd = "rm " + link + container.exec_run(link_cmd) + + udev_file_name = "/etc/udev/rules.d/lava-%s-%s-%s" % (instance, serial_no, subsys) + if ('ID_USB_INTERFACE_NUM' in os.environ): + udev_file_name += "-if%s" % os.environ['ID_USB_INTERFACE_NUM'] + udev_file_name += ".rules" + + os.remove(udev_file_name) + +def main(): + parser = argparse.ArgumentParser(description='USB device passthrough for docker containers', add_help=False) + + parser.add_argument("-d", "--device_serial", type=str, required=True, + help="Devices serial number") + parser.add_argument("-i", "--instance", type=str, required=True, + help="Docker instance") + parser.add_argument("-a", "--add", action="store_true", + help="Add device") + + options = parser.parse_args() + + subsys = os.environ['SUBSYSTEM'] + links = "" + if ('DEVLINKS' in os.environ): + links = os.environ['DEVLINKS'] + + dev_type = "c" + if (subsys == "block"): + dev_type = "b" + + major = os.environ['MAJOR'] + minor = os.environ['MINOR'] + device_node = os.environ['DEVNAME'] + + if (options.add): + pass_device_into_container(options.instance, dev_type, major, minor, device_node, options.device_serial, subsys, links) + else: + remove_device_from_container(options.instance, dev_type, major, minor, device_node, options.device_serial, subsys, links) + + +if __name__ == '__main__': + main() diff --git a/device-types/musca_a.jinja2 b/device-types/musca_a.jinja2 new file mode 100644 index 0000000..94d3215 --- /dev/null +++ b/device-types/musca_a.jinja2 @@ -0,0 +1,36 @@ +{# device_type: musca_a #} +{% extends 'base.jinja2' %} +{% block body %} +board_id: '{{ board_id|default('0000000000') }}' +usb_vendor_id: '0d28' +usb_product_id: '0204' + +actions: + deploy: + connections: + lxc: + methods: + lxc: + image: + parameters: + + boot: + connections: + serial: + lxc: + ssh: + methods: + lxc: + pyocd: + parameters: + command: + pyocd-flashtool + options: + - -d {{ debug|default('debug') }} + - -t musca_a1 + - -f 3000000 + cmsis-dap: + parameters: + usb_mass_device: '{{ usb_mass_device|default('/notset') }}' + resets_after_flash: {{ resets_after_flash|default(True) }} +{% endblock body -%} diff --git a/devices/qemu-01.jinja2 b/devices/qemu-01.jinja2 new file mode 100644 index 0000000..6d145fc --- /dev/null +++ b/devices/qemu-01.jinja2 @@ -0,0 +1,4 @@ +{% extends 'qemu.jinja2' %} +{% set mac_addr = 'DE:AD:BE:EF:05:01' %} +{% set memory = 1024 %} +{% set netdevice = 'tap' %} diff --git a/docker-compose.yaml b/docker-compose.yaml index 7dc076c..cab96fb 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -65,6 +65,18 @@ services: - joboutput:/var/lib/lava-server/default/media/job-output - ./overlays/lava-server/etc/lava-server/instance.conf:/etc/lava-server/instance.conf:ro - ./overlays/lava-server/etc/lava-server/settings.conf:/etc/lava-server/settings.conf:ro + # Example for development + # If you wanted to point to a local git checkout of lava for development + # of lava_dispatcher, you can uncomment out the lines below and + # set the 'source:' to point to where your lava checkout is + # The example assumes its relative in ../lava + # +# - type: bind +# source: ../lava/lava_server +# target: /usr/lib/python3/dist-packages/lava_server +# - type: bind +# source: ../lava/lava_common +# target: /usr/lib/python3/dist-packages/lava_common depends_on: - db environment: @@ -87,9 +99,13 @@ services: lava-dispatcher: container_name: lava-dispatcher - image: ${DC_DISPATCHER_IMAGE} + #image: ${DC_DISPATCHER_IMAGE} + build: + context: ./lite-lava-dispatcher + args: + DC_DISPATCHER_IMAGE: ${DC_DISPATCHER_IMAGE} devices: - - /dev/kvm # needed for QEMU +# - /dev/kvm # needed for QEMU - /dev/net/tun # needed for QEMU security_opt: - apparmor:unconfined # needed for usb mass storage @@ -101,8 +117,31 @@ services: LOGGER_URL: "tcp://lava-logs:5555" MASTER_URL: "tcp://lava-master:5556" volumes: + - /run/udev/control:/run/udev/control:ro # libudev expects it for udev events - /boot:/boot:ro - /lib/modules:/lib/modules:ro + - ./test-images:/test-images:ro + # Example for development + # If you wanted to point to a local git checkout of lava for development + # of lava_dispatcher, you can uncomment out the lines below and + # set the 'source:' to point to where your lava checkout is + # The example assumes its relative in ../lava + # +# - type: bind +# source: ../lava/lava_dispatcher +# target: /usr/lib/python3/dist-packages/lava_dispatcher +# - type: bind +# source: ../lava/lava_common +# target: /usr/lib/python3/dist-packages/lava_common + + ser2net: + container_name: lava-ser2net + build: + context: ./ser2net + volumes: + - type: bind + source: ./ser2net/ser2net.conf + target: /etc/ser2net.conf volumes: db-data: diff --git a/example/board.jinja2 b/example/board.jinja2 new file mode 100644 index 0000000..15955f0 --- /dev/null +++ b/example/board.jinja2 @@ -0,0 +1,5 @@ +{% extends 'frdm-k64f.jinja2' %} +{% set board_id = '0240000031754e45001c0019948500046461000097969900' %} +{% set usb_mass_device = '/dev/disk/by-id/usb-MBED_VFS_0240000031754e45001c0019948500046461000097969900-0:0' %} +{% set connection_command = 'telnet ser2net 5001' %} +{% set resets_after_flash = false %} diff --git a/example/lava.job b/example/lava.job new file mode 100644 index 0000000..950dbae --- /dev/null +++ b/example/lava.job @@ -0,0 +1,50 @@ +# Zephyr JOB definition for frdm-k64f +device_type: 'frdm-k64f' +job_name: 'zephyr-upstream tests-subsys-logging-log_list-logging-log_list-zephyr' + +timeouts: + job: + minutes: 3 + action: + minutes: 3 + actions: + wait-usb-device: + seconds: 40 + +priority: medium +visibility: public + +actions: +- deploy: + timeout: + minutes: 3 + to: tmpfs + images: + zephyr: + url: 'file:///test-images/frdm-k64f/logging.log_list/zephyr.bin' + +- boot: + method: cmsis-dap + timeout: + minutes: 1 + +- test: + timeout: + minutes: 2 + monitors: + - name: 'tests-subsys-logging-log_list-logging-log_list-zephyr' + start: (tc_start\(\)|starting .*test|BOOTING ZEPHYR OS) + end: PROJECT EXECUTION + pattern: (?P(PASS|FAIL))\s-\s(?P\w+)\r\n + fixupdict: + PASS: pass + FAIL: fail + +metadata: + build-url: https://ci.linaro.org/job/zephyr-upstream/PLATFORM=frdm_k64f,ZEPHYR_TOOLCHAIN_VARIANT=zephyr,label=docker-xenial-amd64-13/3708/ + build-log: https://ci.linaro.org/job/zephyr-upstream/PLATFORM=frdm_k64f,ZEPHYR_TOOLCHAIN_VARIANT=zephyr,label=docker-xenial-amd64-13/3708//consoleText + zephyr-gcc-variant: zephyr + platform: frdm_k64f + git-url: https://git.linaro.org/zephyrproject-org/zephyr.git + git-commit: 193fb971 + diff --git a/job-templates/lava_cc3220SF.job b/job-templates/lava_cc3220SF.job new file mode 100644 index 0000000..530010f --- /dev/null +++ b/job-templates/lava_cc3220SF.job @@ -0,0 +1,47 @@ +# Standard job template for zephyr tests on cc3220sf_launchxl +device_type: 'cc3220SF' +job_name: 'zephyr-upstream tests-zephyr-cc3220SF' + +timeouts: + job: + minutes: 3 + action: + minutes: 3 + actions: + wait-usb-device: + seconds: 40 + +priority: medium +visibility: public + +actions: +- deploy: + timeout: + minutes: 3 + to: tmpfs + images: + binary: + url: + +- boot: + method: openocd + timeout: + minutes: 1 + +- test: + timeout: + minutes: 2 + monitors: + - name: 'tests-zephyr-cc3220SF' + start: (tc_start\(\)|starting .*test|BOOTING ZEPHYR OS) + end: PROJECT EXECUTION + pattern: (?P(PASS|FAIL))\s-\s(?P\w+)\r\n + fixupdict: + PASS: pass + FAIL: fail + +metadata: + zephyr-gcc-variant: zephyr + platform: cc3220sf_launchxl + git-url: https://git.linaro.org/zephyrproject-org/zephyr.git + diff --git a/job-templates/lava_frdm-k64f.job b/job-templates/lava_frdm-k64f.job new file mode 100644 index 0000000..3e3e4e1 --- /dev/null +++ b/job-templates/lava_frdm-k64f.job @@ -0,0 +1,50 @@ +# Zephyr JOB definition for frdm-k64f +device_type: 'frdm-k64f' +job_name: 'zephyr-upstream tests-subsys-logging-log_list-logging-log_list-zephyr' + +timeouts: + job: + minutes: 3 + action: + minutes: 3 + actions: + wait-usb-device: + seconds: 40 + +priority: medium +visibility: public + +actions: +- deploy: + timeout: + minutes: 3 + to: tmpfs + images: + zephyr: + url: + +- boot: + method: cmsis-dap + timeout: + minutes: 1 + +- test: + timeout: + minutes: 2 + monitors: + - name: 'tests-subsys-logging-log_list-logging-log_list-zephyr' + start: (tc_start\(\)|starting .*test|BOOTING ZEPHYR OS) + end: PROJECT EXECUTION + pattern: (?P(PASS|FAIL))\s-\s(?P\w+)\r\n + fixupdict: + PASS: pass + FAIL: fail + +metadata: + build-url: https://ci.linaro.org/job/zephyr-upstream/PLATFORM=frdm_k64f,ZEPHYR_TOOLCHAIN_VARIANT=zephyr,label=docker-xenial-amd64-13/3708/ + build-log: https://ci.linaro.org/job/zephyr-upstream/PLATFORM=frdm_k64f,ZEPHYR_TOOLCHAIN_VARIANT=zephyr,label=docker-xenial-amd64-13/3708//consoleText + zephyr-gcc-variant: zephyr + platform: frdm_k64f + git-url: https://git.linaro.org/zephyrproject-org/zephyr.git + git-commit: 193fb971 + diff --git a/lite-lava-dispatcher/Dockerfile b/lite-lava-dispatcher/Dockerfile new file mode 100644 index 0000000..7798b91 --- /dev/null +++ b/lite-lava-dispatcher/Dockerfile @@ -0,0 +1,24 @@ +ARG DC_DISPATCHER_IMAGE + +FROM ${DC_DISPATCHER_IMAGE} + +ENV JLINK_DEB="JLink_Linux_V644h_x86_64.deb" +ENV JLINK_URL="https://www.segger.com/downloads/jlink/${JLINK_DEB}" +ENV ZEPHYR_HOST_TOOLS="zephyr-sdk-x86_64-hosttools-standalone-0.9.sh" +ENV ZEPHYR_HOST_TOOLS_URL="https://builds.zephyrproject.org/sdk/0.10.1/${ZEPHYR_HOST_TOOLS}" + +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get -y install \ + python3-pip libusb-1.0.0 vim && \ + apt-get -y -t stretch-backports install libudev1 && \ + pip3 install --upgrade setuptools && \ + pip3 install -U git+https://github.com/mbedmicro/pyOCD && \ + wget --post-data="accept_license_agreement=accepted&non_emb_ctr=confirmed&submit=Download+software" ${JLINK_URL} -O ${JLINK_DEB} && \ + dpkg -i ${JLINK_DEB} && \ + rm ${JLINK_DEB} && \ + ln -s /opt/SEGGER/JLink/JLinkExe /usr/local/bin && \ + wget ${ZEPHYR_HOST_TOOLS_URL} -O ${ZEPHYR_HOST_TOOLS} && \ + chmod a+x ${ZEPHYR_HOST_TOOLS} && \ + ./${ZEPHYR_HOST_TOOLS} -d /opt/zephyr-sdk -y && \ + rm ${ZEPHYR_HOST_TOOLS} && \ + ln -s /opt/zephyr-sdk/sysroots/x86_64-pokysdk-linux/usr/bin/openocd /usr/local/bin/ diff --git a/overlays/lava-server/etc/lava-server/settings.conf b/overlays/lava-server/etc/lava-server/settings.conf index 57e683e..ab2f6bd 100644 --- a/overlays/lava-server/etc/lava-server/settings.conf +++ b/overlays/lava-server/etc/lava-server/settings.conf @@ -2,6 +2,7 @@ "HTTPS_XML_RPC": false, "MOUNT_POINT": "/", "STATIC_URL": "/static/", + "ALLOWED_HOSTS": ["192.168.1.14", "127.0.0.1", "localhost"], "CSRF_COOKIE_SECURE": false, "SESSION_COOKIE_SECURE": false, "EVENT_NOTIFICATION": true, diff --git a/ser2net/Dockerfile b/ser2net/Dockerfile new file mode 100644 index 0000000..26efa28 --- /dev/null +++ b/ser2net/Dockerfile @@ -0,0 +1,8 @@ +FROM debian:sid-slim + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + ser2net=3.5-2 && \ + rm -rf /var/lib/apt/lists/* + +CMD echo -n "Starting " && ser2net -v && ser2net -d -c /etc/ser2net.conf -P /var/run/ser2net.pid diff --git a/ser2net/ser2net.conf b/ser2net/ser2net.conf new file mode 100644 index 0000000..e69de29 diff --git a/test-images/cc3220SF/logging.log_list/zephyr.elf b/test-images/cc3220SF/logging.log_list/zephyr.elf new file mode 100644 index 0000000..a508051 Binary files /dev/null and b/test-images/cc3220SF/logging.log_list/zephyr.elf differ diff --git a/test-images/frdm-k64f/logging.log_list/zephyr.bin b/test-images/frdm-k64f/logging.log_list/zephyr.bin new file mode 100644 index 0000000..d5ac7ef Binary files /dev/null and b/test-images/frdm-k64f/logging.log_list/zephyr.bin differ