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