Load balancing
This is a extension of what is described in http://wiki.debian.org/DebianEdu/Documentation/Etch/HowTo/NetworkClients#head-7587290321a833444df956e05b30f1a4cde2df3e - it extends part 2 with a second option, where the choosen LTSP server is not random but the least utilized one. Un
Option 2
The second option is more advanced, it gives you a server list generated by querying ldminfod for the server rating. By querying ldminfod, you can get the current rating state of the server. This rating goes from 0 to 100, higher is better. For this to work, you have to use the ubuntu version of ldminfod. (remark from h01ger: which version of ubuntu? it looks like Debian lenny is enough too?) The Skolelinux version doesn't have the server rating function.
First you should make a backup of the original ldminfod-file, which is in /usr/sbin/. Then, put the following script in a new ldminfod-file. Make sure the new file has the same permissions as the old one.
import sys
import os
import locale
from subprocess import *
def get_memory_usage():
memusg = {}
# Get memory usage information, according to /proc/meminfo
f = open('/proc/meminfo', 'r')
swap_free = 0
mem_physical_free = 0
mem_buffers = 0
mem_cached = 0
for line in f.readlines():
tokens = line.split()
label = tokens[0]
size = tokens[1]
try:
size = int(size)
except:
# The line is an header, skip it.
continue
# We approximate kb to bytes
size = size * 1024
if label == 'MemTotal:':
memusg['ram_total'] = size
elif label == 'MemFree:':
mem_physical_free = size
elif label == 'Buffers:':
mem_buffers = size
elif label == 'Cached:':
mem_cached = size
elif label == 'SwapTotal:':
memusg['swap_total'] = size
elif label == 'SwapFree:':
swap_free = size
f.close()
memusg['ram_used'] = memusg['ram_total'] - mem_physical_free - mem_buffers - mem_cached
memusg['swap_used'] = memusg['swap_total'] - swap_free
return memusg
def get_load_average():
# Gets the current system load, according to /proc/loadavg
loadavg = {}
load_file = open('/proc/loadavg')
load_infos = load_file.read().split()
loadavg['one_min_avg'] = load_infos[0]
loadavg['five_min_avg'] = load_infos[1]
loadavg['fifteen_min_avg'] = load_infos[2]
# scheduling_info = load_infos[3] # not used
# last_pid = load_infos[4]
load_file.close()
return loadavg
def compute_server_rating():
"""Compute the server rating from it's state
The rating is computed by using load average and the memory
used. The returned value is betweed 0 and 100, higher is better
"""
max_acceptable_load_avg = 8.0
mem = get_memory_usage()
load = get_load_average()
rating = 100 - int(
50 * ( float(load['fifteen_min_avg']) / max_acceptable_load_avg ) +
50 * ( float(mem['ram_used']) / float(mem['ram_total']) )
)
if rating < 0:
rating = 0
return rating
def get_sessions (dir):
"""Get a list of available sessions.
Returns a list of sessions gathered from .desktop files
"""
sessions = []
if os.path.isdir(dir):
for f in os.listdir(dir):
if f.endswith('.desktop') and os.path.isfile(os.path.join(dir, f)):
x={}
for line in file(os.path.join(dir, f), 'r').readlines():
if 'Exec=' in line:
variable, value = line.split('=')
x[variable]=value
if 'TryExec' in x:
sessions.append(x['TryExec'].rstrip())
elif 'Exec' in x:
sessions.append(x['Exec'].rstrip())
return sessions
if __name__ == "__main__":
# Get the server's default locale
# We want it to appear first in the list
try:
lines = Popen(['locale'], stdout=PIPE).communicate()[0]
except OSError:
print "ERROR: failed to run locale"
sys.exit(0)
for line in lines.split():
if line.startswith('LANG='):
defaultlocale = line.split('=')[1].strip('"')
defaultlocale = defaultlocale.replace('UTF8', 'UTF-8')
print "language:" + defaultlocale
# Get list of valid locales from locale -a
try:
lines = Popen(['locale', '-a'], stdout=PIPE).communicate()[0]
except OSError:
print "ERROR"
sys.exit(0)
langs = lines.split(None)
langs.sort()
for lang in langs:
lang = lang.rstrip()
if lang.endswith('.utf8'):
# locale returns .utf8 when we want .UTF-8
lang = lang.replace('.utf8','.UTF-8')
else:
# if locale did not end with .utf8, do not add to list
continue
if lang != 'POSIX' and lang != 'C' and lang != defaultlocale:
print "language:" + lang
try:
lines = get_sessions('/usr/share/xsessions/')
except OSError:
print "ERROR"
sys.exit(0)
for line in lines:
print "session:" + line
# Get the rating of this server
rate = 0
try:
rate = compute_server_rating()
except:
print "ERROR"
sys.exit(0)
print "rating:" + str(compute_server_rating())Now you have to edit "/opt/ltsp/i386/etc/lts.conf" and add this:
MY_SERVERS = "xxxx xxxx xxxx"
Replace xxxx with either the IP or hostname of the servers, list must be space separated. Then, put the following script in /opt/ltsp/i386/usr/lib/ltsp/get_hosts on the server you chose to be the loadbalancing server.
LIST=""
# Load query on all servers
for i in $MY_SERVERS; do
LOAD=`nc $i 9571|grep rating|cut -d: -f2`
if test $LOAD; then
# add to the list
LIST="$LIST$LOAD $i\n"
fi
done
# Now LIST contains the list of servers sorted by load
LIST=`echo -e $LIST | sort -nr`
# Check if the 1st items have the same load. If so, randomly choose one.
OLDIFS=$IFS
IFS="
"
BESTLIST=( )
I=0
for LINE in $LIST ;do
LOAD=`echo $LINE|cut -f 1 -d " "`
if [ "$OLDLOAD" -a "$LOAD" != "$OLDLOAD" ] ;then
break
fi
BESTLIST[$I]="$LINE"
OLDLOAD=$LOAD
I=$((I+1))
done
RAND=$(( $RANDOM % $I ))
# print the choosen host
echo ${BESTLIST[$RAND]} | cut -f 2 -d " "
exit 0