Skip to content
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
f24398f
Draft implementation for a "launch" script using the env_prototype
BigRoy Mar 13, 2018
7620fd3
Created fusion dir for scripts, moves switch_and_submit in dir
aardschok Mar 16, 2018
d33e471
Added assertion, changed signature order
aardschok Mar 16, 2018
2533166
initial draft of logic for submit_switch_job
aardschok Mar 16, 2018
7d6754a
Added slapcomp support
aardschok Mar 19, 2018
e683347
changed to context plugin, fixed missing keys for job
aardschok Mar 19, 2018
b240c41
Added support for slap comp
aardschok Mar 19, 2018
77dab46
Removed unused import
aardschok Mar 21, 2018
f12152b
Improved switch_and_submit, always abspath, if no path fetch from comp
aardschok Mar 21, 2018
cb30677
Removed constant, fixed format_version_folder, renamed function
aardschok Mar 21, 2018
55d58ab
Merged publish_local logic in switch, use api.publish for publish
aardschok Mar 21, 2018
e53c2d4
Removed argparse from module
aardschok Mar 21, 2018
1208f8d
Cosmetics
aardschok Mar 22, 2018
5dbf143
Job dependend on last submitted, changed get_script function, update env
aardschok Mar 22, 2018
8de8984
Refactored __main__
aardschok Mar 22, 2018
1e0d4b7
Improved format_version_folder logic, explicit error print in publishing
aardschok Mar 22, 2018
e7bf90e
Removed debug print, removed astrixes from update()
aardschok Mar 22, 2018
abd6ff9
Extended docstrings
aardschok Mar 22, 2018
7c5c295
Implemented get_script_dir method
aardschok Mar 22, 2018
7af871d
refactored context[:] to list(context) due to slice issue in Py36
aardschok Mar 22, 2018
4c9a6f5
Added module for deadline switching of comps
aardschok Mar 22, 2018
025ac39
Fixed typo, fetch of machine limit from depending job
aardschok Mar 22, 2018
8aa9799
new line at end of file
aardschok Mar 22, 2018
442eea5
Refactored approach of getting machine limit
aardschok Mar 22, 2018
8d0b0c4
Created backwards compatibility, added message if key is missing
aardschok Mar 22, 2018
f890253
Improved the get_machine_limit function
aardschok Mar 22, 2018
18afd8a
Updated docstrings
aardschok Mar 22, 2018
ac60222
Fixed error
aardschok Mar 22, 2018
87bb43f
Refactored function names, setting current comp to None when closing
aardschok Mar 23, 2018
86f4c9c
Removed __init__ modules from script folder
aardschok Mar 23, 2018
71f2293
Update environment to ensure session and environment are in sync
aardschok Mar 23, 2018
2f1b08a
Updated docstring, fixed type, updated print
aardschok Mar 26, 2018
b6a1e69
Added more explicit comment and docstrings
aardschok Mar 26, 2018
28a779b
Make switch logic more central
aardschok Mar 26, 2018
b0e8d44
Updated _on_switch, and cosmetics
aardschok Mar 26, 2018
75859b1
Moved switch logic to lib, module redundant
aardschok Mar 26, 2018
09911b2
Updated switch logic, removed rendudant function, added submit function
aardschok Mar 26, 2018
0f8f03d
renamed variable
aardschok Mar 26, 2018
d5eacb2
improved error handling, added create_new_filepath
aardschok Mar 26, 2018
6d9c056
Removed the self variable, remvoed create_new_filepath
aardschok Mar 26, 2018
a587b42
Fixed import error, added file filter in brows
aardschok Mar 26, 2018
887ec7a
Removed unused argument
aardschok Mar 26, 2018
a51c350
Raise error if file is invalid
aardschok Mar 26, 2018
155235e
Fixed get_script, retrieve suspendPublishJob correct
aardschok Mar 27, 2018
5b2708f
Set initialStatus active as it has a dependency, minor cosmetics
aardschok Mar 27, 2018
5a8d93f
Added docstrings, removed slapcomp from new filename
aardschok Mar 28, 2018
290fa33
refactored out "slapcomp", fixed typo
aardschok Mar 28, 2018
f36d32c
Merge pull request #2 from BigRoy/app_env
aardschok Mar 29, 2018
ab33232
Added fusion 9 and fusion console node env jsons
aardschok Mar 29, 2018
e22f951
update environment through avalon.api
aardschok Mar 29, 2018
0201333
Removed debug prints
aardschok Mar 29, 2018
0408539
Added comment to job
aardschok Mar 29, 2018
ff8f3f7
Moved api.update_current_task to script
aardschok Mar 30, 2018
1a8c8d9
Script to override GlobalJobPreLoad of Deadline
aardschok Apr 4, 2018
0125fc0
Removed environment folder and jsons
aardschok Apr 4, 2018
ec4c164
Added AVALON_TOOLS to launch in correct environment
aardschok Apr 6, 2018
1044cbc
Added function to collect maya version and yeti if loaded, added AVAL…
aardschok Apr 6, 2018
3697a1f
Removed fixed paths, start process through env_prototype, fix error i…
aardschok Apr 6, 2018
1a5cf73
Added AVALON_TOOLS
aardschok Apr 6, 2018
1d88db8
Added AVALON_TOOLS
aardschok Apr 6, 2018
0dc9454
Merge branch 'FUS-32' of https://github.com/aardschok/colorbleed-conf…
aardschok Apr 6, 2018
75c08be
removed workaround
aardschok Apr 6, 2018
05af7de
Merge branch 'FUS-32' of https://github.com/aardschok/colorbleed-conf…
aardschok Apr 6, 2018
e49306c
Moved \n ot end of string
aardschok Apr 6, 2018
0a5ad41
Launch action through env_prototype
aardschok Apr 6, 2018
fb2f1ce
env prototype set in globals
aardschok Apr 6, 2018
d7ec1db
env prototype set in globals
aardschok Apr 6, 2018
302cbfe
fixed issue with incomplete environment
aardschok Apr 9, 2018
38eeea8
added assertion, get tools from environment
aardschok Apr 9, 2018
6176cf9
Created more elegant way to retriev script file
aardschok Apr 9, 2018
67a3fe7
added assert for AVALON_TOOLS, removed redundant function
aardschok Apr 9, 2018
44e58c2
launch now fully relies on env_prototype
aardschok Apr 9, 2018
a6b55d4
Refactored to match signature of env_prototype.which
aardschok Apr 9, 2018
fda2aaa
renamved env_prototype to acre
aardschok Apr 10, 2018
9e9edb0
renamved env_prototype to acre
aardschok Apr 10, 2018
9d21bef
renamved env_prototype to acre
aardschok Apr 10, 2018
e7f6c56
Fixed conflicts in branch
aardschok Apr 10, 2018
ea52a9a
Refactored with env_prototype to acre
aardschok Apr 10, 2018
b4e745b
Refactored back acre, conflict was solved wrong
aardschok Apr 11, 2018
9cc99ef
Added pretty error message
aardschok Apr 11, 2018
bceaa25
Explicitlty set tool combination and TOOL_ENV
aardschok Apr 13, 2018
c4d703e
Replace env logic with acre, fixed type, print new comp name
aardschok Apr 13, 2018
3a1b1f6
Using print instead of log, print server found and fusion found
aardschok Apr 13, 2018
6911d56
Improved traceback for deadline
aardschok Apr 13, 2018
c0c3011
Delete launch files
aardschok Apr 13, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions colorbleed/plugins/fusion/publish/collect_comp.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import os

