PageRenderTime 2569ms CodeModel.GetById 26ms RepoModel.GetById 8ms app.codeStats 0ms

/tools/libvirt-guests.sh.in

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