[X2Go-Commits] [x2gobroker] 06/06: Provide support for load-balancing to hosts that are all reachable over the same IP address, but different TCP/IP ports (e.g. docker instances or hosts behind a reverse NATed IPv4 gateway). This ended up in a rewrite of the complete selection_session() method of the base broker code.
git-admin at x2go.org
git-admin at x2go.org
Thu Sep 11 22:38:32 CEST 2014
This is an automated email from the git hooks/post-receive script.
x2go pushed a commit to branch master
in repository x2gobroker.
commit 7e96a3af36a232fb7e4fbe6222e336b69ffe5794
Author: Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
Date: Thu Sep 11 22:38:17 2014 +0200
Provide support for load-balancing to hosts that are all reachable over the same IP address, but different TCP/IP ports (e.g. docker instances or hosts behind a reverse NATed IPv4 gateway). This ended up in a rewrite of the complete selection_session() method of the base broker code.
---
debian/changelog | 5 ++
x2gobroker/brokers/base_broker.py | 162 ++++++++++++++++++++--------------
x2gobroker/brokers/inifile_broker.py | 5 ++
3 files changed, 108 insertions(+), 64 deletions(-)
diff --git a/debian/changelog b/debian/changelog
index 4e79add..4f207a1 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -152,6 +152,11 @@ x2gobroker (0.0.3.0-0x2go1) UNRELEASED; urgency=low
/etc/default/x2gobroker-daemon and /etc/x2go/broker/defaults.cfg.
- Move split_host_address() code into x2gobroker.utils.
- Report to log what the broker agent replied to us.
+ - Provide support for load-balancing to hosts that are all reachable
+ over the same IP address, but different TCP/IP ports (e.g. docker
+ instances or hosts behind a reverse NATed IPv4 gateway). This ended
+ up in a rewrite of the complete selection_session() method of the
+ base broker code.
* debian/control:
+ Provide separate bin:package for SSH brokerage: x2gobroker-ssh.
+ Replace LDAP support with session brokerage support in LONG_DESCRIPTION.
diff --git a/x2gobroker/brokers/base_broker.py b/x2gobroker/brokers/base_broker.py
index e92c7d8..b72576d 100644
--- a/x2gobroker/brokers/base_broker.py
+++ b/x2gobroker/brokers/base_broker.py
@@ -1053,19 +1053,81 @@ class X2GoBroker(object):
try:
profile = self.get_profile(profile_id)
except x2gobroker.x2gobroker_exceptions.X2GoBrokerProfileException:
- return {}
+ return { 'server': 'no-server-available', 'port': 22, }
# if we have more than one server, pick one server randomly for X2Go Broker Agent queries
server_list = profile[u'host']
+ if len(server_list) == 0:
+ return { 'server': 'no-server-available', 'port': profile['sshport'], }
+
+ # if everything below fails, this will be the X2Go Server's hostname that
+ # we will connect to...
+ server_name = server_list[0]
+ server_port = profile['sshport']
+
+ # try to retrieve contact to a remote broker agent
remote_agent = self.get_remote_agent(profile_id)
if remote_agent:
agent_query_mode = ( remote_agent == u'LOCAL') and u'LOCAL' or u'SSH'
+ # check for already running sessions for the given user (if any is given)
+ session_list = []
+ if remote_agent and username:
+ try:
+ success, session_list = x2gobroker.agent.list_sessions(username=username, remote_agent=remote_agent)
+ except x2gobroker.x2gobroker_exceptions.X2GoBrokerAgentException:
+ session_list = []
+
+ session_info = None
+ if session_list:
+
+ # Obviously a remote broker agent reported an already running session
+ # on the / on one the available X2Go Server host(s)
+
+ # When resuming, always select the first session in the list,
+ # there should only be one running/suspended session by design
+ # of X2Go brokerage (this may change in the future)
+ try:
+ running_sessions = []
+ suspended_sessions = []
+ for session_info in session_list:
+ if session_info.split('|')[4] == 'R':
+ running_sessions.append(session_info)
+ if session_info.split('|')[4] == 'S':
+ suspended_sessions.append(session_info)
+
+ # we prefer suspended sessions over resuming sessions if we find sessions with both
+ # states of activity
+ if suspended_sessions:
+ session_info = suspended_sessions[0]
+ elif running_sessions:
+ session_info = running_sessions[0]
+ x2gobroker.agent.suspend_session(username=username, session_name=session_info.split('|')[1], remote_agent=remote_agent)
+ # this is the turn-around in x2gocleansessions, so waiting as along as the daemon
+ # that will suspend the session
+ time.sleep(2)
+ session_info = session_info.replace('|R|', '|S|')
+
+ # only use the server's official hostname (as set on the server)
+ # if we know about the real/physical address of the server
+ _session_server_name = session_info.split('|')[3]
+ if profile.has_key('host={server_name}'.format(server_name=_session_server_name)):
+ server_name = _session_server_name
+
+ except IndexError:
+ # FIXME: if we get here, we have to deal with a broken session info
+ # entry in the X2Go session database. -> AWFUL!!!
+ pass
+
# detect best X2Go server for this user if load balancing is configured
- if remote_agent and len(server_list) >= 2 and username:
+ elif remote_agent and len(server_list) >= 2 and username:
- # query remote agent for session info
+ # No running / suspended session was found on any of the available
+ # X2Go Servers. Thus, we will try to detect the best server for this
+ # load balanced X2Go Server farm.
+
+ # query remote agent on how busy our servers are...
busy_servers = None
try:
success, busy_servers = x2gobroker.agent.find_busy_servers(username=username, remote_agent=remote_agent)
@@ -1078,20 +1140,23 @@ class X2GoBroker(object):
# when detecting the server load we have to support handling of differing subdomains (config
# file vs. server load returned by x2gobroker agent). Best approach: all members of a multi-node
- # server farm either (a) do not have a subdomain in their hostname or (b) the subdomain is
- # is identical.
+ # server farm either
+ #
+ # (a) do not have a subdomain in their hostname or
+ # (b) have an identical subdomain in their hostnames
# Example:
#
# ts01, ts02 - hostnames as returned by agent
# ts01.intern, ts02.intern - hostnames configured in session profile option ,,host''
- # -> this will result in the subdomain .intern being stripped off from the hostnames before detecting
- # the best server for this user
+ # -> this will result in the subdomain .intern being stripped off from the hostnames before
+ # detecting the best server for this user
### NORMALIZE (=reduce to hostname only) X2Go server names (as found in config) if possible
server_list_normalized, subdomains_config = x2gobroker.utils.normalize_hostnames(server_list)
- ### NORMALIZE X2Go server names (as returned by x2gobroker agent)--only if the hostnames in the config share the same subdomain
+ ### NORMALIZE X2Go server names (as returned by broker agent)--only if the hostnames in
+ # the config share the same subdomain
if len(subdomains_config) == 1:
busy_servers_normalized, subdomains_agent = x2gobroker.utils.normalize_hostnames(busy_servers)
@@ -1110,74 +1175,43 @@ class X2GoBroker(object):
logger_broker.debug('base_broker.X2GoBroker.select_session(): load balancer analysis: {server_load}'.format(server_load=unicode(busy_server_list)))
- best_server = busy_server_list[0][1]
+ server_name = busy_server_list[0][1]
else:
logger_broker.warning('base_broker.X2GoBroker.select_session(): no broker agent could be contacted, this does not look good. We tried these agent hosts: {agent_hosts}'.format(agent_hosts=unicode(server_list)))
- if server_list: best_server = server_list[0]
- else: return { 'server': 'no-server-available', 'port': profile[u'sshport'], }
- else:
- if server_list: best_server = server_list[0]
- else: return { 'server': 'no-server-available', 'port': profile[u'sshport'], }
+ # detect best X2Go server for this user if load balancing is configured
+ elif len(server_list) >= 2:
+
+ # no remote broker agent or no username? Let's play roulette then...
+ server_name = random.choice(server_list)
+
+ ###
+ ### by now we should know the proper host to connect to...
+ ###
- # if we have an explicit IP address for best_server, let's use that instead...
+ # if we have an explicit TCP/IP port server_name, let's use that instead...
try:
- best_server = profile['host={hostname}'.format(hostname=best_server)]
+ server_port = profile['sshport={hostname}'.format(hostname=server_name)]
+ logger_broker.debug('base_broker.X2GoBroker.select_session(): use physical server port: {port}'.format(port=server_port))
+ except KeyError:
+ pass
+
+ # if we have an explicit TCP/IP address for server_name, let's use that instead...
+ try:
+ server_name = profile['host={hostname}'.format(hostname=server_name)]
+ logger_broker.debug('base_broker.X2GoBroker.select_session(): use physical server address: {address}'.format(address=server_name))
except KeyError:
pass
selected_session = {
- 'server': best_server,
- 'port': profile[u'sshport'],
+ 'server': server_name,
+ 'port': server_port,
}
- # find already running/suspended sessions and resume the first one found
- if remote_agent and server_list and username:
-
- try:
- success, session_list = x2gobroker.agent.list_sessions(username=username, remote_agent=remote_agent)
- except x2gobroker.x2gobroker_exceptions.X2GoBrokerAgentException:
- session_list = []
-
- if session_list:
-
- # if resuming, always select the first session in the list, there should only be one suspended session
- try:
- running_sessions = []
- suspended_sessions = []
- for session_info in session_list:
- if session_info.split('|')[4] == 'R':
- running_sessions.append(session_info)
- if session_info.split('|')[4] == 'S':
- suspended_sessions.append(session_info)
-
- # we prefer suspended sessions for resuming if we find sessions with both states of activity
- if suspended_sessions:
- session_info = suspended_sessions[0]
- else:
- session_info = running_sessions[0]
- x2gobroker.agent.suspend_session(username=username, session_name=session_info.split('|')[1], remote_agent=remote_agent)
- # this is the turn-around in x2gocleansessions, so waiting as along as the daemon that will suspend the session
- time.sleep(2)
- session_info = session_info.replace('|R|', '|S|')
- server_name = session_info.split('|')[3]
-
- # if we have an explicit IP address for server_name, let's use that instead...
- try:
- server_name = profile['host={hostname}'.format(hostname=server_name)]
- except KeyError:
- pass
-
- selected_session.update({
- 'server': server_name,
- 'session_info': session_info,
- })
-
- except IndexError:
-
- # FIXME: if we get here, we have to deal with a broker session info entry in the X2Go session database
- pass
+ # are we resuming a running/suspended session?
+ if session_info is not None:
+ selected_session['session_info'] = session_info
# define a remote SSH proxy agent if an SSH proxy host is used with this session profile
if profile.has_key(u'sshproxyhost') and profile[u'sshproxyhost']:
diff --git a/x2gobroker/brokers/inifile_broker.py b/x2gobroker/brokers/inifile_broker.py
index 81a69cb..18341bf 100644
--- a/x2gobroker/brokers/inifile_broker.py
+++ b/x2gobroker/brokers/inifile_broker.py
@@ -87,11 +87,14 @@ class X2GoBroker(base.X2GoBroker):
del profile[key]
if key == 'host':
_hosts = copy.deepcopy(profile[key])
+ try: _default_sshport = int(profile['sshport'])
+ except TypeError: _default_sshport = 22
profile[key] = []
for host in _hosts:
if re.match('^.*\ \(.*\)$', host):
_hostname = host.split(' ')[0]
_address = host.split(' ')[1][1:-1]
+ _address, _port = x2gobroker.utils.split_host_address(_address, default_port=_default_sshport)
# test if _address is a valid hostname, a valid DNS name or an IPv4/IPv6 address
if (re.match('(?!-)[A-Z\d-]{1,63}(?<!-)$', _hostname, flags=re.IGNORECASE) or \
@@ -100,6 +103,8 @@ class X2GoBroker(base.X2GoBroker):
netaddr.valid_ipv4(_address) or netaddr.valid_ipv6(_address)):
profile["host={hostname}".format(hostname=_hostname)] = _address
+ if _port != _default_sshport:
+ profile["sshport={hostname}".format(hostname=_hostname)] = _port
profile[key].append(_hostname)
else:
--
Alioth's /srv/git/_hooks_/post-receive-email on /srv/git/code.x2go.org/x2gobroker.git
More information about the x2go-commits
mailing list