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