[X2Go-Dev] [X2Go-Commits] [x2gobroker] 05/08: New feature: x2gobroker-loadchecker daemon.
Mike Gabriel
mike.gabriel at das-netzwerkteam.de
Thu Mar 26 11:50:57 CET 2015
Hi all,
I just pushed a new feature to the X2Go Session Broker Git repo
(master branch).
This feature is still work in progress, but for multi-site testing, I
need the packages from the nightly builds repo.
So, stay tuned...
Mike
On Do 26 Mär 2015 11:47:54 CET, git-admin wrote:
> This is an automated email from the git hooks/post-receive script.
>
> x2go pushed a commit to branch master
> in repository x2gobroker.
>
> commit c571e891e4203eedd1b4f68346e66eb49b09c7b5
> Author: Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
> Date: Thu Mar 26 09:57:39 2015 +0100
>
> New feature: x2gobroker-loadchecker daemon.
> ---
> bin/x2gobroker-ssh | Bin 10328 ->
> 10328 bytes
> debian/control | 33 ++
> debian/x2gobroker-agent.postrm | 6 +-
> debian/x2gobroker-authservice.postrm | 6 +-
> debian/x2gobroker-daemon.postrm | 6 +-
> debian/x2gobroker-loadchecker.default | 1 +
> debian/x2gobroker-loadchecker.init | 1 +
> debian/x2gobroker-loadchecker.install | 3 +
> debian/x2gobroker-loadchecker.manpages | 1 +
> debian/x2gobroker-loadchecker.postinst | 76 +++++
> ...ervice.postrm => x2gobroker-loadchecker.postrm} | 8 +-
> debian/x2gobroker-loadchecker.service | 1 +
> debian/x2gobroker-wsgi.postrm | 6 +-
> defaults/python-x2gobroker.default | 3 +
> defaults/x2gobroker-loadchecker.default | 20 ++
> etc/broker/defaults.conf | 3 +
> etc/broker/x2gobroker-loadchecker-logger.conf | 51 ++++
> etc/x2gobroker.conf | 49 +++
> init/x2gobroker-loadchecker.init | 104 +++++++
> lib/x2gobroker-agent.pl | 49 +++
> logrotate/x2gobroker-loadchecker | 14 +
> man/man8/x2gobroker-authservice.8 | 2 +-
> man/man8/x2gobroker-loadchecker.8 | 93 ++++++
> sbin/x2gobroker-loadchecker | 319
> ++++++++++++++++++++
> x2gobroker-loadchecker.service | 12 +
> x2gobroker.spec | 119 ++++++++
> x2gobroker/agent.py | 25 ++
> x2gobroker/brokers/base_broker.py | 115 +++++++
> x2gobroker/defaults.py | 18 ++
> x2gobroker/loadchecker.py | 219 ++++++++++++++
> 30 files changed, 1346 insertions(+), 17 deletions(-)
>
> diff --git a/bin/x2gobroker-ssh b/bin/x2gobroker-ssh
> index 10f24a3..ad1a492 100755
> Binary files a/bin/x2gobroker-ssh and b/bin/x2gobroker-ssh differ
> diff --git a/debian/control b/debian/control
> index aca8fce..1a90ed5 100644
> --- a/debian/control
> +++ b/debian/control
> @@ -115,6 +115,37 @@ Description: X2Go Session Broker (PAM
> authentication service)
> This package contains the authentication service
> against the PAM system.
>
> +Package: x2gobroker-loadchecker
> +Architecture: all
> +Depends:
> + ${python:Depends},
> + ${misc:Depends},
> + adduser,
> + python,
> + python-argparse,
> + python-setproctitle,
> + python-x2gobroker (>= ${source:Version}), python-x2gobroker (<<
> ${source:Version}.1~),
> +Suggests:
> + x2gobroker-daemon,
> +Description: X2Go Session Broker (load checker service)
> + X2Go is a server based computing environment with
> + - session resuming
> + - low bandwidth support
> + - session brokerage support
> + - client side mass storage mounting support
> + - client side printing support
> + - audio support
> + - authentication by smartcard and USB stick
> + .
> + The session broker is a server tool for X2Go that tells your X2Go Client
> + application in a terminal server cluster what servers and session types are
> + most appropriate for the user in front of the X2Go terminal.
> + .
> + A session broker is most useful in load balanced X2Go server farms.
> + .
> + This package contains the load checker service required for broker setups
> + with dynamic load balancing.
> +
> Package: x2gobroker-daemon
> Architecture: all
> Depends:
> @@ -123,6 +154,8 @@ Depends:
> x2gobroker (>= ${source:Version}), x2gobroker (<< ${source:Version}.1~),
> Recommends:
> x2gobroker-authservice,
> +Suggests:
> + x2gobroker-loadchecker,
> Description: X2Go Session Broker (standalone daemon)
> X2Go is a server based computing environment with
> - session resuming
> diff --git a/debian/x2gobroker-agent.postrm b/debian/x2gobroker-agent.postrm
> index 0739da6..0f5c579 100755
> --- a/debian/x2gobroker-agent.postrm
> +++ b/debian/x2gobroker-agent.postrm
> @@ -23,15 +23,15 @@ case "$1" in
> dpkg-statoverride --remove /usr/lib/x2go/x2gobroker-agent
> fi
>
> - if [ ! -d /usr/share/doc/x2gobroker-daemon ] && [ ! -d
> /usr/share/doc/x2gobroker-authservice ] && [ ! -d
> /usr/share/doc/x2gobroker-wsgi ]; then
> + if [ ! -d /usr/share/doc/x2gobroker-daemon ] && [ ! -d
> /usr/share/doc/x2gobroker-authservice ] && [ ! -d
> /usr/share/doc/x2gobroker-wsgi ] && [ ! -d
> /usr/share/doc/x2gobroker-loadchecker ]; then
> if dpkg-statoverride --list /var/log/x2gobroker 1>/dev/null; then
> dpkg-statoverride --remove /var/log/x2gobroker
> fi
> rm -Rf /var/log/x2gobroker
> fi
>
> - if [ ! -d /usr/share/doc/x2gobroker-daemon ] && [ ! -d
> /usr/share/doc/x2gobroker-authservice ] && [ ! -d
> /usr/share/doc/x2gobroker-wsgi ]; then
> - # remove user/group x2gobroker from system (if not in use by
> x2gobroker-daemon, x2gobroker-authservice, x2gobroker-wsgi)
> + if [ ! -d /usr/share/doc/x2gobroker-daemon ] && [ ! -d
> /usr/share/doc/x2gobroker-authservice ] && [ ! -d
> /usr/share/doc/x2gobroker-wsgi ] && [ ! -d
> /usr/share/doc/x2gobroker-loadchecker ]; then
> + # remove user/group x2gobroker from system (if not in use by
> x2gobroker-daemon, x2gobroker-authservice, x2gobroker-wsgi,
> x2gobroker-loadchecker)
> getent passwd x2gobroker 1>/dev/null && deluser x2gobroker
> getent group x2gobroker 1>/dev/null && delgroup x2gobroker
> getent group x2gobroker 1>/dev/null && delgroup x2gobroker
> diff --git a/debian/x2gobroker-authservice.postrm
> b/debian/x2gobroker-authservice.postrm
> index 851bfa5..6db5213 100755
> --- a/debian/x2gobroker-authservice.postrm
> +++ b/debian/x2gobroker-authservice.postrm
> @@ -19,15 +19,15 @@ set -e
> case "$1" in
> purge)
>
> - if [ ! -d /usr/share/doc/x2gobroker-daemon ] && [ ! -d
> /usr/share/doc/x2gobroker-wsgi ] && [ ! -d
> /usr/share/doc/x2gobroker-agent ] ; then
> + if [ ! -d /usr/share/doc/x2gobroker-daemon ] && [ ! -d
> /usr/share/doc/x2gobroker-wsgi ] && [ ! -d
> /usr/share/doc/x2gobroker-agent ] && [ ! -d
> /usr/share/doc/x2gobroker-loadchecker ]; then
> if dpkg-statoverride --list /var/log/x2gobroker 1>/dev/null; then
> dpkg-statoverride --remove /var/log/x2gobroker
> fi
> rm -Rf /var/log/x2gobroker
> fi
>
> - if [ ! -d /usr/share/doc/x2gobroker-daemon ] && [ ! -d
> /usr/share/doc/x2gobroker-agent ] && [ ! -d
> /usr/share/doc/x2gobroker-wsgi ]; then
> - # remove user/group x2gobroker from system (only if not in use
> by x2gobroker-daemon, x2gobroker-agent, x2gobroker-wsgi)
> + if [ ! -d /usr/share/doc/x2gobroker-daemon ] && [ ! -d
> /usr/share/doc/x2gobroker-agent ] && [ ! -d
> /usr/share/doc/x2gobroker-wsgi ] && [ ! -d
> /usr/share/doc/x2gobroker-loadchecker ]; then
> + # remove user/group x2gobroker from system (only if not in use
> by x2gobroker-daemon, x2gobroker-agent, x2gobroker-wsgi,
> x2gobroker-loadchecker)
> getent passwd x2gobroker 1>/dev/null && deluser x2gobroker
> getent group x2gobroker 1>/dev/null && delgroup x2gobroker
> getent group x2gobroker 1>/dev/null && delgroup x2gobroker
> diff --git a/debian/x2gobroker-daemon.postrm
> b/debian/x2gobroker-daemon.postrm
> index 5d76a1b..ab50ca0 100755
> --- a/debian/x2gobroker-daemon.postrm
> +++ b/debian/x2gobroker-daemon.postrm
> @@ -19,15 +19,15 @@ set -e
> case "$1" in
> purge)
>
> - if [ ! -d /usr/share/doc/x2gobroker-agent ] && [ ! -d
> /usr/share/doc/x2gobroker-authservice ] && [ ! -d
> /usr/share/doc/x2gobroker-wsgi ]; then
> + if [ ! -d /usr/share/doc/x2gobroker-agent ] && [ ! -d
> /usr/share/doc/x2gobroker-authservice ] && [ ! -d
> /usr/share/doc/x2gobroker-wsgi ] && [ ! -d
> /usr/share/doc/x2gobroker-loadchecker ]; then
> if dpkg-statoverride --list /var/log/x2gobroker 1>/dev/null; then
> dpkg-statoverride --remove /var/log/x2gobroker
> fi
> rm -Rf /var/log/x2gobroker
> fi
>
> - if [ ! -d /usr/share/doc/x2gobroker-agent ] && [ ! -d
> /usr/share/doc/x2gobroker-authservice ] && [ ! -d
> /usr/share/doc/x2gobroker-wsgi ]; then
> - # remove user/group x2gobroker from system (only if not in use
> by x2gobroker-agent, x2gobroker-authservice, x2gobroker-wsgi)
> + if [ ! -d /usr/share/doc/x2gobroker-agent ] && [ ! -d
> /usr/share/doc/x2gobroker-authservice ] && [ ! -d
> /usr/share/doc/x2gobroker-wsgi ] && [ ! -d
> /usr/share/doc/x2gobroker-loadchecker ]; then
> + # remove user/group x2gobroker from system (only if not in use
> by x2gobroker-agent, x2gobroker-authservice, x2gobroker-wsgi,
> x2gobroker-loadchecker)
> getent passwd x2gobroker 1>/dev/null && deluser x2gobroker
> getent group x2gobroker 1>/dev/null && delgroup x2gobroker
> getent group x2gobroker 1>/dev/null && delgroup x2gobroker
> diff --git a/debian/x2gobroker-loadchecker.default
> b/debian/x2gobroker-loadchecker.default
> new file mode 120000
> index 0000000..25ba295
> --- /dev/null
> +++ b/debian/x2gobroker-loadchecker.default
> @@ -0,0 +1 @@
> +../defaults/x2gobroker-loadchecker.default
> \ No newline at end of file
> diff --git a/debian/x2gobroker-loadchecker.init
> b/debian/x2gobroker-loadchecker.init
> new file mode 120000
> index 0000000..b970565
> --- /dev/null
> +++ b/debian/x2gobroker-loadchecker.init
> @@ -0,0 +1 @@
> +../init/x2gobroker-loadchecker.init
> \ No newline at end of file
> diff --git a/debian/x2gobroker-loadchecker.install
> b/debian/x2gobroker-loadchecker.install
> new file mode 100644
> index 0000000..769abbc
> --- /dev/null
> +++ b/debian/x2gobroker-loadchecker.install
> @@ -0,0 +1,3 @@
> +sbin/x2gobroker-loadchecker usr/sbin/
> +logrotate/x2gobroker-loadchecker etc/logrotate.d/
> +etc/broker/x2gobroker-loadchecker-logger.conf etc/x2go/broker
> \ No newline at end of file
> diff --git a/debian/x2gobroker-loadchecker.manpages
> b/debian/x2gobroker-loadchecker.manpages
> new file mode 100644
> index 0000000..1832989
> --- /dev/null
> +++ b/debian/x2gobroker-loadchecker.manpages
> @@ -0,0 +1 @@
> +man/man8/x2gobroker-loadchecker.8
> \ No newline at end of file
> diff --git a/debian/x2gobroker-loadchecker.postinst
> b/debian/x2gobroker-loadchecker.postinst
> new file mode 100755
> index 0000000..c2f42ce
> --- /dev/null
> +++ b/debian/x2gobroker-loadchecker.postinst
> @@ -0,0 +1,76 @@
> +#!/bin/sh
> +# postinst script for x2gobroker-loadchecker
> +#
> +# see: dh_installdeb(1)
> +
> +set -e
> +
> +# summary of how this script can be called:
> +# * <postinst> `configure' <most-recently-configured-version>
> +# * <old-postinst> `abort-upgrade' <new version>
> +# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
> +# <new-version>
> +# * <postinst> `abort-remove'
> +# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
> +# <failed-install-package> <version> `removing'
> +# <conflicting-package> <version>
> +# for details, see http://www.debian.org/doc/debian-policy/ or
> +# the debian-policy package
> +
> +
> +case "$1" in
> + configure)
> +
> + # setup user/group x2gobroker
> + if ! getent group x2gobroker 1>/dev/null; then
> + echo "Creating x2gobroker group." 1>&2
> + addgroup --system x2gobroker
> + else
> + echo "Group x2gobroker already exists." 1>&2
> + fi
> + if ! getent passwd x2gobroker 1>/dev/null; then
> + echo "Creating x2gobroker user." 1>&2
> + adduser --system \
> + --disabled-password --disabled-login \
> + --shell /bin/bash --group --home /var/lib/x2gobroker x2gobroker
> + else
> + echo "User x2gobroker already exists." 1>&2
> +
> + # make sure the home directory exists belongs to x2gobroker:x2gobroker
> + mkdir -p /var/lib/x2gobroker
> + chown x2gobroker:x2gobroker /var/lib/x2gobroker -f
> +
> + # make sure all settings are appropriate
> + if getent passwd x2gobroker | grep /dev/null 1>/dev/null
> 2>/dev/null; then
> + usermod --home /var/lib/x2gobroker x2gobroker
> + fi
> + if getent passwd x2gobroker | grep /bin/false 1>/dev/null
> 2>/dev/null; then
> + usermod --shell /bin/bash x2gobroker
> + fi
> + fi
> +
> + # the x2gobroker-daemon needs special permissions on its log directory
> + if ! dpkg-statoverride --list /var/log/x2gobroker 1>/dev/null; then
> + dpkg-statoverride --add --update x2gobroker adm 2750 /var/log/x2gobroker
> + fi
> +
> + mkdir -p /var/log/x2gobroker && chown x2gobroker:adm
> /var/log/x2gobroker && chmod 2755 /var/log/x2gobroker
> + touch /var/log/x2gobroker/authservice.log && chown x2gobroker:adm
> /var/log/x2gobroker/authservice.log
> + touch /var/log/x2gobroker/error.log && chown x2gobroker:adm
> /var/log/x2gobroker/error.log
> + ;;
> +
> + abort-upgrade|abort-remove|abort-deconfigure)
> + ;;
> +
> + *)
> + echo "postinst called with unknown argument \`$1'" 1>&2
> + exit 1
> + ;;
> +esac
> +
> +# dh_installdeb will replace this with shell code automatically
> +# generated by other debhelper scripts.
> +
> +#DEBHELPER#
> +
> +exit 0
> diff --git a/debian/x2gobroker-authservice.postrm
> b/debian/x2gobroker-loadchecker.postrm
> similarity index 86%
> copy from debian/x2gobroker-authservice.postrm
> copy to debian/x2gobroker-loadchecker.postrm
> index 851bfa5..588f3a1 100755
> --- a/debian/x2gobroker-authservice.postrm
> +++ b/debian/x2gobroker-loadchecker.postrm
> @@ -1,5 +1,5 @@
> #! /bin/sh
> -# postrm script for x2gobroker-authservice
> +# postrm script for x2gobroker-loadchecker
> #
> # see: dh_installdeb(1)
> # summary of how this script can be called:
> @@ -19,15 +19,15 @@ set -e
> case "$1" in
> purge)
>
> - if [ ! -d /usr/share/doc/x2gobroker-daemon ] && [ ! -d
> /usr/share/doc/x2gobroker-wsgi ] && [ ! -d
> /usr/share/doc/x2gobroker-agent ] ; then
> + if [ ! -d /usr/share/doc/x2gobroker-daemon ] && [ ! -d
> /usr/share/doc/x2gobroker-wsgi ] && [ ! -d
> /usr/share/doc/x2gobroker-agent ] && [ ! -d
> /usr/share/doc/x2gobroker-authservice ]; then
> if dpkg-statoverride --list /var/log/x2gobroker 1>/dev/null; then
> dpkg-statoverride --remove /var/log/x2gobroker
> fi
> rm -Rf /var/log/x2gobroker
> fi
>
> - if [ ! -d /usr/share/doc/x2gobroker-daemon ] && [ ! -d
> /usr/share/doc/x2gobroker-agent ] && [ ! -d
> /usr/share/doc/x2gobroker-wsgi ]; then
> - # remove user/group x2gobroker from system (only if not in use
> by x2gobroker-daemon, x2gobroker-agent, x2gobroker-wsgi)
> + if [ ! -d /usr/share/doc/x2gobroker-daemon ] && [ ! -d
> /usr/share/doc/x2gobroker-agent ] && [ ! -d
> /usr/share/doc/x2gobroker-wsgi ] && [ ! -d
> /usr/share/doc/x2gobroker-authservice ]; then
> + # remove user/group x2gobroker from system (only if not in use
> by x2gobroker-daemon, x2gobroker-agent, x2gobroker-wsgi,
> x2gobroker-authservice)
> getent passwd x2gobroker 1>/dev/null && deluser x2gobroker
> getent group x2gobroker 1>/dev/null && delgroup x2gobroker
> getent group x2gobroker 1>/dev/null && delgroup x2gobroker
> diff --git a/debian/x2gobroker-loadchecker.service
> b/debian/x2gobroker-loadchecker.service
> new file mode 120000
> index 0000000..426059a
> --- /dev/null
> +++ b/debian/x2gobroker-loadchecker.service
> @@ -0,0 +1 @@
> +../x2gobroker-authservice.service
> \ No newline at end of file
> diff --git a/debian/x2gobroker-wsgi.postrm b/debian/x2gobroker-wsgi.postrm
> index 4c7c120..1299a7a 100755
> --- a/debian/x2gobroker-wsgi.postrm
> +++ b/debian/x2gobroker-wsgi.postrm
> @@ -35,15 +35,15 @@ case "$1" in
>
> apacheconf_remove
>
> - if [ ! -d /usr/share/doc/x2gobroker-agent ] && [ ! -d
> /usr/share/doc/x2gobroker-authservice ] && [ ! -d
> /usr/share/doc/x2gobroker-daemon ]; then
> + if [ ! -d /usr/share/doc/x2gobroker-agent ] && [ ! -d
> /usr/share/doc/x2gobroker-authservice ] && [ ! -d
> /usr/share/doc/x2gobroker-daemon ] && [ ! -d
> /usr/share/doc/x2gobroker-loadchecker ]; then
> if dpkg-statoverride --list /var/log/x2gobroker 1>/dev/null; then
> dpkg-statoverride --remove /var/log/x2gobroker
> fi
> rm -Rf /var/log/x2gobroker
> fi
>
> - if [ ! -d /usr/share/doc/x2gobroker-agent ] && [ ! -d
> /usr/share/doc/x2gobroker-authservice ] && [ ! -d
> /usr/share/doc/x2gobroker-daemon ]; then
> - # remove user/group x2gobroker from system (only if not in use
> by x2gobroker-agent, x2gobroker-authservice, x2gobroker-daemon)
> + if [ ! -d /usr/share/doc/x2gobroker-agent ] && [ ! -d
> /usr/share/doc/x2gobroker-authservice ] && [ ! -d
> /usr/share/doc/x2gobroker-daemon ] && [ ! -d
> /usr/share/doc/x2gobroker-loadchecker ]; then
> + # remove user/group x2gobroker from system (only if not in use
> by x2gobroker-agent, x2gobroker-authservice, x2gobroker-daemon,
> x2gobroker-loadchecker)
> getent passwd x2gobroker 1>/dev/null && deluser x2gobroker
> getent group x2gobroker 1>/dev/null && delgroup x2gobroker
> getent group x2gobroker 1>/dev/null && delgroup x2gobroker
> diff --git a/defaults/python-x2gobroker.default
> b/defaults/python-x2gobroker.default
> index 73ee9ac..d31c113 100644
> --- a/defaults/python-x2gobroker.default
> +++ b/defaults/python-x2gobroker.default
> @@ -37,3 +37,6 @@
>
> # The unix socket file for communication between the broker and the
> authentication service.
> #X2GOBROKER_AUTHSERVICE_SOCKET=/run/x2gobroker/x2gobroker-authservice.socket
> +
> +# The unix socket file for communication between the broker and the
> authentication service.
> +#X2GOBROKER_LOADCHECKER_SOCKET=/run/x2gobroker/x2gobroker-loadchecker.socket
> diff --git a/defaults/x2gobroker-loadchecker.default
> b/defaults/x2gobroker-loadchecker.default
> new file mode 100644
> index 0000000..b5bcd3e
> --- /dev/null
> +++ b/defaults/x2gobroker-loadchecker.default
> @@ -0,0 +1,20 @@
> +# X2Go Session Broker (Load Checker Service) configuration
> +# SystemV-like init systems
> +
> +# For PAM authentication the X2Go Session Broker needs its authentication
> +# service. The session broker itself runs as a non-privileged user
> (see below)
> +# whereas the authentication service must run as super-user root.
> +#
> +# If you do not use PAM as authentication mechanism with the X2Go
> Session Broker,
> +# you can disable the authentication service here.
> +START_LOADCHECKER=false
> +
> +# Control debug mode (0=disable, 1=enable) of the X2Go Broker Authentication
> +# Service.
> +#
> +# Logging is (by default) written to /var/log/x2gobroker/*log.
> +#
> +# This option can also be configured in /etc/default/python-x2go.
> +# The value configured here overrides the value from python-x2go
> +# defaults and only sets the x2gobroker-authservice into debug mode.
> +#X2GOBROKER_DEBUG=0
> diff --git a/etc/broker/defaults.conf b/etc/broker/defaults.conf
> index 01ca667..38dfc2d 100644
> --- a/etc/broker/defaults.conf
> +++ b/etc/broker/defaults.conf
> @@ -35,6 +35,9 @@
> # The unix socket file for communication between the broker and the
> authentication service.
> #X2GOBROKER_AUTHSERVICE_SOCKET=/run/x2gobroker/x2gobroker-authservice.socket
>
> +# The unix socket file for communication between the broker and the
> authentication service.
> +#X2GOBROKER_LOADCHECKER_SOCKET=/run/x2gobroker/x2gobroker-loadchecker.socket
> +
> [daemon]
> # X2Go Session Broker configuration for Debian
>
> diff --git a/etc/broker/x2gobroker-loadchecker-logger.conf
> b/etc/broker/x2gobroker-loadchecker-logger.conf
> new file mode 100644
> index 0000000..e18a143
> --- /dev/null
> +++ b/etc/broker/x2gobroker-loadchecker-logger.conf
> @@ -0,0 +1,51 @@
> +# This file is part of the X2Go Project - http://www.x2go.org
> +# Copyright (C) 2012-2015 by Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
> +#
> +# X2Go Session Broker is free software; you can redistribute it
> and/or modify
> +# it under the terms of the GNU Affero General Public License as
> published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# X2Go Session Broker is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU Affero General Public License for more details.
> +#
> +# You should have received a copy of the GNU Affero General Public License
> +# along with this program; if not, write to the
> +# Free Software Foundation, Inc.,
> +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
> +
> +# WARNING: only modify this file if you _exactly_ know what you are doing!!!
> +
> +[loggers]
> +keys=root,loadchecker
> +
> +[logger_root]
> +level=NOTSET
> +handlers=stderrHandler
> +
> +[handlers]
> +keys=stderrHandler,loadcheckerFileHandler
> +
> +[formatters]
> +keys=loadcheckerFormatter
> +
> +[handler_stderrHandler]
> +class=StreamHandler
> +args=(sys.stderr,)
> +
> +[logger_loadchecker]
> +level=DEBUG
> +handlers=loadcheckerFileHandler
> +qualname=loadchecker
> +propagate=0
> +
> +[handler_loadcheckerFileHandler]
> +class=FileHandler
> +formatter=loadcheckerFormatter
> +args=('/var/log/x2gobroker/loadchecker.log',)
> +
> +[formatter_loadcheckerFormatter]
> +format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
> +datefmt=
> diff --git a/etc/x2gobroker.conf b/etc/x2gobroker.conf
> index 1901673..8d57ae1 100644
> --- a/etc/x2gobroker.conf
> +++ b/etc/x2gobroker.conf
> @@ -247,6 +247,53 @@
> # false in the session profile configuration).
> #default-portscan-x2goservers = true
>
> +# Use load checker for querying X2Go Servers' loads in regular intervals
> +#
> +# When load-balancing shall be used, the simplest way to detect
> "server load"
> +# is counting the numbers of running and suspended sessions. No extra daemon
> +# nor service is required for this.
> +#
> +# However, simply counting running and suspended sessions per X2Go Server
> +# as a representative for the server load can be highly inaccurate. A better
> +# approach is checking each X2Go Server's load in regular intervals by a
> +# separate daemon (running on the broker host) and querying this
> load checker
> +# service before selecting the best server on session startup requests.
> +#
> +# The load factor calculation uses this algorithm:
> +#
> +# ( memAvail/1000 ) * numCPUs * typeCPUs
> +# load-factor = --------------------------------------
> +# loadavg*100 * numSessions
> +#
> +# (memAvail in MByte, typeCPUs in MHz)
> +#
> +# The higher the load-factor, the more likely that a server will be chosen
> +# for the next to be allocated X2Go session.
> +#
> +# If you set the default-use-load-checker option here, the queries to the
> +# x2gobroker-loadchecker daemon will be performed for all broker backends by
> +# defaults.
> +#
> +# The x2gobroker-loadchecker only gets consulted, if:
> +#
> +# o if enabled here for all backends
> +# o or if enabled on a per broker backend basis (see below)
> +#
> +# and
> +#
> +# o the session profile defines more than one host
> +# o the session profile does not block queries to the load checker daemon
> +# on a per profile basis
> +#
> +#default-use-load-checker = false
> +
> +# If the x2gobroker-loadchecker daemon gets used, define here how
> +# many seconds to sleep between querying system load from the
> +# associated X2Go Servers.
> +#
> +#load-checker-intervals = 300
> +
> +
> ###
> ### Auth Mechs section
> ###
> @@ -291,6 +338,7 @@
> [broker_inifile]
> #enable = true
> #session-profiles = /etc/x2go/broker/x2gobroker-sessionprofiles.conf
> +#use-load-checker = false
>
> #[broker_ldap] -> MUSIC OF THE FUTURE
> #enable = false
> @@ -304,4 +352,5 @@
> #group-search-filter = (&(objectClass=posifxGroup)(cn=*))
> #starttls = false
> #agent-query-mode = SSH
> +#use-load-checker = true
>
> diff --git a/init/x2gobroker-loadchecker.init
> b/init/x2gobroker-loadchecker.init
> new file mode 100755
> index 0000000..de8f2d0
> --- /dev/null
> +++ b/init/x2gobroker-loadchecker.init
> @@ -0,0 +1,104 @@
> +#!/bin/sh
> +#
> +# Start the X2Go Session Broker PAM Authentication Service
> +#
> +# Copyright © 2014 Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
> +# Distributable under the terms of the GNU AGPL version 3+.
> +#
> +### BEGIN INIT INFO
> +# Provides: x2gobroker-loadchecker
> +# Required-Start: $remote_fs $syslog
> +# Required-Stop: $remote_fs $syslog
> +# Default-Start: 2 3 4 5
> +# Default-Stop: 0 1 6
> +# Short-Description: X2Go Session Broker PAM Authentication Service
> +# Description: PAM authentication service for X2Go Session Broker
> +### END INIT INFO
> +#
> +
> +set -eu
> +
> +LOADCHECKER=/usr/sbin/x2gobroker-loadchecker
> +test -d /run && RUNDIR=/run || RUNDIR=/var/run
> +PIDFILE_LOADCHECKER=$RUNDIR/x2gobroker/x2gobroker-loadchecker.pid
> +DEBIANCONFIG_COMMON=/etc/default/python-x2gobroker
> +DEBIANCONFIG_LOADCHECKER=/etc/default/x2gobroker-loadchecker
> +
> +test -x "$LOADCHECKER" || exit 0
> +
> +START_LOADCHECKER=false
> +X2GOBROKER_DEBUG=0
> +X2GOBROKER_DAEMON_USER='x2gobroker'
> +X2GOBROKER_DAEMON_GROUP='x2gobroker'
> +X2GOBROKER_LOADCHECKER_SOCKET="$RUNDIR/x2gobroker/x2gobroker-loadchecker.socket"
> +test -f $DEBIANCONFIG_COMMON && . $DEBIANCONFIG_COMMON
> +test -f $DEBIANCONFIG_LOADCHECKER && . $DEBIANCONFIG_LOADCHECKER
> +
> +if ! getent passwd $X2GOBROKER_DAEMON_USER 1>/dev/null 2>/dev/null; then
> + X2GOBROKER_DAEMON_USER=nobody
> +fi
> +if ! getent group $X2GOBROKER_DAEMON_GROUP 1>/dev/null 2>/dev/null; then
> + X2GOBROKER_DAEMON_GROUP=nogroup
> +fi
> +
> +# create PID directory
> +mkdir -p $RUNDIR/x2gobroker
> +chown $X2GOBROKER_DAEMON_USER:$X2GOBROKER_DAEMON_GROUP $RUNDIR/x2gobroker
> +chmod 0770 $RUNDIR/x2gobroker
> +
> +export X2GOBROKER_DEBUG
> +export X2GOBROKER_DAEMON_USER
> +export X2GOBROKER_DAEMON_GROUP
> +export X2GOBROKER_LOADCHECKER_SOCKET
> +
> +. /lib/lsb/init-functions
> +
> +is_true()
> +{
> + case "${1:-}" in
> + [Yy]es|[Yy]|1|[Tt]|[Tt]rue) return 0;;
> + *) return 1;
> + esac
> +}
> +
> +case "${1:-}" in
> + start)
> + if [ -f $PIDFILE_LOADCHECKER ]; then
> + if ps a -u root | grep -v egrep | egrep ".*$(basename
> $LOADCHECKER).*$X2GOBROKER_LOADCHECKER_SOCKET.*" 1>/dev/null
> 2>/dev/null; then
> + log_warning_msg "X2Go Broker Authentication Service already running"
> + else
> + log_warning_msg "X2Go Broker Authentication Service: stale PID
> file ($PIDFILE_LOADCHECKER). Delete it manually!"
> + fi
> + START_LOADCHECKER=no
> + fi
> + if is_true $START_LOADCHECKER; then
> + set +e
> + # once we are here, we have to make sure the loadchecker.socket
> does not exist
> + rm -f $X2GOBROKER_LOADCHECKER_SOCKET
> + # and now we can start the auth service
> + log_daemon_msg "Starting X2Go Broker Authentication Service"
> "$(basename $LOADCHECKER)"
> + start-stop-daemon -b -m -S -p $PIDFILE_LOADCHECKER -x
> $LOADCHECKER -- -s $X2GOBROKER_LOADCHECKER_SOCKET -o
> $X2GOBROKER_DAEMON_USER -g $X2GOBROKER_DAEMON_GROUP -p 0660
> + log_end_msg $?
> + set -e
> + fi
> + ;;
> + stop)
> + if [ -f $PIDFILE_LOADCHECKER ] ; then
> + log_daemon_msg "Stopping X2Go Broker Authentication Service"
> "$(basename $LOADCHECKER)"
> + set +e
> + start-stop-daemon -K -p $PIDFILE_LOADCHECKER && rm -f
> $PIDFILE_LOADCHECKER
> + log_end_msg $?
> + set -e
> + fi
> + ;;
> + restart|reload|force-reload)
> + ${0:-} stop
> + ${0:-} start
> + ;;
> + *)
> + echo "Usage: ${0:-} {start|stop|restart|reload|force-reload}" >&2
> + exit 1
> + ;;
> +esac
> +
> +exit 0
> diff --git a/lib/x2gobroker-agent.pl b/lib/x2gobroker-agent.pl
> index 242f78a..9463018 100755
> --- a/lib/x2gobroker-agent.pl
> +++ b/lib/x2gobroker-agent.pl
> @@ -36,6 +36,7 @@ if ( system("x2goversion x2goserver 1>/dev/null") == 0 )
> "listsessions",
> "findbusyservers",
> "findbusyservers_by_sessionstats",
> + "checkload",
> "getservers",
> "suspendsession",
> "terminatesession",
> @@ -133,6 +134,54 @@ if($mode eq 'ping')
> exit;
> }
>
> +if ( $mode eq 'checkload' ) {
> + print "OK\n";
> +
> + # Read the values from /proc/loadavg and combine all three values
> in a linear
> + # way and make a percent value out of the result:
> + open FILE, "< /proc/loadavg" or die return ("Cannot open
> /proc/loadavg: $!");
> + my ($avg1, $avg5, $avg15, undef, undef) = split / /, <FILE>;
> + close FILE;
> + my $loadavgXX = ( $avg1 + $avg5 + $avg15 ) * 100/3;
> + if ( $loadavgXX == 0 ) {
> + # load may not be ZERO
> + $loadavgXX = 1;
> + }
> +
> + # calculate total memory vs. free memory
> + my $memAvail;
> + open FILE, "< /proc/meminfo" or die return ("Cannot open
> /proc/meminfo: $!");
> + foreach(<FILE>) {
> + if ( m/^MemAvailable:\s+(\S+)/ ) {
> + $memAvail = $1/1000;
> + last;
> + }
> + }
> + close(FILE);
> +
> + # check number and type of available CPU cores
> + my $numCPU = 0;
> + my $typeCPU;
> + open FILE, "< /proc/cpuinfo" or die return ("Cannot open
> /proc/cpuinfo: $!");
> + foreach(<FILE>) {
> + if ( m/model name.*CPU\ \@\ ([\d\.]+)/ ) {
> + $typeCPU = $1*1000;
> + $numCPU += 1;
> + }
> + }
> + close(FILE);
> +
> + print sprintf 'loadavgXX:%1$d', $loadavgXX;
> + print "\n";
> + print sprintf 'memAvail:%1$d', $memAvail;
> + print "\n";
> + print sprintf 'numCPU:%1$d', $numCPU;
> + print "\n";
> + print sprintf 'typeCPU:%1$d', $typeCPU;
> + print "\n";
> + exit;
> +}
> +
> if($mode eq 'availabletasks')
> {
> print "OK\n";
> diff --git a/logrotate/x2gobroker-loadchecker
> b/logrotate/x2gobroker-loadchecker
> new file mode 100644
> index 0000000..e5c8c65
> --- /dev/null
> +++ b/logrotate/x2gobroker-loadchecker
> @@ -0,0 +1,14 @@
> +/var/log/x2gobroker/loadchecker.log {
> + weekly
> + missingok
> + rotate 52
> + compress
> + delaycompress
> + notifempty
> + create 640 root adm
> + su root adm
> + sharedscripts
> + postrotate
> + invoke-rc.d x2gobroker-loadchecker restart > /dev/null
> + endscript
> +}
> diff --git a/man/man8/x2gobroker-authservice.8
> b/man/man8/x2gobroker-authservice.8
> index eff8569..8981ee6 100644
> --- a/man/man8/x2gobroker-authservice.8
> +++ b/man/man8/x2gobroker-authservice.8
> @@ -31,7 +31,7 @@ Thus, the PAM authentication has been moved into a
> separate service. The communi
> between X2Go Session Broker and PAM Authentication Service is
> handled through a
> unix domain socket file
> (\fI<RUNDIR>/x2gobroker/x2gobroker-authservice.socket\fR).
> .PP
> -This command is normally started through an init script.
> +This command is normally started through the host's init system.
> .SH COMMON OPTIONS
> \fBx2gobroker-authservice\fR accepts the following common options:
> .TP
> diff --git a/man/man8/x2gobroker-loadchecker.8
> b/man/man8/x2gobroker-loadchecker.8
> new file mode 100644
> index 0000000..f025f5e
> --- /dev/null
> +++ b/man/man8/x2gobroker-loadchecker.8
> @@ -0,0 +1,93 @@
> +'\" -*- coding: utf-8 -*-
> +.if \n(.g .ds T< \\FC
> +.if \n(.g .ds T> \\F[\n[.fam]]
> +.de URL
> +\\$2 \(la\\$1\(ra\\$3
> +..
> +.if \n(.g .mso www.tmac
> +.TH x2gobroker-loadchecker 8 "Mar 2015" "Version 0.0.3.x" "X2Go
> Session Broker"
> +.SH NAME
> +x2gobroker-loadchecker \- Load checker service for X2Go Session Broker
> +.SH SYNOPSIS
> +'nh
> +.fi
> +.ad l
> +\fBx2gobroker-loadchecker\fR \kx
> +.if (\nx>(\n(.l/2)) .nr x (\n(.l/5)
> +'in \n(.iu+\nxu
> +[
> +\fIoptions\fR
> +]
> +'in \n(.iu-\nxu
> +.ad b
> +'hy
> +.SH DESCRIPTION
> +\fBx2gobroker-loadchecker\fR is a service that collects system load
> metrics from
> +broker-associated X2Go Servers.
> +.PP
> +When load-balancing shall be used, the simplest way to detect "server load"
> +is counting the numbers of running and suspended sessions. No extra daemon
> +nor service is required for this. The server with the least amount
> of sessions
> +will be selected for starting the next X2Go session.
> +.PP
> +However, simply counting running and suspended sessions per X2Go Server
> +as a representative for the server load can be highly inaccurate. A better
> +approach is checking each X2Go Server's load in regular intervals by the
> +\fBx2gobroker-loadchecker\fR daemon (running on the broker host)
> and querying
> +the \fBx2gobroker-loadchecker\fR daemon before selecting the best server on
> +session startup requests.
> +.PP
> +The \fBx2gobroker-loadchecker\fR collects server metrics of all
> +associated X2Go Servers and keeps the latest load factors in RAM. Once
> +the broker needs load factors of a certain session profile (i.e., of all
> +servers configured in that session profile), the
> +\fBx2gobroker-loadchecker\fR delivers that info immediately. The
> +remembered load factors may not be 100% up-to-date (default: collected
> +within the last five minutes), but the response time of the load checker
> +is much faster than a query to all possible X2Go Servers would be.
> +.PP
> +The load factor calculation uses this algorithm:
> +.PP
> + ( memAvail[MByte]/1000 ) * numCPUs * typeCPUs[MHz]
> + load-factor = --------------------------------------------------
> + loadavg*100 * numSessions
> +.PP
> +The higher the load-factor, the more likely that a server will be chosen
> +for the next to be allocated X2Go session.
> +.PP
> +The communication
> +between X2Go Session Broker and the Load Checker Service is handled
> through a
> +unix domain socket file
> (\fI<RUNDIR>/x2gobroker/x2gobroker-loadchecker.socket\fR).
> +.PP
> +This command is normally started through the host's init system.
> +.SH COMMON OPTIONS
> +\fBx2gobroker-loadchecker\fR accepts the following common options:
> +.TP
> +\*(T<\fB\-h, \-\-help\fR\*(T>
> +Display a help with all available command line options and exit.
> +.TP
> +\*(T<\fB\-D, \-\-daemonize\fR\*(T>
> +Fork this application to background and detach from the running terminal.
> +.TP
> +\*(T<\fB\-P, \-\-pidfile\fR\*(T>
> +Custom PID file location when daemonizing (default:
> \fI<RUNDIR>/x2gobroker/x2gobroker-loadchecker.pid\fR).
> +.TP
> +\*(T<\fB\-L, \-\-logdir\fR\*(T>
> +Directory where stdout/stderr will be redirected after having
> daemonized (default: \fI/var/log/x2gobroker/\fR).
> +.TP
> +\*(T<\fB\-s <LOADCHECKERSOCKET>, \-\-socket <LOADCHECKERSOCKET>\fR\*(T>
> +File name of the unix domain socket file used for communication
> between broker and load checker service.
> +.TP
> +\*(T<\fB\-o <OWNER>, \-\-owner <OWNER>\fR\*(T>
> +User ownership of the \fI<LOADCHECKERSOCKET>\fR file.
> +.TP
> +\*(T<\fB\-g <GROUP>, \-\-group <GROUP>\fR\*(T>
> +Group ownership of the \fI<LOADCHECKERSOCKET>\fR file.
> +.TP
> +\*(T<\fB\-p <PERMISSIONS>, \-\-permissions <PERMISSIONS>\fR\*(T>
> +Set these file permissions for the \fI<LOADCHECKERSOCKET>\fR file.
> Use numerical permissions (e.g. 0640).
> +.SH "FILES"
> +<RUNDIR>/x2gobroker/x2gobroker-loadchecker.socket
> +.SH AUTHOR
> +This manual has been written for the X2Go project by
> +Mike Gabriel <mike.gabriel at das-netzwerkteam.de>.
> diff --git a/sbin/x2gobroker-loadchecker b/sbin/x2gobroker-loadchecker
> new file mode 100755
> index 0000000..8d6ae32
> --- /dev/null
> +++ b/sbin/x2gobroker-loadchecker
> @@ -0,0 +1,319 @@
> +#!/usr/bin/env python
> +# -*- coding: utf-8 -*-
> +
> +# This file is part of the X2Go Project - http://www.x2go.org
> +# Copyright (C) 2012-2015 by Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
> +#
> +# X2Go Session Broker is free software; you can redistribute it
> and/or modify
> +# it under the terms of the GNU Affero General Public License as
> published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# X2Go Session Broker is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU Affero General Public License for more details.
> +#
> +# You should have received a copy of the GNU Affero General Public License
> +# along with this program; if not, write to the
> +# Free Software Foundation, Inc.,
> +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
> +
> +import os
> +import sys
> +import setproctitle
> +import argparse
> +import logging
> +import asyncore
> +import socket
> +import getpass
> +import logging.config
> +import atexit
> +import ConfigParser
> +
> +if os.path.isdir('/run'):
> + RUNDIR = '/run'
> +else:
> + RUNDIR = '/var/run'
> +
> +try:
> + import daemon
> + import lockfile
> + CAN_DAEMONIZE = True
> + pidfile =
> '{run}/x2gobroker/x2gobroker-loadchecker.pid'.format(run=RUNDIR)
> + daemon_logdir = '/var/log/x2gobroker/'
> +except ImportError:
> + CAN_DAEMONIZE = False
> +
> +from pwd import getpwnam
> +from grp import getgrnam
> +
> +PROG_NAME = os.path.basename(sys.argv[0])
> +PROG_OPTIONS = sys.argv[1:]
> +setproctitle.setproctitle("%s %s" % (PROG_NAME, " ".join(PROG_OPTIONS)))
> +
> +from x2gobroker import __VERSION__
> +from x2gobroker import __AUTHOR__
> +import x2gobroker.loadchecker
> +
> +global load_checker
> +
> +class LoadCheckerServiceHandler(asyncore.dispatcher_with_send):
> +
> + def __init__(self, sock, logger=None):
> + self.logger = logger
> + asyncore.dispatcher_with_send.__init__(self, sock)
> + self._buf = ''
> +
> + def handle_read(self):
> + data = self._buf + self.recv(1024)
> + if not data:
> + self.close()
> + return
> + reqs, data = data.rsplit('\n', 1)
> + self._buf = data
> + output = ""
> + for req in reqs.split('\n'):
> + backend, profile_id, hostname = req.split('\r')
> + if self.logger:
> self.logger.debug('LoadCheckServiceHandler.handle_read(): received
> load check query: backend={backend}, profile_id={profile_id},
> hostname={hostname}'.format(backend=backend, profile_id=profile_id,
> hostname=hostname))
> + if hostname:
> + load_factor = load_checker.get_server_load(backend,
> profile_id, hostname)
> + if load_factor is not None:
> + output += "{lf}".format(lf=load_factor)
> + if self.logger:
> self.logger.info('LoadCheckServiceHandler.handle_read(): load check
> result for backend={backend}, profile_id={profile_id},
> hostname={hostname}: {lf}'.format(backend=backend,
> profile_id=profile_id, hostname=hostname, lf=load_factor))
> + else:
> + output += "LOAD-UNAVAILABLE"
> + if self.logger:
> self.logger.warning('LoadCheckServiceHandler.handle_read(): load
> check failure for backend={backend}, profile_id={profile_id},
> hostname={hostname}: LOAD-UNAVAILABLE'.format(backend=backend,
> profile_id=profile_id, hostname=hostname))
> + else:
> + load_factors =
> load_checker.get_profile_load(backend, profile_id)
> + if load_factors:
> + for h in load_factors.keys():
> + if load_factors[h] is not None:
> + output
> +="{hostname}:{loadfactor}".format(hostname=h,
> loadfactor=load_factors[h])
> + if self.logger:
> self.logger.info('LoadCheckServiceHandler.handle_read(): load check
> result for backend={backend}, profile_id={profile_id},
> hostname={hostname}: {lf}'.format(backend=backend,
> profile_id=profile_id, hostname=h, lf=load_factors[h]))
> + else:
> + output +=
> "{hostname}:LOAD-UNAVAILABLE".formate(hostname=h)
> + if self.logger:
> self.logger.warning('LoadCheckServiceHandler.handle_read(): load
> check failure for backend={backend}, profile_id={profile_id},
> hostname={hostname}: LOAD-UNAVAILABLE'.format(backend=backend,
> profile_id=profile_id, hostname=h))
> + else:
> + output += "LOAD-UNAVAILABLE"
> + if self.logger:
> self.logger.warning('LoadCheckServiceHandler.handle_read(): load
> check failure for backend={backend}, profile_id={profile_id}:
> LOAD-UNAVAILABLE'.format(backend=backend, profile_id=profile_id))
> + self.send(output)
> +
> + def handle_close(self):
> + self.close()
> +
> +
> +class LoadCheckerService(asyncore.dispatcher_with_send):
> +
> + def __init__(self, socketfile, owner='root',
> group_owner='root', permissions='0660', logger=None):
> + self.logger = logger
> + asyncore.dispatcher_with_send.__init__(self)
> + self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM)
> + self.set_reuse_addr()
> + self.bind(socketfile)
> + os.chown(socketfile, getpwnam(owner).pw_uid,
> getgrnam(group_owner).gr_gid)
> + os.chmod(socketfile, int(permissions, 8))
> + self.listen(1)
> +
> + def handle_accept(self):
> + conn, _ = self.accept()
> + LoadCheckerServiceHandler(conn, logger=self.logger)
> +
> +
> +def loop():
> + ### the "loop" has two tasks...
> +
> + # 1. Do regular queries to remote X2Go Broker Agent instances to collect
> + # load average, CPU usage and type, memory usage, etc.
> + load_checker.start()
> +
> + # 2. Provide a listening UNIX domain socket file that can be
> used for querying
> + # server states.
> + asyncore.loop()
> +
> +
> +def cleanup_on_exit():
> + os.remove(X2GOBROKER_LOADCHECKER_SOCKET)
> + try: os.remove(pidfile)
> + except: pass
> +
> +
> +# load the defaults.conf file, if present
> +iniconfig_loaded = None
> +iniconfig_section = '-'.join(PROG_NAME.split('-')[1:])
> +X2GOBROKER_DEFAULTS = "/etc/x2go/broker/defaults.conf"
> +if os.path.isfile(X2GOBROKER_DEFAULTS) and
> os.access(X2GOBROKER_DEFAULTS, os.R_OK):
> + iniconfig = ConfigParser.SafeConfigParser()
> + iniconfig.optionxform = str
> + iniconfig_loaded = iniconfig.read(X2GOBROKER_DEFAULTS)
> +
> +# normally this would go into defaults.py, however, we do not want
> to pull in defaults.py here as that will create
> +# unwanted logfiles (access.log, broker.log, error.log) when
> x2gobroker-loadchecker is installed as standalone service
> +if os.environ.has_key('X2GOBROKER_DEBUG'):
> + X2GOBROKER_DEBUG = ( os.environ['X2GOBROKER_DEBUG'].lower() in
> ('1', 'on', 'true', 'yes', ) )
> +elif iniconfig_loaded and iniconfig.has_option(iniconfig_section,
> 'X2GOBROKER_DEBUG'):
> + X2GOBROKER_DEBUG=iniconfig.get(iniconfig_section, 'X2GOBROKER_DEBUG')
> +elif iniconfig_loaded and iniconfig.has_option('common',
> 'X2GOBROKER_DEBUG'):
> + X2GOBROKER_DEBUG=iniconfig.get('common', 'X2GOBROKER_DEBUG')
> +else:
> + X2GOBROKER_DEBUG = False
> +
> +if os.environ.has_key('X2GOBROKER_DAEMON_USER'):
> + X2GOBROKER_DAEMON_USER=os.environ['X2GOBROKER_DAEMON_USER']
> +elif iniconfig_loaded and iniconfig.has_option(iniconfig_section,
> 'X2GOBROKER_DAEMON_USER'):
> + X2GOBROKER_DAEMON_USER=iniconfig.get(iniconfig_section,
> 'X2GOBROKER_DAEMON_USER')
> +elif iniconfig_loaded and iniconfig.has_option('common',
> 'X2GOBROKER_DAEMON_USER'):
> + X2GOBROKER_DAEMON_USER=iniconfig.get('common', 'X2GOBROKER_DAEMON_USER')
> +else:
> + X2GOBROKER_DAEMON_USER="x2gobroker"
> +
> +if os.environ.has_key('X2GOBROKER_LOADCHECKER_LOGCONFIG'):
> +
> X2GOBROKER_LOADCHECKER_LOGCONFIG=os.environ['X2GOBROKER_LOADCHECKER_LOGCONFIG']
> +elif iniconfig_loaded and iniconfig.has_option(iniconfig_section,
> 'X2GOBROKER_LOADCHECKER_LOGCONFIG'):
> +
> X2GOBROKER_LOADCHECKER_LOGCONFIG=iniconfig.get(iniconfig_section,
> 'X2GOBROKER_LOADCHECKER_LOGCONFIG')
> +elif iniconfig_loaded and iniconfig.has_option('common',
> 'X2GOBROKER_LOADCHECKER_LOGCONFIG'):
> + X2GOBROKER_LOADCHECKER_LOGCONFIG=iniconfig.get('common',
> 'X2GOBROKER_LOADCHECKER_LOGCONFIG')
> +else:
> +
> X2GOBROKER_LOADCHECKER_LOGCONFIG="/etc/x2go/broker/x2gobroker-loadchecker-logger.conf"
> +
> +if os.environ.has_key('X2GOBROKER_LOADCHECKER_SOCKET'):
> +
> X2GOBROKER_LOADCHECKER_SOCKET=os.environ['X2GOBROKER_LOADCHECKER_SOCKET']
> +elif iniconfig_loaded and iniconfig.has_option(iniconfig_section,
> 'X2GOBROKER_LOADCHECKER_SOCKET'):
> + X2GOBROKER_LOADCHECKER_SOCKET=iniconfig.get(iniconfig_section,
> 'X2GOBROKER_LOADCHECKER_SOCKET')
> +elif iniconfig_loaded and iniconfig.has_option('common',
> 'X2GOBROKER_LOADCHECKER_SOCKET'):
> + X2GOBROKER_LOADCHECKER_SOCKET=iniconfig.get('common',
> 'X2GOBROKER_LOADCHECKER_SOCKET')
> +else:
> +
> X2GOBROKER_LOADCHECKER_SOCKET="{run}/x2gobroker/x2gobroker-loadchecker.socket".format(run=RUNDIR)
> +
> +
> +if __name__ == '__main__':
> +
> + common_options = [
> + {'args':['-s','--socket-file'], 'default':
> X2GOBROKER_LOADCHECKER_SOCKET, 'metavar': 'LOADCHECKERSOCKET',
> 'help': 'socket file for LoadChecker communication', },
> + {'args':['-o','--owner'], 'default': 'root', 'help': 'owner
> of the LoadChecker socket file', },
> + {'args':['-g','--group'], 'default': 'root', 'help': 'group
> ownership of the LoadChecker socket file', },
> + {'args':['-p','--permissions'], 'default': '0660', 'help':
> 'set these file permissions for the LoadChecker socket file', },
> + {'args':['-d','--debug'], 'default': False, 'action':
> 'store_true', 'help': 'enable debugging code', },
> + {'args':['-i','--debug-interactively'], 'default': False,
> 'action': 'store_true', 'help': 'force output of log message to the
> stderr (rather than to the log files)', },
> +
> + ]
> + if CAN_DAEMONIZE:
> + common_options.extend([
> + {'args':['-D', '--daemonize'], 'default': False,
> 'action': 'store_true', 'help': 'Detach the X2Go Broker process from
> the current terminal and fork to background', },
> + {'args':['-P', '--pidfile'], 'default': pidfile,
> 'help': 'Alternative file path for the daemon\'s PID file', },
> + {'args':['-L', '--logdir'], 'default': daemon_logdir,
> 'help': 'Directory where log files for the process\'s stdout and
> stderr can be created', },
> + ])
> + p = argparse.ArgumentParser(description='X2Go Session Broker
> (Load Checker Service)',\
> +
> formatter_class=argparse.RawDescriptionHelpFormatter, \
> + add_help=True, argument_default=None)
> + p_common = p.add_argument_group('common parameters')
> +
> + for (p_group, opts) in ( (p_common, common_options), ):
> + for opt in opts:
> + args = opt['args']
> + del opt['args']
> + p_group.add_argument(*args, **opt)
> +
> + cmdline_args = p.parse_args()
> +
> + # standalone daemon mode (x2gobroker-loadchecker as daemon) or
> interactive mode (called from the cmdline)?
> + if getpass.getuser() in (X2GOBROKER_DAEMON_USER, 'root') and
> not cmdline_args.debug_interactively:
> +
> + # we run in standalone daemon mode, so let's use the system
> configuration for logging
> + logging.config.fileConfig(X2GOBROKER_LOADCHECKER_LOGCONFIG)
> +
> + # create loadchecker logger
> + logger_loadchecker = logging.getLogger('loadchecker')
> +
> + else:
> + logger_root = logging.getLogger()
> + stderr_handler = logging.StreamHandler(sys.stderr)
> +
> stderr_handler.setFormatter(logging.Formatter(fmt='%(asctime)s -
> %(name)s - %(levelname)s - %(message)s', datefmt=''))
> +
> + # all loggers stream to stderr...
> + logger_root.addHandler(stderr_handler)
> +
> + logger_loadchecker = logging.getLogger('loadchecker')
> + logger_loadchecker.addHandler(stderr_handler)
> + logger_loadchecker.propagate = 0
> +
> + if cmdline_args.debug_interactively:
> + cmdline_args.debug = True
> +
> + # raise log level to DEBUG if requested...
> + if cmdline_args.debug or X2GOBROKER_DEBUG:
> + X2GOBROKER_DEBUG = True
> + logger_loadchecker.setLevel(logging.DEBUG)
> +
> + logger_loadchecker.info('X2Go Session Broker ({version}),
> written by {author}'.format(version=__VERSION__, author=__AUTHOR__))
> + logger_loadchecker.info('Setting up the Load Checker service\'s
> environment...')
> + logger_loadchecker.info(' X2GOBROKER_DEBUG:
> {value}'.format(value=X2GOBROKER_DEBUG))
> + logger_loadchecker.info(' X2GOBROKER_LOADCHECKER_SOCKET:
> {value}'.format(value=X2GOBROKER_LOADCHECKER_SOCKET))
> +
> + load_checker =
> x2gobroker.loadchecker.LoadChecker(logger=logger_loadchecker)
> +
> + if CAN_DAEMONIZE and cmdline_args.daemonize:
> +
> + # create directory for the PID file
> + pidfile = os.path.expanduser(cmdline_args.pidfile)
> + if not os.path.isdir(os.path.dirname(pidfile)):
> + try:
> + os.makedirs(os.path.dirname(pidfile))
> + except:
> + pass
> + if not os.access(os.path.dirname(pidfile), os.W_OK) or
> (os.path.exists(pidfile) and not os.access(pidfile, os.W_OK)):
> + print("")
> + p.print_usage()
> + print("Insufficent privileges. Cannot create PID file
> {pidfile} path".format(pidfile=pidfile))
> + print("")
> + sys.exit(-3)
> +
> + # create directory for logging
> + daemon_logdir = os.path.expanduser(cmdline_args.logdir)
> + if not os.path.isdir(daemon_logdir):
> + try:
> + os.makedirs(daemon_logdir)
> + except:
> + pass
> + if not os.access(daemon_logdir, os.W_OK):
> + print("")
> + p.print_usage()
> + print("Insufficent privileges. Cannot create directory
> for stdout/stderr log files: {logdir}".format(logdir=daemon_logdir))
> + print("")
> + sys.exit(-3)
> + else:
> + if not daemon_logdir.endswith('/'):
> + daemon_logdir += '/'
> +
> + socket_file = cmdline_args.socket_file
> +
> + if os.path.exists(socket_file):
> + os.remove(socket_file)
> +
> + if not os.path.exists(os.path.dirname(socket_file)):
> + os.makedirs(os.path.dirname(socket_file))
> +
> + runtimedir_permissions = int(cmdline_args.permissions, 8)
> + if runtimedir_permissions & 0400: runtimedir_permissions =
> runtimedir_permissions | 0100
> + if runtimedir_permissions & 0040: runtimedir_permissions =
> runtimedir_permissions | 0010
> + if runtimedir_permissions & 0004: runtimedir_permissions =
> runtimedir_permissions | 0001
> + try:
> + os.chown(os.path.dirname(socket_file),
> getpwnam(cmdline_args.owner).pw_uid,
> getpwnam(cmdline_args.group).pw_gid)
> + os.chmod(os.path.dirname(socket_file), runtimedir_permissions)
> + except OSError:
> + pass
> +
> + LoadCheckerService(socket_file, owner=cmdline_args.owner,
> group_owner=cmdline_args.group,
> permissions=cmdline_args.permissions, logger=logger_loadchecker)
> + atexit.register(cleanup_on_exit)
> + try:
> + if CAN_DAEMONIZE and cmdline_args.daemonize:
> + keep_fds = [int(fd) for fd in
> os.listdir('/proc/self/fd') if fd not in (0,1,2) ]
> + daemon_stdout =
> file(daemon_logdir+'x2gobroker-loadchecker.stdout', 'w+')
> + daemon_stderr =
> file(daemon_logdir+'x2gobroker-loadchecker.stderr', 'w+')
> + with daemon.DaemonContext(stdout=daemon_stdout,
> stderr=daemon_stderr, files_preserve=keep_fds, umask=0o027,
> pidfile=lockfile.FileLock(pidfile), detach_process=True):
> + file(pidfile, 'w+').write(str(os.getpid())+"\n")
> + loop()
> + else:
> + loop()
> + except KeyboardInterrupt:
> + pass
> diff --git a/x2gobroker-loadchecker.service b/x2gobroker-loadchecker.service
> new file mode 100644
> index 0000000..692e15f
> --- /dev/null
> +++ b/x2gobroker-loadchecker.service
> @@ -0,0 +1,12 @@
> +[Unit]
> +Description=X2Go Session Broker Load Checker Service
> +
> +[Service]
> +User=root
> +Group=root
> +Type=forking
> +ExecStart=/usr/sbin/x2gobroker-loadchecker
> --socket-file=/run/x2gobroker/x2gobroker-loadchecker.socket
> --daemonize -o root -g x2gobroker -p 0660
> --pidfile=/run/x2gobroker/x2gobroker-loadchecker.pid
> +PIDFile=/run/x2gobroker/x2gobroker-loadchecker.pid
> +
> +[Install]
> +WantedBy=multi-user.target
> diff --git a/x2gobroker.spec b/x2gobroker.spec
> index bcb398f..55b1fdb 100644
> --- a/x2gobroker.spec
> +++ b/x2gobroker.spec
> @@ -14,6 +14,7 @@ Url: http://www.x2go.org/
> Source0:
> http://code.x2go.org/releases/source/%name/%name-%version.tar.gz
> Source1: x2gobroker-daemon.init
> Source2: x2gobroker-authservice.init
> +Source2: x2gobroker-loadchecker.init
> Source3: x2gobroker-rpmlintrc
>
> %if 0%{?el5}
> @@ -147,6 +148,46 @@ A session broker is most useful in load
> balanced X2Go server farms.
>
> This package contains the authentication service against the PAM system.
>
> +%package loadchecker
> +Summary: X2Go Session Broker (load checker service)
> +%if 0%{?suse_version}
> +Group: Productivity/Networking/Remote Desktop
> +%else
> +Group: Applications/Communications
> +%endif
> +%if 0%{?suse_version}
> +Requires: python
> +%else
> +Requires: python2
> +%endif
> +Requires: python-argparse
> +Requires: python-setproctitle
> +Requires: logrotate
> +Requires(pre): python-x2gobroker = %{version}-%{release}
> +%if 0%{?suse_version}
> +Requires(pre): permissions
> +%endif
> +
> +%description loadchecker
> +X2Go is a server based computing environment with
> + - session resuming
> + - low bandwidth support
> + - session brokerage support
> + - client side mass storage mounting support
> + - client side printing support
> + - audio support
> + - authentication by smartcard and USB stick
> +
> +The session broker is a server tool for X2Go that tells your X2Go Client
> +application in a terminal server cluster what servers and session types are
> +most appropriate for the user in front of the X2Go terminal.
> +
> +A session broker is most useful in load balanced X2Go server farms.
> +
> +This package contains the load checker service required for broker setups
> +with dynamic load balancing.
> +
> +
> %package daemon
> Summary: X2Go Session Broker (standalone daemon)
> %if 0%{?suse_version}
> @@ -346,6 +387,8 @@ grep -l -r -E '^#!/usr/bin/env python$' | while
> read file; do \
> %endif
> sed -i logrotate/x2gobroker-authservice \
> -e 's/adm/root/'
> +sed -i logrotate/x2gobroker-loadchecker \
> + -e 's/adm/root/'
> sed -i logrotate/x2gobroker-daemon \
> -e 's/adm/root/'
> sed -i logrotate/x2gobroker-wsgi \
> @@ -379,14 +422,18 @@ ln -s
> "%_sysconfdir/x2go/x2gobroker-wsgi.apache.vhost" \
> mkdir -p %{buildroot}%{_unitdir}
> install -pm0644 x2gobroker-daemon.service %{buildroot}%{_unitdir}
> install -pm0644 x2gobroker-authservice.service %{buildroot}%{_unitdir}
> +install -pm0644 x2gobroker-loadchecker.service %{buildroot}%{_unitdir}
> rm -f %{buildroot}%{_sysconfdir}/default/x2gobroker-daemon
> rm -f %{buildroot}%{_sysconfdir}/default/x2gobroker-authservice
> +rm -f %{buildroot}%{_sysconfdir}/default/x2gobroker-loadchecker
> rm -f %{buildroot}%{_sysconfdir}/default/python-x2gobroker
> %else
> # SysV session cleanup script
> %if 0%{?el5}
> rm -f %{buildroot}%{_sysconfdir}/x2go/broker/defaults.conf
> mkdir -p %{buildroot}%{_initrddir}
> +install -pm0755 %SOURCE3 \
> + "$b/%_initrddir/x2gobroker-loadchecker"
> install -pm0755 %SOURCE2 \
> "$b/%_initrddir/x2gobroker-authservice"
> install -pm0755 %SOURCE1 \
> @@ -395,6 +442,8 @@ install -pm0755 %SOURCE1 \
> %if 0%{?el6} || ( 0%{?suse_version} && 0%{?suse_version} < 1140)
> rm -f %{buildroot}%{_sysconfdir}/x2go/broker/defaults.conf
> mkdir -p "$b/%_initddir"
> +install -pm0755 %SOURCE3 \
> + "$b/%_initddir/x2gobroker-loadchecker"
> install -pm0755 %SOURCE2 \
> "$b/%_initddir/x2gobroker-authservice"
> install -pm0755 %SOURCE1 \
> @@ -480,6 +529,56 @@ fi
>
>
> %if 0%{?suse_version} >= 1230
> +%pre loadchecker
> +%service_add_pre x2gobroker-loadchecker.service
> +%endif
> +
> +%post loadchecker
> +%if 0%{?fedora} || 0%{?el7} || 0%{?suse_version} >= 1230
> +%if 0%{?suse_version}
> +%service_add_post x2gobroker-loadchecker.service
> +%else
> +%systemd_post x2gobroker-loadchecker.service
> +%endif
> +%else
> +/sbin/chkconfig --add x2gobroker-loadchecker
> +if [ "$1" -ge "1" ] ; then
> + /sbin/service x2gobroker-loadchecker condrestart >/dev/null 2>&1 || :
> +fi
> +%endif
> +%if 0%{?suse_version}
> +%set_permissions %{_localstatedir}/log/x2gobroker
> +
> +
> +%verifyscript loadchecker
> +%verify_permissions -e %{_localstatedir}/log/x2gobroker
> +%endif
> +
> +%preun loadchecker
> +%if 0%{?fedora} || 0%{?el7} || 0%{?suse_version} >= 1230
> +%if 0%{?suse_version}
> +%service_del_preun x2gobroker-loadchecker.service
> +%else
> +%systemd_preun x2gobroker-loadchecker.service
> +%endif
> +%else
> +if [ "$1" = 0 ]; then
> + /sbin/service x2gobroker-loadchecker stop >/dev/null 2>&1
> + /sbin/chkconfig --del x2gobroker-loadchecker
> +fi
> +%endif
> +
> +%if 0%{?fedora} || 0%{?el7} || 0%{?suse_version} >= 1230
> +%postun loadchecker
> +%if 0%{?suse_version}
> +%service_del_postun x2gobroker-loadchecker.service
> +%else
> +%systemd_postun x2gobroker-loadchecker.service
> +%endif
> +%endif
> +
> +
> +%if 0%{?suse_version} >= 1230
> %pre daemon
> %service_add_pre x2gobroker-daemon.service
> %endif
> @@ -620,6 +719,26 @@ fi
> %attr(02750,x2gobroker,x2gobroker) %_localstatedir/log/x2gobroker
>
>
> +%files loadchecker
> +%defattr(-,root,root)
> +%if 0%{?el5}
> +%_initrddir/x2gobroker-loadchecker
> +%endif
> +%if 0%{?el6}
> +%_initddir/x2gobroker-loadchecker
> +%endif
> +%if 0%{?fedora} || 0%{?el7} || 0%{?suse_version} >= 1230
> +%{_unitdir}/x2gobroker-loadchecker.service
> +%endif
> +%if 0%{?el5} || 0%{?el6} || (0%{?suse_version} && 0%{?suse_version} < 1140)
> +%config %_sysconfdir/default/x2gobroker-loadchecker
> +%endif
> +%config %_sysconfdir/logrotate.d/x2gobroker-loadchecker
> +%_sbindir/x2gobroker-loadchecker
> +%_mandir/man8/x2gobroker-loadchecker.8*
> +%attr(02750,x2gobroker,x2gobroker) %_localstatedir/log/x2gobroker
> +
> +
> %files daemon
> %defattr(-,root,root)
> %_bindir/x2gobroker-daemon
> diff --git a/x2gobroker/agent.py b/x2gobroker/agent.py
> index 5364825..462f188 100644
> --- a/x2gobroker/agent.py
> +++ b/x2gobroker/agent.py
> @@ -338,6 +338,31 @@ def find_busy_servers(username,
> remote_agent=None, **kwargs):
> tasks['findbusyservers'] = find_busy_servers
>
>
> +def checkload(remote_agent=None, **kwargs):
> + """\
> + Query X2Go Broker Agent for a summary of system load specific
> + parameters.
> +
> + @param remote_agent: information about the remote agent that is
> to be called.
> + @type remote_agent: C{dict}
> +
> + """
> + _success, _load_params = call_broker_agent('foo',
> task='checkload', remote_agent=remote_agent, **kwargs)
> +
> + p = {}
> + for _param in _load_params:
> + if ':' in _param:
> + key, val = _param.split(':', 1)
> + p[key] = float(val)
> +
> + load_factor = None
> + try:
> + load_factor = long( ( (p['memAvail']/1000) * p['numCPU'] *
> p['typeCPU'] * 100 ) / p['loadavgXX'] )
> + except KeyError:
> + pass
> +
> + return load_factor
> +
> def add_authorized_key(username, pubkey_hash,
> authorized_keys_file='%h/.x2go/authorized_keys', remote_agent=None,
> **kwargs):
> """\
> Add a public key hash to the user's authorized_keys file.
> diff --git a/x2gobroker/brokers/base_broker.py
> b/x2gobroker/brokers/base_broker.py
> index fe9ecc5..ad22803 100644
> --- a/x2gobroker/brokers/base_broker.py
> +++ b/x2gobroker/brokers/base_broker.py
> @@ -708,6 +708,59 @@ class X2GoBroker(object):
>
> return unicode(_group_db)
>
> + def get_use_load_checker(self):
> + """\
> + Is this broker backend configured to access an X2Go Broker
> LoadChecker daemon.
> +
> + @return: C{True} if there should a load checker daemon running.
> + @rtype: C{bool}
> +
> + """
> + _use_load_checker = False
> + if self.config.has_value('global', 'default-use-load-checker'):
> + _use_load_checker = self.config.get_value('global',
> 'default-use-load-checker') or _use_load_checker
> +
> + if
> self.config.has_value('broker_{backend}'.format(backend=self.backend_name),
> 'use-load-checker'):
> + _use_load_checker =
> self.config.get_value('broker_{backend}'.format(backend=self.backend_name),
> 'use-load-checker') or _use_load_checker
> +
> + return _use_load_checker
> +
> + def use_load_checker(self, profile_id):
> + """\
> + Actually query the load checker daemon for the given
> session profile ID.
> + This method will check:
> +
> + - broker backend configured to use load checker daemon?
> + - more than one host configured?
> + - load checker queries not explicitly disabled in session profile?
> +
> + @param profile_id: choose remote agent for this profile ID
> + @type profile_id: C{unicode}
> +
> + @return: C{True} if there is a load checker daemon running.
> + @rtype: C{bool}
> +
> + """
> + # only use load checker if...
> +
> + # it is enabled for the broker backend...
> + if self.get_use_load_checker():
> +
> + _profile = self.get_profile(profile_id)
> +
> + # it is not explicitly disabled per session profile definition
> + if _profile and
> _profile.has_key(u'broker-use-load-checker') and
> _profile['broker-use-load-checker'] is False:
> + return False
> +
> + # more than one host is defined in the session profile
> + if len(_profile[u'host']) < 2:
> + return False
> +
> + else:
> + return False
> +
> + return True
> +
> def _import_nameservice_module(self, service='libnss'):
> try:
> if self.nameservice_module is None:
> @@ -1046,6 +1099,49 @@ class X2GoBroker(object):
>
> return remote_agent
>
> + def get_all_remote_agents(self, profile_id):
> + """\
> + Get all remote agents.
> +
> + @param profile_id: choose remote agent for this profile ID
> + @type profile_id: C{unicode}
> +
> + @return: C{list} of remote agents for the given profile ID
> + @rtype: C{list}
> +
> + """
> + remote_agents = []
> +
> + # no remote agent needed for shadow sessions
> + if self.is_shadow_profile(profile_id):
> + return remote_agents
> +
> + agent_query_mode = self.get_agent_query_mode(profile_id).upper()
> + if agent_query_mode == u'SSH' and
> x2gobroker.agent.has_remote_broker_agent_setup():
> +
> + profile = self.get_profile(profile_id)
> + server_list = profile[u'host']
> +
> + while server_list:
> +
> + remote_agent_hostname = server_list[-1]
> + remote_agent_hostaddr = remote_agent_hostname
> + remote_agent_port = profile[u'sshport']
> + if
> profile.has_key('sshport={hostname}'.format(hostname=remote_agent_hostname)):
> + remote_agent_port =
> profile["sshport={hostname}".format(hostname=remote_agent_hostname)]
> + if
> profile.has_key('host={hostname}'.format(hostname=remote_agent_hostname)):
> + remote_agent_hostaddr =
> profile["host={hostname}".format(hostname=remote_agent_hostname)]
> +
> + remote_agents.append({
> + u'hostname': remote_agent_hostname,
> + u'hostaddr': remote_agent_hostaddr,
> + u'port': remote_agent_port, }
> + )
> +
> + server_list = server_list[0:-1]
> +
> + return remote_agents
> +
> def is_shadow_profile(self, profile_id):
> """\
> Detect from the session profile, if it defines a desktop
> sharing (shadow)
> @@ -1357,6 +1453,25 @@ class X2GoBroker(object):
> if busy_server not in server_list:
> del busy_servers[busy_server]
>
> +
> + # dynamic load-balancing
> + if self.use_load_checker(self, profile_id):
> + busy_servers_temp = copy.deepcopy(busy_servers)
> + load_factors =
> x2gobroker.loadchecker.checkload(self.backend_name, profile_id)
> + if load_factors:
> + for busy_server in busy_servers_temp.keys():
> + # FIXME: this may need love later
> for peculiar multi-farm setups with non-FQDN hostnames in session
> profile configurations
> + if busy_server in
> load_factors.keys() and type(load_factors[busy_server]) ==
> types.LongType:
> + # do the load-factor /
> relX2GoServerUsage calculation here...
> + busy_servers_temp[busy_server]
> = load_factors[busy_server] / busy_servers[busy_server]
> + else:
> + # ignore the load checker,
> results are garbage...
> + busy_servers_temp = None
> + break
> +
> + if busy_servers_temp is not None:
> + busy_servers = copy.deepcopy(busy_servers_temp)
> +
> busy_server_list = [ (load, server) for server,
> load in busy_servers.items() ]
> busy_server_list.sort()
>
> diff --git a/x2gobroker/defaults.py b/x2gobroker/defaults.py
> index c7d4dbf..a6d4830 100644
> --- a/x2gobroker/defaults.py
> +++ b/x2gobroker/defaults.py
> @@ -131,6 +131,19 @@ else:
> RUNDIR = '/var/run/x2gobroker'
>
> X2GOBROKER_AUTHSERVICE_SOCKET="{run}/x2gobroker/x2gobroker-authservice.socket".format(run=RUNDIR)
>
> +if os.environ.has_key('X2GOBROKER_LOADCHECKER_SOCKET'):
> +
> X2GOBROKER_LOADCHECKER_SOCKET=os.environ['X2GOBROKER_LOADCHECKER_SOCKET']
> +elif iniconfig_loaded and iniconfig.has_option(iniconfig_section,
> 'X2GOBROKER_LOADCHECKER_SOCKET'):
> + X2GOBROKER_LOADCHECKER_SOCKET=iniconfig.get(iniconfig_section,
> 'X2GOBROKER_LOADCHECKER_SOCKET')
> +elif iniconfig_loaded and iniconfig.has_option('common',
> 'X2GOBROKER_LOADCHECKER_SOCKET'):
> + X2GOBROKER_LOADCHECKER_SOCKET=iniconfig.get('common',
> 'X2GOBROKER_LOADCHECKER_SOCKET')
> +else:
> + if os.path.isdir('/run/x2gobroker'):
> + RUNDIR = '/run'
> + else:
> + RUNDIR = '/var/run/x2gobroker'
> +
> X2GOBROKER_LOADCHECKER_SOCKET="{run}/x2gobroker/x2gobroker-loadchecker.socket".format(run=RUNDIR)
> +
> if os.environ.has_key('X2GOBROKER_DEFAULT_BACKEND'):
> X2GOBROKER_DEFAULT_BACKEND = os.environ['X2GOBROKER_DEFAULT_BACKEND']
> elif iniconfig_loaded and iniconfig.has_option(iniconfig_section,
> 'X2GOBROKER_DEFAULT_BACKEND'):
> @@ -210,6 +223,8 @@ X2GOBROKER_CONFIG_DEFAULTS = {
> u'default-sshproxy-authorized-keys': u'%h/.x2go/authorized_keys',
> u'default-agent-query-mode': u'NONE',
> u'default-portscan-x2goservers': True,
> + u'default-use-load-checker': False,
> + u'load-checker-intervals': 300,
> },
> 'broker_base': {
> u'enable': False,
> @@ -220,6 +235,7 @@ X2GOBROKER_CONFIG_DEFAULTS = {
> u'user-db': u'libnss',
> u'group-db': u'libnss',
> u'desktop-shell': u'KDE',
> + u'load-checker': False,
> },
> 'broker_inifile': {
> u'enable': True,
> @@ -227,6 +243,7 @@ X2GOBROKER_CONFIG_DEFAULTS = {
> u'auth-mech': u'',
> u'user-db': u'',
> u'group-db': u'',
> + u'use-load-checker': True,
> },
> 'broker_ldap': {
> u'enable': False,
> @@ -240,6 +257,7 @@ X2GOBROKER_CONFIG_DEFAULTS = {
> u'group-search-filter': u'(&(objectClass=posifxGroup)(cn=*))',
> u'starttls': False,
> u'agent-query-mode': u'SSH',
> + u'load-checker': True,
> },
> }
>
> diff --git a/x2gobroker/loadchecker.py b/x2gobroker/loadchecker.py
> new file mode 100644
> index 0000000..f38717e
> --- /dev/null
> +++ b/x2gobroker/loadchecker.py
> @@ -0,0 +1,219 @@
> +# -*- coding: utf-8 -*-
> +
> +# This file is part of the X2Go Project - http://www.x2go.org
> +# Copyright (C) 2012-2015 by Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
> +#
> +# X2Go Session Broker is free software; you can redistribute it
> and/or modify
> +# it under the terms of the GNU Affero General Public License as
> published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# X2Go Session Broker is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU Affero General Public License for more details.
> +#
> +# You should have received a copy of the GNU Affero General Public License
> +# along with this program; if not, write to the
> +# Free Software Foundation, Inc.,
> +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
> +
> +import threading
> +import time
> +import copy
> +import socket
> +
> +# X2Go Session Broker modules
> +import x2gobroker.defaults
> +import x2gobroker.config
> +from x2gobroker.loggers import logger_broker
> +
> +
> +def checkload(backend, profile_id, hostname=None):
> + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
> + logger_broker.debug('loadchecker.checkload(): connecting to
> load checker service socket
> {socket}'.format(socket=x2gobroker.defaults.X2GOBROKER_LOADCHECKER_SOCKET))
> + try:
> + s.connect(x2gobroker.defaults.X2GOBROKER_LOADCHECKER_SOCKET)
> + except socket.error, e:
> + logger_broker.error('loadchecker.checkload(): failure when
> connecting to the load checker service socket {socket}:
> {errmsg}'.format(socket=x2gobroker.defaults.X2GOBROKER_LOADCHECKER_SOCKET,
> errmsg=str(e)))
> +
> + if hostname is not None:
> + load_factor = 'LOAD-UNAVAILABLE'
> + logger_broker.debug('loadchecker.checkload(): sending
> backend={backend}, profile_id={profile_id}, hostname={hostname} to
> load checker service'.format(backend=backend, profile_id=profile_id,
> hostname=hostname))
> + try:
> +
> s.send('{backend}\r{profile_id}\r{hostname}\n'.format(backend=backend,
> profile_id=profile_id, hostname=hostname))
> + load_factor = s.recv(1024)
> + s.close()
> + except socket.error, e:
> + logger_broker.error('loadchecker.checkload(): failure
> when sending data to the load checker service socket {socket}:
> {errmsg}'.format(socket=x2gobroker.defaults.X2GOBROKER_LOADCHECKER_SOCKET,
> errmsg=str(e)))
> +
> + if load_factor.startswith('LOAD-UNAVAILABLE'):
> + logger_broker.warning('loadchecker.checkload(): load
> unavailable for backend={backend}, profile_id={profile_id},
> hostname={hostname}'.format(backend=backend, profile_id=profile_id,
> hostname=hostname))
> + return None
> +
> + logger_broker.info('loadchecker.checkload(): load factor
> for backend={backend}, profile_id={profile_id}, hostname={hostname}
> is: {lf}'.format(backend=backend, profile_id=profile_id,
> hostname=hostname, lf=load_factor))
> + return load_factor
> +
> + else:
> + raw_output = ""
> + logger_broker.debug('loadchecker.checkload(): sending
> backend={backend}, profile_id={profile_id} to load checker
> service'.format(backend=backend, profile_id=profile_id,
> hostname=hostname))
> + try:
> +
> s.send('{backend}\r{profile_id}\r\n'.format(backend=backend,
> profile_id=profile_id))
> + raw_output = s.recv(1024)
> + s.close()
> + except socket.error, e:
> + logger_broker.error('loadchecker.checkload(): failure
> when sending data to the load checker service socket {socket}:
> {errmsg}'.format(socket=x2gobroker.defaults.X2GOBROKER_LOADCHECKER_SOCKET,
> errmsg=str(e)))
> +
> + load_factors = {}
> + items = raw_output.split('\n')
> + for item in items:
> + if ":" in item:
> + key, val = item.split(':', 1)
> + load_factors[key] = val
> +
> + logger_broker.info('loadchecker.checkload(): load metrics
> for backend={backend}, profile_id={profile_id} are:
> {lf}'.format(backend=backend, profile_id=profile_id,
> hostname=hostname, lf=load_factors))
> + return load_factors
> +
> +
> +class LoadChecker(threading.Thread):
> +
> + def __init__(self, config_file=None, config_defaults=None,
> logger=None, **kwargs):
> + """\
> + Initialize a new LoadChecker instance for querying remote
> X2Go Broker Agent instances
> + about server/system load, CPU usage, etc.
> +
> + """
> + self.logger = logger
> +
> + self.config_file = config_file
> + if self.config_file is None: self.config_file =
> x2gobroker.defaults.X2GOBROKER_CONFIG
> + self.config_defaults = config_defaults
> + if self.config_defaults is None: self.config_defaults =
> x2gobroker.defaults.X2GOBROKER_CONFIG_DEFAULTS
> + self.kwargs = kwargs
> +
> + threading.Thread.__init__(self, target=self.loadchecker)
> + self.server_load = {}
> + self.keep_alive = True
> + self.daemon = True
> +
> + def get_server_load(self, backend, profile_id, hostname):
> + """\
> + Retrieve system load for a given server (via broker backend,
> + profile ID and hostname).
> +
> + @param backend: broker backend to query.
> + @type backend: C{unicode}
> + @param profile_id: profile ID of the session profile to query
> + @type profile_id: C{unicode}
> + @param hostname: hostname of the X2Go Server
> + @type hostname: C{unicode}
> +
> + @return: load factor of the given server (or None if an
> error occurs)
> + @rtype: C{int}
> +
> + """
> + try:
> + return self.server_load[backend][profile_id][hostname]
> + except KeyError:
> + return None
> +
> + def get_profile_load(self, backend, profile_id):
> + """\
> + Retrieve system load for all servers for a given profile ID
> (and a given
> + broker backend).
> +
> + @param backend: broker backend to query.
> + @type backend: C{unicode}
> + @param profile_id: profile ID of the session profile to query
> + @type profile_id: C{unicode}
> +
> + @return: load factor of the given server (or None if an
> error occurs)
> + @rtype: C{dict}
> +
> + """
> + try:
> + return self.server_load[backend][profile_id]
> + except KeyError:
> + return None
> +
> + def loadchecker(self):
> + """\
> + This is the actual thread runner that queries configured /
> available X2Go Broker Agents in regular
> + intervals about system load, CPU types and usage.
> +
> + """
> + time_to_sleep = 0
> + while self.keep_alive:
> +
> + # in every loop we re-read the main configuration file(s)
> + # this allows changes to the config files to take
> effect immediately...
> +
> + self.config =
> x2gobroker.config.X2GoBrokerConfigFile(config_files=self.config_file,
> defaults=self.config_defaults)
> + self.load_checker_intervals =
> self.config.get_value('global', 'load-checker-intervals')
> +
> + self.broker_backends = [ "_".join(bs.split('_')[1:])
> for bs in self.config.list_sections() if bs.startswith('broker_')
> and self.config.get_value(bs, 'enable') ]
> +
> + # potentially, the X2Go Session Broker can manage
> different broker backends at the same time, so we initialize
> + # all configured/enabled broker backends
> + self.brokers = {}
> + num_queries = 0
> + for backend in self.broker_backends:
> +
> + if self.logger:
> self.logger.debug('LoadChecker.loadchecker(): load checker thread
> waking up...')
> +
> + if not self.server_load.has_key(backend):
> + self.server_load[backend] = {}
> +
> + _broker_backend_module = None
> + exec("import x2gobroker.brokers.{backend}_broker as
> _broker_backend_module".format(backend=backend))
> + self.brokers[backend] =
> _broker_backend_module.X2GoBroker(config_file=self.config_file,
> config_defaults=self.config_defaults, **self.kwargs)
> + profile_ids_to_check = [ id for id in
> self.brokers[backend].get_profile_ids() if
> self.brokers[backend].use_load_checker(id) ]
> +
> + for profile_id in profile_ids_to_check:
> +
> + if not self.server_load[backend].has_key(profile_id):
> + self.server_load[backend][profile_id] = {}
> + remote_agents =
> self.brokers[backend].get_all_remote_agents(profile_id)
> + for remote_agent in remote_agents:
> +
> self.server_load[backend][profile_id][remote_agent[u'hostname']] =
> x2gobroker.agent.checkload(remote_agent)
> + if self.logger:
> self.logger.info('LoadChecker.loadchecker(): contacted remote broker
> agent for backend={backend}, profile_id={profile_id},
> hostname={hostname}, new load factor is:
> {lf}'.format(backend=backend, profile_id=profile_id,
> hostname=remote_agent[u'hostname'],
> lf=self.server_load[backend][profile_id][remote_agent[u'hostname']]))
> + num_queries += 1
> + if time_to_sleep > 0:
> + if self.logger:
> self.logger.debug('LoadChecker.loadchecker(): sleeping for
> {secs}secs before querying next server'.format(secs=time_to_sleep))
> + time.sleep(time_to_sleep)
> +
> + # clean up vanished hostnames
> + _hostnames =
> self.server_load[backend][profile_id].keys()
> + for hostname in _hostnames:
> + if hostname not in [ ra[u'hostname'] for ra
> in remote_agents ]:
> + del
> self.server_load[backend][profile_id][hostname]
> +
> + # clean up vanished profile IDs
> + _profile_ids =
> copy.deepcopy(self.server_load[backend].keys())
> + for profile_id in _profile_ids:
> + if profile_id not in profile_ids_to_check:
> + del self.server_load[backend][profile_id]
> +
> + # clean up vanished backends
> + _backends = copy.deepcopy(self.server_load.keys())
> + for backend in _backends:
> + if backend not in self.broker_backends:
> + del self.server_load[backend]
> +
> + # don't do all queries every 300-or-so seconds, but
> distribute next round of queries over the
> + # complete load_checker_intervals range
> + if time_to_sleep == 0:
> + if self.logger:
> self.logger.debug('LoadChecker.loadchecker(): sleeping for
> {secs}secs before starting next query
> cycle'.format(secs=self.load_checker_intervals))
> + time.sleep(self.load_checker_intervals)
> + if num_queries > 0:
> + if time_to_sleep > 0:
> + if self.logger:
> self.logger.debug('LoadChecker.loadchecker(): performed {num}
> queries, sleeping for {secs}secs before starting next query
> cycle'.format(num=num_queries, secs=self.load_checker_intervals -
> time_to_sleep * num_queries))
> + time.sleep(self.load_checker_intervals -
> time_to_sleep * num_queries)
> + time_to_sleep = self.load_checker_intervals /
> (num_queries +1)
> + else:
> + if num_queries == 0:
> + if self.logger:
> self.logger.warning('LoadChecker.loadchecker(): performed {num}
> queries in this cycle, if this message keeps repeating itself,
> consider disabling the X2Go Broker Load Checker
> daemon'.format(num=num_queries, secs=self.load_checker_intervals -
> time_to_sleep * num_queries))
> + time_to_sleep = 0
> +
> + def stop_thread(self):
> + self.keep_alive = False
>
> --
> Alioth's
> /srv/git/code.x2go.org/x2gobroker.git//..//_hooks_/post-receive-email on
> /srv/git/code.x2go.org/x2gobroker.git
> _______________________________________________
> x2go-commits mailing list
> x2go-commits at lists.x2go.org
> http://lists.x2go.org/listinfo/x2go-commits
--
DAS-NETZWERKTEAM
mike gabriel, herweg 7, 24357 fleckeby
fon: +49 (1520) 1976 148
GnuPG Key ID 0x25771B31
mail: mike.gabriel at das-netzwerkteam.de, http://das-netzwerkteam.de
freeBusy:
https://mail.das-netzwerkteam.de/freebusy/m.gabriel%40das-netzwerkteam.de.xfb
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: Digitale PGP-Signatur
URL: <http://lists.x2go.org/pipermail/x2go-dev/attachments/20150326/f7cc0bb8/attachment-0001.pgp>
More information about the x2go-dev
mailing list