Hi Dejan, 22.02.2011 13:02, Dejan Muhamedagic wrote: > Hi, > > Where can you get STP stuff from? How to interpret it? And then
Please look at attached RA. I decided that today is a good time to finally brace myself to find 5 hours to write it, thanks Frederik ;) . Tested, "works for me" (c) in that configuration I talked earlier - STP bridge over 1x10Gbps eth + 2x1Gpbs bond. (Hopefully) Supports any combination of bridges, bonds, vlans and physical ethernet interfaces. Tries to guess correct upstream bridge ports, can't test it more thoroughly due to absence of more switch hardware (I currently have only one c3570x stack per cluster). May require some additional checks to be included. Also it is linux-specific and requires bash because of my laziness and fact that I mainly use Fedora which has nothing against bash yet. Can be considered for inclusion in resource-agents (with common license, GPLv2?). > how do you know it's something that won't change within next five > minutes? Finally, every failover can incur downtime, is it worth > the trouble because what you want is just more performance? This could be controlled by non-inf location score and f.e. time-based stickiness. Anyways, I'd better have 10 seconds lockup rather than 10Mb/s per-client read for long time when second cluster node (32 disks in HW RAID10) is able to easily give another 250-400Mb/s of aggregate throughput. > Perhaps you don't even need the extra performance at the time. This depends on what SLA I provide services with... > Other than that it sounds interesting :-) Then, please look at the implementation ;) Best, Vladislav
#!/bin/bash # # OCF resource agent which monitors state of network interfaces and records it as a value in CIB # based on summ of speeds of active interfaces from list. # # Copyright (c) 2011 Vladislav Bogdanov <bub...@hoster-ok.com> # Partially based on 'ping' RA by Andrew Beekhof # # OCF instance parameters: # OCF_RESKEY_name: name of attribute to set in CIB # OCF_RESKEY_iface: space separated list of network interfaces 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: weight of each 10Mbps in interface speed (1Gbps = 100 * 10Mbps) in CIB score points # # Initialization: : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/heartbeat} . ${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs # Defaults OCF_RESKEY_name_default="ifstatus" OCF_RESKEY_bridge_ports_default="detect" OCF_RESKEY_weight_base_default=10 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="ping"> <version>1.0</version> <longdesc lang="en"> Every time the monitor action is run, this resource agent records (in the CIB) speed of active network interfaces from a list. </longdesc> <shortdesc lang="en">Network interface status</shortdesc> <parameters> <parameter name="name" unique="1"> <longdesc lang="en"> The name of the attributes 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 interfaces 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"> Weight of each 10Mbps in interface speed (1Gbps = 100 * 10Mbps). With default value 1Gbps interface will be counted as 1000. </longdesc> <shortdesc lang="en">Weight of 10Mbps interface</shortdesc> <content type="integer" default="${OCF_RESKEY_weight_base_default}"/> </parameter> <parameter name="dampen" unique="0"> <longdesc lang="en"> The time to wait (dampening) further changes 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"> Enables to use default attrd_updater verbose logging on every call. </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 } ifstatus_usage() { cat <<END usage: $0 {start|stop|monitor|migrate_to|migrate_from|validate-all|meta-data} Expects to have a fully populated OCF RA-compliant environment set. END } ifstatus_start() { ifstatus_monitor if [ $? = $OCF_SUCCESS ]; then return $OCF_SUCCESS fi touch ${OCF_RESKEY_pidfile} ifstatus_update } ifstatus_stop() { rm -f ${OCF_RESKEY_pidfile} attrd_updater -D -n $OCF_RESKEY_name -d $OCF_RESKEY_dampen $attrd_options return $OCF_SUCCESS } ifstatus_monitor() { if [ -f ${OCF_RESKEY_pidfile} ]; then ifstatus_update return $OCF_SUCCESS fi return $OCF_NOT_RUNNING } ifstatus_validate() { # Is the state directory writable? state_dir=`dirname "$OCF_RESKEY_pidfile"` touch "$state_dir/$$" if [ $? != 0 ]; then ocf_log err "Invalid location for 'state': $state_dir is not writable" return $OCF_ERR_ARGS fi rm "$state_dir/$$" # Pidfile better be an absolute path case $OCF_RESKEY_pidfile in /*) ;; *) ocf_log warn "You should use an absolute path for pidfile not: $OCF_RESKEY_pidfile" ;; esac # Check the check interval if ocf_is_decimal "$OCF_RESKEY_CRM_meta_interval" && [ $OCF_RESKEY_CRM_meta_interval -gt 0 ]; then : else ocf_log err "Invalid check interval $OCF_RESKEY_interval. It should be positive integer!" exit $OCF_ERR_CONFIGURED fi # Check the intarfaces list if [ "x" = "x$OCF_RESKEY_iface" ]; then ocf_log err "Empty iface parameter. Please specify some network interface to check" exit $OCF_ERR_CONFIGURED fi return $OCF_SUCCESS } ifstatus_iface_get_speed() { local iface=$1 local operstate local carrier local speed if [ ! -e /sys/class/net/$iface ] ; then echo 0 elif ifstatus_iface_is_bridge $iface ; then ifstatus_bridge_get_speed $iface elif ifstatus_iface_is_bond $iface ; then ifstatus_bond_get_speed $iface elif ifstatus_iface_is_vlan $iface ; then ifstatus_iface_get_speed $( ifstatus_vlan_get_phy $iface ) else read operstate < "/sys/class/net/$iface/operstate" read carrier < "/sys/class/net/$iface/carrier" if [ "$operstate" != "up" ] || [ "$carrier" != "1" ] ; then speed="0" else read speed < "/sys/class/net/$iface/speed" fi echo $speed fi } ifstatus_iface_is_vlan() { local iface=$1 [ -e "/proc/net/vlan/$iface" ] && return 0 || return 1 } ifstatus_iface_is_bridge() { local iface=$1 [ -e "/sys/class/net/$iface/bridge" ] && return 0 || return 1 } ifstatus_iface_is_bond() { local iface=$1 [ -e "/sys/class/net/$iface/bonding" ] && return 0 || return 1 } ifstatus_vlan_get_phy() { local iface=$1 grep "^$iface " "/proc/net/vlan/config" | sed -r 's/.*\| +(.*)/\1/' } ifstatus_bridge_is_stp_enabled() { local iface=$1 local stp read stp < "/sys/class/net/$iface/bridge/stp_state" case $stp in "1") return 0 ;; *) return 1 ;; esac } ifstatus_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 echo "${root_ports# }" } # 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 ifstatus_bridge_get_active_ports() { local bridge=$1 shift 1 local ports="$*" local active_ports="" local port_state local stp_state=ifstatus_bridge_is_stp_enabled $bridge local warn=0 if [ "$ports" = "detect" ] ; then ports=$( ifstatus_bridge_get_root_ports $bridge ) 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 bridge port in '$bridge' is in forwarding state while STP is enabled: $active_ports" fi echo "${active_ports# }" } ifstatus_bridge_get_speed() { local $iface=$1 ! ifstatus_iface_is_bridge $iface && echo 0 local ports=$( ifstatus_bridge_get_active_ports $iface ${OCF_RESKEY_bridge_ports} ) for port in $ports ; do : $(( aggregate_speed += $( ifstatus_iface_get_speed $port ) )) done echo $aggregate_speed } ifstatus_bond_get_slaves() { local iface=$1 local slaves read slaves < "/sys/class/net/$iface/bonding/slaves" echo $slaves } ifstatus_bond_get_active_iface() { local iface=$1 local active read active < "/sys/class/net/$iface/bonding/active_slave" echo $active } ifstatus_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 ;; "active-backup"|"broadcast") return 1 ;; *) # Just for safety return 0 ;; esac } ifstatus_bond_get_speed() { local iface=$1 local aggregate_speed=0 ! ifstatus_iface_is_bond $iface && echo 0 local slaves=$( ifstatus_bond_get_slaves $iface ) if ifstatus_bond_is_balancing $iface ; then for slave in $slaves ; do : $(( aggregate_speed += $( ifstatus_iface_get_speed $slave ) )) done # Bonding is unable to get speed*n : $(( aggregate_speed = aggregate_speed*8/10 )) else : $(( aggregate_speed = $( ifstatus_iface_get_speed $( ifstatus_bond_get_active_iface $iface ) ) )) fi echo $aggregate_speed } ifstatus_update() { local speed=$( ifstatus_iface_get_speed $OCF_RESKEY_iface) : $(( score = speed * $OCF_RESKEY_weight_base / 10 )) 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 } if [ `uname` != "Linux" ] ; then ocf_log err "This RA works only on linux." exit $OCF_ERR_INSTALLED fi if ! ocf_is_true ${OCF_RESKEY_CRM_meta_globally_unique} ; then : ${OCF_RESKEY_pidfile:="$HA_VARRUN/ifstatus-${OCF_RESKEY_name}"} else : ${OCF_RESKEY_pidfile:="$HA_VARRUN/ifstatus-${OCF_RESOURCE_INSTANCE}"} fi attrd_options='-q' if ocf_is_true ${OCF_RESKEY_debug} ; then attrd_options='' fi case $__OCF_ACTION in meta-data) meta_data exit $OCF_SUCCESS ;; start) ifstatus_start ;; stop) ifstatus_stop ;; monitor) ifstatus_monitor ;; reload) ifstatus_start ;; validate-all) ifstatus_validate ;; usage|help) ifstatus_usage exit $OCF_SUCCESS ;; *) ifstatus_usage exit $OCF_ERR_UNIMPLEMENTED ;; esac exit $?
_______________________________________________ Pacemaker mailing list: Pacemaker@oss.clusterlabs.org 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