| #!/bin/bash | |
| # | |
| # Copyright (c) 2007 LINBIT Information Technologies GmbH | |
| # Based on the original "block" VBD script by XenSource Inc. | |
| # | |
| # This script implements the "drbd" VBD type. To use a DRBD resource | |
| # as a virtual block device, include a line similar to this in your | |
| # domain xl.cfg(5) configuration: | |
| # | |
| # disk = [ "vdev=vda,access=rw,script=block-drbd,target=myresource" ] | |
| # | |
| # The legacy xmdomain.cfg(5) configuration format is also supported | |
| # | |
| # disk = [ 'drbd:myresource,xvda,w' ] | |
| # | |
| # This will direct Xen to put the DRBD resource named 'myresource' | |
| # into the Primary role, and configure it as device xvda in your | |
| # domU. You may use as many DRBD resources as you like. If you are | |
| # using DRBD in dual-Primary mode (available in DRBD versions 8.0 and | |
| # up), your DRBD-backed domU will be live migration capable. | |
| # | |
| # IMPORTANT: If you run DRBD in dual-Primary mode with Xen, you MUST | |
| # ensure that the only time the resource is accessed by | |
| # both nodes is during domain migration. If you fire up a | |
| # DRBD-backed domU simultaneously on two nodes, you WILL | |
| # wreck your VBD data. DO NOT attempt to set up a live | |
| # migration capable, DRBD-backed domU unless you | |
| # understand these implications. | |
| # | |
| # This script will not load the DRBD kernel module for you, nor will | |
| # it attach, detach, connect, or disconnect your resource. The init | |
| # script distributed with DRBD will do that for you. Make sure it is | |
| # started before attempting to start a DRBD-backed domU. | |
| # | |
| # Known limitations: | |
| # | |
| # - With 'file' and 'phy' VBD's, Xen will allow one block device to be | |
| # made available read-only to multiple domU's, or be mounted | |
| # read-only in the dom0 and be made available read-only to | |
| # domU's. This is not supported by the 'drbd' VBD type. | |
| # | |
| # For more information about DRBD, visit http://www.drbd.org/. | |
| # | |
| # | |
| # This library is free software; you can redistribute it and/or | |
| # modify it under the terms of version 2.1 of the GNU Lesser General Public | |
| # License as published by the Free Software Foundation. | |
| # | |
| # This library is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| # Lesser General Public License for more details. | |
| # | |
| # You should have received a copy of the GNU Lesser General Public | |
| # License along with this library; if not, write to the Free Software | |
| # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| # | |
| dir=$(dirname "$0") | |
| . "$dir/block-common.sh" | |
| PATH=/usr/sbin:/sbin:$PATH | |
| ## | |
| # canonicalise_mode mode | |
| # | |
| # Takes the given mode, which may be r, w, ro, rw, w!, or rw!, or variations | |
| # thereof, and canonicalises them to one of | |
| # | |
| # 'r': perform checks for a new read-only mount; | |
| # 'w': perform checks for a read-write mount; or | |
| # '!': perform no checks at all. | |
| # | |
| canonicalise_mode() | |
| { | |
| local mode="$1" | |
| if ! expr index "$mode" 'w' >/dev/null | |
| then | |
| echo 'r' | |
| elif ! expr index "$mode" '!' >/dev/null | |
| then | |
| echo 'w' | |
| else | |
| echo '!' | |
| fi | |
| } | |
| ## | |
| # check_sharing device mode | |
| # | |
| # Check whether the device requested is already in use. To use the device in | |
| # read-only mode, it may be in use in read-only mode, but may not be in use in | |
| # read-write anywhere at all. To use the device in read-write mode, it must | |
| # not be in use anywhere at all. | |
| # | |
| # Prints one of | |
| # | |
| # 'local': the device may not be used because it is mounted in the current | |
| # (i.e. the privileged domain) in a way incompatible with the | |
| # requested mode; | |
| # 'guest': the device may not be used because it already mounted by a guest | |
| # in a way incompatible with the requested mode; or | |
| # 'ok': the device may be used. | |
| # | |
| check_sharing() | |
| { | |
| local dev="$1" | |
| local mode="$2" | |
| local devmm=$(device_major_minor "$dev") | |
| local file | |
| # Here, different from the original 'block' script, we don't check | |
| # explicitly for read/write mounts. See "known limitations" above. | |
| toskip="^$" | |
| for file in $(cat /proc/mounts | grep -v "$toskip" | cut -f 1 -d ' ') | |
| do | |
| if [ -e "$file" ] | |
| then | |
| local d=$(device_major_minor "$file") | |
| if [ "$d" = "$devmm" ] | |
| then | |
| echo 'local' | |
| return | |
| fi | |
| fi | |
| done | |
| local base_path="$XENBUS_BASE_PATH/$XENBUS_TYPE" | |
| for dom in $(xenstore-list "$base_path") | |
| do | |
| for dev in $(xenstore-list "$base_path/$dom") | |
| do | |
| d=$(xenstore_read_default "$base_path/$dom/$dev/physical-device" "") | |
| if [ "$d" = "$devmm" ] | |
| then | |
| # Here, different from the original 'block' script, we don't | |
| # check explicitly for read/write mounts. See "known | |
| # limitations" above. | |
| if ! same_vm $dom | |
| then | |
| echo 'guest' | |
| return | |
| fi | |
| fi | |
| done | |
| done | |
| echo 'ok' | |
| } | |
| same_vm() | |
| { | |
| local otherdom="$1" | |
| # Note that othervm can be MISSING here, because Xend will be racing with | |
| # the hotplug scripts -- the entries in /local/domain can be removed by | |
| # Xend before the hotplug scripts have removed the entry in | |
| # /local/domain/0/backend/. In this case, we want to pretend that the | |
| # VM is the same as FRONTEND_UUID, because that way the 'sharing' will be | |
| # allowed. | |
| local othervm=$(xenstore_read_default "/local/domain/$otherdom/vm" \ | |
| "$FRONTEND_UUID") | |
| [ "$FRONTEND_UUID" = "$othervm" ] | |
| } | |
| ## | |
| # check_device_sharing dev mode | |
| # | |
| # Perform the sharing check for the given physical device and mode. | |
| # | |
| check_device_sharing() | |
| { | |
| local dev="$1" | |
| local mode=$(canonicalise_mode "$2") | |
| local result | |
| if [ "x$mode" = 'x!' ] | |
| then | |
| return 0 | |
| fi | |
| result=$(check_sharing "$dev" "$mode") | |
| if [ "$result" != 'ok' ] | |
| then | |
| do_ebusy "Device $dev is mounted " "$mode" "$result" | |
| fi | |
| } | |
| ## | |
| # do_ebusy prefix mode result | |
| # | |
| # Helper function for check_device_sharing check_file_sharing, calling ebusy | |
| # with an error message constructed from the given prefix, mode, and result | |
| # from a call to check_sharing. | |
| # | |
| do_ebusy() | |
| { | |
| local prefix="$1" | |
| local mode="$2" | |
| local result="$3" | |
| if [ "$result" = 'guest' ] | |
| then | |
| dom='a guest ' | |
| when='now' | |
| else | |
| dom='the privileged ' | |
| when='by a guest' | |
| fi | |
| if [ "$mode" = 'w' ] | |
| then | |
| m1='' | |
| m2='' | |
| else | |
| m1='read-write ' | |
| m2='read-only ' | |
| fi | |
| release_lock "block" | |
| ebusy \ | |
| "${prefix}${m1}in ${dom}domain, | |
| and so cannot be mounted ${m2}${when}." | |
| } | |
| t=$(xenstore_read_default "$XENBUS_PATH/type" 'MISSING') | |
| case "$command" in | |
| add) | |
| phys=$(xenstore_read_default "$XENBUS_PATH/physical-device" 'MISSING') | |
| if [ "$phys" != 'MISSING' ] | |
| then | |
| # Depending upon the hotplug configuration, it is possible for this | |
| # script to be called twice, so just bail. | |
| exit 0 | |
| fi | |
| if [ -n "$t" ] | |
| then | |
| p=$(xenstore_read "$XENBUS_PATH/params") | |
| p=${p#drbd:} | |
| mode=$(xenstore_read "$XENBUS_PATH/mode") | |
| fi | |
| case $t in | |
| drbd|phy) | |
| drbd_resource=$p | |
| drbd_role="$(drbdadm role $drbd_resource)" | |
| drbd_lrole="${drbd_role%%/*}" | |
| drbd_dev="$(drbdadm sh-dev $drbd_resource)" | |
| if [ "$drbd_lrole" != 'Primary' ]; then | |
| drbdadm primary $drbd_resource | |
| fi | |
| dev=$drbd_dev | |
| FRONTEND_ID=$(xenstore_read "$XENBUS_PATH/frontend-id") | |
| FRONTEND_UUID=$(xenstore_read_default \ | |
| "/local/domain/$FRONTEND_ID/vm" 'unknown') | |
| claim_lock "block" | |
| check_device_sharing "$dev" "$mode" | |
| write_dev "$dev" | |
| release_lock "block" | |
| exit 0 | |
| ;; | |
| "") | |
| claim_lock "block" | |
| success | |
| release_lock "block" | |
| ;; | |
| esac | |
| ;; | |
| remove) | |
| case $t in | |
| drbd|phy) | |
| p=$(xenstore_read "$XENBUS_PATH/params") | |
| drbd_resource=${p#drbd:} | |
| drbd_role="$(drbdadm role $drbd_resource)" | |
| drbd_lrole="${drbd_role%%/*}" | |
| drbd_dev="$(drbdadm sh-dev $drbd_resource)" | |
| if [ "$drbd_lrole" != 'Secondary' ]; then | |
| drbdadm secondary $drbd_resource | |
| fi | |
| exit 0 | |
| ;; | |
| "") | |
| exit 0 | |
| ;; | |
| esac | |
| ;; | |
| esac |