]>
git.proxmox.com Git - pve-manager.git/blob - lib/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
;
18 use PVE
::API2
::Services
;
19 use PVE
::API2
::Network
;
21 use PVE
::API2
::Storage
::Scan
;
22 use PVE
::API2
::Storage
::Status
;
26 use base
qw(PVE::RESTHandler);
28 __PACKAGE__-
>register_method ({
29 subclass
=> "PVE::API2::Qemu",
33 __PACKAGE__-
>register_method ({
34 subclass
=> "PVE::API2::Services",
38 __PACKAGE__-
>register_method ({
39 subclass
=> "PVE::API2::Network",
43 __PACKAGE__-
>register_method ({
44 subclass
=> "PVE::API2::Tasks",
48 __PACKAGE__-
>register_method ({
49 subclass
=> "PVE::API2::Storage::Scan",
53 __PACKAGE__-
>register_method ({
54 subclass
=> "PVE::API2::Storage::Status",
58 __PACKAGE__-
>register_method ({
62 permissions
=> { user
=> 'all' },
63 description
=> "Node index.",
65 additionalProperties
=> 0,
67 node
=> get_standard_option
('pve-node'),
76 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
85 { name
=> 'rrd' }, # fixme: remove?
86 { name
=> 'rrddata' },# fixme: remove?
87 { name
=> 'vncshell' },
90 { name
=> 'services' },
92 { name
=> 'storage' },
95 { name
=> 'network' },
96 { name
=> 'network_changes' },
102 __PACKAGE__-
>register_method({
103 name
=> 'network_changes',
104 path
=> 'network_changes',
107 path
=> '/nodes/{node}',
108 privs
=> [ 'Sys.Audit' ],
110 description
=> "Get network configuration changes (diff) since last boot.",
113 additionalProperties
=> 0,
115 node
=> get_standard_option
('pve-node'),
118 returns
=> { type
=> "string" },
122 my $res = PVE
::INotify
::read_file
('interfaces', 1);
124 return $res->{changes
} || '';
127 __PACKAGE__-
>register_method({
128 name
=> 'revert_network_changes',
129 path
=> 'network_changes',
132 path
=> '/nodes/{node}',
133 privs
=> [ 'Sys.Modify' ],
136 description
=> "Revert network configuration changes.",
139 additionalProperties
=> 0,
141 node
=> get_standard_option
('pve-node'),
144 returns
=> { type
=> "null" },
148 unlink "/etc/network/interfaces.new";
153 __PACKAGE__-
>register_method({
158 path
=> '/nodes/{node}',
159 privs
=> [ 'Sys.Audit' ],
161 description
=> "Read node status",
164 additionalProperties
=> 0,
166 node
=> get_standard_option
('pve-node'),
183 my ($uptime, $idle) = PVE
::ProcFSTools
::read_proc_uptime
();
184 $res->{uptime
} = $uptime;
186 my ($avg1, $avg5, $avg15) = PVE
::ProcFSTools
::read_loadavg
();
187 $res->{loadavg
} = [ $avg1, $avg5, $avg15];
189 my ($sysname, $nodename, $release, $version, $machine) = POSIX
::uname
();
191 $res->{kversion
} = "$sysname $release $version";
193 $res->{cpuinfo
} = PVE
::ProcFSTools
::read_cpuinfo
();
195 my $stat = PVE
::ProcFSTools
::read_proc_stat
();
196 $res->{cpu
} = $stat->{cpu
};
197 $res->{wait} = $stat->{wait};
199 my $meminfo = PVE
::ProcFSTools
::read_meminfo
();
201 free
=> $meminfo->{memfree
},
202 total
=> $meminfo->{memtotal
},
203 used
=> $meminfo->{memused
},
206 free
=> $meminfo->{swapfree
},
207 total
=> $meminfo->{swaptotal
},
208 used
=> $meminfo->{swapused
},
211 $res->{pveversion
} = PVE
::pvecfg
::package() . "/" .
212 PVE
::pvecfg
::version
() . "/" .
213 PVE
::pvecfg
::repoid
();
215 my $dinfo = df
('/', 1); # output is bytes
218 total
=> $dinfo->{blocks
},
219 avail
=> $dinfo->{bavail
},
220 used
=> $dinfo->{used
},
221 free
=> $dinfo->{bavail
} - $dinfo->{used
},
227 __PACKAGE__-
>register_method({
232 path
=> '/nodes/{node}',
233 privs
=> [ 'Sys.PowerMgmt' ],
236 description
=> "Reboot or shutdown a node.",
239 additionalProperties
=> 0,
241 node
=> get_standard_option
('pve-node'),
243 description
=> "Specify the command.",
245 enum
=> [qw(reboot shutdown)],
249 returns
=> { type
=> "null" },
253 if ($param->{command
} eq 'reboot') {
254 system ("(sleep 2;/sbin/reboot)&");
255 } elsif ($param->{command
} eq 'shutdown') {
256 system ("(sleep 2;/sbin/poweroff)&");
263 __PACKAGE__-
>register_method({
267 protected
=> 1, # fixme: can we avoid that?
269 path
=> '/nodes/{node}',
270 privs
=> [ 'Sys.Audit' ],
272 description
=> "Read node RRD statistics (returns PNG)",
274 additionalProperties
=> 0,
276 node
=> get_standard_option
('pve-node'),
278 description
=> "Specify the time frame you are interested in.",
280 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
283 description
=> "The list of datasources you want to display.",
284 type
=> 'string', format
=> 'pve-configid-list',
287 description
=> "The RRD consolidation function",
289 enum
=> [ 'AVERAGE', 'MAX' ],
297 filename
=> { type
=> 'string' },
303 return PVE
::Cluster
::create_rrd_graph
(
304 "pve2-node/$param->{node}", $param->{timeframe
},
305 $param->{ds
}, $param->{cf
});
309 __PACKAGE__-
>register_method({
313 protected
=> 1, # fixme: can we avoid that?
315 path
=> '/nodes/{node}',
316 privs
=> [ 'Sys.Audit' ],
318 description
=> "Read node RRD statistics",
320 additionalProperties
=> 0,
322 node
=> get_standard_option
('pve-node'),
324 description
=> "Specify the time frame you are interested in.",
326 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
329 description
=> "The RRD consolidation function",
331 enum
=> [ 'AVERAGE', 'MAX' ],
346 return PVE
::Cluster
::create_rrd_data
(
347 "pve2-node/$param->{node}", $param->{timeframe
}, $param->{cf
});
350 __PACKAGE__-
>register_method({
354 description
=> "Read system log",
357 path
=> '/nodes/{node}',
358 privs
=> [ 'Sys.Syslog' ],
362 additionalProperties
=> 0,
364 node
=> get_standard_option
('pve-node'),
383 description
=> "Line number",
387 description
=> "Line text",
398 my $rpcenv = PVE
::RPCEnvironment
::get
();
399 my $user = $rpcenv->get_user();
400 my $node = $param->{node
};
402 my $fh = IO
::File-
>new("/var/log/syslog", "r");
403 die "unable to open file - $!" if !$fh;
405 my $start = $param->{start
} || 0;
406 my $limit = $param->{limit
} || 50;
409 while (defined ($line = <$fh>)) {
410 next if $count++ < $start;
413 push @$lines, { n
=> $count, t
=> $line};
419 # HACK: ExtJS store.guaranteeRange() does not like empty array
423 push @$lines, { n
=> $count, t
=> "no content"};
426 $rpcenv->set_result_count($count);
433 __PACKAGE__-
>register_method ({
439 path
=> '/nodes/{node}',
440 privs
=> [ 'Sys.Console' ],
442 description
=> "Creates a VNC Shell proxy.",
444 additionalProperties
=> 0,
446 node
=> get_standard_option
('pve-node'),
450 additionalProperties
=> 0,
452 user
=> { type
=> 'string' },
453 ticket
=> { type
=> 'string' },
454 cert
=> { type
=> 'string' },
455 port
=> { type
=> 'integer' },
456 upid
=> { type
=> 'string' },
462 my $rpcenv = PVE
::RPCEnvironment
::get
();
464 my $user = $rpcenv->get_user();
466 my $ticket = PVE
::AccessControl
::assemble_ticket
($user);
468 my $node = $param->{node
};
470 $sslcert = PVE
::Tools
::file_get_contents
("/etc/pve/pve-root-ca.pem", 8192)
473 my $port = PVE
::Tools
::next_vnc_port
();
477 if ($node ne PVE
::INotify
::nodename
()) {
478 $remip = PVE
::Cluster
::remote_node_ip
($node);
481 # NOTE: vncterm VNC traffic is already TLS encrypted,
482 # so we select the fastest chipher here (or 'none'?)
483 my $remcmd = $remip ?
484 ['/usr/bin/ssh', '-c', 'blowfish-cbc', '-t', $remip] : [];
486 my $shcmd = $user eq 'root@pam' ?
[ "/bin/bash", "-l" ] : [ "/bin/login" ];
490 # fixme: do we want to require special auth permissions?
491 # example "-perm Shell"
492 my @cmd = ('/usr/bin/vncterm', '-rfbport', $port,
493 '-timeout', $timeout, '-authpath', "/nodes/$node",
494 '-perm', 'Sys.Console', '-c', @$remcmd, @$shcmd);
499 syslog
('info', "starting vnc proxy $upid\n");
501 my $cmdstr = join (' ', @cmd);
502 syslog
('info', "launch command: $cmdstr");
504 if (system(@cmd) != 0) {
505 my $msg = "vncterm failed - $?";
506 syslog
('err', $msg);
513 my $upid = $rpcenv->fork_worker('vncshell', "", $user, $realcmd);
524 __PACKAGE__-
>register_method({
529 path
=> '/nodes/{node}',
530 privs
=> [ 'Sys.Audit' ],
532 description
=> "Read DNS settings.",
535 additionalProperties
=> 0,
537 node
=> get_standard_option
('pve-node'),
542 additionalProperties
=> 0,
545 description
=> "Search domain for host-name lookup.",
550 description
=> 'First name server IP address.',
555 description
=> 'Second name server IP address.',
560 description
=> 'Third name server IP address.',
569 my $res = PVE
::INotify
::read_file
('resolvconf');
574 __PACKAGE__-
>register_method({
575 name
=> 'update_dns',
578 description
=> "Write DNS settings.",
582 additionalProperties
=> 0,
584 node
=> get_standard_option
('pve-node'),
586 description
=> "Search domain for host-name lookup.",
590 description
=> 'First name server IP address.',
591 type
=> 'string', format
=> 'ipv4',
595 description
=> 'Second name server IP address.',
596 type
=> 'string', format
=> 'ipv4',
600 description
=> 'Third name server IP address.',
601 type
=> 'string', format
=> 'ipv4',
606 returns
=> { type
=> "null" },
610 PVE
::INotify
::update_file
('resolvconf', $param);
615 __PACKAGE__-
>register_method({
620 path
=> '/nodes/{node}',
621 privs
=> [ 'Sys.Audit' ],
623 description
=> "Read server time and time zone settings.",
626 additionalProperties
=> 0,
628 node
=> get_standard_option
('pve-node'),
633 additionalProperties
=> 0,
636 description
=> "Time zone",
640 description
=> "Seconds since 1970-01-01 00:00:00 UTC.",
642 minimum
=> 1297163644,
645 description
=> "Seconds since 1970-01-01 00:00:00 (local time)",
647 minimum
=> 1297163644,
655 my $ltime = timegm_nocheck
(localtime($ctime));
657 timezone
=> PVE
::INotify
::read_file
('timezone'),
665 __PACKAGE__-
>register_method({
666 name
=> 'set_timezone',
669 description
=> "Set time zone.",
673 additionalProperties
=> 0,
675 node
=> get_standard_option
('pve-node'),
677 description
=> "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.",
682 returns
=> { type
=> "null" },
686 PVE
::INotify
::write_file
('timezone', $param->{timezone
});
691 __PACKAGE__-
>register_method ({
696 path
=> '/storage/{storage}',
697 privs
=> [ 'Datastore.AllocateSpace' ],
699 description
=> "Upload content.",
701 additionalProperties
=> 0,
703 node
=> get_standard_option
('pve-node'),
704 storage
=> get_standard_option
('pve-storage-id'),
706 description
=> "The name of the file to create/upload.",
709 vmid
=> get_standard_option
711 description
=> "Specify owner VM",
717 description
=> "Volume identifier",
723 # todo: can we proxy file uploads to remote nodes?
724 if ($param->{node
} ne PVE
::INotify
::nodename
()) {
725 raise_param_exc
({ node
=> "can't upload content to remote node" });
728 my $node = $param->{node
};
729 my $storeid = $param->{storage
};
730 my $name = $param->{filename
};
732 my $fh = CGI
::upload
('filename') || die "unable to get file handle\n";
734 syslog
('info', "UPLOAD $name to $node $storeid");
737 die "upload not implemented\n";
740 my $tmpname = "/tmp/proxmox_upload-$$.bin";
743 open FILE
, ">$tmpname" || die "can't open temporary file '$tmpname' - $!\n";
744 while (read($fh, $buffer, 32768)) {
745 die "write failed - $!" unless print FILE
$buffer;
747 close FILE
|| die " can't close temporary file '$tmpname' - $!\n";
756 unlink $tmpname; # fixme: proxy to local host import
758 # fixme: return volid
764 package PVE
::API2
::Nodes
;
771 use PVE
::RESTHandler
;
772 use PVE
::RPCEnvironment
;
774 use base
qw(PVE::RESTHandler);
776 __PACKAGE__-
>register_method ({
777 subclass
=> "PVE::API2::Nodes::Nodeinfo",
781 __PACKAGE__-
>register_method ({
785 permissions
=> { user
=> 'all' },
786 description
=> "Cluster node index.",
788 additionalProperties
=> 0,
797 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
802 my $clinfo = PVE
::Cluster
::get_clinfo
();
805 my $nodename = PVE
::INotify
::nodename
();
806 my $nodelist = $clinfo->{nodelist
};
808 my $rrd = PVE
::Cluster
::rrd_dump
();
810 my @nodes = $nodelist ?
(keys %$nodelist) : $nodename;
812 foreach my $node (@nodes) {
813 my $entry = { name
=> $node };
814 if (my $d = $rrd->{"pve2-node/$node"}) {
816 $entry->{uptime
} = $d->[0];
817 $entry->{maxcpu
} = $d->[3];
818 $entry->{cpu
} = $d->[4];
819 $entry->{maxmem
} = $d->[6];
820 $entry->{mem
} = $d->[7];
821 $entry->{maxdisk
} = $d->[10];
822 $entry->{disk
} = $d->[11];