]>
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);
15 use PVE
::RPCEnvironment
;
16 use PVE
::JSONSchema
qw(get_standard_option);
17 use PVE
::AccessControl
;
19 use PVE
::API2
::Services
;
20 use PVE
::API2
::Network
;
22 use PVE
::API2
::Storage
::Scan
;
23 use PVE
::API2
::Storage
::Status
;
25 use PVE
::API2
::OpenVZ
;
26 use PVE
::API2
::VZDump
;
29 use base
qw(PVE::RESTHandler);
31 __PACKAGE__-
>register_method ({
32 subclass
=> "PVE::API2::Qemu",
36 __PACKAGE__-
>register_method ({
37 subclass
=> "PVE::API2::OpenVZ",
41 __PACKAGE__-
>register_method ({
42 subclass
=> "PVE::API2::VZDump",
46 __PACKAGE__-
>register_method ({
47 subclass
=> "PVE::API2::Services",
51 __PACKAGE__-
>register_method ({
52 subclass
=> "PVE::API2::Network",
56 __PACKAGE__-
>register_method ({
57 subclass
=> "PVE::API2::Tasks",
61 __PACKAGE__-
>register_method ({
62 subclass
=> "PVE::API2::Storage::Scan",
66 __PACKAGE__-
>register_method ({
67 subclass
=> "PVE::API2::Storage::Status",
71 __PACKAGE__-
>register_method ({
75 permissions
=> { user
=> 'all' },
76 description
=> "Node index.",
78 additionalProperties
=> 0,
80 node
=> get_standard_option
('pve-node'),
89 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
98 { name
=> 'rrd' }, # fixme: remove?
99 { name
=> 'rrddata' },# fixme: remove?
100 { name
=> 'vncshell' },
103 { name
=> 'services' },
105 { name
=> 'storage' },
106 { name
=> 'upload' },
108 { name
=> 'openvz' },
109 { name
=> 'vzdump' },
110 { name
=> 'ubcfailcnt' },
111 { name
=> 'network' },
112 { name
=> 'network_changes' },
118 __PACKAGE__-
>register_method({
119 name
=> 'beancounters_failcnt',
120 path
=> 'ubcfailcnt',
122 path
=> '/nodes/{node}',
123 privs
=> [ 'Sys.Audit' ],
127 protected
=> 1, # openvz /proc entries are only readable by root
128 description
=> "Get user_beancounters failcnt for all active containers.",
130 additionalProperties
=> 0,
132 node
=> get_standard_option
('pve-node'),
140 id
=> { type
=> 'string' },
141 failcnt
=> { type
=> 'number' },
148 my $ubchash = PVE
::OpenVZ
::read_user_beancounters
();
151 foreach my $vmid (keys %$ubchash) {
153 push @$res, { id
=> $vmid, failcnt
=> $ubchash->{$vmid}->{failcntsum
} };
159 __PACKAGE__-
>register_method({
160 name
=> 'network_changes',
161 path
=> 'network_changes',
164 path
=> '/nodes/{node}',
165 privs
=> [ 'Sys.Audit' ],
167 description
=> "Get network configuration changes (diff) since last boot.",
170 additionalProperties
=> 0,
172 node
=> get_standard_option
('pve-node'),
175 returns
=> { type
=> "string" },
179 my $res = PVE
::INotify
::read_file
('interfaces', 1);
181 return $res->{changes
} || '';
184 __PACKAGE__-
>register_method({
185 name
=> 'revert_network_changes',
186 path
=> 'network_changes',
189 path
=> '/nodes/{node}',
190 privs
=> [ 'Sys.Modify' ],
193 description
=> "Revert network configuration changes.",
196 additionalProperties
=> 0,
198 node
=> get_standard_option
('pve-node'),
201 returns
=> { type
=> "null" },
205 unlink "/etc/network/interfaces.new";
210 __PACKAGE__-
>register_method({
215 path
=> '/nodes/{node}',
216 privs
=> [ 'Sys.Audit' ],
218 description
=> "Read node status",
221 additionalProperties
=> 0,
223 node
=> get_standard_option
('pve-node'),
240 my ($uptime, $idle) = PVE
::ProcFSTools
::read_proc_uptime
();
241 $res->{uptime
} = $uptime;
243 my ($avg1, $avg5, $avg15) = PVE
::ProcFSTools
::read_loadavg
();
244 $res->{loadavg
} = [ $avg1, $avg5, $avg15];
246 my ($sysname, $nodename, $release, $version, $machine) = POSIX
::uname
();
248 $res->{kversion
} = "$sysname $release $version";
250 $res->{cpuinfo
} = PVE
::ProcFSTools
::read_cpuinfo
();
252 my $stat = PVE
::ProcFSTools
::read_proc_stat
();
253 $res->{cpu
} = $stat->{cpu
};
254 $res->{wait} = $stat->{wait};
256 my $meminfo = PVE
::ProcFSTools
::read_meminfo
();
258 free
=> $meminfo->{memfree
},
259 total
=> $meminfo->{memtotal
},
260 used
=> $meminfo->{memused
},
263 free
=> $meminfo->{swapfree
},
264 total
=> $meminfo->{swaptotal
},
265 used
=> $meminfo->{swapused
},
268 $res->{pveversion
} = PVE
::pvecfg
::package() . "/" .
269 PVE
::pvecfg
::version
() . "/" .
270 PVE
::pvecfg
::repoid
();
272 my $dinfo = df
('/', 1); # output is bytes
275 total
=> $dinfo->{blocks
},
276 avail
=> $dinfo->{bavail
},
277 used
=> $dinfo->{used
},
278 free
=> $dinfo->{bavail
} - $dinfo->{used
},
284 __PACKAGE__-
>register_method({
289 path
=> '/nodes/{node}',
290 privs
=> [ 'Sys.PowerMgmt' ],
293 description
=> "Reboot or shutdown a node.",
296 additionalProperties
=> 0,
298 node
=> get_standard_option
('pve-node'),
300 description
=> "Specify the command.",
302 enum
=> [qw(reboot shutdown)],
306 returns
=> { type
=> "null" },
310 if ($param->{command
} eq 'reboot') {
311 system ("(sleep 2;/sbin/reboot)&");
312 } elsif ($param->{command
} eq 'shutdown') {
313 system ("(sleep 2;/sbin/poweroff)&");
320 __PACKAGE__-
>register_method({
324 protected
=> 1, # fixme: can we avoid that?
326 path
=> '/nodes/{node}',
327 privs
=> [ 'Sys.Audit' ],
329 description
=> "Read node RRD statistics (returns PNG)",
331 additionalProperties
=> 0,
333 node
=> get_standard_option
('pve-node'),
335 description
=> "Specify the time frame you are interested in.",
337 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
340 description
=> "The list of datasources you want to display.",
341 type
=> 'string', format
=> 'pve-configid-list',
344 description
=> "The RRD consolidation function",
346 enum
=> [ 'AVERAGE', 'MAX' ],
354 filename
=> { type
=> 'string' },
360 return PVE
::Cluster
::create_rrd_graph
(
361 "pve2-node/$param->{node}", $param->{timeframe
},
362 $param->{ds
}, $param->{cf
});
366 __PACKAGE__-
>register_method({
370 protected
=> 1, # fixme: can we avoid that?
372 path
=> '/nodes/{node}',
373 privs
=> [ 'Sys.Audit' ],
375 description
=> "Read node RRD statistics",
377 additionalProperties
=> 0,
379 node
=> get_standard_option
('pve-node'),
381 description
=> "Specify the time frame you are interested in.",
383 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
386 description
=> "The RRD consolidation function",
388 enum
=> [ 'AVERAGE', 'MAX' ],
403 return PVE
::Cluster
::create_rrd_data
(
404 "pve2-node/$param->{node}", $param->{timeframe
}, $param->{cf
});
407 __PACKAGE__-
>register_method({
411 description
=> "Read system log",
414 path
=> '/nodes/{node}',
415 privs
=> [ 'Sys.Syslog' ],
419 additionalProperties
=> 0,
421 node
=> get_standard_option
('pve-node'),
440 description
=> "Line number",
444 description
=> "Line text",
455 my $rpcenv = PVE
::RPCEnvironment
::get
();
456 my $user = $rpcenv->get_user();
457 my $node = $param->{node
};
459 my $fh = IO
::File-
>new("/var/log/syslog", "r");
460 die "unable to open file - $!" if !$fh;
462 my $start = $param->{start
} || 0;
463 my $limit = $param->{limit
} || 50;
466 while (defined ($line = <$fh>)) {
467 next if $count++ < $start;
470 push @$lines, { n
=> $count, t
=> $line};
476 # HACK: ExtJS store.guaranteeRange() does not like empty array
480 push @$lines, { n
=> $count, t
=> "no content"};
483 $rpcenv->set_result_count($count);
490 __PACKAGE__-
>register_method ({
496 path
=> '/nodes/{node}',
497 privs
=> [ 'Sys.Console' ],
499 description
=> "Creates a VNC Shell proxy.",
501 additionalProperties
=> 0,
503 node
=> get_standard_option
('pve-node'),
507 additionalProperties
=> 0,
509 user
=> { type
=> 'string' },
510 ticket
=> { type
=> 'string' },
511 cert
=> { type
=> 'string' },
512 port
=> { type
=> 'integer' },
513 upid
=> { type
=> 'string' },
519 my $rpcenv = PVE
::RPCEnvironment
::get
();
521 my $user = $rpcenv->get_user();
523 my $ticket = PVE
::AccessControl
::assemble_ticket
($user);
525 my $node = $param->{node
};
527 $sslcert = PVE
::Tools
::file_get_contents
("/etc/pve/pve-root-ca.pem", 8192)
530 my $port = PVE
::Tools
::next_vnc_port
();
534 if ($node ne PVE
::INotify
::nodename
()) {
535 $remip = PVE
::Cluster
::remote_node_ip
($node);
538 # NOTE: vncterm VNC traffic is already TLS encrypted,
539 # so we select the fastest chipher here (or 'none'?)
540 my $remcmd = $remip ?
541 ['/usr/bin/ssh', '-c', 'blowfish-cbc', '-t', $remip] : [];
543 my $shcmd = $user eq 'root@pam' ?
[ "/bin/bash", "-l" ] : [ "/bin/login" ];
547 # fixme: do we want to require special auth permissions?
548 # example "-perm Shell"
549 my @cmd = ('/usr/bin/vncterm', '-rfbport', $port,
550 '-timeout', $timeout, '-authpath', "/nodes/$node",
551 '-perm', 'Sys.Console', '-c', @$remcmd, @$shcmd);
556 syslog
('info', "starting vnc proxy $upid\n");
558 my $cmdstr = join (' ', @cmd);
559 syslog
('info', "launch command: $cmdstr");
561 if (system(@cmd) != 0) {
562 my $msg = "vncterm failed - $?";
563 syslog
('err', $msg);
570 my $upid = $rpcenv->fork_worker('vncshell', "", $user, $realcmd);
581 __PACKAGE__-
>register_method({
586 path
=> '/nodes/{node}',
587 privs
=> [ 'Sys.Audit' ],
589 description
=> "Read DNS settings.",
592 additionalProperties
=> 0,
594 node
=> get_standard_option
('pve-node'),
599 additionalProperties
=> 0,
602 description
=> "Search domain for host-name lookup.",
607 description
=> 'First name server IP address.',
612 description
=> 'Second name server IP address.',
617 description
=> 'Third name server IP address.',
626 my $res = PVE
::INotify
::read_file
('resolvconf');
631 __PACKAGE__-
>register_method({
632 name
=> 'update_dns',
635 description
=> "Write DNS settings.",
639 additionalProperties
=> 0,
641 node
=> get_standard_option
('pve-node'),
643 description
=> "Search domain for host-name lookup.",
647 description
=> 'First name server IP address.',
648 type
=> 'string', format
=> 'ipv4',
652 description
=> 'Second name server IP address.',
653 type
=> 'string', format
=> 'ipv4',
657 description
=> 'Third name server IP address.',
658 type
=> 'string', format
=> 'ipv4',
663 returns
=> { type
=> "null" },
667 PVE
::INotify
::update_file
('resolvconf', $param);
672 __PACKAGE__-
>register_method({
677 path
=> '/nodes/{node}',
678 privs
=> [ 'Sys.Audit' ],
680 description
=> "Read server time and time zone settings.",
683 additionalProperties
=> 0,
685 node
=> get_standard_option
('pve-node'),
690 additionalProperties
=> 0,
693 description
=> "Time zone",
697 description
=> "Seconds since 1970-01-01 00:00:00 UTC.",
699 minimum
=> 1297163644,
702 description
=> "Seconds since 1970-01-01 00:00:00 (local time)",
704 minimum
=> 1297163644,
712 my $ltime = timegm_nocheck
(localtime($ctime));
714 timezone
=> PVE
::INotify
::read_file
('timezone'),
722 __PACKAGE__-
>register_method({
723 name
=> 'set_timezone',
726 description
=> "Set time zone.",
730 additionalProperties
=> 0,
732 node
=> get_standard_option
('pve-node'),
734 description
=> "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.",
739 returns
=> { type
=> "null" },
743 PVE
::INotify
::write_file
('timezone', $param->{timezone
});
748 __PACKAGE__-
>register_method ({
753 path
=> '/storage/{storage}',
754 privs
=> [ 'Datastore.AllocateSpace' ],
756 description
=> "Upload content.",
758 additionalProperties
=> 0,
760 node
=> get_standard_option
('pve-node'),
761 storage
=> get_standard_option
('pve-storage-id'),
763 description
=> "The name of the file to create/upload.",
766 vmid
=> get_standard_option
768 description
=> "Specify owner VM",
774 description
=> "Volume identifier",
780 # todo: can we proxy file uploads to remote nodes?
781 if ($param->{node
} ne PVE
::INotify
::nodename
()) {
782 raise_param_exc
({ node
=> "can't upload content to remote node" });
785 my $node = $param->{node
};
786 my $storeid = $param->{storage
};
787 my $name = $param->{filename
};
789 my $fh = CGI
::upload
('filename') || die "unable to get file handle\n";
791 syslog
('info', "UPLOAD $name to $node $storeid");
794 die "upload not implemented\n";
797 my $tmpname = "/tmp/proxmox_upload-$$.bin";
800 open FILE
, ">$tmpname" || die "can't open temporary file '$tmpname' - $!\n";
801 while (read($fh, $buffer, 32768)) {
802 die "write failed - $!" unless print FILE
$buffer;
804 close FILE
|| die " can't close temporary file '$tmpname' - $!\n";
813 unlink $tmpname; # fixme: proxy to local host import
815 # fixme: return volid
821 package PVE
::API2
::Nodes
;
828 use PVE
::RESTHandler
;
829 use PVE
::RPCEnvironment
;
831 use base
qw(PVE::RESTHandler);
833 __PACKAGE__-
>register_method ({
834 subclass
=> "PVE::API2::Nodes::Nodeinfo",
838 __PACKAGE__-
>register_method ({
842 permissions
=> { user
=> 'all' },
843 description
=> "Cluster node index.",
845 additionalProperties
=> 0,
854 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
859 my $clinfo = PVE
::Cluster
::get_clinfo
();
862 my $nodename = PVE
::INotify
::nodename
();
863 my $nodelist = $clinfo->{nodelist
};
865 my $rrd = PVE
::Cluster
::rrd_dump
();
867 my @nodes = $nodelist ?
(keys %$nodelist) : $nodename;
869 foreach my $node (@nodes) {
870 my $entry = { name
=> $node };
871 if (my $d = $rrd->{"pve2-node/$node"}) {
873 $entry->{uptime
} = $d->[0];
874 $entry->{maxcpu
} = $d->[3];
875 $entry->{cpu
} = $d->[4];
876 $entry->{maxmem
} = $d->[6];
877 $entry->{mem
} = $d->[7];
878 $entry->{maxdisk
} = $d->[10];
879 $entry->{disk
} = $d->[11];