]> git.proxmox.com Git - pve-storage.git/blame - PVE/Storage/Plugin.pm
bump version to 4.0-31
[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
dc6ff39f 285 # we want 'local' to be always the same 'type' (on all cluster nodes)
1dc01b9f
DM
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 # make sure we have a path
298 $ids->{local}->{path} = '/var/lib/vz' if !$ids->{local}->{path};
299
300 # remove node restrictions for local storage
301 delete($ids->{local}->{nodes});
302
303 foreach my $storeid (keys %$ids) {
304 my $d = $ids->{$storeid};
305 my $type = $d->{type};
306
307 my $def = $defaultData->{plugindata}->{$type};
308
309 if ($def->{content}) {
310 $d->{content} = $def->{content}->[1] if !$d->{content};
311 }
312
d26e1891 313 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
314 $d->{shared} = 1;
315 }
316 }
317
318 return $cfg;
319}
320
321# Storage implementation
322
323sub cluster_lock_storage {
324 my ($class, $storeid, $shared, $timeout, $func, @param) = @_;
325
326 my $res;
327 if (!$shared) {
328 my $lockid = "pve-storage-$storeid";
329 my $lockdir = "/var/lock/pve-manager";
330 mkdir $lockdir;
045ae0a7 331 $res = PVE::Tools::lock_file("$lockdir/$lockid", $timeout, $func, @param);
1dc01b9f
DM
332 die $@ if $@;
333 } else {
334 $res = PVE::Cluster::cfs_lock_storage($storeid, $timeout, $func, @param);
335 die $@ if $@;
045ae0a7 336 }
1dc01b9f
DM
337 return $res;
338}
339
340sub parse_name_dir {
341 my $name = shift;
342
35533c68
DM
343 if ($name =~ m!^((base-)?[^/\s]+\.(raw|qcow2|vmdk|subvol))$!) {
344 return ($1, $3, $2); # (name, format, isBase)
1dc01b9f
DM
345 }
346
347 die "unable to parse volume filename '$name'\n";
348}
349
350sub parse_volname {
351 my ($class, $volname) = @_;
352
2502b33b
DM
353 if ($volname =~ m!^(\d+)/(\S+)/(\d+)/(\S+)$!) {
354 my ($basedvmid, $basename) = ($1, $2);
355 parse_name_dir($basename);
356 my ($vmid, $name) = ($3, $4);
35533c68
DM
357 my (undef, $format, $isBase) = parse_name_dir($name);
358 return ('images', $name, $vmid, $basename, $basedvmid, $isBase, $format);
2502b33b 359 } elsif ($volname =~ m!^(\d+)/(\S+)$!) {
1dc01b9f 360 my ($vmid, $name) = ($1, $2);
35533c68
DM
361 my (undef, $format, $isBase) = parse_name_dir($name);
362 return ('images', $name, $vmid, undef, undef, $isBase, $format);
1dc01b9f
DM
363 } elsif ($volname =~ m!^iso/([^/]+\.[Ii][Ss][Oo])$!) {
364 return ('iso', $1);
13d2cb79 365 } elsif ($volname =~ m!^vztmpl/([^/]+\.tar\.[gx]z)$!) {
1dc01b9f
DM
366 return ('vztmpl', $1);
367 } elsif ($volname =~ m!^rootdir/(\d+)$!) {
368 return ('rootdir', $1, $1);
a22854e5 369 } elsif ($volname =~ m!^backup/([^/]+(\.(tar|tar\.gz|tar\.lzo|tgz|vma|vma\.gz|vma\.lzo)))$!) {
1dc01b9f 370 my $fn = $1;
4cb6e060 371 if ($fn =~ m/^vzdump-(openvz|lxc|qemu)-(\d+)-.+/) {
1dc01b9f
DM
372 return ('backup', $fn, $2);
373 }
374 return ('backup', $fn);
375 }
376
377 die "unable to parse directory volume name '$volname'\n";
378}
379
045ae0a7 380my $vtype_subdirs = {
1dc01b9f
DM
381 images => 'images',
382 rootdir => 'private',
383 iso => 'template/iso',
384 vztmpl => 'template/cache',
385 backup => 'dump',
386};
387
388sub get_subdir {
389 my ($class, $scfg, $vtype) = @_;
390
391 my $path = $scfg->{path};
392
393 die "storage definintion has no path\n" if !$path;
394
395 my $subdir = $vtype_subdirs->{$vtype};
396
397 die "unknown vtype '$vtype'\n" if !defined($subdir);
398
045ae0a7 399 return "$path/$subdir";
1dc01b9f
DM
400}
401
08480ce7 402sub filesystem_path {
e67069eb 403 my ($class, $scfg, $volname, $snapname) = @_;
1dc01b9f 404
e67069eb
DM
405 my ($vtype, $name, $vmid, undef, undef, $isBase, $format) =
406 $class->parse_volname($volname);
407
408 # Note: qcow2/qed has internal snapshot, so path is always
409 # the same (with or without snapshot => same file).
410 die "can't snapshot this image format\n"
411 if defined($snapname) && $format !~ m/^(qcow2|qed)$/;
1dc01b9f
DM
412
413 my $dir = $class->get_subdir($scfg, $vtype);
414
415 $dir .= "/$vmid" if $vtype eq 'images';
416
417 my $path = "$dir/$name";
418
419 return wantarray ? ($path, $vmid, $vtype) : $path;
420}
421
08480ce7 422sub path {
e67069eb 423 my ($class, $scfg, $volname, $storeid, $snapname) = @_;
08480ce7 424
e67069eb 425 return $class->filesystem_path($scfg, $volname, $snapname);
08480ce7
DM
426}
427
2502b33b
DM
428sub create_base {
429 my ($class, $storeid, $scfg, $volname) = @_;
430
431 # this only works for file based storage types
432 die "storage definintion has no path\n" if !$scfg->{path};
433
35533c68 434 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format) =
2502b33b
DM
435 $class->parse_volname($volname);
436
437 die "create_base on wrong vtype '$vtype'\n" if $vtype ne 'images';
438
439 die "create_base not possible with base image\n" if $isBase;
440
08480ce7 441 my $path = $class->filesystem_path($scfg, $volname);
2502b33b 442
35533c68
DM
443 my ($size, undef, $used, $parent) = file_size_info($path);
444 die "file_size_info on '$volname' failed\n" if !($format && defined($size));
2502b33b
DM
445
446 die "volname '$volname' contains wrong information about parent\n"
447 if $basename && (!$parent || $parent ne "../$basevmid/$basename");
448
449 my $newname = $name;
450 $newname =~ s/^vm-/base-/;
451
452 my $newvolname = $basename ? "$basevmid/$basename/$vmid/$newname" :
453 "$vmid/$newname";
454
08480ce7 455 my $newpath = $class->filesystem_path($scfg, $newvolname);
2502b33b
DM
456
457 die "file '$newpath' already exists\n" if -f $newpath;
458
1a3459ac 459 rename($path, $newpath) ||
2502b33b
DM
460 die "rename '$path' to '$newpath' failed - $!\n";
461
c803c396 462 # We try to protect base volume
2502b33b 463
c803c396
DM
464 chmod(0444, $newpath); # nobody should write anything
465
466 # also try to set immutable flag
467 eval { run_command(['/usr/bin/chattr', '+i', $newpath]); };
468 warn $@ if $@;
1a3459ac 469
2502b33b
DM
470 return $newvolname;
471}
472
473my $find_free_diskname = sub {
474 my ($imgdir, $vmid, $fmt) = @_;
475
476 my $disk_ids = {};
1a3459ac 477 PVE::Tools::dir_glob_foreach($imgdir,
2502b33b
DM
478 qr!(vm|base)-$vmid-disk-(\d+)\..*!,
479 sub {
1a3459ac 480 my ($fn, $type, $disk) = @_;
2502b33b
DM
481 $disk_ids->{$disk} = 1;
482 });
483
484 for (my $i = 1; $i < 100; $i++) {
485 if (!$disk_ids->{$i}) {
486 return "vm-$vmid-disk-$i.$fmt";
487 }
488 }
489
490 die "unable to allocate a new image name for VM $vmid in '$imgdir'\n";
491};
492
493sub clone_image {
f236eaf8 494 my ($class, $scfg, $storeid, $volname, $vmid, $snap) = @_;
2502b33b
DM
495
496 # this only works for file based storage types
497 die "storage definintion has no path\n" if !$scfg->{path};
498
35533c68 499 my ($vtype, $basename, $basevmid, undef, undef, $isBase, $format) =
2502b33b
DM
500 $class->parse_volname($volname);
501
502 die "clone_image on wrong vtype '$vtype'\n" if $vtype ne 'images';
503
f236eaf8
SP
504 die "this storage type does not support clone_image on snapshot\n" if $snap;
505
35533c68
DM
506 die "this storage type does not support clone_image on subvolumes\n" if $format eq 'subvol';
507
f236eaf8 508 die "clone_image only works on base images\n" if !$isBase;
1dc01b9f
DM
509
510 my $imagedir = $class->get_subdir($scfg, 'images');
511 $imagedir .= "/$vmid";
512
513 mkpath $imagedir;
514
2502b33b
DM
515 my $name = &$find_free_diskname($imagedir, $vmid, "qcow2");
516
517 warn "clone $volname: $vtype, $name, $vmid to $name (base=../$basevmid/$basename)\n";
518
519 my $newvol = "$basevmid/$basename/$vmid/$name";
520
08480ce7 521 my $path = $class->filesystem_path($scfg, $newvol);
2502b33b 522
1a3459ac 523 # Note: we use relative paths, so we need to call chdir before qemu-img
2502b33b 524 eval {
7fc619d5 525 local $CWD = $imagedir;
2502b33b 526
1a3459ac 527 my $cmd = ['/usr/bin/qemu-img', 'create', '-b', "../$basevmid/$basename",
2502b33b 528 '-f', 'qcow2', $path];
1a3459ac 529
2502b33b
DM
530 run_command($cmd);
531 };
532 my $err = $@;
1dc01b9f 533
2502b33b
DM
534 die $err if $err;
535
536 return $newvol;
537}
538
539sub alloc_image {
540 my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
541
542 my $imagedir = $class->get_subdir($scfg, 'images');
543 $imagedir .= "/$vmid";
544
545 mkpath $imagedir;
546
547 $name = &$find_free_diskname($imagedir, $vmid, $fmt) if !$name;
1a3459ac 548
1dc01b9f
DM
549 my (undef, $tmpfmt) = parse_name_dir($name);
550
045ae0a7 551 die "illegal name '$name' - wrong extension for format ('$tmpfmt != '$fmt')\n"
1dc01b9f
DM
552 if $tmpfmt ne $fmt;
553
554 my $path = "$imagedir/$name";
555
556 die "disk image '$path' already exists\n" if -e $path;
557
35533c68
DM
558 if ($fmt eq 'subvol') {
559 # only allow this if size = 0, so that user knows what he is doing
560 die "storage does not support subvol quotas\n" if $size != 0;
561
562 (mkdir $path) || die "unable to create subvol '$path' - $!\n";
563 } else {
564 my $cmd = ['/usr/bin/qemu-img', 'create'];
045ae0a7 565
35533c68
DM
566 push @$cmd, '-o', 'preallocation=metadata' if $fmt eq 'qcow2';
567
568 push @$cmd, '-f', $fmt, $path, "${size}K";
1dc01b9f 569
35533c68
DM
570 run_command($cmd, errmsg => "unable to create image");
571 }
572
1dc01b9f
DM
573 return "$vmid/$name";
574}
575
576sub free_image {
35533c68 577 my ($class, $storeid, $scfg, $volname, $isBase, $format) = @_;
1dc01b9f 578
08480ce7 579 my $path = $class->filesystem_path($scfg, $volname);
1dc01b9f 580
35533c68
DM
581 if ($format eq 'subvol') {
582 File::Path::remove_tree($path);
583 } else {
584
585 if (! -f $path) {
586 warn "disk image '$path' does not exists\n";
587 return undef;
588 }
1dc01b9f 589
35533c68
DM
590 if ($isBase) {
591 # try to remove immutable flag
592 eval { run_command(['/usr/bin/chattr', '-i', $path]); };
593 warn $@ if $@;
594 }
a7f3d909 595
35533c68
DM
596 unlink($path) || die "unlink '$path' failed - $!\n";
597 }
598
1dc01b9f
DM
599 return undef;
600}
601
602sub file_size_info {
603 my ($filename, $timeout) = @_;
604
35533c68
DM
605 if (-d $filename) {
606 return wantarray ? (0, 'subvol', 0, undef) : 1;
607 }
608
1dc01b9f
DM
609 my $cmd = ['/usr/bin/qemu-img', 'info', $filename];
610
611 my $format;
73b7847e 612 my $parent;
1dc01b9f
DM
613 my $size = 0;
614 my $used = 0;
615
616 eval {
617 run_command($cmd, timeout => $timeout, outfunc => sub {
618 my $line = shift;
1dc01b9f
DM
619 if ($line =~ m/^file format:\s+(\S+)\s*$/) {
620 $format = $1;
73b7847e
AD
621 } elsif ($line =~ m/^backing file:\s(\S+)\s/) {
622 $parent = $1;
1dc01b9f
DM
623 } elsif ($line =~ m/^virtual size:\s\S+\s+\((\d+)\s+bytes\)$/) {
624 $size = int($1);
625 } elsif ($line =~ m/^disk size:\s+(\d+(.\d+)?)([KMGT])\s*$/) {
626 $used = $1;
627 my $u = $3;
628
629 $used *= 1024 if $u eq 'K';
630 $used *= (1024*1024) if $u eq 'M';
631 $used *= (1024*1024*1024) if $u eq 'G';
632 $used *= (1024*1024*1024*1024) if $u eq 'T';
633
634 $used = int($used);
635 }
636 });
637 };
638
73b7847e 639 return wantarray ? ($size, $format, $used, $parent) : $size;
1dc01b9f
DM
640}
641
e47e548e
AD
642sub volume_size_info {
643 my ($class, $scfg, $storeid, $volname, $timeout) = @_;
08480ce7 644 my $path = $class->filesystem_path($scfg, $volname);
e47e548e
AD
645 return file_size_info($path, $timeout);
646
647}
648
81f5058c
AD
649sub volume_resize {
650 my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
651
6d788031 652 die "can't resize this image format\n" if $volname !~ m/\.(raw|qcow2)$/;
81f5058c
AD
653
654 return 1 if $running;
655
08480ce7 656 my $path = $class->filesystem_path($scfg, $volname);
81f5058c 657
0589e5f9
WL
658 my $format = ($class->parse_volname($volname))[6];
659
660 my $cmd = ['/usr/bin/qemu-img', 'resize', '-f', $format, $path , $size];
81f5058c 661
1059cc99 662 run_command($cmd, timeout => 10);
81f5058c
AD
663
664 return undef;
665}
666
7dcb0697 667sub volume_snapshot {
f5640e7d 668 my ($class, $scfg, $storeid, $volname, $snap) = @_;
7dcb0697 669
6d788031 670 die "can't snapshot this image format\n" if $volname !~ m/\.(qcow2|qed)$/;
7dcb0697 671
08480ce7 672 my $path = $class->filesystem_path($scfg, $volname);
7dcb0697
AD
673
674 my $cmd = ['/usr/bin/qemu-img', 'snapshot','-c', $snap, $path];
675
3f13fd7d 676 run_command($cmd);
7dcb0697
AD
677
678 return undef;
679}
680
1597f1f9
WL
681sub volume_rollback_is_possible {
682 my ($class, $scfg, $storeid, $volname, $snap) = @_;
683
684 return 1;
685}
686
41dffa85
AD
687sub volume_snapshot_rollback {
688 my ($class, $scfg, $storeid, $volname, $snap) = @_;
689
6d788031 690 die "can't rollback snapshot this image format\n" if $volname !~ m/\.(qcow2|qed)$/;
41dffa85 691
08480ce7 692 my $path = $class->filesystem_path($scfg, $volname);
41dffa85
AD
693
694 my $cmd = ['/usr/bin/qemu-img', 'snapshot','-a', $snap, $path];
695
3f13fd7d 696 run_command($cmd);
41dffa85
AD
697
698 return undef;
699}
700
6000a061
AD
701sub volume_snapshot_delete {
702 my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
703
6d788031 704 die "can't delete snapshot for this image format\n" if $volname !~ m/\.(qcow2|qed)$/;
6000a061
AD
705
706 return 1 if $running;
707
08480ce7 708 my $path = $class->filesystem_path($scfg, $volname);
6000a061 709
399581a2
WB
710 $class->deactivate_volume($storeid, $scfg, $volname, $snap, {});
711
6000a061
AD
712 my $cmd = ['/usr/bin/qemu-img', 'snapshot','-d', $snap, $path];
713
3f13fd7d 714 run_command($cmd);
6000a061
AD
715
716 return undef;
717}
718
f884fe11
AD
719sub volume_has_feature {
720 my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
721
722 my $features = {
5649ccfe
AD
723 snapshot => { current => { qcow2 => 1}, snap => { qcow2 => 1} },
724 clone => { base => {qcow2 => 1, raw => 1, vmdk => 1} },
35533c68 725 template => { current => {qcow2 => 1, raw => 1, vmdk => 1, subvol => 1} },
5649ccfe 726 copy => { base => {qcow2 => 1, raw => 1, vmdk => 1},
22b8cf97
AD
727 current => {qcow2 => 1, raw => 1, vmdk => 1},
728 snap => {qcow2 => 1} },
f884fe11
AD
729 };
730
35533c68 731 my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format) =
dc4f2cb3
AD
732 $class->parse_volname($volname);
733
dc4f2cb3
AD
734 my $key = undef;
735 if($snapname){
2c5a7097 736 $key = 'snap';
dc4f2cb3
AD
737 }else{
738 $key = $isBase ? 'base' : 'current';
f884fe11 739 }
dc4f2cb3
AD
740
741 return 1 if defined($features->{$feature}->{$key}->{$format});
742
f884fe11
AD
743 return undef;
744}
745
1dc01b9f
DM
746sub list_images {
747 my ($class, $storeid, $scfg, $vmid, $vollist, $cache) = @_;
748
749 my $imagedir = $class->get_subdir($scfg, 'images');
750
751 my ($defFmt, $vaidFmts) = default_format($scfg);
045ae0a7 752 my $fmts = join ('|', @$vaidFmts);
1dc01b9f
DM
753
754 my $res = [];
755
756 foreach my $fn (<$imagedir/[0-9][0-9]*/*>) {
757
758 next if $fn !~ m!^(/.+/(\d+)/([^/]+\.($fmts)))$!;
759 $fn = $1; # untaint
760
761 my $owner = $2;
762 my $name = $3;
1dc01b9f 763
2502b33b 764 next if !$vollist && defined($vmid) && ($owner ne $vmid);
1dc01b9f 765
73b7847e 766 my ($size, $format, $used, $parent) = file_size_info($fn);
35533c68 767 next if !($format && defined($size));
2502b33b
DM
768
769 my $volid;
770 if ($parent && $parent =~ m!^../(\d+)/([^/]+\.($fmts))$!) {
771 my ($basevmid, $basename) = ($1, $2);
772 $volid = "$storeid:$basevmid/$basename/$owner/$name";
773 } else {
774 $volid = "$storeid:$owner/$name";
775 }
1dc01b9f 776
2502b33b
DM
777 if ($vollist) {
778 my $found = grep { $_ eq $volid } @$vollist;
779 next if !$found;
1dc01b9f
DM
780 }
781
2502b33b
DM
782 push @$res, {
783 volid => $volid, format => $format,
1a3459ac 784 size => $size, vmid => $owner, used => $used, parent => $parent
2502b33b 785 };
1dc01b9f
DM
786 }
787
788 return $res;
789}
790
791sub status {
792 my ($class, $storeid, $scfg, $cache) = @_;
793
794 my $path = $scfg->{path};
795
796 die "storage definintion has no path\n" if !$path;
045ae0a7 797
1dc01b9f
DM
798 my $timeout = 2;
799 my $res = PVE::Tools::df($path, $timeout);
800
801 return undef if !$res || !$res->{total};
802
803 return ($res->{total}, $res->{avail}, $res->{used}, 1);
804}
805
806sub activate_storage {
807 my ($class, $storeid, $scfg, $cache) = @_;
808
809 my $path = $scfg->{path};
810
811 die "storage definintion has no path\n" if !$path;
812
813 die "unable to activate storage '$storeid' - " .
814 "directory '$path' does not exist\n" if ! -d $path;
815
816 if (defined($scfg->{content})) {
817 foreach my $vtype (keys %$vtype_subdirs) {
6bcc16d7
DM
818 # OpenVZMigrate uses backup (dump) dir
819 if (defined($scfg->{content}->{$vtype}) ||
820 ($vtype eq 'backup' && defined($scfg->{content}->{'rootdir'}))) {
821 my $subdir = $class->get_subdir($scfg, $vtype);
822 mkpath $subdir if $subdir ne $path;
823 }
1dc01b9f
DM
824 }
825 }
826}
827
828sub deactivate_storage {
829 my ($class, $storeid, $scfg, $cache) = @_;
830
831 # do nothing by default
832}
833
834sub activate_volume {
02e797b8 835 my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
1dc01b9f 836
02e797b8 837 my $path = $class->filesystem_path($scfg, $volname, $snapname);
1dc01b9f
DM
838
839 # check is volume exists
840 if ($scfg->{path}) {
841 die "volume '$storeid:$volname' does not exist\n" if ! -e $path;
842 } else {
843 die "volume '$storeid:$volname' does not exist\n" if ! -b $path;
844 }
845}
846
847sub deactivate_volume {
02e797b8 848 my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
1dc01b9f
DM
849
850 # do nothing by default
851}
852
c9eeac01
AD
853sub check_connection {
854 my ($class, $storeid, $scfg) = @_;
855 # do nothing by default
856 return 1;
857}
858
e47e548e 859
1dc01b9f 8601;