]>
Commit | Line | Data |
---|---|---|
f76a2828 DM |
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; | |
9c2d4ce9 | 10 | use PVE::Cluster qw(cfs_read_file); |
f76a2828 DM |
11 | use PVE::AccessControl; |
12 | use PVE::Storage; | |
13 | use PVE::RESTHandler; | |
14 | use PVE::RPCEnvironment; | |
15 | use PVE::LXC; | |
16 | use PVE::JSONSchema qw(get_standard_option); | |
17 | use base qw(PVE::RESTHandler); | |
18 | ||
19 | use Data::Dumper; # fixme: remove | |
20 | ||
21 | my $get_container_storage = sub { | |
22 | my ($stcfg, $vmid, $lxc_conf) = @_; | |
23 | ||
24 | my $path = $lxc_conf->{'lxc.rootfs'}; | |
25 | my ($vtype, $volid) = PVE::Storage::path_to_volume_id($stcfg, $path); | |
26 | my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1) if $volid; | |
27 | return wantarray ? ($sid, $volname, $path) : $sid; | |
28 | }; | |
29 | ||
30 | my $check_ct_modify_config_perm = sub { | |
31 | my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_; | |
32 | ||
33 | return 1 if $authuser ne 'root@pam'; | |
34 | ||
35 | foreach my $opt (@$key_list) { | |
36 | ||
37 | if ($opt eq 'cpus' || $opt eq 'cpuunits' || $opt eq 'cpulimit') { | |
38 | $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']); | |
39 | } elsif ($opt eq 'disk') { | |
40 | $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']); | |
41 | } elsif ($opt eq 'memory' || $opt eq 'swap') { | |
42 | $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']); | |
43 | } elsif ($opt =~ m/^net\d+$/ || $opt eq 'nameserver' || | |
44 | $opt eq 'searchdomain' || $opt eq 'hostname') { | |
45 | $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']); | |
46 | } else { | |
47 | $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']); | |
48 | } | |
49 | } | |
50 | ||
51 | return 1; | |
52 | }; | |
53 | ||
54 | ||
55 | __PACKAGE__->register_method({ | |
56 | name => 'vmlist', | |
57 | path => '', | |
58 | method => 'GET', | |
59 | description => "LXC container index (per node).", | |
60 | permissions => { | |
61 | description => "Only list CTs where you have VM.Audit permissons on /vms/<vmid>.", | |
62 | user => 'all', | |
63 | }, | |
64 | proxyto => 'node', | |
65 | protected => 1, # /proc files are only readable by root | |
66 | parameters => { | |
67 | additionalProperties => 0, | |
68 | properties => { | |
69 | node => get_standard_option('pve-node'), | |
70 | }, | |
71 | }, | |
72 | returns => { | |
73 | type => 'array', | |
74 | items => { | |
75 | type => "object", | |
76 | properties => {}, | |
77 | }, | |
78 | links => [ { rel => 'child', href => "{vmid}" } ], | |
79 | }, | |
80 | code => sub { | |
81 | my ($param) = @_; | |
82 | ||
83 | my $rpcenv = PVE::RPCEnvironment::get(); | |
84 | my $authuser = $rpcenv->get_user(); | |
85 | ||
86 | my $vmstatus = PVE::LXC::vmstatus(); | |
87 | ||
88 | my $res = []; | |
89 | foreach my $vmid (keys %$vmstatus) { | |
90 | next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1); | |
91 | ||
92 | my $data = $vmstatus->{$vmid}; | |
93 | $data->{vmid} = $vmid; | |
94 | push @$res, $data; | |
95 | } | |
96 | ||
97 | return $res; | |
98 | ||
99 | }}); | |
100 | ||
9c2d4ce9 DM |
101 | __PACKAGE__->register_method({ |
102 | name => 'create_vm', | |
103 | path => '', | |
104 | method => 'POST', | |
105 | description => "Create or restore a container.", | |
106 | permissions => { | |
107 | user => 'all', # check inside | |
108 | description => "You need 'VM.Allocate' permissions on /vms/{vmid} or on the VM pool /pool/{pool}. " . | |
109 | "For restore, it is enough if the user has 'VM.Backup' permission and the VM already exists. " . | |
110 | "You also need 'Datastore.AllocateSpace' permissions on the storage.", | |
111 | }, | |
112 | protected => 1, | |
113 | proxyto => 'node', | |
114 | parameters => { | |
115 | additionalProperties => 0, | |
116 | properties => PVE::LXC::json_config_properties({ | |
117 | node => get_standard_option('pve-node'), | |
118 | vmid => get_standard_option('pve-vmid'), | |
119 | ostemplate => { | |
120 | description => "The OS template or backup file.", | |
121 | type => 'string', | |
122 | maxLength => 255, | |
123 | }, | |
124 | password => { | |
125 | optional => 1, | |
126 | type => 'string', | |
127 | description => "Sets root password inside container.", | |
128 | }, | |
129 | storage => get_standard_option('pve-storage-id', { | |
130 | description => "Target storage.", | |
131 | default => 'local', | |
132 | optional => 1, | |
133 | }), | |
134 | force => { | |
135 | optional => 1, | |
136 | type => 'boolean', | |
137 | description => "Allow to overwrite existing container.", | |
138 | }, | |
139 | restore => { | |
140 | optional => 1, | |
141 | type => 'boolean', | |
142 | description => "Mark this as restore task.", | |
143 | }, | |
144 | pool => { | |
145 | optional => 1, | |
146 | type => 'string', format => 'pve-poolid', | |
147 | description => "Add the VM to the specified pool.", | |
148 | }, | |
149 | }), | |
150 | }, | |
151 | returns => { | |
152 | type => 'string', | |
153 | }, | |
154 | code => sub { | |
155 | my ($param) = @_; | |
156 | ||
157 | my $rpcenv = PVE::RPCEnvironment::get(); | |
158 | ||
159 | my $authuser = $rpcenv->get_user(); | |
160 | ||
161 | my $node = extract_param($param, 'node'); | |
162 | ||
163 | my $vmid = extract_param($param, 'vmid'); | |
164 | ||
165 | my $basecfg_fn = PVE::LXC::config_file($vmid); | |
166 | ||
167 | my $same_container_exists = -f $basecfg_fn; | |
168 | ||
169 | my $restore = extract_param($param, 'restore'); | |
170 | ||
171 | my $force = extract_param($param, 'force'); | |
172 | ||
173 | if (!($same_container_exists && $restore && $force)) { | |
174 | PVE::Cluster::check_vmid_unused($vmid); | |
175 | } | |
176 | ||
177 | my $password = extract_param($param, 'password'); | |
178 | ||
179 | my $storage = extract_param($param, 'storage') || 'local'; | |
180 | ||
181 | my $pool = extract_param($param, 'pool'); | |
182 | ||
183 | my $storage_cfg = cfs_read_file("storage.cfg"); | |
184 | ||
185 | my $scfg = PVE::Storage::storage_check_node($storage_cfg, $storage, $node); | |
186 | ||
187 | raise_param_exc({ storage => "storage '$storage' does not support container root directories"}) | |
188 | if !$scfg->{content}->{rootdir}; | |
189 | ||
190 | my $private = PVE::Storage::get_private_dir($storage_cfg, $storage, $vmid); | |
191 | ||
192 | if (defined($pool)) { | |
193 | $rpcenv->check_pool_exist($pool); | |
194 | $rpcenv->check_perm_modify($authuser, "/pool/$pool"); | |
195 | } | |
196 | ||
197 | if ($rpcenv->check($authuser, "/vms/$vmid", ['VM.Allocate'], 1)) { | |
198 | # OK | |
199 | } elsif ($pool && $rpcenv->check($authuser, "/pool/$pool", ['VM.Allocate'], 1)) { | |
200 | # OK | |
201 | } elsif ($restore && $force && $same_container_exists && | |
202 | $rpcenv->check($authuser, "/vms/$vmid", ['VM.Backup'], 1)) { | |
203 | # OK: user has VM.Backup permissions, and want to restore an existing VM | |
204 | } else { | |
205 | raise_perm_exc(); | |
206 | } | |
207 | ||
208 | &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]); | |
209 | ||
210 | PVE::Storage::activate_storage($storage_cfg, $storage); | |
211 | ||
212 | my $ostemplate = extract_param($param, 'ostemplate'); | |
213 | ||
214 | my $archive; | |
215 | ||
216 | if ($ostemplate eq '-') { | |
217 | die "archive pipe not implemented\n" | |
218 | # $archive = '-'; | |
219 | } else { | |
220 | $rpcenv->check_volume_access($authuser, $storage_cfg, $vmid, $ostemplate); | |
221 | $archive = PVE::Storage::abs_filesystem_path($storage_cfg, $ostemplate); | |
222 | } | |
223 | ||
224 | my $memory = $param->{memory} || 512; | |
225 | my $hostname = $param->{hostname} || "T$vmid"; | |
226 | my $conf = {}; | |
227 | ||
228 | $conf->{'lxc.utsname'} = $param->{hostname} || "CT$vmid"; | |
229 | $conf->{'lxc.cgroup.memory.limit_in_bytes'} = "${memory}M"; | |
230 | ||
231 | my $code = sub { | |
232 | my $temp_conf_fn = PVE::LXC::write_temp_config($vmid, $conf); | |
233 | ||
234 | my $cmd = ['lxc-create', '-f', $temp_conf_fn, '-t', 'pve', '-n', $vmid, | |
235 | '--', '--archive', $archive]; | |
236 | ||
237 | eval { PVE::Tools::run_command($cmd); }; | |
238 | my $err = $@; | |
239 | ||
240 | unlink $temp_conf_fn; | |
241 | ||
242 | die $err if $err; | |
243 | }; | |
244 | ||
245 | my $realcmd = sub { PVE::LXC::lock_container($vmid, 1, $code); }; | |
246 | ||
247 | return $rpcenv->fork_worker($param->{restore} ? 'vzrestore' : 'vzcreate', | |
248 | $vmid, $authuser, $realcmd); | |
249 | ||
250 | }}); | |
251 | ||
f76a2828 DM |
252 | my $vm_config_perm_list = [ |
253 | 'VM.Config.Disk', | |
254 | 'VM.Config.CPU', | |
255 | 'VM.Config.Memory', | |
256 | 'VM.Config.Network', | |
257 | 'VM.Config.Options', | |
258 | ]; | |
259 | ||
260 | __PACKAGE__->register_method({ | |
261 | name => 'update_vm', | |
262 | path => '{vmid}/config', | |
263 | method => 'PUT', | |
264 | protected => 1, | |
265 | proxyto => 'node', | |
266 | description => "Set container options.", | |
267 | permissions => { | |
268 | check => ['perm', '/vms/{vmid}', $vm_config_perm_list, any => 1], | |
269 | }, | |
270 | parameters => { | |
271 | additionalProperties => 0, | |
272 | properties => PVE::LXC::json_config_properties( | |
273 | { | |
274 | node => get_standard_option('pve-node'), | |
275 | vmid => get_standard_option('pve-vmid'), | |
ec52ac21 DM |
276 | delete => { |
277 | type => 'string', format => 'pve-configid-list', | |
278 | description => "A list of settings you want to delete.", | |
279 | optional => 1, | |
280 | }, | |
f76a2828 DM |
281 | digest => { |
282 | type => 'string', | |
283 | description => 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.', | |
284 | maxLength => 40, | |
285 | optional => 1, | |
286 | } | |
287 | }), | |
288 | }, | |
289 | returns => { type => 'null'}, | |
290 | code => sub { | |
291 | my ($param) = @_; | |
292 | ||
293 | my $rpcenv = PVE::RPCEnvironment::get(); | |
294 | ||
295 | my $authuser = $rpcenv->get_user(); | |
296 | ||
297 | my $node = extract_param($param, 'node'); | |
298 | ||
299 | my $vmid = extract_param($param, 'vmid'); | |
300 | ||
301 | my $digest = extract_param($param, 'digest'); | |
302 | ||
303 | die "no options specified\n" if !scalar(keys %$param); | |
304 | ||
ec52ac21 DM |
305 | my $delete_str = extract_param($param, 'delete'); |
306 | my @delete = PVE::Tools::split_list($delete_str); | |
307 | ||
308 | &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [@delete]); | |
309 | ||
310 | foreach my $opt (@delete) { | |
311 | raise_param_exc({ delete => "you can't use '-$opt' and " . | |
312 | "-delete $opt' at the same time" }) | |
313 | if defined($param->{$opt}); | |
314 | ||
315 | if (!PVE::LXC::option_exists($opt)) { | |
316 | raise_param_exc({ delete => "unknown option '$opt'" }); | |
317 | } | |
318 | } | |
319 | ||
f76a2828 DM |
320 | &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]); |
321 | ||
322 | my $code = sub { | |
323 | ||
324 | my $conf = PVE::LXC::load_config($vmid); | |
325 | ||
326 | PVE::Tools::assert_if_modified($digest, $conf->{digest}); | |
327 | ||
ec52ac21 DM |
328 | # die if running |
329 | ||
330 | foreach my $opt (@delete) { | |
331 | if ($opt eq 'hostname') { | |
332 | die "unable to delete required option '$opt'\n"; | |
7dfc49cc DM |
333 | } elsif ($opt =~ m/^net\d$/) { |
334 | delete $conf->{$opt}; | |
ec52ac21 DM |
335 | } else { |
336 | die "implement me" | |
337 | } | |
338 | } | |
339 | ||
340 | foreach my $opt (keys %$param) { | |
341 | my $value = $param->{$opt}; | |
342 | if ($opt eq 'hostname') { | |
343 | $conf->{'lxc.utsname'} = $value; | |
822de0c3 DM |
344 | } elsif ($opt eq 'memory') { |
345 | $conf->{'lxc.cgroup.memory.limit_in_bytes'} = $value*1024*1024; | |
346 | } elsif ($opt eq 'swap') { | |
347 | $conf->{'lxc.cgroup.memory.memsw.usage_in_bytes'} = $value*1024*1024; | |
348 | } elsif ($opt =~ m/^net(\d+)$/) { | |
7dfc49cc DM |
349 | my $netid = $1; |
350 | my $net = PVE::LXC::parse_lxc_network($value); | |
351 | $net->{'veth.pair'} = "veth${vmid}.$netid"; | |
352 | $conf->{$opt} = $net; | |
ec52ac21 DM |
353 | } else { |
354 | die "implement me" | |
355 | } | |
356 | } | |
357 | ||
358 | PVE::LXC::write_config($vmid, $conf); | |
f76a2828 DM |
359 | }; |
360 | ||
361 | PVE::LXC::lock_container($vmid, undef, $code); | |
362 | ||
363 | return undef; | |
364 | }}); | |
365 | ||
366 | __PACKAGE__->register_method ({ | |
367 | subclass => "PVE::API2::Firewall::CT", | |
368 | path => '{vmid}/firewall', | |
369 | }); | |
370 | ||
371 | __PACKAGE__->register_method({ | |
372 | name => 'vmdiridx', | |
373 | path => '{vmid}', | |
374 | method => 'GET', | |
375 | proxyto => 'node', | |
376 | description => "Directory index", | |
377 | permissions => { | |
378 | user => 'all', | |
379 | }, | |
380 | parameters => { | |
381 | additionalProperties => 0, | |
382 | properties => { | |
383 | node => get_standard_option('pve-node'), | |
384 | vmid => get_standard_option('pve-vmid'), | |
385 | }, | |
386 | }, | |
387 | returns => { | |
388 | type => 'array', | |
389 | items => { | |
390 | type => "object", | |
391 | properties => { | |
392 | subdir => { type => 'string' }, | |
393 | }, | |
394 | }, | |
395 | links => [ { rel => 'child', href => "{subdir}" } ], | |
396 | }, | |
397 | code => sub { | |
398 | my ($param) = @_; | |
399 | ||
400 | # test if VM exists | |
e901d418 | 401 | my $conf = PVE::LXC::load_config($param->{vmid}); |
f76a2828 DM |
402 | |
403 | my $res = [ | |
404 | { subdir => 'config' }, | |
405 | # { subdir => 'status' }, | |
406 | # { subdir => 'vncproxy' }, | |
407 | # { subdir => 'spiceproxy' }, | |
408 | # { subdir => 'migrate' }, | |
409 | # { subdir => 'initlog' }, | |
410 | { subdir => 'rrd' }, | |
411 | { subdir => 'rrddata' }, | |
412 | { subdir => 'firewall' }, | |
413 | ]; | |
414 | ||
415 | return $res; | |
416 | }}); | |
417 | ||
418 | __PACKAGE__->register_method({ | |
419 | name => 'rrd', | |
420 | path => '{vmid}/rrd', | |
421 | method => 'GET', | |
422 | protected => 1, # fixme: can we avoid that? | |
423 | permissions => { | |
424 | check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]], | |
425 | }, | |
426 | description => "Read VM RRD statistics (returns PNG)", | |
427 | parameters => { | |
428 | additionalProperties => 0, | |
429 | properties => { | |
430 | node => get_standard_option('pve-node'), | |
431 | vmid => get_standard_option('pve-vmid'), | |
432 | timeframe => { | |
433 | description => "Specify the time frame you are interested in.", | |
434 | type => 'string', | |
435 | enum => [ 'hour', 'day', 'week', 'month', 'year' ], | |
436 | }, | |
437 | ds => { | |
438 | description => "The list of datasources you want to display.", | |
439 | type => 'string', format => 'pve-configid-list', | |
440 | }, | |
441 | cf => { | |
442 | description => "The RRD consolidation function", | |
443 | type => 'string', | |
444 | enum => [ 'AVERAGE', 'MAX' ], | |
445 | optional => 1, | |
446 | }, | |
447 | }, | |
448 | }, | |
449 | returns => { | |
450 | type => "object", | |
451 | properties => { | |
452 | filename => { type => 'string' }, | |
453 | }, | |
454 | }, | |
455 | code => sub { | |
456 | my ($param) = @_; | |
457 | ||
458 | return PVE::Cluster::create_rrd_graph( | |
459 | "pve2-vm/$param->{vmid}", $param->{timeframe}, | |
460 | $param->{ds}, $param->{cf}); | |
461 | ||
462 | }}); | |
463 | ||
464 | __PACKAGE__->register_method({ | |
465 | name => 'rrddata', | |
466 | path => '{vmid}/rrddata', | |
467 | method => 'GET', | |
468 | protected => 1, # fixme: can we avoid that? | |
469 | permissions => { | |
470 | check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]], | |
471 | }, | |
472 | description => "Read VM RRD statistics", | |
473 | parameters => { | |
474 | additionalProperties => 0, | |
475 | properties => { | |
476 | node => get_standard_option('pve-node'), | |
477 | vmid => get_standard_option('pve-vmid'), | |
478 | timeframe => { | |
479 | description => "Specify the time frame you are interested in.", | |
480 | type => 'string', | |
481 | enum => [ 'hour', 'day', 'week', 'month', 'year' ], | |
482 | }, | |
483 | cf => { | |
484 | description => "The RRD consolidation function", | |
485 | type => 'string', | |
486 | enum => [ 'AVERAGE', 'MAX' ], | |
487 | optional => 1, | |
488 | }, | |
489 | }, | |
490 | }, | |
491 | returns => { | |
492 | type => "array", | |
493 | items => { | |
494 | type => "object", | |
495 | properties => {}, | |
496 | }, | |
497 | }, | |
498 | code => sub { | |
499 | my ($param) = @_; | |
500 | ||
501 | return PVE::Cluster::create_rrd_data( | |
502 | "pve2-vm/$param->{vmid}", $param->{timeframe}, $param->{cf}); | |
503 | }}); | |
504 | ||
505 | ||
506 | __PACKAGE__->register_method({ | |
507 | name => 'vm_config', | |
508 | path => '{vmid}/config', | |
509 | method => 'GET', | |
510 | proxyto => 'node', | |
511 | description => "Get container configuration.", | |
512 | permissions => { | |
513 | check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]], | |
514 | }, | |
515 | parameters => { | |
516 | additionalProperties => 0, | |
517 | properties => { | |
518 | node => get_standard_option('pve-node'), | |
519 | vmid => get_standard_option('pve-vmid'), | |
520 | }, | |
521 | }, | |
522 | returns => { | |
523 | type => "object", | |
524 | properties => { | |
525 | digest => { | |
526 | type => 'string', | |
527 | description => 'SHA1 digest of configuration file. This can be used to prevent concurrent modifications.', | |
528 | } | |
529 | }, | |
530 | }, | |
531 | code => sub { | |
532 | my ($param) = @_; | |
533 | ||
534 | my $lxc_conf = PVE::LXC::load_config($param->{vmid}); | |
535 | ||
536 | # NOTE: we only return selected/converted values | |
537 | ||
538 | my $conf = { digest => $lxc_conf->{digest} }; | |
539 | ||
540 | my $stcfg = PVE::Cluster::cfs_read_file("storage.cfg"); | |
541 | ||
542 | my ($sid, undef, $path) = &$get_container_storage($stcfg, $param->{vmid}, $lxc_conf); | |
543 | $conf->{storage} = $sid || $path; | |
544 | ||
545 | my $properties = PVE::LXC::json_config_properties(); | |
546 | ||
547 | foreach my $k (keys %$properties) { | |
548 | ||
549 | if ($k eq 'description') { | |
550 | if (my $raw = $lxc_conf->{'pve.comment'}) { | |
551 | $conf->{$k} = PVE::Tools::decode_text($raw); | |
552 | } | |
553 | } elsif ($k eq 'hostname') { | |
554 | $conf->{$k} = $lxc_conf->{'lxc.utsname'} if $lxc_conf->{'lxc.utsname'}; | |
822de0c3 DM |
555 | } elsif ($k eq 'memory') { |
556 | if (my $value = $lxc_conf->{'lxc.cgroup.memory.limit_in_bytes'}) { | |
557 | $conf->{$k} = int($value / (1024*1024)); | |
558 | } | |
559 | } elsif ($k eq 'swap') { | |
560 | if (my $value = $lxc_conf->{'lxc.cgroup.memory.memsw.usage_in_bytes'}) { | |
561 | $conf->{$k} = int($value / (1024*1024)); | |
562 | } | |
f76a2828 DM |
563 | } elsif ($k =~ m/^net\d$/) { |
564 | my $net = $lxc_conf->{$k}; | |
565 | next if !$net; | |
7dfc49cc | 566 | $conf->{$k} = PVE::LXC::print_lxc_network($net); |
f76a2828 DM |
567 | } |
568 | } | |
569 | ||
570 | return $conf; | |
571 | }}); | |
572 | ||
573 | __PACKAGE__->register_method({ | |
574 | name => 'destroy_vm', | |
575 | path => '{vmid}', | |
576 | method => 'DELETE', | |
577 | protected => 1, | |
578 | proxyto => 'node', | |
579 | description => "Destroy the container (also delete all uses files).", | |
580 | permissions => { | |
581 | check => [ 'perm', '/vms/{vmid}', ['VM.Allocate']], | |
582 | }, | |
583 | parameters => { | |
584 | additionalProperties => 0, | |
585 | properties => { | |
586 | node => get_standard_option('pve-node'), | |
587 | vmid => get_standard_option('pve-vmid'), | |
588 | }, | |
589 | }, | |
590 | returns => { | |
591 | type => 'string', | |
592 | }, | |
593 | code => sub { | |
594 | my ($param) = @_; | |
595 | ||
596 | my $rpcenv = PVE::RPCEnvironment::get(); | |
597 | ||
598 | my $authuser = $rpcenv->get_user(); | |
599 | ||
600 | my $vmid = $param->{vmid}; | |
601 | ||
602 | # test if container exists | |
603 | my $conf = PVE::LXC::load_config($param->{vmid}); | |
604 | ||
605 | my $realcmd = sub { | |
606 | my $cmd = ['lxc-destroy', '-n', $vmid ]; | |
607 | ||
608 | run_command($cmd); | |
609 | ||
610 | PVE::AccessControl::remove_vm_from_pool($vmid); | |
611 | }; | |
612 | ||
613 | return $rpcenv->fork_worker('vzdestroy', $vmid, $authuser, $realcmd); | |
614 | }}); | |
615 | ||
616 | 1; |