[pve-devel] [[PATCH_2 pve-container]] Add the functionality of full clone
Thomas Lamprecht
t.lamprecht at proxmox.com
Wed Apr 20 11:14:13 CEST 2016
some general comments inline:
On 04/20/2016 08:06 AM, Wolfgang Link wrote:
> ---
> src/PVE/API2/LXC.pm | 21 +++++++++----------
> src/PVE/LXC.pm | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 68 insertions(+), 11 deletions(-)
>
> diff --git a/src/PVE/API2/LXC.pm b/src/PVE/API2/LXC.pm
> index 90cdba6..537ab69 100644
> --- a/src/PVE/API2/LXC.pm
> +++ b/src/PVE/API2/LXC.pm
> @@ -1073,6 +1073,7 @@ __PACKAGE__->register_method({
>
> my $storage = extract_param($param, 'storage');
>
> + die "Full clone require a storage.\n"if $param->{full} && !$storage;
space behind the if would give some better readability, also
s/require/requires/
> my $localnode = PVE::INotify::nodename();
>
> my $storecfg = PVE::Storage::config();
> @@ -1126,10 +1127,6 @@ __PACKAGE__->register_method({
> if ($mp->{type} eq 'volume') {
> my $volid = $mp->{volume};
> if ($param->{full}) {
> - die "fixme: full clone not implemented";
> -
> - die "Full clone feature for '$volid' is not available\n"
> - if !PVE::Storage::volume_has_feature($storecfg, 'copy', $volid, $snapname, $running);
> $fullclone->{$opt} = 1;
> } else {
> # not full means clone instead of copy
> @@ -1176,18 +1173,20 @@ __PACKAGE__->register_method({
> foreach my $opt (keys %$mountpoints) {
> my $mp = $mountpoints->{$opt};
> my $volid = $mp->{volume};
> + my $newvolid;
>
> if ($fullclone->{$opt}) {
> - die "fixme: full clone not implemented\n";
> + print "create full clone of mountpoint $opt ($volid)\n";
> + $newvolid = PVE::LXC::copy_volume($mp, $vmid, $newid, $storage, $storecfg, $conf);
> } else {
> print "create linked clone of mountpoint $opt ($volid)\n";
> - my $newvolid = PVE::Storage::vdisk_clone($storecfg, $volid, $newid, $snapname);
> - push @$newvollist, $newvolid;
> - $mp->{volume} = $newvolid;
> -
> - $newconf->{$opt} = PVE::LXC::Config->print_ct_mountpoint($mp, $opt eq 'rootfs');
> - PVE::LXC::Config->write_config($newid, $newconf);
> + $newvolid = PVE::Storage::vdisk_clone($storecfg, $volid, $newid, $snapname);
> }
> + push @$newvollist, $newvolid;
> + $mp->{volume} = $newvolid;
> +
> + $newconf->{$opt} = PVE::LXC::Config->print_ct_mountpoint($mp, $opt eq 'rootfs');
> + PVE::LXC::Config->write_config($newid, $newconf);
> }
>
> delete $newconf->{lock};
> diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm
> index fe97d31..7c773bf 100644
> --- a/src/PVE/LXC.pm
> +++ b/src/PVE/LXC.pm
> @@ -22,6 +22,7 @@ use PVE::AccessControl;
> use PVE::ProcFSTools;
> use PVE::LXC::Config;
> use Time::HiRes qw (gettimeofday);
> +use Storable;
>
> use Data::Dumper;
>
> @@ -1348,5 +1349,62 @@ sub userns_command {
> return [];
> }
>
> +sub copy_volume {
> + my ($mp, $vmid, $newid, $storage, $storage_cfg, $conf) = @_;
> + my $mount_dir = "/tmp/$vmid";
> + my $dest = "$mount_dir\/dest";
> + my $src = "$mount_dir\/src";
> +
> + my $new_format = $storage_cfg->{ids}->{$storage}->{type} eq "zfspool" ? 'subvol' : 'raw';
> +
> + my $new_volid = PVE::Storage::vdisk_alloc($storage_cfg, $storage, $newid, $new_format, undef, $mp->{size}/1024);
> +
> + #simplify mount struct
> + my $tmp_mp = $mp->{mp};
> + $mp->{mp} = '/';
as I'm not that involved with mount points this may be a really stupid
question, but what is that for?
seems a little weird and the comment isn't helping me.
A comment explaining why and not you're doing would be helpful, e.g. for
an not mp involved Dev who needs to touch this.
> +
> + my $new_mp = Storable::dclone($mp);
> + $new_mp->{volume} = $new_volid;
> +
> + #get id's for unprivileged container
> + my (undef, $rootuid, $rootgid) = parse_id_maps($conf);
> +
> + my $path = PVE::Storage::path($storage_cfg, $new_volid, undef);
> +
> + eval {
> + mkfs($path, $rootuid, $rootgid) if $new_format ne "subvol";
> +
> + mkdir $mount_dir;
> + mkdir $dest;
> + mkdir $src;
> +
> + #mount for copy mp
> + mountpoint_mount($mp, $src, $storage_cfg);
> + mountpoint_mount($new_mp, $dest, $storage_cfg);
> +
> + PVE::Tools::run_command(['/usr/bin/rsync', '--stats', '-X', '-A', '--numeric-ids',
> + '--whole-file', '--inplace', '--one-file-system', '-aH',
> + "$src\/.", $dest]);
> +
> + };
> + my $err = $@;
> + eval { PVE::Tools::run_command(['/bin/umount', '-f', $src], errfunc => sub{})};
> + warn "Can't umount $src\n" if $@;
> + eval { PVE::Tools::run_command(['/bin/umount', '-f', $dest], errfunc => sub{})};
> + warn "Can't umount $src\n" if $@;
> +
> + rmdir $dest;
> + rmdir $src;
if the previous umount fails those will also fail (rmdir works on empty
directory only).
Also is a force umount a good way, shouldn't be checked if the caches
are all written (i.e. executing a fsync previously),
or am I wrong?
> + rmdir $mount_dir;
> +
> + #set it back to orgin value
s/orgin/original/
> + $mp->{mp} = $tmp_mp;
> +
> + if ($err) {
> + PVE::Storage::vdisk_free($storage_cfg, $new_volid);
> + die $err;
> + }
> + return $new_volid;
> +}
>
> 1;
More information about the pve-devel
mailing list