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: 7993 $" 
 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 self._workerList.notifySelected() 257 else: 258 self._workerList.hide() 259 260 self._fetchDescription(step) 261 self._setupWorker(step, self._workerList.getWorker())
262
263 - def prepareNextStep(self, step):
264 self._setupWorker(step, self._workerList.getWorker()) 265 SectionWizard.prepareNextStep(self, step)
266
267 - def blockNext(self, block):
268 # Do not block/unblock if we have tasks running 269 if self._tasks: 270 return 271 SectionWizard.blockNext(self, block)
272 273 # Public API 274 275 # FIXME: Remove this and make fgc create a new scenario 276
277 - def addInitialSteps(self):
278 """Add the step sections of the wizard, can be 279 overridden in a subclass 280 """ 281 # These two steps are independent of the scenarios, they 282 # should always be added. 283 self.addStepSection(WelcomeStep) 284 self.addStepSection(ScenarioStep)
285
286 - def setScenario(self, scenario):
287 """Sets the current scenario of the assistant. 288 Normally called by ScenarioStep to tell the assistant the 289 current scenario just after creating it. 290 @param scenario: the scenario of the assistant 291 @type scenario: a L{flumotion.admin.assistant.scenarios.Scenario} 292 subclass 293 """ 294 self._scenario = scenario
295
296 - def getScenario(self):
297 """Fetches the currently set scenario of the assistant. 298 @returns scenario: the scenario of the assistant 299 @rtype: a L{flumotion.admin.assistant.scenarios.Scenario} subclass 300 """ 301 return self._scenario
302
303 - def setWorkerHeavenState(self, workerHeavenState):
304 """ 305 Sets the worker heaven state of the assistant 306 @param workerHeavenState: the worker heaven state 307 @type workerHeavenState: L{WorkerComponentUIState} 308 """ 309 self._workerHeavenState = workerHeavenState 310 self._workerList.setWorkerHeavenState(workerHeavenState)
311
312 - def setAdminModel(self, adminModel):
313 """ 314 Sets the admin model of the assistant 315 @param adminModel: the admin model 316 @type adminModel: L{AdminModel} 317 """ 318 self._adminModel = adminModel 319 self._adminModel.connect('connected', 320 self.on_admin_connected_cb) 321 self._adminModel.connect('disconnected', 322 self.on_admin_disconnected_cb)
323
324 - def setHTTPPorters(self, porters):
325 """ 326 Sets the list of currently configured porters so 327 we can reuse them for future streamers. 328 329 @param porters: list of porters 330 @type porters : list of L{flumotion.admin.assistant.models.Porter} 331 """ 332 333 self._porters = porters
334
335 - def getHTTPPorters(self):
336 """ 337 Obtains the list of the currently configured porters. 338 339 @rtype : list of L{flumotion.admin.assistant.models.Porter} 340 """ 341 return self._porters
342
343 - def addMountPoint(self, worker, port, mount_point, consumer=None):
344 """ 345 Marks a mount point as used on the given worker and port. 346 If a consumer name is provided it means we are changing the 347 mount point for that consumer and that we should keep track of 348 it for further modifications. 349 350 @param worker : The worker where the mount_point is configured. 351 @type worker : str 352 @param port : The port where the streamer should be listening. 353 @type port : int 354 @param mount_point : The mount point where the data will be served. 355 @type mount_point : str 356 @param consumer : The consumer that is changing its mountpoint. 357 @type consumer : str 358 359 @returns : True if the mount point is not used and has been 360 inserted correctly, False otherwise. 361 @rtype : boolean 362 """ 363 if not worker or not port or not mount_point: 364 return False 365 366 if consumer in self._consumers: 367 oldData = self._consumers[consumer] 368 if oldData in self._mountPoints: 369 self._mountPoints.remove(oldData) 370 371 data = (worker, port, mount_point) 372 373 if data in self._mountPoints: 374 return False 375 376 self._mountPoints.append(data) 377 378 if consumer: 379 self._consumers[consumer] = data 380 381 return True
382
383 - def getAdminModel(self):
384 """Gets the admin model of the assistant 385 @returns adminModel: the admin model 386 @rtype adminModel: L{AdminModel} 387 """ 388 return self._adminModel
389
390 - def waitForTask(self, taskName):
391 """Instruct the assistant that we're waiting for a task 392 to be finished. This changes the cursor and prevents 393 the user from continuing moving forward. 394 Each call to this method should have another call 395 to taskFinished() when the task is actually done. 396 @param taskName: name of the name 397 @type taskName: string 398 """ 399 self.info("waiting for task %s" % (taskName, )) 400 if not self._tasks: 401 if self.window1.window is not None: 402 self.window1.window.set_cursor(self._cursorWatch) 403 self.blockNext(True) 404 self._tasks.append(taskName)
405
406 - def taskFinished(self, blockNext=False):
407 """Instruct the assistant that a task was finished. 408 @param blockNext: if we should still next when done 409 @type blockNext: boolean 410 """ 411 if not self._tasks: 412 raise AssertionError( 413 "Stray call to taskFinished(), forgot to call waitForTask()?") 414 415 taskName = self._tasks.pop() 416 self.info("task %s has now finished" % (taskName, )) 417 if not self._tasks: 418 self.window1.window.set_cursor(None) 419 self.blockNext(blockNext)
420
421 - def pendingTask(self):
422 """Returns true if there are any pending tasks 423 @returns: if there are pending tasks 424 @rtype: bool 425 """ 426 return bool(self._tasks)
427
428 - def checkElements(self, workerName, *elementNames):
429 """Check if the given list of GStreamer elements exist on the 430 given worker. 431 @param workerName: name of the worker to check on 432 @type workerName: string 433 @param elementNames: names of the elements to check 434 @type elementNames: list of strings 435 @returns: a deferred returning a tuple of the missing elements 436 @rtype: L{twisted.internet.defer.Deferred} 437 """ 438 if not self._adminModel: 439 self.debug('No admin connected, not checking presence of elements') 440 return 441 442 asked = python.set(elementNames) 443 444 def _checkElementsCallback(existing, workerName): 445 existing = python.set(existing) 446 self.taskFinished() 447 return tuple(asked.difference(existing))
448 449 self.waitForTask('check elements %r' % (elementNames, )) 450 d = self._adminModel.checkElements(workerName, elementNames) 451 d.addCallback(_checkElementsCallback, workerName) 452 return d
453
454 - def requireElements(self, workerName, *elementNames):
455 """Require that the given list of GStreamer elements exists on the 456 given worker. If the elements do not exist, an error message is 457 posted and the next button remains blocked. 458 @param workerName: name of the worker to check on 459 @type workerName: string 460 @param elementNames: names of the elements to check 461 @type elementNames: list of strings 462 @returns: element name 463 @rtype: deferred -> list of strings 464 """ 465 if not self._adminModel: 466 self.debug('No admin connected, not checking presence of elements') 467 return 468 469 self.debug('requiring elements %r' % (elementNames, )) 470 f = ngettext("Checking the existence of GStreamer element '%s' " 471 "on %s worker.", 472 "Checking the existence of GStreamer elements '%s' " 473 "on %s worker.", 474 len(elementNames)) 475 msg = messages.Info(T_(f, "', '".join(elementNames), workerName), 476 mid='require-elements') 477 478 self.add_msg(msg) 479 480 def gotMissingElements(elements, workerName): 481 self.clear_msg('require-elements') 482 483 if elements: 484 self.warning('elements %r do not exist' % (elements, )) 485 f = ngettext("Worker '%s' is missing GStreamer element '%s'.", 486 "Worker '%s' is missing GStreamer elements '%s'.", 487 len(elements)) 488 message = messages.Error(T_(f, workerName, 489 "', '".join(elements))) 490 message.add(T_(N_("\n" 491 "Please install the necessary GStreamer plug-ins that " 492 "provide these elements and restart the worker."))) 493 message.add(T_(N_("\n\n" 494 "You will not be able to go forward using this worker."))) 495 message.id = 'element' + '-'.join(elementNames) 496 documentation.messageAddGStreamerInstall(message) 497 self.add_msg(message) 498 self.taskFinished(bool(elements)) 499 return elements
500 501 self.waitForTask('require elements %r' % (elementNames, )) 502 d = self.checkElements(workerName, *elementNames) 503 d.addCallback(gotMissingElements, workerName) 504 505 return d 506
507 - def checkImport(self, workerName, moduleName):
508 """Check if the given module can be imported. 509 @param workerName: name of the worker to check on 510 @type workerName: string 511 @param moduleName: name of the module to import 512 @type moduleName: string 513 @returns: a deferred firing None or Failure. 514 @rtype: L{twisted.internet.defer.Deferred} 515 """ 516 if not self._adminModel: 517 self.debug('No admin connected, not checking presence of elements') 518 return 519 520 d = self._adminModel.checkImport(workerName, moduleName) 521 return d
522
523 - def requireImport(self, workerName, moduleName, projectName=None, 524 projectURL=None):
525 """Require that the given module can be imported on the given worker. 526 If the module cannot be imported, an error message is 527 posted and the next button remains blocked. 528 @param workerName: name of the worker to check on 529 @type workerName: string 530 @param moduleName: name of the module to import 531 @type moduleName: string 532 @param projectName: name of the module to import 533 @type projectName: string 534 @param projectURL: URL of the project 535 @type projectURL: string 536 @returns: a deferred firing None or Failure 537 @rtype: L{twisted.internet.defer.Deferred} 538 """ 539 if not self._adminModel: 540 self.debug('No admin connected, not checking presence of elements') 541 return 542 543 self.debug('requiring module %s' % moduleName) 544 545 def _checkImportErrback(failure): 546 self.warning('could not import %s', moduleName) 547 message = messages.Error(T_(N_( 548 "Worker '%s' cannot import module '%s'."), 549 workerName, moduleName)) 550 if projectName: 551 message.add(T_(N_("\n" 552 "This module is part of '%s'."), projectName)) 553 if projectURL: 554 message.add(T_(N_("\n" 555 "The project's homepage is %s"), projectURL)) 556 message.add(T_(N_("\n\n" 557 "You will not be able to go forward using this worker."))) 558 message.id = 'module-%s' % moduleName 559 documentation.messageAddPythonInstall(message, moduleName) 560 self.add_msg(message) 561 self.taskFinished(blockNext=True) 562 return False
563 564 d = self.checkImport(workerName, moduleName) 565 d.addErrback(_checkImportErrback) 566 return d 567 568 # FIXME: maybe add id here for return messages ? 569
570 - def runInWorker(self, workerName, moduleName, functionName, 571 *args, **kwargs):
572 """ 573 Run the given function and arguments on the selected worker. 574 The given function should return a L{messages.Result}. 575 576 @param workerName: name of the worker to run the function in 577 @type workerName: string 578 @param moduleName: name of the module where the function is found 579 @type moduleName: string 580 @param functionName: name of the function to run 581 @type functionName: string 582 583 @returns: a deferred firing the Result's value. 584 @rtype: L{twisted.internet.defer.Deferred} 585 """ 586 self.debug('runInWorker(moduleName=%r, functionName=%r)' % ( 587 moduleName, functionName)) 588 admin = self._adminModel 589 if not admin: 590 self.warning('skipping runInWorker, no admin') 591 return defer.fail(errors.FlumotionError('no admin')) 592 593 if not workerName: 594 self.warning('skipping runInWorker, no worker') 595 return defer.fail(errors.FlumotionError('no worker')) 596 597 def callback(result): 598 self.debug('runInWorker callbacked a result') 599 self.clear_msg(functionName) 600 601 if not isinstance(result, messages.Result): 602 msg = messages.Error(T_( 603 N_("Internal error: could not run check code on worker.")), 604 debug=('function %r returned a non-Result %r' 605 % (functionName, result))) 606 self.add_msg(msg) 607 self.taskFinished(True) 608 raise errors.RemoteRunError(functionName, 'Internal error.') 609 610 for m in result.messages: 611 self.debug('showing msg %r' % m) 612 self.add_msg(m) 613 614 if result.failed: 615 self.debug('... that failed') 616 self.taskFinished(True) 617 raise errors.RemoteRunFailure(functionName, 'Result failed') 618 self.debug('... that succeeded') 619 self.taskFinished() 620 return result.value
621 622 def errback(failure): 623 self.debug('runInWorker errbacked, showing error msg') 624 if failure.check(errors.RemoteRunError): 625 debug = failure.value 626 else: 627 debug = "Failure while running %s.%s:\n%s" % ( 628 moduleName, functionName, failure.getTraceback()) 629 630 msg = messages.Error(T_( 631 N_("Internal error: could not run check code on worker.")), 632 debug=debug) 633 self.add_msg(msg) 634 self.taskFinished(True) 635 raise errors.RemoteRunError(functionName, 'Internal error.') 636 637 self.waitForTask('run in worker: %s.%s(%r, %r)' % ( 638 moduleName, functionName, args, kwargs)) 639 d = admin.workerRun(workerName, moduleName, 640 functionName, *args, **kwargs) 641 d.addErrback(errback) 642 d.addCallback(callback) 643 return d 644
645 - def getWizardEntry(self, componentType):
646 """Fetches a assistant bundle from a specific kind of component 647 @param componentType: the component type to get the assistant entry 648 bundle from. 649 @type componentType: string 650 @returns: a deferred returning either:: 651 - factory of the component 652 - noBundle error: if the component lacks a assistant bundle 653 @rtype: L{twisted.internet.defer.Deferred} 654 """ 655 self.waitForTask('get assistant entry %s' % (componentType, )) 656 self.clear_msg('assistant-bundle') 657 d = self._adminModel.callRemote( 658 'getEntryByType', componentType, 'wizard') 659 d.addCallback(self._gotEntryPoint) 660 return d
661
662 - def getWizardScenario(self, scenarioType):
663 """ 664 Fetches a scenario bundle from a specific kind of component. 665 666 @param scenarioType: the scenario type to get the assistant entry 667 bundle from. 668 @type scenarioType: string 669 @returns: a deferred returning either:: 670 - factory of the component 671 - noBundle error: if the component lacks a assistant bundle 672 @rtype: L{twisted.internet.defer.Deferred} 673 """ 674 self.waitForTask('get assistant entry %s' % (scenarioType, )) 675 self.clear_msg('assistant-bundle') 676 d = self._adminModel.callRemote( 677 'getScenarioByType', scenarioType, 'wizard') 678 d.addCallback(self._gotEntryPoint) 679 return d
680
681 - def getWizardPlugEntry(self, plugType):
682 """Fetches a assistant bundle from a specific kind of plug 683 @param plugType: the plug type to get the assistant entry 684 bundle from. 685 @type plugType: string 686 @returns: a deferred returning either:: 687 - factory of the plug 688 - noBundle error: if the plug lacks a assistant bundle 689 @rtype: L{twisted.internet.defer.Deferred} 690 """ 691 self.waitForTask('get assistant plug %s' % (plugType, )) 692 self.clear_msg('assistant-bundle') 693 d = self._adminModel.callRemote( 694 'getPlugEntry', plugType, 'wizard') 695 d.addCallback(self._gotEntryPoint) 696 return d
697
698 - def getWizardEntries(self, wizardTypes=None, provides=None, accepts=None):
699 """Queries the manager for a list of assistant entries matching the 700 query. 701 @param wizardTypes: list of component types to fetch, is usually 702 something like ['video-producer'] or 703 ['audio-encoder'] 704 @type wizardTypes: list of str 705 @param provides: formats provided, eg ['jpeg', 'speex'] 706 @type provides: list of str 707 @param accepts: formats accepted, eg ['theora'] 708 @type accepts: list of str 709 710 @returns: a deferred firing a list 711 of L{flumotion.common.componentui.WizardEntryState} 712 @rtype: L{twisted.internet.defer.Deferred} 713 """ 714 self.debug('querying wizard entries (wizardTypes=%r,provides=%r' 715 ',accepts=%r)'% (wizardTypes, provides, accepts)) 716 return self._adminModel.getWizardEntries(wizardTypes=wizardTypes, 717 provides=provides, 718 accepts=accepts)
719
720 - def setExistingComponentNames(self, componentNames):
721 """Tells the assistant about the existing components available, so 722 we can resolve naming conflicts when saving the configuration 723 @param componentNames: existing component names 724 @type componentNames: list of strings 725 """ 726 self._existingComponentNames = componentNames
727
728 - def workerChangedForStep(self, step, workerName):
729 """Tell a step that its worker changed. 730 @param step: step which worker changed for 731 @type step: a L{WorkerWizardStep} subclass 732 @param workerName: name of the worker 733 @type workerName: string 734 """ 735 if self._stepWorkers.get(step) == workerName: 736 return 737 738 self.debug('calling %r.workerChanged' % step) 739 step.workerChanged(workerName) 740 self._stepWorkers[step] = workerName
741 742 # Private 743
744 - def _gotEntryPoint(self, (filename, procname)):
745 # The manager always returns / as a path separator, replace them with 746 # the separator since the rest of our infrastructure depends on that. 747 filename = filename.replace('/', os.path.sep) 748 modname = pathToModuleName(filename) 749 d = self._adminModel.getBundledFunction(modname, procname) 750 self.clear_msg('assistant-bundle') 751 self.taskFinished() 752 return d
753
754 - def _setupWorker(self, step, worker):
755 # get name of active worker 756 self.debug('%r setting worker to %s' % (step, worker)) 757 step.worker = worker
758 767
768 - def _fetchDescription(self, step):
769 if not hasattr(step, 'model'): 770 self.setStepDescription('') 771 return 772 773 def gotComponentEntry(entry): 774 self.setStepDescription(entry.description)
775 776 d = self._adminModel.callRemote( 777 'getComponentEntry', step.model.componentType) 778 d.addCallback(gotComponentEntry) 779 780 # Callbacks 781
782 - def on_combobox_worker_changed(self, combobox, worker):
783 self.debug('combobox_workerChanged, worker %r' % worker) 784 if worker: 785 self.clear_msg('worker-error') 786 self._lastWorker = worker 787 step = self.getCurrentStep() 788 if step and isinstance(step, WorkerWizardStep): 789 self._setupWorker(step, worker) 790 self.workerChangedForStep(step, worker) 791 else: 792 msg = messages.Error(T_( 793 N_('All workers have logged out.\n' 794 'Make sure your Flumotion network is running ' 795 'properly and try again.')), 796 mid='worker-error') 797 self.add_msg(msg)
798
799 - def _on_assistant__help_clicked(self, assistant, section, anchor, version):
800 self._showHelpLink(section, anchor, version)
801
802 - def on_admin_connected_cb(self, admin):
803 self.window1.set_sensitive(True)
804
805 - def on_admin_disconnected_cb(self, admin):
806 self.window1.set_sensitive(False)
807