]> git.proxmox.com Git - pve-container.git/blame - src/PVE/API2/LXC.pm
bump version to 0.9-11
[pve-container.git] / src / PVE / API2 / LXC.pm
CommitLineData
f76a2828
DM
1package PVE::API2::LXC;
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;
9c2d4ce9 10use PVE::Cluster qw(cfs_read_file);
f76a2828 11use PVE::AccessControl;
2f9b5ead 12use PVE::Firewall;
f76a2828
DM
13use PVE::Storage;
14use PVE::RESTHandler;
15use PVE::RPCEnvironment;
16use PVE::LXC;
7af97ad5 17use PVE::LXC::Create;
5c752bbf 18use PVE::HA::Config;
f76a2828
DM
19use PVE::JSONSchema qw(get_standard_option);
20use base qw(PVE::RESTHandler);
21
22use Data::Dumper; # fixme: remove
23
f76a2828
DM
24my $check_ct_modify_config_perm = sub {
25 my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_;
5c752bbf 26
f76a2828
DM
27 return 1 if $authuser ne 'root@pam';
28
29 foreach my $opt (@$key_list) {
30
31 if ($opt eq 'cpus' || $opt eq 'cpuunits' || $opt eq 'cpulimit') {
32 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']);
33 } elsif ($opt eq 'disk') {
34 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
35 } elsif ($opt eq 'memory' || $opt eq 'swap') {
36 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']);
5c752bbf 37 } elsif ($opt =~ m/^net\d+$/ || $opt eq 'nameserver' ||
f76a2828
DM
38 $opt eq 'searchdomain' || $opt eq 'hostname') {
39 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
40 } else {
41 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']);
42 }
43 }
44
45 return 1;
46};
47
27916659
DM
48my $alloc_rootfs = sub {
49 my ($storage_conf, $storage, $disk_size_gb, $vmid) = @_;
50
51 my $volid;
52
53 my $size = 4*1024*1024; # defaults to 4G
54
55 $size = int($disk_size_gb*1024) * 1024 if defined($disk_size_gb);
56
57 eval {
58 my $scfg = PVE::Storage::storage_config($storage_conf, $storage);
59 # fixme: use better naming ct-$vmid-disk-X.raw?
60
61 if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs') {
62 if ($size > 0) {
63 $volid = PVE::Storage::vdisk_alloc($storage_conf, $storage, $vmid, 'raw',
3d8cbc23 64 undef, $size);
27916659
DM
65 } else {
66 $volid = PVE::Storage::vdisk_alloc($storage_conf, $storage, $vmid, 'subvol',
3d8cbc23 67 undef, 0);
27916659
DM
68 }
69 } elsif ($scfg->{type} eq 'zfspool') {
70
71 $volid = PVE::Storage::vdisk_alloc($storage_conf, $storage, $vmid, 'subvol',
3d8cbc23 72 undef, $size);
644f2f8f
DM
73 } elsif ($scfg->{type} eq 'drbd') {
74
75 $volid = PVE::Storage::vdisk_alloc($storage_conf, $storage, $vmid, 'raw', undef, $size);
63f2ddfb
AD
76
77 } elsif ($scfg->{type} eq 'rbd') {
78
79 die "krbd option must be enabled on storage type '$scfg->{type}'\n" if !$scfg->{krbd};
80 $volid = PVE::Storage::vdisk_alloc($storage_conf, $storage, $vmid, 'raw', undef, $size);
81
27916659
DM
82 } else {
83 die "unable to create containers on storage type '$scfg->{type}'\n";
84 }
85 };
86 if (my $err = $@) {
87 # cleanup
88 eval { PVE::Storage::vdisk_free($storage_conf, $volid) if $volid; };
89 warn $@ if $@;
90 die $err;
91 }
92
93 return $volid;
94};
95
489e960d
WL
96PVE::JSONSchema::register_standard_option('pve-lxc-snapshot-name', {
97 description => "The name of the snapshot.",
98 type => 'string', format => 'pve-configid',
99 maxLength => 40,
100});
f76a2828
DM
101
102__PACKAGE__->register_method({
5c752bbf
DM
103 name => 'vmlist',
104 path => '',
f76a2828
DM
105 method => 'GET',
106 description => "LXC container index (per node).",
107 permissions => {
108 description => "Only list CTs where you have VM.Audit permissons on /vms/<vmid>.",
109 user => 'all',
110 },
111 proxyto => 'node',
112 protected => 1, # /proc files are only readable by root
113 parameters => {
114 additionalProperties => 0,
115 properties => {
116 node => get_standard_option('pve-node'),
117 },
118 },
119 returns => {
120 type => 'array',
121 items => {
122 type => "object",
123 properties => {},
124 },
125 links => [ { rel => 'child', href => "{vmid}" } ],
126 },
127 code => sub {
128 my ($param) = @_;
129
130 my $rpcenv = PVE::RPCEnvironment::get();
131 my $authuser = $rpcenv->get_user();
132
133 my $vmstatus = PVE::LXC::vmstatus();
134
135 my $res = [];
136 foreach my $vmid (keys %$vmstatus) {
137 next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
138
139 my $data = $vmstatus->{$vmid};
140 $data->{vmid} = $vmid;
141 push @$res, $data;
142 }
143
144 return $res;
5c752bbf 145
f76a2828
DM
146 }});
147
9c2d4ce9 148__PACKAGE__->register_method({
5c752bbf
DM
149 name => 'create_vm',
150 path => '',
9c2d4ce9
DM
151 method => 'POST',
152 description => "Create or restore a container.",
153 permissions => {
154 user => 'all', # check inside
155 description => "You need 'VM.Allocate' permissions on /vms/{vmid} or on the VM pool /pool/{pool}. " .
156 "For restore, it is enough if the user has 'VM.Backup' permission and the VM already exists. " .
157 "You also need 'Datastore.AllocateSpace' permissions on the storage.",
158 },
159 protected => 1,
160 proxyto => 'node',
161 parameters => {
162 additionalProperties => 0,
27916659 163 properties => PVE::LXC::json_config_properties_no_rootfs({
9c2d4ce9
DM
164 node => get_standard_option('pve-node'),
165 vmid => get_standard_option('pve-vmid'),
166 ostemplate => {
167 description => "The OS template or backup file.",
5c752bbf 168 type => 'string',
9c2d4ce9
DM
169 maxLength => 255,
170 },
5c752bbf
DM
171 password => {
172 optional => 1,
9c2d4ce9
DM
173 type => 'string',
174 description => "Sets root password inside container.",
168d6b07 175 minLength => 5,
9c2d4ce9
DM
176 },
177 storage => get_standard_option('pve-storage-id', {
178 description => "Target storage.",
179 default => 'local',
180 optional => 1,
181 }),
27916659
DM
182 size => {
183 optional => 1,
184 type => 'number',
185 description => "Amount of disk space for the VM in GB. A zero indicates no limits.",
186 minimum => 0,
187 default => 4,
188 },
9c2d4ce9 189 force => {
5c752bbf 190 optional => 1,
9c2d4ce9
DM
191 type => 'boolean',
192 description => "Allow to overwrite existing container.",
193 },
194 restore => {
5c752bbf 195 optional => 1,
9c2d4ce9
DM
196 type => 'boolean',
197 description => "Mark this as restore task.",
198 },
5c752bbf 199 pool => {
9c2d4ce9
DM
200 optional => 1,
201 type => 'string', format => 'pve-poolid',
202 description => "Add the VM to the specified pool.",
203 },
204 }),
205 },
5c752bbf 206 returns => {
9c2d4ce9
DM
207 type => 'string',
208 },
209 code => sub {
210 my ($param) = @_;
211
212 my $rpcenv = PVE::RPCEnvironment::get();
213
214 my $authuser = $rpcenv->get_user();
215
216 my $node = extract_param($param, 'node');
217
218 my $vmid = extract_param($param, 'vmid');
219
220 my $basecfg_fn = PVE::LXC::config_file($vmid);
221
222 my $same_container_exists = -f $basecfg_fn;
223
224 my $restore = extract_param($param, 'restore');
225
148d1cb4
DM
226 if ($restore) {
227 # fixme: limit allowed parameters
228
229 }
230
9c2d4ce9
DM
231 my $force = extract_param($param, 'force');
232
233 if (!($same_container_exists && $restore && $force)) {
234 PVE::Cluster::check_vmid_unused($vmid);
235 }
5c752bbf 236
9c2d4ce9
DM
237 my $password = extract_param($param, 'password');
238
27916659 239 my $disksize = extract_param($param, 'size');
5c752bbf 240
27916659
DM
241 my $storage = extract_param($param, 'storage') // 'local';
242
9c2d4ce9
DM
243 my $storage_cfg = cfs_read_file("storage.cfg");
244
245 my $scfg = PVE::Storage::storage_check_node($storage_cfg, $storage, $node);
246
247 raise_param_exc({ storage => "storage '$storage' does not support container root directories"})
644f2f8f 248 if !($scfg->{content}->{images} || $scfg->{content}->{rootdir});
9c2d4ce9 249
27916659
DM
250 my $pool = extract_param($param, 'pool');
251
9c2d4ce9
DM
252 if (defined($pool)) {
253 $rpcenv->check_pool_exist($pool);
254 $rpcenv->check_perm_modify($authuser, "/pool/$pool");
5c752bbf 255 }
9c2d4ce9 256
27916659
DM
257 $rpcenv->check($authuser, "/storage/$storage", ['Datastore.AllocateSpace']);
258
9c2d4ce9
DM
259 if ($rpcenv->check($authuser, "/vms/$vmid", ['VM.Allocate'], 1)) {
260 # OK
261 } elsif ($pool && $rpcenv->check($authuser, "/pool/$pool", ['VM.Allocate'], 1)) {
262 # OK
263 } elsif ($restore && $force && $same_container_exists &&
264 $rpcenv->check($authuser, "/vms/$vmid", ['VM.Backup'], 1)) {
265 # OK: user has VM.Backup permissions, and want to restore an existing VM
266 } else {
267 raise_perm_exc();
268 }
269
270 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]);
271
272 PVE::Storage::activate_storage($storage_cfg, $storage);
273
274 my $ostemplate = extract_param($param, 'ostemplate');
5c752bbf 275
9c2d4ce9
DM
276 my $archive;
277
278 if ($ostemplate eq '-') {
148d1cb4
DM
279 die "pipe requires cli environment\n"
280 if $rpcenv->{type} ne 'cli';
281 die "pipe can only be used with restore tasks\n"
282 if !$restore;
27916659
DM
283 die "pipe requires --size parameter\n"
284 if !defined($disksize);
148d1cb4 285 $archive = '-';
9c2d4ce9
DM
286 } else {
287 $rpcenv->check_volume_access($authuser, $storage_cfg, $vmid, $ostemplate);
288 $archive = PVE::Storage::abs_filesystem_path($storage_cfg, $ostemplate);
289 }
290
9c2d4ce9 291 my $conf = {};
5b4657d0 292
27916659 293 PVE::LXC::update_pct_config($vmid, $conf, 0, $param);
9c2d4ce9 294
148d1cb4
DM
295 my $check_vmid_usage = sub {
296 if ($force) {
297 die "cant overwrite running container\n"
298 if PVE::LXC::check_running($vmid);
299 } else {
300 PVE::Cluster::check_vmid_unused($vmid);
301 }
302 };
f507c3a7 303
5b4657d0 304 my $code = sub {
148d1cb4 305 &$check_vmid_usage(); # final check after locking
87273b2b 306
148d1cb4
DM
307 PVE::Cluster::check_cfs_quorum();
308
27916659
DM
309 my $volid;
310
311 eval {
312 if (!defined($disksize)) {
313 if ($restore) {
7af97ad5 314 (undef, $disksize) = PVE::LXC::Create::recover_config($archive);
27916659
DM
315 die "unable to detect disk size - please specify with --size\n"
316 if !$disksize;
317 } else {
318 $disksize = 4;
319 }
320 }
321 $volid = &$alloc_rootfs($storage_cfg, $storage, $disksize, $vmid);
322
7af97ad5
DM
323 PVE::LXC::Create::create_rootfs($storage_cfg, $storage, $volid, $vmid, $conf,
324 $archive, $password, $restore);
27916659 325
dde7b02b 326 $conf->{rootfs} = PVE::LXC::print_ct_mountpoint({volume => $volid, size => $disksize });
27916659
DM
327
328 # set some defaults
329 $conf->{hostname} ||= "CT$vmid";
330 $conf->{memory} ||= 512;
331 $conf->{swap} //= 512;
332
333 PVE::LXC::create_config($vmid, $conf);
334 };
335 if (my $err = $@) {
336 eval { PVE::Storage::vdisk_free($storage_cfg, $volid) if $volid; };
337 warn $@ if $@;
338 PVE::LXC::destroy_config($vmid);
339 die $err;
6d098bf4 340 }
87273b2b 341 PVE::AccessControl::add_vm_to_pool($vmid, $pool) if $pool;
9c2d4ce9 342 };
5c752bbf 343
9c2d4ce9
DM
344 my $realcmd = sub { PVE::LXC::lock_container($vmid, 1, $code); };
345
148d1cb4
DM
346 &$check_vmid_usage(); # first check before locking
347
348 return $rpcenv->fork_worker($restore ? 'vzrestore' : 'vzcreate',
9c2d4ce9 349 $vmid, $authuser, $realcmd);
5c752bbf 350
9c2d4ce9
DM
351 }});
352
f76a2828 353my $vm_config_perm_list = [
5c752bbf
DM
354 'VM.Config.Disk',
355 'VM.Config.CPU',
356 'VM.Config.Memory',
357 'VM.Config.Network',
f76a2828
DM
358 'VM.Config.Options',
359 ];
360
361__PACKAGE__->register_method({
5c752bbf
DM
362 name => 'update_vm',
363 path => '{vmid}/config',
f76a2828
DM
364 method => 'PUT',
365 protected => 1,
366 proxyto => 'node',
367 description => "Set container options.",
368 permissions => {
369 check => ['perm', '/vms/{vmid}', $vm_config_perm_list, any => 1],
370 },
371 parameters => {
372 additionalProperties => 0,
373 properties => PVE::LXC::json_config_properties(
374 {
375 node => get_standard_option('pve-node'),
376 vmid => get_standard_option('pve-vmid'),
ec52ac21
DM
377 delete => {
378 type => 'string', format => 'pve-configid-list',
379 description => "A list of settings you want to delete.",
380 optional => 1,
381 },
f76a2828
DM
382 digest => {
383 type => 'string',
384 description => 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
385 maxLength => 40,
5c752bbf 386 optional => 1,
f76a2828
DM
387 }
388 }),
389 },
390 returns => { type => 'null'},
391 code => sub {
392 my ($param) = @_;
393
394 my $rpcenv = PVE::RPCEnvironment::get();
395
396 my $authuser = $rpcenv->get_user();
397
398 my $node = extract_param($param, 'node');
399
400 my $vmid = extract_param($param, 'vmid');
401
402 my $digest = extract_param($param, 'digest');
403
404 die "no options specified\n" if !scalar(keys %$param);
405
ec52ac21
DM
406 my $delete_str = extract_param($param, 'delete');
407 my @delete = PVE::Tools::split_list($delete_str);
5c752bbf 408
ec52ac21 409 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [@delete]);
5c752bbf 410
ec52ac21
DM
411 foreach my $opt (@delete) {
412 raise_param_exc({ delete => "you can't use '-$opt' and " .
413 "-delete $opt' at the same time" })
414 if defined($param->{$opt});
5c752bbf 415
ec52ac21
DM
416 if (!PVE::LXC::option_exists($opt)) {
417 raise_param_exc({ delete => "unknown option '$opt'" });
418 }
419 }
420
f76a2828
DM
421 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]);
422
27916659
DM
423 my $storage_cfg = cfs_read_file("storage.cfg");
424
f76a2828
DM
425 my $code = sub {
426
427 my $conf = PVE::LXC::load_config($vmid);
673cf209 428 PVE::LXC::check_lock($conf);
f76a2828
DM
429
430 PVE::Tools::assert_if_modified($digest, $conf->{digest});
431
93285df8 432 my $running = PVE::LXC::check_running($vmid);
ec52ac21 433
27916659 434 PVE::LXC::update_pct_config($vmid, $conf, $running, $param, \@delete);
ec52ac21
DM
435
436 PVE::LXC::write_config($vmid, $conf);
27916659 437 PVE::LXC::update_lxc_config($storage_cfg, $vmid, $conf);
f76a2828
DM
438 };
439
440 PVE::LXC::lock_container($vmid, undef, $code);
441
442 return undef;
443 }});
444
445__PACKAGE__->register_method ({
5c752bbf 446 subclass => "PVE::API2::Firewall::CT",
f76a2828
DM
447 path => '{vmid}/firewall',
448});
449
450__PACKAGE__->register_method({
451 name => 'vmdiridx',
5c752bbf 452 path => '{vmid}',
f76a2828
DM
453 method => 'GET',
454 proxyto => 'node',
455 description => "Directory index",
456 permissions => {
457 user => 'all',
458 },
459 parameters => {
460 additionalProperties => 0,
461 properties => {
462 node => get_standard_option('pve-node'),
463 vmid => get_standard_option('pve-vmid'),
464 },
465 },
466 returns => {
467 type => 'array',
468 items => {
469 type => "object",
470 properties => {
471 subdir => { type => 'string' },
472 },
473 },
474 links => [ { rel => 'child', href => "{subdir}" } ],
475 },
476 code => sub {
477 my ($param) = @_;
478
479 # test if VM exists
e901d418 480 my $conf = PVE::LXC::load_config($param->{vmid});
f76a2828
DM
481
482 my $res = [
483 { subdir => 'config' },
fff3a342
DM
484 { subdir => 'status' },
485 { subdir => 'vncproxy' },
486 { subdir => 'vncwebsocket' },
487 { subdir => 'spiceproxy' },
488 { subdir => 'migrate' },
f76a2828
DM
489# { subdir => 'initlog' },
490 { subdir => 'rrd' },
491 { subdir => 'rrddata' },
492 { subdir => 'firewall' },
cc5392c8 493 { subdir => 'snapshot' },
f76a2828 494 ];
5c752bbf 495
f76a2828
DM
496 return $res;
497 }});
498
499__PACKAGE__->register_method({
5c752bbf
DM
500 name => 'rrd',
501 path => '{vmid}/rrd',
f76a2828
DM
502 method => 'GET',
503 protected => 1, # fixme: can we avoid that?
504 permissions => {
505 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
506 },
507 description => "Read VM RRD statistics (returns PNG)",
508 parameters => {
509 additionalProperties => 0,
510 properties => {
511 node => get_standard_option('pve-node'),
512 vmid => get_standard_option('pve-vmid'),
513 timeframe => {
514 description => "Specify the time frame you are interested in.",
515 type => 'string',
516 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
517 },
518 ds => {
519 description => "The list of datasources you want to display.",
520 type => 'string', format => 'pve-configid-list',
521 },
522 cf => {
523 description => "The RRD consolidation function",
524 type => 'string',
525 enum => [ 'AVERAGE', 'MAX' ],
526 optional => 1,
527 },
528 },
529 },
530 returns => {
531 type => "object",
532 properties => {
533 filename => { type => 'string' },
534 },
535 },
536 code => sub {
537 my ($param) = @_;
538
539 return PVE::Cluster::create_rrd_graph(
5c752bbf 540 "pve2-vm/$param->{vmid}", $param->{timeframe},
f76a2828 541 $param->{ds}, $param->{cf});
5c752bbf 542
f76a2828
DM
543 }});
544
545__PACKAGE__->register_method({
5c752bbf
DM
546 name => 'rrddata',
547 path => '{vmid}/rrddata',
f76a2828
DM
548 method => 'GET',
549 protected => 1, # fixme: can we avoid that?
550 permissions => {
551 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
552 },
553 description => "Read VM RRD statistics",
554 parameters => {
555 additionalProperties => 0,
556 properties => {
557 node => get_standard_option('pve-node'),
558 vmid => get_standard_option('pve-vmid'),
559 timeframe => {
560 description => "Specify the time frame you are interested in.",
561 type => 'string',
562 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
563 },
564 cf => {
565 description => "The RRD consolidation function",
566 type => 'string',
567 enum => [ 'AVERAGE', 'MAX' ],
568 optional => 1,
569 },
570 },
571 },
572 returns => {
573 type => "array",
574 items => {
575 type => "object",
576 properties => {},
577 },
578 },
579 code => sub {
580 my ($param) = @_;
581
582 return PVE::Cluster::create_rrd_data(
583 "pve2-vm/$param->{vmid}", $param->{timeframe}, $param->{cf});
584 }});
585
586
587__PACKAGE__->register_method({
5c752bbf
DM
588 name => 'vm_config',
589 path => '{vmid}/config',
f76a2828
DM
590 method => 'GET',
591 proxyto => 'node',
592 description => "Get container configuration.",
593 permissions => {
594 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
595 },
596 parameters => {
597 additionalProperties => 0,
598 properties => {
599 node => get_standard_option('pve-node'),
600 vmid => get_standard_option('pve-vmid'),
601 },
602 },
5c752bbf 603 returns => {
f76a2828
DM
604 type => "object",
605 properties => {
606 digest => {
607 type => 'string',
608 description => 'SHA1 digest of configuration file. This can be used to prevent concurrent modifications.',
609 }
610 },
611 },
612 code => sub {
613 my ($param) = @_;
614
27916659 615 my $conf = PVE::LXC::load_config($param->{vmid});
f76a2828 616
27916659 617 delete $conf->{snapshots};
e576f689 618 delete $conf->{lxc};
f76a2828 619
f76a2828
DM
620 return $conf;
621 }});
622
623__PACKAGE__->register_method({
5c752bbf
DM
624 name => 'destroy_vm',
625 path => '{vmid}',
f76a2828
DM
626 method => 'DELETE',
627 protected => 1,
628 proxyto => 'node',
629 description => "Destroy the container (also delete all uses files).",
630 permissions => {
631 check => [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
632 },
633 parameters => {
634 additionalProperties => 0,
635 properties => {
636 node => get_standard_option('pve-node'),
637 vmid => get_standard_option('pve-vmid'),
638 },
639 },
5c752bbf 640 returns => {
f76a2828
DM
641 type => 'string',
642 },
643 code => sub {
644 my ($param) = @_;
645
646 my $rpcenv = PVE::RPCEnvironment::get();
647
648 my $authuser = $rpcenv->get_user();
649
650 my $vmid = $param->{vmid};
651
652 # test if container exists
673cf209 653 my $conf = PVE::LXC::load_config($vmid);
f76a2828 654
611fe3aa
DM
655 my $storage_cfg = cfs_read_file("storage.cfg");
656
657 my $code = sub {
673cf209
DM
658 # reload config after lock
659 $conf = PVE::LXC::load_config($vmid);
660 PVE::LXC::check_lock($conf);
661
27916659 662 PVE::LXC::destroy_lxc_container($storage_cfg, $vmid, $conf);
be5fc936 663 PVE::AccessControl::remove_vm_access($vmid);
2f9b5ead 664 PVE::Firewall::remove_vmfw_conf($vmid);
f76a2828
DM
665 };
666
611fe3aa
DM
667 my $realcmd = sub { PVE::LXC::lock_container($vmid, 1, $code); };
668
f76a2828
DM
669 return $rpcenv->fork_worker('vzdestroy', $vmid, $authuser, $realcmd);
670 }});
671
fff3a342
DM
672my $sslcert;
673
674__PACKAGE__->register_method ({
5b4657d0
DM
675 name => 'vncproxy',
676 path => '{vmid}/vncproxy',
fff3a342
DM
677 method => 'POST',
678 protected => 1,
679 permissions => {
680 check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
681 },
682 description => "Creates a TCP VNC proxy connections.",
683 parameters => {
684 additionalProperties => 0,
685 properties => {
686 node => get_standard_option('pve-node'),
687 vmid => get_standard_option('pve-vmid'),
688 websocket => {
689 optional => 1,
690 type => 'boolean',
691 description => "use websocket instead of standard VNC.",
692 },
693 },
694 },
5b4657d0 695 returns => {
fff3a342
DM
696 additionalProperties => 0,
697 properties => {
698 user => { type => 'string' },
699 ticket => { type => 'string' },
700 cert => { type => 'string' },
701 port => { type => 'integer' },
702 upid => { type => 'string' },
703 },
704 },
705 code => sub {
706 my ($param) = @_;
707
708 my $rpcenv = PVE::RPCEnvironment::get();
709
710 my $authuser = $rpcenv->get_user();
711
712 my $vmid = $param->{vmid};
713 my $node = $param->{node};
714
715 my $authpath = "/vms/$vmid";
716
717 my $ticket = PVE::AccessControl::assemble_vnc_ticket($authuser, $authpath);
718
719 $sslcert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192)
720 if !$sslcert;
721
ec2c57eb 722 my ($remip, $family);
5b4657d0 723
fff3a342 724 if ($node ne PVE::INotify::nodename()) {
85ae6211 725 ($remip, $family) = PVE::Cluster::remote_node_ip($node);
ec2c57eb
WB
726 } else {
727 $family = PVE::Tools::get_host_address_family($node);
fff3a342
DM
728 }
729
ec2c57eb
WB
730 my $port = PVE::Tools::next_vnc_port($family);
731
fff3a342
DM
732 # NOTE: vncterm VNC traffic is already TLS encrypted,
733 # so we select the fastest chipher here (or 'none'?)
5b4657d0 734 my $remcmd = $remip ?
fff3a342
DM
735 ['/usr/bin/ssh', '-t', $remip] : [];
736
aca816ad
DM
737 my $conf = PVE::LXC::load_config($vmid);
738 my $concmd = PVE::LXC::get_console_command($vmid, $conf);
739
5b4657d0
DM
740 my $shcmd = [ '/usr/bin/dtach', '-A',
741 "/var/run/dtach/vzctlconsole$vmid",
aca816ad 742 '-r', 'winch', '-z', @$concmd];
fff3a342
DM
743
744 my $realcmd = sub {
745 my $upid = shift;
746
5b4657d0 747 syslog ('info', "starting lxc vnc proxy $upid\n");
fff3a342 748
5b4657d0 749 my $timeout = 10;
fff3a342
DM
750
751 my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
5b4657d0 752 '-timeout', $timeout, '-authpath', $authpath,
fff3a342
DM
753 '-perm', 'VM.Console'];
754
755 if ($param->{websocket}) {
5b4657d0 756 $ENV{PVE_VNC_TICKET} = $ticket; # pass ticket to vncterm
fff3a342
DM
757 push @$cmd, '-notls', '-listen', 'localhost';
758 }
759
760 push @$cmd, '-c', @$remcmd, @$shcmd;
761
762 run_command($cmd);
763
764 return;
765 };
766
767 my $upid = $rpcenv->fork_worker('vncproxy', $vmid, $authuser, $realcmd);
768
769 PVE::Tools::wait_for_vnc_port($port);
770
771 return {
772 user => $authuser,
773 ticket => $ticket,
5b4657d0
DM
774 port => $port,
775 upid => $upid,
776 cert => $sslcert,
fff3a342
DM
777 };
778 }});
779
780__PACKAGE__->register_method({
781 name => 'vncwebsocket',
782 path => '{vmid}/vncwebsocket',
783 method => 'GET',
5b4657d0 784 permissions => {
fff3a342
DM
785 description => "You also need to pass a valid ticket (vncticket).",
786 check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
787 },
788 description => "Opens a weksocket for VNC traffic.",
789 parameters => {
790 additionalProperties => 0,
791 properties => {
792 node => get_standard_option('pve-node'),
793 vmid => get_standard_option('pve-vmid'),
794 vncticket => {
795 description => "Ticket from previous call to vncproxy.",
796 type => 'string',
797 maxLength => 512,
798 },
799 port => {
800 description => "Port number returned by previous vncproxy call.",
801 type => 'integer',
802 minimum => 5900,
803 maximum => 5999,
804 },
805 },
806 },
807 returns => {
808 type => "object",
809 properties => {
810 port => { type => 'string' },
811 },
812 },
813 code => sub {
814 my ($param) = @_;
815
816 my $rpcenv = PVE::RPCEnvironment::get();
817
818 my $authuser = $rpcenv->get_user();
819
820 my $authpath = "/vms/$param->{vmid}";
821
822 PVE::AccessControl::verify_vnc_ticket($param->{vncticket}, $authuser, $authpath);
823
824 my $port = $param->{port};
5b4657d0 825
fff3a342
DM
826 return { port => $port };
827 }});
828
829__PACKAGE__->register_method ({
5b4657d0
DM
830 name => 'spiceproxy',
831 path => '{vmid}/spiceproxy',
fff3a342
DM
832 method => 'POST',
833 protected => 1,
834 proxyto => 'node',
835 permissions => {
836 check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
837 },
838 description => "Returns a SPICE configuration to connect to the CT.",
839 parameters => {
840 additionalProperties => 0,
841 properties => {
842 node => get_standard_option('pve-node'),
843 vmid => get_standard_option('pve-vmid'),
844 proxy => get_standard_option('spice-proxy', { optional => 1 }),
845 },
846 },
847 returns => get_standard_option('remote-viewer-config'),
848 code => sub {
849 my ($param) = @_;
850
851 my $vmid = $param->{vmid};
852 my $node = $param->{node};
853 my $proxy = $param->{proxy};
854
855 my $authpath = "/vms/$vmid";
856 my $permissions = 'VM.Console';
857
aca816ad
DM
858 my $conf = PVE::LXC::load_config($vmid);
859 my $concmd = PVE::LXC::get_console_command($vmid, $conf);
860
5b4657d0
DM
861 my $shcmd = ['/usr/bin/dtach', '-A',
862 "/var/run/dtach/vzctlconsole$vmid",
aca816ad 863 '-r', 'winch', '-z', @$concmd];
fff3a342
DM
864
865 my $title = "CT $vmid";
866
867 return PVE::API2Tools::run_spiceterm($authpath, $permissions, $vmid, $node, $proxy, $title, $shcmd);
868 }});
5c752bbf
DM
869
870__PACKAGE__->register_method({
871 name => 'vmcmdidx',
872 path => '{vmid}/status',
873 method => 'GET',
874 proxyto => 'node',
875 description => "Directory index",
876 permissions => {
877 user => 'all',
878 },
879 parameters => {
880 additionalProperties => 0,
881 properties => {
882 node => get_standard_option('pve-node'),
883 vmid => get_standard_option('pve-vmid'),
884 },
885 },
886 returns => {
887 type => 'array',
888 items => {
889 type => "object",
890 properties => {
891 subdir => { type => 'string' },
892 },
893 },
894 links => [ { rel => 'child', href => "{subdir}" } ],
895 },
896 code => sub {
897 my ($param) = @_;
898
899 # test if VM exists
a468f934 900 my $conf = PVE::LXC::load_config($param->{vmid});
5c752bbf
DM
901
902 my $res = [
903 { subdir => 'current' },
904 { subdir => 'start' },
905 { subdir => 'stop' },
906 { subdir => 'shutdown' },
907 { subdir => 'migrate' },
908 ];
909
910 return $res;
911 }});
912
913__PACKAGE__->register_method({
914 name => 'vm_status',
915 path => '{vmid}/status/current',
916 method => 'GET',
917 proxyto => 'node',
918 protected => 1, # openvz /proc entries are only readable by root
919 description => "Get virtual machine status.",
920 permissions => {
921 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
922 },
923 parameters => {
924 additionalProperties => 0,
925 properties => {
926 node => get_standard_option('pve-node'),
927 vmid => get_standard_option('pve-vmid'),
928 },
929 },
930 returns => { type => 'object' },
931 code => sub {
932 my ($param) = @_;
933
934 # test if VM exists
935 my $conf = PVE::LXC::load_config($param->{vmid});
936
937 my $vmstatus = PVE::LXC::vmstatus($param->{vmid});
938 my $status = $vmstatus->{$param->{vmid}};
939
940 $status->{ha} = PVE::HA::Config::vm_is_ha_managed($param->{vmid}) ? 1 : 0;
941
942 return $status;
943 }});
944
945__PACKAGE__->register_method({
946 name => 'vm_start',
947 path => '{vmid}/status/start',
948 method => 'POST',
949 protected => 1,
950 proxyto => 'node',
951 description => "Start the container.",
952 permissions => {
953 check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
954 },
955 parameters => {
956 additionalProperties => 0,
957 properties => {
958 node => get_standard_option('pve-node'),
959 vmid => get_standard_option('pve-vmid'),
960 },
961 },
962 returns => {
963 type => 'string',
964 },
965 code => sub {
966 my ($param) = @_;
967
968 my $rpcenv = PVE::RPCEnvironment::get();
969
970 my $authuser = $rpcenv->get_user();
971
972 my $node = extract_param($param, 'node');
973
974 my $vmid = extract_param($param, 'vmid');
975
976 die "CT $vmid already running\n" if PVE::LXC::check_running($vmid);
977
978 if (PVE::HA::Config::vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
979
980 my $hacmd = sub {
981 my $upid = shift;
982
983 my $service = "ct:$vmid";
984
985 my $cmd = ['ha-manager', 'enable', $service];
986
987 print "Executing HA start for CT $vmid\n";
988
989 PVE::Tools::run_command($cmd);
990
991 return;
992 };
993
994 return $rpcenv->fork_worker('hastart', $vmid, $authuser, $hacmd);
995
996 } else {
997
998 my $realcmd = sub {
999 my $upid = shift;
1000
1001 syslog('info', "starting CT $vmid: $upid\n");
1002
1003 my $conf = PVE::LXC::load_config($vmid);
bb1ac2de
DM
1004
1005 die "you can't start a CT if it's a template\n"
1006 if PVE::LXC::is_template($conf);
1007
27916659 1008 my $storage_cfg = cfs_read_file("storage.cfg");
5c752bbf 1009
27916659
DM
1010 PVE::LXC::update_lxc_config($storage_cfg, $vmid, $conf);
1011
22b9057a 1012 my $cmd = ['lxc-start', '-n', $vmid];
5c752bbf 1013
22b9057a 1014 run_command($cmd);
5c752bbf
DM
1015
1016 return;
1017 };
1018
1019 return $rpcenv->fork_worker('vzstart', $vmid, $authuser, $realcmd);
1020 }
1021 }});
1022
1023__PACKAGE__->register_method({
1024 name => 'vm_stop',
1025 path => '{vmid}/status/stop',
1026 method => 'POST',
1027 protected => 1,
1028 proxyto => 'node',
1029 description => "Stop the container.",
1030 permissions => {
1031 check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1032 },
1033 parameters => {
1034 additionalProperties => 0,
1035 properties => {
1036 node => get_standard_option('pve-node'),
1037 vmid => get_standard_option('pve-vmid'),
1038 },
1039 },
1040 returns => {
1041 type => 'string',
1042 },
1043 code => sub {
1044 my ($param) = @_;
1045
1046 my $rpcenv = PVE::RPCEnvironment::get();
1047
1048 my $authuser = $rpcenv->get_user();
1049
1050 my $node = extract_param($param, 'node');
1051
1052 my $vmid = extract_param($param, 'vmid');
1053
1054 die "CT $vmid not running\n" if !PVE::LXC::check_running($vmid);
1055
1056 if (PVE::HA::Config::vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
1057
1058 my $hacmd = sub {
1059 my $upid = shift;
1060
1061 my $service = "ct:$vmid";
1062
1063 my $cmd = ['ha-manager', 'disable', $service];
1064
1065 print "Executing HA stop for CT $vmid\n";
1066
1067 PVE::Tools::run_command($cmd);
1068
1069 return;
1070 };
1071
1072 return $rpcenv->fork_worker('hastop', $vmid, $authuser, $hacmd);
1073
1074 } else {
1075
1076 my $realcmd = sub {
1077 my $upid = shift;
1078
1079 syslog('info', "stoping CT $vmid: $upid\n");
1080
ef241384
DM
1081 my $conf = PVE::LXC::load_config($vmid);
1082
1083 my $storage_cfg = PVE::Storage::config();
1084
5c752bbf
DM
1085 my $cmd = ['lxc-stop', '-n', $vmid, '--kill'];
1086
1087 run_command($cmd);
1088
1089 return;
1090 };
1091
1092 return $rpcenv->fork_worker('vzstop', $vmid, $authuser, $realcmd);
1093 }
1094 }});
1095
1096__PACKAGE__->register_method({
1097 name => 'vm_shutdown',
1098 path => '{vmid}/status/shutdown',
1099 method => 'POST',
1100 protected => 1,
1101 proxyto => 'node',
1102 description => "Shutdown the container.",
1103 permissions => {
1104 check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1105 },
1106 parameters => {
1107 additionalProperties => 0,
1108 properties => {
1109 node => get_standard_option('pve-node'),
1110 vmid => get_standard_option('pve-vmid'),
1111 timeout => {
1112 description => "Wait maximal timeout seconds.",
1113 type => 'integer',
1114 minimum => 0,
1115 optional => 1,
1116 default => 60,
1117 },
1118 forceStop => {
1119 description => "Make sure the Container stops.",
1120 type => 'boolean',
1121 optional => 1,
1122 default => 0,
1123 }
1124 },
1125 },
1126 returns => {
1127 type => 'string',
1128 },
1129 code => sub {
1130 my ($param) = @_;
1131
1132 my $rpcenv = PVE::RPCEnvironment::get();
1133
1134 my $authuser = $rpcenv->get_user();
1135
1136 my $node = extract_param($param, 'node');
1137
1138 my $vmid = extract_param($param, 'vmid');
1139
1140 my $timeout = extract_param($param, 'timeout');
1141
1142 die "CT $vmid not running\n" if !PVE::LXC::check_running($vmid);
1143
1144 my $realcmd = sub {
1145 my $upid = shift;
1146
1147 syslog('info', "shutdown CT $vmid: $upid\n");
1148
1149 my $cmd = ['lxc-stop', '-n', $vmid];
1150
1151 $timeout = 60 if !defined($timeout);
1152
ef241384
DM
1153 my $conf = PVE::LXC::load_config($vmid);
1154
1155 my $storage_cfg = PVE::Storage::config();
1156
5c752bbf
DM
1157 push @$cmd, '--timeout', $timeout;
1158
1159 eval { run_command($cmd, timeout => $timeout+5); };
1160 my $err = $@;
ef241384
DM
1161 if ($err && $param->{forceStop}) {
1162 $err = undef;
1163 warn "shutdown failed - forcing stop now\n";
5c752bbf 1164
ef241384
DM
1165 push @$cmd, '--kill';
1166 run_command($cmd);
1167
1168 }
5c752bbf 1169
ef241384 1170 die $err if !$err;
5c752bbf
DM
1171
1172 return;
1173 };
1174
1175 my $upid = $rpcenv->fork_worker('vzshutdown', $vmid, $authuser, $realcmd);
1176
1177 return $upid;
1178 }});
1179
1180__PACKAGE__->register_method({
1181 name => 'vm_suspend',
1182 path => '{vmid}/status/suspend',
1183 method => 'POST',
1184 protected => 1,
1185 proxyto => 'node',
1186 description => "Suspend the container.",
1187 permissions => {
1188 check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1189 },
1190 parameters => {
1191 additionalProperties => 0,
1192 properties => {
1193 node => get_standard_option('pve-node'),
1194 vmid => get_standard_option('pve-vmid'),
1195 },
1196 },
1197 returns => {
1198 type => 'string',
1199 },
1200 code => sub {
1201 my ($param) = @_;
1202
1203 my $rpcenv = PVE::RPCEnvironment::get();
1204
1205 my $authuser = $rpcenv->get_user();
1206
1207 my $node = extract_param($param, 'node');
1208
1209 my $vmid = extract_param($param, 'vmid');
1210
1211 die "CT $vmid not running\n" if !PVE::LXC::check_running($vmid);
1212
1213 my $realcmd = sub {
1214 my $upid = shift;
1215
1216 syslog('info', "suspend CT $vmid: $upid\n");
1217
1218 my $cmd = ['lxc-checkpoint', '-n', $vmid, '-s', '-D', '/var/liv/vz/dump'];
1219
1220 run_command($cmd);
1221
1222 return;
1223 };
1224
1225 my $upid = $rpcenv->fork_worker('vzsuspend', $vmid, $authuser, $realcmd);
1226
1227 return $upid;
1228 }});
1229
1230__PACKAGE__->register_method({
1231 name => 'vm_resume',
1232 path => '{vmid}/status/resume',
1233 method => 'POST',
1234 protected => 1,
1235 proxyto => 'node',
1236 description => "Resume the container.",
1237 permissions => {
1238 check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1239 },
1240 parameters => {
1241 additionalProperties => 0,
1242 properties => {
1243 node => get_standard_option('pve-node'),
1244 vmid => get_standard_option('pve-vmid'),
1245 },
1246 },
1247 returns => {
1248 type => 'string',
1249 },
1250 code => sub {
1251 my ($param) = @_;
1252
1253 my $rpcenv = PVE::RPCEnvironment::get();
1254
1255 my $authuser = $rpcenv->get_user();
1256
1257 my $node = extract_param($param, 'node');
1258
1259 my $vmid = extract_param($param, 'vmid');
1260
1261 die "CT $vmid already running\n" if PVE::LXC::check_running($vmid);
1262
1263 my $realcmd = sub {
1264 my $upid = shift;
1265
1266 syslog('info', "resume CT $vmid: $upid\n");
1267
1268 my $cmd = ['lxc-checkpoint', '-n', $vmid, '-r', '--foreground',
1269 '-D', '/var/liv/vz/dump'];
1270
1271 run_command($cmd);
1272
1273 return;
1274 };
1275
1276 my $upid = $rpcenv->fork_worker('vzresume', $vmid, $authuser, $realcmd);
1277
1278 return $upid;
1279 }});
1280
1281__PACKAGE__->register_method({
1282 name => 'migrate_vm',
1283 path => '{vmid}/migrate',
1284 method => 'POST',
1285 protected => 1,
1286 proxyto => 'node',
1287 description => "Migrate the container to another node. Creates a new migration task.",
1288 permissions => {
1289 check => ['perm', '/vms/{vmid}', [ 'VM.Migrate' ]],
1290 },
1291 parameters => {
1292 additionalProperties => 0,
1293 properties => {
1294 node => get_standard_option('pve-node'),
1295 vmid => get_standard_option('pve-vmid'),
1296 target => get_standard_option('pve-node', { description => "Target node." }),
1297 online => {
1298 type => 'boolean',
1299 description => "Use online/live migration.",
1300 optional => 1,
1301 },
1302 },
1303 },
1304 returns => {
1305 type => 'string',
1306 description => "the task ID.",
1307 },
1308 code => sub {
1309 my ($param) = @_;
1310
1311 my $rpcenv = PVE::RPCEnvironment::get();
1312
1313 my $authuser = $rpcenv->get_user();
1314
1315 my $target = extract_param($param, 'target');
1316
1317 my $localnode = PVE::INotify::nodename();
1318 raise_param_exc({ target => "target is local node."}) if $target eq $localnode;
1319
1320 PVE::Cluster::check_cfs_quorum();
1321
1322 PVE::Cluster::check_node_exists($target);
1323
1324 my $targetip = PVE::Cluster::remote_node_ip($target);
1325
1326 my $vmid = extract_param($param, 'vmid');
1327
1328 # test if VM exists
1329 PVE::LXC::load_config($vmid);
1330
1331 # try to detect errors early
1332 if (PVE::LXC::check_running($vmid)) {
1333 die "cant migrate running container without --online\n"
1334 if !$param->{online};
1335 }
1336
1337 if (PVE::HA::Config::vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
1338
1339 my $hacmd = sub {
1340 my $upid = shift;
1341
1342 my $service = "ct:$vmid";
1343
1344 my $cmd = ['ha-manager', 'migrate', $service, $target];
1345
1346 print "Executing HA migrate for CT $vmid to node $target\n";
1347
1348 PVE::Tools::run_command($cmd);
1349
1350 return;
1351 };
1352
1353 return $rpcenv->fork_worker('hamigrate', $vmid, $authuser, $hacmd);
1354
1355 } else {
1356
1357 my $realcmd = sub {
1358 my $upid = shift;
1359
1360 # fixme: implement lxc container migration
1361 die "lxc container migration not implemented\n";
1362
1363 return;
1364 };
1365
1366 return $rpcenv->fork_worker('vzmigrate', $vmid, $authuser, $realcmd);
1367 }
1368 }});
1369
489e960d
WL
1370__PACKAGE__->register_method({
1371 name => 'snapshot',
1372 path => '{vmid}/snapshot',
1373 method => 'POST',
1374 protected => 1,
1375 proxyto => 'node',
1376 description => "Snapshot a container.",
1377 permissions => {
1378 check => ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
1379 },
1380 parameters => {
1381 additionalProperties => 0,
1382 properties => {
1383 node => get_standard_option('pve-node'),
1384 vmid => get_standard_option('pve-vmid'),
1385 snapname => get_standard_option('pve-lxc-snapshot-name'),
09d3ec42
DM
1386# vmstate => {
1387# optional => 1,
1388# type => 'boolean',
1389# description => "Save the vmstate",
1390# },
489e960d
WL
1391 description => {
1392 optional => 1,
1393 type => 'string',
1394 description => "A textual description or comment.",
1395 },
1396 },
1397 },
1398 returns => {
1399 type => 'string',
1400 description => "the task ID.",
1401 },
1402 code => sub {
1403 my ($param) = @_;
1404
1405 my $rpcenv = PVE::RPCEnvironment::get();
1406
1407 my $authuser = $rpcenv->get_user();
1408
1409 my $node = extract_param($param, 'node');
1410
1411 my $vmid = extract_param($param, 'vmid');
1412
1413 my $snapname = extract_param($param, 'snapname');
1414
1415 die "unable to use snapshot name 'current' (reserved name)\n"
1416 if $snapname eq 'current';
1417
1418 my $realcmd = sub {
1419 PVE::Cluster::log_msg('info', $authuser, "snapshot container $vmid: $snapname");
1420 PVE::LXC::snapshot_create($vmid, $snapname, $param->{description});
1421 };
1422
1423 return $rpcenv->fork_worker('pctsnapshot', $vmid, $authuser, $realcmd);
1424 }});
57ccb3f8
WL
1425
1426__PACKAGE__->register_method({
1427 name => 'delsnapshot',
1428 path => '{vmid}/snapshot/{snapname}',
1429 method => 'DELETE',
1430 protected => 1,
1431 proxyto => 'node',
1432 description => "Delete a LXC snapshot.",
1433 permissions => {
1434 check => ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
1435 },
1436 parameters => {
1437 additionalProperties => 0,
1438 properties => {
1439 node => get_standard_option('pve-node'),
1440 vmid => get_standard_option('pve-vmid'),
1441 snapname => get_standard_option('pve-lxc-snapshot-name'),
1442 force => {
1443 optional => 1,
1444 type => 'boolean',
1445 description => "For removal from config file, even if removing disk snapshots fails.",
1446 },
1447 },
1448 },
1449 returns => {
1450 type => 'string',
1451 description => "the task ID.",
1452 },
1453 code => sub {
1454 my ($param) = @_;
1455
1456 my $rpcenv = PVE::RPCEnvironment::get();
1457
1458 my $authuser = $rpcenv->get_user();
1459
1460 my $node = extract_param($param, 'node');
1461
1462 my $vmid = extract_param($param, 'vmid');
1463
1464 my $snapname = extract_param($param, 'snapname');
1465
1466 my $realcmd = sub {
1467 PVE::Cluster::log_msg('info', $authuser, "delete snapshot VM $vmid: $snapname");
1468 PVE::LXC::snapshot_delete($vmid, $snapname, $param->{force});
1469 };
1470
1471 return $rpcenv->fork_worker('lxcdelsnapshot', $vmid, $authuser, $realcmd);
1472 }});
cc5392c8 1473
723157f6
WL
1474__PACKAGE__->register_method({
1475 name => 'rollback',
1476 path => '{vmid}/snapshot/{snapname}/rollback',
1477 method => 'POST',
1478 protected => 1,
1479 proxyto => 'node',
1480 description => "Rollback LXC state to specified snapshot.",
1481 permissions => {
1482 check => ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
1483 },
1484 parameters => {
1485 additionalProperties => 0,
1486 properties => {
1487 node => get_standard_option('pve-node'),
1488 vmid => get_standard_option('pve-vmid'),
1489 snapname => get_standard_option('pve-lxc-snapshot-name'),
1490 },
1491 },
1492 returns => {
1493 type => 'string',
1494 description => "the task ID.",
1495 },
1496 code => sub {
1497 my ($param) = @_;
1498
1499 my $rpcenv = PVE::RPCEnvironment::get();
1500
1501 my $authuser = $rpcenv->get_user();
1502
1503 my $node = extract_param($param, 'node');
1504
1505 my $vmid = extract_param($param, 'vmid');
1506
1507 my $snapname = extract_param($param, 'snapname');
1508
1509 my $realcmd = sub {
1510 PVE::Cluster::log_msg('info', $authuser, "rollback snapshot LXC $vmid: $snapname");
1511 PVE::LXC::snapshot_rollback($vmid, $snapname);
1512 };
1513
1514 return $rpcenv->fork_worker('lxcrollback', $vmid, $authuser, $realcmd);
1515 }});
1516
cc5392c8
WL
1517__PACKAGE__->register_method({
1518 name => 'snapshot_list',
1519 path => '{vmid}/snapshot',
1520 method => 'GET',
1521 description => "List all snapshots.",
1522 permissions => {
1523 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
1524 },
1525 proxyto => 'node',
1526 protected => 1, # lxc pid files are only readable by root
1527 parameters => {
1528 additionalProperties => 0,
1529 properties => {
1530 vmid => get_standard_option('pve-vmid'),
1531 node => get_standard_option('pve-node'),
1532 },
1533 },
1534 returns => {
1535 type => 'array',
1536 items => {
1537 type => "object",
1538 properties => {},
1539 },
1540 links => [ { rel => 'child', href => "{name}" } ],
1541 },
1542 code => sub {
1543 my ($param) = @_;
1544
1545 my $vmid = $param->{vmid};
1546
1547 my $conf = PVE::LXC::load_config($vmid);
1548 my $snaphash = $conf->{snapshots} || {};
1549
1550 my $res = [];
1551
1552 foreach my $name (keys %$snaphash) {
1553 my $d = $snaphash->{$name};
1554 my $item = {
1555 name => $name,
09d3ec42
DM
1556 snaptime => $d->{snaptime} || 0,
1557 description => $d->{description} || '',
cc5392c8 1558 };
09d3ec42
DM
1559 $item->{parent} = $d->{parent} if defined($d->{parent});
1560 $item->{snapstate} = $d->{snapstate} if $d->{snapstate};
cc5392c8
WL
1561 push @$res, $item;
1562 }
1563
1564 my $running = PVE::LXC::check_running($vmid) ? 1 : 0;
1565 my $current = { name => 'current', digest => $conf->{digest}, running => $running };
09d3ec42 1566 $current->{parent} = $conf->{parent} if defined($conf->{parent});
cc5392c8
WL
1567
1568 push @$res, $current;
1569
1570 return $res;
1571 }});
1572
1573__PACKAGE__->register_method({
1574 name => 'snapshot_cmd_idx',
1575 path => '{vmid}/snapshot/{snapname}',
1576 description => '',
1577 method => 'GET',
1578 permissions => {
1579 user => 'all',
1580 },
1581 parameters => {
1582 additionalProperties => 0,
1583 properties => {
1584 vmid => get_standard_option('pve-vmid'),
1585 node => get_standard_option('pve-node'),
1586 snapname => get_standard_option('pve-lxc-snapshot-name'),
1587 },
1588 },
1589 returns => {
1590 type => 'array',
1591 items => {
1592 type => "object",
1593 properties => {},
1594 },
1595 links => [ { rel => 'child', href => "{cmd}" } ],
1596 },
1597 code => sub {
1598 my ($param) = @_;
1599
1600 my $res = [];
1601
1602 push @$res, { cmd => 'rollback' };
1603 push @$res, { cmd => 'config' };
1604
1605 return $res;
1606 }});
1607
1608__PACKAGE__->register_method({
1609 name => 'update_snapshot_config',
1610 path => '{vmid}/snapshot/{snapname}/config',
1611 method => 'PUT',
1612 protected => 1,
1613 proxyto => 'node',
1614 description => "Update snapshot metadata.",
1615 permissions => {
1616 check => ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
1617 },
1618 parameters => {
1619 additionalProperties => 0,
1620 properties => {
1621 node => get_standard_option('pve-node'),
1622 vmid => get_standard_option('pve-vmid'),
1623 snapname => get_standard_option('pve-lxc-snapshot-name'),
1624 description => {
1625 optional => 1,
1626 type => 'string',
1627 description => "A textual description or comment.",
1628 },
1629 },
1630 },
1631 returns => { type => 'null' },
1632 code => sub {
1633 my ($param) = @_;
1634
1635 my $rpcenv = PVE::RPCEnvironment::get();
1636
1637 my $authuser = $rpcenv->get_user();
1638
1639 my $vmid = extract_param($param, 'vmid');
1640
1641 my $snapname = extract_param($param, 'snapname');
1642
1643 return undef if !defined($param->{description});
1644
1645 my $updatefn = sub {
1646
1647 my $conf = PVE::LXC::load_config($vmid);
cc5392c8
WL
1648 PVE::LXC::check_lock($conf);
1649
1650 my $snap = $conf->{snapshots}->{$snapname};
1651
1652 die "snapshot '$snapname' does not exist\n" if !defined($snap);
1653
09d3ec42 1654 $snap->{description} = $param->{description} if defined($param->{description});
cc5392c8 1655
09d3ec42 1656 PVE::LXC::write_config($vmid, $conf, 1);
cc5392c8
WL
1657 };
1658
1659 PVE::LXC::lock_container($vmid, 10, $updatefn);
1660
1661 return undef;
1662 }});
1663
1664__PACKAGE__->register_method({
1665 name => 'get_snapshot_config',
1666 path => '{vmid}/snapshot/{snapname}/config',
1667 method => 'GET',
1668 proxyto => 'node',
1669 description => "Get snapshot configuration",
1670 permissions => {
1671 check => ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
1672 },
1673 parameters => {
1674 additionalProperties => 0,
1675 properties => {
1676 node => get_standard_option('pve-node'),
1677 vmid => get_standard_option('pve-vmid'),
1678 snapname => get_standard_option('pve-lxc-snapshot-name'),
1679 },
1680 },
1681 returns => { type => "object" },
1682 code => sub {
1683 my ($param) = @_;
1684
1685 my $rpcenv = PVE::RPCEnvironment::get();
1686
1687 my $authuser = $rpcenv->get_user();
1688
1689 my $vmid = extract_param($param, 'vmid');
1690
1691 my $snapname = extract_param($param, 'snapname');
1692
09d3ec42 1693 my $conf = PVE::LXC::load_config($vmid);
cc5392c8 1694
09d3ec42 1695 my $snap = $conf->{snapshots}->{$snapname};
cc5392c8
WL
1696
1697 die "snapshot '$snapname' does not exist\n" if !defined($snap);
1698
e576f689
DM
1699 delete $snap->{lxc};
1700
09d3ec42 1701 return $snap;
cc5392c8
WL
1702 }});
1703
1704__PACKAGE__->register_method({
1705 name => 'vm_feature',
1706 path => '{vmid}/feature',
1707 method => 'GET',
1708 proxyto => 'node',
1709 protected => 1,
1710 description => "Check if feature for virtual machine is available.",
1711 permissions => {
1712 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
1713 },
1714 parameters => {
1715 additionalProperties => 0,
1716 properties => {
1717 node => get_standard_option('pve-node'),
1718 vmid => get_standard_option('pve-vmid'),
1719 feature => {
1720 description => "Feature to check.",
1721 type => 'string',
1722 enum => [ 'snapshot' ],
1723 },
1724 snapname => get_standard_option('pve-lxc-snapshot-name', {
1725 optional => 1,
1726 }),
1727 },
1728 },
1729 returns => {
1730 type => "object",
1731 properties => {
1732 hasFeature => { type => 'boolean' },
1733 #nodes => {
1734 #type => 'array',
1735 #items => { type => 'string' },
1736 #}
1737 },
1738 },
1739 code => sub {
1740 my ($param) = @_;
1741
1742 my $node = extract_param($param, 'node');
1743
1744 my $vmid = extract_param($param, 'vmid');
1745
1746 my $snapname = extract_param($param, 'snapname');
1747
1748 my $feature = extract_param($param, 'feature');
1749
1750 my $conf = PVE::LXC::load_config($vmid);
1751
1752 if($snapname){
1753 my $snap = $conf->{snapshots}->{$snapname};
1754 die "snapshot '$snapname' does not exist\n" if !defined($snap);
1755 $conf = $snap;
1756 }
ef241384 1757 my $storage_cfg = PVE::Storage::config();
cc5392c8 1758 #Maybe include later
ef241384
DM
1759 #my $nodelist = PVE::LXC::shared_nodes($conf, $storage_cfg);
1760 my $hasFeature = PVE::LXC::has_feature($feature, $conf, $storage_cfg, $snapname);
cc5392c8
WL
1761
1762 return {
1763 hasFeature => $hasFeature,
1764 #nodes => [ keys %$nodelist ],
1765 };
1766 }});
bb1ac2de
DM
1767
1768__PACKAGE__->register_method({
1769 name => 'template',
1770 path => '{vmid}/template',
1771 method => 'POST',
1772 protected => 1,
1773 proxyto => 'node',
1774 description => "Create a Template.",
1775 permissions => {
1776 description => "You need 'VM.Allocate' permissions on /vms/{vmid}",
1777 check => [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
1778 },
1779 parameters => {
1780 additionalProperties => 0,
1781 properties => {
1782 node => get_standard_option('pve-node'),
1783 vmid => get_standard_option('pve-vmid'),
1784 },
1785 },
1786 returns => { type => 'null'},
1787 code => sub {
1788 my ($param) = @_;
1789
1790 my $rpcenv = PVE::RPCEnvironment::get();
1791
1792 my $authuser = $rpcenv->get_user();
1793
1794 my $node = extract_param($param, 'node');
1795
1796 my $vmid = extract_param($param, 'vmid');
1797
1798 my $updatefn = sub {
1799
1800 my $conf = PVE::LXC::load_config($vmid);
1801 PVE::LXC::check_lock($conf);
1802
1803 die "unable to create template, because CT contains snapshots\n"
1804 if $conf->{snapshots} && scalar(keys %{$conf->{snapshots}});
1805
1806 die "you can't convert a template to a template\n"
1807 if PVE::LXC::is_template($conf);
1808
1809 die "you can't convert a CT to template if the CT is running\n"
1810 if PVE::LXC::check_running($vmid);
1811
1812 my $realcmd = sub {
1813 PVE::LXC::template_create($vmid, $conf);
1814 };
1815
1816 $conf->{template} = 1;
1817
1818 PVE::LXC::write_config($vmid, $conf);
1819 # and remove lxc config
1820 PVE::LXC::update_lxc_config(undef, $vmid, $conf);
1821
1822 return $rpcenv->fork_worker('vztemplate', $vmid, $authuser, $realcmd);
1823 };
1824
1825 PVE::LXC::lock_container($vmid, undef, $updatefn);
1826
1827 return undef;
1828 }});
1829
f76a2828 18301;