| #!/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 |