Example #1
0
    def run(self):

        print "start"
        self.logFile.write("start\n")

        # Grab the first agenda item
        print "Requesting Work:"
        self.logFile.write("Requesting Work\n")
        self.currAgendaItem = qb.requestwork()
        print "Got Work"
        self.logFile.write("Got Work\n")
        self.logFile.write("Work: " + str(self.currAgendaItem) + '\n')
        print "Work: " + str(self.currAgendaItem)

        while True:
            # Get the progress from the queue
            progress = int(self.queue.get())

            # Loop to update progress gaps
            while True:
                agendaProgress = int(
                    self.currAgendaItem['id']
                )  # ID represents the percent complete 1-100
                if (progress >= agendaProgress):
                    self.currAgendaItem['status'] = 'complete'
                    qb.reportwork(self.currAgendaItem)

                    # Get a new agenda item if possible
                    self.currAgendaItem = qb.requestwork()
                    self.logFile.write("Work: " + str(self.currAgendaItem) +
                                       '\n')
                    print "Work: " + str(self.currAgendaItem)
                    if (int(self.currAgendaItem['id']) == -1
                            or int(self.currAgendaItem['id']) == 101):
                        break
                elif (progress < agendaProgress):
                    break

            # Signal that the queue task is complete
            self.queue.task_done()

            # If the job is complete, empty the queue of remaining jobs
            if (int(self.currAgendaItem['id']) == -1
                    or int(self.currAgendaItem['id']) == 101):
                print 'Reporter: Job Complete, emptying queue of ' + str(
                    self.queue.qsize()) + ' extra jobs'
                while True:
                    self.queue.get()
                    self.queue.task_done()

        self.logFile.close()
Example #2
0
    def run(self):
        
        print "start"
        self.logFile.write("start\n")

        # Grab the first agenda item
        print "Requesting Work:"
        self.logFile.write("Requesting Work\n")
        self.currAgendaItem = qb.requestwork()
        print "Got Work"
        self.logFile.write("Got Work\n")
        self.logFile.write("Work: " + str(self.currAgendaItem) + '\n')
        print "Work: " + str(self.currAgendaItem)
        
        while True:
            # Get the progress from the queue
            progress = int(self.queue.get())
                
            # Loop to update progress gaps
            while True:
                agendaProgress = int(self.currAgendaItem['id']) # ID represents the percent complete 1-100
                if (progress >= agendaProgress):
                    self.currAgendaItem['status'] = 'complete'
                    qb.reportwork(self.currAgendaItem)
                
                    # Get a new agenda item if possible
                    self.currAgendaItem = qb.requestwork()
                    self.logFile.write("Work: " + str(self.currAgendaItem) + '\n')
                    print "Work: " + str(self.currAgendaItem)
                    if (int(self.currAgendaItem['id']) == -1 or int(self.currAgendaItem['id']) == 101):
                        break
                elif (progress < agendaProgress):
                    break
            
            # Signal that the queue task is complete
            self.queue.task_done()
            
            # If the job is complete, empty the queue of remaining jobs
            if (int(self.currAgendaItem['id']) == -1 or int(self.currAgendaItem['id']) == 101):
                print 'Reporter: Job Complete, emptying queue of ' + str(self.queue.qsize()) + ' extra jobs'
                while True:
                    self.queue.get()
                    self.queue.task_done()
    
        self.logFile.close()
