]> git.proxmox.com Git - pve-storage.git/blame - PVE/Storage/Plugin.pm
bump version to 4.0-26
[pve-storage.git] / PVE / Storage / Plugin.pm
CommitLineData
1dc01b9f
DM
1package PVE::Storage::Plugin;
2
3use strict;
4use warnings;
7fc619d5 5use File::chdir;
1dc01b9f
DM
6use File::Path;
7use PVE::Tools qw(run_command);
8use PVE::JSONSchema qw(get_standard_option);
9use PVE::Cluster qw(cfs_register_file);
10
11use Data::Dumper;
12
13use base qw(PVE::SectionConfig);
14
045ae0a7 15cfs_register_file ('storage.cfg',
1dc01b9f
DM
16 sub { __PACKAGE__->parse_config(@_); },
17 sub { __PACKAGE__->write_config(@_); });
18
35533c68 19
1dc01b9f
DM
20my $defaultData = {
21 propertyList => {
22 type => { description => "Storage type." },
f7621c01
DM
23 storage => get_standard_option('pve-storage-id',
24 { completion => \&PVE::Storage::complete_storage }),
1dc01b9f
DM
25 nodes => get_standard_option('pve-node-list', { optional => 1 }),
26 content => {
1f79bb07 27 description => "Allowed content types. Note: value 'rootdir' is used for Containers, and value 'images' for KVM-Qemu VM's.\n",
1dc01b9f
DM
28 type => 'string', format => 'pve-storage-content-list',
29 optional => 1,
98437f4c 30 completion => \&PVE::Storage::complete_content_type,
1dc01b9f
DM
31 },
32 disable => {
33 description => "Flag to disable the storage.",
34 type => 'boolean',
35 optional => 1,
36 },
37 maxfiles => {
38 description => "Maximal number of backup files per VM. Use '0' for unlimted.",
39 type => 'integer',
40 minimum => 0,
41 optional => 1,
42 },
43 shared => {
44 description => "Mark storage as shared.",
45 type => 'boolean',
46 optional => 1,
47 },
045ae0a7 48 'format' => {
1dc01b9f
DM
49 description => "Default Image format.",
50 type => 'string', format => 'pve-storage-format',
51 optional => 1,
52 },
53 },
54};
55
56sub content_hash_to_string {
57 my $hash = shift;
58
59 my @cta;
60 foreach my $ct (keys %$hash) {
61 push @cta, $ct if $hash->{$ct};
045ae0a7 62 }
1dc01b9f
DM
63
64 return join(',', @cta);
65}
66
67sub valid_content_types {
68 my ($type) = @_;
69
70 my $def = $defaultData->{plugindata}->{$type};
71
72 return {} if !$def;
73
74 return $def->{content}->[0];
75}
76
77sub default_format {
78 my ($scfg) = @_;
79
80 my $type = $scfg->{type};
81 my $def = $defaultData->{plugindata}->{$type};
045ae0a7 82
1dc01b9f
DM
83 my $def_format = 'raw';
84 my $valid_formats = [ $def_format ];
85
86 if (defined($def->{format})) {
87 $def_format = $scfg->{format} || $def->{format}->[1];
88 $valid_formats = [ sort keys %{$def->{format}->[0]} ];
89 }
045ae0a7 90
1dc01b9f
DM
91 return wantarray ? ($def_format, $valid_formats) : $def_format;
92}
93
94PVE::JSONSchema::register_format('pve-storage-path', \&verify_path);
95sub verify_path {
96 my ($path, $noerr) = @_;
97
98 # fixme: exclude more shell meta characters?
99 # we need absolute paths
100 if ($path !~ m|^/[^;\(\)]+|) {
101 return undef if $noerr;
102 die "value does not look like a valid absolute path\n";
103 }
104 return $path;
105}
106
107PVE::JSONSchema::register_format('pve-storage-server', \&verify_server);
108sub verify_server {
109 my ($server, $noerr) = @_;
110
6bf617a9
WB
111 if (!(PVE::JSONSchema::pve_verify_ip($server, 1) ||
112 PVE::JSONSchema::pve_verify_dns_name($server, 1)))
113 {
1dc01b9f
DM
114 return undef if $noerr;
115 die "value does not look like a valid server name or IP address\n";
116 }
117 return $server;
118}
119
120# fixme: do we need this
121#PVE::JSONSchema::register_format('pve-storage-portal', \&verify_portal);
122#sub verify_portal {
123# my ($portal, $noerr) = @_;
124#
125# # IP with optional port
126# if ($portal !~ m/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d+)?$/) {
127# return undef if $noerr;
128# die "value does not look like a valid portal address\n";
129# }
130# return $portal;
131#}
132
133PVE::JSONSchema::register_format('pve-storage-portal-dns', \&verify_portal_dns);
134sub verify_portal_dns {
135 my ($portal, $noerr) = @_;
136
137 # IP or DNS name with optional port
1689e627 138 if (!PVE::Tools::parse_host_and_port($portal)) {
1dc01b9f
DM
139 return undef if $noerr;
140 die "value does not look like a valid portal address\n";
141 }
142 return $portal;
143}
144
145PVE::JSONSchema::register_format('pve-storage-content', \&verify_content);
146sub verify_content {
147 my ($ct, $noerr) = @_;
148
149 my $valid_content = valid_content_types('dir'); # dir includes all types
045ae0a7 150
1dc01b9f
DM
151 if (!$valid_content->{$ct}) {
152 return undef if $noerr;
153 die "invalid content type '$ct'\n";
154 }
155
156 return $ct;
157}
158
159PVE::JSONSchema::register_format('pve-storage-format', \&verify_format);
160sub verify_format {
161 my ($fmt, $noerr) = @_;
162
35533c68 163 if ($fmt !~ m/(raw|qcow2|vmdk|subvol)/) {
1dc01b9f
DM
164 return undef if $noerr;
165 die "invalid format '$fmt'\n";
166 }
167
168 return $fmt;
169}
170
171PVE::JSONSchema::register_format('pve-storage-options', \&verify_options);
172sub verify_options {
173 my ($value, $noerr) = @_;
174
175 # mount options (see man fstab)
176 if ($value !~ m/^\S+$/) {
177 return undef if $noerr;
178 die "invalid options '$value'\n";
179 }
180
181 return $value;
182}
183
a7f3d909
DM
184PVE::JSONSchema::register_format('pve-volume-id', \&parse_volume_id);
185sub parse_volume_id {
186 my ($volid, $noerr) = @_;
187
188 if ($volid =~ m/^([a-z][a-z0-9\-\_\.]*[a-z0-9]):(.+)$/i) {
189 return wantarray ? ($1, $2) : $1;
190 }
191 return undef if $noerr;
192 die "unable to parse volume ID '$volid'\n";
193}
194
1dc01b9f
DM
195
196sub private {
197 return $defaultData;
198}
199
200sub parse_section_header {
201 my ($class, $line) = @_;
202
203 if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
204 my ($type, $storeid) = (lc($1), $2);
205 my $errmsg = undef; # set if you want to skip whole section
206 eval { PVE::JSONSchema::parse_storage_id($storeid); };
207 $errmsg = $@ if $@;
208 my $config = {}; # to return additional attributes
209 return ($type, $storeid, $errmsg, $config);
210 }
211 return undef;
212}
213
214sub decode_value {
215 my ($class, $type, $key, $value) = @_;
216
217 my $def = $defaultData->{plugindata}->{$type};
218
219 if ($key eq 'content') {
220 my $valid_content = $def->{content}->[0];
045ae0a7 221
1dc01b9f
DM
222 my $res = {};
223
224 foreach my $c (PVE::Tools::split_list($value)) {
225 if (!$valid_content->{$c}) {
226 die "storage does not support content type '$c'\n";
227 }
228 $res->{$c} = 1;
045ae0a7 229 }
1dc01b9f
DM
230
231 if ($res->{none} && scalar (keys %$res) > 1) {
232 die "unable to combine 'none' with other content types\n";
233 }
234
235 return $res;
236 } elsif ($key eq 'format') {
237 my $valid_formats = $def->{format}->[0];
238
239 if (!$valid_formats->{$value}) {
240 die "storage does not support format '$value'\n";
241 }
242
243 return $value;
244 } elsif ($key eq 'nodes') {
245 my $res = {};
246
247 foreach my $node (PVE::Tools::split_list($value)) {
248 if (PVE::JSONSchema::pve_verify_node_name($node)) {
249 $res->{$node} = 1;
250 }
251 }
252
253 # fixme:
254 # no node restrictions for local storage
255 #if ($storeid && $storeid eq 'local' && scalar(keys(%$res))) {
256 # die "storage '$storeid' does not allow node restrictions\n";
257 #}
258
259 return $res;
260 }
261
262 return $value;
263}
264
265sub encode_value {
266 my ($class, $type, $key, $value) = @_;
267
268 if ($key eq 'nodes') {
269 return join(',', keys(%$value));
270 } elsif ($key eq 'content') {
271 my $res = content_hash_to_string($value) || 'none';
272 return $res;
273 }
274
275 return $value;
276}
277
278sub parse_config {
279 my ($class, $filename, $raw) = @_;
280
281 my $cfg = $class->SUPER::parse_config($filename, $raw);
282 my $ids = $cfg->{ids};
283
284 # make sure we have a reasonable 'local:' storage
285 # openvz expects things to be there
286 if (!$ids->{local} || $ids->{local}->{type} ne 'dir' ||
287 ($ids->{local}->{path} && $ids->{local}->{path} ne '/var/lib/vz')) {
288 $ids->{local} = {
289 type => 'dir',
290 priority => 0, # force first entry
291 path => '/var/lib/vz',
292 maxfiles => 0,
293 content => { images => 1, rootdir => 1, vztmpl => 1, iso => 1},
294 };
295 }
045ae0a7 296
1dc01b9f
DM
297 # we always need this for OpenVZ
298 $ids->{local}->{content}->{rootdir} = 1;
299 $ids->{local}->{content}->{vztmpl} = 1;
300 delete ($ids->{local}->{disable});
301
302 # make sure we have a path
303 $ids->{local}->{path} = '/var/lib/vz' if !$ids->{local}->{path};
304
305 # remove node restrictions for local storage
306 delete($ids->{local}->{nodes});
307
308 foreach my $storeid (keys %$ids) {
309 my $d = $ids->{$storeid};
310 my $type = $d->{type};
311
312 my $def = $defaultData->{plugindata}->{$type};
313
314 if ($def->{content}) {
315 $d->{content} = $def->{content}->[1] if !$d->{content};
316 }
317
d26e1891 318 if ($type eq 'iscsi' || $type eq 'nfs' || $type eq 'rbd' || $type eq 'sheepdog' || $type eq 'iscsidirect' || $type eq 'glusterfs' || $type eq 'zfs' || $type eq 'drbd') {
1dc01b9f
DM
319 $d->{shared} = 1;
320 }
321 }
322
323 return $cfg;
324}
325
326# Storage implementation
327
328sub cluster_lock_storage {
329 my ($class, $storeid, $shared, $timeout, $func, @param) = @_;
330
331 my $res;
332 if (!$shared) {
333 my $lockid = "pve-storage-$storeid";
334 my $lockdir = "/var/lock/pve-manager";
335 mkdir $lockdir;
045ae0a7 336 $res = PVE::Tools::lock_file("$lockdir/$lockid", $timeout, $func, @param);
1dc01b9f
DM
337 die $@ if $@;
338 } else {
339 $res = PVE::Cluster::cfs_lock_storage($storeid, $timeout, $func, @param);
340 die $@ if $@;
045ae0a7 341 }
1dc01b9f
DM
342 return $res;
343}
344
345sub parse_name_dir {
346 my $name = shift;
347
35533c68
DM
348 if ($name =~ m!^((base-)?[^/\s]+\.(raw|qcow2|vmdk|subvol))$!) {
349 return ($1, $3, $2); # (name, format, isBase)
1dc01b9f
DM
350 }
351
352 die "unable to parse volume filename '$name'\n";
353}
354
355sub parse_volname {
356 my ($class, $volname) = @_;
357
2502b33b
DM
358 if ($volname =~ m!^(\d+)/(\S+)/(\d+)/(\S+)$!) {
359 my ($basedvmid, $basename) = ($1, $2);
360 parse_name_dir($basename);
361 my ($vmid, $name) = ($3, $4);
35533c68
DM
362 my (undef, $format, $isBase) = parse_name_dir($name);
363 return ('images', $name, $vmid, $basename, $basedvmid, $isBase, $format);
2502b33b 364 } elsif ($volname =~ m!^(\d+)/(\S+)$!) {
1dc01b9f 365 my ($vmid, $name) = ($1, $2);
35533c68
DM
366 my (undef, $format, $isBase) = parse_name_dir($name);
367 return ('images', $name, $vmid, undef, undef, $isBase, $format);
1dc01b9f
DM
368 } elsif ($volname =~ m!^iso/([^/]+\.[Ii][Ss][Oo])$!) {
369 return ('iso', $1);
13d2cb79 370 } elsif ($volname =~ m!^vztmpl/([^/]+\.tar\.[gx]z)$!) {
1dc01b9f
DM
371 return ('vztmpl', $1);
372 } elsif ($volname =~ m!^rootdir/(\d+)$!) {
373 return ('rootdir', $1, $1);
a22854e5 374 } elsif ($volname =~ m!^backup/([^/]+(\.(tar|tar\.gz|tar\.lzo|tgz|vma|vma\.gz|vma\.lzo)))$!) {
1dc01b9f 375 my $fn = $1;
4cb6e060 376 if ($fn =~ m/^vzdump-(openvz|lxc|qemu)-(\d+)-.+/) {
1dc01b9f
DM
377 return ('backup', $fn, $2);
378 }
379 return ('backup', $fn);
380 }
381
382 die "unable to parse directory volume name '$volname'\n";
383}
384
045ae0a7 385my $vtype_subdirs = {
1dc01b9f
DM
386 images => 'images',
387 rootdir => 'private',
388 iso => 'template/iso',
389 vztmpl => 'template/cache',
390 backup => 'dump',
391};
392
393sub get_subdir {
394 my ($class, $scfg, $vtype) = @_;
395
396 my $path = $scfg->{path};
397
398 die "storage definintion has no path\n" if !$path;
399
400 my $subdir = $vtype_subdirs->{$vtype};
401
402 die "unknown vtype '$vtype'\n" if !defined($subdir);
403
045ae0a7 404 return "$path/$subdir";
1dc01b9f
DM
405}
406
08480ce7 407sub filesystem_path {
e67069eb 408 my ($class, $scfg, $volname, $snapname) = @_;
1dc01b9f 409
e67069eb
DM
410 my ($vtype, $name, $vmid, undef, undef, $isBase, $format) =
411 $class->parse_volname($volname);
412
413 # Note: qcow2/qed has internal snapshot, so path is always
414 # the same (with or without snapshot => same file).
415 die "can't snapshot this image format\n"
416 if defined($snapname) && $format !~ m/^(qcow2|qed)$/;
1dc01b9f
DM
417
418 my $dir = $class->get_subdir($scfg, $vtype);
419
420 $dir .= "/$vmid" if $vtype eq 'images';
421
422 my $path = "$dir/$name";
423
424 return wantarray ? ($path, $vmid, $vtype) : $path;
425}
426
08480ce7 427sub path {
e67069eb 428 my ($class, $scfg, $volname, $storeid, $snapname) = @_;
08480ce7 429
e67069eb 430 return $class->filesystem_path($scfg, $volname, $snapname);
08480ce7
DM
431}
432
2502b33b
DM
433sub create_base {
434 my ($class, $storeid, $scfg, $volname) = @_;
435
436 # this only works for file based storage types
437 die "storage definintion has no path\n" if !$scfg->{path};
438
35533c68 439 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format) =
2502b33b
DM
440 $class->parse_volname($volname);
441
442 die "create_base on wrong vtype '$vtype'\n" if $vtype ne 'images';
443
444 die "create_base not possible with base image\n" if $isBase;
445
08480ce7 446 my $path = $class->filesystem_path($scfg, $volname);
2502b33b 447
35533c68
DM
448 my ($size, undef, $used, $parent) = file_size_info($path);
449 die "file_size_info on '$volname' failed\n" if !($format && defined($size));
2502b33b
DM
450
451 die "volname '$volname' contains wrong information about parent\n"
452 if $basename && (!$parent || $parent ne "../$basevmid/$basename");
453
454 my $newname = $name;
455 $newname =~ s/^vm-/base-/;
456
457 my $newvolname = $basename ? "$basevmid/$basename/$vmid/$newname" :
458 "$vmid/$newname";
459
08480ce7 460 my $newpath = $class->filesystem_path($scfg, $newvolname);
2502b33b
DM
461
462 die "file '$newpath' already exists\n" if -f $newpath;
463
1a3459ac 464 rename($path, $newpath) ||
2502b33b
DM
465 die "rename '$path' to '$newpath' failed - $!\n";
466
c803c396 467 # We try to protect base volume
2502b33b 468
c803c396
DM
469 chmod(0444, $newpath); # nobody should write anything
470
471 # also try to set immutable flag
472 eval { run_command(['/usr/bin/chattr', '+i', $newpath]); };
473 warn $@ if $@;
1a3459ac 474
2502b33b
DM
475 return $newvolname;
476}
477
478my $find_free_diskname = sub {
479 my ($imgdir, $vmid, $fmt) = @_;
480
481 my $disk_ids = {};
1a3459ac 482 PVE::Tools::dir_glob_foreach($imgdir,
2502b33b
DM
483 qr!(vm|base)-$vmid-disk-(\d+)\..*!,
484 sub {
1a3459ac 485 my ($fn, $type, $disk) = @_;
2502b33b
DM
486 $disk_ids->{$disk} = 1;
487 });
488
489 for (my $i = 1; $i < 100; $i++) {
490 if (!$disk_ids->{$i}) {
491 return "vm-$vmid-disk-$i.$fmt";
492 }
493 }
494
495 die "unable to allocate a new image name for VM $vmid in '$imgdir'\n";
496};
497
498sub clone_image {
f236eaf8 499 my ($class, $scfg, $storeid, $volname, $vmid, $snap) = @_;
2502b33b
DM
500
501 # this only works for file based storage types
502 die "storage definintion has no path\n" if !$scfg->{path};
503
35533c68 504 my ($vtype, $basename, $basevmid, undef, undef, $isBase, $format) =
2502b33b
DM
505 $class->parse_volname($volname);
506
507 die "clone_image on wrong vtype '$vtype'\n" if $vtype ne 'images';
508
f236eaf8
SP
509 die "this storage type does not support clone_image on snapshot\n" if $snap;
510
35533c68
DM
511 die "this storage type does not support clone_image on subvolumes\n" if $format eq 'subvol';
512
f236eaf8 513 die "clone_image only works on base images\n" if !$isBase;
1dc01b9f
DM
514
515 my $imagedir = $class->get_subdir($scfg, 'images');
516 $imagedir .= "/$vmid";
517
518 mkpath $imagedir;
519
2502b33b
DM
520 my $name = &$find_free_diskname($imagedir, $vmid, "qcow2");
521
522 warn "clone $volname: $vtype, $name, $vmid to $name (base=../$basevmid/$basename)\n";
523
524 my $newvol = "$basevmid/$basename/$vmid/$name";
525
08480ce7 526 my $path = $class->filesystem_path($scfg, $newvol);
2502b33b 527
1a3459ac 528 # Note: we use relative paths, so we need to call chdir before qemu-img
2502b33b 529 eval {
7fc619d5 530 local $CWD = $imagedir;
2502b33b 531
1a3459ac 532 my $cmd = ['/usr/bin/qemu-img', 'create', '-b', "../$basevmid/$basename",
2502b33b 533 '-f', 'qcow2', $path];
1a3459ac 534
2502b33b
DM
535 run_command($cmd);
536 };
537 my $err = $@;
1dc01b9f 538
2502b33b
DM
539 die $err if $err;
540
541 return $newvol;
542}
543
544sub alloc_image {
545 my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
546
547 my $imagedir = $class->get_subdir($scfg, 'images');
548 $imagedir .= "/$vmid";
549
550 mkpath $imagedir;
551
552 $name = &$find_free_diskname($imagedir, $vmid, $fmt) if !$name;
1a3459ac 553
1dc01b9f
DM
554 my (undef, $tmpfmt) = parse_name_dir($name);
555
045ae0a7 556 die "illegal name '$name' - wrong extension for format ('$tmpfmt != '$fmt')\n"
1dc01b9f
DM
557 if $tmpfmt ne $fmt;
558
559 my $path = "$imagedir/$name";
560
561 die "disk image '$path' already exists\n" if -e $path;
562
35533c68
DM
563 if ($fmt eq 'subvol') {
564 # only allow this if size = 0, so that user knows what he is doing
565 die "storage does not support subvol quotas\n" if $size != 0;
566
567 (mkdir $path) || die "unable to create subvol '$path' - $!\n";
568 } else {
569 my $cmd = ['/usr/bin/qemu-img', 'create'];
045ae0a7 570
35533c68
DM
571 push @$cmd, '-o', 'preallocation=metadata' if $fmt eq 'qcow2';
572
573 push @$cmd, '-f', $fmt, $path, "${size}K";
1dc01b9f 574
35533c68
DM
575 run_command($cmd, errmsg => "unable to create image");
576 }
577
1dc01b9f
DM
578 return "$vmid/$name";
579}
580
581sub free_image {
35533c68 582 my ($class, $storeid, $scfg, $volname, $isBase, $format) = @_;
1dc01b9f 583
08480ce7 584 my $path = $class->filesystem_path($scfg, $volname);
1dc01b9f 585
35533c68
DM
586 if ($format eq 'subvol') {
587 File::Path::remove_tree($path);
588 } else {
589
590 if (! -f $path) {
591 warn "disk image '$path' does not exists\n";
592 return undef;
593 }
1dc01b9f 594
35533c68
DM
595 if ($isBase) {
596 # try to remove immutable flag
597 eval { run_command(['/usr/bin/chattr', '-i', $path]); };
598 warn $@ if $@;
599 }
a7f3d909 600
35533c68
DM
601 unlink($path) || die "unlink '$path' failed - $!\n";
602 }
603
1dc01b9f
DM
604 return undef;
605}
606
607sub file_size_info {
608 my ($filename, $timeout) = @_;
609
35533c68
DM
610 if (-d $filename) {
611 return wantarray ? (0, 'subvol', 0, undef) : 1;
612 }
613
1dc01b9f
DM
614 my $cmd = ['/usr/bin/qemu-img', 'info', $filename];
615
616 my $format;
73b7847e 617 my $parent;
1dc01b9f
DM
618 my $size = 0;
619 my $used = 0;
620
621 eval {
622 run_command($cmd, timeout => $timeout, outfunc => sub {
623 my $line = shift;
1dc01b9f
DM
624 if ($line =~ m/^file format:\s+(\S+)\s*$/) {
625 $format = $1;
73b7847e
AD
626 } elsif ($line =~ m/^backing file:\s(\S+)\s/) {
627 $parent = $1;
1dc01b9f
DM
628 } elsif ($line =~ m/^virtual size:\s\S+\s+\((\d+)\s+bytes\)$/) {
629 $size = int($1);
630 } elsif ($line =~ m/^disk size:\s+(\d+(.\d+)?)([KMGT])\s*$/) {
631 $used = $1;
632 my $u = $3;
633
634 $used *= 1024 if $u eq 'K';
635 $used *= (1024*1024) if $u eq 'M';
636 $used *= (1024*1024*1024) if $u eq 'G';
637 $used *= (1024*1024*1024*1024) if $u eq 'T';
638
639 $used = int($used);
640 }
641 });
642 };
643
73b7847e 644 return wantarray ? ($size, $format, $used, $parent) : $size;
1dc01b9f
DM
645}
646
e47e548e
AD
647sub volume_size_info {
648 my ($class, $scfg, $storeid, $volname, $timeout) = @_;
08480ce7 649 my $path = $class->filesystem_path($scfg, $volname);
e47e548e
AD
650 return file_size_info($path, $timeout);
651
652}
653
81f5058c
AD
654sub volume_resize {
655 my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
656
6d788031 657 die "can't resize this image format\n" if $volname !~ m/\.(raw|qcow2)$/;
81f5058c
AD
658
659 return 1 if $running;
660
08480ce7 661 my $path = $class->filesystem_path($scfg, $volname);
81f5058c
AD
662
663 my $cmd = ['/usr/bin/qemu-img', 'resize', $path , $size];
664
1059cc99 665 run_command($cmd, timeout => 10);
81f5058c
AD
666
667 return undef;
668}
669
7dcb0697 670sub volume_snapshot {
f5640e7d 671 my ($class, $scfg, $storeid, $volname, $snap) = @_;
7dcb0697 672
6d788031 673 die "can't snapshot this image format\n" if $volname !~ m/\.(qcow2|qed)$/;
7dcb0697 674
08480ce7 675 my $path = $class->filesystem_path($scfg, $volname);
7dcb0697
AD
676
677 my $cmd = ['/usr/bin/qemu-img', 'snapshot','-c', $snap, $path];
678
3f13fd7d 679 run_command($cmd);
7dcb0697
AD
680
681 return undef;
682}
683
1597f1f9
WL
684sub volume_rollback_is_possible {
685 my ($class, $scfg, $storeid, $volname, $snap) = @_;
686
687 return 1;
688}
689
41dffa85
AD
690sub volume_snapshot_rollback {
691 my ($class, $scfg, $storeid, $volname, $snap) = @_;
692
6d788031 693 die "can't rollback snapshot this image format\n" if $volname !~ m/\.(qcow2|qed)$/;
41dffa85 694
08480ce7 695 my $path = $class->filesystem_path($scfg, $volname);
41dffa85
AD
696
697 my $cmd = ['/usr/bin/qemu-img', 'snapshot','-a', $snap, $path];
698
3f13fd7d 699 run_command($cmd);
41dffa85
AD
700
701 return undef;
702}
703
6000a061
AD
704sub volume_snapshot_delete {
705 my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
706
6d788031 707 die "can't delete snapshot for this image format\n" if $volname !~ m/\.(qcow2|qed)$/;
6000a061
AD
708
709 return 1 if $running;
710
08480ce7 711 my $path = $class->filesystem_path($scfg, $volname);
6000a061 712
399581a2
WB
713 $class->deactivate_volume($storeid, $scfg, $volname, $snap, {});
714
6000a061
AD
715 my $cmd = ['/usr/bin/qemu-img', 'snapshot','-d', $snap, $path];
716
3f13fd7d 717 run_command($cmd);
6000a061
AD
718
719 return undef;
720}
721
f884fe11
AD
722sub volume_has_feature {
723 my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
724
725 my $features = {
5649ccfe
AD
726 snapshot => { current => { qcow2 => 1}, snap => { qcow2 => 1} },
727 clone => { base => {qcow2 => 1, raw => 1, vmdk => 1} },
35533c68 728 template => { current => {qcow2 => 1, raw => 1, vmdk => 1, subvol => 1} },
5649ccfe 729 copy => { base => {qcow2 => 1, raw => 1, vmdk => 1},
22b8cf97
AD
730 current => {qcow2 => 1, raw => 1, vmdk => 1},
731 snap => {qcow2 => 1} },
f884fe11
AD
732 };
733
35533c68 734 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format) =
dc4f2cb3
AD
735 $class->parse_volname($volname);
736
dc4f2cb3
AD
737 my $key = undef;
738 if($snapname){
2c5a7097 739 $key = 'snap';
dc4f2cb3
AD
740 }else{
741 $key = $isBase ? 'base' : 'current';
f884fe11 742 }
dc4f2cb3
AD
743
744 return 1 if defined($features->{$feature}->{$key}->{$format});
745
f884fe11
AD
746 return undef;
747}
748
1dc01b9f
DM
749sub list_images {
750 my ($class, $storeid, $scfg, $vmid, $vollist, $cache) = @_;
751
752 my $imagedir = $class->get_subdir($scfg, 'images');
753
754 my ($defFmt, $vaidFmts) = default_format($scfg);
045ae0a7 755 my $fmts = join ('|', @$vaidFmts);
1dc01b9f
DM
756
757 my $res = [];
758
759 foreach my $fn (<$imagedir/[0-9][0-9]*/*>) {
760
761 next if $fn !~ m!^(/.+/(\d+)/([^/]+\.($fmts)))$!;
762 $fn = $1; # untaint
763
764 my $owner = $2;
765 my $name = $3;
1dc01b9f 766
2502b33b 767 next if !$vollist && defined($vmid) && ($owner ne $vmid);
1dc01b9f 768
73b7847e 769 my ($size, $format, $used, $parent) = file_size_info($fn);
35533c68 770 next if !($format && defined($size));
2502b33b
DM
771
772 my $volid;
773 if ($parent && $parent =~ m!^../(\d+)/([^/]+\.($fmts))$!) {
774 my ($basevmid, $basename) = ($1, $2);
775 $volid = "$storeid:$basevmid/$basename/$owner/$name";
776 } else {
777 $volid = "$storeid:$owner/$name";
778 }
1dc01b9f 779
2502b33b
DM
780 if ($vollist) {
781 my $found = grep { $_ eq $volid } @$vollist;
782 next if !$found;
1dc01b9f
DM
783 }
784
2502b33b
DM
785 push @$res, {
786 volid => $volid, format => $format,
1a3459ac 787 size => $size, vmid => $owner, used => $used, parent => $parent
2502b33b 788 };
1dc01b9f
DM
789 }
790
791 return $res;
792}
793
794sub status {
795 my ($class, $storeid, $scfg, $cache) = @_;
796
797 my $path = $scfg->{path};
798
799 die "storage definintion has no path\n" if !$path;
045ae0a7 800
1dc01b9f
DM
801 my $timeout = 2;
802 my $res = PVE::Tools::df($path, $timeout);
803
804 return undef if !$res || !$res->{total};
805
806 return ($res->{total}, $res->{avail}, $res->{used}, 1);
807}
808
809sub activate_storage {
810 my ($class, $storeid, $scfg, $cache) = @_;
811
812 my $path = $scfg->{path};
813
814 die "storage definintion has no path\n" if !$path;
815
816 die "unable to activate storage '$storeid' - " .
817 "directory '$path' does not exist\n" if ! -d $path;
818
819 if (defined($scfg->{content})) {
820 foreach my $vtype (keys %$vtype_subdirs) {
6bcc16d7
DM
821 # OpenVZMigrate uses backup (dump) dir
822 if (defined($scfg->{content}->{$vtype}) ||
823 ($vtype eq 'backup' && defined($scfg->{content}->{'rootdir'}))) {
824 my $subdir = $class->get_subdir($scfg, $vtype);
825 mkpath $subdir if $subdir ne $path;
826 }
1dc01b9f
DM
827 }
828 }
829}
830
831sub deactivate_storage {
832 my ($class, $storeid, $scfg, $cache) = @_;
833
834 # do nothing by default
835}
836
837sub activate_volume {
02e797b8 838 my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
1dc01b9f 839
02e797b8 840 my $path = $class->filesystem_path($scfg, $volname, $snapname);
1dc01b9f
DM
841
842 # check is volume exists
843 if ($scfg->{path}) {
844 die "volume '$storeid:$volname' does not exist\n" if ! -e $path;
845 } else {
846 die "volume '$storeid:$volname' does not exist\n" if ! -b $path;
847 }
848}
849
850sub deactivate_volume {
02e797b8 851 my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
1dc01b9f
DM
852
853 # do nothing by default
854}
855
c9eeac01
AD
856sub check_connection {
857 my ($class, $storeid, $scfg) = @_;
858 # do nothing by default
859 return 1;
860}
861
e47e548e 862
1dc01b9f 8631;