]> git.proxmox.com Git - pve-storage.git/blame - PVE/Storage/Plugin.pm
add vm_diskname helpers (get_next, is_valid)
[pve-storage.git] / PVE / Storage / Plugin.pm
CommitLineData
1dc01b9f
DM
1package PVE::Storage::Plugin;
2
3use strict;
4use warnings;
074b2cb4 5
7fc619d5 6use File::chdir;
1dc01b9f 7use File::Path;
074b2cb4 8
1dc01b9f
DM
9use PVE::Tools qw(run_command);
10use PVE::JSONSchema qw(get_standard_option);
11use PVE::Cluster qw(cfs_register_file);
12
1dc01b9f
DM
13use base qw(PVE::SectionConfig);
14
766cfd9a
WB
15our @COMMON_TAR_FLAGS = qw(
16 --one-file-system
17 -p --sparse --numeric-owner --acls
18 --xattrs --xattrs-include=user.* --xattrs-include=security.capability
19 --warning=no-file-ignored --warning=no-xattr-write
20);
21
d7875239
WL
22our @SHARED_STORAGE = (
23 'iscsi',
24 'nfs',
25 'cifs',
26 'rbd',
e34ce144 27 'cephfs',
d7875239
WL
28 'sheepdog',
29 'iscsidirect',
30 'glusterfs',
31 'zfs',
32 'drbd');
33
045ae0a7 34cfs_register_file ('storage.cfg',
1dc01b9f
DM
35 sub { __PACKAGE__->parse_config(@_); },
36 sub { __PACKAGE__->write_config(@_); });
37
35533c68 38
1dc01b9f
DM
39my $defaultData = {
40 propertyList => {
41 type => { description => "Storage type." },
f7621c01
DM
42 storage => get_standard_option('pve-storage-id',
43 { completion => \&PVE::Storage::complete_storage }),
1dc01b9f
DM
44 nodes => get_standard_option('pve-node-list', { optional => 1 }),
45 content => {
daccf21e
FG
46 description => "Allowed content types.\n\nNOTE: the value " .
47 "'rootdir' is used for Containers, and value 'images' for VMs.\n",
1dc01b9f
DM
48 type => 'string', format => 'pve-storage-content-list',
49 optional => 1,
98437f4c 50 completion => \&PVE::Storage::complete_content_type,
1dc01b9f
DM
51 },
52 disable => {
53 description => "Flag to disable the storage.",
54 type => 'boolean',
55 optional => 1,
56 },
57 maxfiles => {
58 description => "Maximal number of backup files per VM. Use '0' for unlimted.",
59 type => 'integer',
60 minimum => 0,
61 optional => 1,
62 },
63 shared => {
64 description => "Mark storage as shared.",
65 type => 'boolean',
66 optional => 1,
67 },
045ae0a7 68 'format' => {
daccf21e 69 description => "Default image format.",
1dc01b9f
DM
70 type => 'string', format => 'pve-storage-format',
71 optional => 1,
72 },
73 },
74};
75
76sub content_hash_to_string {
77 my $hash = shift;
78
79 my @cta;
80 foreach my $ct (keys %$hash) {
81 push @cta, $ct if $hash->{$ct};
045ae0a7 82 }
1dc01b9f
DM
83
84 return join(',', @cta);
85}
86
87sub valid_content_types {
88 my ($type) = @_;
89
90 my $def = $defaultData->{plugindata}->{$type};
91
92 return {} if !$def;
93
94 return $def->{content}->[0];
95}
96
97sub default_format {
98 my ($scfg) = @_;
99
100 my $type = $scfg->{type};
101 my $def = $defaultData->{plugindata}->{$type};
045ae0a7 102
1dc01b9f
DM
103 my $def_format = 'raw';
104 my $valid_formats = [ $def_format ];
105
106 if (defined($def->{format})) {
107 $def_format = $scfg->{format} || $def->{format}->[1];
108 $valid_formats = [ sort keys %{$def->{format}->[0]} ];
109 }
045ae0a7 110
1dc01b9f
DM
111 return wantarray ? ($def_format, $valid_formats) : $def_format;
112}
113
114PVE::JSONSchema::register_format('pve-storage-path', \&verify_path);
115sub verify_path {
116 my ($path, $noerr) = @_;
117
118 # fixme: exclude more shell meta characters?
119 # we need absolute paths
120 if ($path !~ m|^/[^;\(\)]+|) {
121 return undef if $noerr;
122 die "value does not look like a valid absolute path\n";
123 }
124 return $path;
125}
126
127PVE::JSONSchema::register_format('pve-storage-server', \&verify_server);
128sub verify_server {
129 my ($server, $noerr) = @_;
130
6bf617a9
WB
131 if (!(PVE::JSONSchema::pve_verify_ip($server, 1) ||
132 PVE::JSONSchema::pve_verify_dns_name($server, 1)))
133 {
1dc01b9f
DM
134 return undef if $noerr;
135 die "value does not look like a valid server name or IP address\n";
136 }
137 return $server;
138}
139
5dca5c7c
DM
140PVE::JSONSchema::register_format('pve-storage-vgname', \&parse_lvm_name);
141sub parse_lvm_name {
142 my ($name, $noerr) = @_;
143
144 if ($name !~ m/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i) {
145 return undef if $noerr;
146 die "lvm name '$name' contains illegal characters\n";
147 }
148
149 return $name;
150}
151
1dc01b9f
DM
152# fixme: do we need this
153#PVE::JSONSchema::register_format('pve-storage-portal', \&verify_portal);
154#sub verify_portal {
155# my ($portal, $noerr) = @_;
156#
157# # IP with optional port
158# if ($portal !~ m/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d+)?$/) {
159# return undef if $noerr;
160# die "value does not look like a valid portal address\n";
161# }
162# return $portal;
163#}
164
165PVE::JSONSchema::register_format('pve-storage-portal-dns', \&verify_portal_dns);
166sub verify_portal_dns {
167 my ($portal, $noerr) = @_;
168
169 # IP or DNS name with optional port
1689e627 170 if (!PVE::Tools::parse_host_and_port($portal)) {
1dc01b9f
DM
171 return undef if $noerr;
172 die "value does not look like a valid portal address\n";
173 }
174 return $portal;
175}
176
177PVE::JSONSchema::register_format('pve-storage-content', \&verify_content);
178sub verify_content {
179 my ($ct, $noerr) = @_;
180
181 my $valid_content = valid_content_types('dir'); # dir includes all types
045ae0a7 182
1dc01b9f
DM
183 if (!$valid_content->{$ct}) {
184 return undef if $noerr;
185 die "invalid content type '$ct'\n";
186 }
187
188 return $ct;
189}
190
191PVE::JSONSchema::register_format('pve-storage-format', \&verify_format);
192sub verify_format {
193 my ($fmt, $noerr) = @_;
194
35533c68 195 if ($fmt !~ m/(raw|qcow2|vmdk|subvol)/) {
1dc01b9f
DM
196 return undef if $noerr;
197 die "invalid format '$fmt'\n";
198 }
199
200 return $fmt;
201}
202
203PVE::JSONSchema::register_format('pve-storage-options', \&verify_options);
204sub verify_options {
205 my ($value, $noerr) = @_;
206
207 # mount options (see man fstab)
208 if ($value !~ m/^\S+$/) {
209 return undef if $noerr;
210 die "invalid options '$value'\n";
211 }
212
213 return $value;
214}
215
a7f3d909
DM
216PVE::JSONSchema::register_format('pve-volume-id', \&parse_volume_id);
217sub parse_volume_id {
218 my ($volid, $noerr) = @_;
219
220 if ($volid =~ m/^([a-z][a-z0-9\-\_\.]*[a-z0-9]):(.+)$/i) {
221 return wantarray ? ($1, $2) : $1;
222 }
223 return undef if $noerr;
224 die "unable to parse volume ID '$volid'\n";
225}
226
1dc01b9f
DM
227
228sub private {
229 return $defaultData;
230}
231
232sub parse_section_header {
233 my ($class, $line) = @_;
234
235 if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
236 my ($type, $storeid) = (lc($1), $2);
237 my $errmsg = undef; # set if you want to skip whole section
238 eval { PVE::JSONSchema::parse_storage_id($storeid); };
239 $errmsg = $@ if $@;
240 my $config = {}; # to return additional attributes
241 return ($type, $storeid, $errmsg, $config);
242 }
243 return undef;
244}
245
246sub decode_value {
247 my ($class, $type, $key, $value) = @_;
248
249 my $def = $defaultData->{plugindata}->{$type};
250
251 if ($key eq 'content') {
252 my $valid_content = $def->{content}->[0];
045ae0a7 253
1dc01b9f
DM
254 my $res = {};
255
256 foreach my $c (PVE::Tools::split_list($value)) {
257 if (!$valid_content->{$c}) {
703de49e
WL
258 warn "storage does not support content type '$c'\n";
259 next;
1dc01b9f
DM
260 }
261 $res->{$c} = 1;
045ae0a7 262 }
1dc01b9f
DM
263
264 if ($res->{none} && scalar (keys %$res) > 1) {
265 die "unable to combine 'none' with other content types\n";
266 }
267
268 return $res;
269 } elsif ($key eq 'format') {
270 my $valid_formats = $def->{format}->[0];
271
272 if (!$valid_formats->{$value}) {
703de49e
WL
273 warn "storage does not support format '$value'\n";
274 next;
1dc01b9f
DM
275 }
276
277 return $value;
278 } elsif ($key eq 'nodes') {
279 my $res = {};
280
281 foreach my $node (PVE::Tools::split_list($value)) {
282 if (PVE::JSONSchema::pve_verify_node_name($node)) {
283 $res->{$node} = 1;
284 }
285 }
286
287 # fixme:
288 # no node restrictions for local storage
289 #if ($storeid && $storeid eq 'local' && scalar(keys(%$res))) {
290 # die "storage '$storeid' does not allow node restrictions\n";
291 #}
292
293 return $res;
294 }
295
296 return $value;
297}
298
299sub encode_value {
300 my ($class, $type, $key, $value) = @_;
301
302 if ($key eq 'nodes') {
303 return join(',', keys(%$value));
304 } elsif ($key eq 'content') {
305 my $res = content_hash_to_string($value) || 'none';
306 return $res;
307 }
308
309 return $value;
310}
311
312sub parse_config {
313 my ($class, $filename, $raw) = @_;
314
315 my $cfg = $class->SUPER::parse_config($filename, $raw);
316 my $ids = $cfg->{ids};
317
318 # make sure we have a reasonable 'local:' storage
dc6ff39f 319 # we want 'local' to be always the same 'type' (on all cluster nodes)
1dc01b9f
DM
320 if (!$ids->{local} || $ids->{local}->{type} ne 'dir' ||
321 ($ids->{local}->{path} && $ids->{local}->{path} ne '/var/lib/vz')) {
322 $ids->{local} = {
323 type => 'dir',
324 priority => 0, # force first entry
325 path => '/var/lib/vz',
326 maxfiles => 0,
327 content => { images => 1, rootdir => 1, vztmpl => 1, iso => 1},
328 };
329 }
045ae0a7 330
1dc01b9f
DM
331 # make sure we have a path
332 $ids->{local}->{path} = '/var/lib/vz' if !$ids->{local}->{path};
333
334 # remove node restrictions for local storage
335 delete($ids->{local}->{nodes});
336
337 foreach my $storeid (keys %$ids) {
338 my $d = $ids->{$storeid};
339 my $type = $d->{type};
340
341 my $def = $defaultData->{plugindata}->{$type};
342
343 if ($def->{content}) {
344 $d->{content} = $def->{content}->[1] if !$d->{content};
345 }
d7875239 346 if (grep { $_ eq $type } @SHARED_STORAGE) {
1dc01b9f
DM
347 $d->{shared} = 1;
348 }
349 }
350
351 return $cfg;
352}
353
354# Storage implementation
355
3932ca0d
TL
356# called during addition of storage (before the new storage config got written)
357# die to abort additon if there are (grave) problems
358# NOTE: runs in a storage config *locked* context
359sub on_add_hook {
360 my ($class, $storeid, $scfg, %param) = @_;
361
362 # do nothing by default
363}
364
365# called during deletion of storage (before the new storage config got written)
366# and if the activate check on addition fails, to cleanup all storage traces
367# which on_add_hook may have created.
368# die to abort deletion if there are (very grave) problems
369# NOTE: runs in a storage config *locked* context
370sub on_delete_hook {
371 my ($class, $storeid, $scfg) = @_;
372
373 # do nothing by default
374}
375
1dc01b9f
DM
376sub cluster_lock_storage {
377 my ($class, $storeid, $shared, $timeout, $func, @param) = @_;
378
379 my $res;
380 if (!$shared) {
381 my $lockid = "pve-storage-$storeid";
382 my $lockdir = "/var/lock/pve-manager";
383 mkdir $lockdir;
045ae0a7 384 $res = PVE::Tools::lock_file("$lockdir/$lockid", $timeout, $func, @param);
1dc01b9f
DM
385 die $@ if $@;
386 } else {
387 $res = PVE::Cluster::cfs_lock_storage($storeid, $timeout, $func, @param);
388 die $@ if $@;
045ae0a7 389 }
1dc01b9f
DM
390 return $res;
391}
392
393sub parse_name_dir {
394 my $name = shift;
395
35533c68
DM
396 if ($name =~ m!^((base-)?[^/\s]+\.(raw|qcow2|vmdk|subvol))$!) {
397 return ($1, $3, $2); # (name, format, isBase)
1dc01b9f
DM
398 }
399
400 die "unable to parse volume filename '$name'\n";
401}
402
403sub parse_volname {
404 my ($class, $volname) = @_;
405
2502b33b
DM
406 if ($volname =~ m!^(\d+)/(\S+)/(\d+)/(\S+)$!) {
407 my ($basedvmid, $basename) = ($1, $2);
408 parse_name_dir($basename);
409 my ($vmid, $name) = ($3, $4);
35533c68
DM
410 my (undef, $format, $isBase) = parse_name_dir($name);
411 return ('images', $name, $vmid, $basename, $basedvmid, $isBase, $format);
2502b33b 412 } elsif ($volname =~ m!^(\d+)/(\S+)$!) {
1dc01b9f 413 my ($vmid, $name) = ($1, $2);
35533c68
DM
414 my (undef, $format, $isBase) = parse_name_dir($name);
415 return ('images', $name, $vmid, undef, undef, $isBase, $format);
1dc01b9f
DM
416 } elsif ($volname =~ m!^iso/([^/]+\.[Ii][Ss][Oo])$!) {
417 return ('iso', $1);
13d2cb79 418 } elsif ($volname =~ m!^vztmpl/([^/]+\.tar\.[gx]z)$!) {
1dc01b9f
DM
419 return ('vztmpl', $1);
420 } elsif ($volname =~ m!^rootdir/(\d+)$!) {
421 return ('rootdir', $1, $1);
a22854e5 422 } elsif ($volname =~ m!^backup/([^/]+(\.(tar|tar\.gz|tar\.lzo|tgz|vma|vma\.gz|vma\.lzo)))$!) {
1dc01b9f 423 my $fn = $1;
4cb6e060 424 if ($fn =~ m/^vzdump-(openvz|lxc|qemu)-(\d+)-.+/) {
1dc01b9f
DM
425 return ('backup', $fn, $2);
426 }
427 return ('backup', $fn);
428 }
429
430 die "unable to parse directory volume name '$volname'\n";
431}
432
045ae0a7 433my $vtype_subdirs = {
1dc01b9f
DM
434 images => 'images',
435 rootdir => 'private',
436 iso => 'template/iso',
437 vztmpl => 'template/cache',
438 backup => 'dump',
439};
440
441sub get_subdir {
442 my ($class, $scfg, $vtype) = @_;
443
444 my $path = $scfg->{path};
445
446 die "storage definintion has no path\n" if !$path;
447
448 my $subdir = $vtype_subdirs->{$vtype};
449
450 die "unknown vtype '$vtype'\n" if !defined($subdir);
451
045ae0a7 452 return "$path/$subdir";
1dc01b9f
DM
453}
454
08480ce7 455sub filesystem_path {
e67069eb 456 my ($class, $scfg, $volname, $snapname) = @_;
1dc01b9f 457
e67069eb
DM
458 my ($vtype, $name, $vmid, undef, undef, $isBase, $format) =
459 $class->parse_volname($volname);
460
461 # Note: qcow2/qed has internal snapshot, so path is always
462 # the same (with or without snapshot => same file).
463 die "can't snapshot this image format\n"
464 if defined($snapname) && $format !~ m/^(qcow2|qed)$/;
1dc01b9f
DM
465
466 my $dir = $class->get_subdir($scfg, $vtype);
467
468 $dir .= "/$vmid" if $vtype eq 'images';
469
470 my $path = "$dir/$name";
471
472 return wantarray ? ($path, $vmid, $vtype) : $path;
473}
474
08480ce7 475sub path {
e67069eb 476 my ($class, $scfg, $volname, $storeid, $snapname) = @_;
08480ce7 477
e67069eb 478 return $class->filesystem_path($scfg, $volname, $snapname);
08480ce7
DM
479}
480
2502b33b
DM
481sub create_base {
482 my ($class, $storeid, $scfg, $volname) = @_;
483
484 # this only works for file based storage types
5510f5c9 485 die "storage definition has no path\n" if !$scfg->{path};
2502b33b 486
35533c68 487 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format) =
2502b33b
DM
488 $class->parse_volname($volname);
489
490 die "create_base on wrong vtype '$vtype'\n" if $vtype ne 'images';
491
492 die "create_base not possible with base image\n" if $isBase;
493
08480ce7 494 my $path = $class->filesystem_path($scfg, $volname);
2502b33b 495
35533c68
DM
496 my ($size, undef, $used, $parent) = file_size_info($path);
497 die "file_size_info on '$volname' failed\n" if !($format && defined($size));
2502b33b
DM
498
499 die "volname '$volname' contains wrong information about parent\n"
500 if $basename && (!$parent || $parent ne "../$basevmid/$basename");
501
502 my $newname = $name;
503 $newname =~ s/^vm-/base-/;
504
505 my $newvolname = $basename ? "$basevmid/$basename/$vmid/$newname" :
506 "$vmid/$newname";
507
08480ce7 508 my $newpath = $class->filesystem_path($scfg, $newvolname);
2502b33b
DM
509
510 die "file '$newpath' already exists\n" if -f $newpath;
511
1a3459ac 512 rename($path, $newpath) ||
2502b33b
DM
513 die "rename '$path' to '$newpath' failed - $!\n";
514
c803c396 515 # We try to protect base volume
2502b33b 516
c803c396
DM
517 chmod(0444, $newpath); # nobody should write anything
518
519 # also try to set immutable flag
520 eval { run_command(['/usr/bin/chattr', '+i', $newpath]); };
521 warn $@ if $@;
1a3459ac 522
2502b33b
DM
523 return $newvolname;
524}
525
345f8981
SI
526sub is_valid_vm_diskname {
527 my ($disk_name, $scfg, $vmid, $fmt, $add_fmt_suffix) = @_;
528
529 $vmid = qr/\d+/ if !defined($vmid);
530
531 my $suffix = (defined($fmt) && $add_fmt_suffix) ? ".$fmt" : '';
532
533 my $type = $scfg->{type};
534 my $def = $defaultData->{plugindata}->{$type};
535 my $valid_formats = $def->{format}[0];
536
537 my $disk_regex = qr/(vm|base)-$vmid-disk-(\d+)$suffix/;
538 $disk_regex = qr/(vm|base|subvol|basevol)-$vmid-disk-(\d+)/
539 if $valid_formats->{subvol};
540
541 if($disk_name =~ m/$disk_regex/){
542 return wantarray ? (1, $2) : 1;
543 }
544}
545
546sub get_next_vm_diskname {
547 my ($disk_list, $storeid, $vmid, $fmt, $scfg, $add_fmt_suffix) = @_;
548
549 my $disk_ids = {};
550 my ($match, $disknum);
551 foreach my $disk (@$disk_list) {
552 ($match, $disknum) = is_valid_vm_diskname($disk, $scfg, $vmid, $fmt, $add_fmt_suffix);
553 $disk_ids->{$disknum} = 1 if $match;
554 }
555
556 $fmt //= '';
557 my $prefix = ($fmt eq 'subvol') ? 'subvol' : 'vm';
558 my $suffix = $add_fmt_suffix ? ".$fmt" : '';
559
560 for (my $i = 1; $i < 100; $i++) {
561 if (!$disk_ids->{$i}) {
562 return "$prefix-$vmid-disk-$i$suffix";
563 }
564 }
565
566 die "unable to allocate an image name for VM $vmid in storage '$storeid'\n"
567}
568
2502b33b
DM
569my $find_free_diskname = sub {
570 my ($imgdir, $vmid, $fmt) = @_;
571
572 my $disk_ids = {};
1a3459ac 573 PVE::Tools::dir_glob_foreach($imgdir,
2502b33b
DM
574 qr!(vm|base)-$vmid-disk-(\d+)\..*!,
575 sub {
1a3459ac 576 my ($fn, $type, $disk) = @_;
2502b33b
DM
577 $disk_ids->{$disk} = 1;
578 });
579
580 for (my $i = 1; $i < 100; $i++) {
581 if (!$disk_ids->{$i}) {
582 return "vm-$vmid-disk-$i.$fmt";
583 }
584 }
585
586 die "unable to allocate a new image name for VM $vmid in '$imgdir'\n";
587};
588
589sub clone_image {
f236eaf8 590 my ($class, $scfg, $storeid, $volname, $vmid, $snap) = @_;
2502b33b
DM
591
592 # this only works for file based storage types
593 die "storage definintion has no path\n" if !$scfg->{path};
594
35533c68 595 my ($vtype, $basename, $basevmid, undef, undef, $isBase, $format) =
2502b33b
DM
596 $class->parse_volname($volname);
597
598 die "clone_image on wrong vtype '$vtype'\n" if $vtype ne 'images';
599
f236eaf8
SP
600 die "this storage type does not support clone_image on snapshot\n" if $snap;
601
35533c68
DM
602 die "this storage type does not support clone_image on subvolumes\n" if $format eq 'subvol';
603
f236eaf8 604 die "clone_image only works on base images\n" if !$isBase;
1dc01b9f
DM
605
606 my $imagedir = $class->get_subdir($scfg, 'images');
607 $imagedir .= "/$vmid";
608
609 mkpath $imagedir;
610
2502b33b
DM
611 my $name = &$find_free_diskname($imagedir, $vmid, "qcow2");
612
613 warn "clone $volname: $vtype, $name, $vmid to $name (base=../$basevmid/$basename)\n";
614
615 my $newvol = "$basevmid/$basename/$vmid/$name";
616
08480ce7 617 my $path = $class->filesystem_path($scfg, $newvol);
2502b33b 618
1a3459ac 619 # Note: we use relative paths, so we need to call chdir before qemu-img
2502b33b 620 eval {
7fc619d5 621 local $CWD = $imagedir;
2502b33b 622
1a3459ac 623 my $cmd = ['/usr/bin/qemu-img', 'create', '-b', "../$basevmid/$basename",
2502b33b 624 '-f', 'qcow2', $path];
1a3459ac 625
2502b33b
DM
626 run_command($cmd);
627 };
628 my $err = $@;
1dc01b9f 629
2502b33b
DM
630 die $err if $err;
631
632 return $newvol;
633}
634
635sub alloc_image {
636 my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
637
638 my $imagedir = $class->get_subdir($scfg, 'images');
639 $imagedir .= "/$vmid";
640
641 mkpath $imagedir;
642
643 $name = &$find_free_diskname($imagedir, $vmid, $fmt) if !$name;
1a3459ac 644
1dc01b9f
DM
645 my (undef, $tmpfmt) = parse_name_dir($name);
646
045ae0a7 647 die "illegal name '$name' - wrong extension for format ('$tmpfmt != '$fmt')\n"
1dc01b9f
DM
648 if $tmpfmt ne $fmt;
649
650 my $path = "$imagedir/$name";
651
652 die "disk image '$path' already exists\n" if -e $path;
653
35533c68
DM
654 if ($fmt eq 'subvol') {
655 # only allow this if size = 0, so that user knows what he is doing
656 die "storage does not support subvol quotas\n" if $size != 0;
657
1f5734bb
WB
658 my $old_umask = umask(0022);
659 my $err;
660 mkdir($path) or $err = "unable to create subvol '$path' - $!\n";
661 umask $old_umask;
662 die $err if $err;
35533c68
DM
663 } else {
664 my $cmd = ['/usr/bin/qemu-img', 'create'];
045ae0a7 665
35533c68
DM
666 push @$cmd, '-o', 'preallocation=metadata' if $fmt eq 'qcow2';
667
668 push @$cmd, '-f', $fmt, $path, "${size}K";
1dc01b9f 669
35533c68
DM
670 run_command($cmd, errmsg => "unable to create image");
671 }
672
1dc01b9f
DM
673 return "$vmid/$name";
674}
675
676sub free_image {
35533c68 677 my ($class, $storeid, $scfg, $volname, $isBase, $format) = @_;
1dc01b9f 678
08480ce7 679 my $path = $class->filesystem_path($scfg, $volname);
1dc01b9f 680
f5451f28
DC
681 if ($isBase) {
682 # try to remove immutable flag
683 eval { run_command(['/usr/bin/chattr', '-i', $path]); };
684 warn $@ if $@;
685 }
686
4a7d2222 687 if (defined($format) && ($format eq 'subvol')) {
35533c68
DM
688 File::Path::remove_tree($path);
689 } else {
690
691 if (! -f $path) {
692 warn "disk image '$path' does not exists\n";
693 return undef;
694 }
1dc01b9f 695
35533c68
DM
696 unlink($path) || die "unlink '$path' failed - $!\n";
697 }
698
1dc01b9f
DM
699 return undef;
700}
701
702sub file_size_info {
703 my ($filename, $timeout) = @_;
704
35533c68
DM
705 if (-d $filename) {
706 return wantarray ? (0, 'subvol', 0, undef) : 1;
707 }
708
1dc01b9f
DM
709 my $cmd = ['/usr/bin/qemu-img', 'info', $filename];
710
711 my $format;
73b7847e 712 my $parent;
1dc01b9f
DM
713 my $size = 0;
714 my $used = 0;
715
716 eval {
717 run_command($cmd, timeout => $timeout, outfunc => sub {
718 my $line = shift;
1dc01b9f
DM
719 if ($line =~ m/^file format:\s+(\S+)\s*$/) {
720 $format = $1;
73b7847e
AD
721 } elsif ($line =~ m/^backing file:\s(\S+)\s/) {
722 $parent = $1;
1dc01b9f
DM
723 } elsif ($line =~ m/^virtual size:\s\S+\s+\((\d+)\s+bytes\)$/) {
724 $size = int($1);
725 } elsif ($line =~ m/^disk size:\s+(\d+(.\d+)?)([KMGT])\s*$/) {
726 $used = $1;
727 my $u = $3;
728
729 $used *= 1024 if $u eq 'K';
730 $used *= (1024*1024) if $u eq 'M';
731 $used *= (1024*1024*1024) if $u eq 'G';
732 $used *= (1024*1024*1024*1024) if $u eq 'T';
733
734 $used = int($used);
735 }
736 });
737 };
738
73b7847e 739 return wantarray ? ($size, $format, $used, $parent) : $size;
1dc01b9f
DM
740}
741
e47e548e
AD
742sub volume_size_info {
743 my ($class, $scfg, $storeid, $volname, $timeout) = @_;
08480ce7 744 my $path = $class->filesystem_path($scfg, $volname);
e47e548e
AD
745 return file_size_info($path, $timeout);
746
747}
748
81f5058c
AD
749sub volume_resize {
750 my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
751
6d788031 752 die "can't resize this image format\n" if $volname !~ m/\.(raw|qcow2)$/;
81f5058c
AD
753
754 return 1 if $running;
755
08480ce7 756 my $path = $class->filesystem_path($scfg, $volname);
81f5058c 757
0589e5f9
WL
758 my $format = ($class->parse_volname($volname))[6];
759
760 my $cmd = ['/usr/bin/qemu-img', 'resize', '-f', $format, $path , $size];
81f5058c 761
1059cc99 762 run_command($cmd, timeout => 10);
81f5058c
AD
763
764 return undef;
765}
766
7dcb0697 767sub volume_snapshot {
f5640e7d 768 my ($class, $scfg, $storeid, $volname, $snap) = @_;
7dcb0697 769
6d788031 770 die "can't snapshot this image format\n" if $volname !~ m/\.(qcow2|qed)$/;
7dcb0697 771
08480ce7 772 my $path = $class->filesystem_path($scfg, $volname);
7dcb0697
AD
773
774 my $cmd = ['/usr/bin/qemu-img', 'snapshot','-c', $snap, $path];
775
3f13fd7d 776 run_command($cmd);
7dcb0697
AD
777
778 return undef;
779}
780
1597f1f9
WL
781sub volume_rollback_is_possible {
782 my ($class, $scfg, $storeid, $volname, $snap) = @_;
783
784 return 1;
785}
786
41dffa85
AD
787sub volume_snapshot_rollback {
788 my ($class, $scfg, $storeid, $volname, $snap) = @_;
789
6d788031 790 die "can't rollback snapshot this image format\n" if $volname !~ m/\.(qcow2|qed)$/;
41dffa85 791
08480ce7 792 my $path = $class->filesystem_path($scfg, $volname);
41dffa85
AD
793
794 my $cmd = ['/usr/bin/qemu-img', 'snapshot','-a', $snap, $path];
795
3f13fd7d 796 run_command($cmd);
41dffa85
AD
797
798 return undef;
799}
800
6000a061
AD
801sub volume_snapshot_delete {
802 my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
803
6d788031 804 die "can't delete snapshot for this image format\n" if $volname !~ m/\.(qcow2|qed)$/;
6000a061
AD
805
806 return 1 if $running;
807
08480ce7 808 my $path = $class->filesystem_path($scfg, $volname);
6000a061 809
399581a2
WB
810 $class->deactivate_volume($storeid, $scfg, $volname, $snap, {});
811
6000a061
AD
812 my $cmd = ['/usr/bin/qemu-img', 'snapshot','-d', $snap, $path];
813
3f13fd7d 814 run_command($cmd);
6000a061
AD
815
816 return undef;
817}
818
7118dd91
DM
819sub storage_can_replicate {
820 my ($class, $scfg, $storeid, $format) = @_;
821
822 return 0;
823}
824
f884fe11
AD
825sub volume_has_feature {
826 my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
827
828 my $features = {
5649ccfe
AD
829 snapshot => { current => { qcow2 => 1}, snap => { qcow2 => 1} },
830 clone => { base => {qcow2 => 1, raw => 1, vmdk => 1} },
35533c68 831 template => { current => {qcow2 => 1, raw => 1, vmdk => 1, subvol => 1} },
5649ccfe 832 copy => { base => {qcow2 => 1, raw => 1, vmdk => 1},
22b8cf97
AD
833 current => {qcow2 => 1, raw => 1, vmdk => 1},
834 snap => {qcow2 => 1} },
baafddbd
DC
835 sparseinit => { base => {qcow2 => 1, raw => 1, vmdk => 1},
836 current => {qcow2 => 1, raw => 1, vmdk => 1} },
f884fe11
AD
837 };
838
35533c68 839 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format) =
dc4f2cb3
AD
840 $class->parse_volname($volname);
841
dc4f2cb3
AD
842 my $key = undef;
843 if($snapname){
2c5a7097 844 $key = 'snap';
dc4f2cb3
AD
845 }else{
846 $key = $isBase ? 'base' : 'current';
f884fe11 847 }
dc4f2cb3
AD
848
849 return 1 if defined($features->{$feature}->{$key}->{$format});
850
f884fe11
AD
851 return undef;
852}
853
1dc01b9f
DM
854sub list_images {
855 my ($class, $storeid, $scfg, $vmid, $vollist, $cache) = @_;
856
857 my $imagedir = $class->get_subdir($scfg, 'images');
858
859 my ($defFmt, $vaidFmts) = default_format($scfg);
045ae0a7 860 my $fmts = join ('|', @$vaidFmts);
1dc01b9f
DM
861
862 my $res = [];
863
864 foreach my $fn (<$imagedir/[0-9][0-9]*/*>) {
865
866 next if $fn !~ m!^(/.+/(\d+)/([^/]+\.($fmts)))$!;
867 $fn = $1; # untaint
868
869 my $owner = $2;
870 my $name = $3;
1dc01b9f 871
2502b33b 872 next if !$vollist && defined($vmid) && ($owner ne $vmid);
1dc01b9f 873
73b7847e 874 my ($size, $format, $used, $parent) = file_size_info($fn);
35533c68 875 next if !($format && defined($size));
2502b33b
DM
876
877 my $volid;
878 if ($parent && $parent =~ m!^../(\d+)/([^/]+\.($fmts))$!) {
879 my ($basevmid, $basename) = ($1, $2);
880 $volid = "$storeid:$basevmid/$basename/$owner/$name";
881 } else {
882 $volid = "$storeid:$owner/$name";
883 }
1dc01b9f 884
2502b33b
DM
885 if ($vollist) {
886 my $found = grep { $_ eq $volid } @$vollist;
887 next if !$found;
1dc01b9f
DM
888 }
889
2502b33b
DM
890 push @$res, {
891 volid => $volid, format => $format,
1a3459ac 892 size => $size, vmid => $owner, used => $used, parent => $parent
2502b33b 893 };
1dc01b9f
DM
894 }
895
896 return $res;
897}
898
899sub status {
900 my ($class, $storeid, $scfg, $cache) = @_;
901
902 my $path = $scfg->{path};
903
904 die "storage definintion has no path\n" if !$path;
045ae0a7 905
1dc01b9f
DM
906 my $timeout = 2;
907 my $res = PVE::Tools::df($path, $timeout);
908
909 return undef if !$res || !$res->{total};
910
911 return ($res->{total}, $res->{avail}, $res->{used}, 1);
912}
913
aefe82ea 914sub volume_snapshot_list {
8b622c2d 915 my ($class, $scfg, $storeid, $volname) = @_;
aefe82ea
WL
916
917 # implement in subclass
918 die "Volume_snapshot_list is not implemented for $class";
919
636ac5b8 920 # return an empty array if dataset does not exist.
aefe82ea
WL
921}
922
1dc01b9f
DM
923sub activate_storage {
924 my ($class, $storeid, $scfg, $cache) = @_;
925
926 my $path = $scfg->{path};
927
928 die "storage definintion has no path\n" if !$path;
929
e53050ed
EK
930 # this path test may hang indefinitely on unresponsive mounts
931 my $timeout = 2;
932 if (! PVE::Tools::run_fork_with_timeout($timeout, sub {-d $path})) {
933 die "unable to activate storage '$storeid' - " .
934 "directory '$path' does not exist or is unreachable\n";
935 }
936
1dc01b9f 937
c7616abc
WB
938 return if defined($scfg->{mkdir}) && !$scfg->{mkdir};
939
1dc01b9f
DM
940 if (defined($scfg->{content})) {
941 foreach my $vtype (keys %$vtype_subdirs) {
6bcc16d7
DM
942 # OpenVZMigrate uses backup (dump) dir
943 if (defined($scfg->{content}->{$vtype}) ||
944 ($vtype eq 'backup' && defined($scfg->{content}->{'rootdir'}))) {
945 my $subdir = $class->get_subdir($scfg, $vtype);
946 mkpath $subdir if $subdir ne $path;
947 }
1dc01b9f
DM
948 }
949 }
950}
951
952sub deactivate_storage {
953 my ($class, $storeid, $scfg, $cache) = @_;
954
955 # do nothing by default
956}
957
958sub activate_volume {
02e797b8 959 my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
1dc01b9f 960
02e797b8 961 my $path = $class->filesystem_path($scfg, $volname, $snapname);
1dc01b9f
DM
962
963 # check is volume exists
964 if ($scfg->{path}) {
965 die "volume '$storeid:$volname' does not exist\n" if ! -e $path;
966 } else {
967 die "volume '$storeid:$volname' does not exist\n" if ! -b $path;
968 }
969}
970
971sub deactivate_volume {
02e797b8 972 my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
1dc01b9f
DM
973
974 # do nothing by default
975}
976
c9eeac01
AD
977sub check_connection {
978 my ($class, $storeid, $scfg) = @_;
979 # do nothing by default
980 return 1;
981}
982
e1f6cb39
DM
983# Import/Export interface:
984# Any path based storage is assumed to support 'raw' and 'tar' streams, so
985# the default implementations will return this if $scfg->{path} is set,
986# mimicking the old PVE::Storage::storage_migrate() function.
987#
988# Plugins may fall back to PVE::Storage::Plugin::volume_{export,import}...
989# functions in case the format doesn't match their specialized
990# implementations to reuse the raw/tar code.
991#
992# Format specification:
993# The following formats are all prefixed with image information in the form
994# of a 64 bit little endian unsigned integer (pack('Q<')) in order to be able
995# to preallocate the image on storages which require it.
996#
997# raw+size: (image files only)
998# A raw binary data stream such as produced via `dd if=TheImageFile`.
999# qcow2+size, vmdk: (image files only)
1000# A raw qcow2/vmdk/... file such as produced via `dd if=some.qcow2` for
1001# files which are already in qcow2 format, or via `qemu-img convert`.
1002# Note that these formats are only valid with $with_snapshots being true.
1003# tar+size: (subvolumes only)
6b3a4a25
WB
1004# A GNU tar stream containing just the inner contents of the subvolume.
1005# This does not distinguish between the contents of a privileged or
1006# unprivileged container. In other words, this is from the root user
1007# namespace's point of view with no uid-mapping in effect.
1008# As produced via `tar -C vm-100-disk-1.subvol -cpf TheOutputFile.dat .`
e1f6cb39
DM
1009
1010# Plugins may reuse these helpers. Changes to the header format should be
1011# reflected by changes to the function prototypes.
1012sub write_common_header($$) {
1013 my ($fh, $image_size_in_bytes) = @_;
1014 syswrite($fh, pack("Q<", $image_size_in_bytes), 8);
1015}
1016
1017sub read_common_header($) {
1018 my ($fh) = @_;
1019 sysread($fh, my $size, 8);
1020 $size = unpack('Q<', $size);
1021 die "got a bad size (not a multiple of 1K)\n" if ($size&1023);
1022 # Size is in bytes!
1023 return $size;
1024}
1025
47f37b53
WB
1026# Export a volume into a file handle as a stream of desired format.
1027sub volume_export {
1028 my ($class, $scfg, $storeid, $fh, $volname, $format, $snapshot, $base_snapshot, $with_snapshots) = @_;
e1f6cb39
DM
1029 if ($scfg->{path} && !defined($snapshot) && !defined($base_snapshot)) {
1030 my $file = $class->path($scfg, $volname, $storeid)
1031 or goto unsupported;
1032 my ($size, $file_format) = file_size_info($file);
1033
1034 if ($format eq 'raw+size') {
1035 goto unsupported if $with_snapshots || $file_format eq 'subvol';
1036 write_common_header($fh, $size);
1037 if ($file_format eq 'raw') {
1038 run_command(['dd', "if=$file", "bs=4k"], output => '>&'.fileno($fh));
1039 } else {
1040 run_command(['qemu-img', 'convert', '-f', $file_format, '-O', 'raw', $file, '/dev/stdout'],
1041 output => '>&'.fileno($fh));
1042 }
1043 return;
1044 } elsif ($format =~ /^(qcow2|vmdk)\+size$/) {
1045 my $data_format = $1;
1046 goto unsupported if !$with_snapshots || $file_format ne $data_format;
1047 write_common_header($fh, $size);
1048 run_command(['dd', "if=$file", "bs=4k"], output => '>&'.fileno($fh));
1049 return;
1050 } elsif ($format eq 'tar+size') {
1051 goto unsupported if $file_format ne 'subvol';
1052 write_common_header($fh, $size);
6b3a4a25 1053 run_command(['tar', @COMMON_TAR_FLAGS, '-cf', '-', '-C', $file, '.'],
e1f6cb39
DM
1054 output => '>&'.fileno($fh));
1055 return;
1056 }
1057 }
1058 unsupported:
1059 die "volume export format $format not available for $class";
47f37b53
WB
1060}
1061
d390328b
WB
1062sub volume_export_formats {
1063 my ($class, $scfg, $storeid, $volname, $snapshot, $base_snapshot, $with_snapshots) = @_;
e1f6cb39
DM
1064 if ($scfg->{path} && !defined($snapshot) && !defined($base_snapshot)) {
1065 my $file = $class->path($scfg, $volname, $storeid)
1066 or return;
1067 my ($size, $format) = file_size_info($file);
1068
1069 if ($with_snapshots) {
1070 return ($format.'+size') if ($format eq 'qcow2' || $format eq 'vmdk');
1071 return ();
1072 }
1073 return ('tar+size') if $format eq 'subvol';
1074 return ('raw+size');
1075 }
1076 return ();
d390328b
WB
1077}
1078
47f37b53
WB
1079# Import data from a stream, creating a new or replacing or adding to an existing volume.
1080sub volume_import {
1081 my ($class, $scfg, $storeid, $fh, $volname, $format, $base_snapshot, $with_snapshots) = @_;
e1f6cb39
DM
1082
1083 die "volume import format '$format' not available for $class\n"
1084 if $format !~ /^(raw|tar|qcow2|vmdk)\+size$/;
1085 my $data_format = $1;
1086
1087 die "format $format cannot be imported without snapshots\n"
1088 if !$with_snapshots && ($data_format eq 'qcow2' || $data_format eq 'vmdk');
1089 die "format $format cannot be imported with snapshots\n"
1090 if $with_snapshots && ($data_format eq 'raw' || $data_format eq 'tar');
1091
1092 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $file_format) =
1093 $class->parse_volname($volname);
1094
1095 # XXX: Should we bother with conversion routines at this level? This won't
1096 # happen without manual CLI usage, so for now we just error out...
1097 die "cannot import format $format into a file of format $file_format\n"
1098 if $data_format ne $file_format && !($data_format eq 'tar' && $file_format eq 'subvol');
1099
1100 # Check for an existing file first since interrupting alloc_image doesn't
1101 # free it.
1102 my $file = $class->path($scfg, $volname, $storeid);
1103 die "file '$file' already exists\n" if -e $file;
1104
1105 my ($size) = read_common_header($fh);
1106 $size = int($size/1024);
1107
1108 eval {
1109 my $allocname = $class->alloc_image($storeid, $scfg, $vmid, $file_format, $name, $size);
1110 if ($allocname ne $volname) {
1111 my $oldname = $volname;
1112 $volname = $allocname; # Let the cleanup code know what to free
1113 die "internal error: unexpected allocated name: '$allocname' != '$oldname'\n";
1114 }
1115 my $file = $class->path($scfg, $volname, $storeid)
1116 or die "internal error: failed to get path to newly allocated volume $volname\n";
1117 if ($data_format eq 'raw' || $data_format eq 'qcow2' || $data_format eq 'vmdk') {
1118 run_command(['dd', "of=$file", 'conv=sparse', 'bs=64k'],
1119 input => '<&'.fileno($fh));
1120 } elsif ($data_format eq 'tar') {
6b3a4a25 1121 run_command(['tar', @COMMON_TAR_FLAGS, '-C', $file, '-xf', '-'],
e1f6cb39
DM
1122 input => '<&'.fileno($fh));
1123 } else {
1124 die "volume import format '$format' not available for $class";
1125 }
1126 };
1127 if (my $err = $@) {
1128 eval { $class->free_image($storeid, $scfg, $volname, 0, $file_format) };
1129 warn $@ if $@;
1130 die $err;
1131 }
47f37b53 1132}
e47e548e 1133
d390328b
WB
1134sub volume_import_formats {
1135 my ($class, $scfg, $storeid, $volname, $base_snapshot, $with_snapshots) = @_;
e1f6cb39
DM
1136 if ($scfg->{path} && !defined($base_snapshot)) {
1137 my $format = ($class->parse_volname($volname))[6];
1138 if ($with_snapshots) {
1139 return ($format.'+size') if ($format eq 'qcow2' || $format eq 'vmdk');
1140 return ();
1141 }
1142 return ('tar+size') if $format eq 'subvol';
1143 return ('raw+size');
1144 }
1145 return ();
d390328b
WB
1146}
1147
1dc01b9f 11481;