]> git.proxmox.com Git - pve-storage.git/blame - PVE/CLI/pvesm.pm
scan_cifs: do not add NT_STATUS lines to result
[pve-storage.git] / PVE / CLI / pvesm.pm
CommitLineData
c669f42d
DM
1package PVE::CLI::pvesm;
2
3use strict;
4use warnings;
5
9559a62a 6use POSIX qw(O_RDONLY O_WRONLY O_CREAT O_TRUNC);
c669f42d
DM
7use Fcntl ':flock';
8use File::Path;
9
10use PVE::SafeSyslog;
11use PVE::Cluster;
12use PVE::INotify;
13use PVE::RPCEnvironment;
14use PVE::Storage;
15use PVE::API2::Storage::Config;
16use PVE::API2::Storage::Content;
17use PVE::API2::Storage::Status;
c669f42d 18use PVE::JSONSchema qw(get_standard_option);
c26f3a71 19use PVE::PTY;
c669f42d
DM
20
21use PVE::CLIHandler;
22
23use base qw(PVE::CLIHandler);
24
9559a62a 25my $KNOWN_EXPORT_FORMATS = ['raw+size', 'tar+size', 'qcow2+size', 'vmdk+size', 'zfs'];
47f37b53 26
c669f42d
DM
27my $nodename = PVE::INotify::nodename();
28
42f2c57d
DC
29sub param_mapping {
30 my ($name) = @_;
31
32 my $password_map = PVE::CLIHandler::get_standard_mapping('pve-password', {
33 func => sub {
34 my ($value) = @_;
35 return $value if $value;
36 return PVE::PTY::read_password("Enter Password: ");
37 },
38 });
39 my $mapping = {
40 'cifsscan' => [ $password_map ],
41 'create' => [ $password_map ],
42 };
43 return $mapping->{$name};
c26f3a71
WL
44}
45
f984732e
DM
46sub setup_environment {
47 PVE::RPCEnvironment->setup_default_cli_env();
48}
49
5f184292
FE
50__PACKAGE__->register_method ({
51 name => 'apiinfo',
52 path => 'apiinfo',
53 method => 'GET',
54 description => "Returns APIVER and APIAGE.",
55 parameters => {
56 additionalProperties => 0,
57 properties => {},
58 },
59 returns => {
60 type => 'object',
61 properties => {
62 apiver => { type => 'integer' },
63 apiage => { type => 'integer' },
64 },
65 },
66 code => sub {
67 return {
68 apiver => PVE::Storage::APIVER,
69 apiage => PVE::Storage::APIAGE,
70 };
71 }
72});
73
c669f42d
DM
74__PACKAGE__->register_method ({
75 name => 'path',
76 path => 'path',
77 method => 'GET',
78 description => "Get filesystem path for specified volume",
79 parameters => {
80 additionalProperties => 0,
81 properties => {
82 volume => {
83 description => "Volume identifier",
84 type => 'string', format => 'pve-volume-id',
f3bd890d 85 completion => \&PVE::Storage::complete_volume,
c669f42d
DM
86 },
87 },
88 },
89 returns => { type => 'null' },
90
91 code => sub {
92 my ($param) = @_;
93
94 my $cfg = PVE::Storage::config();
95
96 my $path = PVE::Storage::path ($cfg, $param->{volume});
97
98 print "$path\n";
99
100 return undef;
101
102 }});
103
fa017b96
FG
104__PACKAGE__->register_method ({
105 name => 'extractconfig',
106 path => 'extractconfig',
107 method => 'GET',
108 description => "Extract configuration from vzdump backup archive.",
109 permissions => {
110 description => "The user needs 'VM.Backup' permissions on the backed up guest ID, and 'Datastore.AllocateSpace' on the backup storage.",
111 user => 'all',
112 },
113 protected => 1,
114 parameters => {
115 additionalProperties => 0,
116 properties => {
117 volume => {
118 description => "Volume identifier",
119 type => 'string',
120 completion => \&PVE::Storage::complete_volume,
121 },
122 },
123 },
124 returns => { type => 'null' },
125 code => sub {
126 my ($param) = @_;
127 my $volume = $param->{volume};
128
129 my $rpcenv = PVE::RPCEnvironment::get();
130 my $authuser = $rpcenv->get_user();
131
132 my $storage_cfg = PVE::Storage::config();
04a13668 133 PVE::Storage::check_volume_access($rpcenv, $authuser, $storage_cfg, undef, $volume);
fa017b96
FG
134
135 my $config_raw = PVE::Storage::extract_vzdump_config($storage_cfg, $volume);
136
137 print "$config_raw\n";
138 return;
139 }});
140
c669f42d
DM
141my $print_content = sub {
142 my ($list) = @_;
143
61c261e7 144 my ($maxlenname, $maxsize) = (0, 0);
c669f42d 145 foreach my $info (@$list) {
c669f42d
DM
146 my $volid = $info->{volid};
147 my $sidlen = length ($volid);
148 $maxlenname = $sidlen if $sidlen > $maxlenname;
61c261e7 149 $maxsize = $info->{size} if ($info->{size} // 0) > $maxsize;
c669f42d 150 }
61c261e7
TL
151 my $sizemaxdigits = length($maxsize);
152
153 my $basefmt = "%-${maxlenname}s %-7s %-9s %${sizemaxdigits}s";
154 printf "$basefmt %s\n", "Volid", "Format", "Type", "Size", "VMID";
c669f42d
DM
155
156 foreach my $info (@$list) {
157 next if !$info->{vmid};
158 my $volid = $info->{volid};
159
61c261e7 160 printf "$basefmt %d\n", $volid, $info->{format}, $info->{content}, $info->{size}, $info->{vmid};
c669f42d
DM
161 }
162
163 foreach my $info (sort { $a->{format} cmp $b->{format} } @$list) {
164 next if $info->{vmid};
165 my $volid = $info->{volid};
166
61c261e7 167 printf "$basefmt\n", $volid, $info->{format}, $info->{content}, $info->{size};
c669f42d
DM
168 }
169};
170
171my $print_status = sub {
172 my $res = shift;
173
174 my $maxlen = 0;
175 foreach my $res (@$res) {
176 my $storeid = $res->{storage};
177 $maxlen = length ($storeid) if length ($storeid) > $maxlen;
178 }
179 $maxlen+=1;
180
d40e27de
TL
181 printf "%-${maxlen}s %10s %10s %15s %15s %15s %8s\n", 'Name', 'Type',
182 'Status', 'Total', 'Used', 'Available', '%';
183
c669f42d
DM
184 foreach my $res (sort { $a->{storage} cmp $b->{storage} } @$res) {
185 my $storeid = $res->{storage};
186
d40e27de
TL
187 my $active = $res->{active} ? 'active' : 'inactive';
188 my ($per, $per_fmt) = (0, '% 7.2f%%');
189 $per = ($res->{used}*100)/$res->{total} if $res->{total} > 0;
190
191 if (!$res->{enabled}) {
04301013 192 $per = 'N/A';
d40e27de
TL
193 $per_fmt = '% 8s';
194 $active = 'disabled';
195 }
c669f42d 196
d40e27de
TL
197 printf "%-${maxlen}s %10s %10s %15d %15d %15d $per_fmt\n", $storeid,
198 $res->{type}, $active, $res->{total}/1024, $res->{used}/1024,
199 $res->{avail}/1024, $per;
c669f42d
DM
200 }
201};
202
47f37b53
WB
203__PACKAGE__->register_method ({
204 name => 'export',
205 path => 'export',
206 method => 'GET',
a43a796c 207 description => "Used internally to export a volume.",
47f37b53
WB
208 protected => 1,
209 parameters => {
210 additionalProperties => 0,
211 properties => {
212 volume => {
213 description => "Volume identifier",
214 type => 'string',
215 completion => \&PVE::Storage::complete_volume,
216 },
217 format => {
218 description => "Export stream format",
219 type => 'string',
220 enum => $KNOWN_EXPORT_FORMATS,
221 },
222 filename => {
223 description => "Destination file name",
224 type => 'string',
225 },
226 base => {
227 description => "Snapshot to start an incremental stream from",
228 type => 'string',
229 pattern => qr/[a-z0-9_\-]{1,40}/,
230 maxLength => 40,
231 optional => 1,
232 },
233 snapshot => {
234 description => "Snapshot to export",
235 type => 'string',
236 pattern => qr/[a-z0-9_\-]{1,40}/,
237 maxLength => 40,
238 optional => 1,
239 },
240 'with-snapshots' => {
241 description =>
242 "Whether to include intermediate snapshots in the stream",
243 type => 'boolean',
244 optional => 1,
245 default => 0,
246 },
247 },
248 },
249 returns => { type => 'null' },
250 code => sub {
251 my ($param) = @_;
252
253 my $filename = $param->{filename};
254
255 my $outfh;
256 if ($filename eq '-') {
257 $outfh = \*STDOUT;
258 } else {
9559a62a 259 sysopen($outfh, $filename, O_CREAT|O_WRONLY|O_TRUNC)
47f37b53
WB
260 or die "open($filename): $!\n";
261 }
262
263 eval {
264 my $cfg = PVE::Storage::config();
265 PVE::Storage::volume_export($cfg, $outfh, $param->{volume}, $param->{format},
266 $param->{snapshot}, $param->{base}, $param->{'with-snapshots'});
267 };
268 my $err = $@;
269 if ($filename ne '-') {
270 close($outfh);
271 unlink($filename) if $err;
272 }
273 die $err if $err;
274 return;
275 }
276});
277
278__PACKAGE__->register_method ({
279 name => 'import',
280 path => 'import',
281 method => 'PUT',
a43a796c 282 description => "Used internally to import a volume.",
47f37b53
WB
283 protected => 1,
284 parameters => {
285 additionalProperties => 0,
286 properties => {
287 volume => {
288 description => "Volume identifier",
289 type => 'string',
290 completion => \&PVE::Storage::complete_volume,
291 },
292 format => {
293 description => "Import stream format",
294 type => 'string',
295 enum => $KNOWN_EXPORT_FORMATS,
296 },
297 filename => {
228e5be9
TL
298 description => "Source file name. For '-' stdin is used, the " .
299 "tcp://<IP-or-CIDR> format allows to use a TCP connection as input. " .
300 "Else, the file is treated as common file.",
47f37b53
WB
301 type => 'string',
302 },
303 base => {
304 description => "Base snapshot of an incremental stream",
305 type => 'string',
306 pattern => qr/[a-z0-9_\-]{1,40}/,
307 maxLength => 40,
308 optional => 1,
309 },
310 'with-snapshots' => {
311 description =>
312 "Whether the stream includes intermediate snapshots",
313 type => 'boolean',
314 optional => 1,
315 default => 0,
316 },
52595938
WB
317 'delete-snapshot' => {
318 description => "A snapshot to delete on success",
319 type => 'string',
320 pattern => qr/[a-z0-9_\-]{1,80}/,
321 maxLength => 80,
322 optional => 1,
323 },
a97d3ee4
FE
324 'allow-rename' => {
325 description => "Choose a new volume ID if the requested " .
326 "volume ID already exists, instead of throwing an error.",
327 type => 'boolean',
328 optional => 1,
329 default => 0,
330 },
47f37b53
WB
331 },
332 },
a97d3ee4 333 returns => { type => 'string' },
47f37b53
WB
334 code => sub {
335 my ($param) = @_;
336
337 my $filename = $param->{filename};
338
339 my $infh;
340 if ($filename eq '-') {
341 $infh = \*STDIN;
228e5be9
TL
342 } elsif ($filename =~ m!^tcp://(([^/]+)(/\d+)?)$!) {
343 my ($cidr, $ip, $subnet) = ($1, $2, $3);
344 if ($subnet) { # got real CIDR notation, not just IP
a2aae38c 345 my $ips = PVE::Network::get_local_ip_from_cidr($cidr);
ed2df8e3
TL
346 die "Unable to get any local IP address in network '$cidr'\n"
347 if scalar(@$ips) < 1;
348 die "Got multiple local IP address in network '$cidr'\n"
349 if scalar(@$ips) > 1;
350
351 $ip = $ips->[0];
228e5be9
TL
352 }
353 my $family = PVE::Tools::get_host_address_family($ip);
354 my $port = PVE::Tools::next_migrate_port($family, $ip);
355
356 my $sock_params = {
357 Listen => 1,
358 ReuseAddr => 1,
359 Proto => &Socket::IPPROTO_TCP,
360 GetAddrInfoFlags => 0,
361 LocalAddr => $ip,
362 LocalPort => $port,
363 };
364 my $socket = IO::Socket::IP->new(%$sock_params)
365 or die "failed to open socket: $!\n";
366
367 print "$ip\n$port\n"; # tell remote where to connect
368 *STDOUT->flush();
369
370 my $prev_alarm = alarm 0;
371 local $SIG{ALRM} = sub { die "timed out waiting for client\n" };
372 alarm 30;
373 my $client = $socket->accept; # Wait for a client
374 alarm $prev_alarm;
375 close($socket);
376
377 $infh = \*$client;
47f37b53 378 } else {
9559a62a 379 sysopen($infh, $filename, O_RDONLY)
47f37b53
WB
380 or die "open($filename): $!\n";
381 }
382
383 my $cfg = PVE::Storage::config();
52595938
WB
384 my $volume = $param->{volume};
385 my $delete = $param->{'delete-snapshot'};
a97d3ee4
FE
386 my $imported_volid = PVE::Storage::volume_import($cfg, $infh, $volume, $param->{format},
387 $param->{base}, $param->{'with-snapshots'}, $param->{'allow-rename'});
388 PVE::Storage::volume_snapshot_delete($cfg, $imported_volid, $delete)
52595938 389 if defined($delete);
a97d3ee4 390 return $imported_volid;
47f37b53
WB
391 }
392});
393
7963ba74
DC
394__PACKAGE__->register_method ({
395 name => 'nfsscan',
396 path => 'nfs',
397 method => 'GET',
398 description => "Scan remote NFS server.",
399 protected => 1,
400 proxyto => "node",
401 permissions => {
402 check => ['perm', '/storage', ['Datastore.Allocate']],
403 },
404 parameters => {
405 additionalProperties => 0,
406 properties => {
407 node => get_standard_option('pve-node'),
408 server => {
409 description => "The server address (name or IP).",
410 type => 'string', format => 'pve-storage-server',
411 },
412 },
413 },
414 returns => {
415 type => 'array',
416 items => {
417 type => "object",
418 properties => {
419 path => {
420 description => "The exported path.",
421 type => 'string',
422 },
423 options => {
424 description => "NFS export options.",
425 type => 'string',
426 },
427 },
428 },
429 },
430 code => sub {
431 my ($param) = @_;
432
433 my $server = $param->{server};
434 my $res = PVE::Storage::scan_nfs($server);
435
436 my $data = [];
437 foreach my $k (keys %$res) {
438 push @$data, { path => $k, options => $res->{$k} };
439 }
440 return $data;
441 }});
442
443__PACKAGE__->register_method ({
444 name => 'cifsscan',
445 path => 'cifs',
446 method => 'GET',
447 description => "Scan remote CIFS server.",
448 protected => 1,
449 proxyto => "node",
450 permissions => {
451 check => ['perm', '/storage', ['Datastore.Allocate']],
452 },
453 parameters => {
454 additionalProperties => 0,
455 properties => {
456 node => get_standard_option('pve-node'),
457 server => {
458 description => "The server address (name or IP).",
459 type => 'string', format => 'pve-storage-server',
460 },
461 username => {
462 description => "User name.",
463 type => 'string',
464 optional => 1,
465 },
466 password => {
467 description => "User password.",
468 type => 'string',
469 optional => 1,
470 },
471 domain => {
472 description => "SMB domain (Workgroup).",
473 type => 'string',
474 optional => 1,
475 },
476 },
477 },
478 returns => {
479 type => 'array',
480 items => {
481 type => "object",
482 properties => {
483 share => {
484 description => "The cifs share name.",
485 type => 'string',
486 },
487 description => {
488 description => "Descriptive text from server.",
489 type => 'string',
490 },
491 },
492 },
493 },
494 code => sub {
495 my ($param) = @_;
496
497 my $server = $param->{server};
498
499 my $username = $param->{username};
500 my $password = $param->{password};
501 my $domain = $param->{domain};
502
503 my $res = PVE::Storage::scan_cifs($server, $username, $password, $domain);
504
505 my $data = [];
506 foreach my $k (keys %$res) {
7963ba74
DC
507 push @$data, { share => $k, description => $res->{$k} };
508 }
509
510 return $data;
511 }});
512
513# Note: GlusterFS currently does not have an equivalent of showmount.
514# As workaround, we simply use nfs showmount.
515# see http://www.gluster.org/category/volumes/
516
517__PACKAGE__->register_method ({
518 name => 'glusterfsscan',
519 path => 'glusterfs',
520 method => 'GET',
521 description => "Scan remote GlusterFS server.",
522 protected => 1,
523 proxyto => "node",
524 permissions => {
525 check => ['perm', '/storage', ['Datastore.Allocate']],
526 },
527 parameters => {
528 additionalProperties => 0,
529 properties => {
530 node => get_standard_option('pve-node'),
531 server => {
532 description => "The server address (name or IP).",
533 type => 'string', format => 'pve-storage-server',
534 },
535 },
536 },
537 returns => {
538 type => 'array',
539 items => {
540 type => "object",
2e2e11db 541 properties => {
7963ba74
DC
542 volname => {
543 description => "The volume name.",
544 type => 'string',
545 },
546 },
547 },
548 },
549 code => sub {
550 my ($param) = @_;
551
552 my $server = $param->{server};
553 my $res = PVE::Storage::scan_nfs($server);
554
555 my $data = [];
556 foreach my $path (keys %$res) {
557 if ($path =~ m!^/([^\s/]+)$!) {
558 push @$data, { volname => $1 };
559 }
560 }
561 return $data;
562 }});
563
564__PACKAGE__->register_method ({
565 name => 'iscsiscan',
566 path => 'iscsi',
567 method => 'GET',
568 description => "Scan remote iSCSI server.",
569 protected => 1,
570 proxyto => "node",
571 permissions => {
572 check => ['perm', '/storage', ['Datastore.Allocate']],
573 },
574 parameters => {
575 additionalProperties => 0,
576 properties => {
577 node => get_standard_option('pve-node'),
578 portal => {
579 description => "The iSCSI portal (IP or DNS name with optional port).",
580 type => 'string', format => 'pve-storage-portal-dns',
581 },
582 },
583 },
584 returns => {
585 type => 'array',
586 items => {
587 type => "object",
588 properties => {
589 target => {
590 description => "The iSCSI target name.",
591 type => 'string',
592 },
593 portal => {
594 description => "The iSCSI portal name.",
595 type => 'string',
596 },
597 },
598 },
599 },
600 code => sub {
601 my ($param) = @_;
602
603 my $res = PVE::Storage::scan_iscsi($param->{portal});
604
605 my $data = [];
606 foreach my $k (keys %$res) {
607 push @$data, { target => $k, portal => join(',', @{$res->{$k}}) };
608 }
609
610 return $data;
611 }});
612
613__PACKAGE__->register_method ({
614 name => 'lvmscan',
615 path => 'lvm',
616 method => 'GET',
617 description => "List local LVM volume groups.",
618 protected => 1,
619 proxyto => "node",
620 permissions => {
621 check => ['perm', '/storage', ['Datastore.Allocate']],
622 },
623 parameters => {
624 additionalProperties => 0,
625 properties => {
626 node => get_standard_option('pve-node'),
627 },
628 },
629 returns => {
630 type => 'array',
631 items => {
632 type => "object",
633 properties => {
634 vg => {
635 description => "The LVM logical volume group name.",
636 type => 'string',
637 },
638 },
639 },
640 },
641 code => sub {
642 my ($param) = @_;
643
644 my $res = PVE::Storage::LVMPlugin::lvm_vgs();
645 return PVE::RESTHandler::hash_to_array($res, 'vg');
646 }});
647
648__PACKAGE__->register_method ({
649 name => 'lvmthinscan',
650 path => 'lvmthin',
651 method => 'GET',
652 description => "List local LVM Thin Pools.",
653 protected => 1,
654 proxyto => "node",
655 permissions => {
656 check => ['perm', '/storage', ['Datastore.Allocate']],
657 },
658 parameters => {
659 additionalProperties => 0,
660 properties => {
661 node => get_standard_option('pve-node'),
662 vg => {
663 type => 'string',
664 pattern => '[a-zA-Z0-9\.\+\_][a-zA-Z0-9\.\+\_\-]+', # see lvm(8) manpage
665 maxLength => 100,
666 },
667 },
668 },
669 returns => {
670 type => 'array',
671 items => {
672 type => "object",
673 properties => {
674 lv => {
675 description => "The LVM Thin Pool name (LVM logical volume).",
676 type => 'string',
677 },
678 },
679 },
680 },
681 code => sub {
682 my ($param) = @_;
683
684 return PVE::Storage::LvmThinPlugin::list_thinpools($param->{vg});
685 }});
686
687__PACKAGE__->register_method ({
688 name => 'zfsscan',
689 path => 'zfs',
690 method => 'GET',
691 description => "Scan zfs pool list on local node.",
692 protected => 1,
693 proxyto => "node",
694 permissions => {
695 check => ['perm', '/storage', ['Datastore.Allocate']],
696 },
697 parameters => {
698 additionalProperties => 0,
699 properties => {
700 node => get_standard_option('pve-node'),
701 },
702 },
703 returns => {
704 type => 'array',
705 items => {
706 type => "object",
707 properties => {
708 pool => {
709 description => "ZFS pool name.",
710 type => 'string',
711 },
712 },
713 },
714 },
715 code => sub {
716 my ($param) = @_;
717
718 return PVE::Storage::scan_zfs();
719 }});
720
c669f42d
DM
721our $cmddef = {
722 add => [ "PVE::API2::Storage::Config", 'create', ['type', 'storage'] ],
723 set => [ "PVE::API2::Storage::Config", 'update', ['storage'] ],
724 remove => [ "PVE::API2::Storage::Config", 'delete', ['storage'] ],
725 status => [ "PVE::API2::Storage::Status", 'index', [],
726 { node => $nodename }, $print_status ],
727 list => [ "PVE::API2::Storage::Content", 'index', ['storage'],
728 { node => $nodename }, $print_content ],
729 alloc => [ "PVE::API2::Storage::Content", 'create', ['storage', 'vmid', 'filename', 'size'],
730 { node => $nodename }, sub {
731 my $volid = shift;
e967e0ef 732 print "successfully created '$volid'\n";
c669f42d
DM
733 }],
734 free => [ "PVE::API2::Storage::Content", 'delete', ['volume'],
735 { node => $nodename } ],
957321a8
TL
736 scan => {
737 nfs => [ __PACKAGE__, 'nfsscan', ['server'], { node => $nodename }, sub {
738 my $res = shift;
739
740 my $maxlen = 0;
741 foreach my $rec (@$res) {
742 my $len = length ($rec->{path});
743 $maxlen = $len if $len > $maxlen;
744 }
745 foreach my $rec (@$res) {
746 printf "%-${maxlen}s %s\n", $rec->{path}, $rec->{options};
747 }
748 }],
749 cifs => [ __PACKAGE__, 'cifsscan', ['server'], { node => $nodename }, sub {
750 my $res = shift;
751
752 my $maxlen = 0;
753 foreach my $rec (@$res) {
754 my $len = length ($rec->{share});
755 $maxlen = $len if $len > $maxlen;
756 }
757 foreach my $rec (@$res) {
758 printf "%-${maxlen}s %s\n", $rec->{share}, $rec->{description};
759 }
760 }],
761 glusterfs => [ __PACKAGE__, 'glusterfsscan', ['server'], { node => $nodename }, sub {
762 my $res = shift;
763
764 foreach my $rec (@$res) {
765 printf "%s\n", $rec->{volname};
766 }
767 }],
768 iscsi => [ __PACKAGE__, 'iscsiscan', ['portal'], { node => $nodename }, sub {
769 my $res = shift;
770
771 my $maxlen = 0;
772 foreach my $rec (@$res) {
773 my $len = length ($rec->{target});
774 $maxlen = $len if $len > $maxlen;
775 }
776 foreach my $rec (@$res) {
777 printf "%-${maxlen}s %s\n", $rec->{target}, $rec->{portal};
778 }
779 }],
780 lvm => [ __PACKAGE__, 'lvmscan', [], { node => $nodename }, sub {
781 my $res = shift;
782 foreach my $rec (@$res) {
783 printf "$rec->{vg}\n";
784 }
785 }],
786 lvmthin => [ __PACKAGE__, 'lvmthinscan', ['vg'], { node => $nodename }, sub {
787 my $res = shift;
788 foreach my $rec (@$res) {
789 printf "$rec->{lv}\n";
790 }
791 }],
792 zfs => [ __PACKAGE__, 'zfsscan', [], { node => $nodename }, sub {
793 my $res = shift;
794
795 foreach my $rec (@$res) {
796 printf "$rec->{pool}\n";
797 }
798 }],
799 },
800 nfsscan => { alias => 'scan nfs' },
801 cifsscan => { alias => 'scan cifs' },
802 glusterfsscan => { alias => 'scan glusterfs' },
803 iscsiscan => { alias => 'scan iscsi' },
804 lvmscan => { alias => 'scan lvm' },
805 lvmthinscan => { alias => 'scan lvmthin' },
806 zfsscan => { alias => 'scan zfs' },
c669f42d 807 path => [ __PACKAGE__, 'path', ['volume']],
fa017b96 808 extractconfig => [__PACKAGE__, 'extractconfig', ['volume']],
47f37b53 809 export => [ __PACKAGE__, 'export', ['volume', 'format', 'filename']],
a97d3ee4
FE
810 import => [ __PACKAGE__, 'import', ['volume', 'format', 'filename'], {}, sub {
811 my $volid = shift;
812 print PVE::Storage::volume_imported_message($volid);
813 }],
5f184292
FE
814 apiinfo => [ __PACKAGE__, 'apiinfo', [], {}, sub {
815 my $res = shift;
816
817 print "APIVER $res->{apiver}\n";
818 print "APIAGE $res->{apiage}\n";
819 }],
c669f42d
DM
820};
821
8221;