]> git.proxmox.com Git - pve-storage.git/blame - PVE/Storage/Plugin.pm
fix #1598: use glusterfs daemon default port for online check
[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;
712e27f1 8use File::Basename;
074b2cb4 9
1dc01b9f
DM
10use PVE::Tools qw(run_command);
11use PVE::JSONSchema qw(get_standard_option);
12use PVE::Cluster qw(cfs_register_file);
13
1dc01b9f
DM
14use base qw(PVE::SectionConfig);
15
766cfd9a
WB
16our @COMMON_TAR_FLAGS = qw(
17 --one-file-system
18 -p --sparse --numeric-owner --acls
19 --xattrs --xattrs-include=user.* --xattrs-include=security.capability
20 --warning=no-file-ignored --warning=no-xattr-write
21);
22
d7875239
WL
23our @SHARED_STORAGE = (
24 'iscsi',
25 'nfs',
26 'cifs',
27 'rbd',
e34ce144 28 'cephfs',
d7875239
WL
29 'sheepdog',
30 'iscsidirect',
31 'glusterfs',
32 'zfs',
33 'drbd');
34
c0535aa7
SI
35our $MAX_VOLUMES_PER_GUEST = 1024;
36
045ae0a7 37cfs_register_file ('storage.cfg',
1dc01b9f
DM
38 sub { __PACKAGE__->parse_config(@_); },
39 sub { __PACKAGE__->write_config(@_); });
40
35533c68 41
1dc01b9f
DM
42my $defaultData = {
43 propertyList => {
44 type => { description => "Storage type." },
f7621c01
DM
45 storage => get_standard_option('pve-storage-id',
46 { completion => \&PVE::Storage::complete_storage }),
1dc01b9f
DM
47 nodes => get_standard_option('pve-node-list', { optional => 1 }),
48 content => {
daccf21e
FG
49 description => "Allowed content types.\n\nNOTE: the value " .
50 "'rootdir' is used for Containers, and value 'images' for VMs.\n",
1dc01b9f
DM
51 type => 'string', format => 'pve-storage-content-list',
52 optional => 1,
98437f4c 53 completion => \&PVE::Storage::complete_content_type,
1dc01b9f
DM
54 },
55 disable => {
56 description => "Flag to disable the storage.",
57 type => 'boolean',
58 optional => 1,
59 },
60 maxfiles => {
61 description => "Maximal number of backup files per VM. Use '0' for unlimted.",
62 type => 'integer',
63 minimum => 0,
64 optional => 1,
65 },
66 shared => {
67 description => "Mark storage as shared.",
68 type => 'boolean',
69 optional => 1,
70 },
045ae0a7 71 'format' => {
daccf21e 72 description => "Default image format.",
1dc01b9f
DM
73 type => 'string', format => 'pve-storage-format',
74 optional => 1,
75 },
76 },
77};
78
79sub content_hash_to_string {
80 my $hash = shift;
81
82 my @cta;
83 foreach my $ct (keys %$hash) {
84 push @cta, $ct if $hash->{$ct};
045ae0a7 85 }
1dc01b9f
DM
86
87 return join(',', @cta);
88}
89
90sub valid_content_types {
91 my ($type) = @_;
92
93 my $def = $defaultData->{plugindata}->{$type};
94
95 return {} if !$def;
96
97 return $def->{content}->[0];
98}
99
100sub default_format {
101 my ($scfg) = @_;
102
103 my $type = $scfg->{type};
104 my $def = $defaultData->{plugindata}->{$type};
045ae0a7 105
1dc01b9f
DM
106 my $def_format = 'raw';
107 my $valid_formats = [ $def_format ];
108
109 if (defined($def->{format})) {
110 $def_format = $scfg->{format} || $def->{format}->[1];
111 $valid_formats = [ sort keys %{$def->{format}->[0]} ];
112 }
045ae0a7 113
1dc01b9f
DM
114 return wantarray ? ($def_format, $valid_formats) : $def_format;
115}
116
117PVE::JSONSchema::register_format('pve-storage-path', \&verify_path);
118sub verify_path {
119 my ($path, $noerr) = @_;
120
121 # fixme: exclude more shell meta characters?
122 # we need absolute paths
123 if ($path !~ m|^/[^;\(\)]+|) {
124 return undef if $noerr;
125 die "value does not look like a valid absolute path\n";
126 }
127 return $path;
128}
129
130PVE::JSONSchema::register_format('pve-storage-server', \&verify_server);
131sub verify_server {
132 my ($server, $noerr) = @_;
133
6bf617a9
WB
134 if (!(PVE::JSONSchema::pve_verify_ip($server, 1) ||
135 PVE::JSONSchema::pve_verify_dns_name($server, 1)))
136 {
1dc01b9f
DM
137 return undef if $noerr;
138 die "value does not look like a valid server name or IP address\n";
139 }
140 return $server;
141}
142
5dca5c7c
DM
143PVE::JSONSchema::register_format('pve-storage-vgname', \&parse_lvm_name);
144sub parse_lvm_name {
145 my ($name, $noerr) = @_;
146
147 if ($name !~ m/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i) {
148 return undef if $noerr;
149 die "lvm name '$name' contains illegal characters\n";
150 }
151
152 return $name;
153}
154
1dc01b9f
DM
155# fixme: do we need this
156#PVE::JSONSchema::register_format('pve-storage-portal', \&verify_portal);
157#sub verify_portal {
158# my ($portal, $noerr) = @_;
159#
160# # IP with optional port
161# if ($portal !~ m/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d+)?$/) {
162# return undef if $noerr;
163# die "value does not look like a valid portal address\n";
164# }
165# return $portal;
166#}
167
168PVE::JSONSchema::register_format('pve-storage-portal-dns', \&verify_portal_dns);
169sub verify_portal_dns {
170 my ($portal, $noerr) = @_;
171
172 # IP or DNS name with optional port
1689e627 173 if (!PVE::Tools::parse_host_and_port($portal)) {
1dc01b9f
DM
174 return undef if $noerr;
175 die "value does not look like a valid portal address\n";
176 }
177 return $portal;
178}
179
180PVE::JSONSchema::register_format('pve-storage-content', \&verify_content);
181sub verify_content {
182 my ($ct, $noerr) = @_;
183
184 my $valid_content = valid_content_types('dir'); # dir includes all types
045ae0a7 185
1dc01b9f
DM
186 if (!$valid_content->{$ct}) {
187 return undef if $noerr;
188 die "invalid content type '$ct'\n";
189 }
190
191 return $ct;
192}
193
194PVE::JSONSchema::register_format('pve-storage-format', \&verify_format);
195sub verify_format {
196 my ($fmt, $noerr) = @_;
197
35533c68 198 if ($fmt !~ m/(raw|qcow2|vmdk|subvol)/) {
1dc01b9f
DM
199 return undef if $noerr;
200 die "invalid format '$fmt'\n";
201 }
202
203 return $fmt;
204}
205
206PVE::JSONSchema::register_format('pve-storage-options', \&verify_options);
207sub verify_options {
208 my ($value, $noerr) = @_;
209
210 # mount options (see man fstab)
211 if ($value !~ m/^\S+$/) {
212 return undef if $noerr;
213 die "invalid options '$value'\n";
214 }
215
216 return $value;
217}
218
a7f3d909
DM
219PVE::JSONSchema::register_format('pve-volume-id', \&parse_volume_id);
220sub parse_volume_id {
221 my ($volid, $noerr) = @_;
222
223 if ($volid =~ m/^([a-z][a-z0-9\-\_\.]*[a-z0-9]):(.+)$/i) {
224 return wantarray ? ($1, $2) : $1;
225 }
226 return undef if $noerr;
227 die "unable to parse volume ID '$volid'\n";
228}
229
1dc01b9f
DM
230
231sub private {
232 return $defaultData;
233}
234
235sub parse_section_header {
236 my ($class, $line) = @_;
237
238 if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
239 my ($type, $storeid) = (lc($1), $2);
240 my $errmsg = undef; # set if you want to skip whole section
241 eval { PVE::JSONSchema::parse_storage_id($storeid); };
242 $errmsg = $@ if $@;
243 my $config = {}; # to return additional attributes
244 return ($type, $storeid, $errmsg, $config);
245 }
246 return undef;
247}
248
249sub decode_value {
250 my ($class, $type, $key, $value) = @_;
251
252 my $def = $defaultData->{plugindata}->{$type};
253
254 if ($key eq 'content') {
255 my $valid_content = $def->{content}->[0];
045ae0a7 256
1dc01b9f
DM
257 my $res = {};
258
259 foreach my $c (PVE::Tools::split_list($value)) {
260 if (!$valid_content->{$c}) {
703de49e
WL
261 warn "storage does not support content type '$c'\n";
262 next;
1dc01b9f
DM
263 }
264 $res->{$c} = 1;
045ae0a7 265 }
1dc01b9f
DM
266
267 if ($res->{none} && scalar (keys %$res) > 1) {
268 die "unable to combine 'none' with other content types\n";
269 }
270
271 return $res;
272 } elsif ($key eq 'format') {
273 my $valid_formats = $def->{format}->[0];
274
275 if (!$valid_formats->{$value}) {
703de49e
WL
276 warn "storage does not support format '$value'\n";
277 next;
1dc01b9f
DM
278 }
279
280 return $value;
281 } elsif ($key eq 'nodes') {
282 my $res = {};
283
284 foreach my $node (PVE::Tools::split_list($value)) {
285 if (PVE::JSONSchema::pve_verify_node_name($node)) {
286 $res->{$node} = 1;
287 }
288 }
289
290 # fixme:
291 # no node restrictions for local storage
292 #if ($storeid && $storeid eq 'local' && scalar(keys(%$res))) {
293 # die "storage '$storeid' does not allow node restrictions\n";
294 #}
295
296 return $res;
297 }
298
299 return $value;
300}
301
302sub encode_value {
303 my ($class, $type, $key, $value) = @_;
304
305 if ($key eq 'nodes') {
306 return join(',', keys(%$value));
307 } elsif ($key eq 'content') {
308 my $res = content_hash_to_string($value) || 'none';
309 return $res;
310 }
311
312 return $value;
313}
314
315sub parse_config {
316 my ($class, $filename, $raw) = @_;
317
318 my $cfg = $class->SUPER::parse_config($filename, $raw);
319 my $ids = $cfg->{ids};
320
321 # make sure we have a reasonable 'local:' storage
dc6ff39f 322 # we want 'local' to be always the same 'type' (on all cluster nodes)
1dc01b9f
DM
323 if (!$ids->{local} || $ids->{local}->{type} ne 'dir' ||
324 ($ids->{local}->{path} && $ids->{local}->{path} ne '/var/lib/vz')) {
325 $ids->{local} = {
326 type => 'dir',
327 priority => 0, # force first entry
328 path => '/var/lib/vz',
329 maxfiles => 0,
330 content => { images => 1, rootdir => 1, vztmpl => 1, iso => 1},
331 };
332 }
045ae0a7 333
1dc01b9f
DM
334 # make sure we have a path
335 $ids->{local}->{path} = '/var/lib/vz' if !$ids->{local}->{path};
336
337 # remove node restrictions for local storage
338 delete($ids->{local}->{nodes});
339
340 foreach my $storeid (keys %$ids) {
341 my $d = $ids->{$storeid};
342 my $type = $d->{type};
343
344 my $def = $defaultData->{plugindata}->{$type};
345
346 if ($def->{content}) {
347 $d->{content} = $def->{content}->[1] if !$d->{content};
348 }
d7875239 349 if (grep { $_ eq $type } @SHARED_STORAGE) {
1dc01b9f
DM
350 $d->{shared} = 1;
351 }
352 }
353
354 return $cfg;
355}
356
357# Storage implementation
358
3932ca0d
TL
359# called during addition of storage (before the new storage config got written)
360# die to abort additon if there are (grave) problems
361# NOTE: runs in a storage config *locked* context
362sub on_add_hook {
363 my ($class, $storeid, $scfg, %param) = @_;
364
365 # do nothing by default
366}
367
368# called during deletion of storage (before the new storage config got written)
369# and if the activate check on addition fails, to cleanup all storage traces
370# which on_add_hook may have created.
371# die to abort deletion if there are (very grave) problems
372# NOTE: runs in a storage config *locked* context
373sub on_delete_hook {
374 my ($class, $storeid, $scfg) = @_;
375
376 # do nothing by default
377}
378
1dc01b9f
DM
379sub cluster_lock_storage {
380 my ($class, $storeid, $shared, $timeout, $func, @param) = @_;
381
382 my $res;
383 if (!$shared) {
384 my $lockid = "pve-storage-$storeid";
385 my $lockdir = "/var/lock/pve-manager";
386 mkdir $lockdir;
045ae0a7 387 $res = PVE::Tools::lock_file("$lockdir/$lockid", $timeout, $func, @param);
1dc01b9f
DM
388 die $@ if $@;
389 } else {
390 $res = PVE::Cluster::cfs_lock_storage($storeid, $timeout, $func, @param);
391 die $@ if $@;
045ae0a7 392 }
1dc01b9f
DM
393 return $res;
394}
395
396sub parse_name_dir {
397 my $name = shift;
398
35533c68
DM
399 if ($name =~ m!^((base-)?[^/\s]+\.(raw|qcow2|vmdk|subvol))$!) {
400 return ($1, $3, $2); # (name, format, isBase)
1dc01b9f
DM
401 }
402
403 die "unable to parse volume filename '$name'\n";
404}
405
406sub parse_volname {
407 my ($class, $volname) = @_;
408
2502b33b
DM
409 if ($volname =~ m!^(\d+)/(\S+)/(\d+)/(\S+)$!) {
410 my ($basedvmid, $basename) = ($1, $2);
411 parse_name_dir($basename);
412 my ($vmid, $name) = ($3, $4);
35533c68
DM
413 my (undef, $format, $isBase) = parse_name_dir($name);
414 return ('images', $name, $vmid, $basename, $basedvmid, $isBase, $format);
2502b33b 415 } elsif ($volname =~ m!^(\d+)/(\S+)$!) {
1dc01b9f 416 my ($vmid, $name) = ($1, $2);
35533c68
DM
417 my (undef, $format, $isBase) = parse_name_dir($name);
418 return ('images', $name, $vmid, undef, undef, $isBase, $format);
1dc01b9f
DM
419 } elsif ($volname =~ m!^iso/([^/]+\.[Ii][Ss][Oo])$!) {
420 return ('iso', $1);
13d2cb79 421 } elsif ($volname =~ m!^vztmpl/([^/]+\.tar\.[gx]z)$!) {
1dc01b9f
DM
422 return ('vztmpl', $1);
423 } elsif ($volname =~ m!^rootdir/(\d+)$!) {
424 return ('rootdir', $1, $1);
a22854e5 425 } elsif ($volname =~ m!^backup/([^/]+(\.(tar|tar\.gz|tar\.lzo|tgz|vma|vma\.gz|vma\.lzo)))$!) {
1dc01b9f 426 my $fn = $1;
4cb6e060 427 if ($fn =~ m/^vzdump-(openvz|lxc|qemu)-(\d+)-.+/) {
1dc01b9f
DM
428 return ('backup', $fn, $2);
429 }
430 return ('backup', $fn);
431 }
432
433 die "unable to parse directory volume name '$volname'\n";
434}
435
045ae0a7 436my $vtype_subdirs = {
1dc01b9f
DM
437 images => 'images',
438 rootdir => 'private',
439 iso => 'template/iso',
440 vztmpl => 'template/cache',
441 backup => 'dump',
442};
443
444sub get_subdir {
445 my ($class, $scfg, $vtype) = @_;
446
447 my $path = $scfg->{path};
448
449 die "storage definintion has no path\n" if !$path;
450
451 my $subdir = $vtype_subdirs->{$vtype};
452
453 die "unknown vtype '$vtype'\n" if !defined($subdir);
454
045ae0a7 455 return "$path/$subdir";
1dc01b9f
DM
456}
457
08480ce7 458sub filesystem_path {
e67069eb 459 my ($class, $scfg, $volname, $snapname) = @_;
1dc01b9f 460
e67069eb
DM
461 my ($vtype, $name, $vmid, undef, undef, $isBase, $format) =
462 $class->parse_volname($volname);
463
464 # Note: qcow2/qed has internal snapshot, so path is always
465 # the same (with or without snapshot => same file).
466 die "can't snapshot this image format\n"
467 if defined($snapname) && $format !~ m/^(qcow2|qed)$/;
1dc01b9f
DM
468
469 my $dir = $class->get_subdir($scfg, $vtype);
470
471 $dir .= "/$vmid" if $vtype eq 'images';
472
473 my $path = "$dir/$name";
474
475 return wantarray ? ($path, $vmid, $vtype) : $path;
476}
477
08480ce7 478sub path {
e67069eb 479 my ($class, $scfg, $volname, $storeid, $snapname) = @_;
08480ce7 480
e67069eb 481 return $class->filesystem_path($scfg, $volname, $snapname);
08480ce7
DM
482}
483
2502b33b
DM
484sub create_base {
485 my ($class, $storeid, $scfg, $volname) = @_;
486
487 # this only works for file based storage types
5510f5c9 488 die "storage definition has no path\n" if !$scfg->{path};
2502b33b 489
35533c68 490 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format) =
2502b33b
DM
491 $class->parse_volname($volname);
492
493 die "create_base on wrong vtype '$vtype'\n" if $vtype ne 'images';
494
495 die "create_base not possible with base image\n" if $isBase;
496
08480ce7 497 my $path = $class->filesystem_path($scfg, $volname);
2502b33b 498
35533c68
DM
499 my ($size, undef, $used, $parent) = file_size_info($path);
500 die "file_size_info on '$volname' failed\n" if !($format && defined($size));
2502b33b
DM
501
502 die "volname '$volname' contains wrong information about parent\n"
503 if $basename && (!$parent || $parent ne "../$basevmid/$basename");
504
505 my $newname = $name;
506 $newname =~ s/^vm-/base-/;
507
508 my $newvolname = $basename ? "$basevmid/$basename/$vmid/$newname" :
509 "$vmid/$newname";
510
08480ce7 511 my $newpath = $class->filesystem_path($scfg, $newvolname);
2502b33b
DM
512
513 die "file '$newpath' already exists\n" if -f $newpath;
514
1a3459ac 515 rename($path, $newpath) ||
2502b33b
DM
516 die "rename '$path' to '$newpath' failed - $!\n";
517
c803c396 518 # We try to protect base volume
2502b33b 519
c803c396
DM
520 chmod(0444, $newpath); # nobody should write anything
521
522 # also try to set immutable flag
523 eval { run_command(['/usr/bin/chattr', '+i', $newpath]); };
524 warn $@ if $@;
1a3459ac 525
2502b33b
DM
526 return $newvolname;
527}
528
83a40b8d
TL
529my $get_vm_disk_number = sub {
530 my ($disk_name, $scfg, $vmid, $suffix) = @_;
345f8981 531
dd1fa860
TL
532 my $disk_regex = qr/(vm|base)-$vmid-disk-(\d+)$suffix/;
533
345f8981 534 my $type = $scfg->{type};
f4cc2c4a 535 my $def = { %{$defaultData->{plugindata}->{$type}} };
345f8981 536
dd1fa860
TL
537 my $valid = $def->{format}[0];
538 if ($valid->{subvol}) {
539 $disk_regex = qr/(vm|base|subvol|basevol)-$vmid-disk-(\d+)/;
540 }
345f8981 541
83a40b8d
TL
542 if ($disk_name =~ m/$disk_regex/) {
543 return $2;
345f8981 544 }
83a40b8d
TL
545
546 return undef;
547};
345f8981
SI
548
549sub get_next_vm_diskname {
550 my ($disk_list, $storeid, $vmid, $fmt, $scfg, $add_fmt_suffix) = @_;
551
345f8981
SI
552 $fmt //= '';
553 my $prefix = ($fmt eq 'subvol') ? 'subvol' : 'vm';
554 my $suffix = $add_fmt_suffix ? ".$fmt" : '';
555
83a40b8d
TL
556 my $disk_ids = {};
557 foreach my $disk (@$disk_list) {
558 my $disknum = $get_vm_disk_number->($disk, $scfg, $vmid, $suffix);
559 $disk_ids->{$disknum} = 1 if defined($disknum);
560 }
561
59fa9fd6 562 for (my $i = 0; $i < $MAX_VOLUMES_PER_GUEST; $i++) {
345f8981
SI
563 if (!$disk_ids->{$i}) {
564 return "$prefix-$vmid-disk-$i$suffix";
565 }
566 }
567
568 die "unable to allocate an image name for VM $vmid in storage '$storeid'\n"
569}
570
2502b33b 571my $find_free_diskname = sub {
c4a29df4 572 my ($imgdir, $vmid, $fmt, $scfg) = @_;
2502b33b 573
c4a29df4
SI
574 my $disk_list = [];
575
576 if (defined(my $dh = IO::Dir->new($imgdir))) {
577 @$disk_list = $dh->read();
578 $dh->close();
2502b33b
DM
579 }
580
c4a29df4 581 return get_next_vm_diskname($disk_list, $imgdir, $vmid, $fmt, $scfg, 1);
2502b33b
DM
582};
583
584sub clone_image {
f236eaf8 585 my ($class, $scfg, $storeid, $volname, $vmid, $snap) = @_;
2502b33b
DM
586
587 # this only works for file based storage types
588 die "storage definintion has no path\n" if !$scfg->{path};
589
35533c68 590 my ($vtype, $basename, $basevmid, undef, undef, $isBase, $format) =
2502b33b
DM
591 $class->parse_volname($volname);
592
593 die "clone_image on wrong vtype '$vtype'\n" if $vtype ne 'images';
594
f236eaf8
SP
595 die "this storage type does not support clone_image on snapshot\n" if $snap;
596
35533c68
DM
597 die "this storage type does not support clone_image on subvolumes\n" if $format eq 'subvol';
598
f236eaf8 599 die "clone_image only works on base images\n" if !$isBase;
1dc01b9f
DM
600
601 my $imagedir = $class->get_subdir($scfg, 'images');
602 $imagedir .= "/$vmid";
603
604 mkpath $imagedir;
605
c4a29df4 606 my $name = $find_free_diskname->($imagedir, $vmid, "qcow2", $scfg);
2502b33b
DM
607
608 warn "clone $volname: $vtype, $name, $vmid to $name (base=../$basevmid/$basename)\n";
609
610 my $newvol = "$basevmid/$basename/$vmid/$name";
611
08480ce7 612 my $path = $class->filesystem_path($scfg, $newvol);
2502b33b 613
1a3459ac 614 # Note: we use relative paths, so we need to call chdir before qemu-img
2502b33b 615 eval {
7fc619d5 616 local $CWD = $imagedir;
2502b33b 617
1a3459ac 618 my $cmd = ['/usr/bin/qemu-img', 'create', '-b', "../$basevmid/$basename",
2502b33b 619 '-f', 'qcow2', $path];
1a3459ac 620
2502b33b
DM
621 run_command($cmd);
622 };
623 my $err = $@;
1dc01b9f 624
2502b33b
DM
625 die $err if $err;
626
627 return $newvol;
628}
629
630sub alloc_image {
631 my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
632
633 my $imagedir = $class->get_subdir($scfg, 'images');
634 $imagedir .= "/$vmid";
635
636 mkpath $imagedir;
637
c4a29df4 638 $name = $find_free_diskname->($imagedir, $vmid, $fmt, $scfg) if !$name;
1a3459ac 639
1dc01b9f
DM
640 my (undef, $tmpfmt) = parse_name_dir($name);
641
045ae0a7 642 die "illegal name '$name' - wrong extension for format ('$tmpfmt != '$fmt')\n"
1dc01b9f
DM
643 if $tmpfmt ne $fmt;
644
645 my $path = "$imagedir/$name";
646
647 die "disk image '$path' already exists\n" if -e $path;
648
35533c68
DM
649 if ($fmt eq 'subvol') {
650 # only allow this if size = 0, so that user knows what he is doing
651 die "storage does not support subvol quotas\n" if $size != 0;
652
1f5734bb
WB
653 my $old_umask = umask(0022);
654 my $err;
655 mkdir($path) or $err = "unable to create subvol '$path' - $!\n";
656 umask $old_umask;
657 die $err if $err;
35533c68
DM
658 } else {
659 my $cmd = ['/usr/bin/qemu-img', 'create'];
045ae0a7 660
35533c68
DM
661 push @$cmd, '-o', 'preallocation=metadata' if $fmt eq 'qcow2';
662
663 push @$cmd, '-f', $fmt, $path, "${size}K";
1dc01b9f 664
35533c68
DM
665 run_command($cmd, errmsg => "unable to create image");
666 }
667
1dc01b9f
DM
668 return "$vmid/$name";
669}
670
671sub free_image {
35533c68 672 my ($class, $storeid, $scfg, $volname, $isBase, $format) = @_;
1dc01b9f 673
08480ce7 674 my $path = $class->filesystem_path($scfg, $volname);
1dc01b9f 675
f5451f28
DC
676 if ($isBase) {
677 # try to remove immutable flag
678 eval { run_command(['/usr/bin/chattr', '-i', $path]); };
679 warn $@ if $@;
680 }
681
4a7d2222 682 if (defined($format) && ($format eq 'subvol')) {
35533c68
DM
683 File::Path::remove_tree($path);
684 } else {
685
686 if (! -f $path) {
687 warn "disk image '$path' does not exists\n";
688 return undef;
689 }
1dc01b9f 690
35533c68
DM
691 unlink($path) || die "unlink '$path' failed - $!\n";
692 }
712e27f1
CE
693
694 # try to cleanup directory to not clutter storage with empty $vmid dirs if
695 # all images from a guest got deleted
696 my $dir = dirname($path);
697 rmdir($dir);
35533c68 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
40d69893
DM
958sub map_volume {
959 my ($class, $storeid, $scfg, $volname, $snapname) = @_;
960
961 return undef;
962}
963
964sub unmap_volume {
965 my ($class, $storeid, $scfg, $volname, $snapname) = @_;
966
967 return 1;
968}
969
1dc01b9f 970sub activate_volume {
02e797b8 971 my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
1dc01b9f 972
02e797b8 973 my $path = $class->filesystem_path($scfg, $volname, $snapname);
1dc01b9f
DM
974
975 # check is volume exists
976 if ($scfg->{path}) {
977 die "volume '$storeid:$volname' does not exist\n" if ! -e $path;
978 } else {
979 die "volume '$storeid:$volname' does not exist\n" if ! -b $path;
980 }
981}
982
983sub deactivate_volume {
02e797b8 984 my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
1dc01b9f
DM
985
986 # do nothing by default
987}
988
c9eeac01
AD
989sub check_connection {
990 my ($class, $storeid, $scfg) = @_;
991 # do nothing by default
992 return 1;
993}
994
e1f6cb39
DM
995# Import/Export interface:
996# Any path based storage is assumed to support 'raw' and 'tar' streams, so
997# the default implementations will return this if $scfg->{path} is set,
998# mimicking the old PVE::Storage::storage_migrate() function.
999#
1000# Plugins may fall back to PVE::Storage::Plugin::volume_{export,import}...
1001# functions in case the format doesn't match their specialized
1002# implementations to reuse the raw/tar code.
1003#
1004# Format specification:
1005# The following formats are all prefixed with image information in the form
1006# of a 64 bit little endian unsigned integer (pack('Q<')) in order to be able
1007# to preallocate the image on storages which require it.
1008#
1009# raw+size: (image files only)
1010# A raw binary data stream such as produced via `dd if=TheImageFile`.
1011# qcow2+size, vmdk: (image files only)
1012# A raw qcow2/vmdk/... file such as produced via `dd if=some.qcow2` for
1013# files which are already in qcow2 format, or via `qemu-img convert`.
1014# Note that these formats are only valid with $with_snapshots being true.
1015# tar+size: (subvolumes only)
6b3a4a25
WB
1016# A GNU tar stream containing just the inner contents of the subvolume.
1017# This does not distinguish between the contents of a privileged or
1018# unprivileged container. In other words, this is from the root user
1019# namespace's point of view with no uid-mapping in effect.
1020# As produced via `tar -C vm-100-disk-1.subvol -cpf TheOutputFile.dat .`
e1f6cb39
DM
1021
1022# Plugins may reuse these helpers. Changes to the header format should be
1023# reflected by changes to the function prototypes.
1024sub write_common_header($$) {
1025 my ($fh, $image_size_in_bytes) = @_;
1026 syswrite($fh, pack("Q<", $image_size_in_bytes), 8);
1027}
1028
1029sub read_common_header($) {
1030 my ($fh) = @_;
1031 sysread($fh, my $size, 8);
1032 $size = unpack('Q<', $size);
1033 die "got a bad size (not a multiple of 1K)\n" if ($size&1023);
1034 # Size is in bytes!
1035 return $size;
1036}
1037
47f37b53
WB
1038# Export a volume into a file handle as a stream of desired format.
1039sub volume_export {
1040 my ($class, $scfg, $storeid, $fh, $volname, $format, $snapshot, $base_snapshot, $with_snapshots) = @_;
e1f6cb39
DM
1041 if ($scfg->{path} && !defined($snapshot) && !defined($base_snapshot)) {
1042 my $file = $class->path($scfg, $volname, $storeid)
1043 or goto unsupported;
1044 my ($size, $file_format) = file_size_info($file);
1045
1046 if ($format eq 'raw+size') {
1047 goto unsupported if $with_snapshots || $file_format eq 'subvol';
1048 write_common_header($fh, $size);
1049 if ($file_format eq 'raw') {
1050 run_command(['dd', "if=$file", "bs=4k"], output => '>&'.fileno($fh));
1051 } else {
1052 run_command(['qemu-img', 'convert', '-f', $file_format, '-O', 'raw', $file, '/dev/stdout'],
1053 output => '>&'.fileno($fh));
1054 }
1055 return;
1056 } elsif ($format =~ /^(qcow2|vmdk)\+size$/) {
1057 my $data_format = $1;
1058 goto unsupported if !$with_snapshots || $file_format ne $data_format;
1059 write_common_header($fh, $size);
1060 run_command(['dd', "if=$file", "bs=4k"], output => '>&'.fileno($fh));
1061 return;
1062 } elsif ($format eq 'tar+size') {
1063 goto unsupported if $file_format ne 'subvol';
1064 write_common_header($fh, $size);
6b3a4a25 1065 run_command(['tar', @COMMON_TAR_FLAGS, '-cf', '-', '-C', $file, '.'],
e1f6cb39
DM
1066 output => '>&'.fileno($fh));
1067 return;
1068 }
1069 }
1070 unsupported:
1071 die "volume export format $format not available for $class";
47f37b53
WB
1072}
1073
d390328b
WB
1074sub volume_export_formats {
1075 my ($class, $scfg, $storeid, $volname, $snapshot, $base_snapshot, $with_snapshots) = @_;
e1f6cb39
DM
1076 if ($scfg->{path} && !defined($snapshot) && !defined($base_snapshot)) {
1077 my $file = $class->path($scfg, $volname, $storeid)
1078 or return;
1079 my ($size, $format) = file_size_info($file);
1080
1081 if ($with_snapshots) {
1082 return ($format.'+size') if ($format eq 'qcow2' || $format eq 'vmdk');
1083 return ();
1084 }
1085 return ('tar+size') if $format eq 'subvol';
1086 return ('raw+size');
1087 }
1088 return ();
d390328b
WB
1089}
1090
47f37b53
WB
1091# Import data from a stream, creating a new or replacing or adding to an existing volume.
1092sub volume_import {
1093 my ($class, $scfg, $storeid, $fh, $volname, $format, $base_snapshot, $with_snapshots) = @_;
e1f6cb39
DM
1094
1095 die "volume import format '$format' not available for $class\n"
1096 if $format !~ /^(raw|tar|qcow2|vmdk)\+size$/;
1097 my $data_format = $1;
1098
1099 die "format $format cannot be imported without snapshots\n"
1100 if !$with_snapshots && ($data_format eq 'qcow2' || $data_format eq 'vmdk');
1101 die "format $format cannot be imported with snapshots\n"
1102 if $with_snapshots && ($data_format eq 'raw' || $data_format eq 'tar');
1103
1104 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $file_format) =
1105 $class->parse_volname($volname);
1106
1107 # XXX: Should we bother with conversion routines at this level? This won't
1108 # happen without manual CLI usage, so for now we just error out...
1109 die "cannot import format $format into a file of format $file_format\n"
1110 if $data_format ne $file_format && !($data_format eq 'tar' && $file_format eq 'subvol');
1111
1112 # Check for an existing file first since interrupting alloc_image doesn't
1113 # free it.
1114 my $file = $class->path($scfg, $volname, $storeid);
1115 die "file '$file' already exists\n" if -e $file;
1116
1117 my ($size) = read_common_header($fh);
1118 $size = int($size/1024);
1119
1120 eval {
1121 my $allocname = $class->alloc_image($storeid, $scfg, $vmid, $file_format, $name, $size);
1122 if ($allocname ne $volname) {
1123 my $oldname = $volname;
1124 $volname = $allocname; # Let the cleanup code know what to free
1125 die "internal error: unexpected allocated name: '$allocname' != '$oldname'\n";
1126 }
1127 my $file = $class->path($scfg, $volname, $storeid)
1128 or die "internal error: failed to get path to newly allocated volume $volname\n";
1129 if ($data_format eq 'raw' || $data_format eq 'qcow2' || $data_format eq 'vmdk') {
1130 run_command(['dd', "of=$file", 'conv=sparse', 'bs=64k'],
1131 input => '<&'.fileno($fh));
1132 } elsif ($data_format eq 'tar') {
6b3a4a25 1133 run_command(['tar', @COMMON_TAR_FLAGS, '-C', $file, '-xf', '-'],
e1f6cb39
DM
1134 input => '<&'.fileno($fh));
1135 } else {
1136 die "volume import format '$format' not available for $class";
1137 }
1138 };
1139 if (my $err = $@) {
1140 eval { $class->free_image($storeid, $scfg, $volname, 0, $file_format) };
1141 warn $@ if $@;
1142 die $err;
1143 }
47f37b53 1144}
e47e548e 1145
d390328b
WB
1146sub volume_import_formats {
1147 my ($class, $scfg, $storeid, $volname, $base_snapshot, $with_snapshots) = @_;
e1f6cb39
DM
1148 if ($scfg->{path} && !defined($base_snapshot)) {
1149 my $format = ($class->parse_volname($volname))[6];
1150 if ($with_snapshots) {
1151 return ($format.'+size') if ($format eq 'qcow2' || $format eq 'vmdk');
1152 return ();
1153 }
1154 return ('tar+size') if $format eq 'subvol';
1155 return ('raw+size');
1156 }
1157 return ();
d390328b
WB
1158}
1159
1dc01b9f 11601;