]> git.proxmox.com Git - pve-container.git/blame - src/PVE/API2/LXC/Config.pm
replace disk-size calculation in pct resize
[pve-container.git] / src / PVE / API2 / LXC / Config.pm
CommitLineData
52389a07
DM
1package PVE::API2::LXC::Config;
2
3use strict;
4use warnings;
5
6use PVE::SafeSyslog;
7use PVE::Tools qw(extract_param run_command);
8use PVE::Exception qw(raise raise_param_exc);
9use PVE::INotify;
10use PVE::Cluster qw(cfs_read_file);
11use PVE::AccessControl;
12use PVE::Firewall;
13use PVE::Storage;
14use PVE::RESTHandler;
15use PVE::RPCEnvironment;
16use PVE::LXC;
17use PVE::LXC::Create;
18use PVE::HA::Config;
19use PVE::JSONSchema qw(get_standard_option);
20use base qw(PVE::RESTHandler);
21
22use Data::Dumper; # fixme: remove
23
24__PACKAGE__->register_method({
25 name => 'vm_config',
26 path => '',
27 method => 'GET',
28 proxyto => 'node',
29 description => "Get container configuration.",
30 permissions => {
31 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
32 },
33 parameters => {
34 additionalProperties => 0,
35 properties => {
36 node => get_standard_option('pve-node'),
68e8f3c5 37 vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid }),
52389a07
DM
38 },
39 },
40 returns => {
41 type => "object",
42 properties => {
43 digest => {
44 type => 'string',
45 description => 'SHA1 digest of configuration file. This can be used to prevent concurrent modifications.',
46 }
47 },
48 },
49 code => sub {
50 my ($param) = @_;
51
52 my $conf = PVE::LXC::load_config($param->{vmid});
53
54 delete $conf->{snapshots};
55 delete $conf->{lxc};
56
57 return $conf;
58 }});
59
60my $vm_config_perm_list = [
61 'VM.Config.Disk',
62 'VM.Config.CPU',
63 'VM.Config.Memory',
64 'VM.Config.Network',
65 'VM.Config.Options',
66 ];
67
68__PACKAGE__->register_method({
69 name => 'update_vm',
70 path => '',
71 method => 'PUT',
72 protected => 1,
73 proxyto => 'node',
74 description => "Set container options.",
75 permissions => {
76 check => ['perm', '/vms/{vmid}', $vm_config_perm_list, any => 1],
77 },
78 parameters => {
79 additionalProperties => 0,
80 properties => PVE::LXC::json_config_properties(
81 {
82 node => get_standard_option('pve-node'),
68e8f3c5 83 vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid }),
52389a07
DM
84 delete => {
85 type => 'string', format => 'pve-configid-list',
86 description => "A list of settings you want to delete.",
87 optional => 1,
88 },
89 digest => {
90 type => 'string',
91 description => 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
92 maxLength => 40,
93 optional => 1,
94 }
95 }),
96 },
97 returns => { type => 'null'},
98 code => sub {
99 my ($param) = @_;
100
101 my $rpcenv = PVE::RPCEnvironment::get();
102
103 my $authuser = $rpcenv->get_user();
104
105 my $node = extract_param($param, 'node');
106
107 my $vmid = extract_param($param, 'vmid');
108
109 my $digest = extract_param($param, 'digest');
110
111 die "no options specified\n" if !scalar(keys %$param);
112
113 my $delete_str = extract_param($param, 'delete');
114 my @delete = PVE::Tools::split_list($delete_str);
115
116 PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [@delete]);
117
118 foreach my $opt (@delete) {
119 raise_param_exc({ delete => "you can't use '-$opt' and " .
120 "-delete $opt' at the same time" })
121 if defined($param->{$opt});
122
123 if (!PVE::LXC::option_exists($opt)) {
124 raise_param_exc({ delete => "unknown option '$opt'" });
125 }
126 }
127
128 PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]);
129
130 my $storage_cfg = cfs_read_file("storage.cfg");
131
132 my $code = sub {
133
134 my $conf = PVE::LXC::load_config($vmid);
135 PVE::LXC::check_lock($conf);
136
137 PVE::Tools::assert_if_modified($digest, $conf->{digest});
138
139 my $running = PVE::LXC::check_running($vmid);
140
141 PVE::LXC::update_pct_config($vmid, $conf, $running, $param, \@delete);
142
143 PVE::LXC::write_config($vmid, $conf);
144 PVE::LXC::update_lxc_config($storage_cfg, $vmid, $conf);
145 };
146
147 PVE::LXC::lock_container($vmid, undef, $code);
148
149 return undef;
150 }});
151
40626217
WB
152my $query_loopdev = sub {
153 my ($path) = @_;
154 my $found;
155 my $parser = sub {
156 my $line = shift;
157 if ($line =~ m@^(/dev/loop\d+):@) {
158 $found = $1;
159 }
160 };
161 my $cmd = ['losetup', '--associated', $path];
162 PVE::Tools::run_command($cmd, outfunc => $parser);
163 return $found;
164};
165
166__PACKAGE__->register_method({
167 name => 'resize_vm',
168 path => '{vmid}/resize',
169 method => 'PUT',
170 protected => 1,
171 proxyto => 'node',
172 description => "Resize a container mountpoint.",
173 permissions => {
174 check => ['perm', '/vms/{vmid}', $vm_config_perm_list, any => 1],
175 },
176 parameters => {
177 additionalProperties => 0,
178 properties => PVE::LXC::json_config_properties(
179 {
180 node => get_standard_option('pve-node'),
bd491d60 181 vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid }),
40626217
WB
182 disk => {
183 type => 'string',
184 description => "The disk you want to resize.",
185 enum => [PVE::LXC::mountpoint_names()],
186 },
187 size => {
188 type => 'string',
189 pattern => '\+?\d+(\.\d+)?[KMGT]?',
190 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.",
191 },
192 digest => {
193 type => 'string',
194 description => 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
195 maxLength => 40,
196 optional => 1,
197 }
198 }),
199 },
200 returns => { type => 'null'},
201 code => sub {
202 my ($param) = @_;
203
204 my $rpcenv = PVE::RPCEnvironment::get();
205
206 my $authuser = $rpcenv->get_user();
207
208 my $node = extract_param($param, 'node');
209
210 my $vmid = extract_param($param, 'vmid');
211
212 my $digest = extract_param($param, 'digest');
213
214 my $sizestr = extract_param($param, 'size');
ff6bba6c
WB
215 my $ext = ($sizestr =~ s/^\+//);
216 my $newsize = PVE::JSONSchema::parse_size($sizestr);
217 die "invalid size string" if !defined($newsize);
40626217
WB
218
219 die "no options specified\n" if !scalar(keys %$param);
220
221 PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]);
222
223 my $storage_cfg = cfs_read_file("storage.cfg");
224
225 my $code = sub {
226
227 my $conf = PVE::LXC::load_config($vmid);
228 PVE::LXC::check_lock($conf);
229
230 PVE::Tools::assert_if_modified($digest, $conf->{digest});
231
232 my $running = PVE::LXC::check_running($vmid);
233
234 my $disk = $param->{disk};
235 my $mp = PVE::LXC::parse_ct_mountpoint($conf->{$disk});
236 my $volid = $mp->{volume};
237
238 my (undef, undef, $owner, undef, undef, undef, $format) =
239 PVE::Storage::parse_volname($storage_cfg, $volid);
240
241 die "can't resize mountpoint owned by another container ($owner)"
242 if $vmid != $owner;
243
244 die "can't resize volume: $disk if snapshot exists\n"
245 if %{$conf->{snapshots}} && $format eq 'qcow2';
246
247 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
248
249 $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
250
251 my $size = PVE::Storage::volume_size_info($storage_cfg, $volid, 5);
40626217
WB
252 $newsize += $size if $ext;
253 $newsize = int($newsize);
254
255 die "unable to skrink disk size\n" if $newsize < $size;
256
257 return if $size == $newsize;
258
259 PVE::Cluster::log_msg('info', $authuser, "update CT $vmid: resize --disk $disk --size $sizestr");
260
261 # FIXME: volume_resize doesn't do anything if $running=1?
262 PVE::Storage::volume_resize($storage_cfg, $volid, $newsize, 0);
263
264 $mp->{size} = $newsize/1024; # kB
265 $conf->{$disk} = PVE::LXC::print_ct_mountpoint($mp, $disk eq 'rootfs');
266
267 PVE::LXC::write_config($vmid, $conf);
268
269 if ($format eq 'raw') {
270 my $path = PVE::Storage::path($storage_cfg, $volid, undef);
271 if ($running) {
40626217
WB
272 $path = &$query_loopdev($path);
273 die "internal error: CT running but mountpoint not attached to a loop device"
274 if !$path; # fixme: zvols and other storages?
275 PVE::Tools::run_command(['losetup', '--set-capacity', $path]);
276
277 # In order for resize2fs to know that we need online-resizing a mountpoint needs
278 # to be visible to it in its namespace.
279 # To not interfere with the rest of the system we unshare the current mount namespace,
280 # mount over /tmp and then run resize2fs.
281
282 # interestingly we don't need to e2fsck on mounted systems...
283 my $quoted = PVE::Tools::shellquote($path);
284 my $cmd = "mount --make-rprivate / && mount $quoted /tmp && resize2fs $quoted";
285 PVE::Tools::run_command(['unshare', '-m', '--', 'sh', '-c', $cmd]);
286 } else {
287 PVE::Tools::run_command(['e2fsck', '-f', '-y', $path]);
288 PVE::Tools::run_command(['resize2fs', $path]);
289 }
290 }
291 };
292
293 PVE::LXC::lock_container($vmid, undef, $code);
294
295 return undef;
296 }});
297
52389a07 2981;