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