31
32
from threading import Thread, Event
32
33
from weblive_pristine import WebLive
34
class ServerNotReadyError(Exception):
34
import softwarecenter.paths
37
36
class WebLiveBackend(object):
38
""" backend for interacting with the weblive service """
37
""" Backend for interacting with the WebLive service """
39
# Check if x2go module exists but don't load it (gevent breaks everything)
41
imp.find_module("x2go")
41
47
NXML_TEMPLATE = """
79
""" return true if data from the remote server was loaded
84
""" Return true if data from the remote server was loaded
81
86
return self._ready.is_set()
84
89
def is_supported(cls):
85
""" return if the current system will work (has the required
90
""" Return if the current system will work
91
(has the required dependencies)
88
# FIXME: also test if package is available on the weblive server
89
if os.path.exists(cls.QTNX):
93
if cls.X2GO or os.path.exists(cls.QTNX):
93
97
def query_available(self):
94
""" (sync) get available server and limits """
98
""" Get all the available data from WebLive """
95
99
servers=self.weblive.list_everything()
98
102
def query_available_async(self):
99
""" query available in a thread and set self.ready """
103
""" Call query_available in a thread and set self.ready """
100
104
def _query_available_helper():
101
105
self.available_servers = self.query_available()
102
106
self._ready.set()
125
133
servers.append(server)
128
def create_automatic_user_and_run_session(self, serverid=None,
136
def create_automatic_user_and_run_session(self, serverid,
129
137
session="desktop", wait=False):
130
""" login into serverid and automatically create a user """
132
serverid = self.DEFAULT_SERVER
138
""" Create a user on 'serverid' and start the session """
140
# Use the boot_id to get a temporary unique identifier (till next reboot)
134
141
if os.path.exists('/proc/sys/kernel/random/boot_id'):
135
142
uuid=open('/proc/sys/kernel/random/boot_id','r').read().strip().replace('-','')
136
143
random.seed(uuid)
145
# Generate a 20 characters string based on the boot_id
137
146
identifier=''.join(random.choice(string.ascii_lowercase) for x in range (20))
148
# Use the current username as the GECOS on the server
149
# if it's invalid (by weblive's standard), use "WebLive user" instead
139
150
fullname=str(os.environ.get('USER','WebLive user'))
140
151
if not re.match("^[A-Za-z0-9 ]*$",fullname) or len(fullname) == 0:
141
152
fullname='WebLive user'
154
# Send the user's locale so it's automatically selected when connecting
143
155
locale=os.environ.get("LANG","None").replace("UTF-8","utf8")
157
# Create the user and retrieve host and port of the target server
145
158
connection=self.weblive.create_user(serverid, identifier, fullname, identifier, session, locale)
146
self._spawn_qtnx(connection[0], connection[1], session, identifier, identifier, wait)
160
# Connect using x2go or fallback to qtnx if not available
162
self._spawn_x2go(connection[0], connection[1], session, identifier, identifier, wait)
163
elif (os.path.exists(self.QTNX)):
164
self._spawn_qtnx(connection[0], connection[1], session, identifier, identifier, wait)
166
raise IOError("No remote desktop client available.")
148
170
def _spawn_qtnx(self, host, port, session, username, password, wait):
149
if not os.path.exists(self.QTNX):
150
raise IOError("qtnx not found")
171
""" Start a session using qtnx """
151
173
if not os.path.exists(os.path.expanduser('~/.qtnx')):
152
174
os.mkdir(os.path.expanduser('~/.qtnx'))
176
# Generate qtnx's configuration file
153
177
filename=os.path.expanduser('~/.qtnx/%s-%s-%s.nxml') % (
154
178
host, port, session.replace("/","_"))
155
179
nxml=open(filename,"w+")
161
185
nxml.write(config)
164
189
cmd = [self.QTNX,
165
190
'%s-%s-%s' % (str(host), str(port), session.replace("/","_")),
169
194
if wait == False:
195
# Start in the background and attach a watch for when it exits
170
196
(pid, stdin, stdout, stderr) = glib.spawn_async(
171
197
cmd, flags=glib.SPAWN_DO_NOT_REAP_CHILD)
172
198
glib.child_watch_add(pid, self._on_qtnx_exit,filename)
200
# Start it and wait till it finishes
174
201
p=subprocess.Popen(cmd)
177
204
def _on_qtnx_exit(self, pid, status, filename):
205
""" Called when the qtnx process exits (when in the background) """
207
# Remove configuration file
178
208
if os.path.exists(filename):
179
209
os.remove(filename)
213
def _spawn_x2go(self, host, port, session, username, password, wait):
214
""" Start a session using x2go """
216
# Start in the background and attach a watch for when it exits
217
cmd = [os.path.join(softwarecenter.paths.datadir, softwarecenter.paths.X2GO_HELPER)]
218
(pid, stdin, stdout, stderr) = glib.spawn_async(
219
cmd, standard_input=True, standard_output=True, standard_error=True)
220
glib.child_watch_add(pid, self._on_x2go_exit)
222
# Add a watch on stdout
223
glib.io_add_watch(os.fdopen(stdout), glib.IO_IN, self._on_x2go_activity)
225
# Start the connection
226
os.fdopen(stdin,"w").write("CONNECT: \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"" % (host, port, username, password, session))
228
def _on_x2go_exit(self, pid, status):
229
print "x2go: exit with status: %s" % status
231
def _on_x2go_activity(self, stdout, condition):
232
print "x2go: received: %s" % stdout.readline().strip()
182
236
_weblive_backend = None
183
237
def get_weblive_backend():
190
244
return _weblive_backend
192
246
if __name__ == "__main__":
247
# Contact the weblive daemon to get all servers
193
248
weblive = get_weblive_backend()
194
249
weblive.query_available_async()
195
250
weblive._ready.wait()
252
# Show the currently available servers
197
253
print weblive.available_servers
200
weblive.create_automatic_user_and_run_session(session="firefox",wait=True)
255
# Start firefox on the first available server and wait for it to finish
256
weblive.create_automatic_user_and_run_session(serverid=weblive.available_servers[0].name,session="firefox",wait=True)