Example #3
0
def main():
    """The main worker, responsible for all direct communication with Qube.
    
    Since this is the only process that is able to communicate directly with
    Qube, we do not have control over what specific process is called, and we
    will often want to run code in a different interpreter, this function does
    not execute the job directly. Instead, is spawns a child which it talks to
    via pipes. This also gives us the oppourtunity to bootstrap the development
    environment if needed.
    
    """

    log('using worker %s' % __file__)

    job = qb.jobobj()

    log('recieved job %d' % job['id'])

    # We don't need the child to have the full agenda.
    job_for_child = dict(job)
    job_for_child.pop('agenda', None)

    # The main loop. Continuously request more work and dispatch it to a child.
    # Keep on going until we are no longer given an agenda that we can operate
    # on.
    while True:

        agenda = qb.requestwork()

        log('recieved work %r (%s)' % (agenda['name'], agenda['status']))
        sys.stdout.flush()

        # Be aware that we cannot unpack the agenda at this point since it may
        # reply upon the child's environment in order to function.

        # Handle finished states. When a job is complete Qube will continue to
        # hand us agendas, but they will have a status that indicates that we
        # cannot work on them. So we report that as the final job status and
        # finish working on it.
        if agenda['status'] in ('complete', 'pending', 'blocked'):
            # 'complete' -> No more frames.
            # 'pending' -> Preempted, so bail out.
            # 'blocked' -> Perhaps item is part of a dependency.
            log('reporting job as %s' % agenda['status'])
            qb.reportjob(agenda['status'])
            log('worker shutting down')
            return

        # Waiting; relatively rare, try again shortly.
        elif agenda['status'] == 'waiting':
            timeout = 10  # seconds
            log('job %s is waiting for %ds' % (job['id'], timeout))
            time.sleep(timeout)
            continue

        package_to_print = dict(agenda['package'])
        package_to_print.pop('__pickle__', None)
        log('work package')
        pprint.pprint(package_to_print)
        print '# ---'

        # Prepare some pipes for communicating with the subprocess.
        request_pipe = os.pipe()
        response_pipe = os.pipe()

        # Open the process, doing a dev bootstrap if this is a dev environment.
        # We are calling this module, which will end up calling the
        # :func:`execute` function.
        cmd = []
        if 'VEE_EXEC_ARGS' in os.environ or 'KS_DEV_ARGS' in os.environ:
            # These both have a "dev" command with a "--bootstrap" which do
            # the same thing.
            cmd.extend(('dev', '--bootstrap'))
        cmd.extend((
            agenda['package'].get('interpreter', 'python'),
            '-m',
            'qbfutures.sandbox.the_corner',
            str(request_pipe[0]),
            str(response_pipe[1]),
        ))
        log('spawning child: %s' % subprocess.list2cmdline(cmd))
        proc = subprocess.Popen(cmd, close_fds=False)

        # Close our end of the pipes so that there is only one process which
        # still has them open (the child).
        os.close(request_pipe[0])
        os.close(response_pipe[1])

        # Send the job and agenda package to the child.
        with os.fdopen(request_pipe[1], 'w') as request_fh:
            pickle.dump(job_for_child, request_fh, -1)
            pickle.dump(agenda['package'], request_fh, -1)

        # Get the response from the child.
        with os.fdopen(response_pipe[0], 'r') as response_fh:
            try:
                package = pickle.load(response_fh)
            except Exception as e:
                traceback.print_exc()
                sys.stderr.flush()
                package = {
                    'status': 'failed',
                    'exception': e,
                }

        # Wait for the child to finish.
        # log('waiting for child...')
        proc.wait()

        package.setdefault('status', 'failed')
        agenda['resultpackage'] = utils.pack(package)
        agenda['status'] = package['status']

        log('work resultpackage')
        pprint.pprint(package)
        print '# ---'

        if agenda['status'] == 'failed':
            log('os.environ')
            for k, v in sorted(os.environ.iteritems()):
                print '#  %s = %r' % (k, v)
            print '# ---'

        log('reporting work as %s' % agenda['status'])

        qb.reportwork(agenda)
Example #4
0
def executeJob(job):
    '''
    Execute the transcoding process.

    '''

    # Convert the paths
    logging.debug("#### Start Path Convert")
    job = pathConvert.convertDict(job)
    logging.debug("#### End Path Convert")

    jobstate = 'pending'
    pkg = job.setdefault('package', {})
    # logger.debug("Qube Job: " + str(job))

    try:
        memUsage = setupAEMemory()

        if memUsage['error'] == True:
            raise Exception, "Fatal Error with Memory Usage"

        # Setup multiprocessing
        multProcs = memUsage['multProcs']
        ramPerCPU = 2
        if (pkg.has_key("complexity")):
            logging.debug("complexity found")
            complexValue = pkg["complexity"].lower()
            if complexValue == "normal":
                pass
            elif complexValue == "complex":
                multProcs = True
                ramPerCPU = 3
            elif complexValue == "simple":
                multProcs = False

        # Get the number of virtual cores
        cores = getCPUCores(memUsage['aeAvailMem'] / 1024, ramPerCPU)
        logging.debug("Cores: %s" % cores)

        script = "RenderTools.setMultiprocessing(\"%s\",%d, %d);" % (
            multProcs, ramPerCPU, cores['reserved'])
        if (multProcs):
            logging.info(
                "Multiprocessing: %s - %dGB per CPU with %d Cores Available" %
                (multProcs, ramPerCPU, cores['available']))
        else:
            logging.info("Multiprocessing: %s" % multProcs)

        aeSocket = AESocket.AESocket()
        aeSocket.launchAERender(multProcs,
                                projectFile=pkg['renderProjectPath'])

        # Load the render tools
        script = "$.evalFile(new File(\"%s\"));\"Render Tools Loaded\"" % os.path.abspath(
            RENDERTOOLS)
        logging.debug("Render Tools Script: %s" % script)
        aeSocket.runScript(script, "Render Tools Setup")

        script = "RenderTools.openProject(\"%s\");" % pkg['renderProjectPath']
        result = aeSocket.runScript(script, "Open Project")
        logging.debug("Open Project Result: %s" % script)

        # Finalize multiprocessing setting
        # result = aeSocket.runScript(script, "Set Multiprocessing")
        # logging.debug("Set Multiprocessing Result: %s" % result)

        # script = "RenderTools.setRenderQuality(\"%s\");" % pkg['quality']
        # result = aeSocket.runScript(script, "Set Render Quality")
        # logging.debug("Set Render Quality Result: %s" % script)

        while 1:
            agendaItem = qb.requestwork()
            logger.debug("Agenda Item: " + str(agendaItem))
            returnCode = 0
            '''
            First Handle non-running state cases
            '''
            if agendaItem['status'] in ('complete', 'pending', 'blocked',
                                        'waiting'):
                '''
                complete -- no more frames
                pending -- preempted, so bail out
                blocked -- perhaps item is part of a dependency
                '''
                jobstate = agendaItem['status']
                logger.debug('Job %s state is now %s' % (job['id'], jobstate))
                break
            '''
            Main
            '''
            if agendaItem['name'].strip() != '':
                if '-' in agendaItem['name']:
                    startFrame, endFrame = agendaItem['name'].split('-')
                else:
                    startFrame = endFrame = agendaItem['name']
                logger.debug("startFrame: " + str(startFrame) + " endFrame: " +
                             str(endFrame))

                script = "RenderTools.setupSegment(%s, %s, %s);" % (
                    startFrame, endFrame, pkg['rqIndex'])
                logging.debug("Setup Segment Script: %s" % script)
                aeSocket.runScript(script, "Setup Segment")
                outputs = pkg['outputFiles'].split(",")
                logger.debug("Outputs: " + str(outputs))

                renderTools = Tools.Tools(agendaItem, outputs,
                                          aeSocket.logFilePath, startFrame,
                                          endFrame)
                script = "RenderTools.renderQueued();"
                aeSocket._sendScript(script, "Render Queued",
                                     timeout=None)  # Disable timeout here
                monitorResult = renderTools.monitorRender(
                    timeout=1800)  # Max time for frame is 30min
                socketResult = aeSocket.getResponse()
                renderTools.alreadyComplete = True

                if socketResult == True:
                    returnCode = 1
                if not monitorResult:
                    returnCode = 2

                outputPaths = renderTools.getOutputPaths(startFrame, endFrame)
                for path in outputPaths:
                    if not os.path.exists(path):
                        logging.error("Frame doesn't exist: %s" % path)
                        returnCode = 3
                '''
                Update the work status based on the return code.
                '''
                if returnCode == 0:
                    outputPaths = pathConvert.convertList(outputPaths,
                                                          reverse=True)
                    agendaItem['resultpackage'] = {
                        'outputPaths': ','.join(outputPaths),
                        'progress': '1'
                    }
                    agendaItem['status'] = 'complete'
                else:
                    agendaItem['status'] = 'failed'
            ''' Report back the results to the Supervisor '''
            qb.reportwork(agendaItem)

    except Exception, e:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        logger.error(repr(traceback.extract_tb(exc_traceback)))
        logger.error(
            repr(traceback.format_exception(exc_type, exc_value,
                                            exc_traceback)))
