[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