]> git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Nodes.pm
fix spelling errors
[pve-manager.git] / PVE / API2 / Nodes.pm
1 package PVE::API2::Nodes::Nodeinfo;
2
3 use strict;
4 use warnings;
5 use POSIX qw(LONG_MAX);
6 use Filesys::Df;
7 use Time::Local qw(timegm_nocheck);
8 use PVE::pvecfg;
9 use PVE::Tools;
10 use PVE::ProcFSTools;
11 use PVE::SafeSyslog;
12 use PVE::Cluster qw(cfs_read_file);
13 use PVE::INotify;
14 use PVE::Exception qw(raise raise_perm_exc);
15 use PVE::RESTHandler;
16 use PVE::RPCEnvironment;
17 use PVE::JSONSchema qw(get_standard_option);
18 use PVE::AccessControl;
19 use PVE::Storage;
20 use PVE::OpenVZ;
21 use PVE::APLInfo;
22 use PVE::QemuServer;
23 use PVE::API2::Subscription;
24 use PVE::API2::Services;
25 use PVE::API2::Network;
26 use PVE::API2::Tasks;
27 use PVE::API2::Storage::Scan;
28 use PVE::API2::Storage::Status;
29 use PVE::API2::Qemu;
30 use PVE::API2::OpenVZ;
31 use PVE::API2::VZDump;
32 use JSON;
33
34 use base qw(PVE::RESTHandler);
35
36 __PACKAGE__->register_method ({
37 subclass => "PVE::API2::Qemu",
38 path => 'qemu',
39 });
40
41 __PACKAGE__->register_method ({
42 subclass => "PVE::API2::OpenVZ",
43 path => 'openvz',
44 });
45
46 __PACKAGE__->register_method ({
47 subclass => "PVE::API2::VZDump",
48 path => 'vzdump',
49 });
50
51 __PACKAGE__->register_method ({
52 subclass => "PVE::API2::Services",
53 path => 'services',
54 });
55
56 __PACKAGE__->register_method ({
57 subclass => "PVE::API2::Subscription",
58 path => 'subscription',
59 });
60
61 __PACKAGE__->register_method ({
62 subclass => "PVE::API2::Network",
63 path => 'network',
64 });
65
66 __PACKAGE__->register_method ({
67 subclass => "PVE::API2::Tasks",
68 path => 'tasks',
69 });
70
71 __PACKAGE__->register_method ({
72 subclass => "PVE::API2::Storage::Scan",
73 path => 'scan',
74 });
75
76 __PACKAGE__->register_method ({
77 subclass => "PVE::API2::Storage::Status",
78 path => 'storage',
79 });
80
81 __PACKAGE__->register_method ({
82 name => 'index',
83 path => '',
84 method => 'GET',
85 permissions => { user => 'all' },
86 description => "Node index.",
87 parameters => {
88 additionalProperties => 0,
89 properties => {
90 node => get_standard_option('pve-node'),
91 },
92 },
93 returns => {
94 type => 'array',
95 items => {
96 type => "object",
97 properties => {},
98 },
99 links => [ { rel => 'child', href => "{name}" } ],
100 },
101 code => sub {
102 my ($param) = @_;
103
104 my $result = [
105 { name => 'version' },
106 { name => 'syslog' },
107 { name => 'status' },
108 { name => 'subscription' },
109 { name => 'tasks' },
110 { name => 'rrd' }, # fixme: remove?
111 { name => 'rrddata' },# fixme: remove?
112 { name => 'vncshell' },
113 { name => 'time' },
114 { name => 'dns' },
115 { name => 'services' },
116 { name => 'scan' },
117 { name => 'storage' },
118 { name => 'qemu' },
119 { name => 'openvz' },
120 { name => 'vzdump' },
121 { name => 'ubcfailcnt' },
122 { name => 'network' },
123 { name => 'aplinfo' },
124 { name => 'startall' },
125 { name => 'stopall' },
126 ];
127
128 return $result;
129 }});
130
131 __PACKAGE__->register_method ({
132 name => 'version',
133 path => 'version',
134 method => 'GET',
135 proxyto => 'node',
136 permissions => { user => 'all' },
137 description => "API version details",
138 parameters => {
139 additionalProperties => 0,
140 properties => {
141 node => get_standard_option('pve-node'),
142 },
143 },
144 returns => {
145 type => "object",
146 properties => {
147 version => { type => 'string' },
148 release => { type => 'string' },
149 repoid => { type => 'string' },
150 },
151 },
152 code => sub {
153 my ($resp, $param) = @_;
154
155 return PVE::pvecfg::version_info();
156 }});
157
158 __PACKAGE__->register_method({
159 name => 'beancounters_failcnt',
160 path => 'ubcfailcnt',
161 permissions => {
162 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
163 },
164 method => 'GET',
165 proxyto => 'node',
166 protected => 1, # openvz /proc entries are only readable by root
167 description => "Get user_beancounters failcnt for all active containers.",
168 parameters => {
169 additionalProperties => 0,
170 properties => {
171 node => get_standard_option('pve-node'),
172 },
173 },
174 returns => {
175 type => 'array',
176 items => {
177 type => "object",
178 properties => {
179 id => { type => 'string' },
180 failcnt => { type => 'number' },
181 },
182 },
183 },
184 code => sub {
185 my ($param) = @_;
186
187 my $ubchash = PVE::OpenVZ::read_user_beancounters();
188
189 my $res = [];
190 foreach my $vmid (keys %$ubchash) {
191 next if !$vmid;
192 push @$res, { id => $vmid, failcnt => $ubchash->{$vmid}->{failcntsum} };
193
194 }
195 return $res;
196 }});
197
198 __PACKAGE__->register_method({
199 name => 'status',
200 path => 'status',
201 method => 'GET',
202 permissions => {
203 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
204 },
205 description => "Read node status",
206 proxyto => 'node',
207 parameters => {
208 additionalProperties => 0,
209 properties => {
210 node => get_standard_option('pve-node'),
211 },
212 },
213 returns => {
214 type => "object",
215 properties => {
216
217 },
218 },
219 code => sub {
220 my ($param) = @_;
221
222 my $res = {
223 uptime => 0,
224 idle => 0,
225 };
226
227 my ($uptime, $idle) = PVE::ProcFSTools::read_proc_uptime();
228 $res->{uptime} = $uptime;
229
230 my ($avg1, $avg5, $avg15) = PVE::ProcFSTools::read_loadavg();
231 $res->{loadavg} = [ $avg1, $avg5, $avg15];
232
233 my ($sysname, $nodename, $release, $version, $machine) = POSIX::uname();
234
235 $res->{kversion} = "$sysname $release $version";
236
237 $res->{cpuinfo} = PVE::ProcFSTools::read_cpuinfo();
238
239 my $stat = PVE::ProcFSTools::read_proc_stat();
240 $res->{cpu} = $stat->{cpu};
241 $res->{wait} = $stat->{wait};
242
243 my $meminfo = PVE::ProcFSTools::read_meminfo();
244 $res->{memory} = {
245 free => $meminfo->{memfree},
246 total => $meminfo->{memtotal},
247 used => $meminfo->{memused},
248 };
249 $res->{swap} = {
250 free => $meminfo->{swapfree},
251 total => $meminfo->{swaptotal},
252 used => $meminfo->{swapused},
253 };
254
255 $res->{pveversion} = PVE::pvecfg::package() . "/" .
256 PVE::pvecfg::version_text();
257
258 my $dinfo = df('/', 1); # output is bytes
259
260 $res->{rootfs} = {
261 total => $dinfo->{blocks},
262 avail => $dinfo->{bavail},
263 used => $dinfo->{used},
264 free => $dinfo->{bavail} - $dinfo->{used},
265 };
266
267 return $res;
268 }});
269
270 __PACKAGE__->register_method({
271 name => 'node_cmd',
272 path => 'status',
273 method => 'POST',
274 permissions => {
275 check => ['perm', '/nodes/{node}', [ 'Sys.PowerMgmt' ]],
276 },
277 protected => 1,
278 description => "Reboot or shutdown a node.",
279 proxyto => 'node',
280 parameters => {
281 additionalProperties => 0,
282 properties => {
283 node => get_standard_option('pve-node'),
284 command => {
285 description => "Specify the command.",
286 type => 'string',
287 enum => [qw(reboot shutdown)],
288 },
289 },
290 },
291 returns => { type => "null" },
292 code => sub {
293 my ($param) = @_;
294
295 if ($param->{command} eq 'reboot') {
296 system ("(sleep 2;/sbin/reboot)&");
297 } elsif ($param->{command} eq 'shutdown') {
298 system ("(sleep 2;/sbin/poweroff)&");
299 }
300
301 return undef;
302 }});
303
304
305 __PACKAGE__->register_method({
306 name => 'rrd',
307 path => 'rrd',
308 method => 'GET',
309 protected => 1, # fixme: can we avoid that?
310 permissions => {
311 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
312 },
313 description => "Read node RRD statistics (returns PNG)",
314 parameters => {
315 additionalProperties => 0,
316 properties => {
317 node => get_standard_option('pve-node'),
318 timeframe => {
319 description => "Specify the time frame you are interested in.",
320 type => 'string',
321 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
322 },
323 ds => {
324 description => "The list of datasources you want to display.",
325 type => 'string', format => 'pve-configid-list',
326 },
327 cf => {
328 description => "The RRD consolidation function",
329 type => 'string',
330 enum => [ 'AVERAGE', 'MAX' ],
331 optional => 1,
332 },
333 },
334 },
335 returns => {
336 type => "object",
337 properties => {
338 filename => { type => 'string' },
339 },
340 },
341 code => sub {
342 my ($param) = @_;
343
344 return PVE::Cluster::create_rrd_graph(
345 "pve2-node/$param->{node}", $param->{timeframe},
346 $param->{ds}, $param->{cf});
347
348 }});
349
350 __PACKAGE__->register_method({
351 name => 'rrddata',
352 path => 'rrddata',
353 method => 'GET',
354 protected => 1, # fixme: can we avoid that?
355 permissions => {
356 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
357 },
358 description => "Read node RRD statistics",
359 parameters => {
360 additionalProperties => 0,
361 properties => {
362 node => get_standard_option('pve-node'),
363 timeframe => {
364 description => "Specify the time frame you are interested in.",
365 type => 'string',
366 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
367 },
368 cf => {
369 description => "The RRD consolidation function",
370 type => 'string',
371 enum => [ 'AVERAGE', 'MAX' ],
372 optional => 1,
373 },
374 },
375 },
376 returns => {
377 type => "array",
378 items => {
379 type => "object",
380 properties => {},
381 },
382 },
383 code => sub {
384 my ($param) = @_;
385
386 return PVE::Cluster::create_rrd_data(
387 "pve2-node/$param->{node}", $param->{timeframe}, $param->{cf});
388 }});
389
390 __PACKAGE__->register_method({
391 name => 'syslog',
392 path => 'syslog',
393 method => 'GET',
394 description => "Read system log",
395 proxyto => 'node',
396 permissions => {
397 check => ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
398 },
399 protected => 1,
400 parameters => {
401 additionalProperties => 0,
402 properties => {
403 node => get_standard_option('pve-node'),
404 start => {
405 type => 'integer',
406 minimum => 0,
407 optional => 1,
408 },
409 limit => {
410 type => 'integer',
411 minimum => 0,
412 optional => 1,
413 },
414 },
415 },
416 returns => {
417 type => 'array',
418 items => {
419 type => "object",
420 properties => {
421 n => {
422 description=> "Line number",
423 type=> 'integer',
424 },
425 t => {
426 description=> "Line text",
427 type => 'string',
428 }
429 }
430 }
431 },
432 code => sub {
433 my ($param) = @_;
434
435 my $rpcenv = PVE::RPCEnvironment::get();
436 my $user = $rpcenv->get_user();
437 my $node = $param->{node};
438
439 my ($count, $lines) = PVE::Tools::dump_logfile("/var/log/syslog", $param->{start}, $param->{limit});
440
441 $rpcenv->set_result_attrib('total', $count);
442
443 return $lines;
444 }});
445
446 my $sslcert;
447
448 __PACKAGE__->register_method ({
449 name => 'vncshell',
450 path => 'vncshell',
451 method => 'POST',
452 protected => 1,
453 permissions => {
454 description => "Restricted to users on realm 'pam'",
455 check => ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
456 },
457 description => "Creates a VNC Shell proxy.",
458 parameters => {
459 additionalProperties => 0,
460 properties => {
461 node => get_standard_option('pve-node'),
462 },
463 },
464 returns => {
465 additionalProperties => 0,
466 properties => {
467 user => { type => 'string' },
468 ticket => { type => 'string' },
469 cert => { type => 'string' },
470 port => { type => 'integer' },
471 upid => { type => 'string' },
472 },
473 },
474 code => sub {
475 my ($param) = @_;
476
477 my $rpcenv = PVE::RPCEnvironment::get();
478
479 my ($user, undef, $realm) = PVE::AccessControl::verify_username($rpcenv->get_user());
480
481 raise_perm_exc("realm != pam") if $realm ne 'pam';
482
483 my $node = $param->{node};
484
485 my $authpath = "/nodes/$node";
486
487 my $ticket = PVE::AccessControl::assemble_vnc_ticket($user, $authpath);
488
489 $sslcert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192)
490 if !$sslcert;
491
492 my $port = PVE::Tools::next_vnc_port();
493
494 my $remip;
495
496 if ($node ne PVE::INotify::nodename()) {
497 $remip = PVE::Cluster::remote_node_ip($node);
498 }
499
500 # NOTE: vncterm VNC traffic is already TLS encrypted,
501 # so we select the fastest chipher here (or 'none'?)
502 my $remcmd = $remip ?
503 ['/usr/bin/ssh', '-c', 'blowfish-cbc', '-t', $remip] : [];
504
505 my $shcmd = $user eq 'root@pam' ? [ "/bin/bash", "-l" ] : [ "/bin/login" ];
506
507 my $timeout = 10;
508
509 my @cmd = ('/usr/bin/vncterm', '-rfbport', $port,
510 '-timeout', $timeout, '-authpath', $authpath,
511 '-perm', 'Sys.Console', '-c', @$remcmd, @$shcmd);
512
513 my $realcmd = sub {
514 my $upid = shift;
515
516 syslog ('info', "starting vnc proxy $upid\n");
517
518 my $cmdstr = join (' ', @cmd);
519 syslog ('info', "launch command: $cmdstr");
520
521 if (system(@cmd) != 0) {
522 my $msg = "vncterm failed - $?";
523 syslog ('err', $msg);
524 return;
525 }
526
527 return;
528 };
529
530 my $upid = $rpcenv->fork_worker('vncshell', "", $user, $realcmd);
531
532 return {
533 user => $user,
534 ticket => $ticket,
535 port => $port,
536 upid => $upid,
537 cert => $sslcert,
538 };
539 }});
540
541 __PACKAGE__->register_method({
542 name => 'dns',
543 path => 'dns',
544 method => 'GET',
545 permissions => {
546 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
547 },
548 description => "Read DNS settings.",
549 proxyto => 'node',
550 parameters => {
551 additionalProperties => 0,
552 properties => {
553 node => get_standard_option('pve-node'),
554 },
555 },
556 returns => {
557 type => "object",
558 additionalProperties => 0,
559 properties => {
560 search => {
561 description => "Search domain for host-name lookup.",
562 type => 'string',
563 optional => 1,
564 },
565 dns1 => {
566 description => 'First name server IP address.',
567 type => 'string',
568 optional => 1,
569 },
570 dns2 => {
571 description => 'Second name server IP address.',
572 type => 'string',
573 optional => 1,
574 },
575 dns3 => {
576 description => 'Third name server IP address.',
577 type => 'string',
578 optional => 1,
579 },
580 },
581 },
582 code => sub {
583 my ($param) = @_;
584
585 my $res = PVE::INotify::read_file('resolvconf');
586
587 return $res;
588 }});
589
590 __PACKAGE__->register_method({
591 name => 'update_dns',
592 path => 'dns',
593 method => 'PUT',
594 description => "Write DNS settings.",
595 permissions => {
596 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
597 },
598 proxyto => 'node',
599 protected => 1,
600 parameters => {
601 additionalProperties => 0,
602 properties => {
603 node => get_standard_option('pve-node'),
604 search => {
605 description => "Search domain for host-name lookup.",
606 type => 'string',
607 },
608 dns1 => {
609 description => 'First name server IP address.',
610 type => 'string', format => 'ipv4',
611 optional => 1,
612 },
613 dns2 => {
614 description => 'Second name server IP address.',
615 type => 'string', format => 'ipv4',
616 optional => 1,
617 },
618 dns3 => {
619 description => 'Third name server IP address.',
620 type => 'string', format => 'ipv4',
621 optional => 1,
622 },
623 },
624 },
625 returns => { type => "null" },
626 code => sub {
627 my ($param) = @_;
628
629 PVE::INotify::update_file('resolvconf', $param);
630
631 return undef;
632 }});
633
634 __PACKAGE__->register_method({
635 name => 'time',
636 path => 'time',
637 method => 'GET',
638 permissions => {
639 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
640 },
641 description => "Read server time and time zone settings.",
642 proxyto => 'node',
643 parameters => {
644 additionalProperties => 0,
645 properties => {
646 node => get_standard_option('pve-node'),
647 },
648 },
649 returns => {
650 type => "object",
651 additionalProperties => 0,
652 properties => {
653 timezone => {
654 description => "Time zone",
655 type => 'string',
656 },
657 time => {
658 description => "Seconds since 1970-01-01 00:00:00 UTC.",
659 type => 'integer',
660 minimum => 1297163644,
661 },
662 localtime => {
663 description => "Seconds since 1970-01-01 00:00:00 (local time)",
664 type => 'integer',
665 minimum => 1297163644,
666 },
667 },
668 },
669 code => sub {
670 my ($param) = @_;
671
672 my $ctime = time();
673 my $ltime = timegm_nocheck(localtime($ctime));
674 my $res = {
675 timezone => PVE::INotify::read_file('timezone'),
676 time => time(),
677 localtime => $ltime,
678 };
679
680 return $res;
681 }});
682
683 __PACKAGE__->register_method({
684 name => 'set_timezone',
685 path => 'time',
686 method => 'PUT',
687 description => "Set time zone.",
688 permissions => {
689 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
690 },
691 proxyto => 'node',
692 protected => 1,
693 parameters => {
694 additionalProperties => 0,
695 properties => {
696 node => get_standard_option('pve-node'),
697 timezone => {
698 description => "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.",
699 type => 'string',
700 },
701 },
702 },
703 returns => { type => "null" },
704 code => sub {
705 my ($param) = @_;
706
707 PVE::INotify::write_file('timezone', $param->{timezone});
708
709 return undef;
710 }});
711
712 __PACKAGE__->register_method({
713 name => 'aplinfo',
714 path => 'aplinfo',
715 method => 'GET',
716 permissions => {
717 user => 'all',
718 },
719 description => "Get list of appliances.",
720 proxyto => 'node',
721 parameters => {
722 additionalProperties => 0,
723 properties => {
724 node => get_standard_option('pve-node'),
725 },
726 },
727 returns => {
728 type => 'array',
729 items => {
730 type => "object",
731 properties => {},
732 },
733 },
734 code => sub {
735 my ($param) = @_;
736
737 my $res = [];
738
739 my $list = PVE::APLInfo::load_data();
740
741 foreach my $template (keys %{$list->{all}}) {
742 my $pd = $list->{all}->{$template};
743 next if $pd->{'package'} eq 'pve-web-news';
744 push @$res, $pd;
745 }
746
747 return $res;
748 }});
749
750 __PACKAGE__->register_method({
751 name => 'apl_download',
752 path => 'aplinfo',
753 method => 'POST',
754 permissions => {
755 check => ['perm', '/storage/{storage}', ['Datastore.AllocateTemplate']],
756 },
757 description => "Download appliance templates.",
758 proxyto => 'node',
759 protected => 1,
760 parameters => {
761 additionalProperties => 0,
762 properties => {
763 node => get_standard_option('pve-node'),
764 storage => get_standard_option('pve-storage-id'),
765 template => { type => 'string', maxLength => 255 },
766 },
767 },
768 returns => { type => "string" },
769 code => sub {
770 my ($param) = @_;
771
772 my $rpcenv = PVE::RPCEnvironment::get();
773
774 my $user = $rpcenv->get_user();
775
776 my $node = $param->{node};
777
778 my $list = PVE::APLInfo::load_data();
779
780 my $template = $param->{template};
781 my $pd = $list->{all}->{$template};
782
783 raise_param_exc({ template => "no such template"}) if !$pd;
784
785 my $cfg = cfs_read_file("storage.cfg");
786 my $scfg = PVE::Storage::storage_check_enabled($cfg, $param->{storage}, $node);
787
788 die "cannot download to storage type '$scfg->{type}'"
789 if !($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs');
790
791 die "unknown template type '$pd->{type}'\n" if $pd->{type} ne 'openvz';
792
793 die "storage '$param->{storage}' does not support templates\n"
794 if !$scfg->{content}->{vztmpl};
795
796 my $src = $pd->{location};
797 my $tmpldir = PVE::Storage::get_vztmpl_dir($cfg, $param->{storage});
798 my $dest = "$tmpldir/$template";
799 my $tmpdest = "$tmpldir/${template}.tmp.$$";
800
801 my $worker = sub {
802 my $upid = shift;
803
804 print "starting template download from: $src\n";
805 print "target file: $dest\n";
806
807 eval {
808
809 if (-f $dest) {
810 my $md5 = (split (/\s/, `md5sum '$dest'`))[0];
811
812 if ($md5 && (lc($md5) eq lc($pd->{md5sum}))) {
813 print "file already exists $md5 - no need to download\n";
814 return;
815 }
816 }
817
818 local %ENV;
819 my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
820 if ($dccfg->{http_proxy}) {
821 $ENV{http_proxy} = $dccfg->{http_proxy};
822 }
823
824 my @cmd = ('/usr/bin/wget', '--progress=dot:mega', '-O', $tmpdest, $src);
825 if (system (@cmd) != 0) {
826 die "download failed - $!\n";
827 }
828
829 my $md5 = (split (/\s/, `md5sum '$tmpdest'`))[0];
830
831 if (!$md5 || (lc($md5) ne lc($pd->{md5sum}))) {
832 die "wrong checksum: $md5 != $pd->{md5sum}\n";
833 }
834
835 if (system ('mv', $tmpdest, $dest) != 0) {
836 die "unable to save file - $!\n";
837 }
838 };
839 my $err = $@;
840
841 unlink $tmpdest;
842
843 if ($err) {
844 print "\n";
845 die $err if $err;
846 }
847
848 print "download finished\n";
849 };
850
851 return $rpcenv->fork_worker('download', undef, $user, $worker);
852 }});
853
854 my $get_start_stop_list = sub {
855 my ($nodename, $autostart) = @_;
856
857 my $cc = PVE::Cluster::cfs_read_file('cluster.conf');
858 my $vmlist = PVE::Cluster::get_vmlist();
859
860 my $resList = {};
861 foreach my $vmid (keys %{$vmlist->{ids}}) {
862 my $d = $vmlist->{ids}->{$vmid};
863 my $startup;
864
865 eval {
866 return if $d->{node} ne $nodename;
867
868 my $bootorder = LONG_MAX;
869
870 if ($d->{type} eq 'openvz') {
871 my $conf = PVE::OpenVZ::load_config($vmid);
872 return if $autostart && !($conf->{onboot} && $conf->{onboot}->{value});
873
874 if ($conf->{bootorder} && defined($conf->{bootorder}->{value})) {
875 $bootorder = $conf->{bootorder}->{value};
876 }
877 $startup = { order => $bootorder };
878
879 } elsif ($d->{type} eq 'qemu') {
880 my $conf = PVE::QemuServer::load_config($vmid);
881 return if $autostart && !$conf->{onboot};
882
883 if ($conf->{startup}) {
884 $startup = PVE::QemuServer::parse_startup($conf->{startup});
885 $startup->{order} = $bootorder if !defined($startup->{order});
886 } else {
887 $startup = { order => $bootorder };
888 }
889 } else {
890 die "unknown VM type '$d->{type}'\n";
891 }
892
893 # skip ha managed VMs (started by rgmanager)
894 return if PVE::Cluster::cluster_conf_lookup_pvevm($cc, 0, $vmid, 1);
895
896 $resList->{$startup->{order}}->{$vmid} = $startup;
897 $resList->{$startup->{order}}->{$vmid}->{type} = $d->{type};
898 };
899 warn $@ if $@;
900 }
901
902 return $resList;
903 };
904
905 __PACKAGE__->register_method ({
906 name => 'startall',
907 path => 'startall',
908 method => 'POST',
909 protected => 1,
910 description => "Start all VMs and containers (when onboot=1).",
911 parameters => {
912 additionalProperties => 0,
913 properties => {
914 node => get_standard_option('pve-node'),
915 },
916 },
917 returns => {
918 type => 'string',
919 },
920 code => sub {
921 my ($param) = @_;
922
923 my $rpcenv = PVE::RPCEnvironment::get();
924 my $authuser = $rpcenv->get_user();
925
926 my $nodename = $param->{node};
927 $nodename = PVE::INotify::nodename() if $nodename eq 'localhost';
928
929 my $code = sub {
930
931 $rpcenv->{type} = 'priv'; # to start tasks in background
932
933 # wait up to 10 seconds for quorum
934 for (my $i = 10; $i >= 0; $i--) {
935 last if PVE::Cluster::check_cfs_quorum($i != 0 ? 1 : 0);
936 sleep(1);
937 }
938
939 my $startList = &$get_start_stop_list($nodename, 1);
940
941 # Note: use numeric sorting with <=>
942 foreach my $order (sort {$a <=> $b} keys %$startList) {
943 my $vmlist = $startList->{$order};
944
945 foreach my $vmid (sort {$a <=> $b} keys %$vmlist) {
946 my $d = $vmlist->{$vmid};
947
948 PVE::Cluster::check_cfs_quorum(); # abort when we loose quorum
949
950 eval {
951 my $default_delay = 0;
952 my $upid;
953
954 if ($d->{type} eq 'openvz') {
955 return if PVE::OpenVZ::check_running($vmid);
956 print STDERR "Starting CT $vmid\n";
957 $upid = PVE::API2::OpenVZ->vm_start({node => $nodename, vmid => $vmid });
958 } elsif ($d->{type} eq 'qemu') {
959 $default_delay = 3; # to redruce load
960 return if PVE::QemuServer::check_running($vmid, 1);
961 print STDERR "Starting VM $vmid\n";
962 $upid = PVE::API2::Qemu->vm_start({node => $nodename, vmid => $vmid });
963 } else {
964 die "unknown VM type '$d->{type}'\n";
965 }
966
967 my $res = PVE::Tools::upid_decode($upid);
968 while (PVE::ProcFSTools::check_process_running($res->{pid})) {
969 sleep(1);
970 }
971
972 my $status = PVE::Tools::upid_read_status($upid);
973 if ($status eq 'OK') {
974 # use default delay to reduce load
975 my $delay = defined($d->{up}) ? int($d->{up}) : $default_delay;
976 if ($delay > 0) {
977 print STDERR "Waiting for $delay seconds (startup delay)\n" if $d->{up};
978 for (my $i = 0; $i < $delay; $i++) {
979 sleep(1);
980 }
981 }
982 } else {
983 if ($d->{type} eq 'openvz') {
984 print STDERR "Starting CT $vmid failed: $status\n";
985 } elsif ($d->{type} eq 'qemu') {
986 print STDERR "Starting VM $vmid failed: status\n";
987 }
988 }
989 };
990 warn $@ if $@;
991 }
992 }
993 return;
994 };
995
996 return $rpcenv->fork_worker('startall', undef, $authuser, $code);
997 }});
998
999 my $create_stop_worker = sub {
1000 my ($nodename, $type, $vmid, $down_timeout) = @_;
1001
1002 my $upid;
1003 if ($type eq 'openvz') {
1004 return if !PVE::OpenVZ::check_running($vmid);
1005 my $timeout = defined($down_timeout) ? int($down_timeout) : 60;
1006 print STDERR "Stopping CT $vmid (timeout = $timeout seconds)\n";
1007 $upid = PVE::API2::OpenVZ->vm_shutdown({node => $nodename, vmid => $vmid,
1008 timeout => $timeout, forceStop => 1 });
1009 } elsif ($type eq 'qemu') {
1010 return if !PVE::QemuServer::check_running($vmid, 1);
1011 my $timeout = defined($down_timeout) ? int($down_timeout) : 60*3;
1012 print STDERR "Stopping VM $vmid (timeout = $timeout seconds)\n";
1013 $upid = PVE::API2::Qemu->vm_shutdown({node => $nodename, vmid => $vmid,
1014 timeout => $timeout, forceStop => 1 });
1015 } else {
1016 die "unknown VM type '$type'\n";
1017 }
1018
1019 my $res = PVE::Tools::upid_decode($upid);
1020
1021 return $res->{pid};
1022 };
1023
1024 __PACKAGE__->register_method ({
1025 name => 'stopall',
1026 path => 'stopall',
1027 method => 'POST',
1028 protected => 1,
1029 description => "Stop all VMs and Containers.",
1030 parameters => {
1031 additionalProperties => 0,
1032 properties => {
1033 node => get_standard_option('pve-node'),
1034 },
1035 },
1036 returns => {
1037 type => 'string',
1038 },
1039 code => sub {
1040 my ($param) = @_;
1041
1042 my $rpcenv = PVE::RPCEnvironment::get();
1043 my $authuser = $rpcenv->get_user();
1044
1045 my $nodename = $param->{node};
1046 $nodename = PVE::INotify::nodename() if $nodename eq 'localhost';
1047
1048 my $code = sub {
1049
1050 $rpcenv->{type} = 'priv'; # to start tasks in background
1051
1052 my $stopList = &$get_start_stop_list($nodename);
1053
1054 my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
1055 my $maxWorkers = $cpuinfo->{cpus};
1056
1057 foreach my $order (sort {$b <=> $a} keys %$stopList) {
1058 my $vmlist = $stopList->{$order};
1059 my $workers = {};
1060 foreach my $vmid (sort {$b <=> $a} keys %$vmlist) {
1061 my $d = $vmlist->{$vmid};
1062 my $pid;
1063 eval { $pid = &$create_stop_worker($nodename, $d->{type}, $vmid, $d->{down}); };
1064 warn $@ if $@;
1065 next if !$pid;
1066
1067 $workers->{$pid} = 1;
1068 while (scalar(keys %$workers) >= $maxWorkers) {
1069 foreach my $p (keys %$workers) {
1070 if (!PVE::ProcFSTools::check_process_running($p)) {
1071 delete $workers->{$p};
1072 }
1073 }
1074 sleep(1);
1075 }
1076 }
1077 while (scalar(keys %$workers)) {
1078 foreach my $p (keys %$workers) {
1079 if (!PVE::ProcFSTools::check_process_running($p)) {
1080 delete $workers->{$p};
1081 }
1082 }
1083 sleep(1);
1084 }
1085 }
1086 return;
1087 };
1088
1089 return $rpcenv->fork_worker('stopall', undef, $authuser, $code);
1090
1091 }});
1092
1093 package PVE::API2::Nodes;
1094
1095 use strict;
1096 use warnings;
1097
1098 use PVE::SafeSyslog;
1099 use PVE::Cluster;
1100 use PVE::RESTHandler;
1101 use PVE::RPCEnvironment;
1102 use PVE::API2Tools;
1103
1104 use base qw(PVE::RESTHandler);
1105
1106 __PACKAGE__->register_method ({
1107 subclass => "PVE::API2::Nodes::Nodeinfo",
1108 path => '{node}',
1109 });
1110
1111 __PACKAGE__->register_method ({
1112 name => 'index',
1113 path => '',
1114 method => 'GET',
1115 permissions => { user => 'all' },
1116 description => "Cluster node index.",
1117 parameters => {
1118 additionalProperties => 0,
1119 properties => {},
1120 },
1121 returns => {
1122 type => 'array',
1123 items => {
1124 type => "object",
1125 properties => {},
1126 },
1127 links => [ { rel => 'child', href => "{node}" } ],
1128 },
1129 code => sub {
1130 my ($param) = @_;
1131
1132 my $clinfo = PVE::Cluster::get_clinfo();
1133 my $res = [];
1134
1135 my $nodelist = PVE::Cluster::get_nodelist();
1136 my $members = PVE::Cluster::get_members();
1137 my $rrd = PVE::Cluster::rrd_dump();
1138
1139 foreach my $node (@$nodelist) {
1140 my $entry = PVE::API2Tools::extract_node_stats($node, $members, $rrd);
1141 push @$res, $entry;
1142 }
1143
1144 return $res;
1145 }});
1146
1147 1;