Example #5
0
def executeJob(job):
    '''
    Execute the transcoding process.

    '''

    # Convert the paths
    logging.debug("#### Start Path Convert")
    job = pathConvert.convertDict(job)
    logging.debug("#### End Path Convert")

    jobstate = 'pending'
    pkg = job.setdefault('package', {})
    # logger.debug("Qube Job: " + str(job))

    try:
        memUsage = setupAEMemory()
        
        if memUsage['error'] == True:
            raise Exception, "Fatal Error with Memory Usage"
        
        # Setup multiprocessing
        multProcs = memUsage['multProcs']
        ramPerCPU = 2
        if (pkg.has_key("complexity")):
            logging.debug("complexity found")
            complexValue = pkg["complexity"].lower()
            if complexValue == "normal":
                pass
            elif complexValue == "complex":
                multProcs = True
                ramPerCPU = 3
            elif complexValue == "simple":
                multProcs = False

        # Get the number of virtual cores
        cores = getCPUCores(memUsage['aeAvailMem']/1024, ramPerCPU)
        logging.debug("Cores: %s" % cores)

        script = "RenderTools.setMultiprocessing(\"%s\",%d, %d);" % (multProcs, ramPerCPU, cores['reserved'])
        if (multProcs):
            logging.info("Multiprocessing: %s - %dGB per CPU with %d Cores Available" % (multProcs, ramPerCPU, cores['available']))
        else:
            logging.info("Multiprocessing: %s" % multProcs)

        aeSocket = AESocket.AESocket()
        aeSocket.launchAERender(multProcs, projectFile=pkg['renderProjectPath'])

        # Load the render tools
        script = "$.evalFile(new File(\"%s\"));\"Render Tools Loaded\"" % os.path.abspath(RENDERTOOLS)
        logging.debug("Render Tools Script: %s" % script)
        aeSocket.runScript(script, "Render Tools Setup")

        script = "RenderTools.openProject(\"%s\");" % pkg['renderProjectPath']
        result = aeSocket.runScript(script, "Open Project")
        logging.debug("Open Project Result: %s" % script)
        
        # Finalize multiprocessing setting
        # result = aeSocket.runScript(script, "Set Multiprocessing")
        # logging.debug("Set Multiprocessing Result: %s" % result)
         
        # script = "RenderTools.setRenderQuality(\"%s\");" % pkg['quality']
        # result = aeSocket.runScript(script, "Set Render Quality")
        # logging.debug("Set Render Quality Result: %s" % script)

        while 1:
            agendaItem = qb.requestwork()
            logger.debug("Agenda Item: " + str(agendaItem))
            returnCode = 0

            '''
            First Handle non-running state cases
            '''
            if agendaItem['status'] in ('complete', 'pending', 'blocked', 'waiting'):
                '''
                complete -- no more frames
                pending -- preempted, so bail out
                blocked -- perhaps item is part of a dependency
                '''
                jobstate = agendaItem['status']
                logger.debug('Job %s state is now %s' % (job['id'], jobstate))
                break

            '''
            Main
            '''
            if agendaItem['name'].strip() != '':
                if '-' in agendaItem['name']:
                    startFrame, endFrame = agendaItem['name'].split('-')
                else:
                    startFrame = endFrame = agendaItem['name']
                logger.debug("startFrame: " + str(startFrame) + " endFrame: " + str(endFrame))
                
                script = "RenderTools.setupSegment(%s, %s, %s);" % (startFrame, endFrame, pkg['rqIndex'])
                logging.debug("Setup Segment Script: %s" % script)
                aeSocket.runScript(script, "Setup Segment")
                outputs = pkg['outputFiles'].split(",")
                logger.debug("Outputs: " + str(outputs))

                renderTools = Tools.Tools(agendaItem, outputs, aeSocket.logFilePath, startFrame, endFrame)
                script = "RenderTools.renderQueued();"
                aeSocket._sendScript(script, "Render Queued", timeout=None) # Disable timeout here
                monitorResult = renderTools.monitorRender(timeout=1800) # Max time for frame is 30min
                socketResult = aeSocket.getResponse()
                renderTools.alreadyComplete = True

                if socketResult == True:
                    returnCode = 1
                if not monitorResult:
                    returnCode = 2

                outputPaths = renderTools.getOutputPaths(startFrame, endFrame)
                for path in outputPaths:
                    if not os.path.exists(path):
                        logging.error("Frame doesn't exist: %s" % path)
                        returnCode = 3

                '''
                Update the work status based on the return code.
                '''
                if returnCode == 0:
                    outputPaths = pathConvert.convertList(outputPaths, reverse=True)
                    agendaItem['resultpackage'] = {'outputPaths': ','.join(outputPaths),'progress':'1'}
                    agendaItem['status'] = 'complete'
                else:
                    agendaItem['status'] = 'failed'

            ''' Report back the results to the Supervisor '''
            qb.reportwork(agendaItem)
    
    except Exception, e:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        logger.error(repr(traceback.extract_tb(exc_traceback)))
        logger.error(repr(traceback.format_exception(exc_type, exc_value, exc_traceback)))
