Example output during shutdown:
Running guests on default URI: console, rhel6-1, rhel5-64
Running guests on lxc:/// URI: lxc-shell
Running guests on xen:/// URI: error: no hypervisor driver available for xen:///
error: failed to connect to the hypervisor
Running guests on vbox+tcp://orkuz/system URI: no running guests.
Suspending guests on default URI...
Suspending console: done
Suspending rhel6-1: done
Suspending rhel5-64: done
Suspending guests on lxc:/// URI...
Suspending lxc-shell: error: Failed to save domain 9cba8bfb-56f4-6589-2d12-8a58c886dd3b
state
error: this function is not supported by the hypervisor: virDomainManagedSave
Note, the "Suspending $guest: " shows progress during the suspend phase
if domjobinfo gives meaningful output.
Example output during boot:
Resuming guests on default URI...
Resuming guest rhel6-1: done
Resuming guest rhel5-64: done
Resuming guest console: done
Resuming guests on lxc:/// URI...
Resuming guest lxc-shell: already active
Configuration used for generating the examples above:
URIS='default lxc:/// xen:/// vbox+tcp://orkuz/system'
The script uses /var/lib/libvirt/libvirt-guests files to note all active
guest it should try to resume on next boot. It's content looks like:
default 7f8b9d93-30e1-f0b9-47a7-cb408482654b 085b4c95-5da2-e8e1-712f-6ea6a4156af2
fb4d8360-5305-df3a-2da1-07d682891b8c
lxc:/// 9cba8bfb-56f4-6589-2d12-8a58c886dd3b
---
Version 2 changes:
- fixes suggested by Eric
- configurable ON_BOOT and ON_SHUTDOWN behavior inspired by Gerd
daemon/Makefile.am | 16 ++-
daemon/libvirt-guests.init.in | 295 +++++++++++++++++++++++++++++++++++++++++
daemon/libvirt-guests.sysconf | 24 ++++
libvirt.spec.in | 4 +
4 files changed, 335 insertions(+), 4 deletions(-)
create mode 100644 daemon/libvirt-guests.init.in
create mode 100644 daemon/libvirt-guests.sysconf
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index a82e9a9..ed469bf 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -29,6 +29,8 @@ EXTRA_DIST = \
libvirtd.lxc.logrotate.in \
libvirtd.uml.logrotate.in \
test_libvirtd.aug \
+ libvirt-guests.init.in \
+ libvirt-guests.sysconf \
$(AVAHI_SOURCES) \
$(DAEMON_SOURCES)
@@ -216,21 +218,27 @@ install-logrotate: $(LOGROTATE_CONFS)
$(INSTALL_DATA) libvirtd.uml.logrotate $(DESTDIR)$(sysconfdir)/logrotate.d/libvirtd.uml
if LIBVIRT_INIT_SCRIPT_RED_HAT
-install-init: libvirtd.init
+install-init: libvirtd.init libvirt-guests.init
mkdir -p $(DESTDIR)$(sysconfdir)/rc.d/init.d
$(INSTALL_SCRIPT) libvirtd.init \
$(DESTDIR)$(sysconfdir)/rc.d/init.d/libvirtd
+ $(INSTALL_SCRIPT) libvirt-guests.init \
+ $(DESTDIR)$(sysconfdir)/rc.d/init.d/libvirt-guests
mkdir -p $(DESTDIR)$(sysconfdir)/sysconfig
$(INSTALL_SCRIPT) $(srcdir)/libvirtd.sysconf \
$(DESTDIR)$(sysconfdir)/sysconfig/libvirtd
+ $(INSTALL_SCRIPT) $(srcdir)/libvirt-guests.sysconf \
+ $(DESTDIR)$(sysconfdir)/sysconfig/libvirt-guests
uninstall-init:
rm -f $(DESTDIR)$(sysconfdir)/rc.d/init.d/libvirtd \
- $(DESTDIR)$(sysconfdir)/sysconfig/libvirtd
+ $(DESTDIR)$(sysconfdir)/sysconfig/libvirtd \
+ $(DESTDIR)$(sysconfdir)/rc.d/init.d/libvirt-guests \
+ $(DESTDIR)$(sysconfdir)/sysconfig/libvirt-guests
-BUILT_SOURCES += libvirtd.init
+BUILT_SOURCES += libvirtd.init libvirt-guests.init
-libvirtd.init: libvirtd.init.in
+%.init: %.init.in
$(AM_V_GEN)sed \
-e s!\@localstatedir\@!@localstatedir@!g \
-e s!\@sbindir\@!@sbindir@!g \
diff --git a/daemon/libvirt-guests.init.in b/daemon/libvirt-guests.init.in
new file mode 100644
index 0000000..826f415
--- /dev/null
+++ b/daemon/libvirt-guests.init.in
@@ -0,0 +1,295 @@
+#!/bin/sh
+
+# the following is the LSB init header
+#
+### BEGIN INIT INFO
+# Provides: libvirt-guests
+# Required-Start: libvirtd
+# Required-Stop: libvirtd
+# Default-Start: 3 4 5
+# Short-Description: suspend/resume libvirt guests on shutdown/boot
+# Description: This is a script for suspending active libvirt guests
+# on shutdown and resuming them on next boot
+# See
http://libvirt.org
+### END INIT INFO
+
+# the following is chkconfig init header
+#
+# libvirt-guests: suspend/resume libvirt guests on shutdown/boot
+#
+# chkconfig: 345 98 02
+# description: This is a script for suspending active libvirt guests
+# on shutdown and resuming them on next boot
+# See
http://libvirt.org
+#
+
+sysconfdir=@sysconfdir@
+localstatedir=@localstatedir@
+
+# Source function library.
+. "$sysconfdir"/rc.d/init.d/functions
+
+URIS=default
+ON_BOOT=start
+ON_SHUTDOWN=suspend
+SHUTDOWN_TIMEOUT=0
+
+test -f "$sysconfdir"/sysconfig/libvirt-guests && .
"$sysconfdir"/sysconfig/libvirt-guests
+
+LISTFILE="$localstatedir"/lib/libvirt/libvirt-guests
+
+RETVAL=0
+
+retval() {
+ "$@"
+ if [ $? -ne 0 ]; then
+ RETVAL=1
+ return 1
+ else
+ return 0
+ fi
+}
+
+run_virsh() {
+ uri=$1
+ shift
+
+ if [ "x$uri" = xdefault ]; then
+ conn=
+ else
+ conn="-c $uri"
+ fi
+
+ virsh $conn "$@"
+}
+
+run_virsh_c() {
+ ( export LC_ALL=C; run_virsh "$@" )
+}
+
+list_guests() {
+ uri=$1
+
+ list=$(run_virsh_c $uri list)
+ if [ $? -ne 0 ]; then
+ RETVAL=1
+ return 1
+ fi
+
+ uuids=
+ for id in $(echo "$list" | awk 'NR > 2 {print $1}'); do
+ uuid=$(run_virsh_c $uri dominfo $id | awk '/^UUID:/{print $2}')
+ if [ -z "$uuid" ]; then
+ RETVAL=1
+ return 1
+ fi
+ uuids="$uuids $uuid"
+ done
+
+ echo $uuids
+}
+
+guest_name() {
+ uri=$1
+ uuid=$2
+
+ name=$(run_virsh_c $uri dominfo $uuid 2>/dev/null | \
+ awk '/^Name:/{print $2}')
+ [ -n "$name" ] || name=$uuid
+
+ echo "$name"
+}
+
+guest_is_on() {
+ uri=$1
+ uuid=$2
+
+ guest_running=false
+ info=$(run_virsh_c $uri dominfo $uuid)
+ if [ $? -ne 0 ]; then
+ RETVAL=1
+ return 1
+ fi
+
+ id=$(echo "$info" | awk '/^Id:/{print $2}')
+
+ [ -n "$id" ] && [ "x$id" != x- ] &&
guest_running=true
+ return 0
+}
+
+start() {
+ [ -f $LISTFILE ] || return 0
+
+ if [ "x$ON_BOOT" != xstart ]; then
+ echo $"libvirt-guests is configured not to start any guests on boot"
+ rm -f $LISTFILE
+ return 0
+ fi
+
+ while read uri list; do
+ configured=false
+ for confuri in $URIS; do
+ if [ $confuri = $uri ]; then
+ configured=true
+ break
+ fi
+ done
+ if ! $configured; then
+ echo $"Ignoring guests on $uri URI"
+ continue
+ fi
+
+ echo $"Resuming guests on $uri URI..."
+ for guest in $list; do
+ name=$(guest_name $uri $guest)
+ echo -n $"Resuming guest $name: "
+ if guest_is_on $uri $guest; then
+ if $guest_running; then
+ echo $"already active"
+ else
+ retval run_virsh $uri start "$name" >/dev/null
&& \
+ echo $"done"
+ fi
+ fi
+ done
+ done <$LISTFILE
+
+ rm -f $LISTFILE
+}
+
+suspend_guest()
+{
+ uri=$1
+ guest=$2
+
+ name=$(guest_name $uri $guest)
+ label=$"Suspending $name: "
+ echo -n "$label"
+ run_virsh $uri managedsave $guest >/dev/null &
+ virsh_pid=$!
+ while true; do
+ sleep 1
+ kill -0 $virsh_pid >&/dev/null || break
+ progress=$(run_virsh_c $uri domjobinfo $guest 2>/dev/null | \
+ awk '/^Data processed:/{print $3, $4}')
+ if [ -n "$progress" ]; then
+ printf '\r%s%12s ' "$label" "$progress"
+ else
+ printf '\r%s%-12s ' "$label" "..."
+ fi
+ done
+ retval wait $virsh_pid && printf '\r%s%-12s\n' "$label"
$"done"
+}
+
+shutdown_guest()
+{
+ uri=$1
+ guest=$2
+
+ name=$(guest_name $uri $guest)
+ label=$"Shutting down $name: "
+ echo -n "$label"
+ retval run_virsh $uri shutdown $guest >/dev/null || return
+ timeout=$SHUTDOWN_TIMEOUT
+ while [ $timeout -gt 0 ]; do
+ sleep 1
+ timeout=$[timeout - 1]
+ guest_is_on $uri $guest || return
+ $guest_running || break
+ printf '\r%s%-12d ' "$label" $timeout
+ done
+
+ if guest_is_on $uri $guest; then
+ if $guest_running; then
+ printf '\r%s%-12s\n' "$label" $"failed to shutdown in
time"
+ else
+ printf '\r%s%-12s\n' "$label" $"done"
+ fi
+ fi
+}
+
+stop() {
+ # last stop was not followed by start
+ [ -f $LISTFILE ] && return 0
+
+ suspending=true
+ if [ "x$ON_SHUTDOWN" = xshutdown ]; then
+ suspending=false
+ if [ $SHUTDOWN_TIMEOUT -le 0 ]; then
+ echo $"Shutdown action requested but SHUTDOWN_TIMEOUT was not set"
+ RETVAL=6
+ return
+ fi
+ fi
+
+ : >$LISTFILE
+ for uri in $URIS; do
+ echo -n $"Running guests on $uri URI: "
+ list=$(list_guests $uri)
+ if [ $? -eq 0 ]; then
+ empty=true
+ for uuid in $list; do
+ $empty || printf ", "
+ echo -n $(guest_name $uri $uuid)
+ empty=false
+ done
+ if $empty; then
+ echo $"no running guests."
+ else
+ echo
+ echo $uri $list >>$LISTFILE
+ fi
+ fi
+ done
+
+ while read uri list; do
+ if $suspending; then
+ echo $"Suspending guests on $uri URI..."
+ else
+ echo $"Shutting down guests on $uri URI..."
+ fi
+
+ for guest in $list; do
+ if $suspending; then
+ suspend_guest $uri $guest
+ else
+ shutdown_guest $uri $guest
+ fi
+ done
+ done <$LISTFILE
+}
+
+gueststatus() {
+ for uri in $URIS; do
+ echo "* $uri URI:"
+ retval run_virsh $uri list || echo
+ done
+}
+
+# See how we were called.
+case "$1" in
+ start|stop|gueststatus)
+ $1
+ ;;
+ restart)
+ stop && start
+ ;;
+ force-reload)
+ ;;
+ status)
+ if [ -f $LISTFILE ]; then
+ RETVAL=3
+ else
+ RETVAL=0
+ fi
+ ;;
+ shutdown)
+ ON_SHUTDOWN=shutdown
+ stop
+ ;;
+ *)
+ echo $"Usage: $0
{start|stop|restart|force-reload|gueststatus|shutdown}"
+ exit 3
+ ;;
+esac
+exit $RETVAL
diff --git a/daemon/libvirt-guests.sysconf b/daemon/libvirt-guests.sysconf
new file mode 100644
index 0000000..cd58728
--- /dev/null
+++ b/daemon/libvirt-guests.sysconf
@@ -0,0 +1,24 @@
+# URIs to check for running guests
+# example: URIS='default xen:/// vbox+tcp://host/system lxc:///'
+#URIS=default
+
+# action taken on host boot
+# - start all guests which were running on shutdown are started on boot
+# regardless on their autostart settings
+# - ignore libvirt-guests init script won't start any guest on boot, however,
+# guests marked as autostart will still be automatically started by
+# libvirtd
+#ON_BOOT=start
+
+# action taken on host shutdown
+# - suspend all running guests are suspended using virsh managedsave
+# - shutdown all running guests are asked to shutdown. Please be careful with
+# this settings since there is no way to distinguish between a
+# guest which is stuck or ignores shutdown requests and a guest
+# which just needs a long time to shutdown. When setting
+# ON_SHUTDOWN=shutdown, you must also set SHUTDOWN_TIMEOUT to a
+# value suitable for your guests.
+#ON_SHUTDOWN=suspend
+
+# number of seconds we're willing to wait for a guest to shut down
+#SHUTDOWN_TIMEOUT=0
diff --git a/libvirt.spec.in b/libvirt.spec.in
index 7d5ea85..3d3b871 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -830,6 +830,10 @@ fi
%{_datadir}/libvirt/cpu_map.xml
+%{_sysconfdir}/rc.d/init.d/libvirt-guests
+%config(noreplace) %{_sysconfdir}/sysconfig/libvirt-guests
+%dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt
+
%if %{with_sasl}
%config(noreplace) %{_sysconfdir}/sasl2/libvirt.conf
%endif
--
1.7.1