#!/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 # domU configuration: # # disk = [ 'drbd:myresource,xvda1,w' ] # # This will direct Xen to put the DRBD resource named 'myresource' # into the Primary role, and configure it as device xvda1 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. # - Tested, thus far, only on Debian etch with Xen 3.0.3. # # 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" ## # 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") mode=$(xenstore_read "$XENBUS_PATH/mode") fi case $t in drbd) drbd_resource=$p drbd_state="$(/sbin/drbdadm state $drbd_resource)" drbd_lstate="${drbd_state%%/*}" drbd_dev="$(/sbin/drbdadm sh-dev $drbd_resource)" if [ "$drbd_lstate" != 'Primary' ]; then /sbin/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) p=$(xenstore_read "$XENBUS_PATH/params") drbd_resource=$p drbd_state="$(/sbin/drbdadm state $drbd_resource)" drbd_lstate="${drbd_state%%/*}" drbd_dev="$(/sbin/drbdadm sh-dev $drbd_resource)" if [ "$drbd_lstate" != 'Secondary' ]; then /sbin/drbdadm secondary $drbd_resource fi exit 0 ;; "") exit 0 ;; esac ;; esac