This is an automated email from the git hooks/post-receive script. x2go pushed a change to branch master in repository x2gobroker. from d8e4194 Provide desktop sharing (shadow session) example in x2gobroker-sessionprofiles.conf. new 830f982 X2GoBroker.get_remote_agent(): Add @return, @rtype to API documentation. new 0723daa defaults/*.default: Reference SystemV rather than Debian in init script header. new 050ec9d debian/changelog: fix too-long-lines new b2a878f x2gobroker.1: Since systemd there are not only init scripts. Rephrasing man page. new c571e89 New feature: x2gobroker-loadchecker daemon. new c213d36 x2gobroker-agent.pl: Use var name server_usage instead of server_load. Reflects better what that var denotes. new 80bccbe agent.py: Completion of several __doc__ strings (missing @return:, @rtype: fields). new 31696a1 X2GoBroker.check_for_sessions(): Fix check for shadow / non-shadow sessions. The 8 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. Summary of changes: bin/x2gobroker-ssh | Bin 10328 -> 10328 bytes debian/changelog | 10 +- 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 + ...ce.postinst => x2gobroker-loadchecker.postinst} | 2 +- ...ervice.postrm => x2gobroker-loadchecker.postrm} | 8 +- ...vice.service => x2gobroker-loadchecker.service} | 0 debian/x2gobroker-wsgi.postrm | 6 +- defaults/python-x2gobroker.default | 6 +- defaults/x2gobroker-authservice.default | 3 +- defaults/x2gobroker-daemon.default | 2 +- ...vice.default => x2gobroker-loadchecker.default} | 5 +- etc/broker/defaults.conf | 3 + ...ger.conf => x2gobroker-loadchecker-logger.conf} | 20 +- etc/x2gobroker.conf | 49 +++++ ...uthservice.init => x2gobroker-loadchecker.init} | 42 ++-- lib/x2gobroker-agent.pl | 57 ++++- ...gobroker-authservice => x2gobroker-loadchecker} | 4 +- man/man1/x2gobroker.1 | 2 +- man/man8/x2gobroker-authservice.8 | 2 +- man/man8/x2gobroker-loadchecker.8 | 93 +++++++++ ...gobroker-authservice => x2gobroker-loadchecker} | 131 +++++++----- x2gobroker-loadchecker.service | 12 ++ x2gobroker.spec | 119 +++++++++++ x2gobroker/agent.py | 91 ++++++-- x2gobroker/brokers/base_broker.py | 120 ++++++++++- x2gobroker/defaults.py | 18 ++ x2gobroker/loadchecker.py | 219 ++++++++++++++++++++ 34 files changed, 945 insertions(+), 136 deletions(-) create mode 120000 debian/x2gobroker-loadchecker.default create mode 120000 debian/x2gobroker-loadchecker.init create mode 100644 debian/x2gobroker-loadchecker.install create mode 100644 debian/x2gobroker-loadchecker.manpages copy debian/{x2gobroker-authservice.postinst => x2gobroker-loadchecker.postinst} (98%) copy debian/{x2gobroker-authservice.postrm => x2gobroker-loadchecker.postrm} (86%) copy debian/{x2gobroker-authservice.service => x2gobroker-loadchecker.service} (100%) copy defaults/{x2gobroker-authservice.default => x2gobroker-loadchecker.default} (86%) copy etc/broker/{x2gobroker-authservice-logger.conf => x2gobroker-loadchecker-logger.conf} (79%) copy init/{x2gobroker-authservice.init => x2gobroker-loadchecker.init} (62%) copy logrotate/{x2gobroker-authservice => x2gobroker-loadchecker} (59%) create mode 100644 man/man8/x2gobroker-loadchecker.8 copy sbin/{x2gobroker-authservice => x2gobroker-loadchecker} (62%) create mode 100644 x2gobroker-loadchecker.service create mode 100644 x2gobroker/loadchecker.py -- Alioth's /srv/git/code.x2go.org/x2gobroker.git//..//_hooks_/post-receive-email on /srv/git/code.x2go.org/x2gobroker.git
This is an automated email from the git hooks/post-receive script. x2go pushed a commit to branch master in repository x2gobroker. commit 830f9825ee0bcb1a9bd2ea5d6cc5232f09642acf Author: Mike Gabriel <mike.gabriel@das-netzwerkteam.de> Date: Wed Mar 25 10:51:33 2015 +0100 X2GoBroker.get_remote_agent(): Add @return, @rtype to API documentation. --- x2gobroker/brokers/base_broker.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x2gobroker/brokers/base_broker.py b/x2gobroker/brokers/base_broker.py index 793da39..fe9ecc5 100644 --- a/x2gobroker/brokers/base_broker.py +++ b/x2gobroker/brokers/base_broker.py @@ -992,6 +992,9 @@ class X2GoBroker(object): @param exclude_agents: a list of remote agent dict objects to be exclude from the random choice @type exclude_agents: C{list} + @return: remote agent to use for queries for profile ID + @rtype: C{dict} + """ remote_agent = None -- Alioth's /srv/git/code.x2go.org/x2gobroker.git//..//_hooks_/post-receive-email on /srv/git/code.x2go.org/x2gobroker.git
This is an automated email from the git hooks/post-receive script. x2go pushed a commit to branch master in repository x2gobroker. commit 0723daa85958cbe747d5a6538b6883e860725117 Author: Mike Gabriel <mike.gabriel@das-netzwerkteam.de> Date: Wed Mar 25 11:03:22 2015 +0100 defaults/*.default: Reference SystemV rather than Debian in init script header. --- defaults/python-x2gobroker.default | 3 ++- defaults/x2gobroker-authservice.default | 3 ++- defaults/x2gobroker-daemon.default | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/defaults/python-x2gobroker.default b/defaults/python-x2gobroker.default index e9f0be5..73ee9ac 100644 --- a/defaults/python-x2gobroker.default +++ b/defaults/python-x2gobroker.default @@ -1,4 +1,5 @@ -# X2Go Broker Session Broker (common) configuration for Debian +# X2Go Broker Session Broker (common) configuration for +# SystemV-like init systems # The posix user/group ID the broker runs under (do not change!) # if you change those nonetheless, make sure that the log file diff --git a/defaults/x2gobroker-authservice.default b/defaults/x2gobroker-authservice.default index e8b5f7b..7108fca 100644 --- a/defaults/x2gobroker-authservice.default +++ b/defaults/x2gobroker-authservice.default @@ -1,4 +1,5 @@ -# X2Go Session Broker (PAM Authentication Service) configuration for Debian +# X2Go Session Broker (PAM Authentication Service) configuration for +# 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) diff --git a/defaults/x2gobroker-daemon.default b/defaults/x2gobroker-daemon.default index 0eb0a81..b3b4743 100644 --- a/defaults/x2gobroker-daemon.default +++ b/defaults/x2gobroker-daemon.default @@ -1,4 +1,4 @@ -# X2Go Session Broker configuration for Debian +# X2Go Session Broker configuration for SystemV-like init systems # Uncomment to enable the X2Go Session Broker standalone daemon START_BROKER=true -- Alioth's /srv/git/code.x2go.org/x2gobroker.git//..//_hooks_/post-receive-email on /srv/git/code.x2go.org/x2gobroker.git
This is an automated email from the git hooks/post-receive script. x2go pushed a commit to branch master in repository x2gobroker. commit 050ec9df93127e4842b2458090ced380f5cc21a0 Author: Mike Gabriel <mike.gabriel@das-netzwerkteam.de> Date: Thu Mar 26 09:55:10 2015 +0100 debian/changelog: fix too-long-lines --- debian/changelog | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/debian/changelog b/debian/changelog index 07186ae..278d641 100644 --- a/debian/changelog +++ b/debian/changelog @@ -217,11 +217,11 @@ x2gobroker (0.0.3.0-0x2go1) UNRELEASED; urgency=low left bogus in the session DB (plus unit tests). - Fix remote agent detection if one ore more X2Go Servers are offline and hostname does not match host address (plus unit test). - - Allow remote agent calls via hostname or host address when using the format - "<hostname> (<hostaddr>)" in the session profile. This can be useful - if the <hostname> is a valid address on the local network (broker <-> - <server> communication), but the host address is valid for clients - (client <-> server communication). + - Allow remote agent calls via hostname or host address when using the + format "<hostname> (<hostaddr>)" in the session profile. This can be + useful if the <hostname> is a valid address on the local network + (broker <-> <server> communication), but the host address is valid for + clients (client <-> server communication). - Don't check for running/suspended session if the session profile will request a shadowing session. - Disabled broker agent calls and load-balancing for session profiles that -- Alioth's /srv/git/code.x2go.org/x2gobroker.git//..//_hooks_/post-receive-email on /srv/git/code.x2go.org/x2gobroker.git
This is an automated email from the git hooks/post-receive script. x2go pushed a commit to branch master in repository x2gobroker. commit b2a878fcb4975fc12d0420c286b4ec4c94cbcabf Author: Mike Gabriel <mike.gabriel@das-netzwerkteam.de> Date: Thu Mar 26 09:56:38 2015 +0100 x2gobroker.1: Since systemd there are not only init scripts. Rephrasing man page. --- man/man1/x2gobroker.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/man1/x2gobroker.1 b/man/man1/x2gobroker.1 index 6a925d1..fdff520 100644 --- a/man/man1/x2gobroker.1 +++ b/man/man1/x2gobroker.1 @@ -34,7 +34,7 @@ x2gobroker,x2gobroker-daemon \- Session Broker for X2Go .SH DESCRIPTION \fBx2gobroker\fR is a Python Tornado based implementation of the X2Go Session Broker API. .PP -This command is normally executed either through an init script or via the WSGI module in your httpd server. +This command is normally executed either through the host's init system or via the WSGI module in your httpd server. .PP See the included README and TODO for further information on \fBx2gobroker\fR. .SH GENERAL OPTIONS -- Alioth's /srv/git/code.x2go.org/x2gobroker.git//..//_hooks_/post-receive-email on /srv/git/code.x2go.org/x2gobroker.git
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@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@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@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@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@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@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
This is an automated email from the git hooks/post-receive script. x2go pushed a commit to branch master in repository x2gobroker. commit c213d3675748e91d530c9cf28b24ba678a113084 Author: Mike Gabriel <mike.gabriel@das-netzwerkteam.de> Date: Thu Mar 26 11:38:13 2015 +0100 x2gobroker-agent.pl: Use var name server_usage instead of server_load. Reflects better what that var denotes. --- lib/x2gobroker-agent.pl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/x2gobroker-agent.pl b/lib/x2gobroker-agent.pl index 9463018..c98c603 100755 --- a/lib/x2gobroker-agent.pl +++ b/lib/x2gobroker-agent.pl @@ -235,19 +235,19 @@ if( (($mode eq 'findbusyservers_by_sessionstats') || ($mode eq 'findbusyservers' print "OK\n"; my $busy_servers = `sudo -u $uid -- x2gogetservers`; - my %server_load = (); + my %server_usage = (); my $num_sessions = 0; foreach (split('\n', $busy_servers)) { my ($hostname, $num_users) = split(' ', $_); - $server_load{$hostname} = $num_users; + $server_usage{$hostname} = $num_users; $num_sessions += $num_users; } # render the output result my @result; - for my $hostname ( keys %server_load ) { - my $available = $server_load{$hostname}/$num_sessions*100; + for my $hostname ( keys %server_usage ) { + my $available = $server_usage{$hostname}/$num_sessions*100; push @result, sprintf '%1$d:%2$s', $available, $hostname; } print join("\n", sort @result); -- Alioth's /srv/git/code.x2go.org/x2gobroker.git//..//_hooks_/post-receive-email on /srv/git/code.x2go.org/x2gobroker.git
This is an automated email from the git hooks/post-receive script. x2go pushed a commit to branch master in repository x2gobroker. commit 80bccbe510389416daefdb0867d408ab94e92841 Author: Mike Gabriel <mike.gabriel@das-netzwerkteam.de> Date: Thu Mar 26 11:44:03 2015 +0100 agent.py: Completion of several __doc__ strings (missing @return:, @rtype: fields). --- x2gobroker/agent.py | 68 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/x2gobroker/agent.py b/x2gobroker/agent.py index 462f188..566a8c0 100644 --- a/x2gobroker/agent.py +++ b/x2gobroker/agent.py @@ -157,7 +157,7 @@ def _call_remote_broker_agent(username, task, cmdline_args=[], remote_agent=None @type task: C{unicode} @param cmdline_args: additional command line parameters for the broker agent @type cmdline_args: C{list} - @param remote_agent: information about the remote agent that is to be called. + @param remote_agent: information about the remote agent that is to be called @type remote_agent: C{dict} @raise: L{X2GoBrokerAgentException} @@ -232,9 +232,12 @@ def ping(remote_agent=None, **kwargs): """\ Ping X2Go Broker Agent. - @param remote_agent: information about the remote agent that is to be called. + @param remote_agent: information about the remote agent that is to be called @type remote_agent: C{dict} + @return: C{True} if broker agent responds + @rtype: C{bool} + """ username='foo' if remote_agent is None: @@ -252,9 +255,13 @@ def list_sessions(username, remote_agent=None, **kwargs): @param username: run the query on behalf of this username @type username: C{unicode} - @param remote_agent: information about the remote agent that is to be called. + @param remote_agent: information about the remote agent that is to be called @type remote_agent: C{dict} + @return: (<success>, <list-of-sessions>), a tuple with the <success> flag as first item + and a session C{list} as second item + @rtype: C{tuple} + """ return call_broker_agent(username, task='listsessions', remote_agent=remote_agent, **kwargs) tasks['listsessions'] = list_sessions @@ -266,9 +273,12 @@ def suspend_session(username, session_name, remote_agent=None, **kwargs): @param username: suspend the session on behalf of this username @type username: C{unicode} - @param remote_agent: information about the remote agent that is to be called. + @param remote_agent: information about the remote agent that is to be called @type remote_agent: C{dict} + @return: (<success>, ), a tuple with the <success> flag as first item + @rtype: C{tuple} + """ return call_broker_agent(username, task='suspendsession', cmdline_args=[session_name, ], remote_agent=remote_agent, **kwargs) tasks['suspendsession'] = suspend_session @@ -280,9 +290,12 @@ def terminate_session(username, session_name, remote_agent=None, **kwargs): @param username: terminate the session on behalf of this username @type username: C{unicode} - @param remote_agent: information about the remote agent that is to be called. + @param remote_agent: information about the remote agent that is to be called @type remote_agent: C{dict} + @return: (<success>, ), a tuple with the <success> flag as first item + @rtype: C{tuple} + """ return call_broker_agent(username, task='terminatesession', cmdline_args=[session_name, ], remote_agent=remote_agent, **kwargs) tasks['terminatesession'] = terminate_session @@ -295,10 +308,10 @@ def has_sessions(username, remote_agent=None, **kwargs): @param username: run the query on behalf of this username @type username: C{unicode} - @param remote_agent: information about the remote agent that is to be called. + @param remote_agent: information about the remote agent that is to be called @type remote_agent: C{dict} - @return: (<has-running-sessions>, <has-suspended-session>), a tuple of two Boolean values + @return: (<success>, <has-running-sessions>, <has-suspended-session>), a tuple of two Boolean values @rtype: C{tuple} """ @@ -307,7 +320,7 @@ def has_sessions(username, remote_agent=None, **kwargs): return (_success, [ s.split('|')[3] for s in _session_list if s.split('|')[4] == 'R' ], [ s.split('|')[3] for s in _session_list if s.split('|')[4] == 'S' ]) else: return (False, [], []) - +tasks['has-sessions'] = has_sessions def find_busy_servers(username, remote_agent=None, **kwargs): """\ @@ -319,9 +332,13 @@ def find_busy_servers(username, remote_agent=None, **kwargs): @param username: run the query on behalf of this username @type username: C{unicode} - @param remote_agent: information about the remote agent that is to be called. + @param remote_agent: information about the remote agent that is to be called @type remote_agent: C{dict} + @return: (<success>, <server-usage>), a tuple with the <success> flag as first item + and a dict reflecting the relative server usage + @rtype: C{tuple} + """ _success, server_list = call_broker_agent(username, task='findbusyservers', remote_agent=remote_agent, **kwargs) @@ -343,9 +360,13 @@ 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. + @param remote_agent: information about the remote agent that is to be called @type remote_agent: C{dict} + @return: (<success>, <load-factor>), a tuple with the <success> flag as first item + and the queried server's load factor as second item + @rtype: C{tuple} + """ _success, _load_params = call_broker_agent('foo', task='checkload', remote_agent=remote_agent, **kwargs) @@ -362,6 +383,7 @@ def checkload(remote_agent=None, **kwargs): pass return load_factor +tasks['checkload'] = checkload def add_authorized_key(username, pubkey_hash, authorized_keys_file='%h/.x2go/authorized_keys', remote_agent=None, **kwargs): """\ @@ -373,9 +395,12 @@ def add_authorized_key(username, pubkey_hash, authorized_keys_file='%h/.x2go/aut @type pubkey_hash: C{unicode} @param authorized_keys_file: the full path to the remote X2Go server's authorized_keys file @type authorized_keys_file: C{unicode} - @param remote_agent: information about the remote agent that is to be called. + @param remote_agent: information about the remote agent that is to be called @type remote_agent: C{dict} + @return: (<success>, ), a tuple with the <success> flag as first item + @rtype: C{tuple} + """ return call_broker_agent(username, task='addauthkey', cmdline_args=[pubkey_hash, authorized_keys_file, ], remote_agent=remote_agent, **kwargs) tasks['addauthkey'] = add_authorized_key @@ -391,9 +416,12 @@ def delete_authorized_key(username, pubkey_hash, authorized_keys_file='%h/.x2go/ @type pubkey_hash: C{unicode} @param authorized_keys_file: the full path to the remote X2Go server's authorized_keys file @type authorized_keys_file: C{unicode} - @param remote_agent: information about the remote agent that is to be called. + @param remote_agent: information about the remote agent that is to be called @type remote_agent: C{dict} + @return: (<success>, ), a tuple with the <success> flag as first item + @rtype: C{tuple} + """ # this is for the logger output if remote_agent in ('LOCAL', None): @@ -418,9 +446,13 @@ def get_servers(username, remote_agent=None, **kwargs): @param username: run the query on behalf of this username @type username: C{unicode} - @param remote_agent: information about the remote agent that is to be called. + @param remote_agent: information about the remote agent that is to be called @type remote_agent: C{dict} + @return: (<success>, <server-list>), a tuple with the <success> flag as first item + and the list of used X2Go Servers as second item + @rtype: C{tuple} + """ return call_broker_agent(username, task='getservers', remote_agent=remote_agent, **kwargs) tasks['getservers'] = get_servers @@ -435,9 +467,13 @@ def tasks_available(username, remote_agent=None, **kwargs): @param username: run the query on behalf of this username @type username: C{unicode} - @param remote_agent: information about the remote agent that is to be called. + @param remote_agent: information about the remote agent that is to be called @type remote_agent: C{dict} + @return: (<success>, <server-list>), a tuple with the <success> flag as first item + and a list of available broker agent tasks as second item + @rtype: C{tuple} + """ return call_broker_agent(username, task='availabletasks', remote_agent=remote_agent, **kwargs) tasks['availabletasks'] = tasks_available @@ -454,6 +490,9 @@ def genkeypair(local_username, client_address, key_type='RSA'): @param key_type: either of: RSA, DSA @type key_type: C{unicode} + @return: two-item tuple: (<pubkey>, <privkey>) + @rtype: C{tuple} + """ key = None pubkey = None @@ -483,4 +522,3 @@ def genkeypair(local_username, client_address, key_type='RSA'): privkey = privkey_obj.getvalue() return (pubkey, privkey) - -- Alioth's /srv/git/code.x2go.org/x2gobroker.git//..//_hooks_/post-receive-email on /srv/git/code.x2go.org/x2gobroker.git
This is an automated email from the git hooks/post-receive script. x2go pushed a commit to branch master in repository x2gobroker. commit 31696a18f48fafc103c798d56a987fee448933d1 Author: Mike Gabriel <mike.gabriel@das-netzwerkteam.de> Date: Thu Mar 26 11:45:33 2015 +0100 X2GoBroker.check_for_sessions(): Fix check for shadow / non-shadow sessions. --- x2gobroker/brokers/base_broker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x2gobroker/brokers/base_broker.py b/x2gobroker/brokers/base_broker.py index ad22803..189c679 100644 --- a/x2gobroker/brokers/base_broker.py +++ b/x2gobroker/brokers/base_broker.py @@ -1172,7 +1172,7 @@ class X2GoBroker(object): do_check = True # do check, for all commands except the "SHADOW" command - do_check = do_check and self.is_shadow_profile(profile_id) + do_check = do_check and not self.is_shadow_profile(profile_id) return do_check -- Alioth's /srv/git/code.x2go.org/x2gobroker.git//..//_hooks_/post-receive-email on /srv/git/code.x2go.org/x2gobroker.git