import pyblish.api

from avalon import fusion
Expand Down
2 changes: 1 addition & 1 deletion colorbleed/plugins/fusion/publish/submit_deadline.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def process(self, instance):

# Collect all saver instances in context that are to be rendered
saver_instances = []
for instance in context[:]:
for instance in list(context):
if not self.families[0] in instance.data.get("families"):
# Allow only saver family instances
continue
Expand Down
5 changes: 2 additions & 3 deletions colorbleed/plugins/fusion/publish/validate_unique_subsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ class ValidateUniqueSubsets(pyblish.api.InstancePlugin):

@classmethod
def get_invalid(cls, instance):

context = instance.context
subset = instance.data["subset"]
for other_instance in context[:]:
for other_instance in list(context):
if other_instance == instance:
continue

Expand All @@ -26,4 +25,4 @@ def get_invalid(cls, instance):
def process(self, instance):
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Animation content is invalid. See log.")
raise RuntimeError("Content is invalid. See log.")
2 changes: 2 additions & 0 deletions colorbleed/plugins/maya/create/colorbleed_renderglobals.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ def __init__(self, *args, **kwargs):
data["priority"] = 50
data["whitelist"] = False
data["machineList"] = ""
data["runSlapComp"] = False
data["flowFile"] = ""

self.data = data
self.options = {"useSelection": False} # Force no content
Expand Down
17 changes: 13 additions & 4 deletions colorbleed/plugins/maya/publish/collect_renderlayers.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,13 @@ def parse_options(self, render_globals):

