def projectChangeHandler(self, index):
     """Handler for the event where the project selection changed."""
     
     selectedProject = self.projectComboBox.itemText(index)
     choice = yesNoBox(self, "Change project", "Reassign this node to " + 
                       selectedProject + "? (will avoid jobs from other"
                       " projects)")
     
     if choice != QMessageBox.Yes:
         aboutBox(self, "No changes", "This node will remain assigned to " + 
                  self.thisNode.project + ".")
         self.projectComboBox.setCurrentIndex(self.lastProjectIndex)
         return
     
     # get the most up to date info from the database
     thisNode = None
     try:
         thisNode = getThisNodeData()
     except sqlerror as err:
         logger.debug(str(err))
         self.sqlErrorBox()
         return
     
     thisNode.project = self.projectComboBox.currentText()
     with transaction() as t:
         thisNode.update(t)
     self.lastProjectIndex = self.projectComboBox.currentIndex()
     aboutBox(self, "Success", "Node reassigned to " + selectedProject)
def killJob(job_id):
    """Kills every task associated with job_id. Killed tasks have status code 
    'K'. If a task was already started, an a kill request is sent to the host 
    running it.
    @return: False if no errors while killing started tasks, else True"""
    
    # mark all of the Ready tasks as Killed
    with transaction() as t:
        t.cur.execute("""update Hydra_rendertask set status = 'K' 
                        where job_id = '%d' and status = 'R'""" % job_id)
    
    # get hostnames for tasks that were already started
    tuples = None # @UnusedVariable
    with transaction() as t:
        t.cur.execute("""select host from Hydra_rendertask 
                        where job_id = '%d' and status = 'S'""" % job_id)
        tuples = t.cur.fetchall()
        
    # make flat list out of single-element tuples fetched from db
    hosts = [t for (t,) in tuples]
    
    # send a kill request to each host, note if any failures occurred
    error = False
    for host in hosts:
        try:
            error = error or not sendKillQuestion(host)
        except socketerror:
            logger.debug("There was a problem communicating with {:s}"
                         .format(host))
            error = True
    
    return error
 def online(self):
     """Changes the local render node's status to online if it wasn't on-line already"""
     with transaction():
         [thisNode] = Hydra_rendernode.fetch ("where host = '%s'" % Utils.myHostName( ))
         if thisNode.status == OFFLINE:
             thisNode.status = IDLE
             thisNode.update()
         else:
             logger.debug("Node is already online.")
         self.updateRenderNodeInfo()
 def killCurrentJob(self, statusAfterDeath):
     """Kills the render node's current job if it's running one."""
     
     logger.debug("killing %r", self.childProcess)
     print "Status after death should be: {0:s}".format(statusAfterDeath)
     if self.childProcess:
         self.childProcess.kill()
         self.childKilled = True
         self.statusAfterDeath = statusAfterDeath
     else:
         logger.debug("no process was running.")
 def refreshHandler(self, *args):
     try:
         jobs = Hydra_job.fetch()
         self.jobTable.setRowCount(len(jobs))
         for pos, job in enumerate(jobs):
             ticket = pickle.loads(job.pickledTicket)
             self.jobTable.setItem(pos, 0, QTableWidgetItem_int(str(job.id)))
             self.jobTable.setItem(pos, 1, QTableWidgetItem_int(str(job.priority)))
             self.jobTable.setItem(pos, 2, QTableWidgetItem(ticket.name()))
     except sqlerror as err:
         logger.debug(str(err))
         aboutBox(self, "SQL error", str(err))
 def doFetch( self ):
     """Aggregate method for updating all of the widgets."""
     
     try:
         self.updateThisNodeInfo()
         self.updateRenderNodeTable()
         self.updateRenderTaskGrid()
         self.updateJobTable()
         self.updateStatusBar()
     except sqlerror as err:
         logger.debug(str(err))
         self.sqlErrorBox()
def withOutputToFile( filename, command ):
    "run a command, sending output to a file"

    logger.debug( 'writing log file %s', filename )

    with file( filename, 'w' ) as f:
        f.write( "Launching command %r\n\n" % command )
        f.flush( )
        # the 'with' statement closes f if there's an error
        return subprocess.call( command,
                                stdout = f,
                                stderr = subprocess.STDOUT,
                                )
 def updateStatusBar(self):
     
     with transaction() as t:
         t.cur.execute ("""select count(status), status 
                             from Hydra_rendernode
                             group by status""")
         counts = t.cur.fetchall ()
     logger.debug (counts)
     countString = ", ".join (["%d %s" % (count, niceNames[status])
                               for (count, status) in counts])
     time = dt.now().strftime ("%H:%M")
     msg = "%s as of %s" % (countString, time)
     self.statusLabel.setText (msg)
