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

Source Code for Module flumotion.admin.gtk.configurationassistant

  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  """Configuration Assistant - A graphical user interface to create a stream. 
 23   
 24   
 25  This simple drawing explains the basic user interface: 
 26   
 27    +----------+---------------------------------+ 
 28    |          |             Title               | 
 29    | Sidebar  |---------------------------------+ 
 30    |          |                                 | 
 31    |          |                                 | 
 32    |          |                                 | 
 33    |          |         WizardStep              | 
 34    |          |                                 | 
 35    |          |                                 | 
 36    |          |                                 | 
 37    |          |                                 | 
 38    |          |                                 | 
 39    |          +---------------------------------+ 
 40    |          |            Buttons              | 
 41    +----------+---------------------------------+ 
 42   
 43  Sidebar shows the available and visited steps, it allows you to quickly 
 44  navigate back to a previous step. 
 45  Title and the sidebar name contains text / icon the wizard step can set. 
 46  Buttons contain navigation and help. 
 47   
 48  Most WizardSteps are loaded over the network from the manager (to the admin 
 49  client where the code runs). 
 50  """ 
 51  import gettext 
 52  import os 
 53  import webbrowser 
 54   
 55  import gtk 
 56  from gtk import gdk 
 57  from twisted.internet import defer 
 58   
 59  from flumotion.admin.assistant.save import AssistantSaver 
 60  from flumotion.admin.gtk.workerstep import WorkerWizardStep 
 61  from flumotion.admin.gtk.workerlist import WorkerList 
 62  from flumotion.common import errors, messages, python 
 63  from flumotion.common.common import pathToModuleName 
 64  from flumotion.common import documentation 
 65  from flumotion.common.i18n import N_, ngettext, gettexter 
 66  from flumotion.common.pygobject import gsignal 
 67  from flumotion.configure import configure 
 68  from flumotion.ui.wizard import SectionWizard, WizardStep 
 69   
 70   
 71  # pychecker doesn't like the auto-generated widget attrs 
 72  # or the extra args we name in callbacks 
 73  __pychecker__ = 'no-classattr no-argsused' 
 74  __version__ = "$Rev$" 
 75  T_ = gettexter() 
 76  _ = gettext.gettext 
 77   
 78   
 79  # the denominator arg for all calls of this function was sniffed from 
 80  # the glade file's spinbutton adjustment 
 81   
 82   