attributes = maya.read(render_globals)

options = {"renderGlobals": {}}
options["renderGlobals"]["Priority"] = attributes["priority"]
legacy = attributes["useLegacyRenderLayers"]
options["renderGlobals"]["UseLegacyRenderLayers"] = legacy
options = {
"renderGlobals":
{
"Priority": attributes["priority"],
"UseLegacyRenderLayers": attributes["useLegacyRenderLayers"]
}
}

# Machine list
machine_list = attributes["machineList"]
Expand All @@ -135,4 +138,10 @@ def parse_options(self, render_globals):
state = "Suspended" if attributes["suspendPublishJob"] else "Active"
options["suspendPublishJob"] = state

# Check if the run slap comp
if attributes["runSlapComp"]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will break backwards compatibility for render global nodes that don't have the slap comp attribute yet, right?

self.log.info("Running render through slap comp as post ..")
options["runSlapComp"] = True
options["flowFile"] = attributes["flowFile"]

return options
108 changes: 108 additions & 0 deletions colorbleed/plugins/maya/publish/submit_switch_job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import os
import pprint

from avalon import api
from avalon.vendor import requests

import pyblish.api


def _get_script_dir():
"""Get path to the image sequence script"""
try:
import colorbleed
scriptdir = os.path.dirname(colorbleed.__file__)
fusion_scripts = os.path.join(scriptdir,
"scripts",
"fusion")
except:
raise RuntimeError("This is a bug")

assert os.path.isdir(fusion_scripts), "Config is incomplete"
fusion_scripts = fusion_scripts.replace(os.sep, "/")

return fusion_scripts


class SubmitDependentSwitchJobDeadline(pyblish.api.ContextPlugin):
"""Run Switch Shot on specified comp as depending job

"""

label = "Submit Switch Jobs to Deadline"
order = pyblish.api.IntegratorOrder + 0.2
hosts = ["maya"]
families = ["colorbleed.renderlayer"]

def process(self, context):

# Run it as depend on the last submitted instance
instance = context[-1]

AVALON_DEADLINE = api.Session.get("AVALON_DEADLINE",
"http://localhost:8082")
assert AVALON_DEADLINE, "Requires AVALON_DEADLINE"

job = instance.data.get("deadlineDependJob", None)
if not job:
self.log.warning("No dependent Job found")
return True

filepath = instance.data("flowFile", "")
if not filepath:
raise RuntimeError("No flow file (comp) chosen")

shot = api.Session["AVALON_ASSET"]
comment = instance.context.data["comment"]

scriptdir = _get_script_dir()
scriptfile = os.path.join(scriptdir, "deadline_swith_and_submit.py")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you test this? :) Probably won't work because of a typo. The filename is: deadline_switch_and_submit.py

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Saw an issues with retrieving the listedslaves, will fix this before merging

I saw this as well when attempting to fix the above mentioned issue


args = '--file_path "{}" --asset_name "{}" --render 1'.format(
filepath, shot)
payload_name = "{} SWITCH".format(os.path.basename(filepath))

