]>
git.proxmox.com Git - pve-storage.git/blob - PVE/API2/Storage/Content.pm
45b8de836a211f6ea5ddf6787ae7bac855044bff
1 package PVE
:: API2
:: Storage
:: Content
;
11 use PVE
:: Exception
qw(raise_param_exc) ;
12 use PVE
:: RPCEnvironment
;
14 use PVE
:: JSONSchema
qw(get_standard_option) ;
17 use base
qw(PVE::RESTHandler) ;
19 __PACKAGE__-
> register_method ({
23 description
=> "List storage content." ,
25 check
=> [ 'perm' , '/storage/{storage}' , [ 'Datastore.Audit' , 'Datastore.AllocateSpace' ], any
=> 1 ],
30 additionalProperties
=> 0 ,
32 node
=> get_standard_option
( 'pve-node' ),
33 storage
=> get_standard_option
( 'pve-storage-id' , {
34 completion
=> \
& PVE
:: Storage
:: complete_storage_enabled
,
37 description
=> "Only list content of this type." ,
38 type
=> 'string' , format
=> 'pve-storage-content' ,
40 completion
=> \
& PVE
:: Storage
:: complete_content_type
,
42 vmid
=> get_standard_option
( 'pve-vmid' , {
43 description
=> "Only list images for this VM" ,
45 completion
=> \
& PVE
:: Cluster
:: complete_vmid
,
55 description
=> "Volume identifier." ,
59 description
=> "Associated Owner VMID." ,
64 description
=> "Volume identifier of parent (for linked cloned)." ,
69 description
=> "Format identifier ('raw', 'qcow2', 'subvol', 'iso', 'tgz' ...)" ,
73 description
=> "Volume size in bytes." ,
78 description
=> "Used space. Please note that most storage plugins " .
79 "do not report anything useful here." ,
85 description
=> "Creation time (seconds since the UNIX Epoch)." ,
91 description
=> "Optional notes. If they contain multiple lines, only the first one is returned here." ,
96 description
=> "If whole backup is encrypted, value is the fingerprint or '1' "
97 . " if encrypted. Only useful for the Proxmox Backup Server storage type." ,
102 description
=> "Last backup verification result, only useful for PBS storages." ,
106 description
=> "Last backup verification state." ,
110 description
=> "Last backup verification UPID." ,
117 description
=> "Protection status. Currently only supported for backups." ,
123 links
=> [ { rel
=> 'child' , href
=> "{volid}" } ],
128 my $rpcenv = PVE
:: RPCEnvironment
:: get
();
130 my $authuser = $rpcenv -> get_user ();
132 my $storeid = $param ->{ storage
};
134 my $cfg = PVE
:: Storage
:: config
();
136 my $vollist = PVE
:: Storage
:: volume_list
( $cfg, $storeid, $param ->{ vmid
}, $param ->{ content
});
139 foreach my $item ( @$vollist ) {
140 eval { PVE
:: Storage
:: check_volume_access
( $rpcenv, $authuser, $cfg, undef , $item ->{ volid
}); };
142 $item ->{ vmid
} = int ( $item ->{ vmid
}) if ( defined ( $item ->{ vmid
}));
149 __PACKAGE__-
> register_method ({
153 description
=> "Allocate disk images." ,
155 check
=> [ 'perm' , '/storage/{storage}' , [ 'Datastore.AllocateSpace' ]],
160 additionalProperties
=> 0 ,
162 node
=> get_standard_option
( 'pve-node' ),
163 storage
=> get_standard_option
( 'pve-storage-id' , {
164 completion
=> \
& PVE
:: Storage
:: complete_storage_enabled
,
167 description
=> "The name of the file to create." ,
170 vmid
=> get_standard_option
( 'pve-vmid' , {
171 description
=> "Specify owner VM" ,
172 completion
=> \
& PVE
:: Cluster
:: complete_vmid
,
175 description
=> "Size in kilobyte (1024 bytes). Optional suffixes 'M' (megabyte, 1024K) and 'G' (gigabyte, 1024M)" ,
177 pattern
=> '\d+[MG]?' ,
181 enum
=> [ 'raw' , 'qcow2' , 'subvol' ],
188 description
=> "Volume identifier" ,
194 my $storeid = $param ->{ storage
};
195 my $name = $param ->{ filename
};
196 my $sizestr = $param ->{ size
};
199 if ( $sizestr =~ m/^\d+$/ ) {
201 } elsif ( $sizestr =~ m/^(\d+)M$/ ) {
203 } elsif ( $sizestr =~ m/^(\d+)G$/ ) {
204 $size = $1 * 1024 * 1024 ;
206 raise_param_exc
({ size
=> "unable to parse size ' $sizestr '" });
209 # extract FORMAT from name
210 if ( $name =~ m/\.(raw|qcow2|vmdk)$/ ) {
213 raise_param_exc
({ format
=> "different storage formats ( $param ->{format} != $fmt )" })
214 if $param ->{ format
} && $param ->{ format
} ne $fmt ;
216 $param ->{ format
} = $fmt ;
219 my $cfg = PVE
:: Storage
:: config
();
221 my $volid = PVE
:: Storage
:: vdisk_alloc
( $cfg, $storeid, $param ->{ vmid
},
228 # we allow to pass volume names (without storage prefix) if the storage
229 # is specified as separate parameter.
230 my $real_volume_id = sub {
231 my ( $storeid, $volume ) = @_ ;
235 if ( $volume =~ m/:/ ) {
237 my ( $sid, $volname ) = PVE
:: Storage
:: parse_volume_id
( $volume );
238 die "storage ID mismatch ( $sid != $storeid ) \n "
239 if $storeid && $sid ne $storeid ;
243 raise_param_exc
({ volume
=> $@ }) if $@ ;
246 raise_param_exc
({ volume
=> "no storage specified - incomplete volume ID" })
249 $volid = " $storeid : $volume " ;
252 return wantarray ?
( $volid, $storeid ) : $volid ;
255 __PACKAGE__-
> register_method ({
259 description
=> "Get volume attributes" ,
261 description
=> "You need read access for the volume." ,
267 additionalProperties
=> 0 ,
269 node
=> get_standard_option
( 'pve-node' ),
270 storage
=> get_standard_option
( 'pve-storage-id' , { optional
=> 1 }),
272 description
=> "Volume identifier" ,
281 description
=> "The Path" ,
285 description
=> "Volume size in bytes." ,
290 description
=> "Used space. Please note that most storage plugins " .
291 "do not report anything useful here." ,
296 description
=> "Format identifier ('raw', 'qcow2', 'subvol', 'iso', 'tgz' ...)" ,
300 description
=> "Optional notes." ,
305 description
=> "Protection status. Currently only supported for backups." ,
314 my $rpcenv = PVE
:: RPCEnvironment
:: get
();
315 my $authuser = $rpcenv -> get_user ();
317 my ( $volid, $storeid ) = & $real_volume_id ( $param ->{ storage
}, $param ->{ volume
});
319 my $cfg = PVE
:: Storage
:: config
();
321 PVE
:: Storage
:: check_volume_access
( $rpcenv, $authuser, $cfg, undef , $volid );
323 my $path = PVE
:: Storage
:: path
( $cfg, $volid );
324 my ( $size, $format, $used, $parent ) = PVE
:: Storage
:: volume_size_info
( $cfg, $volid );
325 die "volume_size_info on ' $volid ' failed \n " if !( $format && $size );
334 for my $attribute ( qw(notes protected) ) {
335 # keep going if fetching an optional attribute fails
337 my $value = PVE
:: Storage
:: get_volume_attribute
( $cfg, $volid, $attribute );
338 $entry ->{ $attribute } = $value if defined ( $value );
346 __PACKAGE__-
> register_method ({
347 name
=> 'updateattributes' ,
350 description
=> "Update volume attributes" ,
352 description
=> "You need read access for the volume." ,
358 additionalProperties
=> 0 ,
360 node
=> get_standard_option
( 'pve-node' ),
361 storage
=> get_standard_option
( 'pve-storage-id' , { optional
=> 1 }),
363 description
=> "Volume identifier" ,
367 description
=> "The new notes." ,
372 description
=> "Protection status. Currently only supported for backups." ,
378 returns
=> { type
=> 'null' },
382 my $rpcenv = PVE
:: RPCEnvironment
:: get
();
383 my $authuser = $rpcenv -> get_user ();
385 my ( $volid, $storeid ) = & $real_volume_id ( $param ->{ storage
}, $param ->{ volume
});
387 my $cfg = PVE
:: Storage
:: config
();
389 PVE
:: Storage
:: check_volume_access
( $rpcenv, $authuser, $cfg, undef , $volid );
391 for my $attr ( qw(notes protected) ) {
392 if ( exists $param ->{ $attr }) {
393 PVE
:: Storage
:: update_volume_attribute
( $cfg, $volid, $attr, $param ->{ $attr });
400 __PACKAGE__-
> register_method ({
404 description
=> "Delete volume" ,
406 description
=> "You need 'Datastore.Allocate' privilege on the storage (or 'Datastore.AllocateSpace' for backup volumes if you have VM.Backup privilege on the VM)." ,
412 additionalProperties
=> 0 ,
414 node
=> get_standard_option
( 'pve-node' ),
415 storage
=> get_standard_option
( 'pve-storage-id' , {
417 completion
=> \
& PVE
:: Storage
:: complete_storage
,
420 description
=> "Volume identifier" ,
422 completion
=> \
& PVE
:: Storage
:: complete_volume
,
426 description
=> "Time to wait for the task to finish. We return 'null' if the task finish within that time." ,
433 returns
=> { type
=> 'string' , optional
=> 1 , },
437 my $rpcenv = PVE
:: RPCEnvironment
:: get
();
438 my $authuser = $rpcenv -> get_user ();
440 my $cfg = PVE
:: Storage
:: config
();
442 my ( $volid, $storeid ) = & $real_volume_id ( $param ->{ storage
}, $param ->{ volume
});
444 my ( $path, $ownervm, $vtype ) = PVE
:: Storage
:: path
( $cfg, $volid );
445 if ( $vtype eq 'backup' && $ownervm ) {
446 $rpcenv -> check ( $authuser, "/storage/ $storeid " , [ 'Datastore.AllocateSpace' ]);
447 $rpcenv -> check ( $authuser, "/vms/ $ownervm " , [ 'VM.Backup' ]);
449 $rpcenv -> check ( $authuser, "/storage/ $storeid " , [ 'Datastore.Allocate' ]);
453 PVE
:: Storage
:: vdisk_free
( $cfg, $volid );
454 print "Removed volume ' $volid ' \n " ;
455 if ( $vtype eq 'backup'
456 && $path =~ /(.*\/ vzdump-\w
+- \d
+- \d
{ 4 } _\d
{ 2 } _\d
{ 2 }- \d
{ 2 } _\d
{ 2 } _\d
{ 2 })[^ \
/]+$/ ) {
457 my $logpath = " $1 .log" ;
458 # try to cleanup our backup log file too, if still existing, #318
459 unlink ( $logpath ) if - e
$logpath ;
463 my $id = ( defined $ownervm ?
" $ownervm@ " : '' ) . $storeid ;
464 my $upid = $rpcenv -> fork_worker ( 'imgdel' , $id, $authuser, $worker );
465 my $background_delay = $param ->{ delay
};
466 if ( $background_delay ) {
467 my $end_time = time () + $background_delay ;
468 my $currently_deleting ; # not necessarily true, e.g. sequential api call from cli
470 my $task = PVE
:: Tools
:: upid_decode
( $upid );
471 $currently_deleting = PVE
:: ProcFSTools
:: check_process_running
( $task ->{ pid
}, $task ->{ pstart
});
472 sleep 1 if $currently_deleting ;
473 } while ( time () < $end_time && $currently_deleting );
475 if (! $currently_deleting ) {
476 my $status = PVE
:: Tools
:: upid_read_status
( $upid );
478 return undef if ! PVE
:: Tools
:: upid_status_is_error
( $status );
485 __PACKAGE__-
> register_method ({
489 description
=> "Copy a volume. This is experimental code - do not use." ,
493 additionalProperties
=> 0 ,
495 node
=> get_standard_option
( 'pve-node' ),
496 storage
=> get_standard_option
( 'pve-storage-id' , { optional
=> 1 }),
498 description
=> "Source volume identifier" ,
502 description
=> "Target volume identifier" ,
505 target_node
=> get_standard_option
( 'pve-node' , {
506 description
=> "Target node. Default is local node." ,
517 my $rpcenv = PVE
:: RPCEnvironment
:: get
();
519 my $user = $rpcenv -> get_user ();
521 my $target_node = $param ->{ target_node
} || PVE
:: INotify
:: nodename
();
523 # cd /nodes/localhost/storage/local/content
524 # pve:/> create local:103/vm-103-disk-1.raw -target local:103/vm-103-disk-2.raw
525 # pve:/> create 103/vm-103-disk-1.raw -target 103/vm-103-disk-3.raw
527 my $src_volid = & $real_volume_id ( $param ->{ storage
}, $param ->{ volume
});
528 my $dst_volid = & $real_volume_id ( $param ->{ storage
}, $param ->{ target
});
530 print "DEBUG: COPY $src_volid TO $dst_volid\n " ;
532 my $cfg = PVE
:: Storage
:: config
();
534 # do all parameter checks first
536 # then do all short running task (to raise errors before we go to background)
538 # then start the worker task
542 print "DEBUG: starting worker $upid\n " ;
544 my ( $target_sid, $target_volname ) = PVE
:: Storage
:: parse_volume_id
( $dst_volid );
545 #my $target_ip = PVE::Cluster::remote_node_ip($target_node);
547 # you need to get this working (fails currently, because storage_migrate() uses
548 # ssh to connect to local host (which is not needed
549 my $sshinfo = PVE
:: SSHInfo
:: get_ssh_info
( $target_node );
550 PVE
:: Storage
:: storage_migrate
( $cfg, $src_volid, $sshinfo, $target_sid, { 'target_volname' => $target_volname });
552 print "DEBUG: end worker $upid\n " ;
556 return $rpcenv -> fork_worker ( 'imgcopy' , undef , $user, $worker );