make it work

This commit is contained in:
Cat /dev/Nulo 2023-04-05 17:25:45 -03:00
parent c6cd179efd
commit eb0a9d937d
7 changed files with 244 additions and 573 deletions

7
.gitignore vendored
View File

@ -1,6 +1,7 @@
artifacts
node_modules
index.js
index.js.map
*.ext4
*.qcow2
*.qcow2
.env
initramfs-virt
vmlinuz-virt

199
index.js Normal file
View File

@ -0,0 +1,199 @@
// @ts-check
import { nanoid } from "nanoid";
import { execFile as execFileCallback } from "node:child_process";
import {
constants,
copyFile,
mkdir,
opendir,
readdir,
rm,
symlink,
writeFile,
} from "node:fs/promises";
import { tmpdir } from "node:os";
import { basename, join } from "node:path";
import { promisify } from "node:util";
import * as dotenv from "dotenv";
dotenv.config();
const execFile = promisify(execFileCallback);
// TODO: configurar runsc
// TODO: cache de apk (robar de define-alpine-the-sequel)
const currRootPath = await setupRootfsDir();
await setupBasicApkEnvironment(currRootPath);
const packages = ["alpine-base", "fish", "docker", "linux-virt"];
await execFile("doas", [
"apk",
"--initdb",
"--clean-protected",
"--root",
currRootPath,
"add",
...packages,
]);
await chroot(currRootPath, ["rc-update", "add", "networking", "sysinit"]);
await chroot(currRootPath, ["rc-update", "add", "docker", "boot"]);
await chroot(currRootPath, ["rc-update", "add", "acpid", "default"]);
await chroot(currRootPath, ["rc-update", "add", "local", "default"]);
await writeFile(
"interfaces",
`auto lo
iface lo inet loopback
auto eth0
iface eth0
use dhcp`,
{ mode: 0o600 }
);
await execFile("doas", [
"mv",
"interfaces",
join(currRootPath, "/etc/network/interfaces"),
]);
await execFile("doas", [
"chown",
"0:0",
join(currRootPath, "/etc/network/interfaces"),
]);
const woodpeckerSecret = getConfig("WOODPECKER_SECRET");
const woodpeckerServer = getConfig("WOODPECKER_SERVER");
await writeFile(
"woodpecker.start",
`#!/bin/sh
docker pull docker.io/woodpeckerci/woodpecker-agent:latest || exit $?
exec docker run --detach --rm \
--name=woodpecker-agent \
-v /var/run/docker.sock:/var/run/docker.sock \
-e WOODPECKER_SERVER='${woodpeckerServer}' \
-e WOODPECKER_SECRET='${woodpeckerSecret}' \
-e WOODPECKER_MAX_PROCS=8 \
docker.io/woodpeckerci/woodpecker-agent:latest \
agent`,
{ mode: 0o700 }
);
await execFile("doas", [
"mv",
"woodpecker.start",
join(currRootPath, "/etc/local.d/woodpecker.start"),
]);
await execFile("doas", [
"chown",
"0:0",
join(currRootPath, "/etc/local.d/woodpecker.start"),
]);
await chroot(currRootPath, ["rc-update", "add", "localmount", "boot"]);
await writeFstab(currRootPath);
// make VM disk image
const ext4Path = "raw.ext4";
await execFile("qemu-img", ["create", "-f", "raw", ext4Path, "50G"]);
await execFile("mkfs.ext4", [ext4Path]);
const mntdir = join(tmpdir(), "woodpecker-in-a-vm-" + nanoid());
await mkdir(mntdir);
await execFile("doas", ["mount", ext4Path, mntdir]);
for (const path of await readdir(currRootPath))
await execFile("doas", ["cp", "-r", join(currRootPath, path), mntdir]);
await execFile("doas", ["umount", mntdir]);
await execFile("doas", [
"qemu-img",
"convert",
"-O",
"qcow2",
ext4Path,
"vm.qcow2",
]);
await copyFile(
join(currRootPath, "boot/vmlinuz-virt"),
"./vmlinuz-virt",
constants.COPYFILE_FICLONE
);
await copyFile(
join(currRootPath, "boot/initramfs-virt"),
"./initramfs-virt",
constants.COPYFILE_FICLONE
);
await execFile("doas", [
"chown",
"1000:1000",
"./vmlinuz-virt",
"./initramfs-virt",
"./vm.qcow2",
]);
async function setupRootfsDir() {
const artifactsPath = "./artifacts";
const currRootPath = join(artifactsPath, "rootfs" + nanoid());
await mkdir(currRootPath, { recursive: true });
{
const currentPath = join(artifactsPath, "current");
try {
await rm(currentPath);
} catch {}
await symlink(basename(currRootPath), currentPath);
}
return currRootPath;
}
/**
* @param {string} rootfs
*/
async function setupBasicApkEnvironment(rootfs) {
{
const apkKeysDir = join(rootfs, "/etc/apk/keys");
const keysSrcDir = "alpine/keys";
await mkdir(apkKeysDir, { recursive: true });
for await (const { name } of await opendir(keysSrcDir))
await copyFile(join(keysSrcDir, name), join(apkKeysDir, name));
}
await writeFile(
join(rootfs, "/etc/apk/repositories"),
[
// "https://dl-cdn.alpinelinux.org/alpine/v3.17/main",
// "https://dl-cdn.alpinelinux.org/alpine/v3.17/community",
"http://alpinelinux.c3sl.ufpr.br/v3.17/main",
"http://alpinelinux.c3sl.ufpr.br/v3.17/community",
].join("\n")
);
}
/**
* @param {string} rootfs
*/
async function writeFstab(rootfs) {
const virtualDiskName = "/dev/vda";
await writeFile(
"fstab",
`${virtualDiskName} / ext4 rw,relatime,discard 0 1
`
);
await execFile("doas", ["mv", "fstab", join(rootfs, "/etc/fstab")]);
}
/**
* Corre comando en chroot
* @param {string} rootfs
* @param {string[]} cmd
*/
async function chroot(rootfs, cmd) {
await execFile("doas", ["chroot", rootfs, ...cmd]);
}
/**
* @param {string} name
* @returns {string} la variable conseguida
*/
function getConfig(name) {
if (name in process.env) return process.env[name] || "";
else throw new Error(`Falta la variable ${name}`);
}

