{"id":15374819,"url":"https://github.com/com8/os-tester","last_synced_at":"2025-04-15T12:31:45.268Z","repository":{"id":211233410,"uuid":"728567804","full_name":"COM8/os-tester","owner":"COM8","description":"A Python pip package to automate testing of whole operating systems with an image recognition based approach and libvirt (qemu). Inspired by openQA.","archived":false,"fork":false,"pushed_at":"2025-03-25T17:03:29.000Z","size":468,"stargazers_count":3,"open_issues_count":2,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-28T21:12:21.530Z","etag":null,"topics":["libvirt","operating-system","python","python3","qemu","testing"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/COM8.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-12-07T08:11:25.000Z","updated_at":"2025-02-19T18:28:20.000Z","dependencies_parsed_at":"2023-12-07T09:25:42.720Z","dependency_job_id":"d5362649-4b6b-4299-8d83-8ab3ce7fc08e","html_url":"https://github.com/COM8/os-tester","commit_stats":{"total_commits":39,"total_committers":4,"mean_commits":9.75,"dds":0.3846153846153846,"last_synced_commit":"c941cbeaf76badb6074edad3edd14a9bf1d21902"},"previous_names":["ap-sensing/os-tester","com8/os-tester"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/COM8%2Fos-tester","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/COM8%2Fos-tester/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/COM8%2Fos-tester/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/COM8%2Fos-tester/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/COM8","download_url":"https://codeload.github.com/COM8/os-tester/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249072279,"owners_count":21208155,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["libvirt","operating-system","python","python3","qemu","testing"],"created_at":"2024-10-01T13:59:50.490Z","updated_at":"2025-04-15T12:31:44.969Z","avatar_url":"https://github.com/COM8.png","language":"Python","readme":"# OS Tester\nA Python pip package to automate testing of whole operating systems with an image recognition based approach and libvirt (qemu). Inspired by openQA.\n\n## Example\n\n![example_when_debug_is_enabled](examples/example.png)\n\n```python\nfrom os_tester.vm import vm\nfrom os_tester.stages import stages\nimport libvirt\n\n# SELinux Policy for allowing Qemu to access image files:\n# ausearch -c 'qemu-system-x86' --raw | audit2allow -M my-qemusystemx86\n# semodule -X 300 -i my-qemusystemx86.pp\n\n# NVME: http://blog.frankenmichl.de/2018/02/13/add-nvme-device-to-vm/\n# dd if=/dev/zero of=/tmp/test_vm_1.img bs=1M count=8192\n# Or:\n# qemu-img create -f qcow2 /tmp/test_vm_1.qcow2 8G\n\n\ndef get_vm_xml(name: str, title: str, uuid: str, isoPath: str, vmImagePath: str) -\u003e str:\n    ramGiB: int = 2\n    numCpus: int = 2\n\n    return f\"\"\"\n\u003cdomain type=\"kvm\" xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'\u003e\n  \u003cname\u003e{name}\u003c/name\u003e\n  \u003cuuid\u003e{uuid}\u003c/uuid\u003e\n  \u003ctitle\u003e{title}\u003c/title\u003e\n  \u003cmemory unit=\"GiB\"\u003e{ramGiB}\u003c/memory\u003e\n  \u003ccurrentMemory unit=\"GiB\"\u003e{ramGiB}\u003c/currentMemory\u003e\n  \u003cvcpu placement=\"static\"\u003e{numCpus}\u003c/vcpu\u003e\n  \u003cos firmware='efi'\u003e\n  \u003c!--To get a list of all machines: qemu-system-x86_64 -machine help--\u003e\n    \u003ctype arch=\"x86_64\" machine=\"q35\"\u003ehvm\u003c/type\u003e\n    \u003cbootmenu enable=\"yes\"/\u003e\n    \u003cboot dev=\"hd\"/\u003e\n    \u003cboot dev=\"cdrom\"/\u003e\n  \u003c/os\u003e\n  \u003cfeatures\u003e\n    \u003cacpi/\u003e\n    \u003capic/\u003e\n  \u003c/features\u003e\n  \u003cclock offset=\"localtime\"\u003e\n    \u003ctimer name=\"rtc\" tickpolicy=\"catchup\"/\u003e\n    \u003ctimer name=\"pit\" tickpolicy=\"delay\"/\u003e\n    \u003ctimer name=\"hpet\" present=\"no\"/\u003e\n  \u003c/clock\u003e\n  \u003con_poweroff\u003edestroy\u003c/on_poweroff\u003e\n  \u003con_reboot\u003erestart\u003c/on_reboot\u003e\n  \u003con_crash\u003erestart\u003c/on_crash\u003e\n  \u003cpm\u003e\n    \u003csuspend-to-mem enabled=\"no\"/\u003e\n    \u003csuspend-to-disk enabled=\"no\"/\u003e\n  \u003c/pm\u003e\n  \u003cdevices\u003e\n    \u003cemulator\u003e/usr/bin/qemu-system-x86_64\u003c/emulator\u003e\n    \u003ccontroller type='pci' index='0' model='pcie-root'/\u003e\n    \u003ccontroller type='pci' index='1' model='pcie-root-port'\u003e\n      \u003cmodel name='pcie-root-port'/\u003e\n      \u003ctarget chassis='1' port='0x10'/\u003e\n      \u003caddress type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/\u003e\n    \u003c/controller\u003e\n    \u003cdisk type=\"file\" device=\"cdrom\"\u003e\n      \u003cdriver name=\"qemu\" type=\"raw\"/\u003e\n      \u003csource file=\"{isoPath}\" startupPolicy=\"mandatory\"/\u003e\n      \u003ctarget dev=\"hdc\" bus=\"sata\"/\u003e\n      \u003creadonly/\u003e\n      \u003caddress type=\"drive\" controller=\"0\" bus=\"0\" target=\"0\" unit=\"2\"/\u003e\n    \u003c/disk\u003e\n    \u003cinterface type=\"user\"\u003e\n      \u003cmac address=\"52:54:00:8d:ce:97\"/\u003e\n      \u003cmodel type=\"virtio\"/\u003e\n      \u003caddress type=\"pci\" domain=\"0x0000\" bus=\"0x01\" slot=\"0x00\" function=\"0x00\"/\u003e\n    \u003c/interface\u003e\n    \u003cserial type=\"pty\"\u003e\n      \u003ctarget type=\"isa-serial\" port=\"0\"\u003e\n        \u003cmodel name=\"isa-serial\"/\u003e\n      \u003c/target\u003e\n    \u003c/serial\u003e\n    \u003cconsole type=\"pty\"\u003e\n      \u003ctarget type=\"serial\" port=\"0\"/\u003e\n    \u003c/console\u003e\n    \u003cinput type=\"tablet\" bus=\"usb\"\u003e\n      \u003caddress type=\"usb\" bus=\"0\" port=\"2\"/\u003e\n    \u003c/input\u003e\n    \u003cinput type=\"mouse\" bus=\"ps2\"/\u003e\n    \u003cinput type=\"keyboard\" bus=\"ps2\"/\u003e\n    \u003cmemballoon model=\"virtio\"\u003e\n      \u003caddress type=\"pci\" domain=\"0x0000\" bus=\"0x05\" slot=\"0x00\" function=\"0x0\"/\u003e\n    \u003c/memballoon\u003e\n  \u003c/devices\u003e\n  \u003cqemu:commandline\u003e\n\t  \u003cqemu:arg value='-drive'/\u003e\n\t  \u003cqemu:arg value='file={vmImagePath},format=qcow2,if=none,id=nvdisk1,media=disk'/\u003e\n\t  \u003cqemu:arg value='-device'/\u003e\n\t  \u003cqemu:arg value='nvme,bootindex=1,drive=nvdisk1,serial=1234,id=nvme0,bus=pcie.0,addr=0x04'/\u003e\n  \u003c/qemu:commandline\u003e\n\u003c/domain\u003e\n    \"\"\"\n\nif __name__ == \"__main__\":\n    # Connect to qemu\n    conn: libvirt.virConnect = libvirt.open(\"qemu:///system\")\n\n    uuid: str = \"1e6cae9f-41d7-4fca-8033-fbd538a65173\" # Replace with your (random?) UUID\n    vmObj: vm = vm(conn, uuid, debugPlt=False)\n\n    # Delete eventually existing VMs\n    if vmObj.try_load():\n        print(f\"Deleting existing VM for UUID '{uuid}'...\")\n        vmObj.destroy()\n        exit(0)\n        print(f\"VM destroyed.\")\n    else:\n        print(f\"No existing VM found for UUID '{uuid}'.\")\n\n    # Create and start a new VM\n    vmXml: str = get_vm_xml(\n        \"test_vm_1\",\n        \"Test_VM_1\",\n        uuid,\n        \"\u003cPATH_TO_THE_ISO_FILE_TO_BOOT_FROM\u003e\",\n        \"test_vm_1.qcow2\", # qemu-img create -f qcow2 test_vm_1.qcow2 8G\n    )\n    vmObj.create(vmXml)\n\n    # Load stages automation.\n    # We expect the `stages.yml` and referenced files inside the stages directory.\n    basePath: str = \"stages\"\n    stagesObj: stages = stages(basePath)\n    print(stagesObj)\n    \n    vmObj.run_stages(stagesObj)\n\n    print(\"All stages done. Exiting...\")\n    conn.close()\n    exit(0)\n```\n\n### Stages\nStages are defined as a YAML file. The schema for it is available under [`stages_schema.yml`](stages_schema.yml).\nThe following shows an example of such a file:\n```yaml\nstages:\n  - stage: Bootloader Selection\n    timeout_s: 15\n    paths:\n      - path:\n          check:\n            file: 0.png\n            mse_leq: 0.1\n            ssim_geq: 0.99\n          actions:\n            - keyboard_key:\n                value: up\n                duration_s: 0.25\n            - keyboard_key:\n                value: ret\n                duration_s: 0.25\n          nextStage: Installation Started\n      - path:\n          check:\n            file: 0_1.png\n            mse_leq: 0.1\n            ssim_geq: 0.99\n          actions:\n            - keyboard_key:\n                value: up\n                duration_s: 0.25\n            - keyboard_key:\n                value: up\n                duration_s: 0.25\n            - keyboard_key:\n                value: ret\n                duration_s: 0.25\n          nextStage: Installation Started\n\n  - stage: Installation Started\n    timeout_s: 600\n    paths:\n      - path:\n          check:\n            file: 1.png\n            mse_leq: 0.1\n            ssim_geq: 0.99\n          actions:\n            - keyboard_key:\n                value: up\n                duration_s: 0.25\n          nextStage: Installation Complete\n\n  - stage: Installation Complete\n    timeout_s: 600\n    paths:\n      - path:\n          check:\n            file: 2.png\n            mse_leq: 0.1\n            ssim_geq: 0.99\n          actions:\n            - keyboard_key:\n                value: tab\n                duration_s: 0.25\n            - keyboard_key:\n                value: tab\n                duration_s: 0.25\n            - keyboard_key:\n                value: ret\n                duration_s: 0.25\n          nextStage: Enter LUKS Password\n\n  - stage: Enter LUKS Password\n    timeout_s: 600\n    paths:\n      - path:\n          check:\n            file: 3.png\n            mse_leq: 0.1\n            ssim_geq: 0.99\n          actions:\n            - keyboard_text:\n                value: something\n                duration_s: 0.25\n            - keyboard_key:\n                value: ret\n                duration_s: 0.25\n          nextStage: None\n\n```\n\n## Building the pip-Package\n\nTo build the pip package run:\n```bash\nrm -rf dist/\npython3 -m build\n```\nThe output is then available inside the `dist/` directory.\n\n## Upload\n```bash\ntwine upload dist/*\n```\n\n## pre-commit\nBefore committing you have to run `pre-commit` to check for linting and type errors.\nFor this first install `pre-commit`.\n\n```bash\ndnf install pre-commit\npre-commit install\n```\n\nTo run `pre-commit` manually run:\n```bash\npre-commit run --all-files\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcom8%2Fos-tester","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcom8%2Fos-tester","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcom8%2Fos-tester/lists"}