def getDbInfo():
    # open config file
    config = ConfigParser.RawConfigParser()

    # creat a copy if it doesn't exist
    if not os.path.exists (SETTINGS):
        folder = os.path.dirname (SETTINGS)
        logger.debug ('check for folder %s' % folder)
        if os.path.exists (folder):
            logger.debug ('exists')
        else:
            logger.debug ('make %s' % folder)
            os.mkdir (folder)
        cfgFile = os.path.join (os.path.dirname (sys.argv[0]),
                                os.path.basename (SETTINGS))
        logger.debug ('copy %s' % cfgFile)
        shutil.copyfile (cfgFile, SETTINGS)
        
        
        
    config.read(SETTINGS)

    # get server & db names
    host = config.get(section="database", option="host")
    db = config.get(section="database", option="db")
    
    return host, db
 def killJobButtonHandler(self):
     item = self.jobTable.currentItem()
     if item and item.isSelected():
         row = self.jobTable.currentRow()
         id = int(self.jobTable.item(row, 0).text())  # @ReservedAssignment
         choice = yesNoBox(self, "Confirm", "Really kill job {:d}?".format(id))
         if choice == QMessageBox.Yes:
             try:
                 if killJob(id):
                     aboutBox(self, "Error", "Some nodes couldn't kill " + "their tasks.")
             except sqlerror as err:
                 logger.debug(str(err))
                 aboutBox(self, "SQL Error", str(err))
             finally:
                 self.jobCellClickedHandler(item.row(), 0)
    def handle( self ):

        logger.info ("request")

        try:        
            questionBytes = self.rfile.read( )
            question = pickle.loads( questionBytes )
            logger.debug(question)
            
            answer = question.computeAnswer( self.TCPserver )

            answerBytes = pickle.dumps( answer )
            self.wfile.write( answerBytes )
        except:
            logger.error( """Exception caught:
%s""", traceback.format_exc( ) )
 def getOff(self):
     """
     Offlines the node and sends a message to the render node server running on localhost to
     kill its current task
     """
     self.offline()
     try:
         self.connection = TCPConnection()
         killed = self.getAnswer(KillCurrentJobQuestion(statusAfterDeath=READY))
         if not killed:
             logger.debug("There was a problem killing the task.")
             QMessageBox.about(self, "Error", "There was a problem killing the task.")
     except socketerror:
         QMessageBox.about(self, "Error", "The render node software is not running or has become unresponsive.")
         
     self.updateRenderNodeInfo()
 def insert (self, transaction):
         
     names = self.attributes ()
     values = [getattr (self, name)
               for name in names]
     nameString = ", ".join (names)
     valueString = ", ".join (len(names) * ["%s"])
     query = "insert into %s (%s) values (%s)" % (self.tableName (),
                                                  nameString,
                                                  valueString)
     logger.debug (query)
     transaction.cur.executemany (query, [values])
     if self.autoColumn:
         transaction.cur.execute ("select last_insert_id()")
         [id] = transaction.cur.fetchone ()
         self.__dict__[self.autoColumn] = id
    def getMayaProjectPath(self, scenePath):
        """
Walks up the file tree looking for workspace.mel,
returns the path if found"""
        
        
        # remove Maya scene file name from the end of the path
        mayaProjectPath = os.path.dirname (scenePath)
        lastPath = None
        wrkspc = "workspace.mel"
        while not os.path.exists(os.path.join(mayaProjectPath, wrkspc)):
            logger.debug ("%s not in %s", wrkspc, mayaProjectPath)
            lastPath = mayaProjectPath
            mayaProjectPath = os.path.dirname (mayaProjectPath)
            if lastPath == mayaProjectPath:
                return ""
        return mayaProjectPath
 def onlineThisNodeButtonClicked(self):
     """Changes the local render node's status to online if it was offline,
     goes back to started if it was pending offline."""
     
     # get most current info from the database
     thisNode = None
     try:
         thisNode = getThisNodeData()
     except sqlerror as err:
         logger.debug(str(err))
         self.sqlErrorBox()
         return
     
     if thisNode:
         onlineNode(thisNode)
     
     self.doFetch()
 def offlineThisNodeButtonClicked(self):
     """Changes the local render node's status to offline if it was idle,
     pending if it was working on something."""
     
     # get the most current info from the database
     thisNode = None
     try:
         thisNode = getThisNodeData()
     except sqlerror as err:
         logger.debug(str(err))
         self.sqlErrorBox()
         return
     
     if thisNode:
         offlineNode(thisNode)
         
     self.doFetch()
 def update (self, transaction):
     
     names = list (self.__dirty__)
     if not names:
         return
     
     values = ([getattr (self, name)
               for name in names]
                  +
               [getattr (self, self.primaryKey)])
     assignments = ", ".join(["%s = %%s" % name
                              for name in names])
     query = "update %s set %s where %s = %%s" % (self.tableName (),
                                                   assignments,
                                                   self.primaryKey)
     logger.debug ((query, values))
     transaction.cur.executemany (query, [values])
    def testRenderCommand( self ):
        """Tests a Render Command, a command that invokes the Maya renderer."""
        command = [
                r'c:\program files\autodesk\maya2011\bin\render.exe',
                '-mr:v', '5',
                r'\\flex2\ProjectHydra\TestMayaFiles\Chair2.ma'
                  ]
        render_task = Hydra_rendertask()
        render_task.status = 'R'
        render_task.command = repr( command )
        render_task.insert()

        logger.debug(render_task)

        render_question = Questions.RenderQuestion( render_task.id )
        render_answer = self.getAnswer( render_question )
        logger.debug( render_answer )        
 def createTasks( self, job ):
     starts = range( self.startFrame, self.endFrame + 1, self.batchSize )
     ends = [min( start + self.batchSize - 1,
                  self.endFrame )
             for start in starts
             ]
     for start, end in zip( starts, ends ):
         command = self.renderCommand(start, end)
         logger.debug( command )
         task = Hydra_rendertask( status = READY, 
                                  command = repr( command ),
                                  job_id = job.id, 
                                  priority = self.priority, 
                                  project = self.project,
                                  createTime = job.createTime,
                                  requirements = job.requirements,
                                  )
         with transaction() as t:
             task.insert(transaction=t)
    def fetch (cls, whereClause = "", order = None, limit = None,
               explicitTransaction=None):
        orderClause = "" if order is None else " " + order + " "
        limitClause = "" if limit is None else " limit %d " % limit
        select = "select * from %s %s %s %s" % (cls.tableName (),
                                             whereClause,
                                                orderClause,
                                             limitClause)
        logger.debug (select)

        def doFetch (t):
            t.cur.execute (select)
            names = [desc[0] for desc in t.cur.description]
            return [cls (**dict (zip (names, tuple)))
                    for tuple in t.cur.fetchall ()]
        if explicitTransaction:
            return doFetch (explicitTransaction)
        else:
            with transaction() as t:
                return doFetch (t)
 def getOffRenderNodesButtonClicked(self):
     """For all nodes with boxes checked in the render nodes table, changes
     status to offline if idle, or pending if started, and attempts to kill
     any task that is running on each node."""
     
     hosts = getCheckedItems(table=self.renderNodeTable, itemColumn=1,
                             checkBoxColumn=0)
     if len(hosts) == 0:
         self.noneCheckedBox()
         return
     
     choice = yesNoBox(self, "Confirm", "<B>WARNING</B>: All progress on"
                       " current tasks will be lost for the selected"
                       " render nodes. Are you sure you want to stop these"
                       " nodes? <br>" + str(hosts))
     
     if choice != QMessageBox.Yes:
         aboutBox(self, "Aborted", "No action taken.")
         return
     
     error = False
     notKilledList = list()
     with transaction() as t:
         rendernode_rows = Hydra_rendernode.fetch(explicitTransaction=t)
         for node_row in rendernode_rows:
             if node_row.host in hosts:
                 offlineNode(node_row)
                 try:
                     killed = sendKillQuestion(node_row.host, READY)
                     error = error or not killed
                 except socketerror as err:
                     logger.debug(str(err) + '\n' 
                                  + "Error while trying to contact " 
                                  + node_row.host)
                     notKilledList.append(node_row.host)
                     error = True
     if error:
         aboutBox(self, "Error", "The following nodes could not be stopped"
                  " for some reason. Look in FarmView.log for more details."
                  "<br>" + str(notKilledList))
     self.doFetch()
 def killTaskButtonHandler(self):
     item = self.taskTable.currentItem()
     if item and item.isSelected():
         row = self.taskTable.currentRow()
         id = int(self.taskTable.item(row, 0).text())  # @ReservedAssignment
         choice = yesNoBox(self, "Confirm", "Really kill task {:d}?".format(id))
         if choice == QMessageBox.Yes:
             try:
                 killTask(id)
             except socketerror as err:
                 logger.debug(str(err))
                 aboutBox(
                     self,
                     "Error",
                     "Task couldn't be killed because "
                     "there was a problem communicating with the host running "
                     "it.",
                 )
             except sqlerror as err:
                 logger.debug(str(err))
                 aboutBox(self, "SQL Error", str(err))
 def resurrectTaskButtonHandler(self):
     taskItem = self.taskTable.currentItem()
     if taskItem and taskItem.isSelected():
         row = self.taskTable.currentRow()
         id = int(self.taskTable.item(row, 0).text())  # @ReservedAssignment
         choice = yesNoBox(self, "Confirm", "Resurrect task {:d}?".format(id))
         if choice == QMessageBox.Yes:
             error = None
             try:
                 error = resurrectTask(id)
             except sqlerror as err:
                 logger.debug(str(err))
                 aboutBox(self, "SQL Error", str(err))
             finally:
                 if error:
                     msg = "Task couldn't be resurrected because it's " "either not dead or is currently running."
                     logger.debug(msg)
                     aboutBox(self, "Error", msg)
                 else:
                     jobItem = self.jobTable.currentItem()
                     self.jobCellClickedHandler(jobItem.row(), 0)
 def killTaskButtonHandler (self):
     item = self.taskTable.currentItem ()
     if item and item.isSelected ():
         row = self.taskTable.currentRow ()
         task_id = int(self.taskTable.item(row, 0).text())
         choice = yesNoBox(self, "Confirm", "Really kill task {:d}?"
                           .format(task_id))
         if choice == QMessageBox.Yes:
             killed = None
             try:
                 killed = killTask(task_id)
                 if not killed:
                     # TODO: make a better error message
                     aboutBox(self, "Error", "Task couldn't be killed for "
                              "some reason.")
             except socketerror as err:
                 logger.debug(str(err))
                 aboutBox(self, "Error", "Task couldn't be killed because "
                 "there was a problem communicating with the host running "
                 "it.")
             except sqlerror as err:
                 logger.debug(str(err))
                 aboutBox(self, "SQL Error", str(err))
 def __exit__ (self, errorType, value, traceback):
     if errorType is None:
         logger.debug ("commit %s", self)
         self.cur.execute ("commit")
     else:
         logger.debug ("rollback %s", self)
         self.cur.execute ("rollback")
     logger.debug ("exit transaction %s", self)
     self.db.close()
 def updateThisNodeInfo(self):
     """Updates widgets on the "This Node" tab with the most recent 
     information available."""
     
     # if the buttons are disabled, don't bother
     if not self.thisNodeButtonsEnabled:
         return
     
     # get the most current info from the database
     thisNode = None
     try:
         thisNode = getThisNodeData()
         self.updateProjectComboBox()
     except sqlerror as err:
         logger.debug(str(err))
         self.sqlErrorBox()
     
     if thisNode:
         # update the labels
         self.nodeNameLabel.setText(thisNode.host)
         self.nodeStatusLabel.setText(niceNames[thisNode.status])
         self.updateTaskIDLabel(thisNode.task_id)
         self.nodeVersionLabel.setText(
                     getSoftwareVersionText(thisNode.software_version))
         self.setCurrentProjectSelection(thisNode.project)
         self.updateRestrictToProjectLabel(thisNode.restrict_to_project)
         self.updateMinPriorityLabel(thisNode.minPriority)
         self.updateCapabilitiesLabel(thisNode.capabilities)
         
     else:
         QMessageBox.about(self, "Notice", 
             "Information about this node cannot be displayed because it is"
             "not registered on the render farm. You may continue to use"
             " Farm View, but it must be restarted after this node is "
             "registered if you wish to see this node's information.")
         self.setThisNodeButtonsEnabled(False)
