Alex Oddbratt

Forum Replies Created

Viewing 6 posts - 1 through 6 (of 6 total)
  • 17th April 2020 at 2:49 pm #32076

    Ah, didn’t know about the versions tab under the details tab for each template.

    – So basically what we’re doing is launching nuke from fTrack with our own custom Actions.

    – The user can pick which version of nuke to launch, ie 12.0v4 or v6 aso,
    and the path to the executable gets stored in a custom env var upon launch.

    – The user then wants to submit a job to muster through our custom submitter and this picks up our custom template.
    This template then has the following variables:

            exePath_Nuke = MTemplateAPI.MTemplateItemFile('EXEPATH_NUKE','Nuke executable','Path to Nuke render.exe application batch render','C:\\Program Files\\Nuke12.0v4\\Nuke12.0.exe',0,0,1,'*.exe')
            self.platformWindows.addClientConfigurationItem(exePath_Nuke)
            softwarePath_Nuke = MTemplateAPI.MTemplateItemFolder('SOFTWAREPATH_NUKE','Starting folder','Specify the starting folder for the nuke','C:\\Program Files\\Nuke12.0v4',0,0,1)
            self.platformWindows.addClientConfigurationItem(softwarePath_Nuke)
            exePath_Python = MTemplateAPI.MTemplateItemFile('EXEPATH_PYTHON','Python executable','Path to python.exe','\\\\fs\\data\\_pipeline\\_bin\\python\\2.7.11_64bit\\python.exe',0,0,1,'*.exe')
            self.platformWindows.addClientConfigurationItem(exePath_Python)
            softwarePath_Python = MTemplateAPI.MTemplateItemFolder('SOFTWAREPATH_PYTHON','Python software folder','Specify the software folder for python','\\\\fs\\data\\_pipeline\\_bin\\python\\2.7.11_64bit',0,0,1)
            self.platformWindows.addClientConfigurationItem(softwarePath_Python)

    – In the onBuildEnvironment it then reads in a bunch of custom env vars from a file on disk that contains custom env vars for this job including which nuke version it should run.

    – We would like to dynamically switch the version being used to match the version the user picked when launching through fTrack without having to have multiple templates and/or a manual selection process.
    Basically we would like to use our env vars instead of the hard coded paths to the EXEs.

    17th October 2019 at 11:55 am #23425

    I’ve tried having a job running for 10+ minutes on 3 nodes vs 4 nodes enabled.
    With 3 nodes enabled we always get 3×4, 12 instances through the whole job.
    With 4 nodes enabled we always get 4×1, 4 instances through the whole job.

    So it doesn’t seem like selection logic is working properly and it’s not refreshing/rerunning after a few seconds.
    Almost seems like the setLicenseMode is active as long as the amount of enabled nodes are below self.setMaximumLicenses(4) and then shuts off or changes if it’s more or equal.

    17th October 2019 at 11:42 am #23421

    So I’ve installed the latest version and modified my template so it’s
    self.setMaximumLicenses(4)
    self.setLicensesMode(MTemplateAPI.MTemplate.kTemplateLicenseCountNodes)

    I now get another funky issue.

    All our nodes are split into 4 instances. I have a job submitted and the 1 by 1 I unpause the nodes.
    So when I have 1×4 nodes unpause all instances picks up the job, same for 2×4 and 3×4. So while I only have 3 nodes unpaused all 12 instances run the job. But as soon as I unpause the 4th nodes instances ie trying to run 4×4 in accordance with the self.setMaximumLicenses(4)
    each node only allows 1 instance to run the job, so I get 4 nodes running the job on 1 instance each. If I pause the 4th node again all 12 instances pick up the job just fine. Something fishy seems to be going on here

    14th September 2019 at 3:43 pm #21849

    I tired the stock nuke template and it has the same issue.
    Even though I set the maximum licenses to 4 it still renders on all machines in the pool.

    14th September 2019 at 1:44 pm #21844

    I haven’t tried it with the stock template yet. I’ll give that a go, and yes we’re on 9.0.14.

    Here’s our custom template (Hope the code inject works):

    
    import os
    import re
    
    import MClientAPI
    import MTemplateAPI
    
    manager = MTemplateAPI.MManager.getUniqueClass()
    
    class STUDIO_nuke_template(MTemplateAPI.MTemplate):
        def __init__(self):
            MTemplateAPI.MTemplate.__init__(self)
            self.setID(1100)
            self.setName('STUDIO_nuke')
            self.setIconFilename('nuke.png')
            self.setDescription('Nuke render')
            self.setMaximumLicenses(4)
            self.setLicensesGroup('Nuke-licensing')
            self.setTemplateLogic(MTemplateAPI.kTemplateMultiframe)
            self.setDefaultPriority(70)
            self.setDefaultPools('2D_NUKE')
            
            ''' Multiframe template specific settings '''
            self.multiframe.setEnableFrameCheck(True)
            self.multiframe.setEnableMovieAssembler(False)
            self.multiframe.setCanProvideFramesMask(True)
            self.multiframe.setSupportsRenderLayerFlag(False)
    
            ''' Submission form items allocation '''
            filePath = MTemplateAPI.MTemplateItemFile('FILEPATH','Nuke script','Specifies the nuke script to render','',0,1,1,'*.nk')
            envPath = MTemplateAPI.MTemplateItemFile('ENVPATH','Environment file','Specifies the environment file to use','',0,1,1,'*.env')
            writeNode = MTemplateAPI.MTemplateItemString('WRITENODE','WriteNode','WriteNode','',0,1,1)
            outputPath = MTemplateAPI.MTemplateItemFolder('OUTPUTPATH','Frames destination','Specifies the frames destination','',0,1,1)
    
            self.addSubmissionItem(filePath)
            self.addSubmissionItem(envPath)
            self.addSubmissionItem(writeNode)
            self.addSubmissionItem(outputPath)
    
            ''' items mapping to Muster default tags '''
            self.addMapping('FILEPATH','job_file')
            self.addMapping('ENVPATH','job_env')
            self.addMapping('WRITENODE','job_writeNode')
            self.addMapping('OUTPUTPATH','output_folder')
            
            ''' Windows support '''
            self.platformWindows.setPlatformEnabled(True)
            self.platformWindows.setEnableErrorCheck(True)
            self.platformWindows.setEnabledByDefault(True)
            self.platformWindows.setFramesFloating(3)
            self.platformWindows.setDetectionLogic(MTemplateAPI.kProcessChild)
            self.platformWindows.setDetectionLogicProcessName('nuke.exe')
            
            applicationPath = MTemplateAPI.MTemplateItemFile('EXEPATH','Nuke executable','Path to Nuke render.exe application batch render','C:\\Program Files\\Nuke11.3v5\\Nuke11.3.exe',0,0,1,'*.exe')
            self.platformWindows.addClientConfigurationItem(applicationPath)
            startingFolderPath = MTemplateAPI.MTemplateItemFolder('SOFTWAREPATH','Starting folder','Specify the starting folder for the nuke','C:\\Program Files\\Nuke11.3v5',0,0,1)
            self.platformWindows.addClientConfigurationItem(startingFolderPath)
    
        ''' virtual functions overrides '''
        def onBuildEnvironment(self, job, chunk, clientTemplatePreferences, existingEnvironment):
            environment = existingEnvironment
            
            envFile = job.attributeGetString(manager.resolveMappingToJob(self.getID(),'ENVPATH'))
            if os.path.exists(envFile):
                FILE = open(envFile, 'r')
                for line in FILE:
                    if '=' in line:
                        envVar = line.rstrip('\n').rstrip('\r').split('=')[0]
                        value = line.rstrip('\n').rstrip('\r').split('=')[1]
                        environment.setValue(envVar, value)
                FILE.close()
    
            return environment
        
        def onBuildCommandLine(self, platform, job, chunk, clientTemplatePreferences,instanceNum):
            renderCmd = ' -F ' + '%.0f' % chunk.getStartFrame() + '-' + '%.0f' % chunk.getEndFrame()
            renderCmd += ' -X \"' + job.attributeGetString(manager.resolveMappingToJob(self.getID(),'WRITENODE')) + '\"'
            renderCmd += ' \"' + job.attributeGetString(manager.resolveMappingToJob(self.getID(),'FILEPATH')) + '\"'
            return renderCmd
            
        def onGetApplicationPath(self,job,chunk,clientTemplatePreferences,pathOut):
            pathOut.setString(clientTemplatePreferences['EXEPATH'])
            return MTemplateAPI.MTemplateError()
            
        def onGetApplicationStartingFolder(self,job,chunk,clientTemplatePreferences,pathOut):
            pathOut.setString(clientTemplatePreferences['SOFTWAREPATH'])
            return MTemplateAPI.MTemplateError()
            
        def onCheckForSubframeAdvancingString(self,job,chunk,line):
            return 0
    
        def onCheckForSubframeProgress(self,job,chunk,line, progressOut):
            return 0
    
        def onCheckForFramesMask(self,job,chunk,line,prefixOut):
            return 0
    		
        def onCheckForFramesMaskForLayer(self,job,chunk,line, prefixOut,layerOut):
            return 0
    
        def onGetFlagForRenderLayers(self,job,layers):
            return ""
    		
        def onCheckLogLine(self,job,chunk,clientTemplatePreferences,line,lineNum,warnings,errors,silencedWarnings,silencedErrors):	
            return MTemplateAPI.MTemplateError()
    		
        def onCheckLog(self,job,chunk,clientTemplatePreferences,log,warnings,errors,silencedWarnings,silencedErrors):
            lines = log.split("\n")
    		
            for idx, val in enumerate(lines):
                marker = manager.checkForLineParsingOverrides(job,chunk,val,idx)
                if marker.getType() == MClientAPI.MTextFileMarker.kFileMarkerError:
                    errors.append(marker)
                elif marker.getType() == MClientAPI.MTextFileMarker.kFileMarkerWarning:
                    warnings.append(marker)
                elif marker.getType() == MClientAPI.MTextFileMarker.kFileMarkerSilenceError:
                    ''' The builtin parser has a skip rule for this line, do not include it in the warnings/errors but in the silenced errors'''
                    silencedErrors.append(marker)
                elif marker.getType() == MClientAPI.MTextFileMarker.kFileMarkerSilenceWarning:
                    ''' The builtin parser has a skip rule for this line, do not include it in the warnings/errors but in the silenced warnings'''
                    silencedWarnings.append(marker)
                else:
                    ''' We have no fixed rule , parse the line according to the template logic '''
                    baseError = -1
                    baseWarning = -1
                    length = -1
                    baseError = val.lower().find('error')
                    baseWarning = val.lower().find('warning')
    				
                    if baseError != -1:
                        length = len(val)-baseError;
                        marker = MClientAPI.MTextFileMarker(MClientAPI.MTextFileMarker.kFileMarkerError,idx,baseError,length)
                        errors.append(marker)
                    if baseWarning != -1:
                        length = len(val)-baseWarning;
                        marker = MClientAPI.MTextFileMarker(MClientAPI.MTextFileMarker.kFileMarkerWarning,idx,baseWarning,length)
                        warnings.append(marker)
    			
            if len(errors) > 0 and len(warnings) > 0:
                return MTemplateAPI.MTemplateError(2,'Errors and warnings reported in the log. Please check the chunk details for a detailed list',MTemplateAPI.MTemplateError.kTemplateErrorTypeError)
            elif len(errors) > 0:
                return MTemplateAPI.MTemplateError(2,'Errors reported in the log. Please check the chunk details for a detailed list',MTemplateAPI.MTemplateError.kTemplateErrorTypeError)
            elif len(warnings) > 0:
                return MTemplateAPI.MTemplateError(1,'Warning reported in the log. Please check the chunk details for a detailed list',MTemplateAPI.MTemplateError.kTemplateErrorTypeWarning)
    	
            return MTemplateAPI.MTemplateError()
    		
        def onCheckExitCode(self,job,chunk,clientTemplatePreferences,exitCode):
            if exitCode != 0:
                return MTemplateAPI.MTemplateError(exitCode, 'Exit code %d different from expected 0 value' % exitCode,MTemplateAPI.MTemplateError.kTemplateErrorTypeWarning)
            return MTemplateAPI.MTemplateError()
    
        def onApplicationFinder(self,moduleRegExp,moduleTag):
            ''' Search nuke '''
            moduleTag.setString('Nuke')
            platform = MClientAPI.GetPlatform()
            moduleRegExp.setString('Nuke\d+\.\dv\d+.*?')
            return MTemplateAPI.kTemplateScanModule
    		
        def onFindApplication(self,basePath,clientTemplatePreferences):
            pass
    		
        def onModuleFound(self,moduleExec,modulePath,clientTemplatePreferences):
            platform = MClientAPI.GetPlatform()
            basePath = modulePath.getString()
            baseModule = moduleExec.getString()
            clientTemplatePreferences['SOFTWAREPATH'] = basePath
            clientTemplatePreferences['EXEPATH'] = os.path.join(basePath,baseModule)
            return 1
            
    ''' Create an instance of the template class and install it into the template manager context '''
    temp = STUDIO_nuke_template()
    manager.installPyTemplate(temp)
    
    13th September 2019 at 11:11 am #21787

    Are there any examples of how the mulit-task template would work?

Viewing 6 posts - 1 through 6 (of 6 total)