]> git.proxmox.com Git - pve-container.git/blob - src/PVE/API2/LXC.pm
initial commit
[pve-container.git] / src / PVE / API2 / LXC.pm
1 package PVE::API2::LXC;
2
3 use strict;
4 use warnings;
5
6 use PVE::SafeSyslog;
7 use PVE::Tools qw(extract_param run_command);
8 use PVE::Exception qw(raise raise_param_exc);
9 use PVE::INotify;
10 use PVE::AccessControl;
11 use PVE::Storage;
12 use PVE::RESTHandler;
13 use PVE::RPCEnvironment;
14 use PVE::LXC;
15 use PVE::JSONSchema qw(get_standard_option);
16 use base qw(PVE::RESTHandler);
17
18 use Data::Dumper; # fixme: remove
19
20 my $get_container_storage = sub {
21 my ($stcfg, $vmid, $lxc_conf) = @_;
22
23 my $path = $lxc_conf->{'lxc.rootfs'};
24 my ($vtype, $volid) = PVE::Storage::path_to_volume_id($stcfg, $path);
25 my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1) if $volid;
26 return wantarray ? ($sid, $volname, $path) : $sid;
27 };
28
29 my $check_ct_modify_config_perm = sub {
30 my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_;
31
32 return 1 if $authuser ne 'root@pam';
33
34 foreach my $opt (@$key_list) {
35
36 if ($opt eq 'cpus' || $opt eq 'cpuunits' || $opt eq 'cpulimit') {
37 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']);
38 } elsif ($opt eq 'disk') {
39 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
40 } elsif ($opt eq 'memory' || $opt eq 'swap') {
41 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']);
42 } elsif ($opt =~ m/^net\d+$/ || $opt eq 'nameserver' ||
43 $opt eq 'searchdomain' || $opt eq 'hostname') {
44 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
45 } else {
46 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']);
47 }
48 }
49
50 return 1;
51 };
52
53
54 __PACKAGE__->register_method({
55 name => 'vmlist',
56 path => '',
57 method => 'GET',
58 description => "LXC container index (per node).",
59 permissions => {
60 description => "Only list CTs where you have VM.Audit permissons on /vms/<vmid>.",
61 user => 'all',
62 },
63 proxyto => 'node',
64 protected => 1, # /proc files are only readable by root
65 parameters => {
66 additionalProperties => 0,
67 properties => {
68 node => get_standard_option('pve-node'),
69 },
70 },
71 returns => {
72 type => 'array',
73 items => {
74 type => "object",
75 properties => {},
76 },
77 links => [ { rel => 'child', href => "{vmid}" } ],
78 },
79 code => sub {
80 my ($param) = @_;
81
82 my $rpcenv = PVE::RPCEnvironment::get();
83 my $authuser = $rpcenv->get_user();
84
85 my $vmstatus = PVE::LXC::vmstatus();
86
87 my $res = [];
88 foreach my $vmid (keys %$vmstatus) {
89 next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
90
91 my $data = $vmstatus->{$vmid};
92 $data->{vmid} = $vmid;
93 push @$res, $data;
94 }
95
96 return $res;
97
98 }});
99
100 my $vm_config_perm_list = [
101 'VM.Config.Disk',
102 'VM.Config.CPU',
103 'VM.Config.Memory',
104 'VM.Config.Network',
105 'VM.Config.Options',
106 ];
107
108 __PACKAGE__->register_method({
109 name => 'update_vm',
110 path => '{vmid}/config',
111 method => 'PUT',
112 protected => 1,
113 proxyto => 'node',
114 description => "Set container options.",
115 permissions => {
116 check => ['perm', '/vms/{vmid}', $vm_config_perm_list, any => 1],
117 },
118 parameters => {
119 additionalProperties => 0,
120 properties => PVE::LXC::json_config_properties(
121 {
122 node => get_standard_option('pve-node'),
123 vmid => get_standard_option('pve-vmid'),
124 digest => {
125 type => 'string',
126 description => 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
127 maxLength => 40,
128 optional => 1,
129 }
130 }),
131 },
132 returns => { type => 'null'},
133 code => sub {
134 my ($param) = @_;
135
136 my $rpcenv = PVE::RPCEnvironment::get();
137
138 my $authuser = $rpcenv->get_user();
139
140 my $node = extract_param($param, 'node');
141
142 my $vmid = extract_param($param, 'vmid');
143
144 my $digest = extract_param($param, 'digest');
145
146 die "no options specified\n" if !scalar(keys %$param);
147
148 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]);
149
150 my $code = sub {
151
152 my $conf = PVE::LXC::load_config($vmid);
153
154 PVE::Tools::assert_if_modified($digest, $conf->{digest});
155
156 die "implement me"
157 };
158
159 PVE::LXC::lock_container($vmid, undef, $code);
160
161 return undef;
162 }});
163
164 __PACKAGE__->register_method ({
165 subclass => "PVE::API2::Firewall::CT",
166 path => '{vmid}/firewall',
167 });
168
169 __PACKAGE__->register_method({
170 name => 'vmdiridx',
171 path => '{vmid}',
172 method => 'GET',
173 proxyto => 'node',
174 description => "Directory index",
175 permissions => {
176 user => 'all',
177 },
178 parameters => {
179 additionalProperties => 0,
180 properties => {
181 node => get_standard_option('pve-node'),
182 vmid => get_standard_option('pve-vmid'),
183 },
184 },
185 returns => {
186 type => 'array',
187 items => {
188 type => "object",
189 properties => {
190 subdir => { type => 'string' },
191 },
192 },
193 links => [ { rel => 'child', href => "{subdir}" } ],
194 },
195 code => sub {
196 my ($param) = @_;
197
198 # test if VM exists
199 my $conf = PVE::OpenVZ::load_config($param->{vmid});
200
201 my $res = [
202 { subdir => 'config' },
203 # { subdir => 'status' },
204 # { subdir => 'vncproxy' },
205 # { subdir => 'spiceproxy' },
206 # { subdir => 'migrate' },
207 # { subdir => 'initlog' },
208 { subdir => 'rrd' },
209 { subdir => 'rrddata' },
210 { subdir => 'firewall' },
211 ];
212
213 return $res;
214 }});
215
216 __PACKAGE__->register_method({
217 name => 'rrd',
218 path => '{vmid}/rrd',
219 method => 'GET',
220 protected => 1, # fixme: can we avoid that?
221 permissions => {
222 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
223 },
224 description => "Read VM RRD statistics (returns PNG)",
225 parameters => {
226 additionalProperties => 0,
227 properties => {
228 node => get_standard_option('pve-node'),
229 vmid => get_standard_option('pve-vmid'),
230 timeframe => {
231 description => "Specify the time frame you are interested in.",
232 type => 'string',
233 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
234 },
235 ds => {
236 description => "The list of datasources you want to display.",
237 type => 'string', format => 'pve-configid-list',
238 },
239 cf => {
240 description => "The RRD consolidation function",
241 type => 'string',
242 enum => [ 'AVERAGE', 'MAX' ],
243 optional => 1,
244 },
245 },
246 },
247 returns => {
248 type => "object",
249 properties => {
250 filename => { type => 'string' },
251 },
252 },
253 code => sub {
254 my ($param) = @_;
255
256 return PVE::Cluster::create_rrd_graph(
257 "pve2-vm/$param->{vmid}", $param->{timeframe},
258 $param->{ds}, $param->{cf});
259
260 }});
261
262 __PACKAGE__->register_method({
263 name => 'rrddata',
264 path => '{vmid}/rrddata',
265 method => 'GET',
266 protected => 1, # fixme: can we avoid that?
267 permissions => {
268 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
269 },
270 description => "Read VM RRD statistics",
271 parameters => {
272 additionalProperties => 0,
273 properties => {
274 node => get_standard_option('pve-node'),
275 vmid => get_standard_option('pve-vmid'),
276 timeframe => {
277 description => "Specify the time frame you are interested in.",
278 type => 'string',
279 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
280 },
281 cf => {
282 description => "The RRD consolidation function",
283 type => 'string',
284 enum => [ 'AVERAGE', 'MAX' ],
285 optional => 1,
286 },
287 },
288 },
289 returns => {
290 type => "array",
291 items => {
292 type => "object",
293 properties => {},
294 },
295 },
296 code => sub {
297 my ($param) = @_;
298
299 return PVE::Cluster::create_rrd_data(
300 "pve2-vm/$param->{vmid}", $param->{timeframe}, $param->{cf});
301 }});
302
303
304 __PACKAGE__->register_method({
305 name => 'vm_config',
306 path => '{vmid}/config',
307 method => 'GET',
308 proxyto => 'node',
309 description => "Get container configuration.",
310 permissions => {
311 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
312 },
313 parameters => {
314 additionalProperties => 0,
315 properties => {
316 node => get_standard_option('pve-node'),
317 vmid => get_standard_option('pve-vmid'),
318 },
319 },
320 returns => {
321 type => "object",
322 properties => {
323 digest => {
324 type => 'string',
325 description => 'SHA1 digest of configuration file. This can be used to prevent concurrent modifications.',
326 }
327 },
328 },
329 code => sub {
330 my ($param) = @_;
331
332 my $lxc_conf = PVE::LXC::load_config($param->{vmid});
333
334 # NOTE: we only return selected/converted values
335
336 my $conf = { digest => $lxc_conf->{digest} };
337
338 my $stcfg = PVE::Cluster::cfs_read_file("storage.cfg");
339
340 my ($sid, undef, $path) = &$get_container_storage($stcfg, $param->{vmid}, $lxc_conf);
341 $conf->{storage} = $sid || $path;
342
343 my $properties = PVE::LXC::json_config_properties();
344
345 foreach my $k (keys %$properties) {
346
347 if ($k eq 'description') {
348 if (my $raw = $lxc_conf->{'pve.comment'}) {
349 $conf->{$k} = PVE::Tools::decode_text($raw);
350 }
351 } elsif ($k eq 'hostname') {
352 $conf->{$k} = $lxc_conf->{'lxc.utsname'} if $lxc_conf->{'lxc.utsname'};
353 } elsif ($k =~ m/^net\d$/) {
354 my $net = $lxc_conf->{$k};
355 next if !$net;
356 $conf->{$k} = PVE::LXC::print_netif($net);
357 }
358 }
359
360 return $conf;
361 }});
362
363 __PACKAGE__->register_method({
364 name => 'destroy_vm',
365 path => '{vmid}',
366 method => 'DELETE',
367 protected => 1,
368 proxyto => 'node',
369 description => "Destroy the container (also delete all uses files).",
370 permissions => {
371 check => [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
372 },
373 parameters => {
374 additionalProperties => 0,
375 properties => {
376 node => get_standard_option('pve-node'),
377 vmid => get_standard_option('pve-vmid'),
378 },
379 },
380 returns => {
381 type => 'string',
382 },
383 code => sub {
384 my ($param) = @_;
385
386 my $rpcenv = PVE::RPCEnvironment::get();
387
388 my $authuser = $rpcenv->get_user();
389
390 my $vmid = $param->{vmid};
391
392 # test if container exists
393 my $conf = PVE::LXC::load_config($param->{vmid});
394
395 my $realcmd = sub {
396 my $cmd = ['lxc-destroy', '-n', $vmid ];
397
398 run_command($cmd);
399
400 PVE::AccessControl::remove_vm_from_pool($vmid);
401 };
402
403 return $rpcenv->fork_worker('vzdestroy', $vmid, $authuser, $realcmd);
404 }});
405
406 1;