Most of this deals with moving the libvirt-guests.sh script which does all the work to /usr/libexec, so it can be shared by both systemd and traditional init. Previously systemd depended on the script being in /etc/init.d Required to fix https://bugzilla.redhat.com/show_bug.cgi?id=789747 --- Only change to the big script is dropping the LSB header .gitignore | 1 + libvirt.spec.in | 6 +- po/POTFILES.in | 2 +- tools/Makefile.am | 18 +- tools/libvirt-guests.init.in | 27 ++ tools/libvirt-guests.init.sh | 597 ---------------------------------------- tools/libvirt-guests.service.in | 4 +- tools/libvirt-guests.sh.in | 573 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 620 insertions(+), 608 deletions(-) create mode 100644 tools/libvirt-guests.init.in delete mode 100644 tools/libvirt-guests.init.sh create mode 100644 tools/libvirt-guests.sh.in diff --git a/.gitignore b/.gitignore index 0dadd21..ca7d07a 100644 --- a/.gitignore +++ b/.gitignore @@ -179,6 +179,7 @@ /tools/*.[18] /tools/libvirt-guests.init /tools/libvirt-guests.service +/tools/libvirt-guests.sh /tools/virsh /tools/virsh-*-edit.c /tools/virt-*-validate diff --git a/libvirt.spec.in b/libvirt.spec.in index d6e1fbe..d463c27 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1409,8 +1409,6 @@ rm -rf $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/libvirtd.uml mv $RPM_BUILD_ROOT%{_datadir}/doc/libvirt-%{version} \ $RPM_BUILD_ROOT%{_datadir}/doc/libvirt-docs-%{version} -sed -i -e "s|$RPM_BUILD_ROOT||g" $RPM_BUILD_ROOT%{_sysconfdir}/rc.d/init.d/libvirt-guests - %if %{with_dtrace} %ifarch %{power64} s390x x86_64 ia64 alpha sparc64 mv $RPM_BUILD_ROOT%{_datadir}/systemtap/tapset/libvirt_probes.stp \ @@ -1657,10 +1655,13 @@ fi %dir %attr(0700, root, root) %{_sysconfdir}/libvirt/nwfilter/ +%attr(0755, root, root) %{_libexecdir}/libvirt-guests.sh %if %{with_systemd} %{_unitdir}/libvirtd.service +%{_unitdir}/libvirt-guests.service %else %{_sysconfdir}/rc.d/init.d/libvirtd +%{_sysconfdir}/rc.d/init.d/libvirt-guests %endif %doc daemon/libvirtd.upstart %config(noreplace) %{_sysconfdir}/sysconfig/libvirtd @@ -1925,7 +1926,6 @@ rm -f $RPM_BUILD_ROOT%{_sysconfdir}/sysctl.d/libvirtd %{_datadir}/libvirt/cpu_map.xml -%{_sysconfdir}/rc.d/init.d/libvirt-guests %if %{with_systemd} %{_unitdir}/libvirt-guests.service %endif diff --git a/po/POTFILES.in b/po/POTFILES.in index 586aa2b..6d78f48 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -194,7 +194,7 @@ src/xenapi/xenapi_utils.c src/xenxs/xen_sxpr.c src/xenxs/xen_xm.c tools/console.c -tools/libvirt-guests.init.sh +tools/libvirt-guests.sh.in tools/virsh.c tools/virsh-domain-monitor.c tools/virsh-domain.c diff --git a/tools/Makefile.am b/tools/Makefile.am index d646bab..605bf3b 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -41,6 +41,7 @@ DISTCLEANFILES = bin_SCRIPTS = virt-xml-validate virt-pki-validate bin_PROGRAMS = virsh virt-host-validate +libexec_SCRIPTS = libvirt-guests.sh if HAVE_SANLOCK sbin_SCRIPTS = virt-sanlock-cleanup @@ -187,7 +188,7 @@ uninstall-sysconfig: rm -f $(DESTDIR)$(sysconfdir)/sysconfig/libvirt-guests rmdir $(DESTDIR)$(sysconfdir)/sysconfig ||: -EXTRA_DIST += libvirt-guests.init.sh +EXTRA_DIST += libvirt-guests.sh.in libvirt-guests.init.in install-initscript: libvirt-guests.init $(MKDIR_P) $(DESTDIR)$(sysconfdir)/rc.d/init.d @@ -208,7 +209,7 @@ install-init: uninstall-init: endif # LIBVIRT_INIT_SCRIPT_RED_HAT -libvirt-guests.init: libvirt-guests.init.sh $(top_builddir)/config.status +libvirt-guests.sh: libvirt-guests.sh.in $(top_builddir)/config.status $(AM_V_GEN)sed \ -e 's!\@PACKAGE\@!$(PACKAGE)!g' \ -e 's!\@bindir\@!$(bindir)!g' \ @@ -219,20 +220,26 @@ libvirt-guests.init: libvirt-guests.init.sh $(top_builddir)/config.status < $< > $@-t && \ chmod a+x $@-t && \ mv $@-t $@ +BUILT_SOURCES += libvirt-guests.sh +libvirt-guests.init: libvirt-guests.init.in libvirt-guests.sh + $(AM_V_GEN)sed \ + -e 's!\@libexecdir\@!$(libexecdir)!g' \ + < $< > $@-t && \ + chmod a+x $@-t && \ + mv $@-t $@ EXTRA_DIST += libvirt-guests.service.in - SYSTEMD_UNIT_DIR = /lib/systemd/system if LIBVIRT_INIT_SCRIPT_SYSTEMD -install-systemd: libvirt-guests.service install-initscript install-sysconfig +install-systemd: libvirt-guests.service install-sysconfig libvirt-guests.sh $(MKDIR_P) $(DESTDIR)$(SYSTEMD_UNIT_DIR) $(INSTALL_DATA) libvirt-guests.service \ $(DESTDIR)$(SYSTEMD_UNIT_DIR)/libvirt-guests.service -uninstall-systemd: uninstall-initscript uninstall-sysconfig +uninstall-systemd: uninstall-sysconfig rm -f $(DESTDIR)$(SYSTEMD_UNIT_DIR)/libvirt-guests.service rmdir $(DESTDIR)$(SYSTEMD_UNIT_DIR) ||: @@ -251,6 +258,7 @@ libvirt-guests.service: libvirt-guests.service.in $(top_builddir)/config.status -e 's!\@localstatedir\@!$(localstatedir)!g' \ -e 's!\@sbindir\@!$(sbindir)!g' \ -e 's!\@sysconfdir\@!$(sysconfdir)!g' \ + -e 's!\@libexecdir\@!$(libexecdir)!g' \ < $< > $@-t && \ chmod a+x $@-t && \ mv $@-t $@ diff --git a/tools/libvirt-guests.init.in b/tools/libvirt-guests.init.in new file mode 100644 index 0000000..5f9a60e --- /dev/null +++ b/tools/libvirt-guests.init.in @@ -0,0 +1,27 @@ +#!/bin/sh + +# the following is the LSB init header +# +### BEGIN INIT INFO +# Provides: libvirt-guests +# Required-Start: libvirtd +# Required-Stop: libvirtd +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# 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 99 01 +# description: This is a script for suspending active libvirt guests \ +# on shutdown and resuming them on next boot \ +# See http://libvirt.org +# + +exec @libexecdir@/libvirt-guests.sh "$@" diff --git a/tools/libvirt-guests.init.sh b/tools/libvirt-guests.init.sh deleted file mode 100644 index 99ef331..0000000 --- a/tools/libvirt-guests.init.sh +++ /dev/null @@ -1,597 +0,0 @@ -#!/bin/sh - -# the following is the LSB init header -# -### BEGIN INIT INFO -# Provides: libvirt-guests -# Required-Start: libvirtd -# Required-Stop: libvirtd -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# 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 99 01 -# 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@" -libvirtd="@sbindir@"/libvirtd - -# Source function library. -test ! -r "$sysconfdir"/rc.d/init.d/functions || - . "$sysconfdir"/rc.d/init.d/functions - -# Source gettext library. -# Make sure this file is recognized as having translations: _("dummy") -. "@bindir@"/gettext.sh - -export TEXTDOMAIN="@PACKAGE@" TEXTDOMAINDIR="@localedir@" - -URIS=default -ON_BOOT=start -ON_SHUTDOWN=suspend -SHUTDOWN_TIMEOUT=300 -PARALLEL_SHUTDOWN=0 -START_DELAY=0 -BYPASS_CACHE=0 - -test -f "$sysconfdir"/sysconfig/libvirt-guests && - . "$sysconfdir"/sysconfig/libvirt-guests - -LISTFILE="$localstatedir"/lib/libvirt/libvirt-guests -VAR_SUBSYS_LIBVIRT_GUESTS="$localstatedir"/lock/subsys/libvirt-guests - -RETVAL=0 - -# retval COMMAND ARGUMENTS... -# run command with arguments and convert non-zero return value to 1 and set -# the global return variable -retval() { - "$@" - if [ $? -ne 0 ]; then - RETVAL=1 - return 1 - else - return 0 - fi -} - -# run_virsh URI ARGUMENTS... -# start virsh and let it execute ARGUMENTS on URI -# If URI is "default" virsh is called without the "-c" argument -# (using libvirt's default connection) -run_virsh() { - uri=$1 - shift - - if [ "x$uri" = xdefault ]; then - virsh "$@" </dev/null - else - virsh -c "$uri" "$@" </dev/null - fi -} - -# run_virsh_c URI ARGUMENTS -# Same as "run_virsh" but the "C" locale is used instead of -# the system's locale. -run_virsh_c() { - ( export LC_ALL=C; run_virsh "$@" ) -} - -# test_connect URI -# check if URI is reachable -test_connect() -{ - uri=$1 - - run_virsh "$uri" connect 2>/dev/null - if [ $? -ne 0 ]; then - eval_gettext "Can't connect to \$uri. Skipping." - echo - return 1 - fi -} - -# list_guests URI PERSISTENT -# List running guests on URI. -# PERSISTENT argument options: -# --persistent: list only persistent guests -# --transient: list only transient guests -# [none]: list both persistent and transient guests -list_guests() { - uri=$1 - persistent=$2 - - list=$(run_virsh_c "$uri" list --uuid $persistent) - if [ $? -ne 0 ]; then - RETVAL=1 - return 1 - fi - - echo $list -} - -# guest_name URI UUID -# return name of guest UUID on URI -guest_name() { - uri=$1 - uuid=$2 - - run_virsh "$uri" domname "$uuid" 2>/dev/null -} - -# guest_is_on URI UUID -# check if guest UUID on URI is running -# Result is returned by variable "guest_running" -guest_is_on() { - uri=$1 - uuid=$2 - - guest_running=false - id=$(run_virsh "$uri" domid "$uuid") - if [ $? -ne 0 ]; then - RETVAL=1 - return 1 - fi - - [ -n "$id" ] && [ "x$id" != x- ] && guest_running=true - return 0 -} - -# started -# Create the startup lock file -started() { - touch "$VAR_SUBSYS_LIBVIRT_GUESTS" -} - -# start -# Start or resume the guests -start() { - [ -f "$LISTFILE" ] || { started; return 0; } - - if [ "x$ON_BOOT" != xstart ]; then - gettext "libvirt-guests is configured not to start any guests on boot" - echo - rm -f "$LISTFILE" - started - return 0 - fi - - isfirst=true - bypass= - test "x$BYPASS_CACHE" = x0 || bypass=--bypass-cache - while read uri list; do - configured=false - set -f - for confuri in $URIS; do - set +f - if [ "x$confuri" = "x$uri" ]; then - configured=true - break - fi - done - set +f - if ! "$configured"; then - eval_gettext "Ignoring guests on \$uri URI"; echo - continue - fi - - test_connect "$uri" || continue - - eval_gettext "Resuming guests on \$uri URI..."; echo - for guest in $list; do - name=$(guest_name "$uri" "$guest") - eval_gettext "Resuming guest \$name: " - if guest_is_on "$uri" "$guest"; then - if "$guest_running"; then - gettext "already active"; echo - else - if "$isfirst"; then - isfirst=false - else - sleep $START_DELAY - fi - retval run_virsh "$uri" start $bypass "$name" \ - >/dev/null && \ - gettext "done"; echo - fi - fi - done - done <"$LISTFILE" - - rm -f "$LISTFILE" - started -} - -# suspend_guest URI GUEST -# Do a managed save on a GUEST on URI. This function returns after the guest -# was saved. -suspend_guest() -{ - uri=$1 - guest=$2 - - name=$(guest_name "$uri" "$guest") - label=$(eval_gettext "Suspending \$name: ") - bypass= - slept=0 - test "x$BYPASS_CACHE" = x0 || bypass=--bypass-cache - printf '%s...\n' "$label" - run_virsh "$uri" managedsave $bypass "$guest" >/dev/null & - virsh_pid=$! - while true; do - sleep 1 - kill -0 "$virsh_pid" >/dev/null 2>&1 || break - - slept=$(($slept + 1)) - if [ $(($slept % 5)) -eq 0 ]; then - progress=$(run_virsh_c "$uri" domjobinfo "$guest" 2>/dev/null | \ - awk '/^Data processed:/{print $3, $4}') - if [ -n "$progress" ]; then - printf '%s%s\n' "$label" "$progress" - else - printf '%s%s\n' "$label" "..." - fi - fi - done - retval wait "$virsh_pid" && printf '%s%s\n' "$label" "$(gettext "done")" -} - -# shutdown_guest URI GUEST -# Start a ACPI shutdown of GUEST on URI. This function return after the quest -# was successfully shutdown or the timeout defined by $SHUTDOWN_TIMEOUT expires. -shutdown_guest() -{ - uri=$1 - guest=$2 - - name=$(guest_name "$uri" "$guest") - eval_gettext "Starting shutdown on guest: \$name" - echo - retval run_virsh "$uri" shutdown "$guest" >/dev/null || return - timeout=$SHUTDOWN_TIMEOUT - check_timeout=false - if [ $timeout -gt 0 ]; then - check_timeout=true - format=$(eval_gettext "Waiting for guest %s to shut down, %d seconds left\n") - else - slept=0 - format=$(eval_gettext "Waiting for guest %s to shut down\n") - fi - while ! $check_timeout || [ "$timeout" -gt 0 ]; do - sleep 1 - guest_is_on "$uri" "$guest" || return - "$guest_running" || break - - if $check_timeout; then - if [ $(($timeout % 5)) -eq 0 ]; then - printf "$format" "$name" "$timeout" - fi - timeout=$(($timeout - 1)) - else - slept=$(($slept + 1)) - if [ $(($slept % 5)) -eq 0 ]; then - printf "$format" "$name" - fi - fi - done - - if guest_is_on "$uri" "$guest"; then - if "$guest_running"; then - eval_gettext "Shutdown of guest \$name failed to complete in time." - else - eval_gettext "Shutdown of guest \$name complete." - fi - fi -} - -# shutdown_guest_async URI GUEST -# Start a ACPI shutdown of GUEST on URI. This function returns after the command -# was issued to libvirt to allow parallel shutdown. -shutdown_guest_async() -{ - uri=$1 - guest=$2 - - name=$(guest_name "$uri" "$guest") - eval_gettext "Starting shutdown on guest: \$name" - echo - retval run_virsh "$uri" shutdown "$guest" > /dev/null -} - -# guest_count GUEST_LIST -# Returns number of guests in GUEST_LIST -guest_count() -{ - set -- $1 - echo $# -} - -# check_guests_shutdown URI GUESTS -# check if shutdown is complete on guests in "GUESTS" and returns only -# guests that are still shutting down -check_guests_shutdown() -{ - uri=$1 - guests=$2 - - guests_up= - for guest in $guests; do - if ! guest_is_on "$uri" "$guest" >/dev/null 2>&1; then - eval_gettext "Failed to determine state of guest: \$guest. Not tracking it anymore." - echo - continue - fi - if "$guest_running"; then - guests_up="$guests_up $guest" - fi - done - echo "$guests_up" -} - -# print_guests_shutdown URI BEFORE AFTER -# Checks for differences in the lists BEFORE and AFTER and prints -# a shutdown complete notice for guests that have finished -print_guests_shutdown() -{ - uri=$1 - before=$2 - after=$3 - - for guest in $before; do - case " $after " in - *" $guest "*) continue;; - esac - - name=$(guest_name "$uri" "$guest") - eval_gettext "Shutdown of guest \$name complete." - echo - done -} - -# shutdown_guests_parallel URI GUESTS -# Shutdown guests GUESTS on machine URI in parallel -shutdown_guests_parallel() -{ - uri=$1 - guests=$2 - - on_shutdown= - check_timeout=false - timeout=$SHUTDOWN_TIMEOUT - if [ $timeout -gt 0 ]; then - check_timeout=true - format=$(eval_gettext "Waiting for %d guests to shut down, %d seconds left\n") - else - slept=0 - format=$(eval_gettext "Waiting for %d guests to shut down\n") - fi - while [ -n "$on_shutdown" ] || [ -n "$guests" ]; do - while [ -n "$guests" ] && - [ $(guest_count "$on_shutdown") -lt "$PARALLEL_SHUTDOWN" ]; do - set -- $guests - guest=$1 - shift - guests=$* - shutdown_guest_async "$uri" "$guest" - on_shutdown="$on_shutdown $guest" - done - sleep 1 - - set -- $guests - guestcount=$# - set -- $on_shutdown - shutdowncount=$# - - if $check_timeout; then - if [ $(($timeout % 5)) -eq 0 ]; then - printf "$format" $(($guestcount + $shutdowncount)) "$timeout" - fi - timeout=$(($timeout - 1)) - if [ $timeout -le 0 ]; then - eval_gettext "Timeout expired while shutting down domains"; echo - RETVAL=1 - return - fi - else - slept=$(($slept + 1)) - if [ $(($slept % 5)) -eq 0 ]; then - printf "$format" $(($guestcount + $shutdowncount)) - fi - fi - - on_shutdown_prev=$on_shutdown - on_shutdown=$(check_guests_shutdown "$uri" "$on_shutdown") - print_guests_shutdown "$uri" "$on_shutdown_prev" "$on_shutdown" - done -} - -# stop -# Shutdown or save guests on the configured uris -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 -lt 0 ]; then - gettext "SHUTDOWN_TIMEOUT must be equal or greater than 0" - echo - RETVAL=6 - return - fi - fi - - : >"$LISTFILE" - set -f - for uri in $URIS; do - set +f - - test_connect "$uri" || continue - - eval_gettext "Running guests on \$uri URI: " - - list=$(list_guests "$uri") - if [ $? -eq 0 ]; then - empty=true - for uuid in $list; do - "$empty" || printf ", " - printf %s "$(guest_name "$uri" "$uuid")" - empty=false - done - - if "$empty"; then - gettext "no running guests." - fi - echo - fi - - if "$suspending"; then - transient=$(list_guests "$uri" "--transient") - if [ $? -eq 0 ]; then - empty=true - for uuid in $transient; do - if "$empty"; then - eval_gettext "Not suspending transient guests on URI: \$uri: " - empty=false - else - printf ", " - fi - printf %s "$(guest_name "$uri" "$uuid")" - done - echo - # reload domain list to contain only persistent guests - list=$(list_guests "$uri" "--persistent") - if [ $? -ne 0 ]; then - eval_gettext "Failed to list persistent guests on \$uri" - echo - RETVAL=1 - set +f - return - fi - else - gettext "Failed to list transient guests" - echo - RETVAL=1 - set +f - return - fi - fi - - if [ -n "$list" ]; then - echo "$uri" "$list" >>"$LISTFILE" - fi - done - set +f - - if [ -s "$LISTFILE" ]; then - while read uri list; do - if "$suspending"; then - eval_gettext "Suspending guests on \$uri URI..."; echo - else - eval_gettext "Shutting down guests on \$uri URI..."; echo - fi - - if [ "$PARALLEL_SHUTDOWN" -gt 1 ] && - ! "$suspending"; then - shutdown_guests_parallel "$uri" "$list" - else - for guest in $list; do - if "$suspending"; then - suspend_guest "$uri" "$guest" - else - shutdown_guest "$uri" "$guest" - fi - done - fi - done <"$LISTFILE" - else - rm -f "$LISTFILE" - fi - - rm -f "$VAR_SUBSYS_LIBVIRT_GUESTS" -} - -# gueststatus -# List status of guests -gueststatus() { - set -f - for uri in $URIS; do - set +f - echo "* $uri URI:" - retval run_virsh "$uri" list || echo - done - set +f -} - -# rh_status -# Display current status: whether saved state exists, and whether start -# has been executed. We cannot use status() from the functions library, -# since there is no external daemon process matching this init script. -rh_status() { - if [ -f "$LISTFILE" ]; then - gettext "stopped, with saved guests"; echo - RETVAL=3 - else - if [ -f "$VAR_SUBSYS_LIBVIRT_GUESTS" ]; then - gettext "started"; echo - else - gettext "stopped, with no saved guests"; echo - fi - RETVAL=0 - fi -} - -# usage [val] -# Display usage string, then exit with VAL (defaults to 2). -usage() { - program_name=$0 - eval_gettext "Usage: \$program_name {start|stop|status|restart|"\ -"condrestart|try-restart|reload|force-reload|gueststatus|shutdown}"; echo - exit ${1-2} -} - -# See how we were called. -if test $# != 1; then - usage -fi -case "$1" in - --help) - usage 0 - ;; - start|stop|gueststatus) - "$1" - ;; - restart) - stop && start - ;; - condrestart|try-restart) - [ -f "$VAR_SUBSYS_LIBVIRT_GUESTS" ] && stop && start - ;; - reload|force-reload) - # Nothing to do; we reread configuration on each invocation - ;; - status) - rh_status - ;; - shutdown) - ON_SHUTDOWN=shutdown - stop - ;; - *) - usage - ;; -esac -exit $RETVAL diff --git a/tools/libvirt-guests.service.in b/tools/libvirt-guests.service.in index 0f0c41c..d41bf2b 100644 --- a/tools/libvirt-guests.service.in +++ b/tools/libvirt-guests.service.in @@ -6,8 +6,8 @@ After=syslog.target network.target EnvironmentFile=-/etc/sysconfig/libvirt-guests # Hack just call traditional service until we factor # out the code -ExecStart=/etc/init.d/libvirt-guests start -ExecStop=/etc/init.d/libvirt-guests stop +ExecStart=@libexecdir@/libvirt-guests.sh start +ExecStop=@libexecdir@/libvirt-guests.sh stop Type=oneshot RemainAfterExit=yes StandardOutput=journal+console diff --git a/tools/libvirt-guests.sh.in b/tools/libvirt-guests.sh.in new file mode 100644 index 0000000..1c9c46b --- /dev/null +++ b/tools/libvirt-guests.sh.in @@ -0,0 +1,573 @@ +#!/bin/sh + +sysconfdir="@sysconfdir@" +localstatedir="@localstatedir@" +libvirtd="@sbindir@"/libvirtd + +# Source function library. +test ! -r "$sysconfdir"/rc.d/init.d/functions || + . "$sysconfdir"/rc.d/init.d/functions + +# Source gettext library. +# Make sure this file is recognized as having translations: _("dummy") +. "@bindir@"/gettext.sh + +export TEXTDOMAIN="@PACKAGE@" TEXTDOMAINDIR="@localedir@" + +URIS=default +ON_BOOT=start +ON_SHUTDOWN=suspend +SHUTDOWN_TIMEOUT=300 +PARALLEL_SHUTDOWN=0 +START_DELAY=0 +BYPASS_CACHE=0 + +test -f "$sysconfdir"/sysconfig/libvirt-guests && + . "$sysconfdir"/sysconfig/libvirt-guests + +LISTFILE="$localstatedir"/lib/libvirt/libvirt-guests +VAR_SUBSYS_LIBVIRT_GUESTS="$localstatedir"/lock/subsys/libvirt-guests + +RETVAL=0 + +# retval COMMAND ARGUMENTS... +# run command with arguments and convert non-zero return value to 1 and set +# the global return variable +retval() { + "$@" + if [ $? -ne 0 ]; then + RETVAL=1 + return 1 + else + return 0 + fi +} + +# run_virsh URI ARGUMENTS... +# start virsh and let it execute ARGUMENTS on URI +# If URI is "default" virsh is called without the "-c" argument +# (using libvirt's default connection) +run_virsh() { + uri=$1 + shift + + if [ "x$uri" = xdefault ]; then + virsh "$@" </dev/null + else + virsh -c "$uri" "$@" </dev/null + fi +} + +# run_virsh_c URI ARGUMENTS +# Same as "run_virsh" but the "C" locale is used instead of +# the system's locale. +run_virsh_c() { + ( export LC_ALL=C; run_virsh "$@" ) +} + +# test_connect URI +# check if URI is reachable +test_connect() +{ + uri=$1 + + run_virsh "$uri" connect 2>/dev/null + if [ $? -ne 0 ]; then + eval_gettext "Can't connect to \$uri. Skipping." + echo + return 1 + fi +} + +# list_guests URI PERSISTENT +# List running guests on URI. +# PERSISTENT argument options: +# --persistent: list only persistent guests +# --transient: list only transient guests +# [none]: list both persistent and transient guests +list_guests() { + uri=$1 + persistent=$2 + + list=$(run_virsh_c "$uri" list --uuid $persistent) + if [ $? -ne 0 ]; then + RETVAL=1 + return 1 + fi + + echo $list +} + +# guest_name URI UUID +# return name of guest UUID on URI +guest_name() { + uri=$1 + uuid=$2 + + run_virsh "$uri" domname "$uuid" 2>/dev/null +} + +# guest_is_on URI UUID +# check if guest UUID on URI is running +# Result is returned by variable "guest_running" +guest_is_on() { + uri=$1 + uuid=$2 + + guest_running=false + id=$(run_virsh "$uri" domid "$uuid") + if [ $? -ne 0 ]; then + RETVAL=1 + return 1 + fi + + [ -n "$id" ] && [ "x$id" != x- ] && guest_running=true + return 0 +} + +# started +# Create the startup lock file +started() { + touch "$VAR_SUBSYS_LIBVIRT_GUESTS" +} + +# start +# Start or resume the guests +start() { + [ -f "$LISTFILE" ] || { started; return 0; } + + if [ "x$ON_BOOT" != xstart ]; then + gettext "libvirt-guests is configured not to start any guests on boot" + echo + rm -f "$LISTFILE" + started + return 0 + fi + + isfirst=true + bypass= + test "x$BYPASS_CACHE" = x0 || bypass=--bypass-cache + while read uri list; do + configured=false + set -f + for confuri in $URIS; do + set +f + if [ "x$confuri" = "x$uri" ]; then + configured=true + break + fi + done + set +f + if ! "$configured"; then + eval_gettext "Ignoring guests on \$uri URI"; echo + continue + fi + + test_connect "$uri" || continue + + eval_gettext "Resuming guests on \$uri URI..."; echo + for guest in $list; do + name=$(guest_name "$uri" "$guest") + eval_gettext "Resuming guest \$name: " + if guest_is_on "$uri" "$guest"; then + if "$guest_running"; then + gettext "already active"; echo + else + if "$isfirst"; then + isfirst=false + else + sleep $START_DELAY + fi + retval run_virsh "$uri" start $bypass "$name" \ + >/dev/null && \ + gettext "done"; echo + fi + fi + done + done <"$LISTFILE" + + rm -f "$LISTFILE" + started +} + +# suspend_guest URI GUEST +# Do a managed save on a GUEST on URI. This function returns after the guest +# was saved. +suspend_guest() +{ + uri=$1 + guest=$2 + + name=$(guest_name "$uri" "$guest") + label=$(eval_gettext "Suspending \$name: ") + bypass= + slept=0 + test "x$BYPASS_CACHE" = x0 || bypass=--bypass-cache + printf '%s...\n' "$label" + run_virsh "$uri" managedsave $bypass "$guest" >/dev/null & + virsh_pid=$! + while true; do + sleep 1 + kill -0 "$virsh_pid" >/dev/null 2>&1 || break + + slept=$(($slept + 1)) + if [ $(($slept % 5)) -eq 0 ]; then + progress=$(run_virsh_c "$uri" domjobinfo "$guest" 2>/dev/null | \ + awk '/^Data processed:/{print $3, $4}') + if [ -n "$progress" ]; then + printf '%s%s\n' "$label" "$progress" + else + printf '%s%s\n' "$label" "..." + fi + fi + done + retval wait "$virsh_pid" && printf '%s%s\n' "$label" "$(gettext "done")" +} + +# shutdown_guest URI GUEST +# Start a ACPI shutdown of GUEST on URI. This function return after the quest +# was successfully shutdown or the timeout defined by $SHUTDOWN_TIMEOUT expires. +shutdown_guest() +{ + uri=$1 + guest=$2 + + name=$(guest_name "$uri" "$guest") + eval_gettext "Starting shutdown on guest: \$name" + echo + retval run_virsh "$uri" shutdown "$guest" >/dev/null || return + timeout=$SHUTDOWN_TIMEOUT + check_timeout=false + if [ $timeout -gt 0 ]; then + check_timeout=true + format=$(eval_gettext "Waiting for guest %s to shut down, %d seconds left\n") + else + slept=0 + format=$(eval_gettext "Waiting for guest %s to shut down\n") + fi + while ! $check_timeout || [ "$timeout" -gt 0 ]; do + sleep 1 + guest_is_on "$uri" "$guest" || return + "$guest_running" || break + + if $check_timeout; then + if [ $(($timeout % 5)) -eq 0 ]; then + printf "$format" "$name" "$timeout" + fi + timeout=$(($timeout - 1)) + else + slept=$(($slept + 1)) + if [ $(($slept % 5)) -eq 0 ]; then + printf "$format" "$name" + fi + fi + done + + if guest_is_on "$uri" "$guest"; then + if "$guest_running"; then + eval_gettext "Shutdown of guest \$name failed to complete in time." + else + eval_gettext "Shutdown of guest \$name complete." + fi + fi +} + +# shutdown_guest_async URI GUEST +# Start a ACPI shutdown of GUEST on URI. This function returns after the command +# was issued to libvirt to allow parallel shutdown. +shutdown_guest_async() +{ + uri=$1 + guest=$2 + + name=$(guest_name "$uri" "$guest") + eval_gettext "Starting shutdown on guest: \$name" + echo + retval run_virsh "$uri" shutdown "$guest" > /dev/null +} + +# guest_count GUEST_LIST +# Returns number of guests in GUEST_LIST +guest_count() +{ + set -- $1 + echo $# +} + +# check_guests_shutdown URI GUESTS +# check if shutdown is complete on guests in "GUESTS" and returns only +# guests that are still shutting down +check_guests_shutdown() +{ + uri=$1 + guests=$2 + + guests_up= + for guest in $guests; do + if ! guest_is_on "$uri" "$guest" >/dev/null 2>&1; then + eval_gettext "Failed to determine state of guest: \$guest. Not tracking it anymore." + echo + continue + fi + if "$guest_running"; then + guests_up="$guests_up $guest" + fi + done + echo "$guests_up" +} + +# print_guests_shutdown URI BEFORE AFTER +# Checks for differences in the lists BEFORE and AFTER and prints +# a shutdown complete notice for guests that have finished +print_guests_shutdown() +{ + uri=$1 + before=$2 + after=$3 + + for guest in $before; do + case " $after " in + *" $guest "*) continue;; + esac + + name=$(guest_name "$uri" "$guest") + eval_gettext "Shutdown of guest \$name complete." + echo + done +} + +# shutdown_guests_parallel URI GUESTS +# Shutdown guests GUESTS on machine URI in parallel +shutdown_guests_parallel() +{ + uri=$1 + guests=$2 + + on_shutdown= + check_timeout=false + timeout=$SHUTDOWN_TIMEOUT + if [ $timeout -gt 0 ]; then + check_timeout=true + format=$(eval_gettext "Waiting for %d guests to shut down, %d seconds left\n") + else + slept=0 + format=$(eval_gettext "Waiting for %d guests to shut down\n") + fi + while [ -n "$on_shutdown" ] || [ -n "$guests" ]; do + while [ -n "$guests" ] && + [ $(guest_count "$on_shutdown") -lt "$PARALLEL_SHUTDOWN" ]; do + set -- $guests + guest=$1 + shift + guests=$* + shutdown_guest_async "$uri" "$guest" + on_shutdown="$on_shutdown $guest" + done + sleep 1 + + set -- $guests + guestcount=$# + set -- $on_shutdown + shutdowncount=$# + + if $check_timeout; then + if [ $(($timeout % 5)) -eq 0 ]; then + printf "$format" $(($guestcount + $shutdowncount)) "$timeout" + fi + timeout=$(($timeout - 1)) + if [ $timeout -le 0 ]; then + eval_gettext "Timeout expired while shutting down domains"; echo + RETVAL=1 + return + fi + else + slept=$(($slept + 1)) + if [ $(($slept % 5)) -eq 0 ]; then + printf "$format" $(($guestcount + $shutdowncount)) + fi + fi + + on_shutdown_prev=$on_shutdown + on_shutdown=$(check_guests_shutdown "$uri" "$on_shutdown") + print_guests_shutdown "$uri" "$on_shutdown_prev" "$on_shutdown" + done +} + +# stop +# Shutdown or save guests on the configured uris +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 -lt 0 ]; then + gettext "SHUTDOWN_TIMEOUT must be equal or greater than 0" + echo + RETVAL=6 + return + fi + fi + + : >"$LISTFILE" + set -f + for uri in $URIS; do + set +f + + test_connect "$uri" || continue + + eval_gettext "Running guests on \$uri URI: " + + list=$(list_guests "$uri") + if [ $? -eq 0 ]; then + empty=true + for uuid in $list; do + "$empty" || printf ", " + printf %s "$(guest_name "$uri" "$uuid")" + empty=false + done + + if "$empty"; then + gettext "no running guests." + fi + echo + fi + + if "$suspending"; then + transient=$(list_guests "$uri" "--transient") + if [ $? -eq 0 ]; then + empty=true + for uuid in $transient; do + if "$empty"; then + eval_gettext "Not suspending transient guests on URI: \$uri: " + empty=false + else + printf ", " + fi + printf %s "$(guest_name "$uri" "$uuid")" + done + echo + # reload domain list to contain only persistent guests + list=$(list_guests "$uri" "--persistent") + if [ $? -ne 0 ]; then + eval_gettext "Failed to list persistent guests on \$uri" + echo + RETVAL=1 + set +f + return + fi + else + gettext "Failed to list transient guests" + echo + RETVAL=1 + set +f + return + fi + fi + + if [ -n "$list" ]; then + echo "$uri" "$list" >>"$LISTFILE" + fi + done + set +f + + if [ -s "$LISTFILE" ]; then + while read uri list; do + if "$suspending"; then + eval_gettext "Suspending guests on \$uri URI..."; echo + else + eval_gettext "Shutting down guests on \$uri URI..."; echo + fi + + if [ "$PARALLEL_SHUTDOWN" -gt 1 ] && + ! "$suspending"; then + shutdown_guests_parallel "$uri" "$list" + else + for guest in $list; do + if "$suspending"; then + suspend_guest "$uri" "$guest" + else + shutdown_guest "$uri" "$guest" + fi + done + fi + done <"$LISTFILE" + else + rm -f "$LISTFILE" + fi + + rm -f "$VAR_SUBSYS_LIBVIRT_GUESTS" +} + +# gueststatus +# List status of guests +gueststatus() { + set -f + for uri in $URIS; do + set +f + echo "* $uri URI:" + retval run_virsh "$uri" list || echo + done + set +f +} + +# rh_status +# Display current status: whether saved state exists, and whether start +# has been executed. We cannot use status() from the functions library, +# since there is no external daemon process matching this init script. +rh_status() { + if [ -f "$LISTFILE" ]; then + gettext "stopped, with saved guests"; echo + RETVAL=3 + else + if [ -f "$VAR_SUBSYS_LIBVIRT_GUESTS" ]; then + gettext "started"; echo + else + gettext "stopped, with no saved guests"; echo + fi + RETVAL=0 + fi +} + +# usage [val] +# Display usage string, then exit with VAL (defaults to 2). +usage() { + program_name=$0 + eval_gettext "Usage: \$program_name {start|stop|status|restart|"\ +"condrestart|try-restart|reload|force-reload|gueststatus|shutdown}"; echo + exit ${1-2} +} + +# See how we were called. +if test $# != 1; then + usage +fi +case "$1" in + --help) + usage 0 + ;; + start|stop|gueststatus) + "$1" + ;; + restart) + stop && start + ;; + condrestart|try-restart) + [ -f "$VAR_SUBSYS_LIBVIRT_GUESTS" ] && stop && start + ;; + reload|force-reload) + # Nothing to do; we reread configuration on each invocation + ;; + status) + rh_status + ;; + shutdown) + ON_SHUTDOWN=shutdown + stop + ;; + *) + usage + ;; +esac +exit $RETVAL -- 1.8.0.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list