A Python pip package to automate testing of whole operating systems with an image recognition based approach and libvirt (qemu). Inspired by openQA.
Project description
OS Tester
A Python pip package to automate testing of whole operating systems with an image recognition based approach and libvirt (qemu). Inspired by openQA.
Example
from os_tester.vm import vm
from os_tester.stages import stages
import libvirt
# SELinux Policy for allowing Qemu to access image files:
# ausearch -c 'qemu-system-x86' --raw | audit2allow -M my-qemusystemx86
# semodule -X 300 -i my-qemusystemx86.pp
# NVME: http://blog.frankenmichl.de/2018/02/13/add-nvme-device-to-vm/
# dd if=/dev/zero of=/tmp/test_vm_1.img bs=1M count=8192
# Or:
# qemu-img create -f qcow2 /tmp/test_vm_1.qcow2 8G
def get_vm_xml(name: str, title: str, uuid: str, isoPath: str, vmImagePath: str) -> str:
ramGiB: int = 2
numCpus: int = 2
return f"""
<domain type="kvm" xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
<name>{name}</name>
<uuid>{uuid}</uuid>
<title>{title}</title>
<memory unit="GiB">{ramGiB}</memory>
<currentMemory unit="GiB">{ramGiB}</currentMemory>
<vcpu placement="static">{numCpus}</vcpu>
<os firmware='efi'>
<!--To get a list of all machines: qemu-system-x86_64 -machine help-->
<type arch="x86_64" machine="q35">hvm</type>
<bootmenu enable="yes"/>
<boot dev="hd"/>
<boot dev="cdrom"/>
</os>
<features>
<acpi/>
<apic/>
</features>
<clock offset="localtime">
<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>restart</on_crash>
<pm>
<suspend-to-mem enabled="no"/>
<suspend-to-disk enabled="no"/>
</pm>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<controller type='pci' index='0' model='pcie-root'/>
<controller type='pci' index='1' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='1' port='0x10'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
</controller>
<disk type="file" device="cdrom">
<driver name="qemu" type="raw"/>
<source file="{isoPath}" startupPolicy="mandatory"/>
<target dev="hdc" bus="sata"/>
<readonly/>
<address type="drive" controller="0" bus="0" target="0" unit="2"/>
</disk>
<interface type="user">
<mac address="52:54:00:8d:ce:97"/>
<model type="virtio"/>
<address type="pci" domain="0x0000" bus="0x01" slot="0x00" function="0x00"/>
</interface>
<serial type="pty">
<target type="isa-serial" port="0">
<model name="isa-serial"/>
</target>
</serial>
<console type="pty">
<target type="serial" port="0"/>
</console>
<input type="tablet" bus="usb">
<address type="usb" bus="0" port="2"/>
</input>
<input type="mouse" bus="ps2"/>
<input type="keyboard" bus="ps2"/>
<memballoon model="virtio">
<address type="pci" domain="0x0000" bus="0x05" slot="0x00" function="0x0"/>
</memballoon>
</devices>
<qemu:commandline>
<qemu:arg value='-drive'/>
<qemu:arg value='file={vmImagePath},format=qcow2,if=none,id=nvdisk1,media=disk'/>
<qemu:arg value='-device'/>
<qemu:arg value='nvme,bootindex=1,drive=nvdisk1,serial=1234,id=nvme0,bus=pcie.0,addr=0x04'/>
</qemu:commandline>
</domain>
"""
if __name__ == "__main__":
# Connect to qemu
conn: libvirt.virConnect = libvirt.open("qemu:///system")
uuid: str = "1e6cae9f-41d7-4fca-8033-fbd538a65173" # Replace with your (random?) UUID
vmObj: vm = vm(conn, uuid, debugPlt=False)
# Delete eventually existing VMs
if vmObj.try_load():
print(f"Deleting existing VM for UUID '{uuid}'...")
vmObj.destroy()
exit(0)
print(f"VM destroyed.")
else:
print(f"No existing VM found for UUID '{uuid}'.")
# Create and start a new VM
vmXml: str = get_vm_xml(
"test_vm_1",
"Test_VM_1",
uuid,
"<PATH_TO_THE_ISO_FILE_TO_BOOT_FROM>",
"test_vm_1.qcow2", # qemu-img create -f qcow2 test_vm_1.qcow2 8G
)
vmObj.create(vmXml)
# Load stages automation.
# We expect the `stages.yml` and referenced files inside the stages directory.
basePath: str = "stages"
stagesObj: stages = stages(basePath)
print(stagesObj)
vmObj.run_stages(stagesObj)
print("All stages done. Exiting...")
conn.close()
exit(0)
Stages
Stages are defined as a YAML file. The schema for it is available under stages_schema.yml
.
The following shows an example of such a file:
stages:
- stage: Bootloader Selection
timeout_s: 15
paths:
- path:
check:
file: 0.png
mse_leq: 0.1
ssim_geq: 0.99
actions:
- keyboard_key:
value: up
duration_s: 0.25
- keyboard_key:
value: ret
duration_s: 0.25
nextStage: Installation Started
- path:
check:
file: 0_1.png
mse_leq: 0.1
ssim_geq: 0.99
actions:
- keyboard_key:
value: up
duration_s: 0.25
- keyboard_key:
value: up
duration_s: 0.25
- keyboard_key:
value: ret
duration_s: 0.25
nextStage: Installation Started
- stage: Installation Started
timeout_s: 600
paths:
- path:
check:
file: 1.png
mse_leq: 0.1
ssim_geq: 0.99
actions:
- keyboard_key:
value: up
duration_s: 0.25
nextStage: Installation Complete
- stage: Installation Complete
timeout_s: 600
paths:
- path:
check:
file: 2.png
mse_leq: 0.1
ssim_geq: 0.99
actions:
- keyboard_key:
value: tab
duration_s: 0.25
- keyboard_key:
value: tab
duration_s: 0.25
- keyboard_key:
value: ret
duration_s: 0.25
nextStage: Enter LUKS Password
- stage: Enter LUKS Password
timeout_s: 600
paths:
- path:
check:
file: 3.png
mse_leq: 0.1
ssim_geq: 0.99
actions:
- keyboard_text:
value: something
duration_s: 0.25
- keyboard_key:
value: ret
duration_s: 0.25
nextStage: None
Building the pip-Package
To build the pip package run:
python3 -m build
The output is then available inside the dist/
directory.
pre-commit
Before committing you have to run pre-commit
to check for linting and type errors.
For this first install pre-commit
.
dnf install pre-commit
pre-commit install
To run pre-commit
manually run:
pre-commit run --all-files
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file os_tester-0.3.0.tar.gz
.
File metadata
- Download URL: os_tester-0.3.0.tar.gz
- Upload date:
- Size: 25.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | d016a6c29b2335c5a2dc28dbd9d459629fcf43e304eb8c7871581ab916bd1f87 |
|
MD5 | 7ca64aa1360a891299ca2a26b814644d |
|
BLAKE2b-256 | ae374f9c2dc99fbb0b2560f687bde627ef9cf969c0532a5ef896049401ffdcb6 |
File details
Details for the file os_tester-0.3.0-py3-none-any.whl
.
File metadata
- Download URL: os_tester-0.3.0-py3-none-any.whl
- Upload date:
- Size: 22.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0f939af09f7cf3fedbf0a1a78175db62c011bc80484ada764e284f6cfd9ef13a |
|
MD5 | 4ea2d6d78f203db7199df7f83804ac80 |
|
BLAKE2b-256 | 6a98d36fb349d5984e54adec31675d2567190cd8fd10b5174302b881a93967fa |