]> git.proxmox.com Git - qemu-server.git/blame - PVE/QemuServer/Drive.pm
bump version to 7.1-1
[qemu-server.git] / PVE / QemuServer / Drive.pm
CommitLineData
e0fd2b2f
FE
1package PVE::QemuServer::Drive;
2
3use strict;
4use warnings;
5
6use PVE::Storage;
7use PVE::JSONSchema qw(get_standard_option);
8
9use base qw(Exporter);
10
11our @EXPORT_OK = qw(
12is_valid_drivename
13drive_is_cloudinit
14drive_is_cdrom
75748d44 15drive_is_read_only
e0fd2b2f
FE
16parse_drive
17print_drive
e0fd2b2f
FE
18);
19
20our $QEMU_FORMAT_RE = qr/raw|cow|qcow|qcow2|qed|vmdk|cloop/;
21
22PVE::JSONSchema::register_standard_option('pve-qm-image-format', {
23 type => 'string',
24 enum => [qw(raw cow qcow qed qcow2 vmdk cloop)],
25 description => "The drive's backing file's data format.",
26 optional => 1,
27});
28
29my $MAX_IDE_DISKS = 4;
30my $MAX_SCSI_DISKS = 31;
31my $MAX_VIRTIO_DISKS = 16;
32our $MAX_SATA_DISKS = 6;
33our $MAX_UNUSED_DISKS = 256;
34
35our $drivedesc_hash;
36
37my %drivedesc_base = (
38 volume => { alias => 'file' },
39 file => {
40 type => 'string',
41 format => 'pve-volume-id-or-qm-path',
42 default_key => 1,
43 format_description => 'volume',
44 description => "The drive's backing volume.",
45 },
46 media => {
47 type => 'string',
48 enum => [qw(cdrom disk)],
49 description => "The drive's media type.",
50 default => 'disk',
51 optional => 1
52 },
53 cyls => {
54 type => 'integer',
55 description => "Force the drive's physical geometry to have a specific cylinder count.",
56 optional => 1
57 },
58 heads => {
59 type => 'integer',
60 description => "Force the drive's physical geometry to have a specific head count.",
61 optional => 1
62 },
63 secs => {
64 type => 'integer',
65 description => "Force the drive's physical geometry to have a specific sector count.",
66 optional => 1
67 },
68 trans => {
69 type => 'string',
70 enum => [qw(none lba auto)],
71 description => "Force disk geometry bios translation mode.",
72 optional => 1,
73 },
74 snapshot => {
75 type => 'boolean',
76 description => "Controls qemu's snapshot mode feature."
77 . " If activated, changes made to the disk are temporary and will"
78 . " be discarded when the VM is shutdown.",
79 optional => 1,
80 },
81 cache => {
82 type => 'string',
83 enum => [qw(none writethrough writeback unsafe directsync)],
84 description => "The drive's cache mode",
85 optional => 1,
86 },
87 format => get_standard_option('pve-qm-image-format'),
88 size => {
89 type => 'string',
90 format => 'disk-size',
91 format_description => 'DiskSize',
92 description => "Disk size. This is purely informational and has no effect.",
93 optional => 1,
94 },
95 backup => {
96 type => 'boolean',
97 description => "Whether the drive should be included when making backups.",
98 optional => 1,
99 },
100 replicate => {
101 type => 'boolean',
102 description => 'Whether the drive should considered for replication jobs.',
103 optional => 1,
104 default => 1,
105 },
106 rerror => {
107 type => 'string',
108 enum => [qw(ignore report stop)],
109 description => 'Read error action.',
110 optional => 1,
111 },
112 werror => {
113 type => 'string',
114 enum => [qw(enospc ignore report stop)],
115 description => 'Write error action.',
116 optional => 1,
117 },
118 aio => {
119 type => 'string',
59e59342 120 enum => [qw(native threads io_uring)],
e0fd2b2f
FE
121 description => 'AIO type to use.',
122 optional => 1,
123 },
124 discard => {
125 type => 'string',
126 enum => [qw(ignore on)],
127 description => 'Controls whether to pass discard/trim requests to the underlying storage.',
128 optional => 1,
129 },
130 detect_zeroes => {
131 type => 'boolean',
132 description => 'Controls whether to detect and try to optimize writes of zeroes.',
133 optional => 1,
134 },
135 serial => {
136 type => 'string',
137 format => 'urlencoded',
138 format_description => 'serial',
139 maxLength => 20*3, # *3 since it's %xx url enoded
140 description => "The drive's reported serial number, url-encoded, up to 20 bytes long.",
141 optional => 1,
142 },
143 shared => {
144 type => 'boolean',
145 description => 'Mark this locally-managed volume as available on all nodes',
146 verbose_description => "Mark this locally-managed volume as available on all nodes.\n\nWARNING: This option does not share the volume automatically, it assumes it is shared already!",
147 optional => 1,
148 default => 0,
149 }
150);
151
152my %iothread_fmt = ( iothread => {
153 type => 'boolean',
154 description => "Whether to use iothreads for this drive",
155 optional => 1,
156});
157
158my %model_fmt = (
159 model => {
160 type => 'string',
161 format => 'urlencoded',
162 format_description => 'model',
163 maxLength => 40*3, # *3 since it's %xx url enoded
164 description => "The drive's reported model name, url-encoded, up to 40 bytes long.",
165 optional => 1,
166 },
167);
168
169my %queues_fmt = (
170 queues => {
171 type => 'integer',
172 description => "Number of queues.",
173 minimum => 2,
174 optional => 1
175 }
176);
177
12e1d472
DC
178my %readonly_fmt = (
179 ro => {
180 type => 'boolean',
181 description => "Whether the drive is read-only.",
182 optional => 1,
183 },
184);
185
e0fd2b2f
FE
186my %scsiblock_fmt = (
187 scsiblock => {
188 type => 'boolean',
189 description => "whether to use scsi-block for full passthrough of host block device\n\nWARNING: can lead to I/O errors in combination with low memory or high memory fragmentation on host",
190 optional => 1,
191 default => 0,
192 },
193);
194
195my %ssd_fmt = (
196 ssd => {
197 type => 'boolean',
198 description => "Whether to expose this drive as an SSD, rather than a rotational hard disk.",
199 optional => 1,
200 },
201);
202
203my %wwn_fmt = (
204 wwn => {
205 type => 'string',
206 pattern => qr/^(0x)[0-9a-fA-F]{16}/,
207 format_description => 'wwn',
208 description => "The drive's worldwide name, encoded as 16 bytes hex string, prefixed by '0x'.",
209 optional => 1,
210 },
211);
212
213my $add_throttle_desc = sub {
214 my ($key, $type, $what, $unit, $longunit, $minimum) = @_;
215 my $d = {
216 type => $type,
217 format_description => $unit,
218 description => "Maximum $what in $longunit.",
219 optional => 1,
220 };
221 $d->{minimum} = $minimum if defined($minimum);
222 $drivedesc_base{$key} = $d;
223};
224# throughput: (leaky bucket)
225$add_throttle_desc->('bps', 'integer', 'r/w speed', 'bps', 'bytes per second');
226$add_throttle_desc->('bps_rd', 'integer', 'read speed', 'bps', 'bytes per second');
227$add_throttle_desc->('bps_wr', 'integer', 'write speed', 'bps', 'bytes per second');
228$add_throttle_desc->('mbps', 'number', 'r/w speed', 'mbps', 'megabytes per second');
229$add_throttle_desc->('mbps_rd', 'number', 'read speed', 'mbps', 'megabytes per second');
230$add_throttle_desc->('mbps_wr', 'number', 'write speed', 'mbps', 'megabytes per second');
231$add_throttle_desc->('iops', 'integer', 'r/w I/O', 'iops', 'operations per second');
232$add_throttle_desc->('iops_rd', 'integer', 'read I/O', 'iops', 'operations per second');
233$add_throttle_desc->('iops_wr', 'integer', 'write I/O', 'iops', 'operations per second');
234
235# pools: (pool of IO before throttling starts taking effect)
236$add_throttle_desc->('mbps_max', 'number', 'unthrottled r/w pool', 'mbps', 'megabytes per second');
237$add_throttle_desc->('mbps_rd_max', 'number', 'unthrottled read pool', 'mbps', 'megabytes per second');
238$add_throttle_desc->('mbps_wr_max', 'number', 'unthrottled write pool', 'mbps', 'megabytes per second');
239$add_throttle_desc->('iops_max', 'integer', 'unthrottled r/w I/O pool', 'iops', 'operations per second');
240$add_throttle_desc->('iops_rd_max', 'integer', 'unthrottled read I/O pool', 'iops', 'operations per second');
241$add_throttle_desc->('iops_wr_max', 'integer', 'unthrottled write I/O pool', 'iops', 'operations per second');
242
243# burst lengths
244$add_throttle_desc->('bps_max_length', 'integer', 'length of I/O bursts', 'seconds', 'seconds', 1);
245$add_throttle_desc->('bps_rd_max_length', 'integer', 'length of read I/O bursts', 'seconds', 'seconds', 1);
246$add_throttle_desc->('bps_wr_max_length', 'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
247$add_throttle_desc->('iops_max_length', 'integer', 'length of I/O bursts', 'seconds', 'seconds', 1);
248$add_throttle_desc->('iops_rd_max_length', 'integer', 'length of read I/O bursts', 'seconds', 'seconds', 1);
249$add_throttle_desc->('iops_wr_max_length', 'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
250
251# legacy support
252$drivedesc_base{'bps_rd_length'} = { alias => 'bps_rd_max_length' };
253$drivedesc_base{'bps_wr_length'} = { alias => 'bps_wr_max_length' };
254$drivedesc_base{'iops_rd_length'} = { alias => 'iops_rd_max_length' };
255$drivedesc_base{'iops_wr_length'} = { alias => 'iops_wr_max_length' };
256
257my $ide_fmt = {
258 %drivedesc_base,
259 %model_fmt,
260 %ssd_fmt,
261 %wwn_fmt,
262};
263PVE::JSONSchema::register_format("pve-qm-ide", $ide_fmt);
264
98c3d99e
FE
265my $ALLOCATION_SYNTAX_DESC =
266 "Use the special syntax STORAGE_ID:SIZE_IN_GiB to allocate a new volume.";
267
e0fd2b2f
FE
268my $idedesc = {
269 optional => 1,
270 type => 'string', format => $ide_fmt,
98c3d99e
FE
271 description => "Use volume as IDE hard disk or CD-ROM (n is 0 to " .($MAX_IDE_DISKS -1) . "). " .
272 $ALLOCATION_SYNTAX_DESC,
e0fd2b2f
FE
273};
274PVE::JSONSchema::register_standard_option("pve-qm-ide", $idedesc);
275
276my $scsi_fmt = {
277 %drivedesc_base,
278 %iothread_fmt,
279 %queues_fmt,
12e1d472 280 %readonly_fmt,
e0fd2b2f
FE
281 %scsiblock_fmt,
282 %ssd_fmt,
283 %wwn_fmt,
284};
285my $scsidesc = {
286 optional => 1,
287 type => 'string', format => $scsi_fmt,
98c3d99e
FE
288 description => "Use volume as SCSI hard disk or CD-ROM (n is 0 to " . ($MAX_SCSI_DISKS - 1) . "). " .
289 $ALLOCATION_SYNTAX_DESC,
e0fd2b2f
FE
290};
291PVE::JSONSchema::register_standard_option("pve-qm-scsi", $scsidesc);
292
293my $sata_fmt = {
294 %drivedesc_base,
295 %ssd_fmt,
296 %wwn_fmt,
297};
298my $satadesc = {
299 optional => 1,
300 type => 'string', format => $sata_fmt,
98c3d99e
FE
301 description => "Use volume as SATA hard disk or CD-ROM (n is 0 to " . ($MAX_SATA_DISKS - 1). "). " .
302 $ALLOCATION_SYNTAX_DESC,
e0fd2b2f
FE
303};
304PVE::JSONSchema::register_standard_option("pve-qm-sata", $satadesc);
305
306my $virtio_fmt = {
307 %drivedesc_base,
308 %iothread_fmt,
12e1d472 309 %readonly_fmt,
e0fd2b2f
FE
310};
311my $virtiodesc = {
312 optional => 1,
313 type => 'string', format => $virtio_fmt,
98c3d99e
FE
314 description => "Use volume as VIRTIO hard disk (n is 0 to " . ($MAX_VIRTIO_DISKS - 1) . "). " .
315 $ALLOCATION_SYNTAX_DESC,
e0fd2b2f
FE
316};
317PVE::JSONSchema::register_standard_option("pve-qm-virtio", $virtiodesc);
318
b5099b4f
SR
319my %efitype_fmt = (
320 efitype => {
321 type => 'string',
322 enum => [qw(2m 4m)],
323 description => "Size and type of the OVMF EFI vars. '4m' is newer and recommended,"
324 . " and required for Secure Boot. For backwards compatibility, '2m' is used"
325 . " if not otherwise specified.",
326 optional => 1,
327 default => '2m',
328 },
a064e511 329 'pre-enrolled-keys' => {
b5099b4f 330 type => 'boolean',
a064e511
TL
331 description => "Use am EFI vars template with distribution-specific and Microsoft Standard"
332 ." keys enrolled, if used with 'efitype=4m'. Note that this will enable Secure Boot by"
333 ." default, though it can still be turned off from within the VM.",
b5099b4f
SR
334 optional => 1,
335 default => 0,
336 },
337);
338
e0fd2b2f
FE
339my $efidisk_fmt = {
340 volume => { alias => 'file' },
341 file => {
342 type => 'string',
343 format => 'pve-volume-id-or-qm-path',
344 default_key => 1,
345 format_description => 'volume',
346 description => "The drive's backing volume.",
347 },
348 format => get_standard_option('pve-qm-image-format'),
349 size => {
350 type => 'string',
351 format => 'disk-size',
352 format_description => 'DiskSize',
353 description => "Disk size. This is purely informational and has no effect.",
354 optional => 1,
355 },
b5099b4f 356 %efitype_fmt,
e0fd2b2f
FE
357};
358
359my $efidisk_desc = {
360 optional => 1,
361 type => 'string', format => $efidisk_fmt,
98c3d99e
FE
362 description => "Configure a Disk for storing EFI vars. " .
363 $ALLOCATION_SYNTAX_DESC . " Note that SIZE_IN_GiB is ignored here " .
364 "and that the default EFI vars are copied to the volume instead.",
e0fd2b2f
FE
365};
366
367PVE::JSONSchema::register_standard_option("pve-qm-efidisk", $efidisk_desc);
368
f9dde219
SR
369my %tpmversion_fmt = (
370 version => {
371 type => 'string',
372 enum => [qw(v1.2 v2.0)],
2b9ee944
TL
373 description => "The TPM interface version. v2.0 is newer and should be preferred."
374 ." Note that this cannot be changed later on.",
f9dde219
SR
375 optional => 1,
376 default => 'v2.0',
377 },
378);
379my $tpmstate_fmt = {
380 volume => { alias => 'file' },
381 file => {
382 type => 'string',
383 format => 'pve-volume-id-or-qm-path',
384 default_key => 1,
385 format_description => 'volume',
386 description => "The drive's backing volume.",
387 },
388 size => {
389 type => 'string',
390 format => 'disk-size',
391 format_description => 'DiskSize',
392 description => "Disk size. This is purely informational and has no effect.",
393 optional => 1,
394 },
395 %tpmversion_fmt,
396};
397my $tpmstate_desc = {
398 optional => 1,
399 type => 'string', format => $tpmstate_fmt,
400 description => "Configure a Disk for storing TPM state. " .
401 $ALLOCATION_SYNTAX_DESC . " Note that SIZE_IN_GiB is ignored here " .
402 "and that the default size of 4 MiB will always be used instead. The " .
403 "format is also fixed to 'raw'.",
404};
405use constant TPMSTATE_DISK_SIZE => 4 * 1024 * 1024;
406
407my $alldrive_fmt = {
408 %drivedesc_base,
409 %iothread_fmt,
410 %model_fmt,
411 %queues_fmt,
12e1d472 412 %readonly_fmt,
f9dde219
SR
413 %scsiblock_fmt,
414 %ssd_fmt,
415 %wwn_fmt,
416 %tpmversion_fmt,
b5099b4f 417 %efitype_fmt,
f9dde219
SR
418};
419
43c4c7b6
FE
420my $unused_fmt = {
421 volume => { alias => 'file' },
422 file => {
423 type => 'string',
424 format => 'pve-volume-id',
425 default_key => 1,
426 format_description => 'volume',
427 description => "The drive's backing volume.",
428 },
429};
430
431my $unuseddesc = {
432 optional => 1,
433 type => 'string', format => $unused_fmt,
434 description => "Reference to unused volumes. This is used internally, and should not be modified manually.",
435};
436
e0fd2b2f
FE
437for (my $i = 0; $i < $MAX_IDE_DISKS; $i++) {
438 $drivedesc_hash->{"ide$i"} = $idedesc;
439}
440
441for (my $i = 0; $i < $MAX_SATA_DISKS; $i++) {
442 $drivedesc_hash->{"sata$i"} = $satadesc;
443}
444
445for (my $i = 0; $i < $MAX_SCSI_DISKS; $i++) {
446 $drivedesc_hash->{"scsi$i"} = $scsidesc;
447}
448
449for (my $i = 0; $i < $MAX_VIRTIO_DISKS; $i++) {
450 $drivedesc_hash->{"virtio$i"} = $virtiodesc;
451}
452
453$drivedesc_hash->{efidisk0} = $efidisk_desc;
f9dde219 454$drivedesc_hash->{tpmstate0} = $tpmstate_desc;
e0fd2b2f 455
43c4c7b6
FE
456for (my $i = 0; $i < $MAX_UNUSED_DISKS; $i++) {
457 $drivedesc_hash->{"unused$i"} = $unuseddesc;
458}
e0fd2b2f
FE
459
460sub valid_drive_names {
461 # order is important - used to autoselect boot disk
462 return ((map { "ide$_" } (0 .. ($MAX_IDE_DISKS - 1))),
463 (map { "scsi$_" } (0 .. ($MAX_SCSI_DISKS - 1))),
464 (map { "virtio$_" } (0 .. ($MAX_VIRTIO_DISKS - 1))),
465 (map { "sata$_" } (0 .. ($MAX_SATA_DISKS - 1))),
f9dde219
SR
466 'efidisk0',
467 'tpmstate0');
e0fd2b2f
FE
468}
469
10713730
AL
470sub valid_drive_names_with_unused {
471 return (valid_drive_names(), map {"unused$_"} (0 .. ($MAX_UNUSED_DISKS - 1)));
472}
473
e0fd2b2f
FE
474sub is_valid_drivename {
475 my $dev = shift;
476
43c4c7b6 477 return defined($drivedesc_hash->{$dev}) && $dev !~ /^unused\d+$/;
e0fd2b2f
FE
478}
479
480PVE::JSONSchema::register_format('pve-qm-bootdisk', \&verify_bootdisk);
481sub verify_bootdisk {
482 my ($value, $noerr) = @_;
483
484 return $value if is_valid_drivename($value);
485
d1c1af4b 486 return if $noerr;
e0fd2b2f
FE
487
488 die "invalid boot disk '$value'\n";
489}
490
491sub drive_is_cloudinit {
492 my ($drive) = @_;
493 return $drive->{file} =~ m@[:/]vm-\d+-cloudinit(?:\.$QEMU_FORMAT_RE)?$@;
494}
495
496sub drive_is_cdrom {
497 my ($drive, $exclude_cloudinit) = @_;
498
499 return 0 if $exclude_cloudinit && drive_is_cloudinit($drive);
500
501 return $drive && $drive->{media} && ($drive->{media} eq 'cdrom');
502}
503
75748d44
FG
504sub drive_is_read_only {
505 my ($conf, $drive) = @_;
506
507 return 0 if !PVE::QemuConfig->is_template($conf);
508
509 # don't support being marked read-only
510 return $drive->{interface} ne 'sata' && $drive->{interface} ne 'ide';
511}
512
e0fd2b2f
FE
513# ideX = [volume=]volume-id[,media=d][,cyls=c,heads=h,secs=s[,trans=t]]
514# [,snapshot=on|off][,cache=on|off][,format=f][,backup=yes|no]
515# [,rerror=ignore|report|stop][,werror=enospc|ignore|report|stop]
516# [,aio=native|threads][,discard=ignore|on][,detect_zeroes=on|off]
517# [,iothread=on][,serial=serial][,model=model]
518
519sub parse_drive {
520 my ($key, $data) = @_;
521
522 my ($interface, $index);
523
524 if ($key =~ m/^([^\d]+)(\d+)$/) {
525 $interface = $1;
526 $index = $2;
527 } else {
d1c1af4b 528 return;
e0fd2b2f
FE
529 }
530
43c4c7b6 531 if (!defined($drivedesc_hash->{$key})) {
e0fd2b2f 532 warn "invalid drive key: $key\n";
d1c1af4b 533 return;
e0fd2b2f 534 }
43c4c7b6
FE
535
536 my $desc = $drivedesc_hash->{$key}->{format};
e0fd2b2f 537 my $res = eval { PVE::JSONSchema::parse_property_string($desc, $data) };
d1c1af4b 538 return if !$res;
e0fd2b2f
FE
539 $res->{interface} = $interface;
540 $res->{index} = $index;
541
542 my $error = 0;
543 foreach my $opt (qw(bps bps_rd bps_wr)) {
544 if (my $bps = defined(delete $res->{$opt})) {
545 if (defined($res->{"m$opt"})) {
546 warn "both $opt and m$opt specified\n";
547 ++$error;
548 next;
549 }
550 $res->{"m$opt"} = sprintf("%.3f", $bps / (1024*1024.0));
551 }
552 }
553
554 # can't use the schema's 'requires' because of the mbps* => bps* "transforming aliases"
555 for my $requirement (
556 [mbps_max => 'mbps'],
557 [mbps_rd_max => 'mbps_rd'],
558 [mbps_wr_max => 'mbps_wr'],
559 [miops_max => 'miops'],
560 [miops_rd_max => 'miops_rd'],
561 [miops_wr_max => 'miops_wr'],
562 [bps_max_length => 'mbps_max'],
563 [bps_rd_max_length => 'mbps_rd_max'],
564 [bps_wr_max_length => 'mbps_wr_max'],
565 [iops_max_length => 'iops_max'],
566 [iops_rd_max_length => 'iops_rd_max'],
567 [iops_wr_max_length => 'iops_wr_max']) {
568 my ($option, $requires) = @$requirement;
569 if ($res->{$option} && !$res->{$requires}) {
570 warn "$option requires $requires\n";
571 ++$error;
572 }
573 }
574
d1c1af4b 575 return if $error;
e0fd2b2f 576
d1c1af4b
TL
577 return if $res->{mbps_rd} && $res->{mbps};
578 return if $res->{mbps_wr} && $res->{mbps};
579 return if $res->{iops_rd} && $res->{iops};
580 return if $res->{iops_wr} && $res->{iops};
e0fd2b2f
FE
581
582 if ($res->{media} && ($res->{media} eq 'cdrom')) {
d1c1af4b
TL
583 return if $res->{snapshot} || $res->{trans} || $res->{format};
584 return if $res->{heads} || $res->{secs} || $res->{cyls};
585 return if $res->{interface} eq 'virtio';
e0fd2b2f
FE
586 }
587
588 if (my $size = $res->{size}) {
d1c1af4b 589 return if !defined($res->{size} = PVE::JSONSchema::parse_size($size));
e0fd2b2f
FE
590 }
591
592 return $res;
593}
594
595sub print_drive {
596 my ($drive) = @_;
597 my $skip = [ 'index', 'interface' ];
598 return PVE::JSONSchema::print_property_string($drive, $alldrive_fmt, $skip);
599}
600
2141a802
SR
601sub get_bootdisks {
602 my ($conf) = @_;
603
f7d1505b
TL
604 my $bootcfg;
605 $bootcfg = PVE::JSONSchema::parse_property_string('pve-qm-boot', $conf->{boot}) if $conf->{boot};
2141a802
SR
606
607 if (!defined($bootcfg) || $bootcfg->{legacy}) {
608 return [$conf->{bootdisk}] if $conf->{bootdisk};
609 return [];
610 }
611
612 my @list = PVE::Tools::split_list($bootcfg->{order});
613 @list = grep {is_valid_drivename($_)} @list;
614 return \@list;
615}
616
776c5f50 617sub bootdisk_size {
e0fd2b2f
FE
618 my ($storecfg, $conf) = @_;
619
2141a802 620 my $bootdisks = get_bootdisks($conf);
d1c1af4b 621 return if !@$bootdisks;
30664f14
DC
622 for my $bootdisk (@$bootdisks) {
623 next if !is_valid_drivename($bootdisk);
624 next if !$conf->{$bootdisk};
625 my $drive = parse_drive($bootdisk, $conf->{$bootdisk});
626 next if !defined($drive);
627 next if drive_is_cdrom($drive);
628 my $volid = $drive->{file};
629 next if !$volid;
630 return $drive->{size};
631 }
e0fd2b2f 632
30664f14 633 return;
e0fd2b2f
FE
634}
635
636sub update_disksize {
9b29cbd0 637 my ($drive, $newsize) = @_;
e0fd2b2f 638
d1c1af4b 639 return if !defined($newsize);
e0fd2b2f 640
63e313f3 641 my $oldsize = $drive->{size} // 0;
e0fd2b2f 642
63e313f3 643 if ($newsize != $oldsize) {
e0fd2b2f
FE
644 $drive->{size} = $newsize;
645
646 my $old_fmt = PVE::JSONSchema::format_size($oldsize);
647 my $new_fmt = PVE::JSONSchema::format_size($newsize);
648
9b29cbd0
FE
649 my $msg = "size of disk '$drive->{file}' updated from $old_fmt to $new_fmt";
650
651 return ($drive, $msg);
e0fd2b2f
FE
652 }
653
d1c1af4b 654 return;
e0fd2b2f
FE
655}
656
657sub is_volume_in_use {
658 my ($storecfg, $conf, $skip_drive, $volid) = @_;
659
660 my $path = PVE::Storage::path($storecfg, $volid);
661
662 my $scan_config = sub {
ab5b97d8 663 my ($cref) = @_;
e0fd2b2f
FE
664
665 foreach my $key (keys %$cref) {
666 my $value = $cref->{$key};
667 if (is_valid_drivename($key)) {
668 next if $skip_drive && $key eq $skip_drive;
669 my $drive = parse_drive($key, $value);
670 next if !$drive || !$drive->{file} || drive_is_cdrom($drive);
671 return 1 if $volid eq $drive->{file};
672 if ($drive->{file} =~ m!^/!) {
673 return 1 if $drive->{file} eq $path;
674 } else {
675 my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file}, 1);
676 next if !$storeid;
677 my $scfg = PVE::Storage::storage_config($storecfg, $storeid, 1);
678 next if !$scfg;
a0d8d859 679 return 1 if $path eq PVE::Storage::path($storecfg, $drive->{file});
e0fd2b2f
FE
680 }
681 }
682 }
683
684 return 0;
685 };
686
687 return 1 if &$scan_config($conf);
688
689 undef $skip_drive;
690
ab5b97d8
FE
691 for my $snap (values %{$conf->{snapshots}}) {
692 return 1 if $scan_config->($snap);
e0fd2b2f
FE
693 }
694
695 return 0;
696}
697
698sub resolve_first_disk {
5cfa9f5f 699 my ($conf, $cdrom) = @_;
e0fd2b2f 700 my @disks = valid_drive_names();
5cfa9f5f 701 foreach my $ds (@disks) {
e0fd2b2f
FE
702 next if !$conf->{$ds};
703 my $disk = parse_drive($ds, $conf->{$ds});
5cfa9f5f
SR
704 next if drive_is_cdrom($disk) xor $cdrom;
705 return $ds;
e0fd2b2f 706 }
d1c1af4b 707 return;
e0fd2b2f
FE
708}
709
7101;