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