Am 01.02.2016 um 09:45 schrieb Wolfgang Bumiller: > I wonder if it makes more sense to just handle the > ($content eq 'images') case in the existing upload call since this > duplicates quite a lot of code and you might just have to adapt the > $content if/else cases and add the '# convert image with qemu-img' > portion conditionally to the worker for the image case? The 'vmid' > option would have to be made optional and checked for the non-images > case.
This is a good idea, but I think it's better to have different REST URLs with different meanings for this. I will check to put the __PACKAGE__->register_method call in a parametrized sub like sub register_upload_method { my ( $class, $name, $path, $validate_fn, $worker_fn ) = @_; __PACKAGE__->register_method( { name => $name, path => $path, ... } ); } __PACKAGE__->register_upload_method( 'upload_image,'{storage}/upload_image', ... ); when the code will be more final. > > > On Fri, Jan 29, 2016 at 06:42:18PM +0100, Timo Grodzinski wrote: >> Signed-off-by: Timo Grodzinski <t.grodzin...@profihost.ag> >> --- >> PVE/API2/Storage/Status.pm | 211 >> +++++++++++++++++++++++++++++++++++++++++++++ >> PVE/Storage.pm | 2 +- >> PVE/Storage/DirPlugin.pm | 2 +- >> PVE/Storage/Plugin.pm | 2 +- >> 4 files changed, 214 insertions(+), 3 deletions(-) >> >> diff --git a/PVE/API2/Storage/Status.pm b/PVE/API2/Storage/Status.pm >> index 49bb58c..f887932 100644 >> --- a/PVE/API2/Storage/Status.pm >> +++ b/PVE/API2/Storage/Status.pm >> @@ -421,4 +421,215 @@ __PACKAGE__->register_method ({ >> return $upid; >> }}); >> >> + >> +__PACKAGE__->register_method( >> + { >> + name => 'upload_image', >> + path => '{storage}/upload_image', >> + method => 'POST', >> + description => "Upload templates and ISO images.", >> + permissions => { >> + check => [ 'perm', '/storage/{storage}', >> ['Datastore.AllocateTemplate'] ] >> + , # XXX [ 'Datastore.AllocateSpace' ] >> + }, >> + protected => 1, >> + parameters => { >> + additionalProperties => 0, >> + properties => { >> + node => get_standard_option( 'pve-node' ), >> + vmid => get_standard_option( 'pve-vmid', { completion => >> \&PVE::QemuServer::complete_vmid } ), >> + storage => get_standard_option( 'pve-storage-id' ), # >> temporary storage before qemu-img >> + format => { >> + description => "Target format for file storage.", >> + type => 'string', >> + optional => 1, >> + enum => [ 'raw', 'qcow2', 'vmdk' ], >> + }, >> + content => { >> + description => "Content type.", >> + type => 'string', >> + format => 'pve-storage-content', >> + }, >> + filename => { >> + description => "The name of the file to create.", >> + type => 'string', >> + }, >> + tmpfilename => { >> + description => >> +"The source file name. This parameter is usually set by the REST handler. >> You can only overwrite it when connecting to the trustet port on localhost.", >> + type => 'string', >> + optional => 1, >> + }, >> + }, >> + }, >> + returns => { type => "string" }, >> + code => sub { >> + my ( $param ) = @_; >> + >> + my $rpcenv = PVE::RPCEnvironment::get(); >> + >> + my $user = $rpcenv->get_user(); >> + >> + my $cfg = cfs_read_file( "storage.cfg" ); >> + >> + # XXX use PVE::Tools::extract_param ? >> + my $node = $param->{node}; >> + my $vmid = $param->{vmid}; >> + my $format = $param->{format}; >> + >> + my $scfg = PVE::Storage::storage_check_enabled( $cfg, >> $param->{storage}, $node ); >> + >> + die "cant upload to storage type '$scfg->{type}'\n" >> + if !( $scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs' || >> $scfg->{type} eq 'glusterfs' ); >> + >> + my $content = $param->{content}; >> + >> + my $tmpfilename = $param->{tmpfilename}; >> + die "missing temporary file name\n" if !$tmpfilename; >> + >> + my $size = -s $tmpfilename; >> + die "temporary file '$tmpfilename' does not exists\n" if >> !defined( $size ); >> + >> + my $filename = $param->{filename}; >> + >> + chomp $filename; >> + $filename =~ s/^.*[\/\\]//; >> + $filename =~ s/[;:,=\s\x80-\xff]/_/g; >> + >> + my $path; >> + >> + if ( $content eq 'images' ) { # XXX see also >> pve-storage/PVE/Storage/DirPlugin.pm and other >> + >> + my @allowed_extensions = qw(raw qcow qcow2 cow vdi vmdk vpc >> cloop); >> + raise_param_exc( >> + { filename => "extension must be one of " . join ', ', >> map "'$_'", @allowed_extensions } ) >> + if !grep { $filename =~ m![^/]+\.$_$! } >> @allowed_extensions; >> + >> + # XXX use another location! >> + $path = PVE::Storage::get_vztmpl_dir( $cfg, >> $param->{storage} ); >> + } >> + else { >> + raise_param_exc( { content => "upload content type >> '$content' not allowed" } ); >> + } >> + >> + die "storage '$param->{storage}' does not support '$content' >> content\n" >> + if !$scfg->{content}->{$content}; >> + >> + my $dest = "$path/$filename"; >> + my $dirname = dirname( $dest ); >> + >> + # we simply overwrite when destination when file already exists >> + >> + # -- copy -- >> + >> + my $cmd; >> + if ( $node ne 'localhost' && $node ne PVE::INotify::nodename() >> ) { >> + my $remip = PVE::Cluster::remote_node_ip( $node ); >> + >> + my @ssh_options = ( '-o', 'BatchMode=yes' ); >> + >> + my @remcmd = ( '/usr/bin/ssh', @ssh_options, $remip, '--' ); >> + >> + eval { >> + # activate remote storage >> + PVE::Tools::run_command( [ @remcmd, '/usr/sbin/pvesm', >> 'status', '--storage', $param->{storage} ] ); >> + }; >> + die "can't activate storage '$param->{storage}' on node >> '$node'\n" if $@; >> + >> + PVE::Tools::run_command( [ @remcmd, '/bin/mkdir', '-p', >> '--', PVE::Tools::shell_quote( $dirname ) ], >> + errmsg => "mkdir failed" ); >> + >> + $cmd = >> + [ '/usr/bin/scp', @ssh_options, '--', $tmpfilename, >> "[$remip]:" . PVE::Tools::shell_quote( $dest ) ]; >> + } >> + else { >> + PVE::Storage::activate_storage( $cfg, $param->{storage} ); >> + File::Path::make_path( $dirname ); >> + $cmd = [ 'cp', '--', $tmpfilename, $dest ]; >> + } >> + >> + # -- import -- >> + >> + die "vm $vmid is running\n" if PVE::QemuServer::check_running( >> $vmid ); # XXX >> + >> + my $conf = PVE::QemuServer::load_config( $vmid ); >> + my $storecfg = PVE::Storage::config(); >> + my $full = 1; >> + >> + my ( $drivename, $drive ); >> + for my $opt ( keys %{$conf} ) { >> + if ( PVE::QemuServer::valid_drivename( $opt ) ) { >> + $drivename = $opt; >> + $drive = PVE::QemuServer::parse_drive( $opt, >> $conf->{$opt} ); >> + die "unable to parse drive options for '$opt'\n" if >> !$drive; >> + next if PVE::QemuServer::drive_is_cdrom( $drive ); >> + die "Full clone feature is not available" >> + if $full && !PVE::Storage::volume_has_feature( >> $storecfg, 'copy', $drive->{file}, undef, undef ); >> + last; >> + } >> + } >> + >> + my $storage = PVE::Storage::parse_volume_id( $drive->{file} ); >> + >> + my $worker = sub { >> + my $upid = shift; >> + >> + # copy file to storage >> + print "starting file import from: $tmpfilename\n"; >> + print "target node: $node\n"; >> + print "target file: $dest\n"; >> + print "file size is: $size\n"; >> + print "command: " . join( ' ', @$cmd ) . "\n"; >> + >> + eval { PVE::Tools::run_command( $cmd, errmsg => 'import >> failed' ); }; >> + if ( my $err = $@ ) { >> + unlink $dest; >> + die $err; >> + } >> + print "finished file import successfully\n"; >> + >> + # convert image with qemu-img >> + print "\n"; >> + print "starting import of image: $dest\n"; >> + >> + # my $source_file = "$param->{storage}:$dest"; >> + my $source = PVE::Storage::path_to_volume_id( $storecfg, >> $dest ); >> + >> + # XXX remove >> + use Data::Dumper; >> + warn 'PVE::API2::Storage::Status: ' . Dumper( >> + { >> + filename => $filename, >> + dest => $dest, >> + dirname => dirname( $dest ), >> + node => $node, >> + vmid => $vmid, >> + conf => $conf, >> + storecfg => $storecfg, >> + drive => $drive, >> + storage => $storage, >> + #source_file => $source_file, >> + source => $source, >> + } >> + ); >> + >> + my $newvollist = []; >> + my $newdrive = PVE::QemuServer::import_disk( $source, >> $storecfg, $vmid, $drive, $newvollist ); >> + >> + print Dumper $newdrive; >> + >> + }; >> + >> + my $upid = $rpcenv->fork_worker( 'imgcopy', undef, $user, >> $worker ); # XXX other name than imgcopy >> + >> + # apache removes the temporary file on return, so we need >> + # to wait here to make sure the worker process starts and >> + # opens the file before it gets removed. >> + sleep( 1 ); >> + >> + return $upid; >> + } >> + } >> +); >> + >> 1; >> diff --git a/PVE/Storage.pm b/PVE/Storage.pm >> index 140f8ae..e112d4e 100755 >> --- a/PVE/Storage.pm >> +++ b/PVE/Storage.pm >> @@ -372,7 +372,7 @@ sub path_to_volume_id { >> } elsif ($path =~ m!^$isodir/([^/]+\.[Ii][Ss][Oo])$!) { >> my $name = $1; >> return ('iso', "$sid:iso/$name"); >> - } elsif ($path =~ m!^$tmpldir/([^/]+\.tar\.gz)$!) { >> + } elsif ($path =~ m!^$tmpldir/([^/]+(:?\.tar\.gz|\.vmdk))$!) { >> my $name = $1; >> return ('vztmpl', "$sid:vztmpl/$name"); >> } elsif ($path =~ m!^$privatedir/(\d+)$!) { >> diff --git a/PVE/Storage/DirPlugin.pm b/PVE/Storage/DirPlugin.pm >> index bc3c61f..87e9cd7 100644 >> --- a/PVE/Storage/DirPlugin.pm >> +++ b/PVE/Storage/DirPlugin.pm >> @@ -16,7 +16,7 @@ sub type { >> >> sub plugindata { >> return { >> - content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup >> => 1, none => 1 }, >> + content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup >> => 1, none => 1 }, # XXX maybe add new content type? >> { images => 1, rootdir => 1 }], >> format => [ { raw => 1, qcow2 => 1, vmdk => 1, subvol => 1 } , 'raw' ], >> }; >> diff --git a/PVE/Storage/Plugin.pm b/PVE/Storage/Plugin.pm >> index 6aa71e0..a8bb385 100644 >> --- a/PVE/Storage/Plugin.pm >> +++ b/PVE/Storage/Plugin.pm >> @@ -374,7 +374,7 @@ sub parse_volname { >> return ('images', $name, $vmid, undef, undef, $isBase, $format); >> } elsif ($volname =~ m!^iso/([^/]+\.[Ii][Ss][Oo])$!) { >> return ('iso', $1); >> - } elsif ($volname =~ m!^vztmpl/([^/]+\.tar\.[gx]z)$!) { >> + } elsif ($volname =~ >> m!^vztmpl/([^/]+(:?\.tar\.[gx]z|\.vmdk|\.qcow2|\.raw))$!) { >> return ('vztmpl', $1); >> } elsif ($volname =~ m!^rootdir/(\d+)$!) { >> return ('rootdir', $1, $1); >> -- >> 2.1.4 >> >> _______________________________________________ >> pve-devel mailing list >> pve-devel@pve.proxmox.com >> http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel >> > -- Mit freundlichen Grüßen Timo Grodzinski Ihr Profihost Team ------------------------------- Profihost AG Expo Plaza 1 30539 Hannover Deutschland Tel.: +49 (511) 5151 8181 | Fax.: +49 (511) 5151 8282 URL: http://www.profihost.com | E-Mail: i...@profihost.com Sitz der Gesellschaft: Hannover, USt-IdNr. DE813460827 Registergericht: Amtsgericht Hannover, Register-Nr.: HRB 202350 Vorstand: Cristoph Bluhm, Sebastian Bluhm, Stefan Priebe Aufsichtsrat: Prof. Dr. iur. Winfried Huck (Vorsitzender) _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel