You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

623 lines
16 KiB

  1. #!/bin/sh
  2. # Copyright (C) 2011-2014 Red Hat, Inc.
  3. #
  4. # This library is free software; you can redistribute it and/or
  5. # modify it under the terms of the GNU Lesser General Public
  6. # License as published by the Free Software Foundation; either
  7. # version 2.1 of the License, or (at your option) any later version.
  8. #
  9. # This library is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. # Lesser General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU Lesser General Public
  15. # License along with this library. If not, see
  16. # <http://www.gnu.org/licenses/>.
  17. sysconfdir="@sysconfdir@"
  18. localstatedir="@localstatedir@"
  19. libvirtd="@sbindir@"/libvirtd
  20. # Source function library.
  21. test ! -r "$sysconfdir"/rc.d/init.d/functions ||
  22. . "$sysconfdir"/rc.d/init.d/functions
  23. # Source gettext library.
  24. # Make sure this file is recognized as having translations: _("dummy")
  25. . "@bindir@"/gettext.sh
  26. export TEXTDOMAIN="@PACKAGE@" TEXTDOMAINDIR="@localedir@"
  27. URIS=default
  28. ON_BOOT=start
  29. ON_SHUTDOWN=suspend
  30. SHUTDOWN_TIMEOUT=300
  31. PARALLEL_SHUTDOWN=0
  32. START_DELAY=0
  33. BYPASS_CACHE=0
  34. SYNC_TIME=0
  35. test -f "$sysconfdir"/sysconfig/libvirt-guests &&
  36. . "$sysconfdir"/sysconfig/libvirt-guests
  37. LISTFILE="$localstatedir"/lib/libvirt/libvirt-guests
  38. VAR_SUBSYS_LIBVIRT_GUESTS="$localstatedir"/lock/subsys/libvirt-guests
  39. RETVAL=0
  40. # retval COMMAND ARGUMENTS...
  41. # run command with arguments and convert non-zero return value to 1 and set
  42. # the global return variable
  43. retval() {
  44. "$@"
  45. if [ $? -ne 0 ]; then
  46. RETVAL=1
  47. return 1
  48. else
  49. return 0
  50. fi
  51. }
  52. # run_virsh URI ARGUMENTS...
  53. # start virsh and let it execute ARGUMENTS on URI
  54. # If URI is "default" virsh is called without the "-c" argument
  55. # (using libvirt's default connection)
  56. run_virsh() {
  57. local uri=$1
  58. shift
  59. if [ "x$uri" = xdefault ]; then
  60. virsh "$@" </dev/null
  61. else
  62. virsh -c "$uri" "$@" </dev/null
  63. fi
  64. }
  65. # run_virsh_c URI ARGUMENTS
  66. # Same as "run_virsh" but the "C" locale is used instead of
  67. # the system's locale.
  68. run_virsh_c() {
  69. ( export LC_ALL=C; run_virsh "$@" )
  70. }
  71. # test_connect URI
  72. # check if URI is reachable
  73. test_connect()
  74. {
  75. local uri=$1
  76. if run_virsh "$uri" connect 2>/dev/null; then
  77. return 0;
  78. else
  79. eval_gettext "Can't connect to \$uri. Skipping."
  80. return 1
  81. fi
  82. }
  83. # list_guests URI PERSISTENT
  84. # List running guests on URI.
  85. # PERSISTENT argument options:
  86. # --persistent: list only persistent guests
  87. # --transient: list only transient guests
  88. # [none]: list both persistent and transient guests
  89. list_guests() {
  90. local uri=$1
  91. local persistent=$2
  92. local list=$(run_virsh_c "$uri" list --uuid $persistent)
  93. if [ $? -ne 0 ]; then
  94. RETVAL=1
  95. return 1
  96. fi
  97. echo "$list" | sed "/00000000-0000-0000-0000-000000000000/d"
  98. }
  99. # guest_name URI UUID
  100. # return name of guest UUID on URI
  101. guest_name() {
  102. local uri=$1
  103. local uuid=$2
  104. run_virsh "$uri" domname "$uuid" 2>/dev/null
  105. }
  106. # guest_is_on URI UUID
  107. # check if guest UUID on URI is running
  108. # Result is returned by variable "guest_running"
  109. guest_is_on() {
  110. local uri=$1
  111. local uuid=$2
  112. local id=$(run_virsh "$uri" domid "$uuid")
  113. guest_running=false
  114. if [ $? -ne 0 ]; then
  115. RETVAL=1
  116. return 1
  117. fi
  118. [ -n "$id" ] && [ "x$id" != x- ] && guest_running=true
  119. return 0
  120. }
  121. # started
  122. # Create the startup lock file
  123. started() {
  124. touch "$VAR_SUBSYS_LIBVIRT_GUESTS"
  125. }
  126. # start
  127. # Start or resume the guests
  128. start() {
  129. local isfirst=true
  130. local bypass=
  131. local sync_time=false
  132. local uri=
  133. local list=
  134. [ -f "$LISTFILE" ] || { started; return 0; }
  135. if [ "x$ON_BOOT" != xstart ]; then
  136. gettext "libvirt-guests is configured not to start any guests on boot"
  137. echo
  138. rm -f "$LISTFILE"
  139. started
  140. return 0
  141. fi
  142. test "x$BYPASS_CACHE" = x0 || bypass=--bypass-cache
  143. test "x$SYNC_TIME" = x0 || sync_time=true
  144. while read uri list; do
  145. local configured=false
  146. local confuri=
  147. local guest=
  148. set -f
  149. for confuri in $URIS; do
  150. set +f
  151. if [ "x$confuri" = "x$uri" ]; then
  152. configured=true
  153. break
  154. fi
  155. done
  156. set +f
  157. if ! "$configured"; then
  158. eval_gettext "Ignoring guests on \$uri URI"; echo
  159. continue
  160. fi
  161. test_connect "$uri" || continue
  162. eval_gettext "Resuming guests on \$uri URI..."; echo
  163. for guest in $list; do
  164. local name=$(guest_name "$uri" "$guest")
  165. eval_gettext "Resuming guest \$name: "
  166. if guest_is_on "$uri" "$guest"; then
  167. if "$guest_running"; then
  168. gettext "already active"; echo
  169. else
  170. if "$isfirst"; then
  171. isfirst=false
  172. else
  173. sleep $START_DELAY
  174. fi
  175. retval run_virsh "$uri" start $bypass "$name" \
  176. >/dev/null && \
  177. gettext "done"; echo
  178. if "$sync_time"; then
  179. run_virsh "$uri" domtime --sync "$name" >/dev/null
  180. fi
  181. fi
  182. fi
  183. done
  184. done <"$LISTFILE"
  185. rm -f "$LISTFILE"
  186. started
  187. }
  188. # suspend_guest URI GUEST
  189. # Do a managed save on a GUEST on URI. This function returns after the guest
  190. # was saved.
  191. suspend_guest()
  192. {
  193. local uri=$1
  194. local guest=$2
  195. local name=$(guest_name "$uri" "$guest")
  196. local label=$(eval_gettext "Suspending \$name: ")
  197. local bypass=
  198. local slept=0
  199. test "x$BYPASS_CACHE" = x0 || bypass=--bypass-cache
  200. printf '%s...\n' "$label"
  201. run_virsh "$uri" managedsave $bypass "$guest" >/dev/null &
  202. local virsh_pid=$!
  203. while true; do
  204. sleep 1
  205. kill -0 "$virsh_pid" >/dev/null 2>&1 || break
  206. slept=$(($slept + 1))
  207. if [ $(($slept % 5)) -eq 0 ]; then
  208. local progress=$(run_virsh_c "$uri" domjobinfo "$guest" 2>/dev/null | \
  209. awk '/^Data processed:/{print $3, $4}')
  210. if [ -n "$progress" ]; then
  211. printf '%s%s\n' "$label" "$progress"
  212. else
  213. printf '%s%s\n' "$label" "..."
  214. fi
  215. fi
  216. done
  217. retval wait "$virsh_pid" && printf '%s%s\n' "$label" "$(gettext "done")"
  218. }
  219. # shutdown_guest URI GUEST
  220. # Start an ACPI shutdown of GUEST on URI. This function returns after the guest
  221. # was successfully shutdown or the timeout defined by $SHUTDOWN_TIMEOUT expired.
  222. shutdown_guest()
  223. {
  224. local uri=$1
  225. local guest=$2
  226. local name=$(guest_name "$uri" "$guest")
  227. local timeout=$SHUTDOWN_TIMEOUT
  228. local check_timeout=false
  229. local format=
  230. local slept=
  231. eval_gettext "Starting shutdown on guest: \$name"
  232. echo
  233. retval run_virsh "$uri" shutdown "$guest" >/dev/null || return
  234. if [ $timeout -gt 0 ]; then
  235. check_timeout=true
  236. format=$(eval_gettext "Waiting for guest %s to shut down, %d seconds left\n")
  237. else
  238. slept=0
  239. format=$(eval_gettext "Waiting for guest %s to shut down\n")
  240. fi
  241. while ! $check_timeout || [ "$timeout" -gt 0 ]; do
  242. sleep 1
  243. guest_is_on "$uri" "$guest" || return
  244. "$guest_running" || break
  245. if $check_timeout; then
  246. if [ $(($timeout % 5)) -eq 0 ]; then
  247. printf "$format" "$name" "$timeout"
  248. fi
  249. timeout=$(($timeout - 1))
  250. else
  251. slept=$(($slept + 1))
  252. if [ $(($slept % 5)) -eq 0 ]; then
  253. printf "$format" "$name"
  254. fi
  255. fi
  256. done
  257. if guest_is_on "$uri" "$guest"; then
  258. if "$guest_running"; then
  259. eval_gettext "Shutdown of guest \$name failed to complete in time."
  260. else
  261. eval_gettext "Shutdown of guest \$name complete."
  262. fi
  263. echo
  264. fi
  265. }
  266. # shutdown_guest_async URI GUEST
  267. # Start a ACPI shutdown of GUEST on URI. This function returns after the command
  268. # was issued to libvirt to allow parallel shutdown.
  269. shutdown_guest_async()
  270. {
  271. local uri=$1
  272. local guest=$2
  273. local name=$(guest_name "$uri" "$guest")
  274. eval_gettext "Starting shutdown on guest: \$name"
  275. echo
  276. retval run_virsh "$uri" shutdown "$guest" > /dev/null
  277. }
  278. # guest_count GUEST_LIST
  279. # Returns number of guests in GUEST_LIST
  280. guest_count()
  281. {
  282. set -- $1
  283. echo $#
  284. }
  285. # check_guests_shutdown URI GUESTS
  286. # check if shutdown is complete on guests in "GUESTS" and returns only
  287. # guests that are still shutting down
  288. # Result is returned in "guests_shutting_down"
  289. check_guests_shutdown()
  290. {
  291. local uri=$1
  292. local guests_to_check=$2
  293. local guest=
  294. guests_shutting_down=
  295. for guest in $guests_to_check; do
  296. if ! guest_is_on "$uri" "$guest" >/dev/null 2>&1; then
  297. eval_gettext "Failed to determine state of guest: \$guest. Not tracking it anymore."
  298. echo
  299. continue
  300. fi
  301. if "$guest_running"; then
  302. guests_shutting_down="$guests_shutting_down $guest"
  303. fi
  304. done
  305. }
  306. # print_guests_shutdown URI BEFORE AFTER
  307. # Checks for differences in the lists BEFORE and AFTER and prints
  308. # a shutdown complete notice for guests that have finished
  309. print_guests_shutdown()
  310. {
  311. local uri=$1
  312. local before=$2
  313. local after=$3
  314. local guest=
  315. for guest in $before; do
  316. case " $after " in
  317. *" $guest "*) continue;;
  318. esac
  319. local name=$(guest_name "$uri" "$guest")
  320. if [ -n "$name" ]; then
  321. eval_gettext "Shutdown of guest \$name complete."
  322. echo
  323. fi
  324. done
  325. }
  326. # shutdown_guests_parallel URI GUESTS
  327. # Shutdown guests GUESTS on machine URI in parallel
  328. shutdown_guests_parallel()
  329. {
  330. local uri=$1
  331. local guests=$2
  332. local on_shutdown=
  333. local check_timeout=false
  334. local timeout=$SHUTDOWN_TIMEOUT
  335. local slept=
  336. local format=
  337. if [ $timeout -gt 0 ]; then
  338. check_timeout=true
  339. format=$(eval_gettext "Waiting for %d guests to shut down, %d seconds left\n")
  340. else
  341. slept=0
  342. format=$(eval_gettext "Waiting for %d guests to shut down\n")
  343. fi
  344. while [ -n "$on_shutdown" ] || [ -n "$guests" ]; do
  345. while [ -n "$guests" ] &&
  346. [ $(guest_count "$on_shutdown") -lt "$PARALLEL_SHUTDOWN" ]; do
  347. set -- $guests
  348. local guest=$1
  349. shift
  350. guests=$*
  351. if [ -z "$(echo $on_shutdown | grep $guest)" ] &&
  352. [ -n "$(guest_name "$uri" "$guest")" ]; then
  353. shutdown_guest_async "$uri" "$guest"
  354. on_shutdown="$on_shutdown $guest"
  355. fi
  356. done
  357. sleep 1
  358. set -- $guests
  359. local guestcount=$#
  360. set -- $on_shutdown
  361. local shutdowncount=$#
  362. if $check_timeout; then
  363. if [ $(($timeout % 5)) -eq 0 ]; then
  364. printf "$format" $(($guestcount + $shutdowncount)) "$timeout"
  365. fi
  366. timeout=$(($timeout - 1))
  367. if [ $timeout -le 0 ]; then
  368. eval_gettext "Timeout expired while shutting down domains"; echo
  369. RETVAL=1
  370. return
  371. fi
  372. else
  373. slept=$(($slept + 1))
  374. if [ $(($slept % 5)) -eq 0 ]; then
  375. printf "$format" $(($guestcount + $shutdowncount))
  376. fi
  377. fi
  378. local on_shutdown_prev=$on_shutdown
  379. check_guests_shutdown "$uri" "$on_shutdown"
  380. on_shutdown="$guests_shutting_down"
  381. print_guests_shutdown "$uri" "$on_shutdown_prev" "$on_shutdown"
  382. done
  383. }
  384. # stop
  385. # Shutdown or save guests on the configured uris
  386. stop() {
  387. local suspending=true
  388. local uri=
  389. # last stop was not followed by start
  390. [ -f "$LISTFILE" ] && return 0
  391. if [ "x$ON_SHUTDOWN" = xshutdown ]; then
  392. suspending=false
  393. if [ $SHUTDOWN_TIMEOUT -lt 0 ]; then
  394. gettext "SHUTDOWN_TIMEOUT must be equal or greater than 0"
  395. echo
  396. RETVAL=6
  397. return
  398. fi
  399. fi
  400. : >"$LISTFILE"
  401. set -f
  402. for uri in $URIS; do
  403. set +f
  404. test_connect "$uri" || continue
  405. eval_gettext "Running guests on \$uri URI: "
  406. local list=$(list_guests "$uri")
  407. if [ $? -eq 0 ]; then
  408. local empty=true
  409. for uuid in $list; do
  410. "$empty" || printf ", "
  411. printf %s "$(guest_name "$uri" "$uuid")"
  412. empty=false
  413. done
  414. if "$empty"; then
  415. gettext "no running guests."
  416. fi
  417. echo
  418. fi
  419. if "$suspending"; then
  420. local transient=$(list_guests "$uri" "--transient")
  421. if [ $? -eq 0 ]; then
  422. local empty=true
  423. local uuid=
  424. for uuid in $transient; do
  425. if "$empty"; then
  426. eval_gettext "Not suspending transient guests on URI: \$uri: "
  427. empty=false
  428. else
  429. printf ", "
  430. fi
  431. printf %s "$(guest_name "$uri" "$uuid")"
  432. done
  433. echo
  434. # reload domain list to contain only persistent guests
  435. list=$(list_guests "$uri" "--persistent")
  436. if [ $? -ne 0 ]; then
  437. eval_gettext "Failed to list persistent guests on \$uri"
  438. echo
  439. RETVAL=1
  440. set +f
  441. return
  442. fi
  443. else
  444. gettext "Failed to list transient guests"
  445. echo
  446. RETVAL=1
  447. set +f
  448. return
  449. fi
  450. fi
  451. if [ -n "$list" ]; then
  452. echo "$uri" $list >>"$LISTFILE"
  453. fi
  454. done
  455. set +f
  456. if [ -s "$LISTFILE" ]; then
  457. while read uri list; do
  458. if "$suspending"; then
  459. eval_gettext "Suspending guests on \$uri URI..."; echo
  460. else
  461. eval_gettext "Shutting down guests on \$uri URI..."; echo
  462. fi
  463. if [ "$PARALLEL_SHUTDOWN" -gt 1 ] &&
  464. ! "$suspending"; then
  465. shutdown_guests_parallel "$uri" "$list"
  466. else
  467. local guest=
  468. for guest in $list; do
  469. if "$suspending"; then
  470. suspend_guest "$uri" "$guest"
  471. else
  472. shutdown_guest "$uri" "$guest"
  473. fi
  474. done
  475. fi
  476. done <"$LISTFILE"
  477. else
  478. rm -f "$LISTFILE"
  479. fi
  480. rm -f "$VAR_SUBSYS_LIBVIRT_GUESTS"
  481. }
  482. # gueststatus
  483. # List status of guests
  484. gueststatus() {
  485. local uri=
  486. set -f
  487. for uri in $URIS; do
  488. set +f
  489. echo "* $uri URI:"
  490. retval run_virsh "$uri" list | grep -v "Domain-0" || echo
  491. done
  492. set +f
  493. }
  494. # rh_status
  495. # Display current status: whether saved state exists, and whether start
  496. # has been executed. We cannot use status() from the functions library,
  497. # since there is no external daemon process matching this init script.
  498. rh_status() {
  499. if [ -f "$LISTFILE" ]; then
  500. gettext "stopped, with saved guests"; echo
  501. RETVAL=3
  502. else
  503. if [ -f "$VAR_SUBSYS_LIBVIRT_GUESTS" ]; then
  504. gettext "started"; echo
  505. RETVAL=0
  506. else
  507. gettext "stopped, with no saved guests"; echo
  508. RETVAL=3
  509. fi
  510. fi
  511. }
  512. # usage [val]
  513. # Display usage string, then exit with VAL (defaults to 2).
  514. usage() {
  515. local program_name=$0
  516. eval_gettext "Usage: \$program_name {start|stop|status|restart|"\
  517. "condrestart|try-restart|reload|force-reload|gueststatus|shutdown}"; echo
  518. exit ${1-2}
  519. }
  520. # See how we were called.
  521. if test $# != 1; then
  522. usage
  523. fi
  524. case "$1" in
  525. --help)
  526. usage 0
  527. ;;
  528. start|stop|gueststatus)
  529. "$1"
  530. ;;
  531. restart)
  532. stop && start
  533. ;;
  534. condrestart|try-restart)
  535. [ -f "$VAR_SUBSYS_LIBVIRT_GUESTS" ] && stop && start
  536. ;;
  537. reload|force-reload)
  538. # Nothing to do; we reread configuration on each invocation
  539. ;;
  540. status)
  541. rh_status
  542. ;;
  543. shutdown)
  544. ON_SHUTDOWN=shutdown
  545. stop
  546. ;;
  547. *)
  548. usage
  549. ;;
  550. esac
  551. exit $RETVAL