~mterry/software-center/network-manager-0.9

« back to all changes in this revision

Viewing changes to softwarecenter/backend/weblive.py

  • Committer: Michael Vogt
  • Date: 2011-05-31 07:26:24 UTC
  • mfrom: (1797.1.10 weblive-x2go)
  • Revision ID: michael.vogt@ubuntu.com-20110531072624-d313lrhn0hybayvt
merged lp:~weblive-dev/software-center/weblive-x2go, many thanks
to Stephane Graber!

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
import random
28
28
import subprocess
29
29
import string
 
30
import imp
30
31
 
31
32
from threading import Thread, Event
32
33
from weblive_pristine import WebLive
33
 
 
34
 
class ServerNotReadyError(Exception):
35
 
    pass
 
34
import softwarecenter.paths
36
35
 
37
36
class WebLiveBackend(object):
38
 
    """ backend for interacting with the weblive service """
 
37
    """ Backend for interacting with the WebLive service """
 
38
 
 
39
    # Check if x2go module exists but don't load it (gevent breaks everything)
 
40
    try:
 
41
        imp.find_module("x2go")
 
42
        X2GO=True
 
43
    except:
 
44
        X2GO=False
39
45
 
40
46
    # NXML template
41
47
    NXML_TEMPLATE = """
67
73
    URL = os.environ.get('SOFTWARE_CENTER_WEBLIVE_HOST',
68
74
        'https://weblive.stgraber.org/weblive/json')
69
75
    QTNX = "/usr/bin/qtnx"
70
 
    DEFAULT_SERVER = "ubuntu-natty01"
71
76
 
72
77
    def __init__(self):
73
78
        self.weblive = WebLive(self.URL,True)
76
81
 
77
82
    @property
78
83
    def ready(self):
79
 
        """ return true if data from the remote server was loaded
 
84
        """ Return true if data from the remote server was loaded
80
85
        """
81
86
        return self._ready.is_set()
82
87
 
83
88
    @classmethod
84
89
    def is_supported(cls):
85
 
        """ return if the current system will work (has the required
86
 
            dependencies
 
90
        """ Return if the current system will work
 
91
            (has the required dependencies)
87
92
        """
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):
90
94
            return True
91
95
        return False
92
96
 
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()
96
100
        return servers
97
101
 
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()
106
110
        p.start()
107
111
 
108
112
    def is_pkgname_available_on_server(self, pkgname, serverid=None):
 
113
        """ Check if the package is available (on all servers or on 'serverid') """
 
114
 
109
115
        for server in self.available_servers:
110
116
            if not serverid or server.name == serverid:
111
117
                for pkg in server.packages:
114
120
        return False
115
121
 
116
122
    def get_servers_for_pkgname(self, pkgname):
 
123
        """ Return a list of servers having a given package """
 
124
 
117
125
        servers=[]
118
126
        for server in self.available_servers:
119
127
            # No point in returning a server that's full
125
133
                    servers.append(server)
126
134
        return servers
127
135
 
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 """
131
 
        if not serverid:
132
 
            serverid = self.DEFAULT_SERVER
 
138
        """ Create a user on 'serverid' and start the session """
133
139
 
 
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)
 
144
 
 
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))
138
147
 
 
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'
142
153
 
 
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")
144
156
 
 
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)
 
159
 
 
160
        # Connect using x2go or fallback to qtnx if not available
 
161
        if (self.X2GO):
 
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)
 
165
        else:
 
166
            raise IOError("No remote desktop client available.")
 
167
 
 
168
# qtnx backend
147
169
 
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 """
 
172
 
151
173
        if not os.path.exists(os.path.expanduser('~/.qtnx')):
152
174
            os.mkdir(os.path.expanduser('~/.qtnx'))
 
175
 
 
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)
162
186
        nxml.close()
163
187
 
 
188
        # Prepare qtnx call
164
189
        cmd = [self.QTNX,
165
190
               '%s-%s-%s' % (str(host), str(port), session.replace("/","_")),
166
191
               username,
167
192
               password]
168
193
 
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)
173
199
        else:
 
200
            # Start it and wait till it finishes
174
201
            p=subprocess.Popen(cmd)
175
202
            p.wait()
176
203
 
177
204
    def _on_qtnx_exit(self, pid, status, filename):
 
205
        """ Called when the qtnx process exits (when in the background) """
 
206
 
 
207
        # Remove configuration file
178
208
        if os.path.exists(filename):
179
209
            os.remove(filename)
180
210
 
 
211
# x2go backend
 
212
 
 
213
    def _spawn_x2go(self, host, port, session, username, password, wait):
 
214
        """ Start a session using x2go """
 
215
 
 
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)
 
221
 
 
222
        # Add a watch on stdout
 
223
        glib.io_add_watch(os.fdopen(stdout), glib.IO_IN, self._on_x2go_activity)
 
224
 
 
225
        # Start the connection
 
226
        os.fdopen(stdin,"w").write("CONNECT: \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"" % (host, port, username, password, session))
 
227
 
 
228
    def _on_x2go_exit(self, pid, status):
 
229
        print "x2go: exit with status: %s" % status
 
230
 
 
231
    def _on_x2go_activity(self, stdout, condition):
 
232
        print "x2go: received: %s" % stdout.readline().strip()
 
233
        return True
 
234
 
181
235
# singleton
182
236
_weblive_backend = None
183
237
def get_weblive_backend():
190
244
    return _weblive_backend
191
245
 
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()
196
251
 
 
252
    # Show the currently available servers
197
253
    print weblive.available_servers
198
254
 
199
 
    # run session
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)