13.04.2011 12:06, Andrew Beekhof wrote: > On Sat, Mar 19, 2011 at 6:10 PM, Vladislav Bogdanov > <[email protected]> wrote: >> Hi, >> >> just bumping this to be not forgotten. > > Actually I'd missed that this was a Pacemaker specific one and > therefore something I needed to look at :-) > >> RA runs fine for almost a month, several simulated network outages were >> passed with full success, so it could be included in some package. I >> think pacemaker, because this RA uses pacemaker-specific calls. >> >> Andrew? > > Looks pretty reasonable. > Can you resend with hg headers (ie. "hg export") so that you can have > the appropriate credit? > It should be added to extra/resources
Please find attached. It also has one issue with newer pacemaker fixed (OCF_RESKEY_CRM_meta_interval is not longer supplied for some reason). Vladislav
# HG changeset patch # User Vladislav Bogdanov <[email protected]> # Date 1302687018 -10800 # Node ID db47bd038103b1f476c90dd787b7f65cdabac9fe # Parent 5bdfc629f35af0305bb87676297d81251f0511ea Add ifspeed resource agent diff -r 5bdfc629f35a -r db47bd038103 extra/resources/ifspeed --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extra/resources/ifspeed Wed Apr 13 12:30:18 2011 +0300 @@ -0,0 +1,457 @@ +#!/bin/bash +# +# OCF resource agent which monitors state of network interface and records it +# as a value in CIB based on summ of speeds of its active (up, link detected, +# not blocked) underlying interfaces. +# +# Copyright (c) 2011 Vladislav Bogdanov <[email protected]> +# Partially based on 'ping' RA by Andrew Beekhof +# +# OCF instance parameters: +# OCF_RESKEY_name: name of attribute to set in CIB +# OCF_RESKEY_iface: network interface to monitor +# OCF_RESKEY_bridge_ports: if not null and OCF_RESKEY_iface is a bridge, list of +# bridge ports to consider. +# Default is all ports which have designated_bridge=root_id +# OCF_RESKEY_weight_base: Relative weight of 1Gbps. This can be used to tune +# value of resulting CIB attribute. +# +# Initialization: + +: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} +. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs + +# Defaults +OCF_RESKEY_name_default="ifspeed" +OCF_RESKEY_bridge_ports_default="detect" +OCF_RESKEY_weight_base_default=1000 +OCF_RESKEY_dampen_default=5 + +: ${OCF_RESKEY_name=${OCF_RESKEY_name_default}} +: ${OCF_RESKEY_bridge_ports=${OCF_RESKEY_bridge_ports_default}} +: ${OCF_RESKEY_weight_base=${OCF_RESKEY_weight_base_default}} +: ${OCF_RESKEY_dampen=${OCF_RESKEY_dampen_default}} + +meta_data() { + cat <<END +<?xml version="1.0"?> +<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd"> +<resource-agent name="ifspeed"> +<version>1.0</version> + +<longdesc lang="en"> +Every time the monitor action is run, this resource agent records (in the CIB) +(relative) speed of network interface it monitors. + +This RA can monitor physical interfaces, bonds, bridges, vlans and (hopefully) +any combination of them. + +Examples: +*) Bridge on top of one 10Gbps interface (eth2) and 802.3ad bonding (bond0) built + on two 1Gbps interfaces (eth0 and eth1). +*) Active-backup bonding built on top of one physical interface and one vlan on + another interface. + +For STP-enabled bridges this RA tries to some-how guess network topology and by +default looks only on ports which are connected to upstream switch. This can be +overriden by 'bridge_ports' parameter. Active interfaces in this case are those +in "forwarding" state. + +For balancing bonds this RA summs speeds of underlying "up" slave interfaces +(and applies coefficient 0.8 to result). + +For non-balancing bonds ('active-backup' and probably 'brodcast') only speed of +now active slave is used. +</longdesc> +<shortdesc lang="en">Network interface speed monitor</shortdesc> + +<parameters> + +<parameter name="name" unique="1"> +<longdesc lang="en"> +The name of the attribute to set. This is the name to be used in the constraints. +</longdesc> +<shortdesc lang="en">Attribute name</shortdesc> +<content type="string" default="${OCF_RESKEY_name_default}"/> +</parameter> + +<parameter name="iface" unique="0" required="1"> +<longdesc lang="en"> +Network interface to monitor. +</longdesc> +<shortdesc lang="en">Network interface</shortdesc> +<content type="string" default=""/> +</parameter> + +<parameter name="bridge_ports" unique="0"> +<longdesc lang="en"> +If not null and OCF_RESKEY_iface is a bridge, list of bridge ports to consider. +Default is all ports which have designated_bridge=root_id. +</longdesc> +<shortdesc lang="en">Bridge ports</shortdesc> +<content type="string" default="${OCF_RESKEY_bridge_ports_default}"/> +</parameter> + +<parameter name="weight_base" unique="0"> +<longdesc lang="en"> +Relative weight of 1Gbps in interface speed. +Can be used to tune how big attribute value will be. +</longdesc> +<shortdesc lang="en">Weight of 1Gbps</shortdesc> +<content type="integer" default="${OCF_RESKEY_weight_base_default}"/> +</parameter> + +<parameter name="dampen" unique="0"> +<longdesc lang="en"> +The time to wait (dampening) for further changes to occur. +</longdesc> +<shortdesc lang="en">Dampening interval</shortdesc> +<content type="integer" default="${OCF_RESKEY_dampen_default}"/> +</parameter> + +<parameter name="debug" unique="0"> +<longdesc lang="en"> +Log what have been done more verbosely. +</longdesc> +<shortdesc lang="en">Verbose logging</shortdesc> +<content type="string" default="false"/> +</parameter> + +</parameters> + +<actions> +<action name="start" timeout="30" /> +<action name="stop" timeout="30" /> +<action name="reload" timeout="30" /> +<action name="monitor" depth="0" timeout="30" interval="10"/> +<action name="meta-data" timeout="5" /> +<action name="validate-all" timeout="30" /> +</actions> +</resource-agent> +END +} + +usage() { + cat <<END +usage: $0 {start|stop|reload|monitor|validate-all|meta-data} + +Expects to have a fully populated OCF RA-compliant environment set. +END +} + +start() { + monitor + if [ $? -eq $OCF_SUCCESS ]; then + return $OCF_SUCCESS + fi + ha_pseudo_resource ${ha_pseudo_resource_name} start + update + return $? +} + +stop() { + ha_pseudo_resource ${ha_pseudo_resource_name} stop + attrd_updater -D -n ${OCF_RESKEY_name} -d ${OCF_RESKEY_dampen} ${attrd_options} + return $OCF_SUCCESS +} + +monitor() { + local ret + ha_pseudo_resource ${ha_pseudo_resource_name} monitor + ret=$? + if [ ${ret} -eq $OCF_SUCCESS ] ; then + update + fi + return ${ret} +} + +validate() { + # Check the interface parameter + if [ -z "${OCF_RESKEY_iface}" ]; then + ocf_log err "Empty iface parameter. Please specify network interface to check" + exit $OCF_ERR_CONFIGURED + fi + + return $OCF_SUCCESS +} + +iface_get_speed() { + local iface=$1 + local operstate + local carrier + local bridge_iface_speed + local bond_iface_speed + local vlan_iface_speed + local speed + + if [ ! -e "/sys/class/net/${iface}" ] ; then + echo "0" + elif iface_is_bridge ${iface} ; then # bridges do not have operstate + read carrier < "/sys/class/net/${iface}/carrier" + + if [ "${carrier}" != "1" ] ; then + echo "0" + else + bridge_get_speed ${iface} + fi + else + read operstate < "/sys/class/net/${iface}/operstate" + read carrier < "/sys/class/net/${iface}/carrier" + + if [ "${operstate}" != "up" ] || [ "${carrier}" != "1" ] ; then + echo "0" + elif iface_is_bond ${iface} ; then + bond_get_speed ${iface} + elif iface_is_vlan ${iface} ; then + iface_get_speed $( vlan_get_phy ${iface} ) + else + read speed < "/sys/class/net/${iface}/speed" + echo ${speed} + fi + fi +} + +iface_is_vlan() { + local iface=$1 + [ -e "/proc/net/vlan/${iface}" ] && return 0 || return 1 +} + +iface_is_bridge() { + local iface=$1 + [ -e "/sys/class/net/${iface}/bridge" ] && return 0 || return 1 +} + +iface_is_bond() { + local iface=$1 + [ -e "/sys/class/net/${iface}/bonding" ] && return 0 || return 1 +} + +vlan_get_phy() { + local iface=$1 + sed -ne "s/^${iface} .*| *//p" < /proc/net/vlan/config +} + +bridge_is_stp_enabled() { + local iface=$1 + local stp + read stp < "/sys/class/net/${iface}/bridge/stp_state" + [ "${stp}" = "1" ] && return 0 || return 1 +} + +bridge_get_root_ports() { + local bridge=$1 + local root_id + local root_ports="" + local bridge_id + + read root_id < "/sys/class/net/${bridge}/bridge/root_id" + + for port in /sys/class/net/${bridge}/brif/* ; do + read bridge_id < "${port}/designated_bridge" + if [ "${bridge_id}" = "${root_id}" ] ; then + root_ports="${root_ports} ${port##*/}" + fi + done + + root_ports=${root_ports# } + + if [ -n "$2" ] ; then # Record value in specified var. This expects we were called not in a sub-shell. + eval $2=\${root_ports} + else # Expect sub-shell + echo ${root_ports} + fi +} + +# From /inlude/linux/if_bridge.h: +#define BR_STATE_DISABLED 0 +#define BR_STATE_LISTENING 1 +#define BR_STATE_LEARNING 2 +#define BR_STATE_FORWARDING 3 +#define BR_STATE_BLOCKING 4 + +bridge_get_active_ports() { + local bridge=$1 + shift 1 + local ports="$*" + local active_ports="" + local port_state + local stp_state + local warn=0 + + bridge_is_stp_enabled ${bridge} + stp_state=$? + + if [ -z "${ports}" ] || [ "${ports}" = "detect" ] ; then + bridge_get_root_ports ${bridge} ports + fi + + for port in $ports ; do + if [ ! -e "/sys/class/net/${bridge}/brif/${port}" ] ; then + ocf_log warning "Port ${port} doesn't belong to bridge ${bridge}" + continue + fi + read port_state < "/sys/class/net/${bridge}/brif/${port}/state" + if [ "${port_state}" = "3" ] ; then + if [ -n "${active_ports}" ] && ${stp_state} ; then + warn=1 + fi + active_ports="${active_ports} ${port}" + fi + done + if [ ${warn} -eq 1 ] ; then + ocf_log warning "More then one upstream port in bridge '${bridge}' is in forwarding state while STP is enabled: ${active_ports}" + fi + echo "${active_ports# }" +} + +bridge_get_speed() { + local iface=$1 + local bridge_port_speed + local aggregate_speed=0 + + if ! iface_is_bridge ${iface} ; then + echo 0 + return + fi + + local ports=$( bridge_get_active_ports ${iface} ${OCF_RESKEY_bridge_ports} ) + for port in ${ports} ; do + : $(( aggregate_speed += $( iface_get_speed ${port} ) )) + done + if [ -n "$2" ] ; then # Record value in specified var. This expects we were called not in a sub-shell. + eval $2=\${aggregate_speed} + else # Expect sub-shell + echo ${aggregate_speed} + fi +} + +bond_get_slaves() { + local iface=$1 + local slaves + read slaves < "/sys/class/net/${iface}/bonding/slaves" + if [ -n "$2" ] ; then # Record value in specified var. This expects we were called not in a sub-shell. + eval $2=\${slaves} + else # Expect sub-shell + echo ${slaves} + fi +} + +bond_get_active_iface() { + local iface=$1 + local active + read active < "/sys/class/net/${iface}/bonding/active_slave" + if [ -n "$2" ] ; then # Record value in specified var. This expects we were called not in a sub-shell. + eval $2=\${active} + else # Expect sub-shell + echo ${active} + fi +} + +bond_is_balancing() { + local iface=$1 + read mode mode_index < "/sys/class/net/${iface}/bonding/mode" + case ${mode} in + "balance-rr"|"balance-xor"|"802.3ad"|"balance-tlb"|"balance-alb") + return 0 + ;; + *) + return 1 + ;; + esac +} + +bond_get_speed() { + local iface=$1 + local aggregate_speed=0 + local bond_slave_speed + local active_iface + local bond_slaves + + if ! iface_is_bond ${iface} ; then + echo 0 + return + fi + + bond_get_slaves ${iface} bond_slaves + + if bond_is_balancing ${iface} ; then + for slave in ${bond_slaves} ; do + : $(( aggregate_speed += $( iface_get_speed ${slave} ) )) + done + # Bonding is unable to get speed*n + : $(( aggregate_speed = aggregate_speed * 8 / 10 )) + else + bond_get_active_iface ${iface} active_iface + aggregate_speed=$( iface_get_speed $active_iface ) + fi + if [ -n "$2" ] ; then # Record value in specified var. This expects we were called not in a sub-shell. + eval $2=\${aggregate_speed} + else # Expect sub-shell + echo ${aggregate_speed} + fi +} + +update() { + local speed=$( iface_get_speed ${OCF_RESKEY_iface} ) + + : $(( score = speed * ${OCF_RESKEY_weight_base} / 1000 )) + attrd_updater -n ${OCF_RESKEY_name} -v ${score} -d ${OCF_RESKEY_dampen} ${attrd_options} + rc=$? + case ${rc} in + 0) + ocf_is_true ${OCF_RESKEY_debug} && ocf_log debug "Updated ${OCF_RESKEY_name} = ${score}" + ;; + *) + ocf_log warn "Could not update ${OCF_RESKEY_name} = ${score}: rc=${rc}" + ;; + esac + return ${rc} +} + +case $__OCF_ACTION in + meta-data) + meta_data + exit $OCF_SUCCESS + ;; + usage|help) + usage + exit $OCF_SUCCESS + ;; +esac + +if [ `uname` != "Linux" ] ; then + ocf_log err "This RA works only on linux." + exit $OCF_ERR_INSTALLED +fi + +: ${ha_pseudo_resource_name:="ifspeed-${OCF_RESOURCE_INSTANCE}"} + +attrd_options='-q' +if ocf_is_true ${OCF_RESKEY_debug} ; then + attrd_options='' +fi + +validate || exit $? + +case $__OCF_ACTION in + start) + start + ;; + stop) + stop + ;; + monitor) + monitor + ;; + reload) + start + ;; + validate-all) + ;; + *) + usage + exit $OCF_ERR_UNIMPLEMENTED + ;; +esac + +exit $?
_______________________________________________ Pacemaker mailing list: [email protected] http://oss.clusterlabs.org/mailman/listinfo/pacemaker Project Home: http://www.clusterlabs.org Getting started: http://www.clusterlabs.org/doc/Cluster_from_Scratch.pdf Bugs: http://developerbugs.linux-foundation.org/enter_bug.cgi?product=Pacemaker
