diff options
Diffstat (limited to 'qa/cephfs/unshare_ns_mount.sh')
-rwxr-xr-x | qa/cephfs/unshare_ns_mount.sh | 594 |
1 files changed, 594 insertions, 0 deletions
diff --git a/qa/cephfs/unshare_ns_mount.sh b/qa/cephfs/unshare_ns_mount.sh new file mode 100755 index 000000000..88ac3e933 --- /dev/null +++ b/qa/cephfs/unshare_ns_mount.sh @@ -0,0 +1,594 @@ +#!/usr/bin/env bash + +# This is one helper for mounting the ceph-fuse/kernel clients by +# unsharing the network namespace, let's call it netns container. +# With the netns container, you can easily suspend or resume the +# virtual network interface to simulate the client node hard +# shutdown for some test cases. +# +# netnsX netnsY netnsZ +# -------------- -------------- -------------- +# | mount client | | mount client | | mount client | +# | default | ... | default | ... | default | +# |192.168.0.1/16| |192.168.0.2/16| |192.168.0.3/16| +# | veth0 | | veth0 | | veth0 | +# -------------- -------------- ------------- +# | | | +# \ | brx.Y / +# \ ---------------------- / +# \ brx.X | ceph-brx | brx.Z / +# \------>| default |<------/ +# | | 192.168.255.254/16 | | +# | ---------------------- | +# (suspend/resume) | (suspend/resume) +# ----------- +# | Physical | +# | A.B.C.D/M | +# ----------- +# +# Defaultly it will use the 192.168.X.Y/16 private network IPs for +# the ceph-brx and netnses as above. And you can also specify your +# own new ip/mask for the ceph-brx, like: +# +# $ unshare_ns_mount.sh --fuse /mnt/cephfs --brxip 172.19.100.100/12 +# +# Then the each netns will get a new ip from the ranges: +# [172.16.0.1 ~ 172.19.100.99]/12 and [172.19.100.101 ~ 172.31.255.254]/12 + +usage() { + echo "" + echo "This will help to isolate the network namespace from OS for the mount client!" + echo "" + echo "usage: unshare_ns_mount.sh [OPTIONS [paramters]] [--brxip <ip_address/mask>]" + echo "OPTIONS:" + echo -e " --fuse <ceph-fuse options>" + echo -e "\tThe ceph-fuse command options" + echo -e "\t $ unshare_ns_mount.sh --fuse -m 192.168.0.1:6789 /mnt/cephfs -o nonempty" + echo "" + echo -e " --kernel <mount options>" + echo -e "\tThe mount command options" + echo -e "\t $ unshare_ns_mount.sh --kernel -t ceph 192.168.0.1:6789:/ /mnt/cephfs -o fs=a" + echo "" + echo -e " --suspend <mountpoint>" + echo -e "\tDown the veth interface in the network namespace" + echo -e "\t $ unshare_ns_mount.sh --suspend /mnt/cephfs" + echo "" + echo -e " --resume <mountpoint>" + echo -e "\tUp the veth interface in the network namespace" + echo -e "\t $ unshare_ns_mount.sh --resume /mnt/cephfs" + echo "" + echo -e " --umount <mountpoint>" + echo -e "\tUmount and delete the network namespace" + echo -e "\t $ unshare_ns_mount.sh --umount /mnt/cephfs" + echo "" + echo -e " --brxip <ip_address/mask>" + echo -e "\tSpecify ip/mask for ceph-brx and it only makes sense for --fuse/--kernel options" + echo -e "\t(default: 192.168.255.254/16, netns ip: 192.168.0.1/16 ~ 192.168.255.253/16)" + echo -e "\t $ unshare_ns_mount.sh --fuse -m 192.168.0.1:6789 /mnt/cephfs --brxip 172.19.255.254/12" + echo -e "\t $ unshare_ns_mount.sh --kernel 192.168.0.1:6789:/ /mnt/cephfs --brxip 172.19.255.254/12" + echo "" + echo -e " -h, --help" + echo -e "\tPrint help" + echo "" +} + +CEPH_BRX=ceph-brx +CEPH_BRX_IP_DEF=192.168.255.254 +NET_MASK_DEF=16 +BRD_DEF=192.168.255.255 + +CEPH_BRX_IP=$CEPH_BRX_IP_DEF +NET_MASK=$NET_MASK_DEF +BRD=$BRD_DEF + +mountpoint="" +new_netns="" +fuse_type=false + +function get_mountpoint() { + for param in $@ + do + if [ -d $param ]; then + # skipping "--client_mountpoint/-r root_directory" + # option for ceph-fuse command + if [ "$last" == "-r" -o "$last" == "--client_mountpoint" ]; then + last=$param + continue + fi + if [ "0$mountpoint" != "0" ]; then + echo "Oops: too many mountpiont options!" + exit 1 + fi + mountpoint=$param + fi + last=$param + done + + if [ "0$mountpoint" == "0" ]; then + echo "Oops: mountpoint path is not a directory or no mountpoint specified!" + exit 1 + fi +} + +function get_new_netns() { + # prune the repeating slashes: + # "/mnt///cephfs///" --> "/mnt/cephfs/" + __mountpoint=`echo "$mountpoint" | sed 's/\/\+/\//g'` + + # prune the leading slashes + while [ ${__mountpoint:0:1} == "/" ] + do + __mountpoint=${__mountpoint:1} + done + + # prune the last slashes + while [ ${__mountpoint: -1} == "/" ] + do + __mountpoint=${__mountpoint:0:-1} + done + + # replace '/' with '-' + __mountpoint=${__mountpoint//\//-} + + # "mnt/cephfs" --> "ceph-fuse-mnt-cephfs" + if [ "$1" == "--fuse" ]; then + new_netns=`echo ceph-fuse-$__mountpoint` + fuse_type=true + return + fi + + # "mnt/cephfs" --> "ceph-kernel-mnt-cephfs" + if [ "$1" == "--kernel" ]; then + new_netns=`echo ceph-kernel-$__mountpoint` + return + fi + + # we are in umount/suspend/resume routines + for ns in `ip netns list | awk '{print $1}'` + do + if [ "$ns" == "ceph-fuse-$__mountpoint" ]; then + new_netns=$ns + fuse_type=true + return + fi + if [ "$ns" == "ceph-kernel-$__mountpoint" ]; then + new_netns=$ns + return + fi + done + + if [ "0$new_netns" == "0" ]; then + echo "Oops, netns 'ceph-{fuse/kernel}-$__mountpoint' does not exists!" + exit 1 + fi +} + +# the peer veth name will be "brx.$nsid" on host node +function get_netns_brx() { + get_new_netns + + nsid=`ip netns list-id | grep "$new_netns" | awk '{print $2}'` + netns_veth=brx.$nsid + eval $1="$netns_veth" +} + +function suspend_netns_veth() { + get_mountpoint $@ + + get_netns_brx brx + ip link set $brx down + exit 0 +} + +function resume_netns_veth() { + get_mountpoint $@ + + get_netns_brx brx + ip link set $brx up + exit 0 +} + +# help and usage +if [ $# == 0 -o "$1" == "-h" -o "$1" == "--help" ]; then + usage + exit 0 +fi + +# suspend the veth from network namespace +if [ $1 == "--suspend" ]; then + suspend_netns_veth $@ + exit 0 +fi + +# resume the veth from network namespace +if [ $1 == "--resume" ]; then + resume_netns_veth $@ + exit 0 +fi + +function ceph_umount() { + get_mountpoint $@ + get_new_netns + + if [ $fuse_type == true ]; then + nsenter --net=/var/run/netns/$new_netns fusermount -u $mountpoint 2>/dev/null + else + nsenter --net=/var/run/netns/$new_netns umount $mountpoint 2>/dev/null + fi + + # let's wait for a while to let the umount operation + # to finish before deleting the netns + while [ 1 ] + do + for pid in `ip netns pids $new_netns 2>/dev/null` + do + name=`cat /proc/$pid/comm 2>/dev/null` + if [ "$name" == "ceph-fuse" ]; then + break + fi + done + + if [ "$name" == "ceph-fuse" ]; then + name="" + usleep 100000 + continue + fi + + break + done + + nsid=`ip netns list-id | grep "$new_netns" | awk '{print $2}'` + netns_brx=brx.$nsid + + # brctl delif $CEPH_BRX $netns_brx 2>/dev/null + nmcli connection down $netns_brx down 2>/dev/null + nmcli connection delete $netns_brx 2>/dev/null + + ip netns delete $new_netns 2>/dev/null + + # if this is the last netns_brx, will delete + # the $CEPH_BRX and restore the OS configure + # rc=`brctl show ceph-brx 2>/dev/null | grep 'brx\.'|wc -l` + rc=`nmcli connection show 2>/dev/null | grep 'brx\.' | wc -l` + if [ $rc == 0 ]; then + ip link set $CEPH_BRX down 2>/dev/null + # brctl delbr $CEPH_BRX 2>/dev/null + nmcli connection delete $CEPH_BRX 2>/dev/null + + # restore the ip forward + tmpfile=`ls /tmp/ | grep "$CEPH_BRX\."` + tmpfile=/tmp/$tmpfile + if [ ! -f $tmpfile ]; then + echo "Oops, the $CEPH_BRX.XXX temp file does not exist!" + else + save=`cat $tmpfile` + echo $save > /proc/sys/net/ipv4/ip_forward + rm -rf $tmpfile + fi + + # drop the iptables NAT rules + host_nic=`route | grep default | awk '{print $8}'` + iptables -D FORWARD -o $host_nic -i $CEPH_BRX -j ACCEPT + iptables -D FORWARD -i $host_nic -o $CEPH_BRX -j ACCEPT + iptables -t nat -D POSTROUTING -s $CEPH_BRX_IP/$NET_MASK -o $host_nic -j MASQUERADE + fi +} + +function get_brd_mask() { + first=`echo "$CEPH_BRX_IP" | awk -F. '{print $1}'` + second=`echo "$CEPH_BRX_IP" | awk -F. '{print $2}'` + third=`echo "$CEPH_BRX_IP" | awk -F. '{print $3}'` + fourth=`echo "$CEPH_BRX_IP" | awk -F. '{print $4}'` + + if [ "$first" == "172" ]; then + second_max=31 + else + second_max=255 + fi + third_max=255 + fourth_max=255 + + if [ $NET_MASK -lt 16 ]; then + let power=16-$NET_MASK + m=`awk 'BEGIN{printf 2^"'$power'"-1}'` + second=$((second&~m)) + let second_max=$second+$m + elif [ $NET_MASK -lt 24 ]; then + let power=24-$NET_MASK + m=`awk 'BEGIN{printf 2^"'$power'"-1}'` + third=$((third&~m)) + let third_max=$third+$m + second_max=$second + elif [ $NET_MASK -lt 32 ]; then + let power=32-$NET_MASK + m=`awk 'BEGIN{printf 2^"'$power'"-1}'` + fourth=$((fourth&~m)) + let fourth_max=$fourth+$m + second_max=$second + third_max=$third + fi + + BRD=$first.$second_max.$third_max.$fourth_max +} + +# As default: +# The netns IP will be 192.168.0.1 ~ 192.168.255.253, +# and 192.168.255.254 is saved for $CEPH_BRX +function get_new_ns_ip() { + first=`echo "$CEPH_BRX_IP" | awk -F. '{print $1}'` + second=`echo "$CEPH_BRX_IP" | awk -F. '{print $2}'` + third=`echo "$CEPH_BRX_IP" | awk -F. '{print $3}'` + fourth=`echo "$CEPH_BRX_IP" | awk -F. '{print $4}'` + + if [ "$first" == ""172 ]; then + second_max=31 + else + second_max=255 + fi + third_max=255 + fourth_max=254 + + if [ $NET_MASK -lt 16 ]; then + let power=16-$NET_MASK + m=`awk 'BEGIN{printf 2^"'$power'"-1}'` + second=$((second&~m)) + let second_max=$second+$m + third=0 + fourth=1 + elif [ $NET_MASK -lt 24 ]; then + let power=24-$NET_MASK + m=`awk 'BEGIN{printf 2^"'$power'"-1}'` + third=$((third&~m)) + let third_max=$third+$m + second_max=$second + fourth=1 + elif [ $NET_MASK -lt 32 ]; then + let power=32-$NET_MASK + m=`awk 'BEGIN{printf 2^"'$power'"-1}'` + fourth=$((fourth&~m)) + let fourth+=1 + let fourth_max=$fourth+$m-1 + second_max=$second + third_max=$third + fi + + while [ $second -le $second_max -a $third -le $third_max -a $fourth -le $fourth_max ] + do + conflict=false + + # check from the existing network namespaces + for netns in `ip netns list | awk '{print $1}'` + do + ip=`ip netns exec $netns ip addr | grep "inet " | grep "veth0"` + ip=`echo "$ip" | awk '{print $2}' | awk -F/ '{print $1}'` + if [ "0$ip" == "0" ]; then + continue + fi + if [ "$first.$second.$third.$fourth" == "$ip" ]; then + conflict=true + + let fourth+=1 + if [ $fourth -le $fourth_max ]; then + break + fi + + fourth=0 + let third+=1 + if [ $third -le $third_max ]; then + break + fi + + third=0 + let second+=1 + if [ $second -le $second_max ]; then + break + fi + + echo "Oops: we have ran out of the ip addresses!" + exit 1 + fi + done + + # have we found one ? + if [ $conflict == false ]; then + break + fi + done + + ip=$first.$second.$third.$fourth + max=$first.$second_max.$third_max.$fourth_max + if [ "$ip" == "$max" ]; then + echo "Oops: we have ran out of the ip addresses!" + exit 1 + fi + + eval $1="$ip" +} + +function check_valid_private_ip() { + first=`echo "$1" | awk -F. '{print $1}'` + second=`echo "$1" | awk -F. '{print $2}'` + + # private network class A 10.0.0.0 - 10.255.255.255 + if [ "$first" == "10" -a $NET_MASK -ge 8 ]; then + return + fi + + # private network class B 172.16.0.0 - 172.31.255.255 + if [ "$first" == "172" -a $second -ge 16 -a $second -le 31 -a $NET_MASK -ge 12 ]; then + return + fi + + # private network class C 192.168.0.0 - 192.168.255.255 + if [ "$first" == "192" -a "$second" == "168" -a $NET_MASK -ge 16 ]; then + return + fi + + echo "Oops: invalid private ip address '$CEPH_BRX_IP/$NET_MASK'!" + exit 1 +} + +function setup_bridge_and_nat() { + # check and parse the --brxip parameter + is_brxip=false + for ip in $@ + do + if [ "$ip" == "--brxip" ]; then + is_brxip=true + continue + fi + if [ $is_brxip == true ]; then + new_brxip=$ip + break + fi + done + + # if the $CEPH_BRX already exists, then check the new + # brxip, if not match fail it without doing anything. + rc=`ip addr | grep "inet " | grep " $CEPH_BRX"` + if [ "0$rc" != "0" ]; then + existing_brxip=`echo "$rc" | awk '{print $2}'` + if [ "0$new_brxip" != "0" -a "$existing_brxip" != "$new_brxip" ]; then + echo "Oops: conflict with the existing $CEPH_BRX ip '$existing_brxip', new '$new_brxip'!" + exit 1 + fi + + CEPH_BRX_IP=`echo "$existing_brxip" | awk -F/ '{print $1}'` + NET_MASK=`echo "$existing_brxip" | awk -F/ '{print $2}'` + get_brd_mask + return + fi + + # if it is the first time to run the the script or there + # is no any network namespace exists, we need to setup + # the $CEPH_BRX, if no --brxip is specified will use the + # default $CEPH_BRX_IP/$NET_MASK + if [ "0$new_brxip" != "0" ]; then + CEPH_BRX_IP=`echo "$new_brxip" | awk -F/ '{print $1}'` + NET_MASK=`echo "$new_brxip" | awk -F/ '{print $2}'` + get_brd_mask + check_valid_private_ip $CEPH_BRX_IP + fi + + # brctl addbr $CEPH_BRX + nmcli connection add type bridge con-name $CEPH_BRX ifname $CEPH_BRX stp no + # ip link set $CEPH_BRX up + # ip addr add $CEPH_BRX_IP/$NET_MASK brd $BRD dev $CEPH_BRX + nmcli connection modify $CEPH_BRX ipv4.addresses $CEPH_BRX_IP/$NET_MASK ipv4.method manual + nmcli connection up $CEPH_BRX + + # setup the NAT + rm -rf /tmp/ceph-brx.* + tmpfile=$(mktemp /tmp/ceph-brx.XXXXXXXX) + save=`cat /proc/sys/net/ipv4/ip_forward` + echo $save > $tmpfile + echo 1 > /proc/sys/net/ipv4/ip_forward + + host_nic=`route | grep default | awk '{print $8}'` + iptables -A FORWARD -o $host_nic -i $CEPH_BRX -j ACCEPT + iptables -A FORWARD -i $host_nic -o $CEPH_BRX -j ACCEPT + iptables -t nat -A POSTROUTING -s $CEPH_BRX_IP/$NET_MASK -o $host_nic -j MASQUERADE +} + +function __ceph_mount() { + # for some options like the '-t' in mount command + # the nsenter command will take over it, so it is + # hard to pass it direct to the netns. + # here we will create one temp file with x mode + tmpfile=$(mktemp /tmp/ceph-nsenter.XXXXXXXX) + chmod +x $tmpfile + if [ "$1" == "--kernel" ]; then + cmd=`echo "$@" | sed 's/--kernel/mount/'` + else + cmd=`echo "$@" | sed 's/--fuse/ceph-fuse/'` + fi + + # remove the --brxip parameter + cmd=`echo "$cmd" | sed 's/--brxip.*\/[0-9]* //'` + + # enter $new_netns and run ceph fuse client mount, + # we couldn't use 'ip netns exec' here because it + # will unshare the mount namespace. + echo "$cmd" > $tmpfile + nsenter --net=/var/run/netns/$new_netns /bin/bash $tmpfile ; echo $? > $tmpfile + rc=`cat $tmpfile` + rm -f $tmpfile + + # fall back + if [ $rc != 0 ]; then + m=$mountpoint + mountpoint="" + ceph_umount $m + fi +} + +function get_new_nsid() { + # get one uniq netns id + uniq_id=0 + while [ 1 ] + do + rc=`ip netns list-id | grep "nsid $uniq_id "` + if [ "0$rc" == "0" ]; then + break + fi + let uniq_id+=1 + done + + eval $1="$uniq_id" +} + +function ceph_mount() { + get_mountpoint $@ + setup_bridge_and_nat $@ + + get_new_netns $1 + rc=`ip netns list | grep "$new_netns" | awk '{print $1}'` + if [ "0$rc" != "0" ]; then + echo "Oops: the netns "$new_netns" already exists!" + exit 1 + fi + + get_new_nsid new_nsid + + # create a new network namespace + ip netns add $new_netns + ip netns set $new_netns $new_nsid + + get_new_ns_ip ns_ip + if [ 0"$ns_ip" == "0" ]; then + echo "Oops: there is no ip address could be used any more!" + exit 1 + fi + + # veth interface in netns + ns_veth=veth0 + netns_brx=brx.$new_nsid + + # setup veth interfaces + ip link add $ns_veth netns $new_netns type veth peer name $netns_brx + ip netns exec $new_netns ip addr add $ns_ip/$NET_MASK brd $BRD dev $ns_veth + ip netns exec $new_netns ip link set $ns_veth up + ip netns exec $new_netns ip link set lo up + ip netns exec $new_netns ip route add default via $CEPH_BRX_IP + + # bring up the bridge interface and join it to $CEPH_BRX + # brctl addif $CEPH_BRX $netns_brx + nmcli connection add type bridge-slave con-name $netns_brx ifname $netns_brx master $CEPH_BRX + nmcli connection up $netns_brx + # ip link set $netns_brx up + + __ceph_mount $@ +} + +if [ "$1" == "--umount" ]; then + ceph_umount $@ + exit 0 +fi + +# mount in the netns +if [ "$1" != "--kernel" -a "$1" != "--fuse" ]; then + echo "Oops: invalid mount options '$1'!" + exit 1 +fi + +ceph_mount $@ |