File Coverage

OpenQA/Qemu/BlockDev.pm
Criterion Covered Total %
statement 60 64 93.7
total 60 64 93.7


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;