aboutsummaryrefslogtreecommitdiff
path: root/tests/qemu-iotests/258
blob: cfd536d6dced97f9c3dde648ae1578a2f78900cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/usr/bin/env python3
# group: rw quick
#
# Very specific tests for adjacent commit/stream block jobs
#
# Copyright (C) 2019 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Creator/Owner: Hanna Reitz <hreitz@redhat.com>

import iotests
from iotests import log, qemu_img, qemu_io_silent, \
        filter_qmp_testfiles, filter_qmp_imgfmt

# Returns a node for blockdev-add
def node(node_name, path, backing=None, fmt=None, throttle=None):
    if fmt is None:
        fmt = iotests.imgfmt

    res = {
        'node-name': node_name,
        'driver': fmt,
        'file': {
            'driver': 'file',
            'filename': path
        }
    }

    if backing is not None:
        res['backing'] = backing

    if throttle:
        res['file'] = {
            'driver': 'throttle',
            'throttle-group': throttle,
            'file': res['file']
        }

    return res

# Finds a node in the debug block graph
def find_graph_node(graph, node_id):
    return next(node for node in graph['nodes'] if node['id'] == node_id)


def test_concurrent_finish(write_to_stream_node):
    log('')
    log('=== Commit and stream finish concurrently (letting %s write) ===' % \
        ('stream' if write_to_stream_node else 'commit'))
    log('')

    # All chosen in such a way that when the commit job wants to
    # finish, it polls and thus makes stream finish concurrently --
    # and the other way around, depending on whether the commit job
    # is finalized before stream completes or not.

    with iotests.FilePath('node4.img') as node4_path, \
         iotests.FilePath('node3.img') as node3_path, \
         iotests.FilePath('node2.img') as node2_path, \
         iotests.FilePath('node1.img') as node1_path, \
         iotests.FilePath('node0.img') as node0_path, \
         iotests.VM() as vm:

        # It is important to use raw for the base layer (so that
        # permissions are just handed through to the protocol layer)
        qemu_img('create', '-f', 'raw', node0_path, '64M')

        stream_throttle=None
        commit_throttle=None

        for path in [node1_path, node2_path, node3_path, node4_path]:
            qemu_img('create', '-f', iotests.imgfmt, path, '64M')

        if write_to_stream_node:
            # This is what (most of the time) makes commit finish
            # earlier and then pull in stream
            assert qemu_io_silent(node2_path,
                                  '-c', 'write %iK 64K' % (65536 - 192),
                                  '-c', 'write %iK 64K' % (65536 -  64)) == 0

            stream_throttle='tg'
        else:
            # And this makes stream finish earlier
            assert qemu_io_silent(node1_path,
                                  '-c', 'write %iK 64K' % (65536 - 64)) == 0

            commit_throttle='tg'

        vm.launch()

        vm.qmp_log('object-add',
                   qom_type='throttle-group',
                   id='tg',
                   limits={
                       'iops-write': 1,
                       'iops-write-max': 1
                   })

        vm.qmp_log('blockdev-add',
                   filters=[filter_qmp_testfiles, filter_qmp_imgfmt],
                   **node('node4', node4_path, throttle=stream_throttle,
                     backing=node('node3', node3_path,
                     backing=node('node2', node2_path,
                     backing=node('node1', node1_path,
                     backing=node('node0', node0_path, throttle=commit_throttle,
                                  fmt='raw'))))))

        vm.qmp_log('block-commit',
                   job_id='commit',
                   device='node4',
                   filter_node_name='commit-filter',
                   top_node='node1',
                   base_node='node0',
                   auto_finalize=False)

        vm.qmp_log('block-stream',
                   job_id='stream',
                   device='node3',
                   base_node='commit-filter')

        if write_to_stream_node:
            vm.run_job('commit', auto_finalize=False, auto_dismiss=True)
            vm.run_job('stream', auto_finalize=True, auto_dismiss=True)
        else:
            # No, the jobs do not really finish concurrently here,
            # the stream job does complete strictly before commit.
            # But still, this is close enough for what we want to
            # test.
            vm.run_job('stream', auto_finalize=True, auto_dismiss=True)
            vm.run_job('commit', auto_finalize=False, auto_dismiss=True)

        # Assert that the backing node of node3 is node 0 now
        graph = vm.qmp('x-debug-query-block-graph')['return']
        for edge in graph['edges']:
            if edge['name'] == 'backing' and \
               find_graph_node(graph, edge['parent'])['name'] == 'node3':
                assert find_graph_node(graph, edge['child'])['name'] == 'node0'
                break


def main():
    log('Running tests:')
    test_concurrent_finish(True)
    test_concurrent_finish(False)

if __name__ == '__main__':
    # Need backing file and change-backing-file support
    iotests.script_main(main,
                        supported_fmts=['qcow2', 'qed'],
                        supported_platforms=['linux'])