payload = {
"JobInfo": {
"Plugin": "Python",
"BatchName": job["Props"]["Batch"],
"Name": payload_name,
"JobType": "Normal",
"JobDependency0": job["_id"],
"UserName": job["Props"]["User"],
"Comment": comment,
"InitialStatus": "Suspended"},
"PluginInfo": {
"Version": "3.6",
"ScriptFile": scriptfile,
"Arguments": args,
"SingleFrameOnly": "True"
},
"AuxFiles": []
}

# Update payload with machine limits
list_type = self._get_list_type(job)
payload["JobInfo"][list_type] = job["Props"]["ListedSlaves"]

environment = job["Props"].get("Env", {})
payload["JobInfo"].update({
"EnvironmentKeyValue%d" % index: "{key}={value}".format(
key=key,
value=environment[key]
) for index, key in enumerate(environment)
})

url = "{}/api/jobs".format(AVALON_DEADLINE)
response = requests.post(url, json=payload)
if not response.ok:
raise Exception(response.text)

# Temporary key name, deadlineSubmissionJob was already taken
if instance.data("runSlapComp", False):
instance.data["deadlineDependJob"] = response.json()

self.log.info("Slap comp arguments: %s" % args)

def _get_list_type(self, job):
return "Whitelist" if job["Props"]["White"]is True else "Blacklist"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space in job["Props"]["White"]is - it's interesting that this is actually valid python code.

4 changes: 4 additions & 0 deletions colorbleed/plugins/publish/submit_publish_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,7 @@ def process(self, instance):
response = requests.post(url, json=payload)
if not response.ok:
raise Exception(response.text)

# Temporary key name, deadlineSubmissionJob was already taken
if instance.data("runSlapComp", False):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is doing what you intended. Probably meant: instance.data.get("runSlapComp", False)?

instance.data["deadlineDependJob"] = response.json()
Empty file.
200 changes: 200 additions & 0 deletions colorbleed/scripts/fusion/deadline_switch_and_submit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
"""
This module is for a standalone approach for Fusion similar to Maya.
Note that this will require FusionConsoleNode.exe and the BlackmagicFusion
module,

Deadline runs a python process, lets call it P

P will start the FusionConsoleNode in a new SUBPROCESS
This SUBPROCESS will need to have the same environment as P to ensure it can
use AVALON

P --> SUBPROCESS (FusionConsoleNode.EXE /listen)

From the SUBPROCESS comes a Fusion Console Node which will be used as the Fusion
instance to work in. In order to get the correct Fusion instance we use a
ScriptServer to get all Fusion programs which are running.
This is done by comparing the process ids with the subprocess.pid.

See `get_fusion_instance` function for more details

In `avalon.fusion.pipeline` we have create a work around to get the fusion
instance. This is done through

getattr(sys.module["__main__"], "fusion", None)

Because we do this we can also allow to set correct fusion module, this is done
by using the setattr. This will ensure that all other functions which are run
within `process()` can find `fusion`.

"""


import subprocess
import traceback
import site
import time
import sys
import os

# This script only works with Python 2.7 and 3.6
version = "{0}{1}".format(*sys.version_info) # {major}{minor}
assert version in ["27", "36"], "Script only works in Python 2.7 or 3.6"
key = "FUSION_PYTHON{0}_HOME".format(version)

# Set Python 3.6 home for fusion, debug
Copy link
Member

@BigRoy BigRoy Mar 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't for debugging purposes but is a explicit and direct fix for a crash on BlackmagicFusion library crashing.

Let's state that in the comment so the intent is clear.

This could just read:

# Importing BlackmagicFusion package in standalone Python interpreter
# crashes when not installed on default location but runs from, e.g. a
# network share. Forcing Fusion's Python home magically fixes it.
print("Setting %s to Python executable directory.." % key)
os.environ[key] = os.path.dirname(sys.executable)

print("Settings FUSION_PYTHON36_HOME ..")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Python 2.7 this won't be setting FUSION_PYTHON36_HOME but FUSION_PYTHON27_HOME

