line |
stmt |
code |
1
|
|
# Copyright 2018-2021 SUSE LLC |
2
|
|
# SPDX-License-Identifier: GPL-2.0-or-later |
3
|
|
|
4
|
|
=head2 OpenQA::Qemu::BlockDev |
5
|
|
|
6
|
|
Represents a QEMU block device in a block device chain. In the diagram below |
7
|
|
the letter in brackets is the node_name. |
8
|
|
|
9
|
|
[A] <- [B] <- [C] |
10
|
|
|
11
|
|
If we are node B then backing_file is node A and overlay is node C (the |
12
|
|
"active layer"). See qemu/docs/interop/live-block-operations.rst for an |
13
|
|
explanation of the terminology used. |
14
|
|
|
15
|
|
The last BlockDev in a chain is associated with a DriveDevice. |
16
|
|
|
17
|
|
The chain is essentially a doubly linked list. To avoid circular reference |
18
|
|
between the overlay and backing_file properties, the overlay property is |
19
|
|
weakened. |
20
|
|
|
21
|
|
=cut |
22
|
|
|
23
|
|
use Mojo::Base 'OpenQA::Qemu::MutParams', -signatures; |
24
|
16
|
|
|
16
|
|
|
16
|
|
25
|
|
use Scalar::Util 'weaken'; |
26
|
16
|
use OpenQA::Qemu::SnapshotConf; |
|
16
|
|
|
16
|
|
27
|
16
|
|
|
16
|
|
|
16
|
|
28
|
|
use constant FILE_POSTFIX => '-file'; |
29
|
16
|
|
|
16
|
|
|
16
|
|
30
|
|
=head3 driver |
31
|
|
|
32
|
|
The file format and software layer used to power the block device. Usually we |
33
|
|
use qcow2; in fact only the backing files for ISOs and firmwares should be in |
34
|
|
any other format. |
35
|
|
|
36
|
|
=cut |
37
|
|
has driver => 'qcow2'; |
38
|
|
|
39
|
|
=head3 file |
40
|
|
|
41
|
|
The file name for the file which holds the block devices data and meta data. |
42
|
|
e.g. hd0-overlay0.qcow2. QEMU allows none file like objects to be used as the data |
43
|
|
store, including other block devices, but we hide that fact for now. |
44
|
|
|
45
|
|
=cut |
46
|
|
has 'file'; |
47
|
|
|
48
|
|
=head3 backing_file |
49
|
|
|
50
|
|
A link to another OpenQA::Qemu::BlockDev object which represents the backing |
51
|
|
file for this object. Can be undefined in which case this is the base layer. |
52
|
|
This is the inverse of overlay. |
53
|
|
|
54
|
|
=cut |
55
|
|
has 'backing_file'; |
56
|
|
|
57
|
|
=head3 node_name |
58
|
|
|
59
|
|
An alphanumeric ID for this block device which allows us to identify it to |
60
|
|
QEMU. |
61
|
|
|
62
|
|
=cut |
63
|
|
has 'node_name'; |
64
|
|
has 'size'; |
65
|
|
|
66
|
|
=head3 overlay |
67
|
|
|
68
|
|
A link to another OpenQA::Qemu::BlockDev object which represents the overlay on |
69
|
|
top of this block device. This can be undefined in which case this is the |
70
|
|
'active layer', that is, the block device which QEMU is writing to (reads may |
71
|
|
still be taken from the backing files in the chain). |
72
|
|
|
73
|
|
To avoid memory leaks we weaken this reference. |
74
|
|
|
75
|
|
=cut |
76
|
|
return $self->{overlay} unless defined $ol; |
77
|
170
|
$self->{overlay} = $ol; |
|
170
|
|
|
170
|
|
|
170
|
|
78
|
170
|
weaken($self->{overlay}); |
79
|
170
|
|
80
|
170
|
return $self; |
81
|
|
} |
82
|
170
|
|
83
|
|
=head3 needs_creating |
84
|
|
|
85
|
|
If true then the blockdevice's data file does not exist yet and needs to be created |
86
|
|
(probably by qemu-img). |
87
|
|
|
88
|
|
=cut |
89
|
|
has needs_creating => 0; |
90
|
|
|
91
|
|
=head3 implicit |
92
|
|
|
93
|
|
True if this is a backing file and it can only be referenced by the overlay's |
94
|
|
qcow2 image. If set to false then we can explicitly declare it on the command |
95
|
|
line for later reference by its node name. |
96
|
|
|
97
|
|
Files inside the factory/hdd folder can't be explicitly referenced because it |
98
|
|
results in a permission error. |
99
|
|
|
100
|
|
=cut |
101
|
|
has implicit => 0; |
102
|
|
|
103
|
|
=head3 snapshot |
104
|
|
|
105
|
|
The snapshot which this overlay belongs to or was created for. |
106
|
|
|
107
|
|
If this is set to an empty snapshot then this blockdevice does not belong to |
108
|
|
any snapshot. |
109
|
|
|
110
|
|
=cut |
111
|
|
has snapshot => sub ($self) { OpenQA::Qemu::Snapshot->new() }; |
112
|
|
|
113
|
|
# See MutParams.pm |
114
|
|
my @cmdl = (); |
115
|
|
|
116
|
67
|
# The first blockdev defines the data store, we only use files, but in |
|
67
|
|
|
67
|
|
117
|
67
|
# theory it could be a http address, ISCSI or a link to an object store |
118
|
|
# item (like Ceph). |
119
|
|
push(@cmdl, ('-blockdev', |
120
|
|
join(',', ('driver=file', |
121
|
|
'node-name=' . $self->node_name . FILE_POSTFIX, |
122
|
67
|
'filename=' . $self->file, |
123
|
|
'cache.no-flush=on')))); |
124
|
|
# The second blockdev tells QEMU what format we are using i.e. qcow2. |
125
|
|
push(@cmdl, ('-blockdev', |
126
|
|
join(',', ('driver=' . $self->driver, |
127
|
|
'node-name=' . $self->node_name, |
128
|
67
|
'file=' . $self->node_name . FILE_POSTFIX, |
129
|
|
'cache.no-flush=on')))); |
130
|
|
|
131
|
|
return @cmdl; |
132
|
|
} |
133
|
|
|
134
|
67
|
=head3 gen_qemu_img_cmdlines |
135
|
|
|
136
|
|
Generate the qemu-img command line to create this blockdevice, if it needs |
137
|
|
creating. This is a recursive function which returns an array of command lines |
138
|
|
for the entire blockdevice chain or an empty array if it does not need |
139
|
|
creating. |
140
|
|
|
141
|
|
=cut |
142
|
|
my $backing_file = $self->backing_file; |
143
|
|
my @cmdlns = defined $backing_file ? $backing_file->gen_qemu_img_cmdlines : (); |
144
|
|
return @cmdlns unless $self->needs_creating; |
145
|
68
|
|
|
68
|
|
|
68
|
|
146
|
68
|
my @params = ('create', '-f', $self->driver); |
147
|
68
|
push(@params, '-F', $backing_file->driver, '-b', $backing_file->file) |
148
|
68
|
if defined $backing_file; |
149
|
|
push(@params, $self->file); |
150
|
44
|
push(@params, $self->size); |
151
|
44
|
|
152
|
|
return (@cmdlns, \@params); |
153
|
44
|
} |
154
|
44
|
|
155
|
|
=head 3 gen_unlink_list |
156
|
44
|
|
157
|
|
If any image file in the chain is marked as needs_creating, but already exists |
158
|
|
this will return it in an array so that the caller can unlink it. |
159
|
|
|
160
|
|
=cut |
161
|
|
return () unless $self->needs_creating; |
162
|
|
return ($self->file, $self->backing_file->gen_unlink_list()) |
163
|
|
if defined $self->backing_file; |
164
|
|
return ($self->file); |
165
|
36
|
} |
|
36
|
|
|
36
|
|
166
|
36
|
|
167
|
28
|
return {driver => $self->driver, |
168
|
|
file => $self->file, |
169
|
20
|
node_name => $self->node_name, |
170
|
|
size => $self->size, |
171
|
|
needs_creating => $self->needs_creating, |
172
|
174
|
implicit => $self->implicit, |
|
174
|
|
|
174
|
|
173
|
174
|
snapshot => $self->snapshot->sequence}; |
174
|
|
} |
175
|
|
|
176
|
|
my ($this, @rest) = @$drives; |
177
|
|
|
178
|
|
$self->backing_file(OpenQA::Qemu::BlockDev->new()->_from_map(\@rest, $snap_conf)) |
179
|
|
if @rest > 0; |
180
|
|
|
181
|
|
return $self->driver($this->{driver}) |
182
|
164
|
->file($this->{file}) |
|
164
|
|
|
164
|
|
|
164
|
|
|
164
|
|
183
|
164
|
->node_name($this->{node_name}) |
184
|
|
->size($this->{size}) |
185
|
164
|
->needs_creating($this->{needs_creating}) |
186
|
|
->implicit($this->{implicit}) |
187
|
|
->snapshot($snap_conf->get_snapshot(sequence => $this->{snapshot})); |
188
|
|
} |
189
|
|
|
190
|
|
|
191
|
|
|
192
|
|
1; |