Example #6
0
def executeJob(control):
    '''
    Execute the transcoding process.

    '''

    jobstate = 'pending'
    qbJob = control.getQubeJobObject()

    if not control.checkForErrors():
        while 1:
            agendaItem = qb.requestwork()
            returnCode = 1
            '''
            First Handle non-running state cases
            '''
            if agendaItem['status'] in ('complete', 'pending', 'blocked',
                                        'waiting'):
                '''
                complete -- no more frames
                pending -- preempted, so bail out
                blocked -- perhaps item is part of a dependency
                '''
                jobstate = agendaItem['status']
                logger.info('Job %s state is now %s' % (qbJob['id'], jobstate))
                break
            '''
            Run the transcoding process that corresponds to the agendaItem name.
                Initialize
                    Setup the blender scene.
                Segment
                    Transcode a segment of frames.
                Final Output
                    Merge that output's segments together with audio.
            '''
            if agendaItem['name'] == 'Initialize':
                logger.info('Initializing...\n')

                if os.path.exists(control.getBlendFile()):
                    logger.info("Initialization script alread exists.")
                    returnCode = 0
                else:
                    returnCode = runCMD(control.getInitCMD(agendaItem))

                if returnCode == 0:
                    logger.info('Initialization Complete! (' +
                                str(returnCode) + ')\n')
                else:
                    logger.error('Initialization Failed! (' + str(returnCode) +
                                 ')\n')

            elif agendaItem['name'].startswith('Segment'):
                '''
                Segment Process:
                    > Make sure the blender project was created
                    > Load the segment frame range from the work package.
                    > Check for missing frames in the segments frame range.
                        True
                            > Report an error and end the subjob
                        False
                            > Continue with transcode
                    > Check if smartUpdate is turned on.
                        True
                            > Check if the Modification Times Database and the output file exists.
                                True
                                    > Check if there have been modifications to any frames in this segment.
                                        True
                                            > Continue with transcode
                                        False
                                            > Skip transcoding
                    > Check if we are still transcoding.
                        True
                            > Try to Remove output file if it already exists
                                Success
                                    > Transcode
                                Failure
                                    > Add '_' to the output file name and try again until sucess.
                                        Max retry is 3 times, then fail the subjob.
                    > Check if smartUpdate is turned on.
                        True
                            > Update the modification times database
                        False
                            > Continue
                    > Update the resultPackage with whether changes were made and the segment output file.
                '''

                logger.info("Starting Transcoder Segment Process...")
                render = False
                error = False
                ''' Check for the blender project '''
                if not os.path.exists(control.getBlendFile()):
                    logger.error('Blender Project doesn\'t exist.')
                    error = True
                else:
                    ''' Load the frame range from the work package '''
                    frameRangeString = agendaItem['package']['frameRange']
                    logging.debug("FrameRangeString: %s" % frameRangeString)
                    frameRange = sequenceTools.loadFrameRange(frameRangeString)
                    logging.debug("Loaded frameRange: %s" % frameRange)
                    ''' Check for Missing Frames '''
                    mySequence = control.getSequence()
                    missingFrames = mySequence.getMissingFrames(frameRange)

                    if len(missingFrames) > 0:
                        logger.error('Missing frames!\n' +
                                     ','.join(missingFrames))
                        error = True
                    else:
                        logger.info('No Missing Frames')

                if not error:
                    ''' Variables used multiple times '''
                    segmentFilePath = agendaItem.setdefault('package', {}).get(
                        'segmentFile', '')
                    segmentFileExists = os.path.exists(segmentFilePath)
                    currentModTimes = {}
                    modTimeDBFile = control.getModTimeDBFile()

                    if control.getSmartUpdate():

                        modTimeDBFileExists = os.path.exists(modTimeDBFile)

                        logger.debug('Segment Output: ' + str(segmentFilePath))
                        logger.debug('Segment Exists: ' +
                                     str(segmentFileExists))
                        logger.debug('ModTimeDBFile: ' + str(modTimeDBFile))
                        logger.debug('ModTimeDBFile Exists: ' +
                                     str(modTimeDBFileExists))

                        if segmentFileExists and modTimeDBFileExists:
                            logger.info('Smart Updating')

                            logger.debug(
                                'Loading current modification times...')
                            currentModTimes = mySequence.getModTimes(
                                frameRange)
                            logger.debug(
                                'Current modication times loaded. %s' %
                                currentModTimes)

                            logger.debug(
                                'Comparing modification times for frame range %s...'
                                % frameRange)
                            compare = mySequence.compare(
                                modTimeDBFile,
                                frameRange,
                                currentModTimes=currentModTimes)
                            logger.debug('Sequence Differences: %s' %
                                         str(compare))

                            differences = ''
                            if len(compare['Added']) > 0:
                                frameNumbers = mySequence.getFramesFromFilenames(
                                    compare['Added'])
                                differences += '\n\tAdded: %s' % mySequence.convertListToRanges(
                                    frameNumbers)
                            if len(compare['Deleted']) > 0:
                                frameNumbers = mySequence.getFramesFromFilenames(
                                    compare['Deleted'])
                                differences += '\n\tDeleted: %s' % mySequence.convertListToRanges(
                                    frameNumbers)
                            if len(compare['Modified']) > 0:
                                frameNumbers = mySequence.getFramesFromFilenames(
                                    compare['Modified'])
                                differences += '\n\tModified: %s' % mySequence.convertListToRanges(
                                    frameNumbers)

                            if differences:
                                logger.info('Sequence Differences: %s' %
                                            differences)
                                render = True

                        else:
                            render = True

                    else:
                        render = True

                    if render:
                        segmentFilePath = control.getValidOutputPath(
                            segmentFilePath)
                        if segmentFilePath:
                            render = True
                        else:
                            error = True

                    if render:
                        logger.info('Transcoding Segment %s' %
                                    agendaItem['name'])
                        cmd = control.getSegmentCMD(agendaItem)
                        returnCode = runCMD(cmd)
                        logger.info('Transcoding Segment Complete! (' +
                                    str(returnCode) + ')')

                        if control.getSmartUpdate():
                            logger.debug("Saved ModTimes: " +
                                         str(currentModTimes))
                            control.job.sequence.saveModTimes(
                                modTimeDBFile,
                                modTimeDict=currentModTimes,
                                frameRange=frameRange)
                            logger.info("Saved Modification Times")

                    else:
                        if not error:
                            logger.info("No changes to segment " +
                                        agendaItem['name'])
                            returnCode = 0
                    '''
                    Check if this is the last agenda item that's complete.
                    If so, unblock the final output subjobs.
                    '''
                    agendaItem['resultpackage'] = {
                        'Changed': render,
                        'segmentFile': segmentFilePath
                    }

                    logger.info("Transcoder Segment Process Complete!\n")

            elif agendaItem['name'].startswith('Output'):
                '''
                Final Output Process
                    > Gather the output paths and changes for all of the segments
                    stored in their resultPackages.
                    > Check if there were changes.
                        True
                            > Continue Transcoding
                        False
                            > Skip Trancoding
                    > Load segmentOutputPaths from the agenda
                    > Check if segmentPaths exist.
                        True
                            > Continue Transcoding
                        False
                            > Skip Transcoding with error
                    > Check if output path is valid
                        True
                            > Continue Transcoding
                        False
                            > Skip Transcoding with error
                    > Check if we are still transcoding.
                        True
                            > Concatenate segments together.
                        False
                            > Skip
                    > Update the resultPackage with the outputPath.
                '''

                logger.info("Starting Final Output Process...\n")

                transcode = False
                errors = False

                dependantNames = agendaItem.get('package',
                                                {}).get('segmentSubjobs', '')
                logger.debug('Dependants Names: ' + str(dependantNames))

                dependantSegments = control.getSegments(dependantNames)
                logger.debug('Dependants Segments: ' + str(dependantSegments))

                finalOutputPath = agendaItem['package']['outputFile']
                if finalOutputPath:
                    transcode = True
                else:
                    errors = True

                if transcode:
                    changes = control.checkSegmentsForChanges(
                        dependantSegments)
                    if not changes:
                        logger.info("No changes to segments.")

                        if os.path.exists(str(finalOutputPath)):
                            logger.info('Final output movie already exists. ' +
                                        str(finalOutputPath))
                            returnCode = 0
                            transcode = False
                        else:
                            logger.info('Final output movie doesn\'t exist. ' +
                                        str(finalOutputPath))
                            transcode = True
                    else:
                        transcode = True

                if transcode:
                    segmentOutputPaths = control.getSegmentOutputPaths(
                        dependantSegments)
                    if segmentOutputPaths:
                        transcode = True
                    else:
                        errors = True

                if transcode:
                    startFrame = control.getOutputStartFrame(dependantSegments)
                    logger.debug('Output Start Frame: ' + startFrame)

                if transcode:
                    finalOutputPath = control.getValidOutputPath(
                        finalOutputPath)
                    if finalOutputPath:
                        transcode = True
                    else:
                        errors = True

                if transcode:
                    cmd = control.getFinalOutputCMD(segmentOutputPaths,
                                                    finalOutputPath,
                                                    startFrame, 29.97,
                                                    agendaItem)
                    returnCode = runCMD(cmd)
                    if returnCode != 0:
                        errors = True
                else:
                    logger.info('No changes to Final ' + agendaItem['name'])

                if not errors:
                    agendaItem['resultpackage'] = {
                        'outputPaths': finalOutputPath
                    }
                    logger.info('Transcoder Final ' + agendaItem['name'] +
                                ' Completed Succesfully! (' + str(returnCode) +
                                ')\n')
                else:
                    logger.info('Transcoder Final ' + agendaItem['name'] +
                                ' Failed!' + str(returnCode) + ')\n')

            else:
                logger.error("Invalid Agenda Item")
                logger.error(str(agendaItem))
            '''
            Update the work status based on the return code.
            '''
            if returnCode == 0:
                agendaItem['status'] = 'complete'
            else:
                agendaItem['status'] = 'failed'
            ''' Report back the results to the Supervisor '''
            qb.reportwork(agendaItem)

    if jobstate == 'blocked':
        jobstate = 'pending'

    return jobstate
