From 5a7382185b89e09211ac90ac86a7781df4d3de57 Mon Sep 17 00:00:00 2001 From: lgcosta Date: Thu, 12 Nov 2026 00:11:31 -0300 Subject: [PATCH] Add a libvirt/KVM test environment and fix the gcc-12 warning it found The VM harness (tests/vm/) builds and exercises the tools on real Debian and Alpine guests, driving the serial console (no sshd/cloud-init dependency). Results on Debian 12 (glibc 2.36, gcc 12.2): 12/12 checks pass, 0 warnings with -Wall -Wextra, including a real 128M -> 256M resize and the rollback save/restore/reject paths. Results on Alpine 3.20 (musl 1.2.5): the build cannot start. configure stops at "reiserfsprogs requires a method to add printf modifiers" because reiserfscore/prints.c depends on glibc's register_printf_specifier(3) and , which musl does not provide. This is pre-existing and out of scope here; measured at 341 use sites across 20+ files, all routed through reiserfs_warning()/reiserfs_info(). Documented in tests/vm/RESULTS.md. Fix the one real defect the harness surfaced: fsck/semantic_check.c: check_check_regular_file() is called with NULL for new_ih (line 466), so the block that would fill *new_ih is unreachable and check_semantic_pass() never returns RELOCATED through that path. The "case RELOCATED:" arm still reads relocated_ih.ih_key, which gcc 12.2 reports as "may be used uninitialized" (-Wmaybe-uninitialized); gcc 16 does not, because it proves the path dead. Zero-initialise the struct and say why. Verified: 0 warnings on gcc 12.2 after the change, and the Debian battery stays at 12/12. --- fsck/semantic_check.c | 5 +- tests/vm/README.md | 85 +++++++++++++++++++++++++ tests/vm/RESULTS.md | 124 ++++++++++++++++++++++++++++++++++++ tests/vm/guestcmd.py | 126 ++++++++++++++++++++++++++++++++++++ tests/vm/run_in_vm.sh | 51 +++++++++++++++ tests/vm/run_tests.sh | 134 +++++++++++++++++++++++++++++++++++++++ tests/vm/setup_env.sh | 91 ++++++++++++++++++++++++++ tests/vm/teardown_env.sh | 13 ++++ 8 files changed, 628 insertions(+), 1 deletion(-) create mode 100644 tests/vm/README.md create mode 100644 tests/vm/RESULTS.md create mode 100755 tests/vm/guestcmd.py create mode 100755 tests/vm/run_in_vm.sh create mode 100755 tests/vm/run_tests.sh create mode 100755 tests/vm/setup_env.sh create mode 100755 tests/vm/teardown_env.sh diff --git a/fsck/semantic_check.c b/fsck/semantic_check.c index b168456..d7cf9e1 100644 --- a/fsck/semantic_check.c +++ b/fsck/semantic_check.c @@ -551,7 +551,10 @@ static int check_semantic_pass(struct reiserfs_key *key, for (i = pos_in_item; i < get_ih_entry_count(&tmp_ih); i++, deh++) { - struct item_head relocated_ih; + /* Only meaningful when check_check_regular_file() returns + RELOCATED; that path currently passes NULL for new_ih, + so this stays zero-initialised. */ + struct item_head relocated_ih = { 0 }; int ret = OK; if (name) { diff --git a/tests/vm/README.md b/tests/vm/README.md new file mode 100644 index 0000000..40029d1 --- /dev/null +++ b/tests/vm/README.md @@ -0,0 +1,85 @@ +# Ambiente de testes em VM + +Este documento descreve o ambiente de validação do reiserfsprogs em máquinas +virtuais (libvirt/KVM), usado para exercitar o build e as ferramentas em +distribuições diferentes da máquina de desenvolvimento. + +O ambiente é **efêmero**: as VMs ficam em `/var/tmp/vmtest` e não fazem parte +do repositório. Os scripts que o reproduzem estão em `tests/vm/`. + +## Requisitos do host + +| Componente | Verificação | +|---|---| +| KVM | `ls -l /dev/kvm` | +| libvirt (user session) | `virsh -c qemu:///session list --all` | +| `virt-install`, `qemu-img` | `which virt-install qemu-img` | +| `guestfs-tools` (offline) | `which virt-customize` | +| `mkisofs` ou `xorriso` | `which mkisofs xorriso` | + +No Fedora: `sudo dnf install libvirt-daemon-kvm qemu-kvm virt-install +guestfs-tools genisoimage`. + +O ambiente usa **qemu:///session** (sem root). Não requer a rede `default` +do libvirt: a conectividade vem do backend `passt` em modo *user*. + +## Topologia + +```mermaid +graph LR + H[host] -->|qemu:///session| D[rfs-debian
Debian 12 · gcc] + H -->|qemu:///session| A[rfs-alpine
Alpine 3.20 · gcc/musl] + D -->|passt :2222| S[ssh opcional] + A -->|passt| S + H -->|attach-disk| V[vdb: fontes + run_tests.sh] +``` + +Cada VM recebe: + +* imagem cloud (qcow2) redimensionada para 8 GiB; +* um disco de dados `vdb` (ext4) com `rfs-src.tar.gz` e `run_tests.sh`; +* acesso por **console serial** (pty do libvirt), que é o canal de + automação usado — independente de rede. + +## Por que console serial e não SSH + +Nas imagens cloud o `sshd` demora a subir e o `cloud-init` nem sempre aplica +a senha (o datasource NoCloud exige rótulo `cidata` e o CD precisa estar no +Barramento certo). O console serial está disponível assim que o getty sobe e +é suficiente para: autenticar, copiar arquivos (já estão no `vdb`) e rodar +qualquer comando. Por isso o `tests/vm/guestcmd.py` dirige o pty. + +## Ciclo de uso + +```sh +cd tests/vm + +# 1. preparar imagens e VMs (uma vez) +./setup_env.sh + +# 2. rodar a bateria em uma VM +./run_in_vm.sh rfs-alpine +./run_in_vm.sh rfs-debian + +# 3. destruir tudo +./teardown_env.sh +``` + +## O que a bateria cobre (`tests/vm/run_tests.sh`) + +Executado **dentro** do guest, sobre o código extraído do `vdb`: + +1. `autogen.sh && configure --enable-warnings && make -j` — build limpo; +2. contagem de warnings (esperado: 0); +3. `mkreiserfs` em imagem de 64 MiB; +4. `reiserfsck --check` (esperado: *No corruptions found*); +5. `debugreiserfs -S` (superblock consistente); +6. `reiserfstune -l` (troca de label); +7. `resize_reiserfs` 128 MiB → 256 MiB; +8. `reiserfsck` pós-resize; +9. rollback: `-R` grava, `--rollback-fsck-changes` restaura, arquivo + inválido é rejeitado. + +## Resultados obtidos + +Ver `tests/vm/RESULTS.md`. \ No newline at end of file diff --git a/tests/vm/RESULTS.md b/tests/vm/RESULTS.md new file mode 100644 index 0000000..856397a --- /dev/null +++ b/tests/vm/RESULTS.md @@ -0,0 +1,124 @@ +# Resultados dos testes em VM + +Bateria executada com `tests/vm/run_in_vm.sh` em duas distribuições, ambas em +KVM (`qemu:///session`), com o código de `293e6a7`. + +## Ambiente + +| | Debian 12 (bookworm) | Alpine 3.20.3 | +|---|---|---| +| Kernel | 6.1.0-54-cloud-amd64 | 6.6.49-0-virt | +| libc | glibc 2.36 | **musl 1.2.5** | +| gcc | 12.2.0 | 13.2.1 | +| init | systemd + cloud-init | OpenRC + cloud-init | + +## Resultado por item + +| # | Teste | Debian | Alpine | +|---|---|---|---| +| 1 | `autogen.sh` | passa | passa | +| 1 | `configure --enable-warnings` | passa | **falha** (ver abaixo) | +| 1 | `make -j` | passa, **0 warnings** | não alcançado | +| 2 | `mkreiserfs` | passa | não alcançado | +| 3 | `reiserfsck --check` | passa | não alcançado | +| 4 | `debugreiserfs -S` | passa | não alcançado | +| 5 | `reiserfstune -l` | passa | não alcançado | +| 6 | `resize_reiserfs` 128→256 MiB | passa | não alcançado | +| 7 | `fsck` pós-resize | passa | não alcançado | +| 8 | rollback: grava | passa | não alcançado | +| 8 | rollback: restaura | passa | não alcançado | +| 8 | rollback: magic inválida rejeitada | passa | não alcançado | +| 8 | rollback: arquivo inexistente rejeitado | passa | não alcançado | + +**Debian: 12/12.** Alpine: bloqueado no item 1. + +## Achado 1 (bloqueante): não compila em musl + +O `configure` do Alpine falha com: + +``` +checking for register_printf_modifier... no +checking for register_printf_specifier... no +configure: error: reiserfsprogs requires a method to add printf modifiers +``` + +Causa: `reiserfscore/prints.c` usa `register_printf_specifier(3)`, extensão +**glibc-only** (``). O musl não fornece `printf.h` nem essas +funções. O `configure.ac` trata isso como erro fatal explícito — o +comportamento é correto, mas o projeto **não compila fora da glibc**. + +Alcance medido: + +| Especificador | Uso | +|---|---| +| `%k` | 139 | +| `%K` | 106 | +| `%H` | 52 | +| `%M` | 25 | +| `%b` | 9 | +| `%U` | 4 | +| `%y` | 2 | +| `%30H`, `%20y`, `%20k`, `%20K` | 4 | +| **total** | **341** | + +Todos passam por `reiserfs_warning()` / `reiserfs_info()`. Portar exigiria +substituir o mecanismo por funções de formatação explícitas (por exemplo +`char *format_key(const struct reiserfs_key *)` com `%s`), uma mudança de API +interna ampla — não um ajuste local. + +**Impacto prático**: reiserfsprogs fica restrito à glibc. Como o único uso +atual da ferramenta é resgate de dados, isso importa: live images de +recuperação baseadas em Alpine/musl não conseguem compilar o projeto. + +## Achado 2 (corrigido nesta rodada): warning real no gcc 12 + +O gcc 12.2 do Debian acusou, duas vezes: + +``` +../include/reiserfs_fs.h:63:54: warning: ‘relocated_ih’ may be used uninitialized +``` + +O gcc 16 do host não acusa. Investigação: + +* `fsck/semantic_check.c:620` passa `&relocated_ih` para + `check_semantic_pass()`; +* o bloco que escreveria `*new_ih` está **comentado** (linhas 471–486); +* a chamada em `check_check_regular_file()` (linha 466) passa + `NULL` como `new_ih`, com o comentário `/* relocate ? new_ih : */ NULL`; +* portanto `RELOCATED` nunca é retornado por esse caminho e + `case RELOCATED:` (linha 665) leria `relocated_ih` não inicializado. + +Não é alcançável hoje, mas é uma leitura potencial de lixo de pilha atrás de +um `#case` que o compilador não consegue descartar. Correção: inicialização +explícita `= { 0 }` com comentário explicando por que permanece zero. + +Verificado: **0 warnings no gcc 12.2 após a correção**. + +Em `fsck/semantic_rebuild.c` o caminho equivalente é seguro: o bloco +`if (relocate) { ... *new_ih = *ih; retval = RELOCATED; }` está ativo +(linhas 808–822), então `new_ih` é escrito antes do `case RELOCATED:` +(linha 999). + +## Achado 3: limitação do ambiente (não do código) + +Nas imagens cloud o `cloud-init` não aplicou a senha do usuário: o datasource +NoCloud exige o rótulo `cidata` e o CD correto, e o `sshd` demora a subir. +O console serial, porém, fica disponível assim que o getty sobe — por isso o +`tests/vm/guestcmd.py` dirige o pty e é o canal de automação usado. Isso não +afeta o produto; afeta apenas como os testes são conduzidos. + +Além disso, a rede user-mode (`passt`) não entrega DHCP nem DNS por padrão: a +interface sobe como `enp1s0` mas fica sem IP. Foi necessário configurar IP e +resolver manualmente para instalar pacotes nas VMs. + +## Como reproduzir + +```sh +cd tests/vm +./setup_env.sh # imagens + VMs (uma vez) +./run_in_vm.sh rfs-debian # build + bateria completa +./run_in_vm.sh rfs-alpine # deve falhar no configure (esperado) +./teardown_env.sh +``` + +Detalhes do ambiente, topologia e limitações: `tests/vm/README.md`. \ No newline at end of file diff --git a/tests/vm/guestcmd.py b/tests/vm/guestcmd.py new file mode 100755 index 0000000..018f4fc --- /dev/null +++ b/tests/vm/guestcmd.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +"""Roda um comando no guest atraves do console serial do libvirt. + +O console serial e o unico canal garantido nas imagens cloud minimas: nao +depende de sshd, de cloud-init nem de rede. Este script sincroniza com o +prompt, autentica (root/rfstest) e executa o comando, lendo a saida do pty +em blocos pequenos para evitar a sobreposicao de eco que ocorre em comandos +longos. + +Uso: + guestcmd.py '' + guestcmd.py -f # executa um script local +""" +import base64 +import os +import re +import select +import subprocess +import sys +import time + +USER = "root" +PASSWORD = "rfstest" + + +def console_pty(domain): + xml = subprocess.run(["virsh", "dumpxml", domain], + capture_output=True, text=True).stdout + m = (re.search(r"]*>\s*]*>\s* {out_path} 2>&1; " + f"echo __GC_DONE__\n") + deadline = time.time() + timeout + while time.time() < deadline: + if b"__GC_DONE__" in self.drain(2.0): + break + else: + return "(timeout)\n" + + # tamanho em bytes + self.send(f"wc -c < {out_path}\n") + time.sleep(1.2) + raw = self.drain(2.0).decode(errors="replace") + nums = re.findall(r"\d+", raw) + size = int(nums[-1]) if nums else 0 + + # ler em blocos base64 (evita eco/sobreposicao) + data = b"" + while len(data) < size: + self.send(f"dd if={out_path} bs=1 skip={len(data)} count=150 " + f"2>/dev/null | base64 -w0; echo\n") + time.sleep(0.8) + enc = self.drain(2.0).decode(errors="replace") + token = "".join(re.findall(r"[A-Za-z0-9+/=]{4,}", enc)) + if not token: + break + try: + data += base64.b64decode(token + "=" * (-len(token) % 4)) + except Exception: + break + return data.decode(errors="replace") + + +def main(): + if len(sys.argv) < 3: + sys.exit(__doc__) + domain = sys.argv[1] + console = Console(console_pty(domain)) + console.login() + + if sys.argv[2] == "-f": + with open(sys.argv[3]) as fh: + script = fh.read().replace("'", "'\\''") + command = f"sh -c '{script}'" + else: + command = sys.argv[2] + + sys.stdout.write(console.run(command)) + sys.stdout.write("\n") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tests/vm/run_in_vm.sh b/tests/vm/run_in_vm.sh new file mode 100755 index 0000000..331ab48 --- /dev/null +++ b/tests/vm/run_in_vm.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# Envia o codigo-fonte e o script de teste para dentro de uma VM e executa. +# +# Uso: run_in_vm.sh +# Requer: guestcmd.py (mesmo diretorio), mkisofs, libvirt. +set -e + +DOMAIN=${1:?uso: run_in_vm.sh } +HERE=$(cd "$(dirname "$0")" && pwd) +REPO=$(cd "$HERE/../.." && pwd) +WORK=${WORK:-/var/tmp/vmtest} + +echo "== empacotando o codigo-fonte ==" +tar czf "$WORK/rfs-src.tar.gz" -C "$REPO" \ + --exclude=.git --exclude='*.o' --exclude='*.lo' --exclude='*.la' \ + --exclude='.libs' --exclude=build-aux --exclude=m4 \ + --exclude=autom4te.cache --exclude=config.h --exclude=config.log \ + --exclude=config.status --exclude=libtool \ + --exclude='Makefile' --exclude='Makefile.in' --exclude=configure \ + --exclude=aclocal.m4 --exclude='*.8' \ + . + +echo "== montando o disco de dados ==" +rm -rf /tmp/vmdata.$$ && mkdir -p /tmp/vmdata.$$ +sudo mount -o loop "$WORK/data.img" /tmp/vmdata.$$ +sudo cp "$WORK/rfs-src.tar.gz" "$HERE/run_tests.sh" /tmp/vmdata.$$ +sudo umount /tmp/vmdata.$$ && rmdir /tmp/vmdata.$$ + +echo "== transferindo para $DOMAIN ==" +ATTACH=/tmp/vdb.$$.xml +cat > "$ATTACH" < + + + + +EOF +virsh attach-device "$DOMAIN" "$ATTACH" --live >/dev/null 2>&1 || true +rm -f "$ATTACH" + +echo "== extraindo e rodando os testes ==" +"$HERE/guestcmd.py" "$DOMAIN" ' +set -e +mkdir -p /mnt/data +mountpoint -q /mnt/data || mount -o ro /dev/vdb /mnt/data +mkdir -p /root/rfs +tar xzf /mnt/data/rfs-src.tar.gz -C /root/rfs +cp /mnt/data/run_tests.sh /root/run_tests.sh +chmod +x /root/run_tests.sh +SRC=/root/rfs sh /root/run_tests.sh +' 900 \ No newline at end of file diff --git a/tests/vm/run_tests.sh b/tests/vm/run_tests.sh new file mode 100755 index 0000000..c54f71d --- /dev/null +++ b/tests/vm/run_tests.sh @@ -0,0 +1,134 @@ +#!/bin/sh +# Bateria de testes do reiserfsprogs, executada DENTRO do guest. +# Espera receber o codigo-fonte em $SRC (padrao: /root/rfs). +# +# Uso (dentro da VM): +# SRC=/root/rfs sh run_tests.sh +set -u + +SRC=${SRC:-/root/rfs} +PASS=0 +FAIL=0 +ok() { echo "PASS: $1"; PASS=$((PASS + 1)); } +bad() { echo "FAIL: $1"; FAIL=$((FAIL + 1)); } + +cd "$SRC" || { echo "FAIL: $SRC nao existe"; exit 1; } + +echo "=== ambiente ===" +uname -srm +(cc --version 2>/dev/null || echo "sem cc") | head -1 +ldd --version 2>&1 | head -1 + +echo +echo "=== 1. bootstrap + build com -Wall -Wextra ===" +make distclean >/dev/null 2>&1 +if ./autogen.sh >/tmp/ag.log 2>&1 && ./configure --enable-warnings >/tmp/cfg.log 2>&1 \ + && make -j"$(nproc 2>/dev/null || echo 2)" >/tmp/mk.log 2>&1; then + ok "autogen.sh + configure + make" +else + bad "build" + echo "--- configure ---"; tail -15 /tmp/cfg.log + echo "--- make ---"; tail -25 /tmp/mk.log + echo + echo "RESULTADO: $PASS passaram, $FAIL falharam" + exit 1 +fi + +# grep -c retorna 1 quando nao ha ocorrencias; capturar so a primeira linha +WARN=$(grep -c "warning:" /tmp/mk.log 2>/dev/null | head -1) +WARN=${WARN:-0} +if [ "$WARN" = "0" ]; then + ok "0 warnings com -Wall -Wextra" +else + bad "$WARN warnings" + grep "warning:" /tmp/mk.log | head -10 +fi + +echo +echo "=== 2. mkfs ===" +rm -f /tmp/t.img +dd if=/dev/zero of=/tmp/t.img bs=1M count=64 status=none 2>/dev/null +if ./mkreiserfs/mkreiserfs -f -q -l TESTVOL /tmp/t.img >/dev/null 2>&1; then + ok "mkreiserfs" +else + bad "mkreiserfs" +fi + +echo +echo "=== 3. reiserfsck --check ===" +if ./fsck/reiserfsck --check -f -q -y /tmp/t.img 2>&1 | grep -q "No corruptions found"; then + ok "reiserfsck --check" +else + bad "reiserfsck --check" +fi + +echo +echo "=== 4. debugreiserfs -S ===" +if ./debugreiserfs/debugreiserfs -S /tmp/t.img 2>&1 | grep -q "Filesystem state: consistent"; then + ok "debugreiserfs -S" +else + bad "debugreiserfs -S" +fi + +echo +echo "=== 5. reiserfstune -l ===" +if ./tune/reiserfstune -l NEWLABEL -f /tmp/t.img >/dev/null 2>&1; then + ok "reiserfstune -l" +else + bad "reiserfstune -l" +fi + +echo +echo "=== 6. resize 128M -> 256M ===" +rm -f /tmp/big.img +dd if=/dev/zero of=/tmp/big.img bs=1M count=128 status=none 2>/dev/null +./mkreiserfs/mkreiserfs -f -q /tmp/big.img >/dev/null 2>&1 +truncate -s 256M /tmp/big.img +if ./resize_reiserfs/resize_reiserfs -s +128M -f /tmp/big.img 2>&1 \ + | grep -q "Resizing finished successfully"; then + ok "resize_reiserfs" +else + bad "resize_reiserfs" +fi + +echo +echo "=== 7. fsck pos-resize ===" +if ./fsck/reiserfsck --check -f -q -y /tmp/big.img 2>&1 | grep -q "No corruptions found"; then + ok "fsck apos resize" +else + bad "fsck apos resize" +fi + +echo +echo "=== 8. rollback (save/restore/reject) ===" +cp /tmp/t.img /tmp/rb.img +rm -f /tmp/rb.dat +./fsck/reiserfsck --fix-fixable -y -q -R /tmp/rb.dat /tmp/rb.img >/dev/null 2>&1 +if [ -f /tmp/rb.dat ]; then + ok "rollback file criado" +else + bad "rollback file nao criado" +fi +if ./fsck/reiserfsck --rollback-fsck-changes -y -q -R /tmp/rb.dat /tmp/rb.img 2>&1 \ + | grep -q "blocks restored"; then + ok "rollback-fsck-changes" +else + bad "rollback-fsck-changes" +fi +head -c 100 /dev/urandom > /tmp/rollback-invalido.dat +if ./fsck/reiserfsck --rollback-fsck-changes -y -q -R /tmp/rollback-invalido.dat /tmp/rb.img 2>&1 \ + | grep -q "does not look like a rollback file"; then + ok "rollback com magic invalida rejeitado" +else + bad "rollback com magic invalida aceito" +fi +if ./fsck/reiserfsck --rollback-fsck-changes -y -q -R /tmp/nao-existe.dat /tmp/rb.img 2>&1 \ + | grep -q "Cannot stat rollback file"; then + ok "rollback inexistente rejeitado" +else + bad "rollback inexistente aceito" +fi + +echo +echo "RESULTADO: $PASS passaram, $FAIL falharam" +[ "$FAIL" -eq 0 ] \ No newline at end of file diff --git a/tests/vm/setup_env.sh b/tests/vm/setup_env.sh new file mode 100755 index 0000000..f082074 --- /dev/null +++ b/tests/vm/setup_env.sh @@ -0,0 +1,91 @@ +#!/bin/sh +# Prepara o ambiente de VMs: imagens, seed e maquinas virtuais. +# Requer: libvirt (qemu:///session), virt-install, qemu-img, mkisofs. +set -e + +HERE=$(cd "$(dirname "$0")" && pwd) +WORK=${WORK:-/var/tmp/vmtest} +CONN=qemu:///session + +mkdir -p "$WORK" +cd "$WORK" + +fetch() { + url=$1; out=$2 + [ -s "$out" ] && { echo " $out ja existe"; return; } + echo " baixando $out" + curl -fsSL -o "$out.part" "$url" + mv "$out.part" "$out" +} + +echo "== imagens ==" +fetch https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2 debian.qcow2 +fetch https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/cloud/nocloud_alpine-3.20.3-x86_64-bios-cloudinit-r0.qcow2 alpine.qcow2 + +echo "== redimensionando ==" +for img in debian.qcow2 alpine.qcow2; do + size=$(qemu-img info --output=json "$img" | grep -oP '"virtual-size":\s*\K[0-9]+') + if [ "${size:-0}" -lt 8589934592 ]; then + qemu-img resize "$img" 8G + else + echo " $img ja tem 8G" + fi +done + +echo "== disco de dados (fontes + script de teste) ==" +if [ ! -s data.img ]; then + dd if=/dev/zero of=data.img bs=1M count=8 status=none + mkfs.ext4 -q -F data.img +fi + +echo "== seed cloud-init ==" +cat > user-data <<'EOF' +#cloud-config +hostname: rfs-test +ssh_pwauth: true +disable_root: false +users: + - name: root + lock_passwd: false + plain_text_passwd: rfstest +chpasswd: + expire: false +EOF +cat > meta-data <<'EOF' +instance-id: rfs-test-1 +local-hostname: rfs-test +EOF +cat > network-config <<'EOF' +version: 2 +ethernets: + eth0: + dhcp4: true +EOF +rm -f seed.iso +mkisofs -output seed.iso -volid cidata -joliet -rock \ + user-data meta-data network-config >/dev/null 2>&1 + +echo "== definindo VMs ==" +define_vm() { + name=$1; img=$2; variant=$3; mem=$4 + if virsh -c "$CONN" dominfo "$name" >/dev/null 2>&1; then + echo " $name ja existe" + return + fi + virt-install -c "$CONN" \ + --name "$name" \ + --memory "$mem" --vcpus 2 \ + --disk "path=$WORK/$img,format=qcow2,bus=virtio" \ + --disk "path=$WORK/seed.iso,device=cdrom" \ + --os-variant "$variant" \ + --network user,model=virtio \ + --graphics none --console pty \ + --import --noautoconsole >/dev/null + echo " criada: $name" +} + +define_vm rfs-debian debian.qcow2 debian12 2048 +define_vm rfs-alpine alpine.qcow2 alpinelinux3.20 1024 + +echo +echo "Pronto. Use tests/vm/run_in_vm.sh para testar." \ No newline at end of file diff --git a/tests/vm/teardown_env.sh b/tests/vm/teardown_env.sh new file mode 100755 index 0000000..3fbb64a --- /dev/null +++ b/tests/vm/teardown_env.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# Destrói as VMs do ambiente de teste. Nao remove as imagens baixadas. +set -e +CONN=qemu:///session +for d in rfs-debian rfs-alpine; do + if virsh -c "$CONN" dominfo "$d" >/dev/null 2>&1; then + virsh -c "$CONN" destroy "$d" >/dev/null 2>&1 || true + virsh -c "$CONN" undefine "$d" --remove-all-storage >/dev/null 2>&1 || true + echo "removida: $d" + else + echo "ausente: $d" + fi +done \ No newline at end of file