]> git.proxmox.com Git - pve-container.git/blame - src/PVE/API2/LXC.pm
Correct package name in sub update_ipconfig()
[pve-container.git] / src / PVE / API2 / LXC.pm
CommitLineData
f76a2828
DM
1package PVE::API2::LXC;
2
3use strict;
4use warnings;
5
6use PVE::SafeSyslog;
7use PVE::Tools qw(extract_param run_command);
8use PVE::Exception qw(raise raise_param_exc);
9use PVE::INotify;
9c2d4ce9 10use PVE::Cluster qw(cfs_read_file);
f76a2828 11use PVE::AccessControl;
2f9b5ead 12use PVE::Firewall;
f76a2828
DM
13use PVE::Storage;
14use PVE::RESTHandler;
15use PVE::RPCEnvironment;
16use PVE::LXC;
7af97ad5 17use PVE::LXC::Create;
52389a07
DM
18use PVE::API2::LXC::Config;
19use PVE::API2::LXC::Status;
20use PVE::API2::LXC::Snapshot;
5c752bbf 21use PVE::HA::Config;
f76a2828
DM
22use PVE::JSONSchema qw(get_standard_option);
23use base qw(PVE::RESTHandler);
24
25use Data::Dumper; # fixme: remove
26
52389a07
DM
27__PACKAGE__->register_method ({
28 subclass => "PVE::API2::LXC::Config",
29 path => '{vmid}/config',
30});
f76a2828 31
52389a07
DM
32__PACKAGE__->register_method ({
33 subclass => "PVE::API2::LXC::Status",
34 path => '{vmid}/status',
35});
f76a2828 36
52389a07
DM
37__PACKAGE__->register_method ({
38 subclass => "PVE::API2::LXC::Snapshot",
39 path => '{vmid}/snapshot',
40});
f76a2828 41
52389a07
DM
42__PACKAGE__->register_method ({
43 subclass => "PVE::API2::Firewall::CT",
44 path => '{vmid}/firewall',
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
f76a2828 95__PACKAGE__->register_method({
5c752bbf
DM
96 name => 'vmlist',
97 path => '',
f76a2828
DM
98 method => 'GET',
99 description => "LXC container index (per node).",
100 permissions => {
101 description => "Only list CTs where you have VM.Audit permissons on /vms/<vmid>.",
102 user => 'all',
103 },
104 proxyto => 'node',
105 protected => 1, # /proc files are only readable by root
106 parameters => {
107 additionalProperties => 0,
108 properties => {
109 node => get_standard_option('pve-node'),
110 },
111 },
112 returns => {
113 type => 'array',
114 items => {
115 type => "object",
116 properties => {},
117 },
118 links => [ { rel => 'child', href => "{vmid}" } ],
119 },
120 code => sub {
121 my ($param) = @_;
122
123 my $rpcenv = PVE::RPCEnvironment::get();
124 my $authuser = $rpcenv->get_user();
125
126 my $vmstatus = PVE::LXC::vmstatus();
127
128 my $res = [];
129 foreach my $vmid (keys %$vmstatus) {
130 next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
131
132 my $data = $vmstatus->{$vmid};
133 $data->{vmid} = $vmid;
134 push @$res, $data;
135 }
136
137 return $res;
5c752bbf 138
f76a2828
DM
139 }});
140
9c2d4ce9 141__PACKAGE__->register_method({
5c752bbf
DM
142 name => 'create_vm',
143 path => '',
9c2d4ce9
DM
144 method => 'POST',
145 description => "Create or restore a container.",
146 permissions => {
147 user => 'all', # check inside
148 description => "You need 'VM.Allocate' permissions on /vms/{vmid} or on the VM pool /pool/{pool}. " .
149 "For restore, it is enough if the user has 'VM.Backup' permission and the VM already exists. " .
150 "You also need 'Datastore.AllocateSpace' permissions on the storage.",
151 },
152 protected => 1,
153 proxyto => 'node',
154 parameters => {
155 additionalProperties => 0,
27916659 156 properties => PVE::LXC::json_config_properties_no_rootfs({
9c2d4ce9
DM
157 node => get_standard_option('pve-node'),
158 vmid => get_standard_option('pve-vmid'),
159 ostemplate => {
160 description => "The OS template or backup file.",
5c752bbf 161 type => 'string',
9c2d4ce9
DM
162 maxLength => 255,
163 },
5c752bbf
DM
164 password => {
165 optional => 1,
9c2d4ce9
DM
166 type => 'string',
167 description => "Sets root password inside container.",
168d6b07 168 minLength => 5,
9c2d4ce9
DM
169 },
170 storage => get_standard_option('pve-storage-id', {
171 description => "Target storage.",
172 default => 'local',
173 optional => 1,
174 }),
27916659
DM
175 size => {
176 optional => 1,
177 type => 'number',
178 description => "Amount of disk space for the VM in GB. A zero indicates no limits.",
179 minimum => 0,
180 default => 4,
181 },
9c2d4ce9 182 force => {
5c752bbf 183 optional => 1,
9c2d4ce9
DM
184 type => 'boolean',
185 description => "Allow to overwrite existing container.",
186 },
187 restore => {
5c752bbf 188 optional => 1,
9c2d4ce9
DM
189 type => 'boolean',
190 description => "Mark this as restore task.",
191 },
5c752bbf 192 pool => {
9c2d4ce9
DM
193 optional => 1,
194 type => 'string', format => 'pve-poolid',
195 description => "Add the VM to the specified pool.",
196 },
197 }),
198 },
5c752bbf 199 returns => {
9c2d4ce9
DM
200 type => 'string',
201 },
202 code => sub {
203 my ($param) = @_;
204
205 my $rpcenv = PVE::RPCEnvironment::get();
206
207 my $authuser = $rpcenv->get_user();
208
209 my $node = extract_param($param, 'node');
210
211 my $vmid = extract_param($param, 'vmid');
212
213 my $basecfg_fn = PVE::LXC::config_file($vmid);
214
215 my $same_container_exists = -f $basecfg_fn;
216
217 my $restore = extract_param($param, 'restore');
218
148d1cb4
DM
219 if ($restore) {
220 # fixme: limit allowed parameters
221
222 }
223
9c2d4ce9
DM
224 my $force = extract_param($param, 'force');
225
226 if (!($same_container_exists && $restore && $force)) {
227 PVE::Cluster::check_vmid_unused($vmid);
228 }
5c752bbf 229
9c2d4ce9
DM
230 my $password = extract_param($param, 'password');
231
27916659 232 my $disksize = extract_param($param, 'size');
5c752bbf 233
27916659
DM
234 my $storage = extract_param($param, 'storage') // 'local';
235
9c2d4ce9
DM
236 my $storage_cfg = cfs_read_file("storage.cfg");
237
238 my $scfg = PVE::Storage::storage_check_node($storage_cfg, $storage, $node);
239
240 raise_param_exc({ storage => "storage '$storage' does not support container root directories"})
644f2f8f 241 if !($scfg->{content}->{images} || $scfg->{content}->{rootdir});
9c2d4ce9 242
27916659
DM
243 my $pool = extract_param($param, 'pool');
244
9c2d4ce9
DM
245 if (defined($pool)) {
246 $rpcenv->check_pool_exist($pool);
247 $rpcenv->check_perm_modify($authuser, "/pool/$pool");
5c752bbf 248 }
9c2d4ce9 249
27916659
DM
250 $rpcenv->check($authuser, "/storage/$storage", ['Datastore.AllocateSpace']);
251
9c2d4ce9
DM
252 if ($rpcenv->check($authuser, "/vms/$vmid", ['VM.Allocate'], 1)) {
253 # OK
254 } elsif ($pool && $rpcenv->check($authuser, "/pool/$pool", ['VM.Allocate'], 1)) {
255 # OK
256 } elsif ($restore && $force && $same_container_exists &&
257 $rpcenv->check($authuser, "/vms/$vmid", ['VM.Backup'], 1)) {
258 # OK: user has VM.Backup permissions, and want to restore an existing VM
259 } else {
260 raise_perm_exc();
261 }
262
52389a07 263 PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]);
9c2d4ce9
DM
264
265 PVE::Storage::activate_storage($storage_cfg, $storage);
266
267 my $ostemplate = extract_param($param, 'ostemplate');
5c752bbf 268
9c2d4ce9
DM
269 my $archive;
270
271 if ($ostemplate eq '-') {
148d1cb4
DM
272 die "pipe requires cli environment\n"
273 if $rpcenv->{type} ne 'cli';
274 die "pipe can only be used with restore tasks\n"
275 if !$restore;
27916659
DM
276 die "pipe requires --size parameter\n"
277 if !defined($disksize);
148d1cb4 278 $archive = '-';
9c2d4ce9
DM
279 } else {
280 $rpcenv->check_volume_access($authuser, $storage_cfg, $vmid, $ostemplate);
281 $archive = PVE::Storage::abs_filesystem_path($storage_cfg, $ostemplate);
282 }
283
9c2d4ce9 284 my $conf = {};
5b4657d0 285
27916659 286 PVE::LXC::update_pct_config($vmid, $conf, 0, $param);
9c2d4ce9 287
148d1cb4
DM
288 my $check_vmid_usage = sub {
289 if ($force) {
290 die "cant overwrite running container\n"
291 if PVE::LXC::check_running($vmid);
292 } else {
293 PVE::Cluster::check_vmid_unused($vmid);
294 }
295 };
f507c3a7 296
5b4657d0 297 my $code = sub {
148d1cb4 298 &$check_vmid_usage(); # final check after locking
87273b2b 299
148d1cb4
DM
300 PVE::Cluster::check_cfs_quorum();
301
27916659
DM
302 my $volid;
303
304 eval {
305 if (!defined($disksize)) {
306 if ($restore) {
7af97ad5 307 (undef, $disksize) = PVE::LXC::Create::recover_config($archive);
27916659
DM
308 die "unable to detect disk size - please specify with --size\n"
309 if !$disksize;
310 } else {
311 $disksize = 4;
312 }
313 }
314 $volid = &$alloc_rootfs($storage_cfg, $storage, $disksize, $vmid);
315
7af97ad5
DM
316 PVE::LXC::Create::create_rootfs($storage_cfg, $storage, $volid, $vmid, $conf,
317 $archive, $password, $restore);
27916659 318
dde7b02b 319 $conf->{rootfs} = PVE::LXC::print_ct_mountpoint({volume => $volid, size => $disksize });
27916659
DM
320
321 # set some defaults
322 $conf->{hostname} ||= "CT$vmid";
323 $conf->{memory} ||= 512;
324 $conf->{swap} //= 512;
325
326 PVE::LXC::create_config($vmid, $conf);
327 };
328 if (my $err = $@) {
329 eval { PVE::Storage::vdisk_free($storage_cfg, $volid) if $volid; };
330 warn $@ if $@;
331 PVE::LXC::destroy_config($vmid);
332 die $err;
6d098bf4 333 }
87273b2b 334 PVE::AccessControl::add_vm_to_pool($vmid, $pool) if $pool;
9c2d4ce9 335 };
5c752bbf 336
9c2d4ce9
DM
337 my $realcmd = sub { PVE::LXC::lock_container($vmid, 1, $code); };
338
148d1cb4
DM
339 &$check_vmid_usage(); # first check before locking
340
341 return $rpcenv->fork_worker($restore ? 'vzrestore' : 'vzcreate',
9c2d4ce9 342 $vmid, $authuser, $realcmd);
5c752bbf 343
9c2d4ce9
DM
344 }});
345
f76a2828
DM
346__PACKAGE__->register_method({
347 name => 'vmdiridx',
5c752bbf 348 path => '{vmid}',
f76a2828
DM
349 method => 'GET',
350 proxyto => 'node',
351 description => "Directory index",
352 permissions => {
353 user => 'all',
354 },
355 parameters => {
356 additionalProperties => 0,
357 properties => {
358 node => get_standard_option('pve-node'),
359 vmid => get_standard_option('pve-vmid'),
360 },
361 },
362 returns => {
363 type => 'array',
364 items => {
365 type => "object",
366 properties => {
367 subdir => { type => 'string' },
368 },
369 },
370 links => [ { rel => 'child', href => "{subdir}" } ],
371 },
372 code => sub {
373 my ($param) = @_;
374
375 # test if VM exists
e901d418 376 my $conf = PVE::LXC::load_config($param->{vmid});
f76a2828
DM
377
378 my $res = [
379 { subdir => 'config' },
fff3a342
DM
380 { subdir => 'status' },
381 { subdir => 'vncproxy' },
382 { subdir => 'vncwebsocket' },
383 { subdir => 'spiceproxy' },
384 { subdir => 'migrate' },
f76a2828
DM
385# { subdir => 'initlog' },
386 { subdir => 'rrd' },
387 { subdir => 'rrddata' },
388 { subdir => 'firewall' },
cc5392c8 389 { subdir => 'snapshot' },
f76a2828 390 ];
5c752bbf 391
f76a2828
DM
392 return $res;
393 }});
394
395__PACKAGE__->register_method({
5c752bbf
DM
396 name => 'rrd',
397 path => '{vmid}/rrd',
f76a2828
DM
398 method => 'GET',
399 protected => 1, # fixme: can we avoid that?
400 permissions => {
401 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
402 },
403 description => "Read VM RRD statistics (returns PNG)",
404 parameters => {
405 additionalProperties => 0,
406 properties => {
407 node => get_standard_option('pve-node'),
408 vmid => get_standard_option('pve-vmid'),
409 timeframe => {
410 description => "Specify the time frame you are interested in.",
411 type => 'string',
412 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
413 },
414 ds => {
415 description => "The list of datasources you want to display.",
416 type => 'string', format => 'pve-configid-list',
417 },
418 cf => {
419 description => "The RRD consolidation function",
420 type => 'string',
421 enum => [ 'AVERAGE', 'MAX' ],
422 optional => 1,
423 },
424 },
425 },
426 returns => {
427 type => "object",
428 properties => {
429 filename => { type => 'string' },
430 },
431 },
432 code => sub {
433 my ($param) = @_;
434
435 return PVE::Cluster::create_rrd_graph(
5c752bbf 436 "pve2-vm/$param->{vmid}", $param->{timeframe},
f76a2828 437 $param->{ds}, $param->{cf});
5c752bbf 438
f76a2828
DM
439 }});
440
441__PACKAGE__->register_method({
5c752bbf
DM
442 name => 'rrddata',
443 path => '{vmid}/rrddata',
f76a2828
DM
444 method => 'GET',
445 protected => 1, # fixme: can we avoid that?
446 permissions => {
447 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
448 },
449 description => "Read VM RRD statistics",
450 parameters => {
451 additionalProperties => 0,
452 properties => {
453 node => get_standard_option('pve-node'),
454 vmid => get_standard_option('pve-vmid'),
455 timeframe => {
456 description => "Specify the time frame you are interested in.",
457 type => 'string',
458 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
459 },
460 cf => {
461 description => "The RRD consolidation function",
462 type => 'string',
463 enum => [ 'AVERAGE', 'MAX' ],
464 optional => 1,
465 },
466 },
467 },
468 returns => {
469 type => "array",
470 items => {
471 type => "object",
472 properties => {},
473 },
474 },
475 code => sub {
476 my ($param) = @_;
477
478 return PVE::Cluster::create_rrd_data(
479 "pve2-vm/$param->{vmid}", $param->{timeframe}, $param->{cf});
480 }});
481
f76a2828 482__PACKAGE__->register_method({
5c752bbf
DM
483 name => 'destroy_vm',
484 path => '{vmid}',
f76a2828
DM
485 method => 'DELETE',
486 protected => 1,
487 proxyto => 'node',
488 description => "Destroy the container (also delete all uses files).",
489 permissions => {
490 check => [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
491 },
492 parameters => {
493 additionalProperties => 0,
494 properties => {
495 node => get_standard_option('pve-node'),
496 vmid => get_standard_option('pve-vmid'),
497 },
498 },
5c752bbf 499 returns => {
f76a2828
DM
500 type => 'string',
501 },
502 code => sub {
503 my ($param) = @_;
504
505 my $rpcenv = PVE::RPCEnvironment::get();
506
507 my $authuser = $rpcenv->get_user();
508
509 my $vmid = $param->{vmid};
510
511 # test if container exists
673cf209 512 my $conf = PVE::LXC::load_config($vmid);
f76a2828 513
611fe3aa
DM
514 my $storage_cfg = cfs_read_file("storage.cfg");
515
516 my $code = sub {
673cf209
DM
517 # reload config after lock
518 $conf = PVE::LXC::load_config($vmid);
519 PVE::LXC::check_lock($conf);
520
27916659 521 PVE::LXC::destroy_lxc_container($storage_cfg, $vmid, $conf);
be5fc936 522 PVE::AccessControl::remove_vm_access($vmid);
2f9b5ead 523 PVE::Firewall::remove_vmfw_conf($vmid);
f76a2828
DM
524 };
525
611fe3aa
DM
526 my $realcmd = sub { PVE::LXC::lock_container($vmid, 1, $code); };
527
f76a2828
DM
528 return $rpcenv->fork_worker('vzdestroy', $vmid, $authuser, $realcmd);
529 }});
530
fff3a342
DM
531my $sslcert;
532
533__PACKAGE__->register_method ({
5b4657d0
DM
534 name => 'vncproxy',
535 path => '{vmid}/vncproxy',
fff3a342
DM
536 method => 'POST',
537 protected => 1,
538 permissions => {
539 check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
540 },
541 description => "Creates a TCP VNC proxy connections.",
542 parameters => {
543 additionalProperties => 0,
544 properties => {
545 node => get_standard_option('pve-node'),
546 vmid => get_standard_option('pve-vmid'),
547 websocket => {
548 optional => 1,
549 type => 'boolean',
550 description => "use websocket instead of standard VNC.",
551 },
552 },
553 },
5b4657d0 554 returns => {
fff3a342
DM
555 additionalProperties => 0,
556 properties => {
557 user => { type => 'string' },
558 ticket => { type => 'string' },
559 cert => { type => 'string' },
560 port => { type => 'integer' },
561 upid => { type => 'string' },
562 },
563 },
564 code => sub {
565 my ($param) = @_;
566
567 my $rpcenv = PVE::RPCEnvironment::get();
568
569 my $authuser = $rpcenv->get_user();
570
571 my $vmid = $param->{vmid};
572 my $node = $param->{node};
573
574 my $authpath = "/vms/$vmid";
575
576 my $ticket = PVE::AccessControl::assemble_vnc_ticket($authuser, $authpath);
577
578 $sslcert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192)
579 if !$sslcert;
580
ec2c57eb 581 my ($remip, $family);
5b4657d0 582
fff3a342 583 if ($node ne PVE::INotify::nodename()) {
85ae6211 584 ($remip, $family) = PVE::Cluster::remote_node_ip($node);
ec2c57eb
WB
585 } else {
586 $family = PVE::Tools::get_host_address_family($node);
fff3a342
DM
587 }
588
ec2c57eb
WB
589 my $port = PVE::Tools::next_vnc_port($family);
590
fff3a342
DM
591 # NOTE: vncterm VNC traffic is already TLS encrypted,
592 # so we select the fastest chipher here (or 'none'?)
5b4657d0 593 my $remcmd = $remip ?
fff3a342
DM
594 ['/usr/bin/ssh', '-t', $remip] : [];
595
d18499cf 596 my $conf = PVE::LXC::load_config($vmid, $node);
aca816ad
DM
597 my $concmd = PVE::LXC::get_console_command($vmid, $conf);
598
5b4657d0
DM
599 my $shcmd = [ '/usr/bin/dtach', '-A',
600 "/var/run/dtach/vzctlconsole$vmid",
aca816ad 601 '-r', 'winch', '-z', @$concmd];
fff3a342
DM
602
603 my $realcmd = sub {
604 my $upid = shift;
605
5b4657d0 606 syslog ('info', "starting lxc vnc proxy $upid\n");
fff3a342 607
5b4657d0 608 my $timeout = 10;
fff3a342
DM
609
610 my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
5b4657d0 611 '-timeout', $timeout, '-authpath', $authpath,
fff3a342
DM
612 '-perm', 'VM.Console'];
613
614 if ($param->{websocket}) {
5b4657d0 615 $ENV{PVE_VNC_TICKET} = $ticket; # pass ticket to vncterm
fff3a342
DM
616 push @$cmd, '-notls', '-listen', 'localhost';
617 }
618
619 push @$cmd, '-c', @$remcmd, @$shcmd;
620
621 run_command($cmd);
622
623 return;
624 };
625
626 my $upid = $rpcenv->fork_worker('vncproxy', $vmid, $authuser, $realcmd);
627
628 PVE::Tools::wait_for_vnc_port($port);
629
630 return {
631 user => $authuser,
632 ticket => $ticket,
5b4657d0
DM
633 port => $port,
634 upid => $upid,
635 cert => $sslcert,
fff3a342
DM
636 };
637 }});
638
639__PACKAGE__->register_method({
640 name => 'vncwebsocket',
641 path => '{vmid}/vncwebsocket',
642 method => 'GET',
5b4657d0 643 permissions => {
fff3a342
DM
644 description => "You also need to pass a valid ticket (vncticket).",
645 check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
646 },
647 description => "Opens a weksocket for VNC traffic.",
648 parameters => {
649 additionalProperties => 0,
650 properties => {
651 node => get_standard_option('pve-node'),
652 vmid => get_standard_option('pve-vmid'),
653 vncticket => {
654 description => "Ticket from previous call to vncproxy.",
655 type => 'string',
656 maxLength => 512,
657 },
658 port => {
659 description => "Port number returned by previous vncproxy call.",
660 type => 'integer',
661 minimum => 5900,
662 maximum => 5999,
663 },
664 },
665 },
666 returns => {
667 type => "object",
668 properties => {
669 port => { type => 'string' },
670 },
671 },
672 code => sub {
673 my ($param) = @_;
674
675 my $rpcenv = PVE::RPCEnvironment::get();
676
677 my $authuser = $rpcenv->get_user();
678
679 my $authpath = "/vms/$param->{vmid}";
680
681 PVE::AccessControl::verify_vnc_ticket($param->{vncticket}, $authuser, $authpath);
682
683 my $port = $param->{port};
5b4657d0 684
fff3a342
DM
685 return { port => $port };
686 }});
687
688__PACKAGE__->register_method ({
5b4657d0
DM
689 name => 'spiceproxy',
690 path => '{vmid}/spiceproxy',
fff3a342
DM
691 method => 'POST',
692 protected => 1,
693 proxyto => 'node',
694 permissions => {
695 check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
696 },
697 description => "Returns a SPICE configuration to connect to the CT.",
698 parameters => {
699 additionalProperties => 0,
700 properties => {
701 node => get_standard_option('pve-node'),
702 vmid => get_standard_option('pve-vmid'),
703 proxy => get_standard_option('spice-proxy', { optional => 1 }),
704 },
705 },
706 returns => get_standard_option('remote-viewer-config'),
707 code => sub {
708 my ($param) = @_;
709
710 my $vmid = $param->{vmid};
711 my $node = $param->{node};
712 my $proxy = $param->{proxy};
713
714 my $authpath = "/vms/$vmid";
715 my $permissions = 'VM.Console';
716
aca816ad
DM
717 my $conf = PVE::LXC::load_config($vmid);
718 my $concmd = PVE::LXC::get_console_command($vmid, $conf);
719
5b4657d0
DM
720 my $shcmd = ['/usr/bin/dtach', '-A',
721 "/var/run/dtach/vzctlconsole$vmid",
aca816ad 722 '-r', 'winch', '-z', @$concmd];
fff3a342
DM
723
724 my $title = "CT $vmid";
725
726 return PVE::API2Tools::run_spiceterm($authpath, $permissions, $vmid, $node, $proxy, $title, $shcmd);
727 }});
5c752bbf 728
5c752bbf
DM
729
730__PACKAGE__->register_method({
52389a07
DM
731 name => 'migrate_vm',
732 path => '{vmid}/migrate',
5c752bbf
DM
733 method => 'POST',
734 protected => 1,
735 proxyto => 'node',
52389a07 736 description => "Migrate the container to another node. Creates a new migration task.",
5c752bbf 737 permissions => {
52389a07 738 check => ['perm', '/vms/{vmid}', [ 'VM.Migrate' ]],
5c752bbf
DM
739 },
740 parameters => {
741 additionalProperties => 0,
742 properties => {
743 node => get_standard_option('pve-node'),
744 vmid => get_standard_option('pve-vmid'),
52389a07
DM
745 target => get_standard_option('pve-node', { description => "Target node." }),
746 online => {
747 type => 'boolean',
748 description => "Use online/live migration.",
749 optional => 1,
750 },
5c752bbf
DM
751 },
752 },
753 returns => {
754 type => 'string',
52389a07 755 description => "the task ID.",
5c752bbf
DM
756 },
757 code => sub {
758 my ($param) = @_;
759
760 my $rpcenv = PVE::RPCEnvironment::get();
761
762 my $authuser = $rpcenv->get_user();
763
52389a07 764 my $target = extract_param($param, 'target');
bb1ac2de 765
52389a07
DM
766 my $localnode = PVE::INotify::nodename();
767 raise_param_exc({ target => "target is local node."}) if $target eq $localnode;
bb1ac2de 768
52389a07 769 PVE::Cluster::check_cfs_quorum();
5c752bbf 770
52389a07 771 PVE::Cluster::check_node_exists($target);
27916659 772
52389a07 773 my $targetip = PVE::Cluster::remote_node_ip($target);
5c752bbf 774
52389a07 775 my $vmid = extract_param($param, 'vmid');
5c752bbf 776
52389a07
DM
777 # test if VM exists
778 PVE::LXC::load_config($vmid);
5c752bbf 779
52389a07
DM
780 # try to detect errors early
781 if (PVE::LXC::check_running($vmid)) {
782 die "cant migrate running container without --online\n"
783 if !$param->{online};
5c752bbf 784 }
5c752bbf
DM
785
786 if (PVE::HA::Config::vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
787
788 my $hacmd = sub {
789 my $upid = shift;
790
791 my $service = "ct:$vmid";
792
52389a07 793 my $cmd = ['ha-manager', 'migrate', $service, $target];
5c752bbf 794
52389a07 795 print "Executing HA migrate for CT $vmid to node $target\n";
5c752bbf
DM
796
797 PVE::Tools::run_command($cmd);
798
799 return;
800 };
801
52389a07 802 return $rpcenv->fork_worker('hamigrate', $vmid, $authuser, $hacmd);
5c752bbf
DM
803
804 } else {
805
806 my $realcmd = sub {
807 my $upid = shift;
808
52389a07
DM
809 # fixme: implement lxc container migration
810 die "lxc container migration not implemented\n";
5c752bbf
DM
811
812 return;
813 };
814
52389a07 815 return $rpcenv->fork_worker('vzmigrate', $vmid, $authuser, $realcmd);
5c752bbf
DM
816 }
817 }});
818
cc5392c8
WL
819__PACKAGE__->register_method({
820 name => 'vm_feature',
821 path => '{vmid}/feature',
822 method => 'GET',
823 proxyto => 'node',
824 protected => 1,
825 description => "Check if feature for virtual machine is available.",
826 permissions => {
827 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
828 },
829 parameters => {
830 additionalProperties => 0,
831 properties => {
832 node => get_standard_option('pve-node'),
833 vmid => get_standard_option('pve-vmid'),
834 feature => {
835 description => "Feature to check.",
836 type => 'string',
837 enum => [ 'snapshot' ],
838 },
839 snapname => get_standard_option('pve-lxc-snapshot-name', {
840 optional => 1,
841 }),
842 },
843 },
844 returns => {
845 type => "object",
846 properties => {
847 hasFeature => { type => 'boolean' },
848 #nodes => {
849 #type => 'array',
850 #items => { type => 'string' },
851 #}
852 },
853 },
854 code => sub {
855 my ($param) = @_;
856
857 my $node = extract_param($param, 'node');
858
859 my $vmid = extract_param($param, 'vmid');
860
861 my $snapname = extract_param($param, 'snapname');
862
863 my $feature = extract_param($param, 'feature');
864
865 my $conf = PVE::LXC::load_config($vmid);
866
867 if($snapname){
868 my $snap = $conf->{snapshots}->{$snapname};
869 die "snapshot '$snapname' does not exist\n" if !defined($snap);
870 $conf = $snap;
871 }
ef241384 872 my $storage_cfg = PVE::Storage::config();
cc5392c8 873 #Maybe include later
ef241384
DM
874 #my $nodelist = PVE::LXC::shared_nodes($conf, $storage_cfg);
875 my $hasFeature = PVE::LXC::has_feature($feature, $conf, $storage_cfg, $snapname);
cc5392c8
WL
876
877 return {
878 hasFeature => $hasFeature,
879 #nodes => [ keys %$nodelist ],
880 };
881 }});
bb1ac2de
DM
882
883__PACKAGE__->register_method({
884 name => 'template',
885 path => '{vmid}/template',
886 method => 'POST',
887 protected => 1,
888 proxyto => 'node',
889 description => "Create a Template.",
890 permissions => {
891 description => "You need 'VM.Allocate' permissions on /vms/{vmid}",
892 check => [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
893 },
894 parameters => {
895 additionalProperties => 0,
896 properties => {
897 node => get_standard_option('pve-node'),
898 vmid => get_standard_option('pve-vmid'),
899 },
900 },
901 returns => { type => 'null'},
902 code => sub {
903 my ($param) = @_;
904
905 my $rpcenv = PVE::RPCEnvironment::get();
906
907 my $authuser = $rpcenv->get_user();
908
909 my $node = extract_param($param, 'node');
910
911 my $vmid = extract_param($param, 'vmid');
912
913 my $updatefn = sub {
914
915 my $conf = PVE::LXC::load_config($vmid);
916 PVE::LXC::check_lock($conf);
917
918 die "unable to create template, because CT contains snapshots\n"
919 if $conf->{snapshots} && scalar(keys %{$conf->{snapshots}});
920
921 die "you can't convert a template to a template\n"
922 if PVE::LXC::is_template($conf);
923
924 die "you can't convert a CT to template if the CT is running\n"
925 if PVE::LXC::check_running($vmid);
926
927 my $realcmd = sub {
928 PVE::LXC::template_create($vmid, $conf);
929 };
930
931 $conf->{template} = 1;
932
933 PVE::LXC::write_config($vmid, $conf);
934 # and remove lxc config
935 PVE::LXC::update_lxc_config(undef, $vmid, $conf);
936
937 return $rpcenv->fork_worker('vztemplate', $vmid, $authuser, $realcmd);
938 };
939
940 PVE::LXC::lock_container($vmid, undef, $updatefn);
941
942 return undef;
943 }});
944
f76a2828 9451;