]>
git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Nodes.pm
1 package PVE
::API2
::Nodes
::Nodeinfo
;
7 use Time
::Local
qw(timegm_nocheck);
12 use PVE
::Cluster
qw(cfs_read_file);
14 use PVE
::Exception
qw(raise raise_perm_exc);
16 use PVE
::RPCEnvironment
;
17 use PVE
::JSONSchema
qw(get_standard_option);
18 use PVE
::AccessControl
;
22 use PVE
::API2
::Services
;
23 use PVE
::API2
::Network
;
25 use PVE
::API2
::Storage
::Scan
;
26 use PVE
::API2
::Storage
::Status
;
28 use PVE
::API2
::OpenVZ
;
29 use PVE
::API2
::VZDump
;
32 use base
qw(PVE::RESTHandler);
34 __PACKAGE__-
>register_method ({
35 subclass
=> "PVE::API2::Qemu",
39 __PACKAGE__-
>register_method ({
40 subclass
=> "PVE::API2::OpenVZ",
44 __PACKAGE__-
>register_method ({
45 subclass
=> "PVE::API2::VZDump",
49 __PACKAGE__-
>register_method ({
50 subclass
=> "PVE::API2::Services",
54 __PACKAGE__-
>register_method ({
55 subclass
=> "PVE::API2::Network",
59 __PACKAGE__-
>register_method ({
60 subclass
=> "PVE::API2::Tasks",
64 __PACKAGE__-
>register_method ({
65 subclass
=> "PVE::API2::Storage::Scan",
69 __PACKAGE__-
>register_method ({
70 subclass
=> "PVE::API2::Storage::Status",
74 __PACKAGE__-
>register_method ({
78 permissions
=> { user
=> 'all' },
79 description
=> "Node index.",
81 additionalProperties
=> 0,
83 node
=> get_standard_option
('pve-node'),
92 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
98 { name
=> 'version' },
100 { name
=> 'status' },
102 { name
=> 'rrd' }, # fixme: remove?
103 { name
=> 'rrddata' },# fixme: remove?
104 { name
=> 'vncshell' },
107 { name
=> 'services' },
109 { name
=> 'storage' },
111 { name
=> 'openvz' },
112 { name
=> 'vzdump' },
113 { name
=> 'ubcfailcnt' },
114 { name
=> 'network' },
115 { name
=> 'aplinfo' },
121 __PACKAGE__-
>register_method ({
126 permissions
=> { user
=> 'all' },
127 description
=> "API version details",
129 additionalProperties
=> 0,
131 node
=> get_standard_option
('pve-node'),
137 version
=> { type
=> 'string' },
138 release
=> { type
=> 'string' },
139 repoid
=> { type
=> 'string' },
143 my ($resp, $param) = @_;
145 return PVE
::pvecfg
::version_info
();
148 __PACKAGE__-
>register_method({
149 name
=> 'beancounters_failcnt',
150 path
=> 'ubcfailcnt',
152 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
156 protected
=> 1, # openvz /proc entries are only readable by root
157 description
=> "Get user_beancounters failcnt for all active containers.",
159 additionalProperties
=> 0,
161 node
=> get_standard_option
('pve-node'),
169 id
=> { type
=> 'string' },
170 failcnt
=> { type
=> 'number' },
177 my $ubchash = PVE
::OpenVZ
::read_user_beancounters
();
180 foreach my $vmid (keys %$ubchash) {
182 push @$res, { id
=> $vmid, failcnt
=> $ubchash->{$vmid}->{failcntsum
} };
188 __PACKAGE__-
>register_method({
193 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
195 description
=> "Read node status",
198 additionalProperties
=> 0,
200 node
=> get_standard_option
('pve-node'),
217 my ($uptime, $idle) = PVE
::ProcFSTools
::read_proc_uptime
();
218 $res->{uptime
} = $uptime;
220 my ($avg1, $avg5, $avg15) = PVE
::ProcFSTools
::read_loadavg
();
221 $res->{loadavg
} = [ $avg1, $avg5, $avg15];
223 my ($sysname, $nodename, $release, $version, $machine) = POSIX
::uname
();
225 $res->{kversion
} = "$sysname $release $version";
227 $res->{cpuinfo
} = PVE
::ProcFSTools
::read_cpuinfo
();
229 my $stat = PVE
::ProcFSTools
::read_proc_stat
();
230 $res->{cpu
} = $stat->{cpu
};
231 $res->{wait} = $stat->{wait};
233 my $meminfo = PVE
::ProcFSTools
::read_meminfo
();
235 free
=> $meminfo->{memfree
},
236 total
=> $meminfo->{memtotal
},
237 used
=> $meminfo->{memused
},
240 free
=> $meminfo->{swapfree
},
241 total
=> $meminfo->{swaptotal
},
242 used
=> $meminfo->{swapused
},
245 $res->{pveversion
} = PVE
::pvecfg
::package() . "/" .
246 PVE
::pvecfg
::version_text
();
248 my $dinfo = df
('/', 1); # output is bytes
251 total
=> $dinfo->{blocks
},
252 avail
=> $dinfo->{bavail
},
253 used
=> $dinfo->{used
},
254 free
=> $dinfo->{bavail
} - $dinfo->{used
},
260 __PACKAGE__-
>register_method({
265 check
=> ['perm', '/nodes/{node}', [ 'Sys.PowerMgmt' ]],
268 description
=> "Reboot or shutdown a node.",
271 additionalProperties
=> 0,
273 node
=> get_standard_option
('pve-node'),
275 description
=> "Specify the command.",
277 enum
=> [qw(reboot shutdown)],
281 returns
=> { type
=> "null" },
285 if ($param->{command
} eq 'reboot') {
286 system ("(sleep 2;/sbin/reboot)&");
287 } elsif ($param->{command
} eq 'shutdown') {
288 system ("(sleep 2;/sbin/poweroff)&");
295 __PACKAGE__-
>register_method({
299 protected
=> 1, # fixme: can we avoid that?
301 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
303 description
=> "Read node RRD statistics (returns PNG)",
305 additionalProperties
=> 0,
307 node
=> get_standard_option
('pve-node'),
309 description
=> "Specify the time frame you are interested in.",
311 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
314 description
=> "The list of datasources you want to display.",
315 type
=> 'string', format
=> 'pve-configid-list',
318 description
=> "The RRD consolidation function",
320 enum
=> [ 'AVERAGE', 'MAX' ],
328 filename
=> { type
=> 'string' },
334 return PVE
::Cluster
::create_rrd_graph
(
335 "pve2-node/$param->{node}", $param->{timeframe
},
336 $param->{ds
}, $param->{cf
});
340 __PACKAGE__-
>register_method({
344 protected
=> 1, # fixme: can we avoid that?
346 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
348 description
=> "Read node RRD statistics",
350 additionalProperties
=> 0,
352 node
=> get_standard_option
('pve-node'),
354 description
=> "Specify the time frame you are interested in.",
356 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
359 description
=> "The RRD consolidation function",
361 enum
=> [ 'AVERAGE', 'MAX' ],
376 return PVE
::Cluster
::create_rrd_data
(
377 "pve2-node/$param->{node}", $param->{timeframe
}, $param->{cf
});
380 __PACKAGE__-
>register_method({
384 description
=> "Read system log",
387 check
=> ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
391 additionalProperties
=> 0,
393 node
=> get_standard_option
('pve-node'),
412 description
=> "Line number",
416 description
=> "Line text",
425 my $rpcenv = PVE
::RPCEnvironment
::get
();
426 my $user = $rpcenv->get_user();
427 my $node = $param->{node
};
429 my ($count, $lines) = PVE
::Tools
::dump_logfile
("/var/log/syslog", $param->{start
}, $param->{limit
});
431 $rpcenv->set_result_attrib('total', $count);
438 __PACKAGE__-
>register_method ({
444 description
=> "Restricted to users on realm 'pam'",
445 check
=> ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
447 description
=> "Creates a VNC Shell proxy.",
449 additionalProperties
=> 0,
451 node
=> get_standard_option
('pve-node'),
455 additionalProperties
=> 0,
457 user
=> { type
=> 'string' },
458 ticket
=> { type
=> 'string' },
459 cert
=> { type
=> 'string' },
460 port
=> { type
=> 'integer' },
461 upid
=> { type
=> 'string' },
467 my $rpcenv = PVE
::RPCEnvironment
::get
();
469 my ($user, undef, $realm) = PVE
::AccessControl
::verify_username
($rpcenv->get_user());
471 raise_perm_exc
("realm != pam") if $realm ne 'pam';
473 my $node = $param->{node
};
475 my $authpath = "/nodes/$node";
477 my $ticket = PVE
::AccessControl
::assemble_vnc_ticket
($user, $authpath);
479 $sslcert = PVE
::Tools
::file_get_contents
("/etc/pve/pve-root-ca.pem", 8192)
482 my $port = PVE
::Tools
::next_vnc_port
();
486 if ($node ne PVE
::INotify
::nodename
()) {
487 $remip = PVE
::Cluster
::remote_node_ip
($node);
490 # NOTE: vncterm VNC traffic is already TLS encrypted,
491 # so we select the fastest chipher here (or 'none'?)
492 my $remcmd = $remip ?
493 ['/usr/bin/ssh', '-c', 'blowfish-cbc', '-t', $remip] : [];
495 my $shcmd = $user eq 'root@pam' ?
[ "/bin/bash", "-l" ] : [ "/bin/login" ];
499 my @cmd = ('/usr/bin/vncterm', '-rfbport', $port,
500 '-timeout', $timeout, '-authpath', $authpath,
501 '-perm', 'Sys.Console', '-c', @$remcmd, @$shcmd);
506 syslog
('info', "starting vnc proxy $upid\n");
508 my $cmdstr = join (' ', @cmd);
509 syslog
('info', "launch command: $cmdstr");
511 if (system(@cmd) != 0) {
512 my $msg = "vncterm failed - $?";
513 syslog
('err', $msg);
520 my $upid = $rpcenv->fork_worker('vncshell', "", $user, $realcmd);
531 __PACKAGE__-
>register_method({
536 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
538 description
=> "Read DNS settings.",
541 additionalProperties
=> 0,
543 node
=> get_standard_option
('pve-node'),
548 additionalProperties
=> 0,
551 description
=> "Search domain for host-name lookup.",
556 description
=> 'First name server IP address.',
561 description
=> 'Second name server IP address.',
566 description
=> 'Third name server IP address.',
575 my $res = PVE
::INotify
::read_file
('resolvconf');
580 __PACKAGE__-
>register_method({
581 name
=> 'update_dns',
584 description
=> "Write DNS settings.",
586 check
=> ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
591 additionalProperties
=> 0,
593 node
=> get_standard_option
('pve-node'),
595 description
=> "Search domain for host-name lookup.",
599 description
=> 'First name server IP address.',
600 type
=> 'string', format
=> 'ipv4',
604 description
=> 'Second name server IP address.',
605 type
=> 'string', format
=> 'ipv4',
609 description
=> 'Third name server IP address.',
610 type
=> 'string', format
=> 'ipv4',
615 returns
=> { type
=> "null" },
619 PVE
::INotify
::update_file
('resolvconf', $param);
624 __PACKAGE__-
>register_method({
629 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
631 description
=> "Read server time and time zone settings.",
634 additionalProperties
=> 0,
636 node
=> get_standard_option
('pve-node'),
641 additionalProperties
=> 0,
644 description
=> "Time zone",
648 description
=> "Seconds since 1970-01-01 00:00:00 UTC.",
650 minimum
=> 1297163644,
653 description
=> "Seconds since 1970-01-01 00:00:00 (local time)",
655 minimum
=> 1297163644,
663 my $ltime = timegm_nocheck
(localtime($ctime));
665 timezone
=> PVE
::INotify
::read_file
('timezone'),
673 __PACKAGE__-
>register_method({
674 name
=> 'set_timezone',
677 description
=> "Set time zone.",
679 check
=> ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
684 additionalProperties
=> 0,
686 node
=> get_standard_option
('pve-node'),
688 description
=> "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.",
693 returns
=> { type
=> "null" },
697 PVE
::INotify
::write_file
('timezone', $param->{timezone
});
702 __PACKAGE__-
>register_method({
709 description
=> "Get list of appliances.",
712 additionalProperties
=> 0,
714 node
=> get_standard_option
('pve-node'),
729 my $list = PVE
::APLInfo
::load_data
();
731 foreach my $template (keys %{$list->{all
}}) {
732 my $pd = $list->{all
}->{$template};
733 next if $pd->{'package'} eq 'pve-web-news';
740 __PACKAGE__-
>register_method({
741 name
=> 'apl_download',
745 check
=> ['perm', '/storage/{storage}', ['Datastore.AllocateTemplate']],
747 description
=> "Download appliance templates.",
751 additionalProperties
=> 0,
753 node
=> get_standard_option
('pve-node'),
754 storage
=> get_standard_option
('pve-storage-id'),
755 template
=> { type
=> 'string', maxLength
=> 255 },
758 returns
=> { type
=> "string" },
762 my $rpcenv = PVE
::RPCEnvironment
::get
();
764 my $user = $rpcenv->get_user();
766 my $node = $param->{node
};
768 my $list = PVE
::APLInfo
::load_data
();
770 my $template = $param->{template
};
771 my $pd = $list->{all
}->{$template};
773 raise_param_exc
({ template
=> "no such template"}) if !$pd;
775 my $cfg = cfs_read_file
("storage.cfg");
776 my $scfg = PVE
::Storage
::storage_check_enabled
($cfg, $param->{storage
}, $node);
778 die "cannot download to storage type '$scfg->{type}'"
779 if !($scfg->{type
} eq 'dir' || $scfg->{type
} eq 'nfs');
781 die "unknown template type '$pd->{type}'\n" if $pd->{type
} ne 'openvz';
783 die "storage '$param->{storage}' does not support templates\n"
784 if !$scfg->{content
}->{vztmpl
};
786 my $src = $pd->{location
};
787 my $tmpldir = PVE
::Storage
::get_vztmpl_dir
($cfg, $param->{storage
});
788 my $dest = "$tmpldir/$template";
789 my $tmpdest = "$tmpldir/${template}.tmp.$$";
794 print "starting template download from: $src\n";
795 print "target file: $dest\n";
800 my $md5 = (split (/\s/, `md5sum '$dest'`))[0];
802 if ($md5 && (lc($md5) eq lc($pd->{md5sum
}))) {
803 print "file already exists $md5 - no need to download\n";
809 my $dccfg = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
810 if ($dccfg->{http_proxy
}) {
811 $ENV{http_proxy
} = $dccfg->{http_proxy
};
814 my @cmd = ('/usr/bin/wget', '--progress=dot:mega', '-O', $tmpdest, $src);
815 if (system (@cmd) != 0) {
816 die "download failed - $!\n";
819 my $md5 = (split (/\s/, `md5sum '$tmpdest'`))[0];
821 if (!$md5 || (lc($md5) ne lc($pd->{md5sum
}))) {
822 die "wrong checksum: $md5 != $pd->{md5sum}\n";
825 if (system ('mv', $tmpdest, $dest) != 0) {
826 die "unable to save file - $!\n";
838 print "download finished\n";
841 return $rpcenv->fork_worker('download', undef, $user, $worker);
844 package PVE
::API2
::Nodes
;
851 use PVE
::RESTHandler
;
852 use PVE
::RPCEnvironment
;
854 use base
qw(PVE::RESTHandler);
856 __PACKAGE__-
>register_method ({
857 subclass
=> "PVE::API2::Nodes::Nodeinfo",
861 __PACKAGE__-
>register_method ({
865 permissions
=> { user
=> 'all' },
866 description
=> "Cluster node index.",
868 additionalProperties
=> 0,
877 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
882 my $clinfo = PVE
::Cluster
::get_clinfo
();
885 my $nodename = PVE
::INotify
::nodename
();
886 my $nodelist = $clinfo->{nodelist
};
888 my $rrd = PVE
::Cluster
::rrd_dump
();
890 my @nodes = $nodelist ?
(keys %$nodelist) : $nodename;
892 foreach my $node (@nodes) {
893 my $entry = { name
=> $node };
894 if (my $d = $rrd->{"pve2-node/$node"}) {
896 $entry->{uptime
} = $d->[0];
897 $entry->{maxcpu
} = $d->[3];
898 $entry->{cpu
} = $d->[4];
899 $entry->{maxmem
} = $d->[6];
900 $entry->{mem
} = $d->[7];
901 $entry->{maxdisk
} = $d->[10];
902 $entry->{disk
} = $d->[11];