diff --git a/docs/software/houdini.rst b/docs/software/houdini.rst index d8fd717a1..7e2eccea9 100644 --- a/docs/software/houdini.rst +++ b/docs/software/houdini.rst @@ -273,6 +273,18 @@ This can be used to explain other ROP network what to do with Afanasy node. Afanasy TOP =========== +.. figure:: images/houdini_pdg_scheduler.png + + Afanasy TOP Shecduler tab + +.. figure:: images/houdini_pdg_parameters.png + + Afanasy TOP Tasks Parameters tab + +.. figure:: images/houdini_pdg_adjustment.png + + Afanasy TOP Adjustment tab + Examples ======== diff --git a/docs/software/images/houdini_pdg_adjustment.png b/docs/software/images/houdini_pdg_adjustment.png new file mode 100644 index 000000000..baa5dddf1 Binary files /dev/null and b/docs/software/images/houdini_pdg_adjustment.png differ diff --git a/docs/software/images/houdini_pdg_parameters.png b/docs/software/images/houdini_pdg_parameters.png new file mode 100644 index 000000000..4403af8d9 Binary files /dev/null and b/docs/software/images/houdini_pdg_parameters.png differ diff --git a/docs/software/images/houdini_pdg_scheduler.png b/docs/software/images/houdini_pdg_scheduler.png new file mode 100644 index 000000000..dcb688cc0 Binary files /dev/null and b/docs/software/images/houdini_pdg_scheduler.png differ diff --git a/plugins/houdini/otls/afanasyscheduler.hda b/plugins/houdini/otls/afanasyscheduler.hda index 912fd84a1..bbfddbd79 100644 Binary files a/plugins/houdini/otls/afanasyscheduler.hda and b/plugins/houdini/otls/afanasyscheduler.hda differ diff --git a/plugins/houdini/pdg/types/afanasyscheduler.py b/plugins/houdini/pdg/types/afanasyscheduler.py index cb8141297..99cecfac6 100644 --- a/plugins/houdini/pdg/types/afanasyscheduler.py +++ b/plugins/houdini/pdg/types/afanasyscheduler.py @@ -18,6 +18,28 @@ import af +def displayError(msg, exception=None): + """Pop up a message dialog to display the given error message. + If the ui is unavailable, then it writes the message to the console. + """ + if hou.isUIAvailable(): + details = (str(exception) if exception is not None else None) + hou.ui.displayMessage(msg, severity=hou.severityType.Error, details=details) + else: + if exception is not None: + msg += "\n" + str(exception) + raise hou.OperationFailed(msg) + +def displayMessage(msg): + """Pop up a message dialog to display the given message. + If the ui is unavailable, then it writes the message to the console. + """ + if hou.isUIAvailable(): + hou.ui.displayMessage(msg, severity=hou.severityType.Message) + else: + print(msg) + + class AfanasyScheduler(CallbackServerMixin, PyScheduler): """ Scheduler implementation that interfaces with a Afanasy farm instance. @@ -53,13 +75,34 @@ def _log(self, i_msg): print(self.topNode().name() + ': ' + str(i_msg)) - def _getWorkItemServiceAndParser(self, work_item): + def _getTicketsDictFromString(self, i_str): + tickets = dict() + + if i_str is None or len(i_str) == 0: + return tickets + + for ticket in i_str.split(','): + ticket = ticket.strip().split(':') + if len(ticket) != 2: + displayMessage('Invalid ticket data: "%s".' % ticket) + tickets[ticket[0]] = int(ticket[1]) + + return tickets + + + def _getWorkItemServiceParserTickets(self, i_work_item): # Set the default service values service = 'hbatch' # PDG uses "ALF_PROGRESS" everywhere parser = 'mantra' - - topnode = work_item.node.topNode() + # By default there are no tickets at all + tickets = dict() + tickets_auto = self['afanasy_tickets_auto'].evaluateInt() + if tickets_auto: + # If auto tickets, almost all nodes launches hython + tickets['HYTHON'] = 1 + + topnode = i_work_item.node.topNode() toptype = topnode.type().name() if toptype == 'ropfetch': # Try to detect ROP type @@ -70,21 +113,28 @@ def _getWorkItemServiceAndParser(self, work_item): roptype = ropnode.type().name() if roptype == 'ifd': service = 'hbatch_mantra' + if tickets_auto: + tickets['MANTRA'] = 1 elif toptype == 'ffmpegencodevideo': service = 'ffmpeg' parser = 'ffmpeg' + tickets.pop('HYTHON', None) # Service can be specified directly: - value = self.evaluateStringOverride(work_item.node, self.parmprefix, 'service', work_item, '') + value = self.evaluateStringOverride(i_work_item.node, self.parmprefix, 'service', i_work_item, '') if value is not None and len(value): service = value # Parser can be specified directly: - value = self.evaluateStringOverride(work_item.node, self.parmprefix, 'parser', work_item, '') + value = self.evaluateStringOverride(i_work_item.node, self.parmprefix, 'parser', i_work_item, '') if value is not None and len(value): parser = value - return service, parser + # Add tickets that are specified directly: + value = self.evaluateStringOverride(i_work_item.node, self.parmprefix, 'tickets', i_work_item, '') + tickets.update(self._getTicketsDictFromString(value)) + + return service, parser, tickets def _constructJob(self): @@ -100,9 +150,11 @@ def _constructJob(self): def _constructBlock(self, work_item): - service, parser = self._getWorkItemServiceAndParser(work_item) + service, parser, tickets = self._getWorkItemServiceParserTickets(work_item) block = af.Block(work_item.node.name, service) block.setParser(parser) + for name in tickets: + block.addTicket(name, tickets[name]) block.setCapacity(self.evaluateIntOverride(work_item.node, self.parmprefix, 'capacity', work_item, -1)) block.setHostsMask(self.evaluateStringOverride(work_item.node, self.parmprefix, 'hosts_mask', work_item, '')) block.setHostsMaskExclude(self.evaluateStringOverride(work_item.node, self.parmprefix, 'hosts_mask_exclude', work_item, '')) @@ -111,6 +163,9 @@ def _constructBlock(self, work_item): block.setNeedMemory(self.evaluateIntOverride(work_item.node, self.parmprefix, 'need_memory', work_item, -1)*1024) block.setTaskMinRunTime(self.evaluateIntOverride(work_item.node, self.parmprefix, 'task_min_run_time', work_item, -1)) block.setTaskMaxRunTime(int(self.evaluateFloatOverride(work_item.node, self.parmprefix, 'task_max_run_time', work_item, -1)*3600.0)) + env_dict, removekeys = self.resolveEnvParams(self.parmprefix, work_item, False) + for name in env_dict: + block.setEnv(name, env_dict[name]) return block diff --git a/plugins/houdini/topscheduler.user.ds b/plugins/houdini/topscheduler.user.ds index 71129d1c6..8910c6017 100644 --- a/plugins/houdini/topscheduler.user.ds +++ b/plugins/houdini/topscheduler.user.ds @@ -113,3 +113,42 @@ help "Tasks output parser." } + + parm { + name "afanasy_tickets" + label "Tickets" + type string + default { "" } + parmtag { "pdg::scheduler" "" } + parmtag { spare_category "Afanasy" } + help "Tasks block tickets. A comma separated list of key:count. Example: MEM:64,GPU:1" + } + + +multiparm { + name "afanasy_envmulti" + label "Extra Environment" + parmtag { "pdg::nocopy" "" } + parmtag { "pdg::scheduler" "" } + parmtag { spare_category "Afanasy" } + + parm { + name "afanasy_envname#" + label "Name" + type string + joinnext + default { "" } + parmtag { "pdg::scheduler" "" } + parmtag { spare_category "Afanasy" } + help "Name of the work item environment variable." + } + parm { + name "afanasy_envvalue#" + label "Value" + type string + default { "" } + parmtag { "pdg::scheduler" "" } + parmtag { spare_category "Afanasy" } + help "Value of the work item environment variable." + } +}