Muster Builtin scripting

Starting with version 8.5.0, Muster supports integrated scripting through the Console interface or extending the python functions inside any template. The Python scripting API let you customize behaviors of the Console, the Dispatcher and any Renderclient instance.

The Python modules available are:

  • Dispatcher : MDispatcherAPI , gives access to the entire Dispatcher data structures
  • Renderclient : MInstanceAPI , gives access to the Renderclient status as well as the running jobs
  • Console : MConsoleAPI , gives access to the Console data structures. It also gives access to the running QT Gui libraries to create custom UI inside Console

Usage scenario

Here are some idea in what contexts you can use the builtin Muster scripting facilities:

Dispatcher scripting

One of the most useful script you can write on the Dispatcher is to perform background tasks. You can script the Dispatcher from within the global template to perform idling tasks using the template member function onDispatcherIdling() available only in the global template (0). This function is invoked automatically by the Dispatcher every 60 seconds. If you need to call it more or less frequently, you can change the idling time by setting the preferences like in the example below:

def onDispatcherIdling(self):
    import MClientAPI
    import MDispatcherAPI
    config = MClientAPI.MDispatcherConfiguration()
    err = MDispatcherAPI.systemGetConfiguration(config)
    config.tuning.setIdlingScriptingTime(10) # Execute this function each 10 seconds
    MDispatcherAPI.systemSetConfiguration(config,0) # We do not store the settings in the dispatcher.conf file, just flush back the changes in memory
 
    # Now executes your idling function, in this example , we automatically lock completed jobs
    err, jobs = MDispatcherAPI.jobGetList()
    for job in jobs:
        if (job.getStatus() == MClientAPI.MJob.kJobsStatusJobCompleted):
            job.setLocked(1) # Locks the job
            MDispatcherAPI.jobUpdate(job.getJobId()) # Refresh the job status to connected Consoles

Other useful tasks may be performed on the Dispatcher using the onJobCompleted() and onJobStart() templates functions. Also you can register a script to be run on the Dispatcher by registering a new script into the Console and select the Dispatcher as the execution context. This will make the script on-demand from the Console GUI.

Client scripting

One of the most useful script you can write on the client is to perform background tasks as well as checking your client availability.

You can script the client from within the global template to perform idling tasks using the template member function onClientIdling() available only in the global template (0). This function is invoked automatically by the client every 60 seconds. If you need to call it more or less frequently, you can change the idling time by setting the preferences like in the example below:

def onClientIdling(self):
    import MClientAPI
    import MInstanceAPI
    config = MClientAPI.MNodeConfiguration()
    err = MInstanceAPI.getConfiguration(config)
    config.tuning.setIdlingScriptingTime(10) # Execute this function each 10 seconds
    MInstanceAPI.setConfiguration(config,0) # We do not store the settings in the dispatcher.conf file, just flush back the changes in memory
 
    # Now executes your idling function
def onCheckClientAvailability:
    import MClientAPI
    import MInstanceAPI
    err, status = MInstanceAPI.getInstanceStatus(0) # Grabs the status of instance 0, you should tweak this if running multiple instances by collecting the overall status
    if (status == MClientAPI.MNode.kInstanceStatusInprogress):
        # There's a rendering in progress, check what job is rendering
        err, job = MInstanceAPI.getRunningJob()
        if (job.getName() == "My very special job"):
            # Do not stop the client availability but confirm it
            return MTemplateAPI.kTemplateFunctionSuccess # Client will be available for rendering
 
    # Otherwise calculates the default availability
    return MTemplateAPI.kTemplateFunctionDoDefault

Console scripting

Here there are unlimited capabilities. With the inclusion of PySide inside the Muster python distribution, you can write your own GUI to react to context menus inside the Muster views as well as create your own submission or job checking dialogs.

One of the most useful implementation may be to create a custom dialog to validate any job you submit into Console. This can be easily done writing the onJobValidate() on the global template (0) or any specific engine template. This is a full example that asks for an extra job attribute at the time of the submission, you can then recall your custom attribute on any template or script:

# The following script goes at the top of the global template, it declares a Job Validator Class only if running inside the Console context
 
if (muster.runningContext() == "console"):
 
    from PySide import QtCore, QtGui
    import MConsoleAPI
 
    class JobValidatorDialog(QtGui.QDialog):
 
        def __init__(self):
            super(JobValidatorDialog,self).__init__()
            self.initUI()
 
        def initUI(self):
            self.layout = QtGui.QVBoxLayout()
 
            self.label = QtGui.QLabel("Job shot:")
            self.shot = QtGui.QLineEdit()
            self.buttonBox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok| QtGui.QDialogButtonBox.Cancel)
 
            self.layout.addWidget(self.label)
            self.layout.addWidget(self.shot)
            self.layout.addStretch()
            self.layout.addWidget(self.buttonBox)
 
            self.setLayout(self.layout)
            self.buttonBox.accepted.connect(self.accept)
            self.buttonBox.rejected.connect(self.reject)

Then, at the template definition level, you can change the onValidateJob function in this way:

    def onValidateJobSubmission(self, job, err):
        if (muster.runningContext() != "console"): return 1 # Skip validation on no console contexts
        dlg = JobValidatorDialog()
        result = dlg.exec()
        if (result == QtGui.QDialog.Accepted):
            job.attributeSetString("JOBSHOT",  dlg.shot.text(), 1 ,0) # If user clicks OK, a new attribute is appended to the job, it can be later checked from the templates, or shown in console by customizing the job view headers
            return 1
        else:
            err.setString("Cancelled by user!")
            return 0