]>
Commit | Line | Data |
---|---|---|
9ee2dd4c | 1 | #!/usr/bin/env python3 |
9dd003a9 | 2 | # group: rw auto quick |
c78dc182 HR |
3 | # |
4 | # Test case for ejecting BDSs with block jobs still running on them | |
5 | # | |
9ee2dd4c SH |
6 | # Originally written in bash by Hanna Czenczek, ported to Python by Stefan |
7 | # Hajnoczi. | |
8 | # | |
9 | # Copyright Red Hat | |
c78dc182 HR |
10 | # |
11 | # This program is free software; you can redistribute it and/or modify | |
12 | # it under the terms of the GNU General Public License as published by | |
13 | # the Free Software Foundation; either version 2 of the License, or | |
14 | # (at your option) any later version. | |
15 | # | |
16 | # This program is distributed in the hope that it will be useful, | |
17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | # GNU General Public License for more details. | |
20 | # | |
21 | # You should have received a copy of the GNU General Public License | |
22 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
23 | # | |
24 | ||
9ee2dd4c SH |
25 | import iotests |
26 | ||
27 | # Common filters to mask values that vary in the test output | |
28 | QMP_FILTERS = [iotests.filter_qmp_testfiles, \ | |
29 | iotests.filter_qmp_imgfmt] | |
30 | ||
31 | ||
32 | class TestCase: | |
33 | def __init__(self, name, vm, image_path, cancel_event): | |
34 | self.name = name | |
35 | self.vm = vm | |
36 | self.image_path = image_path | |
37 | self.cancel_event = cancel_event | |
38 | ||
39 | def __enter__(self): | |
40 | iotests.log(f'=== Testing {self.name} ===') | |
41 | self.vm.qmp_log('blockdev-add', \ | |
42 | node_name='drv0', \ | |
43 | driver=iotests.imgfmt, \ | |
44 | file={'driver': 'file', 'filename': self.image_path}, \ | |
45 | filters=QMP_FILTERS) | |
46 | ||
47 | def __exit__(self, *exc_details): | |
48 | # This is expected to fail because the job still exists | |
49 | self.vm.qmp_log('blockdev-del', node_name='drv0', \ | |
50 | filters=[iotests.filter_qmp_generated_node_ids]) | |
51 | ||
52 | self.vm.qmp_log('block-job-cancel', device='job0') | |
53 | event = self.vm.event_wait(self.cancel_event) | |
54 | iotests.log(event, filters=[iotests.filter_qmp_event]) | |
55 | ||
56 | # This time it succeeds | |
57 | self.vm.qmp_log('blockdev-del', node_name='drv0') | |
58 | ||
59 | # Separate test cases in output | |
60 | iotests.log('') | |
61 | ||
62 | ||
63 | def main() -> None: | |
64 | with iotests.FilePath('bottom', 'middle', 'top', 'target') as \ | |
65 | (bottom_path, middle_path, top_path, target_path), \ | |
66 | iotests.VM() as vm: | |
67 | ||
68 | iotests.log('Creating bottom <- middle <- top backing file chain...') | |
69 | IMAGE_SIZE='1M' | |
70 | iotests.qemu_img_create('-f', iotests.imgfmt, bottom_path, IMAGE_SIZE) | |
71 | iotests.qemu_img_create('-f', iotests.imgfmt, \ | |
72 | '-F', iotests.imgfmt, \ | |
73 | '-b', bottom_path, \ | |
74 | middle_path, \ | |
75 | IMAGE_SIZE) | |
76 | iotests.qemu_img_create('-f', iotests.imgfmt, \ | |
77 | '-F', iotests.imgfmt, \ | |
78 | '-b', middle_path, \ | |
79 | top_path, \ | |
80 | IMAGE_SIZE) | |
81 | ||
82 | iotests.log('Starting VM...') | |
83 | vm.add_args('-nodefaults') | |
84 | vm.launch() | |
85 | ||
86 | # drive-backup will not send BLOCK_JOB_READY by itself, and cancelling | |
87 | # the job will consequently result in BLOCK_JOB_CANCELLED being | |
88 | # emitted. | |
89 | with TestCase('drive-backup', vm, top_path, 'BLOCK_JOB_CANCELLED'): | |
90 | vm.qmp_log('drive-backup', \ | |
91 | job_id='job0', \ | |
92 | device='drv0', \ | |
93 | target=target_path, \ | |
94 | format=iotests.imgfmt, \ | |
95 | sync='none', \ | |
96 | filters=QMP_FILTERS) | |
97 | ||
98 | # drive-mirror will send BLOCK_JOB_READY basically immediately, and | |
99 | # cancelling the job will consequently result in BLOCK_JOB_COMPLETED | |
100 | # being emitted. | |
101 | with TestCase('drive-mirror', vm, top_path, 'BLOCK_JOB_COMPLETED'): | |
102 | vm.qmp_log('drive-mirror', \ | |
103 | job_id='job0', \ | |
104 | device='drv0', \ | |
105 | target=target_path, \ | |
106 | format=iotests.imgfmt, \ | |
107 | sync='none', \ | |
108 | filters=QMP_FILTERS) | |
109 | event = vm.event_wait('BLOCK_JOB_READY') | |
110 | assert event is not None # silence mypy | |
111 | iotests.log(event, filters=[iotests.filter_qmp_event]) | |
112 | ||
113 | # An active block-commit will send BLOCK_JOB_READY basically | |
114 | # immediately, and cancelling the job will consequently result in | |
115 | # BLOCK_JOB_COMPLETED being emitted. | |
116 | with TestCase('active block-commit', vm, top_path, \ | |
117 | 'BLOCK_JOB_COMPLETED'): | |
118 | vm.qmp_log('block-commit', \ | |
119 | job_id='job0', \ | |
120 | device='drv0') | |
121 | event = vm.event_wait('BLOCK_JOB_READY') | |
122 | assert event is not None # silence mypy | |
123 | iotests.log(event, filters=[iotests.filter_qmp_event]) | |
124 | ||
125 | # Give block-commit something to work on, otherwise it would be done | |
126 | # immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would | |
127 | # work just fine without the block job still running. | |
128 | iotests.qemu_io(middle_path, '-c', f'write 0 {IMAGE_SIZE}') | |
129 | with TestCase('non-active block-commit', vm, top_path, \ | |
130 | 'BLOCK_JOB_CANCELLED'): | |
131 | vm.qmp_log('block-commit', \ | |
132 | job_id='job0', \ | |
133 | device='drv0', \ | |
134 | top=middle_path, \ | |
135 | speed=1, \ | |
136 | filters=[iotests.filter_qmp_testfiles]) | |
137 | ||
138 | # Give block-stream something to work on, otherwise it would be done | |
139 | # immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would | |
140 | # work just fine without the block job still running. | |
141 | iotests.qemu_io(bottom_path, '-c', f'write 0 {IMAGE_SIZE}') | |
142 | with TestCase('block-stream', vm, top_path, 'BLOCK_JOB_CANCELLED'): | |
143 | vm.qmp_log('block-stream', \ | |
144 | job_id='job0', \ | |
145 | device='drv0', \ | |
146 | speed=1) | |
147 | ||
148 | if __name__ == '__main__': | |
149 | iotests.script_main(main, supported_fmts=['qcow2', 'qed'], | |
150 | supported_protocols=['file']) |