[X2Go-Commits] x2gobroker.git - build-main (branch) updated: 0.0.0.1-39-g1c5e41f
X2Go dev team
git-admin at x2go.org
Sun May 19 13:04:46 CEST 2013
The branch, build-main has been updated
via 1c5e41f4849a2c8273eb96bf09b5c9993a9c7a3f (commit)
from 769d5c17f51f20e91f8bff1fb1830fba2ade4e05 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
-----------------------------------------------------------------------
Summary of changes:
etc/x2gobroker.conf | 45 ++++++++++++++
lib/x2gobroker-agent.pl | 31 +++++++---
x2gobroker/agent.py | 122 +++++++++++++++++++++++++++++--------
x2gobroker/brokers/base_broker.py | 49 +++++++++++++--
x2gobroker/defaults.py | 2 +
5 files changed, 211 insertions(+), 38 deletions(-)
The diff of changes is:
diff --git a/etc/x2gobroker.conf b/etc/x2gobroker.conf
index 83f6bd8..b512cf4 100644
--- a/etc/x2gobroker.conf
+++ b/etc/x2gobroker.conf
@@ -90,6 +90,50 @@
# CPU intensive on the X2Go Broker server.
#ignore-primary-group-memberships = true
+# default X2Go Broker Agent query mode:
+#
+# The X2Go Broker Agent is needed for multi-server sites configured for
+# load balancing. Multi-server sites require a setup that uses the
+# PostgreSQL X2Go session DB backend. The X2Go Broker Agent has to be installed
+# on the local system (mode: LOCAL) or on all X2Go Servers (mode: SSH) in a
+# multi-server farm.
+#
+# So, there are two query modes for the X2GO Broker Agent: LOCAL and SSH.
+#
+# LOCAL - This LOCAL mode only works for _one_ configured multi-server farm.
+# If the locally installed X2Go Session Broker is to server many different
+# multi-server farms, then the LOCAL mode will not work!!!
+#
+# How it works: Assume that the local system has an X2Go Broker Agent
+# that knows about the multi-server setup. This means: X2Go Server has
+# to be installed locally and the X2Go Server has to be configured to
+# use the multi-server farms PostgreSQL session DB backend.
+#
+# The local system that is running the broker does not necessarily have
+# to be a real application server. It only has to be aware of running/suspended
+# sessions within the X2Go multi-server farm setup.
+#
+# A typical use-case is X2Go on top of a Debian Edu Terminal-Server farm:
+#
+# TJENER -> PostgreSQL DB, X2Go Server, X2Go Session Broker + Broker Agent
+# TS01 - TS0X -> X2Go Server configured to use the PostgreSQL DB on TJENER
+#
+# SSH - The more generic approach, but also more complex. It allows that the broker
+# on this system may serve for many different X2Go Server multi-server setups.
+#
+# With the SSH agent query mode, the X2Go Session Broker will query one of the X2Go
+# Servers in the targeted multi-server setup (through SSH). The SSH authentication
+# is done by a system user account (normally UID=x2gobroker) and SSH pub/priv
+# key authentication has to be configured to make this work.
+#
+# All X2Go Servers in a multi-server farm need the X2Go Broker Agent installed,
+# whereas this local system running the X2Go Session Broker does not need a
+# local X2Go Broker Agent at all.
+
+# The agent query mode can be configured on a per-broker-backend basis, the below value is
+# the default.
+#default-agent-query-mode=LOCAL
+
###
### BACKEND section
###
@@ -136,3 +180,4 @@
#host-search-filter = (&(objectClass=ipHost)(serial=X2GoServer)(cn=*))
#group-search-filter = (&(objectClass=posifxGroup)(cn=*))
#starttls = false
+#agent-query-mode = SSH
diff --git a/lib/x2gobroker-agent.pl b/lib/x2gobroker-agent.pl
index 74e0751..481de06 100755
--- a/lib/x2gobroker-agent.pl
+++ b/lib/x2gobroker-agent.pl
@@ -98,17 +98,36 @@ if($mode eq 'listsessions')
exec ("/bin/su - $uid -c \"x2golistsessions --all-servers\"");
}
-if($mode eq 'findbestserver_by_sessionstats') || ($mode eq 'findbestserver')
+if( ($mode eq 'findbusyservers_by_sessionstats') || ($mode eq 'findbusyservers'))
{
+
+ # Normally the session broker setup knows about all servers,
+ # make sure your configuration of X2Go Session Broker is correct and
+ # lists all available servers.
+
+ # The findbusyservers algorithm only returns servers that are currently
+ # in use (i.e. have running or suspended sessions on them). So the
+ # result may be empty or contain a server list not containing all
+ # available servers.
+
+ # The logic of findbusyservers is this, then:
+ # 1. if no server is returned, any of the configured servers is best server
+ # 2. if some servers are returned, a best server is one that is not returned
+ # 3. if all configured servers are returned, than evaluate the usage value
+ # (e.g. 90:server1, 20:server2, 10:server3 -> best server is server3)
+
+ # The above interpretation has to be handled by the broker implementation
+ # calling »x2gobroker-agent findbusyservers«.
+
InitX2GoUser($uid, $uidNumber, $gidNumber, $home);
print "OK\n";
my $session_list = `/bin/su - -c \"x2golistsessions_root --all-servers\"`;
- my $avail_servers = `/bin/su - $uid -c \"x2gogetservers\"`;
+ my $busy_servers = `/bin/su - $uid -c \"x2gogetservers\"`;
my $amount_sessions = 0;
# initialize server_load hash
my %server_load = ();
- foreach (split('\n', $avail_servers))
+ foreach (split('\n', $busy_servers))
{
$server_load{$_} = 0;
}
@@ -125,17 +144,13 @@ if($mode eq 'findbestserver_by_sessionstats') || ($mode eq 'findbestserver')
# render the output result
my @result;
for my $hostname ( keys %server_load ) {
- my $available = 100-$server_load{$hostname}/$amount_sessions*100;
- if ($available eq 0) {
- $available = 100;
- }
+ my $available = $server_load{$hostname}/$amount_sessions*100;
push @result, sprintf '%1$d:%2$s', $available, $hostname;
}
print join('\n', sort @result);
print "\n";
}
-
if($mode eq 'getservers')
{
InitX2GoUser($uid, $uidNumber, $gidNumber, $home);
diff --git a/x2gobroker/agent.py b/x2gobroker/agent.py
index 81edc04..72e95e3 100644
--- a/x2gobroker/agent.py
+++ b/x2gobroker/agent.py
@@ -20,13 +20,16 @@
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
import subprocess
+import paramiko
# X2Go Broker modules
import x2gobroker.defaults
+from x2gobroker.loggers import logger_error
-def call_broker_agent(username, mode):
+
+def call_local_broker_agent(username, mode):
"""\
- Launch X2Go Broker Agent and process its output.
+ Launch X2Go Broker Agent locally and process its output.
@param username: run the broker agent for this user
@type username: C{unicode}
@@ -40,48 +43,117 @@ def call_broker_agent(username, mode):
'{mode}'.format(mode=mode),
]
- subprocess.Popen(cmd_line,
- stdin=None,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- shell=False,
+ agent_process = subprocess.Popen(cmd_line,
+ stdin=None,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ shell=False,
)
- return subprocess.stdout.read()
+ return agent_process.stdout.read().split('\n')
+
+def call_remote_broker_agent(username, mode, remote_agent):
+ """\
+ Launch remote X2Go Broker Agent via SSH and process its output.
+
+ @param username: run the broker agent for this user
+ @type username: C{unicode}
+ @param mode: execution mode of the broker (listsessions, getservers, etc.)
+ @type mode: C{unicode}
+ @param remote_agent: information about the remote agent that is to be called.
+ @type remote_agent: C{dict}
+
+ """
+ cmd_line = [
+ '{x2gobroker_agent_binary}'.format(x2gobroker_agent_binary=x2gobroker.defaults.X2GOBROKER_AGENT_CMD),
+ '{username}'.format(username=username),
+ '{mode}'.format(mode=mode),
+ ]
-def list_sessions(username):
+ remote_username = x2gobroker.defaults.X2GOBROKER_USER
+ remote_hostname = remote_agent[u'hostname']
+ remote_port = int(remote_agent[u'port'])
+
+ # now, connect and use paramiko Client to negotiate SSH2 across the connection
+ try:
+ client = paramiko.SSHClient()
+ client.load_system_host_keys()
+ client.set_missing_host_key_policy(paramiko.WarningPolicy)
+ client.connect(remote_hostname, remote_port, remote_username, look_for_keys=True, allow_agent=True)
+
+ ssh_transport = client.get_transport()
+ if ssh_transport.is_authenticated():
+ cmd = cmd_line.join(' ')
+ cmd = 'sh -c \"{cmd}\"'.format(cmd=cmd)
+ (stdin, stdout, stderr) = client.exec_command(cmd)
+ client.close()
+ return stdout.read().split('\n')
+ except paramiko.SSHException:
+ logger_error.error('could not connect to remote X2Go Broker Agent (user: {user}, hostname: {hostname}, port: {port}'.format(user=remote_username, hostname=remote_hostname, port=remote_port))
+
+def list_sessions(username, query_mode='LOCAL', remote_agent=None):
"""\
Query X2Go Broker Agent for a session list for a given username.
- @param username: username for who to query a session list
+ @param username: run the query on behalf of this username
@type username: C{unicode}
+ @param query_mode: query mode used when calling X2Go Broker Agent (C{LOCAL} or C{SSH})
+ @type query_mode: C{unicode}
+ @param remote_agent: information about the remote agent that is to be called.
+ @type remote_agent: C{dict}
"""
- return call_broker_agent(username, mode='listsessions')
+ if query_mode == 'LOCAL':
+ return call_local_broker_agent(username, mode='listsessions')
+ else:
+ return call_remote_broker_agent(username, mode='listsessions', remote_agent=remote_agent)
-def find_best_server(username=None):
+def find_busy_servers(username, query_mode='LOCAL', remote_agent=None):
"""\
- Query X2Go Broker Agent for the best server for the given user.
- In many cases the username does not have an effect on the
- detection of an optimal server.
+ Query X2Go Broker Agent for a list of servers with running
+ and/or suspended sessions and a percentage that tells about
+ the busy-state of the server.
- @param username: username for who to query a session list
+ The result is independent from the username given.
+
+ @param username: run the query on behalf of this username
@type username: C{unicode}
+ @param query_mode: query mode used when calling X2Go Broker Agent (C{LOCAL} or C{SSH})
+ @type query_mode: C{unicode}
+ @param remote_agent: information about the remote agent that is to be called.
+ @type remote_agent: C{dict}
"""
- server_list = call_broker_agent(username, mode='findbestserver')
- server_list.sort(reverse=True)
- return server_list[0].split(':')[1]
+ if query_mode == 'LOCAL':
+ server_list = call_local_broker_agent(username, mode='findbusyservers')
+ else:
+ server_list = call_remote_broker_agent(username, mode='findbusyservers', remote_agent=remote_agent)
+
+ server_usage = {}
+
+ if server_list:
+ for server_item in server_list.split('\n'):
+ usage, server = line.split(':')
+ server_usage.update({ server: int(usage) })
-def get_servers(username=None):
+ return server_usage
+
+def get_servers(username, query_mode='LOCAL', remote_agent=None):
"""\
- Query X2Go Broker Agent for the list of available servers
- for the given user. In many cases the username does not
- have an effect on the list of available servers.
+ Query X2Go Broker Agent for the list of currently used servers.
+
+ The result is independent from the username given.
- @param username: username for who to query a session list
+ @param username: run the query on behalf of this username
@type username: C{unicode}
+ @param query_mode: query mode used when calling X2Go Broker Agent (C{LOCAL} or C{SSH})
+ @type query_mode: C{unicode}
+ @param remote_agent: information about the remote agent that is to be called.
+ @type remote_agent: C{dict}
"""
- return call_broker_agent(username, mode='getservers')
+ if query_mode == 'LOCAL':
+ return call_local_broker_agent(username, mode='getservers')
+ else:
+ return call_local_broker_agent(username, mode='getservers', remote_agent=remote_agent)
diff --git a/x2gobroker/brokers/base_broker.py b/x2gobroker/brokers/base_broker.py
index be8b4e8..1f71430 100644
--- a/x2gobroker/brokers/base_broker.py
+++ b/x2gobroker/brokers/base_broker.py
@@ -421,10 +421,31 @@ class X2GoBroker(object):
if self.config.has_value(self.backend_name, 'auth-mech'):
_auth_mech = self.config.get_value(self.backend_name, 'auth-mech').lower()
- logger_broker.debug('base_broker.X2GoBroker.get_authentication_mechanism(): found auth-mech in backend config section »{backend}«: {value}'.format(backend=self.backend_name, value=_auth_mech))
+ logger_broker.debug('base_broker.X2GoBroker.get_authentication_mechanism(): found auth-mech in backend config section »{backend}«: {value}. This one has precendence over the default value.'.format(backend=self.backend_name, value=_auth_mech))
return unicode(_auth_mech) or unicode(_default_auth_mech)
+ def get_agent_query_mode(self):
+ """\
+ Get the agent query mode (LOCAL or SSH, normally) that is configured for this
+ X2Go Session Broker instance.
+
+ @return: agent query mode
+ @rtype: C{unicode}
+
+ """
+ _default_agent_query_mode = "LOCAL"
+ _agent_query_mode = ""
+ if self.config.has_value('global', 'default-agent-query-mode'):
+ _default_agent_query_mode = self.config.get_value('global', 'default-agent-query-mode').lower()
+ logger_broker.debug('base_broker.X2GoBroker.get_agent_query_mode(): found default-agent-query-mode in global config section: {value}'.format(value=_default_agent_query_mode))
+
+ if self.config.has_value(self.backend_name, 'agent-query-mode'):
+ _agent_query_mode = self.config.get_value(self.backend_name, 'agent-query-mode').lower()
+ logger_broker.debug('base_broker.X2GoBroker.get_agent_query_mode(): found agent-query-mode in backend config section »{backend}«: {value}. This one has precendence over the default value.'.format(backend=self.backend_name, value=_agent_query_mode))
+
+ return unicode(_agent_query_mode) or unicode(_default_agent_query_mode)
+
def get_userdb_service(self):
"""\
Get the name of the backend being used for retrieving user information from the
@@ -694,7 +715,7 @@ class X2GoBroker(object):
return list_of_profiles
- def select_session(self, profile_id, username=None):
+ def select_session(self, profile_id, username):
"""\
Start/resume a session by selecting a profile name offered by the X2Go client.
@@ -711,11 +732,29 @@ class X2GoBroker(object):
# if we have more than one server, pick one server randomly for X2Go Broker Agent queries
server_list = profile[u'host']
- random.shuffle(server_list)
- agent_query_server = server_list[0]
+ agent_query_mode = self.get_agent_query_mode()
if len(server_list) >= 2:
- best_server = x2gobroker.agent.find_best_server()
+
+ remote_agent = None
+ if agent_query_mode == 'SSH':
+ random.shuffle(server_list)
+ remote_agent_server = server_list[0]
+ remote_agent_port = profile[u'sshport']
+ remote_agent = {u'hostname': remote_agent_server, u'port': remote_agent_port, }
+
+ busy_servers = x2gobroker.agent.find_busy_servers(username=username, query_mode=agent_query_mode, remote_agent = remote_agent)
+
+ for server in server_list:
+ if server not in busy_servers.keys():
+ busy_servers[server] = 0
+
+ busy_server_list = [ (load, server) for server, load in busy_servers.items() ]
+ busy_server_list.sort()
+ print busy_server_list
+
+ best_server = busy_server_list[0][1]
+
else:
best_server = server_list[0]
diff --git a/x2gobroker/defaults.py b/x2gobroker/defaults.py
index 720fa94..e777142 100644
--- a/x2gobroker/defaults.py
+++ b/x2gobroker/defaults.py
@@ -109,6 +109,7 @@ X2GOBROKER_CONFIG_DEFAULTS = {
u'default-user-db': u'libnss',
u'default-group-db': u'libnss',
u'ignore-primary-group-memberships': True,
+ u'default-agent-query-mode': u'LOCAL',
},
'zeroconf': {
u'enable': True,
@@ -135,6 +136,7 @@ X2GOBROKER_CONFIG_DEFAULTS = {
u'host-search-filter': u'(&(objectClass=ipHost)(serial=X2GoServer)(cn=*))',
u'group-search-filter': u'(&(objectClass=posifxGroup)(cn=*))',
u'starttls': False,
+ u'agent-query-mode': u'SSH',
},
}
hooks/post-receive
--
x2gobroker.git (HTTP(S) Session broker for X2Go)
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "x2gobroker.git" (HTTP(S) Session broker for X2Go).
More information about the x2go-commits
mailing list