83 -def _fraction_from_float(number, denominator):
84 """ 85 Return a string to be used in serializing to XML. 86 """ 87 return "%d/%d" % (number * denominator, denominator)
88 89
90 -class WelcomeStep(WizardStep):
91 """ 92 This step is showing an informative description which introduces 93 the user to the configuration assistant. 94 """ 95 name = "Welcome" 96 title = _('Welcome') 97 section = _('Welcome') 98 icon = 'wizard.png' 99 gladeFile = 'welcome-wizard.glade' 100 docSection = 'help-configuration-assistant-welcome' 101 docAnchor = '' 102 docVersion = 'local' 103
104 - def getNext(self):
105 return None
106 107
108 -class ScenarioStep(WizardStep):
109 """ 110 This step is showing a list of possible scenarios. 111 The user will select the scenario he want to use, 112 then the scenario itself will decide the future steps. 113 """ 114 name = "Scenario" 115 title = _('Scenario') 116 section = _('Scenario') 117 icon = 'wizard.png' 118 gladeFile = 'scenario-wizard.glade' 119 docSection = 'help-configuration-assistant-scenario' 120 docAnchor = '' 121 docVersion = 'local' 122 123 # WizardStep 124
125 - def __init__(self, wizard):
126 self._currentScenarioType = None 127 self._radioGroup = None 128 self._scenarioRadioButtons = [] 129 super(ScenarioStep, self).__init__(wizard)
130
131 - def setup(self):
132 133 def addScenarios(list): 134 for scenario in list: 135 self.addScenario(_(scenario.getDescription()), 136 scenario.getType()) 137 138 firstButton = self.scenarios_box.get_children()[0] 139 firstButton.set_active(True) 140 firstButton.toggled() 141 firstButton.grab_focus()
142 143 d = self.wizard.getAdminModel().getScenarios() 144 d.addCallback(addScenarios) 145 146 return d
147
148 - def getNext(self):
149 self.wizard.waitForTask('get-next-step') 150 self.wizard.cleanFutureSteps() 151 152 def addScenarioSteps(scenarioClass): 153 scenario = scenarioClass() 154 scenario.addSteps(self.wizard) 155 self.wizard.setScenario(scenario) 156 self.wizard.taskFinished()
157 158 d = self.wizard.getWizardScenario(self._currentScenarioType) 159 d.addCallback(addScenarioSteps) 160 161 return d 162 163 # Public 164
165 - def addScenario(self, scenarioDesc, scenarioType):
166 """ 167 Adds a new entry to the scenarios list of the wizard. 168 169 @param scenarioDesc: Description that will be shown on the list. 170 @type scenarioDesc: str 171 @param scenarioType: The type of the scenario we are adding. 172 @type scenarioType: str 173 """ 174 button = gtk.RadioButton(self._radioGroup, scenarioDesc) 175 button.connect('toggled', 176 self._on_radiobutton__toggled, 177 scenarioType) 178 button.connect('activate', 179 self._on_radiobutton__activate) 180 181 self.scenarios_box.pack_start(button, False, False) 182 button.show() 183 184 if self._radioGroup is None: 185 self._radioGroup = button
186 187 # Private 188 189 # Callbacks 190
191 - def _on_radiobutton__activate(self, radio):
192 self.wizard.goNext()
193
194 - def _on_radiobutton__toggled(self, radio, scenarioType):
195 if radio.get_active(): 196 self._currentScenarioType = scenarioType
197 198
199 -class ConfigurationAssistant(SectionWizard):
200 """This is the main configuration assistant class, 201 it is responsible for:: 202 - executing tasks which will block the ui 203 - showing a worker list in the UI 204 - communicating with the manager, fetching bundles 205 and registry information 206 - running check defined by a step in a worker, for instance 207 querying for hardware devices and their capabilities 208 It extends SectionWizard which provides the basic user interface, such 209 as sidebar, buttons, title bar and basic step navigation. 210 """ 211 gsignal('finished', str) 212
213 - def __init__(self, parent=None):
214 SectionWizard.__init__(self, parent) 215 self.connect('help-clicked', self._on_assistant__help_clicked) 216 # Set the name of the toplevel window, this is used by the 217 # ui unittest framework 218 self.window1.set_name('ConfigurationAssistant') 219 self.message_area.disableTimestamps() 220 221 self._cursorWatch = gdk.Cursor(gdk.WATCH) 222 self._tasks = [] 223 self._adminModel = None 224 self._workerHeavenState = None 225 self._lastWorker = 0 # combo id last worker from step to step 226 self._stepWorkers = {} 227 self._scenario = None 228 self._existingComponentNames = [] 229 self._porters = [] 230 self._mountPoints = [] 231 self._consumers = {} 232 233 self._workerList = WorkerList() 234 self.top_vbox.pack_start(self._workerList, False, False) 235 self._workerList.connect('worker-selected', 236 self.on_combobox_worker_changed)
237 238 # SectionWizard 239
240 - def completed(self):
241 saver = AssistantSaver() 242 saver.setFlowName('default') 243 saver.setExistingComponentNames(self._existingComponentNames) 244 self._scenario.save(self, saver) 245 246 xml = saver.getXML() 247 self.emit('finished', xml)
248
249 - def destroy(self):
250 SectionWizard.destroy(self) 251 self._adminModel = None
252
253 - def beforeShowStep(self, step):
254 if isinstance(step, WorkerWizardStep): 255 self._workerList.show() 256 if step.worker: 257 self._workerList.selectWorker(step.worker) 258 self._workerList.notifySelected() 259 else: 260 self._workerList.hide() 261 262 self._fetchDescription(step) 263 self._setupWorker(step, self._workerList.getWorker())
264
265 - def prepareNextStep(self, step):
266 self._setupWorker(step, self._workerList.getWorker()) 267 SectionWizard.prepareNextStep(self, step)
268
269 - def blockNext(self, block):
270 # Do not block/unblock if we have tasks running 271 if self._tasks: 272 return 273 SectionWizard.blockNext(self, block)
274 275 # Public API 276 277 # FIXME: Remove this and make fgc create a new scenario 278
279 - def addInitialSteps(self):
280 """Add the step sections of the wizard, can be 281 overridden in a subclass 282 """ 283 # These two steps are independent of the scenarios, they 284 # should always be added. 285 self.addStepSection(WelcomeStep) 286 self.addStepSection(ScenarioStep)
287
288 - def setScenario(self, scenario):
289 """Sets the current scenario of the assistant. 290 Normally called by ScenarioStep to tell the assistant the 291 current scenario just after creating it. 292 @param scenario: the scenario of the assistant 293 @type scenario: a L{flumotion.admin.assistant.scenarios.Scenario} 294 subclass 295 """ 296 self._scenario = scenario
297
298 - def getScenario(self):
299 """Fetches the currently set scenario of the assistant. 300 @returns scenario: the scenario of the assistant 301 @rtype: a L{flumotion.admin.assistant.scenarios.Scenario} subclass 302 """ 303 return self._scenario
304
305 - def setWorkerHeavenState(self, workerHeavenState):
306 """ 307 Sets the worker heaven state of the assistant 308 @param workerHeavenState: the worker heaven state 309 @type workerHeavenState: L{WorkerComponentUIState} 310 """ 311 self._workerHeavenState = workerHeavenState 312 self._workerList.setWorkerHeavenState(workerHeavenState)
313
314 - def setAdminModel(self, adminModel):
315 """ 316 Sets the admin model of the assistant 317 @param adminModel: the admin model 318 @type adminModel: L{AdminModel} 319 """ 320 self._adminModel = adminModel 321 self._adminModel.connect('connected', 322 self.on_admin_connected_cb) 323 self._adminModel.connect('disconnected', 324 self.on_admin_disconnected_cb)
325
326 - def setHTTPPorters(self, porters):
327 """ 328 Sets the list of currently configured porters so 329 we can reuse them for future streamers. 330 331 @param porters: list of porters 332 @type porters : list of L{flumotion.admin.assistant.models.Porter} 333 """ 334 335 self._porters = porters
336
337 - def getHTTPPorters(self):
338 """ 339 Obtains the list of the currently configured porters. 340 341 @rtype : list of L{flumotion.admin.assistant.models.Porter} 342 """ 343 return self._porters
344
345 - def addMountPoint(self, worker, port, mount_point, consumer=None):
346 """ 347 Marks a mount point as used on the given worker and port. 348 If a consumer name is provided it means we are changing the 349 mount point for that consumer and that we should keep track of 350 it for further modifications. 351 352 @param worker : The worker where the mount_point is configured. 353 @type worker : str 354 @param port : The port where the streamer should be listening. 355 @type port : int 356 @param mount_point : The mount point where the data will be served. 357 @type mount_point : str 358 @param consumer : The consumer that is changing its mountpoint. 359 @type consumer : str 360 361 @returns : True if the mount point is not used and has been 362 inserted correctly, False otherwise. 363 @rtype : boolean 364 """ 365 if not worker or not port or not mount_point: 366 return False 367 368 if consumer in self._consumers: 369 oldData = self._consumers[consumer] 370 if oldData in self._mountPoints: 371 self._mountPoints.remove(oldData) 372 373 data = (worker, port, mount_point) 374 375 if data in self._mountPoints: 376 return False 377 378 self._mountPoints.append(data) 379 380 if consumer: 381 self._consumers[consumer] = data 382 383 return True
384
385 - def getAdminModel(self):
386 """Gets the admin model of the assistant 387 @returns adminModel: the admin model 388 @rtype adminModel: L{AdminModel} 389 """ 390 return self._adminModel
391
392 - def waitForTask(self, taskName):
393 """Instruct the assistant that we're waiting for a task 394 to be finished. This changes the cursor and prevents 395 the user from continuing moving forward. 396 Each call to this method should have another call 397 to taskFinished() when the task is actually done. 398 @param taskName: name of the name 399 @type taskName: string 400 """ 401 self.info("waiting for task %s" % (taskName, )) 402 if not self._tasks: 403 if self.window1.window is not None: 404 self.window1.window.set_cursor(self._cursorWatch) 405 self.blockNext(True) 406 self._tasks.append(taskName)
407
408 - def taskFinished(self, blockNext=False):
409 """Instruct the assistant that a task was finished. 410 @param blockNext: if we should still next when done 411 @type blockNext: boolean 412 """ 413 if not self._tasks: 414 raise AssertionError( 415 "Stray call to taskFinished(), forgot to call waitForTask()?") 416 417 taskName = self._tasks.pop() 418 self.info("task %s has now finished" % (taskName, )) 419 if not self._tasks: 420 self.window1.window.set_cursor(None) 421 self.blockNext(blockNext)
422
423 - def pendingTask(self):
424 """Returns true if there are any pending tasks 425 @returns: if there are pending tasks 426 @rtype: bool 427 """ 428 return bool(self._tasks)
429
430 - def checkElements(self, workerName, *elementNames):
431 """Check if the given list of GStreamer elements exist on the 432 given worker. 433 @param workerName: name of the worker to check on 434 @type workerName: string 435 @param elementNames: names of the elements to check 436 @type elementNames: list of strings 437 @returns: a deferred returning a tuple of the missing elements 438 @rtype: L{twisted.internet.defer.Deferred} 439 """ 440 if not self._adminModel: 441 self.debug('No admin connected, not checking presence of elements') 442 return 443 444 asked = python.set(elementNames) 445 446 def _checkElementsCallback(existing, workerName): 447 existing = python.set(existing) 448 self.taskFinished() 449 return tuple(asked.difference(existing))
450 451 self.waitForTask('check elements %r' % (elementNames, )) 452 d = self._adminModel.checkElements(workerName, elementNames) 453 d.addCallback(_checkElementsCallback, workerName) 454 return d
455
456 - def requireElements(self, workerName, *elementNames):
457 """Require that the given list of GStreamer elements exists on the 458 given worker. If the elements do not exist, an error message is 459 posted and the next button remains blocked. 460 @param workerName: name of the worker to check on 461 @type workerName: string 462 @param elementNames: names of the elements to check 463 @type elementNames: list of strings 464 @returns: element name 465 @rtype: deferred -> list of strings 466 """ 467 if not self._adminModel: 468 self.debug('No admin connected, not checking presence of elements') 469 return 470 471 self.debug('requiring elements %r' % (elementNames, )) 472 f = ngettext("Checking the existence of GStreamer element '%s' " 473 "on %s worker.", 474 "Checking the existence of GStreamer elements '%s' " 475 "on %s worker.", 476 len(elementNames)) 477 msg = messages.Info(T_(f, "', '".join(elementNames), workerName), 478 mid='require-elements') 479 480 self.add_msg(msg) 481 482 def gotMissingElements(elements, workerName): 483 self.clear_msg('require-elements') 484 485 if elements: 486 self.warning('elements %r do not exist' % (elements, )) 487 f = ngettext("Worker '%s' is missing GStreamer element '%s'.", 488 "Worker '%s' is missing GStreamer elements '%s'.", 489 len(elements)) 490 message = messages.Error(T_(f, workerName, 491 "', '".join(elements))) 492 message.add(T_(N_("\n" 493 "Please install the necessary GStreamer plug-ins that " 494 "provide these elements and restart the worker."))) 495 message.add(T_(N_("\n\n" 496 "You will not be able to go forward using this worker."))) 497 message.id = 'element' + '-'.join(elementNames) 498 documentation.messageAddGStreamerInstall(message) 499 self.add_msg(message) 500 self.taskFinished(bool(elements)) 501 return elements
502 503 self.waitForTask('require elements %r' % (elementNames, )) 504 d = self.checkElements(workerName, *elementNames) 505 d.addCallback(gotMissingElements, workerName) 506 507 return d 508
509 - def checkImport(self, workerName, moduleName):
510 """Check if the given module can be imported. 511 @param workerName: name of the worker to check on 512 @type workerName: string 513 @param moduleName: name of the module to import 514 @type moduleName: string 515 @returns: a deferred firing None or Failure. 516 @rtype: L{twisted.internet.defer.Deferred} 517 """ 518 if not self._adminModel: 519 self.debug('No admin connected, not checking presence of elements') 520 return 521 522 d = self._adminModel.checkImport(workerName, moduleName) 523 return d
524
525 - def requireImport(self, workerName, moduleName, projectName=None, 526 projectURL=None):
527 """Require that the given module can be imported on the given worker. 528 If the module cannot be imported, an error message is 529 posted and the next button remains blocked. 530 @param workerName: name of the worker to check on 531 @type workerName: string 532 @param moduleName: name of the module to import 533 @type moduleName: string 534 @param projectName: name of the module to import 535 @type projectName: string 536 @param projectURL: URL of the project 537 @type projectURL: string 538 @returns: a deferred firing None or Failure 539 @rtype: L{twisted.internet.defer.Deferred} 540 """ 541 if not self._adminModel: 542 self.debug('No admin connected, not checking presence of elements') 543 return 544 545 self.debug('requiring module %s' % moduleName) 546 547 def _checkImportErrback(failure): 548 self.warning('could not import %s', moduleName) 549 message = messages.Error(T_(N_( 550 "Worker '%s' cannot import module '%s'."), 551 workerName, moduleName)) 552 if projectName: 553 message.add(T_(N_("\n" 554 "This module is part of '%s'."), projectName)) 555 if projectURL: 556 message.add(T_(N_("\n" 557 "The project's homepage is %s"), projectURL)) 558 message.add(T_(N_("\n\n" 559 "You will not be able to go forward using this worker."))) 560 message.id = 'module-%s' % moduleName 561 documentation.messageAddPythonInstall(message, moduleName) 562 self.add_msg(message) 563 self.taskFinished(blockNext=True) 564 return False
565 566 d = self.checkImport(workerName, moduleName) 567 d.addErrback(_checkImportErrback) 568 return d 569 570 # FIXME: maybe add id here for return messages ? 571
572 - def runInWorker(self, workerName, moduleName, functionName, 573 *args, **kwargs):
574 """ 575 Run the given function and arguments on the selected worker. 576 The given function should return a L{messages.Result}. 577 578 @param workerName: name of the worker to run the function in 579 @type workerName: string 580 @param moduleName: name of the module where the function is found 581 @type moduleName: string 582 @param functionName: name of the function to run 583 @type functionName: string 584 585 @returns: a deferred firing the Result's value. 586 @rtype: L{twisted.internet.defer.Deferred} 587 """ 588 self.debug('runInWorker(moduleName=%r, functionName=%r)' % ( 589 moduleName, functionName)) 590 admin = self._adminModel 591 if not admin: 592 self.warning('skipping runInWorker, no admin') 593 return defer.fail(errors.FlumotionError('no admin')) 594 595 if not workerName: 596 self.warning('skipping runInWorker, no worker') 597 return defer.fail(errors.FlumotionError('no worker')) 598 599 def callback(result): 600 self.debug('runInWorker callbacked a result') 601 self.clear_msg(functionName) 602 603 if not isinstance(result, messages.Result): 604 msg = messages.Error(T_( 605 N_("Internal error: could not run check code on worker.")), 606 debug=('function %r returned a non-Result %r' 607 % (functionName, result))) 608 self.add_msg(msg) 609 self.taskFinished(True) 610 raise errors.RemoteRunError(functionName, 'Internal error.') 611 612 for m in result.messages: 613 self.debug('showing msg %r' % m) 614 self.add_msg(m) 615 616 if result.failed: 617 self.debug('... that failed') 618 self.taskFinished(True) 619 raise errors.RemoteRunFailure(functionName, 'Result failed') 620 self.debug('... that succeeded') 621 self.taskFinished() 622 return result.value
623 624 def errback(failure): 625 self.debug('runInWorker errbacked, showing error msg') 626 if failure.check(errors.RemoteRunError): 627 debug = failure.value 628 else: 629 debug = "Failure while running %s.%s:\n%s" % ( 630 moduleName, functionName, failure.getTraceback()) 631 632 msg = messages.Error(T_( 633 N_("Internal error: could not run check code on worker.")), 634 debug=debug) 635 self.add_msg(msg) 636 self.taskFinished(True) 637 raise errors.RemoteRunError(functionName, 'Internal error.') 638 639 self.waitForTask('run in worker: %s.%s(%r, %r)' % ( 640 moduleName, functionName, args, kwargs)) 641 d = admin.workerRun(workerName, moduleName, 642 functionName, *args, **kwargs) 643 d.addErrback(errback) 644 d.addCallback(callback) 645 return d 646
647 - def getWizardEntry(self, componentType):
648 """Fetches a assistant bundle from a specific kind of component 649 @param componentType: the component type to get the assistant entry 650 bundle from. 651 @type componentType: string 652 @returns: a deferred returning either:: 653 - factory of the component 654 - noBundle error: if the component lacks a assistant bundle 655 @rtype: L{twisted.internet.defer.Deferred} 656 """ 657 self.waitForTask('get assistant entry %s' % (componentType, )) 658 self.clear_msg('assistant-bundle') 659 d = self._adminModel.callRemote( 660 'getEntryByType', componentType, 'wizard') 661 d.addCallback(self._gotEntryPoint) 662 return d
663
664 - def getWizardScenario(self, scenarioType):
665 """ 666 Fetches a scenario bundle from a specific kind of component. 667 668 @param scenarioType: the scenario type to get the assistant entry 669 bundle from. 670 @type scenarioType: string 671 @returns: a deferred returning either:: 672 - factory of the component 673 - noBundle error: if the component lacks a assistant bundle 674 @rtype: L{twisted.internet.defer.Deferred} 675 """ 676 self.waitForTask('get assistant entry %s' % (scenarioType, )) 677 self.clear_msg('assistant-bundle') 678 d = self._adminModel.callRemote( 679 'getScenarioByType', scenarioType, 'wizard') 680 d.addCallback(self._gotEntryPoint) 681 return d
682
683 - def getWizardPlugEntry(self, plugType):
684 """Fetches a assistant bundle from a specific kind of plug 685 @param plugType: the plug type to get the assistant entry 686 bundle from. 687 @type plugType: string 688 @returns: a deferred returning either:: 689 - factory of the plug 690 - noBundle error: if the plug lacks a assistant bundle 691 @rtype: L{twisted.internet.defer.Deferred} 692 """ 693 self.waitForTask('get assistant plug %s' % (plugType, )) 694 self.clear_msg('assistant-bundle') 695 d = self._adminModel.callRemote( 696 'getPlugEntry', plugType, 'wizard') 697 d.addCallback(self._gotEntryPoint) 698 return d
699
700 - def getWizardEntries(self, wizardTypes=None, provides=None, accepts=None):
701 """Queries the manager for a list of assistant entries matching the 702 query. 703 @param wizardTypes: list of component types to fetch, is usually 704 something like ['video-producer'] or 705 ['audio-encoder'] 706 @type wizardTypes: list of str 707 @param provides: formats provided, eg ['jpeg', 'speex'] 708 @type provides: list of str 709 @param accepts: formats accepted, eg ['theora'] 710 @type accepts: list of str 711 712 @returns: a deferred firing a list 713 of L{flumotion.common.componentui.WizardEntryState} 714 @rtype: L{twisted.internet.defer.Deferred} 715 """ 716 self.debug('querying wizard entries (wizardTypes=%r,provides=%r' 717 ',accepts=%r)'% (wizardTypes, provides, accepts)) 718 return self._adminModel.getWizardEntries(wizardTypes=wizardTypes, 719 provides=provides, 720 accepts=accepts)
721
722 - def setExistingComponentNames(self, componentNames):
723 """Tells the assistant about the existing components available, so 724 we can resolve naming conflicts when saving the configuration 725 @param componentNames: existing component names 726 @type componentNames: list of strings 727 """ 728 self._existingComponentNames = componentNames
729
730 - def workerChangedForStep(self, step, workerName):
731 """Tell a step that its worker changed. 732 @param step: step which worker changed for 733 @type step: a L{WorkerWizardStep} subclass 734 @param workerName: name of the worker 735 @type workerName: string 736 """ 737 if self._stepWorkers.get(step) == workerName: 738 return 739 740 self.debug('calling %r.workerChanged' % step) 741 step.workerChanged(workerName) 742 self._stepWorkers[step] = workerName
743 744 # Private 745
746 - def _gotEntryPoint(self, (filename, procname)):
747 # The manager always returns / as a path separator, replace them with 748 # the separator since the rest of our infrastructure depends on that. 749 filename = filename.replace('/', os.path.sep) 750 modname = pathToModuleName(filename) 751 d = self._adminModel.getBundledFunction(modname, procname) 752 self.clear_msg('assistant-bundle') 753 self.taskFinished() 754 return d
755
756 - def _setupWorker(self, step, worker):
757 # get name of active worker 758 self.debug('%r setting worker to %s' % (step, worker)) 759 step.worker = worker
760 769
770 - def _fetchDescription(self, step):
771 if not hasattr(step, 'model'): 772 self.setStepDescription('') 773 return 774 775 def gotComponentEntry(entry): 776 self.setStepDescription(entry.description)
777 778 d = self._adminModel.callRemote( 779 'getComponentEntry', step.model.componentType) 780 d.addCallback(gotComponentEntry) 781 782 # Callbacks 783
784 - def on_combobox_worker_changed(self, combobox, worker):
785 self.debug('combobox_workerChanged, worker %r' % worker) 786 if worker: 787 self.clear_msg('worker-error') 788 self._lastWorker = worker 789 step = self.getCurrentStep() 790 if step and isinstance(step, WorkerWizardStep): 791 self._setupWorker(step, worker) 792 self.workerChangedForStep(step, worker) 793 else: 794 msg = messages.Error(T_( 795 N_('All workers have logged out.\n' 796 'Make sure your Flumotion network is running ' 797 'properly and try again.')), 798 mid='worker-error') 799 self.add_msg(msg)
800
801 - def _on_assistant__help_clicked(self, assistant, section, anchor, version):
802 self._showHelpLink(section, anchor, version)
803
804 - def on_admin_connected_cb(self, admin):
805 self.window1.set_sensitive(True)
806
807 - def on_admin_disconnected_cb(self, admin):
808 self.window1.set_sensitive(False)
809