]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/Nodes.pm
access log rotate: try to reload not restart services
[pve-manager.git] / PVE / API2 / Nodes.pm
CommitLineData
aff192e6
DM
1package PVE::API2::Nodes::Nodeinfo;
2
3use strict;
4use warnings;
b92400b6 5use POSIX qw(LONG_MAX);
aff192e6
DM
6use Filesys::Df;
7use Time::Local qw(timegm_nocheck);
40993593 8use HTTP::Status qw(:constants);
aff192e6
DM
9use PVE::pvecfg;
10use PVE::Tools;
b289829f 11use PVE::API2Tools;
aff192e6
DM
12use PVE::ProcFSTools;
13use PVE::SafeSyslog;
c9164975 14use PVE::Cluster qw(cfs_read_file);
aff192e6 15use PVE::INotify;
05b252dc 16use PVE::Exception qw(raise raise_perm_exc raise_param_exc);
aff192e6
DM
17use PVE::RESTHandler;
18use PVE::RPCEnvironment;
19use PVE::JSONSchema qw(get_standard_option);
20use PVE::AccessControl;
c9164975 21use PVE::Storage;
4a07fced 22use PVE::Firewall;
989b7743 23use PVE::LXC;
c9164975 24use PVE::APLInfo;
34ada77a 25use PVE::Report;
10a9a736 26use PVE::HA::Env::PVE2;
7e5cb2f0 27use PVE::HA::Config;
7141ae25 28use PVE::QemuConfig;
b92400b6 29use PVE::QemuServer;
21ace8d3 30use PVE::API2::Subscription;
aff192e6
DM
31use PVE::API2::Services;
32use PVE::API2::Network;
33use PVE::API2::Tasks;
34use PVE::API2::Storage::Scan;
35use PVE::API2::Storage::Status;
36use PVE::API2::Qemu;
989b7743 37use PVE::API2::LXC;
b38516eb 38use PVE::API2::LXC::Status;
bf58f8dd 39use PVE::API2::VZDump;
21299915 40use PVE::API2::APT;
38db610a 41use PVE::API2::Ceph;
4a07fced 42use PVE::API2::Firewall::Host;
892821fd 43use PVE::API2::Replication;
3b81e495
FG
44use Digest::MD5;
45use Digest::SHA;
e781363f 46use PVE::API2::Disks;
aff192e6
DM
47use JSON;
48
49use base qw(PVE::RESTHandler);
50
51__PACKAGE__->register_method ({
52 subclass => "PVE::API2::Qemu",
53 path => 'qemu',
54});
55
989b7743
DM
56__PACKAGE__->register_method ({
57 subclass => "PVE::API2::LXC",
58 path => 'lxc',
59});
60
38db610a
DM
61__PACKAGE__->register_method ({
62 subclass => "PVE::API2::Ceph",
63 path => 'ceph',
64});
65
bf58f8dd
DM
66__PACKAGE__->register_method ({
67 subclass => "PVE::API2::VZDump",
68 path => 'vzdump',
69});
70
aff192e6
DM
71__PACKAGE__->register_method ({
72 subclass => "PVE::API2::Services",
73 path => 'services',
74});
75
21ace8d3
DM
76__PACKAGE__->register_method ({
77 subclass => "PVE::API2::Subscription",
78 path => 'subscription',
79});
80
aff192e6
DM
81__PACKAGE__->register_method ({
82 subclass => "PVE::API2::Network",
83 path => 'network',
84});
85
86__PACKAGE__->register_method ({
87 subclass => "PVE::API2::Tasks",
88 path => 'tasks',
89});
90
91__PACKAGE__->register_method ({
92 subclass => "PVE::API2::Storage::Scan",
93 path => 'scan',
94});
95
96__PACKAGE__->register_method ({
97 subclass => "PVE::API2::Storage::Status",
98 path => 'storage',
99});
100
e781363f
DC
101__PACKAGE__->register_method ({
102 subclass => "PVE::API2::Disks",
103 path => 'disks',
104});
105
21299915
DM
106__PACKAGE__->register_method ({
107 subclass => "PVE::API2::APT",
108 path => 'apt',
109});
110
4a07fced
DM
111__PACKAGE__->register_method ({
112 subclass => "PVE::API2::Firewall::Host",
113 path => 'firewall',
114});
115
892821fd
DM
116__PACKAGE__->register_method ({
117 subclass => "PVE::API2::Replication",
118 path => 'replication',
119});
120
aff192e6
DM
121__PACKAGE__->register_method ({
122 name => 'index',
123 path => '',
124 method => 'GET',
125 permissions => { user => 'all' },
126 description => "Node index.",
127 parameters => {
128 additionalProperties => 0,
129 properties => {
130 node => get_standard_option('pve-node'),
131 },
132 },
133 returns => {
134 type => 'array',
135 items => {
136 type => "object",
137 properties => {},
138 },
139 links => [ { rel => 'child', href => "{name}" } ],
140 },
141 code => sub {
142 my ($param) = @_;
143
144 my $result = [
38db610a 145 { name => 'ceph' },
e781363f 146 { name => 'disks' },
21299915 147 { name => 'apt' },
8747a9ec 148 { name => 'version' },
aff192e6
DM
149 { name => 'syslog' },
150 { name => 'status' },
21ace8d3 151 { name => 'subscription' },
34ada77a 152 { name => 'report' },
aff192e6
DM
153 { name => 'tasks' },
154 { name => 'rrd' }, # fixme: remove?
155 { name => 'rrddata' },# fixme: remove?
892821fd 156 { name => 'replication' },
aff192e6 157 { name => 'vncshell' },
2d802f8c 158 { name => 'spiceshell' },
aff192e6
DM
159 { name => 'time' },
160 { name => 'dns' },
161 { name => 'services' },
162 { name => 'scan' },
163 { name => 'storage' },
aff192e6 164 { name => 'qemu' },
989b7743 165 { name => 'lxc' },
bf58f8dd 166 { name => 'vzdump' },
aff192e6 167 { name => 'network' },
c9164975 168 { name => 'aplinfo' },
b92400b6
DM
169 { name => 'startall' },
170 { name => 'stopall' },
0455911d 171 { name => 'netstat' },
4a07fced 172 { name => 'firewall' },
aff192e6
DM
173 ];
174
175 return $result;
176 }});
177
8747a9ec
DM
178__PACKAGE__->register_method ({
179 name => 'version',
180 path => 'version',
181 method => 'GET',
182 proxyto => 'node',
183 permissions => { user => 'all' },
184 description => "API version details",
185 parameters => {
186 additionalProperties => 0,
187 properties => {
188 node => get_standard_option('pve-node'),
189 },
190 },
191 returns => {
192 type => "object",
193 properties => {
194 version => { type => 'string' },
195 release => { type => 'string' },
196 repoid => { type => 'string' },
197 },
198 },
199 code => sub {
200 my ($resp, $param) = @_;
201
202 return PVE::pvecfg::version_info();
203 }});
204
aff192e6
DM
205__PACKAGE__->register_method({
206 name => 'status',
207 path => 'status',
208 method => 'GET',
209 permissions => {
7d020b42 210 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
aff192e6
DM
211 },
212 description => "Read node status",
213 proxyto => 'node',
214 parameters => {
215 additionalProperties => 0,
216 properties => {
217 node => get_standard_option('pve-node'),
218 },
219 },
220 returns => {
221 type => "object",
222 properties => {
223
224 },
225 },
226 code => sub {
227 my ($param) = @_;
228
229 my $res = {
230 uptime => 0,
231 idle => 0,
232 };
233
234 my ($uptime, $idle) = PVE::ProcFSTools::read_proc_uptime();
235 $res->{uptime} = $uptime;
236
237 my ($avg1, $avg5, $avg15) = PVE::ProcFSTools::read_loadavg();
238 $res->{loadavg} = [ $avg1, $avg5, $avg15];
239
240 my ($sysname, $nodename, $release, $version, $machine) = POSIX::uname();
241
242 $res->{kversion} = "$sysname $release $version";
243
244 $res->{cpuinfo} = PVE::ProcFSTools::read_cpuinfo();
245
246 my $stat = PVE::ProcFSTools::read_proc_stat();
247 $res->{cpu} = $stat->{cpu};
248 $res->{wait} = $stat->{wait};
249
250 my $meminfo = PVE::ProcFSTools::read_meminfo();
251 $res->{memory} = {
252 free => $meminfo->{memfree},
253 total => $meminfo->{memtotal},
254 used => $meminfo->{memused},
255 };
20539e0c
DM
256
257 $res->{ksm} = {
258 shared => $meminfo->{memshared},
259 };
260
aff192e6
DM
261 $res->{swap} = {
262 free => $meminfo->{swapfree},
263 total => $meminfo->{swaptotal},
264 used => $meminfo->{swapused},
265 };
266
267 $res->{pveversion} = PVE::pvecfg::package() . "/" .
8747a9ec 268 PVE::pvecfg::version_text();
aff192e6
DM
269
270 my $dinfo = df('/', 1); # output is bytes
271
272 $res->{rootfs} = {
273 total => $dinfo->{blocks},
274 avail => $dinfo->{bavail},
275 used => $dinfo->{used},
276 free => $dinfo->{bavail} - $dinfo->{used},
277 };
278
279 return $res;
280 }});
281
0455911d
SP
282__PACKAGE__->register_method({
283 name => 'netstat',
284 path => 'netstat',
285 method => 'GET',
286 permissions => {
287 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
288 },
289 description => "Read tap/vm network device interface counters",
290 proxyto => 'node',
291 parameters => {
292 additionalProperties => 0,
293 properties => {
294 node => get_standard_option('pve-node'),
295 },
296 },
297 returns => {
298 type => "array",
299 items => {
300 type => "object",
301 properties => {},
302 },
303 },
304 code => sub {
305 my ($param) = @_;
306
307 my $res = [ ];
308
309 my $netdev = PVE::ProcFSTools::read_proc_net_dev();
310 foreach my $dev (keys %$netdev) {
675946f2 311 next if $dev !~ m/^(?:tap|veth)([1-9]\d*)i(\d+)$/;
0455911d
SP
312 my $vmid = $1;
313 my $netid = $2;
314
315 push(
316 @$res,
317 {
318 vmid => $vmid,
319 dev => "net$netid",
320 in => $netdev->{$dev}->{transmit},
321 out => $netdev->{$dev}->{receive},
322 }
323 );
324 }
325
326 return $res;
327 }});
328
87c3e931
SP
329__PACKAGE__->register_method({
330 name => 'execute',
331 path => 'execute',
40993593 332 method => 'POST',
87c3e931
SP
333 permissions => {
334 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
335 },
40993593 336 description => "Execute multiple commands in order.",
87c3e931 337 proxyto => 'node',
40993593 338 protected => 1, # avoid problems with proxy code
87c3e931
SP
339 parameters => {
340 additionalProperties => 0,
341 properties => {
342 node => get_standard_option('pve-node'),
40993593
DM
343 commands => {
344 description => "JSON encoded array of commands.",
345 type => "string",
346 }
87c3e931
SP
347 },
348 },
349 returns => {
350 type => 'array',
351 properties => {
352
353 },
354 },
355 code => sub {
356 my ($param) = @_;
357 my $res = [];
358
40993593
DM
359 my $rpcenv = PVE::RPCEnvironment::get();
360 my $user = $rpcenv->get_user();
361
362 my $commands = eval { decode_json($param->{commands}); };
363
87c3e931
SP
364 die "commands param did not contain valid JSON: $@" if $@;
365 die "commands is not an array" if ref($commands) ne "ARRAY";
366
367 foreach my $cmd (@$commands) {
87c3e931 368 eval {
40993593
DM
369 die "$cmd is not a valid command" if (ref($cmd) ne "HASH" || !$cmd->{path} || !$cmd->{method});
370
371 $cmd->{args} //= {};
372
373 my $path = "nodes/$param->{node}/$cmd->{path}";
374
375 my $uri_param = {};
376 my ($handler, $info) = PVE::API2->find_handler($cmd->{method}, $path, $uri_param);
377 if (!$handler || !$info) {
378 die "no handler for '$path'\n";
379 }
380
381 foreach my $p (keys %{$cmd->{args}}) {
382 raise_param_exc({ $p => "duplicate parameter" }) if defined($uri_param->{$p});
383 $uri_param->{$p} = $cmd->{args}->{$p};
384 }
385
386 # check access permissions
387 $rpcenv->check_api2_permissions($info->{permissions}, $user, $uri_param);
388
389 push @$res, {
390 status => HTTP_OK,
a51ceeb7 391 data => $handler->handle($info, $uri_param),
40993593 392 };
87c3e931 393 };
40993593
DM
394 if (my $err = $@) {
395 my $resp = { status => HTTP_INTERNAL_SERVER_ERROR };
396 if (ref($err) eq "PVE::Exception") {
397 $resp->{status} = $err->{code} if $err->{code};
398 $resp->{errors} = $err->{errors} if $err->{errors};
399 $resp->{message} = $err->{msg};
400 } else {
401 $resp->{message} = $err;
402 }
403 push @$res, $resp;
404 }
87c3e931
SP
405 }
406
407 return $res;
408 }});
409
410
aff192e6
DM
411__PACKAGE__->register_method({
412 name => 'node_cmd',
413 path => 'status',
414 method => 'POST',
415 permissions => {
7d020b42 416 check => ['perm', '/nodes/{node}', [ 'Sys.PowerMgmt' ]],
aff192e6
DM
417 },
418 protected => 1,
419 description => "Reboot or shutdown a node.",
420 proxyto => 'node',
421 parameters => {
422 additionalProperties => 0,
423 properties => {
424 node => get_standard_option('pve-node'),
425 command => {
426 description => "Specify the command.",
427 type => 'string',
428 enum => [qw(reboot shutdown)],
429 },
430 },
431 },
432 returns => { type => "null" },
433 code => sub {
434 my ($param) = @_;
435
436 if ($param->{command} eq 'reboot') {
437 system ("(sleep 2;/sbin/reboot)&");
438 } elsif ($param->{command} eq 'shutdown') {
439 system ("(sleep 2;/sbin/poweroff)&");
440 }
441
442 return undef;
443 }});
444
445
446__PACKAGE__->register_method({
447 name => 'rrd',
448 path => 'rrd',
449 method => 'GET',
450 protected => 1, # fixme: can we avoid that?
451 permissions => {
7d020b42 452 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
aff192e6
DM
453 },
454 description => "Read node RRD statistics (returns PNG)",
455 parameters => {
456 additionalProperties => 0,
457 properties => {
458 node => get_standard_option('pve-node'),
459 timeframe => {
460 description => "Specify the time frame you are interested in.",
461 type => 'string',
462 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
463 },
464 ds => {
465 description => "The list of datasources you want to display.",
466 type => 'string', format => 'pve-configid-list',
467 },
468 cf => {
469 description => "The RRD consolidation function",
470 type => 'string',
471 enum => [ 'AVERAGE', 'MAX' ],
472 optional => 1,
473 },
474 },
475 },
476 returns => {
477 type => "object",
478 properties => {
479 filename => { type => 'string' },
480 },
481 },
482 code => sub {
483 my ($param) = @_;
484
485 return PVE::Cluster::create_rrd_graph(
486 "pve2-node/$param->{node}", $param->{timeframe},
487 $param->{ds}, $param->{cf});
488
489 }});
490
491__PACKAGE__->register_method({
492 name => 'rrddata',
493 path => 'rrddata',
494 method => 'GET',
495 protected => 1, # fixme: can we avoid that?
496 permissions => {
7d020b42 497 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
aff192e6
DM
498 },
499 description => "Read node RRD statistics",
500 parameters => {
501 additionalProperties => 0,
502 properties => {
503 node => get_standard_option('pve-node'),
504 timeframe => {
505 description => "Specify the time frame you are interested in.",
506 type => 'string',
507 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
508 },
509 cf => {
510 description => "The RRD consolidation function",
511 type => 'string',
512 enum => [ 'AVERAGE', 'MAX' ],
513 optional => 1,
514 },
515 },
516 },
517 returns => {
518 type => "array",
519 items => {
520 type => "object",
521 properties => {},
522 },
523 },
524 code => sub {
525 my ($param) = @_;
526
527 return PVE::Cluster::create_rrd_data(
528 "pve2-node/$param->{node}", $param->{timeframe}, $param->{cf});
529 }});
530
531__PACKAGE__->register_method({
532 name => 'syslog',
533 path => 'syslog',
534 method => 'GET',
535 description => "Read system log",
536 proxyto => 'node',
537 permissions => {
7d020b42 538 check => ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
aff192e6
DM
539 },
540 protected => 1,
541 parameters => {
542 additionalProperties => 0,
543 properties => {
544 node => get_standard_option('pve-node'),
545 start => {
546 type => 'integer',
547 minimum => 0,
548 optional => 1,
549 },
550 limit => {
551 type => 'integer',
552 minimum => 0,
553 optional => 1,
554 },
01b753b6
TL
555 since => {
556 type=> 'string',
557 pattern => '^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$',
558 description => "Display all log since this date-time string.",
559 optional => 1,
560 },
561 until => {
562 type=> 'string',
563 pattern => '^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$',
564 description => "Display all log until this date-time string.",
565 optional => 1,
566 },
aff192e6
DM
567 },
568 },
569 returns => {
570 type => 'array',
571 items => {
572 type => "object",
573 properties => {
574 n => {
575 description=> "Line number",
576 type=> 'integer',
577 },
578 t => {
579 description=> "Line text",
580 type => 'string',
581 }
582 }
583 }
584 },
585 code => sub {
586 my ($param) = @_;
587
aff192e6
DM
588 my $rpcenv = PVE::RPCEnvironment::get();
589 my $user = $rpcenv->get_user();
590 my $node = $param->{node};
591
01b753b6
TL
592 my ($count, $lines) = PVE::Tools::dump_journal($param->{start}, $param->{limit},
593 $param->{since}, $param->{until});
ff9c330c
DM
594
595 $rpcenv->set_result_attrib('total', $count);
01b753b6
TL
596
597 return $lines;
ff9c330c
DM
598 }});
599
aff192e6
DM
600my $sslcert;
601
602__PACKAGE__->register_method ({
603 name => 'vncshell',
604 path => 'vncshell',
605 method => 'POST',
606 protected => 1,
607 permissions => {
d0289a19 608 description => "Restricted to users on realm 'pam'",
7d020b42 609 check => ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
aff192e6
DM
610 },
611 description => "Creates a VNC Shell proxy.",
612 parameters => {
613 additionalProperties => 0,
614 properties => {
615 node => get_standard_option('pve-node'),
3a76893d
DM
616 upgrade => {
617 type => 'boolean',
618 description => "Run 'apt-get dist-upgrade' instead of normal shell.",
619 optional => 1,
620 default => 0,
621 },
8dfca17e
DM
622 websocket => {
623 optional => 1,
624 type => 'boolean',
625 description => "use websocket instead of standard vnc.",
626 },
b8ac8b0c
DC
627 width => {
628 optional => 1,
629 description => "sets the width of the console in pixels.",
630 type => 'integer',
631 minimum => 16,
632 maximum => 4096,
633 },
634 height => {
635 optional => 1,
636 description => "sets the height of the console in pixels.",
637 type => 'integer',
638 minimum => 16,
639 maximum => 2160,
640 },
aff192e6
DM
641 },
642 },
643 returns => {
644 additionalProperties => 0,
645 properties => {
646 user => { type => 'string' },
647 ticket => { type => 'string' },
648 cert => { type => 'string' },
649 port => { type => 'integer' },
650 upid => { type => 'string' },
651 },
652 },
653 code => sub {
654 my ($param) = @_;
655
656 my $rpcenv = PVE::RPCEnvironment::get();
657
d553e535 658 my ($user, undef, $realm) = PVE::AccessControl::verify_username($rpcenv->get_user());
d0289a19
DM
659
660 raise_perm_exc("realm != pam") if $realm ne 'pam';
aff192e6 661
3a76893d
DM
662 raise_perm_exc('user != root@pam') if $param->{upgrade} && $user ne 'root@pam';
663
aff192e6
DM
664 my $node = $param->{node};
665
57ebda08
DM
666 my $authpath = "/nodes/$node";
667
668 my $ticket = PVE::AccessControl::assemble_vnc_ticket($user, $authpath);
669
aff192e6
DM
670 $sslcert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192)
671 if !$sslcert;
672
eb6d7497 673 my ($remip, $family);
b179a622 674
aff192e6 675 if ($node ne PVE::INotify::nodename()) {
eb6d7497
WB
676 ($remip, $family) = PVE::Cluster::remote_node_ip($node);
677 } else {
678 $family = PVE::Tools::get_host_address_family($node);
aff192e6
DM
679 }
680
eb6d7497
WB
681 my $port = PVE::Tools::next_vnc_port($family);
682
aff192e6
DM
683 # NOTE: vncterm VNC traffic is already TLS encrypted,
684 # so we select the fastest chipher here (or 'none'?)
685 my $remcmd = $remip ?
f2c68844 686 ['/usr/bin/ssh', '-t', $remip] : [];
aff192e6 687
3a76893d
DM
688 my $shcmd;
689
690 if ($user eq 'root@pam') {
691 if ($param->{upgrade}) {
ea5eb430
DM
692 my $upgradecmd = "pveupgrade --shell";
693 $upgradecmd = PVE::Tools::shellquote($upgradecmd) if $remip;
694 $shcmd = [ '/bin/bash', '-c', $upgradecmd ];
3a76893d 695 } else {
e890b92d 696 $shcmd = [ '/bin/login', '-f', 'root' ];
3a76893d
DM
697 }
698 } else {
699 $shcmd = [ '/bin/login' ];
700 }
aff192e6
DM
701
702 my $timeout = 10;
703
6d394492 704 my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
57ebda08 705 '-timeout', $timeout, '-authpath', $authpath,
8dfca17e
DM
706 '-perm', 'Sys.Console'];
707
b8ac8b0c
DC
708 if ($param->{width}) {
709 push @$cmd, '-width', $param->{width};
710 }
711
712 if ($param->{height}) {
713 push @$cmd, '-height', $param->{height};
714 }
715
8dfca17e
DM
716 if ($param->{websocket}) {
717 $ENV{PVE_VNC_TICKET} = $ticket; # pass ticket to vncterm
718 push @$cmd, '-notls', '-listen', 'localhost';
719 }
720
721 push @$cmd, '-c', @$remcmd, @$shcmd;
aff192e6
DM
722
723 my $realcmd = sub {
724 my $upid = shift;
725
726 syslog ('info', "starting vnc proxy $upid\n");
727
6d394492 728 my $cmdstr = join (' ', @$cmd);
aff192e6
DM
729 syslog ('info', "launch command: $cmdstr");
730
6d394492
DM
731 eval {
732 foreach my $k (keys %ENV) {
8dfca17e 733 next if $k eq 'PVE_VNC_TICKET';
b0d4b407 734 next if $k eq 'PATH' || $k eq 'TERM' || $k eq 'USER' || $k eq 'HOME' || $k eq 'LANG' || $k eq 'LANGUAGE';
6d394492
DM
735 delete $ENV{$k};
736 }
737 $ENV{PWD} = '/';
738
b0d4b407 739 PVE::Tools::run_command($cmd, errmsg => "vncterm failed", keeplocale => 1);
6d394492
DM
740 };
741 if (my $err = $@) {
742 syslog ('err', $err);
aff192e6
DM
743 }
744
745 return;
746 };
747
748 my $upid = $rpcenv->fork_worker('vncshell', "", $user, $realcmd);
6806a0f8
DM
749
750 PVE::Tools::wait_for_vnc_port($port);
aff192e6
DM
751
752 return {
753 user => $user,
754 ticket => $ticket,
755 port => $port,
756 upid => $upid,
757 cert => $sslcert,
758 };
759 }});
760
8dfca17e
DM
761__PACKAGE__->register_method({
762 name => 'vncwebsocket',
763 path => 'vncwebsocket',
764 method => 'GET',
765 permissions => {
766 description => "Restricted to users on realm 'pam'. You also need to pass a valid ticket (vncticket).",
767 check => ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
768 },
769 description => "Opens a weksocket for VNC traffic.",
770 parameters => {
771 additionalProperties => 0,
772 properties => {
773 node => get_standard_option('pve-node'),
774 vncticket => {
775 description => "Ticket from previous call to vncproxy.",
776 type => 'string',
777 maxLength => 512,
778 },
779 port => {
780 description => "Port number returned by previous vncproxy call.",
781 type => 'integer',
782 minimum => 5900,
783 maximum => 5999,
784 },
785 },
786 },
787 returns => {
788 type => "object",
789 properties => {
790 port => { type => 'string' },
791 },
792 },
793 code => sub {
794 my ($param) = @_;
795
796 my $rpcenv = PVE::RPCEnvironment::get();
797
798 my ($user, undef, $realm) = PVE::AccessControl::verify_username($rpcenv->get_user());
799
800 raise_perm_exc("realm != pam") if $realm ne 'pam';
801
802 my $authpath = "/nodes/$param->{node}";
803
804 PVE::AccessControl::verify_vnc_ticket($param->{vncticket}, $user, $authpath);
805
806 my $port = $param->{port};
807
808 return { port => $port };
809 }});
810
2d802f8c
DM
811__PACKAGE__->register_method ({
812 name => 'spiceshell',
813 path => 'spiceshell',
814 method => 'POST',
815 protected => 1,
816 proxyto => 'node',
817 permissions => {
818 description => "Restricted to users on realm 'pam'",
819 check => ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
820 },
7774df78 821 description => "Creates a SPICE shell.",
2d802f8c
DM
822 parameters => {
823 additionalProperties => 0,
824 properties => {
825 node => get_standard_option('pve-node'),
7774df78 826 proxy => get_standard_option('spice-proxy', { optional => 1 }),
2d802f8c
DM
827 upgrade => {
828 type => 'boolean',
829 description => "Run 'apt-get dist-upgrade' instead of normal shell.",
830 optional => 1,
831 default => 0,
832 },
833 },
834 },
7774df78 835 returns => get_standard_option('remote-viewer-config'),
2d802f8c
DM
836 code => sub {
837 my ($param) = @_;
838
839 my $rpcenv = PVE::RPCEnvironment::get();
840 my $authuser = $rpcenv->get_user();
841
842 my ($user, undef, $realm) = PVE::AccessControl::verify_username($authuser);
843
844 raise_perm_exc("realm != pam") if $realm ne 'pam';
845
846 raise_perm_exc('user != root@pam') if $param->{upgrade} && $user ne 'root@pam';
847
848 my $node = $param->{node};
849 my $proxy = $param->{proxy};
2d802f8c
DM
850
851 my $authpath = "/nodes/$node";
b289829f 852 my $permissions = 'Sys.Console';
2d802f8c
DM
853
854 my $shcmd;
855
856 if ($user eq 'root@pam') {
857 if ($param->{upgrade}) {
858 my $upgradecmd = "pveupgrade --shell";
859 $shcmd = [ '/bin/bash', '-c', $upgradecmd ];
860 } else {
e890b92d 861 $shcmd = [ '/bin/login', '-f', 'root' ];
2d802f8c
DM
862 }
863 } else {
864 $shcmd = [ '/bin/login' ];
865 }
866
b289829f 867 my $title = "Shell on '$node'";
2d802f8c 868
b289829f 869 return PVE::API2Tools::run_spiceterm($authpath, $permissions, 0, $node, $proxy, $title, $shcmd);
2d802f8c
DM
870 }});
871
aff192e6
DM
872__PACKAGE__->register_method({
873 name => 'dns',
874 path => 'dns',
875 method => 'GET',
876 permissions => {
7d020b42 877 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
aff192e6
DM
878 },
879 description => "Read DNS settings.",
880 proxyto => 'node',
881 parameters => {
882 additionalProperties => 0,
883 properties => {
884 node => get_standard_option('pve-node'),
885 },
886 },
887 returns => {
888 type => "object",
889 additionalProperties => 0,
890 properties => {
891 search => {
892 description => "Search domain for host-name lookup.",
893 type => 'string',
894 optional => 1,
895 },
896 dns1 => {
897 description => 'First name server IP address.',
898 type => 'string',
899 optional => 1,
900 },
901 dns2 => {
902 description => 'Second name server IP address.',
903 type => 'string',
904 optional => 1,
905 },
906 dns3 => {
907 description => 'Third name server IP address.',
908 type => 'string',
909 optional => 1,
910 },
911 },
912 },
913 code => sub {
914 my ($param) = @_;
915
916 my $res = PVE::INotify::read_file('resolvconf');
917
918 return $res;
919 }});
920
921__PACKAGE__->register_method({
922 name => 'update_dns',
923 path => 'dns',
924 method => 'PUT',
925 description => "Write DNS settings.",
d0289a19
DM
926 permissions => {
927 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
928 },
aff192e6
DM
929 proxyto => 'node',
930 protected => 1,
931 parameters => {
932 additionalProperties => 0,
933 properties => {
934 node => get_standard_option('pve-node'),
935 search => {
936 description => "Search domain for host-name lookup.",
937 type => 'string',
938 },
939 dns1 => {
940 description => 'First name server IP address.',
7a9486a7 941 type => 'string', format => 'ip',
aff192e6
DM
942 optional => 1,
943 },
944 dns2 => {
945 description => 'Second name server IP address.',
7a9486a7 946 type => 'string', format => 'ip',
aff192e6
DM
947 optional => 1,
948 },
949 dns3 => {
950 description => 'Third name server IP address.',
7a9486a7 951 type => 'string', format => 'ip',
aff192e6
DM
952 optional => 1,
953 },
954 },
955 },
956 returns => { type => "null" },
957 code => sub {
958 my ($param) = @_;
959
960 PVE::INotify::update_file('resolvconf', $param);
961
962 return undef;
963 }});
964
965__PACKAGE__->register_method({
966 name => 'time',
967 path => 'time',
968 method => 'GET',
969 permissions => {
7d020b42
DM
970 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
971 },
aff192e6
DM
972 description => "Read server time and time zone settings.",
973 proxyto => 'node',
974 parameters => {
975 additionalProperties => 0,
976 properties => {
977 node => get_standard_option('pve-node'),
978 },
979 },
980 returns => {
981 type => "object",
982 additionalProperties => 0,
983 properties => {
984 timezone => {
985 description => "Time zone",
986 type => 'string',
987 },
988 time => {
989 description => "Seconds since 1970-01-01 00:00:00 UTC.",
990 type => 'integer',
991 minimum => 1297163644,
992 },
993 localtime => {
994 description => "Seconds since 1970-01-01 00:00:00 (local time)",
995 type => 'integer',
996 minimum => 1297163644,
997 },
998 },
999 },
1000 code => sub {
1001 my ($param) = @_;
1002
1003 my $ctime = time();
1004 my $ltime = timegm_nocheck(localtime($ctime));
1005 my $res = {
1006 timezone => PVE::INotify::read_file('timezone'),
1007 time => time(),
1008 localtime => $ltime,
1009 };
1010
1011 return $res;
1012 }});
1013
1014__PACKAGE__->register_method({
1015 name => 'set_timezone',
1016 path => 'time',
1017 method => 'PUT',
1018 description => "Set time zone.",
d0289a19
DM
1019 permissions => {
1020 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
1021 },
aff192e6
DM
1022 proxyto => 'node',
1023 protected => 1,
1024 parameters => {
1025 additionalProperties => 0,
1026 properties => {
1027 node => get_standard_option('pve-node'),
1028 timezone => {
1029 description => "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.",
1030 type => 'string',
1031 },
1032 },
1033 },
1034 returns => { type => "null" },
1035 code => sub {
1036 my ($param) = @_;
1037
1038 PVE::INotify::write_file('timezone', $param->{timezone});
1039
1040 return undef;
1041 }});
1042
c9164975
DM
1043__PACKAGE__->register_method({
1044 name => 'aplinfo',
1045 path => 'aplinfo',
1046 method => 'GET',
1047 permissions => {
1048 user => 'all',
1049 },
1050 description => "Get list of appliances.",
1051 proxyto => 'node',
1052 parameters => {
1053 additionalProperties => 0,
1054 properties => {
1055 node => get_standard_option('pve-node'),
1056 },
1057 },
1058 returns => {
1059 type => 'array',
1060 items => {
1061 type => "object",
1062 properties => {},
1063 },
1064 },
1065 code => sub {
1066 my ($param) = @_;
1067
1068 my $res = [];
1069
1070 my $list = PVE::APLInfo::load_data();
1071
1072 foreach my $template (keys %{$list->{all}}) {
1073 my $pd = $list->{all}->{$template};
1074 next if $pd->{'package'} eq 'pve-web-news';
1075 push @$res, $pd;
1076 }
1077
1078 return $res;
1079 }});
1080
0532bd28
DM
1081__PACKAGE__->register_method({
1082 name => 'apl_download',
1083 path => 'aplinfo',
1084 method => 'POST',
1085 permissions => {
1086 check => ['perm', '/storage/{storage}', ['Datastore.AllocateTemplate']],
1087 },
1088 description => "Download appliance templates.",
1089 proxyto => 'node',
1090 protected => 1,
1091 parameters => {
1092 additionalProperties => 0,
1093 properties => {
1094 node => get_standard_option('pve-node'),
82282acf 1095 storage => get_standard_option('pve-storage-id', {
62180d0f 1096 description => "The storage where the template will be stored",
82282acf
WL
1097 completion => \&PVE::Storage::complete_storage_enabled,
1098 }),
1099 template => { type => 'string',
1100 description => "The template wich will downloaded",
1101 maxLength => 255,
1102 completion => \&complete_templet_repo,
1103 },
0532bd28
DM
1104 },
1105 },
1106 returns => { type => "string" },
1107 code => sub {
1108 my ($param) = @_;
1109
1110 my $rpcenv = PVE::RPCEnvironment::get();
1111
1112 my $user = $rpcenv->get_user();
1113
1114 my $node = $param->{node};
1115
1116 my $list = PVE::APLInfo::load_data();
1117
1118 my $template = $param->{template};
1119 my $pd = $list->{all}->{$template};
1120
1121 raise_param_exc({ template => "no such template"}) if !$pd;
1122
bbcfdc08 1123 my $cfg = PVE::Storage::config();
0532bd28
DM
1124 my $scfg = PVE::Storage::storage_check_enabled($cfg, $param->{storage}, $node);
1125
761a2b31
DM
1126 die "unknown template type '$pd->{type}'\n"
1127 if !($pd->{type} eq 'openvz' || $pd->{type} eq 'lxc');
0532bd28
DM
1128
1129 die "storage '$param->{storage}' does not support templates\n"
1130 if !$scfg->{content}->{vztmpl};
1131
1132 my $src = $pd->{location};
1133 my $tmpldir = PVE::Storage::get_vztmpl_dir($cfg, $param->{storage});
1134 my $dest = "$tmpldir/$template";
1135 my $tmpdest = "$tmpldir/${template}.tmp.$$";
1136
1137 my $worker = sub {
1138 my $upid = shift;
1139
1140 print "starting template download from: $src\n";
1141 print "target file: $dest\n";
1142
3b81e495
FG
1143 my $check_hash = sub {
1144 my ($template_info, $filename, $noerr) = @_;
1145
1146 my $digest;
1147 my $expected;
1148
1149 eval {
79be6db3
FG
1150 open(my $fh, '<', $filename) or die "Can't open '$filename': $!";
1151 binmode($fh);
95f99e8c
FG
1152 if (defined($template_info->{sha512sum})) {
1153 $expected = $template_info->{sha512sum};
79be6db3 1154 $digest = Digest::SHA->new(512)->addfile($fh)->hexdigest;
95f99e8c 1155 } elsif (defined($template_info->{md5sum})) {
3b81e495 1156 #fallback to MD5
95f99e8c 1157 $expected = $template_info->{md5sum};
3b81e495 1158 $digest = Digest::MD5->new->addfile($fh)->hexdigest;
3b81e495
FG
1159 } else {
1160 die "no expected checksum defined";
1161 }
79be6db3 1162 close($fh);
3b81e495
FG
1163 };
1164
1165 die "checking hash failed - $@\n" if $@ && !$noerr;
0532bd28 1166
3b81e495
FG
1167 return ($digest, $digest ? lc($digest) eq lc($expected) : 0);
1168 };
1169
1170 eval {
0532bd28 1171 if (-f $dest) {
3b81e495 1172 my ($hash, $correct) = &$check_hash($pd, $dest, 1);
0532bd28 1173
3b81e495
FG
1174 if ($hash && $correct) {
1175 print "file already exists $hash - no need to download\n";
0532bd28
DM
1176 return;
1177 }
1178 }
1179
1180 local %ENV;
1181 my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
1182 if ($dccfg->{http_proxy}) {
1183 $ENV{http_proxy} = $dccfg->{http_proxy};
1184 }
1185
1186 my @cmd = ('/usr/bin/wget', '--progress=dot:mega', '-O', $tmpdest, $src);
1187 if (system (@cmd) != 0) {
1188 die "download failed - $!\n";
1189 }
1190
3b81e495
FG
1191 my ($hash, $correct) = &$check_hash($pd, $tmpdest);
1192
1193 die "could not calculate checksum\n" if !$hash;
0532bd28 1194
3b81e495
FG
1195 if (!$correct) {
1196 my $expected = $pd->{sha512sum} // $pd->{md5sum};
1197 die "wrong checksum: $hash != $expected\n";
0532bd28
DM
1198 }
1199
c351eda9 1200 if (!rename($tmpdest, $dest)) {
0532bd28
DM
1201 die "unable to save file - $!\n";
1202 }
1203 };
1204 my $err = $@;
1205
1206 unlink $tmpdest;
1207
1208 if ($err) {
1209 print "\n";
1210 die $err if $err;
1211 }
1212
1213 print "download finished\n";
1214 };
1215
1216 return $rpcenv->fork_worker('download', undef, $user, $worker);
1217 }});
1218
34ada77a
EK
1219__PACKAGE__->register_method({
1220 name => 'report',
1221 path => 'report',
1222 method => 'GET',
1223 permissions => {
1224 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
1225 },
6bbdf981 1226 protected => 1,
34ada77a
EK
1227 description => "Gather various systems information about a node",
1228 proxyto => 'node',
1229 parameters => {
1230 additionalProperties => 0,
1231 properties => {
1232 node => get_standard_option('pve-node'),
1233 },
1234 },
1235 returns => {
cdb5a209 1236 type => 'string',
34ada77a
EK
1237 },
1238 code => sub {
cdb5a209 1239 return PVE::Report::generate();
34ada77a 1240 }});
0532bd28 1241
b72cdbb7
TL
1242# returns a list of VMIDs, those can be filtered by
1243# * current parent node
1244# * vmid whitelist
1245# * guest is a template (default: skip)
1246# * guest is HA manged (default: skip)
1247my $get_filtered_vmlist = sub {
1248 my ($nodename, $vmfilter, $templates, $ha_managed) = @_;
b92400b6 1249
b92400b6
DM
1250 my $vmlist = PVE::Cluster::get_vmlist();
1251
b72cdbb7 1252 my $vms_allowed = {};
2a498506 1253 if (defined($vmfilter)) {
2a498506 1254 foreach my $vmid (PVE::Tools::split_list($vmfilter)) {
b72cdbb7 1255 $vms_allowed->{$vmid} = 1;
2a498506
DC
1256 }
1257 }
1258
b72cdbb7 1259 my $res = {};
b92400b6 1260 foreach my $vmid (keys %{$vmlist->{ids}}) {
b72cdbb7 1261 next if %$vms_allowed && !$vms_allowed->{$vmid};
b92400b6 1262
b72cdbb7
TL
1263 my $d = $vmlist->{ids}->{$vmid};
1264 next if $nodename && $d->{node} ne $nodename;
b92400b6 1265
b72cdbb7
TL
1266 eval {
1267 my $class;
2c27e4b7 1268 if ($d->{type} eq 'lxc') {
b72cdbb7 1269 $class = 'PVE::LXC::Config';
2c27e4b7 1270 } elsif ($d->{type} eq 'qemu') {
b72cdbb7 1271 $class = 'PVE::QemuConfig';
b92400b6
DM
1272 } else {
1273 die "unknown VM type '$d->{type}'\n";
1274 }
1275
b72cdbb7
TL
1276 my $conf = $class->load_config($vmid);
1277 return if !$templates && $class->is_template($conf);
1278 return if !$ha_managed && PVE::HA::Config::vm_is_ha_managed($vmid);
04b2004b 1279
b2bb6d77 1280 $res->{$vmid}->{conf} = $conf;
b72cdbb7 1281 $res->{$vmid}->{type} = $d->{type};
1c8dc310 1282 $res->{$vmid}->{class} = $class;
b92400b6
DM
1283 };
1284 warn $@ if $@;
1285 }
1286
b72cdbb7
TL
1287 return $res;
1288};
1289
1290# return all VMs which should get started/stopped on power up/down
1291my $get_start_stop_list = sub {
1292 my ($nodename, $autostart, $vmfilter) = @_;
1293
ae9d10ca
TL
1294 # do not skip HA vms on force or if a specific VMID set is wanted
1295 my $include_ha_managed = defined($vmfilter) ? 1 : 0;
1296
1297 my $vmlist = &$get_filtered_vmlist($nodename, $vmfilter, undef, $include_ha_managed);
b72cdbb7
TL
1298
1299 my $resList = {};
1300 foreach my $vmid (keys %$vmlist) {
b2bb6d77 1301 my $conf = $vmlist->{$vmid}->{conf};
b72cdbb7
TL
1302
1303 next if $autostart && !$conf->{onboot};
1304
1305 my $startup = {};
1306 if ($conf->{startup}) {
1307 $startup = PVE::JSONSchema::pve_parse_startup_order($conf->{startup});
1308 }
1309
1310 $startup->{order} = LONG_MAX if !defined($startup->{order});
1311
1312 $resList->{$startup->{order}}->{$vmid} = $startup;
23b54109 1313 $resList->{$startup->{order}}->{$vmid}->{type} = $vmlist->{$vmid}->{type};
b72cdbb7
TL
1314 }
1315
b92400b6
DM
1316 return $resList;
1317};
1318
1c8dc310
TL
1319my $remove_locks_on_startup = sub {
1320 my ($nodename) = @_;
1321
1322 my $vmlist = &$get_filtered_vmlist($nodename, undef, undef, 1);
1323
1324 foreach my $vmid (keys %$vmlist) {
1325 my $conf = $vmlist->{$vmid}->{conf};
23b54109 1326 my $class = $vmlist->{$vmid}->{class};
1c8dc310
TL
1327
1328 eval {
1329 if ($class->has_lock($conf, 'backup')) {
1330 $class->remove_lock($vmid, 'backup');
1331 my $msg = "removed left over backup lock from '$vmid'!";
1332 warn "$msg\n"; # prints to task log
1333 syslog('warning', $msg);
1334 }
1335 }; warn $@ if $@;
1336 }
1337};
1338
b92400b6
DM
1339__PACKAGE__->register_method ({
1340 name => 'startall',
1341 path => 'startall',
1342 method => 'POST',
1343 protected => 1,
17e3b3b2
CS
1344 permissions => {
1345 check => ['perm', '/', [ 'VM.PowerMgmt' ]],
1346 },
9c8a09a7 1347 proxyto => 'node',
b92400b6
DM
1348 description => "Start all VMs and containers (when onboot=1).",
1349 parameters => {
1350 additionalProperties => 0,
1351 properties => {
1352 node => get_standard_option('pve-node'),
c09c7160
AD
1353 force => {
1354 optional => 1,
1355 type => 'boolean',
1356 description => "force if onboot=0.",
1357 },
2a498506
DC
1358 vms => {
1359 description => "Only consider Guests with these IDs.",
1360 type => 'string', format => 'pve-vmid-list',
1361 optional => 1,
1362 },
b92400b6
DM
1363 },
1364 },
1365 returns => {
1366 type => 'string',
1367 },
1368 code => sub {
1369 my ($param) = @_;
1370
1371 my $rpcenv = PVE::RPCEnvironment::get();
1372 my $authuser = $rpcenv->get_user();
1373
1374 my $nodename = $param->{node};
1375 $nodename = PVE::INotify::nodename() if $nodename eq 'localhost';
1376
c09c7160
AD
1377 my $force = $param->{force};
1378
b92400b6
DM
1379 my $code = sub {
1380
1381 $rpcenv->{type} = 'priv'; # to start tasks in background
1382
3947d0a0
DM
1383 if (!PVE::Cluster::check_cfs_quorum(1)) {
1384 print "waiting for quorum ...\n";
1385 do {
1386 sleep(1);
1387 } while (!PVE::Cluster::check_cfs_quorum(1));
1388 print "got quorum\n";
b92400b6 1389 }
1c8dc310
TL
1390
1391 eval { # remove backup locks, but avoid running into a scheduled backup job
1392 PVE::Tools::lock_file('/var/run/vzdump.lock', 10, $remove_locks_on_startup, $nodename);
1393 }; warn $@ if $@;
1394
c09c7160 1395 my $autostart = $force ? undef : 1;
2a498506 1396 my $startList = &$get_start_stop_list($nodename, $autostart, $param->{vms});
3f12bcfb
DM
1397
1398 # Note: use numeric sorting with <=>
5eec2b04 1399 foreach my $order (sort {$a <=> $b} keys %$startList) {
b92400b6
DM
1400 my $vmlist = $startList->{$order};
1401
3f12bcfb 1402 foreach my $vmid (sort {$a <=> $b} keys %$vmlist) {
b92400b6
DM
1403 my $d = $vmlist->{$vmid};
1404
1405 PVE::Cluster::check_cfs_quorum(); # abort when we loose quorum
b72cdbb7 1406
b92400b6
DM
1407 eval {
1408 my $default_delay = 0;
1409 my $upid;
1410
2c27e4b7
DM
1411 if ($d->{type} eq 'lxc') {
1412 return if PVE::LXC::check_running($vmid);
1413 print STDERR "Starting CT $vmid\n";
1b5e56f2 1414 $upid = PVE::API2::LXC::Status->vm_start({node => $nodename, vmid => $vmid });
2c27e4b7 1415 } elsif ($d->{type} eq 'qemu') {
d6c49392 1416 $default_delay = 3; # to reduce load
b92400b6
DM
1417 return if PVE::QemuServer::check_running($vmid, 1);
1418 print STDERR "Starting VM $vmid\n";
1419 $upid = PVE::API2::Qemu->vm_start({node => $nodename, vmid => $vmid });
1420 } else {
1421 die "unknown VM type '$d->{type}'\n";
1422 }
1423
1424 my $res = PVE::Tools::upid_decode($upid);
1425 while (PVE::ProcFSTools::check_process_running($res->{pid})) {
1426 sleep(1);
1427 }
1428
1429 my $status = PVE::Tools::upid_read_status($upid);
1430 if ($status eq 'OK') {
1431 # use default delay to reduce load
1432 my $delay = defined($d->{up}) ? int($d->{up}) : $default_delay;
1433 if ($delay > 0) {
1434 print STDERR "Waiting for $delay seconds (startup delay)\n" if $d->{up};
1435 for (my $i = 0; $i < $delay; $i++) {
1436 sleep(1);
1437 }
1438 }
1439 } else {
2c27e4b7
DM
1440 if ($d->{type} eq 'lxc') {
1441 print STDERR "Starting CT $vmid failed: $status\n";
1442 } elsif ($d->{type} eq 'qemu') {
1443 print STDERR "Starting VM $vmid failed: status\n";
1444 }
b92400b6
DM
1445 }
1446 };
1447 warn $@ if $@;
1448 }
1449 }
1450 return;
1451 };
1452
1453 return $rpcenv->fork_worker('startall', undef, $authuser, $code);
1454 }});
1455
1456my $create_stop_worker = sub {
1457 my ($nodename, $type, $vmid, $down_timeout) = @_;
1458
1459 my $upid;
2c27e4b7
DM
1460 if ($type eq 'lxc') {
1461 return if !PVE::LXC::check_running($vmid);
1462 my $timeout = defined($down_timeout) ? int($down_timeout) : 60;
1463 print STDERR "Stopping CT $vmid (timeout = $timeout seconds)\n";
1b5e56f2 1464 $upid = PVE::API2::LXC::Status->vm_shutdown({node => $nodename, vmid => $vmid,
2c27e4b7
DM
1465 timeout => $timeout, forceStop => 1 });
1466 } elsif ($type eq 'qemu') {
b92400b6
DM
1467 return if !PVE::QemuServer::check_running($vmid, 1);
1468 my $timeout = defined($down_timeout) ? int($down_timeout) : 60*3;
88ba9a1d 1469 print STDERR "Stopping VM $vmid (timeout = $timeout seconds)\n";
b92400b6
DM
1470 $upid = PVE::API2::Qemu->vm_shutdown({node => $nodename, vmid => $vmid,
1471 timeout => $timeout, forceStop => 1 });
1472 } else {
1473 die "unknown VM type '$type'\n";
1474 }
1475
37bec895 1476 return $upid;
b92400b6
DM
1477};
1478
1479__PACKAGE__->register_method ({
1480 name => 'stopall',
1481 path => 'stopall',
1482 method => 'POST',
1483 protected => 1,
17e3b3b2
CS
1484 permissions => {
1485 check => ['perm', '/', [ 'VM.PowerMgmt' ]],
1486 },
9c8a09a7 1487 proxyto => 'node',
b92400b6
DM
1488 description => "Stop all VMs and Containers.",
1489 parameters => {
1490 additionalProperties => 0,
1491 properties => {
1492 node => get_standard_option('pve-node'),
2a498506
DC
1493 vms => {
1494 description => "Only consider Guests with these IDs.",
1495 type => 'string', format => 'pve-vmid-list',
1496 optional => 1,
1497 },
b92400b6
DM
1498 },
1499 },
1500 returns => {
1501 type => 'string',
1502 },
1503 code => sub {
1504 my ($param) = @_;
1505
1506 my $rpcenv = PVE::RPCEnvironment::get();
1507 my $authuser = $rpcenv->get_user();
1508
1509 my $nodename = $param->{node};
1510 $nodename = PVE::INotify::nodename() if $nodename eq 'localhost';
1511
1512 my $code = sub {
1513
1514 $rpcenv->{type} = 'priv'; # to start tasks in background
1515
2a498506 1516 my $stopList = &$get_start_stop_list($nodename, undef, $param->{vms});
b92400b6
DM
1517
1518 my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
89ceb802
TL
1519 my $datacenterconfig = cfs_read_file('datacenter.cfg');
1520 # if not set by user spawn max cpu count number of workers
1521 my $maxWorkers = $datacenterconfig->{max_workers} || $cpuinfo->{cpus};
b92400b6
DM
1522
1523 foreach my $order (sort {$b <=> $a} keys %$stopList) {
1524 my $vmlist = $stopList->{$order};
1525 my $workers = {};
37bec895
DM
1526
1527 my $finish_worker = sub {
1528 my $pid = shift;
1529 my $d = $workers->{$pid};
1530 return if !$d;
1531 delete $workers->{$pid};
1532
1533 syslog('info', "end task $d->{upid}");
1534 };
1535
b92400b6
DM
1536 foreach my $vmid (sort {$b <=> $a} keys %$vmlist) {
1537 my $d = $vmlist->{$vmid};
37bec895
DM
1538 my $upid;
1539 eval { $upid = &$create_stop_worker($nodename, $d->{type}, $vmid, $d->{down}); };
b92400b6 1540 warn $@ if $@;
37bec895
DM
1541 next if !$upid;
1542
1543 my $res = PVE::Tools::upid_decode($upid, 1);
1544 next if !$res;
1545
1546 my $pid = $res->{pid};
1547
1548 $workers->{$pid} = { type => $d->{type}, upid => $upid, vmid => $vmid };
b92400b6
DM
1549 while (scalar(keys %$workers) >= $maxWorkers) {
1550 foreach my $p (keys %$workers) {
1551 if (!PVE::ProcFSTools::check_process_running($p)) {
37bec895 1552 &$finish_worker($p);
b92400b6
DM
1553 }
1554 }
1555 sleep(1);
1556 }
1557 }
1558 while (scalar(keys %$workers)) {
1559 foreach my $p (keys %$workers) {
1560 if (!PVE::ProcFSTools::check_process_running($p)) {
37bec895 1561 &$finish_worker($p);
b92400b6
DM
1562 }
1563 }
1564 sleep(1);
1565 }
1566 }
37bec895
DM
1567
1568 syslog('info', "all VMs and CTs stopped");
1569
b92400b6
DM
1570 return;
1571 };
1572
1573 return $rpcenv->fork_worker('stopall', undef, $authuser, $code);
b92400b6
DM
1574 }});
1575
9c8a09a7
AD
1576my $create_migrate_worker = sub {
1577 my ($nodename, $type, $vmid, $target) = @_;
1578
1579 my $upid;
2c27e4b7
DM
1580 if ($type eq 'lxc') {
1581 my $online = PVE::LXC::check_running($vmid) ? 1 : 0;
1582 print STDERR "Migrating CT $vmid\n";
1583 $upid = PVE::API2::LXC->migrate_vm({node => $nodename, vmid => $vmid, target => $target,
1584 online => $online });
1585 } elsif ($type eq 'qemu') {
9c8a09a7
AD
1586 my $online = PVE::QemuServer::check_running($vmid, 1) ? 1 : 0;
1587 print STDERR "Migrating VM $vmid\n";
1588 $upid = PVE::API2::Qemu->migrate_vm({node => $nodename, vmid => $vmid, target => $target,
2c27e4b7 1589 online => $online });
9c8a09a7
AD
1590 } else {
1591 die "unknown VM type '$type'\n";
1592 }
1593
1594 my $res = PVE::Tools::upid_decode($upid);
1595
1596 return $res->{pid};
1597};
1598
1599__PACKAGE__->register_method ({
1600 name => 'migrateall',
1601 path => 'migrateall',
1602 method => 'POST',
1603 proxyto => 'node',
1604 protected => 1,
17e3b3b2
CS
1605 permissions => {
1606 check => ['perm', '/', [ 'VM.Migrate' ]],
1607 },
9c8a09a7
AD
1608 description => "Migrate all VMs and Containers.",
1609 parameters => {
1610 additionalProperties => 0,
1611 properties => {
1612 node => get_standard_option('pve-node'),
1613 target => get_standard_option('pve-node', { description => "Target node." }),
1614 maxworkers => {
89ceb802
TL
1615 description => "Maximal number of parallel migration job." .
1616 " If not set use 'max_workers' from datacenter.cfg," .
1617 " one of both must be set!",
1618 optional => 1,
9c8a09a7
AD
1619 type => 'integer',
1620 minimum => 1
1621 },
2a498506
DC
1622 vms => {
1623 description => "Only consider Guests with these IDs.",
1624 type => 'string', format => 'pve-vmid-list',
1625 optional => 1,
1626 },
9c8a09a7
AD
1627 },
1628 },
1629 returns => {
1630 type => 'string',
1631 },
1632 code => sub {
1633 my ($param) = @_;
1634
1635 my $rpcenv = PVE::RPCEnvironment::get();
1636 my $authuser = $rpcenv->get_user();
1637
1638 my $nodename = $param->{node};
1639 $nodename = PVE::INotify::nodename() if $nodename eq 'localhost';
1640
1641 my $target = $param->{target};
89ceb802
TL
1642
1643 my $datacenterconfig = cfs_read_file('datacenter.cfg');
1644 # prefer parameter over datacenter cfg settings
1645 my $maxWorkers = $param->{maxworkers} || $datacenterconfig->{max_workers} ||
1646 die "either 'maxworkers' parameter or max_workers in datacenter.cfg must be set!\n";
9c8a09a7
AD
1647
1648 my $code = sub {
9c8a09a7
AD
1649 $rpcenv->{type} = 'priv'; # to start tasks in background
1650
f5c1dde5 1651 my $vmlist = &$get_filtered_vmlist($nodename, $param->{vms}, 1, 1);
9c8a09a7 1652
f5c1dde5
TL
1653 my $workers = {};
1654 foreach my $vmid (sort keys %$vmlist) {
1655 my $d = $vmlist->{$vmid};
1656 my $pid;
1657 eval { $pid = &$create_migrate_worker($nodename, $d->{type}, $vmid, $target); };
1658 warn $@ if $@;
1659 next if !$pid;
9c8a09a7 1660
f5c1dde5
TL
1661 $workers->{$pid} = 1;
1662 while (scalar(keys %$workers) >= $maxWorkers) {
9c8a09a7
AD
1663 foreach my $p (keys %$workers) {
1664 if (!PVE::ProcFSTools::check_process_running($p)) {
1665 delete $workers->{$p};
1666 }
1667 }
1668 sleep(1);
1669 }
1670 }
f5c1dde5
TL
1671 while (scalar(keys %$workers)) {
1672 foreach my $p (keys %$workers) {
1673 if (!PVE::ProcFSTools::check_process_running($p)) {
1674 delete $workers->{$p};
1675 }
1676 }
1677 sleep(1);
1678 }
9c8a09a7
AD
1679 return;
1680 };
1681
1682 return $rpcenv->fork_worker('migrateall', undef, $authuser, $code);
1683
1684 }});
1685
82282acf
WL
1686# bash completion helper
1687
1688sub complete_templet_repo {
1689 my ($cmdname, $pname, $cvalue) = @_;
1690
1691 my $repo = PVE::APLInfo::load_data();
1692 my $res = [];
1693 foreach my $templ (keys %{$repo->{all}}) {
1694 next if $templ !~ m/^$cvalue/;
1695 push @$res, $templ;
1696 }
1697
1698 return $res;
1699}
1700
aff192e6
DM
1701package PVE::API2::Nodes;
1702
1703use strict;
1704use warnings;
1705
1706use PVE::SafeSyslog;
1707use PVE::Cluster;
1708use PVE::RESTHandler;
1709use PVE::RPCEnvironment;
b193e4ac 1710use PVE::API2Tools;
aff192e6
DM
1711
1712use base qw(PVE::RESTHandler);
1713
1714__PACKAGE__->register_method ({
1715 subclass => "PVE::API2::Nodes::Nodeinfo",
1716 path => '{node}',
1717});
1718
1719__PACKAGE__->register_method ({
1720 name => 'index',
1721 path => '',
1722 method => 'GET',
1723 permissions => { user => 'all' },
1724 description => "Cluster node index.",
1725 parameters => {
1726 additionalProperties => 0,
1727 properties => {},
1728 },
1729 returns => {
1730 type => 'array',
1731 items => {
1732 type => "object",
1733 properties => {},
1734 },
b193e4ac 1735 links => [ { rel => 'child', href => "{node}" } ],
aff192e6
DM
1736 },
1737 code => sub {
1738 my ($param) = @_;
1739
1740 my $clinfo = PVE::Cluster::get_clinfo();
1741 my $res = [];
1742
b193e4ac
DM
1743 my $nodelist = PVE::Cluster::get_nodelist();
1744 my $members = PVE::Cluster::get_members();
aff192e6
DM
1745 my $rrd = PVE::Cluster::rrd_dump();
1746
b193e4ac
DM
1747 foreach my $node (@$nodelist) {
1748 my $entry = PVE::API2Tools::extract_node_stats($node, $members, $rrd);
aff192e6
DM
1749 push @$res, $entry;
1750 }
1751
1752 return $res;
1753 }});
1754
17551;