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