os.environ[key] = os.path.dirname(sys.executable)

FUSCRIPT_EXE = r"C:/Program Files/Blackmagic Design/Fusion9/FuScript.exe"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me this is fine for now, but should be "defined" elsewhere eventually. Maybe add a TODO line for now?

FUSION_CONSOLE_EXE = r"C:/Program Files/Blackmagic Design/Fusion Render Node 9/FusionConsoleNode.exe"

import BlackmagicFusion as bmf


def _get_script_dir():
"""Get path to the image sequence script"""
try:
import colorbleed
scriptdir = os.path.dirname(colorbleed.__file__)
fusion_scripts = os.path.join(scriptdir,
"scripts",
"fusion")
except:
raise RuntimeError("This is a bug")

assert os.path.isdir(fusion_scripts), "Config is incomplete"
fusion_scripts = fusion_scripts.replace(os.sep, "/")

return fusion_scripts


def start_server():
bmf.startserver()
return get_server()


def get_server(tries=10, timeout=0.5):

count = 0
srv = None

while not srv:
count += 1
print("Connecting to ScriptServer (try: %s)" % count)
srv = bmf.scriptapp("", "localhost", timeout) # Runs script server
if count > tries:
break

return srv


def get_fusion_instance(pid, srv, timeout=10):
"""Get the fusion instance which has been launched"""

count = 0
host = None
while not host:
if count > timeout:
break
fusion_hosts = srv.GetHostList().values()
host = next((i for i in fusion_hosts if int(i["ProcessID"]) == pid),
None)
if not host:
print("Find Fusion host... (%ss)" % count)
time.sleep(0.5)
count += 0.5

assert host, "Fusion not found with pid: %s" % pid

return bmf.scriptapp(host["Name"], "localhost", 2, host["UUID"])


def process(file_path, asset_name, deadline=False):
"""Run switch in a Fusion Console Node (cmd)

Args:
file_path (str): File path of the comp to use
asset_name (str): Name of the asset (shot) to switch
deadline (bool, optional): If set True the new composition file will be
used to render
Returns:
None

"""

# Start a fusion console node in "listen" mode
proc = subprocess.Popen([FUSION_CONSOLE_EXE, "/listen"])

srv = get_server()
if not srv:
print("No server found, starting server ..")
srv = start_server()

# Force fusion into main magical module so that host.ls() works
fusion = get_fusion_instance(proc.pid, srv)
assert fusion
print("Connected to: %s" % fusion)
setattr(sys.modules["__main__"], "fusion", fusion)

# Get fusion.pipeline
from avalon.fusion import pipeline

# This does not set
loaded_comp = fusion.LoadComp(file_path)
if not loaded_comp:
raise RuntimeError("Comp could not be loaded. File '%s'" % file_path)
pipeline.force_current_comp(loaded_comp)
current_comp = pipeline.get_current_comp()

assert loaded_comp == current_comp, "Could not find the correct comp"

print("Loaded comp name: %s" % current_comp.GetAttrs("COMPS_FileName"))

# Get switch and submit script
scriptdir = _get_script_dir()
site.addsitedir(scriptdir)
import switch_and_submit as switch

# Fusion host
try:
# Execute script in comp
result = switch.switch(asset_name=asset_name, deadline=deadline)
except:
tb = traceback.format_exc()
proc.terminate() # Ensure process closes when failing
raise RuntimeError(tb)

print("Success:", result is not None)
print("Closing all running process ..")
proc.terminate()


# Usability for deadline job submission
if __name__ == '__main__':

import argparse

parser = argparse.ArgumentParser(description="Switch to a shot within an"
"existing comp file")

parser.add_argument("--file_path",
type=str,
default=True,
help="File path of the comp to use")

parser.add_argument("--asset_name",
type=str,
default=True,
help="Name of the asset (shot) to switch")

parser.add_argument("--render",
default=False,
help="If set True the new composition file will be used"
"to render")

args = parser.parse_args()

process(file_path=args.file_path,
asset_name=args.asset_name,
deadline=args.render)
Loading