View File

@ -1,96 +0,0 @@
import { nanoid } from "nanoid";
import { execFile as execFileCallback } from "node:child_process";
import {
copyFile,
mkdir,
opendir,
readdir,
rm,
symlink,
writeFile,
} from "node:fs/promises";
import { tmpdir } from "node:os";
import { basename, join } from "node:path";
import { promisify } from "node:util";
const execFile = promisify(execFileCallback);
// TODO: configurar runsc
// TODO: cache de apk (robar de define-alpine-the-sequel)
const currRootPath = await setupRootfsDir();
await setupBasicApkEnvironment(currRootPath);
const packages = ["alpine-base", "fish", "docker", "linux-virt"];
await execFile("doas", [
"apk",
"--initdb",
"--clean-protected",
"--root",
currRootPath,
"add",
...packages,
]);
await chroot(currRootPath, ["rc-update", "add", "docker", "default"]);
await chroot(currRootPath, ["rc-update", "add", "localmount", "boot"]);
await writeFstab(currRootPath);
// make VM disk image
const ext4Path = "raw.ext4";
await execFile("qemu-img", ["create", "-f", "raw", ext4Path, "50G"]);
await execFile("mkfs.ext4", [ext4Path]);
const mntdir = join(tmpdir(), "woodpecker-in-a-vm-" + nanoid());
await mkdir(mntdir);
await execFile("doas", ["mount", ext4Path, mntdir]);
for (const path of await readdir(currRootPath))
await execFile("doas", ["cp", "-r", join(currRootPath, path), mntdir]);
await execFile("doas", ["umount", mntdir]);
await execFile("qemu-img", ["convert", "-O", "qcow2", ext4Path, "vm.qcow2"]);
async function setupRootfsDir() {
const artifactsPath = "./artifacts";
const currRootPath = join(artifactsPath, "rootfs" + nanoid());
await mkdir(currRootPath, { recursive: true });
{
const currentPath = join(artifactsPath, "current");
try {
await rm(currentPath);
} catch {}
await symlink(basename(currRootPath), currentPath);
}
return currRootPath;
}
async function setupBasicApkEnvironment(rootfs: string) {
{
const apkKeysDir = join(rootfs, "/etc/apk/keys");
const keysSrcDir = "alpine/keys";
await mkdir(apkKeysDir, { recursive: true });
for await (const { name } of await opendir(keysSrcDir))
await copyFile(join(keysSrcDir, name), join(apkKeysDir, name));
}
await writeFile(
join(rootfs, "/etc/apk/repositories"),
[
"https://dl-cdn.alpinelinux.org/alpine/v3.17/main",
"https://dl-cdn.alpinelinux.org/alpine/v3.17/community",
].join("\n")
);
}
async function writeFstab(rootfs: string) {
const virtualDiskName = "/dev/vda";
await writeFile(
"fstab",
`${virtualDiskName} / ext4 rw,relatime,discard 0 1
`
);
await execFile("doas", ["mv", "fstab", join(rootfs, "/etc/fstab")]);
}
async function chroot(rootfs: string, cmd: string[]) {
await execFile("doas", ["chroot", rootfs, ...cmd]);
}

