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,2008 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  """greeter interface, displayed when the user first starts flumotion. 
 23  """ 
 24   
 25  import gettext 
 26  import os 
 27  from sys import platform 
 28   
 29  import gobject 
 30  import gtk 
 31  from twisted.internet import reactor 
 32   
 33  from flumotion.admin.connections import hasRecentConnections 
 34  from flumotion.admin.gtk.dialogs import showConnectionErrorDialog 
 35  from flumotion.common.connection import parsePBConnectionInfo 
 36  from flumotion.common.errors import ConnectionFailedError, \ 
 37       ConnectionRefusedError 
 38  from flumotion.common.managerspawner import LocalManagerSpawner 
 39  from flumotion.common.netutils import tryPort 
 40  from flumotion.common.pygobject import gsignal 
 41  from flumotion.configure import configure 
 42  from flumotion.ui.simplewizard import SimpleWizard, WizardStep, \ 
 43       WizardCancelled 
 44   
 45  __version__ = "$Rev: 8057 $" 
 46  _ = gettext.gettext 
 47   
 48   
49 -class Initial(WizardStep):
50 name = 'initial' 51 title = _('Connect to Flumotion Manager') 52 text = (_('Flumotion Admin needs to connect to a Flumotion manager.\n') + 53 _('Choose an option from the list and click "Forward" to begin.')) 54 connect_to_existing = None 55 next_pages = ['load_connection', 56 'connect_to_existing', 57 'start_new'] 58
59 - def __init__(self, wizard, parent):
60 super(Initial, self).__init__(wizard, parent) 61 62 for radio in self.load_connection.get_group(): 63 if radio.name == 'start_new' and platform.find('win') >= 0: 64 radio.hide() 65 else: 66 radio.connect('activate', self._on_radio__activiate)
67 68 # WizardSteps 69
70 - def setup(self, state, available_pages):
71 # the group of radio buttons is named after the first check button 72 for radio in self.load_connection.get_group(): 73 isAvailable = radio.get_name() in available_pages 74 radio.set_sensitive(isAvailable) 75 76 hasRecent = hasRecentConnections() 77 self.load_connection.set_sensitive(hasRecent) 78 if hasRecent: 79 self.load_connection.set_active(True) 80 else: 81 self.connect_to_existing.set_active(True) 82 83 # Find which radio button should be focused: 84 for radioName in available_pages: 85 radio = getattr(self, radioName) 86 if radio.get_active(): 87 break 88 else: 89 raise AssertionError("no button to focus") 90 radio.grab_focus()
91
92 - def on_next(self, state):
93 for radio in self.connect_to_existing.get_group(): 94 if radio.get_active(): 95 return radio.get_name() 96 raise AssertionError
97 98 # Callbacks 99
100 - def _on_radio__activiate(self, radio):
101 if not radio.get_active(): 102 return 103 self.button_next.clicked()
104 105
106 -class ConnectToExisting(WizardStep):
107 name = 'connect_to_existing' 108 title = _('Host Information') 109 text = _('Please enter the address at which the manager is running.') 110 next_pages = ['authenticate'] 111 open_connection = None 112 113 # WizardSteps 114
115 - def setup(self, state, available_pages):
116 try: 117 oc_state = [(k, state[k]) for k in ( 118 'host', 'port', 'use_insecure')] 119 self.open_connection.set_state(dict(oc_state)) 120 except KeyError: 121 pass 122 self.open_connection.grab_focus()
123 124 # Callbacks 125
126 - def on_can_activate(self, obj, *args):
127 self.button_next.set_sensitive(obj.get_property('can-activate'))
128
129 - def on_next(self, state):
130 for k, v in self.open_connection.get_state().items(): 131 state[k] = v 132 return 'authenticate'
133 134
135 -class Authenticate(WizardStep):
136 name = 'authenticate' 137 title = _('Authentication') 138 text = _('Please enter your username and password.') 139 auth_method_combo = user_entry = passwd_entry = None 140 next_pages = [] 141 authenticate = None 142 143 # WizardStep 144
145 - def setup(self, state, available_pages):
146 try: 147 oc_state = [(k, state[k]) for k in ('user', 'passwd')] 148 self.authenticate.set_state(dict(oc_state)) 149 except KeyError: 150 self.authenticate.set_state(None) 151 self.authenticate.grab_focus() 152 self.on_can_activate(self.authenticate)
153
154 - def on_next(self, state):
155 for k, v in self.authenticate.get_state().items(): 156 state[k] = v 157 state['connectionInfo'] = parsePBConnectionInfo( 158 state['host'], 159 username=state['user'], 160 password=state['passwd'], 161 port=state['port'], 162 use_ssl=not state['use_insecure']) 163 return '*finished*'
164 165 # Callbacks 166
167 - def on_can_activate(self, obj, *args):
168 self.button_next.set_sensitive(obj.get_property('can-activate'))
169 170
171 -class LoadConnection(WizardStep):
172 name = 'load_connection' 173 title = _('Recent Connections') 174 text = _('Please choose a connection from the box below.') 175 connections = None 176 next_pages = [] 177 178 # WizardStep 179
180 - def setup(self, state, available_pages):
181 self.connections.grab_focus() 182 self.button_next.set_label(gtk.STOCK_CONNECT)
183
184 - def on_next(self, state):
185 connection = self.connections.get_selected() 186 state['connection'] = connection 187 state['connectionInfo'] = connection.info 188 return '*finished*'
189 190 # Callbacks 191
192 - def on_connection_activated(self, widget, state):
193 self.button_next.emit('clicked')
194
195 - def on_connections_cleared(self, widget):
196 self.button_next.set_sensitive(False)
197 198
199 -class StartNew(WizardStep):
200 name = 'start_new' 201 title = _('Start a New Manager and Worker') 202 text = _("""This will start a new manager and worker for you. 203 204 The manager and worker will run under your user account. 205 The manager will only accept connections from the local machine. 206 This mode is only useful for testing Flumotion. 207 """) 208 start_worker_check = None 209 next_pages = ['start_new_error', 'start_new_success'] 210 gsignal('finished', str) 211 212 _timeout_id = None 213 214 # WizardStep 215
216 - def setup(self, state, available_pages):
217 self.button_next.grab_focus()
218
219 - def on_next(self, state):
220 self.label_starting.show() 221 self.progressbar_starting.set_fraction(0.0) 222 self.progressbar_starting.show() 223 224 def pulse(): 225 self.progressbar_starting.pulse() 226 return True
227 self._timeout_id = gobject.timeout_add(200, pulse) 228 229 self._startManager(state) 230 return '*signaled*'
231 232 # Private 233
234 - def _startManager(self, state):
235 port = tryPort() 236 managerSpawner = LocalManagerSpawner(port) 237 managerSpawner.connect('error', self._on_spawner_error, state) 238 managerSpawner.connect('description-changed', 239 self._on_spawner_description_changed) 240 managerSpawner.connect('finished', self._on_spawner_finished, state) 241 managerSpawner.start()
242
243 - def _on_spawner_error(self, spawner, failure, msg, args, state):
244 # error should trigger going to next page with an overview 245 state.update({ 246 'command': ' '.join(args), 247 'error': msg, 248 'failure': failure, 249 }) 250 self._finished('start_new_error')
251
252 - def _on_spawner_description_changed(self, spawner, description):
253 self.label_starting.set_text(description)
254
255 - def _on_spawner_finished(self, spawner, state):
256 # because of the ugly call-by-reference passing of state, 257 # we have to update the existing dict, not re-bind with state = 258 state['connectionInfo'] = parsePBConnectionInfo( 259 'localhost', 260 username='user', 261 password='test', 262 port=spawner.getPort(), 263 use_ssl=True) 264 state.update(dict(confDir=spawner.getConfDir(), 265 logDir=spawner.getLogDir(), 266 runDir=spawner.getRunDir())) 267 self._finished('start_new_success')
268
269 - def _finished(self, result):
270 # result: start_new_error or start_new_success 271 self.label_starting.hide() 272 self.progressbar_starting.hide() 273 gobject.source_remove(self._timeout_id) 274 self.emit('finished', result)
275 276
277 -class StartNewError(WizardStep):
278 name = 'start_new_error' 279 title = _('Failed to Start') 280 text = "" 281 start_worker_check = None 282 next_pages = [] 283 284 # WizardStep 285
286 - def setup(self, state, available_pages):
287 self.button_next.set_sensitive(False) 288 self.message.set_text(state['error']) 289 f = state['failure'] 290 result = "" 291 if f.value.exitCode is not None: 292 result = _('The command exited with an exit code of %d.' % 293 f.value.exitCode) 294 self.more.set_markup(_("""The command that failed was: 295 <i>%s</i> 296 %s""") % (state['command'], result))
297 298
299 -class StartNewSuccess(WizardStep):
300 name = 'start_new_success' 301 title = _('Started Manager and Worker') 302 start_worker_check = None 303 text = '' 304 next_pages = [] 305 306 # WizardStep 307
308 - def setup(self, state, available_pages):
309 self.button_prev.set_sensitive(False) 310 self.button_next.set_label(gtk.STOCK_CONNECT) 311 executable = os.path.join(configure.sbindir, 'flumotion') 312 confDir = state['confDir'] 313 logDir = state['logDir'] 314 runDir = state['runDir'] 315 stop = "%s -C %s -L %s -R %s stop" % ( 316 executable, confDir, logDir, runDir) 317 self.message.set_markup(_( 318 """The admin client will now connect to the manager. 319 320 Configuration files are stored in 321 <i>%s</i> 322 Log files are stored in 323 <i>%s</i> 324 325 You can shut down the manager and worker later with the following command: 326 327 <i>%s</i> 328 """) % (confDir, logDir, stop)) 329 self.button_next.grab_focus()
330
331 - def on_next(self, state):
332 return '*finished*'
333 334
335 -class Greeter(SimpleWizard):
336 name = 'greeter' 337 steps = [Initial, ConnectToExisting, Authenticate, LoadConnection, 338 StartNew, StartNewError, StartNewSuccess] 339
340 - def __init__(self, adminWindow):
341 self._adminWindow = adminWindow 342 SimpleWizard.__init__(self, 'initial', 343 parent=adminWindow.getWindow()) 344 # Set the name of the toplevel window, this is used by the 345 # ui unittest framework 346 self.window1.set_name('Greeter') 347 self.window1.set_size_request(-1, 450)
348 349 # SimpleWizard 350
351 - def runAsync(self):
352 d = SimpleWizard.runAsync(self) 353 d.addCallback(self._runAsyncFinished) 354 d.addErrback(self._wizardCancelledErrback) 355 return d
356 357 # Private 358
359 - def _runAsyncFinished(self, state):
360 connection = state.get('connection') 361 info = state['connectionInfo'] 362 363 def connected(unused): 364 if connection is not None: 365 connection.updateTimestamp()
366 367 def errorMessageDisplayed(unused): 368 return self.runAsync()
369 370 def connectionFailed(failure): 371 failure.trap(ConnectionFailedError, ConnectionRefusedError) 372 d = showConnectionErrorDialog(failure, info, 373 parent=self.window) 374 d.addCallback(errorMessageDisplayed) 375 return d 376 377 d = self._adminWindow.openConnection(info) 378 d.addCallbacks(connected, connectionFailed) 379 self.set_sensitive(False) 380 return d 381
382 - def _wizardCancelledErrback(self, failure):
383 failure.trap(WizardCancelled) 384 reactor.stop()
385 386 387 # This is used by the gtk admin to connect to an existing manager 388 389
390 -class ConnectExisting(SimpleWizard):
391 name = 'greeter' 392 steps = [ConnectToExisting, Authenticate] 393
394 - def __init__(self, parent=None):
395 SimpleWizard.__init__(self, 'connect_to_existing', 396 parent=parent)
397