]> git.proxmox.com Git - pve-storage.git/blame - PVE/CLI/pvesm.pm
import/export: new formats: raw, tar, qcow2, vmdk
[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;
18use PVE::API2::Storage::Scan;
19use PVE::JSONSchema qw(get_standard_option);
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
f984732e
DM
29sub setup_environment {
30 PVE::RPCEnvironment->setup_default_cli_env();
31}
32
c669f42d
DM
33__PACKAGE__->register_method ({
34 name => 'path',
35 path => 'path',
36 method => 'GET',
37 description => "Get filesystem path for specified volume",
38 parameters => {
39 additionalProperties => 0,
40 properties => {
41 volume => {
42 description => "Volume identifier",
43 type => 'string', format => 'pve-volume-id',
f3bd890d 44 completion => \&PVE::Storage::complete_volume,
c669f42d
DM
45 },
46 },
47 },
48 returns => { type => 'null' },
49
50 code => sub {
51 my ($param) = @_;
52
53 my $cfg = PVE::Storage::config();
54
55 my $path = PVE::Storage::path ($cfg, $param->{volume});
56
57 print "$path\n";
58
59 return undef;
60
61 }});
62
fa017b96
FG
63__PACKAGE__->register_method ({
64 name => 'extractconfig',
65 path => 'extractconfig',
66 method => 'GET',
67 description => "Extract configuration from vzdump backup archive.",
68 permissions => {
69 description => "The user needs 'VM.Backup' permissions on the backed up guest ID, and 'Datastore.AllocateSpace' on the backup storage.",
70 user => 'all',
71 },
72 protected => 1,
73 parameters => {
74 additionalProperties => 0,
75 properties => {
76 volume => {
77 description => "Volume identifier",
78 type => 'string',
79 completion => \&PVE::Storage::complete_volume,
80 },
81 },
82 },
83 returns => { type => 'null' },
84 code => sub {
85 my ($param) = @_;
86 my $volume = $param->{volume};
87
88 my $rpcenv = PVE::RPCEnvironment::get();
89 my $authuser = $rpcenv->get_user();
90
91 my $storage_cfg = PVE::Storage::config();
04a13668 92 PVE::Storage::check_volume_access($rpcenv, $authuser, $storage_cfg, undef, $volume);
fa017b96
FG
93
94 my $config_raw = PVE::Storage::extract_vzdump_config($storage_cfg, $volume);
95
96 print "$config_raw\n";
97 return;
98 }});
99
c669f42d
DM
100my $print_content = sub {
101 my ($list) = @_;
102
103 my $maxlenname = 0;
104 foreach my $info (@$list) {
105
106 my $volid = $info->{volid};
107 my $sidlen = length ($volid);
108 $maxlenname = $sidlen if $sidlen > $maxlenname;
109 }
110
111 foreach my $info (@$list) {
112 next if !$info->{vmid};
113 my $volid = $info->{volid};
114
115 printf "%-${maxlenname}s %5s %10d %d\n", $volid,
116 $info->{format}, $info->{size}, $info->{vmid};
117 }
118
119 foreach my $info (sort { $a->{format} cmp $b->{format} } @$list) {
120 next if $info->{vmid};
121 my $volid = $info->{volid};
122
123 printf "%-${maxlenname}s %5s %10d\n", $volid,
124 $info->{format}, $info->{size};
125 }
126};
127
128my $print_status = sub {
129 my $res = shift;
130
131 my $maxlen = 0;
132 foreach my $res (@$res) {
133 my $storeid = $res->{storage};
134 $maxlen = length ($storeid) if length ($storeid) > $maxlen;
135 }
136 $maxlen+=1;
137
138 foreach my $res (sort { $a->{storage} cmp $b->{storage} } @$res) {
139 my $storeid = $res->{storage};
140
141 my $sum = $res->{used} + $res->{avail};
142 my $per = $sum ? (0.5 + ($res->{used}*100)/$sum) : 100;
143
144 printf "%-${maxlen}s %5s %1d %15d %15d %15d %.2f%%\n", $storeid,
145 $res->{type}, $res->{active},
146 $res->{total}/1024, $res->{used}/1024, $res->{avail}/1024, $per;
147 }
148};
149
47f37b53
WB
150__PACKAGE__->register_method ({
151 name => 'export',
152 path => 'export',
153 method => 'GET',
154 description => "Export a volume.",
155 protected => 1,
156 parameters => {
157 additionalProperties => 0,
158 properties => {
159 volume => {
160 description => "Volume identifier",
161 type => 'string',
162 completion => \&PVE::Storage::complete_volume,
163 },
164 format => {
165 description => "Export stream format",
166 type => 'string',
167 enum => $KNOWN_EXPORT_FORMATS,
168 },
169 filename => {
170 description => "Destination file name",
171 type => 'string',
172 },
173 base => {
174 description => "Snapshot to start an incremental stream from",
175 type => 'string',
176 pattern => qr/[a-z0-9_\-]{1,40}/,
177 maxLength => 40,
178 optional => 1,
179 },
180 snapshot => {
181 description => "Snapshot to export",
182 type => 'string',
183 pattern => qr/[a-z0-9_\-]{1,40}/,
184 maxLength => 40,
185 optional => 1,
186 },
187 'with-snapshots' => {
188 description =>
189 "Whether to include intermediate snapshots in the stream",
190 type => 'boolean',
191 optional => 1,
192 default => 0,
193 },
194 },
195 },
196 returns => { type => 'null' },
197 code => sub {
198 my ($param) = @_;
199
200 my $filename = $param->{filename};
201
202 my $outfh;
203 if ($filename eq '-') {
204 $outfh = \*STDOUT;
205 } else {
9559a62a 206 sysopen($outfh, $filename, O_CREAT|O_WRONLY|O_TRUNC)
47f37b53
WB
207 or die "open($filename): $!\n";
208 }
209
210 eval {
211 my $cfg = PVE::Storage::config();
212 PVE::Storage::volume_export($cfg, $outfh, $param->{volume}, $param->{format},
213 $param->{snapshot}, $param->{base}, $param->{'with-snapshots'});
214 };
215 my $err = $@;
216 if ($filename ne '-') {
217 close($outfh);
218 unlink($filename) if $err;
219 }
220 die $err if $err;
221 return;
222 }
223});
224
225__PACKAGE__->register_method ({
226 name => 'import',
227 path => 'import',
228 method => 'PUT',
229 description => "Import a volume.",
230 protected => 1,
231 parameters => {
232 additionalProperties => 0,
233 properties => {
234 volume => {
235 description => "Volume identifier",
236 type => 'string',
237 completion => \&PVE::Storage::complete_volume,
238 },
239 format => {
240 description => "Import stream format",
241 type => 'string',
242 enum => $KNOWN_EXPORT_FORMATS,
243 },
244 filename => {
245 description => "Source file name",
246 type => 'string',
247 },
248 base => {
249 description => "Base snapshot of an incremental stream",
250 type => 'string',
251 pattern => qr/[a-z0-9_\-]{1,40}/,
252 maxLength => 40,
253 optional => 1,
254 },
255 'with-snapshots' => {
256 description =>
257 "Whether the stream includes intermediate snapshots",
258 type => 'boolean',
259 optional => 1,
260 default => 0,
261 },
52595938
WB
262 'delete-snapshot' => {
263 description => "A snapshot to delete on success",
264 type => 'string',
265 pattern => qr/[a-z0-9_\-]{1,80}/,
266 maxLength => 80,
267 optional => 1,
268 },
47f37b53
WB
269 },
270 },
271 returns => { type => 'null' },
272 code => sub {
273 my ($param) = @_;
274
275 my $filename = $param->{filename};
276
277 my $infh;
278 if ($filename eq '-') {
279 $infh = \*STDIN;
280 } else {
9559a62a 281 sysopen($infh, $filename, O_RDONLY)
47f37b53
WB
282 or die "open($filename): $!\n";
283 }
284
285 my $cfg = PVE::Storage::config();
52595938
WB
286 my $volume = $param->{volume};
287 my $delete = $param->{'delete-snapshot'};
288 PVE::Storage::volume_import($cfg, $infh, $volume, $param->{format},
47f37b53 289 $param->{base}, $param->{'with-snapshots'});
52595938
WB
290 PVE::Storage::volume_snapshot_delete($cfg, $volume, $delete)
291 if defined($delete);
47f37b53
WB
292 return;
293 }
294});
295
c669f42d
DM
296our $cmddef = {
297 add => [ "PVE::API2::Storage::Config", 'create', ['type', 'storage'] ],
298 set => [ "PVE::API2::Storage::Config", 'update', ['storage'] ],
299 remove => [ "PVE::API2::Storage::Config", 'delete', ['storage'] ],
300 status => [ "PVE::API2::Storage::Status", 'index', [],
301 { node => $nodename }, $print_status ],
302 list => [ "PVE::API2::Storage::Content", 'index', ['storage'],
303 { node => $nodename }, $print_content ],
304 alloc => [ "PVE::API2::Storage::Content", 'create', ['storage', 'vmid', 'filename', 'size'],
305 { node => $nodename }, sub {
306 my $volid = shift;
e967e0ef 307 print "successfully created '$volid'\n";
c669f42d
DM
308 }],
309 free => [ "PVE::API2::Storage::Content", 'delete', ['volume'],
310 { node => $nodename } ],
311 nfsscan => [ "PVE::API2::Storage::Scan", 'nfsscan', ['server'],
312 { node => $nodename }, sub {
313 my $res = shift;
314
315 my $maxlen = 0;
316 foreach my $rec (@$res) {
317 my $len = length ($rec->{path});
318 $maxlen = $len if $len > $maxlen;
319 }
320 foreach my $rec (@$res) {
321 printf "%-${maxlen}s %s\n", $rec->{path}, $rec->{options};
322 }
323 }],
324 glusterfsscan => [ "PVE::API2::Storage::Scan", 'glusterfsscan', ['server'],
325 { node => $nodename }, sub {
326 my $res = shift;
327
328 foreach my $rec (@$res) {
329 printf "%s\n", $rec->{volname};
330 }
331 }],
332 iscsiscan => [ "PVE::API2::Storage::Scan", 'iscsiscan', ['server'],
333 { node => $nodename }, sub {
334 my $res = shift;
335
336 my $maxlen = 0;
337 foreach my $rec (@$res) {
338 my $len = length ($rec->{target});
339 $maxlen = $len if $len > $maxlen;
340 }
341 foreach my $rec (@$res) {
342 printf "%-${maxlen}s %s\n", $rec->{target}, $rec->{portal};
343 }
344 }],
345 lvmscan => [ "PVE::API2::Storage::Scan", 'lvmscan', [],
346 { node => $nodename }, sub {
347 my $res = shift;
348 foreach my $rec (@$res) {
349 printf "$rec->{vg}\n";
350 }
351 }],
668f6d9f
DM
352 lvmthinscan => [ "PVE::API2::Storage::Scan", 'lvmthinscan', ['vg'],
353 { node => $nodename }, sub {
354 my $res = shift;
355 foreach my $rec (@$res) {
356 printf "$rec->{lv}\n";
357 }
358 }],
c669f42d
DM
359 zfsscan => [ "PVE::API2::Storage::Scan", 'zfsscan', [],
360 { node => $nodename }, sub {
361 my $res = shift;
362
363 foreach my $rec (@$res) {
364 printf "$rec->{pool}\n";
365 }
366 }],
367 path => [ __PACKAGE__, 'path', ['volume']],
fa017b96 368 extractconfig => [__PACKAGE__, 'extractconfig', ['volume']],
47f37b53
WB
369 export => [ __PACKAGE__, 'export', ['volume', 'format', 'filename']],
370 import => [ __PACKAGE__, 'import', ['volume', 'format', 'filename']],
c669f42d
DM
371};
372
3731;