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