Package flumotion :: Package admin :: Package gtk :: Module greeter
[hide private]

Source Code for Module flumotion.admin.gtk.greeter

  1  # -*- Mode: Python -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3  # 
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007 Fluendo, S.L. (www.fluendo.com). 
  6  # All rights reserved. 
  7   
  8  # This file may be distributed and/or modified under the terms of 
  9  # the GNU General Public License version 2 as published by 
 10  # the Free Software Foundation. 
 11  # This file is distributed without any warranty; without even the implied 
 12  # warranty of merchantability or fitness for a particular purpose. 
 13  # See "LICENSE.GPL" in the source distribution for more information. 
 14   
 15  # Licensees having purchased or holding a valid Flumotion Advanced 
 16  # Streaming Server license may use this file in accordance with the 
 17  # Flumotion Advanced Streaming Server Commercial License Agreement. 
 18  # See "LICENSE.Flumotion" in the source distribution for more information. 
 19   
 20  # Headers in this file shall remain intact. 
 21   
 22  import os 
 23  import errno 
 24   
 25  import gobject 
 26   
 27  from gettext import gettext as _ 
 28   
 29  from twisted.internet import reactor, protocol, defer, error 
 30   
 31  from flumotion.common.pygobject import gsignal 
 32  from flumotion.configure import configure 
 33  from flumotion.admin.gtk import wizard 
 34   
 35   
 36   
 37  # A wizard run when the user first starts flumotion. 
 38   
 39   
 40  # personal note: these things duplicate to a large extent the code in 
 41  # flumotion.wizard.steps. A bit irritating to find that out after 
 42  # hacking on it for a bit. 
 43   
 44   
 45  # Page classes (see wizard.py for details) 
 46   
