--- src/PVE/API2/LXC/Config.pm | 159 +++++++++++++++++++++++++++++++++++++++++++++ src/PVE/CLI/pct.pm | 2 + 2 files changed, 161 insertions(+)
diff --git a/src/PVE/API2/LXC/Config.pm b/src/PVE/API2/LXC/Config.pm index 95eafaa..476e07c 100644 --- a/src/PVE/API2/LXC/Config.pm +++ b/src/PVE/API2/LXC/Config.pm @@ -149,4 +149,163 @@ __PACKAGE__->register_method({ return undef; }}); +my $query_loopdev = sub { + my ($path) = @_; + my $found; + my $parser = sub { + my $line = shift; + if ($line =~ m@^(/dev/loop\d+):@) { + $found = $1; + } + }; + my $cmd = ['losetup', '--associated', $path]; + PVE::Tools::run_command($cmd, outfunc => $parser); + return $found; +}; + +__PACKAGE__->register_method({ + name => 'resize_vm', + path => '{vmid}/resize', + method => 'PUT', + protected => 1, + proxyto => 'node', + description => "Resize a container mountpoint.", + permissions => { + check => ['perm', '/vms/{vmid}', $vm_config_perm_list, any => 1], + }, + parameters => { + additionalProperties => 0, + properties => PVE::LXC::json_config_properties( + { + node => get_standard_option('pve-node'), + vmid => get_standard_option('pve-vmid'), + disk => { + type => 'string', + description => "The disk you want to resize.", + enum => [PVE::LXC::mountpoint_names()], + }, + size => { + type => 'string', + pattern => '\+?\d+(\.\d+)?[KMGT]?', + description => "The new size. With the '+' sign the value is added to the actual size of the volume and without it, the value is taken as an absolute one. Shrinking disk size is not supported.", + }, + digest => { + type => 'string', + description => 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.', + maxLength => 40, + optional => 1, + } + }), + }, + returns => { type => 'null'}, + code => sub { + my ($param) = @_; + + my $rpcenv = PVE::RPCEnvironment::get(); + + my $authuser = $rpcenv->get_user(); + + my $node = extract_param($param, 'node'); + + my $vmid = extract_param($param, 'vmid'); + + my $digest = extract_param($param, 'digest'); + + my $sizestr = extract_param($param, 'size'); + die "invalid size string" if $sizestr !~ m/^(\+)?(\d+(\.\d+)?)([KMGT])?$/; + my ($ext, $newsize, $unit) = ($1, $2, $4); + + die "no options specified\n" if !scalar(keys %$param); + + PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]); + + my $storage_cfg = cfs_read_file("storage.cfg"); + + my $code = sub { + + my $conf = PVE::LXC::load_config($vmid); + PVE::LXC::check_lock($conf); + + PVE::Tools::assert_if_modified($digest, $conf->{digest}); + + my $running = PVE::LXC::check_running($vmid); + + my $disk = $param->{disk}; + my $mp = PVE::LXC::parse_ct_mountpoint($conf->{$disk}); + my $volid = $mp->{volume}; + + my (undef, undef, $owner, undef, undef, undef, $format) = + PVE::Storage::parse_volname($storage_cfg, $volid); + + die "can't resize mountpoint owned by another container ($owner)" + if $vmid != $owner; + + die "can't resize volume: $disk if snapshot exists\n" + if %{$conf->{snapshots}} && $format eq 'qcow2'; + + my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid); + + $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']); + + my $size = PVE::Storage::volume_size_info($storage_cfg, $volid, 5); + # FIXME: also in PVE::API2::Qemu (PVE::Tools candidate?) + if ($unit) { + if ($unit eq 'K') { + $newsize = $newsize * 1024; + } elsif ($unit eq 'M') { + $newsize = $newsize * 1024 * 1024; + } elsif ($unit eq 'G') { + $newsize = $newsize * 1024 * 1024 * 1024; + } elsif ($unit eq 'T') { + $newsize = $newsize * 1024 * 1024 * 1024 * 1024; + } + } + $newsize += $size if $ext; + $newsize = int($newsize); + + die "unable to skrink disk size\n" if $newsize < $size; + + return if $size == $newsize; + + PVE::Cluster::log_msg('info', $authuser, "update CT $vmid: resize --disk $disk --size $sizestr"); + + # FIXME: volume_resize doesn't do anything if $running=1? + PVE::Storage::volume_resize($storage_cfg, $volid, $newsize, 0); + + $mp->{size} = $newsize/1024; # kB + $conf->{$disk} = PVE::LXC::print_ct_mountpoint($mp, $disk eq 'rootfs'); + + PVE::LXC::write_config($vmid, $conf); + + if ($format eq 'raw') { + my $path = PVE::Storage::path($storage_cfg, $volid, undef); + if ($running) { + # NOTE: do not use loopdevices_list as losetup's paths get scrambled after a container started + # (possibly due to namespaces?) + $path = &$query_loopdev($path); + die "internal error: CT running but mountpoint not attached to a loop device" + if !$path; # fixme: zvols and other storages? + PVE::Tools::run_command(['losetup', '--set-capacity', $path]); + + # In order for resize2fs to know that we need online-resizing a mountpoint needs + # to be visible to it in its namespace. + # To not interfere with the rest of the system we unshare the current mount namespace, + # mount over /tmp and then run resize2fs. + + # interestingly we don't need to e2fsck on mounted systems... + my $quoted = PVE::Tools::shellquote($path); + my $cmd = "mount --make-rprivate / && mount $quoted /tmp && resize2fs $quoted"; + PVE::Tools::run_command(['unshare', '-m', '--', 'sh', '-c', $cmd]); + } else { + PVE::Tools::run_command(['e2fsck', '-f', '-y', $path]); + PVE::Tools::run_command(['resize2fs', $path]); + } + } + }; + + PVE::LXC::lock_container($vmid, undef, $code); + + return undef; + }}); + 1; diff --git a/src/PVE/CLI/pct.pm b/src/PVE/CLI/pct.pm index ac67e07..8c56f89 100755 --- a/src/PVE/CLI/pct.pm +++ b/src/PVE/CLI/pct.pm @@ -147,6 +147,8 @@ our $cmddef = { } }], set => [ 'PVE::API2::LXC::Config', 'update_vm', ['vmid'], { node => $nodename }], + + resize => [ "PVE::API2::LXC::Config", 'resize_vm', ['vmid', 'disk', 'size'], { node => $nodename } ], create => [ 'PVE::API2::LXC', 'create_vm', ['vmid', 'ostemplate'], { node => $nodename }, $upid_exit ], restore => [ 'PVE::API2::LXC', 'create_vm', ['vmid', 'ostemplate'], { node => $nodename, restore => 1 }, $upid_exit ], -- 2.1.4 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel