Configuring a remote session relies a great deal on the resources and requirements of a specific site. Nexus provides some core functionality that simplifies the delivery of VNC-based rendering solutions to end-users. These features include:
Note: all of the files used in examples here can be found in the Nexus distribution as: $CEI_HOME/nexus231/django/utils/remote/config
To enable embedded remote sessions, the "Enable external session" option must be enabled. There are two types of remote sessions.
The first step in the configuration is to consider what types of remote rendering solutions one wants to provide access to.
In this example, we will focus on providing access to a software rendering vncserver-based configuration. Included in the distribution is a simple example that leverages a "start/stop" script running on remote nodes. That example uses an instance of the x11vnc application to provide VNC access to a hardware accelerated X11 server (see the scripts start_x11_server.sh, stop_x11_server.sh, x11vnc_desktop_session.py for an example of how this type of configuration can be realized).
The Nexus remote rendering system utilizes a configuration file in JSON format. The file describes a Python dictionary that contains descriptions of all of the available session types. An example might be:
{
"Local Software Session": {"script": "desktop_session.py", "count": 2,
"options": {"sizes": ["12080x1024", "1600x1200"], "display_base": 100}},
"Cluster Node Session": {"script": "x11vnc_desktop_session.py", "count": 1,
"options": {"sizes": ["1600x1200"], "group": "ClusterRendering", "host": "n8"}}
}
This configuration file defines a single Python dictionary with multiple keys (e.g. "Local Software Session"). Each key is the name of a type of session. The value of each session key is a Python dictionary. There are three required keys in each session dictionary. These include:
Each type of session must provide the name of a Python "script" file. This file must create an instance of a Python object (a subclass of RemoteServiceScript) that is responsible for starting and ending a session. There can be multiple instances of each session type active at the same time. This is limited with the "count" key which specifies the maximum number of simultaneously allowed sessions. When a session of a given type is started, an index number running from zero to this number minus one will be passed to the Python code. The code should use this number to ensure that each session uses non-conflicting resources (e.g. port numbers, node allocations, graphics cards, etc).
Additional pieces of data can be passed via the "options" object. The option dictionary is passed to the RemoteServiceScript creation method as 'common_options' and by default is stored as the _common_options attribute on the RemoteServiceScript subclass instance. The user is encouraged to add any customization options to this dictionary.
The Nexus system uses two keys to configure the HTML page it presents for selecting sessions. The first is 'sizes'. This should be a list of strings that are displayed in the pulldown menu used to start the session. It defaults to a list of common window sizes.
The value selected for sizes is passed in the "options" dictionary to the RemoteServiceScript.start() method.
The Nexus system will also look for the "group" key in the options dictionary. In the example above, this key has been set to the value "ClusterRendering" for instances of the "Cluster Node Session" session type. If the "group" key is set to the name of a Nexus administration group, the session type will only be made available to users who are members of that specific group. In the example, if you are not a member of the "ClusterRendering" group, you will not be allowed to create "Cluster Node Session" sessions.
The "script" file is responsible for actually created and deleting a session. It takes the form of a Python script that creates instances of the core session lifecycle class. Taking a deeper look at the file "desktop_session.py" (which should be located in the same directory as the configuration JSON formatted file):
# Simple example remote service. The loader will call a method to
# realize the services associated with a specific service name.
# All services must subclass RemoteServiceScript.
#
from nexus_remote_service import RemoteServiceScript
import subprocess
import time
# Launch a vnc server (tightvnc) and run an optional command in the token json map file
class SimpleVNC(RemoteServiceScript):
def __init__(self, *args, **kwargs):
super(SimpleVNC, self).__init__(*args, **kwargs)
def display_base(self):
return self._common_options.get("display_base", 100)
def start(self, token, timestamp=time.time(), options=dict()):
username = options.get('username', '')
size = options.get('size', '1600x1200')
cmd = ["vncserver", "-depth", "24", "-geometry", size, ":"+str(self.display_base()+self._index)]
try:
print("SimpleVNC start: {} {}".format(self, cmd))
code = subprocess.call(cmd)
if code != 0:
return False
except:
return False
self.set_host_port("localhost", 5900+self._index+self.display_base())
return super(SimpleVNC, self).start(token, timestamp=timestamp, options=options)
def stop(self):
cmd = ["vncserver", "-kill", ":"+str(self.display_base()+self._index)]
try:
print("SimpleVNC stop: {} {}".format(self, cmd))
code = subprocess.call(cmd)
except:
pass
return super(SimpleVNC, self).stop()
def realize_service(name="", count=1, index=0, common_options=dict()):
print("Realizing SimpleVNC service: {} {} {} {}".format(name, count, index, common_options))
return SimpleVNC(name, count=count, index=index, common_options=common_options)
The Nexus core will import this file and call the function realize_service() with information gleaned from the JSON configuration file. The name will be the session type name (the top level key in the JSON file). The count and common_options keyword values will also have come directly from the JSON file, "count" and "option" keys respectively. realize_service() will be called once for each of the instances that may be legal. This for count=3, it will be called with index=0, index=1 and index=2. realize_service() must respond with an instance of a subclass of the RemoteServiceScript class. This subclass must at least implement the start() and stop() methods. All other methods are optional.
The start() method is called when a session has been authorized, authenticated and is ready to begin (e.g. an upstream entity has requested data over a websocket). In the example, the start() method creates an instance of 'vncserver' using the 'size' option selected in the Nexus web page. It also selects a port number using a base port number specified in the JSON configuration file and the index of this session instance. In a production environment, it can use the authenticated/authorized 'username' from Nexus to select a user target for the session. The start() method must call set_host_port() to set the hostname and port over which the processes it created (vncserver in this example) will be accepting VNC connections. The Nexus system will automatically tunnel this port over the websocket interface and apply any selected SSL encryption to the stream. If the operation fails, the function must return False.
The stop() method is called after all active connections to a specific session instance have closed their connections (e.g. the browser or the specific tab is closed). It can also timeout. It will only be called if start() had previously been called. In our example, it simply calls 'vncserver -kill' with the proper port specification for the instance.
See the Known issues & limitations section for additional notes.