00 LAVA nedir? — Board Farm kavramı
LAVA (Linaro Automated Validation Architecture), fiziksel gömülü kartları uzaktan kontrol edip üzerlerinde otomatik test çalıştırmak için geliştirilmiş açık kaynaklı bir test otomasyon altyapısıdır.
Gömülü Linux geliştirmede en büyük zorluklardan biri donanım testini CI/CD döngüsüne dahil etmektir. Sanal makinelerde çalıştırılamayan sürücü testleri, gerçek UART davranışı, DMA transfer hataları ve çevre birimine özgü timing sorunları ancak fiziksel donanım üzerinde yakalanabilir. LAVA tam bu boşluğu doldurmak için tasarlanmıştır.
Board Farm nedir?
Board farm, ağa bağlı, merkezi olarak yönetilen fiziksel gömülü kartlardan oluşan bir donanım havuzudur. Her kart güç kontrolü (PDU — Power Distribution Unit veya akıllı priz), seri konsol (USB-UART dönüştürücü) ve genellikle ağ erişimi (TFTP/NFS sunucusu) ile donatılmıştır. LAVA bu altyapıyı soyutlayarak kullanıcıların kartlara sanki bir bulut kaynağıymış gibi iş gönderebilmesini sağlar.
Geliştirici / CI Pipeline
│
│ LAVA job.yaml gönder (REST API veya lava-tool)
▼
┌─────────────────────────────────────────────────┐
│ LAVA Server │
│ • Job queue yönetimi │
│ • Cihaz havuzu (device pool) │
│ • REST API / Web UI │
│ • Test sonuçlarını veritabanına kaydet │
└─────────────────┬───────────────────────────────┘
│ ZMQ mesajlaşma
▼
┌─────────────────────────────────────────────────┐
│ LAVA Dispatcher │
│ • Job'u DUT'a iletir │
│ • TFTP/NFS, serial console yönetir │
│ • Test çıktısını parse eder │
└──────┬──────────────────────────────────────────┘
│ Serial + Power + Network
▼
┌─────────────────────────────────────────────────┐
│ DUT (Device Under Test) │
│ Raspberry Pi 4 / i.MX8 / STM32MP1 … │
└─────────────────────────────────────────────────┘
LAVA'nın sağladığı avantajlar
| Özellik | Manuel Test | LAVA ile |
|---|---|---|
| Paralel kart testi | Sıralı, manuel | Otomatik, eş zamanlı |
| CI entegrasyonu | Yok | GitLab/Jenkins webhook |
| Power cycle | Elle fiş çek | PDU API ile otomatik |
| Sonuç raporlama | Log dosyası | Yapılandırılmış JSON/XML |
| Cihaz havuzu | Yok | Health check + queue |
LAVA, Linaro tarafından ARM ekosistemi için geliştirilmiş olsa da x86, RISC-V ve diğer mimarilerde de kullanılabilir. Linaro'nun kendi LAVA instance'ı (validation.linaro.org) kamuya açık test altyapısı olarak çalışmaktadır.
01 Mimari — Server, Dispatcher, Worker, DUT
LAVA dört ana bileşenden oluşur. Her bileşen farklı bir sorumluluk alanına sahiptir ve aralarındaki iletişim ZMQ protokolü üzerinden yürütülür.
LAVA Server
lava-server, Django tabanlı web uygulamasıdır. Görevleri: kullanıcı kimlik doğrulama, cihaz tipi ve cihaz kaydı, job kuyruğu yönetimi, test sonuçlarının PostgreSQL veritabanına kaydedilmesi ve REST API sunumudur. Web UI üzerinden job durumu izlenir, log dosyaları incelenir, cihaz sağlık durumları görülür.
# LAVA paketlerini kur
apt-get install lava-server lava-server-doc
# Veritabanı migrasyonu çalıştır
lava-server manage migrate
# Superuser oluştur
lava-server manage createsuperuser
# Servisi başlat
systemctl enable --now lava-server lava-worker
LAVA Dispatcher
lava-dispatcher, job'ları fiilen çalıştıran bileşendir. Server'dan ZMQ aracılığıyla iş alır, cihazın seri konsolunu açar, TFTP üzerinden kernel/DTB/rootfs transfer eder, boot sürecini izler ve test çıktısını parse eder. Dispatcher, DUT'a fiziksel olarak yakın bir makinede çalışır.
Worker
Worker, dispatcher'ın çalıştığı makinedir. Bir LAVA kurulumunda birden fazla worker olabilir; her worker'a farklı cihaz tipleri atanabilir. Worker, server ile ZMQ PUSH/PULL socket'leri üzerinden haberleşir.
DUT — Device Under Test
DUT, test edilen fiziksel karttır. LAVA açısından DUT'un üç bağlantısı kritiktir:
Bileşenler arası iletişim portları
# Server ZMQ publish/subscribe portları
EVENT_SOCKET = "tcp://*:5500"
EVENT_NOTIFICATION = "tcp://*:5501"
# Worker → Server handshake
MASTER_URL = "tcp://lava-server.local:5556"
LOG_SOCKET = "tcp://lava-server.local:5555"
02 Device Dictionary — YAML cihaz tanımı
Device Dictionary, bir cihazın LAVA sistemindeki kimliğini ve bağlantı parametrelerini tanımlayan Jinja2 şablonudur. Her fiziksel DUT için ayrı bir dictionary oluşturulur.
Temel yapı
{% extends 'rpi4b.jinja2' %}
{% set connection_command = 'telnet dispatcher01.local 7001' %}
{% set hard_reset_command = '/usr/local/bin/pdu-ctrl.sh off rpi4-01 && sleep 3 && /usr/local/bin/pdu-ctrl.sh on rpi4-01' %}
{% set power_off_command = '/usr/local/bin/pdu-ctrl.sh off rpi4-01' %}
{% set booti_dtb = 'bcm2711-rpi-4-b.dtb' %}
{% set booti_load = '0x80000' %}
{% set tftp_server_ip = '192.168.1.10' %}
{% set nfs_server_ip = '192.168.1.10' %}
{% set nfs_rootfs = '/srv/nfs/rpi4-rootfs' %}
Önemli alanlar
Cihaz tipi şablonu (device-type)
Device dictionary, bir device-type şablonundan türetilir. Linaro, yaygın kartlar için hazır şablonlar sağlar: rpi4b.jinja2, imx8mq.jinja2, qemu.jinja2. Özel kartlar için yeni şablon oluşturulur ve /etc/lava-server/dispatcher-config/device-types/ dizinine yerleştirilir.
Device dictionary içindeki PDU şifreleri ve SSH anahtarları düz metin olarak tutulmamalıdır. LAVA, şifrelenmiş environment variable desteği sunar. Üretim ortamında Vault veya CI secret yönetimi ile entegre edin.
03 Job tanımı — job.yaml yapısı
LAVA job'ları YAML formatında tanımlanır. Bir job üç ana eylem grubundan oluşur: deploy (dosyaları DUT'a aktar), boot (işletim sistemini başlat) ve test (komutları çalıştır, sonuçları değerlendir).
Minimal job.yaml örneği
job_name: kernel-smoke-test-v6.6
device_type: rpi4b
priority: medium
visibility: public
timeouts:
job:
minutes: 30
action:
minutes: 10
connection:
seconds: 60
actions:
- deploy:
to: tftp
kernel:
url: https://storage.example.com/artifacts/Image
type: image
dtb:
url: https://storage.example.com/artifacts/bcm2711-rpi-4-b.dtb
nfsrootfs:
url: https://storage.example.com/rootfs/core-image-minimal.tar.xz
compression: xz
os: oe
- boot:
method: u-boot
commands: nfs
auto_login:
login_prompt: "login:"
username: root
prompts:
- "root@raspberrypi4-64:~#"
timeout:
minutes: 5
- test:
definitions:
- repository: https://git.example.com/lava-tests.git
from: git
path: tests/smoke/smoke.yaml
name: smoke-test
Timeout hiyerarşisi
LAVA'nın timeout sistemi iç içe çalışır: job timeout en dışta, action timeout ortada, connection timeout en içtedir. Bir alt timeout dolduğunda LAVA o eylemi iptal eder ve job'u hata olarak işaretler.
Visibility seçenekleri
Job YAML'ini gönderilmeden önce doğrulamak için lava-run --validate job.yaml komutunu kullanabilirsiniz. Bu komut YAML yapısını ve device-type uyumluluğunu kontrol eder, gerçek donanım erişimi gerektirmez.
04 Deploy stratejileri — tftp, ramdisk, fastboot
LAVA, farklı boot altyapılarına uygun çeşitli deploy yöntemlerini destekler. Hedef cihaz ve ağ yapısına göre uygun strateji seçilmelidir.
tftp + nfs (önerilen)
En yaygın strateji. Kernel ve DTB TFTP üzerinden indirilir, rootfs NFS'e mount edilir. Avantaj: rootfs değişikliği için SD kart yazmak gerekmez, her test run'da temiz rootfs.
- deploy:
to: tftp
kernel:
url: http://artifact-server/Image
type: image
dtb:
url: http://artifact-server/device.dtb
nfsrootfs:
url: http://artifact-server/rootfs.tar.xz
compression: xz
os: oe
ramdisk deploy
Kernel + initramfs birlikte yüklenerek çalıştırılır. NFS altyapısı gerektirmez, ağ izole ortamlar için uygundur. Rootfs boyutu RAM ile sınırlıdır; büyük test ortamları için tftp+nfs tercih edilir.
- deploy:
to: tftp
kernel:
url: http://artifact-server/Image
type: image
dtb:
url: http://artifact-server/device.dtb
ramdisk:
url: http://artifact-server/initramfs.cpio.gz
compression: gz
header: u-boot
os: oe
fastboot deploy
Android veya Qualcomm tabanlı cihazlar için. USB fastboot protokolü kullanılır. Flash bölümlerine imaj yazılır. Ön koşul: DUT fastboot modunda başlamalıdır.
- deploy:
to: fastboot
images:
boot:
url: http://artifact-server/boot.img
system:
url: http://artifact-server/system.img
os: android
Strateji karşılaştırması
| Strateji | Hız | Altyapı gereksinimi | En iyi kullanım |
|---|---|---|---|
| tftp+nfs | Orta | TFTP + NFS sunucu | Gömülü Linux, Yocto |
| ramdisk | Hızlı boot | Yalnızca TFTP | Minimal smoke test |
| fastboot | Yavaş (flash) | USB + fastboot | Android, eMMC flash |
| usb/sd | Çok yavaş | USB mass storage | Air-gapped lab |
05 Boot stratejileri — u-boot, grub, uefi
Boot action, LAVA'nın deploy edilen imajı nasıl başlatacağını tanımlar. Farklı bootloader'lar için farklı boot method'ları kullanılır.
u-boot boot
Gömülü Linux'ta en yaygın senaryo. LAVA, seri konsol üzerinden u-boot prompt'unu yakalar ve commands parametresine göre önceden tanımlanmış bir komut seti gönderir. Device-type şablonu, hangi komutların gönderileceğini belirler.
- boot:
method: u-boot
commands: nfs
auto_login:
login_prompt: "login:"
username: root
password_prompt: "Password:"
password: ""
prompts:
- "root@raspberrypi4:~#"
- "\\$"
timeout:
minutes: 5
# LAVA bu komutları u-boot'a gönderir (device-type şablonundan gelir):
# setenv serverip 192.168.1.10
# setenv bootargs root=/dev/nfs nfsroot=... ip=dhcp console=ttyS0,115200
# tftpboot ${kernel_addr_r} Image
# tftpboot ${fdt_addr_r} bcm2711-rpi-4-b.dtb
# booti ${kernel_addr_r} - ${fdt_addr_r}
grub boot
x86 veya UEFI destekli ARM platformlar için. LAVA, grub menüsünden otomatik olarak doğru menü girişini seçer veya kernel parametrelerini dinamik olarak ayarlar.
- boot:
method: grub
commands: nfs
auto_login:
login_prompt: "login:"
username: root
prompts:
- "#"
timeout:
minutes: 3
UEFI boot
Modern ARM64 sunucu sınıfı kartlar (Ampere, Neoverse N1) için. UEFI shell üzerinden kernel başlatılır. LAVA, UEFI menüsünden önyükleme girişini seçer ya da UEFI shell komutları gönderir.
prompts listesindeki regex'ler dikkatli yazılmalıdır. Çok kısa bir ifade (ör. sadece #) grub veya kernel mesajlarında erken eşleşebilir. root@hostname:~# gibi spesifik bir ifade kullanmak daha güvenlidir. YAML içinde özel karakterler çift ters eğik çizgi ile escape edilmelidir.
auto_login akışı
LAVA boot başlatır
│
▼
Serial konsol izlenir
│
"login:" prompt görülür
│
▼
Kullanıcı adı gönderilir
│
Şifre gerekiyorsa "Password:" beklenir
│
▼
Shell prompt (prompts listesi) beklenir
│
▼
Test phase'e geçilir
06 Test actions — lava-test-shell ve assert
LAVA'nın test altyapısı, DUT üzerinde çalıştırılan komutların çıktısını PASS/FAIL sonuçlarına dönüştürür ve bunları yapılandırılmış biçimde veritabanına kaydeder.
lava-test-shell
lava-test-shell, DUT'ta çalışan küçük bir shell yardımcı programıdır. Test script'leri lava-test-case komutu aracılığıyla sonuç bildirir:
#!/bin/sh
# LAVA test script örneği
# Basit geçer/kalır bildirimi
lava-test-case "kernel-version" --result pass --measurement "$(uname -r)"
# Komut başarıya göre otomatik sonuç
if ping -c 1 8.8.8.8 > /dev/null 2>&1; then
lava-test-case "network-connectivity" --result pass
else
lava-test-case "network-connectivity" --result fail
fi
# Ölçüm ile birlikte
BOOT_SECS=$(awk '{print int($1)}' /proc/uptime)
lava-test-case "boot-time" --result pass \
--measurement "${BOOT_SECS}" --units seconds
# Çoklu alt test döngüsü
for DEV in /dev/mmcblk0 /dev/sda; do
if [ -b "$DEV" ]; then
lava-test-case "storage-$(basename $DEV)" --result pass
fi
done
Inline test tanımı
- test:
timeout:
minutes: 10
definitions:
- from: inline
name: basic-smoke
path: inline/smoke.yaml
repository:
metadata:
format: Lava-Test Test Definition 1.0
name: basic-smoke
description: Temel sistem kontrolleri
run:
steps:
- lava-test-case "uname" --result pass \
--measurement "$(uname -r)"
- |
if [ -e /sys/class/net/eth0 ]; then
lava-test-case "eth0-present" --result pass
else
lava-test-case "eth0-present" --result fail
fi
Git repository test definitions
Büyük test paketleri için test definition'ları Git repository'sinde tutulur. LAVA job'ı bu repository'yi DUT'a çeker ve içindeki YAML'i çalıştırır:
metadata:
format: Lava-Test Test Definition 1.0
name: kernel-smoke
description: Kernel temel sağlık testi
version: "1.0"
run:
steps:
- lava-test-case "kernel-booted" --result pass
- lava-test-case "cpu-online" --result pass \
--measurement "$(nproc)"
- lava-test-case "memory-available" --result pass \
--measurement "$(free -m | awk '/Mem/{print $7}')" --units MB
- ./run-peripherals.sh
Test sonuçları ve raporlama
Tüm test sonuçları LAVA veritabanına kaydedilir. REST API aracılığıyla JSON formatında çekilebilir veya JUnit XML'e dönüştürülerek GitLab/Jenkins'e aktarılabilir. Test suite'ler gruplanarak tarihsel trend analizi yapılabilir.
07 GitLab CI entegrasyonu
LAVA, GitLab CI pipeline'ına lava-tool veya doğrudan REST API kullanılarak entegre edilir. Her push'ta otomatik olarak board farm'da test çalıştırılır ve sonuçlar GitLab'a raporlanır.
Tam .gitlab-ci.yml örneği
stages:
- build
- test-board
build-kernel:
stage: build
image: registry.example.com/cross-toolchain:arm64
script:
- make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
- make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) Image dtbs
artifacts:
paths:
- arch/arm64/boot/Image
- arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dtb
expire_in: 1 day
lava-board-test:
stage: test-board
image: registry.example.com/lava-tools:latest
needs: [build-kernel]
variables:
LAVA_SERVER: "https://lava.example.com"
ARTIFACT_BASE: "${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw"
script:
- |
JOB_ID=$(curl -s -X POST \
-H "Authorization: Token ${LAVA_API_TOKEN}" \
-F "definition=@ci/lava-job.yaml" \
"${LAVA_SERVER}/api/v0.2/jobs/" | jq -r '.job_ids[0]')
echo "LAVA Job ID: ${JOB_ID}"
# Tamamlanmasını bekle
while true; do
STATE=$(curl -s -H "Authorization: Token ${LAVA_API_TOKEN}" \
"${LAVA_SERVER}/api/v0.2/jobs/${JOB_ID}/" | jq -r '.state')
[ "$STATE" = "Finished" ] && break
sleep 20
done
# Sonuçları JUnit XML'e dönüştür
curl -s -H "Authorization: Token ${LAVA_API_TOKEN}" \
"${LAVA_SERVER}/api/v0.2/jobs/${JOB_ID}/junit/" \
-o lava-results.xml
# Hata durumunu kontrol et
HEALTH=$(curl -s -H "Authorization: Token ${LAVA_API_TOKEN}" \
"${LAVA_SERVER}/api/v0.2/jobs/${JOB_ID}/" | jq -r '.health')
[ "$HEALTH" = "Complete" ] || exit 1
artifacts:
reports:
junit: lava-results.xml
when: always
LAVA job şablonu CI'dan parametre alımı
job_name: "GitLab Pipeline #${CI_PIPELINE_ID}"
device_type: rpi4b
priority: medium
visibility: public
timeouts:
job:
minutes: 30
actions:
- deploy:
to: tftp
kernel:
url: "${ARTIFACT_BASE}/arch/arm64/boot/Image"
type: image
dtb:
url: "${ARTIFACT_BASE}/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dtb"
nfsrootfs:
url: "https://storage.example.com/rootfs/latest.tar.xz"
compression: xz
os: oe
- boot:
method: u-boot
commands: nfs
prompts: ["root@raspberrypi4:~#"]
- test:
definitions:
- from: git
repository: https://gitlab.example.com/lava-tests.git
path: tests/regression/regression.yaml
name: regression
08 Pratik: Raspberry Pi 4 Board Farm Kurulumu
Sıfırdan minimal bir Raspberry Pi 4 board farm kurulumu. USB-UART bağlantısı, PDU kontrolü, TFTP sunucusu, LAVA kaydı ve ilk job çalıştırma adımları.
1. Fiziksel bağlantı ve ser2net kurulumu
# ser2net kur
apt-get install ser2net
# Hangi USB-UART portuna bağlandığını bul
dmesg | grep -E "ttyUSB|cp210x|ch341"
# [ 1234.567] usb 1-1.2: cp210x converter now attached to ttyUSB0
# /etc/ser2net.yaml içine bağlantı ekle
cat >> /etc/ser2net.yaml << 'EOF'
connection: &rpi4-01
accepter: tcp,7001
enable: on
options:
kickolduser: true
connector: serialdev,/dev/ttyUSB0,115200n81,local
EOF
systemctl restart ser2net
# Test: seri konsola bağlan
telnet localhost 7001
2. TFTP ve NFS sunucuları
# TFTP
apt-get install tftpd-hpa
cat > /etc/default/tftpd-hpa << 'EOF'
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure --create"
EOF
mkdir -p /srv/tftp && chown tftp:tftp /srv/tftp
systemctl enable --now tftpd-hpa
# NFS
apt-get install nfs-kernel-server
echo "/srv/nfs 192.168.1.0/24(rw,sync,no_root_squash,no_subtree_check)" \
>> /etc/exports
mkdir -p /srv/nfs/rpi4-rootfs
exportfs -ra
systemctl enable --now nfs-kernel-server
3. LAVA'ya cihaz kaydı
# Cihaz tipini ekle
lava-server manage device-types add rpi4b
# Fiziksel cihazı kaydet
lava-server manage devices add \
--device-type rpi4b \
--worker dispatcher01.local \
rpi4-01
# Device dictionary ata
lava-server manage device-dictionary \
--hostname rpi4-01 \
--import /etc/lava-server/dispatcher-config/devices/rpi4-01.jinja2
# Cihazı online al
lava-server manage devices update rpi4-01 --health GOOD
# Durum kontrolü
lava-server manage devices list
4. İlk job ve doğrulama
# lava-tool kimlik doğrulama
lava-tool auth-add https://lava.example.com \
--username admin --token $(cat /etc/lava-server/api-token)
# Job gönder
JOB_ID=$(lava-tool submit-job https://lava.example.com smoke-job.yaml)
echo "Job ID: $JOB_ID"
# Tamamlanmasını bekle
lava-tool wait-for-job https://lava.example.com $JOB_ID
# Sonuçları listele
lava-tool results https://lava.example.com $JOB_ID
# Web UI: https://lava.example.com/scheduler/job/$JOB_ID/
İlk kurulumda en sık yaşanan sorunlar: (1) TFTP transferinde timeout — firewall UDP/69'u engelliyor olabilir; (2) u-boot prompt'unun kaçırılması — autoboot_interrupt süresini device dictionary'de artırın; (3) NFS mount hatası — no_root_squash seçeneği eksik olabilir. Her durumda LAVA job log'undaki serial output bölümü en doğru tanı kaynağıdır.