Package: os-tester
OS TesterA Python pip package to automate testing of whole operating systems with an image recognition based approach and libvirt (qemu). Inspired by openQA.Examplefromos_tester.vmimportvmfromos_tester.stagesimportstagesimportlibvirt# 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 8Gdefget_vm_xml(name:str,title:str,uuid:str,isoPath:str,vmImagePath:str)->str:ramGiB:int=2numCpus:int=2returnf"""<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__":# Load stages automation.# We expect the `stages.yml` and referenced files inside the stages directory.basePath:str="stages"stagesObj:stages=stages(basePath)print(stagesObj)# Connect to qemuconn:libvirt.virConnect=libvirt.open("qemu:///system")uuid:str="1e6cae9f-41d7-4fca-8033-fbd538a65173"# Replace with your (random?) UUIDvmObj:vm=vm(conn,uuid,stagesObj,debugPlt=False)# Delete eventually existing VMsifvmObj.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 VMvmXml: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)vmObj.run_stages()print("All stages done. Exiting...")conn.close()exit(0)StagesStages are defined as a YAML file. The schema for it is available understages_schema.yml.
The following shows an example of such a file:stages:-stage:Bootloader Selectiontimeout_s:15check:file:0.pngmse_leq:0.1ssim_geq:0.99actions:-keyboard_key:value:upduration_s:0.25-keyboard_key:value:retduration_s:0.25-stage:Installation Startedtimeout_s:600check:file:1.pngmse_leq:0.1ssim_geq:0.99-stage:Installation Completetimeout_s:600check:file:2.pngmse_leq:0.1ssim_geq:0.99actions:-keyboard_key:value:tabduration_s:0.25-keyboard_key:value:tabduration_s:0.25-keyboard_key:value:retduration_s:0.25-stage:Enter LUKS Passwordtimeout_s:600check:file:3.pngmse_leq:0.1ssim_geq:0.99actions:-keyboard_text:value:somethingduration_s:0.25-keyboard_key:value:retduration_s:0.25Building the pip-PackageTo build the pip package run:python3-mbuildThe output is then available inside thedist/directory.pre-commitBefore committing you have to runpre-committo check for linting and type errors.
For this first installpre-commit.dnfinstallpre-commit
pre-commitinstallTo runpre-commitmanually run:pre-commitrun--all-files