View File

@ -1,14 +1,13 @@
{
"type": "module",
"scripts": {
"run": "esbuild --log-level=warning --target=node18 --platform=node --sourcemap --outdir=. --format=esm --bundle index.ts && node --enable-source-maps index.js"
"run": "node index.js"
},
"devDependencies": {
"@types/node": "^18.15.5",
"esbuild": "^0.17.12",
"typescript": "^5.0.2"
"@types/node": "^18.15.5"
},
"dependencies": {
"dotenv": "^16.0.3",
"nanoid": "^4.0.1"
}
}

View File

@ -1,261 +1,31 @@
lockfileVersion: 5.4
specifiers:
'@types/node': ^18.15.5
esbuild: ^0.17.12
nanoid: ^4.0.1
typescript: ^5.0.2
lockfileVersion: '6.0'
dependencies:
nanoid: 4.0.1
dotenv:
specifier: ^16.0.3
version: 16.0.3
nanoid:
specifier: ^4.0.1
version: 4.0.1
devDependencies:
'@types/node': 18.15.5
esbuild: 0.17.12
typescript: 5.0.2
'@types/node':
specifier: ^18.15.5
version: 18.15.5
packages:
/@esbuild/android-arm/0.17.12:
resolution: {integrity: sha512-E/sgkvwoIfj4aMAPL2e35VnUJspzVYl7+M1B2cqeubdBhADV4uPon0KCc8p2G+LqSJ6i8ocYPCqY3A4GGq0zkQ==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-arm64/0.17.12:
resolution: {integrity: sha512-WQ9p5oiXXYJ33F2EkE3r0FRDFVpEdcDiwNX3u7Xaibxfx6vQE0Sb8ytrfQsA5WO6kDn6mDfKLh6KrPBjvkk7xA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-x64/0.17.12:
resolution: {integrity: sha512-m4OsaCr5gT+se25rFPHKQXARMyAehHTQAz4XX1Vk3d27VtqiX0ALMBPoXZsGaB6JYryCLfgGwUslMqTfqeLU0w==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-arm64/0.17.12:
resolution: {integrity: sha512-O3GCZghRIx+RAN0NDPhyyhRgwa19MoKlzGonIb5hgTj78krqp9XZbYCvFr9N1eUxg0ZQEpiiZ4QvsOQwBpP+lg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-x64/0.17.12:
resolution: {integrity: sha512-5D48jM3tW27h1qjaD9UNRuN+4v0zvksqZSPZqeSWggfMlsVdAhH3pwSfQIFJwcs9QJ9BRibPS4ViZgs3d2wsCA==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-arm64/0.17.12:
resolution: {integrity: sha512-OWvHzmLNTdF1erSvrfoEBGlN94IE6vCEaGEkEH29uo/VoONqPnoDFfShi41Ew+yKimx4vrmmAJEGNoyyP+OgOQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-x64/0.17.12:
resolution: {integrity: sha512-A0Xg5CZv8MU9xh4a+7NUpi5VHBKh1RaGJKqjxe4KG87X+mTjDE6ZvlJqpWoeJxgfXHT7IMP9tDFu7IZ03OtJAw==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm/0.17.12:
resolution: {integrity: sha512-WsHyJ7b7vzHdJ1fv67Yf++2dz3D726oO3QCu8iNYik4fb5YuuReOI9OtA+n7Mk0xyQivNTPbl181s+5oZ38gyA==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm64/0.17.12:
resolution: {integrity: sha512-cK3AjkEc+8v8YG02hYLQIQlOznW+v9N+OI9BAFuyqkfQFR+DnDLhEM5N8QRxAUz99cJTo1rLNXqRrvY15gbQUg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ia32/0.17.12:
resolution: {integrity: sha512-jdOBXJqcgHlah/nYHnj3Hrnl9l63RjtQ4vn9+bohjQPI2QafASB5MtHAoEv0JQHVb/xYQTFOeuHnNYE1zF7tYw==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-loong64/0.17.12:
resolution: {integrity: sha512-GTOEtj8h9qPKXCyiBBnHconSCV9LwFyx/gv3Phw0pa25qPYjVuuGZ4Dk14bGCfGX3qKF0+ceeQvwmtI+aYBbVA==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-mips64el/0.17.12:
resolution: {integrity: sha512-o8CIhfBwKcxmEENOH9RwmUejs5jFiNoDw7YgS0EJTF6kgPgcqLFjgoc5kDey5cMHRVCIWc6kK2ShUePOcc7RbA==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ppc64/0.17.12:
resolution: {integrity: sha512-biMLH6NR/GR4z+ap0oJYb877LdBpGac8KfZoEnDiBKd7MD/xt8eaw1SFfYRUeMVx519kVkAOL2GExdFmYnZx3A==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-riscv64/0.17.12:
resolution: {integrity: sha512-jkphYUiO38wZGeWlfIBMB72auOllNA2sLfiZPGDtOBb1ELN8lmqBrlMiucgL8awBw1zBXN69PmZM6g4yTX84TA==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-s390x/0.17.12:
resolution: {integrity: sha512-j3ucLdeY9HBcvODhCY4b+Ds3hWGO8t+SAidtmWu/ukfLLG/oYDMaA+dnugTVAg5fnUOGNbIYL9TOjhWgQB8W5g==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-x64/0.17.12:
resolution: {integrity: sha512-uo5JL3cgaEGotaqSaJdRfFNSCUJOIliKLnDGWaVCgIKkHxwhYMm95pfMbWZ9l7GeW9kDg0tSxcy9NYdEtjwwmA==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/netbsd-x64/0.17.12:
resolution: {integrity: sha512-DNdoRg8JX+gGsbqt2gPgkgb00mqOgOO27KnrWZtdABl6yWTST30aibGJ6geBq3WM2TIeW6COs5AScnC7GwtGPg==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/openbsd-x64/0.17.12:
resolution: {integrity: sha512-aVsENlr7B64w8I1lhHShND5o8cW6sB9n9MUtLumFlPhG3elhNWtE7M1TFpj3m7lT3sKQUMkGFjTQBrvDDO1YWA==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/sunos-x64/0.17.12:
resolution: {integrity: sha512-qbHGVQdKSwi0JQJuZznS4SyY27tYXYF0mrgthbxXrZI3AHKuRvU+Eqbg/F0rmLDpW/jkIZBlCO1XfHUBMNJ1pg==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-arm64/0.17.12:
resolution: {integrity: sha512-zsCp8Ql+96xXTVTmm6ffvoTSZSV2B/LzzkUXAY33F/76EajNw1m+jZ9zPfNJlJ3Rh4EzOszNDHsmG/fZOhtqDg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-ia32/0.17.12:
resolution: {integrity: sha512-FfrFjR4id7wcFYOdqbDfDET3tjxCozUgbqdkOABsSFzoZGFC92UK7mg4JKRc/B3NNEf1s2WHxJ7VfTdVDPN3ng==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-x64/0.17.12:
resolution: {integrity: sha512-JOOxw49BVZx2/5tW3FqkdjSD/5gXYeVGPDcB0lvap0gLQshkh1Nyel1QazC+wNxus3xPlsYAgqU1BUmrmCvWtw==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@types/node/18.15.5:
/@types/node@18.15.5:
resolution: {integrity: sha512-Ark2WDjjZO7GmvsyFFf81MXuGTA/d6oP38anyxWOL6EREyBKAxKoFHwBhaZxCfLRLpO8JgVXwqOwSwa7jRcjew==}
dev: true
/esbuild/0.17.12:
resolution: {integrity: sha512-bX/zHl7Gn2CpQwcMtRogTTBf9l1nl+H6R8nUbjk+RuKqAE3+8FDulLA+pHvX7aA7Xe07Iwa+CWvy9I8Y2qqPKQ==}
/dotenv@16.0.3:
resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/android-arm': 0.17.12
'@esbuild/android-arm64': 0.17.12
'@esbuild/android-x64': 0.17.12
'@esbuild/darwin-arm64': 0.17.12
'@esbuild/darwin-x64': 0.17.12
'@esbuild/freebsd-arm64': 0.17.12
'@esbuild/freebsd-x64': 0.17.12
'@esbuild/linux-arm': 0.17.12
'@esbuild/linux-arm64': 0.17.12
'@esbuild/linux-ia32': 0.17.12
'@esbuild/linux-loong64': 0.17.12
'@esbuild/linux-mips64el': 0.17.12
'@esbuild/linux-ppc64': 0.17.12
'@esbuild/linux-riscv64': 0.17.12
'@esbuild/linux-s390x': 0.17.12
'@esbuild/linux-x64': 0.17.12
'@esbuild/netbsd-x64': 0.17.12
'@esbuild/openbsd-x64': 0.17.12
'@esbuild/sunos-x64': 0.17.12
'@esbuild/win32-arm64': 0.17.12
'@esbuild/win32-ia32': 0.17.12
'@esbuild/win32-x64': 0.17.12
dev: true
dev: false
/nanoid/4.0.1:
/nanoid@4.0.1:
resolution: {integrity: sha512-udKGtCCUafD3nQtJg9wBhRP3KMbPglUsgV5JVsXhvyBs/oefqb4sqMEhKBBgqZncYowu58p1prsZQBYvAj/Gww==}
engines: {node: ^14 || ^16 || >=18}
hasBin: true
dev: false
/typescript/5.0.2:
resolution: {integrity: sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==}
engines: {node: '>=12.20'}
hasBin: true
dev: true

View File

@ -4,3 +4,26 @@
1. lo configuré para que borre en el disco real si se borra algo dentro de la vm
1. como woodpecker-agent no tiene state, puedo regenerar la vm con el script las veces que quiera
1. no tengo que exponer mi docker real al agent porque está en una vm con el suyo
## compilar imágen
```sh
node index.js
```
copiar `initramfs-virt vmlinuz-virt vm.qcow2` al servidor a `/var/lib/libvirt/images`
## definir vm
```sh
virsh shutdown woodpecker-in-a-vm
virsh undefine woodpecker-in-a-vm
virt-install --name woodpecker-in-a-vm \
--osinfo alpinelinux3.16 \
--memory 4096 --vcpus 4 \
--import \
--disk path=/var/lib/libvirt/images/vm.qcow2,format=qcow2 \
--boot kernel=/var/lib/libvirt/images/vmlinuz-virt,initrd=/var/lib/libvirt/images/initramfs-virt,kernel_args="console=/dev/ttyS0 quiet root=/dev/vda rw modules=ext4" \
--noautoconsole
```

225
vm.xml
View File

@ -1,225 +0,0 @@
<domain type='kvm' id='2'>
<name>woodpecker-in-a-vm</name>
<uuid>bdf26ac3-7eb1-41d7-9e29-846c60c85133</uuid>
<metadata>
<libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
<libosinfo:os id="http://alpinelinux.org/alpinelinux/3.17"/>
</libosinfo:libosinfo>
</metadata>
<memory unit='KiB'>4194304</memory>
<currentMemory unit='KiB'>4194304</currentMemory>
<vcpu placement='static'>2</vcpu>
<resource>
<partition>/machine</partition>
</resource>
<os>
<type arch='x86_64' machine='pc-q35-7.2'>hvm</type>
<kernel>/home/diablo/proy/woodpecker-in-a-vm/artifacts/current/boot/vmlinuz-virt</kernel>
<initrd>/home/diablo/proy/woodpecker-in-a-vm/artifacts/current/boot/initramfs-virt</initrd>
<cmdline>quiet root=/dev/vda rw modules=ext4</cmdline>
<boot dev='hd'/>
</os>
<features>
<acpi/>
<apic/>
<vmport state='off'/>
</features>
<cpu mode='host-passthrough' check='none' migratable='on'/>
<clock offset='utc'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
<timer name='hpet' present='no'/>
</clock>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<pm>
<suspend-to-mem enabled='no'/>
<suspend-to-disk enabled='no'/>
</pm>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2' discard='unmap'/>
<source file='/home/diablo/proy/woodpecker-in-a-vm/vm.qcow2' index='1'/>
<backingStore/>
<target dev='vda' bus='virtio'/>
<alias name='virtio-disk0'/>
<address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
</disk>
<controller type='usb' index='0' model='qemu-xhci' ports='15'>
<alias name='usb'/>
<address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
</controller>
<controller type='pci' index='0' model='pcie-root'>
<alias name='pcie.0'/>
</controller>
<controller type='pci' index='1' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='1' port='0x10'/>
<alias name='pci.1'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0' multifunction='on'/>
</controller>
<controller type='pci' index='2' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='2' port='0x11'/>
<alias name='pci.2'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x1'/>
</controller>
<controller type='pci' index='3' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='3' port='0x12'/>
<alias name='pci.3'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x2'/>
</controller>
<controller type='pci' index='4' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='4' port='0x13'/>
<alias name='pci.4'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x3'/>
</controller>
<controller type='pci' index='5' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='5' port='0x14'/>
<alias name='pci.5'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x4'/>
</controller>
<controller type='pci' index='6' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='6' port='0x15'/>
<alias name='pci.6'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x5'/>
</controller>
<controller type='pci' index='7' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='7' port='0x16'/>
<alias name='pci.7'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x6'/>
</controller>
<controller type='pci' index='8' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='8' port='0x17'/>
<alias name='pci.8'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x7'/>
</controller>
<controller type='pci' index='9' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='9' port='0x18'/>
<alias name='pci.9'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0' multifunction='on'/>
</controller>
<controller type='pci' index='10' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='10' port='0x19'/>
<alias name='pci.10'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x1'/>
</controller>
<controller type='pci' index='11' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='11' port='0x1a'/>
<alias name='pci.11'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x2'/>
</controller>
<controller type='pci' index='12' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='12' port='0x1b'/>
<alias name='pci.12'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x3'/>
</controller>
<controller type='pci' index='13' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='13' port='0x1c'/>
<alias name='pci.13'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x4'/>
</controller>
<controller type='pci' index='14' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='14' port='0x1d'/>
<alias name='pci.14'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x5'/>
</controller>
<controller type='sata' index='0'>
<alias name='ide'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
</controller>
<controller type='virtio-serial' index='0'>
<alias name='virtio-serial0'/>
<address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
</controller>
<interface type='network'>
<mac address='52:54:00:08:f9:dc'/>
<source network='default' portid='82cc0c21-4cab-4010-ad3b-c66373e8beca' bridge='virbr0'/>
<target dev='vnet1'/>
<model type='virtio'/>
<alias name='net0'/>
<address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</interface>
<serial type='pty'>
<source path='/dev/pts/10'/>
<target type='isa-serial' port='0'>
<model name='isa-serial'/>
</target>
<alias name='serial0'/>
</serial>
<console type='pty' tty='/dev/pts/10'>
<source path='/dev/pts/10'/>
<target type='serial' port='0'/>
<alias name='serial0'/>
</console>
<channel type='unix'>
<source mode='bind' path='/var/lib/libvirt/qemu/channel/target/domain-2-woodpecker-in-a-vm/org.qemu.guest_agent.0'/>
<target type='virtio' name='org.qemu.guest_agent.0' state='disconnected'/>
<alias name='channel0'/>
<address type='virtio-serial' controller='0' bus='0' port='1'/>
</channel>
<channel type='spicevmc'>
<target type='virtio' name='com.redhat.spice.0' state='disconnected'/>
<alias name='channel1'/>
<address type='virtio-serial' controller='0' bus='0' port='2'/>
</channel>
<input type='tablet' bus='usb'>
<alias name='input0'/>
<address type='usb' bus='0' port='1'/>
</input>
<input type='mouse' bus='ps2'>
<alias name='input1'/>
</input>
<input type='keyboard' bus='ps2'>
<alias name='input2'/>
</input>
<graphics type='spice' port='5901' autoport='yes' listen='127.0.0.1'>
<listen type='address' address='127.0.0.1'/>
<image compression='off'/>
</graphics>
<video>
<model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
<alias name='video0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
</video>
<redirdev bus='usb' type='spicevmc'>
<alias name='redir0'/>
<address type='usb' bus='0' port='2'/>
</redirdev>
<redirdev bus='usb' type='spicevmc'>
<alias name='redir1'/>
<address type='usb' bus='0' port='3'/>
</redirdev>
<watchdog model='itco' action='reset'>
<alias name='watchdog0'/>
</watchdog>
<memballoon model='virtio'>
<alias name='balloon0'/>
<address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
</memballoon>
<rng model='virtio'>
<backend model='random'>/dev/urandom</backend>
<alias name='rng0'/>
<address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
</rng>
</devices>
<seclabel type='dynamic' model='dac' relabel='yes'>
<label>+0:+0</label>
<imagelabel>+0:+0</imagelabel>
</seclabel>
</domain>