def sendKillQuestion(renderhost, newStatus=KILLED):
    """Tries to kill the current task running on the renderhost. Returns True
    if successful, otherwise False"""

    logger.debug ('kill job on %s' % renderhost)
    connection = TCPConnection(hostname=renderhost)
    answer = connection.getAnswer(
                            KillCurrentJobQuestion(newStatus))

    logger.debug("child killed: %s" % answer.childKilled)
    
    if not answer.childKilled:
        logger.debug("%r tried to kill its job but failed for some reason." 
                        % renderhost)
    
    return answer.childKilled
    def doSubmit( self ):
        """Submits a job ticket for this scene to be split into
        tasks and processed."""

        logger.debug ('doSubmit')

        reqs = sorted ([str (req.text ())
                        for req in self.requirementsListWidget.selectedItems ()])
        reqs_pattern = '%' + '%'.join (reqs) + '%' if reqs else '%'
        print reqs_pattern
        
        sceneFile = str( self.sceneText.text() ).replace ('\\', '/')
        startFrame = self.startSpinBox.value( )
        endFrame = self.endSpinBox.value( )
        numJobs = self.numJobsSpinBox.value( )
        batchSize = int(math.ceil((endFrame - startFrame + 1.0) / numJobs))
        logger.debug ("numJobs %s batchSize %s", numJobs, batchSize)
        priority = self.prioritySpinBox.value( )
        project = str(self.projectComboBox.currentText())
        executable = str(self.executableComboBox.currentText())
        
        mayaProjectPath = str(self.projectDirLineEdit.text())
        if not os.path.exists(os.path.join (mayaProjectPath, "workspace.mel")):
            # try to find workspace.mel
            mayaProjectPath = self.getMayaProjectPath(sceneFile)
            if not mayaProjectPath:
                logger.debug("workspace.mel not found")
                aboutBox(self, "Error", """
The project path cannot be set because workspace.mel could not
be located. Please set the project path manually.""")
                return
            if yesNoBox(self, "Confirm",
                        "Maya project path set to:<br>" +
                        mayaProjectPath +
                        "<br> Is this correct?") == QMessageBox.No:
                aboutBox(self, "Abort",
"Submission aborted. Please set the Maya project path manually.")
                return
        self.projectDirLineEdit.setText(mayaProjectPath)

        # executable names a class in the JobTicket module
        ticketClass = getattr (JobTicket, executable)
        ticketClass(sceneFile, mayaProjectPath, startFrame, endFrame, batchSize,
                    priority, project, reqs_pattern).submit()

        aboutBox(self, "Success",
"Job submitted. Please close the submitter window.")
 def getOffThisNodeButtonClicked(self):
     """Offlines the node and sends a message to the render node server 
     running on localhost to kill its current task (task will be 
     resubmitted)"""
     
     thisNode = None
     try:
         thisNode = getThisNodeData()
     except sqlerror as err:
         logger.debug(str(err))
         self.sqlErrorBox()
         return
     
     choice = yesNoBox(self, "Confirm", "All progress on the current job"
                       " will be lost. Are you sure you want to stop it?")
     if choice != QMessageBox.Yes:
         aboutBox(self, "Abort", "No action taken.")
         return
     
     if thisNode:
         offlineNode(thisNode)
             
         if thisNode.task_id:
             try:
                 # TODO: use JobKill for getOff instead of doing it manually
                 killed = sendKillQuestion(renderhost = "localhost", 
                                           newStatus = READY)
                 if not killed:
                     logger.debug("There was a problem killing the task.")
                     aboutBox(self, "Error", "There was a problem killing"
                              " the task.")
                 else:
                     aboutBox(self, "Success", "Job was successfully"
                              " stopped. Node offlined.")
             except socketerror:
                 logger.debug(socketerror.message)
                 aboutBox(self, "Error", "There was a problem communicating"
                          " with the render node software. Either it's not"
                          " running, or it has become unresponsive.")
         else:
             aboutBox(self, "Offline", "No job was running. Node offlined.")
             
     self.doFetch()
            if not startDir:
                startDir = os.getcwd()
        else:
            startDir = currentDir
            
        mayaProjectPath = QFileDialog.getOpenFileName(
            parent=self,
            caption="Find workspace.mel",
            directory=startDir,
            filter="workspace.mel")
        if mayaProjectPath:
            # remove "workspace.mel" from the end of the path
            mayaProjectPath = str(mayaProjectPath).split('/')
            mayaProjectPath.pop()
            mayaProjectPath = '/'.join(mayaProjectPath) + '/'
            self.projectDirLineEdit.setText(mayaProjectPath)
        
if __name__ == '__main__':
    try:
        logger.debug(sys.argv) # prints out argv
        app = QApplication( sys.argv ) 

        window = SubmitterWindow( )

        window.show( )
        retcode = app.exec_( )
        sys.exit( retcode )
    except Exception, e:
        logger.error( traceback.format_exc( e ) )
        raise