47 -class Initial(wizard.WizardStep):
48 name = 'initial' 49 title = _('Connect to Flumotion manager') 50 text = _('Flumotion Admin needs to connect to a Flumotion manager.\n') + \ 51 _('Choose an option from the list and click "Forward" to begin.') 52 connect_to_existing = None 53 next_pages = ['load_connection', 'connect_to_existing', 'start_new'] 54
55 - def on_next(self, state):
56 radio_buttons = self.connect_to_existing.get_group() 57 58 for i in range(len(radio_buttons)): 59 if radio_buttons[i].get_active(): 60 return radio_buttons[i].get_name() 61 raise AssertionError
62
63 - def setup(self, state, available_pages):
64 # the group of radio buttons is named after the first check button 65 radio_buttons = self.load_connection.get_group() 66 for w in radio_buttons: 67 w.set_sensitive(w.get_name() in available_pages) 68 if w.get_active() and not w.get_property('sensitive'): 69 getattr(self, available_pages[0]).set_active(True)
70 71
72 -class ConnectToExisting(wizard.WizardStep):
73 name = 'connect_to_existing' 74 title = _('Host information') 75 text = _('Please enter the address where the manager is running.') 76 next_pages = ['authenticate'] 77 open_connection = None 78
79 - def setup(self, state, available_pages):
80 try: 81 oc_state = [(k, state[k]) for k in ('host', 'port', 'use_insecure')] 82 self.open_connection.set_state(dict(oc_state)) 83 except KeyError: 84 pass 85 self.open_connection.grab_focus()
86
87 - def on_can_activate(self, obj, *args):
88 self.button_next.set_sensitive(obj.get_property('can-activate'))
89
90 - def on_next(self, state):
91 for k, v in self.open_connection.get_state().items(): 92 state[k] = v 93 return 'authenticate'
94 95
96 -class Authenticate(wizard.WizardStep):
97 name = 'authenticate' 98 title = _('Authentication') 99 text = _('Please select among the following authentication methods.') 100 auth_method_combo = user_entry = passwd_entry = None 101 next_pages = [] 102 103 authenticate = None 104
105 - def setup(self, state, available_pages):
106 try: 107 oc_state = [(k, state[k]) for k in ('user', 'passwd')] 108 self.authenticate.set_state(dict(oc_state)) 109 except KeyError: 110 self.authenticate.set_state(None) 111 self.authenticate.grab_focus() 112 self.on_can_activate(self.authenticate)
113
114 - def on_can_activate(self, obj, *args):
115 self.button_next.set_sensitive(obj.get_property('can-activate'))
116
117 - def on_next(self, state):
118 for k, v in self.authenticate.get_state().items(): 119 state[k] = v 120 return '*finished*'
121 122
123 -class LoadConnection(wizard.WizardStep):
124 name = 'load_connection' 125 title = _('Recent connections') 126 text = _('Please choose a connection from the box below.') 127 connections = None 128 next_pages = [] 129
130 - def is_available(self):
131 return self.connections.get_selected()
132
133 - def on_has_selection(self, widget, has_selection):
134 self.button_next.set_sensitive(has_selection)
135
136 - def on_connection_activated(self, widget, state):
137 self.button_next.emit('clicked')
138
139 - def on_next(self, state):
140 info = self.connections.get_selected() 141 for k, v in (('host', info.host), ('port', info.port), 142 ('use_insecure', not info.use_ssl), 143 ('user', info.authenticator.username), 144 ('passwd', info.authenticator.password)): 145 state[k] = v 146 return '*finished*'
147
148 - def setup(self, state, available_pages):
149 self.connections.grab_focus()
150
151 -class GreeterProcessProtocol(protocol.ProcessProtocol):
152 - def __init__(self):
153 # no parent init 154 self.deferred = defer.Deferred()
155
156 - def processEnded(self, failure):
157 if failure.check(error.ProcessDone): 158 self.deferred.callback(None) 159 else: 160 self.deferred.callback(failure)
161
162 -class StartNew(wizard.WizardStep):
163 name = 'start_new' 164 title = _('Start a new manager and worker') 165 text = _("""This will start a new manager and worker for you. 166 167 The manager and worker will run under your user account. 168 The manager will only accept connections from the local machine. 169 This mode is only useful for testing Flumotion. 170 """) 171 start_worker_check = None 172 next_pages = ['start_new_error', 'start_new_success'] 173 gsignal('finished', str) 174 175 _timeout_id = None 176
177 - def on_has_selection(self, widget, has_selection):
178 self.button_next.set_sensitive(has_selection)
179
180 - def on_next(self, state):
181 self.label_starting.show() 182 self.progressbar_starting.set_fraction(0.0) 183 self.progressbar_starting.show() 184 # start a manager first 185 import socket 186 port = 7531 187 def tryPort(port=0): 188 # tries the given port, or a random one, and return None or port 189 # number 190 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 191 self.debug('Binding to port %d' % port) 192 193 try: 194 s.bind(('', port)) 195 port = s.getsockname()[1] 196 except socket.error, e: 197 if e.args[0] == errno.EADDRINUSE: 198 self.debug('Port %d already in use', port) 199 port = None 200 201 s.close() 202 return port
203 204 if tryPort(port) is None: 205 port = tryPort() 206 207 # ready to start spawning processes 208 209 def pulse(): 210 self.progressbar_starting.pulse() 211 return True
212 213 self._timeout_id = gobject.timeout_add(200, pulse) 214 215 import tempfile 216 path = tempfile.mkdtemp(suffix='.flumotion') 217 confDir = os.path.join(path, 'etc') 218 logDir = os.path.join(path, 'var', 'log') 219 runDir = os.path.join(path, 'var', 'run') 220 221 # We need to run 4 commands in a row, and each of them can fail 222 d = defer.Deferred() 223 def run(result, args, description, failMessage): 224 # run the given command 225 # show a dialog to say what we are doing 226 self.label_starting.set_text(description) 227 args[0] = os.path.join(configure.sbindir, args[0]) 228 protocol = GreeterProcessProtocol() 229 env = os.environ.copy() 230 paths = env['PATH'].split(os.pathsep) 231 if configure.bindir not in paths: 232 paths.insert(0, configure.bindir) 233 env['PATH'] = os.pathsep.join(paths) 234 process = reactor.spawnProcess(protocol, args[0], args, env=env) 235 def error(failure, failMessage): 236 self.label_starting.set_text('Failed to %s' % description) 237 # error should trigger going to next page with an overview 238 state.update({ 239 'command': ' '.join(args), 240 'error': failMessage, 241 'failure': failure, 242 }) 243 self.finished('start_new_error') 244 return failure 245 protocol.deferred.addErrback(error, failMessage) 246 return protocol.deferred 247 248 def chain(args, description, failMessage): 249 d.addCallback(run, args, description, failMessage) 250 251 chain(["flumotion", "-C", confDir, "-L", logDir, "-R", runDir, 252 "create", "manager", "admin", str(port)], 253 _('Creating manager ...'), 254 _("Could not create manager.")) 255 256 chain(["flumotion", "-C", confDir, "-L", logDir, "-R", runDir, 257 "start", "manager", "admin"], 258 _('Starting manager ...'), 259 _("Could not start manager.")) 260 261 chain(["flumotion", "-C", confDir, "-L", logDir, "-R", runDir, 262 "create", "worker", "admin", str(port)], 263 _('Creating worker ...'), 264 _("Could not create worker.")) 265 266 chain(["flumotion", "-C", confDir, "-L", logDir, "-R", runDir, 267 "start", "worker", "admin"], 268 _('Starting worker ...'), 269 _("Could not start worker.")) 270 271 d.addErrback(lambda f: None) 272 273 def done(result, state): 274 # because of the ugly call-by-reference passing of state, 275 # we have to update the existing dict, not re-bind with state = 276 state.update({ 277 'host': 'localhost', 278 'port': port, 279 'use_insecure': False, 280 'user': 'user', 281 'passwd': 'test', 282 'confDir': confDir, 283 'logDir': logDir, 284 'runDir': runDir, 285 }) 286 self.finished('start_new_success') 287 288 d.addCallback(done, state) 289 290 # start chain 291 d.callback(None) 292 return '*signaled*' 293
294 - def finished(self, result):
295 # result: start_new_error or start_new_success 296 self.label_starting.hide() 297 self.progressbar_starting.hide() 298 gobject.source_remove(self._timeout_id) 299 self.emit('finished', result)
300
301 -class StartNewError(wizard.WizardStep):
302 name = 'start_new_error' 303 title = _('Failed to start') 304 text = "" 305 start_worker_check = None 306 next_pages = [] 307
308 - def setup(self, state, available_pages):
309 self.button_next.set_sensitive(False) 310 self.message.set_text(state['error']) 311 f = state['failure'] 312 result = "" 313 if f.value.exitCode is not None: 314 result = _('The command exited with an exit code of %d.' % 315 f.value.exitCode) 316 self.more.set_markup(_("""The command that failed was: 317 <i>%s</i> 318 %s""") % (state['command'], result))
319 320
321 -class StartNewSuccess(wizard.WizardStep):
322 name = 'start_new_success' 323 title = _('Started manager and worker') 324 start_worker_check = None 325 text = '' 326 next_pages = [] 327
328 - def setup(self, state, available_pages):
329 executable = os.path.join(configure.sbindir, 'flumotion') 330 confDir = state['confDir'] 331 logDir = state['logDir'] 332 runDir = state['runDir'] 333 stop = "%s -C %s -L %s -R %s stop" % ( 334 executable, confDir, logDir, runDir) 335 self.message.set_markup(_( 336 """The admin client will now connect to the manager. 337 338 Configuration files are stored in 339 <i>%s</i> 340 Log files are stored in 341 <i>%s</i> 342 343 You can shut down the manager and worker later with the following command: 344 345 <i>%s</i> 346 """) % (confDir, logDir, stop))
347
348 - def on_next(self, state):
349 return '*finished*'
350
351 -class Greeter(wizard.Wizard):
352 name = 'greeter' 353 steps = [Initial, ConnectToExisting, Authenticate, LoadConnection, 354 StartNew, StartNewError, StartNewSuccess] 355
356 - def __init__(self):
357 wizard.Wizard.__init__(self, 'initial')
358 359 # This is used by the gtk admin to connect to an existing manager
360 -class ConnectExisting(wizard.Wizard):
361 name = 'greeter' 362 steps = [ConnectToExisting, Authenticate] 363
364 - def __init__(self):
365 wizard.Wizard.__init__(self, 'connect_to_existing')
366