Example #7
0
def main():
    """The main worker, responsible for all direct communication with Qube.
    
    Since this is the only process that is able to communicate directly with
    Qube, we do not have control over what specific process is called, and we
    will often want to run code in a different interpreter, this function does
    not execute the job directly. Instead, is spawns a child which it talks to
    via pipes. This also gives us the oppourtunity to bootstrap the development
    environment if needed.
    
    """
    
    log('using worker %s' % __file__)

    job = qb.jobobj()
    
    log('recieved job %d' % job['id'])
    
    # We don't need the child to have the full agenda.
    job_for_child = dict(job)
    job_for_child.pop('agenda', None)
    
    # The main loop. Continuously request more work and dispatch it to a child.
    # Keep on going until we are no longer given an agenda that we can operate
    # on.
    while True:
        
        agenda = qb.requestwork()
        
        log('recieved work %r (%s)' % (agenda['name'], agenda['status']))
        sys.stdout.flush()
        
        # Be aware that we cannot unpack the agenda at this point since it may
        # reply upon the child's environment in order to function.
        
        # Handle finished states. When a job is complete Qube will continue to
        # hand us agendas, but they will have a status that indicates that we
        # cannot work on them. So we report that as the final job status and
        # finish working on it.
        if agenda['status'] in ('complete', 'pending', 'blocked'):
            # 'complete' -> No more frames.
            # 'pending' -> Preempted, so bail out.
            # 'blocked' -> Perhaps item is part of a dependency.
            log('reporting job as %s' % agenda['status'])
            qb.reportjob(agenda['status'])
            log('worker shutting down')
            return
        
        # Waiting; relatively rare, try again shortly.
        elif agenda['status'] == 'waiting':
            timeout = 10 # seconds
            log('job %s is waiting for %ds' % (job['id'], timeout))
            time.sleep(timeout)
            continue
        
        package_to_print = dict(agenda['package'])
        package_to_print.pop('__pickle__', None)
        log('work package')
        pprint.pprint(package_to_print)
        print '# ---'
        
        # Prepare some pipes for communicating with the subprocess.
        request_pipe = os.pipe()
        response_pipe = os.pipe()
        
        # Open the process, doing a dev bootstrap if this is a dev environment.
        # We are calling this module, which will end up calling the
        # :func:`execute` function.
        cmd = []
        if 'VEE_EXEC_ARGS' in os.environ or 'KS_DEV_ARGS' in os.environ:
            # These both have a "dev" command with a "--bootstrap" which do
            # the same thing.
            cmd.extend(('dev', '--bootstrap'))
        cmd.extend((
            agenda['package'].get('interpreter', 'python'),
            '-m', 'qbfutures.sandbox.the_corner',
            str(request_pipe[0]), str(response_pipe[1]),
        ))
        log('spawning child: %s' % subprocess.list2cmdline(cmd))
        proc = subprocess.Popen(cmd, close_fds=False)
        
        # Close our end of the pipes so that there is only one process which
        # still has them open (the child).
        os.close(request_pipe[0])
        os.close(response_pipe[1])
        
        # Send the job and agenda package to the child.
        with os.fdopen(request_pipe[1], 'w') as request_fh:
            pickle.dump(job_for_child, request_fh, -1)
            pickle.dump(agenda['package'], request_fh, -1)
        
        # Get the response from the child.
        with os.fdopen(response_pipe[0], 'r') as response_fh:
            try:
                package = pickle.load(response_fh)
            except Exception as e:
                traceback.print_exc()
                sys.stderr.flush()
                package = {
                    'status': 'failed',
                    'exception': e,
                }
        
        # Wait for the child to finish.
        # log('waiting for child...')
        proc.wait()
        
        package.setdefault('status', 'failed')
        agenda['resultpackage'] = utils.pack(package)
        agenda['status'] = package['status']
        
        log('work resultpackage')
        pprint.pprint(package)
        print '# ---'
        
        if agenda['status'] == 'failed':
            log('os.environ')
            for k, v in sorted(os.environ.iteritems()):
                print '#  %s = %r' % (k, v)
            print '# ---'

        log('reporting work as %s' % agenda['status'])
        
        qb.reportwork(agenda)
