3508d8bc34
Corrected all bash tests for zero and non-zero length vars. Corrected missing quotes when checking for netmask and ns string length. Renamed [[ to [ where appropriate for -n and -z tests. Change-Id: Ic83a406ab7681222679edee6688f4ba1aa0a19aa Closes-Bug: #1455961
543 lines
15 KiB
Bash
Executable File
543 lines
15 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
#######################################################################
|
|
# Initialization:
|
|
|
|
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
|
|
. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
|
|
|
|
# Defaults
|
|
|
|
OCF_RESKEY_cidr_netmask_default="32"
|
|
OCF_RESKEY_base_veth_default="" # may be omited if OVS used
|
|
OCF_RESKEY_gateway_default="none" # can be "none", "link", IPaddr
|
|
OCF_RESKEY_gateway_metric_default=0 # can be "", or metric value
|
|
OCF_RESKEY_also_check_interfaces_default="" # can be "", or list of interfaces
|
|
OCF_RESKEY_other_networks_default="" # can be "", or list of networks in CIDR format
|
|
|
|
: ${HA_LOGTAG="ocf-ns_IPaddr2"}
|
|
: ${HA_LOGFACILITY="daemon"}
|
|
: ${OCF_RESKEY_cidr_netmask=${OCF_RESKEY_cidr_netmask_default}}
|
|
: ${OCF_RESKEY_base_veth=${OCF_RESKEY_base_veth_default}}
|
|
: ${OCF_RESKEY_gateway=${OCF_RESKEY_gateway_default}}
|
|
: ${OCF_RESKEY_gateway_metric=${OCF_RESKEY_gateway_metric_default}}
|
|
: ${OCF_RESKEY_also_check_interfaces=${OCF_RESKEY_also_check_interfaces_default}}
|
|
: ${OCF_RESKEY_other_networks=${OCF_RESKEY_other_networks_default}}
|
|
|
|
FAMILY='inet'
|
|
RUN_IN_NS="ip netns exec $OCF_RESKEY_ns "
|
|
SH="/bin/bash"
|
|
|
|
#######################################################################
|
|
|
|
#######################################################################
|
|
|
|
meta_data() {
|
|
cat <<END
|
|
<?xml version="1.0"?>
|
|
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
|
|
<resource-agent name="IPaddr2">
|
|
<version>1.0</version>
|
|
|
|
<longdesc lang="en">
|
|
This Linux-specific resource manages IP address inside network namespace.
|
|
</longdesc>
|
|
|
|
<shortdesc lang="en">This Linux-specific resource manages IP address inside network namespace.</shortdesc>
|
|
|
|
<parameters>
|
|
|
|
<parameter name="bridge" required="1">
|
|
<longdesc lang="en">
|
|
Name of the bridge that has network namespace with VIP connected to it.
|
|
</longdesc>
|
|
<shortdesc lang="en">Name of the bridge.</shortdesc>
|
|
<content type="string" />
|
|
</parameter>
|
|
|
|
<parameter name="ip" unique="1" required="1">
|
|
<longdesc lang="en">
|
|
The IPv4 address to be configured in dotted quad notation, for example
|
|
"192.168.1.1".
|
|
</longdesc>
|
|
<shortdesc lang="en">IPv4 address</shortdesc>
|
|
<content type="string" />
|
|
</parameter>
|
|
|
|
<parameter name="cidr_netmask">
|
|
<longdesc lang="en">
|
|
The netmask for the interface in CIDR format
|
|
(e.g., 24 and not 255.255.255.0)
|
|
|
|
If unspecified, the script will also try to determine this from the
|
|
routing table.
|
|
</longdesc>
|
|
<shortdesc lang="en">CIDR netmask</shortdesc>
|
|
<content type="string" default=""/>
|
|
</parameter>
|
|
|
|
<parameter name="iflabel">
|
|
<longdesc lang="en">
|
|
You can specify an additional label for your IP address here.
|
|
This label is appended to your interface name.
|
|
|
|
A label can be specified in nic parameter but it is deprecated.
|
|
If a label is specified in nic name, this parameter has no effect.
|
|
</longdesc>
|
|
<shortdesc lang="en">Interface label</shortdesc>
|
|
<content type="string" default=""/>
|
|
</parameter>
|
|
|
|
<parameter name="ns" required="1">
|
|
<longdesc lang="en">
|
|
Name of network namespace.\n
|
|
Should be present.
|
|
</longdesc>
|
|
<shortdesc lang="en">Name of network namespace.</shortdesc>
|
|
<content type="string" />
|
|
</parameter>
|
|
|
|
<parameter name="base_veth">
|
|
<longdesc lang="en">
|
|
Name of base system side veth pair tail.\n
|
|
Should be present.
|
|
</longdesc>
|
|
<shortdesc lang="en">Name of base system side veth pair tail.</shortdesc>
|
|
<content type="string" default="$OCF_RESKEY_base_veth_default"/>
|
|
</parameter>
|
|
|
|
<parameter name="ns_veth" required="1">
|
|
<longdesc lang="en">
|
|
Name of net.namespace side veth pair tail.\n
|
|
Should be present.
|
|
</longdesc>
|
|
<shortdesc lang="en">Name of net.namespace side veth pair tail.</shortdesc>
|
|
<content type="string"/>
|
|
</parameter>
|
|
|
|
<parameter name="gateway">
|
|
<longdesc lang="en">
|
|
Default route address.\n
|
|
Can be "", "link" or IP address.
|
|
</longdesc>
|
|
<shortdesc lang="en">Default route address.</shortdesc>
|
|
<content type="string" default="$OCF_RESKEY_gateway_default"/>
|
|
</parameter>
|
|
|
|
<parameter name="gateway_metric">
|
|
<longdesc lang="en">
|
|
Default route address.\n
|
|
Can be "", "link" or IP address.
|
|
</longdesc>
|
|
<shortdesc lang="en">Default route address.</shortdesc>
|
|
<content type="string" default="$OCF_RESKEY_gateway_metric_default"/>
|
|
</parameter>
|
|
|
|
<parameter name="ns_iptables_start_rules">
|
|
<longdesc lang="en">
|
|
Iptables rules that should be started along with IP in the namespace.\n
|
|
</longdesc>
|
|
<shortdesc lang="en">Iptables rules associated with IP start in ns.</shortdesc>
|
|
<content type="string" default=""/>
|
|
</parameter>
|
|
|
|
<parameter name="ns_iptables_stop_rules">
|
|
<longdesc lang="en">
|
|
Iptables rules that should be stopped along with IP in the namespace.\n
|
|
</longdesc>
|
|
<shortdesc lang="en">Iptables rules associated with IP stop in ns.</shortdesc>
|
|
<content type="string" default=""/>
|
|
</parameter>
|
|
|
|
<parameter name="iptables_comment">
|
|
<longdesc lang="en">
|
|
Iptables comment to associate with rules.\n
|
|
</longdesc>
|
|
<shortdesc lang="en">Iptables comment to associate with rules.</shortdesc>
|
|
<content type="string" default="something_rule_for_VIP"/>
|
|
</parameter>
|
|
|
|
<parameter name="also_check_interfaces">
|
|
<longdesc lang="en">
|
|
Network interfaces list (ex. NIC), that should be in UP state for monitor action returns succesful.\n
|
|
</longdesc>
|
|
<shortdesc lang="en">Network interfaces list (ex. NIC), that should be in UP state for monitor action returns succesful.</shortdesc>
|
|
<content type="string" default="$OCF_RESKEY_also_check_interfaces_default"/>
|
|
</parameter>
|
|
|
|
<parameter name="other_networks">
|
|
<longdesc lang="en">
|
|
Additional routes that should be added to this resource. Routes will be added via value ns_veth. Should be space separated list of networks in CIDR format.
|
|
</longdesc>
|
|
<shortdesc lang="en">List of addtional routes to add routes for.</shortdesc>
|
|
<content type="string" default="$OCF_RESKEY_other_networks_default"/>
|
|
</parameter>
|
|
|
|
</parameters>
|
|
<actions>
|
|
<action name="start" timeout="20s" />
|
|
<action name="stop" timeout="20s" />
|
|
<action name="status" depth="0" timeout="20s" interval="10s" />
|
|
<action name="monitor" depth="0" timeout="20s" interval="10s" />
|
|
<action name="meta-data" timeout="5s" />
|
|
<action name="validate-all" timeout="20s" />
|
|
</actions>
|
|
</resource-agent>
|
|
END
|
|
|
|
exit $OCF_SUCCESS
|
|
}
|
|
|
|
|
|
ip_validate() {
|
|
|
|
if [[ X`uname -s` != "XLinux" ]] ; then
|
|
ocf_log err "ns_IPaddr2 only supported Linux."
|
|
exit $OCF_ERR_INSTALLED
|
|
fi
|
|
|
|
if [ -z "$OCF_RESKEY_ip" ] ; then
|
|
ocf_log err "IP address not given"
|
|
exit $OCF_ERR_CONFIGURED
|
|
fi
|
|
|
|
if [ -z "$OCF_RESKEY_ns" ] ; then
|
|
ocf_log err "Network namespace not given"
|
|
exit $OCF_ERR_CONFIGURED
|
|
fi
|
|
|
|
if [ -z "$OCF_RESKEY_cidr_netmask" ] ; then
|
|
ocf_log err "CIDR Netmask not given"
|
|
exit $OCF_ERR_CONFIGURED
|
|
fi
|
|
|
|
if [ -z "$OCF_RESKEY_ns_veth" ] ; then
|
|
ocf_log err "NS veth tail name not given"
|
|
exit $OCF_ERR_CONFIGURED
|
|
fi
|
|
|
|
if ! ocf_is_decimal "$OCF_RESKEY_gateway_metric"; then
|
|
ocf_log err "Gateway_metric should be a positive digital value"
|
|
exit $OCF_ERR_CONFIGURED
|
|
fi
|
|
|
|
return $OCF_SUCCESS
|
|
}
|
|
|
|
|
|
#
|
|
# Find out which interfaces serve the given IP address and netmask.
|
|
# The arguments are an IP address and a netmask.
|
|
# Its output are interface names devided by spaces (e.g., "eth0 eth1").
|
|
#
|
|
find_interface() {
|
|
local ipaddr="$1"
|
|
local netmask="$2"
|
|
[ -z "$netmask" ] || ipaddr="$ipaddr/$netmask"
|
|
|
|
#
|
|
# List interfaces but exclude FreeS/WAN ipsecN virtual interfaces
|
|
local iface="`ip -o -f inet addr show \
|
|
| grep "\ $ipaddr" \
|
|
| cut -d ' ' -f2 \
|
|
| grep -v '^ipsec[[0-9]][[0-9]]*$'`"
|
|
local rc=$?
|
|
echo "$iface"
|
|
return $rc
|
|
}
|
|
|
|
find_interface_in_ns() {
|
|
local ns="$1"
|
|
local ipaddr="$2"
|
|
local netmask="$3"
|
|
[ -z "$netmask" ] || ipaddr="$ipaddr/$netmask"
|
|
|
|
#
|
|
# List interfaces but exclude FreeS/WAN ipsecN virtual interfaces
|
|
local iface=`ip netns exec $ns ip -o -f inet addr show \
|
|
| grep "\ $ipaddr" \
|
|
| cut -d ' ' -f2 \
|
|
| grep -v '^ipsec[[0-9]][[0-9]]*$'`
|
|
local rc=$?
|
|
echo "$iface"
|
|
return $rc
|
|
}
|
|
|
|
setup_routes() {
|
|
local network
|
|
if [ ! -z "$OCF_RESKEY_other_networks" ] ; then
|
|
for network in ${OCF_RESKEY_other_networks} ; do
|
|
ocf_log debug "Adding route on the host system to ${network}: ${OCF_RESKEY_namespace_ip}"
|
|
ocf_run $RUN_IN_NS ip route add ${network} dev ${OCF_RESKEY_ns_veth}
|
|
done
|
|
fi
|
|
}
|
|
|
|
# add veth to bridge if not already added
|
|
add_to_bridge() {
|
|
local br="$1"
|
|
local veth="$2"
|
|
local ns_veth="$3"
|
|
local bridge_mtu=`cat /sys/class/net/${br}/mtu`
|
|
# check which bridge (OVS or LNX) used
|
|
if [[ -d /sys/class/net/${br}/brif ]] ; then
|
|
# LNX
|
|
if [[ ! -d /sys/class/net/${br}/brif/${veth} ]] ; then
|
|
# attach 1-st jack to an LNX bridge
|
|
ocf_run brctl addif $br $veth || return $OCF_ERR_GENERIC
|
|
fi
|
|
else
|
|
# OVS
|
|
ovs-vsctl list port $veth > /dev/null 2>&1
|
|
if [[ $? != 0 ]] ; then
|
|
# attach 1-st jack to an OVS bridge
|
|
ocf_run ovs-vsctl --may-exist add-port $br $veth || return $OCF_ERR_GENERIC
|
|
fi
|
|
fi
|
|
# adjust MTU
|
|
ocf_run ip link set mtu $bridge_mtu dev $veth
|
|
ocf_run $RUN_IN_NS ip link set mtu $bridge_mtu dev $ns_veth
|
|
return $OCF_SUCCESS
|
|
}
|
|
|
|
remove_from_bridge() {
|
|
if [[ -d /sys/class/net/${OCF_RESKEY_bridge}/brif ]] ; then
|
|
# native linux bridges
|
|
if [[ -d /sys/class/net/${OCF_RESKEY_bridge}/brif/${OCF_RESKEY_base_veth} ]] ; then
|
|
ocf_run brctl delif $OCF_RESKEY_bridge $OCF_RESKEY_base_veth || return $OCF_ERR_GENERIC
|
|
else
|
|
ocf_log warn "Jack ${OCF_RESKEY_base_veth} not a member of ${OCF_RESKEY_bridge} yet."
|
|
fi
|
|
else
|
|
# OVS bridge
|
|
ocf_run ovs-vsctl del-port $OCF_RESKEY_bridge $OCF_RESKEY_base_veth || return $OCF_ERR_GENERIC
|
|
fi
|
|
return $OCF_SUCCESS
|
|
}
|
|
|
|
|
|
#######################################################################
|
|
|
|
|
|
check_ns() {
|
|
local ns=`ip netns list | grep "$OCF_RESKEY_ns"`
|
|
[[ "$ns" != "$OCF_RESKEY_ns" ]] && return $OCF_ERR_GENERIC
|
|
return $OCF_SUCCESS
|
|
}
|
|
|
|
get_ns() {
|
|
local rc
|
|
check_ns || ocf_run ip netns add $OCF_RESKEY_ns
|
|
ocf_run $RUN_IN_NS ip link set up dev lo || return $OCF_ERR_GENERIC
|
|
return $OCF_SUCCESS
|
|
}
|
|
|
|
get_or_create_veth_pair() {
|
|
local rc
|
|
local rc1
|
|
|
|
# check tail of veth-pair in base system
|
|
ip link show $OCF_RESKEY_base_veth 2>&1 > /dev/null
|
|
rc=$?
|
|
if [[ $rc != 0 ]] ; then
|
|
# 1st jack not found, need to create pair and attach 2nd jack to the net.namespace
|
|
# create veth pair and put 2nd jack to the net.ns
|
|
ocf_run ip link add $OCF_RESKEY_base_veth type veth peer name $OCF_RESKEY_ns_veth
|
|
ocf_run ip link set dev $OCF_RESKEY_ns_veth netns $OCF_RESKEY_ns
|
|
ocf_run ip link set up dev $OCF_RESKEY_base_veth
|
|
ocf_run $RUN_IN_NS ip link set up dev $OCF_RESKEY_ns_veth
|
|
sleep 1
|
|
# connect veth-pair to the bridge and adjust MTU
|
|
add_to_bridge $OCF_RESKEY_bridge $OCF_RESKEY_base_veth $OCF_RESKEY_ns_veth
|
|
fi
|
|
return $OCF_SUCCESS
|
|
}
|
|
|
|
check_interfaces_for_up_state() {
|
|
local interfaces=$(echo "$1" | tr " ,:;" "\n")
|
|
local rc=$OCF_SUCCESS
|
|
|
|
for i in $interfaces ; do
|
|
rv=$(cat /sys/class/net/$i/carrier) # can return non-zero error-code for administrative-downed interface
|
|
if [[ $? != 0 || $rv != "1" ]] ; then
|
|
rc=$OCF_NOT_RUNNING
|
|
break
|
|
fi
|
|
done
|
|
|
|
return $rc
|
|
}
|
|
|
|
ip_prepare() {
|
|
local rc
|
|
ip_validate
|
|
[[ $? != 0 ]] && return $OCF_ERR_GENERIC
|
|
|
|
# create or get existing network namespace
|
|
get_ns || return $OCF_ERR_GENERIC
|
|
|
|
# create or get existing pair of veth interfaces
|
|
get_or_create_veth_pair || return $OCF_ERR_GENERIC
|
|
|
|
# attach IP address inside network namespace
|
|
ocf_run $RUN_IN_NS ip addr replace "$OCF_RESKEY_ip/$OCF_RESKEY_cidr_netmask" dev $OCF_RESKEY_ns_veth
|
|
[[ $? != 0 ]] && return $OCF_ERR_GENERIC
|
|
|
|
# setup default routing in namespace if gateway given
|
|
if [[ "$OCF_RESKEY_gateway" == 'link' ]] ; then
|
|
ocf_run $RUN_IN_NS ip route replace default dev $OCF_RESKEY_ns_veth metric $OCF_RESKEY_gateway_metric
|
|
elif [[ "$OCF_RESKEY_gateway" == 'none' ]] ; then
|
|
echo "Setup default gateway -- do nothing."
|
|
else
|
|
ocf_run $RUN_IN_NS ip route replace default via $OCF_RESKEY_gateway metric $OCF_RESKEY_gateway_metric
|
|
fi
|
|
|
|
# Send Gratuitous ARP REQUEST packets to update all neighbours in a detached background process
|
|
ARGS="-U -c 32 -w 10 -I $OCF_RESKEY_ns_veth -q $OCF_RESKEY_ip"
|
|
$RUN_IN_NS arping $ARGS 2>&1 > /dev/null &
|
|
|
|
# Send Gratuitous ARP REPLY packets to update all neighbours in a detached background process
|
|
ARGS="-A -c 32 -w 10 -I $OCF_RESKEY_ns_veth -q $OCF_RESKEY_ip"
|
|
$RUN_IN_NS arping $ARGS 2>&1 > /dev/null &
|
|
|
|
return $OCF_SUCCESS
|
|
}
|
|
|
|
iptables_start() {
|
|
local ns_iptables_rules
|
|
local rule
|
|
# setup iptables rules if given
|
|
|
|
if [[ "$OCF_RESKEY_ns_iptables_start_rules" != "false" ]] ; then
|
|
IFS=';' read -a ns_iptables_rules <<< "$OCF_RESKEY_ns_iptables_start_rules"
|
|
for rule in "${ns_iptables_rules[@]}" ; do
|
|
ocf_run $RUN_IN_NS $rule
|
|
done
|
|
fi
|
|
|
|
setup_routes
|
|
return $OCF_SUCCESS
|
|
}
|
|
|
|
iptables_stop() {
|
|
local ns_iptables_rules
|
|
local rule
|
|
# remove iptables rules if given
|
|
if [[ $OCF_RESKEY_ns_iptables_stop_rules != "false" ]] ; then
|
|
IFS=';' read -a ns_iptables_rules <<< "$OCF_RESKEY_ns_iptables_stop_rules"
|
|
for rule in "${ns_iptables_rules[@]}" ; do
|
|
ocf_run $RUN_IN_NS $rule
|
|
done
|
|
fi
|
|
|
|
return $OCF_SUCCESS
|
|
}
|
|
|
|
ip_start() {
|
|
check_interfaces_for_up_state "$OCF_RESKEY_bridge:$OCF_RESKEY_also_check_interfaces" || return $OCF_ERR_GENERIC
|
|
ip_prepare
|
|
|
|
rc=$?
|
|
if [[ $rc != $OCF_SUCCESS ]] ; then
|
|
# cleanun ns
|
|
ip_stop
|
|
rc=$OCF_ERR_GENERIC
|
|
else
|
|
iptables_start
|
|
fi
|
|
return $rc
|
|
}
|
|
|
|
ip_stop() {
|
|
local rc
|
|
ip_validate
|
|
if [ -n "$OCF_RESKEY_bridge" ] ; then
|
|
remove_from_bridge
|
|
fi
|
|
# destroy veth-pair in base system
|
|
ocf_run ip link show $OCF_RESKEY_base_veth
|
|
rc=$?
|
|
if [[ $rc == 0 ]] ; then
|
|
ocf_run ip link set down dev $OCF_RESKEY_base_veth &&
|
|
sleep 1 && # prevent race
|
|
ocf_run ip link del dev $OCF_RESKEY_base_veth
|
|
rc=$?
|
|
else
|
|
rc=0
|
|
fi
|
|
|
|
if [[ $rc == 0 ]] ; then
|
|
rc=$OCF_SUCCESS # it means stop was success
|
|
iptables_stop
|
|
else
|
|
rc=$OCF_ERR_GENERIC
|
|
fi
|
|
return $rc
|
|
}
|
|
|
|
ip_monitor() {
|
|
local rc
|
|
ip_validate
|
|
check_ns || return $OCF_NOT_RUNNING
|
|
local iface=$(find_interface_in_ns $OCF_RESKEY_ns $OCF_RESKEY_ip $OCF_RESKEY_cidr_netmask)
|
|
|
|
[ -z "$iface" ] && return $OCF_NOT_RUNNING
|
|
|
|
check_interfaces_for_up_state "$OCF_RESKEY_bridge:$OCF_RESKEY_also_check_interfaces" || return $OCF_NOT_RUNNING
|
|
# use arping here, because no IP from VIP network allowed on host system
|
|
ocf_run arping -c 10 -w 2 -I $OCF_RESKEY_bridge $OCF_RESKEY_ip || return $OCF_NOT_RUNNING
|
|
|
|
# Send Gratuitous ARP REQUEST packets to update all neighbours in a detached background process
|
|
ARGS="-U -c 5 -w 2 -I $OCF_RESKEY_ns_veth -q $OCF_RESKEY_ip"
|
|
$RUN_IN_NS arping $ARGS 2>&1 > /dev/null &
|
|
|
|
# Send Gratuitous ARP REPLY packets to update all neighbours in a detached background process
|
|
ARGS="-A -c 5 -w 2 -I $OCF_RESKEY_ns_veth -q $OCF_RESKEY_ip"
|
|
$RUN_IN_NS arping $ARGS 2>&1 > /dev/null &
|
|
|
|
return $OCF_SUCCESS
|
|
}
|
|
|
|
|
|
ip_usage() {
|
|
cat <<END
|
|
usage: $0 {start|stop|status|monitor|validate-all|meta-data}
|
|
|
|
Expects to have a fully populated OCF RA-compliant environment set.
|
|
END
|
|
}
|
|
|
|
## main
|
|
|
|
rc=$OCF_SUCCESS
|
|
case $__OCF_ACTION in
|
|
meta-data)
|
|
meta_data
|
|
exit $OCF_SUCCESS
|
|
;;
|
|
usage|help)
|
|
ip_usage
|
|
exit $OCF_SUCCESS
|
|
;;
|
|
start)
|
|
ip_start
|
|
rc=$?
|
|
;;
|
|
stop)
|
|
ip_stop
|
|
rc=$?
|
|
;;
|
|
monitor)
|
|
ip_monitor
|
|
rc=$?
|
|
;;
|
|
validate-all)
|
|
;;
|
|
*)
|
|
ip_usage
|
|
exit $OCF_ERR_UNIMPLEMENTED
|
|
;;
|
|
esac
|
|
exit $rc
|
|
# vi:sw=4:ts=8:
|