#!/bin/sh prog="ctdb" # Print a message and exit. die() { echo "$1" >&2 exit "${2:-1}" } not_implemented_exit_code=1 usage() { cat >&2 <&2 exit $not_implemented_exit_code } verbose=false machine_readable=false nodespec="" args="" # Options and command argument can appear in any order, so when # getopts thinks it is done, process any non-option arguments and go # around again. while [ $# -gt 0 ]; do while getopts "Xvhn:?" opt; do case "$opt" in X) machine_readable=true ;; v) verbose=true ;; n) nodespec="$OPTARG" ;; \? | *) usage ;; esac done shift $((OPTIND - 1)) # Anything left over must be a non-option arg if [ $# -gt 0 ]; then args="${args}${args:+ }${1}" shift fi done [ -n "$args" ] || usage # Want word splitting # shellcheck disable=SC2086 set -- $args setup_tickles() { # Make sure tickles file exists. tickles_file="${CTDB_TEST_TMP_DIR}/fake-ctdb/tickles" mkdir -p "$(dirname "$tickles_file")" touch "$tickles_file" } ctdb_gettickles() { _ip="$1" _port="$2" setup_tickles echo "|source ip|port|destination ip|port|" while read -r _src _dst; do if [ -z "$_ip" ] || [ "$_ip" = "${_dst%:*}" ]; then if [ -z "$_port" ] || [ "$_port" = "${_dst##*:}" ]; then echo "|${_src%:*}|${_src##*:}|${_dst%:*}|${_dst##*:}|" fi fi done <"$tickles_file" } ctdb_addtickle() { _src="$1" _dst="$2" setup_tickles if [ -n "$_dst" ]; then echo "${_src} ${_dst}" >>"$tickles_file" else cat >>"$tickles_file" fi } ctdb_deltickle() { _src="$1" _dst="$2" setup_tickles if [ -n "$_dst" ]; then _t=$(grep -F -v "${_src} $${_dst}" "$tickles_file") else _t=$(cat "$tickles_file") while read -r _src _dst; do _t=$(echo "$_t" | grep -F -v "${_src} ${_dst}") done fi echo "$_t" >"$tickles_file" } parse_nodespec() { if [ "$nodespec" = "all" ]; then nodes="$(seq 0 $((FAKE_CTDB_NUMNODES - 1)))" elif [ -n "$nodespec" ]; then nodes="$(echo "$nodespec" | sed -e 's@,@ @g')" else nodes=$(ctdb_pnn) fi } # For testing backward compatibility... for i in $CTDB_NOT_IMPLEMENTED; do if [ "$i" = "$1" ]; then not_implemented "$i" fi done ctdb_pnn() { # Defaults to 0 echo "${FAKE_CTDB_PNN:-0}" } ###################################################################### FAKE_CTDB_NODE_STATE="$FAKE_CTDB_STATE/node-state" FAKE_CTDB_NODES_DISABLED="$FAKE_CTDB_NODE_STATE/0x4" ###################################################################### # NOTE: all nodes share public addresses file FAKE_CTDB_IP_LAYOUT="$FAKE_CTDB_STATE/ip-layout" ip_reallocate() { touch "$FAKE_CTDB_IP_LAYOUT" # ShellCheck doesn't understand this flock pattern # shellcheck disable=SC2094 ( flock 0 _pa="${CTDB_BASE}/public_addresses" if [ ! -s "$FAKE_CTDB_IP_LAYOUT" ]; then sed -n -e 's@^\([^#][^/]*\)/.*@\1 -1@p' \ "$_pa" >"$FAKE_CTDB_IP_LAYOUT" fi _t="${FAKE_CTDB_IP_LAYOUT}.new" _flags="" for _i in $(seq 0 $((FAKE_CTDB_NUMNODES - 1))); do if ls "$FAKE_CTDB_STATE/node-state/"*"/$_i" >/dev/null 2>&1; then # Have non-zero flags _this=0 for _j in "$FAKE_CTDB_STATE/node-state/"*"/$_i"; do _tf="${_j%/*}" # dirname _f="${_tf##*/}" # basename _this=$((_this | _f)) done else _this="0" fi _flags="${_flags}${_flags:+,}${_this}" done CTDB_TEST_LOGLEVEL=NOTICE \ "ctdb_takeover_tests" \ "ipalloc" "$_flags" <"$FAKE_CTDB_IP_LAYOUT" | sort >"$_t" mv "$_t" "$FAKE_CTDB_IP_LAYOUT" ) <"$FAKE_CTDB_IP_LAYOUT" } ctdb_ip() { # If nobody has done any IP-fu then generate a layout. [ -f "$FAKE_CTDB_IP_LAYOUT" ] || ip_reallocate _mypnn=$(ctdb_pnn) if $machine_readable; then if $verbose; then echo "|Public IP|Node|ActiveInterface|AvailableInterfaces|ConfiguredInterfaces|" else echo "|Public IP|Node|" fi else echo "Public IPs on node ${_mypnn}" fi # Join public addresses file with $FAKE_CTDB_IP_LAYOUT, and # process output line by line... _pa="${CTDB_BASE}/public_addresses" sed -e 's@/@ @' "$_pa" | sort | join - "$FAKE_CTDB_IP_LAYOUT" | while read -r _ip _ _ifaces _pnn; do if $verbose; then # If more than 1 interface, assume all addresses are on the 1st. _first_iface="${_ifaces%%,*}" # Only show interface if address is on this node. _my_iface="" if [ "$_pnn" = "$_mypnn" ]; then _my_iface="$_first_iface" fi if $machine_readable; then echo "|${_ip}|${_pnn}|${_my_iface}|${_first_iface}|${_ifaces}|" else echo "${_ip} node[${_pnn}] active[${_my_iface}] available[${_first_iface}] configured[[${_ifaces}]" fi else if $machine_readable; then echo "|${_ip}|${_pnn}|" else echo "${_ip} ${_pnn}" fi fi done } ctdb_moveip() { _ip="$1" _target="$2" ip_reallocate # should be harmless and ensures we have good state # ShellCheck doesn't understand this flock pattern # shellcheck disable=SC2094 ( flock 0 _t="${FAKE_CTDB_IP_LAYOUT}.new" while read -r _i _pnn; do if [ "$_ip" = "$_i" ]; then echo "$_i $_target" else echo "$_i $_pnn" fi done | sort >"$_t" mv "$_t" "$FAKE_CTDB_IP_LAYOUT" ) <"$FAKE_CTDB_IP_LAYOUT" } ###################################################################### ctdb_enable() { parse_nodespec for _i in $nodes; do rm -f "${FAKE_CTDB_NODES_DISABLED}/${_i}" done ip_reallocate } ctdb_disable() { parse_nodespec for _i in $nodes; do mkdir -p "$FAKE_CTDB_NODES_DISABLED" touch "${FAKE_CTDB_NODES_DISABLED}/${_i}" done ip_reallocate } ###################################################################### ctdb_shutdown() { echo "CTDB says BYE!" } ###################################################################### # This is only used by the NAT and LVS gateway code at the moment, so # use a hack. Assume that $CTDB_NATGW_NODES or $CTDB_LVS_NODES # contains all nodes in the cluster (which is what current tests # assume). Use the PNN to find the address from this file. The NAT # gateway code only used the address, so just mark the node healthy. ctdb_nodestatus() { echo '|Node|IP|Disconnected|Banned|Disabled|Unhealthy|Stopped|Inactive|PartiallyOnline|ThisNode|' _line=$((FAKE_CTDB_PNN + 1)) _ip=$(sed -e "${_line}p" "${CTDB_NATGW_NODES:-${CTDB_LVS_NODES}}") echo "|${FAKE_CTDB_PNN}|${_ip}|0|0|0|0|0|0|0|Y|" } ###################################################################### _t_setup() { _t_dir="${CTDB_TEST_TMP_DIR}/fake-ctdb/fake-tdb/$1" mkdir -p "$_t_dir" } _t_put() { echo "$2" >"${_t_dir}/$1" } _t_get() { cat "${_t_dir}/$1" } _t_del() { rm -f "${_t_dir}/$1" } ctdb_pstore() { _t_setup "$1" _t_put "$2" "$3" } ctdb_pdelete() { _t_setup "$1" _t_del "$2" } ctdb_pfetch() { _t_setup "$1" _t_get "$2" >"$3" 2>/dev/null } ctdb_ptrans() { _t_setup "$1" while IFS="" read -r _line; do _k=$(echo "$_line" | sed -n -e 's@^"\([^"]*\)" "[^"]*"$@\1@p') _v=$(echo "$_line" | sed -e 's@^"[^"]*" "\([^"]*\)"$@\1@') [ -n "$_k" ] || die "ctdb ptrans: bad line \"${_line}\"" if [ -n "$_v" ]; then _t_put "$_k" "$_v" else _t_del "$_k" fi done } ctdb_catdb() { _t_setup "$1" # This will break on keys with spaces but we don't have any of # those yet. _count=0 for _i in "${_t_dir}/"*; do [ -r "$_i" ] || continue _k="${_i##*/}" # basename _v=$(_t_get "$_k") _kn=$(printf '%s' "$_k" | wc -c) _vn=$(printf '%s' "$_v" | wc -c) cat </dev/null 2>&1; then "$func" "$@" else not_implemented "$cmd" fi