1 package PVE
::API2
::Nodes
::Nodeinfo
;
5 use POSIX
qw(LONG_MAX);
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
;
23 use PVE
::API2
::Subscription
;
24 use PVE
::API2
::Services
;
25 use PVE
::API2
::Network
;
27 use PVE
::API2
::Storage
::Scan
;
28 use PVE
::API2
::Storage
::Status
;
30 use PVE
::API2
::OpenVZ
;
31 use PVE
::API2
::VZDump
;
35 use base
qw(PVE::RESTHandler);
37 __PACKAGE__-
>register_method ({
38 subclass
=> "PVE::API2::Qemu",
42 __PACKAGE__-
>register_method ({
43 subclass
=> "PVE::API2::OpenVZ",
47 __PACKAGE__-
>register_method ({
48 subclass
=> "PVE::API2::VZDump",
52 __PACKAGE__-
>register_method ({
53 subclass
=> "PVE::API2::Services",
57 __PACKAGE__-
>register_method ({
58 subclass
=> "PVE::API2::Subscription",
59 path
=> 'subscription',
62 __PACKAGE__-
>register_method ({
63 subclass
=> "PVE::API2::Network",
67 __PACKAGE__-
>register_method ({
68 subclass
=> "PVE::API2::Tasks",
72 __PACKAGE__-
>register_method ({
73 subclass
=> "PVE::API2::Storage::Scan",
77 __PACKAGE__-
>register_method ({
78 subclass
=> "PVE::API2::Storage::Status",
82 __PACKAGE__-
>register_method ({
83 subclass
=> "PVE::API2::APT",
87 __PACKAGE__-
>register_method ({
91 permissions
=> { user
=> 'all' },
92 description
=> "Node index.",
94 additionalProperties
=> 0,
96 node
=> get_standard_option
('pve-node'),
105 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
112 { name
=> 'version' },
113 { name
=> 'syslog' },
114 { name
=> 'bootlog' },
115 { name
=> 'status' },
116 { name
=> 'subscription' },
118 { name
=> 'rrd' }, # fixme: remove?
119 { name
=> 'rrddata' },# fixme: remove?
120 { name
=> 'vncshell' },
123 { name
=> 'services' },
125 { name
=> 'storage' },
127 { name
=> 'openvz' },
128 { name
=> 'vzdump' },
129 { name
=> 'ubcfailcnt' },
130 { name
=> 'network' },
131 { name
=> 'aplinfo' },
132 { name
=> 'startall' },
133 { name
=> 'stopall' },
134 { name
=> 'netstat' },
140 __PACKAGE__-
>register_method ({
145 permissions
=> { user
=> 'all' },
146 description
=> "API version details",
148 additionalProperties
=> 0,
150 node
=> get_standard_option
('pve-node'),
156 version
=> { type
=> 'string' },
157 release
=> { type
=> 'string' },
158 repoid
=> { type
=> 'string' },
162 my ($resp, $param) = @_;
164 return PVE
::pvecfg
::version_info
();
167 __PACKAGE__-
>register_method({
168 name
=> 'beancounters_failcnt',
169 path
=> 'ubcfailcnt',
171 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
175 protected
=> 1, # openvz /proc entries are only readable by root
176 description
=> "Get user_beancounters failcnt for all active containers.",
178 additionalProperties
=> 0,
180 node
=> get_standard_option
('pve-node'),
188 id
=> { type
=> 'string' },
189 failcnt
=> { type
=> 'number' },
196 my $ubchash = PVE
::OpenVZ
::read_user_beancounters
();
199 foreach my $vmid (keys %$ubchash) {
201 push @$res, { id
=> $vmid, failcnt
=> $ubchash->{$vmid}->{failcntsum
} };
207 __PACKAGE__-
>register_method({
212 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
214 description
=> "Read node status",
217 additionalProperties
=> 0,
219 node
=> get_standard_option
('pve-node'),
236 my ($uptime, $idle) = PVE
::ProcFSTools
::read_proc_uptime
();
237 $res->{uptime
} = $uptime;
239 my ($avg1, $avg5, $avg15) = PVE
::ProcFSTools
::read_loadavg
();
240 $res->{loadavg
} = [ $avg1, $avg5, $avg15];
242 my ($sysname, $nodename, $release, $version, $machine) = POSIX
::uname
();
244 $res->{kversion
} = "$sysname $release $version";
246 $res->{cpuinfo
} = PVE
::ProcFSTools
::read_cpuinfo
();
248 my $stat = PVE
::ProcFSTools
::read_proc_stat
();
249 $res->{cpu
} = $stat->{cpu
};
250 $res->{wait} = $stat->{wait};
252 my $meminfo = PVE
::ProcFSTools
::read_meminfo
();
254 free
=> $meminfo->{memfree
},
255 total
=> $meminfo->{memtotal
},
256 used
=> $meminfo->{memused
},
260 shared
=> $meminfo->{memshared
},
264 free
=> $meminfo->{swapfree
},
265 total
=> $meminfo->{swaptotal
},
266 used
=> $meminfo->{swapused
},
269 $res->{pveversion
} = PVE
::pvecfg
::package() . "/" .
270 PVE
::pvecfg
::version_text
();
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 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
291 description
=> "Read tap/vm network device interface counters",
294 additionalProperties
=> 0,
296 node
=> get_standard_option
('pve-node'),
311 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
312 foreach my $dev (keys %$netdev) {
313 next if $dev !~ m/^tap([1-9]\d*)i(\d+)$/;
322 in => $netdev->{$dev}->{transmit
},
323 out
=> $netdev->{$dev}->{receive
},
331 __PACKAGE__-
>register_method({
336 check
=> ['perm', '/nodes/{node}', [ 'Sys.PowerMgmt' ]],
339 description
=> "Reboot or shutdown a node.",
342 additionalProperties
=> 0,
344 node
=> get_standard_option
('pve-node'),
346 description
=> "Specify the command.",
348 enum
=> [qw(reboot shutdown)],
352 returns
=> { type
=> "null" },
356 if ($param->{command
} eq 'reboot') {
357 system ("(sleep 2;/sbin/reboot)&");
358 } elsif ($param->{command
} eq 'shutdown') {
359 system ("(sleep 2;/sbin/poweroff)&");
366 __PACKAGE__-
>register_method({
370 protected
=> 1, # fixme: can we avoid that?
372 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
374 description
=> "Read node RRD statistics (returns PNG)",
376 additionalProperties
=> 0,
378 node
=> get_standard_option
('pve-node'),
380 description
=> "Specify the time frame you are interested in.",
382 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
385 description
=> "The list of datasources you want to display.",
386 type
=> 'string', format
=> 'pve-configid-list',
389 description
=> "The RRD consolidation function",
391 enum
=> [ 'AVERAGE', 'MAX' ],
399 filename
=> { type
=> 'string' },
405 return PVE
::Cluster
::create_rrd_graph
(
406 "pve2-node/$param->{node}", $param->{timeframe
},
407 $param->{ds
}, $param->{cf
});
411 __PACKAGE__-
>register_method({
415 protected
=> 1, # fixme: can we avoid that?
417 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
419 description
=> "Read node RRD statistics",
421 additionalProperties
=> 0,
423 node
=> get_standard_option
('pve-node'),
425 description
=> "Specify the time frame you are interested in.",
427 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
430 description
=> "The RRD consolidation function",
432 enum
=> [ 'AVERAGE', 'MAX' ],
447 return PVE
::Cluster
::create_rrd_data
(
448 "pve2-node/$param->{node}", $param->{timeframe
}, $param->{cf
});
451 __PACKAGE__-
>register_method({
455 description
=> "Read system log",
458 check
=> ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
462 additionalProperties
=> 0,
464 node
=> get_standard_option
('pve-node'),
483 description
=> "Line number",
487 description
=> "Line text",
496 my $rpcenv = PVE
::RPCEnvironment
::get
();
497 my $user = $rpcenv->get_user();
498 my $node = $param->{node
};
500 my ($count, $lines) = PVE
::Tools
::dump_logfile
("/var/log/syslog", $param->{start
}, $param->{limit
});
502 $rpcenv->set_result_attrib('total', $count);
507 __PACKAGE__-
>register_method({
511 description
=> "Read boot log",
514 check
=> ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
518 additionalProperties
=> 0,
520 node
=> get_standard_option
('pve-node'),
539 description
=> "Line number",
543 description
=> "Line text",
552 my $rpcenv = PVE
::RPCEnvironment
::get
();
553 my $user = $rpcenv->get_user();
554 my $node = $param->{node
};
556 my ($count, $lines) = PVE
::Tools
::dump_logfile
("/var/log/boot", $param->{start
}, $param->{limit
});
558 $rpcenv->set_result_attrib('total', $count);
565 __PACKAGE__-
>register_method ({
571 description
=> "Restricted to users on realm 'pam'",
572 check
=> ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
574 description
=> "Creates a VNC Shell proxy.",
576 additionalProperties
=> 0,
578 node
=> get_standard_option
('pve-node'),
581 description
=> "Run 'apt-get dist-upgrade' instead of normal shell.",
588 additionalProperties
=> 0,
590 user
=> { type
=> 'string' },
591 ticket
=> { type
=> 'string' },
592 cert
=> { type
=> 'string' },
593 port
=> { type
=> 'integer' },
594 upid
=> { type
=> 'string' },
600 my $rpcenv = PVE
::RPCEnvironment
::get
();
602 my ($user, undef, $realm) = PVE
::AccessControl
::verify_username
($rpcenv->get_user());
604 raise_perm_exc
("realm != pam") if $realm ne 'pam';
606 raise_perm_exc
('user != root@pam') if $param->{upgrade
} && $user ne 'root@pam';
608 my $node = $param->{node
};
610 my $authpath = "/nodes/$node";
612 my $ticket = PVE
::AccessControl
::assemble_vnc_ticket
($user, $authpath);
614 $sslcert = PVE
::Tools
::file_get_contents
("/etc/pve/pve-root-ca.pem", 8192)
617 my $port = PVE
::Tools
::next_vnc_port
();
621 if ($node ne PVE
::INotify
::nodename
()) {
622 $remip = PVE
::Cluster
::remote_node_ip
($node);
625 # NOTE: vncterm VNC traffic is already TLS encrypted,
626 # so we select the fastest chipher here (or 'none'?)
627 my $remcmd = $remip ?
628 ['/usr/bin/ssh', '-t', $remip] : [];
632 if ($user eq 'root@pam') {
633 if ($param->{upgrade
}) {
634 $shcmd = [ '/bin/bash', '-c', '"pveupgrade --shell"' ];
636 $shcmd = [ '/bin/bash', '-l' ];
639 $shcmd = [ '/bin/login' ];
644 my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
645 '-timeout', $timeout, '-authpath', $authpath,
646 '-perm', 'Sys.Console', '-c', @$remcmd, @$shcmd];
651 syslog
('info', "starting vnc proxy $upid\n");
653 my $cmdstr = join (' ', @$cmd);
654 syslog
('info', "launch command: $cmdstr");
657 foreach my $k (keys %ENV) {
658 next if $k eq 'PATH' || $k eq 'TERM' || $k eq 'USER' || $k eq 'HOME';
663 PVE
::Tools
::run_command
($cmd, errmsg
=> "vncterm failed");
666 syslog
('err', $err);
672 my $upid = $rpcenv->fork_worker('vncshell', "", $user, $realcmd);
674 PVE
::Tools
::wait_for_vnc_port
($port);
685 __PACKAGE__-
>register_method({
690 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
692 description
=> "Read DNS settings.",
695 additionalProperties
=> 0,
697 node
=> get_standard_option
('pve-node'),
702 additionalProperties
=> 0,
705 description
=> "Search domain for host-name lookup.",
710 description
=> 'First name server IP address.',
715 description
=> 'Second name server IP address.',
720 description
=> 'Third name server IP address.',
729 my $res = PVE
::INotify
::read_file
('resolvconf');
734 __PACKAGE__-
>register_method({
735 name
=> 'update_dns',
738 description
=> "Write DNS settings.",
740 check
=> ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
745 additionalProperties
=> 0,
747 node
=> get_standard_option
('pve-node'),
749 description
=> "Search domain for host-name lookup.",
753 description
=> 'First name server IP address.',
754 type
=> 'string', format
=> 'ipv4',
758 description
=> 'Second name server IP address.',
759 type
=> 'string', format
=> 'ipv4',
763 description
=> 'Third name server IP address.',
764 type
=> 'string', format
=> 'ipv4',
769 returns
=> { type
=> "null" },
773 PVE
::INotify
::update_file
('resolvconf', $param);
778 __PACKAGE__-
>register_method({
783 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
785 description
=> "Read server time and time zone settings.",
788 additionalProperties
=> 0,
790 node
=> get_standard_option
('pve-node'),
795 additionalProperties
=> 0,
798 description
=> "Time zone",
802 description
=> "Seconds since 1970-01-01 00:00:00 UTC.",
804 minimum
=> 1297163644,
807 description
=> "Seconds since 1970-01-01 00:00:00 (local time)",
809 minimum
=> 1297163644,
817 my $ltime = timegm_nocheck
(localtime($ctime));
819 timezone
=> PVE
::INotify
::read_file
('timezone'),
827 __PACKAGE__-
>register_method({
828 name
=> 'set_timezone',
831 description
=> "Set time zone.",
833 check
=> ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
838 additionalProperties
=> 0,
840 node
=> get_standard_option
('pve-node'),
842 description
=> "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.",
847 returns
=> { type
=> "null" },
851 PVE
::INotify
::write_file
('timezone', $param->{timezone
});
856 __PACKAGE__-
>register_method({
863 description
=> "Get list of appliances.",
866 additionalProperties
=> 0,
868 node
=> get_standard_option
('pve-node'),
883 my $list = PVE
::APLInfo
::load_data
();
885 foreach my $template (keys %{$list->{all
}}) {
886 my $pd = $list->{all
}->{$template};
887 next if $pd->{'package'} eq 'pve-web-news';
894 __PACKAGE__-
>register_method({
895 name
=> 'apl_download',
899 check
=> ['perm', '/storage/{storage}', ['Datastore.AllocateTemplate']],
901 description
=> "Download appliance templates.",
905 additionalProperties
=> 0,
907 node
=> get_standard_option
('pve-node'),
908 storage
=> get_standard_option
('pve-storage-id'),
909 template
=> { type
=> 'string', maxLength
=> 255 },
912 returns
=> { type
=> "string" },
916 my $rpcenv = PVE
::RPCEnvironment
::get
();
918 my $user = $rpcenv->get_user();
920 my $node = $param->{node
};
922 my $list = PVE
::APLInfo
::load_data
();
924 my $template = $param->{template
};
925 my $pd = $list->{all
}->{$template};
927 raise_param_exc
({ template
=> "no such template"}) if !$pd;
929 my $cfg = cfs_read_file
("storage.cfg");
930 my $scfg = PVE
::Storage
::storage_check_enabled
($cfg, $param->{storage
}, $node);
932 die "cannot download to storage type '$scfg->{type}'"
933 if !($scfg->{type
} eq 'dir' || $scfg->{type
} eq 'nfs');
935 die "unknown template type '$pd->{type}'\n" if $pd->{type
} ne 'openvz';
937 die "storage '$param->{storage}' does not support templates\n"
938 if !$scfg->{content
}->{vztmpl
};
940 my $src = $pd->{location
};
941 my $tmpldir = PVE
::Storage
::get_vztmpl_dir
($cfg, $param->{storage
});
942 my $dest = "$tmpldir/$template";
943 my $tmpdest = "$tmpldir/${template}.tmp.$$";
948 print "starting template download from: $src\n";
949 print "target file: $dest\n";
954 my $md5 = (split (/\s/, `md5sum '$dest'`))[0];
956 if ($md5 && (lc($md5) eq lc($pd->{md5sum
}))) {
957 print "file already exists $md5 - no need to download\n";
963 my $dccfg = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
964 if ($dccfg->{http_proxy
}) {
965 $ENV{http_proxy
} = $dccfg->{http_proxy
};
968 my @cmd = ('/usr/bin/wget', '--progress=dot:mega', '-O', $tmpdest, $src);
969 if (system (@cmd) != 0) {
970 die "download failed - $!\n";
973 my $md5 = (split (/\s/, `md5sum '$tmpdest'`))[0];
975 if (!$md5 || (lc($md5) ne lc($pd->{md5sum
}))) {
976 die "wrong checksum: $md5 != $pd->{md5sum}\n";
979 if (system ('mv', $tmpdest, $dest) != 0) {
980 die "unable to save file - $!\n";
992 print "download finished\n";
995 return $rpcenv->fork_worker('download', undef, $user, $worker);
998 my $get_start_stop_list = sub {
999 my ($nodename, $autostart) = @_;
1001 my $cc = PVE
::Cluster
::cfs_read_file
('cluster.conf');
1002 my $vmlist = PVE
::Cluster
::get_vmlist
();
1005 foreach my $vmid (keys %{$vmlist->{ids
}}) {
1006 my $d = $vmlist->{ids
}->{$vmid};
1010 return if $d->{node
} ne $nodename;
1012 my $bootorder = LONG_MAX
;
1014 if ($d->{type
} eq 'openvz') {
1015 my $conf = PVE
::OpenVZ
::load_config
($vmid);
1016 return if $autostart && !($conf->{onboot
} && $conf->{onboot
}->{value
});
1018 if ($conf->{bootorder
} && defined($conf->{bootorder
}->{value
})) {
1019 $bootorder = $conf->{bootorder
}->{value
};
1021 $startup = { order
=> $bootorder };
1023 } elsif ($d->{type
} eq 'qemu') {
1024 my $conf = PVE
::QemuServer
::load_config
($vmid);
1025 return if $autostart && !$conf->{onboot
};
1027 if ($conf->{startup
}) {
1028 $startup = PVE
::QemuServer
::parse_startup
($conf->{startup
});
1029 $startup->{order
} = $bootorder if !defined($startup->{order
});
1031 $startup = { order
=> $bootorder };
1034 die "unknown VM type '$d->{type}'\n";
1037 # skip ha managed VMs (started by rgmanager)
1038 return if PVE
::Cluster
::cluster_conf_lookup_pvevm
($cc, 0, $vmid, 1);
1040 $resList->{$startup->{order
}}->{$vmid} = $startup;
1041 $resList->{$startup->{order
}}->{$vmid}->{type
} = $d->{type
};
1049 __PACKAGE__-
>register_method ({
1054 description
=> "Start all VMs and containers (when onboot=1).",
1056 additionalProperties
=> 0,
1058 node
=> get_standard_option
('pve-node'),
1067 my $rpcenv = PVE
::RPCEnvironment
::get
();
1068 my $authuser = $rpcenv->get_user();
1070 my $nodename = $param->{node
};
1071 $nodename = PVE
::INotify
::nodename
() if $nodename eq 'localhost';
1075 $rpcenv->{type
} = 'priv'; # to start tasks in background
1077 # wait up to 10 seconds for quorum
1078 for (my $i = 10; $i >= 0; $i--) {
1079 last if PVE
::Cluster
::check_cfs_quorum
($i != 0 ?
1 : 0);
1083 my $startList = &$get_start_stop_list($nodename, 1);
1085 # Note: use numeric sorting with <=>
1086 foreach my $order (sort {$a <=> $b} keys %$startList) {
1087 my $vmlist = $startList->{$order};
1089 foreach my $vmid (sort {$a <=> $b} keys %$vmlist) {
1090 my $d = $vmlist->{$vmid};
1092 PVE
::Cluster
::check_cfs_quorum
(); # abort when we loose quorum
1095 my $default_delay = 0;
1098 if ($d->{type
} eq 'openvz') {
1099 return if PVE
::OpenVZ
::check_running
($vmid);
1100 print STDERR
"Starting CT $vmid\n";
1101 $upid = PVE
::API2
::OpenVZ-
>vm_start({node
=> $nodename, vmid
=> $vmid });
1102 } elsif ($d->{type
} eq 'qemu') {
1103 $default_delay = 3; # to redruce load
1104 return if PVE
::QemuServer
::check_running
($vmid, 1);
1105 print STDERR
"Starting VM $vmid\n";
1106 $upid = PVE
::API2
::Qemu-
>vm_start({node
=> $nodename, vmid
=> $vmid });
1108 die "unknown VM type '$d->{type}'\n";
1111 my $res = PVE
::Tools
::upid_decode
($upid);
1112 while (PVE
::ProcFSTools
::check_process_running
($res->{pid
})) {
1116 my $status = PVE
::Tools
::upid_read_status
($upid);
1117 if ($status eq 'OK') {
1118 # use default delay to reduce load
1119 my $delay = defined($d->{up
}) ?
int($d->{up
}) : $default_delay;
1121 print STDERR
"Waiting for $delay seconds (startup delay)\n" if $d->{up
};
1122 for (my $i = 0; $i < $delay; $i++) {
1127 if ($d->{type
} eq 'openvz') {
1128 print STDERR
"Starting CT $vmid failed: $status\n";
1129 } elsif ($d->{type
} eq 'qemu') {
1130 print STDERR
"Starting VM $vmid failed: status\n";
1140 return $rpcenv->fork_worker('startall', undef, $authuser, $code);
1143 my $create_stop_worker = sub {
1144 my ($nodename, $type, $vmid, $down_timeout) = @_;
1147 if ($type eq 'openvz') {
1148 return if !PVE
::OpenVZ
::check_running
($vmid);
1149 my $timeout = defined($down_timeout) ?
int($down_timeout) : 60;
1150 print STDERR
"Stopping CT $vmid (timeout = $timeout seconds)\n";
1151 $upid = PVE
::API2
::OpenVZ-
>vm_shutdown({node
=> $nodename, vmid
=> $vmid,
1152 timeout
=> $timeout, forceStop
=> 1 });
1153 } elsif ($type eq 'qemu') {
1154 return if !PVE
::QemuServer
::check_running
($vmid, 1);
1155 my $timeout = defined($down_timeout) ?
int($down_timeout) : 60*3;
1156 print STDERR
"Stopping VM $vmid (timeout = $timeout seconds)\n";
1157 $upid = PVE
::API2
::Qemu-
>vm_shutdown({node
=> $nodename, vmid
=> $vmid,
1158 timeout
=> $timeout, forceStop
=> 1 });
1160 die "unknown VM type '$type'\n";
1163 my $res = PVE
::Tools
::upid_decode
($upid);
1168 __PACKAGE__-
>register_method ({
1173 description
=> "Stop all VMs and Containers.",
1175 additionalProperties
=> 0,
1177 node
=> get_standard_option
('pve-node'),
1186 my $rpcenv = PVE
::RPCEnvironment
::get
();
1187 my $authuser = $rpcenv->get_user();
1189 my $nodename = $param->{node
};
1190 $nodename = PVE
::INotify
::nodename
() if $nodename eq 'localhost';
1194 $rpcenv->{type
} = 'priv'; # to start tasks in background
1196 my $stopList = &$get_start_stop_list($nodename);
1198 my $cpuinfo = PVE
::ProcFSTools
::read_cpuinfo
();
1199 my $maxWorkers = $cpuinfo->{cpus
};
1201 foreach my $order (sort {$b <=> $a} keys %$stopList) {
1202 my $vmlist = $stopList->{$order};
1204 foreach my $vmid (sort {$b <=> $a} keys %$vmlist) {
1205 my $d = $vmlist->{$vmid};
1207 eval { $pid = &$create_stop_worker($nodename, $d->{type
}, $vmid, $d->{down
}); };
1211 $workers->{$pid} = 1;
1212 while (scalar(keys %$workers) >= $maxWorkers) {
1213 foreach my $p (keys %$workers) {
1214 if (!PVE
::ProcFSTools
::check_process_running
($p)) {
1215 delete $workers->{$p};
1221 while (scalar(keys %$workers)) {
1222 foreach my $p (keys %$workers) {
1223 if (!PVE
::ProcFSTools
::check_process_running
($p)) {
1224 delete $workers->{$p};
1233 return $rpcenv->fork_worker('stopall', undef, $authuser, $code);
1237 package PVE
::API2
::Nodes
;
1242 use PVE
::SafeSyslog
;
1244 use PVE
::RESTHandler
;
1245 use PVE
::RPCEnvironment
;
1248 use base
qw(PVE::RESTHandler);
1250 __PACKAGE__-
>register_method ({
1251 subclass
=> "PVE::API2::Nodes::Nodeinfo",
1255 __PACKAGE__-
>register_method ({
1259 permissions
=> { user
=> 'all' },
1260 description
=> "Cluster node index.",
1262 additionalProperties
=> 0,
1271 links
=> [ { rel
=> 'child', href
=> "{node}" } ],
1276 my $clinfo = PVE
::Cluster
::get_clinfo
();
1279 my $nodelist = PVE
::Cluster
::get_nodelist
();
1280 my $members = PVE
::Cluster
::get_members
();
1281 my $rrd = PVE
::Cluster
::rrd_dump
();
1283 foreach my $node (@$nodelist) {
1284 my $entry = PVE
::API2Tools
::extract_node_stats
($node, $members, $rrd);