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