Example #8
0
def executeJob(control):
    '''
    Execute the transcoding process.

    '''

    jobstate = 'pending'
    qbJob = control.getQubeJobObject()

    if not control.checkForErrors():
        while 1:
            agendaItem = qb.requestwork()
            returnCode = 1

            '''
            First Handle non-running state cases
            '''
            if agendaItem['status'] in ('complete', 'pending', 'blocked', 'waiting'):
                '''
                complete -- no more frames
                pending -- preempted, so bail out
                blocked -- perhaps item is part of a dependency
                '''
                jobstate = agendaItem['status']
                logger.info('Job %s state is now %s' % (qbJob['id'], jobstate))
                break

            '''
            Run the transcoding process that corresponds to the agendaItem name.
                Initialize
                    Setup the blender scene.
                Segment
                    Transcode a segment of frames.
                Final Output
                    Merge that output's segments together with audio.
            '''
            if agendaItem['name'] == 'Initialize':
                logger.info('Initializing...\n')

                if os.path.exists(control.getBlendFile()):
                    logger.info("Initialization script alread exists.")
                    returnCode = 0
                else:
                    returnCode = runCMD(control.getInitCMD(agendaItem))

                if returnCode == 0:
                    logger.info('Initialization Complete! (' + str(returnCode) + ')\n')
                else:
                    logger.error('Initialization Failed! (' + str(returnCode) + ')\n')

            elif agendaItem['name'].startswith('Segment'):
                '''
                Segment Process:
                    > Make sure the blender project was created
                    > Load the segment frame range from the work package.
                    > Check for missing frames in the segments frame range.
                        True
                            > Report an error and end the subjob
                        False
                            > Continue with transcode
                    > Check if smartUpdate is turned on.
                        True
                            > Check if the Modification Times Database and the output file exists.
                                True
                                    > Check if there have been modifications to any frames in this segment.
                                        True
                                            > Continue with transcode
                                        False
                                            > Skip transcoding
                    > Check if we are still transcoding.
                        True
                            > Try to Remove output file if it already exists
                                Success
                                    > Transcode
                                Failure
                                    > Add '_' to the output file name and try again until sucess.
                                        Max retry is 3 times, then fail the subjob.
                    > Check if smartUpdate is turned on.
                        True
                            > Update the modification times database
                        False
                            > Continue
                    > Update the resultPackage with whether changes were made and the segment output file.
                '''

                logger.info("Starting Transcoder Segment Process...")
                render = False
                error = False

                ''' Check for the blender project '''
                if not os.path.exists(control.getBlendFile()):
                    logger.error('Blender Project doesn\'t exist.')
                    error = True
                else:

                    ''' Load the frame range from the work package '''
                    frameRangeString = agendaItem['package']['frameRange']
                    logging.debug("FrameRangeString: %s" % frameRangeString)
                    frameRange = sequenceTools.loadFrameRange(frameRangeString)
                    logging.debug("Loaded frameRange: %s" % frameRange)

                    ''' Check for Missing Frames '''
                    mySequence = control.getSequence()
                    missingFrames = mySequence.getMissingFrames(frameRange)

                    if len(missingFrames) > 0:
                        logger.error('Missing frames!\n' + ','.join(missingFrames))
                        error = True
                    else:
                        logger.info('No Missing Frames')

                if not error:

                    ''' Variables used multiple times '''
                    segmentFilePath = agendaItem.setdefault('package', {}).get('segmentFile', '')
                    segmentFileExists = os.path.exists(segmentFilePath)
                    currentModTimes = {}
                    modTimeDBFile = control.getModTimeDBFile()

                    if control.getSmartUpdate():

                        modTimeDBFileExists = os.path.exists(modTimeDBFile)

                        logger.debug('Segment Output: ' + str(segmentFilePath))
                        logger.debug('Segment Exists: ' + str(segmentFileExists))
                        logger.debug('ModTimeDBFile: ' + str(modTimeDBFile))
                        logger.debug('ModTimeDBFile Exists: ' + str(modTimeDBFileExists))

                        if segmentFileExists and modTimeDBFileExists:
                            logger.info('Smart Updating')

                            logger.debug('Loading current modification times...')
                            currentModTimes = mySequence.getModTimes(frameRange)
                            logger.debug('Current modication times loaded. %s' % currentModTimes)

                            logger.debug('Comparing modification times for frame range %s...' % frameRange)
                            compare = mySequence.compare(modTimeDBFile, frameRange, currentModTimes=currentModTimes)
                            logger.debug('Sequence Differences: %s' % str(compare))

                            differences = ''
                            if len(compare['Added']) > 0:
                                frameNumbers = mySequence.getFramesFromFilenames(compare['Added'])
                                differences += '\n\tAdded: %s' % mySequence.convertListToRanges(frameNumbers)
                            if len(compare['Deleted']) > 0:
                                frameNumbers = mySequence.getFramesFromFilenames(compare['Deleted'])
                                differences += '\n\tDeleted: %s' % mySequence.convertListToRanges(frameNumbers)
                            if len(compare['Modified']) > 0:
                                frameNumbers = mySequence.getFramesFromFilenames(compare['Modified'])
                                differences += '\n\tModified: %s' % mySequence.convertListToRanges(frameNumbers)    
                            
                            if differences:
                                logger.info('Sequence Differences: %s' % differences)
                                render = True

                        else:
                            render = True

                    else:
                        render = True

                    if render:
                        segmentFilePath = control.getValidOutputPath(segmentFilePath)
                        if segmentFilePath:
                            render = True
                        else:
                            error = True

                    if render:
                        logger.info('Transcoding Segment %s' % agendaItem['name'])
                        cmd = control.getSegmentCMD(agendaItem)
                        returnCode = runCMD(cmd)
                        logger.info('Transcoding Segment Complete! (' + str(returnCode) + ')')

                        if control.getSmartUpdate():
                            logger.debug("Saved ModTimes: " + str(currentModTimes))
                            control.job.sequence.saveModTimes(modTimeDBFile,
                                modTimeDict=currentModTimes, frameRange=frameRange)
                            logger.info("Saved Modification Times")

                    else:
                        if not error:
                            logger.info("No changes to segment " + agendaItem['name'])
                            returnCode = 0

                    '''
                    Check if this is the last agenda item that's complete.
                    If so, unblock the final output subjobs.
                    '''
                    agendaItem['resultpackage'] = {'Changed': render, 'segmentFile': segmentFilePath}

                    logger.info("Transcoder Segment Process Complete!\n")

            elif agendaItem['name'].startswith('Output'):
                '''
                Final Output Process
                    > Gather the output paths and changes for all of the segments
                    stored in their resultPackages.
                    > Check if there were changes.
                        True
                            > Continue Transcoding
                        False
                            > Skip Trancoding
                    > Load segmentOutputPaths from the agenda
                    > Check if segmentPaths exist.
                        True
                            > Continue Transcoding
                        False
                            > Skip Transcoding with error
                    > Check if output path is valid
                        True
                            > Continue Transcoding
                        False
                            > Skip Transcoding with error
                    > Check if we are still transcoding.
                        True
                            > Concatenate segments together.
                        False
                            > Skip
                    > Update the resultPackage with the outputPath.
                '''

                logger.info("Starting Final Output Process...\n")

                transcode = False
                errors = False

                dependantNames = agendaItem.get('package', {}).get('segmentSubjobs', '')
                logger.debug('Dependants Names: ' + str(dependantNames))

                dependantSegments = control.getSegments(dependantNames)
                logger.debug('Dependants Segments: ' + str(dependantSegments))

                finalOutputPath = agendaItem['package']['outputFile']
                if finalOutputPath:
                    transcode = True
                else:
                    errors = True

                if transcode:
                    changes = control.checkSegmentsForChanges(dependantSegments)
                    if not changes:
                        logger.info("No changes to segments.")
                    
                        if os.path.exists(str(finalOutputPath)):
                            logger.info('Final output movie already exists. ' + str(finalOutputPath))
                            returnCode = 0
                            transcode = False
                        else:
                            logger.info('Final output movie doesn\'t exist. ' + str(finalOutputPath))
                            transcode = True
                    else:
                        transcode = True

                if transcode:
                    segmentOutputPaths = control.getSegmentOutputPaths(dependantSegments)
                    if segmentOutputPaths:
                        transcode = True
                    else:
                        errors = True

                if transcode:
                    startFrame = control.getOutputStartFrame(dependantSegments)
                    logger.debug('Output Start Frame: ' + startFrame)

                if transcode:                    
                    finalOutputPath = control.getValidOutputPath(finalOutputPath)
                    if finalOutputPath:
                        transcode = True
                    else:
                        errors = True
                    
                if transcode:
                    cmd = control.getFinalOutputCMD(segmentOutputPaths, finalOutputPath, startFrame, 29.97, agendaItem)
                    returnCode = runCMD(cmd)
                    if returnCode != 0:
                        errors = True
                else:
                    logger.info('No changes to Final ' + agendaItem['name'])

                if not errors:
                    agendaItem['resultpackage'] = {'outputPaths': finalOutputPath}
                    logger.info('Transcoder Final ' + agendaItem['name'] + ' Completed Succesfully! (' + str(returnCode) + ')\n')
                else:
                    logger.info('Transcoder Final ' + agendaItem['name'] + ' Failed!' + str(returnCode) + ')\n')


            else:
                logger.error("Invalid Agenda Item")
                logger.error(str(agendaItem))

            '''
            Update the work status based on the return code.
            '''
            if returnCode == 0:
                agendaItem['status'] = 'complete'
            else:
                agendaItem['status'] = 'failed'

            ''' Report back the results to the Supervisor '''
            qb.reportwork(agendaItem)


    if jobstate == 'blocked':
        jobstate = 'pending'

    return jobstate