os-tester 1.1.0.dev8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,293 @@
1
+ Metadata-Version: 2.4
2
+ Name: os-tester
3
+ Version: 1.1.0.dev8
4
+ Summary: A Python pip package to automate testing of whole operating systems with an image recognition based approach and libvirt (qemu). Inspired by openQA.
5
+ Author-email: Fabian Sauter <fabian.sauter+pip@apsensing.com>
6
+ Maintainer-email: Fabian Sauter <fabian.sauter+pip@apsensing.com>
7
+ License: GNU General Public License v3 (GPLv3)
8
+ Project-URL: Homepage, https://github.com/AP-Sensing/os-tester
9
+ Project-URL: Repository, https://github.com/AP-Sensing/os-tester
10
+ Project-URL: Issues, https://github.com/AP-Sensing/os-tester/issues
11
+ Keywords: testing,os,qemu,libvirt
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: Implementation
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
17
+ Classifier: Natural Language :: English
18
+ Requires-Python: >=3.7
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: scikit-image
22
+ Requires-Dist: opencv-python-headless
23
+ Requires-Dist: libvirt-python
24
+ Requires-Dist: PyYAML
25
+ Requires-Dist: numpy
26
+ Requires-Dist: matplotlib
27
+ Dynamic: license-file
28
+
29
+ # OS Tester
30
+ A Python pip package to automate testing of whole operating systems with an image recognition based approach and libvirt (qemu). Inspired by openQA.
31
+
32
+ ## Example
33
+
34
+ ![example_when_debug_is_enabled](examples/example.png)
35
+
36
+ ```python
37
+ from os_tester.vm import vm
38
+ from os_tester.stages import stages
39
+ import libvirt
40
+
41
+ # SELinux Policy for allowing Qemu to access image files:
42
+ # ausearch -c 'qemu-system-x86' --raw | audit2allow -M my-qemusystemx86
43
+ # semodule -X 300 -i my-qemusystemx86.pp
44
+
45
+ # NVME: http://blog.frankenmichl.de/2018/02/13/add-nvme-device-to-vm/
46
+ # dd if=/dev/zero of=/tmp/test_vm_1.img bs=1M count=8192
47
+ # Or:
48
+ # qemu-img create -f qcow2 /tmp/test_vm_1.qcow2 8G
49
+
50
+
51
+ def get_vm_xml(name: str, title: str, uuid: str, isoPath: str, vmImagePath: str) -> str:
52
+ ramGiB: int = 2
53
+ numCpus: int = 2
54
+
55
+ return f"""
56
+ <domain type="kvm" xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
57
+ <name>{name}</name>
58
+ <uuid>{uuid}</uuid>
59
+ <title>{title}</title>
60
+ <memory unit="GiB">{ramGiB}</memory>
61
+ <currentMemory unit="GiB">{ramGiB}</currentMemory>
62
+ <vcpu placement="static">{numCpus}</vcpu>
63
+ <os firmware='efi'>
64
+ <!--To get a list of all machines: qemu-system-x86_64 -machine help-->
65
+ <type arch="x86_64" machine="q35">hvm</type>
66
+ <bootmenu enable="yes"/>
67
+ <boot dev="hd"/>
68
+ <boot dev="cdrom"/>
69
+ </os>
70
+ <features>
71
+ <acpi/>
72
+ <apic/>
73
+ </features>
74
+ <clock offset="localtime">
75
+ <timer name="rtc" tickpolicy="catchup"/>
76
+ <timer name="pit" tickpolicy="delay"/>
77
+ <timer name="hpet" present="no"/>
78
+ </clock>
79
+ <on_poweroff>destroy</on_poweroff>
80
+ <on_reboot>restart</on_reboot>
81
+ <on_crash>restart</on_crash>
82
+ <pm>
83
+ <suspend-to-mem enabled="no"/>
84
+ <suspend-to-disk enabled="no"/>
85
+ </pm>
86
+ <devices>
87
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
88
+ <controller type='pci' index='0' model='pcie-root'/>
89
+ <controller type='pci' index='1' model='pcie-root-port'>
90
+ <model name='pcie-root-port'/>
91
+ <target chassis='1' port='0x10'/>
92
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
93
+ </controller>
94
+ <disk type="file" device="cdrom">
95
+ <driver name="qemu" type="raw"/>
96
+ <source file="{isoPath}" startupPolicy="mandatory"/>
97
+ <target dev="hdc" bus="sata"/>
98
+ <readonly/>
99
+ <address type="drive" controller="0" bus="0" target="0" unit="2"/>
100
+ </disk>
101
+ <interface type="user">
102
+ <mac address="52:54:00:8d:ce:97"/>
103
+ <model type="virtio"/>
104
+ <address type="pci" domain="0x0000" bus="0x01" slot="0x00" function="0x00"/>
105
+ </interface>
106
+ <serial type="pty">
107
+ <target type="isa-serial" port="0">
108
+ <model name="isa-serial"/>
109
+ </target>
110
+ </serial>
111
+ <console type="pty">
112
+ <target type="serial" port="0"/>
113
+ </console>
114
+ <input type="tablet" bus="usb">
115
+ <address type="usb" bus="0" port="2"/>
116
+ </input>
117
+ <input type="mouse" bus="ps2"/>
118
+ <input type="keyboard" bus="ps2"/>
119
+ <memballoon model="virtio">
120
+ <address type="pci" domain="0x0000" bus="0x05" slot="0x00" function="0x0"/>
121
+ </memballoon>
122
+ </devices>
123
+ <qemu:commandline>
124
+ <qemu:arg value='-drive'/>
125
+ <qemu:arg value='file={vmImagePath},format=qcow2,if=none,id=nvdisk1,media=disk'/>
126
+ <qemu:arg value='-device'/>
127
+ <qemu:arg value='nvme,bootindex=1,drive=nvdisk1,serial=1234,id=nvme0,bus=pcie.0,addr=0x04'/>
128
+ </qemu:commandline>
129
+ </domain>
130
+ """
131
+
132
+ if __name__ == "__main__":
133
+ # Connect to qemu
134
+ conn: libvirt.virConnect = libvirt.open("qemu:///system")
135
+
136
+ uuid: str = "1e6cae9f-41d7-4fca-8033-fbd538a65173" # Replace with your (random?) UUID
137
+ vmObj: vm = vm(conn, uuid, debugPlt=False)
138
+
139
+ # Delete eventually existing VMs
140
+ if vmObj.try_load():
141
+ print(f"Deleting existing VM for UUID '{uuid}'...")
142
+ vmObj.destroy()
143
+ exit(0)
144
+ print(f"VM destroyed.")
145
+ else:
146
+ print(f"No existing VM found for UUID '{uuid}'.")
147
+
148
+ # Create and start a new VM
149
+ vmXml: str = get_vm_xml(
150
+ "test_vm_1",
151
+ "Test_VM_1",
152
+ uuid,
153
+ "<PATH_TO_THE_ISO_FILE_TO_BOOT_FROM>",
154
+ "test_vm_1.qcow2", # qemu-img create -f qcow2 test_vm_1.qcow2 8G
155
+ )
156
+ vmObj.create(vmXml)
157
+
158
+ # Load stages automation.
159
+ # We expect the `stages.yml` and referenced files inside the stages directory.
160
+ basePath: str = "stages"
161
+ stagesObj: stages = stages(basePath)
162
+ print(stagesObj)
163
+
164
+ vmObj.run_stages(stagesObj)
165
+
166
+ print("All stages done. Exiting...")
167
+ conn.close()
168
+ exit(0)
169
+ ```
170
+
171
+ ### Stages
172
+ Stages are defined as a YAML file. The schema for it is available under [`stages_schema.yml`](stages_schema.yml).
173
+ The following shows an example of such a file:
174
+ ```yaml
175
+ stages:
176
+ - stage: Bootloader Selection
177
+ timeout_s: 15
178
+ paths:
179
+ - path:
180
+ checks:
181
+ - file:
182
+ path: 0.png
183
+ mse_leq: 0.1
184
+ ssim_geq: 0.99
185
+ actions:
186
+ - keyboard_key:
187
+ value: up
188
+ duration_s: 0.25
189
+ - keyboard_key:
190
+ value: ret
191
+ duration_s: 0.25
192
+ nextStage: Installation Started
193
+ - path:
194
+ checks:
195
+ - file:
196
+ path: 0_1.png
197
+ mse_leq: 0.1
198
+ ssim_geq: 0.99
199
+ actions:
200
+ - keyboard_key:
201
+ value: up
202
+ duration_s: 0.25
203
+ - keyboard_key:
204
+ value: up
205
+ duration_s: 0.25
206
+ - keyboard_key:
207
+ value: ret
208
+ duration_s: 0.25
209
+ nextStage: Installation Started
210
+
211
+ - stage: Installation Started
212
+ timeout_s: 600
213
+ paths:
214
+ - path:
215
+ checks:
216
+ - file:
217
+ path: 1.png
218
+ mse_leq: 0.1
219
+ ssim_geq: 0.99
220
+ actions:
221
+ - keyboard_key:
222
+ value: up
223
+ duration_s: 0.25
224
+ nextStage: Installation Complete
225
+
226
+ - stage: Installation Complete
227
+ timeout_s: 600
228
+ paths:
229
+ - path:
230
+ checks:
231
+ - file:
232
+ path: 2.png
233
+ mse_leq: 0.1
234
+ ssim_geq: 0.99
235
+ actions:
236
+ - keyboard_key:
237
+ value: tab
238
+ duration_s: 0.25
239
+ - keyboard_key:
240
+ value: tab
241
+ duration_s: 0.25
242
+ - keyboard_key:
243
+ value: ret
244
+ duration_s: 0.25
245
+ nextStage: Enter LUKS Password
246
+
247
+ - stage: Enter LUKS Password
248
+ timeout_s: 600
249
+ paths:
250
+ - path:
251
+ checks:
252
+ - file:
253
+ path: 3.png
254
+ mse_leq: 0.1
255
+ ssim_geq: 0.99
256
+ actions:
257
+ - keyboard_text:
258
+ value: something
259
+ duration_s: 0.25
260
+ - keyboard_key:
261
+ value: ret
262
+ duration_s: 0.25
263
+ nextStage: None
264
+
265
+ ```
266
+
267
+ ## Building the pip-Package
268
+
269
+ To build the pip package run:
270
+ ```bash
271
+ rm -rf dist/
272
+ python3 -m build
273
+ ```
274
+ The output is then available inside the `dist/` directory.
275
+
276
+ ## Upload
277
+ ```bash
278
+ twine upload dist/*
279
+ ```
280
+
281
+ ## pre-commit
282
+ Before committing you have to run `pre-commit` to check for linting and type errors.
283
+ For this first install `pre-commit`.
284
+
285
+ ```bash
286
+ dnf install pre-commit
287
+ pre-commit install
288
+ ```
289
+
290
+ To run `pre-commit` manually run:
291
+ ```bash
292
+ pre-commit run --all-files
293
+ ```
@@ -0,0 +1,9 @@
1
+ os_tester/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ os_tester/debug_plot.py,sha256=czTzWOanbqB46Q1_KDPT0u36dnGQ_yBNv7f5Tr1Hxhs,2706
3
+ os_tester/stages.py,sha256=6y19TfzDhlGayoREXSvEJohQlMZJcIvRxTvoZZL7x0I,3778
4
+ os_tester/vm.py,sha256=7X4nq0_IYdlVqOonNvncpF4rFIk-OiYrZ7N1FTD_oZE,17225
5
+ os_tester-1.1.0.dev8.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
6
+ os_tester-1.1.0.dev8.dist-info/METADATA,sha256=zmcFiIIuGfzpA2DWYb7A8v9YFlB7XoFWsy8NWQljEEE,8556
7
+ os_tester-1.1.0.dev8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ os_tester-1.1.0.dev8.dist-info/top_level.txt,sha256=pgV5qkt2c8hLJbwyn1zZCG1Vq0YgJN25QoubK25YyDQ,10
9
+ os_tester-1.1.0.dev8.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+