1 package PVE
::API2
::Nodes
::Nodeinfo
;
5 use POSIX
qw(LONG_MAX);
7 use Time
::Local
qw(timegm_nocheck);
8 use HTTP
::Status
qw(:constants);
14 use PVE
::Cluster
qw(cfs_read_file);
16 use PVE
::Exception
qw(raise raise_perm_exc);
18 use PVE
::RPCEnvironment
;
19 use PVE
::JSONSchema
qw(get_standard_option);
20 use PVE
::AccessControl
;
28 use PVE
::API2
::Subscription
;
29 use PVE
::API2
::Services
;
30 use PVE
::API2
::Network
;
32 use PVE
::API2
::Storage
::Scan
;
33 use PVE
::API2
::Storage
::Status
;
36 use PVE
::API2
::LXC
::Status
;
37 use PVE
::API2
::VZDump
;
40 use PVE
::API2
::Firewall
::Host
;
43 use base
qw(PVE::RESTHandler);
45 __PACKAGE__-
>register_method ({
46 subclass
=> "PVE::API2::Qemu",
50 __PACKAGE__-
>register_method ({
51 subclass
=> "PVE::API2::LXC",
55 __PACKAGE__-
>register_method ({
56 subclass
=> "PVE::API2::Ceph",
60 __PACKAGE__-
>register_method ({
61 subclass
=> "PVE::API2::VZDump",
65 __PACKAGE__-
>register_method ({
66 subclass
=> "PVE::API2::Services",
70 __PACKAGE__-
>register_method ({
71 subclass
=> "PVE::API2::Subscription",
72 path
=> 'subscription',
75 __PACKAGE__-
>register_method ({
76 subclass
=> "PVE::API2::Network",
80 __PACKAGE__-
>register_method ({
81 subclass
=> "PVE::API2::Tasks",
85 __PACKAGE__-
>register_method ({
86 subclass
=> "PVE::API2::Storage::Scan",
90 __PACKAGE__-
>register_method ({
91 subclass
=> "PVE::API2::Storage::Status",
95 __PACKAGE__-
>register_method ({
96 subclass
=> "PVE::API2::APT",
100 __PACKAGE__-
>register_method ({
101 subclass
=> "PVE::API2::Firewall::Host",
105 __PACKAGE__-
>register_method ({
109 permissions
=> { user
=> 'all' },
110 description
=> "Node index.",
112 additionalProperties
=> 0,
114 node
=> get_standard_option
('pve-node'),
123 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
131 { name
=> 'version' },
132 { name
=> 'syslog' },
133 { name
=> 'status' },
134 { name
=> 'subscription' },
135 { name
=> 'report' },
137 { name
=> 'rrd' }, # fixme: remove?
138 { name
=> 'rrddata' },# fixme: remove?
139 { name
=> 'vncshell' },
140 { name
=> 'spiceshell' },
143 { name
=> 'services' },
145 { name
=> 'storage' },
148 { name
=> 'vzdump' },
149 { name
=> 'network' },
150 { name
=> 'aplinfo' },
151 { name
=> 'startall' },
152 { name
=> 'stopall' },
153 { name
=> 'netstat' },
154 { name
=> 'firewall' },
160 __PACKAGE__-
>register_method ({
165 permissions
=> { user
=> 'all' },
166 description
=> "API version details",
168 additionalProperties
=> 0,
170 node
=> get_standard_option
('pve-node'),
176 version
=> { type
=> 'string' },
177 release
=> { type
=> 'string' },
178 repoid
=> { type
=> 'string' },
182 my ($resp, $param) = @_;
184 return PVE
::pvecfg
::version_info
();
187 __PACKAGE__-
>register_method({
192 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
194 description
=> "Read node status",
197 additionalProperties
=> 0,
199 node
=> get_standard_option
('pve-node'),
216 my ($uptime, $idle) = PVE
::ProcFSTools
::read_proc_uptime
();
217 $res->{uptime
} = $uptime;
219 my ($avg1, $avg5, $avg15) = PVE
::ProcFSTools
::read_loadavg
();
220 $res->{loadavg
} = [ $avg1, $avg5, $avg15];
222 my ($sysname, $nodename, $release, $version, $machine) = POSIX
::uname
();
224 $res->{kversion
} = "$sysname $release $version";
226 $res->{cpuinfo
} = PVE
::ProcFSTools
::read_cpuinfo
();
228 my $stat = PVE
::ProcFSTools
::read_proc_stat
();
229 $res->{cpu
} = $stat->{cpu
};
230 $res->{wait} = $stat->{wait};
232 my $meminfo = PVE
::ProcFSTools
::read_meminfo
();
234 free
=> $meminfo->{memfree
},
235 total
=> $meminfo->{memtotal
},
236 used
=> $meminfo->{memused
},
240 shared
=> $meminfo->{memshared
},
244 free
=> $meminfo->{swapfree
},
245 total
=> $meminfo->{swaptotal
},
246 used
=> $meminfo->{swapused
},
249 $res->{pveversion
} = PVE
::pvecfg
::package() . "/" .
250 PVE
::pvecfg
::version_text
();
252 my $dinfo = df
('/', 1); # output is bytes
255 total
=> $dinfo->{blocks
},
256 avail
=> $dinfo->{bavail
},
257 used
=> $dinfo->{used
},
258 free
=> $dinfo->{bavail
} - $dinfo->{used
},
264 __PACKAGE__-
>register_method({
269 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
271 description
=> "Read tap/vm network device interface counters",
274 additionalProperties
=> 0,
276 node
=> get_standard_option
('pve-node'),
291 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
292 foreach my $dev (keys %$netdev) {
293 next if $dev !~ m/^tap([1-9]\d*)i(\d+)$/;
302 in => $netdev->{$dev}->{transmit
},
303 out
=> $netdev->{$dev}->{receive
},
311 __PACKAGE__-
>register_method({
316 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
318 description
=> "Execute multiple commands in order.",
320 protected
=> 1, # avoid problems with proxy code
322 additionalProperties
=> 0,
324 node
=> get_standard_option
('pve-node'),
326 description
=> "JSON encoded array of commands.",
341 my $rpcenv = PVE
::RPCEnvironment
::get
();
342 my $user = $rpcenv->get_user();
344 my $commands = eval { decode_json
($param->{commands
}); };
346 die "commands param did not contain valid JSON: $@" if $@;
347 die "commands is not an array" if ref($commands) ne "ARRAY";
349 foreach my $cmd (@$commands) {
351 die "$cmd is not a valid command" if (ref($cmd) ne "HASH" || !$cmd->{path
} || !$cmd->{method});
355 my $path = "nodes/$param->{node}/$cmd->{path}";
358 my ($handler, $info) = PVE
::API2-
>find_handler($cmd->{method}, $path, $uri_param);
359 if (!$handler || !$info) {
360 die "no handler for '$path'\n";
363 foreach my $p (keys %{$cmd->{args
}}) {
364 raise_param_exc
({ $p => "duplicate parameter" }) if defined($uri_param->{$p});
365 $uri_param->{$p} = $cmd->{args
}->{$p};
368 # check access permissions
369 $rpcenv->check_api2_permissions($info->{permissions
}, $user, $uri_param);
373 data
=> $handler->handle($info, $uri_param),
377 my $resp = { status
=> HTTP_INTERNAL_SERVER_ERROR
};
378 if (ref($err) eq "PVE::Exception") {
379 $resp->{status
} = $err->{code
} if $err->{code
};
380 $resp->{errors
} = $err->{errors
} if $err->{errors
};
381 $resp->{message
} = $err->{msg
};
383 $resp->{message
} = $err;
393 __PACKAGE__-
>register_method({
398 check
=> ['perm', '/nodes/{node}', [ 'Sys.PowerMgmt' ]],
401 description
=> "Reboot or shutdown a node.",
404 additionalProperties
=> 0,
406 node
=> get_standard_option
('pve-node'),
408 description
=> "Specify the command.",
410 enum
=> [qw(reboot shutdown)],
414 returns
=> { type
=> "null" },
418 if ($param->{command
} eq 'reboot') {
419 system ("(sleep 2;/sbin/reboot)&");
420 } elsif ($param->{command
} eq 'shutdown') {
421 system ("(sleep 2;/sbin/poweroff)&");
428 __PACKAGE__-
>register_method({
432 protected
=> 1, # fixme: can we avoid that?
434 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
436 description
=> "Read node RRD statistics (returns PNG)",
438 additionalProperties
=> 0,
440 node
=> get_standard_option
('pve-node'),
442 description
=> "Specify the time frame you are interested in.",
444 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
447 description
=> "The list of datasources you want to display.",
448 type
=> 'string', format
=> 'pve-configid-list',
451 description
=> "The RRD consolidation function",
453 enum
=> [ 'AVERAGE', 'MAX' ],
461 filename
=> { type
=> 'string' },
467 return PVE
::Cluster
::create_rrd_graph
(
468 "pve2-node/$param->{node}", $param->{timeframe
},
469 $param->{ds
}, $param->{cf
});
473 __PACKAGE__-
>register_method({
477 protected
=> 1, # fixme: can we avoid that?
479 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
481 description
=> "Read node RRD statistics",
483 additionalProperties
=> 0,
485 node
=> get_standard_option
('pve-node'),
487 description
=> "Specify the time frame you are interested in.",
489 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
492 description
=> "The RRD consolidation function",
494 enum
=> [ 'AVERAGE', 'MAX' ],
509 return PVE
::Cluster
::create_rrd_data
(
510 "pve2-node/$param->{node}", $param->{timeframe
}, $param->{cf
});
513 __PACKAGE__-
>register_method({
517 description
=> "Read system log",
520 check
=> ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
524 additionalProperties
=> 0,
526 node
=> get_standard_option
('pve-node'),
545 description
=> "Line number",
549 description
=> "Line text",
558 my $rpcenv = PVE
::RPCEnvironment
::get
();
559 my $user = $rpcenv->get_user();
560 my $node = $param->{node
};
562 my ($count, $lines) = PVE
::Tools
::dump_journal
($param->{start
}, $param->{limit
});
564 $rpcenv->set_result_attrib('total', $count);
571 __PACKAGE__-
>register_method ({
577 description
=> "Restricted to users on realm 'pam'",
578 check
=> ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
580 description
=> "Creates a VNC Shell proxy.",
582 additionalProperties
=> 0,
584 node
=> get_standard_option
('pve-node'),
587 description
=> "Run 'apt-get dist-upgrade' instead of normal shell.",
594 description
=> "use websocket instead of standard vnc.",
599 additionalProperties
=> 0,
601 user
=> { type
=> 'string' },
602 ticket
=> { type
=> 'string' },
603 cert
=> { type
=> 'string' },
604 port
=> { type
=> 'integer' },
605 upid
=> { type
=> 'string' },
611 my $rpcenv = PVE
::RPCEnvironment
::get
();
613 my ($user, undef, $realm) = PVE
::AccessControl
::verify_username
($rpcenv->get_user());
615 raise_perm_exc
("realm != pam") if $realm ne 'pam';
617 raise_perm_exc
('user != root@pam') if $param->{upgrade
} && $user ne 'root@pam';
619 my $node = $param->{node
};
621 my $authpath = "/nodes/$node";
623 my $ticket = PVE
::AccessControl
::assemble_vnc_ticket
($user, $authpath);
625 $sslcert = PVE
::Tools
::file_get_contents
("/etc/pve/pve-root-ca.pem", 8192)
628 my ($remip, $family);
630 if ($node ne PVE
::INotify
::nodename
()) {
631 ($remip, $family) = PVE
::Cluster
::remote_node_ip
($node);
633 $family = PVE
::Tools
::get_host_address_family
($node);
636 my $port = PVE
::Tools
::next_vnc_port
($family);
638 # NOTE: vncterm VNC traffic is already TLS encrypted,
639 # so we select the fastest chipher here (or 'none'?)
640 my $remcmd = $remip ?
641 ['/usr/bin/ssh', '-t', $remip] : [];
645 if ($user eq 'root@pam') {
646 if ($param->{upgrade
}) {
647 my $upgradecmd = "pveupgrade --shell";
648 $upgradecmd = PVE
::Tools
::shellquote
($upgradecmd) if $remip;
649 $shcmd = [ '/bin/bash', '-c', $upgradecmd ];
651 $shcmd = [ '/bin/bash', '-l' ];
654 $shcmd = [ '/bin/login' ];
659 my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
660 '-timeout', $timeout, '-authpath', $authpath,
661 '-perm', 'Sys.Console'];
663 if ($param->{websocket
}) {
664 $ENV{PVE_VNC_TICKET
} = $ticket; # pass ticket to vncterm
665 push @$cmd, '-notls', '-listen', 'localhost';
668 push @$cmd, '-c', @$remcmd, @$shcmd;
673 syslog
('info', "starting vnc proxy $upid\n");
675 my $cmdstr = join (' ', @$cmd);
676 syslog
('info', "launch command: $cmdstr");
679 foreach my $k (keys %ENV) {
680 next if $k eq 'PVE_VNC_TICKET';
681 next if $k eq 'PATH' || $k eq 'TERM' || $k eq 'USER' || $k eq 'HOME';
686 PVE
::Tools
::run_command
($cmd, errmsg
=> "vncterm failed");
689 syslog
('err', $err);
695 my $upid = $rpcenv->fork_worker('vncshell', "", $user, $realcmd);
697 PVE
::Tools
::wait_for_vnc_port
($port);
708 __PACKAGE__-
>register_method({
709 name
=> 'vncwebsocket',
710 path
=> 'vncwebsocket',
713 description
=> "Restricted to users on realm 'pam'. You also need to pass a valid ticket (vncticket).",
714 check
=> ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
716 description
=> "Opens a weksocket for VNC traffic.",
718 additionalProperties
=> 0,
720 node
=> get_standard_option
('pve-node'),
722 description
=> "Ticket from previous call to vncproxy.",
727 description
=> "Port number returned by previous vncproxy call.",
737 port
=> { type
=> 'string' },
743 my $rpcenv = PVE
::RPCEnvironment
::get
();
745 my ($user, undef, $realm) = PVE
::AccessControl
::verify_username
($rpcenv->get_user());
747 raise_perm_exc
("realm != pam") if $realm ne 'pam';
749 my $authpath = "/nodes/$param->{node}";
751 PVE
::AccessControl
::verify_vnc_ticket
($param->{vncticket
}, $user, $authpath);
753 my $port = $param->{port
};
755 return { port
=> $port };
758 __PACKAGE__-
>register_method ({
759 name
=> 'spiceshell',
760 path
=> 'spiceshell',
765 description
=> "Restricted to users on realm 'pam'",
766 check
=> ['perm', '/nodes/{node}', [ 'Sys.Console' ]],
768 description
=> "Creates a SPICE shell.",
770 additionalProperties
=> 0,
772 node
=> get_standard_option
('pve-node'),
773 proxy
=> get_standard_option
('spice-proxy', { optional
=> 1 }),
776 description
=> "Run 'apt-get dist-upgrade' instead of normal shell.",
782 returns
=> get_standard_option
('remote-viewer-config'),
786 my $rpcenv = PVE
::RPCEnvironment
::get
();
787 my $authuser = $rpcenv->get_user();
789 my ($user, undef, $realm) = PVE
::AccessControl
::verify_username
($authuser);
791 raise_perm_exc
("realm != pam") if $realm ne 'pam';
793 raise_perm_exc
('user != root@pam') if $param->{upgrade
} && $user ne 'root@pam';
795 my $node = $param->{node
};
796 my $proxy = $param->{proxy
};
798 my $authpath = "/nodes/$node";
799 my $permissions = 'Sys.Console';
803 if ($user eq 'root@pam') {
804 if ($param->{upgrade
}) {
805 my $upgradecmd = "pveupgrade --shell";
806 $shcmd = [ '/bin/bash', '-c', $upgradecmd ];
808 $shcmd = [ '/bin/bash', '-l' ];
811 $shcmd = [ '/bin/login' ];
814 my $title = "Shell on '$node'";
816 return PVE
::API2Tools
::run_spiceterm
($authpath, $permissions, 0, $node, $proxy, $title, $shcmd);
819 __PACKAGE__-
>register_method({
824 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
826 description
=> "Read DNS settings.",
829 additionalProperties
=> 0,
831 node
=> get_standard_option
('pve-node'),
836 additionalProperties
=> 0,
839 description
=> "Search domain for host-name lookup.",
844 description
=> 'First name server IP address.',
849 description
=> 'Second name server IP address.',
854 description
=> 'Third name server IP address.',
863 my $res = PVE
::INotify
::read_file
('resolvconf');
868 __PACKAGE__-
>register_method({
869 name
=> 'update_dns',
872 description
=> "Write DNS settings.",
874 check
=> ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
879 additionalProperties
=> 0,
881 node
=> get_standard_option
('pve-node'),
883 description
=> "Search domain for host-name lookup.",
887 description
=> 'First name server IP address.',
888 type
=> 'string', format
=> 'ip',
892 description
=> 'Second name server IP address.',
893 type
=> 'string', format
=> 'ip',
897 description
=> 'Third name server IP address.',
898 type
=> 'string', format
=> 'ip',
903 returns
=> { type
=> "null" },
907 PVE
::INotify
::update_file
('resolvconf', $param);
912 __PACKAGE__-
>register_method({
917 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
919 description
=> "Read server time and time zone settings.",
922 additionalProperties
=> 0,
924 node
=> get_standard_option
('pve-node'),
929 additionalProperties
=> 0,
932 description
=> "Time zone",
936 description
=> "Seconds since 1970-01-01 00:00:00 UTC.",
938 minimum
=> 1297163644,
941 description
=> "Seconds since 1970-01-01 00:00:00 (local time)",
943 minimum
=> 1297163644,
951 my $ltime = timegm_nocheck
(localtime($ctime));
953 timezone
=> PVE
::INotify
::read_file
('timezone'),
961 __PACKAGE__-
>register_method({
962 name
=> 'set_timezone',
965 description
=> "Set time zone.",
967 check
=> ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
972 additionalProperties
=> 0,
974 node
=> get_standard_option
('pve-node'),
976 description
=> "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.",
981 returns
=> { type
=> "null" },
985 PVE
::INotify
::write_file
('timezone', $param->{timezone
});
990 __PACKAGE__-
>register_method({
997 description
=> "Get list of appliances.",
1000 additionalProperties
=> 0,
1002 node
=> get_standard_option
('pve-node'),
1017 my $list = PVE
::APLInfo
::load_data
();
1019 foreach my $template (keys %{$list->{all
}}) {
1020 my $pd = $list->{all
}->{$template};
1021 next if $pd->{'package'} eq 'pve-web-news';
1028 __PACKAGE__-
>register_method({
1029 name
=> 'apl_download',
1033 check
=> ['perm', '/storage/{storage}', ['Datastore.AllocateTemplate']],
1035 description
=> "Download appliance templates.",
1039 additionalProperties
=> 0,
1041 node
=> get_standard_option
('pve-node'),
1042 storage
=> get_standard_option
('pve-storage-id'),
1043 template
=> { type
=> 'string', maxLength
=> 255 },
1046 returns
=> { type
=> "string" },
1050 my $rpcenv = PVE
::RPCEnvironment
::get
();
1052 my $user = $rpcenv->get_user();
1054 my $node = $param->{node
};
1056 my $list = PVE
::APLInfo
::load_data
();
1058 my $template = $param->{template
};
1059 my $pd = $list->{all
}->{$template};
1061 raise_param_exc
({ template
=> "no such template"}) if !$pd;
1063 my $cfg = cfs_read_file
("storage.cfg");
1064 my $scfg = PVE
::Storage
::storage_check_enabled
($cfg, $param->{storage
}, $node);
1066 die "cannot download to storage type '$scfg->{type}'"
1067 if !($scfg->{type
} eq 'dir' || $scfg->{type
} eq 'nfs');
1069 die "unknown template type '$pd->{type}'\n"
1070 if !($pd->{type
} eq 'openvz' || $pd->{type
} eq 'lxc');
1072 die "storage '$param->{storage}' does not support templates\n"
1073 if !$scfg->{content
}->{vztmpl
};
1075 my $src = $pd->{location
};
1076 my $tmpldir = PVE
::Storage
::get_vztmpl_dir
($cfg, $param->{storage
});
1077 my $dest = "$tmpldir/$template";
1078 my $tmpdest = "$tmpldir/${template}.tmp.$$";
1083 print "starting template download from: $src\n";
1084 print "target file: $dest\n";
1089 my $md5 = (split (/\s/, `md5sum '$dest'`))[0];
1091 if ($md5 && (lc($md5) eq lc($pd->{md5sum
}))) {
1092 print "file already exists $md5 - no need to download\n";
1098 my $dccfg = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
1099 if ($dccfg->{http_proxy
}) {
1100 $ENV{http_proxy
} = $dccfg->{http_proxy
};
1103 my @cmd = ('/usr/bin/wget', '--progress=dot:mega', '-O', $tmpdest, $src);
1104 if (system (@cmd) != 0) {
1105 die "download failed - $!\n";
1108 my $md5 = (split (/\s/, `md5sum '$tmpdest'`))[0];
1110 if (!$md5 || (lc($md5) ne lc($pd->{md5sum
}))) {
1111 die "wrong checksum: $md5 != $pd->{md5sum}\n";
1114 if (system ('mv', $tmpdest, $dest) != 0) {
1115 die "unable to save file - $!\n";
1127 print "download finished\n";
1130 return $rpcenv->fork_worker('download', undef, $user, $worker);
1133 __PACKAGE__-
>register_method({
1138 check
=> ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
1141 description
=> "Gather various systems information about a node",
1144 additionalProperties
=> 0,
1146 node
=> get_standard_option
('pve-node'),
1153 return PVE
::Report
::generate
();
1156 my $get_start_stop_list = sub {
1157 my ($nodename, $autostart) = @_;
1159 my $vmlist = PVE
::Cluster
::get_vmlist
();
1162 foreach my $vmid (keys %{$vmlist->{ids
}}) {
1163 my $d = $vmlist->{ids
}->{$vmid};
1167 return if $d->{node
} ne $nodename;
1169 my $bootorder = LONG_MAX
;
1172 if ($d->{type
} eq 'lxc') {
1173 $conf = PVE
::LXC
::load_config
($vmid);
1174 } elsif ($d->{type
} eq 'qemu') {
1175 $conf = PVE
::QemuServer
::load_config
($vmid);
1177 die "unknown VM type '$d->{type}'\n";
1180 return if $autostart && !$conf->{onboot
};
1182 if ($conf->{startup
}) {
1183 $startup = PVE
::JSONSchema
::pve_parse_startup_order
($conf->{startup
});
1184 $startup->{order
} = $bootorder if !defined($startup->{order
});
1186 $startup = { order
=> $bootorder };
1189 # skip ha managed VMs (started by pve-ha-manager)
1190 return if PVE
::HA
::Config
::vm_is_ha_managed
($vmid);
1192 $resList->{$startup->{order
}}->{$vmid} = $startup;
1193 $resList->{$startup->{order
}}->{$vmid}->{type
} = $d->{type
};
1201 __PACKAGE__-
>register_method ({
1207 description
=> "Start all VMs and containers (when onboot=1).",
1209 additionalProperties
=> 0,
1211 node
=> get_standard_option
('pve-node'),
1215 description
=> "force if onboot=0.",
1225 my $rpcenv = PVE
::RPCEnvironment
::get
();
1226 my $authuser = $rpcenv->get_user();
1228 my $nodename = $param->{node
};
1229 $nodename = PVE
::INotify
::nodename
() if $nodename eq 'localhost';
1231 my $force = $param->{force
};
1235 $rpcenv->{type
} = 'priv'; # to start tasks in background
1237 # wait up to 10 seconds for quorum
1238 for (my $i = 10; $i >= 0; $i--) {
1239 last if PVE
::Cluster
::check_cfs_quorum
($i != 0 ?
1 : 0);
1242 my $autostart = $force ?
undef : 1;
1243 my $startList = &$get_start_stop_list($nodename, $autostart);
1245 # Note: use numeric sorting with <=>
1246 foreach my $order (sort {$a <=> $b} keys %$startList) {
1247 my $vmlist = $startList->{$order};
1249 foreach my $vmid (sort {$a <=> $b} keys %$vmlist) {
1250 my $d = $vmlist->{$vmid};
1252 PVE
::Cluster
::check_cfs_quorum
(); # abort when we loose quorum
1255 my $default_delay = 0;
1258 if ($d->{type
} eq 'lxc') {
1259 return if PVE
::LXC
::check_running
($vmid);
1260 print STDERR
"Starting CT $vmid\n";
1261 $upid = PVE
::API2
::LXC
::Status-
>vm_start({node
=> $nodename, vmid
=> $vmid });
1262 } elsif ($d->{type
} eq 'qemu') {
1263 $default_delay = 3; # to redruce load
1264 return if PVE
::QemuServer
::check_running
($vmid, 1);
1265 print STDERR
"Starting VM $vmid\n";
1266 $upid = PVE
::API2
::Qemu-
>vm_start({node
=> $nodename, vmid
=> $vmid });
1268 die "unknown VM type '$d->{type}'\n";
1271 my $res = PVE
::Tools
::upid_decode
($upid);
1272 while (PVE
::ProcFSTools
::check_process_running
($res->{pid
})) {
1276 my $status = PVE
::Tools
::upid_read_status
($upid);
1277 if ($status eq 'OK') {
1278 # use default delay to reduce load
1279 my $delay = defined($d->{up
}) ?
int($d->{up
}) : $default_delay;
1281 print STDERR
"Waiting for $delay seconds (startup delay)\n" if $d->{up
};
1282 for (my $i = 0; $i < $delay; $i++) {
1287 if ($d->{type
} eq 'lxc') {
1288 print STDERR
"Starting CT $vmid failed: $status\n";
1289 } elsif ($d->{type
} eq 'qemu') {
1290 print STDERR
"Starting VM $vmid failed: status\n";
1300 return $rpcenv->fork_worker('startall', undef, $authuser, $code);
1303 my $create_stop_worker = sub {
1304 my ($nodename, $type, $vmid, $down_timeout) = @_;
1307 if ($type eq 'lxc') {
1308 return if !PVE
::LXC
::check_running
($vmid);
1309 my $timeout = defined($down_timeout) ?
int($down_timeout) : 60;
1310 print STDERR
"Stopping CT $vmid (timeout = $timeout seconds)\n";
1311 $upid = PVE
::API2
::LXC
::Status-
>vm_shutdown({node
=> $nodename, vmid
=> $vmid,
1312 timeout
=> $timeout, forceStop
=> 1 });
1313 } elsif ($type eq 'qemu') {
1314 return if !PVE
::QemuServer
::check_running
($vmid, 1);
1315 my $timeout = defined($down_timeout) ?
int($down_timeout) : 60*3;
1316 print STDERR
"Stopping VM $vmid (timeout = $timeout seconds)\n";
1317 $upid = PVE
::API2
::Qemu-
>vm_shutdown({node
=> $nodename, vmid
=> $vmid,
1318 timeout
=> $timeout, forceStop
=> 1 });
1320 die "unknown VM type '$type'\n";
1323 my $res = PVE
::Tools
::upid_decode
($upid);
1328 __PACKAGE__-
>register_method ({
1334 description
=> "Stop all VMs and Containers.",
1336 additionalProperties
=> 0,
1338 node
=> get_standard_option
('pve-node'),
1347 my $rpcenv = PVE
::RPCEnvironment
::get
();
1348 my $authuser = $rpcenv->get_user();
1350 my $nodename = $param->{node
};
1351 $nodename = PVE
::INotify
::nodename
() if $nodename eq 'localhost';
1355 $rpcenv->{type
} = 'priv'; # to start tasks in background
1357 my $stopList = &$get_start_stop_list($nodename);
1359 my $cpuinfo = PVE
::ProcFSTools
::read_cpuinfo
();
1360 my $maxWorkers = $cpuinfo->{cpus
};
1362 foreach my $order (sort {$b <=> $a} keys %$stopList) {
1363 my $vmlist = $stopList->{$order};
1365 foreach my $vmid (sort {$b <=> $a} keys %$vmlist) {
1366 my $d = $vmlist->{$vmid};
1368 eval { $pid = &$create_stop_worker($nodename, $d->{type
}, $vmid, $d->{down
}); };
1372 $workers->{$pid} = 1;
1373 while (scalar(keys %$workers) >= $maxWorkers) {
1374 foreach my $p (keys %$workers) {
1375 if (!PVE
::ProcFSTools
::check_process_running
($p)) {
1376 delete $workers->{$p};
1382 while (scalar(keys %$workers)) {
1383 foreach my $p (keys %$workers) {
1384 if (!PVE
::ProcFSTools
::check_process_running
($p)) {
1385 delete $workers->{$p};
1394 return $rpcenv->fork_worker('stopall', undef, $authuser, $code);
1398 my $create_migrate_worker = sub {
1399 my ($nodename, $type, $vmid, $target) = @_;
1402 if ($type eq 'lxc') {
1403 my $online = PVE
::LXC
::check_running
($vmid) ?
1 : 0;
1404 print STDERR
"Migrating CT $vmid\n";
1405 $upid = PVE
::API2
::LXC-
>migrate_vm({node
=> $nodename, vmid
=> $vmid, target
=> $target,
1406 online
=> $online });
1407 } elsif ($type eq 'qemu') {
1408 my $online = PVE
::QemuServer
::check_running
($vmid, 1) ?
1 : 0;
1409 print STDERR
"Migrating VM $vmid\n";
1410 $upid = PVE
::API2
::Qemu-
>migrate_vm({node
=> $nodename, vmid
=> $vmid, target
=> $target,
1411 online
=> $online });
1413 die "unknown VM type '$type'\n";
1416 my $res = PVE
::Tools
::upid_decode
($upid);
1421 __PACKAGE__-
>register_method ({
1422 name
=> 'migrateall',
1423 path
=> 'migrateall',
1427 description
=> "Migrate all VMs and Containers.",
1429 additionalProperties
=> 0,
1431 node
=> get_standard_option
('pve-node'),
1432 target
=> get_standard_option
('pve-node', { description
=> "Target node." }),
1434 description
=> "Max parralel migration job.",
1446 my $rpcenv = PVE
::RPCEnvironment
::get
();
1447 my $authuser = $rpcenv->get_user();
1449 my $nodename = $param->{node
};
1450 $nodename = PVE
::INotify
::nodename
() if $nodename eq 'localhost';
1452 my $target = $param->{target
};
1453 my $maxWorkers = $param->{maxworkers
};
1457 $rpcenv->{type
} = 'priv'; # to start tasks in background
1459 my $migrateList = &$get_start_stop_list($nodename);
1461 foreach my $order (sort {$b <=> $a} keys %$migrateList) {
1462 my $vmlist = $migrateList->{$order};
1464 foreach my $vmid (sort {$b <=> $a} keys %$vmlist) {
1465 my $d = $vmlist->{$vmid};
1467 eval { $pid = &$create_migrate_worker($nodename, $d->{type
}, $vmid, $target); };
1471 $workers->{$pid} = 1;
1472 while (scalar(keys %$workers) >= $maxWorkers) {
1473 foreach my $p (keys %$workers) {
1474 if (!PVE
::ProcFSTools
::check_process_running
($p)) {
1475 delete $workers->{$p};
1481 while (scalar(keys %$workers)) {
1482 foreach my $p (keys %$workers) {
1483 if (!PVE
::ProcFSTools
::check_process_running
($p)) {
1484 delete $workers->{$p};
1493 return $rpcenv->fork_worker('migrateall', undef, $authuser, $code);
1497 package PVE
::API2
::Nodes
;
1502 use PVE
::SafeSyslog
;
1504 use PVE
::RESTHandler
;
1505 use PVE
::RPCEnvironment
;
1508 use base
qw(PVE::RESTHandler);
1510 __PACKAGE__-
>register_method ({
1511 subclass
=> "PVE::API2::Nodes::Nodeinfo",
1515 __PACKAGE__-
>register_method ({
1519 permissions
=> { user
=> 'all' },
1520 description
=> "Cluster node index.",
1522 additionalProperties
=> 0,
1531 links
=> [ { rel
=> 'child', href
=> "{node}" } ],
1536 my $clinfo = PVE
::Cluster
::get_clinfo
();
1539 my $nodelist = PVE
::Cluster
::get_nodelist
();
1540 my $members = PVE
::Cluster
::get_members
();
1541 my $rrd = PVE
::Cluster
::rrd_dump
();
1543 foreach my $node (@$nodelist) {
1544 my $entry = PVE
::API2Tools
::extract_node_stats
($node, $members, $rrd);