From ab71c272cf02fc3da7f73b1a918d363f962a01d1 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Fri, 20 Apr 2012 08:21:37 +0200 Subject: [PATCH] add startall/stopall API and init script --- PVE/API2/Nodes.pm | 243 ++++++++++++++++++++++++++++++++++++++++- bin/init.d/Makefile | 1 + bin/init.d/pve-manager | 39 +++++++ debian/conffiles | 1 + debian/postinst | 1 + debian/postrm | 1 + 6 files changed, 285 insertions(+), 1 deletion(-) create mode 100755 bin/init.d/pve-manager diff --git a/PVE/API2/Nodes.pm b/PVE/API2/Nodes.pm index 423d2344..55277842 100644 --- a/PVE/API2/Nodes.pm +++ b/PVE/API2/Nodes.pm @@ -2,7 +2,7 @@ package PVE::API2::Nodes::Nodeinfo; use strict; use warnings; -use POSIX; +use POSIX qw(LONG_MAX); use Filesys::Df; use Time::Local qw(timegm_nocheck); use PVE::pvecfg; @@ -19,6 +19,7 @@ use PVE::AccessControl; use PVE::Storage; use PVE::OpenVZ; use PVE::APLInfo; +use PVE::QemuServer; use PVE::API2::Subscription; use PVE::API2::Services; use PVE::API2::Network; @@ -120,6 +121,8 @@ __PACKAGE__->register_method ({ { name => 'ubcfailcnt' }, { name => 'network' }, { name => 'aplinfo' }, + { name => 'startall' }, + { name => 'stopall' }, ]; return $result; @@ -848,6 +851,244 @@ __PACKAGE__->register_method({ return $rpcenv->fork_worker('download', undef, $user, $worker); }}); +my $get_start_stop_list = sub { + my ($nodename, $autostart) = @_; + + my $cc = PVE::Cluster::cfs_read_file('cluster.conf'); + my $vmlist = PVE::Cluster::get_vmlist(); + + my $resList = {}; + foreach my $vmid (keys %{$vmlist->{ids}}) { + my $d = $vmlist->{ids}->{$vmid}; + my $startup; + + eval { + return if $d->{node} ne $nodename; + + my $bootorder = LONG_MAX; + + if ($d->{type} eq 'openvz') { + my $conf = PVE::OpenVZ::load_config($vmid); + return if $autostart && !($conf->{onboot} && $conf->{onboot}->{value}); + + if ($conf->{bootorder} && defined($conf->{bootorder}->{value})) { + $bootorder = $conf->{bootorder}->{value}; + } + $startup = { order => $bootorder }; + + } elsif ($d->{type} eq 'qemu') { + my $conf = PVE::QemuServer::load_config($vmid); + return if $autostart && !$conf->{onboot}; + + if ($conf->{startup}) { + $startup = PVE::QemuServer::parse_startup($conf->{startup}); + $startup->{order} = $bootorder if !defined($startup->{order}); + } else { + $startup = { order => $bootorder }; + } + } else { + die "unknown VM type '$d->{type}'\n"; + } + + # skip ha managed VMs (started by rgmanager) + return if PVE::Cluster::cluster_conf_lookup_pvevm($cc, 0, $vmid, 1); + + $resList->{$startup->{order}}->{$vmid} = $startup; + $resList->{$startup->{order}}->{$vmid}->{type} = $d->{type}; + }; + warn $@ if $@; + } + + return $resList; +}; + +__PACKAGE__->register_method ({ + name => 'startall', + path => 'startall', + method => 'POST', + protected => 1, + description => "Start all VMs and containers (when onboot=1).", + parameters => { + additionalProperties => 0, + properties => { + node => get_standard_option('pve-node'), + }, + }, + returns => { + type => 'string', + }, + code => sub { + my ($param) = @_; + + my $rpcenv = PVE::RPCEnvironment::get(); + my $authuser = $rpcenv->get_user(); + + my $nodename = $param->{node}; + $nodename = PVE::INotify::nodename() if $nodename eq 'localhost'; + + my $code = sub { + + $rpcenv->{type} = 'priv'; # to start tasks in background + + # wait up to 10 seconds for quorum + for (my $i = 10; $i >= 0; $i--) { + last if PVE::Cluster::check_cfs_quorum($i != 0 ? 1 : 0); + sleep(1); + } + + my $startList = &$get_start_stop_list($nodename, 1); + + foreach my $order (sort keys %$startList) { + my $vmlist = $startList->{$order}; + + foreach my $vmid (sort keys %$vmlist) { + my $d = $vmlist->{$vmid}; + + PVE::Cluster::check_cfs_quorum(); # abort when we loose quorum + + eval { + my $default_delay = 0; + my $upid; + + if ($d->{type} eq 'openvz') { + return if PVE::OpenVZ::check_running($vmid); + print STDERR "Starting CT $vmid\n"; + $upid = PVE::API2::OpenVZ->vm_start({node => $nodename, vmid => $vmid }); + } elsif ($d->{type} eq 'qemu') { + $default_delay = 3; # to redruce load + return if PVE::QemuServer::check_running($vmid, 1); + print STDERR "Starting VM $vmid\n"; + $upid = PVE::API2::Qemu->vm_start({node => $nodename, vmid => $vmid }); + } else { + die "unknown VM type '$d->{type}'\n"; + } + + my $res = PVE::Tools::upid_decode($upid); + while (PVE::ProcFSTools::check_process_running($res->{pid})) { + sleep(1); + } + + my $status = PVE::Tools::upid_read_status($upid); + if ($status eq 'OK') { + # use default delay to reduce load + my $delay = defined($d->{up}) ? int($d->{up}) : $default_delay; + if ($delay > 0) { + print STDERR "Waiting for $delay seconds (startup delay)\n" if $d->{up}; + for (my $i = 0; $i < $delay; $i++) { + sleep(1); + } + } + } else { + if ($d->{type} eq 'openvz') { + print STDERR "Starting CT $vmid failed: $status\n"; + } elsif ($d->{type} eq 'qemu') { + print STDERR "Starting VM $vmid failed: status\n"; + } + } + }; + warn $@ if $@; + } + } + return; + }; + + return $rpcenv->fork_worker('startall', undef, $authuser, $code); + }}); + +my $create_stop_worker = sub { + my ($nodename, $type, $vmid, $down_timeout) = @_; + + my $upid; + if ($type eq 'openvz') { + return if !PVE::OpenVZ::check_running($vmid); + my $timeout = defined($down_timeout) ? int($down_timeout) : 60; + print STDERR "Stoping CT $vmid (timeout = $timeout seconds)\n"; + $upid = PVE::API2::OpenVZ->vm_shutdown({node => $nodename, vmid => $vmid, + timeout => $timeout, forceStop => 1 }); + } elsif ($type eq 'qemu') { + return if !PVE::QemuServer::check_running($vmid, 1); + my $timeout = defined($down_timeout) ? int($down_timeout) : 60*3; + print STDERR "Stoping VM $vmid (timeout = $timeout seconds)\n"; + $upid = PVE::API2::Qemu->vm_shutdown({node => $nodename, vmid => $vmid, + timeout => $timeout, forceStop => 1 }); + } else { + die "unknown VM type '$type'\n"; + } + + my $res = PVE::Tools::upid_decode($upid); + + return $res->{pid}; +}; + +__PACKAGE__->register_method ({ + name => 'stopall', + path => 'stopall', + method => 'POST', + protected => 1, + description => "Stop all VMs and Containers.", + parameters => { + additionalProperties => 0, + properties => { + node => get_standard_option('pve-node'), + }, + }, + returns => { + type => 'string', + }, + code => sub { + my ($param) = @_; + + my $rpcenv = PVE::RPCEnvironment::get(); + my $authuser = $rpcenv->get_user(); + + my $nodename = $param->{node}; + $nodename = PVE::INotify::nodename() if $nodename eq 'localhost'; + + my $code = sub { + + $rpcenv->{type} = 'priv'; # to start tasks in background + + my $stopList = &$get_start_stop_list($nodename); + + my $cpuinfo = PVE::ProcFSTools::read_cpuinfo(); + my $maxWorkers = $cpuinfo->{cpus}; + + foreach my $order (sort {$b <=> $a} keys %$stopList) { + my $vmlist = $stopList->{$order}; + my $workers = {}; + foreach my $vmid (sort {$b <=> $a} keys %$vmlist) { + my $d = $vmlist->{$vmid}; + my $pid; + eval { $pid = &$create_stop_worker($nodename, $d->{type}, $vmid, $d->{down}); }; + warn $@ if $@; + next if !$pid; + + $workers->{$pid} = 1; + while (scalar(keys %$workers) >= $maxWorkers) { + foreach my $p (keys %$workers) { + if (!PVE::ProcFSTools::check_process_running($p)) { + delete $workers->{$p}; + } + } + sleep(1); + } + } + while (scalar(keys %$workers)) { + foreach my $p (keys %$workers) { + if (!PVE::ProcFSTools::check_process_running($p)) { + delete $workers->{$p}; + } + } + sleep(1); + } + } + return; + }; + + return $rpcenv->fork_worker('stopall', undef, $authuser, $code); + + }}); + package PVE::API2::Nodes; use strict; diff --git a/bin/init.d/Makefile b/bin/init.d/Makefile index e0d72124..1db889d6 100644 --- a/bin/init.d/Makefile +++ b/bin/init.d/Makefile @@ -3,6 +3,7 @@ include ../../defines.mk all: SCRIPTS = \ + pve-manager \ pvedaemon \ pvebanner \ pvestatd \ diff --git a/bin/init.d/pve-manager b/bin/init.d/pve-manager new file mode 100755 index 00000000..cd8f6aa9 --- /dev/null +++ b/bin/init.d/pve-manager @@ -0,0 +1,39 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: pve-manager +# Required-Start: $remote_fs apache2 qemu-server vz pvestatd +# Required-Stop: $remote_fs apache2 qemu-server vz pvestatd +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: PVE VM Manager +### END INIT INFO + +. /lib/lsb/init-functions + +PATH=/sbin:/bin:/usr/bin:/usr/sbin +DESC="PVE Status Daemon" +PVESH=/usr/bin/pvesh + +test -f $PVESH || exit 0 + +case "$1" in + start) + echo "Starting VMs and Containers" + pvesh --nooutput create /nodes/localhost/startall + ;; + stop) + echo "Stopping VMs and Containers" + pvesh --nooutput create /nodes/localhost/stopall + ;; + reload|restart|force-reload) + # do nothing here + ;; + *) + N=/etc/init.d/$NAME + echo "Usage: $N {start|stop|reload|restart|force-reload}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/debian/conffiles b/debian/conffiles index 2ac125cc..88988e52 100644 --- a/debian/conffiles +++ b/debian/conffiles @@ -1,3 +1,4 @@ +/etc/init.d/pve-manager /etc/init.d/pvedaemon /etc/init.d/pvebanner /etc/init.d/pvenetcommit diff --git a/debian/postinst b/debian/postinst index af0e8bfa..f2176110 100755 --- a/debian/postinst +++ b/debian/postinst @@ -59,6 +59,7 @@ case "$1" in update-rc.d pvestatd defaults 21 79 >/dev/null update-rc.d pvebanner start 99 2 3 4 5 . >/dev/null update-rc.d pvenetcommit start 15 S . >/dev/null + update-rc.d pve-manager defaults 25 75 >/dev/null test -e /proxmox_install_mode || invoke-rc.d pvedaemon restart test -e /proxmox_install_mode || invoke-rc.d pvestatd restart diff --git a/debian/postrm b/debian/postrm index 8f97f0d0..85c47496 100755 --- a/debian/postrm +++ b/debian/postrm @@ -8,6 +8,7 @@ if [ "$1" = purge ]; then update-rc.d pvestatd remove >/dev/null 2>&1 update-rc.d pvebanner remove >/dev/null 2>&1 update-rc.d pvenetcommit remove >/dev/null 2>&1 + update-rc.d pve-manager remove >/dev/null 2>&1 if [ -e /usr/share/debconf/confmodule ]; then . /usr/share/debconf/confmodule -- 2.39.2